fix: .gitignore + add 44 language JSON files
This commit is contained in:
1 parent
e8fd68e27b
commit
8de545dd2c
227 files changed
+22603
No files matched your search
@@ -0,0 +1,289 @@
|
||||
#include "terrain.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
// ============================================================================
|
||||
// 简单的 Value Noise + 叠加
|
||||
// ============================================================================
|
||||
|
||||
float hash_noise(int x, int z, int seed) {
|
||||
unsigned int n = (unsigned int)x * 374761393u
|
||||
+ (unsigned int)z * 668265263u
|
||||
+ (unsigned int)seed * 1013904223u;
|
||||
n = (n ^ (n >> 13)) * 1274126177u;
|
||||
n = n ^ (n >> 16);
|
||||
return (float)(n & 0x7fffffffu) / (float)0x7fffffffu;
|
||||
}
|
||||
|
||||
static float smooth_noise(float x, float z, int seed) {
|
||||
int ix = (int)floorf(x);
|
||||
int iz = (int)floorf(z);
|
||||
float fx = x - ix;
|
||||
float fz = z - iz;
|
||||
|
||||
float v00 = hash_noise(ix, iz, seed);
|
||||
float v10 = hash_noise(ix + 1, iz, seed);
|
||||
float v01 = hash_noise(ix, iz + 1, seed);
|
||||
float v11 = hash_noise(ix + 1, iz + 1, seed);
|
||||
|
||||
float u = fx * fx * (3.0f - 2.0f * fx);
|
||||
float v = fz * fz * (3.0f - 2.0f * fz);
|
||||
|
||||
float a = v00 + (v10 - v00) * u;
|
||||
float b = v01 + (v11 - v01) * u;
|
||||
return a + (b - a) * v;
|
||||
}
|
||||
|
||||
float fbm_noise(float x, float z, int seed, int octaves, float persistence) {
|
||||
float total = 0.0f;
|
||||
float amplitude = 1.0f;
|
||||
float frequency = 1.0f;
|
||||
float max_value = 0.0f;
|
||||
for (int i = 0; i < octaves; i++) {
|
||||
total += smooth_noise(x * frequency, z * frequency, seed + i * 57) * amplitude;
|
||||
max_value += amplitude;
|
||||
amplitude *= persistence;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
return total / max_value;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 高度图
|
||||
// ============================================================================
|
||||
|
||||
TerrainConfig terrain_default_config(void) {
|
||||
TerrainConfig cfg = {0};
|
||||
cfg.chunks_x = 4;
|
||||
cfg.chunks_z = 4;
|
||||
cfg.vertices_per_chunk = 32;
|
||||
cfg.chunk_size = 64.0f;
|
||||
cfg.height_scale = 20.0f;
|
||||
cfg.uv_scale = 0.1f;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
TerrainHeightmap terrain_heightmap_create(int size) {
|
||||
TerrainHeightmap hm = {0};
|
||||
if (size < 2) size = 2;
|
||||
if (size > TERRAIN_MAX_HEIGHTMAP_SIZE) size = TERRAIN_MAX_HEIGHTMAP_SIZE;
|
||||
hm.size = size;
|
||||
hm.data = (float*)calloc(size * size, sizeof(float));
|
||||
return hm;
|
||||
}
|
||||
|
||||
void terrain_heightmap_destroy(TerrainHeightmap* hm) {
|
||||
if (hm && hm->data) {
|
||||
free(hm->data);
|
||||
hm->data = NULL;
|
||||
hm->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void terrain_heightmap_fill_noise(TerrainHeightmap* hm, int seed, float scale, float amplitude) {
|
||||
if (!hm || !hm->data) return;
|
||||
int s = hm->size;
|
||||
for (int z = 0; z < s; z++) {
|
||||
for (int x = 0; x < s; x++) {
|
||||
float nx = (float)x / (float)(s - 1) * scale;
|
||||
float nz = (float)z / (float)(s - 1) * scale;
|
||||
float h = fbm_noise(nx, nz, seed, 6, 0.5f);
|
||||
hm->data[z * s + x] = h * amplitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float terrain_heightmap_sample(const TerrainHeightmap* hm, float x, float z) {
|
||||
if (!hm || !hm->data || hm->size < 2) return 0.0f;
|
||||
int s = hm->size;
|
||||
float fx = x * (float)(s - 1);
|
||||
float fz = z * (float)(s - 1);
|
||||
int ix = (int)floorf(fx);
|
||||
int iz = (int)floorf(fz);
|
||||
if (ix < 0) ix = 0;
|
||||
if (iz < 0) iz = 0;
|
||||
if (ix >= s - 1) ix = s - 2;
|
||||
if (iz >= s - 1) iz = s - 2;
|
||||
float tx = fx - ix;
|
||||
float tz = fz - iz;
|
||||
|
||||
float h00 = hm->data[iz * s + ix];
|
||||
float h10 = hm->data[iz * s + ix + 1];
|
||||
float h01 = hm->data[(iz + 1) * s + ix];
|
||||
float h11 = hm->data[(iz + 1) * s + ix + 1];
|
||||
|
||||
float a = h00 + (h10 - h00) * tx;
|
||||
float b = h01 + (h11 - h01) * tx;
|
||||
return a + (b - a) * tz;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 地形网格
|
||||
// ============================================================================
|
||||
|
||||
static Mesh* create_mesh_from_terrain(ID3D11Device* dev, const TerrainHeightmap* hm,
|
||||
float size, float height_scale, float uv_scale) {
|
||||
if (!hm || !hm->data || hm->size < 2) return NULL;
|
||||
int s = hm->size;
|
||||
int vc = s * s;
|
||||
int ic = (s - 1) * (s - 1) * 6;
|
||||
|
||||
Vertex* verts = (Vertex*)calloc(vc, sizeof(Vertex));
|
||||
uint32_t* idx = (uint32_t*)malloc(ic * sizeof(uint32_t));
|
||||
|
||||
float half = size * 0.5f;
|
||||
float step = size / (float)(s - 1);
|
||||
|
||||
for (int z = 0; z < s; z++) {
|
||||
for (int x = 0; x < s; x++) {
|
||||
int i = z * s + x;
|
||||
float px = (float)x * step - half;
|
||||
float pz = (float)z * step - half;
|
||||
float py = hm->data[i] * height_scale;
|
||||
verts[i].pos[0] = px;
|
||||
verts[i].pos[1] = py;
|
||||
verts[i].pos[2] = pz;
|
||||
verts[i].uv[0] = (float)x * uv_scale;
|
||||
verts[i].uv[1] = (float)z * uv_scale;
|
||||
verts[i].color[0] = 1.0f;
|
||||
verts[i].color[1] = 1.0f;
|
||||
verts[i].color[2] = 1.0f;
|
||||
verts[i].color[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算法线
|
||||
memset(verts, 0, vc * sizeof(Vertex)); // 重新初始化法线
|
||||
for (int z = 0; z < s; z++) {
|
||||
for (int x = 0; x < s; x++) {
|
||||
int i = z * s + x;
|
||||
float px = (float)x * step - half;
|
||||
float pz = (float)z * step - half;
|
||||
float py = hm->data[i] * height_scale;
|
||||
verts[i].pos[0] = px;
|
||||
verts[i].pos[1] = py;
|
||||
verts[i].pos[2] = pz;
|
||||
verts[i].uv[0] = (float)x * uv_scale;
|
||||
verts[i].uv[1] = (float)z * uv_scale;
|
||||
|
||||
// 估算法线
|
||||
float hl = (x > 0) ? hm->data[z * s + x - 1] * height_scale : py;
|
||||
float hr = (x < s - 1) ? hm->data[z * s + x + 1] * height_scale : py;
|
||||
float hd = (z > 0) ? hm->data[(z - 1) * s + x] * height_scale : py;
|
||||
float hu = (z < s - 1) ? hm->data[(z + 1) * s + x] * height_scale : py;
|
||||
verts[i].norm[0] = (hl - hr);
|
||||
verts[i].norm[1] = 2.0f * step;
|
||||
verts[i].norm[2] = (hd - hu);
|
||||
|
||||
float nx = verts[i].norm[0];
|
||||
float ny = verts[i].norm[1];
|
||||
float nz = verts[i].norm[2];
|
||||
float len = sqrtf(nx * nx + ny * ny + nz * nz);
|
||||
if (len > 0.0001f) {
|
||||
verts[i].norm[0] = nx / len;
|
||||
verts[i].norm[1] = ny / len;
|
||||
verts[i].norm[2] = nz / len;
|
||||
} else {
|
||||
verts[i].norm[0] = 0;
|
||||
verts[i].norm[1] = 1;
|
||||
verts[i].norm[2] = 0;
|
||||
}
|
||||
|
||||
// 根据高度和坡度着色
|
||||
float h = hm->data[i];
|
||||
float slope = (ny > 0.0001f) ? sqrtf(nx * nx + nz * nz) / ny : 0.0f;
|
||||
if (h > 0.72f) {
|
||||
verts[i].color[0] = 0.92f;
|
||||
verts[i].color[1] = 0.92f;
|
||||
verts[i].color[2] = 0.95f;
|
||||
} else if (h > 0.55f || slope > 0.42f) {
|
||||
verts[i].color[0] = 0.62f;
|
||||
verts[i].color[1] = 0.60f;
|
||||
verts[i].color[2] = 0.56f;
|
||||
} else if (h < 0.18f) {
|
||||
verts[i].color[0] = 0.82f;
|
||||
verts[i].color[1] = 0.76f;
|
||||
verts[i].color[2] = 0.50f;
|
||||
} else {
|
||||
verts[i].color[0] = 0.40f;
|
||||
verts[i].color[1] = 0.68f;
|
||||
verts[i].color[2] = 0.36f;
|
||||
}
|
||||
verts[i].color[3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
int ii = 0;
|
||||
for (int z = 0; z < s - 1; z++) {
|
||||
for (int x = 0; x < s - 1; x++) {
|
||||
uint32_t a = z * s + x;
|
||||
uint32_t b = z * s + x + 1;
|
||||
uint32_t c = (z + 1) * s + x;
|
||||
uint32_t d = (z + 1) * s + x + 1;
|
||||
idx[ii++] = a;
|
||||
idx[ii++] = c;
|
||||
idx[ii++] = b;
|
||||
idx[ii++] = b;
|
||||
idx[ii++] = c;
|
||||
idx[ii++] = d;
|
||||
}
|
||||
}
|
||||
|
||||
Mesh* m = (Mesh*)calloc(1, sizeof(Mesh));
|
||||
m->vcount = vc;
|
||||
m->icount = ic;
|
||||
m->stride = sizeof(Vertex);
|
||||
|
||||
D3D11_BUFFER_DESC bd = {0};
|
||||
D3D11_SUBRESOURCE_DATA sd = {0};
|
||||
bd.ByteWidth = sizeof(Vertex) * vc;
|
||||
bd.Usage = D3D11_USAGE_DEFAULT;
|
||||
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
sd.pSysMem = verts;
|
||||
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->vb);
|
||||
|
||||
bd.ByteWidth = sizeof(uint32_t) * ic;
|
||||
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||
sd.pSysMem = idx;
|
||||
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->ib);
|
||||
|
||||
free(verts);
|
||||
free(idx);
|
||||
return m;
|
||||
}
|
||||
|
||||
Mesh* terrain_mesh_create(ID3D11Device* dev, const TerrainHeightmap* hm, float size, float height_scale, float uv_scale) {
|
||||
return create_mesh_from_terrain(dev, hm, size, height_scale, uv_scale);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 地形对象
|
||||
// ============================================================================
|
||||
|
||||
Terrain* terrain_create(ID3D11Device* dev, const TerrainConfig* config) {
|
||||
if (!dev || !config) return NULL;
|
||||
Terrain* t = (Terrain*)calloc(1, sizeof(Terrain));
|
||||
t->config = *config;
|
||||
t->device = dev;
|
||||
t->heightmap = terrain_heightmap_create(config->vertices_per_chunk);
|
||||
terrain_heightmap_fill_noise(&t->heightmap, 12345, 4.0f, 1.0f);
|
||||
t->mesh = terrain_mesh_create(dev, &t->heightmap, config->chunk_size, config->height_scale, config->uv_scale);
|
||||
return t;
|
||||
}
|
||||
|
||||
void terrain_destroy(Terrain* terrain) {
|
||||
if (!terrain) return;
|
||||
terrain_heightmap_destroy(&terrain->heightmap);
|
||||
if (terrain->mesh) mesh_destroy(terrain->mesh);
|
||||
free(terrain);
|
||||
}
|
||||
|
||||
float terrain_get_height(const Terrain* terrain, float world_x, float world_z) {
|
||||
if (!terrain) return 0.0f;
|
||||
float half = terrain->config.chunk_size * 0.5f;
|
||||
float local_x = (world_x + half) / terrain->config.chunk_size;
|
||||
float local_z = (world_z + half) / terrain->config.chunk_size;
|
||||
float h = terrain_heightmap_sample(&terrain->heightmap, local_x, local_z);
|
||||
return h * terrain->config.height_scale;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// terrain.h - 程序化地形
|
||||
// ============================================================================
|
||||
|
||||
#include <d3d11.h>
|
||||
#include "../Rendering/mesh.h"
|
||||
|
||||
#define TERRAIN_MAX_HEIGHTMAP_SIZE 256
|
||||
|
||||
typedef struct {
|
||||
float* data;
|
||||
int size;
|
||||
} TerrainHeightmap;
|
||||
|
||||
typedef struct {
|
||||
int chunks_x;
|
||||
int chunks_z;
|
||||
int vertices_per_chunk;
|
||||
float chunk_size;
|
||||
float height_scale;
|
||||
float uv_scale;
|
||||
} TerrainConfig;
|
||||
|
||||
typedef struct {
|
||||
TerrainConfig config;
|
||||
TerrainHeightmap heightmap;
|
||||
Mesh* mesh;
|
||||
ID3D11Device* device;
|
||||
} Terrain;
|
||||
|
||||
// 默认配置
|
||||
TerrainConfig terrain_default_config(void);
|
||||
|
||||
// 高度图
|
||||
TerrainHeightmap terrain_heightmap_create(int size);
|
||||
void terrain_heightmap_destroy(TerrainHeightmap* hm);
|
||||
void terrain_heightmap_fill_noise(TerrainHeightmap* hm, int seed, float scale, float amplitude);
|
||||
float terrain_heightmap_sample(const TerrainHeightmap* hm, float x, float z);
|
||||
|
||||
// 地形对象
|
||||
Terrain* terrain_create(ID3D11Device* dev, const TerrainConfig* config);
|
||||
void terrain_destroy(Terrain* terrain);
|
||||
|
||||
// 从世界坐标采样高度 (用于相机/角色贴地)
|
||||
float terrain_get_height(const Terrain* terrain, float world_x, float world_z);
|
||||
|
||||
// 网格生成
|
||||
Mesh* terrain_mesh_create(ID3D11Device* dev, const TerrainHeightmap* hm, float size, float height_scale, float uv_scale);
|
||||
|
||||
// 噪声工具(供分块管理器复用)
|
||||
float hash_noise(int x, int z, int seed);
|
||||
float fbm_noise(float x, float z, int seed, int octaves, float persistence);
|
||||
@@ -0,0 +1,199 @@
|
||||
#include "terrain_manager.h"
|
||||
#include "../Rendering/renderer.h"
|
||||
#include "../Rendering/frustum.h"
|
||||
#include "../Core/logger.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
// ============================================================================
|
||||
// 噪声:分块需要世界一致的高度,这里复用 terrain.c 的 hash_noise/fbm_noise
|
||||
// ============================================================================
|
||||
|
||||
static float sample_world_height(int seed, float world_x, float world_z, float scale) {
|
||||
return fbm_noise(world_x * scale, world_z * scale, seed, 6, 0.5f);
|
||||
}
|
||||
|
||||
static void fill_heightmap_for_chunk(TerrainHeightmap* hm, int seed, int cx, int cz,
|
||||
float chunk_size, float scale) {
|
||||
if (!hm || !hm->data) return;
|
||||
int s = hm->size;
|
||||
float half = chunk_size * 0.5f;
|
||||
float step = chunk_size / (float)(s - 1);
|
||||
float origin_x = (float)cx * chunk_size - half;
|
||||
float origin_z = (float)cz * chunk_size - half;
|
||||
|
||||
for (int z = 0; z < s; z++) {
|
||||
for (int x = 0; x < s; x++) {
|
||||
float wx = origin_x + (float)x * step;
|
||||
float wz = origin_z + (float)z * step;
|
||||
hm->data[z * s + x] = sample_world_height(seed, wx, wz, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void make_chunk_world_matrix(TerrainChunk* chunk, Mat4* out) {
|
||||
mat4_identity(out);
|
||||
out->m[3][0] = (float)chunk->cx * chunk->terrain->config.chunk_size;
|
||||
out->m[3][1] = 0.0f;
|
||||
out->m[3][2] = (float)chunk->cz * chunk->terrain->config.chunk_size;
|
||||
}
|
||||
|
||||
TerrainManager* terrain_manager_create(ID3D11Device* dev, const TerrainConfig* config, Material* base_material) {
|
||||
if (!dev || !config) return NULL;
|
||||
TerrainManager* tm = (TerrainManager*)calloc(1, sizeof(TerrainManager));
|
||||
tm->config = *config;
|
||||
tm->device = dev;
|
||||
tm->base_material = base_material;
|
||||
tm->load_radius = 3;
|
||||
tm->seed = 12345;
|
||||
tm->chunk_count = 0;
|
||||
return tm;
|
||||
}
|
||||
|
||||
void terrain_manager_destroy(TerrainManager* tm) {
|
||||
if (!tm) return;
|
||||
for (int i = 0; i < tm->chunk_count; i++) {
|
||||
if (tm->chunks[i].terrain) terrain_destroy(tm->chunks[i].terrain);
|
||||
}
|
||||
free(tm);
|
||||
}
|
||||
|
||||
static int find_chunk_index(TerrainManager* tm, int cx, int cz) {
|
||||
for (int i = 0; i < tm->chunk_count; i++) {
|
||||
if (tm->chunks[i].loaded && tm->chunks[i].cx == cx && tm->chunks[i].cz == cz)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static TerrainChunk* alloc_chunk_slot(TerrainManager* tm) {
|
||||
if (tm->chunk_count >= TERRAIN_MAX_CHUNKS) return NULL;
|
||||
return &tm->chunks[tm->chunk_count++];
|
||||
}
|
||||
|
||||
static TerrainChunk* create_chunk(TerrainManager* tm, int cx, int cz) {
|
||||
TerrainChunk* chunk = alloc_chunk_slot(tm);
|
||||
if (!chunk) {
|
||||
LOG_ERROR("Terrain: failed to alloc chunk slot");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chunk->cx = cx;
|
||||
chunk->cz = cz;
|
||||
chunk->terrain = (Terrain*)calloc(1, sizeof(Terrain));
|
||||
chunk->terrain->config = tm->config;
|
||||
chunk->terrain->device = tm->device;
|
||||
chunk->terrain->heightmap = terrain_heightmap_create(tm->config.vertices_per_chunk);
|
||||
fill_heightmap_for_chunk(&chunk->terrain->heightmap, tm->seed, cx, cz,
|
||||
tm->config.chunk_size, 0.015f);
|
||||
chunk->terrain->mesh = terrain_mesh_create(tm->device, &chunk->terrain->heightmap,
|
||||
tm->config.chunk_size,
|
||||
tm->config.height_scale,
|
||||
tm->config.uv_scale);
|
||||
chunk->material = tm->base_material;
|
||||
chunk->loaded = 1;
|
||||
if (!chunk->terrain->mesh) {
|
||||
LOG_ERROR("Terrain: chunk mesh create failed at (%d, %d)", cx, cz);
|
||||
} else {
|
||||
LOG_INFO("Terrain: created chunk (%d, %d) mesh v=%u i=%u", cx, cz,
|
||||
chunk->terrain->mesh->vcount, chunk->terrain->mesh->icount);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void terrain_manager_update(TerrainManager* tm, Vec3 observer_pos) {
|
||||
if (!tm) return;
|
||||
float inv_size = 1.0f / tm->config.chunk_size;
|
||||
int ox = (int)floorf(observer_pos.x * inv_size + 0.5f);
|
||||
int oz = (int)floorf(observer_pos.z * inv_size + 0.5f);
|
||||
static int logged = 0;
|
||||
if (!logged) {
|
||||
LOG_INFO("Terrain update: observer=(%.1f,%.1f,%.1f) center_chunk=(%d,%d)",
|
||||
observer_pos.x, observer_pos.y, observer_pos.z, ox, oz);
|
||||
logged = 1;
|
||||
}
|
||||
|
||||
// 标记需要保留
|
||||
int keep[TERRAIN_MAX_CHUNKS] = {0};
|
||||
|
||||
// 加载需要的分块
|
||||
int r = tm->load_radius;
|
||||
for (int z = oz - r; z <= oz + r; z++) {
|
||||
for (int x = ox - r; x <= ox + r; x++) {
|
||||
int idx = find_chunk_index(tm, x, z);
|
||||
if (idx < 0) {
|
||||
if (create_chunk(tm, x, z)) {
|
||||
keep[tm->chunk_count - 1] = 1;
|
||||
}
|
||||
} else {
|
||||
keep[idx] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 卸载不需要的分块
|
||||
int write = 0;
|
||||
for (int i = 0; i < tm->chunk_count; i++) {
|
||||
if (keep[i]) {
|
||||
tm->chunks[write] = tm->chunks[i];
|
||||
write++;
|
||||
} else {
|
||||
terrain_destroy(tm->chunks[i].terrain);
|
||||
}
|
||||
}
|
||||
tm->chunk_count = write;
|
||||
}
|
||||
|
||||
void terrain_manager_render(TerrainManager* tm, Renderer* renderer) {
|
||||
if (!tm || !renderer) return;
|
||||
static int logged = 0;
|
||||
if (!logged) {
|
||||
LOG_INFO("Terrain render: chunk_count=%d", tm->chunk_count);
|
||||
logged = 1;
|
||||
}
|
||||
|
||||
Mat4 vp;
|
||||
renderer_get_view_projection(renderer, &vp);
|
||||
Vec4 planes[6];
|
||||
frustum_from_matrix(&vp, planes);
|
||||
|
||||
float half = tm->config.chunk_size * 0.5f;
|
||||
float radius = sqrtf(half * half * 2.0f +
|
||||
(tm->config.height_scale * 0.5f) *
|
||||
(tm->config.height_scale * 0.5f));
|
||||
float cy = tm->config.height_scale * 0.5f;
|
||||
|
||||
int rendered = 0;
|
||||
for (int i = 0; i < tm->chunk_count; i++) {
|
||||
TerrainChunk* c = &tm->chunks[i];
|
||||
if (!c->loaded || !c->terrain || !c->terrain->mesh) continue;
|
||||
|
||||
Vec3 center = { (float)c->cx * tm->config.chunk_size, cy, (float)c->cz * tm->config.chunk_size };
|
||||
if (!frustum_intersects_sphere(planes, center, radius)) continue;
|
||||
|
||||
Mat4 world;
|
||||
make_chunk_world_matrix(c, &world);
|
||||
renderer_draw_mesh_with_material(renderer, c->terrain->mesh, c->material, &world);
|
||||
rendered++;
|
||||
}
|
||||
LOG_INFO("Terrain render: visible_chunks=%d/%d", rendered, tm->chunk_count);
|
||||
}
|
||||
|
||||
float terrain_manager_get_height(TerrainManager* tm, float world_x, float world_z) {
|
||||
if (!tm) return 0.0f;
|
||||
float inv_size = 1.0f / tm->config.chunk_size;
|
||||
int cx = (int)floorf(world_x * inv_size + 0.5f);
|
||||
int cz = (int)floorf(world_z * inv_size + 0.5f);
|
||||
|
||||
// 先在已加载分块中查找
|
||||
int idx = find_chunk_index(tm, cx, cz);
|
||||
if (idx >= 0 && tm->chunks[idx].terrain) {
|
||||
Terrain* t = tm->chunks[idx].terrain;
|
||||
float local_x = world_x - (float)cx * tm->config.chunk_size;
|
||||
float local_z = world_z - (float)cz * tm->config.chunk_size;
|
||||
return terrain_get_height(t, local_x, local_z);
|
||||
}
|
||||
|
||||
// 回退到直接采样噪声
|
||||
return sample_world_height(tm->seed, world_x, world_z, 0.015f) * tm->config.height_scale;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// terrain_manager.h - 地形分块流式加载
|
||||
// ============================================================================
|
||||
|
||||
#include "terrain.h"
|
||||
#include "../Rendering/material.h"
|
||||
#include "../Rendering/renderer.h"
|
||||
#include "../ASM/math_c_fallback.h"
|
||||
|
||||
#define TERRAIN_MAX_CHUNKS 64
|
||||
|
||||
typedef struct {
|
||||
int cx;
|
||||
int cz;
|
||||
Terrain* terrain;
|
||||
Material* material;
|
||||
int loaded;
|
||||
} TerrainChunk;
|
||||
|
||||
typedef struct {
|
||||
TerrainConfig config;
|
||||
TerrainChunk chunks[TERRAIN_MAX_CHUNKS];
|
||||
int chunk_count;
|
||||
int load_radius;
|
||||
int seed;
|
||||
ID3D11Device* device;
|
||||
Material* base_material;
|
||||
} TerrainManager;
|
||||
|
||||
TerrainManager* terrain_manager_create(ID3D11Device* dev, const TerrainConfig* config, Material* base_material);
|
||||
void terrain_manager_destroy(TerrainManager* tm);
|
||||
|
||||
// 根据观察者位置更新分块
|
||||
void terrain_manager_update(TerrainManager* tm, Vec3 observer_pos);
|
||||
|
||||
// 渲染所有已加载分块
|
||||
void terrain_manager_render(TerrainManager* tm, Renderer* renderer);
|
||||
|
||||
// 从世界坐标获取地形高度(在所有已加载分块中采样)
|
||||
float terrain_manager_get_height(TerrainManager* tm, float world_x, float world_z);
|
||||
Reference in new issue
Block a user