From 5727eda0e1b7d9f686b11e1cd9afa110167d4341 Mon Sep 17 00:00:00 2001 From: Julian Date: Sat, 4 Jan 2025 14:28:38 -0500 Subject: [PATCH] GLTFDocument test case --- tests/data/models/cube.bin | Bin 0 -> 1224 bytes tests/data/models/cube.gltf | 312 +++++++++++++++++++++++++++++++ tests/data/models/suzanne.glb | Bin 0 -> 70636 bytes tests/scene/test_gltf_document.h | 250 +++++++++++++++++++++++++ tests/test_main.cpp | 1 + 5 files changed, 563 insertions(+) create mode 100644 tests/data/models/cube.bin create mode 100644 tests/data/models/cube.gltf create mode 100644 tests/data/models/suzanne.glb create mode 100644 tests/scene/test_gltf_document.h diff --git a/tests/data/models/cube.bin b/tests/data/models/cube.bin new file mode 100644 index 0000000000000000000000000000000000000000..755b568b68edeedd3c24a1f5366c0497964ee54c GIT binary patch literal 1224 zcmZQzXxPud&|nY6FgA>aiNomq3}66K4`whx^pVO2=?7s324)6E1`x@>zyvZ2WG@JV z>;ustjExNv17Vmvj1A%w!pLf2Y-AcA8zhGf!|Vjf;bOz&Kr{$L!x9vBkg$Nn1(=T{ z2jYV;NDU~iKx||T6GNxb)xhLY#O)zGm>kFshzNoNiGeUe1j2&rfw~o!8gy|OADxDn ziXdU~AQ2E7gh6Z&hOt33GKR^4#6WBq2JvASBnD%HXc!-q0$CV18JHM28Q2+E88{e( z7+4vE82A|k8H5@57z7#k7uhOzzy=s*$ z0|SHI>Q!2Hp`oF63=9o+p`lalR=+mr>{qYivJVXvwP#>Z zuwT7O%RV&J)SiLC!G85BFZ+{VxVA{Xsx1L=dZ*+BAO_Gc+CID65aHaPoK z_6j)rv(za#Tl&;%ID63^HV06q1DS6&MI6pPm8}71$2nWV*`KAn;OzEKQE;~OscblV s{lXeJd(oaYI6Ft9AI>(LG9AulU|0ZVgTfEf->~=rxeyc%AU<{s09yvZy8r+H literal 0 HcmV?d00001 diff --git a/tests/data/models/cube.gltf b/tests/data/models/cube.gltf new file mode 100644 index 0000000000..836344c384 --- /dev/null +++ b/tests/data/models/cube.gltf @@ -0,0 +1,312 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.3.47", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "animations":[ + { + "channels":[ + { + "sampler":0, + "target":{ + "node":0, + "path":"translation" + } + }, + { + "sampler":1, + "target":{ + "node":0, + "path":"rotation" + } + }, + { + "sampler":2, + "target":{ + "node":0, + "path":"scale" + } + } + ], + "name":"CubeAction", + "samplers":[ + { + "input":8, + "interpolation":"LINEAR", + "output":9 + }, + { + "input":10, + "interpolation":"STEP", + "output":11 + }, + { + "input":10, + "interpolation":"STEP", + "output":12 + } + ] + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material1", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 1.9073486328125e-06, + 0, + 1, + 1 + ], + "metallicFactor":0 + } + }, + { + "doubleSided":true, + "name":"Material2", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0, + 1, + 0, + 1 + ], + "roughnessFactor":0 + } + } + ], + "meshes":[ + { + "name":"Cube.001", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + }, + { + "attributes":{ + "POSITION":4, + "NORMAL":5, + "TEXCOORD_0":6 + }, + "indices":7, + "material":1 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":4, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + 1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":4, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":4, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":6, + "type":"SCALAR" + }, + { + "bufferView":4, + "componentType":5126, + "count":20, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":5, + "componentType":5126, + "count":20, + "type":"VEC3" + }, + { + "bufferView":6, + "componentType":5126, + "count":20, + "type":"VEC2" + }, + { + "bufferView":7, + "componentType":5123, + "count":30, + "type":"SCALAR" + }, + { + "bufferView":8, + "componentType":5126, + "count":20, + "max":[ + 0.8333333333333334 + ], + "min":[ + 0.041666666666666664 + ], + "type":"SCALAR" + }, + { + "bufferView":9, + "componentType":5126, + "count":20, + "type":"VEC3" + }, + { + "bufferView":10, + "componentType":5126, + "count":2, + "max":[ + 0.8333333333333334 + ], + "min":[ + 0.041666666666666664 + ], + "type":"SCALAR" + }, + { + "bufferView":11, + "componentType":5126, + "count":2, + "type":"VEC4" + }, + { + "bufferView":12, + "componentType":5126, + "count":2, + "type":"VEC3" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":48, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":48, + "byteOffset":48, + "target":34962 + }, + { + "buffer":0, + "byteLength":32, + "byteOffset":96, + "target":34962 + }, + { + "buffer":0, + "byteLength":12, + "byteOffset":128, + "target":34963 + }, + { + "buffer":0, + "byteLength":240, + "byteOffset":140, + "target":34962 + }, + { + "buffer":0, + "byteLength":240, + "byteOffset":380, + "target":34962 + }, + { + "buffer":0, + "byteLength":160, + "byteOffset":620, + "target":34962 + }, + { + "buffer":0, + "byteLength":60, + "byteOffset":780, + "target":34963 + }, + { + "buffer":0, + "byteLength":80, + "byteOffset":840 + }, + { + "buffer":0, + "byteLength":240, + "byteOffset":920 + }, + { + "buffer":0, + "byteLength":8, + "byteOffset":1160 + }, + { + "buffer":0, + "byteLength":32, + "byteOffset":1168 + }, + { + "buffer":0, + "byteLength":24, + "byteOffset":1200 + } + ], + "buffers":[ + { + "byteLength":1224, + "uri":"cube.bin" + } + ] +} diff --git a/tests/data/models/suzanne.glb b/tests/data/models/suzanne.glb new file mode 100644 index 0000000000000000000000000000000000000000..baedd49d75bb61bceb8a9bd522943f0b0b256727 GIT binary patch literal 70636 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 literal 0 HcmV?d00001 diff --git a/tests/scene/test_gltf_document.h b/tests/scene/test_gltf_document.h new file mode 100644 index 0000000000..f8106c0614 --- /dev/null +++ b/tests/scene/test_gltf_document.h @@ -0,0 +1,250 @@ +/**************************************************************************/ +/* test_gltf_document.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_GLTF_DOCUMENT_H +#define TEST_GLTF_DOCUMENT_H + +#include "modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h" +#include "modules/gltf/gltf_document.h" + +#include "tests/test_macros.h" +#include "tests/test_utils.h" + +namespace TestGLTFDocument { + +struct GLTFArraySize { + String key; + int val; +}; + +struct GLTFKeyValue { + String key; + Variant val; +}; + +struct GLTFTestCase { + String filename; + String copywrite; + String generator; + String version; + Vector array_sizes; + Vector json_array_sizes; + Vector keyvalues; +}; + +const GLTFTestCase glTF_test_cases[] = { + { "models/cube.gltf", + "", + "Khronos glTF Blender I/O v4.3.47", + "2.0", + // Here are the array sizes. + { + { "nodes", 1 }, + { "buffers", 1 }, + { "buffer_views", 13 }, + { "accessors", 13 }, + { "meshes", 1 }, + { "materials", 2 }, + { "root_nodes", 1 }, + { "textures", 0 }, + { "texture_samplers", 0 }, + { "images", 0 }, + { "skins", 0 }, + { "cameras", 0 }, + { "lights", 0 }, + { "skeletons", 0 }, + { "animations", 1 }, + }, + // Here are the json array sizes. + { + { "scenes", 1 }, + { "nodes", 1 }, + { "animations", 1 }, + { "meshes", 1 }, + { "accessors", 13 }, + { "bufferViews", 13 }, + { "buffers", 1 }, + }, + // Here are the key-value pairs. + { + { "major_version", 2 }, + { "minor_version", 0 }, + { "scene_name", "cube" }, + { "filename", "cube" } } }, + { "models/suzanne.glb", + "this is example text", + "Khronos glTF Blender I/O v4.3.47", + "2.0", + // Here are the array sizes. + { + { "glb_data", 68908 }, + { "nodes", 2 }, + { "buffers", 1 }, + { "buffer_views", 5 }, + { "accessors", 4 }, + { "meshes", 1 }, + { "materials", 1 }, + { "root_nodes", 2 }, + { "textures", 1 }, + { "texture_samplers", 1 }, + { "images", 1 }, + { "skins", 0 }, + { "cameras", 1 }, + { "lights", 0 }, + { "unique_names", 4 }, + { "skeletons", 0 }, + { "animations", 0 }, + }, + // Here are the json array sizes. + { + { "scenes", 1 }, + { "nodes", 2 }, + { "cameras", 1 }, + { "materials", 1 }, + { "meshes", 1 }, + { "textures", 1 }, + { "images", 1 }, + { "accessors", 4 }, + { "bufferViews", 5 }, + { "buffers", 1 }, + }, + // Here are the key-value pairs. + { + { "major_version", 2 }, + { "minor_version", 0 }, + { "scene_name", "suzanne" }, + { "filename", "suzanne" } } }, +}; + +void register_gltf_extension() { + GLTFDocument::unregister_all_gltf_document_extensions(); + + // Ensures meshes become a MeshInstance3D and not an ImporterMeshInstance3D. + Ref extension_GLTFDocumentExtensionConvertImporterMesh; + extension_GLTFDocumentExtensionConvertImporterMesh.instantiate(); + GLTFDocument::register_gltf_document_extension(extension_GLTFDocumentExtensionConvertImporterMesh); +} + +void test_gltf_document_values(Ref &p_gltf_document, Ref &p_gltf_state, const GLTFTestCase &p_test_case) { + const Error err = p_gltf_document->append_from_file(TestUtils::get_data_path(p_test_case.filename), p_gltf_state); + REQUIRE(err == OK); + + for (GLTFArraySize array_size : p_test_case.array_sizes) { + CHECK_MESSAGE(((Array)(p_gltf_state->getvar(array_size.key))).size() == array_size.val, "Expected \"", array_size.key, "\" to have ", array_size.val, " elements."); + } + + for (GLTFArraySize array_size : p_test_case.json_array_sizes) { + CHECK(p_gltf_state->get_json().has(array_size.key)); + CHECK_MESSAGE(((Array)(p_gltf_state->get_json()[array_size.key])).size() == array_size.val, "Expected \"", array_size.key, "\" to have ", array_size.val, " elements."); + } + + for (GLTFKeyValue key_value : p_test_case.keyvalues) { + CHECK_MESSAGE(p_gltf_state->getvar(key_value.key) == key_value.val, "Expected \"", key_value.key, "\" to be \"", key_value.val, "\"."); + } + + CHECK(p_gltf_state->get_copyright() == p_test_case.copywrite); + CHECK(((Dictionary)p_gltf_state->get_json()["asset"])["generator"] == p_test_case.generator); + CHECK(((Dictionary)p_gltf_state->get_json()["asset"])["version"] == p_test_case.version); +} + +void test_gltf_save(Node *p_node) { + Ref gltf_document_save; + gltf_document_save.instantiate(); + Ref gltf_state_save; + gltf_state_save.instantiate(); + + gltf_document_save->append_from_scene(p_node, gltf_state_save); + + // Check saving the scene to gltf and glb. + const Error err_save_gltf = gltf_document_save->write_to_filesystem(gltf_state_save, TestUtils::get_temp_path("cube.gltf")); + const Error err_save_glb = gltf_document_save->write_to_filesystem(gltf_state_save, TestUtils::get_temp_path("cube.glb")); + CHECK(err_save_gltf == OK); + CHECK(err_save_glb == OK); +} + +TEST_CASE("[SceneTree][GLTFDocument] Load cube.gltf") { + register_gltf_extension(); + + Ref gltf_document; + gltf_document.instantiate(); + Ref gltf_state; + gltf_state.instantiate(); + + test_gltf_document_values(gltf_document, gltf_state, glTF_test_cases[0]); + + Node *node = gltf_document->generate_scene(gltf_state); + + // Check the loaded scene. + CHECK(node->is_class("Node3D")); + CHECK(node->get_name() == "cube"); + + CHECK(node->get_child(0)->is_class("MeshInstance3D")); + CHECK(node->get_child(0)->get_name() == "Cube"); + + CHECK(node->get_child(1)->is_class("AnimationPlayer")); + CHECK(node->get_child(1)->get_name() == "AnimationPlayer"); + + test_gltf_save(node); + + // Clean up the node. + memdelete(node); +} + +TEST_CASE("[SceneTree][GLTFDocument] Load suzanne.glb") { + register_gltf_extension(); + + Ref gltf_document; + gltf_document.instantiate(); + Ref gltf_state; + gltf_state.instantiate(); + + test_gltf_document_values(gltf_document, gltf_state, glTF_test_cases[1]); + + Node *node = gltf_document->generate_scene(gltf_state); + + // Check the loaded scene. + CHECK(node->is_class("Node3D")); + CHECK(node->get_name() == "suzanne"); + + CHECK(node->get_child(0)->is_class("MeshInstance3D")); + CHECK(node->get_child(0)->get_name() == "Suzanne"); + + CHECK(node->get_child(1)->is_class("Camera3D")); + CHECK(node->get_child(1)->get_name() == "Camera"); + + test_gltf_save(node); + + // Clean up the node. + memdelete(node); +} + +} // namespace TestGLTFDocument + +#endif // TEST_GLTF_DOCUMENT_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index e264cfb292..6598d57d1b 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -170,6 +170,7 @@ #include "tests/scene/test_arraymesh.h" #include "tests/scene/test_camera_3d.h" +#include "tests/scene/test_gltf_document.h" #include "tests/scene/test_height_map_shape_3d.h" #include "tests/scene/test_path_3d.h" #include "tests/scene/test_path_follow_3d.h"