Merge "Add log for font load validation" into devel/master
[platform/core/uifw/dali-toolkit.git] / automated-tests / src / dali-scene3d / utc-Dali-Gltf2Loader.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // Enable debug log for test coverage
19 #define DEBUG_ENABLED 1
20
21 #include <dali-test-suite-utils.h>
22 #include <string_view>
23 #include "dali-scene3d/public-api/loader/gltf2-loader.h"
24 #include "dali-scene3d/public-api/loader/load-result.h"
25 #include "dali-scene3d/public-api/loader/resource-bundle.h"
26 #include "dali-scene3d/public-api/loader/scene-definition.h"
27 #include "dali-scene3d/public-api/loader/shader-definition-factory.h"
28
29 using namespace Dali;
30 using namespace Dali::Scene3D::Loader;
31
32 #define DALI_TEST_THROW(expression, exception, predicate) \
33   {                                                       \
34     bool daliTestThrowSuccess__ = false;                  \
35     try                                                   \
36     {                                                     \
37       do                                                  \
38       {                                                   \
39         expression;                                       \
40       } while(0);                                         \
41       printf("No exception was thrown.\n");               \
42     }                                                     \
43     catch(std::decay<exception>::type & ex)               \
44     {                                                     \
45       daliTestThrowSuccess__ = predicate(ex);             \
46     }                                                     \
47     catch(...)                                            \
48     {                                                     \
49       printf("Wrong type of exception thrown.\n");        \
50     }                                                     \
51     DALI_TEST_CHECK(daliTestThrowSuccess__);              \
52   }
53
54 namespace
55 {
56 struct Context
57 {
58   ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
59     return TEST_RESOURCE_DIR "/";
60   };
61
62   ResourceBundle  resources;
63   SceneDefinition scene;
64
65   std::vector<AnimationDefinition>      animations;
66   std::vector<AnimationGroupDefinition> animationGroups;
67   std::vector<CameraParameters>         cameras;
68   std::vector<LightParameters>          lights;
69
70   LoadResult loadResult{
71     resources,
72     scene,
73     animations,
74     animationGroups,
75     cameras,
76     lights};
77 };
78
79 struct ExceptionMessageStartsWith
80 {
81   const std::string_view expected;
82
83   bool operator()(const std::runtime_error& e)
84   {
85     const bool success = (0 == strncmp(e.what(), expected.data(), expected.size()));
86     if(!success)
87     {
88       printf("Expected: %s, got: %s.\n", expected.data(), e.what());
89     }
90     return success;
91   }
92 };
93
94 } // namespace
95
96 int UtcDaliGltfLoaderFailedToLoad(void)
97 {
98   Context ctx;
99
100   ShaderDefinitionFactory sdf;
101   sdf.SetResources(ctx.resources);
102
103   DALI_TEST_THROW(LoadGltfScene("non-existent.gltf", sdf, ctx.loadResult),
104                   std::runtime_error,
105                   ExceptionMessageStartsWith{"Failed to load"});
106
107   DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
108   DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
109
110   DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
111   DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
112   DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
113   DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
114   DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
115
116   DALI_TEST_EQUAL(0, ctx.cameras.size());
117   DALI_TEST_EQUAL(0, ctx.lights.size());
118   DALI_TEST_EQUAL(0, ctx.animations.size());
119   DALI_TEST_EQUAL(0, ctx.animationGroups.size());
120
121   END_TEST;
122 }
123
124 int UtcDaliGltfLoaderFailedToParse(void)
125 {
126   Context ctx;
127
128   ShaderDefinitionFactory sdf;
129   sdf.SetResources(ctx.resources);
130
131   DALI_TEST_THROW(LoadGltfScene(TEST_RESOURCE_DIR "/invalid.gltf", sdf, ctx.loadResult),
132                   std::runtime_error,
133                   ExceptionMessageStartsWith{"Failed to parse"});
134
135   DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
136   DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
137
138   DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
139   DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
140   DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
141   DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
142   DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
143
144   DALI_TEST_EQUAL(0, ctx.cameras.size());
145   DALI_TEST_EQUAL(0, ctx.lights.size());
146   DALI_TEST_EQUAL(0, ctx.animations.size());
147   DALI_TEST_EQUAL(0, ctx.animationGroups.size());
148
149   END_TEST;
150 }
151
152 int UtcDaliGltfLoaderSuccess1(void)
153 {
154   Context ctx;
155
156   ShaderDefinitionFactory sdf;
157   sdf.SetResources(ctx.resources);
158
159   LoadGltfScene(TEST_RESOURCE_DIR "/AnimatedCube.gltf", sdf, ctx.loadResult);
160
161   DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
162   DALI_TEST_EQUAL(6u, ctx.scene.GetNodeCount());
163
164   // Default envmap is used
165   DALI_TEST_EQUAL(1u, ctx.resources.mEnvironmentMaps.size());
166
167   auto& materials = ctx.resources.mMaterials;
168   DALI_TEST_EQUAL(2u, materials.size());
169   const MaterialDefinition materialGroundTruth[]{
170     {MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
171        MaterialDefinition::NORMAL | MaterialDefinition::TRANSPARENCY |
172        (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
173      0,
174      Color::WHITE,
175      1.f,
176      0.f,
177      Vector4(1.000, 0.766, 0.336, 1.0),
178      1.f,
179      1.f,
180      Vector3(0.2, 0.1, 0.0),
181      true,
182      false,
183      true,
184      {
185        {MaterialDefinition::ALBEDO,
186         {"AnimatedCube_BaseColor.png",
187          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
188        {MaterialDefinition::NORMAL,
189         {"AnimatedCube_BaseColor.png",
190          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
191        {MaterialDefinition::OCCLUSION,
192         {"AnimatedCube_BaseColor.png",
193          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
194        {MaterialDefinition::EMISSIVE,
195         {"AnimatedCube_BaseColor.png",
196          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
197      }},
198     {MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
199        MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
200        MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
201      0,
202      Color::WHITE,
203      1.f,
204      0.f,
205      Vector4(1.000, 0.766, 0.336, 1.0),
206      1.f,
207      1.f,
208      Vector3(0.2, 0.1, 0.0),
209      true,
210      true,
211      true,
212      {
213        {MaterialDefinition::ALBEDO,
214         {"AnimatedCube_BaseColor.png",
215          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
216        {MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
217         {"AnimatedCube_MetallicRoughness.png",
218          SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT)}},
219        {MaterialDefinition::NORMAL,
220         {"AnimatedCube_BaseColor.png",
221          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
222        {MaterialDefinition::OCCLUSION,
223         {"AnimatedCube_BaseColor.png",
224          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
225        {MaterialDefinition::EMISSIVE,
226         {"AnimatedCube_BaseColor.png",
227          SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
228      }},
229   };
230
231   auto iMaterial = materials.begin();
232   for(auto& m : materialGroundTruth)
233   {
234     printf("material %ld\n", iMaterial - materials.begin());
235     auto& md = iMaterial->first;
236     DALI_TEST_EQUAL(md.mFlags, m.mFlags);
237     DALI_TEST_EQUAL(md.mEnvironmentIdx, m.mEnvironmentIdx);
238     DALI_TEST_EQUAL(md.mColor, m.mColor);
239     DALI_TEST_EQUAL(md.mMetallic, m.mMetallic);
240     DALI_TEST_EQUAL(md.mRoughness, m.mRoughness);
241     DALI_TEST_EQUAL(md.mBaseColorFactor, m.mBaseColorFactor);
242     DALI_TEST_EQUAL(md.mNormalScale, m.mNormalScale);
243     DALI_TEST_EQUAL(md.mOcclusionStrength, m.mOcclusionStrength);
244     DALI_TEST_EQUAL(md.mEmissiveFactor, m.mEmissiveFactor);
245     DALI_TEST_EQUAL(md.mNeedAlbedoTexture, m.mNeedAlbedoTexture);
246     DALI_TEST_EQUAL(md.mNeedMetallicRoughnessTexture, m.mNeedMetallicRoughnessTexture);
247     DALI_TEST_EQUAL(md.mNeedNormalTexture, m.mNeedNormalTexture);
248
249     DALI_TEST_EQUAL(md.mTextureStages.size(), m.mTextureStages.size());
250     auto iTexture = md.mTextureStages.begin();
251     for(auto& ts : m.mTextureStages)
252     {
253       printf("texture %ld\n", iTexture - md.mTextureStages.begin());
254       DALI_TEST_EQUAL(iTexture->mSemantic, ts.mSemantic);
255       DALI_TEST_EQUAL(iTexture->mTexture.mImageUri, ts.mTexture.mImageUri);
256       DALI_TEST_EQUAL(uint32_t(iTexture->mTexture.mSamplerFlags), uint32_t(ts.mTexture.mSamplerFlags)); // don't interpret it as a character
257       ++iTexture;
258     }
259     ++iMaterial;
260   }
261
262   auto& meshes = ctx.resources.mMeshes;
263   DALI_TEST_EQUAL(2u, meshes.size());
264
265   using Blob     = MeshDefinition::Blob;
266   using Accessor = MeshDefinition::Accessor;
267   const MeshDefinition meshGroundTruth[]{
268     {
269       0,
270       Geometry::TRIANGLES,
271       "AnimatedCube.bin",
272       Accessor{Blob{0, 0}, {}},
273       Accessor{Blob{0, 0}, {}},
274       Accessor{Blob{0, 0}, {}},
275       Accessor{Blob{0, 0}, {}},
276       Accessor{Blob{0, 0}, {}},
277       Accessor{Blob{0, 0}, {}},
278     },
279     {
280       0,
281       Geometry::TRIANGLES,
282       "AnimatedCube.bin",
283       Accessor{Blob{0, 0}, {}},
284       Accessor{Blob{0, 0}, {}},
285       Accessor{Blob{0, 0}, {}},
286       Accessor{Blob{0, 0}, {}},
287       Accessor{Blob{0, 0}, {}},
288       Accessor{Blob{0, 0}, {}},
289     },
290   };
291
292   auto iMesh = meshes.begin();
293   for(auto& m : meshGroundTruth)
294   {
295     printf("mesh %ld\n", iMesh - meshes.begin());
296
297     auto& md = iMesh->first;
298     DALI_TEST_EQUAL(md.mFlags, m.mFlags);
299     DALI_TEST_EQUAL(md.mPrimitiveType, m.mPrimitiveType);
300     for(auto mp : {
301           &MeshDefinition::mIndices,
302           &MeshDefinition::mPositions,
303           &MeshDefinition::mNormals,
304           &MeshDefinition::mTexCoords,
305           &MeshDefinition::mColors,
306           &MeshDefinition::mTangents,
307           &MeshDefinition::mJoints0,
308           &MeshDefinition::mWeights0})
309     {
310       DALI_TEST_EQUAL((md.*mp).IsDefined(), (m.*mp).IsDefined());
311       DALI_TEST_EQUAL((md.*mp).mBlob.IsDefined(), (m.*mp).mBlob.IsDefined());
312     }
313
314     DALI_TEST_EQUAL(md.mBlendShapeHeader.IsDefined(), m.mBlendShapeHeader.IsDefined());
315
316     ++iMesh;
317   }
318
319   DALI_TEST_EQUAL(2u, ctx.resources.mShaders.size());
320   DALI_TEST_EQUAL(0u, ctx.resources.mSkeletons.size());
321
322   DALI_TEST_EQUAL(3u, ctx.cameras.size());
323   DALI_TEST_EQUAL(0u, ctx.lights.size());
324   DALI_TEST_EQUAL(1u, ctx.animations.size());
325   DALI_TEST_EQUAL(0u, ctx.animationGroups.size());
326
327   END_TEST;
328 }
329
330 int UtcDaliGltfLoaderSuccessShort(void)
331 {
332   TestApplication app;
333
334   const std::string resourcePath = TEST_RESOURCE_DIR "/";
335   auto              pathProvider = [resourcePath](ResourceType::Value) {
336     return resourcePath;
337   };
338
339   Customization::Choices choices;
340   for(auto modelName : {
341         "2CylinderEngine",
342         "AnimatedMorphCube",
343         "AnimatedMorphSphere",
344         "AnimatedTriangle",
345         "BoxAnimated",
346         "CesiumMan",
347         "CesiumMilkTruck",
348         "EnvironmentTest",
349         "MetalRoughSpheres",
350         "MorphPrimitivesTest",
351         "MRendererTest",
352         "SimpleSparseAccessor",
353         "AnimatedCube",
354       })
355   {
356     Context ctx;
357
358     ShaderDefinitionFactory sdf;
359
360     auto& resources = ctx.resources;
361     resources.mEnvironmentMaps.push_back({});
362
363     sdf.SetResources(resources);
364
365     printf("%s\n", modelName);
366     LoadGltfScene(resourcePath + modelName + ".gltf", sdf, ctx.loadResult);
367     DALI_TEST_CHECK(ctx.scene.GetNodeCount() > 0);
368
369     auto& scene = ctx.scene;
370     for(auto iRoot : scene.GetRoots())
371     {
372       struct Visitor : NodeDefinition::IVisitor
373       {
374         struct ResourceReceiver : IResourceReceiver
375         {
376           std::vector<bool> mCounts;
377
378           void Register(ResourceType::Value type, Index id) override
379           {
380             if(type == ResourceType::Mesh)
381             {
382               mCounts[id] = true;
383             }
384           }
385         } receiver;
386
387         void Start(NodeDefinition& n) override
388         {
389           if(n.mRenderable)
390           {
391             n.mRenderable->RegisterResources(receiver);
392           }
393         }
394
395         void Finish(NodeDefinition& n) override
396         {
397         }
398       } visitor;
399       visitor.receiver.mCounts.resize(resources.mMeshes.size(), false);
400
401       scene.Visit(iRoot, choices, visitor);
402       for(uint32_t i0 = 0, i1 = resources.mMeshes.size(); i0 < i1; ++i0)
403       {
404         if(visitor.receiver.mCounts[i0])
405         {
406           auto raw = resources.mMeshes[i0].first.LoadRaw(resourcePath);
407           DALI_TEST_CHECK(!raw.mAttribs.empty());
408
409           resources.mMeshes[i0].second = resources.mMeshes[i0].first.Load(std::move(raw));
410           DALI_TEST_CHECK(resources.mMeshes[i0].second.geometry);
411         }
412       }
413     }
414   }
415
416   END_TEST;
417 }
418
419 int UtcDaliGltfLoaderMRendererTest(void)
420 {
421   Context ctx;
422
423   ShaderDefinitionFactory sdf;
424   sdf.SetResources(ctx.resources);
425   auto& resources = ctx.resources;
426
427   LoadGltfScene(TEST_RESOURCE_DIR "/MRendererTest.gltf", sdf, ctx.loadResult);
428
429   auto& scene = ctx.scene;
430   auto& roots = scene.GetRoots();
431   DALI_TEST_EQUAL(roots.size(), 1u);
432   DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "RootNode");
433   DALI_TEST_EQUAL(scene.GetNode(roots[0])->mScale, Vector3(1.0f, 1.0f, 1.0f));
434
435   DALI_TEST_EQUAL(scene.GetNodeCount(), 1u);
436
437   ViewProjection viewProjection;
438   Transforms     xforms{
439     MatrixStack{},
440     viewProjection};
441   NodeDefinition::CreateParams nodeParams{
442     resources,
443     xforms,
444   };
445
446   Customization::Choices choices;
447
448   TestApplication app;
449
450   Actor root = Actor::New();
451   SetActorCentered(root);
452   for(auto iRoot : roots)
453   {
454     auto resourceRefs = resources.CreateRefCounter();
455     scene.CountResourceRefs(iRoot, choices, resourceRefs);
456     resources.CountEnvironmentReferences(resourceRefs);
457     resources.LoadResources(resourceRefs, ctx.pathProvider);
458     if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
459     {
460       scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
461       scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
462       scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
463       root.Add(actor);
464     }
465   }
466
467   DALI_TEST_EQUAL(root.GetChildCount(), 1u);
468   Actor child = root.GetChildAt(0);
469
470   DALI_TEST_EQUAL(child.GetProperty(Actor::Property::NAME).Get<std::string>(), "RootNode");
471   DALI_TEST_EQUAL(child.GetProperty(Actor::Property::SCALE).Get<Vector3>(), Vector3(1.0f, 1.0f, 1.0f));
472   DALI_TEST_EQUAL(child.GetRendererCount(), 1u);
473   DALI_TEST_EQUAL(child.GetRendererAt(0).GetTextures().GetTextureCount(), 4u);
474
475   END_TEST;
476 }