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