The Back Facing CameraThe back facing camera is a second ViewPlatform node, which requires its own Canvas3D object so the view can be displayed. The minimal subgraph for the camera is shown in Figure 25-11. Figure 25-11. The minimal view branch graphAttributes related to the user's head are defined in PhysicalBody, such as the position of her eyes relative to the scene. PhysicalEnvironment specifies the environment in which the view will be generated, such as whether head-tracking or wall displays are being utilized. I'm using a standard PC configuration, so these objects can be created with their default settings. The ViewPlatform represents the camera's viewpoint, and the transformGroup above it positions the viewpoint and is the connection point between the camera and the top-level BranchGroup in the main scene. The SecondViewPanel class creates a subgraph such as the one in Figure 25-11, and makes its TRansformGroup, called canvas2TG, visible via the method getCamera2TG( ). The class's other role is to embed its Canvas3D object inside a JPanel so that it can be easily inserted into the Swing-based GUI. SecondViewPanel is a subclass of JPanel, so the attachment of the Canvas3D object is carried out in its constructor: // globals private static final int PWIDTH = 256; // size of panel private static final int PHEIGHT = 256; private MazeManager mazeMan; private TransformGroup camera2TG; // TG for the camera; will be linked to the main scene public SecondViewPanel(MazeManager mm) { mazeMan = mm; setLayout(new BorderLayout( )); setOpaque(false); setPreferredSize( new Dimension(PWIDTH, PHEIGHT)); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration( ); Canvas3D canvas3D = new Canvas3D(config); add("Center", canvas3D); // add Canvas3D to the JPanel initView(canvas3D); } initView( ) constructs the subgraph: private void initView(Canvas3D canvas3D) { ViewPlatform vp = new ViewPlatform( ); // create a View node for the ViewPlatform // it has the same attributes as the forward facing camera View View view = new View( ); view.setPhysicalBody( new PhysicalBody( ) ); view.setPhysicalEnvironment( new PhysicalEnvironment( ) ); view.addCanvas3D(canvas3D); view.attachViewPlatform(vp); // attach the ViewPlatform view.setFieldOfView( Math.toRadians(90.0)); view.setBackClipDistance(20); view.setFrontClipDistance(0.05); camera2TG = setCameraPosition( ); camera2TG.addChild(vp); // add ViewPlatform to camera TG } The View attributes (FOV, clip distances) are the same as in the forward facing camera. No spotlight is added to the subgraph, causing the view behind the user to be substantially darker than the view ahead. setCameraPosition( ) sets up the camera's location by creating the top-level transformGroup and positioning it with lookAt( ): private TransformGroup setCameraPosition( ) { Vector3d startVec = mazeMan.getMazeStartPosn( ); Transform3D t3d = new Transform3D( ); // args are: viewer posn, where looking, up direction t3d.lookAt( new Point3d(startVec.x, startVec.y, startVec.z), new Point3d(startVec.x, startVec.y, -10), //any -z value will do new Vector3d(0,1,0)); t3d.invert( ); TransformGroup tg = new TransformGroup(t3d); tg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); // moveable tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); return tg; } lookAt( ) places the camera at the starting position in the maze, pointing along the negative z-axis. An implicit assumption is that the start is somewhere in the positive quadrant of the XZ plane so startVec.z cannot be less than 0. Since the camera's default viewpoint faces into the scene, the transform created with lookAt( ) must be inverted to have the necessary effect. This back facing camera is now positioned and oriented. However, it has to move at runtime as the user moves through the maze. The forward facing and back facing cameras are controlled by KeyBehavior. For the back facing camera, KeyBehavior affects its TRansformGroup (called tg in setCameraPosition( )), so the node's capability bits are set to allow changes Adding the Second View to the GUIThe creation of the SecondViewPanel and its addition to the GUI is carried out in Maze3D. Maze3D passes a reference to the camera's top-level transformGroup into WrapMaze3D: public Maze3D(String args[]) { // check the args[] array... MazeManager mm = new MazeManager(fnm); BirdsEye be = new BirdsEye(mm); // bird's eye view over the maze SecondViewPanel secondVP = new SecondViewPanel(mm); WrapMaze3D w3d = new WrapMaze3D(mm, be, secondVP.getCamera2TG( ) ); // GUI creation code ... ... Box vertBox = Box.createVerticalBox( ); vertBox.add( secondVP ); // add back-facing camera pane c.add(vertBox); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); pack( ); setResizable(false); // fixed size display show( ); } The transformGroup, called camera2TG in WrapMaze3D, is added to the scene by the createSceneGraph( ) method: sceneBG.addChild( camera2TG ); |