/** * Copyright (C) 2013 Gundog Studios LLC. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.godsandtowers.graphics; import java.util.ArrayList; import com.godsandtowers.core.GameInfo; import com.godsandtowers.core.grid.Grid; import com.godsandtowers.core.grid.GridSquare; import com.godsandtowers.core.networking.SpriteSnapshot; import com.godsandtowers.messaging.ApplicationMessageProcessor; import com.godsandtowers.messaging.GLMessageProcessor; import com.godsandtowers.messaging.LogicMessageProcessor; import com.godsandtowers.sprites.Creature; import com.godsandtowers.sprites.MovingProjectile; import com.godsandtowers.sprites.Player; import com.godsandtowers.sprites.Tower; import com.gundogstudios.gl.Actions; import com.gundogstudios.gl.CombinedModel; import com.gundogstudios.gl.Scene; import com.gundogstudios.gl.Sprite; import com.gundogstudios.gl.TouchPlane; import com.gundogstudios.gl.custom.Border; import com.gundogstudios.gl.custom.SkyBox; import com.gundogstudios.gl.custom.Wall; import com.gundogstudios.modules.GLES11Module; import com.gundogstudios.modules.Modules; public class GameSurface { private static final float TOUCH_SCALE = 0.05f; private static final float DEFAULT_XROT = -20f; private static final float DEFAULT_XTRANS = 0f; private static final float DEFAULT_YTRANS = 4f; private static final float DEFAULT_ZTRANS = -10f; private float xMaxTrans; private float yMaxTrans; private float xRot = DEFAULT_XROT; private float xTrans = DEFAULT_XTRANS; private float yTrans = DEFAULT_YTRANS; private float zTrans = DEFAULT_ZTRANS; private int pressedX = 0; private int pressedY = 0; private int height; private boolean released = false; private Scene[] boards; private int activeBoard; private GameInfo gameInfo; private long lastRender = 0; private TouchPlane touchPlane; private ModelFetcher modelFetcher; public GameSurface(GameInfo gameInfo) { this.gameInfo = gameInfo; float rowSize = gameInfo.getPlayers()[0].getGrid().getNumRows(); float colSize = gameInfo.getPlayers()[0].getGrid().getNumColumns(); xMaxTrans = colSize / 2f; yMaxTrans = rowSize; } public void release() { if (modelFetcher != null) { modelFetcher.release(); modelFetcher = null; } } public void onSurfaceCreated() { GLES11Module gl = Modules.GL; gl.glClearColor(.5f, .5f, .5f, 1.0f); gl.glShadeModel(GLES11Module.GL_SMOOTH); gl.glEnable(GLES11Module.GL_DITHER); // Culling does not work with current 3D models due to transparency in the textures // gl.glEnable(GLES11Module.GL_CULL_FACE); // gl.glCullFace(GLES11Module.GL_CCW); gl.glClearDepthf(1.0f); gl.glEnable(GLES11Module.GL_DEPTH_TEST); gl.glDepthFunc(GLES11Module.GL_LESS); gl.glHint(GLES11Module.GL_PERSPECTIVE_CORRECTION_HINT, GLES11Module.GL_FASTEST); gl.glEnable(GLES11Module.GL_BLEND); gl.glBlendFunc(GLES11Module.GL_SRC_ALPHA, GLES11Module.GL_ONE_MINUS_SRC_ALPHA); gl.glEnable(GLES11Module.GL_ALPHA_TEST); gl.glAlphaFunc(GLES11Module.GL_GEQUAL, .01f); Player[] players = gameInfo.getPlayers(); int rows = players[0].getGrid().getNumRows(); int columns = players[0].getGrid().getNumColumns(); boards = new Scene[players.length]; touchPlane = new TouchPlane(rows, columns, 1f); this.modelFetcher = new ModelFetcher(); String background = gameInfo.getBoard().getBackgroundTextureName(); String wall = gameInfo.getBoard().getWallTextureName(); try { modelFetcher.loadCustomModels(rows + 2, columns + 2, background, wall); } catch (OutOfMemoryError e) { Modules.MESSENGER.submit(ApplicationMessageProcessor.ID, ApplicationMessageProcessor.LOWER_GRAPHICS); } for (int i = 0; i < players.length; i++) { Grid grid = players[i].getGrid(); boards[i] = new Scene(); // TODO Take count here, and until addSprite is called for all of the models, dont call FINISHED_LOADING ArrayList<Sprite> sprites = new ArrayList<Sprite>(); sprites.add(new SpriteSnapshot(0, 0, Actions.IDLE, background, 0, 0, 0, 0, false)); sprites.add(new SpriteSnapshot(0, 0, Actions.IDLE, SkyBox.NAME, 0, 0, 0, 0, false)); sprites.add(new SpriteSnapshot(0, 0, Actions.IDLE, Wall.NAME, 0, 0, 0, 0, false)); sprites.add(new SpriteSnapshot(0, 0, Actions.IDLE, Border.NAME, 0, 0, 0, 0, false)); Modules.MESSENGER.submit(GLMessageProcessor.ID, GLMessageProcessor.ADD_SPRITE_BATCH, i, sprites); sprites = new ArrayList<Sprite>(); for (Creature creature : grid.getCreatures()) { sprites.add(creature); } Modules.MESSENGER.submit(GLMessageProcessor.ID, GLMessageProcessor.ADD_SPRITE_BATCH, i, sprites); sprites = new ArrayList<Sprite>(); for (Tower tower : grid.getTowers()) { sprites.add(tower); } Modules.MESSENGER.submit(GLMessageProcessor.ID, GLMessageProcessor.ADD_SPRITE_BATCH, i, sprites); sprites = new ArrayList<Sprite>(); for (MovingProjectile projectile : grid.getMovingProjectiles()) { sprites.add(projectile); } Modules.MESSENGER.submit(GLMessageProcessor.ID, GLMessageProcessor.ADD_SPRITE_BATCH, i, sprites); ArrayList<Sprite> transparentSprites = new ArrayList<Sprite>(); sprites = new ArrayList<Sprite>(); for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { GridSquare square = grid.getGridSquare(r, c); if (square.isStartOrFinish()) transparentSprites.add(square); if (square.hasModel()) sprites.add(square); } } Modules.MESSENGER.submit(GLMessageProcessor.ID, GLMessageProcessor.ADD_SPRITE_BATCH, i, sprites); Modules.MESSENGER.submit(GLMessageProcessor.ID, GLMessageProcessor.ADD_TRANSPARENT_SPRITE_BATCH, i, transparentSprites); } activeBoard = gameInfo.getLocalPlayerID(); Modules.MESSENGER.submit(LogicMessageProcessor.ID, LogicMessageProcessor.GL_FINISHED_LOADING); } public void onDrawFrame() { if (modelFetcher == null) { Modules.LOG.info("GameSurface", "modelFetcher was released, unable to render"); try { Thread.sleep(50); } catch (InterruptedException e) { } return; } GLES11Module gl = Modules.GL; long currentTime = System.currentTimeMillis(); int timePassed = (int) (currentTime - lastRender); if (timePassed < 0 || timePassed > 1000) { timePassed = 15; } lastRender = currentTime; modelFetcher.loadVBOandTextures(); gl.glClear(GLES11Module.GL_COLOR_BUFFER_BIT | GLES11Module.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GLES11Module.GL_MODELVIEW); gl.glLoadIdentity(); gl.glPushMatrix(); gl.glRotatef(xRot, 1, 0, 0); gl.glTranslatef(xTrans, yTrans, zTrans); if (activeBoard != gameInfo.getLocalPlayerID()) { released = false; } else if (released) { int[] arr = touchPlane.touch(pressedX, height - pressedY); if (arr != null) { Modules.MESSENGER.submit(LogicMessageProcessor.ID, LogicMessageProcessor.GRID_TOUCHED, gameInfo.getLocalPlayerID(), arr[0], arr[1]); } released = false; } boards[activeBoard].draw(timePassed); Modules.PROFILER.updateRenderFPS(); gl.glPopMatrix(); Thread.yield(); } public void onSurfaceChanged(int width, int height) { GLES11Module gl = Modules.GL; gl.glViewport(0, 0, width, height); gl.glMatrixMode(GLES11Module.GL_PROJECTION); gl.glLoadIdentity(); this.height = height; Modules.GLUTIL.gluPerspective(45.0f, (float) width / (float) height, 0.01f, 100.0f); gl.glMatrixMode(GLES11Module.GL_MODELVIEW); gl.glLoadIdentity(); } public void nextBoard() { if (++activeBoard > boards.length - 1) activeBoard = 0; } public void previousBoard() { if (--activeBoard < 0) activeBoard = boards.length - 1; } public void addSprite(int player, Sprite sprite, CombinedModel model) { if (model != null) boards[player].addSprite(sprite, model); } public void removeSprite(int player, Sprite sprite) { boards[player].removeSprite(sprite); } public void addTransparentSprite(int player, Sprite sprite, CombinedModel model) { if (model != null) boards[player].addTransparentSprite(sprite, model); } public void removeTransparentSprite(int player, Sprite sprite) { boards[player].removeTransparentSprite(sprite); } public void zoomIn() { if (zTrans < -1f) zTrans += 1f; } public void zoomOut() { if (zTrans > -15f) zTrans -= 1f; } public void rotateUp() { if (xRot > -75f) xRot -= 5f; } public void rotateDown() { if (xRot < 0f) xRot += 5f; } public void resetView() { xRot = DEFAULT_XROT; xTrans = DEFAULT_XTRANS; yTrans = DEFAULT_YTRANS; zTrans = DEFAULT_ZTRANS; } public void setPressed(int pressedX, int pressedY) { this.pressedX = pressedX; this.pressedY = pressedY; } public void setReleased(boolean released) { this.released = released; } public boolean getReleased() { return released; } public void updateTrans(float dx, float dy, float dz) { xTrans += dx * TOUCH_SCALE * zTrans / DEFAULT_ZTRANS; yTrans -= dy * TOUCH_SCALE * zTrans / DEFAULT_ZTRANS; if (xTrans > xMaxTrans) xTrans = xMaxTrans; else if (xTrans < -xMaxTrans) xTrans = -xMaxTrans; if (yTrans > yMaxTrans) yTrans = yMaxTrans; else if (yTrans < -yMaxTrans) yTrans = -yMaxTrans; } public ModelFetcher getModelFetcher() { return modelFetcher; } }