package metro.adam.tank;
import java.net.URL;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.logging.Level;
import javax.swing.ImageIcon;
import jmetest.TutorialGuide.HelloIntersection;
import jmetest.terrain.TestTerrain;
//import jmetest.TutorialGuide.HelloIntersection.BulletMover;
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Texture;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.Line;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.Text;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Cylinder;
import com.jme.scene.shape.Dome;
import com.jme.scene.shape.Sphere;
import com.jme.scene.shape.Pyramid;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.util.LoggingSystem;
import com.jme.util.TextureManager;
import com.jme.util.geom.BufferUtils;
import com.jmex.sound.openAL.SoundSystem;
import com.jmex.terrain.TerrainBlock;
import com.jmex.terrain.util.MidPointHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;
public class Tank extends SimpleGame{
public static void main(String[] args) {
Tank app=new Tank(); // Create Object
app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
// Turn the logger off so we can see the XML later on
LoggingSystem.getLogger().setLevel(Level.OFF);
// Signal to show properties dialog
app.start(); // Start the program
}
private Node tankTurret;
private float currentPitch = -30.0f;
private float currentYaw = 30.0f;
private Node missleTrajectory = new Node("missleTrajectory");
private static final float BARREL_LENGTH = 1.8f;
private static final float BARREL_ELEVATION = .3f;
private Vector3f barrelPosition = new Vector3f(0.0f,BARREL_ELEVATION,1.1f);
private Text gameStats = new Text("gameStats", "yaw: "+ currentYaw+ "\npitcdh: "+ currentPitch +"\n");
private Cylinder tankTurretBarrel;
//DDaniels added 05/20/2006
private static final String HIT = "HIT!!";
private static final String NO_HIT = "NO HIT";
private Text tankHUD = new Text("tankHUD", "");
private Text hitStatus = new Text("hitStatus", NO_HIT);
private Line trajectoryLine;
private Node trajectoryNode = new Node("trajectorynode");
private Node tankNode;
//The target getting hit
private TriMesh target;
/** Used to move target location on a hit */
Random r = new Random();
private TerrainBlock tb;
private float agl = 0.0f;
protected void simpleInitGame()
{
//Sphere dome = new Sphere("TankDome", new Vector3f(-1.0f,1.0f,0.5f),16,16,.5f);
TriMesh dome = new Dome("TankDome", new Vector3f(0, 0, 0), 16, 16, .5f);
dome.setLocalTranslation(new Vector3f(-1.0f,1.0f,0.5f));
//Cylinder canon = new Cylinder("Canon", 16, 16, 0.25f, 3.0f,true);
TriMesh mesh = new TriMesh("TankMesh");
Vector3f[] vertexes = {
new Vector3f(1.0f,.5f,0.0f),
new Vector3f(0.0f,0.0f,0.0f),
new Vector3f(0.0f,1.0f,0.0f),
new Vector3f(-2.0f,0.0f,0.0f),
new Vector3f(-2.0f,1.0f,0.0f),
new Vector3f(-3.0f,0.5f,0.0f),
new Vector3f(1.0f,.5f,1.0f),
new Vector3f(0.0f,0.0f,1.0f),
new Vector3f(0.0f,1.0f,1.0f),
new Vector3f(-2.0f,0.0f,1.0f),
new Vector3f(-2.0f,1.0f,1.0f),
new Vector3f(-3.0f,0.5f,1.0f)};
Vector3f[] normals={
new Vector3f(0,0,-1),
new Vector3f(0,0,-1),
new Vector3f(0,0,-1),
new Vector3f(0,0,-1),
new Vector3f(0,0,-1),
new Vector3f(0,0,-1),
new Vector3f(0,0,1),
new Vector3f(0,0,1),
new Vector3f(0,0,1),
new Vector3f(0,0,1),
new Vector3f(0,0,1),
new Vector3f(0,0,1)};
int[] indexes = {0,1,2,2,3,1,2,4,3,4,5,3,6,7,8,8,9,7,
8,10,9,10,11,9,1,7,9,1,3,9,9,3,11,3,11,5,
6,0,8,8,2,0,8,10,2,10,4,2,4,11,5,10,4,11,1,7,6,0,6,1};
mesh.reconstruct(BufferUtils.createFloatBuffer(vertexes),
BufferUtils.createFloatBuffer(normals),
null, null,BufferUtils.createIntBuffer(indexes));
mesh.setModelBound(new BoundingBox());
mesh.updateModelBound();
tankNode = new Node("TankNode");
tankTurret = new Node("tankTurret");
tankTurretBarrel = new Cylinder("tankTurretBarrel", 16, 16, .1f, BARREL_LENGTH );
tankTurretBarrel.setModelBound(new BoundingBox());
tankTurretBarrel.updateModelBound();
tankTurretBarrel.setLocalTranslation(barrelPosition);
tankTurret.attachChild(tankTurretBarrel);
//tankTurret.attachChild(dome);
tankTurret.setLocalTranslation(new Vector3f(-1.0f,1.0f,0.5f));
//Text code
tankHUD.setLocalTranslation(new Vector3f(display.getWidth() / 2f - 8f,
30, 0));
tankHUD.print("yaw: "+ currentYaw+ "\npitch: "+ currentPitch +"\npower:"+Vm);
hitStatus.setLocalTranslation(new Vector3f(display.getWidth() / 2f - 8f,
60, 0));
target = new Sphere("my sphere", 15, 15, 1);
target.setModelBound(new BoundingSphere());
target.updateModelBound();
target.setLocalTranslation(new Vector3f(r.nextFloat() * 10, r
.nextFloat() * 10, r.nextFloat() * 10));
//ddaniels
Vector3f lineV[] = {new Vector3f(0.0f, 0.0f, 0.0f)};
trajectoryLine = new Line("Trajectory",lineV, null, null,null);
trajectoryNode.attachChild(trajectoryLine);
tankNode.attachChild(mesh);
tankNode.attachChild(dome);
tankNode.attachChild(tankTurret);
tankNode.setLocalScale(2);
tankNode.setLocalTranslation(new Vector3f(100,0, 100));
tankNode.updateWorldBound();
//we now store this initial value, because we are rotating the wheels the bounding box will
//change each frame.
agl = ((BoundingBox)tankNode.getWorldBound()).yExtent;
System.out.println("agl="+agl);
//Add terrain to the scene
buildTerrain();
//Add lighting
buildLighting();
rootNode.attachChild(tankNode);
//rootNode.attachChild(missleTrajectory);
rootNode.attachChild(target);
rootNode.attachChild(trajectoryNode);
rootNode.attachChild(posBox);
fpsNode.attachChild(tankHUD);
fpsNode.attachChild(hitStatus);
/** Create a skybox to suround our world */
Skybox sb = new Skybox("skybox", 500, 500, 500);
URL monkeyLoc = HelloIntersection.class.getClassLoader().getResource(
"data/texture/sky4stars-a_original.jpg");
TextureState ts = display.getRenderer().createTextureState();
ts.setTexture(TextureManager.loadTexture(monkeyLoc, Texture.MM_LINEAR,
Texture.FM_LINEAR));
sb.setRenderState(ts);
// Attach the skybox to our root node, and force the rootnode to show
// so that the skybox will always show
rootNode.attachChild(sb);
rootNode.setCullMode(Spatial.CULL_NEVER);
rootNode.setLocalTranslation(new Vector3f(-20f, -60f, -20f));
// Assign the "+" key on the keypad to the command "coordsUp"
KeyBindingManager.getKeyBindingManager().set(
"pitchDown",
KeyInput.KEY_U);
// Adds the "u" key to the command "coordsUp"
KeyBindingManager.getKeyBindingManager().set(
"yawRight",
KeyInput.KEY_K);
// Assign the "-" key on the keypad to the command "coordsDown"
KeyBindingManager.getKeyBindingManager().set(
"pitchUp",
KeyInput.KEY_J);
// Adds the "d" key to the command "coordsDown"
KeyBindingManager.getKeyBindingManager().set(
"yawLeft",
KeyInput.KEY_H);
KeyBindingManager.getKeyBindingManager().set(
"fire",
KeyInput.KEY_SPACE);
KeyBindingManager.getKeyBindingManager().set(
"powerUp",
KeyInput.KEY_Z);
KeyBindingManager.getKeyBindingManager().set(
"powerDown",
KeyInput.KEY_X);
setTurretAngle();
}
/**
* creates a light for the terrain.
*/
private void buildLighting() {
/** Set up a basic, default light. */
DirectionalLight light = new DirectionalLight();
light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, .5f));
light.setDirection(new Vector3f(1,-1,0));
light.setShadowCaster(true);
light.setEnabled(true);
/** Attach the light to a lightState and the lightState to rootNode. */
LightState lightState = display.getRenderer().createLightState();
lightState.setEnabled(true);
lightState.setGlobalAmbient(new ColorRGBA(.2f, .2f, .2f, 1f));
lightState.attach(light);
rootNode.setRenderState(lightState);
}
/**
* build the height map and terrain block.
*/
private void buildTerrain() {
MidPointHeightMap heightMap = new MidPointHeightMap(64, 1f);
// Scale the data
Vector3f terrainScale = new Vector3f(4, 0.1575f, 4);
// create a terraiwblock
tb = new TerrainBlock("Terrain", heightMap.getSize(), terrainScale,
heightMap.getHeightMap(), new Vector3f(0, 0, 0), false);
//tb.setLocalTranslation(new Vector3f(-20, -60, -20));
tb.setModelBound(new BoundingBox());
tb.updateModelBound();
// generate a terrain texture with 2 textures
ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
heightMap);
pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
.getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
.getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
.getResource("jmetest/data/texture/highest.jpg")), 128, 255,
384);
pt.createTexture(32);
// assign the texture to the terrain
TextureState ts = display.getRenderer().createTextureState();
Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, true);
ts.setTexture(t1, 0);
//load a detail texture and set the combine modes for the two terrain textures.
Texture t2 = TextureManager.loadTexture(
TestTerrain.class.getClassLoader().getResource(
"jmetest/data/texture/Detail.jpg"),
Texture.MM_LINEAR_LINEAR,
Texture.FM_LINEAR);
ts.setTexture(t2, 1);
t2.setWrap(Texture.WM_WRAP_S_WRAP_T);
t1.setApply(Texture.AM_COMBINE);
t1.setCombineFuncRGB(Texture.ACF_MODULATE);
t1.setCombineSrc0RGB(Texture.ACS_TEXTURE);
t1.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
t1.setCombineSrc1RGB(Texture.ACS_PRIMARY_COLOR);
t1.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
t1.setCombineScaleRGB(1.0f);
t2.setApply(Texture.AM_COMBINE);
t2.setCombineFuncRGB(Texture.ACF_ADD_SIGNED);
t2.setCombineSrc0RGB(Texture.ACS_TEXTURE);
t2.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
t2.setCombineSrc1RGB(Texture.ACS_PREVIOUS);
t2.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
t2.setCombineScaleRGB(1.0f);
tb.setRenderState(ts);
//set the detail parameters.
tb.setDetailTexture(1, 16);
tb.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
rootNode.attachChild(tb);
}
private static final float MIN_PITCH = -90;
private static final float MAX_PITCH = 25;
private boolean fired = false;
private Vector3f normal = new Vector3f();
private void updateTankHeight(){
//make sure that if the player left the level we don't crash. When we add collisions,
//the fence will do its job and keep the player inside.
Vector3f tankPos = tankNode.getLocalTranslation();
//tankPos.x -= 20;
//tankPos.y -= 60;
//tankPos.z -= 20;
float characterMinHeight = tb.getHeight(tankPos)+agl;
//System.out.println("MinHeight="+characterMinHeight);
if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
tankNode.setLocalTranslation(new Vector3f(tankPos.x,characterMinHeight, tankPos.z )) ;
}
//get the normal of the terrain at our current location. We then apply it to the up vector
//of the player.
tb.getSurfaceNormal(tankNode.getLocalTranslation(), normal);
if(normal != null) {
tankNode.rotateUpTo(normal);
}
//rootNode.updateWorldTranslation();
//Because we are changing the scene (moving the skybox and player) we need to update
//the graph.
//rootNode.updateGeometricState();
}
// Called every frame update
protected void simpleUpdate(){
//Remove any
comittNodeRemoval();
boolean updateTurret = false;
//update Tank position
updateTankHeight();
// If the coordsDown command was activated
if (KeyBindingManager.getKeyBindingManager().isValidCommand("pitchDown",true)){
currentPitch -= .5f;
if(currentPitch<MIN_PITCH){
currentPitch = MIN_PITCH;
}
updateTurret = true;
}
// if the coordsUp command was activated
if (KeyBindingManager.getKeyBindingManager().isValidCommand("pitchUp",true)){
currentPitch += .5f;
if(currentPitch>MAX_PITCH){
currentPitch = MAX_PITCH;
}
updateTurret = true;
}
if (KeyBindingManager.getKeyBindingManager().isValidCommand("yawRight",true)){
currentYaw -= .5f;
/*if(currentYaw<=0)
currentYaw = 360;*/
updateTurret = true;
}
if (KeyBindingManager.getKeyBindingManager().isValidCommand("yawLeft",true)){
currentYaw += .5f;
/*if(currentYaw>=360)
currentYaw=0;*/
updateTurret = true;
}
// jhurst 05/20/06 changed from "true" to "false"
if (KeyBindingManager.getKeyBindingManager().isValidCommand("fire",false) && !fired){
fire();
fired = true;
}
if(KeyBindingManager.getKeyBindingManager().isValidCommand("powerUp",true)){
Vm += 1.0f;
updateTurret = true;
if(Vm>50.0f){
Vm = 50.0f;
}
}
if(KeyBindingManager.getKeyBindingManager().isValidCommand("powerDown",true)){
Vm -= 1.0f;
updateTurret = true;
if(Vm<10.0f){
Vm = 10.0f;
}
}
else{
fired = false;
}
if (updateTurret) {
setTurretAngle();
tankHUD.print("yaw: "+ currentYaw+ "\npitch: "+ currentPitch +"\npower:"+Vm);
}
}
private static final float METERS_TO_SIMULATION_UNITS = 0.5f;
private static final float GRAVITY = -9.8f * METERS_TO_SIMULATION_UNITS;
private static final float TIME_INCREMENT = 0.15f;
private float Vm = 25.0f; //muzzle velcoity
private Box posBox = new Box("posBox", new Vector3f(-.5f, -.5f, -.5f), new Vector3f(.5f, .5f, .5f));
private void fire(){
//System.out.println("Firing!!");
System.out.println("Yaw="+currentYaw + "Pitch="+ currentPitch + " Vm="+Vm);
posBox.setLocalTranslation(tankTurretBarrel.getWorldTranslation());
// jhurst 05/20/06 Commented out
/* missleTrajectory.removeFromParent();
missleTrajectory = new Node("missleTrajectory");
//Initial positions - should use muzzle position but whatever
float x0 = 0.0f;
float y0 = 0.0f;
float z0 = 0.0f;
float velX = (float)(velocity*METERS_TO_SIMULATION_UNITS*Math.cos(FastMath.DEG_TO_RAD*currentPitch));
float velY =(float)(velocity*METERS_TO_SIMULATION_UNITS*-Math.sin(FastMath.DEG_TO_RAD*currentPitch));
float velZ = (float)(velocity*METERS_TO_SIMULATION_UNITS*Math.cos(FastMath.DEG_TO_RAD*currentYaw));
System.out.println( "velX="+velX+" velY="+velY+" velZ="+velZ);
*/
//time
float t = 0.0f;
float dx = 0.0f;
float dy = BARREL_ELEVATION;
float dz = 0.0f;
float cosX;
float cosY;
float cosZ;
float xe;
float ze;
float b, Lx, Ly, Lz, Yb;
float L = 1.0f;
Quaternion turretAngle = tankTurretBarrel.getWorldRotation();
float angles[] = turretAngle.toAngles(null);
System.out.println("angles[0]="+angles[0]*FastMath.RAD_TO_DEG);
System.out.println("angles[1]="+angles[1]*FastMath.RAD_TO_DEG);
System.out.println("angles[2]="+angles[2]*FastMath.RAD_TO_DEG);
float Alpha = -(angles[0]*FastMath.RAD_TO_DEG) - 90;
float Gamma = -(angles[1]*FastMath.RAD_TO_DEG) - 90;
b = (float)(L * Math.cos((90-Alpha) * FastMath.DEG_TO_RAD)); //projection of barrel onto xz plane
Lx = (float)(b * Math.cos(Gamma * FastMath.DEG_TO_RAD)); // x-component barrel of gun
Ly = (float)(L * Math.cos(Alpha * FastMath.DEG_TO_RAD)); //y-component
Lz = (float)(b * Math.sin(Gamma * FastMath.DEG_TO_RAD)); // x-component barrel of gun
Yb = BARREL_ELEVATION;
cosX = Lx/L;
cosY = Ly/L;
cosZ = Lz/L;
//x- and z- coordinates at end of barrel
//dx = xe = (float)(L*Math.cos((90-Alpha)*FastMath.DEG_TO_RAD) * Math.cos(Gamma * FastMath.DEG_TO_RAD));
//dz = ze = (float)(L*Math.cos((90-Alpha)*FastMath.DEG_TO_RAD) * Math.sin(Gamma * FastMath.DEG_TO_RAD));
Vector3f position = tankTurretBarrel.getWorldTranslation();
System.out.println("tankNode.getWorldTranslation="+tankNode.getWorldTranslation());
System.out.println("tankTurretBarrel.getWorldTranslation="+this.tankTurretBarrel.getWorldTranslation());
//DDANIELS HACK = translate to reverse terrain positioning translation, (-20, -60, -20)
dx = xe = position.x + 20;
dy = Yb = position.y + 60;
dz = ze = position.z + 20;
// jhurst 05/20/06 Commented out
//while(dy>=0){
// Box box = new Box("position"+t, new Vector3f(-.5f, -.5f, -.5f), new Vector3f(.5f, .5f, .5f));
// box.setLocalTranslation(new Vector3f(dx, dy, dz));
// box.setLocalScale(.15f);
// box.setDefaultColor(ColorRGBA.red);
// box.setModelBound(new BoundingBox());
// box.updateModelBound();
// missleTrajectory.attachChild(box);
// //update positionss
// t+=TIME_INCREMENT;
// dx = Vm * cosX * t + xe;
// dy = (float)((Yb + L * Math.cos(Alpha*FastMath.DEG_TO_RAD))+ (Vm * cosY * t) + (0.5f * GRAVITY*t*t));
// dz = Vm * cosZ * t + ze;
//System.out.println("t="+t+" dx="+dx+" dy="+dy+" dz="+dz);
//}
// jhurst 05/20/06 I reorganized the code above to make the new
// firing method work. Instead of dealing with creating a box along
// the trajectory, like doug implemented to start, I just take the
// vector representing the initial motion of the block and use that to
// create a box with a BulletMover controller that manages the movement
// for me. ROCK
// Get the initial velocity components based on the muzzel velocity
// Vm and the pitch/yaw of the barrel
dx = Vm * cosX + xe;
dy = (float)((Yb + L * Math.cos(Alpha*FastMath.DEG_TO_RAD))+ (Vm * cosY));
dz = Vm * cosZ + ze;
// Create the bullet and translate it to be in the center of the barrel.
// This doesn't end up looking all that great at the moment, but its a
// start. Also, set the size, color and bounding box of the bullet.
Box box = new Box("position"+t, new Vector3f(-.5f, -.5f, -.5f), new Vector3f(.5f, .5f, .5f));
box.setLocalTranslation(new Vector3f(xe, Yb, ze));
box.setLocalScale(3f);
box.setDefaultColor(ColorRGBA.red);
box.setModelBound(new BoundingBox());
box.updateModelBound();
// Create a node for the missle trajectory. This makes it possible to apply
// a light state to the bullets. Now that I think of it, this could probably
// be removed by applying the LightState to the rootNode... but I don't really
// know... too tired to try.
Node missleTrajectory2 = new Node("missleTrajectory");
missleTrajectory2.attachChild(box);
// Don't know what this is doing really.... see comments below
LightState ls = display.getRenderer().createLightState();
ls.setEnabled(false);
missleTrajectory2.setRenderState(ls);
missleTrajectory2.updateRenderState();
// Add the controller class to the box. Notice, I am passing the
// missleTrajectory2 node to the BulletMover class. This is so the
// bullet can be removed from the rootNode when it times out. If
// I could remove the missleTrajectory2 junk, I could make it a lot simpler.
box.addController(new BulletMover(this, box, new Vector3f(dx,dy,dz),missleTrajectory2));
rootNode.attachChild(missleTrajectory2);
}
private void setTurretAngle(){
Quaternion turretAnglePitch = new Quaternion();
Quaternion turretAngleYaw = new Quaternion();
Quaternion turretAngle;
turretAngleYaw.fromAngleAxis(FastMath.DEG_TO_RAD*currentYaw, new Vector3f(0, 1, 0));
turretAnglePitch.fromAngleAxis(FastMath.DEG_TO_RAD*currentPitch, new Vector3f(1, 0, 0));
turretAngle = turretAngleYaw.mult(turretAnglePitch);
tankTurret.setLocalRotation(turretAngle);
//ddaniels added 05/20/06
//updateTrajectoryLine();
// jhurst 05/20/06 commented out.
// fire();
}
//List of items to remove
ArrayList<Spatial> nodesToRemove = new ArrayList<Spatial>();
public void removeNode(Spatial s)
{
if(!nodesToRemove.contains(s))
{
nodesToRemove.add(s);
}
}
//Actually perform the removal
private void comittNodeRemoval()
{
Iterator<Spatial> removeNodeIter = nodesToRemove.iterator();
while(removeNodeIter.hasNext())
{
Spatial s = removeNodeIter.next();
s.removeFromParent();
//remove spatial from list now that it has been cleaned
removeNodeIter.remove();
}
}
private void updateTrajectoryLine(){
//time
float t = 0.0f;
float dx = 0.0f;
float dy = BARREL_ELEVATION;
float dz = 0.0f;
float cosX;
float cosY;
float cosZ;
float xe;
float ze;
float bar, Lx, Ly, Lz, Yb;
float L = 1.0f;
float Alpha = -currentPitch - 90;
float Gamma = -currentYaw - 90;
bar = (float)(L * Math.cos((90-Alpha) * FastMath.DEG_TO_RAD)); //projection of barrel onto xz plane
Lx = (float)(bar * Math.cos(Gamma * FastMath.DEG_TO_RAD)); // x-component barrel of gun
Ly = (float)(L * Math.cos(Alpha * FastMath.DEG_TO_RAD)); //y-component
Lz = (float)(bar * Math.sin(Gamma * FastMath.DEG_TO_RAD)); // x-component barrel of gun
Yb = BARREL_ELEVATION;
ArrayList linePoints = new ArrayList();
cosX = Lx/L;
cosY = Ly/L;
cosZ = Lz/L;
//x- and z- coordinates at end of barrel
//dx = xe = (float)(L*Math.cos((90-Alpha)*FastMath.DEG_TO_RAD) * Math.cos(Gamma * FastMath.DEG_TO_RAD));
//dz = ze = (float)(L*Math.cos((90-Alpha)*FastMath.DEG_TO_RAD) * Math.sin(Gamma * FastMath.DEG_TO_RAD));
Vector3f position = tankTurretBarrel.getLocalTranslation();
dx = xe = position.x;
dy = Yb = position.y;
dz = ze = position.z;
//Solve for when dy == 0 to figure out how many time increments there are
/*
//dy == 0 when t = (-b +- sqrt(b^2-4ac)/(2a)
float a = Vm * cosY;
float b = -0.5f * GRAVITY;
float c = (float) (Yb + L * Math.cos(Alpha*FastMath.DEG_TO_RAD) );
//float c = .1f;
float ty0 = (float) ((-b + Math.sqrt(b*b+4*a*c))/(2*a));
System.out.println("a="+a + "\nb="+b+"\nc="+c+"\nty0="+ty0);
//calculate the number of time increments
//int numTimeIncrements = (int)Math.ceil(ty0/TIME_INCREMENT);
int numTimeIncrements = 0;
//count to find out how big to allocate
*/
while(dy>=0){
linePoints.add(new Vector3f(dx,dy,dz));
//update positionss
t+=TIME_INCREMENT;
dx = Vm * cosX * t + xe;
//System.out.println("Yb + L * Math.cos(Alpha*FastMath.DEG_TO_RAD))+ (Vm * cosY * t) + (0.5f * GRAVITY*t*t)==\n"+
// Yb + " + " + L * Math.cos(Alpha*FastMath.DEG_TO_RAD)+ " + (" + (Vm * cosY+"*"+t) + ")" + 0.5f * GRAVITY+"*"+t*t+"=\n"+
// (float)((Yb + L * Math.cos(Alpha*FastMath.DEG_TO_RAD))+ (Vm * cosY * t) + (0.5f * GRAVITY*t*t)));
dy = (float)((Yb + L * Math.cos(Alpha*FastMath.DEG_TO_RAD))+ (Vm * cosY * t) + (0.5f * GRAVITY*t*t));
dz = Vm * cosZ * t + ze;
System.out.println("t="+t+" dx="+dx+" dy="+dy+" dz="+dz);
}
Vector3f[] vertex = new Vector3f[linePoints.size()];
ColorRGBA[] color = new ColorRGBA[linePoints.size()];
for (int i = 0; i < linePoints.size(); i++) {
vertex[i] = (Vector3f)linePoints.get(i);
color[i] = ColorRGBA.red;
}
Line l = new Line("Line Group", vertex, null, color, null);
l.setMode(Line.CONNECTED);
l.setModelBound(new BoundingBox());
l.updateModelBound();
l.setLightCombineMode(LightState.OFF);
trajectoryLine.removeFromParent();
trajectoryLine = l;
this.trajectoryNode.attachChild(trajectoryLine);
}
class BulletMover extends Controller {
private static final long serialVersionUID = 1L;
// The bullet to be controlled
// TODO make this more general. Use a super class instead.
TriMesh bullet;
float lifeTime = 15; // sec
// Velocity in each component so we can determine the
// updated bullet location. We hack in a constant gravity
// in the y direction to accelerate the object back
// towards the x-z plane.
float velX = 0f; // meters/sec
float velY = 0f; // meters/sec
float velZ = 0f; // meters/sec
// Node for easy removal from the scene
// TODO remove this Node passing non-sense
Node traj;
Tank tankGame;
// basic constructor sets all our members
BulletMover(Tank tGame, TriMesh bullet, Vector3f direction, Node traj) {
//We need the tnak game so that we can safely remove objects
tankGame = tGame;
this.bullet = bullet;
this.velX = direction.x;
this.velY = direction.y;
this.velZ = direction.z;
this.traj = traj;
}
public void update(float time) {
// Check if our node has timed out, is so, remove it from
// the rootNode tree
lifeTime -= time;
if (lifeTime < 0) {
traj.removeController(this);
removeNode(traj);
return;
}
Vector3f bulletPos = bullet.getLocalTranslation();
/** Does the bullet intersect with target? */
if (bullet.getWorldBound().intersects(target.getWorldBound())) {
target.setLocalTranslation(new Vector3f(r.nextFloat() * 10+100, r
.nextFloat() * 10+100, r.nextFloat() * 10+100));
traj.removeController(this);
removeNode(traj);
hitStatus.print(HIT);
return;
}
// Check if we have hit the ground, or we have hit
// the enemy tank so we can remove the bullet and
// possibly trigger an explosion.
// TODO add collision detection or something.
if(bulletPos.y < 0)
{
// These lines are commented out so the box will site on
// the ground until it times out.
//rootNode.detachChild(traj);
traj.removeController(this);
return;
}
// Move the bullet to the next location based on time.
// TODO use the conversion from METERS to GRID SQUARES here....
Vector3f dPos = new Vector3f( velX * time, // X position
(float)(velY * time + .5f*(-9.8f)*time*time), // Y
velZ * time); // Z position
bulletPos.addLocal(dPos);
bullet.setLocalTranslation(bulletPos);
// update the Y velocity based on gravity.
velY = velY + (-9.8f)*time;
}
}
}