/**
 * Viewer3D
 *
 * Simple Web 3D Applet Viewer for 3DS file
 * 
 * @author Bruno Augier
 * @website: http://dzzd.net/
 * @version 1.00 2008/07/03
 */

import net.dzzd.utils.Log;
 
import net.dzzd.access.*;
import net.dzzd.DzzDApplet;
import net.dzzd.*;
import net.dzzd.utils.io.*;

import java.awt.event.KeyEvent;
import java.awt.event.*;

import java.awt.*;
import java.net.*;
import java.awt.image.*;

import java.applet.*;


import java.util.*;



public final class FPSSample extends DzzDApplet implements Runnable,IScene3DRenderCallBack
{		
	private int nbStart=0;
	
	IScene3DRender scene3DRender;
	IScene3DRenderCallBack mainSceneHandler;
	
	private String model1Base="FPSSample/";			//Default 3D model directory
	private String model1File="FPSSample.3DS";		//Default 3D model files
	private String loadingImage="FPSSample/LOAD.GIF";	//Default loading image
	private String cameraName="Camera01";
	private int bgColor=0x000000;					//Default background color
	private int loadingBarFrontColor=0xAAAAAA;		//Default loading bar front color
	private int loadingBarBackColor=0xEEEEEE;		//Default loading bar back color
	private int loadingBarBorderColor=0x0000EE;		//Default loading bar border color
	private int loadingBarWidth=280;				//Default loading bar width
	private int loadingBarHeight=40;				//Default loading bar height
	
	private Image loadImage;	//Image object for loading
	private Graphics bg;		//Back buffer
	private Image bgi;			//Back buffer image

	private int loadProgress3D=0;	//Progress of 3D download
	private int maxProgress3D=0;	//Max progress of 3D download for multiple files
	private int loadProgressIMG=0;	//Progress of 3D download
	private int maxProgressIMG=0;	//Progress of 3D download for multiple files
	
	//still loading flag ?
	private boolean loading=true;	
	//game over ?
	private boolean gameOver=false;	
	
	AudioClip shot;
	AudioClip boom;
	
	//font...
	Font f=new Font("Verdana",Font.PLAIN,12);
	Font gameOverFont=new Font("Verdana",Font.PLAIN,24);
 
	/**
	 * Init Method called by the browser
	 * there we replace default value by applet param tag value
	 */		 
	public void init()
	{
		try
		{
			this.shot=this.getAudioClip(new URL(this.getBaseURL()+"FPSSample/shot.au"));
			this.boom=this.getAudioClip(new URL(this.getBaseURL()+"FPSSample/boom.au"));
		}
		catch(MalformedURLException mue)
		{
			mue.printStackTrace();
		}
		
		System.out.println("Init : "+Thread.currentThread());
		super.init();
		
		//Set the path to extension libraries (JOGL for now)
		DzzD.extensionBaseURL=this.getBaseURL()+"/LIB/";
		
		/**
		 * Below we read applet parameter tag to enable customisation of 
		 *  the loading progress bar with <param> tag in <applet> tag
		 */
		if(this.getParameter("MODELFILENAME")!=null)
		{
			this.model1Base="./";
			this.model1File=this.getParameter("MODELFILENAME");
		}
		
		if(this.getParameter("MODELFILEPATH")!=null)
			this.model1Base=this.getParameter("MODELFILEPATH");
		
		if(this.getParameter("IMAGELOADFILE")!=null)
			this.loadingImage=this.getParameter("IMAGELOADFILE");
		
		if(this.getParameter("CAMERANAME")!=null)
			this.cameraName=this.getParameter("CAMERANAME");

		if(this.getParameter("BGCOLOR")!=null)
		{
			try
			{
				this.bgColor=Integer.parseInt(this.getParameter("BGCOLOR"),16);
			}
			catch(NumberFormatException nfe)
			{
				Log.log("Invalid background color value BGCOLOR="+this.getParameter("BGCOLOR"));
			}
			
		}
		
		if(this.getParameter("LOADBARFRONTCOLOR")!=null)
		{
			try
			{
				this.loadingBarFrontColor=Integer.parseInt(this.getParameter("LOADBARFRONTCOLOR"),16);
			}
			catch(NumberFormatException nfe)
			{
				Log.log("Invalid color value LOADBARFRONTCOLOR="+this.getParameter("LOADBARFRONTCOLOR"));
			}
			
		}	
		
		if(this.getParameter("LOADBARBACKCOLOR")!=null)
		{
			try
			{
				this.loadingBarBackColor=Integer.parseInt(this.getParameter("LOADBARBACKCOLOR"),16);
			}
			catch(NumberFormatException nfe)
			{
				Log.log("Invalid color value LOADBARBACKCOLOR="+this.getParameter("LOADBARBACKCOLOR"));
			}
			
		}				
		
		if(this.getParameter("LOADBARBORDERCOLOR")!=null)
		{
			try
			{
				this.loadingBarBorderColor=Integer.parseInt(this.getParameter("LOADBARBORDERCOLOR"),16);
			}
			catch(NumberFormatException nfe)
			{
				Log.log("Invalid color value LOADBARBORDERCOLOR="+this.getParameter("LOADBARBORDERCOLOR"));
			}
			
		}				
		
		if(this.getParameter("LOADBARWIDTH")!=null)
		{
			try
			{
				this.loadingBarWidth=Integer.parseInt(this.getParameter("LOADBARWIDTH"),10);
			}
			catch(NumberFormatException nfe)
			{
				Log.log("Invalid color value LOADBARWIDTH="+this.getParameter("LOADBARWIDTH"));
			}
			
		}		
		
		if(this.getParameter("LOADBARHEIGHT")!=null)
		{
			try
			{
				this.loadingBarHeight=Integer.parseInt(this.getParameter("LOADBARHEIGHT"),10);
			}
			catch(NumberFormatException nfe)
			{
				Log.log("Invalid color value LOADBARHEIGHT="+this.getParameter("LOADBARHEIGHT"));
			}
			
		}						
		
		
		this.setBackground(new Color(this.bgColor));
	}
	
	/**
	 * Start Method called by the browser
	 * There we start a Thread
	 */
	public void start()
	{
		System.out.println("Start : "+Thread.currentThread());
		super.start();
		if(this.nbStart++==0)			
			this.startOnce();		
	}
	
	/**
	 * Start Once Method called once by start() method
	 * There we start a Thread
	 */		
	public void startOnce()
	{
		//Create a backbuffer to display a loading progress bar (can be used for menu too)
		this.setLayout(null);
		this.bgi=this.createImage(this.getWidth(),this.getHeight());
		this.bg=this.bgi.getGraphics();		
		
		//Load our custom loading progress bar
		String loadingImageTmp=this.getBaseURL()+this.loadingImage;
		Log.log("Load image "+loadingImageTmp);
		this.loadImage=IOManager.loadImage(loadingImageTmp);
		this.update(this.getGraphics());
		
		//Create a main scene3DRender that will enable us to load, 
		// display and control our 3D scene
    	this.scene3DRender=DzzD.newScene3DRender();
		
		
		//Start a Thread that will load & display 3d and exit.
		Thread t=new Thread(this);
		t.start();	
	}

	/**
	 * Destroy Method called by the browser
	 * There we stop Scene3DRender 
	 */
	public void destroy()
	{
		System.out.println("Destroy : "+Thread.currentThread());
		if(this.scene3DRender!=null)
			this.scene3DRender.stop();
		this.scene3DRender=null;
	}
	
	/**
	 * Main thread
	 */	
	public void run()
	{		
		//Load 3D
		this.load3D();
		
		this.loading=false;
		this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
		
		//Show 3D and activate our scene3DRenderCallBack to control it
		this.show3D();
	}
	
	/**
	 *  Synchronously load 3D & texture (avoid stream loading on 3d & texture)
	 */	
	public void load3D()
	{    	
    	//Create a Scene3DLoader to load our 3DS file
		IScene3DLoader s1=DzzD.newScene3DLoader();
		String loadImage=this.getBaseURL()+this.loadImage;
		String loadFile=this.getBaseURL()+this.model1Base+this.model1File;
		Log.log("Load 3ds "+loadFile);
		
		//Start loading the 3D scene
		s1.loadScene3D(this.getBaseURL()+this.model1Base,this.model1File);
		
		/**
		 * Attach the 3D loader to the Scene3D to monitor loading progress.
		 *  This way the scene3D will automatically load materials textures.
		 */		
		IScene3D s=this.scene3DRender.getScene3D();
		s.setScene3DLoader(s1);	
		
		/** 
		 * Wait until all streaming object are loaded
		 * In this case we load a 3ds file so we will have a 
		 *  IScene3DLoader as monitored object.
		 * Then once it will be loaded we will have the textures it contain 
		 *  as a cupple of ITexture monitored objects
		 */
		do
		{
			this.loadProgress3D=0;
			this.loadProgressIMG=0;
			this.maxProgressIMG=0;
			this.maxProgress3D=0;

			s.updateMonitoredSceneObjects();
			if(s.getNbMonitoredSceneObject()!=0)
			{
				IMonitoredSceneObject m=s.getMonitoredSceneObject(0);
				//Compute how much 3D have been loaded if any
				if(m instanceof IScene3DLoader)
				{
					this.loadProgress3D+=(m.getProgress()*100)/m.getMaximumProgress();
					this.maxProgress3D+=100;
				}
				//Compute how much texture have been loaded if any
				if(m instanceof ITexture)
				{
					this.loadProgressIMG+=(m.getProgress()*100)/m.getMaximumProgress();
					this.maxProgressIMG+=100;
				}
				
			}
			this.update(this.getGraphics());
			DzzD.sleep(10);
		}
		while(s.getNbMonitoredSceneObject()!=0);	
		
		this.update(this.getGraphics());
		
		//Here there is no more monitored object, 
		// the whole 3d scene is loaded and ready to display
	}
		
	/**
	 * Initialise some value in the Scene3DRender before showing it
	 */
	public void show3D()
	{
		IRender3D r=this.scene3DRender.getRender3D();
		if(this.mainSceneHandler==null)
		{
			//Set scene background color
			IScene3D s=this.scene3DRender.getScene3D();
			s.setBackgroundColor(this.bgColor);	
			
			//Set main light direction
			IPoint3D lRot=s.getLight3DById(0).getRotation();
			lRot.set(-Math.PI*0.9,Math.PI*0.5,0.00);
			
			//Resize to fit applet size
			r.setSize(this.getWidth(),this.getHeight());
			
			//Set camera for rendering
			s.setCurrentCamera3DByName(cameraName);
			//Set camera FOV and vertical zoom (not well handled by the loader)
			s.getCurrentCamera3D().setHeight(0.16);
			s.getCurrentCamera3D().setWidth(0.9);
			s.getCurrentCamera3D().setFOV(65);
			s.getCurrentCamera3D().setZMin(1);
			s.getCurrentCamera3D().setZoomY(((double)r.getWidth())/((double)r.getHeight()));	
			
			
			//Remove ligthing and mipmap on sky box and zoom it
			IMesh3D sky=s.getMesh3DByName("SKY");
			if(sky!=null)
			{
				sky.zoom(1000,1000,1000);
				s.setSkyBoxMesh3DById(sky.getId());
				sky.getRender3DMode().disableRender3DMode(DzzD.RM_LIGHT|DzzD.RM_TEXTURE_MIPMAP);
				sky.setSolid(false);
			
			}					
			
			//Enable the Scene3DRender to draw pixel on its canvas			
			r.setScreenUpdateEnabled(true);
			
			//Set its canvas visible
			r.getCanvas().setVisible(true);
			
			/*
			 * Set our applet as the Scene3DRenderCallBack 
			 *  for our main Scene3DRender not really
			 *  a good practice but than we dont 
			 *  need another class file, as our applet 
			 *  implements IScene3DRenderCallBack.
			 */
			this.mainSceneHandler=this;	
			this.scene3DRender.setScene3DRenderCallBack(this.mainSceneHandler);
		}
		
		this.add(r.getCanvas());
		r.getCanvas().requestFocus();
		this.scene3DRender.start();			
	}
	
	/**
	 * When passive rendering we ensure screen is 
	 *  looking good by drawing it when an area
	 *  is made visible (not really needed if there is no menu).
	 */
	public void paint(Graphics g)
	{
		this.update(g);
	}
		
	/**
	 * Standard Update method
	 * If we are loading we render the 
	 * custom loading progress bar
	 */
	public void update(Graphics g)
	{

		if(g==null) return;
		if(this.bgi==null) return;
		
		try
		{
			if(this.loading)
			{
					this.bg=this.bgi.getGraphics();
					this.renderLoading(g);
					Thread.sleep(50);
			}
			if(this.gameOver)
			{
				g.setColor(Color.red);
				g.setFont(this.gameOverFont);
				g.drawString("Game Over (F5)",230,150);
			}
		}
		catch(InterruptedException ie)
		{
		}
		
		
	}		
	
	public void gameOver()
	{
		this.gameOver=true;
		this.remove(this.scene3DRender.getRender3D().getCanvas());
	}
	
	
	//Utility method to draw our customized loading progress bar
	private void renderLoading(Graphics g)
	{
		
		int cx=this.getWidth()>>1;
		int cy=this.getHeight()>>1;
		this.bg.setColor(Color.black);
		this.bg.setFont(f);
		
		int progress=0;
		if(this.maxProgress3D!=0)
			progress+=this.loadProgress3D*50/this.maxProgress3D;
		if(this.maxProgressIMG!=0)
			progress+=50+this.loadProgressIMG*50/this.maxProgressIMG;			
		progress=progress*this.loadingBarWidth/100;
		
		int width2=this.loadingBarWidth>>1;
		int height2=this.loadingBarHeight>>1;
		
		drawProgressBar(this.bg,cx-width2,cy-height2,this.loadingBarWidth,this.loadingBarHeight,progress,this.loadingBarBorderColor,this.loadingBarFrontColor,this.loadingBarBackColor);
		
		if(this.loadImage!=null)
			this.bg.drawImage(this.loadImage,cx-(this.loadImage.getWidth(null)>>1),cy-(this.loadImage.getHeight(null)>>1),null);
			
		g.drawImage(this.bgi,0,0,null);
		
	}
	
	//Utility method to draw a generic a progress bar
    private void drawProgressBar(Graphics g,int x,int y,int w,int h,int p,int bcolor,int fcolor,int bgcolor)
    {
		g.setColor(new Color(bcolor));
		g.drawRect(x,y,w,h);
		
		if(p>=w)
			p=w-1;
				
		g.setColor(new Color(fcolor));
		g.fillRect(x+1,y+1,p,h-1);	
		
		g.setColor(new Color(bgcolor));
		g.fillRect(x+1+p,y+1,(w-p)-1,h-1);
    }



	/**
	 * Below follow the IScene3DRenderCallBack implementation
	 *
	 * this one should be put in a different class file
	 *  implementing the IScene3DRenderCallBack interface.
	 *
	 */


    /**
     * We keep things locally to avoid to 
     *  have to ask for them at each loops
     */
    IRender3D render;
	IScene3D scene;
	ICamera3D camera;
	IDirectInput input;
	ISolidSphere3D sSphere;
	ISolidSphere3D bullet;
	ISolidSphere3DResult bulletRes;
	IPoint3D gravity;
	IMesh3D player;
	IMesh3D impactModel;
	IMesh3D badBoyModel;
	
	IMesh3D gun;
	
	BadBoys badBoy;
	Impact impact;
	long shotTime=-1;
	boolean playerOnGround=false;
	IPoint3D playerMove;
	long playerStartRunning=-1;
	double playerCamY;
	
	
	/**
	 * Impact classe
	 */
	private class Impact
	{
		private IScene3D scene;
		public IMesh3D mesh;
		public ISolidSphere3D sSphere;
		public IPoint3D move;
		public long time;
		private IPoint3D gravity;
		
		
		public Impact(IScene3D s,IMesh3D impactModel,ISolidSphere3DResult source,IPoint3D gravity)
		{
			this.scene=s;
			this.gravity=gravity;
			
			this.sSphere=DzzD.newSolidSphere3D();
	 		this.sSphere.setScene3D(this.scene);
	 		this.sSphere.setRadius(0.3);
	 		this.sSphere.getSource().copy(source.getPosition());			
	 		
	 		this.move=DzzD.newPoint3D();
	 		this.move.copy(source.getSlidePlane());
	 		this.move.mul(2);
	 		source.getSlideResponse().normalize();
	 		this.move.add(source.getSlideResponse());
	 		
	 		this.mesh=(IMesh3D)impactModel.getClone(false);
	 		this.mesh.setVisible(true);
	 		this.mesh.setSolid(false);
	 		this.mesh.setActive(true);
	 		this.mesh.setName("tmp");
	 		
	 		this.mesh.getPosition().copy(this.sSphere.getSource());
	 		
	 		this.scene.addMesh3D(this.mesh);
	 		this.time=System.currentTimeMillis();
	 		
	 	
		}
		
		public void move()
		{
			this.sSphere.getDestination().copy(this.gravity);
			this.sSphere.getDestination().add(this.sSphere.getSource());
			this.sSphere.getDestination().add(this.move);
			
			this.sSphere.moveSlide(3);
			this.mesh.getPosition().copy(this.sSphere.getSource());
			this.move.mul(0.9);
			this.mesh.getRotation().add(this.move);
			
		}
		
		
		
		public boolean end()
		{
			
			if((System.currentTimeMillis()-this.time)>1000)
			{
				this.mesh.setVisible(false);
	 			this.mesh.setActive(false);
				this.scene.removeScene3DObject(this.mesh);
				return true;
			}
			return false;
				
		}
		
		
		
		
	}
	
	/**
	 * Enemy unit
	 */
	 private class BadBoys
	 {
	 	private IScene3D scene;
	 	public ISolidSphere3D sSphere;
	 	public IMesh3D mesh;
	 	public IMesh3D player;
	 	public IPoint3D move;
	 	private IPoint3D gravity;
	 	public int health;
	 	
	 	
	 	
	 	public BadBoys(IScene3D s,IMesh3D player,IMesh3D badBoyModel,IPoint3D gravity)
	 	{
	 		this.scene=s;
	 		this.gravity=gravity;
	 		this.sSphere=DzzD.newSolidSphere3D();
	 		this.sSphere.setScene3D(this.scene);
	 		this.sSphere.setRadius(3);
	 		
	 		this.mesh=(IMesh3D)badBoyModel.getClone(false);
	 		this.mesh.setVisible(true);
	 		this.mesh.setSolid(false);	 		
	 		this.mesh.setActive(true);
	 		this.mesh.setName("tmp");
	 		
	 		this.mesh.getPosition().copy(this.sSphere.getSource());
	 		this.move=DzzD.newPoint3D();
	 		this.player=player;
	 		this.scene.addMesh3D(this.mesh);
	 		this.init();
	 		
	 	}
	 	
	 	public void init()
	 	{
	 		this.health=3;
	 		this.sSphere.getSource().set(200.0*Math.random()-0.5,200.0,200.0*Math.random()-0.5);
	 	}
	 	
	 	public boolean end()
	 	{
	 		this.health--;
	 		if(this.health==0)
	 		{
				this.init();
	 			return true;
	 		}	 		 
	 		else
	 		 return false;
	 	}
	 	public void move()
	 	{
	 		this.move.copy(this.player.getPosition());
	 		this.move.sub(this.sSphere.getSource());
	 		this.move.normalize();
	 		this.move.add(this.gravity);
	 		this.sSphere.getDestination().copy(this.sSphere.getSource());
	 		this.sSphere.getDestination().add(move);
	 		this.sSphere.moveSlide(3);
	 		this.mesh.getPosition().copy(this.sSphere.getSource());
	 		
	 	}
	 	
	 }
	
	/** 
	 * IScene3DRenderCallBack interface
	 * This method is called once when a new Scene3DRenderCallback is set to a Scene3DRender
	 */
	public void render3DstartCallBack(IScene3DRender r)
	{
		this.render=r.getRender3D();
		this.scene=r.getScene3D();
		this.input=this.render.getDirectInput();
		this.camera=this.scene.getCurrentCamera3D();
		this.sSphere=DzzD.newSolidSphere3D();
		this.sSphere.setScene3D(this.scene);
		this.sSphere.getSource().copy(this.camera.getPosition());
		this.sSphere.setRadius(4);
		this.gravity=DzzD.newPoint3D();
		this.gravity.set(0,-1,0);
		
		this.player=this.scene.getMesh3DByName("Player");
		
		this.impactModel=this.scene.getMesh3DByName("Impact");
		this.impactModel.setVisible(false);
		this.impactModel.setActive(false);
		this.impactModel.setSolid(false);
		
		this.badBoyModel=this.scene.getMesh3DByName("BadBoy");
		this.badBoyModel.setVisible(false);
		this.badBoyModel.setActive(false);
		this.badBoyModel.setSolid(false);		
		
		this.bullet=DzzD.newSolidSphere3D();
		this.bullet.setScene3D(this.scene);
		this.bullet.setRadius(0.4);
		
		this.playerMove=DzzD.newPoint3D();
		this.playerCamY=this.camera.getPosition().getY();
		
		
		
		
		this.gun=this.scene.getMesh3DByName("Gun");
		this.gun.setSolid(false);


		this.badBoy=new BadBoys(this.scene,this.player,this.badBoyModel,this.gravity);
		
		//this.gun.zoom(1,-1,-1);

		
		
		/**
		 * Start the Scene3DRender 
		 *  below call back methods will be called automatically now
		 *  until we stop it.
		 */
		r.start();		
	}
	
	/**
	 * IScene3DRenderCallBack interface
	 * This method is called once when a Render3D switch happen on a Scene3DRender
	 */	
	public void render3DSwitched(IScene3DRender r)
	{
		this.render=r.getRender3D();
		this.input=this.render.getDirectInput();
	}
	
	
	/**
	 * IScene3DRenderCallBack interface
	 * Called for each rendered frame before any other operations
	 */
	public void render3DStart(IScene3DRender r)
	{
		//Set skybox pos to current camera pos
		IMesh3D sky=this.scene.getMesh3DByName("SKY");
		if(sky!=null)
			sky.getPosition().copy(this.scene.getCurrentCamera3D().getPosition());
		
		if(this.impact!=null)
		{
			this.impact.move();
			if(this.impact.end())
				this.impact=null;
			
		}
		
		
		if(this.bulletRes!=null && this.bulletRes.getMesh3DId()==this.badBoy.mesh.getId())
		{
			if(this.badBoy.end())
			{
				this.boom.play();
				//this.badBoy.init();//=new BadBoys(this.scene,this.player,this.badBoyModel,this.gravity);
				
			}
			this.bulletRes=null;
			
		}	
		
	}
	


	/**
	 * IScene3DRenderCallBack interface
	 * Called for each rendered frame when all object axis are in world space
	 */	
	public void render3DWorldSpace(IScene3DRender r)
	{
		
		if(this.badBoy!=null)	
			this.badBoy.move();
		
		//Read current cam axis in world space
		IAxis3D playerAxis=this.camera.getAxis3D();
		
		//Create a forward vector and lateral vector with player axis
		IPoint3D az= DzzD.newPoint3D();
		az.copy(playerAxis.getAZ()).sub(playerAxis.getOrigin());
		IPoint3D ax= DzzD.newPoint3D();
		ax.copy(playerAxis.getAX()).sub(playerAxis.getOrigin());
		
		//Copy current player position & rotation locally
		IPoint3D playerRotation=this.player.getRotation();
		IPoint3D playerPosition=this.player.getPosition();

		
		//Read mouse pos
		double mx=this.input.getMouseX();
		double my=this.input.getMouseY();

		//Rotate player using mouse coordinate
		double rx=playerRotation.getX(); 
		double ry=playerRotation.getY();
		
		
		double crx=rx+(my-this.render.getHeight()*0.5)*0.0003;
		if(crx>Math.PI*0.5*0.8)
			crx=Math.PI*0.5*0.8;
		if(crx<-Math.PI*0.5*0.8)
			crx=-Math.PI*0.5*0.8;
		playerRotation.setX(crx);
		playerRotation.setY(ry+(this.render.getWidth()*0.5-mx)*0.0003);
		
				 
		//Move player using key pressed
		double moveForward=0;
		double moveLeftRight=0;
		double mvx=0;
		double mvy=0;
		double mvz=0;
		double speed=1.0;


		if(this.input.isKey(KeyEvent.VK_UP))
				moveForward=speed;  
				
		if(this.input.isKey(KeyEvent.VK_DOWN))
				moveForward=-speed;
		
		if(this.input.isKey(KeyEvent.VK_RIGHT))
				moveLeftRight=speed; 
				
		if(this.input.isKey(KeyEvent.VK_LEFT))
				moveLeftRight=-speed; 	
				
		if(((moveLeftRight==0) && (moveForward==0)))
			this.playerStartRunning=-1;
			
		if(this.playerStartRunning==-1 && this.playerOnGround && ((moveLeftRight!=0) || (moveForward!=0)))			
			this.playerStartRunning=System.currentTimeMillis();
				
		mvx=az.getX()*moveForward+ax.getX()*moveLeftRight;
		mvy=az.getY()*moveForward+ax.getY()*moveLeftRight;
		mvz=az.getZ()*moveForward+ax.getZ()*moveLeftRight;
		
		if(this.playerOnGround)
		{
		 if(this.input.isMouseB3())
		  mvy+=5;
		 else
		 	if(this.playerStartRunning!=-1)
		 	{
		  		double decalY=0.3*Math.sin(Math.PI*0.005*(this.playerStartRunning-System.currentTimeMillis()));
				this.camera.getPosition().setY(this.playerCamY+decalY);
			}
		 	
		 this.playerMove.set(mvx,mvy,mvz);
		}
		else
			this.playerStartRunning=-1;
		this.playerMove.add(this.gravity);
		
		
		//MOVE WITH GRAVITY AND COLLISION	
		
		//Move SolidSphere with gravity and collision trought the 3D scene 
		this.sSphere.getDestination().copy(this.sSphere.getSource()).add(this.playerMove);
		//this.sSphere.getDestination().add(this.gravity);
		this.playerOnGround=false;
		for(int n=0;n<3;n++)
		{
			if(this.sSphere.moveSlide(1).isImpact())
			{
				this.playerOnGround=true;
			}
		}
		this.playerMove.mul(0.99);
		
		
		
		//Copy moved SolidSphere position to current camera	position	
		playerPosition.copy(this.sSphere.getSource());
		
		
		Thread.yield();
		//System.out.println("cam="+camPosition);
		//System.out.println("gun="+this.gun.getPosition());
		double rxReload=0.0;
		if(this.shotTime!=-1)
		{
			long shotTime2=System.currentTimeMillis()-this.shotTime;
			if(shotTime2>1000)
				this.shotTime=-1;
			if(shotTime2>500)
				rxReload=-Math.PI*0.1*Math.sin(Math.PI*(shotTime2-500)/500.0);
		}
		this.gun.getRotation().set(
			rxReload+((double)(my-this.render.getHeight()*0.5)/this.render.getHeight())*45.0*Math.PI/180.0,
			((double)(this.render.getWidth()*0.5-mx)/this.render.getWidth())*65.0*Math.PI/180.0,0);
		
	/*	this.gun.getRotation().copy(camRotation);
		az.mul(5);
		this.gun.getPosition().add(az);
		*/
		
		//Fire ?
		if(this.shotTime==-1 && this.impact==null && this.input.isMouseB1())
		{
			if(this.shot!=null)
			this.shot.play();
			System.out.println("Fire");
			
			this.shotTime=System.currentTimeMillis();
			
			this.bullet.getDestination().copy(this.gun.getAxis3D().getAZ());
			this.bullet.getDestination().sub(this.gun.getAxis3D().getOrigin());
			this.bullet.getSource().copy(this.gun.getAxis3D().getOrigin());
			this.bullet.getDestination().mul(-1000);
			this.bullet.getDestination().add(this.bullet.getSource());
			
			this.badBoy.mesh.setSolid(true);
			this.bulletRes=this.bullet.moveSlide(1);
			this.badBoy.mesh.setSolid(false);
			if(this.bulletRes.isImpact())
			{
				System.out.println("Impact");
				this.impact=new Impact(this.scene,this.impactModel,this.bulletRes,this.gravity);

			}
			
			
			
		}

		
		
		
		
		if(this.badBoy.mesh.getPosition().dist(this.player.getPosition())<4)
			this.gameOver();
		
		
		//Here we handle switching between Hardware(Java 1.4 signed+JOGL) and Software(Java 1.1) renderer
		if(this.input.isKey(KeyEvent.VK_H))
			r.switchRender3D("JOGL");
		if(this.input.isKey(KeyEvent.VK_S))
			r.switchRender3D("SOFT");
		
			
			
		
	}
	
	/**
	 * IScene3DRenderCallBack interface
	 * Called for each rendered frame when all object axis are in camera space
	 */		
	public void render3DCameraSpace(IScene3DRender r){}
	
	/**
	 * IScene3DRenderCallBack interface
	 * Called for each rendered frame just before drawing pixels on canvas
	 */		
	public void render3DPixelsUpdate(IScene3DRender r){}
	
	/**
	 * IScene3DRenderCallBack interface
	 * Called for each rendered frame just after drawing pixels on canvas
	 */	
	public void render3DPixelsUpdated(IScene3DRender r){}
	
	/**
	 * IScene3DRenderCallBack interface
	 * Called when a new frame as been drawn on screen
	 */	
	public void render3DEnd(IScene3DRender r){}	
}
