载入中...
搜索中...
未找到
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 for (int v = 0; v < kChunkSize; ++v) {
94 for (int u = 0; u < kChunkSize;) {
95 const int key = mask[v][u];
96 if (key < 0) { // 空
97 ++u;
98 continue;
99 }
100
101 const int tex = key & 0xFF;
102 const uint32_t cellAO = uint32_t(key >> 8) & 0xFFu;
103 const uint32_t ao0 = cellAO & 3u;
104 const bool uniformAO = ((cellAO >> 2) & 3u) == ao0 &&
105 ((cellAO >> 4) & 3u) == ao0 &&
106 ((cellAO >> 6) & 3u) == ao0;
107
108 int w = 1;
109 // One quad can reproduce per-voxel AO exactly only when AO is uniform
110 // across the merged region. Non-uniform cells stay as individual quads;
111 // otherwise their four corner values would be interpolated over an
112 // arbitrarily large greedy rectangle and create visible light/dark blocks.
113 while (uniformAO && u + w < kChunkSize && mask[v][u + w] == key) ++w;
114
115 int h = 1;
116 bool grow = true;
117 while (v + h < kChunkSize && grow) {
118 for (int k = 0; k < w; ++k) {
119 if (!uniformAO || mask[v + h][u + k] != key) {
120 grow = false;
121 break;
122 }
123 }
124 if (grow) ++h;
125 }
126
127 for (int dv = 0; dv < h; ++dv)
128 for (int du = 0; du < w; ++du) mask[v + dv][u + du] = -1;
129
130 int x = 0, y = 0, z = 0;
131 int rw = w, rh = h;
132 switch (dir) {
133 case FaceDir::PosX:
134 case FaceDir::NegX:
135 x = slice;
136 y = v;
137 z = u;
138 rw = w; // along Z
139 rh = h; // along Y
140 break;
141 case FaceDir::PosY:
142 case FaceDir::NegY:
143 x = u;
144 y = slice;
145 z = v;
146 rw = w; // along X
147 rh = h; // along Z
148 break;
149 case FaceDir::PosZ:
150 case FaceDir::NegZ:
151 default:
152 x = u;
153 y = v;
154 z = slice;
155 rw = w; // along X
156 rh = h; // along Y
157 break;
158 }
159 out.push_back(PackedRect::pack(x, y, z, rw, rh, tex));
160 if (aoOut) aoOut->push_back(cellAO);
161 u += w;
162 }
163 }
164}
165
166void GreedyMesher::meshFace(const uint8_t *voxels, FaceDir dir, std::vector<PackedRect> &out,
168 void *samplerUserData, int chunkX, int chunkY, int chunkZ,
169 std::vector<uint32_t> *aoOut) {
171
172 for (int s = 0; s < kChunkSize; ++s) {
173 for (int a = 0; a < kChunkSize; ++a)
174 for (int b = 0; b < kChunkSize; ++b) mask[a][b] = -1;
175
176 for (int a = 0; a < kChunkSize; ++a) {
177 for (int b = 0; b < kChunkSize; ++b) {
178 int x = 0, y = 0, z = 0;
179 int nx = 0, ny = 0, nz = 0;
180 int u = 0, v = 0;
181 switch (dir) {
182 case FaceDir::PosX:
183 x = s;
184 y = a;
185 z = b;
186 nx = s + 1;
187 ny = a;
188 nz = b;
189 u = b;
190 v = a;
191 break;
192 case FaceDir::NegX:
193 x = s;
194 y = a;
195 z = b;
196 nx = s - 1;
197 ny = a;
198 nz = b;
199 u = b;
200 v = a;
201 break;
202 case FaceDir::PosY:
203 x = b;
204 y = s;
205 z = a;
206 nx = b;
207 ny = s + 1;
208 nz = a;
209 u = b;
210 v = a;
211 break;
212 case FaceDir::NegY:
213 x = b;
214 y = s;
215 z = a;
216 nx = b;
217 ny = s - 1;
218 nz = a;
219 u = b;
220 v = a;
221 break;
222 case FaceDir::PosZ:
223 x = b;
224 y = a;
225 z = s;
226 nx = b;
227 ny = a;
228 nz = s + 1;
229 u = b;
230 v = a;
231 break;
232 case FaceDir::NegZ:
233 x = b;
234 y = a;
235 z = s;
236 nx = b;
237 ny = a;
238 nz = s - 1;
239 u = b;
240 v = a;
241 break;
242 default:
243 break;
244 }
245
246 const uint8_t id = at(voxels, x, y, z);
247 if (!isSolid(id)) continue;
248 if (isSolid(neighborAt(voxels, sampler, samplerUserData, chunkX, chunkY, chunkZ,
249 nx, ny, nz)))
250 continue;
251 const int tex = int(resolveFaceTex(types, id, dir));
252 const uint32_t ao = rectAOWord(voxels, sampler, samplerUserData, chunkX, chunkY,
253 chunkZ, dir, s, x, y, z, 1, 1);
254 mask[v][u] = tex | int(ao << 8);
255 }
256 }
257
258 greedy2D(mask, dir, s, out, aoOut);
259 }
260}
261
262void GreedyMesher::meshChunk(const uint8_t *voxels, std::vector<PackedRect> outFaces[6],
264 void *samplerUserData, int chunkX, int chunkY, int chunkZ,
265 std::vector<uint32_t> aoOut[6]) {
266 for (int i = 0; i < faceDirCount(); ++i) {
267 outFaces[i].clear();
268 if (aoOut) aoOut[i].clear();
269 meshFace(voxels, FaceDir(i), outFaces[i], types, sampler, samplerUserData, chunkX, chunkY,
270 chunkZ, aoOut ? &aoOut[i] : nullptr);
271 }
272}
273
274} // namespace eve::voxel
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
int mask
float nx
float nz
float ny
std::uint32_t key
vk::UniqueSampler sampler
float u
Definition Grass.cpp:233
float v
int h
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float d
V3 dir
Definition TreeMesh.cpp:150
std::size_t at
EVENGINE_API_DOMAINS public API.
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].
std::string word
方块类型定义:名字、各面图集纹理、方向性、组合声明。 方向性方块在注册时按 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()
Face dir count.
Definition FaceDir.h:28
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)
Pack.
Definition VoxelPack.h:26