diff --git a/plugin/environment/pom.xml b/plugin/environment/pom.xml
index 70c1ee1..f5a90c5 100644
--- a/plugin/environment/pom.xml
+++ b/plugin/environment/pom.xml
@@ -66,19 +66,6 @@
21
-
- org.apache.maven.plugins
- maven-jar-plugin
- 3.5.0
-
-
-
- true
- com.aaaaahhhhhhh.bananapuncher714.minietest.MeshingTest6
-
-
-
-
\ No newline at end of file
diff --git a/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/EnvironmentHandler.java b/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/EnvironmentHandler.java
new file mode 100644
index 0000000..5e4af7c
--- /dev/null
+++ b/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/EnvironmentHandler.java
@@ -0,0 +1,10 @@
+package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
+
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkLocation;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkMesh;
+
+public interface EnvironmentHandler {
+ public void onMeshLoad( ChunkLocation location, ChunkMesh mesh );
+ public void onMeshUnload( ChunkLocation location, ChunkMesh mesh );
+ public void onMeshUpdate( ChunkLocation location, ChunkMesh mesh );
+}
diff --git a/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/MeshEnvironment.java b/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/MeshEnvironment.java
index 6bf6c31..1895605 100644
--- a/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/MeshEnvironment.java
+++ b/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/MeshEnvironment.java
@@ -59,36 +59,34 @@ import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil;
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil.BlockShapeType;
public class MeshEnvironment {
- private JavaPlugin plugin;
- private File cacheDirectory;
-
- private Map< ChunkLocation, Collection< Location > > updateLocations = new TreeMap< ChunkLocation, Collection< Location > >();
- private Map< ChunkLocation, ChunkMesh > chunkMeshes = new HashMap< ChunkLocation, ChunkMesh >();
-
- private Map< ChunkLocation, ChunkTask > tasks = new HashMap< ChunkLocation, ChunkTask >();
-
- private ChunkMeshCache cache;
-
- private ReentrantLock lock = new ReentrantLock();
-
+ private JavaPlugin plugin;
+ private File cacheDirectory;
+
+ private Map< ChunkLocation, Collection< Location > > updateLocations = new TreeMap< ChunkLocation, Collection< Location > >();
+ private Map< ChunkLocation, ChunkMesh > chunkMeshes = new HashMap< ChunkLocation, ChunkMesh >();
+
+ private Map< ChunkLocation, ChunkTask > tasks = new HashMap< ChunkLocation, ChunkTask >();
+
+ private ChunkMeshCache cache;
+
+ private EnvironmentHandler handler;
+
+ private ReentrantLock lock = new ReentrantLock();
+
final BukkitTask task = Bukkit.getScheduler().runTaskTimer( plugin, () -> {
-// final Collection< ChunkLocation > locations = new ArrayDeque< ChunkLocation >();
lock.lock();
updateLocations.entrySet().stream().forEach( e -> {
ChunkMesh mesh = chunkMeshes.get( e.getKey() );
if ( mesh != null ) {
final Collection< Location > locs = update( mesh, e.getValue() );
e.getValue().removeAll( locs );
-// if ( !locs.isEmpty() ) {
-// locations.add( e.getKey() );
-// }
+
+ if ( handler != null && !locs.isEmpty() ) {
+ handler.onMeshUpdate( e.getKey(), mesh );
+ }
}
} );
-// if ( !locations.isEmpty() ) {
-// space.activateAll( true );
-// }
-
updateLocations.values().removeIf( c -> c.isEmpty() );
lock.unlock();
}, 1, 1 );
@@ -103,48 +101,48 @@ public class MeshEnvironment {
lock.unlock();
return locations;
};
-
- private Listener eventListener = new Listener() {
+
+ private Listener eventListener = new Listener() {
@EventHandler
private void onChunkLoad( ChunkLoadEvent event ) {
- final ChunkLocation location = new ChunkLocation( event.getChunk() );
-
- lock.lock();
- try {
- if ( chunkMeshes.containsKey( location ) ) {
- // Do nothing
- } else {
- final ChunkTask task = tasks.get( location );
- if ( task == null ) {
- loadMesh( location );
- } else if ( task.type == ChunkTaskType.LOAD ) {
- // Do nothing
- } else if ( task.type == ChunkTaskType.UNLOAD ) {
- // Do nothing
- }
- }
- } finally {
- lock.unlock();
- }
-
- /*
- * Order of operations:
- * - Check if:
- * - The mesh is loaded
- * - Do nothing
- * - There is an unload queued
- * - Do nothing
- * - There is a load queued
- * - Do nothing
- * - Nothing is queued
- * - Queue a load
- */
+ final ChunkLocation location = new ChunkLocation( event.getChunk() );
+
+ lock.lock();
+ try {
+ if ( chunkMeshes.containsKey( location ) ) {
+ // Do nothing
+ } else {
+ final ChunkTask task = tasks.get( location );
+ if ( task == null ) {
+ loadMesh( location );
+ } else if ( task.type == ChunkTaskType.LOAD ) {
+ // Do nothing
+ } else if ( task.type == ChunkTaskType.UNLOAD ) {
+ // Do nothing
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ /*
+ * Order of operations:
+ * - Check if:
+ * - The mesh is loaded
+ * - Do nothing
+ * - There is an unload queued
+ * - Do nothing
+ * - There is a load queued
+ * - Do nothing
+ * - Nothing is queued
+ * - Queue a load
+ */
}
-
+
@EventHandler
private void onChunkUnload( ChunkUnloadEvent event ) {
final ChunkLocation location = new ChunkLocation( event.getChunk() );
-
+
lock.lock();
try {
if ( chunkMeshes.containsKey( location ) ) {
@@ -168,7 +166,7 @@ public class MeshEnvironment {
} finally {
lock.unlock();
}
-
+
/*
* Order of operations:
* - Check if:
@@ -182,106 +180,119 @@ public class MeshEnvironment {
* - Queue an unload
*/
}
-
+
@EventHandler( priority = EventPriority.MONITOR )
private void onBlockUpdateEvent( final BlockPhysicsEvent event ) {
final ChunkLocation chunkLocation = new ChunkLocation( event.getBlock().getChunk() );
getLocationFor.apply( chunkLocation ).add( event.getBlock().getLocation() );
}
};
-
- public MeshEnvironment( JavaPlugin plugin, File cacheDirectory ) {
- this.plugin = plugin;
- this.cacheDirectory = cacheDirectory;
- cache = new ChunkMeshCache( new File( cacheDirectory, "meshes" ) );
- }
-
- public void activate() {
- Bukkit.getPluginManager().registerEvents( eventListener, plugin );
- }
-
- public void deactivate() {
- HandlerList.unregisterAll( eventListener );
- }
-
- private void completeTask( ChunkLocation location ) {
- lock.lock();
- try {
- final ChunkTask task = tasks.remove( location );
- if ( task.type == ChunkTaskType.LOAD ) {
- if ( !location.isLoaded() ) {
- // Update all and unload
- unloadMesh( location );
- } else {
- // Chunk is still loaded, do nothing
- }
- } else if ( task.type == ChunkTaskType.UNLOAD ) {
- if ( !location.isLoaded() ) {
- // Check if there are any updates that we missed
- // If so, then queue another unload with the newest mesh
- // Otherwise, remove the location from our list of locations
- if ( updateLocations.containsKey( location ) ) {
- unloadMesh( location );
- } else {
- chunkMeshes.remove( location );
- }
- } else {
- // Chunk is loaded, but we saved. Do nothing
- }
- }
- } finally {
- lock.unlock();
- }
- }
-
- private void loadMesh( ChunkLocation location ) {
- ChunkTask task = new ChunkTask();
- lock.lock();
- try {
- tasks.put( location, task );
-
- task.type = ChunkTaskType.LOAD;
- task.task = Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
- final ChunkMesh mesh = cache.load( location );
- if ( mesh != null ) {
- lock.lock();
- try {
- // Save the mesh
- if ( !chunkMeshes.containsKey( location ) ) {
- chunkMeshes.put( location, mesh );
- }
- completeTask( location );
- } finally {
- lock.unlock();
- }
- } else {
- // Process and generate the mesh
- Bukkit.getScheduler().runTask( plugin, () -> {
- generateMesh( plugin, location.getWorld(), location.getChunk(), m -> {
- lock.lock();
- try {
- chunkMeshes.put( location, m );
-
- completeTask( location );
- } finally {
- lock.unlock();
- }
- } );
- } );
- }
- } );
- } finally {
- lock.unlock();
- }
- }
+ public MeshEnvironment( JavaPlugin plugin, File cacheDirectory ) {
+ this.plugin = plugin;
+ this.cacheDirectory = cacheDirectory;
+ cache = new ChunkMeshCache( new File( cacheDirectory, "meshes" ) );
+ }
- private void unloadMesh( ChunkLocation location ) {
- ChunkTask task = new ChunkTask();
+ public void activate() {
+ Bukkit.getPluginManager().registerEvents( eventListener, plugin );
+ }
+
+ public void deactivate() {
+ HandlerList.unregisterAll( eventListener );
+ }
+
+ public void load( ChunkLocation location ) {
+ loadMesh( location );
+ }
+
+ public void setHandler( EnvironmentHandler handler ) {
+ this.handler = handler;
+ }
+
+ private void completeTask( ChunkLocation location ) {
+ lock.lock();
+ try {
+ final ChunkTask task = tasks.remove( location );
+ if ( task.type == ChunkTaskType.LOAD ) {
+ handler.onMeshLoad( location, chunkMeshes.get( location ) );
+ if ( !location.isLoaded() ) {
+ // Update all and unload
+ unloadMesh( location );
+ } else {
+ // Chunk is still loaded, do nothing
+ }
+ } else if ( task.type == ChunkTaskType.UNLOAD ) {
+ if ( !location.isLoaded() ) {
+ // Check if there are any updates that we missed
+ // If so, then queue another unload with the newest mesh
+ // Otherwise, remove the location from our list of locations
+ if ( updateLocations.containsKey( location ) ) {
+ unloadMesh( location );
+ } else {
+ final ChunkMesh mesh = chunkMeshes.remove( location );
+
+ if ( handler != null ) {
+ handler.onMeshUnload( location, mesh );
+ }
+ }
+ } else {
+ // Chunk is loaded, but we saved. Do nothing
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private void loadMesh( ChunkLocation location ) {
+ ChunkTask task = new ChunkTask();
+ lock.lock();
+ try {
+ tasks.put( location, task );
+
+ task.type = ChunkTaskType.LOAD;
+ task.task = Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
+ final ChunkMesh mesh = cache.load( location );
+ if ( mesh != null ) {
+ lock.lock();
+ try {
+ // Save the mesh
+ if ( !chunkMeshes.containsKey( location ) ) {
+ chunkMeshes.put( location, mesh );
+ }
+
+ completeTask( location );
+ } finally {
+ lock.unlock();
+ }
+ } else {
+ // Process and generate the mesh
+ Bukkit.getScheduler().runTask( plugin, () -> {
+ generateMesh( plugin, location.getWorld(), location.getChunk(), m -> {
+ lock.lock();
+ try {
+ chunkMeshes.put( location, m );
+
+ completeTask( location );
+ } finally {
+ lock.unlock();
+ }
+ } );
+ } );
+ }
+ } );
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private void unloadMesh( ChunkLocation location ) {
+ ChunkTask task = new ChunkTask();
lock.lock();
try {
tasks.put( location, task );
-
+
if ( !applyUpdatesToMesh( location ) ) {
// Not good! Piston issue somewhere
}
@@ -297,7 +308,7 @@ public class MeshEnvironment {
} else {
// Not really good
}
-
+
completeTask( location );
} finally {
lock.unlock();
@@ -306,50 +317,55 @@ public class MeshEnvironment {
} finally {
lock.unlock();
}
- }
-
- private boolean applyUpdatesToMesh( ChunkLocation location ) {
- lock.lock();
- try {
- final Collection< Location > locations = updateLocations.get( location );
- final ChunkMesh mesh = chunkMeshes.get( location );
-
- if ( locations != null && !locations.isEmpty() && mesh != null ) {
- locations.removeAll( update( mesh, locations ) );
-
- return locations.isEmpty();
- }
- } finally {
- lock.unlock();
- }
-
- return true;
- }
-
- private static void generateSpareMesh( JavaPlugin plugin, World world, Collection< Vector > locations, Consumer< SparseMesh > callback ) {
+ }
+
+ private boolean applyUpdatesToMesh( ChunkLocation location ) {
+ lock.lock();
+ try {
+ final Collection< Location > locations = updateLocations.get( location );
+ final ChunkMesh mesh = chunkMeshes.get( location );
+
+ if ( locations != null && !locations.isEmpty() && mesh != null ) {
+ final Collection< Location > removed = update( mesh, locations );
+ locations.removeAll( removed );
+
+ if ( handler != null && !removed.isEmpty() ) {
+ handler.onMeshUpdate( location, mesh );
+ }
+
+ return locations.isEmpty();
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ return true;
+ }
+
+ public static void generateSpareMesh( JavaPlugin plugin, World world, Collection< Vector > locations, Consumer< SparseMesh > callback ) {
if ( locations.isEmpty() ) {
return;
}
-
- boolean set = false;
- Vector min = new Vector();
- Vector max = new Vector();
-
- for ( Vector v : locations ) {
- if ( !set ) {
- set = true;
-
- min = v.toBlockVector();
- max = v.toBlockVector();
- } else {
- min = Vector.getMinimum( min, v.toBlockVector() );
- max = Vector.getMaximum( max, v.toBlockVector() );
- }
- }
-
+
+ boolean set = false;
+ Vector min = new Vector();
+ Vector max = new Vector();
+
+ for ( Vector v : locations ) {
+ if ( !set ) {
+ set = true;
+
+ min = v.toBlockVector();
+ max = v.toBlockVector();
+ } else {
+ min = Vector.getMinimum( min, v.toBlockVector() );
+ max = Vector.getMaximum( max, v.toBlockVector() );
+ }
+ }
+
final SparseMesh sparseMesh = new SparseMesh( min, max );
final Map< MinecraftPlane, Collection< Polygon > > polygons = new TreeMap< MinecraftPlane, Collection< Polygon > >();
-
+
{
final Function< MinecraftPlane, Collection< Polygon > > getPolygons = p -> {
Collection< Polygon > polygonCollection = polygons.get( p );
@@ -359,29 +375,29 @@ public class MeshEnvironment {
}
return polygonCollection;
};
-
+
for ( final Vector location : locations ) {
- final Block block = world.getBlockAt( location.getBlockX(), location.getBlockY(), location.getBlockZ() );
-
- if ( !( block.isEmpty() && block.isLiquid() ) ) {
+ final Block block = world.getBlockAt( location.getBlockX(), location.getBlockY(), location.getBlockZ() );
+
+ if ( !( block.isEmpty() && block.isLiquid() ) ) {
final BlockData data = block.getBlockData();
final Location loc = block.getLocation();
-
+
// Start the box at 0, 0, 0
final BoundingBox[] boundingBoxes = NmsUtil.getShape( data, loc, BlockShapeType.COLLISION_SHAPE ).e().stream().map( aabb -> new BoundingBox( aabb.a, aabb.b, aabb.c, aabb.d, aabb.e, aabb.f ) ).toArray( BoundingBox[]::new );
-
+
final BlockDataInformation information = new BlockDataInformation( data );
-
+
if ( boundingBoxes.length == 0 ) {
// No bounding box
- information.type = BlockDataType.NONE;
+ information.type = BlockDataType.NONE;
} else if ( boundingBoxes.length == 1 && boundingBoxes[ 0 ].getVolume() == 1 ) {
// Solid
- information.type = BlockDataType.SOLID;
+ information.type = BlockDataType.SOLID;
} else {
- information.type = BlockDataType.COMPLEX;
+ information.type = BlockDataType.COMPLEX;
// Complex bounding shape
-
+
// Add the voxel shape to the chunk mesh
for ( final BoundingBox aabb : boundingBoxes ) {
final double minX = aabb.getMinX() + loc.getBlockX() - min.getBlockX();
@@ -390,26 +406,26 @@ public class MeshEnvironment {
final double maxX = aabb.getMaxX() + loc.getBlockX() - min.getBlockX();
final double maxY = aabb.getMaxY() + loc.getBlockY() - min.getBlockY();
final double maxZ = aabb.getMaxZ() + loc.getBlockZ() - min.getBlockZ();
-
+
final Polygon xPoly = new Polygon( Arrays.asList(
new Point( minY, minZ ),
new Point( minY, maxZ ),
new Point( maxY, maxZ ),
new Point( maxY, minZ )
- ) );
+ ) );
final Polygon yPoly = new Polygon( Arrays.asList(
new Point( minX, minZ ),
new Point( minX, maxZ ),
new Point( maxX, maxZ ),
new Point( maxX, minZ )
- ) );
+ ) );
final Polygon zPoly = new Polygon( Arrays.asList(
new Point( minX, minY ),
new Point( minX, maxY ),
new Point( maxX, maxY ),
new Point( maxX, minY )
- ) );
-
+ ) );
+
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, minX ) ).add( xPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, maxX ) ).add( xPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, minY ) ).add( yPoly );
@@ -417,89 +433,89 @@ public class MeshEnvironment {
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, minZ ) ).add( zPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, maxZ ) ).add( zPoly );
}
-
+
information.boundingBoxes = boundingBoxes;
sparseMesh.setBlockDataInformationFor( information, loc.toVector().subtract( min ) );
}
- }
+ }
}
-
+
// Now generate facets for each solid block based on if the side they are on is visible
for ( final Vector location : locations ) {
- final Vector blockVector = location.toBlockVector().subtract( min );
- final BlockDataInformation info = sparseMesh.getBlockDataInformationFor( blockVector );
- if ( info != null ) {
- if ( info.type == BlockDataType.SOLID ) {
- final Polygon xPoly = new Polygon( Arrays.asList(
- new Point( blockVector.getY(), blockVector.getZ() ),
- new Point( blockVector.getY(), blockVector.getZ() + 1 ),
- new Point( blockVector.getY() + 1, blockVector.getZ() + 1 ),
- new Point( blockVector.getY() + 1, blockVector.getZ() )
- ) );
- final Polygon yPoly = new Polygon( Arrays.asList(
- new Point( blockVector.getX(), blockVector.getZ() ),
- new Point( blockVector.getX(), blockVector.getZ() + 1 ),
- new Point( blockVector.getX() + 1, blockVector.getZ() + 1 ),
- new Point( blockVector.getX() + 1, blockVector.getZ() )
- ) );
- final Polygon zPoly = new Polygon( Arrays.asList(
- new Point( blockVector.getX(), blockVector.getY() ),
- new Point( blockVector.getX(), blockVector.getY() + 1 ),
- new Point( blockVector.getX() + 1, blockVector.getY() + 1 ),
- new Point( blockVector.getX() + 1, blockVector.getY() )
- ) );
-
- BlockDataInformation adjacent;
- if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().subtract( new Vector( 0, 1, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
- getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, blockVector.getY() ) ).add( yPoly );
- }
-
- if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().add( new Vector( 0, 1, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
- getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, blockVector.getY() + 1 ) ).add( yPoly );
- }
-
- if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().subtract( new Vector( 0, 0, 1 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
- getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, blockVector.getZ() ) ).add( zPoly );
- }
-
- if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().add( new Vector( 0, 0, 1 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
- getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, blockVector.getZ() + 1 ) ).add( zPoly );
- }
-
- if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().subtract( new Vector( 1, 0, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
- getPolygons.apply( new MinecraftPlane( PlaneAxis.X, blockVector.getX() ) ).add( xPoly );
- }
-
- if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().add( new Vector( 1, 0, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
- getPolygons.apply( new MinecraftPlane( PlaneAxis.X, blockVector.getX() + 1 ) ).add( xPoly );
- }
- }
- } else {
- // Shouldn't be null btw
- }
+ final Vector blockVector = location.toBlockVector().subtract( min );
+ final BlockDataInformation info = sparseMesh.getBlockDataInformationFor( blockVector );
+ if ( info != null ) {
+ if ( info.type == BlockDataType.SOLID ) {
+ final Polygon xPoly = new Polygon( Arrays.asList(
+ new Point( blockVector.getY(), blockVector.getZ() ),
+ new Point( blockVector.getY(), blockVector.getZ() + 1 ),
+ new Point( blockVector.getY() + 1, blockVector.getZ() + 1 ),
+ new Point( blockVector.getY() + 1, blockVector.getZ() )
+ ) );
+ final Polygon yPoly = new Polygon( Arrays.asList(
+ new Point( blockVector.getX(), blockVector.getZ() ),
+ new Point( blockVector.getX(), blockVector.getZ() + 1 ),
+ new Point( blockVector.getX() + 1, blockVector.getZ() + 1 ),
+ new Point( blockVector.getX() + 1, blockVector.getZ() )
+ ) );
+ final Polygon zPoly = new Polygon( Arrays.asList(
+ new Point( blockVector.getX(), blockVector.getY() ),
+ new Point( blockVector.getX(), blockVector.getY() + 1 ),
+ new Point( blockVector.getX() + 1, blockVector.getY() + 1 ),
+ new Point( blockVector.getX() + 1, blockVector.getY() )
+ ) );
+
+ BlockDataInformation adjacent;
+ if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().subtract( new Vector( 0, 1, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
+ getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, blockVector.getY() ) ).add( yPoly );
+ }
+
+ if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().add( new Vector( 0, 1, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
+ getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, blockVector.getY() + 1 ) ).add( yPoly );
+ }
+
+ if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().subtract( new Vector( 0, 0, 1 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
+ getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, blockVector.getZ() ) ).add( zPoly );
+ }
+
+ if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().add( new Vector( 0, 0, 1 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
+ getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, blockVector.getZ() + 1 ) ).add( zPoly );
+ }
+
+ if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().subtract( new Vector( 1, 0, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
+ getPolygons.apply( new MinecraftPlane( PlaneAxis.X, blockVector.getX() ) ).add( xPoly );
+ }
+
+ if ( ( adjacent = sparseMesh.getBlockDataInformationFor( blockVector.clone().add( new Vector( 1, 0, 0 ) ) ) ) == null || adjacent.type != BlockDataType.SOLID ) {
+ getPolygons.apply( new MinecraftPlane( PlaneAxis.X, blockVector.getX() + 1 ) ).add( xPoly );
+ }
+ }
+ } else {
+ // Shouldn't be null btw
+ }
}
}
-
+
Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
// Mesh the data and get the output
final MeshResult meshResult = generateMeshFrom( polygons );
-
+
sparseMesh.planes = meshResult.meshes;
sparseMesh.pointReferences = meshResult.points;
-
+
callback.accept( sparseMesh );
} );
}
-
+
// TODO Use ChunkSnapshot in the future
- private static void generateMesh( JavaPlugin plugin, World world, final Chunk chunk, Consumer< ChunkMesh > callback ) {
+ public static void generateMesh( JavaPlugin plugin, World world, final Chunk chunk, Consumer< ChunkMesh > callback ) {
final int worldMinHeight = world.getMinHeight();
final int worldMaxHeight = world.getMaxHeight();
final int worldHeight = worldMaxHeight - worldMinHeight;
-
+
final ChunkMesh chunkMesh = new ChunkMesh( new BoundingBox( 0, 0, 0, 16, worldHeight, 16 ) );
final Map< MinecraftPlane, Collection< Polygon > > polygons = new TreeMap< MinecraftPlane, Collection< Polygon > >();
-
+
{
final Function< MinecraftPlane, Collection< Polygon > > getPolygons = p -> {
Collection< Polygon > polygonCollection = polygons.get( p );
@@ -509,7 +525,7 @@ public class MeshEnvironment {
}
return polygonCollection;
};
-
+
// When we would have inserted each facet, instead, find or create the
// corresponding plane, and add directly.
// Also, create a chunk mesh and populate the structs
@@ -524,10 +540,10 @@ public class MeshEnvironment {
if ( !( block.isEmpty() && block.isLiquid() ) ) {
final BlockData data = block.getBlockData();
final Location loc = block.getLocation();
-
+
// Start the box at 0, 0, 0
final BoundingBox[] boundingBoxes = NmsUtil.getShape( data, loc, BlockShapeType.COLLISION_SHAPE ).e().stream().map( aabb -> new BoundingBox( aabb.a, aabb.b, aabb.c, aabb.d, aabb.e, aabb.f ) ).toArray( BoundingBox[]::new );
-
+
if ( boundingBoxes.length == 0 ) {
// No bounding box
chunkMesh.setType( index, BlockDataType.NONE );
@@ -537,10 +553,10 @@ public class MeshEnvironment {
} else {
chunkMesh.setType( index, BlockDataType.COMPLEX );
// Complex bounding shape
-
+
// Only store the old block data if we know it is not empty or solid!!
chunkMesh.setData( index, data );
-
+
// Add the voxel shape to the chunk mesh
for ( final BoundingBox aabb : boundingBoxes ) {
final double minX = aabb.getMinX() + x;
@@ -549,26 +565,26 @@ public class MeshEnvironment {
final double maxX = aabb.getMaxX() + x;
final double maxY = aabb.getMaxY() + relY;
final double maxZ = aabb.getMaxZ() + z;
-
+
final Polygon xPoly = new Polygon( Arrays.asList(
new Point( minY, minZ ),
new Point( minY, maxZ ),
new Point( maxY, maxZ ),
new Point( maxY, minZ )
- ) );
+ ) );
final Polygon yPoly = new Polygon( Arrays.asList(
new Point( minX, minZ ),
new Point( minX, maxZ ),
new Point( maxX, maxZ ),
new Point( maxX, minZ )
- ) );
+ ) );
final Polygon zPoly = new Polygon( Arrays.asList(
new Point( minX, minY ),
new Point( minX, maxY ),
new Point( maxX, maxY ),
new Point( maxX, minY )
- ) );
-
+ ) );
+
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, minX ) ).add( xPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, maxX ) ).add( xPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, minY ) ).add( yPoly );
@@ -576,7 +592,7 @@ public class MeshEnvironment {
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, minZ ) ).add( zPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, maxZ ) ).add( zPoly );
}
-
+
chunkMesh.setBoxes( index, boundingBoxes );
}
} else {
@@ -585,7 +601,7 @@ public class MeshEnvironment {
}
}
}
-
+
// Now generate facets for each solid block based on if the side they are on is visible
for ( int y = 0; y < worldHeight; ++y ) {
final int yIndex = y << 8;
@@ -599,41 +615,41 @@ public class MeshEnvironment {
new Point( y, z + 1 ),
new Point( y + 1, z + 1 ),
new Point( y + 1, z )
- ) );
+ ) );
final Polygon yPoly = new Polygon( Arrays.asList(
new Point( x, z ),
new Point( x, z + 1 ),
new Point( x + 1, z + 1 ),
new Point( x + 1, z )
- ) );
+ ) );
final Polygon zPoly = new Polygon( Arrays.asList(
new Point( x, y ),
new Point( x, y + 1 ),
new Point( x + 1, y + 1 ),
new Point( x + 1, y )
- ) );
-
+ ) );
+
// Check each face
if ( y == 0 || chunkMesh.getType( index - 256 ) != BlockDataType.SOLID ) {
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, y ) ).add( yPoly );
}
-
+
if ( y == worldHeight - 1 || chunkMesh.getType( index + 256 ) != BlockDataType.SOLID ) {
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, y + 1 ) ).add( yPoly );
}
-
+
if ( z == 0 || chunkMesh.getType( index - 16 ) != BlockDataType.SOLID ) {
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, z ) ).add( zPoly );
}
-
+
if ( z == 15 || chunkMesh.getType( index + 16 ) != BlockDataType.SOLID ) {
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, z + 1 ) ).add( zPoly );
}
-
+
if ( x == 0 || chunkMesh.getType( index - 1 ) != BlockDataType.SOLID ) {
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, x ) ).add( xPoly );
}
-
+
if ( x == 15 || chunkMesh.getType( index + 1 ) != BlockDataType.SOLID ) {
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, x + 1 ) ).add( xPoly );
}
@@ -642,23 +658,23 @@ public class MeshEnvironment {
}
}
}
-
+
Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
// Mesh the data and get the output
final MeshResult meshResult = generateMeshFrom( polygons );
-
+
chunkMesh.planes = meshResult.meshes;
chunkMesh.pointReferences = meshResult.points;
-
+
callback.accept( chunkMesh );
} );
}
-
- private static Collection< Location > update( final AbstractMesh mesh, final Collection< Location > locations ) {
+
+ public static Collection< Location > update( final AbstractMesh mesh, final Collection< Location > locations ) {
if ( locations.isEmpty() ) {
return Collections.emptySet();
}
-
+
final Collection< Location > canRemove = new ArrayDeque< Location >();
// Check each location
final Map< MinecraftPlane, Collection< Polygon > > polygons = new TreeMap< MinecraftPlane, Collection< Polygon > >();
@@ -670,41 +686,41 @@ public class MeshEnvironment {
}
return polygonCollection;
};
-
+
final BiFunction< List< BoundingBox >, BoundingBox[], Boolean > isEqual = ( a, b ) -> {
if ( a.size() == b.length ) {
for ( int i = 0; i < b.length; ++i ) {
final BoundingBox b1 = a.get( i );
final BoundingBox b2 = b[ i ];
-
+
if ( !b1.equals( b2 ) ) {
return false;
}
}
}
-
+
return true;
};
-
+
for ( final Location location : locations ) {
final BlockData data = location.getBlock().getBlockData();
-
+
// Defer moving piston updates until another tick when it's done moving
if ( data.getMaterial() == Material.MOVING_PISTON ) {
continue;
}
-
+
final Vector position = location.toVector().subtract( mesh.getMinimumCorner() );
final BlockDataInformation information = mesh.getBlockDataInformationFor( position );
if ( information != null ) {
- canRemove.add( location );
- if ( information.isBlockData( data ) ) {
- // Ignore if the block data has not changed
- continue;
- } else {
- final List< BoundingBox > boxes = NmsUtil.getShape( data, location, BlockShapeType.COLLISION_SHAPE ).e().stream()
+ canRemove.add( location );
+ if ( information.isBlockData( data ) ) {
+ // Ignore if the block data has not changed
+ continue;
+ } else {
+ final List< BoundingBox > boxes = NmsUtil.getShape( data, location, BlockShapeType.COLLISION_SHAPE ).e().stream()
.map( aabb -> new BoundingBox( aabb.a, aabb.b, aabb.c, aabb.d, aabb.e, aabb.f ) ).collect( Collectors.toList() );
- final BlockDataType oldType = information.type;
+ final BlockDataType oldType = information.type;
final BoundingBox[] otherBoxes = information.boundingBoxes;
if ( boxes.size() == 0 ) {
if ( oldType == BlockDataType.NONE ) {
@@ -714,7 +730,7 @@ public class MeshEnvironment {
BlockDataInformation newInfo = new BlockDataInformation( data );
newInfo.type = BlockDataType.NONE;
newInfo.boundingBoxes = null;
-
+
mesh.setBlockDataInformationFor( newInfo, position );
} else if ( boxes.size() == 1 && boxes.get( 0 ).getVolume() == 1 ) {
if ( oldType == BlockDataType.SOLID ) {
@@ -724,7 +740,7 @@ public class MeshEnvironment {
BlockDataInformation newInfo = new BlockDataInformation( data );
newInfo.type = BlockDataType.SOLID;
newInfo.boundingBoxes = null;
-
+
mesh.setBlockDataInformationFor( newInfo, position );
} else {
if ( otherBoxes != null && isEqual.apply( boxes, otherBoxes ) ) {
@@ -734,22 +750,22 @@ public class MeshEnvironment {
BlockDataInformation newInfo = new BlockDataInformation( data );
newInfo.type = BlockDataType.COMPLEX;
newInfo.boundingBoxes = boxes.toArray( BoundingBox[]::new );
-
+
mesh.setBlockDataInformationFor( newInfo, position );
}
-
+
if ( oldType == BlockDataType.SOLID ) {
boxes.add( new BoundingBox( 0, 0, 0, 1, 1, 1 ) );
} else if ( oldType == BlockDataType.COMPLEX ) {
if ( otherBoxes == null || otherBoxes.length == 0 ) {
throw new IllegalStateException( "Complex shape but no bounding boxes!" );
}
-
+
for ( final BoundingBox box : otherBoxes ) {
boxes.add( box );
}
}
-
+
// Add the voxel shape to the chunk mesh
for ( final BoundingBox aabb : boxes ) {
final double minX = aabb.getMinX() + position.getX();
@@ -758,26 +774,26 @@ public class MeshEnvironment {
final double maxX = aabb.getMaxX() + position.getX();
final double maxY = aabb.getMaxY() + position.getY();
final double maxZ = aabb.getMaxZ() + position.getZ();
-
+
final Polygon xPoly = new Polygon( Arrays.asList(
new Point( minY, minZ ),
new Point( minY, maxZ ),
new Point( maxY, maxZ ),
new Point( maxY, minZ )
- ) );
+ ) );
final Polygon yPoly = new Polygon( Arrays.asList(
new Point( minX, minZ ),
new Point( minX, maxZ ),
new Point( maxX, maxZ ),
new Point( maxX, minZ )
- ) );
+ ) );
final Polygon zPoly = new Polygon( Arrays.asList(
new Point( minX, minY ),
new Point( minX, maxY ),
new Point( maxX, maxY ),
new Point( maxX, minY )
- ) );
-
+ ) );
+
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, minX ) ).add( xPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.X, maxX ) ).add( xPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, minY ) ).add( yPoly );
@@ -785,63 +801,63 @@ public class MeshEnvironment {
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, minZ ) ).add( zPoly );
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, maxZ ) ).add( zPoly );
}
- }
+ }
}
}
-
+
// No polygons found... which means nothing changed
if ( polygons.isEmpty() ) {
return canRemove;
}
-
+
final Map< MinecraftPlane, Mesh< RegionSimple > > meshMap = new HashMap< MinecraftPlane, Mesh< RegionSimple > >();
-
+
// Now mesh the blocks asynchronously and combine, then do the bare minimum to regenerate the chunk mesh
for ( final Entry< MinecraftPlane, Collection< Polygon > > entry : polygons.entrySet() ) {
final MinecraftPlane plane = entry.getKey();
final PlaneMesh planeMesh = mesh.planes.remove( plane );
final Mesh< RegionSimple > mesher = new Mesh< RegionSimple >( () -> new RegionSimple( GluWindingRule.ODD ) );
-
+
if ( planeMesh != null ) {
// Reset all indexed polygons(triangles) for this plane
for ( final IndexedTriangle poly : planeMesh.completedPolygons ) {
poly.getPoints().parallelStream().forEach( p -> --p.referenceCount );
}
-
+
// Add all polygons that were previously generated
for ( final Polygon polygon : planeMesh.generatedRegions ) {
- mesher.addPolygon( polygon, RegionRuleWinding.CLOCKWISE );
+ mesher.addPolygon( polygon, RegionRuleWinding.CLOCKWISE );
}
}
for ( final Polygon polygon : entry.getValue() ) {
- mesher.addPolygon( polygon, RegionRuleWinding.CLOCKWISE );
+ mesher.addPolygon( polygon, RegionRuleWinding.CLOCKWISE );
}
-
+
meshMap.put( plane, mesher );
}
-
+
// Mesh each plane
final Map< MinecraftPlane, CompleteMesh > triangles = meshMap.entrySet().parallelStream().collect( Collectors.toConcurrentMap( e -> e.getKey(), e -> new CompleteMesh( e.getKey(), e.getValue() ) ) );
-
+
for ( final Entry< MinecraftPlane, CompleteMesh > entry : triangles.entrySet() ) {
final MinecraftPlane plane = entry.getKey();
final CompleteMesh completeMesh = entry.getValue();
final Collection< Polygon > tris = completeMesh.triangles;
final Mesh< RegionSimple > mesher = completeMesh.mesh;
-
+
// Ignore any planes that are empty
if ( !tris.isEmpty() ) {
final PlaneMesh newMesh = new PlaneMesh();
newMesh.completedPolygons = new ArrayList< IndexedTriangle >();
for ( final Polygon tri : tris ) {
- final IndexedTriangle newPoly = new IndexedTriangle();
+ final IndexedTriangle newPoly = new IndexedTriangle();
for ( int i = 0; i < tri.getPoints().size() && i < 3; ++i ) {
- final Point point = tri.getPoints().get( i );
+ final Point point = tri.getPoints().get( i );
final Vector vert = plane.convert( point );
-
+
// Find an existing vertex reference if one exists
final VectorReference tempRef = new VectorReference( vert );
-
+
VectorReference ref = null;
final VectorReference upperRef = mesh.pointReferences.ceiling( tempRef );
if ( upperRef != null && upperRef.vector.distanceSquared( vert ) < 1e-8 ) {
@@ -852,7 +868,7 @@ public class MeshEnvironment {
ref = lowerRef;
}
}
-
+
if ( ref == null ) {
ref = tempRef;
ref.referenceCount = 1;
@@ -860,39 +876,39 @@ public class MeshEnvironment {
} else {
++ref.referenceCount;
}
-
+
switch ( i ) {
- case 0:
- newPoly.setPoint1( ref );
- break;
- case 1:
- newPoly.setPoint2( ref );
- break;
- case 2:
- newPoly.setPoint3( ref );
- break;
- default:
- // Should never be here!
- }
+ case 0:
+ newPoly.setPoint1( ref );
+ break;
+ case 1:
+ newPoly.setPoint2( ref );
+ break;
+ case 2:
+ newPoly.setPoint3( ref );
+ break;
+ default:
+ // Should never be here!
+ }
}
newMesh.completedPolygons.add( newPoly );
}
-
+
// Save the generated regions as an intermediary step so we can re-use them later
newMesh.generatedRegions = mesher.getPolygons();
-
+
mesh.planes.put( plane, newMesh );
}
}
-
+
// Remove all vector references that have a reference count of 0
mesh.pointReferences.removeIf( v -> v.referenceCount <= 0 );
-
+
return canRemove;
}
-
- private static MeshResult generateMeshFrom( final Map< MinecraftPlane, Collection< Polygon > > polygons ) {
- final Map< MinecraftPlane, Mesh< RegionSimple > > meshMap = new HashMap< MinecraftPlane, Mesh< RegionSimple > >();
+
+ public static MeshResult generateMeshFrom( final Map< MinecraftPlane, Collection< Polygon > > polygons ) {
+ final Map< MinecraftPlane, Mesh< RegionSimple > > meshMap = new HashMap< MinecraftPlane, Mesh< RegionSimple > >();
for ( final Entry< MinecraftPlane, Collection< Polygon > > entry : polygons.entrySet() ) {
final MinecraftPlane plane = entry.getKey();
final Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> new RegionSimple( GluWindingRule.ODD ) );
@@ -901,20 +917,20 @@ public class MeshEnvironment {
}
meshMap.put( plane, mesh );
}
-
+
// Mesh each plane
final Map< MinecraftPlane, CompleteMesh > triangles = meshMap.entrySet().parallelStream().collect( Collectors.toConcurrentMap( e -> e.getKey(), e -> new CompleteMesh( e.getKey(), e.getValue() ) ) );
-
+
// Construct a series of indexed polygons
final TreeSet< VectorReference > vertices = new TreeSet< VectorReference >();
-
+
final MeshResult result = new MeshResult();
for ( final Entry< MinecraftPlane, CompleteMesh > entry : triangles.entrySet() ) {
final MinecraftPlane plane = entry.getKey();
final CompleteMesh completeMesh = entry.getValue();
final Collection< Polygon > tris = completeMesh.triangles;
final Mesh< RegionSimple > mesh = completeMesh.mesh;
-
+
// Ignore any planes that are empty
if ( !tris.isEmpty() ) {
final PlaneMesh newMesh = new PlaneMesh();
@@ -922,63 +938,63 @@ public class MeshEnvironment {
for ( final Polygon tri : tris ) {
final IndexedTriangle newPoly = new IndexedTriangle();
for ( int i = 0; i < tri.getPoints().size() && i < 3; ++i ) {
- final Point point = tri.getPoints().get( i );
- final Vector vert = plane.convert( point );
-
- final VectorReference tempRef = new VectorReference( vert );
- // Find an existing vertex reference if one exists
- VectorReference ref = null;
- if ( ref == null ) {
- final VectorReference upperRef = vertices.ceiling( tempRef );
- if ( upperRef != null && upperRef.vector.distanceSquared( vert ) < 1e-8 ) {
- ref = upperRef;
- } else {
- final VectorReference lowerRef = vertices.floor( tempRef );
- if ( lowerRef != null && lowerRef.vector.distanceSquared( vert ) < 1e-8 ) {
- ref = lowerRef;
- }
- }
- }
-
- if ( ref == null ) {
- ref = tempRef;
- ref.referenceCount = 1;
- vertices.add( ref );
- } else {
- ++ref.referenceCount;
- }
-
- switch ( i ) {
- case 0:
- newPoly.setPoint1( ref );
- break;
- case 1:
- newPoly.setPoint2( ref );
- break;
- case 2:
- newPoly.setPoint3( ref );
- break;
- default:
- // Should never be here!
- }
-
+ final Point point = tri.getPoints().get( i );
+ final Vector vert = plane.convert( point );
+
+ final VectorReference tempRef = new VectorReference( vert );
+ // Find an existing vertex reference if one exists
+ VectorReference ref = null;
+ if ( ref == null ) {
+ final VectorReference upperRef = vertices.ceiling( tempRef );
+ if ( upperRef != null && upperRef.vector.distanceSquared( vert ) < 1e-8 ) {
+ ref = upperRef;
+ } else {
+ final VectorReference lowerRef = vertices.floor( tempRef );
+ if ( lowerRef != null && lowerRef.vector.distanceSquared( vert ) < 1e-8 ) {
+ ref = lowerRef;
+ }
+ }
+ }
+
+ if ( ref == null ) {
+ ref = tempRef;
+ ref.referenceCount = 1;
+ vertices.add( ref );
+ } else {
+ ++ref.referenceCount;
+ }
+
+ switch ( i ) {
+ case 0:
+ newPoly.setPoint1( ref );
+ break;
+ case 1:
+ newPoly.setPoint2( ref );
+ break;
+ case 2:
+ newPoly.setPoint3( ref );
+ break;
+ default:
+ // Should never be here!
+ }
+
}
newMesh.completedPolygons.add( newPoly );
}
-
+
// Save the generated regions as an intermediary step so we can re-use them later
newMesh.generatedRegions = mesh.getPolygons();
-
+
result.meshes.put( plane, newMesh );
}
}
-
+
// Add all vector references
result.points = vertices;
-
+
return result;
}
-
+
private static class CompleteMesh {
Mesh< RegionSimple > mesh;
Collection< Polygon > triangles;
@@ -988,16 +1004,16 @@ public class MeshEnvironment {
triangles = mesh.meshify();
}
}
-
- private static class MeshResult {
- Map< MinecraftPlane, PlaneMesh > meshes;
- TreeSet< VectorReference > points;
+
+ public static class MeshResult {
+ Map< MinecraftPlane, PlaneMesh > meshes;
+ TreeSet< VectorReference > points;
}
-
+
private enum ChunkTaskType {
LOAD, UNLOAD
}
-
+
private class ChunkTask {
BukkitTask task;
ChunkTaskType type;
diff --git a/plugin/environment/src/main/resources/plugin.yml b/plugin/environment/src/main/resources/plugin.yml
index 6e7032b..b5fa074 100644
--- a/plugin/environment/src/main/resources/plugin.yml
+++ b/plugin/environment/src/main/resources/plugin.yml
@@ -1,14 +1,6 @@
name: MC-Mesh-Environment
-main: com.aaaaahhhhhhh.bananapuncher714.minietest.MiniePlugin
+main: com.aaaaahhhhhhh.bananapuncher714.mesh.environment.McMeshEnvironmentPlugin
version: 0.0.1
description: BulletJME and MC-Mesh integration
author: BananaPuncher714
-api-version: 1.13
-#libraries:
-#- com.github.stephengold:Minie:7.5.0
-
-commands:
- minie:
- description: Main Minie command
- aliases: []
- permission: minie
\ No newline at end of file
+api-version: 1.13
\ No newline at end of file
diff --git a/plugin/physics/pom.xml b/plugin/physics/pom.xml
new file mode 100644
index 0000000..1ba62d7
--- /dev/null
+++ b/plugin/physics/pom.xml
@@ -0,0 +1,53 @@
+
+ 4.0.0
+
+ com.aaaaahhhhhhh.bananapuncher714
+ mc-mesh-plugin
+ 0.0.1
+
+ mc-mesh-plugin-physics
+ 0.0.1-SNAPSHOT
+
+
+
+ org.spigotmc
+ spigot
+ 1.21.10-R0.1-SNAPSHOT
+ provided
+
+
+ com.aaaaahhhhhhh.bananapuncher714
+ mc-mesh-plugin-environment
+ 0.0.1-SNAPSHOT
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.1.0
+
+
+ package
+
+ shade
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.14.1
+
+ 21
+
+
+
+
+
\ No newline at end of file
diff --git a/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/AuxiliaryDisplayStruct.java b/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/AuxiliaryDisplayStruct.java
new file mode 100644
index 0000000..85ecf04
--- /dev/null
+++ b/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/AuxiliaryDisplayStruct.java
@@ -0,0 +1,22 @@
+package com.aaaaahhhhhhh.bananapuncher714.mesh.physics;
+
+import org.bukkit.entity.Display;
+import org.bukkit.util.Vector;
+
+class AuxiliaryDisplayStruct {
+ Display display;
+ Vector offset;
+ Vector scale;
+
+ AuxiliaryDisplayStruct( Display display, Vector offset ) {
+ this.display = display;
+ this.offset = offset.clone();
+ this.scale = new Vector( 1, 1, 1 );
+ }
+
+ AuxiliaryDisplayStruct setScale( Vector scale ) {
+ this.scale = scale.clone();
+
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/McMeshPhysicsPlugin.java b/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/McMeshPhysicsPlugin.java
new file mode 100644
index 0000000..693fbb3
--- /dev/null
+++ b/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/McMeshPhysicsPlugin.java
@@ -0,0 +1,342 @@
+package com.aaaaahhhhhhh.bananapuncher714.mesh.physics;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.BlockDisplay;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.util.BoundingBox;
+import org.bukkit.util.Transformation;
+import org.bukkit.util.Vector;
+import org.joml.Matrix4f;
+import org.joml.Quaternionf;
+
+import com.aaaaahhhhhhh.bananapuncher714.mesh.command.CommandParameters;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.command.SubCommand;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.command.executor.CommandExecutableMessage;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.InputValidatorDouble;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.InputValidatorInt;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.sender.SenderValidatorPlayer;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.EnvironmentHandler;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.MeshEnvironment;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkLocation;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkMesh;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil.BlockShapeType;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.PhysicsSpace.BroadphaseType;
+import com.jme3.bullet.collision.PhysicsCollisionEvent;
+import com.jme3.bullet.collision.PhysicsCollisionListener;
+import com.jme3.bullet.collision.PhysicsCollisionObject;
+import com.jme3.bullet.collision.shapes.BoxCollisionShape;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
+import com.jme3.bullet.collision.shapes.infos.IndexedMesh;
+import com.jme3.bullet.objects.PhysicsBody;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+
+public class McMeshPhysicsPlugin extends JavaPlugin {
+ private static final int TIME_STEPS = 5;
+ private static final float SIMULATION_SPEED = 1f;
+
+ private MeshEnvironment environment;
+
+ private PhysicsSpace space;
+ private boolean paused = false;
+ private boolean teleportOnDeactivate = false;
+ private float tick = 0;
+ private Map< PhysicsCollisionObject, AuxiliaryDisplayStruct > linkedDisplays = new HashMap< PhysicsCollisionObject, AuxiliaryDisplayStruct >();
+ private Set< PhysicsCollisionObject > active = new HashSet< PhysicsCollisionObject >();
+ private Map< ChunkLocation, PhysicsRigidBody > chunks = new HashMap< ChunkLocation, PhysicsRigidBody >();
+
+ @Override
+ public void onEnable() {
+ registerCommands();
+
+ space = new PhysicsSpace( BroadphaseType.DBVT );
+ space.setAccuracy( 1f / ( TIME_STEPS * 20f ) );
+
+ environment = new MeshEnvironment( this, new File( getDataFolder(), "environment" ) );
+ environment.setHandler( new EnvironmentHandler() {
+ @Override
+ public void onMeshLoad( ChunkLocation location, ChunkMesh mesh ) {
+ // 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 meshCollision = new MeshCollisionShape( true, nativeMesh );
+
+ // Create a rigid body
+ PhysicsRigidBody rigid = new PhysicsRigidBody( meshCollision, PhysicsBody.massForStatic );
+ rigid.setPhysicsLocation( new Vector3f( location.getX() << 4, location.getWorld().getMinHeight(), location.getZ() << 4 ) );
+ rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
+ rigid.setKinematic( false );
+
+ Bukkit.getScheduler().runTask( McMeshPhysicsPlugin.this, () -> {
+ space.addCollisionObject( rigid );
+
+ PhysicsRigidBody old = chunks.put( location, rigid );
+ if ( old != null ) {
+ space.remove( old );
+ }
+ } );
+ }
+
+ @Override
+ public void onMeshUnload( ChunkLocation location, ChunkMesh mesh ) {
+ space.removeCollisionObject( chunks.get( location ) );
+ }
+
+ @Override
+ public void onMeshUpdate( ChunkLocation location, ChunkMesh mesh ) {
+ // 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 meshCollision = new MeshCollisionShape( true, nativeMesh );
+
+ // Create a rigid body
+ PhysicsRigidBody rigid = new PhysicsRigidBody( meshCollision, PhysicsBody.massForStatic );
+ rigid.setPhysicsLocation( new Vector3f( location.getX() << 4, location.getWorld().getMinHeight(), location.getZ() << 4 ) );
+ rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
+ rigid.setKinematic( false );
+
+ Bukkit.getScheduler().runTask( McMeshPhysicsPlugin.this, () -> {
+ space.addCollisionObject( rigid );
+
+ PhysicsRigidBody old = chunks.put( location, rigid );
+ if ( old != null ) {
+ space.remove( old );
+ }
+ } );
+
+ space.activateAll( true );
+ }
+ } );
+
+ Bukkit.getScheduler().runTaskTimer( this, () -> {
+ if ( !paused ) {
+ // Update the real world with the physics space objects
+ space.distributeEvents();
+ space.update( tick, TIME_STEPS );
+
+ for ( Iterator< Entry< PhysicsCollisionObject, AuxiliaryDisplayStruct > > it = linkedDisplays.entrySet().iterator(); it.hasNext(); ) {
+ Entry< PhysicsCollisionObject, AuxiliaryDisplayStruct > entry = it.next();
+ PhysicsCollisionObject obj = entry.getKey();
+ AuxiliaryDisplayStruct disp = entry.getValue();
+
+ if ( !disp.display.isValid() ) {
+ it.remove();
+ space.removeCollisionObject( obj );
+
+ continue;
+ }
+
+ Location displayLocation = disp.display.getLocation();
+
+ // Do not calculate for this object if it is inactive
+ if ( !obj.isActive() ) {
+ if ( teleportOnDeactivate && active.remove( obj ) ) {
+ // Newly deactivated object, teleport the display to the actual position to prevent the translation from getting too large
+
+ Transformation currentTransformation = disp.display.getTransformation();
+ org.joml.Vector3f trans = currentTransformation.getTranslation();
+
+ displayLocation.add( trans.x(), trans.y(), trans.z() );
+ disp.display.teleport( displayLocation );
+
+ Transformation newTrans = new Transformation( new org.joml.Vector3f( 0, 0, 0 ), currentTransformation.getLeftRotation(), currentTransformation.getScale(), currentTransformation.getRightRotation() );
+ disp.display.setInterpolationDelay( 0 );
+ disp.display.setInterpolationDuration( 0 );
+ disp.display.setTransformation( newTrans );
+ }
+
+ continue;
+ } else {
+ active.add( obj );
+ }
+
+ Vector3f location = obj.getPhysicsLocation();
+ Quaternion rotation = new Quaternion();
+ obj.getPhysicsRotation( rotation );
+
+ Vector offsetVec = disp.offset;
+ Vector scale = disp.scale;
+ Matrix4f transformRot = new Matrix4f();
+ transformRot.set( new Quaternionf( rotation.getX(), rotation.getY(), rotation.getZ(), rotation.getW() ) );
+
+ transformRot.mul( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, - ( float ) offsetVec.getX(), - ( float ) offsetVec.getY(), - ( float ) offsetVec.getZ(), 1 );
+ transformRot.mul( ( float ) scale.getX(), 0, 0, 0, 0, ( float ) scale.getY(), 0, 0, 0, 0, ( float ) scale.getZ(), 0, 0, 0, 0, 1 );
+ Matrix4f transMat = new Matrix4f( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ( float ) ( location.getX() - displayLocation.getX() ), ( float ) ( location.getY() - displayLocation.getY() ), ( float ) ( location.getZ() - displayLocation.getZ() ), 1 );
+
+ disp.display.setInterpolationDelay( 0 );
+ disp.display.setInterpolationDuration( 2 );
+ disp.display.setTransformationMatrix( transMat.mul( transformRot ) );
+ }
+
+ tick += SIMULATION_SPEED / 20f;
+ }
+ }, 0, 1 );
+ }
+
+ private void registerCommands() {
+ new SubCommand( "minie" )
+ .addSenderValidator( new SenderValidatorPlayer() )
+ .add( new SubCommand( "spawn" )
+ .add( new SubCommand( new InputValidatorDouble( 0.01, 100 ) )
+ .defaultTo( this::spawnBlock ) )
+ .defaultTo( this::spawnBlock ) )
+ .add( new SubCommand( "toggle" )
+ .defaultTo( ( sender, args, params ) -> {
+ paused = !paused;
+ } ) )
+ .add( new SubCommand( "teleport" )
+ .defaultTo( ( sender, args, params ) -> {
+ teleportOnDeactivate = !teleportOnDeactivate;
+ } ) )
+ .add( new SubCommand( "impulse" )
+ .add( new SubCommand( new InputValidatorInt() )
+ .defaultTo( ( sender, args, params ) -> {
+ Player player = ( Player ) sender;
+ Location loc = player.getLocation();
+
+ impulse( new Vector3f( ( float ) loc.getX(), ( float ) loc.getY(), ( float ) loc.getZ() ), params.getLast( int.class ) );
+ } ) )
+ .defaultTo( ( sender, args, params ) -> {
+ Player player = ( Player ) sender;
+ Location loc = player.getLocation();
+
+ impulse( new Vector3f( ( float ) loc.getX(), ( float ) loc.getY(), ( float ) loc.getZ() ), 15f );
+ } ) )
+ .defaultTo( new CommandExecutableMessage( "An argument must be provided" ) )
+ .whenUnknown( new CommandExecutableMessage( "Unknown argument" ) )
+ .applyTo( getCommand( "minie" ) );
+ }
+
+ private void impulse( Vector3f location, float power ) {
+ PhysicsRigidBody rigid = new PhysicsRigidBody( new SphereCollisionShape( 20 ), 1f );
+ rigid.setPhysicsLocation( location );
+ space.contactTest( rigid, new PhysicsCollisionListener() {
+ @Override
+ public void collision( PhysicsCollisionEvent event ) {
+ if ( event.getObjectB() instanceof PhysicsRigidBody ) {
+ PhysicsRigidBody objB = ( PhysicsRigidBody ) event.getObjectB();
+
+ Vector3f rLoc = objB.getPhysicsLocation();
+ Vector3f to = rLoc.subtract( ( float ) location.getX(), ( float ) location.getY(), ( float ) location.getZ() );
+
+ float distSq = to.lengthSquared();
+
+ if ( distSq != 0 ) {
+ if ( power / distSq > 1 ) {
+ objB.applyCentralImpulse( to.normalize().mult( power / distSq ) );
+ }
+ }
+ }
+
+ }
+ } );
+ }
+
+ private void spawnBlock( final CommandSender sender, final String[] args, final CommandParameters params ) {
+ Player player = ( Player ) sender;
+ Location loc = player.getLocation();
+
+ // Optional scale
+ double scale = 1;
+ if ( params.size() > 2 ) {
+ scale = params.getFirst( double.class );
+ }
+
+ loc.setPitch( 0 );
+ loc.setDirection( new Vector( 0, 0, 0 ) );
+
+ BlockData displayData;
+ ItemStack item = player.getInventory().getItemInMainHand();
+ if ( item != null && item.getType() != Material.AIR && item.getType().isBlock() ) {
+ displayData = item.getType().createBlockData();
+ } else {
+ displayData = Material.TNT.createBlockData();
+ }
+
+ BoundingBox[] boxes = NmsUtil.convertFrom( NmsUtil.getShape( displayData, null, BlockShapeType.SHAPE ) );
+ Vector blockCenter = calculateCenter( boxes );
+
+ CollisionShape box;
+ if ( boxes.length == 0 ) {
+ player.sendMessage( "No bounding box detected! Wrong method?" );
+ return;
+ }
+
+ if ( boxes.length == 1 ) {
+ Vector min = boxes[ 0 ].getMin();
+ Vector center = boxes[ 0 ].getCenter().subtract( min );
+ box = new BoxCollisionShape( ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
+ } else {
+ CompoundCollisionShape compound = new CompoundCollisionShape();
+ for ( BoundingBox aabb : boxes ) {
+ Vector min = aabb.getMin();
+ Vector center = aabb.getCenter();
+ Vector half = center.clone().subtract( min );
+ center.subtract( blockCenter );
+ CollisionShape subShape = new BoxCollisionShape( ( float ) half.getX(), ( float ) half.getY(), ( float ) half.getZ() );
+ compound.addChildShape( subShape, ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
+ }
+ box = compound;
+ }
+
+ box.setScale( ( float ) scale );
+
+ PhysicsRigidBody rigid = new PhysicsRigidBody( box, 1f );
+
+ // Since the center of the rigid body and the box shape are different, we need to offset the location
+ rigid.setPhysicsLocation( new Vector3f( ( float ) ( loc.getX() + blockCenter.getX() ), ( float ) ( loc.getY() + blockCenter.getY() ), ( float ) ( loc.getZ() + blockCenter.getZ() ) ) );
+
+ // Convert the display direction vector to a quaternion
+ rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
+
+ rigid.setMass( rigid.getMass() * ( float ) ( scale * scale ) );
+
+ space.addCollisionObject( rigid );
+
+ // Create the display
+ BlockDisplay display = loc.getWorld().spawn( loc, BlockDisplay.class, d -> {
+ d.setBlock( displayData );
+ d.setViewRange( 1000 );
+ } );
+
+ linkedDisplays.put( rigid, new AuxiliaryDisplayStruct( display, blockCenter.multiply( scale ) ).setScale( new Vector( scale, scale, scale ) ) );
+
+ player.sendMessage( "Created display" );
+ }
+
+ private static Vector calculateCenter( BoundingBox[] boxes ) {
+ Vector center = new Vector( 0, 0, 0 );
+ // TODO Throw error, probably
+ if ( boxes.length == 0 ) {
+ return center;
+ }
+
+ double totalVolume = 0;
+ for ( BoundingBox box : boxes ) {
+ Vector mid = box.getCenter();
+ center.add( mid.multiply( box.getVolume() ) );
+ totalVolume += box.getVolume();
+ }
+ return center.multiply( 1 / totalVolume );
+ }
+}
diff --git a/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/MiniePlugin.java b/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/MiniePlugin.java
similarity index 98%
rename from plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/MiniePlugin.java
rename to plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/MiniePlugin.java
index 9f6a4a6..55b4d0a 100644
--- a/plugin/environment/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/environment/MiniePlugin.java
+++ b/plugin/physics/src/main/java/com/aaaaahhhhhhh/bananapuncher714/mesh/physics/MiniePlugin.java
@@ -1,4 +1,4 @@
-package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
+package com.aaaaahhhhhhh.bananapuncher714.mesh.physics;
import java.io.File;
import java.io.FileWriter;
@@ -75,6 +75,7 @@ import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.BlockDataType;
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkLocation;
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkMesh;
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.IndexedPolygon;
+import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.IndexedTriangle;
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.MinecraftPlane;
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.PlaneMesh;
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.VectorReference;
@@ -1050,7 +1051,7 @@ public class MiniePlugin extends JavaPlugin {
for ( final Entry< Vector, VectorRef > entry : vertices.entrySet() ) {
final Vector existingVec = entry.getKey();
- if ( existingVec.distanceSquared( vec ) < 1e-8 ) {
+ if ( existingVec.distanceSquared( new Vector( vec.getX(), vec.getY(), vec.getZ() ) ) < 1e-8 ) {
ref = entry.getValue();
break;
}
@@ -1058,7 +1059,7 @@ public class MiniePlugin extends JavaPlugin {
if ( ref == null ) {
ref = new VectorRef();
- vertices.put( vec, ref );
+ vertices.put( new Vector( vec.getX(), vec.getY(), vec.getZ() ), ref );
}
triangle.refs.add( ref );
@@ -1313,9 +1314,9 @@ public class MiniePlugin extends JavaPlugin {
// Ignore any planes that are empty
if ( !tris.isEmpty() ) {
final PlaneMesh newMesh = new PlaneMesh();
- newMesh.completedPolygons = new ArrayList< IndexedPolygon >();
+ newMesh.completedPolygons = new ArrayList< IndexedTriangle >();
for ( final Polygon tri : tris ) {
- final IndexedPolygon newPoly = new IndexedPolygon();
+ final IndexedTriangle newPoly = new IndexedTriangle();
for ( final Point point : tri.getPoints() ) {
final Vector vert = plane.convert( point );
@@ -1342,7 +1343,7 @@ public class MiniePlugin extends JavaPlugin {
++ref.referenceCount;
}
- newPoly.points.add( ref );
+// newPoly.points.add( ref );
}
newMesh.completedPolygons.add( newPoly );
}
@@ -1536,7 +1537,7 @@ public class MiniePlugin extends JavaPlugin {
if ( planeMesh != null ) {
// Reset all indexed polygons(triangles) for this plane
for ( final IndexedPolygon poly : planeMesh.completedPolygons ) {
- poly.points.parallelStream().forEach( p -> --p.referenceCount );
+// poly.points.parallelStream().forEach( p -> --p.referenceCount );
}
// Add all polygons that were previously generated
@@ -1563,9 +1564,9 @@ public class MiniePlugin extends JavaPlugin {
// Ignore any planes that are empty
if ( !tris.isEmpty() ) {
final PlaneMesh newMesh = new PlaneMesh();
- newMesh.completedPolygons = new ArrayList< IndexedPolygon >();
+ newMesh.completedPolygons = new ArrayList< IndexedTriangle >();
for ( final Polygon tri : tris ) {
- final IndexedPolygon newPoly = new IndexedPolygon();
+ final IndexedTriangle newPoly = new IndexedTriangle();
for ( final Point point : tri.getPoints() ) {
final Vector vert = plane.convert( point );
@@ -1591,7 +1592,7 @@ public class MiniePlugin extends JavaPlugin {
++ref.referenceCount;
}
- newPoly.points.add( ref );
+// newPoly.points.add( ref );
}
newMesh.completedPolygons.add( newPoly );
}
@@ -1667,12 +1668,12 @@ public class MiniePlugin extends JavaPlugin {
}
final Triangle triangle = new Triangle();
- for ( final Vector vec : facet.points ) {
+ for ( final Vector3d vec : facet.points ) {
VectorRef ref = null;
for ( final Entry< Vector, VectorRef > entry : vertices.entrySet() ) {
final Vector existingVec = entry.getKey();
- if ( existingVec.distanceSquared( vec ) < 1e-8 ) {
+ if ( existingVec.distanceSquared( new Vector( vec.getX(), vec.getY(), vec.getZ() ) ) < 1e-8 ) {
ref = entry.getValue();
break;
}
@@ -1680,7 +1681,7 @@ public class MiniePlugin extends JavaPlugin {
if ( ref == null ) {
ref = new VectorRef();
- vertices.put( vec, ref );
+ vertices.put( new Vector( vec.getX(), vec.getY(), vec.getZ() ), ref );
}
triangle.refs.add( ref );
@@ -1765,10 +1766,10 @@ public class MiniePlugin extends JavaPlugin {
try ( FileWriter writer = new FileWriter( file ) ) {
for ( final Facet facet : facets ) {
- final Vector normal = facet.normal;
+ final Vector3d normal = facet.normal;
writer.write( facet.points.size() + " " + normal.getX() + " " + normal.getY() + " " + normal.getZ() + "\n" );
- for ( final Vector point : facet.points ) {
+ for ( final Vector3d point : facet.points ) {
writer.write( point.getX() + " " + point.getY() + " " + point.getZ() + "\n" );
}
}
diff --git a/plugin/physics/src/main/resources/plugin.yml b/plugin/physics/src/main/resources/plugin.yml
new file mode 100644
index 0000000..255eac4
--- /dev/null
+++ b/plugin/physics/src/main/resources/plugin.yml
@@ -0,0 +1,15 @@
+name: MC-Mesh-Physics
+main: com.aaaaahhhhhhh.bananapuncher714.mesh.physics.McMeshPhysicsPlugin
+version: 0.0.1
+description: BulletJME and MC-Mesh integration
+author: BananaPuncher714
+api-version: 1.13
+depend: [ "Mc-Mesh-Environment" ]
+#libraries:
+#- com.github.stephengold:Minie:7.5.0
+
+commands:
+ minie:
+ description: Main Minie command
+ aliases: []
+ permission: minie
\ No newline at end of file
diff --git a/plugin/pom.xml b/plugin/pom.xml
index 0960211..9efc589 100644
--- a/plugin/pom.xml
+++ b/plugin/pom.xml
@@ -13,5 +13,6 @@
environment
+ physics
\ No newline at end of file