Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/jolt_physics/objects/jolt_soft_body_3d.cpp
10278 views
1
/**************************************************************************/
2
/* jolt_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 "jolt_soft_body_3d.h"
32
33
#include "../jolt_project_settings.h"
34
#include "../misc/jolt_type_conversions.h"
35
#include "../spaces/jolt_broad_phase_layer.h"
36
#include "../spaces/jolt_space_3d.h"
37
#include "jolt_area_3d.h"
38
#include "jolt_body_3d.h"
39
#include "jolt_group_filter.h"
40
41
#include "servers/rendering_server.h"
42
43
#include "Jolt/Physics/SoftBody/SoftBodyMotionProperties.h"
44
45
namespace {
46
47
template <typename TJoltVertex>
48
void pin_vertices(const JoltSoftBody3D &p_body, const HashSet<int> &p_pinned_vertices, const LocalVector<int> &p_mesh_to_physics, JPH::Array<TJoltVertex> &r_physics_vertices) {
49
const int mesh_vertex_count = p_mesh_to_physics.size();
50
const int physics_vertex_count = (int)r_physics_vertices.size();
51
52
for (int mesh_index : p_pinned_vertices) {
53
ERR_CONTINUE_MSG(mesh_index < 0 || mesh_index >= mesh_vertex_count, vformat("Index %d of pinned vertex in soft body '%s' is out of bounds. There are only %d vertices in the current mesh.", mesh_index, p_body.to_string(), mesh_vertex_count));
54
55
const int physics_index = p_mesh_to_physics[mesh_index];
56
ERR_CONTINUE_MSG(physics_index < 0 || physics_index >= physics_vertex_count, vformat("Index %d of pinned vertex in soft body '%s' is out of bounds. There are only %d vertices in the current mesh. This should not happen. Please report this.", physics_index, p_body.to_string(), physics_vertex_count));
57
58
r_physics_vertices[physics_index].mInvMass = 0.0f;
59
}
60
}
61
62
} // namespace
63
64
JPH::BroadPhaseLayer JoltSoftBody3D::_get_broad_phase_layer() const {
65
return JoltBroadPhaseLayer::BODY_DYNAMIC;
66
}
67
68
JPH::ObjectLayer JoltSoftBody3D::_get_object_layer() const {
69
ERR_FAIL_NULL_V(space, 0);
70
71
return space->map_to_object_layer(_get_broad_phase_layer(), collision_layer, collision_mask);
72
}
73
74
void JoltSoftBody3D::_space_changing() {
75
JoltObject3D::_space_changing();
76
77
// Note that we should not use `in_space()` as the condition here, since we could have cleared the mesh at this point.
78
if (jolt_body != nullptr) {
79
jolt_settings = new JPH::SoftBodyCreationSettings(jolt_body->GetSoftBodyCreationSettings());
80
jolt_settings->mSettings = nullptr;
81
}
82
83
_deref_shared_data();
84
}
85
86
void JoltSoftBody3D::_space_changed() {
87
JoltObject3D::_space_changed();
88
89
_update_mass();
90
_update_pressure();
91
_update_damping();
92
_update_simulation_precision();
93
_update_group_filter();
94
}
95
96
void JoltSoftBody3D::_add_to_space() {
97
if (unlikely(space == nullptr || !mesh.is_valid())) {
98
return;
99
}
100
101
const bool has_valid_shared = _ref_shared_data();
102
ERR_FAIL_COND(!has_valid_shared);
103
104
JPH::CollisionGroup::GroupID group_id = 0;
105
JPH::CollisionGroup::SubGroupID sub_group_id = 0;
106
JoltGroupFilter::encode_object(this, group_id, sub_group_id);
107
108
jolt_settings->mSettings = shared->settings;
109
jolt_settings->mUserData = reinterpret_cast<JPH::uint64>(this);
110
jolt_settings->mObjectLayer = _get_object_layer();
111
jolt_settings->mCollisionGroup = JPH::CollisionGroup(nullptr, group_id, sub_group_id);
112
jolt_settings->mMaxLinearVelocity = JoltProjectSettings::max_linear_velocity;
113
114
JPH::Body *new_jolt_body = space->add_object(*this, *jolt_settings);
115
if (new_jolt_body == nullptr) {
116
return;
117
}
118
119
jolt_body = new_jolt_body;
120
121
delete jolt_settings;
122
jolt_settings = nullptr;
123
}
124
125
bool JoltSoftBody3D::_ref_shared_data() {
126
HashMap<RID, Shared>::Iterator iter_shared_data = mesh_to_shared.find(mesh);
127
128
if (iter_shared_data == mesh_to_shared.end()) {
129
RenderingServer *rendering = RenderingServer::get_singleton();
130
131
// TODO: calling RenderingServer::mesh_surface_get_arrays() from the physics thread
132
// is not safe and can deadlock when physics/3d/run_on_separate_thread is enabled.
133
// This method blocks on the main thread to return data, but the main thread may be
134
// blocked waiting on us in PhysicsServer3D::sync().
135
const Array mesh_data = rendering->mesh_surface_get_arrays(mesh, 0);
136
ERR_FAIL_COND_V(mesh_data.is_empty(), false);
137
138
const PackedInt32Array mesh_indices = mesh_data[RenderingServer::ARRAY_INDEX];
139
ERR_FAIL_COND_V(mesh_indices.is_empty(), false);
140
141
const PackedVector3Array mesh_vertices = mesh_data[RenderingServer::ARRAY_VERTEX];
142
ERR_FAIL_COND_V(mesh_vertices.is_empty(), false);
143
144
iter_shared_data = mesh_to_shared.insert(mesh, Shared());
145
146
LocalVector<int> &mesh_to_physics = iter_shared_data->value.mesh_to_physics;
147
148
JPH::SoftBodySharedSettings &settings = *iter_shared_data->value.settings;
149
settings.mVertexRadius = JoltProjectSettings::soft_body_point_radius;
150
151
JPH::Array<JPH::SoftBodySharedSettings::Vertex> &physics_vertices = settings.mVertices;
152
JPH::Array<JPH::SoftBodySharedSettings::Face> &physics_faces = settings.mFaces;
153
154
HashMap<Vector3, int> vertex_to_physics;
155
156
const int mesh_vertex_count = mesh_vertices.size();
157
const int mesh_index_count = mesh_indices.size();
158
159
mesh_to_physics.resize(mesh_vertex_count);
160
for (int &index : mesh_to_physics) {
161
index = -1;
162
}
163
physics_vertices.reserve(mesh_vertex_count);
164
vertex_to_physics.reserve(mesh_vertex_count);
165
166
int physics_index_count = 0;
167
168
for (int i = 0; i < mesh_index_count; i += 3) {
169
int physics_face[3];
170
171
for (int j = 0; j < 3; ++j) {
172
const int mesh_index = mesh_indices[i + j];
173
const Vector3 vertex = mesh_vertices[mesh_index];
174
175
HashMap<Vector3, int>::Iterator iter_physics_index = vertex_to_physics.find(vertex);
176
177
if (iter_physics_index == vertex_to_physics.end()) {
178
physics_vertices.emplace_back(JPH::Float3((float)vertex.x, (float)vertex.y, (float)vertex.z), JPH::Float3(0.0f, 0.0f, 0.0f), 1.0f);
179
iter_physics_index = vertex_to_physics.insert(vertex, physics_index_count++);
180
}
181
182
physics_face[j] = iter_physics_index->value;
183
mesh_to_physics[mesh_index] = iter_physics_index->value;
184
}
185
186
if (physics_face[0] == physics_face[1] || physics_face[0] == physics_face[2] || physics_face[1] == physics_face[2]) {
187
continue; // We skip degenerate faces, since they're problematic, and Jolt will assert about it anyway.
188
}
189
190
// Jolt uses a different winding order, so we swap the indices to account for that.
191
physics_faces.emplace_back((JPH::uint32)physics_face[2], (JPH::uint32)physics_face[1], (JPH::uint32)physics_face[0]);
192
}
193
194
// Pin whatever pinned vertices we have currently. This is used during the `Optimize` call below to order the
195
// constraints. Note that it's fine if the pinned vertices change later, but that will reduce the effectiveness
196
// of the constraints a bit.
197
pin_vertices(*this, pinned_vertices, mesh_to_physics, physics_vertices);
198
199
// Since Godot's stiffness is input as a coefficient between 0 and 1, and Jolt uses actual stiffness for its
200
// edge constraints, we crudely map one to the other with an arbitrary constant.
201
const float stiffness = MAX(Math::pow(stiffness_coefficient, 3.0f) * 100000.0f, 0.000001f);
202
const float inverse_stiffness = 1.0f / stiffness;
203
204
JPH::SoftBodySharedSettings::VertexAttributes vertex_attrib;
205
vertex_attrib.mCompliance = vertex_attrib.mShearCompliance = inverse_stiffness;
206
207
settings.CreateConstraints(&vertex_attrib, 1, JPH::SoftBodySharedSettings::EBendType::None);
208
float multiplier = 1.0f - shrinking_factor;
209
for (JPH::SoftBodySharedSettings::Edge &e : settings.mEdgeConstraints) {
210
e.mRestLength *= multiplier;
211
}
212
settings.Optimize();
213
} else {
214
iter_shared_data->value.ref_count++;
215
}
216
217
shared = &iter_shared_data->value;
218
219
return true;
220
}
221
222
void JoltSoftBody3D::_deref_shared_data() {
223
if (unlikely(shared == nullptr)) {
224
return;
225
}
226
227
HashMap<RID, Shared>::Iterator iter = mesh_to_shared.find(mesh);
228
if (unlikely(iter == mesh_to_shared.end())) {
229
return;
230
}
231
232
if (--iter->value.ref_count == 0) {
233
mesh_to_shared.remove(iter);
234
}
235
236
shared = nullptr;
237
}
238
239
void JoltSoftBody3D::_update_mass() {
240
if (!in_space()) {
241
return;
242
}
243
244
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
245
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
246
247
const float inverse_vertex_mass = mass == 0.0f ? 1.0f : (float)physics_vertices.size() / mass;
248
249
for (JPH::SoftBodyVertex &vertex : physics_vertices) {
250
vertex.mInvMass = inverse_vertex_mass;
251
}
252
253
pin_vertices(*this, pinned_vertices, shared->mesh_to_physics, physics_vertices);
254
}
255
256
void JoltSoftBody3D::_update_pressure() {
257
if (!in_space()) {
258
jolt_settings->mPressure = pressure;
259
return;
260
}
261
262
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
263
motion_properties.SetPressure(pressure);
264
}
265
266
void JoltSoftBody3D::_update_damping() {
267
if (!in_space()) {
268
jolt_settings->mLinearDamping = linear_damping;
269
return;
270
}
271
272
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
273
motion_properties.SetLinearDamping(linear_damping);
274
}
275
276
void JoltSoftBody3D::_update_simulation_precision() {
277
if (!in_space()) {
278
jolt_settings->mNumIterations = (JPH::uint32)simulation_precision;
279
return;
280
}
281
282
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
283
motion_properties.SetNumIterations((JPH::uint32)simulation_precision);
284
}
285
286
void JoltSoftBody3D::_update_group_filter() {
287
JPH::GroupFilter *group_filter = !exceptions.is_empty() ? JoltGroupFilter::instance : nullptr;
288
289
if (!in_space()) {
290
jolt_settings->mCollisionGroup.SetGroupFilter(group_filter);
291
} else {
292
jolt_body->GetCollisionGroup().SetGroupFilter(group_filter);
293
}
294
}
295
296
void JoltSoftBody3D::_try_rebuild() {
297
if (space != nullptr) {
298
_reset_space();
299
}
300
}
301
302
void JoltSoftBody3D::_mesh_changed() {
303
_try_rebuild();
304
}
305
306
void JoltSoftBody3D::_simulation_precision_changed() {
307
wake_up();
308
}
309
310
void JoltSoftBody3D::_mass_changed() {
311
wake_up();
312
}
313
314
void JoltSoftBody3D::_pressure_changed() {
315
_update_pressure();
316
wake_up();
317
}
318
319
void JoltSoftBody3D::_damping_changed() {
320
_update_damping();
321
wake_up();
322
}
323
324
void JoltSoftBody3D::_pins_changed() {
325
_update_mass();
326
wake_up();
327
}
328
329
void JoltSoftBody3D::_vertices_changed() {
330
wake_up();
331
}
332
333
void JoltSoftBody3D::_exceptions_changed() {
334
_update_group_filter();
335
}
336
337
void JoltSoftBody3D::_motion_changed() {
338
wake_up();
339
}
340
341
JoltSoftBody3D::JoltSoftBody3D() :
342
JoltObject3D(OBJECT_TYPE_SOFT_BODY) {
343
jolt_settings->mRestitution = 0.0f;
344
jolt_settings->mFriction = 1.0f;
345
jolt_settings->mUpdatePosition = false;
346
jolt_settings->mMakeRotationIdentity = false;
347
}
348
349
JoltSoftBody3D::~JoltSoftBody3D() {
350
if (jolt_settings != nullptr) {
351
delete jolt_settings;
352
jolt_settings = nullptr;
353
}
354
}
355
356
bool JoltSoftBody3D::in_space() const {
357
return JoltObject3D::in_space() && shared != nullptr;
358
}
359
360
void JoltSoftBody3D::add_collision_exception(const RID &p_excepted_body) {
361
exceptions.push_back(p_excepted_body);
362
363
_exceptions_changed();
364
}
365
366
void JoltSoftBody3D::remove_collision_exception(const RID &p_excepted_body) {
367
exceptions.erase(p_excepted_body);
368
369
_exceptions_changed();
370
}
371
372
bool JoltSoftBody3D::has_collision_exception(const RID &p_excepted_body) const {
373
return exceptions.find(p_excepted_body) >= 0;
374
}
375
376
bool JoltSoftBody3D::can_interact_with(const JoltBody3D &p_other) const {
377
return (can_collide_with(p_other) || p_other.can_collide_with(*this)) && !has_collision_exception(p_other.get_rid()) && !p_other.has_collision_exception(rid);
378
}
379
380
bool JoltSoftBody3D::can_interact_with(const JoltSoftBody3D &p_other) const {
381
return (can_collide_with(p_other) || p_other.can_collide_with(*this)) && !has_collision_exception(p_other.get_rid()) && !p_other.has_collision_exception(rid);
382
}
383
384
bool JoltSoftBody3D::can_interact_with(const JoltArea3D &p_other) const {
385
return p_other.can_interact_with(*this);
386
}
387
388
Vector3 JoltSoftBody3D::get_velocity_at_position(const Vector3 &p_position) const {
389
return Vector3();
390
}
391
392
void JoltSoftBody3D::set_mesh(const RID &p_mesh) {
393
if (unlikely(mesh == p_mesh)) {
394
return;
395
}
396
397
_deref_shared_data();
398
mesh = p_mesh;
399
_mesh_changed();
400
}
401
402
bool JoltSoftBody3D::is_sleeping() const {
403
if (!in_space()) {
404
return false;
405
} else {
406
return !jolt_body->IsActive();
407
}
408
}
409
410
void JoltSoftBody3D::apply_vertex_impulse(int p_index, const Vector3 &p_impulse) {
411
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply impulse to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
412
413
ERR_FAIL_NULL(shared);
414
ERR_FAIL_INDEX(p_index, (int)shared->mesh_to_physics.size());
415
const int physics_index = shared->mesh_to_physics[p_index];
416
ERR_FAIL_COND_MSG(physics_index < 0, vformat("Soft body vertex %d was not used by a face and has been omitted for '%s'. No impulse can be applied.", p_index, to_string()));
417
ERR_FAIL_COND_MSG(pinned_vertices.has(physics_index), vformat("Failed to apply impulse to point at index %d for '%s'. Point was found to be pinned.", static_cast<int>(physics_index), to_string()));
418
419
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
420
421
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
422
JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];
423
424
physics_vertex.mVelocity += to_jolt(p_impulse) * physics_vertex.mInvMass;
425
426
_motion_changed();
427
}
428
429
void JoltSoftBody3D::apply_vertex_force(int p_index, const Vector3 &p_force) {
430
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply force to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
431
432
apply_vertex_impulse(p_index, p_force * space->get_last_step());
433
}
434
435
void JoltSoftBody3D::apply_central_impulse(const Vector3 &p_impulse) {
436
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply central impulse to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
437
438
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
439
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
440
441
const JPH::Vec3 impulse = to_jolt(p_impulse) / physics_vertices.size();
442
443
for (JPH::SoftBodyVertex &physics_vertex : physics_vertices) {
444
if (physics_vertex.mInvMass > 0.0f) {
445
physics_vertex.mVelocity += impulse * physics_vertex.mInvMass;
446
}
447
}
448
449
_motion_changed();
450
}
451
452
void JoltSoftBody3D::apply_central_force(const Vector3 &p_force) {
453
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply central force to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
454
455
jolt_body->AddForce(to_jolt(p_force));
456
457
_motion_changed();
458
}
459
460
void JoltSoftBody3D::set_is_sleeping(bool p_enabled) {
461
if (!in_space()) {
462
return;
463
}
464
465
space->set_is_object_sleeping(jolt_body->GetID(), p_enabled);
466
}
467
468
bool JoltSoftBody3D::is_sleep_allowed() const {
469
if (!in_space()) {
470
return jolt_settings->mAllowSleeping;
471
} else {
472
return jolt_body->GetAllowSleeping();
473
}
474
}
475
476
void JoltSoftBody3D::set_is_sleep_allowed(bool p_enabled) {
477
if (!in_space()) {
478
jolt_settings->mAllowSleeping = p_enabled;
479
} else {
480
jolt_body->SetAllowSleeping(p_enabled);
481
}
482
}
483
484
void JoltSoftBody3D::set_simulation_precision(int p_precision) {
485
if (unlikely(simulation_precision == p_precision)) {
486
return;
487
}
488
489
simulation_precision = MAX(p_precision, 0);
490
491
_simulation_precision_changed();
492
}
493
494
void JoltSoftBody3D::set_mass(float p_mass) {
495
if (unlikely(mass == p_mass)) {
496
return;
497
}
498
499
mass = MAX(p_mass, 0.0f);
500
501
_mass_changed();
502
}
503
504
float JoltSoftBody3D::get_stiffness_coefficient() const {
505
return stiffness_coefficient;
506
}
507
508
void JoltSoftBody3D::set_stiffness_coefficient(float p_coefficient) {
509
stiffness_coefficient = CLAMP(p_coefficient, 0.0f, 1.0f);
510
}
511
512
float JoltSoftBody3D::get_shrinking_factor() const {
513
return shrinking_factor;
514
}
515
516
void JoltSoftBody3D::set_shrinking_factor(float p_shrinking_factor) {
517
shrinking_factor = p_shrinking_factor;
518
}
519
520
void JoltSoftBody3D::set_pressure(float p_pressure) {
521
if (unlikely(pressure == p_pressure)) {
522
return;
523
}
524
525
pressure = MAX(p_pressure, 0.0f);
526
527
_pressure_changed();
528
}
529
530
void JoltSoftBody3D::set_linear_damping(float p_damping) {
531
if (unlikely(linear_damping == p_damping)) {
532
return;
533
}
534
535
linear_damping = MAX(p_damping, 0.0f);
536
537
_damping_changed();
538
}
539
540
float JoltSoftBody3D::get_drag() const {
541
// Drag is not a thing in Jolt, and not supported by Godot Physics either.
542
return 0.0f;
543
}
544
545
void JoltSoftBody3D::set_drag(float p_drag) {
546
// Drag is not a thing in Jolt, and not supported by Godot Physics either.
547
}
548
549
Variant JoltSoftBody3D::get_state(PhysicsServer3D::BodyState p_state) const {
550
switch (p_state) {
551
case PhysicsServer3D::BODY_STATE_TRANSFORM: {
552
return get_transform();
553
}
554
case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {
555
ERR_FAIL_V_MSG(Variant(), "Linear velocity is not supported for soft bodies.");
556
}
557
case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {
558
ERR_FAIL_V_MSG(Variant(), "Angular velocity is not supported for soft bodies.");
559
}
560
case PhysicsServer3D::BODY_STATE_SLEEPING: {
561
return is_sleeping();
562
}
563
case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
564
return is_sleep_allowed();
565
}
566
default: {
567
ERR_FAIL_V_MSG(Variant(), vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
568
}
569
}
570
}
571
572
void JoltSoftBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_value) {
573
switch (p_state) {
574
case PhysicsServer3D::BODY_STATE_TRANSFORM: {
575
set_transform(p_value);
576
} break;
577
case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {
578
ERR_FAIL_MSG("Linear velocity is not supported for soft bodies.");
579
} break;
580
case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {
581
ERR_FAIL_MSG("Angular velocity is not supported for soft bodies.");
582
} break;
583
case PhysicsServer3D::BODY_STATE_SLEEPING: {
584
set_is_sleeping(p_value);
585
} break;
586
case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
587
set_is_sleep_allowed(p_value);
588
} break;
589
default: {
590
ERR_FAIL_MSG(vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
591
} break;
592
}
593
}
594
595
Transform3D JoltSoftBody3D::get_transform() const {
596
// Since any transform gets baked into the vertices anyway we can just return identity here.
597
return Transform3D();
598
}
599
600
void JoltSoftBody3D::set_transform(const Transform3D &p_transform) {
601
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to set transform for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
602
603
// For whatever reason this has to be interpreted as a relative global-space transform rather than an absolute one,
604
// because `SoftBody3D` will immediately upon entering the scene tree set itself to be top-level and also set its
605
// transform to be identity, while still expecting to stay in its original position.
606
//
607
// We also discard any scaling, since we have no way of scaling the actual edge lengths.
608
const JPH::Mat44 relative_transform = to_jolt(p_transform.orthonormalized());
609
610
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
611
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
612
613
for (JPH::SoftBodyVertex &vertex : physics_vertices) {
614
vertex.mPosition = vertex.mPreviousPosition = relative_transform * vertex.mPosition;
615
vertex.mVelocity = JPH::Vec3::sZero();
616
}
617
wake_up();
618
}
619
620
AABB JoltSoftBody3D::get_bounds() const {
621
ERR_FAIL_COND_V_MSG(!in_space(), AABB(), vformat("Failed to retrieve world bounds of '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
622
return to_godot(jolt_body->GetWorldSpaceBounds());
623
}
624
625
void JoltSoftBody3D::update_rendering_server(PhysicsServer3DRenderingServerHandler *p_rendering_server_handler) {
626
// Ideally we would emit an actual error here, but that would spam the logs to the point where the actual cause will be drowned out.
627
if (unlikely(!in_space())) {
628
return;
629
}
630
631
const JPH::SoftBodyMotionProperties &motion_properties = static_cast<const JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
632
633
typedef JPH::SoftBodyMotionProperties::Vertex SoftBodyVertex;
634
typedef JPH::SoftBodyMotionProperties::Face SoftBodyFace;
635
636
const JPH::Array<SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
637
const JPH::Array<SoftBodyFace> &physics_faces = motion_properties.GetFaces();
638
639
const int physics_vertex_count = (int)physics_vertices.size();
640
641
normals.clear();
642
normals.resize(physics_vertex_count);
643
644
// Compute vertex normals using smooth-shading:
645
// Each vertex should use the average normal of all faces it is a part of.
646
// Iterate over each face, and add the face normal to each of the face vertices.
647
// By the end of the loop, each vertex normal will be the sum of all face normals it belongs to.
648
for (const SoftBodyFace &physics_face : physics_faces) {
649
// Jolt uses a different winding order, so we swap the indices to account for that.
650
651
const uint32_t i0 = physics_face.mVertex[2];
652
const uint32_t i1 = physics_face.mVertex[1];
653
const uint32_t i2 = physics_face.mVertex[0];
654
655
const Vector3 v0 = to_godot(physics_vertices[i0].mPosition);
656
const Vector3 v1 = to_godot(physics_vertices[i1].mPosition);
657
const Vector3 v2 = to_godot(physics_vertices[i2].mPosition);
658
659
const Vector3 normal = (v2 - v0).cross(v1 - v0).normalized();
660
661
normals[i0] += normal;
662
normals[i1] += normal;
663
normals[i2] += normal;
664
}
665
// Normalize the vertex normals to have length 1.0
666
for (Vector3 &n : normals) {
667
real_t len = n.length();
668
// Some normals may have length 0 if the face was degenerate,
669
// so don't divide by zero.
670
if (len > CMP_EPSILON) {
671
n /= len;
672
}
673
}
674
675
const int mesh_vertex_count = shared->mesh_to_physics.size();
676
677
for (int i = 0; i < mesh_vertex_count; ++i) {
678
const int physics_index = shared->mesh_to_physics[i];
679
if (physics_index >= 0) {
680
const Vector3 vertex = to_godot(physics_vertices[(size_t)physics_index].mPosition);
681
const Vector3 normal = normals[(uint32_t)physics_index];
682
683
p_rendering_server_handler->set_vertex(i, vertex);
684
p_rendering_server_handler->set_normal(i, normal);
685
}
686
}
687
688
p_rendering_server_handler->set_aabb(get_bounds());
689
}
690
691
Vector3 JoltSoftBody3D::get_vertex_position(int p_index) {
692
ERR_FAIL_COND_V_MSG(!in_space(), Vector3(), vformat("Failed to retrieve point position for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
693
694
ERR_FAIL_NULL_V(shared, Vector3());
695
ERR_FAIL_INDEX_V(p_index, (int)shared->mesh_to_physics.size(), Vector3());
696
const int physics_index = shared->mesh_to_physics[p_index];
697
ERR_FAIL_COND_V_MSG(physics_index < 0, Vector3(), vformat("Soft body vertex %d was not used by a face and has been omitted for '%s'. Position cannot be returned.", p_index, to_string()));
698
699
const JPH::SoftBodyMotionProperties &motion_properties = static_cast<const JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
700
const JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
701
const JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];
702
703
return to_godot(jolt_body->GetCenterOfMassPosition() + physics_vertex.mPosition);
704
}
705
706
void JoltSoftBody3D::set_vertex_position(int p_index, const Vector3 &p_position) {
707
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to set point position for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
708
709
ERR_FAIL_NULL(shared);
710
ERR_FAIL_INDEX(p_index, (int)shared->mesh_to_physics.size());
711
const int physics_index = shared->mesh_to_physics[p_index];
712
ERR_FAIL_COND_MSG(physics_index < 0, vformat("Soft body vertex %d was not used by a face and has been omitted for '%s'. Position cannot be set.", p_index, to_string()));
713
714
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
715
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
716
JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];
717
718
const JPH::RVec3 center_of_mass = jolt_body->GetCenterOfMassPosition();
719
physics_vertex.mPosition = JPH::Vec3(to_jolt_r(p_position) - center_of_mass);
720
721
_vertices_changed();
722
}
723
724
void JoltSoftBody3D::pin_vertex(int p_index) {
725
pinned_vertices.insert(p_index);
726
727
_pins_changed();
728
}
729
730
void JoltSoftBody3D::unpin_vertex(int p_index) {
731
pinned_vertices.erase(p_index);
732
733
_pins_changed();
734
}
735
736
void JoltSoftBody3D::unpin_all_vertices() {
737
pinned_vertices.clear();
738
739
_pins_changed();
740
}
741
742
bool JoltSoftBody3D::is_vertex_pinned(int p_index) const {
743
ERR_FAIL_COND_V_MSG(!in_space(), false, vformat("Failed retrieve pin status of point for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
744
745
ERR_FAIL_NULL_V(shared, false);
746
ERR_FAIL_INDEX_V(p_index, (int)shared->mesh_to_physics.size(), false);
747
const int physics_index = shared->mesh_to_physics[p_index];
748
749
return pinned_vertices.has(physics_index);
750
}
751
752