2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "dali-scene-loader/public-api/dli-loader.h"
22 #include "dali/public-api/object/property-array.h"
23 #include "dali/devel-api/common/map-wrapper.h"
24 #include "dali-toolkit/devel-api/builder/json-parser.h"
25 #include "dali/integration-api/debug.h"
33 #include "dali-scene-loader/public-api/parse-renderer-state.h"
34 #include "dali-scene-loader/public-api/skinning-details.h"
35 #include "dali-scene-loader/public-api/load-result.h"
36 #include "dali-scene-loader/public-api/scene-definition.h"
37 #include "dali-scene-loader/public-api/animation-definition.h"
38 #include "dali-scene-loader/public-api/alpha-function-helper.h"
39 #include "dali-scene-loader/public-api/camera-parameters.h"
40 #include "dali-scene-loader/public-api/light-parameters.h"
41 #include "dali-scene-loader/public-api/blend-shape-details.h"
42 #include "dali-scene-loader/public-api/ktx-loader.h"
43 #include "dali-scene-loader/public-api/utils.h"
44 #include "dali-scene-loader/internal/json-util.h"
46 #define DLI_0_1_COMPATIBILITY
50 using namespace Toolkit;
55 namespace rs = RendererState;
59 const std::string NODES = "nodes";
60 const std::string SCENES = "scenes";
61 const std::string NODE = "node";
62 const std::string URI = "uri";
63 const std::string URL = "url";
64 const std::string CUSTOMIZATION = "customization";
65 const std::string HINTS = "hints";
66 const std::string NAME("name");
67 const std::string BLEND_SHAPE_HEADER("blendShapeHeader");
68 const std::string BLEND_SHAPES("blendShapes");
69 const std::string BLEND_SHAPE_VERSION_1_0("1.0");
70 const std::string BLEND_SHAPE_VERSION_2_0("2.0");
71 const std::string VERSION("version");
73 const char* const SHADOW_MAP_SIZE = "shadowMapSize";
74 const char* const ORTHOGRAPHIC_SIZE = "orthographicSize";
75 const char* const PIXEL_UNITS = "px";
77 const char SLASH = '/';
79 void ReadModelTransform(const TreeNode* node, Quaternion& orientation, Vector3& translation, Vector3& scale)
81 float num[16u] = { .0f };
83 if (ReadVector(node->GetChild("matrix"), num, 16u))
86 mat.GetTransformComponents(translation, orientation, scale);
90 if (ReadVector(node->GetChild("angle"), num, 3u))
92 orientation = Quaternion(Radian(Degree(num[0u])), Radian(Degree(num[1u])), Radian(Degree(num[2u])));
95 if (ReadVector(node->GetChild("position"), num, 3u))
97 translation = Vector3(num);
102 bool ReadAttribBlob(const TreeNode* node, MeshDefinition::Blob& buffer)
104 return ReadBlob(node, buffer.mOffset, buffer.mLength);
107 bool ReadAttribAccessor(const TreeNode* node, MeshDefinition::Accessor& accessor)
109 return ReadBlob(node, accessor.mBlob.mOffset, accessor.mBlob.mLength);
112 bool ReadColorCode(const TreeNode* node, Vector4& color,
113 DliLoader::ConvertColorCode convertColorCode)
115 if (!node || !convertColorCode)
120 color = convertColorCode(node->GetString());
125 bool ReadColorCodeOrColor(const TreeNode* node, Vector4& color,
126 DliLoader::ConvertColorCode convertColorCode)
128 return ReadColorCode(node->GetChild("colorCode"), color, convertColorCode) ||
129 ReadColor(node->GetChild("color"), color);
132 RendererState::Type ReadRendererState(const TreeNode& tnRendererState)
134 if (tnRendererState.GetType() == TreeNode::INTEGER)
136 return static_cast<RendererState::Type>(tnRendererState.GetInteger());
138 else if (tnRendererState.GetType() == TreeNode::STRING)
140 return RendererState::Parse(tnRendererState.GetString());
148 ///@brief Reads arc properties.
149 void ReadArcField(const TreeNode* eArc, ArcNode& arc)
151 ReadBool(eArc->GetChild("antiAliasing"), arc.mAntiAliasing);
152 ReadInt(eArc->GetChild("arcCaps"), arc.mArcCaps);
153 ReadFloat(eArc->GetChild("radius"), arc.mRadius);
155 arc.mStartAngleDegrees = .0f;
156 ReadFloat(eArc->GetChild("startAngle"), arc.mStartAngleDegrees);
158 arc.mEndAngleDegrees = .0f;
159 ReadFloat(eArc->GetChild("endAngle"), arc.mEndAngleDegrees);
162 const TreeNode *GetNthChild(const TreeNode *node, uint32_t index)
165 for (TreeNode::ConstIterator it = (*node).CBegin(); it != (*node).CEnd(); ++it, ++i)
169 return &((*it).second);
175 const TreeNode* RequireChild(const TreeNode* node, const std::string& childName)
177 auto child = node->GetChild(childName);
180 ExceptionFlinger flinger(ASSERT_LOCATION);
181 flinger << "Failed to find child node '" << childName << "'";
182 if (auto nodeName = node->GetName())
184 flinger << " on '" << nodeName << "'";
191 void ParseProperties(const Toolkit::TreeNode& node, Property::Array& array);
193 void ParseProperties(const Toolkit::TreeNode& node, Property::Map& map)
195 DALI_ASSERT_DEBUG(node.GetType() == TreeNode::OBJECT);
196 for (auto i0 = node.CBegin(), i1 = node.CEnd(); i0 != i1; ++i0)
199 switch (kv.second.GetType())
201 case TreeNode::ARRAY:
203 Property::Array array;
204 ParseProperties(kv.second, array);
205 map.Insert(kv.first, array);
208 case TreeNode::OBJECT:
210 Property::Map innerMap;
211 ParseProperties(kv.second, innerMap);
212 map.Insert(kv.first, innerMap);
215 case TreeNode::STRING:
216 map.Insert(kv.first, kv.second.GetString());
219 case TreeNode::INTEGER:
220 map.Insert(kv.first, kv.second.GetInteger());
223 case TreeNode::BOOLEAN:
224 map.Insert(kv.first, kv.second.GetBoolean());
227 case TreeNode::FLOAT:
228 map.Insert(kv.first, kv.second.GetFloat());
231 case TreeNode::IS_NULL:
237 void ParseProperties(const Toolkit::TreeNode& node, Property::Array& array)
239 DALI_ASSERT_DEBUG(node.GetType() == TreeNode::ARRAY);
240 for (auto i0 = node.CBegin(), i1 = node.CEnd(); i0 != i1; ++i0)
243 switch (kv.second.GetType())
245 case TreeNode::ARRAY:
247 Property::Array innerArray;
248 ParseProperties(kv.second, innerArray);
249 array.PushBack(innerArray);
252 case TreeNode::OBJECT:
255 ParseProperties(kv.second, map);
259 case TreeNode::STRING:
260 array.PushBack(kv.second.GetString());
263 case TreeNode::INTEGER:
264 array.PushBack(kv.second.GetInteger());
267 case TreeNode::BOOLEAN:
268 array.PushBack(kv.second.GetBoolean());
271 case TreeNode::FLOAT:
272 array.PushBack(kv.second.GetFloat());
275 case TreeNode::IS_NULL:
283 struct DliLoader::Impl
285 StringCallback mOnError = DefaultErrorCallback;
286 Toolkit::JsonParser mParser;
288 void ParseScene(LoadParams& params);
291 std::map<Index, Matrix> mInverseBindMatrices;
294 * @brief Due to .dli nodes being processed in depth-first traversal with orphans being
295 * ignored, features that rely on node indices (which is more compact and closer to
296 * glTF) require a mapping from .dli node indices to those in the resulting SceneDefinition.
297 * The index mapper is responsible for maintaing this mapping, and resolving node IDs
298 * once the processing of the nodes has finished.
299 * @note The resolution requires the whole scene graph to finish parsing, therefore any
300 * node extensions relying on node IDs will see the dli ID in their processor.
305 * @brief Attempts to create a mapping from a node's @a dli index to its @a scene
307 * @return Whether the operation was successful.
309 virtual bool Map(Index iDli, Index iScene) =0;
312 * @return The scene index for the node's @a dli index.
314 virtual Index Resolve(Index iDli) =0;
318 * @brief Traverses the DOM tree created by LoadDocument() in an attempt to create
319 * an intermediate representation of resources and nodes.
321 void ParseSceneInternal(Index iScene, const Toolkit::TreeNode* tnScenes,
322 const Toolkit::TreeNode* tnNodes, LoadParams& params);
324 void ParseSkeletons(const Toolkit::TreeNode* skeletons, SceneDefinition& scene, ResourceBundle& resources);
325 void ParseEnvironments(const Toolkit::TreeNode* environments, ResourceBundle& resources);
326 void ParseMaterials(const Toolkit::TreeNode* materials, ConvertColorCode convertColorCode,
327 ResourceBundle& resources);
329 void ParseNodes(const Toolkit::TreeNode* nodes, Index index, LoadParams& params);
330 void ParseNodesInternal(const Toolkit::TreeNode* nodes, Index index,
331 std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& indexMapper);
333 void ParseAnimations(const Toolkit::TreeNode* animations, LoadParams& params);
334 void ParseAnimationGroups(const Toolkit::TreeNode* animationGroups, LoadParams& params);
336 void ParseShaders(const Toolkit::TreeNode* shaders, ResourceBundle& resources);
337 void ParseMeshes(const Toolkit::TreeNode* meshes, ResourceBundle& resources);
339 void GetCameraParameters(std::vector<CameraParameters>& cameras) const;
340 void GetLightParameters(std::vector<LightParameters>& lights) const;
343 DliLoader::DliLoader()
347 DliLoader::~DliLoader() = default;
349 void DliLoader::SetErrorCallback(StringCallback onError)
351 mImpl->mOnError = onError;
354 bool DliLoader::LoadScene(const std::string& uri, LoadParams& params)
356 std::string daliBuffer = LoadTextFile(uri.c_str());
358 auto& parser = mImpl->mParser;
359 parser = JsonParser::New();
360 if (!parser.Parse(daliBuffer))
365 mImpl->ParseScene(params);
369 std::string DliLoader::GetParseError() const
371 std::stringstream stream;
373 auto& parser = mImpl->mParser;
374 if (parser.ParseError())
376 stream << "position: " << parser.GetErrorPosition() << ", line: " << parser.GetErrorLineNumber() << ", column: " << parser.GetErrorColumn() << ", description: " << parser.GetErrorDescription() << ".";
382 void DliLoader::Impl::ParseScene(LoadParams& params)
384 auto& input = params.input;
385 auto& output = params.output;
387 // get index of root node.
388 auto docRoot = mParser.GetRoot();
390 // Process resources first - these are shared
391 if (auto environments = docRoot->GetChild("environment"))
393 ParseEnvironments(environments, output.mResources); // NOTE: must precede parsing of materials
396 if (auto meshes = docRoot->GetChild("meshes"))
398 ParseMeshes(meshes, output.mResources);
401 if (auto shaders = docRoot->GetChild("shaders"))
403 ParseShaders(shaders, output.mResources);
406 if (auto materials = docRoot->GetChild("materials"))
408 ParseMaterials(materials, input.mConvertColorCode, output.mResources);
411 for (auto& c : input.mPreNodeCategoryProcessors)
413 if (auto node = docRoot->GetChild(c.first))
415 Property::Array array;
416 ParseProperties(*node, array);
417 c.second(std::move(array), mOnError);
422 Index iScene = 0; // default scene
423 ReadIndex(docRoot->GetChild("scene"), iScene);
425 auto tnScenes = RequireChild(docRoot, "scenes");
426 auto tnNodes = RequireChild(docRoot, "nodes");
427 ParseSceneInternal(iScene, tnScenes, tnNodes, params);
429 ParseSkeletons(docRoot->GetChild("skeletons"), output.mScene, output.mResources);
431 output.mScene.EnsureUniqueSkinningShaderInstances(output.mResources);
432 output.mScene.EnsureUniqueBlendShapeShaderInstances(output.mResources);
434 // Ger cameras and lights
435 GetCameraParameters(output.mCameraParameters);
436 GetLightParameters(output.mLightParameters);
438 // Post-node processors and animations last
439 for (auto& c : input.mPostNodeCategoryProcessors)
441 if (auto node = docRoot->GetChild(c.first))
443 Property::Array array;
444 ParseProperties(*node, array);
445 c.second(std::move(array), mOnError);
449 if (auto animations = docRoot->GetChild("animations"))
451 ParseAnimations(animations, params);
454 if (!output.mAnimationDefinitions.empty())
456 if (auto animationGroups = docRoot->GetChild("animationGroups"))
458 ParseAnimationGroups(animationGroups, params);
463 void DliLoader::Impl::ParseSceneInternal(Index iScene, const Toolkit::TreeNode* tnScenes,
464 const Toolkit::TreeNode* tnNodes, LoadParams& params)
466 auto getSceneRootIdx = [tnScenes, tnNodes](Index iScene) {
467 auto tn = GetNthChild(tnScenes, iScene); // now a "scene" object
470 ExceptionFlinger(ASSERT_LOCATION) << iScene << " is out of bounds access into " << SCENES << ".";
473 tn = RequireChild(tn, NODES); // now a "nodes" array
474 if (tn->GetType() != TreeNode::ARRAY)
476 ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES <<
477 " has an invalid type; array required.";
482 ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES <<
483 " must define a node id.";
486 tn = GetNthChild(tn, 0); // now the first element of the array
488 if (!ReadIndex(tn, iRootNode))
490 ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES <<
491 " has an invalid value for root node index: '" << iRootNode << "'.";
494 if (iRootNode >= tnNodes->Size())
496 ExceptionFlinger(ASSERT_LOCATION) << "Root node index << " << iRootNode << " of scene " << iScene << " is out of bounds.";
499 tn = GetNthChild(tnNodes, iRootNode); // now a "node" object
500 if (tn->GetType() != TreeNode::OBJECT)
502 ExceptionFlinger(ASSERT_LOCATION) << "Root node of scene " << iScene << " is of invalid JSON type; object required";
508 Index iRootNode = getSceneRootIdx(iScene);
509 ParseNodes(tnNodes, iRootNode, params);
511 auto& scene = params.output.mScene;
512 scene.AddRootNode(0);
514 for (Index i = 0; i < iScene; ++i)
516 Index iRootNode = getSceneRootIdx(i);
517 const Index iRoot = scene.GetNodeCount();
518 ParseNodes(tnNodes, iRootNode, params);
519 scene.AddRootNode(iRoot);
522 auto numScenes = tnScenes->Size();
523 for (Index i = iScene + 1; i < numScenes; ++i)
525 Index iRootNode = getSceneRootIdx(i);
526 const Index iRoot = scene.GetNodeCount();
527 ParseNodes(tnNodes, iRootNode, params);
528 scene.AddRootNode(iRoot);
532 void DliLoader::Impl::ParseSkeletons(const TreeNode* skeletons, SceneDefinition& scene, ResourceBundle& resources)
536 auto iStart = skeletons->CBegin();
537 for (auto i0 = iStart, i1 = skeletons->CEnd(); i0 != i1; ++i0)
539 auto& node = (*i0).second;
540 std::string skeletonRootName;
541 if (ReadString(node.GetChild(NODE), skeletonRootName))
543 SkeletonDefinition skeleton;
544 if (!scene.FindNode(skeletonRootName, &skeleton.mRootNodeIdx))
546 ExceptionFlinger(ASSERT_LOCATION) << FormatString("Skeleton %d: node '%s' not defined.", resources.mSkeletons.size(), skeletonRootName.c_str());
549 uint32_t jointCount = 0;
550 std::function<void(Index)> visitFn;
551 auto& ibms = mInverseBindMatrices;
552 visitFn = [&](Index id) {
553 auto node = scene.GetNode(id);
554 jointCount += ibms.find(id) != ibms.end();
556 for (auto i : node->mChildren)
561 visitFn(skeleton.mRootNodeIdx);
563 if (jointCount > Skinning::MAX_JOINTS)
565 mOnError(FormatString("Skeleton %d: joint count exceeds supported limit.", resources.mSkeletons.size()));
566 jointCount = Skinning::MAX_JOINTS;
569 skeleton.mJoints.reserve(jointCount);
571 visitFn = [&](Index id) {
572 auto iFind = ibms.find(id);
573 if (iFind != ibms.end() && skeleton.mJoints.size() < Skinning::MAX_JOINTS)
575 skeleton.mJoints.push_back({ id, iFind->second });
578 auto node = scene.GetNode(id);
579 for (auto i : node->mChildren)
584 visitFn(skeleton.mRootNodeIdx);
586 resources.mSkeletons.push_back(std::move(skeleton));
590 ExceptionFlinger(ASSERT_LOCATION) << "skeleton " << std::distance(iStart, i0) <<
591 ": Missing required attribute '"<< NODE <<"'.";
597 void DliLoader::Impl::ParseEnvironments(const TreeNode* environments, ResourceBundle& resources)
599 Matrix cubeOrientation(Matrix::IDENTITY);
601 for (auto i0 = environments->CBegin(), i1 = environments->CEnd(); i0 != i1; ++i0)
603 auto& node = (*i0).second;
605 EnvironmentDefinition envDef;
606 ReadString(node.GetChild("cubeSpecular"), envDef.mSpecularMapPath);
607 ReadString(node.GetChild("cubeDiffuse"), envDef.mDiffuseMapPath);
608 ToUnixFileSeparators(envDef.mSpecularMapPath);
609 ToUnixFileSeparators(envDef.mDiffuseMapPath);
610 envDef.mIblIntensity = 1.0f;
611 ReadFloat(node.GetChild("iblIntensity"), envDef.mIblIntensity);
612 if (ReadVector(node.GetChild("cubeInitialOrientation"), cubeOrientation.AsFloat(), 16u))
614 envDef.mCubeOrientation = Quaternion(cubeOrientation);
617 resources.mEnvironmentMaps.emplace_back(std::move(envDef), EnvironmentDefinition::Textures());
620 // NOTE: guarantees environmentMaps to have an empty environment.
621 if (resources.mEnvironmentMaps.empty())
623 resources.mEnvironmentMaps.emplace_back(EnvironmentDefinition(), EnvironmentDefinition::Textures());
627 void DliLoader::Impl::ParseShaders(const TreeNode* shaders, ResourceBundle& resources)
629 uint32_t iShader = 0;
630 for (auto i0 = shaders->CBegin(), i1 = shaders->CEnd(); i0 != i1; ++i0, ++iShader)
632 auto& node = (*i0).second;
633 ShaderDefinition shaderDef;
634 ReadStringVector(node.GetChild("defines"), shaderDef.mDefines);
636 // Read shader hints. Possible values are:
637 // Don't define for No hints.
638 // "OUTPUT_IS_TRANSPARENT" Might generate transparent alpha from opaque inputs.
639 // "MODIFIES_GEOMETRY" Might change position of vertices, this option disables any culling optimizations.
641 ReadStringVector(node.GetChild( HINTS ), shaderDef.mHints);
643 if (ReadString(node.GetChild("vertex"), shaderDef.mVertexShaderPath) &&
644 ReadString(node.GetChild("fragment"), shaderDef.mFragmentShaderPath))
646 ToUnixFileSeparators(shaderDef.mVertexShaderPath);
647 ToUnixFileSeparators(shaderDef.mFragmentShaderPath);
649 for (TreeNode::ConstIterator j0 = node.CBegin(), j1 = node.CEnd(); j0 != j1; ++j0)
651 const TreeNode::KeyNodePair& keyValue = *j0;
652 const std::string& key = keyValue.first;
653 const TreeNode& value = keyValue.second;
655 Property::Value uniformValue;
656 if (key.compare("vertex") == 0 || key.compare("fragment") == 0 || key.compare("defines") == 0 || key.compare(HINTS) == 0)
660 else if (key.compare("rendererState") == 0)
662 shaderDef.mRendererState = ReadRendererState(keyValue.second);
664 else if (value.GetType() == TreeNode::INTEGER || value.GetType() == TreeNode::FLOAT)
667 ReadFloat(&value, f);
670 else if (value.GetType() == TreeNode::BOOLEAN)
672 DALI_LOG_WARNING("\"bool\" uniforms are handled as floats in shader");
674 if (ReadBool(&keyValue.second, value))
676 uniformValue = value ? 1.0f : 0.0f;
679 else switch (auto size = GetNumericalArraySize(&value))
684 ReadVector(&value, m.AsFloat(), size);
692 ReadVector(&value, m.AsFloat(), size);
700 ReadVector(&value, v.AsFloat(), size);
708 ReadVector(&value, v.AsFloat(), size);
716 ReadVector(&value, v.AsFloat(), size);
722 mOnError(FormatString(
723 "shader %d: Ignoring uniform '%s': failed to infer type from %d elements.",
724 iShader, key.c_str()));
728 if (Property::NONE != uniformValue.GetType())
730 shaderDef.mUniforms.Insert(key, uniformValue);
734 resources.mShaders.emplace_back(std::move(shaderDef), Shader());
738 ExceptionFlinger(ASSERT_LOCATION) << "shader " << iShader <<
739 ": Missing vertex / fragment shader definition.";
744 void DliLoader::Impl::ParseMeshes(const TreeNode* meshes, ResourceBundle& resources)
746 for (auto i0 = meshes->CBegin(), i1 = meshes->CEnd(); i0 != i1; ++i0)
748 auto& node = (*i0).second;
750 MeshDefinition meshDef;
751 if (!ReadString(node.GetChild(URI), meshDef.mUri))
753 ExceptionFlinger(ASSERT_LOCATION) << "mesh " << resources.mMeshes.size() <<
754 ": Missing required attribute '" << URI << "'.";
757 ToUnixFileSeparators(meshDef.mUri);
759 std::string primitive;
760 if (ReadString(node.GetChild("primitive"), primitive))
762 if (primitive == "LINES")
764 meshDef.mPrimitiveType = Geometry::LINES;
766 else if (primitive == "POINTS")
768 meshDef.mPrimitiveType = Geometry::POINTS;
770 else if (primitive != "TRIANGLES")
772 mOnError(FormatString(
773 "mesh %d: Using TRIANGLES instead of unsupported primitive type '%s'.",
774 resources.mMeshes.size(), primitive.c_str()));
779 if (ReadInt(node.GetChild("attributes"), attributes))
781 if (MaskMatch(attributes, MeshDefinition::INDICES) &&
782 !ReadAttribAccessor(node.GetChild("indices"), meshDef.mIndices))
784 ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
785 resources.mMeshes.size(), "indices");
788 if (MaskMatch(attributes, MeshDefinition::POSITIONS) &&
789 !ReadAttribAccessor(node.GetChild("positions"), meshDef.mPositions))
791 ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
792 resources.mMeshes.size(), "positions");
795 if (MaskMatch(attributes, MeshDefinition::NORMALS) &&
796 !ReadAttribAccessor(node.GetChild("normals"), meshDef.mNormals))
798 mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(),
802 if (MaskMatch(attributes, MeshDefinition::TEX_COORDS) &&
803 !ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords))
805 mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(),
809 if (MaskMatch(attributes, MeshDefinition::TANGENTS) &&
810 !ReadAttribAccessor(node.GetChild("tangents"), meshDef.mTangents))
812 mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(),
816 // NOTE: we're no longer reading bitangents as these are calculated in the shaders.
817 if (ReadIndex(node.GetChild("skeleton"), meshDef.mSkeletonIdx))
819 if (!MaskMatch(attributes, MeshDefinition::JOINTS_0) &&
820 !MaskMatch(attributes, MeshDefinition::WEIGHTS_0))
822 mOnError(FormatString("mesh %d: Expected joints0 / weights0 attribute(s) missing.",
823 resources.mMeshes.size()));
825 else if (!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints0) ||
826 !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights0))
828 mOnError(FormatString("mesh %d: Failed to read skinning information.",
829 resources.mMeshes.size()));
833 if (auto blendshapeHeader = node.GetChild(BLEND_SHAPE_HEADER))
835 std::string blendShapeVersion;
836 ReadString(blendshapeHeader->GetChild(VERSION), blendShapeVersion);
838 if (0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_1_0))
840 meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_1_0;
842 else if (0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_2_0))
844 meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
847 switch (meshDef.mBlendShapeVersion)
849 case BlendShapes::Version::VERSION_1_0:
850 case BlendShapes::Version::VERSION_2_0: // FALL THROUGH
852 ReadAttribBlob(blendshapeHeader, meshDef.mBlendShapeHeader);
863 if (auto blendShapes = node.GetChild(BLEND_SHAPES) )
865 meshDef.mBlendShapes.resize(blendShapes->Size());
868 for (auto it = blendShapes->CBegin(), endIt = blendShapes->CEnd(); it != endIt; ++it, ++index)
870 // Each blend shape is stored as the difference with the original mesh.
872 auto& blendShapeNode = (*it).second;
874 auto& blendShape = meshDef.mBlendShapes[index];
875 ReadString(blendShapeNode.GetChild("name"), blendShape.name);
876 if (auto position = blendShapeNode.GetChild("positions"))
878 ReadAttribAccessor(position, blendShape.deltas);
880 if (auto normals = blendShapeNode.GetChild("normals"))
882 ReadAttribAccessor(normals, blendShape.normals);
884 if (auto tangents = blendShapeNode.GetChild("tangents"))
886 ReadAttribAccessor(tangents, blendShape.tangents);
888 ReadFloat(blendShapeNode.GetChild("weight"), blendShape.weight);
893 if (ReadBool(node.GetChild("flipV"), flipV))
895 meshDef.mFlags |= flipV * MeshDefinition::FLIP_UVS_VERTICAL;
898 resources.mMeshes.emplace_back(std::move(meshDef), MeshGeometry());
903 void DliLoader::Impl::ParseMaterials(const TreeNode* materials, ConvertColorCode convertColorCode,
904 ResourceBundle& resources)
906 for (auto i0 = materials->CBegin(), i1 = materials->CEnd(); i0 != i1; ++i0)
908 auto& node = (*i0).second;
910 MaterialDefinition materialDef;
911 if (auto eEnvironment = node.GetChild("environment"))
913 ReadIndex(eEnvironment, materialDef.mEnvironmentIdx);
914 if (static_cast<unsigned int>(materialDef.mEnvironmentIdx) >= resources.mEnvironmentMaps.size())
916 ExceptionFlinger(ASSERT_LOCATION) << "material " << resources.mMaterials.size() <<
917 ": Environment index " << materialDef.mEnvironmentIdx << " out of bounds (" <<
918 resources.mEnvironmentMaps.size() << ").";
922 //TODO : need to consider AGIF
923 std::vector<std::string> texturePaths;
924 std::string texturePath;
925 if (ReadString(node.GetChild("albedoMap"), texturePath))
927 ToUnixFileSeparators(texturePath);
928 const auto semantic = MaterialDefinition::ALBEDO;
929 materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
930 materialDef.mFlags |= semantic | MaterialDefinition::TRANSPARENCY; // NOTE: only in dli does single / separate ALBEDO texture mean TRANSPARENCY.
932 if (ReadString(node.GetChild("albedoMetallicMap"), texturePath))
934 ToUnixFileSeparators(texturePath);
936 if (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
938 mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "albedo"));
941 const auto semantic = MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC;
942 materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
943 materialDef.mFlags |= semantic;
946 if (ReadString(node.GetChild("metallicRoughnessMap"), texturePath))
948 ToUnixFileSeparators(texturePath);
950 if (MaskMatch(materialDef.mFlags, MaterialDefinition::METALLIC))
952 mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "metallic"));
955 const auto semantic = MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS;
956 materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
957 materialDef.mFlags |= semantic |
958 // We have a metallic-roughhness map and the first texture did not have albedo semantics - we're in the transparency workflow.
959 (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
962 if (ReadString(node.GetChild("normalMap"), texturePath))
964 ToUnixFileSeparators(texturePath);
966 const auto semantic = MaterialDefinition::NORMAL;
967 materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
968 materialDef.mFlags |= semantic |
969 // We have a standalone normal map and the first texture did not have albedo semantics - we're in the transparency workflow.
970 (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
973 if (ReadString(node.GetChild("normalRoughnessMap"), texturePath))
975 ToUnixFileSeparators(texturePath);
977 if (MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
979 mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "normal"));
982 if (MaskMatch(materialDef.mFlags, MaterialDefinition::ROUGHNESS))
984 mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "roughness"));
987 if (MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY))
989 mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "transparency"));
992 const auto semantic = MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS;
993 materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
994 materialDef.mFlags |= semantic;
997 if (ReadString(node.GetChild("subsurfaceMap"), texturePath))
999 ToUnixFileSeparators(texturePath);
1001 const auto semantic = MaterialDefinition::SUBSURFACE;
1002 materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
1003 materialDef.mFlags |= semantic;
1006 if (ReadColorCodeOrColor(&node, materialDef.mColor, convertColorCode) &&
1007 materialDef.mColor.a < 1.0f)
1009 materialDef.mFlags |= MaterialDefinition::TRANSPARENCY;
1012 ReadFloat(node.GetChild("metallic"), materialDef.mMetallic);
1013 ReadFloat(node.GetChild("roughness"), materialDef.mRoughness);
1016 if (ReadBool(node.GetChild("mipmap"), mipmaps) && mipmaps)
1018 for (auto& ts : materialDef.mTextureStages)
1020 ts.mTexture.mSamplerFlags |= SamplerFlags::FILTER_MIPMAP_LINEAR;
1024 resources.mMaterials.emplace_back(std::move(materialDef), TextureSet());
1028 void DliLoader::Impl::ParseNodes(const TreeNode* const nodes, Index index, LoadParams& params)
1030 std::vector<Index> parents;
1033 struct IndexMapper : IIndexMapper
1035 IndexMapper(size_t numNodes)
1037 mIndices.reserve(numNodes);
1040 virtual bool Map(Index iDli, Index iScene) override
1042 Entry idx{ iDli, iScene };
1043 auto iInsert = std::lower_bound(mIndices.begin(), mIndices.end(), idx);
1044 if (iInsert == mIndices.end() || iInsert->iDli != iDli)
1046 mIndices.insert(iInsert, idx);
1048 else if (iInsert->iScene != iScene)
1055 virtual unsigned int Resolve(Index iDli) override
1057 auto iFind = std::lower_bound(mIndices.begin(), mIndices.end(), iDli,
1058 [](const Entry& idx, Index iDli) {
1059 return idx.iDli < iDli;
1061 DALI_ASSERT_ALWAYS(iFind != mIndices.end());
1062 return iFind->iScene;
1069 unsigned int iScene;
1071 bool operator<(const Entry& other) const
1073 return iDli < other.iDli;
1076 std::vector<Entry> mIndices;
1077 } mapper(nodes->Size());
1078 ParseNodesInternal(nodes, index, parents, params, mapper);
1080 auto& scene = params.output.mScene;
1081 for (size_t i0 = 0, i1 = scene.GetNodeCount(); i0 < i1; ++i0)
1083 for (auto& c : scene.GetNode(i0)->mConstraints)
1085 c.mSourceIdx = mapper.Resolve(c.mSourceIdx);
1090 void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index index,
1091 std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& mapper)
1093 // Properties that may be resolved from a JSON value with ReadInt() -- or default to 0.
1094 struct IndexProperty { ResourceType::Value type; const TreeNode* source; Index& target; };
1095 std::vector<IndexProperty> resourceIds;
1096 resourceIds.reserve(4);
1098 if (auto node = GetNthChild(nodes, index))
1100 NodeDefinition nodeDef;
1101 nodeDef.mParentIdx = inOutParentStack.empty() ? INVALID_INDEX : inOutParentStack.back();
1104 ReadString(node->GetChild(NAME), nodeDef.mName);
1107 ReadModelTransform(node, nodeDef.mOrientation, nodeDef.mPosition, nodeDef.mScale);
1109 // Reads the size of the node.
1111 // * It can be given as 'size' or 'bounds'.
1112 // * The sdk saves the 'size' as a vector2 in some cases.
1113 // * To avoid size related issues the following code attemps
1114 // to read the 'size/bounds' as a vector3 first, if it's
1115 // not successful then reads it as a vector2.
1116 ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 3) ||
1117 ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 2) ||
1118 ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 3) ||
1119 ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 2);
1122 ReadBool(node->GetChild("visible"), nodeDef.mIsVisible);
1124 // type classification
1125 if (auto eCustomization = node->GetChild("customization")) // customization
1128 if (ReadString(eCustomization->GetChild("tag"), tag))
1130 nodeDef.mCustomization.reset(new NodeDefinition::CustomizationDefinition{ tag });
1133 else // something renderable maybe
1135 std::unique_ptr<NodeDefinition::Renderable> renderable;
1136 ModelNode* modelNode = nullptr; // no ownership, aliasing renderable for the right type.
1138 const TreeNode* eRenderable = nullptr;
1139 if ((eRenderable = node->GetChild("model")))
1141 // check for mesh before allocating - this can't be missing.
1142 auto eMesh = eRenderable->GetChild("mesh");
1145 ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1148 modelNode = new ModelNode();
1149 renderable.reset(modelNode);
1151 resourceIds.push_back({ ResourceType::Mesh, eMesh, modelNode->mMeshIdx });
1153 else if ((eRenderable = node->GetChild("arc")))
1155 // check for mesh before allocating - this can't be missing.
1156 auto eMesh = eRenderable->GetChild("mesh");
1159 ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1162 auto arcNode = new ArcNode;
1163 renderable.reset(arcNode);
1164 modelNode = arcNode;
1166 resourceIds.push_back({ ResourceType::Mesh, eMesh, arcNode->mMeshIdx });
1168 ReadArcField(eRenderable, *arcNode);
1171 if (renderable) // process common properties of all renderables + register payload
1174 renderable->mShaderIdx = 0;
1175 auto eShader = eRenderable->GetChild("shader");
1176 resourceIds.push_back({ ResourceType::Shader, eShader, renderable->mShaderIdx });
1181 modelNode->mMaterialIdx = 0; // must offer default of 0
1182 auto eMaterial = eRenderable->GetChild("material");
1183 resourceIds.push_back({ ResourceType::Material, eMaterial, modelNode->mMaterialIdx });
1185 if (!ReadColorCodeOrColor(eRenderable, modelNode->mColor, params.input.mConvertColorCode))
1187 ReadColorCodeOrColor(node, modelNode->mColor, params.input.mConvertColorCode);
1191 nodeDef.mRenderable = std::move(renderable);
1195 // Resolve ints - default to 0 if undefined
1196 auto& output = params.output;
1197 for (auto& idRes : resourceIds)
1202 case ResourceType::Shader:
1203 iCheck = output.mResources.mShaders.size();
1206 case ResourceType::Mesh:
1207 iCheck = output.mResources.mMeshes.size();
1210 case ResourceType::Material:
1211 iCheck = output.mResources.mMaterials.size();
1215 ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid resource type: " <<
1216 idRes.type << " (Programmer error)";
1223 else if (idRes.source->GetType() != TreeNode::INTEGER)
1225 ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid " <<
1226 GetResourceTypeName(idRes.type) << " index type.";
1230 idRes.target = idRes.source->GetInteger();
1233 if (idRes.target >= iCheck)
1235 ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": " <<
1236 GetResourceTypeName(idRes.type) << " index " << idRes.target << " out of bounds (" <<
1240 resourceIds.clear();
1243 if (auto eExtras = node->GetChild("extras"))
1245 auto& extras = nodeDef.mExtras;
1246 extras.reserve(eExtras->Size());
1248 NodeDefinition::Extra e;
1249 for (auto i0 = eExtras->CBegin(), i1 = eExtras->CEnd(); i0 != i1; ++i0)
1252 e.mKey = eExtra.first;
1255 mOnError(FormatString("node %d: empty string is invalid for name of extra %d; ignored.",
1256 index, extras.size()));
1260 e.mValue = ReadPropertyValue(eExtra.second);
1261 if (e.mValue.GetType() == Property::Type::NONE)
1263 mOnError(FormatString("node %d: failed to interpret value of extra '%s' : %s; ignored.",
1264 index, e.mKey.c_str(), eExtra.second.GetString()));
1268 auto iInsert = std::lower_bound(extras.begin(), extras.end(), e);
1269 if (iInsert != extras.end() && iInsert->mKey == e.mKey)
1271 mOnError(FormatString("node %d: extra '%s' already defined; overriding with %s.",
1272 index, e.mKey.c_str(), eExtra.second.GetString()));
1273 *iInsert = std::move(e);
1277 extras.insert(iInsert, e);
1284 if (auto eConstraints = node->GetChild("constraints"))
1286 auto& constraints = nodeDef.mConstraints;
1287 constraints.reserve(eConstraints->Size());
1289 ConstraintDefinition cDef;
1290 for (auto i0 = eConstraints->CBegin(), i1 = eConstraints->CEnd(); i0 != i1; ++i0)
1292 auto eConstraint = *i0;
1293 if (!ReadIndex(&eConstraint.second, cDef.mSourceIdx))
1295 mOnError(FormatString("node %d: node ID %s for constraint %d is invalid; ignored.",
1296 index, eConstraint.second.GetString(), constraints.size()));
1300 cDef.mProperty = eConstraint.first;
1302 auto iInsert = std::lower_bound(constraints.begin(), constraints.end(), cDef);
1303 if (iInsert != constraints.end() && *iInsert == cDef)
1305 mOnError(FormatString("node %d: constraint %s@%d already defined; ignoring.",
1306 index, cDef.mProperty.c_str(), cDef.mSourceIdx));
1310 constraints.insert(iInsert, cDef);
1316 // Determine index for mapping
1317 const unsigned int myIndex = output.mScene.GetNodeCount();
1318 if (!mapper.Map(index, myIndex))
1320 mOnError(FormatString("node %d: error mapping dli index %d: node has multiple parents. Ignoring subtree."));
1324 // if the node is a bone in a skeletal animation, it will have the inverse bind pose matrix.
1325 Matrix invBindMatrix{ false };
1326 if (ReadVector(node->GetChild("inverseBindPoseMatrix"), invBindMatrix.AsFloat(), 16u)) // TODO: more robust error checking?
1328 mInverseBindMatrices[myIndex] = invBindMatrix;
1332 auto rawDef = output.mScene.AddNode(std::make_unique<NodeDefinition>(std::move(nodeDef)));
1333 if (rawDef) // NOTE: no ownership. Guaranteed to stay in scope.
1335 // ...And only then parse children.
1336 if (auto children = node->GetChild("children"))
1338 inOutParentStack.push_back(myIndex);
1340 rawDef->mChildren.reserve(children->Size());
1342 uint32_t iChild = 0;
1343 for (auto j0 = children->CBegin(), j1 = children->CEnd(); j0 != j1; ++j0, ++iChild)
1345 auto& child = (*j0).second;
1346 if (child.GetType() == TreeNode::INTEGER)
1348 ParseNodesInternal(nodes, child.GetInteger(), inOutParentStack, params, mapper); // child object is created in scene definition.
1352 ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ", child " << iChild <<
1353 ": invalid index type.";
1357 inOutParentStack.pop_back();
1359 else if (rawDef->mCustomization)
1361 mOnError(FormatString("node %d: not an actual customization without children.", index));
1364 if (auto proc = params.input.mNodePropertyProcessor) // optional processing
1366 // WARNING: constraint IDs are not resolved at this point.
1367 Property::Map nodeData;
1368 ParseProperties(*node, nodeData);
1369 proc(*rawDef, std::move(nodeData), mOnError);
1374 ExceptionFlinger(ASSERT_LOCATION) << "Node " << index << ": name already used.";
1379 void DliLoader::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams& params)
1381 auto& definitions = params.output.mAnimationDefinitions;
1382 definitions.reserve(definitions.size() + tnAnimations->Size());
1384 for (TreeNode::ConstIterator iAnim = tnAnimations->CBegin(), iAnimEnd = tnAnimations->CEnd();
1385 iAnim != iAnimEnd; ++iAnim)
1387 const TreeNode& tnAnim = (*iAnim).second;
1388 AnimationDefinition animDef;
1389 ReadString(tnAnim.GetChild(NAME), animDef.mName);
1391 auto iFind = std::lower_bound(definitions.begin(), definitions.end(), animDef,
1392 [](const AnimationDefinition& ad0, const AnimationDefinition& ad1) {
1393 return ad0.mName < ad1.mName;
1395 const bool overwrite = iFind != definitions.end() && iFind->mName == animDef.mName;
1398 mOnError(FormatString("Pre-existing animation with name '%s' is being overwritten.", animDef.mName.c_str()));
1401 // Duration -- We need something that animated properties' delay / duration can
1402 // be expressed as a multiple of; 0 won't work. This is small enough (i.e. shorter
1403 // than our frame delay) to not be restrictive WRT replaying. If anything needs
1404 // to occur more frequently, then Animations are likely not your solution anyway.
1405 animDef.mDuration = AnimationDefinition::MIN_DURATION_SECONDS;
1406 if (!ReadFloat(tnAnim.GetChild("duration"), animDef.mDuration))
1408 mOnError(FormatString("Animation '%s' fails to define '%s', defaulting to %f.",
1409 animDef.mName.c_str(), "duration", animDef.mDuration));
1412 // Get loop count - # of playbacks. Default is once. 0 means repeat indefinitely.
1413 animDef.mLoopCount = 1;
1414 if (ReadInt(tnAnim.GetChild("loopCount"), animDef.mLoopCount) &&
1415 animDef.mLoopCount < 0)
1417 animDef.mLoopCount = 0;
1420 std::string endAction;
1421 if (ReadString(tnAnim.GetChild("endAction"), endAction))
1423 if ("BAKE" == endAction)
1425 animDef.mEndAction = Animation::BAKE;
1427 else if ("DISCARD" == endAction)
1429 animDef.mEndAction = Animation::DISCARD;
1431 else if ("BAKE_FINAL" == endAction)
1433 animDef.mEndAction = Animation::BAKE_FINAL;
1437 if (ReadString(tnAnim.GetChild("disconnectAction"), endAction))
1439 if ("BAKE" == endAction)
1441 animDef.mDisconnectAction = Animation::BAKE;
1443 else if ("DISCARD" == endAction)
1445 animDef.mDisconnectAction = Animation::DISCARD;
1447 else if ("BAKE_FINAL" == endAction)
1449 animDef.mDisconnectAction = Animation::BAKE_FINAL;
1453 if (const TreeNode* tnProperties = tnAnim.GetChild("properties"))
1455 animDef.mProperties.reserve(tnProperties->Size());
1456 for (TreeNode::ConstIterator iProperty = tnProperties->CBegin(), iPropertyEnd = tnProperties->CEnd();
1457 iProperty != iPropertyEnd; ++iProperty)
1459 const TreeNode &tnProperty = (*iProperty).second;
1461 AnimatedProperty animProp;
1462 if (!ReadString(tnProperty.GetChild("node"), animProp.mNodeName))
1464 mOnError(FormatString("Animation '%s': Failed to read the 'node' tag.", animDef.mName.c_str()));
1468 if (!ReadString(tnProperty.GetChild("property"), animProp.mPropertyName))
1470 mOnError(FormatString("Animation '%s': Failed to read the 'property' tag", animDef.mName.c_str()));
1474 // these are the defaults
1475 animProp.mTimePeriod.delaySeconds = 0.f;
1476 animProp.mTimePeriod.durationSeconds = animDef.mDuration;
1477 if (!ReadTimePeriod(tnProperty.GetChild("timePeriod"), animProp.mTimePeriod))
1479 mOnError(FormatString("Animation '%s': timePeriod missing in Property #%d: defaulting to %f.",
1480 animDef.mName.c_str(), animDef.mProperties.size(), animProp.mTimePeriod.durationSeconds));
1483 std::string alphaFunctionValue;
1484 if (ReadString(tnProperty.GetChild("alphaFunction"), alphaFunctionValue))
1486 animProp.mAlphaFunction = GetAlphaFunction(alphaFunctionValue);
1489 if (const TreeNode* tnKeyFramesBin = tnProperty.GetChild("keyFramesBin"))
1491 DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1493 std::ifstream binAniFile;
1494 std::string animationFilename;
1495 if (ReadString(tnKeyFramesBin->GetChild(URL), animationFilename))
1497 std::string animationFullPath = params.input.mAnimationsPath + animationFilename;
1498 binAniFile.open(animationFullPath, std::ios::binary);
1499 if (binAniFile.fail())
1501 ExceptionFlinger(ASSERT_LOCATION) << "Failed to open animation data '" <<
1502 animationFullPath << "'";
1507 ReadInt(tnKeyFramesBin->GetChild("byteOffset"), byteOffset);
1508 DALI_ASSERT_ALWAYS(byteOffset >= 0);
1510 binAniFile.seekg(byteOffset, std::ios::beg);
1513 ReadInt(tnKeyFramesBin->GetChild("numKeys"), numKeys);
1514 DALI_ASSERT_ALWAYS(numKeys >= 0);
1516 animProp.mKeyFrames = KeyFrames::New();
1518 //In binary animation file only is saved the position, rotation, scale and blend shape weight keys.
1519 //so, if it is vector3 we assume is position or scale keys, if it is vector4 we assume is rotation,
1520 // otherwise are blend shape weight keys.
1521 // TODO support for binary header with size information
1522 Property::Type propType = Property::FLOAT; // assume blend shape weights
1523 if (animProp.mPropertyName == "orientation")
1525 propType = Property::VECTOR4;
1527 else if ((animProp.mPropertyName == "position") || (animProp.mPropertyName == "scale"))
1529 propType = Property::VECTOR3;
1532 //alphafunction is reserved for future implementation
1533 // NOTE: right now we're just using AlphaFunction::LINEAR.
1534 unsigned char dummyAlphaFunction;
1537 Property::Value propValue;
1538 for (int key = 0; key < numKeys; key++)
1540 binAniFile.read(reinterpret_cast<char*>(&progress), sizeof(float));
1541 if (propType == Property::VECTOR3)
1544 binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 3);
1545 propValue = Property::Value(value);
1547 else if (propType == Property::VECTOR4)
1550 binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 4);
1551 propValue = Property::Value(Quaternion(value));
1556 binAniFile.read(reinterpret_cast<char*>(&value), sizeof(float));
1557 propValue = Property::Value(value);
1560 binAniFile.read(reinterpret_cast<char*>(&dummyAlphaFunction), sizeof(unsigned char));
1562 animProp.mKeyFrames.Add(progress, propValue, AlphaFunction::LINEAR);
1565 else if (const TreeNode* tnKeyFrames = tnProperty.GetChild("keyFrames"))
1567 DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1568 animProp.mKeyFrames = KeyFrames::New();
1570 float progress = 0.0f;
1571 for (auto i0 = tnKeyFrames->CBegin(), i1 = tnKeyFrames->CEnd(); i1 != i0; ++i0)
1573 const TreeNode::KeyNodePair& kfKeyChild = *i0;
1574 bool readResult = ReadFloat(kfKeyChild.second.GetChild("progress"), progress);
1575 DALI_ASSERT_ALWAYS(readResult && "Key frame entry must have 'progress'");
1577 const TreeNode* tnValue = kfKeyChild.second.GetChild("value");
1578 DALI_ASSERT_ALWAYS(tnValue && "Key frame entry must have 'value'");
1580 // For the "orientation" property, convert from Vector4 -> Rotation value
1581 // This work-around is preferable to a null-pointer exception in the DALi update thread
1582 Property::Value propValue(ReadPropertyValue(*tnValue));
1583 if (propValue.GetType() == Property::VECTOR4 &&
1584 animProp.mPropertyName == "orientation")
1588 propValue = Property::Value(Quaternion(v.w, v.x, v.y, v.z));
1591 AlphaFunction kfAlphaFunction(AlphaFunction::DEFAULT);
1592 std::string alphaFuncStr;
1593 if (ReadString(kfKeyChild.second.GetChild("alphaFunction"), alphaFuncStr))
1595 kfAlphaFunction = GetAlphaFunction(alphaFuncStr);
1598 animProp.mKeyFrames.Add(progress, propValue, kfAlphaFunction);
1603 const TreeNode* tnValue = tnProperty.GetChild("value");
1606 animProp.mValue.reset(new AnimatedProperty::Value{ ReadPropertyValue(*tnValue) });
1610 mOnError(FormatString("Property '%s' fails to define target value.",
1611 animProp.mPropertyName.c_str()));
1614 ReadBool(tnProperty.GetChild("relative"), animProp.mValue->mIsRelative);
1617 animDef.mProperties.push_back(std::move(animProp));
1623 *iFind = std::move(animDef);
1627 iFind = definitions.insert(iFind, std::move(animDef));
1630 if (auto proc = params.input.mAnimationPropertyProcessor) // optional processing
1633 ParseProperties(tnAnim, map);
1634 proc(animDef, std::move(map), mOnError);
1639 void DliLoader::Impl::ParseAnimationGroups(const Toolkit::TreeNode* tnAnimationGroups, LoadParams& params)
1641 auto& animGroups = params.output.mAnimationGroupDefinitions;
1644 for (auto iGroups = tnAnimationGroups->CBegin(), iGroupsEnd = tnAnimationGroups->CEnd();
1645 iGroups != iGroupsEnd; ++iGroups, ++numGroups)
1647 const auto& tnGroup = *iGroups;
1648 auto tnName = tnGroup.second.GetChild(NAME);
1649 std::string groupName;
1650 if (!tnName || !ReadString(tnName, groupName))
1652 mOnError(FormatString("Failed to get the name for the Animation group %d; ignoring.", numGroups));
1656 auto iFind = std::lower_bound(animGroups.begin(), animGroups.end(), groupName,
1657 [](const AnimationGroupDefinition& group, const std::string& name) {
1658 return group.mName < name;
1660 if (iFind != animGroups.end() && iFind->mName == groupName)
1662 mOnError(FormatString("Animation group with name '%s' already exists; new entries will be merged.", groupName.c_str()));
1666 iFind = animGroups.insert(iFind, AnimationGroupDefinition{});
1669 iFind->mName = groupName;
1671 auto tnAnims = tnGroup.second.GetChild("animations");
1672 if (tnAnims && tnAnims->Size() > 0)
1674 auto& anims = iFind->mAnimations;
1675 anims.reserve(anims.size() + tnAnims->Size());
1676 for (auto iAnims = tnAnims->CBegin(), iAnimsEnd = tnAnims->CEnd(); iAnims != iAnimsEnd; ++iAnims)
1678 anims.push_back((*iAnims).second.GetString());
1684 void DliLoader::Impl::GetCameraParameters(std::vector<CameraParameters>& cameras) const
1686 if (const TreeNode* jsonCameras = mParser.GetRoot()->GetChild("cameras"))
1688 cameras.resize(jsonCameras->Size());
1689 auto iCamera = cameras.begin();
1690 for (auto i0 = jsonCameras->CBegin(), i1 = jsonCameras->CEnd(); i0 != i1; ++i0)
1692 auto& jsonCamera = (*i0).second;
1694 ReadFloat(jsonCamera.GetChild("fov"), iCamera->yFov);
1695 ReadFloat(jsonCamera.GetChild("near"), iCamera->zNear);
1696 ReadFloat(jsonCamera.GetChild("far"), iCamera->zFar);
1697 if (ReadVector(jsonCamera.GetChild("orthographic"), iCamera->orthographicSize.AsFloat(), 4u))
1699 iCamera->isPerspective = false;
1702 if (auto jsonMatrix = jsonCamera.GetChild("matrix"))
1704 ReadVector(jsonMatrix, iCamera->matrix.AsFloat(), 16u);
1712 void DliLoader::Impl::GetLightParameters(std::vector<LightParameters>& lights) const
1714 if (const TreeNode* jsonLights = mParser.GetRoot()->GetChild("lights"))
1716 lights.resize(jsonLights->Size());
1717 auto iLight = lights.begin();
1718 for (auto i0 = jsonLights->CBegin(), i1 = jsonLights->CEnd(); i0 != i1; ++i0)
1720 auto& jsonLight = (*i0).second;
1721 if (!ReadVector(jsonLight.GetChild("matrix"), iLight->transform.AsFloat(), 16))
1724 FormatString("Failed to parse light %d - \"matrix\" child with 16 floats expected.\n",
1725 std::distance(jsonLights->CBegin(), i0)));
1729 int shadowMapSize = 0;
1730 if (ReadInt(jsonLight.GetChild(SHADOW_MAP_SIZE), shadowMapSize) && shadowMapSize < 0)
1733 FormatString("Failed to parse light %d - %s has an invalid value.",
1734 std::distance(jsonLights->CBegin(), i0), SHADOW_MAP_SIZE));
1737 iLight->shadowMapSize = shadowMapSize;
1739 float orthoSize = 0.f;
1740 if (ReadFloat(jsonLight.GetChild(ORTHOGRAPHIC_SIZE), orthoSize) &&
1741 (orthoSize < .0f || std::isnan(orthoSize) || std::isinf(orthoSize)))
1744 FormatString("Failed to parse light %d - %s has an invalid value.",
1745 std::distance(jsonLights->CBegin(), i0), ORTHOGRAPHIC_SIZE));
1748 iLight->orthographicSize = orthoSize;
1750 if ((iLight->shadowMapSize > 0) != (iLight->orthographicSize > .0f))
1752 mOnError(FormatString(
1753 "Light %d: Both shadow map size and orthographic size must be set for shadows to work.",
1754 std::distance(jsonLights->CBegin(), i0)));
1757 if (!ReadVector(jsonLight.GetChild("color"), iLight->color.AsFloat(), 3)) // color is optional
1759 iLight->color = Vector3::ONE; // default to white
1762 if (!ReadFloat(jsonLight.GetChild("intensity"), iLight->intensity)) // intensity is optional
1764 iLight->intensity = 1.0f; // default to 1.0
1767 if (!ReadFloat(jsonLight.GetChild("shadowIntensity"), iLight->shadowIntensity)) // intensity is optional
1769 iLight->shadowIntensity = 1.0f; // default to 1.0