1/**************************************************************************/
2/* soft_body_3d.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 "soft_body_3d.h"
32
33#include "scene/3d/physics_body_3d.h"
34
35SoftBodyRenderingServerHandler::SoftBodyRenderingServerHandler() {}
36
37void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) {
38 clear();
39
40 ERR_FAIL_COND(!p_mesh.is_valid());
41
42 mesh = p_mesh;
43 surface = p_surface;
44
45 RS::SurfaceData surface_data = RS::get_singleton()->mesh_get_surface(mesh, surface);
46
47 uint32_t surface_offsets[RS::ARRAY_MAX];
48 uint32_t vertex_stride;
49 uint32_t attrib_stride;
50 uint32_t skin_stride;
51 RS::get_singleton()->mesh_surface_make_offsets_from_format(surface_data.format, surface_data.vertex_count, surface_data.index_count, surface_offsets, vertex_stride, attrib_stride, skin_stride);
52
53 buffer = surface_data.vertex_data;
54 stride = vertex_stride;
55 offset_vertices = surface_offsets[RS::ARRAY_VERTEX];
56 offset_normal = surface_offsets[RS::ARRAY_NORMAL];
57}
58
59void SoftBodyRenderingServerHandler::clear() {
60 buffer.resize(0);
61 stride = 0;
62 offset_vertices = 0;
63 offset_normal = 0;
64
65 surface = 0;
66 mesh = RID();
67}
68
69void SoftBodyRenderingServerHandler::open() {
70 write_buffer = buffer.ptrw();
71}
72
73void SoftBodyRenderingServerHandler::close() {
74 write_buffer = nullptr;
75}
76
77void SoftBodyRenderingServerHandler::commit_changes() {
78 RS::get_singleton()->mesh_surface_update_vertex_region(mesh, surface, 0, buffer);
79}
80
81void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) {
82 memcpy(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3);
83}
84
85void SoftBodyRenderingServerHandler::set_normal(int p_vertex_id, const void *p_vector3) {
86 // Store normal vector in A2B10G10R10 format.
87 Vector3 n;
88 memcpy(&n, p_vector3, sizeof(Vector3));
89 n *= Vector3(0.5, 0.5, 0.5);
90 n += Vector3(0.5, 0.5, 0.5);
91 Vector2 res = n.octahedron_encode();
92 uint32_t value = 0;
93 value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
94 value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
95 memcpy(&write_buffer[p_vertex_id * stride + offset_normal], &value, sizeof(uint32_t));
96}
97
98void SoftBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) {
99 RS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb);
100}
101
102SoftBody3D::PinnedPoint::PinnedPoint() {
103}
104
105SoftBody3D::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) {
106 point_index = obj_tocopy.point_index;
107 spatial_attachment_path = obj_tocopy.spatial_attachment_path;
108 spatial_attachment = obj_tocopy.spatial_attachment;
109 offset = obj_tocopy.offset;
110}
111
112void SoftBody3D::PinnedPoint::operator=(const PinnedPoint &obj) {
113 point_index = obj.point_index;
114 spatial_attachment_path = obj.spatial_attachment_path;
115 spatial_attachment = obj.spatial_attachment;
116 offset = obj.offset;
117}
118
119void SoftBody3D::_update_pickable() {
120 if (!is_inside_tree()) {
121 return;
122 }
123 bool pickable = ray_pickable && is_visible_in_tree();
124 PhysicsServer3D::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable);
125}
126
127bool SoftBody3D::_set(const StringName &p_name, const Variant &p_value) {
128 String name = p_name;
129 String which = name.get_slicec('/', 0);
130
131 if ("pinned_points" == which) {
132 return _set_property_pinned_points_indices(p_value);
133
134 } else if ("attachments" == which) {
135 int idx = name.get_slicec('/', 1).to_int();
136 String what = name.get_slicec('/', 2);
137
138 return _set_property_pinned_points_attachment(idx, what, p_value);
139 }
140
141 return false;
142}
143
144bool SoftBody3D::_get(const StringName &p_name, Variant &r_ret) const {
145 String name = p_name;
146 String which = name.get_slicec('/', 0);
147
148 if ("pinned_points" == which) {
149 Array arr_ret;
150 const int pinned_points_indices_size = pinned_points.size();
151 const PinnedPoint *r = pinned_points.ptr();
152 arr_ret.resize(pinned_points_indices_size);
153
154 for (int i = 0; i < pinned_points_indices_size; ++i) {
155 arr_ret[i] = r[i].point_index;
156 }
157
158 r_ret = arr_ret;
159 return true;
160
161 } else if ("attachments" == which) {
162 int idx = name.get_slicec('/', 1).to_int();
163 String what = name.get_slicec('/', 2);
164
165 return _get_property_pinned_points(idx, what, r_ret);
166 }
167
168 return false;
169}
170
171void SoftBody3D::_get_property_list(List<PropertyInfo> *p_list) const {
172 const int pinned_points_indices_size = pinned_points.size();
173
174 p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, PNAME("pinned_points")));
175
176 for (int i = 0; i < pinned_points_indices_size; ++i) {
177 const String prefix = vformat("%s/%d/", PNAME("attachments"), i);
178 p_list->push_back(PropertyInfo(Variant::INT, prefix + PNAME("point_index")));
179 p_list->push_back(PropertyInfo(Variant::NODE_PATH, prefix + PNAME("spatial_attachment_path")));
180 p_list->push_back(PropertyInfo(Variant::VECTOR3, prefix + PNAME("offset")));
181 }
182}
183
184bool SoftBody3D::_set_property_pinned_points_indices(const Array &p_indices) {
185 const int p_indices_size = p_indices.size();
186
187 { // Remove the pined points on physics server that will be removed by resize
188 const PinnedPoint *r = pinned_points.ptr();
189 if (p_indices_size < pinned_points.size()) {
190 for (int i = pinned_points.size() - 1; i >= p_indices_size; --i) {
191 pin_point(r[i].point_index, false);
192 }
193 }
194 }
195
196 pinned_points.resize(p_indices_size);
197
198 PinnedPoint *w = pinned_points.ptrw();
199 int point_index;
200 for (int i = 0; i < p_indices_size; ++i) {
201 point_index = p_indices.get(i);
202 if (w[i].point_index != point_index) {
203 if (-1 != w[i].point_index) {
204 pin_point(w[i].point_index, false);
205 }
206 w[i].point_index = point_index;
207 pin_point(w[i].point_index, true);
208 }
209 }
210 return true;
211}
212
213bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) {
214 if (pinned_points.size() <= p_item) {
215 return false;
216 }
217
218 if ("spatial_attachment_path" == p_what) {
219 PinnedPoint *w = pinned_points.ptrw();
220 callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value);
221 } else if ("offset" == p_what) {
222 PinnedPoint *w = pinned_points.ptrw();
223 w[p_item].offset = p_value;
224 } else {
225 return false;
226 }
227
228 return true;
229}
230
231bool SoftBody3D::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const {
232 if (pinned_points.size() <= p_item) {
233 return false;
234 }
235 const PinnedPoint *r = pinned_points.ptr();
236
237 if ("point_index" == p_what) {
238 r_ret = r[p_item].point_index;
239 } else if ("spatial_attachment_path" == p_what) {
240 r_ret = r[p_item].spatial_attachment_path;
241 } else if ("offset" == p_what) {
242 r_ret = r[p_item].offset;
243 } else {
244 return false;
245 }
246
247 return true;
248}
249
250void SoftBody3D::_notification(int p_what) {
251 switch (p_what) {
252 case NOTIFICATION_ENTER_WORLD: {
253 if (Engine::get_singleton()->is_editor_hint()) {
254 // I have no idea what this is supposed to do, it's really weird
255 // leaving for upcoming PK work on physics
256 //add_change_receptor(this);
257 }
258
259 RID space = get_world_3d()->get_space();
260 PhysicsServer3D::get_singleton()->soft_body_set_space(physics_rid, space);
261 _prepare_physics_server();
262 } break;
263
264 case NOTIFICATION_READY: {
265 if (!parent_collision_ignore.is_empty()) {
266 add_collision_exception_with(get_node(parent_collision_ignore));
267 }
268 } break;
269
270 case NOTIFICATION_TRANSFORM_CHANGED: {
271 if (Engine::get_singleton()->is_editor_hint()) {
272 _reset_points_offsets();
273 return;
274 }
275
276 PhysicsServer3D::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform());
277
278 set_notify_transform(false);
279 // Required to be top level with Transform at center of world in order to modify RenderingServer only to support custom Transform
280 set_as_top_level(true);
281 set_transform(Transform3D());
282 set_notify_transform(true);
283 } break;
284
285 case NOTIFICATION_VISIBILITY_CHANGED: {
286 _update_pickable();
287 } break;
288
289 case NOTIFICATION_EXIT_WORLD: {
290 PhysicsServer3D::get_singleton()->soft_body_set_space(physics_rid, RID());
291 } break;
292
293 case NOTIFICATION_DISABLED: {
294 if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) {
295 _prepare_physics_server();
296 }
297 } break;
298
299 case NOTIFICATION_ENABLED: {
300 if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) {
301 _prepare_physics_server();
302 }
303 } break;
304 }
305}
306
307void SoftBody3D::_bind_methods() {
308 ClassDB::bind_method(D_METHOD("get_physics_rid"), &SoftBody3D::get_physics_rid);
309
310 ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody3D::set_collision_mask);
311 ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody3D::get_collision_mask);
312
313 ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody3D::set_collision_layer);
314 ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody3D::get_collision_layer);
315
316 ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftBody3D::set_collision_mask_value);
317 ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftBody3D::get_collision_mask_value);
318
319 ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftBody3D::set_collision_layer_value);
320 ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftBody3D::get_collision_layer_value);
321
322 ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody3D::set_parent_collision_ignore);
323 ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody3D::get_parent_collision_ignore);
324
325 ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &SoftBody3D::set_disable_mode);
326 ClassDB::bind_method(D_METHOD("get_disable_mode"), &SoftBody3D::get_disable_mode);
327
328 ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftBody3D::get_collision_exceptions);
329 ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody3D::add_collision_exception_with);
330 ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody3D::remove_collision_exception_with);
331
332 ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody3D::set_simulation_precision);
333 ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody3D::get_simulation_precision);
334
335 ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody3D::set_total_mass);
336 ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody3D::get_total_mass);
337
338 ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody3D::set_linear_stiffness);
339 ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody3D::get_linear_stiffness);
340
341 ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody3D::set_pressure_coefficient);
342 ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody3D::get_pressure_coefficient);
343
344 ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody3D::set_damping_coefficient);
345 ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody3D::get_damping_coefficient);
346
347 ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody3D::set_drag_coefficient);
348 ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody3D::get_drag_coefficient);
349
350 ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform);
351
352 ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath()));
353 ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned);
354
355 ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable);
356 ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody3D::is_ray_pickable);
357
358 ADD_GROUP("Collision", "collision_");
359 ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
360 ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
361
362 ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject3D"), "set_parent_collision_ignore", "get_parent_collision_ignore");
363 ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision");
364 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass");
365 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness");
366 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient");
367 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient");
368 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient");
369
370 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ray_pickable"), "set_ray_pickable", "is_ray_pickable");
371
372 ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,KeepActive"), "set_disable_mode", "get_disable_mode");
373
374 BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE);
375 BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE);
376}
377
378PackedStringArray SoftBody3D::get_configuration_warnings() const {
379 PackedStringArray warnings = Node::get_configuration_warnings();
380
381 if (mesh.is_null()) {
382 warnings.push_back(RTR("This body will be ignored until you set a mesh."));
383 }
384
385 return warnings;
386}
387
388void SoftBody3D::_update_physics_server() {
389 if (!simulation_started) {
390 return;
391 }
392
393 _update_cache_pin_points_datas();
394 // Submit bone attachment
395 const int pinned_points_indices_size = pinned_points.size();
396 const PinnedPoint *r = pinned_points.ptr();
397 for (int i = 0; i < pinned_points_indices_size; ++i) {
398 if (r[i].spatial_attachment) {
399 PhysicsServer3D::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, r[i].spatial_attachment->get_global_transform().xform(r[i].offset));
400 }
401 }
402}
403
404void SoftBody3D::_draw_soft_mesh() {
405 if (mesh.is_null()) {
406 return;
407 }
408
409 RID mesh_rid = mesh->get_rid();
410 if (owned_mesh != mesh_rid) {
411 _become_mesh_owner();
412 mesh_rid = mesh->get_rid();
413 PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid);
414 }
415
416 if (!rendering_server_handler->is_ready(mesh_rid)) {
417 rendering_server_handler->prepare(mesh_rid, 0);
418
419 /// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
420 simulation_started = true;
421 call_deferred(SNAME("set_as_top_level"), true);
422 call_deferred(SNAME("set_transform"), Transform3D());
423 }
424
425 _update_physics_server();
426
427 rendering_server_handler->open();
428 PhysicsServer3D::get_singleton()->soft_body_update_rendering_server(physics_rid, rendering_server_handler);
429 rendering_server_handler->close();
430
431 rendering_server_handler->commit_changes();
432}
433
434void SoftBody3D::_prepare_physics_server() {
435#ifdef TOOLS_ENABLED
436 if (Engine::get_singleton()->is_editor_hint()) {
437 if (mesh.is_valid()) {
438 PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh->get_rid());
439 } else {
440 PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID());
441 }
442
443 return;
444 }
445#endif
446
447 if (mesh.is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) {
448 RID mesh_rid = mesh->get_rid();
449 if (owned_mesh != mesh_rid) {
450 _become_mesh_owner();
451 mesh_rid = mesh->get_rid();
452 }
453 PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid);
454 RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh));
455 } else {
456 PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID());
457 if (RS::get_singleton()->is_connected("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh))) {
458 RS::get_singleton()->disconnect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh));
459 }
460 }
461}
462
463void SoftBody3D::_become_mesh_owner() {
464 Vector<Ref<Material>> copy_materials;
465 copy_materials.append_array(surface_override_materials);
466
467 ERR_FAIL_COND(!mesh->get_surface_count());
468
469 // Get current mesh array and create new mesh array with necessary flag for SoftBody
470 Array surface_arrays = mesh->surface_get_arrays(0);
471 Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0);
472 Dictionary surface_lods = mesh->surface_get_lods(0);
473 uint32_t surface_format = mesh->surface_get_format(0);
474
475 surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
476
477 Ref<ArrayMesh> soft_mesh;
478 soft_mesh.instantiate();
479 soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_lods, surface_format);
480 soft_mesh->surface_set_material(0, mesh->surface_get_material(0));
481
482 set_mesh(soft_mesh);
483
484 for (int i = copy_materials.size() - 1; 0 <= i; --i) {
485 set_surface_override_material(i, copy_materials[i]);
486 }
487
488 owned_mesh = soft_mesh->get_rid();
489}
490
491void SoftBody3D::set_collision_mask(uint32_t p_mask) {
492 collision_mask = p_mask;
493 PhysicsServer3D::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask);
494}
495
496uint32_t SoftBody3D::get_collision_mask() const {
497 return collision_mask;
498}
499
500void SoftBody3D::set_collision_layer(uint32_t p_layer) {
501 collision_layer = p_layer;
502 PhysicsServer3D::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer);
503}
504
505uint32_t SoftBody3D::get_collision_layer() const {
506 return collision_layer;
507}
508
509void SoftBody3D::set_collision_layer_value(int p_layer_number, bool p_value) {
510 ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
511 ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
512 uint32_t collision_layer_new = get_collision_layer();
513 if (p_value) {
514 collision_layer_new |= 1 << (p_layer_number - 1);
515 } else {
516 collision_layer_new &= ~(1 << (p_layer_number - 1));
517 }
518 set_collision_layer(collision_layer_new);
519}
520
521bool SoftBody3D::get_collision_layer_value(int p_layer_number) const {
522 ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
523 ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
524 return get_collision_layer() & (1 << (p_layer_number - 1));
525}
526
527void SoftBody3D::set_collision_mask_value(int p_layer_number, bool p_value) {
528 ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
529 ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
530 uint32_t mask = get_collision_mask();
531 if (p_value) {
532 mask |= 1 << (p_layer_number - 1);
533 } else {
534 mask &= ~(1 << (p_layer_number - 1));
535 }
536 set_collision_mask(mask);
537}
538
539bool SoftBody3D::get_collision_mask_value(int p_layer_number) const {
540 ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
541 ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
542 return get_collision_mask() & (1 << (p_layer_number - 1));
543}
544
545void SoftBody3D::set_disable_mode(DisableMode p_mode) {
546 if (disable_mode == p_mode) {
547 return;
548 }
549
550 disable_mode = p_mode;
551
552 if (mesh.is_valid() && is_inside_tree() && !is_enabled()) {
553 _prepare_physics_server();
554 }
555}
556
557SoftBody3D::DisableMode SoftBody3D::get_disable_mode() const {
558 return disable_mode;
559}
560
561void SoftBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) {
562 parent_collision_ignore = p_parent_collision_ignore;
563}
564
565const NodePath &SoftBody3D::get_parent_collision_ignore() const {
566 return parent_collision_ignore;
567}
568
569void SoftBody3D::set_pinned_points_indices(Vector<SoftBody3D::PinnedPoint> p_pinned_points_indices) {
570 pinned_points = p_pinned_points_indices;
571 for (int i = pinned_points.size() - 1; 0 <= i; --i) {
572 pin_point(p_pinned_points_indices[i].point_index, true);
573 }
574}
575
576Vector<SoftBody3D::PinnedPoint> SoftBody3D::get_pinned_points_indices() {
577 return pinned_points;
578}
579
580TypedArray<PhysicsBody3D> SoftBody3D::get_collision_exceptions() {
581 List<RID> exceptions;
582 PhysicsServer3D::get_singleton()->soft_body_get_collision_exceptions(physics_rid, &exceptions);
583 TypedArray<PhysicsBody3D> ret;
584 for (const RID &body : exceptions) {
585 ObjectID instance_id = PhysicsServer3D::get_singleton()->body_get_object_instance_id(body);
586 Object *obj = ObjectDB::get_instance(instance_id);
587 PhysicsBody3D *physics_body = Object::cast_to<PhysicsBody3D>(obj);
588 ret.append(physics_body);
589 }
590 return ret;
591}
592
593void SoftBody3D::add_collision_exception_with(Node *p_node) {
594 ERR_FAIL_NULL(p_node);
595 CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node);
596 ERR_FAIL_NULL_MSG(collision_object, "Collision exception only works between two nodes that inherit from CollisionObject3D (such as Area3D or PhysicsBody3D).");
597 PhysicsServer3D::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid());
598}
599
600void SoftBody3D::remove_collision_exception_with(Node *p_node) {
601 ERR_FAIL_NULL(p_node);
602 CollisionObject3D *collision_object = Object::cast_to<CollisionObject3D>(p_node);
603 ERR_FAIL_NULL_MSG(collision_object, "Collision exception only works between two nodes that inherit from CollisionObject3D (such as Area3D or PhysicsBody3D).");
604 PhysicsServer3D::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid());
605}
606
607int SoftBody3D::get_simulation_precision() {
608 return PhysicsServer3D::get_singleton()->soft_body_get_simulation_precision(physics_rid);
609}
610
611void SoftBody3D::set_simulation_precision(int p_simulation_precision) {
612 PhysicsServer3D::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision);
613}
614
615real_t SoftBody3D::get_total_mass() {
616 return PhysicsServer3D::get_singleton()->soft_body_get_total_mass(physics_rid);
617}
618
619void SoftBody3D::set_total_mass(real_t p_total_mass) {
620 PhysicsServer3D::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass);
621}
622
623void SoftBody3D::set_linear_stiffness(real_t p_linear_stiffness) {
624 PhysicsServer3D::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness);
625}
626
627real_t SoftBody3D::get_linear_stiffness() {
628 return PhysicsServer3D::get_singleton()->soft_body_get_linear_stiffness(physics_rid);
629}
630
631real_t SoftBody3D::get_pressure_coefficient() {
632 return PhysicsServer3D::get_singleton()->soft_body_get_pressure_coefficient(physics_rid);
633}
634
635void SoftBody3D::set_pressure_coefficient(real_t p_pressure_coefficient) {
636 PhysicsServer3D::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient);
637}
638
639real_t SoftBody3D::get_damping_coefficient() {
640 return PhysicsServer3D::get_singleton()->soft_body_get_damping_coefficient(physics_rid);
641}
642
643void SoftBody3D::set_damping_coefficient(real_t p_damping_coefficient) {
644 PhysicsServer3D::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient);
645}
646
647real_t SoftBody3D::get_drag_coefficient() {
648 return PhysicsServer3D::get_singleton()->soft_body_get_drag_coefficient(physics_rid);
649}
650
651void SoftBody3D::set_drag_coefficient(real_t p_drag_coefficient) {
652 PhysicsServer3D::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient);
653}
654
655Vector3 SoftBody3D::get_point_transform(int p_point_index) {
656 return PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index);
657}
658
659void SoftBody3D::pin_point_toggle(int p_point_index) {
660 pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index)));
661}
662
663void SoftBody3D::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) {
664 _pin_point_on_physics_server(p_point_index, pin);
665 if (pin) {
666 _add_pinned_point(p_point_index, p_spatial_attachment_path);
667 } else {
668 _remove_pinned_point(p_point_index);
669 }
670}
671
672void SoftBody3D::_pin_point_deferred(int p_point_index, bool pin, const NodePath p_spatial_attachment_path) {
673 pin_point(p_point_index, pin, p_spatial_attachment_path);
674 _make_cache_dirty();
675}
676
677bool SoftBody3D::is_point_pinned(int p_point_index) const {
678 return -1 != _has_pinned_point(p_point_index);
679}
680
681void SoftBody3D::set_ray_pickable(bool p_ray_pickable) {
682 ray_pickable = p_ray_pickable;
683 _update_pickable();
684}
685
686bool SoftBody3D::is_ray_pickable() const {
687 return ray_pickable;
688}
689
690SoftBody3D::SoftBody3D() :
691 physics_rid(PhysicsServer3D::get_singleton()->soft_body_create()) {
692 rendering_server_handler = memnew(SoftBodyRenderingServerHandler);
693 PhysicsServer3D::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id());
694}
695
696SoftBody3D::~SoftBody3D() {
697 memdelete(rendering_server_handler);
698 ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
699 PhysicsServer3D::get_singleton()->free(physics_rid);
700}
701
702void SoftBody3D::_make_cache_dirty() {
703 pinned_points_cache_dirty = true;
704}
705
706void SoftBody3D::_update_cache_pin_points_datas() {
707 if (!pinned_points_cache_dirty) {
708 return;
709 }
710
711 pinned_points_cache_dirty = false;
712
713 PinnedPoint *w = pinned_points.ptrw();
714 for (int i = pinned_points.size() - 1; 0 <= i; --i) {
715 if (!w[i].spatial_attachment_path.is_empty()) {
716 w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(w[i].spatial_attachment_path));
717 }
718 if (!w[i].spatial_attachment) {
719 ERR_PRINT("Node3D node not defined in the pinned point, this is undefined behavior for SoftBody3D!");
720 }
721 }
722}
723
724void SoftBody3D::_pin_point_on_physics_server(int p_point_index, bool pin) {
725 PhysicsServer3D::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin);
726}
727
728void SoftBody3D::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) {
729 SoftBody3D::PinnedPoint *pinned_point;
730 if (-1 == _get_pinned_point(p_point_index, pinned_point)) {
731 // Create new
732 PinnedPoint pp;
733 pp.point_index = p_point_index;
734 pp.spatial_attachment_path = p_spatial_attachment_path;
735
736 if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) {
737 pp.spatial_attachment = Object::cast_to<Node3D>(get_node(p_spatial_attachment_path));
738 pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index));
739 }
740
741 pinned_points.push_back(pp);
742
743 } else {
744 pinned_point->point_index = p_point_index;
745 pinned_point->spatial_attachment_path = p_spatial_attachment_path;
746
747 if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) {
748 Node3D *attachment_node = Object::cast_to<Node3D>(get_node(p_spatial_attachment_path));
749
750 ERR_FAIL_NULL_MSG(attachment_node, "Attachment node path is invalid.");
751
752 pinned_point->spatial_attachment = attachment_node;
753 pinned_point->offset = (pinned_point->spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, pinned_point->point_index));
754 }
755 }
756}
757
758void SoftBody3D::_reset_points_offsets() {
759 if (!Engine::get_singleton()->is_editor_hint()) {
760 return;
761 }
762
763 const PinnedPoint *r = pinned_points.ptr();
764 PinnedPoint *w = pinned_points.ptrw();
765 for (int i = pinned_points.size() - 1; 0 <= i; --i) {
766 if (!r[i].spatial_attachment) {
767 if (!r[i].spatial_attachment_path.is_empty() && has_node(r[i].spatial_attachment_path)) {
768 w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(r[i].spatial_attachment_path));
769 }
770 }
771
772 if (!r[i].spatial_attachment) {
773 continue;
774 }
775
776 w[i].offset = (r[i].spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, r[i].point_index));
777 }
778}
779
780void SoftBody3D::_remove_pinned_point(int p_point_index) {
781 const int id(_has_pinned_point(p_point_index));
782 if (-1 != id) {
783 pinned_points.remove_at(id);
784 }
785}
786
787int SoftBody3D::_get_pinned_point(int p_point_index, SoftBody3D::PinnedPoint *&r_point) const {
788 const int id = _has_pinned_point(p_point_index);
789 if (-1 == id) {
790 r_point = nullptr;
791 return -1;
792 } else {
793 r_point = const_cast<SoftBody3D::PinnedPoint *>(&pinned_points.ptr()[id]);
794 return id;
795 }
796}
797
798int SoftBody3D::_has_pinned_point(int p_point_index) const {
799 const PinnedPoint *r = pinned_points.ptr();
800 for (int i = pinned_points.size() - 1; 0 <= i; --i) {
801 if (p_point_index == r[i].point_index) {
802 return i;
803 }
804 }
805 return -1;
806}
807