1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "gltf/gltf2_importer.h"
17
18 #include <chrono>
19 #include <cstring>
20 #include <functional>
21
22 #include <3d/ecs/components/animation_component.h>
23 #include <3d/ecs/components/animation_input_component.h>
24 #include <3d/ecs/components/animation_output_component.h>
25 #include <3d/ecs/components/animation_track_component.h>
26 #include <3d/ecs/components/camera_component.h>
27 #include <3d/ecs/components/environment_component.h>
28 #include <3d/ecs/components/light_component.h>
29 #include <3d/ecs/components/local_matrix_component.h>
30 #include <3d/ecs/components/material_component.h>
31 #include <3d/ecs/components/morph_component.h>
32 #include <3d/ecs/components/name_component.h>
33 #include <3d/ecs/components/node_component.h>
34 #include <3d/ecs/components/render_configuration_component.h>
35 #include <3d/ecs/components/render_handle_component.h>
36 #include <3d/ecs/components/render_mesh_component.h>
37 #include <3d/ecs/components/rsdz_model_id_component.h>
38 #include <3d/ecs/components/skin_ibm_component.h>
39 #include <3d/ecs/components/skin_joints_component.h>
40 #include <3d/ecs/components/transform_component.h>
41 #include <3d/ecs/components/uri_component.h>
42 #include <3d/ecs/components/world_matrix_component.h>
43 #include <3d/ecs/systems/intf_node_system.h>
44 #include <3d/ecs/systems/intf_skinning_system.h>
45 #include <3d/implementation_uids.h>
46 #include <3d/intf_graphics_context.h>
47 #include <3d/render/default_material_constants.h>
48 #include <3d/util/intf_mesh_builder.h>
49 #include <base/containers/fixed_string.h>
50 #include <base/containers/string.h>
51 #include <base/containers/unique_ptr.h>
52 #include <base/containers/unordered_map.h>
53 #include <base/containers/vector.h>
54 #include <base/math/matrix_util.h>
55 #include <base/math/vector_util.h>
56 #include <core/ecs/intf_ecs.h>
57 #include <core/ecs/intf_entity_manager.h>
58 #include <core/image/intf_image_container.h>
59 #include <core/image/intf_image_loader_manager.h>
60 #include <core/implementation_uids.h>
61 #include <core/intf_engine.h>
62 #include <core/log.h>
63 #include <core/namespace.h>
64 #include <core/perf/cpu_perf_scope.h>
65 #include <core/perf/intf_performance_data_manager.h>
66 #include <core/plugin/intf_class_register.h>
67 #include <core/property/intf_property_handle.h>
68 #include <core/property/property_types.h>
69 #include <render/datastore/intf_render_data_store_default_staging.h>
70 #include <render/datastore/intf_render_data_store_manager.h>
71 #include <render/device/intf_gpu_resource_manager.h>
72 #include <render/device/intf_shader_manager.h>
73 #include <render/implementation_uids.h>
74 #include <render/intf_render_context.h>
75
76 #include "gltf/gltf2_util.h"
77 #include "util/log.h"
78 #include "util/mesh_builder.h"
79 #include "util/mesh_util.h"
80 #include "util/string_util.h"
81 #include "util/uri_lookup.h"
82
83 CORE3D_BEGIN_NAMESPACE()
84 using namespace BASE_NS;
85 using namespace CORE_NS;
86 using namespace RENDER_NS;
87
88 namespace {
89 // How many threads the GLTF2Importer will use to run tasks.
90 constexpr const uint32_t IMPORTER_THREADS = 2u;
91
92 // Helper class for running lambda as a ThreadPool task.
93 template<typename Fn>
94 class FunctionTask final : public IThreadPool::ITask {
95 public:
FunctionTask(Fn && func)96 explicit FunctionTask(Fn&& func) : func_(BASE_NS::move(func)) {};
97
operator ()()98 void operator()() override
99 {
100 func_();
101 }
102
103 protected:
Destroy()104 void Destroy() override
105 {
106 delete this;
107 }
108
109 private:
110 Fn func_;
111 };
112
113 template<typename Fn>
CreateFunctionTask(Fn && func)114 inline IThreadPool::ITask::Ptr CreateFunctionTask(Fn&& func)
115 {
116 return IThreadPool::ITask::Ptr { new FunctionTask<Fn>(BASE_NS::move(func)) };
117 }
118
119 template<class T>
FindIndex(const vector<unique_ptr<T>> & container,T const * item)120 size_t FindIndex(const vector<unique_ptr<T>>& container, T const* item)
121 {
122 for (size_t i = 0; i < container.size(); ++i) {
123 if (container[i].get() == item) {
124 return i;
125 }
126 }
127 return GLTF2::GLTF_INVALID_INDEX;
128 }
129
130 constexpr auto MAX_COMPONENTS = 4U;
131 constexpr Format BYTE_FORMATS[2U][MAX_COMPONENTS] = {
132 { BASE_FORMAT_R8_SINT, BASE_FORMAT_R8G8_SINT, BASE_FORMAT_R8G8B8_SINT, BASE_FORMAT_R8G8B8A8_SINT },
133 { BASE_FORMAT_R8_SNORM, BASE_FORMAT_R8G8_SNORM, BASE_FORMAT_R8G8B8_SNORM, BASE_FORMAT_R8G8B8A8_SNORM },
134 };
135 constexpr Format UNSIGNED_BYTE_FORMATS[2U][MAX_COMPONENTS] = {
136 { BASE_FORMAT_R8_UINT, BASE_FORMAT_R8G8_UINT, BASE_FORMAT_R8G8B8_UINT, BASE_FORMAT_R8G8B8A8_UINT },
137 { BASE_FORMAT_R8_UNORM, BASE_FORMAT_R8G8_UNORM, BASE_FORMAT_R8G8B8_UNORM, BASE_FORMAT_R8G8B8A8_UNORM },
138 };
139
140 constexpr Format SHORT_FORMATS[2U][MAX_COMPONENTS] = {
141 { BASE_FORMAT_R16_SINT, BASE_FORMAT_R16G16_SINT, BASE_FORMAT_R16G16B16_SINT, BASE_FORMAT_R16G16B16A16_SINT },
142 { BASE_FORMAT_R16_SNORM, BASE_FORMAT_R16G16_SNORM, BASE_FORMAT_R16G16B16_SNORM, BASE_FORMAT_R16G16B16A16_SNORM },
143 };
144 constexpr Format UNSIGNED_SHORT_FORMATS[2U][MAX_COMPONENTS] = {
145 { BASE_FORMAT_R16_UINT, BASE_FORMAT_R16G16_UINT, BASE_FORMAT_R16G16B16_UINT, BASE_FORMAT_R16G16B16A16_UINT },
146 { BASE_FORMAT_R16_UNORM, BASE_FORMAT_R16G16_UNORM, BASE_FORMAT_R16G16B16_UNORM, BASE_FORMAT_R16G16B16A16_UNORM },
147 };
148
149 constexpr Format INT_FORMATS[MAX_COMPONENTS] = { BASE_FORMAT_R32_SINT, BASE_FORMAT_R32G32_SINT,
150 BASE_FORMAT_R32G32B32_SINT, BASE_FORMAT_R32G32B32A32_SINT };
151 constexpr Format UNSIGNED_INT_FORMATS[MAX_COMPONENTS] = { BASE_FORMAT_R32_UINT, BASE_FORMAT_R32G32_UINT,
152 BASE_FORMAT_R32G32B32_UINT, BASE_FORMAT_R32G32B32A32_UINT };
153
154 constexpr Format FLOAT_FORMATS[MAX_COMPONENTS] = { BASE_FORMAT_R32_SFLOAT, BASE_FORMAT_R32G32_SFLOAT,
155 BASE_FORMAT_R32G32B32_SFLOAT, BASE_FORMAT_R32G32B32A32_SFLOAT };
156
Convert(GLTF2::ComponentType componentType,size_t componentCount,bool normalized)157 constexpr Format Convert(GLTF2::ComponentType componentType, size_t componentCount, bool normalized)
158 {
159 if (componentCount <= MAX_COMPONENTS) {
160 switch (componentType) {
161 case GLTF2::ComponentType::INVALID:
162 break;
163
164 case GLTF2::ComponentType::BYTE:
165 return BYTE_FORMATS[normalized ? 1U : 0U][componentCount - 1];
166
167 case GLTF2::ComponentType::UNSIGNED_BYTE:
168 return UNSIGNED_BYTE_FORMATS[normalized ? 1U : 0U][componentCount - 1];
169
170 case GLTF2::ComponentType::SHORT:
171 return SHORT_FORMATS[normalized ? 1U : 0U][componentCount - 1];
172
173 case GLTF2::ComponentType::UNSIGNED_SHORT:
174 return UNSIGNED_SHORT_FORMATS[normalized ? 1U : 0U][componentCount - 1];
175
176 case GLTF2::ComponentType::INT:
177 return INT_FORMATS[componentCount - 1];
178
179 case GLTF2::ComponentType::UNSIGNED_INT:
180 return UNSIGNED_INT_FORMATS[componentCount - 1];
181
182 case GLTF2::ComponentType::FLOAT:
183 return FLOAT_FORMATS[componentCount - 1];
184 }
185 }
186 return BASE_FORMAT_UNDEFINED;
187 }
188
ConvertGltf2InputAssembly(const GLTF2::RenderMode renderMode)189 constexpr GraphicsState::InputAssembly ConvertGltf2InputAssembly(const GLTF2::RenderMode renderMode)
190 {
191 GraphicsState::InputAssembly ia;
192 switch (renderMode) {
193 case GLTF2::RenderMode::POINTS:
194 ia.primitiveTopology = PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_POINT_LIST;
195 break;
196 case GLTF2::RenderMode::LINES:
197 ia.primitiveTopology = PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_LINE_LIST;
198 break;
199 case GLTF2::RenderMode::LINE_STRIP:
200 ia.primitiveTopology = PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_LINE_STRIP;
201 break;
202 case GLTF2::RenderMode::TRIANGLES:
203 ia.primitiveTopology = PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
204 break;
205 case GLTF2::RenderMode::TRIANGLE_STRIP:
206 ia.primitiveTopology = PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
207 break;
208 case GLTF2::RenderMode::TRIANGLE_FAN:
209 ia.primitiveTopology = PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
210 break;
211 default:
212 // PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY
213 // would need to be handled with line_strip with some additions
214 ia.primitiveTopology = PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_MAX_ENUM;
215 break;
216 }
217 return ia;
218 }
219
ConvertNormalizedDataToFloat(GLTF2::GLTFLoadDataResult const & result,float * destination,size_t dstComponentCount=0,float paddingValue=0.0f,float scale=0.f)220 void ConvertNormalizedDataToFloat(GLTF2::GLTFLoadDataResult const& result, float* destination,
221 size_t dstComponentCount = 0, float paddingValue = 0.0f, float scale = 0.f)
222 {
223 uint8_t const* source = reinterpret_cast<const uint8_t*>(result.data.data());
224
225 if (dstComponentCount == 0) {
226 // By default, use the source component count.
227 dstComponentCount = result.componentCount;
228 }
229
230 CORE_ASSERT_MSG(dstComponentCount >= result.componentCount,
231 "Padding count cannot be negative. Make sure expected component count is equal or greater than source "
232 "component count.");
233
234 // Amount of padding.
235 const size_t paddingCount = dstComponentCount - result.componentCount;
236
237 for (size_t i = 0; i < result.elementCount; ++i) {
238 for (size_t j = 0; j < result.componentCount; ++j) {
239 switch (result.componentType) {
240 case GLTF2::ComponentType::BYTE:
241 *destination = std::max((reinterpret_cast<const int8_t*>(source))[j] / 127.0f, -1.0f);
242 break;
243
244 case GLTF2::ComponentType::UNSIGNED_BYTE:
245 *destination = source[j] / 255.0f;
246 break;
247
248 case GLTF2::ComponentType::SHORT:
249 *destination = std::max(
250 (reinterpret_cast<const int16_t*>(source))[j] / ((scale != 0.f) ? scale : 32767.0f), -1.0f);
251 break;
252
253 case GLTF2::ComponentType::UNSIGNED_SHORT:
254 *destination = (reinterpret_cast<const uint16_t*>(source))[j] / 65535.0f;
255 break;
256
257 case GLTF2::ComponentType::FLOAT: {
258 *destination = (reinterpret_cast<const float*>(reinterpret_cast<uintptr_t>(source)))[j];
259 break;
260 }
261
262 default:
263 case GLTF2::ComponentType::UNSIGNED_INT:
264 case GLTF2::ComponentType::INT:
265 CORE_ASSERT(false);
266 *destination = 0.0f;
267 break;
268 }
269
270 destination++;
271 }
272
273 // Apply padding.
274 for (size_t padding = 0; padding < paddingCount; ++padding) {
275 *destination = paddingValue;
276 destination++;
277 }
278
279 source += result.elementSize;
280 }
281 }
282
ConvertDataToFloat(GLTF2::GLTFLoadDataResult const & result,float * destination,size_t dstComponentCount=0,float paddingValue=0.0f,float scale=0.f)283 void ConvertDataToFloat(GLTF2::GLTFLoadDataResult const& result, float* destination, size_t dstComponentCount = 0,
284 float paddingValue = 0.0f, float scale = 0.f)
285 {
286 uint8_t const* source = reinterpret_cast<const uint8_t*>(result.data.data());
287
288 if (dstComponentCount == 0) {
289 // By default, use the source component count.
290 dstComponentCount = result.componentCount;
291 }
292
293 CORE_ASSERT_MSG(dstComponentCount >= result.componentCount,
294 "Padding count cannot be negative. Make sure expected component count is equal or greater than source "
295 "component count.");
296
297 // Amount of padding.
298 const size_t paddingCount = dstComponentCount - result.componentCount;
299
300 for (size_t i = 0; i < result.elementCount; ++i) {
301 for (size_t j = 0; j < result.componentCount; ++j) {
302 switch (result.componentType) {
303 case GLTF2::ComponentType::BYTE:
304 *destination = reinterpret_cast<const int8_t*>(source)[j];
305 break;
306
307 case GLTF2::ComponentType::UNSIGNED_BYTE:
308 *destination = source[j];
309 break;
310
311 case GLTF2::ComponentType::SHORT:
312 *destination = reinterpret_cast<const int16_t*>(source)[j];
313 break;
314
315 case GLTF2::ComponentType::UNSIGNED_SHORT:
316 *destination = reinterpret_cast<const uint16_t*>(source)[j];
317 break;
318
319 case GLTF2::ComponentType::FLOAT: {
320 *destination = (reinterpret_cast<const float*>(reinterpret_cast<uintptr_t>(source)))[j];
321 break;
322 }
323
324 default:
325 case GLTF2::ComponentType::UNSIGNED_INT:
326 case GLTF2::ComponentType::INT:
327 CORE_ASSERT(false);
328 *destination = 0.0f;
329 break;
330 }
331
332 destination++;
333 }
334
335 // Apply padding.
336 for (size_t padding = 0; padding < paddingCount; ++padding) {
337 *destination = paddingValue;
338 destination++;
339 }
340
341 source += result.elementSize;
342 }
343 }
344
ConvertDataToBool(GLTF2::GLTFLoadDataResult const & result,bool * destination)345 void ConvertDataToBool(GLTF2::GLTFLoadDataResult const& result, bool* destination)
346 {
347 uint8_t const* source = reinterpret_cast<const uint8_t*>(result.data.data());
348
349 for (size_t i = 0; i < result.elementCount; ++i) {
350 for (size_t j = 0; j < result.componentCount; ++j) {
351 switch (result.componentType) {
352 case GLTF2::ComponentType::BYTE:
353 *destination = reinterpret_cast<const int8_t*>(source)[j] != 0;
354 break;
355
356 case GLTF2::ComponentType::UNSIGNED_BYTE:
357 *destination = source[j] != 0u;
358 break;
359
360 case GLTF2::ComponentType::SHORT:
361 *destination = reinterpret_cast<const int16_t*>(source)[j] != 0;
362 break;
363
364 case GLTF2::ComponentType::UNSIGNED_SHORT:
365 *destination = reinterpret_cast<const uint16_t*>(source)[j] != 0u;
366 break;
367
368 case GLTF2::ComponentType::FLOAT: {
369 *destination = reinterpret_cast<const float*>(reinterpret_cast<uintptr_t>(source))[j] != 0.f;
370 break;
371 }
372
373 default:
374 case GLTF2::ComponentType::UNSIGNED_INT:
375 case GLTF2::ComponentType::INT:
376 CORE_ASSERT(false);
377 *destination = false;
378 break;
379 }
380
381 destination++;
382 }
383
384 source += result.elementSize;
385 }
386 }
387
GetImportedTextureHandle(const GLTFImportResult & importResult,uint32_t index)388 EntityReference GetImportedTextureHandle(const GLTFImportResult& importResult, uint32_t index)
389 {
390 if (index != GLTF2::GLTF_INVALID_INDEX && index < importResult.data.textures.size()) {
391 return importResult.data.textures[index];
392 }
393
394 return EntityReference();
395 }
396
GetImageExtension(GLTF2::MimeType type)397 string_view GetImageExtension(GLTF2::MimeType type)
398 {
399 switch (type) {
400 case GLTF2::MimeType::JPEG:
401 return "jpg";
402 case GLTF2::MimeType::PNG:
403 return "png";
404 case GLTF2::MimeType::KTX:
405 return "ktx";
406 case GLTF2::MimeType::DDS:
407 return "dds";
408
409 case GLTF2::MimeType::INVALID:
410 default:
411 return "";
412 }
413 }
414
ConvertToCoreLightType(GLTF2::LightType lightType)415 LightComponent::Type ConvertToCoreLightType(GLTF2::LightType lightType)
416 {
417 switch (lightType) {
418 case GLTF2::LightType::DIRECTIONAL:
419 return LightComponent::Type::DIRECTIONAL;
420
421 case GLTF2::LightType::POINT:
422 return LightComponent::Type::POINT;
423
424 case GLTF2::LightType::SPOT:
425 return LightComponent::Type::SPOT;
426
427 default:
428 case GLTF2::LightType::INVALID:
429 case GLTF2::LightType::AMBIENT:
430 return LightComponent::Type::DIRECTIONAL;
431 }
432 }
433
434 struct GatherMeshDataResult {
435 GatherMeshDataResult() = default;
436 ~GatherMeshDataResult() = default;
437 GatherMeshDataResult(const GatherMeshDataResult& aOther) = delete;
GatherMeshDataResult__anon621de54f0111::GatherMeshDataResult438 explicit GatherMeshDataResult(const string& error) : success(false), error(error) {}
GatherMeshDataResult__anon621de54f0111::GatherMeshDataResult439 GatherMeshDataResult(GatherMeshDataResult&& other) noexcept
440 : success(other.success), error(move(other.error)), meshBuilder(move(other.meshBuilder))
441 {}
442
operator =__anon621de54f0111::GatherMeshDataResult443 GatherMeshDataResult& operator=(GatherMeshDataResult&& other) noexcept
444 {
445 success = other.success;
446 error = move(other.error);
447 meshBuilder = move(other.meshBuilder);
448 return *this;
449 }
450
451 /** Indicates, whether the load operation is successful. */
452 bool success { true };
453
454 /** In case of import error, contains the description of the error. */
455 string error;
456
457 BASE_NS::refcnt_ptr<MeshBuilder> meshBuilder;
458 };
459
ConvertLoadResultToFloat(GLTF2::GLTFLoadDataResult & data,float scale=0.f)460 void ConvertLoadResultToFloat(GLTF2::GLTFLoadDataResult& data, float scale = 0.f)
461 {
462 vector<uint8_t> converted;
463 auto const componentCount = data.elementCount * data.componentCount;
464 converted.resize(componentCount * sizeof(float));
465 if (data.normalized) {
466 ConvertNormalizedDataToFloat(data, reinterpret_cast<float*>(converted.data()), 0u, 0.f, scale);
467 } else {
468 ConvertDataToFloat(data, reinterpret_cast<float*>(converted.data()), 0u, 0.f, scale);
469 }
470
471 data.componentType = GLTF2::ComponentType::FLOAT;
472 data.componentByteSize = sizeof(float);
473 data.elementSize = data.componentByteSize * data.componentCount;
474 data.data = move(converted);
475 }
476
477 template<typename T>
Validate(GLTF2::GLTFLoadDataResult & indices,uint32_t vertexCount,bool & primitiveRestart)478 void Validate(GLTF2::GLTFLoadDataResult& indices, uint32_t vertexCount, bool& primitiveRestart)
479 {
480 const auto elementCount = Math::min(indices.elementCount, indices.data.size_in_bytes() / sizeof(T));
481 auto source = array_view(static_cast<const T*>(static_cast<const void*>(indices.data.data())), elementCount);
482 primitiveRestart = false;
483 if (std::any_of(source.begin(), source.end(), [vertexCount, &primitiveRestart](const auto& value) {
484 // spec prohibits "maximum possible value for component type", but still some models use it for primitive
485 // restart.
486 if (value == std::numeric_limits<T>::max()) {
487 primitiveRestart = true;
488 return false;
489 }
490 return (value >= vertexCount);
491 })) {
492 indices.success = false;
493 indices.error += "Indices out-of-range.\n";
494 }
495 }
496
ValidateIndices(GLTF2::GLTFLoadDataResult & indices,uint32_t vertexCount,bool & primitiveRestart)497 void ValidateIndices(GLTF2::GLTFLoadDataResult& indices, uint32_t vertexCount, bool& primitiveRestart)
498 {
499 switch (indices.componentType) {
500 case GLTF2::ComponentType::UNSIGNED_BYTE: {
501 Validate<uint8_t>(indices, vertexCount, primitiveRestart);
502 break;
503 }
504 case GLTF2::ComponentType::UNSIGNED_SHORT: {
505 Validate<uint16_t>(indices, vertexCount, primitiveRestart);
506 break;
507 }
508 case GLTF2::ComponentType::UNSIGNED_INT: {
509 Validate<uint32_t>(indices, vertexCount, primitiveRestart);
510 break;
511 }
512
513 default:
514 case GLTF2::ComponentType::BYTE:
515 case GLTF2::ComponentType::SHORT:
516 case GLTF2::ComponentType::FLOAT:
517 case GLTF2::ComponentType::INT:
518 indices.success = false;
519 indices.error += "Invalid componentType for indices.\n";
520 CORE_ASSERT(false);
521 break;
522 }
523 }
524
LoadPrimitiveAttributeData(const GLTF2::MeshPrimitive & primitive,GLTF2::GLTFLoadDataResult & positions,GLTF2::GLTFLoadDataResult & normals,array_view<GLTF2::GLTFLoadDataResult> texcoords,GLTF2::GLTFLoadDataResult & tangents,GLTF2::GLTFLoadDataResult & joints,GLTF2::GLTFLoadDataResult & weights,GLTF2::GLTFLoadDataResult & colors,const uint32_t & flags)525 bool LoadPrimitiveAttributeData(const GLTF2::MeshPrimitive& primitive, GLTF2::GLTFLoadDataResult& positions,
526 GLTF2::GLTFLoadDataResult& normals, array_view<GLTF2::GLTFLoadDataResult> texcoords,
527 GLTF2::GLTFLoadDataResult& tangents, GLTF2::GLTFLoadDataResult& joints, GLTF2::GLTFLoadDataResult& weights,
528 GLTF2::GLTFLoadDataResult& colors, const uint32_t& flags)
529 {
530 bool success = true;
531 for (const auto& attribute : primitive.attributes) {
532 if ((attribute.attribute.type != GLTF2::AttributeType::TEXCOORD && attribute.attribute.index > 0) ||
533 (attribute.attribute.type == GLTF2::AttributeType::TEXCOORD &&
534 attribute.attribute.index >= texcoords.size())) {
535 continue;
536 }
537
538 GLTF2::GLTFLoadDataResult loadDataResult = GLTF2::LoadData(*attribute.accessor);
539 success = success && loadDataResult.success;
540 switch (attribute.attribute.type) {
541 case GLTF2::AttributeType::POSITION:
542 positions = move(loadDataResult);
543 break;
544
545 case GLTF2::AttributeType::NORMAL:
546 normals = move(loadDataResult);
547 break;
548
549 case GLTF2::AttributeType::TEXCOORD:
550 texcoords[attribute.attribute.index] = move(loadDataResult);
551 break;
552
553 case GLTF2::AttributeType::TANGENT:
554 tangents = move(loadDataResult);
555 break;
556
557 case GLTF2::AttributeType::JOINTS:
558 if (flags & CORE_GLTF_IMPORT_RESOURCE_SKIN) {
559 joints = move(loadDataResult);
560 }
561 break;
562
563 case GLTF2::AttributeType::WEIGHTS:
564 if (flags & CORE_GLTF_IMPORT_RESOURCE_SKIN) {
565 weights = move(loadDataResult);
566 }
567 break;
568
569 case GLTF2::AttributeType::COLOR:
570 colors = move(loadDataResult);
571 break;
572
573 case GLTF2::AttributeType::INVALID:
574 default:
575 break;
576 }
577 }
578 return success;
579 }
580
ProcessMorphTargetData(const IMeshBuilder::Submesh & importInfo,size_t targets,GLTF2::GLTFLoadDataResult & loadDataResult,GLTF2::GLTFLoadDataResult & finalDataResult)581 void ProcessMorphTargetData(const IMeshBuilder::Submesh& importInfo, size_t targets,
582 GLTF2::GLTFLoadDataResult& loadDataResult, GLTF2::GLTFLoadDataResult& finalDataResult)
583 {
584 #if !defined(GLTF2_EXTENSION_KHR_MESH_QUANTIZATION)
585 // Spec says POSITION,NORMAL and TANGENT must be FLOAT & VEC3
586 // NOTE: ASSERT for now, if the types don't match, they need to be converted. (or we should fail
587 // since out-of-spec)
588 CORE_ASSERT(loadDataResult.componentType == GLTF2::ComponentType::FLOAT);
589 CORE_ASSERT(loadDataResult.componentCount == 3U);
590 CORE_ASSERT(loadDataResult.elementCount == importInfo.vertexCount);
591 #endif
592 if (finalDataResult.componentCount > 0U) {
593 finalDataResult.data.append(loadDataResult.data.begin(), loadDataResult.data.end());
594 for (size_t i = 0; i < finalDataResult.min.size(); i++) {
595 finalDataResult.min[i] = std::min(finalDataResult.min[i], loadDataResult.min[i]);
596 }
597 for (size_t i = 0; i < finalDataResult.max.size(); i++) {
598 finalDataResult.max[i] = std::max(finalDataResult.max[i], loadDataResult.max[i]);
599 }
600 } else {
601 finalDataResult = move(loadDataResult);
602 finalDataResult.data.reserve(finalDataResult.data.size() * targets);
603 }
604 }
605
GenerateMorphTargets(const GLTF2::MeshPrimitive & primitive,const IMeshBuilder::Submesh & importInfo,GLTF2::GLTFLoadDataResult & targetPositions,GLTF2::GLTFLoadDataResult & targetNormals,GLTF2::GLTFLoadDataResult & targetTangents)606 void GenerateMorphTargets(const GLTF2::MeshPrimitive& primitive, const IMeshBuilder::Submesh& importInfo,
607 GLTF2::GLTFLoadDataResult& targetPositions, GLTF2::GLTFLoadDataResult& targetNormals,
608 GLTF2::GLTFLoadDataResult& targetTangents)
609 {
610 // All targets collected to single buffer.
611 for (const auto& target : primitive.targets) {
612 for (const auto& targetAttribute : target.target) {
613 GLTF2::GLTFLoadDataResult loadDataResult = GLTF2::LoadData(*targetAttribute.accessor);
614 if (loadDataResult.success) {
615 switch (targetAttribute.attribute.type) {
616 case GLTF2::AttributeType::POSITION:
617 #if defined(GLTF2_EXTENSION_IGFX_COMPRESSED)
618 // This is for the IGFX_compressed extension. Morph target offsets were multiplied by 10000(!)
619 // and cast to int16.
620 if (target.iGfxCompressed && loadDataResult.componentType == GLTF2::ComponentType::SHORT) {
621 loadDataResult.normalized = true;
622 ConvertLoadResultToFloat(loadDataResult, 10000.f);
623 }
624 #endif
625 ProcessMorphTargetData(importInfo, primitive.targets.size(), loadDataResult, targetPositions);
626 break;
627
628 case GLTF2::AttributeType::NORMAL:
629 ProcessMorphTargetData(importInfo, primitive.targets.size(), loadDataResult, targetNormals);
630 break;
631
632 case GLTF2::AttributeType::TANGENT:
633 ProcessMorphTargetData(importInfo, primitive.targets.size(), loadDataResult, targetTangents);
634 break;
635
636 case GLTF2::AttributeType::TEXCOORD:
637 case GLTF2::AttributeType::JOINTS:
638 case GLTF2::AttributeType::WEIGHTS:
639 case GLTF2::AttributeType::COLOR:
640 case GLTF2::AttributeType::INVALID:
641 default:
642 // NOTE: Technically there could be custom attributes, but those are not supported at all
643 // currently!
644 break;
645 }
646 }
647 }
648 }
649 }
650
GetPrimitiveIndexType(const GLTF2::MeshPrimitive & primitive)651 IndexType GetPrimitiveIndexType(const GLTF2::MeshPrimitive& primitive)
652 {
653 switch (primitive.indices->componentType) {
654 case GLTF2::ComponentType::UNSIGNED_BYTE:
655 return CORE_INDEX_TYPE_UINT16;
656
657 case GLTF2::ComponentType::UNSIGNED_SHORT:
658 return CORE_INDEX_TYPE_UINT16;
659
660 case GLTF2::ComponentType::UNSIGNED_INT:
661 return CORE_INDEX_TYPE_UINT32;
662
663 case GLTF2::ComponentType::INVALID:
664 case GLTF2::ComponentType::BYTE:
665 case GLTF2::ComponentType::SHORT:
666 case GLTF2::ComponentType::INT:
667 case GLTF2::ComponentType::FLOAT:
668 break;
669 default:
670 break;
671 }
672
673 CORE_ASSERT_MSG(false, "Not supported index type.");
674
675 return CORE_INDEX_TYPE_UINT32;
676 }
677
ContainsAttribute(const GLTF2::MeshPrimitive & primitive,GLTF2::AttributeType type)678 bool ContainsAttribute(const GLTF2::MeshPrimitive& primitive, GLTF2::AttributeType type)
679 {
680 return std::any_of(primitive.attributes.begin(), primitive.attributes.end(),
681 [type](const GLTF2::Attribute& attribute) { return attribute.attribute.type == type; });
682 }
683
CreatePrimitiveImportInfo(const GLTFImportResult & importResult,const IMaterialComponentManager & materialManager,const GLTF2::MeshPrimitive & primitive)684 IMeshBuilder::Submesh CreatePrimitiveImportInfo(const GLTFImportResult& importResult,
685 const IMaterialComponentManager& materialManager, const GLTF2::MeshPrimitive& primitive)
686 {
687 IMeshBuilder::Submesh info;
688 bool hasNormalMap = false;
689
690 // Get material, if one assigned.
691 if (primitive.materialIndex != GLTF2::GLTF_INVALID_INDEX &&
692 primitive.materialIndex < importResult.data.materials.size()) {
693 info.material = importResult.data.materials[primitive.materialIndex];
694 hasNormalMap = (primitive.material->normalTexture.textureInfo.index != GLTF2::GLTF_INVALID_INDEX ||
695 primitive.material->clearcoat.normalTexture.textureInfo.index != GLTF2::GLTF_INVALID_INDEX);
696 }
697
698 info.colors = ContainsAttribute(primitive, GLTF2::AttributeType::COLOR);
699 info.joints = ContainsAttribute(primitive, GLTF2::AttributeType::JOINTS);
700 info.tangents = ContainsAttribute(primitive, GLTF2::AttributeType::TANGENT);
701 if (!info.tangents) {
702 // If material has normal map assigned, then always make sure that we have normals.
703 info.tangents = hasNormalMap;
704 }
705
706 if (!info.tangents) {
707 // NOTE. Currenty morph render node always writes tangent data to output buffer.
708 // Therefore we must have tangents defined for this primitive.
709 info.tangents = primitive.targets.size() > 0;
710 }
711 if (const auto pos = std::find_if(primitive.attributes.begin(), primitive.attributes.end(),
712 [](const GLTF2::Attribute& attribute) {
713 return attribute.attribute.type == GLTF2::AttributeType::POSITION;
714 });
715 pos != primitive.attributes.end()) {
716 info.vertexCount = pos->accessor->count;
717 }
718
719 if (primitive.indices) {
720 info.indexCount = primitive.indices->count;
721 info.indexType = GetPrimitiveIndexType(primitive);
722 }
723
724 info.morphTargetCount = (uint32_t)primitive.targets.size();
725
726 info.inputAssembly = ConvertGltf2InputAssembly(primitive.mode);
727
728 return info;
729 }
730
GatherErrorStrings(size_t primitiveIndex,const GLTF2::GLTFLoadDataResult & position,const GLTF2::GLTFLoadDataResult & normal,array_view<const GLTF2::GLTFLoadDataResult> texcoords,const GLTF2::GLTFLoadDataResult & tangent,const GLTF2::GLTFLoadDataResult & color,const GLTF2::GLTFLoadDataResult & joint,const GLTF2::GLTFLoadDataResult & weight)731 string GatherErrorStrings(size_t primitiveIndex, const GLTF2::GLTFLoadDataResult& position,
732 const GLTF2::GLTFLoadDataResult& normal, array_view<const GLTF2::GLTFLoadDataResult> texcoords,
733 const GLTF2::GLTFLoadDataResult& tangent, const GLTF2::GLTFLoadDataResult& color,
734 const GLTF2::GLTFLoadDataResult& joint, const GLTF2::GLTFLoadDataResult& weight)
735 {
736 string error = "Failed to load primitive " + to_string(primitiveIndex) + '\n' + position.error + normal.error;
737 for (const auto& tc : texcoords) {
738 error += tc.error;
739 }
740 error += tangent.error + color.error + joint.error + weight.error + '\n';
741 return error;
742 }
743
744 struct IndicesResult {
745 GLTF2::GLTFLoadDataResult loadDataResult;
746 bool primitiveRestart { false };
747 };
748
LoadIndices(GatherMeshDataResult & result,const GLTF2::MeshPrimitive & primitive,IndexType indexType,uint32_t loadedVertexCount)749 IndicesResult LoadIndices(GatherMeshDataResult& result, const GLTF2::MeshPrimitive& primitive, IndexType indexType,
750 uint32_t loadedVertexCount)
751 {
752 IndicesResult indicesLoadResult;
753 if (primitive.indices) {
754 if (indicesLoadResult.loadDataResult = LoadData(*primitive.indices); indicesLoadResult.loadDataResult.success) {
755 ValidateIndices(indicesLoadResult.loadDataResult, loadedVertexCount, indicesLoadResult.primitiveRestart);
756 }
757 if (!indicesLoadResult.loadDataResult.success) {
758 result.error += indicesLoadResult.loadDataResult.error;
759 result.success = false;
760 indicesLoadResult.loadDataResult = {};
761 }
762 }
763 return indicesLoadResult;
764 }
765
ProcessPrimitives(GatherMeshDataResult & result,uint32_t flags,array_view<const GLTF2::MeshPrimitive> primitives)766 void ProcessPrimitives(GatherMeshDataResult& result, uint32_t flags, array_view<const GLTF2::MeshPrimitive> primitives)
767 {
768 // Feed primitive data for builder.
769 for (size_t primitiveIndex = 0, count = primitives.size(); primitiveIndex < count; ++primitiveIndex) {
770 const auto& primitive = primitives[primitiveIndex];
771 const auto& importInfo = result.meshBuilder->GetSubmesh(primitiveIndex);
772
773 // Load data.
774 GLTF2::GLTFLoadDataResult position, normal, tangent, color, joint, weight;
775 GLTF2::GLTFLoadDataResult texcoords[2];
776 if (!LoadPrimitiveAttributeData(primitive, position, normal, texcoords, tangent, joint, weight, color, flags)) {
777 result.error +=
778 GatherErrorStrings(primitiveIndex, position, normal, texcoords, tangent, color, joint, weight);
779 result.success = false;
780 break;
781 }
782
783 uint32_t const loadedVertexCount = static_cast<uint32_t>(position.elementCount);
784
785 auto fillDataBuffer = [](GLTF2::GLTFLoadDataResult& attribute) {
786 return IMeshBuilder::DataBuffer {
787 Convert(attribute.componentType, attribute.componentCount, attribute.normalized),
788 static_cast<uint32_t>(attribute.elementSize),
789 attribute.data,
790 };
791 };
792 const IMeshBuilder::DataBuffer positions = fillDataBuffer(position);
793 const IMeshBuilder::DataBuffer normals = fillDataBuffer(normal);
794 const IMeshBuilder::DataBuffer texcoords0 = fillDataBuffer(texcoords[0]);
795 const IMeshBuilder::DataBuffer texcoords1 = fillDataBuffer(texcoords[1]);
796 const IMeshBuilder::DataBuffer tangents = fillDataBuffer(tangent);
797 const IMeshBuilder::DataBuffer colors = fillDataBuffer(color);
798
799 result.meshBuilder->SetVertexData(primitiveIndex, positions, normals, texcoords0, texcoords1, tangents, colors);
800
801 // Process indices.
802 IndicesResult indices = LoadIndices(result, primitive, importInfo.indexType, loadedVertexCount);
803 if (!indices.loadDataResult.data.empty()) {
804 const auto elementSize = indices.loadDataResult.elementSize;
805 const IMeshBuilder::DataBuffer data { (elementSize == sizeof(uint32_t))
806 ? BASE_FORMAT_R32_UINT
807 : ((elementSize == sizeof(uint16_t)) ? BASE_FORMAT_R16_UINT
808 : BASE_FORMAT_R8_UINT),
809 static_cast<uint32_t>(elementSize), { indices.loadDataResult.data } };
810 result.meshBuilder->SetIndexData(primitiveIndex, data);
811 if (indices.primitiveRestart) {
812 result.meshBuilder->EnablePrimitiveRestart(primitiveIndex);
813 }
814 }
815
816 // Set AABB.
817 if (position.min.size() == 3 && position.max.size() == 3) {
818 const Math::Vec3 min = { position.min[0], position.min[1], position.min[2] };
819 const Math::Vec3 max = { position.max[0], position.max[1], position.max[2] };
820 result.meshBuilder->SetAABB(primitiveIndex, min, max);
821 } else {
822 result.meshBuilder->CalculateAABB(primitiveIndex, positions);
823 }
824
825 // Process joints.
826 if (!joint.data.empty() && (flags & CORE_GLTF_IMPORT_RESOURCE_SKIN)) {
827 const IMeshBuilder::DataBuffer joints = fillDataBuffer(joint);
828 const IMeshBuilder::DataBuffer weights = fillDataBuffer(weight);
829 result.meshBuilder->SetJointData(primitiveIndex, joints, weights, positions);
830 }
831 // Process morphs.
832 if (primitive.targets.size()) {
833 GLTF2::GLTFLoadDataResult targetPosition, targetNormal, targetTangent;
834 GenerateMorphTargets(primitive, importInfo, targetPosition, targetNormal, targetTangent);
835 const IMeshBuilder::DataBuffer targetPositions = fillDataBuffer(targetPosition);
836 const IMeshBuilder::DataBuffer targetNormals = fillDataBuffer(targetNormal);
837 const IMeshBuilder::DataBuffer targetTangents = fillDataBuffer(targetTangent);
838
839 result.meshBuilder->SetMorphTargetData(
840 primitiveIndex, positions, normals, tangents, targetPositions, targetNormals, targetTangents);
841 }
842 }
843 }
844
GatherMeshData(const GLTF2::Mesh & mesh,const GLTFImportResult & importResult,uint32_t flags,const IMaterialComponentManager & materialManager,const IDevice & device,IEngine & engine)845 GatherMeshDataResult GatherMeshData(const GLTF2::Mesh& mesh, const GLTFImportResult& importResult, uint32_t flags,
846 const IMaterialComponentManager& materialManager, const IDevice& device, IEngine& engine)
847 {
848 GatherMeshDataResult result;
849 auto context = GetInstance<IRenderContext>(*engine.GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
850 if (!context) {
851 result.success = false;
852 result.error = "RenderContext not found.";
853 return result;
854 }
855 auto& shaderManager = device.GetShaderManager();
856 const VertexInputDeclarationView vertexInputDeclaration =
857 shaderManager.GetVertexInputDeclarationView(shaderManager.GetVertexInputDeclarationHandle(
858 DefaultMaterialShaderConstants::VERTEX_INPUT_DECLARATION_FORWARD));
859
860 result.meshBuilder.reset(static_cast<MeshBuilder*>(CreateInstance<IMeshBuilder>(*context, UID_MESH_BUILDER).get()));
861 result.meshBuilder->Initialize(vertexInputDeclaration, mesh.primitives.size());
862
863 // Create primitive import info for mesh builder.
864 for (const auto& primitive : mesh.primitives) {
865 // Add to builder.
866 result.meshBuilder->AddSubmesh(CreatePrimitiveImportInfo(importResult, materialManager, primitive));
867 }
868
869 // Allocate memory for builder.
870 result.meshBuilder->Allocate();
871
872 // Feed primitive data for builder.
873 ProcessPrimitives(result, flags, mesh.primitives);
874
875 if (result.meshBuilder->GetVertexCount()) {
876 result.meshBuilder->CreateGpuResources();
877 }
878
879 return result;
880 }
881
ImportMesh(IEcs & ecs,const GatherMeshDataResult & gatherResult)882 Entity ImportMesh(IEcs& ecs, const GatherMeshDataResult& gatherResult)
883 {
884 // No vertices, which means we can't import mesh.
885 if (gatherResult.meshBuilder->GetVertexCount() == 0) {
886 return {};
887 }
888 auto meshEntity = gatherResult.meshBuilder->CreateMesh(ecs);
889 return meshEntity;
890 }
891
ResolveNodePath(GLTF2::Node const & node)892 string ResolveNodePath(GLTF2::Node const& node)
893 {
894 string path;
895
896 auto length = node.name.size();
897 GLTF2::Node* parent = node.parent;
898 while (parent) {
899 length += parent->name.size() + 1U;
900 parent = parent->parent;
901 }
902
903 path.resize(length);
904 length -= node.name.size();
905 const auto begin = path.begin();
906 path.replace(begin + static_cast<string::difference_type>(length),
907 begin + static_cast<string::difference_type>(length + node.name.size()), node.name);
908
909 parent = node.parent;
910 while (parent) {
911 length -= 1U;
912 path[length] = '/';
913 length -= parent->name.size();
914 path.replace(begin + static_cast<string::difference_type>(length),
915 begin + static_cast<string::difference_type>(length + parent->name.size()), parent->name);
916 parent = parent->parent;
917 }
918
919 return path;
920 }
921
BuildSkinIbmComponent(GLTF2::Skin const & skin,SkinIbmComponent & skinIbm)922 bool BuildSkinIbmComponent(GLTF2::Skin const& skin, SkinIbmComponent& skinIbm)
923 {
924 skinIbm.matrices.reserve(skin.joints.size());
925 bool failed = false;
926 bool useIdentityMatrix = true;
927 if (skin.inverseBindMatrices) {
928 GLTF2::GLTFLoadDataResult loadDataResult = GLTF2::LoadData(*skin.inverseBindMatrices);
929 if (loadDataResult.success) {
930 useIdentityMatrix = false;
931 auto ibls = array_view(
932 reinterpret_cast<Math::Mat4X4 const*>(loadDataResult.data.data()), loadDataResult.elementCount);
933 skinIbm.matrices.append(ibls.begin(), ibls.end());
934 }
935 }
936 if (failed) {
937 return false;
938 }
939
940 if (useIdentityMatrix) {
941 skinIbm.matrices.append(skin.joints.size(), Math::IDENTITY_4X4);
942 }
943
944 return true;
945 }
946
947 enum ImporterImageUsageFlags : uint32_t {
948 IMAGE_USAGE_BASE_COLOR_BIT = (1 << 1),
949 IMAGE_USAGE_METALLIC_ROUGHNESS_BIT = (1 << 2),
950 IMAGE_USAGE_NORMAL_BIT = (1 << 3),
951 IMAGE_USAGE_EMISSIVE_BIT = (1 << 4),
952 IMAGE_USAGE_OCCLUSION_BIT = (1 << 5),
953 IMAGE_USAGE_SPECULAR_GLOSSINESS_BIT = (1 << 6),
954 IMAGE_USAGE_CLEARCOAT_BIT = (1 << 7),
955 IMAGE_USAGE_CLEARCOAT_ROUGHNESS_BIT = (1 << 8),
956 IMAGE_USAGE_SHEEN_COLOR_BIT = (1 << 9),
957 IMAGE_USAGE_SHEEN_ROUGHNESS_BIT = (1 << 10),
958 IMAGE_USAGE_SPECULAR_BIT = (1 << 11),
959 IMAGE_USAGE_TRANSMISSION_BIT = (1 << 12),
960 IMAGE_USAGE_SINGLE_CHANNEL = IMAGE_USAGE_OCCLUSION_BIT | IMAGE_USAGE_TRANSMISSION_BIT
961 };
962
operator ==(const GLTF2::TextureInfo & info,const GLTF2::Image & image)963 inline bool operator==(const GLTF2::TextureInfo& info, const GLTF2::Image& image) noexcept
964 {
965 return info.texture && info.texture->image == ℑ
966 }
967
BaseColorFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)968 inline void BaseColorFlags(
969 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
970 {
971 if (material.metallicRoughness.baseColorTexture == image) {
972 result |= (IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_SRGB_BIT |
973 IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_PREMULTIPLY_ALPHA);
974 usage |= IMAGE_USAGE_BASE_COLOR_BIT;
975 }
976 }
977
MetallicRoughnessFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)978 inline void MetallicRoughnessFlags(
979 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
980 {
981 if (material.metallicRoughness.metallicRoughnessTexture == image) {
982 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
983 usage |= IMAGE_USAGE_METALLIC_ROUGHNESS_BIT;
984 }
985 }
986
NormalFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)987 inline void NormalFlags(const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
988 {
989 if (material.normalTexture.textureInfo == image) {
990 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
991 usage |= IMAGE_USAGE_NORMAL_BIT;
992 }
993 }
994
EmissiveFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)995 inline void EmissiveFlags(const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
996 {
997 if (material.emissiveTexture == image) {
998 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_SRGB_BIT;
999 usage |= IMAGE_USAGE_EMISSIVE_BIT;
1000 }
1001 }
1002
OcclusionFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1003 inline void OcclusionFlags(
1004 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1005 {
1006 if (material.occlusionTexture.textureInfo == image) {
1007 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1008 usage |= IMAGE_USAGE_OCCLUSION_BIT;
1009 }
1010 }
1011
1012 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
SpecularGlossinessFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1013 inline void SpecularGlossinessFlags(
1014 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1015 {
1016 if (material.specularGlossiness.specularGlossinessTexture == image) {
1017 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_SRGB_BIT;
1018 usage |= IMAGE_USAGE_SPECULAR_GLOSSINESS_BIT;
1019 }
1020 }
1021 #endif
1022
1023 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
ClearcoatFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1024 inline void ClearcoatFlags(
1025 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1026 {
1027 if (material.clearcoat.texture == image) {
1028 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1029 usage |= IMAGE_USAGE_CLEARCOAT_BIT;
1030 }
1031 }
1032
ClearcoatRoughnessFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1033 inline void ClearcoatRoughnessFlags(
1034 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1035 {
1036 if (material.clearcoat.roughnessTexture == image) {
1037 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1038 usage |= IMAGE_USAGE_CLEARCOAT_ROUGHNESS_BIT;
1039 }
1040 }
1041
ClearcoatNormalFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1042 inline void ClearcoatNormalFlags(
1043 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1044 {
1045 if (material.clearcoat.normalTexture.textureInfo == image) {
1046 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1047 usage |= IMAGE_USAGE_NORMAL_BIT;
1048 }
1049 }
1050 #endif
1051
1052 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
SheenFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1053 inline void SheenFlags(const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1054 {
1055 if (material.sheen.texture == image) {
1056 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_SRGB_BIT;
1057 usage |= IMAGE_USAGE_SHEEN_COLOR_BIT;
1058 }
1059 }
1060
SheenRoughnessFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1061 inline void SheenRoughnessFlags(
1062 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1063 {
1064 if (material.sheen.roughnessTexture == image) {
1065 if (!(usage & IMAGE_USAGE_SHEEN_COLOR_BIT)) {
1066 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1067 }
1068 usage |= IMAGE_USAGE_SHEEN_ROUGHNESS_BIT;
1069 }
1070 }
1071 #endif
1072
1073 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
SpecularColorFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1074 inline void SpecularColorFlags(
1075 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1076 {
1077 if (material.specular.colorTexture == image) {
1078 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_SRGB_BIT;
1079 usage |= IMAGE_USAGE_SPECULAR_BIT;
1080 }
1081 }
1082
SpecularStrengthFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1083 inline void SpecularStrengthFlags(
1084 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1085 {
1086 if (material.specular.texture == image) {
1087 if (!(usage & IMAGE_USAGE_SPECULAR_BIT)) {
1088 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1089 }
1090 usage |= IMAGE_USAGE_SPECULAR_BIT;
1091 }
1092 }
1093 #endif
1094
1095 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
TransmissionFlags(const GLTF2::Material & material,const GLTF2::Image & image,uint32_t & result,uint32_t & usage)1096 inline void TransmissionFlags(
1097 const GLTF2::Material& material, const GLTF2::Image& image, uint32_t& result, uint32_t& usage)
1098 {
1099 if (material.transmission.texture == image) {
1100 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1101 usage |= IMAGE_USAGE_TRANSMISSION_BIT;
1102 }
1103 }
1104 #endif
1105
ResolveImageLoadFlags(GLTF2::Image const & image,GLTF2::Data const & data,const ColorSpaceFlags colorSpaceFlags)1106 uint32_t ResolveImageLoadFlags(
1107 GLTF2::Image const& image, GLTF2::Data const& data, const ColorSpaceFlags colorSpaceFlags)
1108 {
1109 // Resolve whether image should be imported as SRGB or LINEAR.
1110 uint32_t result = 0;
1111 // Resolve in which parts of material this texture has been used.
1112 uint32_t usage = 0;
1113
1114 // Generating mipmaps for all textures (if not already contained in the image).
1115 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_GENERATE_MIPS;
1116
1117 for (const auto& material : data.materials) {
1118 BaseColorFlags(*material, image, result, usage);
1119 MetallicRoughnessFlags(*material, image, result, usage);
1120 NormalFlags(*material, image, result, usage);
1121 EmissiveFlags(*material, image, result, usage);
1122 OcclusionFlags(*material, image, result, usage);
1123
1124 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
1125 SpecularGlossinessFlags(*material, image, result, usage);
1126 #endif
1127 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
1128 ClearcoatFlags(*material, image, result, usage);
1129 ClearcoatRoughnessFlags(*material, image, result, usage);
1130 ClearcoatNormalFlags(*material, image, result, usage);
1131 #endif
1132 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
1133 SheenFlags(*material, image, result, usage);
1134 SheenRoughnessFlags(*material, image, result, usage);
1135 #endif
1136 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
1137 SpecularColorFlags(*material, image, result, usage);
1138 SpecularStrengthFlags(*material, image, result, usage);
1139 #endif
1140 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
1141 TransmissionFlags(*material, image, result, usage);
1142 #endif
1143 // possible color space conversions
1144 if (colorSpaceFlags & ColorSpaceFlagBits::COLOR_SPACE_SRGB_AS_LINEAR_BIT) {
1145 // remove srgb
1146 result &= (~IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_SRGB_BIT);
1147 // add forcing of linear
1148 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1149 }
1150 }
1151
1152 // In case the texture is only used in occlusion channel, we can convert it to grayscale R8.
1153 if ((usage & (IMAGE_USAGE_SINGLE_CHANNEL)) && !(usage & ~(IMAGE_USAGE_SINGLE_CHANNEL))) {
1154 result |= IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_GRAYSCALE_BIT;
1155 }
1156
1157 const bool isSRGB = result & IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_SRGB_BIT;
1158 const bool isLinear = result & IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1159 if (isSRGB && isLinear) {
1160 // In case the texture has both SRGB & LINEAR set, default to SRGB and print a warning.
1161 result &= ~IImageLoaderManager::ImageLoaderFlags::IMAGE_LOADER_FORCE_LINEAR_RGB_BIT;
1162
1163 if (GLTF2::IsDataURI(image.uri)) {
1164 CORE_LOG_W("Unable to resolve color space for Image, defaulting to SRGB.");
1165 } else {
1166 CORE_LOG_W("Unable to resolve color space for Image %s, defaulting to SRGB.", image.uri.c_str());
1167 }
1168 }
1169
1170 return result;
1171 }
1172
ResolveSampler(GLTF2::Texture const & texture,GLTF2::Data const & data,GLTFImportResult const & importResult)1173 EntityReference ResolveSampler(
1174 GLTF2::Texture const& texture, GLTF2::Data const& data, GLTFImportResult const& importResult)
1175 {
1176 if (texture.sampler) {
1177 const size_t index = FindIndex(data.samplers, texture.sampler);
1178 if (index != GLTF2::GLTF_INVALID_INDEX) {
1179 return importResult.data.samplers[index];
1180 }
1181 }
1182
1183 return {};
1184 }
1185
ResolveSampler(const GLTF2::TextureInfo & textureInfo,GLTF2::Data const & data,GLTFImportResult const & importResult)1186 inline EntityReference ResolveSampler(
1187 const GLTF2::TextureInfo& textureInfo, GLTF2::Data const& data, GLTFImportResult const& importResult)
1188 {
1189 return textureInfo.texture ? ResolveSampler(*textureInfo.texture, data, importResult) : EntityReference {};
1190 }
1191
1192 // Textures
GatherImageData(GLTF2::Image & image,GLTF2::Data const & data,IFileManager & fileManager,IImageLoaderManager & imageManager,uint32_t loadFlags,ColorSpaceFlags colorSpaceFlags)1193 IImageLoaderManager::LoadResult GatherImageData(GLTF2::Image& image, GLTF2::Data const& data, IFileManager& fileManager,
1194 IImageLoaderManager& imageManager, uint32_t loadFlags, ColorSpaceFlags colorSpaceFlags)
1195 {
1196 vector<uint8_t> raw;
1197
1198 const GLTF2::BufferView* bufferView = image.bufferView;
1199
1200 if (bufferView && image.type != GLTF2::MimeType::INVALID) {
1201 if (bufferView->data) {
1202 raw.append(image.bufferView->data, bufferView->data + bufferView->byteLength);
1203 }
1204 } else if (image.uri.size()) {
1205 auto extension = GetImageExtension(image.type);
1206 const auto result = GLTF2::LoadUri(image.uri, "image", data.filepath, fileManager, extension, raw);
1207 switch (result) {
1208 case GLTF2::URI_LOAD_FAILED_TO_DECODE_BASE64:
1209 return IImageLoaderManager::LoadResult { false, "Base64 decoding failed.", nullptr };
1210
1211 case GLTF2::URI_LOAD_FAILED_TO_READ_FILE:
1212 return IImageLoaderManager::LoadResult { false, "Failed to read file.", nullptr };
1213
1214 case GLTF2::URI_LOAD_FAILED_INVALID_MIME_TYPE:
1215 return IImageLoaderManager::LoadResult { false, "Image data is not image type.", nullptr };
1216
1217 default:
1218 case GLTF2::URI_LOAD_SUCCESS:
1219 break;
1220 }
1221 }
1222
1223 // Resolve image usage and determine flags.
1224 const uint32_t flags = ResolveImageLoadFlags(image, data, colorSpaceFlags) | loadFlags;
1225
1226 array_view<const uint8_t> rawdata { raw };
1227 return imageManager.LoadImage(rawdata, flags);
1228 }
1229
ResolveReferencedImages(GLTF2::Data const & data,vector<bool> & imageLoadingRequred)1230 void ResolveReferencedImages(GLTF2::Data const& data, vector<bool>& imageLoadingRequred)
1231 {
1232 // loader shouldn't add nullptrs to Data::textures or Texture::image, but checking still
1233 for (const auto& texture : data.textures) {
1234 if (!texture) {
1235 continue;
1236 }
1237 const size_t index = FindIndex(data.images, texture->image);
1238 if ((index != GLTF2::GLTF_INVALID_INDEX) && (texture->image)) {
1239 imageLoadingRequred[index] = true;
1240 }
1241 }
1242
1243 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
1244 // Find image references from image based lights.
1245 for (const auto& light : data.imageBasedLights) {
1246 if (light && (light->skymapImage != GLTF2::GLTF_INVALID_INDEX) && (light->skymapImage < data.images.size()) &&
1247 (data.images[light->skymapImage])) {
1248 imageLoadingRequred[light->skymapImage] = true;
1249 }
1250 if (light && (light->specularCubeImage != GLTF2::GLTF_INVALID_INDEX) &&
1251 (light->specularCubeImage < data.images.size()) && (data.images[light->specularCubeImage])) {
1252 imageLoadingRequred[light->specularCubeImage] = true;
1253 }
1254 }
1255 #endif
1256 }
1257
ConvertAnimationInterpolation(GLTF2::AnimationInterpolation mode)1258 AnimationTrackComponent::Interpolation ConvertAnimationInterpolation(GLTF2::AnimationInterpolation mode)
1259 {
1260 switch (mode) {
1261 case GLTF2::AnimationInterpolation::INVALID:
1262 CORE_ASSERT(false);
1263 break;
1264 case GLTF2::AnimationInterpolation::STEP:
1265 return AnimationTrackComponent::Interpolation::STEP;
1266 case GLTF2::AnimationInterpolation::LINEAR:
1267 return AnimationTrackComponent::Interpolation::LINEAR;
1268 case GLTF2::AnimationInterpolation::SPLINE:
1269 return AnimationTrackComponent::Interpolation::SPLINE;
1270 default:
1271 break;
1272 }
1273
1274 return AnimationTrackComponent::Interpolation::LINEAR;
1275 }
1276
1277 template<class T>
CopyFrames(GLTF2::GLTFLoadDataResult const & animationFrameDataResult,vector<T> & destination)1278 void CopyFrames(GLTF2::GLTFLoadDataResult const& animationFrameDataResult, vector<T>& destination)
1279 {
1280 destination.resize(animationFrameDataResult.elementCount);
1281 if (animationFrameDataResult.componentType == GLTF2::ComponentType::FLOAT) {
1282 CORE_ASSERT(animationFrameDataResult.elementSize == sizeof(T));
1283
1284 const size_t dataSizeInBytes = animationFrameDataResult.elementSize * animationFrameDataResult.elementCount;
1285 if (!CloneData(destination.data(), destination.size() * sizeof(T), animationFrameDataResult.data.data(),
1286 dataSizeInBytes)) {
1287 CORE_LOG_E("Copying of raw framedata failed.");
1288 }
1289 } else {
1290 // Convert data.
1291 if (animationFrameDataResult.normalized) {
1292 ConvertNormalizedDataToFloat(animationFrameDataResult, reinterpret_cast<float*>(destination.data()));
1293 } else {
1294 ConvertDataToFloat(animationFrameDataResult, reinterpret_cast<float*>(destination.data()));
1295 }
1296 }
1297 }
1298
1299 template<>
CopyFrames(GLTF2::GLTFLoadDataResult const & animationFrameDataResult,vector<bool> & destination)1300 void CopyFrames(GLTF2::GLTFLoadDataResult const& animationFrameDataResult, vector<bool>& destination)
1301 {
1302 destination.resize(animationFrameDataResult.elementCount);
1303 if (animationFrameDataResult.componentType == GLTF2::ComponentType::BYTE ||
1304 animationFrameDataResult.componentType == GLTF2::ComponentType::UNSIGNED_BYTE) {
1305 CORE_ASSERT(animationFrameDataResult.elementSize == sizeof(bool));
1306
1307 const size_t dataSizeInBytes = animationFrameDataResult.elementSize * animationFrameDataResult.elementCount;
1308 if (!CloneData(destination.data(), destination.size() * sizeof(bool), animationFrameDataResult.data.data(),
1309 dataSizeInBytes)) {
1310 CORE_LOG_E("Copying of raw framedata failed.");
1311 }
1312 } else {
1313 // Convert data.
1314 ConvertDataToBool(animationFrameDataResult, destination.data());
1315 }
1316 }
1317
BuildAnimationInput(GLTF2::Data const & data,IFileManager & fileManager,GLTF2::Accessor & inputAccessor,AnimationInputComponent & inputComponent)1318 bool BuildAnimationInput(GLTF2::Data const& data, IFileManager& fileManager, GLTF2::Accessor& inputAccessor,
1319 AnimationInputComponent& inputComponent)
1320 {
1321 const GLTF2::GLTFLoadDataResult animationInputDataResult = LoadData(inputAccessor);
1322 if (animationInputDataResult.success) {
1323 // Copy timestamps.
1324 inputComponent.timestamps.reserve(animationInputDataResult.elementCount);
1325 const float* timeStamps =
1326 reinterpret_cast<const float*>(reinterpret_cast<uintptr_t>(animationInputDataResult.data.data()));
1327 inputComponent.timestamps.append(timeStamps, timeStamps + animationInputDataResult.elementCount);
1328 }
1329
1330 return animationInputDataResult.success;
1331 }
1332
1333 template<typename ReadType>
AppendAnimationOutputData(uint64_t outputTypeHash,const GLTF2::GLTFLoadDataResult & animationOutputDataResult,AnimationOutputComponent & outputComponent)1334 void AppendAnimationOutputData(uint64_t outputTypeHash, const GLTF2::GLTFLoadDataResult& animationOutputDataResult,
1335 AnimationOutputComponent& outputComponent)
1336 {
1337 outputComponent.type = outputTypeHash;
1338 vector<ReadType> positions;
1339 CopyFrames(animationOutputDataResult, positions);
1340 const auto dataView = array_view(reinterpret_cast<const uint8_t*>(positions.data()), positions.size_in_bytes());
1341 outputComponent.data.append(dataView.cbegin(), dataView.cend());
1342 }
1343
BuildAnimationOutput(GLTF2::Data const & data,IFileManager & fileManager,GLTF2::Accessor & outputAccessor,GLTF2::AnimationPath path,AnimationOutputComponent & outputComponent)1344 bool BuildAnimationOutput(GLTF2::Data const& data, IFileManager& fileManager, GLTF2::Accessor& outputAccessor,
1345 GLTF2::AnimationPath path, AnimationOutputComponent& outputComponent)
1346 {
1347 const GLTF2::GLTFLoadDataResult animationOutputDataResult = LoadData(outputAccessor);
1348 if (animationOutputDataResult.success) {
1349 switch (path) {
1350 case GLTF2::AnimationPath::TRANSLATION:
1351 AppendAnimationOutputData<Math::Vec3>(PropertyType::VEC3_T, animationOutputDataResult, outputComponent);
1352 break;
1353
1354 case GLTF2::AnimationPath::ROTATION:
1355 AppendAnimationOutputData<Math::Vec4>(PropertyType::QUAT_T, animationOutputDataResult, outputComponent);
1356 break;
1357
1358 case GLTF2::AnimationPath::SCALE:
1359 AppendAnimationOutputData<Math::Vec3>(PropertyType::VEC3_T, animationOutputDataResult, outputComponent);
1360 break;
1361
1362 case GLTF2::AnimationPath::WEIGHTS:
1363 AppendAnimationOutputData<float>(
1364 PropertyType::FLOAT_VECTOR_T, animationOutputDataResult, outputComponent);
1365 break;
1366
1367 case GLTF2::AnimationPath::VISIBLE:
1368 AppendAnimationOutputData<bool>(PropertyType::BOOL_T, animationOutputDataResult, outputComponent);
1369 break;
1370
1371 case GLTF2::AnimationPath::OPACITY:
1372 AppendAnimationOutputData<float>(PropertyType::FLOAT_T, animationOutputDataResult, outputComponent);
1373 break;
1374
1375 case GLTF2::AnimationPath::INVALID:
1376 default:
1377 CORE_LOG_W("Animation.channel.path type %d not implemented", static_cast<int>(path));
1378 break;
1379 }
1380 }
1381
1382 return animationOutputDataResult.success;
1383 }
1384
1385 #if defined(GLTF2_EXTENSION_KHR_TEXTURE_TRANSFORM)
FillTextureTransform(const GLTF2::TextureInfo & textureInfo,MaterialComponent::TextureTransform & desc)1386 void FillTextureTransform(const GLTF2::TextureInfo& textureInfo, MaterialComponent::TextureTransform& desc)
1387 {
1388 const auto& transform = textureInfo.transform;
1389 desc.translation = transform.offset;
1390 desc.rotation = transform.rotation;
1391 desc.scale = transform.scale;
1392 }
1393 #endif
1394
FillTextureParams(const GLTF2::TextureInfo & textureInfo,const GLTFImportResult & importResult,const GLTF2::Data & data,IEntityManager & entityManager,MaterialComponent & desc,MaterialComponent::TextureIndex index)1395 void FillTextureParams(const GLTF2::TextureInfo& textureInfo, const GLTFImportResult& importResult,
1396 const GLTF2::Data& data, IEntityManager& entityManager, MaterialComponent& desc,
1397 MaterialComponent::TextureIndex index)
1398 {
1399 desc.textures[index].image = GetImportedTextureHandle(importResult, textureInfo.index);
1400 if (textureInfo.texture) {
1401 desc.textures[index].sampler = ResolveSampler(*textureInfo.texture, data, importResult);
1402 desc.useTexcoordSetBit |= static_cast<uint32_t>((textureInfo.texCoordIndex == 1)) << index;
1403 #if defined(GLTF2_EXTENSION_KHR_TEXTURE_TRANSFORM)
1404 FillTextureTransform(textureInfo, desc.textures[index].transform);
1405 #endif
1406 }
1407 }
1408
ImportTexture(const string_view uri,IImageContainer::Ptr image,IRenderDataStoreDefaultStaging & staging,IGpuResourceManager & gpuResourceManager)1409 RenderHandleReference ImportTexture(const string_view uri, IImageContainer::Ptr image,
1410 IRenderDataStoreDefaultStaging& staging, IGpuResourceManager& gpuResourceManager)
1411 {
1412 RenderHandleReference imageHandle;
1413 if (!image) {
1414 return imageHandle;
1415 }
1416 // return if image created already with the uri
1417 imageHandle = gpuResourceManager.GetImageHandle(uri);
1418 if (imageHandle) {
1419 return imageHandle;
1420 }
1421
1422 const auto& imageDesc = image->GetImageDesc();
1423 const auto& subImageDescs = image->GetBufferImageCopies();
1424 const auto data = image->GetData();
1425
1426 // Create image according to the image container's description. (expects that conversion handles everything)
1427 const GpuImageDesc gpuImageDesc = gpuResourceManager.CreateGpuImageDesc(imageDesc);
1428 imageHandle = gpuResourceManager.Create(uri, gpuImageDesc);
1429
1430 if (imageHandle) {
1431 // Create buffer for uploading image data
1432 GpuBufferDesc gpuBufferDesc;
1433 gpuBufferDesc.usageFlags = BufferUsageFlagBits::CORE_BUFFER_USAGE_TRANSFER_SRC_BIT;
1434 gpuBufferDesc.memoryPropertyFlags = MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
1435 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1436 gpuBufferDesc.engineCreationFlags =
1437 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_CREATE_IMMEDIATE |
1438 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_MAP_OUTSIDE_RENDERER |
1439 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_DEFERRED_DESTROY;
1440 gpuBufferDesc.byteSize = static_cast<uint32_t>(data.size_bytes());
1441 auto bufferHandle = gpuResourceManager.Create(gpuBufferDesc);
1442 // Ideally ImageLoader would decode directly to the buffer to save one copy.
1443 if (auto buffer = static_cast<uint8_t*>(gpuResourceManager.MapBufferMemory(bufferHandle)); buffer) {
1444 const auto count = Math::min(static_cast<uint32_t>(data.size_bytes()), gpuBufferDesc.byteSize);
1445 std::copy(data.data(), data.data() + count, buffer);
1446 }
1447 gpuResourceManager.UnmapBuffer(bufferHandle);
1448
1449 // Gather copy operations for all the sub images (mip levels, cube faces)
1450 vector<BufferImageCopy> copies;
1451 copies.reserve(subImageDescs.size());
1452 for (const auto& subImageDesc : subImageDescs) {
1453 const BufferImageCopy bufferImageCopy {
1454 subImageDesc.bufferOffset, // bufferOffset
1455 subImageDesc.bufferRowLength, // bufferRowLength
1456 subImageDesc.bufferImageHeight, // bufferImageHeight
1457 {
1458 CORE_IMAGE_ASPECT_COLOR_BIT, // imageAspectFlags
1459 subImageDesc.mipLevel, // mipLevel
1460 0, // baseArrayLayer
1461 subImageDesc.layerCount, // layerCount
1462 }, // imageSubresource
1463 {}, // imageOffset
1464 { subImageDesc.width, subImageDesc.height, subImageDesc.depth } // imageExtent
1465 };
1466 copies.push_back(bufferImageCopy);
1467 }
1468 staging.CopyBufferToImage(bufferHandle, imageHandle, copies);
1469 } else {
1470 CORE_LOG_W("Creating an import image failed (format:%u, width:%u, height:%u)",
1471 static_cast<uint32_t>(gpuImageDesc.format), gpuImageDesc.width, gpuImageDesc.height);
1472 }
1473 return imageHandle;
1474 }
1475
FillMetallicRoughness(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1476 void FillMetallicRoughness(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1477 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1478 {
1479 desc.type = MaterialComponent::Type::METALLIC_ROUGHNESS;
1480 FillTextureParams(gltfMaterial.metallicRoughness.baseColorTexture, importResult, data, em, desc,
1481 MaterialComponent::TextureIndex::BASE_COLOR);
1482 desc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor = gltfMaterial.metallicRoughness.baseColorFactor;
1483
1484 // Metallic-roughness.
1485 FillTextureParams(gltfMaterial.metallicRoughness.metallicRoughnessTexture, importResult, data, em, desc,
1486 MaterialComponent::TextureIndex::MATERIAL);
1487 desc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.y = gltfMaterial.metallicRoughness.roughnessFactor;
1488 desc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.z = gltfMaterial.metallicRoughness.metallicFactor;
1489 }
1490
FillSpecularGlossiness(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1491 void FillSpecularGlossiness(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1492 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1493 {
1494 desc.type = MaterialComponent::Type::SPECULAR_GLOSSINESS;
1495 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
1496 FillTextureParams(gltfMaterial.specularGlossiness.diffuseTexture, importResult, data, em, desc,
1497 MaterialComponent::TextureIndex::BASE_COLOR);
1498 desc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor = gltfMaterial.specularGlossiness.diffuseFactor;
1499
1500 // Glossiness.
1501 FillTextureParams(gltfMaterial.specularGlossiness.specularGlossinessTexture, importResult, data, em, desc,
1502 MaterialComponent::TextureIndex::MATERIAL);
1503 desc.textures[MaterialComponent::TextureIndex::MATERIAL].factor = { gltfMaterial.specularGlossiness.specularFactor,
1504 gltfMaterial.specularGlossiness.glossinessFactor };
1505 #endif
1506 }
1507
FillUnlit(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1508 void FillUnlit(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1509 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1510 {
1511 desc.type = MaterialComponent::Type::UNLIT;
1512
1513 FillTextureParams(gltfMaterial.metallicRoughness.baseColorTexture, importResult, data, em, desc,
1514 MaterialComponent::TextureIndex::BASE_COLOR);
1515 desc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor = gltfMaterial.metallicRoughness.baseColorFactor;
1516 }
1517
FillClearcoat(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1518 void FillClearcoat(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1519 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1520 {
1521 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT) || defined(GLTF2_EXTRAS_CLEAR_COAT_MATERIAL)
1522 // Clearcoat.
1523 desc.textures[MaterialComponent::TextureIndex::CLEARCOAT].factor.x = gltfMaterial.clearcoat.factor;
1524 FillTextureParams(
1525 gltfMaterial.clearcoat.texture, importResult, data, em, desc, MaterialComponent::TextureIndex::CLEARCOAT);
1526 desc.textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS].factor.y = gltfMaterial.clearcoat.roughness;
1527 FillTextureParams(gltfMaterial.clearcoat.roughnessTexture, importResult, data, em, desc,
1528 MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS);
1529 FillTextureParams(gltfMaterial.clearcoat.normalTexture.textureInfo, importResult, data, em, desc,
1530 MaterialComponent::TextureIndex::CLEARCOAT_NORMAL);
1531 desc.textures[MaterialComponent::TextureIndex::CLEARCOAT_NORMAL].factor.x =
1532 gltfMaterial.clearcoat.normalTexture.scale;
1533 #endif
1534 }
1535
FillIor(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1536 void FillIor(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1537 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1538 {
1539 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
1540 // IOR.
1541 if (gltfMaterial.ior.ior != 1.5f) {
1542 auto reflectance = (gltfMaterial.ior.ior - 1.f) / (gltfMaterial.ior.ior + 1.f);
1543 desc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.w = reflectance * reflectance;
1544 }
1545 #endif
1546 }
1547
FillSheen(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1548 void FillSheen(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1549 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1550 {
1551 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
1552 // Sheen.
1553 desc.textures[MaterialComponent::TextureIndex::SHEEN].factor = Math::Vec4(gltfMaterial.sheen.factor, 0.f);
1554 FillTextureParams(gltfMaterial.sheen.texture, importResult, data, em, desc, MaterialComponent::TextureIndex::SHEEN);
1555 if (gltfMaterial.sheen.roughnessTexture.texture) {
1556 CORE_LOG_W("Sheen roughness mapping not supported by gltf2 importer");
1557 }
1558 // to sheen alpha
1559 desc.textures[MaterialComponent::TextureIndex::SHEEN].factor.w = gltfMaterial.sheen.roughness;
1560 #endif
1561 }
1562
FillSpecular(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1563 void FillSpecular(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1564 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1565 {
1566 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
1567 // Specular.
1568 desc.textures[MaterialComponent::TextureIndex::SPECULAR].factor =
1569 Math::Vec4(gltfMaterial.specular.color, gltfMaterial.specular.factor);
1570 if (gltfMaterial.specular.texture.index != GLTF2::GLTF_INVALID_INDEX &&
1571 gltfMaterial.specular.colorTexture.index == GLTF2::GLTF_INVALID_INDEX) {
1572 FillTextureParams(
1573 gltfMaterial.specular.texture, importResult, data, em, desc, MaterialComponent::TextureIndex::SPECULAR);
1574 } else if (gltfMaterial.specular.texture.index == GLTF2::GLTF_INVALID_INDEX &&
1575 gltfMaterial.specular.colorTexture.index != GLTF2::GLTF_INVALID_INDEX) {
1576 FillTextureParams(gltfMaterial.specular.colorTexture, importResult, data, em, desc,
1577 MaterialComponent::TextureIndex::SPECULAR);
1578 } else if (gltfMaterial.specular.texture.index != gltfMaterial.specular.colorTexture.index) {
1579 CORE_LOG_W("Separate specular strength and color textures are not supported!");
1580 FillTextureParams(gltfMaterial.specular.colorTexture, importResult, data, em, desc,
1581 MaterialComponent::TextureIndex::SPECULAR);
1582 } else {
1583 FillTextureParams(gltfMaterial.specular.colorTexture, importResult, data, em, desc,
1584 MaterialComponent::TextureIndex::SPECULAR);
1585 }
1586 #endif
1587 }
1588
FillTransmission(MaterialComponent & desc,const GLTFImportResult & importResult,const GLTF2::Data & data,const GLTF2::Material & gltfMaterial,IEntityManager & em)1589 void FillTransmission(MaterialComponent& desc, const GLTFImportResult& importResult, const GLTF2::Data& data,
1590 const GLTF2::Material& gltfMaterial, IEntityManager& em)
1591 {
1592 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
1593 // Transmission.
1594 desc.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x = gltfMaterial.transmission.factor;
1595 FillTextureParams(
1596 gltfMaterial.transmission.texture, importResult, data, em, desc, MaterialComponent::TextureIndex::TRANSMISSION);
1597 #endif
1598 }
1599
SelectShaders(MaterialComponent & desc,const GLTF2::Material & gltfMaterial,const GLTF2::GLTF2Importer::DefaultMaterialShaderData & dmShaderData)1600 void SelectShaders(MaterialComponent& desc, const GLTF2::Material& gltfMaterial,
1601 const GLTF2::GLTF2Importer::DefaultMaterialShaderData& dmShaderData)
1602 {
1603 // Shadow casting is removed from blend modes by default.
1604 // Transmission over writes the gltf material blend mode.
1605 if (desc.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x > 0.0f) {
1606 desc.materialShader.shader = dmShaderData.blend.shader;
1607 // no support for double-sideness with default material and transmission
1608 desc.materialShader.graphicsState = dmShaderData.blend.gfxState;
1609 if (gltfMaterial.alphaMode == GLTF2::AlphaMode::BLEND) { // blending -> no shadows
1610 desc.materialLightingFlags &= (~MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT);
1611 }
1612 } else if (gltfMaterial.alphaMode == GLTF2::AlphaMode::BLEND) {
1613 desc.materialShader.shader = dmShaderData.blend.shader;
1614 desc.materialShader.graphicsState =
1615 gltfMaterial.doubleSided ? dmShaderData.blend.gfxStateDoubleSided : dmShaderData.blend.gfxState;
1616 desc.materialLightingFlags &= (~MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT);
1617 } else {
1618 // opaque materials are expected to have alpha value of 1.0f based on the blend state
1619 desc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor.w = 1.0f;
1620 desc.materialShader.shader = dmShaderData.opaque.shader;
1621 desc.materialShader.graphicsState =
1622 gltfMaterial.doubleSided ? dmShaderData.opaque.gfxStateDoubleSided : dmShaderData.opaque.gfxState;
1623 // default materials support instancing with opaque materials.
1624 desc.extraRenderingFlags |= MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT;
1625 }
1626 // shadow shader data
1627 if (desc.materialLightingFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) {
1628 desc.depthShader.shader = dmShaderData.depth.shader;
1629 desc.depthShader.graphicsState =
1630 gltfMaterial.doubleSided ? dmShaderData.depth.gfxStateDoubleSided : dmShaderData.depth.gfxState;
1631 }
1632 }
1633
ImportMaterial(GLTFImportResult const & importResult,GLTF2::Data const & data,GLTF2::Material const & gltfMaterial,const Entity materialEntity,IMaterialComponentManager & materialManager,IGpuResourceManager const & gpuResourceManager,const GLTF2::GLTF2Importer::DefaultMaterialShaderData & dmShaderData)1634 bool ImportMaterial(GLTFImportResult const& importResult, GLTF2::Data const& data, GLTF2::Material const& gltfMaterial,
1635 const Entity materialEntity, IMaterialComponentManager& materialManager,
1636 IGpuResourceManager const& gpuResourceManager, const GLTF2::GLTF2Importer::DefaultMaterialShaderData& dmShaderData)
1637 {
1638 auto materialHandle = materialManager.Write(materialEntity);
1639 if (!materialHandle) {
1640 return false;
1641 }
1642 auto& desc = *materialHandle;
1643 auto& ecs = materialManager.GetEcs();
1644 auto& em = ecs.GetEntityManager();
1645 if (gltfMaterial.type == GLTF2::Material::Type::MetallicRoughness) {
1646 FillMetallicRoughness(desc, importResult, data, gltfMaterial, em);
1647 } else if (gltfMaterial.type == GLTF2::Material::Type::SpecularGlossiness) {
1648 FillSpecularGlossiness(desc, importResult, data, gltfMaterial, em);
1649 } else if (gltfMaterial.type == GLTF2::Material::Type::Unlit) {
1650 FillUnlit(desc, importResult, data, gltfMaterial, em);
1651 }
1652
1653 // Normal texture.
1654 FillTextureParams(
1655 gltfMaterial.normalTexture.textureInfo, importResult, data, em, desc, MaterialComponent::TextureIndex::NORMAL);
1656 desc.textures[MaterialComponent::TextureIndex::NORMAL].factor.x = gltfMaterial.normalTexture.scale;
1657
1658 // Occlusion texture.
1659 FillTextureParams(
1660 gltfMaterial.occlusionTexture.textureInfo, importResult, data, em, desc, MaterialComponent::TextureIndex::AO);
1661 desc.textures[MaterialComponent::TextureIndex::AO].factor.x = gltfMaterial.occlusionTexture.strength;
1662
1663 // Emissive texture.
1664 FillTextureParams(
1665 gltfMaterial.emissiveTexture, importResult, data, em, desc, MaterialComponent::TextureIndex::EMISSIVE);
1666 desc.textures[MaterialComponent::TextureIndex::EMISSIVE].factor = gltfMaterial.emissiveFactor;
1667
1668 // Handle material extension
1669 FillClearcoat(desc, importResult, data, gltfMaterial, em);
1670 FillIor(desc, importResult, data, gltfMaterial, em);
1671 FillSheen(desc, importResult, data, gltfMaterial, em);
1672 FillSpecular(desc, importResult, data, gltfMaterial, em);
1673 FillTransmission(desc, importResult, data, gltfMaterial, em);
1674
1675 // Always receive and cast shadows. (Modified with blend modes below)
1676 desc.materialLightingFlags |= MaterialComponent::LightingFlagBits::SHADOW_RECEIVER_BIT;
1677 desc.materialLightingFlags |= MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT;
1678
1679 if (gltfMaterial.alphaMode == GLTF2::AlphaMode::MASK) {
1680 // we "enable" if it's set
1681 desc.alphaCutoff = gltfMaterial.alphaCutoff;
1682 }
1683
1684 SelectShaders(desc, gltfMaterial, dmShaderData);
1685 return true;
1686 }
1687
ConvertToCoreFilter(GLTF2::FilterMode mode,Filter & outFilter,Filter & outMipmapMode)1688 void ConvertToCoreFilter(GLTF2::FilterMode mode, Filter& outFilter, Filter& outMipmapMode)
1689 {
1690 switch (mode) {
1691 case GLTF2::FilterMode::NEAREST:
1692 outFilter = CORE_FILTER_NEAREST;
1693 outMipmapMode = CORE_FILTER_NEAREST;
1694 break;
1695
1696 default:
1697 case GLTF2::FilterMode::LINEAR:
1698 outFilter = CORE_FILTER_LINEAR;
1699 outMipmapMode = CORE_FILTER_LINEAR;
1700 break;
1701
1702 case GLTF2::FilterMode::NEAREST_MIPMAP_NEAREST:
1703 outFilter = CORE_FILTER_NEAREST;
1704 outMipmapMode = CORE_FILTER_NEAREST;
1705 break;
1706
1707 case GLTF2::FilterMode::LINEAR_MIPMAP_NEAREST:
1708 outFilter = CORE_FILTER_LINEAR;
1709 outMipmapMode = CORE_FILTER_NEAREST;
1710 break;
1711
1712 case GLTF2::FilterMode::NEAREST_MIPMAP_LINEAR:
1713 outFilter = CORE_FILTER_NEAREST;
1714 outMipmapMode = CORE_FILTER_LINEAR;
1715 break;
1716
1717 case GLTF2::FilterMode::LINEAR_MIPMAP_LINEAR:
1718 outFilter = CORE_FILTER_LINEAR;
1719 outMipmapMode = CORE_FILTER_LINEAR;
1720 break;
1721 }
1722 }
1723
ConvertToCoreFilter(GLTF2::FilterMode mode,Filter & outFilter)1724 void ConvertToCoreFilter(GLTF2::FilterMode mode, Filter& outFilter)
1725 {
1726 Filter unused;
1727 ConvertToCoreFilter(mode, outFilter, unused);
1728 }
1729
ConvertToCoreWrapMode(GLTF2::WrapMode mode)1730 SamplerAddressMode ConvertToCoreWrapMode(GLTF2::WrapMode mode)
1731 {
1732 switch (mode) {
1733 default:
1734 case GLTF2::WrapMode::CLAMP_TO_EDGE:
1735 return CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
1736
1737 case GLTF2::WrapMode::MIRRORED_REPEAT:
1738 return CORE_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
1739
1740 case GLTF2::WrapMode::REPEAT:
1741 return CORE_SAMPLER_ADDRESS_MODE_REPEAT;
1742 }
1743 }
1744
RecursivelyCreateEntities(IEntityManager & entityManager,GLTF2::Data const & data,GLTF2::Node const & node,unordered_map<size_t,Entity> & sceneEntities)1745 void RecursivelyCreateEntities(IEntityManager& entityManager, GLTF2::Data const& data, GLTF2::Node const& node,
1746 unordered_map<size_t, Entity>& sceneEntities)
1747 {
1748 // Look up node index nodes array.
1749 if (size_t const nodeIndex = FindIndex(data.nodes, &node); nodeIndex != GLTF2::GLTF_INVALID_INDEX) {
1750 // Create entity for this node in this scene.
1751 sceneEntities[nodeIndex] = entityManager.Create();
1752 } else {
1753 // NOTE: Failed to find given node.
1754 }
1755
1756 for (auto child : node.children) {
1757 RecursivelyCreateEntities(entityManager, data, *child, sceneEntities);
1758 }
1759 }
1760
FindEntity(unordered_map<size_t,Entity> const & sceneEntities,size_t nodeIndex)1761 Entity FindEntity(unordered_map<size_t, Entity> const& sceneEntities, size_t nodeIndex)
1762 {
1763 if (auto const pos = sceneEntities.find(nodeIndex); pos != sceneEntities.end()) {
1764 return pos->second;
1765 } else {
1766 return {};
1767 }
1768 }
1769
CreateNode(IEcs & ecs,const GLTF2::Node & node,const Entity entity,const GLTF2::Data & data,const unordered_map<size_t,Entity> & sceneEntities,const Entity sceneEntity,const uint32_t sceneId)1770 void CreateNode(IEcs& ecs, const GLTF2::Node& node, const Entity entity, const GLTF2::Data& data,
1771 const unordered_map<size_t, Entity>& sceneEntities, const Entity sceneEntity, const uint32_t sceneId)
1772 {
1773 INodeComponentManager& nodeManager = *(GetManager<INodeComponentManager>(ecs));
1774 nodeManager.Create(entity);
1775
1776 ScopedHandle<NodeComponent> component = nodeManager.Write(entity);
1777 component->sceneId = sceneId;
1778 if (const size_t parentIndex = FindIndex(data.nodes, node.parent); parentIndex != GLTF2::GLTF_INVALID_INDEX) {
1779 component->parent = FindEntity(sceneEntities, parentIndex);
1780 } else {
1781 // Set as child of scene.
1782 component->parent = sceneEntity;
1783 }
1784 }
1785
CreateName(IEcs & ecs,const GLTF2::Node & node,const Entity entity)1786 void CreateName(IEcs& ecs, const GLTF2::Node& node, const Entity entity)
1787 {
1788 INameComponentManager& nameManager = *(GetManager<INameComponentManager>(ecs));
1789 nameManager.Create(entity);
1790 ScopedHandle<NameComponent> nameHandle = nameManager.Write(entity);
1791 nameHandle->name = node.name;
1792 }
1793
CreateTransform(IEcs & ecs,const GLTF2::Node & node,const Entity entity)1794 void CreateTransform(IEcs& ecs, const GLTF2::Node& node, const Entity entity)
1795 {
1796 ITransformComponentManager& transformManager = *(GetManager<ITransformComponentManager>(ecs));
1797
1798 transformManager.Create(entity);
1799
1800 GetManager<ILocalMatrixComponentManager>(ecs)->Create(entity);
1801 GetManager<IWorldMatrixComponentManager>(ecs)->Create(entity);
1802
1803 ScopedHandle<TransformComponent> component = transformManager.Write(entity);
1804 if (node.usesTRS) {
1805 component->position = node.translation;
1806 component->rotation = node.rotation;
1807 component->scale = node.scale;
1808 } else {
1809 Math::Vec3 skew;
1810 Math::Vec4 perspective;
1811
1812 if (!Math::Decompose(
1813 node.matrix, component->scale, component->rotation, component->position, skew, perspective)) {
1814 component->position = { 0.f, 0.f, 0.f };
1815 component->rotation = { 0.f, 0.f, 0.f, 1.f };
1816 component->scale = { 1.f, 1.f, 1.f };
1817 }
1818 }
1819 }
1820
CreateMesh(IEcs & ecs,const GLTF2::Node & node,const Entity entity,const GLTF2::Data & data,const GLTFResourceData & gltfResourceData)1821 void CreateMesh(IEcs& ecs, const GLTF2::Node& node, const Entity entity, const GLTF2::Data& data,
1822 const GLTFResourceData& gltfResourceData)
1823 {
1824 const size_t meshIndex = FindIndex(data.meshes, node.mesh);
1825 if (meshIndex != GLTF2::GLTF_INVALID_INDEX && meshIndex < gltfResourceData.meshes.size()) {
1826 IRenderMeshComponentManager& renderMeshManager = *(GetManager<IRenderMeshComponentManager>(ecs));
1827
1828 renderMeshManager.Create(entity);
1829 ScopedHandle<RenderMeshComponent> component = renderMeshManager.Write(entity);
1830 component->mesh = gltfResourceData.meshes[meshIndex];
1831 }
1832 }
1833
CreateCamera(IEcs & ecs,const GLTF2::Node & node,const Entity entity,const Entity environmentEntity)1834 void CreateCamera(IEcs& ecs, const GLTF2::Node& node, const Entity entity, const Entity environmentEntity)
1835 {
1836 if (node.camera && node.camera->type != GLTF2::CameraType::INVALID) {
1837 ICameraComponentManager& cameraManager = *(GetManager<ICameraComponentManager>(ecs));
1838 cameraManager.Create(entity);
1839 ScopedHandle<CameraComponent> component = cameraManager.Write(entity);
1840 if (node.camera->type == GLTF2::CameraType::ORTHOGRAPHIC) {
1841 component->projection = CameraComponent::Projection::ORTHOGRAPHIC;
1842 component->xMag = node.camera->attributes.ortho.xmag;
1843 component->yMag = node.camera->attributes.ortho.ymag;
1844 component->zFar = node.camera->attributes.ortho.zfar;
1845 component->zNear = node.camera->attributes.ortho.znear;
1846 } else {
1847 // GLTF2::CameraType::PERSPECTIVE
1848 component->projection = CameraComponent::Projection::PERSPECTIVE;
1849 component->aspect = node.camera->attributes.perspective.aspect;
1850 component->yFov = node.camera->attributes.perspective.yfov;
1851 component->zFar = node.camera->attributes.perspective.zfar;
1852 component->zNear = node.camera->attributes.perspective.znear;
1853 }
1854 component->environment = environmentEntity;
1855 }
1856 }
1857
1858 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
CreateLight(IEcs & ecs,const GLTF2::Node & node,const Entity entity)1859 void CreateLight(IEcs& ecs, const GLTF2::Node& node, const Entity entity)
1860 {
1861 if (node.light && node.light->type != GLTF2::LightType::INVALID) {
1862 ILightComponentManager& lightManager = *(GetManager<ILightComponentManager>(ecs));
1863
1864 LightComponent component = CreateComponent(lightManager, entity);
1865
1866 component.type = ConvertToCoreLightType(node.light->type);
1867 component.intensity = node.light->intensity;
1868 component.color = { node.light->color.x, node.light->color.y, node.light->color.z };
1869
1870 if (component.type == LightComponent::Type::POINT || component.type == LightComponent::Type::SPOT) {
1871 // Positional parameters.
1872 component.range = node.light->positional.range;
1873
1874 if (component.type == LightComponent::Type::SPOT) {
1875 // Spot parameters.
1876 component.spotInnerAngle = node.light->positional.spot.innerAngle;
1877 component.spotOuterAngle = node.light->positional.spot.outerAngle;
1878 }
1879 }
1880
1881 lightManager.Set(entity, component);
1882 }
1883 }
1884 #endif
1885
RecursivelyCreateComponents(IEcs & ecs,const GLTF2::Data & data,const GLTF2::Node & node,const unordered_map<size_t,Entity> & sceneEntities,const Entity sceneEntity,const uint32_t sceneId,const Entity environmentEntity,const GLTFResourceData & gltfResourceData,Entity & defaultCamera,uint32_t flags)1886 void RecursivelyCreateComponents(IEcs& ecs, const GLTF2::Data& data, const GLTF2::Node& node,
1887 const unordered_map<size_t, Entity>& sceneEntities, const Entity sceneEntity, const uint32_t sceneId,
1888 const Entity environmentEntity, const GLTFResourceData& gltfResourceData, Entity& defaultCamera, uint32_t flags)
1889 {
1890 const size_t nodeIndex = FindIndex(data.nodes, &node);
1891 CORE_ASSERT_MSG(nodeIndex != GLTF2::GLTF_INVALID_INDEX, "Cannot find node: %s", node.name.c_str());
1892
1893 const Entity entity = FindEntity(sceneEntities, nodeIndex);
1894
1895 // Apply to node hierarchy.
1896 CreateNode(ecs, node, entity, data, sceneEntities, sceneEntity, sceneId);
1897
1898 // Add name.
1899 CreateName(ecs, node, entity);
1900
1901 // Apply transform.
1902 CreateTransform(ecs, node, entity);
1903
1904 // Apply mesh.
1905 if (node.mesh && (flags & CORE_GLTF_IMPORT_COMPONENT_MESH)) {
1906 CreateMesh(ecs, node, entity, data, gltfResourceData);
1907 }
1908
1909 // Apply camera.
1910 if (flags & CORE_GLTF_IMPORT_COMPONENT_CAMERA) {
1911 CreateCamera(ecs, node, entity, environmentEntity);
1912
1913 if (!EntityUtil::IsValid(defaultCamera)) {
1914 defaultCamera = entity;
1915 }
1916 }
1917
1918 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
1919 // Apply light.
1920 if (flags & CORE_GLTF_IMPORT_COMPONENT_LIGHT) {
1921 CreateLight(ecs, node, entity);
1922 }
1923 #endif
1924
1925 #if defined(GLTF2_EXTRAS_RSDZ)
1926 if (!node.modelIdRSDZ.empty()) {
1927 IRSDZModelIdComponentManager& rsdzManager = *(GetManager<IRSDZModelIdComponentManager>(ecs));
1928 RSDZModelIdComponent component = CreateComponent(rsdzManager, entity);
1929 StringUtil::CopyStringToArray(
1930 node.modelIdRSDZ, component.modelId, StringUtil::MaxStringLengthFromArray(component.modelId));
1931 rsdzManager.Set(entity, component);
1932 }
1933 #endif
1934
1935 for (auto child : node.children) {
1936 RecursivelyCreateComponents(ecs, data, *child, sceneEntities, sceneEntity, sceneId, environmentEntity,
1937 gltfResourceData, defaultCamera, flags);
1938 }
1939 }
1940
AddSkinJointsComponents(const GLTF2::Data & data,const GLTFResourceData & gltfResourceData,ISkinningSystem & ss,const unordered_map<size_t,Entity> & sceneEntities)1941 void AddSkinJointsComponents(const GLTF2::Data& data, const GLTFResourceData& gltfResourceData, ISkinningSystem& ss,
1942 const unordered_map<size_t, Entity>& sceneEntities)
1943 {
1944 auto skinEntityIt = gltfResourceData.skins.begin();
1945 auto skinJointsManager = GetManager<ISkinJointsComponentManager>(ss.GetECS());
1946 // gltfResourceData.skins and data.skins should be the same size but take min just in case.
1947 auto skins = array_view(data.skins.data(), Math::min(gltfResourceData.skins.size(), data.skins.size()));
1948 for (auto const& skin : skins) {
1949 if (skin && (*skinEntityIt)) {
1950 skinJointsManager->Create(*skinEntityIt);
1951 auto jointsHandle = skinJointsManager->Write(*skinEntityIt);
1952 jointsHandle->count = Math::min(countof(jointsHandle->jointEntities), skin->joints.size());
1953 auto jointEntities = array_view(jointsHandle->jointEntities, jointsHandle->count);
1954 std::transform(skin->joints.begin(), skin->joints.begin() + static_cast<ptrdiff_t>(jointsHandle->count),
1955 jointEntities.begin(), [&data, &sceneEntities](const auto& jointNode) {
1956 size_t const jointNodeIndex = FindIndex(data.nodes, jointNode);
1957 return FindEntity(sceneEntities, jointNodeIndex);
1958 });
1959 }
1960 ++skinEntityIt;
1961 }
1962 }
1963
CreateSkinComponents(const GLTF2::Data & data,const GLTFResourceData & gltfResourceData,ISkinningSystem & ss,const unordered_map<size_t,Entity> & sceneEntities)1964 void CreateSkinComponents(const GLTF2::Data& data, const GLTFResourceData& gltfResourceData, ISkinningSystem& ss,
1965 const unordered_map<size_t, Entity>& sceneEntities)
1966 {
1967 auto skinJointsManager = GetManager<ISkinJointsComponentManager>(ss.GetECS());
1968 for (auto const& node : data.nodes) {
1969 if (!node->skin) {
1970 continue;
1971 }
1972
1973 if (size_t const skinIndex = FindIndex(data.skins, node->skin);
1974 skinIndex != GLTF2::GLTF_INVALID_INDEX && skinIndex < gltfResourceData.skins.size()) {
1975 size_t const nodeIndex = FindIndex(data.nodes, node.get());
1976 Entity const entity = FindEntity(sceneEntities, nodeIndex);
1977 if (!EntityUtil::IsValid(entity)) {
1978 // node was not part of current scene
1979 continue;
1980 }
1981
1982 Entity skeleton {};
1983 if (node->skin->skeleton) {
1984 size_t const skeletonIndex = FindIndex(data.nodes, node->skin->skeleton);
1985 skeleton = FindEntity(sceneEntities, skeletonIndex);
1986 }
1987
1988 vector<Entity> joints;
1989 if (auto jointsHandle = skinJointsManager->Read(gltfResourceData.skins[skinIndex]); jointsHandle) {
1990 joints.append(jointsHandle->jointEntities, jointsHandle->jointEntities + jointsHandle->count);
1991 } else {
1992 joints.reserve(node->skin->joints.size());
1993 std::transform(node->skin->joints.begin(), node->skin->joints.end(), std::back_inserter(joints),
1994 [&data, &sceneEntities](const auto& jointNode) {
1995 size_t const jointNodeIndex = FindIndex(data.nodes, jointNode);
1996 return FindEntity(sceneEntities, jointNodeIndex);
1997 });
1998 }
1999 ss.CreateInstance(gltfResourceData.skins[skinIndex], joints, entity, skeleton);
2000 }
2001 }
2002 }
2003
CreateMorphComponents(const GLTF2::Data & data,const GLTFResourceData & gltfResourceData,IMorphComponentManager & mm,const unordered_map<size_t,Entity> & sceneEntities,const Entity sceneEntity)2004 void CreateMorphComponents(const GLTF2::Data& data, const GLTFResourceData& gltfResourceData,
2005 IMorphComponentManager& mm, const unordered_map<size_t, Entity>& sceneEntities, const Entity sceneEntity)
2006 {
2007 for (auto const& node : data.nodes) {
2008 if (!node->mesh || node->mesh->primitives.empty() || node->mesh->primitives[0].targets.empty()) {
2009 continue;
2010 }
2011 const size_t meshIndex = FindIndex(data.meshes, node->mesh);
2012 if ((meshIndex == GLTF2::GLTF_INVALID_INDEX) || (meshIndex >= gltfResourceData.meshes.size())) {
2013 continue;
2014 }
2015 const size_t nodeIndex = FindIndex(data.nodes, node.get());
2016 const Entity entity = FindEntity(sceneEntities, nodeIndex);
2017 if (!EntityUtil::IsValid(entity)) {
2018 // node was not part of current scene
2019 continue;
2020 }
2021 const size_t weightCount = node->mesh->primitives[0].targets.size();
2022 // Assert that all primitives have the same targets. (the spec is a bit confusing here or i just
2023 // can't read.)
2024 for (const auto& primitive : node->mesh->primitives) {
2025 CORE_UNUSED(primitive);
2026 CORE_ASSERT(primitive.targets.size() == weightCount);
2027 }
2028
2029 // We have targets, so prepare the morph system/component.
2030 vector<string> names;
2031 names.reserve(weightCount);
2032 std::transform(node->mesh->primitives[0].targets.cbegin(), node->mesh->primitives[0].targets.cend(),
2033 std::back_inserter(names), [](const GLTF2::MorphTarget& target) { return target.name; });
2034
2035 // Apparently the node/mesh weight list is a concatenation of all primitives targets weights....
2036 vector<float> weights;
2037 weights.reserve(weightCount);
2038 if (!node->weights.empty()) {
2039 // Use instance weight (if there is one)
2040 weights.append(node->weights.cbegin(),
2041 node->weights.cbegin() + static_cast<decltype(node->weights)::difference_type>(weightCount));
2042 } else if (!node->mesh->weights.empty()) {
2043 // Use mesh weight (if there is one)
2044 weights.append(node->mesh->weights.cbegin(),
2045 node->mesh->weights.cbegin() + static_cast<decltype(node->weights)::difference_type>(weightCount));
2046 } else {
2047 // Default to zero weight.
2048 weights.append(weightCount, 0.f);
2049 }
2050
2051 mm.Create(entity);
2052 auto handle = mm.Write(entity);
2053 handle->morphNames = BASE_NS::move(names);
2054 handle->morphWeights = BASE_NS::move(weights);
2055 }
2056 }
2057
GetImageEntity(IEcs & ecs,const GLTFResourceData & gltfResourceData,size_t index)2058 EntityReference GetImageEntity(IEcs& ecs, const GLTFResourceData& gltfResourceData, size_t index)
2059 {
2060 if ((index != CORE_GLTF_INVALID_INDEX) && (index < gltfResourceData.images.size()) &&
2061 GetManager<IRenderHandleComponentManager>(ecs)->HasComponent(gltfResourceData.images[index])) {
2062 return gltfResourceData.images[index];
2063 }
2064 return {};
2065 }
2066
CreateEnvironmentComponent(IGpuResourceManager & gpuResourceManager,const GLTF2::Data & data,const GLTFResourceData & gltfResourceData,IEcs & ecs,Entity sceneEntity,const size_t lightIndex)2067 Entity CreateEnvironmentComponent(IGpuResourceManager& gpuResourceManager, const GLTF2::Data& data,
2068 const GLTFResourceData& gltfResourceData, IEcs& ecs, Entity sceneEntity, const size_t lightIndex)
2069 {
2070 Entity environmentEntity;
2071 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
2072 if (lightIndex == GLTF2::GLTF_INVALID_INDEX || lightIndex >= data.imageBasedLights.size()) {
2073 return environmentEntity;
2074 }
2075 // Create the component to the sceneEntity for convinience.
2076 environmentEntity = sceneEntity;
2077 IEnvironmentComponentManager& envManager = *(GetManager<IEnvironmentComponentManager>(ecs));
2078 envManager.Create(environmentEntity);
2079 auto envHandle = envManager.Write(environmentEntity);
2080
2081 GLTF2::ImageBasedLight* light = data.imageBasedLights[lightIndex].get();
2082 envHandle->indirectDiffuseFactor.w = light->intensity;
2083 envHandle->indirectSpecularFactor.w = light->intensity;
2084 envHandle->envMapFactor.w = light->intensity;
2085
2086 envHandle->environmentRotation = light->rotation;
2087
2088 // Either we have full set of coefficients or there are no coefficients at all.
2089 CORE_ASSERT(light->irradianceCoefficients.size() == 0u || light->irradianceCoefficients.size() == 9u);
2090
2091 std::transform(light->irradianceCoefficients.begin(), light->irradianceCoefficients.end(),
2092 envHandle->irradianceCoefficients, [](const GLTF2::ImageBasedLight::LightingCoeff& coeff) {
2093 // Each coefficient needs to have three components.
2094 CORE_ASSERT(coeff.size() == 3u);
2095
2096 // Values are expected to be prescaled with 1.0 / PI for Lambertian diffuse
2097 return Math::Vec3(coeff[0u], coeff[1u], coeff[2u]);
2098 });
2099
2100 if (lightIndex < gltfResourceData.specularRadianceCubemaps.size() &&
2101 EntityUtil::IsValid(gltfResourceData.specularRadianceCubemaps[lightIndex])) {
2102 envHandle->radianceCubemap = gltfResourceData.specularRadianceCubemaps[lightIndex];
2103 envHandle->radianceCubemapMipCount = (uint32_t)light->specularImages.size();
2104 }
2105
2106 if (auto imageEntity = GetImageEntity(ecs, gltfResourceData, light->specularCubeImage); imageEntity) {
2107 envHandle->radianceCubemap = imageEntity;
2108 }
2109
2110 envHandle->envMapLodLevel = light->skymapImageLodLevel;
2111
2112 if (auto imageEntity = GetImageEntity(ecs, gltfResourceData, light->skymapImage); imageEntity) {
2113 envHandle->envMap = imageEntity;
2114 IRenderHandleComponentManager& gpuHandleManager = *(GetManager<IRenderHandleComponentManager>(ecs));
2115 if (const auto& envMapHandle = gpuHandleManager.Read(imageEntity)->reference; envMapHandle) {
2116 const ImageViewType imageViewType = gpuResourceManager.GetImageDescriptor(envMapHandle).imageViewType;
2117 envHandle->background = (imageViewType == CORE_IMAGE_VIEW_TYPE_CUBE)
2118 ? EnvironmentComponent::Background::CUBEMAP
2119 : EnvironmentComponent::Background::EQUIRECTANGULAR;
2120 }
2121 }
2122
2123 #endif
2124 return environmentEntity;
2125 }
2126
operator +(GLTF2::ImportPhase lhs,int rhs)2127 GLTF2::ImportPhase operator+(GLTF2::ImportPhase lhs, int rhs)
2128 {
2129 return static_cast<GLTF2::ImportPhase>(static_cast<int>(lhs) + rhs);
2130 }
2131
2132 using ImageLoadResultVector = vector<IImageLoaderManager::LoadResult>;
2133
2134 struct ImageData {
2135 vector<uint8_t> data;
2136 vector<BufferImageCopy> copyInfo;
2137 };
2138
PrepareImageData(const ImageLoadResultVector & imageLoadResults,const GpuImageDesc & imageDesc)2139 ImageData PrepareImageData(const ImageLoadResultVector& imageLoadResults, const GpuImageDesc& imageDesc)
2140 {
2141 ImageData data;
2142 const size_t availableMipLayerCount = imageLoadResults.size() / imageDesc.layerCount;
2143 data.copyInfo.resize(availableMipLayerCount);
2144
2145 // For all mip levels.
2146 size_t byteOffset = 0;
2147 for (size_t mipIndex = 0; mipIndex < availableMipLayerCount; ++mipIndex) {
2148 {
2149 const auto& imageLoadResult = imageLoadResults[(mipIndex * 6u)];
2150 if (!imageLoadResult.image) {
2151 continue;
2152 }
2153 const auto& loadedImageDesc = imageLoadResult.image->GetImageDesc();
2154 const auto& loadedBufferImageCopy = imageLoadResult.image->GetBufferImageCopies()[0];
2155
2156 BufferImageCopy& bufferCopy = data.copyInfo[mipIndex];
2157 bufferCopy.bufferOffset = (uint32_t)byteOffset;
2158 bufferCopy.bufferRowLength = loadedBufferImageCopy.bufferRowLength;
2159 bufferCopy.bufferImageHeight = loadedBufferImageCopy.bufferImageHeight;
2160 bufferCopy.imageOffset.width = 0;
2161 bufferCopy.imageOffset.height = 0;
2162 bufferCopy.imageOffset.depth = 0;
2163 bufferCopy.imageExtent.width = loadedImageDesc.width;
2164 bufferCopy.imageExtent.height = loadedImageDesc.height;
2165 bufferCopy.imageExtent.depth = loadedImageDesc.depth;
2166 bufferCopy.imageSubresource.imageAspectFlags = CORE_IMAGE_ASPECT_COLOR_BIT;
2167 bufferCopy.imageSubresource.mipLevel = (uint32_t)mipIndex;
2168 bufferCopy.imageSubresource.baseArrayLayer = 0;
2169 bufferCopy.imageSubresource.layerCount = imageDesc.layerCount;
2170 }
2171
2172 for (uint32_t face = 0; face < imageDesc.layerCount; ++face) {
2173 const auto& cubeFaceLoadResult = imageLoadResults[(mipIndex * imageDesc.layerCount) + face];
2174 const auto& cubeFaceBufferImageCopy = cubeFaceLoadResult.image->GetBufferImageCopies()[0];
2175
2176 const auto& loadedImageData = cubeFaceLoadResult.image->GetData();
2177 data.data.append(loadedImageData.begin() + cubeFaceBufferImageCopy.bufferOffset, loadedImageData.end());
2178
2179 byteOffset += loadedImageData.size() - cubeFaceBufferImageCopy.bufferOffset;
2180 }
2181 }
2182 return data;
2183 }
2184
CreateCubemapFromImages(uint32_t imageSize,const ImageLoadResultVector & imageLoadResults,IGpuResourceManager & gpuResourceManager)2185 RenderHandleReference CreateCubemapFromImages(
2186 uint32_t imageSize, const ImageLoadResultVector& imageLoadResults, IGpuResourceManager& gpuResourceManager)
2187 {
2188 if (!imageLoadResults[0].image) {
2189 return {};
2190 }
2191
2192 // Calculate number of mipmaps needed.
2193 uint32_t mipsize = imageSize;
2194 uint32_t totalMipCount = 0;
2195 while (mipsize > 0) {
2196 ++totalMipCount;
2197 mipsize >>= 1;
2198 }
2199
2200 const ImageUsageFlags usageFlags =
2201 CORE_IMAGE_USAGE_SAMPLED_BIT | CORE_IMAGE_USAGE_TRANSFER_DST_BIT | CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
2202
2203 const GpuImageDesc imageDesc = {
2204 ImageType::CORE_IMAGE_TYPE_2D, // imageType
2205 CORE_IMAGE_VIEW_TYPE_CUBE, // imageViewType
2206 imageLoadResults[0].image->GetImageDesc().format, // format
2207 ImageTiling::CORE_IMAGE_TILING_OPTIMAL, // imageTiling
2208 usageFlags, // usageFlags
2209 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, // memoryPropertyFlags
2210 ImageCreateFlagBits::CORE_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, // createFlags
2211 CORE_ENGINE_IMAGE_CREATION_GENERATE_MIPS, // engineCreationFlags
2212 imageSize, // width
2213 imageSize, // height
2214 1, // depth
2215 totalMipCount, // mipCount
2216 6, // layerCount
2217 SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT, // sampleCountFlags
2218 {}, // componentMapping
2219 };
2220
2221 const auto data = PrepareImageData(imageLoadResults, imageDesc);
2222
2223 auto handle = gpuResourceManager.Create("", imageDesc, data.data, data.copyInfo);
2224 if (!handle) {
2225 CORE_LOG_W("Creating an import cubemap image failed (format:%u, width:%u, height:%u)",
2226 static_cast<uint32_t>(imageDesc.format), imageDesc.width, imageDesc.height);
2227 }
2228 return handle;
2229 }
2230
FillShaderData(IEntityManager & em,IUriComponentManager & uriManager,IRenderHandleComponentManager & renderHandleMgr,const string_view renderSlot,GLTF2::GLTF2Importer::DefaultMaterialShaderData::SingleShaderData & shaderData)2231 auto FillShaderData(IEntityManager& em, IUriComponentManager& uriManager,
2232 IRenderHandleComponentManager& renderHandleMgr, const string_view renderSlot,
2233 GLTF2::GLTF2Importer::DefaultMaterialShaderData::SingleShaderData& shaderData)
2234 {
2235 auto uri = "3dshaders://" + renderSlot;
2236 auto resourceEntity = LookupResourceByUri(uri, uriManager, renderHandleMgr);
2237 shaderData.shader = em.GetReferenceCounted(resourceEntity);
2238
2239 uri = "3dshaderstates://";
2240 uri += renderSlot;
2241 resourceEntity = LookupResourceByUri(uri, uriManager, renderHandleMgr);
2242 shaderData.gfxState = em.GetReferenceCounted(resourceEntity);
2243
2244 uri += "_DBL";
2245 resourceEntity = LookupResourceByUri(uri, uriManager, renderHandleMgr);
2246 shaderData.gfxStateDoubleSided = em.GetReferenceCounted(resourceEntity);
2247 if (!(EntityUtil::IsValid(shaderData.shader) && EntityUtil::IsValid(shaderData.gfxState) &&
2248 EntityUtil::IsValid(shaderData.gfxStateDoubleSided))) {
2249 CORE_LOG_ONCE_W(renderSlot, "GLTF2 importer cannot find default shader data.");
2250 }
2251 }
2252 } // namespace
2253
ImportScene(IDevice & device,size_t sceneIndex,const GLTF2::Data & data,const GLTFResourceData & gltfResourceData,IEcs & ecs,Entity rootEntity,uint32_t sceneId,GltfSceneImportFlags flags)2254 Entity ImportScene(IDevice& device, size_t sceneIndex, const GLTF2::Data& data,
2255 const GLTFResourceData& gltfResourceData, IEcs& ecs, Entity rootEntity, uint32_t sceneId,
2256 GltfSceneImportFlags flags)
2257 {
2258 if (sceneIndex >= data.scenes.size()) {
2259 return {};
2260 }
2261 // Create entity for this scene.
2262 const Entity sceneEntity = ecs.GetEntityManager().Create();
2263
2264 // Create default components for scene.
2265 ITransformComponentManager& transformManager = *(GetManager<ITransformComponentManager>(ecs));
2266 transformManager.Create(sceneEntity);
2267 ILocalMatrixComponentManager& localMatrixManager = *(GetManager<ILocalMatrixComponentManager>(ecs));
2268 localMatrixManager.Create(sceneEntity);
2269 IWorldMatrixComponentManager& worldMatrixManager = *(GetManager<IWorldMatrixComponentManager>(ecs));
2270 worldMatrixManager.Create(sceneEntity);
2271
2272 auto& scene = data.scenes[sceneIndex];
2273
2274 INodeComponentManager& nodeManager = *(GetManager<INodeComponentManager>(ecs));
2275 nodeManager.Create(sceneEntity);
2276
2277 // IGltf2::ImportGltfScene has two overloads: either rootEntity or sceneId is given. If rootEntity is valid use the
2278 // sceneId from that node.
2279 if (EntityUtil::IsValid(rootEntity)) {
2280 if (auto nodeHandle = nodeManager.Read(rootEntity)) {
2281 sceneId = nodeHandle->sceneId;
2282 }
2283 }
2284
2285 if (auto nodeHandle = nodeManager.Write(sceneEntity)) {
2286 nodeHandle->parent = rootEntity;
2287 nodeHandle->sceneId = sceneId;
2288 }
2289
2290 // Add name.
2291 {
2292 INameComponentManager& nameManager = *(GetManager<INameComponentManager>(ecs));
2293 nameManager.Create(sceneEntity);
2294 ScopedHandle<NameComponent> nameHandle = nameManager.Write(sceneEntity);
2295 nameHandle->name = scene->name;
2296 }
2297
2298 // Default camera.
2299 Entity defaultCamera;
2300
2301 IEntityManager& em = ecs.GetEntityManager();
2302
2303 // Create environment.
2304 Entity environment;
2305 if (flags & CORE_GLTF_IMPORT_COMPONENT_ENVIRONMENT) {
2306 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
2307 const size_t lightIndex = scene->imageBasedLightIndex;
2308 #else
2309 const size_t lightIndex = 0; // Not used.
2310 #endif
2311 environment = CreateEnvironmentComponent(
2312 device.GetGpuResourceManager(), data, gltfResourceData, ecs, sceneEntity, lightIndex);
2313 }
2314
2315 // Create entities for nodes in scene.
2316 unordered_map<size_t, Entity> sceneEntities;
2317 for (auto node : scene->nodes) {
2318 RecursivelyCreateEntities(em, data, *node, sceneEntities);
2319 }
2320
2321 // Create components for all nodes in this scene.
2322 for (auto node : scene->nodes) {
2323 RecursivelyCreateComponents(
2324 ecs, data, *node, sceneEntities, sceneEntity, sceneId, environment, gltfResourceData, defaultCamera, flags);
2325 }
2326
2327 // Apply skins only after the node hiearachy is complete.
2328 if (flags & CORE_GLTF_IMPORT_COMPONENT_SKIN) {
2329 if (ISkinningSystem* ss = GetSystem<ISkinningSystem>(ecs); ss) {
2330 AddSkinJointsComponents(data, gltfResourceData, *ss, sceneEntities);
2331 CreateSkinComponents(data, gltfResourceData, *ss, sceneEntities);
2332 }
2333 }
2334
2335 // Add the morphing system to entity if needed. (contains the target weights)
2336 if (flags & CORE_GLTF_IMPORT_COMPONENT_MORPH) {
2337 if (IMorphComponentManager* mm = GetManager<IMorphComponentManager>(ecs); mm) {
2338 CreateMorphComponents(data, gltfResourceData, *mm, sceneEntities, sceneEntity);
2339 }
2340 }
2341
2342 return sceneEntity;
2343 }
2344
2345 namespace GLTF2 {
2346 struct GLTF2Importer::ImporterTask {
2347 virtual ~ImporterTask() = default;
2348
2349 enum class State { Queued, Gather, Import, Finished };
2350
2351 string name;
2352 string errors;
2353 uint64_t id;
2354 bool success { true };
2355
2356 std::function<bool()> gather;
2357 std::function<bool()> import;
2358 std::function<void()> finished;
2359
2360 State state { State::Queued };
2361 ImportPhase phase { ImportPhase::FINISHED };
2362 };
2363
2364 class GLTF2Importer::GatherThreadTask final : public IThreadPool::ITask {
2365 public:
GatherThreadTask(GLTF2Importer & importer,ImporterTask & task)2366 explicit GatherThreadTask(GLTF2Importer& importer, ImporterTask& task) : importer_(importer), task_(task) {};
2367
operator ()()2368 void operator()() override
2369 {
2370 importer_.Gather(task_);
2371 }
2372
2373 protected:
Destroy()2374 void Destroy() override
2375 {
2376 delete this;
2377 }
2378
2379 private:
2380 GLTF2Importer& importer_;
2381 ImporterTask& task_;
2382 };
2383
2384 class GLTF2Importer::ImportThreadTask final : public IThreadPool::ITask {
2385 public:
ImportThreadTask(GLTF2Importer & importer,ImporterTask & task)2386 explicit ImportThreadTask(GLTF2Importer& importer, ImporterTask& task) : importer_(importer), task_(task) {};
2387
operator ()()2388 void operator()() override
2389 {
2390 importer_.Import(task_);
2391 }
2392
2393 protected:
Destroy()2394 void Destroy() override
2395 {
2396 delete this;
2397 }
2398
2399 private:
2400 GLTF2Importer& importer_;
2401 ImporterTask& task_;
2402 };
2403
2404 template<class T>
2405 struct GLTF2Importer::GatheredDataTask : GLTF2Importer::ImporterTask {
2406 T data;
2407 };
2408
2409 template<typename Component>
2410 struct GLTF2Importer::ComponentTaskData {
2411 EntityReference entity;
2412 Component component;
2413 };
2414
2415 struct GLTF2Importer::AnimationTaskData {
2416 GLTF2Importer* importer;
2417 size_t index;
2418 string uri;
2419 string_view name;
2420 IAnimationComponentManager* animationManager;
2421 IAnimationTrackComponentManager* trackManager;
2422 vector<GatheredDataTask<ComponentTaskData<AnimationInputComponent>>*> inputs;
2423 vector<GatheredDataTask<ComponentTaskData<AnimationOutputComponent>>*> outputs;
2424
operator ()GLTF2::GLTF2Importer::AnimationTaskData2425 bool operator()()
2426 {
2427 const auto& tracks = importer->data_->animations[index]->tracks;
2428 if (inputs.size() < tracks.size() || outputs.size() < tracks.size()) {
2429 return false;
2430 }
2431 auto& entityManager = importer->ecs_->GetEntityManager();
2432
2433 auto animationEntity = entityManager.CreateReferenceCounted();
2434 animationManager->Create(animationEntity);
2435 const auto animationHandle = animationManager->Write(animationEntity);
2436
2437 auto& animationTracks = animationHandle->tracks;
2438 animationTracks.reserve(tracks.size());
2439 auto inputIt = inputs.begin();
2440 auto outputIt = outputs.begin();
2441 float maxLength = 0.f;
2442 for (const auto& track : tracks) {
2443 if (track.sampler) {
2444 const auto animationTrackEntity = entityManager.CreateReferenceCounted();
2445 animationTracks.push_back(animationTrackEntity);
2446
2447 trackManager->Create(animationTrackEntity);
2448 if (auto trackHandle = trackManager->Write(animationTrackEntity); trackHandle) {
2449 if (track.channel.node) {
2450 // Name the track with the path to the target node. Scene import can then find the
2451 // target entity.
2452 importer->nameManager_->Create(animationTrackEntity);
2453 importer->nameManager_->Write(animationTrackEntity)->name =
2454 ResolveNodePath(*track.channel.node);
2455 }
2456
2457 SelectComponentProperty(track.channel.path, *trackHandle);
2458
2459 trackHandle->interpolationMode = ConvertAnimationInterpolation(track.sampler->interpolation);
2460 trackHandle->timestamps = (*inputIt)->data.entity;
2461 trackHandle->data = (*outputIt)->data.entity;
2462 if (!(*inputIt)->data.component.timestamps.empty()) {
2463 maxLength = Math::max(maxLength, (*inputIt)->data.component.timestamps.back());
2464 }
2465 }
2466 ++inputIt;
2467 ++outputIt;
2468 }
2469 }
2470 animationHandle->duration = maxLength;
2471 if (!uri.empty()) {
2472 importer->uriManager_->Create(animationEntity);
2473 importer->uriManager_->Write(animationEntity)->uri = move(uri);
2474 }
2475 if (!name.empty()) {
2476 importer->nameManager_->Create(animationEntity);
2477 importer->nameManager_->Write(animationEntity)->name = name;
2478 }
2479 importer->result_.data.animations[index] = move(animationEntity);
2480 return true;
2481 }
2482
SelectComponentPropertyGLTF2::GLTF2Importer::AnimationTaskData2483 static void SelectComponentProperty(AnimationPath pathType, AnimationTrackComponent& trackComponent)
2484 {
2485 switch (pathType) {
2486 case GLTF2::AnimationPath::INVALID:
2487 CORE_ASSERT(false);
2488 break;
2489 case GLTF2::AnimationPath::TRANSLATION:
2490 trackComponent.component = ITransformComponentManager::UID;
2491 trackComponent.property = "position";
2492 break;
2493 case GLTF2::AnimationPath::ROTATION:
2494 trackComponent.component = ITransformComponentManager::UID;
2495 trackComponent.property = "rotation";
2496 break;
2497 case GLTF2::AnimationPath::SCALE:
2498 trackComponent.component = ITransformComponentManager::UID;
2499 trackComponent.property = "scale";
2500 break;
2501 case GLTF2::AnimationPath::WEIGHTS:
2502 trackComponent.component = IMorphComponentManager::UID;
2503 trackComponent.property = "morphWeights";
2504 break;
2505 case GLTF2::AnimationPath::VISIBLE:
2506 trackComponent.component = INodeComponentManager::UID;
2507 trackComponent.property = "enabled";
2508 break;
2509 case GLTF2::AnimationPath::OPACITY:
2510 trackComponent.component = IMaterialComponentManager::UID;
2511 trackComponent.property = "enabled";
2512 break;
2513 }
2514 }
2515 };
2516
GLTF2Importer(IEngine & engine,IRenderContext & renderContext,IEcs & ecs,IThreadPool & pool)2517 GLTF2Importer::GLTF2Importer(IEngine& engine, IRenderContext& renderContext, IEcs& ecs, IThreadPool& pool)
2518 : engine_(engine), renderContext_(renderContext), device_(renderContext.GetDevice()),
2519 gpuResourceManager_(device_.GetGpuResourceManager()), ecs_(&ecs),
2520 renderHandleManager_(GetManager<IRenderHandleComponentManager>(*ecs_)),
2521 materialManager_(GetManager<IMaterialComponentManager>(*ecs_)),
2522 meshManager_(GetManager<IMeshComponentManager>(*ecs_)), nameManager_(GetManager<INameComponentManager>(*ecs_)),
2523 uriManager_(GetManager<IUriComponentManager>(*ecs_)), threadPool_(&pool)
2524 {
2525 if (IGraphicsContext* graphicsContext_ =
2526 GetInstance<IGraphicsContext>(*(renderContext_.GetInterface<IClassRegister>()), UID_GRAPHICS_CONTEXT);
2527 graphicsContext_) {
2528 colorSpaceFlags_ = graphicsContext_->GetCreateInfo().colorSpaceFlags;
2529 }
2530
2531 const auto factory = GetInstance<ITaskQueueFactory>(UID_TASK_QUEUE_FACTORY);
2532 mainThreadQueue_ = factory->CreateDispatcherTaskQueue({});
2533 if (renderHandleManager_ && uriManager_) {
2534 // fetch default shaders and graphics states
2535 auto& entityMgr = ecs_->GetEntityManager();
2536 FillShaderData(entityMgr, *uriManager_, *renderHandleManager_,
2537 DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE, dmShaderData_.opaque);
2538 FillShaderData(entityMgr, *uriManager_, *renderHandleManager_,
2539 DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT, dmShaderData_.blend);
2540 FillShaderData(entityMgr, *uriManager_, *renderHandleManager_,
2541 DefaultMaterialShaderConstants::RENDER_SLOT_DEPTH, dmShaderData_.depth);
2542 }
2543 }
2544
GLTF2Importer(IEngine & engine,IRenderContext & renderContext,IEcs & ecs)2545 GLTF2Importer::GLTF2Importer(IEngine& engine, IRenderContext& renderContext, IEcs& ecs)
2546 : GLTF2Importer(engine, renderContext, ecs,
2547 *GetInstance<ITaskQueueFactory>(UID_TASK_QUEUE_FACTORY)->CreateThreadPool(IMPORTER_THREADS))
2548 {}
2549
~GLTF2Importer()2550 GLTF2Importer::~GLTF2Importer()
2551 {
2552 Cancel();
2553 }
2554
ImportGLTF(const IGLTFData & data,GltfResourceImportFlags flags)2555 void GLTF2Importer::ImportGLTF(const IGLTFData& data, GltfResourceImportFlags flags)
2556 {
2557 Cancel();
2558 cancelled_ = false;
2559
2560 if (!renderHandleManager_ || !materialManager_ || !meshManager_ || !nameManager_ || !uriManager_) {
2561 result_.success = false;
2562 result_.error += "Missing required component managers.";
2563 return;
2564 }
2565 data_ = (const GLTF2::Data*)&data;
2566 if (!data_) {
2567 result_.success = false;
2568 result_.error += "Invalid IGLTFData to ImportGLTF.";
2569 return;
2570 }
2571
2572 flags_ = flags;
2573 for (auto& pendingGatherTasks : pendingGatherTasks_) {
2574 pendingGatherTasks = 0U;
2575 }
2576 for (auto& finishedGatherTasks : finishedGatherTasks_) {
2577 finishedGatherTasks.clear();
2578 }
2579 result_.data.samplers.clear();
2580 result_.data.images.clear();
2581 result_.data.textures.clear();
2582 result_.data.materials.clear();
2583 result_.data.meshes.clear();
2584 result_.data.skins.clear();
2585 result_.data.animations.clear();
2586 result_.data.specularRadianceCubemaps.clear();
2587
2588 meshBuilders_.clear();
2589
2590 // Build tasks.
2591 Prepare();
2592
2593 // Fill in task queues for first tasks.
2594 StartPhase(ImportPhase::BUFFERS);
2595
2596 // Run until completed.
2597 while (!Execute(0)) {
2598 if (gatherResults_[static_cast<uint32_t>(phase_)]) {
2599 gatherResults_[static_cast<uint32_t>(phase_)]->Wait();
2600 }
2601 }
2602 }
2603
ImportGLTFAsync(const IGLTFData & data,GltfResourceImportFlags flags,Listener * listener)2604 void GLTF2Importer::ImportGLTFAsync(const IGLTFData& data, GltfResourceImportFlags flags, Listener* listener)
2605 {
2606 Cancel();
2607 cancelled_ = false;
2608
2609 if (!renderHandleManager_ || !materialManager_ || !meshManager_ || !nameManager_ || !uriManager_) {
2610 result_.success = false;
2611 result_.error += "Missing required component managers.";
2612 return;
2613 }
2614
2615 data_ = (const GLTF2::Data*)&data;
2616 if (!data_) {
2617 result_.success = false;
2618 result_.error += "Invalid IGLTFData to ImportGLTFAsync.";
2619 return;
2620 }
2621
2622 flags_ = flags;
2623
2624 result_.success = true;
2625 result_.error.clear();
2626 result_.data.samplers.clear();
2627 result_.data.images.clear();
2628 result_.data.textures.clear();
2629 result_.data.materials.clear();
2630 result_.data.meshes.clear();
2631 result_.data.skins.clear();
2632 result_.data.animations.clear();
2633 result_.data.specularRadianceCubemaps.clear();
2634
2635 meshBuilders_.clear();
2636
2637 listener_ = listener;
2638
2639 // Build tasks.
2640 Prepare();
2641
2642 // Fill in task queues for first tasks.
2643 StartPhase(ImportPhase::BUFFERS);
2644 }
2645
Cancel()2646 void GLTF2Importer::Cancel()
2647 {
2648 if (!IsCompleted()) {
2649 cancelled_ = true;
2650 // Wait all ongoing threads to complete.
2651 for (auto& result : gatherResults_) {
2652 if (result) {
2653 result->Wait();
2654 }
2655 }
2656
2657 StartPhase(ImportPhase::FINISHED);
2658 }
2659 }
2660
IsCompleted() const2661 bool GLTF2Importer::IsCompleted() const
2662 {
2663 return phase_ == ImportPhase::FINISHED;
2664 }
2665
GetResult() const2666 const GLTFImportResult& GLTF2Importer::GetResult() const
2667 {
2668 return result_;
2669 }
2670
GetMeshData() const2671 const GltfMeshData& GLTF2Importer::GetMeshData() const
2672 {
2673 return meshData_;
2674 }
2675
IsValid() const2676 bool GLTF2Importer::IsValid() const
2677 {
2678 return uriManager_ && renderHandleManager_ && materialManager_ && meshManager_ && nameManager_ && uriManager_;
2679 }
2680
LaunchGatherTasks(size_t firstTask,ImportPhase phase)2681 void GLTF2Importer::LaunchGatherTasks(size_t firstTask, ImportPhase phase)
2682 {
2683 auto first = tasks_.begin() + static_cast<ptrdiff_t>(firstTask);
2684 const auto end = tasks_.end();
2685 const auto taskCount = static_cast<size_t>(std::distance(first, end));
2686 vector<const IThreadPool::ITask*> tasks;
2687 tasks.reserve(taskCount);
2688 while (first != end) {
2689 auto& task = **first;
2690 if (task.phase == phase) {
2691 task.state = ImporterTask::State::Gather;
2692 auto threadTask = IThreadPool::ITask::Ptr { new GatherThreadTask(*this, task) };
2693 tasks.push_back(threadTask.get());
2694 threadPool_->PushNoWait(BASE_NS::move(threadTask), BASE_NS::array_view(&bufferTask_, 1U));
2695 }
2696 ++first;
2697 }
2698 // Add a task which waits for all the gather tasks for this cateory.
2699 if (!tasks.empty()) {
2700 gatherResults_[static_cast<uint32_t>(phase)] =
2701 threadPool_->Push(IThreadPool::ITask::Ptr { CreateFunctionTask([]() {}) }, tasks);
2702 }
2703 }
2704
Prepare()2705 void GLTF2Importer::Prepare()
2706 {
2707 // Build tasks.
2708 PrepareBufferTasks();
2709
2710 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_SAMPLER) {
2711 PrepareSamplerTasks();
2712 }
2713
2714 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_IMAGE) {
2715 const auto firstTask = tasks_.size();
2716 PrepareImageTasks();
2717 PrepareImageBasedLightTasks();
2718 LaunchGatherTasks(firstTask, ImportPhase::IMAGES);
2719 }
2720
2721 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_MATERIAL) {
2722 PrepareMaterialTasks();
2723 }
2724
2725 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_ANIMATION) {
2726 const auto firstTask = tasks_.size();
2727 PrepareAnimationTasks();
2728 LaunchGatherTasks(firstTask, ImportPhase::ANIMATION_SAMPLERS);
2729 }
2730
2731 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_SKIN) {
2732 const auto firstTask = tasks_.size();
2733 PrepareSkinTasks();
2734 LaunchGatherTasks(firstTask, ImportPhase::SKINS);
2735 }
2736
2737 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_MESH) {
2738 const auto firstTask = tasks_.size();
2739 PrepareMeshTasks();
2740 LaunchGatherTasks(firstTask, ImportPhase::MESHES);
2741 }
2742
2743 if (listener_) {
2744 listener_->OnImportStarted();
2745 }
2746 }
2747
QueueTask(unique_ptr<ImporterTask> && task)2748 void GLTF2Importer::QueueTask(unique_ptr<ImporterTask>&& task)
2749 {
2750 size_t const taskIndex = tasks_.size();
2751 task->id = taskIndex;
2752 tasks_.push_back(move(task));
2753 }
2754
ProgressTask(ImporterTask & task)2755 bool GLTF2Importer::ProgressTask(ImporterTask& task)
2756 {
2757 if (task.state == ImporterTask::State::Queued) {
2758 // Task not started, proceed to gather phase.
2759 task.state = ImporterTask::State::Gather;
2760 if (task.gather) {
2761 // All gather task should be launched during Prepare() to fully utilize the thread pool.
2762 CORE_ASSERT(false);
2763 return false;
2764 }
2765 }
2766
2767 if (task.state == ImporterTask::State::Gather) {
2768 // Gather phase started or skipped, proceed to import.
2769 task.state = ImporterTask::State::Import;
2770 if (task.import) {
2771 pendingImportTasks_++;
2772 mainThreadQueue_->Submit(task.id, IThreadPool::ITask::Ptr { new ImportThreadTask(*this, task) });
2773 return false;
2774 }
2775 }
2776
2777 if (task.state == ImporterTask::State::Import) {
2778 CompleteTask(task);
2779 }
2780
2781 return task.state == ImporterTask::State::Finished;
2782 }
2783
Gather(ImporterTask & task)2784 void GLTF2Importer::Gather(ImporterTask& task)
2785 {
2786 {
2787 CORE_CPU_PERF_SCOPE("CORE3D", "Gather", task.name, CORE3D_PROFILER_DEFAULT_COLOR);
2788
2789 if (cancelled_ || !task.gather()) {
2790 task.success = false;
2791 }
2792 }
2793
2794 {
2795 // Mark task completed.
2796 std::lock_guard lock(gatherTasksLock_);
2797 finishedGatherTasks_[static_cast<uint32_t>(task.phase)].push_back(task.id);
2798 }
2799 }
2800
Import(ImporterTask & task)2801 void GLTF2Importer::Import(ImporterTask& task)
2802 {
2803 CORE_CPU_PERF_SCOPE("CORE3D", "Import", task.name, CORE3D_PROFILER_DEFAULT_COLOR);
2804
2805 if (cancelled_ || !task.import()) {
2806 task.success = false;
2807 }
2808 }
2809
CompleteTask(ImporterTask & task)2810 void GLTF2Importer::CompleteTask(ImporterTask& task)
2811 {
2812 if (task.finished) {
2813 task.finished();
2814 }
2815
2816 task.state = ImporterTask::State::Finished;
2817
2818 completedTasks_++;
2819
2820 if (listener_) {
2821 listener_->OnImportProgressed(completedTasks_, tasks_.size());
2822 }
2823 }
2824
StartPhase(ImportPhase phase)2825 void GLTF2Importer::StartPhase(ImportPhase phase)
2826 {
2827 phase_ = phase;
2828 pendingImportTasks_ = 0;
2829 completedTasks_ = 0;
2830
2831 if (phase == ImportPhase::FINISHED) {
2832 if ((flags_ & CORE_GLTF_IMPORT_RESOURCE_MESH_CPU_ACCESS) && !meshBuilders_.empty()) {
2833 {
2834 auto& shaderManager = renderContext_.GetDevice().GetShaderManager();
2835 const VertexInputDeclarationView vertexInputDeclaration =
2836 shaderManager.GetVertexInputDeclarationView(shaderManager.GetVertexInputDeclarationHandle(
2837 DefaultMaterialShaderConstants::VERTEX_INPUT_DECLARATION_FORWARD));
2838 meshData_.vertexInputDeclaration.bindingDescriptionCount =
2839 static_cast<uint32_t>(vertexInputDeclaration.bindingDescriptions.size());
2840 meshData_.vertexInputDeclaration.attributeDescriptionCount =
2841 static_cast<uint32_t>(vertexInputDeclaration.attributeDescriptions.size());
2842 std::copy(vertexInputDeclaration.bindingDescriptions.cbegin(),
2843 vertexInputDeclaration.bindingDescriptions.cend(),
2844 meshData_.vertexInputDeclaration.bindingDescriptions);
2845 std::copy(vertexInputDeclaration.attributeDescriptions.cbegin(),
2846 vertexInputDeclaration.attributeDescriptions.cend(),
2847 meshData_.vertexInputDeclaration.attributeDescriptions);
2848 }
2849 meshData_.meshes.resize(meshBuilders_.size());
2850 for (uint32_t mesh = 0U; mesh < meshBuilders_.size(); ++mesh) {
2851 if (!meshBuilders_[mesh]) {
2852 continue;
2853 }
2854 auto& currentMesh = meshData_.meshes[mesh];
2855 auto vertexData = meshBuilders_[mesh]->GetVertexData();
2856 auto indexData = meshBuilders_[mesh]->GetIndexData();
2857 auto jointData = meshBuilders_[mesh]->GetJointData();
2858 auto submeshes = meshBuilders_[mesh]->GetSubmeshes();
2859 currentMesh.subMeshes.resize(submeshes.size());
2860 for (uint32_t subMesh = 0U; subMesh < submeshes.size(); ++subMesh) {
2861 auto& currentSubMesh = currentMesh.subMeshes[subMesh];
2862 currentSubMesh.indices = submeshes[subMesh].indexCount;
2863 currentSubMesh.vertices = submeshes[subMesh].vertexCount;
2864
2865 currentSubMesh.indexBuffer = array_view(indexData.data() + submeshes[subMesh].indexBuffer.offset,
2866 submeshes[subMesh].indexBuffer.byteSize);
2867
2868 auto& bufferAccess = submeshes[subMesh].bufferAccess;
2869 auto fill = [](array_view<const uint8_t> data, const MeshComponent::Submesh::BufferAccess& buffer) {
2870 if (buffer.offset > data.size() || buffer.offset + buffer.byteSize > data.size()) {
2871 return array_view<const uint8_t> {};
2872 }
2873 return array_view(data.data() + buffer.offset, buffer.byteSize);
2874 };
2875 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_POS] =
2876 fill(vertexData, bufferAccess[MeshComponent::Submesh::DM_VB_POS]);
2877 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_NOR] =
2878 fill(vertexData, bufferAccess[MeshComponent::Submesh::DM_VB_NOR]);
2879 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_UV0] =
2880 fill(vertexData, bufferAccess[MeshComponent::Submesh::DM_VB_UV0]);
2881 if (submeshes[subMesh].flags & MeshComponent::Submesh::SECOND_TEXCOORD_BIT) {
2882 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_UV1] =
2883 fill(vertexData, bufferAccess[MeshComponent::Submesh::DM_VB_UV1]);
2884 }
2885 if (submeshes[subMesh].flags & MeshComponent::Submesh::TANGENTS_BIT) {
2886 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_TAN] =
2887 fill(vertexData, bufferAccess[MeshComponent::Submesh::DM_VB_TAN]);
2888 }
2889 if (submeshes[subMesh].flags & MeshComponent::Submesh::VERTEX_COLORS_BIT) {
2890 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_COL] =
2891 fill(vertexData, bufferAccess[MeshComponent::Submesh::DM_VB_COL]);
2892 }
2893 if (submeshes[subMesh].flags & MeshComponent::Submesh::SKIN_BIT) {
2894 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_JOI] =
2895 fill(jointData, bufferAccess[MeshComponent::Submesh::DM_VB_JOI]);
2896 currentSubMesh.attributeBuffers[MeshComponent::Submesh::DM_VB_JOW] =
2897 fill(jointData, bufferAccess[MeshComponent::Submesh::DM_VB_JOW]);
2898 }
2899 }
2900 }
2901 }
2902 // All tasks are done.
2903 if (listener_) {
2904 listener_->OnImportFinished();
2905 }
2906 tasks_.clear();
2907 return;
2908 }
2909
2910 for (auto& task : tasks_) {
2911 if (task->state == ImporterTask::State::Finished) {
2912 continue;
2913 }
2914
2915 if (task->phase != phase) {
2916 continue;
2917 }
2918
2919 // This will turn queued tasks to running ones.
2920 if (task->state == ImporterTask::State::Queued) {
2921 ProgressTask(*task);
2922 }
2923 }
2924 }
2925
FindTaskById(uint64_t id)2926 GLTF2Importer::ImporterTask* GLTF2Importer::FindTaskById(uint64_t id)
2927 {
2928 for (size_t i = 0; i < tasks_.size(); ++i) {
2929 if (tasks_[i]->id == id) {
2930 return tasks_[i].get();
2931 }
2932 }
2933
2934 return nullptr;
2935 }
2936
Execute(uint32_t timeBudget)2937 bool GLTF2Importer::Execute(uint32_t timeBudget)
2938 {
2939 if (IsCompleted()) {
2940 return true;
2941 }
2942
2943 // Handle data gathering.
2944 HandleGatherTasks();
2945
2946 // 'timeBudget' is given in microseconds
2947 const auto budget = std::chrono::microseconds(timeBudget);
2948
2949 // Use steady_clock for measuring
2950 using Clock = std::chrono::steady_clock;
2951
2952 // Start time.
2953 const auto s0 = Clock::now();
2954
2955 // Handle resource / scene importing.
2956 while (pendingImportTasks_ > 0) {
2957 // Execute and handle one task.
2958 mainThreadQueue_->Execute();
2959 HandleImportTasks();
2960
2961 // Check how much time has elapsed.
2962 if (timeBudget > 0) {
2963 const auto s1 = Clock::now();
2964 if (((s1 - s0) >= budget)) {
2965 // Break if whole time budget consumed.
2966 break;
2967 }
2968 }
2969 }
2970
2971 // All tasks done for this phase?
2972 if ((!gatherResults_[static_cast<uint32_t>(phase_)] || gatherResults_[static_cast<uint32_t>(phase_)]->IsDone()) &&
2973 pendingImportTasks_ == 0) {
2974 // Proceed to next phase.
2975 StartPhase((ImportPhase)(phase_ + 1));
2976 }
2977
2978 return IsCompleted();
2979 }
2980
HandleImportTasks()2981 void GLTF2Importer::HandleImportTasks()
2982 {
2983 const vector<uint64_t> finishedImportTasks = mainThreadQueue_->CollectFinishedTasks();
2984 if (!finishedImportTasks.empty()) {
2985 pendingImportTasks_ -= finishedImportTasks.size();
2986
2987 for (auto& finishedId : finishedImportTasks) {
2988 ImporterTask* task = FindTaskById(finishedId);
2989 if (task) {
2990 if (task->success) {
2991 ProgressTask(*task);
2992 } else {
2993 // Import phase failed.
2994 const string error = "Import data failed: " + task->name;
2995 CORE_LOG_W("%s", error.c_str());
2996 result_.success = false;
2997 result_.error += error + '\n';
2998 }
2999 }
3000 }
3001 }
3002 }
3003
HandleGatherTasks()3004 void GLTF2Importer::HandleGatherTasks()
3005 {
3006 auto& result = gatherResults_[static_cast<uint32_t>(phase_)];
3007 if (!result || !result->IsDone()) {
3008 return;
3009 }
3010
3011 vector<uint64_t> finishedGatherTasks;
3012 {
3013 std::lock_guard lock(gatherTasksLock_);
3014 std::swap(finishedGatherTasks, finishedGatherTasks_[static_cast<uint32_t>(phase_)]);
3015 }
3016
3017 if (finishedGatherTasks.size() > 0) {
3018 for (auto& finishedId : finishedGatherTasks) {
3019 ImporterTask* task = FindTaskById(finishedId);
3020 if (task) {
3021 if (task->success) {
3022 ProgressTask(*task);
3023 } else {
3024 // Gather phase failed.
3025 const string error = "Gather data failed: " + task->name;
3026 CORE_LOG_W("%s", error.c_str());
3027 result_.success = false;
3028 result_.error += error + '\n';
3029 }
3030 result_.error += task->errors;
3031 }
3032 }
3033 }
3034 }
3035
PrepareBufferTasks()3036 void GLTF2Importer::PrepareBufferTasks()
3037 {
3038 // Buffers task.
3039 const auto firstTask = static_cast<ptrdiff_t>(tasks_.size());
3040 auto task = make_unique<ImporterTask>();
3041 task->name = "Load buffers";
3042 task->phase = ImportPhase::BUFFERS;
3043 task->gather = [this, t = task.get()]() -> bool {
3044 BufferLoadResult result = LoadBuffers(data_, engine_.GetFileManager());
3045 t->errors += result.error;
3046 return result.success;
3047 };
3048 QueueTask(BASE_NS::move(task));
3049
3050 // We're loading all the buffers in one gather. This is probably fine as typical glTF files have just one binary
3051 // blob.
3052 (*(tasks_.begin() + firstTask))->state = ImporterTask::State::Gather;
3053 auto threadTask = IThreadPool::ITask::Ptr { new GatherThreadTask(*this, *(*(tasks_.begin() + firstTask))) };
3054 bufferTask_ = threadTask.get();
3055 ++pendingGatherTasks_[static_cast<uint32_t>(ImportPhase::BUFFERS)];
3056 gatherResults_[static_cast<uint32_t>(ImportPhase::BUFFERS)] = threadPool_->Push(BASE_NS::move(threadTask));
3057 }
3058
PrepareSamplerTasks()3059 void GLTF2Importer::PrepareSamplerTasks()
3060 {
3061 for (size_t i = 0; i < data_->samplers.size(); ++i) {
3062 auto task = make_unique<ImporterTask>();
3063 task->name = "Import sampler";
3064 task->phase = ImportPhase::SAMPLERS;
3065 task->import = [this, i]() -> bool {
3066 EntityReference entity;
3067 auto const& sampler = data_->samplers[i];
3068 if ((sampler->magFilter != FilterMode::LINEAR) || (sampler->minFilter != FilterMode::LINEAR) ||
3069 (sampler->wrapS != WrapMode::REPEAT) || (sampler->wrapT != WrapMode::REPEAT)) {
3070 GpuSamplerDesc desc;
3071 ConvertToCoreFilter(sampler->magFilter, desc.magFilter);
3072 ConvertToCoreFilter(sampler->minFilter, desc.minFilter, desc.mipMapMode);
3073
3074 desc.minLod = 0.0f;
3075 desc.maxLod = 32.0f;
3076
3077 desc.addressModeU = ConvertToCoreWrapMode(sampler->wrapS);
3078 desc.addressModeV = ConvertToCoreWrapMode(sampler->wrapT);
3079 desc.addressModeW = desc.addressModeU;
3080
3081 entity = ecs_->GetEntityManager().CreateReferenceCounted();
3082 renderHandleManager_->Create(entity);
3083 string const name = data_->defaultResources + "_sampler_" + to_string(i);
3084 renderHandleManager_->Write(entity)->reference = gpuResourceManager_.GetOrCreate(name, desc);
3085 }
3086 result_.data.samplers.push_back(move(entity));
3087
3088 return true;
3089 };
3090
3091 QueueTask(move(task));
3092 }
3093 }
3094
QueueImage(size_t i,string && uri,string && name)3095 void GLTF2Importer::QueueImage(size_t i, string&& uri, string&& name)
3096 {
3097 struct ImageTaskData {
3098 GLTF2Importer* importer;
3099 size_t index;
3100 string uri;
3101 string name;
3102 refcnt_ptr<IRenderDataStoreDefaultStaging> staging;
3103 RenderHandleReference imageHandle;
3104 };
3105
3106 constexpr const string_view RENDER_DATA_STORE_DEFAULT_STAGING = "RenderDataStoreDefaultStaging";
3107
3108 auto staging = refcnt_ptr<IRenderDataStoreDefaultStaging>(
3109 renderContext_.GetRenderDataStoreManager().GetRenderDataStore(RENDER_DATA_STORE_DEFAULT_STAGING.data()));
3110 // Does not exist, which means it needs to be imported.
3111 auto task = make_unique<GatheredDataTask<ImageTaskData>>();
3112 task->data = ImageTaskData { this, i, move(uri), move(name), staging, {} };
3113 task->name = "Import image";
3114 task->phase = ImportPhase::IMAGES;
3115 task->gather = [t = task.get(), colorSpaceFlags = colorSpaceFlags_]() -> bool {
3116 GLTF2Importer* importer = t->data.importer;
3117 auto const& image = importer->data_->images[t->data.index];
3118 IImageLoaderManager::LoadResult result = GatherImageData(*image, *importer->data_,
3119 importer->engine_.GetFileManager(), importer->engine_.GetImageLoaderManager(), 0U, colorSpaceFlags);
3120 if (result.success) {
3121 t->data.imageHandle =
3122 ImportTexture(t->data.uri, move(result.image), *t->data.staging, importer->gpuResourceManager_);
3123 } else {
3124 CORE_LOG_W("Loading image '%s' failed: %s", image->uri.c_str(), result.error);
3125 t->errors += result.error;
3126 t->errors += '\n';
3127 }
3128
3129 return true;
3130 };
3131
3132 task->import = [t = task.get()]() mutable -> bool {
3133 if (t->data.imageHandle) {
3134 GLTF2Importer* importer = t->data.importer;
3135 auto imageEntity = importer->ecs_->GetEntityManager().CreateReferenceCounted();
3136 importer->renderHandleManager_->Create(imageEntity);
3137 importer->renderHandleManager_->Write(imageEntity)->reference = move(t->data.imageHandle);
3138 if (!t->data.uri.empty()) {
3139 importer->uriManager_->Create(imageEntity);
3140 importer->uriManager_->Write(imageEntity)->uri = move(t->data.uri);
3141 }
3142 if (!t->data.name.empty()) {
3143 importer->nameManager_->Create(imageEntity);
3144 importer->nameManager_->Write(imageEntity)->name = move(t->data.name);
3145 }
3146
3147 importer->result_.data.images[t->data.index] = move(imageEntity);
3148 }
3149
3150 return true;
3151 };
3152
3153 task->finished = [t = task.get()]() {};
3154
3155 QueueTask(move(task));
3156 }
3157
PrepareImageTasks()3158 void GLTF2Importer::PrepareImageTasks()
3159 {
3160 result_.data.images.resize(data_->images.size(), {});
3161 result_.data.textures.resize(data_->textures.size(), {});
3162
3163 const bool skipUnusedResources = (flags_ & CORE_GLTF_IMPORT_RESOURCE_SKIP_UNUSED) != 0;
3164
3165 vector<bool> imageLoadingRequred;
3166 imageLoadingRequred.resize(data_->images.size(), skipUnusedResources ? false : true);
3167
3168 if (skipUnusedResources) {
3169 // Find image references from textures.
3170 ResolveReferencedImages(*data_, imageLoadingRequred);
3171 }
3172
3173 // Tasks for image loading.
3174 for (size_t i = 0; i < data_->images.size(); ++i) {
3175 // Skip loading of this image if it is not referenced by textures.
3176 if (!imageLoadingRequred[i]) {
3177 continue;
3178 }
3179
3180 string uri;
3181 string name = data_->defaultResources + "/images/" + to_string(i);
3182
3183 auto const& image = data_->images[i];
3184 if (image->uri.empty() || GLTF2::IsDataURI(image->uri)) {
3185 // NOTE: Might need to figure out better way to reference embedded resources.
3186 uri = data_->filepath + "/" + name;
3187 } else {
3188 uri = data_->filepath + "/" + image->uri;
3189 }
3190
3191 // See if this resource already exists.
3192 Entity imageEntity = LookupResourceByUri(uri, *uriManager_, *renderHandleManager_);
3193 if (EntityUtil::IsValid(imageEntity)) {
3194 // Already exists.
3195 result_.data.images[i] = ecs_->GetEntityManager().GetReferenceCounted(imageEntity);
3196 CORE_LOG_D("Resource already exists, skipping ('%s')", uri.c_str());
3197 continue;
3198 }
3199
3200 QueueImage(i, move(uri), move(name));
3201 }
3202
3203 // Tasks assigning textures to images.
3204 for (size_t i = 0; i < data_->textures.size(); ++i) {
3205 auto task = make_unique<ImporterTask>();
3206 task->name = "Import texture";
3207 task->phase = ImportPhase::TEXTURES;
3208 task->import = [this, i]() -> bool {
3209 const GLTF2::Texture& texture = *(data_->textures[i]);
3210
3211 const size_t index = FindIndex(data_->images, texture.image);
3212 if (index != GLTF_INVALID_INDEX) {
3213 result_.data.textures[i] = result_.data.images[index];
3214 }
3215
3216 return true;
3217 };
3218 QueueTask(move(task));
3219 }
3220 }
3221
PrepareImageBasedLightTasks()3222 void GLTF2Importer::PrepareImageBasedLightTasks()
3223 {
3224 #if defined(GLTF2_EXTENSION_EXT_LIGHTS_IMAGE_BASED)
3225 result_.data.specularRadianceCubemaps.resize(data_->imageBasedLights.size(), {});
3226
3227 // Do nothing in case there are no image based lights.
3228 if (data_->imageBasedLights.empty()) {
3229 return;
3230 }
3231
3232 for (size_t lightIndex = 0; lightIndex < data_->imageBasedLights.size(); ++lightIndex) {
3233 const auto& light = data_->imageBasedLights[lightIndex];
3234
3235 // Create gather task that loads all cubemap faces for this light.
3236 auto task = make_unique<GatheredDataTask<RenderHandleReference>>();
3237 task->name = "Import specular radiance cubemaps";
3238 task->phase = ImportPhase::IMAGES;
3239
3240 task->gather = [this, &light, t = task.get()]() -> bool {
3241 bool success = true;
3242 vector<IImageLoaderManager::LoadResult> mipLevels;
3243 // For all mip levels.
3244 for (const auto& mipLevel : light->specularImages) {
3245 // For all cube faces.
3246 for (const auto& cubeFace : mipLevel) {
3247 if (cubeFace >= data_->images.size()) {
3248 success = false;
3249 break;
3250 }
3251 // Get image for this cube face.
3252 auto& image = data_->images[cubeFace];
3253 if (!image) {
3254 success = false;
3255 break;
3256 }
3257 // Load image.
3258 auto loadResult =
3259 GatherImageData(*image, *data_, engine_.GetFileManager(), engine_.GetImageLoaderManager(),
3260 IImageLoaderManager::IMAGE_LOADER_FLIP_VERTICALLY_BIT, colorSpaceFlags_);
3261 if (!loadResult.success) {
3262 success = false;
3263 CORE_LOG_W("Loading image '%s' failed: %s", image->uri.c_str(), loadResult.error);
3264 break;
3265 }
3266
3267 mipLevels.push_back(move(loadResult));
3268 }
3269 }
3270 if (!mipLevels.empty()) {
3271 t->data = CreateCubemapFromImages(light->specularImageSize, mipLevels, gpuResourceManager_);
3272 }
3273 return success;
3274 };
3275
3276 task->import = [this, lightIndex, t = task.get()]() -> bool {
3277 // Import specular cubemap image if needed.
3278 if (t->data) {
3279 EntityReference imageEntity = ecs_->GetEntityManager().CreateReferenceCounted();
3280 renderHandleManager_->Create(imageEntity);
3281 renderHandleManager_->Write(imageEntity)->reference = move(t->data);
3282 result_.data.specularRadianceCubemaps[lightIndex] = move(imageEntity);
3283 }
3284
3285 return true;
3286 };
3287
3288 QueueTask(move(task));
3289 }
3290 #endif
3291 }
3292
PrepareMaterialTasks()3293 void GLTF2Importer::PrepareMaterialTasks()
3294 {
3295 // Create EntityReferences for materials already at this stage. This allows assigning materials to meshes before
3296 // they are ready.
3297 const auto materialCount = data_->materials.size();
3298 result_.data.materials.reserve(materialCount);
3299
3300 for (size_t i = 0; i < materialCount; ++i) {
3301 string uri;
3302 static constexpr string_view materialsPath = "/materials/";
3303 uri.reserve(data_->filepath.size() + data_->defaultResources.size() + materialsPath.size() + 2U);
3304 uri += data_->filepath;
3305 uri += '/';
3306 uri += data_->defaultResources;
3307 uri += materialsPath;
3308 uri += to_string(i);
3309 auto materialEntity = LookupResourceByUri(uri, *uriManager_, *materialManager_);
3310 if (EntityUtil::IsValid(materialEntity)) {
3311 CORE_LOG_D("Resource already exists, skipping ('%s')", uri.c_str());
3312 } else {
3313 materialEntity = ecs_->GetEntityManager().Create();
3314 if (!uri.empty()) {
3315 uriManager_->Create(materialEntity);
3316 uriManager_->Write(materialEntity)->uri = BASE_NS::move(uri);
3317 }
3318 }
3319
3320 result_.data.materials.push_back(ecs_->GetEntityManager().GetReferenceCounted(materialEntity));
3321 }
3322
3323 auto task = make_unique<ImporterTask>();
3324 task->name = "Import material";
3325 task->phase = ImportPhase::MATERIALS;
3326 task->import = [this]() -> bool {
3327 bool success = true;
3328 for (size_t i = 0; i < data_->materials.size(); ++i) {
3329 const Entity materialEntity = result_.data.materials[i];
3330 if (const auto materialComponentId = materialManager_->GetComponentId(materialEntity);
3331 materialComponentId != IComponentManager::INVALID_COMPONENT_ID) {
3332 // Material was already created
3333 continue;
3334 }
3335 // Does not exist, so needs to be imported.
3336 const auto& gltfMaterial = *data_->materials[i];
3337 if (!gltfMaterial.name.empty()) {
3338 nameManager_->Create(materialEntity);
3339 nameManager_->Write(materialEntity)->name = gltfMaterial.name;
3340 }
3341 materialManager_->Create(materialEntity);
3342 success = ImportMaterial(result_, *data_, gltfMaterial, materialEntity, *materialManager_,
3343 gpuResourceManager_, dmShaderData_) &&
3344 success;
3345 }
3346
3347 return success;
3348 };
3349
3350 QueueTask(move(task));
3351 }
3352
PrepareMeshTasks()3353 void GLTF2Importer::PrepareMeshTasks()
3354 {
3355 result_.data.meshes.resize(data_->meshes.size(), {});
3356 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_MESH_CPU_ACCESS) {
3357 meshBuilders_.resize(data_->meshes.size());
3358 }
3359
3360 for (size_t i = 0; i < data_->meshes.size(); ++i) {
3361 string uri = data_->filepath + '/' + data_->defaultResources + "/meshes/" + to_string(i);
3362 const string_view name = data_->meshes[i]->name;
3363
3364 // See if this resource already exists.
3365 const auto meshEntity = LookupResourceByUri(uri, *uriManager_, *meshManager_);
3366 if (EntityUtil::IsValid(meshEntity)) {
3367 // Already exists.
3368 result_.data.meshes[i] = ecs_->GetEntityManager().GetReferenceCounted(meshEntity);
3369 CORE_LOG_D("Resource already exists, skipping ('%s')", uri.c_str());
3370 continue;
3371 }
3372
3373 auto task = make_unique<GatheredDataTask<GatherMeshDataResult>>();
3374 task->name = "Import mesh";
3375 task->phase = ImportPhase::MESHES;
3376 task->gather = [this, i, t = task.get()]() -> bool {
3377 const GLTF2::Mesh& mesh = *(data_->meshes[i]);
3378
3379 // Gather mesh data.
3380 t->data = GatherMeshData(mesh, result_, flags_, *materialManager_, device_, engine_);
3381 t->errors = t->data.error;
3382 return t->data.success;
3383 };
3384
3385 task->import = [this, i, uri = move(uri), name, t = task.get()]() mutable -> bool {
3386 if (!t->data.success) {
3387 return false;
3388 }
3389 // Import mesh.
3390 auto meshEntity = ImportMesh(*ecs_, t->data);
3391 if (!EntityUtil::IsValid(meshEntity)) {
3392 return false;
3393 }
3394 if (!uri.empty()) {
3395 uriManager_->Create(meshEntity);
3396 uriManager_->Write(meshEntity)->uri = move(uri);
3397 }
3398 if (!name.empty()) {
3399 nameManager_->Create(meshEntity);
3400 nameManager_->Write(meshEntity)->name = name;
3401 }
3402
3403 result_.data.meshes[i] = ecs_->GetEntityManager().GetReferenceCounted(meshEntity);
3404 return true;
3405 };
3406
3407 task->finished = [this, i, t = task.get()]() {
3408 if (flags_ & CORE_GLTF_IMPORT_RESOURCE_MESH_CPU_ACCESS) {
3409 meshBuilders_[i] = BASE_NS::move(t->data.meshBuilder);
3410 } else {
3411 t->data.meshBuilder.reset();
3412 }
3413 };
3414
3415 QueueTask(move(task));
3416 }
3417 }
3418
3419 template<>
3420 GLTF2Importer::GatheredDataTask<GLTF2Importer::ComponentTaskData<AnimationInputComponent>>*
PrepareAnimationInputTask(unordered_map<Accessor *,GatheredDataTask<ComponentTaskData<AnimationInputComponent>> * > & inputs,const AnimationTrack & track,IAnimationInputComponentManager * animationInputManager)3421 GLTF2Importer::PrepareAnimationInputTask(
3422 unordered_map<Accessor*, GatheredDataTask<ComponentTaskData<AnimationInputComponent>>*>& inputs,
3423 const AnimationTrack& track, IAnimationInputComponentManager* animationInputManager)
3424 {
3425 GLTF2Importer::GatheredDataTask<GLTF2Importer::ComponentTaskData<AnimationInputComponent>>* result = nullptr;
3426 if (auto pos = inputs.find(track.sampler->input); pos == inputs.end()) {
3427 auto task = make_unique<GatheredDataTask<ComponentTaskData<AnimationInputComponent>>>();
3428 task->name = "Import animation input";
3429 task->phase = ImportPhase::ANIMATION_SAMPLERS;
3430 task->gather = [this, accessor = track.sampler->input, t = task.get()]() -> bool {
3431 return BuildAnimationInput(*data_, engine_.GetFileManager(), *accessor, t->data.component);
3432 };
3433 task->import = [em = &ecs_->GetEntityManager(), animationInputManager, t = task.get()]() -> bool {
3434 t->data.entity = em->CreateReferenceCounted();
3435 animationInputManager->Set(t->data.entity, t->data.component);
3436 return true;
3437 };
3438 inputs.insert({ track.sampler->input, task.get() });
3439 result = task.get();
3440 QueueTask(move(task));
3441 } else {
3442 result = pos->second;
3443 }
3444 return result;
3445 }
3446
3447 template<>
3448 GLTF2Importer::GatheredDataTask<GLTF2Importer::ComponentTaskData<AnimationOutputComponent>>*
PrepareAnimationOutputTask(unordered_map<Accessor *,GatheredDataTask<ComponentTaskData<AnimationOutputComponent>> * > & outputs,const AnimationTrack & track,IAnimationOutputComponentManager * animationOutputManager)3449 GLTF2Importer::PrepareAnimationOutputTask(
3450 unordered_map<Accessor*, GatheredDataTask<ComponentTaskData<AnimationOutputComponent>>*>& outputs,
3451 const AnimationTrack& track, IAnimationOutputComponentManager* animationOutputManager)
3452 {
3453 GatheredDataTask<ComponentTaskData<AnimationOutputComponent>>* result = nullptr;
3454 if (auto pos = outputs.find(track.sampler->output); pos == outputs.end()) {
3455 auto task = make_unique<GatheredDataTask<ComponentTaskData<AnimationOutputComponent>>>();
3456 task->name = "Import animation output";
3457 task->phase = ImportPhase::ANIMATION_SAMPLERS;
3458 task->gather = [this, accessor = track.sampler->output, path = track.channel.path, t = task.get()]() -> bool {
3459 return BuildAnimationOutput(*data_, engine_.GetFileManager(), *accessor, path, t->data.component);
3460 };
3461 task->import = [em = &ecs_->GetEntityManager(), animationOutputManager, t = task.get()]() -> bool {
3462 t->data.entity = em->CreateReferenceCounted();
3463 animationOutputManager->Set(t->data.entity, t->data.component);
3464 return true;
3465 };
3466 outputs.insert({ track.sampler->output, task.get() });
3467 result = task.get();
3468 QueueTask(move(task));
3469 } else {
3470 result = pos->second;
3471 }
3472 return result;
3473 }
3474
PrepareAnimationTasks()3475 void GLTF2Importer::PrepareAnimationTasks()
3476 {
3477 auto animationManager = GetManager<IAnimationComponentManager>(*ecs_);
3478 auto animationInputManager = GetManager<IAnimationInputComponentManager>(*ecs_);
3479 auto animationOutputManager = GetManager<IAnimationOutputComponentManager>(*ecs_);
3480 auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(*ecs_);
3481 if (!animationManager || !animationInputManager || !animationOutputManager || !animationTrackManager) {
3482 return;
3483 }
3484 result_.data.animations.resize(data_->animations.size(), {});
3485
3486 unordered_map<Accessor*, GatheredDataTask<ComponentTaskData<AnimationInputComponent>>*> inputs;
3487 unordered_map<Accessor*, GatheredDataTask<ComponentTaskData<AnimationOutputComponent>>*> outputs;
3488 for (size_t i = 0; i < data_->animations.size(); i++) {
3489 const string uri = data_->filepath + '/' + data_->defaultResources + "/animations/" + to_string(i);
3490 const string_view name = data_->animations[i]->name;
3491
3492 // See if this resource already exists.
3493 const auto animationEntity = LookupResourceByUri(uri, *uriManager_, *animationManager);
3494 if (EntityUtil::IsValid(animationEntity)) {
3495 result_.data.animations[i] = ecs_->GetEntityManager().GetReferenceCounted(animationEntity);
3496 CORE_LOG_D("Resource already exists, skipping ('%s')", uri.c_str());
3497 continue;
3498 }
3499 vector<GatheredDataTask<ComponentTaskData<AnimationInputComponent>>*> inputResults;
3500 vector<GatheredDataTask<ComponentTaskData<AnimationOutputComponent>>*> outputResults;
3501 for (const auto& track : data_->animations[i]->tracks) {
3502 if (track.sampler && track.sampler->input && track.sampler->output) {
3503 inputResults.push_back(PrepareAnimationInputTask(inputs, track, animationInputManager));
3504 outputResults.push_back(PrepareAnimationOutputTask(outputs, track, animationOutputManager));
3505 }
3506 }
3507
3508 auto task = make_unique<ImporterTask>();
3509 task->name = "Import animation";
3510 task->phase = ImportPhase::ANIMATIONS;
3511 task->import = AnimationTaskData { this, i, uri, name, animationManager, animationTrackManager,
3512 move(inputResults), move(outputResults) };
3513 task->finished = [t = task.get()]() { t->import = {}; };
3514
3515 QueueTask(move(task));
3516 }
3517 }
3518
PrepareSkinTasks()3519 void GLTF2Importer::PrepareSkinTasks()
3520 {
3521 auto* skinIbmManager = GetManager<ISkinIbmComponentManager>(*ecs_);
3522 if (!skinIbmManager) {
3523 return;
3524 }
3525 result_.data.skins.resize(data_->skins.size(), {});
3526
3527 for (size_t i = 0; i < data_->skins.size(); i++) {
3528 string name = data_->defaultResources + "/skins/" + to_string(i);
3529 string uri = data_->filepath + '/' + name;
3530
3531 // See if this resource already exists.
3532 const Entity skinIbmEntity = LookupResourceByUri(uri, *uriManager_, *skinIbmManager);
3533 if (EntityUtil::IsValid(skinIbmEntity)) {
3534 // Already exists.
3535 result_.data.skins[i] = ecs_->GetEntityManager().GetReferenceCounted(skinIbmEntity);
3536 CORE_LOG_D("Resource already exists, skipping ('%s')", uri.c_str());
3537 continue;
3538 }
3539
3540 auto task = make_unique<GatheredDataTask<SkinIbmComponent>>();
3541 task->name = "Import skin";
3542 task->phase = ImportPhase::SKINS;
3543 task->gather = [this, i, t = task.get()]() -> bool {
3544 return BuildSkinIbmComponent(*(data_->skins[i]), t->data);
3545 };
3546
3547 task->import = [this, i, uri = move(uri), name = move(name), t = task.get(), skinIbmManager]() mutable -> bool {
3548 if (!t->data.matrices.empty()) {
3549 auto skinIbmEntity = ecs_->GetEntityManager().CreateReferenceCounted();
3550
3551 skinIbmManager->Create(skinIbmEntity);
3552 {
3553 auto skinIbmHandle = skinIbmManager->Write(skinIbmEntity);
3554 *skinIbmHandle = move(t->data);
3555 }
3556
3557 uriManager_->Create(skinIbmEntity);
3558 uriManager_->Write(skinIbmEntity)->uri = move(uri);
3559 nameManager_->Create(skinIbmEntity);
3560 nameManager_->Write(skinIbmEntity)->name = move(name);
3561 result_.data.skins[i] = move(skinIbmEntity);
3562 return true;
3563 }
3564
3565 return false;
3566 };
3567
3568 task->finished = [t = task.get()]() { t->data = {}; };
3569
3570 QueueTask(move(task));
3571 }
3572 }
3573
Destroy()3574 void GLTF2Importer::Destroy()
3575 {
3576 delete this;
3577 }
3578
Gltf2SceneImporter(IEngine & engine,IRenderContext & renderContext,IEcs & ecs)3579 Gltf2SceneImporter::Gltf2SceneImporter(IEngine& engine, IRenderContext& renderContext, IEcs& ecs)
3580 : ecs_(ecs), graphicsContext_(GetInstance<IGraphicsContext>(
3581 *renderContext.GetInterface<IClassRegister>(), UID_GRAPHICS_CONTEXT)),
3582 importer_(new GLTF2::GLTF2Importer(engine, renderContext, ecs))
3583 {}
3584
Gltf2SceneImporter(IEngine & engine,IRenderContext & renderContext,IEcs & ecs,IThreadPool & pool)3585 Gltf2SceneImporter::Gltf2SceneImporter(IEngine& engine, IRenderContext& renderContext, IEcs& ecs, IThreadPool& pool)
3586 : ecs_(ecs), graphicsContext_(GetInstance<IGraphicsContext>(
3587 *renderContext.GetInterface<IClassRegister>(), UID_GRAPHICS_CONTEXT)),
3588 importer_(new GLTF2::GLTF2Importer(engine, renderContext, ecs, pool))
3589 {}
3590
ImportResources(const ISceneData::Ptr & data,ResourceImportFlags flags)3591 void Gltf2SceneImporter::ImportResources(const ISceneData::Ptr& data, ResourceImportFlags flags)
3592 {
3593 data_ = {};
3594 listener_ = nullptr;
3595 result_.error = 1;
3596 if (auto sceneData = data->GetInterface<SceneData>()) {
3597 if (auto* gltfData = sceneData->GetData()) {
3598 importer_->ImportGLTF(*gltfData, flags);
3599 UpdateResults();
3600 if (!result_.error) {
3601 data_ = data;
3602 }
3603 }
3604 }
3605 }
3606
ImportResources(const ISceneData::Ptr & data,ResourceImportFlags flags,ISceneImporter::Listener * listener)3607 void Gltf2SceneImporter::ImportResources(
3608 const ISceneData::Ptr& data, ResourceImportFlags flags, ISceneImporter::Listener* listener)
3609 {
3610 data_ = {};
3611 listener_ = nullptr;
3612
3613 if (auto sceneData = data->GetInterface<SceneData>()) {
3614 if (auto* gltfData = sceneData->GetData()) {
3615 listener_ = listener;
3616 data_ = data;
3617 importer_->ImportGLTFAsync(*gltfData, flags, this);
3618 }
3619 }
3620 }
3621
Execute(uint32_t timeBudget)3622 bool Gltf2SceneImporter::Execute(uint32_t timeBudget)
3623 {
3624 if (importer_->Execute(timeBudget)) {
3625 UpdateResults();
3626 return true;
3627 }
3628 return false;
3629 }
3630
Cancel()3631 void Gltf2SceneImporter::Cancel()
3632 {
3633 importer_->Cancel();
3634 }
3635
IsCompleted() const3636 bool Gltf2SceneImporter::IsCompleted() const
3637 {
3638 return importer_->IsCompleted();
3639 }
3640
GetResult() const3641 const ISceneImporter::Result& Gltf2SceneImporter::GetResult() const
3642 {
3643 return result_;
3644 }
3645
GetMeshData() const3646 const MeshData& Gltf2SceneImporter::GetMeshData() const
3647 {
3648 return meshData_;
3649 }
3650
ImportScene(size_t sceneIndex)3651 Entity Gltf2SceneImporter::ImportScene(size_t sceneIndex)
3652 {
3653 return ImportScene(sceneIndex, {}, SceneImportFlagBits::CORE_IMPORT_COMPONENT_FLAG_BITS_ALL);
3654 }
3655
ImportScene(size_t sceneIndex,SceneImportFlags flags)3656 Entity Gltf2SceneImporter::ImportScene(size_t sceneIndex, SceneImportFlags flags)
3657 {
3658 return ImportScene(sceneIndex, {}, flags);
3659 }
3660
ImportScene(size_t sceneIndex,Entity parentEntity)3661 Entity Gltf2SceneImporter::ImportScene(size_t sceneIndex, Entity parentEntity)
3662 {
3663 return ImportScene(sceneIndex, parentEntity, SceneImportFlagBits::CORE_IMPORT_COMPONENT_FLAG_BITS_ALL);
3664 }
3665
ImportScene(size_t sceneIndex,Entity parentEntity,SceneImportFlags flags)3666 Entity Gltf2SceneImporter::ImportScene(size_t sceneIndex, Entity parentEntity, SceneImportFlags flags)
3667 {
3668 if (importer_ && data_) {
3669 if (auto sceneData = data_->GetInterface<SceneData>()) {
3670 if (auto* gltfData = sceneData->GetData()) {
3671 auto& gltf = graphicsContext_->GetGltf();
3672 return gltf.ImportGltfScene(
3673 sceneIndex, *gltfData, importer_->GetResult().data, ecs_, parentEntity, flags);
3674 }
3675 }
3676 }
3677 return {};
3678 }
3679
ImportScene(size_t sceneIndex,uint32_t sceneId,SceneImportFlags flags)3680 Entity Gltf2SceneImporter::ImportScene(size_t sceneIndex, uint32_t sceneId, SceneImportFlags flags)
3681 {
3682 if (importer_ && data_) {
3683 if (auto sceneData = data_->GetInterface<SceneData>()) {
3684 if (auto* gltfData = sceneData->GetData()) {
3685 auto& gltf = graphicsContext_->GetGltf();
3686 return gltf.ImportGltfScene(sceneIndex, *gltfData, importer_->GetResult().data, ecs_, sceneId, flags);
3687 }
3688 }
3689 }
3690 return {};
3691 }
3692
UpdateResults()3693 void Gltf2SceneImporter::UpdateResults()
3694 {
3695 const auto& result = importer_->GetResult();
3696 result_.error = result.success ? 0 : 1;
3697 result_.message = result.error;
3698 result_.data.samplers = result.data.samplers;
3699 result_.data.images = result.data.images;
3700 result_.data.textures = result.data.textures;
3701 result_.data.materials = result.data.materials;
3702 result_.data.meshes = result.data.meshes;
3703 result_.data.skins = result.data.skins;
3704 result_.data.animations = result.data.animations;
3705 result_.data.specularRadianceCubemaps = result.data.specularRadianceCubemaps;
3706 const auto& meshData = importer_->GetMeshData();
3707 meshData_.meshes.resize(meshData.meshes.size());
3708 for (size_t i = 0U; i < meshData.meshes.size(); ++i) {
3709 auto& dstMesh = meshData_.meshes[i];
3710 const auto& srcMesh = meshData.meshes[i];
3711 dstMesh.subMeshes.resize(srcMesh.subMeshes.size());
3712 std::transform(srcMesh.subMeshes.cbegin(), srcMesh.subMeshes.cend(), dstMesh.subMeshes.begin(),
3713 [](const GltfMeshData::SubMesh& gltfSubmesh) {
3714 MeshData::SubMesh submesh;
3715 submesh.indices = gltfSubmesh.indices;
3716 submesh.vertices = gltfSubmesh.vertices;
3717 submesh.indexBuffer = gltfSubmesh.indexBuffer;
3718 std::copy(std::begin(gltfSubmesh.attributeBuffers), std::end(gltfSubmesh.attributeBuffers),
3719 std::begin(submesh.attributeBuffers));
3720 return submesh;
3721 });
3722 }
3723 meshData_.vertexInputDeclaration = meshData.vertexInputDeclaration;
3724 }
3725
3726 // IInterface
GetInterface(const BASE_NS::Uid & uid) const3727 const IInterface* Gltf2SceneImporter::GetInterface(const BASE_NS::Uid& uid) const
3728 {
3729 if (uid == ISceneImporter::UID) {
3730 return static_cast<const ISceneImporter*>(this);
3731 }
3732 if (uid == IInterface::UID) {
3733 return static_cast<const IInterface*>(this);
3734 }
3735 return nullptr;
3736 }
3737
GetInterface(const BASE_NS::Uid & uid)3738 IInterface* Gltf2SceneImporter::GetInterface(const BASE_NS::Uid& uid)
3739 {
3740 if (uid == ISceneImporter::UID) {
3741 return static_cast<ISceneImporter*>(this);
3742 }
3743 if (uid == IInterface::UID) {
3744 return static_cast<IInterface*>(this);
3745 }
3746 return nullptr;
3747 }
3748
Ref()3749 void Gltf2SceneImporter::Ref()
3750 {
3751 ++refcnt_;
3752 }
3753
Unref()3754 void Gltf2SceneImporter::Unref()
3755 {
3756 if (--refcnt_ == 0) {
3757 delete this;
3758 }
3759 }
3760
OnImportStarted()3761 void Gltf2SceneImporter::OnImportStarted()
3762 {
3763 if (listener_) {
3764 listener_->OnImportStarted();
3765 }
3766 }
3767
OnImportFinished()3768 void Gltf2SceneImporter::OnImportFinished()
3769 {
3770 if (listener_) {
3771 listener_->OnImportFinished();
3772 }
3773 }
3774
OnImportProgressed(size_t taskIndex,size_t taskCount)3775 void Gltf2SceneImporter::OnImportProgressed(size_t taskIndex, size_t taskCount)
3776 {
3777 if (listener_) {
3778 listener_->OnImportProgressed(taskIndex, taskCount);
3779 }
3780 }
3781 } // namespace GLTF2
3782
3783 CORE3D_END_NAMESPACE()
3784