<file_sep>package andar.tower.defense.model;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Vector;

import andar.tower.defense.GameContext;
import andar.tower.defense.parser.Group;
import andar.tower.defense.parser.Material;

public class Model implements Serializable {
	// position/rotation/scale
	private static final String tag = "Model";
	public String name;
	
	private ParsedObjModel parsedObjModel; // the meshes and textures
	public Model3D model3D;
	public float xrot = 90;
	public float yrot = 90;
	public float zrot = 0;
	/* in normal view distance you can see a model 
	 * on screen in a range of x/y: (-30..+30)/(-30..+30) */
	public float xpos = 0;
	public float ypos = 0;
	public float zpos = 0;
	/* rotate/scale a model to "normal" at start */
	private float defaultXRot;
	private float defaultYRot;
	private float defaultScale;
	public float scale = 4f;
	
	public static final int STATE_DYNAMIC = 0;
	public static final int STATE_FINALIZED = 1;
	
	private boolean hidden = false;
	
	
	// timestamp of last position-update in milliseconds
	private double lastPosUpdate = 0; 

	private Vector<Group> groups = new Vector<Group>();
	/**
	 * all materials
	 */
	protected HashMap<String, Material> materials = new HashMap<String, Material>();
	protected GameContext gameContext;

	public Model(GameContext gameContext, ParsedObjModel parsedObjModel, String patternName) {
		this.gameContext = gameContext;
		this.name = parsedObjModel.name;
		setParsedObjModel(parsedObjModel);
		Model3D model3d = new Model3D(this, parsedObjModel, patternName);
		this.model3D = model3d;
		adjustModel(0f,0f,0f);
	}
	
	/**
	 * calculate new position on path depending on velocity
	 */
	public void positionUpdate() {
	}
	
	/**
	 * Rotate/scale a model to "normal" at start.
	 * @param defaultXRot
	 * @param defaultYRot
	 * @param defaultScale
	 */
	public void adjustModel(float defaultXRot, float defaultYRot, float defaultScale) {
		this.defaultXRot = defaultXRot;
		this.defaultYRot = defaultYRot;
		this.defaultScale = defaultScale;
	}

	public void setScale(float f) {
		this.scale = defaultScale * f;
		if (this.scale < 0.0001f)
			this.scale = 0.0001f;
	}

	public float getScale() {
		return scale;
	}
	
	public void setXrot(float dY) {
		this.xrot = defaultXRot + dY;
	}

	public void setYrot(float dX) {
		this.yrot = defaultYRot + dX;
	}

	public void addXpos(float f) {
		this.xpos += f;
	}

	public void addYpos(float f) {
		this.ypos += f;
	}
	
	/**
	 * get hit/shot: a model that aims at this one reaches its destination
	 * @param hitpoints how hard it got hit
	 * @param gameContext
	 */
	protected void hit(int hitpoints, GameContext gameContext) {
		/* implementation for center:
		 * the enemy reaches the center before getting destroyed
		 */
		gameContext.enemyReachesDestination(hitpoints);
	}

	public ParsedObjModel getParsedObjModel() {
		return parsedObjModel;
	}

	public void setParsedObjModel(ParsedObjModel parsedObjModel) {
		this.parsedObjModel = parsedObjModel;
	}

	public boolean isHidden() {
		return hidden;
	}

	public void setHidden(boolean hidden) {
		this.hidden = hidden;
	}

}
<file_sep>package andar.tower.defense.model;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;

import andar.tower.defense.GameContext;
import andar.tower.defense.GameActivity.Config;
import andar.tower.defense.parser.ObjParser;
import andar.tower.defense.util.BaseFileUtil;
import android.graphics.Point;
import android.net.ParseException;
import android.os.Debug;
import android.util.Log;
import edu.dhbw.andar.ARToolkit;
import edu.dhbw.andar.exceptions.AndARException;

/**
 * Manage a pool of models for reuse
 * 
 * @author jakob
 * 
 */
public class ModelPool {

	private GameContext gameContext;

	private ArrayList<Enemy> activeAirplanes = new ArrayList<Enemy>();
	private ArrayList<Enemy> inactiveAirplanes = new ArrayList<Enemy>();

	private ArrayList<Enemy> activeTanks = new ArrayList<Enemy>();
	private ArrayList<Enemy> inactiveTanks = new ArrayList<Enemy>();

	private ArrayList<Enemy> activeBullets = new ArrayList<Enemy>();
	private ArrayList<Enemy> inactiveBullets = new ArrayList<Enemy>();

	private ArrayList<Tower> activeTowers = new ArrayList<Tower>();
	
	private BaseFileUtil fileUtil;

	private ObjParser parser;

	public static final String CENTER_PATTERN = "marker_fisch16.patt";

	private final ParsedObjModel AIRPLANE_OBJMODEL, TANK_OBJMODEL,
			BULLET_OBJMODEL, TOWER_OBJMODEL;

	private Model center;
	private ARToolkit artoolkit;
	private static final String tag = "EnemyPool";

	public ModelPool(GameContext gameContext, ARToolkit artoolkit, BaseFileUtil fileUtil) {
		this.gameContext = gameContext;
		this.artoolkit = artoolkit;
		this.fileUtil = fileUtil;
		parser = new ObjParser(fileUtil);

		center = loadCenter();

		AIRPLANE_OBJMODEL = loadModelFromFile("Airplane.obj");
		TANK_OBJMODEL = loadModelFromFile("tank3.obj");
		BULLET_OBJMODEL = loadModelFromFile("bullet.obj");
		TOWER_OBJMODEL = loadModelFromFile("Tower.obj");

	}

	private synchronized Enemy getEnemy(int type, int health, int velocity, ArrayList<Point> way, Point startPoint) {
		ArrayList<Enemy> activeList = null;
		ArrayList<Enemy> inactiveList = null;
		ParsedObjModel parsedObjModel = null;
		switch (type) {
		case Enemy.AIRPLANE:
			activeList = activeAirplanes;
			inactiveList = inactiveAirplanes;
			parsedObjModel = AIRPLANE_OBJMODEL;
			break;
		case Enemy.TANK:
			activeList = activeTanks;
			inactiveList = inactiveTanks;
			parsedObjModel = TANK_OBJMODEL;
			break;
		case Enemy.BULLET:
			activeList = activeBullets;
			inactiveList = inactiveBullets;
			parsedObjModel = BULLET_OBJMODEL;
			break;
		default:
			break;
		}
		Enemy enemy;
		if (inactiveList.size() == 0) {
			enemy = new Enemy(gameContext, type, parsedObjModel, CENTER_PATTERN, null, center,
					health, velocity);
			try {
				artoolkit.registerARObject(enemy.model3D);
			} catch (AndARException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			enemy = inactiveList.remove(0);
			enemy.setHidden(false);
		}
		enemy.xpos = startPoint.x;
		enemy.ypos = startPoint.y;
		enemy.way = way;
		activeList.add(enemy);
		return enemy;
	}

	public synchronized void dismissEnemy(int type, Enemy enemy) {
		enemy.setHidden(true);
		ArrayList<Enemy> activeList = null;
		ArrayList<Enemy> inactiveList = null;
		switch (type) {
		case Enemy.AIRPLANE:
			activeList = activeAirplanes;
			inactiveList = inactiveAirplanes;
			break;
		case Enemy.TANK:
			activeList = activeTanks;
			inactiveList = inactiveTanks;
			break;
		case Enemy.BULLET:
			activeList = activeBullets;
			inactiveList = inactiveBullets;
			break;
		default:
			break;
		}
		if (activeList.contains(enemy)) {
			activeList.remove(enemy);
			inactiveList.add(enemy);
		}
	}
	
	public Enemy getAirplane() { 
		Point startPoint = randomWayPoint(60);
		ArrayList<Point> way = new ArrayList<Point>();
		way.add(new Point(0,0));
		return getEnemy(Enemy.AIRPLANE, 20, 3, way, startPoint);
	}
	public void dismissAirplane(Enemy enemy) {
		dismissEnemy(Enemy.AIRPLANE, enemy);
	}
	
	public Enemy getTank() { 
		Point startPoint = randomWayPoint(60);
		ArrayList<Point> way = new ArrayList<Point>();
		way.add(new Point(0,0));
		return getEnemy(Enemy.TANK, 100, 2, way, startPoint);
	}
	public void dismissTank(Enemy enemy) {
		dismissEnemy(Enemy.TANK, enemy);
	}
	
	public Enemy getBullet(Point startPoint, Point targetLocation) { 
		ArrayList<Point> way = new ArrayList<Point>();
		way.add(targetLocation);
		return getEnemy(Enemy.BULLET, 10, 10, way, startPoint);
	}
	public void dismissBullet(Enemy enemy) {
		dismissEnemy(Enemy.BULLET, enemy);
	}
	
	public Tower getTower(String markerName) {
		Tower tower = new Tower(gameContext, TOWER_OBJMODEL, markerName);
		activeTowers.add(tower);
		try {
			artoolkit.registerARObject(tower.model3D);
		} catch (AndARException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return tower;
	}
	
	private Point randomWayPoint(int distanceFromCenter) {
		/* In normal view distance you can see a model on screen in a range
		 * of: model.xpos/model.ypos: (-30..+30)/(-30..+30)
		 */
		
		// enemies head to center starting on a distance of 15
		int radius = distanceFromCenter;
		double maxX = Math.sqrt((radius*radius)/2);
		double deltaX = Math.signum(Math.random()-0.5) * Math.random() * maxX;
		double deltaY = Math.signum(Math.random()-0.5) * Math.sqrt(radius*radius - deltaX*deltaX);
		
		return new Point((int)deltaX, (int)deltaY);
		
	}
	
	private ParsedObjModel loadModelFromFile(String modelFileName) {
		// read the model file:
		ParsedObjModel parsedObjModel = null;
		if (modelFileName.endsWith(".obj")) {
			try {
				if (Config.DEBUG)
					Debug.startMethodTracing("AndObjViewer");

				if (fileUtil != null) {
					BufferedReader fileReader = fileUtil.getReaderFromName(modelFileName);
					if (fileReader != null) {
						parsedObjModel = new ParsedObjModel(modelFileName);
						parser.parse(parsedObjModel, modelFileName.substring(0,
								modelFileName.length() - 4), fileReader);
						Log.i(tag , "new Model3D: " + modelFileName);
					} else {
						Log.w("ModelLoader", "no file reader: "
										+ modelFileName);
					}
				}
				if (Config.DEBUG)
					Debug.stopMethodTracing();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ParseException e) {
				e.printStackTrace();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return parsedObjModel;
	}

	public Model getCenter() {
		return center;
	}

	private Model loadCenter() {
		// load red circle on centermarker
		String centerModelName = "energy.obj";
		ParsedObjModel energyObjModel = loadModelFromFile(centerModelName);
		Model model = new Model(gameContext, energyObjModel, ModelPool.CENTER_PATTERN);
		model.name = "center";
		try {
			artoolkit.registerARObject(model.model3D);
		} catch (AndARException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return model;
	}

	public synchronized ArrayList<Enemy> getActiveEnemies() {
		ArrayList<Enemy> allActiveEnemies = new ArrayList<Enemy>();
		allActiveEnemies.addAll(activeAirplanes);
		allActiveEnemies.addAll(activeTanks);
		return allActiveEnemies;
	}
	
	public ArrayList<Enemy> getActiveBullets() {
		return activeBullets;
	}

	public ArrayList<Tower> getActiveTowers() {
		return activeTowers;
	}
}
<file_sep>package andar.tower.defense;

import andar.tower.defense.model.Enemy;
import andar.tower.defense.model.Tower;
import android.os.Message;

public class GameThread extends Thread {

	// Game objects:
	private GameContext gameContext;
	private boolean running = true;

	// time
	long prevTime;
	long prevLongTime;
	long currTime;
	private GameActivityHandler gameActivityHandler;
	public boolean loadingDone = false;

	// game area limits
	public static final float UPPERLIMITX = 200;
	public static final float LOWERLIMITX = -200;
	public static final float UPPERLIMITY = 150;
	public static final float LOWERLIMITY = -150;

	public GameThread(GameActivityHandler gameActivityHandler,
			GameContext gameContext) {
		this.gameActivityHandler = gameActivityHandler;
		this.gameContext = gameContext;
		setDaemon(true);
		start();
	}

	public void updateHUD(float x, float y) {
		Message msg = Message.obtain(gameActivityHandler,
				gameActivityHandler.UPDATE_X_Y, (int) x, (int) y);
		msg.sendToTarget();
	}

	@Override
	public synchronized void run() {
		super.run();
		setName("GameThread");
		prevTime = System.currentTimeMillis();
		long deltaTime;
		long deltaLongTime = 15 * 1000;// nanoseconds
		long maxDeltaLong = 15 * 1000;
		yield();

		while (running) {
			if (loadingDone) {
				currTime = System.currentTimeMillis();
				deltaTime = currTime - prevTime;
				deltaLongTime = currTime - prevLongTime;
				// update monsters all 15 seconds
				if (deltaLongTime > maxDeltaLong) {
					gameContext.createEnemy();
					prevLongTime = currTime;
				}

				prevTime = currTime;

				gameContext.gameCenter.update(deltaTime);

				// update all positions
				synchronized (gameContext.modelPool) {
					for (Enemy enemy : gameContext.modelPool.getActiveEnemies()) {
						enemy.positionUpdate(gameContext);
					}
				}
				synchronized (gameContext.modelPool) {
					for (Enemy bullet : gameContext.modelPool
							.getActiveBullets()) {
						bullet.positionUpdate(gameContext);
					}
				}

				// update positioning of towers
				int i = 0;
				for (Tower tower : gameContext.modelPool.getActiveTowers()) {
					tower.model3D.update(deltaTime, gameContext.gameCenter);
					int minDistance = tower
							.updateNearestEnemyInRange(gameContext.modelPool
									.getActiveEnemies());
					tower.attack();
					if (i == 0) {
						updateHUD(tower.model3D.getX(), minDistance);
						// updateHUD(tower.model3D.getX(),
						// tower.model3D.getY());
					}
					i++;
				}

			}
			yield();
		}
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

}
<file_sep>package andar.tower.defense;

import andar.tower.defense.model.ModelPool;
import andar.tower.defense.util.AssetsFileUtil;
import andar.tower.defense.util.BaseFileUtil;
import android.app.ProgressDialog;
import android.content.res.Resources;
import android.graphics.Point;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.TextView;
import android.widget.RelativeLayout.LayoutParams;
import edu.dhbw.andar.ARToolkit;
import edu.dhbw.andar.AndARActivity;
import edu.dhbw.andar.exceptions.AndARException;

public class GameActivity extends AndARActivity implements
		SurfaceHolder.Callback {

	private static final String tag = "GameActivity";

	private ProgressDialog waitDialog;
	private Resources res;
	public static final boolean DEBUG = false;
	ARToolkit artoolkit;

	private andar.tower.defense.GameThread gameThread;

	private GameActivityHandler handler;

	TextView hud_x;

	TextView hud_y;
	private GameContext gameContext;

	public GameActivity() {
		super(false);
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);

		super.setNonARRenderer(new LightingRenderer());// or might be omited
		res = getResources();
		artoolkit = getArtoolkit();
		// getSurfaceView().setOnTouchListener(new TouchEventHandler());
		getSurfaceView().getHolder().addCallback(this);

		GameCenter gameCenter = new GameCenter("center", ModelPool.CENTER_PATTERN,
				137.0, new double[] { 0, 0 });// 170
		try {
			artoolkit.registerARObject(gameCenter);
		} catch (AndARException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		createHUD();
		gameContext = new GameContext(100, gameCenter, handler);
		gameThread = new GameThread(handler, gameContext);

	}

	public void createHUD() {

		// add layout
		LayoutInflater controlInflater = LayoutInflater.from(getBaseContext());
		View viewControl = controlInflater.inflate(R.layout.hud, null);
		LayoutParams layoutParamsControl = new LayoutParams(
				LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
		this.addContentView(viewControl, layoutParamsControl);

		// Handler for UI callbacks
		hud_x = (TextView) findViewById(R.id.tower_x);
		hud_y = (TextView) findViewById(R.id.tower_y);

		handler = new GameActivityHandler() {
			@Override
			public void handleMessage(Message msg) {
				if (msg.getData().containsKey("test")) {
					int i = 1;
				}
				if (msg.what == UPDATE_X_Y) {
					int x = msg.arg1;
					hud_x.setText("Tower rel. x: " + x);
					hud_y.setText("Dist En-Tow: " + msg.arg2);
				} else if (msg.what == GET_TANK) {
					gameContext.modelPool.getTank();
				} else if (msg.what == GET_AIRPLANE) {
					gameContext.modelPool.getAirplane();
				} else if (msg.what == GET_BULLET) {
					Bundle data = msg.getData();
					Point startPoint = new Point(data.getInt("start_x"),data.getInt("start_y"));
					Point targetPoint = new Point(data.getInt("target_x"),data.getInt("target_y"));
					gameContext.modelPool.getBullet(startPoint, targetPoint);
				} else {
					Log.i(tag, "Unknown handle with what-code: " + msg.what);
				}
			}
		};

	}

	/**
	 * Inform the user about exceptions that occurred in background threads.
	 */

	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		System.out.println("Exception Occured !!");

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		super.surfaceCreated(holder);
		// load the model
		// this is done here, to assure the surface was already created, so that
		// the preview can be started
		// after loading the model
		
		if (!gameThread.loadingDone) {
			waitDialog = ProgressDialog.show(this, "", getResources().getText(
					R.string.loading), true);
			waitDialog.show();
			new ModelLoader().execute();
		}
	}

	private class ModelLoader extends AsyncTask<Void, Void, Void> {
		
		/* time measures in ms:
		 * ObjParser.parse:       3000-10000 ms
		 * new Model3D(...)        100 ms
		 * registerARObject         10 ms
		 */

		BaseFileUtil fileUtil;
		private String tag = "GameActivity";

		public ModelLoader() {
			super();
			fileUtil = new AssetsFileUtil(getResources().getAssets());
			fileUtil.setBaseFolder("models/");
		}

		@Override
		protected Void doInBackground(Void... params) {

			gameContext.modelPool = new ModelPool(gameContext, artoolkit, fileUtil);

			gameContext.modelPool.getTower("marker_rupee16.patt");
			gameContext.modelPool.getTower("marker_peace16.patt");
			gameContext.modelPool.getTower("marker_at16.patt");
			gameContext.modelPool.getTower("marker_hand16.patt");

			/* loading finished */
			gameThread.loadingDone = true;
			return null;

		}


		@Override
		protected void onPostExecute(Void result) {
			super.onPostExecute(result);
			waitDialog.dismiss();
			Log.d("Starting Preview", "Preview starting  ");
			startPreview();

		}
	}

	public class Config {
		public final static boolean DEBUG = false;
	}

}
