[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / loader / dli-loader-impl.cpp
1 /*
2  * Copyright (c) 2024 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) { return (item == "SSS"); });
671     if(sssIter != shaderDef.mDefines.end())
672     {
673       shaderDef.mDefines.erase(sssIter);
674     }
675
676     // Read shader hints. Possible values are:
677     //                         Don't define for No hints.
678     // "OUTPUT_IS_TRANSPARENT" Might generate transparent alpha from opaque inputs.
679     //     "MODIFIES_GEOMETRY" Might change position of vertices, this option disables any culling optimizations.
680
681     ReadStringVector(node.GetChild(HINTS), shaderDef.mHints);
682
683     if(ReadString(node.GetChild("vertex"), shaderDef.mVertexShaderPath) &&
684        ReadString(node.GetChild("fragment"), shaderDef.mFragmentShaderPath))
685     {
686       ToUnixFileSeparators(shaderDef.mVertexShaderPath);
687       ToUnixFileSeparators(shaderDef.mFragmentShaderPath);
688
689       for(TreeNode::ConstIterator j0 = node.CBegin(), j1 = node.CEnd(); j0 != j1; ++j0)
690       {
691         const TreeNode::KeyNodePair& keyValue = *j0;
692         const std::string&           key      = keyValue.first;
693         const TreeNode&              value    = keyValue.second;
694
695         Property::Value uniformValue;
696         if(key.compare("vertex") == 0 || key.compare("fragment") == 0 || key.compare("defines") == 0 || key.compare(HINTS) == 0)
697         {
698           continue;
699         }
700         else if(key.compare("rendererState") == 0)
701         {
702           shaderDef.mRendererState = ReadRendererState(keyValue.second);
703         }
704         else if(value.GetType() == TreeNode::INTEGER || value.GetType() == TreeNode::FLOAT)
705         {
706           float f = 0.f;
707           ReadFloat(&value, f);
708           uniformValue = f;
709         }
710         else if(value.GetType() == TreeNode::BOOLEAN)
711         {
712           DALI_LOG_WARNING("\"bool\" uniforms are handled as floats in shader");
713           bool value = false;
714           if(ReadBool(&keyValue.second, value))
715           {
716             uniformValue = value ? 1.0f : 0.0f;
717           }
718         }
719         else
720           switch(auto size = GetNumericalArraySize(&value))
721           {
722             case 16:
723             {
724               Matrix m;
725               ReadVector(&value, m.AsFloat(), size);
726               uniformValue = m;
727               break;
728             }
729
730             case 9:
731             {
732               Matrix3 m;
733               ReadVector(&value, m.AsFloat(), size);
734               uniformValue = m;
735               break;
736             }
737
738             case 4:
739             {
740               Vector4 v;
741               ReadVector(&value, v.AsFloat(), size);
742               uniformValue = v;
743               break;
744             }
745
746             case 3:
747             {
748               Vector3 v;
749               ReadVector(&value, v.AsFloat(), size);
750               uniformValue = v;
751               break;
752             }
753
754             case 2:
755             {
756               Vector2 v;
757               ReadVector(&value, v.AsFloat(), size);
758               uniformValue = v;
759               break;
760             }
761
762             default:
763               mOnError(FormatString(
764                 "shader %u: Ignoring uniform '%s': failed to infer type from %zu elements.",
765                 iShader,
766                 key.c_str(),
767                 size));
768               break;
769           }
770
771         if(Property::NONE != uniformValue.GetType())
772         {
773           shaderDef.mUniforms.Insert(key, uniformValue);
774         }
775       }
776
777       resources.mShaders.emplace_back(std::move(shaderDef), Shader());
778     }
779     else
780     {
781       ExceptionFlinger(ASSERT_LOCATION) << "shader " << iShader << ": Missing vertex / fragment shader definition.";
782     }
783   }
784 }
785
786 void DliLoaderImpl::Impl::ParseMeshes(const TreeNode* meshes, Dali::Scene3D::Loader::ResourceBundle& resources)
787 {
788   for(auto i0 = meshes->CBegin(), i1 = meshes->CEnd(); i0 != i1; ++i0)
789   {
790     auto& node = (*i0).second;
791
792     MeshDefinition meshDef;
793     if(!ReadString(node.GetChild(URI), meshDef.mUri))
794     {
795       ExceptionFlinger(ASSERT_LOCATION) << "mesh " << resources.mMeshes.size() << ": Missing required attribute '" << URI << "'.";
796     }
797
798     ToUnixFileSeparators(meshDef.mUri);
799
800     std::string primitive;
801     if(ReadString(node.GetChild("primitive"), primitive))
802     {
803       if(primitive == "LINES")
804       {
805         meshDef.mPrimitiveType = Geometry::LINES;
806       }
807       else if(primitive == "POINTS")
808       {
809         meshDef.mPrimitiveType = Geometry::POINTS;
810       }
811       else if(primitive != "TRIANGLES")
812       {
813         mOnError(FormatString(
814           "mesh %d: Using TRIANGLES instead of unsupported primitive type '%s'.",
815           resources.mMeshes.size(),
816           primitive.c_str()));
817       }
818     }
819
820     int attributes;
821     if(ReadInt(node.GetChild("attributes"), attributes))
822     {
823       if(MaskMatch(attributes, MeshDefinition::INDICES) &&
824          !ReadAttribAccessor(node.GetChild("indices"), meshDef.mIndices))
825       {
826         ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
827                                                           resources.mMeshes.size(),
828                                                           "indices");
829       }
830
831       if(MaskMatch(attributes, MeshDefinition::POSITIONS) &&
832          !ReadAttribAccessor(node.GetChild("positions"), meshDef.mPositions))
833       {
834         ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
835                                                           resources.mMeshes.size(),
836                                                           "positions");
837       }
838
839       if(MaskMatch(attributes, MeshDefinition::NORMALS) &&
840          !ReadAttribAccessor(node.GetChild("normals"), meshDef.mNormals))
841       {
842         mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "normals"));
843       }
844
845       if(MaskMatch(attributes, MeshDefinition::TEX_COORDS))
846       {
847         meshDef.mTexCoords.emplace_back(MeshDefinition::Accessor{});
848         if(!ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords[0]))
849         {
850           mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "textures"));
851         }
852       }
853
854       if(MaskMatch(attributes, MeshDefinition::TANGENTS) &&
855          !ReadAttribAccessor(node.GetChild("tangents"), meshDef.mTangents))
856       {
857         mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(), "tangents"));
858       }
859
860       // NOTE: we're no longer reading bitangents as these are calculated in the shaders.
861       if(ReadIndex(node.GetChild("skeleton"), meshDef.mSkeletonIdx))
862       {
863         if(!MaskMatch(attributes, MeshDefinition::JOINTS_0) &&
864            !MaskMatch(attributes, MeshDefinition::WEIGHTS_0))
865         {
866           mOnError(FormatString("mesh %d: Expected joints0 / weights0 attribute(s) missing.",
867                                 resources.mMeshes.size()));
868         }
869         else
870         {
871           meshDef.mJoints.emplace_back(MeshDefinition::Accessor{});
872           meshDef.mWeights.emplace_back(MeshDefinition::Accessor{});
873           if(!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints[0]) ||
874              !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights[0]))
875           {
876             mOnError(FormatString("mesh %d: Failed to read skinning information.",
877                                   resources.mMeshes.size()));
878           }
879         }
880       }
881
882       if(auto blendshapeHeader = node.GetChild(BLEND_SHAPE_HEADER))
883       {
884         std::string blendShapeVersion;
885         ReadString(blendshapeHeader->GetChild(VERSION), blendShapeVersion);
886
887         if(0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_1_0))
888         {
889           meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_1_0;
890         }
891         else if(0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_2_0))
892         {
893           meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
894         }
895
896         switch(meshDef.mBlendShapeVersion)
897         {
898           case BlendShapes::Version::VERSION_1_0:
899           case BlendShapes::Version::VERSION_2_0: // FALL THROUGH
900           {
901             ReadAttribBlob(blendshapeHeader, meshDef.mBlendShapeHeader);
902             break;
903           }
904           default:
905           {
906             // nothing to do
907             break;
908           }
909         }
910       }
911
912       if(auto blendShapes = node.GetChild(BLEND_SHAPES))
913       {
914         meshDef.mBlendShapes.resize(blendShapes->Size());
915
916         auto index = 0u;
917         for(auto it = blendShapes->CBegin(), endIt = blendShapes->CEnd(); it != endIt; ++it, ++index)
918         {
919           // Each blend shape is stored as the difference with the original mesh.
920
921           auto& blendShapeNode = (*it).second;
922
923           auto& blendShape = meshDef.mBlendShapes[index];
924           ReadString(blendShapeNode.GetChild("name"), blendShape.name);
925           if(auto position = blendShapeNode.GetChild("positions"))
926           {
927             ReadAttribAccessor(position, blendShape.deltas);
928           }
929           if(auto normals = blendShapeNode.GetChild("normals"))
930           {
931             ReadAttribAccessor(normals, blendShape.normals);
932           }
933           if(auto tangents = blendShapeNode.GetChild("tangents"))
934           {
935             ReadAttribAccessor(tangents, blendShape.tangents);
936           }
937           ReadFloat(blendShapeNode.GetChild("weight"), blendShape.weight);
938         }
939       }
940
941       bool flipV;
942       if(ReadBool(node.GetChild("flipV"), flipV))
943       {
944         meshDef.mFlags |= flipV * MeshDefinition::FLIP_UVS_VERTICAL;
945       }
946
947       resources.mMeshes.emplace_back(std::move(meshDef), MeshGeometry());
948     }
949   }
950 }
951
952 void DliLoaderImpl::Impl::ParseMaterials(const TreeNode* materials, DliInputParameter::ConvertColorCode convertColorCode, Dali::Scene3D::Loader::ResourceBundle& resources)
953 {
954   for(auto i0 = materials->CBegin(), i1 = materials->CEnd(); i0 != i1; ++i0)
955   {
956     auto& node = (*i0).second;
957
958     MaterialDefinition materialDef;
959     if(auto eEnvironment = node.GetChild("environment"))
960     {
961       ReadIndex(eEnvironment, materialDef.mEnvironmentIdx);
962       if(static_cast<unsigned int>(materialDef.mEnvironmentIdx) >= resources.mEnvironmentMaps.size())
963       {
964         ExceptionFlinger(ASSERT_LOCATION) << "material " << resources.mMaterials.size() << ": Environment index " << materialDef.mEnvironmentIdx << " out of bounds (" << resources.mEnvironmentMaps.size() << ").";
965       }
966     }
967
968     // TODO : need to consider AGIF
969     std::vector<std::string> texturePaths;
970     std::string              texturePath;
971     if(ReadString(node.GetChild("albedoMap"), texturePath))
972     {
973       ToUnixFileSeparators(texturePath);
974       const auto semantic = MaterialDefinition::ALBEDO;
975       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
976       materialDef.mFlags |= semantic | MaterialDefinition::TRANSPARENCY; // NOTE: only in dli does single / separate ALBEDO texture mean TRANSPARENCY.
977     }
978     if(ReadString(node.GetChild("albedoMetallicMap"), texturePath))
979     {
980       ToUnixFileSeparators(texturePath);
981
982       if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
983       {
984         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "albedo"));
985       }
986
987       const auto semantic = MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC;
988       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
989       materialDef.mFlags |= semantic;
990     }
991
992     if(ReadString(node.GetChild("metallicRoughnessMap"), texturePath))
993     {
994       ToUnixFileSeparators(texturePath);
995
996       if(MaskMatch(materialDef.mFlags, MaterialDefinition::METALLIC))
997       {
998         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "metallic"));
999       }
1000
1001       const auto semantic = MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS;
1002       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1003       materialDef.mFlags |= semantic |
1004                             // We have a metallic-roughhness map and the first texture did not have albedo semantics - we're in the transparency workflow.
1005                             (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
1006     }
1007
1008     if(ReadString(node.GetChild("normalMap"), texturePath))
1009     {
1010       ToUnixFileSeparators(texturePath);
1011
1012       const auto semantic = MaterialDefinition::NORMAL;
1013       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1014       materialDef.mFlags |= semantic |
1015                             // We have a standalone normal map and the first texture did not have albedo semantics - we're in the transparency workflow.
1016                             (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
1017     }
1018
1019     if(ReadString(node.GetChild("normalRoughnessMap"), texturePath))
1020     {
1021       ToUnixFileSeparators(texturePath);
1022
1023       if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
1024       {
1025         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "normal"));
1026       }
1027
1028       if(MaskMatch(materialDef.mFlags, MaterialDefinition::ROUGHNESS))
1029       {
1030         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "roughness"));
1031       }
1032
1033       if(MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY))
1034       {
1035         mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "transparency"));
1036       }
1037
1038       const auto semantic = MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS;
1039       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1040       materialDef.mFlags |= semantic;
1041     }
1042
1043     /// @TODO : Some dli shader don't implement this subsurfaceMp usage.
1044     ///         To make visual test pass, Skip subsurfaceMap texture using
1045     ///         until dli shaders are support it.
1046     //    if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
1047     //    {
1048     //      ToUnixFileSeparators(texturePath);
1049     //
1050     //      const auto semantic = MaterialDefinition::SUBSURFACE;
1051     //      materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1052     //      materialDef.mFlags |= semantic;
1053     //    }
1054
1055     if(ReadString(node.GetChild("occlusionMap"), texturePath))
1056     {
1057       ToUnixFileSeparators(texturePath);
1058       const auto semantic = MaterialDefinition::OCCLUSION;
1059       materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
1060       materialDef.mFlags |= semantic;
1061     }
1062
1063     if(ReadColorCodeOrColor(&node, materialDef.mColor, convertColorCode) &&
1064        materialDef.mColor.a < 1.0f)
1065     {
1066       materialDef.mFlags |= MaterialDefinition::TRANSPARENCY;
1067     }
1068
1069     ReadFloat(node.GetChild("metallic"), materialDef.mMetallic);
1070     ReadFloat(node.GetChild("roughness"), materialDef.mRoughness);
1071
1072     bool mipmaps;
1073     if(ReadBool(node.GetChild("mipmap"), mipmaps) && mipmaps)
1074     {
1075       for(auto& ts : materialDef.mTextureStages)
1076       {
1077         ts.mTexture.mSamplerFlags |= SamplerFlags::FILTER_MIPMAP_LINEAR;
1078       }
1079     }
1080
1081     resources.mMaterials.emplace_back(std::move(materialDef), TextureSet());
1082   }
1083 }
1084
1085 void DliLoaderImpl::Impl::ParseNodes(const TreeNode* const nodes, Index index, LoadParams& params)
1086 {
1087   std::vector<Index> parents;
1088   parents.reserve(8);
1089
1090   struct IndexMapper : IIndexMapper
1091   {
1092     IndexMapper(size_t numNodes)
1093     {
1094       mIndices.reserve(numNodes);
1095     }
1096
1097     virtual bool Map(Index iDli, Index iScene) override
1098     {
1099       Entry idx{iDli, iScene};
1100       auto  iInsert = std::lower_bound(mIndices.begin(), mIndices.end(), idx);
1101       if(iInsert == mIndices.end() || iInsert->iDli != iDli)
1102       {
1103         mIndices.insert(iInsert, idx);
1104       }
1105       else if(iInsert->iScene != iScene)
1106       {
1107         return false;
1108       }
1109       return true;
1110     }
1111
1112     virtual unsigned int Resolve(Index iDli) override
1113     {
1114       auto iFind = std::lower_bound(mIndices.begin(), mIndices.end(), iDli, [](const Entry& idx, Index iDli) { return idx.iDli < iDli; });
1115       DALI_ASSERT_ALWAYS(iFind != mIndices.end());
1116       return iFind->iScene;
1117     }
1118
1119   private:
1120     struct Entry
1121     {
1122       unsigned int iDli;
1123       unsigned int iScene;
1124
1125       bool operator<(const Entry& other) const
1126       {
1127         return iDli < other.iDli;
1128       }
1129     };
1130     std::vector<Entry> mIndices;
1131   } mapper(nodes->Size());
1132   ParseNodesInternal(nodes, index, parents, params, mapper);
1133
1134   auto& scene = params.output->mScene;
1135   for(size_t i0 = 0, i1 = scene.GetNodeCount(); i0 < i1; ++i0)
1136   {
1137     for(auto& c : scene.GetNode(i0)->mConstraints)
1138     {
1139       c.mSourceIdx = mapper.Resolve(c.mSourceIdx);
1140     }
1141   }
1142 }
1143
1144 void DliLoaderImpl::Impl::ParseNodesInternal(const TreeNode* const nodes, Index index, std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& mapper)
1145 {
1146   // Properties that may be resolved from a JSON value with ReadInt() -- or default to 0.
1147   struct IndexProperty
1148   {
1149     ResourceType::Value type;
1150     const TreeNode*     source;
1151     Index&              target;
1152   };
1153   std::vector<IndexProperty> resourceIds;
1154   resourceIds.reserve(4);
1155
1156   if(auto node = GetNthChild(nodes, index))
1157   {
1158     Dali::Scene3D::Loader::NodeDefinition nodeDef;
1159     nodeDef.mParentIdx = inOutParentStack.empty() ? INVALID_INDEX : inOutParentStack.back();
1160
1161     // name
1162     ReadString(node->GetChild(NAME), nodeDef.mName);
1163
1164     // transform
1165     ReadModelTransform(node, nodeDef.mOrientation, nodeDef.mPosition, nodeDef.mScale);
1166
1167     // Reads the size of the node.
1168     //
1169     // * It can be given as 'size' or 'bounds'.
1170     // * The sdk saves the 'size' as a vector2 in some cases.
1171     // * To avoid size related issues the following code attemps
1172     //   to read the 'size/bounds' as a vector3 first, if it's
1173     //   not successful then reads it as a vector2.
1174     ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 3) ||
1175       ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 2) ||
1176       ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 3) ||
1177       ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 2);
1178
1179     // visibility
1180     ReadBool(node->GetChild("visible"), nodeDef.mIsVisible);
1181
1182     // type classification
1183     if(auto eCustomization = node->GetChild("customization")) // customization
1184     {
1185       std::string tag;
1186       if(ReadString(eCustomization->GetChild("tag"), tag))
1187       {
1188         nodeDef.mCustomization.reset(new Dali::Scene3D::Loader::NodeDefinition::CustomizationDefinition{tag});
1189       }
1190     }
1191     else // something renderable maybe
1192     {
1193       std::unique_ptr<Dali::Scene3D::Loader::NodeDefinition::Renderable> renderable;
1194       ModelRenderable*                                                   modelRenderable = nullptr; // no ownership, aliasing renderable for the right type.
1195
1196       const TreeNode* eRenderable = nullptr;
1197       if((eRenderable = node->GetChild("model")))
1198       {
1199         // check for mesh before allocating - this can't be missing.
1200         auto eMesh = eRenderable->GetChild("mesh");
1201         if(!eMesh)
1202         {
1203           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1204         }
1205
1206         modelRenderable = new ModelRenderable();
1207         renderable.reset(modelRenderable);
1208
1209         resourceIds.push_back({ResourceType::Mesh, eMesh, modelRenderable->mMeshIdx});
1210       }
1211       else if((eRenderable = node->GetChild("arc")))
1212       {
1213         // check for mesh before allocating - this can't be missing.
1214         auto eMesh = eRenderable->GetChild("mesh");
1215         if(!eMesh)
1216         {
1217           ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
1218         }
1219
1220         auto arcRenderable = new ArcRenderable;
1221         renderable.reset(arcRenderable);
1222         modelRenderable = arcRenderable;
1223
1224         resourceIds.push_back({ResourceType::Mesh, eMesh, arcRenderable->mMeshIdx});
1225
1226         ReadArcField(eRenderable, *arcRenderable);
1227       }
1228
1229       if(renderable && eRenderable != nullptr) // process common properties of all renderables + register payload
1230       {
1231         // shader
1232         renderable->mShaderIdx = 0;
1233         auto eShader           = eRenderable->GetChild("shader");
1234         if(eShader)
1235         {
1236           resourceIds.push_back({ResourceType::Shader, eShader, renderable->mShaderIdx});
1237         }
1238
1239         // color
1240         if(modelRenderable)
1241         {
1242           modelRenderable->mMaterialIdx = 0; // must offer default of 0
1243           auto eMaterial                = eRenderable->GetChild("material");
1244           if(eMaterial)
1245           {
1246             resourceIds.push_back({ResourceType::Material, eMaterial, modelRenderable->mMaterialIdx});
1247           }
1248
1249           if(!ReadColorCodeOrColor(eRenderable, modelRenderable->mColor, params.input->mConvertColorCode))
1250           {
1251             ReadColorCodeOrColor(node, modelRenderable->mColor, params.input->mConvertColorCode);
1252           }
1253         }
1254
1255         nodeDef.mRenderables.push_back(std::move(renderable));
1256       }
1257     }
1258
1259     // Resolve ints - default to 0 if undefined
1260     auto& output = params.output;
1261     for(auto& idRes : resourceIds)
1262     {
1263       Index iCheck = 0;
1264       switch(idRes.type)
1265       {
1266         case ResourceType::Shader:
1267           iCheck = output->mResources.mShaders.size();
1268           break;
1269
1270         case ResourceType::Mesh:
1271           iCheck = output->mResources.mMeshes.size();
1272           break;
1273
1274         case ResourceType::Material:
1275           iCheck = output->mResources.mMaterials.size();
1276           break;
1277
1278         default:
1279           ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid resource type: " << idRes.type << " (Programmer error)";
1280       }
1281
1282       if(!idRes.source)
1283       {
1284         idRes.target = 0;
1285       }
1286       else if(idRes.source->GetType() != TreeNode::INTEGER)
1287       {
1288         ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid " << GetResourceTypeName(idRes.type) << " index type.";
1289       }
1290       else
1291       {
1292         idRes.target = idRes.source->GetInteger();
1293       }
1294
1295       if(idRes.target >= iCheck)
1296       {
1297         ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": " << GetResourceTypeName(idRes.type) << " index " << idRes.target << " out of bounds (" << iCheck << ").";
1298       }
1299     }
1300     resourceIds.clear();
1301
1302     // Extra properties
1303     if(auto eExtras = node->GetChild("extras"))
1304     {
1305       auto& extras = nodeDef.mExtras;
1306       extras.reserve(eExtras->Size());
1307
1308       for(auto i0 = eExtras->CBegin(), i1 = eExtras->CEnd(); i0 != i1; ++i0)
1309       {
1310         Dali::Scene3D::Loader::NodeDefinition::Extra e;
1311
1312         auto eExtra = *i0;
1313         e.mKey      = eExtra.first;
1314         if(e.mKey.empty())
1315         {
1316           mOnError(FormatString("node %d: empty string is invalid for name of extra %d; ignored.",
1317                                 index,
1318                                 extras.size()));
1319           continue;
1320         }
1321
1322         e.mValue = ReadPropertyValue(eExtra.second);
1323         if(e.mValue.GetType() == Property::Type::NONE)
1324         {
1325           mOnError(FormatString("node %d: failed to interpret value of extra '%s' : %s; ignored.",
1326                                 index,
1327                                 e.mKey.c_str(),
1328                                 eExtra.second.GetString()));
1329         }
1330         else
1331         {
1332           auto iInsert = std::lower_bound(extras.begin(), extras.end(), e);
1333           if(iInsert != extras.end() && iInsert->mKey == e.mKey)
1334           {
1335             mOnError(FormatString("node %d: extra '%s' already defined; overriding with %s.",
1336                                   index,
1337                                   e.mKey.c_str(),
1338                                   eExtra.second.GetString()));
1339             *iInsert = std::move(e);
1340           }
1341           else
1342           {
1343             extras.insert(iInsert, e);
1344           }
1345         }
1346       }
1347     }
1348
1349     // Constraints
1350     if(auto eConstraints = node->GetChild("constraints"))
1351     {
1352       auto& constraints = nodeDef.mConstraints;
1353       constraints.reserve(eConstraints->Size());
1354
1355       ConstraintDefinition cDef;
1356       for(auto i0 = eConstraints->CBegin(), i1 = eConstraints->CEnd(); i0 != i1; ++i0)
1357       {
1358         auto eConstraint = *i0;
1359         if(!ReadIndex(&eConstraint.second, cDef.mSourceIdx))
1360         {
1361           mOnError(FormatString("node %d: node ID %s for constraint %d is invalid; ignored.",
1362                                 index,
1363                                 eConstraint.second.GetString(),
1364                                 constraints.size()));
1365         }
1366         else
1367         {
1368           cDef.mProperty = eConstraint.first;
1369
1370           auto iInsert = std::lower_bound(constraints.begin(), constraints.end(), cDef);
1371           if(iInsert != constraints.end() && *iInsert == cDef)
1372           {
1373             mOnError(FormatString("node %d: constraint %s@%d already defined; ignoring.",
1374                                   index,
1375                                   cDef.mProperty.c_str(),
1376                                   cDef.mSourceIdx));
1377           }
1378           else
1379           {
1380             constraints.insert(iInsert, cDef);
1381           }
1382         }
1383       }
1384     }
1385
1386     // Determine index for mapping
1387     const unsigned int myIndex = output->mScene.GetNodeCount();
1388     if(!mapper.Map(index, myIndex))
1389     {
1390       mOnError(FormatString("node %d: error mapping dli index %d: node has multiple parents. Ignoring subtree.", index, myIndex));
1391       return;
1392     }
1393
1394     // if the node is a bone in a skeletal animation, it will have the inverse bind pose matrix.
1395     Matrix invBindMatrix{false};
1396     if(ReadVector(node->GetChild("inverseBindPoseMatrix"), invBindMatrix.AsFloat(), 16u)) // TODO: more robust error checking?
1397     {
1398       mInverseBindMatrices[myIndex] = invBindMatrix;
1399     }
1400
1401     // Register nodeDef
1402     auto rawDef = output->mScene.AddNode(std::make_unique<Dali::Scene3D::Loader::NodeDefinition>(std::move(nodeDef)));
1403     if(rawDef) // NOTE: no ownership. Guaranteed to stay in scope.
1404     {
1405       // ...And only then parse children.
1406       if(auto children = node->GetChild("children"))
1407       {
1408         inOutParentStack.push_back(myIndex);
1409
1410         rawDef->mChildren.reserve(children->Size());
1411
1412         uint32_t iChild = 0;
1413         for(auto j0 = children->CBegin(), j1 = children->CEnd(); j0 != j1; ++j0, ++iChild)
1414         {
1415           auto& child = (*j0).second;
1416           if(child.GetType() == TreeNode::INTEGER)
1417           {
1418             ParseNodesInternal(nodes, child.GetInteger(), inOutParentStack, params, mapper); // child object is created in scene definition.
1419           }
1420           else
1421           {
1422             ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ", child " << iChild << ": invalid index type.";
1423           }
1424         }
1425
1426         inOutParentStack.pop_back();
1427       }
1428       else if(rawDef->mCustomization)
1429       {
1430         mOnError(FormatString("node %d: not an actual customization without children.", index));
1431       }
1432
1433       if(auto proc = params.input->mNodePropertyProcessor) // optional processing
1434       {
1435         // WARNING: constraint IDs are not resolved at this point.
1436         Property::Map nodeData;
1437         ParseProperties(*node, nodeData);
1438         proc(*rawDef, std::move(nodeData), mOnError);
1439       }
1440     }
1441     else
1442     {
1443       ExceptionFlinger(ASSERT_LOCATION) << "Node " << index << ": name already used.";
1444     }
1445   }
1446 }
1447
1448 void DliLoaderImpl::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams& params)
1449 {
1450   auto& definitions = params.output->mAnimationDefinitions;
1451   definitions.reserve(definitions.size() + tnAnimations->Size());
1452
1453   for(TreeNode::ConstIterator iAnim = tnAnimations->CBegin(), iAnimEnd = tnAnimations->CEnd();
1454       iAnim != iAnimEnd;
1455       ++iAnim)
1456   {
1457     const TreeNode&     tnAnim                 = (*iAnim).second;
1458     uint32_t            animationPropertyIndex = 0;
1459     AnimationDefinition animDef;
1460     std::string         animationName;
1461     ReadString(tnAnim.GetChild(NAME), animationName);
1462     animDef.SetName(animationName);
1463
1464     auto       iFind     = std::lower_bound(definitions.begin(), definitions.end(), animDef, [](const AnimationDefinition& ad0, const AnimationDefinition& ad1) { return ad0.GetName() < ad1.GetName(); });
1465     const bool overwrite = iFind != definitions.end() && iFind->GetName() == animDef.GetName();
1466     if(overwrite)
1467     {
1468       mOnError(FormatString("Pre-existing animation with name '%s' is being overwritten.", animDef.GetName().c_str()));
1469     }
1470
1471     // Duration -- We need something that animated properties' delay / duration can
1472     // be expressed as a multiple of; 0 won't work. This is small enough (i.e. shorter
1473     // than our frame delay) to not be restrictive WRT replaying. If anything needs
1474     // to occur more frequently, then Animations are likely not your solution anyway.
1475     float animationDuration = 0.0f;
1476     if(!ReadFloat(tnAnim.GetChild("duration"), animationDuration))
1477     {
1478       mOnError(FormatString("Animation '%s' fails to define '%s'.",
1479                             animDef.GetName().c_str(),
1480                             "duration"));
1481     }
1482     if(animationDuration < AnimationDefinition::MIN_DURATION_SECONDS)
1483     {
1484       animationDuration = AnimationDefinition::MIN_DURATION_SECONDS;
1485     }
1486     animDef.SetDuration(animationDuration);
1487
1488     // Get loop count - # of playbacks. Default is once. 0 means repeat indefinitely.
1489     int32_t animationLoopCount = 1;
1490     if(ReadInt(tnAnim.GetChild("loopCount"), animationLoopCount) && animationLoopCount < 0)
1491     {
1492       animationLoopCount = 0;
1493     }
1494     animDef.SetLoopCount(animationLoopCount);
1495
1496     std::string endAction;
1497     if(ReadString(tnAnim.GetChild("endAction"), endAction))
1498     {
1499       if("BAKE" == endAction)
1500       {
1501         animDef.SetEndAction(Animation::BAKE);
1502       }
1503       else if("DISCARD" == endAction)
1504       {
1505         animDef.SetEndAction(Animation::DISCARD);
1506       }
1507       else if("BAKE_FINAL" == endAction)
1508       {
1509         animDef.SetEndAction(Animation::BAKE_FINAL);
1510       }
1511     }
1512
1513     if(ReadString(tnAnim.GetChild("disconnectAction"), endAction))
1514     {
1515       if("BAKE" == endAction)
1516       {
1517         animDef.SetDisconnectAction(Animation::BAKE);
1518       }
1519       else if("DISCARD" == endAction)
1520       {
1521         animDef.SetDisconnectAction(Animation::DISCARD);
1522       }
1523       else if("BAKE_FINAL" == endAction)
1524       {
1525         animDef.SetDisconnectAction(Animation::BAKE_FINAL);
1526       }
1527     }
1528
1529     if(const TreeNode* tnProperties = tnAnim.GetChild("properties"))
1530     {
1531       animDef.ReserveSize(tnProperties->Size());
1532       for(TreeNode::ConstIterator iProperty = tnProperties->CBegin(), iPropertyEnd = tnProperties->CEnd();
1533           iProperty != iPropertyEnd;
1534           ++iProperty)
1535       {
1536         const TreeNode& tnProperty = (*iProperty).second;
1537
1538         AnimatedProperty animProp;
1539         if(!ReadString(tnProperty.GetChild("node"), animProp.mNodeName))
1540         {
1541           mOnError(FormatString("Animation '%s': Failed to read the 'node' tag.", animDef.GetName().c_str()));
1542           continue;
1543         }
1544
1545         if(!ReadString(tnProperty.GetChild("property"), animProp.mPropertyName))
1546         {
1547           mOnError(FormatString("Animation '%s': Failed to read the 'property' tag", animDef.GetName().c_str()));
1548           continue;
1549         }
1550
1551         // these are the defaults
1552         animProp.mTimePeriod.delaySeconds    = 0.f;
1553         animProp.mTimePeriod.durationSeconds = animDef.GetDuration();
1554         if(!ReadTimePeriod(tnProperty.GetChild("timePeriod"), animProp.mTimePeriod))
1555         {
1556           mOnError(FormatString("Animation '%s': timePeriod missing in Property #%d: defaulting to %f.",
1557                                 animDef.GetName().c_str(),
1558                                 animDef.GetPropertyCount(),
1559                                 animProp.mTimePeriod.durationSeconds));
1560         }
1561
1562         std::string alphaFunctionValue;
1563         if(ReadString(tnProperty.GetChild("alphaFunction"), alphaFunctionValue))
1564         {
1565           animProp.mAlphaFunction = GetAlphaFunction(alphaFunctionValue);
1566         }
1567
1568         if(const TreeNode* tnKeyFramesBin = tnProperty.GetChild("keyFramesBin"))
1569         {
1570           DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1571
1572           std::ifstream binAniFile;
1573           std::string   animationFilename;
1574           if(ReadString(tnKeyFramesBin->GetChild(URL), animationFilename))
1575           {
1576             std::string animationFullPath = params.input->mAnimationsPath + animationFilename;
1577             binAniFile.open(animationFullPath, std::ios::binary);
1578             if(binAniFile.fail())
1579             {
1580               ExceptionFlinger(ASSERT_LOCATION) << "Failed to open animation data '" << animationFullPath << "'";
1581             }
1582           }
1583
1584           int byteOffset = 0;
1585           ReadInt(tnKeyFramesBin->GetChild("byteOffset"), byteOffset);
1586           DALI_ASSERT_ALWAYS(byteOffset >= 0);
1587
1588           binAniFile.seekg(static_cast<std::streamoff>(byteOffset), std::ios::beg);
1589
1590           int numKeys = 0;
1591           ReadInt(tnKeyFramesBin->GetChild("numKeys"), numKeys);
1592           DALI_ASSERT_ALWAYS(numKeys >= 0);
1593
1594           animProp.mKeyFrames = KeyFrames::New();
1595
1596           // In binary animation file only is saved the position, rotation, scale and blend shape weight keys.
1597           // so, if it is vector3 we assume is position or scale keys, if it is vector4 we assume is rotation,
1598           //  otherwise are blend shape weight keys.
1599           //  TODO support for binary header with size information
1600           Property::Type propType = Property::FLOAT; // assume blend shape weights
1601           if(animProp.mPropertyName == "orientation")
1602           {
1603             propType = Property::VECTOR4;
1604           }
1605           else if((animProp.mPropertyName == "position") || (animProp.mPropertyName == "scale"))
1606           {
1607             propType = Property::VECTOR3;
1608           }
1609
1610           // alphafunction is reserved for future implementation
1611           //  NOTE: right now we're just using AlphaFunction::LINEAR.
1612           unsigned char dummyAlphaFunction;
1613
1614           float           progress;
1615           Property::Value propValue;
1616           for(int key = 0; key < numKeys; key++)
1617           {
1618             binAniFile.read(reinterpret_cast<char*>(&progress), sizeof(float));
1619             if(propType == Property::VECTOR3)
1620             {
1621               Vector3 value;
1622               binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 3);
1623               propValue = Property::Value(value);
1624             }
1625             else if(propType == Property::VECTOR4)
1626             {
1627               Vector4 value;
1628               binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 4);
1629               propValue = Property::Value(Quaternion(value));
1630             }
1631             else
1632             {
1633               float value;
1634               binAniFile.read(reinterpret_cast<char*>(&value), sizeof(float));
1635               propValue = Property::Value(value);
1636             }
1637
1638             binAniFile.read(reinterpret_cast<char*>(&dummyAlphaFunction), sizeof(unsigned char));
1639
1640             animProp.mKeyFrames.Add(progress, propValue, AlphaFunction::LINEAR);
1641           }
1642         }
1643         else if(const TreeNode* tnKeyFrames = tnProperty.GetChild("keyFrames"))
1644         {
1645           DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
1646           animProp.mKeyFrames = KeyFrames::New();
1647
1648           float progress = 0.0f;
1649           for(auto i0 = tnKeyFrames->CBegin(), i1 = tnKeyFrames->CEnd(); i1 != i0; ++i0)
1650           {
1651             const TreeNode::KeyNodePair& kfKeyChild = *i0;
1652             bool                         readResult = ReadFloat(kfKeyChild.second.GetChild("progress"), progress);
1653             DALI_ASSERT_ALWAYS(readResult && "Key frame entry must have 'progress'");
1654
1655             const TreeNode* tnValue = kfKeyChild.second.GetChild("value");
1656             DALI_ASSERT_ALWAYS(tnValue && "Key frame entry must have 'value'");
1657
1658             // For the "orientation" property, convert from Vector4 -> Rotation value
1659             // This work-around is preferable to a null-pointer exception in the DALi update thread
1660             Property::Value propValue(ReadPropertyValue(*tnValue));
1661             if(propValue.GetType() == Property::VECTOR4 &&
1662                animProp.mPropertyName == "orientation")
1663             {
1664               Vector4 v;
1665               propValue.Get(v);
1666               propValue = Property::Value(Quaternion(v.w, v.x, v.y, v.z));
1667             }
1668
1669             AlphaFunction kfAlphaFunction(AlphaFunction::DEFAULT);
1670             std::string   alphaFuncStr;
1671             if(ReadString(kfKeyChild.second.GetChild("alphaFunction"), alphaFuncStr))
1672             {
1673               kfAlphaFunction = GetAlphaFunction(alphaFuncStr);
1674             }
1675
1676             animProp.mKeyFrames.Add(progress, propValue, kfAlphaFunction);
1677           }
1678         }
1679         else
1680         {
1681           const TreeNode* tnValue = tnProperty.GetChild("value");
1682           if(tnValue)
1683           {
1684             animProp.mValue.reset(new AnimatedProperty::Value{ReadPropertyValue(*tnValue)});
1685             ReadBool(tnProperty.GetChild("relative"), animProp.mValue->mIsRelative);
1686           }
1687           else
1688           {
1689             mOnError(FormatString("Property '%s' fails to define target value.",
1690                                   animProp.mPropertyName.c_str()));
1691           }
1692         }
1693
1694         animDef.SetProperty(animationPropertyIndex++, std::move(animProp));
1695       }
1696     }
1697
1698     if(auto proc = params.input->mAnimationPropertyProcessor) // optional processing
1699     {
1700       Property::Map map;
1701       ParseProperties(tnAnim, map);
1702       proc(animDef, std::move(map), mOnError);
1703     }
1704
1705     if(overwrite)
1706     {
1707       *iFind = std::move(animDef);
1708     }
1709     else
1710     {
1711       iFind = definitions.insert(iFind, std::move(animDef));
1712     }
1713   }
1714 }
1715
1716 void DliLoaderImpl::Impl::ParseAnimationGroups(const Toolkit::TreeNode* tnAnimationGroups, LoadParams& params)
1717 {
1718   auto& animGroups = params.output->mAnimationGroupDefinitions;
1719
1720   int numGroups = 0;
1721   for(auto iGroups = tnAnimationGroups->CBegin(), iGroupsEnd = tnAnimationGroups->CEnd();
1722       iGroups != iGroupsEnd;
1723       ++iGroups, ++numGroups)
1724   {
1725     const auto& tnGroup = *iGroups;
1726     auto        tnName  = tnGroup.second.GetChild(NAME);
1727     std::string groupName;
1728     if(!tnName || !ReadString(tnName, groupName))
1729     {
1730       mOnError(FormatString("Failed to get the name for the Animation group %d; ignoring.", numGroups));
1731       continue;
1732     }
1733
1734     auto iFind = std::lower_bound(animGroups.begin(), animGroups.end(), groupName, [](const AnimationGroupDefinition& group, const std::string& name) { return group.mName < name; });
1735     if(iFind != animGroups.end() && iFind->mName == groupName)
1736     {
1737       mOnError(FormatString("Animation group with name '%s' already exists; new entries will be merged.", groupName.c_str()));
1738     }
1739     else
1740     {
1741       iFind = animGroups.insert(iFind, AnimationGroupDefinition{});
1742     }
1743
1744     iFind->mName = groupName;
1745
1746     auto tnAnims = tnGroup.second.GetChild("animations");
1747     if(tnAnims && tnAnims->Size() > 0)
1748     {
1749       auto& anims = iFind->mAnimations;
1750       anims.reserve(anims.size() + tnAnims->Size());
1751       for(auto iAnims = tnAnims->CBegin(), iAnimsEnd = tnAnims->CEnd(); iAnims != iAnimsEnd; ++iAnims)
1752       {
1753         anims.push_back((*iAnims).second.GetString());
1754       }
1755     }
1756   }
1757 }
1758
1759 void DliLoaderImpl::Impl::GetCameraParameters(std::vector<Dali::Scene3D::Loader::CameraParameters>& cameras) const
1760 {
1761   if(mParser.GetRoot())
1762   {
1763     if(const TreeNode* jsonCameras = mParser.GetRoot()->GetChild("cameras"))
1764     {
1765       float dummyFloatArray[4];
1766
1767       cameras.resize(jsonCameras->Size());
1768       auto iCamera = cameras.begin();
1769       for(auto i0 = jsonCameras->CBegin(), i1 = jsonCameras->CEnd(); i0 != i1; ++i0)
1770       {
1771         auto& jsonCamera = (*i0).second;
1772
1773         ReadFloat(jsonCamera.GetChild("fov"), iCamera->yFovDegree.degree);
1774         ReadFloat(jsonCamera.GetChild("near"), iCamera->zNear);
1775         ReadFloat(jsonCamera.GetChild("far"), iCamera->zFar);
1776         if(ReadVector(jsonCamera.GetChild("orthographic"), dummyFloatArray, 4u))
1777         {
1778           iCamera->isPerspective = false;
1779
1780           iCamera->orthographicSize = dummyFloatArray[2] * 0.5f;
1781           iCamera->aspectRatio      = dummyFloatArray[1] / dummyFloatArray[2];
1782         }
1783
1784         if(auto jsonMatrix = jsonCamera.GetChild("matrix"))
1785         {
1786           ReadVector(jsonMatrix, iCamera->matrix.AsFloat(), 16u);
1787         }
1788
1789         ++iCamera;
1790       }
1791     }
1792   }
1793 }
1794
1795 void DliLoaderImpl::Impl::GetLightParameters(std::vector<Dali::Scene3D::Loader::LightParameters>& lights) const
1796 {
1797   if(mParser.GetRoot())
1798   {
1799     if(const TreeNode* jsonLights = mParser.GetRoot()->GetChild("lights"))
1800     {
1801       lights.resize(jsonLights->Size());
1802       auto iLight = lights.begin();
1803       for(auto i0 = jsonLights->CBegin(), i1 = jsonLights->CEnd(); i0 != i1; ++i0)
1804       {
1805         auto& jsonLight = (*i0).second;
1806         if(!ReadVector(jsonLight.GetChild("matrix"), iLight->transform.AsFloat(), 16))
1807         {
1808           mOnError(
1809             FormatString("Failed to parse light %d - \"matrix\" child with 16 floats expected.\n",
1810                          std::distance(jsonLights->CBegin(), i0)));
1811           continue;
1812         }
1813
1814         int shadowMapSize = 0;
1815         if(ReadInt(jsonLight.GetChild(SHADOW_MAP_SIZE), shadowMapSize) && shadowMapSize < 0)
1816         {
1817           mOnError(
1818             FormatString("Failed to parse light %d - %s has an invalid value.",
1819                          std::distance(jsonLights->CBegin(), i0),
1820                          SHADOW_MAP_SIZE));
1821           continue;
1822         }
1823         iLight->shadowMapSize = shadowMapSize;
1824
1825         float orthoSize = 0.f;
1826         if(ReadFloat(jsonLight.GetChild(ORTHOGRAPHIC_SIZE), orthoSize) &&
1827            (orthoSize < .0f || std::isnan(orthoSize) || std::isinf(orthoSize)))
1828         {
1829           mOnError(
1830             FormatString("Failed to parse light %d - %s has an invalid value.",
1831                          std::distance(jsonLights->CBegin(), i0),
1832                          ORTHOGRAPHIC_SIZE));
1833           continue;
1834         }
1835         iLight->orthographicSize = orthoSize;
1836
1837         if((iLight->shadowMapSize > 0) != (iLight->orthographicSize > .0f))
1838         {
1839           mOnError(FormatString(
1840             "Light %d: Both shadow map size and orthographic size must be set for shadows to work.",
1841             std::distance(jsonLights->CBegin(), i0)));
1842         }
1843
1844         if(!ReadVector(jsonLight.GetChild("color"), iLight->color.AsFloat(), 3)) // color is optional
1845         {
1846           iLight->color = Vector3::ONE; // default to white
1847         }
1848
1849         if(!ReadFloat(jsonLight.GetChild("intensity"), iLight->intensity)) // intensity is optional
1850         {
1851           iLight->intensity = 1.0f; // default to 1.0
1852         }
1853
1854         if(!ReadFloat(jsonLight.GetChild("shadowIntensity"), iLight->shadowIntensity)) // intensity is optional
1855         {
1856           iLight->shadowIntensity = 1.0f; // default to 1.0
1857         }
1858
1859         ++iLight;
1860       }
1861     }
1862   }
1863 }
1864
1865 } // namespace Internal
1866 } // namespace Loader
1867 } // namespace Scene3D
1868 } // namespace Dali