Cogs.Core
GltfLoader.h
1#pragma once
2
3#include "Serialization/JsonParser.h"
4
5#include "Services/TaskManager.h"
6#include "Resources/TextureManager.h"
7#include "Resources/ModelManager.h"
8#include "Resources/MeshManager.h"
9
10#include "Foundation/Platform/Timer.h"
11
12#include "Rendering/DataFormat.h"
13
14#include <base64.h>
15
16// NOTE: We cannot include the "stb_image.h" here as this will cause clashing symbols when linking in
17// Emscripten mode. We'll therefore just predefine the needed type and include the actual header-file
18// in the C++ file instead.
19typedef unsigned char stbi_uc; // Used in the GltfImage struct below (line ~180).
20
21#include <glm/gtx/matrix_decompose.hpp>
22
23#include <ctype.h>
24#include <stdint.h>
25#include <string>
26#include <vector>
27#include <map>
28#include <span>
29
30namespace Cogs::Core::GltfLoader {
31 using namespace rapidjson;
32
33 // Bool switch to disable the loader
34 static constexpr Cogs::StringView enableVariableName = "gltf.import.enable";
35
36 // Integer number that controls the amount of cleanup done when reading the mesh:
37 // 0 - Preserve as much as possible
38 // 1 - Strip unused vertex attributes
39 static const char* optimizeVariableName = "gltf.import.optimize";
40
41 typedef GenericMember<UTF8<>, RAPIDJSON_DEFAULT_ALLOCATOR> Member;
42 typedef GenericArray<true, Value> Array;
43 typedef GenericObject<true, Value> Object;
44
45 constexpr int GLTF_BYTE = 5120;
46 constexpr int GLTF_UNSIGNED_BYTE = 5121;
47 constexpr int GLTF_SHORT = 5122;
48 constexpr int GLTF_UNSIGNED_SHORT = 5123;
49 constexpr int GLTF_UNSIGNED_INT = 5125;
50 constexpr int GLTF_FLOAT = 5126;
51
52 constexpr unsigned GLTF_NEAREST = 9728;
53 constexpr unsigned GLTF_LINEAR = 9729;
54 constexpr unsigned GLTF_NEAREST_MIPMAP_NEAREST = 9984;
55 constexpr unsigned GLTF_LINEAR_MIPMAP_NEAREST = 9985;
56 constexpr unsigned GLTF_NEAREST_MIPMAP_LINEAR = 9986;
57 constexpr unsigned GLTF_LINEAR_MIPMAP_LINEAR = 9987;
58
59 //constexpr int GLTF_ARRAY_BUFFER = 34962;
60 //constexpr int GLTF_ELEMENT_ARRAY_BUFFER = 34963;
61
62 constexpr unsigned GLTF_CLAMP_TO_EDGE = 33071;
63 constexpr unsigned GLTF_MIRRORED_REPEAT = 33648;
64 constexpr unsigned GLTF_REPEAT = 10497;
65
66 enum struct AccessorComponentType
67 {
68 None = 0,
69 Byte,
70 UnsignedByte,
71 Short,
72 UnsignedShort,
73 UnsignedInt,
74 Float,
75 Count
76 };
77
78 static const char* accessorComponentTypeName[] = {
79 "None",
80 "Byte",
81 "UnsignedByte",
82 "Short",
83 "UnsignedShort",
84 "UnsignedInt",
85 "Float"
86 };
87 static_assert(sizeof(accessorComponentTypeName) == sizeof(accessorComponentTypeName[0]) * size_t(AccessorComponentType::Count));
88
89 static uint32_t accessorComponentTypeSize[] = {
90 0,
91 uint32_t(sizeof(int8_t)),
92 uint32_t(sizeof(uint8_t)),
93 uint32_t(sizeof(int16_t)),
94 uint32_t(sizeof(uint16_t)),
95 uint32_t(sizeof(uint32_t)),
96 uint32_t(sizeof(float))
97 };
98 static_assert(sizeof(accessorComponentTypeSize) == sizeof(accessorComponentTypeSize[0]) * size_t(AccessorComponentType::Count));
99
100 enum struct AccessorType {
101 None = 0,
102 Scalar,
103 Vec2, Vec3, Vec4,
104 Mat2, Mat3, Mat4,
105 Count
106 };
107
108 static const char* accessorTypeName[] = {
109 "None",
110 "Scalar",
111 "Vec2", "Vec3", "Vec4",
112 "Mat2", "Mat3", "Mat4"
113 };
114 static_assert(sizeof(accessorTypeName) == sizeof(accessorTypeName[0]) * size_t(AccessorType::Count));
115
116 static uint32_t accessorTypeSize[] = {
117 0,
118 1,
119 2, 3, 4,
120 2*2, 3*3, 4*4
121 };
122 static_assert(sizeof(accessorTypeSize) == sizeof(accessorTypeSize[0]) * size_t(AccessorType::Count));
123
124
126 {
127 uint32_t buffer = 0;
128 uint32_t byteLength = 0;
129 uint32_t byteOffset = 0;
130 uint32_t byteStride = 0; // TODO
131 uint32_t target = 0;
132 };
133
135 {
136 uint32_t bufferView = 0;
137 uint32_t byteOffset = 0;
138 uint32_t count = 0;
139
140 AccessorComponentType componentType = AccessorComponentType::None;
141 AccessorType type = AccessorType::None;
142
143 union {
144 int32_t s_int[4 * 4] = { 0 };
145 uint32_t s_uint[4 * 4];
146 float s_float[4 * 4];
147 } min, max;
148
149 uint32_t minCount = 0;
150 uint32_t maxCount = 0;
151
152 bool normalized = false;
153 };
154
156 {
157 Cogs::SamplerState::FilterMode filterMode = Cogs::SamplerState::FilterMode::MinMagMipLinear; // Note: not exposed in cogs materialInstance, i.e. not set
161 bool mipmapped = true;
162 };
163
165 {
166 enum {
167 Unset,
168 LoadFromUri,
169 LoadFromMemory
170 } kind = Unset;
171
172 std::string uri;
174 struct {
175 uint32_t bufferViewIndex =0;
176 std::string mimeType;
177 } from_memory_data;
178
179 struct {
180 Cogs::Core::TaskId task = Cogs::Core::NoTask;
181 stbi_uc* data = nullptr;
182 int width = 0;
183 int height = 0;
184 int comp = 0;
185 bool failed = false;
186 } decoded;
187
188 struct {
190 std::unique_ptr<Cogs::Core::TextureManager::ResourceProxy> proxy;
191 } cache[4];
192 };
193
195 {
196 int32_t sampler = -1;
197 int32_t source = -1;
198 };
199
200 struct GltfNode
201 {
202 std::string name;
203 std::vector<uint32_t> children;
204
205 glm::quat rot = glm::quat();
206 glm::vec3 pos = glm::vec3(0);
207 glm::vec3 scale = glm::vec3(1);
208
209 glm::mat4 mat;
210 uint32_t mesh = (uint32_t) -1;
211 uint32_t skin = (uint32_t) -1;
212
213 uint32_t node_index = (uint32_t) -1;
214 uint32_t parent_node = (uint32_t) -1;
215
216 uint32_t part_index = (uint32_t) -1;
217 uint32_t bone_index = (uint32_t) -1;
218 };
219
221 {
222 std::string name;
223 std::vector<uint32_t> nodes;
224 };
225
226 struct GltfSkin
227 {
228 uint32_t inverseBindMatrices = 0;
229 std::vector<uint32_t> joints;
230 uint32_t skeleton = 0;
231 std::string name; // TODO
232 };
233
235 {
236 uint32_t input = 0; // Time accessor
237 std::string interpolation;
238 uint32_t output = 0; // Ineterpolant accessor
239 };
240
242 {
243 uint32_t sampler = 0;
244 uint32_t node = 0;
245 std::string path;
246 };
247
249 {
250 std::string name;
251 std::vector<GltfSampler> samplers;
252 std::vector<GltfChannel> channels;
253 };
254
256 {
257 int32_t modelMaterialIx = -1;
258
259 uint32_t texCoordSets = 0; // max referenced texcoord set + 1
260 bool needsTangents = false;
261 bool needsNormals = false;
262 };
263
265 {
266 uint32_t meshIx = 0;
267 uint32_t bboxIx = 0;
268 uint32_t vertexCount = (uint32_t) -1;
269 };
270
272 CachedModelMesh mesh;
273 uint32_t materialIx;
274 };
275
277 glm::vec2 offset = glm::vec2(0, 0);
278 glm::vec2 scale = glm::vec2(1.0, 1.0);
279 float rotation = 0.0;
280 };
281
283 {
286
287 Context* context = nullptr;
288 Cogs::StringView path;
289 TaskId rootTask = NoTask;
290 Cogs::Timer timer;
291
292 int32_t version = -1;
293 int32_t min_version = -1;
294 int32_t load_scene = -1; // Display at load time
295
296 unsigned optimizationLevel = 1; // managed via variables
297
298 std::map<std::string, uint32_t> debugMessages;
299
300 std::vector<ResourceBuffer> buffer_data_store;
301 std::vector<std::span<const uint8_t>> buffer_data;
302 std::vector<GltfBufferView> buffer_views;
303 std::vector<GltfAccessor> accessors;
304
305 std::vector<GltfImage> images;
306 std::vector<GltfTextureSampler> texture_samplers;
307 std::vector<GltfTexture> textures;
308
309 std::vector<GltfScene> scenes;
310 std::vector<GltfNode> nodes;
311
312 std::vector<GltfSkin> skins;
313 std::vector<GltfAnimation> animations;
314
315 std::vector<Object> materialsArray;
316 std::vector<Object> meshesArray;
317 std::vector<TextureTransform> textureTransforms;
318
319 std::vector<CachedModelMaterial> modelMaterialsCache;
320 std::unordered_map<size_t, CachedModelMesh> modelMeshCache;
321
322 int32_t default_material = -1;
323 std::vector<int32_t> materials_skinned;
324
325 std::vector<Cogs::Memory::MemoryBuffer> normalsScratch;
326 std::vector<Cogs::Memory::MemoryBuffer> tangentsScratch;
327 std::vector<Cogs::Memory::MemoryBuffer> positionsScratch;
328 std::vector<Cogs::Memory::MemoryBuffer> texCoordsScratch;
329 };
330
332 {
333 const uint8_t* data = nullptr;
334 uint32_t stride = 0;
335 uint32_t count = 0;
336 };
337
338 static_assert(sizeof(BufferDataView) == (sizeof(BufferDataView::data) +
339 sizeof(BufferDataView::stride) +
340 sizeof(BufferDataView::count)), "Struct is not tightly packed");
341
343 {
344 BufferDataView dataView;
345 size_t formatSize = 0;
346 size_t offset = 0;
347 Cogs::DataFormat format = Cogs::DataFormat::Unknown;
349 uint32_t semanticIndex = 0;
350 };
351
352 static_assert(sizeof(VertexStream) == (sizeof(VertexStream::dataView) +
353 sizeof(VertexStream::formatSize) +
354 sizeof(VertexStream::offset) +
355 sizeof(VertexStream::format) +
356 sizeof(VertexStream::semantic) +
357 sizeof(VertexStream::semanticIndex)), "Struct is not tightly packed");
358
359 constexpr size_t hash(const VertexStream& stream, size_t hashValue = Cogs::hash()) {
360 return Cogs::hash(&stream, sizeof(stream), hashValue);
361 }
362
364 {
365 Cogs::Geometry::BoundingBox bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>();
366 BufferDataView positionData;
367 BufferDataView normalData;
368 BufferDataView texcoordData;
369 BufferDataView indexData;
370 AccessorComponentType indexType = AccessorComponentType::None;
371 bool positionPresent = false;
372 bool normalPresent = false;
373 bool tangentPresent = false;
374 uint32_t indexSize = 0;
375 uint32_t texcoordStreamCount = 0;
376 uint32_t colorStreamCount = 0;
377 uint32_t jointsStreamCount = 0;
378 uint32_t weightsStreamCount = 0;
379 uint32_t positionCount = 0;
380 };
381
382 // Converts a non-float-vec(n) bufferview (ie. byte or short) to a regular float-vec(n) bufferview.
383 // T is the GLM vector type for the target, ie. glm::vec2, glm::vec3 or glm::vec4
384 template<typename T>
385 BufferDataView convertVecBufferToFloats(const BufferDataView& srcBuf, AccessorComponentType srcType, Memory::MemoryBuffer* workspace, bool normalize=false)
386 {
387 assert(workspace->size() == 0 && "Cannot reuse MemoryBuffer");
388 assert(srcType != AccessorComponentType::Float && "Buffer is already of type float!");
389
390 size_t stride = sizeof(T);
391 workspace->resize(stride * srcBuf.count);
392
393 T* buf = static_cast<T*>(workspace->data());
394 const uint8_t* src = srcBuf.data;
395
396 size_t c = 0;
397 for (uint32_t i = 0; i < srcBuf.count; ++i) {
398 T v;
399 constexpr int n = v.length();
400 for (int j = 0; j < n; ++j) {
401 const uint8_t* ptr = src + c;
402 switch (srcType) {
403 case AccessorComponentType::Byte:
404 v[j] = float(normalize ? std::fmax(float(reinterpret_cast<const int8_t*>(ptr)[j]) / 127.0, -1.0) : float(reinterpret_cast<const int8_t*>(ptr)[j]));
405 break;
406 case AccessorComponentType::UnsignedByte:
407 v[j] = float(normalize ? (float(reinterpret_cast<const uint8_t*>(ptr)[j])) / 255.0f : float(reinterpret_cast<const int8_t*>(ptr)[j]));
408 break;
409 case AccessorComponentType::Short:
410 v[j] = float(normalize ? std::fmax(float(reinterpret_cast<const int16_t*>(ptr)[j]) / 32767.0f, -1.0) : float(reinterpret_cast<const int16_t*>(ptr)[j]));
411 break;
412 case AccessorComponentType::UnsignedShort:
413 v[j] = float(normalize ? float(reinterpret_cast<const uint16_t*>(ptr)[j]) / 65535.0f : float(reinterpret_cast<const uint16_t*>(ptr)[j]));
414 break;
415 default:
416 assert(false && "Unhandled component type");
417 }
418 }
419
420 buf[i] = v;
421 c += srcBuf.stride;
422 }
423
424 BufferDataView target{
425 .data = reinterpret_cast<uint8_t*>(buf),
426 .stride = (uint32_t) stride,
427 .count = srcBuf.count
428 };
429
430 return target;
431 }
432
434 {
435 public:
436 bool canLoad(Context*, const ModelLoadInfo& loadInfo);
437 size_t getPotentialB3DMOffset(uint8_t* content, size_t size);
438 bool load(Context* context, const ModelLoadInfo& loadInfo, std::unique_ptr<Cogs::FileContents> contents);
439 bool load(Context* context, const ModelLoadInfo& loadInfo);
440 };
441}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
size_t getPotentialB3DMOffset(uint8_t *content, size_t size)
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
Old timer class.
Definition: Timer.h:37
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
ElementSemantic
Element semantics used to map data to the shader stage.
Definition: VertexFormat.h:14
@ Position
Position semantic.
Task id struct used to identify unique Task instances.
Definition: TaskManager.h:20
AddressMode
Addressing modes to use when sampling textures.
Definition: SamplerState.h:15
@ Wrap
Texture coordinates automatically wrap around to [0, 1] range.
Definition: SamplerState.h:19
FilterMode
Filter modes to specify how texture data is treated when sampled.
Definition: SamplerState.h:31
@ MinMagMipLinear
Linear sampling for both minification and magnification.
Definition: SamplerState.h:35