Add de/serialization for chunk meshes
Add half-baked loading and saving to avoid having to mesh the chunk each time
This commit is contained in:
@@ -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" );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user