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"
29#include "stb_image_write.h"
38#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
39#pragma optimize( "", off )
52 uint32_t nextIndex(T& t)
55 if (
static_cast<size_t>(std::numeric_limits<uint32_t>::max()) < ix) {
56 LOG_ERROR(logger,
"Index out of range. Model data too large.");
59 return static_cast<uint32_t
>(ix);
66 CogsBin3::SubSectionType type;
71 CogsBin3::SectionType type;
75 std::vector<uint8_t> contents;
76 std::vector<WSubSection> subSections;
79 struct WVertexFormatRec
88 uint32_t alignment = 0;
93 uint32_t numVertes = 0;
94 uint32_t numIndexes = 0;
97 WriteModelFlags flags = WriteModelFlags::NONE;
101 std::vector<std::unique_ptr<WSection>> sections;
103 std::vector<const TranslationKey*> animTrackTranslateSource;
104 std::vector<const RotationKey*> animTrackRotationSource;
105 std::vector<const ScaleKey*> animTrackScaleSource;
106 std::vector<WBufferContext> bufferContexts;
108 std::vector<uint8_t> stringData;
109 std::vector<uint8_t> propertyData;
110 std::vector<Cogs::Core::CogsBin3::Buffer> buffers;
111 std::vector<Cogs::Core::CogsBin3::Texture> textures;
112 std::vector<Cogs::Core::CogsBin3::String> strings;
113 std::vector<Cogs::Core::CogsBin3::Property> properties;
114 std::vector<Cogs::Core::CogsBin3::MaterialInstance> materialInstances;
115 std::vector<Cogs::Core::CogsBin3::Transform> transforms;
116 std::vector<Cogs::Geometry::BoundingBox> boundingBoxes;
117 std::vector<Cogs::Core::CogsBin3::Node> nodes;
118 std::vector<Cogs::Core::CogsBin3::Mesh> meshes;
119 std::vector<Cogs::Core::CogsBin3::VertexStream> vertexStreams;
120 std::vector<Cogs::Core::CogsBin3::VertexAttribute> vertexAttributes;
121 std::vector<Cogs::Core::CogsBin3::Bone> bones;
122 std::vector<Cogs::Core::CogsBin3::Skeleton> skeletons;
123 std::vector<Cogs::Core::CogsBin3::Model> models;
124 std::vector<Cogs::Core::CogsBin3::AnimTrack> animTracks;
125 std::vector<Cogs::Core::CogsBin3::AnimClip> animClips;
127 std::vector<CogsBin3::SectionInfo> sectionInfos;
129 std::map<const Cogs::Core::Texture*, uint32_t> knownTexturesByPointer;
130 std::map<const Cogs::Core::MaterialInstance*, uint32_t> knownMaterialInstancesByPointer;
131 std::map<size_t, uint32_t> knownMaterialInstancesByHash;
132 std::map<size_t, uint32_t> propByHash;
133 std::map<size_t, uint32_t> stringByHash;
134 std::map<size_t, uint32_t> transformByHash;
135 std::map<size_t, uint32_t> boundingBoxByHash;
136 std::map<const Cogs::Core::Mesh*, uint32_t> meshByPointer;
137 std::map<const Cogs::Core::BufferResource*, uint32_t> bufferByPointer;
138 std::map<size_t, WVertexFormatRec> vertexFormatByHash;
141 template<
typename T,
typename S>
142 T checkedCast(
const S v)
144 assert(v <= std::numeric_limits<T>::max());
148 inline uint64_t align(uint64_t x)
150 const uint64_t m = CogsBin3::CogsSectionAlignment - 1;
156 switch (primitiveType) {
166 case Cogs::PrimitiveType::ControlPoint1PatchList:
return CogsBin3::PrimitiveType::ControlPoint1PatchList;
break;
167 case Cogs::PrimitiveType::ControlPoint2PatchList:
return CogsBin3::PrimitiveType::ControlPoint2PatchList;
break;
168 case Cogs::PrimitiveType::ControlPoint3PatchList:
return CogsBin3::PrimitiveType::ControlPoint3PatchList;
break;
169 case Cogs::PrimitiveType::ControlPoint4PatchList:
return CogsBin3::PrimitiveType::ControlPoint4PatchList;
break;
173 assert(
false &&
"Illegal primitive type encountered");
174 return CogsBin3::PrimitiveType::TriangleList;
177 CogsBin3::Format translateFormat(Cogs::TextureFormat format)
180 case Cogs::TextureFormat::Unknown:
return CogsBin3::Format::Unknown;
break;
181 case Cogs::TextureFormat::R8_UNORM:
return CogsBin3::Format::R8_UNORM;
break;
182 case Cogs::TextureFormat::R8G8_UNORM:
return CogsBin3::Format::R8G8_UNORM;
break;
183 case Cogs::TextureFormat::R8G8B8_UNORM:
return CogsBin3::Format::R8G8B8_UNORM;
break;
184 case Cogs::TextureFormat::R8G8B8A8_UNORM:
return CogsBin3::Format::R8G8B8A8_UNORM;
break;
185 case Cogs::TextureFormat::R16_UNORM:
return CogsBin3::Format::R16_UNORM;
break;
186 case Cogs::TextureFormat::R16G16_UNORM:
return CogsBin3::Format::R16G16_UNORM;
break;
187 case Cogs::TextureFormat::R16G16B16_UNORM:
return CogsBin3::Format::R16G16B16_UNORM;
break;
188 case Cogs::TextureFormat::R16G16B16A16_UNORM:
return CogsBin3::Format::R16G16B16A16_UNORM;
break;
189 case Cogs::TextureFormat::R8_SNORM:
return CogsBin3::Format::R8_SNORM;
break;
190 case Cogs::TextureFormat::R8G8_SNORM:
return CogsBin3::Format::R8G8_SNORM;
break;
191 case Cogs::TextureFormat::R8G8B8_SNORM:
return CogsBin3::Format::R8G8B8_SNORM;
break;
192 case Cogs::TextureFormat::R8G8B8A8_SNORM:
return CogsBin3::Format::R8G8B8A8_SNORM;
break;
193 case Cogs::TextureFormat::R16_SNORM:
return CogsBin3::Format::R16_SNORM;
break;
194 case Cogs::TextureFormat::R16G16_SNORM:
return CogsBin3::Format::R16G16_SNORM;
break;
195 case Cogs::TextureFormat::R16G16B16_SNORM:
return CogsBin3::Format::R16G16B16_SNORM;
break;
196 case Cogs::TextureFormat::R16G16B16A16_SNORM:
return CogsBin3::Format::R16G16B16A16_SNORM;
break;
197 case Cogs::TextureFormat::R8_UINT:
return CogsBin3::Format::R8_UINT;
break;
198 case Cogs::TextureFormat::R8G8_UINT:
return CogsBin3::Format::R8G8_UINT;
break;
199 case Cogs::TextureFormat::R8G8B8_UINT:
return CogsBin3::Format::R8G8B8_UINT;
break;
200 case Cogs::TextureFormat::R8G8B8A8_UINT:
return CogsBin3::Format::R8G8B8A8_UINT;
break;
201 case Cogs::TextureFormat::R16_UINT:
return CogsBin3::Format::R16_UINT;
break;
202 case Cogs::TextureFormat::R16G16_UINT:
return CogsBin3::Format::R16G16_UINT;
break;
203 case Cogs::TextureFormat::R16G16B16_UINT:
return CogsBin3::Format::R16G16B16_UINT;
break;
204 case Cogs::TextureFormat::R16G16B16A16_UINT:
return CogsBin3::Format::R16G16B16A16_UINT;
break;
205 case Cogs::TextureFormat::R32_UINT:
return CogsBin3::Format::R32_UINT;
break;
206 case Cogs::TextureFormat::R32G32_UINT:
return CogsBin3::Format::R32G32_UINT;
break;
207 case Cogs::TextureFormat::R32G32B32_UINT:
return CogsBin3::Format::R32G32B32_UINT;
break;
208 case Cogs::TextureFormat::R32G32B32A32_UINT:
return CogsBin3::Format::R32G32B32A32_UINT;
break;
209 case Cogs::TextureFormat::R8_SINT:
return CogsBin3::Format::R8_SINT;
break;
210 case Cogs::TextureFormat::R8G8_SINT:
return CogsBin3::Format::R8G8_SINT;
break;
211 case Cogs::TextureFormat::R8G8B8_SINT:
return CogsBin3::Format::R8G8B8_SINT;
break;
212 case Cogs::TextureFormat::R8G8B8A8_SINT:
return CogsBin3::Format::R8G8B8A8_SINT;
break;
213 case Cogs::TextureFormat::R16_SINT:
return CogsBin3::Format::R16_SINT;
break;
214 case Cogs::TextureFormat::R16G16_SINT:
return CogsBin3::Format::R16G16_SINT;
break;
215 case Cogs::TextureFormat::R16G16B16_SINT:
return CogsBin3::Format::R16G16B16_SINT;
break;
216 case Cogs::TextureFormat::R16G16B16A16_SINT:
return CogsBin3::Format::R16G16B16A16_SINT;
break;
217 case Cogs::TextureFormat::R32_SINT:
return CogsBin3::Format::R32_SINT;
break;
218 case Cogs::TextureFormat::R32G32_SINT:
return CogsBin3::Format::R32G32_SINT;
break;
219 case Cogs::TextureFormat::R32G32B32_SINT:
return CogsBin3::Format::R32G32B32_SINT;
break;
220 case Cogs::TextureFormat::R32G32B32A32_SINT:
return CogsBin3::Format::R32G32B32A32_SINT;
break;
221 case Cogs::TextureFormat::R16_FLOAT:
return CogsBin3::Format::R16_FLOAT;
break;
222 case Cogs::TextureFormat::R16G16_FLOAT:
return CogsBin3::Format::R16G16_FLOAT;
break;
223 case Cogs::TextureFormat::R16G16B16_FLOAT:
return CogsBin3::Format::R16G16B16_FLOAT;
break;
224 case Cogs::TextureFormat::R16G16B16A16_FLOAT:
return CogsBin3::Format::R16G16B16A16_FLOAT;
break;
225 case Cogs::TextureFormat::R32_FLOAT:
return CogsBin3::Format::R32_FLOAT;
break;
226 case Cogs::TextureFormat::R32G32_FLOAT:
return CogsBin3::Format::R32G32_FLOAT;
break;
227 case Cogs::TextureFormat::R32G32B32_FLOAT:
return CogsBin3::Format::R32G32B32_FLOAT;
break;
228 case Cogs::TextureFormat::R32G32B32A32_FLOAT:
return CogsBin3::Format::R32G32B32A32_FLOAT;
break;
229 case Cogs::TextureFormat::D16_UNORM:
return CogsBin3::Format::D16_UNORM;
break;
230 case Cogs::TextureFormat::D24_UNORM:
return CogsBin3::Format::D24_UNORM;
break;
231 case Cogs::TextureFormat::D24S8_UNORM:
return CogsBin3::Format::D24S8_UNORM;
break;
232 case Cogs::TextureFormat::D32_FLOAT:
return CogsBin3::Format::D32_FLOAT;
break;
233 case Cogs::TextureFormat::R32_TYPELESS:
return CogsBin3::Format::R32_TYPELESS;
break;
234 case Cogs::TextureFormat::R16_TYPELESS:
return CogsBin3::Format::R16_TYPELESS;
break;
235 case Cogs::TextureFormat::R8T:
return CogsBin3::Format::R8T;
break;
236 case Cogs::TextureFormat::R8G8T:
return CogsBin3::Format::R8G8T;
break;
237 case Cogs::TextureFormat::R8G8B8T:
return CogsBin3::Format::R8G8B8T;
break;
238 case Cogs::TextureFormat::R8G8B8A8T:
return CogsBin3::Format::R8G8B8A8T;
break;
239 case Cogs::TextureFormat::B8G8R8:
return CogsBin3::Format::B8G8R8;
break;
240 case Cogs::TextureFormat::B8G8R8A8:
return CogsBin3::Format::B8G8R8A8;
break;
241 case Cogs::TextureFormat::A8_UNORM:
return CogsBin3::Format::A8_UNORM;
break;
242 case Cogs::TextureFormat::BC1_TYPELESS:
return CogsBin3::Format::BC1_TYPELESS;
break;
243 case Cogs::TextureFormat::BC1_UNORM:
return CogsBin3::Format::BC1_UNORM;
break;
244 case Cogs::TextureFormat::BC1_UNORM_SRGB:
return CogsBin3::Format::BC1_UNORM_SRGB;
break;
245 case Cogs::TextureFormat::BC2_TYPELESS:
return CogsBin3::Format::BC2_TYPELESS;
break;
246 case Cogs::TextureFormat::BC2_UNORM:
return CogsBin3::Format::BC2_UNORM;
break;
247 case Cogs::TextureFormat::BC2_UNORM_SRGB:
return CogsBin3::Format::BC2_UNORM_SRGB;
break;
248 case Cogs::TextureFormat::BC3_TYPELESS:
return CogsBin3::Format::BC3_TYPELESS;
break;
249 case Cogs::TextureFormat::BC3_UNORM:
return CogsBin3::Format::BC3_UNORM;
break;
250 case Cogs::TextureFormat::BC3_UNORM_SRGB:
return CogsBin3::Format::BC3_UNORM_SRGB;
break;
251 case Cogs::TextureFormat::BC4_TYPELESS:
return CogsBin3::Format::BC4_TYPELESS;
break;
252 case Cogs::TextureFormat::BC4_UNORM:
return CogsBin3::Format::BC4_UNORM;
break;
253 case Cogs::TextureFormat::BC4_SNORM:
return CogsBin3::Format::BC4_SNORM;
break;
254 case Cogs::TextureFormat::BC5_TYPELESS:
return CogsBin3::Format::BC5_TYPELESS;
break;
255 case Cogs::TextureFormat::BC5_UNORM:
return CogsBin3::Format::BC5_UNORM;
break;
256 case Cogs::TextureFormat::BC5_SNORM:
return CogsBin3::Format::BC5_SNORM;
break;
257 case Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB:
return CogsBin3::Format::R8G8B8A8_UNORM_SRGB;
break;
259 assert(
false &&
"Illegal format");
262 return CogsBin3::Format::Unknown;
265 WSection& getSection(WriteContext& wctx, CogsBin3::SectionType type, uint16_t index,
bool create=
true)
267 if (wctx.sections.size() <= index) wctx.sections.resize((
size_t)index + 1);
268 if (!wctx.sections[index]) {
269 assert(create &&
"Trying to retrieve non-existing section");
270 wctx.sections[index] = std::make_unique<WSection>(WSection{ type, index, 0, 0, {}, {} });
272 auto & section = *wctx.sections[index].get();
273 section.type = section.type | type;
278 uint32_t addMetaSubsection(WriteContext& , WSection& section, CogsBin3::SubSectionType type,
size_t size)
280 assert(section.contents.empty() &&
"Cannot add metasubsections after data payload has been added.");
281 assert(size < std::numeric_limits<uint32_t>::max());
283 auto index = nextIndex(section.subSections);
284 section.subSections.push_back(WSubSection{ ~0u, uint32_t(size), type });
288 uint32_t newNode(WriteContext& wctx)
290 uint32_t index = nextIndex(wctx.nodes);
291 auto & node = wctx.nodes.emplace_back();
295 node.materialInstance = ~0u;
296 node.transform = ~0u;
297 node.boundingBox = ~0u;
298 node.primitiveType = CogsBin3::PrimitiveType::TriangleList;
299 node.vertexFirst = 0;
300 node.vertexCount = 0;
304 uint32_t recordTransform(WriteContext& wctx,
const glm::mat4& M)
307 if (std::memcmp(glm::value_ptr(I), glm::value_ptr(M),
sizeof(M)) == 0) {
313 const auto * m = glm::value_ptr(M);
320 size_t hashValue = T.hash();
322 if (
auto it = wctx.transformByHash.find(hashValue); it != wctx.transformByHash.end()) {
323 if (std::memcmp(&wctx.transforms[it->second], &T,
sizeof(T)) == 0) {
329 auto index = nextIndex(wctx.transforms);
330 wctx.transforms.emplace_back(T);
331 wctx.transformByHash[hashValue] = index;
335 uint32_t recordBoundingBox(WriteContext& wctx,
const Cogs::Geometry::BoundingBox& bbox)
343 size_t hashValue = bbox.hash();
344 if (
auto it = wctx.boundingBoxByHash.find(hashValue); it != wctx.boundingBoxByHash.end()) {
345 if (std::memcmp(&wctx.boundingBoxes[it->second], &bbox,
sizeof(bbox)) == 0) {
346 LOG_DEBUG(logger,
"Found duplicate bounding box.");
351 auto index = nextIndex(wctx.boundingBoxes);
352 wctx.boundingBoxes.emplace_back(bbox);
353 wctx.transformByHash[hashValue] = index;
369 hashValue =
Cogs::hash(
reinterpret_cast<intptr_t
>(material));
376 if (
string.empty())
return ~0u;
378 auto hash =
string.hash();
379 if (
auto it = wctx.stringByHash.find(hash); it != wctx.stringByHash.end()) {
380 auto & str = wctx.strings[it->second];
381 if (
string ==
Cogs::StringView((
const char*)(wctx.stringData.data() + str.offset), str.length)) {
386 auto offset = wctx.stringData.size();
387 assert(offset < std::numeric_limits<uint32_t>::max());
389 auto length =
string.length();
390 assert(length < std::numeric_limits<uint32_t>::max());
392 auto index = nextIndex(wctx.strings);
393 wctx.strings.emplace_back(
CogsBin3::String{ uint32_t(offset), uint32_t(length) });
395 wctx.stringData.resize(offset + length);
396 std::memcpy(wctx.stringData.data() + offset,
string.begin(), length);
398 wctx.stringByHash[hash] = index;
404 assert(element.
offset <= std::numeric_limits<uint16_t>::max());
406 auto index = nextIndex(wctx.vertexAttributes);
409 auto & attribute = wctx.vertexAttributes.back();
411 attribute.offset = uint16_t(element.
offset);
412 attribute.format = (CogsBin3::Format)element.
format;
423 assert(
false &&
"Unknown element.format");
425 assert(element.
semanticIndex <= std::numeric_limits<uint8_t>::max());
429 attribute.instanceStepRate = 0;
432 attribute.instanceStepRate = uint16_t(std::min(1 + (uint32_t)element.
instanceStep,
unsigned(std::numeric_limits<uint16_t>::max())));
439 if (a.elements.size() != b.elements.size())
return false;
440 for (
size_t i = 0; i < a.elements.size(); i++) {
441 const auto & ae = a.elements[i];
442 const auto & be = b.elements[i];
444 if ((ae.offset != be.offset) ||
445 (ae.format != be.format) ||
446 (ae.semantic != be.semantic) ||
447 (ae.semanticIndex != be.semanticIndex) ||
448 (ae.inputType != be.inputType) ||
449 (ae.instanceStep != be.instanceStep))
457 uint32_t recordBuffer(WriteContext& wctx, uint64_t size, CogsBin3::BufferContentFlags flags, uint32_t alignment, uint16_t sectionIx)
461 assert(alignment <= CogsBin3::CogsSectionAlignment);
463 const auto lowerMask = uint64_t(alignment - 1);
465 auto & section = getSection(wctx, CogsBin3::SectionType::DATA, sectionIx);
466 section.dataSize = (section.dataSize + lowerMask)&(~lowerMask);
467 section.dataSize += size;
469 auto index = nextIndex(wctx.buffers);
471 wctx.bufferContexts.emplace_back();
473 auto & buffer = wctx.buffers.back();
474 buffer.sectionOffset = sectionIx;
476 buffer.size = (uint32_t)size;
477 buffer.flags = flags;
479 wctx.bufferContexts[index].contents.resize(size,
false);
480 wctx.bufferContexts[index].alignment = alignment;
485 uint8_t* getBufferPointer(WriteContext& wctx, uint32_t bufferIx, uint32_t offset, uint64_t size)
487 assert(bufferIx < wctx.buffers.size());
488 auto & bufferCtx = wctx.bufferContexts[bufferIx];
489 assert(offset + size <= bufferCtx.contents.size());
490 return (uint8_t*)bufferCtx.contents.data() + offset;
493 uint32_t recordBufferResource(WriteContext& wctx,
BufferResource* srcBuffer, uint16_t sectionIx)
495 if (srcBuffer ==
nullptr || srcBuffer->
empty()) {
499 if (
auto it = wctx.bufferByPointer.find(srcBuffer); it != wctx.bufferByPointer.end()) {
503 CogsBin3::BufferContentFlags flags = CogsBin3::BufferContentFlags::None;
504 if (srcBuffer->
isVertexBuffer()) flags |= CogsBin3::BufferContentFlags::VertexData;
505 if (srcBuffer->
isIndexBuffer()) flags |= CogsBin3::BufferContentFlags::IndexData;
507 constexpr uint32_t bufferStride = 16;
508 auto alignment = std::min(uint32_t(CogsBin3::CogsSectionAlignment), uint32_t(bufferStride));
509 uint32_t index = recordBuffer(wctx, srcBuffer->
size(), flags, alignment, sectionIx);
510 std::memcpy(getBufferPointer(wctx, index, 0, srcBuffer->
size()), srcBuffer->
data(), srcBuffer->
size());
512 wctx.bufferByPointer[srcBuffer] = index;
519 const auto & srcFormat = *Cogs::VertexFormats::getVertexFormat(srcStream.
format);
520 assert(srcFormat.size <= std::numeric_limits<uint16_t>::max());
521 stream.stride = uint16_t(srcFormat.size);
523 assert(srcFormat.elements.size() < std::numeric_limits<uint32_t>::max());
524 stream.attributeCount = uint32_t(srcFormat.elements.size());
526 auto hash = Cogs::getHash(srcFormat);
528 if (
auto it = wctx.vertexFormatByHash.find(hash); it != wctx.vertexFormatByHash.end() && match(*it->second.format, srcFormat)) {
529 stream.firstAttribute = it->second.index;
532 stream.firstAttribute = recordAttribute(wctx, srcFormat.elements[0]);
533 for (
size_t i = 1; i < srcFormat.elements.size(); i++) {
534 auto ix = recordAttribute(wctx, srcFormat.elements[i]);
535 assert(stream.firstAttribute + i == ix);
537 wctx.vertexFormatByHash[hash] = WVertexFormatRec{ &srcFormat, stream.firstAttribute };
541 uint32_t recordVertexStream(WriteContext& wctx,
const DataStream& srcStream, uint16_t sectionIx)
543 assert(srcStream.size() <= std::numeric_limits<uint32_t>::max());
545 auto index = nextIndex(wctx.vertexStreams);
547 stream.firstAttribute = ~0u;
548 stream.vertexDataType = srcStream.
type;
552 recordVertexFormat(wctx, stream, srcStream);
554 stream.stride = (uint16_t)srcStream.
stride;
557 assert((
size_t)stream.count * (
size_t)stream.stride <= srcStream.size());
560 stream.buffer = recordBufferResource(wctx, srcStream.
buffer.
resolve(), sectionIx);
561 stream.offset = srcStream.
offset;
566 uint32_t recordMesh(WriteContext& wctx,
Mesh* srcMesh, uint16_t section)
568 if (srcMesh ==
nullptr || srcMesh->streams.empty()) {
572 if (
auto it = wctx.meshByPointer.find(srcMesh); it != wctx.meshByPointer.end()) {
573 LOG_DEBUG(logger,
"Duplicate mesh, recycling.");
577 uint32_t firstStream = 0;
578 uint32_t streamCount = 0;
579 for (
size_t i = 0; i < srcMesh->streams.size(); i++) {
580 if (srcMesh->streams[i].size() != 0) {
581 uint32_t streamIx = recordVertexStream(wctx, srcMesh->streams[i], section);
582 if (streamCount == 0) {
583 firstStream = streamIx;
585 assert(firstStream + streamCount == streamIx);
590 auto index = nextIndex(wctx.meshes);
591 auto & mesh = wctx.meshes.emplace_back();
592 mesh.name = recordString(wctx, srcMesh->
getName());
593 mesh.firstStream = firstStream;
594 mesh.streamCount = (uint8_t)streamCount;
595 mesh.boundingBox = recordBoundingBox(wctx, srcMesh->boundingBox);
597 (srcMesh->
isCCW() ? CogsBin3::MeshFlags::None : CogsBin3::MeshFlags::Clockwise) |
598 (srcMesh->
isMeshFlagSet(MeshFlags::Skinned) ? CogsBin3::MeshFlags::Skinned : CogsBin3::MeshFlags::None);
600 mesh.primitiveType = translatePrimitiveType(wctx, srcMesh->primitiveType);
602 wctx.meshByPointer[srcMesh] = index;
608 uint32_t recordProperty(WriteContext& wctx,
const Cogs::StringView& key, CogsBin3::PropertyValueType valueType,
const T & value)
610 auto keyIx = recordString(wctx, key);
614 if (
auto it = wctx.propByHash.find(hash); it != wctx.propByHash.end()) {
615 auto & p = wctx.properties[it->second];
616 if (p.keyType == CogsBin3::PropertyKeyType::String &&
618 p.valueType == valueType)
620 if constexpr (
sizeof(T) <=
sizeof(uint32_t)) {
621 if (*(T*)(&p.value) == value) {
627 if (*(T*)(wctx.propertyData.data() + p.value) == value) {
635 auto index = nextIndex(wctx.properties);
636 wctx.properties.emplace_back(
CogsBin3::Property{ CogsBin3::PropertyKeyType::String, valueType, keyIx, ~0u });
637 auto & prop = wctx.properties.back();
639 if constexpr (
sizeof(T) <=
sizeof(uint32_t)) {
640 *(T*)(&prop.value) = value;
643 constexpr auto align =
alignof(T);
644 static_assert(align != 0 && (align & (align - 1)) == 0 &&
"alignment is not a power of two");
645 const auto mask = align - 1;
646 auto offset = (wctx.propertyData.size() + mask) & ~mask;
647 assert(offset < std::numeric_limits<uint32_t>::max());
648 prop.value = uint32_t(offset);
649 wctx.propertyData.resize(wctx.propertyData.size() +
sizeof(value));
650 *(T*)(wctx.propertyData.data() + offset) = value;
657 auto index = nextIndex(wctx.properties);
658 wctx.properties.emplace_back();
659 auto & prop = wctx.properties.back();
660 prop.keyType = CogsBin3::PropertyKeyType::String;
661 prop.key = recordString(wctx, key);
662 prop.valueType = valueType;
663 prop.value = recordString(wctx, value);
667 uint32_t recordTexture(WriteContext& wctx,
const Texture* srcTex, uint16_t sectionIx)
670 assert(srcTex->storage.data.size() != 0);
672 if (
auto it = wctx.knownTexturesByPointer.find(srcTex); it != wctx.knownTexturesByPointer.end()) {
675 auto texIx = nextIndex(wctx.textures);
677 wctx.knownTexturesByPointer[srcTex] = texIx;
678 auto & dstTex = wctx.textures.back();
681 assert(srcTex->storage.data.size() <= std::numeric_limits<uint32_t>::max());
682 const auto size = uint32_t(srcTex->storage.data.size());
683 dstTex.buffer = recordBuffer(wctx, size, CogsBin3::BufferContentFlags::TextureData, 4, sectionIx);
684 std::memcpy(getBufferPointer(wctx, dstTex.buffer, 0, size), srcTex->storage.data.data(), size);
688 dstTex.encoding = CogsBin3::TextureEncoding::None;
689 dstTex.name = recordString(wctx, srcTex->
getName());
690 dstTex.width = srcTex->description.width;
691 dstTex.height = srcTex->description.height;
692 dstTex.depth = srcTex->description.depth;
693 dstTex.format = translateFormat(srcTex->description.format);
694 dstTex.flags = CogsBin3::TextureFlags::None;
699 uint32_t recordMaterialInstance(WriteContext& wctx,
MaterialInstance* srcMatInst, uint16_t sectionIx)
701 if (srcMatInst ==
nullptr)
return ~0u;
703 if (
auto it = wctx.knownMaterialInstancesByPointer.find(srcMatInst); it != wctx.knownMaterialInstancesByPointer.end()) {
707 auto hash = getMaterialInstanceHash(srcMatInst);
708 if (
auto it = wctx.knownMaterialInstancesByHash.find(hash); it != wctx.knownMaterialInstancesByHash.end()) {
712 auto index = nextIndex(wctx.materialInstances);
713 wctx.materialInstances.emplace_back();
714 auto & dstMatInst = wctx.materialInstances.back();
716 wctx.knownMaterialInstancesByPointer[srcMatInst] = index;
717 wctx.knownMaterialInstancesByHash[hash] = index;
719 dstMatInst.material = recordString(wctx, srcMatInst->
material->
getName());
720 dstMatInst.permutation = recordString(wctx, srcMatInst->getPermutation());
721 dstMatInst.propertyFirst = nextIndex(wctx.properties);
722 dstMatInst.propertyCount = 0;
726 case MaterialDataType::Float:
727 if (
auto val = srcMatInst->
getFloatProperty(prop.key); val != prop.defaultFloat()) {
728 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float, val);
729 dstMatInst.propertyCount++;
732 case MaterialDataType::Float2:
733 if (
auto val = srcMatInst->
getVec2Property(prop.key); val != prop.defaultVec2()) {
734 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float2, val);
735 dstMatInst.propertyCount++;
738 case MaterialDataType::Float3:
739 if (
auto val = srcMatInst->
getVec3Property(prop.key); val != prop.defaultVec3()) {
740 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float3, val);
741 dstMatInst.propertyCount++;
744 case MaterialDataType::Float4:
745 if (
auto val = srcMatInst->
getVec4Property(prop.key); val != prop.defaultVec4()) {
746 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Float4, val);
747 dstMatInst.propertyCount++;
750 case MaterialDataType::Bool:
751 if (
auto val = srcMatInst->
getBoolProperty(prop.key); val != prop.defaultBool()) {
752 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::Bool, (uint32_t)val);
753 dstMatInst.propertyCount++;
756 case MaterialDataType::UInt:
757 if (
auto val = srcMatInst->getProperty<uint32_t>(prop.key); val != prop.defaultUInt()) {
758 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::UInt, (uint32_t)val);
759 dstMatInst.propertyCount++;
763 LOG_WARNING(logger,
"Skipped serialization of unsupported material property %s of type %u.", prop.name.c_str(),
unsigned(prop.type));
769 if ((wctx.flags & WriteModelFlags::EMBED_TEXTURES) != 0) {
770 for (
auto & prop : srcMatInst->
material->textureProperties) {
772 if (value.texture.handle && value.texture.handle != prop.texture.handle) {
773 auto * tex = value.texture.handle.resolve();
774 if (tex->storage.data.size() == 0) {
775 LOG_WARNING(logger,
"Empty texture, skipping.");
778 uint32_t texIx = recordTexture(wctx, tex, sectionIx);
779 recordProperty(wctx, prop.name, CogsBin3::PropertyValueType::EmbeddedTexture, texIx);
780 dstMatInst.propertyCount++;
786 for (uint32_t texI = 0; texI < srcMatInst->
material->textureProperties.size(); texI++) {
791 const std::string dir = Cogs::IO::parentPath(wctx.fileName);
792 if (source.empty()) {
795 if (tex->storage.data.size() == 0) {
796 LOG_WARNING(logger,
"Skipping texture without source or data.");
799 const std::string textureName = Cogs::IO::stem(wctx.fileName) +
"_texture_" + std::to_string(index) + std::to_string(texI) +
".png";
800 source = Cogs::IO::combine(dir, textureName);
801 const Cogs::FormatInfo* formatInfo = getFormatInfo(tex->description.format);
802 if (!stbi_write_png(source.c_str(), tex->description.width, tex->description.height, formatInfo->
elements, tex->storage.data.data(), 0)) {
803 LOG_ERROR(logger,
"Failed to write texture %s.", source.c_str());
807 recordStringBasedProperty(wctx, prop.
name, CogsBin3::PropertyValueType::String, Cogs::IO::relativePath(source, dir));
808 dstMatInst.propertyCount++;
813 const ShaderVariants& variants = srcMatInst->
material->definition.variants;
816 if (variant.isShared)
continue;
817 if (selector.value != variant.defaultValue) {
818 if (variant.type == ShaderVariantType::Enum) {
819 recordStringBasedProperty(wctx, variant.name, CogsBin3::PropertyValueType::Variant, variant.values[selector.value].key);
820 dstMatInst.propertyCount++;
823 recordStringBasedProperty(wctx, variant.name, CogsBin3::PropertyValueType::Variant, selector.value ?
"true" :
"false");
824 dstMatInst.propertyCount++;
829 assert(dstMatInst.propertyFirst + dstMatInst.propertyCount == nextIndex(wctx.properties));
833 uint32_t recordSkeleton(WriteContext& wctx,
const Skeleton& srcSkeleton)
835 if (srcSkeleton.bones.empty())
return ~0u;
837 auto index = nextIndex(wctx.skeletons);
839 auto & dstSkeleton = wctx.skeletons.back();
840 dstSkeleton.bindPose = srcSkeleton.bindPose;
841 dstSkeleton.firstBone = nextIndex(wctx.bones);
842 dstSkeleton.boneCount = uint32_t(srcSkeleton.bones.size());
843 for (
const auto & srcBone : srcSkeleton.bones) {
845 auto & dstBone = wctx.bones.back();
846 dstBone.inverseBindPose = srcBone.inverseBindPose;
847 dstBone.relative = srcBone.relative;
848 dstBone.rot = srcBone.rot;
849 dstBone.pos = srcBone.pos;
850 dstBone.scale = srcBone.scale;
851 dstBone.name = recordString(wctx, srcBone.name);
852 dstBone.parentBone = uint16_t(srcBone.parentBone);
854 (srcBone.hasOffset ? CogsBin3::BoneFlags::HasOffset : CogsBin3::BoneFlags::None) |
855 (srcBone.used ? CogsBin3::BoneFlags::Used : CogsBin3::BoneFlags::None) |
856 (srcBone.animated ? CogsBin3::BoneFlags::Animated : CogsBin3::BoneFlags::None);
861 uint32_t recordPart(WriteContext& wctx,
const Model& model,
const ModelPart& part, uint16_t section)
863 auto nodeIndex = newNode(wctx);
864 auto & node = wctx.nodes[nodeIndex];
866 node.parent = part.parentIndex;
867 if (part.nameIndex != -1) {
868 node.name = recordString(wctx, model.getPartName(part));
870 if (part.transformIndex != -1) {
871 node.transform = recordTransform(wctx, model.getPartTransform(part));
874 if (part.meshIndex != -1) {
875 auto * mesh = model.meshes[part.meshIndex].resolve();
876 node.mesh = recordMesh(wctx, mesh, 0);
877 node.boundingBox = recordBoundingBox(wctx, mesh->boundingBox);
878 node.primitiveType = translatePrimitiveType(wctx, mesh->primitiveType);
879 node.vertexFirst = part.startIndex;
880 node.vertexCount = part.vertexCount;
881 if (part.materialIndex != -1) {
882 node.materialInstance = recordMaterialInstance(wctx, model.materials[part.materialIndex].resolve(), section);
889 void recordAnimTrack(WriteContext& wctx,
const AnimationTrack& srcTrack, uint16_t sectionIx)
892 auto & dstTrack = wctx.animTracks.back();
894 const auto translationCount = uint32_t(srcTrack.translations.size());
895 const auto rotationCount = uint32_t(srcTrack.rotations.size());
896 const auto scaleCount = uint32_t(srcTrack.scales.size());
898 dstTrack.boneIndex = uint32_t(srcTrack.
boneIndex);
899 dstTrack.buffer = recordBuffer(wctx,
900 sizeof(
float)*(4 * (
size_t)translationCount + 5 * (
size_t)rotationCount + 4 * (
size_t)scaleCount),
901 CogsBin3::BufferContentFlags::AnimationData, 4, sectionIx);
903 auto * base = (
float*)wctx.bufferContexts[dstTrack.buffer].contents.data();
906 dstTrack.translationOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
907 for(
auto & translation : srcTrack.translations) {
908 *ptr++ = translation.t;
909 *ptr++ = translation.value[0];
910 *ptr++ = translation.value[1];
911 *ptr++ = translation.value[2];
913 dstTrack.translationCount = translationCount;
914 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.translationOffset ==
sizeof(
float) * 4 * translationCount);
916 dstTrack.rotationOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
917 for (
auto & rotation : srcTrack.rotations) {
919 *ptr++ = rotation.value[0];
920 *ptr++ = rotation.value[1];
921 *ptr++ = rotation.value[2];
922 *ptr++ = rotation.value[3];
924 dstTrack.rotationCount = rotationCount;
925 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.rotationOffset ==
sizeof(
float) * 5 * rotationCount);
927 dstTrack.scaleOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
928 for (
auto & scale : srcTrack.scales) {
930 *ptr++ = scale.value[0];
931 *ptr++ = scale.value[1];
932 *ptr++ = scale.value[2];
934 dstTrack.scaleCount = scaleCount;
935 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.scaleOffset ==
sizeof(
float) * 4 * scaleCount);
936 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) ==
sizeof(
float) * (4 * translationCount + 5 * rotationCount + 4 * scaleCount));
939 void recordAnimClip(WriteContext& wctx,
const AnimationClip& srcClip, uint16_t sectionIx)
942 auto & dstClip = wctx.animClips.back();
943 dstClip.name = recordString(wctx, srcClip.name);
944 dstClip.duration = srcClip.duration;
945 dstClip.resolution = srcClip.resolution;
946 if (!srcClip.tracks.empty()) {
947 dstClip.trackFirst = nextIndex(wctx.animTracks);
948 dstClip.trackCount = uint32_t(srcClip.tracks.size());
949 for (
auto const & track : srcClip.tracks) recordAnimTrack(wctx, track, sectionIx);
950 assert(dstClip.trackFirst + dstClip.trackCount == wctx.animTracks.size());
953 dstClip.trackFirst = ~0u;
954 dstClip.trackCount = 0;
958 void recordModel(WriteContext& wctx,
const Model * srcModel, WriteModelFlags )
960 if (srcModel->parts.empty())
return;
962 uint16_t section = 0;
965 auto & dstModel = wctx.models.back();
966 dstModel.skeleton = (uint32_t)-1;
967 dstModel.animClipFirst = (uint32_t)-1;
968 dstModel.animClipCount = 0;
969 if (
auto * animation = srcModel->animation.
resolve(); animation) {
970 dstModel.skeleton = recordSkeleton(wctx, animation->skeleton);
971 if (!animation->clips.empty()) {
972 dstModel.animClipFirst = nextIndex(wctx.animClips);
973 dstModel.animClipCount = uint32_t(animation->clips.size());
974 for (
const auto & clip : animation->clips) recordAnimClip(wctx, clip, section);
975 assert(dstModel.animClipFirst + dstModel.animClipCount == wctx.animClips.size());
979 dstModel.firstNode = recordPart(wctx, *srcModel, srcModel->parts[0], section);
980 for (
size_t i = 1; i < srcModel->parts.size(); i++) {
981 auto ix = recordPart(wctx, *srcModel, srcModel->parts[i], 0);
982 assert(dstModel.firstNode + i == ix);
984 dstModel.nodeCount = uint32_t(srcModel->parts.size());
989 auto nodeIndex = newNode(wctx);
990 auto & node = wctx.nodes[nodeIndex];
992 node.parent = parent;
993 node.name = recordString(wctx, entity->
getName());
996 node.transform = recordTransform(wctx, context->transformSystem->getLocalTransform(trComp));
1000 auto mesh = meshComponent->meshHandle;
1001 if (mesh && mesh->streams.size()) {
1003 if (renderComponent) {
1004 node.mesh = recordMesh(wctx, mesh.resolve(), section);
1006 node.primitiveType = translatePrimitiveType(wctx, renderComponent->primitiveType);
1008 node.primitiveType = CogsBin3::PrimitiveType::Unknown;
1010 node.vertexFirst = renderComponent->startIndex;
1011 node.vertexCount = renderComponent->vertexCount;
1012 node.boundingBox = recordBoundingBox(wctx, context->renderSystem->getLocalBounds(renderComponent));
1013 if (renderComponent->material) {
1014 node.materialInstance = recordMaterialInstance(wctx, renderComponent->material.resolve(), section);
1018 LOG_ERROR(logger,
"MeshComponent must be accompanied by a MeshRenderComponent for export.");
1024 for (
auto & child : sceneComponent->children) {
1025 recordEntity(context, wctx, nodeIndex, child.get(), section);
1031 auto * anim = animComp->animation.resolve();
1032 if (animComp->master) {
1036 if (wctx.currentAnimation ==
nullptr) {
1037 wctx.currentAnimation = anim;
1039 else if (wctx.currentAnimation != anim) {
1040 LOG_ERROR(logger,
"Multiple animations within a single model's hierarchy");
1047 void layoutSections(WriteContext& wctx)
1049 for (
size_t i = 0; i < wctx.sections.size(); i++) {
1050 auto & section = *wctx.sections[i].get();
1052 assert(section.contents.empty() &&
"Sections already laid out");
1056 for (
auto & subsection : section.subSections) {
1057 section.writePos = align(section.writePos);
1058 assert(section.writePos < std::numeric_limits<uint32_t>::max());
1059 subsection.offset = section.writePos;
1060 section.writePos += subsection.size;
1062 section.writePos = align(section.writePos);
1065 section.contents.resize(section.writePos + section.dataSize);
1066 LOG_TRACE(logger,
"Section %zu allocated %zu bytes, data offset=%zu", i, section.contents.size(), section.writePos);
1070 for (
auto & subsection : section.subSections) {
1071 LOG_TRACE(logger,
" Subsection offset=%zu, size=%u, type=%u", subsection.offset, subsection.size,
unsigned(subsection.type));
1072 ptr->offset = subsection.offset;
1073 ptr->size = subsection.size;
1074 ptr->type = subsection.type;
1081 template<
typename T>
1082 void fillSubsection(WSection& section, uint32_t index,
const std::vector<T>& source)
1084 if (index == ~0u)
return;
1085 auto & subSection = section.subSections[index];
1086 assert(
sizeof(T)*source.size() == subSection.size);
1088 std::memcpy(section.contents.data() + subSection.offset, source.data(), subSection.size);
1091 bool pack(
Context* , WriteContext& wctx)
1094 auto & metaSection = getSection(wctx, CogsBin3::SectionType::META, 0);
1096 uint32_t subSections[size_t(CogsBin3::SubSectionType::EnumSize)];
1097 for (
auto & subSection : subSections) subSection = (uint32_t)-1;
1099#define REGISTER(T,A) if(!(A).empty()) subSections[size_t(T)] = addMetaSubsection(wctx, metaSection, T, sizeof((A)[0])*((A).size()))
1100 REGISTER(CogsBin3::SubSectionType::Buffers, wctx.buffers);
1101 REGISTER(CogsBin3::SubSectionType::Textures, wctx.textures);
1102 REGISTER(CogsBin3::SubSectionType::Strings, wctx.strings);
1103 REGISTER(CogsBin3::SubSectionType::StringData, wctx.stringData);
1104 REGISTER(CogsBin3::SubSectionType::Nodes, wctx.nodes);
1105 REGISTER(CogsBin3::SubSectionType::Transforms, wctx.transforms);
1106 REGISTER(CogsBin3::SubSectionType::BoundingBoxes, wctx.boundingBoxes);
1107 REGISTER(CogsBin3::SubSectionType::MaterialInstances, wctx.materialInstances);
1108 REGISTER(CogsBin3::SubSectionType::Properties, wctx.properties);
1109 REGISTER(CogsBin3::SubSectionType::PropertyData, wctx.propertyData);
1110 REGISTER(CogsBin3::SubSectionType::Meshes, wctx.meshes);
1111 REGISTER(CogsBin3::SubSectionType::VertexStreams, wctx.vertexStreams);
1112 REGISTER(CogsBin3::SubSectionType::VertexAttributes, wctx.vertexAttributes);
1113 REGISTER(CogsBin3::SubSectionType::Bones, wctx.bones);
1114 REGISTER(CogsBin3::SubSectionType::Skeletons, wctx.skeletons);
1115 REGISTER(CogsBin3::SubSectionType::AnimTracks, wctx.animTracks);
1116 REGISTER(CogsBin3::SubSectionType::AnimClips, wctx.animClips);
1117 REGISTER(CogsBin3::SubSectionType::Models, wctx.models);
1120 layoutSections(wctx);
1123 for (
size_t i = 0; i < wctx.buffers.size(); i++) {
1124 auto & buffer = wctx.buffers[i];
1125 const auto & bufCtx = wctx.bufferContexts[i];
1126 const auto lowerMask = uint64_t(bufCtx.alignment - 1);
1128 const auto sectionIx = buffer.sectionOffset;
1129 auto & section = getSection(wctx, CogsBin3::SectionType::DATA, uint16_t(sectionIx),
false);
1130 auto offset = (section.writePos + lowerMask) & (~lowerMask);
1131 buffer.sectionOffset = offset | (sectionIx << 48);
1133 assert(offset + buffer.size <= section.contents.size());
1134 std::memcpy(section.contents.data() + offset, bufCtx.contents.data(), buffer.size);
1135 section.writePos = offset + buffer.size;
1139 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Buffers)], wctx.buffers);
1140 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Textures)], wctx.textures);
1141 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Strings)], wctx.strings);
1142 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::StringData)], wctx.stringData);
1143 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Nodes)], wctx.nodes);
1144 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Transforms)], wctx.transforms);
1145 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::BoundingBoxes)], wctx.boundingBoxes);
1146 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::MaterialInstances)], wctx.materialInstances);
1147 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Properties)], wctx.properties);
1148 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::PropertyData)], wctx.propertyData);
1149 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Meshes)], wctx.meshes);
1150 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::VertexStreams)], wctx.vertexStreams);
1151 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::VertexAttributes)], wctx.vertexAttributes);
1152 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Bones)], wctx.bones);
1153 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Skeletons)], wctx.skeletons);
1154 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::AnimTracks)], wctx.animTracks);
1155 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::AnimClips)], wctx.animClips);
1156 fillSubsection(metaSection, subSections[
size_t(CogsBin3::SubSectionType::Models)], wctx.models);
1161#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1162#pragma optimize( "", on )
1164 bool compressSections(
Context* , WriteContext& wctx)
1166 wctx.sectionInfos.resize(wctx.sections.size());
1167 for (
auto & section : wctx.sections) {
1168 auto & info = wctx.sectionInfos[section->index];
1169 info.fileSize = section->contents.size();
1170 info.uncompressedSize = info.fileSize;
1171 info.compression = CogsBin3::Compression::None;
1172 info.type = section->type;
1173 info.subSectionCount = uint16_t(section->subSections.size());
1177 if ((wctx.flags & WriteModelFlags::COMPRESS_ZSTD) != 0) {
1179 int compressionLevel = ZSTD_CLEVEL_DEFAULT;
1180 if ((wctx.flags & WriteModelFlags::COMPRESS_MAX) != 0) {
1181 compressionLevel = ZSTD_maxCLevel();
1183 compressionLevel = wctx.settings.compressionLevel;
1187 ZSTD_CCtx* zstd_ctx = ZSTD_createCCtx();
1188 if (zstd_ctx ==
nullptr) {
1189 LOG_ERROR(logger,
"Failed to create zstd context.");
1193 std::vector<uint8_t> temp;
1194 for (
auto & section : wctx.sections) {
1195 auto & info = wctx.sectionInfos[section->index];
1196 auto maxSize = ZSTD_compressBound(info.uncompressedSize);
1197 temp.resize(maxSize);
1200 auto result = ZSTD_compressCCtx(zstd_ctx, temp.data(), maxSize, section->contents.data(), info.uncompressedSize, compressionLevel);
1201 if (ZSTD_isError(result)) {
1202 LOG_ERROR(logger,
"zstd compression failed: %s", ZSTD_getErrorName(result));
1205 else if (info.uncompressedSize <= result) {
1211 temp.resize(result);
1212 info.compression = CogsBin3::Compression::ZSTD;
1213 info.fileSize = result;
1214 section->contents.swap(temp);
1217 ZSTD_freeCCtx(zstd_ctx);
1224#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1225#pragma optimize( "", off )
1228 bool write(
Context* context, WriteContext& wctx)
1230 pack(context, wctx);
1232 compressSections(context, wctx);
1238 header.magic = CogsBin3::HeaderMagic;
1239 header.version = CogsBin3::Version;
1241 header.sectionCount = nextIndex(wctx.sectionInfos);
1242 for (
auto & info : wctx.sectionInfos) {
1243 if (info.compression == CogsBin3::Compression::None) {
1245 header.fileLength = (header.fileLength + (CogsBin3::CogsSectionAlignment - 1)) & ~(CogsBin3::CogsSectionAlignment - 1);
1247 info.fileOffset = header.fileLength;
1248 header.fileLength += info.fileSize;
1249 uncompressedSize += info.uncompressedSize;
1253 FILE * file = fopen(wctx.fileName.c_str(),
"wb");
1254 if (file ==
nullptr) {
1255 LOG_ERROR(logger,
"Failed to open \"%s\" for writing.", wctx.fileName.c_str());
1263 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1271 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1276 for (uint32_t i = 0; i < header.sectionCount; i++) {
1277 auto & info = wctx.sectionInfos[i];
1278 auto & section = *wctx.sections[i].get();
1281 auto pos = uint64_t(_ftelli64(file));
1283 auto pos = uint64_t(ftell(file));
1285 assert(pos <= info.fileOffset);
1286 if (pos != info.fileOffset) {
1288 _fseeki64(file, info.fileOffset, SEEK_SET);
1290 fseek(file, info.fileOffset, SEEK_SET);
1294 r = fwrite(section.contents.data(), info.fileSize, 1, file);
1297 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1303 auto fileLength = _ftelli64(file);
1305 auto fileLength = ftell(file);
1307 assert(fileLength == (
long long)header.fileLength);
1308 LOG_TRACE(logger,
"Wrote %zu bytes (=%zu uncompressed, ratio=%.1f%%) (%u sections)",
1310 size_t(uncompressedSize),
1311 (100.f*
float(fileLength))/
float(uncompressedSize),
1312 unsigned(header.sectionCount));
1321 auto flags = settings->flags;
1323 if (!model)
return false;
1325 WriteContext wctx{};
1326 wctx.fileName = fileName.to_string();
1327 wctx.flags = WriteModelFlags(flags);
1329 wctx.settings = *settings;
1331 recordModel(wctx, model, flags);
1332 write(context, wctx);
1333 numVertes = wctx.numVertes;
1334 numIndexes = wctx.numIndexes;
1338bool Cogs::Core::writeCogsBin3Models(
Context * context, uint32_t& numVertes, uint32_t& numIndexes,
const StringView & fileName,
ComponentModel::Entity ** entity,
size_t N, WriteModelFlags flags)
1340 if (!entity || N == 0)
return false;
1342 uint16_t section = 0;
1343 WriteContext wctx{};
1344 wctx.fileName = fileName.to_string();
1345 wctx.flags = WriteModelFlags(flags);
1346 wctx.currentAnimation =
nullptr;
1348 auto & dstModel = wctx.models.back();
1350 dstModel.firstNode = recordEntity(context, wctx, ~0u, entity[0], section);
1351 for (
size_t i = 1; i < N; i++) {
1352 recordEntity(context, wctx, ~0u, entity[i], section);
1354 dstModel.nodeCount = uint32_t(wctx.nodes.size() - dstModel.firstNode);
1356 if (wctx.currentAnimation) {
1357 dstModel.skeleton = recordSkeleton(wctx, wctx.currentAnimation->skeleton);
1358 dstModel.animClipFirst = nextIndex(wctx.animClips);
1359 dstModel.animClipCount = uint32_t(wctx.currentAnimation->clips.size());
1360 for (
const auto & clip : wctx.currentAnimation->clips) recordAnimClip(wctx, clip, section);
1361 assert(dstModel.animClipFirst + dstModel.animClipCount == wctx.animClips.size());
1364 dstModel.skeleton = ~0u;
1365 dstModel.animClipFirst = ~0u;
1366 dstModel.animClipCount = 0;
1369 write(context, wctx);
1370 numVertes = wctx.numVertes;
1371 numIndexes = wctx.numIndexes;
1376#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1377#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.
@ 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.
EPrimitiveType
Primitive type enumeration.
@ LineStripAdjacency
Line strip with adjacency.
@ TriangleStrip
Triangle strip.
@ TriangleListAdjacency
List of triangles with adjacency.
@ PointList
List of points.
@ TriangleStripAdjacency
Triangle strip with adjacency.
@ LineListAdjacency
List of lines with adjacency.
@ TriangleList
List of triangles.
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.