Moving the Viewpoint


Viewpoint Creation

Various viewpoint manipulations are carried out by WrapMaze3D's prepareViewPoint( ):


Positioning and orientation

The viewpoint's position is specified using the maze's starting point (represented by an s in the maze plan). The viewpoint is rotated to face along the positive z-axis. There's no reason for doing this, except to show how I can change the default orientation (which is along the negative z-axis).


Movement control

A KeyBehavior object is connected to the viewpoint so key presses can move it during execution.


A Spotlight

The maze is cast into stygian gloom, so a Spotlight node is connected to the viewpoint to help the users see what they're doing. It'll move with the viewpoint, giving the impression that the player is holding a spotlight.


Adjustments to the view parameters

The FOV is widened, so the user can see more of the scene in front of them. The front and back clip distances are adjusted, primarily so the user can move right up to a block or cylinder without it being clipped.


An avatar

Many first-person games include a player avatar (a visible representation of the user, or part of the user); the "gun-in-hand" image in Chapter 24 is a simple example. I've decided not to include an avatar in the Maze3D application, but I'll discuss how to add one later in this section. The example code adds a 3D cone to the viewpoint.

The FOV

The FOV specifies how much of the scene is visible in terms of an angular spread around the viewpoint. The default is 45 degrees or 22.5 degrees on either side of the perpendicular into the scene (see Figure 25-8).

Figure 25-8. Field of view of a user


Increasing the FOV permits the user to see farther to the left and right, which is useful in Maze3D since corridors head off to the left and right. The downside is that a fish-eye effect appearing as the FOV is made larger, distorting the view.

The camera lovers among you may have come across a fish-eye lens, which displays the same effect as a large FOV: the screen image is foreshortened in the center and increasingly distorted at the edges.


Changing the FOV to 90 degrees (from its default of 45 degrees) is straightforward:

     View userView = su.getViewer(  ).getView(  );     userView.setFieldOfView( Math.toRadians(90.0)); // wider FOV 

The user can see more to the left and right, which is useful in a maze for noticing passages leading off to the sides.

Clip Distances

Clip distances specify the closest and farthest objects that can be seen by the viewer. Java 3D's defaults are 0.1 meters for the front clip plane and 10 meters for the back clip, which roughly corresponds to the limitations of human eyesight. These distance are in real-world units, although it's possible to specify values in virtual world units by using the Java 3D View class methods setFrontClipPolicy( ) and setBackClipPolicy( ) to change the units policy.

The back clip value may be too small for a particular application, resulting in objects disappearing when the viewpoint moves far away from them. This can be tested by moving the viewpoint to some distant location in the game and turning around to see if any parts of the scene have disappeared. In Maze3D, I moved the viewpoint out of the maze to the edge of the floor and checked that the maze was still visible.

The default front clip value (0.1 meters) is fine for most applications, but the user is likely to bump into walls in the maze. When this occurs, the front of the block or cylinder will be clipped since the viewpoint is closer than 0.1 meters to the object. What's worse is that the other side of the object will be invisible since the user is looking at its interior face, which is not rendered. The result is that users will see right through a brick or cylinder when they get up close to one.

This effect is shown in Figure 25-9: the image on the left shows the viewpoint when a user is pressed up against a cylinder and the front clip distance is 0.1 meters. The cylinder is invisible. The image on the right shows the same view when the front clip distance is reduced to 0.05 meters. The cylinder is visible again.

Figure 25-9. The same viewpoint with different front clip distances


Knowing this problem, a programmer may be tempted to set the front clip distance to 0 with the hope that no clipping will occur, no matter how close the user gets to a piece of scenery. What happens is that the depth ordering of objects in the scene breaks down, with far and near objects partially overlapping each other in arbitrary ways.

The breakdown is triggered when the ratio of the back clip to front clip distances becomes too large. The machine's hardware (specifically its depth buffer) is being asked to squeeze too large a range of z-values into too few bits per pixel on screen. The critical ratio depends on the bits per pixel used in the depth buffer: older machines may start to "sweat" at ratios close to 100, but modern cards using 32 bits will be happy with ratios close to 100,000.

After the experimentation mentioned above, I chose a back clip distance of 20 meters and a front clip of 0.05 meters, creating a fairly safe ratio of 400:

     userView.setBackClipDistance(20);     userView.setFrontClipDistance(0.05); 

Adding a Spotlight

The spotlight is meant to be in the player's "hand," lighting the way forward as he or she moves around the maze. This means that the spotlight must be connected to the viewpoint and move as it moves. Chapter 24 went into detail about the view branch where the viewpoint is managed, and Figure 25-10 should remind you of its main components.

Figure 25-10. The view branch subgraph


The spotlight must be placed inside a PlatformGeometry node, then attached to the transformGroup (labeled as TG in Figure 25-10) above the ViewPlatform. The code that follows utilizes Java 3D's ViewingPlatform utility class to do this.

     ViewingPlatform vp = su.getViewingPlatform(  );             PlatformGeometry pg = new PlatformGeometry(  );     pg.addChild( makeSpot(  ) );     vp.setPlatformGeometry( pg ); 

The construction of the spotlight is done by makeSpot( ).

The Java 3D Spotlight node has a position, a direction, and focusing controls for its spread angle and concentration. The spread angle controls the width of the beam; no light is generated outside of the angle. An increased concentration value causes the beam to be focused into a narrower beam, though some light will appear beyond the beam's bounds. The default spread angle is 180 degrees; the default concentration is 0.0, which provides uniform light distribution.

Since Spotlight is a subclass of PointLight, it inherits other useful attributes, such as the ability to adjust its attenuation (how quickly the light fades away for objects further away).


A spotlight affects an object's diffuse and specular reflection, which depends on the orientation and position of the object's surfaces and on its Material node.

The diffuse reflection is the color that bounces of an object in random directions when light hits it. The specular reflection is the color of the shiny highlights of the object. The size of the highlights depends on the shininess value for the object.


The overall effect of a spotlight depends on the spotlight's parameters (position, direction, concentration, spread angle, attenuation) and on the lighting and material properties of the surfaces being lit. This interplay of so many factors makes it a matter of trial and error to get a suitable effect.

My aim was to have the spotlight cast a faint light, quickly crowded out by darkness.


The makeSpot( ) method is:

     private SpotLight makeSpot(  )     {       SpotLight spot = new SpotLight(  );       spot.setPosition(0.0f, 0.5f, 0.0f);      // a bit above the user       spot.setAttenuation(0.0f, 1.2f, 0.0f);   // linear attenuation       spot.setSpreadAngle( (float)Math.toRadians(30.0));  // smaller       spot.setConcentration(5.0f);        // reduce strength quicker       spot.setInfluencingBounds(bounds);       return spot;     } 

The 30-degree value for setSpreadAngle( ) corresponds to a spread angle of 60 degrees, 30 degrees on each side of the forward direction. The increased concentration focuses the beam, making the sides of the scene somewhat darker. By making the spread smaller and increasing the concentration, the light becomes weaker.

The setPosition( ) value moves the SpotLight node slightly above the viewpoint, which places the spotlight at a natural looking location relative to the viewpoint.

Adding an Avatar

Chapter 24 describes one way of adding an avatar: by placing a 2D image just in front of the viewpoint. Another solution is to use a 3D model (e.g., a hand, a gun). To illustrate the idea, makeAvatar( ) adds a cone at the viewpoint, rotated by 90 degrees about the x-axis so its apex is pointing forward:

     private TransformGroup makeAvatar(  )     {       Transform3D t3d = new Transform3D(  );       t3d.rotX(Math.PI/2);    // rotate so top of cone is facing front       TransformGroup userTG = new TransformGroup(t3d);           userTG.addChild( new Cone(0.35f, 1.0f) );  // a thin cone       return userTG;     } 

The transformGroup is then linked to PlatformGeometry:

     pg.addChild( makeAvatar(  ) ); 

If you wanted to add this cone avatar to the viewpoint in Maze3D, the pg.addChild( ) call could be included in the code fragment on the previous page, when the spotlight is added to PlatformGeometry.

Using a 3D model has one major advantage over a 2D image: it can be used as your physical presence in the scene. This is useful if the game contains other players who need to see you (e.g., as in a multiplayer networked application). A 3D model is also a drawback since it may be partially obscured if the viewpoint gets too close to another object in the scene.

Positioning the Viewpoint

The viewpoint's position and orientation are set up by accessing and changing the transformGroup above ViewPlatform :

     ViewingPlatform vp = su.getViewingPlatform(  );     TransformGroup steerTG = vp.getViewPlatformTransform(  );     initViewPosition(steerTG); 

steerTG is the transformGroup above the ViewPlatform node shown in Figure 25-10. By default, the initial viewpoint is facing into the scene along the z-axis. initViewPosition( ) rotates it by 180 degrees and moves it to the maze's start position:

     private void initViewPosition(TransformGroup steerTG)     {       Transform3D t3d = new Transform3D(  );       steerTG.getTransform(t3d);       Transform3D toRot = new Transform3D(  );       toRot.rotY(-Math.PI);    // so facing along positive z-axis           t3d.mul(toRot);       t3d.setTranslation( mazeMan.getMazeStartPosn(  ) );       steerTG.setTransform(t3d);     } 

Since the rotation orients the viewpoint in the positive z-axis direction, the translation value doesn't need to be adjusted.

Keyboard Controls

The keyboard behavior is set up inside prepareViewPoint( ) in WrapMaze3D:

     ViewingPlatform vp = su.getViewingPlatform(  );             KeyBehavior keybeh = new KeyBehavior(mazeMan, be, camera2TG);     keybeh.setSchedulingBounds(bounds);     vp.setViewPlatformBehavior(keybeh); 

The KeyBehavior class requires a reference to MazeManager (mazeMan), the BirdsEye panel (be), and the transformGroup for the back facing camera (camera2TG).



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net