package com.codebynumber.java3d.breakout; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import javax.imageio.ImageIO; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.swing.JFrame; import javax.swing.JPanel; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; /** * Main class for breakout demo. The gist of it is this: * Create a simple universe, and add the Breakout objects (more on that below). * Add some lighting and set the camera. * Add generic mouse control for changing camera angle. * Show the world. * * The interesting part are the physical objects. The side bumpers are * cylinders and the back and front are boards. They reverse the direction * of one of the vector's components on collision. * * The Bricks are individual Box objects, placed in a grid. On collision, * they reverse the ball's direction and disappear. * * The behavior that deals with collision is called Collision. It wakes up on * CollisionEntry only, and holds onto an instance of the ball to change its * direction. * * And the ball is a Sphere whose TransformGroup is translated according to the * TimeBehavior object. This behavior moves it along its path by a move rate calculated * based on the elapsed time since the last move. This approach clearly has issues * when the behavior doesn't get to update frequently, as it will jump the ball * across the screen, and likely miss a collision by moving it to the opposite side * of the object. * * Improvements in later version: * - A paddle for user interaction * - Score board for tries left when the user misses the ball * - Better collision reaction - right now Box objects (the bricks) won't send * a ball to the side if it hits on the side edge. * - More reliable collision detection (based on interpolating points to ensure * ball won't jump past an object * - Textures to map onto bricks and bumpers, pretty background image * * @author codebynumber.com * */ public class Breakout extends JFrame { public static final BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0); public static final double length = 10.0; public static final double width = 7.0; public static final Color3f green = new Color3f(0.4f, 0.8f, 0.4f); public static final Color3f black = new Color3f(0.0f, 0.0f, 0.0f); public static final Color3f white = new Color3f(1.0f, 1.0f, 1.0f); private SimpleUniverse univ; private BranchGroup scene; private JPanel drawingPanel; private static final Point3d USERPOSN = new Point3d(0, 5, 2); /** * Initialize components and create scene. */ public Breakout() { initComponents(); Canvas3D c = createUniverse(); drawingPanel.add(c, java.awt.BorderLayout.CENTER); scene = createSceneGraph(); lightScene(); orbitControls(c); initUserPosition(); scene.compile(); univ.addBranchGraph(scene); } /** * * @return */ public BranchGroup createSceneGraph() { BranchGroup objRoot = new BranchGroup(); TransformGroup objScale = new TransformGroup(); Transform3D t3d = new Transform3D(); t3d.setScale(0.4); objScale.setTransform(t3d); objRoot.addChild(objScale); Ball ball = new Ball(); objScale.addChild(ball); GameBoard board = new GameBoard(); objScale.addChild(board.createBumper(GameBoard.LEFT, ball)); objScale.addChild(board.createBumper(GameBoard.RIGHT, ball)); Group blockGroup = new Bricks(4, ball); objScale.addChild(blockGroup); objScale.addChild(board.createFrontBorder(ball)); objScale.addChild(board.createRearBorder(ball)); TimeBehavior timer = new TimeBehavior(100, ball); timer.setSchedulingBounds(bounds); objRoot.addChild(timer); Background bg = null; ImageComponent2D image = null; try { ClassLoader cl = this.getClass().getClassLoader(); File f = new File(cl.getResource("images/thetaspace.jpg").toURI()); image = new ImageComponent2D(ImageComponent2D.FORMAT_RGB, ImageIO.read(f)); bg = new Background(image); } catch (IOException e) { // Create background, sort of blue-ish bg = new Background(new Color3f(0.3f, 0.3f, 0.3f)); } catch(URISyntaxException ex) { // Create background, sort of blue-ish bg = new Background(new Color3f(0.3f, 0.3f, 0.3f)); } catch (NullPointerException ne) { // Create background, sort of blue-ish bg = new Background(new Color3f(0.3f, 0.3f, 0.3f)); } bg.setApplicationBounds(bounds); objScale.addChild(bg); return objRoot; } /** * Set up simple universe * @return Canvas3D object */ private Canvas3D createUniverse() { GraphicsConfiguration config = SimpleUniverse .getPreferredConfiguration(); Canvas3D c = new Canvas3D(config); univ = new SimpleUniverse(c); univ.getViewingPlatform().setNominalViewingTransform(); univ.getViewer().getView().setMinimumFrameCycleTime(5); return c; } /** * Set up Swing GUI components. */ private void initComponents() { drawingPanel = new JPanel(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle("Breakout - codebynumber.com"); drawingPanel.setLayout(new BorderLayout()); drawingPanel.setPreferredSize(new Dimension(700, 700)); getContentPane().add(drawingPanel, BorderLayout.CENTER); pack(); } /** * Add ambient and directional light to the scene. */ private void lightScene() { Color3f white = new Color3f(1.0f, 1.0f, 1.0f); // Set up the ambient light AmbientLight ambientLightNode = new AmbientLight(white); ambientLightNode.setInfluencingBounds(bounds); scene.addChild(ambientLightNode); // Set up the directional lights Vector3f light2Direction = new Vector3f(1.0f, -1.0f, 1.0f); // right, down, forwards DirectionalLight light2 = new DirectionalLight(white, light2Direction); light2.setInfluencingBounds(bounds); scene.addChild(light2); } /** * OrbitBehaviour allows the user to rotate around the scene, and to zoom in * and out. * * @param c */ private void orbitControls(Canvas3D c) { OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL); orbit.setSchedulingBounds(bounds); ViewingPlatform vp = univ.getViewingPlatform(); vp.setViewPlatformBehavior(orbit); } /** * Set the user's initial viewpoint using lookAt() */ private void initUserPosition() { ViewingPlatform vp = univ.getViewingPlatform(); TransformGroup steerTG = vp.getViewPlatformTransform(); Transform3D t3d = new Transform3D(); steerTG.getTransform(t3d); t3d.lookAt(USERPOSN, new Point3d(0, 0, -2.0), new Vector3d(0, 1, 0)); t3d.invert(); steerTG.setTransform(t3d); } /** * * @param args */ public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new Breakout().setVisible(true); } }); } }