diff --git a/buildNumber.properties b/buildNumber.properties index adb720ba..d19bcd3b 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -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 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index cb91ee6b..2dff8b48 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -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 diff --git a/src/main/java/electrosphere/client/block/BlockChunkData.java b/src/main/java/electrosphere/client/block/BlockChunkData.java new file mode 100644 index 00000000..44e2c85c --- /dev/null +++ b/src/main/java/electrosphere/client/block/BlockChunkData.java @@ -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; + } + + +} diff --git a/src/main/java/electrosphere/client/ui/menu/script/ScriptLevelEditorUtils.java b/src/main/java/electrosphere/client/ui/menu/script/ScriptLevelEditorUtils.java index dc7309bb..ad9cd762 100644 --- a/src/main/java/electrosphere/client/ui/menu/script/ScriptLevelEditorUtils.java +++ b/src/main/java/electrosphere/client/ui/menu/script/ScriptLevelEditorUtils.java @@ -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()); } } } diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java index 0b83fed5..e529406f 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java @@ -76,7 +76,7 @@ public class AssetManager { //assets queued to be loaded ReentrantLock queuedAssetLock = new ReentrantLock(); - List queuedAssets = new LinkedList(); + List> queuedAssets = new LinkedList>(); @@ -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; } diff --git a/src/main/java/electrosphere/engine/assetmanager/queue/QueuedAsset.java b/src/main/java/electrosphere/engine/assetmanager/queue/QueuedAsset.java index 9ea8a06e..cf6a8251 100644 --- a/src/main/java/electrosphere/engine/assetmanager/queue/QueuedAsset.java +++ b/src/main/java/electrosphere/engine/assetmanager/queue/QueuedAsset.java @@ -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 { /** * 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); + } diff --git a/src/main/java/electrosphere/engine/assetmanager/queue/QueuedModel.java b/src/main/java/electrosphere/engine/assetmanager/queue/QueuedModel.java new file mode 100644 index 00000000..87932d22 --- /dev/null +++ b/src/main/java/electrosphere/engine/assetmanager/queue/QueuedModel.java @@ -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 { + + //true if loaded + boolean hasLoaded = false; + + /** + * The model that will be loaded + */ + Model model; + + /** + * The runnable to invoke to actually load the model + */ + Callable loadFunc; + + /** + * The path promised + */ + String promisedPath; + + + /** + * Creates the queued texture object + * @param image the image to load to gpu + */ + public QueuedModel(Callable 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; + } + +} diff --git a/src/main/java/electrosphere/engine/assetmanager/queue/QueuedTexture.java b/src/main/java/electrosphere/engine/assetmanager/queue/QueuedTexture.java index 8851be11..a3f8923c 100644 --- a/src/main/java/electrosphere/engine/assetmanager/queue/QueuedTexture.java +++ b/src/main/java/electrosphere/engine/assetmanager/queue/QueuedTexture.java @@ -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 { //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; + } } diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index e2eff047..00ae5d43 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -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; diff --git a/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java new file mode 100644 index 00000000..d70d4b2c --- /dev/null +++ b/src/main/java/electrosphere/renderer/meshgen/BlockMeshgen.java @@ -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 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 verts, List normals, List uvs, List 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 quadMeshes = new LinkedList(); + BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData); + + //allocate lists to store mesh data in + List verts = new LinkedList(); + List normals = new LinkedList(); + List uvs = new LinkedList(); + List indices = new LinkedList(); + + //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 { + 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; + } + } + +} diff --git a/src/test/java/electrosphere/renderer/meshgen/BlockMeshgenTests.java b/src/test/java/electrosphere/renderer/meshgen/BlockMeshgenTests.java new file mode 100644 index 00000000..88cb0b3c --- /dev/null +++ b/src/test/java/electrosphere/renderer/meshgen/BlockMeshgenTests.java @@ -0,0 +1,1097 @@ +package electrosphere.renderer.meshgen; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.LinkedList; +import java.util.List; + +import org.joml.Vector2f; +import org.joml.Vector3f; + +import electrosphere.client.block.BlockChunkData; +import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; +import electrosphere.renderer.meshgen.BlockMeshgen.QuadMesh; +import electrosphere.test.annotations.UnitTest; + +/** + * Tests for block mesh generation + */ +public class BlockMeshgenTests { + + /** + * If a normal vector has same length across all three axis, this will be the length + */ + static final float NORMAL_MULTIPLIER = 0.57735026f; + + @UnitTest + public void test_fillQuadMeshes_1(){ + + //expected data + QuadMesh[] expectedData = new QuadMesh[]{ + new QuadMesh(0,0,0,1,1), + }; + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + List quadMeshes = new LinkedList(); + + //call + BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData); + + + //error check result + assertEquals(expectedData.length, quadMeshes.size()); + + for(QuadMesh expected : expectedData){ + boolean found = false; + for(QuadMesh actual : quadMeshes){ + if(expected.x == actual.x && expected.y == actual.y && expected.w == actual.w && expected.h == actual.h){ + found = true; + assertEquals(expected.x, actual.x); + assertEquals(expected.y, actual.y); + assertEquals(expected.w, actual.w); + assertEquals(expected.h, actual.h); + break; + } + } + assertEquals(true, found); + } + } + + @UnitTest + public void test_fillQuadMeshes_2(){ + //expected data + QuadMesh[] expectedData = new QuadMesh[]{ + new QuadMesh(0,0,0,1,2), + }; + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 1, 0, (short)1); + List quadMeshes = new LinkedList(); + + //call + BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData); + + + //error check result + assertEquals(expectedData.length, quadMeshes.size()); + + for(QuadMesh expected : expectedData){ + boolean found = false; + for(QuadMesh actual : quadMeshes){ + if(expected.x == actual.x && expected.y == actual.y && expected.w == actual.w && expected.h == actual.h){ + found = true; + assertEquals(expected.x, actual.x); + assertEquals(expected.y, actual.y); + assertEquals(expected.w, actual.w); + assertEquals(expected.h, actual.h); + break; + } + } + assertEquals(true, found); + } + } + + @UnitTest + public void test_fillQuadMeshes_3(){ + //expected data + QuadMesh[] expectedData = new QuadMesh[]{ + new QuadMesh(0,0,0,2,2), + }; + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 1, 0, (short)1); + chunkData.setType(1, 0, 0, (short)1); + chunkData.setType(1, 1, 0, (short)1); + List quadMeshes = new LinkedList(); + + //call + BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData); + + + //error check result + assertEquals(expectedData.length, quadMeshes.size()); + + for(QuadMesh expected : expectedData){ + boolean found = false; + for(QuadMesh actual : quadMeshes){ + if(expected.x == actual.x && expected.y == actual.y && expected.w == actual.w && expected.h == actual.h){ + found = true; + assertEquals(expected.x, actual.x); + assertEquals(expected.y, actual.y); + assertEquals(expected.w, actual.w); + assertEquals(expected.h, actual.h); + break; + } + } + assertEquals(true, found); + } + } + + @UnitTest + public void test_fillQuadMeshes_4(){ + //expected data + QuadMesh[] expectedData = new QuadMesh[]{ + new QuadMesh(0,0,0,1,1), + new QuadMesh(0,2,0,1,1), + }; + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 2, 0, (short)1); + List quadMeshes = new LinkedList(); + + //call + BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData); + + + //error check result + assertEquals(expectedData.length, quadMeshes.size()); + + for(QuadMesh expected : expectedData){ + boolean found = false; + for(QuadMesh actual : quadMeshes){ + if(expected.x == actual.x && expected.y == actual.y && expected.w == actual.w && expected.h == actual.h){ + found = true; + assertEquals(expected.x, actual.x); + assertEquals(expected.y, actual.y); + assertEquals(expected.w, actual.w); + assertEquals(expected.h, actual.h); + break; + } + } + assertEquals(true, found); + } + } + + @UnitTest + public void test_fillQuadMeshes_5(){ + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + List quadMeshes = new LinkedList(); + + //call + BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData); + + //error check result + assertEquals(0, quadMeshes.size()); + } + + @UnitTest + public void test_meshifyBox_verts(){ + //expected data + float[] expectedData = new float[]{ + 0,0,0, + 1,0,0, + 0,1,0, + 1,1,0, + + 0,0,0, + 0,0,1, + 0,1,0, + 0,1,1, + + 0,0,0, + 0,0,1, + 1,0,0, + 1,0,1, + + 0,0,1, + 1,0,1, + 0,1,1, + 1,1,1, + + 1,0,0, + 1,0,1, + 1,1,0, + 1,1,1, + + 0,1,0, + 0,1,1, + 1,1,0, + 1,1,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + List verts = new LinkedList(); + List normals = new LinkedList(); + List uvs = new LinkedList(); + List indices = new LinkedList(); + QuadMesh quad = new QuadMesh(0, 0, 0, 1, 1); + + //call + BlockMeshgen.meshifyBox(verts, normals, uvs, indices, quad, 1); + + + //error check result + assertEquals(expectedData.length / 3, verts.size()); + + int i = 0; + for(Vector3f vert : verts){ + assertEquals(expectedData[i + 0], vert.x); + assertEquals(expectedData[i + 1], vert.y); + assertEquals(expectedData[i + 2], vert.z); + i = i + 3; + } + } + + @UnitTest + public void test_meshifyBox_normals(){ + //expected data + float[] expectedData = new float[]{ + 0, 0,-1, + 0, 0,-1, + 0, 0,-1, + 0, 0,-1, + + -1, 0, 0, + -1, 0, 0, + -1, 0, 0, + -1, 0, 0, + + 0,-1, 0, + 0,-1, 0, + 0,-1, 0, + 0,-1, 0, + + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + 0, 0, 1, + + + 1, 0, 0, + 1, 0, 0, + 1, 0, 0, + 1, 0, 0, + + + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + }; + + //setup data + List verts = new LinkedList(); + List normals = new LinkedList(); + List uvs = new LinkedList(); + List indices = new LinkedList(); + QuadMesh quad = new QuadMesh(0, 0, 0, 1, 1); + + //call + BlockMeshgen.meshifyBox(verts, normals, uvs, indices, quad, 1); + + + //error check result + assertEquals(expectedData.length / 3, normals.size()); + + int i = 0; + for(Vector3f normal : normals){ + assertEquals(expectedData[i + 0], normal.x); + assertEquals(expectedData[i + 1], normal.y); + assertEquals(expectedData[i + 2], normal.z); + i = i + 3; + } + } + + @UnitTest + public void test_meshifyBox_uvs(){ + //expected data + float[] expectedData = new float[]{ + 0, 0, + 1, 0, + 0, 1, + 1, 1, + + 0, 0, + 1, 0, + 0, 1, + 1, 1, + + 0, 0, + 1, 0, + 0, 1, + 1, 1, + + 0, 0, + 1, 0, + 0, 1, + 1, 1, + + 0, 0, + 1, 0, + 0, 1, + 1, 1, + + 0, 0, + 1, 0, + 0, 1, + 1, 1, + }; + + //setup data + List verts = new LinkedList(); + List normals = new LinkedList(); + List uvs = new LinkedList(); + List indices = new LinkedList(); + QuadMesh quad = new QuadMesh(0, 0, 0, 1, 1); + + //call + BlockMeshgen.meshifyBox(verts, normals, uvs, indices, quad, 1); + + + //error check result + assertEquals(expectedData.length / 2, uvs.size()); + + int i = 0; + for(Vector2f uv : uvs){ + assertEquals(expectedData[i + 0], uv.x); + assertEquals(expectedData[i + 1], uv.y); + i = i + 2; + } + } + + @UnitTest + public void test_rasterize_1(){ + //expected data + float[] expectedData = new float[]{ + 0,0,0, + 1,0,0, + 0,1,0, + 1,1,0, + + 0,0,0, + 0,0,1, + 0,1,0, + 0,1,1, + + 0,0,0, + 0,0,1, + 1,0,0, + 1,0,1, + + 0,0,1, + 1,0,1, + 0,1,1, + 1,1,1, + + 1,0,0, + 1,0,1, + 1,1,0, + 1,1,1, + + 0,1,0, + 0,1,1, + 1,1,0, + 1,1,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_2(){ + //expected data + float[] expectedData = new float[]{ + 0,0,0, + 1,0,0, + 0,2,0, + 1,2,0, + + 0,0,0, + 0,0,1, + 0,2,0, + 0,2,1, + + 0,0,0, + 0,0,1, + 1,0,0, + 1,0,1, + + 0,0,1, + 1,0,1, + 0,2,1, + 1,2,1, + + 1,0,0, + 1,0,1, + 1,2,0, + 1,2,1, + + 0,2,0, + 0,2,1, + 1,2,0, + 1,2,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 1, 0, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_3(){ + //expected data + float[] expectedData = new float[]{ + 0,0,0, + 2,0,0, + 0,2,0, + 2,2,0, + + 0,0,0, + 0,0,1, + 0,2,0, + 0,2,1, + + 0,0,0, + 0,0,1, + 2,0,0, + 2,0,1, + + 0,0,1, + 2,0,1, + 0,2,1, + 2,2,1, + + 2,0,0, + 2,0,1, + 2,2,0, + 2,2,1, + + 0,2,0, + 0,2,1, + 2,2,0, + 2,2,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 1, 0, (short)1); + chunkData.setType(1, 0, 0, (short)1); + chunkData.setType(1, 1, 0, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_4(){ + //expected data + float[] expectedData = new float[]{ + 0,0,0, + 2,0,0, + 0,2,0, + 2,2,0, + + 0,0,0, + 0,0,2, + 0,2,0, + 0,2,2, + + 0,0,0, + 0,0,2, + 2,0,0, + 2,0,2, + + 0,0,2, + 2,0,2, + 0,2,2, + 2,2,2, + + 2,0,0, + 2,0,2, + 2,2,0, + 2,2,2, + + 0,2,0, + 0,2,2, + 2,2,0, + 2,2,2, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 1, 0, (short)1); + chunkData.setType(1, 0, 0, (short)1); + chunkData.setType(1, 1, 0, (short)1); + chunkData.setType(0, 0, 1, (short)1); + chunkData.setType(0, 1, 1, (short)1); + chunkData.setType(1, 0, 1, (short)1); + chunkData.setType(1, 1, 1, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_5(){ + //expected data + float[] expectedData = new float[]{ + //block 1 + 0,0,0, + 1,0,0, + 0,1,0, + 1,1,0, + + 0,0,0, + 0,0,1, + 0,1,0, + 0,1,1, + + 0,0,0, + 0,0,1, + 1,0,0, + 1,0,1, + + 0,0,1, + 1,0,1, + 0,1,1, + 1,1,1, + + 1,0,0, + 1,0,1, + 1,1,0, + 1,1,1, + + 0,1,0, + 0,1,1, + 1,1,0, + 1,1,1, + + //block 2 + 0,2,0, + 1,2,0, + 0,3,0, + 1,3,0, + + 0,2,0, + 0,2,1, + 0,3,0, + 0,3,1, + + 0,2,0, + 0,2,1, + 1,2,0, + 1,2,1, + + 0,2,1, + 1,2,1, + 0,3,1, + 1,3,1, + + 1,2,0, + 1,2,1, + 1,3,0, + 1,3,1, + + 0,3,0, + 0,3,1, + 1,3,0, + 1,3,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + //block 1 + chunkData.setType(0, 0, 0, (short)1); + + //block 2 + chunkData.setType(0, 2, 0, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_6(){ + //expected data + float[] expectedData = new float[]{ + //block 1 + 0,0,0, + 1,0,0, + 0,1,0, + 1,1,0, + + 0,0,0, + 0,0,1, + 0,1,0, + 0,1,1, + + 0,0,0, + 0,0,1, + 1,0,0, + 1,0,1, + + 0,0,1, + 1,0,1, + 0,1,1, + 1,1,1, + + 1,0,0, + 1,0,1, + 1,1,0, + 1,1,1, + + 0,1,0, + 0,1,1, + 1,1,0, + 1,1,1, + + //block 2 + 0,0,2, + 1,0,2, + 0,1,2, + 1,1,2, + + 0,0,2, + 0,0,3, + 0,1,2, + 0,1,3, + + 0,0,2, + 0,0,3, + 1,0,2, + 1,0,3, + + 0,0,3, + 1,0,3, + 0,1,3, + 1,1,3, + + 1,0,2, + 1,0,3, + 1,1,2, + 1,1,3, + + 0,1,2, + 0,1,3, + 1,1,2, + 1,1,3, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + //block 1 + chunkData.setType(0, 0, 0, (short)1); + + //block 2 + chunkData.setType(0, 0, 2, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_7(){ + //expected data + float[] expectedData = new float[]{ + //block 1 + 0,0,0, + 1,0,0, + 0,1,0, + 1,1,0, + + 0,0,0, + 0,0,1, + 0,1,0, + 0,1,1, + + 0,0,0, + 0,0,1, + 1,0,0, + 1,0,1, + + 0,0,1, + 1,0,1, + 0,1,1, + 1,1,1, + + 1,0,0, + 1,0,1, + 1,1,0, + 1,1,1, + + 0,1,0, + 0,1,1, + 1,1,0, + 1,1,1, + + //block 2 + 2,0,0, + 3,0,0, + 2,1,0, + 3,1,0, + + 2,0,0, + 2,0,1, + 2,1,0, + 2,1,1, + + 2,0,0, + 2,0,1, + 3,0,0, + 3,0,1, + + 2,0,1, + 3,0,1, + 2,1,1, + 3,1,1, + + 3,0,0, + 3,0,1, + 3,1,0, + 3,1,1, + + 2,1,0, + 2,1,1, + 3,1,0, + 3,1,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + //block 1 + chunkData.setType(0, 0, 0, (short)1); + + //block 2 + chunkData.setType(2, 0, 0, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_8(){ + //expected data + float[] expectedData = new float[]{}; + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_9(){ + //expected data + float[] expectedData = new float[]{ + //block 1 + 0,0,0, + 1,0,0, + 0,2,0, + 1,2,0, + + 0,0,0, + 0,0,2, + 0,2,0, + 0,2,2, + + 0,0,0, + 0,0,2, + 1,0,0, + 1,0,2, + + 0,0,2, + 1,0,2, + 0,2,2, + 1,2,2, + + 1,0,0, + 1,0,2, + 1,2,0, + 1,2,2, + + 0,2,0, + 0,2,2, + 1,2,0, + 1,2,2, + + //block 2 + 2,0,0, + 3,0,0, + 2,1,0, + 3,1,0, + + 2,0,0, + 2,0,1, + 2,1,0, + 2,1,1, + + 2,0,0, + 2,0,1, + 3,0,0, + 3,0,1, + + 2,0,1, + 3,0,1, + 2,1,1, + 3,1,1, + + 3,0,0, + 3,0,1, + 3,1,0, + 3,1,1, + + 2,1,0, + 2,1,1, + 3,1,0, + 3,1,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + //block 1 + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 1, 0, (short)1); + chunkData.setType(0, 1, 1, (short)1); + chunkData.setType(0, 0, 1, (short)1); + + //block 2 + chunkData.setType(2, 0, 0, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + + @UnitTest + public void test_rasterize_10(){ + //expected data + float[] expectedData = new float[]{ + //block 1 + 0,0,0, + 1,0,0, + 0,2,0, + 1,2,0, + + 0,0,0, + 0,0,2, + 0,2,0, + 0,2,2, + + 0,0,0, + 0,0,2, + 1,0,0, + 1,0,2, + + 0,0,2, + 1,0,2, + 0,2,2, + 1,2,2, + + 1,0,0, + 1,0,2, + 1,2,0, + 1,2,2, + + 0,2,0, + 0,2,2, + 1,2,0, + 1,2,2, + + //block 2 + 1,0,0, + 2,0,0, + 1,1,0, + 2,1,0, + + 1,0,0, + 1,0,1, + 1,1,0, + 1,1,1, + + 1,0,0, + 1,0,1, + 2,0,0, + 2,0,1, + + 1,0,1, + 2,0,1, + 1,1,1, + 2,1,1, + + 2,0,0, + 2,0,1, + 2,1,0, + 2,1,1, + + 1,1,0, + 1,1,1, + 2,1,0, + 2,1,1, + }; + for(int i = 0; i < expectedData.length; i++){ + expectedData[i] = expectedData[i] * BlockChunkData.BLOCK_SIZE_MULTIPLIER; + } + + //setup data + BlockChunkData chunkData = new BlockChunkData(); + short[] types = new short[BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH * BlockChunkData.CHUNK_DATA_WIDTH]; + chunkData.setType(types); + //block 1 + chunkData.setType(0, 0, 0, (short)1); + chunkData.setType(0, 1, 0, (short)1); + chunkData.setType(0, 1, 1, (short)1); + chunkData.setType(0, 0, 1, (short)1); + + //block 2 + chunkData.setType(1, 0, 0, (short)1); + + //call + BlockMeshData meshData = BlockMeshgen.rasterize(chunkData); + + + //error check result + assertEquals(expectedData.length, meshData.vertices.length); + + for(int i = 0; i < expectedData.length; i++){ + assertEquals(expectedData[i], meshData.vertices[i]); + } + } + +}