/*
* Visage
* Copyright (c) 2015-2016, Aesen Vismea <aesen@unascribed.com>
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.surgeplay.visage.slave.render;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.List;
import javax.imageio.ImageIO;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import com.google.common.collect.Lists;
import com.surgeplay.visage.Visage;
import com.surgeplay.visage.slave.render.primitive.Primitive;
import com.surgeplay.visage.util.Images;
import static com.surgeplay.visage.slave.util.Errors.checkGLError;
import static org.lwjgl.opengl.ARBVertexBufferObject.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;
import static org.lwjgl.util.glu.GLU.gluPerspective;
public abstract class Renderer {
public static final float normalX = -0.2f;
public static final float normalY = 0;
public static final float normalZ = -1;
public static final float[] vertices = {
// Front
-1.0f, -1.0f, 1.0f,
normalX, normalY, normalZ,
1.0f, -1.0f, 1.0f,
normalX, normalY, normalZ,
1.0f, 1.0f, 1.0f,
normalX, normalY, normalZ,
-1.0f, 1.0f, 1.0f,
normalX, normalY, normalZ,
// Back
-1.0f, -1.0f, -1.0f,
normalX, normalY, normalZ,
1.0f, -1.0f, -1.0f,
normalX, normalY, normalZ,
1.0f, 1.0f, -1.0f,
normalX, normalY, normalZ,
-1.0f, 1.0f, -1.0f,
normalX, normalY, normalZ,
// Top
-1.0f, 1.0f, 1.0f,
normalX, normalY, normalZ,
1.0f, 1.0f, 1.0f,
normalX, normalY, normalZ,
1.0f, 1.0f, -1.0f,
normalX, normalY, normalZ,
-1.0f, 1.0f, -1.0f,
normalX, normalY, normalZ,
// Bottom
-1.0f, -1.0f, -1.0f,
normalX, normalY, normalZ,
1.0f, -1.0f, -1.0f,
normalX, normalY, normalZ,
1.0f, -1.0f, 1.0f,
normalX, normalY, normalZ,
-1.0f, -1.0f, 1.0f,
normalX, normalY, normalZ,
// Left
1.0f, -1.0f, 1.0f,
normalX, normalY, normalZ,
1.0f, -1.0f, -1.0f,
normalX, normalY, normalZ,
1.0f, 1.0f, -1.0f,
normalX, normalY, normalZ,
1.0f, 1.0f, 1.0f,
normalX, normalY, normalZ,
// Right
-1.0f, -1.0f, -1.0f,
normalX, normalY, normalZ,
-1.0f, -1.0f, 1.0f,
normalX, normalY, normalZ,
-1.0f, 1.0f, 1.0f,
normalX, normalY, normalZ,
-1.0f, 1.0f, -1.0f,
normalX, normalY, normalZ,
};
public static final float[] planeVertices = {
-1.0f, 0.0f, 1.0f,
0, 0, 0,
1.0f, 0.0f, 1.0f,
0, 0, 0,
1.0f, 0.0f, -1.0f,
0, 0, 0,
-1.0f, 0.0f, -1.0f,
0, 0, 0,
};
public final String name = getClass().getSimpleName();
public Pbuffer pbuffer;
public List<Primitive> prims = Lists.newArrayList();
public int vbo, planeVbo, texture, shadowTexture;
public FloatBuffer lightColor;
public FloatBuffer lightPosition;
private int supersampling;
private boolean initialized = false;
private static final BufferedImage shadow;
static {
BufferedImage img = null;
try {
img = ImageIO.read(ClassLoader.getSystemResource("shadow.png"));
} catch (IOException e) {
e.printStackTrace();
}
shadow = img;
}
protected void addPrimitive(Primitive prim) {
prims.add(prim);
}
public void setSkin(BufferedImage img) throws LWJGLException {
pbuffer.makeCurrent();
upload(img, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
checkGLError();
}
public void upload(BufferedImage img, int tex) {
int width = img.getWidth();
int height = img.getHeight();
if (Visage.debug) Visage.log.finer("Uploading "+width+"x"+height+" ("+(width*height)+" pixel) image");
int[] argb = new int[width*height];
img.getRGB(0, 0, width, height, argb, 0, width);
IntBuffer buf = BufferUtils.createIntBuffer(width*height);
buf.put(argb);
buf.flip();
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buf);
checkGLError();
}
protected void preRender(int width, int height) throws LWJGLException {}
protected void postRender(int width, int height) throws LWJGLException {}
public void render(int width, int height) throws LWJGLException {
initGL(width, height);
preRender(width, height);
for (Primitive prim : prims) {
prim.render(this);
}
postRender(width, height);
checkGLError();
}
public void destroy() {
if (pbuffer != null) {
pbuffer.destroy();
pbuffer = null;
}
vbo = planeVbo = texture = shadowTexture = 0;
prims.clear();
initialized = false;
}
public void init(int supersampling) throws LWJGLException {
this.supersampling = supersampling;
if (Visage.debug) Visage.log.finer("["+name+"] Initializing Pbuffer (assuming "+supersampling+"x supersampling)");
if (pbuffer != null) {
destroy();
}
int width = 512;
int height = 512;
if (this instanceof FullRenderer) {
height = 832;
}
pbuffer = new Pbuffer(width*supersampling, height*supersampling, new PixelFormat(8, 8, 0), null, null, new ContextAttribs(1, 2));
if (pbuffer.isBufferLost())
throw new LWJGLException("Could not create Pbuffer");
pbuffer.makeCurrent();
if (Visage.debug) Visage.log.finer("["+name+"] Setting up VBOs");
IntBuffer ids = BufferUtils.createIntBuffer(2);
glGenBuffersARB(ids);
vbo = ids.get();
planeVbo = ids.get();
checkGLError();
IntBuffer textures = BufferUtils.createIntBuffer(2);
glGenTextures(textures);
texture = textures.get();
shadowTexture = textures.get();
checkGLError();
upload(shadow, shadowTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertices.length);
vertexBuffer.put(vertices);
vertexBuffer.flip();
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexBuffer, GL_STATIC_DRAW_ARB);
checkGLError();
FloatBuffer planeVertexBuffer = BufferUtils.createFloatBuffer(planeVertices.length);
planeVertexBuffer.put(planeVertices);
planeVertexBuffer.flip();
glBindBufferARB(GL_ARRAY_BUFFER_ARB, planeVbo);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, planeVertexBuffer, GL_STATIC_DRAW_ARB);
checkGLError();
glClearColor(0, 0, 0, 0);
glClearDepth(1.0);
checkGLError();
glShadeModel(GL_SMOOTH);
glCullFace(GL_FRONT);
glFrontFace(GL_CCW);
checkGLError();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
checkGLError();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
checkGLError();
lightColor = BufferUtils.createFloatBuffer(4);
lightColor.put(3f);
lightColor.put(3f);
lightColor.put(3f);
lightColor.put(1.0f);
lightColor.flip();
glLight(GL_LIGHT0, GL_AMBIENT, lightColor);
lightPosition = BufferUtils.createFloatBuffer(4);
lightPosition.put(-4f);
lightPosition.put(-2f);
lightPosition.put(1f);
lightPosition.put(1000f);
lightPosition.flip();
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
checkGLError();
if (Visage.debug) Visage.log.finer("["+name+"] Initializing primitives");
initPrimitives();
initialized = true;
}
public boolean isInitialized() {
return initialized;
}
protected abstract void initPrimitives();
protected void initGL(float width, float height) throws LWJGLException {
if (pbuffer.isBufferLost()) {
Visage.log.warning("We appear to have lost the Pbuffer. Checking under the couch cushions...");
Visage.log.info("Nope. Can't find it. Creating a new Pbuffer...");
destroy();
init(supersampling);
}
pbuffer.makeCurrent();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, (int)width, (int)height);
gluPerspective(
45.0f,
width / height,
0.1f,
100.0f);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
}
public void finish() throws LWJGLException {
pbuffer.releaseContext();
}
public BufferedImage readPixels(int width, int height) throws InterruptedException {
glReadBuffer(GL_FRONT);
ByteBuffer buf = BufferUtils.createByteBuffer(width * height * 4);
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, buf);
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int[] pixels = new int[width*height];
buf.asIntBuffer().get(pixels);
img.setRGB(0, 0, width, height, pixels, 0, width);
if (Visage.trace) Visage.log.finest("Read pixels");
return Images.toBuffered(img.getScaledInstance(width/supersampling, height/supersampling, Image.SCALE_AREA_AVERAGING));
}
}