2022-11-22 16:29:56 +01:00
/**************************************************************************/
2023-06-16 22:01:35 +02:00
/* gltf_physics_shape.cpp */
2022-11-22 16:29:56 +01:00
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2023-06-16 22:01:35 +02:00
# include "gltf_physics_shape.h"
2022-11-22 16:29:56 +01:00
# include "../../gltf_state.h"
2023-06-13 16:56:21 +02:00
2022-11-22 16:29:56 +01:00
# include "core/math/convex_hull.h"
# include "scene/3d/area_3d.h"
# include "scene/resources/box_shape_3d.h"
# include "scene/resources/capsule_shape_3d.h"
# include "scene/resources/concave_polygon_shape_3d.h"
# include "scene/resources/convex_polygon_shape_3d.h"
# include "scene/resources/cylinder_shape_3d.h"
# include "scene/resources/importer_mesh.h"
# include "scene/resources/sphere_shape_3d.h"
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : _bind_methods ( ) {
ClassDB : : bind_static_method ( " GLTFPhysicsShape " , D_METHOD ( " from_node " , " shape_node " ) , & GLTFPhysicsShape : : from_node ) ;
ClassDB : : bind_method ( D_METHOD ( " to_node " , " cache_shapes " ) , & GLTFPhysicsShape : : to_node , DEFVAL ( false ) ) ;
2022-11-22 16:29:56 +01:00
2023-06-16 22:01:35 +02:00
ClassDB : : bind_static_method ( " GLTFPhysicsShape " , D_METHOD ( " from_dictionary " , " dictionary " ) , & GLTFPhysicsShape : : from_dictionary ) ;
ClassDB : : bind_method ( D_METHOD ( " to_dictionary " ) , & GLTFPhysicsShape : : to_dictionary ) ;
2022-11-22 16:29:56 +01:00
2023-06-16 22:01:35 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_shape_type " ) , & GLTFPhysicsShape : : get_shape_type ) ;
ClassDB : : bind_method ( D_METHOD ( " set_shape_type " , " shape_type " ) , & GLTFPhysicsShape : : set_shape_type ) ;
ClassDB : : bind_method ( D_METHOD ( " get_size " ) , & GLTFPhysicsShape : : get_size ) ;
ClassDB : : bind_method ( D_METHOD ( " set_size " , " size " ) , & GLTFPhysicsShape : : set_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_radius " ) , & GLTFPhysicsShape : : get_radius ) ;
ClassDB : : bind_method ( D_METHOD ( " set_radius " , " radius " ) , & GLTFPhysicsShape : : set_radius ) ;
ClassDB : : bind_method ( D_METHOD ( " get_height " ) , & GLTFPhysicsShape : : get_height ) ;
ClassDB : : bind_method ( D_METHOD ( " set_height " , " height " ) , & GLTFPhysicsShape : : set_height ) ;
ClassDB : : bind_method ( D_METHOD ( " get_is_trigger " ) , & GLTFPhysicsShape : : get_is_trigger ) ;
ClassDB : : bind_method ( D_METHOD ( " set_is_trigger " , " is_trigger " ) , & GLTFPhysicsShape : : set_is_trigger ) ;
ClassDB : : bind_method ( D_METHOD ( " get_mesh_index " ) , & GLTFPhysicsShape : : get_mesh_index ) ;
ClassDB : : bind_method ( D_METHOD ( " set_mesh_index " , " mesh_index " ) , & GLTFPhysicsShape : : set_mesh_index ) ;
ClassDB : : bind_method ( D_METHOD ( " get_importer_mesh " ) , & GLTFPhysicsShape : : get_importer_mesh ) ;
ClassDB : : bind_method ( D_METHOD ( " set_importer_mesh " , " importer_mesh " ) , & GLTFPhysicsShape : : set_importer_mesh ) ;
2022-11-22 16:29:56 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " shape_type " ) , " set_shape_type " , " get_shape_type " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : VECTOR3 , " size " ) , " set_size " , " get_size " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " radius " ) , " set_radius " , " get_radius " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " height " ) , " set_height " , " get_height " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " is_trigger " ) , " set_is_trigger " , " get_is_trigger " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " mesh_index " ) , " set_mesh_index " , " get_mesh_index " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " importer_mesh " , PROPERTY_HINT_RESOURCE_TYPE , " ImporterMesh " ) , " set_importer_mesh " , " get_importer_mesh " ) ;
}
2023-06-16 22:01:35 +02:00
String GLTFPhysicsShape : : get_shape_type ( ) const {
2022-11-22 16:29:56 +01:00
return shape_type ;
}
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : set_shape_type ( String p_shape_type ) {
2022-11-22 16:29:56 +01:00
shape_type = p_shape_type ;
}
2023-06-16 22:01:35 +02:00
Vector3 GLTFPhysicsShape : : get_size ( ) const {
2022-11-22 16:29:56 +01:00
return size ;
}
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : set_size ( Vector3 p_size ) {
2022-11-22 16:29:56 +01:00
size = p_size ;
}
2023-06-16 22:01:35 +02:00
real_t GLTFPhysicsShape : : get_radius ( ) const {
2022-11-22 16:29:56 +01:00
return radius ;
}
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : set_radius ( real_t p_radius ) {
2022-11-22 16:29:56 +01:00
radius = p_radius ;
}
2023-06-16 22:01:35 +02:00
real_t GLTFPhysicsShape : : get_height ( ) const {
2022-11-22 16:29:56 +01:00
return height ;
}
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : set_height ( real_t p_height ) {
2022-11-22 16:29:56 +01:00
height = p_height ;
}
2023-06-16 22:01:35 +02:00
bool GLTFPhysicsShape : : get_is_trigger ( ) const {
2022-11-22 16:29:56 +01:00
return is_trigger ;
}
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : set_is_trigger ( bool p_is_trigger ) {
2022-11-22 16:29:56 +01:00
is_trigger = p_is_trigger ;
}
2023-06-16 22:01:35 +02:00
GLTFMeshIndex GLTFPhysicsShape : : get_mesh_index ( ) const {
2022-11-22 16:29:56 +01:00
return mesh_index ;
}
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : set_mesh_index ( GLTFMeshIndex p_mesh_index ) {
2022-11-22 16:29:56 +01:00
mesh_index = p_mesh_index ;
}
2023-06-16 22:01:35 +02:00
Ref < ImporterMesh > GLTFPhysicsShape : : get_importer_mesh ( ) const {
2022-11-22 16:29:56 +01:00
return importer_mesh ;
}
2023-06-16 22:01:35 +02:00
void GLTFPhysicsShape : : set_importer_mesh ( Ref < ImporterMesh > p_importer_mesh ) {
2022-11-22 16:29:56 +01:00
importer_mesh = p_importer_mesh ;
}
2023-06-16 22:01:35 +02:00
Ref < GLTFPhysicsShape > GLTFPhysicsShape : : from_node ( const CollisionShape3D * p_collider_node ) {
Ref < GLTFPhysicsShape > gltf_shape ;
gltf_shape . instantiate ( ) ;
ERR_FAIL_NULL_V_MSG ( p_collider_node , gltf_shape , " Tried to create a GLTFPhysicsShape from a CollisionShape3D node, but the given node was null. " ) ;
2022-11-22 16:29:56 +01:00
Node * parent = p_collider_node - > get_parent ( ) ;
if ( cast_to < const Area3D > ( parent ) ) {
2023-06-16 22:01:35 +02:00
gltf_shape - > set_is_trigger ( true ) ;
2022-11-22 16:29:56 +01:00
}
// All the code for working with the shape is below this comment.
2023-06-16 22:01:35 +02:00
Ref < Shape3D > shape_resource = p_collider_node - > get_shape ( ) ;
ERR_FAIL_COND_V_MSG ( shape_resource . is_null ( ) , gltf_shape , " Tried to create a GLTFPhysicsShape from a CollisionShape3D node, but the given node had a null shape. " ) ;
gltf_shape - > _shape_cache = shape_resource ;
if ( cast_to < BoxShape3D > ( shape_resource . ptr ( ) ) ) {
gltf_shape - > shape_type = " box " ;
Ref < BoxShape3D > box = shape_resource ;
gltf_shape - > set_size ( box - > get_size ( ) ) ;
} else if ( cast_to < const CapsuleShape3D > ( shape_resource . ptr ( ) ) ) {
gltf_shape - > shape_type = " capsule " ;
Ref < CapsuleShape3D > capsule = shape_resource ;
gltf_shape - > set_radius ( capsule - > get_radius ( ) ) ;
gltf_shape - > set_height ( capsule - > get_height ( ) ) ;
} else if ( cast_to < const CylinderShape3D > ( shape_resource . ptr ( ) ) ) {
gltf_shape - > shape_type = " cylinder " ;
Ref < CylinderShape3D > cylinder = shape_resource ;
gltf_shape - > set_radius ( cylinder - > get_radius ( ) ) ;
gltf_shape - > set_height ( cylinder - > get_height ( ) ) ;
} else if ( cast_to < const SphereShape3D > ( shape_resource . ptr ( ) ) ) {
gltf_shape - > shape_type = " sphere " ;
Ref < SphereShape3D > sphere = shape_resource ;
gltf_shape - > set_radius ( sphere - > get_radius ( ) ) ;
} else if ( cast_to < const ConvexPolygonShape3D > ( shape_resource . ptr ( ) ) ) {
gltf_shape - > shape_type = " hull " ;
Ref < ConvexPolygonShape3D > convex = shape_resource ;
2022-11-22 16:29:56 +01:00
Vector < Vector3 > hull_points = convex - > get_points ( ) ;
2023-06-16 22:01:35 +02:00
ERR_FAIL_COND_V_MSG ( hull_points . size ( ) < 3 , gltf_shape , " GLTFPhysicsShape: Convex hull has fewer points ( " + itos ( hull_points . size ( ) ) + " ) than the minimum of 3. At least 3 points are required in order to save to GLTF, since it uses a mesh to represent convex hulls. " ) ;
2022-11-22 16:29:56 +01:00
if ( hull_points . size ( ) > 255 ) {
2023-06-16 22:01:35 +02:00
WARN_PRINT ( " GLTFPhysicsShape: Convex hull has more points ( " + itos ( hull_points . size ( ) ) + " ) than the recommended maximum of 255. This may not load correctly in other engines. " ) ;
2022-11-22 16:29:56 +01:00
}
// Convert the convex hull points into an array of faces.
Geometry3D : : MeshData md ;
Error err = ConvexHullComputer : : convex_hull ( hull_points , md ) ;
2023-06-16 22:01:35 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , gltf_shape , " GLTFPhysicsShape: Failed to compute convex hull. " ) ;
2022-11-22 16:29:56 +01:00
Vector < Vector3 > face_vertices ;
for ( uint32_t i = 0 ; i < md . faces . size ( ) ; i + + ) {
uint32_t index_count = md . faces [ i ] . indices . size ( ) ;
for ( uint32_t j = 1 ; j < index_count - 1 ; j + + ) {
face_vertices . append ( hull_points [ md . faces [ i ] . indices [ 0 ] ] ) ;
face_vertices . append ( hull_points [ md . faces [ i ] . indices [ j ] ] ) ;
face_vertices . append ( hull_points [ md . faces [ i ] . indices [ j + 1 ] ] ) ;
}
}
// Create an ImporterMesh from the faces.
Ref < ImporterMesh > importer_mesh ;
importer_mesh . instantiate ( ) ;
Array surface_array ;
surface_array . resize ( Mesh : : ArrayType : : ARRAY_MAX ) ;
surface_array [ Mesh : : ArrayType : : ARRAY_VERTEX ] = face_vertices ;
importer_mesh - > add_surface ( Mesh : : PRIMITIVE_TRIANGLES , surface_array ) ;
2023-06-16 22:01:35 +02:00
gltf_shape - > set_importer_mesh ( importer_mesh ) ;
} else if ( cast_to < const ConcavePolygonShape3D > ( shape_resource . ptr ( ) ) ) {
gltf_shape - > shape_type = " trimesh " ;
Ref < ConcavePolygonShape3D > concave = shape_resource ;
2022-11-22 16:29:56 +01:00
Ref < ImporterMesh > importer_mesh ;
importer_mesh . instantiate ( ) ;
Array surface_array ;
surface_array . resize ( Mesh : : ArrayType : : ARRAY_MAX ) ;
surface_array [ Mesh : : ArrayType : : ARRAY_VERTEX ] = concave - > get_faces ( ) ;
importer_mesh - > add_surface ( Mesh : : PRIMITIVE_TRIANGLES , surface_array ) ;
2023-06-16 22:01:35 +02:00
gltf_shape - > set_importer_mesh ( importer_mesh ) ;
2022-11-22 16:29:56 +01:00
} else {
2023-06-16 22:01:35 +02:00
ERR_PRINT ( " Tried to create a GLTFPhysicsShape from a CollisionShape3D node, but the given node's shape ' " + String ( Variant ( shape_resource ) ) +
2022-11-22 16:29:56 +01:00
" ' had an unsupported shape type. Only BoxShape3D, CapsuleShape3D, CylinderShape3D, SphereShape3D, ConcavePolygonShape3D, and ConvexPolygonShape3D are supported. " ) ;
}
2023-06-16 22:01:35 +02:00
return gltf_shape ;
2022-11-22 16:29:56 +01:00
}
2023-06-16 22:01:35 +02:00
CollisionShape3D * GLTFPhysicsShape : : to_node ( bool p_cache_shapes ) {
CollisionShape3D * gltf_shape = memnew ( CollisionShape3D ) ;
2022-11-22 16:29:56 +01:00
if ( ! p_cache_shapes | | _shape_cache = = nullptr ) {
if ( shape_type = = " box " ) {
Ref < BoxShape3D > box ;
box . instantiate ( ) ;
box - > set_size ( size ) ;
_shape_cache = box ;
} else if ( shape_type = = " capsule " ) {
Ref < CapsuleShape3D > capsule ;
capsule . instantiate ( ) ;
capsule - > set_radius ( radius ) ;
capsule - > set_height ( height ) ;
_shape_cache = capsule ;
} else if ( shape_type = = " cylinder " ) {
Ref < CylinderShape3D > cylinder ;
cylinder . instantiate ( ) ;
cylinder - > set_radius ( radius ) ;
cylinder - > set_height ( height ) ;
_shape_cache = cylinder ;
} else if ( shape_type = = " sphere " ) {
Ref < SphereShape3D > sphere ;
sphere . instantiate ( ) ;
sphere - > set_radius ( radius ) ;
_shape_cache = sphere ;
} else if ( shape_type = = " hull " ) {
2023-06-16 22:01:35 +02:00
ERR_FAIL_COND_V_MSG ( importer_mesh . is_null ( ) , gltf_shape , " GLTFPhysicsShape: Error converting convex hull shape to a node: The mesh resource is null. " ) ;
2022-11-22 16:29:56 +01:00
Ref < ConvexPolygonShape3D > convex = importer_mesh - > get_mesh ( ) - > create_convex_shape ( ) ;
_shape_cache = convex ;
} else if ( shape_type = = " trimesh " ) {
2023-06-16 22:01:35 +02:00
ERR_FAIL_COND_V_MSG ( importer_mesh . is_null ( ) , gltf_shape , " GLTFPhysicsShape: Error converting concave mesh shape to a node: The mesh resource is null. " ) ;
2022-11-22 16:29:56 +01:00
Ref < ConcavePolygonShape3D > concave = importer_mesh - > create_trimesh_shape ( ) ;
_shape_cache = concave ;
} else {
2023-06-16 22:01:35 +02:00
ERR_PRINT ( " GLTFPhysicsShape: Error converting to a node: Shape type ' " + shape_type + " ' is unknown. " ) ;
2022-11-22 16:29:56 +01:00
}
}
2023-06-16 22:01:35 +02:00
gltf_shape - > set_shape ( _shape_cache ) ;
return gltf_shape ;
2022-11-22 16:29:56 +01:00
}
2023-06-16 22:01:35 +02:00
Ref < GLTFPhysicsShape > GLTFPhysicsShape : : from_dictionary ( const Dictionary p_dictionary ) {
ERR_FAIL_COND_V_MSG ( ! p_dictionary . has ( " type " ) , Ref < GLTFPhysicsShape > ( ) , " Failed to parse GLTFPhysicsShape, missing required field 'type'. " ) ;
Ref < GLTFPhysicsShape > gltf_shape ;
gltf_shape . instantiate ( ) ;
2022-11-22 16:29:56 +01:00
const String & shape_type = p_dictionary [ " type " ] ;
2023-06-16 22:01:35 +02:00
gltf_shape - > shape_type = shape_type ;
2022-11-22 16:29:56 +01:00
if ( shape_type ! = " box " & & shape_type ! = " capsule " & & shape_type ! = " cylinder " & & shape_type ! = " sphere " & & shape_type ! = " hull " & & shape_type ! = " trimesh " ) {
2023-06-16 22:01:35 +02:00
ERR_PRINT ( " GLTFPhysicsShape: Error parsing unknown shape type ' " + shape_type + " '. Only box, capsule, cylinder, sphere, hull, and trimesh are supported. " ) ;
2022-11-22 16:29:56 +01:00
}
if ( p_dictionary . has ( " radius " ) ) {
2023-06-16 22:01:35 +02:00
gltf_shape - > set_radius ( p_dictionary [ " radius " ] ) ;
2022-11-22 16:29:56 +01:00
}
if ( p_dictionary . has ( " height " ) ) {
2023-06-16 22:01:35 +02:00
gltf_shape - > set_height ( p_dictionary [ " height " ] ) ;
2022-11-22 16:29:56 +01:00
}
if ( p_dictionary . has ( " size " ) ) {
const Array & arr = p_dictionary [ " size " ] ;
if ( arr . size ( ) = = 3 ) {
2023-06-16 22:01:35 +02:00
gltf_shape - > set_size ( Vector3 ( arr [ 0 ] , arr [ 1 ] , arr [ 2 ] ) ) ;
2022-11-22 16:29:56 +01:00
} else {
2023-06-16 22:01:35 +02:00
ERR_PRINT ( " GLTFPhysicsShape: Error parsing the size, it must have exactly 3 numbers. " ) ;
2022-11-22 16:29:56 +01:00
}
}
if ( p_dictionary . has ( " isTrigger " ) ) {
2023-06-16 22:01:35 +02:00
gltf_shape - > set_is_trigger ( p_dictionary [ " isTrigger " ] ) ;
2022-11-22 16:29:56 +01:00
}
if ( p_dictionary . has ( " mesh " ) ) {
2023-06-16 22:01:35 +02:00
gltf_shape - > set_mesh_index ( p_dictionary [ " mesh " ] ) ;
2022-11-22 16:29:56 +01:00
}
2023-06-16 22:01:35 +02:00
if ( unlikely ( gltf_shape - > get_mesh_index ( ) < 0 & & ( shape_type = = " hull " | | shape_type = = " trimesh " ) ) ) {
ERR_PRINT ( " Error parsing GLTFPhysicsShape: The mesh-based shape type ' " + shape_type + " ' does not have a valid mesh index. " ) ;
2022-11-22 16:29:56 +01:00
}
2023-06-16 22:01:35 +02:00
return gltf_shape ;
2022-11-22 16:29:56 +01:00
}
2023-06-16 22:01:35 +02:00
Dictionary GLTFPhysicsShape : : to_dictionary ( ) const {
2022-11-22 16:29:56 +01:00
Dictionary d ;
d [ " type " ] = shape_type ;
if ( shape_type = = " box " ) {
Array size_array ;
size_array . resize ( 3 ) ;
size_array [ 0 ] = size . x ;
size_array [ 1 ] = size . y ;
size_array [ 2 ] = size . z ;
d [ " size " ] = size_array ;
} else if ( shape_type = = " capsule " ) {
d [ " radius " ] = get_radius ( ) ;
d [ " height " ] = get_height ( ) ;
} else if ( shape_type = = " cylinder " ) {
d [ " radius " ] = get_radius ( ) ;
d [ " height " ] = get_height ( ) ;
} else if ( shape_type = = " sphere " ) {
d [ " radius " ] = get_radius ( ) ;
} else if ( shape_type = = " trimesh " | | shape_type = = " hull " ) {
d [ " mesh " ] = get_mesh_index ( ) ;
}
if ( is_trigger ) {
d [ " isTrigger " ] = is_trigger ;
}
return d ;
}