fix: .gitignore + add 44 language JSON files

This commit is contained in:
shuiyu1123 committed 2026-07-14 01:17:33 +08:00
1 parent e8fd68e27b
commit 8de545dd2c
227 files changed
+22603

No files matched your search

BIN
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
zh
+21
View File
@@ -0,0 +1,21 @@
# Simple cube OBJ for testing
v -1.0 -1.0 -1.0
v 1.0 -1.0 -1.0
v 1.0 1.0 -1.0
v -1.0 1.0 -1.0
v -1.0 -1.0 1.0
v 1.0 -1.0 1.0
v 1.0 1.0 1.0
v -1.0 1.0 1.0
f 1 2 3
f 1 3 4
f 5 7 6
f 5 8 7
f 1 5 6
f 1 6 2
f 2 6 7
f 2 7 3
f 3 7 8
f 3 8 4
f 4 8 5
f 4 5 1
+12
View File
@@ -0,0 +1,12 @@
# Pyramid OBJ
v 0.0 1.0 0.0
v -1.0 -1.0 1.0
v 1.0 -1.0 1.0
v 1.0 -1.0 -1.0
v -1.0 -1.0 -1.0
f 1 2 3
f 1 3 4
f 1 4 5
f 1 5 2
f 2 5 4
f 2 4 3
+87
View File
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{A5A5A5A5-5A5A-5A5A-5A5A-5A5A5A5A5A5A}</ProjectGuid>
<RootNamespace>Demo</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdc17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(SolutionDir)Engine\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>d3d11.lib;dxgi.lib;d3dcompiler.lib;xaudio2.lib;xinput.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdc17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(SolutionDir)Engine\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>d3d11.lib;dxgi.lib;d3dcompiler.lib;xaudio2.lib;xinput.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
+118
View File
@@ -0,0 +1,118 @@
// ============================================================================
// audio_demo.c - XAudio2 音频系统演示
// 生成 440Hz 正弦波并播放 2 秒,无需外部 WAV 文件
// ============================================================================
#include "../Engine/Audio/audio.h"
#include "../Engine/Core/logger.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#pragma comment(lib, "winmm.lib")
// 生成标准 PCM WAV 数据 (44.1kHz, 16bit, 单声道)
static uint8_t* generate_sine_wave_wav(float frequency, float duration_sec, uint32_t* out_size) {
const uint32_t sample_rate = 44100;
const uint16_t channels = 1;
const uint16_t bits_per_sample = 16;
const uint16_t block_align = channels * bits_per_sample / 8;
const uint32_t byte_rate = sample_rate * block_align;
const uint32_t num_samples = (uint32_t)(sample_rate * duration_sec);
const uint32_t data_size = num_samples * block_align;
const uint32_t file_size = 44 + data_size;
uint8_t* wav = (uint8_t*)malloc(file_size);
if (!wav) return NULL;
// RIFF 头
memcpy(wav + 0, "RIFF", 4);
memcpy(wav + 4, &file_size, 4);
memcpy(wav + 8, "WAVE", 4);
// fmt chunk
memcpy(wav + 12, "fmt ", 4);
uint32_t fmt_size = 16;
memcpy(wav + 16, &fmt_size, 4);
uint16_t audio_format = 1; // PCM
memcpy(wav + 20, &audio_format, 2);
memcpy(wav + 22, &channels, 2);
memcpy(wav + 24, &sample_rate, 4);
memcpy(wav + 28, &byte_rate, 4);
memcpy(wav + 32, &block_align, 2);
memcpy(wav + 34, &bits_per_sample, 2);
// data chunk
memcpy(wav + 36, "data", 4);
memcpy(wav + 40, &data_size, 4);
// 生成正弦波样本
int16_t* samples = (int16_t*)(wav + 44);
float amplitude = 0.3f; // 避免削波
for (uint32_t i = 0; i < num_samples; i++) {
float t = (float)i / (float)sample_rate;
// 简单淡出,避免咔嗒声
float envelope = 1.0f;
if (i < 1000) envelope = (float)i / 1000.0f;
if (i > num_samples - 1000) envelope = (float)(num_samples - i) / 1000.0f;
float sample = sinf(2.0f * 3.14159265f * frequency * t) * amplitude * envelope;
samples[i] = (int16_t)(sample * 32767.0f);
}
*out_size = file_size;
return wav;
}
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
printf("Lity3D - Audio Demo\n");
printf("========================\n");
AudioSystem* audio = audio_create();
if (!audio) {
printf("ERROR: Failed to create XAudio2 audio system\n");
return 1;
}
printf("XAudio2 audio system created.\n");
AudioSource* source = audio_source_create(audio);
if (!source) {
printf("ERROR: Failed to create audio source\n");
audio_destroy(audio);
return 1;
}
uint32_t wav_size = 0;
uint8_t* wav_data = generate_sine_wave_wav(440.0f, 2.0f, &wav_size);
if (!wav_data) {
printf("ERROR: Failed to generate sine wave\n");
audio_source_destroy(source);
audio_destroy(audio);
return 1;
}
if (!audio_load_wav_memory(source, wav_data, wav_size)) {
printf("ERROR: Failed to load generated WAV into audio source\n");
free(wav_data);
audio_source_destroy(source);
audio_destroy(audio);
return 1;
}
free(wav_data);
printf("Generated 440Hz sine wave loaded.\n");
printf("Playing 2 seconds...\n");
audio_source_play(source);
// 等待播放结束 (约 2 秒 + 缓冲)
Sleep(2500);
audio_source_destroy(source);
audio_destroy(audio);
printf("Audio demo finished successfully.\n");
return 0;
}
+221
View File
@@ -0,0 +1,221 @@
#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/Rendering/shader.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include "../Engine/Editor/glyph_atlas.h"
#include <d3dcompiler.h>
static GraphicsDevice* g_dev = NULL;
static Shader* g_shader = NULL;
static Mesh* g_tri = NULL;
static GlyphAtlas g_atlas;
static ID3D11VertexShader* g_tvs = NULL;
static ID3D11PixelShader* g_tps = NULL;
static ID3D11InputLayout* g_tlayout = NULL;
static ID3D11BlendState* g_tblend = NULL;
static ID3D11SamplerState* g_tsampler = NULL;
static ID3D11Buffer* g_tcb = NULL;
static ID3D11DepthStencilState* g_no_depth = 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 on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
float sw = (float)g_dev->width;
float sh = (float)g_dev->height;
graphics_device_clear(g_dev, 0.0f, 0.0f, 0.0f, 1.0f);
float proj[64] = {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);
D3D11_VIEWPORT vp = {0};
vp.Width = sw; vp.Height = sh; vp.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
D3D11_RASTERIZER_DESC rdesc = {0};
rdesc.FillMode = D3D11_FILL_SOLID;
rdesc.CullMode = D3D11_CULL_NONE;
ID3D11RasterizerState* rstate = NULL;
g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device, &rdesc, &rstate);
ctx->lpVtbl->RSSetState(ctx, rstate);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(g_shader, ctx);
shader_set_matrix(g_shader, ctx, (float*)&proj_t);
mesh_draw(g_tri, ctx);
{
ctx->lpVtbl->OMSetDepthStencilState(ctx, g_no_depth, 0);
}
draw_quad(ctx, sw - 200, 10, 180, 60, 1, 1, 1, 1);
if (g_tvs && g_tps && g_tlayout && g_tcb && g_tblend && g_tsampler && g_atlas.srv) {
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_tcb, 0, NULL, &proj_t, 0, 0);
ctx->lpVtbl->VSSetShader(ctx, g_tvs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, g_tps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, g_tlayout);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &g_tcb);
ctx->lpVtbl->PSSetShaderResources(ctx, 0, 1, &g_atlas.srv);
ctx->lpVtbl->PSSetSamplers(ctx, 0, 1, &g_tsampler);
ctx->lpVtbl->OMSetBlendState(ctx, g_tblend, NULL, 0xFFFFFFFF);
typedef struct { float p[3], n[3], uv[2], c[4]; } TV;
TV tverts[4];
uint32_t tidx[6] = {0,1,2, 0,2,3};
int cx = (int)(sw - 195), cy = 35;
for (const wchar_t* p = L"\u6d4b\u8bd5"; *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)) {
tverts[0] = (TV){{(float)cx,(float)cy,0},{0,0,1},{u0,v0},{0,0,0,1}};
tverts[1] = (TV){{(float)cx+adv_x,(float)cy,0},{0,0,1},{u1,v0},{0,0,0,1}};
tverts[2] = (TV){{(float)cx+adv_x,(float)cy+adv_y,0},{0,0,1},{u1,v1},{0,0,0,1}};
tverts[3] = (TV){{(float)cx,(float)cy+adv_y,0},{0,0,1},{u0,v1},{0,0,0,1}};
ID3D11Buffer *tvb = NULL, *tib = NULL;
D3D11_BUFFER_DESC bd2 = {0}; D3D11_SUBRESOURCE_DATA sd2 = {0};
bd2.ByteWidth = sizeof(TV)*4; bd2.Usage = D3D11_USAGE_DEFAULT; bd2.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd2.pSysMem = tverts; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd2, &sd2, &tvb);
bd2.ByteWidth = sizeof(uint32_t)*6; bd2.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd2.pSysMem = tidx; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd2, &sd2, &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);
cx += (int)adv_x + 2;
}
}
}
if (rstate) rstate->lpVtbl->Release(rstate);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) {
if (key == VK_ESCAPE) engine_request_quit(engine);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
(void)hInstance; (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow;
LOG_INFO("=== Compatibility Test ===");
EngineConfig config = engine_default_config();
config.app_name = "Compatibility Test";
config.vsync = 1;
config.window_width = 1280;
config.window_height = 720;
Engine* engine = engine_create();
if (!engine_init(engine, &config)) return -1;
g_dev = graphics_device_create();
if (!graphics_device_init(g_dev, window_get_handle(engine->window), config.window_width, config.window_height, 1)) {
LOG_FATAL("D3D11 init failed");
return -1;
}
g_shader = shader_create();
shader_compile(g_shader, g_dev->device, NULL, NULL, 0, 0);
glyph_atlas_init(&g_atlas, g_dev->device);
const char* tvs_src = "cbuffer CB:register(b0){float4x4 WVP;};struct VS_IN{float4 pos:POSITION;float3 n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};struct PS_IN{float4 pos:SV_POSITION;float2 uv:TEXCOORD;float4 col:COLOR;};PS_IN main(VS_IN i){PS_IN o;o.pos=mul(i.pos,WVP);o.uv=i.uv;o.col=i.col;return o;}";
const char* tps_src = "Texture2D tex:register(t0);SamplerState smp:register(s0);struct PS_IN{float4 pos:SV_POSITION;float2 uv:TEXCOORD;float4 col:COLOR;};float4 main(PS_IN i):SV_TARGET{float a=tex.Sample(smp,i.uv).a;return float4(i.col.rgb,i.col.a*a);}";
ID3DBlob *vsb = NULL, *psb = NULL, *err = NULL;
D3DCompile(tvs_src, (UINT)strlen(tvs_src), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vsb, &err);
if (err) { err->lpVtbl->Release(err); err = NULL; }
D3DCompile(tps_src, (UINT)strlen(tps_src), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &psb, &err);
if (err) { err->lpVtbl->Release(err); err = NULL; }
if (vsb && psb) {
g_dev->device->lpVtbl->CreateVertexShader(g_dev->device, vsb->lpVtbl->GetBufferPointer(vsb), vsb->lpVtbl->GetBufferSize(vsb), NULL, &g_tvs);
g_dev->device->lpVtbl->CreatePixelShader(g_dev->device, psb->lpVtbl->GetBufferPointer(psb), psb->lpVtbl->GetBufferSize(psb), NULL, &g_tps);
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},
};
g_dev->device->lpVtbl->CreateInputLayout(g_dev->device, layout, 4, vsb->lpVtbl->GetBufferPointer(vsb), vsb->lpVtbl->GetBufferSize(vsb), &g_tlayout);
vsb->lpVtbl->Release(vsb); psb->lpVtbl->Release(psb);
}
D3D11_SAMPLER_DESC sdesc = {0};
sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sdesc.AddressU = sdesc.AddressV = sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
g_dev->device->lpVtbl->CreateSamplerState(g_dev->device, &sdesc, &g_tsampler);
D3D11_BLEND_DESC bdesc = {0};
bdesc.RenderTarget[0].BlendEnable = TRUE;
bdesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bdesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bdesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bdesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bdesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].RenderTargetWriteMask = 0x0F;
g_dev->device->lpVtbl->CreateBlendState(g_dev->device, &bdesc, &g_tblend);
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_tcb);
D3D11_DEPTH_STENCIL_DESC dsdesc = {0};
dsdesc.DepthEnable = FALSE;
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device, &dsdesc, &g_no_depth);
V tri[3] = {
{{640, 300, 0}, {0,0,-1}, {0,0}, {1, 0.2f, 0.2f, 1}},
{{500, 550, 0}, {0,0,-1}, {0,1}, {0.2f, 1, 0.2f, 1}},
{{780, 550, 0}, {0,0,-1}, {1,0}, {0.2f, 0.2f, 1, 1}},
};
uint32_t idx[3] = {0, 1, 2};
g_tri = (Mesh*)calloc(1, sizeof(Mesh));
g_tri->stride = sizeof(V); g_tri->vcount = 3; g_tri->icount = 3;
D3D11_BUFFER_DESC bd = {0}; D3D11_SUBRESOURCE_DATA sd = {0};
bd.ByteWidth = sizeof(V)*3; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = tri; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd, &sd, &g_tri->vb);
bd.ByteWidth = sizeof(uint32_t)*3; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = idx; g_dev->device->lpVtbl->CreateBuffer(g_dev->device, &bd, &sd, &g_tri->ib);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
LOG_INFO("Running...");
engine_run(engine);
mesh_destroy(g_tri);
shader_destroy(g_shader);
glyph_atlas_destroy(&g_atlas);
graphics_device_destroy(g_dev);
engine_shutdown(engine);
return 0;
}
+80
View File
@@ -0,0 +1,80 @@
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <math.h>
#include <stdint.h>
#include <string.h>
typedef struct { float p[3], n[3], u[2], c[4]; } V3D;
static ID3D11Device* g_dev; static ID3D11DeviceContext* g_ctx;
static IDXGISwapChain* g_sc; static ID3D11RenderTargetView* g_rtv;
static ID3D11VertexShader* g_vs; static ID3D11PixelShader* g_ps;
static ID3D11InputLayout* g_il; static ID3D11Buffer* g_cb;
static int key_l=0,key_r=0,key_u=0,key_d=0,key_sp=0;
static int dead=0, score=0;
static float px=0, py=0, speed=0.4f;
#define NOBS 15
static float ox[NOBS],oy[NOBS],oz[NOBS],osz[NOBS];
static int oa[NOBS],oc=0; static float st=0;
static void Init(HWND h){
HRESULT hr;DXGI_SWAP_CHAIN_DESC sd;memset(&sd,0,sizeof(sd));sd.BufferCount=2;sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;sd.BufferDesc.RefreshRate.Numerator=60;sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;sd.OutputWindow=h;sd.SampleDesc.Count=1;sd.Windowed=TRUE;sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL fl=D3D_FEATURE_LEVEL_11_0,gt;hr=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,&fl,1,D3D11_SDK_VERSION,&sd,&g_sc,&g_dev,&gt,&g_ctx);if(FAILED(hr))return;
ID3D11Texture2D*bb=NULL;hr=g_sc->lpVtbl->GetBuffer(g_sc,0,&IID_ID3D11Texture2D,(void**)&bb);if(FAILED(hr)||!bb)return;hr=g_dev->lpVtbl->CreateRenderTargetView(g_dev,(ID3D11Resource*)bb,NULL,&g_rtv);bb->lpVtbl->Release(bb);if(FAILED(hr))return;
const char*vs="cbuffer CB:register(b0){float4x4 WVP;};struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float4 c:COLOR;};O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.c=i.c;return o;}";
const char*ps="float4 main(float4 p:SV_POSITION, float4 c:COLOR):SV_Target{return c;}";
ID3DBlob*b1,*b2,*e;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;
g_dev->lpVtbl->CreateVertexShader(g_dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);g_dev->lpVtbl->CreatePixelShader(g_dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC el[4];memset(el,0,sizeof(el));el[0].SemanticName="POSITION";el[0].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[0].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[1].SemanticName="NORMAL";el[1].AlignedByteOffset=12;el[1].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[1].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[2].SemanticName="TEXCOORD";el[2].AlignedByteOffset=24;el[2].Format=DXGI_FORMAT_R32G32_FLOAT;el[2].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[3].SemanticName="COLOR";el[3].AlignedByteOffset=32;el[3].Format=DXGI_FORMAT_R32G32B32A32_FLOAT;el[3].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;
g_dev->lpVtbl->CreateInputLayout(g_dev,el,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);b1->lpVtbl->Release(b1);b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd;memset(&cbd,0,sizeof(cbd));cbd.ByteWidth=256;cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;g_dev->lpVtbl->CreateBuffer(g_dev,&cbd,NULL,&g_cb);
}
static void Cube(float cx,float cy,float cz,float s,float cr,float cg,float cb){
V3D v[24];float f[6][4][3]={{{-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}},{{1,-1,-1},{1,-1,1},{1,1,1},{1,1,-1}},{{1,-1,1},{-1,-1,1},{-1,1,1},{1,1,1}},{{-1,-1,1},{-1,-1,-1},{-1,1,-1},{-1,1,1}},{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1}},{{-1,-1,1},{1,-1,1},{1,-1,-1},{-1,-1,-1}}};float nm[6][3]={{0,0,-1},{1,0,0},{0,0,1},{-1,0,0},{0,1,0},{0,-1,0}};
for(int fi=0;fi<6;fi++)for(int vi=0;vi<4;vi++){int i=fi*4+vi;v[i].p[0]=f[fi][vi][0]*s+cx;v[i].p[1]=f[fi][vi][1]*s+cy;v[i].p[2]=f[fi][vi][2]*s+cz;v[i].n[0]=nm[fi][0];v[i].n[1]=nm[fi][1];v[i].n[2]=nm[fi][2];v[i].c[0]=cr;v[i].c[1]=cg;v[i].c[2]=cb;v[i].c[3]=1;}
uint32_t ix[36];for(int fi=0;fi<6;fi++){int b=fi*4;ix[fi*6]=b;ix[fi*6+1]=b+1;ix[fi*6+2]=b+2;ix[fi*6+3]=b;ix[fi*6+4]=b+2;ix[fi*6+5]=b+3;}
ID3D11Buffer*vb,*ib;D3D11_BUFFER_DESC bd;D3D11_SUBRESOURCE_DATA sd;memset(&bd,0,sizeof(bd));memset(&sd,0,sizeof(sd));bd.ByteWidth=sizeof(V3D)*24;bd.Usage=D3D11_USAGE_DEFAULT;bd.BindFlags=D3D11_BIND_VERTEX_BUFFER;sd.pSysMem=v;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&vb);bd.ByteWidth=sizeof(uint32_t)*36;bd.BindFlags=D3D11_BIND_INDEX_BUFFER;sd.pSysMem=ix;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&ib);
UINT s2=sizeof(V3D),o=0;g_ctx->lpVtbl->IASetVertexBuffers(g_ctx,0,1,&vb,&s2,&o);g_ctx->lpVtbl->IASetIndexBuffer(g_ctx,ib,DXGI_FORMAT_R32_UINT,0);g_ctx->lpVtbl->DrawIndexed(g_ctx,36,0,0);ib->lpVtbl->Release(ib);vb->lpVtbl->Release(vb);
}
static void Spawn(void){for(int i=0;i<NOBS;i++){if(!oa[i]){oa[i]=1;ox[i]=-1.0f+(float)(rand()%200)/100.0f;oy[i]=-0.6f+(float)(rand()%120)/100.0f;oz[i]=1.95f;osz[i]=0.08f+(float)(rand()%80)/400.0f;oc++;return;}}}
static void Frame(void){
float dt=0.016f,spd=speed*dt;
if(dead){if(key_sp){dead=0;score=0;speed=0.4f;px=py=0;oc=0;st=0;for(int i=0;i<NOBS;i++)oa[i]=0;}}
else{
if(key_l)px-=spd;if(key_r)px+=spd;if(key_u)py+=spd;if(key_d)py-=spd;
if(px<-1.3f)px=-1.3f;if(px>1.3f)px=1.3f;if(py<-0.9f)py=-0.9f;if(py>0.9f)py=0.9f;
st+=dt;if(st>0.6f/speed){Spawn();st=0;}
for(int i=0;i<NOBS;i++){
if(!oa[i])continue;oz[i]-=speed*dt;
if(oz[i]<1.0f){oa[i]=0;oc--;score++;speed=0.4f+score*0.05f;if(speed>2.0f)speed=2.0f;}
float dx=px>ox[i]?px-ox[i]:ox[i]-px;float dy=py>oy[i]?py-oy[i]:oy[i]-py;float dz=1.5f>oz[i]?1.5f-oz[i]:oz[i]-1.5f;
float md=0.12f+osz[i];if(dx<md&&dy<md&&dz<md)dead=1;
}
}
float fov=1.0f/tanf(0.4f),asp=1280.0f/720.0f;
float mvp[16];memset(mvp,0,sizeof(mvp));
mvp[0]=fov/asp;mvp[5]=-fov;mvp[10]=1;mvp[11]=-1;mvp[14]=0.1f;mvp[15]=1;
float cl[4]={.08f,.1f,.14f,1};if(dead){cl[0]=.12f;cl[1]=.02f;cl[2]=.02f;}
g_ctx->lpVtbl->OMSetRenderTargets(g_ctx,1,&g_rtv,NULL);g_ctx->lpVtbl->ClearRenderTargetView(g_ctx,g_rtv,cl);
D3D11_VIEWPORT vp;memset(&vp,0,sizeof(vp));vp.Width=1280;vp.Height=720;vp.MaxDepth=1;g_ctx->lpVtbl->RSSetViewports(g_ctx,1,&vp);
D3D11_RASTERIZER_DESC rd;memset(&rd,0,sizeof(rd));rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;ID3D11RasterizerState*rs;g_dev->lpVtbl->CreateRasterizerState(g_dev,&rd,&rs);g_ctx->lpVtbl->RSSetState(g_ctx,rs);
D3D11_DEPTH_STENCIL_DESC ds;memset(&ds,0,sizeof(ds));ds.DepthEnable=FALSE;ID3D11DepthStencilState*dss;g_dev->lpVtbl->CreateDepthStencilState(g_dev,&ds,&dss);g_ctx->lpVtbl->OMSetDepthStencilState(g_ctx,dss,0);
g_ctx->lpVtbl->IASetPrimitiveTopology(g_ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);g_ctx->lpVtbl->VSSetShader(g_ctx,g_vs,NULL,0);g_ctx->lpVtbl->PSSetShader(g_ctx,g_ps,NULL,0);g_ctx->lpVtbl->IASetInputLayout(g_ctx,g_il);g_ctx->lpVtbl->VSSetConstantBuffers(g_ctx,0,1,&g_cb);g_ctx->lpVtbl->UpdateSubresource(g_ctx,(ID3D11Resource*)g_cb,0,NULL,mvp,0,0);
if(dead){Cube(0,0,1.5f,0.4f,.9f,.1f,.1f);}
else{
Cube(px,py,1.5f,0.12f,.1f,.9f,.3f);
for(int i=0;i<NOBS;i++){if(!oa[i])continue;float fl=.6f+.4f*sinf(oz[i]*8.0f);Cube(ox[i],oy[i],oz[i],osz[i],fl,.15f,.1f);}
}
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);g_sc->lpVtbl->Present(g_sc,1,0);
}
static LRESULT CALLBACK WP(HWND h,UINT m,WPARAM w,LPARAM l){switch(m){case WM_KEYDOWN:switch(w){case VK_LEFT:key_l=1;break;case VK_RIGHT:key_r=1;break;case VK_UP:key_u=1;break;case VK_DOWN:key_d=1;break;case 'A':key_l=1;break;case 'D':key_r=1;break;case 'W':key_u=1;break;case 'S':key_d=1;break;case VK_SPACE:key_sp=1;break;case VK_ESCAPE:PostQuitMessage(0);break;}break;case WM_KEYUP:switch(w){case VK_LEFT:key_l=0;break;case VK_RIGHT:key_r=0;break;case VK_UP:key_u=0;break;case VK_DOWN:key_d=0;break;case 'A':key_l=0;break;case 'D':key_r=0;break;case 'W':key_u=0;break;case 'S':key_d=0;break;case VK_SPACE:key_sp=0;break;}break;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProcW(h,m,w,l);}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int){srand(42);WNDCLASSEXW wc;memset(&wc,0,sizeof(wc));wc.cbSize=sizeof(wc);wc.style=CS_CLASSDC;wc.lpfnWndProc=WP;wc.hInstance=hI;wc.lpszClassName=L"dodge";RegisterClassExW(&wc);HWND hwnd=CreateWindowW(L"dodge",L"Cube Dodge - WASD/Arrows move, Space restart",WS_OVERLAPPEDWINDOW,200,100,1280,720,NULL,NULL,hI,NULL);if(!hwnd)return -1;ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);Init(hwnd);MSG msg;while(1){while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);if(msg.message==WM_QUIT)goto done;}Frame();}done:if(g_cb)g_cb->lpVtbl->Release(g_cb);if(g_il)g_il->lpVtbl->Release(g_il);if(g_vs)g_vs->lpVtbl->Release(g_vs);if(g_ps)g_ps->lpVtbl->Release(g_ps);if(g_rtv)g_rtv->lpVtbl->Release(g_rtv);if(g_sc)g_sc->lpVtbl->Release(g_sc);if(g_ctx)g_ctx->lpVtbl->Release(g_ctx);if(g_dev)g_dev->lpVtbl->Release(g_dev);return 0;}
+95
View File
@@ -0,0 +1,95 @@
// 独立 D3D11 测试 - 不依赖引擎框架
#include <windows.h>
#include <d3d11.h>
#include <dxgi.h>
#include <stdio.h>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
static ID3D11Device* device = NULL;
static ID3D11DeviceContext* context = NULL;
static IDXGISwapChain* swap_chain = NULL;
static ID3D11RenderTargetView* rtv = NULL;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (msg == WM_DESTROY) { PostQuitMessage(0); return 0; }
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
(void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow;
// 注册窗口类
WNDCLASSEXW wc = {0};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"D3D11Test";
RegisterClassExW(&wc);
// 创建窗口
HWND hwnd = CreateWindowExW(0, L"D3D11Test", L"D3D11 Test",
WS_OVERLAPPEDWINDOW, 100, 100, 800, 600,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// 创建 D3D11 设备和交换链
DXGI_SWAP_CHAIN_DESC scd = {0};
scd.BufferCount = 1;
scd.BufferDesc.Width = 800;
scd.BufferDesc.Height = 600;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferDesc.RefreshRate.Numerator = 60;
scd.BufferDesc.RefreshRate.Denominator = 1;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hwnd;
scd.SampleDesc.Count = 1;
scd.Windowed = TRUE;
scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL levels[] = {D3D_FEATURE_LEVEL_11_0};
HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, levels, 1, D3D11_SDK_VERSION, &scd, &swap_chain, &device, NULL, &context);
if (FAILED(hr)) {
MessageBoxW(hwnd, L"D3D11 CreateDevice Failed!", L"Error", MB_OK);
return -1;
}
// 创建渲染目标
ID3D11Texture2D* back_buffer = NULL;
swap_chain->lpVtbl->GetBuffer(swap_chain, 0, &IID_ID3D11Texture2D, (void**)&back_buffer);
device->lpVtbl->CreateRenderTargetView(device, (ID3D11Resource*)back_buffer, NULL, &rtv);
back_buffer->lpVtbl->Release(back_buffer);
// 设置渲染目标
context->lpVtbl->OMSetRenderTargets(context, 1, &rtv, NULL);
// 主循环
MSG msg = {0};
while (msg.message != WM_QUIT) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// 清屏 - 蓝色
float color[4] = {0.2f, 0.5f, 0.8f, 1.0f};
context->lpVtbl->ClearRenderTargetView(context, rtv, color);
// 呈现
swap_chain->lpVtbl->Present(swap_chain, 0, 0);
}
// 清理
rtv->lpVtbl->Release(rtv);
swap_chain->lpVtbl->Release(swap_chain);
context->lpVtbl->Release(context);
device->lpVtbl->Release(device);
return 0;
}
+682
View File
@@ -0,0 +1,682 @@
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Core/asset_manager.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/renderer.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/material.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/Rendering/camera.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/Scene/scene_render.h"
#include "../Engine/Scene/scene_serialize.h"
#include "../Engine/Editor/glyph_atlas.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include <d3dcompiler.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
/* -------------------------------------------------------------------------- */
/* Editor state */
/* -------------------------------------------------------------------------- */
static Engine* g_engine = NULL;
static GraphicsDevice* g_gdev = NULL;
static Renderer* g_renderer = NULL;
static AssetManager* g_assets = NULL;
static Asset* g_shader_asset = NULL;
static Scene* g_scene = NULL;
static Camera g_camera;
static GlyphAtlas g_atlas;
static int g_atlas_ok = 0;
static SceneNode* g_selected = NULL;
static int g_mouse_x = 0;
static int g_mouse_y = 0;
static int g_mouse_down[3] = {0};
static int g_mouse_last_x = 0;
static int g_mouse_last_y = 0;
static int g_camera_drag = 0;
static char g_console[16][256];
static int g_console_count = 0;
static char g_status[256] = "";
/* UI metrics */
#define ED_MENU_H 30
#define ED_TOOLBAR_H 30
#define ED_LEFT_W 240
#define ED_RIGHT_W 260
#define ED_BOTTOM_H 180
#define ED_SPLITTER 1
/* UI D3D resources */
static ID3D11DepthStencilState* g_ui_depth = NULL;
static ID3D11RasterizerState* g_ui_raster = NULL;
static ID3D11BlendState* g_ui_blend = NULL;
static ID3D11SamplerState* g_ui_sampler = NULL;
static ID3D11Buffer* g_ui_cb = NULL;
static ID3D11VertexShader* g_ui_vs = NULL;
static ID3D11PixelShader* g_ui_ps = NULL;
static ID3D11InputLayout* g_ui_layout = NULL;
static ID3D11VertexShader* g_text_vs = NULL;
static ID3D11PixelShader* g_text_ps = NULL;
static ID3D11InputLayout* g_text_layout = NULL;
typedef struct { float p[3], n[3], uv[2], c[4]; } UIVertex;
/* -------------------------------------------------------------------------- */
/* Console */
/* -------------------------------------------------------------------------- */
static void console_add(const char* msg) {
if (g_console_count >= 16) {
for (int i = 0; i < 15; i++) strcpy(g_console[i], g_console[i+1]);
g_console_count = 15;
}
strncpy(g_console[g_console_count], msg, 255);
g_console[g_console_count][255] = 0;
g_console_count++;
}
/* -------------------------------------------------------------------------- */
/* Shader helpers */
/* -------------------------------------------------------------------------- */
static ID3DBlob* compile_blob(const char* code, const char* target) {
ID3DBlob *blob = NULL, *err = NULL;
D3DCompile(code, strlen(code), NULL, NULL, NULL, "main", target,
D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_PACK_MATRIX_ROW_MAJOR, 0, &blob, &err);
if (err) {
LOG_ERROR("Editor shader compile (%s): %s", target, (char*)err->lpVtbl->GetBufferPointer(err));
err->lpVtbl->Release(err);
}
return blob;
}
static int compile_ui_shaders(ID3D11Device* dev) {
const char* 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* ps =
"struct O{float4 p:SV_POSITION;float2 u:TEXCOORD;float4 c:COLOR;};"
"float4 main(O i):SV_TARGET{return i.c;}";
const char* tps =
"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);}";
ID3DBlob *b1 = compile_blob(vs, "vs_5_0"), *b2 = compile_blob(ps, "ps_5_0"), *b3 = compile_blob(tps, "ps_5_0");
if (!b1 || !b2 || !b3) return 0;
dev->lpVtbl->CreateVertexShader(dev, b1->lpVtbl->GetBufferPointer(b1), b1->lpVtbl->GetBufferSize(b1), NULL, &g_ui_vs);
dev->lpVtbl->CreatePixelShader(dev, b2->lpVtbl->GetBufferPointer(b2), b2->lpVtbl->GetBufferSize(b2), NULL, &g_ui_ps);
dev->lpVtbl->CreatePixelShader(dev, b3->lpVtbl->GetBufferPointer(b3), b3->lpVtbl->GetBufferSize(b3), NULL, &g_text_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), &g_ui_layout);
g_text_layout = g_ui_layout;
g_text_vs = g_ui_vs;
b1->lpVtbl->Release(b1); b2->lpVtbl->Release(b2); b3->lpVtbl->Release(b3);
return g_ui_vs && g_ui_ps && g_text_ps && g_ui_layout;
}
static void create_ui_states(ID3D11Device* dev) {
D3D11_DEPTH_STENCIL_DESC dsd = {0};
dsd.DepthEnable = FALSE; dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dev->lpVtbl->CreateDepthStencilState(dev, &dsd, &g_ui_depth);
D3D11_RASTERIZER_DESC rd = {0};
rd.FillMode = D3D11_FILL_SOLID; rd.CullMode = D3D11_CULL_NONE;
dev->lpVtbl->CreateRasterizerState(dev, &rd, &g_ui_raster);
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 = bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
bd.RenderTarget[0].RenderTargetWriteMask = 0x0F;
dev->lpVtbl->CreateBlendState(dev, &bd, &g_ui_blend);
D3D11_SAMPLER_DESC sd = {0};
sd.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sd.AddressU = sd.AddressV = sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
dev->lpVtbl->CreateSamplerState(dev, &sd, &g_ui_sampler);
D3D11_BUFFER_DESC cbd = {0};
cbd.ByteWidth = 256; cbd.Usage = D3D11_USAGE_DEFAULT; cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
dev->lpVtbl->CreateBuffer(dev, &cbd, NULL, &g_ui_cb);
}
/* -------------------------------------------------------------------------- */
/* UI rendering */
/* -------------------------------------------------------------------------- */
static void ui_set_projection(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;
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_ui_cb, 0, NULL, proj, 0, 0);
}
static void ui_setup_solid(ID3D11DeviceContext* ctx) {
ctx->lpVtbl->VSSetShader(ctx, g_ui_vs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, g_ui_ps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, g_ui_layout);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &g_ui_cb);
ctx->lpVtbl->OMSetBlendState(ctx, NULL, NULL, 0xFFFFFFFF);
}
static void ui_setup_text(ID3D11DeviceContext* ctx) {
ctx->lpVtbl->VSSetShader(ctx, g_text_vs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, g_text_ps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, g_text_layout);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &g_ui_cb);
ctx->lpVtbl->PSSetShaderResources(ctx, 0, 1, &g_atlas.srv);
ctx->lpVtbl->PSSetSamplers(ctx, 0, 1, &g_ui_sampler);
ctx->lpVtbl->OMSetBlendState(ctx, g_ui_blend, NULL, 0xFFFFFFFF);
}
static void ui_begin(ID3D11DeviceContext* ctx, float sw, float sh) {
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_ui_raster);
ctx->lpVtbl->OMSetDepthStencilState(ctx, g_ui_depth, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ui_set_projection(ctx, sw, sh);
ui_setup_solid(ctx);
}
static void draw_quad(ID3D11DeviceContext* ctx, float x, float y, float w, float h,
float r, float g, float b, float a) {
UIVertex 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(UIVertex)*4; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = v; g_gdev->device->lpVtbl->CreateBuffer(g_gdev->device, &bd, &sd, &vb);
bd.ByteWidth = sizeof(uint32_t)*6; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = idx; g_gdev->device->lpVtbl->CreateBuffer(g_gdev->device, &bd, &sd, &ib);
UINT stride = sizeof(UIVertex), 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);
if (ib) ib->lpVtbl->Release(ib);
if (vb) 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) {
if (!g_atlas_ok) return;
ui_setup_text(ctx);
for (const wchar_t* p = text; *p; p++) {
float u0, v0, u1, v1, ax, ay;
if (!glyph_atlas_get_or_create(&g_atlas, g_gdev->device, ctx, *p, &u0, &v0, &u1, &v1, &ax, &ay)) continue;
UIVertex tv[4] = {
{{(float)x, (float)y, 0},{0,0,1},{u0,v0},{r,g,b,a}},
{{(float)x + ax, (float)y, 0},{0,0,1},{u1,v0},{r,g,b,a}},
{{(float)x + ax, (float)y + ay, 0},{0,0,1},{u1,v1},{r,g,b,a}},
{{(float)x, (float)y + ay, 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(UIVertex)*4; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = tv; g_gdev->device->lpVtbl->CreateBuffer(g_gdev->device, &bd, &sd, &tvb);
bd.ByteWidth = sizeof(uint32_t)*6; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = ti; g_gdev->device->lpVtbl->CreateBuffer(g_gdev->device, &bd, &sd, &tib);
UINT stride = sizeof(UIVertex), offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &tvb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, tib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->DrawIndexed(ctx, 6, 0, 0);
if (tib) tib->lpVtbl->Release(tib);
if (tvb) tvb->lpVtbl->Release(tvb);
x += (int)ax + 1;
}
ui_setup_solid(ctx);
}
static void draw_text_a(ID3D11DeviceContext* ctx, const char* text, int x, int y,
float r, float g, float b, float a) {
wchar_t buf[256];
int n = (int)strlen(text); if (n > 255) n = 255;
for (int i = 0; i < n; i++) buf[i] = (wchar_t)text[i];
buf[n] = 0;
draw_text(ctx, buf, x, y, r, g, b, a);
}
/* -------------------------------------------------------------------------- */
/* Scene helpers */
/* -------------------------------------------------------------------------- */
static SceneNode* create_primitive(Scene* scene, SceneNode* parent, AssetManager* am,
ID3D11Device* dev, const char* name, MeshType type,
Vec3 pos, Vec3 rot, Vec3 scale, Material* mat) {
SceneNode* node = scene_create_node(scene, name);
if (!node) return NULL;
if (parent) scene_set_parent(node, parent);
node->position = pos;
node->scale = scale;
quat_from_euler(rot.x, rot.y, rot.z, &node->rotation);
Asset* mesh_asset = asset_manager_load_mesh(am, dev, name);
if (!mesh_asset) {
const char* builtin = NULL;
if (type == MESH_CUBE) builtin = "cube";
else if (type == MESH_SPHERE) builtin = "sphere";
else if (type == MESH_PLANE) builtin = "plane";
if (builtin) mesh_asset = asset_manager_load_mesh(am, dev, builtin);
}
if (mesh_asset && mat) {
MeshRenderer* mr = mesh_renderer_create(type, (Mesh*)asset_data(mesh_asset), mat);
scene_node_set_mesh_renderer(node, mr);
}
return node;
}
static Scene* create_default_editor_scene(AssetManager* am, ID3D11Device* dev, Shader* shader) {
Scene* scene = (Scene*)malloc(sizeof(Scene));
if (!scene) return NULL;
scene_init(scene);
strncpy(scene->root->name, "root", sizeof(scene->root->name) - 1);
Material* mat_red = material_create(shader, NULL);
mat_red->color = (Vec4){1.0f, 0.3f, 0.2f, 1.0f};
Material* mat_blue = material_create(shader, NULL);
mat_blue->color = (Vec4){0.2f, 0.5f, 1.0f, 1.0f};
Material* mat_ground = material_create(shader, NULL);
mat_ground->color = (Vec4){0.3f, 0.3f, 0.35f, 1.0f};
create_primitive(scene, scene->root, am, dev, "cube", MESH_CUBE,
(Vec3){0,0,0}, (Vec3){0,0,0}, (Vec3){1,1,1}, mat_red);
create_primitive(scene, scene->root, am, dev, "sphere", MESH_SPHERE,
(Vec3){2.0f,0,0}, (Vec3){0,0,0}, (Vec3){0.8f,0.8f,0.8f}, mat_blue);
create_primitive(scene, scene->root, am, dev, "ground", MESH_PLANE,
(Vec3){0,-1.0f,0}, (Vec3){0,0,0}, (Vec3){1,1,1}, mat_ground);
scene_update(scene);
return scene;
}
/* -------------------------------------------------------------------------- */
/* Editor UI */
/* -------------------------------------------------------------------------- */
static void draw_panel_bg(ID3D11DeviceContext* ctx, float sw, float sh) {
float body_h = sh - ED_MENU_H - ED_TOOLBAR_H - ED_BOTTOM_H;
/* menu bar */
draw_quad(ctx, 0, 0, sw, ED_MENU_H, 0.14f, 0.14f, 0.18f, 1.0f);
/* toolbar */
draw_quad(ctx, 0, ED_MENU_H, sw, ED_TOOLBAR_H, 0.20f, 0.20f, 0.26f, 1.0f);
/* separators */
draw_quad(ctx, 0, ED_MENU_H, sw, 1, 0.30f, 0.45f, 0.75f, 1.0f);
draw_quad(ctx, 0, ED_MENU_H + ED_TOOLBAR_H, sw, 1, 0.30f, 0.45f, 0.75f, 1.0f);
/* left panel */
draw_quad(ctx, 0, ED_MENU_H + ED_TOOLBAR_H + 1, ED_LEFT_W, body_h, 0.18f, 0.18f, 0.23f, 1.0f);
draw_quad(ctx, ED_LEFT_W, ED_MENU_H + ED_TOOLBAR_H + 1, ED_SPLITTER, body_h, 0.30f, 0.45f, 0.75f, 1.0f);
/* right panel */
draw_quad(ctx, sw - ED_RIGHT_W, ED_MENU_H + ED_TOOLBAR_H + 1, ED_RIGHT_W, body_h, 0.18f, 0.18f, 0.23f, 1.0f);
draw_quad(ctx, sw - ED_RIGHT_W - ED_SPLITTER, ED_MENU_H + ED_TOOLBAR_H + 1, ED_SPLITTER, body_h, 0.30f, 0.45f, 0.75f, 1.0f);
/* bottom console */
draw_quad(ctx, 0, sh - ED_BOTTOM_H - ED_SPLITTER, sw, ED_SPLITTER, 0.30f, 0.45f, 0.75f, 1.0f);
draw_quad(ctx, 0, sh - ED_BOTTOM_H, sw, ED_BOTTOM_H, 0.16f, 0.16f, 0.21f, 1.0f);
/* status bar */
draw_quad(ctx, 0, sh - 22, sw, 22, 0.20f, 0.22f, 0.32f, 1.0f);
}
static void draw_hierarchy(ID3D11DeviceContext* ctx, float sh) {
(void)sh;
int y = ED_MENU_H + ED_TOOLBAR_H + 28;
draw_text(ctx, L"\u573a\u666f\u5c42\u7ea7", 12, y - 20, 0.5f, 0.75f, 1.0f, 1.0f);
if (!g_scene) return;
for (int i = 1; i < g_scene->node_count; i++) {
SceneNode* node = &g_scene->nodes[i];
if (node->id < 0) continue;
int row_y = y + (i - 1) * 22;
if (node == g_selected)
draw_quad(ctx, 4, (float)row_y - 2, ED_LEFT_W - 8, 22, 0.25f, 0.38f, 0.65f, 1.0f);
wchar_t wn[64];
int len = (int)strlen(node->name); if (len > 63) len = 63;
for (int j = 0; j < len; j++) wn[j] = (wchar_t)node->name[j];
wn[len] = 0;
draw_text(ctx, wn, 16, row_y, 1.0f, 1.0f, 1.0f, 1.0f);
}
}
static void draw_inspector(ID3D11DeviceContext* ctx, float sw, float sh) {
(void)sh;
int x = (int)(sw - ED_RIGHT_W + 12);
int y = ED_MENU_H + ED_TOOLBAR_H + 28;
draw_text(ctx, L"\u5c5e\u6027", x, y - 20, 0.5f, 0.75f, 1.0f, 1.0f);
if (!g_selected) {
draw_text_a(ctx, "未选择对象", x, y, 0.7f, 0.7f, 0.75f, 1.0f);
return;
}
char buf[128];
int iy = y;
snprintf(buf, sizeof(buf), "名称: %s", g_selected->name); draw_text_a(ctx, buf, x, iy, 1.0f, 1.0f, 1.0f, 1.0f); iy += 24;
float rx, ry, rz;
quat_to_euler(&g_selected->rotation, &rx, &ry, &rz);
snprintf(buf, sizeof(buf), "位置: %.2f %.2f %.2f", g_selected->position.x, g_selected->position.y, g_selected->position.z);
draw_text_a(ctx, buf, x, iy, 0.85f, 0.85f, 0.88f, 1.0f); iy += 22;
snprintf(buf, sizeof(buf), "旋转: %.1f %.1f %.1f", rx * 57.2958f, ry * 57.2958f, rz * 57.2958f);
draw_text_a(ctx, buf, x, iy, 0.85f, 0.85f, 0.88f, 1.0f); iy += 22;
snprintf(buf, sizeof(buf), "缩放: %.2f %.2f %.2f", g_selected->scale.x, g_selected->scale.y, g_selected->scale.z);
draw_text_a(ctx, buf, x, iy, 0.85f, 0.85f, 0.88f, 1.0f); iy += 30;
draw_text_a(ctx, "WASD 平移 | QE 升降", x, iy, 0.6f, 0.7f, 0.85f, 1.0f); iy += 20;
draw_text_a(ctx, "方向键 旋转 | +/- 缩放", x, iy, 0.6f, 0.7f, 0.85f, 1.0f);
}
static void draw_console(ID3D11DeviceContext* ctx, float sh) {
int y = (int)(sh - ED_BOTTOM_H + 24);
draw_text(ctx, L"\u63a7\u5236\u53f0", 12, y - 18, 0.5f, 0.75f, 1.0f, 1.0f);
int cy = y;
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[i]); if (len > 255) len = 255;
for (int j = 0; j < len; j++) wl[j] = (wchar_t)g_console[i][j];
wl[len] = 0;
draw_text(ctx, wl, 12, cy, 0.75f, 0.75f, 0.80f, 1.0f);
cy += 20;
}
}
static void draw_ui(ID3D11DeviceContext* ctx, float sw, float sh) {
ui_begin(ctx, sw, sh);
draw_quad(ctx, 0, 0, sw, sh, 0.5f, 0, 0, 0.3f); /* debug overlay */
draw_panel_bg(ctx, sw, sh);
/* title / menus */
draw_text(ctx, L"Lity3D Engine 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 = 160;
for (int i = 0; i < 5; i++) { draw_text(ctx, menus[i], mx, 8, 0.9f, 0.9f, 0.92f, 1.0f); mx += 70; }
draw_hierarchy(ctx, sh);
draw_inspector(ctx, sw, sh);
draw_console(ctx, sh);
draw_text_a(ctx, g_status, 12, (int)(sh - 18), 0.65f, 0.65f, 0.70f, 1.0f);
}
/* -------------------------------------------------------------------------- */
/* Input / interaction */
/* -------------------------------------------------------------------------- */
static void update_status(void) {
if (g_selected) {
snprintf(g_status, sizeof(g_status),
"%s | pos(%.2f,%.2f,%.2f) | FOV %.0f | Esc quit | Ctrl+S save",
g_selected->name,
g_selected->position.x, g_selected->position.y, g_selected->position.z,
g_camera.fov_y * 57.2958f);
} else {
snprintf(g_status, sizeof(g_status), "无选中对象 | FOV %.0f | Esc quit | Ctrl+S save", g_camera.fov_y * 57.2958f);
}
}
static void process_hierarchy_click(int mx, int my) {
float body_top = ED_MENU_H + ED_TOOLBAR_H + 1;
float body_h = (float)g_gdev->height - ED_MENU_H - ED_TOOLBAR_H - ED_BOTTOM_H;
if (mx < 0 || mx > ED_LEFT_W || my < body_top || my > body_top + body_h) return;
int list_y = ED_MENU_H + ED_TOOLBAR_H + 28;
int idx = (my - list_y + 2) / 22 + 1;
if (idx >= 1 && idx < g_scene->node_count) {
SceneNode* node = &g_scene->nodes[idx];
if (node->id >= 0) {
g_selected = node;
char buf[128]; snprintf(buf, sizeof(buf), "选中: %s", node->name);
console_add(buf);
update_status();
}
}
}
static void on_mouse_move(Engine* engine, int x, int y) {
(void)engine;
g_mouse_x = x; g_mouse_y = y;
if (g_camera_drag && g_mouse_down[1]) {
float dx = (float)(x - g_mouse_last_x);
float dy = (float)(y - g_mouse_last_y);
camera_rotate_yaw(&g_camera, dx * 0.005f);
camera_rotate_pitch(&g_camera, dy * 0.005f);
g_mouse_last_x = x; g_mouse_last_y = y;
} else if (g_mouse_down[1]) {
g_camera_drag = 1;
g_mouse_last_x = x; g_mouse_last_y = y;
} else {
g_camera_drag = 0;
}
}
static void on_mouse_button(Engine* engine, int button, int pressed) {
(void)engine;
if (button >= 0 && button < 3) g_mouse_down[button] = pressed;
if (button == 0 && pressed) {
process_hierarchy_click(g_mouse_x, g_mouse_y);
}
}
static void on_key(Engine* engine, int key) {
if (key == VK_ESCAPE) { engine_request_quit(engine); return; }
if (GetAsyncKeyState(VK_CONTROL) & 0x8000) {
if (key == 'S' || key == 's') {
if (scene_save(g_scene, "scene.txt")) {
console_add("已保存 scene.txt");
} else {
console_add("保存 scene.txt 失败");
}
return;
}
if (key == 'O' || key == 'o') {
Scene* new_scene = scene_load("scene.txt", g_gdev->device, (Shader*)asset_data(g_shader_asset));
if (new_scene) {
scene_destroy_renderers(g_scene);
scene_destroy(g_scene);
free(g_scene);
g_scene = new_scene;
g_selected = NULL;
console_add("已加载 scene.txt");
} else {
console_add("加载 scene.txt 失败");
}
return;
}
}
if (!g_selected) return;
float speed = 0.1f;
if (GetAsyncKeyState(VK_SHIFT) & 0x8000) speed = 0.5f;
Vec3 pos = g_selected->position;
switch (key) {
case 'W': case 'w': pos.z += speed; break;
case 'S': case 's': pos.z -= speed; break;
case 'A': case 'a': pos.x -= speed; break;
case 'D': case 'd': pos.x += speed; break;
case 'Q': case 'q': pos.y += speed; break;
case 'E': case 'e': pos.y -= speed; break;
default: break;
}
if (pos.x != g_selected->position.x || pos.y != g_selected->position.y || pos.z != g_selected->position.z) {
scene_set_position(g_selected, pos.x, pos.y, pos.z);
}
float rx, ry, rz;
quat_to_euler(&g_selected->rotation, &rx, &ry, &rz);
int rot_changed = 0;
switch (key) {
case VK_LEFT: ry -= 0.05f; rot_changed = 1; break;
case VK_RIGHT: ry += 0.05f; rot_changed = 1; break;
case VK_UP: rx -= 0.05f; rot_changed = 1; break;
case VK_DOWN: rx += 0.05f; rot_changed = 1; break;
case VK_PRIOR: rz += 0.05f; rot_changed = 1; break; /* PageUp */
case VK_NEXT: rz -= 0.05f; rot_changed = 1; break; /* PageDown */
default: break;
}
if (rot_changed) scene_set_rotation_euler(g_selected, rx, ry, rz);
Vec3 scale = g_selected->scale;
int scale_changed = 0;
switch (key) {
case VK_ADD: scale.x += 0.1f; scale.y += 0.1f; scale.z += 0.1f; scale_changed = 1; break;
case VK_SUBTRACT: scale.x -= 0.1f; scale.y -= 0.1f; scale.z -= 0.1f; scale_changed = 1; break;
default: break;
}
if (scale_changed) scene_set_scale(g_selected, scale.x, scale.y, scale.z);
update_status();
}
static void on_resize(Engine* engine, int width, int height) {
(void)engine;
if (width > 0 && height > 0) {
renderer_resize(g_renderer, width, height);
g_camera.aspect = (float)width / (float)height;
g_camera.dirty = 1;
}
}
/* -------------------------------------------------------------------------- */
/* Render callback */
/* -------------------------------------------------------------------------- */
static void on_render(Engine* engine) {
(void)engine;
if (!g_renderer || !g_gdev) return;
renderer_set_view(g_renderer, camera_get_view(&g_camera));
renderer_set_projection(g_renderer, camera_get_projection(&g_camera));
renderer_begin_frame(g_renderer);
if (g_scene) {
scene_update(g_scene);
scene_render(g_scene, g_renderer);
}
draw_ui(g_gdev->context, (float)g_gdev->width, (float)g_gdev->height);
renderer_end_frame(g_renderer);
if (engine->frame_count == 60) {
graphics_device_save_screenshot(g_gdev, "editor_screenshot.bmp");
console_add("已保存 editor_screenshot.bmp");
}
}
static void on_update(Engine* engine, double dt) {
(void)engine; (void)dt;
}
/* -------------------------------------------------------------------------- */
/* Main */
/* -------------------------------------------------------------------------- */
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR cmd, int nS) {
(void)hI; (void)hP; (void)nS;
logger_init("engine.log");
LOG_INFO("=== Lity3D Engine Editor ===");
EngineConfig cfg = engine_default_config();
cfg.app_name = "Lity3D Engine Editor";
cfg.window_width = 1440;
cfg.window_height = 900;
cfg.vsync = 1;
g_engine = engine_create();
if (!engine_init(g_engine, &cfg)) {
LOG_FATAL("Engine init failed");
return -1;
}
g_gdev = engine_get_graphics_device(g_engine);
g_renderer = engine_get_renderer(g_engine);
g_assets = asset_manager_create();
g_shader_asset = asset_manager_load_shader(g_assets, g_gdev->device, NULL, NULL);
if (!g_shader_asset) {
LOG_FATAL("Shader load failed");
return -1;
}
Shader* default_shader = (Shader*)asset_data(g_shader_asset);
g_scene = scene_load("scene.txt", g_gdev->device, default_shader);
if (!g_scene) {
LOG_INFO("Creating default editor scene");
g_scene = create_default_editor_scene(g_assets, g_gdev->device, default_shader);
scene_save(g_scene, "scene.txt");
}
float aspect = (float)cfg.window_width / cfg.window_height;
g_camera = camera_perspective(1.0472f, aspect, 0.1f, 100.0f);
g_camera.position = (Vec3){0, 1.5f, -5.0f};
g_camera.target = (Vec3){0, 0, 0};
g_camera.dirty = 1;
g_atlas_ok = glyph_atlas_init(&g_atlas, g_gdev->device);
if (!g_atlas_ok) LOG_WARN("GlyphAtlas init failed");
create_ui_states(g_gdev->device);
if (!compile_ui_shaders(g_gdev->device)) {
LOG_FATAL("UI shader compile failed");
return -1;
}
console_add("Lity3D Engine Editor ready");
console_add("Ctrl+S 保存 | Ctrl+O 加载 | Esc 退出");
console_add("左键点选层级,右键拖拽旋转视角");
update_status();
engine_set_update_callback(g_engine, on_update);
engine_set_render_callback(g_engine, on_render);
engine_set_key_callback(g_engine, on_key);
engine_set_resize_callback(g_engine, on_resize);
engine_set_mouse_move_callback(g_engine, on_mouse_move);
engine_set_mouse_button_callback(g_engine, on_mouse_button);
engine_run(g_engine);
if (g_scene) {
scene_destroy_renderers(g_scene);
scene_destroy(g_scene);
free(g_scene);
}
asset_manager_destroy(g_assets);
if (g_ui_sampler) g_ui_sampler->lpVtbl->Release(g_ui_sampler);
if (g_ui_blend) g_ui_blend->lpVtbl->Release(g_ui_blend);
if (g_ui_raster) g_ui_raster->lpVtbl->Release(g_ui_raster);
if (g_ui_depth) g_ui_depth->lpVtbl->Release(g_ui_depth);
if (g_ui_cb) g_ui_cb->lpVtbl->Release(g_ui_cb);
if (g_ui_ps) g_ui_ps->lpVtbl->Release(g_ui_ps);
if (g_ui_vs) g_ui_vs->lpVtbl->Release(g_ui_vs);
if (g_ui_layout) g_ui_layout->lpVtbl->Release(g_ui_layout);
if (g_atlas_ok) glyph_atlas_destroy(&g_atlas);
engine_shutdown(g_engine);
logger_shutdown();
return 0;
}
+222
View File
@@ -0,0 +1,222 @@
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <math.h>
#include <stdint.h>
#include <string.h>
typedef struct { float p[3], n[3], u[2], c[4]; } V3D;
static ID3D11Device* g_dev = NULL;
static ID3D11DeviceContext* g_ctx = NULL;
static IDXGISwapChain* g_sc = NULL;
static ID3D11RenderTargetView* g_rtv = NULL;
static ID3D11VertexShader* g_vs = NULL;
static ID3D11PixelShader* g_ps = NULL;
static ID3D11InputLayout* g_il = NULL;
static ID3D11Buffer* g_cb = NULL;
static float cam_x = 0, cam_z = 0;
static int key_w = 0, key_s = 0, key_a = 0, key_d = 0;
static int key_left = 0, key_right = 0;
static int sel = 0;
static void Init(HWND h) {
HRESULT hr;
DXGI_SWAP_CHAIN_DESC sd;
memset(&sd, 0, sizeof(sd));
sd.BufferCount = 2;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = h;
sd.SampleDesc.Count = 1;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_0;
D3D_FEATURE_LEVEL got;
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &fl, 1,
D3D11_SDK_VERSION, &sd, &g_sc, &g_dev, &got, &g_ctx);
if (FAILED(hr)) { return; }
ID3D11Texture2D* bb = NULL;
hr = g_sc->lpVtbl->GetBuffer(g_sc, 0, &IID_ID3D11Texture2D, (void**)&bb);
if (FAILED(hr) || !bb) { return; }
hr = g_dev->lpVtbl->CreateRenderTargetView(g_dev, (ID3D11Resource*)bb, NULL, &g_rtv);
bb->lpVtbl->Release(bb);
if (FAILED(hr)) { return; }
const char* vs_src =
"cbuffer CB:register(b0){float4x4 WVP;};"
"struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};"
"struct O{float4 p:SV_POSITION;float4 c:COLOR;};"
"O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.c=i.c;return o;}";
const char* ps_src =
"float4 main(float4 pos:SV_POSITION, float4 col:COLOR):SV_Target{return col;}";
ID3DBlob *b1 = NULL, *b2 = NULL, *e1 = NULL, *e2 = NULL;
D3DCompile(vs_src, (UINT)strlen(vs_src), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &b1, &e1);
if (e1) { e1->lpVtbl->Release(e1); e1 = NULL; }
D3DCompile(ps_src, (UINT)strlen(ps_src), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &b2, &e2);
if (e2) { e2->lpVtbl->Release(e2); e2 = NULL; }
if (!b1 || !b2) { return; }
g_dev->lpVtbl->CreateVertexShader(g_dev, b1->lpVtbl->GetBufferPointer(b1), b1->lpVtbl->GetBufferSize(b1), NULL, &g_vs);
g_dev->lpVtbl->CreatePixelShader(g_dev, b2->lpVtbl->GetBufferPointer(b2), b2->lpVtbl->GetBufferSize(b2), NULL, &g_ps);
D3D11_INPUT_ELEMENT_DESC el[4];
memset(el, 0, sizeof(el));
el[0].SemanticName = "POSITION"; el[0].Format = DXGI_FORMAT_R32G32B32_FLOAT; el[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
el[1].SemanticName = "NORMAL"; el[1].AlignedByteOffset = 12; el[1].Format = DXGI_FORMAT_R32G32B32_FLOAT; el[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
el[2].SemanticName = "TEXCOORD"; el[2].AlignedByteOffset = 24; el[2].Format = DXGI_FORMAT_R32G32_FLOAT; el[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
el[3].SemanticName = "COLOR"; el[3].AlignedByteOffset = 32; el[3].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; el[3].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
g_dev->lpVtbl->CreateInputLayout(g_dev, el, 4, b1->lpVtbl->GetBufferPointer(b1), b1->lpVtbl->GetBufferSize(b1), &g_il);
b1->lpVtbl->Release(b1); b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd;
memset(&cbd, 0, sizeof(cbd));
cbd.ByteWidth = 256; cbd.Usage = D3D11_USAGE_DEFAULT; cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
g_dev->lpVtbl->CreateBuffer(g_dev, &cbd, NULL, &g_cb);
}
static void DrawCube(float cx, float cy, float cz, float s, float cr, float cg, float cb2) {
V3D v[24];
float f[6][4][3] = {
{{-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}},
{{1,-1,-1},{1,-1,1},{1,1,1},{1,1,-1}},
{{1,-1,1},{-1,-1,1},{-1,1,1},{1,1,1}},
{{-1,-1,1},{-1,-1,-1},{-1,1,-1},{-1,1,1}},
{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1}},
{{-1,-1,1},{1,-1,1},{1,-1,-1},{-1,-1,-1}}
};
float nm[6][3] = {{0,0,-1},{1,0,0},{0,0,1},{-1,0,0},{0,1,0},{0,-1,0}};
for (int fi = 0; fi < 6; fi++)
for (int vi = 0; vi < 4; vi++) {
int i = fi * 4 + vi;
v[i].p[0] = f[fi][vi][0] * s + cx;
v[i].p[1] = f[fi][vi][1] * s + cy;
v[i].p[2] = f[fi][vi][2] * s + cz;
v[i].n[0] = nm[fi][0]; v[i].n[1] = nm[fi][1]; v[i].n[2] = nm[fi][2];
v[i].c[0] = cr; v[i].c[1] = cg; v[i].c[2] = cb2; v[i].c[3] = 1;
}
uint32_t ix[36];
for (int fi = 0; fi < 6; fi++) {
int b = fi * 4;
ix[fi*6]=b; ix[fi*6+1]=b+1; ix[fi*6+2]=b+2; ix[fi*6+3]=b; ix[fi*6+4]=b+2; ix[fi*6+5]=b+3;
}
ID3D11Buffer *vb = NULL, *ib = NULL;
D3D11_BUFFER_DESC bd; D3D11_SUBRESOURCE_DATA sd;
memset(&bd, 0, sizeof(bd)); memset(&sd, 0, sizeof(sd));
bd.ByteWidth = sizeof(V3D) * 24; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = v; g_dev->lpVtbl->CreateBuffer(g_dev, &bd, &sd, &vb);
bd.ByteWidth = sizeof(uint32_t) * 36; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = ix; g_dev->lpVtbl->CreateBuffer(g_dev, &bd, &sd, &ib);
UINT s2 = sizeof(V3D), o = 0;
g_ctx->lpVtbl->IASetVertexBuffers(g_ctx, 0, 1, &vb, &s2, &o);
g_ctx->lpVtbl->IASetIndexBuffer(g_ctx, ib, DXGI_FORMAT_R32_UINT, 0);
g_ctx->lpVtbl->DrawIndexed(g_ctx, 36, 0, 0);
ib->lpVtbl->Release(ib); vb->lpVtbl->Release(vb);
}
static void Frame(void) {
float dt = 0.016f, spd = 3.0f * dt;
if (key_w) cam_z += spd; if (key_s) cam_z -= spd;
if (key_a) cam_x -= spd; if (key_d) cam_x += spd;
if (key_left) sel = (sel + 3) % 4;
if (key_right) sel = (sel + 1) % 4;
float fov = 1.0f / tanf(0.4f), asp = 1280.0f / 720.0f;
float mvp[16]; memset(mvp, 0, sizeof(mvp));
mvp[0] = fov / asp; mvp[5] = -fov; mvp[10] = 1; mvp[11] = -1; mvp[14] = 0.1f; mvp[15] = 1;
float m2[16]; memcpy(m2, mvp, 64);
for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { float t = m2[i*4+j]; m2[i*4+j] = m2[j*4+i]; m2[j*4+i] = t; }
float cl[4] = {.15f, .18f, .25f, 1};
g_ctx->lpVtbl->OMSetRenderTargets(g_ctx, 1, &g_rtv, NULL);
g_ctx->lpVtbl->ClearRenderTargetView(g_ctx, g_rtv, cl);
D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(vp));
vp.Width = 1280; vp.Height = 720; vp.MaxDepth = 1;
g_ctx->lpVtbl->RSSetViewports(g_ctx, 1, &vp);
D3D11_RASTERIZER_DESC rd; memset(&rd, 0, sizeof(rd));
rd.FillMode = D3D11_FILL_SOLID; rd.CullMode = D3D11_CULL_NONE;
ID3D11RasterizerState* rs = NULL;
g_dev->lpVtbl->CreateRasterizerState(g_dev, &rd, &rs);
g_ctx->lpVtbl->RSSetState(g_ctx, rs);
D3D11_DEPTH_STENCIL_DESC ds; memset(&ds, 0, sizeof(ds));
ds.DepthEnable = FALSE;
ID3D11DepthStencilState* dss = NULL;
g_dev->lpVtbl->CreateDepthStencilState(g_dev, &ds, &dss);
g_ctx->lpVtbl->OMSetDepthStencilState(g_ctx, dss, 0);
g_ctx->lpVtbl->IASetPrimitiveTopology(g_ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
g_ctx->lpVtbl->VSSetShader(g_ctx, g_vs, NULL, 0);
g_ctx->lpVtbl->PSSetShader(g_ctx, g_ps, NULL, 0);
g_ctx->lpVtbl->IASetInputLayout(g_ctx, g_il);
g_ctx->lpVtbl->VSSetConstantBuffers(g_ctx, 0, 1, &g_cb);
g_ctx->lpVtbl->UpdateSubresource(g_ctx, (ID3D11Resource*)g_cb, 0, NULL, m2, 0, 0);
float colors[4][3] = {{1,.3f,.3f},{.3f,1,.3f},{.3f,.3f,1},{.8f,.8f,.2f}};
float sizes[4] = {0.3f, 0.25f, 0.2f, 0.35f};
for (int i = 0; i < 4; i++) {
float cr = colors[i][0], cg = colors[i][1], cb = colors[i][2];
if (i == sel) { cr = 1; cg = 1; cb = 1; }
DrawCube(-0.8f + i * 0.6f - cam_x, 0, 1.5f - cam_z, sizes[i], cr, cg, cb);
}
if (rs) rs->lpVtbl->Release(rs);
if (dss) dss->lpVtbl->Release(dss);
g_sc->lpVtbl->Present(g_sc, 1, 0);
}
static LRESULT CALLBACK WP(HWND h, UINT m, WPARAM w, LPARAM l) {
switch (m) {
case WM_KEYDOWN:
switch (w) {
case 'W': key_w = 1; break; case 'S': key_s = 1; break;
case 'A': key_a = 1; break; case 'D': key_d = 1; break;
case VK_LEFT: key_left = 1; break; case VK_RIGHT: key_right = 1; break;
case '1': sel = 0; break; case '2': sel = 1; break;
case '3': sel = 2; break; case '4': sel = 3; break;
case VK_ESCAPE: PostQuitMessage(0); break;
}
break;
case WM_KEYUP:
switch (w) {
case 'W': key_w = 0; break; case 'S': key_s = 0; break;
case 'A': key_a = 0; break; case 'D': key_d = 0; break;
case VK_LEFT: key_left = 0; break; case VK_RIGHT: key_right = 0; break;
}
break;
case WM_DESTROY: PostQuitMessage(0); return 0;
}
return DefWindowProcW(h, m, w, l);
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR nC, int nS) {
(void)hP; (void)nC; (void)nS;
WNDCLASSEXW wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WP;
wc.hInstance = hI;
wc.lpszClassName = L"game_test_cls";
RegisterClassExW(&wc);
HWND hwnd = CreateWindowW(L"game_test_cls", L"lity3d Game Test", WS_OVERLAPPEDWINDOW,
100, 100, 1280, 720, NULL, NULL, hI, NULL);
if (!hwnd) { MessageBoxA(NULL, "CreateWindow failed", "Error", MB_OK); return -1; }
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
Init(hwnd);
MSG msg;
while (1) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg); DispatchMessage(&msg);
if (msg.message == WM_QUIT) goto done;
}
Frame();
}
done:
if (g_cb) g_cb->lpVtbl->Release(g_cb);
if (g_il) g_il->lpVtbl->Release(g_il);
if (g_vs) g_vs->lpVtbl->Release(g_vs);
if (g_ps) g_ps->lpVtbl->Release(g_ps);
if (g_rtv) g_rtv->lpVtbl->Release(g_rtv);
if (g_sc) g_sc->lpVtbl->Release(g_sc);
if (g_ctx) g_ctx->lpVtbl->Release(g_ctx);
if (g_dev) g_dev->lpVtbl->Release(g_dev);
return 0;
}
+89
View File
@@ -0,0 +1,89 @@
#include "../Engine/Core/engine.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 <d3dcompiler.h>
static GraphicsDevice* g_dev = NULL;
static ID3D11VertexShader* g_vs = NULL;
static ID3D11PixelShader* g_ps = NULL;
static ID3D11InputLayout* g_il = NULL;
static ID3D11Buffer* g_cb = NULL;
static float cam_x=0, cam_z=2, cam_yaw=0;
static float mx=0, my=0;
static int keys[256]={0};
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
static void draw_quad(ID3D11DeviceContext* ctx, float x, float y, float z, float s, float r, float g, float b) {
V v[4]={{{x-s,y-s,z},{0,0,-1},{0,0},{r,g,b,1}},{{x+s,y-s,z},{0,0,-1},{1,0},{r,g,b,1}},
{{x+s,y+s,z},{0,0,-1},{1,1},{r,g,b,1}},{{x-s,y+s,z},{0,0,-1},{0,1},{r,g,b,1}}};
uint32_t idx[6]={0,1,2,0,2,3};
ID3D11Buffer *vb=NULL,*ib=NULL; D3D11_BUFFER_DESC bd={}; D3D11_SUBRESOURCE_DATA sd={};
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 s2=sizeof(V),o=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&s2,&o);
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 on_render(Engine* engine) {
if(!g_dev) return;
ID3D11DeviceContext* ctx=g_dev->context;
float dt=(float)engine->delta_time, sp=3.0f*dt;
float fx=-sinf(cam_yaw),fz=-cosf(cam_yaw),rx=cosf(cam_yaw),rz=-sinf(cam_yaw);
if(keys['W']){cam_x+=fx*sp;cam_z+=fz*sp;}
if(keys['S']){cam_x-=fx*sp;cam_z-=fz*sp;}
if(keys['A']){cam_x-=rx*sp;cam_z-=rz*sp;}
if(keys['D']){cam_x+=rx*sp;cam_z+=rz*sp;}
float sw=(float)g_dev->width,sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f),asp=sw/sh;
float mvp[16]={0}; mvp[0]=fov/asp;mvp[5]=-fov;mvp[10]=1;mvp[11]=-1;mvp[14]=0.1f;mvp[15]=1;
Mat4 pm,pt;memcpy(&pm,mvp,sizeof(Mat4));mat4_transpose(&pm,&pt);
graphics_device_clear(g_dev,0.12f,0.14f,0.18f,1.0f);
D3D11_VIEWPORT vp={};vp.Width=sw;vp.Height=sh;vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={};rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState*rs=NULL;g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={};dsd.DepthEnable=FALSE;
ID3D11DepthStencilState*dss=NULL;g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->lpVtbl->VSSetShader(ctx,g_vs,NULL,0);ctx->lpVtbl->PSSetShader(ctx,g_ps,NULL,0);
ctx->lpVtbl->IASetInputLayout(ctx,g_il);ctx->lpVtbl->VSSetConstantBuffers(ctx,0,1,&g_cb);
ctx->lpVtbl->UpdateSubresource(ctx,(ID3D11Resource*)g_cb,0,NULL,&pt,0,0);
for(int x=-5;x<=5;x++)for(int z=-5;z<=5;z++){float c=((x+z)%2==0)?.4f:.3f;draw_quad(ctx,(float)x,(float)z,0.5f,0.5f,c,c,c);}
draw_quad(ctx,0,0,0.5f,0.4f,1,0.3f,0.3f);draw_quad(ctx,2,0,0.5f,0.3f,.3f,1,.3f);draw_quad(ctx,-2,0,0.5f,0.3f,.3f,.3f,1);draw_quad(ctx,0,2,0.5f,0.5f,.8f,.8f,.2f);
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine*e,int k){if(k>=0&&k<256)keys[k]=1;if(k==VK_ESCAPE)engine_request_quit(e);}
static void on_mouse_move(int x,int y){cam_yaw+=(x-mx)*0.003f;mx=(float)x;my=(float)y;}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int){
EngineConfig cfg=engine_default_config();cfg.app_name="lity3d Input Test";
Engine*e=engine_create();if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
ID3DBlob*b1=NULL,*b2=NULL,*er=NULL;
const char*vs="cbuffer CB:register(b0){float4x4 WVP;};struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.n=i.n;o.c=i.c;return o;}";
const char*ps="struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};float4 main(O i){return i.c;}";
D3DCompile(vs,(UINT)strlen(vs),NULL,NULL,NULL,"main","vs_5_0",0,0,&b1,&er);if(er){er->lpVtbl->Release(er);er=NULL;}
D3DCompile(ps,(UINT)strlen(ps),NULL,NULL,NULL,"main","ps_5_0",0,0,&b2,&er);if(er){er->lpVtbl->Release(er);er=NULL;}
ID3D11Device*dev=g_dev->device;
dev->lpVtbl->CreateVertexShader(dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);
dev->lpVtbl->CreatePixelShader(dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC il[]={{"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,il,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);b1->lpVtbl->Release(b1);b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd={};cbd.ByteWidth=256;cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;dev->lpVtbl->CreateBuffer(dev,&cbd,NULL,&g_cb);
window_set_key_callback(e->window,on_key);window_set_mouse_move_callback(e->window,on_mouse_move);
engine_set_render_callback(e,on_render);engine_run(e);
if(g_vs)g_vs->lpVtbl->Release(g_vs);if(g_ps)g_ps->lpVtbl->Release(g_ps);if(g_il)g_il->lpVtbl->Release(g_il);if(g_cb)g_cb->lpVtbl->Release(g_cb);
graphics_device_destroy(g_dev);engine_shutdown(e);return 0;
}
+142
View File
@@ -0,0 +1,142 @@
// light_test.c - 光照系统测试(基?raw3d_test 已验证模式)
#include "../Engine/Core/engine.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/lighting.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include <d3dcompiler.h>
static GraphicsDevice* g_dev = NULL;
static Light* g_sun = NULL;
static float g_time = 0;
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
// 编译带光照的着色器
static ID3D11VertexShader* g_vs = NULL;
static ID3D11PixelShader* g_ps = NULL;
static ID3D11InputLayout* g_il = NULL;
static ID3D11Buffer* g_cb = NULL;
static const char* vs_src =
"cbuffer CB:register(b0){float4x4 WVP;};"
"struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};"
"struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};"
"O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.n=i.n;o.c=i.c;return o;}";
static const char* ps_src =
"cbuffer Light:register(b1){float3 lightDir;float pad1;float3 lightColor;float intensity;float3 ambColor;float pad2;};"
"struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};"
"float4 main(O i):SV_TARGET{"
"float3 n=normalize(i.n);"
"float diff=max(dot(n,-normalize(lightDir)),0.0);"
"float3 col=i.c.rgb*(ambColor+lightColor*diff*intensity);"
"return float4(col,1.0);}";
static void InitShaders() {
ID3DBlob *b1=NULL,*b2=NULL,*e=NULL;
D3DCompile(vs_src,(UINT)strlen(vs_src),NULL,NULL,NULL,"main","vs_5_0",0,0,&b1,&e);
if(e){e->lpVtbl->Release(e);e=NULL;}
D3DCompile(ps_src,(UINT)strlen(ps_src),NULL,NULL,NULL,"main","ps_5_0",0,0,&b2,&e);
if(e){e->lpVtbl->Release(e);e=NULL;}
if(!b1||!b2) return;
ID3D11Device* dev = g_dev->device;
dev->lpVtbl->CreateVertexShader(dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);
dev->lpVtbl->CreatePixelShader(dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC il[]={
{"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,il,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);
b1->lpVtbl->Release(b1); b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd={}; cbd.ByteWidth=256; cbd.Usage=D3D11_USAGE_DEFAULT;
cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;
dev->lpVtbl->CreateBuffer(dev,&cbd,NULL,&g_cb);
}
static void draw_lit_cube(ID3D11DeviceContext* ctx, ID3D11Device* dev, float cx, float cy, float cz, float s, float cr, float cg, float cb) {
V v[24];
float f[6][4][3]={{{-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}},{{1,-1,-1},{1,-1,1},{1,1,1},{1,1,-1}},
{{1,-1,1},{-1,-1,1},{-1,1,1},{1,1,1}},{{-1,-1,1},{-1,-1,-1},{-1,1,-1},{-1,1,1}},
{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1}},{{-1,-1,1},{1,-1,1},{1,-1,-1},{-1,-1,-1}}};
float nm[6][3]={{0,0,-1},{1,0,0},{0,0,1},{-1,0,0},{0,1,0},{0,-1,0}};
for(int fi=0;fi<6;fi++) for(int vi=0;vi<4;vi++){
int i=fi*4+vi;
v[i].p[0]=f[fi][vi][0]*s+cx; v[i].p[1]=f[fi][vi][1]*s+cy; v[i].p[2]=f[fi][vi][2]*s+cz;
v[i].n[0]=nm[fi][0]; v[i].n[1]=nm[fi][1]; v[i].n[2]=nm[fi][2];
v[i].c[0]=cr; v[i].c[1]=cg; v[i].c[2]=cb; v[i].c[3]=1;
}
uint32_t idx[36]; for(int fi=0;fi<6;fi++){int b=fi*4;idx[fi*6]=b;idx[fi*6+1]=b+1;idx[fi*6+2]=b+2;idx[fi*6+3]=b;idx[fi*6+4]=b+2;idx[fi*6+5]=b+3;}
ID3D11Buffer *vb,*ib;D3D11_BUFFER_DESC bd={};D3D11_SUBRESOURCE_DATA sd={};
bd.ByteWidth=sizeof(V)*24;bd.Usage=D3D11_USAGE_DEFAULT;bd.BindFlags=D3D11_BIND_VERTEX_BUFFER;sd.pSysMem=v;
dev->lpVtbl->CreateBuffer(dev,&bd,&sd,&vb);
bd.ByteWidth=sizeof(uint32_t)*36;bd.BindFlags=D3D11_BIND_INDEX_BUFFER;sd.pSysMem=idx;
dev->lpVtbl->CreateBuffer(dev,&bd,&sd,&ib);
UINT s2=sizeof(V),o=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&s2,&o);
ctx->lpVtbl->IASetIndexBuffer(ctx,ib,DXGI_FORMAT_R32_UINT,0);
ctx->lpVtbl->DrawIndexed(ctx,36,0,0);
ib->lpVtbl->Release(ib);vb->lpVtbl->Release(vb);
}
static void on_render(Engine* engine) {
if(!g_dev) return;
ID3D11DeviceContext* ctx=g_dev->context;
g_time+=0.016f;
float sw=(float)g_dev->width,sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f),asp=sw/sh;
float mvp[16]={0}; mvp[0]=fov/asp;mvp[5]=-fov;mvp[10]=1;mvp[11]=-1;mvp[14]=0.1f;mvp[15]=1;
Mat4 pm,pt;memcpy(&pm,mvp,sizeof(Mat4));mat4_transpose(&pm,&pt);
graphics_device_clear(g_dev,0.15f,0.18f,0.25f,1.0f);
D3D11_VIEWPORT vp={};vp.Width=sw;vp.Height=sh;vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={};rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState*rs=NULL;g_dev->device->lpVtbl->CreateRasterizerState(g_dev,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={};dsd.DepthEnable=FALSE;
ID3D11DepthStencilState*dss=NULL;g_dev->device->lpVtbl->CreateDepthStencilState(g_dev,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 绑定光照 shader
ctx->lpVtbl->VSSetShader(ctx,g_vs,NULL,0);
ctx->lpVtbl->PSSetShader(ctx,g_ps,NULL,0);
ctx->lpVtbl->IASetInputLayout(ctx,g_il);
ctx->lpVtbl->VSSetConstantBuffers(ctx,0,1,&g_cb);
ctx->lpVtbl->UpdateSubresource(ctx,(ID3D11Resource*)g_cb,0,NULL,&pt,0,0);
// 更新光照 CB
float light_data[16]={0};
light_data[0]=g_sun->direction[0];light_data[1]=g_sun->direction[1];light_data[2]=g_sun->direction[2];
light_data[4]=g_sun->color[0];light_data[5]=g_sun->color[1];light_data[6]=g_sun->color[2];
light_data[7]=g_sun->intensity;
light_data[8]=0.15f;light_data[9]=0.15f;light_data[10]=0.2f;
ctx->lpVtbl->VSSetConstantBuffers(ctx,1,1,&g_cb);
draw_lit_cube(ctx,g_dev, 0,0,0.5f, 0.3f, 1,0.3f,0.3f);
draw_lit_cube(ctx,g_dev,-0.8f,0,0.7f, 0.2f, 0.3f,1,0.3f);
draw_lit_cube(ctx,g_dev, 0.8f,0.3f,0.3f, 0.15f, 0.3f,0.3f,1);
draw_lit_cube(ctx,g_dev, 0,-0.2f,0.8f, 0.4f, 0.8f,0.8f,0.2f);
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine*e,int k){if(k==VK_ESCAPE)engine_request_quit(e);}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int){
EngineConfig cfg=engine_default_config();cfg.app_name="lity3d Light Test";
Engine*e=engine_create();if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
InitShaders();
g_sun=light_create_dir(1.0f,0.95f,0.8f,1.2f, -0.5f,-1.0f,-0.3f);
engine_set_render_callback(e,on_render);engine_set_key_callback(e,on_key);
engine_run(e);
light_destroy(g_sun);graphics_device_destroy(g_dev);engine_shutdown(e);
return 0;
}
+290
View File
@@ -0,0 +1,290 @@
// ============================================================================
// ly_interpreter.c - .ly 积木语言独立解释器
// 直接解析 .ly 格式,不依赖引擎 VisualScript 框架
// 用法: ly_interpreter.exe <script.ly>
// ============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// === 数据结构 ===
typedef union { double num; char str[256]; int bool_val; } Value;
typedef struct {
int id;
char name[64];
float x, y;
char data[256];
int color;
Value inputs[8];
int input_count;
Value outputs[4];
int output_count;
} Block;
typedef struct {
int from_block, from_slot;
int to_block, to_slot;
} Wire;
#define MAX_BLOCKS 256
#define MAX_WIRES 256
static Block g_blocks[MAX_BLOCKS];
static int g_block_count = 0;
static Wire g_wires[MAX_WIRES];
static int g_wire_count = 0;
// === 解析 ===
static void trim(char* s) {
char* p = s;
while (*p == ' ' || *p == '\t') p++;
if (p != s) memmove(s, p, strlen(p) + 1);
int len = strlen(s);
while (len > 0 && (s[len-1] == '\n' || s[len-1] == '\r' || s[len-1] == ' ')) s[--len] = 0;
}
static int parse_ly(const char* path) {
FILE* f = fopen(path, "r");
if (!f) { fprintf(stderr, "Cannot open %s\n", path); return 0; }
char line[1024];
int section = 0; // 0=none, 1=block, 2=wire
int cur_bid = -1;
while (fgets(line, sizeof(line), f)) {
trim(line);
if (line[0] == '#' || line[0] == 0) continue;
if (strcmp(line, "[B]") == 0 || strcmp(line, "[Block]") == 0) { section = 1; cur_bid = -1; continue; }
if (strcmp(line, "[W]") == 0 || strcmp(line, "[Wire]") == 0) { section = 2; continue; }
if (line[0] == '[') { section = 0; continue; }
if (section == 1) {
char* eq = strchr(line, '=');
if (!eq) continue;
*eq = 0; char* key = line; char* val = eq + 1;
if (strcmp(key, "id") == 0) {
cur_bid = g_block_count;
memset(&g_blocks[cur_bid], 0, sizeof(Block));
g_blocks[cur_bid].id = atoi(val);
g_block_count++;
} else if (cur_bid >= 0) {
if (strcmp(key, "name") == 0) strncpy(g_blocks[cur_bid].name, val, 63);
else if (strcmp(key, "p") == 0 || strcmp(key, "pos") == 0) sscanf(val, "%f,%f", &g_blocks[cur_bid].x, &g_blocks[cur_bid].y);
else if (strcmp(key, "d") == 0 || strcmp(key, "data") == 0) strncpy(g_blocks[cur_bid].data, val, 255);
else if (strcmp(key, "c") == 0 || strcmp(key, "color") == 0) g_blocks[cur_bid].color = atoi(val);
}
} else if (section == 2) {
Wire* w = &g_wires[g_wire_count];
char* eq = strchr(line, '=');
if (!eq) continue;
*eq = 0; char* key = line; char* val = eq + 1;
if (strcmp(key, "a") == 0 || strcmp(key, "from") == 0) {
w->from_block = atoi(val) / 10000;
w->from_slot = atoi(val) % 10000;
} else if (strcmp(key, "b") == 0 || strcmp(key, "to") == 0) {
w->to_block = atoi(val) / 10000;
w->to_slot = atoi(val) % 10000;
if (w->from_block > 0) g_wire_count++;
}
}
}
fclose(f);
printf("Parsed: %d blocks, %d wires\n", g_block_count, g_wire_count);
return 1;
}
// === 查找 ===
static Block* find_block(int id) {
for (int i = 0; i < g_block_count; i++)
if (g_blocks[i].id == id) return &g_blocks[i];
return NULL;
}
// 找到从某个 block 的 output_slot 出发,连接到的下一个 block
static Block* follow_wire(int from_id, int from_slot) {
for (int i = 0; i < g_wire_count; i++) {
if (g_wires[i].from_block == from_id && g_wires[i].from_slot == from_slot)
return find_block(g_wires[i].to_block);
}
return NULL;
}
// 获取连接到某个 block input_slot 的值
static Value get_input_value(int block_id, int slot) {
for (int i = 0; i < g_wire_count; i++) {
if (g_wires[i].to_block == block_id && g_wires[i].to_slot == slot) {
Block* src = find_block(g_wires[i].from_block);
if (src && src->output_count > g_wires[i].from_slot)
return src->outputs[g_wires[i].from_slot];
}
}
Value v; v.num = 0; return v;
}
// === 解释执行 ===
static int g_variables[64]; // 简单变量存储
static char g_var_names[64][64];
static int g_var_count = 0;
static int get_var(const char* name) {
for (int i = 0; i < g_var_count; i++)
if (strcmp(g_var_names[i], name) == 0) return g_variables[i];
return 0;
}
static void set_var(const char* name, int val) {
for (int i = 0; i < g_var_count; i++)
if (strcmp(g_var_names[i], name) == 0) { g_variables[i] = val; return; }
if (g_var_count < 64) { strncpy(g_var_names[g_var_count], name, 63); g_variables[g_var_count++] = val; }
}
static void execute(Block* block, int depth);
static void execute_block(Block* b, int depth) {
if (!b) return;
if (depth > 100) { printf("Stack overflow!\n"); return; }
const char* n = b->name;
if (strcmp(n, "开始") == 0) {
// Follow output wire
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "移动物体") == 0) {
Value dx = get_input_value(b->id, 1);
Value dy = get_input_value(b->id, 2);
Value dz = get_input_value(b->id, 3);
printf(" [move] dx=%.1f dy=%.1f dz=%.1f\n", dx.num, dy.num, dz.num);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "旋转物体") == 0) {
Value dx = get_input_value(b->id, 1);
Value dy = get_input_value(b->id, 2);
Value dz = get_input_value(b->id, 3);
printf(" [rotate] rx=%.1f ry=%.1f rz=%.1f\n", dx.num, dy.num, dz.num);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "当按键") == 0) {
// Event: just follow output (simulated - always fires)
printf(" [key event] key='%s'\n", b->data);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "当点击") == 0) {
printf(" [click event]\n");
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "打印") == 0 || strcmp(n, "print") == 0) {
Value msg = get_input_value(b->id, 1);
printf(" [print] %s\n", msg.str[0] ? msg.str : b->data);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "等待") == 0 || strcmp(n, "wait") == 0) {
Value secs = get_input_value(b->id, 1);
double s = secs.num > 0 ? secs.num : atof(b->data);
printf(" [wait] %.1fs\n", s);
// In real engine, this would pause. For now, just continue.
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "循环") == 0 || strcmp(n, "loop") == 0) {
Value count = get_input_value(b->id, 1);
int n = count.num > 0 ? (int)count.num : atoi(b->data);
if (n <= 0) n = 1;
printf(" [loop] %d times\n", n);
for (int i = 0; i < n; i++) {
Block* body = follow_wire(b->id, 0);
execute(body, depth + 1);
}
}
else if (strcmp(n, "如果") == 0 || strcmp(n, "if") == 0) {
Value cond = get_input_value(b->id, 1);
printf(" [if] condition=%d\n", cond.bool_val);
Block* next = follow_wire(b->id, cond.bool_val ? 0 : 1);
execute(next, depth + 1);
}
else if (strcmp(n, "数学运算") == 0 || strcmp(n, "+") == 0) {
Value a = get_input_value(b->id, 0);
Value b2 = get_input_value(b->id, 1);
b->outputs[0].num = a.num + b2.num;
printf(" [add] %.1f + %.1f = %.1f\n", a.num, b2.num, b->outputs[0].num);
}
else if (strcmp(n, "变量设置") == 0 || strcmp(n, "set_var") == 0) {
Value v = get_input_value(b->id, 1);
set_var(b->data, (int)v.num);
printf(" [set_var] %s = %d\n", b->data, (int)v.num);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "变量读取") == 0 || strcmp(n, "get_var") == 0) {
int v = get_var(b->data);
b->outputs[0].num = v;
printf(" [get_var] %s = %d\n", b->data, v);
}
else {
printf(" [unknown] '%s'\n", n);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
}
static void execute(Block* block, int depth) {
if (!block) return;
execute_block(block, depth);
}
// === 主函数 ===
int main(int argc, char* argv[]) {
const char* script = "Demo/test.ly";
if (argc > 1) script = argv[1];
printf("=== lity3d .ly Interpreter ===\n");
printf("Script: %s\n\n", script);
if (!parse_ly(script)) return 1;
printf("\n--- Blocks ---\n");
for (int i = 0; i < g_block_count; i++)
printf(" [%d] %s data='%s' pos=(%.0f,%.0f)\n", g_blocks[i].id, g_blocks[i].name, g_blocks[i].data, g_blocks[i].x, g_blocks[i].y);
printf("\n--- Wires ---\n");
for (int i = 0; i < g_wire_count; i++)
printf(" [%d].out%d -> [%d].in%d\n", g_wires[i].from_block, g_wires[i].from_slot, g_wires[i].to_block, g_wires[i].to_slot);
printf("\n--- Execute ---\n");
// Find start block
Block* start = NULL;
for (int i = 0; i < g_block_count; i++)
if (strcmp(g_blocks[i].name, "开始") == 0) { start = &g_blocks[i]; break; }
if (!start) {
// Try to find any block with incoming wires as entry point
// Or just execute the first block
if (g_block_count > 0) start = &g_blocks[0];
}
if (start) {
execute(start, 0);
} else {
printf("No executable blocks found.\n");
}
printf("\n--- Done ---\n");
return 0;
}
+224
View File
@@ -0,0 +1,224 @@
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Core/timer.h"
#include "../Engine/Core/asset_manager.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/renderer.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/Rendering/material.h"
#include "../Engine/Rendering/texture.h"
#include "../Engine/Rendering/camera.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/Scene/scene_render.h"
#include "../Engine/Scene/scene_serialize.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
static Scene* g_scene = NULL;
static AssetManager* g_assets = NULL;
static Asset* g_shader_asset = NULL;
static Camera g_camera;
static int g_mouse_down[3] = {0};
static int g_last_mouse_x = 0, g_last_mouse_y = 0;
static int g_mouse_first = 1;
static SceneNode* create_primitive_node(Scene* scene, SceneNode* parent, AssetManager* am,
ID3D11Device* dev, const char* name, MeshType type,
Vec3 pos, Vec3 rot, Vec3 scale,
Material* material) {
SceneNode* node = scene_create_node(scene, name);
if (!node) return NULL;
if (parent) scene_set_parent(node, parent);
node->position = pos;
node->scale = scale;
quat_from_euler(rot.x, rot.y, rot.z, &node->rotation);
Asset* mesh_asset = asset_manager_load_mesh(am, dev, name);
if (!mesh_asset) {
const char* builtin = NULL;
if (type == MESH_CUBE) builtin = "cube";
else if (type == MESH_SPHERE) builtin = "sphere";
else if (type == MESH_PLANE) builtin = "plane";
if (builtin) mesh_asset = asset_manager_load_mesh(am, dev, builtin);
}
if (mesh_asset && material) {
MeshRenderer* mr = mesh_renderer_create(type, (Mesh*)asset_data(mesh_asset), material);
scene_node_set_mesh_renderer(node, mr);
}
return node;
}
static Scene* create_default_scene(AssetManager* am, ID3D11Device* dev) {
Scene* scene = (Scene*)malloc(sizeof(Scene));
if (!scene) return NULL;
scene_init(scene);
SceneNode* root = scene->root;
root->name[0] = '\0';
strncpy(root->name, "root", sizeof(root->name) - 1);
Shader* shader = (Shader*)asset_data(g_shader_asset);
Material* mat_red = material_create(shader, NULL);
mat_red->color = (Vec4){1.0f, 0.3f, 0.2f, 1.0f};
Material* mat_blue = material_create(shader, NULL);
mat_blue->color = (Vec4){0.2f, 0.5f, 1.0f, 1.0f};
Material* mat_ground = material_create(shader, NULL);
mat_ground->color = (Vec4){0.3f, 0.3f, 0.35f, 1.0f};
// 中心红色立方体
create_primitive_node(scene, root, am, dev, "cube", MESH_CUBE,
(Vec3){0, 0, 0}, (Vec3){0, 0, 0}, (Vec3){1, 1, 1}, mat_red);
// 右侧蓝色球体
create_primitive_node(scene, root, am, dev, "sphere", MESH_SPHERE,
(Vec3){2.0f, 0, 0}, (Vec3){0, 0, 0}, (Vec3){0.8f, 0.8f, 0.8f}, mat_blue);
// 下方地面
create_primitive_node(scene, root, am, dev, "ground", MESH_PLANE,
(Vec3){0, -1.0f, 0}, (Vec3){0, 0, 0}, (Vec3){1, 1, 1}, mat_ground);
scene_update(scene);
return scene;
}
static void on_render(Engine* engine) {
Renderer* r = engine_get_renderer(engine);
GraphicsDevice* dev = engine_get_graphics_device(engine);
if (!r || !dev || !g_scene) return;
renderer_set_view(r, camera_get_view(&g_camera));
renderer_set_projection(r, camera_get_projection(&g_camera));
renderer_begin_frame(r);
scene_update(g_scene);
scene_render(g_scene, r);
renderer_end_frame(r);
if (engine->frame_count == 60) {
graphics_device_save_screenshot(dev, "screenshot1.bmp");
}
if (engine->frame_count == 180) {
graphics_device_save_screenshot(dev, "screenshot2.bmp");
}
}
static void on_update(Engine* engine, double dt) {
(void)engine; (void)dt;
}
static void on_key(Engine* engine, int key) {
if (key == VK_ESCAPE) engine_request_quit(engine);
if (key == 'W' || key == 'w') camera_move_forward(&g_camera, 0.5f);
if (key == 'S' || key == 's') camera_move_forward(&g_camera, -0.5f);
if (key == 'A' || key == 'a') camera_move_right(&g_camera, -0.5f);
if (key == 'D' || key == 'd') camera_move_right(&g_camera, 0.5f);
if (key == VK_LEFT) camera_rotate_yaw(&g_camera, -0.1f);
if (key == VK_RIGHT) camera_rotate_yaw(&g_camera, 0.1f);
if (key == VK_UP) camera_rotate_pitch(&g_camera, -0.1f);
if (key == VK_DOWN) camera_rotate_pitch(&g_camera, 0.1f);
}
static void on_mouse_move(Engine* engine, int x, int y) {
(void)engine;
if (g_mouse_down[0]) {
if (g_mouse_first) {
g_last_mouse_x = x;
g_last_mouse_y = y;
g_mouse_first = 0;
} else {
float dx = (float)(x - g_last_mouse_x);
float dy = (float)(y - g_last_mouse_y);
camera_rotate_yaw(&g_camera, dx * 0.005f);
camera_rotate_pitch(&g_camera, dy * 0.005f);
g_last_mouse_x = x;
g_last_mouse_y = y;
}
} else {
g_mouse_first = 1;
}
}
static void on_mouse_button(Engine* engine, int button, int pressed) {
(void)engine;
if (button >= 0 && button < 3) g_mouse_down[button] = pressed;
}
static void on_resize(Engine* engine, int width, int height) {
Renderer* r = engine_get_renderer(engine);
if (r && width > 0 && height > 0) {
renderer_resize(r, width, height);
g_camera.aspect = (float)width / (float)height;
g_camera.dirty = 1;
}
}
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
EngineConfig config = engine_default_config();
config.app_name = "Lity3D - Scene Demo";
config.vsync = 0;
config.window_width = 1280;
config.window_height = 720;
Engine* engine = engine_create();
if (!engine_init(engine, &config)) {
LOG_FATAL("Engine init failed");
return -1;
}
GraphicsDevice* dev = engine_get_graphics_device(engine);
Renderer* r = engine_get_renderer(engine);
g_assets = asset_manager_create();
g_shader_asset = asset_manager_load_shader(g_assets, dev->device, NULL, NULL);
if (!g_shader_asset) {
LOG_FATAL("Shader load failed");
return -1;
}
LOG_INFO("Shader loaded via AssetManager");
Shader* default_shader = (Shader*)asset_data(g_shader_asset);
g_scene = scene_load("scene.txt", dev->device, default_shader);
if (!g_scene) {
LOG_INFO("No scene.txt found, creating default scene");
g_scene = create_default_scene(g_assets, dev->device);
if (!scene_save(g_scene, "scene.txt")) {
LOG_WARN("Failed to save scene.txt");
}
}
float aspect = (float)config.window_width / config.window_height;
g_camera = camera_perspective(1.0472f, aspect, 0.1f, 100.0f);
g_camera.position = (Vec3){0, 0.5f, -5.0f};
g_camera.target = (Vec3){0, 0, 0};
g_camera.dirty = 1;
renderer_set_view(r, camera_get_view(&g_camera));
renderer_set_projection(r, camera_get_projection(&g_camera));
engine_set_update_callback(engine, on_update);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_set_mouse_move_callback(engine, on_mouse_move);
engine_set_mouse_button_callback(engine, on_mouse_button);
engine_set_resize_callback(engine, on_resize);
LOG_INFO("Running scene demo...");
engine_run(engine);
scene_destroy_renderers(g_scene);
scene_destroy(g_scene);
free(g_scene);
asset_manager_destroy(g_assets);
engine_shutdown(engine);
return 0;
}
+73
View File
@@ -0,0 +1,73 @@
// mesh_debug.c - 精确定位 mesh 系统 bug
#include "../Engine/Core/engine.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static Shader* g_shader = NULL;
static ID3D11Buffer* g_vb = NULL;
static ID3D11Buffer* g_ib = NULL;
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
static void on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
float sw=(float)g_dev->width, sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f), asp=sw/sh;
float mvp[16];
mvp[0]=fov/asp; mvp[5]=-fov; mvp[10]=1; mvp[11]=-1; mvp[14]=0.1f; mvp[15]=1;
Mat4 pm, pt; memcpy(&pm, mvp, sizeof(Mat4)); mat4_transpose(&pm, &pt);
graphics_device_clear(g_dev, 0.15f, 0.18f, 0.25f, 1.0f);
D3D11_VIEWPORT vp={}; vp.Width=sw; vp.Height=sh; vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={}; rd.FillMode=D3D11_FILL_SOLID; rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState* rs=NULL; g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={}; dsd.DepthEnable=FALSE;
ID3D11DepthStencilState* dss=NULL; g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(g_shader, ctx);
shader_set_matrix(g_shader, ctx, (float*)&pt);
// 用 init 时创建的 VB 画
UINT stride=sizeof(V), offset=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&g_vb,&stride,&offset);
ctx->lpVtbl->IASetIndexBuffer(ctx,g_ib,DXGI_FORMAT_R32_UINT,0);
ctx->lpVtbl->DrawIndexed(ctx,6,0,0);
if(rs) rs->lpVtbl->Release(rs);
if(dss) dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) { if(key==VK_ESCAPE) engine_request_quit(engine); }
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int) {
EngineConfig cfg=engine_default_config(); cfg.app_name="mesh debug";
Engine* e=engine_create(); if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
g_shader=shader_create(); shader_compile(g_shader,g_dev->device,NULL,NULL,0,0);
// WinMain 中创建 VB/IB(跟 mesh_create_cube 一样的时机)
V v[4]={{{-0.5f,-0.5f,0.5f},{0,0,1},{0,0},{1,0,0,1}},{{0.5f,-0.5f,0.5f},{0,0,1},{1,0},{0,1,0,1}},{{0.5f,0.5f,0.5f},{0,0,1},{1,1},{0,0,1,1}},{{-0.5f,0.5f,0.5f},{0,0,1},{0,1},{1,1,1,1}}};
uint32_t idx[6]={0,1,2,0,2,3};
D3D11_BUFFER_DESC bd={}; D3D11_SUBRESOURCE_DATA sd={};
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,&g_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,&g_ib);
engine_set_render_callback(e,on_render); engine_set_key_callback(e,on_key);
engine_run(e);
if(g_vb)g_vb->lpVtbl->Release(g_vb); if(g_ib)g_ib->lpVtbl->Release(g_ib);
shader_destroy(g_shader); graphics_device_destroy(g_dev); engine_shutdown(e);
return 0;
}
+206
View File
@@ -0,0 +1,206 @@
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static ID3D11Device* g_dev; static ID3D11DeviceContext* g_ctx;
static IDXGISwapChain* g_sc; static ID3D11RenderTargetView* g_rtv;
static ID3D11VertexShader* g_vs; static ID3D11PixelShader* g_ps;
static ID3D11InputLayout* g_il; static ID3D11Buffer* g_cb;
static float cam_dist = 5.0f, cam_yaw = 0.5f, cam_pitch = 0.3f;
static int drag = 0, last_mx, last_my;
static int key_w=0,key_s=0,key_a=0,key_d=0,key_q=0,key_e=0;
typedef struct { float p[3], n[3], u[2], c[4]; } V3D;
static V3D* obj_verts = NULL;
static uint32_t* obj_idx = NULL;
static int obj_vcount = 0, obj_icount = 0;
static ID3D11Buffer* obj_vb = NULL;
static ID3D11Buffer* obj_ib = NULL;
static int LoadOBJ(const char* path) {
FILE* f = fopen(path, "r");
if (!f) return -1;
int max_v = 16384, max_f = 32768;
float* vpos = (float*)malloc(max_v * 3 * sizeof(float));
int vc = 0;
int* fidx = (int*)malloc(max_f * 3 * sizeof(int));
int fc = 0;
char line[256];
while (fgets(line, sizeof(line), f)) {
if (line[0] == 'v' && line[1] == ' ') {
float x, y, z;
if (sscanf(line + 2, "%f %f %f", &x, &y, &z) == 3 && vc < max_v) {
vpos[vc*3]=x; vpos[vc*3+1]=y; vpos[vc*3+2]=z; vc++;
}
} else if (line[0] == 'f' && line[1] == ' ') {
int v0, v1, v2;
char* p = line + 2;
v0 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v1 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v2 = atoi(p) - 1;
if (v0 >= 0 && v1 >= 0 && v2 >= 0 && fc < max_f) {
fidx[fc*3]=v0; fidx[fc*3+1]=v1; fidx[fc*3+2]=v2; fc++;
}
}
}
fclose(f);
if (vc == 0 || fc == 0) { free(vpos); free(fidx); return -1; }
obj_vcount = fc * 3;
obj_icount = fc * 3;
obj_verts = (V3D*)calloc(obj_vcount, sizeof(V3D));
obj_idx = (uint32_t*)malloc(obj_icount * sizeof(uint32_t));
for (int i = 0; i < fc; i++) {
for (int j = 0; j < 3; j++) {
int vi = fidx[i*3+j];
int oi = i*3+j;
if (vi < vc) {
obj_verts[oi].p[0] = vpos[vi*3];
obj_verts[oi].p[1] = vpos[vi*3+1];
obj_verts[oi].p[2] = vpos[vi*3+2];
}
obj_verts[oi].c[0] = 0.6f; obj_verts[oi].c[1] = 0.7f; obj_verts[oi].c[2] = 0.8f; obj_verts[oi].c[3] = 1;
obj_idx[oi] = oi;
}
}
for (int i = 0; i < fc; i++) {
float* p0 = obj_verts[i*3].p;
float* p1 = obj_verts[i*3+1].p;
float* p2 = obj_verts[i*3+2].p;
float e1[3]={p1[0]-p0[0],p1[1]-p0[1],p1[2]-p0[2]};
float e2[3]={p2[0]-p0[0],p2[1]-p0[1],p2[2]-p0[2]};
float nx=e1[1]*e2[2]-e1[2]*e2[1], ny=e1[2]*e2[0]-e1[0]*e2[2], nz=e1[0]*e2[1]-e1[1]*e2[0];
float nl=sqrtf(nx*nx+ny*ny+nz*nz);
if(nl>0.0001f){nx/=nl;ny/=nl;nz/=nl;}
for(int j=0;j<3;j++){obj_verts[i*3+j].n[0]=nx;obj_verts[i*3+j].n[1]=ny;obj_verts[i*3+j].n[2]=nz;}
}
D3D11_BUFFER_DESC bd; D3D11_SUBRESOURCE_DATA sd;
memset(&bd,0,sizeof(bd));memset(&sd,0,sizeof(sd));
bd.ByteWidth=sizeof(V3D)*obj_vcount;bd.Usage=D3D11_USAGE_DEFAULT;bd.BindFlags=D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem=obj_verts;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&obj_vb);
bd.ByteWidth=sizeof(uint32_t)*obj_icount;bd.BindFlags=D3D11_BIND_INDEX_BUFFER;
sd.pSysMem=obj_idx;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&obj_ib);
free(vpos); free(fidx); free(obj_verts); free(obj_idx);
obj_verts = NULL; obj_idx = NULL;
return 0;
}
static void Init(HWND h) {
HRESULT hr;
DXGI_SWAP_CHAIN_DESC sd;memset(&sd,0,sizeof(sd));sd.BufferCount=2;sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;sd.BufferDesc.RefreshRate.Numerator=60;sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;sd.OutputWindow=h;sd.SampleDesc.Count=1;sd.Windowed=TRUE;sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL fl=D3D_FEATURE_LEVEL_11_0,gt;hr=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,&fl,1,D3D11_SDK_VERSION,&sd,&g_sc,&g_dev,&gt,&g_ctx);if(FAILED(hr))return;
ID3D11Texture2D*bb=NULL;hr=g_sc->lpVtbl->GetBuffer(g_sc,0,&IID_ID3D11Texture2D,(void**)&bb);if(FAILED(hr)||!bb)return;hr=g_dev->lpVtbl->CreateRenderTargetView(g_dev,(ID3D11Resource*)bb,NULL,&g_rtv);bb->lpVtbl->Release(bb);if(FAILED(hr))return;
const char*vs="cbuffer CB:register(b0){float4x4 WVP;};struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.n=i.n;o.c=i.c;return o;}";
const char*ps="struct I{float3 n:NORMAL;float4 c:COLOR;};float4 main(float4 p:SV_POSITION,I i):SV_Target{float l=normalize(float3(0.3,1,0.5));float d=max(dot(normalize(i.n),l),0.2);return float4(i.c.rgb*d,1);}";
ID3DBlob*b1,*b2,*e;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;
g_dev->lpVtbl->CreateVertexShader(g_dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);g_dev->lpVtbl->CreatePixelShader(g_dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC el[4];memset(el,0,sizeof(el));el[0].SemanticName="POSITION";el[0].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[0].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[1].SemanticName="NORMAL";el[1].AlignedByteOffset=12;el[1].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[1].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[2].SemanticName="TEXCOORD";el[2].AlignedByteOffset=24;el[2].Format=DXGI_FORMAT_R32G32_FLOAT;el[2].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[3].SemanticName="COLOR";el[3].AlignedByteOffset=32;el[3].Format=DXGI_FORMAT_R32G32B32A32_FLOAT;el[3].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;
g_dev->lpVtbl->CreateInputLayout(g_dev,el,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);b1->lpVtbl->Release(b1);b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd;memset(&cbd,0,sizeof(cbd));cbd.ByteWidth=256;cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;g_dev->lpVtbl->CreateBuffer(g_dev,&cbd,NULL,&g_cb);
}
static void mat4_perspective(float* m, float fov, float asp, float zn, float zf) {
memset(m, 0, 64);
float f = 1.0f / tanf(fov / 2);
m[0] = f / asp; m[5] = -f;
m[10] = zf / (zf - zn); m[11] = -zn * zf / (zf - zn);
m[14] = 1; m[15] = 0;
}
static void mat4_lookat(float* m, float ex, float ey, float ez, float tx, float ty, float tz) {
float fx=tx-ex, fy=ty-ey, fz=tz-ez;
float fl=sqrtf(fx*fx+fy*fy+fz*fz);fx/=fl;fy/=fl;fz/=fl;
float rx=fy*0-fz*1,ry=fz*0-fx*0,rz=fx*1-fy*0;
float rl=sqrtf(rx*rx+ry*ry+rz*rz);rx/=rl;ry/=rl;rz/=rl;
float ux=ry*fz-rz*fy,uy=rz*fx-rx*fz,uz=rx*fy-ry*fx;
memset(m, 0, 64);
m[0]=rx*1;m[1]=ux*1;m[2]=fx*1;
m[4]=ry*1;m[5]=uy*1;m[6]=fy*1;
m[8]=rz*1;m[9]=uz*1;m[10]=fz*1;
m[12]=-(rx*ex+ry*ey+rz*ez);m[13]=-(ux*ex+uy*ey+uz*ez);m[14]=-(fx*ex+fy*ey+fz*ez);m[15]=1;
}
static void mat4_mul(float* o, float* a, float* b) {
float t[16];for(int i=0;i<4;i++)for(int j=0;j<4;j++){t[i*4+j]=0;for(int k=0;k<4;k++)t[i*4+j]+=a[i*4+k]*b[k*4+j];}
memcpy(o,t,64);
}
static void Frame(void) {
float dt = 0.016f, rspd = 2.0f * dt, mspeed = 3.0f * dt;
if (key_a) cam_yaw -= rspd; if (key_d) cam_yaw += rspd;
if (key_q) cam_pitch -= rspd; if (key_e) cam_pitch += rspd;
if (key_w) cam_dist -= mspeed; if (key_s) cam_dist += mspeed;
if (cam_dist < 1.0f) cam_dist = 1.0f; if (cam_dist > 20.0f) cam_dist = 20.0f;
if (cam_pitch > 1.5f) cam_pitch = 1.5f; if (cam_pitch < -1.5f) cam_pitch = -1.5f;
float ex = cam_dist * sinf(cam_yaw) * cosf(cam_pitch);
float ey = cam_dist * sinf(cam_pitch);
float ez = cam_dist * cosf(cam_yaw) * cosf(cam_pitch);
float view[16], proj[16], vp[16], tmp[16];
mat4_lookat(view, ex, ey, ez, 0, 0, 0);
mat4_perspective(proj, 0.8f, 1280.0f/720.0f, 0.1f, 100.0f);
mat4_mul(tmp, view, proj);
float vp_t[16]; for(int i=0;i<4;i++)for(int j=0;j<4;j++) vp_t[i*4+j]=tmp[j*4+i];
float cl[4]={.15f,.18f,.22f,1};
g_ctx->lpVtbl->OMSetRenderTargets(g_ctx,1,&g_rtv,NULL);g_ctx->lpVtbl->ClearRenderTargetView(g_ctx,g_rtv,cl);
D3D11_VIEWPORT vp2;memset(&vp2,0,sizeof(vp2));vp2.Width=1280;vp2.Height=720;vp2.MaxDepth=1;g_ctx->lpVtbl->RSSetViewports(g_ctx,1,&vp2);
D3D11_RASTERIZER_DESC rd;memset(&rd,0,sizeof(rd));rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;ID3D11RasterizerState*rs;g_dev->lpVtbl->CreateRasterizerState(g_dev,&rd,&rs);g_ctx->lpVtbl->RSSetState(g_ctx,rs);
D3D11_DEPTH_STENCIL_DESC ds;memset(&ds,0,sizeof(ds));ds.DepthEnable=FALSE;ID3D11DepthStencilState*dss;g_dev->lpVtbl->CreateDepthStencilState(g_dev,&ds,&dss);g_ctx->lpVtbl->OMSetDepthStencilState(g_ctx,dss,0);
g_ctx->lpVtbl->IASetPrimitiveTopology(g_ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);g_ctx->lpVtbl->VSSetShader(g_ctx,g_vs,NULL,0);g_ctx->lpVtbl->PSSetShader(g_ctx,g_ps,NULL,0);g_ctx->lpVtbl->IASetInputLayout(g_ctx,g_il);g_ctx->lpVtbl->VSSetConstantBuffers(g_ctx,0,1,&g_cb);g_ctx->lpVtbl->UpdateSubresource(g_ctx,(ID3D11Resource*)g_cb,0,NULL,vp_t,0,0);
if (obj_vb && obj_ib) {
UINT stride = sizeof(V3D), off = 0;
g_ctx->lpVtbl->IASetVertexBuffers(g_ctx, 0, 1, &obj_vb, &stride, &off);
g_ctx->lpVtbl->IASetIndexBuffer(g_ctx, obj_ib, DXGI_FORMAT_R32_UINT, 0);
g_ctx->lpVtbl->DrawIndexed(g_ctx, obj_icount, 0, 0);
}
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);
g_sc->lpVtbl->Present(g_sc, 1, 0);
}
static LRESULT CALLBACK WP(HWND h,UINT m,WPARAM w,LPARAM l){
switch(m){
case WM_LBUTTONDOWN:drag=1;last_mx=(short)LOWORD(l);last_my=(short)HIWORD(l);SetCapture(h);break;
case WM_LBUTTONUP:drag=0;ReleaseCapture();break;
case WM_MOUSEMOVE:if(drag){int mx=(short)LOWORD(l),my=(short)HIWORD(l);cam_yaw+=(mx-last_mx)*0.005f;cam_pitch+=(my-last_my)*0.005f;if(cam_pitch>1.5f)cam_pitch=1.5f;if(cam_pitch<-1.5f)cam_pitch=-1.5f;last_mx=mx;last_my=my;}break;
case WM_MOUSEWHEEL:cam_dist-=(short)HIWORD(w)/120.0f;if(cam_dist<1)cam_dist=1;if(cam_dist>20)cam_dist=20;break;
case WM_KEYDOWN:switch(w){case 'A':key_a=1;break;case 'D':key_d=1;break;case 'W':key_w=1;break;case 'S':key_s=1;break;case 'Q':key_q=1;break;case 'E':key_e=1;break;case VK_ESCAPE:PostQuitMessage(0);break;}break;
case WM_KEYUP:switch(w){case 'A':key_a=0;break;case 'D':key_d=0;break;case 'W':key_w=0;break;case 'S':key_s=0;break;case 'Q':key_q=0;break;case 'E':key_e=0;break;}break;
case WM_DESTROY:PostQuitMessage(0);return 0;
}return DefWindowProcW(h,m,w,l);
}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR nC,int nS){
(void)nS;
WNDCLASSEXW wc;memset(&wc,0,sizeof(wc));wc.cbSize=sizeof(wc);wc.style=CS_CLASSDC;wc.lpfnWndProc=WP;wc.hInstance=hI;wc.lpszClassName=L"obj_viewer";
RegisterClassExW(&wc);
HWND hwnd=CreateWindowW(L"obj_viewer",L"OBJ Viewer - Mouse drag orbit, WASD move, Scroll zoom",WS_OVERLAPPEDWINDOW,100,100,1280,720,NULL,NULL,hI,NULL);
if(!hwnd)return -1;ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);Init(hwnd);
char path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
char* sl = strrchr(path, '\\');
if (sl) { *(sl+1) = '\0'; strcat(path, "Assets\\cube.obj"); }
else strcpy(path, "Assets\\cube.obj");
LoadOBJ(path);
MSG msg;
while(1){while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);if(msg.message==WM_QUIT)goto done;}Frame();}
done:
if(obj_vb)obj_vb->lpVtbl->Release(obj_vb);if(obj_ib)obj_ib->lpVtbl->Release(obj_ib);
if(g_cb)g_cb->lpVtbl->Release(g_cb);if(g_il)g_il->lpVtbl->Release(g_il);if(g_vs)g_vs->lpVtbl->Release(g_vs);if(g_ps)g_ps->lpVtbl->Release(g_ps);if(g_rtv)g_rtv->lpVtbl->Release(g_rtv);if(g_sc)g_sc->lpVtbl->Release(g_sc);if(g_ctx)g_ctx->lpVtbl->Release(g_ctx);if(g_dev)g_dev->lpVtbl->Release(g_dev);
return 0;
}
+384
View File
@@ -0,0 +1,384 @@
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Core/timer.h"
#include "../Engine/Core/asset_manager.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/renderer.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/Rendering/material.h"
#include "../Engine/Rendering/camera.h"
#include "../Engine/Rendering/camera_controller.h"
#include "../Engine/Terrain/terrain.h"
#include "../Engine/Terrain/terrain_manager.h"
#include "../Engine/Rendering/frustum.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/Scene/scene_render.h"
#include "../Engine/Scene/scene_serialize.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
static AssetManager* g_assets = NULL;
static Asset* g_shader_asset = NULL;
static Camera g_camera;
static CameraController g_controller;
static TerrainManager* g_terrain = NULL;
static Material* g_terrain_mat = NULL;
static Mesh* g_cube = NULL;
static Material* g_cube_mat = NULL;
static Mesh* g_water = NULL;
static Material* g_water_mat = NULL;
static Scene* g_test_scene = NULL;
static int g_mouse_down[3] = {0};
static int g_last_mouse_x = 0, g_last_mouse_y = 0;
static int g_mouse_first = 1;
static int g_walk_mode = 0;
// ============================================================================
// 简易植被/岩石散布
// ============================================================================
#define MAX_SCATTER 512
typedef struct {
Mesh* mesh;
Material* mat;
Mat4 world;
} ScatterInstance;
static ScatterInstance g_scatter[MAX_SCATTER];
static int g_scatter_count = 0;
static void add_scatter(Mesh* mesh, Material* mat, Vec3 pos, Vec3 scale) {
if (g_scatter_count >= MAX_SCATTER) return;
ScatterInstance* s = &g_scatter[g_scatter_count++];
s->mesh = mesh;
s->mat = mat;
mat4_identity(&s->world);
s->world.m[0][0] = scale.x;
s->world.m[1][1] = scale.y;
s->world.m[2][2] = scale.z;
s->world.m[3][0] = pos.x;
s->world.m[3][1] = pos.y;
s->world.m[3][2] = pos.z;
}
static void generate_scatter(ID3D11Device* dev, Shader* shader) {
Mesh* rock_mesh = mesh_create_cube(dev);
Material* rock_mat = material_create(shader, NULL);
rock_mat->color = (Vec4){0.55f, 0.50f, 0.45f, 1.0f};
Mesh* trunk_mesh = mesh_create_cube(dev);
Material* trunk_mat = material_create(shader, NULL);
trunk_mat->color = (Vec4){0.55f, 0.40f, 0.25f, 1.0f};
Mesh* leaves_mesh = mesh_create_sphere(dev, 1.0f, 8);
Material* leaves_mat = material_create(shader, NULL);
leaves_mat->color = (Vec4){0.22f, 0.65f, 0.28f, 1.0f};
for (int i = 0; i < MAX_SCATTER; i++) {
float x = ((float)rand() / (float)RAND_MAX) * 400.0f - 200.0f;
float z = ((float)rand() / (float)RAND_MAX) * 400.0f - 200.0f;
float h = terrain_manager_get_height(g_terrain, x, z);
// 跳过过低(水)或过高的山顶
if (h < -2.0f || h > 20.0f) continue;
int type = rand() % 3;
if (type == 0) {
// 岩石
float s = 1.0f + ((float)rand() / (float)RAND_MAX) * 2.0f;
Vec3 pos = {x, h + s * 0.3f, z};
Vec3 sc = {s, s * 0.6f, s};
add_scatter(rock_mesh, rock_mat, pos, sc);
} else {
// 树
float trunk_h = 1.5f + ((float)rand() / (float)RAND_MAX) * 1.5f;
Vec3 trunk_pos = {x, h + trunk_h * 0.5f, z};
Vec3 trunk_sc = {0.25f, trunk_h, 0.25f};
add_scatter(trunk_mesh, trunk_mat, trunk_pos, trunk_sc);
float leaves_s = 1.2f + ((float)rand() / (float)RAND_MAX) * 1.2f;
Vec3 leaves_pos = {x, h + trunk_h + leaves_s * 0.4f, z};
Vec3 leaves_sc = {leaves_s, leaves_s, leaves_s};
add_scatter(leaves_mesh, leaves_mat, leaves_pos, leaves_sc);
}
}
LOG_INFO("Generated %d scatter instances", g_scatter_count);
}
static void render_scatter(Renderer* r, const Vec4 planes[6]) {
const float cull_distance = 140.0f;
const float cull_dist2 = cull_distance * cull_distance;
int rendered = 0;
for (int i = 0; i < g_scatter_count; i++) {
ScatterInstance* s = &g_scatter[i];
Vec3 pos = {s->world.m[3][0], s->world.m[3][1], s->world.m[3][2]};
Vec3 d;
vec3_sub(&pos, &g_camera.position, &d);
if (vec3_dot(&d, &d) > cull_dist2) continue;
// 视锥剔除:用包围球近似
float radius = fmaxf(fmaxf(s->world.m[0][0], s->world.m[1][1]), s->world.m[2][2]) * 0.866f;
if (!frustum_intersects_sphere(planes, pos, radius)) continue;
renderer_draw_mesh_with_material(r, s->mesh, s->mat, &s->world);
rendered++;
}
LOG_INFO("Scatter render: visible=%d/%d", rendered, g_scatter_count);
}
// ============================================================================
// 地形高度回调
// ============================================================================
static float get_terrain_height(float x, float z, void* user) {
(void)user;
if (g_terrain) return terrain_manager_get_height(g_terrain, x, z);
return 0.0f;
}
// ============================================================================
// 引擎回调
// ============================================================================
static void on_render(Engine* engine) {
Renderer* r = engine_get_renderer(engine);
GraphicsDevice* dev = engine_get_graphics_device(engine);
if (!r || !dev) return;
renderer_set_view(r, camera_get_view(&g_camera));
renderer_set_projection(r, camera_get_projection(&g_camera));
renderer_begin_frame(r);
Mat4 vp;
renderer_get_view_projection(r, &vp);
Vec4 planes[6];
frustum_from_matrix(&vp, planes);
if (g_water) {
Mat4 water_world;
mat4_identity(&water_world);
water_world.m[3][1] = -5.0f;
renderer_draw_mesh_with_material(r, g_water, g_water_mat, &water_world);
}
if (g_terrain) {
terrain_manager_update(g_terrain, g_camera.position);
terrain_manager_render(g_terrain, r);
}
render_scatter(r, planes);
if (g_test_scene) {
scene_update(g_test_scene);
scene_render(g_test_scene, r);
}
renderer_end_frame(r);
if (engine->frame_count == 60) {
graphics_device_save_screenshot(dev, "open_world_screenshot1.bmp");
}
if (engine->frame_count == 180) {
graphics_device_save_screenshot(dev, "open_world_screenshot2.bmp");
}
}
static void on_update(Engine* engine, double dt) {
(void)engine;
float fdt = (float)dt;
Vec3 dir = {0.0f, 0.0f, 0.0f};
if (GetAsyncKeyState('W') & 0x8000) dir.z += 1.0f;
if (GetAsyncKeyState('S') & 0x8000) dir.z -= 1.0f;
if (GetAsyncKeyState('A') & 0x8000) dir.x -= 1.0f;
if (GetAsyncKeyState('D') & 0x8000) dir.x += 1.0f;
if (!g_walk_mode) {
if (GetAsyncKeyState(VK_SPACE) & 0x8000) dir.y += 1.0f;
if (GetAsyncKeyState(VK_SHIFT) & 0x8000) dir.y -= 1.0f;
}
if (dir.x != 0.0f || dir.y != 0.0f || dir.z != 0.0f) {
camera_controller_move(&g_controller, dir, fdt);
}
camera_controller_update(&g_controller, fdt);
}
static void on_key(Engine* engine, int key) {
(void)engine;
if (key == VK_ESCAPE) engine_request_quit(engine);
// 切换步行/飞行模式
if (key == 'F' || key == 'f') {
g_walk_mode = !g_walk_mode;
camera_controller_set_mode(&g_controller, g_walk_mode ? CAMERA_MODE_WALK : CAMERA_MODE_FLY);
g_controller.move_speed = g_walk_mode ? 8.0f : 40.0f;
LOG_INFO("Camera mode: %s (speed %.1f)", g_walk_mode ? "walk" : "fly", g_controller.move_speed);
}
// 模式切换后重新定位相机高度
if (g_walk_mode) {
float ground = get_terrain_height(g_camera.position.x, g_camera.position.z, NULL);
g_camera.position.y = ground + g_controller.height_offset;
g_camera.target.y = g_camera.position.y + sinf(g_controller.pitch);
g_camera.dirty = 1;
}
}
static void on_mouse_move(Engine* engine, int x, int y) {
(void)engine;
if (g_mouse_down[0]) { // 左键拖拽旋转
if (g_mouse_first) {
g_last_mouse_x = x;
g_last_mouse_y = y;
g_mouse_first = 0;
} else {
float dx = (float)(x - g_last_mouse_x);
float dy = (float)(y - g_last_mouse_y);
camera_controller_look(&g_controller, dx, dy);
g_last_mouse_x = x;
g_last_mouse_y = y;
}
} else {
g_mouse_first = 1;
}
}
static void on_mouse_button(Engine* engine, int button, int pressed) {
(void)engine;
if (button >= 0 && button < 3) g_mouse_down[button] = pressed;
}
static void on_resize(Engine* engine, int width, int height) {
Renderer* r = engine_get_renderer(engine);
if (r && width > 0 && height > 0) {
renderer_resize(r, width, height);
g_camera.aspect = (float)width / (float)height;
g_camera.dirty = 1;
}
}
// ============================================================================
// 入口
// ============================================================================
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
srand((unsigned int)time(NULL));
EngineConfig config = engine_default_config();
config.app_name = "Lity3D - Open World Demo";
config.vsync = 1;
config.window_width = 1280;
config.window_height = 720;
Engine* engine = engine_create();
if (!engine_init(engine, &config)) {
LOG_FATAL("Engine init failed");
return -1;
}
GraphicsDevice* dev = engine_get_graphics_device(engine);
Renderer* r = engine_get_renderer(engine);
g_assets = asset_manager_create();
g_shader_asset = asset_manager_load_shader(g_assets, dev->device, NULL, NULL);
if (!g_shader_asset) {
LOG_FATAL("Shader load failed");
return -1;
}
LOG_INFO("Shader loaded via AssetManager");
Shader* default_shader = (Shader*)asset_data(g_shader_asset);
// 地形材质
g_terrain_mat = material_create(default_shader, NULL);
g_terrain_mat->color = (Vec4){1.0f, 1.0f, 1.0f, 1.0f};
// 参照立方体
g_cube = mesh_create_cube(dev->device);
g_cube_mat = material_create(default_shader, NULL);
g_cube_mat->color = (Vec4){1.0f, 0.2f, 0.2f, 1.0f};
// 水面(不透明,作为低洼处背景)
g_water = mesh_create_plane(dev->device, 2000.0f);
g_water_mat = material_create(default_shader, NULL);
g_water_mat->color = (Vec4){0.18f, 0.45f, 0.78f, 1.0f};
// 测试场景:仅保留一个红色立方体作为原点标记
g_test_scene = (Scene*)calloc(1, sizeof(Scene));
scene_init(g_test_scene);
SceneNode* cube_node = scene_create_node(g_test_scene, "cube");
scene_set_parent(cube_node, g_test_scene->root);
cube_node->position = (Vec3){0.0f, 0.0f, 0.0f};
MeshRenderer* cube_mr = mesh_renderer_create(MESH_CUBE, g_cube, g_cube_mat);
scene_node_set_mesh_renderer(cube_node, cube_mr);
// 地形配置
TerrainConfig tcfg = terrain_default_config();
tcfg.vertices_per_chunk = 48;
tcfg.chunk_size = 64.0f;
tcfg.height_scale = 30.0f;
tcfg.uv_scale = 0.05f;
g_terrain = terrain_manager_create(dev->device, &tcfg, g_terrain_mat);
g_terrain->load_radius = 3;
// 生成植被/岩石
generate_scatter(dev->device, default_shader);
// 天空色与柔和环境光
renderer_set_clear_color(r, 0.53f, 0.81f, 0.98f, 1.0f);
renderer_set_light(r, (Vec3){0.5f, -1.0f, 0.3f},
(Vec4){0.55f, 0.53f, 0.48f, 1.0f},
(Vec4){0.70f, 0.74f, 0.82f, 1.0f});
// 相机初始化:站在地形上,朝 +Z
float aspect = (float)config.window_width / (float)config.window_height;
g_camera = camera_perspective(1.0472f, aspect, 0.1f, 500.0f);
float start_h = terrain_manager_get_height(g_terrain, 0.0f, 0.0f);
g_camera.position = (Vec3){0.0f, start_h + 1.75f, 0.0f};
g_camera.target = (Vec3){0.0f, start_h + 1.75f, 5.0f};
g_camera.far_z = 500.0f;
g_camera.dirty = 1;
g_controller = camera_controller_create(&g_camera);
g_controller.move_speed = 8.0f;
g_controller.mouse_sensitivity = 0.004f;
camera_controller_set_ground_callback(&g_controller, get_terrain_height, NULL);
g_walk_mode = 1;
camera_controller_set_mode(&g_controller, CAMERA_MODE_WALK);
LOG_INFO("Starting in walk mode at height %.2f", start_h);
renderer_set_view(r, camera_get_view(&g_camera));
renderer_set_projection(r, camera_get_projection(&g_camera));
engine_set_update_callback(engine, on_update);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_set_mouse_move_callback(engine, on_mouse_move);
engine_set_mouse_button_callback(engine, on_mouse_button);
engine_set_resize_callback(engine, on_resize);
LOG_INFO("Running open world demo...");
engine_run(engine);
terrain_manager_destroy(g_terrain);
if (g_test_scene) {
scene_destroy_renderers(g_test_scene);
scene_destroy(g_test_scene);
free(g_test_scene);
}
if (g_cube) mesh_destroy(g_cube);
asset_manager_destroy(g_assets);
engine_shutdown(engine);
return 0;
}
+128
View File
@@ -0,0 +1,128 @@
// raw3d_test.c - 用 draw_quad 画 3D 方块,验证场景图变换
#include "../Engine/Core/engine.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static Shader* g_shader = NULL;
static Scene g_scene;
static float g_time = 0;
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
static void draw_quad_3d(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={}; D3D11_SUBRESOURCE_DATA sd={};
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),off=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&stride,&off);
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 on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
scene_update(&g_scene);
g_time += (float)engine->delta_time;
float sw=(float)g_dev->width, sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f), asp=sw/sh;
// VP矩阵: 摄像机在(8,5,10)看原点
float ex=8,ey=5,ez=10;
float fx=-ex,fy=-ey,fz=-ez, fl=sqrtf(fx*fx+fy*fy+fz*fz); fx/=fl;fy/=fl;fz/=fl;
float rx=fy*0-fz*1,ry=fz*0-fx*0,rz=fx*1-fy*0;
float rl=sqrtf(rx*rx+ry*ry+rz*rz);rx/=rl;ry/=rl;rz/=rl;
float ux=ry*fz-rz*fy,uy=rz*fx-rx*fz,uz=rx*fy-ry*fx;
float mvp[16];
mvp[0]=rx*fov/asp;mvp[1]=ry*fov/asp;mvp[2]=rz*fov/asp;mvp[3]=0;
mvp[4]=ux*fov;mvp[5]=uy*fov;mvp[6]=uz*fov;mvp[7]=0;
mvp[8]=fx;mvp[9]=fy;mvp[10]=fz;mvp[11]=0;
mvp[12]=-(rx*ex+ry*ey+rz*ez)*fov/asp;
mvp[13]=-(ux*ex+uy*ey+uz*ez)*fov;
mvp[14]=-(fx*ex+fy*ey+fz*ez);mvp[15]=1;
graphics_device_clear(g_dev, 0.15f, 0.18f, 0.25f, 1.0f);
D3D11_VIEWPORT vp={};vp.Width=sw;vp.Height=sh;vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={};rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState* rs=NULL;
g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={};dsd.DepthEnable=FALSE;
ID3D11DepthStencilState* dss=NULL;
g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(g_shader, ctx);
// 地面
shader_set_matrix(g_shader, ctx, mvp);
draw_quad_3d(ctx, -5, 0, 10, 10, 0.3f, 0.35f, 0.4f, 1);
// 场景节点
for (int i=0;i<g_scene.node_count;i++) {
SceneNode*n=&g_scene.nodes[i];
if (!n->visible||n->id==0||n->user_type!=1) continue;
// 用世界矩阵变换4个角点
float corners[4][3] = {{-0.5f,-0.5f,0},{0.5f,-0.5f,0},{0.5f,0.5f,0},{-0.5f,0.5f,0}};
V v[4]; uint32_t idx[6]={0,1,2,0,2,3};
float colors[][3]={{1,0.2f,0.2f},{0.2f,1,0.2f},{0.2f,0.2f,1},{1,1,0.2f},{0.5f,0.2f,0.8f},{0.2f,0.8f,0.8f},{0.8f,0.5f,0.2f},{0.5f,0.5f,0.5f}};
int ci=(i-1)%8;
for(int c=0;c<4;c++){
float px=0,py=0,pz=0;
for(int r=0;r<4;r++) {float wc=n->world_matrix.m[0][r]*corners[c][0]+n->world_matrix.m[1][r]*corners[c][1]+n->world_matrix.m[2][r]*corners[c][2]+n->world_matrix.m[3][r]; px+=wc*mvp[r*4+0]; py+=wc*mvp[r*4+1]; pz+=wc*mvp[r*4+2];}
// 简化: 直接用世界坐标+投影
float wx=n->world_matrix.m[3][0]+corners[c][0];
float wy=n->world_matrix.m[3][1]+corners[c][1];
float wz=n->world_matrix.m[3][2]+corners[c][2];
v[c].p[0]=wx; v[c].p[1]=wy; v[c].p[2]=wz;
v[c].n[0]=0; v[c].n[1]=0; v[c].n[2]=1;
v[c].c[0]=colors[ci][0]; v[c].c[1]=colors[ci][1]; v[c].c[2]=colors[ci][2]; v[c].c[3]=1;
}
ID3D11Buffer* vb=NULL,*ib2=NULL;
D3D11_BUFFER_DESC bd2={}; D3D11_SUBRESOURCE_DATA sd2={};
bd2.ByteWidth=sizeof(V)*4; bd2.Usage=D3D11_USAGE_DEFAULT; bd2.BindFlags=D3D11_BIND_VERTEX_BUFFER;
sd2.pSysMem=v; g_dev->device->lpVtbl->CreateBuffer(g_dev->device,&bd2,&sd2,&vb);
bd2.ByteWidth=sizeof(uint32_t)*6; bd2.BindFlags=D3D11_BIND_INDEX_BUFFER;
sd2.pSysMem=idx; g_dev->device->lpVtbl->CreateBuffer(g_dev->device,&bd2,&sd2,&ib2);
UINT s2=sizeof(V),o2=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&s2,&o2);
ctx->lpVtbl->IASetIndexBuffer(ctx,ib2,DXGI_FORMAT_R32_UINT,0);
ctx->lpVtbl->DrawIndexed(ctx,6,0,0);
ib2->lpVtbl->Release(ib2); vb->lpVtbl->Release(vb);
}
if(rs) rs->lpVtbl->Release(rs);
if(dss) dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) { if (key==VK_ESCAPE) engine_request_quit(engine); }
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int) {
EngineConfig cfg=engine_default_config(); cfg.app_name="lity3d Raw3D";
Engine* e=engine_create(); if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
g_shader=shader_create(); shader_compile(g_shader,g_dev->device,NULL,NULL,0,0);
scene_init(&g_scene);
SceneNode* c1=scene_create_node(&g_scene,"Cube1"); scene_set_position(c1,0,0.5f,0); c1->user_type=1;
SceneNode* c2=scene_create_node(&g_scene,"Cube2"); scene_set_position(c2,-3,0.5f,2); c2->user_type=1;
SceneNode* c3=scene_create_node(&g_scene,"Cube3"); scene_set_position(c3,3,0.5f,-2); scene_set_scale(c3,0.5f,0.5f,0.5f); c3->user_type=1;
SceneNode* ch=scene_create_node(&g_scene,"Child"); scene_set_position(ch,2,0,0); ch->user_type=1; scene_set_parent(ch,c1);
engine_set_render_callback(e,on_render); engine_set_key_callback(e,on_key);
engine_run(e);
shader_destroy(g_shader); graphics_device_destroy(g_dev); engine_shutdown(e);
return 0;
}
+121
View File
@@ -0,0 +1,121 @@
// scene_test.c - 场景图 + 网格渲染
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static Shader* g_shader = NULL;
static Mesh* g_cube = NULL;
static Mesh* g_plane = NULL;
static Scene g_scene;
static SceneNode* g_camera = NULL;
static float g_time = 0;
static void on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
scene_update(&g_scene);
g_time += (float)engine->delta_time;
float sw = (float)g_dev->width, sh = (float)g_dev->height;
float fov = 1.0f / tanf(0.5f * 0.785f), aspect = sw / sh;
Vec3 cam_pos;
scene_get_world_position(g_camera, &cam_pos);
// VP 矩阵
float proj[16] = {0};
proj[0] = fov / aspect; proj[5] = -fov;
proj[10] = 1.0f; proj[11] = -1.0f; proj[14] = 0.1f; proj[15] = 1.0f;
Vec3 eye = cam_pos, target = {0,0,0}, up = {0,1,0};
Vec3 f, r, u;
vec3_sub(&target, &eye, &f); vec3_normalize(&f, &f);
vec3_cross(&up, &f, &r); vec3_normalize(&r, &r);
vec3_cross(&f, &r, &u);
float view[16] = {0};
view[0]=r.x; view[1]=u.x; view[2]=-f.x;
view[4]=r.y; view[5]=u.y; view[6]=-f.y;
view[8]=r.z; view[9]=u.z; view[10]=-f.z;
view[12]=-vec3_dot(&r,&eye); view[13]=-vec3_dot(&u,&eye); view[14]=vec3_dot(&f,&eye); view[15]=1.0f;
float vp_raw[16] = {0};
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
vp_raw[i*4+j] = view[i*4+0]*proj[0*4+j]+view[i*4+1]*proj[1*4+j]+view[i*4+2]*proj[2*4+j]+view[i*4+3]*proj[3*4+j];
Mat4 vp_m, vp_t;
memcpy(&vp_m, vp_raw, sizeof(Mat4));
mat4_transpose(&vp_m, &vp_t);
graphics_device_clear(g_dev, 0.15f, 0.18f, 0.25f, 1.0f);
D3D11_VIEWPORT vport = {0}; vport.Width = sw; vport.Height = sh; vport.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vport);
D3D11_RASTERIZER_DESC rdesc = {0}; rdesc.FillMode = D3D11_FILL_SOLID; rdesc.CullMode = D3D11_CULL_NONE;
ID3D11RasterizerState* rstate = NULL;
g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device, &rdesc, &rstate);
ctx->lpVtbl->RSSetState(ctx, rstate);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(g_shader, ctx);
shader_set_matrix(g_shader, ctx, (float*)&vp_t);
if (g_plane) mesh_draw(g_plane, ctx);
for (int i = 0; i < g_scene.node_count; i++) {
SceneNode* node = &g_scene.nodes[i];
if (!node->visible || node->id == 0 || node->user_type != 1 || !g_cube) continue;
float wvp_raw[16] = {0};
for (int r2 = 0; r2 < 4; r2++)
for (int c = 0; c < 4; c++)
wvp_raw[r2*4+c] = view[r2*4+0]*node->world_matrix.m[0][c]+view[r2*4+1]*node->world_matrix.m[1][c]+view[r2*4+2]*node->world_matrix.m[2][c]+view[r2*4+3]*node->world_matrix.m[3][c];
Mat4 wvp_m, wvp_t;
memcpy(&wvp_m, wvp_raw, sizeof(Mat4));
mat4_transpose(&wvp_m, &wvp_t);
shader_set_matrix(g_shader, ctx, (float*)&wvp_t);
mesh_draw(g_cube, ctx);
}
if (rstate) rstate->lpVtbl->Release(rstate);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) { if (key == VK_ESCAPE) engine_request_quit(engine); }
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR cmd, int nS) {
(void)hI;(void)hP;(void)cmd;(void)nS;
EngineConfig cfg = engine_default_config();
cfg.app_name = "lity3d Scene Test";
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;
g_shader = shader_create();
shader_compile(g_shader, g_dev->device, NULL, NULL, 0, 0);
g_cube = mesh_create_cube(g_dev->device);
g_plane = mesh_create_plane(g_dev->device, 10.0f);
scene_init(&g_scene);
g_camera = scene_create_node(&g_scene, "Camera");
scene_set_position(g_camera, 5, 4, 8);
SceneNode* c1 = scene_create_node(&g_scene, "Cube1");
scene_set_position(c1, 0, 0.5f, 0); c1->user_type = 1;
SceneNode* c2 = scene_create_node(&g_scene, "Cube2");
scene_set_position(c2, -3, 0.5f, 2); c2->user_type = 1;
SceneNode* c3 = scene_create_node(&g_scene, "Cube3");
scene_set_position(c3, 3, 0.5f, -2); c3->user_type = 1; scene_set_scale(c3, 0.5f, 0.5f, 0.5f);
SceneNode* child = scene_create_node(&g_scene, "Child");
scene_set_position(child, 2, 0, 0); child->user_type = 1; scene_set_parent(child, c1);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_run(engine);
mesh_destroy(g_cube); mesh_destroy(g_plane); shader_destroy(g_shader);
graphics_device_destroy(g_dev); engine_shutdown(engine);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
// scene_test2.c - 最小化场景测试
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static void on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
float clear[4] = {0.2f, 0.3f, 0.5f, 1.0f};
g_dev->context->lpVtbl->ClearRenderTargetView(g_dev->context, g_dev->render_target, clear);
D3D11_VIEWPORT vp = {0};
vp.Width = (float)g_dev->width; vp.Height = (float)g_dev->height;
vp.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) {
if (key == VK_ESCAPE) engine_request_quit(engine);
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR cmd, int nS) {
(void)hI;(void)hP;(void)cmd;(void)nS;
LOG_INFO("=== lity3d Scene Test ===");
EngineConfig cfg = engine_default_config();
cfg.app_name = "lity3d Scene Test";
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;
LOG_INFO("D3D11 ready");
Scene scene; scene_init(&scene);
SceneNode* n1 = scene_create_node(&scene, "A");
scene_set_position(n1, 1, 0, 0);
SceneNode* n2 = scene_create_node(&scene, "B");
scene_set_parent(n2, n1);
scene_set_position(n2, 0, 2, 0);
scene_update(&scene);
LOG_INFO("A world pos: (%.1f,%.1f,%.1f)", scene.nodes[1].world_matrix.m[3][0], scene.nodes[1].world_matrix.m[3][1], scene.nodes[1].world_matrix.m[3][2]);
LOG_INFO("B world pos: (%.1f,%.1f,%.1f)", scene.nodes[2].world_matrix.m[3][0], scene.nodes[2].world_matrix.m[3][1], scene.nodes[2].world_matrix.m[3][2]);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_run(engine);
graphics_device_destroy(g_dev);
engine_shutdown(engine);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
# lity3d 积木脚本 - 测试解释器
# 开始 -> 移动物体 -> 打印 -> 循环3次 -> 移动物体
[B]
id=1
name=开始
p=100,100
c=0
[B]
id=2
name=移动物体
p=350,100
c=1
[B]
id=3
name=打印
p=600,100
d=Hello World
c=3
[B]
id=4
name=循环
p=100,250
d=3
c=2
[B]
id=5
name=移动物体
p=350,250
c=1
# 连线: 开始.输出0 -> 移动物体.执行0
[W]
a=10000
b=20000
# 移动物体.执行0 -> 打印.执行0
[W]
a=20000
b=30000
# 打印.执行0 -> 循环.执行0
[W]
a=30000
b=40000
# 循环.执行0 -> 移动物体2.执行0
[W]
a=40000
b=50000
+118
View File
@@ -0,0 +1,118 @@
// ============================================================================
// audio_demo.c - XAudio2 音频系统演示
// 生成 440Hz 正弦波并播放 2 秒,无需外部 WAV 文件
// ============================================================================
#include "../Engine/Audio/audio.h"
#include "../Engine/Core/logger.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#pragma comment(lib, "winmm.lib")
// 生成标准 PCM WAV 数据 (44.1kHz, 16bit, 单声道)
static uint8_t* generate_sine_wave_wav(float frequency, float duration_sec, uint32_t* out_size) {
const uint32_t sample_rate = 44100;
const uint16_t channels = 1;
const uint16_t bits_per_sample = 16;
const uint16_t block_align = channels * bits_per_sample / 8;
const uint32_t byte_rate = sample_rate * block_align;
const uint32_t num_samples = (uint32_t)(sample_rate * duration_sec);
const uint32_t data_size = num_samples * block_align;
const uint32_t file_size = 44 + data_size;
uint8_t* wav = (uint8_t*)malloc(file_size);
if (!wav) return NULL;
// RIFF 头
memcpy(wav + 0, "RIFF", 4);
memcpy(wav + 4, &file_size, 4);
memcpy(wav + 8, "WAVE", 4);
// fmt chunk
memcpy(wav + 12, "fmt ", 4);
uint32_t fmt_size = 16;
memcpy(wav + 16, &fmt_size, 4);
uint16_t audio_format = 1; // PCM
memcpy(wav + 20, &audio_format, 2);
memcpy(wav + 22, &channels, 2);
memcpy(wav + 24, &sample_rate, 4);
memcpy(wav + 28, &byte_rate, 4);
memcpy(wav + 32, &block_align, 2);
memcpy(wav + 34, &bits_per_sample, 2);
// data chunk
memcpy(wav + 36, "data", 4);
memcpy(wav + 40, &data_size, 4);
// 生成正弦波样本
int16_t* samples = (int16_t*)(wav + 44);
float amplitude = 0.3f; // 避免削波
for (uint32_t i = 0; i < num_samples; i++) {
float t = (float)i / (float)sample_rate;
// 简单淡出,避免咔嗒声
float envelope = 1.0f;
if (i < 1000) envelope = (float)i / 1000.0f;
if (i > num_samples - 1000) envelope = (float)(num_samples - i) / 1000.0f;
float sample = sinf(2.0f * 3.14159265f * frequency * t) * amplitude * envelope;
samples[i] = (int16_t)(sample * 32767.0f);
}
*out_size = file_size;
return wav;
}
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
printf("Lity3D - Audio Demo\n");
printf("========================\n");
AudioSystem* audio = audio_create();
if (!audio) {
printf("ERROR: Failed to create XAudio2 audio system\n");
return 1;
}
printf("XAudio2 audio system created.\n");
AudioSource* source = audio_source_create(audio);
if (!source) {
printf("ERROR: Failed to create audio source\n");
audio_destroy(audio);
return 1;
}
uint32_t wav_size = 0;
uint8_t* wav_data = generate_sine_wave_wav(440.0f, 2.0f, &wav_size);
if (!wav_data) {
printf("ERROR: Failed to generate sine wave\n");
audio_source_destroy(source);
audio_destroy(audio);
return 1;
}
if (!audio_load_wav_memory(source, wav_data, wav_size)) {
printf("ERROR: Failed to load generated WAV into audio source\n");
free(wav_data);
audio_source_destroy(source);
audio_destroy(audio);
return 1;
}
free(wav_data);
printf("Generated 440Hz sine wave loaded.\n");
printf("Playing 2 seconds...\n");
audio_source_play(source);
// 等待播放结束 (约 2 秒 + 缓冲)
Sleep(2500);
audio_source_destroy(source);
audio_destroy(audio);
printf("Audio demo finished successfully.\n");
return 0;
}
+80
View File
@@ -0,0 +1,80 @@
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <math.h>
#include <stdint.h>
#include <string.h>
typedef struct { float p[3], n[3], u[2], c[4]; } V3D;
static ID3D11Device* g_dev; static ID3D11DeviceContext* g_ctx;
static IDXGISwapChain* g_sc; static ID3D11RenderTargetView* g_rtv;
static ID3D11VertexShader* g_vs; static ID3D11PixelShader* g_ps;
static ID3D11InputLayout* g_il; static ID3D11Buffer* g_cb;
static int key_l=0,key_r=0,key_u=0,key_d=0,key_sp=0;
static int dead=0, score=0;
static float px=0, py=0, speed=0.4f;
#define NOBS 15
static float ox[NOBS],oy[NOBS],oz[NOBS],osz[NOBS];
static int oa[NOBS],oc=0; static float st=0;
static void Init(HWND h){
HRESULT hr;DXGI_SWAP_CHAIN_DESC sd;memset(&sd,0,sizeof(sd));sd.BufferCount=2;sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;sd.BufferDesc.RefreshRate.Numerator=60;sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;sd.OutputWindow=h;sd.SampleDesc.Count=1;sd.Windowed=TRUE;sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL fl=D3D_FEATURE_LEVEL_11_0,gt;hr=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,&fl,1,D3D11_SDK_VERSION,&sd,&g_sc,&g_dev,&gt,&g_ctx);if(FAILED(hr))return;
ID3D11Texture2D*bb=NULL;hr=g_sc->lpVtbl->GetBuffer(g_sc,0,&IID_ID3D11Texture2D,(void**)&bb);if(FAILED(hr)||!bb)return;hr=g_dev->lpVtbl->CreateRenderTargetView(g_dev,(ID3D11Resource*)bb,NULL,&g_rtv);bb->lpVtbl->Release(bb);if(FAILED(hr))return;
const char*vs="cbuffer CB:register(b0){float4x4 WVP;};struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float4 c:COLOR;};O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.c=i.c;return o;}";
const char*ps="float4 main(float4 p:SV_POSITION, float4 c:COLOR):SV_Target{return c;}";
ID3DBlob*b1,*b2,*e;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;
g_dev->lpVtbl->CreateVertexShader(g_dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);g_dev->lpVtbl->CreatePixelShader(g_dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC el[4];memset(el,0,sizeof(el));el[0].SemanticName="POSITION";el[0].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[0].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[1].SemanticName="NORMAL";el[1].AlignedByteOffset=12;el[1].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[1].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[2].SemanticName="TEXCOORD";el[2].AlignedByteOffset=24;el[2].Format=DXGI_FORMAT_R32G32_FLOAT;el[2].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[3].SemanticName="COLOR";el[3].AlignedByteOffset=32;el[3].Format=DXGI_FORMAT_R32G32B32A32_FLOAT;el[3].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;
g_dev->lpVtbl->CreateInputLayout(g_dev,el,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);b1->lpVtbl->Release(b1);b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd;memset(&cbd,0,sizeof(cbd));cbd.ByteWidth=256;cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;g_dev->lpVtbl->CreateBuffer(g_dev,&cbd,NULL,&g_cb);
}
static void Cube(float cx,float cy,float cz,float s,float cr,float cg,float cb){
V3D v[24];float f[6][4][3]={{{-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}},{{1,-1,-1},{1,-1,1},{1,1,1},{1,1,-1}},{{1,-1,1},{-1,-1,1},{-1,1,1},{1,1,1}},{{-1,-1,1},{-1,-1,-1},{-1,1,-1},{-1,1,1}},{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1}},{{-1,-1,1},{1,-1,1},{1,-1,-1},{-1,-1,-1}}};float nm[6][3]={{0,0,-1},{1,0,0},{0,0,1},{-1,0,0},{0,1,0},{0,-1,0}};
for(int fi=0;fi<6;fi++)for(int vi=0;vi<4;vi++){int i=fi*4+vi;v[i].p[0]=f[fi][vi][0]*s+cx;v[i].p[1]=f[fi][vi][1]*s+cy;v[i].p[2]=f[fi][vi][2]*s+cz;v[i].n[0]=nm[fi][0];v[i].n[1]=nm[fi][1];v[i].n[2]=nm[fi][2];v[i].c[0]=cr;v[i].c[1]=cg;v[i].c[2]=cb;v[i].c[3]=1;}
uint32_t ix[36];for(int fi=0;fi<6;fi++){int b=fi*4;ix[fi*6]=b;ix[fi*6+1]=b+1;ix[fi*6+2]=b+2;ix[fi*6+3]=b;ix[fi*6+4]=b+2;ix[fi*6+5]=b+3;}
ID3D11Buffer*vb,*ib;D3D11_BUFFER_DESC bd;D3D11_SUBRESOURCE_DATA sd;memset(&bd,0,sizeof(bd));memset(&sd,0,sizeof(sd));bd.ByteWidth=sizeof(V3D)*24;bd.Usage=D3D11_USAGE_DEFAULT;bd.BindFlags=D3D11_BIND_VERTEX_BUFFER;sd.pSysMem=v;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&vb);bd.ByteWidth=sizeof(uint32_t)*36;bd.BindFlags=D3D11_BIND_INDEX_BUFFER;sd.pSysMem=ix;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&ib);
UINT s2=sizeof(V3D),o=0;g_ctx->lpVtbl->IASetVertexBuffers(g_ctx,0,1,&vb,&s2,&o);g_ctx->lpVtbl->IASetIndexBuffer(g_ctx,ib,DXGI_FORMAT_R32_UINT,0);g_ctx->lpVtbl->DrawIndexed(g_ctx,36,0,0);ib->lpVtbl->Release(ib);vb->lpVtbl->Release(vb);
}
static void Spawn(void){for(int i=0;i<NOBS;i++){if(!oa[i]){oa[i]=1;ox[i]=-1.0f+(float)(rand()%200)/100.0f;oy[i]=-0.6f+(float)(rand()%120)/100.0f;oz[i]=1.95f;osz[i]=0.08f+(float)(rand()%80)/400.0f;oc++;return;}}}
static void Frame(void){
float dt=0.016f,spd=speed*dt;
if(dead){if(key_sp){dead=0;score=0;speed=0.4f;px=py=0;oc=0;st=0;for(int i=0;i<NOBS;i++)oa[i]=0;}}
else{
if(key_l)px-=spd;if(key_r)px+=spd;if(key_u)py+=spd;if(key_d)py-=spd;
if(px<-1.3f)px=-1.3f;if(px>1.3f)px=1.3f;if(py<-0.9f)py=-0.9f;if(py>0.9f)py=0.9f;
st+=dt;if(st>0.6f/speed){Spawn();st=0;}
for(int i=0;i<NOBS;i++){
if(!oa[i])continue;oz[i]-=speed*dt;
if(oz[i]<1.0f){oa[i]=0;oc--;score++;speed=0.4f+score*0.05f;if(speed>2.0f)speed=2.0f;}
float dx=px>ox[i]?px-ox[i]:ox[i]-px;float dy=py>oy[i]?py-oy[i]:oy[i]-py;float dz=1.5f>oz[i]?1.5f-oz[i]:oz[i]-1.5f;
float md=0.12f+osz[i];if(dx<md&&dy<md&&dz<md)dead=1;
}
}
float fov=1.0f/tanf(0.4f),asp=1280.0f/720.0f;
float mvp[16];memset(mvp,0,sizeof(mvp));
mvp[0]=fov/asp;mvp[5]=-fov;mvp[10]=1;mvp[11]=-1;mvp[14]=0.1f;mvp[15]=1;
float cl[4]={.08f,.1f,.14f,1};if(dead){cl[0]=.12f;cl[1]=.02f;cl[2]=.02f;}
g_ctx->lpVtbl->OMSetRenderTargets(g_ctx,1,&g_rtv,NULL);g_ctx->lpVtbl->ClearRenderTargetView(g_ctx,g_rtv,cl);
D3D11_VIEWPORT vp;memset(&vp,0,sizeof(vp));vp.Width=1280;vp.Height=720;vp.MaxDepth=1;g_ctx->lpVtbl->RSSetViewports(g_ctx,1,&vp);
D3D11_RASTERIZER_DESC rd;memset(&rd,0,sizeof(rd));rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;ID3D11RasterizerState*rs;g_dev->lpVtbl->CreateRasterizerState(g_dev,&rd,&rs);g_ctx->lpVtbl->RSSetState(g_ctx,rs);
D3D11_DEPTH_STENCIL_DESC ds;memset(&ds,0,sizeof(ds));ds.DepthEnable=FALSE;ID3D11DepthStencilState*dss;g_dev->lpVtbl->CreateDepthStencilState(g_dev,&ds,&dss);g_ctx->lpVtbl->OMSetDepthStencilState(g_ctx,dss,0);
g_ctx->lpVtbl->IASetPrimitiveTopology(g_ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);g_ctx->lpVtbl->VSSetShader(g_ctx,g_vs,NULL,0);g_ctx->lpVtbl->PSSetShader(g_ctx,g_ps,NULL,0);g_ctx->lpVtbl->IASetInputLayout(g_ctx,g_il);g_ctx->lpVtbl->VSSetConstantBuffers(g_ctx,0,1,&g_cb);g_ctx->lpVtbl->UpdateSubresource(g_ctx,(ID3D11Resource*)g_cb,0,NULL,mvp,0,0);
if(dead){Cube(0,0,1.5f,0.4f,.9f,.1f,.1f);}
else{
Cube(px,py,1.5f,0.12f,.1f,.9f,.3f);
for(int i=0;i<NOBS;i++){if(!oa[i])continue;float fl=.6f+.4f*sinf(oz[i]*8.0f);Cube(ox[i],oy[i],oz[i],osz[i],fl,.15f,.1f);}
}
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);g_sc->lpVtbl->Present(g_sc,1,0);
}
static LRESULT CALLBACK WP(HWND h,UINT m,WPARAM w,LPARAM l){switch(m){case WM_KEYDOWN:switch(w){case VK_LEFT:key_l=1;break;case VK_RIGHT:key_r=1;break;case VK_UP:key_u=1;break;case VK_DOWN:key_d=1;break;case 'A':key_l=1;break;case 'D':key_r=1;break;case 'W':key_u=1;break;case 'S':key_d=1;break;case VK_SPACE:key_sp=1;break;case VK_ESCAPE:PostQuitMessage(0);break;}break;case WM_KEYUP:switch(w){case VK_LEFT:key_l=0;break;case VK_RIGHT:key_r=0;break;case VK_UP:key_u=0;break;case VK_DOWN:key_d=0;break;case 'A':key_l=0;break;case 'D':key_r=0;break;case 'W':key_u=0;break;case 'S':key_d=0;break;case VK_SPACE:key_sp=0;break;}break;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProcW(h,m,w,l);}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int){srand(42);WNDCLASSEXW wc;memset(&wc,0,sizeof(wc));wc.cbSize=sizeof(wc);wc.style=CS_CLASSDC;wc.lpfnWndProc=WP;wc.hInstance=hI;wc.lpszClassName=L"dodge";RegisterClassExW(&wc);HWND hwnd=CreateWindowW(L"dodge",L"Cube Dodge - WASD/Arrows move, Space restart",WS_OVERLAPPEDWINDOW,200,100,1280,720,NULL,NULL,hI,NULL);if(!hwnd)return -1;ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);Init(hwnd);MSG msg;while(1){while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);if(msg.message==WM_QUIT)goto done;}Frame();}done:if(g_cb)g_cb->lpVtbl->Release(g_cb);if(g_il)g_il->lpVtbl->Release(g_il);if(g_vs)g_vs->lpVtbl->Release(g_vs);if(g_ps)g_ps->lpVtbl->Release(g_ps);if(g_rtv)g_rtv->lpVtbl->Release(g_rtv);if(g_sc)g_sc->lpVtbl->Release(g_sc);if(g_ctx)g_ctx->lpVtbl->Release(g_ctx);if(g_dev)g_dev->lpVtbl->Release(g_dev);return 0;}
+222
View File
@@ -0,0 +1,222 @@
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <math.h>
#include <stdint.h>
#include <string.h>
typedef struct { float p[3], n[3], u[2], c[4]; } V3D;
static ID3D11Device* g_dev = NULL;
static ID3D11DeviceContext* g_ctx = NULL;
static IDXGISwapChain* g_sc = NULL;
static ID3D11RenderTargetView* g_rtv = NULL;
static ID3D11VertexShader* g_vs = NULL;
static ID3D11PixelShader* g_ps = NULL;
static ID3D11InputLayout* g_il = NULL;
static ID3D11Buffer* g_cb = NULL;
static float cam_x = 0, cam_z = 0;
static int key_w = 0, key_s = 0, key_a = 0, key_d = 0;
static int key_left = 0, key_right = 0;
static int sel = 0;
static void Init(HWND h) {
HRESULT hr;
DXGI_SWAP_CHAIN_DESC sd;
memset(&sd, 0, sizeof(sd));
sd.BufferCount = 2;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = h;
sd.SampleDesc.Count = 1;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_0;
D3D_FEATURE_LEVEL got;
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &fl, 1,
D3D11_SDK_VERSION, &sd, &g_sc, &g_dev, &got, &g_ctx);
if (FAILED(hr)) { return; }
ID3D11Texture2D* bb = NULL;
hr = g_sc->lpVtbl->GetBuffer(g_sc, 0, &IID_ID3D11Texture2D, (void**)&bb);
if (FAILED(hr) || !bb) { return; }
hr = g_dev->lpVtbl->CreateRenderTargetView(g_dev, (ID3D11Resource*)bb, NULL, &g_rtv);
bb->lpVtbl->Release(bb);
if (FAILED(hr)) { return; }
const char* vs_src =
"cbuffer CB:register(b0){float4x4 WVP;};"
"struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};"
"struct O{float4 p:SV_POSITION;float4 c:COLOR;};"
"O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.c=i.c;return o;}";
const char* ps_src =
"float4 main(float4 pos:SV_POSITION, float4 col:COLOR):SV_Target{return col;}";
ID3DBlob *b1 = NULL, *b2 = NULL, *e1 = NULL, *e2 = NULL;
D3DCompile(vs_src, (UINT)strlen(vs_src), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &b1, &e1);
if (e1) { e1->lpVtbl->Release(e1); e1 = NULL; }
D3DCompile(ps_src, (UINT)strlen(ps_src), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &b2, &e2);
if (e2) { e2->lpVtbl->Release(e2); e2 = NULL; }
if (!b1 || !b2) { return; }
g_dev->lpVtbl->CreateVertexShader(g_dev, b1->lpVtbl->GetBufferPointer(b1), b1->lpVtbl->GetBufferSize(b1), NULL, &g_vs);
g_dev->lpVtbl->CreatePixelShader(g_dev, b2->lpVtbl->GetBufferPointer(b2), b2->lpVtbl->GetBufferSize(b2), NULL, &g_ps);
D3D11_INPUT_ELEMENT_DESC el[4];
memset(el, 0, sizeof(el));
el[0].SemanticName = "POSITION"; el[0].Format = DXGI_FORMAT_R32G32B32_FLOAT; el[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
el[1].SemanticName = "NORMAL"; el[1].AlignedByteOffset = 12; el[1].Format = DXGI_FORMAT_R32G32B32_FLOAT; el[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
el[2].SemanticName = "TEXCOORD"; el[2].AlignedByteOffset = 24; el[2].Format = DXGI_FORMAT_R32G32_FLOAT; el[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
el[3].SemanticName = "COLOR"; el[3].AlignedByteOffset = 32; el[3].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; el[3].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
g_dev->lpVtbl->CreateInputLayout(g_dev, el, 4, b1->lpVtbl->GetBufferPointer(b1), b1->lpVtbl->GetBufferSize(b1), &g_il);
b1->lpVtbl->Release(b1); b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd;
memset(&cbd, 0, sizeof(cbd));
cbd.ByteWidth = 256; cbd.Usage = D3D11_USAGE_DEFAULT; cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
g_dev->lpVtbl->CreateBuffer(g_dev, &cbd, NULL, &g_cb);
}
static void DrawCube(float cx, float cy, float cz, float s, float cr, float cg, float cb2) {
V3D v[24];
float f[6][4][3] = {
{{-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}},
{{1,-1,-1},{1,-1,1},{1,1,1},{1,1,-1}},
{{1,-1,1},{-1,-1,1},{-1,1,1},{1,1,1}},
{{-1,-1,1},{-1,-1,-1},{-1,1,-1},{-1,1,1}},
{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1}},
{{-1,-1,1},{1,-1,1},{1,-1,-1},{-1,-1,-1}}
};
float nm[6][3] = {{0,0,-1},{1,0,0},{0,0,1},{-1,0,0},{0,1,0},{0,-1,0}};
for (int fi = 0; fi < 6; fi++)
for (int vi = 0; vi < 4; vi++) {
int i = fi * 4 + vi;
v[i].p[0] = f[fi][vi][0] * s + cx;
v[i].p[1] = f[fi][vi][1] * s + cy;
v[i].p[2] = f[fi][vi][2] * s + cz;
v[i].n[0] = nm[fi][0]; v[i].n[1] = nm[fi][1]; v[i].n[2] = nm[fi][2];
v[i].c[0] = cr; v[i].c[1] = cg; v[i].c[2] = cb2; v[i].c[3] = 1;
}
uint32_t ix[36];
for (int fi = 0; fi < 6; fi++) {
int b = fi * 4;
ix[fi*6]=b; ix[fi*6+1]=b+1; ix[fi*6+2]=b+2; ix[fi*6+3]=b; ix[fi*6+4]=b+2; ix[fi*6+5]=b+3;
}
ID3D11Buffer *vb = NULL, *ib = NULL;
D3D11_BUFFER_DESC bd; D3D11_SUBRESOURCE_DATA sd;
memset(&bd, 0, sizeof(bd)); memset(&sd, 0, sizeof(sd));
bd.ByteWidth = sizeof(V3D) * 24; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = v; g_dev->lpVtbl->CreateBuffer(g_dev, &bd, &sd, &vb);
bd.ByteWidth = sizeof(uint32_t) * 36; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = ix; g_dev->lpVtbl->CreateBuffer(g_dev, &bd, &sd, &ib);
UINT s2 = sizeof(V3D), o = 0;
g_ctx->lpVtbl->IASetVertexBuffers(g_ctx, 0, 1, &vb, &s2, &o);
g_ctx->lpVtbl->IASetIndexBuffer(g_ctx, ib, DXGI_FORMAT_R32_UINT, 0);
g_ctx->lpVtbl->DrawIndexed(g_ctx, 36, 0, 0);
ib->lpVtbl->Release(ib); vb->lpVtbl->Release(vb);
}
static void Frame(void) {
float dt = 0.016f, spd = 3.0f * dt;
if (key_w) cam_z += spd; if (key_s) cam_z -= spd;
if (key_a) cam_x -= spd; if (key_d) cam_x += spd;
if (key_left) sel = (sel + 3) % 4;
if (key_right) sel = (sel + 1) % 4;
float fov = 1.0f / tanf(0.4f), asp = 1280.0f / 720.0f;
float mvp[16]; memset(mvp, 0, sizeof(mvp));
mvp[0] = fov / asp; mvp[5] = -fov; mvp[10] = 1; mvp[11] = -1; mvp[14] = 0.1f; mvp[15] = 1;
float m2[16]; memcpy(m2, mvp, 64);
for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) { float t = m2[i*4+j]; m2[i*4+j] = m2[j*4+i]; m2[j*4+i] = t; }
float cl[4] = {.15f, .18f, .25f, 1};
g_ctx->lpVtbl->OMSetRenderTargets(g_ctx, 1, &g_rtv, NULL);
g_ctx->lpVtbl->ClearRenderTargetView(g_ctx, g_rtv, cl);
D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(vp));
vp.Width = 1280; vp.Height = 720; vp.MaxDepth = 1;
g_ctx->lpVtbl->RSSetViewports(g_ctx, 1, &vp);
D3D11_RASTERIZER_DESC rd; memset(&rd, 0, sizeof(rd));
rd.FillMode = D3D11_FILL_SOLID; rd.CullMode = D3D11_CULL_NONE;
ID3D11RasterizerState* rs = NULL;
g_dev->lpVtbl->CreateRasterizerState(g_dev, &rd, &rs);
g_ctx->lpVtbl->RSSetState(g_ctx, rs);
D3D11_DEPTH_STENCIL_DESC ds; memset(&ds, 0, sizeof(ds));
ds.DepthEnable = FALSE;
ID3D11DepthStencilState* dss = NULL;
g_dev->lpVtbl->CreateDepthStencilState(g_dev, &ds, &dss);
g_ctx->lpVtbl->OMSetDepthStencilState(g_ctx, dss, 0);
g_ctx->lpVtbl->IASetPrimitiveTopology(g_ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
g_ctx->lpVtbl->VSSetShader(g_ctx, g_vs, NULL, 0);
g_ctx->lpVtbl->PSSetShader(g_ctx, g_ps, NULL, 0);
g_ctx->lpVtbl->IASetInputLayout(g_ctx, g_il);
g_ctx->lpVtbl->VSSetConstantBuffers(g_ctx, 0, 1, &g_cb);
g_ctx->lpVtbl->UpdateSubresource(g_ctx, (ID3D11Resource*)g_cb, 0, NULL, m2, 0, 0);
float colors[4][3] = {{1,.3f,.3f},{.3f,1,.3f},{.3f,.3f,1},{.8f,.8f,.2f}};
float sizes[4] = {0.3f, 0.25f, 0.2f, 0.35f};
for (int i = 0; i < 4; i++) {
float cr = colors[i][0], cg = colors[i][1], cb = colors[i][2];
if (i == sel) { cr = 1; cg = 1; cb = 1; }
DrawCube(-0.8f + i * 0.6f - cam_x, 0, 1.5f - cam_z, sizes[i], cr, cg, cb);
}
if (rs) rs->lpVtbl->Release(rs);
if (dss) dss->lpVtbl->Release(dss);
g_sc->lpVtbl->Present(g_sc, 1, 0);
}
static LRESULT CALLBACK WP(HWND h, UINT m, WPARAM w, LPARAM l) {
switch (m) {
case WM_KEYDOWN:
switch (w) {
case 'W': key_w = 1; break; case 'S': key_s = 1; break;
case 'A': key_a = 1; break; case 'D': key_d = 1; break;
case VK_LEFT: key_left = 1; break; case VK_RIGHT: key_right = 1; break;
case '1': sel = 0; break; case '2': sel = 1; break;
case '3': sel = 2; break; case '4': sel = 3; break;
case VK_ESCAPE: PostQuitMessage(0); break;
}
break;
case WM_KEYUP:
switch (w) {
case 'W': key_w = 0; break; case 'S': key_s = 0; break;
case 'A': key_a = 0; break; case 'D': key_d = 0; break;
case VK_LEFT: key_left = 0; break; case VK_RIGHT: key_right = 0; break;
}
break;
case WM_DESTROY: PostQuitMessage(0); return 0;
}
return DefWindowProcW(h, m, w, l);
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR nC, int nS) {
(void)hP; (void)nC; (void)nS;
WNDCLASSEXW wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WP;
wc.hInstance = hI;
wc.lpszClassName = L"game_test_cls";
RegisterClassExW(&wc);
HWND hwnd = CreateWindowW(L"game_test_cls", L"lity3d Game Test", WS_OVERLAPPEDWINDOW,
100, 100, 1280, 720, NULL, NULL, hI, NULL);
if (!hwnd) { MessageBoxA(NULL, "CreateWindow failed", "Error", MB_OK); return -1; }
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
Init(hwnd);
MSG msg;
while (1) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg); DispatchMessage(&msg);
if (msg.message == WM_QUIT) goto done;
}
Frame();
}
done:
if (g_cb) g_cb->lpVtbl->Release(g_cb);
if (g_il) g_il->lpVtbl->Release(g_il);
if (g_vs) g_vs->lpVtbl->Release(g_vs);
if (g_ps) g_ps->lpVtbl->Release(g_ps);
if (g_rtv) g_rtv->lpVtbl->Release(g_rtv);
if (g_sc) g_sc->lpVtbl->Release(g_sc);
if (g_ctx) g_ctx->lpVtbl->Release(g_ctx);
if (g_dev) g_dev->lpVtbl->Release(g_dev);
return 0;
}
+89
View File
@@ -0,0 +1,89 @@
#include "../Engine/Core/engine.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 <d3dcompiler.h>
static GraphicsDevice* g_dev = NULL;
static ID3D11VertexShader* g_vs = NULL;
static ID3D11PixelShader* g_ps = NULL;
static ID3D11InputLayout* g_il = NULL;
static ID3D11Buffer* g_cb = NULL;
static float cam_x=0, cam_z=2, cam_yaw=0;
static float mx=0, my=0;
static int keys[256]={0};
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
static void draw_quad(ID3D11DeviceContext* ctx, float x, float y, float z, float s, float r, float g, float b) {
V v[4]={{{x-s,y-s,z},{0,0,-1},{0,0},{r,g,b,1}},{{x+s,y-s,z},{0,0,-1},{1,0},{r,g,b,1}},
{{x+s,y+s,z},{0,0,-1},{1,1},{r,g,b,1}},{{x-s,y+s,z},{0,0,-1},{0,1},{r,g,b,1}}};
uint32_t idx[6]={0,1,2,0,2,3};
ID3D11Buffer *vb=NULL,*ib=NULL; D3D11_BUFFER_DESC bd={}; D3D11_SUBRESOURCE_DATA sd={};
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 s2=sizeof(V),o=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&s2,&o);
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 on_render(Engine* engine) {
if(!g_dev) return;
ID3D11DeviceContext* ctx=g_dev->context;
float dt=(float)engine->delta_time, sp=3.0f*dt;
float fx=-sinf(cam_yaw),fz=-cosf(cam_yaw),rx=cosf(cam_yaw),rz=-sinf(cam_yaw);
if(keys['W']){cam_x+=fx*sp;cam_z+=fz*sp;}
if(keys['S']){cam_x-=fx*sp;cam_z-=fz*sp;}
if(keys['A']){cam_x-=rx*sp;cam_z-=rz*sp;}
if(keys['D']){cam_x+=rx*sp;cam_z+=rz*sp;}
float sw=(float)g_dev->width,sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f),asp=sw/sh;
float mvp[16]={0}; mvp[0]=fov/asp;mvp[5]=-fov;mvp[10]=1;mvp[11]=-1;mvp[14]=0.1f;mvp[15]=1;
Mat4 pm,pt;memcpy(&pm,mvp,sizeof(Mat4));mat4_transpose(&pm,&pt);
graphics_device_clear(g_dev,0.12f,0.14f,0.18f,1.0f);
D3D11_VIEWPORT vp={};vp.Width=sw;vp.Height=sh;vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={};rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState*rs=NULL;g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={};dsd.DepthEnable=FALSE;
ID3D11DepthStencilState*dss=NULL;g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->lpVtbl->VSSetShader(ctx,g_vs,NULL,0);ctx->lpVtbl->PSSetShader(ctx,g_ps,NULL,0);
ctx->lpVtbl->IASetInputLayout(ctx,g_il);ctx->lpVtbl->VSSetConstantBuffers(ctx,0,1,&g_cb);
ctx->lpVtbl->UpdateSubresource(ctx,(ID3D11Resource*)g_cb,0,NULL,&pt,0,0);
for(int x=-5;x<=5;x++)for(int z=-5;z<=5;z++){float c=((x+z)%2==0)?.4f:.3f;draw_quad(ctx,(float)x,(float)z,0.5f,0.5f,c,c,c);}
draw_quad(ctx,0,0,0.5f,0.4f,1,0.3f,0.3f);draw_quad(ctx,2,0,0.5f,0.3f,.3f,1,.3f);draw_quad(ctx,-2,0,0.5f,0.3f,.3f,.3f,1);draw_quad(ctx,0,2,0.5f,0.5f,.8f,.8f,.2f);
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine*e,int k){if(k>=0&&k<256)keys[k]=1;if(k==VK_ESCAPE)engine_request_quit(e);}
static void on_mouse_move(int x,int y){cam_yaw+=(x-mx)*0.003f;mx=(float)x;my=(float)y;}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int){
EngineConfig cfg=engine_default_config();cfg.app_name="lity3d Input Test";
Engine*e=engine_create();if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
ID3DBlob*b1=NULL,*b2=NULL,*er=NULL;
const char*vs="cbuffer CB:register(b0){float4x4 WVP;};struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.n=i.n;o.c=i.c;return o;}";
const char*ps="struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};float4 main(O i){return i.c;}";
D3DCompile(vs,(UINT)strlen(vs),NULL,NULL,NULL,"main","vs_5_0",0,0,&b1,&er);if(er){er->lpVtbl->Release(er);er=NULL;}
D3DCompile(ps,(UINT)strlen(ps),NULL,NULL,NULL,"main","ps_5_0",0,0,&b2,&er);if(er){er->lpVtbl->Release(er);er=NULL;}
ID3D11Device*dev=g_dev->device;
dev->lpVtbl->CreateVertexShader(dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);
dev->lpVtbl->CreatePixelShader(dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC il[]={{"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,il,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);b1->lpVtbl->Release(b1);b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd={};cbd.ByteWidth=256;cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;dev->lpVtbl->CreateBuffer(dev,&cbd,NULL,&g_cb);
window_set_key_callback(e->window,on_key);window_set_mouse_move_callback(e->window,on_mouse_move);
engine_set_render_callback(e,on_render);engine_run(e);
if(g_vs)g_vs->lpVtbl->Release(g_vs);if(g_ps)g_ps->lpVtbl->Release(g_ps);if(g_il)g_il->lpVtbl->Release(g_il);if(g_cb)g_cb->lpVtbl->Release(g_cb);
graphics_device_destroy(g_dev);engine_shutdown(e);return 0;
}
+142
View File
@@ -0,0 +1,142 @@
// light_test.c - 光照系统测试(基��?raw3d_test 已验证模式)
#include "../Engine/Core/engine.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/lighting.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include <d3dcompiler.h>
static GraphicsDevice* g_dev = NULL;
static Light* g_sun = NULL;
static float g_time = 0;
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
// 编译带光照的着色器
static ID3D11VertexShader* g_vs = NULL;
static ID3D11PixelShader* g_ps = NULL;
static ID3D11InputLayout* g_il = NULL;
static ID3D11Buffer* g_cb = NULL;
static const char* vs_src =
"cbuffer CB:register(b0){float4x4 WVP;};"
"struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};"
"struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};"
"O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.n=i.n;o.c=i.c;return o;}";
static const char* ps_src =
"cbuffer Light:register(b1){float3 lightDir;float pad1;float3 lightColor;float intensity;float3 ambColor;float pad2;};"
"struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};"
"float4 main(O i):SV_TARGET{"
"float3 n=normalize(i.n);"
"float diff=max(dot(n,-normalize(lightDir)),0.0);"
"float3 col=i.c.rgb*(ambColor+lightColor*diff*intensity);"
"return float4(col,1.0);}";
static void InitShaders() {
ID3DBlob *b1=NULL,*b2=NULL,*e=NULL;
D3DCompile(vs_src,(UINT)strlen(vs_src),NULL,NULL,NULL,"main","vs_5_0",0,0,&b1,&e);
if(e){e->lpVtbl->Release(e);e=NULL;}
D3DCompile(ps_src,(UINT)strlen(ps_src),NULL,NULL,NULL,"main","ps_5_0",0,0,&b2,&e);
if(e){e->lpVtbl->Release(e);e=NULL;}
if(!b1||!b2) return;
ID3D11Device* dev = g_dev->device;
dev->lpVtbl->CreateVertexShader(dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);
dev->lpVtbl->CreatePixelShader(dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC il[]={
{"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,il,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);
b1->lpVtbl->Release(b1); b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd={}; cbd.ByteWidth=256; cbd.Usage=D3D11_USAGE_DEFAULT;
cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;
dev->lpVtbl->CreateBuffer(dev,&cbd,NULL,&g_cb);
}
static void draw_lit_cube(ID3D11DeviceContext* ctx, ID3D11Device* dev, float cx, float cy, float cz, float s, float cr, float cg, float cb) {
V v[24];
float f[6][4][3]={{{-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1}},{{1,-1,-1},{1,-1,1},{1,1,1},{1,1,-1}},
{{1,-1,1},{-1,-1,1},{-1,1,1},{1,1,1}},{{-1,-1,1},{-1,-1,-1},{-1,1,-1},{-1,1,1}},
{{-1,1,-1},{1,1,-1},{1,1,1},{-1,1,1}},{{-1,-1,1},{1,-1,1},{1,-1,-1},{-1,-1,-1}}};
float nm[6][3]={{0,0,-1},{1,0,0},{0,0,1},{-1,0,0},{0,1,0},{0,-1,0}};
for(int fi=0;fi<6;fi++) for(int vi=0;vi<4;vi++){
int i=fi*4+vi;
v[i].p[0]=f[fi][vi][0]*s+cx; v[i].p[1]=f[fi][vi][1]*s+cy; v[i].p[2]=f[fi][vi][2]*s+cz;
v[i].n[0]=nm[fi][0]; v[i].n[1]=nm[fi][1]; v[i].n[2]=nm[fi][2];
v[i].c[0]=cr; v[i].c[1]=cg; v[i].c[2]=cb; v[i].c[3]=1;
}
uint32_t idx[36]; for(int fi=0;fi<6;fi++){int b=fi*4;idx[fi*6]=b;idx[fi*6+1]=b+1;idx[fi*6+2]=b+2;idx[fi*6+3]=b;idx[fi*6+4]=b+2;idx[fi*6+5]=b+3;}
ID3D11Buffer *vb,*ib;D3D11_BUFFER_DESC bd={};D3D11_SUBRESOURCE_DATA sd={};
bd.ByteWidth=sizeof(V)*24;bd.Usage=D3D11_USAGE_DEFAULT;bd.BindFlags=D3D11_BIND_VERTEX_BUFFER;sd.pSysMem=v;
dev->lpVtbl->CreateBuffer(dev,&bd,&sd,&vb);
bd.ByteWidth=sizeof(uint32_t)*36;bd.BindFlags=D3D11_BIND_INDEX_BUFFER;sd.pSysMem=idx;
dev->lpVtbl->CreateBuffer(dev,&bd,&sd,&ib);
UINT s2=sizeof(V),o=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&s2,&o);
ctx->lpVtbl->IASetIndexBuffer(ctx,ib,DXGI_FORMAT_R32_UINT,0);
ctx->lpVtbl->DrawIndexed(ctx,36,0,0);
ib->lpVtbl->Release(ib);vb->lpVtbl->Release(vb);
}
static void on_render(Engine* engine) {
if(!g_dev) return;
ID3D11DeviceContext* ctx=g_dev->context;
g_time+=0.016f;
float sw=(float)g_dev->width,sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f),asp=sw/sh;
float mvp[16]={0}; mvp[0]=fov/asp;mvp[5]=-fov;mvp[10]=1;mvp[11]=-1;mvp[14]=0.1f;mvp[15]=1;
Mat4 pm,pt;memcpy(&pm,mvp,sizeof(Mat4));mat4_transpose(&pm,&pt);
graphics_device_clear(g_dev,0.15f,0.18f,0.25f,1.0f);
D3D11_VIEWPORT vp={};vp.Width=sw;vp.Height=sh;vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={};rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState*rs=NULL;g_dev->device->lpVtbl->CreateRasterizerState(g_dev,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={};dsd.DepthEnable=FALSE;
ID3D11DepthStencilState*dss=NULL;g_dev->device->lpVtbl->CreateDepthStencilState(g_dev,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 绑定光照 shader
ctx->lpVtbl->VSSetShader(ctx,g_vs,NULL,0);
ctx->lpVtbl->PSSetShader(ctx,g_ps,NULL,0);
ctx->lpVtbl->IASetInputLayout(ctx,g_il);
ctx->lpVtbl->VSSetConstantBuffers(ctx,0,1,&g_cb);
ctx->lpVtbl->UpdateSubresource(ctx,(ID3D11Resource*)g_cb,0,NULL,&pt,0,0);
// 更新光照 CB
float light_data[16]={0};
light_data[0]=g_sun->direction[0];light_data[1]=g_sun->direction[1];light_data[2]=g_sun->direction[2];
light_data[4]=g_sun->color[0];light_data[5]=g_sun->color[1];light_data[6]=g_sun->color[2];
light_data[7]=g_sun->intensity;
light_data[8]=0.15f;light_data[9]=0.15f;light_data[10]=0.2f;
ctx->lpVtbl->VSSetConstantBuffers(ctx,1,1,&g_cb);
draw_lit_cube(ctx,g_dev, 0,0,0.5f, 0.3f, 1,0.3f,0.3f);
draw_lit_cube(ctx,g_dev,-0.8f,0,0.7f, 0.2f, 0.3f,1,0.3f);
draw_lit_cube(ctx,g_dev, 0.8f,0.3f,0.3f, 0.15f, 0.3f,0.3f,1);
draw_lit_cube(ctx,g_dev, 0,-0.2f,0.8f, 0.4f, 0.8f,0.8f,0.2f);
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine*e,int k){if(k==VK_ESCAPE)engine_request_quit(e);}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int){
EngineConfig cfg=engine_default_config();cfg.app_name="lity3d Light Test";
Engine*e=engine_create();if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
InitShaders();
g_sun=light_create_dir(1.0f,0.95f,0.8f,1.2f, -0.5f,-1.0f,-0.3f);
engine_set_render_callback(e,on_render);engine_set_key_callback(e,on_key);
engine_run(e);
light_destroy(g_sun);graphics_device_destroy(g_dev);engine_shutdown(e);
return 0;
}
+256
View File
@@ -0,0 +1,256 @@
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Core/timer.h"
#include "../Engine/Core/asset_manager.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/renderer.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/Rendering/material.h"
#include "../Engine/Rendering/texture.h"
#include "../Engine/Rendering/camera.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/Scene/scene_render.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
static Scene* g_scene = NULL;
static AssetManager* g_assets = NULL;
static Asset* g_shader_asset = NULL;
static Camera g_camera;
static int g_mouse_down[3] = {0};
static int g_last_mx = 0, g_last_my = 0, g_mouse_first = 1;
static float player_x = 0, player_z = -8, player_y = 0;
static float cam_yaw = 0, cam_pitch = 0.3f, cam_dist = 6.0f, cam_height = 3.0f;
static SceneNode* g_player = NULL;
static float player_vy = 0;
static int g_ground = 1;
static SceneNode* mknode(Scene* s, SceneNode* p, AssetManager* am, ID3D11Device* dev,
const char* name, MeshType mt, Vec3 pos, Vec3 scl, Material* mat) {
SceneNode* n = scene_create_node(s, name);
if (!n) return NULL;
if (p) scene_set_parent(n, p);
n->position = pos; n->scale = scl;
Asset* ma = asset_manager_load_mesh(am, dev, name);
if (!ma) {
const char* b = mt == MESH_CUBE ? "cube" : mt == MESH_SPHERE ? "sphere" : "plane";
ma = asset_manager_load_mesh(am, dev, b);
}
if (ma && mat) {
MeshRenderer* mr = mesh_renderer_create(mt, (Mesh*)asset_data(ma), mat);
scene_node_set_mesh_renderer(n, mr);
}
return n;
}
static Scene* create_world(AssetManager* am, ID3D11Device* dev) {
Scene* scene = (Scene*)malloc(sizeof(Scene));
if (!scene) return NULL;
scene_init(scene);
SceneNode* root = scene->root;
root->name[0] = '\0';
strncpy(root->name, "root", sizeof(root->name) - 1);
Shader* sh = (Shader*)asset_data(g_shader_asset);
Material* m_green = material_create(sh, NULL); m_green->color = (Vec4){0.25f, 0.55f, 0.2f, 1};
Material* m_dkgrn = material_create(sh, NULL); m_dkgrn->color = (Vec4){0.15f, 0.4f, 0.12f, 1};
Material* m_brown = material_create(sh, NULL); m_brown->color = (Vec4){0.45f, 0.28f, 0.15f, 1};
Material* m_stone = material_create(sh, NULL); m_stone->color = (Vec4){0.5f, 0.5f, 0.5f, 1};
Material* m_dstone = material_create(sh, NULL); m_dstone->color = (Vec4){0.35f, 0.35f, 0.38f, 1};
Material* m_red = material_create(sh, NULL); m_red->color = (Vec4){0.8f, 0.2f, 0.15f, 1};
Material* m_blue = material_create(sh, NULL); m_blue->color = (Vec4){0.2f, 0.4f, 0.8f, 1};
Material* m_yellow = material_create(sh, NULL); m_yellow->color = (Vec4){0.85f, 0.75f, 0.2f, 1};
Material* m_white = material_create(sh, NULL); m_white->color = (Vec4){0.85f, 0.85f, 0.85f, 1};
Material* m_water = material_create(sh, NULL); m_water->color = (Vec4){0.15f, 0.3f, 0.6f, 0.8f};
Material* m_orange = material_create(sh, NULL); m_orange->color = (Vec4){0.9f, 0.5f, 0.1f, 1};
Material* m_purple = material_create(sh, NULL); m_purple->color = (Vec4){0.5f, 0.15f, 0.6f, 1};
Material* m_player = material_create(sh, NULL); m_player->color = (Vec4){0.1f, 0.8f, 0.3f, 1};
Material* m_path = material_create(sh, NULL); m_path->color = (Vec4){0.6f, 0.55f, 0.45f, 1};
Material* m_hill = material_create(sh, NULL); m_hill->color = (Vec4){0.2f, 0.4f, 0.18f, 1};
Material* m_flower = material_create(sh, NULL); m_flower->color = (Vec4){0.9f, 0.2f, 0.4f, 1};
mknode(scene, root, am, dev, "ground", MESH_PLANE, (Vec3){0, -0.01f, 0}, (Vec3){40, 1, 40}, m_green);
g_player = mknode(scene, root, am, dev, "player", MESH_CUBE, (Vec3){player_x, 0.4f, player_z}, (Vec3){0.3f, 0.8f, 0.3f}, m_player);
// 村庄
mknode(scene, root, am, dev, "house1", MESH_CUBE, (Vec3){-5, 1, -5}, (Vec3){2, 2, 2}, m_red);
mknode(scene, root, am, dev, "house2", MESH_CUBE, (Vec3){-2, 1.25f, -8}, (Vec3){2.5f, 2.5f, 2}, m_blue);
mknode(scene, root, am, dev, "house3", MESH_CUBE, (Vec3){1, 0.9f, -6}, (Vec3){1.8f, 1.8f, 1.8f}, m_yellow);
mknode(scene, root, am, dev, "tower", MESH_CUBE, (Vec3){-7, 1.5f, -2}, (Vec3){1.5f, 3, 1.5f}, m_white);
mknode(scene, root, am, dev, "house4", MESH_CUBE, (Vec3){4, 1, -4}, (Vec3){2, 2, 2}, m_orange);
// 屋顶
mknode(scene, root, am, dev, "roof1", MESH_CUBE, (Vec3){-5, 2.15f, -5}, (Vec3){2.3f, 0.3f, 2.3f}, m_dstone);
mknode(scene, root, am, dev, "roof2", MESH_CUBE, (Vec3){-2, 2.65f, -8}, (Vec3){2.8f, 0.3f, 2.3f}, m_dstone);
mknode(scene, root, am, dev, "roof3", MESH_CUBE, (Vec3){1, 2.0f, -6}, (Vec3){2.1f, 0.3f, 2.1f}, m_brown);
mknode(scene, root, am, dev, "roof4", MESH_CUBE, (Vec3){4, 2.15f, -4}, (Vec3){2.3f, 0.3f, 2.3f}, m_brown);
// 树木
float trees[][2] = {{-12,3},{-10,5},{-8,8},{-6,10},{-3,12},{2,14},{5,10},{8,7},{10,4},{12,2},
{-14,-1},{-12,-6},{-10,-10},{8,-8},{12,-5},{15,8},{-15,12},{0,18},{-5,15},{10,15},
{-8,-14},{5,-12},{12,-10},{-14,6},{14,-2},{3,8},{-2,10},{7,12},{-11,8},{9,3}};
for (int i = 0; i < 30; i++) {
float x = trees[i][0], z = trees[i][1];
mknode(scene, root, am, dev, "trunk", MESH_CUBE, (Vec3){x, 0.5f, z}, (Vec3){0.2f, 1, 0.2f}, m_brown);
mknode(scene, root, am, dev, "leaves", MESH_SPHERE, (Vec3){x, 1.8f, z}, (Vec3){0.8f, 0.7f, 0.8f}, m_dkgrn);
}
// 岩石
float rocks[][3] = {{6,-2,0.6f},{-9,2,0.4f},{10,10,0.8f},{-6,-12,0.5f},{14,6,0.7f},{-13,-8,0.3f},{3,-10,0.5f},{-4,14,0.4f},{11,-8,0.6f}};
for (int i = 0; i < 9; i++)
mknode(scene, root, am, dev, "rock", MESH_SPHERE, (Vec3){rocks[i][0], rocks[i][2]*0.3f, rocks[i][1]}, (Vec3){rocks[i][2], rocks[i][2]*0.6f, rocks[i][2]}, m_stone);
// 水塘
mknode(scene, root, am, dev, "water", MESH_PLANE, (Vec3){-10, 0.02f, -4}, (Vec3){3, 1, 3}, m_water);
// 小路
for (float tz = -10; tz <= 2; tz += 1.5f)
mknode(scene, root, am, dev, "path", MESH_PLANE, (Vec3){0, 0.01f, tz}, (Vec3){0.6f, 1, 0.4f}, m_path);
// 山丘
mknode(scene, root, am, dev, "hill1", MESH_SPHERE, (Vec3){-18, -0.5f, 15}, (Vec3){8, 3, 8}, m_hill);
mknode(scene, root, am, dev, "hill2", MESH_SPHERE, (Vec3){20, -0.8f, 12}, (Vec3){10, 4, 10}, m_hill);
mknode(scene, root, am, dev, "hill3", MESH_SPHERE, (Vec3){0, -0.3f, 22}, (Vec3){12, 3, 8}, m_hill);
// 花
float flowers[][2] = {{-3,-2},{-1,-3},{2,-1},{-4,0},{3,1}};
for (int i = 0; i < 5; i++)
mknode(scene, root, am, dev, "flower", MESH_SPHERE, (Vec3){flowers[i][0], 0.15f, flowers[i][1]}, (Vec3){0.1f, 0.1f, 0.1f}, m_flower);
scene_update(scene);
return scene;
}
static void on_render(Engine* engine) {
Renderer* r = engine_get_renderer(engine);
GraphicsDevice* dev = engine_get_graphics_device(engine);
if (!r || !dev || !g_scene) return;
renderer_set_view(r, camera_get_view(&g_camera));
renderer_set_projection(r, camera_get_projection(&g_camera));
renderer_begin_frame(r);
scene_update(g_scene);
scene_render(g_scene, r);
renderer_end_frame(r);
}
static void update_camera(void) {
g_camera.position = (Vec3){
player_x - sinf(cam_yaw) * cam_dist,
cam_height + sinf(cam_pitch) * cam_dist * 0.5f,
player_z - cosf(cam_yaw) * cam_dist
};
g_camera.target = (Vec3){player_x, 0.5f, player_z};
g_camera.dirty = 1;
}
static void on_update(Engine* engine, double dt) {
(void)engine;
float fdt = (float)dt;
float spd = (GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 10.0f * fdt : 5.0f * fdt;
float fwd_x = sinf(cam_yaw), fwd_z = cosf(cam_yaw);
float rgt_x = cosf(cam_yaw), rgt_z = -sinf(cam_yaw);
if (GetAsyncKeyState('W') & 0x8000) { player_x += fwd_x * spd; player_z += fwd_z * spd; }
if (GetAsyncKeyState('S') & 0x8000) { player_x -= fwd_x * spd; player_z -= fwd_z * spd; }
if (GetAsyncKeyState('A') & 0x8000) { player_x -= rgt_x * spd; player_z -= rgt_z * spd; }
if (GetAsyncKeyState('D') & 0x8000) { player_x += rgt_x * spd; player_z += rgt_z * spd; }
if (GetAsyncKeyState('Q') & 0x8000) cam_yaw -= 2.0f * fdt;
if (GetAsyncKeyState('E') & 0x8000) cam_yaw += 2.0f * fdt;
if ((GetAsyncKeyState(VK_SPACE) & 0x8000) && g_ground) { player_vy = 8.0f; g_ground = 0; }
player_vy -= 20.0f * fdt;
player_y += player_vy * fdt;
if (player_y < 0) { player_y = 0; player_vy = 0; g_ground = 1; }
if (player_x < -19) player_x = -19; if (player_x > 19) player_x = 19;
if (player_z < -19) player_z = -19; if (player_z > 21) player_z = 21;
if (g_player) {
g_player->position.x = player_x; g_player->position.y = 0.4f + player_y; g_player->position.z = player_z;
quat_from_euler(0, cam_yaw, 0, &g_player->rotation);
g_player->dirty = 1;
}
update_camera();
}
static void on_key(Engine* engine, int key) {
if (key == VK_ESCAPE) engine_request_quit(engine);
}
static void on_mouse_move(Engine* engine, int x, int y) {
(void)engine;
if (g_mouse_down[0]) {
if (g_mouse_first) { g_last_mx = x; g_last_my = y; g_mouse_first = 0; }
else {
cam_yaw += (float)(x - g_last_mx) * 0.005f;
cam_pitch += (float)(y - g_last_my) * 0.003f;
if (cam_pitch > 1.2f) cam_pitch = 1.2f;
if (cam_pitch < -0.2f) cam_pitch = -0.2f;
g_last_mx = x; g_last_my = y;
update_camera();
}
} else { g_mouse_first = 1; }
}
static void on_mouse_button(Engine* engine, int button, int pressed) {
(void)engine;
if (button >= 0 && button < 3) g_mouse_down[button] = pressed;
}
static void on_resize(Engine* engine, int width, int height) {
Renderer* r = engine_get_renderer(engine);
if (r && width > 0 && height > 0) {
renderer_resize(r, width, height);
g_camera.aspect = (float)width / (float)height;
g_camera.dirty = 1;
}
}
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
EngineConfig config = engine_default_config();
config.app_name = "Lity Explorer";
config.vsync = 0;
config.window_width = 1280;
config.window_height = 720;
Engine* engine = engine_create();
if (!engine_init(engine, &config)) { LOG_FATAL("Init failed"); return -1; }
GraphicsDevice* dev = engine_get_graphics_device(engine);
Renderer* r = engine_get_renderer(engine);
g_assets = asset_manager_create();
g_shader_asset = asset_manager_load_shader(g_assets, dev->device, NULL, NULL);
if (!g_shader_asset) { LOG_FATAL("Shader failed"); return -1; }
g_scene = create_world(g_assets, dev->device);
float aspect = (float)config.window_width / config.window_height;
g_camera = camera_perspective(1.0472f, aspect, 0.1f, 200.0f);
g_camera.position = (Vec3){0, cam_height, -8 - cam_dist};
g_camera.target = (Vec3){0, 0.5f, -8};
g_camera.dirty = 1;
engine_set_update_callback(engine, on_update);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_set_mouse_move_callback(engine, on_mouse_move);
engine_set_mouse_button_callback(engine, on_mouse_button);
engine_set_resize_callback(engine, on_resize);
LOG_INFO("Lity Explorer started!");
engine_run(engine);
scene_destroy_renderers(g_scene);
scene_destroy(g_scene);
free(g_scene);
asset_manager_destroy(g_assets);
engine_shutdown(engine);
return 0;
}
+290
View File
@@ -0,0 +1,290 @@
// ============================================================================
// ly_interpreter.c - .ly 积木语言独立解释器
// 直接解析 .ly 格式,不依赖引擎 VisualScript 框架
// 用法: ly_interpreter.exe <script.ly>
// ============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// === 数据结构 ===
typedef union { double num; char str[256]; int bool_val; } Value;
typedef struct {
int id;
char name[64];
float x, y;
char data[256];
int color;
Value inputs[8];
int input_count;
Value outputs[4];
int output_count;
} Block;
typedef struct {
int from_block, from_slot;
int to_block, to_slot;
} Wire;
#define MAX_BLOCKS 256
#define MAX_WIRES 256
static Block g_blocks[MAX_BLOCKS];
static int g_block_count = 0;
static Wire g_wires[MAX_WIRES];
static int g_wire_count = 0;
// === 解析 ===
static void trim(char* s) {
char* p = s;
while (*p == ' ' || *p == '\t') p++;
if (p != s) memmove(s, p, strlen(p) + 1);
int len = strlen(s);
while (len > 0 && (s[len-1] == '\n' || s[len-1] == '\r' || s[len-1] == ' ')) s[--len] = 0;
}
static int parse_ly(const char* path) {
FILE* f = fopen(path, "r");
if (!f) { fprintf(stderr, "Cannot open %s\n", path); return 0; }
char line[1024];
int section = 0; // 0=none, 1=block, 2=wire
int cur_bid = -1;
while (fgets(line, sizeof(line), f)) {
trim(line);
if (line[0] == '#' || line[0] == 0) continue;
if (strcmp(line, "[B]") == 0 || strcmp(line, "[Block]") == 0) { section = 1; cur_bid = -1; continue; }
if (strcmp(line, "[W]") == 0 || strcmp(line, "[Wire]") == 0) { section = 2; continue; }
if (line[0] == '[') { section = 0; continue; }
if (section == 1) {
char* eq = strchr(line, '=');
if (!eq) continue;
*eq = 0; char* key = line; char* val = eq + 1;
if (strcmp(key, "id") == 0) {
cur_bid = g_block_count;
memset(&g_blocks[cur_bid], 0, sizeof(Block));
g_blocks[cur_bid].id = atoi(val);
g_block_count++;
} else if (cur_bid >= 0) {
if (strcmp(key, "name") == 0) strncpy(g_blocks[cur_bid].name, val, 63);
else if (strcmp(key, "p") == 0 || strcmp(key, "pos") == 0) sscanf(val, "%f,%f", &g_blocks[cur_bid].x, &g_blocks[cur_bid].y);
else if (strcmp(key, "d") == 0 || strcmp(key, "data") == 0) strncpy(g_blocks[cur_bid].data, val, 255);
else if (strcmp(key, "c") == 0 || strcmp(key, "color") == 0) g_blocks[cur_bid].color = atoi(val);
}
} else if (section == 2) {
Wire* w = &g_wires[g_wire_count];
char* eq = strchr(line, '=');
if (!eq) continue;
*eq = 0; char* key = line; char* val = eq + 1;
if (strcmp(key, "a") == 0 || strcmp(key, "from") == 0) {
w->from_block = atoi(val) / 10000;
w->from_slot = atoi(val) % 10000;
} else if (strcmp(key, "b") == 0 || strcmp(key, "to") == 0) {
w->to_block = atoi(val) / 10000;
w->to_slot = atoi(val) % 10000;
if (w->from_block > 0) g_wire_count++;
}
}
}
fclose(f);
printf("Parsed: %d blocks, %d wires\n", g_block_count, g_wire_count);
return 1;
}
// === 查找 ===
static Block* find_block(int id) {
for (int i = 0; i < g_block_count; i++)
if (g_blocks[i].id == id) return &g_blocks[i];
return NULL;
}
// 找到从某个 block 的 output_slot 出发,连接到的下一个 block
static Block* follow_wire(int from_id, int from_slot) {
for (int i = 0; i < g_wire_count; i++) {
if (g_wires[i].from_block == from_id && g_wires[i].from_slot == from_slot)
return find_block(g_wires[i].to_block);
}
return NULL;
}
// 获取连接到某个 block input_slot 的值
static Value get_input_value(int block_id, int slot) {
for (int i = 0; i < g_wire_count; i++) {
if (g_wires[i].to_block == block_id && g_wires[i].to_slot == slot) {
Block* src = find_block(g_wires[i].from_block);
if (src && src->output_count > g_wires[i].from_slot)
return src->outputs[g_wires[i].from_slot];
}
}
Value v; v.num = 0; return v;
}
// === 解释执行 ===
static int g_variables[64]; // 简单变量存储
static char g_var_names[64][64];
static int g_var_count = 0;
static int get_var(const char* name) {
for (int i = 0; i < g_var_count; i++)
if (strcmp(g_var_names[i], name) == 0) return g_variables[i];
return 0;
}
static void set_var(const char* name, int val) {
for (int i = 0; i < g_var_count; i++)
if (strcmp(g_var_names[i], name) == 0) { g_variables[i] = val; return; }
if (g_var_count < 64) { strncpy(g_var_names[g_var_count], name, 63); g_variables[g_var_count++] = val; }
}
static void execute(Block* block, int depth);
static void execute_block(Block* b, int depth) {
if (!b) return;
if (depth > 100) { printf("Stack overflow!\n"); return; }
const char* n = b->name;
if (strcmp(n, "开始") == 0) {
// Follow output wire
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "移动物体") == 0) {
Value dx = get_input_value(b->id, 1);
Value dy = get_input_value(b->id, 2);
Value dz = get_input_value(b->id, 3);
printf(" [move] dx=%.1f dy=%.1f dz=%.1f\n", dx.num, dy.num, dz.num);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "旋转物体") == 0) {
Value dx = get_input_value(b->id, 1);
Value dy = get_input_value(b->id, 2);
Value dz = get_input_value(b->id, 3);
printf(" [rotate] rx=%.1f ry=%.1f rz=%.1f\n", dx.num, dy.num, dz.num);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "当按键") == 0) {
// Event: just follow output (simulated - always fires)
printf(" [key event] key='%s'\n", b->data);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "当点击") == 0) {
printf(" [click event]\n");
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "打印") == 0 || strcmp(n, "print") == 0) {
Value msg = get_input_value(b->id, 1);
printf(" [print] %s\n", msg.str[0] ? msg.str : b->data);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "等待") == 0 || strcmp(n, "wait") == 0) {
Value secs = get_input_value(b->id, 1);
double s = secs.num > 0 ? secs.num : atof(b->data);
printf(" [wait] %.1fs\n", s);
// In real engine, this would pause. For now, just continue.
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "循环") == 0 || strcmp(n, "loop") == 0) {
Value count = get_input_value(b->id, 1);
int n = count.num > 0 ? (int)count.num : atoi(b->data);
if (n <= 0) n = 1;
printf(" [loop] %d times\n", n);
for (int i = 0; i < n; i++) {
Block* body = follow_wire(b->id, 0);
execute(body, depth + 1);
}
}
else if (strcmp(n, "如果") == 0 || strcmp(n, "if") == 0) {
Value cond = get_input_value(b->id, 1);
printf(" [if] condition=%d\n", cond.bool_val);
Block* next = follow_wire(b->id, cond.bool_val ? 0 : 1);
execute(next, depth + 1);
}
else if (strcmp(n, "数学运算") == 0 || strcmp(n, "+") == 0) {
Value a = get_input_value(b->id, 0);
Value b2 = get_input_value(b->id, 1);
b->outputs[0].num = a.num + b2.num;
printf(" [add] %.1f + %.1f = %.1f\n", a.num, b2.num, b->outputs[0].num);
}
else if (strcmp(n, "变量设置") == 0 || strcmp(n, "set_var") == 0) {
Value v = get_input_value(b->id, 1);
set_var(b->data, (int)v.num);
printf(" [set_var] %s = %d\n", b->data, (int)v.num);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
else if (strcmp(n, "变量读取") == 0 || strcmp(n, "get_var") == 0) {
int v = get_var(b->data);
b->outputs[0].num = v;
printf(" [get_var] %s = %d\n", b->data, v);
}
else {
printf(" [unknown] '%s'\n", n);
Block* next = follow_wire(b->id, 0);
execute(next, depth + 1);
}
}
static void execute(Block* block, int depth) {
if (!block) return;
execute_block(block, depth);
}
// === 主函数 ===
int main(int argc, char* argv[]) {
const char* script = "Demo/test.ly";
if (argc > 1) script = argv[1];
printf("=== lity3d .ly Interpreter ===\n");
printf("Script: %s\n\n", script);
if (!parse_ly(script)) return 1;
printf("\n--- Blocks ---\n");
for (int i = 0; i < g_block_count; i++)
printf(" [%d] %s data='%s' pos=(%.0f,%.0f)\n", g_blocks[i].id, g_blocks[i].name, g_blocks[i].data, g_blocks[i].x, g_blocks[i].y);
printf("\n--- Wires ---\n");
for (int i = 0; i < g_wire_count; i++)
printf(" [%d].out%d -> [%d].in%d\n", g_wires[i].from_block, g_wires[i].from_slot, g_wires[i].to_block, g_wires[i].to_slot);
printf("\n--- Execute ---\n");
// Find start block
Block* start = NULL;
for (int i = 0; i < g_block_count; i++)
if (strcmp(g_blocks[i].name, "开始") == 0) { start = &g_blocks[i]; break; }
if (!start) {
// Try to find any block with incoming wires as entry point
// Or just execute the first block
if (g_block_count > 0) start = &g_blocks[0];
}
if (start) {
execute(start, 0);
} else {
printf("No executable blocks found.\n");
}
printf("\n--- Done ---\n");
return 0;
}
+73
View File
@@ -0,0 +1,73 @@
// mesh_debug.c - 精确定位 mesh 系统 bug
#include "../Engine/Core/engine.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static Shader* g_shader = NULL;
static ID3D11Buffer* g_vb = NULL;
static ID3D11Buffer* g_ib = NULL;
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
static void on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
float sw=(float)g_dev->width, sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f), asp=sw/sh;
float mvp[16];
mvp[0]=fov/asp; mvp[5]=-fov; mvp[10]=1; mvp[11]=-1; mvp[14]=0.1f; mvp[15]=1;
Mat4 pm, pt; memcpy(&pm, mvp, sizeof(Mat4)); mat4_transpose(&pm, &pt);
graphics_device_clear(g_dev, 0.15f, 0.18f, 0.25f, 1.0f);
D3D11_VIEWPORT vp={}; vp.Width=sw; vp.Height=sh; vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={}; rd.FillMode=D3D11_FILL_SOLID; rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState* rs=NULL; g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={}; dsd.DepthEnable=FALSE;
ID3D11DepthStencilState* dss=NULL; g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(g_shader, ctx);
shader_set_matrix(g_shader, ctx, (float*)&pt);
// 用 init 时创建的 VB 画
UINT stride=sizeof(V), offset=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&g_vb,&stride,&offset);
ctx->lpVtbl->IASetIndexBuffer(ctx,g_ib,DXGI_FORMAT_R32_UINT,0);
ctx->lpVtbl->DrawIndexed(ctx,6,0,0);
if(rs) rs->lpVtbl->Release(rs);
if(dss) dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) { if(key==VK_ESCAPE) engine_request_quit(engine); }
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int) {
EngineConfig cfg=engine_default_config(); cfg.app_name="mesh debug";
Engine* e=engine_create(); if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
g_shader=shader_create(); shader_compile(g_shader,g_dev->device,NULL,NULL,0,0);
// WinMain 中创建 VB/IB(跟 mesh_create_cube 一样的时机)
V v[4]={{{-0.5f,-0.5f,0.5f},{0,0,1},{0,0},{1,0,0,1}},{{0.5f,-0.5f,0.5f},{0,0,1},{1,0},{0,1,0,1}},{{0.5f,0.5f,0.5f},{0,0,1},{1,1},{0,0,1,1}},{{-0.5f,0.5f,0.5f},{0,0,1},{0,1},{1,1,1,1}}};
uint32_t idx[6]={0,1,2,0,2,3};
D3D11_BUFFER_DESC bd={}; D3D11_SUBRESOURCE_DATA sd={};
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,&g_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,&g_ib);
engine_set_render_callback(e,on_render); engine_set_key_callback(e,on_key);
engine_run(e);
if(g_vb)g_vb->lpVtbl->Release(g_vb); if(g_ib)g_ib->lpVtbl->Release(g_ib);
shader_destroy(g_shader); graphics_device_destroy(g_dev); engine_shutdown(e);
return 0;
}
+206
View File
@@ -0,0 +1,206 @@
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static ID3D11Device* g_dev; static ID3D11DeviceContext* g_ctx;
static IDXGISwapChain* g_sc; static ID3D11RenderTargetView* g_rtv;
static ID3D11VertexShader* g_vs; static ID3D11PixelShader* g_ps;
static ID3D11InputLayout* g_il; static ID3D11Buffer* g_cb;
static float cam_dist = 5.0f, cam_yaw = 0.5f, cam_pitch = 0.3f;
static int drag = 0, last_mx, last_my;
static int key_w=0,key_s=0,key_a=0,key_d=0,key_q=0,key_e=0;
typedef struct { float p[3], n[3], u[2], c[4]; } V3D;
static V3D* obj_verts = NULL;
static uint32_t* obj_idx = NULL;
static int obj_vcount = 0, obj_icount = 0;
static ID3D11Buffer* obj_vb = NULL;
static ID3D11Buffer* obj_ib = NULL;
static int LoadOBJ(const char* path) {
FILE* f = fopen(path, "r");
if (!f) return -1;
int max_v = 16384, max_f = 32768;
float* vpos = (float*)malloc(max_v * 3 * sizeof(float));
int vc = 0;
int* fidx = (int*)malloc(max_f * 3 * sizeof(int));
int fc = 0;
char line[256];
while (fgets(line, sizeof(line), f)) {
if (line[0] == 'v' && line[1] == ' ') {
float x, y, z;
if (sscanf(line + 2, "%f %f %f", &x, &y, &z) == 3 && vc < max_v) {
vpos[vc*3]=x; vpos[vc*3+1]=y; vpos[vc*3+2]=z; vc++;
}
} else if (line[0] == 'f' && line[1] == ' ') {
int v0, v1, v2;
char* p = line + 2;
v0 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v1 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v2 = atoi(p) - 1;
if (v0 >= 0 && v1 >= 0 && v2 >= 0 && fc < max_f) {
fidx[fc*3]=v0; fidx[fc*3+1]=v1; fidx[fc*3+2]=v2; fc++;
}
}
}
fclose(f);
if (vc == 0 || fc == 0) { free(vpos); free(fidx); return -1; }
obj_vcount = fc * 3;
obj_icount = fc * 3;
obj_verts = (V3D*)calloc(obj_vcount, sizeof(V3D));
obj_idx = (uint32_t*)malloc(obj_icount * sizeof(uint32_t));
for (int i = 0; i < fc; i++) {
for (int j = 0; j < 3; j++) {
int vi = fidx[i*3+j];
int oi = i*3+j;
if (vi < vc) {
obj_verts[oi].p[0] = vpos[vi*3];
obj_verts[oi].p[1] = vpos[vi*3+1];
obj_verts[oi].p[2] = vpos[vi*3+2];
}
obj_verts[oi].c[0] = 0.6f; obj_verts[oi].c[1] = 0.7f; obj_verts[oi].c[2] = 0.8f; obj_verts[oi].c[3] = 1;
obj_idx[oi] = oi;
}
}
for (int i = 0; i < fc; i++) {
float* p0 = obj_verts[i*3].p;
float* p1 = obj_verts[i*3+1].p;
float* p2 = obj_verts[i*3+2].p;
float e1[3]={p1[0]-p0[0],p1[1]-p0[1],p1[2]-p0[2]};
float e2[3]={p2[0]-p0[0],p2[1]-p0[1],p2[2]-p0[2]};
float nx=e1[1]*e2[2]-e1[2]*e2[1], ny=e1[2]*e2[0]-e1[0]*e2[2], nz=e1[0]*e2[1]-e1[1]*e2[0];
float nl=sqrtf(nx*nx+ny*ny+nz*nz);
if(nl>0.0001f){nx/=nl;ny/=nl;nz/=nl;}
for(int j=0;j<3;j++){obj_verts[i*3+j].n[0]=nx;obj_verts[i*3+j].n[1]=ny;obj_verts[i*3+j].n[2]=nz;}
}
D3D11_BUFFER_DESC bd; D3D11_SUBRESOURCE_DATA sd;
memset(&bd,0,sizeof(bd));memset(&sd,0,sizeof(sd));
bd.ByteWidth=sizeof(V3D)*obj_vcount;bd.Usage=D3D11_USAGE_DEFAULT;bd.BindFlags=D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem=obj_verts;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&obj_vb);
bd.ByteWidth=sizeof(uint32_t)*obj_icount;bd.BindFlags=D3D11_BIND_INDEX_BUFFER;
sd.pSysMem=obj_idx;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&obj_ib);
free(vpos); free(fidx); free(obj_verts); free(obj_idx);
obj_verts = NULL; obj_idx = NULL;
return 0;
}
static void Init(HWND h) {
HRESULT hr;
DXGI_SWAP_CHAIN_DESC sd;memset(&sd,0,sizeof(sd));sd.BufferCount=2;sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;sd.BufferDesc.RefreshRate.Numerator=60;sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;sd.OutputWindow=h;sd.SampleDesc.Count=1;sd.Windowed=TRUE;sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL fl=D3D_FEATURE_LEVEL_11_0,gt;hr=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,&fl,1,D3D11_SDK_VERSION,&sd,&g_sc,&g_dev,&gt,&g_ctx);if(FAILED(hr))return;
ID3D11Texture2D*bb=NULL;hr=g_sc->lpVtbl->GetBuffer(g_sc,0,&IID_ID3D11Texture2D,(void**)&bb);if(FAILED(hr)||!bb)return;hr=g_dev->lpVtbl->CreateRenderTargetView(g_dev,(ID3D11Resource*)bb,NULL,&g_rtv);bb->lpVtbl->Release(bb);if(FAILED(hr))return;
const char*vs="cbuffer CB:register(b0){float4x4 WVP;};struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.n=i.n;o.c=i.c;return o;}";
const char*ps="struct I{float3 n:NORMAL;float4 c:COLOR;};float4 main(float4 p:SV_POSITION,I i):SV_Target{float l=normalize(float3(0.3,1,0.5));float d=max(dot(normalize(i.n),l),0.2);return float4(i.c.rgb*d,1);}";
ID3DBlob*b1,*b2,*e;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;
g_dev->lpVtbl->CreateVertexShader(g_dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);g_dev->lpVtbl->CreatePixelShader(g_dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
D3D11_INPUT_ELEMENT_DESC el[4];memset(el,0,sizeof(el));el[0].SemanticName="POSITION";el[0].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[0].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[1].SemanticName="NORMAL";el[1].AlignedByteOffset=12;el[1].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[1].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[2].SemanticName="TEXCOORD";el[2].AlignedByteOffset=24;el[2].Format=DXGI_FORMAT_R32G32_FLOAT;el[2].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[3].SemanticName="COLOR";el[3].AlignedByteOffset=32;el[3].Format=DXGI_FORMAT_R32G32B32A32_FLOAT;el[3].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;
g_dev->lpVtbl->CreateInputLayout(g_dev,el,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);b1->lpVtbl->Release(b1);b2->lpVtbl->Release(b2);
D3D11_BUFFER_DESC cbd;memset(&cbd,0,sizeof(cbd));cbd.ByteWidth=256;cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;g_dev->lpVtbl->CreateBuffer(g_dev,&cbd,NULL,&g_cb);
}
static void mat4_perspective(float* m, float fov, float asp, float zn, float zf) {
memset(m, 0, 64);
float f = 1.0f / tanf(fov / 2);
m[0] = f / asp; m[5] = -f;
m[10] = zf / (zf - zn); m[11] = -zn * zf / (zf - zn);
m[14] = 1; m[15] = 0;
}
static void mat4_lookat(float* m, float ex, float ey, float ez, float tx, float ty, float tz) {
float fx=tx-ex, fy=ty-ey, fz=tz-ez;
float fl=sqrtf(fx*fx+fy*fy+fz*fz);fx/=fl;fy/=fl;fz/=fl;
float rx=fy*0-fz*1,ry=fz*0-fx*0,rz=fx*1-fy*0;
float rl=sqrtf(rx*rx+ry*ry+rz*rz);rx/=rl;ry/=rl;rz/=rl;
float ux=ry*fz-rz*fy,uy=rz*fx-rx*fz,uz=rx*fy-ry*fx;
memset(m, 0, 64);
m[0]=rx*1;m[1]=ux*1;m[2]=fx*1;
m[4]=ry*1;m[5]=uy*1;m[6]=fy*1;
m[8]=rz*1;m[9]=uz*1;m[10]=fz*1;
m[12]=-(rx*ex+ry*ey+rz*ez);m[13]=-(ux*ex+uy*ey+uz*ez);m[14]=-(fx*ex+fy*ey+fz*ez);m[15]=1;
}
static void mat4_mul(float* o, float* a, float* b) {
float t[16];for(int i=0;i<4;i++)for(int j=0;j<4;j++){t[i*4+j]=0;for(int k=0;k<4;k++)t[i*4+j]+=a[i*4+k]*b[k*4+j];}
memcpy(o,t,64);
}
static void Frame(void) {
float dt = 0.016f, rspd = 2.0f * dt, mspeed = 3.0f * dt;
if (key_a) cam_yaw -= rspd; if (key_d) cam_yaw += rspd;
if (key_q) cam_pitch -= rspd; if (key_e) cam_pitch += rspd;
if (key_w) cam_dist -= mspeed; if (key_s) cam_dist += mspeed;
if (cam_dist < 1.0f) cam_dist = 1.0f; if (cam_dist > 20.0f) cam_dist = 20.0f;
if (cam_pitch > 1.5f) cam_pitch = 1.5f; if (cam_pitch < -1.5f) cam_pitch = -1.5f;
float ex = cam_dist * sinf(cam_yaw) * cosf(cam_pitch);
float ey = cam_dist * sinf(cam_pitch);
float ez = cam_dist * cosf(cam_yaw) * cosf(cam_pitch);
float view[16], proj[16], vp[16], tmp[16];
mat4_lookat(view, ex, ey, ez, 0, 0, 0);
mat4_perspective(proj, 0.8f, 1280.0f/720.0f, 0.1f, 100.0f);
mat4_mul(tmp, view, proj);
float vp_t[16]; for(int i=0;i<4;i++)for(int j=0;j<4;j++) vp_t[i*4+j]=tmp[j*4+i];
float cl[4]={.15f,.18f,.22f,1};
g_ctx->lpVtbl->OMSetRenderTargets(g_ctx,1,&g_rtv,NULL);g_ctx->lpVtbl->ClearRenderTargetView(g_ctx,g_rtv,cl);
D3D11_VIEWPORT vp2;memset(&vp2,0,sizeof(vp2));vp2.Width=1280;vp2.Height=720;vp2.MaxDepth=1;g_ctx->lpVtbl->RSSetViewports(g_ctx,1,&vp2);
D3D11_RASTERIZER_DESC rd;memset(&rd,0,sizeof(rd));rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;ID3D11RasterizerState*rs;g_dev->lpVtbl->CreateRasterizerState(g_dev,&rd,&rs);g_ctx->lpVtbl->RSSetState(g_ctx,rs);
D3D11_DEPTH_STENCIL_DESC ds;memset(&ds,0,sizeof(ds));ds.DepthEnable=FALSE;ID3D11DepthStencilState*dss;g_dev->lpVtbl->CreateDepthStencilState(g_dev,&ds,&dss);g_ctx->lpVtbl->OMSetDepthStencilState(g_ctx,dss,0);
g_ctx->lpVtbl->IASetPrimitiveTopology(g_ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);g_ctx->lpVtbl->VSSetShader(g_ctx,g_vs,NULL,0);g_ctx->lpVtbl->PSSetShader(g_ctx,g_ps,NULL,0);g_ctx->lpVtbl->IASetInputLayout(g_ctx,g_il);g_ctx->lpVtbl->VSSetConstantBuffers(g_ctx,0,1,&g_cb);g_ctx->lpVtbl->UpdateSubresource(g_ctx,(ID3D11Resource*)g_cb,0,NULL,vp_t,0,0);
if (obj_vb && obj_ib) {
UINT stride = sizeof(V3D), off = 0;
g_ctx->lpVtbl->IASetVertexBuffers(g_ctx, 0, 1, &obj_vb, &stride, &off);
g_ctx->lpVtbl->IASetIndexBuffer(g_ctx, obj_ib, DXGI_FORMAT_R32_UINT, 0);
g_ctx->lpVtbl->DrawIndexed(g_ctx, obj_icount, 0, 0);
}
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);
g_sc->lpVtbl->Present(g_sc, 1, 0);
}
static LRESULT CALLBACK WP(HWND h,UINT m,WPARAM w,LPARAM l){
switch(m){
case WM_LBUTTONDOWN:drag=1;last_mx=(short)LOWORD(l);last_my=(short)HIWORD(l);SetCapture(h);break;
case WM_LBUTTONUP:drag=0;ReleaseCapture();break;
case WM_MOUSEMOVE:if(drag){int mx=(short)LOWORD(l),my=(short)HIWORD(l);cam_yaw+=(mx-last_mx)*0.005f;cam_pitch+=(my-last_my)*0.005f;if(cam_pitch>1.5f)cam_pitch=1.5f;if(cam_pitch<-1.5f)cam_pitch=-1.5f;last_mx=mx;last_my=my;}break;
case WM_MOUSEWHEEL:cam_dist-=(short)HIWORD(w)/120.0f;if(cam_dist<1)cam_dist=1;if(cam_dist>20)cam_dist=20;break;
case WM_KEYDOWN:switch(w){case 'A':key_a=1;break;case 'D':key_d=1;break;case 'W':key_w=1;break;case 'S':key_s=1;break;case 'Q':key_q=1;break;case 'E':key_e=1;break;case VK_ESCAPE:PostQuitMessage(0);break;}break;
case WM_KEYUP:switch(w){case 'A':key_a=0;break;case 'D':key_d=0;break;case 'W':key_w=0;break;case 'S':key_s=0;break;case 'Q':key_q=0;break;case 'E':key_e=0;break;}break;
case WM_DESTROY:PostQuitMessage(0);return 0;
}return DefWindowProcW(h,m,w,l);
}
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR nC,int nS){
(void)nS;
WNDCLASSEXW wc;memset(&wc,0,sizeof(wc));wc.cbSize=sizeof(wc);wc.style=CS_CLASSDC;wc.lpfnWndProc=WP;wc.hInstance=hI;wc.lpszClassName=L"obj_viewer";
RegisterClassExW(&wc);
HWND hwnd=CreateWindowW(L"obj_viewer",L"OBJ Viewer - Mouse drag orbit, WASD move, Scroll zoom",WS_OVERLAPPEDWINDOW,100,100,1280,720,NULL,NULL,hI,NULL);
if(!hwnd)return -1;ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);Init(hwnd);
char path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
char* sl = strrchr(path, '\\');
if (sl) { *(sl+1) = '\0'; strcat(path, "Assets\\cube.obj"); }
else strcpy(path, "Assets\\cube.obj");
LoadOBJ(path);
MSG msg;
while(1){while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);if(msg.message==WM_QUIT)goto done;}Frame();}
done:
if(obj_vb)obj_vb->lpVtbl->Release(obj_vb);if(obj_ib)obj_ib->lpVtbl->Release(obj_ib);
if(g_cb)g_cb->lpVtbl->Release(g_cb);if(g_il)g_il->lpVtbl->Release(g_il);if(g_vs)g_vs->lpVtbl->Release(g_vs);if(g_ps)g_ps->lpVtbl->Release(g_ps);if(g_rtv)g_rtv->lpVtbl->Release(g_rtv);if(g_sc)g_sc->lpVtbl->Release(g_sc);if(g_ctx)g_ctx->lpVtbl->Release(g_ctx);if(g_dev)g_dev->lpVtbl->Release(g_dev);
return 0;
}
+384
View File
@@ -0,0 +1,384 @@
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Core/timer.h"
#include "../Engine/Core/asset_manager.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/renderer.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/Rendering/material.h"
#include "../Engine/Rendering/camera.h"
#include "../Engine/Rendering/camera_controller.h"
#include "../Engine/Terrain/terrain.h"
#include "../Engine/Terrain/terrain_manager.h"
#include "../Engine/Rendering/frustum.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/Scene/scene_render.h"
#include "../Engine/Scene/scene_serialize.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
static AssetManager* g_assets = NULL;
static Asset* g_shader_asset = NULL;
static Camera g_camera;
static CameraController g_controller;
static TerrainManager* g_terrain = NULL;
static Material* g_terrain_mat = NULL;
static Mesh* g_cube = NULL;
static Material* g_cube_mat = NULL;
static Mesh* g_water = NULL;
static Material* g_water_mat = NULL;
static Scene* g_test_scene = NULL;
static int g_mouse_down[3] = {0};
static int g_last_mouse_x = 0, g_last_mouse_y = 0;
static int g_mouse_first = 1;
static int g_walk_mode = 0;
// ============================================================================
// 简易植被/岩石散布
// ============================================================================
#define MAX_SCATTER 512
typedef struct {
Mesh* mesh;
Material* mat;
Mat4 world;
} ScatterInstance;
static ScatterInstance g_scatter[MAX_SCATTER];
static int g_scatter_count = 0;
static void add_scatter(Mesh* mesh, Material* mat, Vec3 pos, Vec3 scale) {
if (g_scatter_count >= MAX_SCATTER) return;
ScatterInstance* s = &g_scatter[g_scatter_count++];
s->mesh = mesh;
s->mat = mat;
mat4_identity(&s->world);
s->world.m[0][0] = scale.x;
s->world.m[1][1] = scale.y;
s->world.m[2][2] = scale.z;
s->world.m[3][0] = pos.x;
s->world.m[3][1] = pos.y;
s->world.m[3][2] = pos.z;
}
static void generate_scatter(ID3D11Device* dev, Shader* shader) {
Mesh* rock_mesh = mesh_create_cube(dev);
Material* rock_mat = material_create(shader, NULL);
rock_mat->color = (Vec4){0.55f, 0.50f, 0.45f, 1.0f};
Mesh* trunk_mesh = mesh_create_cube(dev);
Material* trunk_mat = material_create(shader, NULL);
trunk_mat->color = (Vec4){0.55f, 0.40f, 0.25f, 1.0f};
Mesh* leaves_mesh = mesh_create_sphere(dev, 1.0f, 8);
Material* leaves_mat = material_create(shader, NULL);
leaves_mat->color = (Vec4){0.22f, 0.65f, 0.28f, 1.0f};
for (int i = 0; i < MAX_SCATTER; i++) {
float x = ((float)rand() / (float)RAND_MAX) * 400.0f - 200.0f;
float z = ((float)rand() / (float)RAND_MAX) * 400.0f - 200.0f;
float h = terrain_manager_get_height(g_terrain, x, z);
// 跳过过低(水)或过高的山顶
if (h < -2.0f || h > 20.0f) continue;
int type = rand() % 3;
if (type == 0) {
// 岩石
float s = 1.0f + ((float)rand() / (float)RAND_MAX) * 2.0f;
Vec3 pos = {x, h + s * 0.3f, z};
Vec3 sc = {s, s * 0.6f, s};
add_scatter(rock_mesh, rock_mat, pos, sc);
} else {
// 树
float trunk_h = 1.5f + ((float)rand() / (float)RAND_MAX) * 1.5f;
Vec3 trunk_pos = {x, h + trunk_h * 0.5f, z};
Vec3 trunk_sc = {0.25f, trunk_h, 0.25f};
add_scatter(trunk_mesh, trunk_mat, trunk_pos, trunk_sc);
float leaves_s = 1.2f + ((float)rand() / (float)RAND_MAX) * 1.2f;
Vec3 leaves_pos = {x, h + trunk_h + leaves_s * 0.4f, z};
Vec3 leaves_sc = {leaves_s, leaves_s, leaves_s};
add_scatter(leaves_mesh, leaves_mat, leaves_pos, leaves_sc);
}
}
LOG_INFO("Generated %d scatter instances", g_scatter_count);
}
static void render_scatter(Renderer* r, const Vec4 planes[6]) {
const float cull_distance = 140.0f;
const float cull_dist2 = cull_distance * cull_distance;
int rendered = 0;
for (int i = 0; i < g_scatter_count; i++) {
ScatterInstance* s = &g_scatter[i];
Vec3 pos = {s->world.m[3][0], s->world.m[3][1], s->world.m[3][2]};
Vec3 d;
vec3_sub(&pos, &g_camera.position, &d);
if (vec3_dot(&d, &d) > cull_dist2) continue;
// 视锥剔除:用包围球近似
float radius = fmaxf(fmaxf(s->world.m[0][0], s->world.m[1][1]), s->world.m[2][2]) * 0.866f;
if (!frustum_intersects_sphere(planes, pos, radius)) continue;
renderer_draw_mesh_with_material(r, s->mesh, s->mat, &s->world);
rendered++;
}
LOG_INFO("Scatter render: visible=%d/%d", rendered, g_scatter_count);
}
// ============================================================================
// 地形高度回调
// ============================================================================
static float get_terrain_height(float x, float z, void* user) {
(void)user;
if (g_terrain) return terrain_manager_get_height(g_terrain, x, z);
return 0.0f;
}
// ============================================================================
// 引擎回调
// ============================================================================
static void on_render(Engine* engine) {
Renderer* r = engine_get_renderer(engine);
GraphicsDevice* dev = engine_get_graphics_device(engine);
if (!r || !dev) return;
renderer_set_view(r, camera_get_view(&g_camera));
renderer_set_projection(r, camera_get_projection(&g_camera));
renderer_begin_frame(r);
Mat4 vp;
renderer_get_view_projection(r, &vp);
Vec4 planes[6];
frustum_from_matrix(&vp, planes);
if (g_water) {
Mat4 water_world;
mat4_identity(&water_world);
water_world.m[3][1] = -5.0f;
renderer_draw_mesh_with_material(r, g_water, g_water_mat, &water_world);
}
if (g_terrain) {
terrain_manager_update(g_terrain, g_camera.position);
terrain_manager_render(g_terrain, r);
}
render_scatter(r, planes);
if (g_test_scene) {
scene_update(g_test_scene);
scene_render(g_test_scene, r);
}
renderer_end_frame(r);
if (engine->frame_count == 60) {
graphics_device_save_screenshot(dev, "open_world_screenshot1.bmp");
}
if (engine->frame_count == 180) {
graphics_device_save_screenshot(dev, "open_world_screenshot2.bmp");
}
}
static void on_update(Engine* engine, double dt) {
(void)engine;
float fdt = (float)dt;
Vec3 dir = {0.0f, 0.0f, 0.0f};
if (GetAsyncKeyState('W') & 0x8000) dir.z += 1.0f;
if (GetAsyncKeyState('S') & 0x8000) dir.z -= 1.0f;
if (GetAsyncKeyState('A') & 0x8000) dir.x -= 1.0f;
if (GetAsyncKeyState('D') & 0x8000) dir.x += 1.0f;
if (!g_walk_mode) {
if (GetAsyncKeyState(VK_SPACE) & 0x8000) dir.y += 1.0f;
if (GetAsyncKeyState(VK_SHIFT) & 0x8000) dir.y -= 1.0f;
}
if (dir.x != 0.0f || dir.y != 0.0f || dir.z != 0.0f) {
camera_controller_move(&g_controller, dir, fdt);
}
camera_controller_update(&g_controller, fdt);
}
static void on_key(Engine* engine, int key) {
(void)engine;
if (key == VK_ESCAPE) engine_request_quit(engine);
// 切换步行/飞行模式
if (key == 'F' || key == 'f') {
g_walk_mode = !g_walk_mode;
camera_controller_set_mode(&g_controller, g_walk_mode ? CAMERA_MODE_WALK : CAMERA_MODE_FLY);
g_controller.move_speed = g_walk_mode ? 8.0f : 40.0f;
LOG_INFO("Camera mode: %s (speed %.1f)", g_walk_mode ? "walk" : "fly", g_controller.move_speed);
}
// 模式切换后重新定位相机高度
if (g_walk_mode) {
float ground = get_terrain_height(g_camera.position.x, g_camera.position.z, NULL);
g_camera.position.y = ground + g_controller.height_offset;
g_camera.target.y = g_camera.position.y + sinf(g_controller.pitch);
g_camera.dirty = 1;
}
}
static void on_mouse_move(Engine* engine, int x, int y) {
(void)engine;
if (g_mouse_down[0]) { // 左键拖拽旋转
if (g_mouse_first) {
g_last_mouse_x = x;
g_last_mouse_y = y;
g_mouse_first = 0;
} else {
float dx = (float)(x - g_last_mouse_x);
float dy = (float)(y - g_last_mouse_y);
camera_controller_look(&g_controller, dx, dy);
g_last_mouse_x = x;
g_last_mouse_y = y;
}
} else {
g_mouse_first = 1;
}
}
static void on_mouse_button(Engine* engine, int button, int pressed) {
(void)engine;
if (button >= 0 && button < 3) g_mouse_down[button] = pressed;
}
static void on_resize(Engine* engine, int width, int height) {
Renderer* r = engine_get_renderer(engine);
if (r && width > 0 && height > 0) {
renderer_resize(r, width, height);
g_camera.aspect = (float)width / (float)height;
g_camera.dirty = 1;
}
}
// ============================================================================
// 入口
// ============================================================================
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
srand((unsigned int)time(NULL));
EngineConfig config = engine_default_config();
config.app_name = "Lity3D - Open World Demo";
config.vsync = 1;
config.window_width = 1280;
config.window_height = 720;
Engine* engine = engine_create();
if (!engine_init(engine, &config)) {
LOG_FATAL("Engine init failed");
return -1;
}
GraphicsDevice* dev = engine_get_graphics_device(engine);
Renderer* r = engine_get_renderer(engine);
g_assets = asset_manager_create();
g_shader_asset = asset_manager_load_shader(g_assets, dev->device, NULL, NULL);
if (!g_shader_asset) {
LOG_FATAL("Shader load failed");
return -1;
}
LOG_INFO("Shader loaded via AssetManager");
Shader* default_shader = (Shader*)asset_data(g_shader_asset);
// 地形材质
g_terrain_mat = material_create(default_shader, NULL);
g_terrain_mat->color = (Vec4){1.0f, 1.0f, 1.0f, 1.0f};
// 参照立方体
g_cube = mesh_create_cube(dev->device);
g_cube_mat = material_create(default_shader, NULL);
g_cube_mat->color = (Vec4){1.0f, 0.2f, 0.2f, 1.0f};
// 水面(不透明,作为低洼处背景)
g_water = mesh_create_plane(dev->device, 2000.0f);
g_water_mat = material_create(default_shader, NULL);
g_water_mat->color = (Vec4){0.18f, 0.45f, 0.78f, 1.0f};
// 测试场景:仅保留一个红色立方体作为原点标记
g_test_scene = (Scene*)calloc(1, sizeof(Scene));
scene_init(g_test_scene);
SceneNode* cube_node = scene_create_node(g_test_scene, "cube");
scene_set_parent(cube_node, g_test_scene->root);
cube_node->position = (Vec3){0.0f, 0.0f, 0.0f};
MeshRenderer* cube_mr = mesh_renderer_create(MESH_CUBE, g_cube, g_cube_mat);
scene_node_set_mesh_renderer(cube_node, cube_mr);
// 地形配置
TerrainConfig tcfg = terrain_default_config();
tcfg.vertices_per_chunk = 48;
tcfg.chunk_size = 64.0f;
tcfg.height_scale = 30.0f;
tcfg.uv_scale = 0.05f;
g_terrain = terrain_manager_create(dev->device, &tcfg, g_terrain_mat);
g_terrain->load_radius = 3;
// 生成植被/岩石
generate_scatter(dev->device, default_shader);
// 天空色与柔和环境光
renderer_set_clear_color(r, 0.53f, 0.81f, 0.98f, 1.0f);
renderer_set_light(r, (Vec3){0.5f, -1.0f, 0.3f},
(Vec4){0.55f, 0.53f, 0.48f, 1.0f},
(Vec4){0.70f, 0.74f, 0.82f, 1.0f});
// 相机初始化:站在地形上,朝 +Z
float aspect = (float)config.window_width / (float)config.window_height;
g_camera = camera_perspective(1.0472f, aspect, 0.1f, 500.0f);
float start_h = terrain_manager_get_height(g_terrain, 0.0f, 0.0f);
g_camera.position = (Vec3){0.0f, start_h + 1.75f, 0.0f};
g_camera.target = (Vec3){0.0f, start_h + 1.75f, 5.0f};
g_camera.far_z = 500.0f;
g_camera.dirty = 1;
g_controller = camera_controller_create(&g_camera);
g_controller.move_speed = 8.0f;
g_controller.mouse_sensitivity = 0.004f;
camera_controller_set_ground_callback(&g_controller, get_terrain_height, NULL);
g_walk_mode = 1;
camera_controller_set_mode(&g_controller, CAMERA_MODE_WALK);
LOG_INFO("Starting in walk mode at height %.2f", start_h);
renderer_set_view(r, camera_get_view(&g_camera));
renderer_set_projection(r, camera_get_projection(&g_camera));
engine_set_update_callback(engine, on_update);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_set_mouse_move_callback(engine, on_mouse_move);
engine_set_mouse_button_callback(engine, on_mouse_button);
engine_set_resize_callback(engine, on_resize);
LOG_INFO("Running open world demo...");
engine_run(engine);
terrain_manager_destroy(g_terrain);
if (g_test_scene) {
scene_destroy_renderers(g_test_scene);
scene_destroy(g_test_scene);
free(g_test_scene);
}
if (g_cube) mesh_destroy(g_cube);
asset_manager_destroy(g_assets);
engine_shutdown(engine);
return 0;
}
+128
View File
@@ -0,0 +1,128 @@
// raw3d_test.c - 用 draw_quad 画 3D 方块,验证场景图变换
#include "../Engine/Core/engine.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static Shader* g_shader = NULL;
static Scene g_scene;
static float g_time = 0;
typedef struct { float p[3],n[3],uv[2],c[4]; } V;
static void draw_quad_3d(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={}; D3D11_SUBRESOURCE_DATA sd={};
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),off=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&stride,&off);
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 on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
scene_update(&g_scene);
g_time += (float)engine->delta_time;
float sw=(float)g_dev->width, sh=(float)g_dev->height;
float fov=1.0f/tanf(0.4f), asp=sw/sh;
// VP矩阵: 摄像机在(8,5,10)看原点
float ex=8,ey=5,ez=10;
float fx=-ex,fy=-ey,fz=-ez, fl=sqrtf(fx*fx+fy*fy+fz*fz); fx/=fl;fy/=fl;fz/=fl;
float rx=fy*0-fz*1,ry=fz*0-fx*0,rz=fx*1-fy*0;
float rl=sqrtf(rx*rx+ry*ry+rz*rz);rx/=rl;ry/=rl;rz/=rl;
float ux=ry*fz-rz*fy,uy=rz*fx-rx*fz,uz=rx*fy-ry*fx;
float mvp[16];
mvp[0]=rx*fov/asp;mvp[1]=ry*fov/asp;mvp[2]=rz*fov/asp;mvp[3]=0;
mvp[4]=ux*fov;mvp[5]=uy*fov;mvp[6]=uz*fov;mvp[7]=0;
mvp[8]=fx;mvp[9]=fy;mvp[10]=fz;mvp[11]=0;
mvp[12]=-(rx*ex+ry*ey+rz*ez)*fov/asp;
mvp[13]=-(ux*ex+uy*ey+uz*ez)*fov;
mvp[14]=-(fx*ex+fy*ey+fz*ez);mvp[15]=1;
graphics_device_clear(g_dev, 0.15f, 0.18f, 0.25f, 1.0f);
D3D11_VIEWPORT vp={};vp.Width=sw;vp.Height=sh;vp.MaxDepth=1;
ctx->lpVtbl->RSSetViewports(ctx,1,&vp);
D3D11_RASTERIZER_DESC rd={};rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;
ID3D11RasterizerState* rs=NULL;
g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device,&rd,&rs);
ctx->lpVtbl->RSSetState(ctx,rs);
D3D11_DEPTH_STENCIL_DESC dsd={};dsd.DepthEnable=FALSE;
ID3D11DepthStencilState* dss=NULL;
g_dev->device->lpVtbl->CreateDepthStencilState(g_dev->device,&dsd,&dss);
ctx->lpVtbl->OMSetDepthStencilState(ctx,dss,0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(g_shader, ctx);
// 地面
shader_set_matrix(g_shader, ctx, mvp);
draw_quad_3d(ctx, -5, 0, 10, 10, 0.3f, 0.35f, 0.4f, 1);
// 场景节点
for (int i=0;i<g_scene.node_count;i++) {
SceneNode*n=&g_scene.nodes[i];
if (!n->visible||n->id==0||n->user_type!=1) continue;
// 用世界矩阵变换4个角点
float corners[4][3] = {{-0.5f,-0.5f,0},{0.5f,-0.5f,0},{0.5f,0.5f,0},{-0.5f,0.5f,0}};
V v[4]; uint32_t idx[6]={0,1,2,0,2,3};
float colors[][3]={{1,0.2f,0.2f},{0.2f,1,0.2f},{0.2f,0.2f,1},{1,1,0.2f},{0.5f,0.2f,0.8f},{0.2f,0.8f,0.8f},{0.8f,0.5f,0.2f},{0.5f,0.5f,0.5f}};
int ci=(i-1)%8;
for(int c=0;c<4;c++){
float px=0,py=0,pz=0;
for(int r=0;r<4;r++) {float wc=n->world_matrix.m[0][r]*corners[c][0]+n->world_matrix.m[1][r]*corners[c][1]+n->world_matrix.m[2][r]*corners[c][2]+n->world_matrix.m[3][r]; px+=wc*mvp[r*4+0]; py+=wc*mvp[r*4+1]; pz+=wc*mvp[r*4+2];}
// 简化: 直接用世界坐标+投影
float wx=n->world_matrix.m[3][0]+corners[c][0];
float wy=n->world_matrix.m[3][1]+corners[c][1];
float wz=n->world_matrix.m[3][2]+corners[c][2];
v[c].p[0]=wx; v[c].p[1]=wy; v[c].p[2]=wz;
v[c].n[0]=0; v[c].n[1]=0; v[c].n[2]=1;
v[c].c[0]=colors[ci][0]; v[c].c[1]=colors[ci][1]; v[c].c[2]=colors[ci][2]; v[c].c[3]=1;
}
ID3D11Buffer* vb=NULL,*ib2=NULL;
D3D11_BUFFER_DESC bd2={}; D3D11_SUBRESOURCE_DATA sd2={};
bd2.ByteWidth=sizeof(V)*4; bd2.Usage=D3D11_USAGE_DEFAULT; bd2.BindFlags=D3D11_BIND_VERTEX_BUFFER;
sd2.pSysMem=v; g_dev->device->lpVtbl->CreateBuffer(g_dev->device,&bd2,&sd2,&vb);
bd2.ByteWidth=sizeof(uint32_t)*6; bd2.BindFlags=D3D11_BIND_INDEX_BUFFER;
sd2.pSysMem=idx; g_dev->device->lpVtbl->CreateBuffer(g_dev->device,&bd2,&sd2,&ib2);
UINT s2=sizeof(V),o2=0;
ctx->lpVtbl->IASetVertexBuffers(ctx,0,1,&vb,&s2,&o2);
ctx->lpVtbl->IASetIndexBuffer(ctx,ib2,DXGI_FORMAT_R32_UINT,0);
ctx->lpVtbl->DrawIndexed(ctx,6,0,0);
ib2->lpVtbl->Release(ib2); vb->lpVtbl->Release(vb);
}
if(rs) rs->lpVtbl->Release(rs);
if(dss) dss->lpVtbl->Release(dss);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) { if (key==VK_ESCAPE) engine_request_quit(engine); }
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR,int) {
EngineConfig cfg=engine_default_config(); cfg.app_name="lity3d Raw3D";
Engine* e=engine_create(); if(!engine_init(e,&cfg))return -1;
g_dev=graphics_device_create();
if(!graphics_device_init(g_dev,window_get_handle(e->window),cfg.window_width,cfg.window_height,1))return -1;
g_shader=shader_create(); shader_compile(g_shader,g_dev->device,NULL,NULL,0,0);
scene_init(&g_scene);
SceneNode* c1=scene_create_node(&g_scene,"Cube1"); scene_set_position(c1,0,0.5f,0); c1->user_type=1;
SceneNode* c2=scene_create_node(&g_scene,"Cube2"); scene_set_position(c2,-3,0.5f,2); c2->user_type=1;
SceneNode* c3=scene_create_node(&g_scene,"Cube3"); scene_set_position(c3,3,0.5f,-2); scene_set_scale(c3,0.5f,0.5f,0.5f); c3->user_type=1;
SceneNode* ch=scene_create_node(&g_scene,"Child"); scene_set_position(ch,2,0,0); ch->user_type=1; scene_set_parent(ch,c1);
engine_set_render_callback(e,on_render); engine_set_key_callback(e,on_key);
engine_run(e);
shader_destroy(g_shader); graphics_device_destroy(g_dev); engine_shutdown(e);
return 0;
}
+121
View File
@@ -0,0 +1,121 @@
// scene_test.c - 场景图 + 网格渲染
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Rendering/shader.h"
#include "../Engine/Rendering/mesh.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static Shader* g_shader = NULL;
static Mesh* g_cube = NULL;
static Mesh* g_plane = NULL;
static Scene g_scene;
static SceneNode* g_camera = NULL;
static float g_time = 0;
static void on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
scene_update(&g_scene);
g_time += (float)engine->delta_time;
float sw = (float)g_dev->width, sh = (float)g_dev->height;
float fov = 1.0f / tanf(0.5f * 0.785f), aspect = sw / sh;
Vec3 cam_pos;
scene_get_world_position(g_camera, &cam_pos);
// VP 矩阵
float proj[16] = {0};
proj[0] = fov / aspect; proj[5] = -fov;
proj[10] = 1.0f; proj[11] = -1.0f; proj[14] = 0.1f; proj[15] = 1.0f;
Vec3 eye = cam_pos, target = {0,0,0}, up = {0,1,0};
Vec3 f, r, u;
vec3_sub(&target, &eye, &f); vec3_normalize(&f, &f);
vec3_cross(&up, &f, &r); vec3_normalize(&r, &r);
vec3_cross(&f, &r, &u);
float view[16] = {0};
view[0]=r.x; view[1]=u.x; view[2]=-f.x;
view[4]=r.y; view[5]=u.y; view[6]=-f.y;
view[8]=r.z; view[9]=u.z; view[10]=-f.z;
view[12]=-vec3_dot(&r,&eye); view[13]=-vec3_dot(&u,&eye); view[14]=vec3_dot(&f,&eye); view[15]=1.0f;
float vp_raw[16] = {0};
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
vp_raw[i*4+j] = view[i*4+0]*proj[0*4+j]+view[i*4+1]*proj[1*4+j]+view[i*4+2]*proj[2*4+j]+view[i*4+3]*proj[3*4+j];
Mat4 vp_m, vp_t;
memcpy(&vp_m, vp_raw, sizeof(Mat4));
mat4_transpose(&vp_m, &vp_t);
graphics_device_clear(g_dev, 0.15f, 0.18f, 0.25f, 1.0f);
D3D11_VIEWPORT vport = {0}; vport.Width = sw; vport.Height = sh; vport.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vport);
D3D11_RASTERIZER_DESC rdesc = {0}; rdesc.FillMode = D3D11_FILL_SOLID; rdesc.CullMode = D3D11_CULL_NONE;
ID3D11RasterizerState* rstate = NULL;
g_dev->device->lpVtbl->CreateRasterizerState(g_dev->device, &rdesc, &rstate);
ctx->lpVtbl->RSSetState(ctx, rstate);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(g_shader, ctx);
shader_set_matrix(g_shader, ctx, (float*)&vp_t);
if (g_plane) mesh_draw(g_plane, ctx);
for (int i = 0; i < g_scene.node_count; i++) {
SceneNode* node = &g_scene.nodes[i];
if (!node->visible || node->id == 0 || node->user_type != 1 || !g_cube) continue;
float wvp_raw[16] = {0};
for (int r2 = 0; r2 < 4; r2++)
for (int c = 0; c < 4; c++)
wvp_raw[r2*4+c] = view[r2*4+0]*node->world_matrix.m[0][c]+view[r2*4+1]*node->world_matrix.m[1][c]+view[r2*4+2]*node->world_matrix.m[2][c]+view[r2*4+3]*node->world_matrix.m[3][c];
Mat4 wvp_m, wvp_t;
memcpy(&wvp_m, wvp_raw, sizeof(Mat4));
mat4_transpose(&wvp_m, &wvp_t);
shader_set_matrix(g_shader, ctx, (float*)&wvp_t);
mesh_draw(g_cube, ctx);
}
if (rstate) rstate->lpVtbl->Release(rstate);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) { if (key == VK_ESCAPE) engine_request_quit(engine); }
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR cmd, int nS) {
(void)hI;(void)hP;(void)cmd;(void)nS;
EngineConfig cfg = engine_default_config();
cfg.app_name = "lity3d Scene Test";
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;
g_shader = shader_create();
shader_compile(g_shader, g_dev->device, NULL, NULL, 0, 0);
g_cube = mesh_create_cube(g_dev->device);
g_plane = mesh_create_plane(g_dev->device, 10.0f);
scene_init(&g_scene);
g_camera = scene_create_node(&g_scene, "Camera");
scene_set_position(g_camera, 5, 4, 8);
SceneNode* c1 = scene_create_node(&g_scene, "Cube1");
scene_set_position(c1, 0, 0.5f, 0); c1->user_type = 1;
SceneNode* c2 = scene_create_node(&g_scene, "Cube2");
scene_set_position(c2, -3, 0.5f, 2); c2->user_type = 1;
SceneNode* c3 = scene_create_node(&g_scene, "Cube3");
scene_set_position(c3, 3, 0.5f, -2); c3->user_type = 1; scene_set_scale(c3, 0.5f, 0.5f, 0.5f);
SceneNode* child = scene_create_node(&g_scene, "Child");
scene_set_position(child, 2, 0, 0); child->user_type = 1; scene_set_parent(child, c1);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_run(engine);
mesh_destroy(g_cube); mesh_destroy(g_plane); shader_destroy(g_shader);
graphics_device_destroy(g_dev); engine_shutdown(engine);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
// scene_test2.c - 最小化场景测试
#include "../Engine/Core/engine.h"
#include "../Engine/Core/logger.h"
#include "../Engine/Window/window.h"
#include "../Engine/Rendering/graphics_device.h"
#include "../Engine/Scene/scene_graph.h"
#include "../Engine/include/engine_api.h"
#include "../Engine/ASM/math_c_fallback.h"
static GraphicsDevice* g_dev = NULL;
static void on_render(Engine* engine) {
if (!g_dev) return;
ID3D11DeviceContext* ctx = g_dev->context;
float clear[4] = {0.2f, 0.3f, 0.5f, 1.0f};
g_dev->context->lpVtbl->ClearRenderTargetView(g_dev->context, g_dev->render_target, clear);
D3D11_VIEWPORT vp = {0};
vp.Width = (float)g_dev->width; vp.Height = (float)g_dev->height;
vp.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
graphics_device_present(g_dev);
}
static void on_key(Engine* engine, int key) {
if (key == VK_ESCAPE) engine_request_quit(engine);
}
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR cmd, int nS) {
(void)hI;(void)hP;(void)cmd;(void)nS;
LOG_INFO("=== lity3d Scene Test ===");
EngineConfig cfg = engine_default_config();
cfg.app_name = "lity3d Scene Test";
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;
LOG_INFO("D3D11 ready");
Scene scene; scene_init(&scene);
SceneNode* n1 = scene_create_node(&scene, "A");
scene_set_position(n1, 1, 0, 0);
SceneNode* n2 = scene_create_node(&scene, "B");
scene_set_parent(n2, n1);
scene_set_position(n2, 0, 2, 0);
scene_update(&scene);
LOG_INFO("A world pos: (%.1f,%.1f,%.1f)", scene.nodes[1].world_matrix.m[3][0], scene.nodes[1].world_matrix.m[3][1], scene.nodes[1].world_matrix.m[3][2]);
LOG_INFO("B world pos: (%.1f,%.1f,%.1f)", scene.nodes[2].world_matrix.m[3][0], scene.nodes[2].world_matrix.m[3][1], scene.nodes[2].world_matrix.m[3][2]);
engine_set_render_callback(engine, on_render);
engine_set_key_callback(engine, on_key);
engine_run(engine);
graphics_device_destroy(g_dev);
engine_shutdown(engine);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
# lity3d 积木脚本 - 测试解释器
# 开始 -> 移动物体 -> 打印 -> 循环3次 -> 移动物体
[B]
id=1
name=开始
p=100,100
c=0
[B]
id=2
name=移动物体
p=350,100
c=1
[B]
id=3
name=打印
p=600,100
d=Hello World
c=3
[B]
id=4
name=循环
p=100,250
d=3
c=2
[B]
id=5
name=移动物体
p=350,250
c=1
# 连线: 开始.输出0 -> 移动物体.执行0
[W]
a=10000
b=20000
# 移动物体.执行0 -> 打印.执行0
[W]
a=20000
b=30000
# 打印.执行0 -> 循环.执行0
[W]
a=30000
b=40000
# 循环.执行0 -> 移动物体2.执行0
[W]
a=40000
b=50000
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "lity3d-editor"
version = "1.0.0"
edition = "2021"
[dependencies]
eframe = { version = "0.31", features = ["default"] }
rfd = "0.15"
+348
View File
@@ -0,0 +1,348 @@
// lity_editor.c - Lity3D Editor (Win32 Unicode + JSON i18n)
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <shellapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define IDC_TREE 1001
#define IDC_EDIT 1002
#define IDC_CONSOLE 1003
#define IDM_NEW 2001
#define IDM_OPEN 2002
#define IDM_SAVE 2003
#define IDM_RUN 2004
#define IDM_BUILD 2005
#define IDM_PACKAGE 2006
#define IDM_ABOUT 2007
#define IDM_LANG_BASE 3000
static HWND g_hwnd, g_tree, g_edit, g_console;
static WCHAR g_current_file[MAX_PATH] = L"";
static WCHAR g_engine_dir[MAX_PATH] = L"";
static int g_lang_count = 0;
static WCHAR g_lang_codes[128][32];
static WCHAR g_lang_names[128][128];
static WCHAR g_cur_lang[32] = L"en";
// Translation keys
static WCHAR g_t[256][256]; // up to 256 keys
static int g_t_count = 0;
static WCHAR g_t_keys[256][64];
static WCHAR* T(const char* key) {
for (int i = 0; i < g_t_count; i++) {
char kbuf[64];
WideCharToMultiByte(CP_UTF8, 0, g_t_keys[i], -1, kbuf, sizeof(kbuf), NULL, NULL);
if (strcmp(kbuf, key) == 0) return g_t[i];
}
// fallback to English defaults
static const WCHAR* defs[][2] = {
{L"app_title",L"Lity3D Editor"},{L"file",L"File"},{L"build",L"Build"},
{L"scene",L"Scene"},{L"help",L"Help"},{L"lang",L"Language"},
{L"new",L"New"},{L"open",L"Open"},{L"save",L"Save"},{L"exit",L"Exit"},
{L"run",L"Run"},{L"compile",L"Build Engine"},{L"package",L"Package"},
{L"explorer",L"Explorer"},{L"output",L"Output"},{L"ready",L"Ready"},
{L"about",L"About Lity3D"},
};
for (int i = 0; i < 17; i++) {
char kbuf2[64];
WideCharToMultiByte(CP_UTF8, 0, defs[i][0], -1, kbuf2, sizeof(kbuf2), NULL, NULL);
if (strcmp(kbuf2, key) == 0) return defs[i][1];
}
static WCHAR fb[128];
MultiByteToWideChar(CP_UTF8, 0, key, -1, fb, 128);
return fb;
}
static void parse_json_value(const char* json, const char* key, WCHAR* out, int max) {
char pattern[128];
sprintf(pattern, "\"%s\"", key);
const char* p = strstr(json, pattern);
if (!p) { out[0] = 0; return; }
p = strchr(p + strlen(pattern), ':');
if (!p) { out[0] = 0; return; }
p++; while (*p == ' ') p++;
if (*p == '"') {
p++; const char* end = strchr(p, '"'); if (!end) end = p + strlen(p);
int len = (int)(end - p);
char tmp[512] = {0}; if (len > 511) len = 511;
memcpy(tmp, p, len);
MultiByteToWideChar(CP_UTF8, 0, tmp, -1, out, max);
}
}
static void load_lang(const WCHAR* code) {
WCHAR path[MAX_PATH];
wsprintfW(path, L"%s\\languages\\%s.json", g_engine_dir, code);
HANDLE hf = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hf == INVALID_HANDLE_VALUE) {
// try English fallback
wsprintfW(path, L"%s\\languages\\en.json", g_engine_dir);
hf = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hf == INVALID_HANDLE_VALUE) return;
}
DWORD sz = GetFileSize(hf, NULL);
char* buf = malloc(sz + 1); DWORD rd; ReadFile(hf, buf, sz, &rd, NULL); buf[sz] = '\0'; CloseHandle(hf);
g_t_count = 0;
const char* keys[] = {"app_title","file","build","scene","help","lang","new","open","save","exit",
"run","compile","package","explorer","output","ready","running","saved","opened",
"build_first","kill","packaged","about"};
for (int i = 0; i < 23; i++) {
MultiByteToWideChar(CP_UTF8, 0, keys[i], -1, g_t_keys[g_t_count], 64);
parse_json_value(buf, keys[i], g_t[g_t_count], 256);
g_t_count++;
}
free(buf);
wcscpy(g_cur_lang, code);
}
static void discover_languages(void) {
g_lang_count = 0;
WCHAR pattern[MAX_PATH];
wsprintfW(pattern, L"%s\\languages\\*.json", g_engine_dir);
WIN32_FIND_DATAW fd;
HANDLE h = FindFirstFileW(pattern, &fd);
if (h == INVALID_HANDLE_VALUE) return;
do {
WCHAR name[MAX_PATH]; wcscpy(name, fd.cFileName);
WCHAR* dot = wcsrchr(name, L'.'); if (dot) *dot = 0;
wcscpy(g_lang_codes[g_lang_count], name);
// Read language name from JSON
WCHAR path[MAX_PATH]; wsprintfW(path, L"%s\\languages\\%s", g_engine_dir, fd.cFileName);
HANDLE hf = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hf != INVALID_HANDLE_VALUE) {
DWORD sz = GetFileSize(hf, NULL); char* buf = malloc(sz + 1); DWORD rd;
ReadFile(hf, buf, sz, &rd, NULL); buf[sz] = '\0'; CloseHandle(hf);
WCHAR title[256]; parse_json_value(buf, "app_title", title, 256);
wsprintfW(g_lang_names[g_lang_count], L"%s (%s)", title, name);
free(buf);
}
g_lang_count++;
} while (FindNextFileW(h, &fd));
FindClose(h);
}
static void console_msg(const char* msg) {
int len = GetWindowTextLengthA(g_console);
SendMessageA(g_console, EM_SETSEL, len, len);
SendMessageA(g_console, EM_REPLACESEL, FALSE, (LPARAM)msg);
}
static void populate_tree(void) {
TreeView_DeleteAllItems(g_tree);
TVINSERTSTRUCTW tvi = {0};
tvi.hParent = TVI_ROOT; tvi.hInsertAfter = TVI_LAST;
tvi.item.mask = TVIF_TEXT;
tvi.item.pszText = (WCHAR*)L"lity3d";
HTREEITEM root = TreeView_InsertItem(g_tree, &tvi);
WIN32_FIND_DATAW fd; WCHAR pat[MAX_PATH];
wsprintfW(pat, L"%s\\*", g_engine_dir);
HANDLE hFind = FindFirstFileW(pat, &fd);
if (hFind == INVALID_HANDLE_VALUE) return;
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!wcscmp(fd.cFileName,L".")||!wcscmp(fd.cFileName,L"..")) continue;
if (!wcscmp(fd.cFileName,L".git")||!wcscmp(fd.cFileName,L"build")||!wcscmp(fd.cFileName,L"target")) continue;
tvi.hParent = root; tvi.item.pszText = fd.cFileName;
HTREEITEM d = TreeView_InsertItem(g_tree, &tvi);
WCHAR sub[MAX_PATH]; wsprintfW(sub, L"%s\\%s\\*", g_engine_dir, fd.cFileName);
WIN32_FIND_DATAW fd2; HANDLE h2 = FindFirstFileW(sub, &fd2);
if (h2 != INVALID_HANDLE_VALUE) {
do { if (fd2.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
tvi.hParent = d; tvi.item.pszText = fd2.cFileName;
TreeView_InsertItem(g_tree, &tvi);
} while (FindNextFileW(h2, &fd2));
FindClose(h2);
}
} else {
if (wcsstr(fd.cFileName,L".py")||wcsstr(fd.cFileName,L".c")||wcsstr(fd.cFileName,L".h")||
wcsstr(fd.cFileName,L".asm")||wcsstr(fd.cFileName,L".md")||wcsstr(fd.cFileName,L".txt")||
wcsstr(fd.cFileName,L".json")||wcsstr(fd.cFileName,L".toml")||wcsstr(fd.cFileName,L".bat")) {
tvi.hParent = root; tvi.item.pszText = fd.cFileName;
TreeView_InsertItem(g_tree, &tvi);
}
}
} while (FindNextFileW(hFind, &fd));
FindClose(hFind);
}
static void open_file_at(WCHAR* path) {
HANDLE hf = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hf == INVALID_HANDLE_VALUE) return;
DWORD sz = GetFileSize(hf, NULL); char* buf = malloc(sz + 1);
DWORD rd; ReadFile(hf, buf, sz, &rd, NULL); buf[sz] = '\0'; CloseHandle(hf);
SetWindowTextA(g_edit, buf); free(buf);
wcscpy(g_current_file, path);
WCHAR title[MAX_PATH]; wsprintfW(title, L"%s - %s", T("app_title"), path);
SetWindowTextW(g_hwnd, title);
}
static void open_file_dialog(void) {
WCHAR file[MAX_PATH] = L""; OPENFILENAMEW of = {sizeof(of)};
of.lpstrFilter = L"All\0*.*\0Python\0*.py\0C\0*.c;*.h\0";
of.lpstrFile = file; of.nMaxFile = MAX_PATH; of.Flags = OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&of)) open_file_at(file);
}
static void save_file(void) {
if (!g_current_file[0]) { open_file_dialog(); return; }
int len = GetWindowTextLengthA(g_edit); char* buf = malloc(len + 1);
GetWindowTextA(g_edit, buf, len + 1);
HANDLE hf = CreateFileW(g_current_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (hf != INVALID_HANDLE_VALUE) { DWORD wr; WriteFile(hf, buf, len, &wr, NULL); CloseHandle(hf); }
free(buf); console_msg("[OK] Saved.\n");
}
static void run_game(void) {
WCHAR p[MAX_PATH]; wsprintfW(p, L"%s\\build\\lity.exe", g_engine_dir);
if (GetFileAttributesW(p) == INVALID_FILE_ATTRIBUTES) { console_msg("[!] Build engine first\n"); return; }
ShellExecuteW(NULL, L"open", p, NULL, g_engine_dir, SW_SHOWNORMAL);
console_msg("[>] Run game\n");
}
static void build_engine(void) {
WCHAR cmd[MAX_PATH*2];
wsprintfW(cmd, L"/c set PATH=C:\\mingw64\\bin;%%PATH%%&& cd /d %s\\build && cmake --build . -j4", g_engine_dir);
ShellExecuteW(NULL, L"open", L"cmd.exe", cmd, NULL, SW_SHOWNORMAL);
console_msg("[>] Build engine...\n");
}
static void package_exe(void) {
WCHAR src[MAX_PATH], dst[MAX_PATH] = L"";
wsprintfW(src, L"%s\\build\\lity.exe", g_engine_dir);
OPENFILENAMEW of = {sizeof(of)}; of.lpstrFilter = L"EXE\0*.exe\0";
of.lpstrFile = dst; of.nMaxFile = MAX_PATH; of.lpstrTitle = L"Package"; of.Flags = OFN_OVERWRITEPROMPT;
if (GetSaveFileNameW(&of)) { CopyFileW(src, dst, FALSE); console_msg("[OK] Packaged.\n"); }
}
static void rebuild_lang_menu(void) {
HMENU hBar = GetMenu(g_hwnd);
HMENU hLang = CreatePopupMenu();
for (int i = 0; i < g_lang_count; i++) {
UINT flags = (!wcscmp(g_lang_codes[i], g_cur_lang)) ? (MF_STRING|MF_CHECKED) : MF_STRING;
AppendMenuW(hLang, flags, IDM_LANG_BASE + i, g_lang_names[i]);
}
// Replace old Language menu
HMENU hOld = GetSubMenu(hBar, 3); // Language is 4th menu
if (hOld) { RemoveMenu(hBar, 3, MF_BYPOSITION); DestroyMenu(hOld); }
// Insert before Help
InsertMenuW(hBar, 3, MF_BYPOSITION | MF_POPUP, (UINT_PTR)hLang, T("lang"));
DrawMenuBar(g_hwnd);
}
static LRESULT CALLBACK WP(HWND h, UINT m, WPARAM w, LPARAM l) {
switch (m) {
case WM_COMMAND:
if (LOWORD(w) == IDM_NEW) { SetWindowTextA(g_edit, ""); g_current_file[0] = 0; }
else if (LOWORD(w) == IDM_OPEN) open_file_dialog();
else if (LOWORD(w) == IDM_SAVE) save_file();
else if (LOWORD(w) == IDM_RUN) run_game();
else if (LOWORD(w) == IDM_BUILD) build_engine();
else if (LOWORD(w) == IDM_PACKAGE) package_exe();
else if (LOWORD(w) == IDM_ABOUT) MessageBoxW(h, L"Lity3D Engine v1.0\nC + MASM x64\nD3D11 + Python Scripting\n\nhttps://gitee.com/shuiyu1123/lity3d", T("about"), MB_OK|MB_ICONINFORMATION);
else if (LOWORD(w) >= IDM_LANG_BASE && LOWORD(w) < IDM_LANG_BASE + g_lang_count) {
int idx = LOWORD(w) - IDM_LANG_BASE;
load_lang(g_lang_codes[idx]);
rebuild_lang_menu();
SetWindowTextW(g_hwnd, T("app_title"));
populate_tree();
}
break;
case WM_NOTIFY: {
NMHDR* nm = (NMHDR*)l;
if (nm->idFrom == IDC_TREE && nm->code == TVN_SELCHANGED) {
TVITEMW ti = {0}; ti.mask = TVIF_TEXT|TVIF_HANDLE;
ti.hItem = TreeView_GetSelection(g_tree);
WCHAR name[MAX_PATH]; ti.pszText = name; ti.cchTextMax = MAX_PATH;
TreeView_GetItem(g_tree, &ti);
if (!ti.hItem) break;
HTREEITEM par = TreeView_GetParent(g_tree, ti.hItem);
WCHAR full[MAX_PATH];
if (par) { WCHAR parent[MAX_PATH]={0}; TVITEMW pti={0};
pti.mask=TVIF_TEXT; pti.hItem=par; pti.pszText=parent; pti.cchTextMax=MAX_PATH;
TreeView_GetItem(g_tree, &pti);
wsprintfW(full, L"%s\\%s\\%s", g_engine_dir, parent, name);
} else wsprintfW(full, L"%s\\%s", g_engine_dir, name);
DWORD attr = GetFileAttributesW(full);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
open_file_at(full);
}
break; }
case WM_SIZE: {
int w2 = LOWORD(l), h2 = HIWORD(l);
if (w2<100||h2<100) break;
int tw=220, ch=160;
MoveWindow(g_tree,0,0,tw,h2-ch,TRUE);
MoveWindow(g_edit,tw+4,0,w2-tw-4,h2-ch,TRUE);
MoveWindow(g_console,0,h2-ch,w2,ch-24,TRUE);
break; }
case WM_DESTROY: PostQuitMessage(0); return 0;
}
return DefWindowProcW(h, m, w, l);
}
int WINAPI wWinMain(HINSTANCE hI, HINSTANCE hP, LPWSTR nC, int nS) {
(void)hP; (void)nC; (void)nS;
GetModuleFileNameW(NULL, g_engine_dir, MAX_PATH);
WCHAR* sl = wcsrchr(g_engine_dir, L'\\'); if (sl) *sl = 0;
// Editor.exe is in lity3d/Editor/, engine root is one level up
sl = wcsrchr(g_engine_dir, L'\\'); if (sl) *sl = 0;
INITCOMMONCONTROLSEX icc = {sizeof(icc), ICC_TREEVIEW_CLASSES}; InitCommonControlsEx(&icc);
WNDCLASSEXW wc = {sizeof(wc)}; wc.style=CS_HREDRAW|CS_VREDRAW; wc.lpfnWndProc=WP;
wc.hInstance=hI; wc.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1); wc.lpszClassName=L"LityEditor";
wc.hCursor=LoadCursor(NULL,IDC_ARROW); wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassExW(&wc);
// Discover and load languages
discover_languages();
// Auto-detect system language
WCHAR sysLang[10]; int n = GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, sysLang, 10);
if (n > 0) {
for (int i = 0; i < g_lang_count; i++) {
if (!_wcsicmp(g_lang_codes[i], sysLang)) { load_lang(g_lang_codes[i]); goto lang_done; }
}
}
load_lang(L"en");
lang_done:
g_hwnd = CreateWindowExW(0, L"LityEditor", T("app_title"),
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN, 100,100,1400,900, NULL, NULL, hI, NULL);
g_tree = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW, L"",
WS_CHILD|WS_VISIBLE|TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT,
0,0,220,600, g_hwnd, (HMENU)IDC_TREE, hI, NULL);
g_edit = CreateWindowExW(WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL|ES_MULTILINE|ES_AUTOVSCROLL,
224,0,1176,600, g_hwnd, (HMENU)IDC_EDIT, hI, NULL);
SendMessageW(g_edit, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE);
g_console = CreateWindowExW(WS_EX_CLIENTEDGE, L"EDIT", L"",
WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE|ES_READONLY,
0,600,1400,136, g_hwnd, (HMENU)IDC_CONSOLE, hI, NULL);
HFONT hf = CreateFontW(14,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,0,0,0,FIXED_PITCH,L"Consolas");
SendMessageW(g_console, WM_SETFONT, (WPARAM)hf, TRUE);
// Build menu
HMENU hBar = CreateMenu();
HMENU hFile = CreatePopupMenu();
AppendMenuW(hFile,MF_STRING,IDM_NEW,T("new")); AppendMenuW(hFile,MF_STRING,IDM_OPEN,T("open"));
AppendMenuW(hFile,MF_STRING,IDM_SAVE,T("save")); AppendMenuW(hFile,MF_SEPARATOR,0,NULL);
AppendMenuW(hFile,MF_STRING,IDM_ABOUT,T("about"));
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hFile,T("file"));
HMENU hBuild = CreatePopupMenu();
AppendMenuW(hBuild,MF_STRING,IDM_RUN,T("run")); AppendMenuW(hBuild,MF_SEPARATOR,0,NULL);
AppendMenuW(hBuild,MF_STRING,IDM_BUILD,T("compile")); AppendMenuW(hBuild,MF_STRING,IDM_PACKAGE,T("package"));
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hBuild,T("build"));
HMENU hLang = CreatePopupMenu();
for (int i=0;i<g_lang_count;i++) {
UINT flags=(!wcscmp(g_lang_codes[i],g_cur_lang))?(MF_STRING|MF_CHECKED):MF_STRING;
AppendMenuW(hLang,flags,IDM_LANG_BASE+i,g_lang_names[i]);
}
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hLang,T("lang"));
HMENU hHelp=CreatePopupMenu(); AppendMenuW(hHelp,MF_STRING,IDM_ABOUT,T("about"));
AppendMenuW(hBar,MF_POPUP,(UINT_PTR)hHelp,T("help"));
SetMenu(g_hwnd,hBar);
populate_tree();
console_msg("Lity3D Editor ready.\n");
ShowWindow(g_hwnd,SW_SHOW);
MSG msg;
while(GetMessageW(&msg,NULL,0,0)){TranslateMessage(&msg);DispatchMessageW(&msg);}
return (int)msg.wParam;
}
+464
View File
@@ -0,0 +1,464 @@
use eframe::egui;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};
use std::thread;
const SUPPORTED_LANGS: &[(&str, &str)] = &[
("zh", "中文"), ("en", "English"), ("ja", "日本語"), ("ko", "한국어"),
("es", "Español"), ("fr", "Français"), ("de", "Deutsch"), ("ru", "Русский"),
("pt", "Português"), ("it", "Italiano"), ("tr", "Türkçe"),
];
struct LityEditor {
engine_dir: PathBuf,
code: String,
file_path: Option<PathBuf>,
file_tree: Vec<FileNode>,
tree_expanded: std::collections::HashSet<String>,
console: String,
current_lang: String,
process_running: bool,
process_output: Arc<Mutex<Vec<String>>>,
needs_refresh: bool,
show_about: bool,
}
#[derive(Clone)]
struct FileNode {
name: String,
path: PathBuf,
is_dir: bool,
children: Vec<FileNode>,
}
impl LityEditor {
fn new(cc: &eframe::CreationContext) -> Self {
let mut style = (*cc.egui_ctx.style()).clone();
style.spacing.item_spacing = egui::vec2(4.0, 4.0);
cc.egui_ctx.set_style(style);
let engine_dir = PathBuf::from(std::env::var("ENGINE_DIR")
.unwrap_or_else(|_| {
let exe = std::env::current_exe().unwrap_or_default();
exe.parent().unwrap_or(Path::new(".")).to_path_buf()
}));
let mut editor = Self {
engine_dir,
code: String::new(),
file_path: None,
file_tree: Vec::new(),
tree_expanded: std::collections::HashSet::new(),
console: String::new(),
current_lang: "zh".to_string(),
process_running: false,
process_output: Arc::new(Mutex::new(Vec::new())),
needs_refresh: true,
show_about: false,
};
editor.refresh_tree();
editor
}
fn t(&self, key: &str) -> String {
let lang = &self.current_lang;
match (lang.as_str(), key) {
("zh", k) => match k {
"app_title" => "Lity3D 编辑器".into(),
"menu_file" => "文件".into(),
"menu_edit" => "编辑".into(),
"menu_build" => "构建".into(),
"menu_scene" => "场景".into(),
"menu_help" => "帮助".into(),
"menu_view" => "视图".into(),
"menu_lang" => "语言".into(),
"new" => "新建".into(),
"open" => "打开".into(),
"save" => "保存".into(),
"run" => "运行".into(),
"stop" => "停止".into(),
"build" => "编译".into(),
"package" => "打包".into(),
"explorer" => "资源管理器".into(),
"output" => "输出".into(),
"ready" => "就绪".into(),
"running" => "运行中...".into(),
"about" => "关于".into(),
"save_ok" => "已保存".into(),
"open_ok" => "已打开".into(),
"build_first" => "请先编译引擎".into(),
_ => k.into(),
},
("en", k) => match k {
"app_title" => "Lity3D Editor".into(),
"menu_file" => "File".into(),
"menu_edit" => "Edit".into(),
"menu_build" => "Build".into(),
"menu_scene" => "Scene".into(),
"menu_help" => "Help".into(),
"menu_view" => "View".into(),
"menu_lang" => "Language".into(),
"new" => "New".into(),
"open" => "Open".into(),
"save" => "Save".into(),
"run" => "Run".into(),
"stop" => "Stop".into(),
"build" => "Build".into(),
"package" => "Package".into(),
"explorer" => "Explorer".into(),
"output" => "Output".into(),
"ready" => "Ready".into(),
"running" => "Running...".into(),
"about" => "About".into(),
"save_ok" => "Saved".into(),
"open_ok" => "Opened".into(),
"build_first" => "Build engine first".into(),
_ => k.into(),
},
_ => {
// Fallback to English
match key {
"app_title" => "Lity3D Editor".into(),
_ => key.into(),
}
}
}
}
fn refresh_tree(&mut self) {
self.file_tree.clear();
self.file_tree = Self::build_tree(&self.engine_dir, 0);
self.needs_refresh = false;
}
fn build_tree(dir: &Path, depth: usize) -> Vec<FileNode> {
if depth > 3 { return Vec::new(); }
let skip = [".git", "build", "__pycache__", "target", ".vs", ".vscode", "obj"];
let mut nodes = Vec::new();
if let Ok(entries) = fs::read_dir(dir) {
let mut entries: Vec<_> = entries.filter_map(|e| e.ok()).collect();
entries.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
for entry in entries {
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with('.') || skip.contains(&name.as_str()) { continue; }
let path = entry.path();
if path.is_dir() {
let children = Self::build_tree(&path, depth + 1);
nodes.push(FileNode { name, path, is_dir: true, children });
} else if name.ends_with(".py") || name.ends_with(".c") || name.ends_with(".h")
|| name.ends_with(".asm") || name.ends_with(".txt") || name.ends_with(".json")
|| name.ends_with(".md") || name.ends_with(".toml") || name.ends_with(".bat") {
nodes.push(FileNode { name, path, is_dir: false, children: Vec::new() });
}
}
}
nodes
}
fn open_file(&mut self, path: PathBuf) {
if let Ok(content) = fs::read_to_string(&path) {
self.code = content;
self.file_path = Some(path);
}
}
fn save_file(&mut self) {
if let Some(ref path) = self.file_path {
if fs::write(path, &self.code).is_ok() {
self.console.push_str(&format!("[OK] {}\n", self.t("save_ok")));
}
}
}
fn run_process(&mut self, cmd: Vec<String>) {
self.process_running = true;
self.console.push_str(&format!("[>] {}\n", cmd.join(" ")));
let output = self.process_output.clone();
let cmd_clone = cmd.clone();
thread::spawn(move || {
let result = Command::new(&cmd_clone[0])
.args(&cmd_clone[1..])
.current_dir(std::env::current_dir().unwrap_or_default())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output();
match result {
Ok(out) => {
let mut buf = output.lock().unwrap();
if let Ok(s) = String::from_utf8(out.stdout) { buf.push(s); }
if let Ok(s) = String::from_utf8(out.stderr) { buf.push(s); }
buf.push(format!("[exit: {}]\n", out.status.code().unwrap_or(-1)));
}
Err(e) => {
output.lock().unwrap().push(format!("[ERROR] {}\n", e));
}
}
});
}
fn build(&mut self) {
let build_dir = self.engine_dir.join("build");
let _ = fs::create_dir_all(&build_dir);
self.run_process(vec![
"cmake".into(), "--build".into(),
build_dir.to_string_lossy().to_string(),
]);
}
fn run_game(&mut self) {
let exe = self.engine_dir.join("build").join("lity.exe");
if exe.exists() {
self.run_process(vec![exe.to_string_lossy().to_string()]);
} else {
self.console.push_str(&format!("[!] {}\n", self.t("build_first")));
}
}
fn ui_tree_node(ui: &mut egui::Ui, nodes: &[FileNode], expanded: &mut std::collections::HashSet<String>, selected: &mut Option<PathBuf>) {
for node in nodes {
let id = egui::Id::new(&node.path);
if node.is_dir {
let header = ui.horizontal(|ui| {
let icon = if expanded.contains(&node.name) { "▼" } else { "▶" };
if ui.selectable_label(false, format!("{} 📁 {}", icon, node.name)).clicked() {
if expanded.contains(&node.name) {
expanded.remove(&node.name);
} else {
expanded.insert(node.name.clone());
}
}
});
if expanded.contains(&node.name) {
ui.indent(id, |ui| {
Self::ui_tree_node(ui, &node.children, expanded, selected);
});
}
} else {
let ext = node.name.rsplit('.').next().unwrap_or("");
let icon = match ext {
"py" => "🐍", "c" | "h" => "⚙", "asm" => "🔧",
"md" => "📝", "json" => "📋", _ => "📄",
};
if ui.selectable_label(false, format!("{} {}", icon, node.name)).clicked() {
*selected = Some(node.path.clone());
}
}
}
}
}
impl eframe::App for LityEditor {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Collect process output
{
let mut buf = self.process_output.lock().unwrap();
if !buf.is_empty() {
for line in buf.drain(..) {
self.console.push_str(&line);
if !line.ends_with('\n') { self.console.push('\n'); }
}
}
}
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
egui::menu::bar(ui, |ui| {
ui.menu_button(self.t("menu_file"), |ui| {
if ui.button(self.t("new")).clicked() {
self.code.clear();
self.file_path = None;
ui.close_menu();
}
if ui.button(self.t("open")).clicked() {
if let Some(path) = rfd::FileDialog::new()
.set_title(&self.t("open"))
.add_filter("Code", &["py", "c", "h", "asm", "txt", "json"])
.pick_file() {
self.open_file(path);
}
ui.close_menu();
}
if ui.button(self.t("save")).clicked() {
self.save_file();
ui.close_menu();
}
ui.separator();
if ui.button("Exit").clicked() {
std::process::exit(0);
}
});
ui.menu_button(self.t("menu_build"), |ui| {
if ui.button(format!("▶ {}", self.t("run"))).clicked() {
self.run_game();
ui.close_menu();
}
if ui.button(format!("⏹ {}", self.t("stop"))).clicked() {
self.process_running = false;
ui.close_menu();
}
ui.separator();
if ui.button(format!("🔨 {}", self.t("build"))).clicked() {
self.build();
ui.close_menu();
}
if ui.button(format!("📦 {}", self.t("package"))).clicked() {
if let Some(path) = rfd::FileDialog::new()
.set_title(&self.t("package"))
.add_filter("EXE", &["exe"])
.save_file() {
let exe = self.engine_dir.join("build").join("lity.exe");
if exe.exists() {
let _ = fs::copy(&exe, &path);
self.console.push_str(&format!("[OK] Packaged to {}\n", path.display()));
} else {
self.console.push_str(&format!("[!] {}\n", self.t("build_first")));
}
}
ui.close_menu();
}
});
ui.menu_button(self.t("menu_lang"), |ui| {
for (code, name) in SUPPORTED_LANGS {
if ui.selectable_label(self.current_lang == *code, *name).clicked() {
self.current_lang = code.to_string();
ui.close_menu();
}
}
});
ui.menu_button(self.t("menu_help"), |ui| {
if ui.button(self.t("about")).clicked() {
self.show_about = true;
ui.close_menu();
}
});
});
});
egui::TopBottomPanel::bottom("toolbar").show(ctx, |ui| {
ui.horizontal(|ui| {
if ui.button("📂 Open").on_hover_text("Ctrl+O").clicked() {
if let Some(path) = rfd::FileDialog::new().pick_file() {
self.open_file(path);
}
}
if ui.button("💾 Save").on_hover_text("Ctrl+S").clicked() {
self.save_file();
}
ui.separator();
let run_text = if self.process_running { "⏹ Stop" } else { "▶ Run (F5)" };
if ui.button(run_text).clicked() {
if self.process_running {
self.process_running = false;
self.console.push_str("[!] Stopped\n");
} else {
self.run_game();
}
}
if ui.button("🔨 Build (F7)").clicked() {
self.build();
}
ui.separator();
if ui.button("📁 Refresh").clicked() {
self.needs_refresh = true;
}
if let Some(ref p) = self.file_path {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.label(egui::RichText::new(p.file_name().unwrap_or_default().to_string_lossy().to_string())
.color(egui::Color32::GRAY).small());
});
}
});
});
egui::SidePanel::left("explorer").resizable(true).default_width(220.0).show(ctx, |ui| {
ui.heading(self.t("explorer"));
ui.separator();
if self.needs_refresh { self.refresh_tree(); }
egui::ScrollArea::vertical().show(ui, |ui| {
let mut selected = None;
Self::ui_tree_node(ui, &self.file_tree, &mut self.tree_expanded, &mut selected);
if let Some(path) = selected {
self.open_file(path);
}
});
});
egui::TopBottomPanel::bottom("console").resizable(true).default_height(150.0).show(ctx, |ui| {
ui.heading(self.t("output"));
ui.separator();
egui::ScrollArea::vertical().stick_to_bottom(true).show(ui, |ui| {
ui.add(egui::TextEdit::multiline(&mut self.console.as_str())
.desired_width(f32::INFINITY)
.font(egui::TextStyle::Monospace));
});
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.horizontal(|ui| {
let title = self.file_path.as_ref()
.map(|p| p.file_name().unwrap_or_default().to_string_lossy().to_string())
.unwrap_or_else(|| "untitled".into());
ui.heading(title);
if self.file_path.is_some() {
ui.label(egui::RichText::new("●").color(egui::Color32::from_rgb(255, 200, 0)));
}
});
ui.separator();
egui::ScrollArea::both().show(ui, |ui| {
let output = ui.add(egui::TextEdit::multiline(&mut self.code)
.desired_width(f32::INFINITY)
.desired_rows(40)
.font(egui::TextStyle::Monospace)
.code_editor());
});
});
if self.show_about {
egui::Window::new(self.t("about")).collapsible(false).resizable(false).show(ctx, |ui| {
ui.label("Lity3D Engine v1.0");
ui.label("C + MASM x64 Game Engine");
ui.label("D3D11 Rendering + Python Scripting");
ui.label("11 Languages Supported");
ui.separator();
ui.label("https://gitee.com/shuiyu1123/lity3d");
if ui.button("OK").clicked() {
self.show_about = false;
}
});
}
// Request repaint when process is running
if self.process_running {
ctx.request_repaint_after(std::time::Duration::from_millis(100));
}
}
}
fn main() -> eframe::Result<()> {
let engine_dir = std::env::current_exe()
.ok()
.and_then(|p| p.parent().map(|p| p.to_path_buf()))
.unwrap_or_else(|| PathBuf::from("."));
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([1400.0, 900.0])
.with_title("Lity3D Editor"),
..Default::default()
};
eframe::run_native(
"Lity3D Editor",
options,
Box::new(move |cc| {
let mut editor = LityEditor::new(cc);
editor.engine_dir = engine_dir;
editor.refresh_tree();
Ok(Box::new(editor))
}),
)
}
+15
View File
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>editor</AssemblyName>
<RootNamespace>Lity3DEditor</RootNamespace>
<PublishSingleFile>false</PublishSingleFile>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>
+369
View File
@@ -0,0 +1,369 @@
using System.ComponentModel;
namespace Lity3DEditor;
public class MainForm : Form
{
private List<SceneNode> _nodes = new();
private string? _currentPath;
private bool _modified;
private TreeView _tree = null!;
private PropertyGrid _propertyGrid = null!;
private StatusStrip _status = null!;
private ToolStripButton _btnSave = null!;
public MainForm()
{
Text = "Lity3D Engine Scene Editor";
Size = new Size(1280, 800);
StartPosition = FormStartPosition.CenterScreen;
BackColor = Color.FromArgb(30, 30, 38);
Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point);
BuildMenu();
BuildToolbar();
BuildPanels();
BuildStatus();
Load += (_, _) =>
{
foreach (var c in Controls.OfType<SplitContainer>())
{
if (c.Orientation == Orientation.Vertical)
{
c.Panel1MinSize = 180;
c.Panel2MinSize = 300;
c.SplitterDistance = 260;
}
else
{
c.Panel1MinSize = 200;
c.Panel2MinSize = 120;
c.SplitterDistance = c.Height - 220;
}
}
TryOpenScene(Path.Combine(Application.StartupPath, "scene.txt"));
};
FormClosing += (_, e) => { if (!PromptSave()) e.Cancel = true; };
}
private void BuildMenu()
{
var menu = new MenuStrip();
var file = new ToolStripMenuItem("文件(&F)");
file.DropDownItems.Add(new ToolStripMenuItem("打开...", null, (_, _) => OpenScene()) { ShortcutKeys = Keys.Control | Keys.O });
file.DropDownItems.Add(new ToolStripMenuItem("保存", null, (_, _) => SaveScene()) { ShortcutKeys = Keys.Control | Keys.S });
file.DropDownItems.Add(new ToolStripMenuItem("另存为...", null, (_, _) => SaveSceneAs()));
file.DropDownItems.Add(new ToolStripSeparator());
file.DropDownItems.Add(new ToolStripMenuItem("退出", null, (_, _) => Close()) { ShortcutKeys = Keys.Alt | Keys.F4 });
menu.Items.Add(file);
var edit = new ToolStripMenuItem("编辑(&E)");
edit.DropDownItems.Add(new ToolStripMenuItem("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
edit.DropDownItems.Add(new ToolStripSeparator());
edit.DropDownItems.Add(new ToolStripMenuItem("删除选中", null, (_, _) => DeleteSelected()) { ShortcutKeys = Keys.Delete });
menu.Items.Add(edit);
MainMenuStrip = menu;
Controls.Add(menu);
}
private void BuildToolbar()
{
var tool = new ToolStrip();
tool.GripStyle = ToolStripGripStyle.Hidden;
tool.BackColor = Color.FromArgb(40, 40, 50);
tool.ForeColor = Color.White;
tool.Renderer = new DarkToolStripRenderer();
_btnSave = new ToolStripButton("保存", null, (_, _) => SaveScene()) { Enabled = false };
tool.Items.Add(_btnSave);
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
tool.Items.Add(new ToolStripButton("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
tool.Items.Add(new ToolStripButton("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("删除", null, (_, _) => DeleteSelected()));
tool.Dock = DockStyle.Top;
Controls.Add(tool);
}
private void BuildPanels()
{
var split = new SplitContainer
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(30, 30, 38)
};
split.Panel1.BackColor = Color.FromArgb(35, 35, 45);
split.Panel2.BackColor = Color.FromArgb(35, 35, 45);
_tree = new TreeView
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
BorderStyle = BorderStyle.None,
FullRowSelect = true,
HideSelection = false,
ShowLines = true,
ShowPlusMinus = true,
LineColor = Color.FromArgb(100, 120, 160)
};
_tree.AfterSelect += (_, _) => OnNodeSelected();
split.Panel1.Controls.Add(_tree);
var rightSplit = new SplitContainer
{
Dock = DockStyle.Fill,
Orientation = Orientation.Horizontal,
BackColor = Color.FromArgb(30, 30, 38)
};
rightSplit.Panel1.BackColor = Color.FromArgb(35, 35, 45);
rightSplit.Panel2.BackColor = Color.FromArgb(28, 28, 36);
_propertyGrid = new PropertyGrid
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
CategoryForeColor = Color.FromArgb(180, 180, 200),
CommandsBackColor = Color.FromArgb(35, 35, 45),
CommandsForeColor = Color.White,
DisabledItemForeColor = Color.Gray,
HelpBackColor = Color.FromArgb(28, 28, 36),
HelpForeColor = Color.White,
ViewBackColor = Color.FromArgb(35, 35, 45),
ViewForeColor = Color.White,
LineColor = Color.FromArgb(60, 60, 80),
ToolbarVisible = false,
PropertySort = PropertySort.Categorized
};
_propertyGrid.PropertyValueChanged += (_, _) => OnPropertyChanged();
rightSplit.Panel1.Controls.Add(_propertyGrid);
var previewLabel = new Label
{
Dock = DockStyle.Fill,
Text = "3D 预览区\n(由 Lity3D Engine 运行时渲染)",
ForeColor = Color.FromArgb(120, 120, 140),
BackColor = Color.FromArgb(28, 28, 36),
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font(Font.FontFamily, 11f, FontStyle.Regular)
};
rightSplit.Panel2.Controls.Add(previewLabel);
split.Panel2.Controls.Add(rightSplit);
Controls.Add(split);
}
private void BuildStatus()
{
_status = new StatusStrip();
_status.BackColor = Color.FromArgb(40, 40, 50);
_status.ForeColor = Color.White;
_status.Items.Add(new ToolStripStatusLabel("就绪"));
Controls.Add(_status);
}
private void TryOpenScene(string path)
{
if (!File.Exists(path)) return;
try
{
_nodes = SceneIO.Load(path);
_currentPath = path;
RebuildTree();
SetModified(false);
SetStatus($"已打开 {path}");
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "打开失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void OpenScene()
{
if (!PromptSave()) return;
using var dlg = new OpenFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "打开场景"
};
if (dlg.ShowDialog(this) == DialogResult.OK)
TryOpenScene(dlg.FileName);
}
private bool SaveScene()
{
if (string.IsNullOrEmpty(_currentPath))
return SaveSceneAs();
try
{
SceneIO.Save(_currentPath, _nodes);
SetModified(false);
SetStatus($"已保存 {_currentPath}");
return true;
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private bool SaveSceneAs()
{
using var dlg = new SaveFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "另存为"
};
if (dlg.ShowDialog(this) != DialogResult.OK) return false;
_currentPath = dlg.FileName;
return SaveScene();
}
private bool PromptSave()
{
if (!_modified) return true;
var r = MessageBox.Show(this, "场景已修改,是否保存?", "保存确认",
MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Cancel) return false;
if (r == DialogResult.Yes) return SaveScene();
return true;
}
private void AddNode(MeshType type, string baseName)
{
int idx = 1;
string name = baseName;
while (_nodes.Any(n => n.Name == name))
name = $"{baseName}_{idx++}";
var parentIdx = -1;
if (_tree.SelectedNode?.Tag is SceneNode sel && sel.Name != "root")
parentIdx = _nodes.IndexOf(sel);
else if (_nodes.Count > 0)
parentIdx = 0; // root
var node = new SceneNode
{
Name = name,
ParentIndex = parentIdx,
Mesh = type,
PosY = type == MeshType.PLANE ? -1.0f : 0.0f
};
_nodes.Add(node);
RebuildTree();
SelectNode(node);
SetModified(true);
SetStatus($"添加 {name}");
}
private void DeleteSelected()
{
if (_tree.SelectedNode?.Tag is not SceneNode node || node.Name == "root")
{
MessageBox.Show(this, "不能删除根节点", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
_nodes.Remove(node);
RebuildTree();
SetModified(true);
SetStatus($"删除 {node.Name}");
}
private void RebuildTree()
{
_tree.Nodes.Clear();
var treeMap = new Dictionary<int, TreeNode>();
for (int i = 0; i < _nodes.Count; i++)
{
var n = _nodes[i];
var tn = new TreeNode(n.Name) { Tag = n, ForeColor = Color.White };
treeMap[i] = tn;
if (n.ParentIndex >= 0 && treeMap.TryGetValue(n.ParentIndex, out var parent))
parent.Nodes.Add(tn);
else
_tree.Nodes.Add(tn);
}
_tree.ExpandAll();
}
private void SelectNode(SceneNode node)
{
foreach (TreeNode root in _tree.Nodes)
{
var found = FindNode(root, node);
if (found != null)
{
_tree.SelectedNode = found;
return;
}
}
}
private static TreeNode? FindNode(TreeNode parent, SceneNode node)
{
if (parent.Tag == node) return parent;
foreach (TreeNode c in parent.Nodes)
{
var r = FindNode(c, node);
if (r != null) return r;
}
return null;
}
private void OnNodeSelected()
{
_propertyGrid.SelectedObject = _tree.SelectedNode?.Tag;
if (_tree.SelectedNode?.Tag is SceneNode n)
SetStatus(n.Name);
}
private void OnPropertyChanged()
{
SetModified(true);
_tree.Refresh();
}
private void SetModified(bool modified)
{
_modified = modified;
_btnSave.Enabled = modified;
Text = "Lity3D Engine Scene Editor" + (modified ? " *" : "") + (string.IsNullOrEmpty(_currentPath) ? "" : $" - {_currentPath}");
}
private void SetStatus(string msg)
{
if (_status.Items[0] is ToolStripStatusLabel lbl)
lbl.Text = msg;
}
}
public class DarkToolStripRenderer : ToolStripProfessionalRenderer
{
public DarkToolStripRenderer() : base(new DarkColorTable()) { }
}
public class DarkColorTable : ProfessionalColorTable
{
public override Color ToolStripBorder => Color.FromArgb(60, 60, 80);
public override Color ToolStripGradientBegin => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientEnd => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientMiddle => Color.FromArgb(45, 45, 55);
public override Color ButtonSelectedHighlight => Color.FromArgb(70, 70, 90);
public override Color ButtonSelectedBorder => Color.FromArgb(100, 120, 160);
public override Color ButtonPressedHighlight => Color.FromArgb(60, 80, 120);
public override Color ButtonCheckedHighlight => Color.FromArgb(60, 80, 120);
public override Color MenuBorder => Color.FromArgb(60, 60, 80);
public override Color MenuItemSelected => Color.FromArgb(70, 70, 90);
public override Color MenuItemBorder => Color.FromArgb(100, 120, 160);
}
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Update="Form1.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>
+11
View File
@@ -0,0 +1,11 @@
namespace Lity3DEditor;
static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
+56
View File
@@ -0,0 +1,56 @@
using System.Globalization;
namespace Lity3DEditor;
public static class SceneIO
{
public static List<SceneNode> Load(string path)
{
var nodes = new List<SceneNode>();
using var reader = new StreamReader(path);
string? header = reader.ReadLine();
if (header == null || !header.StartsWith("LITY3D_SCENE"))
throw new InvalidDataException("不是有效的 Lity3D 场景文件");
int lineIdx = 1;
string? line;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line)) continue;
var parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 13) continue;
var node = new SceneNode
{
Name = parts[0],
ParentIndex = int.Parse(parts[1]),
Mesh = Enum.Parse<MeshType>(parts[2], true),
MeshPath = parts[3],
PosX = float.Parse(parts[4], CultureInfo.InvariantCulture),
PosY = float.Parse(parts[5], CultureInfo.InvariantCulture),
PosZ = float.Parse(parts[6], CultureInfo.InvariantCulture),
RotX = float.Parse(parts[7], CultureInfo.InvariantCulture),
RotY = float.Parse(parts[8], CultureInfo.InvariantCulture),
RotZ = float.Parse(parts[9], CultureInfo.InvariantCulture),
ScaleX = float.Parse(parts[10], CultureInfo.InvariantCulture),
ScaleY = float.Parse(parts[11], CultureInfo.InvariantCulture),
ScaleZ = float.Parse(parts[12], CultureInfo.InvariantCulture),
LineIndex = lineIdx
};
nodes.Add(node);
lineIdx++;
}
return nodes;
}
public static void Save(string path, List<SceneNode> nodes)
{
using var writer = new StreamWriter(path);
writer.WriteLine("LITY3D_SCENE 1");
foreach (var node in nodes)
{
int parent = node.ParentIndex;
writer.WriteLine(node.ToSceneLine(parent));
}
}
}
+52
View File
@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.Globalization;
namespace Lity3DEditor;
public enum MeshType
{
[Description("无")]
NONE,
[Description("立方体")]
CUBE,
[Description("球体")]
SPHERE,
[Description("平面")]
PLANE,
[Description("OBJ 模型")]
OBJ
}
public class SceneNode
{
public string Name { get; set; } = "";
public int ParentIndex { get; set; } = -1;
public MeshType Mesh { get; set; } = MeshType.NONE;
public string MeshPath { get; set; } = "-";
public float PosX { get; set; }
public float PosY { get; set; }
public float PosZ { get; set; }
public float RotX { get; set; }
public float RotY { get; set; }
public float RotZ { get; set; }
public float ScaleX { get; set; } = 1.0f;
public float ScaleY { get; set; } = 1.0f;
public float ScaleZ { get; set; } = 1.0f;
[Browsable(false)]
public int LineIndex { get; set; }
public override string ToString() => Name;
public string ToSceneLine(int parentIndex)
{
ParentIndex = parentIndex;
return string.Format(CultureInfo.InvariantCulture,
"{0} {1} {2} {3} {4:F4} {5:F4} {6:F4} {7:F4} {8:F4} {9:F4} {10:F4} {11:F4} {12:F4}",
Name, parentIndex, Mesh, MeshPath,
PosX, PosY, PosZ, RotX, RotY, RotZ, ScaleX, ScaleY, ScaleZ);
}
}
+15
View File
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>editor</AssemblyName>
<RootNamespace>Lity3DEditor</RootNamespace>
<PublishSingleFile>false</PublishSingleFile>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>
+369
View File
@@ -0,0 +1,369 @@
using System.ComponentModel;
namespace Lity3DEditor;
public class MainForm : Form
{
private List<SceneNode> _nodes = new();
private string? _currentPath;
private bool _modified;
private TreeView _tree = null!;
private PropertyGrid _propertyGrid = null!;
private StatusStrip _status = null!;
private ToolStripButton _btnSave = null!;
public MainForm()
{
Text = "Lity3D Engine Scene Editor";
Size = new Size(1280, 800);
StartPosition = FormStartPosition.CenterScreen;
BackColor = Color.FromArgb(30, 30, 38);
Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point);
BuildMenu();
BuildToolbar();
BuildPanels();
BuildStatus();
Load += (_, _) =>
{
foreach (var c in Controls.OfType<SplitContainer>())
{
if (c.Orientation == Orientation.Vertical)
{
c.Panel1MinSize = 180;
c.Panel2MinSize = 300;
c.SplitterDistance = 260;
}
else
{
c.Panel1MinSize = 200;
c.Panel2MinSize = 120;
c.SplitterDistance = c.Height - 220;
}
}
TryOpenScene(Path.Combine(Application.StartupPath, "scene.txt"));
};
FormClosing += (_, e) => { if (!PromptSave()) e.Cancel = true; };
}
private void BuildMenu()
{
var menu = new MenuStrip();
var file = new ToolStripMenuItem("文件(&F)");
file.DropDownItems.Add(new ToolStripMenuItem("打开...", null, (_, _) => OpenScene()) { ShortcutKeys = Keys.Control | Keys.O });
file.DropDownItems.Add(new ToolStripMenuItem("保存", null, (_, _) => SaveScene()) { ShortcutKeys = Keys.Control | Keys.S });
file.DropDownItems.Add(new ToolStripMenuItem("另存为...", null, (_, _) => SaveSceneAs()));
file.DropDownItems.Add(new ToolStripSeparator());
file.DropDownItems.Add(new ToolStripMenuItem("退出", null, (_, _) => Close()) { ShortcutKeys = Keys.Alt | Keys.F4 });
menu.Items.Add(file);
var edit = new ToolStripMenuItem("编辑(&E)");
edit.DropDownItems.Add(new ToolStripMenuItem("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
edit.DropDownItems.Add(new ToolStripSeparator());
edit.DropDownItems.Add(new ToolStripMenuItem("删除选中", null, (_, _) => DeleteSelected()) { ShortcutKeys = Keys.Delete });
menu.Items.Add(edit);
MainMenuStrip = menu;
Controls.Add(menu);
}
private void BuildToolbar()
{
var tool = new ToolStrip();
tool.GripStyle = ToolStripGripStyle.Hidden;
tool.BackColor = Color.FromArgb(40, 40, 50);
tool.ForeColor = Color.White;
tool.Renderer = new DarkToolStripRenderer();
_btnSave = new ToolStripButton("保存", null, (_, _) => SaveScene()) { Enabled = false };
tool.Items.Add(_btnSave);
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
tool.Items.Add(new ToolStripButton("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
tool.Items.Add(new ToolStripButton("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("删除", null, (_, _) => DeleteSelected()));
tool.Dock = DockStyle.Top;
Controls.Add(tool);
}
private void BuildPanels()
{
var split = new SplitContainer
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(30, 30, 38)
};
split.Panel1.BackColor = Color.FromArgb(35, 35, 45);
split.Panel2.BackColor = Color.FromArgb(35, 35, 45);
_tree = new TreeView
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
BorderStyle = BorderStyle.None,
FullRowSelect = true,
HideSelection = false,
ShowLines = true,
ShowPlusMinus = true,
LineColor = Color.FromArgb(100, 120, 160)
};
_tree.AfterSelect += (_, _) => OnNodeSelected();
split.Panel1.Controls.Add(_tree);
var rightSplit = new SplitContainer
{
Dock = DockStyle.Fill,
Orientation = Orientation.Horizontal,
BackColor = Color.FromArgb(30, 30, 38)
};
rightSplit.Panel1.BackColor = Color.FromArgb(35, 35, 45);
rightSplit.Panel2.BackColor = Color.FromArgb(28, 28, 36);
_propertyGrid = new PropertyGrid
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
CategoryForeColor = Color.FromArgb(180, 180, 200),
CommandsBackColor = Color.FromArgb(35, 35, 45),
CommandsForeColor = Color.White,
DisabledItemForeColor = Color.Gray,
HelpBackColor = Color.FromArgb(28, 28, 36),
HelpForeColor = Color.White,
ViewBackColor = Color.FromArgb(35, 35, 45),
ViewForeColor = Color.White,
LineColor = Color.FromArgb(60, 60, 80),
ToolbarVisible = false,
PropertySort = PropertySort.Categorized
};
_propertyGrid.PropertyValueChanged += (_, _) => OnPropertyChanged();
rightSplit.Panel1.Controls.Add(_propertyGrid);
var previewLabel = new Label
{
Dock = DockStyle.Fill,
Text = "3D 预览区\n(由 Lity3D Engine 运行时渲染)",
ForeColor = Color.FromArgb(120, 120, 140),
BackColor = Color.FromArgb(28, 28, 36),
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font(Font.FontFamily, 11f, FontStyle.Regular)
};
rightSplit.Panel2.Controls.Add(previewLabel);
split.Panel2.Controls.Add(rightSplit);
Controls.Add(split);
}
private void BuildStatus()
{
_status = new StatusStrip();
_status.BackColor = Color.FromArgb(40, 40, 50);
_status.ForeColor = Color.White;
_status.Items.Add(new ToolStripStatusLabel("就绪"));
Controls.Add(_status);
}
private void TryOpenScene(string path)
{
if (!File.Exists(path)) return;
try
{
_nodes = SceneIO.Load(path);
_currentPath = path;
RebuildTree();
SetModified(false);
SetStatus($"已打开 {path}");
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "打开失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void OpenScene()
{
if (!PromptSave()) return;
using var dlg = new OpenFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "打开场景"
};
if (dlg.ShowDialog(this) == DialogResult.OK)
TryOpenScene(dlg.FileName);
}
private bool SaveScene()
{
if (string.IsNullOrEmpty(_currentPath))
return SaveSceneAs();
try
{
SceneIO.Save(_currentPath, _nodes);
SetModified(false);
SetStatus($"已保存 {_currentPath}");
return true;
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private bool SaveSceneAs()
{
using var dlg = new SaveFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "另存为"
};
if (dlg.ShowDialog(this) != DialogResult.OK) return false;
_currentPath = dlg.FileName;
return SaveScene();
}
private bool PromptSave()
{
if (!_modified) return true;
var r = MessageBox.Show(this, "场景已修改,是否保存?", "保存确认",
MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Cancel) return false;
if (r == DialogResult.Yes) return SaveScene();
return true;
}
private void AddNode(MeshType type, string baseName)
{
int idx = 1;
string name = baseName;
while (_nodes.Any(n => n.Name == name))
name = $"{baseName}_{idx++}";
var parentIdx = -1;
if (_tree.SelectedNode?.Tag is SceneNode sel && sel.Name != "root")
parentIdx = _nodes.IndexOf(sel);
else if (_nodes.Count > 0)
parentIdx = 0; // root
var node = new SceneNode
{
Name = name,
ParentIndex = parentIdx,
Mesh = type,
PosY = type == MeshType.PLANE ? -1.0f : 0.0f
};
_nodes.Add(node);
RebuildTree();
SelectNode(node);
SetModified(true);
SetStatus($"添加 {name}");
}
private void DeleteSelected()
{
if (_tree.SelectedNode?.Tag is not SceneNode node || node.Name == "root")
{
MessageBox.Show(this, "不能删除根节点", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
_nodes.Remove(node);
RebuildTree();
SetModified(true);
SetStatus($"删除 {node.Name}");
}
private void RebuildTree()
{
_tree.Nodes.Clear();
var treeMap = new Dictionary<int, TreeNode>();
for (int i = 0; i < _nodes.Count; i++)
{
var n = _nodes[i];
var tn = new TreeNode(n.Name) { Tag = n, ForeColor = Color.White };
treeMap[i] = tn;
if (n.ParentIndex >= 0 && treeMap.TryGetValue(n.ParentIndex, out var parent))
parent.Nodes.Add(tn);
else
_tree.Nodes.Add(tn);
}
_tree.ExpandAll();
}
private void SelectNode(SceneNode node)
{
foreach (TreeNode root in _tree.Nodes)
{
var found = FindNode(root, node);
if (found != null)
{
_tree.SelectedNode = found;
return;
}
}
}
private static TreeNode? FindNode(TreeNode parent, SceneNode node)
{
if (parent.Tag == node) return parent;
foreach (TreeNode c in parent.Nodes)
{
var r = FindNode(c, node);
if (r != null) return r;
}
return null;
}
private void OnNodeSelected()
{
_propertyGrid.SelectedObject = _tree.SelectedNode?.Tag;
if (_tree.SelectedNode?.Tag is SceneNode n)
SetStatus(n.Name);
}
private void OnPropertyChanged()
{
SetModified(true);
_tree.Refresh();
}
private void SetModified(bool modified)
{
_modified = modified;
_btnSave.Enabled = modified;
Text = "Lity3D Engine Scene Editor" + (modified ? " *" : "") + (string.IsNullOrEmpty(_currentPath) ? "" : $" - {_currentPath}");
}
private void SetStatus(string msg)
{
if (_status.Items[0] is ToolStripStatusLabel lbl)
lbl.Text = msg;
}
}
public class DarkToolStripRenderer : ToolStripProfessionalRenderer
{
public DarkToolStripRenderer() : base(new DarkColorTable()) { }
}
public class DarkColorTable : ProfessionalColorTable
{
public override Color ToolStripBorder => Color.FromArgb(60, 60, 80);
public override Color ToolStripGradientBegin => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientEnd => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientMiddle => Color.FromArgb(45, 45, 55);
public override Color ButtonSelectedHighlight => Color.FromArgb(70, 70, 90);
public override Color ButtonSelectedBorder => Color.FromArgb(100, 120, 160);
public override Color ButtonPressedHighlight => Color.FromArgb(60, 80, 120);
public override Color ButtonCheckedHighlight => Color.FromArgb(60, 80, 120);
public override Color MenuBorder => Color.FromArgb(60, 60, 80);
public override Color MenuItemSelected => Color.FromArgb(70, 70, 90);
public override Color MenuItemBorder => Color.FromArgb(100, 120, 160);
}
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Update="Form1.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>
+11
View File
@@ -0,0 +1,11 @@
namespace Lity3DEditor;
static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
+56
View File
@@ -0,0 +1,56 @@
using System.Globalization;
namespace Lity3DEditor;
public static class SceneIO
{
public static List<SceneNode> Load(string path)
{
var nodes = new List<SceneNode>();
using var reader = new StreamReader(path);
string? header = reader.ReadLine();
if (header == null || !header.StartsWith("LITY3D_SCENE"))
throw new InvalidDataException("不是有效的 Lity3D 场景文件");
int lineIdx = 1;
string? line;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line)) continue;
var parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 13) continue;
var node = new SceneNode
{
Name = parts[0],
ParentIndex = int.Parse(parts[1]),
Mesh = Enum.Parse<MeshType>(parts[2], true),
MeshPath = parts[3],
PosX = float.Parse(parts[4], CultureInfo.InvariantCulture),
PosY = float.Parse(parts[5], CultureInfo.InvariantCulture),
PosZ = float.Parse(parts[6], CultureInfo.InvariantCulture),
RotX = float.Parse(parts[7], CultureInfo.InvariantCulture),
RotY = float.Parse(parts[8], CultureInfo.InvariantCulture),
RotZ = float.Parse(parts[9], CultureInfo.InvariantCulture),
ScaleX = float.Parse(parts[10], CultureInfo.InvariantCulture),
ScaleY = float.Parse(parts[11], CultureInfo.InvariantCulture),
ScaleZ = float.Parse(parts[12], CultureInfo.InvariantCulture),
LineIndex = lineIdx
};
nodes.Add(node);
lineIdx++;
}
return nodes;
}
public static void Save(string path, List<SceneNode> nodes)
{
using var writer = new StreamWriter(path);
writer.WriteLine("LITY3D_SCENE 1");
foreach (var node in nodes)
{
int parent = node.ParentIndex;
writer.WriteLine(node.ToSceneLine(parent));
}
}
}
+52
View File
@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.Globalization;
namespace Lity3DEditor;
public enum MeshType
{
[Description("无")]
NONE,
[Description("立方体")]
CUBE,
[Description("球体")]
SPHERE,
[Description("平面")]
PLANE,
[Description("OBJ 模型")]
OBJ
}
public class SceneNode
{
public string Name { get; set; } = "";
public int ParentIndex { get; set; } = -1;
public MeshType Mesh { get; set; } = MeshType.NONE;
public string MeshPath { get; set; } = "-";
public float PosX { get; set; }
public float PosY { get; set; }
public float PosZ { get; set; }
public float RotX { get; set; }
public float RotY { get; set; }
public float RotZ { get; set; }
public float ScaleX { get; set; } = 1.0f;
public float ScaleY { get; set; } = 1.0f;
public float ScaleZ { get; set; } = 1.0f;
[Browsable(false)]
public int LineIndex { get; set; }
public override string ToString() => Name;
public string ToSceneLine(int parentIndex)
{
ParentIndex = parentIndex;
return string.Format(CultureInfo.InvariantCulture,
"{0} {1} {2} {3} {4:F4} {5:F4} {6:F4} {7:F4} {8:F4} {9:F4} {10:F4} {11:F4} {12:F4}",
Name, parentIndex, Mesh, MeshPath,
PosX, PosY, PosZ, RotX, RotY, RotZ, ScaleX, ScaleY, ScaleZ);
}
}
@@ -0,0 +1,70 @@
{
"format": 1,
"restore": {
"C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj": {}
},
"projects": {
"C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"projectName": "editor",
"projectPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"packagesPath": "C:\\Users\\Administrator\\.nuget\\packages\\",
"outputPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\obj\\",
"projectStyle": "PackageReference",
"configFilePaths": [
"C:\\Users\\Administrator\\AppData\\Roaming\\NuGet\\NuGet.Config"
],
"originalTargetFrameworks": [
"net9.0-windows"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
},
"Microsoft.WindowsDesktop.App.WindowsForms": {
"privateAssets": "none"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.315/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Administrator\.nuget\packages\</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.3</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\Administrator\.nuget\packages\" />
</ItemGroup>
</Project>
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]
@@ -0,0 +1,24 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("editor")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0669d102cea79704d7f44edeff1f40db4677933a")]
[assembly: System.Reflection.AssemblyProductAttribute("editor")]
[assembly: System.Reflection.AssemblyTitleAttribute("editor")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows7.0")]
[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
// 由 MSBuild WriteCodeFragment 类生成。
@@ -0,0 +1 @@
eccab4cf931872db70f4fcf49f5ea48daaa37596c654c8ae7292da5835f1d1aa
@@ -0,0 +1,22 @@
is_global = true
build_property.ApplicationManifest =
build_property.StartupObject =
build_property.ApplicationDefaultFont =
build_property.ApplicationHighDpiMode =
build_property.ApplicationUseCompatibleTextRendering =
build_property.ApplicationVisualStyles =
build_property.TargetFramework = net9.0-windows
build_property.TargetPlatformMinVersion = 7.0
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Lity3DEditor
build_property.ProjectDir = C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =
@@ -0,0 +1,10 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.Drawing;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
global using global::System.Windows.Forms;
@@ -0,0 +1 @@
778c6fb97436ff7fada39ea9df7644fb35174eb8848c29619b96adb0a6265bee
@@ -0,0 +1,14 @@
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.exe
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.deps.json
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.runtimeconfig.json
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.dll
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.pdb
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.GeneratedMSBuildEditorConfig.editorconfig
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.AssemblyInfoInputs.cache
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.AssemblyInfo.cs
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.csproj.CoreCompileInputs.cache
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\editor.dll
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\refint\editor.dll
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\editor.pdb
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.genruntimeconfig.cache
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\ref\editor.dll
@@ -0,0 +1 @@
25097d32ce528727f2896c2b3b57b55035840d12dbe72050663b5fc129c5cec5
Binary file not shown.
+75
View File
@@ -0,0 +1,75 @@
{
"version": 3,
"targets": {
"net9.0-windows7.0": {}
},
"libraries": {},
"projectFileDependencyGroups": {
"net9.0-windows7.0": []
},
"packageFolders": {
"C:\\Users\\Administrator\\.nuget\\packages\\": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"projectName": "editor",
"projectPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"packagesPath": "C:\\Users\\Administrator\\.nuget\\packages\\",
"outputPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\obj\\",
"projectStyle": "PackageReference",
"configFilePaths": [
"C:\\Users\\Administrator\\AppData\\Roaming\\NuGet\\NuGet.Config"
],
"originalTargetFrameworks": [
"net9.0-windows"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
},
"Microsoft.WindowsDesktop.App.WindowsForms": {
"privateAssets": "none"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.315/PortableRuntimeIdentifierGraph.json"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"version": 2,
"dgSpecHash": "icz9DlDWgaA=",
"success": true,
"projectFilePath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"expectedPackageFiles": [],
"logs": []
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
// ============================================================================
// mesh.h - 网格 (顶点缓冲 + 索引缓冲)
// ============================================================================
#include <d3d11.h>
#include <stdint.h>
typedef struct {
ID3D11Buffer* vertex_buffer;
ID3D11Buffer* index_buffer;
uint32_t vertex_count;
uint32_t index_count;
uint32_t vertex_stride;
} MeshBuffer;
// 从内存数据创建网格
MeshBuffer* mesh_create(ID3D11Device* device,
const void* vertices, uint32_t vertex_count, uint32_t vertex_stride,
const uint32_t* indices, uint32_t index_count);
// 从 OBJ 文件加载网格 (简化版: 仅顶点+面)
MeshBuffer* mesh_load_obj(ID3D11Device* device, const char* filepath);
// 销毁
void mesh_destroy(MeshBuffer* mesh);
// 绘制
void mesh_draw(ID3D11DeviceContext* ctx, MeshBuffer* mesh);
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <d3d11.h>
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
uint32_t width, height;
} Texture;
Texture* texture_load_bmp(ID3D11Device* dev, const char* path);
Texture* texture_create_white(ID3D11Device* dev, uint32_t w, uint32_t h);
void texture_destroy(Texture* tex);
+192
View File
@@ -0,0 +1,192 @@
#include "asset_manager.h"
#include "../Rendering/mesh.h"
#include "../Rendering/texture.h"
#include "../Rendering/shader.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ASSET_BUCKET_COUNT 256
struct AssetManager {
Asset* buckets[ASSET_BUCKET_COUNT];
uint32_t next_id;
};
static uint32_t hash_str(const char* s) {
uint32_t h = 5381;
while (*s) h = ((h << 5) + h) + (unsigned char)*s++;
return h % ASSET_BUCKET_COUNT;
}
static int str_ends_with(const char* s, const char* suffix) {
size_t ls = strlen(s), lf = strlen(suffix);
return ls >= lf && strcmp(s + ls - lf, suffix) == 0;
}
static Asset* asset_alloc(AssetManager* am, AssetType type, const char* path, void* data, void (*destroy)(void*)) {
Asset* a = (Asset*)calloc(1, sizeof(Asset));
a->id = ++am->next_id;
a->type = type;
a->refcount = 1;
a->owner = am;
strncpy(a->path, path, sizeof(a->path) - 1);
a->data = data;
a->destroy = destroy;
uint32_t h = hash_str(path);
a->next = am->buckets[h];
am->buckets[h] = a;
return a;
}
static void asset_free(AssetManager* am, Asset* a) {
if (!a) return;
uint32_t h = hash_str(a->path);
Asset** cur = &am->buckets[h];
while (*cur) {
if (*cur == a) {
*cur = a->next;
break;
}
cur = &(*cur)->next;
}
if (a->destroy) a->destroy(a->data);
free(a);
}
AssetManager* asset_manager_create(void) {
AssetManager* am = (AssetManager*)calloc(1, sizeof(AssetManager));
return am;
}
void asset_manager_destroy(AssetManager* am) {
if (!am) return;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
Asset* a = am->buckets[i];
while (a) {
Asset* next = a->next;
if (a->destroy) a->destroy(a->data);
free(a);
a = next;
}
}
free(am);
}
Asset* asset_manager_find(AssetManager* am, const char* path) {
if (!am || !path) return NULL;
uint32_t h = hash_str(path);
for (Asset* a = am->buckets[h]; a; a = a->next) {
if (strcmp(a->path, path) == 0) return a;
}
return NULL;
}
Asset* asset_manager_get(AssetManager* am, uint32_t id) {
if (!am || id == 0) return NULL;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
for (Asset* a = am->buckets[i]; a; a = a->next) {
if (a->id == id) return a;
}
}
return NULL;
}
void* asset_data(Asset* a) { return a ? a->data : NULL; }
void asset_acquire(Asset* a) { if (a) a->refcount++; }
void asset_release(Asset* a) {
if (!a) return;
a->refcount--;
if (a->refcount <= 0 && a->owner) {
asset_free(a->owner, a);
}
}
// ------------------------------------------------------------------
// Mesh
// ------------------------------------------------------------------
static void destroy_mesh_asset(void* data) { mesh_destroy((Mesh*)data); }
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Mesh* mesh = NULL;
if (strcmp(path, "cube") == 0) mesh = mesh_create_cube(dev);
else if (strcmp(path, "sphere") == 0) mesh = mesh_create_sphere(dev, 1.0f, 16);
else if (strcmp(path, "plane") == 0) mesh = mesh_create_plane(dev, 10.0f);
else if (str_ends_with(path, ".obj")) mesh = mesh_load_obj(dev, path);
if (!mesh) return NULL;
return asset_alloc(am, ASSET_MESH, path, mesh, destroy_mesh_asset);
}
// ------------------------------------------------------------------
// Texture
// ------------------------------------------------------------------
static void destroy_texture_asset(void* data) { texture_destroy((Texture*)data); }
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Texture* tex = NULL;
if (strcmp(path, "checkerboard") == 0) tex = texture_create_checkerboard(dev, 256, 8);
else tex = texture_load_from_file(dev, path);
if (!tex) return NULL;
return asset_alloc(am, ASSET_TEXTURE, path, tex, destroy_texture_asset);
}
// ------------------------------------------------------------------
// Shader
// ------------------------------------------------------------------
static void destroy_shader_asset(void* data) { shader_destroy((Shader*)data); }
static char* read_file(const char* path, uint32_t* out_len) {
FILE* f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size <= 0) { fclose(f); return NULL; }
char* buf = (char*)malloc((size_t)size + 1);
if (!buf) { fclose(f); return NULL; }
fread(buf, 1, (size_t)size, f);
buf[size] = '\0';
fclose(f);
if (out_len) *out_len = (uint32_t)size;
return buf;
}
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path) {
if (!am || !dev) return NULL;
char key[512];
if (vs_path && ps_path) snprintf(key, sizeof(key), "%s|%s", vs_path, ps_path);
else strcpy(key, "default");
Asset* existing = asset_manager_find(am, key);
if (existing) { asset_acquire(existing); return existing; }
Shader* s = shader_create();
if (vs_path && ps_path) {
uint32_t vs_len = 0, ps_len = 0;
char* vs = read_file(vs_path, &vs_len);
char* ps = read_file(ps_path, &ps_len);
if (!vs || !ps || !shader_compile(s, dev, vs, ps, vs_len, ps_len)) {
shader_destroy(s); free(vs); free(ps); return NULL;
}
free(vs); free(ps);
} else {
if (!shader_compile(s, dev, NULL, NULL, 0, 0)) {
shader_destroy(s); return NULL;
}
}
return asset_alloc(am, ASSET_SHADER, key, s, destroy_shader_asset);
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
#include <d3d11.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AssetManager AssetManager;
typedef struct Asset Asset;
typedef enum {
ASSET_MESH = 0,
ASSET_TEXTURE,
ASSET_SHADER,
ASSET_MATERIAL,
ASSET_COUNT
} AssetType;
struct Asset {
uint32_t id;
AssetType type;
int refcount;
char path[256];
void* data;
void (*destroy)(void* data);
Asset* next;
struct AssetManager* owner;
};
AssetManager* asset_manager_create(void);
void asset_manager_destroy(AssetManager* am);
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path);
Asset* asset_manager_find(AssetManager* am, const char* path);
Asset* asset_manager_get(AssetManager* am, uint32_t id);
void* asset_data(Asset* a);
void asset_acquire(Asset* a);
void asset_release(Asset* a);
#ifdef __cplusplus
}
#endif
+137
View File
@@ -0,0 +1,137 @@
; ============================================================================
; asm_macros.inc - MASM 通用宏定义
; Windows x64 调用约定: RCX, RDX, R8, R9 传参, XMM0-XMM3 浮点
; ============================================================================
; --- 函数帧管理宏 ---
; PROLOGUE: 保存非易失寄存器, 分配栈空间
; 使用: PROLOGUE 栈空间大小
PROLOGUE MACRO stack_size:REQ
push rbx
push rbp
push rdi
push rsi
push r12
push r13
push r14
push r15
sub rsp, stack_size
; 保存浮点参数寄存器 (如果有)
movsd qword ptr [rsp + stack_size - 8], xmm6
movsd qword ptr [rsp + stack_size - 16], xmm7
ENDM
; EPILOGUE: 恢复非易失寄存器, 释放栈空间
EPILOGUE MACRO stack_size:REQ
movsd xmm6, qword ptr [rsp + stack_size - 8]
movsd xmm7, qword ptr [rsp + stack_size - 16]
add rsp, stack_size
pop r15
pop r14
pop r13
pop r12
pop rsi
pop rdi
pop rbp
pop rbx
ret
ENDM
; --- SSE/AVX 常量 ---
.DATA
ALIGN 16
sse_mask_abs DD 07FFFFFFFH, 07FFFFFFFH, 07FFFFFFFH, 07FFFFFFFH ; 绝对值掩码
sse_negate DD 80000000H, 80000000H, 80000000H, 80000000H ; 符号位掩码
sse_one DD 3F800000H, 3F800000H, 3F800000H, 3F800000H ; 1.0f
sse_half DD 3F000000H, 3F000000H, 3F000000H, 3F000000H ; 0.5f
eps_float DD 33D6BF94H ; 1e-6f
.CODE
; --- 工具宏 ---
; LOAD_VEC3: 从内存加载 Vec3 到 XMM 寄存器 (填充 w=1.0)
; ptr: 内存地址, reg: XMM 寄存器
LOAD_VEC3 MACRO ptr, reg
movss xmm0, dword ptr [ptr] ; x
movss xmm1, dword ptr [ptr + 4] ; y
movss xmm2, dword ptr [ptr + 8] ; z
unpcklps xmm0, xmm1 ; {x, y, ?, ?}
movss xmm3, dword ptr [sse_one] ; w = 1.0
unpcklps xmm2, xmm3 ; {z, 1.0, ?, ?}
movlhps xmm0, xmm2 ; {x, y, z, 1.0}
movaps reg, xmm0
ENDM
; STORE_VEC3: 将 XMM 寄存器的前3个float存储到内存
STORE_VEC3 MACRO reg, ptr
movss dword ptr [ptr], xmm0 ; x
shufps xmm0, xmm0, 01010101b
movss dword ptr [ptr + 4], xmm0 ; y
shufps xmm0, xmm0, 10101010b
movss dword ptr [ptr + 8], xmm0 ; z
ENDM
; --- 外部函数声明 ---
externdef vec3_add:PROC
externdef vec3_sub:PROC
externdef vec3_scale:PROC
externdef vec3_dot:PROC
externdef vec3_cross:PROC
externdef vec3_length:PROC
externdef vec3_normalize:PROC
externdef vec3_lerp:PROC
externdef vec3_distance:PROC
externdef vec4_add:PROC
externdef vec4_sub:PROC
externdef vec4_mul:PROC
externdef vec4_dot:PROC
externdef vec4_length:PROC
externdef vec4_normalize:PROC
externdef mat4_identity:PROC
externdef mat4_multiply:PROC
externdef mat4_transpose:PROC
externdef mat4_invert:PROC
externdef mat4_translate:PROC
externdef mat4_scale_f:PROC
externdef mat4_rotate_x:PROC
externdef mat4_rotate_y:PROC
externdef mat4_rotate_z:PROC
externdef mat4_perspective:PROC
externdef mat4_orthographic:PROC
externdef mat4_lookat:PROC
externdef quat_multiply:PROC
externdef quat_conjugate:PROC
externdef quat_normalize:PROC
externdef quat_rotate_vec3:PROC
externdef quat_slerp:PROC
externdef quat_to_matrix:PROC
externdef quat_from_axis_angle:PROC
externdef quat_from_euler:PROC
externdef mem_aligned_alloc:PROC
externdef mem_aligned_free:PROC
externdef mem_aligned_realloc:PROC
externdef fast_memcpy:PROC
externdef fast_memset:PROC
externdef fast_strlen:PROC
externdef fast_strcmp:PROC
externdef prof_rdtsc:PROC
externdef prof_cycles_to_ms:PROC
externdef prof_get_freq:PROC
; C 数学运行时
externdef cosf:PROC
externdef sinf:PROC
externdef tanf:PROC
; Windows API
externdef Sleep:PROC
externdef QueryPerformanceFrequency:PROC
externdef QueryPerformanceCounter:PROC
File diff suppressed because it is too large. Load diff
+79
View File
@@ -0,0 +1,79 @@
#pragma once
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <Windows.h>
#if !defined(MATH_ASM_AVAILABLE) || MATH_ASM_AVAILABLE == 0
typedef struct { float x, y, z; } Vec3;
typedef struct { float x, y, z, w; } Vec4;
typedef struct { float m[4][4]; } Mat4;
typedef struct { float x, y, z, w; } Quat;
static inline void vec3_add(const Vec3* a, const Vec3* b, Vec3* o) { o->x=a->x+b->x; o->y=a->y+b->y; o->z=a->z+b->z; }
static inline void vec3_sub(const Vec3* a, const Vec3* b, Vec3* o) { o->x=a->x-b->x; o->y=a->y-b->y; o->z=a->z-b->z; }
static inline void vec3_scale(const Vec3* a, float s, Vec3* o) { o->x=a->x*s; o->y=a->y*s; o->z=a->z*s; }
static inline float vec3_dot(const Vec3* a, const Vec3* b) { return a->x*b->x+a->y*b->y+a->z*b->z; }
static inline void vec3_cross(const Vec3* a, const Vec3* b, Vec3* o) { o->x=a->y*b->z-a->z*b->y; o->y=a->z*b->x-a->x*b->z; o->z=a->x*b->y-a->y*b->x; }
static inline float vec3_length(const Vec3* a) { return sqrtf(a->x*a->x+a->y*a->y+a->z*a->z); }
static inline void vec3_normalize(const Vec3* a, Vec3* o) { float l=vec3_length(a); if(l<1e-6f){o->x=o->y=o->z=0;return;} float i=1.0f/l; o->x=a->x*i; o->y=a->y*i; o->z=a->z*i; }
static inline void vec3_lerp(const Vec3* a, const Vec3* b, float t, Vec3* o) { o->x=a->x+(b->x-a->x)*t; o->y=a->y+(b->y-a->y)*t; o->z=a->z+(b->z-a->z)*t; }
static inline float vec3_distance(const Vec3* a, const Vec3* b) { Vec3 d; vec3_sub(a,b,&d); return vec3_length(&d); }
static inline void mat4_identity(Mat4* o) { memset(o,0,sizeof(Mat4)); o->m[0][0]=o->m[1][1]=o->m[2][2]=o->m[3][3]=1.0f; }
static inline void mat4_multiply(const Mat4* a, const Mat4* b, Mat4* o) { Mat4 r; int i,j,k; for(i=0;i<4;i++) for(j=0;j<4;j++) { r.m[i][j]=0; for(k=0;k<4;k++) r.m[i][j]+=a->m[i][k]*b->m[k][j]; } *o=r; }
static inline void mat4_translate(float x, float y, float z, Mat4* o) { mat4_identity(o); o->m[3][0]=x; o->m[3][1]=y; o->m[3][2]=z; }
static inline void mat4_rotate_y(float angle, Mat4* o) { float c=cosf(angle),s=sinf(angle); mat4_identity(o); o->m[0][0]=c; o->m[0][2]=-s; o->m[2][0]=s; o->m[2][2]=c; }
static inline void mat4_perspective(float fov, float aspect, float nearP, float farP, Mat4* o) { float t=tanf(fov*0.5f); memset(o,0,sizeof(Mat4)); o->m[0][0]=1.0f/(aspect*t); o->m[1][1]=1.0f/t; o->m[2][2]=farP/(nearP-farP); o->m[2][3]=-1.0f; o->m[3][2]=(nearP*farP)/(nearP-farP); }
static inline void mat4_orthographic(float w, float h, float nearP, float farP, Mat4* o) { memset(o,0,sizeof(Mat4)); o->m[0][0]=2.0f/w; o->m[1][1]=2.0f/h; o->m[2][2]=1.0f/(nearP-farP); o->m[3][2]=nearP/(nearP-farP); o->m[3][3]=1.0f; }
static inline void mat4_lookat(const Vec3* eye, const Vec3* target, const Vec3* up, Mat4* o) { Vec3 f,r,u; vec3_sub(target,eye,&f); vec3_normalize(&f,&f); vec3_cross(up,&f,&r); vec3_normalize(&r,&r); vec3_cross(&f,&r,&u); mat4_identity(o); o->m[0][0]=r.x; o->m[0][1]=u.x; o->m[0][2]=-f.x; o->m[1][0]=r.y; o->m[1][1]=u.y; o->m[1][2]=-f.y; o->m[2][0]=r.z; o->m[2][1]=u.z; o->m[2][2]=-f.z; o->m[3][0]=-vec3_dot(&r,eye); o->m[3][1]=-vec3_dot(&u,eye); o->m[3][2]=vec3_dot(&f,eye); }
static inline void mat4_transpose(const Mat4* in, Mat4* out) { for (int i=0;i<4;i++) for (int j=0;j<4;j++) out->m[i][j]=in->m[j][i]; }
static inline void quat_conjugate_raw(const Quat* q, Quat* o) { o->x=-q->x; o->y=-q->y; o->z=-q->z; o->w=q->w; }
static inline void quat_multiply(const Quat* a, const Quat* b, Quat* o) { o->x=a->w*b->x+a->x*b->w+a->y*b->z-a->z*b->y; o->y=a->w*b->y-a->x*b->z+a->y*b->w+a->z*b->x; o->z=a->w*b->z+a->x*b->y-a->y*b->x+a->z*b->w; o->w=a->w*b->w-a->x*b->x-a->y*b->y-a->z*b->z; }
static inline void quat_normalize(const Quat* q, Quat* o) { float l=sqrtf(q->x*q->x+q->y*q->y+q->z*q->z+q->w*q->w); if(l<1e-6f){*o=*q;return;} float i=1.0f/l; o->x=q->x*i; o->y=q->y*i; o->z=q->z*i; o->w=q->w*i; }
static inline void quat_rotate_vec3(const Quat* q, const Vec3* v, Vec3* o) { Quat vq={v->x,v->y,v->z,0}; Quat qinv; quat_conjugate_raw(q,&qinv); Quat tmp; quat_multiply(q,&vq,&tmp); Quat result; quat_multiply(&tmp,&qinv,&result); o->x=result.x; o->y=result.y; o->z=result.z; }
static inline void quat_to_matrix(const Quat* q, Mat4* o) { float xx=q->x*q->x,yy=q->y*q->y,zz=q->z*q->z,xy=q->x*q->y,xz=q->x*q->z,yz=q->y*q->z,wx=q->w*q->x,wy=q->w*q->y,wz=q->w*q->z; mat4_identity(o); o->m[0][0]=1-2*(yy+zz); o->m[0][1]=2*(xy+wz); o->m[0][2]=2*(xz-wy); o->m[1][0]=2*(xy-wz); o->m[1][1]=1-2*(xx+zz); o->m[1][2]=2*(yz+wx); o->m[2][0]=2*(xz+wy); o->m[2][1]=2*(yz-wx); o->m[2][2]=1-2*(xx+yy); }
static inline void quat_from_euler(float x, float y, float z, Quat* o) {
float cx = cosf(x * 0.5f), sx = sinf(x * 0.5f);
float cy = cosf(y * 0.5f), sy = sinf(y * 0.5f);
float cz = cosf(z * 0.5f), sz = sinf(z * 0.5f);
o->w = cx * cy * cz + sx * sy * sz;
o->x = sx * cy * cz - cx * sy * sz;
o->y = cx * sy * cz + sx * cy * sz;
o->z = cx * cy * sz - sx * sy * cz;
}
static inline void quat_to_euler(const Quat* q, float* x, float* y, float* z) {
float sinr_cosp = 2.0f * (q->w * q->x + q->y * q->z);
float cosr_cosp = 1.0f - 2.0f * (q->x * q->x + q->y * q->y);
*x = atan2f(sinr_cosp, cosr_cosp);
float sinp = 2.0f * (q->w * q->y - q->z * q->x);
if (fabsf(sinp) >= 1.0f) *y = copysignf(1.57079633f, sinp);
else *y = asinf(sinp);
float siny_cosp = 2.0f * (q->w * q->z + q->x * q->y);
float cosy_cosp = 1.0f - 2.0f * (q->y * q->y + q->z * q->z);
*z = atan2f(siny_cosp, cosy_cosp);
}
static inline void* mem_aligned_alloc(uint64_t size, uint64_t alignment) { void* p; p = _aligned_malloc(size, alignment); return p; }
static inline void mem_aligned_free(void* ptr) { _aligned_free(ptr); }
static inline void fast_memcpy(void* dst, const void* src, uint64_t count) { memcpy(dst, src, (size_t)count); }
static inline void fast_memset(void* dst, int v, uint64_t count) { memset(dst, v, (size_t)count); }
static inline uint64_t fast_strlen(const char* s) { return strlen(s); }
static inline uint64_t prof_rdtsc(void) { LARGE_INTEGER f,t; QueryPerformanceFrequency(&f); QueryPerformanceCounter(&t); return (uint64_t)(t.QuadPart * 1000000000ULL / f.QuadPart); }
static inline double prof_cycles_to_ms(uint64_t c) { return (double)c / 1000000.0; }
static inline uint64_t prof_get_freq(void) { LARGE_INTEGER f; QueryPerformanceFrequency(&f); return (uint64_t)f.QuadPart; }
typedef struct { uint64_t start; uint64_t end; double elapsed_ms; } ProfTimer;
static inline void prof_timer_start(ProfTimer* t) { t->start = prof_rdtsc(); }
static inline void prof_timer_stop(ProfTimer* t) { t->end = prof_rdtsc(); t->elapsed_ms = prof_cycles_to_ms(t->end - t->start); }
#endif
+180
View File
@@ -0,0 +1,180 @@
; ============================================================================
; memory.asm - 对齐内存分配器 (x86-64 MASM)
; 支持 16/32/64 字节对齐 (SIMD 需要)
; ============================================================================
include asm_macros.inc
.DATA
ALIGN 16
_alloc_header_size EQU 32 ; 分配头大小 (包含对齐偏移和原始指针)
.CODE
; ============================================================================
; 对齐内存分配
; ============================================================================
; void* mem_aligned_alloc(uint64_t size, uint64_t alignment)
; RCX = size, RDX = alignment
; 返回: RAX = 对齐指针
mem_aligned_alloc PROC
push rbx
push rdi
push rsi
sub rsp, 40 ; 保留栈空间
mov rbx, rcx ; rbx = size
mov rdi, rdx ; rdi = alignment
; 验证 alignment 是 2 的幂
mov rax, rdi
dec rax
test rdi, rax
jnz alloc_invalid_alignment
; 确保 alignment >= 16
cmp rdi, 16
jae alloc_alignment_ok
mov rdi, 16
alloc_alignment_ok:
; 需要的总大小 = size + alignment + header
mov rax, rbx
add rax, rdi ; + alignment
add rax, _alloc_header_size ; + header
mov rsi, rax ; rsi = total size
; 调用 malloc
mov rcx, rsi
call malloc
test rax, rax
jz alloc_failed
; rax = 原始指针
; 计算对齐后的地址
mov rcx, rax
add rcx, _alloc_header_size ; 跳过 header
; 对齐
mov rdx, rdi
dec rdx
not rdx
add rcx, rdx ; 加上 alignment-1
and rcx, rdx ; 对齐到 alignment
; rcx = 对齐后的指针
; 在对齐指针前存储原始指针
mov [rcx - 8], rax ; 存储原始 malloc 指针
mov [rcx - 16], rbx ; 存储请求的 size
mov [rcx - 24], rdi ; 存储 alignment
mov [rcx - 32], rsi ; 存储 total size
mov rax, rcx ; 返回对齐指针
jmp alloc_done
alloc_invalid_alignment:
xor rax, rax ; 返回 NULL
jmp alloc_done
alloc_failed:
xor rax, rax
alloc_done:
add rsp, 40
pop rsi
pop rdi
pop rbx
ret
mem_aligned_alloc ENDP
; void mem_aligned_free(void* ptr)
; RCX = ptr (对齐指针)
mem_aligned_free PROC
test rcx, rcx
jz free_done
; 获取原始 malloc 指针
mov rax, [rcx - 8]
test rax, rax
jz free_done
; 调用 free
mov rcx, rax
call free
free_done:
ret
mem_aligned_free ENDP
; void* mem_aligned_realloc(void* ptr, uint64_t size, uint64_t alignment)
; RCX = ptr, RDX = size, R8 = alignment
; 返回: RAX = 新对齐指针
mem_aligned_realloc PROC
push rbx
push rdi
push rsi
push r12
sub rsp, 56 ; shadow + 3 saved qwords
mov rbx, rdx ; rbx = new size
mov rdi, r8 ; rdi = alignment
; 如果 ptr 为 NULL, 直接分配
test rcx, rcx
jz realloc_alloc
; 保存旧信息
mov [rsp + 32], rcx ; 旧对齐指针(即用户数据起始)
mov rax, [rcx - 8]
mov [rsp + 24], rax ; 旧原始 malloc 指针
mov rax, [rcx - 16]
mov [rsp + 16], rax ; 旧 size
; 分配新内存
mov rcx, rbx
mov rdx, rdi
call mem_aligned_alloc
test rax, rax
jz realloc_failed
mov rsi, rax ; rsi = 新对齐指针
; 复制旧数据 (取较小值)
mov rdx, [rsp + 16] ; 旧 size
cmp rdx, rbx
jbe realloc_copy_ok
mov rdx, rbx
realloc_copy_ok:
; fast_memcpy(dst, src, count): rcx=dst, rdx=src, r8=count
mov rcx, rsi
mov r8, rdx
mov rdx, [rsp + 32] ; 旧对齐指针
call fast_memcpy
; 释放旧内存(使用原始 malloc 指针)
mov rcx, [rsp + 24]
call mem_aligned_free
mov rax, rsi ; 返回新指针
jmp realloc_done
realloc_alloc:
mov rcx, rbx
mov rdx, rdi
call mem_aligned_alloc
jmp realloc_done
realloc_failed:
xor rax, rax
realloc_done:
add rsp, 56
pop r12
pop rsi
pop rdi
pop rbx
ret
mem_aligned_realloc ENDP
END
+148
View File
@@ -0,0 +1,148 @@
; ============================================================================
; profiling.asm - 性能分析工具 (x86-64 MASM)
; 使用 RDTSC/RDTSCP 进行高精度计时
; ============================================================================
include asm_macros.inc
.DATA
ALIGN 8
cpu_freq DQ 0 ; CPU 频率 (Hz)
cpu_freq_init DB 0 ; 是否已初始化
.CODE
; ============================================================================
; RDTSC 计时
; ============================================================================
; uint64_t prof_rdtsc(void) -> RAX
; 返回: TSC (Time Stamp Counter) 值
prof_rdtsc PROC
; 使用 RDTSCP 获取有序的 TSC
rdtscp
; EDX:EAX = TSC
shl rdx, 32
or rax, rdx
ret
prof_rdtsc ENDP
; uint64_t prof_get_freq(void) -> RAX
; 获取 CPU 频率 (Hz), 使用 QPC 校准 TSC
prof_get_freq PROC
; 检查是否已初始化
cmp byte ptr [cpu_freq_init], 0
jnz prof_freq_return
push rbx
push rdi
push rsi
sub rsp, 40
; 获取 QPC 频率
lea rcx, [rsp + 32]
call QueryPerformanceFrequency
; 获取起始 QPC 和 TSC
lea rcx, [rsp + 24]
call QueryPerformanceCounter
rdtscp
shl rdx, 32
or rax, rdx
mov rbx, rax ; rbx = 起始 TSC
; 等待约 10ms
mov rcx, 10 ; 10 毫秒
call Sleep
; 获取结束 QPC 和 TSC
lea rcx, [rsp + 16]
call QueryPerformanceCounter
rdtscp
shl rdx, 32
or rax, rdx
mov rdi, rax ; rdi = 结束 TSC
; qpc_diff = end_qpc - start_qpc
mov rax, [rsp + 16]
sub rax, [rsp + 24]
mov rsi, rax ; rsi = qpc_diff
; tsc_diff = end_tsc - start_tsc
mov rcx, rdi
sub rcx, rbx ; rcx = tsc_diff
; cpu_freq = tsc_diff * qpc_freq / qpc_diff
mov rax, rcx ; rax = tsc_diff
mov rdx, [rsp + 32] ; rdx = qpc_freq
mul rdx ; rdx:rax = tsc_diff * qpc_freq
div rsi ; rax = freq (Hz)
mov [cpu_freq], rax
mov byte ptr [cpu_freq_init], 1
add rsp, 40
pop rsi
pop rdi
pop rbx
prof_freq_return:
mov rax, [cpu_freq]
ret
prof_get_freq ENDP
; double prof_cycles_to_ms(uint64_t cycles) -> XMM0
; 将 TSC cycles 转换为毫秒: ms = cycles * 1000 / freq
prof_cycles_to_ms PROC
mov rdx, rcx ; 保存 cycles
call prof_get_freq ; rax = CPU freq (Hz)
cvtsi2sd xmm0, rdx ; xmm0 = cycles (double)
mov rcx, 1000
cvtsi2sd xmm1, rcx ; xmm1 = 1000.0
mulsd xmm0, xmm1 ; cycles * 1000
cvtsi2sd xmm1, rax ; xmm1 = freq
divsd xmm0, xmm1 ; ms
ret
prof_cycles_to_ms ENDP
; ============================================================================
; 内联性能宏 (在 C 代码中使用)
; ============================================================================
; 获取当前时间戳 (直接 RDTSC, 无函数调用开销)
; 在 C 中使用: __rdtsc() 内联函数
; 高精度计时器结构
; struct ProfTimer {
; uint64_t start;
; uint64_t end;
; double elapsed_ms;
; };
; void prof_timer_start(ProfTimer* timer)
prof_timer_start PROC
rdtscp
shl rdx, 32
or rax, rdx
mov [rcx], rax ; timer->start = tsc
ret
prof_timer_start ENDP
; void prof_timer_stop(ProfTimer* timer)
prof_timer_stop PROC
rdtscp
shl rdx, 32
or rax, rdx
mov [rcx + 8], rax ; timer->end = tsc
; 计算 elapsed_ms
mov rax, [rcx + 8]
sub rax, [rcx] ; cycles
; 转换为 ms
call prof_cycles_to_ms
movsd qword ptr [rcx + 16], xmm0 ; timer->elapsed_ms
ret
prof_timer_stop ENDP
END
+304
View File
@@ -0,0 +1,304 @@
; ============================================================================
; string_ops.asm - 快速字符串/内存操作 (x86-64 MASM)
; 使用 SSE4.1 加速 memcpy/memset/strlen
; ============================================================================
include asm_macros.inc
.CODE
; ============================================================================
; 内存操作
; ============================================================================
; void fast_memcpy(void* dst, const void* src, uint64_t count)
; RCX = dst, RDX = src, R8 = count
fast_memcpy PROC
mov rax, rcx ; rax = dst
mov r9, r8 ; r9 = count
; 小于 16 字节: 逐字节复制
cmp r9, 16
jb memcpy_small
; 16-32 字节: 两次 128-bit 加载
cmp r9, 32
jb memcpy_16_32
; 32-64 字节: 四次 128-bit 加载
cmp r9, 64
jb memcpy_32_64
; >= 64 字节: SSE 批量复制
; 确保 16 字节对齐
mov r10, rcx
and r10, 0Fh ; 检查 dst 对齐
jnz memcpy_unaligned
memcpy_aligned_loop:
cmp r9, 64
jb memcpy_tail
; 每次复制 64 字节 (4x128-bit)
movaps xmm0, xmmword ptr [rdx]
movaps xmm1, xmmword ptr [rdx + 16]
movaps xmm2, xmmword ptr [rdx + 32]
movaps xmm3, xmmword ptr [rdx + 48]
movaps xmmword ptr [rcx], xmm0
movaps xmmword ptr [rcx + 16], xmm1
movaps xmmword ptr [rcx + 32], xmm2
movaps xmmword ptr [rcx + 48], xmm3
add rcx, 64
add rdx, 64
sub r9, 64
jmp memcpy_aligned_loop
memcpy_tail:
; 处理剩余 0-63 字节
test r9, 32
jz memcpy_tail_16
movaps xmm0, xmmword ptr [rdx]
movaps xmm1, xmmword ptr [rdx + 16]
movaps xmmword ptr [rcx], xmm0
movaps xmmword ptr [rcx + 16], xmm1
add rcx, 32
add rdx, 32
sub r9, 32
memcpy_tail_16:
test r9, 16
jz memcpy_tail_8
movaps xmm0, xmmword ptr [rdx]
movaps xmmword ptr [rcx], xmm0
memcpy_tail_8:
test r9, 8
jz memcpy_tail_4
mov r10, qword ptr [rdx]
mov qword ptr [rcx], r10
memcpy_tail_4:
test r9, 4
jz memcpy_tail_2
mov r10d, dword ptr [rdx]
mov dword ptr [rcx], r10d
memcpy_tail_2:
test r9, 2
jz memcpy_tail_1
mov r10w, word ptr [rdx]
mov word ptr [rcx], r10w
memcpy_tail_1:
test r9, 1
jz memcpy_done
mov r10b, byte ptr [rdx]
mov byte ptr [rcx], r10b
jmp memcpy_done
memcpy_unaligned:
; 非对齐时使用 movups
movups xmm0, xmmword ptr [rdx]
movups xmmword ptr [rcx], xmm0
add rcx, 16
add rdx, 16
sub r9, 16
jmp memcpy_aligned_loop
memcpy_32_64:
mov r10, qword ptr [rdx]
mov qword ptr [rcx], r10
mov r10, qword ptr [rdx + 8]
mov qword ptr [rcx + 8], r10
mov r10, qword ptr [rdx + 16]
mov qword ptr [rcx + 16], r10
mov r10, qword ptr [rdx + 24]
mov qword ptr [rcx + 24], r10
sub r9, 32
add rdx, 32
add rcx, 32
jmp memcpy_small
memcpy_16_32:
mov r10, qword ptr [rdx]
mov qword ptr [rcx], r10
mov r10, qword ptr [rdx + 8]
mov qword ptr [rcx + 8], r10
test r9, 8
jz memcpy_16_32_done
mov r10, qword ptr [rdx + 16]
mov qword ptr [rcx + 16], r10
memcpy_16_32_done:
jmp memcpy_done
memcpy_small:
test r9, r9
jz memcpy_done
mov r10, r9
and r10, 7 ; r10 = count % 8
shr r9, 3 ; r9 = count / 8
; 复制完整 8 字节
memcpy_small_loop:
test r9, r9
jz memcpy_small_tail
mov r11, qword ptr [rdx]
mov qword ptr [rcx], r11
add rdx, 8
add rcx, 8
dec r9
jmp memcpy_small_loop
memcpy_small_tail:
; 复制剩余字节
test r10, r10
jz memcpy_done
mov r11b, byte ptr [rdx]
mov byte ptr [rcx], r11b
inc rdx
inc rcx
dec r10
jmp memcpy_small_tail
memcpy_done:
mov rax, rax ; 返回 dst
ret
fast_memcpy ENDP
; void fast_memset(void* dst, int value, uint64_t count)
; RCX = dst, EDX = value, R8 = count
fast_memset PROC
mov rax, rcx
mov r9, r8
; 将 value 扩展到所有字节
movsx rdx, dl ; 符号扩展
mov rcx, rdx
shl rcx, 8
or rdx, rcx
mov rcx, rdx
shl rcx, 16
or rdx, rcx ; rdx = value 重复4次
mov rcx, rdx
shl rcx, 32
or rdx, rcx ; rdx = value 重复8次
; 复制到 XMM 寄存器
vmovq xmm0, rdx
vinsertf128 ymm0, ymm0, xmm0, 1 ; 256-bit 广播
; >= 32 字节: AVX 批量
cmp r9, 32
jb memset_small
memset_avx_loop:
cmp r9, 32
jb memset_tail
vmovdqu ymmword ptr [rax], ymm0
add rax, 32
sub r9, 32
jmp memset_avx_loop
memset_tail:
; 处理剩余 0-31 字节
test r9, 16
jz memset_tail_8
movups xmmword ptr [rax], xmm0
add rax, 16
sub r9, 16
memset_tail_8:
test r9, 8
jz memset_tail_4
mov qword ptr [rax], rdx
add rax, 8
memset_tail_4:
test r9, 4
jz memset_tail_2
mov dword ptr [rax], edx
add rax, 4
memset_tail_2:
test r9, 2
jz memset_tail_1
mov word ptr [rax], dx
add rax, 2
memset_tail_1:
test r9, 1
jz memset_done
mov byte ptr [rax], dl
jmp memset_done
memset_small:
test r9, r9
jz memset_done
mov byte ptr [rax], dl
inc rax
dec r9
jmp memset_small
memset_done:
vzeroupper
ret
fast_memset ENDP
; ============================================================================
; 字符串操作
; ============================================================================
; uint64_t fast_strlen(const char* str) -> RAX
; 使用 SSE4.2 PCMPESTRI 搜索 null terminator
fast_strlen PROC
mov rdx, rcx ; rdx = original str
pxor xmm0, xmm0 ; 搜索 0
strlen_sse_loop:
mov rax, rcx ; rax = 当前 16 字节块起始
movdqu xmm1, xmmword ptr [rcx]
pcmpestri xmm0, xmm1, 00001000b ; 搜索 0, CF=1 表示找到, ECX=偏移
jc strlen_found
add rcx, 16
jmp strlen_sse_loop
strlen_found:
; rax = 块起始, rcx (ecx) = 块内偏移
add rax, rcx
sub rax, rdx
ret
fast_strlen ENDP
; int fast_strcmp(const char* a, const char* b) -> EAX
; 返回: 0=相等, <0=a<b, >0=a>b
fast_strcmp PROC
xor rax, rax
strcmp_loop:
movzx r8d, byte ptr [rcx + rax]
movzx r9d, byte ptr [rdx + rax]
cmp r8b, r9b
jne strcmp_diff
test r8b, r8b
jz strcmp_equal
inc rax
jmp strcmp_loop
strcmp_diff:
mov eax, r8d
sub eax, r9d
ret
strcmp_equal:
xor eax, eax
ret
fast_strcmp ENDP
END
+30
View File
@@ -0,0 +1,30 @@
#pragma once
// ============================================================================
// mesh.h - 网格 (顶点缓冲 + 索引缓冲)
// ============================================================================
#include <d3d11.h>
#include <stdint.h>
typedef struct {
ID3D11Buffer* vertex_buffer;
ID3D11Buffer* index_buffer;
uint32_t vertex_count;
uint32_t index_count;
uint32_t vertex_stride;
} MeshBuffer;
// 从内存数据创建网格
MeshBuffer* mesh_create(ID3D11Device* device,
const void* vertices, uint32_t vertex_count, uint32_t vertex_stride,
const uint32_t* indices, uint32_t index_count);
// 从 OBJ 文件加载网格 (简化版: 仅顶点+面)
MeshBuffer* mesh_load_obj(ID3D11Device* device, const char* filepath);
// 销毁
void mesh_destroy(MeshBuffer* mesh);
// 绘制
void mesh_draw(ID3D11DeviceContext* ctx, MeshBuffer* mesh);
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <d3d11.h>
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
uint32_t width, height;
} Texture;
Texture* texture_load_bmp(ID3D11Device* dev, const char* path);
Texture* texture_create_white(ID3D11Device* dev, uint32_t w, uint32_t h);
void texture_destroy(Texture* tex);
+277
View File
@@ -0,0 +1,277 @@
#include "audio.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "xaudio2.lib")
// MinGW 没有 xaudio2.lib,通过系统 DLL 动态加载 XAudio2Create
typedef HRESULT (WINAPI *XAudio2CreateFunc)(IXAudio2** ppxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor);
static HRESULT engine_xaudio2_create(IXAudio2** ppxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor) {
static HMODULE mod = NULL;
static XAudio2CreateFunc func = NULL;
if (!func) {
mod = LoadLibraryA("xaudio2_9.dll");
if (!mod) mod = LoadLibraryA("xaudio2_8.dll");
if (!mod) mod = LoadLibraryA("xaudio2_7.dll");
if (mod) func = (XAudio2CreateFunc)GetProcAddress(mod, "XAudio2Create");
}
if (!func) return E_FAIL;
return func(ppxaudio2, flags, processor);
}
typedef struct {
WAVEFORMATEX format;
uint8_t* data;
uint32_t size;
} WavData;
static int wav_load(const uint8_t* file_data, uint32_t file_size, WavData* out) {
memset(out, 0, sizeof(*out));
if (file_size < 12) return 0;
if (memcmp(file_data, "RIFF", 4) != 0) return 0;
if (memcmp(file_data + 8, "WAVE", 4) != 0) return 0;
uint32_t offset = 12;
int has_fmt = 0;
int has_data = 0;
while (offset + 8 <= file_size) {
const uint8_t* chunk_id = file_data + offset;
uint32_t chunk_size = *(const uint32_t*)(file_data + offset + 4);
const uint8_t* chunk_data = file_data + offset + 8;
if (offset + 8 + chunk_size > file_size) return 0;
if (memcmp(chunk_id, "fmt ", 4) == 0 && chunk_size >= 16) {
uint32_t copy_size = chunk_size >= sizeof(WAVEFORMATEX) ? sizeof(WAVEFORMATEX) : chunk_size;
memcpy(&out->format, chunk_data, copy_size);
if (out->format.wFormatTag != WAVE_FORMAT_PCM && out->format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT) {
return 0;
}
if (out->format.cbSize != 0) {
out->format.cbSize = 0;
}
has_fmt = 1;
} else if (memcmp(chunk_id, "data", 4) == 0) {
out->data = (uint8_t*)chunk_data;
out->size = chunk_size;
has_data = 1;
}
offset += 8 + chunk_size;
if (chunk_size & 1) offset += 1; // 对齐到偶数字节
}
return has_fmt && has_data;
}
AudioSystem* audio_create(void) {
CoInitializeEx(NULL, COINIT_MULTITHREADED);
AudioSystem* audio = (AudioSystem*)calloc(1, sizeof(AudioSystem));
if (!audio) return NULL;
HRESULT hr = engine_xaudio2_create(&audio->xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (FAILED(hr) || !audio->xaudio2) {
free(audio);
return NULL;
}
hr = audio->xaudio2->lpVtbl->CreateMasteringVoice(
audio->xaudio2,
&audio->mastering_voice,
XAUDIO2_DEFAULT_CHANNELS,
XAUDIO2_DEFAULT_SAMPLERATE,
0,
NULL,
NULL,
AudioCategory_GameEffects);
if (FAILED(hr)) {
audio->xaudio2->lpVtbl->Release(audio->xaudio2);
free(audio);
return NULL;
}
return audio;
}
void audio_destroy(AudioSystem* audio) {
if (!audio) return;
if (audio->mastering_voice) {
audio->mastering_voice->lpVtbl->DestroyVoice(audio->mastering_voice);
}
if (audio->xaudio2) {
audio->xaudio2->lpVtbl->Release(audio->xaudio2);
}
free(audio);
CoUninitialize();
}
AudioSource* audio_source_create(AudioSystem* audio) {
if (!audio) return NULL;
AudioSource* source = (AudioSource*)calloc(1, sizeof(AudioSource));
if (!source) return NULL;
source->system = audio;
source->volume = 1.0f;
source->pitch = 1.0f;
source->loop = 0;
source->state = AUDIO_STOPPED;
return source;
}
void audio_source_destroy(AudioSource* source) {
if (!source) return;
if (source->source) {
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->DestroyVoice(source->source);
}
free(source->audio_data);
free(source);
}
static int audio_source_load_data(AudioSource* source, const WavData* wav) {
if (!source || !source->system || !source->system->xaudio2) return 0;
// 释放旧的语音与数据
if (source->source) {
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->DestroyVoice(source->source);
source->source = NULL;
}
free(source->audio_data);
source->audio_data = NULL;
source->audio_size = 0;
source->audio_data = (uint8_t*)malloc(wav->size);
if (!source->audio_data) return 0;
memcpy(source->audio_data, wav->data, wav->size);
source->audio_size = wav->size;
HRESULT hr = source->system->xaudio2->lpVtbl->CreateSourceVoice(
source->system->xaudio2,
&source->source,
&wav->format,
0,
XAUDIO2_DEFAULT_FREQ_RATIO,
NULL,
NULL,
NULL);
if (FAILED(hr)) {
free(source->audio_data);
source->audio_data = NULL;
source->audio_size = 0;
return 0;
}
return 1;
}
int audio_load_wav(AudioSource* source, const char* path) {
if (!source || !path) return 0;
FILE* f = fopen(path, "rb");
if (!f) return 0;
fseek(f, 0, SEEK_END);
long file_size = ftell(f);
fseek(f, 0, SEEK_SET);
if (file_size <= 0) {
fclose(f);
return 0;
}
uint8_t* file_data = (uint8_t*)malloc((size_t)file_size);
if (!file_data) {
fclose(f);
return 0;
}
size_t read = fread(file_data, 1, (size_t)file_size, f);
fclose(f);
if (read != (size_t)file_size) {
free(file_data);
return 0;
}
WavData wav;
int ok = wav_load(file_data, (uint32_t)file_size, &wav);
if (ok) {
ok = audio_source_load_data(source, &wav);
}
free(file_data);
return ok;
}
int audio_load_wav_memory(AudioSource* source, const uint8_t* data, uint32_t size) {
if (!source || !data || size == 0) return 0;
WavData wav;
int ok = wav_load(data, size, &wav);
if (ok) {
ok = audio_source_load_data(source, &wav);
}
return ok;
}
void audio_source_play(AudioSource* source) {
if (!source || !source->source) return;
XAUDIO2_BUFFER buffer = {0};
buffer.AudioBytes = source->audio_size;
buffer.pAudioData = source->audio_data;
buffer.Flags = XAUDIO2_END_OF_STREAM;
if (source->loop) {
buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
}
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->FlushSourceBuffers(source->source);
source->source->lpVtbl->SubmitSourceBuffer(source->source, &buffer, NULL);
source->source->lpVtbl->Start(source->source, 0, XAUDIO2_COMMIT_NOW);
source->state = AUDIO_PLAYING;
}
void audio_source_stop(AudioSource* source) {
if (!source || !source->source) return;
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->FlushSourceBuffers(source->source);
source->state = AUDIO_STOPPED;
}
void audio_source_pause(AudioSource* source) {
if (!source || !source->source) return;
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->state = AUDIO_PAUSED;
}
void audio_source_resume(AudioSource* source) {
if (!source || !source->source) return;
source->source->lpVtbl->Start(source->source, 0, XAUDIO2_COMMIT_NOW);
source->state = AUDIO_PLAYING;
}
void audio_source_set_volume(AudioSource* source, float volume) {
if (!source || !source->source) return;
source->volume = volume;
source->source->lpVtbl->SetVolume(source->source, volume, XAUDIO2_COMMIT_NOW);
}
void audio_source_set_pitch(AudioSource* source, float pitch) {
if (!source || !source->source) return;
source->pitch = pitch;
source->source->lpVtbl->SetFrequencyRatio(source->source, pitch, XAUDIO2_COMMIT_NOW);
}
void audio_source_set_loop(AudioSource* source, int loop) {
if (!source) return;
source->loop = loop;
}
void audio_set_master_volume(AudioSystem* audio, float volume) {
if (!audio || !audio->mastering_voice) return;
audio->mastering_voice->lpVtbl->SetVolume(audio->mastering_voice, volume, XAUDIO2_COMMIT_NOW);
}
+62
View File
@@ -0,0 +1,62 @@
#pragma once
// ============================================================================
// audio.h - 音频系统 (XAudio2)
// ============================================================================
#include <xaudio2.h>
#include <stdint.h>
// 播放状态
typedef enum {
AUDIO_STOPPED = 0,
AUDIO_PLAYING,
AUDIO_PAUSED
} AudioState;
struct AudioSystem;
// 音频源
typedef struct {
struct AudioSystem* system;
IXAudio2SourceVoice* source;
IXAudio2Voice* mastering;
float volume;
float pitch;
int loop;
AudioState state;
uint8_t* audio_data;
uint32_t audio_size;
} AudioSource;
// 音频管理器
typedef struct AudioSystem {
IXAudio2* xaudio2;
IXAudio2MasteringVoice* mastering_voice;
} AudioSystem;
// 初始化/销毁
AudioSystem* audio_create(void);
void audio_destroy(AudioSystem* audio);
// 音频源
AudioSource* audio_source_create(AudioSystem* audio);
void audio_source_destroy(AudioSource* source);
// 播放
void audio_source_play(AudioSource* source);
void audio_source_stop(AudioSource* source);
void audio_source_pause(AudioSource* source);
void audio_source_resume(AudioSource* source);
// 音量/音调
void audio_source_set_volume(AudioSource* source, float volume);
void audio_source_set_pitch(AudioSource* source, float pitch);
void audio_source_set_loop(AudioSource* source, int loop);
// WAV加载
int audio_load_wav(AudioSource* source, const char* path);
int audio_load_wav_memory(AudioSource* source, const uint8_t* data, uint32_t size);
// 主音量
void audio_set_master_volume(AudioSystem* audio, float volume);
+192
View File
@@ -0,0 +1,192 @@
#include "asset_manager.h"
#include "../Rendering/mesh.h"
#include "../Rendering/texture.h"
#include "../Rendering/shader.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ASSET_BUCKET_COUNT 256
struct AssetManager {
Asset* buckets[ASSET_BUCKET_COUNT];
uint32_t next_id;
};
static uint32_t hash_str(const char* s) {
uint32_t h = 5381;
while (*s) h = ((h << 5) + h) + (unsigned char)*s++;
return h % ASSET_BUCKET_COUNT;
}
static int str_ends_with(const char* s, const char* suffix) {
size_t ls = strlen(s), lf = strlen(suffix);
return ls >= lf && strcmp(s + ls - lf, suffix) == 0;
}
static Asset* asset_alloc(AssetManager* am, AssetType type, const char* path, void* data, void (*destroy)(void*)) {
Asset* a = (Asset*)calloc(1, sizeof(Asset));
a->id = ++am->next_id;
a->type = type;
a->refcount = 1;
a->owner = am;
strncpy(a->path, path, sizeof(a->path) - 1);
a->data = data;
a->destroy = destroy;
uint32_t h = hash_str(path);
a->next = am->buckets[h];
am->buckets[h] = a;
return a;
}
static void asset_free(AssetManager* am, Asset* a) {
if (!a) return;
uint32_t h = hash_str(a->path);
Asset** cur = &am->buckets[h];
while (*cur) {
if (*cur == a) {
*cur = a->next;
break;
}
cur = &(*cur)->next;
}
if (a->destroy) a->destroy(a->data);
free(a);
}
AssetManager* asset_manager_create(void) {
AssetManager* am = (AssetManager*)calloc(1, sizeof(AssetManager));
return am;
}
void asset_manager_destroy(AssetManager* am) {
if (!am) return;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
Asset* a = am->buckets[i];
while (a) {
Asset* next = a->next;
if (a->destroy) a->destroy(a->data);
free(a);
a = next;
}
}
free(am);
}
Asset* asset_manager_find(AssetManager* am, const char* path) {
if (!am || !path) return NULL;
uint32_t h = hash_str(path);
for (Asset* a = am->buckets[h]; a; a = a->next) {
if (strcmp(a->path, path) == 0) return a;
}
return NULL;
}
Asset* asset_manager_get(AssetManager* am, uint32_t id) {
if (!am || id == 0) return NULL;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
for (Asset* a = am->buckets[i]; a; a = a->next) {
if (a->id == id) return a;
}
}
return NULL;
}
void* asset_data(Asset* a) { return a ? a->data : NULL; }
void asset_acquire(Asset* a) { if (a) a->refcount++; }
void asset_release(Asset* a) {
if (!a) return;
a->refcount--;
if (a->refcount <= 0 && a->owner) {
asset_free(a->owner, a);
}
}
// ------------------------------------------------------------------
// Mesh
// ------------------------------------------------------------------
static void destroy_mesh_asset(void* data) { mesh_destroy((Mesh*)data); }
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Mesh* mesh = NULL;
if (strcmp(path, "cube") == 0) mesh = mesh_create_cube(dev);
else if (strcmp(path, "sphere") == 0) mesh = mesh_create_sphere(dev, 1.0f, 16);
else if (strcmp(path, "plane") == 0) mesh = mesh_create_plane(dev, 10.0f);
else if (str_ends_with(path, ".obj")) mesh = mesh_load_obj(dev, path);
if (!mesh) return NULL;
return asset_alloc(am, ASSET_MESH, path, mesh, destroy_mesh_asset);
}
// ------------------------------------------------------------------
// Texture
// ------------------------------------------------------------------
static void destroy_texture_asset(void* data) { texture_destroy((Texture*)data); }
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Texture* tex = NULL;
if (strcmp(path, "checkerboard") == 0) tex = texture_create_checkerboard(dev, 256, 8);
else tex = texture_load_from_file(dev, path);
if (!tex) return NULL;
return asset_alloc(am, ASSET_TEXTURE, path, tex, destroy_texture_asset);
}
// ------------------------------------------------------------------
// Shader
// ------------------------------------------------------------------
static void destroy_shader_asset(void* data) { shader_destroy((Shader*)data); }
static char* read_file(const char* path, uint32_t* out_len) {
FILE* f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size <= 0) { fclose(f); return NULL; }
char* buf = (char*)malloc((size_t)size + 1);
if (!buf) { fclose(f); return NULL; }
fread(buf, 1, (size_t)size, f);
buf[size] = '\0';
fclose(f);
if (out_len) *out_len = (uint32_t)size;
return buf;
}
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path) {
if (!am || !dev) return NULL;
char key[512];
if (vs_path && ps_path) snprintf(key, sizeof(key), "%s|%s", vs_path, ps_path);
else strcpy(key, "default");
Asset* existing = asset_manager_find(am, key);
if (existing) { asset_acquire(existing); return existing; }
Shader* s = shader_create();
if (vs_path && ps_path) {
uint32_t vs_len = 0, ps_len = 0;
char* vs = read_file(vs_path, &vs_len);
char* ps = read_file(ps_path, &ps_len);
if (!vs || !ps || !shader_compile(s, dev, vs, ps, vs_len, ps_len)) {
shader_destroy(s); free(vs); free(ps); return NULL;
}
free(vs); free(ps);
} else {
if (!shader_compile(s, dev, NULL, NULL, 0, 0)) {
shader_destroy(s); return NULL;
}
}
return asset_alloc(am, ASSET_SHADER, key, s, destroy_shader_asset);
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
#include <d3d11.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AssetManager AssetManager;
typedef struct Asset Asset;
typedef enum {
ASSET_MESH = 0,
ASSET_TEXTURE,
ASSET_SHADER,
ASSET_MATERIAL,
ASSET_COUNT
} AssetType;
struct Asset {
uint32_t id;
AssetType type;
int refcount;
char path[256];
void* data;
void (*destroy)(void* data);
Asset* next;
struct AssetManager* owner;
};
AssetManager* asset_manager_create(void);
void asset_manager_destroy(AssetManager* am);
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path);
Asset* asset_manager_find(AssetManager* am, const char* path);
Asset* asset_manager_get(AssetManager* am, uint32_t id);
void* asset_data(Asset* a);
void asset_acquire(Asset* a);
void asset_release(Asset* a);
#ifdef __cplusplus
}
#endif
+244
View File
@@ -0,0 +1,244 @@
// ============================================================================
// engine.c - 引擎核心实现
// ============================================================================
#include "engine.h"
#include "logger.h"
#include "timer.h"
#include "../Window/window.h"
#include "../Rendering/renderer.h"
#include "../Rendering/graphics_device.h"
#include "../include/engine_api.h"
#include <stdlib.h>
#include <Windows.h>
// 引擎实例 (单例)
static Engine g_engine = {0};
// 回调
static EngineUpdateCallback g_update_callback = NULL;
static EngineRenderCallback g_render_callback = NULL;
static EngineKeyCallback g_key_callback = NULL;
static EngineResizeCallback g_resize_callback = NULL;
static EngineMouseMoveCallback g_mouse_move_callback = NULL;
static EngineMouseButtonCallback g_mouse_button_callback = NULL;
// 内部函数
static void on_key(int key) {
if (g_key_callback) {
g_key_callback(&g_engine, key);
}
if (key == VK_ESCAPE) {
engine_request_quit(&g_engine);
}
}
static void on_resize(int width, int height) {
if (g_resize_callback) {
g_resize_callback(&g_engine, width, height);
}
}
static void on_mouse_move(int x, int y) {
if (g_mouse_move_callback) {
g_mouse_move_callback(&g_engine, x, y);
}
}
static void on_mouse_button(int button, int pressed) {
if (g_mouse_button_callback) {
g_mouse_button_callback(&g_engine, button, pressed);
}
}
Engine* engine_create(void) {
return &g_engine;
}
void engine_destroy(Engine* engine) {
if (engine) {
engine_shutdown(engine);
}
}
int engine_init(Engine* engine, const EngineConfig* config) {
if (engine->initialized) return 1;
engine->config = *config;
// 初始化日志
logger_init("engine.log");
LOG_INFO("Initializing Lity3D Engine...");
// 初始化计时器
timer_init();
// 创建窗口
engine->window = window_create(
config->app_name,
config->window_width,
config->window_height,
config->fullscreen
);
if (!engine->window) {
LOG_ERROR("Failed to create window!");
return 0;
}
// 设置回调
window_set_key_callback(engine->window, on_key);
window_set_resize_callback(engine->window, on_resize);
window_set_mouse_move_callback(engine->window, on_mouse_move);
window_set_mouse_button_callback(engine->window, on_mouse_button);
LOG_INFO("Window created: %dx%d", config->window_width, config->window_height);
// 创建 D3D11 图形设备
engine->graphics_device = graphics_device_create();
if (!graphics_device_init(engine->graphics_device,
window_get_handle(engine->window),
config->window_width,
config->window_height,
config->vsync)) {
LOG_ERROR("Failed to initialize D3D11 graphics device!");
window_destroy(engine->window);
engine->window = NULL;
return 0;
}
// 创建渲染器
engine->renderer = renderer_create(engine->graphics_device);
if (!engine->renderer) {
LOG_ERROR("Failed to create renderer!");
graphics_device_destroy(engine->graphics_device);
engine->graphics_device = NULL;
window_destroy(engine->window);
engine->window = NULL;
return 0;
}
LOG_INFO("Graphics device and renderer ready.");
engine->initialized = 1;
engine->running = 1;
engine->frame_count = 0;
LOG_INFO("Lity3D Engine initialized successfully.");
return 1;
}
void engine_run(Engine* engine) {
if (!engine->initialized) {
LOG_ERROR("Engine not initialized!");
return;
}
double fixed_dt = 1.0 / 60.0; // 60Hz 固定时间步
double accumulator = 0.0;
double fps_timer = 0.0;
int fps_frames = 0;
int fps_display = 0;
while (engine->running) {
// 处理窗口消息
if (!window_process_messages(engine->window)) {
engine->running = 0;
break;
}
// 更新计时
timer_tick();
engine->delta_time = timer_get_delta();
engine->total_time += engine->delta_time;
engine->frame_count++;
// FPS 计算
fps_timer += engine->delta_time;
fps_frames++;
if (fps_timer >= 1.0) {
fps_display = fps_frames;
fps_frames = 0;
fps_timer -= 1.0;
}
// 固定时间步逻辑更新
accumulator += engine->delta_time;
while (accumulator >= fixed_dt) {
if (g_update_callback) {
g_update_callback(engine, fixed_dt);
}
accumulator -= fixed_dt;
}
// 渲染
if (g_render_callback) {
g_render_callback(engine);
}
// 存储 FPS 供外部读取
engine->fps = fps_display;
}
}
void engine_shutdown(Engine* engine) {
if (!engine->initialized) return;
LOG_INFO("Shutting down Lity3D Engine...");
if (engine->renderer) {
renderer_destroy(engine->renderer);
engine->renderer = NULL;
}
if (engine->graphics_device) {
graphics_device_destroy(engine->graphics_device);
engine->graphics_device = NULL;
}
if (engine->window) {
window_destroy(engine->window);
engine->window = NULL;
}
logger_shutdown();
engine->initialized = 0;
engine->running = 0;
}
void engine_request_quit(Engine* engine) {
engine->running = 0;
}
// 回调设置
void engine_set_update_callback(Engine* engine, EngineUpdateCallback callback) {
g_update_callback = callback;
}
void engine_set_render_callback(Engine* engine, EngineRenderCallback callback) {
g_render_callback = callback;
}
void engine_set_key_callback(Engine* engine, EngineKeyCallback callback) {
g_key_callback = callback;
}
void engine_set_resize_callback(Engine* engine, EngineResizeCallback callback) {
g_resize_callback = callback;
}
void engine_set_mouse_move_callback(Engine* engine, EngineMouseMoveCallback callback) {
g_mouse_move_callback = callback;
}
void engine_set_mouse_button_callback(Engine* engine, EngineMouseButtonCallback callback) {
g_mouse_button_callback = callback;
}
GraphicsDevice* engine_get_graphics_device(Engine* engine) {
return engine ? engine->graphics_device : NULL;
}
Renderer* engine_get_renderer(Engine* engine) {
return engine ? (Renderer*)engine->renderer : NULL;
}
+73
View File
@@ -0,0 +1,73 @@
#pragma once
// ============================================================================
// engine.h - 引擎核心
// ============================================================================
#include <stdint.h>
// 前向声明
typedef struct Window Window;
typedef struct GraphicsDevice GraphicsDevice;
typedef struct Renderer Renderer;
// 引擎配置
typedef struct {
const char* app_name;
int window_width;
int window_height;
int fullscreen;
int vsync;
} EngineConfig;
// 引擎状态
typedef struct {
int running;
int initialized;
EngineConfig config;
Window* window;
void* renderer;
GraphicsDevice* graphics_device;
double delta_time;
double total_time;
int fps;
uint64_t frame_count;
} Engine;
// 初始化默认配置
static inline EngineConfig engine_default_config(void) {
EngineConfig config;
config.app_name = "Lity3D Engine";
config.window_width = 1280;
config.window_height = 720;
config.fullscreen = 0;
config.vsync = 1;
return config;
}
// 引擎函数
Engine* engine_create(void);
void engine_destroy(Engine* engine);
int engine_init(Engine* engine, const EngineConfig* config);
void engine_run(Engine* engine);
void engine_shutdown(Engine* engine);
void engine_request_quit(Engine* engine);
// 回调
typedef void (*EngineUpdateCallback)(Engine* engine, double delta_time);
typedef void (*EngineRenderCallback)(Engine* engine);
typedef void (*EngineKeyCallback)(Engine* engine, int key);
typedef void (*EngineResizeCallback)(Engine* engine, int width, int height);
typedef void (*EngineMouseMoveCallback)(Engine* engine, int x, int y);
typedef void (*EngineMouseButtonCallback)(Engine* engine, int button, int pressed);
void engine_set_update_callback(Engine* engine, EngineUpdateCallback callback);
void engine_set_render_callback(Engine* engine, EngineRenderCallback callback);
void engine_set_key_callback(Engine* engine, EngineKeyCallback callback);
void engine_set_resize_callback(Engine* engine, EngineResizeCallback callback);
void engine_set_mouse_move_callback(Engine* engine, EngineMouseMoveCallback callback);
void engine_set_mouse_button_callback(Engine* engine, EngineMouseButtonCallback callback);
// 子系统访问
GraphicsDevice* engine_get_graphics_device(Engine* engine);
Renderer* engine_get_renderer(Engine* engine);
+73
View File
@@ -0,0 +1,73 @@
// ============================================================================
// logger.c - 日志系统实现
// ============================================================================
#include "logger.h"
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
static FILE* g_log_file = NULL;
static LogLevel g_log_level = LOG_TRACE;
static const char* level_strings[] = {
"TRACE", "DEBUG", "INFO ", "WARN ", "ERROR", "FATAL"
};
void logger_init(const char* log_file) {
if (log_file) {
g_log_file = fopen(log_file, "w");
if (g_log_file) {
fprintf(g_log_file, "=== Lity3D Engine Log ===\n");
fflush(g_log_file);
}
}
printf("=== Lity3D Engine Log ===\n");
}
void logger_shutdown(void) {
if (g_log_file) {
fclose(g_log_file);
g_log_file = NULL;
}
}
void logger_set_level(LogLevel level) {
g_log_level = level;
}
void logger_log(LogLevel level, const char* file, int line, const char* fmt, ...) {
if (level < g_log_level) return;
// 时间戳
time_t now = time(NULL);
struct tm* tm_info = localtime(&now);
char time_str[20];
strftime(time_str, sizeof(time_str), "%H:%M:%S", tm_info);
// 文件名 (去掉路径)
const char* filename = strrchr(file, '\\');
if (!filename) filename = strrchr(file, '/');
if (filename) filename++; else filename = file;
// 格式化消息
char msg[2048];
va_list args;
va_start(args, fmt);
vsnprintf(msg, sizeof(msg), fmt, args);
va_end(args);
// 输出到控制台
if (level >= LOG_WARN) {
fprintf(stderr, "[%s] [%s] %s:%d - %s\n", time_str, level_strings[level], filename, line, msg);
} else {
printf("[%s] [%s] %s:%d - %s\n", time_str, level_strings[level], filename, line, msg);
}
// 输出到文件
if (g_log_file) {
fprintf(g_log_file, "[%s] [%s] %s:%d - %s\n", time_str, level_strings[level], filename, line, msg);
fflush(g_log_file);
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
// ============================================================================
// logger.h - 日志系统
// ============================================================================
#include <stdio.h>
// 日志级别
typedef enum {
LOG_TRACE = 0,
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERROR,
LOG_FATAL
} LogLevel;
// 初始化/关闭
void logger_init(const char* log_file);
void logger_shutdown(void);
// 设置日志级别
void logger_set_level(LogLevel level);
// 日志函数
void logger_log(LogLevel level, const char* file, int line, const char* fmt, ...);
// 便捷宏
#define LOG_TRACE(...) logger_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_DEBUG(...) logger_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_INFO(...) logger_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_WARN(...) logger_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_ERROR(...) logger_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_FATAL(...) logger_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
+78
View File
@@ -0,0 +1,78 @@
// ============================================================================
// timer.c - 高精度计时器实现 (使用 rdtsc.asm)
// ============================================================================
#include "timer.h"
#include "../include/engine_api.h"
#include <Windows.h>
static double g_seconds_per_count = 0.0;
static int64_t g_prev_time = 0;
static int64_t g_curr_time = 0;
static int64_t g_start_time = 0;
static double g_delta_time = 0.0;
static double g_total_time = 0.0;
static double g_fps = 0.0;
static uint64_t g_frame_count = 0;
static double g_fps_accumulator = 0.0;
static uint64_t g_fps_frame_count = 0;
void timer_init(void) {
// 使用 QueryPerformanceCounter 获取高精度时间
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
g_seconds_per_count = 1.0 / (double)freq.QuadPart;
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
g_start_time = now.QuadPart;
g_prev_time = now.QuadPart;
g_curr_time = now.QuadPart;
g_delta_time = 0.0;
g_total_time = 0.0;
g_fps = 0.0;
g_frame_count = 0;
g_fps_accumulator = 0.0;
g_fps_frame_count = 0;
}
void timer_reset(void) {
timer_init();
}
void timer_tick(void) {
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
g_curr_time = now.QuadPart;
g_delta_time = (g_curr_time - g_prev_time) * g_seconds_per_count;
g_total_time = (g_curr_time - g_start_time) * g_seconds_per_count;
g_prev_time = g_curr_time;
g_frame_count++;
g_fps_frame_count++;
g_fps_accumulator += g_delta_time;
if (g_fps_accumulator >= 1.0) {
g_fps = (double)g_fps_frame_count / g_fps_accumulator;
g_fps_frame_count = 0;
g_fps_accumulator = 0.0;
}
}
double timer_get_delta(void) {
return g_delta_time;
}
double timer_get_total(void) {
return g_total_time;
}
double timer_get_fps(void) {
return g_fps;
}
uint64_t timer_get_frame_count(void) {
return g_frame_count;
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
// ============================================================================
// timer.h - 高精度计时器
// ============================================================================
#include <stdint.h>
// 初始化计时器
void timer_init(void);
// 重置计时器
void timer_reset(void);
// 更新一帧
void timer_tick(void);
// 获取帧间隔 (秒)
double timer_get_delta(void);
// 获取总时间 (秒)
double timer_get_total(void);
// 获取 FPS
double timer_get_fps(void);
// 获取帧数
uint64_t timer_get_frame_count(void);
+100
View File
@@ -0,0 +1,100 @@
#include "world.h"
#include <stdlib.h>
#include <string.h>
World* world_create(void) {
World* w = (World*)calloc(1, sizeof(World));
w->next_entity = 1;
return w;
}
void world_destroy(World* world) {
if (!world) return;
for (uint32_t i = 0; i < world->entity_count; i++) {
Entity e = world->entities[i];
for (uint32_t c = 0; c < world->component_count; c++) {
if (world->components[c][e] && world->component_tables[c].destroy) {
world->component_tables[c].destroy(world->components[c][e]);
}
free(world->components[c][e]);
}
}
free(world);
}
Entity world_create_entity(World* world) {
if (world->entity_count >= MAX_ENTITIES) return 0;
Entity e = world->next_entity++;
world->entities[world->entity_count++] = e;
world->entity_masks[e] = 0;
return e;
}
void world_destroy_entity(World* world, Entity entity) {
for (uint32_t c = 0; c < world->component_count; c++) {
if (world->components[c][entity] && world->component_tables[c].destroy) {
world->component_tables[c].destroy(world->components[c][entity]);
}
free(world->components[c][entity]);
world->components[c][entity] = NULL;
}
world->entity_masks[entity] = 0;
for (uint32_t i = 0; i < world->entity_count; i++) {
if (world->entities[i] == entity) {
world->entities[i] = world->entities[--world->entity_count];
break;
}
}
}
int world_entity_alive(World* world, Entity entity) {
for (uint32_t i = 0; i < world->entity_count; i++) {
if (world->entities[i] == entity) return 1;
}
return 0;
}
ComponentType world_register_component(World* world, uint32_t size, ComponentVTable vtable) {
ComponentType type = world->component_count++;
world->component_sizes[type] = size;
world->component_tables[type] = vtable;
return type;
}
void* world_add_component(World* world, Entity entity, ComponentType type) {
if (world->components[type][entity]) return world->components[type][entity];
void* comp = calloc(1, world->component_sizes[type]);
world->components[type][entity] = comp;
world->entity_masks[entity] |= (1ULL << type);
return comp;
}
void* world_get_component(World* world, Entity entity, ComponentType type) {
return world->components[type][entity];
}
void world_remove_component(World* world, Entity entity, ComponentType type) {
if (world->components[type][entity]) {
if (world->component_tables[type].destroy) {
world->component_tables[type].destroy(world->components[type][entity]);
}
free(world->components[type][entity]);
world->components[type][entity] = NULL;
world->entity_masks[entity] &= ~(1ULL << type);
}
}
int world_has_component(World* world, Entity entity, ComponentType type) {
return (world->entity_masks[entity] & (1ULL << type)) != 0;
}
void world_each(World* world, uint64_t mask, SystemFunc func, void* data) {
for (uint32_t i = 0; i < world->entity_count; i++) {
Entity e = world->entities[i];
if ((world->entity_masks[e] & mask) == mask) {
func(world, e, data);
}
}
}
+53
View File
@@ -0,0 +1,53 @@
#pragma once
// ============================================================================
// world.h - 实体组件系统 (ECS)
// ============================================================================
#include <stdint.h>
#include "../include/engine_api.h"
#define MAX_ENTITIES 4096
#define MAX_COMPONENTS 32
typedef uint32_t Entity;
typedef uint32_t ComponentType;
// 组件接口
typedef struct {
void (*destroy)(void* component);
} ComponentVTable;
// 世界
typedef struct {
Entity entities[MAX_ENTITIES];
uint32_t entity_count;
uint32_t next_entity;
void* components[MAX_COMPONENTS][MAX_ENTITIES];
ComponentVTable component_tables[MAX_COMPONENTS];
uint32_t component_sizes[MAX_COMPONENTS];
uint32_t component_count;
uint64_t entity_masks[MAX_ENTITIES];
} World;
// 创建/销毁
World* world_create(void);
void world_destroy(World* world);
// 实体
Entity world_create_entity(World* world);
void world_destroy_entity(World* world, Entity entity);
int world_entity_alive(World* world, Entity entity);
// 组件
ComponentType world_register_component(World* world, uint32_t size, ComponentVTable vtable);
void* world_add_component(World* world, Entity entity, ComponentType type);
void* world_get_component(World* world, Entity entity, ComponentType type);
void world_remove_component(World* world, Entity entity, ComponentType type);
int world_has_component(World* world, Entity entity, ComponentType type);
// 系统遍历
typedef void (*SystemFunc)(World* world, Entity entity, void* data);
void world_each(World* world, uint64_t mask, SystemFunc func, void* data);
+303
View File
@@ -0,0 +1,303 @@
#include "bitmap_font.h"
#include <stdlib.h>
#include <string.h>
#include "../Core/logger.h"
static const uint8_t FONT_DATA[FONT_NUM_CHARS][FONT_CHAR_H] = {
{0},{0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00},
{0x18,0x18,0x7C,0xC6,0xC2,0xC0,0x7C,0x06,0x06,0x86,0xC6,0x7C,0x18,0x18,0x00,0x00},
{0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00},
{0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
{0x00,0x30,0x30,0x30,0x70,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xF6,0xE6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00},
{0},{0},{0},
{0x00,0x00,0x00,0x0C,0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
{0},{0},
{0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0},{0},{0},{0},{0},{0},{0},{0},{0},
{0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xF6,0xE6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x0C,0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00},
{0x00,0x00,0xE6,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00},
{0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0x6C,0x38,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x06,0x02,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00},
{0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00},
};
static Shader* g_font_shader = NULL;
static ID3D11Buffer* g_font_vb = NULL;
static ID3D11Buffer* g_font_ib = NULL;
static FontVert g_font_verts[FONT_MAX_QUADS * 4];
static uint32_t g_font_indices[FONT_MAX_QUADS * 6];
static int g_font_vi = 0;
static int g_font_ii = 0;
static void make_ortho(float* m, float sw, float sh) {
memset(m, 0, 64);
m[0] = 2.0f / sw;
m[5] = -2.0f / sh;
m[10] = 1.0f;
m[12] = -1.0f;
m[13] = 1.0f;
m[15] = 1.0f;
}
static void make_atlas(uint8_t* pixels, int w, int h) {
memset(pixels, 0, (size_t)w * h * 4);
int cols = w / FONT_CHAR_W;
for (int i = 0; i < FONT_NUM_CHARS; i++) {
int cx = (i % cols) * FONT_CHAR_W;
int cy = (i / cols) * FONT_CHAR_H;
for (int row = 0; row < FONT_CHAR_H; row++) {
uint8_t bits = FONT_DATA[i][row];
for (int col = 0; col < 8; col++) {
if (bits & (0x80 >> col)) {
int px = cx + col, py = cy + row;
int idx = (py * w + px) * 4;
pixels[idx] = 255; pixels[idx+1] = 255; pixels[idx+2] = 255; pixels[idx+3] = 255;
}
}
}
}
}
int bitmap_font_init(BitmapFont* f, ID3D11Device* dev) {
memset(f, 0, sizeof(*f));
int cols = 16;
f->atlas_w = cols * FONT_CHAR_W;
f->atlas_h = ((FONT_NUM_CHARS + cols - 1) / cols) * FONT_CHAR_H;
uint8_t* pixels = (uint8_t*)malloc((size_t)f->atlas_w * f->atlas_h * 4);
make_atlas(pixels, f->atlas_w, f->atlas_h);
D3D11_TEXTURE2D_DESC td = {0};
td.Width = (UINT)f->atlas_w; td.Height = (UINT)f->atlas_h;
td.MipLevels = 1; td.ArraySize = 1;
td.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA sd = {0};
sd.pSysMem = pixels; sd.SysMemPitch = (UINT)(f->atlas_w * 4);
HRESULT hr = dev->lpVtbl->CreateTexture2D(dev, &td, &sd, &f->texture);
free(pixels);
if (FAILED(hr)) return 0;
dev->lpVtbl->CreateShaderResourceView(dev, (ID3D11Resource*)f->texture, NULL, &f->srv);
f->shader = shader_create();
if (!shader_compile(f->shader, dev, NULL, NULL, 0, 0)) return 0;
g_font_shader = f->shader;
D3D11_BUFFER_DESC bd = {0};
bd.ByteWidth = FONT_MAX_QUADS * 4 * sizeof(FontVert);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
memset(g_font_verts, 0, sizeof(g_font_verts));
sd.pSysMem = g_font_verts;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &g_font_vb);
bd.ByteWidth = FONT_MAX_QUADS * 6 * sizeof(uint32_t);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
memset(g_font_indices, 0, sizeof(g_font_indices));
sd.pSysMem = g_font_indices;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &g_font_ib);
bd.ByteWidth = 256; bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
dev->lpVtbl->CreateBuffer(dev, &bd, NULL, &f->cb);
D3D11_BLEND_DESC bdesc = {0};
bdesc.RenderTarget[0].BlendEnable = TRUE;
bdesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bdesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bdesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bdesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bdesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
dev->lpVtbl->CreateBlendState(dev, &bdesc, &f->blend);
D3D11_SAMPLER_DESC sdesc = {0};
sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sdesc.AddressU = sdesc.AddressV = sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
dev->lpVtbl->CreateSamplerState(dev, &sdesc, &f->sampler);
D3D11_DEPTH_STENCIL_DESC dsdesc = {0};
dsdesc.DepthEnable = FALSE;
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dev->lpVtbl->CreateDepthStencilState(dev, &dsdesc, &f->ds_no_depth);
LOG_INFO("bitmap_font: initialized (%dx%d atlas)", f->atlas_w, f->atlas_h);
return 1;
}
void bitmap_font_destroy(BitmapFont* f) {
if (!f) return;
if (f->ds_no_depth) f->ds_no_depth->lpVtbl->Release(f->ds_no_depth);
if (f->sampler) f->sampler->lpVtbl->Release(f->sampler);
if (f->blend) f->blend->lpVtbl->Release(f->blend);
if (f->cb) f->cb->lpVtbl->Release(f->cb);
if (g_font_ib) { g_font_ib->lpVtbl->Release(g_font_ib); g_font_ib = NULL; }
if (g_font_vb) { g_font_vb->lpVtbl->Release(g_font_vb); g_font_vb = NULL; }
if (f->shader) { shader_destroy(f->shader); g_font_shader = NULL; }
if (f->srv) f->srv->lpVtbl->Release(f->srv);
if (f->texture) f->texture->lpVtbl->Release(f->texture);
memset(f, 0, sizeof(*f));
}
static void font_flush(ID3D11DeviceContext* ctx,
float screen_w, float screen_h, int num_idx) {
if (num_idx == 0) return;
float wvp[64];
make_ortho(wvp, screen_w, screen_h);
ID3D11Device* dev = NULL;
ctx->lpVtbl->GetDevice(ctx, &dev);
D3D11_RASTERIZER_DESC rdesc = {0};
rdesc.FillMode = D3D11_FILL_SOLID;
rdesc.CullMode = D3D11_CULL_NONE;
rdesc.ScissorEnable = FALSE;
ID3D11RasterizerState* rstate = NULL;
HRESULT hr = dev->lpVtbl->CreateRasterizerState(dev, &rdesc, &rstate);
if (rstate) {
ctx->lpVtbl->RSSetState(ctx, rstate);
rstate->lpVtbl->Release(rstate);
}
dev->lpVtbl->Release(dev);
shader_bind(g_font_shader, ctx);
shader_set_matrix(g_font_shader, ctx, wvp);
UINT stride = sizeof(FontVert), offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &g_font_vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, g_font_ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->lpVtbl->DrawIndexed(ctx, (UINT)num_idx, 0, 0);
}
static void font_begin(void) { g_font_vi = 0; g_font_ii = 0; }
static void font_add_quad(BitmapFont* f, float x, float y, float w, float h,
float r, float g, float b, float a) {
if (g_font_vi + 4 > FONT_MAX_QUADS * 4) return;
float u0 = 0.19f, v0 = 0.01f, u1 = 0.24f, v1 = 0.15f;
int vi = g_font_vi;
g_font_verts[vi] = (FontVert){{x, y, 0},{0,0,-1},{u0,v0},{r,g,b,a}};
g_font_verts[vi+1] = (FontVert){{x+w, y, 0},{0,0,-1},{u1,v0},{r,g,b,a}};
g_font_verts[vi+2] = (FontVert){{x+w, y+h, 0},{0,0,-1},{u1,v1},{r,g,b,a}};
g_font_verts[vi+3] = (FontVert){{x, y+h, 0},{0,0,-1},{u0,v1},{r,g,b,a}};
uint32_t base = (uint32_t)vi;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+1; g_font_indices[g_font_ii++] = base+2;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+2; g_font_indices[g_font_ii++] = base+3;
g_font_vi += 4;
}
void bitmap_font_draw(BitmapFont* f, ID3D11DeviceContext* ctx,
const char* text, int x, int y,
float r, float g, float b, float a,
float screen_w, float screen_h) {
if (!text || !*text || !f->shader) return;
int len = (int)strlen(text);
if (len > FONT_MAX_QUADS) len = FONT_MAX_QUADS;
int cols = f->atlas_w / FONT_CHAR_W;
font_begin();
int cx = x;
for (int i = 0; i < len; i++) {
int ch = (unsigned char)text[i] - FONT_FIRST_CHAR;
if (ch < 0 || ch >= FONT_NUM_CHARS) ch = 0;
int col_idx = ch % cols;
int row_idx = ch / cols;
float u0 = (float)(col_idx * FONT_CHAR_W) / (float)f->atlas_w;
float v0 = (float)(row_idx * FONT_CHAR_H) / (float)f->atlas_h;
float u1 = (float)((col_idx + 1) * FONT_CHAR_W) / (float)f->atlas_w;
float v1 = (float)((row_idx + 1) * FONT_CHAR_H) / (float)f->atlas_h;
int vi = g_font_vi;
g_font_verts[vi] = (FontVert){{(float)cx,(float)y,0},{0,0,-1},{u0,v0},{r,g,b,a}};
g_font_verts[vi+1] = (FontVert){{(float)cx+FONT_CHAR_W,(float)y,0},{0,0,-1},{u1,v0},{r,g,b,a}};
g_font_verts[vi+2] = (FontVert){{(float)cx+FONT_CHAR_W,(float)y+FONT_CHAR_H,0},{0,0,-1},{u1,v1},{r,g,b,a}};
g_font_verts[vi+3] = (FontVert){{(float)cx,(float)y+FONT_CHAR_H,0},{0,0,-1},{u0,v1},{r,g,b,a}};
uint32_t base = (uint32_t)vi;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+1; g_font_indices[g_font_ii++] = base+2;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+2; g_font_indices[g_font_ii++] = base+3;
g_font_vi += 4;
cx += FONT_CHAR_W;
}
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_vb, 0, NULL, g_font_verts, 0, 0);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_ib, 0, NULL, g_font_indices, 0, 0);
font_flush(ctx, screen_w, screen_h, g_font_ii);
}
void bitmap_font_rect(BitmapFont* f, ID3D11DeviceContext* ctx,
float x, float y, float w, float h,
float r, float g, float b, float a,
float screen_w, float screen_h) {
if (!f->shader) return;
font_begin();
font_add_quad(f, x, y, w, h, r, g, b, a);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_vb, 0, NULL, g_font_verts, 0, 0);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_ib, 0, NULL, g_font_indices, 0, 0);
font_flush(ctx, screen_w, screen_h, g_font_ii);
}
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
#include "../Rendering/shader.h"
#define FONT_CHAR_W 8
#define FONT_CHAR_H 16
#define FONT_FIRST_CHAR 32
#define FONT_NUM_CHARS 96
#define FONT_MAX_QUADS 4096
typedef struct {
float pos[3];
float norm[3];
float uv[2];
float color[4];
} FontVert;
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
ID3D11Buffer* cb;
Shader* shader;
ID3D11BlendState* blend;
ID3D11SamplerState* sampler;
ID3D11DepthStencilState* ds_no_depth;
int atlas_w;
int atlas_h;
} BitmapFont;
int bitmap_font_init(BitmapFont* f, ID3D11Device* dev);
void bitmap_font_destroy(BitmapFont* f);
void bitmap_font_draw(BitmapFont* f, ID3D11DeviceContext* ctx,
const char* text, int x, int y,
float r, float g, float b, float a,
float screen_w, float screen_h);
void bitmap_font_rect(BitmapFont* f, ID3D11DeviceContext* ctx,
float x, float y, float w, float h,
float r, float g, float b, float a,
float screen_w, float screen_h);
+122
View File
@@ -0,0 +1,122 @@
#include "editor.h"
#include "../Core/logger.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static const float BG_DARK[] = {0.12f, 0.12f, 0.14f};
static const float BG_PANEL[] = {0.16f, 0.16f, 0.18f};
static const float BG_TITLE[] = {0.20f, 0.20f, 0.24f};
static const float BG_INPUT[] = {0.10f, 0.10f, 0.12f};
int editor_init(Editor* ed, GraphicsDevice* dev) {
memset(ed, 0, sizeof(*ed));
ed->device = dev;
if (!uilayer_init(&ed->ui, dev->device, (float)dev->width, (float)dev->height)) return 0;
int sw = dev->width, sh = dev->height;
int menu_h = 28;
ed->panels[0] = (EditorPanel){PANEL_VIEWPORT, "Viewport", 220, menu_h, sw-440, sh-menu_h-200, 1, 0, 0};
ed->panels[1] = (EditorPanel){PANEL_SCENE, "Scene", 0, menu_h, 220, sh-menu_h-200, 1, 0, 0};
ed->panels[2] = (EditorPanel){PANEL_INSPECTOR, "Inspector", sw-220, menu_h, 220, sh-menu_h-200, 1, 0, 0};
ed->panels[3] = (EditorPanel){PANEL_CONSOLE, "Console", 0, sh-200, sw, 200, 1, 0, 0};
ed->panel_count = 4;
ed->scene_entities_count = 8;
strcpy(ed->selected_entity, "Cube");
LOG_INFO("Editor initialized");
return 1;
}
void editor_shutdown(Editor* ed) {
uilayer_destroy(&ed->ui);
}
void editor_update(Editor* ed, float dt) {
ed->time += dt;
ed->viewport_rotation += dt * 0.5f;
}
void editor_render(Editor* ed) {
ID3D11DeviceContext* ctx = ed->device->context;
float sw = (float)ed->device->width;
float sh = (float)ed->device->height;
graphics_device_clear(ed->device, BG_DARK[0], BG_DARK[1], BG_DARK[2], 1.0f);
uilayer_begin(&ed->ui);
uilayer_rect(&ed->ui, 0, 0, sw, 28, 1.0f, 0.0f, 0.0f, 1.0f);
uilayer_rect(&ed->ui, 0, 28, sw, 28, 0.0f, 1.0f, 0.0f, 1.0f);
uilayer_rect(&ed->ui, 0, 56, sw/3, sh-256, 0.0f, 0.0f, 1.0f, 1.0f);
uilayer_rect(&ed->ui, sw/3, 56, sw/3, sh-256, 1.0f, 1.0f, 0.0f, 1.0f);
uilayer_rect(&ed->ui, 2*sw/3, 56, sw/3, sh-256, 1.0f, 0.0f, 1.0f, 1.0f);
uilayer_rect(&ed->ui, 0, sh-200, sw, 200, 0.0f, 1.0f, 1.0f, 1.0f);
uilayer_rect(&ed->ui, 0, sh-22, sw, 22, 1.0f, 0.5f, 0.0f, 1.0f);
uilayer_flush(&ed->ui, ctx);
float sw2 = sw, sh2 = sh;
bitmap_font_draw(&ed->font, ctx, "Lity3D Engine Editor", 10, 6,
0.4f, 0.75f, 1.0f, 1.0f, sw2, sh2);
const char* menu[] = {"File", "Edit", "View", "Build", "Help"};
int mx = 180;
for (int i = 0; i < 5; i++) {
bitmap_font_draw(&ed->font, ctx, menu[i], mx + 8, 6,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
mx += (int)strlen(menu[i]) * 8 + 16;
}
const char* tools[] = {"Select", "Move", "Rotate", "Scale"};
int tx = 6;
for (int i = 0; i < 4; i++) {
bitmap_font_draw(&ed->font, ctx, tools[i], tx + 4, 34,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
tx += (int)strlen(tools[i]) * 8 + 16;
}
for (int i = 0; i < ed->panel_count; i++) {
EditorPanel* p = &ed->panels[i];
if (!p->visible) continue;
bitmap_font_draw(&ed->font, ctx, p->title, p->x + 6, p->y + 6,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
}
bitmap_font_draw(&ed->font, ctx, ed->selected_entity, 226, 56,
0.4f, 0.75f, 1.0f, 1.0f, sw2, sh2);
const char* entities[] = {"Light", "Camera", "Cube", "Plane", "Sphere", "Particles", "Audio", "Skybox"};
for (int i = 0; i < 8; i++) {
bitmap_font_draw(&ed->font, ctx, entities[i], 226, 78 + i * 22,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
}
const char* sections[] = {"Transform", "Mesh Filter", "Material", "Collider"};
int sy = 56;
for (int s = 0; s < 4; s++) {
bitmap_font_draw(&ed->font, ctx, sections[s], (int)sw - 214, sy,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
sy += 26;
}
char status[128];
snprintf(status, sizeof(status), "Entities: %d | Time: %.1fs", ed->scene_entities_count, ed->time);
bitmap_font_draw(&ed->font, ctx, status, 10, (int)sh - 18,
0.55f, 0.55f, 0.6f, 1.0f, sw2, sh2);
}
void editor_handle_input(Editor* ed, int msg, int wp, int lp) {
(void)lp;
switch (msg) {
case 0x0200: ed->input.mouse_x = wp & 0xFFFF; ed->input.mouse_y = (wp >> 16) & 0x7FFF; break;
case 0x0201: ed->input.mouse_lbtn = 1; break;
case 0x0202: ed->input.mouse_lbtn = 0; break;
case 0x0204: ed->input.mouse_rbtn = 1; break;
case 0x0205: ed->input.mouse_rbtn = 0; break;
case 0x0100: if (wp < 256) ed->input.key_down[wp] = 1; break;
case 0x0101: if (wp < 256) ed->input.key_down[wp] = 0; break;
}
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "uilayer.h"
#include "bitmap_font.h"
#include "../Rendering/graphics_device.h"
#include <stdint.h>
#define EDITOR_MAX_PANELS 16
typedef enum {
PANEL_SCENE,
PANEL_INSPECTOR,
PANEL_CONSOLE,
PANEL_ASSETS,
PANEL_VIEWPORT,
} PanelType;
typedef struct {
PanelType type;
const char* title;
int x, y, w, h;
int visible;
int collapsed;
int active_tab;
} EditorPanel;
typedef struct {
int mouse_x, mouse_y;
int mouse_lbtn;
int mouse_rbtn;
int key_down[256];
char text_input[256];
int text_len;
} EditorInput;
typedef struct {
GraphicsDevice* device;
UILayer ui;
BitmapFont font;
EditorPanel panels[EDITOR_MAX_PANELS];
int panel_count;
EditorInput input;
float time;
char console_lines[32][256];
int console_count;
int console_scroll;
char selected_entity[128];
int scene_entities_count;
float viewport_rotation;
} Editor;
int editor_init(Editor* ed, GraphicsDevice* dev);
void editor_shutdown(Editor* ed);
void editor_update(Editor* ed, float dt);
void editor_render(Editor* ed);
void editor_handle_input(Editor* ed, int msg, int wp, int lp);
+124
View File
@@ -0,0 +1,124 @@
#include "editor_2d.h"
#include <stdlib.h>
#include <string.h>
int editor2d_init(Editor2D* e, ID3D11Device* dev) {
memset(e, 0, sizeof(*e));
e->shader = shader_create();
if (!shader_compile(e->shader, dev, NULL, NULL, 0, 0)) return 0;
D3D11_BUFFER_DESC bd = {0};
bd.ByteWidth = ED2D_MAX_QUADS * 4 * sizeof(Ed2DVert);
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (FAILED(dev->lpVtbl->CreateBuffer(dev, &bd, NULL, &e->vb))) return 0;
bd.ByteWidth = ED2D_MAX_QUADS * 6 * sizeof(uint32_t);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
uint32_t* idx = (uint32_t*)malloc(ED2D_MAX_QUADS * 6 * sizeof(uint32_t));
for (int i = 0; i < ED2D_MAX_QUADS; i++) {
uint32_t b2 = i * 4;
idx[i*6]=b2; idx[i*6+1]=b2+1; idx[i*6+2]=b2+2;
idx[i*6+3]=b2; idx[i*6+4]=b2+2; idx[i*6+5]=b2+3;
}
D3D11_SUBRESOURCE_DATA sd = {0}; sd.pSysMem = idx;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &e->ib);
free(idx);
e->verts = (Ed2DVert*)malloc(ED2D_MAX_QUADS * 4 * sizeof(Ed2DVert));
D3D11_BLEND_DESC bdesc = {0};
bdesc.RenderTarget[0].BlendEnable = TRUE;
bdesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bdesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bdesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bdesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bdesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
dev->lpVtbl->CreateBlendState(dev, &bdesc, &e->blend);
D3D11_DEPTH_STENCIL_DESC dsdesc = {0};
dsdesc.DepthEnable = FALSE;
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dev->lpVtbl->CreateDepthStencilState(dev, &dsdesc, &e->ds_no_depth);
return 1;
}
void editor2d_destroy(Editor2D* e) {
if (!e) return;
if (e->ds_no_depth) e->ds_no_depth->lpVtbl->Release(e->ds_no_depth);
if (e->blend) e->blend->lpVtbl->Release(e->blend);
if (e->ib) e->ib->lpVtbl->Release(e->ib);
if (e->vb) e->vb->lpVtbl->Release(e->vb);
shader_destroy(e->shader);
free(e->verts);
memset(e, 0, sizeof(*e));
}
void editor2d_begin(Editor2D* e, float sw, float sh) {
e->quad_count = 0;
e->screen_w = sw;
e->screen_h = sh;
memset(e->wvp, 0, sizeof(e->wvp));
e->wvp[0] = 2.0f / sw;
e->wvp[5] = -2.0f / sh;
e->wvp[12] = -1.0f;
e->wvp[13] = 1.0f;
e->wvp[15] = 1.0f;
}
void editor2d_rect(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a) {
if (e->quad_count >= ED2D_MAX_QUADS) return;
int vi = e->quad_count * 4;
e->verts[vi+0] = (Ed2DVert){x, y, 0, 0,0,-1, 0,1, r,g,b,a};
e->verts[vi+1] = (Ed2DVert){x+w, y, 0, 0,0,-1, 1,1, r,g,b,a};
e->verts[vi+2] = (Ed2DVert){x+w, y+h, 0, 0,0,-1, 1,0, r,g,b,a};
e->verts[vi+3] = (Ed2DVert){x, y+h, 0, 0,0,-1, 0,0, r,g,b,a};
e->quad_count++;
}
void editor2d_rect_border(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a, float t) {
editor2d_rect(e, x, y, w, t, r, g, b, a);
editor2d_rect(e, x, y+h-t, w, t, r, g, b, a);
editor2d_rect(e, x, y, t, h, r, g, b, a);
editor2d_rect(e, x+w-t, y, t, h, r, g, b, a);
}
void editor2d_end(Editor2D* e, ID3D11DeviceContext* ctx) {
if (e->quad_count == 0) return;
D3D11_MAPPED_SUBRESOURCE mapped;
if (FAILED(ctx->lpVtbl->Map(ctx, (ID3D11Resource*)e->vb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) return;
memcpy(mapped.pData, e->verts, (size_t)e->quad_count * 4 * sizeof(Ed2DVert));
ctx->lpVtbl->Unmap(ctx, (ID3D11Resource*)e->vb, 0);
if (e->rtv) {
ctx->lpVtbl->OMSetRenderTargets(ctx, 1, &e->rtv, NULL);
}
D3D11_VIEWPORT vp = {0};
vp.Width = e->screen_w;
vp.Height = e->screen_h;
vp.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
shader_bind(e->shader, ctx);
shader_set_matrix(e->shader, ctx, e->wvp);
UINT stride = sizeof(Ed2DVert), offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &e->vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, e->ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
float bf[4] = {0};
ctx->lpVtbl->OMSetBlendState(ctx, e->blend, bf, 0xFFFFFFFF);
ctx->lpVtbl->OMSetDepthStencilState(ctx, e->ds_no_depth, 0);
ctx->lpVtbl->DrawIndexed(ctx, (UINT)(e->quad_count * 6), 0, 0);
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
#include "../Rendering/shader.h"
#include "../Rendering/mesh.h"
#define ED2D_MAX_QUADS 8192
typedef struct {
float x, y, z;
float nx, ny, nz;
float u, v;
float r, g, b, a;
} Ed2DVert;
typedef struct {
Shader* shader;
ID3D11Buffer* vb;
ID3D11Buffer* ib;
ID3D11BlendState* blend;
ID3D11DepthStencilState* ds_no_depth;
ID3D11RenderTargetView* rtv;
Ed2DVert* verts;
int quad_count;
float screen_w;
float screen_h;
float wvp[16];
} Editor2D;
int editor2d_init(Editor2D* e, ID3D11Device* dev);
void editor2d_destroy(Editor2D* e);
void editor2d_begin(Editor2D* e, float sw, float sh);
void editor2d_rect(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a);
void editor2d_rect_border(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a, float thickness);
void editor2d_end(Editor2D* e, ID3D11DeviceContext* ctx);
+171
View File
@@ -0,0 +1,171 @@
#include "glyph_atlas.h"
#include <stdlib.h>
#include <string.h>
#include <d3d11.h>
#include <Windows.h>
#include "../Core/logger.h"
typedef struct {
wchar_t ch;
int x, y;
int w, h;
float u0, v0, u1, v1;
float advance_x;
} GlyphEntry;
#define MAX_GLYPHS 1024
static GlyphEntry g_entries[MAX_GLYPHS];
static int g_entry_count = 0;
int glyph_atlas_init(GlyphAtlas* ga, ID3D11Device* dev) {
memset(ga, 0, sizeof(*ga));
ga->cell_w = GLYPH_CELL_W;
ga->cell_h = GLYPH_CELL_H;
ga->atlas_w = GLYPH_ATLAS_SIZE;
ga->atlas_h = GLYPH_ATLAS_SIZE;
ga->next_x = 0;
ga->next_y = 0;
ga->u_scale = 1.0f / (float)ga->atlas_w;
ga->v_scale = 1.0f / (float)ga->atlas_h;
uint8_t* pixels = (uint8_t*)calloc(ga->atlas_w * ga->atlas_h * 4, 1);
D3D11_TEXTURE2D_DESC td = {0};
td.Width = ga->atlas_w;
td.Height = ga->atlas_h;
td.MipLevels = 1;
td.ArraySize = 1;
td.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA sd = {0};
sd.pSysMem = pixels;
sd.SysMemPitch = ga->atlas_w * 4;
HRESULT hr = dev->lpVtbl->CreateTexture2D(dev, &td, &sd, &ga->texture);
free(pixels);
if (FAILED(hr)) return 0;
dev->lpVtbl->CreateShaderResourceView(dev, (ID3D11Resource*)ga->texture, NULL, &ga->srv);
LOG_INFO("GlyphAtlas: initialized (%dx%d, max %d glyphs)", ga->atlas_w, ga->atlas_h, MAX_GLYPHS);
return 1;
}
void glyph_atlas_destroy(GlyphAtlas* ga) {
if (!ga) return;
if (ga->srv) ga->srv->lpVtbl->Release(ga->srv);
if (ga->texture) ga->texture->lpVtbl->Release(ga->texture);
memset(ga, 0, sizeof(*ga));
g_entry_count = 0;
}
static int find_glyph(wchar_t ch) {
for (int i = 0; i < g_entry_count; i++) {
if (g_entries[i].ch == ch) return i;
}
return -1;
}
int glyph_atlas_get_or_create(GlyphAtlas* ga, ID3D11Device* dev, ID3D11DeviceContext* ctx, wchar_t ch,
float* u0, float* v0, float* u1, float* v1,
float* advance_x, float* advance_y) {
int idx = find_glyph(ch);
if (idx >= 0) {
*u0 = g_entries[idx].u0;
*v0 = g_entries[idx].v0;
*u1 = g_entries[idx].u1;
*v1 = g_entries[idx].v1;
*advance_x = g_entries[idx].advance_x;
*advance_y = (float)g_entries[idx].h;
return 1;
}
if (g_entry_count >= MAX_GLYPHS) return 0;
if (ga->next_x + ga->cell_w > ga->atlas_w) {
ga->next_x = 0;
ga->next_y += ga->cell_h;
}
if (ga->next_y + ga->cell_h > ga->atlas_h) return 0;
HDC hdc = GetDC(NULL);
HFONT hfont = CreateFontW(
ga->cell_h - 4, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, FIXED_PITCH | FF_DONTCARE, L"Microsoft YaHei"
);
HFONT old = (HFONT)SelectObject(hdc, hfont);
GLYPHMETRICS gm = {0};
MAT2 mat = {{0,0},{0,0},{0,0},{0,0}};
mat.eM11.value = 1; mat.eM22.value = 1;
DWORD size = GetGlyphOutlineW(hdc, ch, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat);
int gw = gm.gmBlackBoxX;
int gh = gm.gmBlackBoxY;
int gx = ga->next_x;
int gy = ga->next_y;
if (size > 0 && gw > 0 && gh > 0) {
uint8_t* buf = (uint8_t*)malloc(size);
GetGlyphOutlineW(hdc, ch, GGO_GRAY8_BITMAP, &gm, size, buf, &mat);
uint8_t* pixels = (uint8_t*)calloc(gw * gh * 4, 1);
int row_bytes = ((gw + 3) / 4) * 4;
for (int row = 0; row < gh; row++) {
for (int col = 0; col < gw; col++) {
int src_idx = row * row_bytes + col;
if (src_idx < (int)size) {
uint8_t gray = buf[src_idx];
uint8_t alpha = (gray > 200) ? 255 : (gray > 100) ? 200 : (gray > 30) ? 128 : 0;
int idx = (row * gw + col) * 4;
pixels[idx] = 255;
pixels[idx + 1] = 255;
pixels[idx + 2] = 255;
pixels[idx + 3] = alpha;
}
}
}
D3D11_BOX box = {0};
box.left = gx;
box.top = gy;
box.front = 0;
box.right = gx + gw;
box.bottom = gy + gh;
box.back = 1;
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)ga->texture, 0, &box, pixels, gw * 4, 0);
free(buf);
free(pixels);
}
SelectObject(hdc, old);
DeleteObject(hfont);
ReleaseDC(NULL, hdc);
GlyphEntry* e = &g_entries[g_entry_count++];
e->ch = ch;
e->x = gx;
e->y = gy;
e->w = gw > 0 ? gw : ga->cell_w / 2;
e->h = gh > 0 ? gh : ga->cell_h;
e->u0 = (float)gx * ga->u_scale;
e->v0 = (float)gy * ga->v_scale;
e->u1 = (float)(gx + e->w) * ga->u_scale;
e->v1 = (float)(gy + e->h) * ga->v_scale;
e->advance_x = (float)(gm.gmCellIncX > 0 ? gm.gmCellIncX : ga->cell_w / 2);
ga->next_x += ga->cell_w;
*u0 = e->u0;
*v0 = e->v0;
*u1 = e->u1;
*v1 = e->v1;
*advance_x = e->advance_x;
*advance_y = (float)e->h;
return 1;
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
#define GLYPH_CELL_W 32
#define GLYPH_CELL_H 32
#define GLYPH_ATLAS_COLS 16
#define GLYPH_ATLAS_ROWS 16
#define GLYPH_ATLAS_SIZE (GLYPH_ATLAS_COLS * GLYPH_CELL_W)
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
int atlas_w;
int atlas_h;
int next_x;
int next_y;
int cell_w;
int cell_h;
float u_scale;
float v_scale;
} GlyphAtlas;
int glyph_atlas_init(GlyphAtlas* ga, ID3D11Device* dev);
void glyph_atlas_destroy(GlyphAtlas* ga);
int glyph_atlas_get_or_create(GlyphAtlas* ga, ID3D11Device* dev, ID3D11DeviceContext* ctx, wchar_t ch,
float* u0, float* v0, float* u1, float* v1,
float* advance_x, float* advance_y);
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "uilayer.h"
#include "bitmap_font.h"
int textlayer_init(TextLayer* t, ID3D11Device* dev, BitmapFont* font, UILayer* ui);
void textlayer_destroy(TextLayer* t);
void textlayer_draw(TextLayer* t, ID3D11DeviceContext* ctx,
const char* text, int x, int y,
float r, float g, float b, float a,
float screen_w, float screen_h);
+252
View File
@@ -0,0 +1,252 @@
#include "uilayer.h"
#include <stdlib.h>
#include <string.h>
#include "../Core/logger.h"
#define UI_MAX_VERTS (UI_MAX_QUADS * 4)
#define UI_MAX_IDXS (UI_MAX_QUADS * 6)
typedef struct { float p[3], n[3], uv[2], c[4]; } UIVert;
static UIVert g_verts[UI_MAX_VERTS];
static uint32_t g_indices[UI_MAX_IDXS];
static int g_vi = 0;
static int g_ii = 0;
static UIVert g_text_verts[UI_MAX_VERTS];
static uint32_t g_text_indices[UI_MAX_IDXS];
static int g_tvi = 0;
static int g_tii = 0;
static ID3D11ShaderResourceView* g_text_srv = NULL;
static void screen_to_world(float sx, float sy, float sw, float sh, float z, float* wx, float* wy) {
*wx = sx - sw * 0.5f;
*wy = -(sy - sh * 0.5f);
}
int uilayer_init(UILayer* u, ID3D11Device* dev, float screen_w, float screen_h) {
memset(u, 0, sizeof(*u));
u->screen_w = screen_w;
u->screen_h = screen_h;
u->z_plane = 0.0f;
u->camera = camera_orthographic(screen_w, screen_h, 0.1f, 200.0f);
u->camera.position = (Vec3){0, 0, 100};
u->camera.target = (Vec3){0, 0, 0};
u->camera.dirty = 1;
camera_update(&u->camera);
u->shader = shader_create();
if (!shader_compile(u->shader, dev, NULL, NULL, 0, 0)) {
LOG_ERROR("UILayer: shader compile failed");
return 0;
}
D3D11_BUFFER_DESC bd = {0};
D3D11_SUBRESOURCE_DATA sd = {0};
UIVert* empty = (UIVert*)calloc(UI_MAX_VERTS, sizeof(UIVert));
bd.ByteWidth = UI_MAX_VERTS * sizeof(UIVert);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = empty;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->vb);
free(empty);
uint32_t* eidx = (uint32_t*)calloc(UI_MAX_IDXS, sizeof(uint32_t));
bd.ByteWidth = UI_MAX_IDXS * sizeof(uint32_t);
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = eidx;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->ib);
free(eidx);
UIVert* empty2 = (UIVert*)calloc(UI_MAX_VERTS, sizeof(UIVert));
bd.ByteWidth = UI_MAX_VERTS * sizeof(UIVert);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = empty2;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->text_vb);
free(empty2);
uint32_t* eidx2 = (uint32_t*)calloc(UI_MAX_IDXS, sizeof(uint32_t));
bd.ByteWidth = UI_MAX_IDXS * sizeof(uint32_t);
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = eidx2;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->text_ib);
free(eidx2);
D3D11_RASTERIZER_DESC rdesc = {0};
rdesc.FillMode = D3D11_FILL_SOLID;
rdesc.CullMode = D3D11_CULL_NONE;
dev->lpVtbl->CreateRasterizerState(dev, &rdesc, &u->rstate);
D3D11_DEPTH_STENCIL_DESC dsdesc = {0};
dsdesc.DepthEnable = FALSE;
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dev->lpVtbl->CreateDepthStencilState(dev, &dsdesc, &u->ds_state);
D3D11_BLEND_DESC bdesc = {0};
bdesc.RenderTarget[0].BlendEnable = TRUE;
bdesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bdesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bdesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bdesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bdesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
dev->lpVtbl->CreateBlendState(dev, &bdesc, &u->text_blend);
D3D11_SAMPLER_DESC sdesc = {0};
sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sdesc.AddressU = sdesc.AddressV = sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
dev->lpVtbl->CreateSamplerState(dev, &sdesc, &u->text_sampler);
const char* text_vs_src =
"cbuffer CB:register(b0){float4x4 WVP;};"
"struct VS_IN{float4 pos:POSITION;float3 n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
"struct PS_IN{float4 pos:SV_POSITION;float2 uv:TEXCOORD;float4 col:COLOR;};"
"PS_IN main(VS_IN i){PS_IN o;o.pos=mul(i.pos,WVP);o.uv=i.uv;o.col=i.col;return o;}";
const char* text_ps_src =
"Texture2D tex:register(t0);SamplerState smp:register(s0);"
"struct PS_IN{float4 pos:SV_POSITION;float2 uv:TEXCOORD;float4 col:COLOR;};"
"float4 main(PS_IN i):SV_TARGET{float a=tex.Sample(smp,i.uv).a;return float4(i.col.rgb,i.col.a*a);}";
ID3DBlob *vsb = NULL, *psb = NULL;
D3DCompile(text_vs_src, (UINT)strlen(text_vs_src), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vsb, NULL);
D3DCompile(text_ps_src, (UINT)strlen(text_ps_src), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &psb, NULL);
if (vsb && psb) {
dev->lpVtbl->CreateVertexShader(dev, vsb->lpVtbl->GetBufferPointer(vsb), vsb->lpVtbl->GetBufferSize(vsb), NULL, &u->text_vs);
dev->lpVtbl->CreatePixelShader(dev, psb->lpVtbl->GetBufferPointer(psb), psb->lpVtbl->GetBufferSize(psb), NULL, &u->text_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, vsb->lpVtbl->GetBufferPointer(vsb), vsb->lpVtbl->GetBufferSize(vsb), &u->text_layout);
vsb->lpVtbl->Release(vsb);
psb->lpVtbl->Release(psb);
}
LOG_INFO("UILayer: initialized (%.0fx%.0f)", screen_w, screen_h);
return 1;
}
void uilayer_destroy(UILayer* u) {
if (!u) return;
if (u->text_sampler) u->text_sampler->lpVtbl->Release(u->text_sampler);
if (u->text_blend) u->text_blend->lpVtbl->Release(u->text_blend);
if (u->text_layout) u->text_layout->lpVtbl->Release(u->text_layout);
if (u->text_ps) u->text_ps->lpVtbl->Release(u->text_ps);
if (u->text_vs) u->text_vs->lpVtbl->Release(u->text_vs);
if (u->text_ib) u->text_ib->lpVtbl->Release(u->text_ib);
if (u->text_vb) u->text_vb->lpVtbl->Release(u->text_vb);
if (u->ds_state) u->ds_state->lpVtbl->Release(u->ds_state);
if (u->rstate) u->rstate->lpVtbl->Release(u->rstate);
if (u->ib) u->ib->lpVtbl->Release(u->ib);
if (u->vb) u->vb->lpVtbl->Release(u->vb);
if (u->shader) shader_destroy(u->shader);
memset(u, 0, sizeof(*u));
}
void uilayer_begin(UILayer* u) {
(void)u;
g_vi = 0; g_ii = 0;
g_tvi = 0; g_tii = 0;
g_text_srv = NULL;
}
void uilayer_rect(UILayer* u, float x, float y, float w, float h,
float r, float g, float b, float a) {
if (g_vi + 4 > UI_MAX_VERTS) return;
float x0, y0, x1, y1;
screen_to_world(x, y, u->screen_w, u->screen_h, u->z_plane, &x0, &y0);
screen_to_world(x + w, y + h, u->screen_w, u->screen_h, u->z_plane, &x1, &y1);
float z = u->z_plane;
uint32_t base = (uint32_t)g_vi;
g_verts[g_vi] = (UIVert){{x0,y0,z},{0,0,1},{0,0},{r,g,b,a}}; g_vi++;
g_verts[g_vi] = (UIVert){{x1,y0,z},{0,0,1},{1,0},{r,g,b,a}}; g_vi++;
g_verts[g_vi] = (UIVert){{x1,y1,z},{0,0,1},{1,1},{r,g,b,a}}; g_vi++;
g_verts[g_vi] = (UIVert){{x0,y1,z},{0,0,1},{0,1},{r,g,b,a}}; g_vi++;
g_indices[g_ii++] = base; g_indices[g_ii++] = base+1; g_indices[g_ii++] = base+2;
g_indices[g_ii++] = base; g_indices[g_ii++] = base+2; g_indices[g_ii++] = base+3;
}
void uilayer_textured_rect(UILayer* u, ID3D11DeviceContext* ctx,
ID3D11ShaderResourceView* srv,
float x, float y, float w, float h,
float u0, float v0, float u1, float v1,
float r, float g, float b, float a) {
(void)ctx;
if (!srv || g_tvi + 4 > UI_MAX_VERTS) return;
float x0, y0, x1, y1;
screen_to_world(x, y, u->screen_w, u->screen_h, u->z_plane, &x0, &y0);
screen_to_world(x + w, y + h, u->screen_w, u->screen_h, u->z_plane, &x1, &y1);
float z = u->z_plane;
uint32_t base = (uint32_t)g_tvi;
g_text_verts[g_tvi] = (UIVert){{x0,y0,z},{0,0,1},{u0,v0},{r,g,b,a}}; g_tvi++;
g_text_verts[g_tvi] = (UIVert){{x1,y0,z},{0,0,1},{u1,v0},{r,g,b,a}}; g_tvi++;
g_text_verts[g_tvi] = (UIVert){{x1,y1,z},{0,0,1},{u1,v1},{r,g,b,a}}; g_tvi++;
g_text_verts[g_tvi] = (UIVert){{x0,y1,z},{0,0,1},{u0,v1},{r,g,b,a}}; g_tvi++;
g_text_indices[g_tii++] = base; g_text_indices[g_tii++] = base+1; g_text_indices[g_tii++] = base+2;
g_text_indices[g_tii++] = base; g_text_indices[g_tii++] = base+2; g_text_indices[g_tii++] = base+3;
g_text_srv = srv;
}
static void flush_batch(ID3D11DeviceContext* ctx, UIVert* verts, uint32_t* indices,
int num_idx, ID3D11Buffer* vb, ID3D11Buffer* ib) {
if (num_idx == 0) return;
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)vb, 0, NULL, verts, 0, 0);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)ib, 0, NULL, indices, 0, 0);
UINT stride = sizeof(UIVert), 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, (UINT)num_idx, 0, 0);
}
void uilayer_flush(UILayer* u, ID3D11DeviceContext* ctx) {
if (g_ii == 0) return;
Mat4 view = u->camera.view;
Mat4 proj = u->camera.projection;
Mat4 vp, vp_t;
mat4_multiply(&view, &proj, &vp);
mat4_transpose(&vp, &vp_t);
D3D11_VIEWPORT vport = {0};
vport.Width = u->screen_w; vport.Height = u->screen_h; vport.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vport);
ctx->lpVtbl->RSSetState(ctx, u->rstate);
ctx->lpVtbl->OMSetDepthStencilState(ctx, u->ds_state, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(u->shader, ctx);
shader_set_matrix(u->shader, ctx, (float*)&vp_t);
flush_batch(ctx, g_verts, g_indices, g_ii, u->vb, u->ib);
}
void uilayer_flush_text(UILayer* u, ID3D11DeviceContext* ctx) {
if (g_tii == 0 || !g_text_srv || !u->text_vs || !u->text_ps) return;
Mat4 view = u->camera.view;
Mat4 proj = u->camera.projection;
Mat4 vp, vp_t;
mat4_multiply(&view, &proj, &vp);
mat4_transpose(&vp, &vp_t);
D3D11_VIEWPORT vport = {0};
vport.Width = u->screen_w; vport.Height = u->screen_h; vport.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vport);
ctx->lpVtbl->RSSetState(ctx, u->rstate);
ctx->lpVtbl->OMSetDepthStencilState(ctx, u->ds_state, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->lpVtbl->VSSetShader(ctx, u->text_vs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, u->text_ps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, u->text_layout);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)u->shader->cb, 0, NULL, &vp_t, 0, 0);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &u->shader->cb);
float bf[4] = {0};
ctx->lpVtbl->OMSetBlendState(ctx, u->text_blend, bf, 0xFFFFFFFF);
ctx->lpVtbl->PSSetShaderResources(ctx, 0, 1, &g_text_srv);
ctx->lpVtbl->PSSetSamplers(ctx, 0, 1, &u->text_sampler);
flush_batch(ctx, g_text_verts, g_text_indices, g_tii, u->text_vb, u->text_ib);
}
Loaded 100 of 227 files, more files were not shown because too many files have changed in this diff. Show more