OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
packedmesh.cpp
Go to the documentation of this file.
1#include "packedmesh.h"
2
3#include <Tempest/Application>
4#include <Tempest/Log>
5#include <cassert>
6#include <fstream>
7#include <algorithm>
8
10#include "gothic.h"
11
12using namespace Tempest;
13
14static uint64_t mkUInt64(uint32_t a, uint32_t b) {
15 return (uint64_t(a)<<32) | uint64_t(b);
16 };
17
18static bool isVisuallySame(const zenkit::Material& a, const zenkit::Material& b) {
19 return
20 // a.name == b.name && // mat name
21 a.group == b.group &&
22 a.color == b.color &&
23 a.smooth_angle == b.smooth_angle &&
24 a.texture == b.texture &&
25 a.texture_scale == b.texture_scale &&
26 a.texture_anim_fps == b.texture_anim_fps &&
27 a.texture_anim_map_mode == b.texture_anim_map_mode &&
28 a.texture_anim_map_dir == b.texture_anim_map_dir &&
29 // a.disable_collision == b.disable_collision &&
30 // a.disable_lightmap == b.disable_lightmap &&
31 // a.dont_collapse == b.dont_collapse &&
32 a.detail_object == b.detail_object &&
33 a.detail_object_scale == b.detail_object_scale &&
34 a.force_occluder == b.force_occluder &&
35 a.environment_mapping == b.environment_mapping &&
36 a.environment_mapping_strength == b.environment_mapping_strength &&
37 a.wave_mode == b.wave_mode &&
38 a.wave_speed == b.wave_speed &&
39 a.wave_max_amplitude == b.wave_max_amplitude &&
40 a.wave_grid_size == b.wave_grid_size &&
41 a.ignore_sun == b.ignore_sun &&
42 // a.alpha_func == b.alpha_func &&
43 a.default_mapping == b.default_mapping;
44 }
45
46static float areaOf(const Vec3 sahMin, const Vec3 sahMax) {
47 auto sz = sahMax - sahMin;
48 return 2*(sz.x*sz.y + sz.x*sz.z + sz.y*sz.z);
49 }
50
51static float areaOf(const Vec3 sz) {
52 return 2*(sz.x*sz.y + sz.x*sz.z + sz.y*sz.z);
53 }
54
55static uint32_t floatBitsToUint(float f) {
56 return reinterpret_cast<uint32_t&>(f);
57 }
58
60 using value_type = std::pair<uint64_t,uint32_t>;
61 using iterator = std::vector<value_type>::iterator;
62
63 std::vector<std::pair<uint64_t,uint32_t>> data;
64
65 void reserve(size_t n) { data.reserve(n*3); }
66
67 void clear() { data.clear(); }
68 size_t size() { return data.size(); }
69 bool empty() { return data.empty(); }
70
71 void push_back(const value_type& v) { data.push_back(v); }
72
73 void sort() {
74 std::sort(data.begin(), data.end(), [](const value_type& l, const value_type& r){
75 return l.first<r.first;
76 });
77 }
78
79 iterator begin() { return data.begin(); }
80 iterator end() { return data.end(); }
81
83 return data.erase(i);
84 }
85
86 std::pair<iterator,iterator> equal_range(uint64_t v) {
87 auto l = std::lower_bound(data.begin(), data.end(), v, [](const value_type& x, uint64_t v){
88 return x.first<v;
89 });
90 auto r = l; ++r;
91 while(r!=data.end()) {
92 if(r->first>v)
93 break;
94 ++r;
95 }
96 return std::make_pair(l,r);
97 }
98 };
99
100void PackedMesh::Meshlet::flush(std::vector<Vertex>& vertices,
101 std::vector<uint32_t>& indices,
102 std::vector<uint8_t>& indices8,
103 std::vector<Cluster>& instances,
104 const zenkit::Mesh& mesh) {
105 if(indSz==0)
106 return;
107
108 if(!validate())
109 return;
110
111 instances.push_back(bounds);
112
113 auto& vbo = mesh.vertices; // xyz
114 auto& uv = mesh.features; // uv, normal
115
116 size_t vboSz = vertices.size();
117 vertices.resize(vboSz + MaxVert);
118 for(size_t i=0; i<vertSz; ++i) {
119 Vertex vx = {};
120 auto& v = uv [vert[i].second];
121
122 vx.pos[0] = vbo[vert[i].first].x;
123 vx.pos[1] = vbo[vert[i].first].y;
124 vx.pos[2] = vbo[vert[i].first].z;
125 vx.norm[0] = v.normal.x;
126 vx.norm[1] = v.normal.y;
127 vx.norm[2] = v.normal.z;
128 vx.uv[0] = v.texture.x;
129 vx.uv[1] = v.texture.y;
130 vx.color = 0xFFFFFFFF;
131 vertices[vboSz+i] = vx;
132 }
133 for(size_t i=vertSz; i<MaxVert; ++i) {
134 Vertex vx = {};
135 vertices[vboSz+i] = vx;
136 }
137
138 size_t iboSz = indices.size();
139 indices.resize(iboSz + MaxInd);
140 for(size_t i=0; i<indSz; ++i) {
141 indices[iboSz+i] = uint32_t(vboSz)+indexes[i];
142 }
143 for(size_t i=indSz; i<MaxInd; ++i) {
144 // padd with degenerated triangles
145 indices[iboSz+i] = uint32_t(vboSz+indSz/3);
146 }
147
148 if(Gothic::options().doMeshShading || true) {
149 size_t iboSz8 = indices8.size();
150 indices8.resize(iboSz8 + MaxPrim*4);
151 for(size_t i=0; i<indSz; i+=3) {
152 size_t at = iboSz8 + (i/3)*4;
153 indices8[at+0] = indexes[i+0];
154 indices8[at+1] = indexes[i+1];
155 indices8[at+2] = indexes[i+2];
156 indices8[at+3] = 0;
157 }
158 if(indSz+1<MaxInd) {
159 size_t at = iboSz8 + MaxPrim*4 - 4;
160 indices8[at+0] = indexes[0];
161 indices8[at+1] = indexes[0];
162 indices8[at+2] = indSz/3;
163 indices8[at+3] = vertSz;
164 }
165 }
166 }
167
168void PackedMesh::Meshlet::flush(std::vector<Vertex>& vertices, std::vector<VertexA>& verticesA,
169 std::vector<uint32_t>& indices, std::vector<uint8_t>& indices8,
170 std::vector<uint32_t>* verticesId,
171 const std::vector<zenkit::Vec3>& vboList,
172 const std::vector<zenkit::MeshWedge>& wedgeList,
173 const std::vector<SkeletalData>* skeletal) {
174 if(indSz==0)
175 return;
176
177 auto& vbo = vboList; // xyz
178 auto& uv = wedgeList; // uv, normal
179
180 size_t vboSz = 0;
181 size_t iboSz = indices.size();
182
183 if(skeletal==nullptr) {
184 vboSz = vertices.size();
185 vertices.resize(vboSz+MaxVert);
186 } else {
187 vboSz = verticesA.size();
188 verticesA.resize(vboSz+MaxVert);
189 }
190
191 indices.resize(iboSz + MaxInd);
192
193 if(verticesId!=nullptr)
194 verticesId->resize(vboSz+MaxVert);
195
196 if(skeletal==nullptr) {
197 for(size_t i=0; i<vertSz; ++i) {
198 Vertex vx = {};
199 auto& v = uv [vert[i].second];
200 vx.pos[0] = vbo[vert[i].first].x;
201 vx.pos[1] = vbo[vert[i].first].y;
202 vx.pos[2] = vbo[vert[i].first].z;
203 vx.norm[0] = v.normal.x;
204 vx.norm[1] = v.normal.y;
205 vx.norm[2] = v.normal.z;
206 vx.uv[0] = v.texture.x;
207 vx.uv[1] = v.texture.y;
208 vx.color = 0xFFFFFFFF;
209 vertices[vboSz+i] = vx;
210 if(verticesId!=nullptr)
211 (*verticesId)[vboSz+i] = vert[i].first;
212 }
213 for(size_t i=vertSz; i<MaxVert; ++i) {
214 Vertex vx = {};
215 vertices[vboSz+i] = vx;
216 if(verticesId!=nullptr)
217 (*verticesId)[vboSz+i] = uint32_t(-1);
218 }
219 } else {
220 auto& sk = *skeletal;
221 for(size_t i=0; i<vertSz; ++i) {
222 VertexA vx = {};
223 auto& v = uv [vert[i].second];
224 vx.norm[0] = v.normal.x;
225 vx.norm[1] = v.normal.y;
226 vx.norm[2] = v.normal.z;
227 vx.uv[0] = v.texture.x;
228 vx.uv[1] = v.texture.y;
229 vx.color = 0xFFFFFFFF;
230 for(int r=0; r<4; ++r) {
231 vx.pos [r][0] = sk[vert[i].first].localPositions[r].x;
232 vx.pos [r][1] = sk[vert[i].first].localPositions[r].y;
233 vx.pos [r][2] = sk[vert[i].first].localPositions[r].z;
234 vx.boneId [r] = sk[vert[i].first].boneIndices[r];
235 vx.weights[r] = sk[vert[i].first].weights[r];
236 }
237 verticesA[vboSz+i] = vx;
238 }
239 for(size_t i=vertSz; i<MaxVert; ++i) {
240 VertexA vx = {};
241 verticesA[vboSz+i] = vx;
242 }
243 }
244
245 for(size_t i=0; i<indSz; ++i) {
246 indices[iboSz+i] = uint32_t(vboSz)+indexes[i];
247 }
248 for(size_t i=indSz; i<MaxInd; ++i) {
249 // padd with degenerated triangles
250 indices[iboSz+i] = uint32_t(vboSz+indSz/3);
251 }
252
253 if(Gothic::options().doMeshShading || true) {
254 size_t iboSz8 = indices8.size();
255 indices8.resize(iboSz8 + MaxPrim*4);
256 for(size_t i=0; i<indSz; i+=3) {
257 size_t at = iboSz8 + (i/3)*4;
258 indices8[at+0] = indexes[i+0];
259 indices8[at+1] = indexes[i+1];
260 indices8[at+2] = indexes[i+2];
261 indices8[at+3] = 0;
262 }
263 if(indSz+1<MaxInd) {
264 size_t at = iboSz8 + MaxPrim*4 - 4;
265 indices8[at+0] = indexes[0];
266 indices8[at+1] = indexes[0];
267 indices8[at+2] = indSz/3;
268 indices8[at+3] = vertSz;
269 }
270 }
271 }
272
273bool PackedMesh::Meshlet::validate() const {
274 return true;
275 /*
276 // debug code
277 for(int i=0; i<indSz; ++i) {
278 for(int r=i+1; r<indSz; ++r) {
279 if(indexes[i]==indexes[r])
280 return true;
281 }
282 }
283 return false;
284 */
285 }
286
287bool PackedMesh::Meshlet::insert(const Vert& a, const Vert& b, const Vert& c) {
288 if(indSz+3>MaxInd)
289 return false;
290
291 uint8_t ea = MaxVert, eb = MaxVert, ec = MaxVert;
292 for(uint8_t i=0; i<vertSz; ++i) {
293 if(vert[i]==a)
294 ea = i;
295 if(vert[i]==b)
296 eb = i;
297 if(vert[i]==c)
298 ec = i;
299 }
300
301 uint8_t vSz = vertSz;
302 if(ea==MaxVert) {
303 ea = vSz;
304 ++vSz;
305 }
306 if(eb==MaxVert) {
307 eb = vSz;
308 ++vSz;
309 }
310 if(ec==MaxVert) {
311 ec = vSz;
312 ++vSz;
313 }
314
315 if(vSz>MaxVert)
316 return false;
317
318 if(vSz==vertSz) {
319 for(size_t i=0; i<indSz; i+=3) {
320 if(indexes[i+0]==ea && indexes[i+1]==eb && indexes[i+2]==ec) {
321 // duplicant?
322 return true;
323 }
324 }
325 }
326
327 indexes[indSz+0] = ea;
328 indexes[indSz+1] = eb;
329 indexes[indSz+2] = ec;
330 indSz = uint8_t(indSz+3u);
331
332 vert[ea] = a;
333 vert[eb] = b;
334 vert[ec] = c;
335 vertSz = vSz;
336
337 return true;
338 }
339
340void PackedMesh::Meshlet::clear() {
341 vertSz = 0;
342 indSz = 0;
343 }
344
345void PackedMesh::Meshlet::updateBounds(const zenkit::Mesh& mesh) {
346 updateBounds(mesh.vertices);
347 }
348
349void PackedMesh::Meshlet::updateBounds(const zenkit::MultiResolutionMesh& mesh) {
350 updateBounds(mesh.positions);
351 }
352
353void PackedMesh::Meshlet::updateBounds(const std::vector<zenkit::Vec3>& vbo) {
354 float dim = 0;
355 for(size_t i=0; i<vertSz; ++i)
356 for(size_t r=i+1; r<vertSz; ++r) {
357 auto a = vbo[vert[i].first];
358 auto b = vbo[vert[r].first];
359 a.x -= b.x;
360 a.y -= b.y;
361 a.z -= b.z;
362 float d = a.x*a.x + a.y*a.y + a.z*a.z;
363 if(dim<d) {
364 bounds.pos = Vec3(b.x,b.y,b.z)+Vec3(a.x,a.y,a.z)*0.5f;
365 dim = d;
366 }
367 }
368 bounds.r = 0;
369 for(size_t i=0; i<vertSz; ++i) {
370 auto a = vbo[vert[i].first];
371 a.x -= bounds.pos.x;
372 a.y -= bounds.pos.y;
373 a.z -= bounds.pos.z;
374 float d = (a.x*a.x + a.y*a.y + a.z*a.z);
375 if(bounds.r<d)
376 bounds.r = d;
377 }
378 bounds.r = std::sqrt(bounds.r);
379 }
380
381bool PackedMesh::Meshlet::canMerge(const Meshlet& other) const {
382 if(vertSz+other.vertSz>MaxVert)
383 return false;
384 if(indSz+other.indSz>MaxInd)
385 return false;
386 return true;
387 }
388
389bool PackedMesh::Meshlet::hasIntersection(const Meshlet& other) const {
390 return (bounds.pos-other.bounds.pos).quadLength() < std::pow(bounds.r+other.bounds.r,2.f);
391 }
392
393float PackedMesh::Meshlet::qDistance(const Meshlet& other) const {
394 return (bounds.pos-other.bounds.pos).quadLength();
395 }
396
397void PackedMesh::Meshlet::merge(const Meshlet& other) {
398 for(uint8_t i=0; i<other.indSz; ++i) {
399 uint8_t index = uint8_t(vertSz+other.indexes[i]);
400 indexes[indSz] = index;
401 vert[index] = other.vert[other.indexes[i]];
402 ++indSz;
403 }
404 vertSz = uint8_t(vertSz+other.vertSz);
405 }
406
407
408PackedMesh::PackedMesh(const zenkit::Mesh& mesh, PkgType type) {
409 if(type==PK_VisualLnd && Gothic::inst().options().doSoftwareRT) {
410 packBVH(mesh);
411 }
412
413 if(type==PK_VisualLnd) {
414 // dbgMesh(mesh);
415 }
416
417 if(type==PK_VisualLnd || type==PK_Visual) {
418 packMeshletsLnd(mesh);
419 computeBbox();
420 return;
421 }
422
423 if(type==PK_Physic) {
424 packPhysics(mesh,type);
425 return;
426 }
427 }
428
429PackedMesh::PackedMesh(const zenkit::MultiResolutionMesh& mesh, PkgType type) {
430 subMeshes.resize(mesh.sub_meshes.size());
431 /* NOTE: some mods do have corrupted content description,
432 * using alpha_test==0, for some of vegetation
433 */
434 isUsingAlphaTest = true || mesh.alpha_test;
435 {
436 auto bbox = mesh.bbox;
437 mBbox[0] = Vec3(bbox.min.x,bbox.min.y,bbox.min.z);
438 mBbox[1] = Vec3(bbox.max.x,bbox.max.y,bbox.max.z);
439 }
440
441 packMeshletsObj(mesh,type,nullptr);
442 }
443
444PackedMesh::PackedMesh(const zenkit::SoftSkinMesh& skinned) {
445 auto& mesh = skinned.mesh;
446 subMeshes.resize(mesh.sub_meshes.size());
447 {
448 auto bbox = phoenix_compat::get_total_aabb(skinned);
449 mBbox[0] = Vec3(bbox.min.x,bbox.min.y,bbox.min.z);
450 mBbox[1] = Vec3(bbox.max.x,bbox.max.y,bbox.max.z);
451 }
452
453 std::vector<SkeletalData> vertices(mesh.positions.size());
454 // Extract weights and local positions
455 auto& stream = skinned.weights;
456 for(size_t i=0; i<stream.size() && i<vertices.size(); ++i) {
457 auto& vert = vertices[i];
458 auto& wx = stream[i];
459
460 for(size_t j=0; j<wx.size(); j++) {
461 auto& weight = wx[j];
462 vert.boneIndices[j] = weight.node_index;
463 vert.localPositions[j] = {weight.position.x, weight.position.y, weight.position.z};
464 vert.weights[j] = weight.weight;
465 }
466 // MODS: renormalize weights. Found issue with not 1.0 summ in gold-remaster mod.
467 const float sum = (vert.weights[0] + vert.weights[1] + vert.weights[2] + vert.weights[3]);
468 if(sum>std::numeric_limits<float>::min()) {
469 vert.weights[0] /= sum;
470 vert.weights[1] /= sum;
471 vert.weights[2] /= sum;
472 vert.weights[3] /= sum;
473 }
474 }
475
476 packMeshletsObj(mesh,PK_Visual,&vertices);
477 }
478
479void PackedMesh::packPhysics(const zenkit::Mesh& mesh, PkgType type) {
480 auto& vbo = mesh.vertices;
481 auto& ibo = mesh.polygons.vertex_indices;
482 vertices.reserve(vbo.size());
483
484 auto& mid = mesh.polygons.material_indices;
485 auto& mat = mesh.materials;
486
487 std::vector<Prim> prim;
488 prim.reserve(mid.size());
489 for(size_t i=0; i<mid.size(); ++i) {
490 auto& m = mat[mid[i]];
491 if(m.disable_collision)
492 continue;
493 Prim p;
494 p.primId = uint32_t(i*3);
495 if(m.name.find(':')==std::string::npos) {
496 // unnamed materials - can merge them
497 p.mat = uint8_t(m.group);
498 } else {
499 // offset named materials
500 p.mat = uint32_t(zenkit::MaterialGroup::NONE) + mid[i];
501 }
502 prim.push_back(p);
503 }
504
505 std::sort(prim.begin(), prim.end(), [](const Prim& a, const Prim& b){
506 return std::tie(a.mat) < std::tie(b.mat);
507 });
508
509 std::unordered_map<uint32_t,size_t> icache;
510 icache.rehash(size_t(std::sqrt(ibo.size())));
511
512 indices.reserve(ibo.size());
513 for(size_t i=0; i<prim.size();) {
514 const auto mId = prim[i].mat;
515
516 SubMesh sub;
517 sub.iboOffset = indices.size();
518
519 if(mId < size_t(zenkit::MaterialGroup::NONE)) {
520 sub.material.name = "";
521 sub.material.group = zenkit::MaterialGroup(mId);
522 } else {
523 auto& m = mat[mId-size_t(zenkit::MaterialGroup::NONE)];
524 sub.material.name = m.name;
525 sub.material.group = m.group;
526 }
527
528 for(; i<prim.size() && prim[i].mat==mId; ++i) {
529 auto pr = prim[i].primId;
530 for(size_t r=0; r<3; ++r) {
531 uint32_t index = ibo[pr+r];
532 auto rx = icache.find(index);
533 if(rx!=icache.end()) {
534 indices.push_back(uint32_t(rx->second));
535 } else {
536 Vertex vx = {};
537 vx.pos[0] = vbo[index].x;
538 vx.pos[1] = vbo[index].y;
539 vx.pos[2] = vbo[index].z;
540
541 size_t val = vertices.size();
542 vertices.emplace_back(vx);
543 indices.push_back(uint32_t(val));
544 icache[index] = uint32_t(val);
545 }
546 }
547 }
548
549 sub.iboLength = indices.size()-sub.iboOffset;
550 if(sub.iboLength>0)
551 subMeshes.emplace_back(std::move(sub));
552 }
553 }
554
555void PackedMesh::packBVH(const zenkit::Mesh& mesh) {
556 packBVH2(mesh);
557 // packCWBVH8(mesh);
558 }
559
560void PackedMesh::quadAddPrim(Fragment& f, const zenkit::Mesh& mesh, uint32_t prim0, uint32_t prim1, uint32_t iMin, uint32_t iMax) {
561 auto& ibo = mesh.polygons.vertex_indices;
562
563 f.prim0 = prim0;
564 f.prim1 = prim1;
565
566 auto i0 = ibo[prim0*3+0];
567 auto i1 = ibo[prim0*3+1];
568 auto i2 = ibo[prim0*3+2];
569 while(i1==iMin || i1==iMax) {
570 auto t = i0;
571 i0 = i1;
572 i1 = i2;
573 i2 = t;
574 }
575 f.ibo[0] = i0;
576 f.ibo[1] = i1;
577 f.ibo[2] = i2;
578
579 if(prim1==0xFFFFFFFF) {
580 f.ibo[3] = i2;
581 return;
582 }
583
584 i0 = ibo[prim1*3+0];
585 i1 = ibo[prim1*3+1];
586 i2 = ibo[prim1*3+2];
587 while(i1==iMin || i1==iMax) {
588 auto t = i0;
589 i0 = i1;
590 i1 = i2;
591 i2 = t;
592 }
593 f.ibo[3] = i1;
594 }
595
596std::vector<PackedMesh::Fragment> PackedMesh::packQuads(const zenkit::Mesh& mesh) {
597 auto pullVert = [&](const zenkit::Mesh& mesh, uint32_t i) {
598 auto v = mesh.vertices[i];
599 return Tempest::Vec3(v.x, v.y, v.z);
600 };
601
602 auto& ibo = mesh.polygons.vertex_indices;
603 //auto& feat = mesh.polygons.feature_indices;
604 auto& mid = mesh.polygons.material_indices;
605
606 // https://www.highperformancegraphics.org/posters23/Fast_Triangle_Pairing_for_Ray_Tracing.pdf
607 std::vector<HalfEdge> edgeFrag;
608 for(size_t i=0; i<mid.size(); ++i) {
609 const auto primId = uint32_t(i*3);
610 uint32_t ib[3] = {};
611 ib[0] = ibo[primId+0];
612 ib[1] = ibo[primId+1];
613 ib[2] = ibo[primId+2];
614
615 if(ib[0]==ib[1] || ib[0]==ib[2] || ib[1]==ib[2])
616 continue;
617
618 for(size_t r=0; r<3; ++r) {
619 auto i0 = ib[(r ) ];
620 auto i1 = ib[(r+1)%3];
621 auto bbox = pullVert(mesh, i0) - pullVert(mesh, i1);
622 bbox.x = std::abs(bbox.x);
623 bbox.y = std::abs(bbox.y);
624 bbox.z = std::abs(bbox.z);
625
626 HalfEdge f;
627 f.sah = areaOf(bbox);
628 f.iMin = std::min(i0, i1);
629 f.iMax = std::max(i0, i1);
630 f.prim = uint32_t(i);
631 f.wnd = i0==f.iMin;
632 edgeFrag.push_back(f);
633 }
634 }
635
636 std::sort(edgeFrag.begin(), edgeFrag.end(), [](const HalfEdge& l, const HalfEdge& r){
637 return std::tie(l.sah, l.iMin, l.iMax, l.prim) > std::tie(r.sah, r.iMin, r.iMax, r.prim);
638 });
639
640 std::vector<bool> pairings(mid.size());
641 std::vector<Fragment> frag;
642 for(size_t i=0; i<edgeFrag.size(); ++i) {
643 const auto& a = edgeFrag[i+0];
644 const auto& b = (i+1<edgeFrag.size()) ? edgeFrag[i+1] : a;
645 if(pairings[a.prim] || pairings[b.prim])
646 continue;
647
648 Fragment f;
649 if(i+1<edgeFrag.size() && a.iMin==b.iMin && a.iMax==b.iMax) {
650 pairings[a.prim] = true;
651 pairings[b.prim] = true;
652 quadAddPrim(f, mesh, a.prim, b.prim, a.iMin, a.iMax);
653 frag.push_back(f);
654 ++i;
655 continue;
656 }
657 }
658 for(size_t i=0; i<pairings.size(); ++i) {
659 if(pairings[i])
660 continue;
661 Fragment f;
662 quadAddPrim(f, mesh, uint32_t(i), 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
663 frag.push_back(f);
664 }
665
666 for(auto& f:frag) {
667 Vec3 bbmin = pullVert(mesh, f.ibo[0]);
668 Vec3 bbmax = bbmin;
669 for(size_t i=1; i<4; ++i) {
670 Vec3 vec = pullVert(mesh, f.ibo[i]);
671 bbmin.x = std::min(bbmin.x, vec.x);
672 bbmin.y = std::min(bbmin.y, vec.y);
673 bbmin.z = std::min(bbmin.z, vec.z);
674
675 bbmax.x = std::max(bbmax.x, vec.x);
676 bbmax.y = std::max(bbmax.y, vec.y);
677 bbmax.z = std::max(bbmax.z, vec.z);
678 }
679
680 f.centroid = (bbmin+bbmax)/2.f;
681 f.bbmin = bbmin;
682 f.bbmax = bbmax;
683 }
684
685 return frag;
686 }
687
688std::pair<uint32_t, float> PackedMesh::findNodeSplit(const Fragment* frag, size_t size, const bool useSah) {
689 if(!useSah)
690 return std::make_pair(size/2, 0); // median split
691
692 size_t split = size/2;
693 float bestCost = std::numeric_limits<float>::max();
694
695 std::vector<float> sahB(size);
696 Vec3 sahMin = frag[size-1].bbmin;
697 Vec3 sahMax = frag[size-1].bbmax;
698 for(size_t i=size; i>1; ) {
699 --i;
700 auto& f = frag[i];
701 sahMin.x = std::min(sahMin.x, f.bbmin.x);
702 sahMin.y = std::min(sahMin.y, f.bbmin.y);
703 sahMin.z = std::min(sahMin.z, f.bbmin.z);
704
705 sahMax.x = std::max(sahMax.x, f.bbmax.x);
706 sahMax.y = std::max(sahMax.y, f.bbmax.y);
707 sahMax.z = std::max(sahMax.z, f.bbmax.z);
708
709 sahB[i-1] = areaOf(sahMin, sahMax);
710 }
711
712 sahMin = frag[0].bbmin;
713 sahMax = frag[0].bbmax;
714 for(size_t i=0; i+1<size; ++i) {
715 auto& f = frag[i];
716 sahMin.x = std::min(sahMin.x, f.bbmin.x);
717 sahMin.y = std::min(sahMin.y, f.bbmin.y);
718 sahMin.z = std::min(sahMin.z, f.bbmin.z);
719
720 sahMax.x = std::max(sahMax.x, f.bbmax.x);
721 sahMax.y = std::max(sahMax.y, f.bbmax.y);
722 sahMax.z = std::max(sahMax.z, f.bbmax.z);
723
724 const float sahA = areaOf(sahMin, sahMax);
725 const float cost = sahA*float(i) + sahB[i]*float(size-i);
726 if(cost<bestCost) {
727 bestCost = cost;
728 split = i+1;
729 }
730 }
731
732 return std::make_pair(split, bestCost);
733 }
734
735std::pair<uint32_t, bool> PackedMesh::findNodeSplitSah(Fragment* frag, size_t size) {
736 const bool useSah = true;
737 if(!useSah)
738 return std::make_pair(size/2, 0); // median split
739
740 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.x < r.centroid.x; });
741 const auto retX = findNodeSplit(frag, size, useSah);
742
743 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.y < r.centroid.y; });
744 const auto retY = findNodeSplit(frag, size, useSah);
745
746 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.z < r.centroid.z; });
747 const auto retZ = findNodeSplit(frag, size, useSah);
748
749 if(retZ.second <= retX.second && retZ.second <= retY.second)
750 return std::make_pair(retZ.first, true);
751 if(retY.second <= retX.second && retY.second <= retZ.second) {
752 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.y < r.centroid.y; });
753 return std::make_pair(retY.first, true);
754 }
755 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.x < r.centroid.x; });
756 return std::make_pair(retX.first, true);
757 }
758
759void PackedMesh::packBlocks(Block* out, uint32_t& outSz, uint8_t destSz, Fragment* frag, size_t size) {
760 out[outSz].frag = frag;
761 out[outSz].size = size;
762 ++outSz;
763
764 while(outSz<destSz) {
765 uint32_t current = 0;
766 for(uint32_t i=1; i<outSz; ++i) {
767 if(out[i].size > out[current].size)
768 current = i;
769 }
770
771 auto& cur = out[current];
772 if(cur.size<=1)
773 break;
774
775 const auto splitV = findNodeSplitSah(cur.frag, cur.size);
776 const size_t split = splitV.first;
777
778 auto& nxt = out[outSz]; ++outSz;
779 nxt.frag = cur.frag+split;
780 nxt.size = cur.size-split;
781 cur.size = split;
782 }
783
784 for(size_t i=0; i<outSz; ++i) {
785 auto& cur = out[i];
786 computeBbox(cur.bbmin, cur.bbmax, cur.frag, cur.size);
787 }
788
789 std::sort(out, out+outSz, [](const Block& l, const Block& r){
790 return areaOf(l.bbmin, l.bbmax)*float(l.size) < areaOf(r.bbmin, r.bbmax)*float(r.size);
791 });
792 }
793
794void PackedMesh::computeBbox(Tempest::Vec3& bbmin, Tempest::Vec3& bbmax, const Fragment* frag, size_t size) {
795 bbmin = frag[0].bbmin;
796 bbmax = frag[0].bbmax;
797 for(size_t i=1; i<size; ++i) {
798 auto& f = frag[i];
799 bbmin.x = std::min(bbmin.x, f.bbmin.x);
800 bbmin.y = std::min(bbmin.y, f.bbmin.y);
801 bbmin.z = std::min(bbmin.z, f.bbmin.z);
802
803 bbmax.x = std::max(bbmax.x, f.bbmax.x);
804 bbmax.y = std::max(bbmax.y, f.bbmax.y);
805 bbmax.z = std::max(bbmax.z, f.bbmax.z);
806 }
807 }
808
809void PackedMesh::packBVH2(const zenkit::Mesh& mesh) {
810 auto frag = packQuads(mesh);
811
812 std::vector<BVHNode> nodes;
813 packBVH2(mesh, nodes, frag.data(), frag.size(), size_t(-1));
814
815 //TODO: ensure, that first node is a box node
816 bvhNodes = std::move(nodes);
817 }
818
819uint32_t PackedMesh::packBVH2(const zenkit::Mesh& mesh, std::vector<BVHNode>& nodes,
820 Fragment* frag, size_t size, size_t parentSz) {
821 auto pullVert = [&](const zenkit::Mesh& mesh, uint32_t i) {
822 auto v = mesh.vertices[i];
823 return Tempest::Vec3(v.x, v.y, v.z);
824 };
825
826 if(size==0) {
827 assert(0);
828 return BVH_NullNode;
829 }
830
831 if(size<=1) {
832 const auto& f = frag[0];
833 BVHNode node = {};
834 node.padd0 = 0;
835 node.padd1 = f.prim0;
836 node.lmin = pullVert(mesh, f.ibo[0]);
837 node.lmax = pullVert(mesh, f.ibo[1]);
838 node.rmin = pullVert(mesh, f.ibo[2]);
839 node.lmax -= node.lmin;
840 node.rmin -= node.lmin;
841
842 const size_t nId = nodes.size();
843 if(true && frag[0].prim1!=0xFFFFFFFF) {
844 node.rmax = pullVert(mesh, f.ibo[3]);
845 node.rmax -= node.lmin;
846 nodes.emplace_back(node);
847 return uint32_t(nId | BVH_Tri2Node);
848 }
849
850 nodes.emplace_back(node);
851 return uint32_t(nId | BVH_Tri1Node);
852 }
853
854 Block block[2] = {};
855 {
856 uint32_t blockSz = 0;
857 packBlocks(block, blockSz, 2, frag, size);
858 }
859
860 const size_t nId = nodes.size();
861 nodes.emplace_back(); //reserve memory
862
863 BVHNode node = {};
864 node.left = packBVH2(mesh, nodes, block[0].frag, block[0].size, size);
865 node.right = packBVH2(mesh, nodes, block[1].frag, block[1].size, size);
866 node.lmin = block[0].bbmin;
867 node.lmax = block[0].bbmax;
868 node.rmin = block[1].bbmin;
869 node.rmax = block[1].bbmax;
870 nodes[nId] = node;
871 /*
872 if(size<=16 && parentSz>16) {
873 paintBvh(nodes.data(), (nId | BVH_BoxNode), counter);
874 ++counter;
875 }
876 */
877 return uint32_t(nId | BVH_BoxNode);
878 }
879
880void PackedMesh::packCWBVH8(const zenkit::Mesh& mesh) {
881 //TODO: quad packing in leaf nodes
882 auto frag = packQuads(mesh);
883
884 std::vector<UVec4> nodes(sizeof(CWBVH8)/sizeof(UVec4));
885 auto root = packCWBVH8(mesh, nodes, frag.data(), frag.size());
886 std::memcpy(nodes.data(), &root, sizeof(root));
887
888 //TODO: ensure, that first node is a box node
889 bvh8Nodes = std::move(nodes);
890 }
891
892PackedMesh::CWBVH8 PackedMesh::packCWBVH8(const zenkit::Mesh& mesh, std::vector<UVec4>& nodes, Fragment* frag, size_t size) {
893 auto pullVert = [&](const zenkit::Mesh& mesh, uint32_t i) {
894 auto v = mesh.vertices[i];
895 return Tempest::Vec3(v.x, v.y, v.z);
896 };
897
898 auto toUvec4 = [](const Vec3 a){
899 UVec4 v;
900 v.x = floatBitsToUint(a.x);
901 v.y = floatBitsToUint(a.y);
902 v.z = floatBitsToUint(a.z);
903 v.w = 0; //TODO: texcoords
904 return v;
905 };
906
907 if(size==0) {
908 assert(0);
909 return CWBVH8();
910 }
911
912 Block block[8] = {};
913 {
914 uint32_t blockSz = 0;
915 packBlocks(block, blockSz, 8, frag, size);
916 assert(blockSz<=8);
917 }
918
919 constexpr uint32_t numVec = (sizeof(CWBVH8)/sizeof(UVec4));
920 auto node = nodeFromBlocks(block);
921
922 const uint32_t numNodes = node.pNodes;
923 const uint32_t numPrims = node.pPrimitives;
924
925 node.pNodes = node.pNodes==0 ? 0 : uint32_t(nodes.size());
926 node.pPrimitives = node.pPrimitives==0 ? 0 : uint32_t(nodes.size() + numNodes*numVec);
927 nodes.resize(nodes.size() + numNodes*numVec + numPrims*3);
928
929 uint32_t iNode = 0, iPrim = 0;
930 for(size_t i=0; i<8; ++i) {
931 auto& b = block[i];
932 if(b.size==0)
933 continue;
934
935 if((node.imask & (1u << i))!=0) {
936 auto child = packCWBVH8(mesh, nodes, b.frag, b.size);
937 auto* cwnode = reinterpret_cast<CWBVH8*>(nodes.data() + node.pNodes);
938 cwnode[iNode] = child;
939 iNode++;
940 continue;
941 }
942
943 if(b.size<=1) {
944 const auto& f = b.frag[0];
945 const uint32_t prim = b.frag[0].prim0;
946 const Vec3 ta = pullVert(mesh, f.ibo[0]);
947 const Vec3 tb = pullVert(mesh, f.ibo[1]);
948 const Vec3 tc = pullVert(mesh, f.ibo[2]);
949
950 auto* cwnode = (nodes.data() + node.pPrimitives + iPrim*3);
951 cwnode[0] = toUvec4(ta);
952 cwnode[1] = toUvec4(tb);
953 cwnode[2] = toUvec4(tc);
954
955 cwnode[0].w = prim;
956 iPrim++;
957 continue;
958 }
959
960 assert(0);
961 }
962
963 return node;
964 }
965
966
967PackedMesh::CWBVH8 PackedMesh::packCWBVH8(const zenkit::Mesh& mesh, std::vector<UVec4>& nodes,
968 const std::vector<uint32_t>& ibo, Fragment* frag, size_t size) {
969 auto pullVert = [&](uint32_t i) {
970 auto v = mesh.vertices[i];
971 return Tempest::Vec3(v.x, v.y, v.z);
972 };
973
974 auto toUvec4 = [](const Vec3 a){
975 UVec4 v;
976 v.x = floatBitsToUint(a.x);
977 v.y = floatBitsToUint(a.y);
978 v.z = floatBitsToUint(a.z);
979 v.w = 0; //TODO: texcoords
980 return v;
981 };
982
983 if(size==0) {
984 assert(0);
985 return CWBVH8();
986 }
987
988 Block block[8] = {};
989 {
990 uint32_t blockSz = 0;
991 packCW8Blocks(block, blockSz, mesh, nodes, frag, size, 0);
992 assert(blockSz<=8);
993 }
994
995 constexpr uint32_t numVec = (sizeof(CWBVH8)/sizeof(UVec4));
996 CWBVH8 node = nodeFromBlocks(block);
997
998 const uint32_t numNodes = node.pNodes;
999 const uint32_t numPrims = node.pPrimitives;
1000
1001 node.pNodes = node.pNodes==0 ? 0 : uint32_t(nodes.size());
1002 node.pPrimitives = node.pPrimitives==0 ? 0 : uint32_t(nodes.size()) + numNodes*numVec;
1003 nodes.resize(nodes.size() + numNodes*numVec + numPrims*3);
1004
1005 uint32_t iNode = 0, iPrim = 0;
1006 for(size_t i=0; i<8; ++i) {
1007 auto& b = block[i];
1008 if(b.size==0)
1009 continue;
1010
1011 if((node.imask & (1u << i))!=0) {
1012 auto child = packCWBVH8(mesh, nodes, ibo, b.frag, b.size);
1013 auto* cwnode = reinterpret_cast<CWBVH8*>(nodes.data() + node.pNodes);
1014 cwnode[iNode] = child;
1015 iNode++;
1016 continue;
1017 }
1018
1019 if(b.size<=1) {
1020 const uint32_t prim = b.frag[0].prim0;
1021 const Vec3 ta = pullVert(ibo[prim+0]);
1022 const Vec3 tb = pullVert(ibo[prim+1]);
1023 const Vec3 tc = pullVert(ibo[prim+2]);
1024
1025 auto* cwnode = (nodes.data() + node.pPrimitives + iPrim*3);
1026 cwnode[0] = toUvec4(ta);
1027 cwnode[1] = toUvec4(tb);
1028 cwnode[2] = toUvec4(tc);
1029
1030 cwnode[0].w = prim;
1031 iPrim++;
1032 continue;
1033 }
1034
1035 assert(0);
1036 }
1037
1038 return node;
1039 }
1040
1041void PackedMesh::packCW8Blocks(Block* out, uint32_t& outSz, const zenkit::Mesh& mesh, std::vector<UVec4>& nodes,
1042 Fragment* frag, size_t size, uint8_t depth) {
1043 Tempest::Vec3 bbmin = {}, bbmax = {};
1044 bbmin = frag[0].bbmin;
1045 bbmax = frag[0].bbmax;
1046 for(size_t i=1; i<size; ++i) {
1047 auto& f = frag[i];
1048 bbmin.x = std::min(bbmin.x, f.bbmin.x);
1049 bbmin.y = std::min(bbmin.y, f.bbmin.y);
1050 bbmin.z = std::min(bbmin.z, f.bbmin.z);
1051
1052 bbmax.x = std::max(bbmax.x, f.bbmax.x);
1053 bbmax.y = std::max(bbmax.y, f.bbmax.y);
1054 bbmax.z = std::max(bbmax.z, f.bbmax.z);
1055 }
1056
1057 if(depth>=3) {
1058 out[outSz].bbmin = bbmin;
1059 out[outSz].bbmax = bbmax;
1060 out[outSz].frag = frag;
1061 out[outSz].size = size;
1062 outSz++;
1063 return;
1064 }
1065
1066 Vec3 sz = bbmax-bbmin;
1067 if(sz.x>sz.y && sz.x>sz.z) {
1068 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.x < r.centroid.x; });
1069 }
1070 else if(sz.y>sz.x && sz.y>sz.z) {
1071 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.y < r.centroid.y; });
1072 }
1073 else {
1074 std::sort(frag, frag+size, [](const Fragment& l, const Fragment& r){ return l.centroid.z < r.centroid.z; });
1075 }
1076 //
1077 const bool useSah = false;
1078 const auto split = findNodeSplit(frag, size, useSah).first;
1079
1080 packCW8Blocks(out, outSz, mesh, nodes, frag, split, depth+1);
1081 packCW8Blocks(out, outSz, mesh, nodes, frag+split, size-split, depth+1);
1082
1083 if(depth==0) {
1084 orderBlocks(out, outSz, bbmin, bbmax);
1085 }
1086 }
1087
1088void PackedMesh::orderBlocks(Block* block, const uint32_t numBlocks, const Tempest::Vec3 bbmin, const Tempest::Vec3 bbmax) {
1089 // Greedy child node ordering
1090 const Vec3 nodeCen = (bbmin + bbmax) * 0.5f;
1091
1092 int assignment[8] = {};
1093 float cost[8][8];
1094 bool isSlotEmpty[8];
1095
1096 for(uint32_t s=0; s<8; s++) {
1097 isSlotEmpty[s] = true;
1098 for(size_t i=0; i<8; ++i)
1099 cost[s][i] = std::numeric_limits<float>::max();
1100 }
1101
1102 for(size_t i=0; i<8; i++)
1103 assignment[i] = -1;
1104
1105 for(uint32_t s = 0; s < 8; s++) {
1106 Vec3 ds = Vec3(
1107 (((s >> 2) & 1) == 1) ? -1.0f : 1.0f,
1108 (((s >> 1) & 1) == 1) ? -1.0f : 1.0f,
1109 (((s >> 0) & 1) == 1) ? -1.0f : 1.0f);
1110
1111 for(size_t i=0; i<numBlocks; ++i) {
1112 Vec3 cen = (block[i].bbmin + block[i].bbmax) * 0.5f;
1113 cost[s][i] = Vec3::dotProduct(cen - nodeCen, ds);
1114 }
1115 }
1116
1117 while(true) {
1118 float minCost = std::numeric_limits<float>::max();
1119 IVec2 minEntry = IVec2(-1);
1120
1121 for(int s = 0; s < 8; s++) {
1122 for(int i = 0; i < 8; i++) {
1123 if(assignment[i] == -1 && isSlotEmpty[s] && cost[s][i] < minCost) {
1124 minCost = cost[s][i];
1125 minEntry = IVec2(s, i);
1126 }
1127 }
1128 }
1129
1130 if(minEntry.x != -1 || minEntry.y != -1) {
1131 assert(minEntry.x != -1 && minEntry.y != -1);
1132 isSlotEmpty[minEntry.x] = false;
1133 assignment [minEntry.y] = minEntry.x;
1134 } else {
1135 assert(minEntry.x == -1 && minEntry.y == -1);
1136 break;
1137 }
1138 }
1139
1140 for(size_t i = 0; i < 8; i++) {
1141 if(assignment[i] == -1) {
1142 for(int s = 0; s < 8; s++) {
1143 if(isSlotEmpty[s]) {
1144 isSlotEmpty[s] = false;
1145 assignment[i] = s;
1146 break;
1147 }
1148 }
1149 }
1150 }
1151
1152 Block tmp[8];
1153 for(size_t i=0; i<8; ++i)
1154 tmp[i] = block[assignment[i]];
1155 std::memcpy(block, tmp, sizeof(Block)*8);
1156 }
1157
1158PackedMesh::CWBVH8 PackedMesh::nodeFromBlocks(Block* block) {
1159 CWBVH8 node = {};
1160
1161 Vec3 bbmin = block[0].bbmin;
1162 Vec3 bbmax = block[0].bbmax;
1163 for(size_t i=1; i<8; ++i) {
1164 auto& f = block[i];
1165 bbmin.x = std::min(bbmin.x, f.bbmin.x);
1166 bbmin.y = std::min(bbmin.y, f.bbmin.y);
1167 bbmin.z = std::min(bbmin.z, f.bbmin.z);
1168
1169 bbmax.x = std::max(bbmax.x, f.bbmax.x);
1170 bbmax.y = std::max(bbmax.y, f.bbmax.y);
1171 bbmax.z = std::max(bbmax.z, f.bbmax.z);
1172 }
1173
1174 node.p[0] = bbmin.x;
1175 node.p[1] = bbmin.y;
1176 node.p[2] = bbmin.z;
1177
1178 // Calculate quantization parameters for each axis respectively
1179 constexpr int Nq = 8;
1180 constexpr float denom = 1.0f / float((1 << Nq) - 1);
1181
1182 const float ex = exp2f(ceilf(log2f((bbmax.x - bbmin.x) * denom)));
1183 const float ey = exp2f(ceilf(log2f((bbmax.y - bbmin.y) * denom)));
1184 const float ez = exp2f(ceilf(log2f((bbmax.z - bbmin.z) * denom)));
1185
1186 const float inv_ex = 1.f/ex;
1187 const float inv_ey = 1.f/ey;
1188 const float inv_ez = 1.f/ez;
1189
1190 node.e[0] = uint8_t(reinterpret_cast<const uint32_t&>(ex) >> 23);
1191 node.e[1] = uint8_t(reinterpret_cast<const uint32_t&>(ey) >> 23);
1192 node.e[2] = uint8_t(reinterpret_cast<const uint32_t&>(ez) >> 23);
1193
1194 node.imask = 0;
1195 node.pPrimitives = 0;
1196 node.pNodes = 0;
1197
1198 const size_t maxPrimPerNode = 1;
1199 for(size_t i=0; i<8; ++i) {
1200 auto& b = block[i];
1201 if(b.size==0)
1202 continue;
1203
1204 if(b.size<=maxPrimPerNode) {
1205 static const uint32_t uNum[] = {0b000, 0b001, 0b011, 0b111};
1206 const uint32_t triIndex = node.pPrimitives * 3; // 3 x uvec4, per trinagle
1207 node.pPrimitives++;
1208 assert(triIndex<=24);
1209
1210 node.imask |= (0u << i); // nop
1211 node.meta[i] = uint8_t((uNum[b.size] << 5) | (triIndex & 0b11111));
1212 } else {
1213 node.pNodes++;
1214
1215 node.imask |= (1u << i);
1216 node.meta[i] = (1u << 5) | ((24+i) & 0b11111);
1217 }
1218
1219 node.qmin_x[i] = uint8_t(floorf((b.bbmin.x - node.p[0]) * inv_ex));
1220 node.qmin_y[i] = uint8_t(floorf((b.bbmin.y - node.p[1]) * inv_ey));
1221 node.qmin_z[i] = uint8_t(floorf((b.bbmin.z - node.p[2]) * inv_ez));
1222
1223 node.qmax_x[i] = uint8_t(ceilf ((b.bbmax.x - node.p[0]) * inv_ex));
1224 node.qmax_y[i] = uint8_t(ceilf ((b.bbmax.y - node.p[1]) * inv_ey));
1225 node.qmax_z[i] = uint8_t(ceilf ((b.bbmax.z - node.p[2]) * inv_ez));
1226 }
1227
1228 return node;
1229 }
1230
1231void PackedMesh::packMeshletsLnd(const zenkit::Mesh& mesh) {
1232 auto& ibo = mesh.polygons.vertex_indices;
1233 auto& feat = mesh.polygons.feature_indices;
1234 auto& mid = mesh.polygons.material_indices;
1235
1236 std::vector<uint32_t> mat(mesh.materials.size());
1237 for(size_t i=0; i<mesh.materials.size(); ++i)
1238 mat[i] = uint32_t(i);
1239
1240 for(size_t i=0; i<mesh.materials.size(); ++i) {
1241 for(size_t r=i+1; r<mesh.materials.size(); ++r) {
1242 if(mat[i]==mat[r])
1243 continue;
1244 auto& a = mesh.materials[i];
1245 auto& b = mesh.materials[r];
1246 if(isVisuallySame(a,b))
1247 mat[r] = mat[i];
1248 }
1249 }
1250
1251 std::vector<Prim> prim;
1252 prim.reserve(mid.size());
1253 for(size_t i=0; i<mid.size(); ++i) {
1254 Prim p;
1255 p.primId = uint32_t(i*3);
1256 p.mat = mat[mid[i]];
1257 prim.push_back(p);
1258 }
1259 std::sort(prim.begin(), prim.end(), [](const Prim& a, const Prim& b){
1260 return std::tie(a.mat) < std::tie(b.mat);
1261 });
1262
1263 PrimitiveHeap heap;
1264 heap.reserve(mid.size());
1265 std::vector<bool> used(mid.size(),false);
1266
1267 vertices.reserve(mesh.vertices.size());
1268 indices .reserve(ibo.size());
1269 indices8.reserve(ibo.size());
1270 meshletBounds.reserve(prim.size()/MaxPrim);
1271 for(size_t i=0; i<prim.size();) {
1272 const auto mId = prim[i].mat;
1273
1274 heap.clear();
1275 for(; i<prim.size() && prim[i].mat==mId; ++i) {
1276 const uint32_t id = prim[i].primId;
1277
1278 auto a = mkUInt64(ibo[id+0],feat[id+0]);
1279 auto b = mkUInt64(ibo[id+1],feat[id+1]);
1280 auto c = mkUInt64(ibo[id+2],feat[id+2]);
1281
1282 heap.push_back(std::make_pair(a, id));
1283 heap.push_back(std::make_pair(b, id));
1284 heap.push_back(std::make_pair(c, id));
1285 }
1286
1287 if(heap.size()==0)
1288 continue;
1289
1290 std::vector<Meshlet> meshlets = buildMeshlets(&mesh,nullptr,heap,used);
1291 for(size_t i=0; i<meshlets.size(); ++i)
1292 meshlets[i].updateBounds(mesh);
1293
1294 SubMesh pack;
1295 pack.material = mesh.materials[mId];
1296 pack.iboOffset = indices.size();
1297 for(auto& i:meshlets)
1298 i.flush(vertices,indices,indices8,meshletBounds,mesh);
1299 pack.iboLength = indices.size() - pack.iboOffset;
1300 if(pack.iboLength>0)
1301 subMeshes.push_back(std::move(pack));
1302
1303 //dbgUtilization(meshlets);
1304 }
1305 }
1306
1307void PackedMesh::packMeshletsObj(const zenkit::MultiResolutionMesh& mesh, PkgType type,
1308 const std::vector<SkeletalData>* skeletal) {
1309 auto* vId = (type==PK_VisualMorph) ? &verticesId : nullptr;
1310
1311 size_t maxTri = 0;
1312 for(auto& sm:mesh.sub_meshes)
1313 maxTri = std::max(maxTri, sm.triangles.size());
1314 PrimitiveHeap heap;
1315 heap.reserve(maxTri);
1316 std::vector<bool> used(maxTri);
1317
1318 for(size_t mId=0; mId<mesh.sub_meshes.size(); ++mId) {
1319 auto& sm = mesh.sub_meshes[mId];
1320 auto& pack = subMeshes[mId];
1321 pack.material = sm.mat;
1322
1323 heap.clear();
1324 for(size_t i=0; i<sm.triangles.size(); ++i) {
1325 const uint16_t* ibo = sm.triangles[i].wedges;
1326 for(int x=0; x<3; ++x) {
1327 auto& wedge = sm.wedges[ibo[x]];
1328 auto vert = mkUInt64(wedge.index,ibo[x]);
1329 heap.push_back(std::make_pair(vert,uint32_t(i*3)));
1330 }
1331 }
1332
1333 std::vector<Meshlet> meshlets = buildMeshlets(nullptr,&sm,heap,used);
1334 for(size_t i=0; i<meshlets.size(); ++i)
1335 meshlets[i].updateBounds(mesh);
1336
1337 pack.iboOffset = indices.size();
1338 for(auto& i:meshlets)
1339 i.flush(vertices,verticesA,indices,indices8,vId,mesh.positions,sm.wedges,skeletal);
1340 pack.iboLength = indices.size() - pack.iboOffset;
1341
1342 //dbgUtilization(meshlets);
1343 }
1344 }
1345
1346std::vector<PackedMesh::Meshlet> PackedMesh::buildMeshlets(const zenkit::Mesh* mesh,
1347 const zenkit::SubMesh* proto_mesh,
1348 PrimitiveHeap& heap, std::vector<bool>& used) {
1349 heap.sort();
1350 std::fill(used.begin(), used.end(), false);
1351
1352 const bool tightPacking = true;
1353
1354 size_t firstUnused = 0;
1355 size_t firstVert = 0;
1356 Meshlet active;
1357
1358 std::vector<Meshlet> meshlets;
1359 while(true) {
1360 size_t triId = size_t(-1);
1361
1362 for(size_t r=firstVert; r<active.vertSz; ++r) {
1363 auto i = active.vert[r];
1364 auto e = heap.equal_range(mkUInt64(i.first,i.second));
1365 for(auto it = e.first; it!=e.second; ++it) {
1366 auto id = it->second/3;
1367 if(used[id])
1368 continue;
1369 if(addTriangle(active,mesh,proto_mesh,id)) {
1370 used[id] = true;
1371 continue;
1372 }
1373 triId = id;
1374 break;
1375 }
1376 firstVert = r+1;
1377 }
1378
1379 if(triId==size_t(-1) && active.indSz!=0 && !tightPacking) {
1380 // active.validate();
1381 meshlets.push_back(std::move(active));
1382 active.clear();
1383 firstVert = 0;
1384 continue;
1385 }
1386
1387 if(triId==size_t(-1)) {
1388 for(auto i=heap.begin()+ptrdiff_t(firstUnused); i!=heap.end();) {
1389 if(used[i->second/3]) {
1390 ++i;
1391 continue;
1392 }
1393 firstUnused = size_t(std::distance(heap.begin(),i+1));
1394 triId = i->second/3;
1395 break;
1396 }
1397 }
1398
1399 if(triId!=size_t(-1) && addTriangle(active,mesh,proto_mesh,triId)) {
1400 used[triId] = true;
1401 continue;
1402 }
1403
1404 if(active.indSz!=0) {
1405 // active.validate();
1406 meshlets.push_back(std::move(active));
1407 active.clear();
1408 firstVert = 0;
1409 }
1410
1411 if(triId==size_t(-1))
1412 break;
1413 }
1414
1415 return meshlets;
1416 }
1417
1418bool PackedMesh::addTriangle(Meshlet& dest, const zenkit::Mesh* mesh, const zenkit::SubMesh* sm, size_t id) {
1419 if(mesh!=nullptr) {
1420 size_t id3 = id*3;
1421 auto& ibo = mesh->polygons.vertex_indices;
1422 auto& feat = mesh->polygons.feature_indices;
1423
1424 auto a = std::make_pair(ibo[id3+0],feat[id3+0]);
1425 auto b = std::make_pair(ibo[id3+1],feat[id3+1]);
1426 auto c = std::make_pair(ibo[id3+2],feat[id3+2]);
1427 return dest.insert(a,b,c);
1428 }
1429
1430 const uint16_t* feat = sm->triangles[id].wedges;
1431 auto a = std::make_pair(sm->wedges[feat[0]].index, feat[0]);
1432 auto b = std::make_pair(sm->wedges[feat[1]].index, feat[1]);
1433 auto c = std::make_pair(sm->wedges[feat[2]].index, feat[2]);
1434 return dest.insert(a,b,c);
1435 }
1436
1437void PackedMesh::debug(std::ostream &out) const {
1438 for(auto& i:vertices) {
1439 out << "v " << i.pos[0] << " " << i.pos[1] << " " << i.pos[2] << std::endl;
1440 out << "vn " << i.norm[0] << " " << i.norm[1] << " " << i.norm[2] << std::endl;
1441 out << "vt " << i.uv[0] << " " << i.uv[1] << std::endl;
1442 }
1443
1444 for(auto& s:subMeshes) {
1445 out << "o " << s.material.name << std::endl;
1446 for(size_t i=0; i<s.iboLength; i+=3) {
1447 const uint32_t* tri = &indices[s.iboOffset+i];
1448 out << "f " << 1+tri[0] << " " << 1+tri[1] << " " << 1+tri[2] << std::endl;
1449 }
1450 }
1451 }
1452
1453std::pair<Vec3, Vec3> PackedMesh::bbox() const {
1454 return std::make_pair(mBbox[0],mBbox[1]);
1455 }
1456
1457void PackedMesh::computeBbox() {
1458 if(vertices.size()==0) {
1459 mBbox[0] = Vec3();
1460 mBbox[1] = Vec3();
1461 return;
1462 }
1463
1464 mBbox[0].x = vertices[0].pos[0];
1465 mBbox[0].y = vertices[0].pos[1];
1466 mBbox[0].z = vertices[0].pos[2];
1467 mBbox[1] = mBbox[0];
1468
1469 for(size_t i=1; i<vertices.size(); ++i) {
1470 mBbox[0].x = std::min(mBbox[0].x, vertices[i].pos[0]);
1471 mBbox[0].y = std::min(mBbox[0].y, vertices[i].pos[1]);
1472 mBbox[0].z = std::min(mBbox[0].z, vertices[i].pos[2]);
1473
1474 mBbox[1].x = std::max(mBbox[1].x, vertices[i].pos[0]);
1475 mBbox[1].y = std::max(mBbox[1].y, vertices[i].pos[1]);
1476 mBbox[1].z = std::max(mBbox[1].z, vertices[i].pos[2]);
1477 }
1478 }
1479
1480void PackedMesh::dbgUtilization(const std::vector<Meshlet>& meshlets) {
1481 size_t usedV = 0, allocatedV = 0;
1482 size_t usedP = 0, allocatedP = 0;
1483 for(auto i:meshlets) {
1484 usedV += i.vertSz;
1485 allocatedV += MaxVert;
1486
1487 usedP += i.indSz;
1488 allocatedP += MaxInd;
1489 }
1490
1491 float procentV = (float(usedV*100)/float(allocatedV));
1492 float procentP = (float(usedP*100)/float(allocatedP));
1493 procentV = float(procentV*100)/100.f;
1494
1495 char buf[256] = {};
1496 std::snprintf(buf,sizeof(buf),"Meshlet usage: prim = %02.02f%%, vert = %02.02f%%, count = %d", procentP, procentV, int(meshlets.size()));
1497 Log::d(buf);
1498 if(procentV<25)
1499 Log::d("");
1500 if(usedV<usedP)
1501 Log::d("");
1502 }
1503
1504void PackedMesh::dbgMeshlets(const zenkit::Mesh& mesh, const std::vector<Meshlet*>& meshlets) {
1505 std::ofstream out("dbg.obj");
1506
1507 size_t off = 1;
1508 auto& vbo = mesh.vertices;
1509 for(auto i:meshlets) {
1510 out << "o meshlet" << off <<" " << i->bounds.r << std::endl;
1511 for(size_t r=0; r<i->vertSz; ++r) {
1512 auto& v = vbo[i->vert[r].first];
1513 out << "v " << v.x << " " << v.y << " " << v.z << std::endl;
1514 }
1515 for(size_t r=0; r<i->indSz; r+=3) {
1516 auto tri = &i->indexes[r];
1517 out << "f " << off+tri[0] << " " << off+tri[1] << " " << off+tri[2] << std::endl;
1518 }
1519 off += i->vertSz;
1520 }
1521 }
static auto options() -> const Options &
Definition gothic.cpp:496
static Gothic & inst()
Definition gothic.cpp:249
std::vector< UVec4 > bvh8Nodes
Definition packedmesh.h:81
std::vector< BVHNode > bvhNodes
Definition packedmesh.h:80
std::pair< Tempest::Vec3, Tempest::Vec3 > bbox() const
std::vector< Cluster > meshletBounds
Definition packedmesh.h:74
Tempest::BasicPoint< uint32_t, 4 > UVec4
Definition packedmesh.h:66
PackedMesh(const zenkit::MultiResolutionMesh &mesh, PkgType type)
std::vector< SubMesh > subMeshes
Definition packedmesh.h:73
void debug(std::ostream &out) const
std::vector< uint32_t > indices
Definition packedmesh.h:70
std::vector< VertexA > verticesA
Definition packedmesh.h:69
std::vector< uint8_t > indices8
Definition packedmesh.h:71
std::vector< uint32_t > verticesId
Definition packedmesh.h:76
std::vector< Vertex > vertices
Definition packedmesh.h:68
Resources::Vertex Vertex
Definition packedmesh.h:17
bool isUsingAlphaTest
Definition packedmesh.h:77
zenkit::AxisAlignedBoundingBox get_total_aabb(const zenkit::SoftSkinMesh &)
Definition phoenix.cpp:5
static bool isVisuallySame(const zenkit::Material &a, const zenkit::Material &b)
static uint64_t mkUInt64(uint32_t a, uint32_t b)
static float areaOf(const Vec3 sahMin, const Vec3 sahMax)
static uint32_t floatBitsToUint(float f)
std::vector< std::pair< uint64_t, uint32_t > > data
std::pair< iterator, iterator > equal_range(uint64_t v)
std::pair< uint64_t, uint32_t > value_type
std::vector< value_type >::iterator iterator
void push_back(const value_type &v)
iterator erase(iterator &i)