29e07dfa4e
This allows distro unbundling again for distros that ship Bullet 2.89+.
1547 lines
46 KiB
C++
1547 lines
46 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.
|
|
*/
|
|
///btSoftBodyHelpers.cpp by Nathanael Presson
|
|
|
|
#include "btSoftBodyInternals.h"
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
#include "btSoftBodyHelpers.h"
|
|
#include "LinearMath/btConvexHull.h"
|
|
#include "LinearMath/btConvexHullComputer.h"
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
static void drawVertex(btIDebugDraw* idraw,
|
|
const btVector3& x, btScalar s, const btVector3& c)
|
|
{
|
|
idraw->drawLine(x - btVector3(s, 0, 0), x + btVector3(s, 0, 0), c);
|
|
idraw->drawLine(x - btVector3(0, s, 0), x + btVector3(0, s, 0), c);
|
|
idraw->drawLine(x - btVector3(0, 0, s), x + btVector3(0, 0, s), c);
|
|
}
|
|
|
|
//
|
|
static void drawBox(btIDebugDraw* idraw,
|
|
const btVector3& mins,
|
|
const btVector3& maxs,
|
|
const btVector3& color)
|
|
{
|
|
const btVector3 c[] = {btVector3(mins.x(), mins.y(), mins.z()),
|
|
btVector3(maxs.x(), mins.y(), mins.z()),
|
|
btVector3(maxs.x(), maxs.y(), mins.z()),
|
|
btVector3(mins.x(), maxs.y(), mins.z()),
|
|
btVector3(mins.x(), mins.y(), maxs.z()),
|
|
btVector3(maxs.x(), mins.y(), maxs.z()),
|
|
btVector3(maxs.x(), maxs.y(), maxs.z()),
|
|
btVector3(mins.x(), maxs.y(), maxs.z())};
|
|
idraw->drawLine(c[0], c[1], color);
|
|
idraw->drawLine(c[1], c[2], color);
|
|
idraw->drawLine(c[2], c[3], color);
|
|
idraw->drawLine(c[3], c[0], color);
|
|
idraw->drawLine(c[4], c[5], color);
|
|
idraw->drawLine(c[5], c[6], color);
|
|
idraw->drawLine(c[6], c[7], color);
|
|
idraw->drawLine(c[7], c[4], color);
|
|
idraw->drawLine(c[0], c[4], color);
|
|
idraw->drawLine(c[1], c[5], color);
|
|
idraw->drawLine(c[2], c[6], color);
|
|
idraw->drawLine(c[3], c[7], color);
|
|
}
|
|
|
|
//
|
|
static void drawTree(btIDebugDraw* idraw,
|
|
const btDbvtNode* node,
|
|
int depth,
|
|
const btVector3& ncolor,
|
|
const btVector3& lcolor,
|
|
int mindepth,
|
|
int maxdepth)
|
|
{
|
|
if (node)
|
|
{
|
|
if (node->isinternal() && ((depth < maxdepth) || (maxdepth < 0)))
|
|
{
|
|
drawTree(idraw, node->childs[0], depth + 1, ncolor, lcolor, mindepth, maxdepth);
|
|
drawTree(idraw, node->childs[1], depth + 1, ncolor, lcolor, mindepth, maxdepth);
|
|
}
|
|
if (depth >= mindepth)
|
|
{
|
|
const btScalar scl = (btScalar)(node->isinternal() ? 1 : 1);
|
|
const btVector3 mi = node->volume.Center() - node->volume.Extents() * scl;
|
|
const btVector3 mx = node->volume.Center() + node->volume.Extents() * scl;
|
|
drawBox(idraw, mi, mx, node->isleaf() ? lcolor : ncolor);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
template <typename T>
|
|
static inline T sum(const btAlignedObjectArray<T>& items)
|
|
{
|
|
T v;
|
|
if (items.size())
|
|
{
|
|
v = items[0];
|
|
for (int i = 1, ni = items.size(); i < ni; ++i)
|
|
{
|
|
v += items[i];
|
|
}
|
|
}
|
|
return (v);
|
|
}
|
|
|
|
//
|
|
template <typename T, typename Q>
|
|
static inline void add(btAlignedObjectArray<T>& items, const Q& value)
|
|
{
|
|
for (int i = 0, ni = items.size(); i < ni; ++i)
|
|
{
|
|
items[i] += value;
|
|
}
|
|
}
|
|
|
|
//
|
|
template <typename T, typename Q>
|
|
static inline void mul(btAlignedObjectArray<T>& items, const Q& value)
|
|
{
|
|
for (int i = 0, ni = items.size(); i < ni; ++i)
|
|
{
|
|
items[i] *= value;
|
|
}
|
|
}
|
|
|
|
//
|
|
template <typename T>
|
|
static inline T average(const btAlignedObjectArray<T>& items)
|
|
{
|
|
const btScalar n = (btScalar)(items.size() > 0 ? items.size() : 1);
|
|
return (sum(items) / n);
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
inline static btScalar tetravolume(const btVector3& x0,
|
|
const btVector3& x1,
|
|
const btVector3& x2,
|
|
const btVector3& x3)
|
|
{
|
|
const btVector3 a=x1-x0;
|
|
const btVector3 b=x2-x0;
|
|
const btVector3 c=x3-x0;
|
|
return(btDot(a,btCross(b,c)));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
#if 0
|
|
static btVector3 stresscolor(btScalar stress)
|
|
{
|
|
static const btVector3 spectrum[]= { btVector3(1,0,1),
|
|
btVector3(0,0,1),
|
|
btVector3(0,1,1),
|
|
btVector3(0,1,0),
|
|
btVector3(1,1,0),
|
|
btVector3(1,0,0),
|
|
btVector3(1,0,0)};
|
|
static const int ncolors=sizeof(spectrum)/sizeof(spectrum[0])-1;
|
|
static const btScalar one=1;
|
|
stress=btMax<btScalar>(0,btMin<btScalar>(1,stress))*ncolors;
|
|
const int sel=(int)stress;
|
|
const btScalar frc=stress-sel;
|
|
return(spectrum[sel]+(spectrum[sel+1]-spectrum[sel])*frc);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
void btSoftBodyHelpers::Draw(btSoftBody* psb,
|
|
btIDebugDraw* idraw,
|
|
int drawflags)
|
|
{
|
|
const btScalar scl = (btScalar)0.1;
|
|
const btScalar nscl = scl * 5;
|
|
const btVector3 lcolor = btVector3(0, 0, 0);
|
|
const btVector3 ncolor = btVector3(1, 1, 1);
|
|
const btVector3 ccolor = btVector3(1, 0, 0);
|
|
int i, j, nj;
|
|
|
|
/* Clusters */
|
|
if (0 != (drawflags & fDrawFlags::Clusters))
|
|
{
|
|
srand(1806);
|
|
for (i = 0; i < psb->m_clusters.size(); ++i)
|
|
{
|
|
if (psb->m_clusters[i]->m_collide)
|
|
{
|
|
btVector3 color(rand() / (btScalar)RAND_MAX,
|
|
rand() / (btScalar)RAND_MAX,
|
|
rand() / (btScalar)RAND_MAX);
|
|
color = color.normalized() * 0.75;
|
|
btAlignedObjectArray<btVector3> vertices;
|
|
vertices.resize(psb->m_clusters[i]->m_nodes.size());
|
|
for (j = 0, nj = vertices.size(); j < nj; ++j)
|
|
{
|
|
vertices[j] = psb->m_clusters[i]->m_nodes[j]->m_x;
|
|
}
|
|
#define USE_NEW_CONVEX_HULL_COMPUTER
|
|
#ifdef USE_NEW_CONVEX_HULL_COMPUTER
|
|
btConvexHullComputer computer;
|
|
int stride = sizeof(btVector3);
|
|
int count = vertices.size();
|
|
btScalar shrink = 0.f;
|
|
btScalar shrinkClamp = 0.f;
|
|
computer.compute(&vertices[0].getX(), stride, count, shrink, shrinkClamp);
|
|
for (int i = 0; i < computer.faces.size(); i++)
|
|
{
|
|
int face = computer.faces[i];
|
|
//printf("face=%d\n",face);
|
|
const btConvexHullComputer::Edge* firstEdge = &computer.edges[face];
|
|
const btConvexHullComputer::Edge* edge = firstEdge->getNextEdgeOfFace();
|
|
|
|
int v0 = firstEdge->getSourceVertex();
|
|
int v1 = firstEdge->getTargetVertex();
|
|
while (edge != firstEdge)
|
|
{
|
|
int v2 = edge->getTargetVertex();
|
|
idraw->drawTriangle(computer.vertices[v0], computer.vertices[v1], computer.vertices[v2], color, 1);
|
|
edge = edge->getNextEdgeOfFace();
|
|
v0 = v1;
|
|
v1 = v2;
|
|
};
|
|
}
|
|
#else
|
|
|
|
HullDesc hdsc(QF_TRIANGLES, vertices.size(), &vertices[0]);
|
|
HullResult hres;
|
|
HullLibrary hlib;
|
|
hdsc.mMaxVertices = vertices.size();
|
|
hlib.CreateConvexHull(hdsc, hres);
|
|
const btVector3 center = average(hres.m_OutputVertices);
|
|
add(hres.m_OutputVertices, -center);
|
|
mul(hres.m_OutputVertices, (btScalar)1);
|
|
add(hres.m_OutputVertices, center);
|
|
for (j = 0; j < (int)hres.mNumFaces; ++j)
|
|
{
|
|
const int idx[] = {hres.m_Indices[j * 3 + 0], hres.m_Indices[j * 3 + 1], hres.m_Indices[j * 3 + 2]};
|
|
idraw->drawTriangle(hres.m_OutputVertices[idx[0]],
|
|
hres.m_OutputVertices[idx[1]],
|
|
hres.m_OutputVertices[idx[2]],
|
|
color, 1);
|
|
}
|
|
hlib.ReleaseResult(hres);
|
|
#endif
|
|
}
|
|
/* Velocities */
|
|
#if 0
|
|
for(int j=0;j<psb->m_clusters[i].m_nodes.size();++j)
|
|
{
|
|
const btSoftBody::Cluster& c=psb->m_clusters[i];
|
|
const btVector3 r=c.m_nodes[j]->m_x-c.m_com;
|
|
const btVector3 v=c.m_lv+btCross(c.m_av,r);
|
|
idraw->drawLine(c.m_nodes[j]->m_x,c.m_nodes[j]->m_x+v,btVector3(1,0,0));
|
|
}
|
|
#endif
|
|
/* Frame */
|
|
// btSoftBody::Cluster& c=*psb->m_clusters[i];
|
|
// idraw->drawLine(c.m_com,c.m_framexform*btVector3(10,0,0),btVector3(1,0,0));
|
|
// idraw->drawLine(c.m_com,c.m_framexform*btVector3(0,10,0),btVector3(0,1,0));
|
|
// idraw->drawLine(c.m_com,c.m_framexform*btVector3(0,0,10),btVector3(0,0,1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Nodes */
|
|
if (0 != (drawflags & fDrawFlags::Nodes))
|
|
{
|
|
for (i = 0; i < psb->m_nodes.size(); ++i)
|
|
{
|
|
const btSoftBody::Node& n = psb->m_nodes[i];
|
|
if (0 == (n.m_material->m_flags & btSoftBody::fMaterial::DebugDraw)) continue;
|
|
idraw->drawLine(n.m_x - btVector3(scl, 0, 0), n.m_x + btVector3(scl, 0, 0), btVector3(1, 0, 0));
|
|
idraw->drawLine(n.m_x - btVector3(0, scl, 0), n.m_x + btVector3(0, scl, 0), btVector3(0, 1, 0));
|
|
idraw->drawLine(n.m_x - btVector3(0, 0, scl), n.m_x + btVector3(0, 0, scl), btVector3(0, 0, 1));
|
|
}
|
|
}
|
|
/* Links */
|
|
if (0 != (drawflags & fDrawFlags::Links))
|
|
{
|
|
for (i = 0; i < psb->m_links.size(); ++i)
|
|
{
|
|
const btSoftBody::Link& l = psb->m_links[i];
|
|
if (0 == (l.m_material->m_flags & btSoftBody::fMaterial::DebugDraw)) continue;
|
|
idraw->drawLine(l.m_n[0]->m_x, l.m_n[1]->m_x, lcolor);
|
|
}
|
|
}
|
|
/* Normals */
|
|
if (0 != (drawflags & fDrawFlags::Normals))
|
|
{
|
|
for (i = 0; i < psb->m_nodes.size(); ++i)
|
|
{
|
|
const btSoftBody::Node& n = psb->m_nodes[i];
|
|
if (0 == (n.m_material->m_flags & btSoftBody::fMaterial::DebugDraw)) continue;
|
|
const btVector3 d = n.m_n * nscl;
|
|
idraw->drawLine(n.m_x, n.m_x + d, ncolor);
|
|
idraw->drawLine(n.m_x, n.m_x - d, ncolor * 0.5);
|
|
}
|
|
}
|
|
/* Contacts */
|
|
if (0 != (drawflags & fDrawFlags::Contacts))
|
|
{
|
|
static const btVector3 axis[] = {btVector3(1, 0, 0),
|
|
btVector3(0, 1, 0),
|
|
btVector3(0, 0, 1)};
|
|
for (i = 0; i < psb->m_rcontacts.size(); ++i)
|
|
{
|
|
const btSoftBody::RContact& c = psb->m_rcontacts[i];
|
|
const btVector3 o = c.m_node->m_x - c.m_cti.m_normal *
|
|
(btDot(c.m_node->m_x, c.m_cti.m_normal) + c.m_cti.m_offset);
|
|
const btVector3 x = btCross(c.m_cti.m_normal, axis[c.m_cti.m_normal.minAxis()]).normalized();
|
|
const btVector3 y = btCross(x, c.m_cti.m_normal).normalized();
|
|
idraw->drawLine(o - x * nscl, o + x * nscl, ccolor);
|
|
idraw->drawLine(o - y * nscl, o + y * nscl, ccolor);
|
|
idraw->drawLine(o, o + c.m_cti.m_normal * nscl * 3, btVector3(1, 1, 0));
|
|
}
|
|
}
|
|
/* Faces */
|
|
if (0 != (drawflags & fDrawFlags::Faces))
|
|
{
|
|
const btScalar scl = (btScalar)0.8;
|
|
const btScalar alp = (btScalar)1;
|
|
const btVector3 col(0, (btScalar)0.7, 0);
|
|
for (i = 0; i < psb->m_faces.size(); ++i)
|
|
{
|
|
const btSoftBody::Face& f = psb->m_faces[i];
|
|
if (0 == (f.m_material->m_flags & btSoftBody::fMaterial::DebugDraw)) continue;
|
|
const btVector3 x[] = {f.m_n[0]->m_x, f.m_n[1]->m_x, f.m_n[2]->m_x};
|
|
const btVector3 c = (x[0] + x[1] + x[2]) / 3;
|
|
idraw->drawTriangle((x[0] - c) * scl + c,
|
|
(x[1] - c) * scl + c,
|
|
(x[2] - c) * scl + c,
|
|
col, alp);
|
|
}
|
|
}
|
|
/* Tetras */
|
|
if (0 != (drawflags & fDrawFlags::Tetras))
|
|
{
|
|
const btScalar scl = (btScalar)0.8;
|
|
const btScalar alp = (btScalar)1;
|
|
const btVector3 col((btScalar)0.3, (btScalar)0.3, (btScalar)0.7);
|
|
for (int i = 0; i < psb->m_tetras.size(); ++i)
|
|
{
|
|
const btSoftBody::Tetra& t = psb->m_tetras[i];
|
|
if (0 == (t.m_material->m_flags & btSoftBody::fMaterial::DebugDraw)) continue;
|
|
const btVector3 x[] = {t.m_n[0]->m_x, t.m_n[1]->m_x, t.m_n[2]->m_x, t.m_n[3]->m_x};
|
|
const btVector3 c = (x[0] + x[1] + x[2] + x[3]) / 4;
|
|
idraw->drawTriangle((x[0] - c) * scl + c, (x[1] - c) * scl + c, (x[2] - c) * scl + c, col, alp);
|
|
idraw->drawTriangle((x[0] - c) * scl + c, (x[1] - c) * scl + c, (x[3] - c) * scl + c, col, alp);
|
|
idraw->drawTriangle((x[1] - c) * scl + c, (x[2] - c) * scl + c, (x[3] - c) * scl + c, col, alp);
|
|
idraw->drawTriangle((x[2] - c) * scl + c, (x[0] - c) * scl + c, (x[3] - c) * scl + c, col, alp);
|
|
}
|
|
}
|
|
}
|
|
/* Anchors */
|
|
if (0 != (drawflags & fDrawFlags::Anchors))
|
|
{
|
|
for (i = 0; i < psb->m_anchors.size(); ++i)
|
|
{
|
|
const btSoftBody::Anchor& a = psb->m_anchors[i];
|
|
const btVector3 q = a.m_body->getWorldTransform() * a.m_local;
|
|
drawVertex(idraw, a.m_node->m_x, 0.25, btVector3(1, 0, 0));
|
|
drawVertex(idraw, q, 0.25, btVector3(0, 1, 0));
|
|
idraw->drawLine(a.m_node->m_x, q, btVector3(1, 1, 1));
|
|
}
|
|
for (i = 0; i < psb->m_nodes.size(); ++i)
|
|
{
|
|
const btSoftBody::Node& n = psb->m_nodes[i];
|
|
if (0 == (n.m_material->m_flags & btSoftBody::fMaterial::DebugDraw)) continue;
|
|
if (n.m_im <= 0)
|
|
{
|
|
drawVertex(idraw, n.m_x, 0.25, btVector3(1, 0, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Notes */
|
|
if (0 != (drawflags & fDrawFlags::Notes))
|
|
{
|
|
for (i = 0; i < psb->m_notes.size(); ++i)
|
|
{
|
|
const btSoftBody::Note& n = psb->m_notes[i];
|
|
btVector3 p = n.m_offset;
|
|
for (int j = 0; j < n.m_rank; ++j)
|
|
{
|
|
p += n.m_nodes[j]->m_x * n.m_coords[j];
|
|
}
|
|
idraw->draw3dText(p, n.m_text);
|
|
}
|
|
}
|
|
/* Node tree */
|
|
if (0 != (drawflags & fDrawFlags::NodeTree)) DrawNodeTree(psb, idraw);
|
|
/* Face tree */
|
|
if (0 != (drawflags & fDrawFlags::FaceTree)) DrawFaceTree(psb, idraw);
|
|
/* Cluster tree */
|
|
if (0 != (drawflags & fDrawFlags::ClusterTree)) DrawClusterTree(psb, idraw);
|
|
/* Joints */
|
|
if (0 != (drawflags & fDrawFlags::Joints))
|
|
{
|
|
for (i = 0; i < psb->m_joints.size(); ++i)
|
|
{
|
|
const btSoftBody::Joint* pj = psb->m_joints[i];
|
|
switch (pj->Type())
|
|
{
|
|
case btSoftBody::Joint::eType::Linear:
|
|
{
|
|
const btSoftBody::LJoint* pjl = (const btSoftBody::LJoint*)pj;
|
|
const btVector3 a0 = pj->m_bodies[0].xform() * pjl->m_refs[0];
|
|
const btVector3 a1 = pj->m_bodies[1].xform() * pjl->m_refs[1];
|
|
idraw->drawLine(pj->m_bodies[0].xform().getOrigin(), a0, btVector3(1, 1, 0));
|
|
idraw->drawLine(pj->m_bodies[1].xform().getOrigin(), a1, btVector3(0, 1, 1));
|
|
drawVertex(idraw, a0, 0.25, btVector3(1, 1, 0));
|
|
drawVertex(idraw, a1, 0.25, btVector3(0, 1, 1));
|
|
}
|
|
break;
|
|
case btSoftBody::Joint::eType::Angular:
|
|
{
|
|
//const btSoftBody::AJoint* pja=(const btSoftBody::AJoint*)pj;
|
|
const btVector3 o0 = pj->m_bodies[0].xform().getOrigin();
|
|
const btVector3 o1 = pj->m_bodies[1].xform().getOrigin();
|
|
const btVector3 a0 = pj->m_bodies[0].xform().getBasis() * pj->m_refs[0];
|
|
const btVector3 a1 = pj->m_bodies[1].xform().getBasis() * pj->m_refs[1];
|
|
idraw->drawLine(o0, o0 + a0 * 10, btVector3(1, 1, 0));
|
|
idraw->drawLine(o0, o0 + a1 * 10, btVector3(1, 1, 0));
|
|
idraw->drawLine(o1, o1 + a0 * 10, btVector3(0, 1, 1));
|
|
idraw->drawLine(o1, o1 + a1 * 10, btVector3(0, 1, 1));
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBodyHelpers::DrawInfos(btSoftBody* psb,
|
|
btIDebugDraw* idraw,
|
|
bool masses,
|
|
bool areas,
|
|
bool /*stress*/)
|
|
{
|
|
for (int i = 0; i < psb->m_nodes.size(); ++i)
|
|
{
|
|
const btSoftBody::Node& n = psb->m_nodes[i];
|
|
char text[2048] = {0};
|
|
char buff[1024];
|
|
if (masses)
|
|
{
|
|
sprintf(buff, " M(%.2f)", 1 / n.m_im);
|
|
strcat(text, buff);
|
|
}
|
|
if (areas)
|
|
{
|
|
sprintf(buff, " A(%.2f)", n.m_area);
|
|
strcat(text, buff);
|
|
}
|
|
if (text[0]) idraw->draw3dText(n.m_x, text);
|
|
}
|
|
}
|
|
|
|
//
|
|
void btSoftBodyHelpers::DrawNodeTree(btSoftBody* psb,
|
|
btIDebugDraw* idraw,
|
|
int mindepth,
|
|
int maxdepth)
|
|
{
|
|
drawTree(idraw, psb->m_ndbvt.m_root, 0, btVector3(1, 0, 1), btVector3(1, 1, 1), mindepth, maxdepth);
|
|
}
|
|
|
|
//
|
|
void btSoftBodyHelpers::DrawFaceTree(btSoftBody* psb,
|
|
btIDebugDraw* idraw,
|
|
int mindepth,
|
|
int maxdepth)
|
|
{
|
|
drawTree(idraw, psb->m_fdbvt.m_root, 0, btVector3(0, 1, 0), btVector3(1, 0, 0), mindepth, maxdepth);
|
|
}
|
|
|
|
//
|
|
void btSoftBodyHelpers::DrawClusterTree(btSoftBody* psb,
|
|
btIDebugDraw* idraw,
|
|
int mindepth,
|
|
int maxdepth)
|
|
{
|
|
drawTree(idraw, psb->m_cdbvt.m_root, 0, btVector3(0, 1, 1), btVector3(1, 0, 0), mindepth, maxdepth);
|
|
}
|
|
|
|
//The btSoftBody object from the BulletSDK includes an array of Nodes and Links. These links appear
|
|
// to be first set up to connect a node to between 5 and 6 of its neighbors [480 links],
|
|
//and then to the rest of the nodes after the execution of the Floyd-Warshall graph algorithm
|
|
//[another 930 links].
|
|
//The way the links are stored by default, we have a number of cases where adjacent links share a node in common
|
|
// - this leads to the creation of a data dependency through memory.
|
|
//The PSolve_Links() function reads and writes nodes as it iterates over each link.
|
|
//So, we now have the possibility of a data dependency between iteration X
|
|
//that processes link L with iteration X+1 that processes link L+1
|
|
//because L and L+1 have one node in common, and iteration X updates the positions of that node,
|
|
//and iteration X+1 reads in the position of that shared node.
|
|
//
|
|
//Such a memory dependency limits the ability of a modern CPU to speculate beyond
|
|
//a certain point because it has to respect a possible dependency
|
|
//- this prevents the CPU from making full use of its out-of-order resources.
|
|
//If we re-order the links such that we minimize the cases where a link L and L+1 share a common node,
|
|
//we create a temporal gap between when the node position is written,
|
|
//and when it is subsequently read. This in turn allows the CPU to continue execution without
|
|
//risking a dependency violation. Such a reordering would result in significant speedups on
|
|
//modern CPUs with lots of execution resources.
|
|
//In our testing, we see it have a tremendous impact not only on the A7,
|
|
//but also on all x86 cores that ship with modern Macs.
|
|
//The attached source file includes a single function (ReoptimizeLinkOrder) which can be called on a
|
|
//btSoftBody object in the solveConstraints() function before the actual solver is invoked,
|
|
//or right after generateBendingConstraints() once we have all 1410 links.
|
|
|
|
//===================================================================
|
|
//
|
|
//
|
|
// This function takes in a list of interdependent Links and tries
|
|
// to maximize the distance between calculation
|
|
// of dependent links. This increases the amount of parallelism that can
|
|
// be exploited by out-of-order instruction processors with large but
|
|
// (inevitably) finite instruction windows.
|
|
//
|
|
//===================================================================
|
|
|
|
// A small structure to track lists of dependent link calculations
|
|
class LinkDeps_t
|
|
{
|
|
public:
|
|
int value; // A link calculation that is dependent on this one
|
|
// Positive values = "input A" while negative values = "input B"
|
|
LinkDeps_t* next; // Next dependence in the list
|
|
};
|
|
typedef LinkDeps_t* LinkDepsPtr_t;
|
|
|
|
// Dependency list constants
|
|
#define REOP_NOT_DEPENDENT -1
|
|
#define REOP_NODE_COMPLETE -2 // Must be less than REOP_NOT_DEPENDENT
|
|
|
|
void btSoftBodyHelpers::ReoptimizeLinkOrder(btSoftBody* psb /* This can be replaced by a btSoftBody pointer */)
|
|
{
|
|
int i, nLinks = psb->m_links.size(), nNodes = psb->m_nodes.size();
|
|
btSoftBody::Link* lr;
|
|
int ar, br;
|
|
btSoftBody::Node* node0 = &(psb->m_nodes[0]);
|
|
btSoftBody::Node* node1 = &(psb->m_nodes[1]);
|
|
LinkDepsPtr_t linkDep;
|
|
int readyListHead, readyListTail, linkNum, linkDepFrees, depLink;
|
|
|
|
// Allocate temporary buffers
|
|
int* nodeWrittenAt = new int[nNodes + 1]; // What link calculation produced this node's current values?
|
|
int* linkDepA = new int[nLinks]; // Link calculation input is dependent upon prior calculation #N
|
|
int* linkDepB = new int[nLinks];
|
|
int* readyList = new int[nLinks]; // List of ready-to-process link calculations (# of links, maximum)
|
|
LinkDeps_t* linkDepFreeList = new LinkDeps_t[2 * nLinks]; // Dependent-on-me list elements (2x# of links, maximum)
|
|
LinkDepsPtr_t* linkDepListStarts = new LinkDepsPtr_t[nLinks]; // Start nodes of dependent-on-me lists, one for each link
|
|
|
|
// Copy the original, unsorted links to a side buffer
|
|
btSoftBody::Link* linkBuffer = new btSoftBody::Link[nLinks];
|
|
memcpy(linkBuffer, &(psb->m_links[0]), sizeof(btSoftBody::Link) * nLinks);
|
|
|
|
// Clear out the node setup and ready list
|
|
for (i = 0; i < nNodes + 1; i++)
|
|
{
|
|
nodeWrittenAt[i] = REOP_NOT_DEPENDENT;
|
|
}
|
|
for (i = 0; i < nLinks; i++)
|
|
{
|
|
linkDepListStarts[i] = NULL;
|
|
}
|
|
readyListHead = readyListTail = linkDepFrees = 0;
|
|
|
|
// Initial link analysis to set up data structures
|
|
for (i = 0; i < nLinks; i++)
|
|
{
|
|
// Note which prior link calculations we are dependent upon & build up dependence lists
|
|
lr = &(psb->m_links[i]);
|
|
ar = (lr->m_n[0] - node0) / (node1 - node0);
|
|
br = (lr->m_n[1] - node0) / (node1 - node0);
|
|
if (nodeWrittenAt[ar] > REOP_NOT_DEPENDENT)
|
|
{
|
|
linkDepA[i] = nodeWrittenAt[ar];
|
|
linkDep = &linkDepFreeList[linkDepFrees++];
|
|
linkDep->value = i;
|
|
linkDep->next = linkDepListStarts[nodeWrittenAt[ar]];
|
|
linkDepListStarts[nodeWrittenAt[ar]] = linkDep;
|
|
}
|
|
else
|
|
{
|
|
linkDepA[i] = REOP_NOT_DEPENDENT;
|
|
}
|
|
if (nodeWrittenAt[br] > REOP_NOT_DEPENDENT)
|
|
{
|
|
linkDepB[i] = nodeWrittenAt[br];
|
|
linkDep = &linkDepFreeList[linkDepFrees++];
|
|
linkDep->value = -(i + 1);
|
|
linkDep->next = linkDepListStarts[nodeWrittenAt[br]];
|
|
linkDepListStarts[nodeWrittenAt[br]] = linkDep;
|
|
}
|
|
else
|
|
{
|
|
linkDepB[i] = REOP_NOT_DEPENDENT;
|
|
}
|
|
|
|
// Add this link to the initial ready list, if it is not dependent on any other links
|
|
if ((linkDepA[i] == REOP_NOT_DEPENDENT) && (linkDepB[i] == REOP_NOT_DEPENDENT))
|
|
{
|
|
readyList[readyListTail++] = i;
|
|
linkDepA[i] = linkDepB[i] = REOP_NODE_COMPLETE; // Probably not needed now
|
|
}
|
|
|
|
// Update the nodes to mark which ones are calculated by this link
|
|
nodeWrittenAt[ar] = nodeWrittenAt[br] = i;
|
|
}
|
|
|
|
// Process the ready list and create the sorted list of links
|
|
// -- By treating the ready list as a queue, we maximize the distance between any
|
|
// inter-dependent node calculations
|
|
// -- All other (non-related) nodes in the ready list will automatically be inserted
|
|
// in between each set of inter-dependent link calculations by this loop
|
|
i = 0;
|
|
while (readyListHead != readyListTail)
|
|
{
|
|
// Use ready list to select the next link to process
|
|
linkNum = readyList[readyListHead++];
|
|
// Copy the next-to-calculate link back into the original link array
|
|
psb->m_links[i++] = linkBuffer[linkNum];
|
|
|
|
// Free up any link inputs that are dependent on this one
|
|
linkDep = linkDepListStarts[linkNum];
|
|
while (linkDep)
|
|
{
|
|
depLink = linkDep->value;
|
|
if (depLink >= 0)
|
|
{
|
|
linkDepA[depLink] = REOP_NOT_DEPENDENT;
|
|
}
|
|
else
|
|
{
|
|
depLink = -depLink - 1;
|
|
linkDepB[depLink] = REOP_NOT_DEPENDENT;
|
|
}
|
|
// Add this dependent link calculation to the ready list if *both* inputs are clear
|
|
if ((linkDepA[depLink] == REOP_NOT_DEPENDENT) && (linkDepB[depLink] == REOP_NOT_DEPENDENT))
|
|
{
|
|
readyList[readyListTail++] = depLink;
|
|
linkDepA[depLink] = linkDepB[depLink] = REOP_NODE_COMPLETE; // Probably not needed now
|
|
}
|
|
linkDep = linkDep->next;
|
|
}
|
|
}
|
|
|
|
// Delete the temporary buffers
|
|
delete[] nodeWrittenAt;
|
|
delete[] linkDepA;
|
|
delete[] linkDepB;
|
|
delete[] readyList;
|
|
delete[] linkDepFreeList;
|
|
delete[] linkDepListStarts;
|
|
delete[] linkBuffer;
|
|
}
|
|
|
|
//
|
|
void btSoftBodyHelpers::DrawFrame(btSoftBody* psb,
|
|
btIDebugDraw* idraw)
|
|
{
|
|
if (psb->m_pose.m_bframe)
|
|
{
|
|
static const btScalar ascl = 10;
|
|
static const btScalar nscl = (btScalar)0.1;
|
|
const btVector3 com = psb->m_pose.m_com;
|
|
const btMatrix3x3 trs = psb->m_pose.m_rot * psb->m_pose.m_scl;
|
|
const btVector3 Xaxis = (trs * btVector3(1, 0, 0)).normalized();
|
|
const btVector3 Yaxis = (trs * btVector3(0, 1, 0)).normalized();
|
|
const btVector3 Zaxis = (trs * btVector3(0, 0, 1)).normalized();
|
|
idraw->drawLine(com, com + Xaxis * ascl, btVector3(1, 0, 0));
|
|
idraw->drawLine(com, com + Yaxis * ascl, btVector3(0, 1, 0));
|
|
idraw->drawLine(com, com + Zaxis * ascl, btVector3(0, 0, 1));
|
|
for (int i = 0; i < psb->m_pose.m_pos.size(); ++i)
|
|
{
|
|
const btVector3 x = com + trs * psb->m_pose.m_pos[i];
|
|
drawVertex(idraw, x, nscl, btVector3(1, 0, 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
btSoftBody* btSoftBodyHelpers::CreateRope(btSoftBodyWorldInfo& worldInfo, const btVector3& from,
|
|
const btVector3& to,
|
|
int res,
|
|
int fixeds)
|
|
{
|
|
/* Create nodes */
|
|
const int r = res + 2;
|
|
btVector3* x = new btVector3[r];
|
|
btScalar* m = new btScalar[r];
|
|
int i;
|
|
|
|
for (i = 0; i < r; ++i)
|
|
{
|
|
const btScalar t = i / (btScalar)(r - 1);
|
|
x[i] = lerp(from, to, t);
|
|
m[i] = 1;
|
|
}
|
|
btSoftBody* psb = new btSoftBody(&worldInfo, r, x, m);
|
|
if (fixeds & 1) psb->setMass(0, 0);
|
|
if (fixeds & 2) psb->setMass(r - 1, 0);
|
|
delete[] x;
|
|
delete[] m;
|
|
/* Create links */
|
|
for (i = 1; i < r; ++i)
|
|
{
|
|
psb->appendLink(i - 1, i);
|
|
}
|
|
/* Finished */
|
|
return (psb);
|
|
}
|
|
|
|
//
|
|
btSoftBody* btSoftBodyHelpers::CreatePatch(btSoftBodyWorldInfo& worldInfo, const btVector3& corner00,
|
|
const btVector3& corner10,
|
|
const btVector3& corner01,
|
|
const btVector3& corner11,
|
|
int resx,
|
|
int resy,
|
|
int fixeds,
|
|
bool gendiags,
|
|
btScalar perturbation)
|
|
{
|
|
#define IDX(_x_, _y_) ((_y_)*rx + (_x_))
|
|
/* Create nodes */
|
|
if ((resx < 2) || (resy < 2)) return (0);
|
|
const int rx = resx;
|
|
const int ry = resy;
|
|
const int tot = rx * ry;
|
|
btVector3* x = new btVector3[tot];
|
|
btScalar* m = new btScalar[tot];
|
|
int iy;
|
|
|
|
for (iy = 0; iy < ry; ++iy)
|
|
{
|
|
const btScalar ty = iy / (btScalar)(ry - 1);
|
|
const btVector3 py0 = lerp(corner00, corner01, ty);
|
|
const btVector3 py1 = lerp(corner10, corner11, ty);
|
|
for (int ix = 0; ix < rx; ++ix)
|
|
{
|
|
const btScalar tx = ix / (btScalar)(rx - 1);
|
|
btScalar pert = perturbation * btScalar(rand())/RAND_MAX;
|
|
btVector3 temp1 = py1;
|
|
temp1.setY(py1.getY() + pert);
|
|
btVector3 temp = py0;
|
|
pert = perturbation * btScalar(rand())/RAND_MAX;
|
|
temp.setY(py0.getY() + pert);
|
|
x[IDX(ix, iy)] = lerp(temp, temp1, tx);
|
|
m[IDX(ix, iy)] = 1;
|
|
}
|
|
}
|
|
btSoftBody* psb = new btSoftBody(&worldInfo, tot, x, m);
|
|
if (fixeds & 1) psb->setMass(IDX(0, 0), 0);
|
|
if (fixeds & 2) psb->setMass(IDX(rx - 1, 0), 0);
|
|
if (fixeds & 4) psb->setMass(IDX(0, ry - 1), 0);
|
|
if (fixeds & 8) psb->setMass(IDX(rx - 1, ry - 1), 0);
|
|
delete[] x;
|
|
delete[] m;
|
|
/* Create links and faces */
|
|
for (iy = 0; iy < ry; ++iy)
|
|
{
|
|
for (int ix = 0; ix < rx; ++ix)
|
|
{
|
|
const int idx = IDX(ix, iy);
|
|
const bool mdx = (ix + 1) < rx;
|
|
const bool mdy = (iy + 1) < ry;
|
|
if (mdx) psb->appendLink(idx, IDX(ix + 1, iy));
|
|
if (mdy) psb->appendLink(idx, IDX(ix, iy + 1));
|
|
if (mdx && mdy)
|
|
{
|
|
if ((ix + iy) & 1)
|
|
{
|
|
psb->appendFace(IDX(ix, iy), IDX(ix + 1, iy), IDX(ix + 1, iy + 1));
|
|
psb->appendFace(IDX(ix, iy), IDX(ix + 1, iy + 1), IDX(ix, iy + 1));
|
|
if (gendiags)
|
|
{
|
|
psb->appendLink(IDX(ix, iy), IDX(ix + 1, iy + 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psb->appendFace(IDX(ix, iy + 1), IDX(ix, iy), IDX(ix + 1, iy));
|
|
psb->appendFace(IDX(ix, iy + 1), IDX(ix + 1, iy), IDX(ix + 1, iy + 1));
|
|
if (gendiags)
|
|
{
|
|
psb->appendLink(IDX(ix + 1, iy), IDX(ix, iy + 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Finished */
|
|
#undef IDX
|
|
return (psb);
|
|
}
|
|
|
|
//
|
|
btSoftBody* btSoftBodyHelpers::CreatePatchUV(btSoftBodyWorldInfo& worldInfo,
|
|
const btVector3& corner00,
|
|
const btVector3& corner10,
|
|
const btVector3& corner01,
|
|
const btVector3& corner11,
|
|
int resx,
|
|
int resy,
|
|
int fixeds,
|
|
bool gendiags,
|
|
float* tex_coords)
|
|
{
|
|
/*
|
|
*
|
|
* corners:
|
|
*
|
|
* [0][0] corner00 ------- corner01 [resx][0]
|
|
* | |
|
|
* | |
|
|
* [0][resy] corner10 -------- corner11 [resx][resy]
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* "fixedgs" map:
|
|
*
|
|
* corner00 --> +1
|
|
* corner01 --> +2
|
|
* corner10 --> +4
|
|
* corner11 --> +8
|
|
* upper middle --> +16
|
|
* left middle --> +32
|
|
* right middle --> +64
|
|
* lower middle --> +128
|
|
* center --> +256
|
|
*
|
|
*
|
|
* tex_coords size (resx-1)*(resy-1)*12
|
|
*
|
|
*
|
|
*
|
|
* SINGLE QUAD INTERNALS
|
|
*
|
|
* 1) btSoftBody's nodes and links,
|
|
* diagonal link is optional ("gendiags")
|
|
*
|
|
*
|
|
* node00 ------ node01
|
|
* | .
|
|
* | .
|
|
* | .
|
|
* | .
|
|
* | .
|
|
* node10 node11
|
|
*
|
|
*
|
|
*
|
|
* 2) Faces:
|
|
* two triangles,
|
|
* UV Coordinates (hier example for single quad)
|
|
*
|
|
* (0,1) (0,1) (1,1)
|
|
* 1 |\ 3 \-----| 2
|
|
* | \ \ |
|
|
* | \ \ |
|
|
* | \ \ |
|
|
* | \ \ |
|
|
* 2 |-----\ 3 \| 1
|
|
* (0,0) (1,0) (1,0)
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
#define IDX(_x_, _y_) ((_y_)*rx + (_x_))
|
|
/* Create nodes */
|
|
if ((resx < 2) || (resy < 2)) return (0);
|
|
const int rx = resx;
|
|
const int ry = resy;
|
|
const int tot = rx * ry;
|
|
btVector3* x = new btVector3[tot];
|
|
btScalar* m = new btScalar[tot];
|
|
|
|
int iy;
|
|
|
|
for (iy = 0; iy < ry; ++iy)
|
|
{
|
|
const btScalar ty = iy / (btScalar)(ry - 1);
|
|
const btVector3 py0 = lerp(corner00, corner01, ty);
|
|
const btVector3 py1 = lerp(corner10, corner11, ty);
|
|
for (int ix = 0; ix < rx; ++ix)
|
|
{
|
|
const btScalar tx = ix / (btScalar)(rx - 1);
|
|
x[IDX(ix, iy)] = lerp(py0, py1, tx);
|
|
m[IDX(ix, iy)] = 1;
|
|
}
|
|
}
|
|
btSoftBody* psb = new btSoftBody(&worldInfo, tot, x, m);
|
|
if (fixeds & 1) psb->setMass(IDX(0, 0), 0);
|
|
if (fixeds & 2) psb->setMass(IDX(rx - 1, 0), 0);
|
|
if (fixeds & 4) psb->setMass(IDX(0, ry - 1), 0);
|
|
if (fixeds & 8) psb->setMass(IDX(rx - 1, ry - 1), 0);
|
|
if (fixeds & 16) psb->setMass(IDX((rx - 1) / 2, 0), 0);
|
|
if (fixeds & 32) psb->setMass(IDX(0, (ry - 1) / 2), 0);
|
|
if (fixeds & 64) psb->setMass(IDX(rx - 1, (ry - 1) / 2), 0);
|
|
if (fixeds & 128) psb->setMass(IDX((rx - 1) / 2, ry - 1), 0);
|
|
if (fixeds & 256) psb->setMass(IDX((rx - 1) / 2, (ry - 1) / 2), 0);
|
|
delete[] x;
|
|
delete[] m;
|
|
|
|
int z = 0;
|
|
/* Create links and faces */
|
|
for (iy = 0; iy < ry; ++iy)
|
|
{
|
|
for (int ix = 0; ix < rx; ++ix)
|
|
{
|
|
const bool mdx = (ix + 1) < rx;
|
|
const bool mdy = (iy + 1) < ry;
|
|
|
|
int node00 = IDX(ix, iy);
|
|
int node01 = IDX(ix + 1, iy);
|
|
int node10 = IDX(ix, iy + 1);
|
|
int node11 = IDX(ix + 1, iy + 1);
|
|
|
|
if (mdx) psb->appendLink(node00, node01);
|
|
if (mdy) psb->appendLink(node00, node10);
|
|
if (mdx && mdy)
|
|
{
|
|
psb->appendFace(node00, node10, node11);
|
|
if (tex_coords)
|
|
{
|
|
tex_coords[z + 0] = CalculateUV(resx, resy, ix, iy, 0);
|
|
tex_coords[z + 1] = CalculateUV(resx, resy, ix, iy, 1);
|
|
tex_coords[z + 2] = CalculateUV(resx, resy, ix, iy, 0);
|
|
tex_coords[z + 3] = CalculateUV(resx, resy, ix, iy, 2);
|
|
tex_coords[z + 4] = CalculateUV(resx, resy, ix, iy, 3);
|
|
tex_coords[z + 5] = CalculateUV(resx, resy, ix, iy, 2);
|
|
}
|
|
psb->appendFace(node11, node01, node00);
|
|
if (tex_coords)
|
|
{
|
|
tex_coords[z + 6] = CalculateUV(resx, resy, ix, iy, 3);
|
|
tex_coords[z + 7] = CalculateUV(resx, resy, ix, iy, 2);
|
|
tex_coords[z + 8] = CalculateUV(resx, resy, ix, iy, 3);
|
|
tex_coords[z + 9] = CalculateUV(resx, resy, ix, iy, 1);
|
|
tex_coords[z + 10] = CalculateUV(resx, resy, ix, iy, 0);
|
|
tex_coords[z + 11] = CalculateUV(resx, resy, ix, iy, 1);
|
|
}
|
|
if (gendiags) psb->appendLink(node00, node11);
|
|
z += 12;
|
|
}
|
|
}
|
|
}
|
|
/* Finished */
|
|
#undef IDX
|
|
return (psb);
|
|
}
|
|
|
|
float btSoftBodyHelpers::CalculateUV(int resx, int resy, int ix, int iy, int id)
|
|
{
|
|
/*
|
|
*
|
|
*
|
|
* node00 --- node01
|
|
* | |
|
|
* node10 --- node11
|
|
*
|
|
*
|
|
* ID map:
|
|
*
|
|
* node00 s --> 0
|
|
* node00 t --> 1
|
|
*
|
|
* node01 s --> 3
|
|
* node01 t --> 1
|
|
*
|
|
* node10 s --> 0
|
|
* node10 t --> 2
|
|
*
|
|
* node11 s --> 3
|
|
* node11 t --> 2
|
|
*
|
|
*
|
|
*/
|
|
|
|
float tc = 0.0f;
|
|
if (id == 0)
|
|
{
|
|
tc = (1.0f / ((resx - 1)) * ix);
|
|
}
|
|
else if (id == 1)
|
|
{
|
|
tc = (1.0f / ((resy - 1)) * (resy - 1 - iy));
|
|
}
|
|
else if (id == 2)
|
|
{
|
|
tc = (1.0f / ((resy - 1)) * (resy - 1 - iy - 1));
|
|
}
|
|
else if (id == 3)
|
|
{
|
|
tc = (1.0f / ((resx - 1)) * (ix + 1));
|
|
}
|
|
return tc;
|
|
}
|
|
//
|
|
btSoftBody* btSoftBodyHelpers::CreateEllipsoid(btSoftBodyWorldInfo& worldInfo, const btVector3& center,
|
|
const btVector3& radius,
|
|
int res)
|
|
{
|
|
struct Hammersley
|
|
{
|
|
static void Generate(btVector3* x, int n)
|
|
{
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
btScalar p = 0.5, t = 0;
|
|
for (int j = i; j; p *= 0.5, j >>= 1)
|
|
if (j & 1) t += p;
|
|
btScalar w = 2 * t - 1;
|
|
btScalar a = (SIMD_PI + 2 * i * SIMD_PI) / n;
|
|
btScalar s = btSqrt(1 - w * w);
|
|
*x++ = btVector3(s * btCos(a), s * btSin(a), w);
|
|
}
|
|
}
|
|
};
|
|
btAlignedObjectArray<btVector3> vtx;
|
|
vtx.resize(3 + res);
|
|
Hammersley::Generate(&vtx[0], vtx.size());
|
|
for (int i = 0; i < vtx.size(); ++i)
|
|
{
|
|
vtx[i] = vtx[i] * radius + center;
|
|
}
|
|
return (CreateFromConvexHull(worldInfo, &vtx[0], vtx.size()));
|
|
}
|
|
|
|
//
|
|
btSoftBody* btSoftBodyHelpers::CreateFromTriMesh(btSoftBodyWorldInfo& worldInfo, const btScalar* vertices,
|
|
const int* triangles,
|
|
int ntriangles, bool randomizeConstraints)
|
|
{
|
|
int maxidx = 0;
|
|
int i, j, ni;
|
|
|
|
for (i = 0, ni = ntriangles * 3; i < ni; ++i)
|
|
{
|
|
maxidx = btMax(triangles[i], maxidx);
|
|
}
|
|
++maxidx;
|
|
btAlignedObjectArray<bool> chks;
|
|
btAlignedObjectArray<btVector3> vtx;
|
|
chks.resize(maxidx * maxidx, false);
|
|
vtx.resize(maxidx);
|
|
for (i = 0, j = 0, ni = maxidx * 3; i < ni; ++j, i += 3)
|
|
{
|
|
vtx[j] = btVector3(vertices[i], vertices[i + 1], vertices[i + 2]);
|
|
}
|
|
btSoftBody* psb = new btSoftBody(&worldInfo, vtx.size(), &vtx[0], 0);
|
|
for (i = 0, ni = ntriangles * 3; i < ni; i += 3)
|
|
{
|
|
const int idx[] = {triangles[i], triangles[i + 1], triangles[i + 2]};
|
|
#define IDX(_x_, _y_) ((_y_)*maxidx + (_x_))
|
|
for (int j = 2, k = 0; k < 3; j = k++)
|
|
{
|
|
if (!chks[IDX(idx[j], idx[k])])
|
|
{
|
|
chks[IDX(idx[j], idx[k])] = true;
|
|
chks[IDX(idx[k], idx[j])] = true;
|
|
psb->appendLink(idx[j], idx[k]);
|
|
}
|
|
}
|
|
#undef IDX
|
|
psb->appendFace(idx[0], idx[1], idx[2]);
|
|
}
|
|
|
|
if (randomizeConstraints)
|
|
{
|
|
psb->randomizeConstraints();
|
|
}
|
|
|
|
return (psb);
|
|
}
|
|
|
|
//
|
|
btSoftBody* btSoftBodyHelpers::CreateFromConvexHull(btSoftBodyWorldInfo& worldInfo, const btVector3* vertices,
|
|
int nvertices, bool randomizeConstraints)
|
|
{
|
|
HullDesc hdsc(QF_TRIANGLES, nvertices, vertices);
|
|
HullResult hres;
|
|
HullLibrary hlib; /*??*/
|
|
hdsc.mMaxVertices = nvertices;
|
|
hlib.CreateConvexHull(hdsc, hres);
|
|
btSoftBody* psb = new btSoftBody(&worldInfo, (int)hres.mNumOutputVertices,
|
|
&hres.m_OutputVertices[0], 0);
|
|
for (int i = 0; i < (int)hres.mNumFaces; ++i)
|
|
{
|
|
const int idx[] = {static_cast<int>(hres.m_Indices[i * 3 + 0]),
|
|
static_cast<int>(hres.m_Indices[i * 3 + 1]),
|
|
static_cast<int>(hres.m_Indices[i * 3 + 2])};
|
|
if (idx[0] < idx[1]) psb->appendLink(idx[0], idx[1]);
|
|
if (idx[1] < idx[2]) psb->appendLink(idx[1], idx[2]);
|
|
if (idx[2] < idx[0]) psb->appendLink(idx[2], idx[0]);
|
|
psb->appendFace(idx[0], idx[1], idx[2]);
|
|
}
|
|
hlib.ReleaseResult(hres);
|
|
if (randomizeConstraints)
|
|
{
|
|
psb->randomizeConstraints();
|
|
}
|
|
return (psb);
|
|
}
|
|
|
|
static int nextLine(const char* buffer)
|
|
{
|
|
int numBytesRead = 0;
|
|
|
|
while (*buffer != '\n')
|
|
{
|
|
buffer++;
|
|
numBytesRead++;
|
|
}
|
|
|
|
if (buffer[0] == 0x0a)
|
|
{
|
|
buffer++;
|
|
numBytesRead++;
|
|
}
|
|
return numBytesRead;
|
|
}
|
|
|
|
/* Create from TetGen .ele, .face, .node data */
|
|
btSoftBody* btSoftBodyHelpers::CreateFromTetGenData(btSoftBodyWorldInfo& worldInfo,
|
|
const char* ele,
|
|
const char* face,
|
|
const char* node,
|
|
bool bfacelinks,
|
|
bool btetralinks,
|
|
bool bfacesfromtetras)
|
|
{
|
|
btAlignedObjectArray<btVector3> pos;
|
|
int nnode = 0;
|
|
int ndims = 0;
|
|
int nattrb = 0;
|
|
int hasbounds = 0;
|
|
int result = sscanf(node, "%d %d %d %d", &nnode, &ndims, &nattrb, &hasbounds);
|
|
result = sscanf(node, "%d %d %d %d", &nnode, &ndims, &nattrb, &hasbounds);
|
|
node += nextLine(node);
|
|
|
|
pos.resize(nnode);
|
|
for (int i = 0; i < pos.size(); ++i)
|
|
{
|
|
int index = 0;
|
|
//int bound=0;
|
|
float x, y, z;
|
|
sscanf(node, "%d %f %f %f", &index, &x, &y, &z);
|
|
|
|
// sn>>index;
|
|
// sn>>x;sn>>y;sn>>z;
|
|
node += nextLine(node);
|
|
|
|
//for(int j=0;j<nattrb;++j)
|
|
// sn>>a;
|
|
|
|
//if(hasbounds)
|
|
// sn>>bound;
|
|
|
|
pos[index].setX(btScalar(x));
|
|
pos[index].setY(btScalar(y));
|
|
pos[index].setZ(btScalar(z));
|
|
}
|
|
btSoftBody* psb = new btSoftBody(&worldInfo, nnode, &pos[0], 0);
|
|
#if 0
|
|
if(face&&face[0])
|
|
{
|
|
int nface=0;
|
|
sf>>nface;sf>>hasbounds;
|
|
for(int i=0;i<nface;++i)
|
|
{
|
|
int index=0;
|
|
int bound=0;
|
|
int ni[3];
|
|
sf>>index;
|
|
sf>>ni[0];sf>>ni[1];sf>>ni[2];
|
|
sf>>bound;
|
|
psb->appendFace(ni[0],ni[1],ni[2]);
|
|
if(btetralinks)
|
|
{
|
|
psb->appendLink(ni[0],ni[1],0,true);
|
|
psb->appendLink(ni[1],ni[2],0,true);
|
|
psb->appendLink(ni[2],ni[0],0,true);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (ele && ele[0])
|
|
{
|
|
int ntetra = 0;
|
|
int ncorner = 0;
|
|
int neattrb = 0;
|
|
sscanf(ele, "%d %d %d", &ntetra, &ncorner, &neattrb);
|
|
ele += nextLine(ele);
|
|
|
|
//se>>ntetra;se>>ncorner;se>>neattrb;
|
|
for (int i = 0; i < ntetra; ++i)
|
|
{
|
|
int index = 0;
|
|
int ni[4];
|
|
|
|
//se>>index;
|
|
//se>>ni[0];se>>ni[1];se>>ni[2];se>>ni[3];
|
|
sscanf(ele, "%d %d %d %d %d", &index, &ni[0], &ni[1], &ni[2], &ni[3]);
|
|
ele += nextLine(ele);
|
|
//for(int j=0;j<neattrb;++j)
|
|
// se>>a;
|
|
psb->appendTetra(ni[0], ni[1], ni[2], ni[3]);
|
|
if (btetralinks)
|
|
{
|
|
psb->appendLink(ni[0], ni[1], 0, true);
|
|
psb->appendLink(ni[1], ni[2], 0, true);
|
|
psb->appendLink(ni[2], ni[0], 0, true);
|
|
psb->appendLink(ni[0], ni[3], 0, true);
|
|
psb->appendLink(ni[1], ni[3], 0, true);
|
|
psb->appendLink(ni[2], ni[3], 0, true);
|
|
}
|
|
}
|
|
}
|
|
psb->initializeDmInverse();
|
|
psb->m_tetraScratches.resize(psb->m_tetras.size());
|
|
psb->m_tetraScratchesTn.resize(psb->m_tetras.size());
|
|
printf("Nodes: %u\r\n", psb->m_nodes.size());
|
|
printf("Links: %u\r\n", psb->m_links.size());
|
|
printf("Faces: %u\r\n", psb->m_faces.size());
|
|
printf("Tetras: %u\r\n", psb->m_tetras.size());
|
|
return (psb);
|
|
}
|
|
|
|
btSoftBody* btSoftBodyHelpers::CreateFromVtkFile(btSoftBodyWorldInfo& worldInfo, const char* vtk_file)
|
|
{
|
|
std::ifstream fs;
|
|
fs.open(vtk_file);
|
|
btAssert(fs);
|
|
|
|
typedef btAlignedObjectArray<int> Index;
|
|
std::string line;
|
|
btAlignedObjectArray<btVector3> X;
|
|
btVector3 position;
|
|
btAlignedObjectArray<Index> indices;
|
|
bool reading_points = false;
|
|
bool reading_tets = false;
|
|
size_t n_points = 0;
|
|
size_t n_tets = 0;
|
|
size_t x_count = 0;
|
|
size_t indices_count = 0;
|
|
while (std::getline(fs, line))
|
|
{
|
|
std::stringstream ss(line);
|
|
if (line.size() == (size_t)(0))
|
|
{
|
|
}
|
|
else if (line.substr(0, 6) == "POINTS")
|
|
{
|
|
reading_points = true;
|
|
reading_tets = false;
|
|
ss.ignore(128, ' '); // ignore "POINTS"
|
|
ss >> n_points;
|
|
X.resize(n_points);
|
|
}
|
|
else if (line.substr(0, 5) == "CELLS")
|
|
{
|
|
reading_points = false;
|
|
reading_tets = true;
|
|
ss.ignore(128, ' '); // ignore "CELLS"
|
|
ss >> n_tets;
|
|
indices.resize(n_tets);
|
|
}
|
|
else if (line.substr(0, 10) == "CELL_TYPES")
|
|
{
|
|
reading_points = false;
|
|
reading_tets = false;
|
|
}
|
|
else if (reading_points)
|
|
{
|
|
btScalar p;
|
|
ss >> p;
|
|
position.setX(p);
|
|
ss >> p;
|
|
position.setY(p);
|
|
ss >> p;
|
|
position.setZ(p);
|
|
X[x_count++] = position;
|
|
}
|
|
else if (reading_tets)
|
|
{
|
|
ss.ignore(128, ' '); // ignore "4"
|
|
Index tet;
|
|
tet.resize(4);
|
|
for (size_t i = 0; i < 4; i++)
|
|
{
|
|
ss >> tet[i];
|
|
}
|
|
indices[indices_count++] = tet;
|
|
}
|
|
}
|
|
btSoftBody* psb = new btSoftBody(&worldInfo, n_points, &X[0], 0);
|
|
|
|
for (int i = 0; i < n_tets; ++i)
|
|
{
|
|
const Index& ni = indices[i];
|
|
psb->appendTetra(ni[0], ni[1], ni[2], ni[3]);
|
|
{
|
|
psb->appendLink(ni[0], ni[1], 0, true);
|
|
psb->appendLink(ni[1], ni[2], 0, true);
|
|
psb->appendLink(ni[2], ni[0], 0, true);
|
|
psb->appendLink(ni[0], ni[3], 0, true);
|
|
psb->appendLink(ni[1], ni[3], 0, true);
|
|
psb->appendLink(ni[2], ni[3], 0, true);
|
|
}
|
|
}
|
|
|
|
|
|
generateBoundaryFaces(psb);
|
|
psb->initializeDmInverse();
|
|
psb->m_tetraScratches.resize(psb->m_tetras.size());
|
|
psb->m_tetraScratchesTn.resize(psb->m_tetras.size());
|
|
printf("Nodes: %u\r\n", psb->m_nodes.size());
|
|
printf("Links: %u\r\n", psb->m_links.size());
|
|
printf("Faces: %u\r\n", psb->m_faces.size());
|
|
printf("Tetras: %u\r\n", psb->m_tetras.size());
|
|
|
|
fs.close();
|
|
return psb;
|
|
}
|
|
|
|
void btSoftBodyHelpers::generateBoundaryFaces(btSoftBody* psb)
|
|
{
|
|
int counter = 0;
|
|
for (int i = 0; i < psb->m_nodes.size(); ++i)
|
|
{
|
|
psb->m_nodes[i].index = counter++;
|
|
}
|
|
typedef btAlignedObjectArray<int> Index;
|
|
btAlignedObjectArray<Index> indices;
|
|
indices.resize(psb->m_tetras.size());
|
|
for (int i = 0; i < indices.size(); ++i)
|
|
{
|
|
Index index;
|
|
index.push_back(psb->m_tetras[i].m_n[0]->index);
|
|
index.push_back(psb->m_tetras[i].m_n[1]->index);
|
|
index.push_back(psb->m_tetras[i].m_n[2]->index);
|
|
index.push_back(psb->m_tetras[i].m_n[3]->index);
|
|
indices[i] = index;
|
|
}
|
|
|
|
std::map<std::vector<int>, std::vector<int> > dict;
|
|
for (int i = 0; i < indices.size(); ++i)
|
|
{
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
std::vector<int> f;
|
|
if (j == 0)
|
|
{
|
|
f.push_back(indices[i][1]);
|
|
f.push_back(indices[i][0]);
|
|
f.push_back(indices[i][2]);
|
|
}
|
|
if (j == 1)
|
|
{
|
|
f.push_back(indices[i][3]);
|
|
f.push_back(indices[i][0]);
|
|
f.push_back(indices[i][1]);
|
|
}
|
|
if (j == 2)
|
|
{
|
|
f.push_back(indices[i][3]);
|
|
f.push_back(indices[i][1]);
|
|
f.push_back(indices[i][2]);
|
|
}
|
|
if (j == 3)
|
|
{
|
|
f.push_back(indices[i][2]);
|
|
f.push_back(indices[i][0]);
|
|
f.push_back(indices[i][3]);
|
|
}
|
|
std::vector<int> f_sorted = f;
|
|
std::sort(f_sorted.begin(), f_sorted.end());
|
|
if (dict.find(f_sorted) != dict.end())
|
|
{
|
|
dict.erase(f_sorted);
|
|
}
|
|
else
|
|
{
|
|
dict.insert(std::make_pair(f_sorted, f));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (std::map<std::vector<int>, std::vector<int> >::iterator it = dict.begin(); it != dict.end(); ++it)
|
|
{
|
|
std::vector<int> f = it->second;
|
|
psb->appendFace(f[0], f[1], f[2]);
|
|
}
|
|
}
|
|
|
|
void btSoftBodyHelpers::writeObj(const char* filename, const btSoftBody* psb)
|
|
{
|
|
std::ofstream fs;
|
|
fs.open(filename);
|
|
btAssert(fs);
|
|
for (int i = 0; i < psb->m_nodes.size(); ++i)
|
|
{
|
|
fs << "v";
|
|
for (int d = 0; d < 3; d++)
|
|
{
|
|
fs << " " << psb->m_nodes[i].m_x[d];
|
|
}
|
|
fs << "\n";
|
|
}
|
|
|
|
for (int i = 0; i < psb->m_faces.size(); ++i)
|
|
{
|
|
fs << "f";
|
|
for (int n = 0; n < 3; n++)
|
|
{
|
|
fs << " " << psb->m_faces[i].m_n[n]->index + 1;
|
|
}
|
|
fs << "\n";
|
|
}
|
|
fs.close();
|
|
}
|
|
|
|
void btSoftBodyHelpers::duplicateFaces(const char* filename, const btSoftBody* psb)
|
|
{
|
|
std::ifstream fs_read;
|
|
fs_read.open(filename);
|
|
std::string line;
|
|
btVector3 pos;
|
|
btAlignedObjectArray<btAlignedObjectArray<int> > additional_faces;
|
|
while (std::getline(fs_read, line))
|
|
{
|
|
std::stringstream ss(line);
|
|
if (line[0] == 'v')
|
|
{
|
|
}
|
|
else if (line[0] == 'f')
|
|
{
|
|
ss.ignore();
|
|
int id0, id1, id2;
|
|
ss >> id0;
|
|
ss >> id1;
|
|
ss >> id2;
|
|
btAlignedObjectArray<int> new_face;
|
|
new_face.push_back(id1);
|
|
new_face.push_back(id0);
|
|
new_face.push_back(id2);
|
|
additional_faces.push_back(new_face);
|
|
}
|
|
}
|
|
fs_read.close();
|
|
|
|
std::ofstream fs_write;
|
|
fs_write.open(filename, std::ios_base::app);
|
|
for (int i = 0; i < additional_faces.size(); ++i)
|
|
{
|
|
fs_write << "f";
|
|
for (int n = 0; n < 3; n++)
|
|
{
|
|
fs_write << " " << additional_faces[i][n];
|
|
}
|
|
fs_write << "\n";
|
|
}
|
|
fs_write.close();
|
|
}
|
|
|
|
// Given a simplex with vertices a,b,c,d, find the barycentric weights of p in this simplex
|
|
void btSoftBodyHelpers::getBarycentricWeights(const btVector3& a, const btVector3& b, const btVector3& c, const btVector3& d, const btVector3& p, btVector4& bary)
|
|
{
|
|
btVector3 vap = p - a;
|
|
btVector3 vbp = p - b;
|
|
|
|
btVector3 vab = b - a;
|
|
btVector3 vac = c - a;
|
|
btVector3 vad = d - a;
|
|
|
|
btVector3 vbc = c - b;
|
|
btVector3 vbd = d - b;
|
|
btScalar va6 = (vbp.cross(vbd)).dot(vbc);
|
|
btScalar vb6 = (vap.cross(vac)).dot(vad);
|
|
btScalar vc6 = (vap.cross(vad)).dot(vab);
|
|
btScalar vd6 = (vap.cross(vab)).dot(vac);
|
|
btScalar v6 = btScalar(1) / (vab.cross(vac).dot(vad));
|
|
bary = btVector4(va6*v6, vb6*v6, vc6*v6, vd6*v6);
|
|
}
|
|
|
|
// Iterate through all render nodes to find the simulation tetrahedron that contains the render node and record the barycentric weights
|
|
// If the node is not inside any tetrahedron, assign it to the tetrahedron in which the node has the least negative barycentric weight
|
|
void btSoftBodyHelpers::interpolateBarycentricWeights(btSoftBody* psb)
|
|
{
|
|
psb->m_renderNodesInterpolationWeights.resize(psb->m_renderNodes.size());
|
|
psb->m_renderNodesParents.resize(psb->m_renderNodes.size());
|
|
for (int i = 0; i < psb->m_renderNodes.size(); ++i)
|
|
{
|
|
const btVector3& p = psb->m_renderNodes[i].m_x;
|
|
btVector4 bary;
|
|
btVector4 optimal_bary;
|
|
btScalar min_bary_weight = -1e3;
|
|
btAlignedObjectArray<const btSoftBody::Node*> optimal_parents;
|
|
bool found = false;
|
|
for (int j = 0; j < psb->m_tetras.size(); ++j)
|
|
{
|
|
const btSoftBody::Tetra& t = psb->m_tetras[j];
|
|
getBarycentricWeights(t.m_n[0]->m_x, t.m_n[1]->m_x, t.m_n[2]->m_x, t.m_n[3]->m_x, p, bary);
|
|
btScalar new_min_bary_weight = bary[0];
|
|
for (int k = 1; k < 4; ++k)
|
|
{
|
|
new_min_bary_weight = btMin(new_min_bary_weight, bary[k]);
|
|
}
|
|
if (new_min_bary_weight > min_bary_weight)
|
|
{
|
|
btAlignedObjectArray<const btSoftBody::Node*> parents;
|
|
parents.push_back(t.m_n[0]);
|
|
parents.push_back(t.m_n[1]);
|
|
parents.push_back(t.m_n[2]);
|
|
parents.push_back(t.m_n[3]);
|
|
optimal_parents = parents;
|
|
optimal_bary = bary;
|
|
min_bary_weight = new_min_bary_weight;
|
|
// stop searching if p is inside the tetrahedron at hand
|
|
if (bary[0]>=0. && bary[1]>=0. && bary[2]>=0. && bary[3]>=0.)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
psb->m_renderNodesInterpolationWeights[i] = optimal_bary;
|
|
psb->m_renderNodesParents[i] = optimal_parents;
|
|
}
|
|
}
|