1#include "ModelWriter.h"
5#include "Components/Core/TransformComponent.h"
6#include "Components/Core/SceneComponent.h"
7#include "Components/Core/AnimationComponent.h"
8#include "Components/Core/MeshComponent.h"
9#include "Components/Core/MeshRenderComponent.h"
11#include "Systems/Core/TransformSystem.h"
12#include "Systems/Core/RenderSystem.h"
14#include "Resources/Buffer.h"
15#include "Resources/Mesh.h"
16#include "Resources/MaterialInstance.h"
17#include "Resources/Texture.h"
18#include "Resources/Model.h"
19#include "Resources/Animation.h"
21#include "Resources/DefaultMaterial.h"
23#include "Foundation/HashSequence.h"
24#include "Foundation/Logging/Logger.h"
25#include "Foundation/Platform/IO.h"
26#include "Foundation/Platform/Timer.h"
31#define STB_IMAGE_WRITE_IMPLEMENTATION
33#include "stb_image_write.h"
42#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
43#pragma optimize( "", off )
56 uint32_t nextIndex(T& t)
59 if (
static_cast<size_t>(std::numeric_limits<uint32_t>::max()) < ix) {
60 LOG_ERROR(logger,
"Index out of range. Model data too large.");
63 return static_cast<uint32_t
>(ix);
70 CogsBin3::SubSectionType type;
75 CogsBin3::SectionType type;
79 std::vector<uint8_t> contents;
80 std::vector<WSubSection> subSections;
83 struct WVertexFormatRec
92 uint32_t alignment = 0;
97 uint32_t numVertes = 0;
98 uint32_t numIndexes = 0;
101 WriteModelFlags flags = WriteModelFlags::NONE;
105 std::vector<std::unique_ptr<WSection>> sections;
107 std::vector<const TranslationKey*> animTrackTranslateSource;
108 std::vector<const RotationKey*> animTrackRotationSource;
109 std::vector<const ScaleKey*> animTrackScaleSource;
110 std::vector<WBufferContext> bufferContexts;
112 std::vector<uint8_t> stringData;
113 std::vector<uint8_t> propertyData;
114 std::vector<Cogs::Core::CogsBin3::Buffer> buffers;
115 std::vector<Cogs::Core::CogsBin3::Texture> textures;
116 std::vector<Cogs::Core::CogsBin3::String> strings;
117 std::vector<Cogs::Core::CogsBin3::Property> properties;
118 std::vector<Cogs::Core::CogsBin3::MaterialInstance> materialInstances;
119 std::vector<Cogs::Core::CogsBin3::Transform> transforms;
120 std::vector<Cogs::Geometry::BoundingBox> boundingBoxes;
121 std::vector<Cogs::Core::CogsBin3::Node> nodes;
122 std::vector<Cogs::Core::CogsBin3::Mesh> meshes;
123 std::vector<Cogs::Core::CogsBin3::VertexStream> vertexStreams;
124 std::vector<Cogs::Core::CogsBin3::VertexAttribute> vertexAttributes;
125 std::vector<Cogs::Core::CogsBin3::Bone> bones;
126 std::vector<Cogs::Core::CogsBin3::Skeleton> skeletons;
127 std::vector<Cogs::Core::CogsBin3::Model> models;
128 std::vector<Cogs::Core::CogsBin3::AnimTrack> animTracks;
129 std::vector<Cogs::Core::CogsBin3::AnimClip> animClips;
131 std::vector<CogsBin3::SectionInfo> sectionInfos;
133 std::map<const Cogs::Core::Texture*, uint32_t> knownTexturesByPointer;
134 std::map<const Cogs::Core::MaterialInstance*, uint32_t> knownMaterialInstancesByPointer;
135 std::map<size_t, uint32_t> knownMaterialInstancesByHash;
136 std::map<size_t, uint32_t> propByHash;
137 std::map<size_t, uint32_t> stringByHash;
138 std::map<size_t, uint32_t> transformByHash;
139 std::map<size_t, uint32_t> boundingBoxByHash;
140 std::map<const Cogs::Core::Mesh*, uint32_t> meshByPointer;
141 std::map<const Cogs::Core::BufferResource*, uint32_t> bufferByPointer;
142 std::map<size_t, WVertexFormatRec> vertexFormatByHash;
145 template<
typename T,
typename S>
146 T checkedCast(
const S v)
148 assert(v <= std::numeric_limits<T>::max());
152 inline uint64_t align(uint64_t x)
154 const uint64_t m = CogsBin3::CogsSectionAlignment - 1;
158 CogsBin3::PrimitiveType translatePrimitiveType(WriteContext& ,
Cogs::PrimitiveType primitiveType)
160 switch (primitiveType) {
170 case Cogs::PrimitiveType::ControlPoint1PatchList:
return CogsBin3::PrimitiveType::ControlPoint1PatchList;
break;
171 case Cogs::PrimitiveType::ControlPoint2PatchList:
return CogsBin3::PrimitiveType::ControlPoint2PatchList;
break;
172 case Cogs::PrimitiveType::ControlPoint3PatchList:
return CogsBin3::PrimitiveType::ControlPoint3PatchList;
break;
173 case Cogs::PrimitiveType::ControlPoint4PatchList:
return CogsBin3::PrimitiveType::ControlPoint4PatchList;
break;
177 assert(
false &&
"Illegal primitive type encountered");
178 return CogsBin3::PrimitiveType::TriangleList;
181 CogsBin3::Format translateFormat(Cogs::TextureFormat format)
184 case Cogs::TextureFormat::Unknown:
return CogsBin3::Format::Unknown;
break;
185 case Cogs::TextureFormat::R8_UNORM:
return CogsBin3::Format::R8_UNORM;
break;
186 case Cogs::TextureFormat::R8G8_UNORM:
return CogsBin3::Format::R8G8_UNORM;
break;
187 case Cogs::TextureFormat::R8G8B8_UNORM:
return CogsBin3::Format::R8G8B8_UNORM;
break;
188 case Cogs::TextureFormat::R8G8B8A8_UNORM:
return CogsBin3::Format::R8G8B8A8_UNORM;
break;
189 case Cogs::TextureFormat::R16_UNORM:
return CogsBin3::Format::R16_UNORM;
break;
190 case Cogs::TextureFormat::R16G16_UNORM:
return CogsBin3::Format::R16G16_UNORM;
break;
191 case Cogs::TextureFormat::R16G16B16_UNORM:
return CogsBin3::Format::R16G16B16_UNORM;
break;
192 case Cogs::TextureFormat::R16G16B16A16_UNORM:
return CogsBin3::Format::R16G16B16A16_UNORM;
break;
193 case Cogs::TextureFormat::R8_SNORM:
return CogsBin3::Format::R8_SNORM;
break;
194 case Cogs::TextureFormat::R8G8_SNORM:
return CogsBin3::Format::R8G8_SNORM;
break;
195 case Cogs::TextureFormat::R8G8B8_SNORM:
return CogsBin3::Format::R8G8B8_SNORM;
break;
196 case Cogs::TextureFormat::R8G8B8A8_SNORM:
return CogsBin3::Format::R8G8B8A8_SNORM;
break;
197 case Cogs::TextureFormat::R16_SNORM:
return CogsBin3::Format::R16_SNORM;
break;
198 case Cogs::TextureFormat::R16G16_SNORM:
return CogsBin3::Format::R16G16_SNORM;
break;
199 case Cogs::TextureFormat::R16G16B16_SNORM:
return CogsBin3::Format::R16G16B16_SNORM;
break;
200 case Cogs::TextureFormat::R16G16B16A16_SNORM:
return CogsBin3::Format::R16G16B16A16_SNORM;
break;
201 case Cogs::TextureFormat::R8_UINT:
return CogsBin3::Format::R8_UINT;
break;
202 case Cogs::TextureFormat::R8G8_UINT:
return CogsBin3::Format::R8G8_UINT;
break;
203 case Cogs::TextureFormat::R8G8B8_UINT:
return CogsBin3::Format::R8G8B8_UINT;
break;
204 case Cogs::TextureFormat::R8G8B8A8_UINT:
return CogsBin3::Format::R8G8B8A8_UINT;
break;
205 case Cogs::TextureFormat::R16_UINT:
return CogsBin3::Format::R16_UINT;
break;
206 case Cogs::TextureFormat::R16G16_UINT:
return CogsBin3::Format::R16G16_UINT;
break;
207 case Cogs::TextureFormat::R16G16B16_UINT:
return CogsBin3::Format::R16G16B16_UINT;
break;
208 case Cogs::TextureFormat::R16G16B16A16_UINT:
return CogsBin3::Format::R16G16B16A16_UINT;
break;
209 case Cogs::TextureFormat::R32_UINT:
return CogsBin3::Format::R32_UINT;
break;
210 case Cogs::TextureFormat::R32G32_UINT:
return CogsBin3::Format::R32G32_UINT;
break;
211 case Cogs::TextureFormat::R32G32B32_UINT:
return CogsBin3::Format::R32G32B32_UINT;
break;
212 case Cogs::TextureFormat::R32G32B32A32_UINT:
return CogsBin3::Format::R32G32B32A32_UINT;
break;
213 case Cogs::TextureFormat::R8_SINT:
return CogsBin3::Format::R8_SINT;
break;
214 case Cogs::TextureFormat::R8G8_SINT:
return CogsBin3::Format::R8G8_SINT;
break;
215 case Cogs::TextureFormat::R8G8B8_SINT:
return CogsBin3::Format::R8G8B8_SINT;
break;
216 case Cogs::TextureFormat::R8G8B8A8_SINT:
return CogsBin3::Format::R8G8B8A8_SINT;
break;
217 case Cogs::TextureFormat::R16_SINT:
return CogsBin3::Format::R16_SINT;
break;
218 case Cogs::TextureFormat::R16G16_SINT:
return CogsBin3::Format::R16G16_SINT;
break;
219 case Cogs::TextureFormat::R16G16B16_SINT:
return CogsBin3::Format::R16G16B16_SINT;
break;
220 case Cogs::TextureFormat::R16G16B16A16_SINT:
return CogsBin3::Format::R16G16B16A16_SINT;
break;
221 case Cogs::TextureFormat::R32_SINT:
return CogsBin3::Format::R32_SINT;
break;
222 case Cogs::TextureFormat::R32G32_SINT:
return CogsBin3::Format::R32G32_SINT;
break;
223 case Cogs::TextureFormat::R32G32B32_SINT:
return CogsBin3::Format::R32G32B32_SINT;
break;
224 case Cogs::TextureFormat::R32G32B32A32_SINT:
return CogsBin3::Format::R32G32B32A32_SINT;
break;
225 case Cogs::TextureFormat::R16_FLOAT:
return CogsBin3::Format::R16_FLOAT;
break;
226 case Cogs::TextureFormat::R16G16_FLOAT:
return CogsBin3::Format::R16G16_FLOAT;
break;
227 case Cogs::TextureFormat::R16G16B16_FLOAT:
return CogsBin3::Format::R16G16B16_FLOAT;
break;
228 case Cogs::TextureFormat::R16G16B16A16_FLOAT:
return CogsBin3::Format::R16G16B16A16_FLOAT;
break;
229 case Cogs::TextureFormat::R32_FLOAT:
return CogsBin3::Format::R32_FLOAT;
break;
230 case Cogs::TextureFormat::R32G32_FLOAT:
return CogsBin3::Format::R32G32_FLOAT;
break;
231 case Cogs::TextureFormat::R32G32B32_FLOAT:
return CogsBin3::Format::R32G32B32_FLOAT;
break;
232 case Cogs::TextureFormat::R32G32B32A32_FLOAT:
return CogsBin3::Format::R32G32B32A32_FLOAT;
break;
233 case Cogs::TextureFormat::D16_UNORM:
return CogsBin3::Format::D16_UNORM;
break;
234 case Cogs::TextureFormat::D24_UNORM:
return CogsBin3::Format::D24_UNORM;
break;
235 case Cogs::TextureFormat::D24S8_UNORM:
return CogsBin3::Format::D24S8_UNORM;
break;
236 case Cogs::TextureFormat::D32_FLOAT:
return CogsBin3::Format::D32_FLOAT;
break;
237 case Cogs::TextureFormat::R32_TYPELESS:
return CogsBin3::Format::R32_TYPELESS;
break;
238 case Cogs::TextureFormat::R16_TYPELESS:
return CogsBin3::Format::R16_TYPELESS;
break;
239 case Cogs::TextureFormat::R8T:
return CogsBin3::Format::R8T;
break;
240 case Cogs::TextureFormat::R8G8T:
return CogsBin3::Format::R8G8T;
break;
241 case Cogs::TextureFormat::R8G8B8T:
return CogsBin3::Format::R8G8B8T;
break;
242 case Cogs::TextureFormat::R8G8B8A8T:
return CogsBin3::Format::R8G8B8A8T;
break;
243 case Cogs::TextureFormat::B8G8R8:
return CogsBin3::Format::B8G8R8;
break;
244 case Cogs::TextureFormat::B8G8R8A8:
return CogsBin3::Format::B8G8R8A8;
break;
245 case Cogs::TextureFormat::A8_UNORM:
return CogsBin3::Format::A8_UNORM;
break;
246 case Cogs::TextureFormat::BC1_TYPELESS:
return CogsBin3::Format::BC1_TYPELESS;
break;
247 case Cogs::TextureFormat::BC1_UNORM:
return CogsBin3::Format::BC1_UNORM;
break;
248 case Cogs::TextureFormat::BC1_UNORM_SRGB:
return CogsBin3::Format::BC1_UNORM_SRGB;
break;
249 case Cogs::TextureFormat::BC2_TYPELESS:
return CogsBin3::Format::BC2_TYPELESS;
break;
250 case Cogs::TextureFormat::BC2_UNORM:
return CogsBin3::Format::BC2_UNORM;
break;
251 case Cogs::TextureFormat::BC2_UNORM_SRGB:
return CogsBin3::Format::BC2_UNORM_SRGB;
break;
252 case Cogs::TextureFormat::BC3_TYPELESS:
return CogsBin3::Format::BC3_TYPELESS;
break;
253 case Cogs::TextureFormat::BC3_UNORM:
return CogsBin3::Format::BC3_UNORM;
break;
254 case Cogs::TextureFormat::BC3_UNORM_SRGB:
return CogsBin3::Format::BC3_UNORM_SRGB;
break;
255 case Cogs::TextureFormat::BC4_TYPELESS:
return CogsBin3::Format::BC4_TYPELESS;
break;
256 case Cogs::TextureFormat::BC4_UNORM:
return CogsBin3::Format::BC4_UNORM;
break;
257 case Cogs::TextureFormat::BC4_SNORM:
return CogsBin3::Format::BC4_SNORM;
break;
258 case Cogs::TextureFormat::BC5_TYPELESS:
return CogsBin3::Format::BC5_TYPELESS;
break;
259 case Cogs::TextureFormat::BC5_UNORM:
return CogsBin3::Format::BC5_UNORM;
break;
260 case Cogs::TextureFormat::BC5_SNORM:
return CogsBin3::Format::BC5_SNORM;
break;
261 case Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB:
return CogsBin3::Format::R8G8B8A8_UNORM_SRGB;
break;
263 assert(
false &&
"Illegal format");
266 return CogsBin3::Format::Unknown;
269 WSection& getSection(WriteContext& wctx, CogsBin3::SectionType type, uint16_t index,
bool create=
true)
271 if (wctx.sections.size() <= index) wctx.sections.resize((
size_t)index + 1);
272 if (!wctx.sections[index]) {
273 assert(create &&
"Trying to retrieve non-existing section");
274 wctx.sections[index] = std::make_unique<WSection>(WSection{ type, index, 0, 0, {}, {} });
276 auto & section = *wctx.sections[index].get();
277 section.type = section.type | type;
282 uint32_t addMetaSubsection(WriteContext& , WSection& section, CogsBin3::SubSectionType type,
size_t size)
284 assert(section.contents.empty() &&
"Cannot add metasubsections after data payload has been added.");
285 assert(size < std::numeric_limits<uint32_t>::max());
287 auto index = nextIndex(section.subSections);
288 section.subSections.push_back(WSubSection{ ~0u, uint32_t(size), type });
292 uint32_t newNode(WriteContext& wctx)
294 uint32_t index = nextIndex(wctx.nodes);
295 auto & node = wctx.nodes.emplace_back();
299 node.materialInstance = ~0u;
300 node.transform = ~0u;
301 node.boundingBox = ~0u;
302 node.primitiveType = CogsBin3::PrimitiveType::TriangleList;
303 node.vertexFirst = 0;
304 node.vertexCount = 0;
308 uint32_t recordTransform(WriteContext& wctx,
const glm::mat4& M)
311 if (std::memcmp(glm::value_ptr(I), glm::value_ptr(M),
sizeof(M)) == 0) {
317 const auto * m = glm::value_ptr(M);
324 size_t hashValue = T.hash();
326 if (
auto it = wctx.transformByHash.find(hashValue); it != wctx.transformByHash.end()) {
327 if (std::memcmp(&wctx.transforms[it->second], &T,
sizeof(T)) == 0) {
333 auto index = nextIndex(wctx.transforms);
334 wctx.transforms.emplace_back(T);
335 wctx.transformByHash[hashValue] = index;
339 uint32_t recordBoundingBox(WriteContext& wctx,
const Cogs::Geometry::BoundingBox& bbox)
347 size_t hashValue = bbox.hash();
348 if (
auto it = wctx.boundingBoxByHash.find(hashValue); it != wctx.boundingBoxByHash.end()) {
349 if (std::memcmp(&wctx.boundingBoxes[it->second], &bbox,
sizeof(bbox)) == 0) {
350 LOG_DEBUG(logger,
"Found duplicate bounding box.");
355 auto index = nextIndex(wctx.boundingBoxes);
356 wctx.boundingBoxes.emplace_back(bbox);
357 wctx.transformByHash[hashValue] = index;
373 hashValue =
Cogs::hash(
reinterpret_cast<intptr_t
>(material));
380 if (
string.empty())
return ~0u;
382 auto hash =
string.hash();
383 if (
auto it = wctx.stringByHash.find(hash); it != wctx.stringByHash.end()) {
384 auto & str = wctx.strings[it->second];
385 if (
string ==
Cogs::StringView((
const char*)(wctx.stringData.data() + str.offset), str.length)) {
390 auto offset = wctx.stringData.size();
391 assert(offset < std::numeric_limits<uint32_t>::max());
393 auto length =
string.length();
394 assert(length < std::numeric_limits<uint32_t>::max());
396 auto index = nextIndex(wctx.strings);
397 wctx.strings.emplace_back(
CogsBin3::String{ uint32_t(offset), uint32_t(length) });
399 wctx.stringData.resize(offset + length);
400 std::memcpy(wctx.stringData.data() + offset,
string.begin(), length);
402 wctx.stringByHash[hash] = index;
408 assert(element.
offset <= std::numeric_limits<uint16_t>::max());
410 auto index = nextIndex(wctx.vertexAttributes);
413 auto & attribute = wctx.vertexAttributes.back();
415 attribute.offset = uint16_t(element.
offset);
416 attribute.format = (CogsBin3::Format)element.
format;
427 assert(
false &&
"Unknown element.format");
429 assert(element.
semanticIndex <= std::numeric_limits<uint8_t>::max());
433 attribute.instanceStepRate = 0;
436 attribute.instanceStepRate = uint16_t(std::min(1 + (uint32_t)element.
instanceStep,
unsigned(std::numeric_limits<uint16_t>::max())));
443 if (a.elements.size() != b.elements.size())
return false;
444 for (
size_t i = 0; i < a.elements.size(); i++) {
445 const auto & ae = a.elements[i];
446 const auto & be = b.elements[i];
448 if ((ae.offset != be.offset) ||
449 (ae.format != be.format) ||
450 (ae.semantic != be.semantic) ||
451 (ae.semanticIndex != be.semanticIndex) ||
452 (ae.inputType != be.inputType) ||
453 (ae.instanceStep != be.instanceStep))
461 uint32_t recordBuffer(WriteContext& wctx, uint64_t size, CogsBin3::BufferContentFlags flags, uint32_t alignment, uint16_t sectionIx)
465 assert(alignment <= CogsBin3::CogsSectionAlignment);
467 const auto lowerMask = uint64_t(alignment - 1);
469 auto & section = getSection(wctx, CogsBin3::SectionType::DATA, sectionIx);
470 section.dataSize = (section.dataSize + lowerMask)&(~lowerMask);
471 section.dataSize += size;
473 auto index = nextIndex(wctx.buffers);
475 wctx.bufferContexts.emplace_back();
477 auto & buffer = wctx.buffers.back();
478 buffer.sectionOffset = sectionIx;
480 buffer.size = (uint32_t)size;
481 buffer.flags = flags;
483 wctx.bufferContexts[index].contents.resize(size,
false);
484 wctx.bufferContexts[index].alignment = alignment;
489 uint8_t* getBufferPointer(WriteContext& wctx, uint32_t bufferIx, uint32_t offset, uint64_t size)
491 assert(bufferIx < wctx.buffers.size());
492 auto & bufferCtx = wctx.bufferContexts[bufferIx];
493 assert(offset + size <= bufferCtx.contents.size());
494 return (uint8_t*)bufferCtx.contents.data() + offset;
497 uint32_t recordBufferResource(WriteContext& wctx,
BufferResource* srcBuffer, uint16_t sectionIx)
499 if (srcBuffer ==
nullptr || srcBuffer->
empty()) {
503 if (
auto it = wctx.bufferByPointer.find(srcBuffer); it != wctx.bufferByPointer.end()) {
507 CogsBin3::BufferContentFlags flags = CogsBin3::BufferContentFlags::None;
508 if (srcBuffer->
isVertexBuffer()) flags |= CogsBin3::BufferContentFlags::VertexData;
509 if (srcBuffer->
isIndexBuffer()) flags |= CogsBin3::BufferContentFlags::IndexData;
511 constexpr uint32_t bufferStride = 16;
512 auto alignment = std::min(uint32_t(CogsBin3::CogsSectionAlignment), uint32_t(bufferStride));
513 uint32_t index = recordBuffer(wctx, srcBuffer->
size(), flags, alignment, sectionIx);
514 std::memcpy(getBufferPointer(wctx, index, 0, srcBuffer->
size()), srcBuffer->
data(), srcBuffer->
size());
516 wctx.bufferByPointer[srcBuffer] = index;
523 const auto & srcFormat = *Cogs::VertexFormats::getVertexFormat(srcStream.
format);
524 assert(srcFormat.size <= std::numeric_limits<uint16_t>::max());
525 stream.stride = uint16_t(srcFormat.size);
527 assert(srcFormat.elements.size() < std::numeric_limits<uint32_t>::max());
528 stream.attributeCount = uint32_t(srcFormat.elements.size());
530 auto hash = Cogs::getHash(srcFormat);
532 if (
auto it = wctx.vertexFormatByHash.find(hash); it != wctx.vertexFormatByHash.end() && match(*it->second.format, srcFormat)) {
533 stream.firstAttribute = it->second.index;
536 stream.firstAttribute = recordAttribute(wctx, srcFormat.elements[0]);
537 for (
size_t i = 1; i < srcFormat.elements.size(); i++) {
538 auto ix = recordAttribute(wctx, srcFormat.elements[i]);
539 assert(stream.firstAttribute + i == ix);
541 wctx.vertexFormatByHash[hash] = WVertexFormatRec{ &srcFormat, stream.firstAttribute };
545 uint32_t recordVertexStream(WriteContext& wctx,
const DataStream& srcStream, uint16_t sectionIx)
547 assert(srcStream.size() <= std::numeric_limits<uint32_t>::max());
549 auto index = nextIndex(wctx.vertexStreams);
551 stream.firstAttribute = ~0u;
552 stream.vertexDataType = srcStream.
type;
556 recordVertexFormat(wctx, stream, srcStream);
558 stream.stride = (uint16_t)srcStream.
stride;
561 assert((
size_t)stream.count * (
size_t)stream.stride <= srcStream.size());
564 stream.buffer = recordBufferResource(wctx, srcStream.
buffer.
resolve(), sectionIx);
565 stream.offset = srcStream.
offset;
570 uint32_t recordMesh(WriteContext& wctx,
Mesh* srcMesh, uint16_t section)
572 if (srcMesh ==
nullptr || srcMesh->streams.empty()) {
576 if (
auto it = wctx.meshByPointer.find(srcMesh); it != wctx.meshByPointer.end()) {
577 LOG_DEBUG(logger,
"Duplicate mesh, recycling.");
581 uint32_t firstStream = 0;
582 uint32_t streamCount = 0;
583 for (
size_t i = 0; i < srcMesh->streams.size(); i++) {
584 if (srcMesh->streams[i].size() != 0) {
585 uint32_t streamIx = recordVertexStream(wctx, srcMesh->streams[i], section);
586 if (streamCount == 0) {
587 firstStream = streamIx;
589 assert(firstStream + streamCount == streamIx);
594 auto index = nextIndex(wctx.meshes);
595 auto & mesh = wctx.meshes.emplace_back();
596 mesh.name = recordString(wctx, srcMesh->
getName());
597 mesh.firstStream = firstStream;
598 mesh.streamCount = (uint8_t)streamCount;
599 mesh.boundingBox = recordBoundingBox(wctx, srcMesh->boundingBox);
601 (srcMesh->
isCCW() ? CogsBin3::MeshFlags::None : CogsBin3::MeshFlags::Clockwise) |
602 (srcMesh->
isMeshFlagSet(MeshFlags::Skinned) ? CogsBin3::MeshFlags::Skinned : CogsBin3::MeshFlags::None);
604 mesh.primitiveType = translatePrimitiveType(wctx, srcMesh->primitiveType);
606 wctx.meshByPointer[srcMesh] = index;
612 uint32_t recordProperty(WriteContext& wctx,
const Cogs::StringView& key, CogsBin3::PropertyValueType valueType,
const T & value)
614 auto keyIx = recordString(wctx, key);
618 if (
auto it = wctx.propByHash.find(hash); it != wctx.propByHash.end()) {
619 auto & p = wctx.properties[it->second];
620 if (p.keyType == CogsBin3::PropertyKeyType::String &&
622 p.valueType == valueType)
624 if constexpr (
sizeof(T) <=
sizeof(uint32_t)) {
625 if (*(T*)(&p.value) == value) {
631 if (*(T*)(wctx.propertyData.data() + p.value) == value) {
639 auto index = nextIndex(wctx.properties);
640 wctx.properties.emplace_back(
CogsBin3::Property{ CogsBin3::PropertyKeyType::String, valueType, keyIx, ~0u });
641 auto & prop = wctx.properties.back();
643 if constexpr (
sizeof(T) <=
sizeof(uint32_t)) {
644 *(T*)(&prop.value) = value;
647 constexpr auto align =
alignof(T);
648 static_assert(align != 0 && (align & (align - 1)) == 0 &&
"alignment is not a power of two");
649 const auto mask = align - 1;
650 auto offset = (wctx.propertyData.size() + mask) & ~mask;
651 assert(offset < std::numeric_limits<uint32_t>::max());
652 prop.value = uint32_t(offset);
653 wctx.propertyData.resize(wctx.propertyData.size() +
sizeof(value));
654 *(T*)(wctx.propertyData.data() + offset) = value;
661 auto index = nextIndex(wctx.properties);
662 wctx.properties.emplace_back();
663 auto & prop = wctx.properties.back();
664 prop.keyType = CogsBin3::PropertyKeyType::String;
665 prop.key = recordString(wctx, key);
666 prop.valueType = valueType;
667 prop.value = recordString(wctx, value);
671 uint32_t recordTexture(WriteContext& wctx,
const Texture* srcTex, uint16_t sectionIx)
674 assert(srcTex->storage.data.size() != 0);
676 if (
auto it = wctx.knownTexturesByPointer.find(srcTex); it != wctx.knownTexturesByPointer.end()) {
679 auto texIx = nextIndex(wctx.textures);
681 wctx.knownTexturesByPointer[srcTex] = texIx;
682 auto & dstTex = wctx.textures.back();
685 assert(srcTex->storage.data.size() <= std::numeric_limits<uint32_t>::max());
686 const auto size = uint32_t(srcTex->storage.data.size());
687 dstTex.buffer = recordBuffer(wctx, size, CogsBin3::BufferContentFlags::TextureData, 4, sectionIx);
688 std::memcpy(getBufferPointer(wctx, dstTex.buffer, 0, size), srcTex->storage.data.data(), size);
692 dstTex.encoding = CogsBin3::TextureEncoding::None;
693 dstTex.name = recordString(wctx, srcTex->
getName());
694 dstTex.width = srcTex->description.width;
695 dstTex.height = srcTex->description.height;
696 dstTex.depth = srcTex->description.depth;
697 dstTex.format = translateFormat(srcTex->description.format);
698 dstTex.flags = CogsBin3::TextureFlags::None;
703 uint32_t recordMaterialInstance(WriteContext& wctx,
MaterialInstance* srcMatInst, uint16_t sectionIx)
705 if (srcMatInst ==
nullptr)
return ~0u;
707 if (
auto it = wctx.knownMaterialInstancesByPointer.find(srcMatInst); it != wctx.knownMaterialInstancesByPointer.end()) {
711 auto hash = getMaterialInstanceHash(srcMatInst);
712 if (
auto it = wctx.knownMaterialInstancesByHash.find(hash); it != wctx.knownMaterialInstancesByHash.end()) {
716 auto index = nextIndex(wctx.materialInstances);
717 wctx.materialInstances.emplace_back();
718 auto & dstMatInst = wctx.materialInstances.back();
720 wctx.knownMaterialInstancesByPointer[srcMatInst] = index;
721 wctx.knownMaterialInstancesByHash[hash] = index;
723 dstMatInst.material = recordString(wctx, srcMatInst->
material->
getName());
724 dstMatInst.permutation = recordString(wctx, srcMatInst->getPermutation());
725 dstMatInst.propertyFirst = nextIndex(wctx.properties);
726 dstMatInst.propertyCount = 0;
730 case MaterialDataType::Float:
731 if (
auto val = srcMatInst->
getFloatProperty(prop.key); val != prop.defaultFloat()) {
732 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float, val);
733 dstMatInst.propertyCount++;
736 case MaterialDataType::Float2:
737 if (
auto val = srcMatInst->
getVec2Property(prop.key); val != prop.defaultVec2()) {
738 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float2, val);
739 dstMatInst.propertyCount++;
742 case MaterialDataType::Float3:
743 if (
auto val = srcMatInst->
getVec3Property(prop.key); val != prop.defaultVec3()) {
744 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float3, val);
745 dstMatInst.propertyCount++;
748 case MaterialDataType::Float4:
749 if (
auto val = srcMatInst->
getVec4Property(prop.key); val != prop.defaultVec4()) {
750 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float4, val);
751 dstMatInst.propertyCount++;
754 case MaterialDataType::Bool:
755 if (
auto val = srcMatInst->
getBoolProperty(prop.key); val != prop.defaultBool()) {
756 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Bool, (uint32_t)val);
757 dstMatInst.propertyCount++;
760 case MaterialDataType::UInt:
761 if (
auto val = srcMatInst->getProperty<uint32_t>(prop.key); val != prop.defaultUInt()) {
762 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::UInt, (uint32_t)val);
763 dstMatInst.propertyCount++;
767 LOG_WARNING(logger,
"Skipped serialization of unsupported material property %s of type %u.", prop.name.c_str(),
unsigned(prop.type));
773 if ((wctx.flags & WriteModelFlags::EMBED_TEXTURES) != 0) {
774 for (
auto & prop : srcMatInst->
material->textureProperties) {
776 if (value.texture.handle && value.texture.handle != prop.texture.handle) {
777 auto * tex = value.texture.handle.resolve();
778 if (tex->storage.data.size() == 0) {
779 LOG_WARNING(logger,
"Empty texture, skipping.");
782 uint32_t texIx = recordTexture(wctx, tex, sectionIx);
783 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::EmbeddedTexture, texIx);
784 dstMatInst.propertyCount++;
790 for (uint32_t texI = 0; texI < srcMatInst->
material->textureProperties.size(); texI++) {
795 const std::string dir = Cogs::IO::parentPath(wctx.fileName);
796 if (source.empty()) {
799 if (tex->storage.data.size() == 0) {
800 LOG_WARNING(logger,
"Skipping texture without source or data.");
803 const std::string textureName = Cogs::IO::stem(wctx.fileName) +
"_texture_" + std::to_string(index) + std::to_string(texI) +
".png";
804 source = Cogs::IO::combine(dir, textureName);
805 const Cogs::FormatInfo* formatInfo = getFormatInfo(tex->description.format);
806 if (!stbi_write_png(source.c_str(), tex->description.width, tex->description.height, formatInfo->
elements, tex->storage.data.data(), 0)) {
807 LOG_ERROR(logger,
"Failed to write texture %s.", source.c_str());
811 recordStringBasedProperty(wctx, prop.
name, CogsBin3::PropertyValueType::String, Cogs::IO::relativePath(source, dir));
812 dstMatInst.propertyCount++;
817 const ShaderVariants& variants = srcMatInst->
material->definition.variants;
820 if (variant.isShared)
continue;
821 if (selector.value != variant.defaultValue) {
822 if (variant.type == ShaderVariantType::Enum) {
823 recordStringBasedProperty(wctx, variant.name, CogsBin3::PropertyValueType::Variant, variant.values[selector.value].key);
824 dstMatInst.propertyCount++;
827 recordStringBasedProperty(wctx, variant.name, CogsBin3::PropertyValueType::Variant, selector.value ?
"true" :
"false");
828 dstMatInst.propertyCount++;
833 assert(dstMatInst.propertyFirst + dstMatInst.propertyCount == nextIndex(wctx.properties));
837 uint32_t recordSkeleton(WriteContext& wctx,
const Skeleton& srcSkeleton)
839 if (srcSkeleton.bones.empty())
return ~0u;
841 auto index = nextIndex(wctx.skeletons);
843 auto & dstSkeleton = wctx.skeletons.back();
844 dstSkeleton.bindPose = srcSkeleton.bindPose;
845 dstSkeleton.firstBone = nextIndex(wctx.bones);
846 dstSkeleton.boneCount = uint32_t(srcSkeleton.bones.size());
847 for (
const auto & srcBone : srcSkeleton.bones) {
849 auto & dstBone = wctx.bones.back();
850 dstBone.inverseBindPose = srcBone.inverseBindPose;
851 dstBone.relative = srcBone.relative;
852 dstBone.rot = srcBone.rot;
853 dstBone.pos = srcBone.pos;
854 dstBone.scale = srcBone.scale;
855 dstBone.name = recordString(wctx, srcBone.name);
856 dstBone.parentBone = uint16_t(srcBone.parentBone);
858 (srcBone.hasOffset ? CogsBin3::BoneFlags::HasOffset : CogsBin3::BoneFlags::None) |
859 (srcBone.used ? CogsBin3::BoneFlags::Used : CogsBin3::BoneFlags::None) |
860 (srcBone.animated ? CogsBin3::BoneFlags::Animated : CogsBin3::BoneFlags::None);
865 uint32_t recordPart(WriteContext& wctx,
const Model& model,
const ModelPart& part, uint16_t section)
867 auto nodeIndex = newNode(wctx);
868 auto & node = wctx.nodes[nodeIndex];
870 node.parent = part.parentIndex;
871 if (part.nameIndex != -1) {
872 node.name = recordString(wctx, model.getPartName(part));
874 if (part.transformIndex != -1) {
875 node.transform = recordTransform(wctx, model.getPartTransform(part));
878 if (part.meshIndex != -1) {
879 auto * mesh = model.meshes[part.meshIndex].resolve();
880 node.mesh = recordMesh(wctx, mesh, 0);
881 node.boundingBox = recordBoundingBox(wctx, mesh->boundingBox);
882 node.primitiveType = translatePrimitiveType(wctx, mesh->primitiveType);
883 node.vertexFirst = part.startIndex;
884 node.vertexCount = part.vertexCount;
885 if (part.materialIndex != -1) {
886 node.materialInstance = recordMaterialInstance(wctx, model.materials[part.materialIndex].resolve(), section);
893 void recordAnimTrack(WriteContext& wctx,
const AnimationTrack& srcTrack, uint16_t sectionIx)
896 auto & dstTrack = wctx.animTracks.back();
898 const auto translationCount = uint32_t(srcTrack.translations.size());
899 const auto rotationCount = uint32_t(srcTrack.rotations.size());
900 const auto scaleCount = uint32_t(srcTrack.scales.size());
902 dstTrack.boneIndex = uint32_t(srcTrack.
boneIndex);
903 dstTrack.buffer = recordBuffer(wctx,
904 sizeof(
float)*(4 * (
size_t)translationCount + 5 * (
size_t)rotationCount + 4 * (
size_t)scaleCount),
905 CogsBin3::BufferContentFlags::AnimationData, 4, sectionIx);
907 auto * base = (
float*)wctx.bufferContexts[dstTrack.buffer].contents.data();
910 dstTrack.translationOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
911 for(
auto & translation : srcTrack.translations) {
912 *ptr++ = translation.t;
913 *ptr++ = translation.value[0];
914 *ptr++ = translation.value[1];
915 *ptr++ = translation.value[2];
917 dstTrack.translationCount = translationCount;
918 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.translationOffset ==
sizeof(
float) * 4 * translationCount);
920 dstTrack.rotationOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
921 for (
auto & rotation : srcTrack.rotations) {
923 *ptr++ = rotation.value[0];
924 *ptr++ = rotation.value[1];
925 *ptr++ = rotation.value[2];
926 *ptr++ = rotation.value[3];
928 dstTrack.rotationCount = rotationCount;
929 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.rotationOffset ==
sizeof(
float) * 5 * rotationCount);
931 dstTrack.scaleOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
932 for (
auto & scale : srcTrack.scales) {
934 *ptr++ = scale.value[0];
935 *ptr++ = scale.value[1];
936 *ptr++ = scale.value[2];
938 dstTrack.scaleCount = scaleCount;
939 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.scaleOffset ==
sizeof(
float) * 4 * scaleCount);
940 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) ==
sizeof(
float) * (4 * translationCount + 5 * rotationCount + 4 * scaleCount));
943 void recordAnimClip(WriteContext& wctx,
const AnimationClip& srcClip, uint16_t sectionIx)
946 auto & dstClip = wctx.animClips.back();
947 dstClip.name = recordString(wctx, srcClip.name);
948 dstClip.duration = srcClip.duration;
949 dstClip.resolution = srcClip.resolution;
950 if (!srcClip.tracks.empty()) {
951 dstClip.trackFirst = nextIndex(wctx.animTracks);
952 dstClip.trackCount = uint32_t(srcClip.tracks.size());
953 for (
auto const & track : srcClip.tracks) recordAnimTrack(wctx, track, sectionIx);
954 assert(dstClip.trackFirst + dstClip.trackCount == wctx.animTracks.size());
957 dstClip.trackFirst = ~0u;
958 dstClip.trackCount = 0;
962 void recordModel(WriteContext& wctx,
const Model * srcModel, WriteModelFlags )
964 if (srcModel->parts.empty())
return;
966 uint16_t section = 0;
969 auto & dstModel = wctx.models.back();
970 dstModel.skeleton = (uint32_t)-1;
971 dstModel.animClipFirst = (uint32_t)-1;
972 dstModel.animClipCount = 0;
973 if (
auto * animation = srcModel->animation.
resolve(); animation) {
974 dstModel.skeleton = recordSkeleton(wctx, animation->skeleton);
975 if (!animation->clips.empty()) {
976 dstModel.animClipFirst = nextIndex(wctx.animClips);
977 dstModel.animClipCount = uint32_t(animation->clips.size());
978 for (
const auto & clip : animation->clips) recordAnimClip(wctx, clip, section);
979 assert(dstModel.animClipFirst + dstModel.animClipCount == wctx.animClips.size());
983 dstModel.firstNode = recordPart(wctx, *srcModel, srcModel->parts[0], section);
984 for (
size_t i = 1; i < srcModel->parts.size(); i++) {
985 auto ix = recordPart(wctx, *srcModel, srcModel->parts[i], 0);
986 assert(dstModel.firstNode + i == ix);
988 dstModel.nodeCount = uint32_t(srcModel->parts.size());
993 auto nodeIndex = newNode(wctx);
994 auto & node = wctx.nodes[nodeIndex];
996 node.parent = parent;
997 node.name = recordString(wctx, entity->
getName());
1000 node.transform = recordTransform(wctx, context->transformSystem->getLocalTransform(trComp));
1004 auto mesh = meshComponent->meshHandle;
1005 if (mesh && mesh->streams.size()) {
1007 if (renderComponent) {
1008 node.mesh = recordMesh(wctx, mesh.resolve(), section);
1010 node.primitiveType = translatePrimitiveType(wctx, renderComponent->primitiveType);
1012 node.primitiveType = CogsBin3::PrimitiveType::Unknown;
1014 node.vertexFirst = renderComponent->startIndex;
1015 node.vertexCount = renderComponent->vertexCount;
1016 node.boundingBox = recordBoundingBox(wctx, context->renderSystem->getLocalBounds(renderComponent));
1017 if (renderComponent->material) {
1018 node.materialInstance = recordMaterialInstance(wctx, renderComponent->material.resolve(), section);
1022 LOG_ERROR(logger,
"MeshComponent must be accompanied by a MeshRenderComponent for export.");
1028 for (
auto & child : sceneComponent->children) {
1029 recordEntity(context, wctx, nodeIndex, child.get(), section);
1035 auto * anim = animComp->animation.resolve();
1036 if (animComp->master) {
1040 if (wctx.currentAnimation ==
nullptr) {
1041 wctx.currentAnimation = anim;
1043 else if (wctx.currentAnimation != anim) {
1044 LOG_ERROR(logger,
"Multiple animations within a single model's hierarchy");
1051 void layoutSections(WriteContext& wctx)
1053 for (
size_t i = 0; i < wctx.sections.size(); i++) {
1054 auto & section = *wctx.sections[i].get();
1056 assert(section.contents.empty() &&
"Sections already laid out");
1060 for (
auto & subsection : section.subSections) {
1061 section.writePos = align(section.writePos);
1062 assert(section.writePos < std::numeric_limits<uint32_t>::max());
1063 subsection.offset = section.writePos;
1064 section.writePos += subsection.size;
1066 section.writePos = align(section.writePos);
1069 section.contents.resize(section.writePos + section.dataSize);
1070 LOG_TRACE(logger,
"Section %zu allocated %zu bytes, data offset=%zu", i, section.contents.size(), section.writePos);
1074 for (
auto & subsection : section.subSections) {
1075 LOG_TRACE(logger,
" Subsection offset=%zu, size=%u, type=%u", subsection.offset, subsection.size,
unsigned(subsection.type));
1076 ptr->offset = subsection.offset;
1077 ptr->size = subsection.size;
1078 ptr->type = subsection.type;
1085 template<
typename T>
1086 void fillSubsection(WSection& section, uint32_t index,
const std::vector<T>& source)
1088 if (index == ~0u)
return;
1089 auto & subSection = section.subSections[index];
1090 assert(
sizeof(T)*source.size() == subSection.size);
1092 std::memcpy(section.contents.data() + subSection.offset, source.data(), subSection.size);
1095 bool pack(
Context* , WriteContext& wctx)
1098 auto & metaSection = getSection(wctx, CogsBin3::SectionType::META, 0);
1100 uint32_t subSections[size_t(CogsBin3::SubSectionType::EnumSize)];
1101 for (
auto & subSection : subSections) subSection = (uint32_t)-1;
1103#define REGISTER(T,A) if(!(A).empty()) subSections[size_t(T)] = addMetaSubsection(wctx, metaSection, T, sizeof((A)[0])*((A).size()))
1104 REGISTER(CogsBin3::SubSectionType::Buffers, wctx.buffers);
1105 REGISTER(CogsBin3::SubSectionType::Textures, wctx.textures);
1106 REGISTER(CogsBin3::SubSectionType::Strings, wctx.strings);
1107 REGISTER(CogsBin3::SubSectionType::StringData, wctx.stringData);
1108 REGISTER(CogsBin3::SubSectionType::Nodes, wctx.nodes);
1109 REGISTER(CogsBin3::SubSectionType::Transforms, wctx.transforms);
1110 REGISTER(CogsBin3::SubSectionType::BoundingBoxes, wctx.boundingBoxes);
1111 REGISTER(CogsBin3::SubSectionType::MaterialInstances, wctx.materialInstances);
1112 REGISTER(CogsBin3::SubSectionType::Properties, wctx.properties);
1113 REGISTER(CogsBin3::SubSectionType::PropertyData, wctx.propertyData);
1114 REGISTER(CogsBin3::SubSectionType::Meshes, wctx.meshes);
1115 REGISTER(CogsBin3::SubSectionType::VertexStreams, wctx.vertexStreams);
1116 REGISTER(CogsBin3::SubSectionType::VertexAttributes, wctx.vertexAttributes);
1117 REGISTER(CogsBin3::SubSectionType::Bones, wctx.bones);
1118 REGISTER(CogsBin3::SubSectionType::Skeletons, wctx.skeletons);
1119 REGISTER(CogsBin3::SubSectionType::AnimTracks, wctx.animTracks);
1120 REGISTER(CogsBin3::SubSectionType::AnimClips, wctx.animClips);
1121 REGISTER(CogsBin3::SubSectionType::Models, wctx.models);
1124 layoutSections(wctx);
1127 for (
size_t i = 0; i < wctx.buffers.size(); i++) {
1128 auto & buffer = wctx.buffers[i];
1129 const auto & bufCtx = wctx.bufferContexts[i];
1130 const auto lowerMask = uint64_t(bufCtx.alignment - 1);
1132 const auto sectionIx = buffer.sectionOffset;
1133 auto & section = getSection(wctx, CogsBin3::SectionType::DATA, uint16_t(sectionIx),
false);
1134 auto offset = (section.writePos + lowerMask) & (~lowerMask);
1135 buffer.sectionOffset = offset | (sectionIx << 48);
1137 assert(offset + buffer.size <= section.contents.size());
1138 std::memcpy(section.contents.data() + offset, bufCtx.contents.data(), buffer.size);
1139 section.writePos = offset + buffer.size;
1143 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Buffers)], wctx.buffers);
1144 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Textures)], wctx.textures);
1145 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Strings)], wctx.strings);
1146 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::StringData)], wctx.stringData);
1147 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Nodes)], wctx.nodes);
1148 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Transforms)], wctx.transforms);
1149 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::BoundingBoxes)], wctx.boundingBoxes);
1150 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::MaterialInstances)], wctx.materialInstances);
1151 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Properties)], wctx.properties);
1152 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::PropertyData)], wctx.propertyData);
1153 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Meshes)], wctx.meshes);
1154 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::VertexStreams)], wctx.vertexStreams);
1155 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::VertexAttributes)], wctx.vertexAttributes);
1156 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Bones)], wctx.bones);
1157 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Skeletons)], wctx.skeletons);
1158 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::AnimTracks)], wctx.animTracks);
1159 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::AnimClips)], wctx.animClips);
1160 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Models)], wctx.models);
1165#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1166#pragma optimize( "", on )
1168 bool compressSections(
Context* , WriteContext& wctx)
1170 wctx.sectionInfos.resize(wctx.sections.size());
1171 for (
auto & section : wctx.sections) {
1172 auto & info = wctx.sectionInfos[section->index];
1173 info.fileSize = section->contents.size();
1174 info.uncompressedSize = info.fileSize;
1175 info.compression = CogsBin3::Compression::None;
1176 info.type = section->type;
1177 info.subSectionCount = uint16_t(section->subSections.size());
1181 if ((wctx.flags & WriteModelFlags::COMPRESS_ZSTD) != 0) {
1183 int compressionLevel = ZSTD_CLEVEL_DEFAULT;
1184 if ((wctx.flags & WriteModelFlags::COMPRESS_MAX) != 0) {
1185 compressionLevel = ZSTD_maxCLevel();
1187 compressionLevel = wctx.settings.compressionLevel;
1191 ZSTD_CCtx* zstd_ctx = ZSTD_createCCtx();
1192 if (zstd_ctx ==
nullptr) {
1193 LOG_ERROR(logger,
"Failed to create zstd context.");
1197 std::vector<uint8_t> temp;
1198 for (
auto & section : wctx.sections) {
1199 auto & info = wctx.sectionInfos[section->index];
1200 auto maxSize = ZSTD_compressBound(info.uncompressedSize);
1201 temp.resize(maxSize);
1204 auto result = ZSTD_compressCCtx(zstd_ctx, temp.data(), maxSize, section->contents.data(), info.uncompressedSize, compressionLevel);
1205 if (ZSTD_isError(result)) {
1206 LOG_ERROR(logger,
"zstd compression failed: %s", ZSTD_getErrorName(result));
1209 else if (info.uncompressedSize <= result) {
1215 temp.resize(result);
1216 info.compression = CogsBin3::Compression::ZSTD;
1217 info.fileSize = result;
1218 section->contents.swap(temp);
1221 ZSTD_freeCCtx(zstd_ctx);
1228#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1229#pragma optimize( "", off )
1232 bool write(
Context* context, WriteContext& wctx)
1234 pack(context, wctx);
1236 compressSections(context, wctx);
1242 header.magic = CogsBin3::HeaderMagic;
1243 header.version = CogsBin3::Version;
1245 header.sectionCount = nextIndex(wctx.sectionInfos);
1246 for (
auto & info : wctx.sectionInfos) {
1247 if (info.compression == CogsBin3::Compression::None) {
1249 header.fileLength = (header.fileLength + (CogsBin3::CogsSectionAlignment - 1)) & ~(CogsBin3::CogsSectionAlignment - 1);
1251 info.fileOffset = header.fileLength;
1252 header.fileLength += info.fileSize;
1253 uncompressedSize += info.uncompressedSize;
1257 FILE * file = fopen(wctx.fileName.c_str(),
"wb");
1258 if (file ==
nullptr) {
1259 LOG_ERROR(logger,
"Failed to open \"%s\" for writing.", wctx.fileName.c_str());
1267 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1275 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1280 for (uint32_t i = 0; i < header.sectionCount; i++) {
1281 auto & info = wctx.sectionInfos[i];
1282 auto & section = *wctx.sections[i].get();
1285 auto pos = uint64_t(_ftelli64(file));
1287 auto pos = uint64_t(ftell(file));
1289 assert(pos <= info.fileOffset);
1290 if (pos != info.fileOffset) {
1292 _fseeki64(file, info.fileOffset, SEEK_SET);
1294 fseek(file, info.fileOffset, SEEK_SET);
1298 r = fwrite(section.contents.data(), info.fileSize, 1, file);
1301 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1307 auto fileLength = _ftelli64(file);
1309 auto fileLength = ftell(file);
1311 assert(fileLength == (
long long)header.fileLength);
1312 LOG_TRACE(logger,
"Wrote %zu bytes (=%zu uncompressed, ratio=%.1f%%) (%u sections)",
1314 size_t(uncompressedSize),
1315 (100.f*
float(fileLength))/
float(uncompressedSize),
1316 unsigned(header.sectionCount));
1325 auto flags = settings->flags;
1327 if (!model)
return false;
1329 WriteContext wctx{};
1330 wctx.fileName = fileName.to_string();
1331 wctx.flags = WriteModelFlags(flags);
1333 wctx.settings = *settings;
1335 recordModel(wctx, model, flags);
1336 write(context, wctx);
1337 numVertes = wctx.numVertes;
1338 numIndexes = wctx.numIndexes;
1342bool Cogs::Core::writeCogsBin3Models(
Context * context, uint32_t& numVertes, uint32_t& numIndexes,
const StringView & fileName,
ComponentModel::Entity ** entity,
size_t N, WriteModelFlags flags)
1344 if (!entity || N == 0)
return false;
1346 uint16_t section = 0;
1347 WriteContext wctx{};
1348 wctx.fileName = fileName.to_string();
1349 wctx.flags = WriteModelFlags(flags);
1350 wctx.currentAnimation =
nullptr;
1352 auto & dstModel = wctx.models.back();
1354 dstModel.firstNode = recordEntity(context, wctx, ~0u, entity[0], section);
1355 for (
size_t i = 1; i < N; i++) {
1356 recordEntity(context, wctx, ~0u, entity[i], section);
1358 dstModel.nodeCount = uint32_t(wctx.nodes.size() - dstModel.firstNode);
1360 if (wctx.currentAnimation) {
1361 dstModel.skeleton = recordSkeleton(wctx, wctx.currentAnimation->skeleton);
1362 dstModel.animClipFirst = nextIndex(wctx.animClips);
1363 dstModel.animClipCount = uint32_t(wctx.currentAnimation->clips.size());
1364 for (
const auto & clip : wctx.currentAnimation->clips) recordAnimClip(wctx, clip, section);
1365 assert(dstModel.animClipFirst + dstModel.animClipCount == wctx.animClips.size());
1368 dstModel.skeleton = ~0u;
1369 dstModel.animClipFirst = ~0u;
1370 dstModel.animClipCount = 0;
1373 write(context, wctx);
1374 numVertes = wctx.numVertes;
1375 numIndexes = wctx.numIndexes;
1380#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1381#pragma optimize( "", on )
Container for components, providing composition of dynamic entities.
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
const std::string & getName() const noexcept
Get the name of this entity.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Renders the contents of a MeshComponent using the given materials.
Contains information on how the entity behaves in the scene.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
std::string to_string() const
String conversion method.
constexpr size_t hash() const noexcept
Get the hash code of the string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
constexpr size_t hashSequence(const T &t, const U &u)
Hash the last two items in a sequence of objects.
@ VertexData
Per vertex data.
PrimitiveType
Primitive types for interpreting vertex data sent to the graphics pipeline.
@ PointList
List of points.
@ TriangleStrip
Triangle strip.
@ TriangleStripAdjacency
Triangle strip with adjacency.
@ LineStripAdjacency
Line strip with adjacency.
@ LineListAdjacency
List of lines with adjacency.
@ TriangleListAdjacency
List of triangles with adjacency.
@ TriangleList
List of triangles.
@ Position
Position semantic.
@ Tangent
Tangent semantic.
@ InstanceMatrix
Instance matrix semantic.
@ InstanceVector
Instance vector semantic.
@ TextureCoordinate
Texture coordinate semantic.
Component handling animation of multiple Poses of an Animation Resource.
AnimationHandle animation
Animation resource handle.
uint32_t boneIndex
Specifies which bone of a skeleton that is animated by this track.
bool empty() const
If the buffer is empty. Note that an empty buffer may still have reserved storage.
bool isVertexBuffer() const
Gets if the buffer data is usable as a vertex data.
size_t size() const
Size of the buffer in bytes.
void * data()
Get a pointer to the buffer data.
bool isIndexBuffer() const
Gets if the buffer data is usable as index buffer data.
std::vector< MaterialProperty > variables
Individual variables from all buffer instances.
Contains a stream of data used by Mesh resources.
uint32_t numElements
Number of elements of the type given by format contained in data.
uint32_t offset
Byte offset from the start of the buffer.
VertexFormatHandle format
A pointer to the format describing the contents of the byte buffer.
VertexDataType::EVertexDataType type
Type index used to index streams in a Mesh resource.
uint32_t stride
Element stride.
Cogs::Core::ResourceBufferHandle buffer
Data buffer.
Material instances represent a specialized Material combined with state for all its buffers and prope...
glm::vec4 getVec4Property(const VariableKey key) const
Get the value of the property with the given key.
bool getBoolProperty(const VariableKey key) const
Get the value of the property with the given key.
TextureValue getTextureProperty(const VariableKey key) const
Get the value of the property with the given key.
float getFloatProperty(const VariableKey key) const
Get the value of the property with the given key.
glm::vec3 getVec3Property(const VariableKey key) const
Get the value of the property with the given key.
glm::vec2 getVec2Property(const VariableKey key) const
Get the value of the property with the given key.
Material * material
Material resource this MaterialInstance is created from.
ShaderVariantSelectors variantSelectors
Variant selectors.
VariableKey key
Key of the property, used to index into the property collection the property will be placed into.
PropertyName name
Name of the property, used to reference named uniforms of constant buffer members in shaders.
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
bool isCCW() const
If triangles in the mesh are specified counter-clockwise, which is the default.
constexpr bool isMeshFlagSet(MeshFlags::EMeshFlags flag) const
Check if the given mesh flag(s) is set.
Model resources define a template for a set of connected entities, with resources such as meshes,...
StringView getName() const
Get the name of the resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
Property value for texture samplers.
TextureWithSampler texture
Value of the property for the material instance this belongs to.
TextureHandle handle
Handle to a texture resource, or TextureHandle::NoHandle if texture should be disabled.
Texture resources contain raster bitmap data to use for texturing.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
InputType inputType
Input type of the element, vertex or instance data.
DataFormat format
Format of the element.
uint16_t offset
Offset in bytes from the vertex position in memory.
uint16_t semanticIndex
Index for the semantic mapping.
ElementSemantic semantic
Semantic mapping of the element (position, normal, etc...).
uint16_t instanceStep
Instance step factor.