diff options
Diffstat (limited to 'modules/gltf')
30 files changed, 710 insertions, 160 deletions
diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub index 71f3ba58d9..d1f337715f 100644 --- a/modules/gltf/SCsub +++ b/modules/gltf/SCsub @@ -7,7 +7,7 @@ env_gltf = env_modules.Clone() # Godot source files env_gltf.add_source_files(env.modules_sources, "*.cpp") -env_gltf.add_source_files(env.modules_sources, "extensions/*.cpp") env_gltf.add_source_files(env.modules_sources, "structures/*.cpp") -if env["tools"]: +SConscript("extensions/SCsub") +if env.editor_build: env_gltf.add_source_files(env.modules_sources, "editor/*.cpp") diff --git a/modules/gltf/config.py b/modules/gltf/config.py index 189b5a831a..130c06d264 100644 --- a/modules/gltf/config.py +++ b/modules/gltf/config.py @@ -26,6 +26,7 @@ def get_doc_classes(): "GLTFSpecGloss", "GLTFState", "GLTFTexture", + "GLTFTextureSampler", ] diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index d2a9022445..936794976d 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFDocumentExtension" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + [GLTFDocument] extension class. </brief_description> <description> + Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of GLTF import or export. </description> <tutorials> </tutorials> @@ -28,6 +30,12 @@ <description> </description> </method> + <method name="_get_supported_extensions" qualifiers="virtual"> + <return type="PackedStringArray" /> + <description> + Returns an array of the GLTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a GLTF file with required extensions can be loaded. + </description> + </method> <method name="_import_node" qualifiers="virtual"> <return type="int" /> <param index="0" name="state" type="GLTFState" /> diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index bac351cc20..4d2df872ea 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -9,7 +9,7 @@ <members> <member name="blend_weights" type="PackedFloat32Array" setter="set_blend_weights" getter="get_blend_weights" default="PackedFloat32Array()"> </member> - <member name="instance_materials" type="Array" setter="set_instance_materials" getter="get_instance_materials" default="[]"> + <member name="instance_materials" type="Material[]" setter="set_instance_materials" getter="get_instance_materials" default="[]"> </member> <member name="mesh" type="ImporterMesh" setter="set_mesh" getter="get_mesh"> </member> diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index e933e6046a..8e48066623 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -1,11 +1,33 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFNode" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + GLTF node class. </brief_description> <description> + Represents a GLTF node. GLTF nodes may have names, transforms, children (other GLTF nodes), and more specialized properties (represented by their own classes). </description> <tutorials> + <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link> </tutorials> + <methods> + <method name="get_additional_data"> + <return type="Variant" /> + <param index="0" name="extension_name" type="StringName" /> + <description> + Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + </description> + </method> + <method name="set_additional_data"> + <return type="void" /> + <param index="0" name="extension_name" type="StringName" /> + <param index="1" name="additional_data" type="Variant" /> + <description> + Sets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + </description> + </method> + </methods> <members> <member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1"> </member> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 1dbd89aed8..d0740cf7ca 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -7,11 +7,27 @@ <tutorials> </tutorials> <methods> + <method name="add_used_extension"> + <return type="void" /> + <param index="0" name="extension_name" type="String" /> + <param index="1" name="required" type="bool" /> + <description> + Appends an extension to the list of extensions used by this GLTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically. + </description> + </method> <method name="get_accessors"> <return type="GLTFAccessor[]" /> <description> </description> </method> + <method name="get_additional_data"> + <return type="Variant" /> + <param index="0" name="extension_name" type="StringName" /> + <description> + Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + </description> + </method> <method name="get_animation_player"> <return type="AnimationPlayer" /> <param index="0" name="idx" type="int" /> @@ -85,6 +101,12 @@ <description> </description> </method> + <method name="get_texture_samplers"> + <return type="GLTFTextureSampler[]" /> + <description> + Retrieves the array of texture samplers that are used by the textures contained in the GLTF. + </description> + </method> <method name="get_textures"> <return type="GLTFTexture[]" /> <description> @@ -106,6 +128,15 @@ <description> </description> </method> + <method name="set_additional_data"> + <return type="void" /> + <param index="0" name="extension_name" type="StringName" /> + <param index="1" name="additional_data" type="Variant" /> + <description> + Sets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + </description> + </method> <method name="set_animations"> <return type="void" /> <param index="0" name="animations" type="GLTFAnimation[]" /> @@ -172,6 +203,13 @@ <description> </description> </method> + <method name="set_texture_samplers"> + <return type="void" /> + <param index="0" name="texture_samplers" type="GLTFTextureSampler[]" /> + <description> + Sets the array of texture samplers that are used by the textures contained in the GLTF. + </description> + </method> <method name="set_textures"> <return type="void" /> <param index="0" name="textures" type="GLTFTexture[]" /> diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml index c0bc424168..f4486fd504 100644 --- a/modules/gltf/doc_classes/GLTFTexture.xml +++ b/modules/gltf/doc_classes/GLTFTexture.xml @@ -7,6 +7,9 @@ <tutorials> </tutorials> <members> + <member name="sampler" type="int" setter="set_sampler" getter="get_sampler" default="-1"> + ID of the texture sampler to use when sampling the image. If -1, then the default texture sampler is used (linear filtering, and repeat wrapping in both axes). + </member> <member name="src_image" type="int" setter="set_src_image" getter="get_src_image" default="0"> </member> </members> diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml new file mode 100644 index 0000000000..1cc69bfe73 --- /dev/null +++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GLTFTextureSampler" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + Represents a GLTF texture sampler + </brief_description> + <description> + Represents a texture sampler as defined by the base GLTF spec. Texture samplers in GLTF specify how to sample data from the texture's base image, when rendering the texture on an object. + </description> + <tutorials> + </tutorials> + <members> + <member name="mag_filter" type="int" setter="set_mag_filter" getter="get_mag_filter" default="9729"> + Texture's magnification filter, used when texture appears larger on screen than the source image. + </member> + <member name="min_filter" type="int" setter="set_min_filter" getter="get_min_filter" default="9987"> + Texture's minification filter, used when the texture appears smaller on screen than the source image. + </member> + <member name="wrap_s" type="int" setter="set_wrap_s" getter="get_wrap_s" default="10497"> + Wrapping mode to use for S-axis (horizontal) texture coordinates. + </member> + <member name="wrap_t" type="int" setter="set_wrap_t" getter="get_wrap_t" default="10497"> + Wrapping mode to use for T-axis (vertical) texture coordinates. + </member> + </members> +</class> diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index ab52761e17..20c9508474 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -77,20 +77,19 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ } else { parameters_arg += "export_extras=False,"; } - if (p_options.has(SNAME("blender/meshes/skins")) && p_options[SNAME("blender/meshes/skins")]) { + if (p_options.has(SNAME("blender/meshes/skins"))) { int32_t skins = p_options["blender/meshes/skins"]; if (skins == BLEND_BONE_INFLUENCES_NONE) { - parameters_arg += "export_all_influences=False,"; + parameters_arg += "export_skins=False,"; } else if (skins == BLEND_BONE_INFLUENCES_COMPATIBLE) { - parameters_arg += "export_all_influences=False,"; + parameters_arg += "export_all_influences=False,export_skins=True,"; } else if (skins == BLEND_BONE_INFLUENCES_ALL) { - parameters_arg += "export_all_influences=True,"; + parameters_arg += "export_all_influences=True,export_skins=True,"; } - parameters_arg += "export_skins=True,"; } else { parameters_arg += "export_skins=False,"; } - if (p_options.has(SNAME("blender/materials/export_materials")) && p_options[SNAME("blender/materials/export_materials")]) { + if (p_options.has(SNAME("blender/materials/export_materials"))) { int32_t exports = p_options["blender/materials/export_materials"]; if (exports == BLEND_MATERIAL_EXPORT_PLACEHOLDER) { parameters_arg += "export_materials='PLACEHOLDER',"; @@ -115,7 +114,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ } else { parameters_arg += "export_colors=False,"; } - if (p_options.has(SNAME("blender/nodes/visible")) && p_options[SNAME("blender/nodes/visible")]) { + if (p_options.has(SNAME("blender/nodes/visible"))) { int32_t visible = p_options["blender/nodes/visible"]; if (visible == BLEND_VISIBLE_VISIBLE_ONLY) { parameters_arg += "use_visible=True,"; @@ -180,13 +179,13 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ "export_format='GLTF_SEPARATE'," "export_yup=True," + parameters_arg; - String script = + String export_script = String("import bpy, sys;") + "print('Blender 3.0 or higher is required.', file=sys.stderr) if bpy.app.version < (3, 0, 0) else None;" + vformat("bpy.ops.wm.open_mainfile(filepath='%s');", source_global) + unpack_all + vformat("bpy.ops.export_scene.gltf(export_keep_originals=True,%s);", common_args); - print_verbose(script); + print_verbose(export_script); // Run script with configured Blender binary. @@ -201,7 +200,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ List<String> args; args.push_back("--background"); args.push_back("--python-expr"); - args.push_back(script); + args.push_back(export_script); String standard_out; int ret; @@ -261,7 +260,7 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li #define ADD_OPTION_ENUM(PATH, ENUM_HINT, VALUE) \ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, SNAME(PATH), PROPERTY_HINT_ENUM, ENUM_HINT), VALUE)); - ADD_OPTION_ENUM("blender/nodes/visible", "Visible Only,Renderable,All", BLEND_VISIBLE_ALL); + ADD_OPTION_ENUM("blender/nodes/visible", "All,Visible Only,Renderable", BLEND_VISIBLE_ALL); ADD_OPTION_BOOL("blender/nodes/punctual_lights", true); ADD_OPTION_BOOL("blender/nodes/cameras", true); ADD_OPTION_BOOL("blender/nodes/custom_properties", true); @@ -350,9 +349,7 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) { bool EditorFileSystemImportFormatSupportQueryBlend::is_active() const { bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled"); - String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); - - if (blend_enabled && !_test_blender_path(blender_path)) { + if (blend_enabled && !_test_blender_path(EDITOR_GET("filesystem/import/blender/blender3_path").operator String())) { // Intending to import Blender, but blend not configured. return true; } diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h index dd1c1b9889..a1485ff82e 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.h +++ b/modules/gltf/editor/editor_scene_importer_blend.h @@ -45,9 +45,9 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter { public: enum { + BLEND_VISIBLE_ALL, BLEND_VISIBLE_VISIBLE_ONLY, - BLEND_VISIBLE_RENDERABLE, - BLEND_VISIBLE_ALL + BLEND_VISIBLE_RENDERABLE }; enum { BLEND_BONE_INFLUENCES_NONE, diff --git a/modules/gltf/extensions/SCsub b/modules/gltf/extensions/SCsub new file mode 100644 index 0000000000..ad214bb79c --- /dev/null +++ b/modules/gltf/extensions/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_gltf = env_modules.Clone() + +# Godot source files +env_gltf.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/gltf/extensions/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp index ab5a15c671..d00bead61c 100644 --- a/modules/gltf/extensions/gltf_light.cpp +++ b/modules/gltf/extensions/gltf_light.cpp @@ -109,6 +109,7 @@ void GLTFLight::set_outer_cone_angle(float p_outer_cone_angle) { Ref<GLTFLight> GLTFLight::from_node(const Light3D *p_light) { Ref<GLTFLight> l; l.instantiate(); + ERR_FAIL_COND_V_MSG(!p_light, l, "Tried to create a GLTFLight from a Light3D node, but the given node was null."); l->color = p_light->get_color(); if (cast_to<DirectionalLight3D>(p_light)) { l->light_type = "directional"; @@ -141,18 +142,17 @@ Light3D *GLTFLight::to_node() const { light->set_color(color); return light; } - const float range = CLAMP(this->range, 0, 4096); if (light_type == "point") { OmniLight3D *light = memnew(OmniLight3D); light->set_param(OmniLight3D::PARAM_ENERGY, intensity); - light->set_param(OmniLight3D::PARAM_RANGE, range); + light->set_param(OmniLight3D::PARAM_RANGE, CLAMP(range, 0, 4096)); light->set_color(color); return light; } if (light_type == "spot") { SpotLight3D *light = memnew(SpotLight3D); light->set_param(SpotLight3D::PARAM_ENERGY, intensity); - light->set_param(SpotLight3D::PARAM_RANGE, range); + light->set_param(SpotLight3D::PARAM_RANGE, CLAMP(range, 0, 4096)); light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad_to_deg(outer_cone_angle)); light->set_color(color); // Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h index c20c87f798..23bf33869e 100644 --- a/modules/gltf/gltf_defines.h +++ b/modules/gltf/gltf_defines.h @@ -58,6 +58,7 @@ class GLTFSkin; class GLTFSpecGloss; class GLTFState; class GLTFTexture; +class GLTFTextureSampler; // GLTF index aliases. using GLTFAccessorIndex = int; @@ -66,13 +67,14 @@ using GLTFBufferIndex = int; using GLTFBufferViewIndex = int; using GLTFCameraIndex = int; using GLTFImageIndex = int; +using GLTFLightIndex = int; using GLTFMaterialIndex = int; using GLTFMeshIndex = int; -using GLTFLightIndex = int; using GLTFNodeIndex = int; using GLTFSkeletonIndex = int; using GLTFSkinIndex = int; using GLTFTextureIndex = int; +using GLTFTextureSamplerIndex = int; enum GLTFType { TYPE_SCALAR, diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 0ed212e21f..99803ed05d 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -145,6 +145,12 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { return Error::FAILED; } + /* STEP SERIALIZE TEXTURE SAMPLERS */ + err = _serialize_texture_samplers(state); + if (err != OK) { + return Error::FAILED; + } + /* STEP SERIALIZE ANIMATIONS */ err = _serialize_animations(state); if (err != OK) { @@ -191,14 +197,14 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { return Error::FAILED; } - /* STEP SERIALIZE SCENE */ + /* STEP SERIALIZE LIGHTS */ err = _serialize_lights(state); if (err != OK) { return Error::FAILED; } /* STEP SERIALIZE EXTENSIONS */ - err = _serialize_extensions(state); + err = _serialize_gltf_extensions(state); if (err != OK) { return Error::FAILED; } @@ -219,9 +225,9 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { return OK; } -Error GLTFDocument::_serialize_extensions(Ref<GLTFState> state) const { - Array extensions_used; - Array extensions_required; +Error GLTFDocument::_serialize_gltf_extensions(Ref<GLTFState> state) const { + Vector<String> extensions_used = state->extensions_used; + Vector<String> extensions_required = state->extensions_required; if (!state->lights.is_empty()) { extensions_used.push_back("KHR_lights_punctual"); } @@ -230,9 +236,11 @@ Error GLTFDocument::_serialize_extensions(Ref<GLTFState> state) const { extensions_required.push_back("KHR_texture_transform"); } if (!extensions_used.is_empty()) { + extensions_used.sort(); state->json["extensionsUsed"] = extensions_used; } if (!extensions_required.is_empty()) { + extensions_required.sort(); state->json["extensionsRequired"] = extensions_required; } return OK; @@ -401,47 +409,47 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) { Array nodes; for (int i = 0; i < state->nodes.size(); i++) { Dictionary node; - Ref<GLTFNode> n = state->nodes[i]; + Ref<GLTFNode> gltf_node = state->nodes[i]; Dictionary extensions; node["extensions"] = extensions; - if (!n->get_name().is_empty()) { - node["name"] = n->get_name(); + if (!gltf_node->get_name().is_empty()) { + node["name"] = gltf_node->get_name(); } - if (n->camera != -1) { - node["camera"] = n->camera; + if (gltf_node->camera != -1) { + node["camera"] = gltf_node->camera; } - if (n->light != -1) { + if (gltf_node->light != -1) { Dictionary lights_punctual; extensions["KHR_lights_punctual"] = lights_punctual; - lights_punctual["light"] = n->light; + lights_punctual["light"] = gltf_node->light; } - if (n->mesh != -1) { - node["mesh"] = n->mesh; + if (gltf_node->mesh != -1) { + node["mesh"] = gltf_node->mesh; } - if (n->skin != -1) { - node["skin"] = n->skin; + if (gltf_node->skin != -1) { + node["skin"] = gltf_node->skin; } - if (n->skeleton != -1 && n->skin < 0) { + if (gltf_node->skeleton != -1 && gltf_node->skin < 0) { } - if (n->xform != Transform3D()) { - node["matrix"] = _xform_to_array(n->xform); + if (gltf_node->xform != Transform3D()) { + node["matrix"] = _xform_to_array(gltf_node->xform); } - if (!n->rotation.is_equal_approx(Quaternion())) { - node["rotation"] = _quaternion_to_array(n->rotation); + if (!gltf_node->rotation.is_equal_approx(Quaternion())) { + node["rotation"] = _quaternion_to_array(gltf_node->rotation); } - if (!n->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) { - node["scale"] = _vec3_to_arr(n->scale); + if (!gltf_node->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) { + node["scale"] = _vec3_to_arr(gltf_node->scale); } - if (!n->position.is_equal_approx(Vector3())) { - node["translation"] = _vec3_to_arr(n->position); + if (!gltf_node->position.is_zero_approx()) { + node["translation"] = _vec3_to_arr(gltf_node->position); } - if (n->children.size()) { + if (gltf_node->children.size()) { Array children; - for (int j = 0; j < n->children.size(); j++) { - children.push_back(n->children[j]); + for (int j = 0; j < gltf_node->children.size(); j++) { + children.push_back(gltf_node->children[j]); } node["children"] = children; } @@ -450,7 +458,7 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) { Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; ERR_CONTINUE(ext.is_null()); ERR_CONTINUE(!state->scene_nodes.find(i)); - Error err = ext->export_node(state, n, state->json, state->scene_nodes[i]); + Error err = ext->export_node(state, gltf_node, node, state->scene_nodes[i]); ERR_CONTINUE(err != OK); } @@ -463,23 +471,23 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) { String GLTFDocument::_gen_unique_name(Ref<GLTFState> state, const String &p_name) { const String s_name = p_name.validate_node_name(); - String name; + String u_name; int index = 1; while (true) { - name = s_name; + u_name = s_name; if (index > 1) { - name += itos(index); + u_name += itos(index); } - if (!state->unique_names.has(name)) { + if (!state->unique_names.has(u_name)) { break; } index++; } - state->unique_names.insert(name); + state->unique_names.insert(u_name); - return name; + return u_name; } String GLTFDocument::_sanitize_animation_name(const String &p_name) { @@ -487,39 +495,39 @@ String GLTFDocument::_sanitize_animation_name(const String &p_name) { // (See animation/animation_player.cpp::add_animation) // TODO: Consider adding invalid_characters or a validate_animation_name to animation_player to mirror Node. - String name = p_name.validate_node_name(); - name = name.replace(",", ""); - name = name.replace("[", ""); - return name; + String anim_name = p_name.validate_node_name(); + anim_name = anim_name.replace(",", ""); + anim_name = anim_name.replace("[", ""); + return anim_name; } String GLTFDocument::_gen_unique_animation_name(Ref<GLTFState> state, const String &p_name) { const String s_name = _sanitize_animation_name(p_name); - String name; + String u_name; int index = 1; while (true) { - name = s_name; + u_name = s_name; if (index > 1) { - name += itos(index); + u_name += itos(index); } - if (!state->unique_animation_names.has(name)) { + if (!state->unique_animation_names.has(u_name)) { break; } index++; } - state->unique_animation_names.insert(name); + state->unique_animation_names.insert(u_name); - return name; + return u_name; } String GLTFDocument::_sanitize_bone_name(const String &p_name) { - String name = p_name; - name = name.replace(":", "_"); - name = name.replace("/", "_"); - return name; + String bone_name = p_name; + bone_name = bone_name.replace(":", "_"); + bone_name = bone_name.replace("/", "_"); + return bone_name; } String GLTFDocument::_gen_unique_bone_name(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i, const String &p_name) { @@ -527,23 +535,23 @@ String GLTFDocument::_gen_unique_bone_name(Ref<GLTFState> state, const GLTFSkele if (s_name.is_empty()) { s_name = "bone"; } - String name; + String u_name; int index = 1; while (true) { - name = s_name; + u_name = s_name; if (index > 1) { - name += "_" + itos(index); + u_name += "_" + itos(index); } - if (!state->skeletons[skel_i]->unique_names.has(name)) { + if (!state->skeletons[skel_i]->unique_names.has(u_name)) { break; } index++; } - state->skeletons.write[skel_i]->unique_names.insert(name); + state->skeletons.write[skel_i]->unique_names.insert(u_name); - return name; + return u_name; } Error GLTFDocument::_parse_scenes(Ref<GLTFState> state) { @@ -2582,10 +2590,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { Mesh::PRIMITIVE_TRIANGLES, // 4 TRIANGLES Mesh::PRIMITIVE_TRIANGLE_STRIP, // 5 TRIANGLE_STRIP Mesh::PRIMITIVE_TRIANGLES, // 6 TRIANGLE_FAN fan not supported, should be converted -#ifndef _MSC_VER -#warning line loop and triangle fan are not supported and need to be converted to lines and triangles -#endif - + // TODO: Line loop and triangle fan are not supported and need to be converted to lines and triangles. }; primitive = primitives2[mode]; @@ -2673,7 +2678,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { } else if (a.has("JOINTS_0") && a.has("JOINTS_1")) { PackedInt32Array joints_0 = _decode_accessor_as_ints(state, a["JOINTS_0"], true); PackedInt32Array joints_1 = _decode_accessor_as_ints(state, a["JOINTS_1"], true); - ERR_FAIL_COND_V(joints_0.size() != joints_0.size(), ERR_INVALID_DATA); + ERR_FAIL_COND_V(joints_0.size() != joints_1.size(), ERR_INVALID_DATA); int32_t weight_8_count = JOINT_GROUP_SIZE * 2; Vector<int> joints; joints.resize(vertex_num * weight_8_count); @@ -2816,8 +2821,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { if (j == 0) { const Array &target_names = extras.has("targetNames") ? (Array)extras["targetNames"] : Array(); for (int k = 0; k < targets.size(); k++) { - const String name = k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k); - import_mesh->add_blend_shape(name); + import_mesh->add_blend_shape(k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k)); } } @@ -3026,12 +3030,12 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path d["mimeType"] = "image/png"; } else { ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); - String name = state->images[i]->get_name(); - if (name.is_empty()) { - name = itos(i); + String img_name = state->images[i]->get_name(); + if (img_name.is_empty()) { + img_name = itos(i); } - name = _gen_unique_name(state, name); - name = name.pad_zeros(3) + ".png"; + img_name = _gen_unique_name(state, img_name); + img_name = img_name.pad_zeros(3) + ".png"; String texture_dir = "textures"; String path = p_path.get_base_dir(); String new_texture_dir = path + "/" + texture_dir; @@ -3039,8 +3043,8 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path if (!da->dir_exists(new_texture_dir)) { da->make_dir(new_texture_dir); } - image->save_png(new_texture_dir.path_join(name)); - d["uri"] = texture_dir.path_join(name).uri_encode(); + image->save_png(new_texture_dir.path_join(img_name)); + d["uri"] = texture_dir.path_join(img_name).uri_encode(); } images.push_back(d); } @@ -3217,6 +3221,11 @@ Error GLTFDocument::_serialize_textures(Ref<GLTFState> state) { Ref<GLTFTexture> t = state->textures[i]; ERR_CONTINUE(t->get_src_image() == -1); d["source"] = t->get_src_image(); + + GLTFTextureSamplerIndex sampler_index = t->get_sampler(); + if (sampler_index != -1) { + d["sampler"] = sampler_index; + } textures.push_back(d); } state->json["textures"] = textures; @@ -3238,13 +3247,18 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> state) { Ref<GLTFTexture> t; t.instantiate(); t->set_src_image(d["source"]); + if (d.has("sampler")) { + t->set_sampler(d["sampler"]); + } else { + t->set_sampler(-1); + } state->textures.push_back(t); } return OK; } -GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture) { +GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats) { ERR_FAIL_COND_V(p_texture.is_null(), -1); Ref<GLTFTexture> gltf_texture; gltf_texture.instantiate(); @@ -3252,6 +3266,7 @@ GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> state, Ref<Texture2D> GLTFImageIndex gltf_src_image_i = state->images.size(); state->images.push_back(p_texture); gltf_texture->set_src_image(gltf_src_image_i); + gltf_texture->set_sampler(_set_sampler_for_mode(state, p_filter_mode, p_repeats)); GLTFTextureIndex gltf_texture_i = state->textures.size(); state->textures.push_back(gltf_texture); return gltf_texture_i; @@ -3266,6 +3281,102 @@ Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> state, const GLTFTextur return state->images[image]; } +GLTFTextureSamplerIndex GLTFDocument::_set_sampler_for_mode(Ref<GLTFState> state, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats) { + for (int i = 0; i < state->texture_samplers.size(); ++i) { + if (state->texture_samplers[i]->get_filter_mode() == p_filter_mode) { + return i; + } + } + + GLTFTextureSamplerIndex gltf_sampler_i = state->texture_samplers.size(); + Ref<GLTFTextureSampler> gltf_sampler; + gltf_sampler.instantiate(); + gltf_sampler->set_filter_mode(p_filter_mode); + gltf_sampler->set_wrap_mode(p_repeats); + state->texture_samplers.push_back(gltf_sampler); + return gltf_sampler_i; +} + +Ref<GLTFTextureSampler> GLTFDocument::_get_sampler_for_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture) { + ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref<Texture2D>()); + const GLTFTextureSamplerIndex sampler = state->textures[p_texture]->get_sampler(); + + if (sampler == -1) { + return state->default_texture_sampler; + } else { + ERR_FAIL_INDEX_V(sampler, state->texture_samplers.size(), Ref<GLTFTextureSampler>()); + + return state->texture_samplers[sampler]; + } +} + +Error GLTFDocument::_serialize_texture_samplers(Ref<GLTFState> state) { + if (!state->texture_samplers.size()) { + return OK; + } + + Array samplers; + for (int32_t i = 0; i < state->texture_samplers.size(); ++i) { + Dictionary d; + Ref<GLTFTextureSampler> s = state->texture_samplers[i]; + d["magFilter"] = s->get_mag_filter(); + d["minFilter"] = s->get_min_filter(); + d["wrapS"] = s->get_wrap_s(); + d["wrapT"] = s->get_wrap_t(); + samplers.push_back(d); + } + state->json["samplers"] = samplers; + + return OK; +} + +Error GLTFDocument::_parse_texture_samplers(Ref<GLTFState> state) { + state->default_texture_sampler.instantiate(); + state->default_texture_sampler->set_min_filter(GLTFTextureSampler::FilterMode::LINEAR_MIPMAP_LINEAR); + state->default_texture_sampler->set_mag_filter(GLTFTextureSampler::FilterMode::LINEAR); + state->default_texture_sampler->set_wrap_s(GLTFTextureSampler::WrapMode::REPEAT); + state->default_texture_sampler->set_wrap_t(GLTFTextureSampler::WrapMode::REPEAT); + + if (!state->json.has("samplers")) { + return OK; + } + + const Array &samplers = state->json["samplers"]; + for (int i = 0; i < samplers.size(); ++i) { + const Dictionary &d = samplers[i]; + + Ref<GLTFTextureSampler> sampler; + sampler.instantiate(); + + if (d.has("minFilter")) { + sampler->set_min_filter(d["minFilter"]); + } else { + sampler->set_min_filter(GLTFTextureSampler::FilterMode::LINEAR_MIPMAP_LINEAR); + } + if (d.has("magFilter")) { + sampler->set_mag_filter(d["magFilter"]); + } else { + sampler->set_mag_filter(GLTFTextureSampler::FilterMode::LINEAR); + } + + if (d.has("wrapS")) { + sampler->set_wrap_s(d["wrapS"]); + } else { + sampler->set_wrap_s(GLTFTextureSampler::WrapMode::DEFAULT); + } + + if (d.has("wrapT")) { + sampler->set_wrap_t(d["wrapT"]); + } else { + sampler->set_wrap_t(GLTFTextureSampler::WrapMode::DEFAULT); + } + + state->texture_samplers.push_back(sampler); + } + + return OK; +} + Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { Array materials; for (int32_t i = 0; i < state->materials.size(); i++) { @@ -3297,7 +3408,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) { albedo_texture->set_name(material->get_name() + "_albedo"); - gltf_texture_index = _set_texture(state, albedo_texture); + gltf_texture_index = _set_texture(state, albedo_texture, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); } if (gltf_texture_index != -1) { bct["index"] = gltf_texture_index; @@ -3373,7 +3484,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { height = albedo_texture->get_height(); width = albedo_texture->get_width(); } - orm_image->create(width, height, false, Image::FORMAT_RGBA8); + orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8); if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) { ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS); } @@ -3427,7 +3538,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { GLTFTextureIndex orm_texture_index = -1; if (has_ao || has_roughness || has_metalness) { orm_texture->set_name(material->get_name() + "_orm"); - orm_texture_index = _set_texture(state, orm_texture); + orm_texture_index = _set_texture(state, orm_texture, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); } if (has_ao) { Dictionary occt; @@ -3482,7 +3593,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { GLTFTextureIndex gltf_texture_index = -1; if (tex.is_valid() && tex->get_image().is_valid()) { tex->set_name(material->get_name() + "_normal"); - gltf_texture_index = _set_texture(state, tex); + gltf_texture_index = _set_texture(state, tex, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); } nt["scale"] = material->get_normal_scale(); if (gltf_texture_index != -1) { @@ -3505,7 +3616,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { GLTFTextureIndex gltf_texture_index = -1; if (emission_texture.is_valid() && emission_texture->get_image().is_valid()) { emission_texture->set_name(material->get_name() + "_emission"); - gltf_texture_index = _set_texture(state, emission_texture); + gltf_texture_index = _set_texture(state, emission_texture, material->get_texture_filter(), material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT)); } if (gltf_texture_index != -1) { @@ -3564,6 +3675,11 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) { if (sgm.has("diffuseTexture")) { const Dictionary &diffuse_texture_dict = sgm["diffuseTexture"]; if (diffuse_texture_dict.has("index")) { + Ref<GLTFTextureSampler> diffuse_sampler = _get_sampler_for_texture(state, diffuse_texture_dict["index"]); + if (diffuse_sampler.is_valid()) { + material->set_texture_filter(diffuse_sampler->get_filter_mode()); + material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, diffuse_sampler->get_wrap_mode()); + } Ref<Texture2D> diffuse_texture = _get_texture(state, diffuse_texture_dict["index"]); if (diffuse_texture.is_valid()) { spec_gloss->diffuse_img = diffuse_texture->get_image(); @@ -3612,6 +3728,9 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) { if (mr.has("baseColorTexture")) { const Dictionary &bct = mr["baseColorTexture"]; if (bct.has("index")) { + Ref<GLTFTextureSampler> bct_sampler = _get_sampler_for_texture(state, bct["index"]); + material->set_texture_filter(bct_sampler->get_filter_mode()); + material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, bct_sampler->get_wrap_mode()); material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(state, bct["index"])); } if (!mr.has("baseColorFactor")) { @@ -3741,13 +3860,11 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Re if (r_spec_gloss->diffuse_img.is_null()) { return; } - Ref<Image> rm_img; - rm_img.instantiate(); bool has_roughness = false; bool has_metal = false; p_material->set_roughness(1.0f); p_material->set_metallic(1.0f); - rm_img->create(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8); + Ref<Image> rm_img = Image::create_empty(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8); r_spec_gloss->spec_gloss_img->decompress(); if (r_spec_gloss->diffuse_img.is_valid()) { r_spec_gloss->diffuse_img->decompress(); @@ -4299,14 +4416,14 @@ Error GLTFDocument::_determine_skeleton_roots(Ref<GLTFState> state, const GLTFSk Ref<GLTFSkeleton> skeleton = state->skeletons.write[skel_i]; - Vector<GLTFNodeIndex> owners; - disjoint_set.get_representatives(owners); + Vector<GLTFNodeIndex> representatives; + disjoint_set.get_representatives(representatives); Vector<GLTFNodeIndex> roots; - for (int i = 0; i < owners.size(); ++i) { + for (int i = 0; i < representatives.size(); ++i) { Vector<GLTFNodeIndex> set; - disjoint_set.get_members(set, owners[i]); + disjoint_set.get_members(set, representatives[i]); const GLTFNodeIndex root = _find_highest_node(state, set); ERR_FAIL_COND_V(root < 0, FAILED); roots.push_back(root); @@ -4837,12 +4954,12 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) { Array samplers = d["samplers"]; if (d.has("name")) { - const String name = d["name"]; - const String name_lower = name.to_lower(); - if (name_lower.begins_with("loop") || name_lower.ends_with("loop") || name_lower.begins_with("cycle") || name_lower.ends_with("cycle")) { + const String anim_name = d["name"]; + const String anim_name_lower = anim_name.to_lower(); + if (anim_name_lower.begins_with("loop") || anim_name_lower.ends_with("loop") || anim_name_lower.begins_with("cycle") || anim_name_lower.ends_with("cycle")) { animation->set_loop(true); } - animation->set_name(_gen_unique_animation_name(state, name)); + animation->set_name(_gen_unique_animation_name(state, anim_name)); } for (int j = 0; j < channels.size(); j++) { @@ -5007,7 +5124,7 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInst Ref<GLTFMesh> gltf_mesh; gltf_mesh.instantiate(); - Array instance_materials; + TypedArray<Material> instance_materials; for (int32_t surface_i = 0; surface_i < current_mesh->get_surface_count(); surface_i++) { Ref<Material> mat = current_mesh->get_surface_material(surface_i); if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) { @@ -5046,7 +5163,7 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> sta return mi; } -Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index) { +Light3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index) { Ref<GLTFNode> gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr); @@ -5102,6 +5219,7 @@ Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, const GLTFNodeInde return spatial; } + void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) { bool retflag = true; _check_visibility(p_current, retflag); @@ -5271,7 +5389,7 @@ void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex cell_xform.basis.scale(Vector3(p_grid_map->get_cell_scale(), p_grid_map->get_cell_scale(), p_grid_map->get_cell_scale())); - cell_xform.set_origin(p_grid_map->map_to_world( + cell_xform.set_origin(p_grid_map->map_to_local( Vector3(cell_location.x, cell_location.y, cell_location.z))); Ref<GLTFMesh> gltf_mesh; gltf_mesh.instantiate(); @@ -5704,15 +5822,15 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps) { Ref<GLTFAnimation> anim = state->animations[index]; - String name = anim->get_name(); - if (name.is_empty()) { + String anim_name = anim->get_name(); + if (anim_name.is_empty()) { // No node represent these, and they are not in the hierarchy, so just make a unique name - name = _gen_unique_name(state, "Animation"); + anim_name = _gen_unique_name(state, "Animation"); } Ref<Animation> animation; animation.instantiate(); - animation->set_name(name); + animation->set_name(anim_name); if (anim->get_loop()) { animation->set_loop_mode(Animation::LOOP_LINEAR); @@ -5935,7 +6053,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, } else { library = ap->get_animation_library(""); } - library->add_animation(name, animation); + library->add_animation(anim_name, animation); } void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) { @@ -6171,7 +6289,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state for (int32_t key_i = 0; key_i < key_count; key_i++) { Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i); - p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian); + p_track.rotation_track.values.write[key_i] = Quaternion::from_euler(rotation_radian); } } else if (path.contains(":scale")) { p_track.scale_track.times = times; @@ -6474,8 +6592,10 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> err = ext->import_preflight(state); ERR_FAIL_COND_V(err != OK, err); } + err = _parse_gltf_state(state, p_path, p_bake_fps); ERR_FAIL_COND_V(err != OK, err); + return OK; } @@ -6831,6 +6951,11 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_sear ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + /* PARSE TEXTURE SAMPLERS */ + err = _parse_texture_samplers(state); + + ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR); + /* PARSE TEXTURES */ err = _parse_textures(state); @@ -6916,12 +7041,32 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> state) { ERR_FAIL_NULL_V(state, ERR_PARSE_ERROR); - if (state->json.has("extensionsRequired") && state->json["extensionsRequired"].get_type() == Variant::ARRAY) { - Array extensions_required = state->json["extensionsRequired"]; - if (extensions_required.find("KHR_draco_mesh_compression") != -1) { - ERR_PRINT("glTF2 extension KHR_draco_mesh_compression is not supported."); - return ERR_UNAVAILABLE; + if (state->json.has("extensionsUsed")) { + Vector<String> ext_array = state->json["extensionsUsed"]; + state->extensions_used = ext_array; + } + if (state->json.has("extensionsRequired")) { + Vector<String> ext_array = state->json["extensionsRequired"]; + state->extensions_required = ext_array; + } + HashSet<String> supported_extensions; + supported_extensions.insert("KHR_lights_punctual"); + supported_extensions.insert("KHR_materials_pbrSpecularGlossiness"); + supported_extensions.insert("KHR_texture_transform"); + for (int ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Vector<String> ext_supported_extensions = ext->get_supported_extensions(); + for (int i = 0; i < ext_supported_extensions.size(); ++i) { + supported_extensions.insert(ext_supported_extensions[i]); } } - return OK; + Error ret = Error::OK; + for (int i = 0; i < state->extensions_required.size(); i++) { + if (!supported_extensions.has(state->extensions_required[i])) { + ERR_PRINT("GLTF: Can't import file '" + state->filename + "', required extension '" + String(state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?"); + ret = ERR_UNAVAILABLE; + } + } + return ret; } diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 36a2f94a4e..62b6e29fe0 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -95,9 +95,14 @@ private: String _gen_unique_bone_name(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i, const String &p_name); - GLTFTextureIndex _set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture); + GLTFTextureIndex _set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture, + StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); Ref<Texture2D> _get_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture); + GLTFTextureSamplerIndex _set_sampler_for_mode(Ref<GLTFState> state, + StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); + Ref<GLTFTextureSampler> _get_sampler_for_texture(Ref<GLTFState> state, + const GLTFTextureIndex p_texture); Error _parse_json(const String &p_path, Ref<GLTFState> state); Error _parse_glb(Ref<FileAccess> f, Ref<GLTFState> state); void _compute_node_heights(Ref<GLTFState> state); @@ -145,10 +150,12 @@ private: const bool p_for_vertex); Error _parse_meshes(Ref<GLTFState> state); Error _serialize_textures(Ref<GLTFState> state); + Error _serialize_texture_samplers(Ref<GLTFState> state); Error _serialize_images(Ref<GLTFState> state, const String &p_path); Error _serialize_lights(Ref<GLTFState> state); Error _parse_images(Ref<GLTFState> state, const String &p_base_path); Error _parse_textures(Ref<GLTFState> state); + Error _parse_texture_samplers(Ref<GLTFState> state); Error _parse_materials(Ref<GLTFState> state); void _set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> material); void spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, @@ -188,7 +195,7 @@ private: const GLTFNodeIndex bone_index); ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, const GLTFNodeIndex node_index); Camera3D *_generate_camera(Ref<GLTFState> state, const GLTFNodeIndex node_index); - Node3D *_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index); + Light3D *_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index); Node3D *_generate_spatial(Ref<GLTFState> state, const GLTFNodeIndex node_index); void _assign_scene_names(Ref<GLTFState> state); template <class T> @@ -265,7 +272,7 @@ private: Dictionary _serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material); Error _serialize_version(Ref<GLTFState> state); Error _serialize_file(Ref<GLTFState> state, const String p_path); - Error _serialize_extensions(Ref<GLTFState> state) const; + Error _serialize_gltf_extensions(Ref<GLTFState> state) const; public: // https://www.itu.int/rec/R-REC-BT.601 diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp index d0bd7651e0..713779712c 100644 --- a/modules/gltf/gltf_document_extension.cpp +++ b/modules/gltf/gltf_document_extension.cpp @@ -31,6 +31,7 @@ #include "gltf_document_extension.h" void GLTFDocumentExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_supported_extensions); GDVIRTUAL_BIND(_import_preflight, "state"); GDVIRTUAL_BIND(_import_post_parse, "state"); GDVIRTUAL_BIND(_import_node, "state", "gltf_node", "json", "node"); @@ -40,49 +41,45 @@ void GLTFDocumentExtension::_bind_methods() { GDVIRTUAL_BIND(_export_post, "state"); } +Vector<String> GLTFDocumentExtension::get_supported_extensions() { + Vector<String> ret; + GDVIRTUAL_CALL(_get_supported_extensions, ret); + return ret; +} + Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); int err = OK; - if (GDVIRTUAL_CALL(_import_post, p_state, p_root, err)) { - return Error(err); - } - return OK; + GDVIRTUAL_CALL(_import_post, p_state, p_root, err); + return Error(err); } Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); int err = OK; - if (GDVIRTUAL_CALL(_import_preflight, p_state, err)) { - return Error(err); - } - return OK; + GDVIRTUAL_CALL(_import_preflight, p_state, err); + return Error(err); } Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); int err = OK; - if (GDVIRTUAL_CALL(_import_post_parse, p_state, err)) { - return Error(err); - } - return OK; + GDVIRTUAL_CALL(_import_post_parse, p_state, err); + return Error(err); } Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); int err = OK; - if (GDVIRTUAL_CALL(_export_post, p_state, err)) { - return Error(err); - } - return OK; + GDVIRTUAL_CALL(_export_post, p_state, err); + return Error(err); } Error GLTFDocumentExtension::export_preflight(Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); int err = OK; - if (GDVIRTUAL_CALL(_export_preflight, p_root, err)) { - return Error(err); - } - return OK; + GDVIRTUAL_CALL(_export_preflight, p_root, err); + return Error(err); } Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { @@ -90,10 +87,8 @@ Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); int err = OK; - if (GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err)) { - return Error(err); - } - return OK; + GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err); + return Error(err); } Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { @@ -101,8 +96,6 @@ Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); int err = OK; - if (GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err)) { - return Error(err); - } - return OK; + GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err); + return Error(err); } diff --git a/modules/gltf/gltf_document_extension.h b/modules/gltf/gltf_document_extension.h index 0ef9109584..d4bb3993dc 100644 --- a/modules/gltf/gltf_document_extension.h +++ b/modules/gltf/gltf_document_extension.h @@ -41,6 +41,7 @@ protected: static void _bind_methods(); public: + virtual Vector<String> get_supported_extensions(); virtual Error import_preflight(Ref<GLTFState> p_state); virtual Error import_post_parse(Ref<GLTFState> p_state); virtual Error export_post(Ref<GLTFState> p_state); @@ -48,6 +49,7 @@ public: virtual Error export_preflight(Node *p_state); virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node); virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node); + GDVIRTUAL0R(Vector<String>, _get_supported_extensions); GDVIRTUAL1R(int, _import_preflight, Ref<GLTFState>); GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>); GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index 85bac446cc..ac5665e396 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -31,6 +31,7 @@ #include "gltf_state.h" void GLTFState::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_used_extension", "extension_name", "required"), &GLTFState::add_used_extension); ClassDB::bind_method(D_METHOD("get_json"), &GLTFState::get_json); ClassDB::bind_method(D_METHOD("set_json", "json"), &GLTFState::set_json); ClassDB::bind_method(D_METHOD("get_major_version"), &GLTFState::get_major_version); @@ -63,6 +64,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes); ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures); ClassDB::bind_method(D_METHOD("set_textures", "textures"), &GLTFState::set_textures); + ClassDB::bind_method(D_METHOD("get_texture_samplers"), &GLTFState::get_texture_samplers); + ClassDB::bind_method(D_METHOD("set_texture_samplers", "texture_samplers"), &GLTFState::set_texture_samplers); ClassDB::bind_method(D_METHOD("get_images"), &GLTFState::get_images); ClassDB::bind_method(D_METHOD("set_images", "images"), &GLTFState::set_images); ClassDB::bind_method(D_METHOD("get_skins"), &GLTFState::get_skins); @@ -84,6 +87,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations); ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations); ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node); + ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFState::get_additional_data); + ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int @@ -100,6 +105,7 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_path"), "set_base_path", "get_base_path"); // String ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>> + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "texture_samplers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_texture_samplers", "get_texture_samplers"); //Vector<Ref<GLTFTextureSampler>> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector<Ref<Texture> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skins", "get_skins"); // Vector<Ref<GLTFSkin>> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "cameras", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_cameras", "get_cameras"); // Vector<Ref<GLTFCamera>> @@ -112,6 +118,17 @@ void GLTFState::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>> } +void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) { + if (!extensions_used.has(p_extension_name)) { + extensions_used.push_back(p_extension_name); + } + if (p_required) { + if (!extensions_required.has(p_extension_name)) { + extensions_required.push_back(p_extension_name); + } + } +} + Dictionary GLTFState::get_json() { return json; } @@ -224,6 +241,14 @@ void GLTFState::set_textures(TypedArray<GLTFTexture> p_textures) { GLTFTemplateConvert::set_from_array(textures, p_textures); } +TypedArray<GLTFTextureSampler> GLTFState::get_texture_samplers() { + return GLTFTemplateConvert::to_array(texture_samplers); +} + +void GLTFState::set_texture_samplers(TypedArray<GLTFTextureSampler> p_texture_samplers) { + GLTFTemplateConvert::set_from_array(texture_samplers, p_texture_samplers); +} + TypedArray<Texture2D> GLTFState::get_images() { return GLTFTemplateConvert::to_array(images); } @@ -335,3 +360,11 @@ String GLTFState::get_base_path() { void GLTFState::set_base_path(String p_base_path) { base_path = p_base_path; } + +Variant GLTFState::get_additional_data(const StringName &p_extension_name) { + return additional_data[p_extension_name]; +} + +void GLTFState::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) { + additional_data[p_extension_name] = p_additional_data; +} diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index 6b2d1ca228..e24017b0fd 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -42,6 +42,7 @@ #include "structures/gltf_skeleton.h" #include "structures/gltf_skin.h" #include "structures/gltf_texture.h" +#include "structures/gltf_texture_sampler.h" #include "core/templates/rb_map.h" #include "scene/animation/animation_player.h" @@ -77,7 +78,11 @@ class GLTFState : public Resource { String scene_name; Vector<int> root_nodes; Vector<Ref<GLTFTexture>> textures; + Vector<Ref<GLTFTextureSampler>> texture_samplers; + Ref<GLTFTextureSampler> default_texture_sampler; Vector<Ref<Texture2D>> images; + Vector<String> extensions_used; + Vector<String> extensions_required; Vector<Ref<GLTFSkin>> skins; Vector<Ref<GLTFCamera>> cameras; @@ -92,11 +97,14 @@ class GLTFState : public Resource { HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton; HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin; + Dictionary additional_data; protected: static void _bind_methods(); public: + void add_used_extension(const String &p_extension, bool p_required = false); + Dictionary get_json(); void set_json(Dictionary p_json); @@ -145,6 +153,9 @@ public: TypedArray<GLTFTexture> get_textures(); void set_textures(TypedArray<GLTFTexture> p_textures); + TypedArray<GLTFTextureSampler> get_texture_samplers(); + void set_texture_samplers(TypedArray<GLTFTextureSampler> p_texture_samplers); + TypedArray<Texture2D> get_images(); void set_images(TypedArray<Texture2D> p_images); @@ -181,6 +192,9 @@ public: AnimationPlayer *get_animation_player(int idx); + Variant get_additional_data(const StringName &p_extension_name); + void set_additional_data(const StringName &p_extension_name, Variant p_additional_data); + //void set_scene_nodes(RBMap<GLTFNodeIndex, Node *> p_scene_nodes) { // this->scene_nodes = p_scene_nodes; //} diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index dbbccc9bcc..b9027f6e3d 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -47,6 +47,7 @@ #include "structures/gltf_skeleton.h" #include "structures/gltf_skin.h" #include "structures/gltf_texture.h" +#include "structures/gltf_texture_sampler.h" #ifdef TOOLS_ENABLED #include "core/config/project_settings.h" @@ -126,6 +127,7 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(GLTFSpecGloss); GDREGISTER_CLASS(GLTFState); GDREGISTER_CLASS(GLTFTexture); + GDREGISTER_CLASS(GLTFTextureSampler); } #ifdef TOOLS_ENABLED @@ -142,6 +144,11 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) { GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true); GDREGISTER_CLASS(EditorSceneFormatImporterBlend); GDREGISTER_CLASS(EditorSceneFormatImporterFBX); + // Can't (a priori) run external app on these platforms. + GLOBAL_DEF_RST("filesystem/import/blender/enabled.android", false); + GLOBAL_DEF_RST("filesystem/import/blender/enabled.web", false); + GLOBAL_DEF_RST("filesystem/import/fbx/enabled.android", false); + GLOBAL_DEF_RST("filesystem/import/fbx/enabled.web", false); ClassDB::set_current_api(prev_api); EditorNode::add_init_callback(_editor_init); diff --git a/modules/gltf/register_types.h b/modules/gltf/register_types.h index 90b9a83c88..bf2730d2ef 100644 --- a/modules/gltf/register_types.h +++ b/modules/gltf/register_types.h @@ -28,7 +28,12 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef GLTF_REGISTER_TYPES_H +#define GLTF_REGISTER_TYPES_H + #include "modules/register_module_types.h" void initialize_gltf_module(ModuleInitializationLevel p_level); void uninitialize_gltf_module(ModuleInitializationLevel p_level); + +#endif // GLTF_REGISTER_TYPES_H diff --git a/modules/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp index 5069f39c4b..212b9b80c8 100644 --- a/modules/gltf/structures/gltf_camera.cpp +++ b/modules/gltf/structures/gltf_camera.cpp @@ -58,6 +58,7 @@ void GLTFCamera::_bind_methods() { Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) { Ref<GLTFCamera> c; c.instantiate(); + ERR_FAIL_COND_V_MSG(!p_camera, c, "Tried to create a GLTFCamera from a Camera3D node, but the given node was null."); c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. c->set_fov(Math::deg_to_rad(p_camera->get_fov())); diff --git a/modules/gltf/structures/gltf_mesh.cpp b/modules/gltf/structures/gltf_mesh.cpp index 3add8304b1..3893f56626 100644 --- a/modules/gltf/structures/gltf_mesh.cpp +++ b/modules/gltf/structures/gltf_mesh.cpp @@ -53,11 +53,11 @@ void GLTFMesh::set_mesh(Ref<ImporterMesh> p_mesh) { mesh = p_mesh; } -Array GLTFMesh::get_instance_materials() { +TypedArray<Material> GLTFMesh::get_instance_materials() { return instance_materials; } -void GLTFMesh::set_instance_materials(Array p_instance_materials) { +void GLTFMesh::set_instance_materials(TypedArray<Material> p_instance_materials) { instance_materials = p_instance_materials; } diff --git a/modules/gltf/structures/gltf_mesh.h b/modules/gltf/structures/gltf_mesh.h index dc26120b48..2fa37fd727 100644 --- a/modules/gltf/structures/gltf_mesh.h +++ b/modules/gltf/structures/gltf_mesh.h @@ -42,7 +42,7 @@ class GLTFMesh : public Resource { private: Ref<ImporterMesh> mesh; Vector<float> blend_weights; - Array instance_materials; + TypedArray<Material> instance_materials; protected: static void _bind_methods(); @@ -52,8 +52,8 @@ public: void set_mesh(Ref<ImporterMesh> p_mesh); Vector<float> get_blend_weights(); void set_blend_weights(Vector<float> p_blend_weights); - Array get_instance_materials(); - void set_instance_materials(Array p_instance_materials); + TypedArray<Material> get_instance_materials(); + void set_instance_materials(TypedArray<Material> p_instance_materials); }; #endif // GLTF_MESH_H diff --git a/modules/gltf/structures/gltf_node.cpp b/modules/gltf/structures/gltf_node.cpp index 86280603fa..6fd36f93b7 100644 --- a/modules/gltf/structures/gltf_node.cpp +++ b/modules/gltf/structures/gltf_node.cpp @@ -57,6 +57,8 @@ void GLTFNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children); ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light); ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light); + ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFNode::get_additional_data); + ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFNode::set_additional_data); ADD_PROPERTY(PropertyInfo(Variant::INT, "parent"), "set_parent", "get_parent"); // GLTFNodeIndex ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); // int @@ -176,3 +178,11 @@ GLTFLightIndex GLTFNode::get_light() { void GLTFNode::set_light(GLTFLightIndex p_light) { light = p_light; } + +Variant GLTFNode::get_additional_data(const StringName &p_extension_name) { + return additional_data[p_extension_name]; +} + +void GLTFNode::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) { + additional_data[p_extension_name] = p_additional_data; +} diff --git a/modules/gltf/structures/gltf_node.h b/modules/gltf/structures/gltf_node.h index 1a57ea32e2..90a4fa99ed 100644 --- a/modules/gltf/structures/gltf_node.h +++ b/modules/gltf/structures/gltf_node.h @@ -53,6 +53,7 @@ private: Vector3 scale = Vector3(1, 1, 1); Vector<int> children; GLTFLightIndex light = -1; + Dictionary additional_data; protected: static void _bind_methods(); @@ -96,6 +97,9 @@ public: GLTFLightIndex get_light(); void set_light(GLTFLightIndex p_light); + + Variant get_additional_data(const StringName &p_extension_name); + void set_additional_data(const StringName &p_extension_name, Variant p_additional_data); }; #endif // GLTF_NODE_H diff --git a/modules/gltf/structures/gltf_texture.cpp b/modules/gltf/structures/gltf_texture.cpp index 2a21cb3df8..5cc96e3221 100644 --- a/modules/gltf/structures/gltf_texture.cpp +++ b/modules/gltf/structures/gltf_texture.cpp @@ -33,8 +33,11 @@ void GLTFTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("get_src_image"), &GLTFTexture::get_src_image); ClassDB::bind_method(D_METHOD("set_src_image", "src_image"), &GLTFTexture::set_src_image); + ClassDB::bind_method(D_METHOD("get_sampler"), &GLTFTexture::get_sampler); + ClassDB::bind_method(D_METHOD("set_sampler", "sampler"), &GLTFTexture::set_sampler); ADD_PROPERTY(PropertyInfo(Variant::INT, "src_image"), "set_src_image", "get_src_image"); // int + ADD_PROPERTY(PropertyInfo(Variant::INT, "sampler"), "set_sampler", "get_sampler"); // int } GLTFImageIndex GLTFTexture::get_src_image() const { @@ -44,3 +47,11 @@ GLTFImageIndex GLTFTexture::get_src_image() const { void GLTFTexture::set_src_image(GLTFImageIndex val) { src_image = val; } + +GLTFTextureSamplerIndex GLTFTexture::get_sampler() const { + return sampler; +} + +void GLTFTexture::set_sampler(GLTFTextureSamplerIndex val) { + sampler = val; +} diff --git a/modules/gltf/structures/gltf_texture.h b/modules/gltf/structures/gltf_texture.h index b1d12dddfa..b26c8a7b07 100644 --- a/modules/gltf/structures/gltf_texture.h +++ b/modules/gltf/structures/gltf_texture.h @@ -39,6 +39,7 @@ class GLTFTexture : public Resource { private: GLTFImageIndex src_image = 0; + GLTFTextureSamplerIndex sampler = -1; protected: static void _bind_methods(); @@ -46,6 +47,8 @@ protected: public: GLTFImageIndex get_src_image() const; void set_src_image(GLTFImageIndex val); + GLTFTextureSamplerIndex get_sampler() const; + void set_sampler(GLTFTextureSamplerIndex val); }; #endif // GLTF_TEXTURE_H diff --git a/modules/gltf/structures/gltf_texture_sampler.cpp b/modules/gltf/structures/gltf_texture_sampler.cpp new file mode 100644 index 0000000000..6cf615b6cc --- /dev/null +++ b/modules/gltf/structures/gltf_texture_sampler.cpp @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* gltf_texture_sampler.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#include "gltf_texture_sampler.h" + +void GLTFTextureSampler::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_mag_filter"), &GLTFTextureSampler::get_mag_filter); + ClassDB::bind_method(D_METHOD("set_mag_filter", "filter_mode"), &GLTFTextureSampler::set_mag_filter); + ClassDB::bind_method(D_METHOD("get_min_filter"), &GLTFTextureSampler::get_min_filter); + ClassDB::bind_method(D_METHOD("set_min_filter", "filter_mode"), &GLTFTextureSampler::set_min_filter); + ClassDB::bind_method(D_METHOD("get_wrap_s"), &GLTFTextureSampler::get_wrap_s); + ClassDB::bind_method(D_METHOD("set_wrap_s", "wrap_mode"), &GLTFTextureSampler::set_wrap_s); + ClassDB::bind_method(D_METHOD("get_wrap_t"), &GLTFTextureSampler::get_wrap_t); + ClassDB::bind_method(D_METHOD("set_wrap_t", "wrap_mode"), &GLTFTextureSampler::set_wrap_t); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "mag_filter"), "set_mag_filter", "get_mag_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "min_filter"), "set_min_filter", "get_min_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_s"), "set_wrap_s", "get_wrap_s"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_t"), "set_wrap_t", "get_wrap_t"); +} diff --git a/modules/gltf/structures/gltf_texture_sampler.h b/modules/gltf/structures/gltf_texture_sampler.h new file mode 100644 index 0000000000..3fad31bbee --- /dev/null +++ b/modules/gltf/structures/gltf_texture_sampler.h @@ -0,0 +1,163 @@ +/*************************************************************************/ +/* gltf_texture_sampler.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 GLTF_TEXTURE_SAMPLER_H +#define GLTF_TEXTURE_SAMPLER_H + +#include "core/io/resource.h" +#include "scene/resources/material.h" + +class GLTFTextureSampler : public Resource { + GDCLASS(GLTFTextureSampler, Resource); + +public: + enum FilterMode { + NEAREST = 9728, + LINEAR = 9729, + NEAREST_MIPMAP_NEAREST = 9984, + LINEAR_MIPMAP_NEAREST = 9985, + NEAREST_MIPMAP_LINEAR = 9986, + LINEAR_MIPMAP_LINEAR = 9987 + }; + + enum WrapMode { + CLAMP_TO_EDGE = 33071, + MIRRORED_REPEAT = 33648, + REPEAT = 10497, + DEFAULT = REPEAT + }; + + int get_mag_filter() const { + return mag_filter; + } + + void set_mag_filter(const int filter_mode) { + mag_filter = (FilterMode)filter_mode; + } + + int get_min_filter() const { + return min_filter; + } + + void set_min_filter(const int filter_mode) { + min_filter = (FilterMode)filter_mode; + } + + int get_wrap_s() const { + return wrap_s; + } + + void set_wrap_s(const int wrap_mode) { + wrap_s = (WrapMode)wrap_mode; + } + + int get_wrap_t() const { + return wrap_t; + } + + void set_wrap_t(const int wrap_mode) { + wrap_s = (WrapMode)wrap_mode; + } + + StandardMaterial3D::TextureFilter get_filter_mode() const { + using TextureFilter = StandardMaterial3D::TextureFilter; + + switch (min_filter) { + case NEAREST: + return TextureFilter::TEXTURE_FILTER_NEAREST; + case LINEAR: + return TextureFilter::TEXTURE_FILTER_LINEAR; + case NEAREST_MIPMAP_NEAREST: + case NEAREST_MIPMAP_LINEAR: + return TextureFilter::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS; + case LINEAR_MIPMAP_NEAREST: + case LINEAR_MIPMAP_LINEAR: + default: + return TextureFilter::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; + } + } + + void set_filter_mode(StandardMaterial3D::TextureFilter mode) { + using TextureFilter = StandardMaterial3D::TextureFilter; + + switch (mode) { + case TextureFilter::TEXTURE_FILTER_NEAREST: + min_filter = FilterMode::NEAREST; + mag_filter = FilterMode::NEAREST; + break; + case TextureFilter::TEXTURE_FILTER_LINEAR: + min_filter = FilterMode::LINEAR; + mag_filter = FilterMode::LINEAR; + break; + case TextureFilter::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: + case TextureFilter::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: + min_filter = FilterMode::NEAREST_MIPMAP_LINEAR; + mag_filter = FilterMode::NEAREST; + break; + case TextureFilter::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: + case TextureFilter::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: + default: + min_filter = FilterMode::LINEAR_MIPMAP_LINEAR; + mag_filter = FilterMode::LINEAR; + break; + } + } + + bool get_wrap_mode() const { + // BaseMaterial3D presents wrapping as a boolean property. Either the texture is repeated + // in both dimensions, non-mirrored, or it isn't repeated at all. This will cause oddities + // when people import models having other wrapping mode combinations. + return (wrap_s == WrapMode::REPEAT) && (wrap_t == WrapMode::REPEAT); + } + + void set_wrap_mode(bool mat_repeats) { + if (mat_repeats) { + wrap_s = WrapMode::REPEAT; + wrap_t = WrapMode::REPEAT; + } else { + wrap_s = WrapMode::CLAMP_TO_EDGE; + wrap_t = WrapMode::CLAMP_TO_EDGE; + } + } + +protected: + static void _bind_methods(); + +private: + FilterMode mag_filter = FilterMode::LINEAR; + FilterMode min_filter = FilterMode::LINEAR_MIPMAP_LINEAR; + WrapMode wrap_s = WrapMode::REPEAT; + WrapMode wrap_t = WrapMode::REPEAT; +}; + +VARIANT_ENUM_CAST(GLTFTextureSampler::FilterMode); +VARIANT_ENUM_CAST(GLTFTextureSampler::WrapMode); + +#endif // GLTF_TEXTURE_SAMPLER_H |