package com.bitwaffle.spaceguts.graphics.render;
import java.util.Iterator;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Quaternion;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;
import com.bitwaffle.spaceguts.entities.DynamicEntity;
import com.bitwaffle.spaceguts.entities.Entities;
import com.bitwaffle.spaceguts.entities.Entity;
import com.bitwaffle.spaceguts.entities.Light;
import com.bitwaffle.spaceguts.graphics.glsl.GLSLProgram;
import com.bitwaffle.spaceguts.graphics.glsl.GLSLShader;
import com.bitwaffle.spaceguts.graphics.glsl.ShaderTypes;
import com.bitwaffle.spaceguts.graphics.model.Material;
import com.bitwaffle.spaceguts.physics.Physics;
import com.bitwaffle.spaceguts.util.DisplayHelper;
import com.bitwaffle.spaceguts.util.MatrixHelper;
import com.bitwaffle.spaceguts.util.QuaternionHelper;
import com.bitwaffle.spaceout.resources.Paths;
/**
* Handles all 3D rendering
* @author TranquilMarmot
* @see Graphics
*/
public class Render3D {
private static final String VERTEX_SHADER = "main.vert", FRAGMENT_SHADER = "main.frag";
/** ModelView and Projection matrices */
public static Matrix4f projection, modelview;
private static Matrix4f oldModelview = new Matrix4f();
/** Draw distance and field-of-view to use for rendering */
public static float drawDistance = 3000000.0f, fov = 45.0f;
/** old aspect ratio is saved to know when to change the projection matrix */
private static float oldAspect = (float) DisplayHelper.windowWidth
/ (float) DisplayHelper.windowHeight;
/** the shader program to use */
public static GLSLProgram program;
/** default diffuse color */
private static final Vector3f DEFAULT_KD = new Vector3f(0.5f, 0.5f, 0.5f);
/** default ambient color */
private static final Vector3f DEFAULT_KA = new Vector3f(0.5f, 0.5f, 0.5f);
/** default specular color */
private static final Vector3f DEFAULT_KS = new Vector3f(0.8f, 0.8f, 0.8f);
/** default shiny factor */
private static final float DEFAULT_SHINY = 50.0f;
/**
* Renders the 3D scene
*/
public static void render3DScene(){
setUp3DRender();
transformToCamera();
if(Entities.skybox != null)
drawSkybox();
setUpLights();
drawLights();
drawDynamicEntities();
drawPassiveEntities();
drawPlayer();
/*
* Modelview matrix
* Rotate, translate, transpose
* Why so confusing
*/
}
/**
* Sets up for doing a 3D render
*/
private static void setUp3DRender(){
// make sure we're using our shader
program.use();
// set the dafault material, just in case
useDefaultMaterial();
// calculate the current aspect ratio and change the projection matrix if it's changed
float aspect = (float) DisplayHelper.windowWidth
/ (float) DisplayHelper.windowHeight;
if(aspect != oldAspect){
projection = MatrixHelper.perspective(fov, aspect, 1.0f, drawDistance);
program.setUniform("ProjectionMatrix", projection);
oldAspect = aspect;
}
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL11.GL_DEPTH_TEST);
modelview.setIdentity();
}
/**
* Sets the current material being used for rendering
* @param mat Material to use
*/
public static void setCurrentMaterial(Material mat){
program.setUniform("Material.Kd" , mat.getKd());
program.setUniform("Material.Ka", mat.getKa());
program.setUniform("Material.Ks", mat.getKs());
program.setUniform("Material.Shininess", mat.getShininess());
}
/**
* Set the current material to the default material
*/
public static void useDefaultMaterial(){
program.setUniform("Material.Kd" , DEFAULT_KD);
program.setUniform("Material.Ka", DEFAULT_KA);
program.setUniform("Material.Ks", DEFAULT_KS);
program.setUniform("Material.Shininess", DEFAULT_SHINY);
}
/**
* Transforms the ModelView matrix to represent the camera's location and rotation
*/
private static void transformToCamera(){
// translate to the camera's location
modelview.translate(new Vector3f(Entities.camera.xOffset, Entities.camera.yOffset, -Entities.camera.zoom));
// reverse the camera's quaternion (we want to look OUT from the camera)
Quaternion reverse = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
Quaternion.negate(Entities.camera.rotation, reverse);
Matrix4f.mul(modelview, QuaternionHelper.toMatrix(reverse), modelview);
}
/**
* Sets up lights for rendering
*/
private static void setUpLights(){
// FIXME only one light supported right now!
if(Entities.lights.size() > 1)
System.out.println("More than one light! Multiple lighting not yet implemented.");
Light l = Entities.lights.iterator().next();
float transX = Entities.camera.location.x - l.location.x;
float transY = Entities.camera.location.y - l.location.y;
float transZ = Entities.camera.location.z - l.location.z;
// crazy quaternion and vector math to get the light into world coordinates
Quaternion reverse = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
Quaternion.negate(Entities.camera.rotation, reverse);
Vector3f rotated = QuaternionHelper.rotateVectorByQuaternion(new Vector3f(transX, transY, transZ), reverse);
// set uniforms
program.setUniform("Light.LightPosition", new Vector4f(rotated.x, rotated.y, rotated.z, 0.0f));
program.setUniform("Light.LightIntensity", l.intensity);
program.setUniform("Light.LightEnabled", true);
}
/**
* Draws the skybox
*/
private static void drawSkybox(){
program.setUniform("Light.LightEnabled", false);
float transX = Entities.camera.location.x - Entities.skybox.location.x;
float transY = Entities.camera.location.y - Entities.skybox.location.y;
float transZ = Entities.camera.location.z - Entities.skybox.location.z;
oldModelview.load(modelview);{
modelview.translate(new Vector3f(transX, transY, transZ));
Matrix4f.mul(modelview, QuaternionHelper.toMatrix(Entities.skybox.rotation), modelview);
program.setUniform("ModelViewMatrix", modelview);
Entities.skybox.draw();
}modelview.load(oldModelview);
program.setUniform("Light.LightEnabled", true);
}
/**
* Draws any lights
*/
private static void drawLights(){
program.setUniform("Light.LightEnabled", false);
Iterator<Light> lightIterator = Entities.lights.iterator();
while(lightIterator.hasNext()){
Light light = lightIterator.next();
float transX = Entities.camera.location.x - light.location.x;
float transY = Entities.camera.location.y - light.location.y;
float transZ = Entities.camera.location.z - light.location.z;
oldModelview.load(modelview);{
modelview.translate(new Vector3f(transX, transY, transZ));
program.setUniform("ModelViewMatrix", modelview);
light.draw();
}modelview.load(oldModelview);
}
program.setUniform("Light.LightEnabled", true);
}
/**
* Draws any passive entities
*/
private static void drawPassiveEntities(){
Iterator<Entity> entityIterator = Entities.passiveEntities.iterator();
while(entityIterator.hasNext()){
// FIXME might be a better spot to put this
useDefaultMaterial();
Entity ent = entityIterator.next();
float transX = Entities.camera.location.x - ent.location.x;
float transY = Entities.camera.location.y - ent.location.y;
float transZ = Entities.camera.location.z - ent.location.z;
oldModelview.load(modelview);{
modelview.translate(new Vector3f(transX, transY, transZ));
Matrix4f.mul(modelview, QuaternionHelper.toMatrix(ent.rotation), modelview);
program.setUniform("ModelViewMatrix", modelview);
ent.draw();
}modelview.load(oldModelview);
}
}
/**
* Draws any dynamic entities
*/
private static void drawDynamicEntities(){
Iterator<DynamicEntity> entityIterator = Entities.dynamicEntities.iterator();
while(entityIterator.hasNext()){
DynamicEntity ent = entityIterator.next();
float transX = Entities.camera.location.x - ent.location.x;
float transY = Entities.camera.location.y - ent.location.y;
float transZ = Entities.camera.location.z - ent.location.z;
oldModelview.load(modelview);{
modelview.translate(new Vector3f(transX, transY, transZ));
if(Physics.drawDebug){
ent.drawPhysicsDebug();
}
Matrix4f.mul(modelview, QuaternionHelper.toMatrix(ent.rotation), modelview);
program.setUniform("ModelViewMatrix", modelview);
ent.draw();
}modelview.load(oldModelview);
}
}
/**
* Draws the player
*/
private static void drawPlayer(){
float transX = Entities.camera.location.x - Entities.player.location.x;
float transY = Entities.camera.location.y - Entities.player.location.y;
float transZ = Entities.camera.location.z - Entities.player.location.z;
oldModelview.load(modelview);{
modelview.translate(new Vector3f(transX, transY, transZ));
Matrix4f.mul(modelview, QuaternionHelper.toMatrix(Entities.player.rotation), modelview);
program.setUniform("ModelViewMatrix", modelview);
Entities.player.draw();
}modelview.load(oldModelview);
}
/**
* This must be called before any 3D rendering is done!
* Loads in the shaders and initializes matrices
*/
public static void init(){
modelview = new Matrix4f();
// create vertex shader
GLSLShader vertShader = new GLSLShader(ShaderTypes.VERTEX);
String vertFile = Paths.SHADER_PATH.path() + VERTEX_SHADER;
if (!vertShader.compileShaderFromFile(vertFile))
System.out.println(vertShader.log());
// create fragment shader
GLSLShader fragShader = new GLSLShader(ShaderTypes.FRAGMENT);
String fragFile = Paths.SHADER_PATH.path() + FRAGMENT_SHADER;
if (!fragShader.compileShaderFromFile(fragFile)) {
System.out.println(fragShader.log());
}
// create and use program
program = new GLSLProgram();
program.addShader(fragShader);
program.addShader(vertShader);
program.link();
program.use();
// calculate the current aspect ratio
float oldAspect = (float) DisplayHelper.windowWidth
/ (float) DisplayHelper.windowHeight;
projection = MatrixHelper.perspective(fov, oldAspect, 1.0f, drawDistance);
program.setUniform("ProjectionMatrix", projection);
}
}