1 /*
2 * Copyright (C) 2023 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 "loader/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 auto const DEFAULT_BASECOLOR_FACTOR = Math::Vec4(1.f, 1.f, 1.f, 1.f);
192 constexpr auto const DEFAULT_DIFFUSE_FACTOR = Math::Vec4(1.f, 1.f, 1.f, 1.f);
193 constexpr auto const DEFAULT_SPECULAR_FACTOR = Math::Vec3(1.f, 1.f, 1.f);
194 constexpr auto const DEFAULT_EMISSIVE_FACTOR = Math::Vec3(0.f, 0.f, 0.f);
195
196 constexpr auto const DEFAULT_TRANSLATION = Math::Vec3(0.f, 0.f, 0.f);
197 constexpr auto const DEFAULT_SCALE = Math::Vec3(1.f, 1.f, 1.f);
198 constexpr auto const DEFAULT_ROTATION = Math::Quat(0.f, 0.f, 0.f, 1.f);
199
200 constexpr auto const 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 auto const comparePointers = [ptr = &object](auto const& aUniqueObject) { return aUniqueObject.get() == ptr; };
226 if (auto const 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 (auto const 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 (auto const 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.emplace_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 auto const bufferViewHash = BASE_NS::Hash(
270 bufferView.buffer, bufferView.byteLength, bufferView.byteOffset, bufferView.byteStride, bufferView.target);
271 auto const 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 auto const pad = buffer_.data.size() % dataAlignment;
278 buffer_.data.insert(buffer_.data.end(), pad, 0);
279 usedBufferViews_[bufferViewIndex]->byteOffset = buffer_.data.size();
280 buffer_.data.insert(buffer_.data.end(), 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 auto const 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 auto const 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 auto const textureHash = BASE_NS::Hash(samplerIndex, imageIndex);
342 auto const 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 auto const& 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 (auto const 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 (auto const& 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 auto const 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 (auto const 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 (auto const cameraManager = GetManager<ICameraComponentManager>(ecs); cameraManager) {
483 auto cameraIterator = result.data->cameras.begin();
484
485 for (auto const cameraEntity : entities.withCamera) {
486 auto& exportCamera = *cameraIterator++;
487
488 auto const 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 (auto const lightManager = GetManager<ILightComponentManager>(ecs); lightManager) {
528 auto lightIterator = result.data->lights.begin();
529
530 for (auto const lightEntity : entities.withLight) {
531 auto& exportLight = *lightIterator++;
532
533 auto const lightComponent = lightManager->Get(lightEntity);
534 switch (lightComponent.type) {
535 default:
536 case LightComponent::Type::INVALID:
537 exportLight->type = LightType::INVALID;
538
539 CORE_LOG_E("cannot export light %u", static_cast<uint32_t>(lightComponent.type));
540
541 result.error += "failed to export light";
542 result.success = false;
543 break;
544 case LightComponent::Type::DIRECTIONAL:
545 exportLight->type = LightType::DIRECTIONAL;
546 break;
547 case LightComponent::Type::POINT:
548 exportLight->type = LightType::POINT;
549 break;
550 case LightComponent::Type::SPOT:
551 exportLight->type = LightType::SPOT;
552 break;
553 }
554 exportLight->color = lightComponent.color;
555 exportLight->intensity = lightComponent.intensity;
556 exportLight->positional.range = lightComponent.range;
557
558 exportLight->positional.spot.innerAngle = lightComponent.spotInnerAngle;
559 exportLight->positional.spot.outerAngle = lightComponent.spotOuterAngle;
560
561 exportLight->shadow.shadowCaster = lightComponent.shadowEnabled;
562 }
563 }
564 }
565 #endif
566 }
567
StoreInverseBindMatrices(array_view<const Math::Mat4X4> ibls,BufferHelper & bufferHelper)568 Accessor* StoreInverseBindMatrices(array_view<const Math::Mat4X4> ibls, BufferHelper& bufferHelper)
569 {
570 vector<Math::Mat4X4> inverseBindMatrices;
571 inverseBindMatrices.reserve(ibls.size());
572 for (auto const& matrix : ibls) {
573 inverseBindMatrices.emplace_back(matrix);
574 }
575
576 if (!inverseBindMatrices.empty()) {
577 auto matrixData = array_view(
578 reinterpret_cast<uint8_t*>(inverseBindMatrices.data()), inverseBindMatrices.size() * sizeof(Math::Mat4X4));
579 BufferView bufferView;
580 bufferView.buffer = &bufferHelper.GetBuffer();
581 bufferView.byteLength = matrixData.size();
582 bufferView.data = matrixData.data();
583
584 Accessor accessor;
585 accessor.bufferView = &bufferView;
586 accessor.byteOffset = 0;
587 accessor.componentType = ComponentType::FLOAT;
588 accessor.count = static_cast<uint32_t>(inverseBindMatrices.size());
589 accessor.type = DataType::MAT4;
590 return bufferHelper.StoreAccessor(accessor);
591 }
592 return nullptr;
593 }
594
595 // helper for evaluating skeleton property for a skin
596 struct NodeDepth {
597 uint32_t depth;
598 GLTF2::Node* node;
599
operator >GLTF2::__anon3f1293350211::NodeDepth600 inline bool operator>(const NodeDepth& rhs) const noexcept
601 {
602 if (depth > rhs.depth) {
603 return true;
604 }
605 if (depth < rhs.depth) {
606 return false;
607 } else if (node < rhs.node) {
608 return true;
609 }
610 return false;
611 }
612
operator ==GLTF2::__anon3f1293350211::NodeDepth613 inline bool operator==(const NodeDepth& rhs) const noexcept
614 {
615 return (depth == rhs.depth) && (node == rhs.node);
616 }
617 };
618
SameDepth(const NodeDepth & lhs,const NodeDepth & rhs)619 inline bool SameDepth(const NodeDepth& lhs, const NodeDepth& rhs)
620 {
621 return lhs.depth == rhs.depth;
622 }
623
FindSkeletonRoot(array_view<GLTF2::Node * > joints)624 Node* FindSkeletonRoot(array_view<GLTF2::Node*> joints)
625 {
626 // find the skeleton root node
627 if (!joints.empty()) {
628 // for each joint calculate distance to the root
629 vector<NodeDepth> depths;
630 depths.reserve(joints.size());
631 for (Node* joint : joints) {
632 uint32_t depth = 0;
633 for (Node* parent = joint->parent; parent; parent = parent->parent) {
634 ++depth;
635 }
636 depths.push_back({ depth, joint });
637 }
638
639 // sort by depth and node
640 std::sort(depths.begin(), depths.end(), std::greater<NodeDepth>());
641
642 // reduce the numer of nodes until one remains (or there are multiple roots)
643 for (auto start = depths.begin(); depths.size() > 1 && start->depth; start = depths.begin()) {
644 // select a range of nodes at an equal depth
645 auto end = std::upper_bound(start, depths.end(), start->depth,
646 [](uint32_t depth, const NodeDepth& current) { return current.depth < depth; });
647 // replace each node with its parent
648 for (auto& data : array_view(start.ptr(), end.ptr())) {
649 data.node = data.node->parent;
650 data.depth -= 1;
651 }
652 // sort again according to updated depths and nodes
653 std::sort(depths.begin(), depths.end(), std::greater<NodeDepth>());
654
655 // remove duplicates
656 depths.erase(std::unique(start, depths.end()), depths.end());
657 }
658
659 return (depths.size() == 1) ? depths.front().node : nullptr;
660 }
661 return nullptr;
662 }
663
ExportGltfSkins(const IEcs & ecs,const Entities & entities,const vector<unique_ptr<Node>> & nodeArray,ExportResult & result,BufferHelper & bufferHelper)664 void ExportGltfSkins(const IEcs& ecs, const Entities& entities, const vector<unique_ptr<Node>>& nodeArray,
665 ExportResult& result, BufferHelper& bufferHelper)
666 {
667 if (!entities.withSkin.empty() && entities.withSkin.size() == result.data->skins.size()) {
668 if (auto const skinManager = GetManager<ISkinComponentManager>(ecs); skinManager) {
669 auto const skinIbmManager = GetManager<ISkinIbmComponentManager>(ecs);
670 auto const skinJointsManager = GetManager<ISkinJointsComponentManager>(ecs);
671
672 auto skinIterator = result.data->skins.begin();
673
674 for (auto const skinnedEntity : entities.withSkin) {
675 auto& exportSkin = *skinIterator++;
676 auto const skinComponent = skinManager->Get(skinnedEntity);
677 if (EntityUtil::IsValid(skinComponent.skin)) {
678 // store IBMs in the buffer handled by BufferHelper
679 if (const auto ibmHandle = skinIbmManager->Read(skinComponent.skin); ibmHandle) {
680 exportSkin->inverseBindMatrices = StoreInverseBindMatrices(ibmHandle->matrices, bufferHelper);
681 }
682
683 // gather all the joint nodes
684 if (auto const skinJointsHandle = skinJointsManager->Read(skinnedEntity); skinJointsHandle) {
685 exportSkin->joints.reserve(skinJointsHandle->count);
686 for (auto jointEntity : array_view(skinJointsHandle->jointEntities, skinJointsHandle->count)) {
687 if (auto const jointIndex = FindHandleIndex(entities.nodes, jointEntity);
688 jointIndex < nodeArray.size()) {
689 exportSkin->joints.emplace_back(nodeArray[jointIndex].get());
690 } else {
691 CORE_LOG_D("joint node not exported");
692 }
693 }
694 }
695
696 // find the skeleton root node
697 exportSkin->skeleton = FindSkeletonRoot(exportSkin->joints);
698 if (!exportSkin->skeleton) {
699 CORE_LOG_D("Couldn't find common root for skinned entity %s", to_hex(skinnedEntity.id).data());
700 }
701 }
702 }
703 }
704 }
705 }
706
GetAnimationTarget(const INodeSystem & nodeSystem,const INameComponentManager & nameManager,const Entities & entities,array_view<const unique_ptr<Node>> nodes,const Entity trackEntity,const AnimationTrackComponent & trackComponent)707 Node* GetAnimationTarget(const INodeSystem& nodeSystem, const INameComponentManager& nameManager,
708 const Entities& entities, array_view<const unique_ptr<Node>> nodes, const Entity trackEntity,
709 const AnimationTrackComponent& trackComponent)
710 {
711 Node* target = nullptr;
712 if (auto animatedNode = nodeSystem.GetNode(trackComponent.target); animatedNode) {
713 if (auto const nodeIndex = FindHandleIndex(entities.nodes, static_cast<Entity>(trackComponent.target));
714 nodeIndex < nodes.size()) {
715 target = nodes[nodeIndex].get();
716 }
717 }
718 if (!target) {
719 string_view nodePath;
720 if (const auto nameHandle = nameManager.Read(trackEntity); nameHandle) {
721 nodePath = nameHandle->name;
722 }
723 CORE_LOG_W("couldn't resolve node path: %s", nodePath.data());
724 }
725 return target;
726 }
727
AnimationInput(const IAnimationInputComponentManager & inputManager,const Entity & animationInput,BufferHelper & bufferHelper)728 Accessor* AnimationInput(
729 const IAnimationInputComponentManager& inputManager, const Entity& animationInput, BufferHelper& bufferHelper)
730 {
731 if (auto inputHandle = inputManager.Read(animationInput); inputHandle) {
732 // BufferView data is not const although at this point it's not modified.
733 auto inputData = array_view(reinterpret_cast<uint8_t*>(const_cast<float*>(inputHandle->timestamps.data())),
734 inputHandle->timestamps.size_in_bytes());
735
736 BufferView bufferView;
737 bufferView.buffer = reinterpret_cast<Buffer*>(static_cast<uintptr_t>(animationInput.id));
738 bufferView.byteLength = inputData.size();
739 bufferView.data = inputData.data();
740
741 Accessor accessor;
742 accessor.bufferView = &bufferView;
743 accessor.byteOffset = 0;
744 accessor.componentType = ComponentType::FLOAT;
745 accessor.count = static_cast<uint32_t>(inputHandle->timestamps.size());
746 accessor.type = DataType::SCALAR;
747
748 auto inputAccessor = bufferHelper.StoreAccessor(accessor);
749 // The accessor used for animation.sampler.input requires min and max values.
750 if (inputAccessor && (inputAccessor->min.empty() || inputAccessor->max.empty())) {
751 auto input =
752 array_view(reinterpret_cast<float const*>(inputAccessor->bufferView->data + inputAccessor->byteOffset),
753 inputAccessor->count);
754 auto inputMin = std::numeric_limits<float>::max();
755 auto inputMax = std::numeric_limits<float>::lowest();
756 for (auto const value : input) {
757 inputMin = std::min(value, inputMin);
758 inputMax = std::max(value, inputMax);
759 }
760 inputAccessor->min.emplace_back(inputMin);
761 inputAccessor->max.emplace_back(inputMax);
762 }
763
764 return inputAccessor;
765 }
766 return {};
767 }
768
AnimationOutput(const IAnimationOutputComponentManager & outputManager,const Entity & animationOutput,AnimationPath type,BufferHelper & bufferHelper)769 Accessor* AnimationOutput(const IAnimationOutputComponentManager& outputManager, const Entity& animationOutput,
770 AnimationPath type, BufferHelper& bufferHelper)
771 {
772 Accessor accessor;
773 // Setup the accessor to match the keyframe data for current animation type. Translation and scale are vec3s,
774 // rotation is quaternions, and morph animation is floats.
775 auto outputData = array_view<const uint8_t>();
776 if (auto outputHandle = outputManager.Read(animationOutput); outputHandle) {
777 outputData = outputHandle->data;
778 switch (type) {
779 case AnimationPath::TRANSLATION:
780 case AnimationPath::SCALE: {
781 accessor.count = static_cast<uint32_t>(outputData.size() / sizeof(Math::Vec3));
782 accessor.type = DataType::VEC3;
783 } break;
784 case AnimationPath::ROTATION: {
785 accessor.count = static_cast<uint32_t>(outputData.size() / sizeof(Math::Vec4));
786 accessor.type = DataType::VEC4;
787 } break;
788 case AnimationPath::WEIGHTS: {
789 accessor.count = static_cast<uint32_t>(outputData.size() / sizeof(float));
790 accessor.type = DataType::SCALAR;
791 } break;
792 default:
793 return nullptr;
794 }
795 }
796 BufferView bufferView;
797 bufferView.buffer = reinterpret_cast<Buffer*>(static_cast<uintptr_t>(animationOutput.id));
798 bufferView.byteLength = outputData.size();
799 bufferView.data = outputData.data();
800
801 accessor.bufferView = &bufferView;
802 accessor.byteOffset = 0;
803 accessor.componentType = ComponentType::FLOAT;
804
805 return bufferHelper.StoreAccessor(accessor);
806 }
807
CreateAnimationSampler(const AnimationTrackComponent & trackComponent,const IAnimationInputComponentManager & animationInputManager,const IAnimationOutputComponentManager & animationOutputManager,BufferHelper & bufferHelper)808 unique_ptr<AnimationSampler> CreateAnimationSampler(const AnimationTrackComponent& trackComponent,
809 const IAnimationInputComponentManager& animationInputManager,
810 const IAnimationOutputComponentManager& animationOutputManager, BufferHelper& bufferHelper)
811 {
812 auto exportSampler = make_unique<AnimationSampler>();
813 exportSampler->interpolation = GetAnimationInterpolation(trackComponent.interpolationMode);
814 exportSampler->input = AnimationInput(animationInputManager, trackComponent.timestamps, bufferHelper);
815 exportSampler->output =
816 AnimationOutput(animationOutputManager, trackComponent.data, GetAnimationPath(trackComponent), bufferHelper);
817 return exportSampler;
818 }
819
CleanupAnimation(Animation & exportAnimation)820 void CleanupAnimation(Animation& exportAnimation)
821 {
822 // Remove all tracks that don't have a node, sampler or sampler is missing input or output.
823 exportAnimation.tracks.erase(std::find_if(exportAnimation.tracks.begin(), exportAnimation.tracks.end(),
824 [](const AnimationTrack& track) {
825 return !track.channel.node || !track.sampler || !track.sampler->input ||
826 !track.sampler->output;
827 }),
828 exportAnimation.tracks.end());
829
830 // Remove all samplers missing input or output.
831 exportAnimation.samplers.erase(
832 std::find_if(exportAnimation.samplers.begin(), exportAnimation.samplers.end(),
833 [](const unique_ptr<AnimationSampler>& sampler) { return !sampler->input || !sampler->output; }),
834 exportAnimation.samplers.end());
835 }
836
Hash(const AnimationTrackComponent & trackComponent)837 uint64_t Hash(const AnimationTrackComponent& trackComponent)
838 {
839 return BASE_NS::Hash(static_cast<const Entity&>(trackComponent.timestamps).id,
840 static_cast<const Entity&>(trackComponent.data).id, static_cast<uint32_t>(trackComponent.interpolationMode));
841 }
842
ExportGltfAnimations(const IEcs & ecs,const Entities & entities,ExportResult & result,BufferHelper & bufferHelper)843 void ExportGltfAnimations(const IEcs& ecs, const Entities& entities, ExportResult& result, BufferHelper& bufferHelper)
844 {
845 auto const nodeSystem = GetSystem<INodeSystem>(ecs);
846 auto const animationSystem = GetSystem<IAnimationSystem>(ecs);
847 auto const animationManager = GetManager<IAnimationComponentManager>(ecs);
848 auto const animationInputManager = GetManager<IAnimationInputComponentManager>(ecs);
849 auto const animationOutputManager = GetManager<IAnimationOutputComponentManager>(ecs);
850 auto const animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs);
851 auto const nameManager = GetManager<INameComponentManager>(ecs);
852 if (nodeSystem && animationSystem && animationManager && animationInputManager && animationOutputManager &&
853 animationTrackManager && nameManager) {
854 auto const animationCount = animationManager->GetComponentCount();
855 auto& animationArray = result.data->animations;
856 animationArray.reserve(animationCount);
857
858 for (IComponentManager::ComponentId i = 0U; i < animationCount; ++i) {
859 auto& exportAnimation = animationArray.emplace_back(make_unique<Animation>());
860 if (auto nameHandle = nameManager->Read(animationManager->GetEntity(i)); nameHandle) {
861 exportAnimation->name = nameHandle->name;
862 }
863
864 // animation.samplers can be shared between channels. Identify samplers by hashing input, output and
865 // interpolationMode.
866 vector<uint64_t> samplerHashes;
867
868 if (const auto animationHandle = animationManager->Read(i); animationHandle) {
869 for (auto const& trackEntity : animationHandle->tracks) {
870 if (const auto trackHandle = animationTrackManager->Read(trackEntity); trackHandle) {
871 auto const samplerIndex = FindOrAddIndex(samplerHashes, Hash(*trackHandle));
872 if ((samplerIndex + 1) >= exportAnimation->samplers.size()) {
873 exportAnimation->samplers.resize(samplerIndex + 1);
874 exportAnimation->samplers[samplerIndex] = CreateAnimationSampler(
875 *trackHandle, *animationInputManager, *animationOutputManager, bufferHelper);
876 }
877 const auto target = GetAnimationTarget(
878 *nodeSystem, *nameManager, entities, result.data->nodes, trackEntity, *trackHandle);
879 exportAnimation->tracks.push_back(AnimationTrack { { target, GetAnimationPath(*trackHandle) },
880 exportAnimation->samplers[samplerIndex].get() });
881 }
882 }
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
895 struct MeshPrimitiveGenerator {
896 const IMaterialComponentManager& materialManager;
897 BufferHelper& buffer;
898 vector<Entity>& usedMaterials;
899
operator ()GLTF2::__anon3f1293350211::MeshPrimitiveGenerator900 MeshPrimitive operator()(const MeshComponent::Submesh& submesh, const MeshPrimitive& original)
901 {
902 MeshPrimitive copy;
903 if (original.indices) {
904 copy.indices = buffer.StoreAccessor(*original.indices);
905 }
906 for (auto const& attrib : original.attributes) {
907 copy.attributes.push_back(Attribute { attrib.attribute, buffer.StoreAccessor(*attrib.accessor) });
908 }
909 if (materialManager.HasComponent(submesh.material)) {
910 copy.materialIndex = FindOrAddIndex(usedMaterials, submesh.material);
911 }
912 for (auto const& target : original.targets) {
913 MorphTarget morphTarget { target.name, {} };
914 for (auto const& attribute : target.target) {
915 morphTarget.target.push_back(
916 Attribute { attribute.attribute, buffer.StoreAccessor(*attribute.accessor) });
917 }
918 copy.targets.push_back(move(morphTarget));
919 }
920
921 return copy;
922 }
923 };
924
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)925 vector<Entity> ExportGltfMeshes(const IMeshComponentManager& meshManager, const INameComponentManager& nameManager,
926 const IUriComponentManager& uriManager, const IMaterialComponentManager& materialManager, IFileManager& fileManager,
927 const vector<Entity>& usedMeshes, ExportResult& result, BufferHelper& buffer,
928 unordered_map<string, IGLTFData::Ptr>& originalGltfs)
929 {
930 vector<Entity> usedMaterials;
931 CORE_ASSERT(usedMeshes.size() == result.data->meshes.size());
932 if (!usedMeshes.empty() && usedMeshes.size() == result.data->meshes.size()) {
933 auto meshIterator = result.data->meshes.begin();
934
935 // Create GLTF2::Meshes from mesh entities
936 for (auto const meshEntity : usedMeshes) {
937 auto& exportMesh = *meshIterator++;
938
939 if (auto const uriId = uriManager.GetComponentId(meshEntity);
940 uriId != IComponentManager::INVALID_COMPONENT_ID) {
941 auto const uc = uriManager.Get(uriId);
942 // mesh data is copied from the original glTF pointer by UriComponent
943 if (const auto [originalGltf, meshIndex] =
944 ResolveGltfAndResourceIndex(uc.uri, fileManager, originalGltfs);
945 originalGltf && meshIndex < originalGltf->meshes.size()) {
946 auto const meshData = meshManager.GetData(meshEntity);
947 auto const& mesh = *static_cast<const MeshComponent*>(meshData->RLock());
948
949 auto const& submeshes = mesh.submeshes;
950 auto const& originalPrimitives = originalGltf->meshes[meshIndex]->primitives;
951 std::transform(submeshes.begin(), submeshes.end(), originalPrimitives.begin(),
952 std::back_inserter(exportMesh->primitives),
953 MeshPrimitiveGenerator { materialManager, buffer, usedMaterials });
954
955 if (const auto nameId = nameManager.GetComponentId(meshEntity);
956 nameId != IComponentManager::INVALID_COMPONENT_ID) {
957 exportMesh->name = nameManager.Get(nameId).name;
958 }
959 // NOTE: exportMesh->weights
960 } else {
961 // couldn't find original glTF.
962 for (const auto& node : result.data->nodes) {
963 if (node->mesh == exportMesh.get()) {
964 node->mesh = nullptr;
965 }
966 }
967 exportMesh.reset();
968 }
969 }
970 }
971 result.data->meshes.erase(
972 std::remove(result.data->meshes.begin(), result.data->meshes.end(), nullptr), result.data->meshes.end());
973 }
974 return usedMaterials;
975 }
976
GetTextureIndex(const MaterialComponent & materialDesc,const MaterialComponent::TextureIndex textureIndex,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)977 inline uint32_t GetTextureIndex(const MaterialComponent& materialDesc,
978 const MaterialComponent::TextureIndex textureIndex, TextureHelper& textureHelper,
979 const IRenderHandleComponentManager& gpuHandleManager)
980 {
981 RenderHandleReference image;
982 if (auto handle = gpuHandleManager.Read(materialDesc.textures[textureIndex].image); handle) {
983 image = handle->reference;
984 }
985 if (image) {
986 auto const imageIndex = textureHelper.GetImageIndex(image);
987 RenderHandleReference sampler;
988 if (auto handle = gpuHandleManager.Read(materialDesc.textures[textureIndex].sampler); handle) {
989 image = handle->reference;
990 }
991 auto const samplerIndex = (sampler) ? textureHelper.GetSamplerIndex(sampler) : 0xFFFFffff;
992 return textureHelper.GetTextureIndex(samplerIndex, imageIndex);
993 }
994 return GLTF_INVALID_INDEX;
995 }
996
ExportGltfMaterialMetallicRoughness(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)997 void ExportGltfMaterialMetallicRoughness(Material& exportMaterial, const MaterialComponent& materialDesc,
998 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
999 {
1000 exportMaterial.type = Material::Type::MetallicRoughness;
1001 exportMaterial.metallicRoughness.baseColorFactor =
1002 materialDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor;
1003 exportMaterial.metallicRoughness.baseColorTexture.index =
1004 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::BASE_COLOR, textureHelper, gpuHandleManager);
1005 exportMaterial.metallicRoughness.metallicFactor =
1006 materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.z;
1007 exportMaterial.metallicRoughness.roughnessFactor =
1008 materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.y;
1009 exportMaterial.metallicRoughness.metallicRoughnessTexture.index =
1010 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::MATERIAL, textureHelper, gpuHandleManager);
1011 }
1012
1013 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT) || defined(GLTF2_EXTRAS_CLEAR_COAT_MATERIAL)
ExportGltfMaterialClearcoat(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1014 void ExportGltfMaterialClearcoat(Material& exportMaterial, const MaterialComponent& materialDesc,
1015 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1016 {
1017 // Clearcoat (must not be used with pbrSpecularGlossiness or unlit).
1018 if (materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT].factor.x > 0.0f) {
1019 exportMaterial.clearcoat.factor = materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT].factor.x;
1020 exportMaterial.clearcoat.roughness =
1021 materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS].factor.y;
1022 exportMaterial.clearcoat.texture.index =
1023 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::CLEARCOAT, textureHelper, gpuHandleManager);
1024 exportMaterial.clearcoat.roughnessTexture.index = GetTextureIndex(
1025 materialDesc, MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS, textureHelper, gpuHandleManager);
1026 exportMaterial.clearcoat.normalTexture.textureInfo.index = GetTextureIndex(
1027 materialDesc, MaterialComponent::TextureIndex::CLEARCOAT_NORMAL, textureHelper, gpuHandleManager);
1028 exportMaterial.clearcoat.normalTexture.scale =
1029 materialDesc.textures[MaterialComponent::TextureIndex::CLEARCOAT_NORMAL].factor.x;
1030 }
1031 }
1032 #endif
1033
1034 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
ExportGltfMaterialIor(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1035 void ExportGltfMaterialIor(Material& exportMaterial, const MaterialComponent& materialDesc,
1036 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1037 {
1038 // IOR (must not be used with pbrSpecularGlossiness or unlit).
1039 if (materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.w != 0.04f) {
1040 const auto refSqr = Math::sqrt(materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.w);
1041 exportMaterial.ior.ior = (1.f + refSqr) / (1.f - refSqr);
1042 }
1043 }
1044 #endif
1045
1046 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
ExportGltfMaterialSheen(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1047 void ExportGltfMaterialSheen(Material& exportMaterial, const MaterialComponent& materialDesc,
1048 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1049 {
1050 // Sheen (must not be used with pbrSpecularGlossiness or unlit).
1051 if ((materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor.x > 0.0f) ||
1052 (materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor.y > 0.0f) ||
1053 (materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor.z > 0.0f)) {
1054 exportMaterial.sheen.factor = materialDesc.textures[MaterialComponent::TextureIndex::SHEEN].factor;
1055 exportMaterial.sheen.texture.index =
1056 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::SHEEN, textureHelper, gpuHandleManager);
1057 }
1058 }
1059 #endif
1060
1061 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
ExportGltfMaterialSpecular(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1062 void ExportGltfMaterialSpecular(Material& exportMaterial, const MaterialComponent& materialDesc,
1063 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1064 {
1065 // Specular (must not be used with pbrSpecularGlossiness or unlit).
1066 if (materialDesc.textures[MaterialComponent::TextureIndex::SPECULAR].factor != Math::Vec4(1.f, 1.f, 1.f, 1.f)) {
1067 exportMaterial.specular.factor = materialDesc.textures[MaterialComponent::TextureIndex::SPECULAR].factor.w;
1068 exportMaterial.specular.texture.index =
1069 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::SPECULAR, textureHelper, gpuHandleManager);
1070 exportMaterial.specular.color = materialDesc.textures[MaterialComponent::TextureIndex::SPECULAR].factor;
1071 exportMaterial.specular.colorTexture.index =
1072 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::SPECULAR, textureHelper, gpuHandleManager);
1073 }
1074 }
1075 #endif
1076 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
ExportGltfMaterialTransmission(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1077 void ExportGltfMaterialTransmission(Material& exportMaterial, const MaterialComponent& materialDesc,
1078 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1079 {
1080 // Transmission (must not be used with pbrSpecularGlossiness or unlit).
1081 if (materialDesc.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x > 0.0f) {
1082 exportMaterial.transmission.factor =
1083 materialDesc.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x;
1084 exportMaterial.transmission.texture.index = GetTextureIndex(
1085 materialDesc, MaterialComponent::TextureIndex::TRANSMISSION, textureHelper, gpuHandleManager);
1086 }
1087 }
1088 #endif
1089
1090 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
ExportGltfMaterialSpecularGlossiness(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1091 void ExportGltfMaterialSpecularGlossiness(Material& exportMaterial, const MaterialComponent& materialDesc,
1092 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1093 {
1094 exportMaterial.type = Material::Type::SpecularGlossiness;
1095 exportMaterial.specularGlossiness.diffuseFactor =
1096 materialDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor;
1097 exportMaterial.specularGlossiness.diffuseTexture.index =
1098 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::BASE_COLOR, textureHelper, gpuHandleManager);
1099 exportMaterial.specularGlossiness.specularFactor =
1100 materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor;
1101 exportMaterial.specularGlossiness.specularGlossinessTexture.index =
1102 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::MATERIAL, textureHelper, gpuHandleManager);
1103 exportMaterial.specularGlossiness.glossinessFactor =
1104 materialDesc.textures[MaterialComponent::TextureIndex::MATERIAL].factor.w;
1105 }
1106 #endif
1107
1108 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_UNLIT)
ExportGltfMaterialUnlit(Material & exportMaterial,const MaterialComponent & materialDesc,TextureHelper & textureHelper,const IRenderHandleComponentManager & gpuHandleManager)1109 void ExportGltfMaterialUnlit(Material& exportMaterial, const MaterialComponent& materialDesc,
1110 TextureHelper& textureHelper, const IRenderHandleComponentManager& gpuHandleManager)
1111 {
1112 exportMaterial.type = Material::Type::Unlit;
1113 exportMaterial.metallicRoughness.baseColorFactor =
1114 materialDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR].factor;
1115 exportMaterial.metallicRoughness.baseColorTexture.index =
1116 GetTextureIndex(materialDesc, MaterialComponent::TextureIndex::BASE_COLOR, textureHelper, gpuHandleManager);
1117 }
1118 #endif
1119
UpdateShaderStateToGltfMaterial(const IDevice * device,Material & exportMaterial,const MaterialComponent & materialDesc,const IRenderHandleComponentManager & gpuHandleManager)1120 void UpdateShaderStateToGltfMaterial(const IDevice* device, Material& exportMaterial,
1121 const MaterialComponent& materialDesc, const IRenderHandleComponentManager& gpuHandleManager)
1122 {
1123 if ((materialDesc.materialShader.shader || materialDesc.materialShader.graphicsState) && device) {
1124 const IShaderManager& shaderMgr = device->GetShaderManager();
1125 if (materialDesc.materialShader.graphicsState) {
1126 const auto handle = gpuHandleManager.GetRenderHandleReference(materialDesc.materialShader.graphicsState);
1127 if (handle.GetHandleType() == RenderHandleType::GRAPHICS_STATE && handle) {
1128 const GraphicsState gfxState = shaderMgr.GetGraphicsState(handle);
1129 if (gfxState.rasterizationState.cullModeFlags == CullModeFlagBits::CORE_CULL_MODE_NONE) {
1130 exportMaterial.doubleSided = true;
1131 }
1132 if (gfxState.colorBlendState.colorAttachmentCount > 0) {
1133 if (gfxState.colorBlendState.colorAttachments[0].enableBlend) {
1134 exportMaterial.alphaMode = AlphaMode::BLEND;
1135 }
1136 }
1137 }
1138 } else if (materialDesc.materialShader.shader) {
1139 const auto handle = gpuHandleManager.GetRenderHandleReference(materialDesc.materialShader.shader);
1140 if (handle.GetHandleType() == RenderHandleType::SHADER_STATE_OBJECT && handle) {
1141 const RenderHandleReference gfxHandle = shaderMgr.GetGraphicsStateHandleByShaderHandle(handle);
1142 const GraphicsState gfxState = shaderMgr.GetGraphicsState(gfxHandle);
1143 if (gfxState.rasterizationState.cullModeFlags == CullModeFlagBits::CORE_CULL_MODE_NONE) {
1144 exportMaterial.doubleSided = true;
1145 }
1146 if (gfxState.colorBlendState.colorAttachmentCount > 0) {
1147 if (gfxState.colorBlendState.colorAttachments[0].enableBlend) {
1148 exportMaterial.alphaMode = AlphaMode::BLEND;
1149 }
1150 }
1151 }
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 (auto const 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 (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 (auto const 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 auto const 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(Data const & data)1358 json::value ExportAccessors(Data const& data)
1359 {
1360 json::value jsonAccessors = json::value::array {};
1361 for (auto const& 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 return jsonAccessors;
1394 }
1395
ExportAnimations(Data const & data)1396 json::value ExportAnimations(Data const& data)
1397 {
1398 json::value jsonAnimations = json::value::array {};
1399 for (auto const& animation : data.animations) {
1400 json::value jsonAnimation = json::value::object {};
1401 {
1402 json::value jsonSamplers = json::value::array {};
1403 for (auto const& 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 (auto const& 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 (auto const 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"] = std::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 return jsonAnimations;
1437 }
1438
ExportBuffers(Data const & data,vector<string> & strings)1439 json::value ExportBuffers(Data const& data, vector<string>& strings)
1440 {
1441 json::value jsonBuffers = json::value::array {};
1442 for (auto const& 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 return jsonBuffers;
1456 }
1457
ExportBufferViews(Data const & data)1458 json::value ExportBufferViews(Data const& data)
1459 {
1460 json::value jsonBufferViews = json::value::array {};
1461 for (auto const& 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 return jsonBufferViews;
1482 }
1483
ExportCameras(Data const & data)1484 json::value ExportCameras(Data const& data)
1485 {
1486 json::value jsonCameras = json::value::array {};
1487
1488 for (auto const& 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 return jsonCameras;
1519 }
1520
ExportImages(Data const & data)1521 json::value ExportImages(Data const& data)
1522 {
1523 json::value jsonImages = json::value::array {};
1524 for (auto const& 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 return 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_IOR)
ExportIor(const Material::Ior & ior)1627 json::value ExportIor(const Material::Ior& ior)
1628 {
1629 json::value jsonIor = json::value::object {};
1630 if (ior.ior != 1.5f) {
1631 jsonIor["ior"] = ior.ior;
1632 }
1633 return jsonIor;
1634 }
1635 #endif
1636
1637 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
ExportSheen(const Material::Sheen & sheen)1638 json::value ExportSheen(const Material::Sheen& sheen)
1639 {
1640 json::value jsonSheen = json::value::object {};
1641 if (sheen.factor != Math::Vec3 {}) {
1642 jsonSheen["sheenColorFactor"] = sheen.factor.data;
1643 }
1644 if (sheen.texture.index != GLTF_INVALID_INDEX) {
1645 jsonSheen["sheenColorTexture"] = ExportTextureInfo(sheen.texture);
1646 }
1647 if (sheen.roughness != 0.f) {
1648 jsonSheen["sheenRoughnessFactor"] = sheen.roughness;
1649 }
1650 if (sheen.roughnessTexture.index != GLTF_INVALID_INDEX) {
1651 jsonSheen["sheenRoughnessTexture"] = ExportTextureInfo(sheen.roughnessTexture);
1652 }
1653 return jsonSheen;
1654 }
1655 #endif
1656
1657 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
ExportSpecular(const Material::Specular & specular)1658 json::value ExportSpecular(const Material::Specular& specular)
1659 {
1660 json::value jsonSpecular = json::value::object {};
1661 if (specular.factor != 1.f) {
1662 jsonSpecular["specularFactor"] = specular.factor;
1663 }
1664 if (specular.texture.index != GLTF_INVALID_INDEX) {
1665 jsonSpecular["specularTexture"] = ExportTextureInfo(specular.texture);
1666 }
1667 if (specular.color != Math::Vec3(1.f, 1.f, 1.f)) {
1668 jsonSpecular["specularColorFactor"] = specular.color.data;
1669 }
1670 if (specular.colorTexture.index != GLTF_INVALID_INDEX) {
1671 jsonSpecular["specularColorTexture"] = ExportTextureInfo(specular.colorTexture);
1672 }
1673 return jsonSpecular;
1674 }
1675 #endif
1676 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
ExportTransmission(const Material::Transmission & transmission)1677 json::value ExportTransmission(const Material::Transmission& transmission)
1678 {
1679 json::value jsonTransmission = json::value::object {};
1680 if (transmission.factor != 0.f) {
1681 jsonTransmission["transmissionFactor"] = transmission.factor;
1682 }
1683 if (transmission.texture.index != GLTF_INVALID_INDEX) {
1684 jsonTransmission["transmissionTexture"] = ExportTextureInfo(transmission.texture);
1685 }
1686 return jsonTransmission;
1687 }
1688 #endif
1689
ExportMaterialExtensions(const Material & material,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1690 json::value ExportMaterialExtensions(
1691 const Material& material, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1692 {
1693 json::value jsonExtensions = json::value::object {};
1694 if (material.type == Material::Type::SpecularGlossiness) {
1695 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_PBRSPECULARGLOSSINESS)
1696 jsonExtensions["KHR_materials_pbrSpecularGlossiness"] = ExportSpecularGlossiness(material);
1697 AppendUnique(jsonExtensionsUsed, "KHR_materials_pbrSpecularGlossiness");
1698 #endif
1699 } else if (material.type == Material::Type::Unlit) {
1700 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_UNLIT)
1701 jsonExtensions["KHR_materials_unlit"] = json::value::object {};
1702 AppendUnique(jsonExtensionsUsed, "KHR_materials_unlit");
1703 #endif
1704 } else if (material.type == Material::Type::TextureSheetAnimation) {
1705 }
1706 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_CLEARCOAT)
1707 if (auto clearcoat = ExportClearcoat(material.clearcoat); !clearcoat.empty()) {
1708 jsonExtensions["KHR_materials_clearcoat"] = move(clearcoat);
1709 AppendUnique(jsonExtensionsUsed, "KHR_materials_clearcoat");
1710 }
1711 #endif
1712 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_IOR)
1713 if (auto ior = ExportIor(material.ior); !ior.empty()) {
1714 jsonExtensions["KHR_materials_ior"] = move(ior);
1715 AppendUnique(jsonExtensionsUsed, "KHR_materials_ior");
1716 }
1717 #endif
1718 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SHEEN)
1719 if (auto sheen = ExportSheen(material.sheen); !sheen.empty()) {
1720 jsonExtensions["KHR_materials_sheen"] = move(sheen);
1721 AppendUnique(jsonExtensionsUsed, "KHR_materials_sheen");
1722 }
1723 #endif
1724 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_SPECULAR)
1725 if (auto specular = ExportSpecular(material.specular); !specular.empty()) {
1726 jsonExtensions["KHR_materials_specular"] = move(specular);
1727 AppendUnique(jsonExtensionsUsed, "KHR_materials_specular");
1728 }
1729 #endif
1730 #if defined(GLTF2_EXTENSION_KHR_MATERIALS_TRANSMISSION)
1731 if (auto transmission = ExportTransmission(material.transmission); !transmission.empty()) {
1732 jsonExtensions["KHR_materials_transmission"] = move(transmission);
1733 AppendUnique(jsonExtensionsUsed, "KHR_materials_transmission");
1734 }
1735 #endif
1736 return jsonExtensions;
1737 }
1738
ExportMaterialExtras(const Material & material)1739 json::value ExportMaterialExtras(const Material& material)
1740 {
1741 auto jsonExtras = json::value::object {};
1742 return jsonExtras;
1743 }
1744
ExportMaterials(Data const & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1745 json::value ExportMaterials(Data const& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1746 {
1747 json::value jsonMaterials = json::value::array {};
1748 for (auto const& material : data.materials) {
1749 json::value jsonMaterial = json::value::object {};
1750 if (!material->name.empty()) {
1751 jsonMaterial["name"] = string_view(material->name);
1752 }
1753 if (material->type == Material::Type::MetallicRoughness || material->type == Material::Type::Unlit) {
1754 jsonMaterial["pbrMetallicRoughness"] = ExportMetallicRoughness(*material);
1755 }
1756 if (auto jsonExtensions = ExportMaterialExtensions(*material, jsonExtensionsUsed, jsonExtensionsRequired);
1757 !jsonExtensions.empty()) {
1758 jsonMaterial["extensions"] = move(jsonExtensions);
1759 }
1760
1761 if (material->normalTexture.textureInfo.index != GLTF_INVALID_INDEX) {
1762 auto& jsonNormalTexture = jsonMaterial["normalTexture"] =
1763 ExportTextureInfo(material->normalTexture.textureInfo);
1764 if (material->normalTexture.scale != 1.f) {
1765 jsonNormalTexture["scale"] = material->normalTexture.scale;
1766 }
1767 }
1768 if (material->occlusionTexture.textureInfo.index != GLTF_INVALID_INDEX) {
1769 auto& jsonOcclusionTexture = jsonMaterial["occlusionTexture"] =
1770 ExportTextureInfo(material->occlusionTexture.textureInfo);
1771 if (material->occlusionTexture.strength < 1.f) {
1772 jsonOcclusionTexture["strength"] = material->occlusionTexture.strength;
1773 }
1774 }
1775 if (material->emissiveTexture.index != GLTF_INVALID_INDEX) {
1776 jsonMaterial["emissiveTexture"] = ExportTextureInfo(material->emissiveTexture);
1777 }
1778 if (material->emissiveFactor != DEFAULT_EMISSIVE_FACTOR) {
1779 jsonMaterial["emissiveFactor"] = material->emissiveFactor.data;
1780 }
1781 if (material->alphaMode != AlphaMode::OPAQUE) {
1782 jsonMaterial["alphaMode"] = GetAlphaMode(material->alphaMode);
1783 }
1784 if (material->alphaCutoff != 0.5f) {
1785 jsonMaterial["alphaCutoff"] = material->alphaCutoff;
1786 }
1787 if (material->doubleSided) {
1788 jsonMaterial["doubleSided"] = material->doubleSided;
1789 }
1790 if (auto jsonExtras = ExportMaterialExtras(*material); !jsonExtras.empty()) {
1791 jsonMaterial["extras"] = move(jsonExtras);
1792 }
1793 jsonMaterials.array_.push_back(move(jsonMaterial));
1794 }
1795 return jsonMaterials;
1796 }
1797
ExportMeshPrimitive(const MeshPrimitive & primitive,const vector<unique_ptr<Accessor>> & accessors,json::value & jsonTargetNames)1798 json::value ExportMeshPrimitive(
1799 const MeshPrimitive& primitive, const vector<unique_ptr<Accessor>>& accessors, json::value& jsonTargetNames)
1800 {
1801 json::value jsonPrimitive = json::value::object {};
1802 {
1803 json::value jsonAttributes = json::value::object {};
1804 for (auto const& attribute : primitive.attributes) {
1805 auto type = GetAttributeType(attribute.attribute);
1806 jsonAttributes[type] = FindObjectIndex(accessors, *attribute.accessor);
1807 }
1808 jsonPrimitive["attributes"] = move(jsonAttributes);
1809 }
1810 if (primitive.indices) {
1811 jsonPrimitive["indices"] = FindObjectIndex(accessors, *primitive.indices);
1812 }
1813 if (primitive.materialIndex != GLTF_INVALID_INDEX) {
1814 jsonPrimitive["material"] = primitive.materialIndex;
1815 }
1816 if (primitive.mode != RenderMode::TRIANGLES) {
1817 jsonPrimitive["mode"] = static_cast<int>(primitive.mode);
1818 }
1819 if (!primitive.targets.empty()) {
1820 json::value jsonTargets = json::value::array {};
1821 for (auto const& target : primitive.targets) {
1822 json::value jsonTarget = json::value::object {};
1823 for (auto const& attribute : target.target) {
1824 auto type = GetAttributeType(attribute.attribute);
1825 jsonTarget[type] = FindObjectIndex(accessors, *attribute.accessor);
1826 }
1827 jsonTargets.array_.push_back(move(jsonTarget));
1828 if (!target.name.empty()) {
1829 jsonTargetNames.array_.push_back(string_view(target.name));
1830 }
1831 }
1832 jsonPrimitive["targets"] = move(jsonTargets);
1833 }
1834 return jsonPrimitive;
1835 }
1836
ExportMeshes(Data const & data)1837 json::value ExportMeshes(Data const& data)
1838 {
1839 json::value jsonMeshes = json::value::array {};
1840 for (auto const& mesh : data.meshes) {
1841 json::value jsonMesh = json::value::object {};
1842 json::value jsonExtras = json::value::object {};
1843 {
1844 json::value jsonPrimitives = json::value::array {};
1845 json::value jsonTargetNames = json::value::array {};
1846 for (auto const& primitive : mesh->primitives) {
1847 jsonPrimitives.array_.push_back(ExportMeshPrimitive(primitive, data.accessors, jsonTargetNames));
1848 }
1849 jsonMesh["primitives"] = move(jsonPrimitives);
1850 if (!jsonTargetNames.empty()) {
1851 jsonExtras["targetNames"] = move(jsonTargetNames);
1852 }
1853 }
1854 if (!mesh->weights.empty()) {
1855 jsonMesh["weights"] = mesh->weights;
1856 }
1857 if (!mesh->name.empty()) {
1858 jsonMesh["name"] = string_view(mesh->name);
1859 }
1860 if (!jsonExtras.empty()) {
1861 jsonMesh["extras"] = move(jsonExtras);
1862 }
1863 jsonMeshes.array_.push_back(move(jsonMesh));
1864 }
1865 return jsonMeshes;
1866 }
1867
ExportNodeExtensions(Data const & data,const Node & node,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1868 json::value ExportNodeExtensions(
1869 Data const& data, const Node& node, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1870 {
1871 json::value jsonExtensions = json::value::object {};
1872 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
1873 if (node.light) {
1874 json::value jsonKHRLights = json::value::object {};
1875 jsonKHRLights["light"] = FindObjectIndex(data.lights, *node.light);
1876 jsonExtensions["KHR_lights_punctual"] = move(jsonKHRLights);
1877 AppendUnique(jsonExtensionsUsed, "KHR_lights_punctual");
1878 }
1879 #endif
1880 return jsonExtensions;
1881 }
1882
ExportNodes(Data const & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)1883 json::value ExportNodes(Data const& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
1884 {
1885 json::value jsonNodes = json::value::array {};
1886
1887 for (auto const& node : data.nodes) {
1888 json::value jsonNodeObject = json::value::object {};
1889
1890 if (node->camera) {
1891 jsonNodeObject["camera"] = FindObjectIndex(data.cameras, *node->camera);
1892 }
1893
1894 if (!node->tmpChildren.empty()) {
1895 jsonNodeObject["children"] = node->tmpChildren;
1896 }
1897
1898 if (!node->name.empty()) {
1899 jsonNodeObject["name"] = string_view(node->name);
1900 }
1901
1902 if (node->skin) {
1903 jsonNodeObject["skin"] = FindObjectIndex(data.skins, *node->skin);
1904 }
1905
1906 if (node->mesh) {
1907 jsonNodeObject["mesh"] = FindObjectIndex(data.meshes, *node->mesh);
1908 }
1909
1910 if (node->usesTRS) {
1911 if (node->translation != DEFAULT_TRANSLATION) {
1912 jsonNodeObject["translation"] = node->translation.data;
1913 }
1914 if (node->rotation != DEFAULT_ROTATION) {
1915 jsonNodeObject["rotation"] = node->rotation.data;
1916 }
1917 if (node->scale != DEFAULT_SCALE) {
1918 jsonNodeObject["scale"] = node->scale.data;
1919 }
1920 } else {
1921 if (node->matrix != IDENTITY_MATRIX) {
1922 jsonNodeObject["matrix"] = node->matrix.data;
1923 }
1924 }
1925
1926 if (!node->weights.empty()) {
1927 jsonNodeObject["weights"] = node->weights;
1928 }
1929
1930 if (auto jsonExtensions = ExportNodeExtensions(data, *node, jsonExtensionsUsed, jsonExtensionsRequired);
1931 !jsonExtensions.empty()) {
1932 jsonNodeObject["extensions"] = move(jsonExtensions);
1933 }
1934
1935 jsonNodes.array_.push_back(move(jsonNodeObject));
1936 }
1937
1938 return jsonNodes;
1939 }
1940
ExportSamplers(Data const & data)1941 json::value ExportSamplers(Data const& data)
1942 {
1943 json::value jsonSamplers = json::value::array {};
1944 for (auto const& sampler : data.samplers) {
1945 json::value jsonSampler = json::value::object {};
1946 if (sampler->magFilter != FilterMode::LINEAR) {
1947 jsonSampler["magFilter"] = static_cast<int>(sampler->magFilter);
1948 }
1949 if (sampler->minFilter != FilterMode::LINEAR) {
1950 jsonSampler["minFilter"] = static_cast<int>(sampler->minFilter);
1951 }
1952 if (sampler->wrapS != WrapMode::REPEAT) {
1953 jsonSampler["wrapS"] = static_cast<int>(sampler->wrapS);
1954 }
1955 if (sampler->wrapT != WrapMode::REPEAT) {
1956 jsonSampler["wrapT"] = static_cast<int>(sampler->wrapT);
1957 }
1958 #ifdef EXPORT_OTHER_OBJECT_NAMES
1959 if (!sampler->name.empty()) {
1960 jsonSampler["name"] = sampler->name;
1961 }
1962 #endif
1963 jsonSamplers.array_.push_back(move(jsonSampler));
1964 }
1965 return jsonSamplers;
1966 }
1967
ExportScenes(Data const & data)1968 json::value ExportScenes(Data const& data)
1969 {
1970 json::value jsonScenes = json::value::array {};
1971 for (auto const& scene : data.scenes) {
1972 json::value jsonScene = json::value::object {};
1973
1974 if (!scene->name.empty()) {
1975 jsonScene["name"] = string_view(scene->name);
1976 }
1977
1978 json::value jsonNodes = json::value::array {};
1979 for (auto const node : scene->nodes) {
1980 jsonNodes.array_.push_back(FindObjectIndex(data.nodes, *node));
1981 }
1982 jsonScene["nodes"] = move(jsonNodes);
1983
1984 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
1985 if (scene->light) {
1986 json::value jsonExtensions = json::value::object {};
1987
1988 json::value jsonKHRLights = json::value::object {};
1989 jsonKHRLights["light"] = FindObjectIndex(data.lights, *scene->light);
1990 jsonExtensions["KHR_lights_punctual"] = move(jsonKHRLights);
1991
1992 jsonScene["extensions"] = move(jsonExtensions);
1993 }
1994 #endif
1995 jsonScenes.array_.push_back(move(jsonScene));
1996 }
1997 return jsonScenes;
1998 }
1999
ExportSkins(Data const & data)2000 json::value ExportSkins(Data const& data)
2001 {
2002 json::value jsonSkins = json::value::array {};
2003 for (auto const& skin : data.skins) {
2004 json::value jsonSkin = json::value::object {};
2005 if (skin->inverseBindMatrices) {
2006 jsonSkin["inverseBindMatrices"] = FindObjectIndex(data.accessors, *skin->inverseBindMatrices);
2007 }
2008 if (skin->skeleton) {
2009 jsonSkin["skeleton"] = FindObjectIndex(data.nodes, *skin->skeleton);
2010 }
2011 json::value jsonJoints = json::value::array {};
2012 for (auto const joint : skin->joints) {
2013 jsonJoints.array_.push_back(FindObjectIndex(data.nodes, *joint));
2014 }
2015 jsonSkin["joints"] = move(jsonJoints);
2016 if (!skin->name.empty()) {
2017 jsonSkin["name"] = skin->name.empty();
2018 }
2019 jsonSkins.array_.push_back(move(jsonSkin));
2020 }
2021 return jsonSkins;
2022 }
2023
ExportTextures(Data const & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)2024 json::value ExportTextures(Data const& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
2025 {
2026 json::value jsonTextures = json::value::array {};
2027 for (auto const& texture : data.textures) {
2028 json::value jsonTexture = json::value::object {};
2029 if (texture->sampler) {
2030 jsonTexture["sampler"] = FindObjectIndex(data.samplers, *texture->sampler);
2031 }
2032 if (texture->image) {
2033 switch (texture->image->type) {
2034 default:
2035 case MimeType::INVALID:
2036 case MimeType::JPEG:
2037 case MimeType::PNG:
2038 case MimeType::KTX: // NOTE: this is incorrect, but there's no extension for .ktx
2039 jsonTexture["source"] = FindObjectIndex(data.images, *texture->image);
2040 break;
2041 case MimeType::DDS: {
2042 json::value jsonMsftTextureDds = json::value::object {};
2043 jsonMsftTextureDds["source"] = FindObjectIndex(data.images, *texture->image);
2044
2045 json::value jsonExtensions = json::value::object {};
2046 jsonExtensions["MSFT_texture_dds"] = move(jsonMsftTextureDds);
2047
2048 jsonTexture["extensions"] = move(jsonExtensions);
2049
2050 AppendUnique(jsonExtensionsUsed, "MSFT_texture_dds");
2051 AppendUnique(jsonExtensionsRequired, "MSFT_texture_dds");
2052 break;
2053 }
2054 case MimeType::KTX2: {
2055 json::value jsonKHRtextureBasisU = json::value::object {};
2056 jsonKHRtextureBasisU["source"] = FindObjectIndex(data.images, *texture->image);
2057
2058 json::value jsonExtensions = json::value::object {};
2059 jsonExtensions["KHR_texture_basisu"] = move(jsonKHRtextureBasisU);
2060
2061 jsonTexture["extensions"] = move(jsonExtensions);
2062
2063 AppendUnique(jsonExtensionsUsed, "KHR_texture_basisu");
2064 AppendUnique(jsonExtensionsRequired, "KHR_texture_basisu");
2065 break;
2066 }
2067 }
2068 }
2069 #ifdef EXPORT_OTHER_OBJECT_NAMES
2070 if (!texture->name.empty()) {
2071 jsonTexture["name"] = texture->name;
2072 }
2073 #endif
2074 jsonTextures.array_.push_back(move(jsonTexture));
2075 }
2076 return jsonTextures;
2077 }
2078
ExportKHRLights(Data const & data)2079 json::value ExportKHRLights(Data const& data)
2080 {
2081 json::value jsonLightArray = json::value::array {};
2082 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2083
2084 for (auto const& light : data.lights) {
2085 if (light->type == LightType::AMBIENT || light->type == LightType::INVALID) {
2086 continue;
2087 }
2088
2089 json::value jsonLightObject = json::value::object {};
2090 if (!light->name.empty()) {
2091 jsonLightObject["name"] = string_view(light->name);
2092 }
2093 if (light->color != Math::Vec3(1.f, 1.f, 1.f)) {
2094 jsonLightObject["color"] = light->color.data;
2095 }
2096 if (light->intensity != 1.f) {
2097 jsonLightObject["intensity"] = light->intensity;
2098 }
2099 jsonLightObject["type"] = GetLightType(light->type);
2100 if ((light->type == LightType::POINT || light->type == LightType::SPOT) && light->positional.range > 0.f) {
2101 jsonLightObject["range"] = light->positional.range;
2102 }
2103 if (light->type == LightType::SPOT) {
2104 json::value jsonSpotObject = json::value::object {};
2105 if (light->positional.spot.innerAngle != 0.f) {
2106 jsonSpotObject["innerConeAngle"] = light->positional.spot.innerAngle;
2107 }
2108 if (light->positional.spot.outerAngle != 0.785398163397448f) {
2109 jsonSpotObject["outerConeAngle"] = light->positional.spot.outerAngle;
2110 }
2111 if (!jsonSpotObject.empty()) {
2112 jsonLightObject["spot"] = move(jsonSpotObject);
2113 }
2114 }
2115 jsonLightArray.array_.push_back(move(jsonLightObject));
2116 }
2117 #endif
2118 json::value jsonLights = json::value::object {};
2119 jsonLights["lights"] = move(jsonLightArray);
2120 return jsonLights;
2121 }
2122
ExportExtensions(Data const & data,json::value & jsonExtensionsUsed,json::value & jsonExtensionsRequired)2123 json::value ExportExtensions(Data const& data, json::value& jsonExtensionsUsed, json::value& jsonExtensionsRequired)
2124 {
2125 json::value jsonExtensions = json::value::object {};
2126
2127 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2128 if (!data.lights.empty()) {
2129 if (auto jsonKHRLights = ExportKHRLights(data); !jsonKHRLights.empty()) {
2130 jsonExtensions["KHR_lights_punctual"] = move(jsonKHRLights);
2131 AppendUnique(jsonExtensionsUsed, "KHR_lights_punctual");
2132 }
2133 }
2134 #endif
2135 return jsonExtensions;
2136 }
2137
ExportAsset(string_view versionString,vector<string> & strings)2138 json::value ExportAsset(string_view versionString, vector<string>& strings)
2139 {
2140 auto jsonAsset = json::value { json::value::object {} };
2141 jsonAsset["version"] = string_view("2.0");
2142 strings.push_back("CoreEngine " + versionString);
2143 jsonAsset["generator"] = string_view(strings.back());
2144 return jsonAsset;
2145 }
2146
2147 /* Returns a JSON string generated from given GLTF2::Data. The above Export* helpers are used to convert different
2148 * parts of the data into JSON objects. */
ExportGLTFData(Data const & data,string_view versionString)2149 auto ExportGLTFData(Data const& data, string_view versionString)
2150 {
2151 vector<string> strings;
2152 auto jsonGltf = json::value { json::value::object {} };
2153
2154 auto jsonExtensionsUsed = json::value { json::value::array {} };
2155 auto jsonExtensionsRequired = json::value { json::value::array {} };
2156 jsonGltf["asset"] = ExportAsset(versionString, strings);
2157
2158 if (!data.animations.empty()) {
2159 jsonGltf["animations"] = ExportAnimations(data);
2160 }
2161
2162 if (!data.cameras.empty()) {
2163 jsonGltf["cameras"] = ExportCameras(data);
2164 }
2165
2166 if (!data.images.empty()) {
2167 jsonGltf["images"] = ExportImages(data);
2168 }
2169
2170 if (!data.materials.empty()) {
2171 jsonGltf["materials"] = ExportMaterials(data, jsonExtensionsUsed, jsonExtensionsRequired);
2172 }
2173
2174 if (!data.meshes.empty()) {
2175 jsonGltf["meshes"] = ExportMeshes(data);
2176 }
2177
2178 if (!data.nodes.empty()) {
2179 jsonGltf["nodes"] = ExportNodes(data, jsonExtensionsUsed, jsonExtensionsRequired);
2180 }
2181
2182 if (!data.samplers.empty()) {
2183 jsonGltf["samplers"] = ExportSamplers(data);
2184 }
2185
2186 if (!data.scenes.empty()) {
2187 jsonGltf["scenes"] = ExportScenes(data);
2188 jsonGltf["scene"] = 0;
2189 }
2190
2191 if (!data.skins.empty()) {
2192 jsonGltf["skins"] = ExportSkins(data);
2193 }
2194
2195 if (!data.textures.empty()) {
2196 jsonGltf["textures"] = ExportTextures(data, jsonExtensionsUsed, jsonExtensionsRequired);
2197 }
2198
2199 if (auto jsonExtensions = ExportExtensions(data, jsonExtensionsUsed, jsonExtensionsRequired);
2200 !jsonExtensions.empty()) {
2201 jsonGltf["extensions"] = move(jsonExtensions);
2202 }
2203
2204 if (!data.accessors.empty()) {
2205 jsonGltf["accessors"] = ExportAccessors(data);
2206 }
2207
2208 if (!data.bufferViews.empty()) {
2209 jsonGltf["bufferViews"] = ExportBufferViews(data);
2210 }
2211
2212 if (!data.buffers.empty()) {
2213 jsonGltf["buffers"] = ExportBuffers(data, strings);
2214 }
2215
2216 if (!jsonExtensionsUsed.empty()) {
2217 jsonGltf["extensionsUsed"] = move(jsonExtensionsUsed);
2218 }
2219 if (!jsonExtensionsRequired.empty()) {
2220 jsonGltf["extensionsRequired"] = move(jsonExtensionsRequired);
2221 }
2222
2223 return to_string(jsonGltf);
2224 }
2225 } // namespace
2226
2227 /* Writes the GLTF2::Data as a GLB file. */
SaveGLB(Data const & data,IFile & file,string_view versionString)2228 void SaveGLB(Data const& data, IFile& file, string_view versionString)
2229 {
2230 auto jsonString = ExportGLTFData(data, versionString);
2231 if (jsonString.empty()) {
2232 return;
2233 }
2234 if (auto const pad = (jsonString.size() % 4); pad) {
2235 jsonString.append(4 - pad, ' ');
2236 }
2237
2238 auto const jsonSize = static_cast<uint32_t>(jsonString.size());
2239 auto const binarySize = [](auto const& aBuffers) {
2240 size_t totalSize = 0;
2241 for (auto const& buffer : aBuffers) {
2242 totalSize += buffer->data.size();
2243 }
2244 return static_cast<uint32_t>(totalSize);
2245 }(data.buffers);
2246
2247 auto const header = GLBHeader { GLTF_MAGIC, 2,
2248 static_cast<uint32_t>(sizeof(GLBHeader) + sizeof(GLBChunk) + jsonSize + sizeof(GLBChunk) + binarySize) };
2249 file.Write(&header, sizeof(header));
2250
2251 auto const jsonChunk = GLBChunk { jsonSize, static_cast<uint32_t>(ChunkType::JSON) };
2252 file.Write(&jsonChunk, sizeof(jsonChunk));
2253
2254 file.Write(jsonString.data(), jsonSize);
2255
2256 auto const binaryChunk = GLBChunk { binarySize, static_cast<uint32_t>(ChunkType::BIN) };
2257 file.Write(&binaryChunk, sizeof(binaryChunk));
2258
2259 file.Write(data.buffers.front()->data.data(), binarySize);
2260 }
2261
2262 /* Writes the GLTF2::Data as a glTF file. */
SaveGLTF(Data const & data,IFile & file,string_view versionString)2263 void SaveGLTF(Data const& data, IFile& file, string_view versionString)
2264 {
2265 auto const jsonString = ExportGLTFData(data, versionString);
2266 file.Write(jsonString.data(), jsonString.size());
2267 }
2268
2269 /* 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)2270 bool IsExportable(ISceneNode const& node, IEcs const& ecs)
2271 {
2272 auto const nodeEntity = node.GetEntity();
2273 if (auto const nodeManager = GetManager<INodeComponentManager>(ecs); nodeManager) {
2274 return nodeManager->HasComponent(nodeEntity) && nodeManager->Get(nodeEntity).exported;
2275 }
2276 return false;
2277 }
2278
2279 namespace {
2280 /* Returns the first parent which can be exported or null if no such parent exists. */
FindExportedParent(ISceneNode const & node,IEcs const & ecs)2281 ISceneNode* FindExportedParent(ISceneNode const& node, IEcs const& ecs)
2282 {
2283 auto parent = node.GetParent();
2284 while (parent) {
2285 if (IsExportable(*parent, ecs)) {
2286 return parent;
2287 }
2288 parent = parent->GetParent();
2289 }
2290 return parent;
2291 }
2292
2293 /* 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)2294 void CombineSkippedParentTransformations(
2295 TransformComponent& transformComponent, ISceneNode const& node, IEcs const& ecs, vector<Entity> const& nodeEntities)
2296 {
2297 auto parent = node.GetParent();
2298 auto const transformManager = GetManager<ITransformComponentManager>(ecs);
2299 while (parent) {
2300 if (auto const parentIndex = FindHandleIndex(nodeEntities, parent->GetEntity());
2301 parentIndex < nodeEntities.size()) {
2302 // found an exported node and no need to continue.
2303 parent = nullptr;
2304 } else {
2305 // apply the transformation of a node which wasn't exported.
2306 if (transformManager->HasComponent(parent->GetEntity())) {
2307 auto const parentTransformComponent = transformManager->Get(parent->GetEntity());
2308
2309 auto const transformation =
2310 Math::Trs(parentTransformComponent.position, parentTransformComponent.rotation,
2311 parentTransformComponent.scale) *
2312 Math::Trs(transformComponent.position, transformComponent.rotation, transformComponent.scale);
2313
2314 Math::Vec3 skew;
2315 Math::Vec4 perspective;
2316 Math::Decompose(transformation, transformComponent.scale, transformComponent.rotation,
2317 transformComponent.position, skew, perspective);
2318 }
2319 parent = parent->GetParent();
2320 }
2321 }
2322 }
2323
GetNode(vector<unique_ptr<Node>> & nodeArray,size_t index)2324 Node& GetNode(vector<unique_ptr<Node>>& nodeArray, size_t index)
2325 {
2326 if (index < nodeArray.size()) {
2327 return *nodeArray[index];
2328 } else {
2329 return *nodeArray.emplace_back(make_unique<Node>());
2330 }
2331 }
2332
AttachMesh(IEcs const & ecs,const Entity nodeEntity,Node & exportNode,decltype(Data::meshes) & meshArray,vector<Entity> & usedMeshes)2333 void AttachMesh(IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::meshes)& meshArray,
2334 vector<Entity>& usedMeshes)
2335 {
2336 if (auto const meshManager = GetManager<IRenderMeshComponentManager>(ecs);
2337 meshManager && meshManager->HasComponent(nodeEntity)) {
2338 auto const meshHandle = meshManager->Get(nodeEntity).mesh;
2339 if (auto const meshIndex = FindOrAddIndex(usedMeshes, meshHandle); meshIndex < meshArray.size()) {
2340 exportNode.mesh = meshArray[meshIndex].get();
2341 } else {
2342 exportNode.mesh = meshArray.emplace_back(make_unique<Mesh>()).get();
2343 }
2344 }
2345 }
2346
AttachCamera(IEcs const & ecs,const Entity nodeEntity,Node & exportNode,decltype(Data::cameras) & cameraArray,Entities & entities)2347 void AttachCamera(IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::cameras)& cameraArray,
2348 Entities& entities)
2349 {
2350 if (auto const cameraManager = GetManager<ICameraComponentManager>(ecs);
2351 cameraManager && cameraManager->HasComponent(nodeEntity)) {
2352 if (auto const cameraIndex = FindOrAddIndex(entities.withCamera, nodeEntity);
2353 cameraIndex < cameraArray.size()) {
2354 exportNode.camera = cameraArray[cameraIndex].get();
2355 } else {
2356 exportNode.camera = cameraArray.emplace_back(make_unique<Camera>()).get();
2357 }
2358 }
2359 }
2360
2361 #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)2362 void AttachLight(
2363 IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::lights)& lightArray, Entities& entities)
2364 {
2365 if (auto const lightManager = GetManager<ILightComponentManager>(ecs);
2366 lightManager && lightManager->HasComponent(nodeEntity)) {
2367 if (auto const lightIndex = FindOrAddIndex(entities.withLight, nodeEntity); lightIndex < lightArray.size()) {
2368 exportNode.light = lightArray[lightIndex].get();
2369 } else {
2370 exportNode.light = lightArray.emplace_back(make_unique<KHRLight>()).get();
2371 }
2372 }
2373 }
2374 #endif
2375
AttachParent(const ISceneNode & node,const IEcs & ecs,Scene & scene,Node & exportNode,uint32_t nodeIndex,const vector<Entity> & nodeEntities,decltype(Data::nodes) & nodeArray)2376 void AttachParent(const ISceneNode& node, const IEcs& ecs, Scene& scene, Node& exportNode, uint32_t nodeIndex,
2377 const vector<Entity>& nodeEntities, decltype(Data::nodes)& nodeArray)
2378 {
2379 if (const auto* parent = FindExportedParent(node, ecs); parent) {
2380 if (auto const parentIndex = FindHandleIndex(nodeEntities, parent->GetEntity());
2381 parentIndex < nodeArray.size()) {
2382 // Parent has been exported -> node has a parent and will be added to the parents list of children.
2383 exportNode.parent = nodeArray[parentIndex].get();
2384 if (std::none_of(exportNode.parent->children.begin(), exportNode.parent->children.end(),
2385 [&exportNode](const auto childNode) { return childNode == &exportNode; })) {
2386 exportNode.parent->children.push_back(&exportNode);
2387 exportNode.parent->tmpChildren.push_back(nodeIndex);
2388 }
2389 } else {
2390 // Parent hasn't been exported i.e. it's outside this scene hierarchy -> add node as a scene root.
2391 scene.nodes.emplace_back(&exportNode);
2392 }
2393 } else {
2394 // Parent marked to be excluded from exporting -> add node as a scene root.
2395 scene.nodes.emplace_back(&exportNode);
2396 }
2397 }
2398
AttachSkin(IEcs const & ecs,const Entity nodeEntity,Node & exportNode,decltype(Data::skins) & skinArray,Entities & entities)2399 void AttachSkin(
2400 IEcs const& ecs, const Entity nodeEntity, Node& exportNode, decltype(Data::skins)& skinArray, Entities& entities)
2401 {
2402 if (auto const skinManager = GetManager<ISkinComponentManager>(ecs);
2403 skinManager && skinManager->HasComponent(nodeEntity)) {
2404 if (auto const entityIndex = FindOrAddIndex(entities.withSkin, nodeEntity); entityIndex < skinArray.size()) {
2405 exportNode.skin = skinArray[entityIndex].get();
2406 } else {
2407 exportNode.skin = skinArray.emplace_back(make_unique<Skin>()).get();
2408 }
2409 }
2410 }
2411
2412 /* Export scene node hierarcy as a glTF scene. Only nodes and indices to other resources are written to GLTF2::Data.
2413 * Mesh, Image etc. have to be written to GLTF2::Data separately based on the usedMeshes, and entities output
2414 * parameters.
2415 * @param node Scene node to consider to be exported.
2416 * @param ecs ECS instance where node and related data lives.
2417 * @param scene Scene where nodes will be included.
2418 * @param data Exported nodes and placeholders for meshes, cameras, lights etc. will be stored here.
2419 * @param nodeEntities Entities with NodeComponents which were exported.
2420 * @param usedMeshes Handles to meshes used by exported entities.
2421 * @param entities Collections of entities having special components attached such as cameras or lights.
2422 */
RecursivelyExportNode(ISceneNode const & node,IEcs const & ecs,Scene & scene,Data & data,vector<Entity> & nodeEntities,vector<Entity> & usedMeshes,Entities & entities)2423 void RecursivelyExportNode(ISceneNode const& node, IEcs const& ecs, Scene& scene, Data& data,
2424 vector<Entity>& nodeEntities, vector<Entity>& usedMeshes, Entities& entities)
2425 {
2426 if (!IsExportable(node, ecs)) {
2427 // if this node shouldn't be exported, try exporting the child nodes.
2428 for (const auto* child : node.GetChildren()) {
2429 RecursivelyExportNode(*child, ecs, scene, data, nodeEntities, usedMeshes, entities);
2430 }
2431 return;
2432 }
2433
2434 auto const nodeEntity = node.GetEntity();
2435 auto const nodeIndex = FindOrAddIndex(nodeEntities, nodeEntity);
2436 auto& exportNode = GetNode(data.nodes, nodeIndex);
2437
2438 // name
2439 exportNode.name = node.GetName();
2440
2441 // mesh
2442 AttachMesh(ecs, nodeEntity, exportNode, data.meshes, usedMeshes);
2443
2444 // camera
2445 AttachCamera(ecs, nodeEntity, exportNode, data.cameras, entities);
2446
2447 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2448 // light
2449 AttachLight(ecs, nodeEntity, exportNode, data.lights, entities);
2450 #endif
2451
2452 // parent
2453 AttachParent(node, ecs, scene, exportNode, nodeIndex, nodeEntities, data.nodes);
2454
2455 // isJoint
2456 // children, tmpChildren, the child will actually add itself to the parents list of children
2457 for (const auto* child : node.GetChildren()) {
2458 RecursivelyExportNode(*child, ecs, scene, data, nodeEntities, usedMeshes, entities);
2459 }
2460
2461 // skin
2462 // tmpSkin
2463 AttachSkin(ecs, nodeEntity, exportNode, data.skins, entities);
2464
2465 // usesTRS, translation, rotation, scale
2466 if (const auto* transformManager = GetManager<ITransformComponentManager>(ecs);
2467 transformManager && transformManager->HasComponent(nodeEntity)) {
2468 exportNode.usesTRS = true;
2469
2470 auto transformComponent = transformManager->Get(nodeEntity);
2471 CombineSkippedParentTransformations(transformComponent, node, ecs, nodeEntities);
2472 transformComponent.rotation = Math::Normalize(transformComponent.rotation);
2473 exportNode.translation = transformComponent.position;
2474 exportNode.rotation = transformComponent.rotation;
2475 exportNode.scale = transformComponent.scale;
2476 }
2477
2478 // NOTE: weights, defaults are not exported
2479 }
2480 } // namespace
2481
2482 // Internal exporting function.
ExportGLTF(IEngine & engine,const IEcs & ecs)2483 ExportResult ExportGLTF(IEngine& engine, const IEcs& ecs)
2484 {
2485 auto result = ExportResult(make_unique<Data>(engine.GetFileManager()));
2486
2487 // We write all the binary data to a single buffer.
2488 auto& exportBuffer = result.data->buffers.emplace_back(make_unique<Buffer>());
2489
2490 // Helper for gathering bufferViews and accessors, and packing data in the buffer.
2491 BufferHelper bufferHelper(*exportBuffer, result.data->bufferViews, result.data->accessors);
2492
2493 Entities entities;
2494 vector<Entity> usedMeshes;
2495
2496 // Create Nodes and Scenes.
2497 auto const nameManager = GetManager<INameComponentManager>(ecs);
2498 auto const nodeManager = GetManager<INodeComponentManager>(ecs);
2499 auto const nodeSystem = GetSystem<INodeSystem>(ecs);
2500 if (nodeManager && nodeSystem) {
2501 auto& sceneArray = result.data->scenes;
2502
2503 auto const nodeCount = nodeManager->GetComponentCount();
2504 auto& nodeArray = result.data->nodes;
2505 nodeArray.reserve(nodeCount);
2506 entities.nodes.reserve(nodeCount);
2507
2508 auto& exportScene = *sceneArray.emplace_back(make_unique<Scene>());
2509
2510 for (const auto* child : nodeSystem->GetRootNode().GetChildren()) {
2511 RecursivelyExportNode(*child, ecs, exportScene, *result.data, entities.nodes, usedMeshes, entities);
2512 }
2513 if (exportScene.nodes.empty()) {
2514 sceneArray.pop_back();
2515 }
2516
2517 // Create Skins.
2518 ExportGltfSkins(ecs, entities, nodeArray, result, bufferHelper);
2519
2520 // Create Cameras.
2521 ExportGltfCameras(ecs, entities, result);
2522
2523 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
2524 // Create KHRLights.
2525 ExportGltfLight(ecs, entities, result);
2526 #endif
2527 // Create Animations.
2528 ExportGltfAnimations(ecs, entities, result, bufferHelper);
2529 }
2530 unordered_map<string, IGLTFData::Ptr> originalGltfs;
2531 // Create Meshes for the mesh handles referenced by exported nodes. Materials referenced by the meshes will
2532 // be gathered for exporting.
2533 auto meshManager = GetManager<IMeshComponentManager>(ecs);
2534 auto materialManager = GetManager<IMaterialComponentManager>(ecs);
2535 auto uriManager = GetManager<IUriComponentManager>(ecs);
2536 auto usedMaterials = ExportGltfMeshes(*meshManager, *nameManager, *uriManager, *materialManager,
2537 engine.GetFileManager(), usedMeshes, result, bufferHelper, originalGltfs);
2538
2539 ExportGltfMaterials(engine, *materialManager, *nameManager, *uriManager, usedMaterials, result, bufferHelper);
2540
2541 // Write image data to the buffer
2542 ExportImageData(engine.GetFileManager(), result, bufferHelper, originalGltfs);
2543
2544 exportBuffer->byteLength = exportBuffer->data.size();
2545
2546 return result;
2547 }
2548 } // namespace GLTF2
2549
2550 CORE3D_END_NAMESPACE()
2551