Renderer/src/main/java/electrosphere/renderer/model/Mesh.java
austin 467c42bfe8
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
opengl fixes
2024-09-01 20:10:33 -04:00

643 lines
23 KiB
Java

package electrosphere.renderer.model;
import electrosphere.engine.Globals;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.actor.ActorTextureMask;
import electrosphere.renderer.actor.instance.InstanceData;
import electrosphere.renderer.buffer.HomogenousInstancedArray;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.light.LightManager;
import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.renderer.texture.Texture;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Sphered;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.GL_INT;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL13.GL_TEXTURE3;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUniform3fv;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import org.lwjgl.opengl.GL45;
import org.lwjgl.system.MemoryStack;
/**
* A mesh, a collection of buffer data on the GPU
*/
public class Mesh {
//the name of the mesh
private String meshName;
//THIS IS NOT GUARANTEED TO BE THE PARENT MODEL THAT THIS WAS LOADED IN
//THIS CAN BE POST-LOAD SET IN MODEL VIA MODELMASK BEHAVIOR
private Model parent;
//various buffers that may or may not be allocated
private int vertexBuffer;
private int normalBuffer;
private int elementArrayBuffer;
private int elementCount; //pulls double duty as both the element array count as well as the direct array count, based on whichever is used
private int vertexArrayObject;
private int boneWeightBuffer;
private int boneIndexBuffer;
private int textureCoordBuffer;
//uses element instances
private boolean useElementArray = true;
//THIS IS NOT GUARANTEED TO BE THE PARENT MODEL THAT THIS WAS LOADED IN
//THIS CAN BE POST-LOAD SET IN MODEL VIA MODELMASK BEHAVIOR
private List<Bone> bones = new ArrayList<Bone>();
private ArrayList<String> boneIdList = new ArrayList<String>();
//the texture mask that may or may not be masking the mesh
private ActorTextureMask textureMask;
//the shaders currently associated with the mesh
private ShaderProgram shader;
private ShaderProgram oitShader;
//the uniforms to be sent to the gpu
private HashMap<String,Object> uniforms = new HashMap<String,Object>();
//the material currently associated with the mesh
private Material material;
//the bounding sphere for this mesh
private Sphered boundingSphere;
/**
* Creates a mesh (does not initialize any data)
* @param name The name of the mesh
*/
public Mesh(String name){
this.meshName = name;
this.boundingSphere = new Sphered();
}
/**
* Generates the VAO for this mesh
*/
public void generateVAO(){
vertexArrayObject = glGenVertexArrays();
glBindVertexArray(vertexArrayObject);
}
/**
* Buffers vertex data to the gpu under this mesh container
* @param verticies the vertex buffer
* @param vertexDimension the dimensionality of the data (2d vectors, 3d vectors, 4d vectors, etc)
*/
public void bufferVertices(FloatBuffer verticies, int vertexDimension){
if(!Globals.HEADLESS){
vertexBuffer = bufferCustomFloatAttribArray(verticies,vertexDimension,0);
}
}
/**
* Buffers normals to the gpu under this mesh container
* @param normals the normal data
* @param normalDimension the dimensionality of the data (2d vector, 3d vector, 4d vector)
*/
public void bufferNormals(FloatBuffer normals, int normalDimension){
if(!Globals.HEADLESS){
normalBuffer = bufferCustomFloatAttribArray(normals,normalDimension,1);
}
}
/**
* Buffers faces to the GPU
* @param faces The face data
* @param elementCount The number of faces
*/
public void bufferFaces(IntBuffer faces, int elementCount){
if(!Globals.HEADLESS){
elementArrayBuffer = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementArrayBuffer);
GL15.glBufferData(GL_ELEMENT_ARRAY_BUFFER, faces, GL_STATIC_DRAW);
this.elementCount = elementCount;
}
}
/**
* Sets the number of elements in the directly referenced arrays underneath this mesh
* @param directArraySize The number of elements (ie the number of vertices, or normals, etc)
*/
public void setDirectArraySize(int directArraySize){
this.elementCount = directArraySize;
}
/**
* Buffers texture coordinates to the gpu
* @param coords the texture coordinates data
* @param textureDimension The dimensionality of the texture coordinate data (3d vec, 4d vec, etc)
*/
public void bufferTextureCoords(FloatBuffer coords, int textureDimension){
if(!Globals.HEADLESS){
textureCoordBuffer = bufferCustomFloatAttribArray(coords, textureDimension, 4);
}
}
/**
* Buffers bone indices to the GPU
* @param buffer The buffer containing the bone indices
*/
public void bufferBoneIndices(FloatBuffer buffer){
boneIndexBuffer = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, boneIndexBuffer);
GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
glVertexAttribPointer(3, 4, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(3);
}
/**
* Buffers bone weights to the gpu
* @param buffer The buffer containing the bone weights
*/
public void bufferBoneWeights(FloatBuffer buffer){
boneWeightBuffer = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, boneWeightBuffer);
GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
glVertexAttribPointer(2, 4, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(2);
}
/**
* Sends a float buffer to the gpu
* @param buffer The buffer
* @param bufferDimension The dimensionality of the buffer (2d vector, 3d vector, 4d vector)
* @param attribIndex The attribute index of the buffer (ie what number will it show up as in the shader)
* @return The pointer to the opengl buffer created
*/
public int bufferCustomFloatAttribArray(FloatBuffer buffer, int bufferDimension, int attribIndex){
int bufferPointer = 0;
if(!Globals.HEADLESS){
bufferPointer = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, bufferPointer);
GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
glVertexAttribPointer(attribIndex, bufferDimension, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(attribIndex);
}
return bufferPointer;
}
/**
* Sends an int buffer to the gpu
* @param buffer The buffer
* @param bufferDimension The dimensionality of the buffer (2d vector, 3d vector, 4d vector)
* @param attribIndex The attribute index of the buffer (ie what number will it show up as in the shader)
* @return The pointer to the opengl buffer created
*/
public int bufferCustomIntAttribArray(IntBuffer buffer, int bufferDimension, int attribIndex){
int bufferPointer = 0;
if(!Globals.HEADLESS){
bufferPointer = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, bufferPointer);
GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
glVertexAttribPointer(attribIndex, bufferDimension, GL_INT, false, 0, 0);
glEnableVertexAttribArray(attribIndex);
}
return bufferPointer;
}
/**
* Sends an unsigned int buffer to the gpu
* @param buffer The buffer
* @param bufferDimension The dimensionality of the buffer (2d vector, 3d vector, 4d vector)
* @param attribIndex The attribute index of the buffer (ie what number will it show up as in the shader)
* @return The pointer to the opengl buffer created
*/
public int bufferCustomUIntAttribArray(IntBuffer buffer, int bufferDimension, int attribIndex){
int bufferPointer = 0;
if(!Globals.HEADLESS){
bufferPointer = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, bufferPointer);
GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
glVertexAttribPointer(attribIndex, bufferDimension, GL_UNSIGNED_INT, false, 0, 0);
glEnableVertexAttribArray(attribIndex);
}
return bufferPointer;
}
/**
* Sets the texture mask for the mesh
* @param textureMask the texture mask
*/
public void setTextureMask(ActorTextureMask textureMask){
this.textureMask = textureMask;
}
/**
* Sets the material for the mesh
* @param material the material
*/
public void setMaterial(Material material){
this.material = material;
}
/**
* Sets the shader of this mesh
* @param shader The shader
*/
public void setShader(ShaderProgram shader){
this.shader = shader;
}
/**
* Gets the shader of this mesh
* @return The shader
*/
public ShaderProgram getShader(){
return shader;
}
/**
* Sets the order independent transparency shader
* @param shader The shader
*/
public void setOITShader(ShaderProgram shader){
this.oitShader = shader;
}
/**
* Sets a uniform on the mesh
* @param key the uniform key
* @param o the value to set the uniform to
*/
public void setUniform(String key, Object o){
uniforms.put(key, o);
}
void bufferAllUniforms(OpenGLState openGLState){
for(String key : uniforms.keySet()){
Object currentUniformRaw = uniforms.get(key);
if(currentUniformRaw instanceof Matrix4f){
Matrix4f currentUniform = (Matrix4f)currentUniformRaw;
glUniformMatrix4fv(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), key), false, currentUniform.get(new float[16]));
Globals.renderingEngine.checkError();
}
if(currentUniformRaw instanceof Vector3f){
Vector3f currentUniform = (Vector3f)currentUniformRaw;
glUniform3fv(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), key), currentUniform.get(BufferUtils.createFloatBuffer(3)));
Globals.renderingEngine.checkError();
}
if(currentUniformRaw instanceof Integer){
int currentInform = (Integer)currentUniformRaw;
glUniform1i(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), key), currentInform);
Globals.renderingEngine.checkError();
}
}
}
/**
* Sends a buffer to the gpu
* @param uniformTypeMap The type of the buffer
* @param buffers The buffer
*/
void bufferInstanceData(
RenderPipelineState renderPipelineState,
Map<ShaderAttribute,Object> buffers,
Map<ShaderAttribute,HomogenousInstancedArray> uniformGlBufferMap
){
for(ShaderAttribute attribute : buffers.keySet()){
HomogenousInstancedArray buffer = uniformGlBufferMap.get(attribute);
buffer.updateBuffer(buffers.get(attribute), 0);
buffer.bind(renderPipelineState);
}
}
/**
* Draws the mesh
* @param renderPipelineState The state of the render pipeline
*/
public void complexDraw(RenderPipelineState renderPipelineState, OpenGLState openGLState){
//bind vao off the rip
glBindVertexArray(vertexArrayObject);
Globals.renderingEngine.checkError();
if(renderPipelineState.getUseMeshShader()){
ShaderProgram selectedProgram = null;
switch(renderPipelineState.getSelectedShader()){
case PRIMARY: {
selectedProgram = shader;
} break;
case OIT: {
selectedProgram = oitShader;
} break;
}
if(selectedProgram == null){
selectedProgram = shader;
}
openGLState.setActiveShader(renderPipelineState, selectedProgram);
}
if(renderPipelineState.getUseLight()){
//Until we switch to uniform buffer objects we will have to buffer lighting data here manually each time we draw
//side note: :(
if(Globals.renderingEngine.getLightManager() == null){
//don't buffer as the light manager hasn't initialized
} else {
LightManager lightManager = Globals.renderingEngine.getLightManager();
lightManager.bindBuffer(openGLState);
}
Globals.renderingEngine.checkError();
}
if(renderPipelineState.getUseMaterial() && textureMask == null){
Globals.renderingEngine.checkError();
if(material == null){
Globals.materialDefault.apply_material(openGLState,0,1);
openGLState.getActiveShader().setUniform(openGLState, "hasTransparency", 0);
} else {
material.apply_material(openGLState);
if(material.hasTransparency){
openGLState.getActiveShader().setUniform(openGLState, "hasTransparency", 1);
} else {
openGLState.getActiveShader().setUniform(openGLState, "hasTransparency", 0);
}
}
Globals.renderingEngine.checkError();
}
//
//The texture masking logic
if(textureMask != null){
//
//path that uses already-defined texture objects
if(textureMask.getTextures() != null){
int i = 0;
for(Texture texture : textureMask.getTextures()){
if(texture != null){
texture.bind(openGLState,5+i);
}
openGLState.getActiveShader().setUniform(openGLState, textureMask.getUniformNames().get(i), 5+i);
i++;
}
} else if(textureMask.getTexturePaths() != null){
//
//path that uses paths to textures in the asset manager
int i = 0;
for(String texturePath : textureMask.getTexturePaths()){
Texture texture = Globals.assetManager.fetchTexture(texturePath);
if(texture != null){
texture.bind(openGLState, i);
}
i++;
}
}
Globals.renderingEngine.checkError();
}
if(renderPipelineState.getUseShadowMap()){
openGLState.glActiveTexture(GL_TEXTURE3);
Globals.renderingEngine.checkError();
openGLState.glBindTexture(GL_TEXTURE_2D, RenderingEngine.lightBufferDepthTexture.getTexturePointer());
Globals.renderingEngine.checkError();
openGLState.getActiveShader().setUniform(openGLState, "shadowMap", 3);
}
if(renderPipelineState.getUseBones()){
//
//Handle bones
//
if(bones != null && !bones.isEmpty()){
openGLState.getActiveShader().setUniform(openGLState, "hasBones", 1);
openGLState.getActiveShader().setUniform(openGLState, "numBones", bones.size());
Iterator<String> boneIterator = boneIdList.iterator();
int incrementer = 0;
while (boneIterator.hasNext()){
String boneName = boneIterator.next();
Bone currentBone = parent.getBoneMap().get(boneName);
String currentUniform = "bones[" + incrementer + "]";
if(currentBone != null){
Matrix4d currentMat = currentBone.getFinalTransform();
openGLState.getActiveShader().setUniform(openGLState, currentUniform, currentMat);
} else {
openGLState.getActiveShader().setUniform(openGLState, currentUniform, new Matrix4f());
}
incrementer++;
}
} else {
openGLState.getActiveShader().setUniform(openGLState, "hasBones", 0);
}
} else {
openGLState.getActiveShader().setUniform(openGLState, "hasBones", 0);
}
if(renderPipelineState.getBufferStandardUniforms()){
//buffer model/view/proj matrices
try(MemoryStack stack = MemoryStack.stackPush()){
openGLState.getActiveShader().setUniform(openGLState, "model", parent.getModelMatrix());
openGLState.getActiveShader().setUniform(openGLState, "view", Globals.viewMatrix);
openGLState.getActiveShader().setUniform(openGLState, "projection", Globals.projectionMatrix);
openGLState.getActiveShader().setUniform(openGLState, "viewPos", CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3f worldPos = new Vector3f((float)parent.getWorldPos().x,(float)parent.getWorldPos().y,(float)parent.getWorldPos().z);
openGLState.getActiveShader().setUniform(openGLState, "modelWorldPos", worldPos);
openGLState.getActiveShader().setUniform(openGLState, "lightSpaceMatrix", Globals.lightDepthMatrix);
openGLState.getActiveShader().setUniform(openGLState, "frame", (int)Globals.timekeeper.getNumberOfRenderFramesElapsed());
openGLState.getActiveShader().setUniform(openGLState, "time", (float)Globals.timekeeper.getCurrentRendererTime());
}
Globals.renderingEngine.checkError();
}
if(renderPipelineState.getBufferNonStandardUniforms()){
bufferAllUniforms(openGLState);
Globals.renderingEngine.checkError();
}
if(renderPipelineState.getInstanced()){
if(renderPipelineState.getInstanceData()!=null){
InstanceData instanceData = renderPipelineState.getInstanceData();
Map<ShaderAttribute,Object> buffers = instanceData.getCpuBufferMap();
Map<ShaderAttribute,HomogenousInstancedArray> glBufferMap = instanceData.getGlBufferMap();
bufferInstanceData(renderPipelineState, buffers, glBufferMap);
renderPipelineState.setInstanceCount(instanceData.getDrawCount());
Globals.renderingEngine.checkError();
}
}
if(renderPipelineState.getInstanced()){
if(elementCount > 0 ){
GL45.glDrawElementsInstanced(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0, renderPipelineState.getInstanceCount());
Globals.renderingEngine.checkError();
}
} else {
if(useElementArray){
if(elementCount > 0){
GL11.glDrawElements(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0);
Globals.renderingEngine.checkError();
}
} else {
if(elementCount > 0){
GL11.glDrawArrays(GL_TRIANGLES, 0, elementCount);
Globals.renderingEngine.checkError();
}
}
}
glBindVertexArray(0);
Globals.renderingEngine.checkError();
}
/**
* Updates the bounding sphere of the mesh
* @param x
* @param y
* @param z
* @param r
*/
public void updateBoundingSphere(float x, float y, float z, float r){
this.boundingSphere.x = x;
this.boundingSphere.y = y;
this.boundingSphere.z = z;
this.boundingSphere.r = r;
}
/**
* Gets the bounding sphere of this mesh
* @return The bounding sphere
*/
public Sphered getBoundingSphere(){
return this.boundingSphere;
}
/**
* Gets the material of the mesh
* @return The material
*/
public Material getMaterial(){
return material;
}
/**
* Gets whether this mesh has bones or not
* @return true if has bones
*/
public boolean hasBones(){
return bones.size() > 0;
}
/**
* Gets the name of this mesh
* @return The name of the mesh
*/
public String getMeshName(){
return meshName;
}
/**
* Sets the parent model of this mesh
* @param parent The parent
*/
public void setParent(Model parent){
this.parent = parent;
}
/**
* Gets the bones for this mesh
* @return The list of bones
*/
public List<Bone> getBones(){
return bones;
}
/**
* Sets the bones for this mesh
* @param bones The list of bones
*/
public void setBones(List<Bone> bones){
this.bones = bones;
}
/**
* Registers a bone id
* @param boneId the bone id
*/
public void registerBoneId(String boneId){
this.boneIdList.add(boneId);
}
/**
* Sets whether to use an element array or a direct array
* @param useElementArray if true, use elements, else use direct array reference
*/
public void setUseElementArray(boolean useElementArray){
this.useElementArray = useElementArray;
}
/**
* Gets whether the mesh uses an element array or a direct array
* @return if true, use elements, else use direct array reference
*/
public boolean getUseElementArray(){
return this.useElementArray;
}
/**
* Gets the vertex buffer's pointer
* @return The vertex buffer's pointer
*/
public int getVertexBuffer(){
return vertexBuffer;
}
/**
* Gets the normal buffer's pointer
* @return The normal buffer's pointer
*/
public int getNormalBuffer(){
return normalBuffer;
}
/**
* Gets the texture coord buffer's pointer
* @return The texture coord buffer's pointer
*/
public int getTextureCoordBuffer(){
return textureCoordBuffer;
}
}