From 793d1e846a882550cd96515151fca7f81ceaf802 Mon Sep 17 00:00:00 2001 From: BananaPuncher714 Date: Sat, 31 May 2025 05:13:11 -0400 Subject: [PATCH] Add de/serialization for chunk meshes Add half-baked loading and saving to avoid having to mesh the chunk each time --- .../minietest/MiniePlugin.java | 149 ++++++--------- .../minietest/objects/Aabb.java | 13 ++ .../minietest/objects/BlockDataTypeSet.java | 12 +- .../minietest/objects/ChunkMesh.java | 177 +++++++++++++++++- .../minietest/util/BinaryWriter.java | 74 ++++++++ .../minietest/util/ChunkMeshUtil.java | 38 +++- 6 files changed, 371 insertions(+), 92 deletions(-) create mode 100644 src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/BinaryWriter.java diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MiniePlugin.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MiniePlugin.java index a30048c..ece9f17 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MiniePlugin.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/MiniePlugin.java @@ -3,6 +3,9 @@ package com.aaaaahhhhhhh.bananapuncher714.minietest; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -20,6 +23,7 @@ import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.logging.Level; import java.util.stream.Collectors; import org.apache.commons.lang.SystemUtils; @@ -75,6 +79,7 @@ import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.PlaneMesh; import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.VectorReference; import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Facet; import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.MeshBuilder; +import com.aaaaahhhhhhh.bananapuncher714.minietest.util.ChunkMeshUtil; import com.aaaaahhhhhhh.bananapuncher714.minietest.util.FileUtil; import com.jme3.bullet.PhysicsSpace; import com.jme3.bullet.PhysicsSpace.BroadphaseType; @@ -129,9 +134,6 @@ public class MiniePlugin extends JavaPlugin { private Map< ChunkLocation, ChunkMesh > chunkMeshes = new HashMap< ChunkLocation, ChunkMesh >(); - private Map< ChunkLocation, BukkitTask > loadMeshTasks = new ConcurrentHashMap< ChunkLocation, BukkitTask >(); - private Map< ChunkLocation, BukkitTask > saveMeshTasks = new ConcurrentHashMap< ChunkLocation, BukkitTask >(); - @Override public void onEnable() { FileUtil.saveToFile( getResource( "native/windows/x86_64/bulletjme.dll" ), new File( getDataFolder() + "/lib", "bulletjme.dll" ), false ); @@ -145,6 +147,11 @@ public class MiniePlugin extends JavaPlugin { registerCommands(); + // Don't log anything + PhysicsSpace.logger.setLevel( Level.WARNING ); + PhysicsSpace.loggerC.setLevel( Level.WARNING ); + PhysicsRigidBody.logger2.setLevel( Level.WARNING ); + space = new PhysicsSpace( BroadphaseType.DBVT ); PlaneCollisionShape plane = new PlaneCollisionShape( new Plane( new Vector3f( 0, 1, 0 ), 0 ) ); planeBody = new PhysicsRigidBody( plane, PhysicsBody.massForStatic ); @@ -186,10 +193,10 @@ public class MiniePlugin extends JavaPlugin { private void onChunkLoad( ChunkLoadEvent event ) { if ( event.getWorld().getName().equalsIgnoreCase( "world" ) ) { // getLogger().info( "Loading chunk " + event.getChunk().getX() + ", " + event.getChunk().getZ() ); - generateMesh( event.getChunk() ); +// generateMesh( event.getChunk() ); // scanAndGenerate( event.getChunk() ); // saveFacets( event.getChunk() ); -// loadOrGenerateMesh( event.getChunk() ); + loadOrGenerateMesh( event.getChunk() ); } } @@ -200,13 +207,12 @@ public class MiniePlugin extends JavaPlugin { if ( chunks.containsKey( loc ) ) { space.removeCollisionObject( chunks.get( loc ) ); chunks.remove( loc ); - chunkMeshes.remove( loc ); } -// final ChunkMesh mesh = chunkMeshes.get( loc ); -// if ( mesh != null ) { -// attemptToSaveMesh( loc, mesh ); -// } + final ChunkMesh mesh = chunkMeshes.remove( loc ); + if ( mesh != null ) { + saveMesh( loc, mesh ); + } } } @@ -247,8 +253,8 @@ public class MiniePlugin extends JavaPlugin { getLogger().info( "There are " + Bukkit.getWorld( "world" ).getLoadedChunks().length + " chunks" ); // getLogger().info( "Converted " + saveAllChunks( Bukkit.getWorld( "world" ) ) ); -// loadOrGenerateWorld( Bukkit.getWorld( "world" ) ); - scanAndGenerate( Bukkit.getWorld( "world" ) ); + loadOrGenerateWorld( Bukkit.getWorld( "world" ) ); +// scanAndGenerate( Bukkit.getWorld( "world" ) ); // saveAllFacets( Bukkit.getWorld( "world" ) ); Bukkit.getScheduler().runTaskTimer( this, () -> { @@ -707,93 +713,62 @@ public class MiniePlugin extends JavaPlugin { } private void loadOrGenerateMesh( final Chunk chunk ) { -// chunk.addPluginChunkTicket( this ); final ChunkLocation location = new ChunkLocation( chunk ); - if ( !chunkMeshes.containsKey( location ) && !loadMeshTasks.containsKey( location ) ) { - loadMeshTasks.put( location, Bukkit.getScheduler().runTaskAsynchronously( this, () -> { - final ChunkMesh mesh = loadMesh( location ); - if ( mesh == null ) { - Bukkit.getScheduler().runTask( this, () -> { - generateMesh( chunk ); - } ); - } else { - // Now, create a physics object from the chunk mesh - final IndexedMesh nativeMesh = mesh.toIndexedMesh(); - // Enable quantized AABB compression... hopefully that doesn't mess with the accuracy or anything... - final MeshCollisionShape meshShape = new MeshCollisionShape( true, nativeMesh ); - - // Create a rigid body - PhysicsRigidBody rigid = new PhysicsRigidBody( meshShape, PhysicsBody.massForStatic ); - rigid.setPhysicsLocation( new Vector3f( chunk.getX() << 4, chunk.getWorld().getMinHeight(), chunk.getZ() << 4 ) ); - rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) ); - rigid.setKinematic( false ); - - Bukkit.getScheduler().runTask( this, () -> { - // Save our chunk mesh - chunkMeshes.put( location, mesh ); - - space.addCollisionObject( rigid ); + final ChunkMesh mesh = loadMesh( location ); + if ( mesh == null ) { + generateMesh( chunk ); + } else { + // Now, create a physics object from the chunk mesh + final IndexedMesh nativeMesh = mesh.toIndexedMesh(); + // Enable quantized AABB compression... hopefully that doesn't mess with the accuracy or anything... + final MeshCollisionShape meshShape = new MeshCollisionShape( true, nativeMesh ); - PhysicsRigidBody old = chunks.put( new ChunkLocation( chunk ), rigid ); - if ( old != null ) { - getLogger().warning( "Old rigid body found!" ); - space.remove( old ); - } - - chunk.removePluginChunkTicket( this ); - - loadMeshTasks.remove( location ); - } ); - } - } ) ); - } else if ( !saveMeshTasks.containsKey( location ) ) { - chunk.removePluginChunkTicket( this ); + // Create a rigid body + PhysicsRigidBody rigid = new PhysicsRigidBody( meshShape, PhysicsBody.massForStatic ); + rigid.setPhysicsLocation( new Vector3f( chunk.getX() << 4, chunk.getWorld().getMinHeight(), chunk.getZ() << 4 ) ); + rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) ); + rigid.setKinematic( false ); + + // Save our chunk mesh + chunkMeshes.put( location, mesh ); + + space.addCollisionObject( rigid ); + + PhysicsRigidBody old = chunks.put( new ChunkLocation( chunk ), rigid ); + if ( old != null ) { + getLogger().warning( "Old rigid body found!" ); + space.remove( old ); + } } } - private void attemptToSaveMesh( final ChunkLocation location, final ChunkMesh mesh ) { -// getLogger().info( "Attempting to save " + location ); - saveMeshTasks.put( location, Bukkit.getScheduler().runTaskAsynchronously( this, () -> { - saveMesh( location, mesh ); - - Bukkit.getScheduler().runTask( this, () -> { - if ( location.isLoaded() ) { - location.getChunk().removePluginChunkTicket( this ); - } else { - chunkMeshes.remove( location ); - - final PhysicsRigidBody old = chunks.remove( location ); - if ( old != null ) { - space.remove( old ); - } - } - - saveMeshTasks.remove( location ); - } ); - } ) ); - } - private ChunkMesh loadMesh( final ChunkLocation location ) { -// getLogger().info( "Attempting to load " + location ); - final File file = new File( getDataFolder(), "meshes/" + location.getWorldName() + "/" + location.getX() + "/" + location.getZ() ); + final File file = new File( getDataFolder(), "meshes/" + location.getWorldName() + "/" + location.getX() + "/" + location.getZ() + ".cmesh" ); if ( file.exists() ) { -// try { -// final ChunkMesh mesh = FileUtil.readObject( file ); -// -// return mesh; -// } catch ( ClassNotFoundException | IOException e ) { -// e.printStackTrace(); -// } + try { + final byte[] data = Files.readAllBytes( file.toPath() ); + final ChunkMesh mesh = ChunkMeshUtil.deserialize( data ); + + return mesh; + } catch ( IOException e ) { + e.printStackTrace(); + } } return null; } private void saveMesh( final ChunkLocation location, final ChunkMesh mesh ) { - final File file = new File( getDataFolder(), "meshes/" + location.getWorldName() + "/" + location.getX() + "/" + location.getZ() ); + final File file = new File( getDataFolder(), "meshes/" + location.getWorldName() + "/" + location.getX() + "/" + location.getZ() + ".cmesh" ); file.getParentFile().mkdirs(); -// FileUtil.writeObject( mesh, file ); + final byte[] data = ChunkMeshUtil.serialize( mesh ); + + try { + Files.write( file.toPath(), data, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE, StandardOpenOption.WRITE ); + } catch ( IOException e ) { + e.printStackTrace(); + } } private Collection< Facet > getFacetsConservative( Chunk chunk ) { @@ -1407,11 +1382,7 @@ public class MiniePlugin extends JavaPlugin { } final long time = System.currentTimeMillis() - start; - getLogger().info( "Took " + time + "ms to mesh chunk " + chunk.getX() + ", " + chunk.getZ() + " into " + mesh.countMeshVertices() + " vertices and " + mesh.countMeshTriangles() + " triangles" ); - - chunk.removePluginChunkTicket( this ); - - loadMeshTasks.remove( chunkLocation ); +// getLogger().info( "Took " + time + "ms to mesh chunk " + chunk.getX() + ", " + chunk.getZ() + " into " + mesh.countMeshVertices() + " vertices and " + mesh.countMeshTriangles() + " triangles" ); } ); } ); } diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/Aabb.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/Aabb.java index a5d9c2a..7c666f9 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/Aabb.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/Aabb.java @@ -18,6 +18,19 @@ public class Aabb implements Serializable { public final int depth; public final int height; + public Aabb( final int minX, final int minY, final int minZ, final int maxX, final int maxY, final int maxZ ) { + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; + + this.width = maxX - minX; + this.depth = maxZ - minZ; + this.height = maxY - minY; + } + public Aabb( final BoundingBox box ) { this.minX = ( int ) box.getMinX(); this.maxX = ( int ) box.getMaxX(); diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/BlockDataTypeSet.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/BlockDataTypeSet.java index 64a1148..104485b 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/BlockDataTypeSet.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/BlockDataTypeSet.java @@ -2,6 +2,8 @@ package com.aaaaahhhhhhh.bananapuncher714.minietest.objects; import java.util.BitSet; +import com.aaaaahhhhhhh.bananapuncher714.minietest.util.BinaryWriter; + public class BlockDataTypeSet { private static final int BLOCK_SIZE; private static final BitSet[] SETS; @@ -21,7 +23,11 @@ public class BlockDataTypeSet { } } - private BitSet buffer; + protected BitSet buffer; + + public BlockDataTypeSet( final byte[] arr ) { + buffer = BitSet.valueOf( arr ); + } public BlockDataTypeSet( final int size ) { buffer = new BitSet( size * BLOCK_SIZE ); @@ -42,4 +48,8 @@ public class BlockDataTypeSet { } return BlockDataType.values()[ ord ]; } + + public byte[] toByteArray() { + return buffer.toByteArray(); + } } diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/ChunkMesh.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/ChunkMesh.java index f0cc757..9c52e91 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/ChunkMesh.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/objects/ChunkMesh.java @@ -1,6 +1,13 @@ package com.aaaaahhhhhhh.bananapuncher714.minietest.objects; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.TreeMap; import java.util.TreeSet; @@ -8,6 +15,10 @@ import org.bukkit.block.data.BlockData; import org.bukkit.util.BoundingBox; import org.bukkit.util.Vector; +import com.aaaaahhhhhhh.bananapuncher714.mesh.Point; +import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon; +import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.MinecraftPlane.PlaneAxis; +import com.aaaaahhhhhhh.bananapuncher714.minietest.util.BinaryWriter; import com.jme3.bullet.collision.shapes.infos.IndexedMesh; import com.jme3.math.Vector3f; @@ -28,7 +39,16 @@ public class ChunkMesh { boxes = new BoundingBox[ volume ][]; blockTypes = new BlockDataTypeSet( volume ); } - + + public ChunkMesh( Aabb box ) { + this.box = box; + + final int volume = this.box.getVolume(); + data = new int[ volume ]; + boxes = new BoundingBox[ volume ][]; + blockTypes = new BlockDataTypeSet( volume ); + } + public void setType( final int index, final BlockDataType type ) { blockTypes.set( index, type ); } @@ -95,4 +115,159 @@ public class ChunkMesh { return new IndexedMesh( positionArray, indexArray ); } + + public void writeTo( final BinaryWriter writer ) { + // Write the bounding box + writer.write( box.minX ); + writer.write( box.minY ); + writer.write( box.minZ ); + writer.write( box.maxX ); + writer.write( box.maxY ); + writer.write( box.maxZ ); + + // Write our data blocks + for ( final int hash : data ) { + writer.write( hash ); + } + + // Write our bounding boxes + for ( final BoundingBox[] boxArr : boxes ) { + // Write how many boxes + if ( boxArr == null ) { + writer.write( ( int ) 0 ); + } else { + writer.write( boxArr.length ); + + for ( final BoundingBox box : boxArr ) { + writer.write( box.getMinX() ); + writer.write( box.getMinY() ); + writer.write( box.getMinZ() ); + writer.write( box.getMaxX() ); + writer.write( box.getMaxY() ); + writer.write( box.getMaxZ() ); + } + } + } + + // Write our block types + final byte[] blockTypeBuf = blockTypes.toByteArray(); + writer.write( blockTypeBuf.length ); + writer.write( blockTypeBuf ); + + // Write our point references + writer.write( pointReferences.size() ); + for ( final VectorReference ref : pointReferences ) { + writer.write( ref.vector.getX() ); + writer.write( ref.vector.getY() ); + writer.write( ref.vector.getZ() ); + writer.write( ref.index ); + writer.write( ref.referenceCount ); + } + + // Write our planes + writer.write( planes.size() ); + for ( final Entry< MinecraftPlane, PlaneMesh > entry : planes.entrySet() ) { + final MinecraftPlane plane = entry.getKey(); + writer.write( plane.normal.ordinal() ); + writer.write( plane.offset ); + + final PlaneMesh mesh = entry.getValue(); + + // Write each region + writer.write( mesh.generatedRegions.size() ); + for ( final Polygon poly : mesh.generatedRegions ) { + writer.write( poly.getPoints().size() ); + for ( final Point p : poly.getPoints() ) { + writer.write( p.getX() ); + writer.write( p.getY() ); + } + } + + // Write each triangle + writer.write( mesh.completedPolygons.size() ); + for ( final IndexedPolygon poly : mesh.completedPolygons ) { + writer.write( poly.points.size() ); + for ( final VectorReference ref : poly.points ) { + writer.write( ref.index ); + } + } + } + } + + public static ChunkMesh readFrom( final ByteBuffer buffer ) { + final Aabb box = new Aabb( buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt() ); + + final ChunkMesh mesh = new ChunkMesh( box ); + + final int size = box.getVolume(); + + buffer.asIntBuffer().get( mesh.data ); + buffer.position( buffer.position() + ( mesh.data.length << 2 ) ); + + // Read our boxes + for ( int i = 0; i < size; ++i ) { + final int boxCount = buffer.getInt(); + if ( boxCount > 0 ) { + final BoundingBox[] boxes = new BoundingBox[ boxCount ]; + for ( int j = 0; j < boxCount; ++j ) { + boxes[ j ] = new BoundingBox( buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble(), buffer.getDouble() ); + } + mesh.boxes[ i ] = boxes; + } + } + + // Read the block types + final int blockTypeBufSize = buffer.getInt(); + final byte[] bitSet = new byte[ blockTypeBufSize ]; + buffer.get( bitSet ); + mesh.blockTypes = new BlockDataTypeSet( bitSet ); + + // Read the plane references + final int pointCount = buffer.getInt(); + final VectorReference[] references = new VectorReference[ pointCount ]; + for ( int i = 0; i < pointCount; ++i ) { + final VectorReference reference = new VectorReference( new Vector( buffer.getDouble(), buffer.getDouble(), buffer.getDouble() ) ); + reference.index = buffer.getInt(); + reference.referenceCount = buffer.getInt(); + references[ reference.index ] = reference; + mesh.pointReferences.add( reference ); + } + + // Get the planes + final int planeCount = buffer.getInt(); + for ( int i = 0; i < planeCount; ++i ) { + final MinecraftPlane plane = new MinecraftPlane( PlaneAxis.values()[ buffer.getInt() ], buffer.getDouble() ); + + final PlaneMesh planeMesh = new PlaneMesh(); + planeMesh.generatedRegions = new ArrayDeque< Polygon >(); + planeMesh.completedPolygons = new ArrayDeque< IndexedPolygon >(); + final int regionCount = buffer.getInt(); + for ( int j = 0; j < regionCount; ++j ) { + final int vertexCount = buffer.getInt(); + + final List< Point > points = new ArrayList< Point >(); + for ( int k = 0; k < vertexCount; ++k ) { + points.add( new Point( buffer.getDouble(), buffer.getDouble() ) ); + } + + planeMesh.generatedRegions.add( new Polygon( points ) ); + } + + final int triangleCount = buffer.getInt(); + for ( int j = 0; j < triangleCount; ++j ) { + final int vertexCount = buffer.getInt(); + + final IndexedPolygon triangle = new IndexedPolygon(); + for ( int k = 0; k < vertexCount; ++k ) { + triangle.points.add( references[ buffer.getInt() ] ); + } + + planeMesh.completedPolygons.add( triangle ); + } + + mesh.planes.put( plane, planeMesh ); + } + + return mesh; + } } diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/BinaryWriter.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/BinaryWriter.java new file mode 100644 index 0000000..963cf2c --- /dev/null +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/BinaryWriter.java @@ -0,0 +1,74 @@ +package com.aaaaahhhhhhh.bananapuncher714.minietest.util; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; + +/** + * Big/little endian binary writer + */ +public class BinaryWriter { + protected final ByteArrayOutputStream buffer; + + public BinaryWriter() { + buffer = new ByteArrayOutputStream(); + } + + public BinaryWriter( final int startSize ) { + buffer = new ByteArrayOutputStream( startSize ); + } + + public void write( final long val, final int length ) { + for ( int i = length - 1; i >= 0; i-- ) { + write( ( byte ) ( ( val >> ( i << 3 ) ) & 0xFFL ) ); + } + } + + public void write( final int val ) { + write( val, 4 ); + } + + public void write( final short val ) { + write( val, 2 ); + } + + public void write( final double val ) { + final byte[] bytes = new byte[ 8 ]; + ByteBuffer.wrap( bytes ).putDouble( val ); + write( bytes ); + } + + public void writeLittle( final long val, final int length ) { + for ( int i = 0; i < length; i++ ) { + write( ( byte ) ( ( val >> ( i << 3 ) ) & 0xFFL ) ); + } + } + + public void write( final long[] a, final int length ) { + for ( long l : a ) { + write( l, length ); + } + } + + public void write( final byte b ) { + buffer.write( b ); + } + + public void write( String str, final int length ) { + final byte[] bytes = str.getBytes(); + final byte[] res = new byte[ length ]; + for ( int i = 0; i < bytes.length && i < length; ++i ) { + res[ i ] = bytes[ i ]; + } + + write( res ); + } + + public void write( final byte[] a ) { + buffer.write( a, 0, a.length ); + } + + public byte[] toByteArray() { + return buffer.toByteArray(); + } +} diff --git a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/ChunkMeshUtil.java b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/ChunkMeshUtil.java index 80c2ed6..3a16204 100644 --- a/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/ChunkMeshUtil.java +++ b/src/main/java/com/aaaaahhhhhhh/bananapuncher714/minietest/util/ChunkMeshUtil.java @@ -1,5 +1,41 @@ package com.aaaaahhhhhhh.bananapuncher714.minietest.util; -public class ChunkMeshUtil { +import java.nio.ByteBuffer; +import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkMesh; + +public class ChunkMeshUtil { + public static byte[] serialize( final ChunkMesh mesh ) { + final BinaryWriter writer = new BinaryWriter(); + + // Header + writer.write( "cmesh1", 16 ); + + // How many chunk meshes are we providing + writer.write( 1, 4 ); + + mesh.writeTo( writer ); + + return writer.toByteArray(); + } + + public static ChunkMesh deserialize( final byte[] data ) { + final ByteBuffer reader = ByteBuffer.wrap( data ); + + final byte[] headerArr = new byte[ 16 ]; + reader.get( headerArr ); + final String header = new String( headerArr ).trim(); + if ( header.equals( "cmesh1" ) ) { + // This is how many chunk meshes this data contains + final int amount = reader.getInt(); + + if ( amount > 1 ) { + // Some data is getting discarded... + } + + return ChunkMesh.readFrom( reader ); + } + + throw new IllegalStateException( "Cannot read invalid header: '" + header + "'" ); + } }