Cogs.Core
PackMeshCommand.cpp
1#include "Foundation/Logging/Logger.h"
2
3#include "Components/Core/MeshComponent.h"
4#include "Components/Core/SceneComponent.h"
5#include "Components/Core/TransformComponent.h"
6
7#include "Systems/Core/TransformSystem.h"
8
9#include "Resources/Mesh.h"
10#include "Resources/MeshManager.h"
11#include "Resources/VertexFormats.h"
12
13#include "EntityStore.h"
14#include "Context.h"
15
16#include "PackMeshCommand.h"
17
18#include <meshoptimizer/meshoptimizer.h>
19
20
21namespace {
22 using namespace Cogs::Core;
23 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("PackMeshCommand");
24
25 void handle(Context* context, PackMeshCommand* cmd, EntityPtr entity)
26 {
27 // Recurse into children
28 if (SceneComponent* sceneComp = entity->getComponent<SceneComponent>(); sceneComp && !sceneComp->children.empty()) {
29 for (EntityPtr& child : sceneComp->children) {
30 handle(context, cmd, child);
31 }
32 }
33
34 // Handle mesh
35 if (MeshComponent* meshComp = entity->getComponent<MeshComponent>(); meshComp && meshComp->meshHandle) {
36 cmd->undoItems.emplace_back(PackMeshCommand::UndoItem{ entity, meshComp->meshHandle });
37
38 glm::mat4 transform(1.f);
39
40 std::string status;
41 if (!PackMeshCommand::pack(context, status, transform, meshComp->meshHandle, cmd->packOptions)) {
42 LOG_ERROR(logger, "Faild to pack mesh: %s", status.c_str());
43 }
44 else {
45 if (!status.empty()) {
46 LOG_DEBUG(logger, "%s", status.c_str());
47 }
48
49 if (TransformComponent* trComp = entity->getComponent<TransformComponent>(); trComp) {
50 context->transformSystem->setLocalTransform(trComp, context->transformSystem->getLocalTransform(trComp) * transform);
51 }
52
53 }
54 meshComp->setChanged();
55 }
56 }
57
58}
59
60
61Cogs::Core::PackMeshCommand::PackMeshCommand(EditorState* state) :
62 EditorCommand(state, state->context)
63{
64}
65
67{
68 for (EntityId entityId : state->selected) {
69 if(EntityPtr entity = context->store->getEntity(entityId); entity) {
70 handle(context, this, entity);
71 }
72 }
73
74}
75
76void Cogs::Core::PackMeshCommand::undo()
77{
78 for (UndoItem& undoItem : undoItems) {
79 if (EntityPtr entity = undoItem.entity.lock(); entity) {
80 if (MeshComponent* meshComp = entity->getComponent<MeshComponent>(); meshComp) {
81 meshComp->meshHandle = undoItem.meshHandle;
82 meshComp->setChanged();
83 }
84 else {
85 LOG_ERROR(logger, "Failed to undo entity, entity lost its mesh component");
86 }
87 }
88 else {
89 LOG_ERROR(logger, "Failed to undo entity, entity does not exist");
90 }
91 }
92}
93
94
95namespace {
96
97 glm::vec2 octEncode(const glm::vec3& n)
98 {
99 const float t = std::abs(n.x) + std::abs(n.y) + std::abs(n.z);
100 glm::vec2 p = (1.f / t)*glm::vec2(n);
101 if (n.z <= 0.f) {
102 p = glm::vec2((0.f <= p.x ? 1.f : -1.f) * (1.f - std::abs(p.y)),
103 (0.f <= p.y ? 1.f : -1.f) * (1.f - std::abs(p.x)));
104
105 }
106 return p;
107 }
108
109 glm::vec3 octDecode(const glm::vec2& f)
110 {
111 // From https://twitter.com/Stubbesaurus/status/937994790553227264
112 glm::vec3 n = glm::vec3(f.x, f.y, 1.f - std::abs(f.x) - std::abs(f.y));
113 float t = std::max(-n.z, 0.f);
114 n.x += 0.f <= n.x ? -t : t;
115 n.y += 0.f <= n.y ? -t : t;
116 return glm::normalize(n);
117 }
118
119 glm::i16vec2 octEncodei16(const glm::vec3& n)
120 {
121#if 1
122 glm::vec2 enc = glm::clamp(32767.f * octEncode(n), glm::vec2(-32767.f), glm::vec2(32767.f));
123 glm::i16vec2 q[4] = {
124 glm::i16vec2(std::floor(enc.x), floor(enc.y)),
125 glm::i16vec2(std::floor(enc.x), ceil(enc.y)),
126 glm::i16vec2(std::ceil(enc.x), floor(enc.y)),
127 glm::i16vec2(std::ceil(enc.x), ceil(enc.y)),
128 };
129 size_t best = 0;
130 float error = 1.f - std::abs(dot(n, octDecode((1.f / 32767.f) * glm::vec2(q[0]))));
131 for (size_t i = 1; i < 4; i++) {
132 float e = 1.f - std::abs(dot(n, octDecode((1.f / 32767.f) * glm::vec2(q[i]))));
133 if (e < error) {
134 best = i;
135 error = e;
136 }
137 }
138 return q[best];
139#else
140 return glm::i16vec2(glm::clamp(glm::round(32767.f * octEncode(n)),
141 glm::vec2(-32767.f),
142 glm::vec2(32767.f)));
143#endif
144 }
145
146 glm::i8vec2 octEncodei8(const glm::vec3& n)
147 {
148#if 1
149 glm::vec2 enc = glm::clamp(127.f * octEncode(n), glm::vec2(-127.f), glm::vec2(127.f));
150 glm::i8vec2 q[4] = {
151 glm::i8vec2(std::floor(enc.x), floor(enc.y)),
152 glm::i8vec2(std::floor(enc.x), ceil(enc.y)),
153 glm::i8vec2(std::ceil(enc.x), floor(enc.y)),
154 glm::i8vec2(std::ceil(enc.x), ceil(enc.y)),
155 };
156
157 size_t best = 0;
158 float error = 1.f - std::abs(dot(n, octDecode((1.f / 127.f) * glm::vec2(q[0]))));
159 for (size_t i = 1; i < 4; i++) {
160 float e = 1.f - std::abs(dot(n, octDecode((1.f / 127.f) * glm::vec2(q[i]))));
161 if (e < error) {
162 best = i;
163 error = e;
164 }
165 }
166 return q[best];
167#else
168 return glm::i8vec2(glm::clamp(glm::round(127.f * octEncode(n)),
169 glm::vec2(-127.f),
170 glm::vec2(127.f)));
171#endif
172 }
173
174 enum struct PackFormat
175 {
176 Pos10un_Nrm8sn_Id16ui,
177 Pos16un_Nrm16sn_Id16ui,
178 Pos16un_Nrm8sn_Id32ui,
179 Pos16un_Nrm8sn_Id32f,
180 Count
181 };
182
183 namespace Pos10un_Nrm8sn_Id16ui {
184 struct Vtx
185 {
186 union {
187 struct {
188 unsigned pos_x : 10;
189 unsigned pos_y : 10;
190 unsigned pos_z : 10;
191 unsigned pos_w : 2;
192 };
193 uint32_t pos;
194 };
195 uint16_t tex;
196 glm::u8vec2 nrm;
197 };
198 static_assert(sizeof(Vtx) == 8);
199
200 const Cogs::VertexElement Fmt[] = {
201 {
202 .offset = uint16_t(offsetof(Vtx, pos)),
203 .format = Cogs::Format::R10G10B10A2_UNORM,
205 .semanticIndex = 0,
206 .inputType = Cogs::InputType::VertexData,
207 .instanceStep = 0
208 },
209 {
210 .offset = uint16_t(offsetof(Vtx, nrm)),
211 .format = Cogs::Format::R8G8_SNORM,
213 .semanticIndex = 0,
214 .inputType = Cogs::InputType::VertexData,
215 .instanceStep = 0
216 },
217 {
218 .offset = uint16_t(offsetof(Vtx, tex)),
219 .format = Cogs::Format::R16_UINT,
221 .semanticIndex = 0,
222 .inputType = Cogs::InputType::VertexData,
223 .instanceStep = 0
224 }
225 };
226 }
227
228 namespace Pos16un_Nrm16sn_Id16ui {
229 struct Vtx
230 {
231 glm::u16vec3 pos;
232 uint16_t tex;
233 glm::u16vec2 nrm;
234 };
235 static_assert(sizeof(Vtx) == 12);
236
237 const Cogs::VertexElement Fmt[] = {
238 {
239 .offset = uint16_t(offsetof(Vtx, pos)),
240 .format = Cogs::Format::R16G16B16_UNORM,
242 .semanticIndex = 0,
243 .inputType = Cogs::InputType::VertexData,
244 .instanceStep = 0
245 },
246 {
247 .offset = uint16_t(offsetof(Vtx, nrm)),
248 .format = Cogs::Format::R16G16_SNORM,
250 .semanticIndex = 0,
251 .inputType = Cogs::InputType::VertexData,
252 .instanceStep = 0
253 },
254 {
255 .offset = uint16_t(offsetof(Vtx, tex)),
256 .format = Cogs::Format::R16_UINT,
258 .semanticIndex = 0,
259 .inputType = Cogs::InputType::VertexData,
260 .instanceStep = 0
261 }
262 };
263 }
264
265 namespace Pos16un_Nrm8sn_Id32ui {
266 struct Vtx
267 {
268 glm::u16vec3 pos;
269 glm::u8vec2 nrm;
270 uint32_t tex;
271 };
272 static_assert(sizeof(Vtx) == 12);
273
274 const Cogs::VertexElement Fmt[] = {
275 {
276 .offset = uint16_t(offsetof(Vtx, pos)),
277 .format = Cogs::Format::R16G16B16_UNORM,
279 .semanticIndex = 0,
280 .inputType = Cogs::InputType::VertexData,
281 .instanceStep = 0
282 },
283 {
284 .offset = uint16_t(offsetof(Vtx, nrm)),
285 .format = Cogs::Format::R8G8_SNORM,
287 .semanticIndex = 0,
288 .inputType = Cogs::InputType::VertexData,
289 .instanceStep = 0
290 },
291 {
292 .offset = uint16_t(offsetof(Vtx, tex)),
293 .format = Cogs::Format::R32_UINT,
295 .semanticIndex = 0,
296 .inputType = Cogs::InputType::VertexData,
297 .instanceStep = 0
298 }
299 };
300 }
301
302 namespace Pos16un_Nrm8sn_Id32f {
303 struct Vtx
304 {
305 glm::u16vec3 pos;
306 glm::u8vec2 nrm;
307 float tex;
308 };
309 static_assert(sizeof(Vtx) == 12);
310
311 const Cogs::VertexElement Fmt[] = {
312 {
313 .offset = uint16_t(offsetof(Vtx, pos)),
314 .format = Cogs::Format::R16G16B16_UNORM,
316 .semanticIndex = 0,
317 .inputType = Cogs::InputType::VertexData,
318 .instanceStep = 0
319 },
320 {
321 .offset = uint16_t(offsetof(Vtx, nrm)),
322 .format = Cogs::Format::R8G8_SNORM,
324 .semanticIndex = 0,
325 .inputType = Cogs::InputType::VertexData,
326 .instanceStep = 0
327 },
328 {
329 .offset = uint16_t(offsetof(Vtx, tex)),
330 .format = Cogs::Format::R32_FLOAT,
332 .semanticIndex = 0,
333 .inputType = Cogs::InputType::VertexData,
334 .instanceStep = 0
335 }
336 };
337 }
338
339 namespace Pos16un_Nrm8sn {
340 struct Vtx
341 {
342 glm::u16vec3 pos;
343 glm::u8vec2 nrm;
344 };
345 static_assert(sizeof(Vtx) == 8);
346
347 const Cogs::VertexElement Fmt[2] = {
348 {
349 .offset = uint16_t(offsetof(Vtx, pos)),
350 .format = Cogs::Format::R16G16B16_UNORM,
352 .semanticIndex = 0,
353 .inputType = Cogs::InputType::VertexData,
354 .instanceStep = 0
355 },
356 {
357 .offset = uint16_t(offsetof(Vtx, nrm)),
358 .format = Cogs::Format::R8G8_SNORM,
360 .semanticIndex = 0,
361 .inputType = Cogs::InputType::VertexData,
362 .instanceStep = 0
363 }
364 };
365 }
366
367 namespace Id16ui {
368 const Cogs::VertexElement Fmt[] = {
369 {
370 .offset = 0,
371 .format = Cogs::Format::R16_UINT,
373 .semanticIndex = 0,
374 .inputType = Cogs::InputType::VertexData,
375 .instanceStep = 0
376 }
377 };
378 }
379
380 namespace Id32ui {
381 const Cogs::VertexElement Fmt[] = {
382 {
383 .offset = 0,
384 .format = Cogs::Format::R32_UINT,
386 .semanticIndex = 0,
387 .inputType = Cogs::InputType::VertexData,
388 .instanceStep = 0
389 }
390 };
391 }
392
393 namespace Id32f {
394 const Cogs::VertexElement Fmt[] = {
395 {
396 .offset = 0,
397 .format = Cogs::Format::R32_FLOAT,
399 .semanticIndex = 0,
400 .inputType = Cogs::InputType::VertexData,
401 .instanceStep = 0
402 }
403 };
404 }
405
406 namespace Id32ui_Inst {
407 const Cogs::VertexElement Fmt[] = {
408 {
409 .offset = 0,
410 .format = Cogs::Format::R32_UINT,
412 .semanticIndex = 1,
414 .instanceStep = 1
415 },
416 };
417 }
418
419 void findUniqueVertices(Cogs::Memory::MemoryBuffer& uniqueVertexData, std::vector<uint32_t>& uniqueVertexMap, const Cogs::Memory::MemoryBuffer& inVertexData, const size_t stride)
420 {
421 if (inVertexData.empty()) return;
422
423 const char* inVertices = (const char*)inVertexData.data();
424 const size_t inVertexCount = inVertexData.size() / stride;
425
426 std::vector<uint32_t> permutation(inVertexCount);
427 for (uint32_t i = 0; i < inVertexCount; i++) {
428 permutation[i] = i;
429 }
430 std::sort(permutation.begin(), permutation.end(),
431 [inVertices, stride](const uint32_t& a, const uint32_t& b) -> bool
432 {
433 return std::memcmp(&inVertices[stride * a], &inVertices[stride * b], stride) < 0;
434 });
435
436 uniqueVertexData.resize(inVertexData.size());
437 char* uniqueVertices = (char*)uniqueVertexData.data();
438 size_t currentUniqueVertex = 0;
439
440 uniqueVertexMap.resize(inVertexCount);
441 uniqueVertexMap[permutation[0]] = uint32_t(currentUniqueVertex);
442 std::memcpy(&uniqueVertices[stride * currentUniqueVertex], &inVertices[stride * permutation[0]], stride);
443
444 for (size_t i = 1; i < inVertexCount; i++) {
445 if (std::memcmp(&uniqueVertices[stride * currentUniqueVertex], &inVertices[stride * permutation[i]], stride) != 0) {
446 std::memcpy(&uniqueVertices[stride * (++currentUniqueVertex)], &inVertices[stride * permutation[i]], stride);
447 }
448 uniqueVertexMap[permutation[i]] = uint32_t(currentUniqueVertex);
449 }
450
451 const size_t uniqueVertexCount = currentUniqueVertex + 1;
452
453 assert(uniqueVertexCount <= inVertexCount);
454 uniqueVertexData.resize(stride * uniqueVertexCount);
455 }
456
457 bool buildPackedVertices(Cogs::VertexFormatHandle& fmtHandle,
458 Cogs::Memory::MemoryBuffer& uniqueVertexData,
459 std::vector<uint32_t>& uniqueVertexMap,
460 const PackFormat packFmt,
464 const size_t vertexCount,
465 const glm::vec3& posMin,
466 const float invScale,
467 const uint32_t idOffset)
468 {
470
471 size_t fmtSize = 0;
472
473 float posFormatScale = 0.f;
474 switch (packFmt) {
475
476 case PackFormat::Pos10un_Nrm8sn_Id16ui: {
477 fmtHandle = Cogs::VertexFormats::createVertexFormat(Pos10un_Nrm8sn_Id16ui::Fmt, std::size(Pos10un_Nrm8sn_Id16ui::Fmt));
478 fmtSize = Cogs::getSize(fmtHandle);
479 assert(sizeof(Pos10un_Nrm8sn_Id16ui::Vtx) == fmtSize);
480 posFormatScale = 1023.f;
481
482 vertexData.resize(fmtSize * vertexCount, false);
483 Pos10un_Nrm8sn_Id16ui::Vtx* vtx = (Pos10un_Nrm8sn_Id16ui::Vtx*)vertexData.data();
484 for (size_t i = 0; i < vertexCount; i++) {
485 glm::vec3 p = glm::clamp(glm::round(posFormatScale * invScale * (srcPos[i] - posMin)), glm::vec3(0.f), glm::vec3(posFormatScale));
486 vtx[i].pos_x = uint32_t(p.x);
487 vtx[i].pos_y = uint32_t(p.y);
488 vtx[i].pos_z = uint32_t(p.z);
489 vtx[i].pos_w = 3u;
490 vtx[i].nrm = octEncodei8(glm::normalize(srcNrm[i]));
491 vtx[i].tex = uint16_t(uint32_t(std::max(0.f, srcTex[i].x)) - idOffset);
492 }
493
494 findUniqueVertices(uniqueVertexData, uniqueVertexMap, vertexData, fmtSize);
495 break;
496 }
497
498 case PackFormat::Pos16un_Nrm16sn_Id16ui: {
499 fmtHandle = Cogs::VertexFormats::createVertexFormat(Pos16un_Nrm16sn_Id16ui::Fmt, std::size(Pos16un_Nrm16sn_Id16ui::Fmt));
500 fmtSize = Cogs::getSize(fmtHandle);
501 assert(sizeof(Pos16un_Nrm16sn_Id16ui::Vtx) == fmtSize);
502 posFormatScale = std::numeric_limits<uint16_t>::max();
503
504 vertexData.resize(fmtSize * vertexCount, false);
505 Pos16un_Nrm16sn_Id16ui::Vtx* vtx = (Pos16un_Nrm16sn_Id16ui::Vtx*)vertexData.data();
506 for (size_t i = 0; i < vertexCount; i++) {
507 glm::vec3 p = glm::clamp(glm::round(posFormatScale * invScale * (srcPos[i] - posMin)), glm::vec3(0.f), glm::vec3(posFormatScale));
508 vtx[i].pos = glm::u16vec3(p);
509 vtx[i].nrm = octEncodei16(glm::normalize(srcNrm[i]));
510 vtx[i].tex = uint16_t(uint32_t(std::max(0.f, srcTex[i].x)) - idOffset);
511 }
512
513 findUniqueVertices(uniqueVertexData, uniqueVertexMap, vertexData, fmtSize);
514 break;
515 }
516
517 case PackFormat::Pos16un_Nrm8sn_Id32ui: {
518 fmtHandle = Cogs::VertexFormats::createVertexFormat(Pos16un_Nrm8sn_Id32ui::Fmt, std::size(Pos16un_Nrm8sn_Id32ui::Fmt));
519 fmtSize = Cogs::getSize(fmtHandle);
520 assert(sizeof(Pos16un_Nrm8sn_Id32ui::Vtx) == fmtSize);
521 posFormatScale = std::numeric_limits<uint16_t>::max();
522
523 vertexData.resize(fmtSize * vertexCount, false);
524 Pos16un_Nrm8sn_Id32ui::Vtx* vtx = (Pos16un_Nrm8sn_Id32ui::Vtx*)vertexData.data();
525 for (size_t i = 0; i < vertexCount; i++) {
526 glm::vec3 p = glm::clamp(glm::round(posFormatScale * invScale * (srcPos[i] - posMin)), glm::vec3(0.f), glm::vec3(posFormatScale));
527 vtx[i].pos = glm::u16vec3(p);
528 vtx[i].nrm = octEncodei8(glm::normalize(srcNrm[i]));
529 vtx[i].tex = uint32_t(std::max(0.f, srcTex[i].x)) - idOffset;
530 }
531
532 findUniqueVertices(uniqueVertexData, uniqueVertexMap, vertexData, fmtSize);
533 break;
534 }
535
536 case PackFormat::Pos16un_Nrm8sn_Id32f: {
537 fmtHandle = Cogs::VertexFormats::createVertexFormat(Pos16un_Nrm8sn_Id32f::Fmt, std::size(Pos16un_Nrm8sn_Id32f::Fmt));
538 fmtSize = Cogs::getSize(fmtHandle);
539 assert(sizeof(Pos16un_Nrm8sn_Id32f::Vtx) == fmtSize);
540 posFormatScale = std::numeric_limits<uint16_t>::max();
541
542 vertexData.resize(fmtSize * vertexCount, false);
543 Pos16un_Nrm8sn_Id32f::Vtx* vtx = (Pos16un_Nrm8sn_Id32f::Vtx*)vertexData.data();
544 for (size_t i = 0; i < vertexCount; i++) {
545 glm::vec3 p = glm::clamp(glm::round(posFormatScale * invScale * (srcPos[i] - posMin)), glm::vec3(0.f), glm::vec3(posFormatScale));
546 vtx[i].pos = glm::u16vec3(p);
547 vtx[i].nrm = octEncodei8(glm::normalize(srcNrm[i]));
548 vtx[i].tex = float(uint32_t(std::max(0.f, srcTex[i].x)) - idOffset);
549 }
550
551 findUniqueVertices(uniqueVertexData, uniqueVertexMap, vertexData, fmtSize);
552 break;
553 }
554
555 default:
556 assert(false && "Invalid enum");
557 return false;
558 }
559
560 return true;
561 }
562
563 bool buildIndices(std::vector<uint32_t>& indices, const std::vector<uint32_t>& uniqueVertexMap, const Mesh* srcMesh)
564 {
565 // Build indexed representation
566 if (srcMesh->isIndexed()) {
567 const DataStream& indexStream = srcMesh->getStream(VertexDataType::Indexes);
568
569 indices.reserve(indexStream.numElements);
570 switch (indexStream.stride)
571 {
572 case 2: {
573 const uint16_t* srcIndices = (const uint16_t*)indexStream.data();
574 for (size_t i = 0; i + 2 < indexStream.numElements; i += 3) {
575 uint32_t a = uniqueVertexMap[srcIndices[i + 0]];
576 uint32_t b = uniqueVertexMap[srcIndices[i + 1]];
577 uint32_t c = uniqueVertexMap[srcIndices[i + 2]];
578 if (a != b && b != c && a != c) {
579 indices.emplace_back(a);
580 indices.emplace_back(b);
581 indices.emplace_back(c);
582 }
583 }
584 break;
585 }
586 case 4: {
587 const uint32_t* srcIndices = (const uint32_t*)indexStream.data();
588 for (size_t i = 0; i + 2 < indexStream.numElements; i += 3) {
589 uint32_t a = uniqueVertexMap[srcIndices[i + 0]];
590 uint32_t b = uniqueVertexMap[srcIndices[i + 1]];
591 uint32_t c = uniqueVertexMap[srcIndices[i + 2]];
592 if (a != b && b != c && a != c) {
593 indices.emplace_back(a);
594 indices.emplace_back(b);
595 indices.emplace_back(c);
596 }
597 }
598 break;
599 }
600 default:
601 assert(false && "Illegal index size");
602 return false;
603 }
604 }
605 else {
606 size_t inVertexCount = uniqueVertexMap.size();
607 indices.reserve(inVertexCount);
608 for (size_t i = 0; i + 2 < inVertexCount; i += 3) {
609 uint32_t a = uniqueVertexMap[i + 0];
610 uint32_t b = uniqueVertexMap[i + 1];
611 uint32_t c = uniqueVertexMap[i + 2];
612 if (a != b && b != c && a != c) {
613 indices.emplace_back(a);
614 indices.emplace_back(b);
615 indices.emplace_back(c);
616 }
617 }
618 }
619
620 return true;
621 }
622
623
624 MeshHandle buildIndexedMesh(Context* context,
625 const Mesh* srcMesh,
626 const Cogs::Geometry::BoundingBox& bbox,
627 const Cogs::VertexFormatHandle& posFmtHandle,
628 const Cogs::Memory::MemoryBuffer& posVertexData,
629 const Cogs::VertexFormatHandle& idsFmtHandle,
630 const Cogs::Memory::MemoryBuffer& idsVertexData,
631 const size_t vertexCount,
632 const std::vector<uint32_t>& indexData,
633 const uint32_t dstIndexTypeSize,
634 const uint32_t idOffset)
635 {
636 MeshManager::ResourceProxy dstMesh = context->meshManager->createLocked();
637 dstMesh->primitiveType = srcMesh->primitiveType;
638
639 // Position, normal and maybe id goes into Interleaved0 stream.
640 const size_t posFmtSize = Cogs::getSize(posFmtHandle);
641 assert(posVertexData.size() == posFmtSize * vertexCount);
642 if (uint8_t* dst = dstMesh->mapStream(VertexDataType::Interleaved0, posFmtHandle, 0, vertexCount, posFmtSize, true); dst != nullptr) {
643 std::memcpy(dst, posVertexData.data(), posVertexData.size());
644 dstMesh->unmap(VertexDataType::Interleaved0);
645 }
646 else {
648 }
649
650 // We have a separate ids tream, store it in TexCoord0 stream
651 if (idsFmtHandle) {
652 const size_t idsFmtSize = Cogs::getSize(idsFmtHandle);
653 assert(idsVertexData.size() == idsFmtSize * vertexCount);
654 if (uint8_t* dst = dstMesh->mapStream(VertexDataType::TexCoords0, idsFmtHandle, 0, vertexCount, idsFmtSize, true); dst != nullptr) {
655 std::memcpy(dst, idsVertexData.data(), idsVertexData.size());
656 dstMesh->unmap(VertexDataType::Interleaved0);
657 }
658 else {
660 }
661 }
662
663 // If we have an offset to the ids, put it texcoord1
664 if (idOffset) {
665 Cogs::VertexFormatHandle instanceFmtHandle = Cogs::VertexFormats::createVertexFormat(Id32ui_Inst::Fmt, std::size(Id32ui_Inst::Fmt));
666 if (uint8_t* dst = dstMesh->mapStream(VertexDataType::TexCoords1, instanceFmtHandle, 0, 1, Cogs::getSize(instanceFmtHandle), true); dst != nullptr) {
667 std::memcpy(dst, &idOffset, sizeof(idOffset));
668 dstMesh->unmap(VertexDataType::TexCoords1);
669 }
670 else {
672 }
673 }
674
675 // Store indices
676 const size_t indexCount = indexData.size();
677
678 if (uint8_t* ptr = dstMesh->mapStream(VertexDataType::Indexes, 0, indexCount, dstIndexTypeSize, true); ptr != nullptr) {
679 switch (dstIndexTypeSize) {
680 case 2: {
681 uint16_t* dstIndices = (uint16_t*)ptr;
682 for (size_t i = 0; i < indexCount; i++) {
683 dstIndices[i] = uint16_t(indexData[i]);
684 }
685 break;
686 }
687 case 4:
688 std::memcpy(ptr, indexData.data(), sizeof(uint32_t) * indexCount);
689 break;
690 default:
691 assert(false);
693 }
694 dstMesh->unmap(VertexDataType::Indexes);
695 dstMesh->setMeshFlag(MeshFlags::Indexed);
696 dstMesh->setMeshFlag(MeshFlags::IndexesChanged);
697 dstMesh->setCount(indexCount);
698 }
699 else {
701 }
702
703 dstMesh->setBounds(bbox);
704 return dstMesh.getHandle();
705 }
706
707 MeshHandle buildUnindexedMesh(Context* context,
708 const Mesh* srcMesh,
709 const Cogs::Geometry::BoundingBox& bbox,
710 Cogs::VertexFormatHandle& posFmtHandle,
711 const Cogs::Memory::MemoryBuffer& posVertexData,
712 const Cogs::VertexFormatHandle& idsFmtHandle,
713 const Cogs::Memory::MemoryBuffer& idsVertexData,
714 const size_t vertexCount,
715 const std::vector<uint32_t>& indexData,
716 const uint32_t idOffset)
717 {
718 const size_t indexCount = indexData.size();
719
720 MeshManager::ResourceProxy dstMesh = context->meshManager->createLocked();
721 dstMesh->primitiveType = srcMesh->primitiveType;
722
723 // Position, normal and maybe id goes into Interleaved0 stream.
724 const size_t posFmtSize = Cogs::getSize(posFmtHandle);
725 assert(posVertexData.size() == posFmtSize * vertexCount);
726 if (uint8_t* dst = dstMesh->mapStream(VertexDataType::Interleaved0, posFmtHandle, 0, indexCount, posFmtSize, true); dst != nullptr)
727 {
728 const uint8_t* src = static_cast<const uint8_t*>(posVertexData.data());
729 for (size_t i = 0; i < indexCount; i++) {
730 const size_t ix = indexData[i];
731 assert(ix < vertexCount);
732 std::memcpy(dst + posFmtSize * i, src + posFmtSize * ix, posFmtSize);
733 }
734 dstMesh->unmap(VertexDataType::Interleaved0);
735 }
736 else {
738 }
739
740 // We have a separate ids tream, store it in TexCoord0 stream
741 if (idsFmtHandle) {
742 const size_t idsFmtSize = Cogs::getSize(idsFmtHandle);
743 assert(idsVertexData.size() == idsFmtSize * vertexCount);
744 if (uint8_t* dst = dstMesh->mapStream(VertexDataType::TexCoords0, idsFmtHandle, 0, indexCount, idsFmtSize, true); dst != nullptr)
745 {
746 const uint8_t* src = static_cast<const uint8_t*>(idsVertexData.data());
747 for (size_t i = 0; i < indexCount; i++) {
748 const size_t ix = indexData[i];
749 assert(ix < vertexCount);
750 std::memcpy(dst + idsFmtSize * i, src + idsFmtSize * ix, idsFmtSize);
751 }
752 dstMesh->unmap(VertexDataType::TexCoords0);
753 }
754 else {
756 }
757 }
758
759 // If we have an offset to the ids, put it texcoord1
760 if (idOffset) {
761 Cogs::VertexFormatHandle instanceFmtHandle = Cogs::VertexFormats::createVertexFormat(Id32ui_Inst::Fmt, std::size(Id32ui_Inst::Fmt));
762 if (uint8_t* dst = dstMesh->mapStream(VertexDataType::TexCoords1, instanceFmtHandle, 0, 1, Cogs::getSize(instanceFmtHandle), true); dst != nullptr) {
763 std::memcpy(dst, &idOffset, sizeof(idOffset));
764 dstMesh->unmap(VertexDataType::TexCoords1);
765 }
766 else {
768 }
769 }
770
771 dstMesh->setBounds(bbox);
772 return dstMesh.getHandle();
773 }
774}
775
776bool Cogs::Core::PackMeshCommand::pack(Context* context, std::string& status, glm::mat4& transform, MeshHandle& h, const Options& options)
777{
778 MeshHandle src = h;
779
780 Mesh* srcMesh = src.resolve();
781 if (srcMesh == nullptr) {
782 status = "Empty mesh";
783 return false;
784 }
785
786 size_t srcVertexCount = 0;
787
788 // -- Verify and map positions
789 if (srcMesh->hasStream(VertexDataType::Positions)) {
790 const DataStream& dataStream = srcMesh->getStream(VertexDataType::Positions);
791 srcVertexCount = dataStream.numElements;
792 }
793 else {
794 status = "Mesh does not have positions stream.";
795 return false;
796 }
797 MappedStreamReadOnly<glm::vec3> srcPos = srcMesh->mapPositionsReadOnly(0, srcVertexCount);
798 if (!srcPos.isValid()) {
799 status = "Failed to map mesh positions as float3.";
800 return false;
801 }
802
803 // -- Verify and map normals
804 if (srcMesh->hasStream(VertexDataType::Normals)) {
805 const DataStream& srcNrmStream = srcMesh->getStream(VertexDataType::Normals);
806 if (srcNrmStream.numElements < srcVertexCount) {
807 status = "Mesh has less normals than positions.";
808 return false;
809 }
810 }
811 else {
812 status = "Mesh does not have normals stream, ignoring";
813 return false;
814 }
815 MappedStreamReadOnly<glm::vec3> srcNrm = srcMesh->mapNormalsReadOnly(0, srcVertexCount);
816 if (!srcNrm.isValid()) {
817 status = "Failed to map mesh normals as float3.";
818 return false;
819 }
820
821 // -- Verify and map texcoords
822 if (srcMesh->hasStream(VertexDataType::TexCoords0)) {
823 const DataStream& dataStream = srcMesh->getStream(VertexDataType::TexCoords0);
824 if (dataStream.numElements < srcVertexCount) {
825 status = "Mesh has less texcoords than positions.";
826 return false;
827 }
828 }
829 else {
830 status = "Mesh does not have texcoords stream, ignoring";
831 return false;
832 }
833 MappedStreamReadOnly<glm::vec2> srcTex = srcMesh->mapTexCoordsReadOnly(0, srcVertexCount);
834 if (!srcTex.isValid()) {
835 status = "Failed to map mesh normals as float3, ignoring.";
836 return false;
837 }
838
839 // Calculate precise bounds
840 glm::vec3 posMin(std::numeric_limits<float>::max());
841 glm::vec3 posMax(-std::numeric_limits<float>::max());
842 for (size_t i = 0; i < srcVertexCount; i++) {
843 posMin = glm::min(posMin, srcPos[i]);
844 posMax = glm::max(posMax, srcPos[i]);
845 }
846 glm::vec3 extent = posMax - posMin;
847 float scale = std::max(std::max(extent.x, extent.y), extent.z);
848 if (srcVertexCount == 0) {
849 LOG_WARNING(logger, "No vertices - skipping scaling");
850 scale = 1.0f;
851 }
852 else if (scale <= 0.0f) {
853 LOG_WARNING(logger, "Zero extent(%f) - skipping scaling", scale);
854 scale = 1.0f;
855 }
856
857 const float invScale = 1.f / scale;
858
859 // Figure out how many bits needed for id. Negative ids is encoded as zero.
860 uint32_t idMax = 0;
861 uint32_t idMin = 0;
862 if (0 < srcVertexCount) {
863 idMin = std::numeric_limits<uint32_t>::max();
864 for (size_t i = 0; i < srcVertexCount; i++) {
865 uint32_t id = uint32_t(std::max(0.f, srcTex[i].x));
866 idMin = std::min(idMin, id);
867 idMax = std::max(idMax, id);
868 }
869 }
870
871 // Choose packing format
872 uint32_t idOffset = 0;
873 PackFormat packFormat;
874 switch (options.target)
875 {
876 case Target::WebGL1_Low:
877 packFormat = PackFormat::Pos16un_Nrm8sn_Id32f;
878 break;
879
880 case Target::WebGL2_Low:
881 // Use id offset if allowed and it maks sense
882 if (options.allowIdOffset && (0xFFFFu <= idMax) && ((idMax - idMin) < 0xFFFFu)) {
883 idOffset = idMin;
884 }
885
886 // If we are not splitting out ids later, and it is possible, use 16 bits for ids.
887 if (((idMax - idOffset) < 0xFFFFu)) {
888 // pos + normal is 6 bytes, but webgl cannot represent this format, it has to be 4 or 8 bytes, so we can just use padding for id.
889 packFormat = PackFormat::Pos10un_Nrm8sn_Id16ui;
890 }
891 else {
892 // If Id32 gets stripped out, so we are left with Pos16Un_Nrm8sn, which is 8 bytes.
893 packFormat = PackFormat::Pos16un_Nrm8sn_Id32ui;
894 }
895 break;
896
897 case Target::WebGL2_Med:
898
899 // Use id offset if allowed and it makes sense
900 if (options.allowIdOffset && (0xFFFFu <= idMax) && ((idMax - idMin) < 0xFFFFu)) {
901 idOffset = idMin;
902 }
903
904 // If we are not splitting out ids later, and it is possible, use 16 bits for ids.
905 if (!options.allowSeparateIdStream && ((idMax - idOffset) < 0xFFFFu)) {
906 packFormat = PackFormat::Pos16un_Nrm16sn_Id16ui;
907 }
908 else {
909 packFormat = PackFormat::Pos16un_Nrm8sn_Id32ui;
910 }
911 break;
912
913 default:
914 assert(false && "Invalid enum");
915 return false;
916 }
917
919
920 std::vector<uint32_t> remapping;
922
923 VertexFormatHandle posFmtHandle;
924 if (!buildPackedVertices(posFmtHandle,
925 posData,
926 remapping,
927 packFormat,
928 srcPos, srcNrm, srcTex, srcVertexCount, posMin, invScale, idOffset)) return false;
929
930 size_t posFmtSize = Cogs::getSize(posFmtHandle);
931 const size_t vertexCount = posData.size() / posFmtSize;
932
933 std::vector<uint32_t> indices;
934 if (!buildIndices(indices, remapping, srcMesh)) return false;
935 const size_t indexCount = indices.size();
936
937 // Reorder indices to reduce the number of GPU vertex shader invocations
938 if (options.optimizeTriangleOrder) {
939 std::vector<uint32_t> indices2(indexCount);
940 meshopt_optimizeVertexCache(indices2.data(), indices.data(), indexCount, vertexCount);
941 indices.swap(indices2);
942 }
943
944 // Reorder vertices to optimize for vertex fetch cache
945 if (options.optimizeVertexOrder) {
946 tmp.resize(posData.size(), false);
947 meshopt_optimizeVertexFetch(tmp.data(), indices.data(), indexCount, posData.data(), vertexCount, posFmtSize);
948 posData.swap(tmp);
949 }
950
951 // Move id/texcoord into its own stream where reasonable.
952 size_t interleavedIdsOffset = 0; // Offset of id into the interleaved vertex format.
953 VertexFormatHandle newPosFmtHandle;
954 VertexFormatHandle idsFmtHandle;
955 if (options.allowSeparateIdStream) {
956 switch (packFormat) {
957 case PackFormat::Pos10un_Nrm8sn_Id16ui:
958 case PackFormat::Pos16un_Nrm16sn_Id16ui:
959 break;
960
961 case PackFormat::Pos16un_Nrm8sn_Id32ui:
962 interleavedIdsOffset = offsetof(Pos16un_Nrm8sn_Id32ui::Vtx, tex);
963 newPosFmtHandle = Cogs::VertexFormats::createVertexFormat(Pos16un_Nrm8sn::Fmt, std::size(Pos16un_Nrm8sn::Fmt));
964 if (idMax - idOffset < 0xFFFFu) {
965 idsFmtHandle = Cogs::VertexFormats::createVertexFormat(Id16ui::Fmt, std::size(Id16ui::Fmt));
966 }
967 else {
968 idsFmtHandle = Cogs::VertexFormats::createVertexFormat(Id32ui::Fmt, std::size(Id32ui::Fmt));
969 }
970 break;
971
972 case PackFormat::Pos16un_Nrm8sn_Id32f:
973 interleavedIdsOffset = offsetof(Pos16un_Nrm8sn_Id32f::Vtx, tex);
974 newPosFmtHandle = Cogs::VertexFormats::createVertexFormat(Pos16un_Nrm8sn::Fmt, std::size(Pos16un_Nrm8sn::Fmt));
975 idsFmtHandle = Cogs::VertexFormats::createVertexFormat(Id32f::Fmt, std::size(Id32f::Fmt));
976 break;
977
978 default:
979 assert(false && "Invalid enum value");
980 break;
981 }
982 }
983
984 // If we are to split out ids into its own stream, we have a new position format and a id format
985 size_t idsFmtSize = 0;
987 if (newPosFmtHandle && idsFmtHandle) {
988 const size_t newPosFmtSize = Cogs::getSize(newPosFmtHandle);
989 idsFmtSize = Cogs::getSize(idsFmtHandle);
990
991 tmp.resize(newPosFmtSize * vertexCount, false);
992 idsData.resize(idsFmtSize * vertexCount, false);
993
994 char* newPosDataPtr = static_cast<char*>(tmp.data());
995 char* idsDataPtr = static_cast<char*>(idsData.data());
996 const char* posDataPtr = static_cast<char*>(posData.data());
997 for (size_t i = 0; i < vertexCount; i++) {
998 std::memcpy(newPosDataPtr + newPosFmtSize * i, posDataPtr + posFmtSize * i, newPosFmtSize);
999 std::memcpy(idsDataPtr + idsFmtSize * i, posDataPtr + posFmtSize * i + interleavedIdsOffset, idsFmtSize);
1000 }
1001
1002 // Swap in new pos/normal data without id
1003 posData.swap(tmp);
1004 posFmtHandle = newPosFmtHandle;
1005 posFmtSize = newPosFmtSize;
1006 }
1007
1008 const uint32_t indexTypeSize = vertexCount < 0xffffu ? 2 : 4;
1009 const size_t indexedSize = (posFmtSize + idsFmtSize) * vertexCount + indexTypeSize * indexCount;
1010 const size_t unindexedSize = (posFmtSize + idsFmtSize) * indexCount;
1011
1012 bool indexed = indexedSize < unindexedSize;
1013 //LOG_DEBUG(logger, "fmt=%s indexed size: %zu, unindexed size: %zu", PackFormatName[size_t(packFormat)], indexedSize, unindexedSize);
1014
1015 const Cogs::Geometry::BoundingBox bbox{ glm::vec3(0.f), invScale * extent };
1016 if (indexed) {
1017 h = buildIndexedMesh(context, srcMesh, bbox, posFmtHandle, posData, idsFmtHandle, idsData, vertexCount, indices, indexTypeSize, idOffset);
1018 }
1019 else {
1020 h = buildUnindexedMesh(context, srcMesh, bbox, posFmtHandle, posData, idsFmtHandle, idsData, vertexCount, indices, idOffset);
1021 }
1022
1023 glm::vec3 shift = posMin;
1024 transform = glm::mat4(scale, 0.f, 0.f, 0.f,
1025 0.f, scale, 0.f, 0.f,
1026 0.f, 0.f, scale, 0.f,
1027 shift.x, shift.y, shift.z, 1.f);
1028
1029 return true;
1030}
ComponentType * getComponent() const
Definition: Component.h:159
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
class EntityStore * store
Entity store.
Definition: Context.h:231
EntityPtr getEntity(const StringView &name, bool logIfNotFound=true) const
Retrieve a reference to the shared entity pointer to the Entity with the given name.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
Definition: MeshComponent.h:15
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Definition: MeshComponent.h:29
ResourceProxy< Mesh, ResourceManager > ResourceProxy
Type of resource proxy objects, specialized on the type of resource.
Contains information on how the entity behaves in the scene.
std::vector< EntityPtr > children
Contains all child entities owned by this component.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
Definition: EntityPtr.h:12
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ InstanceData
Per instance data.
@ VertexData
Per vertex data.
@ Position
Position semantic.
@ Normal
Normal semantic.
@ TextureCoordinate
Texture coordinate semantic.
Contains a stream of data used by Mesh resources.
Definition: Mesh.h:80
uint32_t numElements
Number of elements of the type given by format contained in data.
Definition: Mesh.h:108
uint32_t stride
Element stride.
Definition: Mesh.h:105
Base class for Cogs Editor commands.
Definition: EditorCommand.h:19
Wrapper for read-only access to mapped stream data.
Definition: Mesh.h:197
bool isValid() const
Returns true if stream mapping successful.
Definition: Mesh.h:212
@ IndexesChanged
The index data of the mesh changed.
Definition: Mesh.h:61
@ Indexed
The mesh should be drawn indexed, using index data to order the triangle vertexes.
Definition: Mesh.h:65
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
Definition: Mesh.h:265
bool isIndexed() const
If the mesh uses indexed geometry.
Definition: Mesh.h:953
bool hasStream(VertexDataType::EVertexDataType type) const
Check if the Mesh has a DataStream for the given type.
Definition: Mesh.h:933
MappedStreamReadOnly< glm::vec3 > mapNormalsReadOnly(const size_t start, const size_t end)
Map the normal stream for read access, range between start and end.
Definition: Mesh.h:438
DataStream & getStream(const VertexDataType::EVertexDataType dataType)
Get the stream corresponding to the given dataType.
Definition: Mesh.cpp:85
MappedStreamReadOnly< glm::vec3 > mapPositionsReadOnly(const size_t start, const size_t end)
Map the position stream for read access, range between start and end.
Definition: Mesh.h:382
MappedStreamReadOnly< glm::vec2 > mapTexCoordsReadOnly(const size_t start, const size_t end)
Map the texture coordinate stream for read access, range between start and end.
Definition: Mesh.h:550
void apply() override
Run the command.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
Definition: VertexFormat.h:38
uint16_t offset
Offset in bytes from the vertex position in memory.
Definition: VertexFormat.h:39