package Hexel.rendering;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import javax.imageio.ImageIO;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GL2ES1;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLException;
import javax.media.opengl.fixedfunc.GLPointerFunc;
import Hexel.Engine;
import Hexel.LogData;
import Hexel.chunk.Chunk;
import Hexel.chunk.Chunks;
import Hexel.math.HexGeometry;
import Hexel.math.Vector2d;
import Hexel.math.Vector3i;
import com.jogamp.opengl.util.glsl.ShaderCode;
import com.jogamp.opengl.util.glsl.ShaderProgram;
import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureData;
import com.jogamp.opengl.util.texture.TextureIO;
import com.jogamp.opengl.util.texture.awt.AWTTextureIO;
public class ChunkRenderer {
private Chunks chunks;
private Texture ttex;
private LinkedBlockingQueue<GLChunk> glChunksToLoad = new LinkedBlockingQueue<GLChunk>();
private HashMap<Vector3i, GLChunk> glChunkTable = new HashMap<Vector3i, GLChunk>();
private Integer chunksToLoad = 0;
private Engine engine;
public HashMap<Vector3i, GLChunk> getGLChunkTable() {
return this.glChunkTable;
}
private ShaderProgram transparentsShader;
private int tsUPMatrix;
private int tsUMVMatrix;
private int tsTex;
private ShaderProgram solidsShader;
private int ssUPMatrix;
private int ssUMVMatrix;
private int ssTex;
private ExecutorService chunkVertexBufferGeneratorThreadPool = Executors
.newFixedThreadPool(8);
public ChunkRenderer(Engine engine, Chunks chunks) {
this.engine = engine;
this.chunks = chunks;
}
public void init(GLAutoDrawable drawable, GL2 gl) {
try {
this.loadTex(gl);
} catch(IOException e){
System.out.println("unable to load atlas");
e.printStackTrace();
System.exit(1);
}
ShaderCode tsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(),
"shaders", "shaders/bin", "transparents", true);
ShaderCode tsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
"shaders", "shaders/bin", "transparents", true);
tsVp.defaultShaderCustomization(gl, true, true);
tsFp.defaultShaderCustomization(gl, true, true);
transparentsShader = new ShaderProgram();
transparentsShader.add(tsVp);
transparentsShader.add(tsFp);
if(!transparentsShader.link(gl, System.err)) {
throw new GLException("could not link program: " + transparentsShader);
}
gl.glBindAttribLocation(transparentsShader.program(), 0, "aVertexPosition");
gl.glBindAttribLocation(transparentsShader.program(), 1, "aTextureCoord");
tsUPMatrix = gl.glGetUniformLocation(transparentsShader.program(), "uPMatrix");
tsUMVMatrix = gl.glGetUniformLocation(transparentsShader.program(), "uMVMatrix");
tsTex = gl.glGetUniformLocation(transparentsShader.program(), "uSampler");
ShaderCode ssVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(),
"shaders", "shaders/bin", "solids", true);
ShaderCode ssFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
"shaders", "shaders/bin", "solids", true);
ssVp.defaultShaderCustomization(gl, true, true);
ssFp.defaultShaderCustomization(gl, true, true);
solidsShader = new ShaderProgram();
solidsShader.add(ssVp);
solidsShader.add(ssFp);
if(!solidsShader.link(gl, System.err)) {
throw new GLException("could not link program: " + solidsShader);
}
gl.glBindAttribLocation(solidsShader.program(), 0, "aVertexPosition");
gl.glBindAttribLocation(solidsShader.program(), 1, "aTextureCoord");
gl.glBindAttribLocation(solidsShader.program(), 2, "aVertexColor");
ssUPMatrix = gl.glGetUniformLocation(solidsShader.program(), "uPMatrix");
ssUMVMatrix = gl.glGetUniformLocation(solidsShader.program(), "uMVMatrix");
ssTex = gl.glGetUniformLocation(solidsShader.program(), "uSampler");
gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
GL.GL_NEAREST);
gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
GL.GL_NEAREST);
}
private void loadTex(GL2 gl) throws IOException{
BufferedImage atlas = TextureAtlas.getAtlas();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(atlas, "png", baos);
InputStream is = new ByteArrayInputStream(baos.toByteArray());
TextureData data = TextureIO.newTextureData(gl.getGLProfile(), is, false, "png");
this.ttex = TextureIO.newTexture(data);
}
public void loadChunks(final ArrayList<Vector3i> points) {
if (points.size() == 0)
return;
for (Vector3i p : points) {
if (!hasChunk(p))
this.glChunkTable.put(p, null);
}
synchronized (chunksToLoad){
chunksToLoad += points.size();
LogData.set("chunksToLoad", "" + chunksToLoad);
}
this.chunkVertexBufferGeneratorThreadPool.execute(new Runnable() {
@Override
public void run() {
for (Vector3i point : points) {
Chunk chunk = ChunkRenderer.this.chunks.getChunk(point);
GLChunk glChunk = new GLChunk(engine, chunk, ChunkRenderer.this.chunks);
glChunk.position = new Vector3i(chunk.cx, chunk.cy,
chunk.cz);
glChunk.genArrayList();
ChunkRenderer.this.loadGLChunk(glChunk);
synchronized (chunksToLoad){
chunksToLoad -= 1;
LogData.set("chunksToLoad", "" + chunksToLoad);
}
}
}
});
}
public boolean hasChunk(Vector3i pos) {
return this.glChunkTable.containsKey(pos);
}
public void loadGLChunk(GLChunk glChunk) {
this.glChunksToLoad.add(glChunk);
}
public void unloadGLChunk(Vector3i pos) {
this.glChunkTable.remove(pos);
}
public void update(GL2 gl) {
int i = 0;
LogData.set("chunksToBuffer", "" + this.glChunksToLoad.size());
while (!this.glChunksToLoad.isEmpty()) {
GLChunk glChunk = this.glChunksToLoad.poll();
glChunk.initBuffer(gl);
this.glChunkTable.put(glChunk.position, glChunk);
i++;
if (i > 10)
break;
}
LogData.set("chunksToBuffer", "" + this.glChunksToLoad.size());
}
public void render(GL2 gl) {
this.ttex.enable(gl);
this.ttex.bind(gl);;
Iterator<Map.Entry<Vector3i, GLChunk>> it = this.glChunkTable.entrySet().iterator();
solidsShader.useProgram(gl, true);
it = this.glChunkTable.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Vector3i, GLChunk> entry = it.next();
Vector3i position = entry.getKey();
GLChunk glChunk = entry.getValue();
if (glChunk == null || glChunk.buffer == null)
continue;
gl.glPushMatrix();
Vector2d hexPosition = new Vector2d();
HexGeometry.hexToCartesian(position.x, position.y, hexPosition);
gl.glTranslatef((float) (hexPosition.x * 16),
(float) (hexPosition.y * 16), position.z * 16);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, glChunk.buffer.id[0]);
gl.glVertexAttribPointer(0, 3, GL.GL_FLOAT, false, 9*4, 0);
gl.glVertexAttribPointer(1, 2, GL.GL_FLOAT, false, 9*4, 3*4);
gl.glVertexAttribPointer(2, 4, GL.GL_FLOAT, false, 9*4, 5*4);
gl.glEnableVertexAttribArray(0);
gl.glEnableVertexAttribArray(1);
gl.glEnableVertexAttribArray(2);
float[] mv = new float[16];
gl.glGetFloatv(GL2.GL_MODELVIEW_MATRIX, mv, 0);
gl.glUniformMatrix4fv(ssUMVMatrix, 1, false, mv, 0);
float[] p = new float[16];
gl.glGetFloatv(GL2.GL_PROJECTION_MATRIX, p, 0);
gl.glUniformMatrix4fv(ssUPMatrix, 1, false, p, 0);
gl.glUniform1i(ssTex, 0);
gl.glDrawArrays(GL.GL_TRIANGLES, 0, glChunk.buffer.size / (4 * 9));
gl.glDisableVertexAttribArray(0);
gl.glDisableVertexAttribArray(1);
gl.glDisableVertexAttribArray(2);
gl.glPopMatrix();
}
solidsShader.useProgram(gl, false);
transparentsShader.useProgram(gl, true);
it = this.glChunkTable.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Vector3i, GLChunk> entry = it.next();
Vector3i position = entry.getKey();
GLChunk glChunk = entry.getValue();
if (glChunk == null || glChunk.buffer == null)
continue;
gl.glPushMatrix();
Vector2d hexPosition = new Vector2d();
HexGeometry.hexToCartesian(position.x, position.y, hexPosition);
gl.glTranslatef((float) (hexPosition.x * 16),
(float) (hexPosition.y * 16), position.z * 16);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, glChunk.buffer.id[0]);
gl.glVertexAttribPointer(0, 3, GL.GL_FLOAT, false, 9*4, 0);
gl.glVertexAttribPointer(1, 2, GL.GL_FLOAT, false, 9*4, 3*4);
gl.glEnableVertexAttribArray(0);
gl.glEnableVertexAttribArray(1);
float[] mv = new float[16];
gl.glGetFloatv(GL2.GL_MODELVIEW_MATRIX, mv, 0);
gl.glUniformMatrix4fv(tsUMVMatrix, 1, false, mv, 0);
float[] p = new float[16];
gl.glGetFloatv(GL2.GL_PROJECTION_MATRIX, p, 0);
gl.glUniformMatrix4fv(tsUPMatrix, 1, false, p, 0);
gl.glUniform1i(tsTex, 0);
gl.glDrawArrays(GL.GL_TRIANGLES, 0, glChunk.buffer.size / (4 * 9));
gl.glDisableVertexAttribArray(0);
gl.glDisableVertexAttribArray(1);
gl.glPopMatrix();
}
transparentsShader.useProgram(gl, false);
this.ttex.disable(gl);
}
}