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