Files
lity3d/Demo/open_world_demo.c
T

385 lines
13 KiB
C

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