package com.javamarcher.renderer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import com.javamarcher.configuration.GlobalConfig;
import com.javamarcher.gamestate.Notifiable;
import com.javamarcher.geometry.Quad;
import com.javamarcher.geometry.VertexData;
import com.javamarcher.input.ScrollInput;
import com.javamarcher.shader.ShaderCreator;
import com.javamarcher.systems.CameraSystem;
import com.javamarcher.util.TextureUnit;
import com.javamarcher.window.MainWindow;
public class Renderer implements Notifiable {
private final Quad quad = new Quad();
private int vaoId;
// VBO for positions + colors + uv's
private int interleavedVboId;
private int vboVertId;
private ShaderCreator shader;
private CameraSystem cameraSystem;
// Shader uniform id's
private int projectionMatrixLocation;
private int viewMatrixLocation;
private int modelMatrixLocation;
private int frustumMatrixLocation;
private int inverseViewMatrixLocation;
private int cameraPositionLocation;
private int deltaTimeLocation;
private int globalTimeLocation;
private int sunHeightLocation;
private int maxStepsLocation;
private int maxDistanceLocation;
private int epsilonLocation;
private int sunDirectionLocation;
private int fogColorLocation;
private int fogStrengthLocation;
private int shadowStrengthLocation;
private int shadowSoftnessLocation;
private int shadowIterationLocation;
private int highQualityModeLocation;
private int performanceCountLocation;
private int cloudIterationsLocation;
private int aoEnabledLocation;
private FloatBuffer matrix44Buffer = BufferUtils.createFloatBuffer(4 * 4);
private FloatBuffer vector3Buffer = BufferUtils.createFloatBuffer(3);
//Texture variable
private TextureUnit noiseTexture;
private TextureUnit noiseColorTexture;
private TextureUnit waterBumpTexture;
private TextureUnit cliffBumpTexture;
private ScrollInput scrollInput;
public void setCameraSystem(CameraSystem camSystem) {
this.cameraSystem = camSystem;
}
@Override
public void start() {
// Create a new Vertex Array Object and bind it
vaoId = GL30.glGenVertexArrays();
GL30.glBindVertexArray(vaoId);
GLFW.glfwSetScrollCallback(MainWindow.WINDOW, scrollInput = new ScrollInput());
initializeInterleavedData();
// Deselect VAO -> Bind to 0
GL30.glBindVertexArray(0);
// VBO for indices
initializeIndexVBO();
// Shader setup
initializeShaderProgram();
noiseTexture = new TextureUnit("Textures/noise.png", GL13.GL_TEXTURE0, shader.getProgramId(), 0, "texNoise");
noiseColorTexture = new TextureUnit("Textures/ColorNoise.png", GL13.GL_TEXTURE1, shader.getProgramId(), 1, "texColorNoise");
waterBumpTexture = new TextureUnit("Textures/WaterBump.png", GL13.GL_TEXTURE2, shader.getProgramId(), 2, "texWaterBump");
cliffBumpTexture = new TextureUnit("Textures/CliffBump.png", GL13.GL_TEXTURE3, shader.getProgramId(), 3, "texCliffBump");
}
/**
* Creates an interleaved buffer with the position and color values.
* Binds VBO's and attribute pointers.
*/
private void initializeInterleavedData() {
// Generate an interleaved buffer which contains position and color data
final FloatBuffer interleavedBuffer = BufferUtils.createFloatBuffer(quad.getVertices().length * VertexData.ELEMENT_COUNT);
for(final VertexData v : quad.getVertices()) {
interleavedBuffer.put(v.getPosition());
interleavedBuffer.put(v.getColor());
interleavedBuffer.put(v.getUV()); // X, Y, Z, W | R, G, B, A | U, V
}
interleavedBuffer.flip();
interleavedVboId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, interleavedVboId);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, interleavedBuffer, GL15.GL_STATIC_DRAW);
// Set attribute pointer with stride and offset
GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, VertexData.VERTEX_BYTE_SIZE, 0);
GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, VertexData.VERTEX_BYTE_SIZE, 4 * VertexData.ELEMENT_BYTE_SIZE);
GL20.glVertexAttribPointer(2, 2, GL11.GL_FLOAT, false, VertexData.VERTEX_BYTE_SIZE, 4 * VertexData.ELEMENT_BYTE_SIZE);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
private void initializeShaderProgram() {
shader = new ShaderCreator(GlobalConfig.MAIN_RAYMARCHER_VERT, GlobalConfig.MAIN_RAYMARCHER_FRAG);
shader.createShader();
// Position information will be attribute 0
GL20.glBindAttribLocation(shader.getProgramId(), 0, "iPosition");
// Color information will be attribute 1
GL20.glBindAttribLocation(shader.getProgramId(), 1, "iColor");
// UV -> attribute 2
GL20.glBindAttribLocation(shader.getProgramId(), 2, "iUv");
// Link and validate shader program
GL20.glLinkProgram(shader.getProgramId());
GL20.glValidateProgram(shader.getProgramId());
// Uniform locations
projectionMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "projectionMatrix");
viewMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "viewMatrix");
modelMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "modelMatrix");
frustumMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "frustumMatrix");
inverseViewMatrixLocation = GL20.glGetUniformLocation(shader.getProgramId(), "inverseViewMatrix");
cameraPositionLocation = GL20.glGetUniformLocation(shader.getProgramId(), "cameraWorldPosition");
deltaTimeLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uDeltaTime");
globalTimeLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uGlobalTime");
sunHeightLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uSunHeight");
maxStepsLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uMaxSteps");
maxDistanceLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uMaxDistance");
epsilonLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uEpsilon");
sunDirectionLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uSunDirection");
fogColorLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uFogColor");
fogStrengthLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uFogStrength");
shadowStrengthLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uShadowStrength");
shadowSoftnessLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uShadowSoftness");
shadowIterationLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uShadowIterations");
highQualityModeLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uHighQualityMode");
performanceCountLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uVisualizePerformance");
cloudIterationsLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uCloudIterations");
aoEnabledLocation = GL20.glGetUniformLocation(shader.getProgramId(), "uAOEnabled");
}
private void initializeIndexVBO() {
final ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(quad.getIndices().length);
indicesBuffer.put(quad.getIndices());
indicesBuffer.flip();
// Create new VBO for the indices
vboVertId = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboVertId);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW);
// Deselect VBO -> Bind to 0
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
}
@Override
public void update(double deltaTime) {
// Bind Shader
GL20.glUseProgram(shader.getProgramId());
// Load uniform data into shader
prepareShaderUniforms();
// Bind the VAO which has the vertex information
GL30.glBindVertexArray(vaoId);
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(2);
// Bind to the index VBO that has all the information about the order of the vertices
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboVertId);
// Draw Quad
GL11.glDrawElements(GL11.GL_TRIANGLES, quad.getIndices().length, GL11.GL_UNSIGNED_BYTE, 0);
// Deselect
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
// Unbind Shader
GL20.glUseProgram(0);
}
private void prepareShaderUniforms() {
cameraSystem.getDefaultCamera().getProjectionMatrix().get(matrix44Buffer);
matrix44Buffer.flip(); matrix44Buffer.clear();
GL20.glUniformMatrix4fv(projectionMatrixLocation, false, matrix44Buffer);
cameraSystem.getDefaultCamera().getViewMatrix().get(matrix44Buffer);
matrix44Buffer.flip(); matrix44Buffer.clear();
GL20.glUniformMatrix4fv(viewMatrixLocation, false, matrix44Buffer);
quad.getTransform().getModelMatrix().get(matrix44Buffer);
matrix44Buffer.flip(); matrix44Buffer.clear();
GL20.glUniformMatrix4fv(modelMatrixLocation, false, matrix44Buffer);
// Necessary for Raymarching
cameraSystem.getDefaultCamera().GetFrustumCorners().get(matrix44Buffer);
matrix44Buffer.flip(); matrix44Buffer.clear();
GL20.glUniformMatrix4fv(frustumMatrixLocation, false, matrix44Buffer);
Matrix4f inverseView = new Matrix4f(cameraSystem.getDefaultCamera().getViewMatrix());
inverseView.invert();
inverseView.get(matrix44Buffer);
matrix44Buffer.flip(); matrix44Buffer.clear();
GL20.glUniformMatrix4fv(inverseViewMatrixLocation, false, matrix44Buffer);
vector3Buffer.clear();
cameraSystem.getDefaultCamera().getTransform().position.get(vector3Buffer);
vector3Buffer.flip(); vector3Buffer.clear();
GL20.glUniform3fv(cameraPositionLocation, vector3Buffer);
// Delta time & Global time
GL20.glUniform1f(deltaTimeLocation, (float) MainWindow.getDeltaTime());
GL20.glUniform1f(globalTimeLocation, (float) MainWindow.getGlobalTime());
// Sun Height
GL20.glUniform1f(sunHeightLocation, scrollInput.getyOffset());
// Raymarcher parameters
GL20.glUniform1i(maxStepsLocation, GlobalConfig.MAX_STEPS);
GL20.glUniform1f(maxDistanceLocation, (float) GlobalConfig.MAX_DISTANCE);
GL20.glUniform1f(epsilonLocation, (float) GlobalConfig.EPSILON);
// Sun parameters
final Vector3f normalizedSunDirection = new Vector3f(GlobalConfig.SUN_DIRECTION);
normalizedSunDirection.normalize();
GL20.glUniform3f(sunDirectionLocation, normalizedSunDirection.x, normalizedSunDirection.y, normalizedSunDirection.z);
//Cloud iterations
GL20.glUniform1i(cloudIterationsLocation, GlobalConfig.CLOUD_ITERATIONS);
// Fog
GL20.glUniform3f(fogColorLocation, GlobalConfig.FOG_COLOR.x, GlobalConfig.FOG_COLOR.y, GlobalConfig.FOG_COLOR.z);
GL20.glUniform1f(fogStrengthLocation, (float) GlobalConfig.FOG_STRENGTH);
// Shadow
GL20.glUniform1f(shadowStrengthLocation, (float) GlobalConfig.SHADOW_STRENGTH);
GL20.glUniform1f(shadowSoftnessLocation, (float) GlobalConfig.SHADOW_SOFTNESS);
GL20.glUniform1i(shadowIterationLocation, GlobalConfig.SHADOW_ITERATIONS);
// Quality Mode
GL20.glUniform1i(highQualityModeLocation, GlobalConfig.HIGH_QUALITY_MODE ? 1 : 0);
// Performance Visualization
GL20.glUniform1f(performanceCountLocation, GlobalConfig.PERFORMANCE_VISUALIZATION ? 1 : 0);
// Ambient Occlusion
GL20.glUniform1f(aoEnabledLocation, GlobalConfig.AO_ENABLED ? 1 : 0);
}
@Override
public void destroy() {
// Delete the texture
GL11.glDeleteTextures(noiseTexture.getTextureId());
GL11.glDeleteTextures(noiseColorTexture.getTextureId());
GL11.glDeleteTextures(waterBumpTexture.getTextureId());
GL11.glDeleteTextures(cliffBumpTexture.getTextureId());
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vboVertId);
// Disable the VBO index from the VAO attributes list
GL20.glDisableVertexAttribArray(0);
// Delete the VBO
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glDeleteBuffers(vboVertId);
// Delete the VAO
GL30.glBindVertexArray(0);
GL30.glDeleteVertexArrays(vaoId);
// Shader cleanup
shader.cleanup();
}
}