• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 == &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