Skip to content

Commit

Permalink
Implement KHR_node_visibility in the GLTF module
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronfranke committed Nov 8, 2024
1 parent e65a237 commit d4c1816
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 18 deletions.
12 changes: 12 additions & 0 deletions modules/gltf/doc_classes/GLTFDocument.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
How to process the root node during export. See [enum RootNodeMode] for details. The default and recommended value is [constant ROOT_NODE_MODE_SINGLE_ROOT].
[b]Note:[/b] Regardless of how the glTF file is exported, when importing, the root node type and name can be overridden in the scene import settings tab.
</member>
<member name="visibility_mode" type="int" setter="set_visibility_mode" getter="get_visibility_mode" enum="GLTFDocument.VisibilityMode" default="0">
How to deal with node visibility during export. This setting does nothing if all nodes are visible. See [enum VisibilityMode] for details. The default and recommended value is [constant VISIBILITY_MODE_INCLUDE_REQUIRED], which uses the [code]KHR_node_visibility[/code] extension.
</member>
</members>
<constants>
<constant name="ROOT_NODE_MODE_SINGLE_ROOT" value="0" enum="RootNodeMode">
Expand All @@ -137,5 +140,14 @@
<constant name="ROOT_NODE_MODE_MULTI_ROOT" value="2" enum="RootNodeMode">
Treat the Godot scene's root node as the name of the glTF scene, and add all of its children as root nodes of the glTF file. This uses only vanilla glTF features. This avoids an extra root node, but only the name of the Godot scene's root node will be preserved, as it will not be saved as a node.
</constant>
<constant name="VISIBILITY_MODE_INCLUDE_REQUIRED" value="0" enum="VisibilityMode">
If the scene contains any non-visible nodes, include them, mark them as non-visible with [code]KHR_node_visibility[/code], and require that importers respect their non-visibility. Downside: If the importer does not support [code]KHR_node_visibility[/code], the file cannot be imported.
</constant>
<constant name="VISIBILITY_MODE_INCLUDE_OPTIONAL" value="1" enum="VisibilityMode">
If the scene contains any non-visible nodes, include them, mark them as non-visible with [code]KHR_node_visibility[/code], and do not impose any requirements on importers. Downside: If the importer does not support [code]KHR_node_visibility[/code], invisible objects will be visible.
</constant>
<constant name="VISIBILITY_MODE_EXCLUDE" value="2" enum="VisibilityMode">
If the scene contains any non-visible nodes, do not include them in the export. This is the same as the behavior in Godot 4.3 and earlier. Downside: Invisible nodes will not exist in the exported file.
</constant>
</constants>
</class>
3 changes: 3 additions & 0 deletions modules/gltf/doc_classes/GLTFNode.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@
<member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1">
If this glTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin.
</member>
<member name="visible" type="bool" setter="set_visible" getter="get_visible" default="true">
If [code]true[/code], the GLTF node is visible. If [code]false[/code], the GLTF node is not visible. This is translated to the [member Node3D.visible] property in the Godot scene, and is exported to [code]KHR_node_visibility[/code] when [code]false[/code].
</member>
<member name="xform" type="Transform3D" setter="set_xform" getter="get_xform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
The transform of the glTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred.
</member>
Expand Down
28 changes: 28 additions & 0 deletions modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ bool EditorSceneExporterGLTFSettings::_set(const StringName &p_name, const Varia
_document->set_root_node_mode((GLTFDocument::RootNodeMode)(int64_t)p_value);
return true;
}
if (p_name == StringName("visibility_mode")) {
_document->set_visibility_mode((GLTFDocument::VisibilityMode)(int64_t)p_value);
return true;
}
return false;
}

Expand All @@ -70,6 +74,10 @@ bool EditorSceneExporterGLTFSettings::_get(const StringName &p_name, Variant &r_
r_ret = _document->get_root_node_mode();
return true;
}
if (p_name == StringName("visibility_mode")) {
r_ret = _document->get_visibility_mode();
return true;
}
return false;
}

Expand Down Expand Up @@ -128,6 +136,21 @@ String get_friendly_config_prefix(Ref<GLTFDocumentExtension> p_extension) {
return "Unknown GLTFDocumentExtension";
}

bool is_any_node_invisible(Node *p_node) {
if (p_node->has_method("is_visible")) {
bool visible = p_node->call("is_visible");
if (!visible) {
return true;
}
}
for (int i = 0; i < p_node->get_child_count(); i++) {
if (is_any_node_invisible(p_node->get_child(i))) {
return true;
}
}
return false;
}

// Run this before popping up the export settings, because the extensions may have changed.
void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p_document, Node *p_root) {
_property_list.clear();
Expand Down Expand Up @@ -168,6 +191,11 @@ void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p
_property_list.push_back(lossy_quality_prop);
PropertyInfo root_node_mode_prop = PropertyInfo(Variant::INT, "root_node_mode", PROPERTY_HINT_ENUM, "Single Root,Keep Root,Multi Root");
_property_list.push_back(root_node_mode_prop);
// If the scene contains any non-visible nodes, show the visibility mode setting.
if (p_root != nullptr && is_any_node_invisible(p_root)) {
PropertyInfo visibility_mode_prop = PropertyInfo(Variant::INT, "visibility_mode", PROPERTY_HINT_ENUM, "Include & Required,Include & Optional,Exclude");
_property_list.push_back(visibility_mode_prop);
}
}

String EditorSceneExporterGLTFSettings::get_copyright() const {
Expand Down
59 changes: 41 additions & 18 deletions modules/gltf/gltf_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,17 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) {
extensions["KHR_lights_punctual"] = lights_punctual;
lights_punctual["light"] = gltf_node->light;
}
if (!gltf_node->visible) {
Dictionary khr_node_visibility;
extensions["KHR_node_visibility"] = khr_node_visibility;
khr_node_visibility["visible"] = gltf_node->visible;
if (!p_state->extensions_used.has("KHR_node_visibility")) {
p_state->extensions_used.push_back("KHR_node_visibility");
if (_visibility_mode == VISIBILITY_MODE_INCLUDE_REQUIRED) {
p_state->extensions_required.push_back("KHR_node_visibility");
}
}
}
if (gltf_node->mesh != -1) {
node["mesh"] = gltf_node->mesh;
}
Expand Down Expand Up @@ -629,6 +640,12 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) {
node->light = light;
}
}
if (extensions.has("KHR_node_visibility")) {
Dictionary khr_node_visibility = extensions["KHR_node_visibility"];
if (khr_node_visibility.has("visible")) {
node->visible = khr_node_visibility["visible"];
}
}
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->parse_node_extensions(p_state, node, extensions);
Expand Down Expand Up @@ -5801,11 +5818,6 @@ Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> p_state, const GLTFNodeIn
}

void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
bool retflag = true;
_check_visibility(p_current, retflag);
if (retflag) {
return;
}
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && p_gltf_root != -1 && p_current->get_owner() == nullptr) {
WARN_VERBOSE("glTF export warning: Node '" + p_current->get_name() + "' has no owner. This is likely a temporary node generated by a @tool script. This would not be saved when saving the Godot scene, therefore it will not be exported to glTF.");
Expand All @@ -5814,6 +5826,13 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current,
#endif // TOOLS_ENABLED
Ref<GLTFNode> gltf_node;
gltf_node.instantiate();
if (p_current->has_method("is_visible")) {
bool visible = p_current->call("is_visible");
if (!visible && _visibility_mode == VISIBILITY_MODE_EXCLUDE) {
return;
}
gltf_node->visible = visible;
}
gltf_node->set_original_name(p_current->get_name());
gltf_node->set_name(_gen_unique_name(p_state, p_current->get_name()));
gltf_node->merge_meta_from(p_current);
Expand Down Expand Up @@ -5934,19 +5953,6 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd
}
#endif // MODULE_CSG_ENABLED

void GLTFDocument::_check_visibility(Node *p_node, bool &r_retflag) {
r_retflag = true;
Node3D *spatial = Object::cast_to<Node3D>(p_node);
Node2D *node_2d = Object::cast_to<Node2D>(p_node);
if (node_2d && !node_2d->is_visible()) {
return;
}
if (spatial && !spatial->is_visible()) {
return;
}
r_retflag = false;
}

void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node) {
ERR_FAIL_NULL(camera);
GLTFCameraIndex camera_index = _convert_camera(p_state, camera);
Expand Down Expand Up @@ -6228,6 +6234,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
if (!gltf_node_name.is_empty()) {
current_node->set_name(gltf_node_name);
}
current_node->set_visible(gltf_node->visible);
// Note: p_scene_parent and p_scene_root must either both be null or both be valid.
if (p_scene_root == nullptr) {
// If the root node argument is null, this is the root node.
Expand Down Expand Up @@ -8118,12 +8125,18 @@ void GLTFDocument::_bind_methods() {
BIND_ENUM_CONSTANT(ROOT_NODE_MODE_KEEP_ROOT);
BIND_ENUM_CONSTANT(ROOT_NODE_MODE_MULTI_ROOT);

BIND_ENUM_CONSTANT(VISIBILITY_MODE_INCLUDE_REQUIRED);
BIND_ENUM_CONSTANT(VISIBILITY_MODE_INCLUDE_OPTIONAL);
BIND_ENUM_CONSTANT(VISIBILITY_MODE_EXCLUDE);

ClassDB::bind_method(D_METHOD("set_image_format", "image_format"), &GLTFDocument::set_image_format);
ClassDB::bind_method(D_METHOD("get_image_format"), &GLTFDocument::get_image_format);
ClassDB::bind_method(D_METHOD("set_lossy_quality", "lossy_quality"), &GLTFDocument::set_lossy_quality);
ClassDB::bind_method(D_METHOD("get_lossy_quality"), &GLTFDocument::get_lossy_quality);
ClassDB::bind_method(D_METHOD("set_root_node_mode", "root_node_mode"), &GLTFDocument::set_root_node_mode);
ClassDB::bind_method(D_METHOD("get_root_node_mode"), &GLTFDocument::get_root_node_mode);
ClassDB::bind_method(D_METHOD("set_visibility_mode", "visibility_mode"), &GLTFDocument::set_visibility_mode);
ClassDB::bind_method(D_METHOD("get_visibility_mode"), &GLTFDocument::get_visibility_mode);
ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "base_path"),
&GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(String()));
ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags"),
Expand All @@ -8140,6 +8153,7 @@ void GLTFDocument::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_format"), "set_image_format", "get_image_format");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lossy_quality"), "set_lossy_quality", "get_lossy_quality");
ADD_PROPERTY(PropertyInfo(Variant::INT, "root_node_mode"), "set_root_node_mode", "get_root_node_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_mode"), "set_visibility_mode", "get_visibility_mode");

ClassDB::bind_static_method("GLTFDocument", D_METHOD("import_object_model_property", "state", "json_pointer"), &GLTFDocument::import_object_model_property);
ClassDB::bind_static_method("GLTFDocument", D_METHOD("export_object_model_property", "state", "node_path", "godot_node", "gltf_node_index"), &GLTFDocument::export_object_model_property);
Expand Down Expand Up @@ -8210,6 +8224,7 @@ HashSet<String> GLTFDocument::get_supported_gltf_extensions_hashset() {
supported_extensions.insert("KHR_materials_emissive_strength");
supported_extensions.insert("KHR_materials_pbrSpecularGlossiness");
supported_extensions.insert("KHR_materials_unlit");
supported_extensions.insert("KHR_node_visibility");
supported_extensions.insert("KHR_texture_transform");
for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
ERR_CONTINUE(ext.is_null());
Expand Down Expand Up @@ -8605,6 +8620,14 @@ GLTFDocument::RootNodeMode GLTFDocument::get_root_node_mode() const {
return _root_node_mode;
}

void GLTFDocument::set_visibility_mode(VisibilityMode p_visibility_mode) {
_visibility_mode = p_visibility_mode;
}

GLTFDocument::VisibilityMode GLTFDocument::get_visibility_mode() const {
return _visibility_mode;
}

String GLTFDocument::_gen_unique_name_static(HashSet<String> &r_unique_names, const String &p_name) {
const String s_name = p_name.validate_node_name();

Expand Down
9 changes: 9 additions & 0 deletions modules/gltf/gltf_document.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,19 @@ class GLTFDocument : public Resource {
ROOT_NODE_MODE_KEEP_ROOT,
ROOT_NODE_MODE_MULTI_ROOT,
};
enum VisibilityMode {
VISIBILITY_MODE_INCLUDE_REQUIRED,
VISIBILITY_MODE_INCLUDE_OPTIONAL,
VISIBILITY_MODE_EXCLUDE,
};

private:
int _naming_version = 1;
String _image_format = "PNG";
float _lossy_quality = 0.75f;
Ref<GLTFDocumentExtension> _image_save_extension;
RootNodeMode _root_node_mode = RootNodeMode::ROOT_NODE_MODE_SINGLE_ROOT;
VisibilityMode _visibility_mode = VisibilityMode::VISIBILITY_MODE_INCLUDE_REQUIRED;

protected:
static void _bind_methods();
Expand Down Expand Up @@ -100,6 +106,8 @@ class GLTFDocument : public Resource {
float get_lossy_quality() const;
void set_root_node_mode(RootNodeMode p_root_node_mode);
RootNodeMode get_root_node_mode() const;
void set_visibility_mode(VisibilityMode p_visibility_mode);
VisibilityMode get_visibility_mode() const;
static String _gen_unique_name_static(HashSet<String> &r_unique_names, const String &p_name);

private:
Expand Down Expand Up @@ -388,5 +396,6 @@ class GLTFDocument : public Resource {
};

VARIANT_ENUM_CAST(GLTFDocument::RootNodeMode);
VARIANT_ENUM_CAST(GLTFDocument::VisibilityMode);

#endif // GLTF_DOCUMENT_H
11 changes: 11 additions & 0 deletions modules/gltf/structures/gltf_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ void GLTFNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("append_child_index", "child_index"), &GLTFNode::append_child_index);
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_visible"), &GLTFNode::get_visible);
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &GLTFNode::set_visible);
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);
ClassDB::bind_method(D_METHOD("get_scene_node_path", "gltf_state", "handle_skeletons"), &GLTFNode::get_scene_node_path, DEFVAL(true));
Expand All @@ -77,6 +79,7 @@ void GLTFNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "children"), "set_children", "get_children"); // Vector<int>
ADD_PROPERTY(PropertyInfo(Variant::INT, "light"), "set_light", "get_light"); // GLTFLightIndex
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "get_visible"); // bool
}

String GLTFNode::get_original_name() {
Expand Down Expand Up @@ -186,6 +189,14 @@ void GLTFNode::set_light(GLTFLightIndex p_light) {
light = p_light;
}

bool GLTFNode::get_visible() {
return visible;
}

void GLTFNode::set_visible(bool p_visible) {
visible = p_visible;
}

Variant GLTFNode::get_additional_data(const StringName &p_extension_name) {
return additional_data[p_extension_name];
}
Expand Down
4 changes: 4 additions & 0 deletions modules/gltf/structures/gltf_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class GLTFNode : public Resource {
GLTFSkinIndex skin = -1;
GLTFSkeletonIndex skeleton = -1;
bool joint = false;
bool visible = true;
Vector<int> children;
GLTFLightIndex light = -1;
Dictionary additional_data;
Expand Down Expand Up @@ -102,6 +103,9 @@ class GLTFNode : public Resource {
GLTFLightIndex get_light();
void set_light(GLTFLightIndex p_light);

bool get_visible();
void set_visible(bool p_visible);

Variant get_additional_data(const StringName &p_extension_name);
bool has_additional_data(const StringName &p_extension_name);
void set_additional_data(const StringName &p_extension_name, Variant p_additional_data);
Expand Down

0 comments on commit d4c1816

Please sign in to comment.