import java.awt.BorderLayout; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.media.j3d.Appearance; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.Geometry; import javax.media.j3d.GeometryArray; import javax.media.j3d.GeometryUpdater; import javax.media.j3d.IndexedTriangleArray; import javax.media.j3d.J3DGraphics2D; import javax.media.j3d.Locale; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.RenderingAttributes; import javax.swing.JFrame; import javax.swing.JPanel; import javax.vecmath.Color3f; import org.apache.commons.math3.linear.MatrixUtils; import org.apache.commons.math3.linear.RealMatrix; import org.apache.commons.math3.util.FastMath; import com.sun.j3d.utils.geometry.Text2D; import com.sun.j3d.utils.universe.ConfiguredUniverse; import com.sun.j3d.utils.universe.SimpleUniverse; public class notusingtransform3D { public static void main(String[] args) { System.setProperty("sun.awt.noerasebackground", "true"); new notusingtransform3D(); } float x = 0, y = 0, z = 0, dX = 1, dY = 1, dZ = 1; double rotX = 0, rotY = 0, rotZ = 0, dRotX = 0.05, dRotY = 0.05, dRotZ = 0.05; double zoomScaleX = 0, zoomScaleY = 0; IndexedTriangleArray[] cube_one, cube_two, cube_three; String legendText = "a"; float[] arrayOfRefs_one, arrayOfRefs_two, arrayOfRefs_three; static int[] cube_idx = { // from // https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_05 // front 0, 1, 2, 2, 3, 0, // right 1, 5, 6, 6, 2, 1, // back 7, 6, 5, 5, 4, 7, // left 4, 0, 3, 3, 7, 4, // bottom 4, 5, 1, 1, 0, 4, // top 3, 2, 6, 6, 7, 3 }; static float[] cube_colors = { // front colors 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, // back colors 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }; float[][] cube1_pt = { { -2, +0, -10 }, { -1, +0, -10 }, { -1, +1, -10 }, { -2, +1, -10 }, { -2, +0, -11 }, { -1, +0, -11 }, { -1, +1, -11 }, { -2, +1, -11 } }; float[][] cube2_pt = { { -3, +0, -14 }, { -1, +0, -14 }, { -3, +2, -14 }, { -1, +2, -14 }, { -3, +2, -12 }, { -3, +0, -12 }, { -1, +2, -12 }, { -1, +0, -12 }, }; float[][] cube3_pt = { { -2, +0, -15 }, { -2, +3, -15 }, { -1, +0, -15 }, { -1, +3, -15 }, { -2, +3, -17 }, { -1, +3, -17 }, { -2, +0, -17 }, { -1, +0, -17 }, }; public RealMatrix translationM(double tx, double ty, double tz) { double[][] matrixData = { { 1, 0, 0, tx }, { 0, 1, 0, ty }, { 0, 0, 1, tz }, { 0, 0, 0, 1 } }; RealMatrix mat = MatrixUtils.createRealMatrix(matrixData); return mat; } public RealMatrix rotateX(double fi) { double[][] matrixData = { { 1, 0, 0, 0 }, { 0, FastMath.cos(fi), -FastMath.sin(fi), 0 }, { 0, FastMath.sin(fi), FastMath.cos(fi), 0 }, { 0, 0, 0, 1 } }; RealMatrix mat = MatrixUtils.createRealMatrix(matrixData); return mat; } public RealMatrix rotateY(double fi) { double[][] matrixData = { { FastMath.cos(fi), 0, FastMath.sin(fi), 0 }, { 0, 1, 0, 0 }, { -FastMath.sin(fi), 0, FastMath.cos(fi), 0 }, { 0, 0, 0, 1 } }; RealMatrix mat = MatrixUtils.createRealMatrix(matrixData); return mat; } public RealMatrix rotateZ(double fi) { double[][] matrixData = { { FastMath.cos(fi), -FastMath.sin(fi), 0, 0 }, { FastMath.sin(fi), FastMath.cos(fi), 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; RealMatrix mat = MatrixUtils.createRealMatrix(matrixData); return mat; } public RealMatrix scale(double scaleX, double scaleY, double scaleZ) { double[][] matrixData = { { scaleX, 0, 0, 0 }, { 0, scaleY, 0, 0 }, { 0, 0, scaleZ, 0 }, { 0, 0, 0, 1 } }; RealMatrix mat = MatrixUtils.createRealMatrix(matrixData); return mat; } public RealMatrix quaternion(float x, float y, float z, float w) { double[][] quatData = { { x }, { y }, { z }, { w } }; RealMatrix quat = MatrixUtils.createRealMatrix(quatData); return quat; } public void translateArray(float[] cube, float deltaX, float deltaY, float deltaZ) { RealMatrix quat; for (int i = 0; i < 8; i++) { quat = quaternion(cube[i * 3 + 0], cube[i * 3 + 1], cube[i * 3 + 2], 1); quat = quat.preMultiply(translationM(deltaX, deltaY, deltaZ)); cube[i * 3 + 0] = (float) quat.getEntry(0, 0); cube[i * 3 + 1] = (float) quat.getEntry(1, 0); cube[i * 3 + 2] = (float) quat.getEntry(2, 0); } } public void rotateArrayX(float[] cube, double fi) { RealMatrix quat; for (int i = 0; i < 8; i++) { quat = quaternion(cube[i * 3 + 0], cube[i * 3 + 1], cube[i * 3 + 2], 1); quat = quat.preMultiply(rotateX(fi)); cube[i * 3 + 0] = (float) quat.getEntry(0, 0); cube[i * 3 + 1] = (float) quat.getEntry(1, 0); cube[i * 3 + 2] = (float) quat.getEntry(2, 0); } } public void rotateArrayY(float[] cube, double fi) { RealMatrix quat; for (int i = 0; i < 8; i++) { quat = quaternion(cube[i * 3 + 0], cube[i * 3 + 1], cube[i * 3 + 2], 1); quat = quat.preMultiply(rotateY(fi)); cube[i * 3 + 0] = (float) quat.getEntry(0, 0); cube[i * 3 + 1] = (float) quat.getEntry(1, 0); cube[i * 3 + 2] = (float) quat.getEntry(2, 0); } } public void rotateArrayZ(float[] cube, double fi) { RealMatrix quat; for (int i = 0; i < 8; i++) { quat = quaternion(cube[i * 3 + 0], cube[i * 3 + 1], cube[i * 3 + 2], 1); quat = quat.preMultiply(rotateZ(fi)); cube[i * 3 + 0] = (float) quat.getEntry(0, 0); cube[i * 3 + 1] = (float) quat.getEntry(1, 0); cube[i * 3 + 2] = (float) quat.getEntry(2, 0); } } public void zoom(float[] cube, double scaleX, double scaleY, double scaleZ) { RealMatrix quat; for (int i = 0; i < 8; i++) { quat = quaternion(cube[i * 3 + 0], cube[i * 3 + 1], cube[i * 3 + 2], 1); quat = quat.preMultiply(scale(scaleX, scaleY, scaleZ)); cube[i * 3 + 0] = (float) quat.getEntry(0, 0); cube[i * 3 + 1] = (float) quat.getEntry(1, 0); cube[i * 3 + 2] = (float) quat.getEntry(2, 0); } } public static IndexedTriangleArray[] ccr1(float cube_pts[][]) { float[] flat_pts = flatten(cube_pts); IndexedTriangleArray[] dozenOfSeparateTriangles = new IndexedTriangleArray[12]; for (int i = 0; i < dozenOfSeparateTriangles.length; i++) { IndexedTriangleArray triangleGeometry = new IndexedTriangleArray(3, GeometryArray.COORDINATES | GeometryArray.COLOR_3 | GeometryArray.BY_REFERENCE, 3); triangleGeometry.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE); ccr2(triangleGeometry, flat_pts, i); dozenOfSeparateTriangles[i] = triangleGeometry; } return dozenOfSeparateTriangles; } public static float[] flatten(float cube_pts[][]) { float[] flat_pts = new float[cube_pts.length * 3]; for (int j = 0; j < cube_pts.length; j++) { flat_pts[j * 3 + 0] = cube_pts[j][0]; flat_pts[j * 3 + 1] = cube_pts[j][1]; flat_pts[j * 3 + 2] = cube_pts[j][2]; } return flat_pts; } public static void ccr2(IndexedTriangleArray geom, float flat_pts[], int i) { geom.setCoordRefFloat(flat_pts); geom.setCoordinateIndices(0, subsetOf3(cube_idx, i * 3)); geom.setColorRefFloat(cube_colors); geom.setColorIndices(0, subsetOf3(cube_idx, i * 3)); } public static int[] subsetOf3(int[] cubeIndices, int startIndex) { int[] subset = new int[3]; subset[0] = cubeIndices[startIndex]; subset[1] = cubeIndices[startIndex + 1]; subset[2] = cubeIndices[startIndex + 2]; return subset; } public void sortZAscending(float[][] array) { for (int i = 0; i < array.length; i++) { } } static Appearance app; public notusingtransform3D() { Canvas3D canv = new Canvas3D(ConfiguredUniverse.getPreferredConfiguration()) { // this call is called by the renderer thread and should do a bunch of set // appearance and draw geometry calls public void renderField(int fieldDesc) { getGraphicsContext3D().setAppearance(app); for (int i = 0; i < 12; i++) { getGraphicsContext3D().draw(cube_one[i]); } // note we might keep using the same appearance, but we could easily swap to // something new; with a texture for example // getGraphicsContext3D().draw(cube_two); // notice that the cube order is now fixed to the order of these draw calls, // nothing in the z order will alter that. } // for fun we can now draw on the rendered image and show details @Override public void postRender() { J3DGraphics2D g = getGraphics2D(); // draw a cross hair g.drawLine((this.getWidth() / 2) - 5, (this.getHeight() / 2), (this.getWidth() / 2) + 5, (this.getHeight() / 2)); g.drawLine((this.getWidth() / 2), (this.getHeight() / 2) - 5, (this.getWidth() / 2), (this.getHeight() / 2) + 5); g.drawString("This is an example String " + System.currentTimeMillis(), 50, 20); // etc e.g. // g.drawImage(getBufferedImage(), 10, 50, null); g.flush(false); } }; SimpleUniverse uni = new SimpleUniverse(canv); BranchGroup group = new BranchGroup(); app = new Appearance(); Locale loc = new Locale(uni); cube_one = ccr1(cube1_pt); arrayOfRefs_one = cube_one[0].getCoordRefFloat(); // cube_two = ccr1(cube2_pt); // cube_twoData = cube_two.getCoordRefFloat(); // cube_three = ccr1(cube3_pt); // cube_threeData = cube_three.getCoordRefFloat(); // shapes are no longer needed, we are using the appearance and geometries // directly in the immediate render method // Shape3D cube1 = new Shape3D(cube_one, app); // creates Shape3D from Geometry // and Appearance // Shape3D cube2 = new Shape3D(cube_two, app); // Shape3D cube3 = new Shape3D(cube_three, app); Text2D legend = new Text2D(legendText, new Color3f(1, 1, 1), "Arial", 12, 0); // turn off the Z buffer RenderingAttributes ra = new RenderingAttributes(); ra.setDepthBufferEnable(false); app.setRenderingAttributes(ra); // turn off front facing detection (render either side of a triangle) PolygonAttributes pa = new PolygonAttributes(); pa.setCullFace(PolygonAttributes.CULL_NONE); app.setPolygonAttributes(pa); // group.addChild(cube1); // adding Shape3D to BranchGroup // group.addChild(cube2); // group.addChild(cube3); // lets keep rendering the legend normally group.addChild(legend); loc.addBranchGraph(group); // uni.getViewingPlatform().setNominalViewingTransform(); uni.getCanvas().addKeyListener(new KeyListener() { private void flush() { // https://docs.oracle.com/cd/E17802_01/j2se/javase/technologies/desktop/java3d/forDevelopers/j3dguide/Immediate.doc.html // mixed mode optimizes and waits to be told that the scene has changed, this // call tells it the scene needs to be redrawn, or we could add a behavior canv.getGraphicsContext3D().flush(false); } @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_A) { x -= dX; System.out.println("x = " + x); cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { translateArray(arrayOfRefs_one, dX, 0, 0); legendText = "x = " + x; flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_D) { x += dX; System.out.println("x = " + x); cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { translateArray(arrayOfRefs_one, -dX, 0, 0); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_Q) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { translateArray(arrayOfRefs_one, 0, dY, 0); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_E) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { translateArray(arrayOfRefs_one, 0, -dY, 0); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_W) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { translateArray(arrayOfRefs_one, 0, 0, dZ); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_S) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { translateArray(arrayOfRefs_one, 0, 0, -dZ); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_DOWN) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { rotateArrayX(arrayOfRefs_one, dRotX); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_UP) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { rotateArrayX(arrayOfRefs_one, -dRotX); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_RIGHT) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { rotateArrayY(arrayOfRefs_one, dRotY); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_LEFT) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { rotateArrayY(arrayOfRefs_one, -dRotY); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_C) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { rotateArrayZ(arrayOfRefs_one, dRotZ); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_V) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { rotateArrayZ(arrayOfRefs_one, -dRotZ); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_Z) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { zoom(arrayOfRefs_one, 0.95, 0.95, 1); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } if (e.getKeyCode() == KeyEvent.VK_X) { cube_one[0].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { zoom(arrayOfRefs_one, 1.05, 1.05, 1); flush(); } }); for (int i = 1; i < cube_one.length; i++) { cube_one[i].updateData(new GeometryUpdater() { @Override public void updateData(Geometry geom) { } }); } } } }); // make it visible, because we hand in a canvas3D we also have to get the canvas // (AWT object) on to the screen now JFrame j3dJFrames = new JFrame(); j3dJFrames.setDefaultCloseOperation(j3dJFrames.EXIT_ON_CLOSE); j3dJFrames.getContentPane().setLayout(new BorderLayout()); j3dJFrames.setSize(256, 256); // Put the Canvas3D into a JPanel. JPanel j3dJPanels = new JPanel(); j3dJPanels.setLayout(new BorderLayout()); j3dJPanels.add("Center", canv); j3dJFrames.getContentPane().add("Center", j3dJPanels); j3dJFrames.setVisible(true); } }