Files
lity3d/Demo/editor_main.c
T

392 lines
19 KiB
C

#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Core/timer.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include "../Engine/Editor/glyph_atlas.h"
#include <d3dcompiler.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static GraphicsDevice* g_dev = NULL;
static GlyphAtlas g_atlas;
static ID3D11DepthStencilState* g_no_depth = NULL;
static ID3D11RasterizerState* g_no_cull = NULL;
static ID3D11BlendState* g_alpha_blend = NULL;
static ID3D11SamplerState* g_sampler = NULL;
static ID3D11Buffer* g_cb = NULL;
static ID3D11VertexShader* g_vs = NULL;
static ID3D11PixelShader* g_ps = NULL;
static ID3D11InputLayout* g_layout = NULL;
static ID3D11VertexShader* g_tex_vs = NULL;
static ID3D11PixelShader* g_tex_ps = NULL;
static ID3D11InputLayout* g_tex_layout = NULL;
typedef struct { float p[3], n[3], uv[2], c[4]; } V;
static void draw_quad(ID3D11DeviceContext* ctx, float x, float y, float w, float h,
float r, float g, float b, float a) {
V v[4] = {
{{x,y,0},{0,0,-1},{0,0},{r,g,b,a}},
{{x+w,y,0},{0,0,-1},{1,0},{r,g,b,a}},
{{x+w,y+h,0},{0,0,-1},{1,1},{r,g,b,a}},
{{x,y+h,0},{0,0,-1},{0,1},{r,g,b,a}},
};
uint32_t idx[6] = {0,1,2, 0,2,3};
ID3D11Buffer *vb = NULL, *ib = NULL;
D3D11_BUFFER_DESC bd = {0}; D3D11_SUBRESOURCE_DATA sd = {0};
bd.ByteWidth = sizeof(V)*4; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = v; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd, &sd, &vb);
bd.ByteWidth = sizeof(uint32_t)*6; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = idx; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd, &sd, &ib);
UINT stride = sizeof(V), offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->DrawIndexed(ctx, 6, 0, 0);
ib->lpVtbl->Release(ib); vb->lpVtbl->Release(vb);
}
static void draw_text(ID3D11DeviceContext* ctx, const wchar_t* text,
int x, int y, float r, float g, float b, float a) {
typedef struct { float p[3], n[3], uv[2], c[4]; } TV;
ctx->lpVtbl->VSSetShader(ctx, g_tex_vs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, g_tex_ps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, g_tex_layout);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &g_cb);
ctx->lpVtbl->PSSetShaderResources(ctx, 0, 1, &g_atlas.srv);
ctx->lpVtbl->PSSetSamplers(ctx, 0, 1, &g_sampler);
ctx->lpVtbl->OMSetBlendState(ctx, g_alpha_blend, NULL, 0xFFFFFFFF);
for (const wchar_t* p = text; *p; p++) {
float u0, v0, u1, v1, adv_x, adv_y;
if (!glyph_atlas_get_or_create(&g_atlas, g_dev->device, ctx, *p, &u0, &v0, &u1, &v1, &adv_x, &adv_y)) continue;
TV tv[4] = {
{{(float)x,(float)y,0},{0,0,1},{u0,v0},{r,g,b,a}},
{{(float)x+adv_x,(float)y,0},{0,0,1},{u1,v0},{r,g,b,a}},
{{(float)x+adv_x,(float)y+adv_y,0},{0,0,1},{u1,v1},{r,g,b,a}},
{{(float)x,(float)y+adv_y,0},{0,0,1},{u0,v1},{r,g,b,a}},
};
uint32_t ti[6] = {0,1,2, 0,2,3};
ID3D11Buffer *tvb = NULL, *tib = NULL;
D3D11_BUFFER_DESC bd = {0}; D3D11_SUBRESOURCE_DATA sd = {0};
bd.ByteWidth = sizeof(TV)*4; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = tv; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd, &sd, &tvb);
bd.ByteWidth = sizeof(uint32_t)*6; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = ti; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd, &sd, &tib);
UINT stride2 = sizeof(TV), offset2 = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &tvb, &stride2, &offset2);
ctx->lpVtbl->IASetIndexBuffer(ctx, tib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->DrawIndexed(ctx, 6, 0, 0);
tib->lpVtbl->Release(tib); tvb->lpVtbl->Release(tvb);
x += (int)adv_x + 1;
}
}
static void setup_solid(ID3D11DeviceContext* ctx) {
ctx->lpVtbl->VSSetShader(ctx, g_vs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, g_ps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, g_layout);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &g_cb);
ctx->lpVtbl->OMSetBlendState(ctx, NULL, NULL, 0xFFFFFFFF);
}
static void setup_2d(ID3D11DeviceContext* ctx, float sw, float sh) {
float proj[16] = {0};
proj[0] = 2.0f / sw; proj[5] = -2.0f / sh;
proj[10] = 1.0f; proj[12] = -1.0f; proj[13] = 1.0f; proj[15] = 1.0f;
Mat4 proj_m, proj_t;
memcpy(&proj_m, proj, sizeof(Mat4));
mat4_transpose(&proj_m, &proj_t);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_cb, 0, NULL, &proj_t, 0, 0);
D3D11_VIEWPORT vp = {0};
vp.Width = sw; vp.Height = sh; vp.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
ctx->lpVtbl->RSSetState(ctx, g_no_cull);
ctx->lpVtbl->OMSetDepthStencilState(ctx, g_no_depth, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
setup_solid(ctx);
}
typedef struct {
char name[64];
float x, y, z, rx, ry, rz, sx, sy, sz;
int visible;
} SceneEntity;
#define MAX_ENTITIES 64
static SceneEntity g_entities[MAX_ENTITIES];
static int g_entity_count = 0;
static int g_selected = 2;
static char g_console_lines[32][256];
static int g_console_count = 0;
static char g_file_path[512] = "untitled.ly";
static int g_mouse_x = 0, g_mouse_y = 0;
static int g_mouse_click = 0;
static int g_mouse_click_x = 0, g_mouse_click_y = 0;
static void console_add(const char* msg) {
if (g_console_count >= 32) {
for (int i = 0; i < 31; i++) strcpy(g_console_lines[i], g_console_lines[i+1]);
g_console_count = 31;
}
strncpy(g_console_lines[g_console_count], msg, 255);
g_console_lines[g_console_count][255] = 0;
g_console_count++;
}
static void on_mouse_move(int x, int y) {
g_mouse_x = x; g_mouse_y = y;
}
static void on_mouse_button(int button) {
if (button == 0) {
if (g_mouse_click == 0) {
g_mouse_click = 1;
g_mouse_click_x = g_mouse_x;
g_mouse_click_y = g_mouse_y;
}
}
}
static void process_click(void) {
if (!g_mouse_click) return;
g_mouse_click = 0;
int cx = g_mouse_click_x, cy = g_mouse_click_y;
float pw = 220, mh = 28, th = 28, sh2 = 22, ch = 180;
if (cx >= 0 && cx <= (int)pw && cy >= (int)(mh + th)) {
for (int i = 0; i < g_entity_count; i++) {
float ey = 60 + i * 22;
if (cy >= (int)ey - 4 && cy <= (int)ey + 18) {
g_selected = i;
char buf[128]; snprintf(buf, sizeof(buf), "Selected: %s", g_entities[i].name);
console_add(buf);
return;
}
}
}
}
static void save_ly(const char* path) {
FILE* f = fopen(path, "w");
if (!f) { console_add("ERROR: save failed"); return; }
fprintf(f, "# lity3d scene\n# format: .ly\n\n");
for (int i = 0; i < g_entity_count; i++) {
SceneEntity* e = &g_entities[i];
fprintf(f, "[Entity]\nname = %s\n", e->name);
fprintf(f, "position = %.2f, %.2f, %.2f\n", e->x, e->y, e->z);
fprintf(f, "rotation = %.2f, %.2f, %.2f\n", e->rx, e->ry, e->rz);
fprintf(f, "scale = %.2f, %.2f, %.2f\n", e->sx, e->sy, e->sz);
fprintf(f, "visible = %d\n\n", e->visible);
}
fclose(f);
char buf[128]; snprintf(buf, sizeof(buf), "Saved %d entities -> %s", g_entity_count, path);
console_add(buf);
}
static void load_ly(const char* path) {
FILE* f = fopen(path, "r");
if (!f) { console_add("ERROR: open failed"); return; }
g_entity_count = 0; g_selected = -1;
char line[256]; SceneEntity tmp = {0}; int in_e = 0;
while (fgets(line, sizeof(line), f)) {
if (line[0] == '#' || line[0] == '\n') continue;
if (strstr(line, "[Entity]")) {
if (in_e && g_entity_count < MAX_ENTITIES) g_entities[g_entity_count++] = tmp;
memset(&tmp, 0, sizeof(tmp)); tmp.visible = 1; tmp.sx = tmp.sy = tmp.sz = 1.0f; in_e = 1; continue;
}
if (!in_e) continue;
char* eq = strchr(line, '='); if (!eq) continue;
*eq = 0; eq++; char* key = line; while (*key == ' ') key++;
char* nl = strchr(eq, '\n'); if (nl) *nl = 0; while (*eq == ' ') eq++;
if (strstr(key, "name")) strncpy(tmp.name, eq, 63);
else if (strstr(key, "position")) sscanf(eq, "%f, %f, %f", &tmp.x, &tmp.y, &tmp.z);
else if (strstr(key, "rotation")) sscanf(eq, "%f, %f, %f", &tmp.rx, &tmp.ry, &tmp.rz);
else if (strstr(key, "scale")) sscanf(eq, "%f, %f, %f", &tmp.sx, &tmp.sy, &tmp.sz);
else if (strstr(key, "visible")) tmp.visible = atoi(eq);
}
if (in_e && g_entity_count < MAX_ENTITIES) g_entities[g_entity_count++] = tmp;
fclose(f); char buf[128];
snprintf(buf, sizeof(buf), "Loaded %d entities <- %s", g_entity_count, path);
console_add(buf); strncpy(g_file_path, path, 511);
}
static void init_default_scene(void) {
g_entity_count = 0;
const char* n[] = {"Camera","DirectionalLight","Player","Ground","Tree","Rock","Water","Skybox"};
for (int i = 0; i < 8; i++) {
strncpy(g_entities[i].name, n[i], 63); g_entities[i].visible = 1;
g_entities[i].sx = g_entities[i].sy = g_entities[i].sz = 1.0f;
}
g_entity_count = 8; g_selected = 2;
}
static void on_render(Engine* engine) {
if (!g_dev) return;
process_click();
ID3D11DeviceContext* ctx = g_dev->context;
float sw = (float)g_dev->width, sh = (float)g_dev->height;
graphics_device_clear(g_dev, 0.18f, 0.18f, 0.22f, 1.0f);
setup_2d(ctx, sw, sh);
float mh = 30, th = 30, ch = 180, pw = 240, sbh = 24;
draw_quad(ctx, 0, 0, sw, mh, 0.15f, 0.15f, 0.22f, 1.0f);
draw_quad(ctx, 0, mh, sw, 1, 0.35f, 0.45f, 0.75f, 1.0f);
draw_quad(ctx, 0, mh+1, sw, th, 0.22f, 0.22f, 0.30f, 1.0f);
draw_quad(ctx, 0, mh+th+1, sw, 1, 0.30f, 0.30f, 0.40f, 1.0f);
float body_h = sh - mh - th - ch - sbh - 2;
draw_quad(ctx, 0, mh+th+2, pw, body_h, 0.22f, 0.22f, 0.28f, 1.0f);
draw_quad(ctx, pw, mh+th+2, 1, body_h, 0.35f, 0.45f, 0.75f, 1.0f);
draw_quad(ctx, pw+1, mh+th+2, sw-pw*2-2, body_h, 0.16f, 0.16f, 0.20f, 1.0f);
draw_quad(ctx, sw-pw-1, mh+th+2, 1, body_h, 0.35f, 0.45f, 0.75f, 1.0f);
draw_quad(ctx, sw-pw, mh+th+2, pw, body_h, 0.22f, 0.22f, 0.28f, 1.0f);
draw_quad(ctx, 0, sh-ch-sbh, sw, 1, 0.35f, 0.45f, 0.75f, 1.0f);
draw_quad(ctx, 0, sh-ch-sbh+1, sw, ch, 0.19f, 0.19f, 0.25f, 1.0f);
draw_quad(ctx, 0, sh-sbh, sw, 1, 0.35f, 0.45f, 0.75f, 1.0f);
draw_quad(ctx, 0, sh-sbh+1, sw, sbh, 0.20f, 0.22f, 0.32f, 1.0f);
draw_text(ctx, L"lity3d Editor", 12, 7, 0.6f, 0.8f, 1.0f, 1.0f);
const wchar_t* menus[] = {L"\u6587\u4ef6", L"\u7f16\u8f91", L"\u89c6\u56fe", L"\u6784\u5efa", L"\u5e2e\u52a9"};
int mx = 130;
for (int i = 0; i < 5; i++) { draw_text(ctx, menus[i], mx, 8, 0.9f, 0.9f, 0.92f, 1.0f); mx += 80; }
draw_text(ctx, L"\u573a\u666f", 12, (int)(mh+th+8), 0.5f, 0.7f, 1.0f, 1.0f);
draw_text(ctx, L"\u89c6\u53e3", (int)(pw+16), (int)(mh+th+8), 0.5f, 0.7f, 1.0f, 1.0f);
draw_text(ctx, L"\u5c5e\u6027", (int)(sw-pw+12), (int)(mh+th+8), 0.5f, 0.7f, 1.0f, 1.0f);
draw_text(ctx, L"\u63a7\u5236\u53f0", 12, (int)(sh-ch-sbh+8), 0.5f, 0.7f, 1.0f, 1.0f);
for (int i = 0; i < g_entity_count; i++) {
float ey = mh + th + 30 + i * 24;
if (i == g_selected)
draw_quad(ctx, 4, ey - 2, pw - 8, 22, 0.25f, 0.35f, 0.60f, 1.0f);
wchar_t wn[64]; int len = (int)strlen(g_entities[i].name);
for (int j = 0; j < len && j < 63; j++) wn[j] = (wchar_t)g_entities[i].name[j]; wn[len] = 0;
draw_text(ctx, wn, 16, (int)ey, (i == g_selected) ? 1.0f : 0.85f, (i == g_selected) ? 1.0f : 0.85f, (i == g_selected) ? 1.0f : 0.88f, 1.0f);
}
if (g_selected >= 0 && g_selected < g_entity_count) {
SceneEntity* e = &g_entities[g_selected]; int ix = (int)(sw-pw+12), iy = (int)(mh+th+32);
draw_text(ctx, L"\u540d\u79f0:", ix, iy, 0.5f, 0.7f, 1.0f, 1.0f);
wchar_t wn[64]; int len = (int)strlen(e->name);
for (int j = 0; j < len && j < 63; j++) wn[j] = (wchar_t)e->name[j]; wn[len] = 0;
draw_text(ctx, wn, ix + 48, iy, 1.0f, 1.0f, 1.0f, 1.0f);
iy += 26;
draw_text(ctx, L"\u53ef\u89c1: \u2611", ix, iy, 0.85f, 0.85f, 0.88f, 1.0f);
}
int cy = (int)(sh - ch - sbh + 28);
for (int i = g_console_count - 1; i >= 0 && i >= g_console_count - 6; i--) {
wchar_t wl[256]; int len = (int)strlen(g_console_lines[i]);
for (int j = 0; j < len && j < 255; j++) wl[j] = (wchar_t)g_console_lines[i][j];
wl[len] = 0; draw_text(ctx, wl, 12, cy, 0.7f, 0.7f, 0.75f, 1.0f); cy += 20;
}
char status[256];
snprintf(status, sizeof(status), "%s | %d entities | Ctrl+S save | Ctrl+O load | Esc quit", g_file_path, g_entity_count);
wchar_t ws[256]; int sl = (int)strlen(status);
for (int j = 0; j < sl && j < 255; j++) ws[j] = (wchar_t)status[j]; ws[sl] = 0;
draw_text(ctx, ws, 12, (int)(sh - 20), 0.6f, 0.6f, 0.65f, 1.0f);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) {
if (key == VK_ESCAPE) engine_request_quit(engine);
if (key == 'S' && GetAsyncKeyState(VK_CONTROL) & 0x8000) save_ly(g_file_path);
if (key == 'O' && GetAsyncKeyState(VK_CONTROL) & 0x8000) load_ly(g_file_path);
}
static int compile_shader(ID3D11Device* dev, const char* vs, const char* ps,
ID3D11VertexShader** out_vs, ID3D11PixelShader** out_ps,
ID3D11InputLayout** out_layout) {
ID3DBlob *b1=NULL,*b2=NULL,*e=NULL;
D3DCompile(vs,(UINT)strlen(vs),NULL,NULL,NULL,"main","vs_5_0",0,0,&b1,&e); if(e){e->lpVtbl->Release(e);e=NULL;}
D3DCompile(ps,(UINT)strlen(ps),NULL,NULL,NULL,"main","ps_5_0",0,0,&b2,&e); if(e){e->lpVtbl->Release(e);e=NULL;}
if(!b1||!b2) return 0;
dev->lpVtbl->CreateVertexShader(dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,out_vs);
dev->lpVtbl->CreatePixelShader(dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,out_ps);
D3D11_INPUT_ELEMENT_DESC layout[] = {
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
{"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0},
{"TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,24,D3D11_INPUT_PER_VERTEX_DATA,0},
{"COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,32,D3D11_INPUT_PER_VERTEX_DATA,0},
};
dev->lpVtbl->CreateInputLayout(dev,layout,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),out_layout);
b1->lpVtbl->Release(b1); b2->lpVtbl->Release(b2);
return 1;
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR cmd, int nS) {
(void)hI;(void)hP;(void)nS;
LOG_INFO("=== lity3d Editor ===");
EngineConfig cfg = engine_default_config();
cfg.app_name = "lity3d Editor";
cfg.vsync = 1; cfg.window_width = 1440; cfg.window_height = 900;
Engine* engine = engine_create();
if (!engine_init(engine, &cfg)) return -1;
g_dev = graphics_device_create();
if (!graphics_device_init(g_dev, window_get_handle(engine->window), cfg.window_width, cfg.window_height, 1)) return -1;
glyph_atlas_init(&g_atlas, g_dev->device);
D3D11_DEPTH_STENCIL_DESC dsd = {0}; dsd.DepthEnable = FALSE; dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device, &dsd, &g_no_depth);
D3D11_RASTERIZER_DESC rd = {0}; rd.FillMode = D3D11_FILL_SOLID; rd.CullMode = D3D11_CULL_NONE;
g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device, &rd, &g_no_cull);
D3D11_BLEND_DESC bd = {0}; bd.RenderTarget[0].BlendEnable = TRUE;
bd.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; bd.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
bd.RenderTarget[0].RenderTargetWriteMask = 0x0F;
g_dev->device->lpVtbl->CreateBlendState(g_dev->device, &bd, &g_alpha_blend);
D3D11_SAMPLER_DESC sd = {0}; sd.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sd.AddressU = sd.AddressV = sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
g_dev->device->lpVtbl->CreateSamplerState(g_dev->device, &sd, &g_sampler);
D3D11_BUFFER_DESC cbd = {0}; cbd.ByteWidth = 256; cbd.Usage = D3D11_USAGE_DEFAULT; cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &cbd, NULL, &g_cb);
const char* solid_vs = "cbuffer CB:register(b0){float4x4 WVP;};struct I{float4 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float2 u:TEXCOORD;float4 c:COLOR;};O main(I i){O o;o.p=mul(i.p,WVP);o.u=i.u;o.c=i.c;return o;}";
const char* solid_ps = "struct O{float4 p:SV_POSITION;float2 u:TEXCOORD;float4 c:COLOR;};float4 main(O i):SV_TARGET{return i.c;}";
compile_shader(g_dev->device, solid_vs, solid_ps, &g_vs, &g_ps, &g_layout);
const char* tex_vs = "cbuffer CB:register(b0){float4x4 WVP;};struct I{float4 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float2 u:TEXCOORD;float4 c:COLOR;};O main(I i){O o;o.p=mul(i.p,WVP);o.u=i.u;o.c=i.c;return o;}";
const char* tex_ps = "Texture2D t:register(t0);SamplerState s:register(s0);struct O{float4 p:SV_POSITION;float2 u:TEXCOORD;float4 c:COLOR;};float4 main(O i):SV_TARGET{float a=t.Sample(s,i.u).a;return float4(i.c.rgb,i.c.a*a);}";
compile_shader(g_dev->device, tex_vs, tex_ps, &g_tex_vs, &g_tex_ps, &g_tex_layout);
if (cmd && cmd[0]) { load_ly(cmd); }
else { init_default_scene(); }
console_add("lity3d Editor ready");
console_add("Ctrl+S save .ly | Ctrl+O load .ly | Esc quit");
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
window_set_mouse_move_callback(engine->window, on_mouse_move);
window_set_mouse_button_callback(engine->window, on_mouse_button);
engine_run(engine);
if(g_tex_layout)g_tex_layout->lpVtbl->Release(g_tex_layout);
if(g_tex_ps)g_tex_ps->lpVtbl->Release(g_tex_ps);
if(g_tex_vs)g_tex_vs->lpVtbl->Release(g_tex_vs);
if(g_layout)g_layout->lpVtbl->Release(g_layout);
if(g_ps)g_ps->lpVtbl->Release(g_ps);
if(g_vs)g_vs->lpVtbl->Release(g_vs);
if(g_cb)g_cb->lpVtbl->Release(g_cb);
if(g_sampler)g_sampler->lpVtbl->Release(g_sampler);
if(g_alpha_blend)g_alpha_blend->lpVtbl->Release(g_alpha_blend);
if(g_no_cull)g_no_cull->lpVtbl->Release(g_no_cull);
if(g_no_depth)g_no_depth->lpVtbl->Release(g_no_depth);
glyph_atlas_destroy(&g_atlas);
graphics_device_destroy(g_dev); engine_shutdown(engine);
return 0;
}