diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 13597fd8..d819cf84 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1082,6 +1082,7 @@ Fix draw cells not deleting once all children have reported generation Fix server data cells unloading before ready state Fix terrain chunk generation trying to generate rigid body for no-vertex cell Fix server homogenous chunk check on generation with variadic weights +Optimize data passing from voxel rasterizer to model generation diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 246f463f..15b2db12 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -25,7 +25,7 @@ import electrosphere.entity.Entity; import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.TerrainMessage; -import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; +import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.model.Model; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainManager; @@ -298,7 +298,7 @@ public class ClientTerrainManager { Globals.profiler.beginCpuSample("ClientTerrainManager.generateTerrainChunkGeometry"); lock.acquireUninterruptibly(); for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){ - Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas()); + Model terrainModel = TransvoxelModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas()); Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash()); if(queueItem.notifyTarget != null){ queueItem.notifyTarget.alertToGeneration(); diff --git a/src/main/java/electrosphere/collision/CollisionBodyCreation.java b/src/main/java/electrosphere/collision/CollisionBodyCreation.java index 2ba9b751..9270a9c1 100644 --- a/src/main/java/electrosphere/collision/CollisionBodyCreation.java +++ b/src/main/java/electrosphere/collision/CollisionBodyCreation.java @@ -258,30 +258,10 @@ public class CollisionBodyCreation { */ public static DBody generateBodyFromTerrainData(CollisionEngine collisionEngine, TerrainChunkData data, long categoryBits){ DBody body = null; - - int elementCount = data.getFaceElements().size(); - - float[] vertices = new float[elementCount * 3]; - int vertexInserterPos = 0; - int[] indices = new int[elementCount]; - int indexInserterPos = 0; - - for(int element : data.getFaceElements()){ - //add a new vertex - vertices[vertexInserterPos*3+0] = data.getVertices().get(element*3+0); - vertices[vertexInserterPos*3+1] = data.getVertices().get(element*3+1); - vertices[vertexInserterPos*3+2] = data.getVertices().get(element*3+2); - vertexInserterPos = vertexInserterPos + 1; - - //push faces -- instead of pushing the element directly, instead use the incrementer because we want to draw a new vertex - //there should be no vertex-sharing between triangles. This keeps the meshes from blurring texture/lighting - indices[indexInserterPos] = indexInserterPos; - indexInserterPos++; - } //create trimesh - if(vertices.length > 0){ - DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices,categoryBits); + if(data.getFaceElements().length > 0){ + DTriMesh triMesh = collisionEngine.createTrimeshGeom(data.getVertices(),data.getFaceElements(),categoryBits); body = collisionEngine.createDBody(triMesh); collisionEngine.setKinematic(body); collisionEngine.setGravityMode(body, false); diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java index b4334f3c..fd63442e 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -20,7 +20,6 @@ import electrosphere.entity.EntityUtils; import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.types.collision.CollisionObjUtils; import electrosphere.logger.LoggerInterface; -import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; import electrosphere.server.datacell.Realm; @@ -65,7 +64,7 @@ public class TerrainChunk { if(Globals.clientScene.containsEntity(rVal)){ String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas, notifyTarget, toDelete); EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); - if(levelOfDetail == ClientDrawCellManager.FULL_RES_LOD && data.faceElements.size() > 0){ + if(levelOfDetail == ClientDrawCellManager.FULL_RES_LOD && data.faceElements.length > 0){ PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); CollisionObjUtils.clientPositionCharacter(rVal, new Vector3d(EntityUtils.getPosition(rVal)), new Quaterniond()); } else { @@ -112,10 +111,11 @@ public class TerrainChunk { */ public static Entity serverCreateTerrainChunkEntity(Realm realm, Vector3d position, float[][][] weights, int[][][] values){ - TerrainChunkData data = TerrainChunkModelGeneration.generateTerrainChunkData(weights, values); + TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, values, ClientDrawCellManager.FULL_RES_LOD); + TerrainChunkData data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); Entity rVal = EntityCreationUtils.createServerEntity(realm, position); - if(data.vertices.size() > 0){ + if(data.vertices.length > 0){ PhysicsEntityUtils.serverAttachTerrainChunkRigidBody(rVal, data); rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); // ServerEntityUtils.initiallyPositionEntity(realm, rVal, position); diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java index b9e76c7c..cbbff820 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunkData.java @@ -2,7 +2,6 @@ package electrosphere.entity.types.terrain; import java.nio.FloatBuffer; import java.nio.IntBuffer; -import java.util.List; import org.lwjgl.BufferUtils; @@ -12,17 +11,17 @@ import org.lwjgl.BufferUtils; public class TerrainChunkData { //the verts - List vertices; + float[] vertices; //normals - List normals; + float[] normals; //faces - List faceElements; + int[] faceElements; //UVs - List uvs; + float[] uvs; //texture samplers - List textureSamplers; //what textures in the atlas to sample + float[] textureSamplers; //what textures in the atlas to sample //texture ratio vector - List textureRatioVectors; //HOW MUCH of each texture in the atlas to sample + float[] textureRatioVectors; //HOW MUCH of each texture in the atlas to sample /** * The various buffers of data to send to the gpu @@ -49,7 +48,7 @@ public class TerrainChunkData { * @param textureSamplers * @param lod The LOD of the model */ - public TerrainChunkData(List vertices, List normals, List faceElements, List uvs, List textureSamplers, List textureRatioVectors, int lod){ + public TerrainChunkData(float[] vertices, float[] normals, int[] faceElements, float[] uvs, float[] textureSamplers, float[] textureRatioVectors, int lod){ this.vertices = vertices; this.normals = normals; this.faceElements = faceElements; @@ -63,50 +62,30 @@ public class TerrainChunkData { * Allocates and fills the buffers to send to the gpu */ public void constructBuffers(){ - int elementCount = this.getFaceElements().size(); - vertexArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3); - normalArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 3); - textureArrayBufferData = BufferUtils.createFloatBuffer(elementCount * 2); - elementArrayBufferData = BufferUtils.createIntBuffer(elementCount); - int incrementer = 0; - for(int element : this.getFaceElements()){ - //for each element, need to push vert, normal, etc + vertexArrayBufferData = BufferUtils.createFloatBuffer(vertices.length); + vertexArrayBufferData.put(vertices); - //push current vertex - vertexArrayBufferData.put(this.getVertices().get(element*3+0)); - vertexArrayBufferData.put(this.getVertices().get(element*3+1)); - vertexArrayBufferData.put(this.getVertices().get(element*3+2)); + normalArrayBufferData = BufferUtils.createFloatBuffer(normals.length); + normalArrayBufferData.put(normals); - //push current normals - normalArrayBufferData.put(this.getNormals().get(element*3+0)); - normalArrayBufferData.put(this.getNormals().get(element*3+1)); - normalArrayBufferData.put(this.getNormals().get(element*3+2)); + textureArrayBufferData = BufferUtils.createFloatBuffer(uvs.length); + textureArrayBufferData.put(uvs); - //push current uvs - textureArrayBufferData.put(this.getUVs().get(element*2+0)); - textureArrayBufferData.put(this.getUVs().get(element*2+1)); + elementArrayBufferData = BufferUtils.createIntBuffer(faceElements.length); + elementArrayBufferData.put(faceElements); - //push faces -- instead of pushing the element directly, instead use the incrementer because we want to draw a new vertex - //there should be no vertex-sharing between triangles. This keeps the meshes from blurring texture/lighting - elementArrayBufferData.put(incrementer); - incrementer++; - } - int vertexCount = this.getTextureSamplers().size() / 3; - samplerBuffer = BufferUtils.createFloatBuffer(vertexCount * 3); - for(float samplerVec : this.getTextureSamplers()){ - samplerBuffer.put(samplerVec); - } - ratioBuffer = BufferUtils.createFloatBuffer(vertexCount * 3); - for(float ratioVec : this.getTextureRatioVectors()){ - ratioBuffer.put(ratioVec); - } + samplerBuffer = BufferUtils.createFloatBuffer(textureSamplers.length); + samplerBuffer.put(this.textureSamplers); + + ratioBuffer = BufferUtils.createFloatBuffer(textureRatioVectors.length); + ratioBuffer.put(this.textureRatioVectors); } /** * Gets the vertex data * @return the vertex data */ - public List getVertices(){ + public float[] getVertices(){ return vertices; } @@ -114,7 +93,7 @@ public class TerrainChunkData { * Gets the normal data * @return the normal data */ - public List getNormals(){ + public float[] getNormals(){ return normals; } @@ -122,7 +101,7 @@ public class TerrainChunkData { * Gets the face element data * @return the face element data */ - public List getFaceElements(){ + public int[] getFaceElements(){ return faceElements; } @@ -130,7 +109,7 @@ public class TerrainChunkData { * Gets the uv data * @return the uv data */ - public List getUVs(){ + public float[] getUVs(){ return uvs; } @@ -138,7 +117,7 @@ public class TerrainChunkData { * Gets the texture sampler data * @return the texture sampler data */ - public List getTextureSamplers(){ + public float[] getTextureSamplers(){ return textureSamplers; } @@ -146,7 +125,7 @@ public class TerrainChunkData { * Gets the texture ratio vector data * @return the texture ratio vector data */ - public List getTextureRatioVectors(){ + public float[] getTextureRatioVectors(){ return textureRatioVectors; } diff --git a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java index dfab3546..863ad70b 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java @@ -2,8 +2,6 @@ package electrosphere.renderer.meshgen; import java.nio.FloatBuffer; import java.nio.IntBuffer; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -624,147 +622,147 @@ public class TerrainChunkModelGeneration { return new Vector3f(x,y,z); } - public static TerrainChunkData generateTerrainChunkData(float[][][] terrainGrid, int[][][] textureGrid){ + // public static TerrainChunkData generateTerrainChunkData(float[][][] terrainGrid, int[][][] textureGrid){ - // 5 6 - // +-------------+ +-----5-------+ ^ Y - // / | / | / | /| | _ - // / | / | 4 9 6 10 | /\ Z - // 4 +-----+-------+ 7 | +-----+7------+ | | / - // | 1 +-------+-----+ 2 | +-----1-+-----+ | / - // | / | / 8 0 11 2 | / - // | / | / | / | / |/ - // 0 +-------------+ 3 +------3------+ +---------------> X + // // 5 6 + // // +-------------+ +-----5-------+ ^ Y + // // / | / | / | /| | _ + // // / | / | 4 9 6 10 | /\ Z + // // 4 +-----+-------+ 7 | +-----+7------+ | | / + // // | 1 +-------+-----+ 2 | +-----1-+-----+ | / + // // | / | / 8 0 11 2 | / + // // | / | / | / | / |/ + // // 0 +-------------+ 3 +------3------+ +---------------> X - //the current grid cell - GridCell currentCell = new GridCell(); - //the list of all triangles - List triangles = new LinkedList(); - //the map of vertex to index - Map vertMap = new HashMap(); - //the list of all verts - List verts = new LinkedList(); - //the list of all normals - List normals = new LinkedList(); - //the list of number of triangles that share a vert - List trianglesSharingVert = new LinkedList(); - //List of texture sampler values - List samplerTriangles = new LinkedList(); - //List of UVs - List UVs = new LinkedList(); + // //the current grid cell + // GridCell currentCell = new GridCell(); + // //the list of all triangles + // List triangles = new LinkedList(); + // //the map of vertex to index + // Map vertMap = new HashMap(); + // //the list of all verts + // List verts = new LinkedList(); + // //the list of all normals + // List normals = new LinkedList(); + // //the list of number of triangles that share a vert + // List trianglesSharingVert = new LinkedList(); + // //List of texture sampler values + // List samplerTriangles = new LinkedList(); + // //List of UVs + // List UVs = new LinkedList(); - for(int x = 0; x < terrainGrid.length - 1; x++){ - for(int y = 0; y < terrainGrid[0].length - 1; y++){ - for(int z = 0; z < terrainGrid[0][0].length - 1; z++){ - //push the current cell's values into the gridcell - currentCell.setValues( - new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+0), - new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0), - terrainGrid[x+0][y+0][z+0], terrainGrid[x+0][y+0][z+1], terrainGrid[x+1][y+0][z+1], terrainGrid[x+1][y+0][z+0], - terrainGrid[x+0][y+1][z+0], terrainGrid[x+0][y+1][z+1], terrainGrid[x+1][y+1][z+1], terrainGrid[x+1][y+1][z+0], - textureGrid[x+0][y+0][z+0], textureGrid[x+0][y+0][z+1], textureGrid[x+1][y+0][z+1], textureGrid[x+1][y+0][z+0], - textureGrid[x+0][y+1][z+0], textureGrid[x+0][y+1][z+1], textureGrid[x+1][y+1][z+1], textureGrid[x+1][y+1][z+0] - ); - //polygonize the current gridcell - polygonize(currentCell, MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); - } - } - } + // for(int x = 0; x < terrainGrid.length - 1; x++){ + // for(int y = 0; y < terrainGrid[0].length - 1; y++){ + // for(int z = 0; z < terrainGrid[0][0].length - 1; z++){ + // //push the current cell's values into the gridcell + // currentCell.setValues( + // new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+0), + // new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0), + // terrainGrid[x+0][y+0][z+0], terrainGrid[x+0][y+0][z+1], terrainGrid[x+1][y+0][z+1], terrainGrid[x+1][y+0][z+0], + // terrainGrid[x+0][y+1][z+0], terrainGrid[x+0][y+1][z+1], terrainGrid[x+1][y+1][z+1], terrainGrid[x+1][y+1][z+0], + // textureGrid[x+0][y+0][z+0], textureGrid[x+0][y+0][z+1], textureGrid[x+1][y+0][z+1], textureGrid[x+1][y+0][z+0], + // textureGrid[x+0][y+1][z+0], textureGrid[x+0][y+1][z+1], textureGrid[x+1][y+1][z+1], textureGrid[x+1][y+1][z+0] + // ); + // //polygonize the current gridcell + // polygonize(currentCell, MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + // } + // } + // } - //all verts in order, flattened as an array of floats instead of vecs - List vertsFlat = new LinkedList(); - //all normals in order, flattened as an array of floats instead of vecs - List normalsFlat = new LinkedList(); - //all elements of faces in order - LinkedList elementsFlat = new LinkedList(); - //List of texture sampler values - List textureSamplers = new LinkedList(); - //List of texture ratio values - List textureRatioData = new LinkedList(); + // //all verts in order, flattened as an array of floats instead of vecs + // List vertsFlat = new LinkedList(); + // //all normals in order, flattened as an array of floats instead of vecs + // List normalsFlat = new LinkedList(); + // //all elements of faces in order + // LinkedList elementsFlat = new LinkedList(); + // //List of texture sampler values + // List textureSamplers = new LinkedList(); + // //List of texture ratio values + // List textureRatioData = new LinkedList(); - //flatten verts + normals - for(Vector3f vert : verts){ - vertsFlat.add(vert.x); - vertsFlat.add(vert.y); - vertsFlat.add(vert.z); - } + // //flatten verts + normals + // for(Vector3f vert : verts){ + // vertsFlat.add(vert.x); + // vertsFlat.add(vert.y); + // vertsFlat.add(vert.z); + // } - for(Vector3f normal : normals){ - normalsFlat.add(normal.x); - normalsFlat.add(normal.y); - normalsFlat.add(normal.z); - } + // for(Vector3f normal : normals){ + // normalsFlat.add(normal.x); + // normalsFlat.add(normal.y); + // normalsFlat.add(normal.z); + // } - for(Triangle triangle : triangles){ - elementsFlat.add(triangle.indices[0]); - elementsFlat.add(triangle.indices[1]); - elementsFlat.add(triangle.indices[2]); - } + // for(Triangle triangle : triangles){ + // elementsFlat.add(triangle.indices[0]); + // elementsFlat.add(triangle.indices[1]); + // elementsFlat.add(triangle.indices[2]); + // } - float[] temp = new float[3]; - int i = 0; - for(Vector3f normal : normals){ - Vector3f vert = verts.get(i); + // float[] temp = new float[3]; + // int i = 0; + // for(Vector3f normal : normals){ + // Vector3f vert = verts.get(i); - float absX = Math.abs(normal.x); - float absY = Math.abs(normal.y); - float absZ = Math.abs(normal.z); + // float absX = Math.abs(normal.x); + // float absY = Math.abs(normal.y); + // float absZ = Math.abs(normal.z); - float uvX = vert.z * absX + vert.x * absY + vert.x * absZ; - float uvY = vert.y * absX + vert.z * absY + vert.y * absZ; - temp[0] = uvX; - temp[1] = uvY; + // float uvX = vert.z * absX + vert.x * absY + vert.x * absZ; + // float uvY = vert.y * absX + vert.z * absY + vert.y * absZ; + // temp[0] = uvX; + // temp[1] = uvY; - // if(absX >= absZ && absX >= absY){ - // temp[0] = normal.z / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); - // temp[1] = normal.y / 2.0f + 0.5f + vert.x * (absY / (absX + absY)) + vert.y * (absX / (absX + absY)); - // } else if(absZ >= absX && absZ >= absY){ - // temp[0] = normal.x / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); - // temp[1] = normal.y / 2.0f + 0.5f + vert.z * (absY / (absZ + absY)) + vert.y * (absZ / (absZ + absY)); - // } else if(absY >= absX && absY >= absZ){ - // temp[0] = normal.x / 2.0f + 0.5f + vert.y * (absX / (absX + absY)) + vert.x * (absY / (absX + absY)); - // temp[1] = normal.z / 2.0f + 0.5f + vert.y * (absZ / (absZ + absY)) + vert.z * (absY / (absZ + absY)); - // } else { - // temp[0] = vert.x / 1.5f + vert.z / 1.5f; - // temp[1] = vert.y / 1.5f + vert.z / 1.5f; - // } - i++; - UVs.add(temp[0]); - UVs.add(temp[1]); - } + // // if(absX >= absZ && absX >= absY){ + // // temp[0] = normal.z / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); + // // temp[1] = normal.y / 2.0f + 0.5f + vert.x * (absY / (absX + absY)) + vert.y * (absX / (absX + absY)); + // // } else if(absZ >= absX && absZ >= absY){ + // // temp[0] = normal.x / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); + // // temp[1] = normal.y / 2.0f + 0.5f + vert.z * (absY / (absZ + absY)) + vert.y * (absZ / (absZ + absY)); + // // } else if(absY >= absX && absY >= absZ){ + // // temp[0] = normal.x / 2.0f + 0.5f + vert.y * (absX / (absX + absY)) + vert.x * (absY / (absX + absY)); + // // temp[1] = normal.z / 2.0f + 0.5f + vert.y * (absZ / (absZ + absY)) + vert.z * (absY / (absZ + absY)); + // // } else { + // // temp[0] = vert.x / 1.5f + vert.z / 1.5f; + // // temp[1] = vert.y / 1.5f + vert.z / 1.5f; + // // } + // i++; + // UVs.add(temp[0]); + // UVs.add(temp[1]); + // } - //flatten sampler indices - for(Vector3f samplerVec : samplerTriangles){ - textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.x)); - textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.y)); - textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.z)); - } + // //flatten sampler indices + // for(Vector3f samplerVec : samplerTriangles){ + // textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.x)); + // textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.y)); + // textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.z)); + // } - //set ratio dat - for(int j = 0; j < triangles.size(); j++){ - //first vertex - textureRatioData.add(1.0f); - textureRatioData.add(0.0f); - textureRatioData.add(0.0f); + // //set ratio dat + // for(int j = 0; j < triangles.size(); j++){ + // //first vertex + // textureRatioData.add(1.0f); + // textureRatioData.add(0.0f); + // textureRatioData.add(0.0f); - //second vertex - textureRatioData.add(0.0f); - textureRatioData.add(1.0f); - textureRatioData.add(0.0f); + // //second vertex + // textureRatioData.add(0.0f); + // textureRatioData.add(1.0f); + // textureRatioData.add(0.0f); - //third vertex - textureRatioData.add(0.0f); - textureRatioData.add(0.0f); - textureRatioData.add(1.0f); - } + // //third vertex + // textureRatioData.add(0.0f); + // textureRatioData.add(0.0f); + // textureRatioData.add(1.0f); + // } - //List vertices, List normals, List faceElements, List uvs - TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData, 0); - return rVal; - } + // //List vertices, List normals, List faceElements, List uvs + // TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData, 0); + // return rVal; + // } /** * Generates a mesh based on a terrainchunkdata object @@ -797,7 +795,7 @@ public class TerrainChunkModelGeneration { // // Buffer data to GPU // - int elementCount = data.getFaceElements().size(); + int elementCount = data.getFaceElements().length; try { //actually buffer vertices @@ -883,6 +881,7 @@ public class TerrainChunkModelGeneration { * @param atlas The atlas texture for the chunk * @return The model */ + @Deprecated public static Model generateTerrainModel(TerrainChunkData data, VoxelTextureAtlas atlas){ Model rVal = new Model(); Mesh m = TerrainChunkModelGeneration.generateTerrainMesh(data); diff --git a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java index 65356f42..973843e3 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java @@ -1,15 +1,22 @@ package electrosphere.renderer.meshgen; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.joml.Vector3f; +import org.lwjgl.opengl.GL40; +import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.engine.Globals; import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.logger.LoggerInterface; +import electrosphere.renderer.model.Material; +import electrosphere.renderer.model.Mesh; +import electrosphere.renderer.model.Model; import electrosphere.server.terrain.manager.ServerTerrainChunk; /** @@ -17,6 +24,41 @@ import electrosphere.server.terrain.manager.ServerTerrainChunk; */ public class TransvoxelModelGeneration { + /** + * Number of ints required to store an element + */ + static final int INTS_PER_ELEMENT = 1; + + /** + * Number of elements per triangle + */ + static final int ELEMENTS_PER_TRIANGLE = 3; + + + /** + * Number of floats per vert + */ + static final int FLOATS_PER_VERT = 3; + + /** + * Number of vertices per triangle + */ + static final int VERTS_PER_TRIANGLE = ELEMENTS_PER_TRIANGLE; + + /** + * Number of floats per UV + */ + static final int FLOATS_PER_UV = 2; + + /** + * The number of sampler indices per triangle + */ + static final int SAMPLER_INDICES_PER_TRIANGLE = 3; + + /** + * The number of sampler values to store for each vertex + */ + static final int SAMPLER_VALUES_PER_VERT = 3; //this is the width of the transition cell @@ -1045,8 +1087,6 @@ public class TransvoxelModelGeneration { List trianglesSharingVert = new LinkedList(); //List of texture sampler values List samplerTriangles = new LinkedList(); - //List of UVs - List UVs = new LinkedList(); @@ -1601,96 +1641,123 @@ public class TransvoxelModelGeneration { } + if (verts.size() != normals.size()){ + throw new Error("Generated invalid number of elements in lists! " + verts.size() + " " + normals.size()); + } + + + //incrementer + int i = 0; - //all verts in order, flattened as an array of floats instead of vecs - List vertsFlat = new LinkedList(); - //all normals in order, flattened as an array of floats instead of vecs - List normalsFlat = new LinkedList(); //all elements of faces in order - List elementsFlat = new LinkedList(); + int[] elementsFlat = new int[triangles.size() * ELEMENTS_PER_TRIANGLE * INTS_PER_ELEMENT]; + //all verts in order, flattened as an array of floats instead of vecs + float[] vertsFlat = new float[triangles.size() * VERTS_PER_TRIANGLE * FLOATS_PER_VERT]; + //all normals in order, flattened as an array of floats instead of vecs + float[] normalsFlat = new float[triangles.size() * VERTS_PER_TRIANGLE * FLOATS_PER_VERT]; + //List of UVs + float[] UVs = new float[triangles.size() * VERTS_PER_TRIANGLE * FLOATS_PER_UV]; //List of texture sampler values - List textureSamplers = new LinkedList(); + float[] textureSamplers = new float[samplerTriangles.size() * SAMPLER_INDICES_PER_TRIANGLE]; //List of texture ratio values - List textureRatioData = new LinkedList(); + float[] textureRatioData = new float[triangles.size() * SAMPLER_INDICES_PER_TRIANGLE * SAMPLER_VALUES_PER_VERT]; float scalingFactor = (float)Math.pow(2,chunkData.levelOfDetail); - //flatten verts + normals - for(Vector3f vert : verts){ - vertsFlat.add(vert.x * scalingFactor); - vertsFlat.add(vert.y * scalingFactor); - vertsFlat.add(vert.z * scalingFactor); + + //store indices + for(int j = 0; j < elementsFlat.length; j++){ + //assigning a unique number to each element guarantees there's no vertex sharing between triangles + //we don't want vertex sharing because then the UVs are shared + elementsFlat[j] = j; } - for(Vector3f normal : normals){ - normalsFlat.add(normal.x); - normalsFlat.add(normal.y); - normalsFlat.add(normal.z); - } - - + //flatten verts + normals + uvs + i = 0; + Vector3f vert = null; + Vector3f normal = null; for(Triangle triangle : triangles){ - elementsFlat.add(triangle.indices[0]); - elementsFlat.add(triangle.indices[1]); - elementsFlat.add(triangle.indices[2]); - } - float[] temp = new float[3]; - int i = 0; - for(Vector3f normal : normals){ - Vector3f vert = verts.get(i); + // + //point 1 of the triangle + vert = verts.get(triangle.indices[0]); + normal = normals.get(triangle.indices[0]); + vertsFlat[i*9+0] = vert.x * scalingFactor; + vertsFlat[i*9+1] = vert.y * scalingFactor; + vertsFlat[i*9+2] = vert.z * scalingFactor; - float absX = Math.abs(normal.x); - float absY = Math.abs(normal.y); - float absZ = Math.abs(normal.z); + normalsFlat[i*9+0] = normal.x; + normalsFlat[i*9+1] = normal.y; + normalsFlat[i*9+2] = normal.z; - float uvX = vert.z * absX + vert.x * absY + vert.x * absZ; - float uvY = vert.y * absX + vert.z * absY + vert.y * absZ; - temp[0] = uvX; - temp[1] = uvY; + UVs[i*6+0] = vert.z * Math.abs(normal.x) + vert.x * Math.abs(normal.y) + vert.x * Math.abs(normal.z); + UVs[i*6+1] = vert.y * Math.abs(normal.x) + vert.z * Math.abs(normal.y) + vert.y * Math.abs(normal.z); - // if(absX >= absZ && absX >= absY){ - // temp[0] = normal.z / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); - // temp[1] = normal.y / 2.0f + 0.5f + vert.x * (absY / (absX + absY)) + vert.y * (absX / (absX + absY)); - // } else if(absZ >= absX && absZ >= absY){ - // temp[0] = normal.x / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); - // temp[1] = normal.y / 2.0f + 0.5f + vert.z * (absY / (absZ + absY)) + vert.y * (absZ / (absZ + absY)); - // } else if(absY >= absX && absY >= absZ){ - // temp[0] = normal.x / 2.0f + 0.5f + vert.y * (absX / (absX + absY)) + vert.x * (absY / (absX + absY)); - // temp[1] = normal.z / 2.0f + 0.5f + vert.y * (absZ / (absZ + absY)) + vert.z * (absY / (absZ + absY)); - // } else { - // temp[0] = vert.x / 1.5f + vert.z / 1.5f; - // temp[1] = vert.y / 1.5f + vert.z / 1.5f; - // } + + // + //point 2 of the triangle + vert = verts.get(triangle.indices[1]); + normal = normals.get(triangle.indices[1]); + vertsFlat[i*9+3] = vert.x * scalingFactor; + vertsFlat[i*9+4] = vert.y * scalingFactor; + vertsFlat[i*9+5] = vert.z * scalingFactor; + + normalsFlat[i*9+3] = normal.x; + normalsFlat[i*9+4] = normal.y; + normalsFlat[i*9+5] = normal.z; + + UVs[i*6+2] = vert.z * Math.abs(normal.x) + vert.x * Math.abs(normal.y) + vert.x * Math.abs(normal.z); + UVs[i*6+3] = vert.y * Math.abs(normal.x) + vert.z * Math.abs(normal.y) + vert.y * Math.abs(normal.z); + + + // + //point 3 of the triangle + vert = verts.get(triangle.indices[2]); + normal = normals.get(triangle.indices[2]); + vertsFlat[i*9+6] = vert.x * scalingFactor; + vertsFlat[i*9+7] = vert.y * scalingFactor; + vertsFlat[i*9+8] = vert.z * scalingFactor; + + normalsFlat[i*9+6] = normal.x; + normalsFlat[i*9+7] = normal.y; + normalsFlat[i*9+8] = normal.z; + + UVs[i*6+4] = vert.z * Math.abs(normal.x) + vert.x * Math.abs(normal.y) + vert.x * Math.abs(normal.z); + UVs[i*6+5] = vert.y * Math.abs(normal.x) + vert.z * Math.abs(normal.y) + vert.y * Math.abs(normal.z); + + i++; - UVs.add(temp[0]); - UVs.add(temp[1]); } //flatten sampler indices + i = 0; for(Vector3f samplerVec : samplerTriangles){ - textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.x)); - textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.y)); - textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.z)); + textureSamplers[i*3+0] = (float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.x); + textureSamplers[i*3+1] = (float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.y); + textureSamplers[i*3+2] = (float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.z); + i++; + } + if(i * 3 < textureSamplers.length){ + throw new Error("Did not add all elements! " + samplerTriangles.size() + " " + textureSamplers.length); } //set ratio dat - for(int j = 0; j < triangles.size(); j++){ + for(int j = 0; j < textureRatioData.length / 9; j++){ //first vertex - textureRatioData.add(1.0f); - textureRatioData.add(0.0f); - textureRatioData.add(0.0f); + textureRatioData[j*9+0] = 1.0f; + textureRatioData[j*9+1] = 0.0f; + textureRatioData[j*9+2] = 0.0f; //second vertex - textureRatioData.add(0.0f); - textureRatioData.add(1.0f); - textureRatioData.add(0.0f); + textureRatioData[j*9+3] = 0.0f; + textureRatioData[j*9+4] = 1.0f; + textureRatioData[j*9+5] = 0.0f; //third vertex - textureRatioData.add(0.0f); - textureRatioData.add(0.0f); - textureRatioData.add(1.0f); + textureRatioData[j*9+6] = 0.0f; + textureRatioData[j*9+7] = 0.0f; + textureRatioData[j*9+8] = 1.0f; } //List vertices, List normals, List faceElements, List uvs @@ -1699,6 +1766,143 @@ public class TransvoxelModelGeneration { } + /** + * Generates a mesh based on a terrainchunkdata object + * @param data The terrain chunk data object + * @return The mesh + */ + protected static Mesh generateTerrainMesh(TerrainChunkData data){ + + Mesh mesh = new Mesh("terrainChunk"); + + + // + // VAO + // + mesh.generateVAO(); + + + + + FloatBuffer vertexArrayBufferData = data.getVertexArrayBufferData(); + FloatBuffer normalArrayBufferData = data.getNormalArrayBufferData(); + FloatBuffer textureArrayBufferData = data.getTextureArrayBufferData(); + IntBuffer elementArrayBufferData = data.getElementArrayBufferData(); + FloatBuffer samplerBuffer = data.getSamplerBuffer(); + FloatBuffer ratioBuffer = data.getRatioBuffer(); + + + + + // + // Buffer data to GPU + // + int elementCount = data.getFaceElements().length; + try { + + //actually buffer vertices + if(vertexArrayBufferData.position() > 0){ + vertexArrayBufferData.flip(); + mesh.bufferVertices(vertexArrayBufferData, 3); + } + + //actually buffer normals + if(normalArrayBufferData.position() > 0){ + normalArrayBufferData.flip(); + mesh.bufferNormals(normalArrayBufferData, 3); + } + + //actually buffer UVs + if(textureArrayBufferData.position() > 0){ + textureArrayBufferData.flip(); + mesh.bufferTextureCoords(textureArrayBufferData, 2); + } + + //buffer element indices + if(elementArrayBufferData.position() > 0){ + elementArrayBufferData.flip(); + mesh.bufferFaces(elementArrayBufferData, elementCount); + } + + + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + + + // + // SAMPLER INDICES + // + try { + if(samplerBuffer.position() > 0){ + samplerBuffer.flip(); + mesh.bufferCustomFloatAttribArray(samplerBuffer, 3, SAMPLER_INDEX_ATTRIB_LOC); + } + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + // + // SAMPLER RATIO DATA + // + try { + if(ratioBuffer.position() > 0){ + ratioBuffer.flip(); + mesh.bufferCustomFloatAttribArray(ratioBuffer, 3, SAMPLER_RATIO_ATTRIB_LOC); + } + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + + + + //bounding sphere logic + int distance = ServerTerrainChunk.CHUNK_DIMENSION / 2 * (int)Math.pow(2,data.getLOD()); + mesh.updateBoundingSphere( + distance, + distance, + distance, + (float)Math.sqrt( + distance * distance + + distance * distance + + distance * distance + )); + + + + GL40.glBindVertexArray(0); + return mesh; + } + + + /** + * Generates a model based on a terrainchunkdata object + * @param data The terrain chunk data object + * @param atlas The atlas texture for the chunk + * @return The model + */ + public static Model generateTerrainModel(TerrainChunkData data, VoxelTextureAtlas atlas){ + Model rVal = new Model(); + Mesh m = TransvoxelModelGeneration.generateTerrainMesh(data); + + //construct the material for the chunk + Material groundMat = new Material(); + groundMat.setTexturePointer(atlas.getSpecular().getTexturePointer()); + groundMat.setNormalTexturePointer(atlas.getNormal().getTexturePointer()); + m.setMaterial(groundMat); + + //shader logic + m.setShader(Globals.terrainShaderProgram); + m.setParent(rVal); + + rVal.getMeshes().add(m); + rVal.setBoundingSphere(m.getBoundingSphere()); + + return rVal; + } +