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