2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 // Enable debug log for test coverage
19 #define DEBUG_ENABLED 1
21 #include <dali-scene3d/public-api/loader/gltf2-loader.h>
22 #include <dali-scene3d/public-api/loader/load-result.h>
23 #include <dali-scene3d/public-api/loader/resource-bundle.h>
24 #include <dali-scene3d/public-api/loader/scene-definition.h>
25 #include <dali-scene3d/public-api/loader/shader-definition-factory.h>
26 #include <dali-test-suite-utils.h>
27 #include <string_view>
30 using namespace Dali::Scene3D::Loader;
32 #define DALI_TEST_THROW(expression, exception, predicate) \
34 bool daliTestThrowSuccess__ = false; \
41 printf("No exception was thrown.\n"); \
43 catch(std::decay<exception>::type & ex) \
45 daliTestThrowSuccess__ = predicate(ex); \
49 printf("Wrong type of exception thrown.\n"); \
51 DALI_TEST_CHECK(daliTestThrowSuccess__); \
58 ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
59 return TEST_RESOURCE_DIR "/";
62 ResourceBundle resources;
63 SceneDefinition scene;
64 SceneMetadata metaData;
66 std::vector<AnimationDefinition> animations;
67 std::vector<AnimationGroupDefinition> animationGroups;
68 std::vector<CameraParameters> cameras;
69 std::vector<LightParameters> lights;
71 LoadResult loadResult{
81 struct ExceptionMessageStartsWith
83 const std::string_view expected;
85 bool operator()(const std::runtime_error& e)
87 const bool success = (0 == strncmp(e.what(), expected.data(), expected.size()));
90 printf("Expected: %s, got: %s.\n", expected.data(), e.what());
98 int UtcDaliGltfLoaderFailedToLoad(void)
102 ShaderDefinitionFactory sdf;
103 sdf.SetResources(ctx.resources);
105 DALI_TEST_THROW(LoadGltfScene("non-existent.gltf", sdf, ctx.loadResult),
107 ExceptionMessageStartsWith{"Failed to load"});
109 DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
110 DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
112 DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
113 DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
114 DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
115 DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
116 DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
118 DALI_TEST_EQUAL(0, ctx.cameras.size());
119 DALI_TEST_EQUAL(0, ctx.lights.size());
120 DALI_TEST_EQUAL(0, ctx.animations.size());
121 DALI_TEST_EQUAL(0, ctx.animationGroups.size());
126 int UtcDaliGltfLoaderFailedToParse(void)
130 ShaderDefinitionFactory sdf;
131 sdf.SetResources(ctx.resources);
133 DALI_TEST_THROW(LoadGltfScene(TEST_RESOURCE_DIR "/invalid.gltf", sdf, ctx.loadResult),
135 ExceptionMessageStartsWith{"Failed to parse"});
137 DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
138 DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
140 DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
141 DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
142 DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
143 DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
144 DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
146 DALI_TEST_EQUAL(0, ctx.cameras.size());
147 DALI_TEST_EQUAL(0, ctx.lights.size());
148 DALI_TEST_EQUAL(0, ctx.animations.size());
149 DALI_TEST_EQUAL(0, ctx.animationGroups.size());
154 int UtcDaliGltfLoaderSuccess1(void)
158 LoadSceneMetadata(TEST_RESOURCE_DIR "/AnimatedCube.metadata", ctx.metaData);
160 std::unordered_map<std::string, ImageMetadata> imageMetadataGroundTruth;
161 imageMetadataGroundTruth["AnimatedCube_BaseColor.png"] = ImageMetadata{ImageDimensions(256, 256), Dali::SamplingMode::BOX_THEN_NEAREST};
162 imageMetadataGroundTruth["AnimatedCube_MetallicRoughness.png"] = ImageMetadata{ImageDimensions(256, 256), Dali::SamplingMode::NEAREST};
164 auto metaData = ctx.metaData.mImageMetadata.begin();
165 for(auto& groundTruth : imageMetadataGroundTruth)
167 DALI_TEST_EQUAL(groundTruth.first, metaData->first);
168 DALI_TEST_EQUAL(groundTruth.second.mMinSize, metaData->second.mMinSize);
169 DALI_TEST_EQUAL(groundTruth.second.mSamplingMode, metaData->second.mSamplingMode);
173 ShaderDefinitionFactory sdf;
174 sdf.SetResources(ctx.resources);
176 LoadGltfScene(TEST_RESOURCE_DIR "/AnimatedCube.gltf", sdf, ctx.loadResult);
178 DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
179 DALI_TEST_EQUAL(6u, ctx.scene.GetNodeCount());
181 // Default envmap is used
182 DALI_TEST_EQUAL(1u, ctx.resources.mEnvironmentMaps.size());
186 Customization::Choices choices;
187 for(auto iRoot : ctx.scene.GetRoots())
189 auto resourceRefs = ctx.resources.CreateRefCounter();
190 ctx.scene.CountResourceRefs(iRoot, choices, resourceRefs);
191 ctx.resources.CountEnvironmentReferences(resourceRefs);
192 ctx.resources.LoadResources(resourceRefs, ctx.pathProvider);
195 auto& materials = ctx.resources.mMaterials;
196 DALI_TEST_EQUAL(2u, materials.size());
197 const MaterialDefinition materialGroundTruth[]{
198 {MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
199 MaterialDefinition::NORMAL | MaterialDefinition::SPECULAR | MaterialDefinition::SPECULAR_COLOR |
200 (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
205 Vector4(1.000, 0.766, 0.336, 1.0),
208 Vector3(0.2, 0.1, 0.0),
217 {MaterialDefinition::ALBEDO,
218 {"AnimatedCube_BaseColor.png",
219 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
220 ImageDimensions(256, 256),
221 SamplingMode::BOX_THEN_NEAREST}},
222 {MaterialDefinition::NORMAL,
223 {"AnimatedCube_BaseColor.png",
224 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
225 ImageDimensions(256, 256),
226 SamplingMode::BOX_THEN_NEAREST}},
227 {MaterialDefinition::OCCLUSION,
228 {"AnimatedCube_BaseColor.png",
229 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
230 ImageDimensions(256, 256),
231 SamplingMode::BOX_THEN_NEAREST}},
232 {MaterialDefinition::EMISSIVE,
233 {"AnimatedCube_BaseColor.png",
234 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
235 ImageDimensions(256, 256),
236 SamplingMode::BOX_THEN_NEAREST}},
237 {MaterialDefinition::SPECULAR,
238 {"AnimatedCube_BaseColor.png",
239 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
240 ImageDimensions(256, 256),
241 SamplingMode::BOX_THEN_NEAREST}},
242 {MaterialDefinition::SPECULAR_COLOR,
243 {"AnimatedCube_BaseColor.png",
244 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
245 ImageDimensions(256, 256),
246 SamplingMode::BOX_THEN_NEAREST}},
248 {MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
249 MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
250 MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
255 Vector4(1.000, 0.766, 0.336, 1.0),
258 Vector3(0.2, 0.1, 0.0),
267 {MaterialDefinition::ALBEDO,
268 {"AnimatedCube_BaseColor.png",
269 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
270 ImageDimensions(256, 256),
271 SamplingMode::BOX_THEN_NEAREST}},
272 {MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
273 {"AnimatedCube_MetallicRoughness.png",
274 SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT),
275 ImageDimensions(256, 256),
276 SamplingMode::NEAREST}},
277 {MaterialDefinition::NORMAL,
278 {"AnimatedCube_BaseColor.png",
279 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
280 ImageDimensions(256, 256),
281 SamplingMode::BOX_THEN_NEAREST}},
282 {MaterialDefinition::OCCLUSION,
283 {"AnimatedCube_BaseColor.png",
284 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
285 ImageDimensions(256, 256),
286 SamplingMode::BOX_THEN_NEAREST}},
287 {MaterialDefinition::EMISSIVE,
288 {"AnimatedCube_BaseColor.png",
289 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT),
290 ImageDimensions(256, 256),
291 SamplingMode::BOX_THEN_NEAREST}},
295 auto iMaterial = materials.begin();
296 auto iMetadata = ctx.metaData.mImageMetadata.begin();
297 for(auto& m : materialGroundTruth)
299 printf("material %ld\n", iMaterial - materials.begin());
300 auto& md = iMaterial->first;
301 DALI_TEST_EQUAL(md.mFlags, m.mFlags);
302 DALI_TEST_EQUAL(md.mEnvironmentIdx, m.mEnvironmentIdx);
303 DALI_TEST_EQUAL(md.mColor, m.mColor);
304 DALI_TEST_EQUAL(md.mMetallic, m.mMetallic);
305 DALI_TEST_EQUAL(md.mRoughness, m.mRoughness);
306 DALI_TEST_EQUAL(md.mBaseColorFactor, m.mBaseColorFactor);
307 DALI_TEST_EQUAL(md.mNormalScale, m.mNormalScale);
308 DALI_TEST_EQUAL(md.mOcclusionStrength, m.mOcclusionStrength);
309 DALI_TEST_EQUAL(md.mEmissiveFactor, m.mEmissiveFactor);
310 DALI_TEST_EQUAL(md.mDielectricSpecular, m.mDielectricSpecular);
311 DALI_TEST_EQUAL(md.mSpecularFactor, m.mSpecularFactor);
312 DALI_TEST_EQUAL(md.mSpecularColorFactor, m.mSpecularColorFactor);
313 DALI_TEST_EQUAL(md.mNeedAlbedoTexture, m.mNeedAlbedoTexture);
314 DALI_TEST_EQUAL(md.mNeedMetallicRoughnessTexture, m.mNeedMetallicRoughnessTexture);
315 DALI_TEST_EQUAL(md.mNeedNormalTexture, m.mNeedNormalTexture);
317 DALI_TEST_EQUAL(md.mTextureStages.size(), m.mTextureStages.size());
318 auto iTexture = md.mTextureStages.begin();
319 for(auto& ts : m.mTextureStages)
321 printf("texture %ld\n", iTexture - md.mTextureStages.begin());
322 DALI_TEST_EQUAL(iTexture->mSemantic, ts.mSemantic);
323 DALI_TEST_EQUAL(iTexture->mTexture.mImageUri, ts.mTexture.mImageUri);
324 DALI_TEST_EQUAL(uint32_t(iTexture->mTexture.mSamplerFlags), uint32_t(ts.mTexture.mSamplerFlags)); // don't interpret it as a character
325 DALI_TEST_EQUAL(iTexture->mTexture.mMinImageDimensions, ts.mTexture.mMinImageDimensions);
326 DALI_TEST_EQUAL(iTexture->mTexture.mSamplingMode, ts.mTexture.mSamplingMode);
334 auto& meshes = ctx.resources.mMeshes;
335 DALI_TEST_EQUAL(2u, meshes.size());
337 using Blob = MeshDefinition::Blob;
338 using Accessor = MeshDefinition::Accessor;
339 const MeshDefinition meshGroundTruth[]{
344 Accessor{Blob{0, 0}, {}},
345 Accessor{Blob{0, 0}, {}},
346 Accessor{Blob{0, 0}, {}},
347 Accessor{Blob{0, 0}, {}},
348 Accessor{Blob{0, 0}, {}},
349 Accessor{Blob{0, 0}, {}},
355 Accessor{Blob{0, 0}, {}},
356 Accessor{Blob{0, 0}, {}},
357 Accessor{Blob{0, 0}, {}},
358 Accessor{Blob{0, 0}, {}},
359 Accessor{Blob{0, 0}, {}},
360 Accessor{Blob{0, 0}, {}},
364 auto iMesh = meshes.begin();
365 for(auto& m : meshGroundTruth)
367 printf("mesh %ld\n", iMesh - meshes.begin());
369 auto& md = iMesh->first;
370 DALI_TEST_EQUAL(md.mFlags, m.mFlags);
371 DALI_TEST_EQUAL(md.mPrimitiveType, m.mPrimitiveType);
373 &MeshDefinition::mIndices,
374 &MeshDefinition::mPositions,
375 &MeshDefinition::mNormals,
376 &MeshDefinition::mTexCoords,
377 &MeshDefinition::mColors,
378 &MeshDefinition::mTangents,
379 &MeshDefinition::mJoints0,
380 &MeshDefinition::mWeights0})
382 DALI_TEST_EQUAL((md.*mp).IsDefined(), (m.*mp).IsDefined());
383 DALI_TEST_EQUAL((md.*mp).mBlob.IsDefined(), (m.*mp).mBlob.IsDefined());
386 DALI_TEST_EQUAL(md.mBlendShapeHeader.IsDefined(), m.mBlendShapeHeader.IsDefined());
391 DALI_TEST_EQUAL(2u, ctx.resources.mShaders.size());
392 DALI_TEST_EQUAL(0u, ctx.resources.mSkeletons.size());
394 DALI_TEST_EQUAL(3u, ctx.cameras.size());
395 DALI_TEST_EQUAL(0u, ctx.lights.size());
396 DALI_TEST_EQUAL(1u, ctx.animations.size());
397 DALI_TEST_EQUAL(0u, ctx.animationGroups.size());
402 int UtcDaliGltfLoaderSuccessShort(void)
406 const std::string resourcePath = TEST_RESOURCE_DIR "/";
407 auto pathProvider = [resourcePath](ResourceType::Value) {
411 Customization::Choices choices;
412 for(auto modelName : {
415 "AnimatedMorphSphere",
422 "MorphPrimitivesTest",
424 "SimpleSparseAccessor",
430 ShaderDefinitionFactory sdf;
432 auto& resources = ctx.resources;
433 resources.mEnvironmentMaps.push_back({});
435 sdf.SetResources(resources);
437 printf("%s\n", modelName);
438 LoadGltfScene(resourcePath + modelName + ".gltf", sdf, ctx.loadResult);
439 DALI_TEST_CHECK(ctx.scene.GetNodeCount() > 0);
441 auto& scene = ctx.scene;
442 for(auto iRoot : scene.GetRoots())
444 struct Visitor : NodeDefinition::IVisitor
446 struct ResourceReceiver : IResourceReceiver
448 std::vector<bool> mCounts;
450 void Register(ResourceType::Value type, Index id) override
452 if(type == ResourceType::Mesh)
459 void Start(NodeDefinition& n) override
461 for(auto& renderable : n.mRenderables)
463 renderable->RegisterResources(receiver);
467 void Finish(NodeDefinition& n) override
471 visitor.receiver.mCounts.resize(resources.mMeshes.size(), false);
473 scene.Visit(iRoot, choices, visitor);
474 for(uint32_t i0 = 0, i1 = resources.mMeshes.size(); i0 < i1; ++i0)
476 if(visitor.receiver.mCounts[i0])
478 auto raw = resources.mMeshes[i0].first.LoadRaw(resourcePath);
479 DALI_TEST_CHECK(!raw.mAttribs.empty());
481 resources.mMeshes[i0].second = resources.mMeshes[i0].first.Load(std::move(raw));
482 DALI_TEST_CHECK(resources.mMeshes[i0].second.geometry);
491 int UtcDaliGltfLoaderMRendererTest(void)
495 ShaderDefinitionFactory sdf;
496 sdf.SetResources(ctx.resources);
497 auto& resources = ctx.resources;
499 LoadGltfScene(TEST_RESOURCE_DIR "/MRendererTest.gltf", sdf, ctx.loadResult);
501 auto& scene = ctx.scene;
502 auto& roots = scene.GetRoots();
503 DALI_TEST_EQUAL(roots.size(), 1u);
504 DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "RootNode");
505 DALI_TEST_EQUAL(scene.GetNode(roots[0])->mScale, Vector3(1.0f, 1.0f, 1.0f));
507 DALI_TEST_EQUAL(scene.GetNodeCount(), 1u);
509 ViewProjection viewProjection;
513 NodeDefinition::CreateParams nodeParams{
518 Customization::Choices choices;
522 Actor root = Actor::New();
523 SetActorCentered(root);
524 for(auto iRoot : roots)
526 auto resourceRefs = resources.CreateRefCounter();
527 scene.CountResourceRefs(iRoot, choices, resourceRefs);
528 resources.CountEnvironmentReferences(resourceRefs);
529 resources.LoadResources(resourceRefs, ctx.pathProvider);
530 if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
532 scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
533 scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
534 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
539 DALI_TEST_EQUAL(root.GetChildCount(), 1u);
540 Actor child = root.GetChildAt(0);
542 DALI_TEST_EQUAL(child.GetProperty(Actor::Property::NAME).Get<std::string>(), "RootNode");
543 DALI_TEST_EQUAL(child.GetProperty(Actor::Property::SCALE).Get<Vector3>(), Vector3(1.0f, 1.0f, 1.0f));
544 DALI_TEST_EQUAL(child.GetRendererCount(), 1u);
545 DALI_TEST_EQUAL(child.GetRendererAt(0).GetTextures().GetTextureCount(), 4u);