• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "gltf2_exporter.h"
17 
18 #include <charconv>
19 
20 #include <3d/ecs/components/animation_component.h>
21 #include <3d/ecs/components/animation_input_component.h>
22 #include <3d/ecs/components/animation_output_component.h>
23 #include <3d/ecs/components/animation_track_component.h>
24 #include <3d/ecs/components/camera_component.h>
25 #include <3d/ecs/components/light_component.h>
26 #include <3d/ecs/components/material_component.h>
27 #include <3d/ecs/components/mesh_component.h>
28 #include <3d/ecs/components/morph_component.h>
29 #include <3d/ecs/components/name_component.h>
30 #include <3d/ecs/components/node_component.h>
31 #include <3d/ecs/components/render_configuration_component.h>
32 #include <3d/ecs/components/render_handle_component.h>
33 #include <3d/ecs/components/render_mesh_component.h>
34 #include <3d/ecs/components/skin_component.h>
35 #include <3d/ecs/components/skin_ibm_component.h>
36 #include <3d/ecs/components/skin_joints_component.h>
37 #include <3d/ecs/components/transform_component.h>
38 #include <3d/ecs/components/uri_component.h>
39 #include <3d/ecs/systems/intf_animation_system.h>
40 #include <3d/ecs/systems/intf_morphing_system.h>
41 #include <3d/ecs/systems/intf_node_system.h>
42 #include <base/containers/fixed_string.h>
43 #include <base/containers/string.h>
44 #include <base/containers/unordered_map.h>
45 #include <base/containers/vector.h>
46 #include <base/math/matrix_util.h>
47 #include <base/math/quaternion_util.h>
48 #include <base/util/compile_time_hashes.h>
49 #include <core/ecs/intf_ecs.h>
50 #include <core/intf_engine.h>
51 #include <core/io/intf_file_manager.h>
52 #include <core/log.h>
53 #include <core/namespace.h>
54 #include <core/plugin/intf_class_register.h>
55 #include <core/property/intf_property_handle.h>
56 #include <render/device/intf_gpu_resource_manager.h>
57 #include <render/device/intf_shader_manager.h>
58 #include <render/implementation_uids.h>
59 #include <render/intf_render_context.h>
60 
61 #include "ecs/systems/animation_playback.h"
62 #include "gltf/data.h"
63 #include "gltf/gltf2_data_structures.h"
64 #include "gltf/gltf2_util.h"
65 #include "util/json_util.h"
66 
67 template<>
hash(const bool & val)68 uint64_t BASE_NS::hash(const bool& val)
69 {
70     return static_cast<uint64_t>(val);
71 }
72 
73 template<>
hash(const CORE3D_NS::GLTF2::ComponentType & val)74 uint64_t BASE_NS::hash(const CORE3D_NS::GLTF2::ComponentType& val)
75 {
76     return static_cast<uint64_t>(val);
77 }
78 
79 template<>
hash(const CORE3D_NS::GLTF2::DataType & val)80 uint64_t BASE_NS::hash(const CORE3D_NS::GLTF2::DataType& val)
81 {
82     return static_cast<uint64_t>(val);
83 }
84 
85 template<>
hash(const CORE3D_NS::GLTF2::BufferTarget & val)86 uint64_t BASE_NS::hash(const CORE3D_NS::GLTF2::BufferTarget& val)
87 {
88     return static_cast<uint64_t>(val);
89 }
90 
91 CORE3D_BEGIN_NAMESPACE()
92 using namespace BASE_NS;
93 using namespace CORE_NS;
94 using namespace RENDER_NS;
95 
96 // internal method from
97 GLTFLoadResult LoadGLTF(IFileManager& fileManager, const string_view uri);
98 
AppendUnique(json::value & jsonData,const string_view value)99 void AppendUnique(json::value& jsonData, const string_view value)
100 {
101     if (jsonData.is_array()) {
102         const auto cmpValue = [value](const json::value& it) { return it.string_ == value; };
103         if (std::find_if(jsonData.array_.begin(), jsonData.array_.end(), cmpValue) == jsonData.array_.end()) {
104             jsonData.array_.push_back(value);
105         }
106     }
107 }
108 
109 // Some GLTF object names are not exported at the moment. Placeholder code has been added behind
110 // EXPORT_OTHER_OBJECT_NAMES macro to easily spot the missing names and add support if the need arises.
GetFilterMode(Filter filter)111 GLTF2::FilterMode GetFilterMode(Filter filter)
112 {
113     if (filter == CORE_FILTER_NEAREST) {
114         return GLTF2::FilterMode::NEAREST;
115     } else if (filter == CORE_FILTER_LINEAR) {
116         return GLTF2::FilterMode::LINEAR;
117     }
118 
119     return GLTF2::FilterMode::NEAREST;
120 }
121 
GetFilterMode(Filter filter,Filter mipmapMode)122 GLTF2::FilterMode GetFilterMode(Filter filter, Filter mipmapMode)
123 {
124     /* NOTE: how to figure out should mips be used from SamplerDesc? */
125     if (filter == CORE_FILTER_NEAREST && mipmapMode == CORE_FILTER_NEAREST) {
126         return GLTF2::FilterMode::NEAREST_MIPMAP_NEAREST;
127     } else if (filter == CORE_FILTER_LINEAR && mipmapMode == CORE_FILTER_NEAREST) {
128         return GLTF2::FilterMode::LINEAR_MIPMAP_NEAREST;
129     } else if (filter == CORE_FILTER_NEAREST && mipmapMode == CORE_FILTER_LINEAR) {
130         return GLTF2::FilterMode::NEAREST_MIPMAP_LINEAR;
131     } else if (filter == CORE_FILTER_LINEAR && mipmapMode == CORE_FILTER_LINEAR) {
132         return GLTF2::FilterMode::LINEAR_MIPMAP_LINEAR;
133     }
134 
135     return GLTF2::FilterMode::NEAREST;
136 }
137 
GetWrapMode(SamplerAddressMode mode)138 GLTF2::WrapMode GetWrapMode(SamplerAddressMode mode)
139 {
140     switch (mode) {
141         default:
142         case CORE_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
143         case CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
144         case CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
145             return GLTF2::WrapMode::CLAMP_TO_EDGE;
146 
147         case CORE_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
148             return GLTF2::WrapMode::MIRRORED_REPEAT;
149 
150         case CORE_SAMPLER_ADDRESS_MODE_REPEAT:
151             return GLTF2::WrapMode::REPEAT;
152     }
153 }
154 
GetAnimationPath(const AnimationTrackComponent & track)155 GLTF2::AnimationPath GetAnimationPath(const AnimationTrackComponent& track)
156 {
157     if (track.component == ITransformComponentManager::UID) {
158         if (track.property == "position") {
159             return GLTF2::AnimationPath::TRANSLATION;
160         } else if (track.property == "rotation") {
161             return GLTF2::AnimationPath::ROTATION;
162         } else if (track.property == "scale") {
163             return GLTF2::AnimationPath::SCALE;
164         }
165     } else if (track.component == IMorphComponentManager::UID) {
166         if (track.property == "morphWeights") {
167             return GLTF2::AnimationPath::WEIGHTS;
168         }
169     }
170     return GLTF2::AnimationPath::INVALID;
171 }
172 
GetAnimationInterpolation(AnimationTrackComponent::Interpolation interpolation)173 GLTF2::AnimationInterpolation GetAnimationInterpolation(AnimationTrackComponent::Interpolation interpolation)
174 {
175     switch (interpolation) {
176         case AnimationTrackComponent::Interpolation::STEP:
177             return GLTF2::AnimationInterpolation::STEP;
178         case AnimationTrackComponent::Interpolation::LINEAR:
179             return GLTF2::AnimationInterpolation::LINEAR;
180         case AnimationTrackComponent::Interpolation::SPLINE:
181             return GLTF2::AnimationInterpolation::SPLINE;
182         default:
183             return GLTF2::AnimationInterpolation::INVALID;
184     }
185 }
186 
187 namespace GLTF2 {
188 ExportResult::~ExportResult() = default;
189 
190 namespace {
191 constexpr const auto DEFAULT_BASECOLOR_FACTOR = Math::Vec4(1.f, 1.f, 1.f, 1.f);
192 constexpr const auto DEFAULT_DIFFUSE_FACTOR = Math::Vec4(1.f, 1.f, 1.f, 1.f);
193 constexpr const auto DEFAULT_SPECULAR_FACTOR = Math::Vec3(1.f, 1.f, 1.f);
194 constexpr const auto DEFAULT_EMISSIVE_FACTOR = Math::Vec3(0.f, 0.f, 0.f);
195 
196 constexpr const auto DEFAULT_TRANSLATION = Math::Vec3(0.f, 0.f, 0.f);
197 constexpr const auto DEFAULT_SCALE = Math::Vec3(1.f, 1.f, 1.f);
198 constexpr const auto DEFAULT_ROTATION = Math::Quat(0.f, 0.f, 0.f, 1.f);
199 
200 constexpr const auto IDENTITY_MATRIX =
201     Math::Mat4X4(1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f);
202 
203 /* Entity which have skin, camera, and/or light attached are stored here for further procesing. */
204 struct Entities {
205     vector<Entity> nodes;
206     vector<Entity> withSkin;
207     vector<Entity> withCamera;
208     vector<Entity> withLight;
209 };
210 
operator ==(const RenderHandleReference & lhs,const RenderHandleReference & rhs)211 inline bool operator==(const RenderHandleReference& lhs, const RenderHandleReference& rhs) noexcept
212 {
213     return lhs.GetHandle() == rhs.GetHandle();
214 }
215 
operator <(const RenderHandleReference & lhs,const RenderHandleReference & rhs)216 inline bool operator<(const RenderHandleReference& lhs, const RenderHandleReference& rhs) noexcept
217 {
218     return lhs.GetHandle().id < rhs.GetHandle().id;
219 }
220 
221 /* Returns the index where the given object is in the vector. */
222 template<typename T>
FindObjectIndex(vector<unique_ptr<T>> const & array,T const & object)223 int FindObjectIndex(vector<unique_ptr<T>> const& array, T const& object)
224 {
225     const auto comparePointers = [ptr = &object](const auto& aUniqueObject) { return aUniqueObject.get() == ptr; };
226     if (const auto pos = std::find_if(array.begin(), array.end(), comparePointers); pos != array.end()) {
227         return static_cast<int>(std::distance(array.begin(), pos));
228     }
229 
230     return -1;
231 }
232 
233 /* Returns the index where the given handle is in the vector. */
234 template<typename T>
FindHandleIndex(vector<T> const & handles,T handle)235 uint32_t FindHandleIndex(vector<T> const& handles, T handle)
236 {
237     if (const auto handlePos = std::find(handles.begin(), handles.end(), handle); handlePos != handles.end()) {
238         return static_cast<uint32_t>(std::distance(handles.begin(), handlePos));
239     }
240     return ~0u;
241 }
242 
243 /* Returns the index where the given handle is in the vector. If the handle was not in the vector it is added. */
244 template<typename T>
FindOrAddIndex(vector<T> & handles,const T & handle)245 uint32_t FindOrAddIndex(vector<T>& handles, const T& handle)
246 {
247     if (const auto handlePos =
248             std::find_if(handles.begin(), handles.end(), [handle](const auto& rhs) { return handle == rhs; });
249         handlePos != handles.end()) {
250         return static_cast<uint32_t>(std::distance(handles.begin(), handlePos));
251     } else {
252         handles.push_back(handle);
253         return static_cast<uint32_t>(handles.size() - 1);
254     }
255 }
256 
257 class BufferHelper {
258 public:
BufferHelper(Buffer & buffer,vector<unique_ptr<BufferView>> & usedBufferViews,vector<unique_ptr<Accessor>> & usedAccessors)259     BufferHelper(
260         Buffer& buffer, vector<unique_ptr<BufferView>>& usedBufferViews, vector<unique_ptr<Accessor>>& usedAccessors)
261         : usedBufferViews_(usedBufferViews), usedAccessors_(usedAccessors), buffer_(buffer) {};
262 
263     ~BufferHelper() = default;
264 
265     /* StoreBufferView hashes bufferView and stores it if needed. If dataAlignment is non-zero, data from aBufferView is
266      * written to the buffer. */
StoreBufferView(const BufferView & bufferView,uint32_t dataAlignment)267     BufferView* StoreBufferView(const BufferView& bufferView, uint32_t dataAlignment)
268     {
269         const auto bufferViewHash = BASE_NS::Hash(
270             bufferView.buffer, bufferView.byteLength, bufferView.byteOffset, bufferView.byteStride, bufferView.target);
271         const auto bufferViewIndex = FindOrAddIndex(bufferViewHashes_, bufferViewHash);
272         if ((bufferViewIndex + 1) > usedBufferViews_.size()) {
273             usedBufferViews_.resize(bufferViewIndex + 1);
274             usedBufferViews_[bufferViewIndex] = make_unique<BufferView>(bufferView);
275             usedBufferViews_[bufferViewIndex]->buffer = &buffer_;
276             if (dataAlignment) {
277                 const auto pad = buffer_.data.size() % dataAlignment;
278                 buffer_.data.append(pad, 0);
279                 usedBufferViews_[bufferViewIndex]->byteOffset = buffer_.data.size();
280                 buffer_.data.append(bufferView.data, bufferView.data + bufferView.byteLength);
281             }
282         }
283         return usedBufferViews_[bufferViewIndex].get();
284     }
285 
286     /* StoreAccessor first stores the bufferView used by accessor, then hashes it and stores if needed. */
StoreAccessor(const Accessor & accessor)287     Accessor* StoreAccessor(const Accessor& accessor)
288     {
289         BufferView* bufferView = nullptr;
290         if (accessor.bufferView) {
291             bufferView = StoreBufferView(*accessor.bufferView, GetComponentByteSize(accessor.componentType));
292         }
293         const auto accessorHash = BASE_NS::Hash(bufferView, accessor.componentType, accessor.count, accessor.type,
294             accessor.byteOffset, accessor.normalized, accessor.sparse.count, accessor.sparse.indices.bufferView,
295             accessor.sparse.indices.byteOffset, accessor.sparse.indices.componentType,
296             accessor.sparse.values.bufferView, accessor.sparse.values.byteOffset);
297 
298         const auto accessorIndex = FindOrAddIndex(accessorHashes_, accessorHash);
299         if ((accessorIndex + 1) > usedAccessors_.size()) {
300             usedAccessors_.resize(accessorIndex + 1);
301             usedAccessors_[accessorIndex] = make_unique<Accessor>(accessor);
302             usedAccessors_[accessorIndex]->bufferView = bufferView;
303         }
304         return usedAccessors_[accessorIndex].get();
305     };
306 
GetBuffer() const307     Buffer& GetBuffer() const
308     {
309         return buffer_;
310     }
311 
312 private:
313     vector<uint64_t> bufferViewHashes_;
314     vector<uint64_t> accessorHashes_;
315 
316     vector<unique_ptr<BufferView>>& usedBufferViews_;
317     vector<unique_ptr<Accessor>>& usedAccessors_;
318     Buffer& buffer_;
319 };
320 
321 struct ResourceEntity {
322     Entity entity;
323     RenderHandleReference handle;
324 };
325 
326 class TextureHelper {
327 public:
GetSamplerIndex(const RenderHandleReference & sampler)328     auto GetSamplerIndex(const RenderHandleReference& sampler)
329     {
330         return FindOrAddIndex(usedSamplers_, sampler);
331     }
332 
GetImageIndex(const RenderHandleReference & image)333     auto GetImageIndex(const RenderHandleReference& image)
334     {
335         return FindOrAddIndex(usedImages_, image);
336     }
337 
338     // Helper which returns the index of the "texture", i.e. sampler and image index pair.
GetTextureIndex(uint32_t samplerIndex,uint32_t imageIndex)339     auto GetTextureIndex(uint32_t samplerIndex, uint32_t imageIndex)
340     {
341         const auto textureHash = BASE_NS::Hash(samplerIndex, imageIndex);
342         const auto textureIndex = FindOrAddIndex(textureHashes_, textureHash);
343         if ((textureIndex + 1) >= usedTextures_.size()) {
344             usedTextures_.resize(textureIndex + 1);
345             usedTextures_[textureIndex] = { samplerIndex, imageIndex };
346         }
347         return textureIndex;
348     }
349 
HasSamplers() const350     bool HasSamplers() const
351     {
352         return !usedSamplers_.empty();
353     }
354 
HasImages() const355     bool HasImages() const
356     {
357         return !usedImages_.empty();
358     }
359 
HasTextures() const360     bool HasTextures() const
361     {
362         return !usedTextures_.empty();
363     }
364 
365     /* Generate GLTF2::Samplers from gathered sampler resource handlers. */
GenerateGltfSamplers(IGpuResourceManager const & gpuResourceManager) const366     vector<unique_ptr<Sampler>> GenerateGltfSamplers(IGpuResourceManager const& gpuResourceManager) const
367     {
368         vector<unique_ptr<Sampler>> samplerArray;
369         samplerArray.reserve(usedSamplers_.size());
370         for (const auto& gpuSamplerHandle : usedSamplers_) {
371             auto& exportSampler = samplerArray.emplace_back(make_unique<Sampler>());
372             const auto& samplerDesc =
373                 gpuResourceManager.GetSamplerDescriptor(gpuResourceManager.Get(gpuSamplerHandle.GetHandle()));
374             exportSampler->magFilter = GetFilterMode(samplerDesc.magFilter);
375             exportSampler->minFilter = GetFilterMode(samplerDesc.minFilter, samplerDesc.mipMapMode);
376             exportSampler->wrapS = GetWrapMode(samplerDesc.addressModeU);
377             exportSampler->wrapT = GetWrapMode(samplerDesc.addressModeV);
378         }
379         return samplerArray;
380     }
381 
382     /* Generate GLTF2::Images from gathered image resource handlers. */
GenerateGltfImages(const vector<ResourceEntity> & resourceEnties,const IUriComponentManager & uriManager) const383     vector<unique_ptr<Image>> GenerateGltfImages(
384         const vector<ResourceEntity>& resourceEnties, const IUriComponentManager& uriManager) const
385     {
386         vector<unique_ptr<Image>> imageArray;
387         imageArray.reserve(usedImages_.size());
388 
389         for (const auto& gpuImageHandle : usedImages_) {
390             auto& exportImage = imageArray.emplace_back(make_unique<Image>());
391             // sorted for find performance
392             if (const auto pos = std::lower_bound(resourceEnties.begin(), resourceEnties.end(), gpuImageHandle,
393                     [](ResourceEntity const& info, const RenderHandleReference& gpuImageHandle) {
394                         return info.handle < gpuImageHandle;
395                     });
396                 pos != resourceEnties.end() && pos->handle == gpuImageHandle) {
397                 if (auto id = uriManager.GetComponentId(pos->entity); id != IComponentManager::INVALID_COMPONENT_ID) {
398                     exportImage->uri = uriManager.Get(id).uri;
399                 }
400             }
401         }
402         return imageArray;
403     }
404 
405     /* Combines Sampler Image -pairs into GLTF2::Textures. */
GenerateGltfTextures(vector<unique_ptr<Sampler>> const & samplers,vector<unique_ptr<Image>> const & image) const406     vector<unique_ptr<Texture>> GenerateGltfTextures(
407         vector<unique_ptr<Sampler>> const& samplers, vector<unique_ptr<Image>> const& image) const
408     {
409         vector<unique_ptr<Texture>> textureArray;
410         textureArray.reserve(usedTextures_.size());
411         for (const auto& samplerImage : usedTextures_) {
412             auto& exportTexture = textureArray.emplace_back(make_unique<Texture>());
413             if (samplerImage.first < samplers.size()) {
414                 exportTexture->sampler = samplers[samplerImage.first].get();
415             }
416             exportTexture->image = image[samplerImage.second].get();
417         }
418         return textureArray;
419     }
420 
421 private:
422     vector<RenderHandleReference> usedSamplers_;
423     vector<RenderHandleReference> usedImages_;
424 
425     // Each "texture" is an index pair into usedSamplers and usedImages.
426     vector<std::pair<size_t, size_t>> usedTextures_;
427     vector<uint64_t> textureHashes_;
428 };
429 
430 /* Tries to load the glTF resource pointed by URI (<file URI/resource type/resource index>).
431  * If the file can be loaded data and index of the resource are returned. */
ResolveGltfAndResourceIndex(string_view uri,IFileManager & fileManager,unordered_map<string,IGLTFData::Ptr> & originalGltfs)432 std::pair<Data*, size_t> ResolveGltfAndResourceIndex(
433     string_view uri, IFileManager& fileManager, unordered_map<string, IGLTFData::Ptr>& originalGltfs)
434 {
435     const auto resolveGltfAndResourceIndex =
436         [](string_view originalUri, string_view extension, IFileManager& fileManager,
437             unordered_map<string, IGLTFData::Ptr>& originalGltfs) -> std::pair<Data*, size_t> {
438         const auto gltfPos = originalUri.find(extension);
439         if (gltfPos == string_view::npos) {
440             return { nullptr, GLTF_INVALID_INDEX };
441         }
442         auto uri = string_view(originalUri);
443         const auto resourceIndexString = uri.substr(uri.rfind('/') + 1);
444         uri.remove_suffix(uri.size() - gltfPos - extension.size());
445 
446         size_t resourceIndex = GLTF_INVALID_INDEX;
447         if (const auto result = std::from_chars(
448                 resourceIndexString.data(), resourceIndexString.data() + resourceIndexString.size(), resourceIndex);
449             result.ec != std::errc()) {
450             return { nullptr, GLTF_INVALID_INDEX };
451         }
452 
453         auto pos = originalGltfs.find(uri);
454         if (pos == originalGltfs.end()) {
455             auto loadResult = LoadGLTF(fileManager, uri);
456             if (!loadResult.success || !loadResult.data->LoadBuffers()) {
457                 return { nullptr, GLTF_INVALID_INDEX };
458             }
459             auto inserted = originalGltfs.insert({ uri, move(loadResult.data) });
460             pos = inserted.first;
461         }
462         if (pos != originalGltfs.end()) {
463             return { static_cast<Data*>(pos->second.get()), resourceIndex };
464         } else {
465             return { nullptr, GLTF_INVALID_INDEX };
466         }
467     };
468     if (auto result = resolveGltfAndResourceIndex(uri, ".gltf", fileManager, originalGltfs); result.first) {
469         return result;
470     }
471 
472     if (auto result = resolveGltfAndResourceIndex(uri, ".glb", fileManager, originalGltfs); result.first) {
473         return result;
474     }
475 
476     return { nullptr, GLTF_INVALID_INDEX };
477 }
478 
ExportGltfCameras(const IEcs & ecs,const Entities & entities,ExportResult & result)479 void ExportGltfCameras(const IEcs& ecs, const Entities& entities, ExportResult& result)
480 {
481     if (!entities.withCamera.empty() && entities.withCamera.size() == result.data->cameras.size()) {
482         if (const auto cameraManager = GetManager<ICameraComponentManager>(ecs); cameraManager) {
483             auto cameraIterator = result.data->cameras.begin();
484 
485             for (const auto cameraEntity : entities.withCamera) {
486                 auto& exportCamera = *cameraIterator++;
487 
488                 const auto cameraComponent = cameraManager->Get(cameraEntity);
489                 switch (cameraComponent.projection) {
490                     case CameraComponent::Projection::ORTHOGRAPHIC: {
491                         exportCamera->type = CameraType::ORTHOGRAPHIC;
492                         exportCamera->attributes.ortho.xmag = cameraComponent.xMag;
493                         exportCamera->attributes.ortho.ymag = cameraComponent.yMag;
494                         exportCamera->attributes.ortho.zfar = cameraComponent.zFar;
495                         exportCamera->attributes.ortho.znear = cameraComponent.zNear;
496                         break;
497                     }
498                     case CameraComponent::Projection::PERSPECTIVE: {
499                         exportCamera->type = CameraType::PERSPECTIVE;
500                         exportCamera->attributes.perspective.aspect = cameraComponent.aspect;
501                         exportCamera->attributes.perspective.yfov = cameraComponent.yFov;
502                         exportCamera->attributes.perspective.zfar = cameraComponent.zFar;
503                         exportCamera->attributes.perspective.znear = cameraComponent.zNear;
504                         break;
505                     }
506                     case CameraComponent::Projection::CUSTOM:
507                     default: {
508                         exportCamera->type = CameraType::INVALID;
509 
510                         CORE_LOG_E("cannot export camera %u", static_cast<uint32_t>(cameraComponent.projection));
511 
512                         result.error += "failed to export camera";
513                         result.success = false;
514                         break;
515                     }
516                 }
517             }
518         }
519     }
520 }
521 
ExportGltfLight(const IEcs & ecs,const Entities & entities,ExportResult & result)522 void ExportGltfLight(const IEcs& ecs, const Entities& entities, ExportResult& result)
523 {
524 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
525 
526     if (!entities.withLight.empty() && entities.withLight.size() == result.data->lights.size()) {
527         if (const auto lightManager = GetManager<ILightComponentManager>(ecs); lightManager) {
528             auto lightIterator = result.data->lights.begin();
529 
530             for (const auto lightEntity : entities.withLight) {
531                 auto& exportLight = *lightIterator++;
532 
533                 const auto lightComponent = lightManager->Get(lightEntity);
534                 switch (lightComponent.type) {
535                     default:
536                         CORE_LOG_E("cannot export light %u", static_cast<uint32_t>(lightComponent.type));
537                         exportLight->type = LightType::DIRECTIONAL;
538 
539                         result.error += "failed to export light";
540                         result.success = false;
541                         break;
542                     case LightComponent::Type::DIRECTIONAL:
543                         exportLight->type = LightType::DIRECTIONAL;
544                         break;
545                     case LightComponent::Type::POINT:
546                         exportLight->type = LightType::POINT;
547                         break;
548                     case LightComponent::Type::SPOT:
549                         exportLight->type = LightType::SPOT;
550                         break;
551                 }
552                 exportLight->color = lightComponent.color;
553                 exportLight->intensity = lightComponent.intensity;
554                 exportLight->positional.range = lightComponent.range;
555 
556                 exportLight->positional.spot.innerAngle = lightComponent.spotInnerAngle;
557                 exportLight->positional.spot.outerAngle = lightComponent.spotOuterAngle;
558 
559                 exportLight->shadow.shadowCaster = lightComponent.shadowEnabled;
560             }
561         }
562     }
563 #endif
564 }
565 
StoreInverseBindMatrices(array_view<const Math::Mat4X4> ibls,BufferHelper & bufferHelper)566 Accessor* StoreInverseBindMatrices(array_view<const Math::Mat4X4> ibls, BufferHelper& bufferHelper)
567 {
568     vector<Math::Mat4X4> inverseBindMatrices(ibls.cbegin(), ibls.cend());
569     if (!inverseBindMatrices.empty()) {
570         auto matrixData = array_view(
571             reinterpret_cast<uint8_t*>(inverseBindMatrices.data()), inverseBindMatrices.size() * sizeof(Math::Mat4X4));
572         BufferView bufferView;
573         bufferView.buffer = &bufferHelper.GetBuffer();
574         bufferView.byteLength = matrixData.size();
575         bufferView.data = matrixData.data();
576 
577         Accessor accessor;
578         accessor.bufferView = &bufferView;
579         accessor.byteOffset = 0;
580         accessor.componentType = ComponentType::FLOAT;
581         accessor.count = static_cast<uint32_t>(inverseBindMatrices.size());
582         accessor.type = DataType::MAT4;
583         return bufferHelper.StoreAccessor(accessor);
584     }
585     return nullptr;
586 }
587 
588 // helper for evaluating skeleton property for a skin
589 struct NodeDepth {
590     uint32_t depth;
591     GLTF2::Node* node;
592 
operator >GLTF2::__anon594c39960211::NodeDepth593     inline bool operator>(const NodeDepth& rhs) const noexcept
594     {
595         if (depth > rhs.depth) {
596             return true;
597         }
598         if (depth < rhs.depth) {
599             return false;
600         } else if (node < rhs.node) {
601             return true;
602         }
603         return false;
604     }
605 
operator ==GLTF2::__anon594c39960211::NodeDepth606     inline bool operator==(const NodeDepth& rhs) const noexcept
607     {
608         return (depth == rhs.depth) && (node == rhs.node);
609     }
610 };
611 
FindSkeletonRoot(array_view<GLTF2::Node * > joints)612 Node* FindSkeletonRoot(array_view<GLTF2::Node*> joints)
613 {
614     // find the skeleton root node
615     if (!joints.empty()) {
616         // for each joint calculate distance to the root
617         vector<NodeDepth> depths;
618         depths.reserve(joints.size());
619         for (Node* joint : joints) {
620             uint32_t depth = 0;
621             for (Node* parent = joint->parent; parent; parent = parent->parent) {
622                 ++depth;
623             }
624             depths.push_back({ depth, joint });
625         }
626 
627         // sort by depth and node
628         std::sort(depths.begin(), depths.end(), std::greater<NodeDepth>());
629 
630         // reduce the numer of nodes until one remains (or there are multiple roots)
631         for (auto start = depths.begin(); depths.size() > 1 && start->depth; start = depths.begin()) {
632             // select a range of nodes at an equal depth
633             auto end = std::upper_bound(start, depths.end(), start->depth,
634                 [](uint32_t depth, const NodeDepth& current) { return current.depth < depth; });
635             // replace each node with its parent
636             for (auto& data : array_view(start.ptr(), end.ptr())) {
637                 data.node = data.node->parent;
638                 data.depth -= 1;
639             }
640             // sort again according to updated depths and nodes
641             std::sort(depths.begin(), depths.end(), std::greater<NodeDepth>());
642 
643             // remove duplicates
644             depths.erase(std::unique(start, depths.end()), depths.end());
645         }
646 
647         return (depths.size() == 1) ? depths.front().node : nullptr;
648     }
649     return nullptr;
650 }
651 
ExportGltfSkins(const IEcs & ecs,const Entities & entities,const vector<unique_ptr<Node>> & nodeArray,ExportResult & result,BufferHelper & bufferHelper)652 void ExportGltfSkins(const IEcs& ecs, const Entities& entities, const vector<unique_ptr<Node>>& nodeArray,
653     ExportResult& result, BufferHelper& bufferHelper)
654 {
655     if (entities.withSkin.empty() || (entities.withSkin.size() != result.data->skins.size())) {
656         return;
657     }
658     const auto* skinManager = GetManager<ISkinComponentManager>(ecs);
659     const auto* skinIbmManager = GetManager<ISkinIbmComponentManager>(ecs);
660     const auto* skinJointsManager = GetManager<ISkinJointsComponentManager>(ecs);
661     if (!skinManager || !skinIbmManager || !skinJointsManager) {
662         return;
663     }
664 
665     auto skinIterator = result.data->skins.begin();
666     for (const auto& skinnedEntity : entities.withSkin) {
667         auto& exportSkin = *skinIterator++;
668         const auto skinComponent = skinManager->Get(skinnedEntity);
669         if (!EntityUtil::IsValid(skinComponent.skin)) {
670             continue;
671         }
672         // store IBMs in the buffer handled by BufferHelper
673         if (const auto ibmHandle = skinIbmManager->Read(skinComponent.skin); ibmHandle) {
674             exportSkin->inverseBindMatrices = StoreInverseBindMatrices(ibmHandle->matrices, bufferHelper);
675         }
676 
677         // find the skeleton root node
678         exportSkin->skeleton = FindSkeletonRoot(exportSkin->joints);
679         if (!exportSkin->skeleton) {
680             CORE_LOG_D("Couldn't find common root for skinned entity %s", to_hex(skinnedEntity.id).data());
681         }
682 
683         // gather all the joint nodes
684         const auto skinJointsHandle = skinJointsManager->Read(skinnedEntity);
685         if (!skinJointsHandle) {
686             continue;
687         }
688         exportSkin->joints.reserve(skinJointsHandle->count);
689         for (const auto& jointEntity : array_view(skinJointsHandle->jointEntities, skinJointsHandle->count)) {
690             const auto jointIndex = FindHandleIndex(entities.nodes, jointEntity);
691             if (jointIndex < nodeArray.size()) {
692                 exportSkin->joints.push_back(nodeArray[jointIndex].get());
693             } else {
694                 CORE_LOG_D("joint node not exported");
695             }
696         }
697     }
698 }
699 
GetAnimationTarget(const INodeSystem & nodeSystem,const INameComponentManager & nameManager,const Entities & entities,array_view<const unique_ptr<Node>> nodes,const Entity trackEntity,const AnimationTrackComponent & trackComponent)700 Node* GetAnimationTarget(const INodeSystem& nodeSystem, const INameComponentManager& nameManager,
701     const Entities& entities, array_view<const unique_ptr<Node>> nodes, const Entity trackEntity,
702     const AnimationTrackComponent& trackComponent)
703 {
704     Node* target = nullptr;
705     if (auto animatedNode = nodeSystem.GetNode(trackComponent.target); animatedNode) {
706         if (const auto nodeIndex = FindHandleIndex(entities.nodes, static_cast<Entity>(trackComponent.target));
707             nodeIndex < nodes.size()) {
708             target = nodes[nodeIndex].get();
709         }
710     }
711     if (!target) {
712         string_view nodePath;
713         if (const auto nameHandle = nameManager.Read(trackEntity); nameHandle) {
714             nodePath = nameHandle->name;
715         }
716         CORE_LOG_W("couldn't resolve node path: %s", nodePath.data());
717     }
718     return target;
719 }
720 
AnimationInput(const IAnimationInputComponentManager & inputManager,const Entity & animationInput,BufferHelper & bufferHelper)721 Accessor* AnimationInput(
722     const IAnimationInputComponentManager& inputManager, const Entity& animationInput, BufferHelper& bufferHelper)
723 {
724     if (auto inputHandle = inputManager.Read(animationInput); inputHandle) {
725         // BufferView data is not const although at this point it's not modified.
726         auto inputData = array_view(reinterpret_cast<uint8_t*>(const_cast<float*>(inputHandle->timestamps.data())),
727             inputHandle->timestamps.size_in_bytes());
728 
729         BufferView bufferView;
730         bufferView.buffer = reinterpret_cast<Buffer*>(static_cast<uintptr_t>(animationInput.id));
731         bufferView.byteLength = inputData.size();
732         bufferView.data = inputData.data();
733 
734         Accessor accessor;
735         accessor.bufferView = &bufferView;
736         accessor.byteOffset = 0;
737         accessor.componentType = ComponentType::FLOAT;
738         accessor.count = static_cast<uint32_t>(inputHandle->timestamps.size());
739         accessor.type = DataType::SCALAR;
740 
741         auto inputAccessor = bufferHelper.StoreAccessor(accessor);
742         // The accessor used for animation.sampler.input requires min and max values.
743         if (inputAccessor && (inputAccessor->min.empty() || inputAccessor->max.empty())) {
744             auto input =
745                 array_view(reinterpret_cast<float const*>(inputAccessor->bufferView->data + inputAccessor->byteOffset),
746                     inputAccessor->count);
747             auto inputMin = std::numeric_limits<float>::max();
748             auto inputMax = std::numeric_limits<float>::lowest();
749             for (const auto value : input) {
750                 inputMin = std::min(value, inputMin);
751                 inputMax = std::max(value, inputMax);
752             }
753             inputAccessor->min.push_back(inputMin);
754             inputAccessor->max.push_back(inputMax);
755         }
756 
757         return inputAccessor;
758     }
759     return {};
760 }
761 
AnimationOutput(const IAnimationOutputComponentManager & outputManager,const Entity & animationOutput,AnimationPath type,BufferHelper & bufferHelper)762 Accessor* AnimationOutput(const IAnimationOutputComponentManager& outputManager, const Entity& animationOutput,
763     AnimationPath type, BufferHelper& bufferHelper)
764 {
765     Accessor accessor;
766     // Setup the accessor to match the keyframe data for current animation type. Translation and scale are vec3s,
767     // rotation is quaternions, and morph animation is floats.
768     auto outputData = array_view<const uint8_t>();
769     if (auto outputHandle = outputManager.Read(animationOutput); outputHandle) {
770         outputData = outputHandle->data;
771         switch (type) {
772             case AnimationPath::TRANSLATION:
773             case AnimationPath::SCALE: {
774                 accessor.count = static_cast<uint32_t>(outputData.size() / sizeof(Math::Vec3));
775                 accessor.type = DataType::VEC3;
776             } break;
777             case AnimationPath::ROTATION: {
778                 accessor.count = static_cast<uint32_t>(outputData.size() / sizeof(Math::Vec4));
779                 accessor.type = DataType::VEC4;
780             } break;
781             case AnimationPath::WEIGHTS: {
782                 accessor.count = static_cast<uint32_t>(outputData.size() / sizeof(float));
783                 accessor.type = DataType::SCALAR;
784             } break;
785             default:
786                 return nullptr;
787         }
788     }
789     BufferView bufferView;
790     bufferView.buffer = reinterpret_cast<Buffer*>(static_cast<uintptr_t>(animationOutput.id));
791     bufferView.byteLength = outputData.size();
792     bufferView.data = outputData.data();
793 
794     accessor.bufferView = &bufferView;
795     accessor.byteOffset = 0;
796     accessor.componentType = ComponentType::FLOAT;
797 
798     return bufferHelper.StoreAccessor(accessor);
799 }
800 
CreateAnimationSampler(const AnimationTrackComponent & trackComponent,const IAnimationInputComponentManager & animationInputManager,const IAnimationOutputComponentManager & animationOutputManager,BufferHelper & bufferHelper)801 unique_ptr<AnimationSampler> CreateAnimationSampler(const AnimationTrackComponent& trackComponent,
802     const IAnimationInputComponentManager& animationInputManager,
803     const IAnimationOutputComponentManager& animationOutputManager, BufferHelper& bufferHelper)
804 {
805     auto exportSampler = make_unique<AnimationSampler>();
806     exportSampler->interpolation = GetAnimationInterpolation(trackComponent.interpolationMode);
807     exportSampler->input = AnimationInput(animationInputManager, trackComponent.timestamps, bufferHelper);
808     exportSampler->output =
809         AnimationOutput(animationOutputManager, trackComponent.data, GetAnimationPath(trackComponent), bufferHelper);
810     return exportSampler;
811 }
812 
CleanupAnimation(Animation & exportAnimation)813 void CleanupAnimation(Animation& exportAnimation)
814 {
815     // Remove all tracks that don't have a node, sampler or sampler is missing input or output.
816     exportAnimation.tracks.erase(std::find_if(exportAnimation.tracks.begin(), exportAnimation.tracks.end(),
817                                      [](const AnimationTrack& track) {
818                                          return !track.channel.node || !track.sampler || !track.sampler->input ||
819                                                 !track.sampler->output;
820                                      }),
821         exportAnimation.tracks.end());
822 
823     // Remove all samplers missing input or output.
824     exportAnimation.samplers.erase(
825         std::find_if(exportAnimation.samplers.begin(), exportAnimation.samplers.end(),
826             [](const unique_ptr<AnimationSampler>& sampler) { return !sampler->input || !sampler->output; }),
827         exportAnimation.samplers.end());
828 }
829 
Hash(const AnimationTrackComponent & trackComponent)830 uint64_t Hash(const AnimationTrackComponent& trackComponent)
831 {
832     return BASE_NS::Hash(static_cast<const Entity&>(trackComponent.timestamps).id,
833         static_cast<const Entity&>(trackComponent.data).id, static_cast<uint32_t>(trackComponent.interpolationMode));
834 }
835 
ExportGltfAnimations(const IEcs & ecs,const Entities & entities,ExportResult & result,BufferHelper & bufferHelper)836 void ExportGltfAnimations(const IEcs& ecs, const Entities& entities, ExportResult& result, BufferHelper& bufferHelper)
837 {
838     const auto nodeSystem = GetSystem<INodeSystem>(ecs);
839     const auto animationSystem = GetSystem<IAnimationSystem>(ecs);
840     const auto animationManager = GetManager<IAnimationComponentManager>(ecs);
841     const auto animationInputManager = GetManager<IAnimationInputComponentManager>(ecs);
842     const auto animationOutputManager = GetManager<IAnimationOutputComponentManager>(ecs);
843     const auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs);
844     const auto nameManager = GetManager<INameComponentManager>(ecs);
845     if (!nodeSystem || !animationSystem || !animationManager || !animationInputManager || !animationOutputManager ||
846         !animationTrackManager || !nameManager) {
847         return;
848     }
849     const auto animationCount = animationManager->GetComponentCount();
850     auto& animationArray = result.data->animations;
851     animationArray.reserve(animationCount);
852     auto exportTracks = [&](const array_view<const EntityReference> tracks, vector<uint64_t>& samplerHashes,
853                             Animation& exportAnimation) {
854         for (const auto& trackEntity : tracks) {
855             const auto trackHandle = animationTrackManager->Read(trackEntity);
856             if (!trackHandle) {
857                 continue;
858             }
859             const auto samplerIndex = FindOrAddIndex(samplerHashes, Hash(*trackHandle));
860             if ((samplerIndex + 1) >= exportAnimation.samplers.size()) {
861                 exportAnimation.samplers.resize(samplerIndex + 1);
862                 exportAnimation.samplers[samplerIndex] =
863                     CreateAnimationSampler(*trackHandle, *animationInputManager, *animationOutputManager, bufferHelper);
864             }
865             const auto target =
866                 GetAnimationTarget(*nodeSystem, *nameManager, entities, result.data->nodes, trackEntity, *trackHandle);
867             exportAnimation.tracks.push_back(AnimationTrack {
868                 { target, GetAnimationPath(*trackHandle) }, exportAnimation.samplers[samplerIndex].get() });
869         }
870     };
871     for (IComponentManager::ComponentId i = 0U; i < animationCount; ++i) {
872         auto& exportAnimation = animationArray.emplace_back(make_unique<Animation>());
873         if (auto nameHandle = nameManager->Read(animationManager->GetEntity(i)); nameHandle) {
874             exportAnimation->name = nameHandle->name;
875         }
876 
877         // animation.samplers can be shared between channels. Identify samplers by hashing input, output and
878         // interpolationMode.
879         vector<uint64_t> samplerHashes;
880 
881         if (const auto animationHandle = animationManager->Read(i); animationHandle) {
882             exportTracks(animationHandle->tracks, samplerHashes, *exportAnimation);
883         }
884 
885         CleanupAnimation(*exportAnimation);
886 
887         // Pop the animation if it's empty.
888         if (exportAnimation->samplers.empty() || exportAnimation->tracks.empty()) {
889             animationArray.pop_back();
890         }
891     }
892 }
893 
894 struct MeshPrimitiveGenerator {
895     const IMaterialComponentManager& materialManager;
896     BufferHelper& buffer;
897     vector<Entity>& usedMaterials;
898 
operator ()GLTF2::__anon594c39960211::MeshPrimitiveGenerator899     MeshPrimitive operator()(const MeshComponent::Submesh& submesh, const MeshPrimitive& original)
900     {
901         MeshPrimitive copy;
902         if (original.indices) {
903             copy.indices = buffer.StoreAccessor(*original.indices);
904         }
905         for (const auto& attrib : original.attributes) {
906             copy.attributes.push_back(Attribute { attrib.attribute, buffer.StoreAccessor(*attrib.accessor) });
907         }
908         if (materialManager.HasComponent(submesh.material)) {
909             copy.materialIndex = FindOrAddIndex(usedMaterials, submesh.material);
910         }
911         for (const auto& target : original.targets) {
912             MorphTarget morphTarget { target.name, {} };
913             for (const auto& attribute : target.target) {
914                 morphTarget.target.push_back(
915                     Attribute { attribute.attribute, buffer.StoreAccessor(*attribute.accessor) });
916             }
917             copy.targets.push_back(move(morphTarget));
918         }
919 
920         return copy;
921     }
922 };
923 
ResetInvalidMeshes(array_view<const unique_ptr<Node>> nodes,const Mesh * invalidMesh)924 void ResetInvalidMeshes(array_view<const unique_ptr<Node>> nodes, const Mesh* invalidMesh)
925 {
926     for (const auto& node : nodes) {
927         if (node->mesh == invalidMesh) {
928             node->mesh = nullptr;
929         }
930     }
931 }
932 
ExportGltfMeshes(const IMeshComponentManager & meshManager,const INameComponentManager & nameManager,const IUriComponentManager & uriManager,const IMaterialComponentManager & materialManager,IFileManager & fileManager,const vector<Entity> & usedMeshes,ExportResult & result,BufferHelper & buffer,unordered_map<string,IGLTFData::Ptr> & originalGltfs)933 vector<Entity> ExportGltfMeshes(const IMeshComponentManager& meshManager, const INameComponentManager& nameManager,
934     const IUriComponentManager& uriManager, const IMaterialComponentManager& materialManager, IFileManager& fileManager,
935     const vector<Entity>& usedMeshes, ExportResult& result, BufferHelper& buffer,
936     unordered_map<string, IGLTFData::Ptr>& originalGltfs)
937 {
938     vector<Entity> usedMaterials;
939     CORE_ASSERT(usedMeshes.size() == result.data->meshes.size());
940     if (usedMeshes.empty() || usedMeshes.size() != result.data->meshes.size()) {
941         return usedMaterials;
942     }
943     auto meshIterator = result.data->meshes.begin();
944 
945     // Create GLTF2::Meshes from mesh entities
946     for (const auto& meshEntity : usedMeshes) {
947         auto& exportMesh = *meshIterator++;
948 
949         const auto uriId = uriManager.GetComponentId(meshEntity);
950         if (uriId == IComponentManager::INVALID_COMPONENT_ID) {
951             continue;
952         }
953         const auto uri = uriManager.Read(uriId)->uri;
954         // mesh data is copied from the original glTF pointer by UriComponent
955         if (const auto [originalGltf, meshIndex] = ResolveGltfAndResourceIndex(uri, fileManager, originalGltfs);
956             originalGltf && meshIndex < originalGltf->meshes.size()) {
957             const auto meshHandle = meshManager.Read(meshEntity);
958 
959             const auto& submeshes = meshHandle->submeshes;
960             const auto& originalPrimitives = originalGltf->meshes[meshIndex]->primitives;
961             std::transform(submeshes.cbegin(), submeshes.cend(), originalPrimitives.cbegin(),
962                 std::back_inserter(exportMesh->primitives),
963                 MeshPrimitiveGenerator { materialManager, buffer, usedMaterials });
964 
965             if (const auto nameHandle = nameManager.Read(meshEntity)) {
966                 exportMesh->name = nameHandle->name;
967             }
968             // NOTE: exportMesh->weights
969         } else {
970             // couldn't find original glTF.
971             ResetInvalidMeshes(result.data->nodes, exportMesh.get());
972             exportMesh.reset();
973         }
974     }
975     result.data->meshes.erase(
976         std::remove(result.data->meshes.begin(), result.data->meshes.end(), nullptr), result.data->meshes.end());
977 
978     return usedMaterials;
979 }
980 
GetTextureIndex(const MaterialComponent & materialDesc,const MaterialComponent::TextureIndex textureIndex,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)981 inline uint32_t GetTextureIndex(const MaterialComponent& materialDesc,
982     const MaterialComponent::TextureIndex textureIndex, TextureHelper& textureHelper,
983     const IRenderHandleComponentManager& gpuHandleManager)
984 {
985     RenderHandleReference image;
986     if (auto handle = gpuHandleManager.Read(materialDesc.textures[textureIndex].image); handle) {
987         image = handle->reference;
988     }
989     if (image) {
990         const auto imageIndex = textureHelper.GetImageIndex(image);
991         RenderHandleReference sampler;
992         if (auto handle = gpuHandleManager.Read(materialDesc.textures[textureIndex].sampler); handle) {
993             sampler = handle->reference;
994         }
995         const auto samplerIndex = (sampler) ? textureHelper.GetSamplerIndex(sampler) : 0xFFFFffff;
996         return textureHelper.GetTextureIndex(samplerIndex, imageIndex);
997     }
998     return GLTF_INVALID_INDEX;
999 }
1000 
ExportGltfMaterialMetallicRoughness(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1001 void ExportGltfMaterialMetallicRoughness(Material& exportMaterial, const MaterialComponent& materialDesc,
1002     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1003 {
1004     exportMaterial.type = Material::Type::MetallicRoughness;
1005     exportMaterial.metallicRoughness.baseColorFactor =
1006         materialDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor;
1007     exportMaterial.metallicRoughness.baseColorTexture.index =
1008         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::BASE_COLOR, textureHelper, gpuHandleManager);
1009     exportMaterial.metallicRoughness.metallicFactor =
1010         materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.z;
1011     exportMaterial.metallicRoughness.roughnessFactor =
1012         materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.y;
1013     exportMaterial.metallicRoughness.metallicRoughnessTexture.index =
1014         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::MATERIAL, textureHelper, gpuHandleManager);
1015 }
1016 
1017 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT) || defined(GLTF2_EXTRAS_CLEAR_COAT_MATERIAL)
ExportGltfMaterialClearcoat(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1018 void ExportGltfMaterialClearcoat(Material& exportMaterial, const MaterialComponent& materialDesc,
1019     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1020 {
1021     // Clearcoat (must not be used with pbrSpecularGlossiness or unlit).
1022     if (materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT].factor.x > 0.0f) {
1023         exportMaterial.clearcoat.factor = materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT].factor.x;
1024         exportMaterial.clearcoat.roughness =
1025             materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS].factor.y;
1026         exportMaterial.clearcoat.texture.index =
1027             GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::CLEARCOAT, textureHelper, gpuHandleManager);
1028         exportMaterial.clearcoat.roughnessTexture.index = GetTextureIndex(
1029             materialDesc, MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS, textureHelper, gpuHandleManager);
1030         exportMaterial.clearcoat.normalTexture.textureInfo.index = GetTextureIndex(
1031             materialDesc, MaterialComponent::TextureIndex::CLEARCOAT_NORMAL, textureHelper, gpuHandleManager);
1032         exportMaterial.clearcoat.normalTexture.scale =
1033             materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT_NORMAL].factor.x;
1034     }
1035 }
1036 #endif
1037 
1038 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
ExportGltfMaterialIor(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1039 void ExportGltfMaterialIor(Material& exportMaterial, const MaterialComponent& materialDesc,
1040     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1041 {
1042     // IOR (must not be used with pbrSpecularGlossiness or unlit).
1043     if (materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.w != 0.04f) {
1044         const auto refSqr = Math::sqrt(materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.w);
1045         exportMaterial.ior.ior = (1.f + refSqr) / (1.f - refSqr);
1046     }
1047 }
1048 #endif
1049 
1050 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
ExportGltfMaterialSheen(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1051 void ExportGltfMaterialSheen(Material& exportMaterial, const MaterialComponent& materialDesc,
1052     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1053 {
1054     // Sheen (must not be used with pbrSpecularGlossiness or unlit).
1055     if ((materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor.x > 0.0f) ||
1056         (materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor.y > 0.0f) ||
1057         (materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor.z > 0.0f)) {
1058         exportMaterial.sheen.factor = materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor;
1059         exportMaterial.sheen.texture.index =
1060             GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::SHEEN, textureHelper, gpuHandleManager);
1061     }
1062 }
1063 #endif
1064 
1065 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
ExportGltfMaterialSpecular(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1066 void ExportGltfMaterialSpecular(Material& exportMaterial, const MaterialComponent& materialDesc,
1067     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1068 {
1069     // Specular (must not be used with pbrSpecularGlossiness or unlit).
1070     if (materialDesc.textures[MaterialComponent::TextureIndex::SPECULAR].factor != Math::Vec4(1.f, 1.f, 1.f, 1.f)) {
1071         exportMaterial.specular.factor = materialDesc.textures[MaterialComponent::TextureIndex::SPECULAR].factor.w;
1072         exportMaterial.specular.texture.index =
1073             GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::SPECULAR, textureHelper, gpuHandleManager);
1074         exportMaterial.specular.color = materialDesc.textures[MaterialComponent::TextureIndex::SPECULAR].factor;
1075         exportMaterial.specular.colorTexture.index =
1076             GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::SPECULAR, textureHelper, gpuHandleManager);
1077     }
1078 }
1079 #endif
1080 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
ExportGltfMaterialTransmission(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1081 void ExportGltfMaterialTransmission(Material& exportMaterial, const MaterialComponent& materialDesc,
1082     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1083 {
1084     // Transmission (must not be used with pbrSpecularGlossiness or unlit).
1085     if (materialDesc.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x > 0.0f) {
1086         exportMaterial.transmission.factor =
1087             materialDesc.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x;
1088         exportMaterial.transmission.texture.index = GetTextureIndex(
1089             materialDesc, MaterialComponent::TextureIndex::TRANSMISSION, textureHelper, gpuHandleManager);
1090     }
1091 }
1092 #endif
1093 
1094 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
ExportGltfMaterialSpecularGlossiness(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1095 void ExportGltfMaterialSpecularGlossiness(Material& exportMaterial, const MaterialComponent& materialDesc,
1096     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1097 {
1098     exportMaterial.type = Material::Type::SpecularGlossiness;
1099     exportMaterial.specularGlossiness.diffuseFactor =
1100         materialDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor;
1101     exportMaterial.specularGlossiness.diffuseTexture.index =
1102         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::BASE_COLOR, textureHelper, gpuHandleManager);
1103     exportMaterial.specularGlossiness.specularFactor =
1104         materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor;
1105     exportMaterial.specularGlossiness.specularGlossinessTexture.index =
1106         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::MATERIAL, textureHelper, gpuHandleManager);
1107     exportMaterial.specularGlossiness.glossinessFactor =
1108         materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.w;
1109 }
1110 #endif
1111 
1112 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_UNLIT)
ExportGltfMaterialUnlit(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1113 void ExportGltfMaterialUnlit(Material& exportMaterial, const MaterialComponent& materialDesc,
1114     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1115 {
1116     exportMaterial.type = Material::Type::Unlit;
1117     exportMaterial.metallicRoughness.baseColorFactor =
1118         materialDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor;
1119     exportMaterial.metallicRoughness.baseColorTexture.index =
1120         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::BASE_COLOR, textureHelper, gpuHandleManager);
1121 }
1122 #endif
1123 
UpdateShaderStateToGltfMaterial(const IDevice * device,Material & exportMaterial,const MaterialComponent & materialDesc,const IRenderHandleComponentManager & gpuHandleManager)1124 void UpdateShaderStateToGltfMaterial(const IDevice* device, Material& exportMaterial,
1125     const MaterialComponent& materialDesc, const IRenderHandleComponentManager& gpuHandleManager)
1126 {
1127     if ((!materialDesc.materialShader.shader && !materialDesc.materialShader.graphicsState) || !device) {
1128         return;
1129     }
1130     auto update = [](const IShaderManager& shaderMgr, const RenderHandleReference& gfxHandle,
1131                       Material& exportMaterial) {
1132         const GraphicsState gfxState = shaderMgr.GetGraphicsState(gfxHandle);
1133         exportMaterial.doubleSided =
1134             (gfxState.rasterizationState.cullModeFlags == CullModeFlagBits::CORE_CULL_MODE_NONE);
1135 
1136         if ((gfxState.colorBlendState.colorAttachmentCount > 0) &&
1137             (gfxState.colorBlendState.colorAttachments[0].enableBlend)) {
1138             exportMaterial.alphaMode = AlphaMode::BLEND;
1139         }
1140     };
1141     const IShaderManager& shaderMgr = device->GetShaderManager();
1142     if (materialDesc.materialShader.graphicsState) {
1143         const auto handle = gpuHandleManager.GetRenderHandleReference(materialDesc.materialShader.graphicsState);
1144         if (handle.GetHandleType() == RenderHandleType::GRAPHICS_STATE && handle) {
1145             update(shaderMgr, handle, exportMaterial);
1146         }
1147     } else if (materialDesc.materialShader.shader) {
1148         const auto handle = gpuHandleManager.GetRenderHandleReference(materialDesc.materialShader.shader);
1149         if (handle.GetHandleType() == RenderHandleType::SHADER_STATE_OBJECT && handle) {
1150             const RenderHandleReference gfxHandle = shaderMgr.GetGraphicsStateHandleByShaderHandle(handle);
1151             update(shaderMgr, gfxHandle, exportMaterial);
1152         }
1153     }
1154 }
1155 
ExportGltfMaterial(const IDevice * device,Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1156 void ExportGltfMaterial(const IDevice* device, Material& exportMaterial, const MaterialComponent& materialDesc,
1157     TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1158 {
1159     if (materialDesc.type == MaterialComponent::Type::METALLIC_ROUGHNESS) {
1160         ExportGltfMaterialMetallicRoughness(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1161 
1162 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT) || defined(GLTF2_EXTRAS_CLEAR_COAT_MATERIAL)
1163         ExportGltfMaterialClearcoat(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1164 #endif
1165 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
1166         ExportGltfMaterialIor(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1167 #endif
1168 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
1169         ExportGltfMaterialSheen(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1170 #endif
1171 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
1172         ExportGltfMaterialSpecular(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1173 #endif
1174 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
1175         ExportGltfMaterialTransmission(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1176 #endif
1177     } else if (materialDesc.type == MaterialComponent::Type::SPECULAR_GLOSSINESS) {
1178 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
1179         ExportGltfMaterialSpecularGlossiness(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1180 #endif
1181     } else if (materialDesc.type == MaterialComponent::Type::UNLIT) {
1182 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_UNLIT)
1183         ExportGltfMaterialUnlit(exportMaterial, materialDesc, textureHelper, gpuHandleManager);
1184 #endif
1185     }
1186 
1187     // Normal texture.
1188     exportMaterial.normalTexture.textureInfo.index =
1189         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::NORMAL, textureHelper, gpuHandleManager);
1190     exportMaterial.normalTexture.scale = materialDesc.textures[MaterialComponent::TextureIndex::NORMAL].factor.x;
1191 
1192     // Occlusion texture.
1193     exportMaterial.occlusionTexture.textureInfo.index =
1194         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::AO, textureHelper, gpuHandleManager);
1195     exportMaterial.occlusionTexture.strength = materialDesc.textures[MaterialComponent::TextureIndex::AO].factor.x;
1196 
1197     // Emissive texture.
1198     exportMaterial.emissiveTexture.index =
1199         GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::EMISSIVE, textureHelper, gpuHandleManager);
1200 
1201     exportMaterial.emissiveFactor = materialDesc.textures[MaterialComponent::TextureIndex::EMISSIVE].factor;
1202     exportMaterial.alphaCutoff = materialDesc.alphaCutoff;
1203     exportMaterial.alphaMode = AlphaMode::OPAQUE;
1204 
1205     UpdateShaderStateToGltfMaterial(device, exportMaterial, materialDesc, gpuHandleManager);
1206 
1207     // check if alpha is using cutoff / mask (Lume default is 1.0 -> no mask)
1208     if (materialDesc.alphaCutoff < 1.0f) {
1209         exportMaterial.alphaMode = AlphaMode::MASK;
1210     }
1211 }
1212 
ExportGltfMaterials(const IEngine & engine,const IMaterialComponentManager & materialManager,const INameComponentManager & nameManager,const IUriComponentManager & uriManager,const vector<Entity> & usedMaterials,ExportResult & result,BufferHelper & bufferHelper)1213 void ExportGltfMaterials(const IEngine& engine, const IMaterialComponentManager& materialManager,
1214     const INameComponentManager& nameManager, const IUriComponentManager& uriManager,
1215     const vector<Entity>& usedMaterials, ExportResult& result, BufferHelper& bufferHelper)
1216 {
1217     if (!usedMaterials.empty()) {
1218         IDevice* device = nullptr;
1219         if (auto context = GetInstance<IRenderContext>(*engine.GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
1220             context) {
1221             device = &context->GetDevice();
1222         }
1223         IRenderHandleComponentManager& gpuHandleManager =
1224             *GetManager<IRenderHandleComponentManager>(materialManager.GetEcs());
1225 
1226         TextureHelper textureHash;
1227 
1228         auto& materialArray = result.data->materials;
1229         materialArray.reserve(usedMaterials.size());
1230 
1231         // Create Materials and gather used samplers and images.
1232         for (const auto materialEntity : usedMaterials) {
1233             string_view name;
1234             if (const auto nameHandle = nameManager.Read(materialEntity); nameHandle) {
1235                 name = nameHandle->name;
1236             }
1237             if (const auto materialHandle = materialManager.Read(materialEntity); materialHandle) {
1238                 auto& exportMaterial = materialArray.emplace_back(make_unique<Material>());
1239                 const auto& material = *materialHandle;
1240                 exportMaterial->name = name;
1241                 ExportGltfMaterial(device, *exportMaterial, material, textureHash, gpuHandleManager);
1242             }
1243         }
1244 
1245         vector<ResourceEntity> resourceEnties;
1246         const auto gpuHandleComponents = gpuHandleManager.GetComponentCount();
1247         resourceEnties.reserve(gpuHandleComponents);
1248         // sorted for find performance
1249         for (auto i = 0u; i < gpuHandleComponents; ++i) {
1250             resourceEnties.push_back({ gpuHandleManager.GetEntity(i), gpuHandleManager.Get(i).reference });
1251         }
1252         std::sort(resourceEnties.begin(), resourceEnties.end(),
1253             [](const ResourceEntity& lhs, const ResourceEntity& rhs) { return lhs.handle < rhs.handle; });
1254 
1255         // Create Samplers
1256         if (device && textureHash.HasSamplers()) {
1257             result.data->samplers = textureHash.GenerateGltfSamplers(device->GetGpuResourceManager());
1258         }
1259 
1260         // Create Images
1261         if (textureHash.HasImages()) {
1262             result.data->images = textureHash.GenerateGltfImages(resourceEnties, uriManager);
1263         }
1264 
1265         // Create Textures
1266         if (textureHash.HasTextures()) {
1267             result.data->textures = textureHash.GenerateGltfTextures(result.data->samplers, result.data->images);
1268         }
1269     }
1270 }
1271 
ExportImageData(IFileManager & fileManager,ExportResult & result,BufferHelper & bufferHelper,unordered_map<string,IGLTFData::Ptr> & originalGltfs)1272 void ExportImageData(IFileManager& fileManager, ExportResult& result, BufferHelper& bufferHelper,
1273     unordered_map<string, IGLTFData::Ptr>& originalGltfs)
1274 {
1275     for (auto& image : result.data->images) {
1276         if (!image->uri.empty()) {
1277             // First check if the URI is from ResourceManager in the form of <file URI/resource type/resource
1278             // index>. If that fails, try to open the URI as a file.
1279             if (const auto [originalGltf, imageIndex] =
1280                     ResolveGltfAndResourceIndex(image->uri, fileManager, originalGltfs);
1281                 originalGltf && imageIndex < originalGltf->images.size()) {
1282                 // We can store data from the loaded bufferView.
1283                 auto& originalImage = originalGltf->images[imageIndex];
1284                 image->bufferView = bufferHelper.StoreBufferView(
1285                     *originalImage->bufferView, GetComponentByteSize(ComponentType::UNSIGNED_INT));
1286                 image->bufferView->target = BufferTarget::NOT_DEFINED;
1287                 image->type = originalImage->type;
1288                 image->uri.clear();
1289             } else if (auto imageFile = fileManager.OpenFile(image->uri); imageFile) {
1290                 auto uri = string_view(image->uri);
1291                 // Leave only the file extension.
1292                 if (const auto ext = uri.rfind('.'); ext != string_view::npos) {
1293                     uri.remove_prefix(ext + 1);
1294                 }
1295 
1296                 // Resolve image type from the file extension
1297                 if (uri == "png") {
1298                     image->type = MimeType::PNG;
1299                 } else if (uri == "jpg") {
1300                     image->type = MimeType::JPEG;
1301                 } else if (uri == "ktx") {
1302                     image->type = MimeType::KTX;
1303                 } else if (uri == "ktx2") {
1304                     image->type = MimeType::KTX2;
1305                 } else if (uri == "dds") {
1306                     image->type = MimeType::DDS;
1307                 }
1308 
1309                 if (image->type != MimeType::INVALID) {
1310                     // Read the image directly to the data buffer.
1311                     const size_t imageSize = static_cast<const size_t>(imageFile->GetLength());
1312                     auto& dataBuffer = bufferHelper.GetBuffer().data;
1313                     const auto imageOffset = dataBuffer.size();
1314 
1315                     dataBuffer.resize(imageOffset + imageSize);
1316 
1317                     imageFile->Read(dataBuffer.data() + imageOffset, imageSize);
1318 
1319                     BufferView bufferView;
1320                     bufferView.buffer = &bufferHelper.GetBuffer();
1321                     bufferView.byteLength = imageSize;
1322                     bufferView.byteOffset = imageOffset;
1323                     bufferView.data = dataBuffer.data() + imageOffset;
1324 
1325                     // The special zero data aligment skips copying the data again.
1326                     image->bufferView = bufferHelper.StoreBufferView(bufferView, 0);
1327 
1328                     image->uri.clear();
1329                 }
1330             }
1331         }
1332     }
1333 }
1334 
1335 // The following Export* functions return a JSON object containing the related parts of the GLTF2::Data.
ExportAccessorSparse(const Data & data,const Accessor & accessor)1336 json::value ExportAccessorSparse(const Data& data, const Accessor& accessor)
1337 {
1338     json::value jsonSparse = json::value::object {};
1339     {
1340         jsonSparse["count"] = accessor.sparse.count;
1341         {
1342             json::value jsonSparseIndices = json::value::array {};
1343             jsonSparseIndices["bufferView"] = FindObjectIndex(data.bufferViews, *accessor.sparse.indices.bufferView);
1344             jsonSparseIndices["byteOffset"] = accessor.sparse.indices.byteOffset;
1345             jsonSparseIndices["componentType"] = static_cast<int>(accessor.sparse.indices.componentType);
1346             jsonSparse["indices"] = move(jsonSparseIndices);
1347         }
1348         {
1349             json::value jsonSparseValues = json::value::array {};
1350             jsonSparseValues["bufferView"] = FindObjectIndex(data.bufferViews, *accessor.sparse.values.bufferView);
1351             jsonSparseValues["byteOffset"] = accessor.sparse.values.byteOffset;
1352             jsonSparse["values"] = move(jsonSparseValues);
1353         }
1354     }
1355     return jsonSparse;
1356 }
1357 
ExportAccessors(json::value & jsonGltf,const Data & data)1358 void ExportAccessors(json::value& jsonGltf, const Data& data)
1359 {
1360     json::value jsonAccessors = json::value::array {};
1361     for (const auto& accessor : data.accessors) {
1362         json::value jsonAccessor = json::value::object {};
1363         if (accessor->bufferView) {
1364             jsonAccessor["bufferView"] = FindObjectIndex(data.bufferViews, *accessor->bufferView);
1365         }
1366         if (accessor->byteOffset) {
1367             jsonAccessor["byteOffset"] = accessor->byteOffset;
1368         }
1369         jsonAccessor["componentType"] = static_cast<int>(accessor->componentType);
1370         if (accessor->normalized) {
1371             jsonAccessor["normalized"] = accessor->normalized;
1372         }
1373         jsonAccessor["count"] = accessor->count;
1374         jsonAccessor["type"] = GetDataType(accessor->type);
1375 
1376         if (!accessor->max.empty()) {
1377             jsonAccessor["max"] = accessor->max;
1378         }
1379         if (!accessor->min.empty()) {
1380             jsonAccessor["min"] = accessor->min;
1381         }
1382         if (accessor->sparse.count) {
1383             jsonAccessor["sparse"] = ExportAccessorSparse(data, *accessor);
1384         }
1385 #ifdef EXPORT_OTHER_OBJECT_NAMES
1386         if (accessor->Name) {
1387             jsonAccessor["name"] = accessor->normalized;
1388         }
1389 #endif
1390 
1391         jsonAccessors.array_.push_back(move(jsonAccessor));
1392     }
1393     jsonGltf["accessors"] = BASE_NS::move(jsonAccessors);
1394 }
1395 
ExportAnimations(json::value & jsonGltf,const Data & data)1396 void ExportAnimations(json::value& jsonGltf, const Data& data)
1397 {
1398     json::value jsonAnimations = json::value::array {};
1399     for (const auto& animation : data.animations) {
1400         json::value jsonAnimation = json::value::object {};
1401         {
1402             json::value jsonSamplers = json::value::array {};
1403             for (const auto& sampler : animation->samplers) {
1404                 json::value jsonSampler = json::value::object {};
1405                 jsonSampler["input"] = FindObjectIndex(data.accessors, *sampler->input);
1406                 if (sampler->interpolation != AnimationInterpolation::LINEAR) {
1407                     jsonSampler["interpolation"] = GetAnimationInterpolation(sampler->interpolation);
1408                 }
1409                 jsonSampler["output"] = FindObjectIndex(data.accessors, *sampler->output);
1410                 jsonSamplers.array_.push_back(move(jsonSampler));
1411             }
1412             jsonAnimation["samplers"] = move(jsonSamplers);
1413         }
1414         {
1415             json::value jsonChannels = json::value::array {};
1416             for (const auto& track : animation->tracks) {
1417                 json::value jsonChannel = json::value::object {};
1418                 jsonChannel["sampler"] = FindObjectIndex(animation->samplers, *track.sampler);
1419                 {
1420                     json::value jsonTarget = json::value::object {};
1421                     if (const auto nodeIndex = FindObjectIndex(data.nodes, *track.channel.node); 0 <= nodeIndex) {
1422                         jsonTarget["node"] = nodeIndex;
1423                     }
1424                     jsonTarget["path"] = GetAnimationPath(track.channel.path);
1425                     jsonChannel["target"] = move(jsonTarget);
1426                 }
1427                 jsonChannels.array_.push_back(move(jsonChannel));
1428             }
1429             jsonAnimation["channels"] = move(jsonChannels);
1430         }
1431         if (!animation->name.empty()) {
1432             jsonAnimation["name"] = string_view(animation->name);
1433         }
1434         jsonAnimations.array_.push_back(move(jsonAnimation));
1435     }
1436     jsonGltf["animations"] = BASE_NS::move(jsonAnimations);
1437 }
1438 
ExportBuffers(json::value & jsonGltf,const Data & data)1439 void ExportBuffers(json::value& jsonGltf, const Data& data)
1440 {
1441     json::value jsonBuffers = json::value::array {};
1442     for (const auto& buffer : data.buffers) {
1443         json::value jsonBuffer = json::value::object {};
1444         if (!buffer->uri.empty()) {
1445             jsonBuffer["uri"] = string_view(buffer->uri);
1446         }
1447         jsonBuffer["byteLength"] = buffer->byteLength;
1448 #ifdef EXPORT_OTHER_OBJECT_NAMES
1449         if (!buffer->name.empty()) {
1450             jsonBuffer["name"] = buffer->name;
1451         }
1452 #endif
1453         jsonBuffers.array_.push_back(move(jsonBuffer));
1454     }
1455     jsonGltf["buffers"] = BASE_NS::move(jsonBuffers);
1456 }
1457 
ExportBufferViews(json::value & jsonGltf,const Data & data)1458 void ExportBufferViews(json::value& jsonGltf, const Data& data)
1459 {
1460     json::value jsonBufferViews = json::value::array {};
1461     for (const auto& bufferView : data.bufferViews) {
1462         json::value jsonBufferView = json::value::object {};
1463         jsonBufferView["buffer"] = FindObjectIndex(data.buffers, *bufferView->buffer);
1464         if (bufferView->byteOffset) {
1465             jsonBufferView["byteOffset"] = bufferView->byteOffset;
1466         }
1467         jsonBufferView["byteLength"] = bufferView->byteLength;
1468         if (bufferView->byteStride) {
1469             jsonBufferView["byteStride"] = bufferView->byteStride;
1470         }
1471         if (bufferView->target != BufferTarget::NOT_DEFINED) {
1472             jsonBufferView["target"] = static_cast<int>(bufferView->target);
1473         }
1474 #ifdef EXPORT_OTHER_OBJECT_NAMES
1475         if (!bufferView->name.empty()) {
1476             jsonBufferView["name"] = bufferView->name;
1477         }
1478 #endif
1479         jsonBufferViews.array_.push_back(move(jsonBufferView));
1480     }
1481     jsonGltf["bufferViews"] = BASE_NS::move(jsonBufferViews);
1482 }
1483 
ExportCameras(json::value & jsonGltf,const Data & data)1484 void ExportCameras(json::value& jsonGltf, const Data& data)
1485 {
1486     json::value jsonCameras = json::value::array {};
1487 
1488     for (const auto& camera : data.cameras) {
1489         json::value jsonCamera = json::value::object {};
1490         jsonCamera["type"] = json::value { GetCameraType(camera->type) };
1491 
1492         if (!camera->name.empty()) {
1493             jsonCamera["name"] = json::value { camera->name };
1494         }
1495         if (camera->type == CameraType::PERSPECTIVE) {
1496             json::value jsonPerspective = json::value::object {};
1497             if (camera->attributes.perspective.aspect > 0.f) {
1498                 jsonPerspective["aspectRatio"] = camera->attributes.perspective.aspect;
1499             }
1500             jsonPerspective["yfov"] = camera->attributes.perspective.yfov;
1501             if (camera->attributes.perspective.zfar > 0.f) {
1502                 jsonPerspective["zfar"] = camera->attributes.perspective.zfar;
1503             }
1504             jsonPerspective["znear"] = camera->attributes.perspective.znear;
1505             jsonCamera["perspective"] = move(jsonPerspective);
1506         } else if (camera->type == CameraType::ORTHOGRAPHIC) {
1507             json::value jsonOrthographic = json::value::object {};
1508             jsonOrthographic["xmag"] = json::value { camera->attributes.ortho.xmag };
1509             jsonOrthographic["ymag"] = json::value { camera->attributes.ortho.ymag };
1510             jsonOrthographic["zfar"] = camera->attributes.ortho.zfar;
1511             jsonOrthographic["znear"] = camera->attributes.ortho.znear;
1512             jsonCamera["orthographic"] = move(jsonOrthographic);
1513         }
1514 
1515         jsonCameras.array_.push_back(move(jsonCamera));
1516     }
1517 
1518     jsonGltf["cameras"] = BASE_NS::move(jsonCameras);
1519 }
1520 
ExportImages(json::value & jsonGltf,const Data & data)1521 void ExportImages(json::value& jsonGltf, const Data& data)
1522 {
1523     json::value jsonImages = json::value::array {};
1524     for (const auto& image : data.images) {
1525         json::value jsonImage = json::value::object {};
1526 
1527         if (!image->uri.empty()) {
1528             jsonImage["uri"] = string_view(image->uri);
1529         } else if (image->bufferView) {
1530             jsonImage["mimeType"] = GetMimeType(image->type);
1531             jsonImage["bufferView"] = FindObjectIndex(data.bufferViews, *image->bufferView);
1532         }
1533 #ifdef EXPORT_OTHER_OBJECT_NAMES
1534         if (!image->name.empty()) {
1535             jsonImage["name"] = image->name;
1536         }
1537 #endif
1538         jsonImages.array_.push_back(move(jsonImage));
1539     }
1540     jsonGltf["images"] = BASE_NS::move(jsonImages);
1541 }
1542 
ExportTextureInfo(TextureInfo const & textureInfo)1543 json::value ExportTextureInfo(TextureInfo const& textureInfo)
1544 {
1545     json::value jsonTextureInfo = json::value::object {};
1546     jsonTextureInfo["index"] = textureInfo.index;
1547     if (textureInfo.texCoordIndex != 0 && textureInfo.texCoordIndex != GLTF_INVALID_INDEX) {
1548         jsonTextureInfo["texCoord"] = textureInfo.texCoordIndex;
1549     }
1550     return jsonTextureInfo;
1551 }
1552 
ExportMetallicRoughness(const Material & material)1553 json::value ExportMetallicRoughness(const Material& material)
1554 {
1555     json::value jsonMetallicRoughness = json::value::object {};
1556     if (material.metallicRoughness.baseColorFactor != DEFAULT_BASECOLOR_FACTOR) {
1557         jsonMetallicRoughness["baseColorFactor"] = material.metallicRoughness.baseColorFactor.data;
1558     }
1559     if (material.metallicRoughness.baseColorTexture.index != GLTF_INVALID_INDEX) {
1560         jsonMetallicRoughness["baseColorTexture"] = ExportTextureInfo(material.metallicRoughness.baseColorTexture);
1561     }
1562     if (material.metallicRoughness.metallicFactor < 1.f) {
1563         jsonMetallicRoughness["metallicFactor"] = material.metallicRoughness.metallicFactor;
1564     }
1565     if (material.metallicRoughness.roughnessFactor < 1.f) {
1566         jsonMetallicRoughness["roughnessFactor"] = material.metallicRoughness.roughnessFactor;
1567     }
1568     if (material.metallicRoughness.metallicRoughnessTexture.index != GLTF_INVALID_INDEX) {
1569         jsonMetallicRoughness["metallicRoughnessTexture"] =
1570             ExportTextureInfo(material.metallicRoughness.metallicRoughnessTexture);
1571     }
1572     return jsonMetallicRoughness;
1573 }
1574 
1575 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
ExportSpecularGlossiness(const Material & material)1576 json::value ExportSpecularGlossiness(const Material& material)
1577 {
1578     json::value jsonSpecularGlossiness = json::value::object {};
1579     if (material.specularGlossiness.diffuseFactor != DEFAULT_DIFFUSE_FACTOR) {
1580         jsonSpecularGlossiness["diffuseFactor"] = material.specularGlossiness.diffuseFactor.data;
1581     }
1582     if (material.specularGlossiness.diffuseTexture.index != GLTF_INVALID_INDEX) {
1583         jsonSpecularGlossiness["diffuseTexture"] = ExportTextureInfo(material.specularGlossiness.diffuseTexture);
1584     }
1585     if (material.specularGlossiness.specularFactor != DEFAULT_SPECULAR_FACTOR) {
1586         jsonSpecularGlossiness["specularFactor"] = material.specularGlossiness.specularFactor.data;
1587     }
1588     if (material.specularGlossiness.glossinessFactor != 1.f) {
1589         jsonSpecularGlossiness["glossinessFactor"] = material.specularGlossiness.glossinessFactor;
1590     }
1591     if (material.specularGlossiness.specularGlossinessTexture.index != GLTF_INVALID_INDEX) {
1592         jsonSpecularGlossiness["specularGlossinessTexture"] =
1593             ExportTextureInfo(material.specularGlossiness.specularGlossinessTexture);
1594     }
1595     return jsonSpecularGlossiness;
1596 }
1597 #endif
1598 
1599 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
ExportClearcoat(const Material::Clearcoat & clearcoat)1600 json::value ExportClearcoat(const Material::Clearcoat& clearcoat)
1601 {
1602     json::value jsonClearcoat = json::value::object {};
1603     if (clearcoat.factor != 0.f) {
1604         jsonClearcoat["clearcoatFactor"] = clearcoat.factor;
1605     }
1606     if (clearcoat.texture.index != GLTF_INVALID_INDEX) {
1607         jsonClearcoat["clearcoatTexture"] = ExportTextureInfo(clearcoat.texture);
1608     }
1609     if (clearcoat.roughness != 0.f) {
1610         jsonClearcoat["clearcoatRoughnessFactor"] = clearcoat.roughness;
1611     }
1612     if (clearcoat.roughnessTexture.index != GLTF_INVALID_INDEX) {
1613         jsonClearcoat["clearcoatRoughnessTexture"] = ExportTextureInfo(clearcoat.roughnessTexture);
1614     }
1615     if (clearcoat.normalTexture.textureInfo.index != GLTF_INVALID_INDEX) {
1616         auto& jsonNormalTexture = jsonClearcoat["clearcoatNormalTexture"] =
1617             ExportTextureInfo(clearcoat.normalTexture.textureInfo);
1618         if (clearcoat.normalTexture.scale != 1.f) {
1619             jsonNormalTexture["scale"] = clearcoat.normalTexture.scale;
1620         }
1621     }
1622     return jsonClearcoat;
1623 }
1624 #endif
1625 
1626 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_EMISSIVE_STRENGTH)
ExportEmissiveStrength(const float strength)1627 json::value ExportEmissiveStrength(const float strength)
1628 {
1629     json::value jsonEmissiveStrength = json::value::object {};
1630     if (strength != 1.f) {
1631         jsonEmissiveStrength["emissiveStrength"] = strength;
1632     }
1633     return jsonEmissiveStrength;
1634 }
1635 #endif
1636 
1637 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
ExportIor(const Material::Ior & ior)1638 json::value ExportIor(const Material::Ior& ior)
1639 {
1640     json::value jsonIor = json::value::object {};
1641     if (ior.ior != 1.5f) {
1642         jsonIor["ior"] = ior.ior;
1643     }
1644     return jsonIor;
1645 }
1646 #endif
1647 
1648 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
ExportSheen(const Material::Sheen & sheen)1649 json::value ExportSheen(const Material::Sheen& sheen)
1650 {
1651     json::value jsonSheen = json::value::object {};
1652     if (sheen.factor != Math::Vec3 {}) {
1653         jsonSheen["sheenColorFactor"] = sheen.factor.data;
1654     }
1655     if (sheen.texture.index != GLTF_INVALID_INDEX) {
1656         jsonSheen["sheenColorTexture"] = ExportTextureInfo(sheen.texture);
1657     }
1658     if (sheen.roughness != 0.f) {
1659         jsonSheen["sheenRoughnessFactor"] = sheen.roughness;
1660     }
1661     if (sheen.roughnessTexture.index != GLTF_INVALID_INDEX) {
1662         jsonSheen["sheenRoughnessTexture"] = ExportTextureInfo(sheen.roughnessTexture);
1663     }
1664     return jsonSheen;
1665 }
1666 #endif
1667 
1668 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
ExportSpecular(const Material::Specular & specular)1669 json::value ExportSpecular(const Material::Specular& specular)
1670 {
1671     json::value jsonSpecular = json::value::object {};
1672     if (specular.factor != 1.f) {
1673         jsonSpecular["specularFactor"] = specular.factor;
1674     }
1675     if (specular.texture.index != GLTF_INVALID_INDEX) {
1676         jsonSpecular["specularTexture"] = ExportTextureInfo(specular.texture);
1677     }
1678     if (specular.color != Math::Vec3(1.f, 1.f, 1.f)) {
1679         jsonSpecular["specularColorFactor"] = specular.color.data;
1680     }
1681     if (specular.colorTexture.index != GLTF_INVALID_INDEX) {
1682         jsonSpecular["specularColorTexture"] = ExportTextureInfo(specular.colorTexture);
1683     }
1684     return jsonSpecular;
1685 }
1686 #endif
1687 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
ExportTransmission(const Material::Transmission & transmission)1688 json::value ExportTransmission(const Material::Transmission& transmission)
1689 {
1690     json::value jsonTransmission = json::value::object {};
1691     if (transmission.factor != 0.f) {
1692         jsonTransmission["transmissionFactor"] = transmission.factor;
1693     }
1694     if (transmission.texture.index != GLTF_INVALID_INDEX) {
1695         jsonTransmission["transmissionTexture"] = ExportTextureInfo(transmission.texture);
1696     }
1697     return jsonTransmission;
1698 }
1699 #endif
1700 
ExportMaterialExtensions(const Material & material,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1701 json::value ExportMaterialExtensions(
1702     const Material& material, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1703 {
1704     json::value jsonExtensions = json::value::object {};
1705     if (material.type == Material::Type::SpecularGlossiness) {
1706 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
1707         jsonExtensions["KHR_materials_pbrSpecularGlossiness"] = ExportSpecularGlossiness(material);
1708         AppendUnique(jsonExtensionsUsed, "KHR_materials_pbrSpecularGlossiness");
1709 #endif
1710     } else if (material.type == Material::Type::Unlit) {
1711 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_UNLIT)
1712         jsonExtensions["KHR_materials_unlit"] = json::value::object {};
1713         AppendUnique(jsonExtensionsUsed, "KHR_materials_unlit");
1714 #endif
1715     }
1716 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
1717     if (auto clearcoat = ExportClearcoat(material.clearcoat); !clearcoat.empty()) {
1718         jsonExtensions["KHR_materials_clearcoat"] = move(clearcoat);
1719         AppendUnique(jsonExtensionsUsed, "KHR_materials_clearcoat");
1720     }
1721 #endif
1722 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_EMISSIVE_STRENGTH)
1723     if (auto emissiveStrength = ExportEmissiveStrength(material.emissiveFactor.w); !emissiveStrength.empty()) {
1724         jsonExtensions["KHR_materials_emissive_strength"] = move(emissiveStrength);
1725         AppendUnique(jsonExtensionsUsed, "KHR_materials_emissive_strength");
1726     }
1727 #endif
1728 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
1729     if (auto ior = ExportIor(material.ior); !ior.empty()) {
1730         jsonExtensions["KHR_materials_ior"] = move(ior);
1731         AppendUnique(jsonExtensionsUsed, "KHR_materials_ior");
1732     }
1733 #endif
1734 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
1735     if (auto sheen = ExportSheen(material.sheen); !sheen.empty()) {
1736         jsonExtensions["KHR_materials_sheen"] = move(sheen);
1737         AppendUnique(jsonExtensionsUsed, "KHR_materials_sheen");
1738     }
1739 #endif
1740 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
1741     if (auto specular = ExportSpecular(material.specular); !specular.empty()) {
1742         jsonExtensions["KHR_materials_specular"] = move(specular);
1743         AppendUnique(jsonExtensionsUsed, "KHR_materials_specular");
1744     }
1745 #endif
1746 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
1747     if (auto transmission = ExportTransmission(material.transmission); !transmission.empty()) {
1748         jsonExtensions["KHR_materials_transmission"] = move(transmission);
1749         AppendUnique(jsonExtensionsUsed, "KHR_materials_transmission");
1750     }
1751 #endif
1752     return jsonExtensions;
1753 }
1754 
ExportMaterialExtras(const Material & material)1755 json::value ExportMaterialExtras(const Material& material)
1756 {
1757     auto jsonExtras = json::value::object {};
1758     return jsonExtras;
1759 }
1760 
ExportMaterials(json::value & jsonGltf,const Data & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1761 void ExportMaterials(
1762     json::value& jsonGltf, const Data& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1763 {
1764     json::value jsonMaterials = json::value::array {};
1765     for (const auto& material : data.materials) {
1766         json::value jsonMaterial = json::value::object {};
1767         if (!material->name.empty()) {
1768             jsonMaterial["name"] = string_view(material->name);
1769         }
1770         if (material->type == Material::Type::MetallicRoughness || material->type == Material::Type::Unlit) {
1771             jsonMaterial["pbrMetallicRoughness"] = ExportMetallicRoughness(*material);
1772         }
1773         if (auto jsonExtensions = ExportMaterialExtensions(*material, jsonExtensionsUsed, jsonExtensionsRequired);
1774             !jsonExtensions.empty()) {
1775             jsonMaterial["extensions"] = move(jsonExtensions);
1776         }
1777 
1778         if (material->normalTexture.textureInfo.index != GLTF_INVALID_INDEX) {
1779             auto& jsonNormalTexture = jsonMaterial["normalTexture"] =
1780                 ExportTextureInfo(material->normalTexture.textureInfo);
1781             if (material->normalTexture.scale != 1.f) {
1782                 jsonNormalTexture["scale"] = material->normalTexture.scale;
1783             }
1784         }
1785         if (material->occlusionTexture.textureInfo.index != GLTF_INVALID_INDEX) {
1786             auto& jsonOcclusionTexture = jsonMaterial["occlusionTexture"] =
1787                 ExportTextureInfo(material->occlusionTexture.textureInfo);
1788             if (material->occlusionTexture.strength < 1.f) {
1789                 jsonOcclusionTexture["strength"] = material->occlusionTexture.strength;
1790             }
1791         }
1792         if (material->emissiveTexture.index != GLTF_INVALID_INDEX) {
1793             jsonMaterial["emissiveTexture"] = ExportTextureInfo(material->emissiveTexture);
1794         }
1795         if (Math::Vec3 emissiveFactor(
1796                 material->emissiveFactor.x, material->emissiveFactor.y, material->emissiveFactor.z);
1797             emissiveFactor != DEFAULT_EMISSIVE_FACTOR) {
1798             jsonMaterial["emissiveFactor"] = emissiveFactor.data;
1799         }
1800         if (material->alphaMode != AlphaMode::OPAQUE) {
1801             jsonMaterial["alphaMode"] = GetAlphaMode(material->alphaMode);
1802         }
1803         if (material->alphaCutoff != 0.5f) {
1804             jsonMaterial["alphaCutoff"] = material->alphaCutoff;
1805         }
1806         if (material->doubleSided) {
1807             jsonMaterial["doubleSided"] = material->doubleSided;
1808         }
1809         if (auto jsonExtras = ExportMaterialExtras(*material); !jsonExtras.empty()) {
1810             jsonMaterial["extras"] = move(jsonExtras);
1811         }
1812         jsonMaterials.array_.push_back(move(jsonMaterial));
1813     }
1814     jsonGltf["materials"] = BASE_NS::move(jsonMaterials);
1815 }
1816 
ExportMeshPrimitive(const MeshPrimitive & primitive,const vector<unique_ptr<Accessor>> & accessors,json::value & jsonTargetNames)1817 json::value ExportMeshPrimitive(
1818     const MeshPrimitive& primitive, const vector<unique_ptr<Accessor>>& accessors, json::value& jsonTargetNames)
1819 {
1820     json::value jsonPrimitive = json::value::object {};
1821     {
1822         json::value jsonAttributes = json::value::object {};
1823         for (const auto& attribute : primitive.attributes) {
1824             auto type = GetAttributeType(attribute.attribute);
1825             jsonAttributes[type] = FindObjectIndex(accessors, *attribute.accessor);
1826         }
1827         jsonPrimitive["attributes"] = move(jsonAttributes);
1828     }
1829     if (primitive.indices) {
1830         jsonPrimitive["indices"] = FindObjectIndex(accessors, *primitive.indices);
1831     }
1832     if (primitive.materialIndex != GLTF_INVALID_INDEX) {
1833         jsonPrimitive["material"] = primitive.materialIndex;
1834     }
1835     if (primitive.mode != RenderMode::TRIANGLES) {
1836         jsonPrimitive["mode"] = static_cast<int>(primitive.mode);
1837     }
1838     if (!primitive.targets.empty()) {
1839         json::value jsonTargets = json::value::array {};
1840         for (const auto& target : primitive.targets) {
1841             json::value jsonTarget = json::value::object {};
1842             for (const auto& attribute : target.target) {
1843                 auto type = GetAttributeType(attribute.attribute);
1844                 jsonTarget[type] = FindObjectIndex(accessors, *attribute.accessor);
1845             }
1846             jsonTargets.array_.push_back(move(jsonTarget));
1847             if (!target.name.empty()) {
1848                 jsonTargetNames.array_.push_back(string_view(target.name));
1849             }
1850         }
1851         jsonPrimitive["targets"] = move(jsonTargets);
1852     }
1853     return jsonPrimitive;
1854 }
1855 
ExportMeshes(json::value & jsonGltf,const Data & data)1856 void ExportMeshes(json::value& jsonGltf, const Data& data)
1857 {
1858     json::value jsonMeshes = json::value::array {};
1859     for (const auto& mesh : data.meshes) {
1860         json::value jsonMesh = json::value::object {};
1861         json::value jsonExtras = json::value::object {};
1862         {
1863             json::value jsonPrimitives = json::value::array {};
1864             json::value jsonTargetNames = json::value::array {};
1865             for (const auto& primitive : mesh->primitives) {
1866                 jsonPrimitives.array_.push_back(ExportMeshPrimitive(primitive, data.accessors, jsonTargetNames));
1867             }
1868             jsonMesh["primitives"] = move(jsonPrimitives);
1869             if (!jsonTargetNames.empty()) {
1870                 jsonExtras["targetNames"] = move(jsonTargetNames);
1871             }
1872         }
1873         if (!mesh->weights.empty()) {
1874             jsonMesh["weights"] = mesh->weights;
1875         }
1876         if (!mesh->name.empty()) {
1877             jsonMesh["name"] = string_view(mesh->name);
1878         }
1879         if (!jsonExtras.empty()) {
1880             jsonMesh["extras"] = move(jsonExtras);
1881         }
1882         jsonMeshes.array_.push_back(move(jsonMesh));
1883     }
1884     jsonGltf["meshes"] = BASE_NS::move(jsonMeshes);
1885 }
1886 
ExportNodeExtensions(const Data & data,const Node & node,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1887 json::value ExportNodeExtensions(
1888     const Data& data, const Node& node, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1889 {
1890     json::value jsonExtensions = json::value::object {};
1891 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
1892     if (node.light) {
1893         json::value jsonKHRLights = json::value::object {};
1894         jsonKHRLights["light"] = FindObjectIndex(data.lights, *node.light);
1895         jsonExtensions["KHR_lights_punctual"] = move(jsonKHRLights);
1896         AppendUnique(jsonExtensionsUsed, "KHR_lights_punctual");
1897     }
1898 #endif
1899     return jsonExtensions;
1900 }
1901 
ExportNodes(json::value & jsonGltf,const Data & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1902 void ExportNodes(
1903     json::value& jsonGltf, const Data& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1904 {
1905     json::value jsonNodes = json::value::array {};
1906 
1907     for (const auto& node : data.nodes) {
1908         json::value jsonNodeObject = json::value::object {};
1909 
1910         if (node->camera) {
1911             jsonNodeObject["camera"] = FindObjectIndex(data.cameras, *node->camera);
1912         }
1913 
1914         if (!node->tmpChildren.empty()) {
1915             jsonNodeObject["children"] = node->tmpChildren;
1916         }
1917 
1918         if (!node->name.empty()) {
1919             jsonNodeObject["name"] = string_view(node->name);
1920         }
1921 
1922         if (node->skin) {
1923             jsonNodeObject["skin"] = FindObjectIndex(data.skins, *node->skin);
1924         }
1925 
1926         if (node->mesh) {
1927             jsonNodeObject["mesh"] = FindObjectIndex(data.meshes, *node->mesh);
1928         }
1929 
1930         if (node->usesTRS) {
1931             if (node->translation != DEFAULT_TRANSLATION) {
1932                 jsonNodeObject["translation"] = node->translation.data;
1933             }
1934             if (node->rotation != DEFAULT_ROTATION) {
1935                 jsonNodeObject["rotation"] = node->rotation.data;
1936             }
1937             if (node->scale != DEFAULT_SCALE) {
1938                 jsonNodeObject["scale"] = node->scale.data;
1939             }
1940         } else {
1941             if (node->matrix != IDENTITY_MATRIX) {
1942                 jsonNodeObject["matrix"] = node->matrix.data;
1943             }
1944         }
1945 
1946         if (!node->weights.empty()) {
1947             jsonNodeObject["weights"] = node->weights;
1948         }
1949 
1950         if (auto jsonExtensions = ExportNodeExtensions(data, *node, jsonExtensionsUsed, jsonExtensionsRequired);
1951             !jsonExtensions.empty()) {
1952             jsonNodeObject["extensions"] = move(jsonExtensions);
1953         }
1954 
1955         jsonNodes.array_.push_back(move(jsonNodeObject));
1956     }
1957 
1958     jsonGltf["nodes"] = BASE_NS::move(jsonNodes);
1959 }
1960 
ExportSamplers(json::value & jsonGltf,const Data & data)1961 void ExportSamplers(json::value& jsonGltf, const Data& data)
1962 {
1963     json::value jsonSamplers = json::value::array {};
1964     for (const auto& sampler : data.samplers) {
1965         json::value jsonSampler = json::value::object {};
1966         if (sampler->magFilter != FilterMode::LINEAR) {
1967             jsonSampler["magFilter"] = static_cast<int>(sampler->magFilter);
1968         }
1969         if (sampler->minFilter != FilterMode::LINEAR) {
1970             jsonSampler["minFilter"] = static_cast<int>(sampler->minFilter);
1971         }
1972         if (sampler->wrapS != WrapMode::REPEAT) {
1973             jsonSampler["wrapS"] = static_cast<int>(sampler->wrapS);
1974         }
1975         if (sampler->wrapT != WrapMode::REPEAT) {
1976             jsonSampler["wrapT"] = static_cast<int>(sampler->wrapT);
1977         }
1978 #ifdef EXPORT_OTHER_OBJECT_NAMES
1979         if (!sampler->name.empty()) {
1980             jsonSampler["name"] = sampler->name;
1981         }
1982 #endif
1983         jsonSamplers.array_.push_back(move(jsonSampler));
1984     }
1985     jsonGltf["samplers"] = BASE_NS::move(jsonSamplers);
1986 }
1987 
ExportScenes(json::value & jsonGltf,const Data & data)1988 void ExportScenes(json::value& jsonGltf, const Data& data)
1989 {
1990     json::value jsonScenes = json::value::array {};
1991     for (const auto& scene : data.scenes) {
1992         json::value jsonScene = json::value::object {};
1993 
1994         if (!scene->name.empty()) {
1995             jsonScene["name"] = string_view(scene->name);
1996         }
1997 
1998         json::value jsonNodes = json::value::array {};
1999         for (const auto node : scene->nodes) {
2000             jsonNodes.array_.push_back(FindObjectIndex(data.nodes, *node));
2001         }
2002         jsonScene["nodes"] = move(jsonNodes);
2003 
2004 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2005         if (scene->light) {
2006             json::value jsonExtensions = json::value::object {};
2007 
2008             json::value jsonKHRLights = json::value::object {};
2009             jsonKHRLights["light"] = FindObjectIndex(data.lights, *scene->light);
2010             jsonExtensions["KHR_lights_punctual"] = move(jsonKHRLights);
2011 
2012             jsonScene["extensions"] = move(jsonExtensions);
2013         }
2014 #endif
2015         jsonScenes.array_.push_back(move(jsonScene));
2016     }
2017     jsonGltf["scenes"] = BASE_NS::move(jsonScenes);
2018     jsonGltf["scene"] = 0;
2019 }
2020 
ExportSkins(json::value & jsonGltf,const Data & data)2021 void ExportSkins(json::value& jsonGltf, const Data& data)
2022 {
2023     json::value jsonSkins = json::value::array {};
2024     for (const auto& skin : data.skins) {
2025         json::value jsonSkin = json::value::object {};
2026         if (skin->inverseBindMatrices) {
2027             jsonSkin["inverseBindMatrices"] = FindObjectIndex(data.accessors, *skin->inverseBindMatrices);
2028         }
2029         if (skin->skeleton) {
2030             jsonSkin["skeleton"] = FindObjectIndex(data.nodes, *skin->skeleton);
2031         }
2032         json::value jsonJoints = json::value::array {};
2033         for (const auto joint : skin->joints) {
2034             jsonJoints.array_.push_back(FindObjectIndex(data.nodes, *joint));
2035         }
2036         jsonSkin["joints"] = move(jsonJoints);
2037         if (!skin->name.empty()) {
2038             jsonSkin["name"] = skin->name.empty();
2039         }
2040         jsonSkins.array_.push_back(move(jsonSkin));
2041     }
2042     jsonGltf["skins"] = BASE_NS::move(jsonSkins);
2043 }
2044 
ExportTextures(json::value & jsonGltf,const Data & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)2045 void ExportTextures(
2046     json::value& jsonGltf, const Data& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
2047 {
2048     json::value jsonTextures = json::value::array {};
2049     for (const auto& texture : data.textures) {
2050         json::value jsonTexture = json::value::object {};
2051         if (texture->sampler) {
2052             jsonTexture["sampler"] = FindObjectIndex(data.samplers, *texture->sampler);
2053         }
2054         if (texture->image) {
2055             switch (texture->image->type) {
2056                 default:
2057                 case MimeType::INVALID:
2058                 case MimeType::JPEG:
2059                 case MimeType::PNG:
2060                 case MimeType::KTX: // NOTE: this is incorrect, but there's no extension for .ktx
2061                     jsonTexture["source"] = FindObjectIndex(data.images, *texture->image);
2062                     break;
2063                 case MimeType::DDS: {
2064                     json::value jsonMsftTextureDds = json::value::object {};
2065                     jsonMsftTextureDds["source"] = FindObjectIndex(data.images, *texture->image);
2066 
2067                     json::value jsonExtensions = json::value::object {};
2068                     jsonExtensions["MSFT_texture_dds"] = move(jsonMsftTextureDds);
2069 
2070                     jsonTexture["extensions"] = move(jsonExtensions);
2071 
2072                     AppendUnique(jsonExtensionsUsed, "MSFT_texture_dds");
2073                     AppendUnique(jsonExtensionsRequired, "MSFT_texture_dds");
2074                     break;
2075                 }
2076                 case MimeType::KTX2: {
2077                     json::value jsonKHRtextureBasisU = json::value::object {};
2078                     jsonKHRtextureBasisU["source"] = FindObjectIndex(data.images, *texture->image);
2079 
2080                     json::value jsonExtensions = json::value::object {};
2081                     jsonExtensions["KHR_texture_basisu"] = move(jsonKHRtextureBasisU);
2082 
2083                     jsonTexture["extensions"] = move(jsonExtensions);
2084 
2085                     AppendUnique(jsonExtensionsUsed, "KHR_texture_basisu");
2086                     AppendUnique(jsonExtensionsRequired, "KHR_texture_basisu");
2087                     break;
2088                 }
2089             }
2090         }
2091 #ifdef EXPORT_OTHER_OBJECT_NAMES
2092         if (!texture->name.empty()) {
2093             jsonTexture["name"] = texture->name;
2094         }
2095 #endif
2096         jsonTextures.array_.push_back(move(jsonTexture));
2097     }
2098     jsonGltf["textures"] = BASE_NS::move(jsonTextures);
2099 }
2100 
ExportKHRLights(const Data & data)2101 json::value ExportKHRLights(const Data& data)
2102 {
2103     json::value jsonLightArray = json::value::array {};
2104 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2105 
2106     for (const auto& light : data.lights) {
2107         if (light->type == LightType::AMBIENT || light->type == LightType::INVALID) {
2108             continue;
2109         }
2110 
2111         json::value jsonLightObject = json::value::object {};
2112         if (!light->name.empty()) {
2113             jsonLightObject["name"] = string_view(light->name);
2114         }
2115         if (light->color != Math::Vec3(1.f, 1.f, 1.f)) {
2116             jsonLightObject["color"] = light->color.data;
2117         }
2118         if (light->intensity != 1.f) {
2119             jsonLightObject["intensity"] = light->intensity;
2120         }
2121         jsonLightObject["type"] = GetLightType(light->type);
2122         if ((light->type == LightType::POINT || light->type == LightType::SPOT) && light->positional.range > 0.f) {
2123             jsonLightObject["range"] = light->positional.range;
2124         }
2125         if (light->type == LightType::SPOT) {
2126             json::value jsonSpotObject = json::value::object {};
2127             if (light->positional.spot.innerAngle != 0.f) {
2128                 jsonSpotObject["innerConeAngle"] = light->positional.spot.innerAngle;
2129             }
2130             if (light->positional.spot.outerAngle != 0.785398163397448f) {
2131                 jsonSpotObject["outerConeAngle"] = light->positional.spot.outerAngle;
2132             }
2133             if (!jsonSpotObject.empty()) {
2134                 jsonLightObject["spot"] = move(jsonSpotObject);
2135             }
2136         }
2137         jsonLightArray.array_.push_back(move(jsonLightObject));
2138     }
2139 #endif
2140     json::value jsonLights = json::value::object {};
2141     jsonLights["lights"] = move(jsonLightArray);
2142     return jsonLights;
2143 }
2144 
ExportExtensions(json::value & jsonGltf,const Data & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)2145 void ExportExtensions(
2146     json::value& jsonGltf, const Data& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
2147 {
2148     json::value jsonExtensions = json::value::object {};
2149 
2150 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2151     if (!data.lights.empty()) {
2152         if (auto jsonKHRLights = ExportKHRLights(data); !jsonKHRLights.empty()) {
2153             jsonExtensions["KHR_lights_punctual"] = move(jsonKHRLights);
2154             AppendUnique(jsonExtensionsUsed, "KHR_lights_punctual");
2155         }
2156     }
2157 #endif
2158     if (!jsonExtensions.empty()) {
2159         jsonGltf["extensions"] = BASE_NS::move(jsonExtensions);
2160     }
2161 }
2162 
ExportAsset(json::value & jsonGltf,string_view versionString,vector<string> & strings)2163 void ExportAsset(json::value& jsonGltf, string_view versionString, vector<string>& strings)
2164 {
2165     auto jsonAsset = json::value { json::value::object {} };
2166     jsonAsset["version"] = string_view("2.0");
2167     strings.push_back("CoreEngine " + versionString);
2168     jsonAsset["generator"] = string_view(strings.back());
2169     jsonGltf["asset"] = BASE_NS::move(jsonAsset);
2170 }
2171 
2172 /* Returns a JSON string generated from given GLTF2::Data. The above Export* helpers are used to convert different
2173  * parts of the data into JSON objects. */
ExportGLTFData(const Data & data,string_view versionString)2174 auto ExportGLTFData(const Data& data, string_view versionString)
2175 {
2176     vector<string> strings;
2177     auto jsonGltf = json::value { json::value::object {} };
2178 
2179     auto jsonExtensionsUsed = json::value { json::value::array {} };
2180     auto jsonExtensionsRequired = json::value { json::value::array {} };
2181 
2182     ExportAsset(jsonGltf, versionString, strings);
2183     ExportAccessors(jsonGltf, data);
2184     ExportAnimations(jsonGltf, data);
2185     ExportBuffers(jsonGltf, data);
2186     ExportBufferViews(jsonGltf, data);
2187     ExportCameras(jsonGltf, data);
2188     ExportImages(jsonGltf, data);
2189     ExportMaterials(jsonGltf, data, jsonExtensionsUsed, jsonExtensionsRequired);
2190     ExportMeshes(jsonGltf, data);
2191     ExportNodes(jsonGltf, data, jsonExtensionsUsed, jsonExtensionsRequired);
2192     ExportSamplers(jsonGltf, data);
2193     ExportScenes(jsonGltf, data);
2194     ExportSkins(jsonGltf, data);
2195     ExportTextures(jsonGltf, data, jsonExtensionsUsed, jsonExtensionsRequired);
2196     ExportExtensions(jsonGltf, data, jsonExtensionsUsed, jsonExtensionsRequired);
2197 
2198     if (!jsonExtensionsUsed.empty()) {
2199         jsonGltf["extensionsUsed"] = BASE_NS::move(jsonExtensionsUsed);
2200     }
2201     if (!jsonExtensionsRequired.empty()) {
2202         jsonGltf["extensionsRequired"] = BASE_NS::move(jsonExtensionsRequired);
2203     }
2204 
2205     return to_string(jsonGltf);
2206 }
2207 } // namespace
2208 
2209 /* Writes the GLTF2::Data as a GLB file. */
SaveGLB(const Data & data,IFile & file,string_view versionString)2210 void SaveGLB(const Data& data, IFile& file, string_view versionString)
2211 {
2212     auto jsonString = ExportGLTFData(data, versionString);
2213     if (jsonString.empty()) {
2214         return;
2215     }
2216     if (const auto pad = (jsonString.size() % 4); pad) {
2217         jsonString.append(4 - pad, ' ');
2218     }
2219 
2220     const auto jsonSize = static_cast<uint32_t>(jsonString.size());
2221     const auto binarySize = [](const auto& aBuffers) {
2222         size_t totalSize = 0;
2223         for (const auto& buffer : aBuffers) {
2224             totalSize += buffer->data.size();
2225         }
2226         return static_cast<uint32_t>(totalSize);
2227     }(data.buffers);
2228 
2229     const auto header = GLBHeader { GLTF_MAGIC, 2,
2230         static_cast<uint32_t>(sizeof(GLBHeader) + sizeof(GLBChunk) + jsonSize + sizeof(GLBChunk) + binarySize) };
2231     file.Write(&header, sizeof(header));
2232 
2233     const auto jsonChunk = GLBChunk { jsonSize, static_cast<uint32_t>(ChunkType::JSON) };
2234     file.Write(&jsonChunk, sizeof(jsonChunk));
2235 
2236     file.Write(jsonString.data(), jsonSize);
2237 
2238     const auto binaryChunk = GLBChunk { binarySize, static_cast<uint32_t>(ChunkType::BIN) };
2239     file.Write(&binaryChunk, sizeof(binaryChunk));
2240 
2241     file.Write(data.buffers.front()->data.data(), binarySize);
2242 }
2243 
2244 /* Writes the GLTF2::Data as a glTF file. */
SaveGLTF(const Data & data,IFile & file,string_view versionString)2245 void SaveGLTF(const Data& data, IFile& file, string_view versionString)
2246 {
2247     const auto jsonString = ExportGLTFData(data, versionString);
2248     file.Write(jsonString.data(), jsonString.size());
2249 }
2250 
2251 /* Returns true if the scene node has a node component and it hasn't been excluded from export. */
IsExportable(ISceneNode const & node,IEcs const & ecs)2252 bool IsExportable(ISceneNode const& node, IEcs const& ecs)
2253 {
2254     const auto nodeEntity = node.GetEntity();
2255     if (const auto nodeManager = GetManager<INodeComponentManager>(ecs); nodeManager) {
2256         return nodeManager->HasComponent(nodeEntity) && nodeManager->Get(nodeEntity).exported;
2257     }
2258     return false;
2259 }
2260 
2261 namespace {
2262 /* Returns the first parent which can be exported or null if no such parent exists. */
FindExportedParent(ISceneNode const & node,IEcs const & ecs)2263 ISceneNode* FindExportedParent(ISceneNode const& node, IEcs const& ecs)
2264 {
2265     auto parent = node.GetParent();
2266     while (parent) {
2267         if (IsExportable(*parent, ecs)) {
2268             return parent;
2269         }
2270         parent = parent->GetParent();
2271     }
2272     return parent;
2273 }
2274 
2275 /* Gathers transformations of nodes which haven't been exported and applies them to the exported child. */
CombineSkippedParentTransformations(TransformComponent & transformComponent,ISceneNode const & node,IEcs const & ecs,vector<Entity> const & nodeEntities)2276 void CombineSkippedParentTransformations(
2277     TransformComponent& transformComponent, ISceneNode const& node, IEcs const& ecs, vector<Entity> const& nodeEntities)
2278 {
2279     auto parent = node.GetParent();
2280     const auto transformManager = GetManager<ITransformComponentManager>(ecs);
2281     while (parent) {
2282         if (const auto parentIndex = FindHandleIndex(nodeEntities, parent->GetEntity());
2283             parentIndex < nodeEntities.size()) {
2284             // found an exported node and no need to continue.
2285             parent = nullptr;
2286         } else {
2287             // apply the transformation of a node which wasn't exported.
2288             if (transformManager->HasComponent(parent->GetEntity())) {
2289                 const auto parentTransformComponent = transformManager->Get(parent->GetEntity());
2290 
2291                 const auto transformation =
2292                     Math::Trs(parentTransformComponent.position, parentTransformComponent.rotation,
2293                         parentTransformComponent.scale) *
2294                     Math::Trs(transformComponent.position, transformComponent.rotation, transformComponent.scale);
2295 
2296                 Math::Vec3 skew;
2297                 Math::Vec4 perspective;
2298                 Math::Decompose(transformation, transformComponent.scale, transformComponent.rotation,
2299                     transformComponent.position, skew, perspective);
2300             }
2301             parent = parent->GetParent();
2302         }
2303     }
2304 }
2305 
GetNode(vector<unique_ptr<Node>> & nodeArray,size_t index)2306 Node& GetNode(vector<unique_ptr<Node>>& nodeArray, size_t index)
2307 {
2308     if (index < nodeArray.size()) {
2309         return *nodeArray[index];
2310     } else {
2311         return *nodeArray.emplace_back(make_unique<Node>());
2312     }
2313 }
2314 
AttachMesh(IEcs const & ecs,const Entity nodeEntity,Node & exportNode,decltype(Data::meshes) & meshArray,vector<Entity> & usedMeshes)2315 void AttachMesh(IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::meshes)& meshArray,
2316     vector<Entity>& usedMeshes)
2317 {
2318     if (const auto meshManager = GetManager<IRenderMeshComponentManager>(ecs);
2319         meshManager && meshManager->HasComponent(nodeEntity)) {
2320         const auto meshHandle = meshManager->Get(nodeEntity).mesh;
2321         if (const auto meshIndex = FindOrAddIndex(usedMeshes, meshHandle); meshIndex < meshArray.size()) {
2322             exportNode.mesh = meshArray[meshIndex].get();
2323         } else {
2324             exportNode.mesh = meshArray.emplace_back(make_unique<Mesh>()).get();
2325         }
2326     }
2327 }
2328 
AttachCamera(IEcs const & ecs,const Entity nodeEntity,Node & exportNode,decltype(Data::cameras) & cameraArray,Entities & entities)2329 void AttachCamera(IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::cameras)& cameraArray,
2330     Entities& entities)
2331 {
2332     if (const auto cameraManager = GetManager<ICameraComponentManager>(ecs);
2333         cameraManager && cameraManager->HasComponent(nodeEntity)) {
2334         if (const auto cameraIndex = FindOrAddIndex(entities.withCamera, nodeEntity);
2335             cameraIndex < cameraArray.size()) {
2336             exportNode.camera = cameraArray[cameraIndex].get();
2337         } else {
2338             exportNode.camera = cameraArray.emplace_back(make_unique<Camera>()).get();
2339         }
2340     }
2341 }
2342 
2343 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
AttachLight(IEcs const & ecs,const Entity nodeEntity,Node & exportNode,decltype(Data::lights) & lightArray,Entities & entities)2344 void AttachLight(
2345     IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::lights)& lightArray, Entities& entities)
2346 {
2347     if (const auto lightManager = GetManager<ILightComponentManager>(ecs);
2348         lightManager && lightManager->HasComponent(nodeEntity)) {
2349         if (const auto lightIndex = FindOrAddIndex(entities.withLight, nodeEntity); lightIndex < lightArray.size()) {
2350             exportNode.light = lightArray[lightIndex].get();
2351         } else {
2352             exportNode.light = lightArray.emplace_back(make_unique<KHRLight>()).get();
2353         }
2354     }
2355 }
2356 #endif
2357 
AttachParent(const ISceneNode & node,const IEcs & ecs,Scene & scene,Node & exportNode,uint32_t nodeIndex,const vector<Entity> & nodeEntities,decltype(Data::nodes) & nodeArray)2358 void AttachParent(const ISceneNode& node, const IEcs& ecs, Scene& scene, Node& exportNode, uint32_t nodeIndex,
2359     const vector<Entity>& nodeEntities, decltype(Data::nodes)& nodeArray)
2360 {
2361     if (const auto* parent = FindExportedParent(node, ecs); parent) {
2362         if (const auto parentIndex = FindHandleIndex(nodeEntities, parent->GetEntity());
2363             parentIndex < nodeArray.size()) {
2364             // Parent has been exported -> node has a parent and will be added to the parents list of children.
2365             exportNode.parent = nodeArray[parentIndex].get();
2366             if (std::none_of(exportNode.parent->children.begin(), exportNode.parent->children.end(),
2367                     [&exportNode](const auto childNode) { return childNode == &exportNode; })) {
2368                 exportNode.parent->children.push_back(&exportNode);
2369                 exportNode.parent->tmpChildren.push_back(nodeIndex);
2370             }
2371         } else {
2372             // Parent hasn't been exported i.e. it's outside this scene hierarchy -> add node as a scene root.
2373             scene.nodes.push_back(&exportNode);
2374         }
2375     } else {
2376         // Parent marked to be excluded from exporting -> add node as a scene root.
2377         scene.nodes.push_back(&exportNode);
2378     }
2379 }
2380 
AttachSkin(IEcs const & ecs,const Entity nodeEntity,Node & exportNode,decltype(Data::skins) & skinArray,Entities & entities)2381 void AttachSkin(
2382     IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::skins)& skinArray, Entities& entities)
2383 {
2384     if (const auto skinManager = GetManager<ISkinComponentManager>(ecs);
2385         skinManager && skinManager->HasComponent(nodeEntity)) {
2386         if (const auto entityIndex = FindOrAddIndex(entities.withSkin, nodeEntity); entityIndex < skinArray.size()) {
2387             exportNode.skin = skinArray[entityIndex].get();
2388         } else {
2389             exportNode.skin = skinArray.emplace_back(make_unique<Skin>()).get();
2390         }
2391     }
2392 }
2393 
2394 /* Export scene node hierarcy as a glTF scene. Only nodes and indices to other resources are written to GLTF2::Data.
2395  * Mesh, Image etc. have to be written to GLTF2::Data separately based on the usedMeshes, and entities output
2396  * parameters.
2397  * @param node Scene node to consider to be exported.
2398  * @param ecs ECS instance where node and related data lives.
2399  * @param scene Scene where nodes will be included.
2400  * @param data Exported nodes and placeholders for meshes, cameras, lights etc. will be stored here.
2401  * @param nodeEntities Entities with NodeComponents which were exported.
2402  * @param usedMeshes Handles to meshes used by exported entities.
2403  * @param entities Collections of entities having special components attached such as cameras or lights.
2404  */
RecursivelyExportNode(ISceneNode const & node,IEcs const & ecs,Scene & scene,Data & data,vector<Entity> & nodeEntities,vector<Entity> & usedMeshes,Entities & entities)2405 void RecursivelyExportNode(ISceneNode const& node, IEcs const& ecs, Scene& scene, Data& data,
2406     vector<Entity>& nodeEntities, vector<Entity>& usedMeshes, Entities& entities)
2407 {
2408     if (!IsExportable(node, ecs)) {
2409         // if this node shouldn't be exported, try exporting the child nodes.
2410         for (const auto* child : node.GetChildren()) {
2411             RecursivelyExportNode(*child, ecs, scene, data, nodeEntities, usedMeshes, entities);
2412         }
2413         return;
2414     }
2415 
2416     const auto nodeEntity = node.GetEntity();
2417     const auto nodeIndex = FindOrAddIndex(nodeEntities, nodeEntity);
2418     auto& exportNode = GetNode(data.nodes, nodeIndex);
2419 
2420     // name
2421     exportNode.name = node.GetName();
2422 
2423     // mesh
2424     AttachMesh(ecs, nodeEntity, exportNode, data.meshes, usedMeshes);
2425 
2426     // camera
2427     AttachCamera(ecs, nodeEntity, exportNode, data.cameras, entities);
2428 
2429 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2430     // light
2431     AttachLight(ecs, nodeEntity, exportNode, data.lights, entities);
2432 #endif
2433 
2434     // parent
2435     AttachParent(node, ecs, scene, exportNode, nodeIndex, nodeEntities, data.nodes);
2436 
2437     // isJoint
2438     // children, tmpChildren, the child will actually add itself to the parents list of children
2439     for (const auto* child : node.GetChildren()) {
2440         RecursivelyExportNode(*child, ecs, scene, data, nodeEntities, usedMeshes, entities);
2441     }
2442 
2443     // skin
2444     // tmpSkin
2445     AttachSkin(ecs, nodeEntity, exportNode, data.skins, entities);
2446 
2447     // usesTRS, translation, rotation, scale
2448     if (const auto* transformManager = GetManager<ITransformComponentManager>(ecs);
2449         transformManager && transformManager->HasComponent(nodeEntity)) {
2450         exportNode.usesTRS = true;
2451 
2452         auto transformComponent = transformManager->Get(nodeEntity);
2453         CombineSkippedParentTransformations(transformComponent, node, ecs, nodeEntities);
2454         transformComponent.rotation = Math::Normalize(transformComponent.rotation);
2455         exportNode.translation = transformComponent.position;
2456         exportNode.rotation = transformComponent.rotation;
2457         exportNode.scale = transformComponent.scale;
2458     }
2459 
2460     // NOTE: weights, defaults are not exported
2461 }
2462 } // namespace
2463 
2464 // Internal exporting function.
ExportGLTF(IEngine & engine,const IEcs & ecs)2465 ExportResult ExportGLTF(IEngine& engine, const IEcs& ecs)
2466 {
2467     auto result = ExportResult(make_unique<Data>(engine.GetFileManager()));
2468 
2469     // We write all the binary data to a single buffer.
2470     auto& exportBuffer = result.data->buffers.emplace_back(make_unique<Buffer>());
2471 
2472     // Helper for gathering bufferViews and accessors, and packing data in the buffer.
2473     BufferHelper bufferHelper(*exportBuffer, result.data->bufferViews, result.data->accessors);
2474 
2475     Entities entities;
2476     vector<Entity> usedMeshes;
2477 
2478     // Create Nodes and Scenes.
2479     const auto nameManager = GetManager<INameComponentManager>(ecs);
2480     const auto nodeManager = GetManager<INodeComponentManager>(ecs);
2481     const auto nodeSystem = GetSystem<INodeSystem>(ecs);
2482     if (nodeManager && nodeSystem) {
2483         auto& sceneArray = result.data->scenes;
2484 
2485         const auto nodeCount = nodeManager->GetComponentCount();
2486         auto& nodeArray = result.data->nodes;
2487         nodeArray.reserve(nodeCount);
2488         entities.nodes.reserve(nodeCount);
2489 
2490         auto& exportScene = *sceneArray.emplace_back(make_unique<Scene>());
2491 
2492         for (const auto* child : nodeSystem->GetRootNode().GetChildren()) {
2493             RecursivelyExportNode(*child, ecs, exportScene, *result.data, entities.nodes, usedMeshes, entities);
2494         }
2495         if (exportScene.nodes.empty()) {
2496             sceneArray.pop_back();
2497         }
2498 
2499         // Create Skins.
2500         ExportGltfSkins(ecs, entities, nodeArray, result, bufferHelper);
2501 
2502         // Create Cameras.
2503         ExportGltfCameras(ecs, entities, result);
2504 
2505 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2506         // Create KHRLights.
2507         ExportGltfLight(ecs, entities, result);
2508 #endif
2509         // Create Animations.
2510         ExportGltfAnimations(ecs, entities, result, bufferHelper);
2511     }
2512     unordered_map<string, IGLTFData::Ptr> originalGltfs;
2513     // Create Meshes for the mesh handles referenced by exported nodes. Materials referenced by the meshes will
2514     // be gathered for exporting.
2515     auto meshManager = GetManager<IMeshComponentManager>(ecs);
2516     auto materialManager = GetManager<IMaterialComponentManager>(ecs);
2517     auto uriManager = GetManager<IUriComponentManager>(ecs);
2518     auto usedMaterials = ExportGltfMeshes(*meshManager, *nameManager, *uriManager, *materialManager,
2519         engine.GetFileManager(), usedMeshes, result, bufferHelper, originalGltfs);
2520 
2521     ExportGltfMaterials(engine, *materialManager, *nameManager, *uriManager, usedMaterials, result, bufferHelper);
2522 
2523     // Write image data to the buffer
2524     ExportImageData(engine.GetFileManager(), result, bufferHelper, originalGltfs);
2525 
2526     exportBuffer->byteLength = exportBuffer->data.size();
2527 
2528     return result;
2529 }
2530 } // namespace GLTF2
2531 
2532 CORE3D_END_NAMESPACE()
2533