Java tutorial
/******************************************************************************* * This file is part of TERMINAL RECALL * Copyright (c) 2012-2014 Chuck Ritola * Part of the jTRFP.org project * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * chuck - initial API and implementation ******************************************************************************/ package org.jtrfp.trcl; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import javax.media.opengl.GL3; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLRunnable; import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; import org.jtrfp.trcl.core.TR; import org.jtrfp.trcl.core.Texture; import org.jtrfp.trcl.core.TextureDescription; import org.jtrfp.trcl.core.TriangleVertex2FlatDoubleWindow; import org.jtrfp.trcl.core.TriangleVertexWindow; import org.jtrfp.trcl.core.WindowAnimator; import org.jtrfp.trcl.gpu.Model; import org.jtrfp.trcl.mem.MemoryWindow; public class TriangleList extends PrimitiveList<Triangle> { private Controller controller; private int timeBetweenFramesMsec; private int[] triangleVertexIndices; private final boolean animateUV; private final WindowAnimator xyzAnimator; private TriangleVertex2FlatDoubleWindow flatTVWindow; private Vector3D cachedMinimumVertexDims, cachedMaximumVertexDims; private double cachedMaximumVertexValue; public TriangleList(Triangle[][] triangles, int timeBetweenFramesMsec, String debugName, boolean animateUV, Controller controller, TR tr, Model m) { super(debugName, triangles, new TriangleVertexWindow(tr, debugName), tr, m); this.timeBetweenFramesMsec = timeBetweenFramesMsec; this.animateUV = animateUV; this.controller = controller; if (getPrimitives().length > 1) { this.xyzAnimator = new WindowAnimator(getFlatTVWindow(), this.getNumElements() * 3 * XYZXferFunc.FRONT_STRIDE_LEN, // 3 vertices per triangle, // XYZ+NxNyNz per vertex getPrimitives().length, true, controller, new XYZXferFunc(0)); getModel().addTickableAnimator(xyzAnimator); } else if (animateUV) { this.xyzAnimator = null; } else { this.xyzAnimator = null; } }//end constructor private static class XYZXferFunc implements IntTransferFunction { private final int startIndex; public static final int BACK_STRIDE_LEN = 8; public static final int FRONT_STRIDE_LEN = 6; private static final byte[] STRIDE_PATTERN = new byte[] { 0, 1, 2, //XYZ //..UV 5, 6, 7//NxNyNz }; public XYZXferFunc(int startIndex) { this.startIndex = startIndex; }// end constructor @Override public int transfer(int input) { return startIndex + STRIDE_PATTERN[input % FRONT_STRIDE_LEN] + (input / FRONT_STRIDE_LEN) * BACK_STRIDE_LEN; }// end transfer(...) }// end class XYZXferFunc private static class UVXferFunc implements IntTransferFunction { private final int startIndex; public static final int BACK_STRIDE_LEN = 8; private final int FRONT_STRIDE_LEN = 2; public UVXferFunc(int startIndex) { this.startIndex = startIndex; }// end constructor @Override public int transfer(int input) { return (input / FRONT_STRIDE_LEN) * BACK_STRIDE_LEN + (input % FRONT_STRIDE_LEN) + startIndex + 3; }// end transfer(...) }// end class XYZXferFunc private Controller getVertexSequencer(int timeBetweenFramesMsec, int nFrames) { return controller; } private Triangle triangleAt(int frame, int tIndex) { return getPrimitives()[frame][tIndex]; } private void setupVertex(int vIndex, int gpuTVIndex, int triangleIndex, TextureDescription td) throws ExecutionException, InterruptedException { final int numFrames = getPrimitives().length; final Triangle t = triangleAt(0, triangleIndex); final Vector3D pos = t.getVertices()[vIndex].getPosition(); final TriangleVertexWindow vw = (TriangleVertexWindow) getMemoryWindow(); ////////////////////// V E R T E X ////////////////////////////// if (numFrames == 1) { vw.x.set(gpuTVIndex, (short) applyScale(pos.getX())); vw.y.set(gpuTVIndex, (short) applyScale(pos.getY())); vw.z.set(gpuTVIndex, (short) applyScale(pos.getZ())); final Vector3D normal = t.getVertices()[vIndex].getNormal(); vw.normX.set(gpuTVIndex, (byte) (normal.getX() * 127)); vw.normY.set(gpuTVIndex, (byte) (normal.getY() * 127)); vw.normZ.set(gpuTVIndex, (byte) (normal.getZ() * 127)); } else { float[] xFrames = new float[numFrames]; float[] yFrames = new float[numFrames]; float[] zFrames = new float[numFrames]; float[] nxFrames = new float[numFrames]; float[] nyFrames = new float[numFrames]; float[] nzFrames = new float[numFrames]; for (int i = 0; i < numFrames; i++) { xFrames[i] = (float) applyScale( triangleAt(i, triangleIndex).getVertices()[vIndex].getPosition().getX()); } xyzAnimator.addFrames(xFrames); for (int i = 0; i < numFrames; i++) { yFrames[i] = (float) applyScale( triangleAt(i, triangleIndex).getVertices()[vIndex].getPosition().getY()); } xyzAnimator.addFrames(yFrames); for (int i = 0; i < numFrames; i++) { zFrames[i] = (float) applyScale( triangleAt(i, triangleIndex).getVertices()[vIndex].getPosition().getZ()); } xyzAnimator.addFrames(zFrames); for (int i = 0; i < numFrames; i++) { nxFrames[i] = (float) Math .rint(triangleAt(i, triangleIndex).getVertices()[vIndex].getNormal().getX() * 127); } xyzAnimator.addFrames(nxFrames); for (int i = 0; i < numFrames; i++) { nyFrames[i] = (float) Math .rint(triangleAt(i, triangleIndex).getVertices()[vIndex].getNormal().getY() * 127); } xyzAnimator.addFrames(nyFrames); for (int i = 0; i < numFrames; i++) { nzFrames[i] = (float) Math .rint(triangleAt(i, triangleIndex).getVertices()[vIndex].getNormal().getZ() * 127); } xyzAnimator.addFrames(nzFrames); } //end else(frames!=1) //////////////// T E X T U R E /////////////////////////// if (td == null) { System.err.println("Stack trace of triangle creation below. NullPointerException follows."); for (StackTraceElement el : t.getCreationStackTrace()) { System.err.println("\tat " + el.getClassName() + "." + el.getMethodName() + "(" + el.getFileName() + ":" + el.getLineNumber() + ")"); } //end for(stackTrace) throw new NullPointerException("Texture for triangle in " + debugName + " intolerably null."); } if (td instanceof Texture) {// Static texture final int sideScalar = ((Texture) td).getSideLength() - 1; if (animateUV && numFrames > 1) {// Animated UV float[] uFrames = new float[numFrames]; float[] vFrames = new float[numFrames]; final WindowAnimator uvAnimator = new WindowAnimator(getFlatTVWindow(), 2, // UV per vertex numFrames, false, getVertexSequencer(timeBetweenFramesMsec, numFrames), new UVXferFunc(gpuTVIndex * UVXferFunc.BACK_STRIDE_LEN)); getModel().addTickableAnimator(uvAnimator); uvAnimator.setDebugName(debugName + ".uvAnimator"); for (int i = 0; i < numFrames; i++) { uFrames[i] = (float) Math.rint(sideScalar * triangleAt(i, triangleIndex).getUV(vIndex).getX()); vFrames[i] = (float) Math .rint(sideScalar * (1 - triangleAt(i, triangleIndex).getUV(vIndex).getY())); } // end for(numFrames) uvAnimator.addFrames(uFrames); uvAnimator.addFrames(vFrames); } else {// end if(animateUV) vw.u.set(gpuTVIndex, (short) Math.rint(sideScalar * t.getUV(vIndex).getX())); vw.v.set(gpuTVIndex, (short) Math.rint(sideScalar * (1 - t.getUV(vIndex).getY()))); } // end if(!animateUV) final int textureID = ((Texture) td).getTexturePage(); vw.textureIDLo.set(gpuTVIndex, (byte) (textureID & 0xFF)); vw.textureIDMid.set(gpuTVIndex, (byte) ((textureID >> 8) & 0xFF)); vw.textureIDHi.set(gpuTVIndex, (byte) ((textureID >> 16) & 0xFF)); } // end if(Texture) if (td instanceof AnimatedTexture) {//Animated texture final AnimatedTexture at = (AnimatedTexture) td; if (animateUV && numFrames > 1) {// Animated UV float[] uFrames = new float[numFrames]; float[] vFrames = new float[numFrames]; final WindowAnimator uvAnimator = new WindowAnimator(getFlatTVWindow(), 2, // UV per vertex numFrames, false, getVertexSequencer(timeBetweenFramesMsec, numFrames), new UVXferFunc(gpuTVIndex * UVXferFunc.BACK_STRIDE_LEN)); getModel().addTickableAnimator(uvAnimator); for (int i = 0; i < numFrames; i++) { final int sideScalar = at.getFrames()[i].getSideLength() - 1; uFrames[i] = (float) Math.rint(sideScalar * triangleAt(i, triangleIndex).getUV(vIndex).getX()); vFrames[i] = (float) Math .rint(sideScalar * (1 - triangleAt(i, triangleIndex).getUV(vIndex).getY())); } // end for(numFrames) uvAnimator.addFrames(uFrames); uvAnimator.addFrames(vFrames); } else {// end if(animateUV) final int sideScalar = at.getFrames()[0].getSideLength() - 1; vw.u.set(gpuTVIndex, (short) Math.rint(sideScalar * t.getUV(vIndex).getX())); vw.v.set(gpuTVIndex, (short) Math.rint(sideScalar * (1 - t.getUV(vIndex).getY()))); } // end if(!animateUV) final TexturePageAnimator texturePageAnimator = new TexturePageAnimator(at, vw, gpuTVIndex); texturePageAnimator.setDebugName(debugName + ".texturePageAnimator"); getModel().addTickableAnimator(texturePageAnimator); } //end if(animated texture) }// end setupVertex private void setupTriangle(final int triangleIndex, final TextureDescription textureDescription, final int[] vertexIndices) throws ExecutionException, InterruptedException { int tIndex = triangleIndex * 3; setupVertex(0, vertexIndices[tIndex], triangleIndex, textureDescription); setupVertex(1, vertexIndices[tIndex + 1], triangleIndex, textureDescription); setupVertex(2, vertexIndices[tIndex + 2], triangleIndex, textureDescription); }//setupTriangle @Override public void finalize() { final MemoryWindow mw = getMemoryWindow(); for (int i = 0; i < triangleVertexIndices.length; i++) { mw.free(triangleVertexIndices[i]); } //end for(triangleVertexIndices) }//end finalize() public void uploadToGPU() { final int nPrimitives = getNumElements(); triangleVertexIndices = new int[nPrimitives * 3]; final TextureDescription[] textureDescriptions = new TextureDescription[nPrimitives]; final MemoryWindow mw = getMemoryWindow(); for (int vIndex = 0; vIndex < nPrimitives * 3; vIndex++) { triangleVertexIndices[vIndex] = mw.create(); } for (int tIndex = 0; tIndex < nPrimitives; tIndex++) { textureDescriptions[tIndex] = triangleAt(0, tIndex).texture; } tr.getThreadManager().submitToGPUMemAccess(new Callable<Void>() { @Override public Void call() throws Exception { for (int tIndex = 0; tIndex < nPrimitives; tIndex++) { setupTriangle(tIndex, textureDescriptions[tIndex], triangleVertexIndices); } finalizePrimitives();//This may break max vertex values return null; }//end Call() }); }// end allocateIndices(...) @Override public int getElementSizeInVec4s() { return 3; } @Override public int getGPUVerticesPerElement() { return 3; } @Override public org.jtrfp.trcl.PrimitiveList.RenderStyle getRenderStyle() { return RenderStyle.OPAQUE; } public Vector3D getMaximumVertexDims() { if (isPrimitivesFinalized()) return cachedMaximumVertexDims; Vector3D result = Vector3D.ZERO; Triangle[][] t = getPrimitives(); for (Triangle[] frame : t) { for (Triangle tri : frame) { for (int i = 0; i < 3; i++) { double v; final Vector3D pos = tri.getVertices()[i].getPosition(); v = pos.getX(); result = result.getX() < v ? new Vector3D(v, result.getY(), result.getZ()) : result; v = pos.getY(); result = result.getY() < v ? new Vector3D(result.getX(), v, result.getZ()) : result; v = pos.getZ(); result = result.getZ() < v ? new Vector3D(result.getX(), result.getY(), v) : result; } // end for(vertex) } // end for(triangle) } // end for(triangles) return result; }// end getMaximumVertexDims() public Vector3D getMinimumVertexDims() { if (isPrimitivesFinalized()) return cachedMinimumVertexDims; Vector3D result = new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); Triangle[][] t = getPrimitives(); for (Triangle[] frame : t) { for (Triangle tri : frame) { for (int i = 0; i < 3; i++) { double v; final Vector3D pos = tri.getVertices()[i].getPosition(); v = pos.getX(); result = result.getX() > v ? new Vector3D(v, result.getY(), result.getZ()) : result; v = pos.getY(); result = result.getY() > v ? new Vector3D(result.getX(), v, result.getZ()) : result; v = pos.getZ(); result = result.getZ() > v ? new Vector3D(result.getX(), result.getY(), v) : result; } // end for(vertex) } // end for(triangle) } // end for(triangles) return result; }// end getMaximumVertexDims() public double getMaximumVertexValue() { if (isPrimitivesFinalized()) return cachedMaximumVertexValue; double result = 0; Triangle[][] t = getPrimitives(); for (Triangle[] frame : t) { for (Triangle tri : frame) { for (int i = 0; i < 3; i++) { double v; final Vector3D pos = tri.getVertices()[i].getPosition(); v = Math.abs(pos.getX()); result = result < v ? v : result; v = Math.abs(pos.getY()); result = result < v ? v : result; v = Math.abs(pos.getZ()); result = result < v ? v : result; } // end for(vertex) } // end for(triangle) } // end for(triangles) return result; }// end getMaximumVertexValue() /** * @return the flatTVWindow */ private TriangleVertex2FlatDoubleWindow getFlatTVWindow() { if (flatTVWindow == null) flatTVWindow = new TriangleVertex2FlatDoubleWindow((TriangleVertexWindow) this.getMemoryWindow()); return flatTVWindow; } @Override protected void finalizePrimitives() { cachedMinimumVertexDims = getMinimumVertexDims(); cachedMaximumVertexDims = getMaximumVertexDims(); cachedMaximumVertexValue = getMaximumVertexValue(); super.finalizePrimitives(); } @Override public byte getPrimitiveRenderMode() { return PrimitiveRenderMode.RENDER_MODE_TRIANGLES; } @Override public int getNumMemoryWindowIndicesPerElement() { return 3; } }// end SingleTextureTriangleList