jme学习笔记例12---LOD模型缩放

 

本程序介绍AreaClodMesh, BezierCurve, CurveController,  CameraNode. 学习增加 FPS, 摄像机如何沿弯曲的路径移动.

 

import com.jme.app.SimpleGame;
import com.jme.scene.model.XMLparser.Converters.FormatConverter;
import com.jme.scene.model.XMLparser.Converters.ObjToJme;
import com.jme.scene.model.XMLparser.JmeBinaryReader;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.CameraNode;
import com.jme.scene.Controller;
import com.jme.scene.state.RenderState;
import com.jme.scene.lod.AreaClodMesh;
import com.jme.bounding.BoundingSphere;
import com.jme.math.Vector3f;
import com.jme.math.Matrix3f;
import com.jme.curve.CurveController;
import com.jme.curve.BezierCurve;
import com.jme.input.KeyInput;
import com.jme.input.action.KeyExitAction;
import java.net.URL;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
/**
* Started Date: Aug 16, 2004<br><br>
*
* This program teaches Complex Level of Detail mesh objects. To use this program, move
* the camera backwards and watch the model disappear.
*
* @author Jack Lindamood
*/
public class HelloLOD extends SimpleGame {
CameraNode cn;
public static void main(String[] args) {
HelloLOD app = new HelloLOD();
app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
protected void simpleInitGame() {
// Point to a URL of my model
URL model =
HelloModelLoading.class.getClassLoader().
getResource("jmetest/data/model/maggie.obj");
// Create something to convert .obj format to .jme
FormatConverter converter=new ObjToJme();
// Point the converter to where it will find the .mtl file from
converter.setProperty("mtllib",model);
// This byte array will hold my .jme file
ByteArrayOutputStream BO=new ByteArrayOutputStream();
// This will read the .jme format and convert it into a scene graph
JmeBinaryReader jbr=new JmeBinaryReader();
// Use an exact BoundingSphere bounds
BoundingSphere.useExactBounds=true;
Node meshParent=null;
try {
// Use the format converter to convert .obj to .jme
converter.convert(model.openStream(), BO);
// Load the binary .jme format into a scene graph
Node maggie = jbr.loadBinaryFormat(new
ByteArrayInputStream(BO.toByteArray()));
meshParent=(Node) maggie.getChild(0);
} catch (IOException e) { // Just in case anything happens
System.out.println("Damn exceptions!" + e);
e.printStackTrace();
System.exit(0);
}
// Create a clod duplicate of meshParent.
Node clodNode=getClodNodeFromParent(meshParent);
// Attach the clod mesh at the origin.
clodNode.setLocalScale(.1f);
rootNode.attachChild(clodNode);
// Attach the original at -15,0,0
meshParent.setLocalScale(.1f);
meshParent.setLocalTranslation(new Vector3f(-15,0,0));
rootNode.attachChild(meshParent);
// Clear the keyboard commands that can move the camera.
input.clearKeyboardActions();
input.clearMouseActions();
// Insert a keyboard command that can exit the application.
input.addKeyboardAction("exit",KeyInput.KEY_ESCAPE,
new KeyExitAction(this));
// The path the camera will take.
Vector3f[]cameraPoints=new Vector3f[]{
new Vector3f(0,5,20),
new Vector3f(0,20,90),
new Vector3f(0,30,200),
new Vector3f(0,100,300),
new Vector3f(0,150,400),

};
// Create a path for the camera.
BezierCurve bc=new BezierCurve("camera path",cameraPoints);
// Create a camera node to move along that path. cn=new CameraNode("camera node",cam);
// Create a curve controller to move the CameraNode along the path CurveController cc=new CurveController(bc,cn);
// Cycle the animation. cc.setRepeatType(Controller.RT_CYCLE);
// Slow down the curve controller a bit cc.setSpeed(.25f);
// Add the controller to the node. cn.addController(cc);
// Attach the node to rootNode rootNode.attachChild(cn); }
private Node getClodNodeFromParent(Node meshParent) {
// Create a node to hold my cLOD mesh objects
Node clodNode=new Node("Clod node");
// For each mesh in maggie
for (int i=0;i<meshParent.getQuantity();i++){
// Create an AreaClodMesh for that mesh. Let it compute
// records automatically
AreaClodMesh acm=new AreaClodMesh("part"+i,
(TriMesh) meshParent.getChild(i),null);
acm.setModelBound(new BoundingSphere()); acm.updateModelBound();
// Allow 1/2 of a triangle in every pixel on the screen in
// the bounds. acm.setTrisPerPixel(.5f);
// Force a move of 2 units before updating the mesh geometry acm.setDistanceTolerance(2);
// Give the clodMesh node the material state that the
// original had.
acm.setRenderState(meshParent.getChild(i). getRenderStateList()[RenderState.RS_MATERIAL]);
// Attach clod node. clodNode.attachChild(acm);
} return clodNode; }
Vector3f up=new Vector3f(0,1,0); Vector3f left=new Vector3f(1,0,0);

private static Vector3f tempVa=new Vector3f();
private static Vector3f tempVb=new Vector3f();
private static Vector3f tempVc=new Vector3f();
private static Vector3f tempVd=new Vector3f(); private static Matrix3f tempMa=new Matrix3f();
protected void simpleUpdate(){
// Get the center of root's bound. Vector3f objectCenter=rootNode.getWorldBound().getCenter(tempVa);
// My direction is the place I want to look minus the location
// of the camera.
Vector3f lookAtObject = tempVb.set(objectCenter). subtractLocal(cam.getLocation()).normalizeLocal();
// Left vector
tempMa.setColumn(0,up.cross(lookAtObject,tempVc).normalizeLocal());
// Up vector
tempMa.setColumn(1,left.cross(lookAtObject,tempVd).normalizeLocal());
// Direction vector tempMa.setColumn(2,lookAtObject); cn.setLocalRotation(tempMa); } }

用maggie模型构造一个网深mesh建立一个边对边的模型镜象,相机的移动通过CameraNode实现. 

private Node getClodNodeFromParent(Node meshParent) {
// Create a node to hold my cLOD mesh objects
Node clodNode=new Node("Clod node");
// For each mesh in maggie
for (int i=0;i<meshParent.getQuantity();i++){
// Create an AreaClodMesh for that mesh. Let it compute
// records automatically
AreaClodMesh acm=new AreaClodMesh("part"+i,
(TriMesh) meshParent.getChild(i),null);
acm.setModelBound(new BoundingSphere());
acm.updateModelBound();
// Allow 1/2 of a triangle in every pixel on the screen in
// the bounds.
acm.setTrisPerPixel(.5f);
// Force a move of 2 units before updating the mesh geometry
acm.setDistanceTolerance(2);
// Give the clodMesh node the material state that the
// original had.
acm.setRenderState(meshParent.getChild(i).
getRenderStateList()[RenderState.RS_MATERIAL]);
// Attach clod node.
clodNode.attachChild(acm);
}
return clodNode;
} 

  一个cloudmesh是一个"细节复杂度层次"的mesh,他允许你修剪少量的三角形,效果如例子的复制模型且花费更小
的开销,cloudmesh使渲染速度更快,例如你游戏的角色需要5000个多边形绘制,但是如果角色远离你1000英尺的话
只需要400个多边形绘制就够了,对玩家来说这个区别是无关紧要的,但对计算机来说是一个很大的开销,ClodMesh可以
仅被Trimesh创建,因为maggie本身就是多个trimesh构成的(一个是黄色,另一个是蓝色...),我必须为每一个mesh
创建ClodMesh,这里ClodMesh类型是AreaClodMesh,它使用物体约束边界的屏幕区域觉得那个三角形的数量,
最近的物体有全部的三角形,越远的就越少.
  AreaClodMesh acm=new AreaClodMesh("part"+i,(TriMesh) meshParent.getChild(i),null); 

第一个是Clodmesh的参数名,全部的Spatials必须有一个名字,第2个参数是创建ClodMesh的Trimesh的集合.集合通知
ClodMesh如何减少三角形,为null时几个被创建
  // Allow 1/2 of a triangle in every pixel on the screen in
// the bounds.
acm.setTrisPerPixel(.5f);
这个方法告诉clodmesh如何快速减少三角形,我故意让
mesh变小从而让你看到marrie的变化,在你的游戏中
你将设定可以忽略的三角形的值,clodmesh将预计算acm的区域约束
// Force a move of 2 units before updating the mesh geometry
acm.setDistanceTolerance(2);

每当摄像机的距离改变2个单位强制更新三角形的清除,这个更新在每帧中将会非常慢,为减少更新次数需要用
高速缓存(high FPS)
// Give the clodMesh node the material state that the
// original had.
acm.setRenderState(meshParent.getChild(i).
getRenderStateList()[RenderState.RS_MATERIAL]);

因为acm有原始几何信息并不代表他有原始的渲染状态,因此,我设定acm的材质状态和原始的材质状态一致,
在创建AreaClodMesh节点后,我设置键盘action
// Clear the keyboard commands that can move the camera.
input.clearKeyboardActions();
input.clearMouseActions();
// Insert a keyboard command that can exit the application.
input.addKeyboardAction("exit",KeyInput.KEY_ESCAPE,
new KeyExitAction(this));
在这个程序中我没有要用户象平常一样控制摄像头,因此我
刷新鼠标和键盘输入的input action,我必须重新插入一
个KEYEXITACtion否则用户将没有办法关闭应用,最后去创建我的摄像机的路径
// The path the camera will take.
Vector3f[]cameraPoints=new Vector3f[]{
new Vector3f(0,5,20),
new Vector3f(0,20,90),
new Vector3f(0,30,200),
new Vector3f(0,100,300),
new Vector3f(0,150,400),
};
// Create a path for the camera.
BezierCurve bc=new BezierCurve("camera path",cameraPoints);
我定义摄像机路径的点并创建BezierCurve,一个curve是实际的spatial(所以需要名字),但是curve不用被渲染,
它只需要描述路径,在设置curve后,我创建一个cameranode去移动
// Create a camera node to move along that path.
cn=new CameraNode("camera node",cam);

一个cameranode是一个可以被旋转的spatial,cam的值被Simplegame创建,下面是
// Create a camera node to move along that path.
cn=new CameraNode("camera node",display.getRenderer().getCamera());
摄像机被建立后,我可以为摄像机创建controller了

// Create a curve controller to move the CameraNode along the path
CurveController cc=new CurveController(bc,cn);
// Cycle the animation.
cc.setRepeatType(Controller.RT_CYCLE);
// Slow down the curve controller a bit
cc.setSpeed(.25f);
// Add the controller to the node.
cn.addController(cc);
// Attach the node to rootNode
rootNode.attachChild(cn);

上面实现了完美的旋转,一个curvecontroller让spatial沿曲线移动,最后让cameranode面向maggie模型对象旋转
protected void simpleUpdate(){
// Get the center of root's bound.
Vector3f objectCenter=rootNode.getWorldBound().getCenter(tempVa);
// My direction is the place I want to look minus the location
// of the camera.
Vector3f lookAtObject = tempVb.set(objectCenter).
subtractLocal(cam.getLocation()).normalizeLocal();
// Left vector
tempMa.setColumn(0,up.cross(lookAtObject,tempVc).normalizeLocal());
// Up vector
tempMa.setColumn(1,left.cross(lookAtObject,tempVd).normalizeLocal());
// Direction vector
tempMa.setColumn(2,lookAtObject);
cn.setLocalRotation(tempMa);
}

注意我创建了大量的临时静态变量,它们是更新时
需要数学调用的值,cameranode的旋转仿真使用摄像机
对象的东边,curvecontroller仅是摄像机的位置,开头2行设置一个摄像机到物体方向的vector,最后4行虽然有些奇怪:
// Left vector
tempMa.setColumn(0,up.cross(lookAtObject,tempVc).normalizeLocal());
// Up vector
tempMa.setColumn(1,left.cross(lookAtObject,tempVd).normalizeLocal());
// Direction vector
tempMa.setColumn(2,lookAtObject);
cn.setLocalRotation(tempMa);
第一行是摄像机左边的vector,第2个是上面的,第3个是
摄像机面向的方向,我使用up vector的矢量积和方向
得到left 矢量(up vector如同)。cross的两个向量
是彼此垂直的(也就是高中学的U*V=0. U,V为垂直矢量)

 

你可能感兴趣的:(游戏,UP)