99acec63f1
This updates our local copy to commit 5ec8339b6fc491e3f09a34a4516e82787f053fcc. We need a recent master commit for some new features that we use in Godot (see #25543 and #28909). To avoid warnings generated by Bullet headers included in our own module, we include those headers with -isystem on GCC and Clang. Fixes #29503.
3700 lines
95 KiB
C++
3700 lines
95 KiB
C++
/*
|
|
Bullet Continuous Collision Detection and Physics Library
|
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
In no event will the authors be held liable for any damages arising from the use of this software.
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it freely,
|
|
subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
///btSoftBody implementation by Nathanael Presson
|
|
|
|
#include "btSoftBodyInternals.h"
|
|
#include "BulletSoftBody/btSoftBodySolvers.h"
|
|
#include "btSoftBodyData.h"
|
|
#include "LinearMath/btSerializer.h"
|
|
#include "BulletDynamics/Featherstone/btMultiBodyLinkCollider.h"
|
|
#include "BulletDynamics/Featherstone/btMultiBodyConstraint.h"
|
|
|
|
//
|
|
btSoftBody::btSoftBody(btSoftBodyWorldInfo* worldInfo, int node_count, const btVector3* x, const btScalar* m)
|
|
: m_softBodySolver(0), m_worldInfo(worldInfo)
|
|
{
|
|
/* Init */
|
|
initDefaults();
|
|
|
|
/* Default material */
|
|
Material* pm = appendMaterial();
|
|
pm->m_kLST = 1;
|
|
pm->m_kAST = 1;
|
|
pm->m_kVST = 1;
|
|
pm->m_flags = fMaterial::Default;
|
|
|
|
/* Nodes */
|
|
const btScalar margin = getCollisionShape()->getMargin();
|
|
m_nodes.resize(node_count);
|
|
for (int i = 0, ni = node_count; i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
ZeroInitialize(n);
|
|
n.m_x = x ? *x++ : btVector3(0, 0, 0);
|
|
n.m_q = n.m_x;
|
|
n.m_im = m ? *m++ : 1;
|
|
n.m_im = n.m_im > 0 ? 1 / n.m_im : 0;
|
|
n.m_leaf = m_ndbvt.insert(btDbvtVolume::FromCR(n.m_x, margin), &n);
|
|
n.m_material = pm;
|
|
}
|
|
updateBounds();
|
|
}
|
|
|
|
btSoftBody::btSoftBody(btSoftBodyWorldInfo* worldInfo)
|
|
: m_worldInfo(worldInfo)
|
|
{
|
|
initDefaults();
|
|
}
|
|
|
|
void btSoftBody::initDefaults()
|
|
{
|
|
m_internalType = CO_SOFT_BODY;
|
|
m_cfg.aeromodel = eAeroModel::V_Point;
|
|
m_cfg.kVCF = 1;
|
|
m_cfg.kDG = 0;
|
|
m_cfg.kLF = 0;
|
|
m_cfg.kDP = 0;
|
|
m_cfg.kPR = 0;
|
|
m_cfg.kVC = 0;
|
|
m_cfg.kDF = (btScalar)0.2;
|
|
m_cfg.kMT = 0;
|
|
m_cfg.kCHR = (btScalar)1.0;
|
|
m_cfg.kKHR = (btScalar)0.1;
|
|
m_cfg.kSHR = (btScalar)1.0;
|
|
m_cfg.kAHR = (btScalar)0.7;
|
|
m_cfg.kSRHR_CL = (btScalar)0.1;
|
|
m_cfg.kSKHR_CL = (btScalar)1;
|
|
m_cfg.kSSHR_CL = (btScalar)0.5;
|
|
m_cfg.kSR_SPLT_CL = (btScalar)0.5;
|
|
m_cfg.kSK_SPLT_CL = (btScalar)0.5;
|
|
m_cfg.kSS_SPLT_CL = (btScalar)0.5;
|
|
m_cfg.maxvolume = (btScalar)1;
|
|
m_cfg.timescale = 1;
|
|
m_cfg.viterations = 0;
|
|
m_cfg.piterations = 1;
|
|
m_cfg.diterations = 0;
|
|
m_cfg.citerations = 4;
|
|
m_cfg.collisions = fCollision::Default;
|
|
m_pose.m_bvolume = false;
|
|
m_pose.m_bframe = false;
|
|
m_pose.m_volume = 0;
|
|
m_pose.m_com = btVector3(0, 0, 0);
|
|
m_pose.m_rot.setIdentity();
|
|
m_pose.m_scl.setIdentity();
|
|
m_tag = 0;
|
|
m_timeacc = 0;
|
|
m_bUpdateRtCst = true;
|
|
m_bounds[0] = btVector3(0, 0, 0);
|
|
m_bounds[1] = btVector3(0, 0, 0);
|
|
m_worldTransform.setIdentity();
|
|
setSolver(eSolverPresets::Positions);
|
|
|
|
/* Collision shape */
|
|
///for now, create a collision shape internally
|
|
m_collisionShape = new btSoftBodyCollisionShape(this);
|
|
m_collisionShape->setMargin(0.25f);
|
|
|
|
m_initialWorldTransform.setIdentity();
|
|
|
|
m_windVelocity = btVector3(0, 0, 0);
|
|
m_restLengthScale = btScalar(1.0);
|
|
}
|
|
|
|
//
|
|
btSoftBody::~btSoftBody()
|
|
{
|
|
//for now, delete the internal shape
|
|
delete m_collisionShape;
|
|
int i;
|
|
|
|
releaseClusters();
|
|
for (i = 0; i < m_materials.size(); ++i)
|
|
btAlignedFree(m_materials[i]);
|
|
for (i = 0; i < m_joints.size(); ++i)
|
|
btAlignedFree(m_joints[i]);
|
|
}
|
|
|
|
//
|
|
bool btSoftBody::checkLink(int node0, int node1) const
|
|
{
|
|
return (checkLink(&m_nodes[node0], &m_nodes[node1]));
|
|
}
|
|
|
|
//
|
|
bool btSoftBody::checkLink(const Node* node0, const Node* node1) const
|
|
{
|
|
const Node* n[] = {node0, node1};
|
|
for (int i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
const Link& l = m_links[i];
|
|
if ((l.m_n[0] == n[0] && l.m_n[1] == n[1]) ||
|
|
(l.m_n[0] == n[1] && l.m_n[1] == n[0]))
|
|
{
|
|
return (true);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
bool btSoftBody::checkFace(int node0, int node1, int node2) const
|
|
{
|
|
const Node* n[] = {&m_nodes[node0],
|
|
&m_nodes[node1],
|
|
&m_nodes[node2]};
|
|
for (int i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
const Face& f = m_faces[i];
|
|
int c = 0;
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
if ((f.m_n[j] == n[0]) ||
|
|
(f.m_n[j] == n[1]) ||
|
|
(f.m_n[j] == n[2]))
|
|
c |= 1 << j;
|
|
else
|
|
break;
|
|
}
|
|
if (c == 7) return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
btSoftBody::Material* btSoftBody::appendMaterial()
|
|
{
|
|
Material* pm = new (btAlignedAlloc(sizeof(Material), 16)) Material();
|
|
if (m_materials.size() > 0)
|
|
*pm = *m_materials[0];
|
|
else
|
|
ZeroInitialize(*pm);
|
|
m_materials.push_back(pm);
|
|
return (pm);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendNote(const char* text,
|
|
const btVector3& o,
|
|
const btVector4& c,
|
|
Node* n0,
|
|
Node* n1,
|
|
Node* n2,
|
|
Node* n3)
|
|
{
|
|
Note n;
|
|
ZeroInitialize(n);
|
|
n.m_rank = 0;
|
|
n.m_text = text;
|
|
n.m_offset = o;
|
|
n.m_coords[0] = c.x();
|
|
n.m_coords[1] = c.y();
|
|
n.m_coords[2] = c.z();
|
|
n.m_coords[3] = c.w();
|
|
n.m_nodes[0] = n0;
|
|
n.m_rank += n0 ? 1 : 0;
|
|
n.m_nodes[1] = n1;
|
|
n.m_rank += n1 ? 1 : 0;
|
|
n.m_nodes[2] = n2;
|
|
n.m_rank += n2 ? 1 : 0;
|
|
n.m_nodes[3] = n3;
|
|
n.m_rank += n3 ? 1 : 0;
|
|
m_notes.push_back(n);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendNote(const char* text,
|
|
const btVector3& o,
|
|
Node* feature)
|
|
{
|
|
appendNote(text, o, btVector4(1, 0, 0, 0), feature);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendNote(const char* text,
|
|
const btVector3& o,
|
|
Link* feature)
|
|
{
|
|
static const btScalar w = 1 / (btScalar)2;
|
|
appendNote(text, o, btVector4(w, w, 0, 0), feature->m_n[0],
|
|
feature->m_n[1]);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendNote(const char* text,
|
|
const btVector3& o,
|
|
Face* feature)
|
|
{
|
|
static const btScalar w = 1 / (btScalar)3;
|
|
appendNote(text, o, btVector4(w, w, w, 0), feature->m_n[0],
|
|
feature->m_n[1],
|
|
feature->m_n[2]);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendNode(const btVector3& x, btScalar m)
|
|
{
|
|
if (m_nodes.capacity() == m_nodes.size())
|
|
{
|
|
pointersToIndices();
|
|
m_nodes.reserve(m_nodes.size() * 2 + 1);
|
|
indicesToPointers();
|
|
}
|
|
const btScalar margin = getCollisionShape()->getMargin();
|
|
m_nodes.push_back(Node());
|
|
Node& n = m_nodes[m_nodes.size() - 1];
|
|
ZeroInitialize(n);
|
|
n.m_x = x;
|
|
n.m_q = n.m_x;
|
|
n.m_im = m > 0 ? 1 / m : 0;
|
|
n.m_material = m_materials[0];
|
|
n.m_leaf = m_ndbvt.insert(btDbvtVolume::FromCR(n.m_x, margin), &n);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendLink(int model, Material* mat)
|
|
{
|
|
Link l;
|
|
if (model >= 0)
|
|
l = m_links[model];
|
|
else
|
|
{
|
|
ZeroInitialize(l);
|
|
l.m_material = mat ? mat : m_materials[0];
|
|
}
|
|
m_links.push_back(l);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendLink(int node0,
|
|
int node1,
|
|
Material* mat,
|
|
bool bcheckexist)
|
|
{
|
|
appendLink(&m_nodes[node0], &m_nodes[node1], mat, bcheckexist);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendLink(Node* node0,
|
|
Node* node1,
|
|
Material* mat,
|
|
bool bcheckexist)
|
|
{
|
|
if ((!bcheckexist) || (!checkLink(node0, node1)))
|
|
{
|
|
appendLink(-1, mat);
|
|
Link& l = m_links[m_links.size() - 1];
|
|
l.m_n[0] = node0;
|
|
l.m_n[1] = node1;
|
|
l.m_rl = (l.m_n[0]->m_x - l.m_n[1]->m_x).length();
|
|
m_bUpdateRtCst = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendFace(int model, Material* mat)
|
|
{
|
|
Face f;
|
|
if (model >= 0)
|
|
{
|
|
f = m_faces[model];
|
|
}
|
|
else
|
|
{
|
|
ZeroInitialize(f);
|
|
f.m_material = mat ? mat : m_materials[0];
|
|
}
|
|
m_faces.push_back(f);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendFace(int node0, int node1, int node2, Material* mat)
|
|
{
|
|
if (node0 == node1)
|
|
return;
|
|
if (node1 == node2)
|
|
return;
|
|
if (node2 == node0)
|
|
return;
|
|
|
|
appendFace(-1, mat);
|
|
Face& f = m_faces[m_faces.size() - 1];
|
|
btAssert(node0 != node1);
|
|
btAssert(node1 != node2);
|
|
btAssert(node2 != node0);
|
|
f.m_n[0] = &m_nodes[node0];
|
|
f.m_n[1] = &m_nodes[node1];
|
|
f.m_n[2] = &m_nodes[node2];
|
|
f.m_ra = AreaOf(f.m_n[0]->m_x,
|
|
f.m_n[1]->m_x,
|
|
f.m_n[2]->m_x);
|
|
m_bUpdateRtCst = true;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendTetra(int model, Material* mat)
|
|
{
|
|
Tetra t;
|
|
if (model >= 0)
|
|
t = m_tetras[model];
|
|
else
|
|
{
|
|
ZeroInitialize(t);
|
|
t.m_material = mat ? mat : m_materials[0];
|
|
}
|
|
m_tetras.push_back(t);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendTetra(int node0,
|
|
int node1,
|
|
int node2,
|
|
int node3,
|
|
Material* mat)
|
|
{
|
|
appendTetra(-1, mat);
|
|
Tetra& t = m_tetras[m_tetras.size() - 1];
|
|
t.m_n[0] = &m_nodes[node0];
|
|
t.m_n[1] = &m_nodes[node1];
|
|
t.m_n[2] = &m_nodes[node2];
|
|
t.m_n[3] = &m_nodes[node3];
|
|
t.m_rv = VolumeOf(t.m_n[0]->m_x, t.m_n[1]->m_x, t.m_n[2]->m_x, t.m_n[3]->m_x);
|
|
m_bUpdateRtCst = true;
|
|
}
|
|
|
|
//
|
|
|
|
void btSoftBody::appendAnchor(int node, btRigidBody* body, bool disableCollisionBetweenLinkedBodies, btScalar influence)
|
|
{
|
|
btVector3 local = body->getWorldTransform().inverse() * m_nodes[node].m_x;
|
|
appendAnchor(node, body, local, disableCollisionBetweenLinkedBodies, influence);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendAnchor(int node, btRigidBody* body, const btVector3& localPivot, bool disableCollisionBetweenLinkedBodies, btScalar influence)
|
|
{
|
|
if (disableCollisionBetweenLinkedBodies)
|
|
{
|
|
if (m_collisionDisabledObjects.findLinearSearch(body) == m_collisionDisabledObjects.size())
|
|
{
|
|
m_collisionDisabledObjects.push_back(body);
|
|
}
|
|
}
|
|
|
|
Anchor a;
|
|
a.m_node = &m_nodes[node];
|
|
a.m_body = body;
|
|
a.m_local = localPivot;
|
|
a.m_node->m_battach = 1;
|
|
a.m_influence = influence;
|
|
m_anchors.push_back(a);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendLinearJoint(const LJoint::Specs& specs, Cluster* body0, Body body1)
|
|
{
|
|
LJoint* pj = new (btAlignedAlloc(sizeof(LJoint), 16)) LJoint();
|
|
pj->m_bodies[0] = body0;
|
|
pj->m_bodies[1] = body1;
|
|
pj->m_refs[0] = pj->m_bodies[0].xform().inverse() * specs.position;
|
|
pj->m_refs[1] = pj->m_bodies[1].xform().inverse() * specs.position;
|
|
pj->m_cfm = specs.cfm;
|
|
pj->m_erp = specs.erp;
|
|
pj->m_split = specs.split;
|
|
m_joints.push_back(pj);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendLinearJoint(const LJoint::Specs& specs, Body body)
|
|
{
|
|
appendLinearJoint(specs, m_clusters[0], body);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendLinearJoint(const LJoint::Specs& specs, btSoftBody* body)
|
|
{
|
|
appendLinearJoint(specs, m_clusters[0], body->m_clusters[0]);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendAngularJoint(const AJoint::Specs& specs, Cluster* body0, Body body1)
|
|
{
|
|
AJoint* pj = new (btAlignedAlloc(sizeof(AJoint), 16)) AJoint();
|
|
pj->m_bodies[0] = body0;
|
|
pj->m_bodies[1] = body1;
|
|
pj->m_refs[0] = pj->m_bodies[0].xform().inverse().getBasis() * specs.axis;
|
|
pj->m_refs[1] = pj->m_bodies[1].xform().inverse().getBasis() * specs.axis;
|
|
pj->m_cfm = specs.cfm;
|
|
pj->m_erp = specs.erp;
|
|
pj->m_split = specs.split;
|
|
pj->m_icontrol = specs.icontrol;
|
|
m_joints.push_back(pj);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendAngularJoint(const AJoint::Specs& specs, Body body)
|
|
{
|
|
appendAngularJoint(specs, m_clusters[0], body);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::appendAngularJoint(const AJoint::Specs& specs, btSoftBody* body)
|
|
{
|
|
appendAngularJoint(specs, m_clusters[0], body->m_clusters[0]);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::addForce(const btVector3& force)
|
|
{
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i) addForce(force, i);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::addForce(const btVector3& force, int node)
|
|
{
|
|
Node& n = m_nodes[node];
|
|
if (n.m_im > 0)
|
|
{
|
|
n.m_f += force;
|
|
}
|
|
}
|
|
|
|
void btSoftBody::addAeroForceToNode(const btVector3& windVelocity, int nodeIndex)
|
|
{
|
|
btAssert(nodeIndex >= 0 && nodeIndex < m_nodes.size());
|
|
|
|
const btScalar dt = m_sst.sdt;
|
|
const btScalar kLF = m_cfg.kLF;
|
|
const btScalar kDG = m_cfg.kDG;
|
|
//const btScalar kPR = m_cfg.kPR;
|
|
//const btScalar kVC = m_cfg.kVC;
|
|
const bool as_lift = kLF > 0;
|
|
const bool as_drag = kDG > 0;
|
|
const bool as_aero = as_lift || as_drag;
|
|
const bool as_vaero = as_aero && (m_cfg.aeromodel < btSoftBody::eAeroModel::F_TwoSided);
|
|
|
|
Node& n = m_nodes[nodeIndex];
|
|
|
|
if (n.m_im > 0)
|
|
{
|
|
btSoftBody::sMedium medium;
|
|
|
|
EvaluateMedium(m_worldInfo, n.m_x, medium);
|
|
medium.m_velocity = windVelocity;
|
|
medium.m_density = m_worldInfo->air_density;
|
|
|
|
/* Aerodynamics */
|
|
if (as_vaero)
|
|
{
|
|
const btVector3 rel_v = n.m_v - medium.m_velocity;
|
|
const btScalar rel_v_len = rel_v.length();
|
|
const btScalar rel_v2 = rel_v.length2();
|
|
|
|
if (rel_v2 > SIMD_EPSILON)
|
|
{
|
|
const btVector3 rel_v_nrm = rel_v.normalized();
|
|
btVector3 nrm = n.m_n;
|
|
|
|
if (m_cfg.aeromodel == btSoftBody::eAeroModel::V_TwoSidedLiftDrag)
|
|
{
|
|
nrm *= (btScalar)((btDot(nrm, rel_v) < 0) ? -1 : +1);
|
|
btVector3 fDrag(0, 0, 0);
|
|
btVector3 fLift(0, 0, 0);
|
|
|
|
btScalar n_dot_v = nrm.dot(rel_v_nrm);
|
|
btScalar tri_area = 0.5f * n.m_area;
|
|
|
|
fDrag = 0.5f * kDG * medium.m_density * rel_v2 * tri_area * n_dot_v * (-rel_v_nrm);
|
|
|
|
// Check angle of attack
|
|
// cos(10°) = 0.98480
|
|
if (0 < n_dot_v && n_dot_v < 0.98480f)
|
|
fLift = 0.5f * kLF * medium.m_density * rel_v_len * tri_area * btSqrt(1.0f - n_dot_v * n_dot_v) * (nrm.cross(rel_v_nrm).cross(rel_v_nrm));
|
|
|
|
// Check if the velocity change resulted by aero drag force exceeds the current velocity of the node.
|
|
btVector3 del_v_by_fDrag = fDrag * n.m_im * m_sst.sdt;
|
|
btScalar del_v_by_fDrag_len2 = del_v_by_fDrag.length2();
|
|
btScalar v_len2 = n.m_v.length2();
|
|
|
|
if (del_v_by_fDrag_len2 >= v_len2 && del_v_by_fDrag_len2 > 0)
|
|
{
|
|
btScalar del_v_by_fDrag_len = del_v_by_fDrag.length();
|
|
btScalar v_len = n.m_v.length();
|
|
fDrag *= btScalar(0.8) * (v_len / del_v_by_fDrag_len);
|
|
}
|
|
|
|
n.m_f += fDrag;
|
|
n.m_f += fLift;
|
|
}
|
|
else if (m_cfg.aeromodel == btSoftBody::eAeroModel::V_Point || m_cfg.aeromodel == btSoftBody::eAeroModel::V_OneSided || m_cfg.aeromodel == btSoftBody::eAeroModel::V_TwoSided)
|
|
{
|
|
if (m_cfg.aeromodel == btSoftBody::eAeroModel::V_TwoSided)
|
|
nrm *= (btScalar)((btDot(nrm, rel_v) < 0) ? -1 : +1);
|
|
|
|
const btScalar dvn = btDot(rel_v, nrm);
|
|
/* Compute forces */
|
|
if (dvn > 0)
|
|
{
|
|
btVector3 force(0, 0, 0);
|
|
const btScalar c0 = n.m_area * dvn * rel_v2 / 2;
|
|
const btScalar c1 = c0 * medium.m_density;
|
|
force += nrm * (-c1 * kLF);
|
|
force += rel_v.normalized() * (-c1 * kDG);
|
|
ApplyClampedForce(n, force, dt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void btSoftBody::addAeroForceToFace(const btVector3& windVelocity, int faceIndex)
|
|
{
|
|
const btScalar dt = m_sst.sdt;
|
|
const btScalar kLF = m_cfg.kLF;
|
|
const btScalar kDG = m_cfg.kDG;
|
|
// const btScalar kPR = m_cfg.kPR;
|
|
// const btScalar kVC = m_cfg.kVC;
|
|
const bool as_lift = kLF > 0;
|
|
const bool as_drag = kDG > 0;
|
|
const bool as_aero = as_lift || as_drag;
|
|
const bool as_faero = as_aero && (m_cfg.aeromodel >= btSoftBody::eAeroModel::F_TwoSided);
|
|
|
|
if (as_faero)
|
|
{
|
|
btSoftBody::Face& f = m_faces[faceIndex];
|
|
|
|
btSoftBody::sMedium medium;
|
|
|
|
const btVector3 v = (f.m_n[0]->m_v + f.m_n[1]->m_v + f.m_n[2]->m_v) / 3;
|
|
const btVector3 x = (f.m_n[0]->m_x + f.m_n[1]->m_x + f.m_n[2]->m_x) / 3;
|
|
EvaluateMedium(m_worldInfo, x, medium);
|
|
medium.m_velocity = windVelocity;
|
|
medium.m_density = m_worldInfo->air_density;
|
|
const btVector3 rel_v = v - medium.m_velocity;
|
|
const btScalar rel_v_len = rel_v.length();
|
|
const btScalar rel_v2 = rel_v.length2();
|
|
|
|
if (rel_v2 > SIMD_EPSILON)
|
|
{
|
|
const btVector3 rel_v_nrm = rel_v.normalized();
|
|
btVector3 nrm = f.m_normal;
|
|
|
|
if (m_cfg.aeromodel == btSoftBody::eAeroModel::F_TwoSidedLiftDrag)
|
|
{
|
|
nrm *= (btScalar)((btDot(nrm, rel_v) < 0) ? -1 : +1);
|
|
|
|
btVector3 fDrag(0, 0, 0);
|
|
btVector3 fLift(0, 0, 0);
|
|
|
|
btScalar n_dot_v = nrm.dot(rel_v_nrm);
|
|
btScalar tri_area = 0.5f * f.m_ra;
|
|
|
|
fDrag = 0.5f * kDG * medium.m_density * rel_v2 * tri_area * n_dot_v * (-rel_v_nrm);
|
|
|
|
// Check angle of attack
|
|
// cos(10°) = 0.98480
|
|
if (0 < n_dot_v && n_dot_v < 0.98480f)
|
|
fLift = 0.5f * kLF * medium.m_density * rel_v_len * tri_area * btSqrt(1.0f - n_dot_v * n_dot_v) * (nrm.cross(rel_v_nrm).cross(rel_v_nrm));
|
|
|
|
fDrag /= 3;
|
|
fLift /= 3;
|
|
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
if (f.m_n[j]->m_im > 0)
|
|
{
|
|
// Check if the velocity change resulted by aero drag force exceeds the current velocity of the node.
|
|
btVector3 del_v_by_fDrag = fDrag * f.m_n[j]->m_im * m_sst.sdt;
|
|
btScalar del_v_by_fDrag_len2 = del_v_by_fDrag.length2();
|
|
btScalar v_len2 = f.m_n[j]->m_v.length2();
|
|
|
|
if (del_v_by_fDrag_len2 >= v_len2 && del_v_by_fDrag_len2 > 0)
|
|
{
|
|
btScalar del_v_by_fDrag_len = del_v_by_fDrag.length();
|
|
btScalar v_len = f.m_n[j]->m_v.length();
|
|
fDrag *= btScalar(0.8) * (v_len / del_v_by_fDrag_len);
|
|
}
|
|
|
|
f.m_n[j]->m_f += fDrag;
|
|
f.m_n[j]->m_f += fLift;
|
|
}
|
|
}
|
|
}
|
|
else if (m_cfg.aeromodel == btSoftBody::eAeroModel::F_OneSided || m_cfg.aeromodel == btSoftBody::eAeroModel::F_TwoSided)
|
|
{
|
|
if (m_cfg.aeromodel == btSoftBody::eAeroModel::F_TwoSided)
|
|
nrm *= (btScalar)((btDot(nrm, rel_v) < 0) ? -1 : +1);
|
|
|
|
const btScalar dvn = btDot(rel_v, nrm);
|
|
/* Compute forces */
|
|
if (dvn > 0)
|
|
{
|
|
btVector3 force(0, 0, 0);
|
|
const btScalar c0 = f.m_ra * dvn * rel_v2;
|
|
const btScalar c1 = c0 * medium.m_density;
|
|
force += nrm * (-c1 * kLF);
|
|
force += rel_v.normalized() * (-c1 * kDG);
|
|
force /= 3;
|
|
for (int j = 0; j < 3; ++j) ApplyClampedForce(*f.m_n[j], force, dt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::addVelocity(const btVector3& velocity)
|
|
{
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i) addVelocity(velocity, i);
|
|
}
|
|
|
|
/* Set velocity for the entire body */
|
|
void btSoftBody::setVelocity(const btVector3& velocity)
|
|
{
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
if (n.m_im > 0)
|
|
{
|
|
n.m_v = velocity;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::addVelocity(const btVector3& velocity, int node)
|
|
{
|
|
Node& n = m_nodes[node];
|
|
if (n.m_im > 0)
|
|
{
|
|
n.m_v += velocity;
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setMass(int node, btScalar mass)
|
|
{
|
|
m_nodes[node].m_im = mass > 0 ? 1 / mass : 0;
|
|
m_bUpdateRtCst = true;
|
|
}
|
|
|
|
//
|
|
btScalar btSoftBody::getMass(int node) const
|
|
{
|
|
return (m_nodes[node].m_im > 0 ? 1 / m_nodes[node].m_im : 0);
|
|
}
|
|
|
|
//
|
|
btScalar btSoftBody::getTotalMass() const
|
|
{
|
|
btScalar mass = 0;
|
|
for (int i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
mass += getMass(i);
|
|
}
|
|
return (mass);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setTotalMass(btScalar mass, bool fromfaces)
|
|
{
|
|
int i;
|
|
|
|
if (fromfaces)
|
|
{
|
|
for (i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
m_nodes[i].m_im = 0;
|
|
}
|
|
for (i = 0; i < m_faces.size(); ++i)
|
|
{
|
|
const Face& f = m_faces[i];
|
|
const btScalar twicearea = AreaOf(f.m_n[0]->m_x,
|
|
f.m_n[1]->m_x,
|
|
f.m_n[2]->m_x);
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
f.m_n[j]->m_im += twicearea;
|
|
}
|
|
}
|
|
for (i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
m_nodes[i].m_im = 1 / m_nodes[i].m_im;
|
|
}
|
|
}
|
|
const btScalar tm = getTotalMass();
|
|
const btScalar itm = 1 / tm;
|
|
for (i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
m_nodes[i].m_im /= itm * mass;
|
|
}
|
|
m_bUpdateRtCst = true;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setTotalDensity(btScalar density)
|
|
{
|
|
setTotalMass(getVolume() * density, true);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setVolumeMass(btScalar mass)
|
|
{
|
|
btAlignedObjectArray<btScalar> ranks;
|
|
ranks.resize(m_nodes.size(), 0);
|
|
int i;
|
|
|
|
for (i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
m_nodes[i].m_im = 0;
|
|
}
|
|
for (i = 0; i < m_tetras.size(); ++i)
|
|
{
|
|
const Tetra& t = m_tetras[i];
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
t.m_n[j]->m_im += btFabs(t.m_rv);
|
|
ranks[int(t.m_n[j] - &m_nodes[0])] += 1;
|
|
}
|
|
}
|
|
for (i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
if (m_nodes[i].m_im > 0)
|
|
{
|
|
m_nodes[i].m_im = ranks[i] / m_nodes[i].m_im;
|
|
}
|
|
}
|
|
setTotalMass(mass, false);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setVolumeDensity(btScalar density)
|
|
{
|
|
btScalar volume = 0;
|
|
for (int i = 0; i < m_tetras.size(); ++i)
|
|
{
|
|
const Tetra& t = m_tetras[i];
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
volume += btFabs(t.m_rv);
|
|
}
|
|
}
|
|
setVolumeMass(volume * density / 6);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::transform(const btTransform& trs)
|
|
{
|
|
const btScalar margin = getCollisionShape()->getMargin();
|
|
ATTRIBUTE_ALIGNED16(btDbvtVolume)
|
|
vol;
|
|
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
n.m_x = trs * n.m_x;
|
|
n.m_q = trs * n.m_q;
|
|
n.m_n = trs.getBasis() * n.m_n;
|
|
vol = btDbvtVolume::FromCR(n.m_x, margin);
|
|
|
|
m_ndbvt.update(n.m_leaf, vol);
|
|
}
|
|
updateNormals();
|
|
updateBounds();
|
|
updateConstants();
|
|
m_initialWorldTransform = trs;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::translate(const btVector3& trs)
|
|
{
|
|
btTransform t;
|
|
t.setIdentity();
|
|
t.setOrigin(trs);
|
|
transform(t);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::rotate(const btQuaternion& rot)
|
|
{
|
|
btTransform t;
|
|
t.setIdentity();
|
|
t.setRotation(rot);
|
|
transform(t);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::scale(const btVector3& scl)
|
|
{
|
|
const btScalar margin = getCollisionShape()->getMargin();
|
|
ATTRIBUTE_ALIGNED16(btDbvtVolume)
|
|
vol;
|
|
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
n.m_x *= scl;
|
|
n.m_q *= scl;
|
|
vol = btDbvtVolume::FromCR(n.m_x, margin);
|
|
m_ndbvt.update(n.m_leaf, vol);
|
|
}
|
|
updateNormals();
|
|
updateBounds();
|
|
updateConstants();
|
|
}
|
|
|
|
//
|
|
btScalar btSoftBody::getRestLengthScale()
|
|
{
|
|
return m_restLengthScale;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setRestLengthScale(btScalar restLengthScale)
|
|
{
|
|
for (int i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
Link& l = m_links[i];
|
|
l.m_rl = l.m_rl / m_restLengthScale * restLengthScale;
|
|
l.m_c1 = l.m_rl * l.m_rl;
|
|
}
|
|
m_restLengthScale = restLengthScale;
|
|
|
|
if (getActivationState() == ISLAND_SLEEPING)
|
|
activate();
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setPose(bool bvolume, bool bframe)
|
|
{
|
|
m_pose.m_bvolume = bvolume;
|
|
m_pose.m_bframe = bframe;
|
|
int i, ni;
|
|
|
|
/* Weights */
|
|
const btScalar omass = getTotalMass();
|
|
const btScalar kmass = omass * m_nodes.size() * 1000;
|
|
btScalar tmass = omass;
|
|
m_pose.m_wgh.resize(m_nodes.size());
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
if (m_nodes[i].m_im <= 0) tmass += kmass;
|
|
}
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
m_pose.m_wgh[i] = n.m_im > 0 ? 1 / (m_nodes[i].m_im * tmass) : kmass / tmass;
|
|
}
|
|
/* Pos */
|
|
const btVector3 com = evaluateCom();
|
|
m_pose.m_pos.resize(m_nodes.size());
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
m_pose.m_pos[i] = m_nodes[i].m_x - com;
|
|
}
|
|
m_pose.m_volume = bvolume ? getVolume() : 0;
|
|
m_pose.m_com = com;
|
|
m_pose.m_rot.setIdentity();
|
|
m_pose.m_scl.setIdentity();
|
|
/* Aqq */
|
|
m_pose.m_aqq[0] =
|
|
m_pose.m_aqq[1] =
|
|
m_pose.m_aqq[2] = btVector3(0, 0, 0);
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
const btVector3& q = m_pose.m_pos[i];
|
|
const btVector3 mq = m_pose.m_wgh[i] * q;
|
|
m_pose.m_aqq[0] += mq.x() * q;
|
|
m_pose.m_aqq[1] += mq.y() * q;
|
|
m_pose.m_aqq[2] += mq.z() * q;
|
|
}
|
|
m_pose.m_aqq = m_pose.m_aqq.inverse();
|
|
|
|
updateConstants();
|
|
}
|
|
|
|
void btSoftBody::resetLinkRestLengths()
|
|
{
|
|
for (int i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
Link& l = m_links[i];
|
|
l.m_rl = (l.m_n[0]->m_x - l.m_n[1]->m_x).length();
|
|
l.m_c1 = l.m_rl * l.m_rl;
|
|
}
|
|
}
|
|
|
|
//
|
|
btScalar btSoftBody::getVolume() const
|
|
{
|
|
btScalar vol = 0;
|
|
if (m_nodes.size() > 0)
|
|
{
|
|
int i, ni;
|
|
|
|
const btVector3 org = m_nodes[0].m_x;
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
const Face& f = m_faces[i];
|
|
vol += btDot(f.m_n[0]->m_x - org, btCross(f.m_n[1]->m_x - org, f.m_n[2]->m_x - org));
|
|
}
|
|
vol /= (btScalar)6;
|
|
}
|
|
return (vol);
|
|
}
|
|
|
|
//
|
|
int btSoftBody::clusterCount() const
|
|
{
|
|
return (m_clusters.size());
|
|
}
|
|
|
|
//
|
|
btVector3 btSoftBody::clusterCom(const Cluster* cluster)
|
|
{
|
|
btVector3 com(0, 0, 0);
|
|
for (int i = 0, ni = cluster->m_nodes.size(); i < ni; ++i)
|
|
{
|
|
com += cluster->m_nodes[i]->m_x * cluster->m_masses[i];
|
|
}
|
|
return (com * cluster->m_imass);
|
|
}
|
|
|
|
//
|
|
btVector3 btSoftBody::clusterCom(int cluster) const
|
|
{
|
|
return (clusterCom(m_clusters[cluster]));
|
|
}
|
|
|
|
//
|
|
btVector3 btSoftBody::clusterVelocity(const Cluster* cluster, const btVector3& rpos)
|
|
{
|
|
return (cluster->m_lv + btCross(cluster->m_av, rpos));
|
|
}
|
|
|
|
//
|
|
void btSoftBody::clusterVImpulse(Cluster* cluster, const btVector3& rpos, const btVector3& impulse)
|
|
{
|
|
const btVector3 li = cluster->m_imass * impulse;
|
|
const btVector3 ai = cluster->m_invwi * btCross(rpos, impulse);
|
|
cluster->m_vimpulses[0] += li;
|
|
cluster->m_lv += li;
|
|
cluster->m_vimpulses[1] += ai;
|
|
cluster->m_av += ai;
|
|
cluster->m_nvimpulses++;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::clusterDImpulse(Cluster* cluster, const btVector3& rpos, const btVector3& impulse)
|
|
{
|
|
const btVector3 li = cluster->m_imass * impulse;
|
|
const btVector3 ai = cluster->m_invwi * btCross(rpos, impulse);
|
|
cluster->m_dimpulses[0] += li;
|
|
cluster->m_dimpulses[1] += ai;
|
|
cluster->m_ndimpulses++;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::clusterImpulse(Cluster* cluster, const btVector3& rpos, const Impulse& impulse)
|
|
{
|
|
if (impulse.m_asVelocity) clusterVImpulse(cluster, rpos, impulse.m_velocity);
|
|
if (impulse.m_asDrift) clusterDImpulse(cluster, rpos, impulse.m_drift);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::clusterVAImpulse(Cluster* cluster, const btVector3& impulse)
|
|
{
|
|
const btVector3 ai = cluster->m_invwi * impulse;
|
|
cluster->m_vimpulses[1] += ai;
|
|
cluster->m_av += ai;
|
|
cluster->m_nvimpulses++;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::clusterDAImpulse(Cluster* cluster, const btVector3& impulse)
|
|
{
|
|
const btVector3 ai = cluster->m_invwi * impulse;
|
|
cluster->m_dimpulses[1] += ai;
|
|
cluster->m_ndimpulses++;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::clusterAImpulse(Cluster* cluster, const Impulse& impulse)
|
|
{
|
|
if (impulse.m_asVelocity) clusterVAImpulse(cluster, impulse.m_velocity);
|
|
if (impulse.m_asDrift) clusterDAImpulse(cluster, impulse.m_drift);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::clusterDCImpulse(Cluster* cluster, const btVector3& impulse)
|
|
{
|
|
cluster->m_dimpulses[0] += impulse * cluster->m_imass;
|
|
cluster->m_ndimpulses++;
|
|
}
|
|
|
|
struct NodeLinks
|
|
{
|
|
btAlignedObjectArray<int> m_links;
|
|
};
|
|
|
|
//
|
|
int btSoftBody::generateBendingConstraints(int distance, Material* mat)
|
|
{
|
|
int i, j;
|
|
|
|
if (distance > 1)
|
|
{
|
|
/* Build graph */
|
|
const int n = m_nodes.size();
|
|
const unsigned inf = (~(unsigned)0) >> 1;
|
|
unsigned* adj = new unsigned[n * n];
|
|
|
|
#define IDX(_x_, _y_) ((_y_)*n + (_x_))
|
|
for (j = 0; j < n; ++j)
|
|
{
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
if (i != j)
|
|
{
|
|
adj[IDX(i, j)] = adj[IDX(j, i)] = inf;
|
|
}
|
|
else
|
|
{
|
|
adj[IDX(i, j)] = adj[IDX(j, i)] = 0;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < m_links.size(); ++i)
|
|
{
|
|
const int ia = (int)(m_links[i].m_n[0] - &m_nodes[0]);
|
|
const int ib = (int)(m_links[i].m_n[1] - &m_nodes[0]);
|
|
adj[IDX(ia, ib)] = 1;
|
|
adj[IDX(ib, ia)] = 1;
|
|
}
|
|
|
|
//special optimized case for distance == 2
|
|
if (distance == 2)
|
|
{
|
|
btAlignedObjectArray<NodeLinks> nodeLinks;
|
|
|
|
/* Build node links */
|
|
nodeLinks.resize(m_nodes.size());
|
|
|
|
for (i = 0; i < m_links.size(); ++i)
|
|
{
|
|
const int ia = (int)(m_links[i].m_n[0] - &m_nodes[0]);
|
|
const int ib = (int)(m_links[i].m_n[1] - &m_nodes[0]);
|
|
if (nodeLinks[ia].m_links.findLinearSearch(ib) == nodeLinks[ia].m_links.size())
|
|
nodeLinks[ia].m_links.push_back(ib);
|
|
|
|
if (nodeLinks[ib].m_links.findLinearSearch(ia) == nodeLinks[ib].m_links.size())
|
|
nodeLinks[ib].m_links.push_back(ia);
|
|
}
|
|
for (int ii = 0; ii < nodeLinks.size(); ii++)
|
|
{
|
|
int i = ii;
|
|
|
|
for (int jj = 0; jj < nodeLinks[ii].m_links.size(); jj++)
|
|
{
|
|
int k = nodeLinks[ii].m_links[jj];
|
|
for (int kk = 0; kk < nodeLinks[k].m_links.size(); kk++)
|
|
{
|
|
int j = nodeLinks[k].m_links[kk];
|
|
if (i != j)
|
|
{
|
|
const unsigned sum = adj[IDX(i, k)] + adj[IDX(k, j)];
|
|
btAssert(sum == 2);
|
|
if (adj[IDX(i, j)] > sum)
|
|
{
|
|
adj[IDX(i, j)] = adj[IDX(j, i)] = sum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
///generic Floyd's algorithm
|
|
for (int k = 0; k < n; ++k)
|
|
{
|
|
for (j = 0; j < n; ++j)
|
|
{
|
|
for (i = j + 1; i < n; ++i)
|
|
{
|
|
const unsigned sum = adj[IDX(i, k)] + adj[IDX(k, j)];
|
|
if (adj[IDX(i, j)] > sum)
|
|
{
|
|
adj[IDX(i, j)] = adj[IDX(j, i)] = sum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Build links */
|
|
int nlinks = 0;
|
|
for (j = 0; j < n; ++j)
|
|
{
|
|
for (i = j + 1; i < n; ++i)
|
|
{
|
|
if (adj[IDX(i, j)] == (unsigned)distance)
|
|
{
|
|
appendLink(i, j, mat);
|
|
m_links[m_links.size() - 1].m_bbending = 1;
|
|
++nlinks;
|
|
}
|
|
}
|
|
}
|
|
delete[] adj;
|
|
return (nlinks);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::randomizeConstraints()
|
|
{
|
|
unsigned long seed = 243703;
|
|
#define NEXTRAND (seed = (1664525L * seed + 1013904223L) & 0xffffffff)
|
|
int i, ni;
|
|
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
btSwap(m_links[i], m_links[NEXTRAND % ni]);
|
|
}
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
btSwap(m_faces[i], m_faces[NEXTRAND % ni]);
|
|
}
|
|
#undef NEXTRAND
|
|
}
|
|
|
|
//
|
|
void btSoftBody::releaseCluster(int index)
|
|
{
|
|
Cluster* c = m_clusters[index];
|
|
if (c->m_leaf) m_cdbvt.remove(c->m_leaf);
|
|
c->~Cluster();
|
|
btAlignedFree(c);
|
|
m_clusters.remove(c);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::releaseClusters()
|
|
{
|
|
while (m_clusters.size() > 0) releaseCluster(0);
|
|
}
|
|
|
|
//
|
|
int btSoftBody::generateClusters(int k, int maxiterations)
|
|
{
|
|
int i;
|
|
releaseClusters();
|
|
m_clusters.resize(btMin(k, m_nodes.size()));
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
m_clusters[i] = new (btAlignedAlloc(sizeof(Cluster), 16)) Cluster();
|
|
m_clusters[i]->m_collide = true;
|
|
}
|
|
k = m_clusters.size();
|
|
if (k > 0)
|
|
{
|
|
/* Initialize */
|
|
btAlignedObjectArray<btVector3> centers;
|
|
btVector3 cog(0, 0, 0);
|
|
int i;
|
|
for (i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
cog += m_nodes[i].m_x;
|
|
m_clusters[(i * 29873) % m_clusters.size()]->m_nodes.push_back(&m_nodes[i]);
|
|
}
|
|
cog /= (btScalar)m_nodes.size();
|
|
centers.resize(k, cog);
|
|
/* Iterate */
|
|
const btScalar slope = 16;
|
|
bool changed;
|
|
int iterations = 0;
|
|
do
|
|
{
|
|
const btScalar w = 2 - btMin<btScalar>(1, iterations / slope);
|
|
changed = false;
|
|
iterations++;
|
|
int i;
|
|
|
|
for (i = 0; i < k; ++i)
|
|
{
|
|
btVector3 c(0, 0, 0);
|
|
for (int j = 0; j < m_clusters[i]->m_nodes.size(); ++j)
|
|
{
|
|
c += m_clusters[i]->m_nodes[j]->m_x;
|
|
}
|
|
if (m_clusters[i]->m_nodes.size())
|
|
{
|
|
c /= (btScalar)m_clusters[i]->m_nodes.size();
|
|
c = centers[i] + (c - centers[i]) * w;
|
|
changed |= ((c - centers[i]).length2() > SIMD_EPSILON);
|
|
centers[i] = c;
|
|
m_clusters[i]->m_nodes.resize(0);
|
|
}
|
|
}
|
|
for (i = 0; i < m_nodes.size(); ++i)
|
|
{
|
|
const btVector3 nx = m_nodes[i].m_x;
|
|
int kbest = 0;
|
|
btScalar kdist = ClusterMetric(centers[0], nx);
|
|
for (int j = 1; j < k; ++j)
|
|
{
|
|
const btScalar d = ClusterMetric(centers[j], nx);
|
|
if (d < kdist)
|
|
{
|
|
kbest = j;
|
|
kdist = d;
|
|
}
|
|
}
|
|
m_clusters[kbest]->m_nodes.push_back(&m_nodes[i]);
|
|
}
|
|
} while (changed && (iterations < maxiterations));
|
|
/* Merge */
|
|
btAlignedObjectArray<int> cids;
|
|
cids.resize(m_nodes.size(), -1);
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
for (int j = 0; j < m_clusters[i]->m_nodes.size(); ++j)
|
|
{
|
|
cids[int(m_clusters[i]->m_nodes[j] - &m_nodes[0])] = i;
|
|
}
|
|
}
|
|
for (i = 0; i < m_faces.size(); ++i)
|
|
{
|
|
const int idx[] = {int(m_faces[i].m_n[0] - &m_nodes[0]),
|
|
int(m_faces[i].m_n[1] - &m_nodes[0]),
|
|
int(m_faces[i].m_n[2] - &m_nodes[0])};
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
const int cid = cids[idx[j]];
|
|
for (int q = 1; q < 3; ++q)
|
|
{
|
|
const int kid = idx[(j + q) % 3];
|
|
if (cids[kid] != cid)
|
|
{
|
|
if (m_clusters[cid]->m_nodes.findLinearSearch(&m_nodes[kid]) == m_clusters[cid]->m_nodes.size())
|
|
{
|
|
m_clusters[cid]->m_nodes.push_back(&m_nodes[kid]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Master */
|
|
if (m_clusters.size() > 1)
|
|
{
|
|
Cluster* pmaster = new (btAlignedAlloc(sizeof(Cluster), 16)) Cluster();
|
|
pmaster->m_collide = false;
|
|
pmaster->m_nodes.reserve(m_nodes.size());
|
|
for (int i = 0; i < m_nodes.size(); ++i) pmaster->m_nodes.push_back(&m_nodes[i]);
|
|
m_clusters.push_back(pmaster);
|
|
btSwap(m_clusters[0], m_clusters[m_clusters.size() - 1]);
|
|
}
|
|
/* Terminate */
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
if (m_clusters[i]->m_nodes.size() == 0)
|
|
{
|
|
releaseCluster(i--);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//create a cluster for each tetrahedron (if tetrahedra exist) or each face
|
|
if (m_tetras.size())
|
|
{
|
|
m_clusters.resize(m_tetras.size());
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
m_clusters[i] = new (btAlignedAlloc(sizeof(Cluster), 16)) Cluster();
|
|
m_clusters[i]->m_collide = true;
|
|
}
|
|
for (i = 0; i < m_tetras.size(); i++)
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
m_clusters[i]->m_nodes.push_back(m_tetras[i].m_n[j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_clusters.resize(m_faces.size());
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
m_clusters[i] = new (btAlignedAlloc(sizeof(Cluster), 16)) Cluster();
|
|
m_clusters[i]->m_collide = true;
|
|
}
|
|
|
|
for (i = 0; i < m_faces.size(); ++i)
|
|
{
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
m_clusters[i]->m_nodes.push_back(m_faces[i].m_n[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_clusters.size())
|
|
{
|
|
initializeClusters();
|
|
updateClusters();
|
|
|
|
//for self-collision
|
|
m_clusterConnectivity.resize(m_clusters.size() * m_clusters.size());
|
|
{
|
|
for (int c0 = 0; c0 < m_clusters.size(); c0++)
|
|
{
|
|
m_clusters[c0]->m_clusterIndex = c0;
|
|
for (int c1 = 0; c1 < m_clusters.size(); c1++)
|
|
{
|
|
bool connected = false;
|
|
Cluster* cla = m_clusters[c0];
|
|
Cluster* clb = m_clusters[c1];
|
|
for (int i = 0; !connected && i < cla->m_nodes.size(); i++)
|
|
{
|
|
for (int j = 0; j < clb->m_nodes.size(); j++)
|
|
{
|
|
if (cla->m_nodes[i] == clb->m_nodes[j])
|
|
{
|
|
connected = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
m_clusterConnectivity[c0 + c1 * m_clusters.size()] = connected;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (m_clusters.size());
|
|
}
|
|
|
|
//
|
|
void btSoftBody::refine(ImplicitFn* ifn, btScalar accurary, bool cut)
|
|
{
|
|
const Node* nbase = &m_nodes[0];
|
|
int ncount = m_nodes.size();
|
|
btSymMatrix<int> edges(ncount, -2);
|
|
int newnodes = 0;
|
|
int i, j, k, ni;
|
|
|
|
/* Filter out */
|
|
for (i = 0; i < m_links.size(); ++i)
|
|
{
|
|
Link& l = m_links[i];
|
|
if (l.m_bbending)
|
|
{
|
|
if (!SameSign(ifn->Eval(l.m_n[0]->m_x), ifn->Eval(l.m_n[1]->m_x)))
|
|
{
|
|
btSwap(m_links[i], m_links[m_links.size() - 1]);
|
|
m_links.pop_back();
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
/* Fill edges */
|
|
for (i = 0; i < m_links.size(); ++i)
|
|
{
|
|
Link& l = m_links[i];
|
|
edges(int(l.m_n[0] - nbase), int(l.m_n[1] - nbase)) = -1;
|
|
}
|
|
for (i = 0; i < m_faces.size(); ++i)
|
|
{
|
|
Face& f = m_faces[i];
|
|
edges(int(f.m_n[0] - nbase), int(f.m_n[1] - nbase)) = -1;
|
|
edges(int(f.m_n[1] - nbase), int(f.m_n[2] - nbase)) = -1;
|
|
edges(int(f.m_n[2] - nbase), int(f.m_n[0] - nbase)) = -1;
|
|
}
|
|
/* Intersect */
|
|
for (i = 0; i < ncount; ++i)
|
|
{
|
|
for (j = i + 1; j < ncount; ++j)
|
|
{
|
|
if (edges(i, j) == -1)
|
|
{
|
|
Node& a = m_nodes[i];
|
|
Node& b = m_nodes[j];
|
|
const btScalar t = ImplicitSolve(ifn, a.m_x, b.m_x, accurary);
|
|
if (t > 0)
|
|
{
|
|
const btVector3 x = Lerp(a.m_x, b.m_x, t);
|
|
const btVector3 v = Lerp(a.m_v, b.m_v, t);
|
|
btScalar m = 0;
|
|
if (a.m_im > 0)
|
|
{
|
|
if (b.m_im > 0)
|
|
{
|
|
const btScalar ma = 1 / a.m_im;
|
|
const btScalar mb = 1 / b.m_im;
|
|
const btScalar mc = Lerp(ma, mb, t);
|
|
const btScalar f = (ma + mb) / (ma + mb + mc);
|
|
a.m_im = 1 / (ma * f);
|
|
b.m_im = 1 / (mb * f);
|
|
m = mc * f;
|
|
}
|
|
else
|
|
{
|
|
a.m_im /= 0.5f;
|
|
m = 1 / a.m_im;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (b.m_im > 0)
|
|
{
|
|
b.m_im /= 0.5f;
|
|
m = 1 / b.m_im;
|
|
}
|
|
else
|
|
m = 0;
|
|
}
|
|
appendNode(x, m);
|
|
edges(i, j) = m_nodes.size() - 1;
|
|
m_nodes[edges(i, j)].m_v = v;
|
|
++newnodes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nbase = &m_nodes[0];
|
|
/* Refine links */
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
Link& feat = m_links[i];
|
|
const int idx[] = {int(feat.m_n[0] - nbase),
|
|
int(feat.m_n[1] - nbase)};
|
|
if ((idx[0] < ncount) && (idx[1] < ncount))
|
|
{
|
|
const int ni = edges(idx[0], idx[1]);
|
|
if (ni > 0)
|
|
{
|
|
appendLink(i);
|
|
Link* pft[] = {&m_links[i],
|
|
&m_links[m_links.size() - 1]};
|
|
pft[0]->m_n[0] = &m_nodes[idx[0]];
|
|
pft[0]->m_n[1] = &m_nodes[ni];
|
|
pft[1]->m_n[0] = &m_nodes[ni];
|
|
pft[1]->m_n[1] = &m_nodes[idx[1]];
|
|
}
|
|
}
|
|
}
|
|
/* Refine faces */
|
|
for (i = 0; i < m_faces.size(); ++i)
|
|
{
|
|
const Face& feat = m_faces[i];
|
|
const int idx[] = {int(feat.m_n[0] - nbase),
|
|
int(feat.m_n[1] - nbase),
|
|
int(feat.m_n[2] - nbase)};
|
|
for (j = 2, k = 0; k < 3; j = k++)
|
|
{
|
|
if ((idx[j] < ncount) && (idx[k] < ncount))
|
|
{
|
|
const int ni = edges(idx[j], idx[k]);
|
|
if (ni > 0)
|
|
{
|
|
appendFace(i);
|
|
const int l = (k + 1) % 3;
|
|
Face* pft[] = {&m_faces[i],
|
|
&m_faces[m_faces.size() - 1]};
|
|
pft[0]->m_n[0] = &m_nodes[idx[l]];
|
|
pft[0]->m_n[1] = &m_nodes[idx[j]];
|
|
pft[0]->m_n[2] = &m_nodes[ni];
|
|
pft[1]->m_n[0] = &m_nodes[ni];
|
|
pft[1]->m_n[1] = &m_nodes[idx[k]];
|
|
pft[1]->m_n[2] = &m_nodes[idx[l]];
|
|
appendLink(ni, idx[l], pft[0]->m_material);
|
|
--i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Cut */
|
|
if (cut)
|
|
{
|
|
btAlignedObjectArray<int> cnodes;
|
|
const int pcount = ncount;
|
|
int i;
|
|
ncount = m_nodes.size();
|
|
cnodes.resize(ncount, 0);
|
|
/* Nodes */
|
|
for (i = 0; i < ncount; ++i)
|
|
{
|
|
const btVector3 x = m_nodes[i].m_x;
|
|
if ((i >= pcount) || (btFabs(ifn->Eval(x)) < accurary))
|
|
{
|
|
const btVector3 v = m_nodes[i].m_v;
|
|
btScalar m = getMass(i);
|
|
if (m > 0)
|
|
{
|
|
m *= 0.5f;
|
|
m_nodes[i].m_im /= 0.5f;
|
|
}
|
|
appendNode(x, m);
|
|
cnodes[i] = m_nodes.size() - 1;
|
|
m_nodes[cnodes[i]].m_v = v;
|
|
}
|
|
}
|
|
nbase = &m_nodes[0];
|
|
/* Links */
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
const int id[] = {int(m_links[i].m_n[0] - nbase),
|
|
int(m_links[i].m_n[1] - nbase)};
|
|
int todetach = 0;
|
|
if (cnodes[id[0]] && cnodes[id[1]])
|
|
{
|
|
appendLink(i);
|
|
todetach = m_links.size() - 1;
|
|
}
|
|
else
|
|
{
|
|
if (((ifn->Eval(m_nodes[id[0]].m_x) < accurary) &&
|
|
(ifn->Eval(m_nodes[id[1]].m_x) < accurary)))
|
|
todetach = i;
|
|
}
|
|
if (todetach)
|
|
{
|
|
Link& l = m_links[todetach];
|
|
for (int j = 0; j < 2; ++j)
|
|
{
|
|
int cn = cnodes[int(l.m_n[j] - nbase)];
|
|
if (cn) l.m_n[j] = &m_nodes[cn];
|
|
}
|
|
}
|
|
}
|
|
/* Faces */
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
Node** n = m_faces[i].m_n;
|
|
if ((ifn->Eval(n[0]->m_x) < accurary) &&
|
|
(ifn->Eval(n[1]->m_x) < accurary) &&
|
|
(ifn->Eval(n[2]->m_x) < accurary))
|
|
{
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
int cn = cnodes[int(n[j] - nbase)];
|
|
if (cn) n[j] = &m_nodes[cn];
|
|
}
|
|
}
|
|
}
|
|
/* Clean orphans */
|
|
int nnodes = m_nodes.size();
|
|
btAlignedObjectArray<int> ranks;
|
|
btAlignedObjectArray<int> todelete;
|
|
ranks.resize(nnodes, 0);
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
for (int j = 0; j < 2; ++j) ranks[int(m_links[i].m_n[j] - nbase)]++;
|
|
}
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
for (int j = 0; j < 3; ++j) ranks[int(m_faces[i].m_n[j] - nbase)]++;
|
|
}
|
|
for (i = 0; i < m_links.size(); ++i)
|
|
{
|
|
const int id[] = {int(m_links[i].m_n[0] - nbase),
|
|
int(m_links[i].m_n[1] - nbase)};
|
|
const bool sg[] = {ranks[id[0]] == 1,
|
|
ranks[id[1]] == 1};
|
|
if (sg[0] || sg[1])
|
|
{
|
|
--ranks[id[0]];
|
|
--ranks[id[1]];
|
|
btSwap(m_links[i], m_links[m_links.size() - 1]);
|
|
m_links.pop_back();
|
|
--i;
|
|
}
|
|
}
|
|
#if 0
|
|
for(i=nnodes-1;i>=0;--i)
|
|
{
|
|
if(!ranks[i]) todelete.push_back(i);
|
|
}
|
|
if(todelete.size())
|
|
{
|
|
btAlignedObjectArray<int>& map=ranks;
|
|
for(int i=0;i<nnodes;++i) map[i]=i;
|
|
PointersToIndices(this);
|
|
for(int i=0,ni=todelete.size();i<ni;++i)
|
|
{
|
|
int j=todelete[i];
|
|
int& a=map[j];
|
|
int& b=map[--nnodes];
|
|
m_ndbvt.remove(m_nodes[a].m_leaf);m_nodes[a].m_leaf=0;
|
|
btSwap(m_nodes[a],m_nodes[b]);
|
|
j=a;a=b;b=j;
|
|
}
|
|
IndicesToPointers(this,&map[0]);
|
|
m_nodes.resize(nnodes);
|
|
}
|
|
#endif
|
|
}
|
|
m_bUpdateRtCst = true;
|
|
}
|
|
|
|
//
|
|
bool btSoftBody::cutLink(const Node* node0, const Node* node1, btScalar position)
|
|
{
|
|
return (cutLink(int(node0 - &m_nodes[0]), int(node1 - &m_nodes[0]), position));
|
|
}
|
|
|
|
//
|
|
bool btSoftBody::cutLink(int node0, int node1, btScalar position)
|
|
{
|
|
bool done = false;
|
|
int i, ni;
|
|
// const btVector3 d=m_nodes[node0].m_x-m_nodes[node1].m_x;
|
|
const btVector3 x = Lerp(m_nodes[node0].m_x, m_nodes[node1].m_x, position);
|
|
const btVector3 v = Lerp(m_nodes[node0].m_v, m_nodes[node1].m_v, position);
|
|
const btScalar m = 1;
|
|
appendNode(x, m);
|
|
appendNode(x, m);
|
|
Node* pa = &m_nodes[node0];
|
|
Node* pb = &m_nodes[node1];
|
|
Node* pn[2] = {&m_nodes[m_nodes.size() - 2],
|
|
&m_nodes[m_nodes.size() - 1]};
|
|
pn[0]->m_v = v;
|
|
pn[1]->m_v = v;
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
const int mtch = MatchEdge(m_links[i].m_n[0], m_links[i].m_n[1], pa, pb);
|
|
if (mtch != -1)
|
|
{
|
|
appendLink(i);
|
|
Link* pft[] = {&m_links[i], &m_links[m_links.size() - 1]};
|
|
pft[0]->m_n[1] = pn[mtch];
|
|
pft[1]->m_n[0] = pn[1 - mtch];
|
|
done = true;
|
|
}
|
|
}
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
for (int k = 2, l = 0; l < 3; k = l++)
|
|
{
|
|
const int mtch = MatchEdge(m_faces[i].m_n[k], m_faces[i].m_n[l], pa, pb);
|
|
if (mtch != -1)
|
|
{
|
|
appendFace(i);
|
|
Face* pft[] = {&m_faces[i], &m_faces[m_faces.size() - 1]};
|
|
pft[0]->m_n[l] = pn[mtch];
|
|
pft[1]->m_n[k] = pn[1 - mtch];
|
|
appendLink(pn[0], pft[0]->m_n[(l + 1) % 3], pft[0]->m_material, true);
|
|
appendLink(pn[1], pft[0]->m_n[(l + 1) % 3], pft[0]->m_material, true);
|
|
}
|
|
}
|
|
}
|
|
if (!done)
|
|
{
|
|
m_ndbvt.remove(pn[0]->m_leaf);
|
|
m_ndbvt.remove(pn[1]->m_leaf);
|
|
m_nodes.pop_back();
|
|
m_nodes.pop_back();
|
|
}
|
|
return (done);
|
|
}
|
|
|
|
//
|
|
bool btSoftBody::rayTest(const btVector3& rayFrom,
|
|
const btVector3& rayTo,
|
|
sRayCast& results)
|
|
{
|
|
if (m_faces.size() && m_fdbvt.empty())
|
|
initializeFaceTree();
|
|
|
|
results.body = this;
|
|
results.fraction = 1.f;
|
|
results.feature = eFeature::None;
|
|
results.index = -1;
|
|
|
|
return (rayTest(rayFrom, rayTo, results.fraction, results.feature, results.index, false) != 0);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::setSolver(eSolverPresets::_ preset)
|
|
{
|
|
m_cfg.m_vsequence.clear();
|
|
m_cfg.m_psequence.clear();
|
|
m_cfg.m_dsequence.clear();
|
|
switch (preset)
|
|
{
|
|
case eSolverPresets::Positions:
|
|
m_cfg.m_psequence.push_back(ePSolver::Anchors);
|
|
m_cfg.m_psequence.push_back(ePSolver::RContacts);
|
|
m_cfg.m_psequence.push_back(ePSolver::SContacts);
|
|
m_cfg.m_psequence.push_back(ePSolver::Linear);
|
|
break;
|
|
case eSolverPresets::Velocities:
|
|
m_cfg.m_vsequence.push_back(eVSolver::Linear);
|
|
|
|
m_cfg.m_psequence.push_back(ePSolver::Anchors);
|
|
m_cfg.m_psequence.push_back(ePSolver::RContacts);
|
|
m_cfg.m_psequence.push_back(ePSolver::SContacts);
|
|
|
|
m_cfg.m_dsequence.push_back(ePSolver::Linear);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::predictMotion(btScalar dt)
|
|
{
|
|
int i, ni;
|
|
|
|
/* Update */
|
|
if (m_bUpdateRtCst)
|
|
{
|
|
m_bUpdateRtCst = false;
|
|
updateConstants();
|
|
m_fdbvt.clear();
|
|
if (m_cfg.collisions & fCollision::VF_SS)
|
|
{
|
|
initializeFaceTree();
|
|
}
|
|
}
|
|
|
|
/* Prepare */
|
|
m_sst.sdt = dt * m_cfg.timescale;
|
|
m_sst.isdt = 1 / m_sst.sdt;
|
|
m_sst.velmrg = m_sst.sdt * 3;
|
|
m_sst.radmrg = getCollisionShape()->getMargin();
|
|
m_sst.updmrg = m_sst.radmrg * (btScalar)0.25;
|
|
/* Forces */
|
|
addVelocity(m_worldInfo->m_gravity * m_sst.sdt);
|
|
applyForces();
|
|
/* Integrate */
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
n.m_q = n.m_x;
|
|
btVector3 deltaV = n.m_f * n.m_im * m_sst.sdt;
|
|
{
|
|
btScalar maxDisplacement = m_worldInfo->m_maxDisplacement;
|
|
btScalar clampDeltaV = maxDisplacement / m_sst.sdt;
|
|
for (int c = 0; c < 3; c++)
|
|
{
|
|
if (deltaV[c] > clampDeltaV)
|
|
{
|
|
deltaV[c] = clampDeltaV;
|
|
}
|
|
if (deltaV[c] < -clampDeltaV)
|
|
{
|
|
deltaV[c] = -clampDeltaV;
|
|
}
|
|
}
|
|
}
|
|
n.m_v += deltaV;
|
|
n.m_x += n.m_v * m_sst.sdt;
|
|
n.m_f = btVector3(0, 0, 0);
|
|
}
|
|
/* Clusters */
|
|
updateClusters();
|
|
/* Bounds */
|
|
updateBounds();
|
|
/* Nodes */
|
|
ATTRIBUTE_ALIGNED16(btDbvtVolume)
|
|
vol;
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
vol = btDbvtVolume::FromCR(n.m_x, m_sst.radmrg);
|
|
m_ndbvt.update(n.m_leaf,
|
|
vol,
|
|
n.m_v * m_sst.velmrg,
|
|
m_sst.updmrg);
|
|
}
|
|
/* Faces */
|
|
if (!m_fdbvt.empty())
|
|
{
|
|
for (int i = 0; i < m_faces.size(); ++i)
|
|
{
|
|
Face& f = m_faces[i];
|
|
const btVector3 v = (f.m_n[0]->m_v +
|
|
f.m_n[1]->m_v +
|
|
f.m_n[2]->m_v) /
|
|
3;
|
|
vol = VolumeOf(f, m_sst.radmrg);
|
|
m_fdbvt.update(f.m_leaf,
|
|
vol,
|
|
v * m_sst.velmrg,
|
|
m_sst.updmrg);
|
|
}
|
|
}
|
|
/* Pose */
|
|
updatePose();
|
|
/* Match */
|
|
if (m_pose.m_bframe && (m_cfg.kMT > 0))
|
|
{
|
|
const btMatrix3x3 posetrs = m_pose.m_rot;
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
if (n.m_im > 0)
|
|
{
|
|
const btVector3 x = posetrs * m_pose.m_pos[i] + m_pose.m_com;
|
|
n.m_x = Lerp(n.m_x, x, m_cfg.kMT);
|
|
}
|
|
}
|
|
}
|
|
/* Clear contacts */
|
|
m_rcontacts.resize(0);
|
|
m_scontacts.resize(0);
|
|
/* Optimize dbvt's */
|
|
m_ndbvt.optimizeIncremental(1);
|
|
m_fdbvt.optimizeIncremental(1);
|
|
m_cdbvt.optimizeIncremental(1);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::solveConstraints()
|
|
{
|
|
/* Apply clusters */
|
|
applyClusters(false);
|
|
/* Prepare links */
|
|
|
|
int i, ni;
|
|
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
Link& l = m_links[i];
|
|
l.m_c3 = l.m_n[1]->m_q - l.m_n[0]->m_q;
|
|
l.m_c2 = 1 / (l.m_c3.length2() * l.m_c0);
|
|
}
|
|
/* Prepare anchors */
|
|
for (i = 0, ni = m_anchors.size(); i < ni; ++i)
|
|
{
|
|
Anchor& a = m_anchors[i];
|
|
const btVector3 ra = a.m_body->getWorldTransform().getBasis() * a.m_local;
|
|
a.m_c0 = ImpulseMatrix(m_sst.sdt,
|
|
a.m_node->m_im,
|
|
a.m_body->getInvMass(),
|
|
a.m_body->getInvInertiaTensorWorld(),
|
|
ra);
|
|
a.m_c1 = ra;
|
|
a.m_c2 = m_sst.sdt * a.m_node->m_im;
|
|
a.m_body->activate();
|
|
}
|
|
/* Solve velocities */
|
|
if (m_cfg.viterations > 0)
|
|
{
|
|
/* Solve */
|
|
for (int isolve = 0; isolve < m_cfg.viterations; ++isolve)
|
|
{
|
|
for (int iseq = 0; iseq < m_cfg.m_vsequence.size(); ++iseq)
|
|
{
|
|
getSolver(m_cfg.m_vsequence[iseq])(this, 1);
|
|
}
|
|
}
|
|
/* Update */
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
n.m_x = n.m_q + n.m_v * m_sst.sdt;
|
|
}
|
|
}
|
|
/* Solve positions */
|
|
if (m_cfg.piterations > 0)
|
|
{
|
|
for (int isolve = 0; isolve < m_cfg.piterations; ++isolve)
|
|
{
|
|
const btScalar ti = isolve / (btScalar)m_cfg.piterations;
|
|
for (int iseq = 0; iseq < m_cfg.m_psequence.size(); ++iseq)
|
|
{
|
|
getSolver(m_cfg.m_psequence[iseq])(this, 1, ti);
|
|
}
|
|
}
|
|
const btScalar vc = m_sst.isdt * (1 - m_cfg.kDP);
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
n.m_v = (n.m_x - n.m_q) * vc;
|
|
n.m_f = btVector3(0, 0, 0);
|
|
}
|
|
}
|
|
/* Solve drift */
|
|
if (m_cfg.diterations > 0)
|
|
{
|
|
const btScalar vcf = m_cfg.kVCF * m_sst.isdt;
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
n.m_q = n.m_x;
|
|
}
|
|
for (int idrift = 0; idrift < m_cfg.diterations; ++idrift)
|
|
{
|
|
for (int iseq = 0; iseq < m_cfg.m_dsequence.size(); ++iseq)
|
|
{
|
|
getSolver(m_cfg.m_dsequence[iseq])(this, 1, 0);
|
|
}
|
|
}
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
Node& n = m_nodes[i];
|
|
n.m_v += (n.m_x - n.m_q) * vcf;
|
|
}
|
|
}
|
|
/* Apply clusters */
|
|
dampClusters();
|
|
applyClusters(true);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::staticSolve(int iterations)
|
|
{
|
|
for (int isolve = 0; isolve < iterations; ++isolve)
|
|
{
|
|
for (int iseq = 0; iseq < m_cfg.m_psequence.size(); ++iseq)
|
|
{
|
|
getSolver(m_cfg.m_psequence[iseq])(this, 1, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::solveCommonConstraints(btSoftBody** /*bodies*/, int /*count*/, int /*iterations*/)
|
|
{
|
|
/// placeholder
|
|
}
|
|
|
|
//
|
|
void btSoftBody::solveClusters(const btAlignedObjectArray<btSoftBody*>& bodies)
|
|
{
|
|
const int nb = bodies.size();
|
|
int iterations = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < nb; ++i)
|
|
{
|
|
iterations = btMax(iterations, bodies[i]->m_cfg.citerations);
|
|
}
|
|
for (i = 0; i < nb; ++i)
|
|
{
|
|
bodies[i]->prepareClusters(iterations);
|
|
}
|
|
for (i = 0; i < iterations; ++i)
|
|
{
|
|
const btScalar sor = 1;
|
|
for (int j = 0; j < nb; ++j)
|
|
{
|
|
bodies[j]->solveClusters(sor);
|
|
}
|
|
}
|
|
for (i = 0; i < nb; ++i)
|
|
{
|
|
bodies[i]->cleanupClusters();
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::integrateMotion()
|
|
{
|
|
/* Update */
|
|
updateNormals();
|
|
}
|
|
|
|
//
|
|
btSoftBody::RayFromToCaster::RayFromToCaster(const btVector3& rayFrom, const btVector3& rayTo, btScalar mxt)
|
|
{
|
|
m_rayFrom = rayFrom;
|
|
m_rayNormalizedDirection = (rayTo - rayFrom);
|
|
m_rayTo = rayTo;
|
|
m_mint = mxt;
|
|
m_face = 0;
|
|
m_tests = 0;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::RayFromToCaster::Process(const btDbvtNode* leaf)
|
|
{
|
|
btSoftBody::Face& f = *(btSoftBody::Face*)leaf->data;
|
|
const btScalar t = rayFromToTriangle(m_rayFrom, m_rayTo, m_rayNormalizedDirection,
|
|
f.m_n[0]->m_x,
|
|
f.m_n[1]->m_x,
|
|
f.m_n[2]->m_x,
|
|
m_mint);
|
|
if ((t > 0) && (t < m_mint))
|
|
{
|
|
m_mint = t;
|
|
m_face = &f;
|
|
}
|
|
++m_tests;
|
|
}
|
|
|
|
//
|
|
btScalar btSoftBody::RayFromToCaster::rayFromToTriangle(const btVector3& rayFrom,
|
|
const btVector3& rayTo,
|
|
const btVector3& rayNormalizedDirection,
|
|
const btVector3& a,
|
|
const btVector3& b,
|
|
const btVector3& c,
|
|
btScalar maxt)
|
|
{
|
|
static const btScalar ceps = -SIMD_EPSILON * 10;
|
|
static const btScalar teps = SIMD_EPSILON * 10;
|
|
|
|
const btVector3 n = btCross(b - a, c - a);
|
|
const btScalar d = btDot(a, n);
|
|
const btScalar den = btDot(rayNormalizedDirection, n);
|
|
if (!btFuzzyZero(den))
|
|
{
|
|
const btScalar num = btDot(rayFrom, n) - d;
|
|
const btScalar t = -num / den;
|
|
if ((t > teps) && (t < maxt))
|
|
{
|
|
const btVector3 hit = rayFrom + rayNormalizedDirection * t;
|
|
if ((btDot(n, btCross(a - hit, b - hit)) > ceps) &&
|
|
(btDot(n, btCross(b - hit, c - hit)) > ceps) &&
|
|
(btDot(n, btCross(c - hit, a - hit)) > ceps))
|
|
{
|
|
return (t);
|
|
}
|
|
}
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::pointersToIndices()
|
|
{
|
|
#define PTR2IDX(_p_, _b_) reinterpret_cast<btSoftBody::Node*>((_p_) - (_b_))
|
|
btSoftBody::Node* base = m_nodes.size() ? &m_nodes[0] : 0;
|
|
int i, ni;
|
|
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
if (m_nodes[i].m_leaf)
|
|
{
|
|
m_nodes[i].m_leaf->data = *(void**)&i;
|
|
}
|
|
}
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
m_links[i].m_n[0] = PTR2IDX(m_links[i].m_n[0], base);
|
|
m_links[i].m_n[1] = PTR2IDX(m_links[i].m_n[1], base);
|
|
}
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
m_faces[i].m_n[0] = PTR2IDX(m_faces[i].m_n[0], base);
|
|
m_faces[i].m_n[1] = PTR2IDX(m_faces[i].m_n[1], base);
|
|
m_faces[i].m_n[2] = PTR2IDX(m_faces[i].m_n[2], base);
|
|
if (m_faces[i].m_leaf)
|
|
{
|
|
m_faces[i].m_leaf->data = *(void**)&i;
|
|
}
|
|
}
|
|
for (i = 0, ni = m_anchors.size(); i < ni; ++i)
|
|
{
|
|
m_anchors[i].m_node = PTR2IDX(m_anchors[i].m_node, base);
|
|
}
|
|
for (i = 0, ni = m_notes.size(); i < ni; ++i)
|
|
{
|
|
for (int j = 0; j < m_notes[i].m_rank; ++j)
|
|
{
|
|
m_notes[i].m_nodes[j] = PTR2IDX(m_notes[i].m_nodes[j], base);
|
|
}
|
|
}
|
|
#undef PTR2IDX
|
|
}
|
|
|
|
//
|
|
void btSoftBody::indicesToPointers(const int* map)
|
|
{
|
|
#define IDX2PTR(_p_, _b_) map ? (&(_b_)[map[(((char*)_p_) - (char*)0)]]) : (&(_b_)[(((char*)_p_) - (char*)0)])
|
|
btSoftBody::Node* base = m_nodes.size() ? &m_nodes[0] : 0;
|
|
int i, ni;
|
|
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
if (m_nodes[i].m_leaf)
|
|
{
|
|
m_nodes[i].m_leaf->data = &m_nodes[i];
|
|
}
|
|
}
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
m_links[i].m_n[0] = IDX2PTR(m_links[i].m_n[0], base);
|
|
m_links[i].m_n[1] = IDX2PTR(m_links[i].m_n[1], base);
|
|
}
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
m_faces[i].m_n[0] = IDX2PTR(m_faces[i].m_n[0], base);
|
|
m_faces[i].m_n[1] = IDX2PTR(m_faces[i].m_n[1], base);
|
|
m_faces[i].m_n[2] = IDX2PTR(m_faces[i].m_n[2], base);
|
|
if (m_faces[i].m_leaf)
|
|
{
|
|
m_faces[i].m_leaf->data = &m_faces[i];
|
|
}
|
|
}
|
|
for (i = 0, ni = m_anchors.size(); i < ni; ++i)
|
|
{
|
|
m_anchors[i].m_node = IDX2PTR(m_anchors[i].m_node, base);
|
|
}
|
|
for (i = 0, ni = m_notes.size(); i < ni; ++i)
|
|
{
|
|
for (int j = 0; j < m_notes[i].m_rank; ++j)
|
|
{
|
|
m_notes[i].m_nodes[j] = IDX2PTR(m_notes[i].m_nodes[j], base);
|
|
}
|
|
}
|
|
#undef IDX2PTR
|
|
}
|
|
|
|
//
|
|
int btSoftBody::rayTest(const btVector3& rayFrom, const btVector3& rayTo,
|
|
btScalar& mint, eFeature::_& feature, int& index, bool bcountonly) const
|
|
{
|
|
int cnt = 0;
|
|
btVector3 dir = rayTo - rayFrom;
|
|
|
|
if (bcountonly || m_fdbvt.empty())
|
|
{ /* Full search */
|
|
|
|
for (int i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
const btSoftBody::Face& f = m_faces[i];
|
|
|
|
const btScalar t = RayFromToCaster::rayFromToTriangle(rayFrom, rayTo, dir,
|
|
f.m_n[0]->m_x,
|
|
f.m_n[1]->m_x,
|
|
f.m_n[2]->m_x,
|
|
mint);
|
|
if (t > 0)
|
|
{
|
|
++cnt;
|
|
if (!bcountonly)
|
|
{
|
|
feature = btSoftBody::eFeature::Face;
|
|
index = i;
|
|
mint = t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ /* Use dbvt */
|
|
RayFromToCaster collider(rayFrom, rayTo, mint);
|
|
|
|
btDbvt::rayTest(m_fdbvt.m_root, rayFrom, rayTo, collider);
|
|
if (collider.m_face)
|
|
{
|
|
mint = collider.m_mint;
|
|
feature = btSoftBody::eFeature::Face;
|
|
index = (int)(collider.m_face - &m_faces[0]);
|
|
cnt = 1;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_tetras.size(); i++)
|
|
{
|
|
const btSoftBody::Tetra& tet = m_tetras[i];
|
|
int tetfaces[4][3] = {{0, 1, 2}, {0, 1, 3}, {1, 2, 3}, {0, 2, 3}};
|
|
for (int f = 0; f < 4; f++)
|
|
{
|
|
int index0 = tetfaces[f][0];
|
|
int index1 = tetfaces[f][1];
|
|
int index2 = tetfaces[f][2];
|
|
btVector3 v0 = tet.m_n[index0]->m_x;
|
|
btVector3 v1 = tet.m_n[index1]->m_x;
|
|
btVector3 v2 = tet.m_n[index2]->m_x;
|
|
|
|
const btScalar t = RayFromToCaster::rayFromToTriangle(rayFrom, rayTo, dir,
|
|
v0, v1, v2,
|
|
mint);
|
|
if (t > 0)
|
|
{
|
|
++cnt;
|
|
if (!bcountonly)
|
|
{
|
|
feature = btSoftBody::eFeature::Tetra;
|
|
index = i;
|
|
mint = t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (cnt);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::initializeFaceTree()
|
|
{
|
|
m_fdbvt.clear();
|
|
for (int i = 0; i < m_faces.size(); ++i)
|
|
{
|
|
Face& f = m_faces[i];
|
|
f.m_leaf = m_fdbvt.insert(VolumeOf(f, 0), &f);
|
|
}
|
|
}
|
|
|
|
//
|
|
btVector3 btSoftBody::evaluateCom() const
|
|
{
|
|
btVector3 com(0, 0, 0);
|
|
if (m_pose.m_bframe)
|
|
{
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
com += m_nodes[i].m_x * m_pose.m_wgh[i];
|
|
}
|
|
}
|
|
return (com);
|
|
}
|
|
|
|
//
|
|
bool btSoftBody::checkContact(const btCollisionObjectWrapper* colObjWrap,
|
|
const btVector3& x,
|
|
btScalar margin,
|
|
btSoftBody::sCti& cti) const
|
|
{
|
|
btVector3 nrm;
|
|
const btCollisionShape* shp = colObjWrap->getCollisionShape();
|
|
// const btRigidBody *tmpRigid = btRigidBody::upcast(colObjWrap->getCollisionObject());
|
|
//const btTransform &wtr = tmpRigid ? tmpRigid->getWorldTransform() : colObjWrap->getWorldTransform();
|
|
const btTransform& wtr = colObjWrap->getWorldTransform();
|
|
//todo: check which transform is needed here
|
|
|
|
btScalar dst =
|
|
m_worldInfo->m_sparsesdf.Evaluate(
|
|
wtr.invXform(x),
|
|
shp,
|
|
nrm,
|
|
margin);
|
|
if (dst < 0)
|
|
{
|
|
cti.m_colObj = colObjWrap->getCollisionObject();
|
|
cti.m_normal = wtr.getBasis() * nrm;
|
|
cti.m_offset = -btDot(cti.m_normal, x - cti.m_normal * dst);
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::updateNormals()
|
|
{
|
|
const btVector3 zv(0, 0, 0);
|
|
int i, ni;
|
|
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
m_nodes[i].m_n = zv;
|
|
}
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
btSoftBody::Face& f = m_faces[i];
|
|
const btVector3 n = btCross(f.m_n[1]->m_x - f.m_n[0]->m_x,
|
|
f.m_n[2]->m_x - f.m_n[0]->m_x);
|
|
f.m_normal = n.normalized();
|
|
f.m_n[0]->m_n += n;
|
|
f.m_n[1]->m_n += n;
|
|
f.m_n[2]->m_n += n;
|
|
}
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
btScalar len = m_nodes[i].m_n.length();
|
|
if (len > SIMD_EPSILON)
|
|
m_nodes[i].m_n /= len;
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::updateBounds()
|
|
{
|
|
/*if( m_acceleratedSoftBody )
|
|
{
|
|
// If we have an accelerated softbody we need to obtain the bounds correctly
|
|
// For now (slightly hackily) just have a very large AABB
|
|
// TODO: Write get bounds kernel
|
|
// If that is updating in place, atomic collisions might be low (when the cloth isn't perfectly aligned to an axis) and we could
|
|
// probably do a test and exchange reasonably efficiently.
|
|
|
|
m_bounds[0] = btVector3(-1000, -1000, -1000);
|
|
m_bounds[1] = btVector3(1000, 1000, 1000);
|
|
|
|
} else {*/
|
|
if (m_ndbvt.m_root)
|
|
{
|
|
const btVector3& mins = m_ndbvt.m_root->volume.Mins();
|
|
const btVector3& maxs = m_ndbvt.m_root->volume.Maxs();
|
|
const btScalar csm = getCollisionShape()->getMargin();
|
|
const btVector3 mrg = btVector3(csm,
|
|
csm,
|
|
csm) *
|
|
1; // ??? to investigate...
|
|
m_bounds[0] = mins - mrg;
|
|
m_bounds[1] = maxs + mrg;
|
|
if (0 != getBroadphaseHandle())
|
|
{
|
|
m_worldInfo->m_broadphase->setAabb(getBroadphaseHandle(),
|
|
m_bounds[0],
|
|
m_bounds[1],
|
|
m_worldInfo->m_dispatcher);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bounds[0] =
|
|
m_bounds[1] = btVector3(0, 0, 0);
|
|
}
|
|
//}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::updatePose()
|
|
{
|
|
if (m_pose.m_bframe)
|
|
{
|
|
btSoftBody::Pose& pose = m_pose;
|
|
const btVector3 com = evaluateCom();
|
|
/* Com */
|
|
pose.m_com = com;
|
|
/* Rotation */
|
|
btMatrix3x3 Apq;
|
|
const btScalar eps = SIMD_EPSILON;
|
|
Apq[0] = Apq[1] = Apq[2] = btVector3(0, 0, 0);
|
|
Apq[0].setX(eps);
|
|
Apq[1].setY(eps * 2);
|
|
Apq[2].setZ(eps * 3);
|
|
for (int i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
const btVector3 a = pose.m_wgh[i] * (m_nodes[i].m_x - com);
|
|
const btVector3& b = pose.m_pos[i];
|
|
Apq[0] += a.x() * b;
|
|
Apq[1] += a.y() * b;
|
|
Apq[2] += a.z() * b;
|
|
}
|
|
btMatrix3x3 r, s;
|
|
PolarDecompose(Apq, r, s);
|
|
pose.m_rot = r;
|
|
pose.m_scl = pose.m_aqq * r.transpose() * Apq;
|
|
if (m_cfg.maxvolume > 1)
|
|
{
|
|
const btScalar idet = Clamp<btScalar>(1 / pose.m_scl.determinant(),
|
|
1, m_cfg.maxvolume);
|
|
pose.m_scl = Mul(pose.m_scl, idet);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::updateArea(bool averageArea)
|
|
{
|
|
int i, ni;
|
|
|
|
/* Face area */
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
Face& f = m_faces[i];
|
|
f.m_ra = AreaOf(f.m_n[0]->m_x, f.m_n[1]->m_x, f.m_n[2]->m_x);
|
|
}
|
|
|
|
/* Node area */
|
|
|
|
if (averageArea)
|
|
{
|
|
btAlignedObjectArray<int> counts;
|
|
counts.resize(m_nodes.size(), 0);
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
m_nodes[i].m_area = 0;
|
|
}
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
btSoftBody::Face& f = m_faces[i];
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
const int index = (int)(f.m_n[j] - &m_nodes[0]);
|
|
counts[index]++;
|
|
f.m_n[j]->m_area += btFabs(f.m_ra);
|
|
}
|
|
}
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
if (counts[i] > 0)
|
|
m_nodes[i].m_area /= (btScalar)counts[i];
|
|
else
|
|
m_nodes[i].m_area = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// initialize node area as zero
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
m_nodes[i].m_area = 0;
|
|
}
|
|
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
btSoftBody::Face& f = m_faces[i];
|
|
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
f.m_n[j]->m_area += f.m_ra;
|
|
}
|
|
}
|
|
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
m_nodes[i].m_area *= 0.3333333f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void btSoftBody::updateLinkConstants()
|
|
{
|
|
int i, ni;
|
|
|
|
/* Links */
|
|
for (i = 0, ni = m_links.size(); i < ni; ++i)
|
|
{
|
|
Link& l = m_links[i];
|
|
Material& m = *l.m_material;
|
|
l.m_c0 = (l.m_n[0]->m_im + l.m_n[1]->m_im) / m.m_kLST;
|
|
}
|
|
}
|
|
|
|
void btSoftBody::updateConstants()
|
|
{
|
|
resetLinkRestLengths();
|
|
updateLinkConstants();
|
|
updateArea();
|
|
}
|
|
|
|
//
|
|
void btSoftBody::initializeClusters()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
Cluster& c = *m_clusters[i];
|
|
c.m_imass = 0;
|
|
c.m_masses.resize(c.m_nodes.size());
|
|
for (int j = 0; j < c.m_nodes.size(); ++j)
|
|
{
|
|
if (c.m_nodes[j]->m_im == 0)
|
|
{
|
|
c.m_containsAnchor = true;
|
|
c.m_masses[j] = BT_LARGE_FLOAT;
|
|
}
|
|
else
|
|
{
|
|
c.m_masses[j] = btScalar(1.) / c.m_nodes[j]->m_im;
|
|
}
|
|
c.m_imass += c.m_masses[j];
|
|
}
|
|
c.m_imass = btScalar(1.) / c.m_imass;
|
|
c.m_com = btSoftBody::clusterCom(&c);
|
|
c.m_lv = btVector3(0, 0, 0);
|
|
c.m_av = btVector3(0, 0, 0);
|
|
c.m_leaf = 0;
|
|
/* Inertia */
|
|
btMatrix3x3& ii = c.m_locii;
|
|
ii[0] = ii[1] = ii[2] = btVector3(0, 0, 0);
|
|
{
|
|
int i, ni;
|
|
|
|
for (i = 0, ni = c.m_nodes.size(); i < ni; ++i)
|
|
{
|
|
const btVector3 k = c.m_nodes[i]->m_x - c.m_com;
|
|
const btVector3 q = k * k;
|
|
const btScalar m = c.m_masses[i];
|
|
ii[0][0] += m * (q[1] + q[2]);
|
|
ii[1][1] += m * (q[0] + q[2]);
|
|
ii[2][2] += m * (q[0] + q[1]);
|
|
ii[0][1] -= m * k[0] * k[1];
|
|
ii[0][2] -= m * k[0] * k[2];
|
|
ii[1][2] -= m * k[1] * k[2];
|
|
}
|
|
}
|
|
ii[1][0] = ii[0][1];
|
|
ii[2][0] = ii[0][2];
|
|
ii[2][1] = ii[1][2];
|
|
|
|
ii = ii.inverse();
|
|
|
|
/* Frame */
|
|
c.m_framexform.setIdentity();
|
|
c.m_framexform.setOrigin(c.m_com);
|
|
c.m_framerefs.resize(c.m_nodes.size());
|
|
{
|
|
int i;
|
|
for (i = 0; i < c.m_framerefs.size(); ++i)
|
|
{
|
|
c.m_framerefs[i] = c.m_nodes[i]->m_x - c.m_com;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::updateClusters()
|
|
{
|
|
BT_PROFILE("UpdateClusters");
|
|
int i;
|
|
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
btSoftBody::Cluster& c = *m_clusters[i];
|
|
const int n = c.m_nodes.size();
|
|
//const btScalar invn=1/(btScalar)n;
|
|
if (n)
|
|
{
|
|
/* Frame */
|
|
const btScalar eps = btScalar(0.0001);
|
|
btMatrix3x3 m, r, s;
|
|
m[0] = m[1] = m[2] = btVector3(0, 0, 0);
|
|
m[0][0] = eps * 1;
|
|
m[1][1] = eps * 2;
|
|
m[2][2] = eps * 3;
|
|
c.m_com = clusterCom(&c);
|
|
for (int i = 0; i < c.m_nodes.size(); ++i)
|
|
{
|
|
const btVector3 a = c.m_nodes[i]->m_x - c.m_com;
|
|
const btVector3& b = c.m_framerefs[i];
|
|
m[0] += a[0] * b;
|
|
m[1] += a[1] * b;
|
|
m[2] += a[2] * b;
|
|
}
|
|
PolarDecompose(m, r, s);
|
|
c.m_framexform.setOrigin(c.m_com);
|
|
c.m_framexform.setBasis(r);
|
|
/* Inertia */
|
|
#if 1 /* Constant */
|
|
c.m_invwi = c.m_framexform.getBasis() * c.m_locii * c.m_framexform.getBasis().transpose();
|
|
#else
|
|
#if 0 /* Sphere */
|
|
const btScalar rk=(2*c.m_extents.length2())/(5*c.m_imass);
|
|
const btVector3 inertia(rk,rk,rk);
|
|
const btVector3 iin(btFabs(inertia[0])>SIMD_EPSILON?1/inertia[0]:0,
|
|
btFabs(inertia[1])>SIMD_EPSILON?1/inertia[1]:0,
|
|
btFabs(inertia[2])>SIMD_EPSILON?1/inertia[2]:0);
|
|
|
|
c.m_invwi=c.m_xform.getBasis().scaled(iin)*c.m_xform.getBasis().transpose();
|
|
#else /* Actual */
|
|
c.m_invwi[0] = c.m_invwi[1] = c.m_invwi[2] = btVector3(0, 0, 0);
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
const btVector3 k = c.m_nodes[i]->m_x - c.m_com;
|
|
const btVector3 q = k * k;
|
|
const btScalar m = 1 / c.m_nodes[i]->m_im;
|
|
c.m_invwi[0][0] += m * (q[1] + q[2]);
|
|
c.m_invwi[1][1] += m * (q[0] + q[2]);
|
|
c.m_invwi[2][2] += m * (q[0] + q[1]);
|
|
c.m_invwi[0][1] -= m * k[0] * k[1];
|
|
c.m_invwi[0][2] -= m * k[0] * k[2];
|
|
c.m_invwi[1][2] -= m * k[1] * k[2];
|
|
}
|
|
c.m_invwi[1][0] = c.m_invwi[0][1];
|
|
c.m_invwi[2][0] = c.m_invwi[0][2];
|
|
c.m_invwi[2][1] = c.m_invwi[1][2];
|
|
c.m_invwi = c.m_invwi.inverse();
|
|
#endif
|
|
#endif
|
|
/* Velocities */
|
|
c.m_lv = btVector3(0, 0, 0);
|
|
c.m_av = btVector3(0, 0, 0);
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
const btVector3 v = c.m_nodes[i]->m_v * c.m_masses[i];
|
|
c.m_lv += v;
|
|
c.m_av += btCross(c.m_nodes[i]->m_x - c.m_com, v);
|
|
}
|
|
}
|
|
c.m_lv = c.m_imass * c.m_lv * (1 - c.m_ldamping);
|
|
c.m_av = c.m_invwi * c.m_av * (1 - c.m_adamping);
|
|
c.m_vimpulses[0] =
|
|
c.m_vimpulses[1] = btVector3(0, 0, 0);
|
|
c.m_dimpulses[0] =
|
|
c.m_dimpulses[1] = btVector3(0, 0, 0);
|
|
c.m_nvimpulses = 0;
|
|
c.m_ndimpulses = 0;
|
|
/* Matching */
|
|
if (c.m_matching > 0)
|
|
{
|
|
for (int j = 0; j < c.m_nodes.size(); ++j)
|
|
{
|
|
Node& n = *c.m_nodes[j];
|
|
const btVector3 x = c.m_framexform * c.m_framerefs[j];
|
|
n.m_x = Lerp(n.m_x, x, c.m_matching);
|
|
}
|
|
}
|
|
/* Dbvt */
|
|
if (c.m_collide)
|
|
{
|
|
btVector3 mi = c.m_nodes[0]->m_x;
|
|
btVector3 mx = mi;
|
|
for (int j = 1; j < n; ++j)
|
|
{
|
|
mi.setMin(c.m_nodes[j]->m_x);
|
|
mx.setMax(c.m_nodes[j]->m_x);
|
|
}
|
|
ATTRIBUTE_ALIGNED16(btDbvtVolume)
|
|
bounds = btDbvtVolume::FromMM(mi, mx);
|
|
if (c.m_leaf)
|
|
m_cdbvt.update(c.m_leaf, bounds, c.m_lv * m_sst.sdt * 3, m_sst.radmrg);
|
|
else
|
|
c.m_leaf = m_cdbvt.insert(bounds, &c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::cleanupClusters()
|
|
{
|
|
for (int i = 0; i < m_joints.size(); ++i)
|
|
{
|
|
m_joints[i]->Terminate(m_sst.sdt);
|
|
if (m_joints[i]->m_delete)
|
|
{
|
|
btAlignedFree(m_joints[i]);
|
|
m_joints.remove(m_joints[i--]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::prepareClusters(int iterations)
|
|
{
|
|
for (int i = 0; i < m_joints.size(); ++i)
|
|
{
|
|
m_joints[i]->Prepare(m_sst.sdt, iterations);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::solveClusters(btScalar sor)
|
|
{
|
|
for (int i = 0, ni = m_joints.size(); i < ni; ++i)
|
|
{
|
|
m_joints[i]->Solve(m_sst.sdt, sor);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::applyClusters(bool drift)
|
|
{
|
|
BT_PROFILE("ApplyClusters");
|
|
// const btScalar f0=m_sst.sdt;
|
|
//const btScalar f1=f0/2;
|
|
btAlignedObjectArray<btVector3> deltas;
|
|
btAlignedObjectArray<btScalar> weights;
|
|
deltas.resize(m_nodes.size(), btVector3(0, 0, 0));
|
|
weights.resize(m_nodes.size(), 0);
|
|
int i;
|
|
|
|
if (drift)
|
|
{
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
Cluster& c = *m_clusters[i];
|
|
if (c.m_ndimpulses)
|
|
{
|
|
c.m_dimpulses[0] /= (btScalar)c.m_ndimpulses;
|
|
c.m_dimpulses[1] /= (btScalar)c.m_ndimpulses;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
Cluster& c = *m_clusters[i];
|
|
if (0 < (drift ? c.m_ndimpulses : c.m_nvimpulses))
|
|
{
|
|
const btVector3 v = (drift ? c.m_dimpulses[0] : c.m_vimpulses[0]) * m_sst.sdt;
|
|
const btVector3 w = (drift ? c.m_dimpulses[1] : c.m_vimpulses[1]) * m_sst.sdt;
|
|
for (int j = 0; j < c.m_nodes.size(); ++j)
|
|
{
|
|
const int idx = int(c.m_nodes[j] - &m_nodes[0]);
|
|
const btVector3& x = c.m_nodes[j]->m_x;
|
|
const btScalar q = c.m_masses[j];
|
|
deltas[idx] += (v + btCross(w, x - c.m_com)) * q;
|
|
weights[idx] += q;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < deltas.size(); ++i)
|
|
{
|
|
if (weights[i] > 0)
|
|
{
|
|
m_nodes[i].m_x += deltas[i] / weights[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::dampClusters()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < m_clusters.size(); ++i)
|
|
{
|
|
Cluster& c = *m_clusters[i];
|
|
if (c.m_ndamping > 0)
|
|
{
|
|
for (int j = 0; j < c.m_nodes.size(); ++j)
|
|
{
|
|
Node& n = *c.m_nodes[j];
|
|
if (n.m_im > 0)
|
|
{
|
|
const btVector3 vx = c.m_lv + btCross(c.m_av, c.m_nodes[j]->m_q - c.m_com);
|
|
if (vx.length2() <= n.m_v.length2())
|
|
{
|
|
n.m_v += c.m_ndamping * (vx - n.m_v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::Joint::Prepare(btScalar dt, int)
|
|
{
|
|
m_bodies[0].activate();
|
|
m_bodies[1].activate();
|
|
}
|
|
|
|
//
|
|
void btSoftBody::LJoint::Prepare(btScalar dt, int iterations)
|
|
{
|
|
static const btScalar maxdrift = 4;
|
|
Joint::Prepare(dt, iterations);
|
|
m_rpos[0] = m_bodies[0].xform() * m_refs[0];
|
|
m_rpos[1] = m_bodies[1].xform() * m_refs[1];
|
|
m_drift = Clamp(m_rpos[0] - m_rpos[1], maxdrift) * m_erp / dt;
|
|
m_rpos[0] -= m_bodies[0].xform().getOrigin();
|
|
m_rpos[1] -= m_bodies[1].xform().getOrigin();
|
|
m_massmatrix = ImpulseMatrix(m_bodies[0].invMass(), m_bodies[0].invWorldInertia(), m_rpos[0],
|
|
m_bodies[1].invMass(), m_bodies[1].invWorldInertia(), m_rpos[1]);
|
|
if (m_split > 0)
|
|
{
|
|
m_sdrift = m_massmatrix * (m_drift * m_split);
|
|
m_drift *= 1 - m_split;
|
|
}
|
|
m_drift /= (btScalar)iterations;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::LJoint::Solve(btScalar dt, btScalar sor)
|
|
{
|
|
const btVector3 va = m_bodies[0].velocity(m_rpos[0]);
|
|
const btVector3 vb = m_bodies[1].velocity(m_rpos[1]);
|
|
const btVector3 vr = va - vb;
|
|
btSoftBody::Impulse impulse;
|
|
impulse.m_asVelocity = 1;
|
|
impulse.m_velocity = m_massmatrix * (m_drift + vr * m_cfm) * sor;
|
|
m_bodies[0].applyImpulse(-impulse, m_rpos[0]);
|
|
m_bodies[1].applyImpulse(impulse, m_rpos[1]);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::LJoint::Terminate(btScalar dt)
|
|
{
|
|
if (m_split > 0)
|
|
{
|
|
m_bodies[0].applyDImpulse(-m_sdrift, m_rpos[0]);
|
|
m_bodies[1].applyDImpulse(m_sdrift, m_rpos[1]);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::AJoint::Prepare(btScalar dt, int iterations)
|
|
{
|
|
static const btScalar maxdrift = SIMD_PI / 16;
|
|
m_icontrol->Prepare(this);
|
|
Joint::Prepare(dt, iterations);
|
|
m_axis[0] = m_bodies[0].xform().getBasis() * m_refs[0];
|
|
m_axis[1] = m_bodies[1].xform().getBasis() * m_refs[1];
|
|
m_drift = NormalizeAny(btCross(m_axis[1], m_axis[0]));
|
|
m_drift *= btMin(maxdrift, btAcos(Clamp<btScalar>(btDot(m_axis[0], m_axis[1]), -1, +1)));
|
|
m_drift *= m_erp / dt;
|
|
m_massmatrix = AngularImpulseMatrix(m_bodies[0].invWorldInertia(), m_bodies[1].invWorldInertia());
|
|
if (m_split > 0)
|
|
{
|
|
m_sdrift = m_massmatrix * (m_drift * m_split);
|
|
m_drift *= 1 - m_split;
|
|
}
|
|
m_drift /= (btScalar)iterations;
|
|
}
|
|
|
|
//
|
|
void btSoftBody::AJoint::Solve(btScalar dt, btScalar sor)
|
|
{
|
|
const btVector3 va = m_bodies[0].angularVelocity();
|
|
const btVector3 vb = m_bodies[1].angularVelocity();
|
|
const btVector3 vr = va - vb;
|
|
const btScalar sp = btDot(vr, m_axis[0]);
|
|
const btVector3 vc = vr - m_axis[0] * m_icontrol->Speed(this, sp);
|
|
btSoftBody::Impulse impulse;
|
|
impulse.m_asVelocity = 1;
|
|
impulse.m_velocity = m_massmatrix * (m_drift + vc * m_cfm) * sor;
|
|
m_bodies[0].applyAImpulse(-impulse);
|
|
m_bodies[1].applyAImpulse(impulse);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::AJoint::Terminate(btScalar dt)
|
|
{
|
|
if (m_split > 0)
|
|
{
|
|
m_bodies[0].applyDAImpulse(-m_sdrift);
|
|
m_bodies[1].applyDAImpulse(m_sdrift);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::CJoint::Prepare(btScalar dt, int iterations)
|
|
{
|
|
Joint::Prepare(dt, iterations);
|
|
const bool dodrift = (m_life == 0);
|
|
m_delete = (++m_life) > m_maxlife;
|
|
if (dodrift)
|
|
{
|
|
m_drift = m_drift * m_erp / dt;
|
|
if (m_split > 0)
|
|
{
|
|
m_sdrift = m_massmatrix * (m_drift * m_split);
|
|
m_drift *= 1 - m_split;
|
|
}
|
|
m_drift /= (btScalar)iterations;
|
|
}
|
|
else
|
|
{
|
|
m_drift = m_sdrift = btVector3(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::CJoint::Solve(btScalar dt, btScalar sor)
|
|
{
|
|
const btVector3 va = m_bodies[0].velocity(m_rpos[0]);
|
|
const btVector3 vb = m_bodies[1].velocity(m_rpos[1]);
|
|
const btVector3 vrel = va - vb;
|
|
const btScalar rvac = btDot(vrel, m_normal);
|
|
btSoftBody::Impulse impulse;
|
|
impulse.m_asVelocity = 1;
|
|
impulse.m_velocity = m_drift;
|
|
if (rvac < 0)
|
|
{
|
|
const btVector3 iv = m_normal * rvac;
|
|
const btVector3 fv = vrel - iv;
|
|
impulse.m_velocity += iv + fv * m_friction;
|
|
}
|
|
impulse.m_velocity = m_massmatrix * impulse.m_velocity * sor;
|
|
|
|
if (m_bodies[0].m_soft == m_bodies[1].m_soft)
|
|
{
|
|
if ((impulse.m_velocity.getX() == impulse.m_velocity.getX()) && (impulse.m_velocity.getY() == impulse.m_velocity.getY()) &&
|
|
(impulse.m_velocity.getZ() == impulse.m_velocity.getZ()))
|
|
{
|
|
if (impulse.m_asVelocity)
|
|
{
|
|
if (impulse.m_velocity.length() < m_bodies[0].m_soft->m_maxSelfCollisionImpulse)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
m_bodies[0].applyImpulse(-impulse * m_bodies[0].m_soft->m_selfCollisionImpulseFactor, m_rpos[0]);
|
|
m_bodies[1].applyImpulse(impulse * m_bodies[0].m_soft->m_selfCollisionImpulseFactor, m_rpos[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bodies[0].applyImpulse(-impulse, m_rpos[0]);
|
|
m_bodies[1].applyImpulse(impulse, m_rpos[1]);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::CJoint::Terminate(btScalar dt)
|
|
{
|
|
if (m_split > 0)
|
|
{
|
|
m_bodies[0].applyDImpulse(-m_sdrift, m_rpos[0]);
|
|
m_bodies[1].applyDImpulse(m_sdrift, m_rpos[1]);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::applyForces()
|
|
{
|
|
BT_PROFILE("SoftBody applyForces");
|
|
// const btScalar dt = m_sst.sdt;
|
|
const btScalar kLF = m_cfg.kLF;
|
|
const btScalar kDG = m_cfg.kDG;
|
|
const btScalar kPR = m_cfg.kPR;
|
|
const btScalar kVC = m_cfg.kVC;
|
|
const bool as_lift = kLF > 0;
|
|
const bool as_drag = kDG > 0;
|
|
const bool as_pressure = kPR != 0;
|
|
const bool as_volume = kVC > 0;
|
|
const bool as_aero = as_lift ||
|
|
as_drag;
|
|
//const bool as_vaero = as_aero &&
|
|
// (m_cfg.aeromodel < btSoftBody::eAeroModel::F_TwoSided);
|
|
//const bool as_faero = as_aero &&
|
|
// (m_cfg.aeromodel >= btSoftBody::eAeroModel::F_TwoSided);
|
|
const bool use_medium = as_aero;
|
|
const bool use_volume = as_pressure ||
|
|
as_volume;
|
|
btScalar volume = 0;
|
|
btScalar ivolumetp = 0;
|
|
btScalar dvolumetv = 0;
|
|
btSoftBody::sMedium medium;
|
|
if (use_volume)
|
|
{
|
|
volume = getVolume();
|
|
ivolumetp = 1 / btFabs(volume) * kPR;
|
|
dvolumetv = (m_pose.m_volume - volume) * kVC;
|
|
}
|
|
/* Per vertex forces */
|
|
int i, ni;
|
|
|
|
for (i = 0, ni = m_nodes.size(); i < ni; ++i)
|
|
{
|
|
btSoftBody::Node& n = m_nodes[i];
|
|
if (n.m_im > 0)
|
|
{
|
|
if (use_medium)
|
|
{
|
|
/* Aerodynamics */
|
|
addAeroForceToNode(m_windVelocity, i);
|
|
}
|
|
/* Pressure */
|
|
if (as_pressure)
|
|
{
|
|
n.m_f += n.m_n * (n.m_area * ivolumetp);
|
|
}
|
|
/* Volume */
|
|
if (as_volume)
|
|
{
|
|
n.m_f += n.m_n * (n.m_area * dvolumetv);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Per face forces */
|
|
for (i = 0, ni = m_faces.size(); i < ni; ++i)
|
|
{
|
|
// btSoftBody::Face& f=m_faces[i];
|
|
|
|
/* Aerodynamics */
|
|
addAeroForceToFace(m_windVelocity, i);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::PSolve_Anchors(btSoftBody* psb, btScalar kst, btScalar ti)
|
|
{
|
|
BT_PROFILE("PSolve_Anchors");
|
|
const btScalar kAHR = psb->m_cfg.kAHR * kst;
|
|
const btScalar dt = psb->m_sst.sdt;
|
|
for (int i = 0, ni = psb->m_anchors.size(); i < ni; ++i)
|
|
{
|
|
const Anchor& a = psb->m_anchors[i];
|
|
const btTransform& t = a.m_body->getWorldTransform();
|
|
Node& n = *a.m_node;
|
|
const btVector3 wa = t * a.m_local;
|
|
const btVector3 va = a.m_body->getVelocityInLocalPoint(a.m_c1) * dt;
|
|
const btVector3 vb = n.m_x - n.m_q;
|
|
const btVector3 vr = (va - vb) + (wa - n.m_x) * kAHR;
|
|
const btVector3 impulse = a.m_c0 * vr * a.m_influence;
|
|
n.m_x += impulse * a.m_c2;
|
|
a.m_body->applyImpulse(-impulse, a.m_c1);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::PSolve_RContacts(btSoftBody* psb, btScalar kst, btScalar ti)
|
|
{
|
|
BT_PROFILE("PSolve_RContacts");
|
|
const btScalar dt = psb->m_sst.sdt;
|
|
const btScalar mrg = psb->getCollisionShape()->getMargin();
|
|
btMultiBodyJacobianData jacobianData;
|
|
for (int i = 0, ni = psb->m_rcontacts.size(); i < ni; ++i)
|
|
{
|
|
const RContact& c = psb->m_rcontacts[i];
|
|
const sCti& cti = c.m_cti;
|
|
if (cti.m_colObj->hasContactResponse())
|
|
{
|
|
btVector3 va(0, 0, 0);
|
|
btRigidBody* rigidCol = 0;
|
|
btMultiBodyLinkCollider* multibodyLinkCol = 0;
|
|
btScalar* deltaV;
|
|
|
|
if (cti.m_colObj->getInternalType() == btCollisionObject::CO_RIGID_BODY)
|
|
{
|
|
rigidCol = (btRigidBody*)btRigidBody::upcast(cti.m_colObj);
|
|
va = rigidCol ? rigidCol->getVelocityInLocalPoint(c.m_c1) * dt : btVector3(0, 0, 0);
|
|
}
|
|
else if (cti.m_colObj->getInternalType() == btCollisionObject::CO_FEATHERSTONE_LINK)
|
|
{
|
|
multibodyLinkCol = (btMultiBodyLinkCollider*)btMultiBodyLinkCollider::upcast(cti.m_colObj);
|
|
if (multibodyLinkCol)
|
|
{
|
|
const int ndof = multibodyLinkCol->m_multiBody->getNumDofs() + 6;
|
|
jacobianData.m_jacobians.resize(ndof);
|
|
jacobianData.m_deltaVelocitiesUnitImpulse.resize(ndof);
|
|
btScalar* jac = &jacobianData.m_jacobians[0];
|
|
|
|
multibodyLinkCol->m_multiBody->fillContactJacobianMultiDof(multibodyLinkCol->m_link, c.m_node->m_x, cti.m_normal, jac, jacobianData.scratch_r, jacobianData.scratch_v, jacobianData.scratch_m);
|
|
deltaV = &jacobianData.m_deltaVelocitiesUnitImpulse[0];
|
|
multibodyLinkCol->m_multiBody->calcAccelerationDeltasMultiDof(&jacobianData.m_jacobians[0], deltaV, jacobianData.scratch_r, jacobianData.scratch_v);
|
|
|
|
btScalar vel = 0.0;
|
|
for (int j = 0; j < ndof; ++j)
|
|
{
|
|
vel += multibodyLinkCol->m_multiBody->getVelocityVector()[j] * jac[j];
|
|
}
|
|
va = cti.m_normal * vel * dt;
|
|
}
|
|
}
|
|
|
|
const btVector3 vb = c.m_node->m_x - c.m_node->m_q;
|
|
const btVector3 vr = vb - va;
|
|
const btScalar dn = btDot(vr, cti.m_normal);
|
|
if (dn <= SIMD_EPSILON)
|
|
{
|
|
const btScalar dp = btMin((btDot(c.m_node->m_x, cti.m_normal) + cti.m_offset), mrg);
|
|
const btVector3 fv = vr - (cti.m_normal * dn);
|
|
// c0 is the impulse matrix, c3 is 1 - the friction coefficient or 0, c4 is the contact hardness coefficient
|
|
const btVector3 impulse = c.m_c0 * ((vr - (fv * c.m_c3) + (cti.m_normal * (dp * c.m_c4))) * kst);
|
|
c.m_node->m_x -= impulse * c.m_c2;
|
|
|
|
if (cti.m_colObj->getInternalType() == btCollisionObject::CO_RIGID_BODY)
|
|
{
|
|
if (rigidCol)
|
|
rigidCol->applyImpulse(impulse, c.m_c1);
|
|
}
|
|
else if (cti.m_colObj->getInternalType() == btCollisionObject::CO_FEATHERSTONE_LINK)
|
|
{
|
|
if (multibodyLinkCol)
|
|
{
|
|
double multiplier = 0.5;
|
|
multibodyLinkCol->m_multiBody->applyDeltaVeeMultiDof(deltaV, -impulse.length() * multiplier);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::PSolve_SContacts(btSoftBody* psb, btScalar, btScalar ti)
|
|
{
|
|
BT_PROFILE("PSolve_SContacts");
|
|
|
|
for (int i = 0, ni = psb->m_scontacts.size(); i < ni; ++i)
|
|
{
|
|
const SContact& c = psb->m_scontacts[i];
|
|
const btVector3& nr = c.m_normal;
|
|
Node& n = *c.m_node;
|
|
Face& f = *c.m_face;
|
|
const btVector3 p = BaryEval(f.m_n[0]->m_x,
|
|
f.m_n[1]->m_x,
|
|
f.m_n[2]->m_x,
|
|
c.m_weights);
|
|
const btVector3 q = BaryEval(f.m_n[0]->m_q,
|
|
f.m_n[1]->m_q,
|
|
f.m_n[2]->m_q,
|
|
c.m_weights);
|
|
const btVector3 vr = (n.m_x - n.m_q) - (p - q);
|
|
btVector3 corr(0, 0, 0);
|
|
btScalar dot = btDot(vr, nr);
|
|
if (dot < 0)
|
|
{
|
|
const btScalar j = c.m_margin - (btDot(nr, n.m_x) - btDot(nr, p));
|
|
corr += c.m_normal * j;
|
|
}
|
|
corr -= ProjectOnPlane(vr, nr) * c.m_friction;
|
|
n.m_x += corr * c.m_cfm[0];
|
|
f.m_n[0]->m_x -= corr * (c.m_cfm[1] * c.m_weights.x());
|
|
f.m_n[1]->m_x -= corr * (c.m_cfm[1] * c.m_weights.y());
|
|
f.m_n[2]->m_x -= corr * (c.m_cfm[1] * c.m_weights.z());
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::PSolve_Links(btSoftBody* psb, btScalar kst, btScalar ti)
|
|
{
|
|
BT_PROFILE("PSolve_Links");
|
|
for (int i = 0, ni = psb->m_links.size(); i < ni; ++i)
|
|
{
|
|
Link& l = psb->m_links[i];
|
|
if (l.m_c0 > 0)
|
|
{
|
|
Node& a = *l.m_n[0];
|
|
Node& b = *l.m_n[1];
|
|
const btVector3 del = b.m_x - a.m_x;
|
|
const btScalar len = del.length2();
|
|
if (l.m_c1 + len > SIMD_EPSILON)
|
|
{
|
|
const btScalar k = ((l.m_c1 - len) / (l.m_c0 * (l.m_c1 + len))) * kst;
|
|
a.m_x -= del * (k * a.m_im);
|
|
b.m_x += del * (k * b.m_im);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::VSolve_Links(btSoftBody* psb, btScalar kst)
|
|
{
|
|
BT_PROFILE("VSolve_Links");
|
|
for (int i = 0, ni = psb->m_links.size(); i < ni; ++i)
|
|
{
|
|
Link& l = psb->m_links[i];
|
|
Node** n = l.m_n;
|
|
const btScalar j = -btDot(l.m_c3, n[0]->m_v - n[1]->m_v) * l.m_c2 * kst;
|
|
n[0]->m_v += l.m_c3 * (j * n[0]->m_im);
|
|
n[1]->m_v -= l.m_c3 * (j * n[1]->m_im);
|
|
}
|
|
}
|
|
|
|
//
|
|
btSoftBody::psolver_t btSoftBody::getSolver(ePSolver::_ solver)
|
|
{
|
|
switch (solver)
|
|
{
|
|
case ePSolver::Anchors:
|
|
return (&btSoftBody::PSolve_Anchors);
|
|
case ePSolver::Linear:
|
|
return (&btSoftBody::PSolve_Links);
|
|
case ePSolver::RContacts:
|
|
return (&btSoftBody::PSolve_RContacts);
|
|
case ePSolver::SContacts:
|
|
return (&btSoftBody::PSolve_SContacts);
|
|
default:
|
|
{
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
btSoftBody::vsolver_t btSoftBody::getSolver(eVSolver::_ solver)
|
|
{
|
|
switch (solver)
|
|
{
|
|
case eVSolver::Linear:
|
|
return (&btSoftBody::VSolve_Links);
|
|
default:
|
|
{
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
void btSoftBody::defaultCollisionHandler(const btCollisionObjectWrapper* pcoWrap)
|
|
{
|
|
switch (m_cfg.collisions & fCollision::RVSmask)
|
|
{
|
|
case fCollision::SDF_RS:
|
|
{
|
|
btSoftColliders::CollideSDF_RS docollide;
|
|
btRigidBody* prb1 = (btRigidBody*)btRigidBody::upcast(pcoWrap->getCollisionObject());
|
|
btTransform wtr = pcoWrap->getWorldTransform();
|
|
|
|
const btTransform ctr = pcoWrap->getWorldTransform();
|
|
const btScalar timemargin = (wtr.getOrigin() - ctr.getOrigin()).length();
|
|
const btScalar basemargin = getCollisionShape()->getMargin();
|
|
btVector3 mins;
|
|
btVector3 maxs;
|
|
ATTRIBUTE_ALIGNED16(btDbvtVolume)
|
|
volume;
|
|
pcoWrap->getCollisionShape()->getAabb(pcoWrap->getWorldTransform(),
|
|
mins,
|
|
maxs);
|
|
volume = btDbvtVolume::FromMM(mins, maxs);
|
|
volume.Expand(btVector3(basemargin, basemargin, basemargin));
|
|
docollide.psb = this;
|
|
docollide.m_colObj1Wrap = pcoWrap;
|
|
docollide.m_rigidBody = prb1;
|
|
|
|
docollide.dynmargin = basemargin + timemargin;
|
|
docollide.stamargin = basemargin;
|
|
m_ndbvt.collideTV(m_ndbvt.m_root, volume, docollide);
|
|
}
|
|
break;
|
|
case fCollision::CL_RS:
|
|
{
|
|
btSoftColliders::CollideCL_RS collider;
|
|
collider.ProcessColObj(this, pcoWrap);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBody::defaultCollisionHandler(btSoftBody* psb)
|
|
{
|
|
const int cf = m_cfg.collisions & psb->m_cfg.collisions;
|
|
switch (cf & fCollision::SVSmask)
|
|
{
|
|
case fCollision::CL_SS:
|
|
{
|
|
//support self-collision if CL_SELF flag set
|
|
if (this != psb || psb->m_cfg.collisions & fCollision::CL_SELF)
|
|
{
|
|
btSoftColliders::CollideCL_SS docollide;
|
|
docollide.ProcessSoftSoft(this, psb);
|
|
}
|
|
}
|
|
break;
|
|
case fCollision::VF_SS:
|
|
{
|
|
//only self-collision for Cluster, not Vertex-Face yet
|
|
if (this != psb)
|
|
{
|
|
btSoftColliders::CollideVF_SS docollide;
|
|
/* common */
|
|
docollide.mrg = getCollisionShape()->getMargin() +
|
|
psb->getCollisionShape()->getMargin();
|
|
/* psb0 nodes vs psb1 faces */
|
|
docollide.psb[0] = this;
|
|
docollide.psb[1] = psb;
|
|
docollide.psb[0]->m_ndbvt.collideTT(docollide.psb[0]->m_ndbvt.m_root,
|
|
docollide.psb[1]->m_fdbvt.m_root,
|
|
docollide);
|
|
/* psb1 nodes vs psb0 faces */
|
|
docollide.psb[0] = psb;
|
|
docollide.psb[1] = this;
|
|
docollide.psb[0]->m_ndbvt.collideTT(docollide.psb[0]->m_ndbvt.m_root,
|
|
docollide.psb[1]->m_fdbvt.m_root,
|
|
docollide);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
void btSoftBody::setWindVelocity(const btVector3& velocity)
|
|
{
|
|
m_windVelocity = velocity;
|
|
}
|
|
|
|
const btVector3& btSoftBody::getWindVelocity()
|
|
{
|
|
return m_windVelocity;
|
|
}
|
|
|
|
int btSoftBody::calculateSerializeBufferSize() const
|
|
{
|
|
int sz = sizeof(btSoftBodyData);
|
|
return sz;
|
|
}
|
|
|
|
///fills the dataBuffer and returns the struct name (and 0 on failure)
|
|
const char* btSoftBody::serialize(void* dataBuffer, class btSerializer* serializer) const
|
|
{
|
|
btSoftBodyData* sbd = (btSoftBodyData*)dataBuffer;
|
|
|
|
btCollisionObject::serialize(&sbd->m_collisionObjectData, serializer);
|
|
|
|
btHashMap<btHashPtr, int> m_nodeIndexMap;
|
|
|
|
sbd->m_numMaterials = m_materials.size();
|
|
sbd->m_materials = sbd->m_numMaterials ? (SoftBodyMaterialData**)serializer->getUniquePointer((void*)&m_materials) : 0;
|
|
|
|
if (sbd->m_materials)
|
|
{
|
|
int sz = sizeof(SoftBodyMaterialData*);
|
|
int numElem = sbd->m_numMaterials;
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
//SoftBodyMaterialData** memPtr = chunk->m_oldPtr;
|
|
SoftBodyMaterialData** memPtr = (SoftBodyMaterialData**)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
btSoftBody::Material* mat = m_materials[i];
|
|
*memPtr = mat ? (SoftBodyMaterialData*)serializer->getUniquePointer((void*)mat) : 0;
|
|
if (!serializer->findPointer(mat))
|
|
{
|
|
//serialize it here
|
|
btChunk* chunk = serializer->allocate(sizeof(SoftBodyMaterialData), 1);
|
|
SoftBodyMaterialData* memPtr = (SoftBodyMaterialData*)chunk->m_oldPtr;
|
|
memPtr->m_flags = mat->m_flags;
|
|
memPtr->m_angularStiffness = mat->m_kAST;
|
|
memPtr->m_linearStiffness = mat->m_kLST;
|
|
memPtr->m_volumeStiffness = mat->m_kVST;
|
|
serializer->finalizeChunk(chunk, "SoftBodyMaterialData", BT_SBMATERIAL_CODE, mat);
|
|
}
|
|
}
|
|
serializer->finalizeChunk(chunk, "SoftBodyMaterialData", BT_ARRAY_CODE, (void*)&m_materials);
|
|
}
|
|
|
|
sbd->m_numNodes = m_nodes.size();
|
|
sbd->m_nodes = sbd->m_numNodes ? (SoftBodyNodeData*)serializer->getUniquePointer((void*)&m_nodes) : 0;
|
|
if (sbd->m_nodes)
|
|
{
|
|
int sz = sizeof(SoftBodyNodeData);
|
|
int numElem = sbd->m_numNodes;
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
SoftBodyNodeData* memPtr = (SoftBodyNodeData*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
m_nodes[i].m_f.serializeFloat(memPtr->m_accumulatedForce);
|
|
memPtr->m_area = m_nodes[i].m_area;
|
|
memPtr->m_attach = m_nodes[i].m_battach;
|
|
memPtr->m_inverseMass = m_nodes[i].m_im;
|
|
memPtr->m_material = m_nodes[i].m_material ? (SoftBodyMaterialData*)serializer->getUniquePointer((void*)m_nodes[i].m_material) : 0;
|
|
m_nodes[i].m_n.serializeFloat(memPtr->m_normal);
|
|
m_nodes[i].m_x.serializeFloat(memPtr->m_position);
|
|
m_nodes[i].m_q.serializeFloat(memPtr->m_previousPosition);
|
|
m_nodes[i].m_v.serializeFloat(memPtr->m_velocity);
|
|
m_nodeIndexMap.insert(&m_nodes[i], i);
|
|
}
|
|
serializer->finalizeChunk(chunk, "SoftBodyNodeData", BT_SBNODE_CODE, (void*)&m_nodes);
|
|
}
|
|
|
|
sbd->m_numLinks = m_links.size();
|
|
sbd->m_links = sbd->m_numLinks ? (SoftBodyLinkData*)serializer->getUniquePointer((void*)&m_links[0]) : 0;
|
|
if (sbd->m_links)
|
|
{
|
|
int sz = sizeof(SoftBodyLinkData);
|
|
int numElem = sbd->m_numLinks;
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
SoftBodyLinkData* memPtr = (SoftBodyLinkData*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
memPtr->m_bbending = m_links[i].m_bbending;
|
|
memPtr->m_material = m_links[i].m_material ? (SoftBodyMaterialData*)serializer->getUniquePointer((void*)m_links[i].m_material) : 0;
|
|
memPtr->m_nodeIndices[0] = m_links[i].m_n[0] ? m_links[i].m_n[0] - &m_nodes[0] : -1;
|
|
memPtr->m_nodeIndices[1] = m_links[i].m_n[1] ? m_links[i].m_n[1] - &m_nodes[0] : -1;
|
|
btAssert(memPtr->m_nodeIndices[0] < m_nodes.size());
|
|
btAssert(memPtr->m_nodeIndices[1] < m_nodes.size());
|
|
memPtr->m_restLength = m_links[i].m_rl;
|
|
}
|
|
serializer->finalizeChunk(chunk, "SoftBodyLinkData", BT_ARRAY_CODE, (void*)&m_links[0]);
|
|
}
|
|
|
|
sbd->m_numFaces = m_faces.size();
|
|
sbd->m_faces = sbd->m_numFaces ? (SoftBodyFaceData*)serializer->getUniquePointer((void*)&m_faces[0]) : 0;
|
|
if (sbd->m_faces)
|
|
{
|
|
int sz = sizeof(SoftBodyFaceData);
|
|
int numElem = sbd->m_numFaces;
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
SoftBodyFaceData* memPtr = (SoftBodyFaceData*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
memPtr->m_material = m_faces[i].m_material ? (SoftBodyMaterialData*)serializer->getUniquePointer((void*)m_faces[i].m_material) : 0;
|
|
m_faces[i].m_normal.serializeFloat(memPtr->m_normal);
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
memPtr->m_nodeIndices[j] = m_faces[i].m_n[j] ? m_faces[i].m_n[j] - &m_nodes[0] : -1;
|
|
}
|
|
memPtr->m_restArea = m_faces[i].m_ra;
|
|
}
|
|
serializer->finalizeChunk(chunk, "SoftBodyFaceData", BT_ARRAY_CODE, (void*)&m_faces[0]);
|
|
}
|
|
|
|
sbd->m_numTetrahedra = m_tetras.size();
|
|
sbd->m_tetrahedra = sbd->m_numTetrahedra ? (SoftBodyTetraData*)serializer->getUniquePointer((void*)&m_tetras[0]) : 0;
|
|
if (sbd->m_tetrahedra)
|
|
{
|
|
int sz = sizeof(SoftBodyTetraData);
|
|
int numElem = sbd->m_numTetrahedra;
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
SoftBodyTetraData* memPtr = (SoftBodyTetraData*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
m_tetras[i].m_c0[j].serializeFloat(memPtr->m_c0[j]);
|
|
memPtr->m_nodeIndices[j] = m_tetras[j].m_n[j] ? m_tetras[j].m_n[j] - &m_nodes[0] : -1;
|
|
}
|
|
memPtr->m_c1 = m_tetras[i].m_c1;
|
|
memPtr->m_c2 = m_tetras[i].m_c2;
|
|
memPtr->m_material = m_tetras[i].m_material ? (SoftBodyMaterialData*)serializer->getUniquePointer((void*)m_tetras[i].m_material) : 0;
|
|
memPtr->m_restVolume = m_tetras[i].m_rv;
|
|
}
|
|
serializer->finalizeChunk(chunk, "SoftBodyTetraData", BT_ARRAY_CODE, (void*)&m_tetras[0]);
|
|
}
|
|
|
|
sbd->m_numAnchors = m_anchors.size();
|
|
sbd->m_anchors = sbd->m_numAnchors ? (SoftRigidAnchorData*)serializer->getUniquePointer((void*)&m_anchors[0]) : 0;
|
|
if (sbd->m_anchors)
|
|
{
|
|
int sz = sizeof(SoftRigidAnchorData);
|
|
int numElem = sbd->m_numAnchors;
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
SoftRigidAnchorData* memPtr = (SoftRigidAnchorData*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
m_anchors[i].m_c0.serializeFloat(memPtr->m_c0);
|
|
m_anchors[i].m_c1.serializeFloat(memPtr->m_c1);
|
|
memPtr->m_c2 = m_anchors[i].m_c2;
|
|
m_anchors[i].m_local.serializeFloat(memPtr->m_localFrame);
|
|
memPtr->m_nodeIndex = m_anchors[i].m_node ? m_anchors[i].m_node - &m_nodes[0] : -1;
|
|
|
|
memPtr->m_rigidBody = m_anchors[i].m_body ? (btRigidBodyData*)serializer->getUniquePointer((void*)m_anchors[i].m_body) : 0;
|
|
btAssert(memPtr->m_nodeIndex < m_nodes.size());
|
|
}
|
|
serializer->finalizeChunk(chunk, "SoftRigidAnchorData", BT_ARRAY_CODE, (void*)&m_anchors[0]);
|
|
}
|
|
|
|
sbd->m_config.m_dynamicFriction = m_cfg.kDF;
|
|
sbd->m_config.m_baumgarte = m_cfg.kVCF;
|
|
sbd->m_config.m_pressure = m_cfg.kPR;
|
|
sbd->m_config.m_aeroModel = this->m_cfg.aeromodel;
|
|
sbd->m_config.m_lift = m_cfg.kLF;
|
|
sbd->m_config.m_drag = m_cfg.kDG;
|
|
sbd->m_config.m_positionIterations = m_cfg.piterations;
|
|
sbd->m_config.m_driftIterations = m_cfg.diterations;
|
|
sbd->m_config.m_clusterIterations = m_cfg.citerations;
|
|
sbd->m_config.m_velocityIterations = m_cfg.viterations;
|
|
sbd->m_config.m_maxVolume = m_cfg.maxvolume;
|
|
sbd->m_config.m_damping = m_cfg.kDP;
|
|
sbd->m_config.m_poseMatch = m_cfg.kMT;
|
|
sbd->m_config.m_collisionFlags = m_cfg.collisions;
|
|
sbd->m_config.m_volume = m_cfg.kVC;
|
|
sbd->m_config.m_rigidContactHardness = m_cfg.kCHR;
|
|
sbd->m_config.m_kineticContactHardness = m_cfg.kKHR;
|
|
sbd->m_config.m_softContactHardness = m_cfg.kSHR;
|
|
sbd->m_config.m_anchorHardness = m_cfg.kAHR;
|
|
sbd->m_config.m_timeScale = m_cfg.timescale;
|
|
sbd->m_config.m_maxVolume = m_cfg.maxvolume;
|
|
sbd->m_config.m_softRigidClusterHardness = m_cfg.kSRHR_CL;
|
|
sbd->m_config.m_softKineticClusterHardness = m_cfg.kSKHR_CL;
|
|
sbd->m_config.m_softSoftClusterHardness = m_cfg.kSSHR_CL;
|
|
sbd->m_config.m_softRigidClusterImpulseSplit = m_cfg.kSR_SPLT_CL;
|
|
sbd->m_config.m_softKineticClusterImpulseSplit = m_cfg.kSK_SPLT_CL;
|
|
sbd->m_config.m_softSoftClusterImpulseSplit = m_cfg.kSS_SPLT_CL;
|
|
|
|
//pose for shape matching
|
|
{
|
|
sbd->m_pose = (SoftBodyPoseData*)serializer->getUniquePointer((void*)&m_pose);
|
|
|
|
int sz = sizeof(SoftBodyPoseData);
|
|
btChunk* chunk = serializer->allocate(sz, 1);
|
|
SoftBodyPoseData* memPtr = (SoftBodyPoseData*)chunk->m_oldPtr;
|
|
|
|
m_pose.m_aqq.serializeFloat(memPtr->m_aqq);
|
|
memPtr->m_bframe = m_pose.m_bframe;
|
|
memPtr->m_bvolume = m_pose.m_bvolume;
|
|
m_pose.m_com.serializeFloat(memPtr->m_com);
|
|
|
|
memPtr->m_numPositions = m_pose.m_pos.size();
|
|
memPtr->m_positions = memPtr->m_numPositions ? (btVector3FloatData*)serializer->getUniquePointer((void*)&m_pose.m_pos[0]) : 0;
|
|
if (memPtr->m_numPositions)
|
|
{
|
|
int numElem = memPtr->m_numPositions;
|
|
int sz = sizeof(btVector3Data);
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
btVector3FloatData* memPtr = (btVector3FloatData*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
m_pose.m_pos[i].serializeFloat(*memPtr);
|
|
}
|
|
serializer->finalizeChunk(chunk, "btVector3FloatData", BT_ARRAY_CODE, (void*)&m_pose.m_pos[0]);
|
|
}
|
|
memPtr->m_restVolume = m_pose.m_volume;
|
|
m_pose.m_rot.serializeFloat(memPtr->m_rot);
|
|
m_pose.m_scl.serializeFloat(memPtr->m_scale);
|
|
|
|
memPtr->m_numWeigts = m_pose.m_wgh.size();
|
|
memPtr->m_weights = memPtr->m_numWeigts ? (float*)serializer->getUniquePointer((void*)&m_pose.m_wgh[0]) : 0;
|
|
if (memPtr->m_numWeigts)
|
|
{
|
|
int numElem = memPtr->m_numWeigts;
|
|
int sz = sizeof(float);
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
float* memPtr = (float*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
*memPtr = m_pose.m_wgh[i];
|
|
}
|
|
serializer->finalizeChunk(chunk, "float", BT_ARRAY_CODE, (void*)&m_pose.m_wgh[0]);
|
|
}
|
|
|
|
serializer->finalizeChunk(chunk, "SoftBodyPoseData", BT_ARRAY_CODE, (void*)&m_pose);
|
|
}
|
|
|
|
//clusters for convex-cluster collision detection
|
|
|
|
sbd->m_numClusters = m_clusters.size();
|
|
sbd->m_clusters = sbd->m_numClusters ? (SoftBodyClusterData*)serializer->getUniquePointer((void*)m_clusters[0]) : 0;
|
|
if (sbd->m_numClusters)
|
|
{
|
|
int numElem = sbd->m_numClusters;
|
|
int sz = sizeof(SoftBodyClusterData);
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
SoftBodyClusterData* memPtr = (SoftBodyClusterData*)chunk->m_oldPtr;
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
memPtr->m_adamping = m_clusters[i]->m_adamping;
|
|
m_clusters[i]->m_av.serializeFloat(memPtr->m_av);
|
|
memPtr->m_clusterIndex = m_clusters[i]->m_clusterIndex;
|
|
memPtr->m_collide = m_clusters[i]->m_collide;
|
|
m_clusters[i]->m_com.serializeFloat(memPtr->m_com);
|
|
memPtr->m_containsAnchor = m_clusters[i]->m_containsAnchor;
|
|
m_clusters[i]->m_dimpulses[0].serializeFloat(memPtr->m_dimpulses[0]);
|
|
m_clusters[i]->m_dimpulses[1].serializeFloat(memPtr->m_dimpulses[1]);
|
|
m_clusters[i]->m_framexform.serializeFloat(memPtr->m_framexform);
|
|
memPtr->m_idmass = m_clusters[i]->m_idmass;
|
|
memPtr->m_imass = m_clusters[i]->m_imass;
|
|
m_clusters[i]->m_invwi.serializeFloat(memPtr->m_invwi);
|
|
memPtr->m_ldamping = m_clusters[i]->m_ldamping;
|
|
m_clusters[i]->m_locii.serializeFloat(memPtr->m_locii);
|
|
m_clusters[i]->m_lv.serializeFloat(memPtr->m_lv);
|
|
memPtr->m_matching = m_clusters[i]->m_matching;
|
|
memPtr->m_maxSelfCollisionImpulse = m_clusters[i]->m_maxSelfCollisionImpulse;
|
|
memPtr->m_ndamping = m_clusters[i]->m_ndamping;
|
|
memPtr->m_ldamping = m_clusters[i]->m_ldamping;
|
|
memPtr->m_adamping = m_clusters[i]->m_adamping;
|
|
memPtr->m_selfCollisionImpulseFactor = m_clusters[i]->m_selfCollisionImpulseFactor;
|
|
|
|
memPtr->m_numFrameRefs = m_clusters[i]->m_framerefs.size();
|
|
memPtr->m_numMasses = m_clusters[i]->m_masses.size();
|
|
memPtr->m_numNodes = m_clusters[i]->m_nodes.size();
|
|
|
|
memPtr->m_nvimpulses = m_clusters[i]->m_nvimpulses;
|
|
m_clusters[i]->m_vimpulses[0].serializeFloat(memPtr->m_vimpulses[0]);
|
|
m_clusters[i]->m_vimpulses[1].serializeFloat(memPtr->m_vimpulses[1]);
|
|
memPtr->m_ndimpulses = m_clusters[i]->m_ndimpulses;
|
|
|
|
memPtr->m_framerefs = memPtr->m_numFrameRefs ? (btVector3FloatData*)serializer->getUniquePointer((void*)&m_clusters[i]->m_framerefs[0]) : 0;
|
|
if (memPtr->m_framerefs)
|
|
{
|
|
int numElem = memPtr->m_numFrameRefs;
|
|
int sz = sizeof(btVector3FloatData);
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
btVector3FloatData* memPtr = (btVector3FloatData*)chunk->m_oldPtr;
|
|
for (int j = 0; j < numElem; j++, memPtr++)
|
|
{
|
|
m_clusters[i]->m_framerefs[j].serializeFloat(*memPtr);
|
|
}
|
|
serializer->finalizeChunk(chunk, "btVector3FloatData", BT_ARRAY_CODE, (void*)&m_clusters[i]->m_framerefs[0]);
|
|
}
|
|
|
|
memPtr->m_masses = memPtr->m_numMasses ? (float*)serializer->getUniquePointer((void*)&m_clusters[i]->m_masses[0]) : 0;
|
|
if (memPtr->m_masses)
|
|
{
|
|
int numElem = memPtr->m_numMasses;
|
|
int sz = sizeof(float);
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
float* memPtr = (float*)chunk->m_oldPtr;
|
|
for (int j = 0; j < numElem; j++, memPtr++)
|
|
{
|
|
*memPtr = m_clusters[i]->m_masses[j];
|
|
}
|
|
serializer->finalizeChunk(chunk, "float", BT_ARRAY_CODE, (void*)&m_clusters[i]->m_masses[0]);
|
|
}
|
|
|
|
memPtr->m_nodeIndices = memPtr->m_numNodes ? (int*)serializer->getUniquePointer((void*)&m_clusters[i]->m_nodes) : 0;
|
|
if (memPtr->m_nodeIndices)
|
|
{
|
|
int numElem = memPtr->m_numMasses;
|
|
int sz = sizeof(int);
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
int* memPtr = (int*)chunk->m_oldPtr;
|
|
for (int j = 0; j < numElem; j++, memPtr++)
|
|
{
|
|
int* indexPtr = m_nodeIndexMap.find(m_clusters[i]->m_nodes[j]);
|
|
btAssert(indexPtr);
|
|
*memPtr = *indexPtr;
|
|
}
|
|
serializer->finalizeChunk(chunk, "int", BT_ARRAY_CODE, (void*)&m_clusters[i]->m_nodes);
|
|
}
|
|
}
|
|
serializer->finalizeChunk(chunk, "SoftBodyClusterData", BT_ARRAY_CODE, (void*)m_clusters[0]);
|
|
}
|
|
|
|
sbd->m_numJoints = m_joints.size();
|
|
sbd->m_joints = m_joints.size() ? (btSoftBodyJointData*)serializer->getUniquePointer((void*)&m_joints[0]) : 0;
|
|
|
|
if (sbd->m_joints)
|
|
{
|
|
int sz = sizeof(btSoftBodyJointData);
|
|
int numElem = m_joints.size();
|
|
btChunk* chunk = serializer->allocate(sz, numElem);
|
|
btSoftBodyJointData* memPtr = (btSoftBodyJointData*)chunk->m_oldPtr;
|
|
|
|
for (int i = 0; i < numElem; i++, memPtr++)
|
|
{
|
|
memPtr->m_jointType = (int)m_joints[i]->Type();
|
|
m_joints[i]->m_refs[0].serializeFloat(memPtr->m_refs[0]);
|
|
m_joints[i]->m_refs[1].serializeFloat(memPtr->m_refs[1]);
|
|
memPtr->m_cfm = m_joints[i]->m_cfm;
|
|
memPtr->m_erp = float(m_joints[i]->m_erp);
|
|
memPtr->m_split = float(m_joints[i]->m_split);
|
|
memPtr->m_delete = m_joints[i]->m_delete;
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
memPtr->m_relPosition[0].m_floats[j] = 0.f;
|
|
memPtr->m_relPosition[1].m_floats[j] = 0.f;
|
|
}
|
|
memPtr->m_bodyA = 0;
|
|
memPtr->m_bodyB = 0;
|
|
if (m_joints[i]->m_bodies[0].m_soft)
|
|
{
|
|
memPtr->m_bodyAtype = BT_JOINT_SOFT_BODY_CLUSTER;
|
|
memPtr->m_bodyA = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[0].m_soft);
|
|
}
|
|
if (m_joints[i]->m_bodies[0].m_collisionObject)
|
|
{
|
|
memPtr->m_bodyAtype = BT_JOINT_COLLISION_OBJECT;
|
|
memPtr->m_bodyA = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[0].m_collisionObject);
|
|
}
|
|
if (m_joints[i]->m_bodies[0].m_rigid)
|
|
{
|
|
memPtr->m_bodyAtype = BT_JOINT_RIGID_BODY;
|
|
memPtr->m_bodyA = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[0].m_rigid);
|
|
}
|
|
|
|
if (m_joints[i]->m_bodies[1].m_soft)
|
|
{
|
|
memPtr->m_bodyBtype = BT_JOINT_SOFT_BODY_CLUSTER;
|
|
memPtr->m_bodyB = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[1].m_soft);
|
|
}
|
|
if (m_joints[i]->m_bodies[1].m_collisionObject)
|
|
{
|
|
memPtr->m_bodyBtype = BT_JOINT_COLLISION_OBJECT;
|
|
memPtr->m_bodyB = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[1].m_collisionObject);
|
|
}
|
|
if (m_joints[i]->m_bodies[1].m_rigid)
|
|
{
|
|
memPtr->m_bodyBtype = BT_JOINT_RIGID_BODY;
|
|
memPtr->m_bodyB = serializer->getUniquePointer((void*)m_joints[i]->m_bodies[1].m_rigid);
|
|
}
|
|
}
|
|
serializer->finalizeChunk(chunk, "btSoftBodyJointData", BT_ARRAY_CODE, (void*)&m_joints[0]);
|
|
}
|
|
|
|
return btSoftBodyDataName;
|
|
}
|