2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 package jmetest.TutorialGuide;
35 import java.io.ByteArrayInputStream;
36 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
39 import java.util.logging.Level;
40 import java.util.logging.Logger;
42 import com.jme.app.SimpleGame;
43 import com.jme.bounding.BoundingSphere;
44 import com.jme.curve.BezierCurve;
45 import com.jme.curve.CurveController;
46 import com.jme.input.InputHandler;
47 import com.jme.input.KeyInput;
48 import com.jme.input.action.KeyExitAction;
49 import com.jme.math.Matrix3f;
50 import com.jme.math.Vector3f;
51 import com.jme.scene.CameraNode;
52 import com.jme.scene.Controller;
53 import com.jme.scene.Node;
54 import com.jme.scene.Spatial;
55 import com.jme.scene.TriMesh;
56 import com.jme.scene.lod.AreaClodMesh;
57 import com.jme.scene.state.RenderState;
58 import com.jme.util.export.binary.BinaryImporter;
59 import com.jmex.model.converters.FormatConverter;
60 import com.jmex.model.converters.ObjToJme;
63 * Started Date: Aug 16, 2004<br><br>
65 * This program teaches Complex Level of Detail mesh objects. To use this program, move
66 * the camera backwards and watch the model disappear.
68 * @author Jack Lindamood
70 public class HelloLOD extends SimpleGame {
71 private static final Logger logger = Logger.getLogger(HelloLOD.class
76 public static void main(String[] args) {
77 HelloLOD app = new HelloLOD();
78 app.setConfigShowMode(ConfigShowMode.AlwaysShow);
82 protected void simpleInitGame() {
83 // Point to a URL of my model
84 URL model=HelloModelLoading.class.getClassLoader().getResource("jmetest/data/model/maggie.obj");
86 // Create something to convert .obj format to .jme
87 FormatConverter converter=new ObjToJme();
88 // Point the converter to where it will find the .mtl file from
89 converter.setProperty("mtllib",model);
91 // This byte array will hold my .jme file
92 ByteArrayOutputStream BO=new ByteArrayOutputStream();
93 Spatial maggie = null;
95 // Use the format converter to convert .obj to .jme
96 converter.convert(model.openStream(), BO);
98 // Load the binary .jme format into a scene graph
99 maggie=(Spatial)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
102 } catch (IOException e) { // Just in case anything happens
103 logger.logp(Level.SEVERE, this.getClass().toString(),
104 "simpleInitGame()", "Exception", e);
108 // Create a clod duplicate of meshParent.
109 Node clodNode=getClodNodeFromParent((Node)maggie);
111 // Attach the clod mesh at the origin.
112 clodNode.setLocalScale(.1f);
113 rootNode.attachChild(clodNode);
115 // Attach the original at -15,0,0
116 maggie.setLocalScale(.1f);
117 maggie.setLocalTranslation(new Vector3f(-15,0,0));
119 rootNode.attachChild(maggie);
121 // Clear the keyboard commands that can move the camera.
122 input = new InputHandler();
123 // Insert a keyboard command that can exit the application.
124 input.addAction( new KeyExitAction(this), "exit", KeyInput.KEY_ESCAPE, false );
126 // The path the camera will take.
127 Vector3f[]cameraPoints=new Vector3f[]{
128 new Vector3f(0,5,20),
129 new Vector3f(0,20,90),
130 new Vector3f(0,30,200),
131 new Vector3f(0,100,300),
132 new Vector3f(0,150,400),
134 // Create a path for the camera.
135 BezierCurve bc=new BezierCurve("camera path",cameraPoints);
137 // Create a camera node to move along that path.
138 cn=new CameraNode("camera node",cam);
140 // Create a curve controller to move the CameraNode along the path
141 CurveController cc=new CurveController(bc,cn);
143 // Cycle the animation.
144 cc.setRepeatType(Controller.RT_CYCLE);
146 // Slow down the curve controller a bit
149 // Add the controller to the node.
150 cn.addController(cc);
152 // Attach the node to rootNode
153 rootNode.attachChild(cn);
156 private Node getClodNodeFromParent(Node meshParent) {
157 // Create a node to hold my cLOD mesh objects
158 Node clodNode=new Node("Clod node");
159 // For each mesh in maggie
160 for (int i=0;i<meshParent.getQuantity();i++){
161 final Spatial child = meshParent.getChild(i);
162 if ( child instanceof Node )
164 clodNode.attachChild( getClodNodeFromParent( (Node) child ) );
166 else if ( child instanceof TriMesh )
168 // Create an AreaClodMesh for that mesh. Let it compute records automatically
169 AreaClodMesh acm=new AreaClodMesh("part"+i,(TriMesh) child,null);
170 acm.setModelBound(new BoundingSphere());
171 acm.updateModelBound();
173 // Allow 1/2 of a triangle in every pixel on the screen in the bounds.
174 acm.setTrisPerPixel(.5f);
176 // Force a move of 2 units before updating the mesh geometry
177 acm.setDistanceTolerance(2);
179 // Give the clodMesh node the material state that the original had.
180 acm.setRenderState(child.getRenderState(RenderState.StateType.Material));
183 clodNode.attachChild(acm);
187 logger.warning("Unhandled Spatial type: " + child.getClass());
193 Vector3f up=new Vector3f(0,1,0);
194 Vector3f left=new Vector3f(1,0,0);
196 private static Vector3f tempVa=new Vector3f();
197 private static Vector3f tempVb=new Vector3f();
198 private static Vector3f tempVc=new Vector3f();
199 private static Vector3f tempVd=new Vector3f();
200 private static Matrix3f tempMa=new Matrix3f();
202 protected void simpleUpdate(){
203 // Get the center of root's bound.
204 Vector3f objectCenter=rootNode.getWorldBound().getCenter(tempVa);
206 // My direction is the place I want to look minus the location of the camera.
207 Vector3f lookAtObject=tempVb.set(objectCenter).subtractLocal(cam.getLocation()).normalizeLocal();
210 tempMa.setColumn(0,up.cross(lookAtObject,tempVc).normalizeLocal());
212 tempMa.setColumn(1,left.cross(lookAtObject,tempVd).normalizeLocal());
214 tempMa.setColumn(2,lookAtObject);
216 cn.setLocalRotation(tempMa);