Skip to content

Commit 8459f4c

Browse files
committed
Implement KHR_node_visibility in the GLTF module
1 parent 215acd5 commit 8459f4c

File tree

7 files changed

+108
-18
lines changed

7 files changed

+108
-18
lines changed

modules/gltf/doc_classes/GLTFDocument.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@
126126
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].
127127
[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.
128128
</member>
129+
<member name="visibility_mode" type="int" setter="set_visibility_mode" getter="get_visibility_mode" enum="GLTFDocument.VisibilityMode" default="0">
130+
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.
131+
</member>
129132
</members>
130133
<constants>
131134
<constant name="ROOT_NODE_MODE_SINGLE_ROOT" value="0" enum="RootNodeMode">
@@ -137,5 +140,14 @@
137140
<constant name="ROOT_NODE_MODE_MULTI_ROOT" value="2" enum="RootNodeMode">
138141
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.
139142
</constant>
143+
<constant name="VISIBILITY_MODE_INCLUDE_REQUIRED" value="0" enum="VisibilityMode">
144+
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.
145+
</constant>
146+
<constant name="VISIBILITY_MODE_INCLUDE_OPTIONAL" value="1" enum="VisibilityMode">
147+
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.
148+
</constant>
149+
<constant name="VISIBILITY_MODE_EXCLUDE" value="2" enum="VisibilityMode">
150+
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.4 and earlier. Downside: Invisible nodes will not exist in the exported file.
151+
</constant>
140152
</constants>
141153
</class>

modules/gltf/doc_classes/GLTFNode.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@
8383
<member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1">
8484
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.
8585
</member>
86+
<member name="visible" type="bool" setter="set_visible" getter="get_visible" default="true">
87+
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].
88+
</member>
8689
<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)">
8790
The transform of the glTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred.
8891
</member>

modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ bool EditorSceneExporterGLTFSettings::_set(const StringName &p_name, const Varia
5050
_document->set_root_node_mode((GLTFDocument::RootNodeMode)(int64_t)p_value);
5151
return true;
5252
}
53+
if (p_name == StringName("visibility_mode")) {
54+
_document->set_visibility_mode((GLTFDocument::VisibilityMode)(int64_t)p_value);
55+
return true;
56+
}
5357
return false;
5458
}
5559

@@ -70,6 +74,10 @@ bool EditorSceneExporterGLTFSettings::_get(const StringName &p_name, Variant &r_
7074
r_ret = _document->get_root_node_mode();
7175
return true;
7276
}
77+
if (p_name == StringName("visibility_mode")) {
78+
r_ret = _document->get_visibility_mode();
79+
return true;
80+
}
7381
return false;
7482
}
7583

@@ -128,6 +136,21 @@ String get_friendly_config_prefix(Ref<GLTFDocumentExtension> p_extension) {
128136
return "Unknown GLTFDocumentExtension";
129137
}
130138

139+
bool is_any_node_invisible(Node *p_node) {
140+
if (p_node->has_method("is_visible")) {
141+
bool visible = p_node->call("is_visible");
142+
if (!visible) {
143+
return true;
144+
}
145+
}
146+
for (int i = 0; i < p_node->get_child_count(); i++) {
147+
if (is_any_node_invisible(p_node->get_child(i))) {
148+
return true;
149+
}
150+
}
151+
return false;
152+
}
153+
131154
// Run this before popping up the export settings, because the extensions may have changed.
132155
void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p_document, Node *p_root) {
133156
_property_list.clear();
@@ -168,6 +191,11 @@ void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p
168191
_property_list.push_back(lossy_quality_prop);
169192
PropertyInfo root_node_mode_prop = PropertyInfo(Variant::INT, "root_node_mode", PROPERTY_HINT_ENUM, "Single Root,Keep Root,Multi Root");
170193
_property_list.push_back(root_node_mode_prop);
194+
// If the scene contains any non-visible nodes, show the visibility mode setting.
195+
if (p_root != nullptr && is_any_node_invisible(p_root)) {
196+
PropertyInfo visibility_mode_prop = PropertyInfo(Variant::INT, "visibility_mode", PROPERTY_HINT_ENUM, "Include & Required,Include & Optional,Exclude");
197+
_property_list.push_back(visibility_mode_prop);
198+
}
171199
}
172200

173201
String EditorSceneExporterGLTFSettings::get_copyright() const {

modules/gltf/gltf_document.cpp

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,17 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) {
443443
extensions["KHR_lights_punctual"] = lights_punctual;
444444
lights_punctual["light"] = gltf_node->light;
445445
}
446+
if (!gltf_node->visible) {
447+
Dictionary khr_node_visibility;
448+
extensions["KHR_node_visibility"] = khr_node_visibility;
449+
khr_node_visibility["visible"] = gltf_node->visible;
450+
if (!p_state->extensions_used.has("KHR_node_visibility")) {
451+
p_state->extensions_used.push_back("KHR_node_visibility");
452+
if (_visibility_mode == VISIBILITY_MODE_INCLUDE_REQUIRED) {
453+
p_state->extensions_required.push_back("KHR_node_visibility");
454+
}
455+
}
456+
}
446457
if (gltf_node->mesh != -1) {
447458
node["mesh"] = gltf_node->mesh;
448459
}
@@ -637,6 +648,12 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) {
637648
node->light = light;
638649
}
639650
}
651+
if (extensions.has("KHR_node_visibility")) {
652+
Dictionary khr_node_visibility = extensions["KHR_node_visibility"];
653+
if (khr_node_visibility.has("visible")) {
654+
node->visible = khr_node_visibility["visible"];
655+
}
656+
}
640657
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
641658
ERR_CONTINUE(ext.is_null());
642659
Error err = ext->parse_node_extensions(p_state, node, extensions);
@@ -5844,11 +5861,6 @@ Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> p_state, const GLTFNodeIn
58445861
}
58455862

58465863
void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
5847-
bool retflag = true;
5848-
_check_visibility(p_current, retflag);
5849-
if (retflag) {
5850-
return;
5851-
}
58525864
#ifdef TOOLS_ENABLED
58535865
if (Engine::get_singleton()->is_editor_hint() && p_gltf_root != -1 && p_current->get_owner() == nullptr) {
58545866
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.");
@@ -5857,6 +5869,13 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current,
58575869
#endif // TOOLS_ENABLED
58585870
Ref<GLTFNode> gltf_node;
58595871
gltf_node.instantiate();
5872+
if (p_current->has_method("is_visible")) {
5873+
bool visible = p_current->call("is_visible");
5874+
if (!visible && _visibility_mode == VISIBILITY_MODE_EXCLUDE) {
5875+
return;
5876+
}
5877+
gltf_node->visible = visible;
5878+
}
58605879
gltf_node->set_original_name(p_current->get_name());
58615880
gltf_node->set_name(_gen_unique_name(p_state, p_current->get_name()));
58625881
gltf_node->merge_meta_from(p_current);
@@ -5979,19 +5998,6 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd
59795998
#endif // MODULE_CSG_ENABLED
59805999
}
59816000

5982-
void GLTFDocument::_check_visibility(Node *p_node, bool &r_retflag) {
5983-
r_retflag = true;
5984-
Node3D *spatial = Object::cast_to<Node3D>(p_node);
5985-
Node2D *node_2d = Object::cast_to<Node2D>(p_node);
5986-
if (node_2d && !node_2d->is_visible()) {
5987-
return;
5988-
}
5989-
if (spatial && !spatial->is_visible()) {
5990-
return;
5991-
}
5992-
r_retflag = false;
5993-
}
5994-
59956001
void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node) {
59966002
ERR_FAIL_NULL(camera);
59976003
GLTFCameraIndex camera_index = _convert_camera(p_state, camera);
@@ -6275,6 +6281,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
62756281
if (!gltf_node_name.is_empty()) {
62766282
current_node->set_name(gltf_node_name);
62776283
}
6284+
current_node->set_visible(gltf_node->visible);
62786285
// Note: p_scene_parent and p_scene_root must either both be null or both be valid.
62796286
if (p_scene_root == nullptr) {
62806287
// If the root node argument is null, this is the root node.
@@ -8165,12 +8172,18 @@ void GLTFDocument::_bind_methods() {
81658172
BIND_ENUM_CONSTANT(ROOT_NODE_MODE_KEEP_ROOT);
81668173
BIND_ENUM_CONSTANT(ROOT_NODE_MODE_MULTI_ROOT);
81678174

8175+
BIND_ENUM_CONSTANT(VISIBILITY_MODE_INCLUDE_REQUIRED);
8176+
BIND_ENUM_CONSTANT(VISIBILITY_MODE_INCLUDE_OPTIONAL);
8177+
BIND_ENUM_CONSTANT(VISIBILITY_MODE_EXCLUDE);
8178+
81688179
ClassDB::bind_method(D_METHOD("set_image_format", "image_format"), &GLTFDocument::set_image_format);
81698180
ClassDB::bind_method(D_METHOD("get_image_format"), &GLTFDocument::get_image_format);
81708181
ClassDB::bind_method(D_METHOD("set_lossy_quality", "lossy_quality"), &GLTFDocument::set_lossy_quality);
81718182
ClassDB::bind_method(D_METHOD("get_lossy_quality"), &GLTFDocument::get_lossy_quality);
81728183
ClassDB::bind_method(D_METHOD("set_root_node_mode", "root_node_mode"), &GLTFDocument::set_root_node_mode);
81738184
ClassDB::bind_method(D_METHOD("get_root_node_mode"), &GLTFDocument::get_root_node_mode);
8185+
ClassDB::bind_method(D_METHOD("set_visibility_mode", "visibility_mode"), &GLTFDocument::set_visibility_mode);
8186+
ClassDB::bind_method(D_METHOD("get_visibility_mode"), &GLTFDocument::get_visibility_mode);
81748187
ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "base_path"),
81758188
&GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(String()));
81768189
ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags"),
@@ -8187,6 +8200,7 @@ void GLTFDocument::_bind_methods() {
81878200
ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_format"), "set_image_format", "get_image_format");
81888201
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lossy_quality"), "set_lossy_quality", "get_lossy_quality");
81898202
ADD_PROPERTY(PropertyInfo(Variant::INT, "root_node_mode"), "set_root_node_mode", "get_root_node_mode");
8203+
ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_mode"), "set_visibility_mode", "get_visibility_mode");
81908204

81918205
ClassDB::bind_static_method("GLTFDocument", D_METHOD("import_object_model_property", "state", "json_pointer"), &GLTFDocument::import_object_model_property);
81928206
ClassDB::bind_static_method("GLTFDocument", D_METHOD("export_object_model_property", "state", "node_path", "godot_node", "gltf_node_index"), &GLTFDocument::export_object_model_property);
@@ -8257,6 +8271,7 @@ HashSet<String> GLTFDocument::get_supported_gltf_extensions_hashset() {
82578271
supported_extensions.insert("KHR_materials_emissive_strength");
82588272
supported_extensions.insert("KHR_materials_pbrSpecularGlossiness");
82598273
supported_extensions.insert("KHR_materials_unlit");
8274+
supported_extensions.insert("KHR_node_visibility");
82608275
supported_extensions.insert("KHR_texture_transform");
82618276
for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
82628277
ERR_CONTINUE(ext.is_null());
@@ -8657,6 +8672,14 @@ GLTFDocument::RootNodeMode GLTFDocument::get_root_node_mode() const {
86578672
return _root_node_mode;
86588673
}
86598674

8675+
void GLTFDocument::set_visibility_mode(VisibilityMode p_visibility_mode) {
8676+
_visibility_mode = p_visibility_mode;
8677+
}
8678+
8679+
GLTFDocument::VisibilityMode GLTFDocument::get_visibility_mode() const {
8680+
return _visibility_mode;
8681+
}
8682+
86608683
String GLTFDocument::_gen_unique_name_static(HashSet<String> &r_unique_names, const String &p_name) {
86618684
const String s_name = p_name.validate_node_name();
86628685

modules/gltf/gltf_document.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,19 @@ class GLTFDocument : public Resource {
6060
ROOT_NODE_MODE_KEEP_ROOT,
6161
ROOT_NODE_MODE_MULTI_ROOT,
6262
};
63+
enum VisibilityMode {
64+
VISIBILITY_MODE_INCLUDE_REQUIRED,
65+
VISIBILITY_MODE_INCLUDE_OPTIONAL,
66+
VISIBILITY_MODE_EXCLUDE,
67+
};
6368

6469
private:
6570
int _naming_version = 1;
6671
String _image_format = "PNG";
6772
float _lossy_quality = 0.75f;
6873
Ref<GLTFDocumentExtension> _image_save_extension;
6974
RootNodeMode _root_node_mode = RootNodeMode::ROOT_NODE_MODE_SINGLE_ROOT;
75+
VisibilityMode _visibility_mode = VisibilityMode::VISIBILITY_MODE_INCLUDE_REQUIRED;
7076

7177
protected:
7278
static void _bind_methods();
@@ -94,6 +100,8 @@ class GLTFDocument : public Resource {
94100
float get_lossy_quality() const;
95101
void set_root_node_mode(RootNodeMode p_root_node_mode);
96102
RootNodeMode get_root_node_mode() const;
103+
void set_visibility_mode(VisibilityMode p_visibility_mode);
104+
VisibilityMode get_visibility_mode() const;
97105
static String _gen_unique_name_static(HashSet<String> &r_unique_names, const String &p_name);
98106

99107
private:
@@ -378,3 +386,4 @@ class GLTFDocument : public Resource {
378386
};
379387

380388
VARIANT_ENUM_CAST(GLTFDocument::RootNodeMode);
389+
VARIANT_ENUM_CAST(GLTFDocument::VisibilityMode);

modules/gltf/structures/gltf_node.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ void GLTFNode::_bind_methods() {
6060
ClassDB::bind_method(D_METHOD("append_child_index", "child_index"), &GLTFNode::append_child_index);
6161
ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light);
6262
ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light);
63+
ClassDB::bind_method(D_METHOD("get_visible"), &GLTFNode::get_visible);
64+
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &GLTFNode::set_visible);
6365
ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFNode::get_additional_data);
6466
ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFNode::set_additional_data);
6567
ClassDB::bind_method(D_METHOD("get_scene_node_path", "gltf_state", "handle_skeletons"), &GLTFNode::get_scene_node_path, DEFVAL(true));
@@ -77,6 +79,7 @@ void GLTFNode::_bind_methods() {
7779
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3
7880
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "children"), "set_children", "get_children"); // Vector<int>
7981
ADD_PROPERTY(PropertyInfo(Variant::INT, "light"), "set_light", "get_light"); // GLTFLightIndex
82+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "get_visible"); // bool
8083
}
8184

8285
String GLTFNode::get_original_name() {
@@ -186,6 +189,14 @@ void GLTFNode::set_light(GLTFLightIndex p_light) {
186189
light = p_light;
187190
}
188191

192+
bool GLTFNode::get_visible() {
193+
return visible;
194+
}
195+
196+
void GLTFNode::set_visible(bool p_visible) {
197+
visible = p_visible;
198+
}
199+
189200
Variant GLTFNode::get_additional_data(const StringName &p_extension_name) {
190201
return additional_data[p_extension_name];
191202
}

modules/gltf/structures/gltf_node.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class GLTFNode : public Resource {
5050
GLTFSkinIndex skin = -1;
5151
GLTFSkeletonIndex skeleton = -1;
5252
bool joint = false;
53+
bool visible = true;
5354
Vector<int> children;
5455
GLTFLightIndex light = -1;
5556
Dictionary additional_data;
@@ -101,6 +102,9 @@ class GLTFNode : public Resource {
101102
GLTFLightIndex get_light();
102103
void set_light(GLTFLightIndex p_light);
103104

105+
bool get_visible();
106+
void set_visible(bool p_visible);
107+
104108
Variant get_additional_data(const StringName &p_extension_name);
105109
bool has_additional_data(const StringName &p_extension_name);
106110
void set_additional_data(const StringName &p_extension_name, Variant p_additional_data);

0 commit comments

Comments
 (0)