Initial commit for historical purposes
This commit is contained in:
@@ -0,0 +1,768 @@
|
||||
package com.aaaaahhhhhhh.bananapuncher714.minietest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
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.BlockState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R4.block.data.CraftBlockData;
|
||||
import org.bukkit.entity.BlockDisplay;
|
||||
import org.bukkit.entity.Display;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Transformation;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Quaternionf;
|
||||
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.SubCommand;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.executor.CommandExecutableMessage;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.InputValidatorInt;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.command.validator.sender.SenderValidatorPlayer;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.objects.ChunkLocation;
|
||||
import com.aaaaahhhhhhh.bananapuncher714.minietest.util.FileUtil;
|
||||
import com.jme3.bullet.PhysicsSpace;
|
||||
import com.jme3.bullet.PhysicsSpace.BroadphaseType;
|
||||
import com.jme3.bullet.RotationOrder;
|
||||
import com.jme3.bullet.collision.ContactListener;
|
||||
import com.jme3.bullet.collision.PhysicsCollisionEvent;
|
||||
import com.jme3.bullet.collision.PhysicsCollisionListener;
|
||||
import com.jme3.bullet.collision.PhysicsCollisionObject;
|
||||
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
|
||||
import com.jme3.bullet.collision.shapes.CollisionShape;
|
||||
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
|
||||
import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
|
||||
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
|
||||
import com.jme3.bullet.joints.New6Dof;
|
||||
import com.jme3.bullet.joints.PhysicsJoint;
|
||||
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;
|
||||
|
||||
import net.minecraft.core.BlockPosition;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
|
||||
public class MiniePlugin extends JavaPlugin {
|
||||
private static final int TIME_STEPS = 5;
|
||||
private static final float SIMULATION_SPEED = 1f;
|
||||
|
||||
private PhysicsSpace space;
|
||||
private boolean paused = false;
|
||||
private boolean teleportOnDeactivate = false;
|
||||
private float tick = 0;
|
||||
private Map< PhysicsCollisionObject, AuxiliaryDisplayStruct > linkedDisplays = new HashMap< PhysicsCollisionObject, AuxiliaryDisplayStruct >();
|
||||
|
||||
private Set< PhysicsCollisionObject > active = new HashSet< PhysicsCollisionObject >();
|
||||
private Set< PhysicsCollisionObject > isTnt = new HashSet< PhysicsCollisionObject >();
|
||||
|
||||
private Map< PhysicsJoint, JointStruct > joints = new HashMap< PhysicsJoint, JointStruct >();
|
||||
|
||||
private Map< ChunkLocation, PhysicsRigidBody > chunks = new HashMap< ChunkLocation, PhysicsRigidBody >();
|
||||
|
||||
private Map< Vector3f, CollisionShape > collisionShapes = new HashMap< Vector3f, CollisionShape >();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
FileUtil.saveToFile( getResource( "native/windows/x86_64/bulletjme.dll" ), new File( getDataFolder() + "/lib", "bulletjme.dll" ), false );
|
||||
FileUtil.saveToFile( getResource( "native/osx/x86_64/libbulletjme.dylib" ), new File( getDataFolder() + "/lib", "libbulletjme.dylib" ), false );
|
||||
FileUtil.saveToFile( getResource( "native/linux/x86_64/libbulletjme.so" ), new File( getDataFolder() + "/lib", "libbulletjme.so" ), false );
|
||||
if ( !loadNativeLibraries() ) {
|
||||
getLogger().severe( "Unable to load Bullet JME native libraries! Disabling plugin" );
|
||||
Bukkit.getPluginManager().disablePlugin( this );
|
||||
return;
|
||||
}
|
||||
|
||||
registerCommands();
|
||||
|
||||
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, 128, 0 ) );
|
||||
space.addCollisionObject( planeBody );
|
||||
space.setAccuracy( 1f / ( TIME_STEPS * 20f ) );
|
||||
|
||||
space.addContactListener( new ContactListener() {
|
||||
@Override
|
||||
public void onContactEnded( long manifoldId ) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactProcessed( PhysicsCollisionObject objA, PhysicsCollisionObject objB, long manifoldPointId ) {
|
||||
if ( isTnt.remove( objA ) ) {
|
||||
impulse( objA.getPhysicsLocation(), 30f );
|
||||
if ( linkedDisplays.containsKey( objA ) ) {
|
||||
linkedDisplays.get( objA ).display.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if ( isTnt.remove( objB ) ) {
|
||||
impulse( objB.getPhysicsLocation(), 30f );
|
||||
if ( linkedDisplays.containsKey( objB ) ) {
|
||||
linkedDisplays.get( objB ).display.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactStarted( long manifoldId ) {
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
Bukkit.getPluginManager().registerEvents( new Listener() {
|
||||
@EventHandler
|
||||
private void onChunkLoad( ChunkLoadEvent event ) {
|
||||
if ( event.getWorld().getName().equalsIgnoreCase( "world" ) ) {
|
||||
getLogger().info( "Loading chunk..." );
|
||||
// scanAndGenerate( event.getChunk() );
|
||||
saveChunk( event.getChunk() );
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
private void onChunkUnload( ChunkUnloadEvent event ) {
|
||||
if ( event.getWorld().getName().equalsIgnoreCase( "world" ) ) {
|
||||
ChunkLocation loc = new ChunkLocation( event.getChunk() );
|
||||
if ( chunks.containsKey( loc ) ) {
|
||||
space.removeCollisionObject( chunks.get( loc ) );
|
||||
chunks.remove( loc );
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this );
|
||||
|
||||
|
||||
getLogger().info( "There are " + Bukkit.getWorld( "world" ).getLoadedChunks().length + " chunks" );
|
||||
getLogger().info( "Converted " + saveAllChunks( Bukkit.getWorld( "world" ) ) );
|
||||
|
||||
Bukkit.getScheduler().runTaskTimer( this, () -> {
|
||||
if ( !paused ) {
|
||||
// Update the real world with the physics space objects
|
||||
space.distributeEvents();
|
||||
space.update( tick, TIME_STEPS );
|
||||
|
||||
for ( Iterator< Entry< PhysicsJoint, JointStruct > > it = joints.entrySet().iterator(); it.hasNext(); ) {
|
||||
Entry< PhysicsJoint, JointStruct > entry = it.next();
|
||||
PhysicsJoint joint = entry.getKey();
|
||||
JointStruct struct = entry.getValue();
|
||||
BlockState state = struct.state;
|
||||
|
||||
if ( state.getBlock().getType() != state.getType() || !state.getChunk().isLoaded() ) {
|
||||
space.removeJoint( joint );
|
||||
space.remove( struct.object );
|
||||
it.remove();
|
||||
} else if ( !joint.isEnabled() ) {
|
||||
Location loc = state.getLocation();
|
||||
BlockData displayData = state.getBlockData();
|
||||
|
||||
// Create the display
|
||||
BlockDisplay display = loc.getWorld().spawn( loc, BlockDisplay.class, d -> {
|
||||
d.setBlock( displayData );
|
||||
} );
|
||||
|
||||
linkedDisplays.put( struct.object, new AuxiliaryDisplayStruct( display, struct.offset ) );
|
||||
|
||||
state.getBlock().setType( Material.AIR );
|
||||
|
||||
struct.object.setProtectGravity( false );
|
||||
Vector3f grav = new Vector3f();
|
||||
space.setGravity( grav );
|
||||
struct.object.setGravity( grav );
|
||||
|
||||
space.removeJoint( joint );
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
for ( Iterator< Entry< PhysicsCollisionObject, AuxiliaryDisplayStruct > > it = linkedDisplays.entrySet().iterator(); it.hasNext(); ) {
|
||||
Entry< PhysicsCollisionObject, AuxiliaryDisplayStruct > entry = it.next();
|
||||
PhysicsCollisionObject obj = entry.getKey();
|
||||
AuxiliaryDisplayStruct disp = entry.getValue();
|
||||
|
||||
if ( !disp.display.isValid() ) {
|
||||
it.remove();
|
||||
space.removeCollisionObject( obj );
|
||||
active.remove( obj );
|
||||
isTnt.remove( obj );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Location displayLocation = disp.display.getLocation();
|
||||
|
||||
// Do not calculate for this object if it is inactive
|
||||
if ( !obj.isActive() ) {
|
||||
if ( teleportOnDeactivate && active.remove( obj ) ) {
|
||||
// Newly deactivated object, teleport the display to the actual position to prevent the translation from getting too large
|
||||
|
||||
Transformation currentTransformation = disp.display.getTransformation();
|
||||
org.joml.Vector3f trans = currentTransformation.getTranslation();
|
||||
|
||||
displayLocation.add( trans.x(), trans.y(), trans.z() );
|
||||
disp.display.teleport( displayLocation );
|
||||
|
||||
Transformation newTrans = new Transformation( new org.joml.Vector3f( 0, 0, 0 ), currentTransformation.getLeftRotation(), currentTransformation.getScale(), currentTransformation.getRightRotation() );
|
||||
disp.display.setInterpolationDelay( 0 );
|
||||
disp.display.setInterpolationDuration( 0 );
|
||||
disp.display.setTransformation( newTrans );
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
active.add( obj );
|
||||
}
|
||||
|
||||
Vector3f location = obj.getPhysicsLocation();
|
||||
Quaternion rotation = new Quaternion();
|
||||
obj.getPhysicsRotation( rotation );
|
||||
|
||||
Vector offsetVec = disp.offset;
|
||||
Vector scale = disp.scale;
|
||||
Matrix4f transformRot = new Matrix4f();
|
||||
transformRot.set( new Quaternionf( rotation.getX(), rotation.getY(), rotation.getZ(), rotation.getW() ) );
|
||||
|
||||
transformRot.mul( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, - ( float ) offsetVec.getX(), - ( float ) offsetVec.getY(), - ( float ) offsetVec.getZ(), 1 );
|
||||
transformRot.mul( ( float ) scale.getX(), 0, 0, 0, 0, ( float ) scale.getY(), 0, 0, 0, 0, ( float ) scale.getZ(), 0, 0, 0, 0, 1 );
|
||||
Matrix4f transMat = new Matrix4f( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ( float ) ( location.getX() - displayLocation.getX() ), ( float ) ( location.getY() - displayLocation.getY() ), ( float ) ( location.getZ() - displayLocation.getZ() ), 1 );
|
||||
|
||||
disp.display.setInterpolationDelay( 0 );
|
||||
disp.display.setInterpolationDuration( 2 );
|
||||
disp.display.setTransformationMatrix( transMat.mul( transformRot ) );
|
||||
}
|
||||
|
||||
tick += SIMULATION_SPEED / 20f;
|
||||
}
|
||||
}, 0, 1 );
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
new SubCommand( "minie" )
|
||||
.addSenderValidator( new SenderValidatorPlayer() )
|
||||
.add( new SubCommand( "spawn" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
Location loc = player.getLocation();
|
||||
loc.setPitch( 0 );
|
||||
loc.setDirection( new Vector( 0, 0, 0 ) );
|
||||
|
||||
BlockData displayData;
|
||||
ItemStack item = player.getInventory().getItemInMainHand();
|
||||
if ( item != null && item.getType() != Material.AIR && item.getType().isBlock() ) {
|
||||
displayData = item.getType().createBlockData();
|
||||
} else {
|
||||
displayData = Material.TNT.createBlockData();
|
||||
}
|
||||
|
||||
CraftBlockData data = ( CraftBlockData ) displayData;
|
||||
|
||||
BoundingBox[] boxes = convertFrom( data.getState().f( null, null ) );
|
||||
Vector blockCenter = calculateCenter( boxes );
|
||||
|
||||
CollisionShape box;
|
||||
if ( boxes.length == 0 ) {
|
||||
player.sendMessage( "No bounding box detected! Wrong method?" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( boxes.length == 1 ) {
|
||||
Vector min = boxes[ 0 ].getMin();
|
||||
Vector center = boxes[ 0 ].getCenter().subtract( min );
|
||||
box = new BoxCollisionShape( ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
} else {
|
||||
CompoundCollisionShape compound = new CompoundCollisionShape();
|
||||
for ( BoundingBox aabb : boxes ) {
|
||||
Vector min = aabb.getMin();
|
||||
Vector center = aabb.getCenter();
|
||||
Vector half = center.clone().subtract( min );
|
||||
center.subtract( blockCenter );
|
||||
CollisionShape subShape = new BoxCollisionShape( ( float ) half.getX(), ( float ) half.getY(), ( float ) half.getZ() );
|
||||
compound.addChildShape( subShape, ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
}
|
||||
box = compound;
|
||||
}
|
||||
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( box, 1f );
|
||||
|
||||
// Since the center of the rigid body and the box shape are different, we need to offset the location
|
||||
rigid.setPhysicsLocation( new Vector3f( ( float ) ( loc.getX() + blockCenter.getX() ), ( float ) ( loc.getY() + blockCenter.getY() ), ( float ) ( loc.getZ() + blockCenter.getZ() ) ) );
|
||||
|
||||
// Convert the display direction vector to a quaternion
|
||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
|
||||
// Create the display
|
||||
BlockDisplay display = loc.getWorld().spawn( loc, BlockDisplay.class, d -> {
|
||||
d.setBlock( displayData );
|
||||
} );
|
||||
|
||||
linkedDisplays.put( rigid, new AuxiliaryDisplayStruct( display, blockCenter ) );
|
||||
|
||||
if ( displayData.getMaterial() == Material.TNT ) {
|
||||
isTnt.add( rigid );
|
||||
}
|
||||
|
||||
player.sendMessage( "Created display" );
|
||||
} ) )
|
||||
.add( new SubCommand( "constraint" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
Location loc = player.getLocation();
|
||||
loc.setPitch( 0 );
|
||||
loc.setDirection( new Vector( 0, 0, 0 ) );
|
||||
|
||||
BlockData displayData = Material.IRON_TRAPDOOR.createBlockData();
|
||||
CraftBlockData data = ( CraftBlockData ) displayData;
|
||||
|
||||
final Vector scale = new Vector( 5, 2, 5 );
|
||||
|
||||
BlockPosition pos = new BlockPosition( loc.getBlockX(), loc.getBlockY(), loc.getBlockZ() );
|
||||
BoundingBox[] boxes = convertFrom( data.getState().f( ( ( CraftWorld ) loc.getWorld() ).getHandle(), pos ) );
|
||||
for ( BoundingBox box : boxes ) {
|
||||
Vector min = box.getMin().multiply( scale );
|
||||
Vector max = box.getMax().multiply( scale );
|
||||
box.resize( min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ() );
|
||||
}
|
||||
Vector blockCenter = calculateCenter( boxes );
|
||||
|
||||
CollisionShape box;
|
||||
if ( boxes.length == 0 ) {
|
||||
player.sendMessage( "No bounding box detected! Wrong method?" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( boxes.length == 1 ) {
|
||||
Vector min = boxes[ 0 ].getMin();
|
||||
Vector center = boxes[ 0 ].getCenter().subtract( min );
|
||||
box = new BoxCollisionShape( ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
} else {
|
||||
CompoundCollisionShape compound = new CompoundCollisionShape();
|
||||
for ( BoundingBox aabb : boxes ) {
|
||||
Vector min = aabb.getMin();
|
||||
Vector center = aabb.getCenter();
|
||||
Vector half = center.clone().subtract( min );
|
||||
center.subtract( blockCenter );
|
||||
CollisionShape subShape = new BoxCollisionShape( ( float ) half.getX(), ( float ) half.getY(), ( float ) half.getZ() );
|
||||
compound.addChildShape( subShape, ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
}
|
||||
box = compound;
|
||||
}
|
||||
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( box, 5f );
|
||||
|
||||
// Since the center of the rigid body and the box shape are different, we need to offset the location
|
||||
rigid.setPhysicsLocation( new Vector3f( ( float ) ( loc.getX() + blockCenter.getX() ), ( float ) ( loc.getY() + blockCenter.getY() ), ( float ) ( loc.getZ() + blockCenter.getZ() ) ) );
|
||||
|
||||
// Convert the display direction vector to a quaternion
|
||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
|
||||
New6Dof constraint = new New6Dof( rigid, new Vector3f( 0, 0, 0 ), rigid.getPhysicsLocation(), Matrix3f.IDENTITY, Matrix3f.IDENTITY, RotationOrder.XYZ );
|
||||
|
||||
constraint.set( MotorParam.UpperLimit, 3, ( float ) Math.PI / 6 );
|
||||
constraint.set( MotorParam.LowerLimit, 3, ( float ) - Math.PI / 6 );
|
||||
constraint.set( MotorParam.UpperLimit, 4, 0 );
|
||||
constraint.set( MotorParam.LowerLimit, 4, 0 );
|
||||
constraint.set( MotorParam.UpperLimit, 5, 0 );
|
||||
constraint.set( MotorParam.LowerLimit, 5, 0 );
|
||||
|
||||
space.addJoint( constraint );
|
||||
|
||||
player.sendMessage( "Location: " + rigid.getPhysicsLocation() );
|
||||
|
||||
// Create the display
|
||||
BlockDisplay display = loc.getWorld().spawn( loc, BlockDisplay.class, d -> {
|
||||
d.setBlock( displayData );
|
||||
} );
|
||||
|
||||
linkedDisplays.put( rigid, new AuxiliaryDisplayStruct( display, blockCenter ).setScale( scale ) );
|
||||
|
||||
player.sendMessage( "Created constraint" );
|
||||
}) )
|
||||
.add( new SubCommand( "fixed" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
Block block = player.getTargetBlockExact( 20 );
|
||||
convert( block );
|
||||
|
||||
player.sendMessage( "Fixed" );
|
||||
} ) )
|
||||
.add( new SubCommand( "chunk" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
|
||||
player.sendMessage( "Registering chunk..." );
|
||||
player.sendMessage( "Registered " + scanAndGenerate( player.getLocation().getChunk() ) );
|
||||
} ) )
|
||||
.add( new SubCommand( "toggle" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
paused = !paused;
|
||||
} ) )
|
||||
.add( new SubCommand( "teleport" )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
teleportOnDeactivate = !teleportOnDeactivate;
|
||||
} ) )
|
||||
.add( new SubCommand( "impulse" )
|
||||
.add( new SubCommand( new InputValidatorInt() )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
Location loc = player.getLocation();
|
||||
|
||||
impulse( new Vector3f( ( float ) loc.getX(), ( float ) loc.getY(), ( float ) loc.getZ() ), params.getLast( int.class ) );
|
||||
} ) )
|
||||
.defaultTo( ( sender, args, params ) -> {
|
||||
Player player = ( Player ) sender;
|
||||
Location loc = player.getLocation();
|
||||
|
||||
impulse( new Vector3f( ( float ) loc.getX(), ( float ) loc.getY(), ( float ) loc.getZ() ), 15f );
|
||||
} ) )
|
||||
.defaultTo( new CommandExecutableMessage( "An argument must be provided" ) )
|
||||
.whenUnknown( new CommandExecutableMessage( "Unknown argument" ) )
|
||||
.applyTo( getCommand( "minie" ) );
|
||||
}
|
||||
|
||||
private void impulse( Vector3f location, float power ) {
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( new SphereCollisionShape( 20 ), 1f );
|
||||
rigid.setPhysicsLocation( location );
|
||||
space.contactTest( rigid, new PhysicsCollisionListener() {
|
||||
@Override
|
||||
public void collision( PhysicsCollisionEvent event ) {
|
||||
if ( event.getObjectB() instanceof PhysicsRigidBody ) {
|
||||
PhysicsRigidBody objB = ( PhysicsRigidBody ) event.getObjectB();
|
||||
|
||||
Vector3f rLoc = objB.getPhysicsLocation();
|
||||
Vector3f to = rLoc.subtract( ( float ) location.getX(), ( float ) location.getY(), ( float ) location.getZ() );
|
||||
|
||||
float distSq = to.lengthSquared();
|
||||
|
||||
if ( distSq != 0 ) {
|
||||
if ( power / distSq > 1 ) {
|
||||
objB.applyCentralImpulse( to.normalize().mult( power / distSq ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private int saveAllChunks( World world ) {
|
||||
int total = 0;
|
||||
for ( Chunk chunk : world.getLoadedChunks() ) {
|
||||
total += saveChunk( chunk );
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private int saveChunk( Chunk chunk ) {
|
||||
File saveFile = new File( getDataFolder() + "/chunks", chunk.getWorld().getName() + "," + chunk.getX() + "," + chunk.getZ() );
|
||||
saveFile.getParentFile().mkdirs();
|
||||
saveFile.delete();
|
||||
|
||||
int count = 0;
|
||||
try ( FileWriter writer = new FileWriter( saveFile ) ) {
|
||||
World world = chunk.getWorld();
|
||||
for ( int y = world.getMinHeight(); y < world.getMaxHeight(); y++ ) {
|
||||
for ( int z = 0; z < 16; z++ ) {
|
||||
for ( int x = 0; x < 16; x++ ) {
|
||||
Block block = chunk.getBlock( x, y, z );
|
||||
if ( !( block.isEmpty() && block.isLiquid() ) ) {
|
||||
BlockState state = block.getState();
|
||||
BlockData displayData = state.getBlockData();
|
||||
Location loc = block.getLocation();
|
||||
CraftBlockData data = ( CraftBlockData ) displayData;
|
||||
|
||||
BlockPosition pos = new BlockPosition( loc.getBlockX(), loc.getBlockY(), loc.getBlockZ() );
|
||||
BoundingBox[] boxes = convertFrom( data.getState().f( ( ( CraftWorld ) world ).getHandle(), pos ) );
|
||||
|
||||
if ( boxes.length > 0 ) {
|
||||
// Save this block
|
||||
for ( BoundingBox box : boxes ) {
|
||||
writer.write( ( box.getMinX() + x ) + "," );
|
||||
writer.write( ( box.getMinY() + y ) + "," );
|
||||
writer.write( ( box.getMinZ() + z ) + "," );
|
||||
writer.write( ( box.getMaxX() + x ) + "," );
|
||||
writer.write( ( box.getMaxY() + y ) + "," );
|
||||
writer.write( ( box.getMaxZ() + z ) + "\n" );
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int scanAndGenerate( World world ) {
|
||||
int total = 0;
|
||||
for ( Chunk chunk : world.getLoadedChunks() ) {
|
||||
total += scanAndGenerate( chunk );
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private int scanAndGenerate( Chunk chunk ) {
|
||||
World world = chunk.getWorld();
|
||||
CompoundCollisionShape compound = new CompoundCollisionShape();
|
||||
int worldHeight = world.getMaxHeight() - world.getMinHeight();
|
||||
boolean[][][] isSolid = new boolean[ worldHeight ][ 16 ][ 16 ];
|
||||
for ( int y = 0; y < worldHeight; y++ ) {
|
||||
for ( int z = 0; z < 16; z++ ) {
|
||||
for ( int x = 0; x < 16; x++ ) {
|
||||
Block block = chunk.getBlock( x, y + world.getMinHeight(), z );
|
||||
if ( !( block.isEmpty() && block.isLiquid() ) ) {
|
||||
BlockState state = block.getState();
|
||||
BlockData displayData = state.getBlockData();
|
||||
Location loc = block.getLocation();
|
||||
CraftBlockData data = ( CraftBlockData ) displayData;
|
||||
|
||||
BlockPosition pos = new BlockPosition( loc.getBlockX(), loc.getBlockY(), loc.getBlockZ() );
|
||||
BoundingBox[] boxes = convertFrom( data.getState().f( ( ( CraftWorld ) world ).getHandle(), pos ) );
|
||||
|
||||
if ( boxes.length == 1 && boxes[ 0 ].getVolume() == 1 ) {
|
||||
isSolid[ y ][ z ][ x ] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( int y = 0; y < worldHeight; y++ ) {
|
||||
for ( int z = 0; z < 16; z++ ) {
|
||||
for ( int x = 0; x < 16; x++ ) {
|
||||
if ( x > 0 && x < 15 && z > 0 && z < 15 && y > 0 && y < worldHeight - 1 ) {
|
||||
// Figure out if this block is surrounded by solid blocks to avoid having to add this block
|
||||
if ( isSolid[ y - 1 ][ z ][ x ]
|
||||
&& isSolid[ y + 1 ][ z ][ x ]
|
||||
&& isSolid[ y ][ z - 1 ][ x ]
|
||||
&& isSolid[ y ][ z + 1 ][ x ]
|
||||
&& isSolid[ y ][ z ][ x - 1 ]
|
||||
&& isSolid[ y ][ z ][ x + 1 ] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Block block = chunk.getBlock( x, y + world.getMinHeight(), z );
|
||||
|
||||
if ( !( block.isEmpty() && block.isLiquid() ) ) {
|
||||
BlockState state = block.getState();
|
||||
BlockData displayData = state.getBlockData();
|
||||
Location loc = block.getLocation();
|
||||
CraftBlockData data = ( CraftBlockData ) displayData;
|
||||
|
||||
BlockPosition pos = new BlockPosition( loc.getBlockX(), loc.getBlockY(), loc.getBlockZ() );
|
||||
BoundingBox[] boxes = convertFrom( data.getState().f( ( ( CraftWorld ) world ).getHandle(), pos ) );
|
||||
|
||||
if ( boxes.length == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( boxes.length == 1 ) {
|
||||
Vector min = boxes[ 0 ].getMin();
|
||||
Vector center = boxes[ 0 ].getCenter().subtract( min );
|
||||
Vector3f halfExtents = new Vector3f( ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
|
||||
CollisionShape shape = collisionShapes.get( halfExtents );
|
||||
if ( shape == null ) {
|
||||
shape = new BoxCollisionShape( halfExtents );
|
||||
collisionShapes.put( halfExtents, shape );
|
||||
}
|
||||
Vector3f shapeCenter = new Vector3f( ( float ) boxes[ 0 ].getCenterX() + x, ( float ) boxes[ 0 ].getCenterY() + y, ( float ) boxes[ 0 ].getCenterZ() + z );
|
||||
compound.addChildShape( shape, shapeCenter );
|
||||
} else {
|
||||
for ( BoundingBox aabb : boxes ) {
|
||||
Vector min = aabb.getMin();
|
||||
Vector center = aabb.getCenter();
|
||||
Vector half = center.clone().subtract( min );
|
||||
Vector3f halfExtents = new Vector3f( ( float ) half.getX(), ( float ) half.getY(), ( float ) half.getZ() );
|
||||
|
||||
CollisionShape shape = collisionShapes.get( halfExtents );
|
||||
if ( shape == null ) {
|
||||
shape = new BoxCollisionShape( halfExtents );
|
||||
collisionShapes.put( halfExtents, shape );
|
||||
}
|
||||
compound.addChildShape( shape, ( float ) center.getX() + x, ( float ) center.getY() + y, ( float ) center.getZ() + z );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getLogger().info( "Chunk has " + compound.countChildren() );
|
||||
|
||||
// Create a rigid body
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( compound, PhysicsBody.massForStatic );
|
||||
rigid.setPhysicsLocation( new Vector3f( chunk.getX() << 4, world.getMinHeight(), chunk.getZ() << 4 ) );
|
||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
|
||||
PhysicsRigidBody old = chunks.put( new ChunkLocation( chunk ), rigid );
|
||||
if ( old != null ) {
|
||||
getLogger().warning( "Old rigid body found!" );
|
||||
space.remove( old );
|
||||
}
|
||||
|
||||
return compound.countChildren();
|
||||
}
|
||||
|
||||
private boolean convert( Block block ) {
|
||||
BlockState state = block.getState();
|
||||
BlockData displayData = state.getBlockData();
|
||||
Location loc = block.getLocation();
|
||||
|
||||
CraftBlockData data = ( CraftBlockData ) displayData;
|
||||
|
||||
BlockPosition pos = new BlockPosition( loc.getBlockX(), loc.getBlockY(), loc.getBlockZ() );
|
||||
BoundingBox[] boxes = convertFrom( data.getState().f( ( ( CraftWorld ) loc.getWorld() ).getHandle(), pos ) );
|
||||
Vector blockCenter = calculateCenter( boxes );
|
||||
|
||||
CollisionShape box;
|
||||
if ( boxes.length == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( boxes.length == 1 ) {
|
||||
Vector min = boxes[ 0 ].getMin();
|
||||
Vector center = boxes[ 0 ].getCenter().subtract( min );
|
||||
box = new BoxCollisionShape( ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
} else {
|
||||
CompoundCollisionShape compound = new CompoundCollisionShape();
|
||||
for ( BoundingBox aabb : boxes ) {
|
||||
Vector min = aabb.getMin();
|
||||
Vector center = aabb.getCenter();
|
||||
Vector half = center.clone().subtract( min );
|
||||
center.subtract( blockCenter );
|
||||
CollisionShape subShape = new BoxCollisionShape( ( float ) half.getX(), ( float ) half.getY(), ( float ) half.getZ() );
|
||||
compound.addChildShape( subShape, ( float ) center.getX(), ( float ) center.getY(), ( float ) center.getZ() );
|
||||
}
|
||||
box = compound;
|
||||
}
|
||||
|
||||
PhysicsRigidBody rigid = new PhysicsRigidBody( box, 1f );
|
||||
rigid.setProtectGravity( true );
|
||||
rigid.setGravity( new Vector3f( 0, 0, 0 ) );
|
||||
|
||||
// Since the center of the rigid body and the box shape are different, we need to offset the location
|
||||
rigid.setPhysicsLocation( new Vector3f( ( float ) ( loc.getX() + blockCenter.getX() ), ( float ) ( loc.getY() + blockCenter.getY() ), ( float ) ( loc.getZ() + blockCenter.getZ() ) ) );
|
||||
|
||||
// Convert the display direction vector to a quaternion
|
||||
rigid.setPhysicsRotation( new Quaternion( 0, 0, 0, 1 ) );
|
||||
|
||||
space.addCollisionObject( rigid );
|
||||
|
||||
New6Dof constraint = new New6Dof( rigid, new Vector3f( 0, 0, 0 ), rigid.getPhysicsLocation(), Matrix3f.IDENTITY, Matrix3f.IDENTITY, RotationOrder.XYZ );
|
||||
|
||||
constraint.setBreakingImpulseThreshold( 5 );
|
||||
constraint.set( MotorParam.UpperLimit, 3, 0 );
|
||||
constraint.set( MotorParam.LowerLimit, 3, 0 );
|
||||
constraint.set( MotorParam.UpperLimit, 4, 0 );
|
||||
constraint.set( MotorParam.LowerLimit, 4, 0 );
|
||||
constraint.set( MotorParam.UpperLimit, 5, 0 );
|
||||
constraint.set( MotorParam.LowerLimit, 5, 0 );
|
||||
|
||||
space.addJoint( constraint );
|
||||
|
||||
joints.put( constraint, new JointStruct( state, rigid, blockCenter ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final boolean loadNativeLibraries() {
|
||||
if ( SystemUtils.IS_OS_MAC ) {
|
||||
System.load( new File( getDataFolder() + "/lib", "libbulletjme.dylib" ).getAbsolutePath() );
|
||||
} else if ( SystemUtils.IS_OS_LINUX ) {
|
||||
System.load( new File( getDataFolder() + "/lib", "libbulletjme.so" ).getAbsolutePath() );
|
||||
} else if ( SystemUtils.IS_OS_WINDOWS ) {
|
||||
System.load( new File( getDataFolder() + "/lib", "bulletjme.dll" ).getAbsolutePath() );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Vector calculateCenter( BoundingBox[] boxes ) {
|
||||
Vector center = new Vector( 0, 0, 0 );
|
||||
// TODO Throw error, probably
|
||||
if ( boxes.length == 0 ) {
|
||||
return center;
|
||||
}
|
||||
|
||||
double totalVolume = 0;
|
||||
for ( BoundingBox box : boxes ) {
|
||||
Vector mid = box.getCenter();
|
||||
center.add( mid.multiply( box.getVolume() ) );
|
||||
totalVolume += box.getVolume();
|
||||
}
|
||||
return center.multiply( 1 / totalVolume );
|
||||
}
|
||||
|
||||
private static BoundingBox[] convertFrom( VoxelShape shape ) {
|
||||
return shape.e().stream()
|
||||
.map( aabb -> { return new BoundingBox( aabb.a, aabb.b, aabb.c, aabb.d, aabb.e, aabb.f ); } )
|
||||
.toArray( BoundingBox[]::new );
|
||||
}
|
||||
|
||||
private class JointStruct {
|
||||
BlockState state;
|
||||
PhysicsRigidBody object;
|
||||
Vector offset;
|
||||
|
||||
JointStruct( BlockState state, PhysicsRigidBody body, Vector offset ) {
|
||||
this.state = state;
|
||||
this.object = body;
|
||||
this.offset = offset.clone();
|
||||
}
|
||||
}
|
||||
|
||||
private class AuxiliaryDisplayStruct {
|
||||
AuxiliaryDisplayStruct( Display display, Vector offset ) {
|
||||
this.display = display;
|
||||
this.offset = offset.clone();
|
||||
this.scale = new Vector( 1, 1, 1 );
|
||||
}
|
||||
|
||||
AuxiliaryDisplayStruct setScale( Vector scale ) {
|
||||
this.scale = scale.clone();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Display display;
|
||||
Vector offset;
|
||||
Vector scale;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user