From dda5cb59e571244430ef68796fbeabb3d26fa250 Mon Sep 17 00:00:00 2001 From: austin Date: Sun, 21 Nov 2021 00:13:25 -0500 Subject: [PATCH] Work on minimizing terrain poly count --- .../game/client/cells/DrawCell.java | 2 +- .../electrosphere/renderer/ModelUtils.java | 488 ++++++++++++++++++ 2 files changed, 489 insertions(+), 1 deletion(-) diff --git a/src/main/java/electrosphere/game/client/cells/DrawCell.java b/src/main/java/electrosphere/game/client/cells/DrawCell.java index 8c030a1b..4880d906 100644 --- a/src/main/java/electrosphere/game/client/cells/DrawCell.java +++ b/src/main/java/electrosphere/game/client/cells/DrawCell.java @@ -93,7 +93,7 @@ public class DrawCell { if(modelEntity != null){ Globals.entityManager.deregisterEntity(modelEntity); } - Model terrainModel = ModelUtils.createTerrainModelPrecomputedShader(heightmap, texturemap, program, stride); + Model terrainModel = ModelUtils.createMinimizedTerrainModelPrecomputedShader(heightmap, texturemap, program, stride); Mesh terrainMesh = terrainModel.meshes.get(0); terrainMesh.useTextureList = true; terrainMesh.textureList.add(groundTextureOne); diff --git a/src/main/java/electrosphere/renderer/ModelUtils.java b/src/main/java/electrosphere/renderer/ModelUtils.java index 07e74233..06318daf 100644 --- a/src/main/java/electrosphere/renderer/ModelUtils.java +++ b/src/main/java/electrosphere/renderer/ModelUtils.java @@ -6,6 +6,8 @@ import electrosphere.renderer.texture.Texture; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import org.joml.Vector3f; import org.lwjgl.BufferUtils; import static org.lwjgl.opengl.GL30.glBindVertexArray; @@ -340,6 +342,492 @@ public class ModelUtils { return rVal; } + + static float MINIMIZATION_DIFF_MAX = 0.1f; + + public static Model createMinimizedTerrainModelPrecomputedShader(float[][] heightfield, float[][] texturemap, ShaderProgram program, int stride){ + + class QuadToGenerate { + //coords are inclusive + int startX; + int endX; + int startY; + int endY; + float diff; + 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.diff = diff; + this.min = min; + this.max = max; + this.texture = texture; + this.homogeneousTexture = homogeneousTexture; + } + + } + + + Model rVal = new Model(); + rVal.meshes = new ArrayList(); + Mesh m = new Mesh(); + 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; + int textureVal = -1; + for(int x = 0; x < width - 1; x = x + stride){ + quadCurrent = null; + for(int y = 0; y < height - 1; y = y + stride){ + if(quadCurrent == null){ + minVal = 100000000; + maxVal = 0; + textureVal = -1; + //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 && + texturemap[quadCurrent.startX][quadCurrent.startY] == texturemap[x+stride][y] && + texturemap[quadCurrent.startX][quadCurrent.startY] == texturemap[x][y+stride] && + texturemap[quadCurrent.startX][quadCurrent.startY] == texturemap[x+stride][y+stride] + ){ + //add to quad + quadCurrent.endY = y + stride; + quadCurrent.diff = newMax - newMin; + quadCurrent.min = newMax; + quadCurrent.max = newMax; + } else { + //push quad + firstPhaseQuads.add(quadCurrent); + quadCurrent = null; +// System.out.println("Push"); + } + } else { + if(newMax - newMin < MINIMIZATION_DIFF_MAX){ + //add to quad + quadCurrent.endY = y + stride; + quadCurrent.diff = newMax - newMin; + quadCurrent.min = newMax; + quadCurrent.max = newMax; + } else { + //push quad + firstPhaseQuads.add(quadCurrent); + quadCurrent = null; +// 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; +// for(QuadToGenerate currentIteration : firstPhaseQuads){ +// if(currentQuad == null){ +// currentQuad = currentIteration; +// } else { +// 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; +// } +// } +// } +// } +// finalQuads.add(currentQuad); + + for(QuadToGenerate current : finalQuads){ +// System.out.println(current.startX + " " + current.startY + " " + current.endX + " " + current.endY); + } + +// System.out.println(finalQuads.size()); +// System.exit(0); + + 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++; +// } +// } + + vertices.flip(); + normals.flip(); + faces.flip(); + texture_coords.flip(); + textureIndices.flip(); + + m.vertexArrayObject = glGenVertexArrays(); + glBindVertexArray(m.vertexArrayObject); + //buffer vertices + m.buffer_vertices(vertices, 3); + //buffer normals + m.buffer_normals(normals, 3); + //buffer faces + m.buffer_faces(faces); + //buffer texture coords + m.buffer_texture_coords(texture_coords, 2); + //texture indices + m.bufferCustomFloatAttribArray(textureIndices, 4, 5); + m.shader = program; + glBindVertexArray(0); + m.parent = rVal; + + m.hasBones = false; + + Material groundMat = new Material(); + Globals.assetManager.addTexturePathtoQueue("/Textures/Ground/Dirt1.png"); + groundMat.set_diffuse("/Textures/Ground/Dirt1.png"); + groundMat.set_specular("/Textures/Ground/Dirt1.png"); + m.setMaterial(groundMat); + + rVal.meshes.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){