package electrosphere.renderer; import electrosphere.engine.Globals; import electrosphere.engine.Main; import electrosphere.entity.types.camera.CameraEntityUtils; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.RenderPipelineState.SelectedShaderEnum; import electrosphere.renderer.actor.ActorTextureMask; import electrosphere.renderer.actor.instance.InstanceData; import electrosphere.renderer.buffer.HomogenousInstancedArray; import electrosphere.renderer.buffer.HomogenousUniformBuffer; import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; import electrosphere.renderer.light.LightManager; import electrosphere.renderer.loading.ModelPretransforms; 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.Quaternionf; import org.joml.Vector3f; import org.joml.Vector4d; import org.lwjgl.BufferUtils; import org.lwjgl.PointerBuffer; import org.lwjgl.assimp.AIBone; import org.lwjgl.assimp.AIFace; import org.lwjgl.assimp.AIMesh; import org.lwjgl.assimp.AIVector2D; import org.lwjgl.assimp.AIVector3D; import org.lwjgl.assimp.AIVertexWeight; import static org.lwjgl.opengl.ARBVertexBufferObject.*; import org.lwjgl.opengl.GL11; import static org.lwjgl.opengl.GL11.GL_FALSE; import static org.lwjgl.opengl.GL11.GL_FLOAT; import static org.lwjgl.opengl.GL11.GL_INT; import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15C; 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 org.lwjgl.opengl.GL20; import static org.lwjgl.opengl.GL20.*; 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.glUseProgram; 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.GL40; import org.lwjgl.opengl.GL45; /** * * @author satellite */ public class Mesh { //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 public Model parent; public String nodeID; public AIMesh mesh; public int vertexBuffer; public int normalBuffer; public int elementArrayBuffer; public int elementCount; public int faceCount; public int vertexArrayObject; public int vertexCount; public int normalCount; public int boneWeightBuffer; public int boneIndexBuffer; public int boneCount; public int textureCoordBuffer; public int textureCoordCount; //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 public ArrayList bones = new ArrayList(); public ArrayList bone_id_list = new ArrayList(); HashMap uniforms = new HashMap(); int bone_map_size = 0; boolean hasBones = true; public boolean hasTextureCoords = true; public ActorTextureMask textureMask; public float vertexMinX; public float vertexMaxX; public float vertexMinY; public float vertexMaxY; public float vertexMinZ; public float vertexMaxZ; public ShaderProgram shader; ShaderProgram oitShader; int shaderBoneArrayLocation; Material material; public static Mesh create_mesh_from_aimesh(AIMesh mesh, ModelPretransforms.MeshMetadata metadata){ boolean has_bones = false; boolean apply_lighting = true; Mesh rVal = new Mesh(); rVal.mesh = mesh; rVal.nodeID = mesh.mName().dataString(); // // VAO // //Check for headless to not call gl functions when not running with gpu if(!Globals.HEADLESS){ rVal.vertexArrayObject = glGenVertexArrays(); glBindVertexArray(rVal.vertexArrayObject); } //Basic checks //check num vertices int numVertices = mesh.mNumVertices(); AIVector3D.Buffer vertexData = mesh.mVertices(); // while(vertexData.hasRemaining()){ // vertexData.get(); // numVertices++; // } // vertexData = vertexData.rewind(); //check num normals int numNormals = mesh.mNumVertices(); // AIVector3D.Buffer normalData = mesh.mNormals(); // while(normalData.hasRemaining()){ // normalData.get(); // numNormals++; // } // normalData.rewind(); if(numVertices != numNormals){ System.out.println("Catastrophic failure: Number of vertices =/= Number of normals"); System.exit(1); } Matrix4d vertexPretransform = new Matrix4d().identity(); if(metadata != null){ System.out.println("Pretransforming"); vertexPretransform.translationRotateScale(metadata.getOffset(), metadata.getRotation(), metadata.getScale()); } // //Buffer data to GPU // vertexData.rewind(); try { rVal.vertexCount = mesh.mNumVertices(); FloatBuffer VertexArrayBufferData = BufferUtils.createFloatBuffer(rVal.vertexCount * 3); float[] temp = new float[3]; boolean definedDimensions = false; float minX = 0, maxX = 0, minY = 0, maxY = 0, minZ = 0, maxZ = 0; for (int i = 0; i < rVal.vertexCount; i++) { AIVector3D vertex = vertexData.get(); float x = vertex.x(); float y = vertex.y(); float z = vertex.z(); if(definedDimensions){ if(x < minX){ minX = x; } if(x > maxX){ maxX = x; } if(y < minY){ minY = y; } if(y > maxY){ maxY = y; } if(z < minZ){ minZ = z; } if(z > maxZ){ maxZ = z; } } else { definedDimensions = true; minX = maxX = x; minY = maxY = y; minZ = maxZ = z; } Vector4d transformedVertex = vertexPretransform.transform(new Vector4d(x,y,z,1.0)); temp[0] = (float)transformedVertex.x; temp[1] = (float)transformedVertex.y; temp[2] = (float)transformedVertex.z; VertexArrayBufferData.put(temp); } rVal.vertexMaxX = maxX; rVal.vertexMinX = minX; rVal.vertexMaxY = maxY; rVal.vertexMinY = minY; rVal.vertexMaxZ = maxZ; rVal.vertexMinZ = minZ; VertexArrayBufferData.flip(); rVal.buffer_vertices(VertexArrayBufferData, 3); } catch (NullPointerException ex){ ex.printStackTrace(); } // // NORMALS // AIVector3D.Buffer normals = mesh.mNormals(); try { rVal.normalCount = mesh.mNumVertices(); FloatBuffer NormalArrayBufferData; if(rVal.normalCount > 0){ NormalArrayBufferData = BufferUtils.createFloatBuffer(rVal.normalCount * 3); float[] temp = new float[3]; for (int i = 0; i < rVal.normalCount; i++) { AIVector3D normal = normals.get(i); temp[0] = normal.x(); temp[1] = normal.y(); temp[2] = normal.z(); NormalArrayBufferData.put(temp); } NormalArrayBufferData.flip(); rVal.buffer_normals(NormalArrayBufferData, 3); } } catch (NullPointerException ex){ ex.printStackTrace(); } // // FACES // rVal.faceCount = mesh.mNumFaces(); rVal.elementCount = rVal.faceCount * 3; IntBuffer elementArrayBufferData = BufferUtils.createIntBuffer(rVal.elementCount); AIFace.Buffer facesBuffer = mesh.mFaces(); for(int i = 0; i < rVal.faceCount; i++){ AIFace face = facesBuffer.get(i); if(face.mNumIndices() != 3){ throw new IllegalStateException("AIFace.mNumIndices() != 3"); } elementArrayBufferData.put(face.mIndices()); } elementArrayBufferData.flip(); rVal.buffer_faces(elementArrayBufferData); // // TEXTURE COORDINATES // if(mesh.mTextureCoords().capacity() > 0){ AIVector3D.Buffer texturecoords = mesh.mTextureCoords(0); try { rVal.textureCoordCount = mesh.mTextureCoords(0).capacity(); FloatBuffer TextureArrayBufferData; if(rVal.textureCoordCount > 0){ TextureArrayBufferData = BufferUtils.createFloatBuffer(rVal.textureCoordCount * 2); float[] temp = new float[2]; for (int i = 0; i < rVal.textureCoordCount; i++) { AIVector3D normal = texturecoords.get(i); temp[0] = normal.x(); temp[1] = normal.y(); // temp[2] = normal.z(); TextureArrayBufferData.put(temp); } TextureArrayBufferData.flip(); rVal.buffer_texture_coords(TextureArrayBufferData, 2); } } catch (NullPointerException ex){ ex.printStackTrace(); } //System.out.println("Enabled texture coordinates"); } // //Read in bones //AND buffer data (weights) to GPU // PointerBuffer boneBuffer = mesh.mBones(); if(boneBuffer != null){ has_bones = true; while(boneBuffer.hasRemaining()){ long currentAddr = boneBuffer.get(); AIBone currentBoneData = AIBone.createSafe(currentAddr); // System.out.println("Num weights: " + currentBoneData.mNumWeights()); Bone currentBone = new Bone(); currentBone.boneID = currentBoneData.mName().dataString(); currentBone.inverseBindPoseMatrix = electrosphere.util.Utilities.convertAIMatrix(currentBoneData.mOffsetMatrix()); currentBone.numWeights = currentBoneData.mNumWeights(); currentBone.weights = new HashMap(); Iterator weightIterator = currentBoneData.mWeights().iterator(); while(weightIterator.hasNext()){ AIVertexWeight currentWeightData = weightIterator.next(); currentBone.weights.put(currentWeightData.mVertexId(), currentWeightData.mWeight()); } rVal.bones.add(currentBone); rVal.bone_id_list.add(currentBone.boneID); } rVal.boneCount = rVal.bones.size(); FloatBuffer boneWeightDataBuffer = BufferUtils.createFloatBuffer(4 * rVal.vertexCount);//FloatBuffer.allocate(4 * vertexCount); FloatBuffer boneIndexDataBuffer = BufferUtils.createFloatBuffer(4 * rVal.vertexCount);//IntBuffer.allocate(4 * vertexCount); Iterator boneIterator; for(int i = 0; i < rVal.vertexCount; i++){ float[] weight = new float[4]; float[] index = new float[4]; int boneCounter = 0; boneIterator = rVal.bones.iterator(); while(boneIterator.hasNext()){ Bone currentBone = boneIterator.next(); float boneVal = 0; if(currentBone.weights.get(i) != null){ boneVal = currentBone.weights.get(i); } if(boneVal > 0){ if(boneVal > weight[0]){ weight[3] = weight[2]; weight[2] = weight[1]; weight[1] = weight[0]; weight[0] = boneVal; index[3] = index[2]; index[2] = index[1]; index[1] = index[0]; index[0] = boneCounter; // if(rVal.nodeID.equals("Torso")){ // System.out.println(index[3] + " " + index[2] + " " + index[1] + " " + index[0]); // } } else if(boneVal > weight[1]){ weight[3] = weight[2]; weight[2] = weight[1]; weight[1] = boneVal; index[3] = index[2]; index[2] = index[1]; index[1] = boneCounter; } else if(boneVal > weight[2]){ weight[3] = weight[2]; weight[2] = boneVal; index[3] = index[2]; index[2] = boneCounter; } else if(boneVal > weight[3]){ weight[3] = boneVal; index[3] = boneCounter; } } boneCounter++; } float total = weight[0] + weight[1] + weight[2] + weight[3]; if(total != 1.0f){ weight[0] = weight[0] * (1.0f / total); weight[1] = weight[1] * (1.0f / total); weight[2] = weight[2] * (1.0f / total); weight[3] = weight[3] * (1.0f / total); } //If all are 0 (for instance the vertex doesn't have any bones with any weight > 0), the values for each weight will be NaN after the divide immediately above //If NaN, set all to 0 if(Float.isNaN(weight[0])){ weight[0] = 0; weight[1] = 0; weight[2] = 0; weight[3] = 0; } boneIndexDataBuffer.put(index); boneWeightDataBuffer.put(weight); } boneIndexDataBuffer.flip(); boneWeightDataBuffer.flip(); if(!Globals.HEADLESS){ rVal.boneWeightBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, rVal.boneWeightBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, boneWeightDataBuffer, GL_STATIC_DRAW); glVertexAttribPointer(2, 4, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(2); rVal.boneIndexBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, rVal.boneIndexBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, boneIndexDataBuffer, GL_STATIC_DRAW); glVertexAttribPointer(3, 4, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(3); } } else { rVal.hasBones = false; } if(!Globals.HEADLESS){ rVal.shader = ShaderProgram.smart_assemble_shader(has_bones, apply_lighting); rVal.oitShader = ShaderProgram.smartAssembleOITProgram(has_bones, apply_lighting); } if(!Globals.HEADLESS){ glBindVertexArray(0); } return rVal; } public Mesh(){ } public void buffer_vertices(FloatBuffer verticies, int vertexDimension){ if(!Globals.HEADLESS){ vertexBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, verticies, GL_STATIC_DRAW); glVertexAttribPointer(0, vertexDimension, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(0); } } public void buffer_normals(FloatBuffer normals, int normalDimension){ if(!Globals.HEADLESS){ normalBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, normalBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, normals, GL_STATIC_DRAW); glVertexAttribPointer(1, normalDimension, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(1); } } public void buffer_faces(IntBuffer faces){ if(!Globals.HEADLESS){ elementArrayBuffer = glGenBuffers(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementArrayBuffer); GL15.glBufferData(GL_ELEMENT_ARRAY_BUFFER, faces, GL_STATIC_DRAW); elementCount = faces.capacity(); } } public void buffer_texture_coords(FloatBuffer coords, int textureDimension){ if(!Globals.HEADLESS){ textureCoordBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, textureCoordBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, coords, GL_STATIC_DRAW); glVertexAttribPointer(4, textureDimension, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(4); } } public void bufferCustomFloatAttribArray(FloatBuffer buffer, int bufferDimension, int attribIndex){ if(!Globals.HEADLESS){ int customBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, customBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); glVertexAttribPointer(attribIndex, bufferDimension, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(attribIndex); } } public void bufferCustomIntAttribArray(IntBuffer buffer, int bufferDimension, int attribIndex){ if(!Globals.HEADLESS){ int customBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, customBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); glVertexAttribPointer(attribIndex, bufferDimension, GL_INT, false, 0, 0); glEnableVertexAttribArray(attribIndex); } } public void bufferCustomUIntAttribArray(IntBuffer buffer, int bufferDimension, int attribIndex){ if(!Globals.HEADLESS){ int customBuffer = glGenBuffers(); glBindBuffer(GL_ARRAY_BUFFER, customBuffer); GL15.glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); glVertexAttribPointer(attribIndex, bufferDimension, GL_UNSIGNED_INT, false, 0, 0); glEnableVertexAttribArray(attribIndex); } } public void setTextureMask(ActorTextureMask textureMask){ this.textureMask = textureMask; } public void setMaterial(Material input){ this.material = input; } public void generateShader(SelectedShaderEnum selectedShader, boolean hasBones, boolean applyLighting){ switch(selectedShader){ case PRIMARY: { shader = ShaderProgram.smart_assemble_shader(hasBones, applyLighting); } break; case OIT: { oitShader = ShaderProgram.smartAssembleOITProgram(hasBones, applyLighting); } break; } } public void setShader(ShaderProgram shader){ this.shader = shader; } public void setUniform(String key, Object o){ uniforms.put(key, o); } void bufferAllUniforms(){ for(String key : uniforms.keySet()){ Object currentUniformRaw = uniforms.get(key); if(currentUniformRaw instanceof Matrix4f){ Matrix4f currentUniform = (Matrix4f)currentUniformRaw; glUniformMatrix4fv(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, key), false, currentUniform.get(new float[16])); } if(currentUniformRaw instanceof Vector3f){ Vector3f currentUniform = (Vector3f)currentUniformRaw; glUniform3fv(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, key), currentUniform.get(BufferUtils.createFloatBuffer(3))); } if(currentUniformRaw instanceof Integer){ int currentInform = (Integer)currentUniformRaw; glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, key), currentInform); } } } /** * Sends a buffer to the gpu * @param uniformTypeMap The type of the buffer * @param buffers The buffer */ void bufferInstanceData( RenderPipelineState renderPipelineState, Map buffers, Map 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){ if(renderPipelineState.getUseMeshShader()){ ShaderProgram selectedProgram = null; switch(renderPipelineState.getSelectedShader()){ case PRIMARY: { selectedProgram = shader; } break; case OIT: { selectedProgram = oitShader; } break; } if(selectedProgram == null){ selectedProgram = shader; } Globals.renderingEngine.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(Globals.renderingEngine.getActiveShader().shaderProgram); } } if(renderPipelineState.getUseMaterial() && textureMask == null){ if(material == null){ Globals.materialDefault.apply_material(0,1); GL20.glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "hasTransparency"), 0); } else { material.apply_material(); if(material.hasTransparency){ GL20.glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "hasTransparency"), 1); } else { GL20.glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "hasTransparency"), 0); } } } glBindVertexArray(vertexArrayObject); if(textureMask != null){ int i = 0; // glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "groundTextures"), 5); // for(int j = 1; j < 15; j++){ // textureList.get(0).bind(j); // } for(Texture texture : textureMask.getTextures()){ // System.out.println(texture.getPath() + " => groundTextures[" + i + "]" + "=>" + (i)); if(texture != null){ texture.bind(5+i); } glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, textureMask.getUniformNames().get(i)),5+i); i++; } // for(int j = i; j < 10; j++){ // glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "groundTextures[" + j + "]"),6+j); // } // glActiveTexture(GL_TEXTURE0); } if(renderPipelineState.getUseShadowMap()){ glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, Globals.shadowMapTextureLoc); glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "shadowMap"), 3); } if(renderPipelineState.getUseBones()){ // //Handle bones // if(bones != null && !bones.isEmpty()){ glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "hasBones"), 1); glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "numBones"), bones.size()); Iterator boneIterator = bone_id_list.iterator(); float bufferarray[] = new float[16]; int incrementer = 0; while (boneIterator.hasNext()){ String boneName = boneIterator.next(); Bone currentBone = parent.boneMap.get(boneName); String currentUniform = "bones[" + incrementer + "]"; if(currentBone != null){ Matrix4d currentMat = new Matrix4d(currentBone.final_transform); currentMat.get(bufferarray); // if(boneName.equals("Torso")){ // System.out.println("Found torso bone"); // System.out.println(currentUniform); // System.out.println(currentMat); // System.exit(0); // } GL45.glUniformMatrix4fv(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, currentUniform), false, bufferarray); } else { // System.out.println("Bonename: " + boneName); // System.exit(1); GL45.glUniformMatrix4fv(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, currentUniform), false, new float[16]); } incrementer++; } } else { glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "hasBones"), 0); } } else { glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "hasBones"), 0); } if(renderPipelineState.getBufferStandardUniforms()){ //buffer model/view/proj matrices GL45.glUniformMatrix4fv(Globals.renderingEngine.getActiveShader().shaderVertexModelLoc, false, parent.modelMatrix.get(new float[16])); glUniformMatrix4fv(Globals.renderingEngine.getActiveShader().shaderVertexViewLoc, false, Globals.viewMatrix.get(new float[16])); glUniformMatrix4fv(Globals.renderingEngine.getActiveShader().shaderVertexProjectionLoc, false, Globals.projectionMatrix.get(new float[16])); glUniform3fv(Globals.renderingEngine.getActiveShader().shaderVertexViewPosLoc, CameraEntityUtils.getCameraEye(Globals.playerCamera).get(BufferUtils.createFloatBuffer(3))); glUniformMatrix4fv(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "lightSpaceMatrix"), false, Globals.lightDepthMatrix.get(new float[16])); glUniform1i(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "frame"), (int)Main.getCurrentFrame()); glUniform1f(glGetUniformLocation(Globals.renderingEngine.getActiveShader().shaderProgram, "time"), (float)Main.getCurrentFrame()); } if(renderPipelineState.getBufferNonStandardUniforms()){ bufferAllUniforms(); } if(renderPipelineState.getInstanced()){ InstanceData instanceData = renderPipelineState.getInstanceData(); Map buffers = instanceData.getCpuBufferMap(); Map glBufferMap = instanceData.getGlBufferMap(); bufferInstanceData(renderPipelineState, buffers, glBufferMap); } if(renderPipelineState.getInstanced()){ GL45.glDrawElementsInstanced(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0, renderPipelineState.getInstanceData().getDrawCount()); } else { GL11.glDrawElements(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0); } glBindVertexArray(0); } }