Asset manager queueing + cube voxel meshing
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
316364ad5a
commit
1b1876643d
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
151
src/main/java/electrosphere/client/block/BlockChunkData.java
Normal file
151
src/main/java/electrosphere/client/block/BlockChunkData.java
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
518
src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java
Normal file
518
src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1097
src/test/java/electrosphere/renderer/meshgen/BlockMeshgenTests.java
Normal file
1097
src/test/java/electrosphere/renderer/meshgen/BlockMeshgenTests.java
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user