DALi Version 2.1.25
[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(ReadString(node.GetChild("occlusionMap"), texturePath))
1018     {
1019       ToUnixFileSeparators(texturePath);
1020       const auto semantic = MaterialDefinition::OCCLUSION;
1021       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1022       materialDef.mFlags |= semantic;
1023     }
1024
1025     if(ReadColorCodeOrColor(&node, materialDef.mColor, convertColorCode) &&
1026        materialDef.mColor.a < 1.0f)
1027     {
1028       materialDef.mFlags |= MaterialDefinition::TRANSPARENCY;
1029     }
1030
1031     ReadFloat(node.GetChild("metallic"), materialDef.mMetallic);
1032     ReadFloat(node.GetChild("roughness"), materialDef.mRoughness);
1033
1034     bool mipmaps;
1035     if(ReadBool(node.GetChild("mipmap"), mipmaps) && mipmaps)
1036     {
1037       for(auto& ts : materialDef.mTextureStages)
1038       {
1039         ts.mTexture.mSamplerFlags |= SamplerFlags::FILTER_MIPMAP_LINEAR;
1040       }
1041     }
1042
1043     resources.mMaterials.emplace_back(std::move(materialDef), TextureSet());
1044   }
1045 }
1046
1047 void DliLoader::Impl::ParseNodes(const TreeNode* const nodes, Index index, LoadParams& params)
1048 {
1049   std::vector<Index> parents;
1050   parents.reserve(8);
1051
1052   struct IndexMapper : IIndexMapper
1053   {
1054     IndexMapper(size_t numNodes)
1055     {
1056       mIndices.reserve(numNodes);
1057     }
1058
1059     virtual bool Map(Index iDli, Index iScene) override
1060     {
1061       Entry idx{iDli, iScene};
1062       auto  iInsert = std::lower_bound(mIndices.begin(), mIndices.end(), idx);
1063       if(iInsert == mIndices.end() || iInsert->iDli != iDli)
1064       {
1065         mIndices.insert(iInsert, idx);
1066       }
1067       else if(iInsert->iScene != iScene)
1068       {
1069         return false;
1070       }
1071       return true;
1072     }
1073
1074     virtual unsigned int Resolve(Index iDli) override
1075     {
1076       auto iFind = std::lower_bound(mIndices.begin(), mIndices.end(), iDli, [](const Entry& idx, Index iDli) {
1077         return idx.iDli < iDli;
1078       });
1079       DALI_ASSERT_ALWAYS(iFind != mIndices.end());
1080       return iFind->iScene;
1081     }
1082
1083   private:
1084     struct Entry
1085     {
1086       unsigned int iDli;
1087       unsigned int iScene;
1088
1089       bool operator<(const Entry& other) const
1090       {
1091         return iDli < other.iDli;
1092       }
1093     };
1094     std::vector<Entry> mIndices;
1095   } mapper(nodes->Size());
1096   ParseNodesInternal(nodes, index, parents, params, mapper);
1097
1098   auto& scene = params.output.mScene;
1099   for(size_t i0 = 0, i1 = scene.GetNodeCount(); i0 < i1; ++i0)
1100   {
1101     for(auto& c : scene.GetNode(i0)->mConstraints)
1102     {
1103       c.mSourceIdx = mapper.Resolve(c.mSourceIdx);
1104     }
1105   }
1106 }
1107
1108 void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index index, std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& mapper)
1109 {
1110   // Properties that may be resolved from a JSON value with ReadInt() -- or default to 0.
1111   struct IndexProperty
1112   {
1113     ResourceType::Value type;
1114     const TreeNode*     source;
1115     Index&              target;
1116   };
1117   std::vector<IndexProperty> resourceIds;
1118   resourceIds.reserve(4);
1119
1120   if(auto node = GetNthChild(nodes, index))
1121   {
1122     NodeDefinition nodeDef;
1123     nodeDef.mParentIdx = inOutParentStack.empty() ? INVALID_INDEX : inOutParentStack.back();
1124
1125     // name
1126     ReadString(node->GetChild(NAME), nodeDef.mName);
1127
1128     // transform
1129     ReadModelTransform(node, nodeDef.mOrientation, nodeDef.mPosition, nodeDef.mScale);
1130
1131     // Reads the size of the node.
1132     //
1133     // * It can be given as 'size' or 'bounds'.
1134     // * The sdk saves the 'size' as a vector2 in some cases.
1135     // * To avoid size related issues the following code attemps
1136     //   to read the 'size/bounds' as a vector3 first, if it's
1137     //   not successful then reads it as a vector2.
1138     ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 3) ||
1139       ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 2) ||
1140       ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 3) ||
1141       ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 2);
1142
1143     // visibility
1144     ReadBool(node->GetChild("visible"), nodeDef.mIsVisible);
1145
1146     // type classification
1147     if(auto eCustomization = node->GetChild("customization")) // customization
1148     {
1149       std::string tag;
1150       if(ReadString(eCustomization->GetChild("tag"), tag))
1151       {
1152         nodeDef.mCustomization.reset(new NodeDefinition::CustomizationDefinition{tag});
1153       }
1154     }
1155     else // something renderable maybe
1156     {
1157       std::unique_ptr<NodeDefinition::Renderable> renderable;
1158       ModelNode*                                  modelNode = nullptr; // no ownership, aliasing renderable for the right type.
1159
1160       const TreeNode* eRenderable = nullptr;
1161       if((eRenderable = node->GetChild("model")))
1162       {
1163         // check for mesh before allocating - this can't be missing.
1164         auto eMesh = eRenderable->GetChild("mesh");
1165         if(!eMesh)
1166         {
1167           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1168         }
1169
1170         modelNode = new ModelNode();
1171         renderable.reset(modelNode);
1172
1173         resourceIds.push_back({ResourceType::Mesh, eMesh, modelNode->mMeshIdx});
1174       }
1175       else if((eRenderable = node->GetChild("arc")))
1176       {
1177         // check for mesh before allocating - this can't be missing.
1178         auto eMesh = eRenderable->GetChild("mesh");
1179         if(!eMesh)
1180         {
1181           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1182         }
1183
1184         auto arcNode = new ArcNode;
1185         renderable.reset(arcNode);
1186         modelNode = arcNode;
1187
1188         resourceIds.push_back({ResourceType::Mesh, eMesh, arcNode->mMeshIdx});
1189
1190         ReadArcField(eRenderable, *arcNode);
1191       }
1192
1193       if(renderable) // process common properties of all renderables + register payload
1194       {
1195         // shader
1196         renderable->mShaderIdx = 0;
1197         auto eShader           = eRenderable->GetChild("shader");
1198         resourceIds.push_back({ResourceType::Shader, eShader, renderable->mShaderIdx});
1199
1200         // color
1201         if(modelNode)
1202         {
1203           modelNode->mMaterialIdx = 0; // must offer default of 0
1204           auto eMaterial          = eRenderable->GetChild("material");
1205           resourceIds.push_back({ResourceType::Material, eMaterial, modelNode->mMaterialIdx});
1206
1207           if(!ReadColorCodeOrColor(eRenderable, modelNode->mColor, params.input.mConvertColorCode))
1208           {
1209             ReadColorCodeOrColor(node, modelNode->mColor, params.input.mConvertColorCode);
1210           }
1211         }
1212
1213         nodeDef.mRenderable = std::move(renderable);
1214       }
1215     }
1216
1217     // Resolve ints - default to 0 if undefined
1218     auto& output = params.output;
1219     for(auto& idRes : resourceIds)
1220     {
1221       Index iCheck = 0;
1222       switch(idRes.type)
1223       {
1224         case ResourceType::Shader:
1225           iCheck = output.mResources.mShaders.size();
1226           break;
1227
1228         case ResourceType::Mesh:
1229           iCheck = output.mResources.mMeshes.size();
1230           break;
1231
1232         case ResourceType::Material:
1233           iCheck = output.mResources.mMaterials.size();
1234           break;
1235
1236         default:
1237           ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid resource type: " << idRes.type << " (Programmer error)";
1238       }
1239
1240       if(!idRes.source)
1241       {
1242         idRes.target = 0;
1243       }
1244       else if(idRes.source->GetType() != TreeNode::INTEGER)
1245       {
1246         ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid " << GetResourceTypeName(idRes.type) << " index type.";
1247       }
1248       else
1249       {
1250         idRes.target = idRes.source->GetInteger();
1251       }
1252
1253       if(idRes.target >= iCheck)
1254       {
1255         ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": " << GetResourceTypeName(idRes.type) << " index " << idRes.target << " out of bounds (" << iCheck << ").";
1256       }
1257     }
1258     resourceIds.clear();
1259
1260     // Extra properties
1261     if(auto eExtras = node->GetChild("extras"))
1262     {
1263       auto& extras = nodeDef.mExtras;
1264       extras.reserve(eExtras->Size());
1265
1266       NodeDefinition::Extra e;
1267       for(auto i0 = eExtras->CBegin(), i1 = eExtras->CEnd(); i0 != i1; ++i0)
1268       {
1269         auto eExtra = *i0;
1270         e.mKey      = eExtra.first;
1271         if(e.mKey.empty())
1272         {
1273           mOnError(FormatString("node %d: empty string is invalid for name of extra %d; ignored.",
1274                                 index,
1275                                 extras.size()));
1276           continue;
1277         }
1278
1279         e.mValue = ReadPropertyValue(eExtra.second);
1280         if(e.mValue.GetType() == Property::Type::NONE)
1281         {
1282           mOnError(FormatString("node %d: failed to interpret value of extra '%s' : %s; ignored.",
1283                                 index,
1284                                 e.mKey.c_str(),
1285                                 eExtra.second.GetString()));
1286         }
1287         else
1288         {
1289           auto iInsert = std::lower_bound(extras.begin(), extras.end(), e);
1290           if(iInsert != extras.end() && iInsert->mKey == e.mKey)
1291           {
1292             mOnError(FormatString("node %d: extra '%s' already defined; overriding with %s.",
1293                                   index,
1294                                   e.mKey.c_str(),
1295                                   eExtra.second.GetString()));
1296             *iInsert = std::move(e);
1297           }
1298           else
1299           {
1300             extras.insert(iInsert, e);
1301           }
1302         }
1303       }
1304     }
1305
1306     // Constraints
1307     if(auto eConstraints = node->GetChild("constraints"))
1308     {
1309       auto& constraints = nodeDef.mConstraints;
1310       constraints.reserve(eConstraints->Size());
1311
1312       ConstraintDefinition cDef;
1313       for(auto i0 = eConstraints->CBegin(), i1 = eConstraints->CEnd(); i0 != i1; ++i0)
1314       {
1315         auto eConstraint = *i0;
1316         if(!ReadIndex(&eConstraint.second, cDef.mSourceIdx))
1317         {
1318           mOnError(FormatString("node %d: node ID %s for constraint %d is invalid; ignored.",
1319                                 index,
1320                                 eConstraint.second.GetString(),
1321                                 constraints.size()));
1322         }
1323         else
1324         {
1325           cDef.mProperty = eConstraint.first;
1326
1327           auto iInsert = std::lower_bound(constraints.begin(), constraints.end(), cDef);
1328           if(iInsert != constraints.end() && *iInsert == cDef)
1329           {
1330             mOnError(FormatString("node %d: constraint %s@%d already defined; ignoring.",
1331                                   index,
1332                                   cDef.mProperty.c_str(),
1333                                   cDef.mSourceIdx));
1334           }
1335           else
1336           {
1337             constraints.insert(iInsert, cDef);
1338           }
1339         }
1340       }
1341     }
1342
1343     // Determine index for mapping
1344     const unsigned int myIndex = output.mScene.GetNodeCount();
1345     if(!mapper.Map(index, myIndex))
1346     {
1347       mOnError(FormatString("node %d: error mapping dli index %d: node has multiple parents. Ignoring subtree."));
1348       return;
1349     }
1350
1351     // if the node is a bone in a skeletal animation, it will have the inverse bind pose matrix.
1352     Matrix invBindMatrix{false};
1353     if(ReadVector(node->GetChild("inverseBindPoseMatrix"), invBindMatrix.AsFloat(), 16u)) // TODO: more robust error checking?
1354     {
1355       mInverseBindMatrices[myIndex] = invBindMatrix;
1356     }
1357
1358     // Register nodeDef
1359     auto rawDef = output.mScene.AddNode(std::make_unique<NodeDefinition>(std::move(nodeDef)));
1360     if(rawDef) // NOTE: no ownership. Guaranteed to stay in scope.
1361     {
1362       // ...And only then parse children.
1363       if(auto children = node->GetChild("children"))
1364       {
1365         inOutParentStack.push_back(myIndex);
1366
1367         rawDef->mChildren.reserve(children->Size());
1368
1369         uint32_t iChild = 0;
1370         for(auto j0 = children->CBegin(), j1 = children->CEnd(); j0 != j1; ++j0, ++iChild)
1371         {
1372           auto& child = (*j0).second;
1373           if(child.GetType() == TreeNode::INTEGER)
1374           {
1375             ParseNodesInternal(nodes, child.GetInteger(), inOutParentStack, params, mapper); // child object is created in scene definition.
1376           }
1377           else
1378           {
1379             ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ", child " << iChild << ": invalid index type.";
1380           }
1381         }
1382
1383         inOutParentStack.pop_back();
1384       }
1385       else if(rawDef->mCustomization)
1386       {
1387         mOnError(FormatString("node %d: not an actual customization without children.", index));
1388       }
1389
1390       if(auto proc = params.input.mNodePropertyProcessor) // optional processing
1391       {
1392         // WARNING: constraint IDs are not resolved at this point.
1393         Property::Map nodeData;
1394         ParseProperties(*node, nodeData);
1395         proc(*rawDef, std::move(nodeData), mOnError);
1396       }
1397     }
1398     else
1399     {
1400       ExceptionFlinger(ASSERT_LOCATION) << "Node " << index << ": name already used.";
1401     }
1402   }
1403 }
1404
1405 void DliLoader::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams& params)
1406 {
1407   auto& definitions = params.output.mAnimationDefinitions;
1408   definitions.reserve(definitions.size() + tnAnimations->Size());
1409
1410   for(TreeNode::ConstIterator iAnim = tnAnimations->CBegin(), iAnimEnd = tnAnimations->CEnd();
1411       iAnim != iAnimEnd;
1412       ++iAnim)
1413   {
1414     const TreeNode&     tnAnim = (*iAnim).second;
1415     AnimationDefinition animDef;
1416     ReadString(tnAnim.GetChild(NAME), animDef.mName);
1417
1418     auto       iFind     = std::lower_bound(definitions.begin(), definitions.end(), animDef, [](const AnimationDefinition& ad0, const AnimationDefinition& ad1) {
1419       return ad0.mName < ad1.mName;
1420     });
1421     const bool overwrite = iFind != definitions.end() && iFind->mName == animDef.mName;
1422     if(overwrite)
1423     {
1424       mOnError(FormatString("Pre-existing animation with name '%s' is being overwritten.", animDef.mName.c_str()));
1425     }
1426
1427     // Duration -- We need something that animated properties' delay / duration can
1428     // be expressed as a multiple of; 0 won't work. This is small enough (i.e. shorter
1429     // than our frame delay) to not be restrictive WRT replaying. If anything needs
1430     // to occur more frequently, then Animations are likely not your solution anyway.
1431     animDef.mDuration = AnimationDefinition::MIN_DURATION_SECONDS;
1432     if(!ReadFloat(tnAnim.GetChild("duration"), animDef.mDuration))
1433     {
1434       mOnError(FormatString("Animation '%s' fails to define '%s', defaulting to %f.",
1435                             animDef.mName.c_str(),
1436                             "duration",
1437                             animDef.mDuration));
1438     }
1439
1440     // Get loop count - # of playbacks. Default is once. 0 means repeat indefinitely.
1441     animDef.mLoopCount = 1;
1442     if(ReadInt(tnAnim.GetChild("loopCount"), animDef.mLoopCount) &&
1443        animDef.mLoopCount < 0)
1444     {
1445       animDef.mLoopCount = 0;
1446     }
1447
1448     std::string endAction;
1449     if(ReadString(tnAnim.GetChild("endAction"), endAction))
1450     {
1451       if("BAKE" == endAction)
1452       {
1453         animDef.mEndAction = Animation::BAKE;
1454       }
1455       else if("DISCARD" == endAction)
1456       {
1457         animDef.mEndAction = Animation::DISCARD;
1458       }
1459       else if("BAKE_FINAL" == endAction)
1460       {
1461         animDef.mEndAction = Animation::BAKE_FINAL;
1462       }
1463     }
1464
1465     if(ReadString(tnAnim.GetChild("disconnectAction"), endAction))
1466     {
1467       if("BAKE" == endAction)
1468       {
1469         animDef.mDisconnectAction = Animation::BAKE;
1470       }
1471       else if("DISCARD" == endAction)
1472       {
1473         animDef.mDisconnectAction = Animation::DISCARD;
1474       }
1475       else if("BAKE_FINAL" == endAction)
1476       {
1477         animDef.mDisconnectAction = Animation::BAKE_FINAL;
1478       }
1479     }
1480
1481     if(const TreeNode* tnProperties = tnAnim.GetChild("properties"))
1482     {
1483       animDef.mProperties.reserve(tnProperties->Size());
1484       for(TreeNode::ConstIterator iProperty = tnProperties->CBegin(), iPropertyEnd = tnProperties->CEnd();
1485           iProperty != iPropertyEnd;
1486           ++iProperty)
1487       {
1488         const TreeNode& tnProperty = (*iProperty).second;
1489
1490         AnimatedProperty animProp;
1491         if(!ReadString(tnProperty.GetChild("node"), animProp.mNodeName))
1492         {
1493           mOnError(FormatString("Animation '%s': Failed to read the 'node' tag.", animDef.mName.c_str()));
1494           continue;
1495         }
1496
1497         if(!ReadString(tnProperty.GetChild("property"), animProp.mPropertyName))
1498         {
1499           mOnError(FormatString("Animation '%s': Failed to read the 'property' tag", animDef.mName.c_str()));
1500           continue;
1501         }
1502
1503         // these are the defaults
1504         animProp.mTimePeriod.delaySeconds    = 0.f;
1505         animProp.mTimePeriod.durationSeconds = animDef.mDuration;
1506         if(!ReadTimePeriod(tnProperty.GetChild("timePeriod"), animProp.mTimePeriod))
1507         {
1508           mOnError(FormatString("Animation '%s': timePeriod missing in Property #%d: defaulting to %f.",
1509                                 animDef.mName.c_str(),
1510                                 animDef.mProperties.size(),
1511                                 animProp.mTimePeriod.durationSeconds));
1512         }
1513
1514         std::string alphaFunctionValue;
1515         if(ReadString(tnProperty.GetChild("alphaFunction"), alphaFunctionValue))
1516         {
1517           animProp.mAlphaFunction = GetAlphaFunction(alphaFunctionValue);
1518         }
1519
1520         if(const TreeNode* tnKeyFramesBin = tnProperty.GetChild("keyFramesBin"))
1521         {
1522           DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1523
1524           std::ifstream binAniFile;
1525           std::string   animationFilename;
1526           if(ReadString(tnKeyFramesBin->GetChild(URL), animationFilename))
1527           {
1528             std::string animationFullPath = params.input.mAnimationsPath + animationFilename;
1529             binAniFile.open(animationFullPath, std::ios::binary);
1530             if(binAniFile.fail())
1531             {
1532               ExceptionFlinger(ASSERT_LOCATION) << "Failed to open animation data '" << animationFullPath << "'";
1533             }
1534           }
1535
1536           int byteOffset = 0;
1537           ReadInt(tnKeyFramesBin->GetChild("byteOffset"), byteOffset);
1538           DALI_ASSERT_ALWAYS(byteOffset >= 0);
1539
1540           binAniFile.seekg(byteOffset, std::ios::beg);
1541
1542           int numKeys = 0;
1543           ReadInt(tnKeyFramesBin->GetChild("numKeys"), numKeys);
1544           DALI_ASSERT_ALWAYS(numKeys >= 0);
1545
1546           animProp.mKeyFrames = KeyFrames::New();
1547
1548           //In binary animation file only is saved the position, rotation, scale and blend shape weight keys.
1549           //so, if it is vector3 we assume is position or scale keys, if it is vector4 we assume is rotation,
1550           // otherwise are blend shape weight keys.
1551           // TODO support for binary header with size information
1552           Property::Type propType = Property::FLOAT; // assume blend shape weights
1553           if(animProp.mPropertyName == "orientation")
1554           {
1555             propType = Property::VECTOR4;
1556           }
1557           else if((animProp.mPropertyName == "position") || (animProp.mPropertyName == "scale"))
1558           {
1559             propType = Property::VECTOR3;
1560           }
1561
1562           //alphafunction is reserved for future implementation
1563           // NOTE: right now we're just using AlphaFunction::LINEAR.
1564           unsigned char dummyAlphaFunction;
1565
1566           float           progress;
1567           Property::Value propValue;
1568           for(int key = 0; key < numKeys; key++)
1569           {
1570             binAniFile.read(reinterpret_cast<char*>(&progress), sizeof(float));
1571             if(propType == Property::VECTOR3)
1572             {
1573               Vector3 value;
1574               binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 3);
1575               propValue = Property::Value(value);
1576             }
1577             else if(propType == Property::VECTOR4)
1578             {
1579               Vector4 value;
1580               binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 4);
1581               propValue = Property::Value(Quaternion(value));
1582             }
1583             else
1584             {
1585               float value;
1586               binAniFile.read(reinterpret_cast<char*>(&value), sizeof(float));
1587               propValue = Property::Value(value);
1588             }
1589
1590             binAniFile.read(reinterpret_cast<char*>(&dummyAlphaFunction), sizeof(unsigned char));
1591
1592             animProp.mKeyFrames.Add(progress, propValue, AlphaFunction::LINEAR);
1593           }
1594         }
1595         else if(const TreeNode* tnKeyFrames = tnProperty.GetChild("keyFrames"))
1596         {
1597           DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1598           animProp.mKeyFrames = KeyFrames::New();
1599
1600           float progress = 0.0f;
1601           for(auto i0 = tnKeyFrames->CBegin(), i1 = tnKeyFrames->CEnd(); i1 != i0; ++i0)
1602           {
1603             const TreeNode::KeyNodePair& kfKeyChild = *i0;
1604             bool                         readResult = ReadFloat(kfKeyChild.second.GetChild("progress"), progress);
1605             DALI_ASSERT_ALWAYS(readResult && "Key frame entry must have 'progress'");
1606
1607             const TreeNode* tnValue = kfKeyChild.second.GetChild("value");
1608             DALI_ASSERT_ALWAYS(tnValue && "Key frame entry must have 'value'");
1609
1610             // For the "orientation" property, convert from Vector4 -> Rotation value
1611             // This work-around is preferable to a null-pointer exception in the DALi update thread
1612             Property::Value propValue(ReadPropertyValue(*tnValue));
1613             if(propValue.GetType() == Property::VECTOR4 &&
1614                animProp.mPropertyName == "orientation")
1615             {
1616               Vector4 v;
1617               propValue.Get(v);
1618               propValue = Property::Value(Quaternion(v.w, v.x, v.y, v.z));
1619             }
1620
1621             AlphaFunction kfAlphaFunction(AlphaFunction::DEFAULT);
1622             std::string   alphaFuncStr;
1623             if(ReadString(kfKeyChild.second.GetChild("alphaFunction"), alphaFuncStr))
1624             {
1625               kfAlphaFunction = GetAlphaFunction(alphaFuncStr);
1626             }
1627
1628             animProp.mKeyFrames.Add(progress, propValue, kfAlphaFunction);
1629           }
1630         }
1631         else
1632         {
1633           const TreeNode* tnValue = tnProperty.GetChild("value");
1634           if(tnValue)
1635           {
1636             animProp.mValue.reset(new AnimatedProperty::Value{ReadPropertyValue(*tnValue)});
1637             ReadBool(tnProperty.GetChild("relative"), animProp.mValue->mIsRelative);
1638           }
1639           else
1640           {
1641             mOnError(FormatString("Property '%s' fails to define target value.",
1642                                   animProp.mPropertyName.c_str()));
1643           }
1644         }
1645
1646         animDef.mProperties.push_back(std::move(animProp));
1647       }
1648     }
1649
1650     if(overwrite)
1651     {
1652       *iFind = std::move(animDef);
1653     }
1654     else
1655     {
1656       iFind = definitions.insert(iFind, std::move(animDef));
1657     }
1658
1659     if(auto proc = params.input.mAnimationPropertyProcessor) // optional processing
1660     {
1661       Property::Map map;
1662       ParseProperties(tnAnim, map);
1663       proc(animDef, std::move(map), mOnError);
1664     }
1665   }
1666 }
1667
1668 void DliLoader::Impl::ParseAnimationGroups(const Toolkit::TreeNode* tnAnimationGroups, LoadParams& params)
1669 {
1670   auto& animGroups = params.output.mAnimationGroupDefinitions;
1671
1672   int numGroups = 0;
1673   for(auto iGroups = tnAnimationGroups->CBegin(), iGroupsEnd = tnAnimationGroups->CEnd();
1674       iGroups != iGroupsEnd;
1675       ++iGroups, ++numGroups)
1676   {
1677     const auto& tnGroup = *iGroups;
1678     auto        tnName  = tnGroup.second.GetChild(NAME);
1679     std::string groupName;
1680     if(!tnName || !ReadString(tnName, groupName))
1681     {
1682       mOnError(FormatString("Failed to get the name for the Animation group %d; ignoring.", numGroups));
1683       continue;
1684     }
1685
1686     auto iFind = std::lower_bound(animGroups.begin(), animGroups.end(), groupName, [](const AnimationGroupDefinition& group, const std::string& name) {
1687       return group.mName < name;
1688     });
1689     if(iFind != animGroups.end() && iFind->mName == groupName)
1690     {
1691       mOnError(FormatString("Animation group with name '%s' already exists; new entries will be merged.", groupName.c_str()));
1692     }
1693     else
1694     {
1695       iFind = animGroups.insert(iFind, AnimationGroupDefinition{});
1696     }
1697
1698     iFind->mName = groupName;
1699
1700     auto tnAnims = tnGroup.second.GetChild("animations");
1701     if(tnAnims && tnAnims->Size() > 0)
1702     {
1703       auto& anims = iFind->mAnimations;
1704       anims.reserve(anims.size() + tnAnims->Size());
1705       for(auto iAnims = tnAnims->CBegin(), iAnimsEnd = tnAnims->CEnd(); iAnims != iAnimsEnd; ++iAnims)
1706       {
1707         anims.push_back((*iAnims).second.GetString());
1708       }
1709     }
1710   }
1711 }
1712
1713 void DliLoader::Impl::GetCameraParameters(std::vector<CameraParameters>& cameras) const
1714 {
1715   if(const TreeNode* jsonCameras = mParser.GetRoot()->GetChild("cameras"))
1716   {
1717     cameras.resize(jsonCameras->Size());
1718     auto iCamera = cameras.begin();
1719     for(auto i0 = jsonCameras->CBegin(), i1 = jsonCameras->CEnd(); i0 != i1; ++i0)
1720     {
1721       auto& jsonCamera = (*i0).second;
1722
1723       ReadFloat(jsonCamera.GetChild("fov"), iCamera->yFov);
1724       ReadFloat(jsonCamera.GetChild("near"), iCamera->zNear);
1725       ReadFloat(jsonCamera.GetChild("far"), iCamera->zFar);
1726       if(ReadVector(jsonCamera.GetChild("orthographic"), iCamera->orthographicSize.AsFloat(), 4u))
1727       {
1728         iCamera->isPerspective = false;
1729       }
1730
1731       if(auto jsonMatrix = jsonCamera.GetChild("matrix"))
1732       {
1733         ReadVector(jsonMatrix, iCamera->matrix.AsFloat(), 16u);
1734       }
1735
1736       ++iCamera;
1737     }
1738   }
1739 }
1740
1741 void DliLoader::Impl::GetLightParameters(std::vector<LightParameters>& lights) const
1742 {
1743   if(const TreeNode* jsonLights = mParser.GetRoot()->GetChild("lights"))
1744   {
1745     lights.resize(jsonLights->Size());
1746     auto iLight = lights.begin();
1747     for(auto i0 = jsonLights->CBegin(), i1 = jsonLights->CEnd(); i0 != i1; ++i0)
1748     {
1749       auto& jsonLight = (*i0).second;
1750       if(!ReadVector(jsonLight.GetChild("matrix"), iLight->transform.AsFloat(), 16))
1751       {
1752         mOnError(
1753           FormatString("Failed to parse light %d - \"matrix\" child with 16 floats expected.\n",
1754                        std::distance(jsonLights->CBegin(), i0)));
1755         continue;
1756       }
1757
1758       int shadowMapSize = 0;
1759       if(ReadInt(jsonLight.GetChild(SHADOW_MAP_SIZE), shadowMapSize) && shadowMapSize < 0)
1760       {
1761         mOnError(
1762           FormatString("Failed to parse light %d - %s has an invalid value.",
1763                        std::distance(jsonLights->CBegin(), i0),
1764                        SHADOW_MAP_SIZE));
1765         continue;
1766       }
1767       iLight->shadowMapSize = shadowMapSize;
1768
1769       float orthoSize = 0.f;
1770       if(ReadFloat(jsonLight.GetChild(ORTHOGRAPHIC_SIZE), orthoSize) &&
1771          (orthoSize < .0f || std::isnan(orthoSize) || std::isinf(orthoSize)))
1772       {
1773         mOnError(
1774           FormatString("Failed to parse light %d - %s has an invalid value.",
1775                        std::distance(jsonLights->CBegin(), i0),
1776                        ORTHOGRAPHIC_SIZE));
1777         continue;
1778       }
1779       iLight->orthographicSize = orthoSize;
1780
1781       if((iLight->shadowMapSize > 0) != (iLight->orthographicSize > .0f))
1782       {
1783         mOnError(FormatString(
1784           "Light %d: Both shadow map size and orthographic size must be set for shadows to work.",
1785           std::distance(jsonLights->CBegin(), i0)));
1786       }
1787
1788       if(!ReadVector(jsonLight.GetChild("color"), iLight->color.AsFloat(), 3)) // color is optional
1789       {
1790         iLight->color = Vector3::ONE; // default to white
1791       }
1792
1793       if(!ReadFloat(jsonLight.GetChild("intensity"), iLight->intensity)) // intensity is optional
1794       {
1795         iLight->intensity = 1.0f; // default to 1.0
1796       }
1797
1798       if(!ReadFloat(jsonLight.GetChild("shadowIntensity"), iLight->shadowIntensity)) // intensity is optional
1799       {
1800         iLight->shadowIntensity = 1.0f; // default to 1.0
1801       }
1802
1803       ++iLight;
1804     }
1805   }
1806 }
1807
1808 } // namespace SceneLoader
1809 } // namespace Dali