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