diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/tesseract/TesseractUI.java | 240 | ||||
-rw-r--r-- | src/tesseract/World.java | 154 |
2 files changed, 394 insertions, 0 deletions
diff --git a/src/tesseract/TesseractUI.java b/src/tesseract/TesseractUI.java new file mode 100644 index 0000000..924f859 --- /dev/null +++ b/src/tesseract/TesseractUI.java @@ -0,0 +1,240 @@ +package tesseract; + +import java.awt.GraphicsConfiguration; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; + +import javax.media.j3d.BoundingBox; +import javax.media.j3d.Canvas3D; +import javax.media.j3d.Transform3D; +import javax.media.j3d.TransformGroup; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.Timer; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3f; + +import com.sun.j3d.utils.universe.SimpleUniverse; + +/** + * This class is the main UI for the Tesseract Project. + * + * @author Jesse Morgan + */ +public class TesseractUI extends JFrame { + + /** + * Generated serialVersionUID. + */ + private static final long serialVersionUID = 4097744746899308736L; + + /** + * Update Rate. + */ + private static final int UPDATE_RATE = 30; + + /** + * + */ + private static final double UNIT = 1; + + /** + * A reference to the world. + */ + private World myWorld; + + /** + * The Canvas. + */ + private Canvas3D myCanvas; + + /** + * Camera TransformGroup. + */ + private TransformGroup cameraTG; + + /** + * Camera position information. + */ + private double cameraXRotation, cameraYRotation, cameraDistance; + + /** + * UI Constructor. + */ + public TesseractUI() { + super("Tesseract Project"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + myWorld = new World( + new BoundingBox(new Point3d(-UNIT / 2, -UNIT / 2, -UNIT / 2), + new Point3d(UNIT / 2, UNIT / 2, UNIT / 2))); + + createMenu(); + setupCanvas(); + pack(); + + // Maximize the windows + if (Toolkit.getDefaultToolkit(). + isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { + setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH); + } + } + + /** + * Create the menu. + */ + private void createMenu() { + JMenuBar menuBar = new JMenuBar(); + + JMenu simulationMenu = new JMenu("Simulation"); + menuBar.add(simulationMenu); + + /* + JCheckBoxMenuItem cMenuItem = new JCheckBoxMenuItem("Enable Particle Emitters", enableEmitters); + cMenuItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + enableEmitters = !enableEmitters; + } + }); + menu.add(cMenuItem); + + for (int i = 0; i < forces.length; i++) { + cMenuItem = new JCheckBoxMenuItem(forces[i].toString(), activeForces[i]); + cMenuItem.setActionCommand(i + ""); + cMenuItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int index = Integer.parseInt(e.getActionCommand()); + activeForces[index] = !activeForces[index]; + } + }); + menu.add(cMenuItem); + } + */ + + // Exit Menu Item + JMenuItem exit = new JMenuItem("Exit"); + exit.addActionListener(new ActionListener() { + public void actionPerformed(final ActionEvent e) { + // TODO: I feel this is the wrong way of exiting... + System.exit(0); + } + }); + simulationMenu.add(exit); + + setJMenuBar(menuBar); + } + + /** + * Create and show the UI. + */ + private void setupCanvas() { + GraphicsConfiguration config + = SimpleUniverse.getPreferredConfiguration(); + + myCanvas = new Canvas3D(config); + + SimpleUniverse universe = new SimpleUniverse(myCanvas); + universe.getViewer().getView().setSceneAntialiasingEnable(true); + + // Set the camera + cameraTG = universe.getViewingPlatform().getViewPlatformTransform(); + cameraDistance = 3 * UNIT; + updateCamera(); + + // Add the scene BG. + universe.addBranchGraph(myWorld.getScene()); + + // Add the canvas to the frame. + add(myCanvas); + + + // Event listener time + myCanvas.addMouseMotionListener(new MouseMotionAdapter() { + private MouseEvent lastDragEvent = null; + + public void mouseDragged(final MouseEvent e) { + if (lastDragEvent != null) { + cameraXRotation += + Math.toRadians(e.getY() - lastDragEvent.getY()) / 3; + + if (cameraXRotation > Math.PI / 2) { + cameraXRotation = Math.PI / 2; + + } else if (cameraXRotation < -Math.PI / 2) { + cameraXRotation = -Math.PI / 2; + } + + cameraYRotation += + Math.toRadians(e.getX() - lastDragEvent.getX()) / 3; + + updateCamera(); + } + + lastDragEvent = e; + } + + public void mouseMoved(final MouseEvent e) { + lastDragEvent = null; + } + }); + + myCanvas.addMouseWheelListener(new MouseWheelListener() { + public void mouseWheelMoved(final MouseWheelEvent e) { + if (e.getWheelRotation() > 0) { + cameraDistance *= 1.05; + + } else if (e.getWheelRotation() < 0) { + cameraDistance *= 0.95; + } + + updateCamera(); + } + }); + + // Setup the timer. + new Timer(1000 / UPDATE_RATE, new ActionListener() { + public void actionPerformed(final ActionEvent e) { + myCanvas.stopRenderer(); + myWorld.tick(); + myCanvas.startRenderer(); + } + }).start(); + + } + + /** + * Method to update the camera. + */ + private void updateCamera() { + Transform3D camera3D = new Transform3D(); + camera3D.setTranslation(new Vector3f(0f, 0f, (float) -cameraDistance)); + Transform3D tmp = new Transform3D(); + tmp.rotX(cameraXRotation); + camera3D.mul(tmp); + tmp.rotY(cameraYRotation); + camera3D.mul(tmp); + camera3D.invert(); + cameraTG.setTransform(camera3D); + } + + /** + * Start up the program. + * + * @param args Unused commandline arguments. + */ + public static void main(final String[] args) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + new TesseractUI().setVisible(true); + } + }); + } +} diff --git a/src/tesseract/World.java b/src/tesseract/World.java new file mode 100644 index 0000000..e7555a5 --- /dev/null +++ b/src/tesseract/World.java @@ -0,0 +1,154 @@ +package tesseract; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.media.j3d.BoundingBox; +import javax.media.j3d.BoundingSphere; +import javax.media.j3d.BranchGroup; +import javax.media.j3d.DirectionalLight; +import javax.media.j3d.IndexedLineArray; +import javax.media.j3d.Light; +import javax.media.j3d.Node; +import javax.media.j3d.Shape3D; +import javax.vecmath.Color3f; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3f; + +public class World { + /** + * Root element of the world. + */ + private BranchGroup myScene; + + /** + * Bounding box of the world. + */ + private BoundingBox myVirtualWorldBounds; + + /** + * A list of the objects in the world. + */ + private List<Particle> myObjects; + + /** + * A list of the forces in the world. + */ + private List<Force> myForces; + + //private List<ParticleEmitter> emitters; + //private boolean enableEmitters; + + // A list of all the particles in the world + //private List<Particle> particles; + + // A list of all the objects particles may collide with + //private List<ParticleCollidableObject> collidables; + + // Available forces + //private static final ParticleForceGenerator forces[] = {new Gravity(0.4f)}; + //private boolean activeForces[]; + + // Number of state updates per second + //private final int UPDATE_RATE = 30; + + public World(final BoundingBox bounds) { + myVirtualWorldBounds = bounds; + + myForces = new LinkedList<Force>(); + myObjects = new LinkedList<Particle>(); + + // TODO: Should this go here? + myScene = new BranchGroup(); + myScene.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND); + myScene.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE); + myScene.addChild(createVirtualWorldBoundsShape()); + addLights(); + addEmitters(); + addCollidableObjects(); + myScene.compile(); + } + + + + private Node createVirtualWorldBoundsShape() { + Point3d lower = new Point3d(); + Point3d upper = new Point3d(); + virtualWorldBounds.getLower(lower); + virtualWorldBounds.getUpper(upper); + + double coordinates[] = {lower.x, lower.y, lower.z, upper.x, lower.y, lower.z, + upper.x, lower.y, upper.z, lower.x, lower.y, upper.z, + lower.x, upper.y, lower.z, upper.x, upper.y, lower.z, + upper.x, upper.y, upper.z, lower.x, upper.y, upper.z}; + int coordinateIndices[] = {0, 1, 1, 2, 2, 3, 3, 0, + 4, 5, 5, 6, 6, 7, 7, 4, + 0, 4, 1, 5, 2, 6, 3, 7}; + + IndexedLineArray geometry = new IndexedLineArray(coordinates.length / 3, IndexedLineArray.COORDINATES, coordinateIndices.length); + geometry.setCoordinates(0, coordinates); + geometry.setCoordinateIndices(0, coordinateIndices); + + return new Shape3D(geometry); + } + + private void addLights() { + Light light = new DirectionalLight(new Color3f(1f, 1f, 1f), new Vector3f(-1f, -1f, -1f)); + light.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0), 10)); + scene.addChild(light); + light = new DirectionalLight(new Color3f(0.3f, 0.1f, 0.1f), new Vector3f(1f, 0f, 0f)); + light.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0), 10)); + scene.addChild(light); + } + + private void addEmitters() { + emitters.add(new ColorShiftParticleEmitter(new Vector3f(-0.2f, 0.5f, 0))); + } + + private void addCollidableObjects() { + collidables.add(new Circle(0.25f, new Vector3f(-0.2f, 0.3f, 0), new Vector3f(0.6f, 1, 0))); + collidables.add(new Circle(0.25f, new Vector3f(0.2f, 0.1f, 0), new Vector3f(-0.6f, 1, 0))); + collidables.add(new Circle(0.25f, new Vector3f(-0.2f, -0.1f, 0), new Vector3f(0.6f, 1, 0))); + collidables.add(new Circle(0.25f, new Vector3f(0.2f, -0.3f, 0), new Vector3f(-0.6f, 1, 0))); + +// collidables.add(new Circle(0.25f, new Vector3f(0.15f, 0, 0), new Vector3f(0.02f, 1, 0))); +// collidables.add(new MathMesh(new Vector3f(0.13f, 0, 0), 151)); + for (ParticleCollidableObject object : collidables) + scene.addChild(object.getGroup()); + } + + public void tick() { + for (Iterator<Particle> itr = particles.iterator(); itr.hasNext();) { + Particle particle = itr.next(); + for (int i = 0; i < forces.length; i++) + if (activeForces[i]) + forces[i].applyForceTo(particle); + particle.updateState(1f / UPDATE_RATE); + for (ParticleCollidableObject object : collidables) { + CollisionInfo ci = object.calculateCollision(particle); + if (ci != null) + object.resolveCollision(particle, ci); + } + if (!virtualWorldBounds.intersect(new Point3d(particle.getPosition()))) { + particle.detach(); + itr.remove(); + } + } + if (!enableEmitters) + return; + for (ParticleEmitter emitter : emitters) { + List<Particle> children = emitter.tick(); + for (Particle particle : children) + scene.addChild(particle.getGroup()); + particles.addAll(children); + } + } + + /** + * @return the root BG of the scene. + */ + public BranchGroup getScene() { + return myScene; + } +} |