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.flagrushtut.lesson9;
35 import jmetest.effects.cloth.TestCloth;
37 import com.jme.image.Texture;
38 import com.jme.light.LightNode;
39 import com.jme.light.PointLight;
40 import com.jme.math.FastMath;
41 import com.jme.math.Quaternion;
42 import com.jme.math.Vector3f;
43 import com.jme.math.spring.SpringPoint;
44 import com.jme.math.spring.SpringPointForce;
45 import com.jme.renderer.ColorRGBA;
46 import com.jme.renderer.Renderer;
47 import com.jme.scene.Node;
48 import com.jme.scene.shape.Cylinder;
49 import com.jme.scene.state.CullState;
50 import com.jme.scene.state.LightState;
51 import com.jme.scene.state.TextureState;
52 import com.jme.system.DisplaySystem;
53 import com.jme.util.TextureManager;
54 import com.jmex.effects.cloth.ClothPatch;
55 import com.jmex.effects.cloth.ClothUtils;
56 import com.jmex.terrain.TerrainBlock;
59 * Flag maintains the object that is the "goal" of the game. The
60 * drivers are to try to grab the flags for points. The main job of
61 * the class is to build the flag geometry, and position itself randomly
62 * within the level after a period of time.
66 public class Flag extends Node{
67 private static final long serialVersionUID = 1L;
70 private static final int LIFE_TIME = 10;
71 //start off with a full life time
72 float countdown = LIFE_TIME;
73 //reference to the level terrain for placement
75 //the cloth that makes up the flag.
76 private ClothPatch cloth;
77 //parameters for the wind
78 private float windStrength = 15f;
79 private Vector3f windDirection = new Vector3f(0.8f, 0, 0.2f);
80 private SpringPointForce gravity, wind;
83 * Constructor builds the flag, taking the terrain as the parameter. This
84 * is just the reference to the game's terrain object so that we can
85 * randomly place this flag on the level.
86 * @param tb the terrain used to place the flag.
88 public Flag(TerrainBlock tb) {
91 //create a cloth patch that will handle the flag part of our flag.
92 cloth = new ClothPatch("cloth", 25, 25, 1f, 10);
93 // Add our custom flag wind force to the cloth
94 wind = new RandomFlagWindForce(windStrength, windDirection);
96 // Add a simple gravitational force:
97 gravity = ClothUtils.createBasicGravity();
98 cloth.addForce(gravity);
100 //Create the flag pole
101 Cylinder c = new Cylinder("pole", 10, 10, 0.5f, 50 );
103 Quaternion q = new Quaternion();
104 //rotate the cylinder to be vertical
105 q.fromAngleAxis(FastMath.PI/2, new Vector3f(1,0,0));
106 c.setLocalRotation(q);
107 c.setLocalTranslation(new Vector3f(-12.5f,-12.5f,0));
109 //create a texture that the flag will display.
111 TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
113 TextureManager.loadTexture(
114 TestCloth.class.getClassLoader().getResource(
115 "jmetest/data/images/Monkey.jpg"),
116 Texture.MinificationFilter.Trilinear,
117 Texture.MagnificationFilter.Bilinear));
119 //We'll use a LightNode to give more lighting to the flag, we use the node because
120 //it will allow it to move with the flag as it hops around.
121 //first create the light
122 PointLight dr = new PointLight();
123 dr.setEnabled( true );
124 dr.setDiffuse( new ColorRGBA( 1.0f, 1.0f, 1.0f, 1.0f ) );
125 dr.setAmbient( new ColorRGBA( 0.5f, 0.5f, 0.5f, 1.0f ) );
126 dr.setLocation( new Vector3f( 0.5f, -0.5f, 0 ) );
128 LightState lightState = DisplaySystem.getDisplaySystem().getRenderer().createLightState();
129 lightState.setEnabled(true);
130 lightState.setTwoSidedLighting( true );
131 lightState.attach(dr);
133 LightNode lightNode = new LightNode( "light" );
134 lightNode.setLight( dr );
135 lightNode.setLocalTranslation(new Vector3f(15,10,0));
137 this.setRenderState(lightState);
138 this.attachChild(lightNode);
140 cloth.setRenderState(ts);
141 //We want to see both sides of the flag, so we will turn back facing culling OFF.
142 CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
143 cs.setCullFace(CullState.Face.None);
144 cloth.setRenderState(cs);
145 this.attachChild(cloth);
147 //We need to attach a few points of the cloth to the poll. These points shouldn't
148 //ever move. So, we'll attach five points at the top and 5 at the bottom.
149 //to make them not move the mass has to be high enough that no force can move it.
150 //I also move the position of these points slightly to help bunch up the flag to
151 //give it better realism.
152 for (int i = 0; i < 5; i++) {
153 cloth.getSystem().getNode(i*25).position.y *= .8f;
154 cloth.getSystem().getNode(i*25).setMass(Float.POSITIVE_INFINITY);
158 for (int i = 24; i > 19; i--) {
159 cloth.getSystem().getNode(i*25).position.y *= .8f;
160 cloth.getSystem().getNode(i*25).setMass(Float.POSITIVE_INFINITY);
163 this.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
164 this.setLocalScale(0.25f);
169 * During the update, we decrement the time. When it reaches zero, we will
171 * @param time the time between frame.
173 public void update(float time) {
182 * reset sets the life time back to 10 seconds, and then randomly places the flag
186 public void reset() {
187 countdown = LIFE_TIME;
192 * place flag picks a random point on the terrain and places the flag there. I
193 * set the values to be between (45 and 175) which places it within the force field
197 public void placeFlag() {
198 float x = 45 + FastMath.nextRandomFloat() * 130;
199 float z = 45 + FastMath.nextRandomFloat() * 130;
200 float y = tb.getHeight(x,z) + 7.5f;
201 localTranslation.x = x;
202 localTranslation.y = y;
203 localTranslation.z = z;
208 * RandomFlagWindForce defines a SpringPointForce that will slighly adjust the
209 * direction of the wind and the force of the wind. This will cause the flag
210 * to flap in the wind and rotate about the flag pole slightly, giving it a
211 * realistic movement.
212 * @author Mark Powell
215 private class RandomFlagWindForce extends SpringPointForce{
217 private final float strength;
218 private final Vector3f windDirection;
221 * Creates a new force with a defined max strength and a starting direction.
222 * @param strength the maximum strength of the wind.
223 * @param direction the starting direction of the wind.
225 public RandomFlagWindForce(float strength, Vector3f direction) {
226 this.strength = strength;
227 this.windDirection = direction;
231 * called during the update of the cloth. Will adjust the direction slightly
232 * and adjust the strength slightly.
234 public void apply(float dt, SpringPoint node) {
235 windDirection.x += dt * (FastMath.nextRandomFloat() - 0.5f);
236 windDirection.z += dt * (FastMath.nextRandomFloat() - 0.5f);
237 windDirection.normalize();
238 float tStr = FastMath.nextRandomFloat() * strength;
239 node.acceleration.addLocal(windDirection.x * tStr, windDirection.y
240 * tStr, windDirection.z * tStr);