feat: major engine overhaul - scene demo with lighting, sphere, ground
- Scene Demo: cube + sphere + ground plane with proper lighting - Audio: XAudio2 dynamic loading + WAV playback - Physics: expanded collision system - Renderer: improved pipeline with materials - UI: extended widget system - VisualScript: enhanced serializer + block interactions - ASM: optimized math/memory/profiling routines - Window: mouse input + scroll support
This commit is contained in:
1 parent
2653926d85
commit
0669d102ce
35 files changed
+1844
-314
No files matched your search
+48
-7
@@ -14,18 +14,22 @@ include_directories(
|
||||
|
||||
set(ENGINE_SOURCES
|
||||
Engine/Math/math_impl.c
|
||||
Engine/Core/engine.c
|
||||
Engine/Core/logger.c
|
||||
Engine/Core/timer.c
|
||||
Engine/Core/engine.c
|
||||
Engine/Core/asset_manager.c
|
||||
Engine/Window/window.c
|
||||
Engine/Rendering/graphics_device.c
|
||||
Engine/Rendering/renderer.c
|
||||
Engine/Rendering/shader.c
|
||||
Engine/Rendering/mesh.c
|
||||
Engine/Rendering/texture.c
|
||||
Engine/Rendering/material.c
|
||||
Engine/Rendering/camera.c
|
||||
Engine/Rendering/lighting.c
|
||||
Engine/Scene/scene_graph.c
|
||||
Engine/Scene/scene_render.c
|
||||
Engine/Scene/scene_serialize.c
|
||||
Engine/ECS/world.c
|
||||
Engine/Input/input.c
|
||||
Engine/Physics/physics.c
|
||||
@@ -50,7 +54,7 @@ set(ENGINE_SOURCES
|
||||
|
||||
add_library(Engine STATIC ${ENGINE_SOURCES})
|
||||
|
||||
set(LINK_LIBS Engine d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm)
|
||||
set(LINK_LIBS Engine d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm xinput1_4 windowscodecs)
|
||||
|
||||
add_executable(lity3d Demo/main.c)
|
||||
target_link_libraries(lity3d ${LINK_LIBS})
|
||||
@@ -65,17 +69,54 @@ target_link_libraries(lity3d_compat ${LINK_LIBS})
|
||||
set_target_properties(lity3d_compat PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(ly_interpreter Demo/ly_interpreter.c)
|
||||
target_link_libraries(ly_interpreter Engine d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm)
|
||||
target_link_libraries(ly_interpreter Engine d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm xinput1_4 windowscodecs)
|
||||
set_target_properties(ly_interpreter PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(scene_test WIN32 Demo/game_test.c)
|
||||
target_link_libraries(scene_test d3d11 dxgi d3dcompiler dxguid ole32 oleaut32 uuid gdi32 user32 kernel32 winmm)
|
||||
# Engine-based demos
|
||||
add_executable(scene_test WIN32 Demo/scene_test.c)
|
||||
target_link_libraries(scene_test ${LINK_LIBS})
|
||||
set_target_properties(scene_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(scene_test2 WIN32 Demo/scene_test2.c)
|
||||
target_link_libraries(scene_test2 ${LINK_LIBS})
|
||||
set_target_properties(scene_test2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(light_test WIN32 Demo/light_test.c)
|
||||
target_link_libraries(light_test Engine d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm)
|
||||
target_link_libraries(light_test ${LINK_LIBS})
|
||||
set_target_properties(light_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(input_test WIN32 Demo/input_test.c)
|
||||
target_link_libraries(input_test Engine d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm)
|
||||
target_link_libraries(input_test ${LINK_LIBS})
|
||||
set_target_properties(input_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(mesh_debug WIN32 Demo/mesh_debug.c)
|
||||
target_link_libraries(mesh_debug ${LINK_LIBS})
|
||||
set_target_properties(mesh_debug PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(raw3d_test WIN32 Demo/raw3d_test.c)
|
||||
target_link_libraries(raw3d_test ${LINK_LIBS})
|
||||
set_target_properties(raw3d_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
# Audio demo (console, uses Engine audio system)
|
||||
add_executable(audio_demo Demo/audio_demo.c)
|
||||
target_link_libraries(audio_demo ${LINK_LIBS})
|
||||
set_target_properties(audio_demo PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
# Standalone D3D11 demos (do not link Engine)
|
||||
set(STANDALONE_D3D_LIBS d3d11 dxgi d3dcompiler ole32 oleaut32 uuid gdi32 user32 kernel32 winmm)
|
||||
|
||||
add_executable(game_test WIN32 Demo/game_test.c)
|
||||
target_link_libraries(game_test ${STANDALONE_D3D_LIBS})
|
||||
set_target_properties(game_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(cube_dodge WIN32 Demo/cube_dodge.c)
|
||||
target_link_libraries(cube_dodge ${STANDALONE_D3D_LIBS})
|
||||
set_target_properties(cube_dodge PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(obj_viewer WIN32 Demo/obj_viewer.c)
|
||||
target_link_libraries(obj_viewer ${STANDALONE_D3D_LIBS})
|
||||
set_target_properties(obj_viewer PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
|
||||
add_executable(d3d11_test WIN32 Demo/d3d11_test.c)
|
||||
target_link_libraries(d3d11_test ${STANDALONE_D3D_LIBS})
|
||||
set_target_properties(d3d11_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build)
|
||||
+2
-2
@@ -142,8 +142,8 @@ static void on_mouse_move(int x, int y) {
|
||||
g_mouse_x = x; g_mouse_y = y;
|
||||
}
|
||||
|
||||
static void on_mouse_button(int button) {
|
||||
if (button == 0) {
|
||||
static void on_mouse_button(int button, int pressed) {
|
||||
if (button == 0 && pressed) {
|
||||
if (g_mouse_click == 0) {
|
||||
g_mouse_click = 1;
|
||||
g_mouse_click_x = g_mouse_x;
|
||||
|
||||
+159
-59
@@ -1,71 +1,159 @@
|
||||
#include "../Engine/Core/engine.h"
|
||||
#include "../Engine/Core/logger.h"
|
||||
#include "../Engine/Core/timer.h"
|
||||
#include "../Engine/Window/window.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 GraphicsDevice* g_dev = NULL;
|
||||
static Shader* g_shader = NULL;
|
||||
static Mesh* g_cube = NULL;
|
||||
static Scene* g_scene = NULL;
|
||||
static AssetManager* g_assets = NULL;
|
||||
static Asset* g_shader_asset = NULL;
|
||||
static Camera g_camera;
|
||||
static float g_angle = 0.0f;
|
||||
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 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 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) {
|
||||
if (!g_dev || !g_shader || !g_cube) return;
|
||||
Renderer* r = engine_get_renderer(engine);
|
||||
GraphicsDevice* dev = engine_get_graphics_device(engine);
|
||||
if (!r || !dev || !g_scene) return;
|
||||
|
||||
float w = (float)window_get_width(engine->window);
|
||||
float h = (float)window_get_height(engine->window);
|
||||
renderer_set_view(r, camera_get_view(&g_camera));
|
||||
renderer_set_projection(r, camera_get_projection(&g_camera));
|
||||
|
||||
graphics_device_clear(g_dev, 0.15f, 0.15f, 0.2f, 1.0f);
|
||||
renderer_begin_frame(r);
|
||||
scene_update(g_scene);
|
||||
scene_render(g_scene, r);
|
||||
renderer_end_frame(r);
|
||||
|
||||
g_dev->context->lpVtbl->IASetPrimitiveTopology(g_dev->context, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
if (engine->frame_count == 60) {
|
||||
graphics_device_save_screenshot(dev, "screenshot1.bmp");
|
||||
}
|
||||
if (engine->frame_count == 180) {
|
||||
graphics_device_save_screenshot(dev, "screenshot2.bmp");
|
||||
}
|
||||
}
|
||||
|
||||
Mat4 model, wvp, wvp_t;
|
||||
mat4_rotate_y(g_angle, &model);
|
||||
Mat4* view = (Mat4*)camera_get_view(&g_camera);
|
||||
Mat4* proj = (Mat4*)camera_get_projection(&g_camera);
|
||||
|
||||
Mat4 vp;
|
||||
mat4_multiply(view, proj, &vp);
|
||||
mat4_multiply(&model, &vp, &wvp);
|
||||
mat4_transpose(&wvp, &wvp_t);
|
||||
|
||||
shader_bind(g_shader, g_dev->context);
|
||||
shader_set_matrix(g_shader, g_dev->context, (float*)&wvp_t);
|
||||
|
||||
ID3D11DepthStencilView* dsv = g_dev->depth_stencil;
|
||||
g_dev->context->lpVtbl->OMSetRenderTargets(g_dev->context, 1, &g_dev->render_target, dsv);
|
||||
D3D11_VIEWPORT vp2 = {0};
|
||||
vp2.Width = w; vp2.Height = h; vp2.MaxDepth = 1.0f;
|
||||
g_dev->context->lpVtbl->RSSetViewports(g_dev->context, 1, &vp2);
|
||||
g_dev->context->lpVtbl->RSSetState(g_dev->context, NULL);
|
||||
|
||||
mesh_draw(g_cube, g_dev->context);
|
||||
graphics_device_present(g_dev);
|
||||
|
||||
g_angle += 0.02f;
|
||||
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) {
|
||||
if (g_dev && width > 0 && height > 0) {
|
||||
graphics_device_resize(g_dev, width, 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;
|
||||
}
|
||||
@@ -75,7 +163,7 @@ int main(int argc, char* argv[]) {
|
||||
(void)argc; (void)argv;
|
||||
|
||||
EngineConfig config = engine_default_config();
|
||||
config.app_name = "MiMo Engine - Cube Demo";
|
||||
config.app_name = "MiMo Engine - Scene Demo";
|
||||
config.vsync = 0;
|
||||
config.window_width = 1280;
|
||||
config.window_height = 720;
|
||||
@@ -86,39 +174,51 @@ int main(int argc, char* argv[]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_dev = graphics_device_create();
|
||||
if (!graphics_device_init(g_dev, window_get_handle(engine->window), config.window_width, config.window_height, 0)) {
|
||||
LOG_FATAL("D3D11 init failed");
|
||||
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("D3D11 device ready");
|
||||
LOG_INFO("Shader loaded via AssetManager");
|
||||
|
||||
g_shader = shader_create();
|
||||
if (!shader_compile(g_shader, g_dev->device, NULL, NULL, 0, 0)) {
|
||||
LOG_FATAL("Shader compile failed");
|
||||
return -1;
|
||||
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");
|
||||
}
|
||||
}
|
||||
LOG_INFO("Shader compiled");
|
||||
|
||||
g_cube = mesh_create_cube(g_dev->device);
|
||||
LOG_INFO("Cube mesh created");
|
||||
|
||||
float aspect = (float)config.window_width / (float)config.window_height;
|
||||
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, 1.5f, -4.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 cube demo...");
|
||||
LOG_INFO("Running scene demo...");
|
||||
engine_run(engine);
|
||||
|
||||
mesh_destroy(g_cube);
|
||||
shader_destroy(g_shader);
|
||||
graphics_device_destroy(g_dev);
|
||||
scene_destroy_renderers(g_scene);
|
||||
scene_destroy(g_scene);
|
||||
free(g_scene);
|
||||
asset_manager_destroy(g_assets);
|
||||
engine_shutdown(engine);
|
||||
return 0;
|
||||
}
|
||||
@@ -125,3 +125,13 @@ 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
|
||||
+79
-40
@@ -514,9 +514,13 @@ mat4_scale_f ENDP
|
||||
mat4_rotate_x PROC
|
||||
; cos(angle), sin(angle)
|
||||
movss xmm0, dword ptr [rcx] ; angle
|
||||
movss xmm1, xmm0
|
||||
cosss xmm0 ; cos
|
||||
sinss xmm1 ; sin
|
||||
movss xmm1, xmm0 ; save angle
|
||||
call cosf ; xmm0 = cos
|
||||
movss xmm2, xmm0 ; save cos
|
||||
movss xmm0, xmm1 ; restore angle
|
||||
call sinf ; xmm0 = sin
|
||||
movss xmm1, xmm0 ; xmm1 = sin
|
||||
movss xmm0, xmm2 ; xmm0 = cos
|
||||
mov rax, rdx ; out
|
||||
|
||||
movss dword ptr [rax], 3F800000h ; m[0][0] = 1.0
|
||||
@@ -550,9 +554,13 @@ mat4_rotate_x ENDP
|
||||
; void mat4_rotate_y(float angle, Mat4* out)
|
||||
mat4_rotate_y PROC
|
||||
movss xmm0, dword ptr [rcx]
|
||||
movss xmm1, xmm0
|
||||
cosss xmm0
|
||||
sinss xmm1
|
||||
movss xmm1, xmm0 ; save angle
|
||||
call cosf ; xmm0 = cos
|
||||
movss xmm2, xmm0 ; save cos
|
||||
movss xmm0, xmm1 ; restore angle
|
||||
call sinf ; xmm0 = sin
|
||||
movss xmm1, xmm0 ; xmm1 = sin
|
||||
movss xmm0, xmm2 ; xmm0 = cos
|
||||
mov rax, rdx
|
||||
|
||||
movss dword ptr [rax], xmm0 ; m[0][0] = cos
|
||||
@@ -583,9 +591,13 @@ mat4_rotate_y ENDP
|
||||
; void mat4_rotate_z(float angle, Mat4* out)
|
||||
mat4_rotate_z PROC
|
||||
movss xmm0, dword ptr [rcx]
|
||||
movss xmm1, xmm0
|
||||
cosss xmm0
|
||||
sinss xmm1
|
||||
movss xmm1, xmm0 ; save angle
|
||||
call cosf ; xmm0 = cos
|
||||
movss xmm2, xmm0 ; save cos
|
||||
movss xmm0, xmm1 ; restore angle
|
||||
call sinf ; xmm0 = sin
|
||||
movss xmm1, xmm0 ; xmm1 = sin
|
||||
movss xmm0, xmm2 ; xmm0 = cos
|
||||
mov rax, rdx
|
||||
|
||||
movss dword ptr [rax], xmm0 ; m[0][0] = cos
|
||||
@@ -621,7 +633,9 @@ mat4_perspective PROC
|
||||
; tan(fovY/2)
|
||||
movss xmm4, xmm0
|
||||
mulss xmm4, dword ptr [_half] ; fovY/2
|
||||
tanss xmm4, xmm4 ; tan(fovY/2)
|
||||
movss xmm0, xmm4
|
||||
call tanf
|
||||
movss xmm4, xmm0 ; xmm4 = tan(fovY/2)
|
||||
|
||||
; 1 / (aspect * tan)
|
||||
movss xmm5, xmm1
|
||||
@@ -970,7 +984,8 @@ quat_slerp PROC
|
||||
subss xmm1, dword ptr [rdx]
|
||||
movss dword ptr [rsp - 16], xmm1
|
||||
; ... (需要保存到临时空间)
|
||||
negss xmm0 ; dot = -dot
|
||||
movss xmm1, dword ptr [_negate_mask] ; 0x80000000
|
||||
xorps xmm0, xmm1 ; dot = -dot
|
||||
slerp_no_flip:
|
||||
|
||||
; 简化: 线性插值 + 归一化
|
||||
@@ -1040,7 +1055,10 @@ quat_from_axis_angle PROC
|
||||
mulss xmm0, dword ptr [_half] ; halfAngle
|
||||
|
||||
; s = sin(halfAngle)
|
||||
sinss xmm1, xmm0
|
||||
movss xmm2, xmm0 ; save halfAngle
|
||||
call sinf
|
||||
movss xmm1, xmm0 ; xmm1 = sin
|
||||
movss xmm0, xmm2 ; restore halfAngle
|
||||
|
||||
; 归一化轴
|
||||
movss xmm2, dword ptr [rcx]
|
||||
@@ -1074,9 +1092,7 @@ quat_from_axis_angle PROC
|
||||
movss dword ptr [r8 + 8], xmm0
|
||||
|
||||
; out.w = cos(halfAngle)
|
||||
movss xmm0, dword ptr [rdx]
|
||||
mulss xmm0, dword ptr [_half]
|
||||
cosss xmm0, xmm0
|
||||
call cosf
|
||||
movss dword ptr [r8 + 12], xmm0
|
||||
|
||||
ret
|
||||
@@ -1090,21 +1106,42 @@ quat_from_euler PROC
|
||||
mulss xmm1, dword ptr [_half] ; yaw/2
|
||||
mulss xmm2, dword ptr [_half] ; roll/2
|
||||
|
||||
; cos/sin
|
||||
cosss xmm3, xmm0 ; cp
|
||||
sinss xmm4, xmm0 ; sp
|
||||
cosss xmm5, xmm1 ; cy
|
||||
sinss xmm6, xmm1 ; sy
|
||||
cosss xmm7, xmm2 ; cr
|
||||
; sr 需要额外寄存器
|
||||
sub rsp, 8
|
||||
sinss xmm0, xmm2 ; sr
|
||||
movss dword ptr [rsp], xmm0
|
||||
; 保存半角并计算 cos/sin
|
||||
sub rsp, 48
|
||||
movss dword ptr [rsp + 0], xmm0 ; pitch/2
|
||||
movss dword ptr [rsp + 4], xmm1 ; yaw/2
|
||||
movss dword ptr [rsp + 8], xmm2 ; roll/2
|
||||
|
||||
call cosf
|
||||
movss dword ptr [rsp + 12], xmm0 ; cp
|
||||
movss xmm0, dword ptr [rsp + 0]
|
||||
call sinf
|
||||
movss dword ptr [rsp + 16], xmm0 ; sp
|
||||
|
||||
movss xmm0, dword ptr [rsp + 4]
|
||||
call cosf
|
||||
movss dword ptr [rsp + 20], xmm0 ; cy
|
||||
movss xmm0, dword ptr [rsp + 4]
|
||||
call sinf
|
||||
movss dword ptr [rsp + 24], xmm0 ; sy
|
||||
|
||||
movss xmm0, dword ptr [rsp + 8]
|
||||
call cosf
|
||||
movss dword ptr [rsp + 28], xmm0 ; cr
|
||||
movss xmm0, dword ptr [rsp + 8]
|
||||
call sinf
|
||||
movss dword ptr [rsp + 32], xmm0 ; sr
|
||||
|
||||
movss xmm3, dword ptr [rsp + 12] ; cp
|
||||
movss xmm4, dword ptr [rsp + 16] ; sp
|
||||
movss xmm5, dword ptr [rsp + 20] ; cy
|
||||
movss xmm6, dword ptr [rsp + 24] ; sy
|
||||
movss xmm7, dword ptr [rsp + 28] ; cr
|
||||
movss xmm2, dword ptr [rsp + 32] ; xmm2 = sr
|
||||
add rsp, 48
|
||||
|
||||
; out.x = sr*cp*cy - cr*sp*sy
|
||||
movss xmm0, xmm7 ; cr... 需要重新组织
|
||||
; 简化: 使用栈
|
||||
movss xmm0, dword ptr [rsp] ; sr
|
||||
movss xmm0, xmm2 ; sr
|
||||
mulss xmm0, xmm3 ; sr*cp
|
||||
mulss xmm0, xmm5 ; sr*cp*cy
|
||||
movss xmm1, xmm7 ; cr
|
||||
@@ -1117,9 +1154,10 @@ quat_from_euler PROC
|
||||
movss xmm0, xmm7
|
||||
mulss xmm0, xmm4
|
||||
mulss xmm0, xmm5
|
||||
movss xmm1, dword ptr [rsp]
|
||||
mulss xmm1, xmm3
|
||||
mulss xmm1, xmm6
|
||||
movss xmm1, xmm0 ; cr*sp*cy
|
||||
movss xmm0, xmm2 ; sr (saved above in xmm2)
|
||||
mulss xmm0, xmm3
|
||||
mulss xmm0, xmm6
|
||||
addss xmm0, xmm1
|
||||
movss dword ptr [rcx + 4], xmm0
|
||||
|
||||
@@ -1127,23 +1165,24 @@ quat_from_euler PROC
|
||||
movss xmm0, xmm7
|
||||
mulss xmm0, xmm3
|
||||
mulss xmm0, xmm6
|
||||
movss xmm1, dword ptr [rsp]
|
||||
mulss xmm1, xmm4
|
||||
mulss xmm1, xmm5
|
||||
subss xmm0, xmm1
|
||||
movss dword ptr [rcx + 8], xmm0
|
||||
movss xmm1, xmm0 ; cr*cp*sy
|
||||
movss xmm0, xmm2 ; sr
|
||||
mulss xmm0, xmm4
|
||||
mulss xmm0, xmm5
|
||||
subss xmm1, xmm0
|
||||
movss dword ptr [rcx + 8], xmm1
|
||||
|
||||
; out.w = cr*cp*cy + sr*sp*sy
|
||||
movss xmm0, xmm7
|
||||
mulss xmm0, xmm3
|
||||
mulss xmm0, xmm5
|
||||
movss xmm1, dword ptr [rsp]
|
||||
mulss xmm1, xmm4
|
||||
mulss xmm1, xmm6
|
||||
movss xmm1, xmm0 ; cr*cp*cy
|
||||
movss xmm0, xmm2 ; sr
|
||||
mulss xmm0, xmm4
|
||||
mulss xmm0, xmm6
|
||||
addss xmm0, xmm1
|
||||
movss dword ptr [rcx + 12], xmm0
|
||||
|
||||
add rsp, 8
|
||||
ret
|
||||
quat_from_euler ENDP
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#ifndef MATH_ASM_AVAILABLE
|
||||
#if !defined(MATH_ASM_AVAILABLE) || MATH_ASM_AVAILABLE == 0
|
||||
|
||||
typedef struct { float x, y, z; } Vec3;
|
||||
typedef struct { float x, y, z, w; } Vec4;
|
||||
@@ -37,6 +37,30 @@ static inline void quat_normalize(const Quat* q, Quat* o) { float l=sqrtf(q->x*q
|
||||
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); }
|
||||
|
||||
|
||||
+17
-14
@@ -114,7 +114,7 @@ mem_aligned_realloc PROC
|
||||
push rdi
|
||||
push rsi
|
||||
push r12
|
||||
sub rsp, 40
|
||||
sub rsp, 56 ; shadow + 3 saved qwords
|
||||
|
||||
mov rbx, rdx ; rbx = new size
|
||||
mov rdi, r8 ; rdi = alignment
|
||||
@@ -124,8 +124,11 @@ mem_aligned_realloc PROC
|
||||
jz realloc_alloc
|
||||
|
||||
; 保存旧信息
|
||||
mov rsi, [rcx - 8] ; 原始指针
|
||||
mov r12, [rcx - 16] ; 旧 size
|
||||
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
|
||||
@@ -134,26 +137,26 @@ mem_aligned_realloc PROC
|
||||
test rax, rax
|
||||
jz realloc_failed
|
||||
|
||||
mov rsi, rax ; rsi = 新对齐指针
|
||||
|
||||
; 复制旧数据 (取较小值)
|
||||
mov rdx, r12
|
||||
mov rdx, [rsp + 16] ; 旧 size
|
||||
cmp rdx, rbx
|
||||
jbe realloc_copy_ok
|
||||
mov rdx, rbx
|
||||
|
||||
realloc_copy_ok:
|
||||
mov rdi, rax ; 保存新指针
|
||||
mov rcx, rax ; dst
|
||||
; rsi = 旧数据起始
|
||||
mov rsi, [rcx - 8] ; 旧原始指针
|
||||
; 需要获取旧的对齐偏移
|
||||
; 简化: 直接复制
|
||||
; fast_memcpy(dst, src, count): rcx=dst, rdx=src, r8=count
|
||||
mov rcx, rsi
|
||||
mov r8, rdx
|
||||
mov rdx, [rsp + 32] ; 旧对齐指针
|
||||
call fast_memcpy
|
||||
|
||||
; 释放旧内存
|
||||
mov rcx, rsi
|
||||
; 释放旧内存(使用原始 malloc 指针)
|
||||
mov rcx, [rsp + 24]
|
||||
call mem_aligned_free
|
||||
|
||||
mov rax, rdi ; 返回新指针
|
||||
mov rax, rsi ; 返回新指针
|
||||
jmp realloc_done
|
||||
|
||||
realloc_alloc:
|
||||
@@ -166,7 +169,7 @@ realloc_failed:
|
||||
xor rax, rax
|
||||
|
||||
realloc_done:
|
||||
add rsp, 40
|
||||
add rsp, 56
|
||||
pop r12
|
||||
pop rsi
|
||||
pop rdi
|
||||
|
||||
+25
-49
@@ -28,14 +28,12 @@ prof_rdtsc PROC
|
||||
prof_rdtsc ENDP
|
||||
|
||||
; uint64_t prof_get_freq(void) -> RAX
|
||||
; 获取 CPU 频率 (通过 OS 或推断)
|
||||
; 获取 CPU 频率 (Hz), 使用 QPC 校准 TSC
|
||||
prof_get_freq PROC
|
||||
; 检查是否已初始化
|
||||
cmp byte ptr [cpu_freq_init], 0
|
||||
jnz prof_freq_return
|
||||
|
||||
; 初始化 CPU 频率
|
||||
; 方法: 使用 QueryPerformanceCounter 测量 RDTSC
|
||||
push rbx
|
||||
push rdi
|
||||
push rsi
|
||||
@@ -46,7 +44,7 @@ prof_get_freq PROC
|
||||
call QueryPerformanceFrequency
|
||||
|
||||
; 获取起始 QPC 和 TSC
|
||||
lea rcx, [rsp + 16]
|
||||
lea rcx, [rsp + 24]
|
||||
call QueryPerformanceCounter
|
||||
rdtscp
|
||||
shl rdx, 32
|
||||
@@ -54,37 +52,31 @@ prof_get_freq PROC
|
||||
mov rbx, rax ; rbx = 起始 TSC
|
||||
|
||||
; 等待约 10ms
|
||||
mov rcx, 10000 ; 10,000 微秒
|
||||
mov rcx, 10 ; 10 毫秒
|
||||
call Sleep
|
||||
|
||||
; 获取结束 QPC 和 TSC
|
||||
lea rcx, [rsp]
|
||||
lea rcx, [rsp + 16]
|
||||
call QueryPerformanceCounter
|
||||
rdtscp
|
||||
shl rdx, 32
|
||||
or rax, rdx
|
||||
mov rdi, rax ; rdi = 结束 TSC
|
||||
|
||||
; 计算经过的 QPC ticks
|
||||
mov rax, [rsp]
|
||||
sub rax, [rsp + 16] ; qpc_diff
|
||||
; qpc_diff = end_qpc - start_qpc
|
||||
mov rax, [rsp + 16]
|
||||
sub rax, [rsp + 24]
|
||||
mov rsi, rax ; rsi = qpc_diff
|
||||
|
||||
; 计算 QPC 频率 (ticks/sec)
|
||||
mov rsi, [rsp + 32]
|
||||
; tsc_diff = end_tsc - start_tsc
|
||||
mov rcx, rdi
|
||||
sub rcx, rbx ; rcx = tsc_diff
|
||||
|
||||
; TSC diff
|
||||
mov rax, rdi
|
||||
sub rax, rbx ; tsc_diff
|
||||
|
||||
; CPU freq = tsc_diff * qpc_freq / qpc_diff
|
||||
; 但 qpc_diff 对应 10ms, 所以要 * 100
|
||||
mov rdx, rsi
|
||||
mul rdx ; tsc_diff * qpc_freq
|
||||
; rdx:rax = result
|
||||
div qword ptr [rsp + 16] ; / qpc_diff
|
||||
; rax = freq * 100 (因为等待了 10ms)
|
||||
mov rcx, 100
|
||||
mul rcx
|
||||
; 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
|
||||
@@ -100,33 +92,17 @@ prof_freq_return:
|
||||
prof_get_freq ENDP
|
||||
|
||||
; double prof_cycles_to_ms(uint64_t cycles) -> XMM0
|
||||
; 将 TSC cycles 转换为毫秒
|
||||
; 将 TSC cycles 转换为毫秒: ms = cycles * 1000 / freq
|
||||
prof_cycles_to_ms PROC
|
||||
; 先获取频率
|
||||
call prof_get_freq
|
||||
; rax = CPU freq (Hz)
|
||||
mov rdx, rcx ; 保存 cycles
|
||||
call prof_get_freq ; rax = CPU freq (Hz)
|
||||
|
||||
; ms = cycles / (freq / 1000)
|
||||
; 或: ms = cycles * 1000 / freq
|
||||
mov rdx, 1000
|
||||
mul rdx ; freq * 1000
|
||||
; rdx:rax = freq * 1000
|
||||
|
||||
; 转换为 double
|
||||
cvtsi2sd xmm0, qword ptr [rsp - 8] ; 需要栈存储
|
||||
; 简化: 使用整数除法再转 double
|
||||
mov rcx, rax
|
||||
mov rax, [cpu_freq]
|
||||
; 重新计算
|
||||
mov rax, rcx
|
||||
; cycles * 1000 / freq
|
||||
; 已经: rax = freq * 1000, 需要 cycles * 1000 / freq
|
||||
; 调整: cycles * 1000 / freq = cycles / (freq/1000)
|
||||
|
||||
; 简化实现
|
||||
cvtsi2sd xmm0, rcx ; cycles * 1000 (转 double)
|
||||
cvtsi2sd xmm1, rax ; freq (转 double)
|
||||
divsd xmm0, xmm1 ; (cycles*1000) / freq = ms
|
||||
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
|
||||
|
||||
|
||||
@@ -255,42 +255,21 @@ fast_memset ENDP
|
||||
; uint64_t fast_strlen(const char* str) -> RAX
|
||||
; 使用 SSE4.2 PCMPESTRI 搜索 null terminator
|
||||
fast_strlen PROC
|
||||
mov rax, rcx ; rax = str
|
||||
xor rcx, rcx ; rcx = 计数器
|
||||
|
||||
; 每次检查 16 字节
|
||||
mov rdx, rcx ; rdx = original str
|
||||
pxor xmm0, xmm0 ; 搜索 0
|
||||
|
||||
strlen_sse_loop:
|
||||
; 检查是否接近字符串末尾 (避免越界读取)
|
||||
; 简化: 逐 16 字节检查
|
||||
movdqu xmm1, xmmword ptr [rax + rcx]
|
||||
pcmpestrm xmm0, xmm1, 00001000b ; 搜索 0, 返回掩码
|
||||
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:
|
||||
; 获取第一个匹配位置
|
||||
pcmpestri xmm0, xmm1, 00001000b ; ECX = 匹配位置
|
||||
; rax = 块起始, rcx (ecx) = 块内偏移
|
||||
add rax, rcx
|
||||
add rax, rcx
|
||||
; 需要计算实际位置
|
||||
; 简化: 使用标量回退
|
||||
mov rax, rcx
|
||||
ret
|
||||
|
||||
; 标量回退
|
||||
strlen_scalar:
|
||||
mov rax, rcx
|
||||
strlen_scalar_loop:
|
||||
cmp byte ptr [rax], 0
|
||||
je strlen_scalar_done
|
||||
inc rax
|
||||
jmp strlen_scalar_loop
|
||||
strlen_scalar_done:
|
||||
sub rax, rcx
|
||||
sub rax, rdx
|
||||
ret
|
||||
fast_strlen ENDP
|
||||
|
||||
|
||||
+253
-10
@@ -1,34 +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);
|
||||
}
|
||||
|
||||
void audio_source_play(AudioSource* source) { (void)source; }
|
||||
void audio_source_stop(AudioSource* source) { (void)source; }
|
||||
void audio_source_pause(AudioSource* source) { (void)source; }
|
||||
void audio_source_resume(AudioSource* source) { (void)source; }
|
||||
void audio_source_set_volume(AudioSource* source, float v) { if (source) source->volume = v; }
|
||||
void audio_source_set_pitch(AudioSource* source, float p) { if (source) source->pitch = p; }
|
||||
void audio_source_set_loop(AudioSource* source, int l) { if (source) source->loop = l; }
|
||||
int audio_load_wav(AudioSource* source, const char* path) { (void)source; (void)path; return 0; }
|
||||
int audio_load_wav_memory(AudioSource* source, const uint8_t* data, uint32_t size) { (void)source; (void)data; (void)size; return 0; }
|
||||
void audio_set_master_volume(AudioSystem* audio, float v) { (void)audio; (void)v; }
|
||||
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);
|
||||
}
|
||||
+14
-1
@@ -7,17 +7,30 @@
|
||||
#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 {
|
||||
typedef struct AudioSystem {
|
||||
IXAudio2* xaudio2;
|
||||
IXAudio2MasteringVoice* mastering_voice;
|
||||
} AudioSystem;
|
||||
|
||||
+66
-1
@@ -6,6 +6,8 @@
|
||||
#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>
|
||||
@@ -18,6 +20,8 @@ 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) {
|
||||
@@ -35,6 +39,18 @@ static void on_resize(int width, int 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;
|
||||
}
|
||||
@@ -73,9 +89,37 @@ int engine_init(Engine* engine, const EngineConfig* config) {
|
||||
// 设置回调
|
||||
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;
|
||||
@@ -143,10 +187,15 @@ void engine_shutdown(Engine* engine) {
|
||||
LOG_INFO("Shutting down MiMo Engine ASM...");
|
||||
|
||||
if (engine->renderer) {
|
||||
// renderer_destroy(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;
|
||||
@@ -177,3 +226,19 @@ void engine_set_key_callback(Engine* engine, EngineKeyCallback 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;
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
// 前向声明
|
||||
typedef struct Window Window;
|
||||
typedef struct GraphicsDevice GraphicsDevice;
|
||||
typedef struct Renderer Renderer;
|
||||
|
||||
// 引擎配置
|
||||
typedef struct {
|
||||
@@ -25,6 +27,7 @@ typedef struct {
|
||||
EngineConfig config;
|
||||
Window* window;
|
||||
void* renderer;
|
||||
GraphicsDevice* graphics_device;
|
||||
double delta_time;
|
||||
double total_time;
|
||||
int fps;
|
||||
@@ -55,8 +58,16 @@ 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);
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
static float normalize_trigger(BYTE value) {
|
||||
return value / 255.0f;
|
||||
|
||||
+177
-6
@@ -1,9 +1,12 @@
|
||||
#include "physics.h"
|
||||
#include "../include/engine_api.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
static void move_collider(Collider* c, Vec3 delta);
|
||||
|
||||
PhysicsWorld* physics_create(void) {
|
||||
PhysicsWorld* w = (PhysicsWorld*)calloc(1, sizeof(PhysicsWorld));
|
||||
w->gravity_enabled = 1;
|
||||
@@ -15,6 +18,15 @@ void physics_destroy(PhysicsWorld* world) {
|
||||
free(world);
|
||||
}
|
||||
|
||||
static void init_body(RigidBody* b) {
|
||||
b->velocity = (Vec3){0, 0, 0};
|
||||
b->acceleration = (Vec3){0, 0, 0};
|
||||
b->mass = 1.0f;
|
||||
b->restitution = 0.5f;
|
||||
b->friction = 0.3f;
|
||||
b->is_kinematic = 0;
|
||||
}
|
||||
|
||||
uint32_t physics_add_aabb(PhysicsWorld* world, uint32_t entity, Vec3 min, Vec3 max) {
|
||||
uint32_t idx = world->collider_count++;
|
||||
Collider* c = &world->colliders[idx];
|
||||
@@ -24,6 +36,7 @@ uint32_t physics_add_aabb(PhysicsWorld* world, uint32_t entity, Vec3 min, Vec3 m
|
||||
c->aabb.max = max;
|
||||
c->is_trigger = 0;
|
||||
c->is_static = 0;
|
||||
init_body(&c->body);
|
||||
return idx;
|
||||
}
|
||||
|
||||
@@ -34,6 +47,9 @@ uint32_t physics_add_sphere(PhysicsWorld* world, uint32_t entity, Vec3 center, f
|
||||
c->type = COLLIDER_SPHERE;
|
||||
c->sphere.center = center;
|
||||
c->sphere.radius = radius;
|
||||
c->is_trigger = 0;
|
||||
c->is_static = 0;
|
||||
init_body(&c->body);
|
||||
return idx;
|
||||
}
|
||||
|
||||
@@ -44,7 +60,11 @@ uint32_t physics_add_plane(PhysicsWorld* world, uint32_t entity, Vec3 normal, fl
|
||||
c->type = COLLIDER_PLANE;
|
||||
vec3_normalize(&normal, &c->plane.normal);
|
||||
c->plane.distance = distance;
|
||||
c->is_trigger = 0;
|
||||
c->is_static = 1;
|
||||
init_body(&c->body);
|
||||
c->body.mass = 0.0f;
|
||||
c->body.is_kinematic = 1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
@@ -55,9 +75,19 @@ void physics_remove_collider(PhysicsWorld* world, uint32_t index) {
|
||||
}
|
||||
|
||||
void physics_update_collider(PhysicsWorld* world, uint32_t index, Vec3 position) {
|
||||
if (!world || index >= world->collider_count) return;
|
||||
Collider* c = &world->colliders[index];
|
||||
if (c->type == COLLIDER_SPHERE) {
|
||||
c->sphere.center = position;
|
||||
} else if (c->type == COLLIDER_AABB) {
|
||||
Vec3 center = {
|
||||
(c->aabb.min.x + c->aabb.max.x) * 0.5f,
|
||||
(c->aabb.min.y + c->aabb.max.y) * 0.5f,
|
||||
(c->aabb.min.z + c->aabb.max.z) * 0.5f
|
||||
};
|
||||
Vec3 delta;
|
||||
vec3_sub(&position, ¢er, &delta);
|
||||
move_collider(c, delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +96,7 @@ static int aabb_vs_aabb(const Collider* a, const Collider* b, Vec3* normal, floa
|
||||
Vec3 centerA = {(a->aabb.min.x + a->aabb.max.x) * 0.5f, (a->aabb.min.y + a->aabb.max.y) * 0.5f, (a->aabb.min.z + a->aabb.max.z) * 0.5f};
|
||||
Vec3 centerB = {(b->aabb.min.x + b->aabb.max.x) * 0.5f, (b->aabb.min.y + b->aabb.max.y) * 0.5f, (b->aabb.min.z + b->aabb.max.z) * 0.5f};
|
||||
vec3_sub(¢erA, ¢erB, &diff);
|
||||
Vec3 half_a, half_b, size;
|
||||
Vec3 half_a, half_b;
|
||||
vec3_sub(&a->aabb.max, &a->aabb.min, &half_a);
|
||||
vec3_scale(&half_a, 0.5f, &half_a);
|
||||
vec3_sub(&b->aabb.max, &b->aabb.min, &half_b);
|
||||
@@ -78,9 +108,9 @@ static int aabb_vs_aabb(const Collider* a, const Collider* b, Vec3* normal, floa
|
||||
|
||||
if (dx < 0 && dy < 0 && dz < 0) {
|
||||
*depth = -fmaxf(fmaxf(dx, dy), dz);
|
||||
if (dx > dy && dx > dz) normal = &(Vec3){copysignf(1, diff.x), 0, 0};
|
||||
else if (dy > dz) { normal->x = 0; normal->y = copysignf(1, diff.y); normal->z = 0; }
|
||||
else { normal->x = 0; normal->y = 0; normal->z = copysignf(1, diff.z); }
|
||||
if (dx > dy && dx > dz) { *normal = (Vec3){copysignf(1.0f, diff.x), 0, 0}; }
|
||||
else if (dy > dz) { normal->x = 0; normal->y = copysignf(1.0f, diff.y); normal->z = 0; }
|
||||
else { normal->x = 0; normal->y = 0; normal->z = copysignf(1.0f, diff.z); }
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -192,9 +222,150 @@ int physics_raycast(PhysicsWorld* world, Vec3 origin, Vec3 direction, float max_
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int body_is_movable(Collider* c) {
|
||||
return !c->is_static && !c->body.is_kinematic && c->body.mass > 0.0f;
|
||||
}
|
||||
|
||||
static float body_inv_mass(Collider* c) {
|
||||
if (c->is_static || c->body.is_kinematic || c->body.mass <= 0.0f) return 0.0f;
|
||||
return 1.0f / c->body.mass;
|
||||
}
|
||||
|
||||
static void move_collider(Collider* c, Vec3 delta) {
|
||||
if (c->type == COLLIDER_SPHERE) {
|
||||
vec3_add(&c->sphere.center, &delta, &c->sphere.center);
|
||||
} else if (c->type == COLLIDER_AABB) {
|
||||
vec3_add(&c->aabb.min, &delta, &c->aabb.min);
|
||||
vec3_add(&c->aabb.max, &delta, &c->aabb.max);
|
||||
}
|
||||
}
|
||||
|
||||
RigidBody* physics_get_body(PhysicsWorld* world, uint32_t index) {
|
||||
if (!world || index >= world->collider_count) return NULL;
|
||||
return &world->colliders[index].body;
|
||||
}
|
||||
|
||||
void physics_apply_force(PhysicsWorld* world, uint32_t index, Vec3 force) {
|
||||
RigidBody* b = physics_get_body(world, index);
|
||||
if (!b || b->mass <= 0.0f) return;
|
||||
Vec3 scaled;
|
||||
vec3_scale(&force, 1.0f / b->mass, &scaled);
|
||||
vec3_add(&b->acceleration, &scaled, &b->acceleration);
|
||||
}
|
||||
|
||||
void physics_apply_impulse(PhysicsWorld* world, uint32_t index, Vec3 impulse) {
|
||||
RigidBody* b = physics_get_body(world, index);
|
||||
if (!b || b->mass <= 0.0f) return;
|
||||
Vec3 scaled;
|
||||
vec3_scale(&impulse, 1.0f / b->mass, &scaled);
|
||||
vec3_add(&b->velocity, &scaled, &b->velocity);
|
||||
}
|
||||
|
||||
void physics_step(PhysicsWorld* world, float dt) {
|
||||
(void)world;
|
||||
(void)dt;
|
||||
if (!world || world->collider_count == 0 || dt <= 0.0f) return;
|
||||
|
||||
const float linear_damping = 0.995f;
|
||||
|
||||
// 1. 积分动态刚体
|
||||
for (uint32_t i = 0; i < world->collider_count; i++) {
|
||||
Collider* c = &world->colliders[i];
|
||||
if (!body_is_movable(c)) continue;
|
||||
|
||||
RigidBody* b = &c->body;
|
||||
if (world->gravity_enabled) {
|
||||
vec3_add(&b->acceleration, &world->gravity, &b->acceleration);
|
||||
}
|
||||
|
||||
Vec3 dv;
|
||||
vec3_scale(&b->acceleration, dt, &dv);
|
||||
vec3_add(&b->velocity, &dv, &b->velocity);
|
||||
b->acceleration = (Vec3){0, 0, 0};
|
||||
|
||||
vec3_scale(&b->velocity, linear_damping, &b->velocity);
|
||||
|
||||
Vec3 dp;
|
||||
vec3_scale(&b->velocity, dt, &dp);
|
||||
move_collider(c, dp);
|
||||
}
|
||||
|
||||
// 2. 碰撞检测与响应
|
||||
for (uint32_t i = 0; i < world->collider_count; i++) {
|
||||
Collider* a = &world->colliders[i];
|
||||
for (uint32_t j = i + 1; j < world->collider_count; j++) {
|
||||
Collider* b = &world->colliders[j];
|
||||
if (a->is_static && b->is_static) continue;
|
||||
|
||||
Vec3 normal;
|
||||
float depth = 0.0f;
|
||||
if (!physics_check_collision(a, b, &normal, &depth)) continue;
|
||||
|
||||
// 触发器只报告,不解决
|
||||
if (a->is_trigger || b->is_trigger) continue;
|
||||
|
||||
float inv_a = body_inv_mass(a);
|
||||
float inv_b = body_inv_mass(b);
|
||||
float total_inv = inv_a + inv_b;
|
||||
if (total_inv <= 0.0f) continue;
|
||||
|
||||
// 位置修正(防止沉入)
|
||||
float percent = 0.5f; // 修正比例
|
||||
Vec3 correction;
|
||||
vec3_scale(&normal, depth * percent / total_inv, &correction);
|
||||
if (inv_a > 0.0f) {
|
||||
Vec3 da; vec3_scale(&correction, inv_a, &da);
|
||||
da.x = -da.x; da.y = -da.y; da.z = -da.z;
|
||||
move_collider(a, da);
|
||||
}
|
||||
if (inv_b > 0.0f) {
|
||||
Vec3 db; vec3_scale(&correction, inv_b, &db);
|
||||
move_collider(b, db);
|
||||
}
|
||||
|
||||
// 冲量响应
|
||||
Vec3 rel_v;
|
||||
vec3_sub(&a->body.velocity, &b->body.velocity, &rel_v);
|
||||
float vel_along_normal = vec3_dot(&rel_v, &normal);
|
||||
if (vel_along_normal > 0.0f) continue; // 正在分离
|
||||
|
||||
float e = fminf(a->body.restitution, b->body.restitution);
|
||||
float j = -(1.0f + e) * vel_along_normal / total_inv;
|
||||
|
||||
Vec3 impulse;
|
||||
vec3_scale(&normal, j, &impulse);
|
||||
if (inv_a > 0.0f) {
|
||||
Vec3 va; vec3_scale(&impulse, inv_a, &va);
|
||||
vec3_add(&a->body.velocity, &va, &a->body.velocity);
|
||||
}
|
||||
if (inv_b > 0.0f) {
|
||||
Vec3 vb; vec3_scale(&impulse, inv_b, &vb);
|
||||
vec3_sub(&b->body.velocity, &vb, &b->body.velocity);
|
||||
}
|
||||
|
||||
// 摩擦力
|
||||
vec3_sub(&a->body.velocity, &b->body.velocity, &rel_v);
|
||||
Vec3 tangent;
|
||||
Vec3 vn; vec3_scale(&normal, vec3_dot(&rel_v, &normal), &vn);
|
||||
vec3_sub(&rel_v, &vn, &tangent);
|
||||
float tan_len = vec3_length(&tangent);
|
||||
if (tan_len > 0.0001f) {
|
||||
vec3_scale(&tangent, 1.0f / tan_len, &tangent);
|
||||
float jt = -vec3_dot(&rel_v, &tangent) / total_inv;
|
||||
float mu = sqrtf(a->body.friction * a->body.friction + b->body.friction * b->body.friction);
|
||||
if (jt > j * mu) jt = j * mu;
|
||||
if (jt < -j * mu) jt = -j * mu;
|
||||
Vec3 friction_impulse;
|
||||
vec3_scale(&tangent, jt, &friction_impulse);
|
||||
if (inv_a > 0.0f) {
|
||||
Vec3 va; vec3_scale(&friction_impulse, inv_a, &va);
|
||||
vec3_add(&a->body.velocity, &va, &a->body.velocity);
|
||||
}
|
||||
if (inv_b > 0.0f) {
|
||||
Vec3 vb; vec3_scale(&friction_impulse, inv_b, &vb);
|
||||
vec3_sub(&b->body.velocity, &vb, &b->body.velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void physics_set_gravity(PhysicsWorld* world, Vec3 gravity) {
|
||||
|
||||
@@ -16,6 +16,15 @@ typedef enum {
|
||||
COLLIDER_PLANE,
|
||||
} ColliderType;
|
||||
|
||||
typedef struct {
|
||||
Vec3 velocity;
|
||||
Vec3 acceleration;
|
||||
float mass;
|
||||
float restitution;
|
||||
float friction;
|
||||
int is_kinematic;
|
||||
} RigidBody;
|
||||
|
||||
typedef struct {
|
||||
uint32_t entity_id;
|
||||
ColliderType type;
|
||||
@@ -26,17 +35,9 @@ typedef struct {
|
||||
};
|
||||
int is_trigger;
|
||||
int is_static;
|
||||
RigidBody body;
|
||||
} Collider;
|
||||
|
||||
typedef struct {
|
||||
Vec3 velocity;
|
||||
Vec3 acceleration;
|
||||
float mass;
|
||||
float restitution;
|
||||
float friction;
|
||||
int is_kinematic;
|
||||
} RigidBody;
|
||||
|
||||
typedef struct {
|
||||
Collider colliders[MAX_COLLIDERS];
|
||||
uint32_t collider_count;
|
||||
@@ -63,6 +64,11 @@ int physics_check_collision(const Collider* a, const Collider* b, Vec3* normal,
|
||||
int physics_raycast(PhysicsWorld* world, Vec3 origin, Vec3 direction, float max_dist,
|
||||
uint32_t* hit_entity, Vec3* hit_point, Vec3* hit_normal);
|
||||
|
||||
// 刚体
|
||||
RigidBody* physics_get_body(PhysicsWorld* world, uint32_t index);
|
||||
void physics_apply_force(PhysicsWorld* world, uint32_t index, Vec3 force);
|
||||
void physics_apply_impulse(PhysicsWorld* world, uint32_t index, Vec3 impulse);
|
||||
|
||||
// 模拟
|
||||
void physics_step(PhysicsWorld* world, float dt);
|
||||
void physics_set_gravity(PhysicsWorld* world, Vec3 gravity);
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "graphics_device.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
GraphicsDevice* graphics_device_create(void) {
|
||||
return (GraphicsDevice*)calloc(1, sizeof(GraphicsDevice));
|
||||
@@ -60,7 +61,7 @@ int graphics_device_init(GraphicsDevice* d, void* hwnd, uint32_t w, uint32_t h,
|
||||
d->device->lpVtbl->CreateRenderTargetView(d->device, (ID3D11Resource*)back_buffer, NULL, &d->render_target);
|
||||
if (back_buffer) back_buffer->lpVtbl->Release(back_buffer);
|
||||
create_depth_stencil(d, w, h);
|
||||
d->context->lpVtbl->OMSetRenderTargets(d->context, 1, &d->render_target, NULL);
|
||||
d->context->lpVtbl->OMSetRenderTargets(d->context, 1, &d->render_target, d->depth_stencil);
|
||||
D3D11_VIEWPORT vp = {0};
|
||||
vp.Width = (float)w; vp.Height = (float)h;
|
||||
vp.MaxDepth = 1.0f;
|
||||
@@ -77,6 +78,19 @@ int graphics_device_init(GraphicsDevice* d, void* hwnd, uint32_t w, uint32_t h,
|
||||
d->context->lpVtbl->OMSetDepthStencilState(d->context, default_dss, 0);
|
||||
default_dss->lpVtbl->Release(default_dss);
|
||||
}
|
||||
|
||||
// 设置默认光栅化状态:背面剔除 + 实体填充
|
||||
D3D11_RASTERIZER_DESC rd = {0};
|
||||
rd.FillMode = D3D11_FILL_SOLID;
|
||||
rd.CullMode = D3D11_CULL_BACK;
|
||||
rd.FrontCounterClockwise = FALSE;
|
||||
rd.DepthClipEnable = TRUE;
|
||||
ID3D11RasterizerState* default_rs = NULL;
|
||||
d->device->lpVtbl->CreateRasterizerState(d->device, &rd, &default_rs);
|
||||
if (default_rs) {
|
||||
d->context->lpVtbl->RSSetState(d->context, default_rs);
|
||||
default_rs->lpVtbl->Release(default_rs);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -108,3 +122,68 @@ void graphics_device_clear(GraphicsDevice* d, float r, float g, float b, float a
|
||||
void graphics_device_present(GraphicsDevice* d) {
|
||||
d->swap_chain->lpVtbl->Present(d->swap_chain, d->vsync ? 1 : 0, 0);
|
||||
}
|
||||
|
||||
int graphics_device_save_screenshot(GraphicsDevice* d, const char* path) {
|
||||
if (!d || !d->swap_chain || !d->device || !d->context) return 0;
|
||||
|
||||
ID3D11Texture2D* back = NULL;
|
||||
HRESULT hr = d->swap_chain->lpVtbl->GetBuffer(d->swap_chain, 0, &IID_ID3D11Texture2D, (void**)&back);
|
||||
if (FAILED(hr) || !back) return 0;
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc = {0};
|
||||
back->lpVtbl->GetDesc(back, &desc);
|
||||
|
||||
D3D11_TEXTURE2D_DESC sd = desc;
|
||||
sd.BindFlags = 0;
|
||||
sd.Usage = D3D11_USAGE_STAGING;
|
||||
sd.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
sd.MiscFlags = 0;
|
||||
|
||||
ID3D11Texture2D* staging = NULL;
|
||||
hr = d->device->lpVtbl->CreateTexture2D(d->device, &sd, NULL, &staging);
|
||||
if (FAILED(hr) || !staging) { back->lpVtbl->Release(back); return 0; }
|
||||
|
||||
d->context->lpVtbl->CopyResource(d->context, (ID3D11Resource*)staging, (ID3D11Resource*)back);
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE map = {0};
|
||||
hr = d->context->lpVtbl->Map(d->context, (ID3D11Resource*)staging, 0, D3D11_MAP_READ, 0, &map);
|
||||
if (FAILED(hr)) { staging->lpVtbl->Release(staging); back->lpVtbl->Release(back); return 0; }
|
||||
|
||||
FILE* f = fopen(path, "wb");
|
||||
if (!f) { d->context->lpVtbl->Unmap(d->context, (ID3D11Resource*)staging, 0); staging->lpVtbl->Release(staging); back->lpVtbl->Release(back); return 0; }
|
||||
|
||||
int w = (int)desc.Width;
|
||||
int h = (int)desc.Height;
|
||||
int row_size = ((w * 3 + 3) / 4) * 4;
|
||||
int file_size = 54 + row_size * h;
|
||||
|
||||
unsigned char header[54] = {0};
|
||||
header[0] = 'B'; header[1] = 'M';
|
||||
memcpy(header + 2, &file_size, 4);
|
||||
header[10] = 54;
|
||||
header[14] = 40;
|
||||
memcpy(header + 18, &w, 4);
|
||||
memcpy(header + 22, &h, 4);
|
||||
header[26] = 1;
|
||||
header[28] = 24;
|
||||
fwrite(header, 1, 54, f);
|
||||
|
||||
unsigned char* row = (unsigned char*)malloc(row_size);
|
||||
for (int y = 0; y < h; y++) {
|
||||
uint8_t* src = (uint8_t*)map.pData + (h - 1 - y) * map.RowPitch;
|
||||
for (int x = 0; x < w; x++) {
|
||||
// 后台缓冲区为 RGBA,BMP 24-bit 需要 BGR
|
||||
row[x * 3 + 0] = src[x * 4 + 2];
|
||||
row[x * 3 + 1] = src[x * 4 + 1];
|
||||
row[x * 3 + 2] = src[x * 4 + 0];
|
||||
}
|
||||
fwrite(row, 1, row_size, f);
|
||||
}
|
||||
free(row);
|
||||
fclose(f);
|
||||
|
||||
d->context->lpVtbl->Unmap(d->context, (ID3D11Resource*)staging, 0);
|
||||
staging->lpVtbl->Release(staging);
|
||||
back->lpVtbl->Release(back);
|
||||
return 1;
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
#include <dxgi.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
typedef struct GraphicsDevice GraphicsDevice;
|
||||
struct GraphicsDevice {
|
||||
ID3D11Device* device;
|
||||
ID3D11DeviceContext* context;
|
||||
IDXGISwapChain* swap_chain;
|
||||
@@ -13,7 +14,7 @@ typedef struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
int vsync;
|
||||
} GraphicsDevice;
|
||||
};
|
||||
|
||||
GraphicsDevice* graphics_device_create(void);
|
||||
void graphics_device_destroy(GraphicsDevice* d);
|
||||
@@ -21,3 +22,4 @@ int graphics_device_init(GraphicsDevice* d, void* hwnd, uint32_t w, uint32_t h,
|
||||
void graphics_device_resize(GraphicsDevice* d, uint32_t w, uint32_t h);
|
||||
void graphics_device_clear(GraphicsDevice* d, float r, float g, float b, float a);
|
||||
void graphics_device_present(GraphicsDevice* d);
|
||||
int graphics_device_save_screenshot(GraphicsDevice* d, const char* path);
|
||||
@@ -1,5 +1,18 @@
|
||||
#include "renderer.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void fill_constant_buffer(float* cb, const Mat4* world, const Mat4* wvp,
|
||||
Vec3 light_dir, Vec4 light_color, Vec4 ambient_color,
|
||||
int use_texture, Vec4 diffuse_color) {
|
||||
memcpy(cb + 0, world->m, sizeof(float) * 16);
|
||||
memcpy(cb + 16, wvp->m, sizeof(float) * 16);
|
||||
cb[32] = light_dir.x; cb[33] = light_dir.y; cb[34] = light_dir.z; cb[35] = 0.0f;
|
||||
cb[36] = light_color.x; cb[37] = light_color.y; cb[38] = light_color.z; cb[39] = light_color.w;
|
||||
cb[40] = ambient_color.x; cb[41] = ambient_color.y; cb[42] = ambient_color.z; cb[43] = ambient_color.w;
|
||||
cb[44] = use_texture ? 1.0f : 0.0f; cb[45] = 0.0f; cb[46] = 0.0f; cb[47] = 0.0f;
|
||||
cb[48] = diffuse_color.x; cb[49] = diffuse_color.y; cb[50] = diffuse_color.z; cb[51] = diffuse_color.w;
|
||||
}
|
||||
|
||||
Renderer* renderer_create(GraphicsDevice* device) {
|
||||
Renderer* r = (Renderer*)calloc(1, sizeof(Renderer));
|
||||
@@ -15,7 +28,14 @@ Renderer* renderer_create(GraphicsDevice* device) {
|
||||
void renderer_destroy(Renderer* r) { free(r); }
|
||||
|
||||
void renderer_begin_frame(Renderer* r) {
|
||||
ID3D11DeviceContext* ctx = r->device->context;
|
||||
graphics_device_clear(r->device, 0.1f, 0.1f, 0.15f, 1.0f);
|
||||
D3D11_VIEWPORT vp = {0};
|
||||
vp.Width = (float)r->device->width;
|
||||
vp.Height = (float)r->device->height;
|
||||
vp.MaxDepth = 1.0f;
|
||||
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
|
||||
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
}
|
||||
|
||||
void renderer_end_frame(Renderer* r) {
|
||||
@@ -37,9 +57,41 @@ void renderer_draw_mesh(Renderer* r, Mesh* m) {
|
||||
}
|
||||
|
||||
void renderer_draw_mesh_with_shader(Renderer* r, Mesh* m, Shader* s, const Mat4* world, Texture* t) {
|
||||
if (!m || !s) return;
|
||||
if (!m || !s || !r) return;
|
||||
ID3D11DeviceContext* ctx = r->device->context;
|
||||
shader_bind(s, ctx);
|
||||
|
||||
Mat4 wvp;
|
||||
mat4_multiply(world, &r->view, &wvp);
|
||||
mat4_multiply(&wvp, &r->projection, &wvp);
|
||||
|
||||
float cb[64];
|
||||
memset(cb, 0, sizeof(cb));
|
||||
fill_constant_buffer(cb, world, &wvp, r->light_dir, r->light_color, r->ambient_color, t ? 1 : 0, (Vec4){1,1,1,1});
|
||||
|
||||
shader_set_matrix(s, ctx, cb);
|
||||
if (t) texture_bind(t, ctx, 0);
|
||||
|
||||
renderer_draw_mesh(r, m);
|
||||
}
|
||||
|
||||
void renderer_draw_mesh_with_material(Renderer* r, Mesh* m, Material* mat, const Mat4* world) {
|
||||
if (!m || !mat || !mat->shader || !r) return;
|
||||
ID3D11DeviceContext* ctx = r->device->context;
|
||||
shader_bind(mat->shader, ctx);
|
||||
|
||||
Mat4 wvp;
|
||||
mat4_multiply(world, &r->view, &wvp);
|
||||
mat4_multiply(&wvp, &r->projection, &wvp);
|
||||
|
||||
float cb[64];
|
||||
memset(cb, 0, sizeof(cb));
|
||||
fill_constant_buffer(cb, world, &wvp, r->light_dir, r->light_color, r->ambient_color,
|
||||
mat->use_texture, mat->color);
|
||||
|
||||
shader_set_matrix(mat->shader, ctx, cb);
|
||||
if (mat->use_texture && mat->texture) texture_bind(mat->texture, ctx, 0);
|
||||
|
||||
renderer_draw_mesh(r, m);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
#include "mesh.h"
|
||||
#include "texture.h"
|
||||
#include "camera.h"
|
||||
#include "material.h"
|
||||
#include "../include/engine_api.h"
|
||||
#include "../ASM/math_c_fallback.h"
|
||||
|
||||
typedef struct {
|
||||
typedef struct Renderer Renderer;
|
||||
struct Renderer {
|
||||
GraphicsDevice* device;
|
||||
Shader* current_shader;
|
||||
Mat4 view;
|
||||
@@ -15,7 +17,7 @@ typedef struct {
|
||||
Vec3 light_dir;
|
||||
Vec4 light_color;
|
||||
Vec4 ambient_color;
|
||||
} Renderer;
|
||||
};
|
||||
|
||||
Renderer* renderer_create(GraphicsDevice* device);
|
||||
void renderer_destroy(Renderer* renderer);
|
||||
@@ -23,6 +25,7 @@ void renderer_begin_frame(Renderer* renderer);
|
||||
void renderer_end_frame(Renderer* renderer);
|
||||
void renderer_draw_mesh(Renderer* renderer, Mesh* mesh);
|
||||
void renderer_draw_mesh_with_shader(Renderer* renderer, Mesh* mesh, Shader* shader, const Mat4* world, Texture* tex);
|
||||
void renderer_draw_mesh_with_material(Renderer* renderer, Mesh* mesh, Material* material, const Mat4* world);
|
||||
void renderer_set_view(Renderer* renderer, const Mat4* view);
|
||||
void renderer_set_projection(Renderer* renderer, const Mat4* projection);
|
||||
void renderer_set_light(Renderer* renderer, Vec3 dir, Vec4 color, Vec4 ambient);
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
#include "shader.h"
|
||||
#include "../Core/logger.h"
|
||||
#include <d3dcompiler.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char* default_vs =
|
||||
"struct VS_IN{float3 pos:POSITION;float3 n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
|
||||
"struct PS_IN{float4 pos:SV_POSITION;float4 col:COLOR;};"
|
||||
"cbuffer CB:register(b0){float4x4 WVP;};"
|
||||
"PS_IN main(VS_IN i){PS_IN o;o.pos=mul(float4(i.pos,1),WVP);o.col=i.col;return o;}";
|
||||
"struct PS_IN{float4 pos:SV_POSITION;float3 world_n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
|
||||
"cbuffer CB:register(b0){float4x4 World;float4x4 WVP;float4 LightDir;float4 LightColor;float4 AmbientColor;float4 UseTexture;float4 DiffuseColor;};"
|
||||
"PS_IN main(VS_IN i){PS_IN o;o.pos=mul(float4(i.pos,1),WVP);o.world_n=normalize(mul(float4(i.n,0),World).xyz);o.uv=i.uv;o.col=i.col*DiffuseColor;return o;}";
|
||||
|
||||
static const char* default_ps =
|
||||
"struct PS_IN{float4 pos:SV_POSITION;float4 col:COLOR;};"
|
||||
"float4 main(PS_IN i):SV_TARGET{return i.col;}";
|
||||
"struct PS_IN{float4 pos:SV_POSITION;float3 world_n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
|
||||
"cbuffer CB:register(b0){float4x4 World;float4x4 WVP;float4 LightDir;float4 LightColor;float4 AmbientColor;float4 UseTexture;float4 DiffuseColor;};"
|
||||
"Texture2D tex:register(t0);SamplerState samp:register(s0);"
|
||||
"float4 main(PS_IN i):SV_TARGET{float3 ld=normalize(LightDir.xyz);float diff=saturate(dot(i.world_n,ld));float4 light=AmbientColor+LightColor*diff;float4 albedo=i.col;if(UseTexture.x>0.5)albedo=albedo*tex.Sample(samp,i.uv);return albedo*light;}";
|
||||
|
||||
static ID3DBlob* compile_shader(const char* code, uint32_t len, const char* target) {
|
||||
ID3DBlob *blob = NULL, *err = NULL;
|
||||
D3DCompile(code, len, NULL, NULL, NULL, "main", target, D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &blob, &err);
|
||||
if (err) err->lpVtbl->Release(err);
|
||||
UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_PACK_MATRIX_ROW_MAJOR;
|
||||
D3DCompile(code, len, NULL, NULL, NULL, "main", target, flags, 0, &blob, &err);
|
||||
if (err) {
|
||||
const char* msg = (const char*)err->lpVtbl->GetBufferPointer(err);
|
||||
LOG_ERROR("Shader compile error (%s): %s", target, msg);
|
||||
err->lpVtbl->Release(err);
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
|
||||
@@ -41,6 +49,7 @@ int shader_compile(Shader* s, ID3D11Device* dev, const char* vs_code, const char
|
||||
ID3DBlob* vs_blob = compile_shader(vs_code, vs_len, "vs_5_0");
|
||||
ID3DBlob* ps_blob = compile_shader(ps_code, ps_len, "ps_5_0");
|
||||
if (!vs_blob || !ps_blob) {
|
||||
LOG_ERROR("Failed to compile default shader (vs=%p ps=%p)", (void*)vs_blob, (void*)ps_blob);
|
||||
if (vs_blob) vs_blob->lpVtbl->Release(vs_blob);
|
||||
if (ps_blob) ps_blob->lpVtbl->Release(ps_blob);
|
||||
return 0;
|
||||
@@ -67,6 +76,7 @@ void shader_bind(Shader* s, ID3D11DeviceContext* ctx) {
|
||||
ctx->lpVtbl->PSSetShader(ctx, s->ps, NULL, 0);
|
||||
ctx->lpVtbl->IASetInputLayout(ctx, s->layout);
|
||||
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &s->cb);
|
||||
ctx->lpVtbl->PSSetConstantBuffers(ctx, 0, 1, &s->cb);
|
||||
}
|
||||
|
||||
void shader_set_matrix(Shader* s, ID3D11DeviceContext* ctx, const float* data) {
|
||||
|
||||
@@ -6,6 +6,32 @@
|
||||
|
||||
#pragma comment(lib, "windowscodecs.lib")
|
||||
|
||||
// 为 MinGW 提供 COM 助手宏(MSVC 已内联提供)
|
||||
#ifndef _MSC_VER
|
||||
#define ID3D11ShaderResourceView_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11Texture2D_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11Device_CreateTexture2D(p,a,b,c) (p)->lpVtbl->CreateTexture2D(p,a,b,c)
|
||||
#define ID3D11Device_CreateShaderResourceView(p,a,b,c) (p)->lpVtbl->CreateShaderResourceView(p,a,b,c)
|
||||
#define ID3D11DeviceContext_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a)
|
||||
#define ID3D11DeviceContext_PSSetShaderResources(p,a,b,c) (p)->lpVtbl->PSSetShaderResources(p,a,b,c)
|
||||
#define ID3D11DeviceContext_PSSetSamplers(p,a,b,c) (p)->lpVtbl->PSSetSamplers(p,a,b,c)
|
||||
#define ID3D11Device_CreateSamplerState(p,a,b) (p)->lpVtbl->CreateSamplerState(p,a,b)
|
||||
|
||||
#define IWICBitmapDecoder_GetFrame(p,a,b) (p)->lpVtbl->GetFrame(p,a,b)
|
||||
#define IWICBitmapFrameDecode_GetSize(p,a,b) (p)->lpVtbl->GetSize(p,a,b)
|
||||
#define IWICImagingFactory_CreateFormatConverter(p,a) (p)->lpVtbl->CreateFormatConverter(p,a)
|
||||
#define IWICFormatConverter_Initialize(p,a,b,c,d,e,f) (p)->lpVtbl->Initialize(p,a,b,c,d,e,f)
|
||||
#define IWICFormatConverter_CopyPixels(p,a,b,c,d) (p)->lpVtbl->CopyPixels(p,a,b,c,d)
|
||||
#define IWICFormatConverter_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define IWICBitmapFrameDecode_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define IWICImagingFactory_CreateDecoderFromFilename(p,a,b,c,d,e) (p)->lpVtbl->CreateDecoderFromFilename(p,a,b,c,d,e)
|
||||
#define IWICBitmapDecoder_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define IWICImagingFactory_CreateStream(p,a) (p)->lpVtbl->CreateStream(p,a)
|
||||
#define IWICStream_InitializeFromMemory(p,a,b) (p)->lpVtbl->InitializeFromMemory(p,a,b)
|
||||
#define IWICImagingFactory_CreateDecoderFromStream(p,a,b,c,d) (p)->lpVtbl->CreateDecoderFromStream(p,a,b,c,d)
|
||||
#define IWICStream_Release(p) (p)->lpVtbl->Release(p)
|
||||
#endif
|
||||
|
||||
static IWICImagingFactory* wic_factory = NULL;
|
||||
|
||||
static HRESULT ensure_wic(void) {
|
||||
@@ -183,6 +209,23 @@ Texture* texture_create_checkerboard(ID3D11Device* device, uint32_t size, uint32
|
||||
|
||||
void texture_bind(Texture* texture, ID3D11DeviceContext* ctx, uint32_t slot) {
|
||||
if (!texture || !texture->srv) return;
|
||||
ID3D11DeviceContext_PSSetShaderResources(ctx, slot, 1, &texture->srv);
|
||||
ID3D11DeviceContext_PSSetSamplers(ctx, slot, 1, &(ID3D11SamplerState*){NULL});
|
||||
static ID3D11SamplerState* default_sampler = NULL;
|
||||
if (!default_sampler) {
|
||||
ID3D11Device* device = NULL;
|
||||
ctx->lpVtbl->GetDevice(ctx, &device);
|
||||
if (device) {
|
||||
D3D11_SAMPLER_DESC sd = {0};
|
||||
sd.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
sd.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sd.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sd.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sd.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
||||
sd.MinLOD = 0;
|
||||
sd.MaxLOD = D3D11_FLOAT32_MAX;
|
||||
device->lpVtbl->CreateSamplerState(device, &sd, &default_sampler);
|
||||
device->lpVtbl->Release(device);
|
||||
}
|
||||
}
|
||||
ctx->lpVtbl->PSSetShaderResources(ctx, slot, 1, &texture->srv);
|
||||
ctx->lpVtbl->PSSetSamplers(ctx, slot, 1, &default_sampler);
|
||||
}
|
||||
+198
-9
@@ -2,6 +2,48 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <d3dcompiler.h>
|
||||
#include <windows.h>
|
||||
|
||||
// 为 MinGW 提供 COM 助手宏(MSVC 已内联提供)
|
||||
#ifndef _MSC_VER
|
||||
#define ID3D11Device_CreateBuffer(p,a,b,c) (p)->lpVtbl->CreateBuffer(p,a,b,c)
|
||||
#define ID3D11Device_CreateVertexShader(p,a,b,c,d) (p)->lpVtbl->CreateVertexShader(p,a,b,c,d)
|
||||
#define ID3D11Device_CreatePixelShader(p,a,b,c,d) (p)->lpVtbl->CreatePixelShader(p,a,b,c,d)
|
||||
#define ID3D11Device_CreateInputLayout(p,a,b,c,d,e) (p)->lpVtbl->CreateInputLayout(p,a,b,c,d,e)
|
||||
#define ID3D11Device_CreateBlendState(p,a,b) (p)->lpVtbl->CreateBlendState(p,a,b)
|
||||
#define ID3D11Device_CreateRasterizerState(p,a,b) (p)->lpVtbl->CreateRasterizerState(p,a,b)
|
||||
#define ID3D11Device_CreateTexture2D(p,a,b,c) (p)->lpVtbl->CreateTexture2D(p,a,b,c)
|
||||
#define ID3D11Device_CreateShaderResourceView(p,a,b,c) (p)->lpVtbl->CreateShaderResourceView(p,a,b,c)
|
||||
#define ID3D11Device_CreateSamplerState(p,a,b) (p)->lpVtbl->CreateSamplerState(p,a,b)
|
||||
|
||||
#define ID3D11RasterizerState_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11BlendState_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11InputLayout_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11PixelShader_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11VertexShader_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11Buffer_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11Texture2D_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11ShaderResourceView_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D11SamplerState_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D10Blob_Release(p) (p)->lpVtbl->Release(p)
|
||||
#define ID3D10Blob_GetBufferPointer(p) (p)->lpVtbl->GetBufferPointer(p)
|
||||
#define ID3D10Blob_GetBufferSize(p) (p)->lpVtbl->GetBufferSize(p)
|
||||
|
||||
#define ID3D11DeviceContext_Map(p,a,b,c,d,e) (p)->lpVtbl->Map(p,a,b,c,d,e)
|
||||
#define ID3D11DeviceContext_Unmap(p,a,b) (p)->lpVtbl->Unmap(p,a,b)
|
||||
#define ID3D11DeviceContext_IASetVertexBuffers(p,a,b,c,d,e) (p)->lpVtbl->IASetVertexBuffers(p,a,b,c,d,e)
|
||||
#define ID3D11DeviceContext_IASetIndexBuffer(p,a,b,c) (p)->lpVtbl->IASetIndexBuffer(p,a,b,c)
|
||||
#define ID3D11DeviceContext_IASetPrimitiveTopology(p,a) (p)->lpVtbl->IASetPrimitiveTopology(p,a)
|
||||
#define ID3D11DeviceContext_IASetInputLayout(p,a) (p)->lpVtbl->IASetInputLayout(p,a)
|
||||
#define ID3D11DeviceContext_VSSetShader(p,a,b,c) (p)->lpVtbl->VSSetShader(p,a,b,c)
|
||||
#define ID3D11DeviceContext_PSSetShader(p,a,b,c) (p)->lpVtbl->PSSetShader(p,a,b,c)
|
||||
#define ID3D11DeviceContext_PSSetShaderResources(p,a,b,c) (p)->lpVtbl->PSSetShaderResources(p,a,b,c)
|
||||
#define ID3D11DeviceContext_PSSetSamplers(p,a,b,c) (p)->lpVtbl->PSSetSamplers(p,a,b,c)
|
||||
#define ID3D11DeviceContext_OMSetBlendState(p,a,b,c) (p)->lpVtbl->OMSetBlendState(p,a,b,c)
|
||||
#define ID3D11DeviceContext_RSSetState(p,a) (p)->lpVtbl->RSSetState(p,a)
|
||||
#define ID3D11DeviceContext_DrawIndexed(p,a,b,c) (p)->lpVtbl->DrawIndexed(p,a,b,c)
|
||||
#endif
|
||||
|
||||
static const char* ui_vs = R"(
|
||||
struct VS_INPUT { float2 Pos : POSITION; float2 UV : TEXCOORD0; uint Color : COLOR0; };
|
||||
@@ -18,9 +60,107 @@ VS_OUTPUT main(VS_INPUT input) {
|
||||
|
||||
static const char* ui_ps = R"(
|
||||
struct PS_INPUT { float4 Pos : SV_POSITION; float2 UV : TEXCOORD0; float4 Color : COLOR0; };
|
||||
float4 main(PS_INPUT input) : SV_TARGET { return input.Color; }
|
||||
Texture2D tex : register(t0);
|
||||
SamplerState samp : register(s0);
|
||||
float4 main(PS_INPUT input) : SV_TARGET { return input.Color * tex.Sample(samp, input.UV); }
|
||||
)";
|
||||
|
||||
#define UI_FONT_COLS 16
|
||||
#define UI_FONT_ROWS 8
|
||||
#define UI_FONT_CELL_W 16
|
||||
#define UI_FONT_CELL_H 16
|
||||
#define UI_FONT_ATLAS_W (UI_FONT_COLS * UI_FONT_CELL_W)
|
||||
#define UI_FONT_ATLAS_H (UI_FONT_ROWS * UI_FONT_CELL_H)
|
||||
|
||||
static int ui_create_font_texture(UI* ui, ID3D11Device* device) {
|
||||
BITMAPINFO bmi = {0};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = UI_FONT_ATLAS_W;
|
||||
bmi.bmiHeader.biHeight = -UI_FONT_ATLAS_H;
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
HDC hdc = GetDC(NULL);
|
||||
HDC memdc = CreateCompatibleDC(hdc);
|
||||
void* bits = NULL;
|
||||
HBITMAP bmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
|
||||
if (!bmp) {
|
||||
DeleteDC(memdc);
|
||||
ReleaseDC(NULL, hdc);
|
||||
return 0;
|
||||
}
|
||||
SelectObject(memdc, bmp);
|
||||
PatBlt(memdc, 0, 0, UI_FONT_ATLAS_W, UI_FONT_ATLAS_H, BLACKNESS);
|
||||
SetBkColor(memdc, RGB(0, 0, 0));
|
||||
SetTextColor(memdc, RGB(255, 255, 255));
|
||||
|
||||
HFONT font = CreateFont(UI_FONT_CELL_H - 2, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
|
||||
DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
CLEARTYPE_QUALITY, FIXED_PITCH | FF_MODERN, "Consolas");
|
||||
if (font) SelectObject(memdc, font);
|
||||
|
||||
for (int i = 32; i < 128; i++) {
|
||||
int col = i % UI_FONT_COLS;
|
||||
int row = i / UI_FONT_COLS;
|
||||
char c = (char)i;
|
||||
TextOutA(memdc, col * UI_FONT_CELL_W + 1, row * UI_FONT_CELL_H + 1, &c, 1);
|
||||
|
||||
ui->char_uvs[i][0] = (float)(col * UI_FONT_CELL_W) / UI_FONT_ATLAS_W;
|
||||
ui->char_uvs[i][1] = (float)(row * UI_FONT_CELL_H) / UI_FONT_ATLAS_H;
|
||||
ui->char_uvs[i][2] = (float)((col + 1) * UI_FONT_CELL_W) / UI_FONT_ATLAS_W;
|
||||
ui->char_uvs[i][3] = (float)((row + 1) * UI_FONT_CELL_H) / UI_FONT_ATLAS_H;
|
||||
}
|
||||
|
||||
// 将亮度转为 alpha,文字存储为白色
|
||||
uint32_t* pixels = (uint32_t*)bits;
|
||||
for (int i = 0; i < UI_FONT_ATLAS_W * UI_FONT_ATLAS_H; i++) {
|
||||
uint32_t p = pixels[i];
|
||||
uint8_t b = (uint8_t)(p & 0xFF);
|
||||
uint8_t g = (uint8_t)((p >> 8) & 0xFF);
|
||||
uint8_t r = (uint8_t)((p >> 16) & 0xFF);
|
||||
uint8_t lum = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8);
|
||||
pixels[i] = 0x00FFFFFF | ((uint32_t)lum << 24);
|
||||
}
|
||||
// 左上角像素保留为纯白,供纯色矩形采样
|
||||
pixels[0] = 0xFFFFFFFF;
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc = {0};
|
||||
desc.Width = UI_FONT_ATLAS_W;
|
||||
desc.Height = UI_FONT_ATLAS_H;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Usage = D3D11_USAGE_IMMUTABLE;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
D3D11_SUBRESOURCE_DATA init = {0};
|
||||
init.pSysMem = bits;
|
||||
init.SysMemPitch = UI_FONT_ATLAS_W * 4;
|
||||
|
||||
ID3D11Texture2D* tex = NULL;
|
||||
HRESULT hr = ID3D11Device_CreateTexture2D(device, &desc, &init, &tex);
|
||||
if (SUCCEEDED(hr) && tex) {
|
||||
ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource*)tex, NULL, &ui->font_srv);
|
||||
ID3D11Texture2D_Release(tex);
|
||||
}
|
||||
|
||||
D3D11_SAMPLER_DESC sd = {0};
|
||||
sd.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
|
||||
sd.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sd.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sd.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
ID3D11Device_CreateSamplerState(device, &sd, &ui->font_sampler);
|
||||
|
||||
if (font) DeleteObject(font);
|
||||
DeleteObject(bmp);
|
||||
DeleteDC(memdc);
|
||||
ReleaseDC(NULL, hdc);
|
||||
|
||||
return ui->font_srv != NULL && ui->font_sampler != NULL;
|
||||
}
|
||||
|
||||
UI* ui_create(ID3D11Device* device) {
|
||||
UI* ui = (UI*)calloc(1, sizeof(UI));
|
||||
ui->font_size = 14.0f;
|
||||
@@ -81,11 +221,15 @@ UI* ui_create(ID3D11Device* device) {
|
||||
rd.ScissorEnable = TRUE;
|
||||
ID3D11Device_CreateRasterizerState(device, &rd, &ui->rasterizer);
|
||||
|
||||
ui_create_font_texture(ui, device);
|
||||
|
||||
return ui;
|
||||
}
|
||||
|
||||
void ui_destroy(UI* ui) {
|
||||
if (!ui) return;
|
||||
if (ui->font_sampler) ID3D11SamplerState_Release(ui->font_sampler);
|
||||
if (ui->font_srv) ID3D11ShaderResourceView_Release(ui->font_srv);
|
||||
if (ui->rasterizer) ID3D11RasterizerState_Release(ui->rasterizer);
|
||||
if (ui->blend_state) ID3D11BlendState_Release(ui->blend_state);
|
||||
if (ui->input_layout) ID3D11InputLayout_Release(ui->input_layout);
|
||||
@@ -112,10 +256,11 @@ void ui_end(UI* ui) {
|
||||
|
||||
void ui_draw_rect(UI* ui, float x, float y, float w, float h, uint32_t color) {
|
||||
uint32_t base = ui->vertex_count;
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x, y, 0, 0, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y, 1, 0, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y + h, 1, 1, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x, y + h, 0, 1, color};
|
||||
// UV 指向字体图集左上角纯白像素,使纯色矩形不受纹理内容影响
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x, y, 0.0f, 0.0f, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y, 0.0f, 0.0f, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y + h, 0.0f, 0.0f, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x, y + h, 0.0f, 0.0f, color};
|
||||
ui->indices[ui->index_count++] = base;
|
||||
ui->indices[ui->index_count++] = base + 1;
|
||||
ui->indices[ui->index_count++] = base + 2;
|
||||
@@ -124,6 +269,35 @@ void ui_draw_rect(UI* ui, float x, float y, float w, float h, uint32_t color) {
|
||||
ui->indices[ui->index_count++] = base + 3;
|
||||
}
|
||||
|
||||
void ui_draw_text(UI* ui, float x, float y, const char* text, uint32_t color) {
|
||||
if (!text) return;
|
||||
float px = x;
|
||||
float py = y;
|
||||
const float cw = (float)UI_FONT_CELL_W;
|
||||
const float ch = (float)UI_FONT_CELL_H;
|
||||
while (*text) {
|
||||
unsigned char c = (unsigned char)*text++;
|
||||
if (c < 32 || c >= 128) c = '?';
|
||||
float u1 = ui->char_uvs[c][0];
|
||||
float v1 = ui->char_uvs[c][1];
|
||||
float u2 = ui->char_uvs[c][2];
|
||||
float v2 = ui->char_uvs[c][3];
|
||||
|
||||
uint32_t base = ui->vertex_count;
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){px, py, u1, v1, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){px + cw, py, u2, v1, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){px + cw, py + ch, u2, v2, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){px, py + ch, u1, v2, color};
|
||||
ui->indices[ui->index_count++] = base;
|
||||
ui->indices[ui->index_count++] = base + 1;
|
||||
ui->indices[ui->index_count++] = base + 2;
|
||||
ui->indices[ui->index_count++] = base;
|
||||
ui->indices[ui->index_count++] = base + 2;
|
||||
ui->indices[ui->index_count++] = base + 3;
|
||||
px += cw;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_draw_rect_border(UI* ui, float x, float y, float w, float h,
|
||||
uint32_t fill, uint32_t border, float thickness) {
|
||||
ui_draw_rect(ui, x, y, w, h, fill);
|
||||
@@ -134,12 +308,16 @@ void ui_draw_rect_border(UI* ui, float x, float y, float w, float h,
|
||||
}
|
||||
|
||||
int ui_button(UI* ui, float x, float y, float w, float h, const char* text) {
|
||||
(void)text;
|
||||
int hover = ui->mouse_x >= x && ui->mouse_x <= x + w &&
|
||||
ui->mouse_y >= y && ui->mouse_y <= y + h;
|
||||
uint32_t color = hover ? 0xFF555555 : 0xFF444444;
|
||||
if (hover && ui->mouse_pressed) color = 0xFF666666;
|
||||
ui_draw_rect_border(ui, x, y, w, h, color, 0xFF888888, 1);
|
||||
if (text) {
|
||||
float tx = x + ui->padding;
|
||||
float ty = y + (h - UI_FONT_CELL_H) * 0.5f;
|
||||
ui_draw_text(ui, tx, ty, text, ui->text_color);
|
||||
}
|
||||
return hover && ui->mouse_pressed;
|
||||
}
|
||||
|
||||
@@ -161,9 +339,17 @@ float ui_slider(UI* ui, float x, float y, float w, float h, float value, float m
|
||||
}
|
||||
|
||||
int ui_checkbox(UI* ui, float x, float y, const char* text, int checked) {
|
||||
(void)text;
|
||||
float s = 16.0f;
|
||||
ui_draw_rect_border(ui, x, y, s, s, checked ? 0xFF0078D4 : 0xFF333333, 0xFF666666, 1);
|
||||
if (checked) {
|
||||
// 绘制对勾
|
||||
ui_draw_rect(ui, x + 4, y + 7, 2, 2, 0xFFFFFFFF);
|
||||
ui_draw_rect(ui, x + 6, y + 9, 2, 2, 0xFFFFFFFF);
|
||||
ui_draw_rect(ui, x + 8, y + 5, 5, 2, 0xFFFFFFFF);
|
||||
}
|
||||
if (text) {
|
||||
ui_draw_text(ui, x + s + ui->padding, y + (s - UI_FONT_CELL_H) * 0.5f, text, ui->text_color);
|
||||
}
|
||||
if (ui->mouse_x >= x && ui->mouse_x <= x + s &&
|
||||
ui->mouse_y >= y && ui->mouse_y <= y + s && ui->mouse_pressed) {
|
||||
return !checked;
|
||||
@@ -172,8 +358,9 @@ int ui_checkbox(UI* ui, float x, float y, const char* text, int checked) {
|
||||
}
|
||||
|
||||
void ui_label(UI* ui, float x, float y, const char* text) {
|
||||
(void)text;
|
||||
ui_draw_rect(ui, x, y, (float)(strlen(text) * 8), ui->font_size, 0x00000000);
|
||||
if (text) {
|
||||
ui_draw_text(ui, x, y, text, ui->text_color);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_render(UI* ui, ID3D11DeviceContext* ctx) {
|
||||
@@ -200,6 +387,8 @@ void ui_render(UI* ui, ID3D11DeviceContext* ctx) {
|
||||
|
||||
ID3D11DeviceContext_VSSetShader(ctx, ui->vertex_shader, NULL, 0);
|
||||
ID3D11DeviceContext_PSSetShader(ctx, ui->pixel_shader, NULL, 0);
|
||||
ID3D11DeviceContext_PSSetShaderResources(ctx, 0, 1, &ui->font_srv);
|
||||
ID3D11DeviceContext_PSSetSamplers(ctx, 0, 1, &ui->font_sampler);
|
||||
|
||||
float blend[4] = {1, 1, 1, 1};
|
||||
ID3D11DeviceContext_OMSetBlendState(ctx, ui->blend_state, blend, 0xFFFFFFFF);
|
||||
|
||||
+6
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
#define UI_MAX_VERTICES 16384
|
||||
#define UI_MAX_INDICES 32768
|
||||
#define UI_MAX_WIDGETS 256
|
||||
#define UI_MAX_WIDGETS 256
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
@@ -31,6 +31,8 @@ typedef struct {
|
||||
ID3D11InputLayout* input_layout;
|
||||
ID3D11BlendState* blend_state;
|
||||
ID3D11RasterizerState* rasterizer;
|
||||
ID3D11ShaderResourceView* font_srv;
|
||||
ID3D11SamplerState* font_sampler;
|
||||
|
||||
UIVertex vertices[UI_MAX_VERTICES];
|
||||
uint32_t indices[UI_MAX_INDICES];
|
||||
@@ -47,6 +49,8 @@ typedef struct {
|
||||
uint32_t bg_color;
|
||||
uint32_t border_color;
|
||||
float padding;
|
||||
|
||||
float char_uvs[128][4];
|
||||
} UI;
|
||||
|
||||
// 创建/销毁
|
||||
@@ -61,6 +65,7 @@ void ui_render(UI* ui, ID3D11DeviceContext* ctx);
|
||||
// 基础绘制
|
||||
void ui_draw_rect(UI* ui, float x, float y, float w, float h, uint32_t color);
|
||||
void ui_draw_rect_border(UI* ui, float x, float y, float w, float h, uint32_t fill, uint32_t border, float thickness);
|
||||
void ui_draw_text(UI* ui, float x, float y, const char* text, uint32_t color);
|
||||
|
||||
// 控件
|
||||
int ui_button(UI* ui, float x, float y, float w, float h, const char* text);
|
||||
|
||||
@@ -33,6 +33,30 @@ Block* block_create(BlockType type, float x, float y) {
|
||||
return block;
|
||||
}
|
||||
|
||||
// 复制积木 (深拷贝,不复制连接关系)
|
||||
Block* block_copy(const Block* src) {
|
||||
if (!src) return NULL;
|
||||
Block* block = (Block*)malloc(sizeof(Block));
|
||||
if (!block) return NULL;
|
||||
memcpy(block, src, sizeof(Block));
|
||||
|
||||
// 重新设置拥有者,断开所有连接
|
||||
for (int i = 0; i < MAX_BLOCK_INPUTS; i++) {
|
||||
block->inputs[i].owner = block;
|
||||
block->inputs[i].connected = NULL;
|
||||
}
|
||||
for (int i = 0; i < MAX_BLOCK_OUTPUTS; i++) {
|
||||
block->outputs[i].owner = block;
|
||||
block->outputs[i].connected = NULL;
|
||||
}
|
||||
block->next = NULL;
|
||||
block->parent_if = NULL;
|
||||
block->workspace = NULL;
|
||||
block->user_data = NULL;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
// 销毁积木
|
||||
void block_destroy(Block* block) {
|
||||
if (!block) return;
|
||||
|
||||
@@ -171,6 +171,7 @@ typedef struct BlockConnection {
|
||||
|
||||
// 函数声明
|
||||
Block* block_create(BlockType type, float x, float y);
|
||||
Block* block_copy(const Block* src);
|
||||
void block_destroy(Block* block);
|
||||
BlockSlot* block_add_input(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
|
||||
BlockSlot* block_add_output(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
|
||||
|
||||
@@ -51,6 +51,7 @@ void executor_destroy(BlockExecutor* exec) { free(exec); }
|
||||
void executor_start(BlockExecutor* exec) {
|
||||
exec->state = EXEC_RUNNING;
|
||||
exec->call_stack_top = 0;
|
||||
exec->loop_stack_top = 0;
|
||||
exec->current_block = NULL;
|
||||
for (int i = 0; i < exec->workspace->block_count; i++) {
|
||||
Block* block = exec->workspace->blocks[i];
|
||||
@@ -76,6 +77,26 @@ static Block* next_from_output(Block* block, int output_index) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 查找循环体链的最后一个积木(用于确定循环结束后应跳转到的积木)
|
||||
static Block* find_loop_body_end(Block* loop_block) {
|
||||
if (!loop_block || loop_block->output_count == 0 || !loop_block->outputs[0].connected) return NULL;
|
||||
Block* cur = loop_block->outputs[0].connected->owner;
|
||||
Block* prev = loop_block;
|
||||
int guard = 0;
|
||||
while (cur && guard < 1000) {
|
||||
guard++;
|
||||
if (cur == loop_block) return prev; // 回到循环头,则前一个为体尾
|
||||
Block* next = NULL;
|
||||
if (cur->output_count > 0 && cur->outputs[0].connected) {
|
||||
next = cur->outputs[0].connected->owner;
|
||||
}
|
||||
if (!next) return cur;
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
// 获取值输入
|
||||
static SlotValue get_input(Block* block, int idx) {
|
||||
if (idx >= block->input_count) return make_number(0);
|
||||
@@ -174,22 +195,54 @@ void executor_step(BlockExecutor* exec) {
|
||||
next = next_from_output(cur, cond ? 0 : 1);
|
||||
break;
|
||||
}
|
||||
case BLOCK_CONTROL_REPEAT: {
|
||||
case BLOCK_CONTROL_REPEAT:
|
||||
case BLOCK_CONTROL_WHILE: {
|
||||
int cond = get_input(cur, 1).as.boolean;
|
||||
if (cond) next = next_from_output(cur, 0);
|
||||
else next = next_from_output(cur, 0); // 退出循环: 走输出
|
||||
LoopFrame* frame = NULL;
|
||||
if (exec->loop_stack_top > 0 && exec->loop_stack[exec->loop_stack_top - 1].loop_block == cur) {
|
||||
frame = &exec->loop_stack[exec->loop_stack_top - 1];
|
||||
} else {
|
||||
if (exec->loop_stack_top < MAX_LOOP_STACK) {
|
||||
frame = &exec->loop_stack[exec->loop_stack_top++];
|
||||
frame->loop_block = cur;
|
||||
frame->counter = 0;
|
||||
frame->target = 0;
|
||||
Block* body_end = find_loop_body_end(cur);
|
||||
frame->after_block = body_end ? next_from_output(body_end, 0) : NULL;
|
||||
}
|
||||
}
|
||||
if (frame && cond) {
|
||||
frame->counter++;
|
||||
next = next_from_output(cur, 0);
|
||||
} else {
|
||||
if (frame) exec->loop_stack_top--;
|
||||
next = frame ? frame->after_block : next_from_output(cur, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BLOCK_CONTROL_REPEAT_TIMES: {
|
||||
double times = get_input(cur, 1).as.number;
|
||||
// 简化: 只执行一次,实际需要计数器
|
||||
log_info("循环 %.0f 次 (简化: 执行一次)", times);
|
||||
next = next_from_output(cur, 0);
|
||||
break;
|
||||
}
|
||||
case BLOCK_CONTROL_WHILE: {
|
||||
int cond = get_input(cur, 1).as.boolean;
|
||||
if (cond) next = next_from_output(cur, 0);
|
||||
LoopFrame* frame = NULL;
|
||||
if (exec->loop_stack_top > 0 && exec->loop_stack[exec->loop_stack_top - 1].loop_block == cur) {
|
||||
frame = &exec->loop_stack[exec->loop_stack_top - 1];
|
||||
frame->counter++;
|
||||
} else {
|
||||
if (exec->loop_stack_top < MAX_LOOP_STACK) {
|
||||
frame = &exec->loop_stack[exec->loop_stack_top++];
|
||||
frame->loop_block = cur;
|
||||
frame->counter = 0;
|
||||
frame->target = (int)times;
|
||||
Block* body_end = find_loop_body_end(cur);
|
||||
frame->after_block = body_end ? next_from_output(body_end, 0) : NULL;
|
||||
}
|
||||
}
|
||||
if (frame && frame->counter < frame->target) {
|
||||
log_info("循环 %d / %d", frame->counter + 1, frame->target);
|
||||
next = next_from_output(cur, 0);
|
||||
} else {
|
||||
if (frame) exec->loop_stack_top--;
|
||||
next = frame ? frame->after_block : next_from_output(cur, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BLOCK_CONTROL_WAIT: {
|
||||
@@ -320,19 +373,32 @@ void executor_step(BlockExecutor* exec) {
|
||||
case BLOCK_FUNC_CALL: {
|
||||
const char* fname = get_input(cur, 1).as.string;
|
||||
log_info("调用函数 '%s'", fname);
|
||||
if (exec->call_stack_top < MAX_CALL_STACK) {
|
||||
|
||||
// 在工作区中查找同名函数定义积木
|
||||
Block* def = NULL;
|
||||
for (int i = 0; i < exec->workspace->block_count; i++) {
|
||||
Block* b = exec->workspace->blocks[i];
|
||||
if (b->type == BLOCK_CONTROL_FUNCTION_DEF && strcmp(b->label, fname) == 0) {
|
||||
def = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (def && exec->call_stack_top < MAX_CALL_STACK) {
|
||||
exec->call_stack[exec->call_stack_top].block = cur;
|
||||
exec->call_stack[exec->call_stack_top].return_block = next_from_output(cur, 0);
|
||||
exec->call_stack_top++;
|
||||
next = next_from_output(def, 0);
|
||||
} else {
|
||||
if (!def) log_error("未找到函数定义: '%s'", fname);
|
||||
next = next_from_output(cur, 0);
|
||||
}
|
||||
// TODO: 查找函数定义积木并跳转
|
||||
next = next_from_output(cur, 0);
|
||||
break;
|
||||
}
|
||||
case BLOCK_FUNC_RETURN: {
|
||||
if (exec->call_stack_top > 0) {
|
||||
exec->call_stack_top--;
|
||||
Block* caller = exec->call_stack[exec->call_stack_top].block;
|
||||
next = next_from_output(caller, 0);
|
||||
next = exec->call_stack[exec->call_stack_top].return_block;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -343,6 +409,14 @@ void executor_step(BlockExecutor* exec) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 若当前 next 是某个活跃循环的 after_block,则跳转回循环头继续判断
|
||||
if (next && exec->loop_stack_top > 0) {
|
||||
LoopFrame* frame = &exec->loop_stack[exec->loop_stack_top - 1];
|
||||
if (next == frame->after_block) {
|
||||
next = frame->loop_block;
|
||||
}
|
||||
}
|
||||
|
||||
exec->current_block = next;
|
||||
if (!exec->current_block) {
|
||||
log_info("执行完成");
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define MAX_VARIABLES 256
|
||||
#define MAX_CALL_STACK 32
|
||||
#define MAX_EXEC_STACK 1024
|
||||
#define MAX_LOOP_STACK 32
|
||||
|
||||
// 变量
|
||||
typedef struct {
|
||||
@@ -23,6 +24,14 @@ typedef struct {
|
||||
Block* return_block;
|
||||
} CallFrame;
|
||||
|
||||
// 循环帧
|
||||
typedef struct {
|
||||
Block* loop_block;
|
||||
int counter;
|
||||
int target;
|
||||
Block* after_block;
|
||||
} LoopFrame;
|
||||
|
||||
// 执行状态
|
||||
typedef enum {
|
||||
EXEC_IDLE = 0,
|
||||
@@ -43,6 +52,9 @@ typedef struct BlockExecutor {
|
||||
CallFrame call_stack[MAX_CALL_STACK];
|
||||
int call_stack_top;
|
||||
|
||||
LoopFrame loop_stack[MAX_LOOP_STACK];
|
||||
int loop_stack_top;
|
||||
|
||||
Block* current_block;
|
||||
double wait_timer;
|
||||
|
||||
|
||||
@@ -116,11 +116,25 @@ void interaction_key_down(BlockInteraction* inter, BlockWorkspace* ws, int key)
|
||||
}
|
||||
|
||||
// 复制/粘贴 (Ctrl+C / Ctrl+V)
|
||||
static Block* clipboard = NULL;
|
||||
if (key == 'C' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
|
||||
// TODO: 复制到剪贴板
|
||||
if (inter->selected_block) {
|
||||
if (clipboard) block_destroy(clipboard);
|
||||
clipboard = block_copy(inter->selected_block);
|
||||
}
|
||||
}
|
||||
if (key == 'V' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
|
||||
// TODO: 从剪贴板粘贴
|
||||
if (clipboard) {
|
||||
Block* copy = block_copy(clipboard);
|
||||
if (copy) {
|
||||
copy->x += 20.0f;
|
||||
copy->y += 20.0f;
|
||||
copy->id = ws->next_block_id++;
|
||||
workspace_add_block(ws, copy);
|
||||
inter->selected_block = copy;
|
||||
ws->selected_block = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
#include "block_core.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// 5x7 位图字体 (列优先, LSB 在上)
|
||||
#include "block_font.inc"
|
||||
|
||||
int block_renderer_init(BlockRenderer* renderer, void* device, void* context) {
|
||||
memset(renderer, 0, sizeof(BlockRenderer));
|
||||
@@ -94,10 +98,30 @@ void block_renderer_add_line(BlockRenderer* renderer, float x1, float y1, float
|
||||
float dy = y2 - y1;
|
||||
float len = sqrtf(dx * dx + dy * dy);
|
||||
if (len < 0.001f) return;
|
||||
float nx = -dy / len;
|
||||
float ny = dx / len;
|
||||
|
||||
block_renderer_add_quad(renderer, x1, y1, x2 - x1, y2 - y1, r, g, b, a);
|
||||
const float thickness = 1.0f;
|
||||
float nx = -dy / len * thickness * 0.5f;
|
||||
float ny = dx / len * thickness * 0.5f;
|
||||
|
||||
if (renderer->vertex_count + 4 > renderer->vertex_capacity) return;
|
||||
if (renderer->index_count + 6 > renderer->index_capacity) return;
|
||||
|
||||
uint32_t base = renderer->vertex_count;
|
||||
float* v = renderer->vertex_data + base * 7;
|
||||
|
||||
v[0*7+0] = x1 + nx; v[0*7+1] = y1 + ny; v[0*7+2] = 0; v[0*7+3] = r; v[0*7+4] = g; v[0*7+5] = b; v[0*7+6] = a;
|
||||
v[1*7+0] = x2 + nx; v[1*7+1] = y2 + ny; v[1*7+2] = 0; v[1*7+3] = r; v[1*7+4] = g; v[1*7+5] = b; v[1*7+6] = a;
|
||||
v[2*7+0] = x2 - nx; v[2*7+1] = y2 - ny; v[2*7+2] = 0; v[2*7+3] = r; v[2*7+4] = g; v[2*7+5] = b; v[2*7+6] = a;
|
||||
v[3*7+0] = x1 - nx; v[3*7+1] = y1 - ny; v[3*7+2] = 0; v[3*7+3] = r; v[3*7+4] = g; v[3*7+5] = b; v[3*7+6] = a;
|
||||
|
||||
renderer->vertex_count += 4;
|
||||
|
||||
renderer->index_data[renderer->index_count++] = base + 0;
|
||||
renderer->index_data[renderer->index_count++] = base + 1;
|
||||
renderer->index_data[renderer->index_count++] = base + 2;
|
||||
renderer->index_data[renderer->index_count++] = base + 0;
|
||||
renderer->index_data[renderer->index_count++] = base + 2;
|
||||
renderer->index_data[renderer->index_count++] = base + 3;
|
||||
}
|
||||
|
||||
static void block_renderer_add_bezier(BlockRenderer* renderer, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float r, float g, float b, float a) {
|
||||
@@ -122,6 +146,10 @@ void block_renderer_draw_block(BlockRenderer* renderer, Block* block, float view
|
||||
|
||||
block_renderer_add_quad(renderer, x, y, w, h, 0.3f, 0.3f, 0.35f, 1.0f);
|
||||
block_renderer_add_quad(renderer, x, y, w, 20 * zoom, 0.2f, 0.2f, 0.25f, 1.0f);
|
||||
|
||||
if (block->label[0]) {
|
||||
block_renderer_draw_text(renderer, block->label, x + 4.0f * zoom, y + 4.0f * zoom, 0.9f, 0.9f, 0.9f);
|
||||
}
|
||||
}
|
||||
|
||||
void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, BlockSlot* to, float view_x, float view_y, float zoom) {
|
||||
@@ -138,5 +166,25 @@ void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, Bl
|
||||
|
||||
void block_renderer_draw_text(BlockRenderer* renderer, const char* text, float x, float y, float r, float g, float b) {
|
||||
if (!text) return;
|
||||
(void)renderer; (void)x; (void)y; (void)r; (void)g; (void)b;
|
||||
const float scale = 2.0f;
|
||||
const float advance = 5.0f * scale + 1.0f;
|
||||
float cx = x;
|
||||
for (const char* p = text; *p; ++p) {
|
||||
unsigned char ch = (unsigned char)*p;
|
||||
if (ch < 32 || ch > 127) ch = '?';
|
||||
int idx = ch - 32;
|
||||
for (int col = 0; col < 5; ++col) {
|
||||
uint8_t column = BLOCK_FONT[idx][col];
|
||||
for (int row = 0; row < 7; ++row) {
|
||||
if (column & (1u << row)) {
|
||||
block_renderer_add_quad(renderer,
|
||||
cx + col * scale,
|
||||
y + row * scale,
|
||||
scale, scale,
|
||||
r, g, b, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
cx += advance;
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,6 @@ static void json_append(JsonBuffer* buf, const char* str) {
|
||||
buf->size += len;
|
||||
}
|
||||
|
||||
static void json_free(JsonBuffer* buf) {
|
||||
free(buf->buffer);
|
||||
buf->buffer = NULL;
|
||||
buf->size = 0;
|
||||
}
|
||||
|
||||
int serializer_save(BlockWorkspace* ws, const char* filepath) {
|
||||
char* json = serializer_save_to_string(ws);
|
||||
if (!json) return 0;
|
||||
@@ -98,9 +92,38 @@ char* serializer_save_to_string(BlockWorkspace* ws) {
|
||||
snprintf(tmp, 256, " \"x\": %.1f,\n", block->x);
|
||||
json_append(&buf, tmp);
|
||||
|
||||
snprintf(tmp, 256, " \"y\": %.1f\n", block->y);
|
||||
snprintf(tmp, 256, " \"y\": %.1f,\n", block->y);
|
||||
json_append(&buf, tmp);
|
||||
|
||||
|
||||
snprintf(tmp, 256, " \"label\": \"%s\"", block->label);
|
||||
json_append(&buf, tmp);
|
||||
|
||||
// 保存输入槽默认值
|
||||
if (block->input_count > 0) {
|
||||
json_append(&buf, ",\n \"inputs\": [\n");
|
||||
for (int j = 0; j < block->input_count; j++) {
|
||||
BlockSlot* slot = &block->inputs[j];
|
||||
SlotValue* val = &slot->value;
|
||||
switch (val->type) {
|
||||
case SLOT_VALUE_NUMBER:
|
||||
snprintf(tmp, 256, " {\"type\": \"number\", \"value\": %.6g}%s\n", val->as.number, j < block->input_count - 1 ? "," : "");
|
||||
break;
|
||||
case SLOT_VALUE_STRING:
|
||||
snprintf(tmp, 256, " {\"type\": \"string\", \"value\": \"%s\"}%s\n", val->as.string, j < block->input_count - 1 ? "," : "");
|
||||
break;
|
||||
case SLOT_VALUE_BOOLEAN:
|
||||
snprintf(tmp, 256, " {\"type\": \"boolean\", \"value\": %s}%s\n", val->as.boolean ? "true" : "false", j < block->input_count - 1 ? "," : "");
|
||||
break;
|
||||
default:
|
||||
snprintf(tmp, 256, " {\"type\": \"number\", \"value\": 0}%s\n", j < block->input_count - 1 ? "," : "");
|
||||
break;
|
||||
}
|
||||
json_append(&buf, tmp);
|
||||
}
|
||||
json_append(&buf, " ]");
|
||||
}
|
||||
|
||||
json_append(&buf, "\n");
|
||||
json_append(&buf, i < ws->block_count - 1 ? " },\n" : " }\n");
|
||||
}
|
||||
json_append(&buf, " ],\n");
|
||||
@@ -123,20 +146,270 @@ char* serializer_save_to_string(BlockWorkspace* ws) {
|
||||
return buf.buffer;
|
||||
}
|
||||
|
||||
// 跳过空白字符
|
||||
static const char* skip_ws(const char* p) {
|
||||
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
// 查找字符串值,返回指向引号内内容的指针,并在 *len 中设置长度
|
||||
static const char* parse_json_string(const char* p, int* len) {
|
||||
p = skip_ws(p);
|
||||
if (*p != '"') return NULL;
|
||||
p++;
|
||||
const char* start = p;
|
||||
while (*p && *p != '"') p++;
|
||||
if (*p != '"') return NULL;
|
||||
*len = (int)(p - start);
|
||||
return start;
|
||||
}
|
||||
|
||||
// 查找数字值,返回指向数字开头的指针
|
||||
static const char* parse_json_number(const char* p, float* out) {
|
||||
p = skip_ws(p);
|
||||
const char* start = p;
|
||||
if (*p == '-') p++;
|
||||
while (*p >= '0' && *p <= '9') p++;
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
while (*p >= '0' && *p <= '9') p++;
|
||||
}
|
||||
if (p == start) return NULL;
|
||||
*out = (float)strtod(start, NULL);
|
||||
return p;
|
||||
}
|
||||
|
||||
int serializer_load_from_string(BlockWorkspace* ws, const char* json_str) {
|
||||
// 简化的 JSON 解析
|
||||
// 完整实现需要完整的 JSON 解析器
|
||||
|
||||
workspace_clear(ws);
|
||||
|
||||
// 查找 "blocks" 数组
|
||||
const char* blocks_start = strstr(json_str, "\"blocks\"");
|
||||
if (!blocks_start) return 0;
|
||||
|
||||
// 简化解析 - 只提取基本信息
|
||||
// 实际项目中应使用 cJSON 或类似库
|
||||
|
||||
// TODO: 实现完整的 JSON 解析
|
||||
|
||||
block_types_init();
|
||||
|
||||
// 解析 blocks 数组
|
||||
const char* blocks_key = strstr(json_str, "\"blocks\"");
|
||||
if (!blocks_key) return 0;
|
||||
const char* p = blocks_key + 8;
|
||||
p = skip_ws(p);
|
||||
if (*p != ':') return 0;
|
||||
p++;
|
||||
p = skip_ws(p);
|
||||
if (*p != '[') return 0;
|
||||
p++;
|
||||
|
||||
while (1) {
|
||||
p = skip_ws(p);
|
||||
if (*p == ']') { p++; break; }
|
||||
if (*p != '{') return 0;
|
||||
p++;
|
||||
|
||||
int id = 0;
|
||||
char type_name[64] = {0};
|
||||
char label[64] = {0};
|
||||
float x = 0, y = 0;
|
||||
SlotValue input_values[MAX_BLOCK_INPUTS];
|
||||
int input_count = 0;
|
||||
|
||||
while (1) {
|
||||
p = skip_ws(p);
|
||||
if (*p == '}') { p++; break; }
|
||||
|
||||
int key_len = 0;
|
||||
const char* key = parse_json_string(p, &key_len);
|
||||
if (!key) return 0;
|
||||
p = key + key_len + 1;
|
||||
p = skip_ws(p);
|
||||
if (*p != ':') return 0;
|
||||
p++;
|
||||
|
||||
if (key_len == 2 && strncmp(key, "id", 2) == 0) {
|
||||
float fv; p = parse_json_number(p, &fv); id = (int)fv;
|
||||
} else if (key_len == 4 && strncmp(key, "type", 4) == 0) {
|
||||
int val_len = 0;
|
||||
const char* val = parse_json_string(p, &val_len);
|
||||
if (val) {
|
||||
if (val_len >= (int)sizeof(type_name)) val_len = (int)sizeof(type_name) - 1;
|
||||
strncpy(type_name, val, val_len);
|
||||
type_name[val_len] = '\0';
|
||||
p = val + val_len + 1;
|
||||
}
|
||||
} else if (key_len == 1 && *key == 'x') {
|
||||
p = parse_json_number(p, &x);
|
||||
} else if (key_len == 1 && *key == 'y') {
|
||||
p = parse_json_number(p, &y);
|
||||
} else if (key_len == 5 && strncmp(key, "label", 5) == 0) {
|
||||
int val_len = 0;
|
||||
const char* val = parse_json_string(p, &val_len);
|
||||
if (val) {
|
||||
if (val_len >= (int)sizeof(label)) val_len = (int)sizeof(label) - 1;
|
||||
strncpy(label, val, val_len);
|
||||
label[val_len] = '\0';
|
||||
p = val + val_len + 1;
|
||||
}
|
||||
} else if (key_len == 6 && strncmp(key, "inputs", 6) == 0) {
|
||||
p = skip_ws(p);
|
||||
if (*p == '[') {
|
||||
p++;
|
||||
input_count = 0;
|
||||
while (1) {
|
||||
p = skip_ws(p);
|
||||
if (*p == ']') { p++; break; }
|
||||
if (*p != '{') break;
|
||||
p++;
|
||||
|
||||
char iv_type[16] = {0};
|
||||
float fv = 0;
|
||||
int bv = 0;
|
||||
char sval_str[256] = {0};
|
||||
while (1) {
|
||||
p = skip_ws(p);
|
||||
if (*p == '}') { p++; break; }
|
||||
int ik_len = 0;
|
||||
const char* ik = parse_json_string(p, &ik_len);
|
||||
if (!ik) break;
|
||||
p = ik + ik_len + 1;
|
||||
p = skip_ws(p);
|
||||
if (*p != ':') break;
|
||||
p++;
|
||||
|
||||
if (ik_len == 4 && strncmp(ik, "type", 4) == 0) {
|
||||
int iv_len = 0;
|
||||
const char* iv = parse_json_string(p, &iv_len);
|
||||
if (iv) {
|
||||
if (iv_len >= (int)sizeof(iv_type)) iv_len = (int)sizeof(iv_type) - 1;
|
||||
strncpy(iv_type, iv, iv_len);
|
||||
iv_type[iv_len] = '\0';
|
||||
p = iv + iv_len + 1;
|
||||
}
|
||||
} else if (ik_len == 5 && strncmp(ik, "value", 5) == 0) {
|
||||
p = skip_ws(p);
|
||||
if (*p == '"') {
|
||||
int sv_len = 0;
|
||||
const char* sval = parse_json_string(p, &sv_len);
|
||||
if (sval) {
|
||||
if (sv_len >= (int)sizeof(sval_str)) sv_len = (int)sizeof(sval_str) - 1;
|
||||
strncpy(sval_str, sval, sv_len);
|
||||
sval_str[sv_len] = '\0';
|
||||
p = sval + sv_len + 1;
|
||||
}
|
||||
} else if (strncmp(p, "true", 4) == 0) {
|
||||
bv = 1; p += 4;
|
||||
} else if (strncmp(p, "false", 5) == 0) {
|
||||
bv = 0; p += 5;
|
||||
} else {
|
||||
p = parse_json_number(p, &fv);
|
||||
}
|
||||
} else {
|
||||
while (*p && *p != ',' && *p != '}') p++;
|
||||
}
|
||||
p = skip_ws(p);
|
||||
if (*p == ',') p++;
|
||||
}
|
||||
// 暂存输入值
|
||||
if (input_count < MAX_BLOCK_INPUTS) {
|
||||
SlotValue* sv = &input_values[input_count++];
|
||||
memset(sv, 0, sizeof(SlotValue));
|
||||
if (strcmp(iv_type, "number") == 0) {
|
||||
sv->type = SLOT_VALUE_NUMBER;
|
||||
sv->as.number = (double)fv;
|
||||
} else if (strcmp(iv_type, "string") == 0) {
|
||||
sv->type = SLOT_VALUE_STRING;
|
||||
strncpy(sv->as.string, sval_str, sizeof(sv->as.string) - 1);
|
||||
} else if (strcmp(iv_type, "boolean") == 0) {
|
||||
sv->type = SLOT_VALUE_BOOLEAN;
|
||||
sv->as.boolean = bv;
|
||||
} else {
|
||||
sv->type = SLOT_VALUE_NUMBER;
|
||||
sv->as.number = 0;
|
||||
}
|
||||
}
|
||||
p = skip_ws(p);
|
||||
if (*p == ',') p++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 跳过未知值 (简单跳过直到逗号或右花括号)
|
||||
while (*p && *p != ',' && *p != '}') p++;
|
||||
}
|
||||
|
||||
p = skip_ws(p);
|
||||
if (*p == ',') p++;
|
||||
}
|
||||
|
||||
BlockType type = block_type_find_by_name(type_name);
|
||||
if (type == BLOCK_TYPE_COUNT || type_name[0] == '\0') {
|
||||
type = BLOCK_EVENT_START; // fallback
|
||||
}
|
||||
|
||||
Block* block = block_create(type, x, y);
|
||||
if (block) {
|
||||
block->id = id;
|
||||
strncpy(block->label, label, sizeof(block->label) - 1);
|
||||
block->label[sizeof(block->label) - 1] = '\0';
|
||||
for (int j = 0; j < input_count && j < block->input_count; j++) {
|
||||
block->inputs[j].value = input_values[j];
|
||||
}
|
||||
workspace_add_block(ws, block);
|
||||
if (id >= ws->next_block_id) ws->next_block_id = id + 1;
|
||||
}
|
||||
|
||||
p = skip_ws(p);
|
||||
if (*p == ',') p++;
|
||||
}
|
||||
|
||||
// 解析 connections 数组
|
||||
const char* conn_key = strstr(json_str, "\"connections\"");
|
||||
if (conn_key) {
|
||||
p = conn_key + 13;
|
||||
p = skip_ws(p);
|
||||
if (*p == ':') {
|
||||
p++;
|
||||
p = skip_ws(p);
|
||||
if (*p == '[') {
|
||||
p++;
|
||||
while (1) {
|
||||
p = skip_ws(p);
|
||||
if (*p == ']') { p++; break; }
|
||||
if (*p != '{') break;
|
||||
p++;
|
||||
|
||||
int from_id = -1, to_id = -1;
|
||||
while (1) {
|
||||
p = skip_ws(p);
|
||||
if (*p == '}') { p++; break; }
|
||||
|
||||
int key_len = 0;
|
||||
const char* key = parse_json_string(p, &key_len);
|
||||
if (!key) break;
|
||||
p = key + key_len + 1;
|
||||
p = skip_ws(p);
|
||||
if (*p != ':') break;
|
||||
p++;
|
||||
|
||||
float fv = 0;
|
||||
if (key_len == 4 && strncmp(key, "from", 4) == 0) {
|
||||
p = parse_json_number(p, &fv); from_id = (int)fv;
|
||||
} else if (key_len == 2 && strncmp(key, "to", 2) == 0) {
|
||||
p = parse_json_number(p, &fv); to_id = (int)fv;
|
||||
} else {
|
||||
while (*p && *p != ',' && *p != '}') p++;
|
||||
}
|
||||
|
||||
p = skip_ws(p);
|
||||
if (*p == ',') p++;
|
||||
}
|
||||
|
||||
if (from_id >= 0 && to_id >= 0) {
|
||||
Block* from_block = workspace_find_block(ws, from_id);
|
||||
Block* to_block = workspace_find_block(ws, to_id);
|
||||
if (from_block && to_block && from_block->output_count > 0 && to_block->input_count > 0) {
|
||||
workspace_add_connection(ws, &from_block->outputs[0], &to_block->inputs[0]);
|
||||
}
|
||||
}
|
||||
|
||||
p = skip_ws(p);
|
||||
if (*p == ',') p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -58,23 +58,23 @@ static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM l
|
||||
return 0;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(0);
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(0, 1);
|
||||
return 0;
|
||||
case WM_RBUTTONDOWN:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(1);
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(1, 1);
|
||||
return 0;
|
||||
case WM_MBUTTONDOWN:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(2);
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(2, 1);
|
||||
return 0;
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(0);
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(0, 0);
|
||||
return 0;
|
||||
case WM_RBUTTONUP:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(1);
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(1, 0);
|
||||
return 0;
|
||||
case WM_MBUTTONUP:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(2);
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(2, 0);
|
||||
return 0;
|
||||
|
||||
case WM_DESTROY:
|
||||
|
||||
@@ -20,7 +20,7 @@ typedef struct Window {
|
||||
typedef void (*WindowKeyCallback)(int key);
|
||||
typedef void (*WindowResizeCallback)(int width, int height);
|
||||
typedef void (*WindowMouseMoveCallback)(int x, int y);
|
||||
typedef void (*WindowMouseButtonCallback)(int button);
|
||||
typedef void (*WindowMouseButtonCallback)(int button, int pressed);
|
||||
|
||||
// 创建/销毁
|
||||
Window* window_create(const char* title, int width, int height, int fullscreen);
|
||||
|
||||
@@ -19,7 +19,7 @@ extern "C" {
|
||||
// memory.asm - 对齐内存分配
|
||||
// ============================================================================
|
||||
|
||||
#ifndef MATH_ASM_AVAILABLE
|
||||
#if !defined(MATH_ASM_AVAILABLE) || MATH_ASM_AVAILABLE == 0
|
||||
// 在 C fallback 模式下,这些函数已在 math_c_fallback.h 中定义
|
||||
// 不需要重复声明
|
||||
#else
|
||||
|
||||
@@ -12,25 +12,28 @@ set DEMO_DIR=Demo
|
||||
set OUTPUT=MiMoEngine_ASM.exe
|
||||
|
||||
set CFLAGS=-O2 -Wall -Wextra -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS -DMATH_ASM_AVAILABLE=0 -I%ENGINE_DIR% -I%ENGINE_DIR%\include
|
||||
set LDFLAGS=-ld3d11 -ldxgi -ld3dcompiler -lole32 -loleaut32 -luuid -lgdi32 -luser32 -lkernel32 -lwinmm -static
|
||||
set LDFLAGS=-ld3d11 -ldxgi -ld3dcompiler -lole32 -loleaut32 -luuid -lgdi32 -luser32 -lkernel32 -lwinmm -lxinput1_4 -lwindowscodecs -static
|
||||
|
||||
echo === MiMoEngine ASM Build ===
|
||||
echo Compiler: %GCC%
|
||||
|
||||
REM 创建输出目录
|
||||
REM 创建输出目录并清理旧对象
|
||||
if not exist build mkdir build
|
||||
if exist build\*.o del /Q build\*.o
|
||||
|
||||
REM 编译引擎 C 源文件
|
||||
echo [1/3] Compiling Engine sources...
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Core\engine.c -o build\engine.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Core\logger.c -o build\logger.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Core\timer.c -o build\timer.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Core\asset_manager.c -o build\asset_manager.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Window\window.c -o build\window.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Rendering\graphics_device.c -o build\graphics_device.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Rendering\renderer.c -o build\renderer.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Rendering\shader.c -o build\shader.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Rendering\mesh.c -o build\mesh.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Rendering\texture.c -o build\texture.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Rendering\material.c -o build\material.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Rendering\camera.c -o build\camera.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\ECS\world.c -o build\world.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Input\input.c -o build\input.o
|
||||
@@ -38,9 +41,12 @@ echo [1/3] Compiling Engine sources...
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Audio\audio.c -o build\audio.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\UI\ui.c -o build\ui.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Scripting\lua_engine.c -o build\lua_engine.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Scene\scene_graph.c -o build\scene_graph.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Scene\scene_render.c -o build\scene_render.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Scene\scene_serialize.c -o build\scene_serialize.o
|
||||
|
||||
REM 编译 VisualScript 模块
|
||||
echo [2/3] Compiling VisualScript...
|
||||
echo [2/4] Compiling VisualScript...
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\VisualScript\block_core.c -o build\block_core.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\VisualScript\block_types.c -o build\block_types.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\VisualScript\block_workspace.c -o build\block_workspace.o
|
||||
@@ -51,23 +57,36 @@ echo [2/3] Compiling VisualScript...
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\VisualScript\block_serializer.c -o build\block_serializer.o
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\VisualScript\block_editor.c -o build\block_editor.o
|
||||
|
||||
REM 编译 Demo
|
||||
echo [3/3] Compiling Demo...
|
||||
%GCC% %CFLAGS% -c %DEMO_DIR%\main.c -o build\main.o
|
||||
REM 编译 Editor 模块
|
||||
echo [3/4] Compiling Editor...
|
||||
%GCC% %CFLAGS% -c %ENGINE_DIR%\Editor\glyph_atlas.c -o build\glyph_atlas.o
|
||||
|
||||
REM 链接
|
||||
echo Linking...
|
||||
%GXX% build\*.o -o build\%OUTPUT% %LDFLAGS%
|
||||
REM 编译 Demo
|
||||
echo [4/4] Compiling Demo...
|
||||
%GCC% %CFLAGS% -c %DEMO_DIR%\main.c -o build\main.o
|
||||
%GCC% %CFLAGS% -c %DEMO_DIR%\audio_demo.c -o build\audio_demo.o
|
||||
%GCC% %CFLAGS% -c %DEMO_DIR%\editor_main.c -o build\editor_main.o
|
||||
|
||||
REM 链接主程序
|
||||
echo Linking main...
|
||||
%GXX% build\engine.o build\logger.o build\timer.o build\asset_manager.o build\window.o build\graphics_device.o build\renderer.o build\shader.o build\mesh.o build\texture.o build\material.o build\camera.o build\world.o build\input.o build\physics.o build\audio.o build\ui.o build\lua_engine.o build\scene_graph.o build\scene_render.o build\scene_serialize.o build\block_core.o build\block_types.o build\block_workspace.o build\block_interaction.o build\block_renderer.o build\block_palette.o build\block_executor.o build\block_serializer.o build\block_editor.o build\glyph_atlas.o build\main.o -o build\%OUTPUT% %LDFLAGS%
|
||||
|
||||
REM 链接音频演示
|
||||
echo Linking audio_demo...
|
||||
%GXX% build\engine.o build\logger.o build\timer.o build\asset_manager.o build\window.o build\graphics_device.o build\renderer.o build\shader.o build\mesh.o build\texture.o build\material.o build\camera.o build\world.o build\input.o build\physics.o build\audio.o build\ui.o build\lua_engine.o build\scene_graph.o build\scene_render.o build\scene_serialize.o build\block_core.o build\block_types.o build\block_workspace.o build\block_interaction.o build\block_renderer.o build\block_palette.o build\block_executor.o build\block_serializer.o build\block_editor.o build\glyph_atlas.o build\audio_demo.o -o build\audio_demo.exe %LDFLAGS%
|
||||
|
||||
REM 链接编辑器
|
||||
echo Linking editor...
|
||||
%GXX% build\engine.o build\logger.o build\timer.o build\asset_manager.o build\window.o build\graphics_device.o build\renderer.o build\shader.o build\mesh.o build\texture.o build\material.o build\camera.o build\world.o build\input.o build\physics.o build\audio.o build\ui.o build\lua_engine.o build\scene_graph.o build\scene_render.o build\scene_serialize.o build\block_core.o build\block_types.o build\block_workspace.o build\block_interaction.o build\block_renderer.o build\block_palette.o build\block_executor.o build\block_serializer.o build\block_editor.o build\glyph_atlas.o build\editor_main.o -o build\editor.exe %LDFLAGS% -mwindows
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo.
|
||||
echo === Build succeeded! ===
|
||||
echo Output: build\%OUTPUT%
|
||||
echo Audio Demo: build\audio_demo.exe
|
||||
echo.
|
||||
echo Run with: build\%OUTPUT%
|
||||
echo Run with: build\%OUTPUT% or build\audio_demo.exe
|
||||
) else (
|
||||
echo.
|
||||
echo === Build FAILED! ===
|
||||
)
|
||||
|
||||
pause
|
||||
Reference in new issue
Block a user