OSDN Git Service

Set optimal mime types and executable settings.
[mikumikustudio/MikuMikuStudio.git] / src / jmetest / flagrushtut / Lesson4.java
1 /*
2  * Copyright (c) 2003-2009 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
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.
15  *
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.
19  *
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.
31  */
32
33 package jmetest.flagrushtut;
34
35 import java.util.logging.Level;
36 import java.util.logging.Logger;
37
38 import javax.swing.ImageIcon;
39
40 import jmetest.renderer.TestSkybox;
41 import jmetest.terrain.TestTerrain;
42
43 import com.jme.app.BaseGame;
44 import com.jme.bounding.BoundingBox;
45 import com.jme.image.Texture;
46 import com.jme.input.KeyBindingManager;
47 import com.jme.input.KeyInput;
48 import com.jme.light.DirectionalLight;
49 import com.jme.math.FastMath;
50 import com.jme.math.Quaternion;
51 import com.jme.math.Vector3f;
52 import com.jme.renderer.Camera;
53 import com.jme.renderer.ColorRGBA;
54 import com.jme.renderer.Renderer;
55 import com.jme.scene.Node;
56 import com.jme.scene.SharedMesh;
57 import com.jme.scene.Skybox;
58 import com.jme.scene.shape.Box;
59 import com.jme.scene.shape.Cylinder;
60 import com.jme.scene.state.BlendState;
61 import com.jme.scene.state.LightState;
62 import com.jme.scene.state.TextureState;
63 import com.jme.scene.state.ZBufferState;
64 import com.jme.system.DisplaySystem;
65 import com.jme.system.JmeException;
66 import com.jme.util.TextureManager;
67 import com.jme.util.Timer;
68 import com.jme.util.geom.Debugger;
69 import com.jmex.terrain.TerrainBlock;
70 import com.jmex.terrain.util.MidPointHeightMap;
71 import com.jmex.terrain.util.ProceduralTextureGenerator;
72
73 /**
74  * <code>Tutorial 4</code> Builds the environment (surrounding structure and sky).
75  * framework for Flag Rush. For Flag Rush Tutorial Series.
76  * 
77  * @author Mark Powell
78  */
79 public class Lesson4 extends BaseGame {
80     private static final Logger logger = Logger.getLogger(Lesson4.class
81             .getName());
82     
83         // the terrain we will drive over.
84     private TerrainBlock tb;
85     // The texture that makes up the "force field", we will keep a reference to it
86     // here to allow us to animate it.
87     private Texture t;
88     //Sky box (we update it each frame)
89         private Skybox skybox;
90     
91     //the timer
92         protected Timer timer;
93
94         // Our camera object for viewing the scene
95         private Camera cam;
96
97         // the root node of the scene graph
98         private Node scene;
99
100         // display attributes for the window. We will keep these values
101         // to allow the user to change them
102         private int width, height, depth, freq;
103         private boolean fullscreen;
104
105         /**
106          * Main entry point of the application
107          */
108         public static void main(String[] args) {
109                 Lesson4 app = new Lesson4();
110                 // We will load our own "fantastic" Flag Rush logo. Yes, I'm an artist.
111                 app.setConfigShowMode(ConfigShowMode.AlwaysShow, Lesson4.class
112                                 .getClassLoader().getResource(
113                                                 "jmetest/data/images/FlagRush.png"));
114                 app.start();
115         }
116
117         /**
118          * During an update we look for the escape button and update the timer
119          * to get the framerate. Things are now starting to happen, so we will 
120      * update 
121          * 
122          * @see com.jme.app.BaseGame#update(float)
123          */
124         protected void update(float interpolation) {
125                 // update the time to get the framerate
126                 timer.update();
127                 interpolation = timer.getTimePerFrame();
128
129         //We will use the interpolation value to keep the speed
130         //of the forcefield consistent between computers.
131         //we update the Y have of the texture matrix to give
132         //the appearance the forcefield is moving.
133         t.getTranslation().y += 0.3f * interpolation;
134         //if the translation is over 1, it's wrapped, so go ahead
135         //and check for this (to keep the vector's y value from getting
136         //too large.)
137         if(t.getTranslation().y > 1) {
138             t.getTranslation().y = 0;
139         }
140                 
141         //we want to keep the skybox around our eyes, so move it with
142         //the camera
143                 skybox.setLocalTranslation(cam.getLocation());
144                 
145         // if escape was pressed, we exit
146                 if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
147                         finished = true;
148                 }
149         
150         //Because we are changing the scene (moving the skybox) we need to update
151         //the graph.
152         scene.updateGeometricState(interpolation, true);
153         }
154
155         /**
156          * draws the scene graph
157          * 
158          * @see com.jme.app.BaseGame#render(float)
159          */
160         protected void render(float interpolation) {
161                 // Clear the screen
162                 display.getRenderer().clearBuffers();
163                 display.getRenderer().draw(scene);
164         Debugger.drawBounds(scene, display.getRenderer());
165         }
166
167         /**
168          * initializes the display and camera.
169          * 
170          * @see com.jme.app.BaseGame#initSystem()
171          */
172         protected void initSystem() {
173                 // store the settings information
174                 width = settings.getWidth();
175                 height = settings.getHeight();
176                 depth = settings.getDepth();
177                 freq = settings.getFrequency();
178                 fullscreen = settings.isFullscreen();
179         
180                 try {
181                         display = DisplaySystem.getDisplaySystem(settings.getRenderer());
182                         display.createWindow(width, height, depth, freq, fullscreen);
183
184                         cam = display.getRenderer().createCamera(width, height);
185                 } catch (JmeException e) {
186             logger.log(Level.SEVERE, "Could not create displaySystem", e);
187                         System.exit(1);
188                 }
189
190                 // set the background to black
191                 display.getRenderer().setBackgroundColor(ColorRGBA.black.clone());
192
193                 // initialize the camera
194                 cam.setFrustumPerspective(45.0f, (float) width / (float) height, 1,
195                                 5000);
196                 Vector3f loc = new Vector3f(250.0f, 100.0f, 250.0f);
197                 Vector3f left = new Vector3f(-0.5f, 0.0f, 0.5f);
198                 Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
199                 Vector3f dir = new Vector3f(-0.5f, 0.0f, -0.5f);
200                 // Move our camera to a correct place and orientation.
201                 cam.setFrame(loc, left, up, dir);
202                 /** Signal that we've changed our camera's location/frustum. */
203                 cam.update();
204
205                 /** Get a high resolution timer for FPS updates. */
206                 timer = Timer.getTimer();
207
208                 display.getRenderer().setCamera(cam);
209
210                 KeyBindingManager.getKeyBindingManager().set("exit",
211                                 KeyInput.KEY_ESCAPE);
212         }
213
214         /**
215          * initializes the scene
216          * 
217          * @see com.jme.app.BaseGame#initGame()
218          */
219         protected void initGame() {
220         display.setTitle("Flag Rush");
221         
222                 scene = new Node("Scene graph node");
223                 /** Create a ZBuffer to display pixels closest to the camera above farther ones.  */
224             ZBufferState buf = display.getRenderer().createZBufferState();
225             buf.setEnabled(true);
226             buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
227             scene.setRenderState(buf);
228                 //Add terrain to the scene
229         buildTerrain();
230                 //Light the world
231             buildLighting();
232             //add the force field fence
233             buildEnvironment();
234             //Add the skybox
235             buildSkyBox();
236
237                 // update the scene graph for rendering
238                 scene.updateGeometricState(0.0f, true);
239                 scene.updateRenderState();
240         }
241         
242     /**
243      * buildEnvironment will create a fence. This is done by hand
244      * to show how to create geometry and shared this geometry.
245      * Normally, you wouldn't build your models by hand as it is
246      * too much of a trial and error process.
247      */
248         private void buildEnvironment() {
249         //This is the main node of our fence
250         Node forceFieldFence = new Node("fence");
251         
252         //This cylinder will act as the four main posts at each corner
253         Cylinder postGeometry = new Cylinder("post", 10, 10, 1, 10);
254         Quaternion q = new Quaternion();
255         //rotate the cylinder to be vertical
256         q.fromAngleAxis(FastMath.PI/2, new Vector3f(1,0,0));
257         postGeometry.setLocalRotation(q);
258         postGeometry.setModelBound(new BoundingBox());
259         postGeometry.updateModelBound();
260         
261         //We will share the post 4 times (one for each post)
262         //It is *not* a good idea to add the original geometry 
263         //as the sharedmeshes will alter its local values.
264         //We then translate the posts into position. 
265         //Magic numbers are bad, but help illustrate the point.:)
266         SharedMesh post1 = new SharedMesh("post1", postGeometry);
267         post1.setLocalTranslation(new Vector3f(0,0.5f,0));
268         SharedMesh post2 = new SharedMesh("post2", postGeometry);
269         post2.setLocalTranslation(new Vector3f(32,0.5f,0));
270         SharedMesh post3 = new SharedMesh("post3", postGeometry);
271         post3.setLocalTranslation(new Vector3f(0,0.5f,32));
272         SharedMesh post4 = new SharedMesh("post4", postGeometry);
273         post4.setLocalTranslation(new Vector3f(32,0.5f,32));
274         
275         //This cylinder will be the horizontal struts that hold
276         //the field in place.
277         Cylinder strutGeometry = new Cylinder("strut", 10,10, 0.125f, 32);
278         strutGeometry.setModelBound(new BoundingBox());
279         strutGeometry.updateModelBound();
280         
281         //again, we'll share this mesh.
282         //Some we need to rotate to connect various posts.
283         SharedMesh strut1 = new SharedMesh("strut1", strutGeometry);
284         Quaternion rotate90 = new Quaternion();
285         rotate90.fromAngleAxis(FastMath.PI/2, new Vector3f(0,1,0));
286         strut1.setLocalRotation(rotate90);
287         strut1.setLocalTranslation(new Vector3f(16,3f,0));
288         SharedMesh strut2 = new SharedMesh("strut2", strutGeometry);
289         strut2.setLocalTranslation(new Vector3f(0,3f,16));
290         SharedMesh strut3 = new SharedMesh("strut3", strutGeometry);
291         strut3.setLocalTranslation(new Vector3f(32,3f,16));
292         SharedMesh strut4 = new SharedMesh("strut4", strutGeometry);
293         strut4.setLocalRotation(rotate90);
294         strut4.setLocalTranslation(new Vector3f(16,3f,32));
295         
296         //Create the actual forcefield 
297         //The first box handles the X-axis, the second handles the z-axis.
298         //We don't rotate the box as a demonstration on how boxes can be 
299         //created differently.
300         Box forceFieldX = new Box("forceFieldX", new Vector3f(-16, -3f, -0.1f), new Vector3f(16f, 3f, 0.1f));
301         forceFieldX.setModelBound(new BoundingBox());
302         forceFieldX.updateModelBound();
303         //We are going to share these boxes as well
304         SharedMesh forceFieldX1 = new SharedMesh("forceFieldX1",forceFieldX);
305         forceFieldX1.setLocalTranslation(new Vector3f(16,0,0));
306         SharedMesh forceFieldX2 = new SharedMesh("forceFieldX2",forceFieldX);
307         forceFieldX2.setLocalTranslation(new Vector3f(16,0,32));
308         
309         //The other box for the Z axis
310         Box forceFieldZ = new Box("forceFieldZ", new Vector3f(-0.1f, -3f, -16), new Vector3f(0.1f, 3f, 16));
311         forceFieldZ.setModelBound(new BoundingBox());
312         forceFieldZ.updateModelBound();
313         //and again we will share it
314         SharedMesh forceFieldZ1 = new SharedMesh("forceFieldZ1",forceFieldZ);
315         forceFieldZ1.setLocalTranslation(new Vector3f(0,0,16));
316         SharedMesh forceFieldZ2 = new SharedMesh("forceFieldZ2",forceFieldZ);
317         forceFieldZ2.setLocalTranslation(new Vector3f(32,0,16));
318         
319         //add all the force fields to a single node and make this node part of
320         //the transparent queue.
321         Node forceFieldNode = new Node("forceFieldNode");
322         forceFieldNode.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
323         forceFieldNode.attachChild(forceFieldX1);
324         forceFieldNode.attachChild(forceFieldX2);
325         forceFieldNode.attachChild(forceFieldZ1);
326         forceFieldNode.attachChild(forceFieldZ2);
327         
328         //Add the alpha values for the transparent node
329         BlendState as1 = display.getRenderer().createBlendState();
330         as1.setBlendEnabled(true);
331         as1.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
332         as1.setDestinationFunction(BlendState.DestinationFunction.One);
333         as1.setTestEnabled(true);
334         as1.setTestFunction(BlendState.TestFunction.GreaterThan);
335         as1.setEnabled(true);
336         
337         forceFieldNode.setRenderState(as1);
338         
339         //load a texture for the force field elements
340         TextureState ts = display.getRenderer().createTextureState();
341         t = TextureManager.loadTexture(Lesson2.class.getClassLoader()
342                   .getResource("jmetest/data/texture/reflector.jpg"),
343                   Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear);
344         
345         t.setWrap(Texture.WrapMode.Repeat);
346         t.setTranslation(new Vector3f());
347         ts.setTexture(t);
348         
349         forceFieldNode.setRenderState(ts);
350         
351        
352         //put all the posts into a tower node
353         Node towerNode = new Node("tower");
354         towerNode.attachChild(post1);
355         towerNode.attachChild(post2);
356         towerNode.attachChild(post3);
357         towerNode.attachChild(post4);
358         
359         //add the tower to the opaque queue (we don't want to be able to see through them)
360         //and we do want to see them through the forcefield.
361         towerNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
362         
363         //load a texture for the towers
364         TextureState ts2 = display.getRenderer().createTextureState();
365         Texture t2 = TextureManager.loadTexture(Lesson2.class.getClassLoader()
366                   .getResource("jmetest/data/texture/post.jpg"),
367                   Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear);
368         
369         ts2.setTexture(t2);
370         
371         towerNode.setRenderState(ts2);
372         
373         //put all the struts into a single node.
374         Node strutNode = new Node("strutNode");
375         strutNode.attachChild(strut1);
376         strutNode.attachChild(strut2);
377         strutNode.attachChild(strut3);
378         strutNode.attachChild(strut4);
379         //this too is in the opaque queue.
380         strutNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
381         
382         //load a texture for the struts
383         TextureState ts3 = display.getRenderer().createTextureState();
384         Texture t3 = TextureManager.loadTexture(Lesson2.class.getClassLoader()
385                   .getResource("jmetest/data/texture/rust.jpg"),
386                   Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear);
387         
388         ts3.setTexture(t3);
389         
390         strutNode.setRenderState(ts3);
391         
392         //we will do a little 'tweaking' by hand to make it fit in the terrain a bit better.
393         //first we'll scale the entire "model" by a factor of 5
394         forceFieldFence.setLocalScale(new Vector3f(5,4,4));
395         //now let's move the fence to to the height of the terrain and in a little bit.
396         forceFieldFence.setLocalTranslation(new Vector3f(25, tb.getHeight(25,25) + 15, 25));
397         
398         //Attach all the pieces to the main fence node
399         forceFieldFence.attachChild(forceFieldNode);
400         forceFieldFence.attachChild(towerNode);
401         forceFieldFence.attachChild(strutNode);
402         
403         scene.attachChild(forceFieldFence);
404         }
405
406         /**
407          * creates a light for the terrain.
408          */
409         private void buildLighting() {
410                 /** Set up a basic, default light. */
411             DirectionalLight light = new DirectionalLight();
412             light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
413             light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
414             light.setDirection(new Vector3f(1,-1,0));
415             light.setEnabled(true);
416
417               /** Attach the light to a lightState and the lightState to rootNode. */
418             LightState lightState = display.getRenderer().createLightState();
419             lightState.setEnabled(true);
420             lightState.attach(light);
421             scene.setRenderState(lightState);
422         }
423
424         /**
425          * build the height map and terrain block.
426          */
427         private void buildTerrain() {
428                 
429                 
430         MidPointHeightMap heightMap = new MidPointHeightMap(64, 1f);
431         // Scale the data
432         Vector3f terrainScale = new Vector3f(4, 0.0575f, 4);
433         // create a terrainblock
434          tb = new TerrainBlock("Terrain", heightMap.getSize(), terrainScale,
435                 heightMap.getHeightMap(), new Vector3f(0, 0, 0));
436
437         tb.setModelBound(new BoundingBox());
438         tb.updateModelBound();
439
440         // generate a terrain texture with 2 textures
441         ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
442                 heightMap);
443         pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
444                 .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
445         pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
446                 .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
447         pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
448                 .getResource("jmetest/data/texture/highest.jpg")), 128, 255,
449                 384);
450         pt.createTexture(32);
451         
452         // assign the texture to the terrain
453         TextureState ts = display.getRenderer().createTextureState();
454         Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
455                 Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
456         ts.setTexture(t1, 0);
457
458         tb.setRenderState(ts);
459         tb.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
460         scene.attachChild(tb);
461         
462         
463         }
464         
465     /**
466      * buildSkyBox creates a new skybox object with all the proper textures. The
467      * textures used are the standard skybox textures from all the tests.
468      *
469      */
470         private void buildSkyBox() {
471         skybox = new Skybox("skybox", 10, 10, 10);
472
473         Texture north = TextureManager.loadTexture(
474             TestSkybox.class.getClassLoader().getResource(
475             "jmetest/data/texture/north.jpg"),
476             Texture.MinificationFilter.BilinearNearestMipMap,
477             Texture.MagnificationFilter.Bilinear);
478         Texture south = TextureManager.loadTexture(
479             TestSkybox.class.getClassLoader().getResource(
480             "jmetest/data/texture/south.jpg"),
481             Texture.MinificationFilter.BilinearNearestMipMap,
482             Texture.MagnificationFilter.Bilinear);
483         Texture east = TextureManager.loadTexture(
484             TestSkybox.class.getClassLoader().getResource(
485             "jmetest/data/texture/east.jpg"),
486             Texture.MinificationFilter.BilinearNearestMipMap,
487             Texture.MagnificationFilter.Bilinear);
488         Texture west = TextureManager.loadTexture(
489             TestSkybox.class.getClassLoader().getResource(
490             "jmetest/data/texture/west.jpg"),
491             Texture.MinificationFilter.BilinearNearestMipMap,
492             Texture.MagnificationFilter.Bilinear);
493         Texture up = TextureManager.loadTexture(
494             TestSkybox.class.getClassLoader().getResource(
495             "jmetest/data/texture/top.jpg"),
496             Texture.MinificationFilter.BilinearNearestMipMap,
497             Texture.MagnificationFilter.Bilinear);
498         Texture down = TextureManager.loadTexture(
499             TestSkybox.class.getClassLoader().getResource(
500             "jmetest/data/texture/bottom.jpg"),
501             Texture.MinificationFilter.BilinearNearestMipMap,
502             Texture.MagnificationFilter.Bilinear);
503
504         skybox.setTexture(Skybox.Face.North, north);
505         skybox.setTexture(Skybox.Face.West, west);
506         skybox.setTexture(Skybox.Face.South, south);
507         skybox.setTexture(Skybox.Face.East, east);
508         skybox.setTexture(Skybox.Face.Up, up);
509         skybox.setTexture(Skybox.Face.Down, down);
510         skybox.preloadTextures();
511         scene.attachChild(skybox);
512         }
513
514         /**
515          * will be called if the resolution changes
516          * 
517          * @see com.jme.app.BaseGame#reinit()
518          */
519         protected void reinit() {
520                 display.recreateWindow(width, height, depth, freq, fullscreen);
521         }
522     
523     /**
524      * close the window and also exit the program.
525      */
526     protected void quit() {
527         super.quit();
528         System.exit(0);
529     }
530
531         /**
532          * clean up the textures.
533          * 
534          * @see com.jme.app.BaseGame#cleanup()
535          */
536         protected void cleanup() {
537
538         }
539 }