载入中...
搜索中...
未找到
GreedyMesher.cpp
浏览该文件的文档.
2
3#include <cstring>
4
5namespace eve::voxel {
6
7uint8_t GreedyMesher::at(const uint8_t *v, int x, int y, int z) {
8 if (x < 0 || y < 0 || z < 0 || x >= kChunkSize || y >= kChunkSize || z >= kChunkSize) return 0;
9 return v[x + y * kChunkSize + z * kChunkSize * kChunkSize];
10}
11
12uint8_t GreedyMesher::neighborAt(const uint8_t *voxels, ChunkSampler sampler, void *userData,
13 int chunkX, int chunkY, int chunkZ, int nx, int ny, int nz) {
14 if (nx >= 0 && ny >= 0 && nz >= 0 && nx < kChunkSize && ny < kChunkSize && nz < kChunkSize)
15 return voxels[nx + ny * kChunkSize + nz * kChunkSize * kChunkSize];
16 if (sampler) return sampler(userData, chunkX, chunkY, chunkZ, nx, ny, nz);
17 return 0;
18}
19
20uint8_t GreedyMesher::outsideVoxel(const uint8_t *voxels, ChunkSampler sampler, void *userData,
21 int chunkX, int chunkY, int chunkZ, FaceDir dir, int slice,
22 int gu, int gv) {
23 int x = 0, y = 0, z = 0;
24 switch (dir) {
25 case FaceDir::PosX: x = slice + 1; y = gv; z = gu; break;
26 case FaceDir::NegX: x = slice - 1; y = gv; z = gu; break;
27 case FaceDir::PosY: x = gu; y = slice + 1; z = gv; break;
28 case FaceDir::NegY: x = gu; y = slice - 1; z = gv; break;
29 case FaceDir::PosZ: x = gu; y = gv; z = slice + 1; break;
30 case FaceDir::NegZ:
31 default: x = gu; y = gv; z = slice - 1; break;
32 }
33 return neighborAt(voxels, sampler, userData, chunkX, chunkY, chunkZ, x, y, z);
34}
35
36uint8_t GreedyMesher::vertexAO(const uint8_t *voxels, ChunkSampler sampler, void *userData,
37 int chunkX, int chunkY, int chunkZ, FaceDir dir, int slice, int gu,
38 int gv) {
39 const uint8_t e1 = outsideVoxel(voxels, sampler, userData, chunkX, chunkY, chunkZ, dir, slice,
40 gu - 1, gv);
41 const uint8_t e2 = outsideVoxel(voxels, sampler, userData, chunkX, chunkY, chunkZ, dir, slice,
42 gu, gv - 1);
43 const uint8_t d = outsideVoxel(voxels, sampler, userData, chunkX, chunkY, chunkZ, dir, slice,
44 gu - 1, gv - 1);
45 if (isSolid(e1) && isSolid(e2)) return 0;
46 return uint8_t(3 - (isSolid(e1) ? 1 : 0) - (isSolid(e2) ? 1 : 0) - (isSolid(d) ? 1 : 0));
47}
48
49uint32_t GreedyMesher::rectAOWord(const uint8_t *voxels, ChunkSampler sampler, void *userData,
50 int chunkX, int chunkY, int chunkZ, FaceDir dir, int slice,
51 int x, int y, int z, int w, int h) {
52 // Grid vertices in shader corner order (see decodePackedRect mirror).
53 int gu[4], gv[4];
54 switch (dir) {
55 case FaceDir::PosX:
56 gu[0] = z; gv[0] = y; gu[1] = z + w; gv[1] = y;
57 gu[2] = z + w; gv[2] = y + h; gu[3] = z; gv[3] = y + h;
58 break;
59 case FaceDir::NegX:
60 gu[0] = z + w; gv[0] = y; gu[1] = z; gv[1] = y;
61 gu[2] = z; gv[2] = y + h; gu[3] = z + w; gv[3] = y + h;
62 break;
63 case FaceDir::PosY:
64 gu[0] = x; gv[0] = z; gu[1] = x + w; gv[1] = z;
65 gu[2] = x + w; gv[2] = z + h; gu[3] = x; gv[3] = z + h;
66 break;
67 case FaceDir::NegY:
68 gu[0] = x; gv[0] = z + h; gu[1] = x + w; gv[1] = z + h;
69 gu[2] = x + w; gv[2] = z; gu[3] = x; gv[3] = z;
70 break;
71 case FaceDir::PosZ:
72 gu[0] = x + w; gv[0] = y; gu[1] = x; gv[1] = y;
73 gu[2] = x; gv[2] = y + h; gu[3] = x + w; gv[3] = y + h;
74 break;
75 case FaceDir::NegZ:
76 default:
77 gu[0] = x; gv[0] = y; gu[1] = x + w; gv[1] = y;
78 gu[2] = x + w; gv[2] = y + h; gu[3] = x; gv[3] = y + h;
79 break;
80 }
81 uint32_t word = 0;
82 for (int i = 0; i < 4; ++i) {
83 const uint32_t ao =
84 uint32_t(vertexAO(voxels, sampler, userData, chunkX, chunkY, chunkZ, dir, slice,
85 gu[i], gv[i]));
86 word |= ao << (2 * i);
87 }
88 return word;
89}
90
91void GreedyMesher::greedy2D(int mask[kChunkSize][kChunkSize], FaceDir dir, int slice,
92 std::vector<PackedRect> &out, std::vector<uint32_t> *aoOut,
93 const uint8_t *voxels, ChunkSampler sampler, void *userData,
94 int chunkX, int chunkY, int chunkZ) {
95 for (int v = 0; v < kChunkSize; ++v) {
96 for (int u = 0; u < kChunkSize;) {
97 const int tex = mask[v][u];
98 if (tex < 0) { // 空
99 ++u;
100 continue;
101 }
102
103 int w = 1;
104 while (u + w < kChunkSize && mask[v][u + w] == tex) ++w;
105
106 int h = 1;
107 bool grow = true;
108 while (v + h < kChunkSize && grow) {
109 for (int k = 0; k < w; ++k) {
110 if (mask[v + h][u + k] != tex) {
111 grow = false;
112 break;
113 }
114 }
115 if (grow) ++h;
116 }
117
118 for (int dv = 0; dv < h; ++dv)
119 for (int du = 0; du < w; ++du) mask[v + dv][u + du] = -1;
120
121 int x = 0, y = 0, z = 0;
122 int rw = w, rh = h;
123 switch (dir) {
124 case FaceDir::PosX:
125 case FaceDir::NegX:
126 x = slice;
127 y = v;
128 z = u;
129 rw = w; // along Z
130 rh = h; // along Y
131 break;
132 case FaceDir::PosY:
133 case FaceDir::NegY:
134 x = u;
135 y = slice;
136 z = v;
137 rw = w; // along X
138 rh = h; // along Z
139 break;
140 case FaceDir::PosZ:
141 case FaceDir::NegZ:
142 default:
143 x = u;
144 y = v;
145 z = slice;
146 rw = w; // along X
147 rh = h; // along Y
148 break;
149 }
150 out.push_back(PackedRect::pack(x, y, z, rw, rh, tex));
151 if (aoOut) {
152 aoOut->push_back(rectAOWord(voxels, sampler, userData, chunkX, chunkY, chunkZ,
153 dir, slice, x, y, z, rw, rh));
154 }
155 u += w;
156 }
157 }
158}
159
160void GreedyMesher::meshFace(const uint8_t *voxels, FaceDir dir, std::vector<PackedRect> &out,
161 const CubeTypeRegistry &types, ChunkSampler sampler,
162 void *samplerUserData, int chunkX, int chunkY, int chunkZ,
163 std::vector<uint32_t> *aoOut) {
164 int mask[kChunkSize][kChunkSize];
165
166 for (int s = 0; s < kChunkSize; ++s) {
167 for (int a = 0; a < kChunkSize; ++a)
168 for (int b = 0; b < kChunkSize; ++b) mask[a][b] = -1;
169
170 for (int a = 0; a < kChunkSize; ++a) {
171 for (int b = 0; b < kChunkSize; ++b) {
172 int x = 0, y = 0, z = 0;
173 int nx = 0, ny = 0, nz = 0;
174 int u = 0, v = 0;
175 switch (dir) {
176 case FaceDir::PosX:
177 x = s;
178 y = a;
179 z = b;
180 nx = s + 1;
181 ny = a;
182 nz = b;
183 u = b;
184 v = a;
185 break;
186 case FaceDir::NegX:
187 x = s;
188 y = a;
189 z = b;
190 nx = s - 1;
191 ny = a;
192 nz = b;
193 u = b;
194 v = a;
195 break;
196 case FaceDir::PosY:
197 x = b;
198 y = s;
199 z = a;
200 nx = b;
201 ny = s + 1;
202 nz = a;
203 u = b;
204 v = a;
205 break;
206 case FaceDir::NegY:
207 x = b;
208 y = s;
209 z = a;
210 nx = b;
211 ny = s - 1;
212 nz = a;
213 u = b;
214 v = a;
215 break;
216 case FaceDir::PosZ:
217 x = b;
218 y = a;
219 z = s;
220 nx = b;
221 ny = a;
222 nz = s + 1;
223 u = b;
224 v = a;
225 break;
226 case FaceDir::NegZ:
227 x = b;
228 y = a;
229 z = s;
230 nx = b;
231 ny = a;
232 nz = s - 1;
233 u = b;
234 v = a;
235 break;
236 default:
237 break;
238 }
239
240 const uint8_t id = at(voxels, x, y, z);
241 if (!isSolid(id)) continue;
242 if (isSolid(neighborAt(voxels, sampler, samplerUserData, chunkX, chunkY, chunkZ,
243 nx, ny, nz)))
244 continue;
245 mask[v][u] = int(resolveFaceTex(types, id, dir));
246 }
247 }
248
249 greedy2D(mask, dir, s, out, aoOut, voxels, sampler, samplerUserData, chunkX, chunkY,
250 chunkZ);
251 }
252}
253
254void GreedyMesher::meshChunk(const uint8_t *voxels, std::vector<PackedRect> outFaces[6],
255 const CubeTypeRegistry &types, ChunkSampler sampler,
256 void *samplerUserData, int chunkX, int chunkY, int chunkZ,
257 std::vector<uint32_t> aoOut[6]) {
258 for (int i = 0; i < faceDirCount(); ++i) {
259 outFaces[i].clear();
260 if (aoOut) aoOut[i].clear();
261 meshFace(voxels, FaceDir(i), outFaces[i], types, sampler, samplerUserData, chunkX, chunkY,
262 chunkZ, aoOut ? &aoOut[i] : nullptr);
263 }
264}
265
266} // namespace eve::voxel
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
float u
Definition Grass.cpp:234
int x
Definition Grass.cpp:135
int h
int w
uint32_t a
uint32_t b
int d
int v
V3 dir
Definition TreeMesh.cpp:121
uint32_t s
Definition Weather.cpp:28
static void meshFace(const uint8_t *voxels, FaceDir dir, std::vector< PackedRect > &out, const CubeTypeRegistry &types=CubeTypeRegistry::empty(), ChunkSampler sampler=nullptr, void *samplerUserData=nullptr, int chunkX=0, int chunkY=0, int chunkZ=0, std::vector< uint32_t > *aoOut=nullptr)
Mesh a single face direction into out (appended; caller may clear).
static void meshChunk(const uint8_t *voxels, std::vector< PackedRect > outFaces[6], const CubeTypeRegistry &types=CubeTypeRegistry::empty(), ChunkSampler sampler=nullptr, void *samplerUserData=nullptr, int chunkX=0, int chunkY=0, int chunkZ=0, std::vector< uint32_t > aoOut[6]=nullptr)
Mesh all six faces. Clears and fills outFaces[6].
方块类型定义:名字、各面图集纹理、方向性、组合声明。 方向性方块在注册时按 orientation 绕 Y 轴展开成多个"具体类型"变体, 每个变体持有旋转后的各面纹理;渲染端只消费纹理 id,不接触 ...
Definition Chunk.h:12
constexpr int kChunkSize
Chunk edge length in voxels (fixed).
Definition VoxelPack.h:8
uint8_t resolveFaceTex(const CubeTypeRegistry &types, uint8_t id, FaceDir dir)
求某个体素在某面方向上的实际图集纹理 id。 未注册的 id 退化为“所有面 = 原 id”(向后兼容:体素值即纹理 id)。
constexpr int faceDirCount()
Definition FaceDir.h:27
uint8_t(*)(void *userData, int chunkX, int chunkY, int chunkZ, int localX, int localY, int localZ) ChunkSampler
Optional out-of-chunk neighbor sampler for seam culling. Receives chunk coordinates plus the (possibl...
FaceDir
Six axis-aligned face directions. Each chunk keeps a separate instance buffer per direction so camera...
Definition FaceDir.h:17
static PackedRect pack(int x, int y, int z, int width, int height, int tex)
Definition VoxelPack.h:25