#include "terrain.h" #include #include #include // ============================================================================ // 简单的 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; }