📄 landscape.java
字号:
// Landscape.java
// Andrew Davison, August 2003, dandrew@ratree.psu.ac.th
/* Landscape loads a landscape made up of a mesh and a texture.
If the user supplied the filename, fname, then Landscape looks
in models/fname.obj for the mesh, and models/fname.jpg for the
texture.
The resulting BranchGroup, landBG, is added to the scene (sceneBG).
The Landscape may contain two kinds of scenery:
* full 3D shapes, loaded with PropManager.
Intended for irregular objects which the user can move around,
and perhaps enter (e.g. a castle).
* ground cover, which are 2D images that rotate to always face
the user. Intended for simple, symmetrical objects that
'decorate' the scene, such as trees, bushes.
Since many copies will be required, a SharedGroup node is
used for each unique piece of ground cover.
Ground cover is managed by a GroundCover object.
The Landscape is surrounded by four walls which are covered
in a mountain range image.
*/
import java.io.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.image.*;
import com.sun.j3d.loaders.objectfile.ObjectFile;
import com.sun.j3d.loaders.*;
import com.sun.j3d.utils.picking.*;
public class Landscape
{
private static final double LAND_LEN = 60.0;
// length of landscape in world coordinates
private static final String WALL_PIC = "models/mountain2Sq.jpg";
// texture used for the four walls around the landscape
private BranchGroup sceneBG;
private BranchGroup landBG = null; // holding the landscape parts
private Shape3D landShape3D = null; // holding the landscape mesh
// various mesh dimensions, set in getLandDimensions()
private double landLength, minHeight, maxHeight;
private double scaleLen;
private Vector3d originVec = null;
// where the user starts in the landscape (in world coordinates)
public Landscape(BranchGroup sceneBG, String fname)
{
this.sceneBG = sceneBG;
loadMesh(fname); // initialize landBG
getLandShape(landBG); // initialize landShape3D
// set the picking capabilities so that intersection
// coords can be extracted after the shape is picked
PickTool.setCapabilities(landShape3D, PickTool.INTERSECT_COORD);
getLandDimensions(landShape3D); // extracts sizes from landShape3D
makeScenery(landBG, fname); // add any scenery
addWalls(); // walls around the landscape
GroundCover gc = new GroundCover(fname);
landBG.addChild( gc.getCoverBG() ); // add any ground cover
addLandtoScene(landBG);
addLandTexture(landShape3D, fname);
} // end Landscape()
private void loadMesh(String fname)
/* Load the terrain mesh from models/fname.obj.
We use Java 3D's OBJ loader. Then assign its BranchGroup to
landBG.
*/
{
FileWriter ofw = null;
String fn = new String("models/" + fname + ".obj"); // assume OBJ file in models/
System.out.println( "Loading terrain mesh from: " + fn + " ..." );
try {
ObjectFile f = new ObjectFile(); // ObjectFile.STRIPIFY
Scene loadedScene = f.load(fn);
if(loadedScene == null ) {
System.out.println("Scene not found in: " + fn);
System.exit(0);
}
landBG = loadedScene.getSceneGroup(); // the land's BG
if(landBG == null ) {
System.out.println("No land branch group found");
System.exit(0);
}
}
catch( IOException ioe )
{ System.err.println("Terrain mesh load error: " + fn);
System.exit(0);
}
} // end of loadMesh()
private void getLandShape(BranchGroup landBG)
/* Check that landBG (the mesh's BranchGroup) contains a
single Shape3D, and assign it to landShape3D for later.
Also check that the shape holds a single GeometryArray.
landShape3D is later used for picking, and its dimensions
are extracted.
*/
{
if (landBG.numChildren() > 1)
System.out.println("More than one child in land branch group");
Node node = landBG.getChild(0);
if (!(node instanceof Shape3D)) {
System.out.println("No Shape3D found in land branch group");
System.exit(0);
}
landShape3D = (Shape3D) node;
if (landShape3D == null) {
System.out.println("Land Shape3D has no value");
System.exit(0);
}
if (landShape3D.numGeometries() > 1)
System.out.println("More than 1 geometry in land branch group");
Geometry g = landShape3D.getGeometry();
if (!(g instanceof GeometryArray)) {
System.out.println("No Geometry Array found in land Shape3D");
System.exit(0);
}
} // end of getLandShape()
private void getLandDimensions(Shape3D landShape3D)
/* If we reach this point, then landShape3D contains a single
GeometryArray of points representing the landscape's mesh.
We can get the dimensions of the mesh by using a BoundingBox.
Check if the XY plane is the 'floor' of the landscape, which
is how the TerraGen mesh should have been exported. The X and
Y lengths should be the same, and start at (0,0).
Set the following vars:
* landLength // length of the X (and Y) side
* scaleLen // the scaling necessary to fit landLength
// into LAND_LEN units in the world
* minHeight and maxHeight // along the Z axis
*/
{
// get the bounds of the shape
BoundingBox boundBox = new BoundingBox( landShape3D.getBounds() );
Point3d lower = new Point3d();
Point3d upper = new Point3d();
boundBox.getLower(lower); boundBox.getUpper(upper);
System.out.println("lower: " + lower + "\nupper: " + upper );
if ((lower.y == 0) && (upper.x == upper.y)) {
// System.out.println("XY being used as the floor");
}
else if ((lower.z == 0) && (upper.x == upper.z)) {
System.out.println("Error: XZ set as the floor; change to XY in Terragen");
System.exit(0);
}
else {
System.out.println("Cannot determine floor axes");
System.out.println("Y range should == X range, and start at 0");
System.exit(0);
}
landLength = upper.x;
scaleLen = LAND_LEN/landLength;
System.out.println("scaleLen: " + scaleLen );
minHeight = lower.z;
maxHeight = upper.z;
} // end of getLandDimensions()
private void addLandtoScene(BranchGroup landBG)
/* The floor of the landscape is the XY plane, starting at (0,0),
with sides of landLength units.
The landscape (landBG) is rotated to lie on the XZ plane, scaled
to have floor sides of length LAND_LEN (scaleLen = LAND_LEN/landLength),
then translated so that the center of the landscape is at (0,0).
*/
{
// rotate and scale land
Transform3D t3d = new Transform3D();
t3d.rotX( -Math.PI/2.0 ); // so land's XY is resting on the XZ plane
t3d.setScale( new Vector3d(scaleLen, scaleLen, scaleLen) ); // scale
TransformGroup sTG = new TransformGroup(t3d);
sTG.addChild(landBG);
// center the land, which starts at (0,0) on the XZ plane,
// so move it left and forward
Transform3D t3d1 = new Transform3D();
t3d1.set( new Vector3d(-LAND_LEN/2, 0, LAND_LEN/2)); // translate
TransformGroup posTG = new TransformGroup(t3d1);
posTG.addChild( sTG );
sceneBG.addChild(posTG);
} // end of addLandtoScene()
// ----------------------- add texture to the land ------------------
private void addLandTexture(Shape3D shape, String fname)
/* The land texture (in models/fname.jpg) shows the landscape from
above, and is used to colour the mesh loaded from the OBJ file.
To save memory, the shape is one-sided.
The texture is blended with a white material so that lighting
effects can be shown.
*/
{ Appearance app = shape.getAppearance();
// generate texture coords that 'stretch' the texture over the mesh
app.setTexCoordGeneration( stampTexCoords(shape) );
// combine texture with colour and lighting of underlying surface
TextureAttributes ta = new TextureAttributes();
ta.setTextureMode( TextureAttributes.MODULATE );
app.setTextureAttributes( ta );
// apply texture to shape
Texture2D tex = loadLandTexture(fname);
if (tex != null) {
app.setTexture(tex);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -