You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
GLTF: Don't duplicate textures when importing blend files
Blender imports will always start within `.godot/imported` folder because we first convert the .blend file to .gltf, store it in `.godot/imported` and run the import from there, so on-disk resources linked from .blend files end up with duplicate textures.
This commit is contained in:
@@ -62,6 +62,9 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
|
|||||||
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
|
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
|
||||||
state->set_import_as_skeleton_bones(true);
|
state->set_import_as_skeleton_bones(true);
|
||||||
}
|
}
|
||||||
|
if (p_options.has(SNAME("extract_path"))) {
|
||||||
|
state->set_extract_path(p_options["extract_path"]);
|
||||||
|
}
|
||||||
state->set_bake_fps(p_options["animation/fps"]);
|
state->set_bake_fps(p_options["animation/fps"]);
|
||||||
Error err = gltf->append_from_file(p_path, state, p_flags);
|
Error err = gltf->append_from_file(p_path, state, p_flags);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
|
|||||||
@@ -3932,7 +3932,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c
|
|||||||
return r_image;
|
return r_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image) {
|
void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_resource_uri, const String &p_file_extension, int p_index, Ref<Image> p_image) {
|
||||||
GLTFState::GLTFHandleBinary handling = GLTFState::GLTFHandleBinary(p_state->handle_binary_image);
|
GLTFState::GLTFHandleBinary handling = GLTFState::GLTFHandleBinary(p_state->handle_binary_image);
|
||||||
if (p_image->is_empty() || handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) {
|
if (p_image->is_empty() || handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) {
|
||||||
p_state->images.push_back(Ref<Texture2D>());
|
p_state->images.push_back(Ref<Texture2D>());
|
||||||
@@ -3950,10 +3950,19 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<
|
|||||||
WARN_PRINT(vformat("glTF: Image index '%d' did not have a name. It will be automatically given a name based on its index.", p_index));
|
WARN_PRINT(vformat("glTF: Image index '%d' did not have a name. It will be automatically given a name based on its index.", p_index));
|
||||||
p_image->set_name(itos(p_index));
|
p_image->set_name(itos(p_index));
|
||||||
}
|
}
|
||||||
bool must_import = true;
|
bool must_write = true; // If the resource does not exist on the disk within res:// directory write it.
|
||||||
|
bool must_import = true; // Trigger import.
|
||||||
Vector<uint8_t> img_data = p_image->get_data();
|
Vector<uint8_t> img_data = p_image->get_data();
|
||||||
Dictionary generator_parameters;
|
Dictionary generator_parameters;
|
||||||
String file_path = p_state->get_extract_path().path_join(p_state->get_extract_prefix() + "_" + p_image->get_name());
|
String file_path;
|
||||||
|
// If resource_uri is within res:// folder but outside of .godot/imported folder, use it.
|
||||||
|
if (!p_resource_uri.is_empty() && !p_resource_uri.begins_with("res://.godot/imported") && !p_resource_uri.begins_with("res://..")) {
|
||||||
|
file_path = p_resource_uri;
|
||||||
|
must_import = true;
|
||||||
|
must_write = !FileAccess::exists(file_path);
|
||||||
|
} else {
|
||||||
|
// Texture data has to be written to the res:// folder and imported.
|
||||||
|
file_path = p_state->get_extract_path().path_join(p_state->get_extract_prefix() + "_" + p_image->get_name());
|
||||||
file_path += p_file_extension.is_empty() ? ".png" : p_file_extension;
|
file_path += p_file_extension.is_empty() ? ".png" : p_file_extension;
|
||||||
if (FileAccess::exists(file_path + ".import")) {
|
if (FileAccess::exists(file_path + ".import")) {
|
||||||
Ref<ConfigFile> config;
|
Ref<ConfigFile> config;
|
||||||
@@ -3963,20 +3972,24 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<
|
|||||||
generator_parameters = (Dictionary)config->get_value("remap", "generator_parameters");
|
generator_parameters = (Dictionary)config->get_value("remap", "generator_parameters");
|
||||||
}
|
}
|
||||||
if (!generator_parameters.has("md5")) {
|
if (!generator_parameters.has("md5")) {
|
||||||
must_import = false; // Didn't come from a gltf document; don't overwrite.
|
must_write = false; // Didn't come from a gltf document; don't overwrite.
|
||||||
|
must_import = false; // And don't import.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (must_import) {
|
}
|
||||||
|
|
||||||
|
if (must_write) {
|
||||||
String existing_md5 = generator_parameters["md5"];
|
String existing_md5 = generator_parameters["md5"];
|
||||||
unsigned char md5_hash[16];
|
unsigned char md5_hash[16];
|
||||||
CryptoCore::md5(img_data.ptr(), img_data.size(), md5_hash);
|
CryptoCore::md5(img_data.ptr(), img_data.size(), md5_hash);
|
||||||
String new_md5 = String::hex_encode_buffer(md5_hash, 16);
|
String new_md5 = String::hex_encode_buffer(md5_hash, 16);
|
||||||
generator_parameters["md5"] = new_md5;
|
generator_parameters["md5"] = new_md5;
|
||||||
if (new_md5 == existing_md5) {
|
if (new_md5 == existing_md5) {
|
||||||
|
must_write = false;
|
||||||
must_import = false;
|
must_import = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (must_import) {
|
if (must_write) {
|
||||||
Error err = OK;
|
Error err = OK;
|
||||||
if (p_file_extension.is_empty()) {
|
if (p_file_extension.is_empty()) {
|
||||||
// If a file extension was not specified, save the image data to a PNG file.
|
// If a file extension was not specified, save the image data to a PNG file.
|
||||||
@@ -3989,10 +4002,13 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<
|
|||||||
file->store_buffer(p_bytes);
|
file->store_buffer(p_bytes);
|
||||||
file->close();
|
file->close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (must_import) {
|
||||||
// ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed.
|
// ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed.
|
||||||
HashMap<StringName, Variant> custom_options;
|
HashMap<StringName, Variant> custom_options;
|
||||||
custom_options[SNAME("mipmaps/generate")] = true;
|
custom_options[SNAME("mipmaps/generate")] = true;
|
||||||
// Will only use project settings defaults if custom_importer is empty.
|
// Will only use project settings defaults if custom_importer is empty.
|
||||||
|
|
||||||
EditorFileSystem::get_singleton()->update_file(file_path);
|
EditorFileSystem::get_singleton()->update_file(file_path);
|
||||||
EditorFileSystem::get_singleton()->reimport_append(file_path, custom_options, String(), generator_parameters);
|
EditorFileSystem::get_singleton()->reimport_append(file_path, custom_options, String(), generator_parameters);
|
||||||
}
|
}
|
||||||
@@ -4002,7 +4018,7 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<
|
|||||||
p_state->source_images.push_back(saved_image->get_image());
|
p_state->source_images.push_back(saved_image->get_image());
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
WARN_PRINT(vformat("glTF: Image index '%d' with the name '%s' couldn't be imported. It will be loaded directly instead, uncompressed.", p_index, p_image->get_name()));
|
WARN_PRINT(vformat("glTF: Image index '%d' with the name '%s' resolved to %s couldn't be imported. It will be loaded directly instead, uncompressed.", p_index, p_image->get_name(), file_path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4070,6 +4086,9 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
|
|||||||
while (used_names.has(image_name)) {
|
while (used_names.has(image_name)) {
|
||||||
image_name += "_" + itos(i);
|
image_name += "_" + itos(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String resource_uri;
|
||||||
|
|
||||||
used_names.insert(image_name);
|
used_names.insert(image_name);
|
||||||
// Load the image data. If we get a byte array, store here for later.
|
// Load the image data. If we get a byte array, store here for later.
|
||||||
Vector<uint8_t> data;
|
Vector<uint8_t> data;
|
||||||
@@ -4087,14 +4106,14 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
|
|||||||
ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
|
ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
|
||||||
uri = uri.uri_decode();
|
uri = uri.uri_decode();
|
||||||
uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows.
|
uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows.
|
||||||
// If the image is in the .godot/imported directory, we can't use ResourceLoader.
|
resource_uri = uri.simplify_path();
|
||||||
if (!p_base_path.begins_with("res://.godot/imported")) {
|
|
||||||
// ResourceLoader will rely on the file extension to use the relevant loader.
|
// ResourceLoader will rely on the file extension to use the relevant loader.
|
||||||
// The spec says that if mimeType is defined, it should take precedence (e.g.
|
// The spec says that if mimeType is defined, it should take precedence (e.g.
|
||||||
// there could be a `.png` image which is actually JPEG), but there's no easy
|
// there could be a `.png` image which is actually JPEG), but there's no easy
|
||||||
// API for that in Godot, so we'd have to load as a buffer (i.e. embedded in
|
// API for that in Godot, so we'd have to load as a buffer (i.e. embedded in
|
||||||
// the material), so we only do that only as fallback.
|
// the material), so we only do that only as fallback.
|
||||||
Ref<Texture2D> texture = ResourceLoader::load(uri, "Texture2D");
|
if (ResourceLoader::exists(resource_uri)) {
|
||||||
|
Ref<Texture2D> texture = ResourceLoader::load(resource_uri, "Texture2D");
|
||||||
if (texture.is_valid()) {
|
if (texture.is_valid()) {
|
||||||
p_state->images.push_back(texture);
|
p_state->images.push_back(texture);
|
||||||
p_state->source_images.push_back(texture->get_image());
|
p_state->source_images.push_back(texture->get_image());
|
||||||
@@ -4105,13 +4124,13 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
|
|||||||
// If the mimeType does not match with the file extension, either it should be
|
// If the mimeType does not match with the file extension, either it should be
|
||||||
// specified in the file, or the GLTFDocumentExtension should handle it.
|
// specified in the file, or the GLTFDocumentExtension should handle it.
|
||||||
if (mime_type.is_empty()) {
|
if (mime_type.is_empty()) {
|
||||||
mime_type = "image/" + uri.get_extension();
|
mime_type = "image/" + resource_uri.get_extension();
|
||||||
}
|
}
|
||||||
// Fallback to loading as byte array. This enables us to support the
|
// Fallback to loading as byte array. This enables us to support the
|
||||||
// spec's requirement that we honor mimetype regardless of file URI.
|
// spec's requirement that we honor mimetype regardless of file URI.
|
||||||
data = FileAccess::get_file_as_bytes(uri);
|
data = FileAccess::get_file_as_bytes(resource_uri);
|
||||||
if (data.size() == 0) {
|
if (data.size() == 0) {
|
||||||
WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s because there was no data to load. Skipping it.", i, mime_type, uri));
|
WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s because there was no data to load. Skipping it.", i, mime_type, resource_uri));
|
||||||
p_state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
|
p_state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
|
||||||
p_state->source_images.push_back(Ref<Image>());
|
p_state->source_images.push_back(Ref<Image>());
|
||||||
continue;
|
continue;
|
||||||
@@ -4141,7 +4160,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
|
|||||||
String file_extension;
|
String file_extension;
|
||||||
Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i, file_extension);
|
Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i, file_extension);
|
||||||
img->set_name(image_name);
|
img->set_name(image_name);
|
||||||
_parse_image_save_image(p_state, data, file_extension, i, img);
|
_parse_image_save_image(p_state, data, resource_uri, file_extension, i, img);
|
||||||
}
|
}
|
||||||
|
|
||||||
print_verbose("glTF: Total images: " + itos(p_state->images.size()));
|
print_verbose("glTF: Total images: " + itos(p_state->images.size()));
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ private:
|
|||||||
Error _serialize_images(Ref<GLTFState> p_state);
|
Error _serialize_images(Ref<GLTFState> p_state);
|
||||||
Error _serialize_lights(Ref<GLTFState> p_state);
|
Error _serialize_lights(Ref<GLTFState> p_state);
|
||||||
Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension);
|
Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension);
|
||||||
void _parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image);
|
void _parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_resource_uri, const String &p_file_extension, int p_index, Ref<Image> p_image);
|
||||||
Error _parse_images(Ref<GLTFState> p_state, const String &p_base_path);
|
Error _parse_images(Ref<GLTFState> p_state, const String &p_base_path);
|
||||||
Error _parse_textures(Ref<GLTFState> p_state);
|
Error _parse_textures(Ref<GLTFState> p_state);
|
||||||
Error _parse_texture_samplers(Ref<GLTFState> p_state);
|
Error _parse_texture_samplers(Ref<GLTFState> p_state);
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.2.70",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"mesh_instance_3d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"children":[
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"name":"_Node3D_6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"materials":[
|
||||||
|
{
|
||||||
|
"name":"material",
|
||||||
|
"pbrMetallicRoughness":{
|
||||||
|
"baseColorFactor":[
|
||||||
|
0.9999998807907104,
|
||||||
|
0.9999998807907104,
|
||||||
|
0.9999998807907104,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"baseColorTexture":{
|
||||||
|
"index":0
|
||||||
|
},
|
||||||
|
"metallicFactor":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Mesh_0",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"textures":[
|
||||||
|
{
|
||||||
|
"sampler":0,
|
||||||
|
"source":0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images":[
|
||||||
|
{
|
||||||
|
"mimeType":"image/png",
|
||||||
|
"name":"material_albedo000",
|
||||||
|
"uri":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABZJREFUCJljZGBg+P+fgYGBBUIxMAAAKCAEAplLvcoAAAAASUVORK5CYII="
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":4,
|
||||||
|
"max":[
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
-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"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"samplers":[
|
||||||
|
{
|
||||||
|
"magFilter":9729,
|
||||||
|
"minFilter":9987
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":140,
|
||||||
|
"uri":"data:application/octet-stream;base64,AACAPwAAAAAAAIA/AACAvwAAAAAAAIA/AACAPwAAAAAAAIC/AACAvwAAAAAAAIC/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAACAAEAAAACAAMAAQA="
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.2.70",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"mesh_instance_3d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"children":[
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"name":"_Node3D_6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"materials":[
|
||||||
|
{
|
||||||
|
"name":"material",
|
||||||
|
"pbrMetallicRoughness":{
|
||||||
|
"baseColorFactor":[
|
||||||
|
0.9999998807907104,
|
||||||
|
0.9999998807907104,
|
||||||
|
0.9999998807907104,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"baseColorTexture":{
|
||||||
|
"index":0
|
||||||
|
},
|
||||||
|
"metallicFactor":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Mesh_0",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"textures":[
|
||||||
|
{
|
||||||
|
"sampler":0,
|
||||||
|
"source":0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images":[
|
||||||
|
{
|
||||||
|
"mimeType":"image/png",
|
||||||
|
"name":"material_albedo000",
|
||||||
|
"uri":"texture.png",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":4,
|
||||||
|
"max":[
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
-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"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"samplers":[
|
||||||
|
{
|
||||||
|
"magFilter":9729,
|
||||||
|
"minFilter":9987
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":140,
|
||||||
|
"uri":"data:application/octet-stream;base64,AACAPwAAAAAAAIA/AACAvwAAAAAAAIA/AACAPwAAAAAAAIC/AACAvwAAAAAAAIC/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAACAAEAAAACAAMAAQA="
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 92 B |
@@ -0,0 +1,147 @@
|
|||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v4.2.70",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"mesh_instance_3d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"children":[
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"name":"_Node3D_6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"materials":[
|
||||||
|
{
|
||||||
|
"name":"material",
|
||||||
|
"pbrMetallicRoughness":{
|
||||||
|
"baseColorFactor":[
|
||||||
|
0.9999998807907104,
|
||||||
|
0.9999998807907104,
|
||||||
|
0.9999998807907104,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"baseColorTexture":{
|
||||||
|
"index":0
|
||||||
|
},
|
||||||
|
"metallicFactor":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Mesh_0",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"NORMAL":1,
|
||||||
|
"TEXCOORD_0":2
|
||||||
|
},
|
||||||
|
"indices":3,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"textures":[
|
||||||
|
{
|
||||||
|
"sampler":0,
|
||||||
|
"source":0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images":[
|
||||||
|
{
|
||||||
|
"mimeType":"image/png",
|
||||||
|
"name":"material_albedo000",
|
||||||
|
"uri":"../texture.png",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":4,
|
||||||
|
"max":[
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
-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"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"samplers":[
|
||||||
|
{
|
||||||
|
"magFilter":9729,
|
||||||
|
"minFilter":9987
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":140,
|
||||||
|
"uri":"data:application/octet-stream;base64,AACAPwAAAAAAAIA/AACAvwAAAAAAAIA/AACAPwAAAAAAAIC/AACAvwAAAAAAAIC/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAACAAEAAAACAAMAAQA="
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 92 B |
165
modules/gltf/tests/test_gltf.h
Normal file
165
modules/gltf/tests/test_gltf.h
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* test_gltf.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_H
|
||||||
|
#define TEST_GLTF_H
|
||||||
|
|
||||||
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
|
||||||
|
#include "core/os/os.h"
|
||||||
|
#include "drivers/png/image_loader_png.h"
|
||||||
|
#include "editor/editor_resource_preview.h"
|
||||||
|
#include "editor/import/3d/resource_importer_scene.h"
|
||||||
|
#include "editor/import/resource_importer_texture.h"
|
||||||
|
#include "modules/gltf/editor/editor_scene_importer_gltf.h"
|
||||||
|
#include "modules/gltf/gltf_document.h"
|
||||||
|
#include "modules/gltf/gltf_state.h"
|
||||||
|
#include "scene/3d/mesh_instance_3d.h"
|
||||||
|
#include "scene/3d/skeleton_3d.h"
|
||||||
|
#include "scene/main/window.h"
|
||||||
|
#include "scene/resources/3d/primitive_meshes.h"
|
||||||
|
#include "scene/resources/compressed_texture.h"
|
||||||
|
#include "scene/resources/material.h"
|
||||||
|
#include "scene/resources/packed_scene.h"
|
||||||
|
#include "tests/core/config/test_project_settings.h"
|
||||||
|
|
||||||
|
namespace TestGltf {
|
||||||
|
|
||||||
|
static Node *gltf_import(const String &p_file) {
|
||||||
|
// Setting up importers.
|
||||||
|
Ref<ResourceImporterScene> import_scene;
|
||||||
|
import_scene.instantiate("PackedScene", true);
|
||||||
|
ResourceFormatImporter::get_singleton()->add_importer(import_scene);
|
||||||
|
Ref<EditorSceneFormatImporterGLTF> import_gltf;
|
||||||
|
import_gltf.instantiate();
|
||||||
|
ResourceImporterScene::add_scene_importer(import_gltf);
|
||||||
|
|
||||||
|
// Support processing png files in editor import.
|
||||||
|
Ref<ResourceImporterTexture> import_texture;
|
||||||
|
import_texture.instantiate(true);
|
||||||
|
ResourceFormatImporter::get_singleton()->add_importer(import_texture);
|
||||||
|
|
||||||
|
// Once editor import convert pngs to ctex, we will need to load it as ctex resource.
|
||||||
|
Ref<ResourceFormatLoaderCompressedTexture2D> resource_loader_stream_texture;
|
||||||
|
resource_loader_stream_texture.instantiate();
|
||||||
|
ResourceLoader::add_resource_format_loader(resource_loader_stream_texture);
|
||||||
|
|
||||||
|
HashMap<StringName, Variant> options(21);
|
||||||
|
options["nodes/root_type"] = "";
|
||||||
|
options["nodes/root_name"] = "";
|
||||||
|
options["nodes/apply_root_scale"] = true;
|
||||||
|
options["nodes/root_scale"] = 1.0;
|
||||||
|
options["meshes/ensure_tangents"] = true;
|
||||||
|
options["meshes/generate_lods"] = false;
|
||||||
|
options["meshes/create_shadow_meshes"] = true;
|
||||||
|
options["meshes/light_baking"] = 1;
|
||||||
|
options["meshes/lightmap_texel_size"] = 0.2;
|
||||||
|
options["meshes/force_disable_compression"] = false;
|
||||||
|
options["skins/use_named_skins"] = true;
|
||||||
|
options["animation/import"] = true;
|
||||||
|
options["animation/fps"] = 30;
|
||||||
|
options["animation/trimming"] = false;
|
||||||
|
options["animation/remove_immutable_tracks"] = true;
|
||||||
|
options["import_script/path"] = "";
|
||||||
|
options["extract_path"] = "res://";
|
||||||
|
options["_subresources"] = Dictionary();
|
||||||
|
options["gltf/naming_version"] = 1;
|
||||||
|
|
||||||
|
// Process gltf file, note that this generates `.scn` resource from the 2nd argument.
|
||||||
|
String scene_file = "res://" + p_file.get_file().get_basename();
|
||||||
|
Error err = import_scene->import(0, p_file, scene_file, options, nullptr, nullptr, nullptr);
|
||||||
|
CHECK_MESSAGE(err == OK, "GLTF import failed.");
|
||||||
|
|
||||||
|
Ref<PackedScene> packed_scene = ResourceLoader::load(scene_file + ".scn", "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
|
||||||
|
CHECK_MESSAGE(err == OK, "Loading scene failed.");
|
||||||
|
Node *p_scene = packed_scene->instantiate();
|
||||||
|
|
||||||
|
ResourceImporterScene::remove_scene_importer(import_gltf);
|
||||||
|
ResourceFormatImporter::get_singleton()->remove_importer(import_texture);
|
||||||
|
ResourceLoader::remove_resource_format_loader(resource_loader_stream_texture);
|
||||||
|
return p_scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node *gltf_export_then_import(Node *p_root, const String &p_test_name) {
|
||||||
|
String tempfile = TestUtils::get_temp_path(p_test_name);
|
||||||
|
|
||||||
|
Ref<GLTFDocument> doc;
|
||||||
|
doc.instantiate();
|
||||||
|
Ref<GLTFState> state;
|
||||||
|
state.instantiate();
|
||||||
|
Error err = doc->append_from_scene(p_root, state, EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS);
|
||||||
|
CHECK_MESSAGE(err == OK, "GLTF state generation failed.");
|
||||||
|
|
||||||
|
err = doc->write_to_filesystem(state, tempfile + ".gltf");
|
||||||
|
CHECK_MESSAGE(err == OK, "Writing GLTF to cache dir failed.");
|
||||||
|
|
||||||
|
return gltf_import(tempfile + ".gltf");
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(const String &p_test, const String &p_copy_target = String()) {
|
||||||
|
Error err;
|
||||||
|
|
||||||
|
// Setup project settings since it's needed for the import process.
|
||||||
|
String project_folder = TestUtils::get_temp_path(p_test.get_file().get_basename());
|
||||||
|
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||||
|
da->make_dir_recursive(project_folder.path_join(".godot").path_join("imported"));
|
||||||
|
// Initialize res:// to `project_folder`.
|
||||||
|
TestProjectSettingsInternalsAccessor::resource_path() = project_folder;
|
||||||
|
err = ProjectSettings::get_singleton()->setup(project_folder, String(), true);
|
||||||
|
|
||||||
|
if (p_copy_target.is_empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy all the necessary test data files to the res:// directory.
|
||||||
|
da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||||
|
String test_data = String("modules/gltf/tests/data/").path_join(p_test);
|
||||||
|
da = DirAccess::open(test_data);
|
||||||
|
CHECK_MESSAGE(da.is_valid(), "Unable to open folder.");
|
||||||
|
da->list_dir_begin();
|
||||||
|
for (String item = da->get_next(); !item.is_empty(); item = da->get_next()) {
|
||||||
|
if (!FileAccess::exists(test_data.path_join(item))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Ref<FileAccess> output = FileAccess::open(p_copy_target.path_join(item), FileAccess::WRITE, &err);
|
||||||
|
CHECK_MESSAGE(err == OK, "Unable to open output file.");
|
||||||
|
output->store_buffer(FileAccess::get_file_as_bytes(test_data.path_join(item)));
|
||||||
|
output->close();
|
||||||
|
}
|
||||||
|
da->list_dir_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace TestGltf
|
||||||
|
|
||||||
|
#endif // TOOLS_ENABLED
|
||||||
|
|
||||||
|
#endif // TEST_GLTF_H
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
#ifndef TEST_GLTF_EXTRAS_H
|
#ifndef TEST_GLTF_EXTRAS_H
|
||||||
#define TEST_GLTF_EXTRAS_H
|
#define TEST_GLTF_EXTRAS_H
|
||||||
|
|
||||||
|
#include "test_gltf.h"
|
||||||
#include "tests/test_macros.h"
|
#include "tests/test_macros.h"
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
@@ -47,61 +48,10 @@
|
|||||||
#include "scene/resources/material.h"
|
#include "scene/resources/material.h"
|
||||||
#include "scene/resources/packed_scene.h"
|
#include "scene/resources/packed_scene.h"
|
||||||
|
|
||||||
namespace TestGltfExtras {
|
namespace TestGltf {
|
||||||
|
|
||||||
static Node *_gltf_export_then_import(Node *p_root, String &p_tempfilebase) {
|
|
||||||
Ref<GLTFDocument> doc;
|
|
||||||
doc.instantiate();
|
|
||||||
Ref<GLTFState> state;
|
|
||||||
state.instantiate();
|
|
||||||
Error err = doc->append_from_scene(p_root, state, EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS);
|
|
||||||
CHECK_MESSAGE(err == OK, "GLTF state generation failed.");
|
|
||||||
err = doc->write_to_filesystem(state, p_tempfilebase + ".gltf");
|
|
||||||
CHECK_MESSAGE(err == OK, "Writing GLTF to cache dir failed.");
|
|
||||||
|
|
||||||
// Setting up importers.
|
|
||||||
Ref<ResourceImporterScene> import_scene = memnew(ResourceImporterScene("PackedScene", true));
|
|
||||||
ResourceFormatImporter::get_singleton()->add_importer(import_scene);
|
|
||||||
Ref<EditorSceneFormatImporterGLTF> import_gltf;
|
|
||||||
import_gltf.instantiate();
|
|
||||||
ResourceImporterScene::add_scene_importer(import_gltf);
|
|
||||||
|
|
||||||
// GTLF importer behaves differently outside of editor, it's too late to modify Engine::get_editor_hint
|
|
||||||
// as the registration of runtime extensions already happened, so remove them. See modules/gltf/register_types.cpp
|
|
||||||
GLTFDocument::unregister_all_gltf_document_extensions();
|
|
||||||
|
|
||||||
HashMap<StringName, Variant> options(20);
|
|
||||||
options["nodes/root_type"] = "";
|
|
||||||
options["nodes/root_name"] = "";
|
|
||||||
options["nodes/apply_root_scale"] = true;
|
|
||||||
options["nodes/root_scale"] = 1.0;
|
|
||||||
options["meshes/ensure_tangents"] = true;
|
|
||||||
options["meshes/generate_lods"] = false;
|
|
||||||
options["meshes/create_shadow_meshes"] = true;
|
|
||||||
options["meshes/light_baking"] = 1;
|
|
||||||
options["meshes/lightmap_texel_size"] = 0.2;
|
|
||||||
options["meshes/force_disable_compression"] = false;
|
|
||||||
options["skins/use_named_skins"] = true;
|
|
||||||
options["animation/import"] = true;
|
|
||||||
options["animation/fps"] = 30;
|
|
||||||
options["animation/trimming"] = false;
|
|
||||||
options["animation/remove_immutable_tracks"] = true;
|
|
||||||
options["import_script/path"] = "";
|
|
||||||
options["_subresources"] = Dictionary();
|
|
||||||
options["gltf/naming_version"] = 1;
|
|
||||||
|
|
||||||
// Process gltf file, note that this generates `.scn` resource from the 2nd argument.
|
|
||||||
err = import_scene->import(0, p_tempfilebase + ".gltf", p_tempfilebase, options, nullptr, nullptr, nullptr);
|
|
||||||
CHECK_MESSAGE(err == OK, "GLTF import failed.");
|
|
||||||
ResourceImporterScene::remove_scene_importer(import_gltf);
|
|
||||||
|
|
||||||
Ref<PackedScene> packed_scene = ResourceLoader::load(p_tempfilebase + ".scn", "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
|
|
||||||
CHECK_MESSAGE(err == OK, "Loading scene failed.");
|
|
||||||
Node *p_scene = packed_scene->instantiate();
|
|
||||||
return p_scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[SceneTree][Node] GLTF test mesh and material meta export and import") {
|
TEST_CASE("[SceneTree][Node] GLTF test mesh and material meta export and import") {
|
||||||
|
init("gltf_mesh_material_extras");
|
||||||
// Setup scene.
|
// Setup scene.
|
||||||
Ref<StandardMaterial3D> original_material = memnew(StandardMaterial3D);
|
Ref<StandardMaterial3D> original_material = memnew(StandardMaterial3D);
|
||||||
original_material->set_albedo(Color(1.0, .0, .0));
|
original_material->set_albedo(Color(1.0, .0, .0));
|
||||||
@@ -133,9 +83,11 @@ TEST_CASE("[SceneTree][Node] GLTF test mesh and material meta export and import"
|
|||||||
original->set_meta("extras", node_dict);
|
original->set_meta("extras", node_dict);
|
||||||
original->set_meta("meta_not_nested_under_extras", "should not propagate");
|
original->set_meta("meta_not_nested_under_extras", "should not propagate");
|
||||||
|
|
||||||
|
original->set_owner(SceneTree::get_singleton()->get_root());
|
||||||
|
original_mesh_instance->set_owner(SceneTree::get_singleton()->get_root());
|
||||||
|
|
||||||
// Convert to GLFT and back.
|
// Convert to GLFT and back.
|
||||||
String tempfile = OS::get_singleton()->get_cache_path().path_join("gltf_extras");
|
Node *loaded = gltf_export_then_import(original, "gltf_extras");
|
||||||
Node *loaded = _gltf_export_then_import(original, tempfile);
|
|
||||||
|
|
||||||
// Compare the results.
|
// Compare the results.
|
||||||
CHECK(loaded->get_name() == "node3d");
|
CHECK(loaded->get_name() == "node3d");
|
||||||
@@ -161,6 +113,7 @@ TEST_CASE("[SceneTree][Node] GLTF test mesh and material meta export and import"
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[SceneTree][Node] GLTF test skeleton and bone export and import") {
|
TEST_CASE("[SceneTree][Node] GLTF test skeleton and bone export and import") {
|
||||||
|
init("gltf_skeleton_extras");
|
||||||
// Setup scene.
|
// Setup scene.
|
||||||
Skeleton3D *skeleton = memnew(Skeleton3D);
|
Skeleton3D *skeleton = memnew(Skeleton3D);
|
||||||
skeleton->set_name("skeleton");
|
skeleton->set_name("skeleton");
|
||||||
@@ -189,18 +142,20 @@ TEST_CASE("[SceneTree][Node] GLTF test skeleton and bone export and import") {
|
|||||||
mesh->set_mesh(meshdata);
|
mesh->set_mesh(meshdata);
|
||||||
mesh->set_name("mesh_instance_3d");
|
mesh->set_name("mesh_instance_3d");
|
||||||
|
|
||||||
Node3D *scene = memnew(Node3D);
|
Node3D *original = memnew(Node3D);
|
||||||
SceneTree::get_singleton()->get_root()->add_child(scene);
|
SceneTree::get_singleton()->get_root()->add_child(original);
|
||||||
scene->add_child(skeleton);
|
original->add_child(skeleton);
|
||||||
scene->add_child(mesh);
|
original->add_child(mesh);
|
||||||
scene->set_name("node3d");
|
original->set_name("node3d");
|
||||||
|
|
||||||
// Now that both skeleton and mesh are part of scene, link them.
|
// Now that both skeleton and mesh are part of scene, link them.
|
||||||
mesh->set_skeleton_path(mesh->get_path_to(skeleton));
|
mesh->set_skeleton_path(mesh->get_path_to(skeleton));
|
||||||
|
|
||||||
|
mesh->set_owner(SceneTree::get_singleton()->get_root());
|
||||||
|
original->set_owner(SceneTree::get_singleton()->get_root());
|
||||||
|
|
||||||
// Convert to GLFT and back.
|
// Convert to GLFT and back.
|
||||||
String tempfile = OS::get_singleton()->get_cache_path().path_join("gltf_bone_extras");
|
Node *loaded = gltf_export_then_import(original, "gltf_bone_extras");
|
||||||
Node *loaded = _gltf_export_then_import(scene, tempfile);
|
|
||||||
|
|
||||||
// Compare the results.
|
// Compare the results.
|
||||||
CHECK(loaded->get_name() == "node3d");
|
CHECK(loaded->get_name() == "node3d");
|
||||||
@@ -212,10 +167,10 @@ TEST_CASE("[SceneTree][Node] GLTF test skeleton and bone export and import") {
|
|||||||
|
|
||||||
memdelete(skeleton);
|
memdelete(skeleton);
|
||||||
memdelete(mesh);
|
memdelete(mesh);
|
||||||
memdelete(scene);
|
memdelete(original);
|
||||||
memdelete(loaded);
|
memdelete(loaded);
|
||||||
}
|
}
|
||||||
} // namespace TestGltfExtras
|
} //namespace TestGltf
|
||||||
|
|
||||||
#endif // TOOLS_ENABLED
|
#endif // TOOLS_ENABLED
|
||||||
|
|
||||||
|
|||||||
169
modules/gltf/tests/test_gltf_images.h
Normal file
169
modules/gltf/tests/test_gltf_images.h
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/**************************************************************************/
|
||||||
|
/* test_gltf_images.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_IMAGES_H
|
||||||
|
#define TEST_GLTF_IMAGES_H
|
||||||
|
|
||||||
|
#include "test_gltf.h"
|
||||||
|
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
|
||||||
|
#include "editor/editor_file_system.h"
|
||||||
|
#include "editor/editor_paths.h"
|
||||||
|
#include "scene/resources/image_texture.h"
|
||||||
|
|
||||||
|
namespace TestGltf {
|
||||||
|
Ref<Texture2D> _check_texture(Node *p_node) {
|
||||||
|
MeshInstance3D *mesh_instance_3d = Object::cast_to<MeshInstance3D>(p_node->find_child("mesh_instance_3d", true, true));
|
||||||
|
Ref<StandardMaterial3D> material = mesh_instance_3d->get_active_material(0);
|
||||||
|
Ref<Texture2D> texture = material->get_texture(StandardMaterial3D::TextureParam::TEXTURE_ALBEDO);
|
||||||
|
|
||||||
|
CHECK_MESSAGE(texture->get_size().x == 2, "Texture width not correct.");
|
||||||
|
CHECK_MESSAGE(texture->get_size().y == 2, "Texture height not correct.");
|
||||||
|
|
||||||
|
// Check if the loaded texture pixels are exactly as we expect.
|
||||||
|
for (int x = 0; x < 2; ++x) {
|
||||||
|
for (int y = 0; y < 2; ++y) {
|
||||||
|
Color c = texture->get_image()->get_pixel(x, y);
|
||||||
|
CHECK_MESSAGE(c == Color(x, y, y), "Texture content is incorrect.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[SceneTree][Node] Export GLTF with external texture and import") {
|
||||||
|
init("gltf_images_external_export_import");
|
||||||
|
// Setup scene.
|
||||||
|
Ref<ImageTexture> original_texture;
|
||||||
|
original_texture.instantiate();
|
||||||
|
Ref<Image> image;
|
||||||
|
image.instantiate();
|
||||||
|
image->initialize_data(2, 2, false, Image::FORMAT_RGBA8);
|
||||||
|
for (int x = 0; x < 2; ++x) {
|
||||||
|
for (int y = 0; y < 2; ++y) {
|
||||||
|
image->set_pixel(x, y, Color(x, y, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
original_texture->set_image(image);
|
||||||
|
|
||||||
|
Ref<StandardMaterial3D> original_material;
|
||||||
|
original_material.instantiate();
|
||||||
|
original_material->set_texture(StandardMaterial3D::TextureParam::TEXTURE_ALBEDO, original_texture);
|
||||||
|
original_material->set_name("material");
|
||||||
|
|
||||||
|
Ref<PlaneMesh> original_meshdata;
|
||||||
|
original_meshdata.instantiate();
|
||||||
|
original_meshdata->set_name("planemesh");
|
||||||
|
original_meshdata->surface_set_material(0, original_material);
|
||||||
|
|
||||||
|
MeshInstance3D *original_mesh_instance = memnew(MeshInstance3D);
|
||||||
|
original_mesh_instance->set_mesh(original_meshdata);
|
||||||
|
original_mesh_instance->set_name("mesh_instance_3d");
|
||||||
|
|
||||||
|
Node3D *original = memnew(Node3D);
|
||||||
|
SceneTree::get_singleton()->get_root()->add_child(original);
|
||||||
|
original->add_child(original_mesh_instance);
|
||||||
|
original->set_owner(SceneTree::get_singleton()->get_root());
|
||||||
|
original_mesh_instance->set_owner(SceneTree::get_singleton()->get_root());
|
||||||
|
|
||||||
|
// Convert to GLFT and back.
|
||||||
|
Node *loaded = gltf_export_then_import(original, "gltf_images");
|
||||||
|
_check_texture(loaded);
|
||||||
|
|
||||||
|
memdelete(original_mesh_instance);
|
||||||
|
memdelete(original);
|
||||||
|
memdelete(loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[SceneTree][Node][Editor] Import GLTF from .godot/imported folder with external texture") {
|
||||||
|
init("gltf_placed_in_dot_godot_imported", "res://.godot/imported");
|
||||||
|
|
||||||
|
EditorFileSystem *efs = memnew(EditorFileSystem);
|
||||||
|
EditorResourcePreview *erp = memnew(EditorResourcePreview);
|
||||||
|
|
||||||
|
Node *loaded = gltf_import("res://.godot/imported/gltf_placed_in_dot_godot_imported.gltf");
|
||||||
|
Ref<Texture2D> texture = _check_texture(loaded);
|
||||||
|
|
||||||
|
// In-editor imports of gltf and texture from .godot/imported folder should end up in res:// if extract_path is defined.
|
||||||
|
CHECK_MESSAGE(texture->get_path() == "res://gltf_placed_in_dot_godot_imported_material_albedo000.png", "Texture not parsed as resource.");
|
||||||
|
|
||||||
|
memdelete(loaded);
|
||||||
|
memdelete(erp);
|
||||||
|
memdelete(efs);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[SceneTree][Node][Editor] Import GLTF with texture outside of res:// directory") {
|
||||||
|
init("gltf_pointing_to_texture_outside_of_res_folder", "res://");
|
||||||
|
|
||||||
|
EditorFileSystem *efs = memnew(EditorFileSystem);
|
||||||
|
EditorResourcePreview *erp = memnew(EditorResourcePreview);
|
||||||
|
|
||||||
|
// Copy texture to the parent folder of res:// - i.e. to res://.. where we can't import from.
|
||||||
|
String oneup = TestUtils::get_temp_path("texture.png");
|
||||||
|
Error err;
|
||||||
|
Ref<FileAccess> output = FileAccess::open(oneup, FileAccess::WRITE, &err);
|
||||||
|
CHECK_MESSAGE(err == OK, "Unable to open texture file.");
|
||||||
|
output->store_buffer(FileAccess::get_file_as_bytes("res://texture_source.png"));
|
||||||
|
output->close();
|
||||||
|
|
||||||
|
Node *loaded = gltf_import("res://gltf_pointing_to_texture_outside_of_res_folder.gltf");
|
||||||
|
Ref<Texture2D> texture = _check_texture(loaded);
|
||||||
|
|
||||||
|
// Imports of gltf with texture from outside of res:// folder should end up being copied to res://
|
||||||
|
CHECK_MESSAGE(texture->get_path() == "res://gltf_pointing_to_texture_outside_of_res_folder_material_albedo000.png", "Texture not parsed as resource.");
|
||||||
|
|
||||||
|
memdelete(loaded);
|
||||||
|
memdelete(erp);
|
||||||
|
memdelete(efs);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[SceneTree][Node][Editor] Import GLTF with embedded texture, check how it got extracted") {
|
||||||
|
init("gltf_embedded_texture", "res://");
|
||||||
|
|
||||||
|
EditorFileSystem *efs = memnew(EditorFileSystem);
|
||||||
|
EditorResourcePreview *erp = memnew(EditorResourcePreview);
|
||||||
|
|
||||||
|
Node *loaded = gltf_import("res://embedded_texture.gltf");
|
||||||
|
Ref<Texture2D> texture = _check_texture(loaded);
|
||||||
|
|
||||||
|
// In-editor imports of texture embedded in file should end up with a resource.
|
||||||
|
CHECK_MESSAGE(texture->get_path() == "res://embedded_texture_material_albedo000.png", "Texture not parsed as resource.");
|
||||||
|
|
||||||
|
memdelete(loaded);
|
||||||
|
memdelete(erp);
|
||||||
|
memdelete(efs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace TestGltf
|
||||||
|
|
||||||
|
#endif // TOOLS_ENABLED
|
||||||
|
|
||||||
|
#endif // TEST_GLTF_IMAGES_H
|
||||||
Reference in New Issue
Block a user