Path: blob/master/modules/jolt_physics/objects/jolt_soft_body_3d.cpp
20784 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/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();7576if (in_space()) {77jolt_settings = new JPH::SoftBodyCreationSettings(jolt_body->GetSoftBodyCreationSettings());78jolt_settings->mSettings = nullptr;79jolt_settings->mVertexRadius = JoltProjectSettings::soft_body_point_radius;80}81}8283void JoltSoftBody3D::_space_changed() {84JoltObject3D::_space_changed();8586_update_mass();87_update_pressure();88_update_damping();89_update_simulation_precision();90_update_group_filter();91}9293void JoltSoftBody3D::_add_to_space() {94if (unlikely(space == nullptr || !mesh.is_valid())) {95return;96}9798JPH::SoftBodySharedSettings *shared_settings = _create_shared_settings();99ERR_FAIL_NULL(shared_settings);100101JPH::CollisionGroup::GroupID group_id = 0;102JPH::CollisionGroup::SubGroupID sub_group_id = 0;103JoltGroupFilter::encode_object(this, group_id, sub_group_id);104105jolt_settings->mSettings = shared_settings;106jolt_settings->mUserData = reinterpret_cast<JPH::uint64>(this);107jolt_settings->mObjectLayer = _get_object_layer();108jolt_settings->mCollisionGroup = JPH::CollisionGroup(nullptr, group_id, sub_group_id);109jolt_settings->mMaxLinearVelocity = JoltProjectSettings::max_linear_velocity;110111JPH::Body *new_jolt_body = space->add_object(*this, *jolt_settings);112if (new_jolt_body == nullptr) {113return;114}115116jolt_body = new_jolt_body;117118delete jolt_settings;119jolt_settings = nullptr;120}121122JPH::SoftBodySharedSettings *JoltSoftBody3D::_create_shared_settings() {123RenderingServer *rendering = RenderingServer::get_singleton();124125// TODO: calling RenderingServer::mesh_surface_get_arrays() from the physics thread126// is not safe and can deadlock when physics/3d/run_on_separate_thread is enabled.127// This method blocks on the main thread to return data, but the main thread may be128// blocked waiting on us in PhysicsServer3D::sync().129const Array mesh_data = rendering->mesh_surface_get_arrays(mesh, 0);130ERR_FAIL_COND_V(mesh_data.is_empty(), nullptr);131132const PackedInt32Array mesh_indices = mesh_data[RenderingServer::ARRAY_INDEX];133ERR_FAIL_COND_V(mesh_indices.is_empty(), nullptr);134135const PackedVector3Array mesh_vertices = mesh_data[RenderingServer::ARRAY_VERTEX];136ERR_FAIL_COND_V(mesh_vertices.is_empty(), nullptr);137138JPH::SoftBodySharedSettings *settings = new JPH::SoftBodySharedSettings();139JPH::Array<JPH::SoftBodySharedSettings::Vertex> &physics_vertices = settings->mVertices;140JPH::Array<JPH::SoftBodySharedSettings::Face> &physics_faces = settings->mFaces;141142HashMap<Vector3, int> vertex_to_physics;143144const int mesh_vertex_count = mesh_vertices.size();145const int mesh_index_count = mesh_indices.size();146147mesh_to_physics.resize(mesh_vertex_count);148for (int &index : mesh_to_physics) {149index = -1;150}151physics_vertices.reserve(mesh_vertex_count);152vertex_to_physics.reserve(mesh_vertex_count);153154int physics_index_count = 0;155156const JPH::RVec3 body_position = jolt_settings->mPosition;157158for (int i = 0; i < mesh_index_count; i += 3) {159int physics_face[3];160161for (int j = 0; j < 3; ++j) {162const int mesh_index = mesh_indices[i + j];163const Vector3 vertex = mesh_vertices[mesh_index];164165HashMap<Vector3, int>::Iterator iter_physics_index = vertex_to_physics.find(vertex);166167if (iter_physics_index == vertex_to_physics.end()) {168physics_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);169iter_physics_index = vertex_to_physics.insert(vertex, physics_index_count++);170}171172physics_face[j] = iter_physics_index->value;173mesh_to_physics[mesh_index] = iter_physics_index->value;174}175176if (physics_face[0] == physics_face[1] || physics_face[0] == physics_face[2] || physics_face[1] == physics_face[2]) {177continue; // We skip degenerate faces, since they're problematic, and Jolt will assert about it anyway.178}179180// Jolt uses a different winding order, so we swap the indices to account for that.181physics_faces.emplace_back((JPH::uint32)physics_face[2], (JPH::uint32)physics_face[1], (JPH::uint32)physics_face[0]);182}183184// Pin whatever pinned vertices we have currently. This is used during the `Optimize` call below to order the185// constraints. Note that it's fine if the pinned vertices change later, but that will reduce the effectiveness186// of the constraints a bit.187pin_vertices(*this, pinned_vertices, mesh_to_physics, physics_vertices);188189// Since Godot's stiffness is input as a coefficient between 0 and 1, and Jolt uses actual stiffness for its190// edge constraints, we crudely map one to the other with an arbitrary constant.191const float stiffness = MAX(Math::pow(stiffness_coefficient, 3.0f) * 100000.0f, 0.000001f);192const float inverse_stiffness = 1.0f / stiffness;193194JPH::SoftBodySharedSettings::VertexAttributes vertex_attrib;195vertex_attrib.mCompliance = vertex_attrib.mShearCompliance = inverse_stiffness;196197settings->CreateConstraints(&vertex_attrib, 1, JPH::SoftBodySharedSettings::EBendType::None);198float multiplier = 1.0f - shrinking_factor;199for (JPH::SoftBodySharedSettings::Edge &e : settings->mEdgeConstraints) {200e.mRestLength *= multiplier;201}202settings->Optimize();203204return settings;205}206207void JoltSoftBody3D::_update_mass() {208if (!in_space()) {209return;210}211212JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());213JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();214215const float inverse_vertex_mass = mass == 0.0f ? 1.0f : (float)physics_vertices.size() / mass;216217for (JPH::SoftBodyVertex &vertex : physics_vertices) {218vertex.mInvMass = inverse_vertex_mass;219}220221pin_vertices(*this, pinned_vertices, mesh_to_physics, physics_vertices);222}223224void JoltSoftBody3D::_update_pressure() {225if (!in_space()) {226jolt_settings->mPressure = pressure;227return;228}229230JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());231motion_properties.SetPressure(pressure);232}233234void JoltSoftBody3D::_update_damping() {235if (!in_space()) {236jolt_settings->mLinearDamping = linear_damping;237return;238}239240JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());241motion_properties.SetLinearDamping(linear_damping);242}243244void JoltSoftBody3D::_update_simulation_precision() {245if (!in_space()) {246jolt_settings->mNumIterations = (JPH::uint32)simulation_precision;247return;248}249250JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());251motion_properties.SetNumIterations((JPH::uint32)simulation_precision);252}253254void JoltSoftBody3D::_update_group_filter() {255JPH::GroupFilter *group_filter = !exceptions.is_empty() ? JoltGroupFilter::instance : nullptr;256257if (!in_space()) {258jolt_settings->mCollisionGroup.SetGroupFilter(group_filter);259} else {260jolt_body->GetCollisionGroup().SetGroupFilter(group_filter);261}262}263264void JoltSoftBody3D::_try_rebuild() {265if (space != nullptr) {266_reset_space();267}268}269270void JoltSoftBody3D::_mesh_changed() {271_try_rebuild();272}273274void JoltSoftBody3D::_simulation_precision_changed() {275wake_up();276}277278void JoltSoftBody3D::_mass_changed() {279wake_up();280}281282void JoltSoftBody3D::_pressure_changed() {283_update_pressure();284wake_up();285}286287void JoltSoftBody3D::_damping_changed() {288_update_damping();289wake_up();290}291292void JoltSoftBody3D::_pins_changed() {293_update_mass();294wake_up();295}296297void JoltSoftBody3D::_vertices_changed() {298wake_up();299}300301void JoltSoftBody3D::_exceptions_changed() {302_update_group_filter();303}304305void JoltSoftBody3D::_motion_changed() {306wake_up();307}308309JoltSoftBody3D::JoltSoftBody3D() :310JoltObject3D(OBJECT_TYPE_SOFT_BODY) {311jolt_settings->mRestitution = 0.0f;312jolt_settings->mFriction = 1.0f;313jolt_settings->mUpdatePosition = true;314jolt_settings->mMakeRotationIdentity = false;315}316317JoltSoftBody3D::~JoltSoftBody3D() {318if (jolt_settings != nullptr) {319delete jolt_settings;320jolt_settings = nullptr;321}322}323324void JoltSoftBody3D::add_collision_exception(const RID &p_excepted_body) {325exceptions.push_back(p_excepted_body);326327_exceptions_changed();328}329330void JoltSoftBody3D::remove_collision_exception(const RID &p_excepted_body) {331exceptions.erase(p_excepted_body);332333_exceptions_changed();334}335336bool JoltSoftBody3D::has_collision_exception(const RID &p_excepted_body) const {337return exceptions.find(p_excepted_body) >= 0;338}339340bool JoltSoftBody3D::can_interact_with(const JoltBody3D &p_other) const {341return (can_collide_with(p_other) || p_other.can_collide_with(*this)) && !has_collision_exception(p_other.get_rid()) && !p_other.has_collision_exception(rid);342}343344bool JoltSoftBody3D::can_interact_with(const JoltSoftBody3D &p_other) const {345return (can_collide_with(p_other) || p_other.can_collide_with(*this)) && !has_collision_exception(p_other.get_rid()) && !p_other.has_collision_exception(rid);346}347348bool JoltSoftBody3D::can_interact_with(const JoltArea3D &p_other) const {349return p_other.can_interact_with(*this);350}351352Vector3 JoltSoftBody3D::get_velocity_at_position(const Vector3 &p_position) const {353return Vector3();354}355356void JoltSoftBody3D::set_mesh(const RID &p_mesh) {357if (unlikely(mesh == p_mesh)) {358return;359}360361mesh = p_mesh;362_mesh_changed();363}364365bool JoltSoftBody3D::is_sleeping() const {366if (!in_space()) {367return false;368} else {369return !jolt_body->IsActive();370}371}372373void JoltSoftBody3D::apply_vertex_impulse(int p_index, const Vector3 &p_impulse) {374ERR_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()));375376ERR_FAIL_INDEX(p_index, (int)mesh_to_physics.size());377const int physics_index = mesh_to_physics[p_index];378ERR_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()));379ERR_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()));380381JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());382383JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();384JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];385386physics_vertex.mVelocity += to_jolt(p_impulse) * physics_vertex.mInvMass;387388_motion_changed();389}390391void JoltSoftBody3D::apply_vertex_force(int p_index, const Vector3 &p_force) {392ERR_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()));393394apply_vertex_impulse(p_index, p_force * space->get_last_step());395}396397void JoltSoftBody3D::apply_central_impulse(const Vector3 &p_impulse) {398ERR_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()));399400JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());401JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();402403const JPH::Vec3 impulse = to_jolt(p_impulse) / physics_vertices.size();404405for (JPH::SoftBodyVertex &physics_vertex : physics_vertices) {406if (physics_vertex.mInvMass > 0.0f) {407physics_vertex.mVelocity += impulse * physics_vertex.mInvMass;408}409}410411_motion_changed();412}413414void JoltSoftBody3D::apply_central_force(const Vector3 &p_force) {415ERR_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()));416417jolt_body->AddForce(to_jolt(p_force));418419_motion_changed();420}421422void JoltSoftBody3D::set_is_sleeping(bool p_enabled) {423if (!in_space()) {424return;425}426427space->set_is_object_sleeping(jolt_body->GetID(), p_enabled);428}429430bool JoltSoftBody3D::is_sleep_allowed() const {431if (!in_space()) {432return jolt_settings->mAllowSleeping;433} else {434return jolt_body->GetAllowSleeping();435}436}437438void JoltSoftBody3D::set_is_sleep_allowed(bool p_enabled) {439if (!in_space()) {440jolt_settings->mAllowSleeping = p_enabled;441} else {442jolt_body->SetAllowSleeping(p_enabled);443}444}445446void JoltSoftBody3D::set_simulation_precision(int p_precision) {447if (unlikely(simulation_precision == p_precision)) {448return;449}450451simulation_precision = MAX(p_precision, 0);452453_simulation_precision_changed();454}455456void JoltSoftBody3D::set_mass(float p_mass) {457if (unlikely(mass == p_mass)) {458return;459}460461mass = MAX(p_mass, 0.0f);462463_mass_changed();464}465466float JoltSoftBody3D::get_stiffness_coefficient() const {467return stiffness_coefficient;468}469470void JoltSoftBody3D::set_stiffness_coefficient(float p_coefficient) {471stiffness_coefficient = CLAMP(p_coefficient, 0.0f, 1.0f);472}473474float JoltSoftBody3D::get_shrinking_factor() const {475return shrinking_factor;476}477478void JoltSoftBody3D::set_shrinking_factor(float p_shrinking_factor) {479shrinking_factor = p_shrinking_factor;480}481482void JoltSoftBody3D::set_pressure(float p_pressure) {483if (unlikely(pressure == p_pressure)) {484return;485}486487pressure = MAX(p_pressure, 0.0f);488489_pressure_changed();490}491492void JoltSoftBody3D::set_linear_damping(float p_damping) {493if (unlikely(linear_damping == p_damping)) {494return;495}496497linear_damping = MAX(p_damping, 0.0f);498499_damping_changed();500}501502float JoltSoftBody3D::get_drag() const {503// Drag is not a thing in Jolt, and not supported by Godot Physics either.504return 0.0f;505}506507void JoltSoftBody3D::set_drag(float p_drag) {508// Drag is not a thing in Jolt, and not supported by Godot Physics either.509}510511Variant JoltSoftBody3D::get_state(PhysicsServer3D::BodyState p_state) const {512switch (p_state) {513case PhysicsServer3D::BODY_STATE_TRANSFORM: {514return get_transform();515}516case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {517ERR_FAIL_V_MSG(Variant(), "Linear velocity is not supported for soft bodies.");518}519case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {520ERR_FAIL_V_MSG(Variant(), "Angular velocity is not supported for soft bodies.");521}522case PhysicsServer3D::BODY_STATE_SLEEPING: {523return is_sleeping();524}525case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {526return is_sleep_allowed();527}528default: {529ERR_FAIL_V_MSG(Variant(), vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));530}531}532}533534void JoltSoftBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_value) {535switch (p_state) {536case PhysicsServer3D::BODY_STATE_TRANSFORM: {537set_transform(p_value);538} break;539case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {540ERR_FAIL_MSG("Linear velocity is not supported for soft bodies.");541} break;542case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {543ERR_FAIL_MSG("Angular velocity is not supported for soft bodies.");544} break;545case PhysicsServer3D::BODY_STATE_SLEEPING: {546set_is_sleeping(p_value);547} break;548case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {549set_is_sleep_allowed(p_value);550} break;551default: {552ERR_FAIL_MSG(vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));553} break;554}555}556557Transform3D JoltSoftBody3D::get_transform() const {558// Since any transform gets baked into the vertices anyway we can just return identity here.559return Transform3D();560}561562void JoltSoftBody3D::set_transform(const Transform3D &p_transform) {563ERR_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()));564565// For whatever reason this has to be interpreted as a relative global-space transform rather than an absolute one,566// because `SoftBody3D` will immediately upon entering the scene tree set itself to be top-level and also set its567// transform to be identity, while still expecting to stay in its original position.568//569// We also discard any scaling, since we have no way of scaling the actual edge lengths.570const JPH::Mat44 relative_transform = to_jolt(p_transform.orthonormalized());571572// The translation delta goes to the body's position to avoid vertices getting too far away from it.573JPH::BodyInterface &body_iface = space->get_body_iface();574body_iface.SetPosition(jolt_body->GetID(), jolt_body->GetPosition() + relative_transform.GetTranslation(), JPH::EActivation::DontActivate);575576// The rotation difference goes to the vertices. We also reset the velocity of these vertices.577JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());578JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();579580for (JPH::SoftBodyVertex &vertex : physics_vertices) {581vertex.mPosition = vertex.mPreviousPosition = relative_transform.Multiply3x3(vertex.mPosition);582vertex.mVelocity = JPH::Vec3::sZero();583}584wake_up();585}586587AABB JoltSoftBody3D::get_bounds() const {588ERR_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()));589return to_godot(jolt_body->GetWorldSpaceBounds());590}591592void JoltSoftBody3D::update_rendering_server(PhysicsServer3DRenderingServerHandler *p_rendering_server_handler) {593// 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.594if (unlikely(!in_space())) {595return;596}597598const JPH::SoftBodyMotionProperties &motion_properties = static_cast<const JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());599600typedef JPH::SoftBodyMotionProperties::Vertex SoftBodyVertex;601typedef JPH::SoftBodyMotionProperties::Face SoftBodyFace;602603const JPH::Array<SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();604const JPH::Array<SoftBodyFace> &physics_faces = motion_properties.GetFaces();605606const int physics_vertex_count = (int)physics_vertices.size();607608normals.clear();609normals.resize(physics_vertex_count);610611// Compute vertex normals using smooth-shading:612// Each vertex should use the average normal of all faces it is a part of.613// Iterate over each face, and add the face normal to each of the face vertices.614// By the end of the loop, each vertex normal will be the sum of all face normals it belongs to.615for (const SoftBodyFace &physics_face : physics_faces) {616// Jolt uses a different winding order, so we swap the indices to account for that.617618const uint32_t i0 = physics_face.mVertex[2];619const uint32_t i1 = physics_face.mVertex[1];620const uint32_t i2 = physics_face.mVertex[0];621622const Vector3 v0 = to_godot(physics_vertices[i0].mPosition);623const Vector3 v1 = to_godot(physics_vertices[i1].mPosition);624const Vector3 v2 = to_godot(physics_vertices[i2].mPosition);625626const Vector3 normal = (v2 - v0).cross(v1 - v0).normalized();627628normals[i0] += normal;629normals[i1] += normal;630normals[i2] += normal;631}632// Normalize the vertex normals to have length 1.0633for (Vector3 &n : normals) {634real_t len = n.length();635// Some normals may have length 0 if the face was degenerate,636// so don't divide by zero.637if (len > CMP_EPSILON) {638n /= len;639}640}641642const int mesh_vertex_count = mesh_to_physics.size();643const JPH::RVec3 body_position = jolt_body->GetCenterOfMassPosition();644645for (int i = 0; i < mesh_vertex_count; ++i) {646const int physics_index = mesh_to_physics[i];647if (physics_index >= 0) {648const Vector3 vertex = to_godot(body_position + physics_vertices[(size_t)physics_index].mPosition);649const Vector3 normal = normals[(uint32_t)physics_index];650651p_rendering_server_handler->set_vertex(i, vertex);652p_rendering_server_handler->set_normal(i, normal);653}654}655656p_rendering_server_handler->set_aabb(get_bounds());657}658659Vector3 JoltSoftBody3D::get_vertex_position(int p_index) {660ERR_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()));661662ERR_FAIL_INDEX_V(p_index, (int)mesh_to_physics.size(), Vector3());663const int physics_index = mesh_to_physics[p_index];664ERR_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()));665666const JPH::SoftBodyMotionProperties &motion_properties = static_cast<const JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());667const JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();668const JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];669670return to_godot(jolt_body->GetCenterOfMassPosition() + physics_vertex.mPosition);671}672673void JoltSoftBody3D::set_vertex_position(int p_index, const Vector3 &p_position) {674ERR_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()));675676ERR_FAIL_INDEX(p_index, (int)mesh_to_physics.size());677const int physics_index = mesh_to_physics[p_index];678ERR_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()));679680JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());681JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();682JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];683684const JPH::RVec3 center_of_mass = jolt_body->GetCenterOfMassPosition();685physics_vertex.mPosition = JPH::Vec3(to_jolt_r(p_position) - center_of_mass);686687_vertices_changed();688}689690void JoltSoftBody3D::pin_vertex(int p_index) {691pinned_vertices.insert(p_index);692693_pins_changed();694}695696void JoltSoftBody3D::unpin_vertex(int p_index) {697pinned_vertices.erase(p_index);698699_pins_changed();700}701702void JoltSoftBody3D::unpin_all_vertices() {703pinned_vertices.clear();704705_pins_changed();706}707708bool JoltSoftBody3D::is_vertex_pinned(int p_index) const {709ERR_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()));710711ERR_FAIL_INDEX_V(p_index, (int)mesh_to_physics.size(), false);712const int physics_index = mesh_to_physics[p_index];713714return pinned_vertices.has(physics_index);715}716717718