summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/tesseract/TesseractUI.java240
-rw-r--r--src/tesseract/World.java154
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;
+ }
+}