Program Listing for File TetModel.cpp

Return to documentation for file (Simulation/TetModel.cpp)

#include "TetModel.h"
#include "PositionBasedDynamics/PositionBasedDynamics.h"
#include <iostream>
#include "Utils/Logger.h"

using namespace PBD;
using namespace Utilities;

TetModel::TetModel() :
    m_surfaceMesh(),
    m_visVertices(),
    m_visMesh(),
    m_particleMesh()
{
    m_initialX.setZero();
    m_initialR.setIdentity();
    m_initialScale.setOnes();
    m_restitutionCoeff = static_cast<Real>(0.6);
    m_frictionCoeff = static_cast<Real>(0.2);
}

TetModel::~TetModel(void)
{
    cleanupModel();
}

void TetModel::cleanupModel()
{
    m_particleMesh.release();
}

TetModel::SurfaceMesh &TetModel::getSurfaceMesh()
{
    return m_surfaceMesh;
}

VertexData & TetModel::getVisVertices()
{
    return m_visVertices;
}

TetModel::SurfaceMesh &TetModel::getVisMesh()
{
    return m_visMesh;
}

void TetModel::initMesh(const unsigned int nPoints, const unsigned int nTets, const unsigned int indexOffset, unsigned int* indices)
{
    m_indexOffset = indexOffset;
    m_particleMesh.release();
    m_particleMesh.initMesh(nPoints, nTets * 6, nTets * 4, nTets);

    for (unsigned int i = 0; i < nTets; i++)
    {
        m_particleMesh.addTet(&indices[4 * i]);
    }
    m_particleMesh.buildNeighbors();

    createSurfaceMesh();
}

unsigned int TetModel::getIndexOffset() const
{
    return m_indexOffset;
}

void TetModel::createSurfaceMesh()
{
    const unsigned int nVerts = m_particleMesh.numVertices();

    m_surfaceMesh.initMesh(nVerts, m_particleMesh.numEdges(), m_particleMesh.numFaces());

    // Search for all border faces of the tet mesh
    const IndexedTetMesh::Face *faceData = m_particleMesh.getFaceData().data();
    const unsigned int *faces = m_particleMesh.getFaces().data();
    for (unsigned int i = 0; i < m_particleMesh.numFaces(); i++)
    {
        const IndexedTetMesh::Face &face = faceData[i];
        // Found border face
        if ((face.m_tets[1] == 0xffffffff) || (face.m_tets[0] == 0xffffffff))
        {
            m_surfaceMesh.addFace(&faces[3 * i]);
        }
    }
    m_surfaceMesh.buildNeighbors();
}

void TetModel::updateMeshNormals(const ParticleData &pd)
{
    m_surfaceMesh.updateNormals(pd, m_indexOffset);
    m_surfaceMesh.updateVertexNormals(pd);
}

void TetModel::attachVisMesh(const ParticleData &pd)
{
    const Real eps = static_cast<Real>(1.0e-6);

    // The created surface mesh defines the boundary of the tet mesh
    unsigned int *faces = m_surfaceMesh.getFaces().data();
    const unsigned int nFaces = m_surfaceMesh.numFaces();

    const Vector3r *normals = m_surfaceMesh.getVertexNormals().data();

    // for each point find nearest triangle (TODO: optimize)
    const int nNearstT = 15;
    m_attachments.resize(m_visVertices.size());

    #pragma omp parallel default(shared)
    {
        #pragma omp for schedule(static)
        for (int i = 0; i < (int)m_visVertices.size(); i++)
        {
            const Vector3r &p = m_visVertices.getPosition(i);
            Real curDist[nNearstT];
            int curT[nNearstT];
            for (int k = 0; k < nNearstT; k++)
            {
                curDist[k] = REAL_MAX;
                curT[k] = -1;
            }
            Vector3r curBary[nNearstT];
            Vector3r curInter[nNearstT];
            for (unsigned int j = 0; j < nFaces; j++)
            {
                const unsigned int indexA = faces[3 * j] + m_indexOffset;
                const unsigned int indexB = faces[3 * j + 1] + m_indexOffset;
                const unsigned int indexC = faces[3 * j + 2] + m_indexOffset;
                const Vector3r & a = pd.getPosition0(indexA);
                const Vector3r & b = pd.getPosition0(indexB);
                const Vector3r & c = pd.getPosition0(indexC);

                Vector3r inter, bary;
                // compute nearest point on triangle
                if (pointInTriangle(a, b, c, p, inter, bary))
                {
                    Real len = (p - inter).norm();
                    for (int k = nNearstT - 1; k >= 0; k--) // update the best triangles
                    {
                        if (len < curDist[k])
                        {
                            if (k < nNearstT - 1)
                            {
                                curDist[k + 1] = curDist[k];
                                curBary[k + 1] = curBary[k];
                                curT[k + 1] = curT[k];
                                curInter[k + 1] = curInter[k];
                            }
                            curDist[k] = len;
                            curBary[k] = bary;
                            curT[k] = (int)j;
                            curInter[k] = inter;
                        }
                    }
                }
            }
            if (curT[0] == -1)
            {
                LOG_ERR << "ERROR: vertex has no nearest triangle.";
                continue;
            }

            // take the best bary coords we find from the best 5 triangles
            Real error = REAL_MAX;
            int current_k = 0;
            Real current_dist = 0.0;
            Vector3r current_bary;
            for (int k = 0; k < nNearstT; k++)
            {
                if (curT[k] == -1)
                    break;

                // see Kobbelt: Multiresolution Herarchies on unstructured triangle meshes
                const Vector3r& p = m_visVertices.getPosition(i);
                const Vector3r n1 = -normals[faces[3 * curT[k] + 0]];
                const Vector3r n2 = -normals[faces[3 * curT[k] + 1]];
                const Vector3r n3 = -normals[faces[3 * curT[k] + 2]];
                const Vector3r& p1 = pd.getPosition0(faces[3 * curT[k] + 0] + m_indexOffset);
                const Vector3r& p2 = pd.getPosition0(faces[3 * curT[k] + 1] + m_indexOffset);
                const Vector3r& p3 = pd.getPosition0(faces[3 * curT[k] + 2] + m_indexOffset);
                const Vector3r U = p.cross(n1);
                const Vector3r V = p.cross(n2);
                const Vector3r W = p.cross(n3);
                const Vector3r UU = n1.cross(p1);
                const Vector3r VV = n2.cross(p2);
                const Vector3r WW = n3.cross(p3);
                const Vector3r UV = (n2.cross(p1)) + (n1.cross(p2));
                const Vector3r UW = (n3.cross(p1)) + (n1.cross(p3));
                const Vector3r VW = (n3.cross(p2)) + (n2.cross(p3));
                // F(u,v) = F + Fu*u + Fv*v + Fuu*u*u + Fuv*u*v + Fvv*v*v == 0!
                const Vector3r F = W + WW;
                const Vector3r Fu = U + UW - W - WW * 2.0;
                const Vector3r Fv = V + VW - W - WW * 2.0;
                const Vector3r Fuu = UU - UW + WW;
                const Vector3r Fuv = UV - UW - VW + WW * 2.0;
                const Vector3r Fvv = VV - VW + WW;
                Real u = curBary[k][0];
                Real v = curBary[k][0];
                solveQuadraticForZero(F, Fu, Fv, Fuu, Fuv, Fvv, u, v);
                Real w = static_cast<Real>(1.0) - u - v;

                if (u < 0) u = 0.0;
                if (u > 1) u = 1.0;
                if (v < 0) v = 0.0;
                if (v > 1) v = 1.0;
                if (u + v > 1)
                {
                    Real uv = u + v;
                    Real u_ = u;
                    Real v_ = v;
                    u -= (uv - static_cast<Real>(1.0))*v_ / uv;
                    v -= (uv - static_cast<Real>(1.0))*u_ / uv;
                }
                w = static_cast<Real>(1.0) - u - v;
                Vector3r curInter = p1*u + p2*v + p3*w;
                Real dist = (p - curInter).norm();

                Vector3r n = n1*u + n2*v + n3*w;
                Real err = dist;
                if ((p - curInter).dot(n) < 0.0)
                    dist *= -1.0;
                Vector3r interP = curInter + n*dist;
                err += (interP - p).norm();

                if (err > error)
                    continue;

                error = err;

                current_k = k;
                current_dist = dist;
                current_bary = Vector3r(u, v, w);

                if (error < eps)
                    break;
            }


            Attachment &fp = m_attachments[i];
            fp.m_index = i;
            fp.m_triIndex = (unsigned int)curT[current_k];
            fp.m_bary[0] = current_bary.x();
            fp.m_bary[1] = current_bary.y();
            fp.m_bary[2] = current_bary.z();
            fp.m_dist = current_dist;
            fp.m_minError = error;
        }
    }
}

void TetModel::solveQuadraticForZero(const Vector3r& F, const Vector3r& Fu, const Vector3r& Fv, const Vector3r& Fuu,
    const Vector3r&Fuv, const Vector3r& Fvv, Real& u, Real& v)
{
    // newton iterations search F(u,v) = [0,0,0]
    Real eps = static_cast<Real>(1.0e-6);
    unsigned char k;
    for (k = 0; k < 50; k++)
    {
        // x(n+1) = x(n) - F'^(-1)(x(n))*F(x(n))
        // dx = -F'^(-1)*F => dF*dx = -F
        // => dF^T*dF*dx = dF^T*(-F)
        // solve for dx
        const Vector3r f = -(F + Fu*u + Fv*v + Fuu*u*u + Fuv*u*v + Fvv*v*v);
        if ((fabs(f[0]) < eps) && (fabs(f[1]) < eps) && (fabs(f[2]) < eps))
            break;
        Vector3r dF[2];
        dF[0] = Fu + Fuu*(u * 2) + Fuv*v;
        dF[1] = Fv + Fvv*(v * 2) + Fuv*u;
        Real dFdF[3];
        dFdF[0] = dF[0].dot(dF[0]);
        dFdF[1] = dF[0].dot(dF[1]);
        dFdF[2] = dF[1].dot(dF[1]);
        const Real det = dFdF[0] * dFdF[2] - dFdF[1] * dFdF[1];
        if (fabs(det) < eps)
            break;
        Real H[3];
        H[0] = dFdF[2] / det;
        H[1] = -dFdF[1] / det;
        H[2] = dFdF[0] / det;
        const Real h1 = dF[0].dot(f);
        const Real h2 = dF[1].dot(f);
        u += H[0] * h1 + H[1] * h2;
        v += H[1] * h1 + H[2] * h2;
    }
}

void TetModel::updateVisMesh(const ParticleData &pd)
{
    if (m_attachments.size() == 0)
        return;

    // The collision mesh is the boundary of the tet mesh
    unsigned int *faces = m_surfaceMesh.getFaces().data();
    const unsigned int nFaces = m_surfaceMesh.numFaces();

    const Vector3r *normals = m_surfaceMesh.getVertexNormals().data();

    #pragma omp parallel default(shared)
    {
        #pragma omp for schedule(static)
        for (int i = 0; i < (int) m_attachments.size(); i++)
        {
            const unsigned int pindex = m_attachments[i].m_index;
            const unsigned int triindex = m_attachments[i].m_triIndex;
            const Real *bary = m_attachments[i].m_bary;

            const unsigned int indexA = faces[3 * triindex] + m_indexOffset;
            const unsigned int indexB = faces[3 * triindex + 1] + m_indexOffset;
            const unsigned int indexC = faces[3 * triindex + 2] + m_indexOffset;

            const Vector3r &a = pd.getPosition(indexA);
            const Vector3r &b = pd.getPosition(indexB);
            const Vector3r &c = pd.getPosition(indexC);
            Vector3r p2 = bary[0] * a + bary[1] * b + bary[2] * c;
            Vector3r n = bary[0] * normals[faces[3 * triindex]] + bary[1] * normals[faces[3 * triindex + 1]] + bary[2] * normals[faces[3 * triindex + 2]];
            n.normalize();

            Vector3r &p = m_visVertices.getPosition(pindex);
            p = p2 - n*m_attachments[i].m_dist;
        }
    }

    m_visMesh.updateNormals(m_visVertices, 0);
    m_visMesh.updateVertexNormals(m_visVertices);
}


 bool TetModel::pointInTriangle(const Vector3r& p0, const Vector3r& p1, const Vector3r& p2, const Vector3r& p,
    Vector3r& inter, Vector3r &bary)
 {
    // see Bridson: Robust treatment of collisions contact and friction for cloth animation
    const Vector3r x43 = p - p2;
    const Vector3r x13 = p0 - p2;
    const Vector3r x23 = p1 - p2;

    // compute inv matrix a,b,b,c
    Real a = x13.dot(x13);
    Real b = x13.dot(x23);
    Real c = x23.dot(x23);
    const Real det = a*c - b*b;
    if (fabs(det) < 1.0e-9)
        return false;

    Real d1 = x13.dot(x43);
    Real d2 = x23.dot(x43);

    Real w1 = (c*d1 - b*d2) / det;
    Real w2 = (a*d2 - b*d1) / det;

    // this clamping gives not an exact orthogonal point to the edge!!
    if (w1 < 0) w1 = 0;
    if (w1 > 1) w1 = 1;
    if (w2 < 0) w2 = 0;
    if (w2 > 1) w2 = 1;

    bary[0] = w1;
    bary[1] = w2;
    bary[2] = (Real)1 - w1 - w2;

    if (bary[2] < 0)
    {
        // this gives not an exact orthogonal point to the edge!!
        const Real w12 = w1 + w2;
        bary[0] -= w2 / (w12)*(w12 - 1);
        bary[1] -= w1 / (w12)*(w12 - 1);
        bary[2] = 0;
    }

    inter = p2 + bary[0] * x13 + bary[1] * x23;

    return true;
 }