2 * Copyright (c) 2003-2005 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;
40 import com.jme.app.SimpleGame;
41 import com.jme.bounding.BoundingSphere;
42 import com.jme.curve.BezierCurve;
43 import com.jme.curve.CurveController;
44 import com.jme.input.KeyInput;
45 import com.jme.input.action.KeyExitAction;
46 import com.jme.math.Matrix3f;
47 import com.jme.math.Vector3f;
48 import com.jme.scene.*;
49 import com.jme.scene.lod.AreaClodMesh;
50 import com.jme.scene.state.RenderState;
51 import com.jme.util.LoggingSystem;
52 import com.jmex.model.XMLparser.JmeBinaryReader;
53 import com.jmex.model.XMLparser.Converters.FormatConverter;
54 import com.jmex.model.XMLparser.Converters.ObjToJme;
57 * Started Date: Aug 16, 2004<br><br>
59 * This program teaches Complex Level of Detail mesh objects. To use this program, move
60 * the camera backwards and watch the model disappear.
62 * @author Jack Lindamood
64 public class HelloLOD extends SimpleGame {
68 public static void main(String[] args) {
69 HelloLOD app = new HelloLOD();
70 app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
74 protected void simpleInitGame() {
75 // Point to a URL of my model
76 URL model=HelloModelLoading.class.getClassLoader().getResource("jmetest/data/model/maggie.obj");
78 // Create something to convert .obj format to .jme
79 FormatConverter converter=new ObjToJme();
80 // Point the converter to where it will find the .mtl file from
81 converter.setProperty("mtllib",model);
83 // This byte array will hold my .jme file
84 ByteArrayOutputStream BO=new ByteArrayOutputStream();
85 // This will read the .jme format and convert it into a scene graph
86 JmeBinaryReader jbr=new JmeBinaryReader();
90 // Use the format converter to convert .obj to .jme
91 converter.convert(model.openStream(), BO);
93 // Load the binary .jme format into a scene graph
94 Node maggie=jbr.loadBinaryFormat(new ByteArrayInputStream(BO.toByteArray()));
95 meshParent=(Node) maggie.getChild(0);
97 } catch (IOException e) { // Just in case anything happens
98 System.out.println("Damn exceptions!" + e);
103 // Create a clod duplicate of meshParent.
104 Node clodNode=getClodNodeFromParent(meshParent);
106 // Attach the clod mesh at the origin.
107 clodNode.setLocalScale(.1f);
108 rootNode.attachChild(clodNode);
110 // Attach the original at -15,0,0
111 meshParent.setLocalScale(.1f);
112 meshParent.setLocalTranslation(new Vector3f(-15,0,0));
114 rootNode.attachChild(meshParent);
116 // Clear the keyboard commands that can move the camera.
117 input.clearKeyboardActions();
118 input.clearMouseActions();
119 // Insert a keyboard command that can exit the application.
120 input.addKeyboardAction("exit",KeyInput.KEY_ESCAPE,new KeyExitAction(this));
122 // The path the camera will take.
123 Vector3f[]cameraPoints=new Vector3f[]{
124 new Vector3f(0,5,20),
125 new Vector3f(0,20,90),
126 new Vector3f(0,30,200),
127 new Vector3f(0,100,300),
128 new Vector3f(0,150,400),
130 // Create a path for the camera.
131 BezierCurve bc=new BezierCurve("camera path",cameraPoints);
133 // Create a camera node to move along that path.
134 cn=new CameraNode("camera node",cam);
136 // Create a curve controller to move the CameraNode along the path
137 CurveController cc=new CurveController(bc,cn);
139 // Cycle the animation.
140 cc.setRepeatType(Controller.RT_CYCLE);
142 // Slow down the curve controller a bit
145 // Add the controller to the node.
146 cn.addController(cc);
148 // Attach the node to rootNode
149 rootNode.attachChild(cn);
152 private Node getClodNodeFromParent(Node meshParent) {
153 // Create a node to hold my cLOD mesh objects
154 Node clodNode=new Node("Clod node");
155 // For each mesh in maggie
156 for (int i=0;i<meshParent.getQuantity();i++){
157 final Spatial child = meshParent.getChild(i);
158 if ( child instanceof Node )
160 clodNode.attachChild( getClodNodeFromParent( (Node) child ) );
162 else if ( child instanceof TriMesh )
164 // Create an AreaClodMesh for that mesh. Let it compute records automatically
165 AreaClodMesh acm=new AreaClodMesh("part"+i,(TriMesh) child,null);
166 acm.setModelBound(new BoundingSphere());
167 acm.updateModelBound();
169 // Allow 1/2 of a triangle in every pixel on the screen in the bounds.
170 acm.setTrisPerPixel(.5f);
172 // Force a move of 2 units before updating the mesh geometry
173 acm.setDistanceTolerance(2);
175 // Give the clodMesh node the material state that the original had.
176 acm.setRenderState(child.getRenderStateList()[RenderState.RS_MATERIAL]);
179 clodNode.attachChild(acm);
183 LoggingSystem.getLogger().warning( "Unhandled Spatial type: " + child.getClass() );
189 Vector3f up=new Vector3f(0,1,0);
190 Vector3f left=new Vector3f(1,0,0);
192 private static Vector3f tempVa=new Vector3f();
193 private static Vector3f tempVb=new Vector3f();
194 private static Vector3f tempVc=new Vector3f();
195 private static Vector3f tempVd=new Vector3f();
196 private static Matrix3f tempMa=new Matrix3f();
198 protected void simpleUpdate(){
199 // Get the center of root's bound.
200 Vector3f objectCenter=rootNode.getWorldBound().getCenter(tempVa);
202 // My direction is the place I want to look minus the location of the camera.
203 Vector3f lookAtObject=tempVb.set(objectCenter).subtractLocal(cam.getLocation()).normalizeLocal();
206 tempMa.setColumn(0,up.cross(lookAtObject,tempVc).normalizeLocal());
208 tempMa.setColumn(1,left.cross(lookAtObject,tempVd).normalizeLocal());
210 tempMa.setColumn(2,lookAtObject);
212 cn.setLocalRotation(tempMa);