Finish prototype mesh environment
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
|||||||
target
|
target
|
||||||
.settings
|
.settings
|
||||||
data
|
data
|
||||||
|
bin
|
||||||
@@ -1,5 +1,46 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
||||||
|
|
||||||
public class ChunkMeshCache {
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkLocation;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkMesh;
|
||||||
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.ChunkMeshUtil;
|
||||||
|
|
||||||
|
public class ChunkMeshCache {
|
||||||
|
File directory;
|
||||||
|
|
||||||
|
public ChunkMeshCache( File directory ) {
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkMesh load( ChunkLocation location ) {
|
||||||
|
final File file = new File( directory, location.getWorldName() + "/" + location.getX() + "/" + location.getZ() + ".cmesh" );
|
||||||
|
if ( file.exists() ) {
|
||||||
|
try {
|
||||||
|
final byte[] data = Files.readAllBytes( file.toPath() );
|
||||||
|
final ChunkMesh mesh = ChunkMeshUtil.deserialize( data );
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
} catch ( IOException e ) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save( ChunkLocation location, ChunkMesh mesh ) {
|
||||||
|
final File file = new File( directory, location.getWorldName() + "/" + location.getX() + "/" + location.getZ() + ".cmesh" );
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
|
||||||
|
final byte[] data = ChunkMeshUtil.serialize( mesh );
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.write( file.toPath(), data, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE, StandardOpenOption.WRITE );
|
||||||
|
} catch ( IOException e ) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -16,7 +13,9 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -50,79 +49,140 @@ import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.BlockDataInfor
|
|||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.BlockDataType;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.BlockDataType;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkLocation;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkLocation;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkMesh;
|
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.IndexedTriangle;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.MinecraftPlane;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.MinecraftPlane;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.MinecraftPlane.PlaneAxis;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.MinecraftPlane.PlaneAxis;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.PlaneMesh;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.PlaneMesh;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.SparseMesh;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.SparseMesh;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.VectorReference;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.VectorReference;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.ChunkMeshUtil;
|
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil;
|
||||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil.BlockShapeType;
|
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.NmsUtil.BlockShapeType;
|
||||||
import com.jme3.bullet.collision.shapes.MeshCollisionShape;
|
|
||||||
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 MeshEnvironment {
|
public class MeshEnvironment {
|
||||||
private JavaPlugin plugin;
|
private JavaPlugin plugin;
|
||||||
private File cacheDirectory;
|
private File cacheDirectory;
|
||||||
|
|
||||||
private Map< ChunkLocation, ChunkMesh > chunkMeshes;
|
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();
|
||||||
|
|
||||||
|
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 ( !locations.isEmpty() ) {
|
||||||
|
// space.activateAll( true );
|
||||||
|
// }
|
||||||
|
|
||||||
|
updateLocations.values().removeIf( c -> c.isEmpty() );
|
||||||
|
lock.unlock();
|
||||||
|
}, 1, 1 );
|
||||||
|
|
||||||
|
final Function< ChunkLocation, Collection< Location > > getLocationFor = c -> {
|
||||||
|
lock.lock();
|
||||||
|
Collection< Location > locations = updateLocations.get( c );
|
||||||
|
if ( locations == null ) {
|
||||||
|
locations = new HashSet< Location >();
|
||||||
|
updateLocations.put( c, locations );
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
return locations;
|
||||||
|
};
|
||||||
|
|
||||||
private Listener eventListener = new Listener() {
|
private Listener eventListener = new Listener() {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
private void onChunkLoad( ChunkLoadEvent event ) {
|
private void onChunkLoad( ChunkLoadEvent event ) {
|
||||||
// TODO Queue chunk load
|
final ChunkLocation location = new ChunkLocation( event.getChunk() );
|
||||||
loadOrGenerateMesh( 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
|
@EventHandler
|
||||||
private void onChunkUnload( ChunkUnloadEvent event ) {
|
private void onChunkUnload( ChunkUnloadEvent event ) {
|
||||||
// TODO Queue chunk unload
|
final ChunkLocation location = new ChunkLocation( event.getChunk() );
|
||||||
if ( event.getWorld().getName().equalsIgnoreCase( "world" ) ) {
|
|
||||||
final ChunkLocation loc = new ChunkLocation( event.getChunk() );
|
|
||||||
if ( chunks.containsKey( loc ) ) {
|
|
||||||
space.removeCollisionObject( chunks.get( loc ) );
|
|
||||||
chunks.remove( loc );
|
|
||||||
}
|
|
||||||
|
|
||||||
final ChunkMesh mesh = chunkMeshes.remove( loc );
|
lock.lock();
|
||||||
if ( mesh != null ) {
|
try {
|
||||||
saveMesh( loc, mesh );
|
if ( chunkMeshes.containsKey( location ) ) {
|
||||||
|
if ( tasks.containsKey( location ) ) {
|
||||||
|
// Wait for the chunk to finish loading
|
||||||
|
} else {
|
||||||
|
// Force all updates for this chunk
|
||||||
|
// Queue an unload
|
||||||
|
unloadMesh( location );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final ChunkTask task = tasks.get( location );
|
||||||
|
if ( task == null ) {
|
||||||
|
// Do nothing
|
||||||
|
} 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 unloaded
|
||||||
|
* - Do nothing
|
||||||
|
* - There is an unload queued
|
||||||
|
* - Do nothing
|
||||||
|
* - There is a load queued
|
||||||
|
* - Do nothing
|
||||||
|
* - Nothing is queued
|
||||||
|
* - Queue an unload
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map< ChunkLocation, Collection< Location > > updateLocations = new TreeMap< ChunkLocation, Collection< Location > >();
|
|
||||||
final BukkitTask task = Bukkit.getScheduler().runTaskTimer( plugin, () -> {
|
|
||||||
final Collection< ChunkLocation > locations = new ArrayDeque< ChunkLocation >();
|
|
||||||
updateLocations.entrySet().stream().forEach( e -> {
|
|
||||||
final Collection< Location > locs = update( e.getValue() );
|
|
||||||
e.getValue().removeAll( locs );
|
|
||||||
if ( !locs.isEmpty() ) {
|
|
||||||
locations.add( e.getKey() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
if ( !locations.isEmpty() ) {
|
|
||||||
space.activateAll( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLocations.values().removeIf( c -> c.isEmpty() );
|
|
||||||
}, 1, 1 );
|
|
||||||
final Function< ChunkLocation, Collection< Location > > getLocationFor = c -> {
|
|
||||||
Collection< Location > locations = updateLocations.get( c );
|
|
||||||
if ( locations == null ) {
|
|
||||||
locations = new HashSet< Location >();
|
|
||||||
updateLocations.put( c, locations );
|
|
||||||
}
|
|
||||||
return locations;
|
|
||||||
};
|
|
||||||
|
|
||||||
@EventHandler( priority = EventPriority.MONITOR )
|
@EventHandler( priority = EventPriority.MONITOR )
|
||||||
private void onBlockUpdateEvent( final BlockPhysicsEvent event ) {
|
private void onBlockUpdateEvent( final BlockPhysicsEvent event ) {
|
||||||
final ChunkLocation chunkLocation = new ChunkLocation( event.getBlock().getChunk() );
|
final ChunkLocation chunkLocation = new ChunkLocation( event.getBlock().getChunk() );
|
||||||
@@ -133,6 +193,7 @@ public class MeshEnvironment {
|
|||||||
public MeshEnvironment( JavaPlugin plugin, File cacheDirectory ) {
|
public MeshEnvironment( JavaPlugin plugin, File cacheDirectory ) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.cacheDirectory = cacheDirectory;
|
this.cacheDirectory = cacheDirectory;
|
||||||
|
cache = new ChunkMeshCache( new File( cacheDirectory, "meshes" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void activate() {
|
public void activate() {
|
||||||
@@ -143,52 +204,136 @@ public class MeshEnvironment {
|
|||||||
HandlerList.unregisterAll( eventListener );
|
HandlerList.unregisterAll( eventListener );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadOrGenerateMesh( final Chunk chunk ) {
|
private void completeTask( ChunkLocation location ) {
|
||||||
final ChunkLocation location = new ChunkLocation( chunk );
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final ChunkMesh mesh = loadMesh( location );
|
private void loadMesh( ChunkLocation location ) {
|
||||||
if ( mesh == null ) {
|
ChunkTask task = new ChunkTask();
|
||||||
generateMesh( chunk );
|
lock.lock();
|
||||||
} else {
|
try {
|
||||||
// Save our chunk mesh
|
tasks.put( location, task );
|
||||||
chunkMeshes.put( location, mesh );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChunkMesh loadMesh( final ChunkLocation location ) {
|
task.type = ChunkTaskType.LOAD;
|
||||||
final File file = new File( cacheDirectory, "meshes/" + location.getWorldName() + "/" + location.getX() + "/" + location.getZ() + ".cmesh" );
|
task.task = Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
|
||||||
if ( file.exists() ) {
|
final ChunkMesh mesh = cache.load( location );
|
||||||
try {
|
if ( mesh != null ) {
|
||||||
final byte[] data = Files.readAllBytes( file.toPath() );
|
lock.lock();
|
||||||
final ChunkMesh mesh = ChunkMeshUtil.deserialize( data );
|
try {
|
||||||
|
// Save the mesh
|
||||||
|
if ( !chunkMeshes.containsKey( location ) ) {
|
||||||
|
chunkMeshes.put( location, mesh );
|
||||||
|
}
|
||||||
|
|
||||||
return mesh;
|
completeTask( location );
|
||||||
} catch ( IOException e ) {
|
} finally {
|
||||||
e.printStackTrace();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
return null;
|
// Process and generate the mesh
|
||||||
}
|
Bukkit.getScheduler().runTask( plugin, () -> {
|
||||||
|
generateMesh( plugin, location.getWorld(), location.getChunk(), m -> {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
chunkMeshes.put( location, m );
|
||||||
|
|
||||||
private void saveMesh( final ChunkLocation location, final ChunkMesh mesh ) {
|
completeTask( location );
|
||||||
final File file = new File( cacheDirectory, "meshes/" + location.getWorldName() + "/" + location.getX() + "/" + location.getZ() + ".cmesh" );
|
} finally {
|
||||||
file.getParentFile().mkdirs();
|
lock.unlock();
|
||||||
|
}
|
||||||
final byte[] data = ChunkMeshUtil.serialize( mesh );
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unloadMesh( ChunkLocation location ) {
|
||||||
|
ChunkTask task = new ChunkTask();
|
||||||
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
Files.write( file.toPath(), data, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE, StandardOpenOption.WRITE );
|
tasks.put( location, task );
|
||||||
} catch ( IOException e ) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SparseMesh generateSpareMesh( World world, Collection< Vector > locations ) {
|
if ( !applyUpdatesToMesh( location ) ) {
|
||||||
final long start = System.currentTimeMillis();
|
// Not good! Piston issue somewhere
|
||||||
|
}
|
||||||
|
|
||||||
|
task.type = ChunkTaskType.UNLOAD;
|
||||||
|
task.task = Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
ChunkMesh mesh = chunkMeshes.get( location );
|
||||||
|
if ( mesh != null ) {
|
||||||
|
// Save the mesh
|
||||||
|
cache.save( location, mesh );
|
||||||
|
} else {
|
||||||
|
// Not really good
|
||||||
|
}
|
||||||
|
|
||||||
|
completeTask( location );
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} 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 ) {
|
||||||
|
if ( locations.isEmpty() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean set = false;
|
boolean set = false;
|
||||||
Vector min;
|
Vector min = new Vector();
|
||||||
Vector max;
|
Vector max = new Vector();
|
||||||
|
|
||||||
for ( Vector v : locations ) {
|
for ( Vector v : locations ) {
|
||||||
if ( !set ) {
|
if ( !set ) {
|
||||||
@@ -335,22 +480,19 @@ public class MeshEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mesh the data and get the output
|
Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
|
||||||
final MeshResult meshResult = generateMeshFrom( polygons );
|
// Mesh the data and get the output
|
||||||
|
final MeshResult meshResult = generateMeshFrom( polygons );
|
||||||
|
|
||||||
sparseMesh.planes = meshResult.meshes;
|
sparseMesh.planes = meshResult.meshes;
|
||||||
sparseMesh.pointReferences = meshResult.points;
|
sparseMesh.pointReferences = meshResult.points;
|
||||||
|
|
||||||
// DONE
|
callback.accept( sparseMesh );
|
||||||
|
} );
|
||||||
return sparseMesh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateMesh( final Chunk chunk ) {
|
// TODO Use ChunkSnapshot in the future
|
||||||
final long start = System.currentTimeMillis();
|
private static void generateMesh( JavaPlugin plugin, World world, final Chunk chunk, Consumer< ChunkMesh > callback ) {
|
||||||
final World world = chunk.getWorld();
|
|
||||||
|
|
||||||
final ChunkLocation chunkLocation = new ChunkLocation( chunk );
|
|
||||||
final int worldMinHeight = world.getMinHeight();
|
final int worldMinHeight = world.getMinHeight();
|
||||||
final int worldMaxHeight = world.getMaxHeight();
|
final int worldMaxHeight = world.getMaxHeight();
|
||||||
final int worldHeight = worldMaxHeight - worldMinHeight;
|
final int worldHeight = worldMaxHeight - worldMinHeight;
|
||||||
@@ -508,44 +650,15 @@ public class MeshEnvironment {
|
|||||||
chunkMesh.planes = meshResult.meshes;
|
chunkMesh.planes = meshResult.meshes;
|
||||||
chunkMesh.pointReferences = meshResult.points;
|
chunkMesh.pointReferences = meshResult.points;
|
||||||
|
|
||||||
// DONE
|
callback.accept( chunkMesh );
|
||||||
|
|
||||||
// Now, create a physics object from the chunk mesh
|
|
||||||
final IndexedMesh nativeMesh = chunkMesh.toIndexedMesh();
|
|
||||||
// Enable quantized AABB compression... hopefully that doesn't mess with the accuracy or anything...
|
|
||||||
final MeshCollisionShape mesh = new MeshCollisionShape( true, nativeMesh );
|
|
||||||
|
|
||||||
// Create a rigid body
|
|
||||||
PhysicsRigidBody rigid = new PhysicsRigidBody( mesh, PhysicsBody.massForStatic );
|
|
||||||
rigid.setPhysicsLocation( new Vector3f( chunk.getX() << 4, world.getMinHeight(), chunk.getZ() << 4 ) );
|
|
||||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
|
||||||
rigid.setKinematic( false );
|
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask( plugin, () -> {
|
|
||||||
// Save our chunk mesh
|
|
||||||
chunkMeshes.put( chunkLocation, chunkMesh );
|
|
||||||
|
|
||||||
space.addCollisionObject( rigid );
|
|
||||||
|
|
||||||
PhysicsRigidBody old = chunks.put( new ChunkLocation( chunk ), rigid );
|
|
||||||
if ( old != null ) {
|
|
||||||
getLogger().warning( "Old rigid body found!" );
|
|
||||||
space.remove( old );
|
|
||||||
}
|
|
||||||
|
|
||||||
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" );
|
|
||||||
} );
|
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection< Location > update( final AbstractMesh mesh, final Collection< Location > locations ) {
|
private static Collection< Location > update( final AbstractMesh mesh, final Collection< Location > locations ) {
|
||||||
if ( locations.isEmpty() ) {
|
if ( locations.isEmpty() ) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
final World world = locations.iterator().next().getWorld();
|
|
||||||
|
|
||||||
final Collection< Location > canRemove = new ArrayDeque< Location >();
|
final Collection< Location > canRemove = new ArrayDeque< Location >();
|
||||||
// Check each location
|
// Check each location
|
||||||
final Map< MinecraftPlane, Collection< Polygon > > polygons = new TreeMap< MinecraftPlane, Collection< Polygon > >();
|
final Map< MinecraftPlane, Collection< Polygon > > polygons = new TreeMap< MinecraftPlane, Collection< Polygon > >();
|
||||||
@@ -775,23 +888,10 @@ public class MeshEnvironment {
|
|||||||
// Remove all vector references that have a reference count of 0
|
// Remove all vector references that have a reference count of 0
|
||||||
mesh.pointReferences.removeIf( v -> v.referenceCount <= 0 );
|
mesh.pointReferences.removeIf( v -> v.referenceCount <= 0 );
|
||||||
|
|
||||||
// DONE
|
|
||||||
|
|
||||||
// Now, create a physics object from the chunk mesh
|
|
||||||
final IndexedMesh nativeMesh = chunkMesh.toIndexedMesh();
|
|
||||||
// Enable quantized AABB compression... hopefully that doesn't mess with the accuracy or anything...
|
|
||||||
final MeshCollisionShape mesh = new MeshCollisionShape( true, nativeMesh );
|
|
||||||
|
|
||||||
// Create a rigid body
|
|
||||||
PhysicsRigidBody rigid = new PhysicsRigidBody( mesh, PhysicsBody.massForStatic );
|
|
||||||
rigid.setPhysicsLocation( new Vector3f( chunkLocation.getX() << 4, world.getMinHeight(), chunkLocation.getZ() << 4 ) );
|
|
||||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
|
||||||
rigid.setKinematic( false );
|
|
||||||
|
|
||||||
return canRemove;
|
return canRemove;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MeshResult generateMeshFrom( final Map< MinecraftPlane, Collection< Polygon > > polygons ) {
|
private static MeshResult generateMeshFrom( final Map< MinecraftPlane, Collection< Polygon > > polygons ) {
|
||||||
final Map< MinecraftPlane, Mesh< RegionSimple > > meshMap = new HashMap< MinecraftPlane, Mesh< RegionSimple > >();
|
final Map< MinecraftPlane, Mesh< RegionSimple > > meshMap = new HashMap< MinecraftPlane, Mesh< RegionSimple > >();
|
||||||
for ( final Entry< MinecraftPlane, Collection< Polygon > > entry : polygons.entrySet() ) {
|
for ( final Entry< MinecraftPlane, Collection< Polygon > > entry : polygons.entrySet() ) {
|
||||||
final MinecraftPlane plane = entry.getKey();
|
final MinecraftPlane plane = entry.getKey();
|
||||||
@@ -875,9 +975,11 @@ public class MeshEnvironment {
|
|||||||
|
|
||||||
// Add all vector references
|
// Add all vector references
|
||||||
result.points = vertices;
|
result.points = vertices;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CompleteMesh {
|
private static class CompleteMesh {
|
||||||
Mesh< RegionSimple > mesh;
|
Mesh< RegionSimple > mesh;
|
||||||
Collection< Polygon > triangles;
|
Collection< Polygon > triangles;
|
||||||
|
|
||||||
@@ -887,8 +989,17 @@ public class MeshEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MeshResult {
|
private static class MeshResult {
|
||||||
Map< MinecraftPlane, PlaneMesh > meshes;
|
Map< MinecraftPlane, PlaneMesh > meshes;
|
||||||
TreeSet< VectorReference > points;
|
TreeSet< VectorReference > points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum ChunkTaskType {
|
||||||
|
LOAD, UNLOAD
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChunkTask {
|
||||||
|
BukkitTask task;
|
||||||
|
ChunkTaskType type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user