Path: blob/master/modules/jolt_physics/objects/jolt_soft_body_3d.cpp
10278 views
/**************************************************************************/1/* jolt_soft_body_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "jolt_soft_body_3d.h"3132#include "../jolt_project_settings.h"33#include "../misc/jolt_type_conversions.h"34#include "../spaces/jolt_broad_phase_layer.h"35#include "../spaces/jolt_space_3d.h"36#include "jolt_area_3d.h"37#include "jolt_body_3d.h"38#include "jolt_group_filter.h"3940#include "servers/rendering_server.h"4142#include "Jolt/Physics/SoftBody/SoftBodyMotionProperties.h"4344namespace {4546template <typename TJoltVertex>47void 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) {48const int mesh_vertex_count = p_mesh_to_physics.size();49const int physics_vertex_count = (int)r_physics_vertices.size();5051for (int mesh_index : p_pinned_vertices) {52ERR_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));5354const int physics_index = p_mesh_to_physics[mesh_index];55ERR_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));5657r_physics_vertices[physics_index].mInvMass = 0.0f;58}59}6061} // namespace6263JPH::BroadPhaseLayer JoltSoftBody3D::_get_broad_phase_layer() const {64return JoltBroadPhaseLayer::BODY_DYNAMIC;65}6667JPH::ObjectLayer JoltSoftBody3D::_get_object_layer() const {68ERR_FAIL_NULL_V(space, 0);6970return space->map_to_object_layer(_get_broad_phase_layer(), collision_layer, collision_mask);71}7273void JoltSoftBody3D::_space_changing() {74JoltObject3D::_space_changing();7576// Note that we should not use `in_space()` as the condition here, since we could have cleared the mesh at this point.77if (jolt_body != nullptr) {78jolt_settings = new JPH::SoftBodyCreationSettings(jolt_body->GetSoftBodyCreationSettings());79jolt_settings->mSettings = nullptr;80}8182_deref_shared_data();83}8485void JoltSoftBody3D::_space_changed() {86JoltObject3D::_space_changed();8788_update_mass();89_update_pressure();90_update_damping();91_update_simulation_precision();92_update_group_filter();93}9495void JoltSoftBody3D::_add_to_space() {96if (unlikely(space == nullptr || !mesh.is_valid())) {97return;98}99100const bool has_valid_shared = _ref_shared_data();101ERR_FAIL_COND(!has_valid_shared);102103JPH::CollisionGroup::GroupID group_id = 0;104JPH::CollisionGroup::SubGroupID sub_group_id = 0;105JoltGroupFilter::encode_object(this, group_id, sub_group_id);106107jolt_settings->mSettings = shared->settings;108jolt_settings->mUserData = reinterpret_cast<JPH::uint64>(this);109jolt_settings->mObjectLayer = _get_object_layer();110jolt_settings->mCollisionGroup = JPH::CollisionGroup(nullptr, group_id, sub_group_id);111jolt_settings->mMaxLinearVelocity = JoltProjectSettings::max_linear_velocity;112113JPH::Body *new_jolt_body = space->add_object(*this, *jolt_settings);114if (new_jolt_body == nullptr) {115return;116}117118jolt_body = new_jolt_body;119120delete jolt_settings;121jolt_settings = nullptr;122}123124bool JoltSoftBody3D::_ref_shared_data() {125HashMap<RID, Shared>::Iterator iter_shared_data = mesh_to_shared.find(mesh);126127if (iter_shared_data == mesh_to_shared.end()) {128RenderingServer *rendering = RenderingServer::get_singleton();129130// TODO: calling RenderingServer::mesh_surface_get_arrays() from the physics thread131// is not safe and can deadlock when physics/3d/run_on_separate_thread is enabled.132// This method blocks on the main thread to return data, but the main thread may be133// blocked waiting on us in PhysicsServer3D::sync().134const Array mesh_data = rendering->mesh_surface_get_arrays(mesh, 0);135ERR_FAIL_COND_V(mesh_data.is_empty(), false);136137const PackedInt32Array mesh_indices = mesh_data[RenderingServer::ARRAY_INDEX];138ERR_FAIL_COND_V(mesh_indices.is_empty(), false);139140const PackedVector3Array mesh_vertices = mesh_data[RenderingServer::ARRAY_VERTEX];141ERR_FAIL_COND_V(mesh_vertices.is_empty(), false);142143iter_shared_data = mesh_to_shared.insert(mesh, Shared());144145LocalVector<int> &mesh_to_physics = iter_shared_data->value.mesh_to_physics;146147JPH::SoftBodySharedSettings &settings = *iter_shared_data->value.settings;148settings.mVertexRadius = JoltProjectSettings::soft_body_point_radius;149150JPH::Array<JPH::SoftBodySharedSettings::Vertex> &physics_vertices = settings.mVertices;151JPH::Array<JPH::SoftBodySharedSettings::Face> &physics_faces = settings.mFaces;152153HashMap<Vector3, int> vertex_to_physics;154155const int mesh_vertex_count = mesh_vertices.size();156const int mesh_index_count = mesh_indices.size();157158mesh_to_physics.resize(mesh_vertex_count);159for (int &index : mesh_to_physics) {160index = -1;161}162physics_vertices.reserve(mesh_vertex_count);163vertex_to_physics.reserve(mesh_vertex_count);164165int physics_index_count = 0;166167for (int i = 0; i < mesh_index_count; i += 3) {168int physics_face[3];169170for (int j = 0; j < 3; ++j) {171const int mesh_index = mesh_indices[i + j];172const Vector3 vertex = mesh_vertices[mesh_index];173174HashMap<Vector3, int>::Iterator iter_physics_index = vertex_to_physics.find(vertex);175176if (iter_physics_index == vertex_to_physics.end()) {177physics_vertices.emplace_back(JPH::Float3((float)vertex.x, (float)vertex.y, (float)vertex.z), JPH::Float3(0.0f, 0.0f, 0.0f), 1.0f);178iter_physics_index = vertex_to_physics.insert(vertex, physics_index_count++);179}180181physics_face[j] = iter_physics_index->value;182mesh_to_physics[mesh_index] = iter_physics_index->value;183}184185if (physics_face[0] == physics_face[1] || physics_face[0] == physics_face[2] || physics_face[1] == physics_face[2]) {186continue; // We skip degenerate faces, since they're problematic, and Jolt will assert about it anyway.187}188189// Jolt uses a different winding order, so we swap the indices to account for that.190physics_faces.emplace_back((JPH::uint32)physics_face[2], (JPH::uint32)physics_face[1], (JPH::uint32)physics_face[0]);191}192193// Pin whatever pinned vertices we have currently. This is used during the `Optimize` call below to order the194// constraints. Note that it's fine if the pinned vertices change later, but that will reduce the effectiveness195// of the constraints a bit.196pin_vertices(*this, pinned_vertices, mesh_to_physics, physics_vertices);197198// Since Godot's stiffness is input as a coefficient between 0 and 1, and Jolt uses actual stiffness for its199// edge constraints, we crudely map one to the other with an arbitrary constant.200const float stiffness = MAX(Math::pow(stiffness_coefficient, 3.0f) * 100000.0f, 0.000001f);201const float inverse_stiffness = 1.0f / stiffness;202203JPH::SoftBodySharedSettings::VertexAttributes vertex_attrib;204vertex_attrib.mCompliance = vertex_attrib.mShearCompliance = inverse_stiffness;205206settings.CreateConstraints(&vertex_attrib, 1, JPH::SoftBodySharedSettings::EBendType::None);207float multiplier = 1.0f - shrinking_factor;208for (JPH::SoftBodySharedSettings::Edge &e : settings.mEdgeConstraints) {209e.mRestLength *= multiplier;210}211settings.Optimize();212} else {213iter_shared_data->value.ref_count++;214}215216shared = &iter_shared_data->value;217218return true;219}220221void JoltSoftBody3D::_deref_shared_data() {222if (unlikely(shared == nullptr)) {223return;224}225226HashMap<RID, Shared>::Iterator iter = mesh_to_shared.find(mesh);227if (unlikely(iter == mesh_to_shared.end())) {228return;229}230231if (--iter->value.ref_count == 0) {232mesh_to_shared.remove(iter);233}234235shared = nullptr;236}237238void JoltSoftBody3D::_update_mass() {239if (!in_space()) {240return;241}242243JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());244JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();245246const float inverse_vertex_mass = mass == 0.0f ? 1.0f : (float)physics_vertices.size() / mass;247248for (JPH::SoftBodyVertex &vertex : physics_vertices) {249vertex.mInvMass = inverse_vertex_mass;250}251252pin_vertices(*this, pinned_vertices, shared->mesh_to_physics, physics_vertices);253}254255void JoltSoftBody3D::_update_pressure() {256if (!in_space()) {257jolt_settings->mPressure = pressure;258return;259}260261JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());262motion_properties.SetPressure(pressure);263}264265void JoltSoftBody3D::_update_damping() {266if (!in_space()) {267jolt_settings->mLinearDamping = linear_damping;268return;269}270271JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());272motion_properties.SetLinearDamping(linear_damping);273}274275void JoltSoftBody3D::_update_simulation_precision() {276if (!in_space()) {277jolt_settings->mNumIterations = (JPH::uint32)simulation_precision;278return;279}280281JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());282motion_properties.SetNumIterations((JPH::uint32)simulation_precision);283}284285void JoltSoftBody3D::_update_group_filter() {286JPH::GroupFilter *group_filter = !exceptions.is_empty() ? JoltGroupFilter::instance : nullptr;287288if (!in_space()) {289jolt_settings->mCollisionGroup.SetGroupFilter(group_filter);290} else {291jolt_body->GetCollisionGroup().SetGroupFilter(group_filter);292}293}294295void JoltSoftBody3D::_try_rebuild() {296if (space != nullptr) {297_reset_space();298}299}300301void JoltSoftBody3D::_mesh_changed() {302_try_rebuild();303}304305void JoltSoftBody3D::_simulation_precision_changed() {306wake_up();307}308309void JoltSoftBody3D::_mass_changed() {310wake_up();311}312313void JoltSoftBody3D::_pressure_changed() {314_update_pressure();315wake_up();316}317318void JoltSoftBody3D::_damping_changed() {319_update_damping();320wake_up();321}322323void JoltSoftBody3D::_pins_changed() {324_update_mass();325wake_up();326}327328void JoltSoftBody3D::_vertices_changed() {329wake_up();330}331332void JoltSoftBody3D::_exceptions_changed() {333_update_group_filter();334}335336void JoltSoftBody3D::_motion_changed() {337wake_up();338}339340JoltSoftBody3D::JoltSoftBody3D() :341JoltObject3D(OBJECT_TYPE_SOFT_BODY) {342jolt_settings->mRestitution = 0.0f;343jolt_settings->mFriction = 1.0f;344jolt_settings->mUpdatePosition = false;345jolt_settings->mMakeRotationIdentity = false;346}347348JoltSoftBody3D::~JoltSoftBody3D() {349if (jolt_settings != nullptr) {350delete jolt_settings;351jolt_settings = nullptr;352}353}354355bool JoltSoftBody3D::in_space() const {356return JoltObject3D::in_space() && shared != nullptr;357}358359void JoltSoftBody3D::add_collision_exception(const RID &p_excepted_body) {360exceptions.push_back(p_excepted_body);361362_exceptions_changed();363}364365void JoltSoftBody3D::remove_collision_exception(const RID &p_excepted_body) {366exceptions.erase(p_excepted_body);367368_exceptions_changed();369}370371bool JoltSoftBody3D::has_collision_exception(const RID &p_excepted_body) const {372return exceptions.find(p_excepted_body) >= 0;373}374375bool JoltSoftBody3D::can_interact_with(const JoltBody3D &p_other) const {376return (can_collide_with(p_other) || p_other.can_collide_with(*this)) && !has_collision_exception(p_other.get_rid()) && !p_other.has_collision_exception(rid);377}378379bool JoltSoftBody3D::can_interact_with(const JoltSoftBody3D &p_other) const {380return (can_collide_with(p_other) || p_other.can_collide_with(*this)) && !has_collision_exception(p_other.get_rid()) && !p_other.has_collision_exception(rid);381}382383bool JoltSoftBody3D::can_interact_with(const JoltArea3D &p_other) const {384return p_other.can_interact_with(*this);385}386387Vector3 JoltSoftBody3D::get_velocity_at_position(const Vector3 &p_position) const {388return Vector3();389}390391void JoltSoftBody3D::set_mesh(const RID &p_mesh) {392if (unlikely(mesh == p_mesh)) {393return;394}395396_deref_shared_data();397mesh = p_mesh;398_mesh_changed();399}400401bool JoltSoftBody3D::is_sleeping() const {402if (!in_space()) {403return false;404} else {405return !jolt_body->IsActive();406}407}408409void JoltSoftBody3D::apply_vertex_impulse(int p_index, const Vector3 &p_impulse) {410ERR_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()));411412ERR_FAIL_NULL(shared);413ERR_FAIL_INDEX(p_index, (int)shared->mesh_to_physics.size());414const int physics_index = shared->mesh_to_physics[p_index];415ERR_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()));416ERR_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()));417418JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());419420JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();421JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];422423physics_vertex.mVelocity += to_jolt(p_impulse) * physics_vertex.mInvMass;424425_motion_changed();426}427428void JoltSoftBody3D::apply_vertex_force(int p_index, const Vector3 &p_force) {429ERR_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()));430431apply_vertex_impulse(p_index, p_force * space->get_last_step());432}433434void JoltSoftBody3D::apply_central_impulse(const Vector3 &p_impulse) {435ERR_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()));436437JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());438JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();439440const JPH::Vec3 impulse = to_jolt(p_impulse) / physics_vertices.size();441442for (JPH::SoftBodyVertex &physics_vertex : physics_vertices) {443if (physics_vertex.mInvMass > 0.0f) {444physics_vertex.mVelocity += impulse * physics_vertex.mInvMass;445}446}447448_motion_changed();449}450451void JoltSoftBody3D::apply_central_force(const Vector3 &p_force) {452ERR_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()));453454jolt_body->AddForce(to_jolt(p_force));455456_motion_changed();457}458459void JoltSoftBody3D::set_is_sleeping(bool p_enabled) {460if (!in_space()) {461return;462}463464space->set_is_object_sleeping(jolt_body->GetID(), p_enabled);465}466467bool JoltSoftBody3D::is_sleep_allowed() const {468if (!in_space()) {469return jolt_settings->mAllowSleeping;470} else {471return jolt_body->GetAllowSleeping();472}473}474475void JoltSoftBody3D::set_is_sleep_allowed(bool p_enabled) {476if (!in_space()) {477jolt_settings->mAllowSleeping = p_enabled;478} else {479jolt_body->SetAllowSleeping(p_enabled);480}481}482483void JoltSoftBody3D::set_simulation_precision(int p_precision) {484if (unlikely(simulation_precision == p_precision)) {485return;486}487488simulation_precision = MAX(p_precision, 0);489490_simulation_precision_changed();491}492493void JoltSoftBody3D::set_mass(float p_mass) {494if (unlikely(mass == p_mass)) {495return;496}497498mass = MAX(p_mass, 0.0f);499500_mass_changed();501}502503float JoltSoftBody3D::get_stiffness_coefficient() const {504return stiffness_coefficient;505}506507void JoltSoftBody3D::set_stiffness_coefficient(float p_coefficient) {508stiffness_coefficient = CLAMP(p_coefficient, 0.0f, 1.0f);509}510511float JoltSoftBody3D::get_shrinking_factor() const {512return shrinking_factor;513}514515void JoltSoftBody3D::set_shrinking_factor(float p_shrinking_factor) {516shrinking_factor = p_shrinking_factor;517}518519void JoltSoftBody3D::set_pressure(float p_pressure) {520if (unlikely(pressure == p_pressure)) {521return;522}523524pressure = MAX(p_pressure, 0.0f);525526_pressure_changed();527}528529void JoltSoftBody3D::set_linear_damping(float p_damping) {530if (unlikely(linear_damping == p_damping)) {531return;532}533534linear_damping = MAX(p_damping, 0.0f);535536_damping_changed();537}538539float JoltSoftBody3D::get_drag() const {540// Drag is not a thing in Jolt, and not supported by Godot Physics either.541return 0.0f;542}543544void JoltSoftBody3D::set_drag(float p_drag) {545// Drag is not a thing in Jolt, and not supported by Godot Physics either.546}547548Variant JoltSoftBody3D::get_state(PhysicsServer3D::BodyState p_state) const {549switch (p_state) {550case PhysicsServer3D::BODY_STATE_TRANSFORM: {551return get_transform();552}553case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {554ERR_FAIL_V_MSG(Variant(), "Linear velocity is not supported for soft bodies.");555}556case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {557ERR_FAIL_V_MSG(Variant(), "Angular velocity is not supported for soft bodies.");558}559case PhysicsServer3D::BODY_STATE_SLEEPING: {560return is_sleeping();561}562case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {563return is_sleep_allowed();564}565default: {566ERR_FAIL_V_MSG(Variant(), vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));567}568}569}570571void JoltSoftBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_value) {572switch (p_state) {573case PhysicsServer3D::BODY_STATE_TRANSFORM: {574set_transform(p_value);575} break;576case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {577ERR_FAIL_MSG("Linear velocity is not supported for soft bodies.");578} break;579case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {580ERR_FAIL_MSG("Angular velocity is not supported for soft bodies.");581} break;582case PhysicsServer3D::BODY_STATE_SLEEPING: {583set_is_sleeping(p_value);584} break;585case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {586set_is_sleep_allowed(p_value);587} break;588default: {589ERR_FAIL_MSG(vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));590} break;591}592}593594Transform3D JoltSoftBody3D::get_transform() const {595// Since any transform gets baked into the vertices anyway we can just return identity here.596return Transform3D();597}598599void JoltSoftBody3D::set_transform(const Transform3D &p_transform) {600ERR_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()));601602// For whatever reason this has to be interpreted as a relative global-space transform rather than an absolute one,603// because `SoftBody3D` will immediately upon entering the scene tree set itself to be top-level and also set its604// transform to be identity, while still expecting to stay in its original position.605//606// We also discard any scaling, since we have no way of scaling the actual edge lengths.607const JPH::Mat44 relative_transform = to_jolt(p_transform.orthonormalized());608609JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());610JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();611612for (JPH::SoftBodyVertex &vertex : physics_vertices) {613vertex.mPosition = vertex.mPreviousPosition = relative_transform * vertex.mPosition;614vertex.mVelocity = JPH::Vec3::sZero();615}616wake_up();617}618619AABB JoltSoftBody3D::get_bounds() const {620ERR_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()));621return to_godot(jolt_body->GetWorldSpaceBounds());622}623624void JoltSoftBody3D::update_rendering_server(PhysicsServer3DRenderingServerHandler *p_rendering_server_handler) {625// 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.626if (unlikely(!in_space())) {627return;628}629630const JPH::SoftBodyMotionProperties &motion_properties = static_cast<const JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());631632typedef JPH::SoftBodyMotionProperties::Vertex SoftBodyVertex;633typedef JPH::SoftBodyMotionProperties::Face SoftBodyFace;634635const JPH::Array<SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();636const JPH::Array<SoftBodyFace> &physics_faces = motion_properties.GetFaces();637638const int physics_vertex_count = (int)physics_vertices.size();639640normals.clear();641normals.resize(physics_vertex_count);642643// Compute vertex normals using smooth-shading:644// Each vertex should use the average normal of all faces it is a part of.645// Iterate over each face, and add the face normal to each of the face vertices.646// By the end of the loop, each vertex normal will be the sum of all face normals it belongs to.647for (const SoftBodyFace &physics_face : physics_faces) {648// Jolt uses a different winding order, so we swap the indices to account for that.649650const uint32_t i0 = physics_face.mVertex[2];651const uint32_t i1 = physics_face.mVertex[1];652const uint32_t i2 = physics_face.mVertex[0];653654const Vector3 v0 = to_godot(physics_vertices[i0].mPosition);655const Vector3 v1 = to_godot(physics_vertices[i1].mPosition);656const Vector3 v2 = to_godot(physics_vertices[i2].mPosition);657658const Vector3 normal = (v2 - v0).cross(v1 - v0).normalized();659660normals[i0] += normal;661normals[i1] += normal;662normals[i2] += normal;663}664// Normalize the vertex normals to have length 1.0665for (Vector3 &n : normals) {666real_t len = n.length();667// Some normals may have length 0 if the face was degenerate,668// so don't divide by zero.669if (len > CMP_EPSILON) {670n /= len;671}672}673674const int mesh_vertex_count = shared->mesh_to_physics.size();675676for (int i = 0; i < mesh_vertex_count; ++i) {677const int physics_index = shared->mesh_to_physics[i];678if (physics_index >= 0) {679const Vector3 vertex = to_godot(physics_vertices[(size_t)physics_index].mPosition);680const Vector3 normal = normals[(uint32_t)physics_index];681682p_rendering_server_handler->set_vertex(i, vertex);683p_rendering_server_handler->set_normal(i, normal);684}685}686687p_rendering_server_handler->set_aabb(get_bounds());688}689690Vector3 JoltSoftBody3D::get_vertex_position(int p_index) {691ERR_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()));692693ERR_FAIL_NULL_V(shared, Vector3());694ERR_FAIL_INDEX_V(p_index, (int)shared->mesh_to_physics.size(), Vector3());695const int physics_index = shared->mesh_to_physics[p_index];696ERR_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()));697698const JPH::SoftBodyMotionProperties &motion_properties = static_cast<const JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());699const JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();700const JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];701702return to_godot(jolt_body->GetCenterOfMassPosition() + physics_vertex.mPosition);703}704705void JoltSoftBody3D::set_vertex_position(int p_index, const Vector3 &p_position) {706ERR_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()));707708ERR_FAIL_NULL(shared);709ERR_FAIL_INDEX(p_index, (int)shared->mesh_to_physics.size());710const int physics_index = shared->mesh_to_physics[p_index];711ERR_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()));712713JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());714JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();715JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];716717const JPH::RVec3 center_of_mass = jolt_body->GetCenterOfMassPosition();718physics_vertex.mPosition = JPH::Vec3(to_jolt_r(p_position) - center_of_mass);719720_vertices_changed();721}722723void JoltSoftBody3D::pin_vertex(int p_index) {724pinned_vertices.insert(p_index);725726_pins_changed();727}728729void JoltSoftBody3D::unpin_vertex(int p_index) {730pinned_vertices.erase(p_index);731732_pins_changed();733}734735void JoltSoftBody3D::unpin_all_vertices() {736pinned_vertices.clear();737738_pins_changed();739}740741bool JoltSoftBody3D::is_vertex_pinned(int p_index) const {742ERR_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()));743744ERR_FAIL_NULL_V(shared, false);745ERR_FAIL_INDEX_V(p_index, (int)shared->mesh_to_physics.size(), false);746const int physics_index = shared->mesh_to_physics[p_index];747748return pinned_vertices.has(physics_index);749}750751752