diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp index c6e34cc02355..16edc0a41b78 100644 --- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp +++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp @@ -127,6 +127,11 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref p_state trigger_body->set_body_type("trigger"); p_gltf_node->set_additional_data(StringName("GLTFPhysicsBody"), trigger_body); } + // If this node defines explicit member shape nodes, save this information. + if (node_trigger.has("nodes")) { + Array node_trigger_nodes = node_trigger["nodes"]; + p_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), node_trigger_nodes); + } } if (physics_body_ext.has("motion") || physics_body_ext.has("type")) { p_gltf_node->set_additional_data(StringName("GLTFPhysicsBody"), GLTFPhysicsBody::from_dictionary(physics_body_ext)); @@ -241,6 +246,19 @@ Node3D *_add_physics_node_to_given_node(Node3D *p_current_node, Node3D *p_child, return p_current_node; } +Array _get_ancestor_compound_trigger_nodes(Ref p_state, TypedArray p_state_nodes, CollisionObject3D *p_ancestor_col_obj) { + GLTFNodeIndex ancestor_index = p_state->get_node_index(p_ancestor_col_obj); + ERR_FAIL_INDEX_V(ancestor_index, p_state_nodes.size(), Array()); + Ref ancestor_gltf_node = p_state_nodes[ancestor_index]; + Variant compound_trigger_nodes = ancestor_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes")); + if (compound_trigger_nodes.is_array()) { + return compound_trigger_nodes; + } + Array ret; + ancestor_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), ret); + return ret; +} + Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref p_state, Ref p_gltf_node, Node *p_scene_parent) { Ref gltf_physics_body = p_gltf_node->get_additional_data(StringName("GLTFPhysicsBody")); #ifndef DISABLE_DEPRECATED @@ -269,12 +287,27 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref p_state #endif // DISABLE_DEPRECATED Node3D *ret = nullptr; CollisionObject3D *ancestor_col_obj = nullptr; + Ref gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); + Ref gltf_physics_trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape")); if (gltf_physics_body.is_valid()) { ancestor_col_obj = gltf_physics_body->to_node(); ret = ancestor_col_obj; } else { ancestor_col_obj = _get_ancestor_collision_object(p_scene_parent); - if (!Object::cast_to(ancestor_col_obj)) { + if (Object::cast_to(ancestor_col_obj) && gltf_physics_trigger_shape.is_valid()) { + // At this point, we found an ancestor Area3D node. But do we want to use it for this trigger shape? + TypedArray state_nodes = p_state->get_nodes(); + GLTFNodeIndex self_index = state_nodes.find(p_gltf_node); + Array compound_trigger_nodes = _get_ancestor_compound_trigger_nodes(p_state, state_nodes, ancestor_col_obj); + // Check if the ancestor specifies compound trigger nodes, and if this node is in there. + // Remember that JSON does not have integers, only "number", aka double-precision floats. + if (compound_trigger_nodes.size() > 0 && !compound_trigger_nodes.has(double(self_index))) { + // If the compound trigger we found is not the intended user of + // this shape node, then we need to create a new Area3D node. + ancestor_col_obj = memnew(Area3D); + ret = ancestor_col_obj; + } + } else if (!Object::cast_to(ancestor_col_obj)) { if (p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundCollider"))) { // If the GLTF file wants this node to group solid shapes together, // and there is no parent body, we need to create a static body. @@ -288,8 +321,6 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref p_state // set above. If there is no ancestor body, we will either generate an // Area3D or StaticBody3D implicitly, so prefer an Area3D as the base // node for best compatibility with signal connections to this node. - Ref gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); - Ref gltf_physics_trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape")); bool is_ancestor_col_obj_solid = Object::cast_to(ancestor_col_obj); if (is_ancestor_col_obj_solid && gltf_physics_collider_shape.is_valid()) { Node3D *child = _generate_shape_node_and_body_if_needed(p_state, p_gltf_node, gltf_physics_collider_shape, ancestor_col_obj, false); @@ -361,8 +392,14 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref p_state, Re gltf_shape->set_mesh_index(_get_or_insert_mesh_in_state(p_state, importer_mesh)); } } - if (cast_to(_get_ancestor_collision_object(p_scene_node->get_parent()))) { + CollisionObject3D *ancestor_col_obj = _get_ancestor_collision_object(p_scene_node->get_parent()); + if (cast_to(ancestor_col_obj)) { p_gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShape"), gltf_shape); + // Write explicit member shape nodes to the ancestor compound trigger node. + TypedArray state_nodes = p_state->get_nodes(); + GLTFNodeIndex self_index = state_nodes.size(); // The current p_gltf_node will be inserted next. + Array compound_trigger_nodes = _get_ancestor_compound_trigger_nodes(p_state, p_state->get_nodes(), ancestor_col_obj); + compound_trigger_nodes.push_back(double(self_index)); } else { p_gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShape"), gltf_shape); } @@ -421,6 +458,11 @@ Error GLTFDocumentExtensionPhysics::export_node(Ref p_state, Ref physics_body = p_gltf_node->get_additional_data(StringName("GLTFPhysicsBody")); if (physics_body.is_valid()) { physics_body_ext = physics_body->to_dictionary(); + Variant compound_trigger_nodes = p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes")); + if (compound_trigger_nodes.is_array()) { + Dictionary trigger_property = physics_body_ext.get_or_add("trigger", {}); + trigger_property["nodes"] = compound_trigger_nodes; + } } Ref collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape")); if (collider_shape.is_valid()) {