DemoGameThread.java :  » Graphics-3D-2D-OpenGL » dwarf-fw » se » ltu » android » demo » Android Open Source

Android Open Source » Graphics 3D 2D OpenGL » dwarf fw 
dwarf fw » se » ltu » android » demo » DemoGameThread.java
/* SVN FILE: $Id$ */
package se.ltu.android.demo;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import android.content.res.AssetFileDescriptor;
import android.util.Log;
import android.view.animation.AccelerateDecelerateInterpolator;

import se.ltu.android.demo.scene.Board;
import se.ltu.android.demo.scene.CameraLeaf;
import se.ltu.android.demo.scene.LightNode;
import se.ltu.android.demo.scene.Node;
import se.ltu.android.demo.scene.PieceData;
import se.ltu.android.demo.scene.Spatial;
import se.ltu.android.demo.scene.TriMesh;
import se.ltu.android.demo.scene.animation.AnimationListener;
import se.ltu.android.demo.scene.animation.KeyFrame;
import se.ltu.android.demo.scene.animation.KeyFrameAnimation;
import se.ltu.android.demo.scene.camera.Camera;
import se.ltu.android.demo.scene.intersection.PickResult;
import se.ltu.android.demo.scene.intersection.Ray;
import se.ltu.android.demo.scene.shapes.*;
import se.ltu.android.demo.scene.state.Light;
import se.ltu.android.demo.scene.state.Material;
import se.ltu.android.demo.sensors.SensorHandler;
import se.ltu.android.demo.util.ObjLoader;

/**
 * A thread that updates the world based on input and sensor events. All kind
 * of game states and logic should be placed here. Very heavy calculations should
 * still be placed in a separate asynchronous task.
 * @author ke Svedin <ake.svedin@gmail.com>
 * @version $Revision$
 * @lastmodified $Date$
 */
public class DemoGameThread extends Thread implements AnimationListener {
  private final static String TAG = "GameThread";
  private final static int TARGET_FPS = 25;
  /**
   * [0] = x coordinate<br>
   * [1] = y coordinate<br>
   * [2] = type tap (1 = single, 2 = double, otherwise coordinates are ignored)
   */
  private static float[] tapCoords = new float[3];
  /**
   * [0] = x coordinate<br>
   * [1] = y coordinate<br>
   * [2] = coordinate change (0 = false, true otherwise)<br>
   * [3] = on click (0 = false, true otherwise)<br>
   */
  private static float[] trackInput = new float[4];
  private long timePerFrame = 1000;
  private long timeTarget;
  private Node world;
  private TriMesh[][] board_data = new TriMesh[8][8];
  private DemoGLSurfaceView mGLView;
  private boolean isRunning = true;
  private boolean isPaused = false;
  private TriMesh pickedMesh;
  
  // 
  private boolean moving_piece = false;
  private boolean moving_camera = false;
  
  // Camera variables
  private Camera[] camList;   // list of available cameras
  private int iCam = 0;     // camera pointer
  private CameraLeaf camLeaf = new CameraLeaf("Camera");  //used for animation camera movement
  private float[] modelM = new float[16];
  private int iCamSensor = 0;  // marks which camera that 
  
  // Materials for the pieces
  private Material lightMat;
  private Material darkMat;
  private Material lightMatPicked;
  private Material darkMatPicked;

  /**
   * Creates the game thread
   * @param glview
   */
  public DemoGameThread(DemoGLSurfaceView glview) {
    setName("GameThread");
    mGLView = glview;
    timeTarget = 1000 / TARGET_FPS;
    camList = new Camera[4];

    camList[0] = new Camera();
    camList[0].setIdentity();
    camList[0].setPosition(0, 0, 9);

    camList[1] = new Camera(); // used for rotation
    iCamSensor = 1;
    
    camList[2] = new Camera();
    camList[2].lookAt(5, -5, 3, 0, 0, -2.9f, 0, 0, 1);

    camList[3] = new Camera();
    camList[3].lookAt(6, 0, 6, 0, 0, -2.9f, 0, 0, 1);
  }

  /**
   * Starts the game loop
   */
  @Override
  public void run() {
    createWorld();
    mGLView.getRenderer().setCamera(camList[iCam]);
    mGLView.getRenderer().setScene(world);

    long lastTime = System.currentTimeMillis();
    long timeSleep;
    // Good place to put a method trace
    //Debug.startMethodTracing("mtrace");
    while (isRunning) {
      while (isPaused && isRunning) {
        try {
          sleep(100);
        } catch (InterruptedException e) {
        }
      }
      /*
       * This thread have some control over the frame rate
       * especially timePerFrame will be useful in animations
       */
      timePerFrame = System.currentTimeMillis() - lastTime;
      lastTime = System.currentTimeMillis();
      update();
      /* 
       * We want to sleep because we don't need to update as fast as possible.
       * This way the renderer gets more time and we get a slightly higher frame rate.
       * The last part on timeSleep (+ lastTime - System.currentTimeMillis()) is rather
       * important to get smooth animations.
       */
      timeSleep = timeTarget - timePerFrame + lastTime - System.currentTimeMillis();
      if (timeSleep > 0) {
        try {
          sleep(timeSleep);
        } catch (InterruptedException e) {
        }
      }
    }
  }

  private void update() {
    updateInput();
    world.update(timePerFrame); // updates animations
    updateCamera();
    mGLView.requestRender();
  }

  private void updateCamera() {
    if (iCam == iCamSensor && SensorHandler.getRotM4(modelM)) {
      camList[iCam].setRotationM(modelM);
    }
  }

  private void updateInput() {
    checkTap();
    checkTrack();
  }

  /**
   * @return
   */
  private void checkTap() {
    Ray pickRay = null;
    PickResult result = null;
    int nTaps = 0;

    synchronized (tapCoords) {
      if (tapCoords[2] == 1 && !moving_piece) {
        nTaps = 1;
        pickRay = camList[iCam].calculatePickRay(tapCoords[0],
            tapCoords[1]);
      }
      if (tapCoords[2] == 2 && !moving_camera) {
        // only move camera 0 and iCamSensor (straight above and sensor camera)
        if(iCam == 0 || iCam == iCamSensor) {
          nTaps = 2;
          pickRay = camList[iCam].calculatePickRay(tapCoords[0],
              tapCoords[1]);
        }
      }
      tapCoords[2] = 0;
    }
    if (pickRay != null) {
      result = new PickResult();
      world.calculatePick(pickRay, result);
    }
    if (result == null || !result.hasResult()) {
      return;
    }
    if(nTaps == 1) {
      handleSingleTap(result);
    }
    if(nTaps == 2) {
      handleDoubleTap(result);
    }
  }

  private void checkTrack() {
    synchronized (trackInput) {
      if (trackInput[3] != 0 && !moving_camera) {
        // switch camera
        iCam++;
        if (iCam == camList.length) {
          iCam = 0;
        }
        mGLView.getRenderer().setCamera(camList[iCam]);
      }
      trackInput[3] = 0;
      if (trackInput[2] != 0) {
        float[] pos = camList[iCam].getPosition();
        float newpos = pos[2] + trackInput[1];
        switch(iCam) {
        case 0:
          newpos = clamp(-1, 15, newpos);
          break;
        case 1:
          newpos = clamp(-15, 15, newpos);
          break;
          default:
            newpos = clamp(-1, 9, newpos);
        }
        if(iCam == 0 || iCam == 1) {
          camList[iCam].setPosition(pos[0], pos[1], newpos);
        } else {
          camList[iCam].lookAt(pos[0], pos[1], newpos, 0, 0, -2.9f, 0, 0, 1);
        }
        trackInput[0] = 0;
        trackInput[1] = 0;
        trackInput[2] = 0;
      }
    }
  }

  /**
   * Clamp value between lower and upper
   */
  private float clamp(float lower, float upper, float value) {
    if(value > upper) {
      return upper;
    }
    if(value < lower) {
      return lower;
    }
    return value;
  }

  private void handleSingleTap(PickResult result) {
    Log.d(TAG, "Single tap");
    Spatial spatial = result.getClosest();
    if (spatial.hasData() && (spatial.getData() instanceof PieceData)) {
      checkPickPiece((TriMesh)spatial);
      return;
    } 
    if (spatial.hasParent() && spatial.getParent().getName().equals("Board")) {
      // picked a square
      float[] to = spatial.getLocalTranslation();
      int col = Math.round(to[0] + 3.5f);
      int row = Math.round(to[1] + 3.5f);
      if (board_data[col][row] != null) {
        // picked a square that is occupied by a piece
        checkPickPiece(board_data[col][row]);
        return;
      }
      if (pickedMesh != null) {
        // picked an empty square and we have a previous selection
        moving_piece = true;
        float[] from = pickedMesh.getLocalTranslation();
        
        // create animation
        KeyFrame frame = new KeyFrame(2000);
        frame.setTranslation(to[0], to[1], from[2]);
        KeyFrameAnimation anim = new KeyFrameAnimation(this);
        anim.addFrame(frame);
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        pickedMesh.addController(anim);
        
        // update board
        board_data[col][row] = pickedMesh;
        col = Math.round(from[0] + 3.5f);
        row = Math.round(from[1] + 3.5f);
        board_data[col][row] = null;
        
        unselectPick();
      }
    }
  }
  
  private void handleDoubleTap(PickResult result) {
    Log.d(TAG, "Double tap");
    Spatial spatial = result.getClosest();
    if (spatial.hasParent() && spatial.getParent().getName().equals("Board")) {
      // picked a square
      float[] to = spatial.getLocalTranslation();
      int col = Math.round(to[0] + 3.5f);
      int row = Math.round(to[1] + 3.5f);
      if (board_data[col][row] != null) {
        // picked a square that is occupied by a piece
        return;
      }
      
      // square with no piece, set to go
      moving_camera = true;
      camLeaf.setCamera(camList[iCam]);
      float[] from = camLeaf.getLocalTranslation();
      
      // create animation
      KeyFrame frame = new KeyFrame(2000);
      frame.setTranslation(to[0], to[1], from[2]);
      KeyFrameAnimation anim = new KeyFrameAnimation(this);
      anim.addFrame(frame);
      anim.setInterpolator(new AccelerateDecelerateInterpolator());
      camLeaf.addController(anim);
    }
  }

  private void checkPickPiece(TriMesh spatial) {
    if (pickedMesh != null) {
      if(spatial.equals(pickedMesh)) {
        // picked the previously selected piece
        unselectPick();
        return;
      }
      // picked a new piece and we have a previous selection
      unselectPick();
      selectPick(spatial);
      return;
    }
    // picked a piece and we have no previous selection
    selectPick(spatial);
  }

  private void unselectPick() {
    if(((PieceData)pickedMesh.getData()).isDark()) {
      pickedMesh.setMaterial(darkMat);
    } else {
      pickedMesh.setMaterial(lightMat);
    }
    pickedMesh = null;    
  }
  
  private void selectPick(TriMesh spatial) {
    pickedMesh = spatial;
    if(((PieceData)pickedMesh.getData()).isDark()) {
      pickedMesh.setMaterial(darkMatPicked);
    } else {
      pickedMesh.setMaterial(lightMatPicked);
    }
  }

  /**
   * Pauses the game loop
   */
  public void onPause() {
    isPaused = true;
  }

  /**
   * Resumes the game loop
   */
  public void onResume() {
    isPaused = false;
  }

  private void createWorld() {
    // world = new Node("Root Node");
    TriMesh mesh;
    Board board;
    Quad quad;
    
    Material defaultMat = new Material();
    defaultMat.setUseColorMaterial(true);

    Light light = new Light();
    light.setPosition(new float[] { -1, 1, 1, 0 });
    light.setAmbient(new float[] { 0.1f, 0.1f, 0.1f, 1 });
    light.setDiffuse(new float[] { 0.8f, 0.8f, 0.8f, 1 });
    light.setSpecular(new float[] { 1, 1, 1, 1 });
    world = new LightNode("Root & Light", light);

    quad = new Quad("floor", 12.0f, 12.0f);
    quad.setLocalTranslation(0.0f, 0.0f, -3f);
    quad.setSolidColor(new float[] { 0.4f, 0.4f, 0.4f, 1.0f});
    quad.setPickable(false);
    quad.setMaterial(defaultMat);
    world.attachChild(quad);

    board = new Board("Board");
    board.setMaterial(defaultMat);
    board.setLocalTranslation(0, 0, -2.9f);
    
    lightMat = new Material();
    lightMat.setAmbient(1, 0.5f, 0, 1);
    lightMat.setDiffuse(1, 0.5f, 0, 1);
    lightMat.setSpecular(0.9f, 0.9f, 0.9f, 1.0f);
    lightMat.setShininess(40);
    
    darkMat = new Material();
    darkMat.setAmbient(0, 0.4f, 0.8f, 1);
    darkMat.setDiffuse(0, 0.4f, 0.8f, 1);
    darkMat.setSpecular(0.9f, 0.9f, 0.9f, 1.0f);
    darkMat.setShininess(40);
    
    lightMatPicked = new Material();
    lightMatPicked.copyFrom(lightMat);
    lightMatPicked.setAmbient(1, 0.86f, 0.31f, 1);
    lightMatPicked.setDiffuse(1, 0.86f, 0.31f, 1);
    
    darkMatPicked = new Material();
    darkMatPicked.copyFrom(darkMat);
    darkMatPicked.setAmbient(0.26f, 0.82f, 1, 1);
    darkMatPicked.setDiffuse(0.26f, 0.82f, 1, 1);
    
    PieceData pData;
    
    try {
      char col = 'c';
      int row = 2;
      TriMesh pawn = new TriMesh("Pawn");
      InputStream stream = mGLView.getContext().getResources().openRawResource(R.raw.pawn);
      DataInputStream dis = new DataInputStream(stream);
      pawn.importModel(dis);
      pawn.setLocalTranslation(PieceData.getColPos(col), PieceData
          .getRowPos(row), -2.8f);
      pData = new PieceData(col, row);
      pData.setDark(false);
      pawn.setData(pData);
      pawn.setMaterial(lightMat);
      board_data[PieceData.getColIndex(col)][PieceData.getRowIndex(row)] = pawn;
      world.attachChild(pawn);
      
      col = 'd';
      row = 3;
      TriMesh knight = new TriMesh("Knight");
      stream = mGLView.getContext().getResources().openRawResource(R.raw.knight);
      dis = new DataInputStream(stream);
      knight.importModel(dis);
      knight.setLocalTranslation(PieceData.getColPos(col), PieceData
          .getRowPos(row), -2.8f);
      pData = new PieceData(col, row);
      pData.setDark(false);
      knight.setData(pData);
      knight.setMaterial(lightMat);
      board_data[PieceData.getColIndex(col)][PieceData.getRowIndex(row)] = knight;
      world.attachChild(knight);
      
      col = 'e';
      row = 2;
      TriMesh king = new TriMesh("King");
      stream = mGLView.getContext().getResources().openRawResource(R.raw.king);
      dis = new DataInputStream(stream);
      king.importModel(dis);
      king.setLocalTranslation(PieceData.getColPos(col), PieceData
          .getRowPos(row), -2.8f);
      pData = new PieceData(col, row);
      pData.setDark(false);
      king.setData(pData);
      king.setMaterial(lightMat);
      board_data[PieceData.getColIndex(col)][PieceData.getRowIndex(row)] = king;
      world.attachChild(king);
      
      col = 'd';
      row = 6;
      mesh = pawn.cloneMesh("Cloned pawn");
      mesh.setLocalTranslation(PieceData.getColPos(col), PieceData
          .getRowPos(row), -2.8f);
      mesh.setData(new PieceData(col, row));
      pData = new PieceData(col, row);
      pData.setDark(true);
      mesh.setData(pData);
      mesh.setMaterial(darkMat);
      board_data[PieceData.getColIndex(col)][PieceData.getRowIndex(row)] = mesh;
      world.attachChild(mesh);
      
      col = 'e';
      row = 6;
      mesh = mesh.cloneMesh("Cloned knight");
      mesh.setLocalTranslation(PieceData.getColPos(col), PieceData
          .getRowPos(row), -2.8f);
      mesh.setData(new PieceData(col, row));
      pData = new PieceData(col, row);
      pData.setDark(true);
      mesh.setData(pData);
      mesh.setMaterial(darkMat);
      board_data[PieceData.getColIndex(col)][PieceData.getRowIndex(row)] = mesh;
      world.attachChild(mesh);
      
    } catch (IOException e) {
      e.printStackTrace();
    }
    
    /*
    // convert models
    try {
      convertModel(R.raw.pawn2, "/sdcard/pawn.mod");
      convertModel(R.raw.knight2, "/sdcard/knight.mod");
      convertModel(R.raw.king2, "/sdcard/king.mod");
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    */

    world.attachChild(board);
    world.attachChild(camLeaf);
    world.updateTransform();
    world.updateWorldBound(false);
  }

  /**
   * Converts a model from OBJ to our TriMesh's binary format.
   * @param inputObj a resource identifier for an OBJ-file (like R.raw.whatever)
   * @param outputMod an output file location (like "/sdcard/whatever.mod").
   * @throws IOException if there was any error
   */
  @SuppressWarnings("unused")
  private void convertModel(int inputObj, String outputMod) throws IOException {
    ObjLoader loader = new ObjLoader();
        AssetFileDescriptor fd = mGLView.getContext().getResources().openRawResourceFd(inputObj);
    TriMesh tPawn = loader.loadModel("Pawn", fd);
    FileOutputStream fos = new FileOutputStream(outputMod);
    DataOutputStream dos = new DataOutputStream(fos);
    tPawn.exportModel(dos);
    dos.flush();
    dos.close();
    Log.d(TAG, "Converted obj to mod: "+outputMod);
  }

  /**
   * Register a double tap on this thread
   * 
   * @param x
   *            screen x coordinate
   * @param y
   *            screen y coordinate
   */
  public static void onDoubleTap(float x, float y) {
    synchronized (tapCoords) {
      tapCoords[0] = x;
      tapCoords[1] = y;
      tapCoords[2] = 2;
    }
  }
  
  /**
   * Register a single tap on this thread
   * 
   * @param x
   *            screen x coordinate
   * @param y
   *            screen y coordinate
   */
  public static void onSingleTap(float x, float y) {
    synchronized (tapCoords) {
      tapCoords[0] = x;
      tapCoords[1] = y;
      tapCoords[2] = 1;
    }
  }

  /**
   * Register a track ball click on this thread.
   */
  public static void onTrackballClick() {
    synchronized (trackInput) {
      trackInput[3] = 1;
    }
  }
  
  /**
   * Register a track ball move on this thread
   * @param x relative movement x coordinate
   * @param y relative movement y coordinate
   */
  public static void onTrackballMove(float x, float y) {
    synchronized (trackInput) {
      trackInput[0] += x;
      trackInput[1] += y;
      trackInput[2] = 1;
    }
  }

  /**
   * Handles the end of an animation
   */
  @Override
  public void onAnimationEnd(KeyFrameAnimation anim, Spatial spatial) {
    if(spatial != null) {
      if(spatial instanceof CameraLeaf) {
        // it's a camera
        moving_camera = false;
      } else {
        // it's a piece
        moving_piece = false;
      }
      spatial.removeController(anim);
    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.