Problem dividing face of a WETriangleMesh (bug in toxiclibs ?)

Dear all,

In the following example code I’m trying to add 3 new faces on top of one of the original 4 faces of a tetrahedron mesh. The mesh vertices are connected by springs.

My problem is that the vertices of the newly added faces don’t match the original tetrahedron, as if they were disconnected from the mesh (top grey 3D triangle).

It is all the more surprising that the newly added springs that share the same vertices positions DO fit the tetrahedron (top 3 red lines)

I would really appreciate if someone could help me find what’s wrong with this example sketch

import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.geom.mesh.subdiv.*;
import toxi.physics3d.*;
import toxi.physics3d.behaviors.*;
import toxi.physics3d.constraints.*;
import toxi.processing.*;

import peasy.*;

VerletPhysics3D physics;
WETriangleMesh mesh;
ToxiclibsSupport gfx;

PeasyCam cam;

void setup() {
    perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000);
    size(690, 460, P3D);
    smooth(8);
        
    cam = new PeasyCam(this, 150);
    
    physics = new VerletPhysics3D();
    physics.setDrag(1.1);
    physics.setWorldBounds(new AABB(new Vec3D(), new Vec3D(width, height, height)));
    
    mesh = new WETriangleMesh();
    gfx = new ToxiclibsSupport(this);

            
    //Create 4 particles to form a tetrahedron        
    for (int i = 0; i < 4; i++) {
        VerletParticle3D p = new VerletParticle3D(Vec3D.randomVector());
        physics.addParticle(p);
        physics.addBehavior(new AttractionBehavior3D(p, 10, -4));
    }
        
    //Adding springs between particles
    for (VerletParticle3D p1 : physics.particles) {
        for (VerletParticle3D p2 : physics.particles) {
            if (p1 != p2) {
                VerletSpring3D s = new VerletSpring3D(p1, p2, 60, .1);
                physics.addSpring(s);
            }
        }
    }


    //Adding faces between the 4 particles
    VerletParticle3D p1 = physics.particles.get(0);
    VerletParticle3D p2 = physics.particles.get(1);
    VerletParticle3D p3 = physics.particles.get(2);
    VerletParticle3D p4 = physics.particles.get(3);
    
    mesh.addFace(p1, p2, p3);
    mesh.addFace(p1, p3, p4); 
    mesh.addFace(p1, p2, p4);     
    mesh.addFace(p2, p3, p4);
}
    
    
void draw(){
    background(30);
    
    
    //Update physics
    physics.update();
        
    
    //Update mesh vertices by moving them to the current position of their associated particles
    for (int i = 0, num = physics.particles.size(); i < num; i++) {
        mesh.getVertexForID(i).set(physics.particles.get(i));
    }

    
    //Draw springs
    for (int i = 0, num = physics.springs.size(); i < num; i++) {
        stroke(255, 30, 30);
        strokeWeight(1);
        line(physics.springs.get(i).a.x(), physics.springs.get(i).a.y(), physics.springs.get(i).a.z(),  
        physics.springs.get(i).b.x(), physics.springs.get(i).b.y(), physics.springs.get(i).b.z());
    }

   
    //Display WETriangle mesh
    noStroke();
    fill(255, 200);
    gfx.mesh(mesh);
}
    

   
           
   
        
void keyReleased(){
    
    //Selecting first face + computing its centroid
    Face f = mesh.getFaces().get(0);
    VerletParticle3D centroid = new VerletParticle3D(new Triangle3D(f.a, f.b, f.c).computeCentroid());
    
    // 3 vertices of the the first face
    VerletParticle3D a = physics.particles.get(physics.particles.indexOf(f.a));
    VerletParticle3D b = physics.particles.get(physics.particles.indexOf(f.b));
    VerletParticle3D c = physics.particles.get(physics.particles.indexOf(f.c));

    CreateFaces(a, b, c, centroid);
}
           

    
void CreateFaces(VerletParticle3D a, VerletParticle3D b, VerletParticle3D c, VerletParticle3D n) {
    
    //Add centroid to physics + set repulsive behavior
    physics.addParticle(n);
    physics.addBehavior(new AttractionBehavior3D(n, 10, -4));
    
    //Attach springs between the face vertices and the centroid
    VerletSpring3D s1 = new VerletSpring3D(a, n, 40, .1);
    VerletSpring3D s2 = new VerletSpring3D(b, n, 40, .1);
    VerletSpring3D s3 = new VerletSpring3D(c, n, 40, .1);
    
    physics.addSpring(s1);
    physics.addSpring(s2);
    physics.addSpring(s3);
    
    //Remove original face
    mesh.removeFace(mesh.getFaces().get(0));
    
    
    //Add faces of the newly formed tetrahedron to the mesh
    mesh.addFace(n, a, b);
    mesh.addFace(n, b, c);
    mesh.addFace(n, a, c);
}

It seems the vertices and edges of a mesh in toxiclibs need to be updated (re-indexed) every time a face is added.

mesh.rebuildIndex();

This single line fixed the issue.

Script
import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.geom.mesh.subdiv.*;
import toxi.physics3d.*;
import toxi.physics3d.behaviors.*;
import toxi.physics3d.constraints.*;
import toxi.processing.*;

import peasy.*;

VerletPhysics3D physics;
WETriangleMesh mesh;
ToxiclibsSupport gfx;

PeasyCam cam;

void setup() {
    perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000);
    size(690, 460, P3D);
    smooth(8);
        
    cam = new PeasyCam(this, 150);
    
    physics = new VerletPhysics3D();
    physics.setDrag(1.1);
    physics.setWorldBounds(new AABB(new Vec3D(), new Vec3D(width, height, height)));
    
    mesh = new WETriangleMesh();
    gfx = new ToxiclibsSupport(this);

            
    //Create 4 particles to form a tetrahedron        
    for (int i = 0; i < 4; i++) {
        VerletParticle3D p = new VerletParticle3D(Vec3D.randomVector());
        physics.addParticle(p);
        physics.addBehavior(new AttractionBehavior3D(p, 10, -4));
    }
        
    //Adding springs between particles
    for (VerletParticle3D p1 : physics.particles) {
        for (VerletParticle3D p2 : physics.particles) {
            if (p1 != p2) {
                VerletSpring3D s = new VerletSpring3D(p1, p2, 60, .1);
                physics.addSpring(s);
            }
        }
    }


    //Adding faces between the 4 particles
    VerletParticle3D p1 = physics.particles.get(0);
    VerletParticle3D p2 = physics.particles.get(1);
    VerletParticle3D p3 = physics.particles.get(2);
    VerletParticle3D p4 = physics.particles.get(3);
    
    mesh.addFace(p1, p2, p3);
    mesh.addFace(p1, p3, p4); 
    mesh.addFace(p1, p2, p4);     
    mesh.addFace(p2, p3, p4);
}
    
    
void draw(){
    background(30);
    
    
    //Update physics
    physics.update();
        
    
    //Update mesh vertices by moving them to the current position of their associated particles
    for (int i = 0, num = physics.particles.size(); i < num; i++) {
        mesh.getVertexForID(i).set(physics.particles.get(i));
    }

    
    //Draw springs
    for (int i = 0, num = physics.springs.size(); i < num; i++) {
        stroke(255, 30, 30);
        strokeWeight(1);
        line(physics.springs.get(i).a.x(), physics.springs.get(i).a.y(), physics.springs.get(i).a.z(),  
        physics.springs.get(i).b.x(), physics.springs.get(i).b.y(), physics.springs.get(i).b.z());
    }

   
    //Display WETriangle mesh
    noStroke();
    fill(255, 200);
    gfx.mesh(mesh);
}
    

   
           
   
        
void keyReleased(){
    
    //Selecting first face + computing its centroid
    Face f = mesh.getFaces().get(0);
    VerletParticle3D centroid = new VerletParticle3D(new Triangle3D(f.a, f.b, f.c).computeCentroid());
    
    // 3 vertices of the the first face
    VerletParticle3D a = physics.particles.get(physics.particles.indexOf(f.a));
    VerletParticle3D b = physics.particles.get(physics.particles.indexOf(f.b));
    VerletParticle3D c = physics.particles.get(physics.particles.indexOf(f.c));

    CreateFaces(a, b, c, centroid);
}
           

    
void CreateFaces(VerletParticle3D a, VerletParticle3D b, VerletParticle3D c, VerletParticle3D n) {
    
    //Re-index mesh vertices & edges
    mesh.rebuildIndex();
  
    //Add centroid to physics + set repulsive behavior
    physics.addParticle(n);
    physics.addBehavior(new AttractionBehavior3D(n, 10, -4));
    
    //Attach springs between the face vertices and the centroid
    VerletSpring3D s1 = new VerletSpring3D(a, n, 40, .1);
    VerletSpring3D s2 = new VerletSpring3D(b, n, 40, .1);
    VerletSpring3D s3 = new VerletSpring3D(c, n, 40, .1);
    
    physics.addSpring(s1);
    physics.addSpring(s2);
    physics.addSpring(s3);
    
    //Remove original face
    mesh.removeFace(mesh.getFaces().get(0));
    
    
    //Add faces of the newly formed tetrahedron to the mesh
    mesh.addFace(n, a, b);
    mesh.addFace(n, b, c);
    mesh.addFace(n, a, c);
}