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