Use Renderer::BlendMode::USE_ACTOR_OPACITY instead to use depth write mode
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / dli-loader.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "dali-scene3d/public-api/loader/dli-loader.h"
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23 #include <cmath>
24 #include <fstream>
25 #include <limits>
26 #include <memory>
27 #include "dali-toolkit/devel-api/builder/json-parser.h"
28 #include "dali/devel-api/common/map-wrapper.h"
29 #include "dali/integration-api/debug.h"
30 #include "dali/public-api/object/property-array.h"
31
32 // INTERNAL INCLUDES
33 #include "dali-scene3d/internal/loader/json-util.h"
34 #include "dali-scene3d/public-api/loader/alpha-function-helper.h"
35 #include "dali-scene3d/public-api/loader/animation-definition.h"
36 #include "dali-scene3d/public-api/loader/blend-shape-details.h"
37 #include "dali-scene3d/public-api/loader/camera-parameters.h"
38 #include "dali-scene3d/public-api/loader/ktx-loader.h"
39 #include "dali-scene3d/public-api/loader/light-parameters.h"
40 #include "dali-scene3d/public-api/loader/load-result.h"
41 #include "dali-scene3d/public-api/loader/parse-renderer-state.h"
42 #include "dali-scene3d/public-api/loader/scene-definition.h"
43 #include "dali-scene3d/public-api/loader/skinning-details.h"
44 #include "dali-scene3d/public-api/loader/utils.h"
45
46 #define DLI_0_1_COMPATIBILITY
47
48 namespace Dali
49 {
50 using namespace Toolkit;
51
52 namespace Scene3D
53 {
54 namespace Loader
55 {
56 namespace rs = RendererState;
57
58 namespace
59 {
60 const std::string NODES         = "nodes";
61 const std::string SCENES        = "scenes";
62 const std::string NODE          = "node";
63 const std::string URI           = "uri";
64 const std::string URL           = "url";
65 const std::string CUSTOMIZATION = "customization";
66 const std::string HINTS         = "hints";
67 const std::string NAME("name");
68 const std::string BLEND_SHAPE_HEADER("blendShapeHeader");
69 const std::string BLEND_SHAPES("blendShapes");
70 const std::string BLEND_SHAPE_VERSION_1_0("1.0");
71 const std::string BLEND_SHAPE_VERSION_2_0("2.0");
72 const std::string VERSION("version");
73
74 const char* const SHADOW_MAP_SIZE   = "shadowMapSize";
75 const char* const ORTHOGRAPHIC_SIZE = "orthographicSize";
76 const char* const PIXEL_UNITS       = "px";
77
78 const char SLASH = '/';
79
80 void ReadModelTransform(const TreeNode* node, Quaternion& orientation, Vector3& translation, Vector3& scale)
81 {
82   float num[16u] = {.0f};
83
84   if(ReadVector(node->GetChild("matrix"), num, 16u))
85   {
86     Matrix mat(num);
87     mat.GetTransformComponents(translation, orientation, scale);
88   }
89   else
90   {
91     if(ReadVector(node->GetChild("angle"), num, 3u))
92     {
93       orientation = Quaternion(Radian(Degree(num[0u])), Radian(Degree(num[1u])), Radian(Degree(num[2u])));
94     }
95
96     if(ReadVector(node->GetChild("position"), num, 3u))
97     {
98       translation = Vector3(num);
99     }
100   }
101 }
102
103 bool ReadAttribBlob(const TreeNode* node, MeshDefinition::Blob& buffer)
104 {
105   return ReadBlob(node, buffer.mOffset, buffer.mLength);
106 }
107
108 bool ReadAttribAccessor(const TreeNode* node, MeshDefinition::Accessor& accessor)
109 {
110   return ReadBlob(node, accessor.mBlob.mOffset, accessor.mBlob.mLength);
111 }
112
113 bool ReadColorCode(const TreeNode* node, Vector4& color, DliLoader::ConvertColorCode convertColorCode)
114 {
115   if(!node || !convertColorCode)
116   {
117     return false;
118   }
119
120   color = convertColorCode(node->GetString());
121
122   return true;
123 }
124
125 bool ReadColorCodeOrColor(const TreeNode* node, Vector4& color, DliLoader::ConvertColorCode convertColorCode)
126 {
127   return ReadColorCode(node->GetChild("colorCode"), color, convertColorCode) ||
128          ReadColor(node->GetChild("color"), color);
129 }
130
131 RendererState::Type ReadRendererState(const TreeNode& tnRendererState)
132 {
133   if(tnRendererState.GetType() == TreeNode::INTEGER)
134   {
135     return static_cast<RendererState::Type>(tnRendererState.GetInteger());
136   }
137   else if(tnRendererState.GetType() == TreeNode::STRING)
138   {
139     return RendererState::Parse(tnRendererState.GetString());
140   }
141   else
142   {
143     return -1;
144   }
145 }
146
147 ///@brief Reads arc properties.
148 void ReadArcField(const TreeNode* eArc, ArcRenderable& arc)
149 {
150   ReadBool(eArc->GetChild("antiAliasing"), arc.mAntiAliasing);
151   ReadInt(eArc->GetChild("arcCaps"), arc.mArcCaps);
152   ReadFloat(eArc->GetChild("radius"), arc.mRadius);
153
154   arc.mStartAngleDegrees = .0f;
155   ReadFloat(eArc->GetChild("startAngle"), arc.mStartAngleDegrees);
156
157   arc.mEndAngleDegrees = .0f;
158   ReadFloat(eArc->GetChild("endAngle"), arc.mEndAngleDegrees);
159 }
160
161 const TreeNode* GetNthChild(const TreeNode* node, uint32_t index)
162 {
163   uint32_t i = 0;
164   for(TreeNode::ConstIterator it = (*node).CBegin(); it != (*node).CEnd(); ++it, ++i)
165   {
166     if(i == index)
167     {
168       return &((*it).second);
169     }
170   }
171   return NULL;
172 }
173
174 const TreeNode* RequireChild(const TreeNode* node, const std::string& childName)
175 {
176   auto child = node->GetChild(childName);
177   if(!child)
178   {
179     ExceptionFlinger flinger(ASSERT_LOCATION);
180     flinger << "Failed to find child node '" << childName << "'";
181     if(auto nodeName = node->GetName())
182     {
183       flinger << " on '" << nodeName << "'";
184     }
185     flinger << ".";
186   }
187   return child;
188 }
189
190 void ParseProperties(const Toolkit::TreeNode& node, Property::Array& array);
191
192 void ParseProperties(const Toolkit::TreeNode& node, Property::Map& map)
193 {
194   DALI_ASSERT_DEBUG(node.GetType() == TreeNode::OBJECT);
195   for(auto i0 = node.CBegin(), i1 = node.CEnd(); i0 != i1; ++i0)
196   {
197     auto kv = *i0;
198     switch(kv.second.GetType())
199     {
200       case TreeNode::ARRAY:
201       {
202         Property::Array array;
203         ParseProperties(kv.second, array);
204         map.Insert(kv.first, array);
205         break;
206       }
207
208       case TreeNode::OBJECT:
209       {
210         Property::Map innerMap;
211         ParseProperties(kv.second, innerMap);
212         map.Insert(kv.first, innerMap);
213         break;
214       }
215
216       case TreeNode::STRING:
217       {
218         map.Insert(kv.first, kv.second.GetString());
219         break;
220       }
221
222       case TreeNode::INTEGER:
223       {
224         map.Insert(kv.first, kv.second.GetInteger());
225         break;
226       }
227
228       case TreeNode::BOOLEAN:
229       {
230         map.Insert(kv.first, kv.second.GetBoolean());
231         break;
232       }
233
234       case TreeNode::FLOAT:
235       {
236         map.Insert(kv.first, kv.second.GetFloat());
237         break;
238       }
239
240       case TreeNode::IS_NULL:
241       {
242         break;
243       }
244     }
245   }
246 }
247
248 void ParseProperties(const Toolkit::TreeNode& node, Property::Array& array)
249 {
250   DALI_ASSERT_DEBUG(node.GetType() == TreeNode::ARRAY);
251   for(auto i0 = node.CBegin(), i1 = node.CEnd(); i0 != i1; ++i0)
252   {
253     auto kv = *i0;
254     switch(kv.second.GetType())
255     {
256       case TreeNode::ARRAY:
257       {
258         Property::Array innerArray;
259         ParseProperties(kv.second, innerArray);
260         array.PushBack(innerArray);
261         break;
262       }
263
264       case TreeNode::OBJECT:
265       {
266         Property::Map map;
267         ParseProperties(kv.second, map);
268         array.PushBack(map);
269         break;
270       }
271
272       case TreeNode::STRING:
273       {
274         array.PushBack(kv.second.GetString());
275         break;
276       }
277
278       case TreeNode::INTEGER:
279       {
280         array.PushBack(kv.second.GetInteger());
281         break;
282       }
283
284       case TreeNode::BOOLEAN:
285       {
286         array.PushBack(kv.second.GetBoolean());
287         break;
288       }
289
290       case TreeNode::FLOAT:
291       {
292         array.PushBack(kv.second.GetFloat());
293         break;
294       }
295
296       case TreeNode::IS_NULL:
297       {
298         break;
299       }
300     }
301   }
302 }
303
304 } // namespace
305
306 struct DliLoader::Impl
307 {
308   StringCallback      mOnError = DefaultErrorCallback;
309   Toolkit::JsonParser mParser;
310
311   void ParseScene(LoadParams& params);
312
313 private:
314   std::map<Index, Matrix> mInverseBindMatrices;
315
316   /**
317    * @brief Due to .dli nodes being processed in depth-first traversal with orphans being
318    *  ignored, features that rely on node indices (which is more compact and closer to
319    *  glTF) require a mapping from .dli node indices to those in the resulting SceneDefinition.
320    *  The index mapper is responsible for maintaing this mapping, and resolving node IDs
321    *  once the processing of the nodes has finished.
322    * @note The resolution requires the whole scene graph to finish parsing, therefore any
323    *  node extensions relying on node IDs will see the dli ID in their processor.
324    */
325   struct IIndexMapper
326   {
327     /**
328      * @brief Attempts to create a mapping from a node's @a dli index to its @a scene
329      *  index.
330      * @return Whether the operation was successful.
331      */
332     virtual bool Map(Index iDli, Index iScene) = 0;
333
334     /**
335      * @return The scene index for the node's @a dli index.
336      */
337     virtual Index Resolve(Index iDli) = 0;
338   };
339
340   /**
341    * @brief Traverses the DOM tree created by LoadDocument() in an attempt to create
342    *  an intermediate representation of resources and nodes.
343    */
344   void ParseSceneInternal(Index iScene, const Toolkit::TreeNode* tnScenes, const Toolkit::TreeNode* tnNodes, LoadParams& params);
345
346   void ParseSkeletons(const Toolkit::TreeNode* skeletons, SceneDefinition& scene, ResourceBundle& resources);
347   void ParseEnvironments(const Toolkit::TreeNode* environments, ResourceBundle& resources);
348   void ParseMaterials(const Toolkit::TreeNode* materials, ConvertColorCode convertColorCode, ResourceBundle& resources);
349
350   void ParseNodes(const Toolkit::TreeNode* nodes, Index index, LoadParams& params);
351   void ParseNodesInternal(const Toolkit::TreeNode* nodes, Index index, std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& indexMapper);
352
353   void ParseAnimations(const Toolkit::TreeNode* animations, LoadParams& params);
354   void ParseAnimationGroups(const Toolkit::TreeNode* animationGroups, LoadParams& params);
355
356   void ParseShaders(const Toolkit::TreeNode* shaders, ResourceBundle& resources);
357   void ParseMeshes(const Toolkit::TreeNode* meshes, ResourceBundle& resources);
358
359   void GetCameraParameters(std::vector<CameraParameters>& cameras) const;
360   void GetLightParameters(std::vector<LightParameters>& lights) const;
361 };
362
363 DliLoader::DliLoader()
364 : mImpl{new Impl}
365 {
366 }
367
368 DliLoader::~DliLoader() = default;
369
370 void DliLoader::SetErrorCallback(StringCallback onError)
371 {
372   mImpl->mOnError = onError;
373 }
374
375 bool DliLoader::LoadScene(const std::string& uri, LoadParams& params)
376 {
377   std::string daliBuffer = LoadTextFile(uri.c_str());
378
379   auto& parser = mImpl->mParser;
380   parser       = JsonParser::New();
381   if(!parser.Parse(daliBuffer))
382   {
383     return false;
384   }
385
386   mImpl->ParseScene(params);
387   return true;
388 }
389
390 std::string DliLoader::GetParseError() const
391 {
392   std::stringstream stream;
393
394   auto& parser = mImpl->mParser;
395   if(parser.ParseError())
396   {
397     stream << "position: " << parser.GetErrorPosition() << ", line: " << parser.GetErrorLineNumber() << ", column: " << parser.GetErrorColumn() << ", description: " << parser.GetErrorDescription() << ".";
398   }
399
400   return stream.str();
401 }
402
403 void DliLoader::Impl::ParseScene(LoadParams& params)
404 {
405   auto& input  = params.input;
406   auto& output = params.output;
407
408   // get index of root node.
409   auto docRoot = mParser.GetRoot();
410   if(docRoot)
411   {
412     // Process resources first - these are shared
413     if(auto environments = docRoot->GetChild("environment"))
414     {
415       ParseEnvironments(environments, output.mResources); // NOTE: must precede parsing of materials
416     }
417
418     if(auto meshes = docRoot->GetChild("meshes"))
419     {
420       ParseMeshes(meshes, output.mResources);
421     }
422
423     if(auto shaders = docRoot->GetChild("shaders"))
424     {
425       ParseShaders(shaders, output.mResources);
426     }
427
428     if(auto materials = docRoot->GetChild("materials"))
429     {
430       ParseMaterials(materials, input.mConvertColorCode, output.mResources);
431     }
432
433     for(auto& c : input.mPreNodeCategoryProcessors)
434     {
435       if(auto node = docRoot->GetChild(c.first))
436       {
437         Property::Array array;
438         ParseProperties(*node, array);
439         c.second(std::move(array), mOnError);
440       }
441     }
442
443     // Process scenes
444     Index iScene = 0; // default scene
445     ReadIndex(docRoot->GetChild("scene"), iScene);
446
447     auto tnScenes = RequireChild(docRoot, "scenes");
448     auto tnNodes  = RequireChild(docRoot, "nodes");
449     ParseSceneInternal(iScene, tnScenes, tnNodes, params);
450
451     ParseSkeletons(docRoot->GetChild("skeletons"), output.mScene, output.mResources);
452
453     output.mScene.EnsureUniqueSkinningShaderInstances(output.mResources);
454     output.mScene.EnsureUniqueBlendShapeShaderInstances(output.mResources);
455
456     // Ger cameras and lights
457     GetCameraParameters(output.mCameraParameters);
458     GetLightParameters(output.mLightParameters);
459
460     // Post-node processors and animations last
461     for(auto& c : input.mPostNodeCategoryProcessors)
462     {
463       if(auto node = docRoot->GetChild(c.first))
464       {
465         Property::Array array;
466         ParseProperties(*node, array);
467         c.second(std::move(array), mOnError);
468       }
469     }
470
471     if(auto animations = docRoot->GetChild("animations"))
472     {
473       ParseAnimations(animations, params);
474     }
475
476     if(!output.mAnimationDefinitions.empty())
477     {
478       if(auto animationGroups = docRoot->GetChild("animationGroups"))
479       {
480         ParseAnimationGroups(animationGroups, params);
481       }
482     }
483   }
484 }
485
486 void DliLoader::Impl::ParseSceneInternal(Index iScene, const Toolkit::TreeNode* tnScenes, const Toolkit::TreeNode* tnNodes, LoadParams& params)
487 {
488   auto getSceneRootIdx = [tnScenes, tnNodes](Index iScene)
489   {
490     auto tn = GetNthChild(tnScenes, iScene); // now a "scene" object
491     if(!tn)
492     {
493       ExceptionFlinger(ASSERT_LOCATION) << iScene << " is out of bounds access into " << SCENES << ".";
494     }
495
496     tn = RequireChild(tn, NODES); // now a "nodes" array
497     if(tn->GetType() != TreeNode::ARRAY)
498     {
499       ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES << " has an invalid type; array required.";
500     }
501
502     if(tn->Size() < 1)
503     {
504       ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES << " must define a node id.";
505     }
506
507     tn = GetNthChild(tn, 0); // now the first element of the array
508     Index iRootNode;
509     if(!ReadIndex(tn, iRootNode))
510     {
511       ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES << " has an invalid value for root node index: '" << iRootNode << "'.";
512     }
513
514     if(iRootNode >= tnNodes->Size())
515     {
516       ExceptionFlinger(ASSERT_LOCATION) << "Root node index << " << iRootNode << " of scene " << iScene << " is out of bounds.";
517     }
518
519     tn = GetNthChild(tnNodes, iRootNode); // now a "node" object
520     if(tn->GetType() != TreeNode::OBJECT)
521     {
522       ExceptionFlinger(ASSERT_LOCATION) << "Root node of scene " << iScene << " is of invalid JSON type; object required";
523     }
524
525     return iRootNode;
526   };
527
528   Index iRootNode = getSceneRootIdx(iScene);
529   ParseNodes(tnNodes, iRootNode, params);
530
531   auto& scene = params.output.mScene;
532   scene.AddRootNode(0);
533
534   for(Index i = 0; i < iScene; ++i)
535   {
536     Index       iRootNode = getSceneRootIdx(i);
537     const Index iRoot     = scene.GetNodeCount();
538     ParseNodes(tnNodes, iRootNode, params);
539     scene.AddRootNode(iRoot);
540   }
541
542   auto numScenes = tnScenes->Size();
543   for(Index i = iScene + 1; i < numScenes; ++i)
544   {
545     Index       iRootNode = getSceneRootIdx(i);
546     const Index iRoot     = scene.GetNodeCount();
547     ParseNodes(tnNodes, iRootNode, params);
548     scene.AddRootNode(iRoot);
549   }
550 }
551
552 void DliLoader::Impl::ParseSkeletons(const TreeNode* skeletons, SceneDefinition& scene, ResourceBundle& resources)
553 {
554   if(skeletons)
555   {
556     auto iStart = skeletons->CBegin();
557     for(auto i0 = iStart, i1 = skeletons->CEnd(); i0 != i1; ++i0)
558     {
559       auto&       node = (*i0).second;
560       std::string skeletonRootName;
561       if(ReadString(node.GetChild(NODE), skeletonRootName))
562       {
563         SkeletonDefinition skeleton;
564         if(!scene.FindNode(skeletonRootName, &skeleton.mRootNodeIdx))
565         {
566           ExceptionFlinger(ASSERT_LOCATION) << FormatString("Skeleton %d: node '%s' not defined.", resources.mSkeletons.size(), skeletonRootName.c_str());
567         }
568
569         uint32_t                   jointCount = 0;
570         std::function<void(Index)> visitFn;
571         auto&                      ibms = mInverseBindMatrices;
572         visitFn                         = [&](Index id)
573         {
574           auto node = scene.GetNode(id);
575           jointCount += ibms.find(id) != ibms.end();
576
577           for(auto i : node->mChildren)
578           {
579             visitFn(i);
580           }
581         };
582         visitFn(skeleton.mRootNodeIdx);
583
584         if(jointCount > Skinning::MAX_JOINTS)
585         {
586           mOnError(FormatString("Skeleton %d: joint count exceeds supported limit.", resources.mSkeletons.size()));
587           jointCount = Skinning::MAX_JOINTS;
588         }
589
590         skeleton.mJoints.reserve(jointCount);
591
592         visitFn = [&](Index id)
593         {
594           auto iFind = ibms.find(id);
595           if(iFind != ibms.end() && skeleton.mJoints.size() < Skinning::MAX_JOINTS)
596           {
597             skeleton.mJoints.push_back({id, iFind->second});
598           }
599
600           auto node = scene.GetNode(id);
601           for(auto i : node->mChildren)
602           {
603             visitFn(i);
604           }
605         };
606         visitFn(skeleton.mRootNodeIdx);
607
608         resources.mSkeletons.push_back(std::move(skeleton));
609       }
610       else
611       {
612         ExceptionFlinger(ASSERT_LOCATION) << "skeleton " << std::distance(iStart, i0) << ": Missing required attribute '" << NODE << "'.";
613       }
614     }
615   }
616 }
617
618 void DliLoader::Impl::ParseEnvironments(const TreeNode* environments, ResourceBundle& resources)
619 {
620   Matrix cubeOrientation(Matrix::IDENTITY);
621
622   for(auto i0 = environments->CBegin(), i1 = environments->CEnd(); i0 != i1; ++i0)
623   {
624     auto& node = (*i0).second;
625
626     EnvironmentDefinition envDef;
627     ReadString(node.GetChild("cubeSpecular"), envDef.mSpecularMapPath);
628     ReadString(node.GetChild("cubeDiffuse"), envDef.mDiffuseMapPath);
629     ToUnixFileSeparators(envDef.mSpecularMapPath);
630     ToUnixFileSeparators(envDef.mDiffuseMapPath);
631     envDef.mIblIntensity = 1.0f;
632     ReadFloat(node.GetChild("iblIntensity"), envDef.mIblIntensity);
633     if(ReadVector(node.GetChild("cubeInitialOrientation"), cubeOrientation.AsFloat(), 16u))
634     {
635       envDef.mCubeOrientation = Quaternion(cubeOrientation);
636     }
637
638     resources.mEnvironmentMaps.emplace_back(std::move(envDef), EnvironmentDefinition::Textures());
639   }
640
641   // NOTE: guarantees environmentMaps to have an empty environment.
642   if(resources.mEnvironmentMaps.empty())
643   {
644     resources.mEnvironmentMaps.emplace_back(EnvironmentDefinition(), EnvironmentDefinition::Textures());
645   }
646 }
647
648 void DliLoader::Impl::ParseShaders(const TreeNode* shaders, ResourceBundle& resources)
649 {
650   uint32_t iShader = 0;
651   for(auto i0 = shaders->CBegin(), i1 = shaders->CEnd(); i0 != i1; ++i0, ++iShader)
652   {
653     auto&            node = (*i0).second;
654     ShaderDefinition shaderDef;
655     ReadStringVector(node.GetChild("defines"), shaderDef.mDefines);
656
657     // Read shader hints. Possible values are:
658     //                         Don't define for No hints.
659     // "OUTPUT_IS_TRANSPARENT" Might generate transparent alpha from opaque inputs.
660     //     "MODIFIES_GEOMETRY" Might change position of vertices, this option disables any culling optimizations.
661
662     ReadStringVector(node.GetChild(HINTS), shaderDef.mHints);
663
664     if(ReadString(node.GetChild("vertex"), shaderDef.mVertexShaderPath) &&
665        ReadString(node.GetChild("fragment"), shaderDef.mFragmentShaderPath))
666     {
667       ToUnixFileSeparators(shaderDef.mVertexShaderPath);
668       ToUnixFileSeparators(shaderDef.mFragmentShaderPath);
669
670       for(TreeNode::ConstIterator j0 = node.CBegin(), j1 = node.CEnd(); j0 != j1; ++j0)
671       {
672         const TreeNode::KeyNodePair& keyValue = *j0;
673         const std::string&           key      = keyValue.first;
674         const TreeNode&              value    = keyValue.second;
675
676         Property::Value uniformValue;
677         if(key.compare("vertex") == 0 || key.compare("fragment") == 0 || key.compare("defines") == 0 || key.compare(HINTS) == 0)
678         {
679           continue;
680         }
681         else if(key.compare("rendererState") == 0)
682         {
683           shaderDef.mRendererState = ReadRendererState(keyValue.second);
684         }
685         else if(value.GetType() == TreeNode::INTEGER || value.GetType() == TreeNode::FLOAT)
686         {
687           float f = 0.f;
688           ReadFloat(&value, f);
689           uniformValue = f;
690         }
691         else if(value.GetType() == TreeNode::BOOLEAN)
692         {
693           DALI_LOG_WARNING("\"bool\" uniforms are handled as floats in shader");
694           bool value = false;
695           if(ReadBool(&keyValue.second, value))
696           {
697             uniformValue = value ? 1.0f : 0.0f;
698           }
699         }
700         else
701           switch(auto size = GetNumericalArraySize(&value))
702           {
703             case 16:
704             {
705               Matrix m;
706               ReadVector(&value, m.AsFloat(), size);
707               uniformValue = m;
708               break;
709             }
710
711             case 9:
712             {
713               Matrix3 m;
714               ReadVector(&value, m.AsFloat(), size);
715               uniformValue = m;
716               break;
717             }
718
719             case 4:
720             {
721               Vector4 v;
722               ReadVector(&value, v.AsFloat(), size);
723               uniformValue = v;
724               break;
725             }
726
727             case 3:
728             {
729               Vector3 v;
730               ReadVector(&value, v.AsFloat(), size);
731               uniformValue = v;
732               break;
733             }
734
735             case 2:
736             {
737               Vector2 v;
738               ReadVector(&value, v.AsFloat(), size);
739               uniformValue = v;
740               break;
741             }
742
743             default:
744               mOnError(FormatString(
745                 "shader %d: Ignoring uniform '%s': failed to infer type from %d elements.",
746                 iShader,
747                 key.c_str()));
748               break;
749           }
750
751         if(Property::NONE != uniformValue.GetType())
752         {
753           shaderDef.mUniforms.Insert(key, uniformValue);
754         }
755       }
756
757       resources.mShaders.emplace_back(std::move(shaderDef), Shader());
758     }
759     else
760     {
761       ExceptionFlinger(ASSERT_LOCATION) << "shader " << iShader << ": Missing vertex / fragment shader definition.";
762     }
763   }
764 }
765
766 void DliLoader::Impl::ParseMeshes(const TreeNode* meshes, ResourceBundle& resources)
767 {
768   for(auto i0 = meshes->CBegin(), i1 = meshes->CEnd(); i0 != i1; ++i0)
769   {
770     auto& node = (*i0).second;
771
772     MeshDefinition meshDef;
773     if(!ReadString(node.GetChild(URI), meshDef.mUri))
774     {
775       ExceptionFlinger(ASSERT_LOCATION) << "mesh " << resources.mMeshes.size() << ": Missing required attribute '" << URI << "'.";
776     }
777
778     ToUnixFileSeparators(meshDef.mUri);
779
780     std::string primitive;
781     if(ReadString(node.GetChild("primitive"), primitive))
782     {
783       if(primitive == "LINES")
784       {
785         meshDef.mPrimitiveType = Geometry::LINES;
786       }
787       else if(primitive == "POINTS")
788       {
789         meshDef.mPrimitiveType = Geometry::POINTS;
790       }
791       else if(primitive != "TRIANGLES")
792       {
793         mOnError(FormatString(
794           "mesh %d: Using TRIANGLES instead of unsupported primitive type '%s'.",
795           resources.mMeshes.size(),
796           primitive.c_str()));
797       }
798     }
799
800     int attributes;
801     if(ReadInt(node.GetChild("attributes"), attributes))
802     {
803       if(MaskMatch(attributes, MeshDefinition::INDICES) &&
804          !ReadAttribAccessor(node.GetChild("indices"), meshDef.mIndices))
805       {
806         ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
807                                                           resources.mMeshes.size(),
808                                                           "indices");
809       }
810
811       if(MaskMatch(attributes, MeshDefinition::POSITIONS) &&
812          !ReadAttribAccessor(node.GetChild("positions"), meshDef.mPositions))
813       {
814         ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
815                                                           resources.mMeshes.size(),
816                                                           "positions");
817       }
818
819       if(MaskMatch(attributes, MeshDefinition::NORMALS) &&
820          !ReadAttribAccessor(node.GetChild("normals"), meshDef.mNormals))
821       {
822         mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "normals"));
823       }
824
825       if(MaskMatch(attributes, MeshDefinition::TEX_COORDS) &&
826          !ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords))
827       {
828         mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "textures"));
829       }
830
831       if(MaskMatch(attributes, MeshDefinition::TANGENTS) &&
832          !ReadAttribAccessor(node.GetChild("tangents"), meshDef.mTangents))
833       {
834         mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "tangents"));
835       }
836
837       // NOTE: we're no longer reading bitangents as these are calculated in the shaders.
838       if(ReadIndex(node.GetChild("skeleton"), meshDef.mSkeletonIdx))
839       {
840         if(!MaskMatch(attributes, MeshDefinition::JOINTS_0) &&
841            !MaskMatch(attributes, MeshDefinition::WEIGHTS_0))
842         {
843           mOnError(FormatString("mesh %d: Expected joints0 / weights0 attribute(s) missing.",
844                                 resources.mMeshes.size()));
845         }
846         else if(!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints0) ||
847                 !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights0))
848         {
849           mOnError(FormatString("mesh %d: Failed to read skinning information.",
850                                 resources.mMeshes.size()));
851         }
852       }
853
854       if(auto blendshapeHeader = node.GetChild(BLEND_SHAPE_HEADER))
855       {
856         std::string blendShapeVersion;
857         ReadString(blendshapeHeader->GetChild(VERSION), blendShapeVersion);
858
859         if(0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_1_0))
860         {
861           meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_1_0;
862         }
863         else if(0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_2_0))
864         {
865           meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
866         }
867
868         switch(meshDef.mBlendShapeVersion)
869         {
870           case BlendShapes::Version::VERSION_1_0:
871           case BlendShapes::Version::VERSION_2_0: // FALL THROUGH
872           {
873             ReadAttribBlob(blendshapeHeader, meshDef.mBlendShapeHeader);
874             break;
875           }
876           default:
877           {
878             // nothing to do
879             break;
880           }
881         }
882       }
883
884       if(auto blendShapes = node.GetChild(BLEND_SHAPES))
885       {
886         meshDef.mBlendShapes.resize(blendShapes->Size());
887
888         auto index = 0u;
889         for(auto it = blendShapes->CBegin(), endIt = blendShapes->CEnd(); it != endIt; ++it, ++index)
890         {
891           // Each blend shape is stored as the difference with the original mesh.
892
893           auto& blendShapeNode = (*it).second;
894
895           auto& blendShape = meshDef.mBlendShapes[index];
896           ReadString(blendShapeNode.GetChild("name"), blendShape.name);
897           if(auto position = blendShapeNode.GetChild("positions"))
898           {
899             ReadAttribAccessor(position, blendShape.deltas);
900           }
901           if(auto normals = blendShapeNode.GetChild("normals"))
902           {
903             ReadAttribAccessor(normals, blendShape.normals);
904           }
905           if(auto tangents = blendShapeNode.GetChild("tangents"))
906           {
907             ReadAttribAccessor(tangents, blendShape.tangents);
908           }
909           ReadFloat(blendShapeNode.GetChild("weight"), blendShape.weight);
910         }
911       }
912
913       bool flipV;
914       if(ReadBool(node.GetChild("flipV"), flipV))
915       {
916         meshDef.mFlags |= flipV * MeshDefinition::FLIP_UVS_VERTICAL;
917       }
918
919       resources.mMeshes.emplace_back(std::move(meshDef), MeshGeometry());
920     }
921   }
922 }
923
924 void DliLoader::Impl::ParseMaterials(const TreeNode* materials, ConvertColorCode convertColorCode, ResourceBundle& resources)
925 {
926   for(auto i0 = materials->CBegin(), i1 = materials->CEnd(); i0 != i1; ++i0)
927   {
928     auto& node = (*i0).second;
929
930     MaterialDefinition materialDef;
931     if(auto eEnvironment = node.GetChild("environment"))
932     {
933       ReadIndex(eEnvironment, materialDef.mEnvironmentIdx);
934       if(static_cast<unsigned int>(materialDef.mEnvironmentIdx) >= resources.mEnvironmentMaps.size())
935       {
936         ExceptionFlinger(ASSERT_LOCATION) << "material " << resources.mMaterials.size() << ": Environment index " << materialDef.mEnvironmentIdx << " out of bounds (" << resources.mEnvironmentMaps.size() << ").";
937       }
938     }
939
940     // TODO : need to consider AGIF
941     std::vector<std::string> texturePaths;
942     std::string              texturePath;
943     if(ReadString(node.GetChild("albedoMap"), texturePath))
944     {
945       ToUnixFileSeparators(texturePath);
946       const auto semantic = MaterialDefinition::ALBEDO;
947       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
948       materialDef.mFlags |= semantic | MaterialDefinition::TRANSPARENCY; // NOTE: only in dli does single / separate ALBEDO texture mean TRANSPARENCY.
949     }
950     if(ReadString(node.GetChild("albedoMetallicMap"), texturePath))
951     {
952       ToUnixFileSeparators(texturePath);
953
954       if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
955       {
956         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "albedo"));
957       }
958
959       const auto semantic = MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC;
960       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
961       materialDef.mFlags |= semantic;
962     }
963
964     if(ReadString(node.GetChild("metallicRoughnessMap"), texturePath))
965     {
966       ToUnixFileSeparators(texturePath);
967
968       if(MaskMatch(materialDef.mFlags, MaterialDefinition::METALLIC))
969       {
970         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "metallic"));
971       }
972
973       const auto semantic = MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS;
974       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
975       materialDef.mFlags |= semantic |
976                             // We have a metallic-roughhness map and the first texture did not have albedo semantics - we're in the transparency workflow.
977                             (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
978     }
979
980     if(ReadString(node.GetChild("normalMap"), texturePath))
981     {
982       ToUnixFileSeparators(texturePath);
983
984       const auto semantic = MaterialDefinition::NORMAL;
985       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
986       materialDef.mFlags |= semantic |
987                             // We have a standalone normal map and the first texture did not have albedo semantics - we're in the transparency workflow.
988                             (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
989     }
990
991     if(ReadString(node.GetChild("normalRoughnessMap"), texturePath))
992     {
993       ToUnixFileSeparators(texturePath);
994
995       if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
996       {
997         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "normal"));
998       }
999
1000       if(MaskMatch(materialDef.mFlags, MaterialDefinition::ROUGHNESS))
1001       {
1002         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "roughness"));
1003       }
1004
1005       if(MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY))
1006       {
1007         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "transparency"));
1008       }
1009
1010       const auto semantic = MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS;
1011       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1012       materialDef.mFlags |= semantic;
1013     }
1014
1015     if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
1016     {
1017       ToUnixFileSeparators(texturePath);
1018
1019       const auto semantic = MaterialDefinition::SUBSURFACE;
1020       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1021       materialDef.mFlags |= semantic;
1022     }
1023
1024     if(ReadString(node.GetChild("occlusionMap"), texturePath))
1025     {
1026       ToUnixFileSeparators(texturePath);
1027       const auto semantic = MaterialDefinition::OCCLUSION;
1028       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1029       materialDef.mFlags |= semantic;
1030     }
1031
1032     if(ReadColorCodeOrColor(&node, materialDef.mColor, convertColorCode) &&
1033        materialDef.mColor.a < 1.0f)
1034     {
1035       materialDef.mFlags |= MaterialDefinition::TRANSPARENCY;
1036     }
1037
1038     ReadFloat(node.GetChild("metallic"), materialDef.mMetallic);
1039     ReadFloat(node.GetChild("roughness"), materialDef.mRoughness);
1040
1041     bool mipmaps;
1042     if(ReadBool(node.GetChild("mipmap"), mipmaps) && mipmaps)
1043     {
1044       for(auto& ts : materialDef.mTextureStages)
1045       {
1046         ts.mTexture.mSamplerFlags |= SamplerFlags::FILTER_MIPMAP_LINEAR;
1047       }
1048     }
1049
1050     resources.mMaterials.emplace_back(std::move(materialDef), TextureSet());
1051   }
1052 }
1053
1054 void DliLoader::Impl::ParseNodes(const TreeNode* const nodes, Index index, LoadParams& params)
1055 {
1056   std::vector<Index> parents;
1057   parents.reserve(8);
1058
1059   struct IndexMapper : IIndexMapper
1060   {
1061     IndexMapper(size_t numNodes)
1062     {
1063       mIndices.reserve(numNodes);
1064     }
1065
1066     virtual bool Map(Index iDli, Index iScene) override
1067     {
1068       Entry idx{iDli, iScene};
1069       auto  iInsert = std::lower_bound(mIndices.begin(), mIndices.end(), idx);
1070       if(iInsert == mIndices.end() || iInsert->iDli != iDli)
1071       {
1072         mIndices.insert(iInsert, idx);
1073       }
1074       else if(iInsert->iScene != iScene)
1075       {
1076         return false;
1077       }
1078       return true;
1079     }
1080
1081     virtual unsigned int Resolve(Index iDli) override
1082     {
1083       auto iFind = std::lower_bound(mIndices.begin(), mIndices.end(), iDli, [](const Entry& idx, Index iDli)
1084                                     { return idx.iDli < iDli; });
1085       DALI_ASSERT_ALWAYS(iFind != mIndices.end());
1086       return iFind->iScene;
1087     }
1088
1089   private:
1090     struct Entry
1091     {
1092       unsigned int iDli;
1093       unsigned int iScene;
1094
1095       bool operator<(const Entry& other) const
1096       {
1097         return iDli < other.iDli;
1098       }
1099     };
1100     std::vector<Entry> mIndices;
1101   } mapper(nodes->Size());
1102   ParseNodesInternal(nodes, index, parents, params, mapper);
1103
1104   auto& scene = params.output.mScene;
1105   for(size_t i0 = 0, i1 = scene.GetNodeCount(); i0 < i1; ++i0)
1106   {
1107     for(auto& c : scene.GetNode(i0)->mConstraints)
1108     {
1109       c.mSourceIdx = mapper.Resolve(c.mSourceIdx);
1110     }
1111   }
1112 }
1113
1114 void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index index, std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& mapper)
1115 {
1116   // Properties that may be resolved from a JSON value with ReadInt() -- or default to 0.
1117   struct IndexProperty
1118   {
1119     ResourceType::Value type;
1120     const TreeNode*     source;
1121     Index&              target;
1122   };
1123   std::vector<IndexProperty> resourceIds;
1124   resourceIds.reserve(4);
1125
1126   if(auto node = GetNthChild(nodes, index))
1127   {
1128     NodeDefinition nodeDef;
1129     nodeDef.mParentIdx = inOutParentStack.empty() ? INVALID_INDEX : inOutParentStack.back();
1130
1131     // name
1132     ReadString(node->GetChild(NAME), nodeDef.mName);
1133
1134     // transform
1135     ReadModelTransform(node, nodeDef.mOrientation, nodeDef.mPosition, nodeDef.mScale);
1136
1137     // Reads the size of the node.
1138     //
1139     // * It can be given as 'size' or 'bounds'.
1140     // * The sdk saves the 'size' as a vector2 in some cases.
1141     // * To avoid size related issues the following code attemps
1142     //   to read the 'size/bounds' as a vector3 first, if it's
1143     //   not successful then reads it as a vector2.
1144     ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 3) ||
1145       ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 2) ||
1146       ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 3) ||
1147       ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 2);
1148
1149     // visibility
1150     ReadBool(node->GetChild("visible"), nodeDef.mIsVisible);
1151
1152     // type classification
1153     if(auto eCustomization = node->GetChild("customization")) // customization
1154     {
1155       std::string tag;
1156       if(ReadString(eCustomization->GetChild("tag"), tag))
1157       {
1158         nodeDef.mCustomization.reset(new NodeDefinition::CustomizationDefinition{tag});
1159       }
1160     }
1161     else // something renderable maybe
1162     {
1163       std::unique_ptr<NodeDefinition::Renderable> renderable;
1164       ModelRenderable*                            modelRenderable = nullptr; // no ownership, aliasing renderable for the right type.
1165
1166       const TreeNode* eRenderable = nullptr;
1167       if((eRenderable = node->GetChild("model")))
1168       {
1169         // check for mesh before allocating - this can't be missing.
1170         auto eMesh = eRenderable->GetChild("mesh");
1171         if(!eMesh)
1172         {
1173           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1174         }
1175
1176         modelRenderable = new ModelRenderable();
1177         renderable.reset(modelRenderable);
1178
1179         resourceIds.push_back({ResourceType::Mesh, eMesh, modelRenderable->mMeshIdx});
1180       }
1181       else if((eRenderable = node->GetChild("arc")))
1182       {
1183         // check for mesh before allocating - this can't be missing.
1184         auto eMesh = eRenderable->GetChild("mesh");
1185         if(!eMesh)
1186         {
1187           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1188         }
1189
1190         auto arcRenderable = new ArcRenderable;
1191         renderable.reset(arcRenderable);
1192         modelRenderable = arcRenderable;
1193
1194         resourceIds.push_back({ResourceType::Mesh, eMesh, arcRenderable->mMeshIdx});
1195
1196         ReadArcField(eRenderable, *arcRenderable);
1197       }
1198
1199       if(renderable && eRenderable != nullptr) // process common properties of all renderables + register payload
1200       {
1201         // shader
1202         renderable->mShaderIdx = 0;
1203         auto eShader           = eRenderable->GetChild("shader");
1204         if(eShader)
1205         {
1206           resourceIds.push_back({ResourceType::Shader, eShader, renderable->mShaderIdx});
1207         }
1208
1209         // color
1210         if(modelRenderable)
1211         {
1212           modelRenderable->mMaterialIdx = 0; // must offer default of 0
1213           auto eMaterial                = eRenderable->GetChild("material");
1214           if(eMaterial)
1215           {
1216             resourceIds.push_back({ResourceType::Material, eMaterial, modelRenderable->mMaterialIdx});
1217           }
1218
1219           if(!ReadColorCodeOrColor(eRenderable, modelRenderable->mColor, params.input.mConvertColorCode))
1220           {
1221             ReadColorCodeOrColor(node, modelRenderable->mColor, params.input.mConvertColorCode);
1222           }
1223         }
1224
1225         nodeDef.mRenderables.push_back(std::move(renderable));
1226       }
1227     }
1228
1229     // Resolve ints - default to 0 if undefined
1230     auto& output = params.output;
1231     for(auto& idRes : resourceIds)
1232     {
1233       Index iCheck = 0;
1234       switch(idRes.type)
1235       {
1236         case ResourceType::Shader:
1237           iCheck = output.mResources.mShaders.size();
1238           break;
1239
1240         case ResourceType::Mesh:
1241           iCheck = output.mResources.mMeshes.size();
1242           break;
1243
1244         case ResourceType::Material:
1245           iCheck = output.mResources.mMaterials.size();
1246           break;
1247
1248         default:
1249           ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid resource type: " << idRes.type << " (Programmer error)";
1250       }
1251
1252       if(!idRes.source)
1253       {
1254         idRes.target = 0;
1255       }
1256       else if(idRes.source->GetType() != TreeNode::INTEGER)
1257       {
1258         ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid " << GetResourceTypeName(idRes.type) << " index type.";
1259       }
1260       else
1261       {
1262         idRes.target = idRes.source->GetInteger();
1263       }
1264
1265       if(idRes.target >= iCheck)
1266       {
1267         ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": " << GetResourceTypeName(idRes.type) << " index " << idRes.target << " out of bounds (" << iCheck << ").";
1268       }
1269     }
1270     resourceIds.clear();
1271
1272     // Extra properties
1273     if(auto eExtras = node->GetChild("extras"))
1274     {
1275       auto& extras = nodeDef.mExtras;
1276       extras.reserve(eExtras->Size());
1277
1278       for(auto i0 = eExtras->CBegin(), i1 = eExtras->CEnd(); i0 != i1; ++i0)
1279       {
1280         NodeDefinition::Extra e;
1281
1282         auto eExtra = *i0;
1283         e.mKey      = eExtra.first;
1284         if(e.mKey.empty())
1285         {
1286           mOnError(FormatString("node %d: empty string is invalid for name of extra %d; ignored.",
1287                                 index,
1288                                 extras.size()));
1289           continue;
1290         }
1291
1292         e.mValue = ReadPropertyValue(eExtra.second);
1293         if(e.mValue.GetType() == Property::Type::NONE)
1294         {
1295           mOnError(FormatString("node %d: failed to interpret value of extra '%s' : %s; ignored.",
1296                                 index,
1297                                 e.mKey.c_str(),
1298                                 eExtra.second.GetString()));
1299         }
1300         else
1301         {
1302           auto iInsert = std::lower_bound(extras.begin(), extras.end(), e);
1303           if(iInsert != extras.end() && iInsert->mKey == e.mKey)
1304           {
1305             mOnError(FormatString("node %d: extra '%s' already defined; overriding with %s.",
1306                                   index,
1307                                   e.mKey.c_str(),
1308                                   eExtra.second.GetString()));
1309             *iInsert = std::move(e);
1310           }
1311           else
1312           {
1313             extras.insert(iInsert, e);
1314           }
1315         }
1316       }
1317     }
1318
1319     // Constraints
1320     if(auto eConstraints = node->GetChild("constraints"))
1321     {
1322       auto& constraints = nodeDef.mConstraints;
1323       constraints.reserve(eConstraints->Size());
1324
1325       ConstraintDefinition cDef;
1326       for(auto i0 = eConstraints->CBegin(), i1 = eConstraints->CEnd(); i0 != i1; ++i0)
1327       {
1328         auto eConstraint = *i0;
1329         if(!ReadIndex(&eConstraint.second, cDef.mSourceIdx))
1330         {
1331           mOnError(FormatString("node %d: node ID %s for constraint %d is invalid; ignored.",
1332                                 index,
1333                                 eConstraint.second.GetString(),
1334                                 constraints.size()));
1335         }
1336         else
1337         {
1338           cDef.mProperty = eConstraint.first;
1339
1340           auto iInsert = std::lower_bound(constraints.begin(), constraints.end(), cDef);
1341           if(iInsert != constraints.end() && *iInsert == cDef)
1342           {
1343             mOnError(FormatString("node %d: constraint %s@%d already defined; ignoring.",
1344                                   index,
1345                                   cDef.mProperty.c_str(),
1346                                   cDef.mSourceIdx));
1347           }
1348           else
1349           {
1350             constraints.insert(iInsert, cDef);
1351           }
1352         }
1353       }
1354     }
1355
1356     // Determine index for mapping
1357     const unsigned int myIndex = output.mScene.GetNodeCount();
1358     if(!mapper.Map(index, myIndex))
1359     {
1360       mOnError(FormatString("node %d: error mapping dli index %d: node has multiple parents. Ignoring subtree."));
1361       return;
1362     }
1363
1364     // if the node is a bone in a skeletal animation, it will have the inverse bind pose matrix.
1365     Matrix invBindMatrix{false};
1366     if(ReadVector(node->GetChild("inverseBindPoseMatrix"), invBindMatrix.AsFloat(), 16u)) // TODO: more robust error checking?
1367     {
1368       mInverseBindMatrices[myIndex] = invBindMatrix;
1369     }
1370
1371     // Register nodeDef
1372     auto rawDef = output.mScene.AddNode(std::make_unique<NodeDefinition>(std::move(nodeDef)));
1373     if(rawDef) // NOTE: no ownership. Guaranteed to stay in scope.
1374     {
1375       // ...And only then parse children.
1376       if(auto children = node->GetChild("children"))
1377       {
1378         inOutParentStack.push_back(myIndex);
1379
1380         rawDef->mChildren.reserve(children->Size());
1381
1382         uint32_t iChild = 0;
1383         for(auto j0 = children->CBegin(), j1 = children->CEnd(); j0 != j1; ++j0, ++iChild)
1384         {
1385           auto& child = (*j0).second;
1386           if(child.GetType() == TreeNode::INTEGER)
1387           {
1388             ParseNodesInternal(nodes, child.GetInteger(), inOutParentStack, params, mapper); // child object is created in scene definition.
1389           }
1390           else
1391           {
1392             ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ", child " << iChild << ": invalid index type.";
1393           }
1394         }
1395
1396         inOutParentStack.pop_back();
1397       }
1398       else if(rawDef->mCustomization)
1399       {
1400         mOnError(FormatString("node %d: not an actual customization without children.", index));
1401       }
1402
1403       if(auto proc = params.input.mNodePropertyProcessor) // optional processing
1404       {
1405         // WARNING: constraint IDs are not resolved at this point.
1406         Property::Map nodeData;
1407         ParseProperties(*node, nodeData);
1408         proc(*rawDef, std::move(nodeData), mOnError);
1409       }
1410     }
1411     else
1412     {
1413       ExceptionFlinger(ASSERT_LOCATION) << "Node " << index << ": name already used.";
1414     }
1415   }
1416 }
1417
1418 void DliLoader::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams& params)
1419 {
1420   auto& definitions = params.output.mAnimationDefinitions;
1421   definitions.reserve(definitions.size() + tnAnimations->Size());
1422
1423   for(TreeNode::ConstIterator iAnim = tnAnimations->CBegin(), iAnimEnd = tnAnimations->CEnd();
1424       iAnim != iAnimEnd;
1425       ++iAnim)
1426   {
1427     const TreeNode&     tnAnim = (*iAnim).second;
1428     AnimationDefinition animDef;
1429     ReadString(tnAnim.GetChild(NAME), animDef.mName);
1430
1431     auto       iFind     = std::lower_bound(definitions.begin(), definitions.end(), animDef, [](const AnimationDefinition& ad0, const AnimationDefinition& ad1)
1432                                   { return ad0.mName < ad1.mName; });
1433     const bool overwrite = iFind != definitions.end() && iFind->mName == animDef.mName;
1434     if(overwrite)
1435     {
1436       mOnError(FormatString("Pre-existing animation with name '%s' is being overwritten.", animDef.mName.c_str()));
1437     }
1438
1439     // Duration -- We need something that animated properties' delay / duration can
1440     // be expressed as a multiple of; 0 won't work. This is small enough (i.e. shorter
1441     // than our frame delay) to not be restrictive WRT replaying. If anything needs
1442     // to occur more frequently, then Animations are likely not your solution anyway.
1443     animDef.mDuration = AnimationDefinition::MIN_DURATION_SECONDS;
1444     if(!ReadFloat(tnAnim.GetChild("duration"), animDef.mDuration))
1445     {
1446       mOnError(FormatString("Animation '%s' fails to define '%s', defaulting to %f.",
1447                             animDef.mName.c_str(),
1448                             "duration",
1449                             animDef.mDuration));
1450     }
1451
1452     // Get loop count - # of playbacks. Default is once. 0 means repeat indefinitely.
1453     animDef.mLoopCount = 1;
1454     if(ReadInt(tnAnim.GetChild("loopCount"), animDef.mLoopCount) &&
1455        animDef.mLoopCount < 0)
1456     {
1457       animDef.mLoopCount = 0;
1458     }
1459
1460     std::string endAction;
1461     if(ReadString(tnAnim.GetChild("endAction"), endAction))
1462     {
1463       if("BAKE" == endAction)
1464       {
1465         animDef.mEndAction = Animation::BAKE;
1466       }
1467       else if("DISCARD" == endAction)
1468       {
1469         animDef.mEndAction = Animation::DISCARD;
1470       }
1471       else if("BAKE_FINAL" == endAction)
1472       {
1473         animDef.mEndAction = Animation::BAKE_FINAL;
1474       }
1475     }
1476
1477     if(ReadString(tnAnim.GetChild("disconnectAction"), endAction))
1478     {
1479       if("BAKE" == endAction)
1480       {
1481         animDef.mDisconnectAction = Animation::BAKE;
1482       }
1483       else if("DISCARD" == endAction)
1484       {
1485         animDef.mDisconnectAction = Animation::DISCARD;
1486       }
1487       else if("BAKE_FINAL" == endAction)
1488       {
1489         animDef.mDisconnectAction = Animation::BAKE_FINAL;
1490       }
1491     }
1492
1493     if(const TreeNode* tnProperties = tnAnim.GetChild("properties"))
1494     {
1495       animDef.mProperties.reserve(tnProperties->Size());
1496       for(TreeNode::ConstIterator iProperty = tnProperties->CBegin(), iPropertyEnd = tnProperties->CEnd();
1497           iProperty != iPropertyEnd;
1498           ++iProperty)
1499       {
1500         const TreeNode& tnProperty = (*iProperty).second;
1501
1502         AnimatedProperty animProp;
1503         if(!ReadString(tnProperty.GetChild("node"), animProp.mNodeName))
1504         {
1505           mOnError(FormatString("Animation '%s': Failed to read the 'node' tag.", animDef.mName.c_str()));
1506           continue;
1507         }
1508
1509         if(!ReadString(tnProperty.GetChild("property"), animProp.mPropertyName))
1510         {
1511           mOnError(FormatString("Animation '%s': Failed to read the 'property' tag", animDef.mName.c_str()));
1512           continue;
1513         }
1514
1515         // these are the defaults
1516         animProp.mTimePeriod.delaySeconds    = 0.f;
1517         animProp.mTimePeriod.durationSeconds = animDef.mDuration;
1518         if(!ReadTimePeriod(tnProperty.GetChild("timePeriod"), animProp.mTimePeriod))
1519         {
1520           mOnError(FormatString("Animation '%s': timePeriod missing in Property #%d: defaulting to %f.",
1521                                 animDef.mName.c_str(),
1522                                 animDef.mProperties.size(),
1523                                 animProp.mTimePeriod.durationSeconds));
1524         }
1525
1526         std::string alphaFunctionValue;
1527         if(ReadString(tnProperty.GetChild("alphaFunction"), alphaFunctionValue))
1528         {
1529           animProp.mAlphaFunction = GetAlphaFunction(alphaFunctionValue);
1530         }
1531
1532         if(const TreeNode* tnKeyFramesBin = tnProperty.GetChild("keyFramesBin"))
1533         {
1534           DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1535
1536           std::ifstream binAniFile;
1537           std::string   animationFilename;
1538           if(ReadString(tnKeyFramesBin->GetChild(URL), animationFilename))
1539           {
1540             std::string animationFullPath = params.input.mAnimationsPath + animationFilename;
1541             binAniFile.open(animationFullPath, std::ios::binary);
1542             if(binAniFile.fail())
1543             {
1544               ExceptionFlinger(ASSERT_LOCATION) << "Failed to open animation data '" << animationFullPath << "'";
1545             }
1546           }
1547
1548           int byteOffset = 0;
1549           ReadInt(tnKeyFramesBin->GetChild("byteOffset"), byteOffset);
1550           DALI_ASSERT_ALWAYS(byteOffset >= 0);
1551
1552           binAniFile.seekg(byteOffset, std::ios::beg);
1553
1554           int numKeys = 0;
1555           ReadInt(tnKeyFramesBin->GetChild("numKeys"), numKeys);
1556           DALI_ASSERT_ALWAYS(numKeys >= 0);
1557
1558           animProp.mKeyFrames = KeyFrames::New();
1559
1560           // In binary animation file only is saved the position, rotation, scale and blend shape weight keys.
1561           // so, if it is vector3 we assume is position or scale keys, if it is vector4 we assume is rotation,
1562           //  otherwise are blend shape weight keys.
1563           //  TODO support for binary header with size information
1564           Property::Type propType = Property::FLOAT; // assume blend shape weights
1565           if(animProp.mPropertyName == "orientation")
1566           {
1567             propType = Property::VECTOR4;
1568           }
1569           else if((animProp.mPropertyName == "position") || (animProp.mPropertyName == "scale"))
1570           {
1571             propType = Property::VECTOR3;
1572           }
1573
1574           // alphafunction is reserved for future implementation
1575           //  NOTE: right now we're just using AlphaFunction::LINEAR.
1576           unsigned char dummyAlphaFunction;
1577
1578           float           progress;
1579           Property::Value propValue;
1580           for(int key = 0; key < numKeys; key++)
1581           {
1582             binAniFile.read(reinterpret_cast<char*>(&progress), sizeof(float));
1583             if(propType == Property::VECTOR3)
1584             {
1585               Vector3 value;
1586               binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 3);
1587               propValue = Property::Value(value);
1588             }
1589             else if(propType == Property::VECTOR4)
1590             {
1591               Vector4 value;
1592               binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 4);
1593               propValue = Property::Value(Quaternion(value));
1594             }
1595             else
1596             {
1597               float value;
1598               binAniFile.read(reinterpret_cast<char*>(&value), sizeof(float));
1599               propValue = Property::Value(value);
1600             }
1601
1602             binAniFile.read(reinterpret_cast<char*>(&dummyAlphaFunction), sizeof(unsigned char));
1603
1604             animProp.mKeyFrames.Add(progress, propValue, AlphaFunction::LINEAR);
1605           }
1606         }
1607         else if(const TreeNode* tnKeyFrames = tnProperty.GetChild("keyFrames"))
1608         {
1609           DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1610           animProp.mKeyFrames = KeyFrames::New();
1611
1612           float progress = 0.0f;
1613           for(auto i0 = tnKeyFrames->CBegin(), i1 = tnKeyFrames->CEnd(); i1 != i0; ++i0)
1614           {
1615             const TreeNode::KeyNodePair& kfKeyChild = *i0;
1616             bool                         readResult = ReadFloat(kfKeyChild.second.GetChild("progress"), progress);
1617             DALI_ASSERT_ALWAYS(readResult && "Key frame entry must have 'progress'");
1618
1619             const TreeNode* tnValue = kfKeyChild.second.GetChild("value");
1620             DALI_ASSERT_ALWAYS(tnValue && "Key frame entry must have 'value'");
1621
1622             // For the "orientation" property, convert from Vector4 -> Rotation value
1623             // This work-around is preferable to a null-pointer exception in the DALi update thread
1624             Property::Value propValue(ReadPropertyValue(*tnValue));
1625             if(propValue.GetType() == Property::VECTOR4 &&
1626                animProp.mPropertyName == "orientation")
1627             {
1628               Vector4 v;
1629               propValue.Get(v);
1630               propValue = Property::Value(Quaternion(v.w, v.x, v.y, v.z));
1631             }
1632
1633             AlphaFunction kfAlphaFunction(AlphaFunction::DEFAULT);
1634             std::string   alphaFuncStr;
1635             if(ReadString(kfKeyChild.second.GetChild("alphaFunction"), alphaFuncStr))
1636             {
1637               kfAlphaFunction = GetAlphaFunction(alphaFuncStr);
1638             }
1639
1640             animProp.mKeyFrames.Add(progress, propValue, kfAlphaFunction);
1641           }
1642         }
1643         else
1644         {
1645           const TreeNode* tnValue = tnProperty.GetChild("value");
1646           if(tnValue)
1647           {
1648             animProp.mValue.reset(new AnimatedProperty::Value{ReadPropertyValue(*tnValue)});
1649             ReadBool(tnProperty.GetChild("relative"), animProp.mValue->mIsRelative);
1650           }
1651           else
1652           {
1653             mOnError(FormatString("Property '%s' fails to define target value.",
1654                                   animProp.mPropertyName.c_str()));
1655           }
1656         }
1657
1658         animDef.mProperties.push_back(std::move(animProp));
1659       }
1660     }
1661
1662     if(overwrite)
1663     {
1664       *iFind = std::move(animDef);
1665     }
1666     else
1667     {
1668       iFind = definitions.insert(iFind, std::move(animDef));
1669     }
1670
1671     if(auto proc = params.input.mAnimationPropertyProcessor) // optional processing
1672     {
1673       Property::Map map;
1674       ParseProperties(tnAnim, map);
1675       proc(animDef, std::move(map), mOnError);
1676     }
1677   }
1678 }
1679
1680 void DliLoader::Impl::ParseAnimationGroups(const Toolkit::TreeNode* tnAnimationGroups, LoadParams& params)
1681 {
1682   auto& animGroups = params.output.mAnimationGroupDefinitions;
1683
1684   int numGroups = 0;
1685   for(auto iGroups = tnAnimationGroups->CBegin(), iGroupsEnd = tnAnimationGroups->CEnd();
1686       iGroups != iGroupsEnd;
1687       ++iGroups, ++numGroups)
1688   {
1689     const auto& tnGroup = *iGroups;
1690     auto        tnName  = tnGroup.second.GetChild(NAME);
1691     std::string groupName;
1692     if(!tnName || !ReadString(tnName, groupName))
1693     {
1694       mOnError(FormatString("Failed to get the name for the Animation group %d; ignoring.", numGroups));
1695       continue;
1696     }
1697
1698     auto iFind = std::lower_bound(animGroups.begin(), animGroups.end(), groupName, [](const AnimationGroupDefinition& group, const std::string& name)
1699                                   { return group.mName < name; });
1700     if(iFind != animGroups.end() && iFind->mName == groupName)
1701     {
1702       mOnError(FormatString("Animation group with name '%s' already exists; new entries will be merged.", groupName.c_str()));
1703     }
1704     else
1705     {
1706       iFind = animGroups.insert(iFind, AnimationGroupDefinition{});
1707     }
1708
1709     iFind->mName = groupName;
1710
1711     auto tnAnims = tnGroup.second.GetChild("animations");
1712     if(tnAnims && tnAnims->Size() > 0)
1713     {
1714       auto& anims = iFind->mAnimations;
1715       anims.reserve(anims.size() + tnAnims->Size());
1716       for(auto iAnims = tnAnims->CBegin(), iAnimsEnd = tnAnims->CEnd(); iAnims != iAnimsEnd; ++iAnims)
1717       {
1718         anims.push_back((*iAnims).second.GetString());
1719       }
1720     }
1721   }
1722 }
1723
1724 void DliLoader::Impl::GetCameraParameters(std::vector<CameraParameters>& cameras) const
1725 {
1726   if(mParser.GetRoot())
1727   {
1728     if(const TreeNode* jsonCameras = mParser.GetRoot()->GetChild("cameras"))
1729     {
1730       float dummyFloatArray[4];
1731
1732       cameras.resize(jsonCameras->Size());
1733       auto iCamera = cameras.begin();
1734       for(auto i0 = jsonCameras->CBegin(), i1 = jsonCameras->CEnd(); i0 != i1; ++i0)
1735       {
1736         auto& jsonCamera = (*i0).second;
1737
1738         ReadFloat(jsonCamera.GetChild("fov"), iCamera->yFov);
1739         ReadFloat(jsonCamera.GetChild("near"), iCamera->zNear);
1740         ReadFloat(jsonCamera.GetChild("far"), iCamera->zFar);
1741         if(ReadVector(jsonCamera.GetChild("orthographic"), dummyFloatArray, 4u))
1742         {
1743           iCamera->isPerspective = false;
1744
1745           iCamera->orthographicSize = dummyFloatArray[2] * 0.5f;
1746           iCamera->aspectRatio      = dummyFloatArray[1] / dummyFloatArray[2];
1747         }
1748
1749         if(auto jsonMatrix = jsonCamera.GetChild("matrix"))
1750         {
1751           ReadVector(jsonMatrix, iCamera->matrix.AsFloat(), 16u);
1752         }
1753
1754         ++iCamera;
1755       }
1756     }
1757   }
1758 }
1759
1760 void DliLoader::Impl::GetLightParameters(std::vector<LightParameters>& lights) const
1761 {
1762   if(mParser.GetRoot())
1763   {
1764     if(const TreeNode* jsonLights = mParser.GetRoot()->GetChild("lights"))
1765     {
1766       lights.resize(jsonLights->Size());
1767       auto iLight = lights.begin();
1768       for(auto i0 = jsonLights->CBegin(), i1 = jsonLights->CEnd(); i0 != i1; ++i0)
1769       {
1770         auto& jsonLight = (*i0).second;
1771         if(!ReadVector(jsonLight.GetChild("matrix"), iLight->transform.AsFloat(), 16))
1772         {
1773           mOnError(
1774             FormatString("Failed to parse light %d - \"matrix\" child with 16 floats expected.\n",
1775                          std::distance(jsonLights->CBegin(), i0)));
1776           continue;
1777         }
1778
1779         int shadowMapSize = 0;
1780         if(ReadInt(jsonLight.GetChild(SHADOW_MAP_SIZE), shadowMapSize) && shadowMapSize < 0)
1781         {
1782           mOnError(
1783             FormatString("Failed to parse light %d - %s has an invalid value.",
1784                          std::distance(jsonLights->CBegin(), i0),
1785                          SHADOW_MAP_SIZE));
1786           continue;
1787         }
1788         iLight->shadowMapSize = shadowMapSize;
1789
1790         float orthoSize = 0.f;
1791         if(ReadFloat(jsonLight.GetChild(ORTHOGRAPHIC_SIZE), orthoSize) &&
1792            (orthoSize < .0f || std::isnan(orthoSize) || std::isinf(orthoSize)))
1793         {
1794           mOnError(
1795             FormatString("Failed to parse light %d - %s has an invalid value.",
1796                          std::distance(jsonLights->CBegin(), i0),
1797                          ORTHOGRAPHIC_SIZE));
1798           continue;
1799         }
1800         iLight->orthographicSize = orthoSize;
1801
1802         if((iLight->shadowMapSize > 0) != (iLight->orthographicSize > .0f))
1803         {
1804           mOnError(FormatString(
1805             "Light %d: Both shadow map size and orthographic size must be set for shadows to work.",
1806             std::distance(jsonLights->CBegin(), i0)));
1807         }
1808
1809         if(!ReadVector(jsonLight.GetChild("color"), iLight->color.AsFloat(), 3)) // color is optional
1810         {
1811           iLight->color = Vector3::ONE; // default to white
1812         }
1813
1814         if(!ReadFloat(jsonLight.GetChild("intensity"), iLight->intensity)) // intensity is optional
1815         {
1816           iLight->intensity = 1.0f; // default to 1.0
1817         }
1818
1819         if(!ReadFloat(jsonLight.GetChild("shadowIntensity"), iLight->shadowIntensity)) // intensity is optional
1820         {
1821           iLight->shadowIntensity = 1.0f; // default to 1.0
1822         }
1823
1824         ++iLight;
1825       }
1826     }
1827   }
1828 }
1829
1830 } // namespace Loader
1831 } // namespace Scene3D
1832 } // namespace Dali