| 1 | /**************************************************************************/ | 
|---|
| 2 | /*  gltf_document_extension_physics.cpp                                   */ | 
|---|
| 3 | /**************************************************************************/ | 
|---|
| 4 | /*                         This file is part of:                          */ | 
|---|
| 5 | /*                             GODOT ENGINE                               */ | 
|---|
| 6 | /*                        https://godotengine.org                         */ | 
|---|
| 7 | /**************************************************************************/ | 
|---|
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | 
|---|
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | 
|---|
| 10 | /*                                                                        */ | 
|---|
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining  */ | 
|---|
| 12 | /* a copy of this software and associated documentation files (the        */ | 
|---|
| 13 | /* "Software"), to deal in the Software without restriction, including    */ | 
|---|
| 14 | /* without limitation the rights to use, copy, modify, merge, publish,    */ | 
|---|
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to     */ | 
|---|
| 16 | /* permit persons to whom the Software is furnished to do so, subject to  */ | 
|---|
| 17 | /* the following conditions:                                              */ | 
|---|
| 18 | /*                                                                        */ | 
|---|
| 19 | /* The above copyright notice and this permission notice shall be         */ | 
|---|
| 20 | /* included in all copies or substantial portions of the Software.        */ | 
|---|
| 21 | /*                                                                        */ | 
|---|
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */ | 
|---|
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */ | 
|---|
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ | 
|---|
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */ | 
|---|
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */ | 
|---|
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */ | 
|---|
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */ | 
|---|
| 29 | /**************************************************************************/ | 
|---|
| 30 |  | 
|---|
| 31 | #include "gltf_document_extension_physics.h" | 
|---|
| 32 |  | 
|---|
| 33 | #include "scene/3d/area_3d.h" | 
|---|
| 34 |  | 
|---|
| 35 | // Import process. | 
|---|
| 36 | Error GLTFDocumentExtensionPhysics::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) { | 
|---|
| 37 | if (!p_extensions.has( "OMI_collider") && !p_extensions.has( "OMI_physics_body")) { | 
|---|
| 38 | return ERR_SKIP; | 
|---|
| 39 | } | 
|---|
| 40 | Dictionary state_json = p_state->get_json(); | 
|---|
| 41 | if (state_json.has( "extensions")) { | 
|---|
| 42 | Dictionary state_extensions = state_json[ "extensions"]; | 
|---|
| 43 | if (state_extensions.has( "OMI_collider")) { | 
|---|
| 44 | Dictionary omi_collider_ext = state_extensions[ "OMI_collider"]; | 
|---|
| 45 | if (omi_collider_ext.has( "colliders")) { | 
|---|
| 46 | Array state_collider_dicts = omi_collider_ext[ "colliders"]; | 
|---|
| 47 | if (state_collider_dicts.size() > 0) { | 
|---|
| 48 | Array state_colliders; | 
|---|
| 49 | for (int i = 0; i < state_collider_dicts.size(); i++) { | 
|---|
| 50 | state_colliders.push_back(GLTFPhysicsShape::from_dictionary(state_collider_dicts[i])); | 
|---|
| 51 | } | 
|---|
| 52 | p_state->set_additional_data( "GLTFPhysicsShapes", state_colliders); | 
|---|
| 53 | } | 
|---|
| 54 | } | 
|---|
| 55 | } | 
|---|
| 56 | } | 
|---|
| 57 | return OK; | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | Vector<String> GLTFDocumentExtensionPhysics::get_supported_extensions() { | 
|---|
| 61 | Vector<String> ret; | 
|---|
| 62 | ret.push_back( "OMI_collider"); | 
|---|
| 63 | ret.push_back( "OMI_physics_body"); | 
|---|
| 64 | return ret; | 
|---|
| 65 | } | 
|---|
| 66 |  | 
|---|
| 67 | Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions) { | 
|---|
| 68 | if (p_extensions.has( "OMI_collider")) { | 
|---|
| 69 | Dictionary node_collider_ext = p_extensions[ "OMI_collider"]; | 
|---|
| 70 | if (node_collider_ext.has( "collider")) { | 
|---|
| 71 | // "collider" is the index of the collider in the state colliders array. | 
|---|
| 72 | int node_collider_index = node_collider_ext[ "collider"]; | 
|---|
| 73 | Array state_colliders = p_state->get_additional_data( "GLTFPhysicsShapes"); | 
|---|
| 74 | ERR_FAIL_INDEX_V_MSG(node_collider_index, state_colliders.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node "+ p_gltf_node->get_name() + ", the collider index "+ itos(node_collider_index) + " is not in the state colliders (size: "+ itos(state_colliders.size()) + ")."); | 
|---|
| 75 | p_gltf_node->set_additional_data(StringName( "GLTFPhysicsShape"), state_colliders[node_collider_index]); | 
|---|
| 76 | } else { | 
|---|
| 77 | p_gltf_node->set_additional_data(StringName( "GLTFPhysicsShape"), GLTFPhysicsShape::from_dictionary(p_extensions[ "OMI_collider"])); | 
|---|
| 78 | } | 
|---|
| 79 | } | 
|---|
| 80 | if (p_extensions.has( "OMI_physics_body")) { | 
|---|
| 81 | p_gltf_node->set_additional_data(StringName( "GLTFPhysicsBody"), GLTFPhysicsBody::from_dictionary(p_extensions[ "OMI_physics_body"])); | 
|---|
| 82 | } | 
|---|
| 83 | return OK; | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | void _setup_collider_mesh_resource_from_index_if_needed(Ref<GLTFState> p_state, Ref<GLTFPhysicsShape> p_collider) { | 
|---|
| 87 | GLTFMeshIndex collider_mesh_index = p_collider->get_mesh_index(); | 
|---|
| 88 | if (collider_mesh_index == -1) { | 
|---|
| 89 | return; // No mesh for this collider. | 
|---|
| 90 | } | 
|---|
| 91 | Ref<ImporterMesh> importer_mesh = p_collider->get_importer_mesh(); | 
|---|
| 92 | if (importer_mesh.is_valid()) { | 
|---|
| 93 | return; // The mesh resource is already set up. | 
|---|
| 94 | } | 
|---|
| 95 | TypedArray<GLTFMesh> state_meshes = p_state->get_meshes(); | 
|---|
| 96 | ERR_FAIL_INDEX_MSG(collider_mesh_index, state_meshes.size(), "GLTF Physics: When importing '"+ p_state->get_scene_name() + "', the collider mesh index "+ itos(collider_mesh_index) + " is not in the state meshes (size: "+ itos(state_meshes.size()) + ")."); | 
|---|
| 97 | Ref<GLTFMesh> gltf_mesh = state_meshes[collider_mesh_index]; | 
|---|
| 98 | ERR_FAIL_COND(gltf_mesh.is_null()); | 
|---|
| 99 | importer_mesh = gltf_mesh->get_mesh(); | 
|---|
| 100 | ERR_FAIL_COND(importer_mesh.is_null()); | 
|---|
| 101 | p_collider->set_importer_mesh(importer_mesh); | 
|---|
| 102 | } | 
|---|
| 103 |  | 
|---|
| 104 | CollisionObject3D *_generate_collision_with_body(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Ref<GLTFPhysicsShape> p_collider, Ref<GLTFPhysicsBody> p_physics_body) { | 
|---|
| 105 | print_verbose( "glTF: Creating collision for: "+ p_gltf_node->get_name()); | 
|---|
| 106 | bool is_trigger = p_collider->get_is_trigger(); | 
|---|
| 107 | // This method is used for the case where we must generate a parent body. | 
|---|
| 108 | // This is can happen for multiple reasons. One possibility is that this | 
|---|
| 109 | // GLTF file is using OMI_collider but not OMI_physics_body, or at least | 
|---|
| 110 | // this particular node is not using it. Another possibility is that the | 
|---|
| 111 | // physics body information is set up on the same GLTF node, not a parent. | 
|---|
| 112 | CollisionObject3D *body; | 
|---|
| 113 | if (p_physics_body.is_valid()) { | 
|---|
| 114 | // This code is run when the physics body is on the same GLTF node. | 
|---|
| 115 | body = p_physics_body->to_node(); | 
|---|
| 116 | if (is_trigger != (p_physics_body->get_body_type() == "trigger")) { | 
|---|
| 117 | // Edge case: If the body's trigger and the collider's trigger | 
|---|
| 118 | // are in disagreement, we need to create another new body. | 
|---|
| 119 | CollisionObject3D *child = _generate_collision_with_body(p_state, p_gltf_node, p_collider, nullptr); | 
|---|
| 120 | child->set_name(p_gltf_node->get_name() + (is_trigger ? String( "Trigger") : String( "Solid"))); | 
|---|
| 121 | body->add_child(child); | 
|---|
| 122 | return body; | 
|---|
| 123 | } | 
|---|
| 124 | } else if (is_trigger) { | 
|---|
| 125 | body = memnew(Area3D); | 
|---|
| 126 | } else { | 
|---|
| 127 | body = memnew(StaticBody3D); | 
|---|
| 128 | } | 
|---|
| 129 | CollisionShape3D *shape = p_collider->to_node(); | 
|---|
| 130 | shape->set_name(p_gltf_node->get_name() + "Shape"); | 
|---|
| 131 | body->add_child(shape); | 
|---|
| 132 | return body; | 
|---|
| 133 | } | 
|---|
| 134 |  | 
|---|
| 135 | Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) { | 
|---|
| 136 | Ref<GLTFPhysicsBody> physics_body = p_gltf_node->get_additional_data(StringName( "GLTFPhysicsBody")); | 
|---|
| 137 | Ref<GLTFPhysicsShape> collider = p_gltf_node->get_additional_data(StringName( "GLTFPhysicsShape")); | 
|---|
| 138 | if (collider.is_valid()) { | 
|---|
| 139 | _setup_collider_mesh_resource_from_index_if_needed(p_state, collider); | 
|---|
| 140 | // If the collider has the correct type of parent, we just return one node. | 
|---|
| 141 | if (collider->get_is_trigger()) { | 
|---|
| 142 | if (Object::cast_to<Area3D>(p_scene_parent)) { | 
|---|
| 143 | return collider->to_node(true); | 
|---|
| 144 | } | 
|---|
| 145 | } else { | 
|---|
| 146 | if (Object::cast_to<PhysicsBody3D>(p_scene_parent)) { | 
|---|
| 147 | return collider->to_node(true); | 
|---|
| 148 | } | 
|---|
| 149 | } | 
|---|
| 150 | return _generate_collision_with_body(p_state, p_gltf_node, collider, physics_body); | 
|---|
| 151 | } | 
|---|
| 152 | if (physics_body.is_valid()) { | 
|---|
| 153 | return physics_body->to_node(); | 
|---|
| 154 | } | 
|---|
| 155 | return nullptr; | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|
| 158 | // Export process. | 
|---|
| 159 | bool _are_all_faces_equal(const Vector<Face3> &p_a, const Vector<Face3> &p_b) { | 
|---|
| 160 | if (p_a.size() != p_b.size()) { | 
|---|
| 161 | return false; | 
|---|
| 162 | } | 
|---|
| 163 | for (int i = 0; i < p_a.size(); i++) { | 
|---|
| 164 | const Vector3 *a_vertices = p_a[i].vertex; | 
|---|
| 165 | const Vector3 *b_vertices = p_b[i].vertex; | 
|---|
| 166 | for (int j = 0; j < 3; j++) { | 
|---|
| 167 | if (!a_vertices[j].is_equal_approx(b_vertices[j])) { | 
|---|
| 168 | return false; | 
|---|
| 169 | } | 
|---|
| 170 | } | 
|---|
| 171 | } | 
|---|
| 172 | return true; | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | GLTFMeshIndex _get_or_insert_mesh_in_state(Ref<GLTFState> p_state, Ref<ImporterMesh> p_mesh) { | 
|---|
| 176 | ERR_FAIL_COND_V(p_mesh.is_null(), -1); | 
|---|
| 177 | TypedArray<GLTFMesh> state_meshes = p_state->get_meshes(); | 
|---|
| 178 | Vector<Face3> mesh_faces = p_mesh->get_faces(); | 
|---|
| 179 | // De-duplication: If the state already has the mesh we need, use that one. | 
|---|
| 180 | for (GLTFMeshIndex i = 0; i < state_meshes.size(); i++) { | 
|---|
| 181 | Ref<GLTFMesh> state_gltf_mesh = state_meshes[i]; | 
|---|
| 182 | ERR_CONTINUE(state_gltf_mesh.is_null()); | 
|---|
| 183 | Ref<ImporterMesh> state_importer_mesh = state_gltf_mesh->get_mesh(); | 
|---|
| 184 | ERR_CONTINUE(state_importer_mesh.is_null()); | 
|---|
| 185 | if (state_importer_mesh == p_mesh) { | 
|---|
| 186 | return i; | 
|---|
| 187 | } | 
|---|
| 188 | if (_are_all_faces_equal(state_importer_mesh->get_faces(), mesh_faces)) { | 
|---|
| 189 | return i; | 
|---|
| 190 | } | 
|---|
| 191 | } | 
|---|
| 192 | // After the loop, we have checked that the mesh is not equal to any of the | 
|---|
| 193 | // meshes in the state. So we insert a new mesh into the state mesh array. | 
|---|
| 194 | Ref<GLTFMesh> gltf_mesh; | 
|---|
| 195 | gltf_mesh.instantiate(); | 
|---|
| 196 | gltf_mesh->set_mesh(p_mesh); | 
|---|
| 197 | GLTFMeshIndex mesh_index = state_meshes.size(); | 
|---|
| 198 | state_meshes.push_back(gltf_mesh); | 
|---|
| 199 | p_state->set_meshes(state_meshes); | 
|---|
| 200 | return mesh_index; | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node) { | 
|---|
| 204 | if (cast_to<CollisionShape3D>(p_scene_node)) { | 
|---|
| 205 | CollisionShape3D *shape = Object::cast_to<CollisionShape3D>(p_scene_node); | 
|---|
| 206 | Ref<GLTFPhysicsShape> collider = GLTFPhysicsShape::from_node(shape); | 
|---|
| 207 | { | 
|---|
| 208 | Ref<ImporterMesh> importer_mesh = collider->get_importer_mesh(); | 
|---|
| 209 | if (importer_mesh.is_valid()) { | 
|---|
| 210 | collider->set_mesh_index(_get_or_insert_mesh_in_state(p_state, importer_mesh)); | 
|---|
| 211 | } | 
|---|
| 212 | } | 
|---|
| 213 | p_gltf_node->set_additional_data(StringName( "GLTFPhysicsShape"), collider); | 
|---|
| 214 | } else if (cast_to<CollisionObject3D>(p_scene_node)) { | 
|---|
| 215 | CollisionObject3D *body = Object::cast_to<CollisionObject3D>(p_scene_node); | 
|---|
| 216 | p_gltf_node->set_additional_data(StringName( "GLTFPhysicsBody"), GLTFPhysicsBody::from_node(body)); | 
|---|
| 217 | } | 
|---|
| 218 | } | 
|---|
| 219 |  | 
|---|
| 220 | Array _get_or_create_state_colliders_in_state(Ref<GLTFState> p_state) { | 
|---|
| 221 | Dictionary state_json = p_state->get_json(); | 
|---|
| 222 | Dictionary state_extensions; | 
|---|
| 223 | if (state_json.has( "extensions")) { | 
|---|
| 224 | state_extensions = state_json[ "extensions"]; | 
|---|
| 225 | } else { | 
|---|
| 226 | state_json[ "extensions"] = state_extensions; | 
|---|
| 227 | } | 
|---|
| 228 | Dictionary omi_collider_ext; | 
|---|
| 229 | if (state_extensions.has( "OMI_collider")) { | 
|---|
| 230 | omi_collider_ext = state_extensions[ "OMI_collider"]; | 
|---|
| 231 | } else { | 
|---|
| 232 | state_extensions[ "OMI_collider"] = omi_collider_ext; | 
|---|
| 233 | p_state->add_used_extension( "OMI_collider"); | 
|---|
| 234 | } | 
|---|
| 235 | Array state_colliders; | 
|---|
| 236 | if (omi_collider_ext.has( "colliders")) { | 
|---|
| 237 | state_colliders = omi_collider_ext[ "colliders"]; | 
|---|
| 238 | } else { | 
|---|
| 239 | omi_collider_ext[ "colliders"] = state_colliders; | 
|---|
| 240 | } | 
|---|
| 241 | return state_colliders; | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|
| 244 | Error GLTFDocumentExtensionPhysics::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_node_json, Node *p_node) { | 
|---|
| 245 | Dictionary node_extensions = r_node_json[ "extensions"]; | 
|---|
| 246 | Ref<GLTFPhysicsBody> physics_body = p_gltf_node->get_additional_data(StringName( "GLTFPhysicsBody")); | 
|---|
| 247 | if (physics_body.is_valid()) { | 
|---|
| 248 | node_extensions[ "OMI_physics_body"] = physics_body->to_dictionary(); | 
|---|
| 249 | p_state->add_used_extension( "OMI_physics_body"); | 
|---|
| 250 | } | 
|---|
| 251 | Ref<GLTFPhysicsShape> collider = p_gltf_node->get_additional_data(StringName( "GLTFPhysicsShape")); | 
|---|
| 252 | if (collider.is_valid()) { | 
|---|
| 253 | Array state_colliders = _get_or_create_state_colliders_in_state(p_state); | 
|---|
| 254 | int size = state_colliders.size(); | 
|---|
| 255 | Dictionary omi_collider_ext; | 
|---|
| 256 | node_extensions[ "OMI_collider"] = omi_collider_ext; | 
|---|
| 257 | Dictionary collider_dict = collider->to_dictionary(); | 
|---|
| 258 | for (int i = 0; i < size; i++) { | 
|---|
| 259 | Dictionary other = state_colliders[i]; | 
|---|
| 260 | if (other == collider_dict) { | 
|---|
| 261 | // De-duplication: If we already have an identical collider, | 
|---|
| 262 | // set the collider index to the existing one and return. | 
|---|
| 263 | omi_collider_ext[ "collider"] = i; | 
|---|
| 264 | return OK; | 
|---|
| 265 | } | 
|---|
| 266 | } | 
|---|
| 267 | // If we don't have an identical collider, add it to the array. | 
|---|
| 268 | state_colliders.push_back(collider_dict); | 
|---|
| 269 | omi_collider_ext[ "collider"] = size; | 
|---|
| 270 | } | 
|---|
| 271 | return OK; | 
|---|
| 272 | } | 
|---|
| 273 |  | 
|---|