1#include "ModelWriter.h"
4#include "Services/Variables.h"
6#include "Components/Core/TransformComponent.h"
7#include "Components/Core/SceneComponent.h"
8#include "Components/Core/AnimationComponent.h"
9#include "Components/Core/MeshComponent.h"
10#include "Components/Core/MeshRenderComponent.h"
12#include "Systems/Core/TransformSystem.h"
13#include "Systems/Core/RenderSystem.h"
15#include "Resources/Buffer.h"
16#include "Resources/Mesh.h"
17#include "Resources/MaterialInstance.h"
18#include "Resources/Texture.h"
19#include "Resources/Model.h"
20#include "Resources/Animation.h"
21#include "Resources/DefaultMaterial.h"
23#include "Rendering/SamplerState.h"
25#include "Foundation/HashSequence.h"
26#include "Foundation/Logging/Logger.h"
27#include "Foundation/Memory/MemoryBuffer.h"
28#include "Foundation/Platform/IO.h"
29#include "Foundation/Platform/Timer.h"
30#include "Foundation/Reflection/TypeDatabase.h"
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 CogsBin::SubSectionType type;
75 CogsBin::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::CogsBin::Buffer> buffers;
115 std::vector<Cogs::Core::CogsBin::ExternalTexture> externalTextures;
116 std::vector<Cogs::Core::CogsBin::EmbeddedTexture> embeddedTextures;
117 std::vector<Cogs::Core::CogsBin::SamplerState> samplerStates;
118 std::vector<Cogs::Core::CogsBin::String> strings;
119 std::vector<Cogs::Core::CogsBin::Property> properties;
120 std::vector<Cogs::Core::CogsBin::MaterialInstance> materialInstances;
121 std::vector<Cogs::Core::CogsBin::Transform> transforms;
122 std::vector<Cogs::Geometry::BoundingBox> boundingBoxes;
123 std::vector<Cogs::Core::CogsBin::Node> nodes;
124 std::vector<Cogs::Core::CogsBin::Mesh> meshes;
125 std::vector<Cogs::Core::CogsBin::VertexStream> vertexStreams;
126 std::vector<Cogs::Core::CogsBin::VertexAttribute> vertexAttributes;
127 std::vector<Cogs::Core::CogsBin::Bone> bones;
128 std::vector<Cogs::Core::CogsBin::Skeleton> skeletons;
129 std::vector<Cogs::Core::CogsBin::Model> models;
130 std::vector<Cogs::Core::CogsBin::AnimTrack> animTracks;
131 std::vector<Cogs::Core::CogsBin::AnimClip> animClips;
133 std::vector<CogsBin::SectionInfo> sectionInfos;
135 std::map<const Cogs::Core::Texture*, uint32_t> knownTexturesByPointer;
136 std::map<const Cogs::Core::MaterialInstance*, uint32_t> knownMaterialInstancesByPointer;
137 std::map<size_t, uint32_t> knownMaterialInstancesByHash;
138 std::map<size_t, uint32_t> propByHash;
139 std::map<size_t, uint32_t> stringByHash;
140 std::map<size_t, uint32_t> transformByHash;
141 std::map<size_t, uint32_t> boundingBoxByHash;
142 std::map<const Cogs::Core::Mesh*, uint32_t> meshByPointer;
143 std::map<const Cogs::Core::BufferResource*, uint32_t> bufferByPointer;
144 std::map<size_t, WVertexFormatRec> vertexFormatByHash;
147 template<
typename T,
typename S>
148 T checkedCast(
const S v)
150 assert(v <= std::numeric_limits<T>::max());
154 inline uint64_t align(uint64_t x)
156 const uint64_t m = CogsBin::CogsSectionAlignment - 1;
162 switch (primitiveType) {
172 case Cogs::PrimitiveType::ControlPoint1PatchList:
return CogsBin::PrimitiveType::ControlPoint1PatchList;
break;
173 case Cogs::PrimitiveType::ControlPoint2PatchList:
return CogsBin::PrimitiveType::ControlPoint2PatchList;
break;
174 case Cogs::PrimitiveType::ControlPoint3PatchList:
return CogsBin::PrimitiveType::ControlPoint3PatchList;
break;
175 case Cogs::PrimitiveType::ControlPoint4PatchList:
return CogsBin::PrimitiveType::ControlPoint4PatchList;
break;
179 assert(
false &&
"Illegal primitive type encountered");
180 return CogsBin::PrimitiveType::TriangleList;
183 CogsBin::Format translateFormat(Cogs::TextureFormat format)
186 case Cogs::TextureFormat::Unknown:
return CogsBin::Format::Unknown;
break;
187 case Cogs::TextureFormat::R8_UNORM:
return CogsBin::Format::R8_UNORM;
break;
188 case Cogs::TextureFormat::R8G8_UNORM:
return CogsBin::Format::R8G8_UNORM;
break;
189 case Cogs::TextureFormat::R8G8B8_UNORM:
return CogsBin::Format::R8G8B8_UNORM;
break;
190 case Cogs::TextureFormat::R8G8B8A8_UNORM:
return CogsBin::Format::R8G8B8A8_UNORM;
break;
191 case Cogs::TextureFormat::R16_UNORM:
return CogsBin::Format::R16_UNORM;
break;
192 case Cogs::TextureFormat::R16G16_UNORM:
return CogsBin::Format::R16G16_UNORM;
break;
193 case Cogs::TextureFormat::R16G16B16_UNORM:
return CogsBin::Format::R16G16B16_UNORM;
break;
194 case Cogs::TextureFormat::R16G16B16A16_UNORM:
return CogsBin::Format::R16G16B16A16_UNORM;
break;
195 case Cogs::TextureFormat::R8_SNORM:
return CogsBin::Format::R8_SNORM;
break;
196 case Cogs::TextureFormat::R8G8_SNORM:
return CogsBin::Format::R8G8_SNORM;
break;
197 case Cogs::TextureFormat::R8G8B8_SNORM:
return CogsBin::Format::R8G8B8_SNORM;
break;
198 case Cogs::TextureFormat::R8G8B8A8_SNORM:
return CogsBin::Format::R8G8B8A8_SNORM;
break;
199 case Cogs::TextureFormat::R16_SNORM:
return CogsBin::Format::R16_SNORM;
break;
200 case Cogs::TextureFormat::R16G16_SNORM:
return CogsBin::Format::R16G16_SNORM;
break;
201 case Cogs::TextureFormat::R16G16B16_SNORM:
return CogsBin::Format::R16G16B16_SNORM;
break;
202 case Cogs::TextureFormat::R16G16B16A16_SNORM:
return CogsBin::Format::R16G16B16A16_SNORM;
break;
203 case Cogs::TextureFormat::R8_UINT:
return CogsBin::Format::R8_UINT;
break;
204 case Cogs::TextureFormat::R8G8_UINT:
return CogsBin::Format::R8G8_UINT;
break;
205 case Cogs::TextureFormat::R8G8B8_UINT:
return CogsBin::Format::R8G8B8_UINT;
break;
206 case Cogs::TextureFormat::R8G8B8A8_UINT:
return CogsBin::Format::R8G8B8A8_UINT;
break;
207 case Cogs::TextureFormat::R16_UINT:
return CogsBin::Format::R16_UINT;
break;
208 case Cogs::TextureFormat::R16G16_UINT:
return CogsBin::Format::R16G16_UINT;
break;
209 case Cogs::TextureFormat::R16G16B16_UINT:
return CogsBin::Format::R16G16B16_UINT;
break;
210 case Cogs::TextureFormat::R16G16B16A16_UINT:
return CogsBin::Format::R16G16B16A16_UINT;
break;
211 case Cogs::TextureFormat::R32_UINT:
return CogsBin::Format::R32_UINT;
break;
212 case Cogs::TextureFormat::R32G32_UINT:
return CogsBin::Format::R32G32_UINT;
break;
213 case Cogs::TextureFormat::R32G32B32_UINT:
return CogsBin::Format::R32G32B32_UINT;
break;
214 case Cogs::TextureFormat::R32G32B32A32_UINT:
return CogsBin::Format::R32G32B32A32_UINT;
break;
215 case Cogs::TextureFormat::R8_SINT:
return CogsBin::Format::R8_SINT;
break;
216 case Cogs::TextureFormat::R8G8_SINT:
return CogsBin::Format::R8G8_SINT;
break;
217 case Cogs::TextureFormat::R8G8B8_SINT:
return CogsBin::Format::R8G8B8_SINT;
break;
218 case Cogs::TextureFormat::R8G8B8A8_SINT:
return CogsBin::Format::R8G8B8A8_SINT;
break;
219 case Cogs::TextureFormat::R16_SINT:
return CogsBin::Format::R16_SINT;
break;
220 case Cogs::TextureFormat::R16G16_SINT:
return CogsBin::Format::R16G16_SINT;
break;
221 case Cogs::TextureFormat::R16G16B16_SINT:
return CogsBin::Format::R16G16B16_SINT;
break;
222 case Cogs::TextureFormat::R16G16B16A16_SINT:
return CogsBin::Format::R16G16B16A16_SINT;
break;
223 case Cogs::TextureFormat::R32_SINT:
return CogsBin::Format::R32_SINT;
break;
224 case Cogs::TextureFormat::R32G32_SINT:
return CogsBin::Format::R32G32_SINT;
break;
225 case Cogs::TextureFormat::R32G32B32_SINT:
return CogsBin::Format::R32G32B32_SINT;
break;
226 case Cogs::TextureFormat::R32G32B32A32_SINT:
return CogsBin::Format::R32G32B32A32_SINT;
break;
227 case Cogs::TextureFormat::R16_FLOAT:
return CogsBin::Format::R16_FLOAT;
break;
228 case Cogs::TextureFormat::R16G16_FLOAT:
return CogsBin::Format::R16G16_FLOAT;
break;
229 case Cogs::TextureFormat::R16G16B16_FLOAT:
return CogsBin::Format::R16G16B16_FLOAT;
break;
230 case Cogs::TextureFormat::R16G16B16A16_FLOAT:
return CogsBin::Format::R16G16B16A16_FLOAT;
break;
231 case Cogs::TextureFormat::R32_FLOAT:
return CogsBin::Format::R32_FLOAT;
break;
232 case Cogs::TextureFormat::R32G32_FLOAT:
return CogsBin::Format::R32G32_FLOAT;
break;
233 case Cogs::TextureFormat::R32G32B32_FLOAT:
return CogsBin::Format::R32G32B32_FLOAT;
break;
234 case Cogs::TextureFormat::R32G32B32A32_FLOAT:
return CogsBin::Format::R32G32B32A32_FLOAT;
break;
235 case Cogs::TextureFormat::D16_UNORM:
return CogsBin::Format::D16_UNORM;
break;
236 case Cogs::TextureFormat::D24_UNORM:
return CogsBin::Format::D24_UNORM;
break;
237 case Cogs::TextureFormat::D24S8_UNORM:
return CogsBin::Format::D24S8_UNORM;
break;
238 case Cogs::TextureFormat::D32_FLOAT:
return CogsBin::Format::D32_FLOAT;
break;
239 case Cogs::TextureFormat::R32_TYPELESS:
return CogsBin::Format::R32_TYPELESS;
break;
240 case Cogs::TextureFormat::R16_TYPELESS:
return CogsBin::Format::R16_TYPELESS;
break;
241 case Cogs::TextureFormat::R8T:
return CogsBin::Format::R8T;
break;
242 case Cogs::TextureFormat::R8G8T:
return CogsBin::Format::R8G8T;
break;
243 case Cogs::TextureFormat::R8G8B8T:
return CogsBin::Format::R8G8B8T;
break;
244 case Cogs::TextureFormat::R8G8B8A8T:
return CogsBin::Format::R8G8B8A8T;
break;
245 case Cogs::TextureFormat::B8G8R8:
return CogsBin::Format::B8G8R8;
break;
246 case Cogs::TextureFormat::B8G8R8A8:
return CogsBin::Format::B8G8R8A8;
break;
247 case Cogs::TextureFormat::A8_UNORM:
return CogsBin::Format::A8_UNORM;
break;
248 case Cogs::TextureFormat::BC1_TYPELESS:
return CogsBin::Format::BC1_TYPELESS;
break;
249 case Cogs::TextureFormat::BC1_UNORM:
return CogsBin::Format::BC1_UNORM;
break;
250 case Cogs::TextureFormat::BC1_UNORM_SRGB:
return CogsBin::Format::BC1_UNORM_SRGB;
break;
251 case Cogs::TextureFormat::BC2_TYPELESS:
return CogsBin::Format::BC2_TYPELESS;
break;
252 case Cogs::TextureFormat::BC2_UNORM:
return CogsBin::Format::BC2_UNORM;
break;
253 case Cogs::TextureFormat::BC2_UNORM_SRGB:
return CogsBin::Format::BC2_UNORM_SRGB;
break;
254 case Cogs::TextureFormat::BC3_TYPELESS:
return CogsBin::Format::BC3_TYPELESS;
break;
255 case Cogs::TextureFormat::BC3_UNORM:
return CogsBin::Format::BC3_UNORM;
break;
256 case Cogs::TextureFormat::BC3_UNORM_SRGB:
return CogsBin::Format::BC3_UNORM_SRGB;
break;
257 case Cogs::TextureFormat::BC4_TYPELESS:
return CogsBin::Format::BC4_TYPELESS;
break;
258 case Cogs::TextureFormat::BC4_UNORM:
return CogsBin::Format::BC4_UNORM;
break;
259 case Cogs::TextureFormat::BC4_SNORM:
return CogsBin::Format::BC4_SNORM;
break;
260 case Cogs::TextureFormat::BC5_TYPELESS:
return CogsBin::Format::BC5_TYPELESS;
break;
261 case Cogs::TextureFormat::BC5_UNORM:
return CogsBin::Format::BC5_UNORM;
break;
262 case Cogs::TextureFormat::BC5_SNORM:
return CogsBin::Format::BC5_SNORM;
break;
263 case Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB:
return CogsBin::Format::R8G8B8A8_UNORM_SRGB;
break;
265 assert(
false &&
"Illegal format");
268 return CogsBin::Format::Unknown;
271 CogsBin::DataFormatV5 translateDataFormat(Cogs::DataFormat fmt)
274 case Cogs::DataFormat::Unknown:
return CogsBin::DataFormatV5::Unknown;
275 case Cogs::DataFormat::R8_UNORM:
return CogsBin::DataFormatV5::R8_UNORM;
276 case Cogs::DataFormat::R8G8_UNORM:
return CogsBin::DataFormatV5::R8G8_UNORM;
277 case Cogs::DataFormat::R8G8B8_UNORM:
return CogsBin::DataFormatV5::R8G8B8_UNORM;
278 case Cogs::DataFormat::R8G8B8A8_UNORM:
return CogsBin::DataFormatV5::R8G8B8A8_UNORM;
279 case Cogs::DataFormat::R16_UNORM:
return CogsBin::DataFormatV5::R16_UNORM;
280 case Cogs::DataFormat::R16G16_UNORM:
return CogsBin::DataFormatV5::R16G16_UNORM;
281 case Cogs::DataFormat::R16G16B16_UNORM:
return CogsBin::DataFormatV5::R16G16B16_UNORM;
282 case Cogs::DataFormat::R16G16B16A16_UNORM:
return CogsBin::DataFormatV5::R16G16B16A16_UNORM;
283 case Cogs::DataFormat::R8_SNORM:
return CogsBin::DataFormatV5::R8_SNORM;
284 case Cogs::DataFormat::R8G8_SNORM:
return CogsBin::DataFormatV5::R8G8_SNORM;
285 case Cogs::DataFormat::R8G8B8_SNORM:
return CogsBin::DataFormatV5::R8G8B8_SNORM;
286 case Cogs::DataFormat::R8G8B8A8_SNORM:
return CogsBin::DataFormatV5::R8G8B8A8_SNORM;
287 case Cogs::DataFormat::R16_SNORM:
return CogsBin::DataFormatV5::R16_SNORM;
288 case Cogs::DataFormat::R16G16_SNORM:
return CogsBin::DataFormatV5::R16G16_SNORM;
289 case Cogs::DataFormat::R16G16B16_SNORM:
return CogsBin::DataFormatV5::R16G16B16_SNORM;
290 case Cogs::DataFormat::R16G16B16A16_SNORM:
return CogsBin::DataFormatV5::R16G16B16A16_SNORM;
291 case Cogs::DataFormat::R8_UINT:
return CogsBin::DataFormatV5::R8_UINT;
292 case Cogs::DataFormat::R8G8_UINT:
return CogsBin::DataFormatV5::R8G8_UINT;
293 case Cogs::DataFormat::R8G8B8_UINT:
return CogsBin::DataFormatV5::R8G8B8_UINT;
294 case Cogs::DataFormat::R8G8B8A8_UINT:
return CogsBin::DataFormatV5::R8G8B8A8_UINT;
295 case Cogs::DataFormat::R16_UINT:
return CogsBin::DataFormatV5::R16_UINT;
296 case Cogs::DataFormat::R16G16_UINT:
return CogsBin::DataFormatV5::R16G16_UINT;
297 case Cogs::DataFormat::R16G16B16_UINT:
return CogsBin::DataFormatV5::R16G16B16_UINT;
298 case Cogs::DataFormat::R16G16B16A16_UINT:
return CogsBin::DataFormatV5::R16G16B16A16_UINT;
299 case Cogs::DataFormat::R32_UINT:
return CogsBin::DataFormatV5::R32_UINT;
300 case Cogs::DataFormat::R32G32_UINT:
return CogsBin::DataFormatV5::R32G32_UINT;
301 case Cogs::DataFormat::R32G32B32_UINT:
return CogsBin::DataFormatV5::R32G32B32_UINT;
302 case Cogs::DataFormat::R32G32B32A32_UINT:
return CogsBin::DataFormatV5::R32G32B32A32_UINT;
303 case Cogs::DataFormat::R8_SINT:
return CogsBin::DataFormatV5::R8_SINT;
304 case Cogs::DataFormat::R8G8_SINT:
return CogsBin::DataFormatV5::R8G8_SINT;
305 case Cogs::DataFormat::R8G8B8_SINT:
return CogsBin::DataFormatV5::R8G8B8_SINT;
306 case Cogs::DataFormat::R8G8B8A8_SINT:
return CogsBin::DataFormatV5::R8G8B8A8_SINT;
307 case Cogs::DataFormat::R16_SINT:
return CogsBin::DataFormatV5::R16_SINT;
308 case Cogs::DataFormat::R16G16_SINT:
return CogsBin::DataFormatV5::R16G16_SINT;
309 case Cogs::DataFormat::R16G16B16_SINT:
return CogsBin::DataFormatV5::R16G16B16_SINT;
310 case Cogs::DataFormat::R16G16B16A16_SINT:
return CogsBin::DataFormatV5::R16G16B16A16_SINT;
311 case Cogs::DataFormat::R32_SINT:
return CogsBin::DataFormatV5::R32_SINT;
312 case Cogs::DataFormat::R32G32_SINT:
return CogsBin::DataFormatV5::R32G32_SINT;
313 case Cogs::DataFormat::R32G32B32_SINT:
return CogsBin::DataFormatV5::R32G32B32_SINT;
314 case Cogs::DataFormat::R32G32B32A32_SINT:
return CogsBin::DataFormatV5::R32G32B32A32_SINT;
315 case Cogs::DataFormat::R16_FLOAT:
return CogsBin::DataFormatV5::R16_FLOAT;
316 case Cogs::DataFormat::R16G16_FLOAT:
return CogsBin::DataFormatV5::R16G16_FLOAT;
317 case Cogs::DataFormat::R16G16B16_FLOAT:
return CogsBin::DataFormatV5::R16G16B16_FLOAT;
318 case Cogs::DataFormat::R16G16B16A16_FLOAT:
return CogsBin::DataFormatV5::R16G16B16A16_FLOAT;
319 case Cogs::DataFormat::R32_FLOAT:
return CogsBin::DataFormatV5::R32_FLOAT;
320 case Cogs::DataFormat::R32G32_FLOAT:
return CogsBin::DataFormatV5::R32G32_FLOAT;
321 case Cogs::DataFormat::R32G32B32_FLOAT:
return CogsBin::DataFormatV5::R32G32B32_FLOAT;
322 case Cogs::DataFormat::R32G32B32A32_FLOAT:
return CogsBin::DataFormatV5::R32G32B32A32_FLOAT;
323 case Cogs::DataFormat::D16_UNORM:
return CogsBin::DataFormatV5::D16_UNORM;
324 case Cogs::DataFormat::D24_UNORM:
return CogsBin::DataFormatV5::D24_UNORM;
325 case Cogs::DataFormat::D24S8_UNORM:
return CogsBin::DataFormatV5::D24S8_UNORM;
326 case Cogs::DataFormat::D32_FLOAT:
return CogsBin::DataFormatV5::D32_FLOAT;
327 case Cogs::DataFormat::R32_TYPELESS:
return CogsBin::DataFormatV5::R32_TYPELESS;
328 case Cogs::DataFormat::R16_TYPELESS:
return CogsBin::DataFormatV5::R16_TYPELESS;
329 case Cogs::DataFormat::R8T:
return CogsBin::DataFormatV5::R8T;
330 case Cogs::DataFormat::R8G8T:
return CogsBin::DataFormatV5::R8G8T;
331 case Cogs::DataFormat::R8G8B8T:
return CogsBin::DataFormatV5::R8G8B8T;
332 case Cogs::DataFormat::R8G8B8A8T:
return CogsBin::DataFormatV5::R8G8B8A8T;
333 case Cogs::DataFormat::B8G8R8:
return CogsBin::DataFormatV5::B8G8R8;
334 case Cogs::DataFormat::B8G8R8A8:
return CogsBin::DataFormatV5::B8G8R8A8;
335 case Cogs::DataFormat::A8_UNORM:
return CogsBin::DataFormatV5::A8_UNORM;
336 case Cogs::DataFormat::BC1_TYPELESS:
return CogsBin::DataFormatV5::BC1_TYPELESS;
337 case Cogs::DataFormat::BC1_UNORM:
return CogsBin::DataFormatV5::BC1_UNORM;
338 case Cogs::DataFormat::BC1_UNORM_SRGB:
return CogsBin::DataFormatV5::BC1_UNORM_SRGB;
339 case Cogs::DataFormat::BC2_TYPELESS:
return CogsBin::DataFormatV5::BC2_TYPELESS;
340 case Cogs::DataFormat::BC2_UNORM:
return CogsBin::DataFormatV5::BC2_UNORM;
341 case Cogs::DataFormat::BC2_UNORM_SRGB:
return CogsBin::DataFormatV5::BC2_UNORM_SRGB;
342 case Cogs::DataFormat::BC3_TYPELESS:
return CogsBin::DataFormatV5::BC3_TYPELESS;
343 case Cogs::DataFormat::BC3_UNORM:
return CogsBin::DataFormatV5::BC3_UNORM;
344 case Cogs::DataFormat::BC3_UNORM_SRGB:
return CogsBin::DataFormatV5::BC3_UNORM_SRGB;
345 case Cogs::DataFormat::BC4_TYPELESS:
return CogsBin::DataFormatV5::BC4_TYPELESS;
346 case Cogs::DataFormat::BC4_UNORM:
return CogsBin::DataFormatV5::BC4_UNORM;
347 case Cogs::DataFormat::BC4_SNORM:
return CogsBin::DataFormatV5::BC4_SNORM;
348 case Cogs::DataFormat::BC5_TYPELESS:
return CogsBin::DataFormatV5::BC5_TYPELESS;
349 case Cogs::DataFormat::BC5_UNORM:
return CogsBin::DataFormatV5::BC5_UNORM;
350 case Cogs::DataFormat::BC5_SNORM:
return CogsBin::DataFormatV5::BC5_SNORM;
351 case Cogs::DataFormat::BC6H_SIGNED_FLOAT_RGB:
return CogsBin::DataFormatV5::BC6H_SIGNED_FLOAT_RGB;
352 case Cogs::DataFormat::BC6H_UNSIGNED_FLOAT_RGB:
return CogsBin::DataFormatV5::BC6H_UNSIGNED_FLOAT_RGB;
353 case Cogs::DataFormat::BC7_UNORM_RGBA:
return CogsBin::DataFormatV5::BC7_UNORM_RGBA;
354 case Cogs::DataFormat::BC7_UNORM_SRGBA:
return CogsBin::DataFormatV5::BC7_UNORM_SRGBA;
355 case Cogs::DataFormat::PVRTC1_2BPP_UNORM_RGB:
return CogsBin::DataFormatV5::PVRTC1_2BPP_UNORM_RGB;
356 case Cogs::DataFormat::PVRTC1_4BPP_UNORM_RGB:
return CogsBin::DataFormatV5::PVRTC1_4BPP_UNORM_RGB;
357 case Cogs::DataFormat::PVRTC1_2BPP_UNORM_RGBA:
return CogsBin::DataFormatV5::PVRTC1_2BPP_UNORM_RGBA;
358 case Cogs::DataFormat::PVRTC1_4BPP_UNORM_RGBA:
return CogsBin::DataFormatV5::PVRTC1_4BPP_UNORM_RGBA;
359 case Cogs::DataFormat::EAC_BLOCK8_UNSIGNED_FLOAT_R:
return CogsBin::DataFormatV5::EAC_BLOCK8_UNSIGNED_FLOAT_R;
360 case Cogs::DataFormat::EAC_BLOCK8_SIGNED_FLOAT_R:
return CogsBin::DataFormatV5::EAC_BLOCK8_SIGNED_FLOAT_R;
361 case Cogs::DataFormat::EAC_BLOCK16_UNSIGNED_FLOAT_RG:
return CogsBin::DataFormatV5::EAC_BLOCK16_UNSIGNED_FLOAT_RG;
362 case Cogs::DataFormat::EAC_BLOCK16_SIGNED_FLOAT_RG:
return CogsBin::DataFormatV5::EAC_BLOCK16_SIGNED_FLOAT_RG;
363 case Cogs::DataFormat::ETC2_BLOCK8_UNORM_RGB:
return CogsBin::DataFormatV5::ETC2_BLOCK8_UNORM_RGB;
364 case Cogs::DataFormat::ETC2_BLOCK8_UNORM_SRGB:
return CogsBin::DataFormatV5::ETC2_BLOCK8_UNORM_SRGB;
365 case Cogs::DataFormat::ETC2_BLOCK16_UNORM_RGBA:
return CogsBin::DataFormatV5::ETC2_BLOCK16_UNORM_RGBA;
366 case Cogs::DataFormat::ETC2_BLOCK16_UNORM_SRGBA:
return CogsBin::DataFormatV5::ETC2_BLOCK16_UNORM_SRGBA;
367 case Cogs::DataFormat::ASTC_4x4_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_4x4_UNORM_RGBA;
368 case Cogs::DataFormat::ASTC_5x4_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_5x4_UNORM_RGBA;
369 case Cogs::DataFormat::ASTC_5x5_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_5x5_UNORM_RGBA;
370 case Cogs::DataFormat::ASTC_6x5_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_6x5_UNORM_RGBA;
371 case Cogs::DataFormat::ASTC_6x6_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_6x6_UNORM_RGBA;
372 case Cogs::DataFormat::ASTC_8x5_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_8x5_UNORM_RGBA;
373 case Cogs::DataFormat::ASTC_8x6_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_8x6_UNORM_RGBA;
374 case Cogs::DataFormat::ASTC_8x8_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_8x8_UNORM_RGBA;
375 case Cogs::DataFormat::ASTC_10x5_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_10x5_UNORM_RGBA;
376 case Cogs::DataFormat::ASTC_10x6_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_10x6_UNORM_RGBA;
377 case Cogs::DataFormat::ASTC_10x8_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_10x8_UNORM_RGBA;
378 case Cogs::DataFormat::ASTC_10x10_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_10x10_UNORM_RGBA;
379 case Cogs::DataFormat::ASTC_12x10_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_12x10_UNORM_RGBA;
380 case Cogs::DataFormat::ASTC_12x12_UNORM_RGBA:
return CogsBin::DataFormatV5::ASTC_12x12_UNORM_RGBA;
381 case Cogs::DataFormat::ASTC_4x4_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_4x4_UNORM_SRGBA;
382 case Cogs::DataFormat::ASTC_5x4_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_5x4_UNORM_SRGBA;
383 case Cogs::DataFormat::ASTC_5x5_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_5x5_UNORM_SRGBA;
384 case Cogs::DataFormat::ASTC_6x5_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_6x5_UNORM_SRGBA;
385 case Cogs::DataFormat::ASTC_6x6_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_6x6_UNORM_SRGBA;
386 case Cogs::DataFormat::ASTC_8x5_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_8x5_UNORM_SRGBA;
387 case Cogs::DataFormat::ASTC_8x6_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_8x6_UNORM_SRGBA;
388 case Cogs::DataFormat::ASTC_8x8_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_8x8_UNORM_SRGBA;
389 case Cogs::DataFormat::ASTC_10x5_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_10x5_UNORM_SRGBA;
390 case Cogs::DataFormat::ASTC_10x6_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_10x6_UNORM_SRGBA;
391 case Cogs::DataFormat::ASTC_10x8_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_10x8_UNORM_SRGBA;
392 case Cogs::DataFormat::ASTC_10x10_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_10x10_UNORM_SRGBA;
393 case Cogs::DataFormat::ASTC_12x10_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_12x10_UNORM_SRGBA;
394 case Cogs::DataFormat::ASTC_12x12_UNORM_SRGBA:
return CogsBin::DataFormatV5::ASTC_12x12_UNORM_SRGBA;
395 case Cogs::DataFormat::R8G8B8_UNORM_SRGB:
return CogsBin::DataFormatV5::R8G8B8_UNORM_SRGB;
396 case Cogs::DataFormat::R8G8B8A8_UNORM_SRGB:
return CogsBin::DataFormatV5::R8G8B8A8_UNORM_SRGB;
397 case Cogs::DataFormat::R10G10B10A2_TYPELESS:
return CogsBin::DataFormatV5::R10G10B10A2_TYPELESS;
398 case Cogs::DataFormat::R10G10B10A2_UNORM:
return CogsBin::DataFormatV5::R10G10B10A2_UNORM;
399 case Cogs::DataFormat::R10G10B10A2_UINT:
return CogsBin::DataFormatV5::R10G10B10A2_UINT;
400 case Cogs::DataFormat::R11G11B10_FLOAT:
return CogsBin::DataFormatV5::R11G11B10_FLOAT;
401 case Cogs::DataFormat::R5G6B5_UNORM:
return CogsBin::DataFormatV5::R5G6B5_UNORM;
402 case Cogs::DataFormat::R5G5B5A1_UNORM:
return CogsBin::DataFormatV5::R5G5B5A1_UNORM;
403 case Cogs::DataFormat::R4G4B4A4_UNORM:
return CogsBin::DataFormatV5::R4G4B4A4_UNORM;
404 case Cogs::DataFormat::R9G9B9E5_FLOAT:
return CogsBin::DataFormatV5::R9G9B9E5_FLOAT;
405 case Cogs::DataFormat::MAT4X4_FLOAT:
return CogsBin::DataFormatV5::MAT4X4_FLOAT;
407 assert(
false &&
"Invalid data format");
408 return CogsBin::DataFormatV5::Unknown;
412 CogsBin::TextureDimensions translateTextureDimensions(Cogs::ResourceDimensions dimensions)
414 switch (dimensions) {
415 case Cogs::ResourceDimensions::Unknown:
return CogsBin::TextureDimensions::Unknown;
break;
416 case Cogs::ResourceDimensions::Buffer:
return CogsBin::TextureDimensions::Buffer;
break;
417 case Cogs::ResourceDimensions::Texture1D:
return CogsBin::TextureDimensions::Texture1D;
break;
418 case Cogs::ResourceDimensions::Texture1DArray:
return CogsBin::TextureDimensions::Texture1DArray;
break;
419 case Cogs::ResourceDimensions::Texture2D:
return CogsBin::TextureDimensions::Texture2D;
break;
420 case Cogs::ResourceDimensions::Texture2DArray:
return CogsBin::TextureDimensions::Texture2DArray;
break;
421 case Cogs::ResourceDimensions::Texture2DMS:
return CogsBin::TextureDimensions::Texture2DMS;
break;
422 case Cogs::ResourceDimensions::Texture2DMSArray:
return CogsBin::TextureDimensions::Texture2DMSArray;
break;
423 case Cogs::ResourceDimensions::Texture3D:
return CogsBin::TextureDimensions::Texture3D;
break;
424 case Cogs::ResourceDimensions::Texture3DArray:
return CogsBin::TextureDimensions::Texture3DArray;
break;
425 case Cogs::ResourceDimensions::TextureCube:
return CogsBin::TextureDimensions::TextureCube;
break;
426 case Cogs::ResourceDimensions::TextureCubeArray:
return CogsBin::TextureDimensions::TextureCubeArray;
break;
428 assert(
false &&
"Illegal texture dimension");
431 return CogsBin::TextureDimensions::Unknown;
436 switch (addressMode) {
442 assert(
false &&
"Illegal texture addressing mode");
445 return CogsBin::TextureAddressMode::Wrap;
450 switch (filterMode) {
456 assert(
false &&
"Illegal texture filter mode");
459 return CogsBin::TextureFilterMode::MinMagMipLinear;
465 case Cogs::SamplerState::ComparisonFunction::Never:
return CogsBin::TextureComparisonFunction::Never;
break;
466 case Cogs::SamplerState::ComparisonFunction::Less:
return CogsBin::TextureComparisonFunction::Less;
break;
467 case Cogs::SamplerState::ComparisonFunction::Equal:
return CogsBin::TextureComparisonFunction::Equal;
break;
468 case Cogs::SamplerState::ComparisonFunction::LessEqual:
return CogsBin::TextureComparisonFunction::LessEqual;
break;
469 case Cogs::SamplerState::ComparisonFunction::Greater:
return CogsBin::TextureComparisonFunction::Greater;
break;
470 case Cogs::SamplerState::ComparisonFunction::NotEqual:
return CogsBin::TextureComparisonFunction::NotEqual;
break;
471 case Cogs::SamplerState::ComparisonFunction::GreaterEqual:
return CogsBin::TextureComparisonFunction::GreaterEqual;
break;
472 case Cogs::SamplerState::ComparisonFunction::Always:
return CogsBin::TextureComparisonFunction::Always;
break;
474 assert(
false &&
"Illegal texture comparison function");
477 return CogsBin::TextureComparisonFunction::Never;
480 WSection& getSection(WriteContext& wctx, CogsBin::SectionType type, uint16_t index,
bool create=
true)
482 if (wctx.sections.size() <= index) wctx.sections.resize((
size_t)index + 1);
483 if (!wctx.sections[index]) {
484 assert(create &&
"Trying to retrieve non-existing section");
485 wctx.sections[index] = std::make_unique<WSection>(WSection{ type, index, 0, 0, {}, {} });
487 auto & section = *wctx.sections[index].get();
488 section.type = section.type | type;
493 uint32_t addMetaSubsection(WriteContext& , WSection& section, CogsBin::SubSectionType type,
size_t size)
495 assert(section.contents.empty() &&
"Cannot add metasubsections after data payload has been added.");
496 assert(size < std::numeric_limits<uint32_t>::max());
498 auto index = nextIndex(section.subSections);
499 section.subSections.push_back(WSubSection{ ~0u, uint32_t(size), type });
503 uint32_t newNode(WriteContext& wctx)
505 uint32_t index = nextIndex(wctx.nodes);
506 auto & node = wctx.nodes.emplace_back();
510 node.materialInstance = ~0u;
511 node.transform = ~0u;
512 node.boundingBox = ~0u;
513 node.primitiveType = CogsBin::PrimitiveType::TriangleList;
514 node.vertexFirst = 0;
515 node.vertexCount = 0;
519 uint32_t recordTransform(WriteContext& wctx,
const glm::mat4& M)
522 if (std::memcmp(glm::value_ptr(I), glm::value_ptr(M),
sizeof(M)) == 0) {
528 const auto * m = glm::value_ptr(M);
535 size_t hashValue = T.hash();
537 if (
auto it = wctx.transformByHash.find(hashValue); it != wctx.transformByHash.end()) {
538 if (std::memcmp(&wctx.transforms[it->second], &T,
sizeof(T)) == 0) {
544 auto index = nextIndex(wctx.transforms);
545 wctx.transforms.emplace_back(T);
546 wctx.transformByHash[hashValue] = index;
550 uint32_t recordBoundingBox(WriteContext& wctx,
const Cogs::Geometry::BoundingBox& bbox)
558 size_t hashValue = bbox.hash();
559 if (
auto it = wctx.boundingBoxByHash.find(hashValue); it != wctx.boundingBoxByHash.end()) {
560 if (std::memcmp(&wctx.boundingBoxes[it->second], &bbox,
sizeof(bbox)) == 0) {
561 LOG_DEBUG(logger,
"Found duplicate bounding box.");
566 auto index = nextIndex(wctx.boundingBoxes);
567 wctx.boundingBoxes.emplace_back(bbox);
568 wctx.transformByHash[hashValue] = index;
581 hashValue = material->
getTextureProperty(DefaultMaterial::DiffuseMap).hash(hashValue);
584 hashValue =
Cogs::hash(
reinterpret_cast<intptr_t
>(material));
591 if (
string.empty())
return ~0u;
593 auto hash =
string.hash();
594 if (
auto it = wctx.stringByHash.find(hash); it != wctx.stringByHash.end()) {
595 auto & str = wctx.strings[it->second];
596 if (
string ==
Cogs::StringView((
const char*)(wctx.stringData.data() + str.offset), str.length)) {
601 auto offset = wctx.stringData.size();
602 assert(offset < std::numeric_limits<uint32_t>::max());
604 auto length =
string.length();
605 assert(length < std::numeric_limits<uint32_t>::max());
607 auto index = nextIndex(wctx.strings);
608 wctx.strings.emplace_back(
CogsBin::String{ uint32_t(offset), uint32_t(length) });
610 wctx.stringData.resize(offset + length);
611 std::memcpy(wctx.stringData.data() + offset,
string.begin(), length);
613 wctx.stringByHash[hash] = index;
619 assert(element.
offset <= std::numeric_limits<uint16_t>::max());
621 auto index = nextIndex(wctx.vertexAttributes);
624 auto & attribute = wctx.vertexAttributes.back();
626 attribute.offset = uint16_t(element.
offset);
627 attribute.format =
static_cast<CogsBin::Format
>(translateDataFormat(element.
format));
638 assert(
false &&
"Unknown element.format");
640 assert(element.
semanticIndex <= std::numeric_limits<uint8_t>::max());
644 attribute.instanceStepRate = 0;
647 attribute.instanceStepRate = std::max(uint16_t(1), element.
instanceStep);
654 if (a.elements.size() != b.elements.size())
return false;
655 for (
size_t i = 0; i < a.elements.size(); i++) {
656 const auto & ae = a.elements[i];
657 const auto & be = b.elements[i];
659 if ((ae.offset != be.offset) ||
660 (ae.format != be.format) ||
661 (ae.semantic != be.semantic) ||
662 (ae.semanticIndex != be.semanticIndex) ||
663 (ae.inputType != be.inputType) ||
664 (ae.instanceStep != be.instanceStep))
672 uint32_t recordBuffer(WriteContext& wctx, uint64_t size, CogsBin::BufferContentFlags flags, uint32_t alignment, uint16_t sectionIx)
676 assert(alignment <= CogsBin::CogsSectionAlignment);
678 const auto lowerMask = uint64_t(alignment - 1);
680 auto & section = getSection(wctx, CogsBin::SectionType::DATA, sectionIx);
681 section.dataSize = (section.dataSize + lowerMask)&(~lowerMask);
682 section.dataSize += size;
684 auto index = nextIndex(wctx.buffers);
686 wctx.bufferContexts.emplace_back();
688 auto & buffer = wctx.buffers.back();
689 buffer.sectionOffset = sectionIx;
691 buffer.size = (uint32_t)size;
692 buffer.flags = flags;
694 wctx.bufferContexts[index].contents.resize(size,
false);
695 wctx.bufferContexts[index].alignment = alignment;
700 uint8_t* getBufferPointer(WriteContext& wctx, uint32_t bufferIx, uint32_t offset, uint64_t size)
702 assert(bufferIx < wctx.buffers.size());
703 auto & bufferCtx = wctx.bufferContexts[bufferIx];
704 assert(offset + size <= bufferCtx.contents.size());
705 return (uint8_t*)bufferCtx.contents.data() + offset;
708 uint32_t recordBufferResource(WriteContext& wctx,
BufferResource* srcBuffer, uint16_t sectionIx)
710 if (srcBuffer ==
nullptr || srcBuffer->
empty()) {
714 if (
auto it = wctx.bufferByPointer.find(srcBuffer); it != wctx.bufferByPointer.end()) {
718 CogsBin::BufferContentFlags flags = CogsBin::BufferContentFlags::None;
719 if (srcBuffer->
isVertexBuffer()) flags |= CogsBin::BufferContentFlags::VertexData;
720 if (srcBuffer->
isIndexBuffer()) flags |= CogsBin::BufferContentFlags::IndexData;
722 constexpr uint32_t bufferStride = 16;
723 auto alignment = std::min(uint32_t(CogsBin::CogsSectionAlignment), uint32_t(bufferStride));
724 uint32_t index = recordBuffer(wctx, srcBuffer->
size(), flags, alignment, sectionIx);
725 std::memcpy(getBufferPointer(wctx, index, 0, srcBuffer->
size()), srcBuffer->
data(), srcBuffer->
size());
727 wctx.bufferByPointer[srcBuffer] = index;
734 const auto & srcFormat = *Cogs::VertexFormats::getVertexFormat(srcStream.
format);
735 assert(srcFormat.size <= std::numeric_limits<uint16_t>::max());
736 stream.stride = uint16_t(srcFormat.size);
738 assert(srcFormat.elements.size() < std::numeric_limits<uint32_t>::max());
739 stream.attributeCount = uint32_t(srcFormat.elements.size());
741 auto hash = Cogs::getHash(srcFormat);
743 if (
auto it = wctx.vertexFormatByHash.find(hash); it != wctx.vertexFormatByHash.end() && match(*it->second.format, srcFormat)) {
744 stream.firstAttribute = it->second.index;
747 stream.firstAttribute = recordAttribute(wctx, srcFormat.elements[0]);
748 for (
size_t i = 1; i < srcFormat.elements.size(); i++) {
749 auto ix = recordAttribute(wctx, srcFormat.elements[i]);
750 assert(stream.firstAttribute + i == ix);
752 wctx.vertexFormatByHash[hash] = WVertexFormatRec{ &srcFormat, stream.firstAttribute };
756 uint32_t recordVertexStream(WriteContext& wctx,
const DataStream& srcStream, uint16_t sectionIx)
758 assert(srcStream.size() <= std::numeric_limits<uint32_t>::max());
760 auto index = nextIndex(wctx.vertexStreams);
762 stream.firstAttribute = ~0u;
763 stream.vertexDataType = srcStream.
type;
767 recordVertexFormat(wctx, stream, srcStream);
769 stream.stride = (uint16_t)srcStream.
stride;
772 assert((
size_t)stream.count * (
size_t)stream.stride <= srcStream.size());
775 stream.buffer = recordBufferResource(wctx, srcStream.
buffer.
resolve(), sectionIx);
776 stream.offset = srcStream.
offset;
781 uint32_t recordMesh(WriteContext& wctx,
Mesh* srcMesh, uint16_t section)
783 if (srcMesh ==
nullptr || srcMesh->streams.empty()) {
787 if (
auto it = wctx.meshByPointer.find(srcMesh); it != wctx.meshByPointer.end()) {
788 LOG_DEBUG(logger,
"Duplicate mesh, recycling.");
792 uint32_t firstStream = 0;
793 uint32_t streamCount = 0;
794 for (
size_t i = 0; i < srcMesh->streams.size(); i++) {
795 if (srcMesh->streams[i].size() != 0) {
796 uint32_t streamIx = recordVertexStream(wctx, srcMesh->streams[i], section);
797 if (streamCount == 0) {
798 firstStream = streamIx;
800 assert(firstStream + streamCount == streamIx);
805 auto index = nextIndex(wctx.meshes);
806 auto & mesh = wctx.meshes.emplace_back();
807 mesh.name = recordString(wctx, srcMesh->
getName());
808 mesh.firstStream = firstStream;
809 mesh.streamCount = (uint8_t)streamCount;
810 mesh.boundingBox = recordBoundingBox(wctx, srcMesh->boundingBox);
812 (srcMesh->
isCCW() ? CogsBin::MeshFlags::None : CogsBin::MeshFlags::Clockwise) |
813 (srcMesh->
isMeshFlagSet(MeshFlags::Skinned) ? CogsBin::MeshFlags::Skinned : CogsBin::MeshFlags::None);
815 mesh.primitiveType = translatePrimitiveType(wctx, srcMesh->primitiveType);
817 wctx.meshByPointer[srcMesh] = index;
823 uint32_t recordProperty(WriteContext& wctx,
const Cogs::StringView& key, CogsBin::PropertyValueType valueType,
const T & value)
825 auto keyIx = recordString(wctx, key);;
829 if (
auto it = wctx.propByHash.find(hash); it != wctx.propByHash.end()) {
830 auto & p = wctx.properties[it->second];
831 if (p.keyType == CogsBin::PropertyKeyType::String &&
833 p.valueType == valueType)
835 if constexpr (
sizeof(T) <=
sizeof(uint32_t)) {
836 if (*(T*)(&p.value) == value) {
842 if (*(T*)(wctx.propertyData.data() + p.value) == value) {
850 auto index = nextIndex(wctx.properties);
851 wctx.properties.emplace_back(
CogsBin::Property{ CogsBin::PropertyKeyType::String, valueType, keyIx, ~0u });
852 auto & prop = wctx.properties.back();
854 if constexpr (
sizeof(T) <=
sizeof(uint32_t)) {
855 *(T*)(&prop.value) = value;
858 constexpr auto align =
alignof(T);
859 static_assert(align != 0 && (align & (align - 1)) == 0 &&
"alignment is not a power of two");
860 const auto mask = align - 1;
861 auto offset = (wctx.propertyData.size() + mask) & ~mask;
862 assert(offset < std::numeric_limits<uint32_t>::max());
863 prop.value = uint32_t(offset);
864 wctx.propertyData.resize(wctx.propertyData.size() +
sizeof(value));
865 *(T*)(wctx.propertyData.data() + offset) = value;
872 auto index = nextIndex(wctx.properties);
873 wctx.properties.emplace_back();
874 auto & prop = wctx.properties.back();
875 prop.keyType = CogsBin::PropertyKeyType::String;
876 prop.key = recordString(wctx, key);
877 prop.valueType = valueType;
878 prop.value = recordString(wctx, value);
882 uint32_t recordSamplerState(WriteContext& wctx,
const TextureWithSampler& srcSampler) {
883 auto index = nextIndex(wctx.samplerStates);
884 wctx.samplerStates.emplace_back();
885 auto& samplerState = wctx.samplerStates.back();
886 samplerState.addressModeS = translateTextureAddressMode(srcSampler.
sMode);
887 samplerState.addressModeT = translateTextureAddressMode(srcSampler.tMode);
888 samplerState.addressModeW = translateTextureAddressMode(srcSampler.uMode);
889 samplerState.filter = translateTextureFilterMode(srcSampler.
filterMode);
893 samplerState.comparisonFunction = translateTextureComparisonFunc(defaultState.
comparisonFunction);
895 std::copy(std::begin(defaultState.
borderColor), std::end(defaultState.
borderColor), samplerState.borderColor);
900 uint32_t recordEmbeddedTexture(WriteContext& wctx,
const Texture* srcTex, uint32_t samplerIx, uint16_t sectionIx)
903 assert(srcTex->storage.data.size() != 0);
905 if (
auto it = wctx.knownTexturesByPointer.find(srcTex); it != wctx.knownTexturesByPointer.end()) {
908 auto texIx = nextIndex(wctx.embeddedTextures);
909 wctx.knownTexturesByPointer[srcTex] = texIx;
910 auto & dstTex = wctx.embeddedTextures.emplace_back();;
913 assert(srcTex->storage.data.size() <= std::numeric_limits<uint32_t>::max());
914 const auto size = uint32_t(srcTex->storage.data.size());
915 dstTex.buffer = recordBuffer(wctx, size, CogsBin::BufferContentFlags::TextureData, 4, sectionIx);
916 std::memcpy(getBufferPointer(wctx, dstTex.buffer, 0, size), srcTex->storage.data.data(), size);
920 dstTex.encoding = CogsBin::TextureEncoding::None;
921 dstTex.name = recordString(wctx, srcTex->
getName());
922 dstTex.width = srcTex->description.width;
923 dstTex.height = srcTex->description.height;
924 dstTex.depth = srcTex->description.depth;
925 dstTex.format = translateFormat(srcTex->description.format);
926 dstTex.flags = CogsBin::TextureFlags::None;
927 dstTex.dimension = translateTextureDimensions(srcTex->description.target);
928 dstTex.samplerState = samplerIx;
933 uint32_t recordMaterialOptions(WriteContext& wctx,
const MaterialOptions& options) {
934 uint32_t propCount = 0;
936 switch(options.cullMode) {
937 case CullMode::None: optionVal =
"None";
break;
938 case CullMode::Back: optionVal =
"Back";
break;
939 case CullMode::Front: optionVal =
"Front";
break;
940 default: assert(
false &&
"Invalid cull mode");
break;
942 recordStringBasedProperty(wctx,
"CullMode", CogsBin::PropertyValueType::Option, optionVal);
945 const Cogs::Reflection::Enumerator* blendMode = Cogs::Reflection::TypeDatabase::getType<BlendMode>().getEnumerator(
size_t(options.blendMode));
946 assert(blendMode &&
"Invalid blend mode");
949 recordStringBasedProperty(wctx,
"BlendMode", CogsBin::PropertyValueType::Option, optionVal);
952 switch(options.transparencyMode) {
953 case TransparencyMode::On: optionVal =
"On";
break;
954 case TransparencyMode::Off: optionVal =
"Off";
break;
955 case TransparencyMode::Auto: optionVal =
"Auto";
break;
956 case TransparencyMode::Alpha: optionVal =
"Alpha";
break;
957 default: assert(
false &&
"Invalid transparency mode");
break;
959 recordStringBasedProperty(wctx,
"Transparency", CogsBin::PropertyValueType::Option, optionVal);
962 recordStringBasedProperty(wctx,
"DepthWriteEnabled", CogsBin::PropertyValueType::Option, options.depthWriteEnabled ?
"true" :
"false");
963 recordStringBasedProperty(wctx,
"DepthTestEnabled", CogsBin::PropertyValueType::Option, options.depthTestEnabled ?
"true" :
"false");
965 switch (options.depthFunc) {
966 case DepthFunc::Less: optionVal =
"Less";
break;
967 case DepthFunc::Always: optionVal =
"Always";
break;
968 case DepthFunc::LessOrEqual: optionVal =
"LessOrEqual";
break;
969 case DepthFunc::NotEqual: optionVal =
"NotEqual";
break;
970 case DepthFunc::Equal: optionVal =
"Equal";
break;
971 default: assert(
false &&
"Invalid depthFunc");
break;
973 recordStringBasedProperty(wctx,
"DepthFunc", CogsBin::PropertyValueType::Option, optionVal);
975 recordStringBasedProperty(wctx,
"DepthBiasEnabled", CogsBin::PropertyValueType::Option, options.depthBiasEnabled ?
"true" :
"false");
977 if (options.depthBiasEnabled) {
978 recordStringBasedProperty(wctx,
"DepthBiasConstant", CogsBin::PropertyValueType::Option, std::to_string(options.depthBias.constant));
979 recordStringBasedProperty(wctx,
"DepthBiasSlope", CogsBin::PropertyValueType::Option, std::to_string(options.depthBias.slope));
980 recordStringBasedProperty(wctx,
"DepthBiasClamp", CogsBin::PropertyValueType::Option, std::to_string(options.depthBias.clamp));
986 uint32_t recordMaterialInstance(WriteContext& wctx,
MaterialInstance* srcMatInst, uint16_t sectionIx)
988 if (srcMatInst ==
nullptr)
return ~0u;
990 if (
auto it = wctx.knownMaterialInstancesByPointer.find(srcMatInst); it != wctx.knownMaterialInstancesByPointer.end()) {
994 auto hash = getMaterialInstanceHash(srcMatInst);
995 if (
auto it = wctx.knownMaterialInstancesByHash.find(hash); it != wctx.knownMaterialInstancesByHash.end()) {
999 auto index = nextIndex(wctx.materialInstances);
1000 wctx.materialInstances.emplace_back();
1001 auto & dstMatInst = wctx.materialInstances.back();
1003 wctx.knownMaterialInstancesByPointer[srcMatInst] = index;
1004 wctx.knownMaterialInstancesByHash[hash] = index;
1006 dstMatInst.material = recordString(wctx, srcMatInst->
material->
getName());
1007 dstMatInst.permutation = recordString(wctx, srcMatInst->getPermutation());
1008 dstMatInst.propertyFirst = nextIndex(wctx.properties);
1009 dstMatInst.propertyCount = 0;
1012 switch (prop.type) {
1013 case MaterialDataType::Float:
1014 if (
auto val = srcMatInst->
getFloatProperty(prop.key); val != prop.defaultFloat()) {
1015 recordProperty(wctx, prop.name, CogsBin::PropertyValueType::Float, val);
1016 dstMatInst.propertyCount++;
1019 case MaterialDataType::Float2:
1020 if (
auto val = srcMatInst->
getVec2Property(prop.key); val != prop.defaultVec2()) {
1021 recordProperty(wctx, prop.name, CogsBin::PropertyValueType::Float2, val);
1022 dstMatInst.propertyCount++;
1025 case MaterialDataType::Float3:
1026 if (
auto val = srcMatInst->
getVec3Property(prop.key); val != prop.defaultVec3()) {
1027 recordProperty(wctx, prop.name, CogsBin::PropertyValueType::Float3, val);
1028 dstMatInst.propertyCount++;
1031 case MaterialDataType::Float4:
1032 if (
auto val = srcMatInst->
getVec4Property(prop.key); val != prop.defaultVec4()) {
1033 recordProperty(wctx, prop.name, CogsBin::PropertyValueType::Float4, val);
1034 dstMatInst.propertyCount++;
1037 case MaterialDataType::Bool:
1038 if (
auto val = srcMatInst->
getBoolProperty(prop.key); val != prop.defaultBool()) {
1039 recordProperty(wctx, prop.name, CogsBin::PropertyValueType::Bool, (uint32_t)val);
1040 dstMatInst.propertyCount++;
1043 case MaterialDataType::UInt:
1044 if (
auto val = srcMatInst->getProperty<uint32_t>(prop.key); val != prop.defaultUInt()) {
1045 recordProperty(wctx, prop.name, CogsBin::PropertyValueType::UInt, (uint32_t)val);
1046 dstMatInst.propertyCount++;
1050 LOG_WARNING(logger,
"Skipped serialization of unsupported material property %s of type %u.", prop.name.c_str(),
unsigned(prop.type));
1056 if ((wctx.flags & WriteModelFlags::EMBED_TEXTURES) != 0) {
1057 for (
auto & prop : srcMatInst->
material->textureProperties) {
1059 if (value.texture.handle && value.texture.handle != prop.texture.handle) {
1060 auto * tex = value.texture.handle.resolve();
1061 if (tex->storage.data.size() == 0) {
1062 LOG_WARNING(logger,
"Empty texture, skipping.");
1065 uint32_t samplerIx = recordSamplerState(wctx, value.texture);
1066 uint32_t texIx = recordEmbeddedTexture(wctx, tex, samplerIx, sectionIx);
1067 recordProperty(wctx, prop.name, CogsBin::PropertyValueType::EmbeddedTexture, texIx);
1068 dstMatInst.propertyCount++;
1074 for (uint32_t texI = 0; texI < srcMatInst->
material->textureProperties.size(); texI++) {
1077 if (value.texture.handle && value.texture.handle != prop.texture.
handle) {
1078 std::string source = value.texture.handle->getSource().
to_string();
1079 const std::string dir = Cogs::IO::parentPath(wctx.fileName);
1080 if (source.empty()) {
1082 const Texture* tex = value.texture.handle.resolve();
1083 if (tex->storage.data.size() == 0) {
1084 LOG_WARNING(logger,
"Skipping texture without source or data.");
1087 const std::string textureName = Cogs::IO::stem(wctx.fileName) +
"_texture_" + std::to_string(index) + std::to_string(texI) +
".png";
1088 source = Cogs::IO::combine(dir, textureName);
1089 const Cogs::FormatInfo* formatInfo = getFormatInfo(tex->description.format);
1090 if (!stbi_write_png(source.c_str(), tex->description.width, tex->description.height, formatInfo->
elements, tex->storage.data.data(), 0)) {
1091 LOG_ERROR(logger,
"Failed to write texture %s.", source.c_str());
1095 uint32_t pathIx = recordString(wctx, Cogs::IO::relativePath(source, dir));
1096 uint32_t samplerIx = recordSamplerState(wctx, value.texture);
1097 auto texIx = nextIndex(wctx.externalTextures);
1098 wctx.externalTextures.push_back({ pathIx, samplerIx });
1099 recordProperty(wctx, prop.
name, CogsBin::PropertyValueType::ExternalTexture, texIx);
1100 dstMatInst.propertyCount++;
1105 const ShaderVariants& variants = srcMatInst->
material->definition.variants;
1108 if (variant.isShared)
continue;
1109 if (selector.value != variant.defaultValue) {
1110 if (variant.type == ShaderVariantType::Enum) {
1111 recordStringBasedProperty(wctx, variant.name, CogsBin::PropertyValueType::Variant, variant.values[selector.value].key);
1112 dstMatInst.propertyCount++;
1115 recordStringBasedProperty(wctx, variant.name, CogsBin::PropertyValueType::Variant, selector.value ?
"true" :
"false");
1116 dstMatInst.propertyCount++;
1121 dstMatInst.propertyCount += recordMaterialOptions(wctx, srcMatInst->
options);
1123 assert(dstMatInst.propertyFirst + dstMatInst.propertyCount == nextIndex(wctx.properties));
1127 uint32_t recordSkeleton(WriteContext& wctx,
const Skeleton& srcSkeleton)
1129 if (srcSkeleton.bones.empty())
return ~0u;
1131 auto index = nextIndex(wctx.skeletons);
1133 auto & dstSkeleton = wctx.skeletons.back();
1134 dstSkeleton.bindPose = srcSkeleton.bindPose;
1135 dstSkeleton.firstBone = nextIndex(wctx.bones);
1136 dstSkeleton.boneCount = uint32_t(srcSkeleton.bones.size());
1137 for (
const auto & srcBone : srcSkeleton.bones) {
1139 auto & dstBone = wctx.bones.back();
1140 dstBone.inverseBindPose = srcBone.inverseBindPose;
1141 dstBone.relative = srcBone.relative;
1142 dstBone.rot = srcBone.rot;
1143 dstBone.pos = srcBone.pos;
1144 dstBone.scale = srcBone.scale;
1145 dstBone.name = recordString(wctx, srcBone.name);
1146 dstBone.parentBone = uint16_t(srcBone.parentBone);
1148 (srcBone.hasOffset ? CogsBin::BoneFlags::HasOffset : CogsBin::BoneFlags::None) |
1149 (srcBone.used ? CogsBin::BoneFlags::Used : CogsBin::BoneFlags::None) |
1150 (srcBone.animated ? CogsBin::BoneFlags::Animated : CogsBin::BoneFlags::None);
1155 uint32_t recordPart(WriteContext& wctx,
const Model& model,
const ModelPart& part, uint16_t section)
1157 auto nodeIndex = newNode(wctx);
1158 auto & node = wctx.nodes[nodeIndex];
1160 node.parent = part.parentIndex;
1161 if (part.nameIndex != -1) {
1162 node.name = recordString(wctx, model.getPartName(part));
1164 if (part.transformIndex != -1) {
1165 node.transform = recordTransform(wctx, model.getPartTransform(part));
1168 if (part.meshIndex != -1) {
1169 auto * mesh = model.meshes[part.meshIndex].resolve();
1170 node.mesh = recordMesh(wctx, mesh, 0);
1171 node.boundingBox = recordBoundingBox(wctx, mesh->boundingBox);
1172 node.primitiveType = translatePrimitiveType(wctx, mesh->primitiveType);
1173 node.vertexFirst = part.startIndex;
1174 node.vertexCount = part.vertexCount;
1175 if (part.materialIndex != -1) {
1176 node.materialInstance = recordMaterialInstance(wctx, model.materials[part.materialIndex].resolve(), section);
1183 void recordAnimTrack(WriteContext& wctx,
const AnimationTrack& srcTrack, uint16_t sectionIx)
1186 auto & dstTrack = wctx.animTracks.back();
1188 const auto translationCount = uint32_t(srcTrack.translations.size());
1189 const auto rotationCount = uint32_t(srcTrack.rotations.size());
1190 const auto scaleCount = uint32_t(srcTrack.scales.size());
1192 dstTrack.boneIndex = uint32_t(srcTrack.
boneIndex);
1193 dstTrack.buffer = recordBuffer(wctx,
1194 sizeof(
float)*(4 * (
size_t)translationCount + 5 * (
size_t)rotationCount + 4 * (
size_t)scaleCount),
1195 CogsBin::BufferContentFlags::AnimationData, 4, sectionIx);
1197 auto * base = (
float*)wctx.bufferContexts[dstTrack.buffer].contents.data();
1200 dstTrack.translationOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
1201 for(
auto & translation : srcTrack.translations) {
1202 *ptr++ = translation.t;
1203 *ptr++ = translation.value[0];
1204 *ptr++ = translation.value[1];
1205 *ptr++ = translation.value[2];
1207 dstTrack.translationCount = translationCount;
1208 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.translationOffset ==
sizeof(
float) * 4 * translationCount);
1210 dstTrack.rotationOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
1211 for (
auto & rotation : srcTrack.rotations) {
1212 *ptr++ = rotation.t;
1213 *ptr++ = rotation.value[0];
1214 *ptr++ = rotation.value[1];
1215 *ptr++ = rotation.value[2];
1216 *ptr++ = rotation.value[3];
1218 dstTrack.rotationCount = rotationCount;
1219 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.rotationOffset ==
sizeof(
float) * 5 * rotationCount);
1221 dstTrack.scaleOffset = uint32_t((uint8_t*)ptr - (uint8_t*)base);
1222 for (
auto & scale : srcTrack.scales) {
1224 *ptr++ = scale.value[0];
1225 *ptr++ = scale.value[1];
1226 *ptr++ = scale.value[2];
1228 dstTrack.scaleCount = scaleCount;
1229 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) - dstTrack.scaleOffset ==
sizeof(
float) * 4 * scaleCount);
1230 assert(uint32_t((uint8_t*)ptr - (uint8_t*)base) ==
sizeof(
float) * (4 * translationCount + 5 * rotationCount + 4 * scaleCount));
1233 void recordAnimClip(WriteContext& wctx,
const AnimationClip& srcClip, uint16_t sectionIx)
1236 auto & dstClip = wctx.animClips.back();
1237 dstClip.name = recordString(wctx, srcClip.name);
1238 dstClip.duration = srcClip.duration;
1239 dstClip.resolution = srcClip.resolution;
1240 if (!srcClip.tracks.empty()) {
1241 dstClip.trackFirst = nextIndex(wctx.animTracks);
1242 dstClip.trackCount = uint32_t(srcClip.tracks.size());
1243 for (
auto const & track : srcClip.tracks) recordAnimTrack(wctx, track, sectionIx);
1244 assert(dstClip.trackFirst + dstClip.trackCount == wctx.animTracks.size());
1247 dstClip.trackFirst = ~0u;
1248 dstClip.trackCount = 0;
1252 void recordModel(WriteContext& wctx,
const Model * srcModel, WriteModelFlags )
1254 if (srcModel->parts.empty())
return;
1256 uint16_t section = 0;
1259 auto & dstModel = wctx.models.back();
1260 dstModel.skeleton = (uint32_t)-1;
1261 dstModel.animClipFirst = (uint32_t)-1;
1262 dstModel.animClipCount = 0;
1263 if (
auto * animation = srcModel->animation.
resolve(); animation) {
1264 dstModel.skeleton = recordSkeleton(wctx, animation->skeleton);
1265 if (!animation->clips.empty()) {
1266 dstModel.animClipFirst = nextIndex(wctx.animClips);
1267 dstModel.animClipCount = uint32_t(animation->clips.size());
1268 for (
const auto & clip : animation->clips) recordAnimClip(wctx, clip, section);
1269 assert(dstModel.animClipFirst + dstModel.animClipCount == wctx.animClips.size());
1273 dstModel.firstNode = recordPart(wctx, *srcModel, srcModel->parts[0], section);
1274 for (
size_t i = 1; i < srcModel->parts.size(); i++) {
1275 auto ix = recordPart(wctx, *srcModel, srcModel->parts[i], 0);
1276 assert(dstModel.firstNode + i == ix);
1278 dstModel.nodeCount = uint32_t(srcModel->parts.size());
1283 auto nodeIndex = newNode(wctx);
1284 auto & node = wctx.nodes[nodeIndex];
1286 node.parent = parent;
1287 node.name = recordString(wctx, entity->
getName());
1290 node.transform = recordTransform(wctx, context->transformSystem->getLocalTransform(trComp));
1294 auto mesh = meshComponent->meshHandle;
1295 if (mesh && mesh->streams.size()) {
1297 if (renderComponent) {
1298 node.mesh = recordMesh(wctx, mesh.resolve(), section);
1300 node.primitiveType = translatePrimitiveType(wctx, renderComponent->primitiveType);
1302 node.primitiveType = CogsBin::PrimitiveType::Unknown;
1304 node.vertexFirst = renderComponent->startIndex;
1305 node.vertexCount = renderComponent->vertexCount;
1306 node.boundingBox = recordBoundingBox(wctx, context->renderSystem->getLocalBounds(renderComponent));
1307 if (renderComponent->material) {
1308 node.materialInstance = recordMaterialInstance(wctx, renderComponent->material.resolve(), section);
1312 LOG_ERROR(logger,
"MeshComponent must be accompanied by a MeshRenderComponent for export.");
1318 for (
auto & child : sceneComponent->children) {
1319 recordEntity(context, wctx, nodeIndex, child.get(), section);
1325 auto * anim = animComp->animation.resolve();
1326 if (animComp->master) {
1330 if (wctx.currentAnimation ==
nullptr) {
1331 wctx.currentAnimation = anim;
1333 else if (wctx.currentAnimation != anim) {
1334 LOG_ERROR(logger,
"Multiple animations within a single model's hierarchy");
1341 void layoutSections(WriteContext& wctx)
1343 for (
size_t i = 0; i < wctx.sections.size(); i++) {
1344 auto & section = *wctx.sections[i].get();
1346 assert(section.contents.empty() &&
"Sections already laid out");
1350 for (
auto & subsection : section.subSections) {
1351 section.writePos = align(section.writePos);
1352 assert(section.writePos < std::numeric_limits<uint32_t>::max());
1353 subsection.offset = section.writePos;
1354 section.writePos += subsection.size;
1356 section.writePos = align(section.writePos);
1359 section.contents.resize(section.writePos + section.dataSize);
1360 LOG_TRACE(logger,
"Section %zu allocated %zu bytes, data offset=%zu", i, section.contents.size(), section.writePos);
1364 for (
auto & subsection : section.subSections) {
1365 LOG_TRACE(logger,
" Subsection offset=%zu, size=%u, type=%u", subsection.offset, subsection.size,
unsigned(subsection.type));
1366 ptr->offset = subsection.offset;
1367 ptr->size = subsection.size;
1368 ptr->type = subsection.type;
1375 template<
typename T>
1376 void fillSubsection(WSection& section, uint32_t index,
const std::vector<T>& source)
1378 if (index == ~0u)
return;
1379 auto & subSection = section.subSections[index];
1380 assert(
sizeof(T)*source.size() == subSection.size);
1382 std::memcpy(section.contents.data() + subSection.offset, source.data(), subSection.size);
1385 bool pack(
Context* , WriteContext& wctx)
1388 auto & metaSection = getSection(wctx, CogsBin::SectionType::META, 0);
1390 uint32_t subSections[size_t(CogsBin::SubSectionType::EnumSize)];
1391 for (
auto & subSection : subSections) subSection = (uint32_t)-1;
1393#define REGISTER(T,A) if(!(A).empty()) subSections[size_t(T)] = addMetaSubsection(wctx, metaSection, T, sizeof((A)[0])*((A).size()))
1394 REGISTER(CogsBin::SubSectionType::Buffers, wctx.buffers);
1395 REGISTER(CogsBin::SubSectionType::ExternalTextures, wctx.externalTextures);
1396 REGISTER(CogsBin::SubSectionType::EmbeddedTextures, wctx.embeddedTextures);
1397 REGISTER(CogsBin::SubSectionType::SamplerStates, wctx.samplerStates);
1398 REGISTER(CogsBin::SubSectionType::Strings, wctx.strings);
1399 REGISTER(CogsBin::SubSectionType::StringData, wctx.stringData);
1400 REGISTER(CogsBin::SubSectionType::Nodes, wctx.nodes);
1401 REGISTER(CogsBin::SubSectionType::Transforms, wctx.transforms);
1402 REGISTER(CogsBin::SubSectionType::BoundingBoxes, wctx.boundingBoxes);
1403 REGISTER(CogsBin::SubSectionType::MaterialInstances, wctx.materialInstances);
1404 REGISTER(CogsBin::SubSectionType::Properties, wctx.properties);
1405 REGISTER(CogsBin::SubSectionType::PropertyData, wctx.propertyData);
1406 REGISTER(CogsBin::SubSectionType::Meshes, wctx.meshes);
1407 REGISTER(CogsBin::SubSectionType::VertexStreams, wctx.vertexStreams);
1408 REGISTER(CogsBin::SubSectionType::VertexAttributes, wctx.vertexAttributes);
1409 REGISTER(CogsBin::SubSectionType::Bones, wctx.bones);
1410 REGISTER(CogsBin::SubSectionType::Skeletons, wctx.skeletons);
1411 REGISTER(CogsBin::SubSectionType::AnimTracks, wctx.animTracks);
1412 REGISTER(CogsBin::SubSectionType::AnimClips, wctx.animClips);
1413 REGISTER(CogsBin::SubSectionType::Models, wctx.models);
1416 layoutSections(wctx);
1419 for (
size_t i = 0; i < wctx.buffers.size(); i++) {
1420 auto & buffer = wctx.buffers[i];
1421 const auto & bufCtx = wctx.bufferContexts[i];
1422 const auto lowerMask = uint64_t(bufCtx.alignment - 1);
1424 const auto sectionIx = buffer.sectionOffset;
1425 auto & section = getSection(wctx, CogsBin::SectionType::DATA, uint16_t(sectionIx),
false);
1426 auto offset = (section.writePos + lowerMask) & (~lowerMask);
1427 buffer.sectionOffset = offset | (sectionIx << 48);
1429 assert(offset + buffer.size <= section.contents.size());
1430 std::memcpy(section.contents.data() + offset, bufCtx.contents.data(), buffer.size);
1431 section.writePos = offset + buffer.size;
1435 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Buffers)], wctx.buffers);
1436 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::ExternalTextures)], wctx.externalTextures);
1437 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::EmbeddedTextures)], wctx.embeddedTextures);
1438 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::SamplerStates)], wctx.samplerStates);
1439 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Strings)], wctx.strings);
1440 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::StringData)], wctx.stringData);
1441 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Nodes)], wctx.nodes);
1442 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Transforms)], wctx.transforms);
1443 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::BoundingBoxes)], wctx.boundingBoxes);
1444 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::MaterialInstances)], wctx.materialInstances);
1445 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Properties)], wctx.properties);
1446 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::PropertyData)], wctx.propertyData);
1447 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Meshes)], wctx.meshes);
1448 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::VertexStreams)], wctx.vertexStreams);
1449 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::VertexAttributes)], wctx.vertexAttributes);
1450 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Bones)], wctx.bones);
1451 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Skeletons)], wctx.skeletons);
1452 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::AnimTracks)], wctx.animTracks);
1453 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::AnimClips)], wctx.animClips);
1454 fillSubsection(metaSection, subSections[
size_t(CogsBin::SubSectionType::Models)], wctx.models);
1459#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1460#pragma optimize( "", on )
1462 bool compressSections(
Context* , WriteContext& wctx)
1464 wctx.sectionInfos.resize(wctx.sections.size());
1465 for (
auto & section : wctx.sections) {
1466 auto & info = wctx.sectionInfos[section->index];
1467 info.fileSize = section->contents.size();
1468 info.uncompressedSize = info.fileSize;
1469 info.compression = CogsBin::Compression::None;
1470 info.type = section->type;
1471 info.subSectionCount = uint16_t(section->subSections.size());
1475 if ((wctx.flags & WriteModelFlags::COMPRESS_ZSTD) != 0) {
1477 int compressionLevel = ZSTD_CLEVEL_DEFAULT;
1478 if ((wctx.flags & WriteModelFlags::COMPRESS_MAX) != 0) {
1479 compressionLevel = ZSTD_maxCLevel();
1481 compressionLevel = wctx.settings.compressionLevel;
1485 ZSTD_CCtx* zstd_ctx = ZSTD_createCCtx();
1486 if (zstd_ctx ==
nullptr) {
1487 LOG_ERROR(logger,
"Failed to create zstd context.");
1491 std::vector<uint8_t> temp;
1492 for (
auto & section : wctx.sections) {
1493 auto & info = wctx.sectionInfos[section->index];
1494 auto maxSize = ZSTD_compressBound(info.uncompressedSize);
1495 temp.resize(maxSize);
1498 auto result = ZSTD_compressCCtx(zstd_ctx, temp.data(), maxSize, section->contents.data(), info.uncompressedSize, compressionLevel);
1499 if (ZSTD_isError(result)) {
1500 LOG_ERROR(logger,
"zstd compression failed: %s", ZSTD_getErrorName(result));
1503 else if (info.uncompressedSize <= result) {
1509 temp.resize(result);
1510 info.compression = CogsBin::Compression::ZSTD;
1511 info.fileSize = result;
1512 section->contents.swap(temp);
1515 ZSTD_freeCCtx(zstd_ctx);
1522#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1523#pragma optimize( "", off )
1528 int compressionLevel = ZSTD_CLEVEL_DEFAULT;
1529 if ((wctx.flags & WriteModelFlags::COMPRESS_MAX) != 0) {
1530 compressionLevel = ZSTD_maxCLevel();
1533 compressionLevel = wctx.settings.compressionLevel;
1536 ZSTD_CCtx* zstd_ctx = ZSTD_createCCtx();
1537 if (zstd_ctx ==
nullptr) {
1538 LOG_ERROR(logger,
"Failed to create zstd context.");
1542 size_t maxSize = ZSTD_compressBound(uncompressed.size());
1543 compressed.resize(maxSize,
false);
1545 size_t result = ZSTD_compressCCtx(zstd_ctx, compressed.data(), maxSize, uncompressed.data(), uncompressed.size(), compressionLevel);
1546 if (ZSTD_isError(result)) {
1547 ZSTD_freeCCtx(zstd_ctx);
1548 LOG_ERROR(logger,
"zstd compression failed: %s", ZSTD_getErrorName(result));
1551 else if (uncompressed.size() <= result) {
1555 compressed.resize(result);
1556 ZSTD_freeCCtx(zstd_ctx);
1560 bool write(
Context* context, WriteContext& wctx)
1562 pack(context, wctx);
1564 compressSections(context, wctx);
1570 header.magic = CogsBin::HeaderMagic;
1571 header.version = CogsBin::Version;
1573 header.sectionCount = nextIndex(wctx.sectionInfos);
1574 for (
auto & info : wctx.sectionInfos) {
1575 if (info.compression == CogsBin::Compression::None) {
1577 header.fileLength = (header.fileLength + (CogsBin::CogsSectionAlignment - 1)) & ~(CogsBin::CogsSectionAlignment - 1);
1579 info.fileOffset = header.fileLength;
1580 header.fileLength += info.fileSize;
1581 uncompressedSize += info.uncompressedSize;
1585 FILE * file = fopen(wctx.fileName.c_str(),
"wb");
1586 if (file ==
nullptr) {
1587 LOG_ERROR(logger,
"Failed to open \"%s\" for writing.", wctx.fileName.c_str());
1591 if ((wctx.flags & WriteModelFlags::COMPRESS_ZSTD_AS_FILE) != 0) {
1596 uncompressed.write(wctx.sectionInfos.data(),
sizeof(
CogsBin::SectionInfo) * header.sectionCount);
1597 for (
size_t i = 0; i < header.sectionCount; i++) {
1599 const ::WSection& section = *wctx.sections[i].get();
1600 assert(uncompressed.pos() <= info.fileOffset);
1601 while (uncompressed.pos() < info.fileOffset) {
1602 uncompressed.write8(0);
1604 uncompressed.write(section.contents.data(), info.fileSize);
1606 assert(uncompressedSize == uncompressed.pos());
1609 if (!compressBlob(context, wctx, compressed, uncompressed)) {
1613 size_t r = fwrite(compressed.data(), compressed.size(), 1, file);
1616 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1625 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1630 r = fwrite(wctx.sectionInfos.data(),
sizeof(
CogsBin::SectionInfo) * header.sectionCount, 1, file);
1633 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1638 for (uint32_t i = 0; i < header.sectionCount; i++) {
1639 auto& info = wctx.sectionInfos[i];
1640 auto& section = *wctx.sections[i].get();
1643 auto pos = uint64_t(_ftelli64(file));
1645 auto pos = uint64_t(ftell(file));
1647 assert(pos <= info.fileOffset);
1648 if (pos != info.fileOffset) {
1650 _fseeki64(file, info.fileOffset, SEEK_SET);
1652 fseek(file, info.fileOffset, SEEK_SET);
1656 r = fwrite(section.contents.data(), info.fileSize, 1, file);
1659 LOG_ERROR(logger,
"Error writing to %s", wctx.fileName.c_str());
1666 auto fileLength = _ftelli64(file);
1668 auto fileLength = ftell(file);
1670 if ((wctx.flags & WriteModelFlags::COMPRESS_ZSTD_AS_FILE) == 0) {
1671 assert(fileLength == (
long long)header.fileLength);
1673 LOG_TRACE(logger,
"Wrote %zu bytes (=%zu uncompressed, ratio=%.1f%%) (%u sections)",
1675 size_t(uncompressedSize),
1676 (100.f*
float(fileLength))/
float(uncompressedSize),
1677 unsigned(header.sectionCount));
1686 auto flags = settings->flags;
1688 if (!model)
return false;
1690 if (context->
variables->get(
"cogsbin.writeVersion", 3) == 3) {
1691 return writeCogsBin3Model(context, numVertes, numIndexes, fileName, model, settings);
1694 WriteContext wctx{};
1695 wctx.fileName = fileName.to_string();
1696 wctx.flags = WriteModelFlags(flags);
1698 wctx.settings = *settings;
1700 recordModel(wctx, model, flags);
1701 write(context, wctx);
1702 numVertes = wctx.numVertes;
1703 numIndexes = wctx.numIndexes;
1710 if (!entity || N == 0)
return false;
1712 if (context->
variables->get(
"cogsbin.writeVersion", 3) == 3) {
1713 return writeCogsBin3Models(context, numVertes, numIndexes, fileName, entity, N, flags);
1716 uint16_t section = 0;
1717 WriteContext wctx{};
1718 wctx.fileName = fileName.to_string();;
1719 wctx.flags = WriteModelFlags(flags);
1720 wctx.currentAnimation =
nullptr;
1722 auto & dstModel = wctx.models.back();
1724 dstModel.firstNode = recordEntity(context, wctx, ~0u, entity[0], section);
1725 for (
size_t i = 1; i < N; i++) {
1726 recordEntity(context, wctx, ~0u, entity[i], section);
1728 dstModel.nodeCount = uint32_t(wctx.nodes.size() - dstModel.firstNode);
1730 if (wctx.currentAnimation) {
1731 dstModel.skeleton = recordSkeleton(wctx, wctx.currentAnimation->skeleton);
1732 dstModel.animClipFirst = nextIndex(wctx.animClips);
1733 dstModel.animClipCount = uint32_t(wctx.currentAnimation->clips.size());
1734 for (
const auto & clip : wctx.currentAnimation->clips) recordAnimClip(wctx, clip, section);
1735 assert(dstModel.animClipFirst + dstModel.animClipCount == wctx.animClips.size());
1738 dstModel.skeleton = ~0u;
1739 dstModel.animClipFirst = ~0u;
1740 dstModel.animClipCount = 0;
1743 write(context, wctx);
1744 numVertes = wctx.numVertes;
1745 numIndexes = wctx.numIndexes;
1753 uint32_t numIndexes;
1754 return writeModels(context, numVertes, numIndexes, fileName, &entity, 1, flags);
1759 return writeModels(context, numVertes, numIndexes, fileName, &entity, 1, flags);
1763#if defined(_WIN32) && defined(MAKE_DEBUGABLE)
1764#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.
std::unique_ptr< class Variables > variables
Variables service instance.
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.
MaterialOptions options
Material rendering options used by this instance.
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.
Defines options for rendering using a material instance.
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.
SamplerState::FilterMode filterMode
Filter mode to use when rendering with this texture.
SamplerState::AddressMode sMode
Address mode to use when rendering with this texture.
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.
Single integral constant enumerator.
const Name & getName() const
Get the name of the enumerator.
const char * c_str() const
Gets the name as a null-terminated string.
Encapsulates state for texture sampling in a state object.
ComparisonFunction comparisonFunction
Specifies the comparison function to use when applying a comparison sampler.
unsigned int maxAnisotropy
Specifies the maximum number of anisotropic samples to use when sampling a texture.
static SamplerState & DefaultState()
Constructs a sampler state initialized with the default values.
ComparisonFunction
Comparison functions applied when sampling depth buffers using comparison filters.
AddressMode
Addressing modes to use when sampling textures.
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
@ Border
Texture color is set to the border color when outside [0, 1] range.
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
@ Mirror
Texture coordinates are mirrored when outside [0, 1] range.
FilterMode
Filter modes to specify how texture data is treated when sampled.
@ ComparisonMinMagMipPoint
Comparison filter for depth sample comparisons using point sampling.
@ ComparisonMinMagMipLinear
Comparison filter for depth sample comparisons using linear interpolation sampling.
@ MinMagMipPoint
Point sampling for both minification and magnification.
@ MinMagMipLinear
Linear sampling for both minification and magnification.
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.