diff --git a/tests/data/models/cube.bin b/tests/data/models/cube.bin new file mode 100644 index 00000000000..755b568b68e Binary files /dev/null and b/tests/data/models/cube.bin differ diff --git a/tests/data/models/cube.gltf b/tests/data/models/cube.gltf new file mode 100644 index 00000000000..836344c384e --- /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 00000000000..baedd49d75b Binary files /dev/null and b/tests/data/models/suzanne.glb differ diff --git a/tests/scene/test_gltf_document.h b/tests/scene/test_gltf_document.h new file mode 100644 index 00000000000..f8106c0614f --- /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 e264cfb292b..6598d57d1b2 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"