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