open_toontown_panda3d/panda/src/physx/physxSoftBodyNode.cxx

415 lines
12 KiB
C++

// Filename: physxSoftBodyNode.cxx
// Created by: enn0x (13Sep10)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "physxSoftBodyNode.h"
#include "physxSoftBody.h"
#include "physxFileStream.h"
#include "physxManager.h"
#include "physxMeshHash.h"
#include "geomVertexFormat.h"
#include "geomVertexWriter.h"
#include "geomVertexRewriter.h"
TypeHandle PhysxSoftBodyNode::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::allocate
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void PhysxSoftBodyNode::
allocate(PhysxSoftBody *softbody) {
_softbody = softbody;
// Retrieve number of vertices and triangles the hard way
NxSoftBodyMeshDesc meshDesc;
_softbody->ptr()->getSoftBodyMesh()->saveToDesc(meshDesc);
NxU32 numVertices = meshDesc.numVertices;
NxU32 numTetrahedra = meshDesc.numTetrahedra;
float factor = 1.0f; // TODO max(1.0f, factor);
// Reserve more memory for vertices than the initial mesh takes because
// tearing creates new vertices
NxU32 maxVertices = factor * numVertices;
_mesh.verticesPosBegin = (NxVec3 *)malloc(sizeof(NxVec3) * maxVertices);
_mesh.verticesPosByteStride = sizeof(NxVec3);
_mesh.maxVertices = maxVertices;
_mesh.numVerticesPtr = (NxU32 *)malloc(sizeof(NxU32));
// The number of tetrahedra is constant, even if the softbody is torn
NxU32 maxIndices = 4 * numTetrahedra;
_mesh.indicesBegin = (NxU32 *)malloc(sizeof(NxU32) * maxIndices);
_mesh.indicesByteStride = sizeof(NxU32);
_mesh.maxIndices = maxIndices;
_mesh.numIndicesPtr = (NxU32 *)malloc(sizeof(NxU32));
*(_mesh.numVerticesPtr) = 0;
*(_mesh.numIndicesPtr) = 0;
_softbody->ptr()->setMeshData(_mesh);
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::set_from_geom
// Access: Published
// Description: Reads the vertices and indices from an existing
// Geom and makes a decomposed copy of the data.
// Then computes links between the owning soft body
// tetrahedron mesh in order to render an updated
// geometry every simulation frame.
////////////////////////////////////////////////////////////////////
void PhysxSoftBodyNode::
set_from_geom(const Geom *geom) {
_prim->clear_vertices();
GeomVertexWriter vwriter = GeomVertexWriter(_vdata, InternalName::get_vertex());
GeomVertexWriter nwriter = GeomVertexWriter(_vdata, InternalName::get_normal());
GeomVertexWriter twriter = GeomVertexWriter(_vdata, InternalName::get_texcoord());
CPT(GeomVertexData) vdata = geom->get_vertex_data();
GeomVertexReader vreader = GeomVertexReader(vdata, InternalName::get_vertex());
GeomVertexReader nreader = GeomVertexReader(vdata, InternalName::get_normal());
GeomVertexReader treader = GeomVertexReader(vdata, InternalName::get_texcoord());
while (!vreader.is_at_end()) {
LVecBase3f v = vreader.get_data3f();
vwriter.add_data3f(v.get_x(), v.get_y(), v.get_z());
}
while (!nreader.is_at_end()) {
LVecBase3f n = nreader.get_data3f();
nwriter.add_data3f(n.get_x(), n.get_y(), n.get_z());
}
while (!treader.is_at_end()) {
LVecBase2f t = treader.get_data2f();
twriter.add_data2f(t.get_x(), t.get_y());
}
for (int i=0; i<geom->get_num_primitives(); i++) {
CPT(GeomPrimitive) prim = geom->get_primitive(i);
prim = prim->decompose();
for (int j=0; j<prim->get_num_primitives(); j++) {
int s = prim->get_primitive_start(j);
int e = prim->get_primitive_end(j);
for (int l=s; l<e; l++) {
_prim->add_vertex(prim->get_vertex(l));
}
}
}
_prim->close_primitive();
update_bounds();
build_tetra_links();
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::build_tetra_links
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void PhysxSoftBodyNode::
build_tetra_links() {
NxSoftBodyMeshDesc meshDesc;
_softbody->ptr()->getSoftBodyMesh()->saveToDesc(meshDesc);
const NxVec3 *vertices = (const NxVec3 *) meshDesc.vertices;
const NxU32 *indices = (const NxU32 *) meshDesc.tetrahedra;
const NxU32 numTets = meshDesc.numTetrahedra;
_tetraLinks.clear();
PhysxMeshHash* hash = new PhysxMeshHash();
hash->set_grid_spacing(_bounds.min.distance(_bounds.max) * 0.1f);
for (NxU32 i=0; i<numTets; i++) {
const NxU32 *ix = &indices[4*i];
NxBounds3 tetraBounds;
tetraBounds.setEmpty();
tetraBounds.include(vertices[*ix++]);
tetraBounds.include(vertices[*ix++]);
tetraBounds.include(vertices[*ix++]);
tetraBounds.include(vertices[*ix++]);
hash->add(tetraBounds, i);
}
GeomVertexReader vreader = GeomVertexReader(_vdata, InternalName::get_vertex());
while (!vreader.is_at_end()) {
// Prepare datastructure for drained tetras
_drainedTriVertices.push_back(false);
TetraLink tmpLink;
LVecBase3f v = vreader.get_data3f();
NxVec3 triVert = PhysxManager::vec3_to_nxVec3(v);
pvector<int> itemIndices;
hash->query_unique(triVert, itemIndices);
NxReal minDist = 0.0f;
NxVec3 b;
int num, isize;
num = isize = itemIndices.size();
if (num == 0) {
num = numTets;
}
for (int i=0; i<num; i++) {
int j = i;
if (isize > 0) {
j = itemIndices[i];
}
const NxU32 *ix = &indices[j*4];
const NxVec3 &p0 = vertices[*ix++];
const NxVec3 &p1 = vertices[*ix++];
const NxVec3 &p2 = vertices[*ix++];
const NxVec3 &p3 = vertices[*ix++];
NxVec3 b = compute_bary_coords(triVert, p0, p1, p2, p3);
// Is the vertex inside the tetrahedron? If yes we take it
if (b.x >= 0.0f && b.y >= 0.0f && b.z >= 0.0f && (b.x + b.y + b.z) <= 1.0f) {
tmpLink.barycentricCoords = b;
tmpLink.tetraNr = j;
break;
}
// Otherwise, if we are not in any tetrahedron we take the closest one
NxReal dist = 0.0f;
if (b.x + b.y + b.z > 1.0f) dist = b.x + b.y + b.z - 1.0f;
if (b.x < 0.0f) dist = (-b.x < dist) ? dist : -b.x;
if (b.y < 0.0f) dist = (-b.y < dist) ? dist : -b.y;
if (b.z < 0.0f) dist = (-b.z < dist) ? dist : -b.z;
if (i == 0 || dist < minDist) {
minDist = dist;
tmpLink.barycentricCoords = b;
tmpLink.tetraNr = j;
}
}
_tetraLinks.push_back(tmpLink);
}
delete hash;
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::remove_tris_related_to_vertex
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void PhysxSoftBodyNode::
remove_tris_related_to_vertex(const int vertexIndex) {
GeomVertexRewriter vrewriter = GeomVertexRewriter(_vdata, InternalName::get_vertex());
LVecBase3f v;
for (int j=0; j<_prim->get_num_primitives(); j++) {
int s = _prim->get_primitive_start(j);
int idx0 = _prim->get_vertex(s);
int idx1 = _prim->get_vertex(s+1);
int idx2 = _prim->get_vertex(s+2);
if (vertexIndex == idx0 || vertexIndex == idx1 || vertexIndex == idx2) {
// Make this triangle degenerated
vrewriter.set_row_unsafe(idx0); v = vrewriter.get_data3f();
vrewriter.set_row_unsafe(idx1); vrewriter.set_data3f(v.get_x(), v.get_y(), v.get_z());
vrewriter.set_row_unsafe(idx2); vrewriter.set_data3f(v.get_x(), v.get_y(), v.get_z());
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::update_bounds
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void PhysxSoftBodyNode::
update_bounds() {
_bounds.setEmpty();
GeomVertexReader vreader = GeomVertexReader(_vdata, InternalName::get_vertex());
while (!vreader.is_at_end()) {
LVecBase3f v = vreader.get_data3f();
_bounds.include(PhysxManager::vec3_to_nxVec3(v));
}
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::update_normals
// Access: Public
// Description:_bounds.include(mVertices[i]);
////////////////////////////////////////////////////////////////////
void PhysxSoftBodyNode::
update_normals() {
_normals.resize(_vdata->get_num_rows());
int i;
for (i=0; i<(int)_normals.size(); i++) {
_normals[i] = LVector3f::zero();
}
LVecBase3f n, v0, v1, v2;
GeomVertexReader vreader = GeomVertexReader(_vdata, InternalName::get_vertex());
for (int j=0; j<_prim->get_num_primitives(); j++) {
int s = _prim->get_primitive_start(j);
int idx0 = _prim->get_vertex(s);
int idx1 = _prim->get_vertex(s+1);
int idx2 = _prim->get_vertex(s+2);
vreader.set_row_unsafe(idx0); v0 = vreader.get_data3f();
vreader.set_row_unsafe(idx1); v1 = vreader.get_data3f();
vreader.set_row_unsafe(idx2); v2 = vreader.get_data3f();
n = (v1 - v0).cross(v2 - v0);
_normals[idx0] += n;
_normals[idx1] += n;
_normals[idx2] += n;
}
for (i=0; i<(int)_normals.size(); i++) {
_normals[i].normalize();
}
GeomVertexWriter nwriter = GeomVertexWriter(_vdata, InternalName::get_normal());
for (i=0; i<(int)_normals.size(); i++) {
n = _normals[i];
nwriter.add_data3f(n.get_x(), n.get_y(), n.get_z());
}
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::compute_bary_coords
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
NxVec3 PhysxSoftBodyNode::
compute_bary_coords(NxVec3 vertex, NxVec3 p0, NxVec3 p1, NxVec3 p2, NxVec3 p3) const {
NxVec3 baryCoords;
NxVec3 q = vertex - p3;
NxVec3 q0 = p0 - p3;
NxVec3 q1 = p1 - p3;
NxVec3 q2 = p2 - p3;
NxMat33 m;
m.setColumn(0, q0);
m.setColumn(1, q1);
m.setColumn(2, q2);
NxReal det = m.determinant();
m.setColumn(0, q);
baryCoords.x = m.determinant();
m.setColumn(0, q0);
m.setColumn(1, q);
baryCoords.y = m.determinant();
m.setColumn(1, q1);
m.setColumn(2,q);
baryCoords.z = m.determinant();
if (det != 0.0f) {
baryCoords /= det;
}
return baryCoords;
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::update
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void PhysxSoftBodyNode::
update() {
update_tetra_links();
}
////////////////////////////////////////////////////////////////////
// Function: PhysxSoftBodyNode::update_tetra_links
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
bool PhysxSoftBodyNode::
update_tetra_links() {
if (_tetraLinks.size() != _vdata->get_num_rows())
{
return false;
}
NxU32 numVertices = *_mesh.numVerticesPtr;
NxU32 numTetrahedra = *_mesh.numIndicesPtr / 4;
const NxVec3 *vertices = (NxVec3*)_mesh.verticesPosBegin;
NxU32* indices = (NxU32*)_mesh.indicesBegin;
GeomVertexRewriter vwriter = GeomVertexRewriter(_vdata, InternalName::get_vertex());
int i = 0;
while (!vwriter.is_at_end()) {
TetraLink &link = _tetraLinks[i];
if (!_drainedTriVertices[i]) {
const NxU32 *ix = &indices[4*link.tetraNr];
if (*ix == *(ix + 1)) {
remove_tris_related_to_vertex(i);
_drainedTriVertices[i] = true;
continue;
}
const NxVec3 &p0 = vertices[*ix++];
const NxVec3 &p1 = vertices[*ix++];
const NxVec3 &p2 = vertices[*ix++];
const NxVec3 &p3 = vertices[*ix++];
NxVec3 &b = link.barycentricCoords;
NxVec3 tmp = p0 * b.x + p1 * b.y + p2 * b.z + p3 * (1.0f - b.x - b.y - b.z);
vwriter.set_data3f(tmp.x, tmp.y, tmp.z);
}
i++;
}
update_normals();
return true;
}