package electrosphere.renderer; import electrosphere.engine.Globals; import electrosphere.engine.assetmanager.AssetDataStrings; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.LinkedList; import java.util.List; import electrosphere.renderer.actor.ActorTextureMask; import electrosphere.renderer.model.Material; import electrosphere.renderer.model.Mesh; import electrosphere.renderer.model.Model; import electrosphere.renderer.shader.VisualShader; import electrosphere.renderer.texture.Texture; import org.joml.Vector3f; import org.lwjgl.BufferUtils; import static org.lwjgl.opengl.GL11.GL_FLOAT; import org.lwjgl.opengl.GL40; import org.lwjgl.util.par.ParShapes; import org.lwjgl.util.par.ParShapesMesh; /** * Utilities to assist with rendering */ public class RenderUtils { /** * Name of the mesh for the single block model */ public static final String MESH_NAME_BLOCK_SINGLE = "cube"; static int createScreenTextureVAO(){ int rVal = GL40.glGenVertexArrays(); GL40.glBindVertexArray(rVal); //vertices FloatBuffer vertexArrayBufferData = BufferUtils.createFloatBuffer(12); vertexArrayBufferData.put(-1.0f); vertexArrayBufferData.put( 1.0f); vertexArrayBufferData.put(-1.0f); vertexArrayBufferData.put(-1.0f); vertexArrayBufferData.put( 1.0f); vertexArrayBufferData.put(-1.0f); vertexArrayBufferData.put(-1.0f); vertexArrayBufferData.put( 1.0f); vertexArrayBufferData.put( 1.0f); vertexArrayBufferData.put(-1.0f); vertexArrayBufferData.put( 1.0f); vertexArrayBufferData.put( 1.0f); vertexArrayBufferData.flip(); int vertexBuffer = GL40.glGenBuffers(); GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, vertexBuffer); GL40.glBufferData(GL40.GL_ARRAY_BUFFER, vertexArrayBufferData, GL40.GL_STATIC_DRAW); GL40.glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0); GL40.glEnableVertexAttribArray(0); //texture coords FloatBuffer textureArrayBufferData = BufferUtils.createFloatBuffer(12); textureArrayBufferData.put(0.0f); textureArrayBufferData.put(1.0f); textureArrayBufferData.put(0.0f); textureArrayBufferData.put(0.0f); textureArrayBufferData.put(1.0f); textureArrayBufferData.put(0.0f); textureArrayBufferData.put(0.0f); textureArrayBufferData.put(1.0f); textureArrayBufferData.put(1.0f); textureArrayBufferData.put(0.0f); textureArrayBufferData.put(1.0f); textureArrayBufferData.put(1.0f); textureArrayBufferData.flip(); int textureCoordBuffer = GL40.glGenBuffers(); GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, textureCoordBuffer); GL40.glBufferData(GL40.GL_ARRAY_BUFFER, textureArrayBufferData, GL40.GL_STATIC_DRAW); GL40.glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0); GL40.glEnableVertexAttribArray(1); return rVal; } // public static void recaptureScreen(){ // //Makes the window that was just created the current OS-level window context // // glfwMakeContextCurrent(Globals.window); // //Maximize it // // glfwMaximizeWindow(Globals.window); // //grab focus // GLFW.glfwFocusWindow(Globals.window); // //apply mouse controls state // if(Globals.controlHandler.isMouseVisible()){ // Globals.controlHandler.showMouse(); // } else { // Globals.controlHandler.hideMouse(); // } // } public static Model createParticleModel(){ Model particleModel = new Model(); Mesh particleMesh = new Mesh("particleBillboard"); // // VAO // particleMesh.generateVAO(); float[] vertexcoords = { -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, }; // //Buffer data to GPU // try { int vertexCount = vertexcoords.length / 3; FloatBuffer vertexArrayBufferData = BufferUtils.createFloatBuffer(vertexCount * 3); float[] temp = new float[3]; for (int i = 0; i < vertexCount; i++) { temp[0] = vertexcoords[i * 3 + 0]; temp[1] = vertexcoords[i * 3 + 1]; temp[2] = vertexcoords[i * 3 + 2]; vertexArrayBufferData.put(temp); } vertexArrayBufferData.flip(); particleMesh.bufferVertices(vertexArrayBufferData, 3); } catch (NullPointerException ex){ ex.printStackTrace(); } int[] facedata = { 0,1,2, 1,2,3, }; // // FACES // int faceCount = facedata.length / 3; int elementCount = facedata.length; IntBuffer elementArrayBufferData = BufferUtils.createIntBuffer(elementCount); for(int i = 0; i < faceCount; i++){ int[] temp = new int[3]; temp[0] = facedata[i * 3 + 0]; temp[1] = facedata[i * 3 + 1]; temp[2] = facedata[i * 3 + 2]; elementArrayBufferData.put(temp); } elementArrayBufferData.flip(); particleMesh.bufferFaces(elementArrayBufferData,elementCount); // // TEXTURE COORDS // FloatBuffer texture_coords = BufferUtils.createFloatBuffer(8); float[] texturedata = { 0,1, 1,1, 0,0, 1,0 }; texture_coords.put(texturedata); texture_coords.flip(); particleMesh.bufferTextureCoords(texture_coords, 2); particleMesh.setShader(VisualShader.smartAssembleOITProgram(false, true)); GL40.glBindVertexArray(0); particleMesh.setMaterial(new Material(AssetDataStrings.TEXTURE_PARTICLE)); particleMesh.setParent(particleModel); particleModel.getMeshes().add(particleMesh); return particleModel; } public static Model createPlaneModel(String vertexShader, String fragmentShader){ Model rVal = new Model(); Mesh planeMesh = new Mesh("plane"); // // VAO // planeMesh.generateVAO(); float[] vertexcoords = { -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, }; // //Buffer data to GPU // try { int vertexCount = vertexcoords.length / 3; FloatBuffer vertexArrayBufferData = BufferUtils.createFloatBuffer(vertexCount * 3); float[] temp = new float[3]; for (int i = 0; i < vertexCount; i++) { temp[0] = vertexcoords[i * 3 + 0]; temp[1] = vertexcoords[i * 3 + 1]; temp[2] = vertexcoords[i * 3 + 2]; vertexArrayBufferData.put(temp); } vertexArrayBufferData.flip(); planeMesh.bufferVertices(vertexArrayBufferData, 3); } catch (NullPointerException ex){ ex.printStackTrace(); } int[] facedata = { 0,1,2, 1,2,3, }; // // FACES // int faceCount = facedata.length / 3; int elementCount = facedata.length; IntBuffer elementArrayBufferData = BufferUtils.createIntBuffer(elementCount); for(int i = 0; i < faceCount; i++){ int[] temp = new int[3]; temp[0] = facedata[i * 3 + 0]; temp[1] = facedata[i * 3 + 1]; temp[2] = facedata[i * 3 + 2]; elementArrayBufferData.put(temp); } elementArrayBufferData.flip(); planeMesh.bufferFaces(elementArrayBufferData,elementCount); // // TEXTURE COORDS // FloatBuffer texture_coords = BufferUtils.createFloatBuffer(8); float[] texturedata = { 0,1, 1,1, 0,0, 1,0 }; texture_coords.put(texturedata); texture_coords.flip(); planeMesh.bufferTextureCoords(texture_coords, 2); planeMesh.setShader(VisualShader.loadSpecificShader(vertexShader,fragmentShader)); GL40.glBindVertexArray(0); planeMesh.setParent(rVal); rVal.getMeshes().add(planeMesh); return rVal; } /** * Generates a unit sphere model * @return The model */ public static Model createUnitsphere(){ Model model = new Model(); Mesh sphereMesh = new Mesh("sphere"); sphereMesh.generateVAO(); //buffer coords ParShapesMesh data = ParShapes.par_shapes_create_parametric_sphere(10, 5); int numPoints = data.npoints(); //verts { FloatBuffer verts = data.points(numPoints * 3); FloatBuffer vertsFinal = BufferUtils.createFloatBuffer(verts.limit()); //reallocating to BufferUtils buffer to help minimize memory errors vertsFinal.put(verts); vertsFinal.flip(); sphereMesh.bufferVertices(vertsFinal, 3); } //indices { IntBuffer indices = data.triangles(data.ntriangles() * 3); IntBuffer indicesFinal = BufferUtils.createIntBuffer(indices.limit()); //reallocating to BufferUtils buffer to help minimize memory errors indicesFinal.put(indices); indicesFinal.flip(); sphereMesh.bufferFaces(indicesFinal, data.ntriangles() * 3); } //texture coords { FloatBuffer texCoords = data.tcoords(numPoints * 3); FloatBuffer texCoordsFinal = BufferUtils.createFloatBuffer(texCoords.limit()); //reallocating to BufferUtils buffer to help minimize memory errors texCoordsFinal.put(texCoords); texCoordsFinal.flip(); sphereMesh.bufferTextureCoords(texCoordsFinal, 2); } //setup extra structures Material mat = new Material(); mat.setDiffuse(AssetDataStrings.TEXTURE_TEAL_TRANSPARENT); sphereMesh.setMaterial(mat); sphereMesh.setShader(VisualShader.smartAssembleShader(false, true)); GL40.glBindVertexArray(0); sphereMesh.setParent(model); model.getMeshes().add(sphereMesh); return model; } /** * Creates a unit cylinder model * @return The model */ public static Model createUnitCylinder(){ Model model = new Model(); Mesh sphereMesh = new Mesh("cylinder"); sphereMesh.generateVAO(); //buffer coords ParShapesMesh data = ParShapes.par_shapes_create_cylinder(10, 2); ParShapes.par_shapes_rotate(data, (float)(Math.PI / 2.0), new float[]{-1,0,0}); ParShapes.par_shapes_translate(data, 0, -0.5f, 0); ParShapes.par_shapes_scale(data, -1.0f, 2.0f, 1.0f); int numPoints = data.npoints(); //verts { FloatBuffer verts = data.points(numPoints * 3); FloatBuffer vertsFinal = BufferUtils.createFloatBuffer(verts.limit()); //reallocating to BufferUtils buffer to help minimize memory errors vertsFinal.put(verts); vertsFinal.flip(); sphereMesh.bufferVertices(vertsFinal, 3); } //indices { IntBuffer indices = data.triangles(data.ntriangles() * 3); IntBuffer indicesFinal = BufferUtils.createIntBuffer(indices.limit()); //reallocating to BufferUtils buffer to help minimize memory errors indicesFinal.put(indices); indicesFinal.flip(); sphereMesh.bufferFaces(indicesFinal, data.ntriangles() * 3); } //texture coords { FloatBuffer texCoords = data.tcoords(numPoints * 3); FloatBuffer texCoordsFinal = BufferUtils.createFloatBuffer(texCoords.limit()); //reallocating to BufferUtils buffer to help minimize memory errors texCoordsFinal.put(texCoords); texCoordsFinal.flip(); sphereMesh.bufferTextureCoords(texCoordsFinal, 2); } //setup extra structures Material mat = new Material(); mat.setDiffuse(AssetDataStrings.TEXTURE_TEAL_TRANSPARENT); sphereMesh.setMaterial(mat); sphereMesh.setShader(VisualShader.smartAssembleShader(false, true)); GL40.glBindVertexArray(0); sphereMesh.setParent(model); model.getMeshes().add(sphereMesh); return model; } /** * Creates a unit cylinder model * @return The model */ public static Model createUnitCube(){ Model model = new Model(); Mesh sphereMesh = new Mesh("cube"); sphereMesh.generateVAO(); //buffer coords int numTriangles = 12; //verts BufferUtils.createFloatBuffer(3 * 8); FloatBuffer verts = BufferUtils.createFloatBuffer(3 * 8); verts.put(new float[]{ -0.5f,-0.5f,-0.5f, 0.5f,-0.5f,-0.5f, -0.5f, 0.5f,-0.5f, 0.5f, 0.5f,-0.5f, -0.5f,-0.5f, 0.5f, 0.5f,-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, }); verts.flip(); sphereMesh.bufferVertices(verts, 3); //indices IntBuffer indices = BufferUtils.createIntBuffer(3*12); indices.put(new int[]{ //Top 2, 6, 7, 2, 3, 7, //Bottom 0, 4, 5, 0, 1, 5, //Left 0, 2, 6, 0, 4, 6, //Right 1, 3, 7, 1, 5, 7, //Front 0, 2, 3, 0, 1, 3, //Back 4, 6, 7, 4, 5, 7 }); indices.flip(); sphereMesh.bufferFaces(indices, numTriangles * 3); //texture coords FloatBuffer texCoords = BufferUtils.createFloatBuffer(2*8); texCoords.put(new float[]{ 0,0, 1,0, 0,1, 1,1, 0,0, 0,1, 1,0, 1,1, }); texCoords.flip(); sphereMesh.bufferTextureCoords(texCoords, 2); //setup extra structures Material mat = new Material(); mat.setDiffuse(AssetDataStrings.TEXTURE_TEAL_TRANSPARENT); sphereMesh.setMaterial(mat); sphereMesh.setShader(VisualShader.smartAssembleShader(false, true)); GL40.glBindVertexArray(0); sphereMesh.setParent(model); model.getMeshes().add(sphereMesh); return model; } /** * Creates a unit cylinder model * @return The model */ public static Model createBlockSingleModel(){ Model model = new Model(); Mesh cubeMesh = new Mesh(RenderUtils.MESH_NAME_BLOCK_SINGLE); cubeMesh.generateVAO(); //buffer coords int numTriangles = 12; //verts BufferUtils.createFloatBuffer(3 * 8); FloatBuffer verts = BufferUtils.createFloatBuffer(3 * 8); verts.put(new float[]{ -0.1f,-0.1f,-0.1f, 0.1f,-0.1f,-0.1f, -0.1f, 0.1f,-0.1f, 0.1f, 0.1f,-0.1f, -0.1f,-0.1f, 0.1f, 0.1f,-0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, }); verts.flip(); cubeMesh.bufferVertices(verts, 3); //indices IntBuffer indices = BufferUtils.createIntBuffer(3*12); indices.put(new int[]{ //Top 2, 6, 7, 2, 3, 7, //Bottom 0, 4, 5, 0, 1, 5, //Left 0, 2, 6, 0, 4, 6, //Right 1, 3, 7, 1, 5, 7, //Front 0, 2, 3, 0, 1, 3, //Back 4, 6, 7, 4, 5, 7 }); indices.flip(); cubeMesh.bufferFaces(indices, numTriangles * 3); //texture coords FloatBuffer texCoords = BufferUtils.createFloatBuffer(2*8); texCoords.put(new float[]{ 0,0, 1,0, 0,1, 1,1, 0,0, 0,1, 1,0, 1,1, }); texCoords.flip(); cubeMesh.bufferTextureCoords(texCoords, 2); //setup extra structures Material mat = new Material(); mat.setDiffuse(AssetDataStrings.TEXTURE_BLOCK_ATLAS); cubeMesh.setMaterial(mat); cubeMesh.setShader(VisualShader.loadSpecificShader(AssetDataStrings.SHADER_BLOCK_SINGLE_VERT, AssetDataStrings.SHADER_BLOCK_SINGLE_FRAG)); GL40.glBindVertexArray(0); cubeMesh.setParent(model); model.getMeshes().add(cubeMesh); return model; } @Deprecated public static Model createBitmapDisplay(){ Model rVal = new Model(); Mesh m = new Mesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME); m.generateVAO(); //vertices FloatBuffer vertexArrayBufferData = BufferUtils.createFloatBuffer(12); vertexArrayBufferData.put( 0); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 0); vertexArrayBufferData.put( 0); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 0); vertexArrayBufferData.put( 0); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 0); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 1); vertexArrayBufferData.flip(); IntBuffer faceArrayBufferData = BufferUtils.createIntBuffer(6); faceArrayBufferData.put(0); faceArrayBufferData.put(1); faceArrayBufferData.put(2); faceArrayBufferData.put(3); faceArrayBufferData.put(4); faceArrayBufferData.put(5); faceArrayBufferData.flip(); //texture coords FloatBuffer textureArrayBufferData = BufferUtils.createFloatBuffer(12); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(1); textureArrayBufferData.flip(); //buffer vertices m.bufferVertices(vertexArrayBufferData, 2); //buffer normals m.bufferNormals(vertexArrayBufferData, 2); //buffer faces m.bufferFaces(faceArrayBufferData, 2); //buffer texture coords m.bufferTextureCoords(textureArrayBufferData, 2); m.setShader(VisualShader.loadSpecificShader("/Shaders/ui/font/basicbitmap/basicbitmap.vs", "/Shaders/ui/font/basicbitmap/basicbitmap.fs")); GL40.glBindVertexArray(0); m.setParent(rVal); Material uiMat = new Material(); Globals.assetManager.addTexturePathtoQueue("/Textures/Fonts/myfont1-harsher.png"); uiMat.setDiffuse("/Textures/Fonts/myfont1-harsher.png"); uiMat.setSpecular("/Textures/Fonts/myfont1-harsher.png"); m.setMaterial(uiMat); rVal.getMaterials().add(uiMat); rVal.getMeshes().add(m); return rVal; } /** * Creates a model to use to show bitmap characters * @return The model */ public static Model createBitmapCharacter(){ Model rVal = new Model(); Mesh m = new Mesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME); m.generateVAO(); //vertices FloatBuffer vertexArrayBufferData = BufferUtils.createFloatBuffer(12); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 1); vertexArrayBufferData.flip(); IntBuffer faceArrayBufferData = BufferUtils.createIntBuffer(6); faceArrayBufferData.put(0); faceArrayBufferData.put(1); faceArrayBufferData.put(2); faceArrayBufferData.put(3); faceArrayBufferData.put(4); faceArrayBufferData.put(5); faceArrayBufferData.flip(); //texture coords FloatBuffer textureArrayBufferData = BufferUtils.createFloatBuffer(12); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(1); textureArrayBufferData.flip(); //buffer vertices m.bufferVertices(vertexArrayBufferData, 2); //buffer normals m.bufferNormals(vertexArrayBufferData, 2); //buffer faces m.bufferFaces(faceArrayBufferData, 6); //buffer texture coords m.bufferTextureCoords(textureArrayBufferData, 2); m.setShader(VisualShader.loadSpecificShader("/Shaders/ui/font/bitmapchar/bitmapchar.vs", "/Shaders/ui/font/bitmapchar/bitmapchar.fs")); GL40.glBindVertexArray(0); m.setParent(rVal); rVal.getMeshes().add(m); return rVal; } public static Model createInWindowPanel(String vertexShader, String fragmentShader){ Model rVal = new Model(); Mesh m = new Mesh("plane"); m.generateVAO(); //vertices FloatBuffer vertexArrayBufferData = BufferUtils.createFloatBuffer(12); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put(-1); vertexArrayBufferData.put( 1); vertexArrayBufferData.put( 1); vertexArrayBufferData.flip(); IntBuffer faceArrayBufferData = BufferUtils.createIntBuffer(6); faceArrayBufferData.put(0); faceArrayBufferData.put(1); faceArrayBufferData.put(2); faceArrayBufferData.put(3); faceArrayBufferData.put(4); faceArrayBufferData.put(5); faceArrayBufferData.flip(); //texture coords FloatBuffer textureArrayBufferData = BufferUtils.createFloatBuffer(12); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(1); textureArrayBufferData.put(0); textureArrayBufferData.put(1); textureArrayBufferData.put(1); textureArrayBufferData.flip(); //buffer vertices m.bufferVertices(vertexArrayBufferData, 2); //buffer normals m.bufferNormals(vertexArrayBufferData, 2); //buffer faces m.bufferFaces(faceArrayBufferData, 2); //buffer texture coords m.bufferTextureCoords(textureArrayBufferData, 2); m.setShader(VisualShader.loadSpecificShader(vertexShader, fragmentShader)); GL40.glBindVertexArray(0); m.setParent(rVal); rVal.getMeshes().add(m); return rVal; } public static Model createTerrainModelPrecomputedShader(float[][] heightfield, float[][] texturemap, VisualShader program, int stride){ Model rVal = new Model(); Mesh m = new Mesh("terrain"); int width = heightfield.length; int height = heightfield[0].length; int actualWidth = (int)Math.ceil(1.0f * width / (1.0f * stride)); int actualHeight = (int)Math.ceil(1.0f * height / (1.0f * stride)); // System.out.println(actualWidth + " " + actualHeight); // System.out.println((actualWidth - 1) * (actualHeight - 1)); FloatBuffer vertices; FloatBuffer normals; IntBuffer faces; FloatBuffer texture_coords; FloatBuffer textureIndices; if(stride * actualWidth > width){ int drawWidth = actualWidth + 1; int drawHeight = actualHeight + 1; vertices = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 12); normals = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 12); faces = BufferUtils.createIntBuffer((drawWidth - 1) * (drawHeight - 1) * 2 * 3); texture_coords = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 8); textureIndices = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 16); } else { vertices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 12); normals = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 12); faces = BufferUtils.createIntBuffer((actualWidth - 1) * (actualHeight - 1) * 2 * 3); texture_coords = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 8); textureIndices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 16); } int incrementer = 0; // int numFaces = (actualWidth - 1) * (actualHeight - 1) * 2 * 3; for(int x = 0; x < width - 1; x = x + stride){ for(int y = 0; y < height - 1; y = y + stride){ //deal with vertex //0,0 vertices.put(x); vertices.put(heightfield[x][y]); vertices.put(y); //1,0 vertices.put(x + stride); vertices.put(heightfield[x+stride][y]); vertices.put(y); //0,1 vertices.put(x); vertices.put(heightfield[x][y+stride]); vertices.put(y + stride); //1,1 vertices.put(x + stride); vertices.put(heightfield[x+stride][y+stride]); vertices.put(y + stride); //deal with normal Vector3f normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x, y); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x + stride, y); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x, y + stride); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x + stride, y + stride); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); //deal with texture coordinates // if(x / stride % 2 == 0){ // if(y / stride % 2 == 0){ // texture_coords.put(0); // texture_coords.put(0); // texture_coords.put(1); // texture_coords.put(0); // texture_coords.put(0); // texture_coords.put(1); // texture_coords.put(1); // texture_coords.put(1); // } else { // texture_coords.put(0); // texture_coords.put(1); // } // } else { // if(y / stride % 2 == 0){ // texture_coords.put(1); // texture_coords.put(0); // } else { // texture_coords.put(1); // texture_coords.put(1); // } // } texture_coords.put(0); texture_coords.put(0); texture_coords.put(1); texture_coords.put(0); texture_coords.put(0); texture_coords.put(1); texture_coords.put(1); texture_coords.put(1); if(x + stride < width - 1 && y + stride < height - 1){ // texturemap[x+stride][y+stride]; for(int i = 0; i < 4 ; i++){ // textureIndices.put(1); // textureIndices.put(0); // textureIndices.put(0); // textureIndices.put(0); textureIndices.put(texturemap[x][y]); textureIndices.put(texturemap[x+stride][y]); textureIndices.put(texturemap[x][y+stride]); textureIndices.put(texturemap[x+stride][y+stride]); } } else { for(int i = 0; i < 4 ; i++){ textureIndices.put(0); textureIndices.put(0); textureIndices.put(0); textureIndices.put(0); } } //deal with faces if(1.0f * x / stride < actualWidth - 1 && 1.0f * y / stride < actualHeight - 1){ faces.put(incrementer * 4 + 0); faces.put(incrementer * 4 + 1); faces.put(incrementer * 4 + 2); faces.put(incrementer * 4 + 1); faces.put(incrementer * 4 + 2); faces.put(incrementer * 4 + 3); } incrementer++; } } vertices.flip(); normals.flip(); faces.flip(); texture_coords.flip(); textureIndices.flip(); m.generateVAO(); //buffer vertices m.bufferVertices(vertices, 3); //buffer normals m.bufferNormals(normals, 3); //buffer faces m.bufferFaces(faces,incrementer*2); //buffer texture coords m.bufferTextureCoords(texture_coords, 2); //texture indices m.bufferCustomFloatAttribArray(textureIndices, 4, 5); m.setShader(program); GL40.glBindVertexArray(0); m.setParent(rVal); Material groundMat = new Material(); Globals.assetManager.addTexturePathtoQueue("/Textures/Ground/Dirt1.png"); groundMat.setDiffuse("/Textures/Ground/Dirt1.png"); groundMat.setSpecular("/Textures/Ground/Dirt1.png"); m.setMaterial(groundMat); rVal.getMeshes().add(m); return rVal; } static float MINIMIZATION_DIFF_MAX = 0.001f; public static Model createMinimizedTerrainModelPrecomputedShader(float[][] heightfield, float[][] texturemap, VisualShader program, int stride){ class QuadToGenerate { //coords are inclusive int startX; int endX; int startY; int endY; float min; float max; float texture; boolean homogeneousTexture; QuadToGenerate(int startX, int startY, int endX, int endY, float diff, float min, float max, boolean homogeneousTexture, float texture){ this.startX = startX; this.startY = startY; this.endX = endX; this.endY = endY; this.min = min; this.max = max; this.texture = texture; this.homogeneousTexture = homogeneousTexture; } } Model rVal = new Model(); Mesh m = new Mesh("terrain"); int width = heightfield.length; int height = heightfield[0].length; int actualWidth = (int)Math.ceil(1.0f * width / (1.0f * stride)); int actualHeight = (int)Math.ceil(1.0f * height / (1.0f * stride)); // System.out.println(actualWidth + " " + actualHeight); // System.out.println((actualWidth - 1) * (actualHeight - 1)); FloatBuffer vertices; FloatBuffer normals; IntBuffer faces; FloatBuffer texture_coords; FloatBuffer textureIndices; if(stride * actualWidth > width){ int drawWidth = actualWidth + 1; int drawHeight = actualHeight + 1; vertices = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 12); normals = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 12); faces = BufferUtils.createIntBuffer((drawWidth - 1) * (drawHeight - 1) * 2 * 3); texture_coords = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 8); textureIndices = BufferUtils.createFloatBuffer(drawWidth * drawHeight * 16); } else { vertices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 12); normals = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 12); faces = BufferUtils.createIntBuffer((actualWidth - 1) * (actualHeight - 1) * 2 * 3); texture_coords = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 8); textureIndices = BufferUtils.createFloatBuffer(actualWidth * actualHeight * 16); } //merge along y List firstPhaseQuads = new LinkedList(); QuadToGenerate quadCurrent = null; float minVal = 0; float maxVal = 0; for(int x = 0; x < width - 1; x = x + stride){ quadCurrent = null; for(int y = 0; y < height - 1; y = y + stride){ if((x == 5 && y == 2)){ // System.out.println(quadCurrent); // continue; } if(quadCurrent == null){ minVal = 100000000; maxVal = 0; //minval if(heightfield[x][y] < minVal){ minVal = heightfield[x][y]; } if(heightfield[x+stride][y] < minVal){ minVal = heightfield[x+stride][y]; } if(heightfield[x][y+stride] < minVal){ minVal = heightfield[x][y+stride]; } if(heightfield[x+stride][y+stride] < minVal){ minVal = heightfield[x+stride][y+stride]; } //maxval if(heightfield[x][y] > maxVal){ maxVal = heightfield[x][y]; } if(heightfield[x+stride][y] > maxVal){ maxVal = heightfield[x+stride][y]; } if(heightfield[x][y+stride] > maxVal){ maxVal = heightfield[x][y+stride]; } if(heightfield[x+stride][y+stride] > maxVal){ maxVal = heightfield[x+stride][y+stride]; } boolean textureMatch = false; float texture = -1; if(x+stride < width - 1 && y+stride < height - 1 && texturemap[x][y] == texturemap[x+stride][y] && texturemap[x][y] == texturemap[x][y+stride] && texturemap[x][y] == texturemap[x+stride][y+stride]){ textureMatch = true; texture = texturemap[x][y]; } else { // if(x > 8 && (x+stride < width - 1) && (y+stride < height -1)){ // System.out.println( // (x+stride < width - 1) + " " + // (y+stride < height -1) + " " + // (texturemap[x][y] == texturemap[x+stride][y]) + " " + // (texturemap[x][y] == texturemap[x][y+stride]) + " " + // (texturemap[x][y] == texturemap[x+stride][y+stride]) // ); // } } if(textureMatch){ quadCurrent = new QuadToGenerate(x,y,x+stride,y+stride,maxVal - minVal,minVal,maxVal,textureMatch,texture); } else { firstPhaseQuads.add(new QuadToGenerate(x,y,x+stride,y+stride,maxVal - minVal,minVal,maxVal,textureMatch,texture)); // quadCurrent = null; } } else { float newMin = minVal; float newMax = maxVal; //min if(heightfield[x][y+stride] < newMin){ newMin = heightfield[x][y+stride]; } if(heightfield[x+stride][y+stride] < newMin){ newMin = heightfield[x+stride][y+stride]; } //max if(heightfield[x][y+stride] > newMax){ newMax = heightfield[x][y+stride]; } if(heightfield[x+stride][y+stride] > newMax){ newMax = heightfield[x+stride][y+stride]; } if(y+stride < height - 1 && x+stride < width - 1){ if(newMax - newMin < MINIMIZATION_DIFF_MAX && quadCurrent.texture == texturemap[x+stride][y] && quadCurrent.texture == texturemap[x ][y+stride] && quadCurrent.texture == texturemap[x+stride][y+stride] && quadCurrent.homogeneousTexture ){ //add to quad quadCurrent.endY = y + stride; quadCurrent.min = newMax; quadCurrent.max = newMax; } else { //push quad firstPhaseQuads.add(quadCurrent); firstPhaseQuads.add(new QuadToGenerate(x,y,x+stride,y+stride,maxVal - minVal,minVal,maxVal,false,0)); quadCurrent = null; // System.out.println("Push"); } } else { if(newMax - newMin < MINIMIZATION_DIFF_MAX){ //add to quad quadCurrent.endY = y + stride; quadCurrent.min = newMax; quadCurrent.max = newMax; } else { //push quad that we were building firstPhaseQuads.add(quadCurrent); // quadCurrent = null; //create new quad from what we were just analyzing boolean textureMatch = false; float texture = -1; quadCurrent = new QuadToGenerate(x,y,x+stride,y+stride,maxVal - minVal,minVal,maxVal,textureMatch,texture); // System.out.println("Push"); } } } } if(quadCurrent != null){ firstPhaseQuads.add(quadCurrent); } } List finalQuads = new LinkedList(); // for(QuadToGenerate current : firstPhaseQuads){ // finalQuads.add(current); // } // System.out.println(finalQuads.size()); //merge along x // QuadToGenerate currentQuad = null; List toSkip = new LinkedList(); for(QuadToGenerate currentQuad : firstPhaseQuads){ // toRemove.clear(); if(toSkip.contains(currentQuad)){ continue; } for(QuadToGenerate currentPotentialMatch : firstPhaseQuads){ if(currentPotentialMatch.startX <= currentQuad.startX){ continue; } if(currentPotentialMatch.startX > currentQuad.endX){ break; } if(currentPotentialMatch.startY != currentQuad.startY){ continue; } if(currentPotentialMatch.endY != currentQuad.endY){ continue; } if( !(currentQuad.homogeneousTexture && currentPotentialMatch.homogeneousTexture && currentQuad.texture == currentPotentialMatch.texture) ){ continue; } if(currentQuad.min < currentPotentialMatch.min && currentQuad.max < currentPotentialMatch.max){ float min = currentQuad.min; float max = currentPotentialMatch.max; if(max - min < MINIMIZATION_DIFF_MAX){ currentQuad.endX = currentPotentialMatch.endX; currentQuad.max = currentPotentialMatch.max; toSkip.add(currentPotentialMatch); } } else if(currentQuad.min > currentPotentialMatch.min && currentQuad.max > currentPotentialMatch.max){ float min = currentPotentialMatch.min; float max = currentQuad.max; if(max - min < MINIMIZATION_DIFF_MAX){ currentQuad.endX = currentPotentialMatch.endX; currentQuad.min = currentPotentialMatch.min; toSkip.add(currentPotentialMatch); } } else { if(currentQuad.min < currentPotentialMatch.min){ currentQuad.endX = currentPotentialMatch.endX; } else { currentQuad.endX = currentPotentialMatch.endX; currentQuad.min = currentPotentialMatch.min; currentQuad.max = currentPotentialMatch.max; } toSkip.add(currentPotentialMatch); } } finalQuads.add(currentQuad); } // for(QuadToGenerate currentIteration : firstPhaseQuads){ // if(currentQuad == null){ // currentQuad = currentIteration; // } else { // //if should merge: // if( // currentQuad.homogeneousTexture && // currentIteration.homogeneousTexture && // currentQuad.texture == currentIteration.texture // ){ // if(currentQuad.min < currentIteration.min && currentQuad.max < currentIteration.max){ // float min = currentQuad.min; // float max = currentIteration.max; // if(max - min < MINIMIZATION_DIFF_MAX){ // currentQuad.endX = currentIteration.endX; // currentQuad.max = currentIteration.max; // } else { // finalQuads.add(currentQuad); // currentQuad = currentIteration; // } // } else if(currentQuad.min > currentIteration.min && currentQuad.max > currentIteration.max){ // float min = currentIteration.min; // float max = currentQuad.max; // if(max - min < MINIMIZATION_DIFF_MAX){ // currentQuad.endX = currentIteration.endX; // currentQuad.min = currentIteration.min; // } else { // finalQuads.add(currentQuad); // currentQuad = currentIteration; // } // } else { // if(currentQuad.min < currentIteration.min){ // currentQuad.endX = currentIteration.endX; // } else { // currentQuad.endX = currentIteration.endX; // currentQuad.min = currentIteration.min; // currentQuad.max = currentIteration.max; // } // } // } else { // finalQuads.add(currentQuad); // currentQuad = currentIteration; // } // } // } // finalQuads.add(currentQuad); // for(QuadToGenerate current : finalQuads){ // if(current.startX > 0 && current.startY > 0 && current.endX < 99 && current.endY < 99){ // System.out.println(current.startX + " " + current.startY + " " + current.endX + " " + current.endY); // } // } // System.out.println("AAAAAAAAAAAAAAAAAA"); // System.out.println(finalQuads.size()); int incrementer = 0; for(QuadToGenerate current : finalQuads){ //deal with vertex //0,0 vertices.put(current.startX); vertices.put(heightfield[current.startX][current.startY]); vertices.put(current.startY); //1,0 vertices.put(current.endX); vertices.put(heightfield[current.endX][current.startY]); vertices.put(current.startY); //0,1 vertices.put(current.startX); vertices.put(heightfield[current.startX][current.endY]); vertices.put(current.endY); //1,1 vertices.put(current.endX); vertices.put(heightfield[current.endX][current.endY]); vertices.put(current.endY); //deal with normal Vector3f normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, current.startX, current.startY); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, current.endX, current.startY); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, current.startX, current.endY); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, current.endX, current.endY); normals.put(normal.x); normals.put(normal.y); normals.put(normal.z); //deal with texture coordinates // if(x / stride % 2 == 0){ // if(y / stride % 2 == 0){ // texture_coords.put(0); // texture_coords.put(0); // texture_coords.put(1); // texture_coords.put(0); // texture_coords.put(0); // texture_coords.put(1); // texture_coords.put(1); // texture_coords.put(1); // } else { // texture_coords.put(0); // texture_coords.put(1); // } // } else { // if(y / stride % 2 == 0){ // texture_coords.put(1); // texture_coords.put(0); // } else { // texture_coords.put(1); // texture_coords.put(1); // } // } texture_coords.put(0); texture_coords.put(0); texture_coords.put(current.endX - current.startX); texture_coords.put(0); texture_coords.put(0); texture_coords.put(current.endY - current.startY); texture_coords.put(current.endX - current.startX); texture_coords.put(current.endY - current.startY); if(current.endX < width - 1 && current.endY < height - 1){ // texturemap[x+stride][y+stride]; for(int i = 0; i < 4 ; i++){ // textureIndices.put(1); // textureIndices.put(0); // textureIndices.put(0); // textureIndices.put(0); textureIndices.put(texturemap[current.startX][current.startY]); textureIndices.put(texturemap[current.endX][current.startY]); textureIndices.put(texturemap[current.startX][current.endY]); textureIndices.put(texturemap[current.endX][current.endY]); } } else { for(int i = 0; i < 4 ; i++){ textureIndices.put(0); textureIndices.put(0); textureIndices.put(0); textureIndices.put(0); } } //deal with faces faces.put(incrementer * 4 + 0); faces.put(incrementer * 4 + 1); faces.put(incrementer * 4 + 2); faces.put(incrementer * 4 + 1); faces.put(incrementer * 4 + 2); faces.put(incrementer * 4 + 3); incrementer++; } // int numFaces = (actualWidth - 1) * (actualHeight - 1) * 2 * 3; // for(int x = 0; x < width - 1; x = x + stride){ // for(int y = 0; y < height - 1; y = y + stride){ // //deal with vertex // //0,0 // vertices.put(x); // vertices.put(heightfield[x][y]); // vertices.put(y); // //1,0 // vertices.put(x + stride); // vertices.put(heightfield[x+stride][y]); // vertices.put(y); // //0,1 // vertices.put(x); // vertices.put(heightfield[x][y+stride]); // vertices.put(y + stride); // //1,1 // vertices.put(x + stride); // vertices.put(heightfield[x+stride][y+stride]); // vertices.put(y + stride); // //deal with normal // Vector3f normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x, y); // normals.put(normal.x); // normals.put(normal.y); // normals.put(normal.z); // normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x + stride, y); // normals.put(normal.x); // normals.put(normal.y); // normals.put(normal.z); // normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x, y + stride); // normals.put(normal.x); // normals.put(normal.y); // normals.put(normal.z); // normal = calculateTerrainNormal(heightfield, actualWidth, actualHeight, stride, x + stride, y + stride); // normals.put(normal.x); // normals.put(normal.y); // normals.put(normal.z); // //deal with texture coordinates //// if(x / stride % 2 == 0){ //// if(y / stride % 2 == 0){ //// texture_coords.put(0); //// texture_coords.put(0); //// texture_coords.put(1); //// texture_coords.put(0); //// texture_coords.put(0); //// texture_coords.put(1); //// texture_coords.put(1); //// texture_coords.put(1); //// } else { //// texture_coords.put(0); //// texture_coords.put(1); //// } //// } else { //// if(y / stride % 2 == 0){ //// texture_coords.put(1); //// texture_coords.put(0); //// } else { //// texture_coords.put(1); //// texture_coords.put(1); //// } //// } // texture_coords.put(0); // texture_coords.put(0); // texture_coords.put(1); // texture_coords.put(0); // texture_coords.put(0); // texture_coords.put(1); // texture_coords.put(1); // texture_coords.put(1); // // if(x + stride < width - 1 && y + stride < height - 1){ //// texturemap[x+stride][y+stride]; // for(int i = 0; i < 4 ; i++){ //// textureIndices.put(1); //// textureIndices.put(0); //// textureIndices.put(0); //// textureIndices.put(0); // textureIndices.put(texturemap[x][y]); // textureIndices.put(texturemap[x+stride][y]); // textureIndices.put(texturemap[x][y+stride]); // textureIndices.put(texturemap[x+stride][y+stride]); // } // } else { // for(int i = 0; i < 4 ; i++){ // textureIndices.put(0); // textureIndices.put(0); // textureIndices.put(0); // textureIndices.put(0); // } // } // // // //deal with faces // if(1.0f * x / stride < actualWidth - 1 && 1.0f * y / stride < actualHeight - 1){ // faces.put(incrementer * 4 + 0); // faces.put(incrementer * 4 + 1); // faces.put(incrementer * 4 + 2); // faces.put(incrementer * 4 + 1); // faces.put(incrementer * 4 + 2); // faces.put(incrementer * 4 + 3); // } // incrementer++; // } // } // System.out.println(incrementer + " quads"); vertices.flip(); normals.flip(); faces.flip(); texture_coords.flip(); textureIndices.flip(); m.generateVAO(); //buffer vertices m.bufferVertices(vertices, 3); //buffer normals m.bufferNormals(normals, 3); //buffer faces m.bufferFaces(faces,incrementer*2); //buffer texture coords m.bufferTextureCoords(texture_coords, 2); //texture indices m.bufferCustomFloatAttribArray(textureIndices, 4, 5); m.setShader(program); GL40.glBindVertexArray(0); m.setParent(rVal); Material groundMat = new Material(); Globals.assetManager.addTexturePathtoQueue("/Textures/Ground/Dirt1.png"); groundMat.setDiffuse("/Textures/Ground/Dirt1.png"); groundMat.setSpecular("/Textures/Ground/Dirt1.png"); m.setMaterial(groundMat); rVal.getMeshes().add(m); return rVal; } static Vector3f calculateTerrainNormal(float[][] heightfield, int actualWidth, int actualHeight, int stride, int x, int y){ Vector3f rVal = new Vector3f(); if(x / stride < actualWidth - 1){ if(y / stride < actualHeight - 1){ float hL; if(x > 0){ hL = heightfield[x-1][y]; } else { hL = heightfield[x][y]; } float hR = heightfield[x+1][y]; float hD = heightfield[x][y+1]; float hU; if(y > 0){ hU = heightfield[x][y-1]; } else { hU = heightfield[x][y]; } rVal = new Vector3f(hL - hR, 2.0f, hD - hU); rVal.normalize(); } else { float hL; if(x > 0){ hL = heightfield[x-1][y]; } else { hL = heightfield[x][y]; } float hR = heightfield[x+1][y]; float hD = heightfield[x][y]; float hU = heightfield[x][y-1]; rVal = new Vector3f(hL - hR, 2.0f, hD - hU); rVal.normalize(); } } else { if(y / stride < actualHeight - 1){ float hL = heightfield[x-1][y]; float hR = heightfield[x][y]; float hD = heightfield[x][y+1]; float hU; if(y > 0){ hU = heightfield[x][y-1]; } else { hU = heightfield[x][y]; } rVal = new Vector3f(hL - hR, 2.0f, hD - hU); rVal.normalize(); } else { float hL = heightfield[x-1][y]; float hR = heightfield[x][y]; float hD = heightfield[x][y]; float hU = heightfield[x][y-1]; rVal = new Vector3f(hL - hR, 2.0f, hD - hU); rVal.normalize(); } } return rVal; } public static ActorTextureMask generateVolumetricTextureMask(String meshName){ List textureList = new LinkedList(); textureList.add(Globals.renderingEngine.getVolumeFrontfaceTexture()); textureList.add(Globals.renderingEngine.getVolumeBackfaceTexture()); List uniformList = new LinkedList(); uniformList.add("volumeDepthFrontface"); uniformList.add("volumeDepthBackface"); return new ActorTextureMask(meshName,textureList,uniformList); } }