Hi, here's how I do it.
The following class contains the mesh data. It is basically an array of nodes and an array of faces (I'm using array list but you can also use fixed arrays which may be helpful if you want to assign the mesh to a scene node by reference). The faces are simply three indices referring to the node array that define a triangular face.
package ar.Geometry;
import java.util.ArrayList;
import javax.vecmath.Point3f;
import javax.vecmath.Tuple3f;
import javax.vecmath.Vector3f;
/*******************
* Geometry class to represent a 3D triangular mesh. This is implemented as an array
* of nodes and an array of MeshFace3D objects, which hold the indices of the three nodes
* of the triangular face.
* @author Andrew Reid
* @version 1.0
*/
public class Mesh3D extends Shape3D {
public ArrayList<Point3f> nodes = new ArrayList<Point3f>();
public ArrayList<MeshFace3D> faces = new ArrayList<MeshFace3D>();
public Mesh3D(){
}
public int addNode(Point3f n){
nodes.add(n);
return nodes.size() - 1;
}
public void addNode(Point3f n, int i){
nodes.add(i, n);
}
public boolean addFace(int a, int b, int c){
if (a >= nodes.size() || b >= nodes.size() || c >= nodes.size())
return false;
return faces.add(new MeshFace3D(a, b, c));
}
public void removeNode(int i){
//complication is we need to find and remove all faces with i as nodes
//plus decrement all indices >= i
nodes.remove(i);
for (int a = 0; a < faces.size(); a++){
if (faces.get(a).hasNode(i)){
faces.remove(a);
a--;
}else{
if (faces.get(a).A > i) faces.get(a).A--;
if (faces.get(a).B > i) faces.get(a).B--;
if (faces.get(a).C > i) faces.get(a).C--;
}
}
}
public void removeFace(int i){
faces.remove(i);
}
public ArrayList<Point3f> getNodes(){
ArrayList<Point3f> retNodes = new ArrayList<Point3f>(nodes);
return retNodes;
}
public void setNodes(ArrayList<Point3f> n){
nodes = n;
}
public int[] getFaceIndexArray(){
int[] retArray = new int[faces.size() * 3];
for (int i = 0; i < faces.size(); i++){
retArray[i * 3] = faces.get(i).A;
retArray[i * 3 + 1] = faces.get(i).B;
retArray[i * 3 + 2] = faces.get(i).C;
}
return retArray;
}
public void finalize(){
nodes.trimToSize();
faces.trimToSize();
}
//is this face clockwise?
public boolean isClockwiseFace(int i){
return GeometryFunctions.isClockwise(nodes.get(faces.get(i).A),
nodes.get(faces.get(i).B),
nodes.get(faces.get(i).C));
}
//inner class to define a triangular mesh face
public class MeshFace3D{
//indices of this face's nodes, in clockwise order of A, B, C
public int A, B, C;
public MeshFace3D(int a, int b, int c){
A = a;
B = b;
C = c;
}
public MeshFace3D(MeshFace3D f){
A = f.A;
B = f.B;
C = f.C;
}
public boolean hasNode(int i){
return A == i || B == i || C == i;
}
}
}
And here's the method I use to set a scene node from the mesh object. You need to use an IndexedTriangleArray (subclass of GeometryArray). GeometryInfo takes care of all the tough stuff like normal generation.
public void setScene3DObject(){
scene3DObject = new BranchGroup();
if (!this.isVisible()) return;
Mesh3D mesh = (Mesh3D)thisShape;
if (mesh.nodes.size() == 0 || mesh.faces.size() == 0)
scene3DObject = null;
Point3f[] nodes = new Point3f[mesh.nodes.size()];
mesh.nodes.toArray(nodes);
int[] indices = mesh.getFaceIndexArray();
//show fill if selected
if (((arBoolean)attributes.getValue("HasFill")).value){
//generate normals for shading
GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
gi.setCoordinates(nodes);
gi.setCoordinateIndices(indices);
NormalGenerator ng = new NormalGenerator();
ng.generateNormals(gi);
IndexedTriangleArray triArray = (IndexedTriangleArray)gi.getIndexedGeometryArray();
//branch group respresenting this shape
BranchGroup thisNode = new BranchGroup();
//add geometry to shape node
javax.media.j3d.Shape3D fillShapeNode = new javax.media.j3d.Shape3D(triArray);
//set fill colour
Appearance fillAppNode = new Appearance();
Color thisColour = (Color)attributes.getValue("FillColour");
//turn off back culling
PolygonAttributes pAtt = new PolygonAttributes();
pAtt.setCullFace(PolygonAttributes.CULL_NONE);
pAtt.setBackFaceNormalFlip(true);
fillAppNode.setPolygonAttributes(pAtt);
Material m = new Material();
m.setDiffuseColor(new Color3f(thisColour));
//TransparencyAttributes ta = new TransparencyAttributes();
//ta.setTransparency(0.5f);
//ta.setTransparencyMode(TransparencyAttributes.NICEST);
fillAppNode.setMaterial(m);
//apply appearance settings
fillShapeNode.setAppearance(fillAppNode);
//fillAppNode.setTransparencyAttributes(ta);
thisNode.addChild(fillShapeNode);
}
//show edges if selected
if (((arBoolean)attributes.getValue("ShowEdges")).value){
IndexedTriangleArray edgeArray = new IndexedTriangleArray(nodes.length,
IndexedTriangleArray.COORDINATES,
indices.length);
//set geometry
edgeArray.setCoordinates(0, nodes);
edgeArray.setCoordinateIndices(0, indices);
javax.media.j3d.Shape3D edgeShapeNode = new javax.media.j3d.Shape3D(edgeArray);
Appearance edgeAppNode = new Appearance();
Color edgeColour = (Color)attributes.getValue("LineColour");
ColoringAttributes cAtt = new ColoringAttributes();
PolygonAttributes pAtt = new PolygonAttributes();
pAtt.setPolygonMode(PolygonAttributes.POLYGON_LINE);
pAtt.setCullFace(PolygonAttributes.CULL_NONE);
pAtt.setBackFaceNormalFlip(true);
cAtt.setColor(new Color3f(edgeColour));
edgeAppNode.setColoringAttributes(cAtt);
edgeAppNode.setPolygonAttributes(pAtt);
edgeAppNode.setMaterial(new Material());
edgeShapeNode.setAppearance(edgeAppNode);
thisNode.addChild(edgeShapeNode);
}
scene3DObject = thisNode;
}
Message was edited by:
typically