2017-11-04 20:52:59 +01:00
/*************************************************************************/
/* soft_body_bullet.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2018-01-05 00:50:27 +01:00
/* https://godotengine.org */
2017-11-04 20:52:59 +01:00
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2017-11-04 20:52:59 +01:00
/* */
/* 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. */
/*************************************************************************/
# include "soft_body_bullet.h"
2018-01-05 00:50:27 +01:00
2017-11-04 20:52:59 +01:00
# include "bullet_types_converter.h"
# include "bullet_utilities.h"
# include "space_bullet.h"
2021-10-01 17:42:47 +02:00
# include "servers/rendering_server.h"
2017-12-06 21:36:34 +01:00
SoftBodyBullet : : SoftBodyBullet ( ) :
2020-05-12 17:01:17 +02:00
CollisionObjectBullet ( CollisionObjectBullet : : TYPE_SOFT_BODY ) { }
2017-11-04 20:52:59 +01:00
SoftBodyBullet : : ~ SoftBodyBullet ( ) {
}
2020-10-08 12:22:59 +02:00
void SoftBodyBullet : : reload_body ( ) {
2017-11-04 20:52:59 +01:00
if ( space ) {
space - > remove_soft_body ( this ) ;
space - > add_soft_body ( this ) ;
}
}
void SoftBodyBullet : : set_space ( SpaceBullet * p_space ) {
if ( space ) {
isScratched = false ;
space - > remove_soft_body ( this ) ;
}
space = p_space ;
if ( space ) {
2020-10-08 12:22:59 +02:00
space - > add_soft_body ( this ) ;
2017-11-04 20:52:59 +01:00
}
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : on_enter_area ( AreaBullet * p_area ) { }
void SoftBodyBullet : : on_exit_area ( AreaBullet * p_area ) { }
2021-03-12 04:33:46 +01:00
void SoftBodyBullet : : update_rendering_server ( RenderingServerHandler * p_rendering_server_handler ) {
2020-05-14 16:41:43 +02:00
if ( ! bt_soft_body ) {
2017-11-04 20:52:59 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-11-21 01:36:32 +01:00
2021-10-07 15:46:55 +02:00
/// Update rendering server vertices
2017-11-21 01:36:32 +01:00
const btSoftBody : : tNodeArray & nodes ( bt_soft_body - > m_nodes ) ;
const int nodes_count = nodes . size ( ) ;
2018-07-25 03:11:03 +02:00
const Vector < int > * vs_indices ;
2017-11-21 01:36:32 +01:00
const void * vertex_position ;
const void * vertex_normal ;
for ( int vertex_index = 0 ; vertex_index < nodes_count ; + + vertex_index ) {
vertex_position = reinterpret_cast < const void * > ( & nodes [ vertex_index ] . m_x ) ;
vertex_normal = reinterpret_cast < const void * > ( & nodes [ vertex_index ] . m_n ) ;
vs_indices = & indices_table [ vertex_index ] ;
const int vs_indices_size ( vs_indices - > size ( ) ) ;
for ( int x = 0 ; x < vs_indices_size ; + + x ) {
2020-03-27 19:21:27 +01:00
p_rendering_server_handler - > set_vertex ( ( * vs_indices ) [ x ] , vertex_position ) ;
p_rendering_server_handler - > set_normal ( ( * vs_indices ) [ x ] , vertex_normal ) ;
2017-11-21 01:36:32 +01:00
}
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
/// Generate AABB
btVector3 aabb_min ;
btVector3 aabb_max ;
bt_soft_body - > getAabb ( aabb_min , aabb_max ) ;
btVector3 size ( aabb_max - aabb_min ) ;
AABB aabb ;
B_TO_G ( aabb_min , aabb . position ) ;
B_TO_G ( size , aabb . size ) ;
2020-03-27 19:21:27 +01:00
p_rendering_server_handler - > set_aabb ( aabb ) ;
2017-11-21 01:36:32 +01:00
}
2021-10-01 17:42:47 +02:00
void SoftBodyBullet : : set_soft_mesh ( RID p_mesh ) {
destroy_soft_body ( ) ;
soft_mesh = p_mesh ;
2017-11-21 01:36:32 +01:00
if ( soft_mesh . is_null ( ) ) {
return ;
2017-11-04 20:52:59 +01:00
}
2021-10-01 17:42:47 +02:00
Array arrays = RenderingServer : : get_singleton ( ) - > mesh_surface_get_arrays ( soft_mesh , 0 ) ;
2021-10-21 03:59:17 +02:00
ERR_FAIL_COND ( arrays . is_empty ( ) ) ;
2021-10-01 17:42:47 +02:00
bool success = set_trimesh_body_shape ( arrays [ RS : : ARRAY_INDEX ] , arrays [ RS : : ARRAY_VERTEX ] ) ;
if ( ! success ) {
destroy_soft_body ( ) ;
}
2017-11-21 01:36:32 +01:00
}
void SoftBodyBullet : : destroy_soft_body ( ) {
2021-10-01 17:42:47 +02:00
soft_mesh = RID ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! bt_soft_body ) {
2017-11-21 01:36:32 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-11-21 01:36:32 +01:00
if ( space ) {
/// Remove from world before deletion
space - > remove_soft_body ( this ) ;
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
destroyBulletCollisionObject ( ) ;
2020-04-02 01:20:12 +02:00
bt_soft_body = nullptr ;
2017-11-21 01:36:32 +01:00
}
2020-10-17 07:08:21 +02:00
void SoftBodyBullet : : set_soft_transform ( const Transform3D & p_transform ) {
2017-11-21 01:36:32 +01:00
reset_all_node_positions ( ) ;
move_all_nodes ( p_transform ) ;
2017-11-04 20:52:59 +01:00
}
2021-03-12 04:33:46 +01:00
AABB SoftBodyBullet : : get_bounds ( ) const {
if ( ! bt_soft_body ) {
return AABB ( ) ;
}
btVector3 aabb_min ;
btVector3 aabb_max ;
bt_soft_body - > getAabb ( aabb_min , aabb_max ) ;
btVector3 size ( aabb_max - aabb_min ) ;
AABB aabb ;
B_TO_G ( aabb_min , aabb . position ) ;
B_TO_G ( size , aabb . size ) ;
return aabb ;
}
2020-10-17 07:08:21 +02:00
void SoftBodyBullet : : move_all_nodes ( const Transform3D & p_transform ) {
2020-05-14 16:41:43 +02:00
if ( ! bt_soft_body ) {
2017-11-21 01:36:32 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-11-21 01:36:32 +01:00
btTransform bt_transf ;
G_TO_B ( p_transform , bt_transf ) ;
bt_soft_body - > transform ( bt_transf ) ;
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : set_node_position ( int p_node_index , const Vector3 & p_global_position ) {
btVector3 bt_pos ;
G_TO_B ( p_global_position , bt_pos ) ;
set_node_position ( p_node_index , bt_pos ) ;
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : set_node_position ( int p_node_index , const btVector3 & p_global_position ) {
if ( bt_soft_body ) {
2018-08-30 17:45:27 +02:00
bt_soft_body - > m_nodes [ p_node_index ] . m_q = bt_soft_body - > m_nodes [ p_node_index ] . m_x ;
2017-11-21 01:36:32 +01:00
bt_soft_body - > m_nodes [ p_node_index ] . m_x = p_global_position ;
}
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : get_node_position ( int p_node_index , Vector3 & r_position ) const {
if ( bt_soft_body ) {
B_TO_G ( bt_soft_body - > m_nodes [ p_node_index ] . m_x , r_position ) ;
}
2017-11-04 20:52:59 +01:00
}
2021-10-04 10:12:52 +02:00
void SoftBodyBullet : : set_node_mass ( int p_node_index , btScalar p_mass ) {
2017-11-21 01:36:32 +01:00
if ( 0 > = p_mass ) {
2021-10-04 10:12:52 +02:00
pin_node ( p_node_index ) ;
2017-11-21 01:36:32 +01:00
} else {
2021-10-04 10:12:52 +02:00
unpin_node ( p_node_index ) ;
2017-11-21 01:36:32 +01:00
}
if ( bt_soft_body ) {
2021-10-04 10:12:52 +02:00
ERR_FAIL_INDEX ( p_node_index , bt_soft_body - > m_nodes . size ( ) ) ;
bt_soft_body - > setMass ( p_node_index , p_mass ) ;
2017-11-21 01:36:32 +01:00
}
2017-11-04 20:52:59 +01:00
}
2021-10-04 10:12:52 +02:00
btScalar SoftBodyBullet : : get_node_mass ( int p_node_index ) const {
2017-11-04 20:52:59 +01:00
if ( bt_soft_body ) {
2021-10-04 10:12:52 +02:00
ERR_FAIL_INDEX_V ( p_node_index , bt_soft_body - > m_nodes . size ( ) , 1 ) ;
return bt_soft_body - > getMass ( p_node_index ) ;
2017-11-21 01:36:32 +01:00
} else {
2021-10-04 10:12:52 +02:00
return - 1 = = search_node_pinned ( p_node_index ) ? 1 : 0 ;
2017-11-04 20:52:59 +01:00
}
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : reset_all_node_mass ( ) {
if ( bt_soft_body ) {
for ( int i = pinned_nodes . size ( ) - 1 ; 0 < = i ; - - i ) {
bt_soft_body - > setMass ( pinned_nodes [ i ] , 1 ) ;
}
}
pinned_nodes . resize ( 0 ) ;
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : reset_all_node_positions ( ) {
2020-05-14 16:41:43 +02:00
if ( soft_mesh . is_null ( ) ) {
2017-11-21 01:36:32 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-11-21 01:36:32 +01:00
Array arrays = soft_mesh - > surface_get_arrays ( 0 ) ;
2020-03-27 19:21:27 +01:00
Vector < Vector3 > vs_vertices ( arrays [ RS : : ARRAY_VERTEX ] ) ;
2020-02-17 22:06:54 +01:00
const Vector3 * vs_vertices_read = vs_vertices . ptr ( ) ;
2017-11-21 01:36:32 +01:00
for ( int vertex_index = bt_soft_body - > m_nodes . size ( ) - 1 ; 0 < = vertex_index ; - - vertex_index ) {
G_TO_B ( vs_vertices_read [ indices_table [ vertex_index ] [ 0 ] ] , bt_soft_body - > m_nodes [ vertex_index ] . m_x ) ;
bt_soft_body - > m_nodes [ vertex_index ] . m_q = bt_soft_body - > m_nodes [ vertex_index ] . m_x ;
bt_soft_body - > m_nodes [ vertex_index ] . m_v = btVector3 ( 0 , 0 , 0 ) ;
bt_soft_body - > m_nodes [ vertex_index ] . m_f = btVector3 ( 0 , 0 , 0 ) ;
2017-11-04 20:52:59 +01:00
}
}
void SoftBodyBullet : : set_activation_state ( bool p_active ) {
if ( p_active ) {
bt_soft_body - > setActivationState ( ACTIVE_TAG ) ;
} else {
bt_soft_body - > setActivationState ( WANTS_DEACTIVATION ) ;
}
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : set_total_mass ( real_t p_val ) {
2017-11-04 20:52:59 +01:00
if ( 0 > = p_val ) {
p_val = 1 ;
}
2017-11-21 01:36:32 +01:00
total_mass = p_val ;
2017-11-04 20:52:59 +01:00
if ( bt_soft_body ) {
2017-11-21 01:36:32 +01:00
bt_soft_body - > setTotalMass ( total_mass ) ;
2017-11-04 20:52:59 +01:00
}
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : set_linear_stiffness ( real_t p_val ) {
linear_stiffness = p_val ;
2017-11-04 20:52:59 +01:00
if ( bt_soft_body ) {
2017-11-21 01:36:32 +01:00
mat0 - > m_kLST = linear_stiffness ;
}
}
2017-11-04 20:52:59 +01:00
void SoftBodyBullet : : set_simulation_precision ( int p_val ) {
simulation_precision = p_val ;
if ( bt_soft_body ) {
bt_soft_body - > m_cfg . piterations = simulation_precision ;
2017-11-21 01:36:32 +01:00
bt_soft_body - > m_cfg . viterations = simulation_precision ;
bt_soft_body - > m_cfg . diterations = simulation_precision ;
bt_soft_body - > m_cfg . citerations = simulation_precision ;
2017-11-04 20:52:59 +01:00
}
}
void SoftBodyBullet : : set_pressure_coefficient ( real_t p_val ) {
pressure_coefficient = p_val ;
if ( bt_soft_body ) {
bt_soft_body - > m_cfg . kPR = pressure_coefficient ;
}
}
void SoftBodyBullet : : set_damping_coefficient ( real_t p_val ) {
damping_coefficient = p_val ;
if ( bt_soft_body ) {
bt_soft_body - > m_cfg . kDP = damping_coefficient ;
}
}
void SoftBodyBullet : : set_drag_coefficient ( real_t p_val ) {
drag_coefficient = p_val ;
if ( bt_soft_body ) {
bt_soft_body - > m_cfg . kDG = drag_coefficient ;
}
}
2021-10-01 17:42:47 +02:00
bool SoftBodyBullet : : set_trimesh_body_shape ( Vector < int > p_indices , Vector < Vector3 > p_vertices ) {
ERR_FAIL_COND_V ( p_indices . is_empty ( ) , false ) ;
ERR_FAIL_COND_V ( p_vertices . is_empty ( ) , false ) ;
2017-11-04 20:52:59 +01:00
2021-10-07 15:46:55 +02:00
/// Parse rendering server indices to physical indices.
/// Merge all overlapping vertices and create a map of physical vertices to rendering server
2017-11-04 20:52:59 +01:00
2017-11-21 01:36:32 +01:00
{
2021-10-07 15:46:55 +02:00
/// This is the map of rendering server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map
2017-11-21 01:36:32 +01:00
Vector < int > vs_indices_to_physics_table ;
2017-11-04 20:52:59 +01:00
2017-11-21 01:36:32 +01:00
{ // Map vertices
indices_table . resize ( 0 ) ;
2017-11-04 20:52:59 +01:00
2017-11-21 01:36:32 +01:00
int index = 0 ;
Map < Vector3 , int > unique_vertices ;
2017-11-04 20:52:59 +01:00
2017-11-21 01:36:32 +01:00
const int vs_vertices_size ( p_vertices . size ( ) ) ;
2020-02-17 22:06:54 +01:00
const Vector3 * p_vertices_read = p_vertices . ptr ( ) ;
2017-11-21 01:36:32 +01:00
for ( int vs_vertex_index = 0 ; vs_vertex_index < vs_vertices_size ; + + vs_vertex_index ) {
Map < Vector3 , int > : : Element * e = unique_vertices . find ( p_vertices_read [ vs_vertex_index ] ) ;
int vertex_id ;
if ( e ) {
2021-03-12 14:35:16 +01:00
// Already existing
2017-11-21 01:36:32 +01:00
vertex_id = e - > value ( ) ;
} else {
// Create new one
unique_vertices [ p_vertices_read [ vs_vertex_index ] ] = vertex_id = index + + ;
indices_table . push_back ( Vector < int > ( ) ) ;
}
2020-10-08 12:16:12 +02:00
indices_table . write [ vertex_id ] . push_back ( vs_vertex_index ) ;
2017-11-21 01:36:32 +01:00
vs_indices_to_physics_table . push_back ( vertex_id ) ;
}
}
const int indices_map_size ( indices_table . size ( ) ) ;
2020-10-08 12:16:12 +02:00
Vector < btScalar > bt_vertices ;
2017-11-21 01:36:32 +01:00
{ // Parse vertices to bullet
bt_vertices . resize ( indices_map_size * 3 ) ;
2020-02-17 22:06:54 +01:00
const Vector3 * p_vertices_read = p_vertices . ptr ( ) ;
2017-11-21 01:36:32 +01:00
for ( int i = 0 ; i < indices_map_size ; + + i ) {
2020-10-08 12:16:12 +02:00
bt_vertices . write [ 3 * i + 0 ] = p_vertices_read [ indices_table [ i ] [ 0 ] ] . x ;
bt_vertices . write [ 3 * i + 1 ] = p_vertices_read [ indices_table [ i ] [ 0 ] ] . y ;
bt_vertices . write [ 3 * i + 2 ] = p_vertices_read [ indices_table [ i ] [ 0 ] ] . z ;
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
}
2020-10-08 12:16:12 +02:00
Vector < int > bt_triangles ;
2017-11-21 01:36:32 +01:00
const int triangles_size ( p_indices . size ( ) / 3 ) ;
{ // Parse indices
bt_triangles . resize ( triangles_size * 3 ) ;
2020-02-17 22:06:54 +01:00
const int * p_indices_read = p_indices . ptr ( ) ;
2017-11-04 20:52:59 +01:00
2017-11-21 01:36:32 +01:00
for ( int i = 0 ; i < triangles_size ; + + i ) {
2020-10-08 12:16:12 +02:00
bt_triangles . write [ 3 * i + 0 ] = vs_indices_to_physics_table [ p_indices_read [ 3 * i + 2 ] ] ;
bt_triangles . write [ 3 * i + 1 ] = vs_indices_to_physics_table [ p_indices_read [ 3 * i + 1 ] ] ;
bt_triangles . write [ 3 * i + 2 ] = vs_indices_to_physics_table [ p_indices_read [ 3 * i + 0 ] ] ;
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
}
2017-11-04 20:52:59 +01:00
2017-11-21 01:36:32 +01:00
btSoftBodyWorldInfo fake_world_info ;
bt_soft_body = btSoftBodyHelpers : : CreateFromTriMesh ( fake_world_info , & bt_vertices [ 0 ] , & bt_triangles [ 0 ] , triangles_size , false ) ;
setup_soft_body ( ) ;
2017-11-04 20:52:59 +01:00
}
2021-10-01 17:42:47 +02:00
return true ;
2017-11-21 01:36:32 +01:00
}
void SoftBodyBullet : : setup_soft_body ( ) {
2020-05-14 16:41:43 +02:00
if ( ! bt_soft_body ) {
2017-11-21 01:36:32 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-11-04 20:52:59 +01:00
2017-11-21 01:36:32 +01:00
// Soft body setup
2017-11-04 20:52:59 +01:00
setupBulletCollisionObject ( bt_soft_body ) ;
2020-04-02 01:20:12 +02:00
bt_soft_body - > m_worldInfo = nullptr ; // Remove fake world info
2017-11-21 01:36:32 +01:00
bt_soft_body - > getCollisionShape ( ) - > setMargin ( 0.01 ) ;
2017-11-04 20:52:59 +01:00
bt_soft_body - > setCollisionFlags ( bt_soft_body - > getCollisionFlags ( ) & ( ~ ( btCollisionObject : : CF_KINEMATIC_OBJECT | btCollisionObject : : CF_STATIC_OBJECT ) ) ) ;
2017-11-21 01:36:32 +01:00
// Space setup
if ( space ) {
space - > add_soft_body ( this ) ;
}
2017-11-04 20:52:59 +01:00
mat0 = bt_soft_body - > appendMaterial ( ) ;
2017-11-21 01:36:32 +01:00
// Assign soft body data
bt_soft_body - > generateBendingConstraints ( 2 , mat0 ) ;
mat0 - > m_kLST = linear_stiffness ;
// Clusters allow to have Soft vs Soft collision but doesn't work well right now
//bt_soft_body->m_cfg.kSRHR_CL = 1;// Soft vs rigid hardness [0,1] (cluster only)
//bt_soft_body->m_cfg.kSKHR_CL = 1;// Soft vs kinematic hardness [0,1] (cluster only)
//bt_soft_body->m_cfg.kSSHR_CL = 1;// Soft vs soft hardness [0,1] (cluster only)
//bt_soft_body->m_cfg.kSR_SPLT_CL = 1; // Soft vs rigid impulse split [0,1] (cluster only)
//bt_soft_body->m_cfg.kSK_SPLT_CL = 1; // Soft vs kinematic impulse split [0,1] (cluster only)
//bt_soft_body->m_cfg.kSS_SPLT_CL = 1; // Soft vs Soft impulse split [0,1] (cluster only)
//bt_soft_body->m_cfg.collisions = btSoftBody::fCollision::CL_SS + btSoftBody::fCollision::CL_RS + btSoftBody::fCollision::VF_SS;
//bt_soft_body->generateClusters(64);
bt_soft_body - > m_cfg . piterations = simulation_precision ;
bt_soft_body - > m_cfg . viterations = simulation_precision ;
bt_soft_body - > m_cfg . diterations = simulation_precision ;
bt_soft_body - > m_cfg . citerations = simulation_precision ;
bt_soft_body - > m_cfg . kDP = damping_coefficient ;
bt_soft_body - > m_cfg . kDG = drag_coefficient ;
bt_soft_body - > m_cfg . kPR = pressure_coefficient ;
bt_soft_body - > setTotalMass ( total_mass ) ;
btSoftBodyHelpers : : ReoptimizeLinkOrder ( bt_soft_body ) ;
bt_soft_body - > updateBounds ( ) ;
// Set pinned nodes
for ( int i = pinned_nodes . size ( ) - 1 ; 0 < = i ; - - i ) {
2021-10-04 10:12:52 +02:00
const int node_index = pinned_nodes [ i ] ;
ERR_CONTINUE ( 0 > node_index | | bt_soft_body - > m_nodes . size ( ) < = node_index ) ;
bt_soft_body - > setMass ( node_index , 0 ) ;
2017-11-21 01:36:32 +01:00
}
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
void SoftBodyBullet : : pin_node ( int p_node_index ) {
2021-10-04 10:12:52 +02:00
if ( bt_soft_body ) {
ERR_FAIL_INDEX ( p_node_index , bt_soft_body - > m_nodes . size ( ) ) ;
}
2017-11-21 01:36:32 +01:00
if ( - 1 = = search_node_pinned ( p_node_index ) ) {
pinned_nodes . push_back ( p_node_index ) ;
2017-11-04 20:52:59 +01:00
}
2017-11-21 01:36:32 +01:00
}
void SoftBodyBullet : : unpin_node ( int p_node_index ) {
2021-10-04 10:12:52 +02:00
if ( bt_soft_body ) {
ERR_FAIL_INDEX ( p_node_index , bt_soft_body - > m_nodes . size ( ) ) ;
}
2017-11-21 01:36:32 +01:00
const int id = search_node_pinned ( p_node_index ) ;
if ( - 1 ! = id ) {
2021-07-04 00:17:03 +02:00
pinned_nodes . remove_at ( id ) ;
2017-11-21 01:36:32 +01:00
}
}
int SoftBodyBullet : : search_node_pinned ( int p_node_index ) const {
for ( int i = pinned_nodes . size ( ) - 1 ; 0 < = i ; - - i ) {
if ( p_node_index = = pinned_nodes [ i ] ) {
return i ;
}
}
return - 1 ;
2017-11-04 20:52:59 +01:00
}