package org.simantics.opencascade.jme; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import org.jcae.opencascade.jni.BRepMesh_IncrementalMesh; import org.jcae.opencascade.jni.BRep_Tool; import org.jcae.opencascade.jni.GP_Trsf; import org.jcae.opencascade.jni.Poly_Triangulation; import org.jcae.opencascade.jni.TopAbs_Orientation; import org.jcae.opencascade.jni.TopAbs_ShapeEnum; import org.jcae.opencascade.jni.TopExp_Explorer; import org.jcae.opencascade.jni.TopLoc_Location; import org.jcae.opencascade.jni.TopoDS_Face; import org.jcae.opencascade.jni.TopoDS_Shape; import org.simantics.opencascade.OCCTTool; import org.simantics.utils.datastructures.MapList; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; public class JmeOCCTTool { public static Spatial vtkOCCShapeToAssembly(TopoDS_Shape shape) { double deflection = 0.001; if (deflection <= 0.0) { deflection = 0.0005; System.out.println("Bad value for deflection. Using: " + deflection); } // FIXME : leaks memory! //BRepTools.clean(shape); double mass = OCCTTool.getMass(shape); if (mass < 1.0e-12) { System.out.println("Non 3D-shape detected"); System.out.println("The cad import features are currently limited to 3D models."); } double length = OCCTTool.getBoundingBoxDiagonal(shape); deflection *= length; // use relative units BRepMesh_IncrementalMesh mesh = new BRepMesh_IncrementalMesh(shape,deflection); int faceNumber = 0; TopExp_Explorer expFace = new TopExp_Explorer(); Node node = new Node(); for (expFace.init(shape, TopAbs_ShapeEnum.FACE); expFace.more(); expFace.next()) { TopoDS_Face face = (TopoDS_Face) expFace.current(); Mesh partGrid = createPartGrid(face); face.delete(); if (partGrid == null) continue; faceNumber++; //gridToAssembly(assemblies, partGrid, stlSurfaceData, stlEdgeData); gridToAssembly(node, partGrid); } expFace.delete(); mesh.delete(); if (faceNumber == 0) { System.out .println("Cad import: error: no surface triangulation was generated."); return null; } return node; } public static void gridToAssembly(Node assemblies, Mesh partGrid) { Geometry geom = new Geometry(); geom.setMesh(partGrid); assemblies.attachChild(geom); } public static Mesh createPartGrid ( TopoDS_Face face) { TopLoc_Location Location = new TopLoc_Location(); Poly_Triangulation triangulation = BRep_Tool.triangulation(face, Location); if(triangulation == null) { Location.delete(); System.out.println("Encountered empty triangulation after face"); return null; } boolean reverse = face.orientation()==TopAbs_Orientation.REVERSED; int[]triangles = triangulation.triangles(); double[]nodes = triangulation.nodes(); int nofTriangles = triangulation.nbTriangles(); int nofNodes = triangulation.nbNodes(); triangulation.delete(); if(nofTriangles < 1) { System.out.println("No triangles for mesh on face"); Location.delete(); return null; } if(nofNodes < 1) { System.out.println("No nodes for mesh on face:"); Location.delete(); return null; } Mesh mesh = new Mesh(); int index[] = new int[nofTriangles*3]; for(int i = 0; i < nofTriangles; i++) { int n0, n1, n2; if (!reverse) { n0 = triangles[3 * i]; n1 = triangles[3 * i + 1]; n2 = triangles[3 * i + 2]; } else { n0 = triangles[3 * i + 2]; n1 = triangles[3 * i + 1]; n2 = triangles[3 * i]; } index[i*3] = n0; index[i*3+1] = n1; index[i*3+2] = n2; } GP_Trsf transformation = Location.transformation(); Location.delete(); double d_mat[] = new double[16]; transformation.getValues(d_mat); Matrix4d mat = new Matrix4d(d_mat); float vertex[] = new float[nofNodes*3]; for(int i = 0; i < nofNodes; i++) { // FIXME: GP_Trsf.transform(double[]) leaks memory //double xyz[] = new double[]{nodes[3 * i], nodes[3 * i + 1], nodes[3 * i + 2]}; //transformation.transforms(xyz); //partPoints.InsertPoint(i, xyz); Point3d p = new Point3d(nodes[3 * i], nodes[3 * i + 1], nodes[3 * i + 2]); mat.transform(p); vertex[3 * i] = (float)p.x; vertex[3 * i+1] = (float)p.y; vertex[3 * i+2] = (float)p.z; } transformation.delete(); mesh.setBuffer(Type.Position, 3, vertex); mesh.setBuffer(Type.Index, 3, index); return mesh; } public static Mesh createPartGrid ( List meshPoints, List meshTriangles) { int nofTriangles = meshTriangles.size() / 3; int nofNodes = meshPoints.size() /3; if(nofTriangles < 1) { System.out.println("No triangles for mesh on face"); return null; } if(nofNodes < 1) { System.out.println("No nodes for mesh on face:"); return null; } Mesh mesh = new Mesh(); //System.out.println("v " + nofNodes + " t " +nofTriangles); int index[] = new int[nofTriangles*3]; for(int i = 0; i < nofTriangles; i++) { int n0, n1, n2; n0 = meshTriangles.get(3 * i); n1 = meshTriangles.get(3 * i + 1); n2 = meshTriangles.get(3 * i + 2); index[i * 3 ] = n0; index[i * 3 + 1] = n1; index[i * 3 + 2] = n2; } float vertex[] = new float[nofNodes*3]; for(int i = 0; i < nofNodes; i++) { vertex[3 * i] = meshPoints.get(3*i).floatValue(); vertex[3 * i+1] = meshPoints.get(3*i+1).floatValue(); vertex[3 * i+2] = meshPoints.get(3*i+2).floatValue(); } float normal[] = calcNormals(vertex, index); mesh.setBuffer(Type.Position, 3, vertex); mesh.setBuffer(Type.Normal, 3, normal); mesh.setBuffer(Type.Index, 3, index); return mesh; } private static float[] calcNormals(float[] fnodes, int[] trias) { float nnodes[] = new float[fnodes.length]; for (int i = 0; i < nnodes.length; i++) nnodes[i] = 0.f; Vector3f v1 = new Vector3f(); Vector3f v2 = new Vector3f(); Vector3f v3 = new Vector3f(); Vector3f t1 = new Vector3f(); Vector3f t2 = new Vector3f(); Vector3f n = new Vector3f(); for (int i = 0; i < trias.length; i+=3) { v1.x = fnodes[trias[i]*3]; v1.y = fnodes[trias[i]*3+1]; v1.z = fnodes[trias[i]*3+2]; v2.x = fnodes[trias[i+1]*3]; v2.y = fnodes[trias[i+1]*3+1]; v2.z = fnodes[trias[i+1]*3+2]; v3.x = fnodes[trias[i+2]*3]; v3.y = fnodes[trias[i+2]*3+1]; v3.z = fnodes[trias[i+2]*3+2]; t1.sub(v3,v1); t2.sub(v2,v1); n.cross(t2, t1); //n.normalize(); nnodes[trias[i]*3] += n.x; nnodes[trias[i]*3+1] += n.y; nnodes[trias[i]*3+2] += n.z; nnodes[trias[i+1]*3] += n.x; nnodes[trias[i+1]*3+1] += n.y; nnodes[trias[i+1]*3+2] += n.z; nnodes[trias[i+2]*3] += n.x; nnodes[trias[i+2]*3+1] += n.y; nnodes[trias[i+2]*3+2] += n.z; } for (int i = 0; i < nnodes.length; i+=3) { n.x = nnodes[i]; n.y = nnodes[i+1]; n.z = nnodes[i+2]; n.normalize(); nnodes[i] = n.x; nnodes[i+1] = n.y; nnodes[i+2] = n.z; } return nnodes; } public static Mesh createEdgeMesh(Mesh triMesh) { // list all edges in the mesh MapList triEdgeIndices = new MapList(); for (int i = 0; i < triMesh.getTriangleCount(); i++) { int[] tri = new int[3]; triMesh.getTriangle(i, tri); if (!triEdgeIndices.contains(tri[0], tri[1])) { triEdgeIndices.add(tri[0], tri[1]); } if (!triEdgeIndices.contains(tri[1], tri[2])) { triEdgeIndices.add(tri[1], tri[2]); } if (!triEdgeIndices.contains(tri[2], tri[0])) { triEdgeIndices.add(tri[2], tri[0]); } } // find, which edges are listed only once; those are boundaries of the mesh. MapList edgeIndices = new MapList(); for (int s : triEdgeIndices.getKeys()) { for (int e : triEdgeIndices.getValues(s)) { if (!triEdgeIndices.contains(e, s)) { edgeIndices.add(s,e); edgeIndices.add(e,s); } } } // create a new mesh, containing boundary vertices and edges of the original mesh. // create list of edge vertices in the original mesh List vertices = new ArrayList(); FloatBuffer data = triMesh.getFloatBuffer(Type.Position); for (Integer i : edgeIndices.getKeys()) { List edges = edgeIndices.getValues(i); if (!vertices.contains(i)) vertices.add(i); if (!vertices.contains(edges.get(0))) vertices.add(edges.get(0)); if (!vertices.contains(edges.get(1))) vertices.add(edges.get(1)); } // create vertices for edge mesh, and map new vertices to orignals. float vertex[] = new float[vertices.size() * 3]; int i2 = 0; Map indexMap = new HashMap(); for (int i : vertices) { vertex[i2*3] = data.get(i*3); vertex[i2*3+1] = data.get(i*3+1); vertex[i2*3+2] = data.get(i*3+2); indexMap.put(i, i2); i2++; } // create line indices for the edge mesh. List indices = new ArrayList(); for (int i = 0; i < vertices.size(); i++) { int s = vertices.get(i); List edges = edgeIndices.getValues(s); for (int e : edges) { if (edgeIndices.contains(s, e) && indexMap.containsKey(s) && indexMap.containsKey(e)) { indices.add(indexMap.get(s)); indices.add(indexMap.get(e)); } } } int index[] = new int[indices.size()]; for (int i = 0; i < indices.size(); i++) { index[i] = indices.get(i); } Mesh mesh = new Mesh(); mesh.setMode(Mode.Lines); mesh.setBuffer(Type.Position, 3, vertex); mesh.setBuffer(Type.Index, 2, index); return mesh; } public static Mesh createVertexMesh(Mesh edgeMesh) { MapList edgeIndices = new MapList(); IndexBuffer indices = edgeMesh.getIndexBuffer(); for (int i = 0; i < indices.size(); i+=2) { int s = indices.get(i); int e = indices.get(i+1); if (!edgeIndices.contains(s, e)) { edgeIndices.add(s, e); edgeIndices.add(e, s); } } List vertices = new ArrayList(); float data[] = edgeMesh.getFloatBuffer(Type.Position).array(); for (Integer i : edgeIndices.getKeys()) { List edges = edgeIndices.getValues(i); if (edges.size() != 2) vertices.add(i); else { Point3d t = new Point3d(data[i*3],data[i*3+1],data[i*3+2]); Vector3d v1 = new Vector3d(data[edges.get(0)*3],data[edges.get(0)*3+1],data[edges.get(0)*3+2]); Vector3d v2 = new Vector3d(data[edges.get(1)*3],data[edges.get(1)*3+1],data[edges.get(1)*3+2]); v1.sub(t); v2.sub(t); double angle = Math.PI - v1.angle(v2); if (angle > Math.PI/6) vertices.add(i); } } float vertex[] = new float[vertices.size() * 3]; int i2 = 0; Map indexMap = new HashMap(); for (int i : vertices) { vertex[i2*3] = data[i*3]; vertex[i2*3+1] = data[i*3+1]; vertex[i2*3+2] = data[i*3+2]; indexMap.put(i, i2); i2++; } int index[] = new int[vertices.size()]; for (int i = 0; i < vertices.size(); i++) { index[i] = i; } Mesh mesh = new Mesh(); mesh.setMode(Mode.Points); mesh.setBuffer(Type.Position, 3, vertex); mesh.setBuffer(Type.Index, 1, index); return mesh; } }