#include "../Engine/Core/engine.h" #include "../Engine/Core/logger.h" #include "../Engine/Core/timer.h" #include "../Engine/Core/asset_manager.h" #include "../Engine/Rendering/graphics_device.h" #include "../Engine/Rendering/renderer.h" #include "../Engine/Rendering/shader.h" #include "../Engine/Rendering/mesh.h" #include "../Engine/Rendering/material.h" #include "../Engine/Rendering/camera.h" #include "../Engine/Rendering/camera_controller.h" #include "../Engine/Terrain/terrain.h" #include "../Engine/Terrain/terrain_manager.h" #include "../Engine/Rendering/frustum.h" #include "../Engine/Scene/scene_graph.h" #include "../Engine/Scene/scene_render.h" #include "../Engine/Scene/scene_serialize.h" #include "../Engine/include/engine_api.h" #include "../Engine/ASM/math_c_fallback.h" #include #include #include #include #include static AssetManager* g_assets = NULL; static Asset* g_shader_asset = NULL; static Camera g_camera; static CameraController g_controller; static TerrainManager* g_terrain = NULL; static Material* g_terrain_mat = NULL; static Mesh* g_cube = NULL; static Material* g_cube_mat = NULL; static Mesh* g_water = NULL; static Material* g_water_mat = NULL; static Scene* g_test_scene = NULL; static int g_mouse_down[3] = {0}; static int g_last_mouse_x = 0, g_last_mouse_y = 0; static int g_mouse_first = 1; static int g_walk_mode = 0; // ============================================================================ // 简易植被/岩石散布 // ============================================================================ #define MAX_SCATTER 512 typedef struct { Mesh* mesh; Material* mat; Mat4 world; } ScatterInstance; static ScatterInstance g_scatter[MAX_SCATTER]; static int g_scatter_count = 0; static void add_scatter(Mesh* mesh, Material* mat, Vec3 pos, Vec3 scale) { if (g_scatter_count >= MAX_SCATTER) return; ScatterInstance* s = &g_scatter[g_scatter_count++]; s->mesh = mesh; s->mat = mat; mat4_identity(&s->world); s->world.m[0][0] = scale.x; s->world.m[1][1] = scale.y; s->world.m[2][2] = scale.z; s->world.m[3][0] = pos.x; s->world.m[3][1] = pos.y; s->world.m[3][2] = pos.z; } static void generate_scatter(ID3D11Device* dev, Shader* shader) { Mesh* rock_mesh = mesh_create_cube(dev); Material* rock_mat = material_create(shader, NULL); rock_mat->color = (Vec4){0.55f, 0.50f, 0.45f, 1.0f}; Mesh* trunk_mesh = mesh_create_cube(dev); Material* trunk_mat = material_create(shader, NULL); trunk_mat->color = (Vec4){0.55f, 0.40f, 0.25f, 1.0f}; Mesh* leaves_mesh = mesh_create_sphere(dev, 1.0f, 8); Material* leaves_mat = material_create(shader, NULL); leaves_mat->color = (Vec4){0.22f, 0.65f, 0.28f, 1.0f}; for (int i = 0; i < MAX_SCATTER; i++) { float x = ((float)rand() / (float)RAND_MAX) * 400.0f - 200.0f; float z = ((float)rand() / (float)RAND_MAX) * 400.0f - 200.0f; float h = terrain_manager_get_height(g_terrain, x, z); // 跳过过低(水)或过高的山顶 if (h < -2.0f || h > 20.0f) continue; int type = rand() % 3; if (type == 0) { // 岩石 float s = 1.0f + ((float)rand() / (float)RAND_MAX) * 2.0f; Vec3 pos = {x, h + s * 0.3f, z}; Vec3 sc = {s, s * 0.6f, s}; add_scatter(rock_mesh, rock_mat, pos, sc); } else { // 树 float trunk_h = 1.5f + ((float)rand() / (float)RAND_MAX) * 1.5f; Vec3 trunk_pos = {x, h + trunk_h * 0.5f, z}; Vec3 trunk_sc = {0.25f, trunk_h, 0.25f}; add_scatter(trunk_mesh, trunk_mat, trunk_pos, trunk_sc); float leaves_s = 1.2f + ((float)rand() / (float)RAND_MAX) * 1.2f; Vec3 leaves_pos = {x, h + trunk_h + leaves_s * 0.4f, z}; Vec3 leaves_sc = {leaves_s, leaves_s, leaves_s}; add_scatter(leaves_mesh, leaves_mat, leaves_pos, leaves_sc); } } LOG_INFO("Generated %d scatter instances", g_scatter_count); } static void render_scatter(Renderer* r, const Vec4 planes[6]) { const float cull_distance = 140.0f; const float cull_dist2 = cull_distance * cull_distance; int rendered = 0; for (int i = 0; i < g_scatter_count; i++) { ScatterInstance* s = &g_scatter[i]; Vec3 pos = {s->world.m[3][0], s->world.m[3][1], s->world.m[3][2]}; Vec3 d; vec3_sub(&pos, &g_camera.position, &d); if (vec3_dot(&d, &d) > cull_dist2) continue; // 视锥剔除:用包围球近似 float radius = fmaxf(fmaxf(s->world.m[0][0], s->world.m[1][1]), s->world.m[2][2]) * 0.866f; if (!frustum_intersects_sphere(planes, pos, radius)) continue; renderer_draw_mesh_with_material(r, s->mesh, s->mat, &s->world); rendered++; } LOG_INFO("Scatter render: visible=%d/%d", rendered, g_scatter_count); } // ============================================================================ // 地形高度回调 // ============================================================================ static float get_terrain_height(float x, float z, void* user) { (void)user; if (g_terrain) return terrain_manager_get_height(g_terrain, x, z); return 0.0f; } // ============================================================================ // 引擎回调 // ============================================================================ static void on_render(Engine* engine) { Renderer* r = engine_get_renderer(engine); GraphicsDevice* dev = engine_get_graphics_device(engine); if (!r || !dev) return; renderer_set_view(r, camera_get_view(&g_camera)); renderer_set_projection(r, camera_get_projection(&g_camera)); renderer_begin_frame(r); Mat4 vp; renderer_get_view_projection(r, &vp); Vec4 planes[6]; frustum_from_matrix(&vp, planes); if (g_water) { Mat4 water_world; mat4_identity(&water_world); water_world.m[3][1] = -5.0f; renderer_draw_mesh_with_material(r, g_water, g_water_mat, &water_world); } if (g_terrain) { terrain_manager_update(g_terrain, g_camera.position); terrain_manager_render(g_terrain, r); } render_scatter(r, planes); if (g_test_scene) { scene_update(g_test_scene); scene_render(g_test_scene, r); } renderer_end_frame(r); if (engine->frame_count == 60) { graphics_device_save_screenshot(dev, "open_world_screenshot1.bmp"); } if (engine->frame_count == 180) { graphics_device_save_screenshot(dev, "open_world_screenshot2.bmp"); } } static void on_update(Engine* engine, double dt) { (void)engine; float fdt = (float)dt; Vec3 dir = {0.0f, 0.0f, 0.0f}; if (GetAsyncKeyState('W') & 0x8000) dir.z += 1.0f; if (GetAsyncKeyState('S') & 0x8000) dir.z -= 1.0f; if (GetAsyncKeyState('A') & 0x8000) dir.x -= 1.0f; if (GetAsyncKeyState('D') & 0x8000) dir.x += 1.0f; if (!g_walk_mode) { if (GetAsyncKeyState(VK_SPACE) & 0x8000) dir.y += 1.0f; if (GetAsyncKeyState(VK_SHIFT) & 0x8000) dir.y -= 1.0f; } if (dir.x != 0.0f || dir.y != 0.0f || dir.z != 0.0f) { camera_controller_move(&g_controller, dir, fdt); } camera_controller_update(&g_controller, fdt); } static void on_key(Engine* engine, int key) { (void)engine; if (key == VK_ESCAPE) engine_request_quit(engine); // 切换步行/飞行模式 if (key == 'F' || key == 'f') { g_walk_mode = !g_walk_mode; camera_controller_set_mode(&g_controller, g_walk_mode ? CAMERA_MODE_WALK : CAMERA_MODE_FLY); g_controller.move_speed = g_walk_mode ? 8.0f : 40.0f; LOG_INFO("Camera mode: %s (speed %.1f)", g_walk_mode ? "walk" : "fly", g_controller.move_speed); } // 模式切换后重新定位相机高度 if (g_walk_mode) { float ground = get_terrain_height(g_camera.position.x, g_camera.position.z, NULL); g_camera.position.y = ground + g_controller.height_offset; g_camera.target.y = g_camera.position.y + sinf(g_controller.pitch); g_camera.dirty = 1; } } static void on_mouse_move(Engine* engine, int x, int y) { (void)engine; if (g_mouse_down[0]) { // 左键拖拽旋转 if (g_mouse_first) { g_last_mouse_x = x; g_last_mouse_y = y; g_mouse_first = 0; } else { float dx = (float)(x - g_last_mouse_x); float dy = (float)(y - g_last_mouse_y); camera_controller_look(&g_controller, dx, dy); g_last_mouse_x = x; g_last_mouse_y = y; } } else { g_mouse_first = 1; } } static void on_mouse_button(Engine* engine, int button, int pressed) { (void)engine; if (button >= 0 && button < 3) g_mouse_down[button] = pressed; } static void on_resize(Engine* engine, int width, int height) { Renderer* r = engine_get_renderer(engine); if (r && width > 0 && height > 0) { renderer_resize(r, width, height); g_camera.aspect = (float)width / (float)height; g_camera.dirty = 1; } } // ============================================================================ // 入口 // ============================================================================ int main(int argc, char* argv[]) { (void)argc; (void)argv; srand((unsigned int)time(NULL)); EngineConfig config = engine_default_config(); config.app_name = "Lity3D - Open World Demo"; config.vsync = 1; config.window_width = 1280; config.window_height = 720; Engine* engine = engine_create(); if (!engine_init(engine, &config)) { LOG_FATAL("Engine init failed"); return -1; } GraphicsDevice* dev = engine_get_graphics_device(engine); Renderer* r = engine_get_renderer(engine); g_assets = asset_manager_create(); g_shader_asset = asset_manager_load_shader(g_assets, dev->device, NULL, NULL); if (!g_shader_asset) { LOG_FATAL("Shader load failed"); return -1; } LOG_INFO("Shader loaded via AssetManager"); Shader* default_shader = (Shader*)asset_data(g_shader_asset); // 地形材质 g_terrain_mat = material_create(default_shader, NULL); g_terrain_mat->color = (Vec4){1.0f, 1.0f, 1.0f, 1.0f}; // 参照立方体 g_cube = mesh_create_cube(dev->device); g_cube_mat = material_create(default_shader, NULL); g_cube_mat->color = (Vec4){1.0f, 0.2f, 0.2f, 1.0f}; // 水面(不透明,作为低洼处背景) g_water = mesh_create_plane(dev->device, 2000.0f); g_water_mat = material_create(default_shader, NULL); g_water_mat->color = (Vec4){0.18f, 0.45f, 0.78f, 1.0f}; // 测试场景:仅保留一个红色立方体作为原点标记 g_test_scene = (Scene*)calloc(1, sizeof(Scene)); scene_init(g_test_scene); SceneNode* cube_node = scene_create_node(g_test_scene, "cube"); scene_set_parent(cube_node, g_test_scene->root); cube_node->position = (Vec3){0.0f, 0.0f, 0.0f}; MeshRenderer* cube_mr = mesh_renderer_create(MESH_CUBE, g_cube, g_cube_mat); scene_node_set_mesh_renderer(cube_node, cube_mr); // 地形配置 TerrainConfig tcfg = terrain_default_config(); tcfg.vertices_per_chunk = 48; tcfg.chunk_size = 64.0f; tcfg.height_scale = 30.0f; tcfg.uv_scale = 0.05f; g_terrain = terrain_manager_create(dev->device, &tcfg, g_terrain_mat); g_terrain->load_radius = 3; // 生成植被/岩石 generate_scatter(dev->device, default_shader); // 天空色与柔和环境光 renderer_set_clear_color(r, 0.53f, 0.81f, 0.98f, 1.0f); renderer_set_light(r, (Vec3){0.5f, -1.0f, 0.3f}, (Vec4){0.55f, 0.53f, 0.48f, 1.0f}, (Vec4){0.70f, 0.74f, 0.82f, 1.0f}); // 相机初始化:站在地形上,朝 +Z float aspect = (float)config.window_width / (float)config.window_height; g_camera = camera_perspective(1.0472f, aspect, 0.1f, 500.0f); float start_h = terrain_manager_get_height(g_terrain, 0.0f, 0.0f); g_camera.position = (Vec3){0.0f, start_h + 1.75f, 0.0f}; g_camera.target = (Vec3){0.0f, start_h + 1.75f, 5.0f}; g_camera.far_z = 500.0f; g_camera.dirty = 1; g_controller = camera_controller_create(&g_camera); g_controller.move_speed = 8.0f; g_controller.mouse_sensitivity = 0.004f; camera_controller_set_ground_callback(&g_controller, get_terrain_height, NULL); g_walk_mode = 1; camera_controller_set_mode(&g_controller, CAMERA_MODE_WALK); LOG_INFO("Starting in walk mode at height %.2f", start_h); renderer_set_view(r, camera_get_view(&g_camera)); renderer_set_projection(r, camera_get_projection(&g_camera)); engine_set_update_callback(engine, on_update); engine_set_render_callback(engine, on_render); engine_set_key_callback(engine, on_key); engine_set_mouse_move_callback(engine, on_mouse_move); engine_set_mouse_button_callback(engine, on_mouse_button); engine_set_resize_callback(engine, on_resize); LOG_INFO("Running open world demo..."); engine_run(engine); terrain_manager_destroy(g_terrain); if (g_test_scene) { scene_destroy_renderers(g_test_scene); scene_destroy(g_test_scene); free(g_test_scene); } if (g_cube) mesh_destroy(g_cube); asset_manager_destroy(g_assets); engine_shutdown(engine); return 0; }