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