package net.hvidtfeldts.meshia.engine3d; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; import java.io.Writer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.List; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import net.hvidtfeldts.meshia.engine3d.Transformator.IdentityTransformator; import net.hvidtfeldts.meshia.math.Vector3; import net.hvidtfeldts.utils.Logger; import org.sunflow.SunflowAPI; import org.sunflow.core.ParameterList; import org.sunflow.core.ParameterList.InterpolationType; import org.sunflow.core.primitive.TriangleMesh; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.glsl.ShaderState; public class PolygonBuilder extends AbstractObject3D implements SunflowRenderable { private final List<Vector3> positions = new ArrayList<Vector3>(); private final List<Vector3> normals = new ArrayList<Vector3>(); private final List<Double> weights = new ArrayList<Double>(); private final List<Vector3> colors = new ArrayList<Vector3>(); private final List<Integer> triangles = new ArrayList<Integer>(); private final int[] indexBuffer = new int[] { -1 }; private GLArrayDataServer verticesVBO; private GLArrayDataServer normalsVBO; private GLArrayDataServer colorsVBO; private int elementCount; private Transformator transformator = new IdentityTransformator(); protected PolygonBuilder(ShaderState shaderState, String name) { super(shaderState, name); } void setTransformator(Transformator transformator) { this.transformator = transformator; } void translate(Vector3 t) { for (Vector3 v : positions) { v.add(t); } } /** * Adds a vertex with position, normal, and color. * * @return the index of the new vertex. */ int addColorVertex(Vector3 pos, Vector3 normal, Vector3 color) { positions.add(pos); normals.add(normal); weights.add(0d); colors.add(color); return positions.size() - 1; } int addVertex(Vector3 pos, Vector3 color, Vector3 c) { Vector3 t = transformator.transform(pos); positions.add(t); Vector3 n = new Vector3(t); n.normalize(); normals.add(n); colors.add(color); return positions.size() - 1; } void addTriangle(int vertex1, int vertex2, int vertex3) { triangles.add(vertex1); triangles.add(vertex2); triangles.add(vertex3); } void addSquare(int vertex1, int vertex2, int vertex3, int vertex4) { addTriangle(vertex1, vertex2, vertex3); addTriangle(vertex3, vertex4, vertex1); } @Override public void internalInit(GL2ES2 gl) { verticesVBO = GLArrayDataServer.createGLSL("vertex", 3, GL.GL_FLOAT, false, triangles.size(), GL.GL_STATIC_DRAW); colorsVBO = GLArrayDataServer.createGLSL("color", 4, GL.GL_FLOAT, false, triangles.size(), GL.GL_STATIC_DRAW); normalsVBO = GLArrayDataServer.createGLSL("normal", 3, GL.GL_FLOAT, false, triangles.size(), GL.GL_STATIC_DRAW); elementCount = triangles.size(); for (int j = 0; j < normals.size(); j++) { normals.get(j).normalize(); } for (int j = 0; j < positions.size(); j++) { int in = j; verticesVBO.putf(positions.get(in).getX()); verticesVBO.putf(positions.get(in).getY()); verticesVBO.putf(positions.get(in).getZ()); colorsVBO.putf(colors.get(in).getX()); colorsVBO.putf(colors.get(in).getY()); colorsVBO.putf(colors.get(in).getZ()); colorsVBO.putf(1.0f); normalsVBO.putf(normals.get(in).getX()); normalsVBO.putf(normals.get(in).getY()); normalsVBO.putf(normals.get(in).getZ()); } verticesVBO.seal(gl, true); shaderState.ownAttribute(verticesVBO, true); verticesVBO.enableBuffer(gl, false); colorsVBO.seal(gl, true); shaderState.ownAttribute(colorsVBO, true); colorsVBO.enableBuffer(gl, false); normalsVBO.seal(gl, true); shaderState.ownAttribute(normalsVBO, true); normalsVBO.enableBuffer(gl, false); /* * positions = null; * colors = null; * normals = null; * triangles = null; */ gl.glGenBuffers(1, indexBuffer, 0); int[] indexArray = new int[triangles.size()]; for (int i = 0; i < triangles.size(); i++) { indexArray[i] = triangles.get(i); } IntBuffer intBuffer = Buffers.newDirectIntBuffer(indexArray.length); intBuffer.put(indexArray); intBuffer.rewind(); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, indexBuffer[0]); gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indexArray.length * 4, intBuffer, GL.GL_STATIC_DRAW); Logger.log("Points: " + positions.size() + " normals: " + normals.size() + " indices: " + indexArray.length); Logger.log(String.format("Created %s triangles, %s edges", elementCount / 3, elementCount)); try { File file = new File("e:\\out.obj"); if (!file.exists()) { file.createNewFile(); } FileWriter fw = new FileWriter(file); BufferedWriter bw = new BufferedWriter(fw); // toOBJ(bw); // toSTL("e:\\out.stl"); bw.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void internalDraw(GL2ES2 gl) { verticesVBO.enableBuffer(gl, true); colorsVBO.enableBuffer(gl, true); normalsVBO.enableBuffer(gl, true); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, indexBuffer[0]); gl.glDrawElements( GL.GL_TRIANGLES, // mode elementCount, // count GL.GL_UNSIGNED_INT, // type 0 // element array buffer offset ); verticesVBO.enableBuffer(gl, false); colorsVBO.enableBuffer(gl, false); normalsVBO.enableBuffer(gl, false); } public void toSTL(String filename) throws FileNotFoundException, IOException { /* * UINT8[80] � Header * UINT32 � Number of triangles * * foreach triangle * REAL32[3] � Normal vector * REAL32[3] � Vertex 1 * REAL32[3] � Vertex 2 * REAL32[3] � Vertex 3 * UINT16 � Attribute byte count * end */ try (RandomAccessFile raf = new RandomAccessFile(filename, "rw"); FileChannel ch = raf.getChannel()) { Logger.startTime(); ByteBuffer bb = ByteBuffer.allocate(1000).order(ByteOrder.LITTLE_ENDIAN); for (int i = 0; i < 80; i++) { bb.put((byte) 32); // Header with spaces } bb.putInt(triangles.size() / 3); // Triangle count for (int i = 0; i < triangles.size(); i += 3) { int i1 = triangles.get(i); int i2 = triangles.get(i + 1); int i3 = triangles.get(i + 2); // Per-face Normal Vector3 n = normals.get(i1); n.normalize(); bb.putFloat(n.getX()).putFloat(n.getY()).putFloat(n.getZ()); // Vertices bb.putFloat(positions.get(i1).getX()).putFloat(positions.get(i1).getY()).putFloat(positions.get(i1).getZ()); bb.putFloat(positions.get(i2).getX()).putFloat(positions.get(i2).getY()).putFloat(positions.get(i2).getZ()); bb.putFloat(positions.get(i3).getX()).putFloat(positions.get(i3).getY()).putFloat(positions.get(i3).getZ()); // Two zeroes bb.putShort((short) 0); // Write bb.flip(); ch.write(bb); bb.clear(); } Logger.endTime("Wrote STL"); } } public void toOBJ(Writer sb) throws IOException { sb.append(String.format("// Vertices %n")); for (Vector3 v : positions) { sb.append(String.format("v %s %s %s%n", v.getX(), v.getY(), v.getZ())); } sb.append(String.format("%n// Normals %n")); for (Vector3 v : normals) { sb.append(String.format("vn %s %s %s%n", v.getX(), v.getY(), v.getZ())); } sb.append(String.format("%n// Faces %n")); for (int i = 0; i < triangles.size(); i += 3) { int i1 = triangles.get(i) + 1; int i2 = triangles.get(i + 1) + 1; int i3 = triangles.get(i + 2) + 1; sb.append(String.format("f %s//%s %s//%s %s//%s%n", i1, i1, i2, i2, i3, i3)); } } @Override public TriangleMesh getTriangleMesh(SunflowAPI api) { TriangleMesh tm = new TriangleMesh(); ParameterList pl = new ParameterList(); float[] points = new float[positions.size() * 3]; for (int i = 0; i < positions.size(); i++) { points[i * 3] = positions.get(i).getX(); points[i * 3 + 1] = positions.get(i).getY(); points[i * 3 + 2] = positions.get(i).getZ(); } float[] ns = new float[normals.size() * 3]; for (int i = 0; i < normals.size(); i++) { ns[i * 3] = normals.get(i).getX(); ns[i * 3 + 1] = normals.get(i).getY(); ns[i * 3 + 2] = normals.get(i).getZ(); } int[] indices = new int[triangles.size()]; for (int i = 0; i < triangles.size(); i++) { indices[i] = triangles.get(i); } Logger.log("Points: " + positions.size() + " normals: " + normals.size() + " indices: " + indices.length); pl.addPoints("points", InterpolationType.VERTEX, points); pl.addIntegerArray("triangles", indices); pl.addVectors("normals", InterpolationType.VERTEX, ns); tm.update(pl, api); return tm; } public void setNormal(int i1, Vector3 normal) { Vector3 old = normals.get(i1); if (old != null) { // normals.set(i1, Vector3.add(normal, old)); } else { normals.set(i1, normal); } } }