#include "terrain_mesh.h" #include "m2_coord.h" #include #include namespace fmt { namespace { constexpr int CS = m2coord::CELLSCALE; // 200 cm constexpr double CS_M = double(CS) * m2coord::CM_TO_M; // 2.0 m } // namespace double terrain_height_at(const HeightMap& hm, double lx_cm, double ly_cm, double hs) { // 照 CTerrain::GetHeight(本地坐标版,调用方已把全局->本地 + y 取绝对值) if (lx_cm < 0 || ly_cm < 0 || lx_cm > double(m2coord::TERRAIN_SIZE) * CS || ly_cm > double(m2coord::TERRAIN_SIZE) * CS) return 0.0; long xi = (long)lx_cm, yi = (long)ly_cm; long xdist = xi % CS, ydist = yi % CS; long x = xi / CS, y = yi / CS; const double oo = 1.0 / double(CS); double h1 = hm.at((int)x, (int)y) * hs; // TL double h2 = hm.at((int)x + 1, (int)y + 1) * hs; // BR if (xdist <= ydist) { // 左三角:TL, BR, BL double h3 = hm.at((int)x, (int)y + 1) * hs; double xslope = (h2 - h3) * oo; double yslope = (h3 - h1) * oo; return h1 + (xdist * xslope + ydist * yslope); } // 右三角:TL, BR, TR double h3 = hm.at((int)x + 1, (int)y) * hs; double xslope = (h3 - h1) * oo; double yslope = (h2 - h3) * oo; return h1 + (xdist * xslope + ydist * yslope); } void build_terrain_mesh(const HeightMap& hm, int tile_x, int tile_y, double hs, TerrainMesh& out, bool ccw_from_above) { out = TerrainMesh{}; const int N = TerrainMesh::VERTS_XY; // 129 out.positions.resize(size_t(N) * N * 3); out.normals.resize(size_t(N) * N * 3); out.uvs.resize(size_t(N) * N * 2); out.indices.reserve(size_t(TerrainMesh::QUADS_XY) * TerrainMesh::QUADS_XY * 6); // Godot 空间原点:见 area_data.h 说明 —— 区块 y 取负,position_to_godot 再翻正。 const double X0 = double(tile_x) * m2coord::CHUNK_CM * m2coord::CM_TO_M; const double Z0 = double(tile_y) * m2coord::CHUNK_CM * m2coord::CM_TO_M; auto height_m = [&](int i, int j) -> double { return hm.at(i, j) * hs * m2coord::CM_TO_M; }; double miny = 1e30, maxy = -1e30; for (int j = 0; j < N; ++j) { for (int i = 0; i < N; ++i) { const double gy = height_m(i, j); const size_t o = (size_t(j) * N + i) * 3; out.positions[o + 0] = float(X0 + i * CS_M); out.positions[o + 1] = float(gy); out.positions[o + 2] = float(Z0 + j * CS_M); const size_t uo = (size_t(j) * N + i) * 2; out.uvs[uo + 0] = float(i) / float(TerrainMesh::QUADS_XY); out.uvs[uo + 1] = float(j) / float(TerrainMesh::QUADS_XY); miny = std::min(miny, gy); maxy = std::max(maxy, gy); // 中心差分(用边界样本,跨区块无缝);y=f(x,z) 的法线 = (-df/dx, 1, -df/dz) const double dh_di = (height_m(i + 1, j) - height_m(i - 1, j)) / (2.0 * CS_M); const double dh_dj = (height_m(i, j + 1) - height_m(i, j - 1)) / (2.0 * CS_M); double nx = -dh_di, ny = 1.0, nz = -dh_dj; const double len = std::sqrt(nx * nx + ny * ny + nz * nz); out.normals[o + 0] = float(nx / len); out.normals[o + 1] = float(ny / len); out.normals[o + 2] = float(nz / len); } } out.min_y = float(miny); out.max_y = float(maxy); auto vid = [N](int i, int j) { return int32_t(j * N + i); }; for (int j = 0; j < TerrainMesh::QUADS_XY; ++j) { for (int i = 0; i < TerrainMesh::QUADS_XY; ++i) { int32_t TL = vid(i, j), TR = vid(i + 1, j); int32_t BL = vid(i, j + 1), BR = vid(i + 1, j + 1); // 对角线 TL-BR(= GetHeight 的 h1/h2) if (ccw_from_above) { out.indices.insert(out.indices.end(), {TL, BR, BL}); out.indices.insert(out.indices.end(), {TL, TR, BR}); } else { out.indices.insert(out.indices.end(), {TL, BL, BR}); out.indices.insert(out.indices.end(), {TL, BR, TR}); } } } } } // namespace fmt