/** * 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.gundogstudios.gl; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; import com.gundogstudios.modules.AssetModule; import com.gundogstudios.modules.GLES11Module; import com.gundogstudios.modules.Modules; public class ModelManager { private static final int GS_FRAME_RATE = 20; private static final String MODEL_PATH = "models/"; private HashMap<String, GSModelVBOs> modelVBOs; private Queue<GSModelData> modelData; public ModelManager() { modelVBOs = new HashMap<String, GSModelVBOs>(); modelData = new LinkedList<GSModelData>(); } public void releaseVBOs() { ArrayList<Integer> vbos = new ArrayList<Integer>(); for (GSModelVBOs model : modelVBOs.values()) { addVBO(vbos, model.getTextureBufferId()); addVBO(vbos, model.getIndexBufferId()); addVBOs(vbos, model.getAttackBufferIds()); addVBOs(vbos, model.getDeathBufferIds()); addVBOs(vbos, model.getIdleBufferIds()); addVBOs(vbos, model.getMoveBufferIds()); } int[] ids = new int[vbos.size()]; int c = 0; for (Integer id : vbos) { ids[c++] = id; } Modules.GL.glDeleteBuffers(ids.length, ids, 0); } private void addVBO(ArrayList<Integer> vbos, int id) { if (id != 0) vbos.add(id); } private void addVBOs(ArrayList<Integer> vbos, int[] ids) { if (ids != null) { for (int id : ids) { vbos.add(id); } } } public GSModelVBOs getGSModelVBOs(String name, boolean isGLThread) { GSModelVBOs vbos = modelVBOs.get(name); if (vbos == null) { long start = System.currentTimeMillis(); vbos = loadModelData(name, isGLThread); Modules.LOG.info("ModelManager", "TOOK " + (System.currentTimeMillis() - start) + " to load the mesh"); } return vbos; } private GSModelVBOs loadModelData(String name, boolean isGLThread) { try { AssetModule assets = Modules.ASSETS; InputStream input = assets.openInput(MODEL_PATH, name); ReadableByteChannel channel = Channels.newChannel(input);// input.getChannel(); int headerOffset = Short.SIZE / Byte.SIZE * 11; ByteBuffer buffer = ByteBuffer.allocate(headerOffset); channel.read(buffer); buffer.position(0); // Load GS1 header information int indexBufferLength = buffer.getShort(); int uvBufferLength = buffer.getShort(); int frameLength = buffer.getShort(); int idleFrames = buffer.getShort(); int idleAnimationLength = buffer.getShort(); int moveFrames = buffer.getShort(); int moveAnimationLength = buffer.getShort(); int attackFrames = buffer.getShort(); int attackAnimationLength = buffer.getShort(); int deathFrames = buffer.getShort(); int deathAnimationLength = buffer.getShort(); // long offset = headerOffset; int size = indexBufferLength + uvBufferLength + frameLength * (idleFrames + moveFrames + attackFrames + deathFrames); buffer = ByteBuffer.allocate(size); channel.read(buffer); buffer.position(0); GSModelData data = new GSModelData(name, indexBufferLength, uvBufferLength, frameLength, idleFrames, idleAnimationLength, moveFrames, moveAnimationLength, attackFrames, attackAnimationLength, deathFrames, deathAnimationLength, buffer); input.close(); if (isGLThread) { return convertToVBO(data); } else { synchronized (modelData) { modelData.add(data); modelData.wait(); } return modelVBOs.get(name); } } catch (IOException e) { throw new RuntimeException(e); } catch (InterruptedException e) { throw new RuntimeException(e); } } public void generateVBOs() { synchronized (modelData) { GSModelData data; while ((data = modelData.poll()) != null) convertToVBO(data); modelData.notifyAll(); } } private GSModelVBOs convertToVBO(GSModelData data) { int maxFrames = ModelUtils.getMeshFrameRate(); ByteBuffer buffer = data.buffer; int indexBufferLength = data.indexBufferLength; int uvBufferLength = data.uvBufferLength; int frameLength = data.frameLength; int bufferOffset = 0; int indexBufferId = generateIndexBuffer(buffer, bufferOffset, indexBufferLength); bufferOffset += indexBufferLength; int textureBufferId = generateTextureBuffer(buffer, bufferOffset, uvBufferLength); bufferOffset += uvBufferLength; int idleFrames = data.idleFrames; int[] idleBufferIds = generateVertexBuffer(buffer, bufferOffset, frameLength, idleFrames, maxFrames); bufferOffset += frameLength * idleFrames; int moveFrames = data.moveFrames; int[] moveBufferIds = generateVertexBuffer(buffer, bufferOffset, frameLength, moveFrames, maxFrames); bufferOffset += frameLength * moveFrames; int attackFrames = data.attackFrames; int[] attackBufferIds = generateVertexBuffer(buffer, bufferOffset, frameLength, attackFrames, maxFrames); bufferOffset += frameLength * attackFrames; int deathFrames = data.deathFrames; int[] deathBufferIds = generateVertexBuffer(buffer, bufferOffset, frameLength, deathFrames, maxFrames); GSModelVBOs vbos = new GSModelVBOs(indexBufferId, indexBufferLength / 2, textureBufferId, data.idleAnimationLength, idleBufferIds, data.moveAnimationLength, moveBufferIds, data.attackAnimationLength, attackBufferIds, data.deathAnimationLength, deathBufferIds); modelVBOs.put(data.name, vbos); return vbos; } private int generateIndexBuffer(ByteBuffer buffer, int offset, int size) { buffer.limit(offset + size); buffer.position(offset); GLES11Module gl = Modules.GL; int[] vboIds = new int[1]; gl.glGenBuffers(1, vboIds, 0); gl.glBindBuffer(GLES11Module.GL_ELEMENT_ARRAY_BUFFER, vboIds[0]); gl.glBufferData(GLES11Module.GL_ELEMENT_ARRAY_BUFFER, size, buffer, GLES11Module.GL_STATIC_DRAW); gl.glBindBuffer(GLES11Module.GL_ELEMENT_ARRAY_BUFFER, 0); return vboIds[0]; } private int generateTextureBuffer(ByteBuffer buffer, int offset, int size) { buffer.limit(offset + size); buffer.position(offset); GLES11Module gl = Modules.GL; int[] vboIds = new int[1]; gl.glGenBuffers(vboIds.length, vboIds, 0); gl.glBindBuffer(GLES11Module.GL_ARRAY_BUFFER, vboIds[0]); gl.glBufferData(GLES11Module.GL_ARRAY_BUFFER, size, buffer, GLES11Module.GL_STATIC_DRAW); gl.glBindBuffer(GLES11Module.GL_ARRAY_BUFFER, 0); return vboIds[0]; } private int[] generateVertexBuffer(ByteBuffer buffer, int offset, int frameLength, int frameCount, int maxFrames) { if (frameCount == 0) return null; GLES11Module gl = Modules.GL; int numVBOs = (frameCount < maxFrames) ? frameCount : maxFrames; int[] vboIds = new int[numVBOs]; gl.glGenBuffers(vboIds.length, vboIds, 0); int step = GS_FRAME_RATE / maxFrames; int current = 0; for (int i = 0; i < frameCount; i++) { if (i % step == 0) { buffer.limit(offset + frameLength); buffer.position(offset); gl.glBindBuffer(GLES11Module.GL_ARRAY_BUFFER, vboIds[current]); gl.glBufferData(GLES11Module.GL_ARRAY_BUFFER, frameLength, buffer, GLES11Module.GL_STATIC_DRAW); current++; } offset += frameLength; } gl.glBindBuffer(GLES11Module.GL_ARRAY_BUFFER, 0); return vboIds; } private class GSModelData { public String name; public int indexBufferLength; public int uvBufferLength; public int frameLength; public int idleFrames; public int idleAnimationLength; public int moveFrames; public int moveAnimationLength; public int attackFrames; public int attackAnimationLength; public int deathFrames; public int deathAnimationLength; public ByteBuffer buffer; public GSModelData(String name, int indexBufferLength, int uvBufferLength, int frameLength, int idleFrames, int idleAnimationLength, int moveFrames, int moveAnimationLength, int attackFrames, int attackAnimationLength, int deathFrames, int deathAnimationLength, ByteBuffer buffer) { this.name = name; this.indexBufferLength = indexBufferLength; this.uvBufferLength = uvBufferLength; this.frameLength = frameLength; this.idleFrames = idleFrames; this.idleAnimationLength = idleAnimationLength; this.moveFrames = moveFrames; this.moveAnimationLength = moveAnimationLength; this.attackFrames = attackFrames; this.attackAnimationLength = attackAnimationLength; this.deathFrames = deathFrames; this.deathAnimationLength = deathAnimationLength; this.buffer = buffer; } } }