1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2021, assimp team
7 
8 All rights reserved.
9 
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
13 
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
17 
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
22 
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
41 #include "AbstractImportExportBase.h"
42 #include "UnitTestPCH.h"
43 
44 #include <assimp/commonMetaData.h>
45 #include <assimp/postprocess.h>
46 #include <assimp/scene.h>
47 #include <assimp/Exporter.hpp>
48 #include <assimp/Importer.hpp>
49 #include <assimp/LogStream.hpp>
50 #include <assimp/DefaultLogger.hpp>
51 
52 #include <rapidjson/schema.h>
53 
54 #include <array>
55 
56 #include <assimp/material.h>
57 #include <assimp/GltfMaterial.h>
58 
59 using namespace Assimp;
60 
61 class utglTF2ImportExport : public AbstractImportExportBase {
62 public:
importerMatTest(const char * file,bool spec_gloss,std::array<aiTextureMapMode,2> exp_modes={ aiTextureMapMode_Wrap, aiTextureMapMode_Wrap })63     virtual bool importerMatTest(const char *file, bool spec_gloss, std::array<aiTextureMapMode, 2> exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) {
64         Assimp::Importer importer;
65         const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure);
66         EXPECT_NE(scene, nullptr);
67         if (!scene) {
68             return false;
69         }
70 
71         EXPECT_TRUE(scene->HasMaterials());
72         if (!scene->HasMaterials()) {
73             return false;
74         }
75         const aiMaterial *material = scene->mMaterials[0];
76 
77         // This Material should be a PBR
78         aiShadingMode shadingMode;
79         EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode));
80         EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode);
81 
82         // Should import the texture as diffuse and as base color
83         aiString path;
84         std::array<aiTextureMapMode,2> modes;
85         EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr,
86                                             nullptr, nullptr, modes.data()));
87         EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png");
88         EXPECT_EQ(exp_modes, modes);
89 
90         // Also as Base Color
91         EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr,
92                                             nullptr, nullptr, modes.data()));
93         EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png");
94         EXPECT_EQ(exp_modes, modes);
95 
96         // Should have a MetallicFactor (default is 1.0)
97         ai_real metal_factor = ai_real(0.5);
98         EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor));
99         EXPECT_EQ(ai_real(0.0), metal_factor);
100 
101         // And a roughness factor (default is 1.0)
102         ai_real roughness_factor = ai_real(0.5);
103         EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor));
104         EXPECT_EQ(ai_real(1.0), roughness_factor);
105 
106         aiColor3D spec_color = { 0, 0, 0 };
107         ai_real glossiness = ai_real(0.5);
108         if (spec_gloss) {
109             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
110             constexpr ai_real spec_val(0.20000000298023225); // From the file
111             EXPECT_EQ(spec_val, spec_color.r);
112             EXPECT_EQ(spec_val, spec_color.g);
113             EXPECT_EQ(spec_val, spec_color.b);
114             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
115             EXPECT_EQ(ai_real(1.0), glossiness);
116         } else {
117             EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
118             EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
119         }
120 
121         return true;
122     }
123 
binaryImporterTest()124     virtual bool binaryImporterTest() {
125         Assimp::Importer importer;
126         const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb",
127                 aiProcess_ValidateDataStructure);
128         return nullptr != scene;
129     }
130 
131 #ifndef ASSIMP_BUILD_NO_EXPORT
exporterTest()132     virtual bool exporterTest() {
133         Assimp::Importer importer;
134         Assimp::Exporter exporter;
135         const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
136                 aiProcess_ValidateDataStructure);
137         EXPECT_NE(nullptr, scene);
138         EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.gltf"));
139 
140         return true;
141     }
142 #endif // ASSIMP_BUILD_NO_EXPORT
143 };
144 
TEST_F(utglTF2ImportExport,importglTF2FromFileTest)145 TEST_F(utglTF2ImportExport, importglTF2FromFileTest) {
146     EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp}));
147 }
148 
TEST_F(utglTF2ImportExport,importBinaryglTF2FromFileTest)149 TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) {
150     EXPECT_TRUE(binaryImporterTest());
151 }
152 
TEST_F(utglTF2ImportExport,importglTF2_KHR_materials_pbrSpecularGlossiness)153 TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) {
154     EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true));
155 }
156 
VerifyClearCoatScene(const aiScene * scene)157 void VerifyClearCoatScene(const aiScene *scene) {
158         ASSERT_NE(nullptr, scene);
159 
160     ASSERT_TRUE(scene->HasMaterials());
161 
162     // Find a specific Clearcoat material and check the values
163     const aiString partial_coated("Partial_Coated");
164     bool found_partial_coat = false;
165     for (size_t i = 0; i < scene->mNumMaterials; ++i) {
166         const aiMaterial *material = scene->mMaterials[i];
167         ASSERT_NE(nullptr, material);
168         if (material->GetName() == partial_coated) {
169             found_partial_coat = true;
170 
171             ai_real clearcoat_factor(0.0f);
172             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor));
173             EXPECT_EQ(ai_real(1.0f), clearcoat_factor);
174 
175             ai_real clearcoat_rough_factor(0.0f);
176             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor));
177             EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor);
178 
179             // Should import the texture as diffuse and as base color
180             aiString path;
181             std::array<aiTextureMapMode, 2> modes;
182             static const std::array<aiTextureMapMode, 2> exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap };
183             EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr,
184                                                 nullptr, nullptr, modes.data()));
185             EXPECT_STREQ(path.C_Str(), "PartialCoating.png");
186             EXPECT_EQ(exp_modes, modes);
187         }
188     }
189     EXPECT_TRUE(found_partial_coat);
190 }
191 
TEST_F(utglTF2ImportExport,importglTF2_KHR_materials_clearcoat)192 TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) {
193     Assimp::Importer importer;
194     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure);
195     VerifyClearCoatScene(scene);
196 }
197 
198 #ifndef ASSIMP_BUILD_NO_EXPORT
199 
TEST_F(utglTF2ImportExport,importglTF2AndExport_KHR_materials_clearcoat)200 TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) {
201     {
202         Assimp::Importer importer;
203         Assimp::Exporter exporter;
204         const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure);
205         ASSERT_NE(nullptr, scene);
206         // Export
207         EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb"));
208     }
209 
210     // And re-import
211     Assimp::Importer importer;
212     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure);
213     VerifyClearCoatScene(scene);
214 }
215 
TEST_F(utglTF2ImportExport,importglTF2AndExport_KHR_materials_pbrSpecularGlossiness)216 TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) {
217     Assimp::Importer importer;
218     Assimp::Exporter exporter;
219     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf",
220             aiProcess_ValidateDataStructure);
221     EXPECT_NE(nullptr, scene);
222     // Export
223     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb"));
224 
225     // And re-import
226     EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true));
227 }
228 
TEST_F(utglTF2ImportExport,importglTF2AndExportToOBJ)229 TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) {
230     Assimp::Importer importer;
231     Assimp::Exporter exporter;
232     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
233             aiProcess_ValidateDataStructure);
234     EXPECT_NE(nullptr, scene);
235     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured_out.obj"));
236 }
237 
TEST_F(utglTF2ImportExport,importglTF2EmbeddedAndExportToOBJ)238 TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) {
239     Assimp::Importer importer;
240     Assimp::Exporter exporter;
241     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf",
242             aiProcess_ValidateDataStructure);
243     EXPECT_NE(nullptr, scene);
244     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj"));
245 }
246 
247 #endif // ASSIMP_BUILD_NO_EXPORT
248 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModePointsWithoutIndices)249 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) {
250     Assimp::Importer importer;
251     //Points without indices
252     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf", aiProcess_ValidateDataStructure);
253     EXPECT_NE(nullptr, scene);
254     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 1024u);
255     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
256         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 1u);
257         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i);
258     }
259 }
260 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeLinesWithoutIndices)261 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLinesWithoutIndices) {
262     Assimp::Importer importer;
263     //Lines without indices
264     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf", aiProcess_ValidateDataStructure);
265     EXPECT_NE(nullptr, scene);
266     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 8u);
267     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
268         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2u);
269         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i * 2u);
270         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], i * 2u + 1u);
271     }
272 }
273 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeLinesLoopWithoutIndices)274 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLinesLoopWithoutIndices) {
275     Assimp::Importer importer;
276     //Lines loop without indices
277     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf", aiProcess_ValidateDataStructure);
278     EXPECT_NE(nullptr, scene);
279     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
280 
281     std::array<unsigned int, 5> l1 = { { 0u, 1u, 2u, 3u, 0u } };
282     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
283     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
284         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2u);
285         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
286         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], l1[i + 1u]);
287     }
288 }
289 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeLinesStripWithoutIndices)290 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLinesStripWithoutIndices) {
291     Assimp::Importer importer;
292     //Lines strip without indices
293     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf", aiProcess_ValidateDataStructure);
294     EXPECT_NE(nullptr, scene);
295     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 5u);
296 
297     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
298     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
299         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 2u);
300         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i);
301         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], i + 1u);
302     }
303 }
304 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeTrianglesStripWithoutIndices)305 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesStripWithoutIndices) {
306     Assimp::Importer importer;
307     //Triangles strip without indices
308     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf", aiProcess_ValidateDataStructure);
309     EXPECT_NE(nullptr, scene);
310     EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
311     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
312     std::array<unsigned int, 3> f1 = { { 0u, 1u, 2u } };
313     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
314     for (unsigned int i = 0; i < 3; ++i) {
315         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
316     }
317 
318     std::array<unsigned int, 3> f2 = { { 2u, 1u, 3u } };
319     EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
320     for (size_t i = 0; i < 3; ++i) {
321         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
322     }
323 }
324 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeTrianglesFanWithoutIndices)325 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesFanWithoutIndices) {
326     Assimp::Importer importer;
327     //Triangles fan without indices
328     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf", aiProcess_ValidateDataStructure);
329     EXPECT_NE(nullptr, scene);
330     EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
331     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
332     std::array<unsigned int, 3> f1 = { { 0u, 1u, 2u } };
333     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
334     for (size_t i = 0; i < 3; ++i) {
335         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
336     }
337 
338     std::array<unsigned int, 3> f2 = { { 0u, 2u, 3u } };
339     EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
340     for (size_t i = 0; i < 3; ++i) {
341         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
342     }
343 }
344 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeTrianglesWithoutIndices)345 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesWithoutIndices) {
346     Assimp::Importer importer;
347     //Triangles without indices
348     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf", aiProcess_ValidateDataStructure);
349     EXPECT_NE(nullptr, scene);
350     EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
351     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 6u);
352     std::array<unsigned int, 3> f1 = { { 0u, 1u, 2u } };
353     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
354     for (size_t i = 0; i < 3; ++i) {
355         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
356     }
357 
358     std::array<unsigned int, 3> f2 = { { 3u, 4u, 5u } };
359     EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
360     for (size_t i = 0; i < 3; ++i) {
361         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
362     }
363 }
364 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModePoints)365 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePoints) {
366     Assimp::Importer importer;
367     //Line loop
368     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf", aiProcess_ValidateDataStructure);
369     EXPECT_NE(nullptr, scene);
370     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 1024u);
371     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
372         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mNumIndices, 1u);
373         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], i);
374     }
375 }
376 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeLines)377 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLines) {
378     Assimp::Importer importer;
379     //Lines
380     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf", aiProcess_ValidateDataStructure);
381     EXPECT_NE(nullptr, scene);
382     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
383     std::array<unsigned int, 5> l1 = { { 0u, 3u, 2u, 1u, 0u } };
384     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
385     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
386         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
387         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], l1[i + 1]);
388     }
389 }
390 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeLineLoop)391 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLineLoop) {
392     Assimp::Importer importer;
393     //Line loop
394     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf", aiProcess_ValidateDataStructure);
395     EXPECT_NE(nullptr, scene);
396     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
397     std::array<unsigned int, 5> l1 = { { 0, 3u, 2u, 1u, 0u } };
398     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
399     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
400         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
401         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], l1[i + 1]);
402     }
403 }
404 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeLineStrip)405 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeLineStrip) {
406     Assimp::Importer importer;
407     //Lines Strip
408     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf", aiProcess_ValidateDataStructure);
409     EXPECT_NE(nullptr, scene);
410     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
411     std::array<unsigned int, 5> l1 = { { 0u, 3u, 2u, 1u, 0u } };
412     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 2u);
413     for (unsigned int i = 0; i < scene->mMeshes[0]->mNumFaces; ++i) {
414         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[0], l1[i]);
415         EXPECT_EQ(scene->mMeshes[0]->mFaces[i].mIndices[1], l1[i + 1]);
416     }
417 }
418 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeTrianglesStrip)419 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesStrip) {
420     Assimp::Importer importer;
421     //Triangles strip
422     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf", aiProcess_ValidateDataStructure);
423     EXPECT_NE(nullptr, scene);
424     EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
425     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
426     std::array<unsigned int, 3> f1 = { { 0u, 3u, 1u } };
427     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
428     for (size_t i = 0; i < 3; ++i) {
429         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
430     }
431 
432     std::array<unsigned int, 3> f2 = { { 1u, 3u, 2u } };
433     EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
434     for (size_t i = 0; i < 3; ++i) {
435         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
436     }
437 }
438 
TEST_F(utglTF2ImportExport,importglTF2PrimitiveModeTrianglesFan)439 TEST_F(utglTF2ImportExport, importglTF2PrimitiveModeTrianglesFan) {
440     Assimp::Importer importer;
441     //Triangles fan
442     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf", aiProcess_ValidateDataStructure);
443     EXPECT_NE(nullptr, scene);
444     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 4u);
445     EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 2u);
446     std::array<unsigned int, 3> f1 = { { 0u, 3u, 2u } };
447     EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mNumIndices, 3u);
448     for (size_t i = 0; i < 3; ++i) {
449         EXPECT_EQ(scene->mMeshes[0]->mFaces[0].mIndices[i], f1[i]);
450     }
451 
452     std::array<unsigned int, 3> f2 = { { 0u, 2u, 1u } };
453     EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mNumIndices, 3u);
454     for (size_t i = 0; i < 3; ++i) {
455         EXPECT_EQ(scene->mMeshes[0]->mFaces[1].mIndices[i], f2[i]);
456     }
457 }
458 
ReadFile(const char * name)459 std::vector<char> ReadFile(const char *name) {
460     std::vector<char> ret;
461 
462     FILE *p = ::fopen(name, "r");
463     if (nullptr == p) {
464         return ret;
465     }
466 
467     ::fseek(p, 0, SEEK_END);
468     const size_t size = ::ftell(p);
469     ::fseek(p, 0, SEEK_SET);
470 
471     ret.resize(size);
472     const size_t readSize = ::fread(&ret[0], 1, size, p);
473     EXPECT_EQ(readSize, size);
474     ::fclose(p);
475 
476     return ret;
477 }
478 
TEST_F(utglTF2ImportExport,importglTF2FromMemory)479 TEST_F(utglTF2ImportExport, importglTF2FromMemory) {
480     /*const auto flags = aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_RemoveComponent |
481         aiProcess_GenSmoothNormals | aiProcess_PreTransformVertices | aiProcess_FixInfacingNormals |
482         aiProcess_FindDegenerates | aiProcess_GenUVCoords | aiProcess_SortByPType;
483     const auto& buff = ReadFile("C:\\Users\\kimkulling\\Downloads\\camel\\camel\\scene.gltf");*/
484     /*const aiScene* Scene = ::aiImportFileFromMemory(&buff[0], buff.size(), flags, ".gltf");
485     EXPECT_EQ( nullptr, Scene );*/
486 }
487 
TEST_F(utglTF2ImportExport,bug_import_simple_skin)488 TEST_F(utglTF2ImportExport, bug_import_simple_skin) {
489     Assimp::Importer importer;
490     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/simple_skin/simple_skin.gltf",
491             aiProcess_ValidateDataStructure);
492     EXPECT_NE(nullptr, scene);
493 }
494 
TEST_F(utglTF2ImportExport,import_cameras)495 TEST_F(utglTF2ImportExport, import_cameras) {
496     Assimp::Importer importer;
497     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/cameras/Cameras.gltf",
498             aiProcess_ValidateDataStructure);
499     EXPECT_NE(nullptr, scene);
500 }
501 
TEST_F(utglTF2ImportExport,incorrect_vertex_arrays)502 TEST_F(utglTF2ImportExport, incorrect_vertex_arrays) {
503     Assimp::Importer importer;
504     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IncorrectVertexArrays/Cube.gltf",
505             aiProcess_ValidateDataStructure);
506     EXPECT_NE(nullptr, scene);
507     EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 36u);
508     EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 12u);
509     EXPECT_EQ(scene->mMeshes[1]->mNumVertices, 35u);
510     EXPECT_EQ(scene->mMeshes[1]->mNumFaces, 11u);
511     EXPECT_EQ(scene->mMeshes[2]->mNumVertices, 36u);
512     EXPECT_EQ(scene->mMeshes[2]->mNumFaces, 18u);
513     EXPECT_EQ(scene->mMeshes[3]->mNumVertices, 35u);
514     EXPECT_EQ(scene->mMeshes[3]->mNumFaces, 17u);
515     EXPECT_EQ(scene->mMeshes[4]->mNumVertices, 36u);
516     EXPECT_EQ(scene->mMeshes[4]->mNumFaces, 12u);
517     EXPECT_EQ(scene->mMeshes[5]->mNumVertices, 35u);
518     EXPECT_EQ(scene->mMeshes[5]->mNumFaces, 11u);
519     EXPECT_EQ(scene->mMeshes[6]->mNumVertices, 36u);
520     EXPECT_EQ(scene->mMeshes[6]->mNumFaces, 18u);
521     EXPECT_EQ(scene->mMeshes[7]->mNumVertices, 35u);
522     EXPECT_EQ(scene->mMeshes[7]->mNumFaces, 17u);
523 }
524 
TEST_F(utglTF2ImportExport,texture_transform_test)525 TEST_F(utglTF2ImportExport, texture_transform_test) {
526     Assimp::Importer importer;
527     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/textureTransform/TextureTransformTest.gltf",
528             aiProcess_ValidateDataStructure);
529     EXPECT_NE(nullptr, scene);
530 }
531 
532 #ifndef ASSIMP_BUILD_NO_EXPORT
TEST_F(utglTF2ImportExport,exportglTF2FromFileTest)533 TEST_F(utglTF2ImportExport, exportglTF2FromFileTest) {
534     EXPECT_TRUE(exporterTest());
535 }
536 
TEST_F(utglTF2ImportExport,crash_in_anim_mesh_destructor)537 TEST_F(utglTF2ImportExport, crash_in_anim_mesh_destructor) {
538     Assimp::Importer importer;
539     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf",
540             aiProcess_ValidateDataStructure);
541     ASSERT_NE(nullptr, scene);
542     Assimp::Exporter exporter;
543     ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube_out.glTF"));
544 }
545 
TEST_F(utglTF2ImportExport,error_string_preserved)546 TEST_F(utglTF2ImportExport, error_string_preserved) {
547     Assimp::Importer importer;
548     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/MissingBin/BoxTextured.gltf",
549             aiProcess_ValidateDataStructure);
550     ASSERT_EQ(nullptr, scene);
551     std::string error = importer.GetErrorString();
552     ASSERT_NE(error.find("BoxTextured0.bin"), std::string::npos) << "Error string should contain an error about missing .bin file";
553 }
554 
TEST_F(utglTF2ImportExport,export_bad_accessor_bounds)555 TEST_F(utglTF2ImportExport, export_bad_accessor_bounds) {
556     Assimp::Importer importer;
557     Assimp::Exporter exporter;
558     const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxWithInfinites-glTF-Binary/BoxWithInfinites.glb", aiProcess_ValidateDataStructure);
559     ASSERT_NE(scene, nullptr);
560 
561     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxWithInfinites-glTF-Binary/BoxWithInfinites_out.glb"));
562     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "gltf2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxWithInfinites-glTF-Binary/BoxWithInfinites_out.gltf"));
563 }
564 
TEST_F(utglTF2ImportExport,export_normalized_normals)565 TEST_F(utglTF2ImportExport, export_normalized_normals) {
566     Assimp::Importer importer;
567     Assimp::Exporter exporter;
568     const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals.glb", aiProcess_ValidateDataStructure);
569     ASSERT_NE(scene, nullptr);
570     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals_out.glb"));
571 
572     // load in again and ensure normal-length normals but no Nan's or Inf's introduced
573     scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals_out.glb", aiProcess_ValidateDataStructure);
574     for ( auto i = 0u; i < scene->mMeshes[0]->mNumVertices; ++i ) {
575         const auto length = scene->mMeshes[0]->mNormals[i].Length();
576         EXPECT_TRUE(abs(length) < 1e-6 || abs(length - 1) < 1e-6);
577     }
578 }
579 
580 #endif // ASSIMP_BUILD_NO_EXPORT
581 
TEST_F(utglTF2ImportExport,sceneMetadata)582 TEST_F(utglTF2ImportExport, sceneMetadata) {
583     Assimp::Importer importer;
584     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf",
585             aiProcess_ValidateDataStructure);
586     ASSERT_NE(scene, nullptr);
587     ASSERT_NE(scene->mMetaData, nullptr);
588     {
589         ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
590         aiString format;
591         ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
592         ASSERT_EQ(strcmp(format.C_Str(), "glTF2 Importer"), 0);
593     }
594     {
595         ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
596         aiString version;
597         ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
598         ASSERT_EQ(strcmp(version.C_Str(), "2.0"), 0);
599     }
600     {
601         ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
602         aiString generator;
603         ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
604         ASSERT_EQ(strcmp(generator.C_Str(), "COLLADA2GLTF"), 0);
605     }
606 }
607 
TEST_F(utglTF2ImportExport,texcoords)608 TEST_F(utglTF2ImportExport, texcoords) {
609 
610     Assimp::Importer importer;
611     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure);
612     ASSERT_NE(scene, nullptr);
613     ASSERT_TRUE(scene->HasMaterials());
614     const aiMaterial *material = scene->mMaterials[0];
615 
616     aiString path;
617     unsigned int uvIndex = 255;
618     aiTextureMapMode modes[2];
619     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
620     EXPECT_STREQ(path.C_Str(), "texture.png");
621     EXPECT_EQ(uvIndex, 0u);
622 
623     uvIndex = 255;
624     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
625     EXPECT_STREQ(path.C_Str(), "texture.png");
626     EXPECT_EQ(uvIndex, 1u);
627 }
628 
629 #ifndef ASSIMP_BUILD_NO_EXPORT
630 
TEST_F(utglTF2ImportExport,texcoords_export)631 TEST_F(utglTF2ImportExport, texcoords_export) {
632     {
633         Assimp::Importer importer;
634         Assimp::Exporter exporter;
635         const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure);
636         ASSERT_NE(scene, nullptr);
637         ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb"));
638     }
639 
640     Assimp::Importer importer;
641     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure);
642     ASSERT_NE(scene, nullptr);
643 
644     ASSERT_TRUE(scene->HasMaterials());
645     const aiMaterial *material = scene->mMaterials[0];
646 
647     aiString path;
648     unsigned int uvIndex = 255;
649     aiTextureMapMode modes[2];
650     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
651     EXPECT_STREQ(path.C_Str(), "texture.png");
652     EXPECT_EQ(uvIndex, 0u);
653 
654     uvIndex = 255;
655     EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes));
656     EXPECT_STREQ(path.C_Str(), "texture.png");
657     EXPECT_EQ(uvIndex, 1u);
658 }
659 
660 #endif // ASSIMP_BUILD_NO_EXPORT
TEST_F(utglTF2ImportExport,recursive_nodes)661 TEST_F(utglTF2ImportExport, recursive_nodes) {
662     Assimp::Importer importer;
663     const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure);
664     EXPECT_EQ(nullptr, scene);
665 }
666 
TEST_F(utglTF2ImportExport,norootnode_noscene)667 TEST_F(utglTF2ImportExport, norootnode_noscene) {
668     Assimp::Importer importer;
669     const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/TestNoRootNode/NoScene.gltf", aiProcess_ValidateDataStructure);
670     ASSERT_EQ(scene, nullptr);
671 }
672 
TEST_F(utglTF2ImportExport,norootnode_scenewithoutnodes)673 TEST_F(utglTF2ImportExport, norootnode_scenewithoutnodes) {
674     Assimp::Importer importer;
675     const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/TestNoRootNode/SceneWithoutNodes.gltf", aiProcess_ValidateDataStructure);
676     ASSERT_NE(scene, nullptr);
677     ASSERT_NE(scene->mRootNode, nullptr);
678 }
679 
680 // Shall not crash!
TEST_F(utglTF2ImportExport,norootnode_issue_3269)681 TEST_F(utglTF2ImportExport, norootnode_issue_3269) {
682     Assimp::Importer importer;
683     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/issue_3269/texcoord_crash.gltf", aiProcess_ValidateDataStructure);
684     ASSERT_EQ(scene, nullptr);
685 }
686 
TEST_F(utglTF2ImportExport,indexOutOfRange)687 TEST_F(utglTF2ImportExport, indexOutOfRange) {
688     // The contents of an asset should not lead to an assert.
689     Assimp::Importer importer;
690 
691     struct LogObserver : Assimp::LogStream {
692         bool m_observedWarning = false;
693         void write(const char *message) override {
694             m_observedWarning = m_observedWarning || std::strstr(message, "faces were dropped");
695         }
696     };
697     LogObserver logObserver;
698 
699     DefaultLogger::get()->attachStream(&logObserver);
700     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IndexOutOfRange/IndexOutOfRange.gltf", aiProcess_ValidateDataStructure);
701     ASSERT_NE(scene, nullptr);
702     ASSERT_NE(scene->mRootNode, nullptr);
703     ASSERT_EQ(scene->mNumMeshes, 1u);
704     EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 11u);
705     DefaultLogger::get()->detachStream(&logObserver);
706     EXPECT_TRUE(logObserver.m_observedWarning);
707 }
708 
TEST_F(utglTF2ImportExport,allIndicesOutOfRange)709 TEST_F(utglTF2ImportExport, allIndicesOutOfRange) {
710     // The contents of an asset should not lead to an assert.
711     Assimp::Importer importer;
712     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IndexOutOfRange/AllIndicesOutOfRange.gltf", aiProcess_ValidateDataStructure);
713     ASSERT_EQ(scene, nullptr);
714     std::string error = importer.GetErrorString();
715     ASSERT_NE(error.find("Mesh \"Mesh\" has no faces"), std::string::npos);
716 }
717 
718 /////////////////////////////////
719 // Draco decoding
720 
TEST_F(utglTF2ImportExport,import_dracoEncoded)721 TEST_F(utglTF2ImportExport, import_dracoEncoded) {
722     Assimp::Importer importer;
723     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/draco/2CylinderEngine.gltf",
724             aiProcess_ValidateDataStructure);
725 #ifndef ASSIMP_ENABLE_DRACO
726     // No draco support, scene should not load
727     ASSERT_EQ(scene, nullptr);
728 #else
729     ASSERT_NE(scene, nullptr);
730     ASSERT_NE(scene->mMetaData, nullptr);
731     {
732         ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
733         aiString format;
734         ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
735         ASSERT_EQ(strcmp(format.C_Str(), "glTF2 Importer"), 0);
736     }
737     {
738         ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
739         aiString version;
740         ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
741         ASSERT_EQ(strcmp(version.C_Str(), "2.0"), 0);
742     }
743     {
744         ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
745         aiString generator;
746         ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
747         ASSERT_EQ(strcmp(generator.C_Str(), "COLLADA2GLTF"), 0);
748     }
749 #endif
750 }
751 
TEST_F(utglTF2ImportExport,wrongTypes)752 TEST_F(utglTF2ImportExport, wrongTypes) {
753     // Deliberately broken version of the BoxTextured.gltf asset.
754     using tup_T = std::tuple<std::string, std::string, std::string, std::string>;
755     std::vector<tup_T> wrongTypes = {
756         { "/glTF2/wrongTypes/badArray.gltf", "array", "primitives", "meshes[0]" },
757         { "/glTF2/wrongTypes/badString.gltf", "string", "name", "scenes[0]" },
758         { "/glTF2/wrongTypes/badUint.gltf", "uint", "index", "materials[0]" },
759         { "/glTF2/wrongTypes/badNumber.gltf", "number", "scale", "materials[0]" },
760         { "/glTF2/wrongTypes/badObject.gltf", "object", "pbrMetallicRoughness", "materials[0]" },
761         { "/glTF2/wrongTypes/badExtension.gltf", "object", "KHR_texture_transform", "materials[0]" }
762     };
763     for (const auto& tuple : wrongTypes)
764     {
765         const auto& file = std::get<0>(tuple);
766         const auto& type = std::get<1>(tuple);
767         const auto& member = std::get<2>(tuple);
768         const auto& context = std::get<3>(tuple);
769         Assimp::Importer importer;
770         const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR + file , aiProcess_ValidateDataStructure);
771         ASSERT_EQ(scene, nullptr);
772         const std::string error = importer.GetErrorString();
773         EXPECT_FALSE(error.empty());
774         EXPECT_NE(error.find(member + "\" was not of type \"" + type + "\" when reading " + context), std::string::npos);
775     }
776 }
777 
778 namespace {
779     /// This class provides a fake schema to the GLTF importer.
780     /// It just checks that the file has a top-level "scene" property which is an integer.
781     class FakeSchemaProvider : public rapidjson::IRemoteSchemaDocumentProvider
782     {
783     public:
FakeSchemaProvider(const char * schemaName)784         FakeSchemaProvider(const char* schemaName) :
785             m_schemaName(schemaName)
786         {
787             rapidjson::Document schemaDoc;
788             schemaDoc.Parse(R"==({"properties":{"scene" : { "type" : "integer" }}, "required": [ "scene" ]})==");
789             EXPECT_FALSE(schemaDoc.HasParseError());
790         	m_schema.reset(new rapidjson::SchemaDocument(schemaDoc, m_schemaName.c_str(), static_cast<rapidjson::SizeType>(m_schemaName.size()), this));
791         }
792 
GetRemoteDocument(const char * uri,rapidjson::SizeType)793         const rapidjson::SchemaDocument* GetRemoteDocument(const char* uri, rapidjson::SizeType) override {
794             if (m_schemaName == uri) {
795                 return m_schema.get();
796             }
797             return nullptr;
798         }
799 
800     private:
801         std::string m_schemaName;
802         std::unique_ptr<const rapidjson::SchemaDocument> m_schema;
803     };
804 }
805 
TEST_F(utglTF2ImportExport,schemaCheckPass)806 TEST_F(utglTF2ImportExport, schemaCheckPass) {
807     FakeSchemaProvider schemaProvider("glTF.schema.json");
808     Assimp::Importer importer;
809     importer.SetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER, &schemaProvider);
810     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure);
811     EXPECT_NE(scene, nullptr);
812     EXPECT_STREQ(importer.GetErrorString(), "");
813 }
814 
TEST_F(utglTF2ImportExport,schemaCheckFail)815 TEST_F(utglTF2ImportExport, schemaCheckFail) {
816     FakeSchemaProvider schemaProvider("glTF.schema.json");
817     Assimp::Importer importer;
818     importer.SetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER, &schemaProvider);
819     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/SchemaFailures/sceneWrongType.gltf", aiProcess_ValidateDataStructure);
820     EXPECT_EQ(scene, nullptr);
821     const std::string errorString = importer.GetErrorString();
822     EXPECT_NE(errorString.find("The JSON document did not satisfy the glTF2 schema"), std::string::npos);
823 }
824 
TEST_F(utglTF2ImportExport,noSchemaFound)825 TEST_F(utglTF2ImportExport, noSchemaFound) {
826     // More than one importer might make use the provider, but not all schemas might be present.
827     // Check that the glTF importer handles the case when an non-null provider returns null when asked for schemas.
828     FakeSchemaProvider schemaProvider("missingSchema.json");
829     Assimp::Importer importer;
830     importer.SetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER, &schemaProvider);
831     const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", aiProcess_ValidateDataStructure);
832     EXPECT_NE(scene, nullptr);
833     EXPECT_STREQ(importer.GetErrorString(), "");
834 }
835