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-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"
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;
65 std::vector<AnimationDefinition> animations;
66 std::vector<AnimationGroupDefinition> animationGroups;
67 std::vector<CameraParameters> cameras;
68 std::vector<LightParameters> lights;
70 LoadResult loadResult{
79 struct ExceptionMessageStartsWith
81 const std::string_view expected;
83 bool operator()(const std::runtime_error& e)
85 const bool success = (0 == strncmp(e.what(), expected.data(), expected.size()));
88 printf("Expected: %s, got: %s.\n", expected.data(), e.what());
96 int UtcDaliGltfLoaderFailedToLoad(void)
100 ShaderDefinitionFactory sdf;
101 sdf.SetResources(ctx.resources);
103 DALI_TEST_THROW(LoadGltfScene("non-existent.gltf", sdf, ctx.loadResult),
105 ExceptionMessageStartsWith{"Failed to load"});
107 DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
108 DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
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());
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());
124 int UtcDaliGltfLoaderFailedToParse(void)
128 ShaderDefinitionFactory sdf;
129 sdf.SetResources(ctx.resources);
131 DALI_TEST_THROW(LoadGltfScene(TEST_RESOURCE_DIR "/invalid.gltf", sdf, ctx.loadResult),
133 ExceptionMessageStartsWith{"Failed to parse"});
135 DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
136 DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
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());
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());
152 int UtcDaliGltfLoaderSuccess1(void)
156 ShaderDefinitionFactory sdf;
157 sdf.SetResources(ctx.resources);
159 LoadGltfScene(TEST_RESOURCE_DIR "/AnimatedCube.gltf", sdf, ctx.loadResult);
161 DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
162 DALI_TEST_EQUAL(6u, ctx.scene.GetNodeCount());
164 // Default envmap is used
165 DALI_TEST_EQUAL(1u, ctx.resources.mEnvironmentMaps.size());
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::SPECULAR | MaterialDefinition::SPECULAR_COLOR |
172 (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
177 Vector4(1.000, 0.766, 0.336, 1.0),
180 Vector3(0.2, 0.1, 0.0),
189 {MaterialDefinition::ALBEDO,
190 {"AnimatedCube_BaseColor.png",
191 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
192 {MaterialDefinition::NORMAL,
193 {"AnimatedCube_BaseColor.png",
194 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
195 {MaterialDefinition::OCCLUSION,
196 {"AnimatedCube_BaseColor.png",
197 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
198 {MaterialDefinition::EMISSIVE,
199 {"AnimatedCube_BaseColor.png",
200 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
201 {MaterialDefinition::SPECULAR,
202 {"AnimatedCube_BaseColor.png",
203 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
204 {MaterialDefinition::SPECULAR_COLOR,
205 {"AnimatedCube_BaseColor.png",
206 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
208 {MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
209 MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
210 MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
215 Vector4(1.000, 0.766, 0.336, 1.0),
218 Vector3(0.2, 0.1, 0.0),
227 {MaterialDefinition::ALBEDO,
228 {"AnimatedCube_BaseColor.png",
229 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
230 {MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
231 {"AnimatedCube_MetallicRoughness.png",
232 SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT)}},
233 {MaterialDefinition::NORMAL,
234 {"AnimatedCube_BaseColor.png",
235 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
236 {MaterialDefinition::OCCLUSION,
237 {"AnimatedCube_BaseColor.png",
238 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
239 {MaterialDefinition::EMISSIVE,
240 {"AnimatedCube_BaseColor.png",
241 SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
245 auto iMaterial = materials.begin();
246 for(auto& m : materialGroundTruth)
248 printf("material %ld\n", iMaterial - materials.begin());
249 auto& md = iMaterial->first;
250 DALI_TEST_EQUAL(md.mFlags, m.mFlags);
251 DALI_TEST_EQUAL(md.mEnvironmentIdx, m.mEnvironmentIdx);
252 DALI_TEST_EQUAL(md.mColor, m.mColor);
253 DALI_TEST_EQUAL(md.mMetallic, m.mMetallic);
254 DALI_TEST_EQUAL(md.mRoughness, m.mRoughness);
255 DALI_TEST_EQUAL(md.mBaseColorFactor, m.mBaseColorFactor);
256 DALI_TEST_EQUAL(md.mNormalScale, m.mNormalScale);
257 DALI_TEST_EQUAL(md.mOcclusionStrength, m.mOcclusionStrength);
258 DALI_TEST_EQUAL(md.mEmissiveFactor, m.mEmissiveFactor);
259 DALI_TEST_EQUAL(md.mDielectricSpecular, m.mDielectricSpecular);
260 DALI_TEST_EQUAL(md.mSpecularFactor, m.mSpecularFactor);
261 DALI_TEST_EQUAL(md.mSpecularColorFactor, m.mSpecularColorFactor);
262 DALI_TEST_EQUAL(md.mNeedAlbedoTexture, m.mNeedAlbedoTexture);
263 DALI_TEST_EQUAL(md.mNeedMetallicRoughnessTexture, m.mNeedMetallicRoughnessTexture);
264 DALI_TEST_EQUAL(md.mNeedNormalTexture, m.mNeedNormalTexture);
266 DALI_TEST_EQUAL(md.mTextureStages.size(), m.mTextureStages.size());
267 auto iTexture = md.mTextureStages.begin();
268 for(auto& ts : m.mTextureStages)
270 printf("texture %ld\n", iTexture - md.mTextureStages.begin());
271 DALI_TEST_EQUAL(iTexture->mSemantic, ts.mSemantic);
272 DALI_TEST_EQUAL(iTexture->mTexture.mImageUri, ts.mTexture.mImageUri);
273 DALI_TEST_EQUAL(uint32_t(iTexture->mTexture.mSamplerFlags), uint32_t(ts.mTexture.mSamplerFlags)); // don't interpret it as a character
279 auto& meshes = ctx.resources.mMeshes;
280 DALI_TEST_EQUAL(2u, meshes.size());
282 using Blob = MeshDefinition::Blob;
283 using Accessor = MeshDefinition::Accessor;
284 const MeshDefinition meshGroundTruth[]{
289 Accessor{Blob{0, 0}, {}},
290 Accessor{Blob{0, 0}, {}},
291 Accessor{Blob{0, 0}, {}},
292 Accessor{Blob{0, 0}, {}},
293 Accessor{Blob{0, 0}, {}},
294 Accessor{Blob{0, 0}, {}},
300 Accessor{Blob{0, 0}, {}},
301 Accessor{Blob{0, 0}, {}},
302 Accessor{Blob{0, 0}, {}},
303 Accessor{Blob{0, 0}, {}},
304 Accessor{Blob{0, 0}, {}},
305 Accessor{Blob{0, 0}, {}},
309 auto iMesh = meshes.begin();
310 for(auto& m : meshGroundTruth)
312 printf("mesh %ld\n", iMesh - meshes.begin());
314 auto& md = iMesh->first;
315 DALI_TEST_EQUAL(md.mFlags, m.mFlags);
316 DALI_TEST_EQUAL(md.mPrimitiveType, m.mPrimitiveType);
318 &MeshDefinition::mIndices,
319 &MeshDefinition::mPositions,
320 &MeshDefinition::mNormals,
321 &MeshDefinition::mTexCoords,
322 &MeshDefinition::mColors,
323 &MeshDefinition::mTangents,
324 &MeshDefinition::mJoints0,
325 &MeshDefinition::mWeights0})
327 DALI_TEST_EQUAL((md.*mp).IsDefined(), (m.*mp).IsDefined());
328 DALI_TEST_EQUAL((md.*mp).mBlob.IsDefined(), (m.*mp).mBlob.IsDefined());
331 DALI_TEST_EQUAL(md.mBlendShapeHeader.IsDefined(), m.mBlendShapeHeader.IsDefined());
336 DALI_TEST_EQUAL(2u, ctx.resources.mShaders.size());
337 DALI_TEST_EQUAL(0u, ctx.resources.mSkeletons.size());
339 DALI_TEST_EQUAL(3u, ctx.cameras.size());
340 DALI_TEST_EQUAL(0u, ctx.lights.size());
341 DALI_TEST_EQUAL(1u, ctx.animations.size());
342 DALI_TEST_EQUAL(0u, ctx.animationGroups.size());
347 int UtcDaliGltfLoaderSuccessShort(void)
351 const std::string resourcePath = TEST_RESOURCE_DIR "/";
352 auto pathProvider = [resourcePath](ResourceType::Value) {
356 Customization::Choices choices;
357 for(auto modelName : {
360 "AnimatedMorphSphere",
367 "MorphPrimitivesTest",
369 "SimpleSparseAccessor",
375 ShaderDefinitionFactory sdf;
377 auto& resources = ctx.resources;
378 resources.mEnvironmentMaps.push_back({});
380 sdf.SetResources(resources);
382 printf("%s\n", modelName);
383 LoadGltfScene(resourcePath + modelName + ".gltf", sdf, ctx.loadResult);
384 DALI_TEST_CHECK(ctx.scene.GetNodeCount() > 0);
386 auto& scene = ctx.scene;
387 for(auto iRoot : scene.GetRoots())
389 struct Visitor : NodeDefinition::IVisitor
391 struct ResourceReceiver : IResourceReceiver
393 std::vector<bool> mCounts;
395 void Register(ResourceType::Value type, Index id) override
397 if(type == ResourceType::Mesh)
404 void Start(NodeDefinition& n) override
406 for(auto& renderable : n.mRenderables)
408 renderable->RegisterResources(receiver);
412 void Finish(NodeDefinition& n) override
416 visitor.receiver.mCounts.resize(resources.mMeshes.size(), false);
418 scene.Visit(iRoot, choices, visitor);
419 for(uint32_t i0 = 0, i1 = resources.mMeshes.size(); i0 < i1; ++i0)
421 if(visitor.receiver.mCounts[i0])
423 auto raw = resources.mMeshes[i0].first.LoadRaw(resourcePath);
424 DALI_TEST_CHECK(!raw.mAttribs.empty());
426 resources.mMeshes[i0].second = resources.mMeshes[i0].first.Load(std::move(raw));
427 DALI_TEST_CHECK(resources.mMeshes[i0].second.geometry);
436 int UtcDaliGltfLoaderMRendererTest(void)
440 ShaderDefinitionFactory sdf;
441 sdf.SetResources(ctx.resources);
442 auto& resources = ctx.resources;
444 LoadGltfScene(TEST_RESOURCE_DIR "/MRendererTest.gltf", sdf, ctx.loadResult);
446 auto& scene = ctx.scene;
447 auto& roots = scene.GetRoots();
448 DALI_TEST_EQUAL(roots.size(), 1u);
449 DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "RootNode");
450 DALI_TEST_EQUAL(scene.GetNode(roots[0])->mScale, Vector3(1.0f, 1.0f, 1.0f));
452 DALI_TEST_EQUAL(scene.GetNodeCount(), 1u);
454 ViewProjection viewProjection;
458 NodeDefinition::CreateParams nodeParams{
463 Customization::Choices choices;
467 Actor root = Actor::New();
468 SetActorCentered(root);
469 for(auto iRoot : roots)
471 auto resourceRefs = resources.CreateRefCounter();
472 scene.CountResourceRefs(iRoot, choices, resourceRefs);
473 resources.CountEnvironmentReferences(resourceRefs);
474 resources.LoadResources(resourceRefs, ctx.pathProvider);
475 if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
477 scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
478 scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
479 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
484 DALI_TEST_EQUAL(root.GetChildCount(), 1u);
485 Actor child = root.GetChildAt(0);
487 DALI_TEST_EQUAL(child.GetProperty(Actor::Property::NAME).Get<std::string>(), "RootNode");
488 DALI_TEST_EQUAL(child.GetProperty(Actor::Property::SCALE).Get<Vector3>(), Vector3(1.0f, 1.0f, 1.0f));
489 DALI_TEST_EQUAL(child.GetRendererCount(), 1u);
490 DALI_TEST_EQUAL(child.GetRendererAt(0).GetTextures().GetTextureCount(), 4u);