Cogs.Core
ModelLoader.cpp
1#include "Context.h"
2#include "ModelLoader.h"
3#include "CogsBin.h"
4
5#include "Resources/Buffer.h"
6#include "Resources/ResourceStore.h"
7#include "Resources/ModelManager.h"
8#include "Resources/MeshManager.h"
9#include "Resources/MaterialManager.h"
10#include "Resources/TextureManager.h"
11#include "Resources/AnimationManager.h"
12#include "Resources/BufferManager.h"
13
14#include "Platform/ResourceBufferBackedFileContents.h"
15
16#include "Foundation/Geometry/BoundingBox.hpp"
17#include "Foundation/Logging/Logger.h"
18#include "Foundation/Platform/FileHandle.h"
19#include "Foundation/Platform/IO.h"
20#include "Foundation/Platform/MMapBackedFileContents.h"
21#include "Foundation/Platform/Threads.h"
22#include "Foundation/Platform/Timer.h"
23
24#include "zstd.h"
25#include <span>
26#include <type_traits>
27
28// Use of sub-tasks for loading will be disabled when more than the
29// following number of models are currently in flight...
30// Increasing this number could lead to a stack overflow if many
31// models are being loaded concurrently.
32#define COGS_MODELLOADER_MAX_TASKS 20
33
34//#pragma optimize( "", off )
35
36#ifndef EMSCRIPTEN
37// Toggle using memory mapped IO when reading file.
38#define COGS_USE_MMAP
39#endif
40
41// NOTE (chrisdy): I've tried to use asserts on errors resulting from bugs/illegal usage from cogs code,
42// But illegal data in file should only produce errors and bail out.
43
44namespace
45{
46 using namespace Cogs::Core;
47 Cogs::Logging::Log logger = Cogs::Logging::getLogger("ModelLoader");
48 std::atomic<uint32_t> loadCount(0);
49
50 template<typename T>
51 struct TypedView
52 {
53 const T* base = nullptr;
54 size_t count = 0;
55
56 const T& operator[](size_t i) const { assert(i < count); return base[i]; }
57 };
58
59 template<typename T>
60 constexpr bool isValidIndex(T v)
61 {
62 static_assert(std::is_integral_v<T>);
63 return v != T(-1);
64 }
65
66 struct ReadContext
67 {
68 Context* context = nullptr;
69 std::unique_ptr<Cogs::FileContents> contents;
70 Cogs::Core::TaskId group = NoTask;
71 std::string path;
72 uint32_t version = 0;
73 bool success = false;
74
75 CogsBin::Header* header = nullptr;
76 std::span<CogsBin::SectionInfo> sectionInfo;
77 std::vector<const uint8_t*> sectionPtr;
78 std::unique_ptr<Cogs::Mutex[]> sectionLocks;
79 Cogs::Mutex buffersMutex;
80
81 TypedView<CogsBin::Buffer> buffers;
82 TypedView<CogsBin::ExternalTexture> externalTextures;
83 TypedView<CogsBin::EmbeddedTexture> embeddedTextures;
84 TypedView<CogsBin::SamplerState> samplerStates;
85 TypedView<CogsBin::String> strings;
86 TypedView<char> stringData;
87 TypedView<CogsBin::Node> nodes;
88 TypedView<CogsBin::Transform> transforms;
89 TypedView<Cogs::Geometry::BoundingBox> boundingBoxes;
90 TypedView<CogsBin::MaterialInstance> materialInstances;
91 TypedView<CogsBin::Property> props;
92 TypedView<uint8_t> propData;
93 TypedView<CogsBin::Mesh> meshes;
94 TypedView<CogsBin::VertexStream> vertexStreams;
95 TypedView<CogsBin::VertexAttribute> vertexAttributes;
96 TypedView<CogsBin::Bone> bones;
97 TypedView<CogsBin::Skeleton> skeletons;
98 TypedView<CogsBin::AnimTrack> animTracks;
99 TypedView<CogsBin::AnimClip> animClips;
100 TypedView<CogsBin::Model> models;
101
102 std::vector<ResourceBufferHandle> bufferCache;
103 std::vector<TextureHandle> textureCache;
104
105 std::list<std::unique_ptr<uint8_t[]>> allocations; // we should have an arena here.
106 };
107
108 Cogs::PrimitiveType::EPrimitiveType translatePrimitiveType(ReadContext& rctx, CogsBin::PrimitiveType primitiveType)
109 {
110 switch (primitiveType) {
111 case CogsBin::PrimitiveType::TriangleList: return Cogs::PrimitiveType::TriangleList; break;
112 case CogsBin::PrimitiveType::TriangleStrip: return Cogs::PrimitiveType::TriangleStrip; break;
113 case CogsBin::PrimitiveType::LineList: return Cogs::PrimitiveType::LineList; break;
114 case CogsBin::PrimitiveType::LineStrip: return Cogs::PrimitiveType::LineStrip; break;
115 case CogsBin::PrimitiveType::PointList: return Cogs::PrimitiveType::PointList; break;
116 case CogsBin::PrimitiveType::TriangleListAdjacency: return Cogs::PrimitiveType::TriangleListAdjacency; break;
117 case CogsBin::PrimitiveType::TriangleStripAdjacency: return Cogs::PrimitiveType::TriangleStripAdjacency; break;
118 case CogsBin::PrimitiveType::LineListAdjacency: return Cogs::PrimitiveType::LineListAdjacency; break;
119 case CogsBin::PrimitiveType::LineStripAdjacency: return Cogs::PrimitiveType::LineStripAdjacency; break;
120 case CogsBin::PrimitiveType::ControlPoint1PatchList: return Cogs::PrimitiveType::ControlPoint1PatchList; break;
121 case CogsBin::PrimitiveType::ControlPoint2PatchList: return Cogs::PrimitiveType::ControlPoint2PatchList; break;
122 case CogsBin::PrimitiveType::ControlPoint3PatchList: return Cogs::PrimitiveType::ControlPoint3PatchList; break;
123 case CogsBin::PrimitiveType::ControlPoint4PatchList: return Cogs::PrimitiveType::ControlPoint4PatchList; break;
124 default:
125 break;
126 }
127 rctx.success = false;
128 LOG_ERROR(logger, "Illegal primitive type: %u", unsigned(primitiveType));
130 }
131
132 const uint8_t* sectionPointer(ReadContext& rctx, uint32_t index)
133 {
134 // FIXME (chrisdy): Should we leave this one around indefinitely?
135 thread_local ZSTD_DCtx* zstd_dctx = nullptr;
136
137 assert(index < rctx.header->sectionCount);
138
139 Cogs::LockGuard lock(rctx.sectionLocks[index]);
140 const auto & sectionInfo = rctx.sectionInfo[index];
141 if (rctx.sectionPtr[index] == nullptr) {
142 //LOG_DEBUG(logger, "%zx Resolving section %u (compr=%d, size=%zd, fileOffset=%zu, fileSize=%zu)",
143 // std::hash<std::thread::id>{}(std::this_thread::get_id()),
144 // index,
145 // sectionInfo.compression,
146 // size_t(sectionInfo.uncompressedSize),
147 // size_t(sectionInfo.fileOffset),
148 // size_t(sectionInfo.fileSize));
149 switch (rctx.sectionInfo[index].compression)
150 {
151 case CogsBin::Compression::None:
152 rctx.sectionPtr[index] = rctx.contents->ptr + rctx.sectionInfo[index].fileOffset;
153 break;
154 case CogsBin::Compression::ZSTD: {
155 if (zstd_dctx == nullptr) {
156 zstd_dctx = ZSTD_createDCtx();
157 }
158
159 // Allocate mem for uncompressed data. Clamp it to minimum 1 bytes so we don't get a nullptr
160 // for empty sections, as we use a null pointer to flag that something has gone horribly
161 // wrong and we give up.
162 rctx.allocations.emplace_back(new uint8_t[sectionInfo.uncompressedSize ? sectionInfo.uncompressedSize : 1]);
163 rctx.sectionPtr[index] = rctx.allocations.back().get();
164
165 auto result = ZSTD_decompressDCtx(zstd_dctx,
166 rctx.allocations.back().get(), sectionInfo.uncompressedSize,
167 rctx.contents->ptr + sectionInfo.fileOffset, sectionInfo.fileSize);
168 if (ZSTD_isError(result)) {
169 LOG_ERROR(logger, "zstd decompression failed: %s", ZSTD_getErrorName(result));
170 return nullptr;
171 }
172 else if (result != sectionInfo.uncompressedSize) {
173 LOG_ERROR(logger, "Uncompressed data size didn't match size recorded in file.");
174 return nullptr;
175 }
176 break;
177 }
178 default:
179 LOG_ERROR(logger, "Illegal compression flag on section.");
180 return nullptr;
181 }
182 }
183 return rctx.sectionPtr[index];
184 }
185
186 Cogs::StringView getString(ReadContext& rctx, uint32_t ix)
187 {
188 if (ix < rctx.strings.count) {
189 auto & str = rctx.strings[ix];
190 Cogs::StringView rv(rctx.stringData.base + str.offset, str.length);
191 //LOG_DEBUG(logger, "Retrieved string %.*s", StringViewFormat(rv));
192 return rv;
193 }
194 else if (!isValidIndex(ix)) {
195 return std::string_view();
196 }
197 else {
198 LOG_ERROR(logger, "Invalid string index %u", ix);
199 rctx.success = false;
200 return Cogs::StringView();
201 }
202 }
203
204 const Cogs::Geometry::BoundingBox& getBoundingBox(ReadContext& rctx, uint32_t ix)
205 {
206 if (ix < rctx.boundingBoxes.count) {
207 return rctx.boundingBoxes[ix];
208 }
209 else {
210 rctx.success = false;
211 LOG_ERROR(logger, "Invalid bounding box index %u", ix);
212 static const Cogs::Geometry::BoundingBox empty = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>();
213 return empty;
214 }
215 }
216
217 Cogs::TextureFormat getTextureFormat(ReadContext& /*rctx*/, CogsBin::Format format)
218 {
219 switch (format) {
220 case CogsBin::Format::Unknown: return Cogs::TextureFormat::Unknown; break;
221 case CogsBin::Format::R8_UINT: return Cogs::TextureFormat::R8_UINT; break;
222 case CogsBin::Format::R8G8_UINT: return Cogs::TextureFormat::R8G8_UINT; break;
223 case CogsBin::Format::R8G8B8_UINT: return Cogs::TextureFormat::R8G8B8_UINT; break;
224 case CogsBin::Format::R8G8B8A8_UINT: return Cogs::TextureFormat::R8G8B8A8_UINT; break;
225 case CogsBin::Format::R16_UINT: return Cogs::TextureFormat::R16_UINT; break;
226 case CogsBin::Format::R16G16_UINT: return Cogs::TextureFormat::R16G16_UINT; break;
227 case CogsBin::Format::R16G16B16_UINT: return Cogs::TextureFormat::R16G16B16_UINT; break;
228 case CogsBin::Format::R16G16B16A16_UINT: return Cogs::TextureFormat::R16G16B16A16_UINT; break;
229 case CogsBin::Format::R32_UINT: return Cogs::TextureFormat::R32_UINT; break;
230 case CogsBin::Format::R32G32_UINT: return Cogs::TextureFormat::R32G32_UINT; break;
231 case CogsBin::Format::R32G32B32_UINT: return Cogs::TextureFormat::R32G32B32_UINT; break;
232 case CogsBin::Format::R32G32B32A32_UINT: return Cogs::TextureFormat::R32G32B32A32_UINT; break;
233 case CogsBin::Format::R8_SINT: return Cogs::TextureFormat::R8_SINT; break;
234 case CogsBin::Format::R8G8_SINT: return Cogs::TextureFormat::R8G8_SINT; break;
235 case CogsBin::Format::R8G8B8_SINT: return Cogs::TextureFormat::R8G8B8_SINT; break;
236 case CogsBin::Format::R8G8B8A8_SINT: return Cogs::TextureFormat::R8G8B8A8_SINT; break;
237 case CogsBin::Format::R16_SINT: return Cogs::TextureFormat::R16_SINT; break;
238 case CogsBin::Format::R16G16_SINT: return Cogs::TextureFormat::R16G16_SINT; break;
239 case CogsBin::Format::R16G16B16_SINT: return Cogs::TextureFormat::R16G16B16_SINT; break;
240 case CogsBin::Format::R16G16B16A16_SINT: return Cogs::TextureFormat::R16G16B16A16_SINT; break;
241 case CogsBin::Format::R32_SINT: return Cogs::TextureFormat::R32_SINT; break;
242 case CogsBin::Format::R32G32_SINT: return Cogs::TextureFormat::R32G32_SINT; break;
243 case CogsBin::Format::R32G32B32_SINT: return Cogs::TextureFormat::R32G32B32_SINT; break;
244 case CogsBin::Format::R32G32B32A32_SINT: return Cogs::TextureFormat::R32G32B32A32_SINT; break;
245 case CogsBin::Format::R16_FLOAT: return Cogs::TextureFormat::R16_FLOAT; break;
246 case CogsBin::Format::R16G16_FLOAT: return Cogs::TextureFormat::R16G16_FLOAT; break;
247 case CogsBin::Format::R16G16B16_FLOAT: return Cogs::TextureFormat::R16G16B16_FLOAT; break;
248 case CogsBin::Format::R16G16B16A16_FLOAT: return Cogs::TextureFormat::R16G16B16A16_FLOAT; break;
249 case CogsBin::Format::R32_FLOAT: return Cogs::TextureFormat::R32_FLOAT; break;
250 case CogsBin::Format::R32G32_FLOAT: return Cogs::TextureFormat::R32G32_FLOAT; break;
251 case CogsBin::Format::R32G32B32_FLOAT: return Cogs::TextureFormat::R32G32B32_FLOAT; break;
252 case CogsBin::Format::R32G32B32A32_FLOAT: return Cogs::TextureFormat::R32G32B32A32_FLOAT; break;
253 case CogsBin::Format::R64_FLOAT: assert(false && "Illegal texture format"); break;
254 case CogsBin::Format::R64G64_FLOAT: assert(false && "Illegal texture format"); break;
255 case CogsBin::Format::R64G64B64_FLOAT: assert(false && "Illegal texture format"); break;
256 case CogsBin::Format::R64G65B64A64_FLOAT: assert(false && "Illegal texture format"); break;
257 case CogsBin::Format::R8_UNORM: return Cogs::TextureFormat::R8_UNORM; break;
258 case CogsBin::Format::R8G8_UNORM: return Cogs::TextureFormat::R8G8_UNORM; break;
259 case CogsBin::Format::R8G8B8_UNORM: return Cogs::TextureFormat::R8G8B8_UNORM; break;
260 case CogsBin::Format::R8G8B8A8_UNORM: return Cogs::TextureFormat::R8G8B8A8_UNORM; break;
261 case CogsBin::Format::R16_UNORM: return Cogs::TextureFormat::R16_UNORM; break;
262 case CogsBin::Format::R16G16_UNORM: return Cogs::TextureFormat::R16G16_UNORM; break;
263 case CogsBin::Format::R16G16B16_UNORM: return Cogs::TextureFormat::R16G16B16_UNORM; break;
264 case CogsBin::Format::R16G16B16A16_UNORM: return Cogs::TextureFormat::R16G16B16A16_UNORM; break;
265 case CogsBin::Format::R8_SNORM: return Cogs::TextureFormat::R8_SNORM; break;
266 case CogsBin::Format::R8G8_SNORM: return Cogs::TextureFormat::R8G8_SNORM; break;
267 case CogsBin::Format::R8G8B8_SNORM: return Cogs::TextureFormat::R8G8B8_SNORM; break;
268 case CogsBin::Format::R8G8B8A8_SNORM: return Cogs::TextureFormat::R8G8B8A8_SNORM; break;
269 case CogsBin::Format::R16_SNORM: return Cogs::TextureFormat::R16_SNORM; break;
270 case CogsBin::Format::R16G16_SNORM: return Cogs::TextureFormat::R16G16_SNORM; break;
271 case CogsBin::Format::R16G16B16_SNORM: return Cogs::TextureFormat::R16G16B16_SNORM; break;
272 case CogsBin::Format::R16G16B16A16_SNORM: return Cogs::TextureFormat::R16G16B16A16_SNORM; break;
273 case CogsBin::Format::D16_UNORM: return Cogs::TextureFormat::D16_UNORM; break;
274 case CogsBin::Format::D24_UNORM: return Cogs::TextureFormat::D24_UNORM; break;
275 case CogsBin::Format::D24S8_UNORM: return Cogs::TextureFormat::D24S8_UNORM; break;
276 case CogsBin::Format::D32_FLOAT: return Cogs::TextureFormat::D32_FLOAT; break;
277 case CogsBin::Format::R32_TYPELESS: return Cogs::TextureFormat::R32_TYPELESS; break;
278 case CogsBin::Format::R16_TYPELESS: return Cogs::TextureFormat::R16_TYPELESS; break;
279 case CogsBin::Format::R8T: return Cogs::TextureFormat::R8T; break;
280 case CogsBin::Format::R8G8T: return Cogs::TextureFormat::R8G8T; break;
281 case CogsBin::Format::R8G8B8T: return Cogs::TextureFormat::R8G8B8T; break;
282 case CogsBin::Format::R8G8B8A8T: return Cogs::TextureFormat::R8G8B8A8T; break;
283 case CogsBin::Format::B8G8R8: return Cogs::TextureFormat::B8G8R8; break;
284 case CogsBin::Format::B8G8R8A8: return Cogs::TextureFormat::B8G8R8A8; break;
285 case CogsBin::Format::A8_UNORM: return Cogs::TextureFormat::A8_UNORM; break;
286 case CogsBin::Format::BC1_TYPELESS: return Cogs::TextureFormat::BC1_TYPELESS; break;
287 case CogsBin::Format::BC1_UNORM: return Cogs::TextureFormat::BC1_UNORM; break;
288 case CogsBin::Format::BC1_UNORM_SRGB: return Cogs::TextureFormat::BC1_UNORM_SRGB; break;
289 case CogsBin::Format::BC2_TYPELESS: return Cogs::TextureFormat::BC2_TYPELESS; break;
290 case CogsBin::Format::BC2_UNORM: return Cogs::TextureFormat::BC2_UNORM; break;
291 case CogsBin::Format::BC2_UNORM_SRGB: return Cogs::TextureFormat::BC2_UNORM_SRGB; break;
292 case CogsBin::Format::BC3_TYPELESS: return Cogs::TextureFormat::BC3_TYPELESS; break;
293 case CogsBin::Format::BC3_UNORM: return Cogs::TextureFormat::BC3_UNORM; break;
294 case CogsBin::Format::BC3_UNORM_SRGB: return Cogs::TextureFormat::BC3_UNORM_SRGB; break;
295 case CogsBin::Format::BC4_TYPELESS: return Cogs::TextureFormat::BC4_TYPELESS; break;
296 case CogsBin::Format::BC4_UNORM: return Cogs::TextureFormat::BC4_UNORM; break;
297 case CogsBin::Format::BC4_SNORM: return Cogs::TextureFormat::BC4_SNORM; break;
298 case CogsBin::Format::BC5_TYPELESS: return Cogs::TextureFormat::BC5_TYPELESS; break;
299 case CogsBin::Format::BC5_UNORM: return Cogs::TextureFormat::BC5_UNORM; break;
300 case CogsBin::Format::BC5_SNORM: return Cogs::TextureFormat::BC5_SNORM; break;
301 case CogsBin::Format::R8G8B8A8_UNORM_SRGB: return Cogs::TextureFormat::R8G8B8A8_UNORM_SRGB; break;
302 default:
303 assert(false && "Illegal format");
304 break;
305 }
306 return Cogs::TextureFormat::Unknown;
307 }
308
309 Cogs::DataFormat decodeDataFormat(ReadContext& rctx, CogsBin::Format fmt)
310 {
311 if (rctx.version < 5) {
312 switch (static_cast<CogsBin::DataFormatV4>(fmt)) {
313 case CogsBin::DataFormatV4::Unknown: return Cogs::DataFormat::Unknown;
314 case CogsBin::DataFormatV4::R8_UNORM: return Cogs::DataFormat::R8_UNORM;
315 case CogsBin::DataFormatV4::R8G8_UNORM: return Cogs::DataFormat::R8G8_UNORM;
316 case CogsBin::DataFormatV4::R8G8B8_UNORM: return Cogs::DataFormat::R8G8B8_UNORM;
317 case CogsBin::DataFormatV4::R8G8B8A8_UNORM: return Cogs::DataFormat::R8G8B8A8_UNORM;
318 case CogsBin::DataFormatV4::R16_UNORM: return Cogs::DataFormat::R16_UNORM;
319 case CogsBin::DataFormatV4::R16G16_UNORM: return Cogs::DataFormat::R16G16_UNORM;
320 case CogsBin::DataFormatV4::R16G16B16_UNORM: return Cogs::DataFormat::R16G16B16_UNORM;
321 case CogsBin::DataFormatV4::R16G16B16A16_UNORM: return Cogs::DataFormat::R16G16B16A16_UNORM;
322 case CogsBin::DataFormatV4::R8_SNORM: return Cogs::DataFormat::R8_SNORM;
323 case CogsBin::DataFormatV4::R8G8_SNORM: return Cogs::DataFormat::R8G8_SNORM;
324 case CogsBin::DataFormatV4::R8G8B8_SNORM: return Cogs::DataFormat::R8G8B8_SNORM;
325 case CogsBin::DataFormatV4::R8G8B8A8_SNORM: return Cogs::DataFormat::R8G8B8A8_SNORM;
326 case CogsBin::DataFormatV4::R16_SNORM: return Cogs::DataFormat::R16_SNORM;
327 case CogsBin::DataFormatV4::R16G16_SNORM: return Cogs::DataFormat::R16G16_SNORM;
328 case CogsBin::DataFormatV4::R16G16B16_SNORM: return Cogs::DataFormat::R16G16B16_SNORM;
329 case CogsBin::DataFormatV4::R16G16B16A16_SNORM: return Cogs::DataFormat::R16G16B16A16_SNORM;
330 case CogsBin::DataFormatV4::R8_UINT: return Cogs::DataFormat::R8_UINT;
331 case CogsBin::DataFormatV4::R8G8_UINT: return Cogs::DataFormat::R8G8_UINT;
332 case CogsBin::DataFormatV4::R8G8B8_UINT: return Cogs::DataFormat::R8G8B8_UINT;
333 case CogsBin::DataFormatV4::R8G8B8A8_UINT: return Cogs::DataFormat::R8G8B8A8_UINT;
334 case CogsBin::DataFormatV4::R16_UINT: return Cogs::DataFormat::R16_UINT;
335 case CogsBin::DataFormatV4::R16G16_UINT: return Cogs::DataFormat::R16G16_UINT;
336 case CogsBin::DataFormatV4::R16G16B16_UINT: return Cogs::DataFormat::R16G16B16_UINT;
337 case CogsBin::DataFormatV4::R16G16B16A16_UINT: return Cogs::DataFormat::R16G16B16A16_UINT;
338 case CogsBin::DataFormatV4::R32_UINT: return Cogs::DataFormat::R32_UINT;
339 case CogsBin::DataFormatV4::R32G32_UINT: return Cogs::DataFormat::R32G32_UINT;
340 case CogsBin::DataFormatV4::R32G32B32_UINT: return Cogs::DataFormat::R32G32B32_UINT;
341 case CogsBin::DataFormatV4::R32G32B32A32_UINT: return Cogs::DataFormat::R32G32B32A32_UINT;
342 case CogsBin::DataFormatV4::R8_SINT: return Cogs::DataFormat::R8_SINT;
343 case CogsBin::DataFormatV4::R8G8_SINT: return Cogs::DataFormat::R8G8_SINT;
344 case CogsBin::DataFormatV4::R8G8B8_SINT: return Cogs::DataFormat::R8G8B8_SINT;
345 case CogsBin::DataFormatV4::R8G8B8A8_SINT: return Cogs::DataFormat::R8G8B8A8_SINT;
346 case CogsBin::DataFormatV4::R16_SINT: return Cogs::DataFormat::R16_SINT;
347 case CogsBin::DataFormatV4::R16G16_SINT: return Cogs::DataFormat::R16G16_SINT;
348 case CogsBin::DataFormatV4::R16G16B16_SINT: return Cogs::DataFormat::R16G16B16_SINT;
349 case CogsBin::DataFormatV4::R16G16B16A16_SINT: return Cogs::DataFormat::R16G16B16A16_SINT;
350 case CogsBin::DataFormatV4::R32_SINT: return Cogs::DataFormat::R32_SINT;
351 case CogsBin::DataFormatV4::R32G32_SINT: return Cogs::DataFormat::R32G32_SINT;
352 case CogsBin::DataFormatV4::R32G32B32_SINT: return Cogs::DataFormat::R32G32B32_SINT;
353 case CogsBin::DataFormatV4::R32G32B32A32_SINT: return Cogs::DataFormat::R32G32B32A32_SINT;
354 case CogsBin::DataFormatV4::R16_FLOAT: return Cogs::DataFormat::R16_FLOAT;
355 case CogsBin::DataFormatV4::R16G16_FLOAT: return Cogs::DataFormat::R16G16_FLOAT;
356 case CogsBin::DataFormatV4::R16G16B16_FLOAT: return Cogs::DataFormat::R16G16B16_FLOAT;
357 case CogsBin::DataFormatV4::R16G16B16A16_FLOAT: return Cogs::DataFormat::R16G16B16A16_FLOAT;
358 case CogsBin::DataFormatV4::R32_FLOAT: return Cogs::DataFormat::R32_FLOAT;
359 case CogsBin::DataFormatV4::R32G32_FLOAT: return Cogs::DataFormat::R32G32_FLOAT;
360 case CogsBin::DataFormatV4::R32G32B32_FLOAT: return Cogs::DataFormat::R32G32B32_FLOAT;
361 case CogsBin::DataFormatV4::R32G32B32A32_FLOAT: return Cogs::DataFormat::R32G32B32A32_FLOAT;
362 case CogsBin::DataFormatV4::D16_UNORM: return Cogs::DataFormat::D16_UNORM;
363 case CogsBin::DataFormatV4::D24_UNORM: return Cogs::DataFormat::D24_UNORM;
364 case CogsBin::DataFormatV4::D24S8_UNORM: return Cogs::DataFormat::D24S8_UNORM;
365 case CogsBin::DataFormatV4::D32_FLOAT: return Cogs::DataFormat::D32_FLOAT;
366 case CogsBin::DataFormatV4::R32_TYPELESS: return Cogs::DataFormat::R32_TYPELESS;
367 case CogsBin::DataFormatV4::R16_TYPELESS: return Cogs::DataFormat::R16_TYPELESS;
368 case CogsBin::DataFormatV4::R8T: return Cogs::DataFormat::R8T;
369 case CogsBin::DataFormatV4::R8G8T: return Cogs::DataFormat::R8G8T;
370 case CogsBin::DataFormatV4::R8G8B8T: return Cogs::DataFormat::R8G8B8T;
371 case CogsBin::DataFormatV4::R8G8B8A8T: return Cogs::DataFormat::R8G8B8A8T;
372 case CogsBin::DataFormatV4::B8G8R8: return Cogs::DataFormat::B8G8R8;
373 case CogsBin::DataFormatV4::B8G8R8A8: return Cogs::DataFormat::B8G8R8A8;
374 case CogsBin::DataFormatV4::A8_UNORM: return Cogs::DataFormat::A8_UNORM;
375 case CogsBin::DataFormatV4::BC1_TYPELESS: return Cogs::DataFormat::BC1_TYPELESS;
376 case CogsBin::DataFormatV4::BC1_UNORM: return Cogs::DataFormat::BC1_UNORM;
377 case CogsBin::DataFormatV4::BC1_UNORM_SRGB: return Cogs::DataFormat::BC1_UNORM_SRGB;
378 case CogsBin::DataFormatV4::BC2_TYPELESS: return Cogs::DataFormat::BC2_TYPELESS;
379 case CogsBin::DataFormatV4::BC2_UNORM: return Cogs::DataFormat::BC2_UNORM;
380 case CogsBin::DataFormatV4::BC2_UNORM_SRGB: return Cogs::DataFormat::BC2_UNORM_SRGB;
381 case CogsBin::DataFormatV4::BC3_TYPELESS: return Cogs::DataFormat::BC3_TYPELESS;
382 case CogsBin::DataFormatV4::BC3_UNORM: return Cogs::DataFormat::BC3_UNORM;
383 case CogsBin::DataFormatV4::BC3_UNORM_SRGB: return Cogs::DataFormat::BC3_UNORM_SRGB;
384 case CogsBin::DataFormatV4::BC4_TYPELESS: return Cogs::DataFormat::BC4_TYPELESS;
385 case CogsBin::DataFormatV4::BC4_UNORM: return Cogs::DataFormat::BC4_UNORM;
386 case CogsBin::DataFormatV4::BC4_SNORM: return Cogs::DataFormat::BC4_SNORM;
387 case CogsBin::DataFormatV4::BC5_TYPELESS: return Cogs::DataFormat::BC5_TYPELESS;
388 case CogsBin::DataFormatV4::BC5_UNORM: return Cogs::DataFormat::BC5_UNORM;
389 case CogsBin::DataFormatV4::BC5_SNORM: return Cogs::DataFormat::BC5_SNORM;
390 case CogsBin::DataFormatV4::R8G8B8_UNORM_SRGB: return Cogs::DataFormat::R8G8B8_UNORM_SRGB;
391 case CogsBin::DataFormatV4::R8G8B8A8_UNORM_SRGB: return Cogs::DataFormat::R8G8B8A8_UNORM_SRGB;
392 case CogsBin::DataFormatV4::R10G10B10A2_TYPELESS: return Cogs::DataFormat::R10G10B10A2_TYPELESS;
393 case CogsBin::DataFormatV4::R10G10B10A2_UNORM: return Cogs::DataFormat::R10G10B10A2_UNORM;
394 case CogsBin::DataFormatV4::R10G10B10A2_UINT: return Cogs::DataFormat::R10G10B10A2_UINT;
395 case CogsBin::DataFormatV4::R11G11B10_FLOAT: return Cogs::DataFormat::R11G11B10_FLOAT;
396 case CogsBin::DataFormatV4::R5G6B5_UNORM: return Cogs::DataFormat::R5G6B5_UNORM;
397 case CogsBin::DataFormatV4::R5G5B5A1_UNORM: return Cogs::DataFormat::R5G5B5A1_UNORM;
398 case CogsBin::DataFormatV4::R4G4B4A4_UNORM: return Cogs::DataFormat::R4G4B4A4_UNORM;
399 case CogsBin::DataFormatV4::R9G9B9E5_FLOAT: return Cogs::DataFormat::R9G9B9E5_FLOAT;
400 case CogsBin::DataFormatV4::MAT4X4_FLOAT: return Cogs::DataFormat::MAT4X4_FLOAT;
401 default: return Cogs::DataFormat::Unknown;
402 }
403 }
404 else {
405 switch (static_cast<CogsBin::DataFormatV5>(fmt)) {
406 case CogsBin::DataFormatV5::Unknown: return Cogs::DataFormat::Unknown;
407 case CogsBin::DataFormatV5::R8_UNORM: return Cogs::DataFormat::R8_UNORM;
408 case CogsBin::DataFormatV5::R8G8_UNORM: return Cogs::DataFormat::R8G8_UNORM;
409 case CogsBin::DataFormatV5::R8G8B8_UNORM: return Cogs::DataFormat::R8G8B8_UNORM;
410 case CogsBin::DataFormatV5::R8G8B8A8_UNORM: return Cogs::DataFormat::R8G8B8A8_UNORM;
411 case CogsBin::DataFormatV5::R16_UNORM: return Cogs::DataFormat::R16_UNORM;
412 case CogsBin::DataFormatV5::R16G16_UNORM: return Cogs::DataFormat::R16G16_UNORM;
413 case CogsBin::DataFormatV5::R16G16B16_UNORM: return Cogs::DataFormat::R16G16B16_UNORM;
414 case CogsBin::DataFormatV5::R16G16B16A16_UNORM: return Cogs::DataFormat::R16G16B16A16_UNORM;
415 case CogsBin::DataFormatV5::R8_SNORM: return Cogs::DataFormat::R8_SNORM;
416 case CogsBin::DataFormatV5::R8G8_SNORM: return Cogs::DataFormat::R8G8_SNORM;
417 case CogsBin::DataFormatV5::R8G8B8_SNORM: return Cogs::DataFormat::R8G8B8_SNORM;
418 case CogsBin::DataFormatV5::R8G8B8A8_SNORM: return Cogs::DataFormat::R8G8B8A8_SNORM;
419 case CogsBin::DataFormatV5::R16_SNORM: return Cogs::DataFormat::R16_SNORM;
420 case CogsBin::DataFormatV5::R16G16_SNORM: return Cogs::DataFormat::R16G16_SNORM;
421 case CogsBin::DataFormatV5::R16G16B16_SNORM: return Cogs::DataFormat::R16G16B16_SNORM;
422 case CogsBin::DataFormatV5::R16G16B16A16_SNORM: return Cogs::DataFormat::R16G16B16A16_SNORM;
423 case CogsBin::DataFormatV5::R8_UINT: return Cogs::DataFormat::R8_UINT;
424 case CogsBin::DataFormatV5::R8G8_UINT: return Cogs::DataFormat::R8G8_UINT;
425 case CogsBin::DataFormatV5::R8G8B8_UINT: return Cogs::DataFormat::R8G8B8_UINT;
426 case CogsBin::DataFormatV5::R8G8B8A8_UINT: return Cogs::DataFormat::R8G8B8A8_UINT;
427 case CogsBin::DataFormatV5::R16_UINT: return Cogs::DataFormat::R16_UINT;
428 case CogsBin::DataFormatV5::R16G16_UINT: return Cogs::DataFormat::R16G16_UINT;
429 case CogsBin::DataFormatV5::R16G16B16_UINT: return Cogs::DataFormat::R16G16B16_UINT;
430 case CogsBin::DataFormatV5::R16G16B16A16_UINT: return Cogs::DataFormat::R16G16B16A16_UINT;
431 case CogsBin::DataFormatV5::R32_UINT: return Cogs::DataFormat::R32_UINT;
432 case CogsBin::DataFormatV5::R32G32_UINT: return Cogs::DataFormat::R32G32_UINT;
433 case CogsBin::DataFormatV5::R32G32B32_UINT: return Cogs::DataFormat::R32G32B32_UINT;
434 case CogsBin::DataFormatV5::R32G32B32A32_UINT: return Cogs::DataFormat::R32G32B32A32_UINT;
435 case CogsBin::DataFormatV5::R8_SINT: return Cogs::DataFormat::R8_SINT;
436 case CogsBin::DataFormatV5::R8G8_SINT: return Cogs::DataFormat::R8G8_SINT;
437 case CogsBin::DataFormatV5::R8G8B8_SINT: return Cogs::DataFormat::R8G8B8_SINT;
438 case CogsBin::DataFormatV5::R8G8B8A8_SINT: return Cogs::DataFormat::R8G8B8A8_SINT;
439 case CogsBin::DataFormatV5::R16_SINT: return Cogs::DataFormat::R16_SINT;
440 case CogsBin::DataFormatV5::R16G16_SINT: return Cogs::DataFormat::R16G16_SINT;
441 case CogsBin::DataFormatV5::R16G16B16_SINT: return Cogs::DataFormat::R16G16B16_SINT;
442 case CogsBin::DataFormatV5::R16G16B16A16_SINT: return Cogs::DataFormat::R16G16B16A16_SINT;
443 case CogsBin::DataFormatV5::R32_SINT: return Cogs::DataFormat::R32_SINT;
444 case CogsBin::DataFormatV5::R32G32_SINT: return Cogs::DataFormat::R32G32_SINT;
445 case CogsBin::DataFormatV5::R32G32B32_SINT: return Cogs::DataFormat::R32G32B32_SINT;
446 case CogsBin::DataFormatV5::R32G32B32A32_SINT: return Cogs::DataFormat::R32G32B32A32_SINT;
447 case CogsBin::DataFormatV5::R16_FLOAT: return Cogs::DataFormat::R16_FLOAT;
448 case CogsBin::DataFormatV5::R16G16_FLOAT: return Cogs::DataFormat::R16G16_FLOAT;
449 case CogsBin::DataFormatV5::R16G16B16_FLOAT: return Cogs::DataFormat::R16G16B16_FLOAT;
450 case CogsBin::DataFormatV5::R16G16B16A16_FLOAT: return Cogs::DataFormat::R16G16B16A16_FLOAT;
451 case CogsBin::DataFormatV5::R32_FLOAT: return Cogs::DataFormat::R32_FLOAT;
452 case CogsBin::DataFormatV5::R32G32_FLOAT: return Cogs::DataFormat::R32G32_FLOAT;
453 case CogsBin::DataFormatV5::R32G32B32_FLOAT: return Cogs::DataFormat::R32G32B32_FLOAT;
454 case CogsBin::DataFormatV5::R32G32B32A32_FLOAT: return Cogs::DataFormat::R32G32B32A32_FLOAT;
455 case CogsBin::DataFormatV5::D16_UNORM: return Cogs::DataFormat::D16_UNORM;
456 case CogsBin::DataFormatV5::D24_UNORM: return Cogs::DataFormat::D24_UNORM;
457 case CogsBin::DataFormatV5::D24S8_UNORM: return Cogs::DataFormat::D24S8_UNORM;
458 case CogsBin::DataFormatV5::D32_FLOAT: return Cogs::DataFormat::D32_FLOAT;
459 case CogsBin::DataFormatV5::R32_TYPELESS: return Cogs::DataFormat::R32_TYPELESS;
460 case CogsBin::DataFormatV5::R16_TYPELESS: return Cogs::DataFormat::R16_TYPELESS;
461 case CogsBin::DataFormatV5::R8T: return Cogs::DataFormat::R8T;
462 case CogsBin::DataFormatV5::R8G8T: return Cogs::DataFormat::R8G8T;
463 case CogsBin::DataFormatV5::R8G8B8T: return Cogs::DataFormat::R8G8B8T;
464 case CogsBin::DataFormatV5::R8G8B8A8T: return Cogs::DataFormat::R8G8B8A8T;
465 case CogsBin::DataFormatV5::B8G8R8: return Cogs::DataFormat::B8G8R8;
466 case CogsBin::DataFormatV5::B8G8R8A8: return Cogs::DataFormat::B8G8R8A8;
467 case CogsBin::DataFormatV5::A8_UNORM: return Cogs::DataFormat::A8_UNORM;
468 case CogsBin::DataFormatV5::BC1_TYPELESS: return Cogs::DataFormat::BC1_TYPELESS;
469 case CogsBin::DataFormatV5::BC1_UNORM: return Cogs::DataFormat::BC1_UNORM;
470 case CogsBin::DataFormatV5::BC1_UNORM_SRGB: return Cogs::DataFormat::BC1_UNORM_SRGB;
471 case CogsBin::DataFormatV5::BC2_TYPELESS: return Cogs::DataFormat::BC2_TYPELESS;
472 case CogsBin::DataFormatV5::BC2_UNORM: return Cogs::DataFormat::BC2_UNORM;
473 case CogsBin::DataFormatV5::BC2_UNORM_SRGB: return Cogs::DataFormat::BC2_UNORM_SRGB;
474 case CogsBin::DataFormatV5::BC3_TYPELESS: return Cogs::DataFormat::BC3_TYPELESS;
475 case CogsBin::DataFormatV5::BC3_UNORM: return Cogs::DataFormat::BC3_UNORM;
476 case CogsBin::DataFormatV5::BC3_UNORM_SRGB: return Cogs::DataFormat::BC3_UNORM_SRGB;
477 case CogsBin::DataFormatV5::BC4_TYPELESS: return Cogs::DataFormat::BC4_TYPELESS;
478 case CogsBin::DataFormatV5::BC4_UNORM: return Cogs::DataFormat::BC4_UNORM;
479 case CogsBin::DataFormatV5::BC4_SNORM: return Cogs::DataFormat::BC4_SNORM;
480 case CogsBin::DataFormatV5::BC5_TYPELESS: return Cogs::DataFormat::BC5_TYPELESS;
481 case CogsBin::DataFormatV5::BC5_UNORM: return Cogs::DataFormat::BC5_UNORM;
482 case CogsBin::DataFormatV5::BC5_SNORM: return Cogs::DataFormat::BC5_SNORM;
483 case CogsBin::DataFormatV5::BC6H_SIGNED_FLOAT_RGB: return Cogs::DataFormat::BC6H_SIGNED_FLOAT_RGB;
484 case CogsBin::DataFormatV5::BC6H_UNSIGNED_FLOAT_RGB: return Cogs::DataFormat::BC6H_UNSIGNED_FLOAT_RGB;
485 case CogsBin::DataFormatV5::BC7_UNORM_RGBA: return Cogs::DataFormat::BC7_UNORM_RGBA;
486 case CogsBin::DataFormatV5::BC7_UNORM_SRGBA: return Cogs::DataFormat::BC7_UNORM_SRGBA;
487 case CogsBin::DataFormatV5::PVRTC1_2BPP_UNORM_RGB: return Cogs::DataFormat::PVRTC1_2BPP_UNORM_RGB;
488 case CogsBin::DataFormatV5::PVRTC1_4BPP_UNORM_RGB: return Cogs::DataFormat::PVRTC1_4BPP_UNORM_RGB;
489 case CogsBin::DataFormatV5::PVRTC1_2BPP_UNORM_RGBA: return Cogs::DataFormat::PVRTC1_2BPP_UNORM_RGBA;
490 case CogsBin::DataFormatV5::PVRTC1_4BPP_UNORM_RGBA: return Cogs::DataFormat::PVRTC1_4BPP_UNORM_RGBA;
491 case CogsBin::DataFormatV5::EAC_BLOCK8_UNSIGNED_FLOAT_R: return Cogs::DataFormat::EAC_BLOCK8_UNSIGNED_FLOAT_R;
492 case CogsBin::DataFormatV5::EAC_BLOCK8_SIGNED_FLOAT_R: return Cogs::DataFormat::EAC_BLOCK8_SIGNED_FLOAT_R;
493 case CogsBin::DataFormatV5::EAC_BLOCK16_UNSIGNED_FLOAT_RG: return Cogs::DataFormat::EAC_BLOCK16_UNSIGNED_FLOAT_RG;
494 case CogsBin::DataFormatV5::EAC_BLOCK16_SIGNED_FLOAT_RG: return Cogs::DataFormat::EAC_BLOCK16_SIGNED_FLOAT_RG;
495 case CogsBin::DataFormatV5::ETC2_BLOCK8_UNORM_RGB: return Cogs::DataFormat::ETC2_BLOCK8_UNORM_RGB;
496 case CogsBin::DataFormatV5::ETC2_BLOCK8_UNORM_SRGB: return Cogs::DataFormat::ETC2_BLOCK8_UNORM_SRGB;
497 case CogsBin::DataFormatV5::ETC2_BLOCK16_UNORM_RGBA: return Cogs::DataFormat::ETC2_BLOCK16_UNORM_RGBA;
498 case CogsBin::DataFormatV5::ETC2_BLOCK16_UNORM_SRGBA: return Cogs::DataFormat::ETC2_BLOCK16_UNORM_SRGBA;
499 case CogsBin::DataFormatV5::ASTC_4x4_UNORM_RGBA: return Cogs::DataFormat::ASTC_4x4_UNORM_RGBA;
500 case CogsBin::DataFormatV5::ASTC_5x4_UNORM_RGBA: return Cogs::DataFormat::ASTC_5x4_UNORM_RGBA;
501 case CogsBin::DataFormatV5::ASTC_5x5_UNORM_RGBA: return Cogs::DataFormat::ASTC_5x5_UNORM_RGBA;
502 case CogsBin::DataFormatV5::ASTC_6x5_UNORM_RGBA: return Cogs::DataFormat::ASTC_6x5_UNORM_RGBA;
503 case CogsBin::DataFormatV5::ASTC_6x6_UNORM_RGBA: return Cogs::DataFormat::ASTC_6x6_UNORM_RGBA;
504 case CogsBin::DataFormatV5::ASTC_8x5_UNORM_RGBA: return Cogs::DataFormat::ASTC_8x5_UNORM_RGBA;
505 case CogsBin::DataFormatV5::ASTC_8x6_UNORM_RGBA: return Cogs::DataFormat::ASTC_8x6_UNORM_RGBA;
506 case CogsBin::DataFormatV5::ASTC_8x8_UNORM_RGBA: return Cogs::DataFormat::ASTC_8x8_UNORM_RGBA;
507 case CogsBin::DataFormatV5::ASTC_10x5_UNORM_RGBA: return Cogs::DataFormat::ASTC_10x5_UNORM_RGBA;
508 case CogsBin::DataFormatV5::ASTC_10x6_UNORM_RGBA: return Cogs::DataFormat::ASTC_10x6_UNORM_RGBA;
509 case CogsBin::DataFormatV5::ASTC_10x8_UNORM_RGBA: return Cogs::DataFormat::ASTC_10x8_UNORM_RGBA;
510 case CogsBin::DataFormatV5::ASTC_10x10_UNORM_RGBA: return Cogs::DataFormat::ASTC_10x10_UNORM_RGBA;
511 case CogsBin::DataFormatV5::ASTC_12x10_UNORM_RGBA: return Cogs::DataFormat::ASTC_12x10_UNORM_RGBA;
512 case CogsBin::DataFormatV5::ASTC_12x12_UNORM_RGBA: return Cogs::DataFormat::ASTC_12x12_UNORM_RGBA;
513 case CogsBin::DataFormatV5::ASTC_4x4_UNORM_SRGBA: return Cogs::DataFormat::ASTC_4x4_UNORM_SRGBA;
514 case CogsBin::DataFormatV5::ASTC_5x4_UNORM_SRGBA: return Cogs::DataFormat::ASTC_5x4_UNORM_SRGBA;
515 case CogsBin::DataFormatV5::ASTC_5x5_UNORM_SRGBA: return Cogs::DataFormat::ASTC_5x5_UNORM_SRGBA;
516 case CogsBin::DataFormatV5::ASTC_6x5_UNORM_SRGBA: return Cogs::DataFormat::ASTC_6x5_UNORM_SRGBA;
517 case CogsBin::DataFormatV5::ASTC_6x6_UNORM_SRGBA: return Cogs::DataFormat::ASTC_6x6_UNORM_SRGBA;
518 case CogsBin::DataFormatV5::ASTC_8x5_UNORM_SRGBA: return Cogs::DataFormat::ASTC_8x5_UNORM_SRGBA;
519 case CogsBin::DataFormatV5::ASTC_8x6_UNORM_SRGBA: return Cogs::DataFormat::ASTC_8x6_UNORM_SRGBA;
520 case CogsBin::DataFormatV5::ASTC_8x8_UNORM_SRGBA: return Cogs::DataFormat::ASTC_8x8_UNORM_SRGBA;
521 case CogsBin::DataFormatV5::ASTC_10x5_UNORM_SRGBA: return Cogs::DataFormat::ASTC_10x5_UNORM_SRGBA;
522 case CogsBin::DataFormatV5::ASTC_10x6_UNORM_SRGBA: return Cogs::DataFormat::ASTC_10x6_UNORM_SRGBA;
523 case CogsBin::DataFormatV5::ASTC_10x8_UNORM_SRGBA: return Cogs::DataFormat::ASTC_10x8_UNORM_SRGBA;
524 case CogsBin::DataFormatV5::ASTC_10x10_UNORM_SRGBA: return Cogs::DataFormat::ASTC_10x10_UNORM_SRGBA;
525 case CogsBin::DataFormatV5::ASTC_12x10_UNORM_SRGBA: return Cogs::DataFormat::ASTC_12x10_UNORM_SRGBA;
526 case CogsBin::DataFormatV5::ASTC_12x12_UNORM_SRGBA: return Cogs::DataFormat::ASTC_12x12_UNORM_SRGBA;
527 case CogsBin::DataFormatV5::R8G8B8_UNORM_SRGB: return Cogs::DataFormat::R8G8B8_UNORM_SRGB;
528 case CogsBin::DataFormatV5::R8G8B8A8_UNORM_SRGB: return Cogs::DataFormat::R8G8B8A8_UNORM_SRGB;
529 case CogsBin::DataFormatV5::R10G10B10A2_TYPELESS: return Cogs::DataFormat::R10G10B10A2_TYPELESS;
530 case CogsBin::DataFormatV5::R10G10B10A2_UNORM: return Cogs::DataFormat::R10G10B10A2_UNORM;
531 case CogsBin::DataFormatV5::R10G10B10A2_UINT: return Cogs::DataFormat::R10G10B10A2_UINT;
532 case CogsBin::DataFormatV5::R11G11B10_FLOAT: return Cogs::DataFormat::R11G11B10_FLOAT;
533 case CogsBin::DataFormatV5::R5G6B5_UNORM: return Cogs::DataFormat::R5G6B5_UNORM;
534 case CogsBin::DataFormatV5::R5G5B5A1_UNORM: return Cogs::DataFormat::R5G5B5A1_UNORM;
535 case CogsBin::DataFormatV5::R4G4B4A4_UNORM: return Cogs::DataFormat::R4G4B4A4_UNORM;
536 case CogsBin::DataFormatV5::R9G9B9E5_FLOAT: return Cogs::DataFormat::R9G9B9E5_FLOAT;
537 case CogsBin::DataFormatV5::MAT4X4_FLOAT: return Cogs::DataFormat::MAT4X4_FLOAT;
538 default: return Cogs::DataFormat::Unknown;
539 }
540 }
541 }
542
543
544 Cogs::ResourceDimensions getTextureDimensions(CogsBin::TextureDimensions dimensions) {
545 switch (dimensions) {
546 case CogsBin::TextureDimensions::Unknown: return Cogs::ResourceDimensions::Unknown; break;
547 case CogsBin::TextureDimensions::Buffer: return Cogs::ResourceDimensions::Buffer; break;
548 case CogsBin::TextureDimensions::Texture1D: return Cogs::ResourceDimensions::Texture1D; break;
549 case CogsBin::TextureDimensions::Texture1DArray: return Cogs::ResourceDimensions::Texture1DArray; break;
550 case CogsBin::TextureDimensions::Texture2D: return Cogs::ResourceDimensions::Texture2D; break;
551 case CogsBin::TextureDimensions::Texture2DArray: return Cogs::ResourceDimensions::Texture2DArray; break;
552 case CogsBin::TextureDimensions::Texture2DMS: return Cogs::ResourceDimensions::Texture2DMS; break;
553 case CogsBin::TextureDimensions::Texture2DMSArray: return Cogs::ResourceDimensions::Texture2DMSArray; break;
554 case CogsBin::TextureDimensions::Texture3D: return Cogs::ResourceDimensions::Texture3D; break;
555 case CogsBin::TextureDimensions::Texture3DArray: return Cogs::ResourceDimensions::Texture3DArray; break;
556 case CogsBin::TextureDimensions::TextureCube: return Cogs::ResourceDimensions::TextureCube; break;
557 case CogsBin::TextureDimensions::TextureCubeArray: return Cogs::ResourceDimensions::TextureCubeArray; break;
558 default:
559 assert(false && "Illegal texture dimension");
560 break;
561 }
562 return Cogs::ResourceDimensions::Unknown;
563 }
564
565 Cogs::SamplerState::AddressMode getTextureAddressMode(CogsBin::TextureAddressMode addressMode)
566 {
567 switch (addressMode) {
568 case CogsBin::TextureAddressMode::Clamp: return Cogs::SamplerState::Clamp; break;
569 case CogsBin::TextureAddressMode::Wrap: return Cogs::SamplerState::Wrap; break;
570 case CogsBin::TextureAddressMode::Mirror: return Cogs::SamplerState::Mirror; break;
571 case CogsBin::TextureAddressMode::Border: return Cogs::SamplerState::Border; break;
572 default:
573 assert(false && "Illegal texture addressing mode");
574 break;
575 }
577 }
578
579 Cogs::SamplerState::FilterMode getTextureFilterMode(CogsBin::TextureFilterMode filterMode)
580 {
581 switch (filterMode) {
582 case CogsBin::TextureFilterMode::MinMagMipPoint: return Cogs::SamplerState::FilterMode::MinMagMipPoint;
583 case CogsBin::TextureFilterMode::MinMagMipLinear: return Cogs::SamplerState::FilterMode::MinMagMipLinear;
584 case CogsBin::TextureFilterMode::ComparisonMinMagMipPoint: return Cogs::SamplerState::FilterMode::ComparisonMinMagMipPoint;
585 case CogsBin::TextureFilterMode::ComparisonMinMagMipLinear: return Cogs::SamplerState::FilterMode::ComparisonMinMagMipLinear;
586 default:
587 assert(false && "Illegal texture addressing mode");
589 }
590 }
591
592 Cogs::VertexFormatHandle getVertexFormat(ReadContext& rctx, uint32_t firstAttribute, uint32_t attributeCount)
593 {
594 const auto * attributes = rctx.vertexAttributes.base + firstAttribute;
595
596 std::vector<Cogs::VertexElement> elements;
597 elements.resize(attributeCount);
598 for (unsigned i = 0; i < attributeCount; i++) {
599 const auto & attribute = attributes[i];
600
601 auto & element = elements[i];
602 element.offset = attribute.offset;
603 element.format = decodeDataFormat(rctx, attribute.format);
604
605 switch (attribute.semantic) {
606 case CogsBin::Semantic::Position: element.semantic = Cogs::ElementSemantic::Position; break;
607 case CogsBin::Semantic::Normal: element.semantic = Cogs::ElementSemantic::Normal; break;
608 case CogsBin::Semantic::Color: element.semantic = Cogs::ElementSemantic::Color; break;
609 case CogsBin::Semantic::TexCoord: element.semantic = Cogs::ElementSemantic::TextureCoordinate; break;
610 case CogsBin::Semantic::Tangent: element.semantic = Cogs::ElementSemantic::Tangent; break;
611 case CogsBin::Semantic::InstanceVector: element.semantic = Cogs::ElementSemantic::InstanceVector; break;
612 case CogsBin::Semantic::InstanceMatrix: element.semantic = Cogs::ElementSemantic::InstanceMatrix; break;
613 default:
614 LOG_ERROR(logger, "Illegal vertex element semantic %u", unsigned(attribute.semantic));
615 return {};
616 }
617 element.semanticIndex = attribute.semanticIndex;
618 element.inputType = attribute.instanceStepRate == 0 ? Cogs::InputType::VertexData : Cogs::InputType::InstanceData;
619 element.instanceStep = attribute.instanceStepRate;
620 }
621
622 return Cogs::VertexFormats::createVertexFormat(elements.data(), elements.size());
623 }
624
625 const uint8_t* resolveDataOffset(ReadContext& rctx, uint64_t offset, uint32_t size)
626 {
627 const auto sectionIx = uint16_t(offset >> 48);
628 const auto * section = sectionPointer(rctx, sectionIx);
629 if (section == nullptr) {
630 LOG_ERROR(logger, "Failed to retrieve data section %u", unsigned(sectionIx));
631 rctx.success = false;
632 return nullptr;
633 }
634 const auto sectionOffset = size_t(offset & 0xFFFFFFFFFFFFull);
635 if (rctx.sectionInfo[sectionIx].uncompressedSize < sectionOffset + size) {
636 LOG_ERROR(logger, "Stream source offset %zX size %u is outside section memory range.", sectionOffset, size);
637 rctx.success = false;
638 return nullptr;
639 }
640 return section + sectionOffset;
641 }
642
643 template<typename D, typename S>
644 void widenAndCopy(std::span<D>& dstSpan, const S* src, uint32_t count)
645 {
646 auto * dst = dstSpan.data();
647 // preserve notzeros. When narrowing in the writer, we check for
648 // *less than*, so an actual value of 256 should trigger uint16_t storage.
649 constexpr auto notzero = std::numeric_limits<S>::max();
650 for (uint32_t i = 0; i < count; i++) {
651 const auto v = *src++;
652 if (v == notzero) *dst++ = ~D(0);
653 else *dst++ = D(v);
654 }
655 }
656
657 void populateMesh(ReadContext& rctx, MeshHandle handle, uint32_t index)
658 {
659 const auto & srcMesh = rctx.meshes[index];
660 auto dstMesh = rctx.context->meshManager->lock(handle);
661
662 if (isValidIndex(srcMesh.name)) {
663 dstMesh->setName(getString(rctx, srcMesh.name));
664 }
665 if (isValidIndex(srcMesh.boundingBox)) {
666 dstMesh->setBounds(getBoundingBox(rctx, srcMesh.boundingBox));
667 }
668 if ((srcMesh.flags & CogsBin::MeshFlags::Clockwise) != 0) {
669 dstMesh->setMeshFlag(MeshFlags::ClockwiseWinding);
670 }
671 if ((srcMesh.flags & CogsBin::MeshFlags::Skinned) != 0) {
672 dstMesh->setMeshFlag(MeshFlags::Skinned);
673 }
674
675 dstMesh->primitiveType = translatePrimitiveType(rctx, srcMesh.primitiveType);
676
677 dstMesh->streams.resize(srcMesh.streamCount);
678 for (size_t i = 0; i < srcMesh.streamCount; i++) {
679 assert(srcMesh.firstStream + i < rctx.vertexStreams.count);
680 const auto & srcStream = rctx.vertexStreams[srcMesh.firstStream + i];
681
682 auto & dstStream = dstMesh->streams[i];
683
684 {
685 Cogs::LockGuard lock(rctx.buffersMutex);
686 assert(srcStream.buffer < rctx.buffers.count);
687
688 if (rctx.bufferCache[srcStream.buffer]) {
689 dstStream.buffer = rctx.bufferCache[srcStream.buffer];
690 } else {
691 auto & dstBuffer = rctx.bufferCache[srcStream.buffer] = rctx.context->bufferManager->create();
692 dstStream.buffer = dstBuffer;
693
694 const auto & srcBuffer = rctx.buffers[srcStream.buffer];
695 const auto * srcData = resolveDataOffset(rctx, srcBuffer.sectionOffset, srcBuffer.size);
696
697 dstBuffer->reset(srcBuffer.size, rctx.context->memory->resourceAllocator);
698 std::memcpy(dstBuffer->data(), srcData, srcBuffer.size);
699
700 if ((srcBuffer.flags & CogsBin::BufferContentFlags::VertexData) != 0) {
701 dstBuffer->setBindFlags(BufferBindFlags::VertexBuffer);
702 }
703
704 if ((srcBuffer.flags & CogsBin::BufferContentFlags::IndexData) != 0) {
705 dstBuffer->setBindFlags(BufferBindFlags::IndexBuffer);
706 }
707 }
708 }
709
710 dstStream.numElements = srcStream.count;
711 dstStream.type = VertexDataType::EVertexDataType(srcStream.vertexDataType);
712 dstStream.stride = srcStream.stride;
713 dstStream.offset = srcStream.offset;
714
715 dstMesh->streamIndexes[dstStream.type] = uint8_t(i);
716 dstMesh->streamsUpdated |= Mesh::toFlag(dstStream.type);
717 dstMesh->setMeshFlag(MeshFlags::StreamsChanged);
718
719 if (srcStream.vertexDataType < VertexDataType::LastVertexType) {
720 dstStream.format = isValidIndex(srcStream.firstAttribute)
721 ? getVertexFormat(rctx, srcStream.firstAttribute, srcStream.attributeCount)
723
724 if (!dstStream.format) {
725 LOG_ERROR(logger, "Stream %zu has illegal format", i);
726 return;
727 }
728
729 if (!dstMesh->isIndexed()) {
730 switch (dstStream.type) {
731 case VertexDataType::Positions:
732 case VertexDataType::Interleaved0:
733 case VertexDataType::Interleaved1:
734 dstMesh->setCount(srcStream.count);
735 break;
736 default:
737 break;
738 }
739 }
740 } else if (srcStream.vertexDataType == VertexDataType::Indexes) {
741 dstMesh->setCount(srcStream.count);
742 dstMesh->setMeshFlag(MeshFlags::Indexed);
743 dstMesh->setMeshFlag(MeshFlags::IndexesChanged);
744 } else if (srcStream.vertexDataType == VertexDataType::SubMeshes) {
745 dstMesh->submeshCount = (uint16_t)srcStream.count;
746 }
747 }
748 }
749
750
751 template<typename T>
752 const T& getPropertyValue(ReadContext& rctx, const CogsBin::Property& prop)
753 {
754 if constexpr (sizeof(T) <= sizeof(uint32_t)) {
755 return *(const T*)(&prop.value);
756 }
757 else {
758 if (prop.value < rctx.propData.count) {
759 return *(const T*)(rctx.propData.base + prop.value);
760 }
761 else {
762 LOG_ERROR(logger, "Illegal property data offset %u", prop.value);
763 static T t = {};
764 return t;
765 }
766 }
767 }
768
769 TextureHandle populateEmbeddedTexture(ReadContext& rctx, uint32_t textureIx)
770 {
771 assert(textureIx < rctx.embeddedTextures.count);
772 if (auto handle = rctx.textureCache[textureIx]; HandleIsValid(handle)) {
773 return handle;
774 }
775 const auto & tex = rctx.embeddedTextures[textureIx];
776
777 assert(tex.buffer < rctx.buffers.count);
778 const auto & srcBuffer = rctx.buffers[tex.buffer];
779 const auto * bufferData = resolveDataOffset(rctx, srcBuffer.sectionOffset, srcBuffer.size) + tex.offset;
780
781 TextureLoadInfo * loadInfo = rctx.context->textureManager->createLoadInfo();
782
783 loadInfo->resourceId = (uint32_t)NoResourceId;
784 //loadInfo.resourcePath;
785 if (isValidIndex(tex.name)) {
786 loadInfo->resourceName = getString(rctx, tex.name).to_string();
787 }
788 loadInfo->resourceData.assign(bufferData, bufferData + tex.size);
789 //loadInfo.resourceFlags;
790 //loadInfo.handle;
791 loadInfo->format = getTextureFormat(rctx, tex.format);
792 loadInfo->target = getTextureDimensions(tex.dimension);
793 loadInfo->width = static_cast<int>(tex.width);
794 loadInfo->height = static_cast<int>(tex.height);
795 loadInfo->depth = static_cast<int>(tex.depth);
796 loadInfo->stride = 0;
797 //loadInfo.flip = 0;
798
799 // Setting loadInfo->addressMode has no effect.
800
801 auto handle = rctx.context->textureManager->loadTexture(loadInfo);
802
803 rctx.textureCache[textureIx] = handle;
804
805 return handle;
806 }
807
808 MaterialInstanceHandle populateMaterialInstance(ReadContext& rctx, uint32_t materialInstanceIx)
809 {
810 const auto & srcMaterialInstance = rctx.materialInstances[materialInstanceIx];
811 if (!isValidIndex(srcMaterialInstance.material)) {
812 LOG_ERROR(logger, "Material instance without material.");
813 rctx.success = false;
815 }
816
817 auto materialName = getString(rctx, srcMaterialInstance.material);
818 if (materialName.empty()) {
819 LOG_ERROR(logger, "Writer should not include empty strings.");
820 rctx.success = false;
822 }
823
824 auto materialTemplate = rctx.context->materialManager->getMaterial(materialName);
825 if (!materialTemplate) {
826 LOG_ERROR(logger, "Invalid material template %.*s", StringViewFormat(materialName));
827 rctx.success = false;
829 }
830 //LOG_DEBUG(logger, "Instancing material %.*s:", StringViewFormat(materialName));
831
832 auto material = rctx.context->materialInstanceManager->createMaterialInstance(materialTemplate);
833
834 if (isValidIndex(srcMaterialInstance.permutation)) {
835 material->setPermutation(getString(rctx, srcMaterialInstance.permutation));
836 }
837
838 for (uint32_t i = 0; i < srcMaterialInstance.propertyCount; i++) {
839 if (rctx.props.count <= srcMaterialInstance.propertyFirst + i) {
840 LOG_ERROR(logger, "Invalid property index.");
841 rctx.success = false;
843 }
844
845 auto & prop = rctx.props[srcMaterialInstance.propertyFirst + i];
846 switch (prop.keyType) {
847 case CogsBin::PropertyKeyType::Index:
848 assert(false && "FIXME");
849 break;
850
851 case CogsBin::PropertyKeyType::String: {
852
853 auto keyString = getString(rctx, prop.key);
854 switch (prop.valueType) {
855
856 case CogsBin::PropertyValueType::Bool:
857 if (auto key = materialTemplate->getBoolKey(keyString); key != NoProperty)
858 material->setBoolProperty(key, getPropertyValue<uint32_t>(rctx, prop));
859 else
860 LOG_ERROR(logger, "Invalid bool key %.*s", StringViewFormat(keyString));
861 break;
862
863 case CogsBin::PropertyValueType::UInt:
864 if (auto key = materialTemplate->getUIntKey(keyString); key != NoProperty)
865 material->setUIntProperty(key, getPropertyValue<uint32_t>(rctx, prop));
866 else
867 LOG_ERROR(logger, "Invalid uint key %.*s", StringViewFormat(keyString));
868 break;
869
870 case CogsBin::PropertyValueType::Float:
871 if (auto key = materialTemplate->getFloatKey(keyString); key != NoProperty)
872 material->setFloatProperty(key, getPropertyValue<float>(rctx, prop));
873 else
874 LOG_ERROR(logger, "Invalid float key %.*s", StringViewFormat(keyString));
875 break;
876
877 case CogsBin::PropertyValueType::Float2:
878 if (auto key = materialTemplate->getVec2Key(keyString); key != NoProperty)
879 material->setVec2Property(key, getPropertyValue<glm::vec2>(rctx, prop));
880 else
881 LOG_ERROR(logger, "Invalid float2 key %.*s", StringViewFormat(keyString));
882 break;
883
884 case CogsBin::PropertyValueType::Float3:
885 if (auto key = materialTemplate->getVec3Key(keyString); key != NoProperty)
886 material->setVec3Property(key, getPropertyValue<glm::vec3>(rctx, prop));
887 else
888 LOG_ERROR(logger, "Invalid float3 key %.*s", StringViewFormat(keyString));
889 break;
890
891 case CogsBin::PropertyValueType::Float4:
892 if (auto key = materialTemplate->getVec4Key(keyString); key != NoProperty)
893 material->setVec4Property(key, getPropertyValue<glm::vec4>(rctx, prop));
894 else
895 LOG_ERROR(logger, "Invalid float4 key %.*s", StringViewFormat(keyString));
896 break;
897
898 case CogsBin::PropertyValueType::ExternalTexture:
899 if (auto key = materialTemplate->getTextureKey(keyString); key != NoProperty) {
900 auto textureFlags = TextureLoadFlags::None;
901 if (const auto & mprop = materialTemplate->textureProperties[key]; ((int)mprop.flags & (int)MaterialPropertyFlags::sRGB) == 0) {
902 textureFlags |= TextureLoadFlags::LinearColorSpace;
903 }
904 auto textureIx = getPropertyValue<uint32_t>(rctx, prop);
905 assert(textureIx < rctx.externalTextures.count);
906 const CogsBin::ExternalTexture& tex = rctx.externalTextures[textureIx];
907
908 std::string sourcePath = getString(rctx, tex.source).to_string();
909 if (Cogs::IO::isRelative(sourcePath)) {
910 sourcePath = Cogs::IO::combine(Cogs::IO::parentPath(rctx.path), sourcePath);
911 }
912 auto texture = rctx.context->textureManager->loadTexture(sourcePath, NoResourceId, textureFlags);
913 material->setTextureProperty(key, texture);
914
915 assert(tex.samplerState < rctx.samplerStates.count);
916 CogsBin::SamplerState srcSampler = rctx.samplerStates[tex.samplerState];
917 // Currently, just the texture addressing mode is used.
918 material->setTextureAddressMode(key,
919 getTextureAddressMode(srcSampler.addressModeS),
920 getTextureAddressMode(srcSampler.addressModeT),
921 getTextureAddressMode(srcSampler.addressModeW));
922 material->setTextureFilterMode(key, getTextureFilterMode(srcSampler.filter));
923 }
924 else
925 LOG_ERROR(logger, "Invalid string key %.*s", StringViewFormat(keyString));
926 break;
927
928 case CogsBin::PropertyValueType::EmbeddedTexture:
929 if (auto key = materialTemplate->getTextureKey(keyString); key != NoProperty) {
930 auto textureIx = getPropertyValue<uint32_t>(rctx, prop);
931
932 auto texture = populateEmbeddedTexture(rctx, textureIx);
933 material->setTextureProperty(key, texture);
934
935 const CogsBin::EmbeddedTexture& tex = rctx.embeddedTextures[textureIx];
936 assert(tex.samplerState < rctx.samplerStates.count);
937 CogsBin::SamplerState srcSampler = rctx.samplerStates[tex.samplerState];
938 // Currently, just the texture addressing mode is used.
939 material->setTextureAddressMode(key,
940 getTextureAddressMode(srcSampler.addressModeS),
941 getTextureAddressMode(srcSampler.addressModeT),
942 getTextureAddressMode(srcSampler.addressModeW));
943 material->setTextureFilterMode(key, getTextureFilterMode(srcSampler.filter));
944 }
945 break;
946
947 case CogsBin::PropertyValueType::Variant: {
948 auto key = getString(rctx, prop.key);
949 auto val = getString(rctx, prop.value);
950 material->setVariant(key, val);
951 //LOG_DEBUG(logger, "Variant %.*s=%.*s",
952 // StringViewFormat(key),
953 // StringViewFormat(val));
954 break;
955 }
956
957 case CogsBin::PropertyValueType::Option: {
958 auto key = getString(rctx, prop.key);
959 auto val = getString(rctx, prop.value);
960 material->setOption(key, val);
961 //LOG_DEBUG(logger, "Option %.*s=%.*s",
962 // StringViewFormat(key),
963 // StringViewFormat(val));
964 break;
965 }
966
967 default:
968 LOG_ERROR(logger, "Invalid property value type.");
969 rctx.success = false;
971 }
972 break;
973 }
974 default:
975 LOG_ERROR(logger, "Invalid property key type.");
976 rctx.success = false;
978 }
979 }
980 return material;
981
982 }
983
984 template<typename ResourceProxy>
985 void populatePart(ReadContext& rctx, ResourceProxy& model, ModelPart& part, const CogsBin::Node& node)
986 {
987 // parentIndex is populated by caller
988
989 if (isValidIndex(node.boundingBox)) {
990 part.boundsIndex = uint32_t(model->bounds.size());
991 model->bounds.emplace_back(getBoundingBox(rctx, node.boundingBox));
992 }
993 if (isValidIndex(node.transform)) {
994 const auto * m = rctx.transforms[node.transform].m;
995 model->setPartTransform(part, glm::mat4(m[0], m[1], m[2], 0.f,
996 m[3], m[4], m[5], 0.f,
997 m[6], m[7], m[8], 0.f,
998 m[9], m[10], m[11], 1.f));
999 }
1000 if (isValidIndex(node.name)) {
1001 model->setPartName(part, getString(rctx, node.name));
1002 }
1003 if (isValidIndex(node.mesh)) {
1004 part.meshIndex = uint32_t(model->meshes.size());
1005 model->meshes.emplace_back(rctx.context->meshManager->create());
1006 if (rctx.group != NoTask) {
1007 rctx.context->taskManager->enqueueChild(rctx.group, [rc = &rctx, h = model->meshes.back(), ix = node.mesh](){populateMesh(*rc, h, ix); });
1008 }
1009 else {
1010 populateMesh(rctx, model->meshes.back(), node.mesh);
1011 }
1012 }
1013 if (isValidIndex(node.materialInstance)) {
1014 part.materialIndex = uint32_t(model->materials.size());
1015 model->materials.emplace_back(populateMaterialInstance(rctx, node.materialInstance));
1016 }
1017 if (node.primitiveType != CogsBin::PrimitiveType::Unknown) {
1018 part.primitiveType = translatePrimitiveType(rctx, node.primitiveType);
1019 } else {
1020 part.primitiveType = (uint32_t)-1;
1021 }
1022 part.startIndex = node.vertexFirst;
1023 part.vertexCount = node.vertexCount;
1024 }
1025
1026 void populateBone(ReadContext& rctx, Bone& dstBone, const uint32_t boneIx, const uint32_t boneCount)
1027 {
1028 if (rctx.bones.count <= boneIx) {
1029 LOG_ERROR(logger, "Invalid bone index %u (bone count is %zu)", boneIx, rctx.bones.count);
1030 rctx.success = false;
1031 return;
1032 }
1033 const auto & srcBone = rctx.bones[boneIx];
1034 dstBone.name = getString(rctx, srcBone.name).to_string();
1035 if (isValidIndex(srcBone.parentBone)) {
1036 if (boneCount <= srcBone.parentBone) {
1037 LOG_ERROR(logger, "Invalid parent bone index %u (skeleton bone count is %u)", unsigned(srcBone.parentBone), boneCount);
1038 rctx.success = false;
1039 return;
1040 }
1041 dstBone.parentBone = srcBone.parentBone;
1042 }
1043 else {
1044 dstBone.parentBone = (size_t)-1;
1045 }
1046 dstBone.pos = srcBone.pos;
1047 dstBone.scale = srcBone.scale;
1048 dstBone.rot = srcBone.rot;
1049 dstBone.relative = srcBone.relative;
1050 dstBone.inverseBindPose = srcBone.inverseBindPose;
1051 dstBone.hasOffset = (srcBone.flags & CogsBin::BoneFlags::HasOffset) != 0;
1052 dstBone.used = (srcBone.flags & CogsBin::BoneFlags::Used) != 0;
1053 dstBone.animated = (srcBone.flags & CogsBin::BoneFlags::Animated) != 0;
1054 }
1055
1056 void populateSkeleton(ReadContext& rctx, Skeleton& dstSkeleton, const uint32_t skeletonIx)
1057 {
1058 if (rctx.skeletons.count <= skeletonIx) {
1059 LOG_ERROR(logger, "Invalid skeleton index %u (skeleton count is %zu)", skeletonIx, rctx.skeletons.count);
1060 rctx.success = false;
1061 return;
1062 }
1063 const auto & srcSkeleton = rctx.skeletons[skeletonIx];
1064 dstSkeleton.bindPose = srcSkeleton.bindPose;
1065 dstSkeleton.bones.resize(srcSkeleton.boneCount);
1066 for (uint32_t i = 0; i < srcSkeleton.boneCount && rctx.success; i++) {
1067 populateBone(rctx, dstSkeleton.bones[i], srcSkeleton.firstBone + i, srcSkeleton.boneCount);
1068 dstSkeleton.bonesByName[dstSkeleton.bones[i].name] = i;
1069 }
1070 updateSkeletonPose(dstSkeleton);
1071 }
1072
1073 void populateAnimTrack(ReadContext& rctx, AnimationTrack& dstTrack, const uint32_t animTrackIx)
1074 {
1075 assert(animTrackIx < rctx.animTracks.count);
1076 const auto & srcTrack = rctx.animTracks[animTrackIx];
1077
1078 assert(srcTrack.buffer < rctx.buffers.count);
1079 const auto & buffer = rctx.buffers[srcTrack.buffer];
1080 const auto * bufferData = resolveDataOffset(rctx, buffer.sectionOffset, buffer.size);
1081
1082 dstTrack.boneIndex = isValidIndex(srcTrack.boneIndex) ? srcTrack.boneIndex : -1;
1083
1084 const auto * translationSrc = (const float*)(bufferData + srcTrack.translationOffset);
1085 dstTrack.translations.resize(srcTrack.translationCount);
1086 for (unsigned i = 0; i < srcTrack.translationCount; i++) {
1087 dstTrack.translations[i].t = *translationSrc++;
1088 dstTrack.translations[i].value[0] = *translationSrc++;
1089 dstTrack.translations[i].value[1] = *translationSrc++;
1090 dstTrack.translations[i].value[2] = *translationSrc++;
1091 }
1092
1093 const auto * rotationSrc = (const float*)(bufferData + srcTrack.rotationOffset);
1094 dstTrack.rotations.resize(srcTrack.rotationCount);
1095 for (unsigned i = 0; i < srcTrack.rotationCount; i++) {
1096 dstTrack.rotations[i].t = *rotationSrc++;
1097 dstTrack.rotations[i].value[0] = *rotationSrc++;
1098 dstTrack.rotations[i].value[1] = *rotationSrc++;
1099 dstTrack.rotations[i].value[2] = *rotationSrc++;
1100 dstTrack.rotations[i].value[3] = *rotationSrc++;
1101 }
1102
1103 const auto * scaleSrc = (const float*)(bufferData + srcTrack.scaleOffset);
1104 dstTrack.scales.resize(srcTrack.scaleCount);
1105 for (unsigned i = 0; i < srcTrack.scaleCount; i++) {
1106 dstTrack.scales[i].t = *scaleSrc++;
1107 dstTrack.scales[i].value[0] = *scaleSrc++;
1108 dstTrack.scales[i].value[1] = *scaleSrc++;
1109 dstTrack.scales[i].value[2] = *scaleSrc++;
1110 }
1111 }
1112
1113 void populateAnimClip(ReadContext& rctx, AnimationClip& dstClip, const uint32_t animClipIx)
1114 {
1115 if (rctx.animClips.count <= animClipIx) {
1116 LOG_ERROR(logger, "Invalid animation clip index %u (animation clip count is %zu)", animClipIx, rctx.animClips.count);
1117 rctx.success = false;
1118 return;
1119 }
1120 const CogsBin::AnimClip& srcClip = rctx.animClips[animClipIx];
1121 if (isValidIndex(srcClip.name)) {
1122 dstClip.name = std::string(getString(rctx, srcClip.name));
1123 }
1124 dstClip.duration = srcClip.duration;
1125 dstClip.resolution = srcClip.resolution;
1126 dstClip.tracks.resize(srcClip.trackCount);
1127 for (unsigned i = 0; i < srcClip.trackCount && rctx.success; i++) {
1128 populateAnimTrack(rctx, dstClip.tracks[i], srcClip.trackFirst + i);
1129 }
1130 }
1131
1132 void populateModel(ReadContext& rctx, ResourceHandleBase modelHandle, const uint32_t modelIx)
1133 {
1134 const auto & srcModel = rctx.models[modelIx];
1135 if (srcModel.nodeCount == 0) return;
1136
1137 assert(modelHandle && "Model handle without resource");
1138 auto dstModel = rctx.context->modelManager->lock(modelHandle);
1139 assert(dstModel->parts.empty() && "Destination model is not empty");
1140
1141 // Populate skeleton
1142 if (isValidIndex(srcModel.skeleton)) populateSkeleton(rctx, dstModel->skeleton, srcModel.skeleton);
1143
1144 // Populate animation
1145 if (srcModel.animClipCount != 0) {
1146 dstModel->animation = rctx.context->animationManager->create();
1147
1148 auto * anim = dstModel->animation.resolve();
1149 anim->skeleton = dstModel->skeleton; // use model skeleton as animation skeleton.
1150 anim->clips.resize(srcModel.animClipCount);
1151 for (uint32_t i = 0; i < srcModel.animClipCount && rctx.success; i++) {
1152 populateAnimClip(rctx, anim->clips[i], srcModel.animClipFirst + i);
1153 anim->clipsByName[anim->clips[i].name] = i;
1154 }
1155 }
1156
1157 // Populate parts
1158 dstModel->parts.resize(srcModel.nodeCount);
1159 for (size_t i = 0; i < srcModel.nodeCount && rctx.success; i++) {
1160 if (!isValidIndex(srcModel.firstNode) || rctx.nodes.count <= srcModel.firstNode + i) {
1161 LOG_ERROR(logger, "Invalid node index %u (node count is %zu)", srcModel.firstNode, rctx.nodes.count);
1162 rctx.success = false;
1163 return;
1164 }
1165 const auto & node = rctx.nodes[srcModel.firstNode + i];
1166 auto & part = dstModel->parts[i];
1167 if (!isValidIndex(node.parent)) {
1168 part.parentIndex = ~0u;
1169 }
1170 else if (node.parent < srcModel.firstNode || srcModel.firstNode + srcModel.nodeCount <= node.parent) {
1171 LOG_ERROR(logger, "Illegal parent node index %u (node count is %zu)", node.parent, rctx.nodes.count);
1172 rctx.success = false;
1173 return;
1174 }
1175 else {
1176 part.parentIndex = node.parent - srcModel.firstNode;
1177 }
1178 populatePart(rctx, dstModel, part, node);
1179 }
1180 }
1181
1182
1183}
1184
1185bool Cogs::Core::CogsModelLoader::canLoad(Context * /*context*/, const ModelLoadInfo & loadInfo)
1186{
1187 auto extension = IO::extension(loadInfo.resourcePath);
1188
1189 if (extension == ".cogsbin") {
1190 return true;
1191 }
1192
1193 return false;
1194}
1195
1196bool Cogs::Core::CogsModelLoader::load(Context * context, const ModelLoadInfo & loadInfo, std::unique_ptr<FileContents> contents_)
1197{
1198 if (!contents_ || contents_->size < sizeof(CogsBin::Header)) {
1199 return false;
1200 }
1201
1202 ReadContext rctx;
1203 {
1204 const CogsBin::Header* header = (const CogsBin::Header*)contents_->ptr;
1205 if (header->magic == CogsBin::HeaderMagic) {
1206 if(header->version == 1) {
1207 return loadCogsBin1(context, loadInfo, std::move(contents_));
1208 }
1209 else if (header->version == 3) {
1210 return loadCogsBin3(context, loadInfo, std::move(contents_));
1211 }
1212 else if (5 < header->version) {
1213 LOG_ERROR(logger, "Unsupported file format version %u", header->version);
1214 return false;
1215 }
1216 }
1217 else {
1218 LOG_ERROR(logger, "Missing magic value");
1219 return false;
1220 }
1221
1222 rctx.version = header->version;
1223 }
1224 // TODO: Check alignment restrictions for cogsbin2, cogsbin1 handles in its load func.
1225
1226 rctx.context = context;
1227 rctx.contents = std::move(contents_);
1228
1229 if (loadCount.fetch_add(1) < COGS_MODELLOADER_MAX_TASKS) {
1230 rctx.group = context->taskManager->createGroup(TaskManager::ResourceQueue);
1231 }
1232 rctx.path = loadInfo.resourcePath;
1233 rctx.success = true;
1234
1235 unsigned modelIx = 0u;
1236 if (auto i = rctx.path.find('#'); i != std::string::npos) {
1237 if (i + 1 < rctx.path.length()) {
1238 modelIx = std::atoi(rctx.path.c_str() + i + 1);
1239 }
1240 rctx.path.resize(i);
1241 }
1242
1243 if (rctx.contents->size < sizeof(CogsBin::Header)) {
1244 LOG_ERROR(logger, "Model file too small to contain a header, unable to load.");
1245 return false;
1246 }
1247
1248 rctx.header = (CogsBin::Header *)rctx.contents->ptr;
1249 if (rctx.header->sectionCount == 0) {
1250 LOG_ERROR(logger, "No sections in file.");
1251 return false;
1252 }
1253 if (rctx.header->fileLength != rctx.contents->size) {
1254 LOG_ERROR(logger, "File size mismatch in read context, potentially corrupt or truncated model file.");
1255 return false;
1256 }
1257
1258 rctx.sectionInfo = std::span<CogsBin::SectionInfo>((CogsBin::SectionInfo*)(rctx.contents->ptr + sizeof(CogsBin::Header)),
1259 (CogsBin::SectionInfo*)(rctx.contents->ptr + sizeof(CogsBin::Header)) + rctx.header->sectionCount);
1260 rctx.sectionPtr.resize(rctx.header->sectionCount);
1261 rctx.sectionLocks.reset(new Mutex[rctx.header->sectionCount]);
1262
1263 LOG_TRACE(logger, "Parsed header, %u sections, filesize=%zu", rctx.header->sectionCount, rctx.contents->size);
1264
1265
1266 if ((rctx.sectionInfo[0].type & CogsBin::SectionType::META) == 0) {
1267 LOG_ERROR(logger, "First section is not a meta section.");
1268 return false;
1269 }
1270
1271 const auto * sectionBase = sectionPointer(rctx, 0);
1272 if (sectionBase == nullptr) return false;
1273
1274 for (uint16_t sub = 0; sub < rctx.sectionInfo[0].subSectionCount; sub++) {
1275 const auto & metaSubSection = ((CogsBin::SubSectionInfo*)sectionBase)[sub];
1276 switch (metaSubSection.type)
1277 {
1278 case CogsBin::SubSectionType::Buffers:
1279 rctx.buffers.base = (const CogsBin::Buffer*)(sectionBase + metaSubSection.offset);
1280 rctx.buffers.count = metaSubSection.size / sizeof(CogsBin::Buffer);
1281 rctx.bufferCache.resize(rctx.buffers.count);
1282 //LOG_DEBUG(logger, "Found %zu buffers.", rctx.buffers.count);
1283 break;
1284 case CogsBin::SubSectionType::ExternalTextures:
1285 rctx.externalTextures.base = (const CogsBin::ExternalTexture*)(sectionBase + metaSubSection.offset);
1286 rctx.externalTextures.count = metaSubSection.size / sizeof(CogsBin::ExternalTexture);
1287 //LOG_DEBUG(logger, "Found %zu textures.", rctx.textures.count);
1288 break;
1289 case CogsBin::SubSectionType::EmbeddedTextures:
1290 rctx.embeddedTextures.base = (const CogsBin::EmbeddedTexture*)(sectionBase + metaSubSection.offset);
1291 rctx.embeddedTextures.count = metaSubSection.size / sizeof(CogsBin::EmbeddedTexture);
1292 rctx.textureCache.resize(rctx.embeddedTextures.count);
1293 //LOG_DEBUG(logger, "Found %zu embedded textures.", rctx.embeddedTextures.count);
1294 break;
1295 case CogsBin::SubSectionType::SamplerStates:
1296 rctx.samplerStates.base = (const CogsBin::SamplerState*)(sectionBase + metaSubSection.offset);
1297 rctx.samplerStates.count = metaSubSection.size / sizeof(CogsBin::SamplerState);
1298 //LOG_DEBUG(logger, "Found %zu sampler states.", rctx.samplerStates.count);
1299 break;
1300 case CogsBin::SubSectionType::Strings:
1301 rctx.strings.base = (const CogsBin::String*)(sectionBase + metaSubSection.offset);
1302 rctx.strings.count = metaSubSection.size / sizeof(CogsBin::String);
1303 //LOG_DEBUG(logger, "Found %zu strings.", rctx.strings.count);
1304 break;
1305 case CogsBin::SubSectionType::StringData:
1306 rctx.stringData.base = (char*)(sectionBase + metaSubSection.offset);
1307 rctx.stringData.count = metaSubSection.size;
1308 //LOG_DEBUG(logger, "Found %zu stringData bytes.", rctx.stringData.count);
1309 break;
1310 case CogsBin::SubSectionType::Nodes:
1311 rctx.nodes.base = (const CogsBin::Node*)(sectionBase + metaSubSection.offset);
1312 rctx.nodes.count = metaSubSection.size / sizeof(CogsBin::Node);
1313 //LOG_DEBUG(logger, "Found %zu nodes.", rctx.nodes.count);
1314 break;
1315 case CogsBin::SubSectionType::Transforms:
1316 rctx.transforms.base = (const CogsBin::Transform*)(sectionBase + metaSubSection.offset);
1317 rctx.transforms.count = metaSubSection.size / sizeof(CogsBin::Transform);
1318 //LOG_DEBUG(logger, "Found %zu transforms.", rctx.transforms.count);
1319 break;
1320 case CogsBin::SubSectionType::BoundingBoxes:
1321 rctx.boundingBoxes.base = (const Cogs::Geometry::BoundingBox*)(sectionBase + metaSubSection.offset);
1322 rctx.boundingBoxes.count = metaSubSection.size / sizeof(Cogs::Geometry::BoundingBox);
1323 //LOG_DEBUG(logger, "Found %zu bounding boxes.", rctx.boundingBoxes.count);
1324 break;
1325 case CogsBin::SubSectionType::MaterialInstances:
1326 rctx.materialInstances.base = (const CogsBin::MaterialInstance*)(sectionBase + metaSubSection.offset);
1327 rctx.materialInstances.count = metaSubSection.size / sizeof(CogsBin::MaterialInstance);
1328 //LOG_DEBUG(logger, "Found %zu material instances.", rctx.materialInstances.count);
1329 break;
1330 case CogsBin::SubSectionType::Properties:
1331 rctx.props.base = (const CogsBin::Property*)(sectionBase + metaSubSection.offset);
1332 rctx.props.count = metaSubSection.size / sizeof(CogsBin::Property);
1333 //LOG_DEBUG(logger, "Found %zu properties.", rctx.props.count);
1334 break;
1335 case CogsBin::SubSectionType::PropertyData:
1336 rctx.propData.base = sectionBase + metaSubSection.offset;
1337 rctx.propData.count = metaSubSection.size;
1338 //LOG_DEBUG(logger, "Found %zu propData bytes.", rctx.propData.count);
1339 break;
1340 case CogsBin::SubSectionType::Meshes:
1341 rctx.meshes.base = (const CogsBin::Mesh*)(sectionBase + metaSubSection.offset);
1342 rctx.meshes.count = metaSubSection.size / sizeof(CogsBin::Mesh);
1343 //LOG_DEBUG(logger, "Found %zu meshes.", rctx.meshes.count);
1344 break;
1345 case CogsBin::SubSectionType::VertexStreams:
1346 rctx.vertexStreams.base = (const CogsBin::VertexStream*)(sectionBase + metaSubSection.offset);
1347 rctx.vertexStreams.count = metaSubSection.size / sizeof(CogsBin::VertexStream);
1348 //LOG_DEBUG(logger, "Found %zu vertex streams.", rctx.vertexStreams.count);
1349 break;
1350 case CogsBin::SubSectionType::VertexAttributes:
1351 rctx.vertexAttributes.base = (const CogsBin::VertexAttribute*)(sectionBase + metaSubSection.offset);
1352 rctx.vertexAttributes.count = metaSubSection.size / sizeof(CogsBin::VertexAttribute);
1353 //LOG_DEBUG(logger, "Found %zu vertex attributes.", rctx.vertexAttributes.count);
1354 break;
1355 case CogsBin::SubSectionType::Bones:
1356 rctx.bones.base = (const CogsBin::Bone*)(sectionBase + metaSubSection.offset);
1357 rctx.bones.count = metaSubSection.size / sizeof(CogsBin::Bone);
1358 //LOG_DEBUG(logger, "Found %zu bones.", rctx.bones.count);
1359 break;
1360 case CogsBin::SubSectionType::Skeletons:
1361 rctx.skeletons.base = (const CogsBin::Skeleton*)(sectionBase + metaSubSection.offset);
1362 rctx.skeletons.count = metaSubSection.size / sizeof(CogsBin::Skeleton);
1363 //LOG_DEBUG(logger, "Found %zu skeletons.", rctx.skeletons.count);
1364 break;
1365 case CogsBin::SubSectionType::AnimTracks:
1366 rctx.animTracks.base = (const CogsBin::AnimTrack*)(sectionBase + metaSubSection.offset);
1367 rctx.animTracks.count = metaSubSection.size / sizeof(CogsBin::AnimTrack);
1368 //LOG_DEBUG(logger, "Found %zu anim tracks.", rctx.animTracks.count);
1369 break;
1370 case CogsBin::SubSectionType::AnimClips:
1371 rctx.animClips.base = (const CogsBin::AnimClip*)(sectionBase + metaSubSection.offset);
1372 rctx.animClips.count = metaSubSection.size / sizeof(CogsBin::AnimClip);
1373 //LOG_DEBUG(logger, "Found %zu anim clips.", rctx.animClips.count);
1374 break;
1375 case CogsBin::SubSectionType::Models:
1376 rctx.models.base = (const CogsBin::Model*)(sectionBase + metaSubSection.offset);
1377 rctx.models.count = metaSubSection.size / sizeof(CogsBin::Model);
1378 //LOG_DEBUG(logger, "Found %zu models.", rctx.models.count);
1379 break;
1380 default:
1381 LOG_ERROR(logger, "Unknown subsection type %u", unsigned(metaSubSection.type));
1382 }
1383 }
1384
1385 // Try to detect problems upfront gracefully
1386 for (uint32_t i = 0; i < rctx.buffers.count; i++) {
1387 const auto & buffer = rctx.buffers[i];
1388 const auto sectionIx = buffer.sectionOffset >> 48;
1389 const auto sectionOffset = size_t(buffer.sectionOffset & 0xFFFFFFFFFFFFull);
1390 if (rctx.header->sectionCount <= sectionIx) {
1391 LOG_ERROR(logger, "Illegal section index %u (section count is %u)", unsigned(sectionIx), rctx.header->sectionCount);
1392 return false;
1393 }
1394 const auto & sectionInfo = rctx.sectionInfo[sectionIx];
1395 if (sectionInfo.uncompressedSize < sectionOffset + buffer.size) {
1396 LOG_ERROR(logger, "Buffer out of bounds");
1397 return false;
1398 }
1399 }
1400
1401 for (uint32_t i = 0; i < rctx.embeddedTextures.count; i++) {
1402 const auto & tex = rctx.embeddedTextures[i];
1403 if (rctx.buffers.count <= tex.buffer) { LOG_ERROR(logger, "Texture has illegal buffer index %u", tex.buffer); return false; }
1404 auto & buf = rctx.buffers[tex.buffer];
1405 if (buf.size < tex.offset + tex.size) { LOG_ERROR(logger, "Texture buffer view outside buffer"); return false; }
1406 if (isValidIndex(tex.name) && rctx.strings.count <= tex.name) { LOG_ERROR(logger, "Illegal name string %u", tex.name); return false; }
1407 if (0x10000 < tex.width) { LOG_ERROR(logger, "Suspicious texture width %u", tex.width); return false; }
1408 if (0x10000 < tex.height) { LOG_ERROR(logger, "Suspicious texture height %u", tex.height); return false; }
1409 if (0x10000 < tex.depth) { LOG_ERROR(logger, "Suspicious texture depth %u", tex.depth); return false; }
1410 if (6 < tex.faces) { LOG_ERROR(logger, "Suspicious texture faces %u", tex.faces); return false; }
1411 if (0x10000 < tex.layers) { LOG_ERROR(logger, "Suspicious texture layers %u", tex.layers); return false; }
1412 if (CogsBin::Format::EnumSize <= tex.format) { LOG_ERROR(logger, "Illegal format %u", unsigned(tex.format)); return false; }
1413 if (CogsBin::TextureEncoding::EnumSize <= tex.encoding) { LOG_ERROR(logger, "Illegal encoding %u", unsigned(tex.encoding)); return false; }
1414 }
1415
1416 for (uint32_t i = 0; i < rctx.vertexStreams.count; i++) {
1417 const auto & dataStream = rctx.vertexStreams[i];
1418 if (rctx.buffers.count <= dataStream.buffer) {
1419 LOG_ERROR(logger, "Vertex stream has illegal buffer index %u (count is %zu)", dataStream.buffer, rctx.buffers.count);
1420 return false;
1421 }
1422 if (dataStream.vertexDataType < VertexDataType::LastVertexType) {
1423 if (!isValidIndex(dataStream.firstAttribute)) {
1424 LOG_ERROR(logger, "Vertex stream without attributes");
1425 return false;
1426 }
1427 if (rctx.vertexAttributes.count <= dataStream.firstAttribute || rctx.vertexAttributes.count < dataStream.firstAttribute + dataStream.attributeCount) {
1428 LOG_ERROR(logger, "Vertex stream has illegal vertex attribute indices first=%u count=%u (total count is %zu)",
1429 dataStream.firstAttribute, dataStream.attributeCount, rctx.vertexAttributes.count);
1430 return false;
1431 }
1432 } else if (dataStream.vertexDataType == VertexDataType::Indexes) {
1433 if (dataStream.stride != 4 && dataStream.stride != 2) {
1434 LOG_ERROR(logger, "Index stream has unsupported stride.");
1435 return false;
1436 }
1437 }
1438
1439 if (!isValidIndex(dataStream.buffer)) {
1440 LOG_ERROR(logger, "Data stream without valid buffer.");
1441 return false;
1442 }
1443 }
1444
1445 for (uint32_t i = 0; i < rctx.animTracks.count; i++) {
1446 const auto & track = rctx.animTracks[i];
1447 if (rctx.buffers.count <= track.buffer) {
1448 LOG_ERROR(logger, "Anim track has illegal buffer index %u.", track.buffer);
1449 return false;
1450 }
1451 const auto & buffer = rctx.buffers[track.buffer];
1452 if (buffer.size < track.translationOffset + 4 * sizeof(float)*track.translationCount) {
1453 LOG_ERROR(logger, "Anim track translation buffer view is outside buffer.");
1454 return false;
1455 }
1456 if (buffer.size < track.rotationOffset + 5 * sizeof(float)*track.rotationCount) {
1457 LOG_ERROR(logger, "Anim track translation buffer view is outside buffer.");
1458 return false;
1459 }
1460 if (buffer.size < track.scaleOffset + 4 * sizeof(float)*track.scaleCount) {
1461 LOG_ERROR(logger, "Anim track translation buffer view is outside buffer.");
1462 return false;
1463 }
1464 }
1465
1466 for (uint32_t i = 0; i < rctx.meshes.count; i++) {
1467 const auto & mesh = rctx.meshes[i];
1468 if (isValidIndex(mesh.name) && rctx.strings.count <= mesh.name) {
1469 LOG_ERROR(logger, "Mesh name has illegal string index %u (string count is %zu)", mesh.name, rctx.strings.count);
1470 return false;
1471 }
1472 if (isValidIndex(mesh.boundingBox) && rctx.boundingBoxes.count <= mesh.boundingBox) {
1473 LOG_ERROR(logger, "Mesh name has illegal boundingBox index %u (count is %zu)", mesh.boundingBox, rctx.boundingBoxes.count);
1474 return false;
1475 }
1476 if (isValidIndex(mesh.firstStream) && rctx.vertexStreams.count <= mesh.firstStream) {
1477 LOG_ERROR(logger, "Mesh name has illegal data stream index %u (count is %zu)", mesh.firstStream, rctx.vertexStreams.count);
1478 return false;
1479 }
1480 }
1481
1482 if (modelIx < rctx.models.count) {
1483 populateModel(rctx, loadInfo.handle, modelIx);
1484 }
1485 else {
1486 LOG_ERROR(logger, "Invalid model index %u (model count=%zu)", modelIx, rctx.models.count);
1487 rctx.success = false;
1488 }
1489
1490 if (rctx.group != NoTask) {
1491 // wait here before we release the memory mapping.
1492 context->taskManager->wait(rctx.group);
1493 }
1494 loadCount--;
1495
1496 return rctx.success;
1497}
1498
1499
1500bool Cogs::Core::CogsModelLoader::load(Context * context, const ModelLoadInfo & loadInfo)
1501{
1502 LOG_DEBUG(logger, "Loading model: %s", loadInfo.resourceName.c_str());
1503
1504 Cogs::Timer ioTimer = Timer::startNew();
1505 Cogs::Timer timer = Timer::startNew();
1506
1507 std::unique_ptr<Cogs::FileContents> contents;
1508#ifdef COGS_USE_MMAP
1509 if (loadInfo.protocol == ResourceProtocol::Archive) {
1510#endif
1511 ResourceBuffer buffer = context->resourceStore->getResourceContents(loadInfo.resourcePath);
1512 if (!buffer.buffer->data()) {
1513 LOG_ERROR(logger, "Could not load resource data for model %s.", loadInfo.resourcePath.c_str());
1514 return false;
1515 }
1516 contents = std::make_unique<Cogs::Platform::ResourceBufferBackedFileContents>(buffer, loadInfo.resourcePath, Cogs::FileContentsHints::None);
1517#ifdef COGS_USE_MMAP
1518 }
1519 else {
1520 Cogs::FileHandle::Ptr file = Cogs::IO::openFile(loadInfo.resourcePath, Cogs::FileHandle::OpenMode::OpenAlways, Cogs::FileHandle::AccessMode::Read);
1521 if (!file) {
1522 LOG_ERROR(logger, "Could not open file %s for mapping.", loadInfo.resourcePath.c_str());
1523 return false;
1524 }
1525
1526 size_t size = Cogs::IO::fileSize(file);
1527 if (!size) {
1528 LOG_ERROR(logger, "File size invalid: %zd.", size);
1529 return false;
1530 }
1531
1532 const uint8_t* ptr = static_cast<const uint8_t*>(Cogs::IO::mapFile(file, Cogs::FileHandle::ProtectionMode::ReadOnly, 0, size));
1533 if (!ptr) {
1534 LOG_ERROR(logger, "Could not map file %s for reading model data.", loadInfo.resourcePath.c_str());
1535 return false;
1536 }
1537
1538 contents = std::make_unique<Cogs::MMapBackedFileContents>(ptr, size, file, loadInfo.resourcePath, Cogs::FileContentsHints::None);
1539 }
1540#endif
1541
1542 double ioElapsed = ioTimer.elapsedSeconds();
1543
1544 bool rv = load(context, loadInfo, std::move(contents));
1545
1546 LOG_TRACE(logger, "Elapsed: %f (IO: %f)", timer.elapsedSeconds(), ioElapsed);
1547
1548 return rv;
1549}
1550
1551
1552// #pragma optimize( "", on )
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
Definition: Context.h:210
static constexpr TaskQueueId ResourceQueue
Resource task queue.
Definition: TaskManager.h:232
Log implementation class.
Definition: LogManager.h:139
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
Old timer class.
Definition: Timer.h:37
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ InstanceData
Per instance data.
@ VertexData
Per vertex data.
@ Position
Position semantic.
@ Tangent
Tangent semantic.
@ Normal
Normal semantic.
@ InstanceMatrix
Instance matrix semantic.
@ InstanceVector
Instance vector semantic.
@ Color
Color semantic.
@ TextureCoordinate
Texture coordinate semantic.
uint32_t boneIndex
Specifies which bone of a skeleton that is animated by this track.
Definition: Animation.h:38
@ VertexBuffer
Buffer can be bound as vertex buffer.
Definition: Buffer.h:40
@ IndexBuffer
Buffer can be bound as index buffer.
Definition: Buffer.h:42
uint32_t name
Offset into strings subsection of current section.
Definition: CogsBin.h:765
Generic blob of data.
Definition: CogsBin.h:515
uint32_t size
Byte size of image data.
Definition: CogsBin.h:646
uint32_t offset
Byte offset within buffer for start of imagte data.
Definition: CogsBin.h:645
uint32_t buffer
Buffer that holds image data, index into buffers.
Definition: CogsBin.h:644
PrimitiveType primitiveType
Primitive type to use for this node.
Definition: CogsBin.h:780
uint32_t name
Name of node, offset into Strings subsection.
Definition: CogsBin.h:775
uint32_t vertexFirst
First vertex of mesh to draw.
Definition: CogsBin.h:781
uint32_t boundingBox
Bounding box of node, offset into BoundingBoxes subsection.
Definition: CogsBin.h:778
uint32_t materialInstance
Material instance of node, offset into MaterialInstances subsection.
Definition: CogsBin.h:777
uint32_t mesh
Mesh of node, offset into Meshes subsection.
Definition: CogsBin.h:776
uint32_t vertexCount
Number of vertices to draw.
Definition: CogsBin.h:782
uint32_t parent
Parent node, offset into Nodes subsection.
Definition: CogsBin.h:774
uint32_t transform
Transform of node, offset into Transforms subsection.
Definition: CogsBin.h:779
TextureAddressMode addressModeW
Specifies the addressing mode along the W axis in texture coordinate space.
Definition: CogsBin.h:602
TextureFilterMode filter
Specifies the filter to use for texture sampling.
Definition: CogsBin.h:604
TextureAddressMode addressModeS
Specifies the addressing mode along the S axis in texture coordinate space.
Definition: CogsBin.h:600
TextureAddressMode addressModeT
Specifies the addressing mode along the T axis in texture coordinate space.
Definition: CogsBin.h:601
@ StreamsChanged
One or more of the data streams in the mesh changed.
Definition: Mesh.h:59
@ IndexesChanged
The index data of the mesh changed.
Definition: Mesh.h:61
@ Indexed
The mesh should be drawn indexed, using index data to order the triangle vertexes.
Definition: Mesh.h:65
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Definition: Mesh.h:63
static constexpr uint16_t toFlag(VertexDataType::EVertexDataType type)
Convert the given type index to a flag.
Definition: Mesh.h:577
std::shared_ptr< Memory::MemoryBuffer > buffer
Actual resource contents. Prefer using access methods.
Definition: ResourceStore.h:61
Resource handle base class handling reference counting of resources derived from ResourceBase.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
std::string resourcePath
Resource path. Used to locate resource.
std::string resourceName
Desired resource name. If no name is given, a default name will be chosen.
ResourceId resourceId
Unique resource identifier. Must be unique among resources of the same kind.
ResourceHandleBase handle
Handle to resource structure for holding actual resource data.
std::vector< uint8_t > resourceData
Resource load data.
Task id struct used to identify unique Task instances.
Definition: TaskManager.h:20
EVertexDataType
Contains data types.
Definition: Mesh.h:26
EPrimitiveType
Primitive type enumeration.
Definition: Common.h:114
@ LineStrip
Line strip.
Definition: Common.h:122
@ LineStripAdjacency
Line strip with adjacency.
Definition: Common.h:132
@ LineList
List of lines.
Definition: Common.h:120
@ TriangleStrip
Triangle strip.
Definition: Common.h:118
@ TriangleListAdjacency
List of triangles with adjacency.
Definition: Common.h:126
@ PointList
List of points.
Definition: Common.h:124
@ TriangleStripAdjacency
Triangle strip with adjacency.
Definition: Common.h:128
@ LineListAdjacency
List of lines with adjacency.
Definition: Common.h:130
@ TriangleList
List of triangles.
Definition: Common.h:116
AddressMode
Addressing modes to use when sampling textures.
Definition: SamplerState.h:15
@ Clamp
Texture coordinates are clamped to the [0, 1] range.
Definition: SamplerState.h:17
@ Border
Texture color is set to the border color when outside [0, 1] range.
Definition: SamplerState.h:23
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
Definition: SamplerState.h:19
@ Mirror
Texture coordinates are mirrored when outside [0, 1] range.
Definition: SamplerState.h:21
FilterMode
Filter modes to specify how texture data is treated when sampled.
Definition: SamplerState.h:31
@ ComparisonMinMagMipPoint
Comparison filter for depth sample comparisons using point sampling.
Definition: SamplerState.h:38
@ ComparisonMinMagMipLinear
Comparison filter for depth sample comparisons using linear interpolation sampling.
Definition: SamplerState.h:40
@ MinMagMipPoint
Point sampling for both minification and magnification.
Definition: SamplerState.h:33
@ MinMagMipLinear
Linear sampling for both minification and magnification.
Definition: SamplerState.h:35