LiteCraft/src/main/java/io/github/hydos/ginger/engine/render/renderers/ParticleRenderer.java

163 lines
5.7 KiB
Java

package io.github.hydos.ginger.engine.render.renderers;
import java.nio.FloatBuffer;
import java.util.List;
import java.util.Map;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import io.github.hydos.ginger.engine.cameras.ThirdPersonCamera;
import io.github.hydos.ginger.engine.math.Maths;
import io.github.hydos.ginger.engine.particle.Particle;
import io.github.hydos.ginger.engine.particle.ParticleTexture;
import io.github.hydos.ginger.engine.render.models.RawModel;
import io.github.hydos.ginger.engine.render.shaders.ParticleShader;
import io.github.hydos.ginger.engine.utils.Loader;
public class ParticleRenderer
{
private static final float[] VERTICES =
{
-0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f, -0.5f
};
private static final int MAX_INSTANCES = 10000;
private static final int INSTANCE_DATA_LENGTH = 21;
private static final FloatBuffer buffer = BufferUtils.createFloatBuffer(MAX_INSTANCES * INSTANCE_DATA_LENGTH);
private RawModel quad;
private ParticleShader shader;
private int vbo;
private int pointer;
public ParticleRenderer(Matrix4f projectionMatrix)
{
quad = Loader.loadToVAO(VERTICES, 2);
this.vbo = Loader.createEmptyVbo(INSTANCE_DATA_LENGTH * MAX_INSTANCES);
Loader.addInstancedAttribute(quad.getVaoID(), vbo, 1, 4, INSTANCE_DATA_LENGTH, 0);
Loader.addInstancedAttribute(quad.getVaoID(), vbo, 2, 4, INSTANCE_DATA_LENGTH, 4);
Loader.addInstancedAttribute(quad.getVaoID(), vbo, 3, 4, INSTANCE_DATA_LENGTH, 8);
Loader.addInstancedAttribute(quad.getVaoID(), vbo, 4, 4, INSTANCE_DATA_LENGTH, 12);
Loader.addInstancedAttribute(quad.getVaoID(), vbo, 5, 4, INSTANCE_DATA_LENGTH, 16);
Loader.addInstancedAttribute(quad.getVaoID(), vbo, 6, 1, INSTANCE_DATA_LENGTH, 20);
shader = new ParticleShader();
shader.start();
shader.loadProjectionMatrix(projectionMatrix);
shader.stop();
}
public void render(Map<ParticleTexture, List<Particle>> particles, ThirdPersonCamera camera)
{
Matrix4f viewMatrix = Maths.createViewMatrix(camera);
prepare();
for (ParticleTexture texture : particles.keySet())
{
bindTexture(texture);
List<Particle> particleList = particles.get(texture);
pointer = 0;
float[] vboData = new float[particleList.size() * INSTANCE_DATA_LENGTH];
for (Particle particle : particleList)
{
updateModelViewMatrix(particle.getPosition(), particle.getRotation(), particle.getScale().x, viewMatrix, vboData);
updateTexCoordInfo(particle, vboData);
}
Loader.updateVbo(vbo, vboData, buffer);
GL31.glDrawArraysInstanced(GL11.GL_TRIANGLE_STRIP, 0, quad.getVertexCount(), particleList.size());
}
finishRendering();
}
public void cleanUp()
{ shader.cleanUp(); }
private void updateTexCoordInfo(Particle particle, float[] data)
{
data[pointer++] = particle.getTexOffset1().x;
data[pointer++] = particle.getTexOffset1().y;
data[pointer++] = particle.getTexOffset2().x;
data[pointer++] = particle.getTexOffset2().y;
data[pointer++] = particle.getBlend();
}
private void bindTexture(ParticleTexture texture)
{
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture.getTextureID());
shader.loadNumberOfRows(texture.getNumberOfRows());
}
private void updateModelViewMatrix(Vector3f position, float rotation, float scale, Matrix4f viewMatrix, float[] vboData)
{
Matrix4f modelMatrix = new Matrix4f();
modelMatrix.translate(position, modelMatrix);
modelMatrix.m00(viewMatrix.m00());
modelMatrix.m01(viewMatrix.m10());
modelMatrix.m02(viewMatrix.m20());
modelMatrix.m10(viewMatrix.m01());
modelMatrix.m11(viewMatrix.m11());
modelMatrix.m12(viewMatrix.m21());
modelMatrix.m20(viewMatrix.m02());
modelMatrix.m21(viewMatrix.m12());
modelMatrix.m22(viewMatrix.m22());
modelMatrix.rotate((float) Math.toRadians(rotation), new Vector3f(0, 0, 1), modelMatrix);
modelMatrix.scale(new Vector3f(scale, scale, scale), modelMatrix);
Matrix4f modelViewMatrix = new Matrix4f();
modelViewMatrix.mul(viewMatrix, modelMatrix);
storeMatrixData(modelViewMatrix, vboData);
}
private void storeMatrixData(Matrix4f matrix, float[] vboData)
{
vboData[pointer++] = matrix.m00();
vboData[pointer++] = matrix.m01();
vboData[pointer++] = matrix.m02();
vboData[pointer++] = matrix.m03();
vboData[pointer++] = matrix.m10();
vboData[pointer++] = matrix.m11();
vboData[pointer++] = matrix.m12();
vboData[pointer++] = matrix.m13();
vboData[pointer++] = matrix.m20();
vboData[pointer++] = matrix.m21();
vboData[pointer++] = matrix.m22();
vboData[pointer++] = matrix.m23();
vboData[pointer++] = matrix.m30();
vboData[pointer++] = matrix.m31();
vboData[pointer++] = matrix.m32();
vboData[pointer++] = matrix.m33();
}
private void prepare()
{
shader.start();
GL30.glBindVertexArray(quad.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
GL20.glEnableVertexAttribArray(2);
GL20.glEnableVertexAttribArray(3);
GL20.glEnableVertexAttribArray(4);
GL20.glEnableVertexAttribArray(5);
GL20.glEnableVertexAttribArray(6);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);//TODO: add all particle effects into 1 texture to fix overlapping
}
private void finishRendering()
{
shader.stop();
GL30.glBindVertexArray(0);
GL11.glDisable(GL11.GL_BLEND);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(2);
GL20.glDisableVertexAttribArray(3);
GL20.glDisableVertexAttribArray(4);
GL20.glDisableVertexAttribArray(5);
GL20.glDisableVertexAttribArray(6);
}
}