Asset manager queueing + cube voxel meshing
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-11-23 21:29:21 -05:00
parent 316364ad5a
commit 1b1876643d
11 changed files with 1944 additions and 20 deletions

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
#Sat Nov 23 11:28:50 EST 2024
buildNumber=401
#Sat Nov 23 20:49:19 EST 2024
buildNumber=403

View File

@ -1140,6 +1140,10 @@ Move text files to documentation pages
1000th commit milestone!
Pine tree collidables
Fix human animation bad data
Fix script entity spawn utils not spawning on max cursor distance
Block mesh generation
Add asset manager support for queueing models asynchronously w/ promised path
Add promised path support to queued textures
# TODO

View File

@ -0,0 +1,151 @@
package electrosphere.client.block;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/**
* Stores data about a chunk of blocks
*/
public class BlockChunkData {
/**
* Number of blocks in each dimension of a chunk
*/
public static final int CHUNK_DATA_WIDTH = 64;
/**
* The number of blocks to place within each unit of distance
*/
public static final int BLOCKS_PER_UNIT_DISTANCE = CHUNK_DATA_WIDTH / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
/**
* The amount to scale block size by
*/
public static final float BLOCK_SIZE_MULTIPLIER = 1.0f / BLOCKS_PER_UNIT_DISTANCE;
/**
* The type of block at a given position
*/
short[] type;
/**
* Metadata about a block
* first 4 bits are the rotation of the block)
*/
short[] metadata;
/**
* Constructor
*/
public BlockChunkData(){
}
/**
* Constructor
*/
public static BlockChunkData allocate(){
BlockChunkData rVal = new BlockChunkData();
rVal.setType(new short[CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH]);
rVal.setMetadata(new short[CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH]);
return rVal;
}
/**
* Gets the type data for the chunk
* @return The type data
*/
public short[] getType() {
return type;
}
/**
* Sets the type data for the chunk
* @param type The type data
*/
public void setType(short[] type) {
if(type.length != CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH){
throw new Error("Set type with invalid length! " + type.length);
}
this.type = type;
}
/**
* Gets the metadata for the chunk
* @return The metadata
*/
public short[] getMetadata() {
return metadata;
}
/**
* Sets the metadata for the chunk
* @param metadata The metadata
*/
public void setMetadata(short[] metadata) {
if(metadata.length != CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH){
throw new Error("Set metadata with invalid length! " + metadata.length);
}
this.metadata = metadata;
}
/**
* Gets the type at a given position
* @param x The x position
* @param y The y position
* @param z The z position
* @return The type at that position
*/
public short getType(int x, int y, int z){
return this.type[x * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH + z * CHUNK_DATA_WIDTH + y];
}
/**
* Sets a specific block's type
* @param x The x position
* @param y The y position
* @param z The z position
* @param type The type
*/
public void setType(int x, int y, int z, short type){
this.type[x * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH + z * CHUNK_DATA_WIDTH + y] = type;
}
/**
* Sets a specific block's type
* @param x The x position
* @param y The y position
* @param z The z position
* @param type The type
*/
public void setType(int x, int y, int z, int type){
this.type[x * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH + z * CHUNK_DATA_WIDTH + y] = (short)type;
}
/**
* Gets the metadata at a given position
* @param x The x position
* @param y The y position
* @param z The z position
* @return The metadata at that position
*/
public short getMetadata(int x, int y, int z){
return this.metadata[x * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH + z * CHUNK_DATA_WIDTH + y];
}
/**
* Checks if a given location is empty
* @param x The x position
* @param y The y position
* @param z The z position
* @return true if empty, false otherwise
*/
public boolean isEmpty(int x, int y, int z){
boolean empty = this.getType(x,y,z) == 0;
return empty;
}
}

View File

@ -41,10 +41,11 @@ public class ScriptLevelEditorUtils {
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), CollisionEngine.DEFAULT_INTERACT_DISTANCE);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, Globals.selectedSpawntype.getId(), null);
if(cursorPos == null){
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
}
cursorPos = cursorPos.add(cursorVerticalOffset);
CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, Globals.selectedSpawntype.getId(), null);
} else if(Globals.selectedSpawntype instanceof Item){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
@ -52,10 +53,11 @@ public class ScriptLevelEditorUtils {
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), CollisionEngine.DEFAULT_INTERACT_DISTANCE);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
ItemUtils.serverSpawnBasicItem(realm, cursorPos, Globals.selectedSpawntype.getId());
if(cursorPos == null){
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
}
cursorPos = cursorPos.add(cursorVerticalOffset);
ItemUtils.serverSpawnBasicItem(realm, cursorPos, Globals.selectedSpawntype.getId());
} else if(Globals.selectedSpawntype instanceof FoliageType){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
@ -63,10 +65,11 @@ public class ScriptLevelEditorUtils {
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), CollisionEngine.DEFAULT_INTERACT_DISTANCE);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, Globals.selectedSpawntype.getId(), new Random().nextLong());
if(cursorPos == null){
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
}
cursorPos = cursorPos.add(cursorVerticalOffset);
FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, Globals.selectedSpawntype.getId(), new Random().nextLong());
} else {
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
@ -74,10 +77,11 @@ public class ScriptLevelEditorUtils {
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), CollisionEngine.DEFAULT_INTERACT_DISTANCE);
if(cursorPos != null){
cursorPos = cursorPos.add(cursorVerticalOffset);
CommonEntityUtils.serverSpawnBasicObject(realm, cursorPos, Globals.selectedSpawntype.getId());
if(cursorPos == null){
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
}
cursorPos = cursorPos.add(cursorVerticalOffset);
CommonEntityUtils.serverSpawnBasicObject(realm, cursorPos, Globals.selectedSpawntype.getId());
}
}
}

View File

@ -76,7 +76,7 @@ public class AssetManager {
//assets queued to be loaded
ReentrantLock queuedAssetLock = new ReentrantLock();
List<QueuedAsset> queuedAssets = new LinkedList<QueuedAsset>();
List<QueuedAsset<?>> queuedAssets = new LinkedList<QueuedAsset<?>>();
@ -160,8 +160,13 @@ public class AssetManager {
//queued assets
LoggerInterface.loggerEngine.DEBUG_LOOP("AssetManager - Load queued assets");
queuedAssetLock.lock();
for(QueuedAsset queuedAsset : queuedAssets){
for(QueuedAsset<?> queuedAsset : queuedAssets){
queuedAsset.load();
if(queuedAsset.get() instanceof Model){
this.modelsLoadedIntoMemory.put(queuedAsset.getPromisedPath(),(Model)queuedAsset.get());
} else if(queuedAsset.get() instanceof Model){
this.texturesLoadedIntoMemory.put(queuedAsset.getPromisedPath(),(Texture)queuedAsset.get());
}
}
queuedAssets.clear();
queuedAssetLock.unlock();
@ -606,10 +611,18 @@ public class AssetManager {
* Queues an asset to be loaded on the main thread
* @param asset the asset
*/
public void queuedAsset(QueuedAsset asset){
public String queuedAsset(QueuedAsset<?> asset){
queuedAssetLock.lock();
this.queuedAssets.add(asset);
//promise a specific string for this asset
UUID newUUID = UUID.randomUUID();
String promisedPath = newUUID.toString();
asset.setPromisedPath(promisedPath);
queuedAssetLock.unlock();
return promisedPath;
}

View File

@ -3,7 +3,7 @@ package electrosphere.engine.assetmanager.queue;
/**
* An asset that has its data ready to be buffered to gpu
*/
public interface QueuedAsset {
public interface QueuedAsset<T> {
/**
* Loads the asset
@ -16,4 +16,22 @@ public interface QueuedAsset {
*/
public boolean hasLoaded();
/**
* Gets the asset
* @return The asset
*/
public T get();
/**
* Gets the path the asset manager promises this asset will be stored at
* @return The promised path
*/
public String getPromisedPath();
/**
* Sets the path the asset manager promises this asset will be stored at
* @param promisedPath The path
*/
public void setPromisedPath(String promisedPath);
}

View File

@ -0,0 +1,80 @@
package electrosphere.engine.assetmanager.queue;
import java.util.concurrent.Callable;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.model.Model;
/**
* A model that is queued to be loaded
*/
public class QueuedModel implements QueuedAsset<Model> {
//true if loaded
boolean hasLoaded = false;
/**
* The model that will be loaded
*/
Model model;
/**
* The runnable to invoke to actually load the model
*/
Callable<Model> loadFunc;
/**
* The path promised
*/
String promisedPath;
/**
* Creates the queued texture object
* @param image the image to load to gpu
*/
public QueuedModel(Callable<Model> loadFunc){
this.loadFunc = loadFunc;
}
@Override
public void load() {
if(loadFunc != null){
try {
this.model = loadFunc.call();
} catch (Exception e) {
LoggerInterface.loggerEngine.ERROR(e);
}
}
hasLoaded = true;
}
@Override
public boolean hasLoaded() {
return hasLoaded;
}
@Override
public String getPromisedPath(){
return promisedPath;
}
/**
* Gets the model from this queued item
* @return The model
*/
public Model getModel(){
return model;
}
@Override
public void setPromisedPath(String promisedPath) {
this.promisedPath = promisedPath;
}
@Override
public Model get(){
return model;
}
}

View File

@ -9,7 +9,7 @@ import electrosphere.renderer.texture.Texture;
/**
* A texture queued to be sent to the gpu
*/
public class QueuedTexture implements QueuedAsset {
public class QueuedTexture implements QueuedAsset<Texture> {
//true if loaded
boolean hasLoaded = false;
@ -35,6 +35,11 @@ public class QueuedTexture implements QueuedAsset {
*/
int height = -1;
/**
* The path the asset manager promises this texture will be stored at
*/
String promisedPath;
/**
* Creates the queued texture object
@ -104,6 +109,19 @@ public class QueuedTexture implements QueuedAsset {
return height;
}
@Override
public void setPromisedPath(String promisedPath) {
this.promisedPath = promisedPath;
}
@Override
public String getPromisedPath(){
return this.promisedPath;
}
@Override
public Texture get(){
return texture;
}
}

View File

@ -5,6 +5,7 @@ import java.util.concurrent.TimeUnit;
import org.joml.Vector3f;
import electrosphere.client.block.BlockChunkData;
import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.client.entity.crosshair.Crosshair;
import electrosphere.client.fluid.cells.FluidCellManager;
@ -18,6 +19,7 @@ import electrosphere.client.ui.menu.mainmenu.MenuCharacterCreation;
import electrosphere.controls.ControlHandler;
import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.AssetDataStrings;
import electrosphere.engine.assetmanager.queue.QueuedModel;
import electrosphere.engine.signal.Signal.SignalType;
import electrosphere.engine.threads.LabeledThread.ThreadLabel;
import electrosphere.entity.DrawableUtils;
@ -29,6 +31,8 @@ import electrosphere.net.NetUtils;
import electrosphere.net.client.ClientNetworking;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.ActorTextureMask;
import electrosphere.renderer.meshgen.BlockMeshgen;
import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData;
public class ClientLoading {
@ -272,6 +276,23 @@ public class ClientLoading {
cursorActor.addTextureMask(new ActorTextureMask("sphere", Arrays.asList(new String[]{"Textures/transparent_red.png"})));
DrawableUtils.makeEntityTransparent(Globals.playerCursor);
EntityUtils.getScale(Globals.playerCursor).set(0.2f);
//block test
// Entity blockEntity = EntityCreationUtils.createClientSpatialEntity();
// BlockChunkData blockChunkData = BlockChunkData.allocate();
// blockChunkData.setType(0, 0, 0, 1);
// blockChunkData.setType(1, 0, 0, 1);
// blockChunkData.setType(0, 1, 0, 1);
// blockChunkData.setType(1, 1, 0, 1);
// blockChunkData.setType(0, 0, 1, 1);
// blockChunkData.setType(1, 0, 1, 1);
// blockChunkData.setType(0, 1, 1, 1);
// blockChunkData.setType(1, 1, 1, 1);
// BlockMeshData meshData = BlockMeshgen.rasterize(blockChunkData);
// String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> {
// return BlockMeshgen.generateBlockModel(meshData);
// }));
// EntityCreationUtils.makeEntityDrawablePreexistingModel(blockEntity, modelPath);
}
static final int MAX_DRAW_CELL_WAIT = 1000;

View File

@ -0,0 +1,518 @@
package electrosphere.renderer.meshgen;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL40;
import electrosphere.client.block.BlockChunkData;
import electrosphere.engine.Globals;
import electrosphere.renderer.model.Mesh;
import electrosphere.renderer.model.Model;
/**
* Generates a model for a block
*/
public class BlockMeshgen {
/**
* The indices to draw faces on cubes
*/
static final int[] CUBE_INDICES = 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
};
/**
* Calculates the quad meshes for the provided chunk data
* @param quadMeshes The quad mesh list to fill
* @param chunkData The chunk data
*/
protected static void fillQuadMeshes(List<QuadMesh> quadMeshes, BlockChunkData chunkData){
for(int z = 0; z < BlockChunkData.CHUNK_DATA_WIDTH; z++){
for(int x = 0; x < BlockChunkData.CHUNK_DATA_WIDTH; x++){
QuadMesh currentQuad = null;
for(int y = 0; y < BlockChunkData.CHUNK_DATA_WIDTH; y++){
if(chunkData.isEmpty(x, y, z)){
if(currentQuad == null){
continue;
} else {
currentQuad.h = y - currentQuad.y;
//check if should merge with previous quad
for(QuadMesh prevMesh : quadMeshes){
if(prevMesh.x + prevMesh.w == currentQuad.x && prevMesh.y == currentQuad.y && prevMesh.h == currentQuad.h){
prevMesh.w = prevMesh.w + 1;
currentQuad = null;
break;
}
}
if(currentQuad != null){
quadMeshes.add(currentQuad);
}
currentQuad = null;
}
} else {
if(currentQuad == null){
currentQuad = new QuadMesh();
currentQuad.x = x;
currentQuad.y = y;
currentQuad.z = z;
currentQuad.w = 1;
currentQuad.h = 1;
} else {
continue;
}
}
}
}
}
}
/**
* Meshes a box
* @param verts The list of verts to store into
* @param normals The list of normals to store into
* @param uvs The list of uvs to store into
* @param indices The list of indices to store into
* @param quad The quad
* @param depth The depth of the box
*/
protected static void meshifyBox(List<Vector3f> verts, List<Vector3f> normals, List<Vector2f> uvs, List<Integer> indices, QuadMesh quad, int depth){
//
//face 1
//
//verts
verts.add(new Vector3f(quad.x, quad.y, quad.z).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y, quad.z).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x, quad.y + quad.h, quad.z).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
//indices
indices.add(0);
indices.add(2);
indices.add(3);
indices.add(0);
indices.add(1);
indices.add(3);
//normals
normals.add(new Vector3f(0,0,-1));
normals.add(new Vector3f(0,0,-1));
normals.add(new Vector3f(0,0,-1));
normals.add(new Vector3f(0,0,-1));
//uvs
uvs.add(new Vector2f( 0, 0));
uvs.add(new Vector2f(quad.w, 0));
uvs.add(new Vector2f( 0, quad.h));
uvs.add(new Vector2f(quad.w, quad.h));
//
//face 2
//
//verts
verts.add(new Vector3f(quad.x, quad.y, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x, quad.y, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x, quad.y + quad.h, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
//indices
indices.add(4);
indices.add(6);
indices.add(7);
indices.add(4);
indices.add(5);
indices.add(7);
//normals
normals.add(new Vector3f(-1,0,0));
normals.add(new Vector3f(-1,0,0));
normals.add(new Vector3f(-1,0,0));
normals.add(new Vector3f(-1,0,0));
//uvs
uvs.add(new Vector2f( 0, 0));
uvs.add(new Vector2f(depth, 0));
uvs.add(new Vector2f( 0, quad.h));
uvs.add(new Vector2f(depth, quad.h));
//
//face 3
//
//verts
verts.add(new Vector3f(quad.x, quad.y, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x, quad.y, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
//indices
indices.add( 8);
indices.add(10);
indices.add(11);
indices.add( 8);
indices.add( 9);
indices.add(11);
//normals
normals.add(new Vector3f(0,-1,0));
normals.add(new Vector3f(0,-1,0));
normals.add(new Vector3f(0,-1,0));
normals.add(new Vector3f(0,-1,0));
//uvs
uvs.add(new Vector2f( 0, 0));
uvs.add(new Vector2f(depth, 0));
uvs.add(new Vector2f( 0, quad.w));
uvs.add(new Vector2f(depth, quad.w));
//
//face 4
//
//verts
verts.add(new Vector3f(quad.x, quad.y, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
//indices
indices.add(12);
indices.add(14);
indices.add(15);
indices.add(12);
indices.add(13);
indices.add(15);
//normals
normals.add(new Vector3f(0,0,1));
normals.add(new Vector3f(0,0,1));
normals.add(new Vector3f(0,0,1));
normals.add(new Vector3f(0,0,1));
//uvs
uvs.add(new Vector2f( 0, 0));
uvs.add(new Vector2f(quad.w, 0));
uvs.add(new Vector2f( 0, quad.h));
uvs.add(new Vector2f(quad.w, quad.h));
//
//face 5
//
//verts
verts.add(new Vector3f(quad.x + quad.w, quad.y, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
//indices
indices.add(16);
indices.add(18);
indices.add(19);
indices.add(16);
indices.add(17);
indices.add(19);
//normals
normals.add(new Vector3f(1,0,0));
normals.add(new Vector3f(1,0,0));
normals.add(new Vector3f(1,0,0));
normals.add(new Vector3f(1,0,0));
//uvs
uvs.add(new Vector2f( 0, 0));
uvs.add(new Vector2f(depth, 0));
uvs.add(new Vector2f( 0, quad.h));
uvs.add(new Vector2f(depth, quad.h));
//
//face 6
//
//verts
verts.add(new Vector3f(quad.x, quad.y + quad.h, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z ).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
verts.add(new Vector3f(quad.x + quad.w, quad.y + quad.h, quad.z + depth).mul(BlockChunkData.BLOCK_SIZE_MULTIPLIER));
//indices
indices.add(20);
indices.add(22);
indices.add(23);
indices.add(20);
indices.add(21);
indices.add(23);
//normals
normals.add(new Vector3f(0,1,0));
normals.add(new Vector3f(0,1,0));
normals.add(new Vector3f(0,1,0));
normals.add(new Vector3f(0,1,0));
//uvs
uvs.add(new Vector2f( 0, 0));
uvs.add(new Vector2f(depth, 0));
uvs.add(new Vector2f( 0, quad.w));
uvs.add(new Vector2f(depth, quad.w));
}
/**
* Rasterizes a block chunk data into mesh data
* @param chunkData The block chunk data
* @return The mesh data
*/
public static BlockMeshData rasterize(BlockChunkData chunkData){
BlockMeshData rVal = new BlockMeshData();
//calculate quad meshes
List<QuadMesh> quadMeshes = new LinkedList<QuadMesh>();
BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData);
//allocate lists to store mesh data in
List<Vector3f> verts = new LinkedList<Vector3f>();
List<Vector3f> normals = new LinkedList<Vector3f>();
List<Vector2f> uvs = new LinkedList<Vector2f>();
List<Integer> indices = new LinkedList<Integer>();
//sort
Collections.sort(quadMeshes);
//generate volumes
QuadMesh quad1 = null;
QuadMesh quad2 = null;
int zEnd = 0;
for(int i = 0; i < quadMeshes.size(); i++){
quad1 = quadMeshes.get(i);
zEnd = 1;
for(int j = i + 1; j < quadMeshes.size(); j++){
quad2 = quadMeshes.get(j);
if(quad1.x == quad2.x && quad1.y == quad2.y && quad1.w == quad2.w && quad1.h == quad2.h && quad1.z + zEnd == quad2.z){
zEnd++;
} else {
BlockMeshgen.meshifyBox(verts,normals,uvs,indices,quad1,zEnd);
quad1 = quad2;
zEnd = 1;
break;
}
}
i = i + zEnd;
}
if(quad1 != null){
BlockMeshgen.meshifyBox(verts,normals,uvs,indices,quad1,zEnd);
}
//
//store in flat arrays
//
//verts
rVal.vertices = new float[verts.size() * 3];
for(int i = 0; i < verts.size(); i++){
Vector3f currentVert = verts.get(i);
rVal.vertices[3 * i + 0] = currentVert.x;
rVal.vertices[3 * i + 1] = currentVert.y;
rVal.vertices[3 * i + 2] = currentVert.z;
}
rVal.vertBuffer = BufferUtils.createFloatBuffer(rVal.vertices.length);
rVal.vertBuffer.put(rVal.vertices);
//faces
rVal.faceElements = new int[indices.size()];
for(int i = 0; i < indices.size(); i++){
rVal.faceElements[i] = indices.get(i);
}
rVal.faceBuffer = BufferUtils.createIntBuffer(rVal.faceElements.length);
rVal.faceBuffer.put(rVal.faceElements);
//normals
rVal.normals = new float[normals.size() * 3];
for(int i = 0; i < normals.size(); i++){
Vector3f currentNormal = normals.get(i);
rVal.normals[3 * i + 0] = currentNormal.x;
rVal.normals[3 * i + 1] = currentNormal.y;
rVal.normals[3 * i + 2] = currentNormal.z;
}
rVal.normalBuffer = BufferUtils.createFloatBuffer(rVal.normals.length);
rVal.normalBuffer.put(rVal.normals);
//uvs
rVal.uvs = new float[uvs.size() * 2];
for(int i = 0; i < uvs.size(); i++){
Vector2f currentUV = uvs.get(i);
rVal.uvs[2 * i + 0] = currentUV.x;
rVal.uvs[2 * i + 1] = currentUV.y;
}
rVal.uvBuffer = BufferUtils.createFloatBuffer(rVal.uvs.length);
rVal.uvBuffer.put(rVal.uvs);
return rVal;
}
/**
* Generates a mesh based on a block mesh data object
* @param data The block mesh data object
* @return The mesh
*/
protected static Mesh generateBlockMesh(BlockMeshData meshData){
Mesh mesh = new Mesh("blockChunk");
//
// VAO
//
mesh.generateVAO();
FloatBuffer vertexArrayBufferData = meshData.vertBuffer;
FloatBuffer normalArrayBufferData = meshData.normalBuffer;
FloatBuffer textureArrayBufferData = meshData.uvBuffer;
IntBuffer elementArrayBufferData = meshData.faceBuffer;
//
// Buffer data to GPU
//
int elementCount = meshData.faceElements.length;
try {
//actually buffer vertices
if(vertexArrayBufferData.position() > 0){
vertexArrayBufferData.flip();
mesh.bufferVertices(vertexArrayBufferData, 3);
}
//actually buffer normals
if(normalArrayBufferData != null && normalArrayBufferData.position() > 0){
normalArrayBufferData.flip();
mesh.bufferNormals(normalArrayBufferData, 3);
}
//actually buffer UVs
if(textureArrayBufferData != null && 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();
}
//bounding sphere logic
int distance = BlockChunkData.CHUNK_DATA_WIDTH / 2;
mesh.updateBoundingSphere(
distance,
distance,
distance,
(float)Math.sqrt(
distance * distance +
distance * distance +
distance * distance
));
GL40.glBindVertexArray(0);
return mesh;
}
/**
* Generates the model for the block mesh
* @param chunkData The mesh data
* @return The model object
*/
public static Model generateBlockModel(BlockMeshData meshData){
Model rVal = new Model();
Mesh m = BlockMeshgen.generateBlockMesh(meshData);
//construct the material for the chunk
// Material groundMat = new Material();
// groundMat.setTexturePointer(Globals.defaultMeshShader);
// groundMat.setNormalTexturePointer(atlas.getNormal().getTexturePointer());
// m.setMaterial(groundMat);
//shader logic
m.setShader(Globals.defaultMeshShader);
m.setParent(rVal);
rVal.getMeshes().add(m);
rVal.setBoundingSphere(m.getBoundingSphere());
return rVal;
}
/**
* The final rasterization data that is emitted
*/
public static class BlockMeshData {
//the verts
float[] vertices;
//normals
float[] normals;
//faces
int[] faceElements;
//UVs
float[] uvs;
FloatBuffer vertBuffer;
FloatBuffer normalBuffer;
IntBuffer faceBuffer;
FloatBuffer uvBuffer;
}
/**
* Intermediary structure used during rasterization
*/
public static class QuadMesh implements Comparable<QuadMesh> {
int x;
int y;
int z;
int w;
int h;
public QuadMesh(){}
public QuadMesh(int x, int y, int z, int w, int h){
this.x = x;
this.y = y;
this.z = z;
this.w = w;
this.h = h;
}
@Override
public int compareTo(QuadMesh other) {
if(this.y != other.y){
return this.y - other.y;
}
if(this.x != other.x){
return this.x - other.x;
}
if(this.w != other.w){
return this.w - other.w;
}
return this.h - other.h;
}
}
}

File diff suppressed because it is too large Load Diff