Refactor and add basic sparse mesh implementation
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -8,7 +8,7 @@ import java.util.Set;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.util.BukkitUtil;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.BukkitUtil;
|
||||
|
||||
public class CommandBase {
|
||||
protected PluginCommand command;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.executor.CommandExecutable;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.command.executor.CommandExecutable;
|
||||
|
||||
public class CommandOption {
|
||||
protected CommandExecutable exe;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command;
|
||||
|
||||
public class SplitCommand {
|
||||
protected String[] input;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -13,10 +13,10 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.util.StringUtil;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.executor.CommandExecutable;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.InputValidator;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.InputValidatorString;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.sender.SenderValidator;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.command.executor.CommandExecutable;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.InputValidator;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.InputValidatorString;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.sender.SenderValidator;
|
||||
|
||||
/**
|
||||
* A build and run command framework for automatic tab completions and easy branching.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.executor;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.executor;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.CommandParameters;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.command.CommandParameters;
|
||||
|
||||
public interface CommandExecutable {
|
||||
void execute( CommandSender sender, String[] args, CommandParameters params );
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.executor;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.executor;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.CommandParameters;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.command.CommandParameters;
|
||||
|
||||
public class CommandExecutableMessage implements CommandExecutable {
|
||||
protected String message;
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
* @author BananaPuncher714
|
||||
*
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.executor;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.executor;
|
||||
@@ -3,4 +3,4 @@
|
||||
*
|
||||
* @author BananaPuncher714
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
* @author BananaPuncher714
|
||||
*
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.sender;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.sender;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.sender;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.sender;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.sender;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.sender;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.sender;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.sender;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
* @author BananaPuncher714
|
||||
*
|
||||
*/
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.sender;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.command.validator.sender;
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
||||
|
||||
public class ChunkMeshCache {
|
||||
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.FileUtil;
|
||||
import com.jme3.bullet.PhysicsSpace;
|
||||
import com.jme3.bullet.objects.PhysicsRigidBody;
|
||||
|
||||
public class McMeshEnvironment extends JavaPlugin {
|
||||
public class McMeshEnvironmentPlugin extends JavaPlugin {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
FileUtil.saveToFile( getResource( "native/windows/x86_64/bulletjme.dll" ), new File( getDataFolder() + "/lib", "bulletjme.dll" ), false );
|
||||
@@ -17,6 +22,11 @@ public class McMeshEnvironment extends JavaPlugin {
|
||||
Bukkit.getPluginManager().disablePlugin( this );
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't log anything
|
||||
PhysicsSpace.logger.setLevel( Level.WARNING );
|
||||
PhysicsSpace.loggerC.setLevel( Level.WARNING );
|
||||
PhysicsRigidBody.logger2.setLevel( Level.WARNING );
|
||||
}
|
||||
|
||||
private final boolean loadNativeLibraries() {
|
||||
@@ -0,0 +1,894 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
||||
|
||||
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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.region.simple.RegionRuleWinding;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.region.simple.RegionSimple;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.region.simple.RegionSimple.GluWindingRule;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.AbstractMesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.BlockDataInformation;
|
||||
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.MinecraftPlane.PlaneAxis;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.PlaneMesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.SparseMesh;
|
||||
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.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 {
|
||||
private JavaPlugin plugin;
|
||||
private File cacheDirectory;
|
||||
|
||||
private Map< ChunkLocation, ChunkMesh > chunkMeshes;
|
||||
|
||||
private Listener eventListener = new Listener() {
|
||||
@EventHandler
|
||||
private void onChunkLoad( ChunkLoadEvent event ) {
|
||||
// TODO Queue chunk load
|
||||
loadOrGenerateMesh( event.getChunk() );
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onChunkUnload( ChunkUnloadEvent event ) {
|
||||
// TODO Queue chunk unload
|
||||
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 );
|
||||
if ( mesh != null ) {
|
||||
saveMesh( loc, mesh );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 )
|
||||
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;
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
Bukkit.getPluginManager().registerEvents( eventListener, plugin );
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
HandlerList.unregisterAll( eventListener );
|
||||
}
|
||||
|
||||
private void loadOrGenerateMesh( final Chunk chunk ) {
|
||||
final ChunkLocation location = new ChunkLocation( chunk );
|
||||
|
||||
final ChunkMesh mesh = loadMesh( location );
|
||||
if ( mesh == null ) {
|
||||
generateMesh( chunk );
|
||||
} else {
|
||||
// Save our chunk mesh
|
||||
chunkMeshes.put( location, mesh );
|
||||
}
|
||||
}
|
||||
|
||||
private ChunkMesh loadMesh( final ChunkLocation location ) {
|
||||
final File file = new File( cacheDirectory, "meshes/" + 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;
|
||||
}
|
||||
|
||||
private void saveMesh( final ChunkLocation location, final ChunkMesh mesh ) {
|
||||
final File file = new File( cacheDirectory, "meshes/" + 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();
|
||||
}
|
||||
}
|
||||
|
||||
private SparseMesh generateSpareMesh( World world, Collection< Vector > locations ) {
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
boolean set = false;
|
||||
Vector min;
|
||||
Vector max;
|
||||
|
||||
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 );
|
||||
if ( polygonCollection == null ) {
|
||||
polygonCollection = new ArrayDeque< Polygon >();
|
||||
polygons.put( p, polygonCollection );
|
||||
}
|
||||
return polygonCollection;
|
||||
};
|
||||
|
||||
for ( final Vector location : locations ) {
|
||||
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;
|
||||
} else if ( boundingBoxes.length == 1 && boundingBoxes[ 0 ].getVolume() == 1 ) {
|
||||
// Solid
|
||||
information.type = BlockDataType.SOLID;
|
||||
} else {
|
||||
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();
|
||||
final double minY = aabb.getMinY() + loc.getBlockY() - min.getBlockY();
|
||||
final double minZ = aabb.getMinZ() + loc.getBlockZ() - min.getBlockZ();
|
||||
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 );
|
||||
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, maxY ) ).add( yPoly );
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mesh the data and get the output
|
||||
final MeshResult meshResult = generateMeshFrom( polygons );
|
||||
|
||||
sparseMesh.planes = meshResult.meshes;
|
||||
sparseMesh.pointReferences = meshResult.points;
|
||||
|
||||
// DONE
|
||||
|
||||
return sparseMesh;
|
||||
}
|
||||
|
||||
private void generateMesh( final Chunk chunk ) {
|
||||
final long start = System.currentTimeMillis();
|
||||
final World world = chunk.getWorld();
|
||||
|
||||
final ChunkLocation chunkLocation = new ChunkLocation( chunk );
|
||||
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 );
|
||||
if ( polygonCollection == null ) {
|
||||
polygonCollection = new ArrayDeque< Polygon >();
|
||||
polygons.put( p, polygonCollection );
|
||||
}
|
||||
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
|
||||
for ( int y = worldMinHeight; y < worldMaxHeight; ++y ) {
|
||||
final int relY = y - worldMinHeight;
|
||||
final int yIndex = relY << 8;
|
||||
for ( int z = 0; z < 16; ++z ) {
|
||||
final int zIndex = yIndex + ( z << 4 );
|
||||
for ( int x = 0; x < 16; ++x ) {
|
||||
final int index = zIndex + x;
|
||||
final Block block = chunk.getBlock( x, y, z );
|
||||
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 );
|
||||
} else if ( boundingBoxes.length == 1 && boundingBoxes[ 0 ].getVolume() == 1 ) {
|
||||
// Solid
|
||||
chunkMesh.setType( index, BlockDataType.SOLID );
|
||||
} 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;
|
||||
final double minY = aabb.getMinY() + relY;
|
||||
final double minZ = aabb.getMinZ() + z;
|
||||
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 );
|
||||
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, maxY ) ).add( yPoly );
|
||||
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, minZ ) ).add( zPoly );
|
||||
getPolygons.apply( new MinecraftPlane( PlaneAxis.Z, maxZ ) ).add( zPoly );
|
||||
}
|
||||
|
||||
chunkMesh.setBoxes( index, boundingBoxes );
|
||||
}
|
||||
} else {
|
||||
chunkMesh.setType( index, BlockDataType.NONE );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
for ( int z = 0; z < 16; ++z ) {
|
||||
final int zIndex = z << 4;
|
||||
for ( int x = 0; x < 16; ++x ) {
|
||||
final int index = yIndex + zIndex + x;
|
||||
if ( chunkMesh.getType( index ) == BlockDataType.SOLID ) {
|
||||
final Polygon xPoly = new Polygon( Arrays.asList(
|
||||
new Point( y, z ),
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously( plugin, () -> {
|
||||
// Mesh the data and get the output
|
||||
final MeshResult meshResult = generateMeshFrom( polygons );
|
||||
|
||||
chunkMesh.planes = meshResult.meshes;
|
||||
chunkMesh.pointReferences = meshResult.points;
|
||||
|
||||
// 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( 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 ) {
|
||||
if ( locations.isEmpty() ) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
final World world = locations.iterator().next().getWorld();
|
||||
|
||||
final Collection< Location > canRemove = new ArrayDeque< Location >();
|
||||
// Check each location
|
||||
final Map< MinecraftPlane, Collection< Polygon > > polygons = new TreeMap< MinecraftPlane, Collection< Polygon > >();
|
||||
final Function< MinecraftPlane, Collection< Polygon > > getPolygons = p -> {
|
||||
Collection< Polygon > polygonCollection = polygons.get( p );
|
||||
if ( polygonCollection == null ) {
|
||||
polygonCollection = new ArrayDeque< Polygon >();
|
||||
polygons.put( p, polygonCollection );
|
||||
}
|
||||
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()
|
||||
.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 BoundingBox[] otherBoxes = information.boundingBoxes;
|
||||
if ( boxes.size() == 0 ) {
|
||||
if ( oldType == BlockDataType.NONE ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockDataInformation newInfo = new BlockDataInformation( data );
|
||||
newInfo.type = BlockDataType.SOLID;
|
||||
newInfo.boundingBoxes = null;
|
||||
|
||||
mesh.setBlockDataInformationFor( newInfo, position );
|
||||
} else {
|
||||
if ( otherBoxes != null && isEqual.apply( boxes, otherBoxes ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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();
|
||||
final double minY = aabb.getMinY() + position.getY();
|
||||
final double minZ = aabb.getMinZ() + position.getZ();
|
||||
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 );
|
||||
getPolygons.apply( new MinecraftPlane( PlaneAxis.Y, maxY ) ).add( yPoly );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
for ( final Polygon polygon : entry.getValue() ) {
|
||||
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();
|
||||
for ( int i = 0; i < tri.getPoints().size() && i < 3; ++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 ) {
|
||||
ref = upperRef;
|
||||
} else {
|
||||
final VectorReference lowerRef = mesh.pointReferences.floor( tempRef );
|
||||
if ( lowerRef != null && lowerRef.vector.distanceSquared( vert ) < 1e-8 ) {
|
||||
ref = lowerRef;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ref == null ) {
|
||||
ref = tempRef;
|
||||
ref.referenceCount = 1;
|
||||
mesh.pointReferences.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 = 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 );
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
private 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 ) );
|
||||
for ( final Polygon polygon : entry.getValue() ) {
|
||||
mesh.addPolygon( polygon, RegionRuleWinding.CLOCKWISE );
|
||||
}
|
||||
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();
|
||||
newMesh.completedPolygons = new ArrayList< IndexedTriangle >();
|
||||
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!
|
||||
}
|
||||
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
private class CompleteMesh {
|
||||
Mesh< RegionSimple > mesh;
|
||||
Collection< Polygon > triangles;
|
||||
|
||||
CompleteMesh( final MinecraftPlane plane, final Mesh< RegionSimple > mesh ) {
|
||||
this.mesh = mesh;
|
||||
triangles = mesh.meshify();
|
||||
}
|
||||
}
|
||||
|
||||
private class MeshResult {
|
||||
Map< MinecraftPlane, PlaneMesh > meshes;
|
||||
TreeSet< VectorReference > points;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment;
|
||||
|
||||
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;
|
||||
@@ -20,7 +19,6 @@ import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
@@ -57,30 +55,32 @@ import org.bukkit.util.Vector;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Quaternionf;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionRuleWinding;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.region.simple.RegionSimple.GluWindingRule;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.CommandParameters;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.SubCommand;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.executor.CommandExecutableMessage;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.InputValidatorDouble;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.InputValidatorInt;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.command.validator.sender.SenderValidatorPlayer;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.BlockDataType;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkMesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.IndexedPolygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.MinecraftPlane;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.MinecraftPlane.PlaneAxis;
|
||||
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.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Mesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.region.simple.RegionRuleWinding;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.region.simple.RegionSimple;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.region.simple.RegionSimple.GluWindingRule;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.base.Facet;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.base.MeshBuilder;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.base.Plane;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.base.Vector3d;
|
||||
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.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.MinecraftPlane;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.PlaneMesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.VectorReference;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.MinecraftPlane.PlaneAxis;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.ChunkMeshUtil;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.FileUtil;
|
||||
import com.jme3.bullet.PhysicsSpace;
|
||||
import com.jme3.bullet.PhysicsSpace.BroadphaseType;
|
||||
import com.jme3.bullet.RotationOrder;
|
||||
@@ -101,7 +101,6 @@ import com.jme3.bullet.joints.motors.MotorParam;
|
||||
import com.jme3.bullet.objects.PhysicsBody;
|
||||
import com.jme3.bullet.objects.PhysicsRigidBody;
|
||||
import com.jme3.math.Matrix3f;
|
||||
import com.jme3.math.Plane;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
@@ -153,7 +152,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
PhysicsRigidBody.logger2.setLevel( Level.WARNING );
|
||||
|
||||
space = new PhysicsSpace( BroadphaseType.DBVT );
|
||||
PlaneCollisionShape plane = new PlaneCollisionShape( new Plane( new Vector3f( 0, 1, 0 ), 0 ) );
|
||||
PlaneCollisionShape plane = new PlaneCollisionShape( new com.jme3.math.Plane( new Vector3f( 0, 1, 0 ), 0 ) );
|
||||
planeBody = new PhysicsRigidBody( plane, PhysicsBody.massForStatic );
|
||||
planeBody.setPhysicsLocation( new Vector3f( 0, 128, 0 ) );
|
||||
// space.addCollisionObject( planeBody );
|
||||
@@ -829,61 +828,61 @@ public class MiniePlugin extends JavaPlugin {
|
||||
// Check each face
|
||||
if ( y == 0 || types[ index - 256 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facet.points.add( new Vector3d( x, y, z ) );
|
||||
facet.points.add( new Vector3d( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z ) );
|
||||
facet.normal = new Vector3d( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( y == worldHeight - 1 || types[ index + 256 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facet.points.add( new Vector3d( x, y + 1, z ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector3d( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( z == 0 || types[ index - 16 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facet.points.add( new Vector3d( x, y, z ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z ) );
|
||||
facet.normal = new Vector3d( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( z == 15 || types[ index + 16 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facet.points.add( new Vector3d( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z + 1 ) );
|
||||
facet.normal = new Vector3d( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( x == 0 || types[ index - 1 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facet.points.add( new Vector3d( x, y, z ) );
|
||||
facet.points.add( new Vector3d( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z ) );
|
||||
facet.normal = new Vector3d( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( x == 15 || types[ index + 1 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facet.points.add( new Vector3d( x + 1, y, z ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector3d( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
}
|
||||
@@ -945,61 +944,61 @@ public class MiniePlugin extends JavaPlugin {
|
||||
// Check each face
|
||||
if ( y == 0 || types[ index - 256 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facet.points.add( new Vector3d( x, y, z ) );
|
||||
facet.points.add( new Vector3d( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z ) );
|
||||
facet.normal = new Vector3d( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( y == worldHeight - 1 || types[ index + 256 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facet.points.add( new Vector3d( x, y + 1, z ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector3d( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( z == 0 || types[ index - 16 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facet.points.add( new Vector3d( x, y, z ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z ) );
|
||||
facet.normal = new Vector3d( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( z == 15 || types[ index + 16 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facet.points.add( new Vector3d( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z + 1 ) );
|
||||
facet.normal = new Vector3d( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( x == 0 || types[ index - 1 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x, y, z ) );
|
||||
facet.points.add( new Vector( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x, y + 1, z ) );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facet.points.add( new Vector3d( x, y, z ) );
|
||||
facet.points.add( new Vector3d( x, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x, y + 1, z ) );
|
||||
facet.normal = new Vector3d( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
if ( x == 15 || types[ index + 1 ] != BlockVoxelType.SOLID ) {
|
||||
Facet facet = new Facet();
|
||||
facet.points.add( new Vector( x + 1, y, z ) );
|
||||
facet.points.add( new Vector( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facet.points.add( new Vector3d( x + 1, y, z ) );
|
||||
facet.points.add( new Vector3d( x + 1, y, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z + 1 ) );
|
||||
facet.points.add( new Vector3d( x + 1, y + 1, z ) );
|
||||
facet.normal = new Vector3d( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
}
|
||||
@@ -1046,7 +1045,7 @@ 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();
|
||||
@@ -1850,7 +1849,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Collection< Polygon > process( final com.aaaaahhhhhhh.bananapuncher714.minietest.objects.mesh.Plane plane ) {
|
||||
private static Collection< Polygon > process( final Plane plane ) {
|
||||
final Mesh< RegionSimple > mesh = new Mesh< RegionSimple >( () -> { return new RegionSimple( GluWindingRule.ODD ); } );
|
||||
for ( Polygon poly : plane.polygons ) {
|
||||
mesh.addPolygon( poly, RegionRuleWinding.CLOCKWISE );
|
||||
@@ -1914,14 +1913,14 @@ public class MiniePlugin extends JavaPlugin {
|
||||
private static List< Facet > generateFacetsFor( BoundingBox box ) {
|
||||
List< Facet > facets = new ArrayList< Facet >();
|
||||
|
||||
Vector p1 = new Vector( box.getMinX(), box.getMinY(), box.getMinZ() );
|
||||
Vector p2 = new Vector( box.getMinX(), box.getMinY(), box.getMaxZ() );
|
||||
Vector p3 = new Vector( box.getMinX(), box.getMaxY(), box.getMinZ() );
|
||||
Vector p4 = new Vector( box.getMinX(), box.getMaxY(), box.getMaxZ() );
|
||||
Vector p5 = new Vector( box.getMaxX(), box.getMinY(), box.getMinZ() );
|
||||
Vector p6 = new Vector( box.getMaxX(), box.getMinY(), box.getMaxZ() );
|
||||
Vector p7 = new Vector( box.getMaxX(), box.getMaxY(), box.getMinZ() );
|
||||
Vector p8 = new Vector( box.getMaxX(), box.getMaxY(), box.getMaxZ() );
|
||||
Vector3d p1 = new Vector3d( box.getMinX(), box.getMinY(), box.getMinZ() );
|
||||
Vector3d p2 = new Vector3d( box.getMinX(), box.getMinY(), box.getMaxZ() );
|
||||
Vector3d p3 = new Vector3d( box.getMinX(), box.getMaxY(), box.getMinZ() );
|
||||
Vector3d p4 = new Vector3d( box.getMinX(), box.getMaxY(), box.getMaxZ() );
|
||||
Vector3d p5 = new Vector3d( box.getMaxX(), box.getMinY(), box.getMinZ() );
|
||||
Vector3d p6 = new Vector3d( box.getMaxX(), box.getMinY(), box.getMaxZ() );
|
||||
Vector3d p7 = new Vector3d( box.getMaxX(), box.getMaxY(), box.getMinZ() );
|
||||
Vector3d p8 = new Vector3d( box.getMaxX(), box.getMaxY(), box.getMaxZ() );
|
||||
|
||||
{
|
||||
Facet facet = new Facet();
|
||||
@@ -1929,7 +1928,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p3 );
|
||||
facet.normal = new Vector( -1, 0, 0 );
|
||||
facet.normal = new Vector3d( -1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
@@ -1939,7 +1938,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 1, 0, 0 );
|
||||
facet.normal = new Vector3d( 1, 0, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
@@ -1949,7 +1948,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
facet.points.add( p2 );
|
||||
facet.points.add( p6 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, -1, 0 );
|
||||
facet.normal = new Vector3d( 0, -1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
@@ -1959,7 +1958,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p7 );
|
||||
facet.normal = new Vector( 0, 1, 0 );
|
||||
facet.normal = new Vector3d( 0, 1, 0 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
@@ -1969,7 +1968,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
facet.points.add( p3 );
|
||||
facet.points.add( p7 );
|
||||
facet.points.add( p5 );
|
||||
facet.normal = new Vector( 0, 0, -1 );
|
||||
facet.normal = new Vector3d( 0, 0, -1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
@@ -1979,7 +1978,7 @@ public class MiniePlugin extends JavaPlugin {
|
||||
facet.points.add( p4 );
|
||||
facet.points.add( p8 );
|
||||
facet.points.add( p6 );
|
||||
facet.normal = new Vector( 0, 0, 1 );
|
||||
facet.normal = new Vector3d( 0, 0, 1 );
|
||||
facets.add( facet );
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class Aabb implements Serializable {
|
||||
private static final long serialVersionUID = 6518512405568765913L;
|
||||
@@ -18,6 +19,10 @@ public class Aabb implements Serializable {
|
||||
public final int depth;
|
||||
public final int height;
|
||||
|
||||
public Aabb( Vector min, Vector max ) {
|
||||
this( min.getBlockX(), min.getBlockY(), min.getBlockZ(), max.getBlockX(), max.getBlockY(), max.getBlockZ() );
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -47,4 +52,12 @@ public class Aabb implements Serializable {
|
||||
public int getVolume() {
|
||||
return width * depth * height;
|
||||
}
|
||||
|
||||
public Vector getMinimumCorner() {
|
||||
return new Vector( minX, minY, minZ );
|
||||
}
|
||||
|
||||
public Vector getMaximumCorner() {
|
||||
return new Vector( maxX, maxY, maxZ );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.jme3.bullet.collision.shapes.infos.IndexedMesh;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
public abstract class AbstractMesh {
|
||||
// References to every point used by at least one planemesh
|
||||
public TreeSet< VectorReference > pointReferences = new TreeSet< VectorReference >();
|
||||
// Map of meshes to each plane
|
||||
// TODO Allow for non axis-aligned planes
|
||||
public Map< MinecraftPlane, PlaneMesh > planes = new TreeMap< MinecraftPlane, PlaneMesh >();
|
||||
|
||||
public abstract Vector getMinimumCorner();
|
||||
public abstract Vector getMaximumCorner();
|
||||
|
||||
public abstract BlockDataInformation getBlockDataInformationFor( Vector position );
|
||||
public abstract void setBlockDataInformationFor( BlockDataInformation information, Vector position );
|
||||
|
||||
public IndexedMesh toIndexedMesh() {
|
||||
final Vector3f[] positionArray = new Vector3f[ pointReferences.size() ];
|
||||
int vertexIndex = 0;
|
||||
for ( final VectorReference ref : pointReferences ) {
|
||||
final Vector vector = ref.vector;
|
||||
|
||||
positionArray[ vertexIndex ] = new Vector3f( ( float ) vector.getX(), ( float ) vector.getY(), ( float ) vector.getZ() );
|
||||
ref.index = vertexIndex;
|
||||
|
||||
++vertexIndex;
|
||||
}
|
||||
|
||||
final int totalTriangleCount = planes.values().stream().mapToInt( m -> m.completedPolygons.size() ).sum();
|
||||
|
||||
int triangleIndex = 0;
|
||||
final int[] indexArray = new int[ totalTriangleCount * 3 ];
|
||||
for ( final PlaneMesh mesh : planes.values() ) {
|
||||
for ( final IndexedPolygon triangle : mesh.completedPolygons ) {
|
||||
Collection< VectorReference > refs = triangle.getPoints();
|
||||
if ( refs.size() != 3 ) {
|
||||
throw new IllegalStateException( "Triangle does not have exactly 3 vertices!" );
|
||||
}
|
||||
|
||||
final int indexArrayStart = triangleIndex * 3;
|
||||
Iterator< VectorReference > it = refs.iterator();
|
||||
indexArray[ indexArrayStart ] = it.next().index;
|
||||
indexArray[ indexArrayStart + 1 ] = it.next().index;
|
||||
indexArray[ indexArrayStart + 2 ] = it.next().index;
|
||||
|
||||
++triangleIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return new IndexedMesh( positionArray, indexArray );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
|
||||
public class BlockDataInformation {
|
||||
// If the block is complex/simple/empty
|
||||
public BlockDataType type;
|
||||
// Hashcode of the current block's blockdata so we can check if it changed
|
||||
public final int blockDataHash;
|
||||
// The bounding boxes for each complex block, null if air or a cube
|
||||
public BoundingBox[] boundingBoxes;
|
||||
|
||||
public BlockDataInformation( int hash ) {
|
||||
blockDataHash = hash;
|
||||
}
|
||||
|
||||
public BlockDataInformation( BlockData data ) {
|
||||
blockDataHash = data.hashCode();
|
||||
}
|
||||
|
||||
public boolean isBlockData( BlockData data ) {
|
||||
return blockDataHash == data.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
public enum BlockDataType {
|
||||
NONE, COMPLEX, SOLID
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.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;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -1,28 +1,25 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
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.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Polygon;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.MinecraftPlane.PlaneAxis;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util.BinaryWriter;
|
||||
import com.jme3.bullet.collision.shapes.infos.IndexedMesh;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
public class ChunkMesh {
|
||||
public class ChunkMesh extends AbstractMesh {
|
||||
// TODO Public for now while I figure out how to organize the stuff properly
|
||||
|
||||
// Hashcode of the current block's blockdata so we can check if it changed
|
||||
@@ -31,11 +28,6 @@ public class ChunkMesh {
|
||||
private BoundingBox[][] boxes;
|
||||
// Stores which blocks are complex/simple/empty
|
||||
private BlockDataTypeSet blockTypes;
|
||||
// References to every point used by at least one planemesh
|
||||
public TreeSet< VectorReference > pointReferences = new TreeSet< VectorReference >();
|
||||
// Map of meshes to each plane
|
||||
// TODO Allow for non axis-aligned planes
|
||||
public Map< MinecraftPlane, PlaneMesh > planes = new TreeMap< MinecraftPlane, PlaneMesh >();
|
||||
// Bounding box of this mesh
|
||||
private Aabb box;
|
||||
|
||||
@@ -89,7 +81,38 @@ public class ChunkMesh {
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMinimumCorner() {
|
||||
return box.getMinimumCorner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMaximumCorner() {
|
||||
return box.getMaximumCorner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockDataInformation getBlockDataInformationFor( Vector position ) {
|
||||
final int index = position.getBlockX() + ( position.getBlockZ() << 4 ) + ( position.getBlockY() << 8 );
|
||||
|
||||
BlockDataInformation information = new BlockDataInformation( data[ index ] );
|
||||
information.type = getType( index );
|
||||
information.boundingBoxes = getBoxesAt( index );
|
||||
|
||||
return information;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlockDataInformationFor( BlockDataInformation information, Vector position ) {
|
||||
final int index = position.getBlockX() + ( position.getBlockZ() << 4 ) + ( position.getBlockY() << 8 );
|
||||
|
||||
data[ index ] = information.blockDataHash;
|
||||
setType( index, information.type );
|
||||
setBoxes( index, information.boundingBoxes );
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexedMesh toIndexedMesh() {
|
||||
final Vector3f[] positionArray = new Vector3f[ pointReferences.size() ];
|
||||
int vertexIndex = 0;
|
||||
@@ -108,14 +131,16 @@ public class ChunkMesh {
|
||||
final int[] indexArray = new int[ totalTriangleCount * 3 ];
|
||||
for ( final PlaneMesh mesh : planes.values() ) {
|
||||
for ( final IndexedPolygon triangle : mesh.completedPolygons ) {
|
||||
if ( triangle.points.size() != 3 ) {
|
||||
Collection< VectorReference > refs = triangle.getPoints();
|
||||
if ( refs.size() != 3 ) {
|
||||
throw new IllegalStateException( "Triangle does not have exactly 3 vertices!" );
|
||||
}
|
||||
|
||||
final int indexArrayStart = triangleIndex * 3;
|
||||
indexArray[ indexArrayStart ] = triangle.points.get( 0 ).index;
|
||||
indexArray[ indexArrayStart + 1 ] = triangle.points.get( 1 ).index;
|
||||
indexArray[ indexArrayStart + 2 ] = triangle.points.get( 2 ).index;
|
||||
Iterator< VectorReference > it = refs.iterator();
|
||||
indexArray[ indexArrayStart ] = it.next().index;
|
||||
indexArray[ indexArrayStart + 1 ] = it.next().index;
|
||||
indexArray[ indexArrayStart + 2 ] = it.next().index;
|
||||
|
||||
++triangleIndex;
|
||||
}
|
||||
@@ -198,8 +223,8 @@ public class ChunkMesh {
|
||||
// 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( poly.getPointCount() );
|
||||
for ( final VectorReference ref : poly.getPoints() ) {
|
||||
writer.write( ref.index );
|
||||
}
|
||||
}
|
||||
@@ -252,7 +277,7 @@ public class ChunkMesh {
|
||||
|
||||
final PlaneMesh planeMesh = new PlaneMesh();
|
||||
planeMesh.generatedRegions = new ArrayDeque< Polygon >();
|
||||
planeMesh.completedPolygons = new ArrayDeque< IndexedPolygon >();
|
||||
planeMesh.completedPolygons = new ArrayDeque< IndexedTriangle >();
|
||||
final int regionCount = buffer.getInt();
|
||||
for ( int j = 0; j < regionCount; ++j ) {
|
||||
final int vertexCount = buffer.getInt();
|
||||
@@ -269,9 +294,22 @@ public class ChunkMesh {
|
||||
for ( int j = 0; j < triangleCount; ++j ) {
|
||||
final int vertexCount = buffer.getInt();
|
||||
|
||||
final IndexedPolygon triangle = new IndexedPolygon();
|
||||
final IndexedTriangle triangle = new IndexedTriangle();
|
||||
for ( int k = 0; k < vertexCount; ++k ) {
|
||||
triangle.points.add( references[ buffer.getInt() ] );
|
||||
switch ( k ) {
|
||||
case 0:
|
||||
triangle.setPoint1( references[ buffer.getInt() ] );
|
||||
break;
|
||||
case 1:
|
||||
triangle.setPoint2( references[ buffer.getInt() ] );
|
||||
break;
|
||||
case 2:
|
||||
triangle.setPoint3( references[ buffer.getInt() ] );
|
||||
break;
|
||||
default:
|
||||
// Discard
|
||||
buffer.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
planeMesh.completedPolygons.add( triangle );
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class IndexedNGon implements IndexedPolygon {
|
||||
List< VectorReference > points = new ArrayList< VectorReference >();
|
||||
|
||||
@Override
|
||||
public Collection< VectorReference > getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPointCount() {
|
||||
return points.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/*
|
||||
* Represents a polygon with associated vector references
|
||||
*/
|
||||
public abstract interface IndexedPolygon {
|
||||
public Collection< VectorReference > getPoints();
|
||||
public int getPointCount();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class IndexedTriangle implements IndexedPolygon {
|
||||
private VectorReference p1;
|
||||
private VectorReference p2;
|
||||
private VectorReference p3;
|
||||
|
||||
public VectorReference getPoint1() {
|
||||
return p1;
|
||||
}
|
||||
|
||||
public VectorReference getPoint2() {
|
||||
return p2;
|
||||
}
|
||||
|
||||
public VectorReference getPoint3() {
|
||||
return p3;
|
||||
}
|
||||
|
||||
public void setPoint1( VectorReference p1 ) {
|
||||
this.p1 = p1;
|
||||
}
|
||||
|
||||
public void setPoint2( VectorReference p2 ) {
|
||||
this.p2 = p2;
|
||||
}
|
||||
|
||||
public void setPoint3( VectorReference p3 ) {
|
||||
this.p3 = p3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection< VectorReference > getPoints() {
|
||||
return List.of( p1, p2, p3 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPointCount() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Point;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Point;
|
||||
|
||||
public class MinecraftPlane implements Comparable< MinecraftPlane > {
|
||||
public final PlaneAxis normal;
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.algorithm.Polygon;
|
||||
|
||||
public class PlaneMesh {
|
||||
public Collection< Polygon > generatedRegions;
|
||||
public Collection< IndexedTriangle > completedPolygons;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class SparseMesh extends AbstractMesh {
|
||||
private Map< Vector, BlockDataInformation > data = new HashMap< Vector, BlockDataInformation >();
|
||||
|
||||
// Bounding box of this mesh
|
||||
private Vector min, max;
|
||||
|
||||
public SparseMesh( Vector min, Vector max ) {
|
||||
this.min = min.clone();
|
||||
this.max = max.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMinimumCorner() {
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMaximumCorner() {
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockDataInformation getBlockDataInformationFor( Vector position ) {
|
||||
return data.get( position.toBlockVector() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlockDataInformationFor( BlockDataInformation information, Vector position ) {
|
||||
data.put( position, information );
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects;
|
||||
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.bukkit.plugin.SimplePluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.plugin.java.JavaPluginLoader;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.McMeshEnvironment;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.McMeshEnvironmentPlugin;
|
||||
|
||||
public class BukkitUtil {
|
||||
private static Constructor< PluginCommand > PLUGINCOMMAND_CONSTRUCTOR;
|
||||
@@ -69,7 +69,7 @@ public class BukkitUtil {
|
||||
public static PluginCommand constructCommand( String id ) {
|
||||
PluginCommand command = null;
|
||||
try {
|
||||
command = PLUGINCOMMAND_CONSTRUCTOR.newInstance( id, JavaPlugin.getPlugin( McMeshEnvironment.class ) );
|
||||
command = PLUGINCOMMAND_CONSTRUCTOR.newInstance( id, JavaPlugin.getPlugin( McMeshEnvironmentPlugin.class ) );
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
@@ -84,7 +84,7 @@ public class BukkitUtil {
|
||||
if ( Bukkit.getServer().isPrimaryThread() ) {
|
||||
Bukkit.getPluginManager().callEvent( event );
|
||||
} else {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask( JavaPlugin.getPlugin( McMeshEnvironment.class ), () -> { callEventSync( event ); } );
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask( JavaPlugin.getPlugin( McMeshEnvironmentPlugin.class ), () -> { callEventSync( event ); } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class BukkitUtil {
|
||||
if ( JAVAPLUGINLOADER_GETCLASS_1 != null ) {
|
||||
return ( Class< ? > ) JAVAPLUGINLOADER_GETCLASS_1.invoke( loader, name );
|
||||
} else {
|
||||
return ( Class< ? > ) JAVAPLUGINLOADER_GETCLASS_2.invoke( loader, name, true, JavaPlugin.getPlugin( MiniePlugin.class ).getDescription() );
|
||||
return ( Class< ? > ) JAVAPLUGINLOADER_GETCLASS_2.invoke( loader, name, true, JavaPlugin.getPlugin( McMeshEnvironmentPlugin.class ).getDescription() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkMesh;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.environment.objects.ChunkMesh;
|
||||
|
||||
public class ChunkMeshUtil {
|
||||
public static byte[] serialize( final ChunkMesh mesh ) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.mesh.environment.util;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R6.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R6.block.data.CraftBlockData;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import net.minecraft.core.BlockPosition;
|
||||
import net.minecraft.server.level.WorldServer;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import net.minecraft.world.phys.shapes.VoxelShapeCollision;
|
||||
|
||||
public class NmsUtil {
|
||||
// 1.21.5
|
||||
public static VoxelShape getShape( BlockData blockData, Location location, BlockShapeType type ) {
|
||||
final CraftBlockData data = ( CraftBlockData ) blockData;
|
||||
|
||||
WorldServer server = null;
|
||||
BlockPosition pos = null;
|
||||
if ( location != null ) {
|
||||
server = ( ( CraftWorld ) location.getWorld() ).getHandle();
|
||||
pos = new BlockPosition( location.getBlockX(), location.getBlockY(), location.getBlockZ() );
|
||||
}
|
||||
|
||||
switch ( type ) {
|
||||
default:
|
||||
case SHAPE:
|
||||
return data.getState().f( server, pos );
|
||||
case COLLISION_SHAPE:
|
||||
return data.getState().g( server, pos );
|
||||
case VISUAL_SHAPE:
|
||||
return data.getState().c( server, pos, VoxelShapeCollision.a() );
|
||||
case INTERACTION_SHAPE:
|
||||
return data.getState().i( server, pos );
|
||||
case BLOCK_SUPPORT_SHAPE:
|
||||
return data.getState().h( server, pos );
|
||||
}
|
||||
}
|
||||
|
||||
public static BoundingBox[] convertFrom( final VoxelShape shape ) {
|
||||
return convertFrom( shape, new Vector() );
|
||||
}
|
||||
|
||||
public static BoundingBox[] convertFrom( final VoxelShape shape, final Vector offset ) {
|
||||
return shape.e().stream()
|
||||
.map( aabb -> { return new BoundingBox( aabb.a, aabb.b, aabb.c, aabb.d, aabb.e, aabb.f ).shift( offset ); } )
|
||||
.toArray( BoundingBox[]::new );
|
||||
}
|
||||
|
||||
public enum BlockShapeType {
|
||||
SHAPE,
|
||||
COLLISION_SHAPE,
|
||||
VISUAL_SHAPE,
|
||||
INTERACTION_SHAPE,
|
||||
BLOCK_SUPPORT_SHAPE
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkMesh;
|
||||
|
||||
public class ChunkMeshCache {
|
||||
final File directory;
|
||||
Map< ChunkLocation, ChunkMesh > meshes = new TreeMap< ChunkLocation, ChunkMesh >();
|
||||
Collection< ChunkLocation > shouldRemove = new HashSet< ChunkLocation >();
|
||||
ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public ChunkMeshCache( final File directory ) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
public void onChunkLoad( final Chunk chunk ) {
|
||||
final ChunkLocation location = new ChunkLocation( chunk );
|
||||
|
||||
// Unqueue this chunk for removal if it's queued
|
||||
shouldRemove.remove( location );
|
||||
|
||||
// TODO Add a ticket
|
||||
chunk.addPluginChunkTicket( null );
|
||||
|
||||
// Does the mesh map contain the current location?
|
||||
{
|
||||
// If not, then load or mesh the chunk
|
||||
// Emit a chunk mesh complete event
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void saveAndRemove( final ChunkLocation location ) {
|
||||
lock.lock();
|
||||
meshes.remove( location );
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public void saveAndRemoveAll() {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import com.jme3.bullet.PhysicsSpace;
|
||||
import com.jme3.bullet.PhysicsSpace.BroadphaseType;
|
||||
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
|
||||
import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
|
||||
import com.jme3.bullet.objects.PhysicsBody;
|
||||
import com.jme3.bullet.objects.PhysicsRigidBody;
|
||||
import com.jme3.math.Plane;
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
public class MinieTest {
|
||||
|
||||
public static void main( String[] args ) {
|
||||
System.loadLibrary( "bulletjme" );
|
||||
|
||||
System.out.println( "Testing a simple PhysicsSpace" );
|
||||
PhysicsSpace space = new PhysicsSpace( BroadphaseType.DBVT );
|
||||
|
||||
PlaneCollisionShape plane = new PlaneCollisionShape( new Plane( new Vector3f( 0, 1, 0 ), 0 ) );
|
||||
PhysicsRigidBody planeBody = new PhysicsRigidBody( plane, PhysicsBody.massForStatic );
|
||||
planeBody.setPhysicsLocation( new Vector3f( 0, 0, 0 ) );
|
||||
|
||||
BoxCollisionShape box = new BoxCollisionShape( 0.5f );
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( box, 1f );
|
||||
rigid.setPhysicsLocation( new Vector3f( 0f, 100f, 0f ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
space.addCollisionObject( planeBody );
|
||||
|
||||
for ( float i = 0; i < 9.6; i += 1 / 256.0 ) {
|
||||
space.update( i );
|
||||
Vector3f loc = rigid.getPhysicsLocation();
|
||||
System.out.println( i + "\t: " + loc.getX() + ", " + loc.getY() + ", " + loc.getZ() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
public enum BlockDataType {
|
||||
NONE, COMPLEX, SOLID
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class IndexedPolygon {
|
||||
public List< VectorReference > points = new ArrayList< VectorReference >();
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest.objects;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.mesh.Polygon;
|
||||
|
||||
public class PlaneMesh {
|
||||
public Collection< Polygon > generatedRegions;
|
||||
public Collection< IndexedPolygon > completedPolygons;
|
||||
}
|
||||
Reference in New Issue
Block a user