Merge pull request #88301 from aaronfranke/gltf-explicit-compound-triggers

Add support for explicitly-defined compound triggers in GLTF files
This commit is contained in:
Rémi Verschelde 2024-04-26 11:08:19 +02:00
commit cb01094ccd
No known key found for this signature in database
GPG Key ID: C3336907360768E1

View File

@ -127,6 +127,11 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> 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<GLTFState> p_state, TypedArray<GLTFNode> 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<GLTFNode> 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<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
Ref<GLTFPhysicsBody> gltf_physics_body = p_gltf_node->get_additional_data(StringName("GLTFPhysicsBody"));
#ifndef DISABLE_DEPRECATED
@ -269,12 +287,27 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state
#endif // DISABLE_DEPRECATED
Node3D *ret = nullptr;
CollisionObject3D *ancestor_col_obj = nullptr;
Ref<GLTFPhysicsShape> gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape"));
Ref<GLTFPhysicsShape> 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<PhysicsBody3D>(ancestor_col_obj)) {
if (Object::cast_to<Area3D>(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<GLTFNode> 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<PhysicsBody3D>(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<GLTFState> 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<GLTFPhysicsShape> gltf_physics_collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape"));
Ref<GLTFPhysicsShape> gltf_physics_trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape"));
bool is_ancestor_col_obj_solid = Object::cast_to<PhysicsBody3D>(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);
@ -362,8 +393,14 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Re
gltf_shape->set_mesh_index(_get_or_insert_mesh_in_state(p_state, importer_mesh));
}
}
if (cast_to<Area3D>(_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<Area3D>(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<GLTFNode> 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);
}
@ -422,6 +459,11 @@ Error GLTFDocumentExtensionPhysics::export_node(Ref<GLTFState> p_state, Ref<GLTF
Ref<GLTFPhysicsBody> 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<GLTFPhysicsShape> collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape"));
if (collider_shape.is_valid()) {