diff --git a/assets/Data/creatures.json b/assets/Data/creatures.json index bb8841ae..bb77deb1 100644 --- a/assets/Data/creatures.json +++ b/assets/Data/creatures.json @@ -88,9 +88,18 @@ { "type" : "GROUND", "acceleration" : 0.001, - "maxVelocity" : 0.015 + "maxVelocity" : 0.025 } ], + "physicsObject" : { + "type" : "CYLINDER", + "dimension1" : 0.2, + "dimension2" : 0.45, + "dimension3" : 0.2, + "offsetX" : 0, + "offsetY" : -0.45, + "offsetZ" : 0 + }, "attackMoves" : [ { "type" : "MELEE_WEAPON_SWING_ONE_HAND", @@ -199,6 +208,15 @@ "maxVelocity" : 0.025 } ], + "physicsObject" : { + "type" : "CYLINDER", + "dimension1" : 0.2, + "dimension2" : 0.2, + "dimension3" : 0.2, + "offsetX" : 0, + "offsetY" : -0.2, + "offsetZ" : 0 + }, "attackMoves" : [ { "type" : "MELEE_WEAPON_SWING_ONE_HAND", diff --git a/assets/Data/structures.json b/assets/Data/structures.json new file mode 100644 index 00000000..2d0badb7 --- /dev/null +++ b/assets/Data/structures.json @@ -0,0 +1,140 @@ +{ + "structures" : [ + { + "name" : "building1", + "modelPath" : "Models/building1.fbx", + "collision" : [ + { + "type" : "CUBE", + "positionX" : 0, + "positionY" : -1, + "positionZ" : 0.3, + "scaleX" : 7.5, + "scaleY" : 1.5, + "scaleZ" : 0.3, + "rotationW" : 1, + "rotationX" : 0, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : 7.2, + "positionY" : -1, + "positionZ" : 3, + "scaleX" : 0.3, + "scaleY" : 1.5, + "scaleZ" : 3, + "rotationW" : 1, + "rotationX" : 0, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : 7.2, + "positionY" : 0.4, + "positionZ" : 3, + "scaleX" : 0.3, + "scaleY" : 2, + "scaleZ" : 2, + "rotationW" : 0.9238796, + "rotationX" : 0.3826834, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : -7.2, + "positionY" : -1, + "positionZ" : 3, + "scaleX" : 0.3, + "scaleY" : 1.5, + "scaleZ" : 3, + "rotationW" : 1, + "rotationX" : 0, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : -7.2, + "positionY" : 0.4, + "positionZ" : 3, + "scaleX" : 0.3, + "scaleY" : 2, + "scaleZ" : 2, + "rotationW" : 0.9238796, + "rotationX" : 0.3826834, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : -1.5, + "positionY" : -1, + "positionZ" : 5.7, + "scaleX" : 5.5, + "scaleY" : 1.5, + "scaleZ" : 0.3, + "rotationW" : 1, + "rotationX" : 0, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : 6.75, + "positionY" : -1, + "positionZ" : 5.7, + "scaleX" : 0.75, + "scaleY" : 1.5, + "scaleZ" : 0.3, + "rotationW" : 1, + "rotationX" : 0, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : 0, + "positionY" : -2.6, + "positionZ" : 3, + "scaleX" : 7.5, + "scaleY" : 0.2, + "scaleZ" : 3, + "rotationW" : 1, + "rotationX" : 0, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : 0, + "positionY" : 1.3, + "positionZ" : 4.4, + "scaleX" : 7.5, + "scaleY" : 0.2, + "scaleZ" : 1.8, + "rotationW" : 0.9570922, + "rotationX" : 0.2897836, + "rotationY" : 0, + "rotationZ" : 0 + }, + { + "type" : "CUBE", + "positionX" : 0, + "positionY" : 1.3, + "positionZ" : 1.6, + "scaleX" : 7.5, + "scaleY" : 0.2, + "scaleZ" : 1.8, + "rotationW" : 0.9570922, + "rotationX" : -0.2897836, + "rotationY" : 0, + "rotationZ" : 0 + } + ] + } + ] +} \ No newline at end of file diff --git a/assets/Models/unitcube.fbx b/assets/Models/unitcube.fbx new file mode 100644 index 00000000..5c597300 Binary files /dev/null and b/assets/Models/unitcube.fbx differ diff --git a/assets/Models/unitcylinder.fbx b/assets/Models/unitcylinder.fbx new file mode 100644 index 00000000..b94ba405 Binary files /dev/null and b/assets/Models/unitcylinder.fbx differ diff --git a/assets/Models/unitplane.fbx b/assets/Models/unitplane.fbx new file mode 100644 index 00000000..b4633218 Binary files /dev/null and b/assets/Models/unitplane.fbx differ diff --git a/assets/Textures/default_texture_map.json b/assets/Textures/default_texture_map.json index fcebd4b2..ccebf1dc 100644 --- a/assets/Textures/default_texture_map.json +++ b/assets/Textures/default_texture_map.json @@ -77,6 +77,24 @@ "/Textures/building_diffuse.png", "/Textures/building_diffuse.png" ] + }, + "Models/unitcylinder.fbx" : { + "Cylinder" : [ + "/Textures/transparent_blue.png", + "/Textures/transparent_blue.png" + ] + }, + "Models/unitplane.fbx" : { + "Plane" : [ + "/Textures/transparent_blue.png", + "/Textures/transparent_blue.png" + ] + }, + "Models/unitcube.fbx" : { + "Cube" : [ + "/Textures/transparent_blue.png", + "/Textures/transparent_blue.png" + ] } } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 01711d08..5c42214f 100644 --- a/pom.xml +++ b/pom.xml @@ -87,13 +87,26 @@ 2.8.6 - + cz.advel.jbullet jbullet 20101010-1 + + org.apache.commons diff --git a/src/main/java/electrosphere/controls/ControlHandler.java b/src/main/java/electrosphere/controls/ControlHandler.java index 6147af69..f272ff7a 100644 --- a/src/main/java/electrosphere/controls/ControlHandler.java +++ b/src/main/java/electrosphere/controls/ControlHandler.java @@ -5,8 +5,8 @@ import electrosphere.entity.EntityDataStrings; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.AttackTree; -import electrosphere.entity.state.MovementTree; -import electrosphere.entity.state.MovementTree.MovementTreeState; +import electrosphere.entity.state.movement.MovementTree; +import electrosphere.entity.state.movement.MovementTree.MovementTreeState; import electrosphere.main.Globals; import electrosphere.menu.MenuTransition; import electrosphere.net.parser.net.message.EntityMessage; diff --git a/src/main/java/electrosphere/engine/LoadingThread.java b/src/main/java/electrosphere/engine/LoadingThread.java index 4a5ae3d2..63021e81 100644 --- a/src/main/java/electrosphere/engine/LoadingThread.java +++ b/src/main/java/electrosphere/engine/LoadingThread.java @@ -1,5 +1,6 @@ package electrosphere.engine; +import com.bulletphysics.collision.dispatch.CollisionObject; import electrosphere.controls.ControlHandler; import electrosphere.entity.CameraEntityUtils; import electrosphere.entity.Entity; @@ -17,6 +18,10 @@ import electrosphere.game.server.terrain.manager.ServerTerrainManager; import electrosphere.game.server.world.ServerWorldData; import electrosphere.entity.types.attach.AttachUtils; import electrosphere.entity.types.particle.ParticleUtils; +import electrosphere.entity.types.collision.CollisionObjUtils; +import electrosphere.entity.types.structure.StructureUtils; +import electrosphere.game.collision.PhysicsUtils; +import electrosphere.game.collision.collidable.Collidable; import electrosphere.game.server.ai.creature.MindlessAttacker; import electrosphere.game.state.MicroSimulation; import electrosphere.logger.LoggerInterface; @@ -35,6 +40,7 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import org.joml.Quaternionf; import org.joml.Vector3f; /** @@ -307,7 +313,6 @@ public class LoadingThread extends Thread { static void initServerArenaTerrainManager(){ Globals.serverTerrainManager = ServerTerrainManager.constructArenaTerrainManager(); - Globals.spawnPoint = new Vector3f(1,0,1); } static void initClientTerrainManager(){ @@ -322,7 +327,7 @@ public class LoadingThread extends Thread { static void initServerArenaWorldData(){ Globals.serverWorldData = ServerWorldData.createArenaWorld(); - Globals.spawnPoint = new Vector3f(0,0,0); + Globals.spawnPoint = new Vector3f(1,3,1); } static void initServerGameWorldData(){ @@ -335,7 +340,6 @@ public class LoadingThread extends Thread { } else { Globals.commonWorldData = new CommonWorldData(Globals.clientWorldData, Globals.clientTerrainManager); } - Globals.collisionEngine = new CollisionEngine(); } @@ -480,24 +484,24 @@ public class LoadingThread extends Thread { // } //trees \:D/ -// for(int i = 0; i < 10; i++){ -// Random rand = new Random(); -// String treePath = "Models/tree1.fbx"; -// Entity tree = EntityUtils.spawnDrawableEntity(treePath); -// EntityUtils.getPosition(tree).set(rand.nextFloat() * 150 + 10, 0, rand.nextFloat() * 150 + 10); -//// EntityUtils.getEntityRotation(tree).rotateAxis((float)-Math.PI/2.0f, new Vector3f(1,0,0)); -// } + for(int i = 0; i < 10; i++){ + Random rand = new Random(); + String treePath = "Models/tree1.fbx"; + Entity tree = EntityUtils.spawnDrawableEntity(treePath); + EntityUtils.getPosition(tree).set(rand.nextFloat() * 150 + 10, 0, rand.nextFloat() * 150 + 10); +// EntityUtils.getEntityRotation(tree).rotateAxis((float)-Math.PI/2.0f, new Vector3f(1,0,0)); + } - String buildingPath = "Models/building1.fbx"; - Entity building = EntityUtils.spawnDrawableEntity(buildingPath); - EntityUtils.getPosition(building).set(5,1.2f,5); - EntityUtils.getScale(building).set(0.5f); - EntityUtils.getRotation(building).rotateLocalY((float)(Math.PI)); +// String buildingPath = "Models/building1.fbx"; +// Entity building = EntityUtils.spawnDrawableEntity(buildingPath); +// EntityUtils.getPosition(building).set(5,1.2f,5); +// EntityUtils.getScale(building).set(0.5f); +// EntityUtils.getRotation(building).rotateLocalY((float)(Math.PI)); // ActorUtils.applyBlenderTransformer(building); - //spawn evil goblin +// //spawn evil goblin // Entity goblin = CreatureUtils.spawnBasicCreature("Goblin"); -// EntityUtils.getPosition(goblin).set(30, 0, 30); +// CreatureUtils.positionCharacter(goblin, new Vector3f(30, 3, 30)); // EntityUtils.getScale(goblin).set(0.005f); // //give evil goblin sword // Entity goblinSword = ItemUtils.spawnBasicItem("Katana"); @@ -506,12 +510,21 @@ public class LoadingThread extends Thread { // MindlessAttacker.attachToCreature(goblin); // // -// Entity testHomie = CreatureUtils.spawnBasicCreature("Human"); -// EntityUtils.getScale(testHomie).set(0.005f); -// EntityUtils.getPosition(testHomie).set(2,0,2); + Entity testHomie = CreatureUtils.spawnBasicCreature("Human"); + EntityUtils.getScale(testHomie).set(0.005f); + CreatureUtils.positionCharacter(testHomie, new Vector3f(10,1,10)); + + Entity sword = ItemUtils.spawnBasicItem("Katana"); + AttachUtils.attachEntityToEntityAtBone(testHomie, sword, "Bone.020"); + + +// CollisionObjUtils.spawnCollisionPlane(new Vector3f(1,1,1), new Vector3f(11,0.5f,5), new Quaternionf().rotateLocalX(0.75f)); // -// Entity sword = ItemUtils.spawnBasicItem("Katana"); -// AttachUtils.attachEntityToEntityAtBone(testHomie, sword, "Bone.020"); +// CollisionObjUtils.spawnCollisionCube(new Vector3f(1,1,1), new Vector3f(10,1,10), new Quaternionf()); + + CreatureUtils.positionCharacter(Globals.playerCharacter, new Vector3f(5,2,10)); + + StructureUtils.spawnBasicStructure("building1", new Vector3f(5,2.4f,15), new Quaternionf().rotateLocalY((float)Math.PI)); } diff --git a/src/main/java/electrosphere/entity/CameraEntityUtils.java b/src/main/java/electrosphere/entity/CameraEntityUtils.java index 0080a7a9..43dfc041 100644 --- a/src/main/java/electrosphere/entity/CameraEntityUtils.java +++ b/src/main/java/electrosphere/entity/CameraEntityUtils.java @@ -71,7 +71,7 @@ public class CameraEntityUtils { cameraEye, //eye cameraCenter, //center cameraUp // up - ).scale(1.0f, 1.5f, 1.0f); + ).scale(1.0f, 1.0f, 1.0f); return rVal; } diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index cd6696ed..acad78c0 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -69,6 +69,13 @@ public class EntityDataStrings { public static final String DATA_STRING_UI_ELEMENT = "uiEntity"; public static final String DATA_STRING_UI_ELEMENT_FONT = "uiFont"; + /* + Physics Entity + */ + public static final String PHYSICS_RIGID_BODY = "physicsRigidBody"; + public static final String PHYSICS_RIGID_BODY_OFFSET = "physicsRigidBodyOffset"; + public static final String PHYSICS_MODEL_TEMPLATE = "physicsModelTemplate"; + /* Collision Entity */ @@ -76,6 +83,14 @@ public class EntityDataStrings { public static final String DATA_STRING_COLLISION_ENTITY = "collisionEntity"; public static final String DATA_STRING_COLLISION_ENTITY_TYPE_SPHERE = "collisionSphere"; + + public static final String COLLISION_ENTITY_COLLISION_OBJECT = "collisionEntityBulletObject"; + public static final String COLLISION_ENTITY_COLLIDABLE = "collisionEntityCollidable"; + + public static final String COLLISION_ENTITY_TYPE_PLANE = "collisionTypePlane"; + public static final String COLLISION_ENTITY_TYPE_CUBE = "collisionTypeCube"; + + public static final String COLLISION_ENTITY_DATA_TYPE_HIT = "collisionDataTypeHit"; public static final String COLLISION_ENTITY_DATA_TYPE_HURT = "collisionDataTypeHurt"; diff --git a/src/main/java/electrosphere/entity/EntityUtils.java b/src/main/java/electrosphere/entity/EntityUtils.java index e191bf53..bf535902 100644 --- a/src/main/java/electrosphere/entity/EntityUtils.java +++ b/src/main/java/electrosphere/entity/EntityUtils.java @@ -5,7 +5,7 @@ */ package electrosphere.entity; -import electrosphere.entity.state.MovementTree; +import electrosphere.entity.state.movement.MovementTree; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.item.ItemUtils; import electrosphere.renderer.Model; diff --git a/src/main/java/electrosphere/entity/state/GravityTree.java b/src/main/java/electrosphere/entity/state/GravityTree.java new file mode 100644 index 00000000..d6e722df --- /dev/null +++ b/src/main/java/electrosphere/entity/state/GravityTree.java @@ -0,0 +1,122 @@ +package electrosphere.entity.state; + +import electrosphere.entity.Entity; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.net.parser.net.message.EntityMessage; +import electrosphere.renderer.Actor; +import java.util.concurrent.CopyOnWriteArrayList; +import org.joml.Vector3f; + +/** + * + * @author amaterasu + */ +public class GravityTree { + + public static enum IdleTreeState { + ACTIVE, + NOT_ACTIVE, + } + + IdleTreeState state; + + Entity parent; + + CopyOnWriteArrayList networkMessageQueue = new CopyOnWriteArrayList(); + + int frameCurrent; + + int maxFrame = 60; + + public GravityTree(Entity e){ + state = IdleTreeState.NOT_ACTIVE; + parent = e; + } + + public IdleTreeState getState(){ + return state; + } + + public void start(){ + //TODO: check if can start moving + state = IdleTreeState.ACTIVE; + frameCurrent = 0; + } + + public void interrupt(){ + state = IdleTreeState.NOT_ACTIVE; + } + + public void stop(){ + state = IdleTreeState.NOT_ACTIVE; + } + + public void simulate(){ + float velocity = CreatureUtils.getVelocity(parent); + float acceleration = CreatureUtils.getAcceleration(parent); + float maxNaturalVelocity = CreatureUtils.getMaxNaturalVelocity(parent); + Actor entityActor = EntityUtils.getActor(parent); + Vector3f position = EntityUtils.getPosition(parent); + Vector3f movementVector = CreatureUtils.getMovementVector(parent); + Vector3f newPosition; + + //parse attached network messages +// for(EntityMessage message : networkMessageQueue){ +// networkMessageQueue.remove(message); +//// System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ()); +// switch(message.getMessageSubtype()){ +// case ATTACKUPDATE: +// switch(message.gettreeState()){ +// case 0: +// state = IdleTreeState.IDLE; +// break; +// case 1: +// state = IdleTreeState.NOT_IDLE; +// break; +// } +// EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ()); +// CreatureUtils.setMovementVector(parent, new Vector3f(message.getrotationX(),message.getrotationY(),message.getrotationZ())); +// break; +// } +// } + + boolean isIdle; + + //state machine +// switch(state){ +// case ACTIVE: +// isIdle = true; +// if(hasMovementTree){ +// if(movementTree.getState() != MovementTreeState.IDLE){ +// isIdle = false; +// } +// } +// if(hasAttackTree){ +// if(attackTree.getState() != AttackTreeState.IDLE){ +// isIdle = false; +// } +// } +// if(!isIdle){ +// state = IdleTreeState.NOT_IDLE; +// } +// break; +// case NOT_ACTIVE: +// isIdle = true; +// if(hasAttackTree){ +// if(attackTree.getState() != AttackTreeState.IDLE){ +// isIdle = false; +// } +// } +// if(isIdle){ +// state = IdleTreeState.IDLE; +// } +// break; +// } + } + + public void addNetworkMessage(EntityMessage networkMessage) { + networkMessageQueue.add(networkMessage); + } + +} diff --git a/src/main/java/electrosphere/entity/state/IdleTree.java b/src/main/java/electrosphere/entity/state/IdleTree.java index c5219640..a544ea91 100644 --- a/src/main/java/electrosphere/entity/state/IdleTree.java +++ b/src/main/java/electrosphere/entity/state/IdleTree.java @@ -1,10 +1,11 @@ package electrosphere.entity.state; +import electrosphere.entity.state.movement.MovementTree; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.AttackTree.AttackTreeState; -import electrosphere.entity.state.MovementTree.MovementTreeState; +import electrosphere.entity.state.movement.MovementTree.MovementTreeState; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.renderer.Actor; diff --git a/src/main/java/electrosphere/entity/state/movement/Impulse.java b/src/main/java/electrosphere/entity/state/movement/Impulse.java new file mode 100644 index 00000000..123ab728 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/movement/Impulse.java @@ -0,0 +1,28 @@ +package electrosphere.entity.state.movement; + +import org.joml.Vector3f; + +/** + * + * @author amaterasu + */ +public class Impulse { + + Vector3f direction; + float force; + + public Impulse(Vector3f dir, float force){ + this.force = force; + this.direction = dir; + } + + public Vector3f getDirection() { + return direction; + } + + public float getForce() { + return force; + } + + +} diff --git a/src/main/java/electrosphere/entity/state/MovementTree.java b/src/main/java/electrosphere/entity/state/movement/MovementTree.java similarity index 51% rename from src/main/java/electrosphere/entity/state/MovementTree.java rename to src/main/java/electrosphere/entity/state/movement/MovementTree.java index 8c30ba11..0357fb58 100644 --- a/src/main/java/electrosphere/entity/state/MovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/MovementTree.java @@ -1,8 +1,12 @@ -package electrosphere.entity.state; +package electrosphere.entity.state.movement; +import com.bulletphysics.collision.dispatch.CollisionObject; +import com.bulletphysics.dynamics.RigidBody; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; +import electrosphere.game.collision.PhysicsUtils; +import electrosphere.game.collision.collidable.Collidable; import electrosphere.main.Globals; import electrosphere.net.NetUtils; import electrosphere.net.parser.net.message.EntityMessage; @@ -10,6 +14,7 @@ import electrosphere.renderer.Actor; import electrosphere.renderer.anim.Animation; import electrosphere.renderer.Model; import java.util.LinkedList; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.joml.Quaternionf; import org.joml.Vector3f; @@ -32,6 +37,9 @@ public class MovementTree { Entity parent; + CollisionObject body; + Collidable collidable; + CopyOnWriteArrayList networkMessageQueue = new CopyOnWriteArrayList(); long lastUpdateTime = 0; @@ -46,6 +54,11 @@ public class MovementTree { return state; } + public void setCollisionObject(CollisionObject body, Collidable collidable){ + this.body = body; + this.collidable = collidable; + } + public void start(){ //TODO: check if can start moving state = MovementTreeState.STARTUP; @@ -67,7 +80,9 @@ public class MovementTree { // Model entityModel = Globals.assetManager.fetchModel(EntityUtils.getEntityModelPath(parent)); Vector3f position = EntityUtils.getPosition(parent); Vector3f movementVector = CreatureUtils.getMovementVector(parent); + Quaternionf rotation = EntityUtils.getRotation(parent); Vector3f newPosition; + javax.vecmath.Matrix4f bodyTransformMatrix; //parse attached network messages for(EntityMessage message : networkMessageQueue){ @@ -123,6 +138,12 @@ public class MovementTree { } } + //handle impulses + for(Impulse impulse : collidable.getImpulses()){ + collidable.getImpulses().remove(impulse); + position.add(impulse.direction.mul(impulse.force)); + } + //state machine switch(state){ case STARTUP: @@ -140,46 +161,53 @@ public class MovementTree { velocity = maxNaturalVelocity; state = MovementTreeState.MOVE; } +// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector.x,0,movementVector.z).normalize().mul(velocity))); + EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f(movementVector.x,0,movementVector.z)); //move the entity - newPosition = new Vector3f(position).add(new Vector3f(movementVector).mul(velocity)).add(0,-9.8f,0); + newPosition = new Vector3f(position).add(new Vector3f(movementVector).mul(velocity)); //check/update if collision if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); } - //actually update - EntityUtils.getPosition(parent).set(newPosition); - EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), movementVector); - if(Globals.RUN_SERVER){ - Globals.server.broadcastMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - System.currentTimeMillis(), - newPosition.x, - newPosition.y, - newPosition.z, - movementVector.x, - movementVector.y, - movementVector.z, - velocity, - 0 - ) - ); - } else if(Globals.RUN_CLIENT && parent.getId() == Globals.clientCharacterID){ - Globals.clientConnection.queueOutgoingMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - System.currentTimeMillis(), - newPosition.x, - newPosition.y, - newPosition.z, - movementVector.x, - movementVector.y, - movementVector.z, - velocity, - 0 - ) - ); - } +// //actually update + position.set(newPosition); + rotation.rotationTo(new Vector3f(0,0,1), movementVector); + + bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(newPosition),1.0f); + body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix)); + + +// if(Globals.RUN_SERVER){ +// Globals.server.broadcastMessage( +// EntityMessage.constructmoveUpdateMessage( +// parent.getId(), +// System.currentTimeMillis(), +// newPosition.x, +// newPosition.y, +// newPosition.z, +// movementVector.x, +// movementVector.y, +// movementVector.z, +// velocity, +// 0 +// ) +// ); +// } else if(Globals.RUN_CLIENT && parent.getId() == Globals.clientCharacterID){ +// Globals.clientConnection.queueOutgoingMessage( +// EntityMessage.constructmoveUpdateMessage( +// parent.getId(), +// System.currentTimeMillis(), +// newPosition.x, +// newPosition.y, +// newPosition.z, +// movementVector.x, +// movementVector.y, +// movementVector.z, +// velocity, +// 0 +// ) +// ); +// } break; case MOVE: //check if can restart animation @@ -190,45 +218,52 @@ public class MovementTree { entityActor.incrementAnimationTime(0.01); } } + Vector3f force = new Vector3f(movementVector).normalize().mul(velocity); +// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(force)); + EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f(movementVector.x,0,movementVector.z)); //check if can move forward (collision engine) //if can, move forward by entity movement stats - newPosition = new Vector3f(position).add(new Vector3f(movementVector).mul(velocity)).add(0,-9.8f,0); + newPosition = new Vector3f(position).add(new Vector3f(movementVector).mul(velocity)); if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); } - EntityUtils.getPosition(parent).set(newPosition); - EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), movementVector); - if(Globals.RUN_SERVER){ - Globals.server.broadcastMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - System.currentTimeMillis(), - newPosition.x, - newPosition.y, - newPosition.z, - movementVector.x, - movementVector.y, - movementVector.z, - velocity, - 1 - ) - ); - } else if(Globals.RUN_CLIENT && parent.getId() == Globals.clientCharacterID){ - Globals.clientConnection.queueOutgoingMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - System.currentTimeMillis(), - newPosition.x, - newPosition.y, - newPosition.z, - movementVector.x, - movementVector.y, - movementVector.z, - velocity, - 1 - ) - ); - } + position.set(newPosition); + rotation.rotationTo(new Vector3f(0,0,1), movementVector); + + bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(newPosition),1.0f); + body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix)); + +// if(Globals.RUN_SERVER){ +// Globals.server.broadcastMessage( +// EntityMessage.constructmoveUpdateMessage( +// parent.getId(), +// System.currentTimeMillis(), +// newPosition.x, +// newPosition.y, +// newPosition.z, +// movementVector.x, +// movementVector.y, +// movementVector.z, +// velocity, +// 1 +// ) +// ); +// } else if(Globals.RUN_CLIENT && parent.getId() == Globals.clientCharacterID){ +// Globals.clientConnection.queueOutgoingMessage( +// EntityMessage.constructmoveUpdateMessage( +// parent.getId(), +// System.currentTimeMillis(), +// newPosition.x, +// newPosition.y, +// newPosition.z, +// movementVector.x, +// movementVector.y, +// movementVector.z, +// velocity, +// 1 +// ) +// ); +// } break; case SLOWDOWN: //run slowdown code @@ -245,55 +280,65 @@ public class MovementTree { velocity = 0; state = MovementTreeState.IDLE; } +// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector).mul(-1.0f).normalize().mul(velocity))); + EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f(movementVector.x,0,movementVector.z)); //move the entity - newPosition = new Vector3f(position).add(new Vector3f(movementVector).mul(velocity)).add(0,-9.8f,0); + newPosition = new Vector3f(position).add(new Vector3f(movementVector).mul(velocity)); if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); } - EntityUtils.getPosition(parent).set(newPosition); - EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), movementVector); - if(Globals.RUN_SERVER){ - Globals.server.broadcastMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - System.currentTimeMillis(), - newPosition.x, - newPosition.y, - newPosition.z, - movementVector.x, - movementVector.y, - movementVector.z, - velocity, - 2 - ) - ); - } else if(Globals.RUN_CLIENT && parent.getId() == Globals.clientCharacterID){ - Globals.clientConnection.queueOutgoingMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - System.currentTimeMillis(), - newPosition.x, - newPosition.y, - newPosition.z, - movementVector.x, - movementVector.y, - movementVector.z, - velocity, - 2 - ) - ); - } + position.set(newPosition); + rotation.rotationTo(new Vector3f(0,0,1), movementVector); + + bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(newPosition),1.0f); + body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix)); + +// if(Globals.RUN_SERVER){ +// Globals.server.broadcastMessage( +// EntityMessage.constructmoveUpdateMessage( +// parent.getId(), +// System.currentTimeMillis(), +// newPosition.x, +// newPosition.y, +// newPosition.z, +// movementVector.x, +// movementVector.y, +// movementVector.z, +// velocity, +// 2 +// ) +// ); +// } else if(Globals.RUN_CLIENT && parent.getId() == Globals.clientCharacterID){ +// Globals.clientConnection.queueOutgoingMessage( +// EntityMessage.constructmoveUpdateMessage( +// parent.getId(), +// System.currentTimeMillis(), +// newPosition.x, +// newPosition.y, +// newPosition.z, +// movementVector.x, +// movementVector.y, +// movementVector.z, +// velocity, +// 2 +// ) +// ); +// } break; case IDLE: +// body.clearForces(); // if(entityActor != null){ // if(!entityActor.isPlayingAnimation() || !entityActor.getCurrentAnimation().equals(Animation.ANIMATION_IDLE_1)){ // entityActor.playAnimation(Animation.ANIMATION_IDLE_1); // entityActor.incrementAnimationTime(0.01); // } // } - if(Globals.collisionEngine.gravityCheck(Globals.commonWorldData, parent)){ - EntityUtils.getPosition(parent).set(Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData,parent,new Vector3f(position.x,position.y - 9.8f,position.z))); - } +// if(Globals.collisionEngine.gravityCheck(Globals.commonWorldData, parent)){ +// position.set(Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData,parent,new Vector3f(position.x,position.y - 9.8f,position.z))); +// } + position.set(new Vector3f(position.x,position.y - 0.1f,position.z)); + bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(position),1.0f); + body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix)); break; } } diff --git a/src/main/java/electrosphere/entity/types/collision/CollisionObjUtils.java b/src/main/java/electrosphere/entity/types/collision/CollisionObjUtils.java new file mode 100644 index 00000000..f8c82a2a --- /dev/null +++ b/src/main/java/electrosphere/entity/types/collision/CollisionObjUtils.java @@ -0,0 +1,68 @@ +package electrosphere.entity.types.collision; + +import com.bulletphysics.collision.dispatch.CollisionObject; +import com.bulletphysics.collision.shapes.CylinderShape; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.game.collision.PhysicsUtils; +import electrosphere.game.collision.collidable.Collidable; +import electrosphere.main.Globals; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +/** + * + * @author amaterasu + */ +public class CollisionObjUtils { + + public static Entity spawnCollisionPlane(Vector3f scale, Vector3f position, Quaternionf rotation){ + Entity rVal = new Entity(); + + CollisionObject planeObject = PhysicsUtils.getPlaneObject(scale); + javax.vecmath.Matrix4f planeTransform = new javax.vecmath.Matrix4f( + PhysicsUtils.jomlToVecmathQuaternionf(rotation), + PhysicsUtils.jomlToVecmathVector3f(position), + 1.0f); + planeObject.setWorldTransform(new com.bulletphysics.linearmath.Transform(planeTransform)); + Collidable collidable = new Collidable(rVal, Collidable.TYPE_STRUCTURE); + Globals.collisionEngine.registerCollisionObject(planeObject, collidable); + Globals.collisionEngine.registerStructurePhysicsEntity(rVal); + + rVal.putData(EntityDataStrings.COLLISION_ENTITY_TYPE_PLANE, true); + rVal.putData(EntityDataStrings.DATA_STRING_POSITION, position); + rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, rotation); + rVal.putData(EntityDataStrings.DATA_STRING_SCALE, scale); + rVal.putData(EntityDataStrings.COLLISION_ENTITY_COLLISION_OBJECT, planeObject); + rVal.putData(EntityDataStrings.COLLISION_ENTITY_COLLIDABLE, collidable); + + Globals.entityManager.registerEntity(rVal); + return rVal; + } + + + public static Entity spawnCollisionCube(Vector3f scale, Vector3f position, Quaternionf rotation){ + Entity rVal = new Entity(); + + CollisionObject cubeObject = PhysicsUtils.getCubeObject(scale); + javax.vecmath.Matrix4f planeTransform = new javax.vecmath.Matrix4f( + PhysicsUtils.jomlToVecmathQuaternionf(rotation), + PhysicsUtils.jomlToVecmathVector3f(position), + 1.0f); + cubeObject.setWorldTransform(new com.bulletphysics.linearmath.Transform(planeTransform)); + Collidable collidable = new Collidable(rVal, Collidable.TYPE_STRUCTURE); + Globals.collisionEngine.registerCollisionObject(cubeObject, collidable); + Globals.collisionEngine.registerStructurePhysicsEntity(rVal); + + rVal.putData(EntityDataStrings.COLLISION_ENTITY_TYPE_CUBE, true); + rVal.putData(EntityDataStrings.DATA_STRING_POSITION, position); + rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, rotation); + rVal.putData(EntityDataStrings.DATA_STRING_SCALE, scale); + rVal.putData(EntityDataStrings.COLLISION_ENTITY_COLLISION_OBJECT, cubeObject); + rVal.putData(EntityDataStrings.COLLISION_ENTITY_COLLIDABLE, collidable); + + Globals.entityManager.registerEntity(rVal); + return rVal; + } + +} diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java index 4b8eadaf..7440ac0a 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java @@ -1,9 +1,11 @@ package electrosphere.entity.types.creature; +import com.bulletphysics.collision.dispatch.CollisionObject; +import com.bulletphysics.dynamics.RigidBody; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; -import electrosphere.entity.state.MovementTree; +import electrosphere.entity.state.movement.MovementTree; import electrosphere.entity.types.hitbox.HitboxData; import electrosphere.entity.types.hitbox.HitboxUtils; import electrosphere.game.server.creature.type.CreatureType; @@ -11,7 +13,10 @@ import electrosphere.game.server.creature.type.MovementSystem; import electrosphere.entity.state.AttackTree; import electrosphere.entity.state.IdleTree; import electrosphere.entity.types.life.LifeState; +import electrosphere.game.collision.PhysicsUtils; +import electrosphere.game.collision.collidable.Collidable; import electrosphere.game.server.creature.type.AttackMove; +import electrosphere.game.server.creature.type.PhysicsObject; import electrosphere.main.Globals; import electrosphere.main.Main; import electrosphere.net.parser.net.message.EntityMessage; @@ -69,6 +74,24 @@ public class CreatureUtils { break; } } + if(rawType.getPhysicsObject() != null){ + PhysicsObject physicsTemplate = rawType.getPhysicsObject(); + switch(physicsTemplate.getType()){ + case "CYLINDER": + CollisionObject rigidBody = PhysicsUtils.getCylinderBody(1f, new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3())); + Collidable collidable = new Collidable(rVal, Collidable.TYPE_CREATURE); + rVal.putData(EntityDataStrings.PHYSICS_RIGID_BODY, rigidBody); + rVal.putData(EntityDataStrings.PHYSICS_RIGID_BODY_OFFSET, new Vector3f(physicsTemplate.getOffsetX(),physicsTemplate.getOffsetY(),physicsTemplate.getOffsetZ())); + rVal.putData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE, physicsTemplate); + Globals.collisionEngine.registerPhysicsEntity(rVal); + Globals.collisionEngine.registerDynamicPhysicsEntity(rVal); + Globals.collisionEngine.registerCollisionObject(rigidBody, collidable); + if(rVal.getDataKeys().contains(EntityDataStrings.DATA_STRING_MOVEMENT_BT)){ + CreatureUtils.getEntityMovementTree(rVal).setCollisionObject(rigidBody, collidable); + } + break; + } + } for(String token : rawType.getTokens()){ switch(token){ case "BLENDER_TRANSFORM": @@ -177,4 +200,15 @@ public class CreatureUtils { public static IdleTree getIdleTree(Entity e){ return (IdleTree)e.getData(EntityDataStrings.IDLE_TREE); } + + public static CollisionObject getCreatureRigidBody(Entity e){ + return (CollisionObject)e.getData(EntityDataStrings.PHYSICS_RIGID_BODY); + } + + public static void positionCharacter(Entity e, Vector3f position){ + EntityUtils.getPosition(e).set(position); + Quaternionf rotation = EntityUtils.getRotation(e); + CollisionObject body = getCreatureRigidBody(e); + PhysicsUtils.setRigidBodyTransform(position, rotation, body); + } } diff --git a/src/main/java/electrosphere/entity/types/item/ItemUtils.java b/src/main/java/electrosphere/entity/types/item/ItemUtils.java index 9008136d..d6fb7e1b 100644 --- a/src/main/java/electrosphere/entity/types/item/ItemUtils.java +++ b/src/main/java/electrosphere/entity/types/item/ItemUtils.java @@ -3,7 +3,7 @@ package electrosphere.entity.types.item; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; -import electrosphere.entity.state.MovementTree; +import electrosphere.entity.state.movement.MovementTree; import electrosphere.entity.types.hitbox.HitboxData; import electrosphere.entity.types.hitbox.HitboxUtils; import electrosphere.game.server.creature.type.CreatureType; diff --git a/src/main/java/electrosphere/entity/types/structure/StructureUtils.java b/src/main/java/electrosphere/entity/types/structure/StructureUtils.java new file mode 100644 index 00000000..04c63f1f --- /dev/null +++ b/src/main/java/electrosphere/entity/types/structure/StructureUtils.java @@ -0,0 +1,46 @@ +package electrosphere.entity.types.structure; + +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.collision.CollisionObjUtils; +import electrosphere.game.collision.collidable.Collidable; +import electrosphere.game.server.structure.model.CollisionObjectTemplate; +import electrosphere.game.server.structure.model.StructureType; +import electrosphere.main.Globals; +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import org.joml.Vector4f; + +/** + * + * @author amaterasu + */ +public class StructureUtils { + + + public static Entity spawnBasicStructure(String type, Vector3f position, Quaternionf rotation){ + StructureType rawType = Globals.structureTypeMap.getType(type); + Entity rVal = EntityUtils.spawnDrawableEntity(rawType.getModelPath()); + EntityUtils.getPosition(rVal).set(position); + EntityUtils.getRotation(rVal).set(rotation); + for(CollisionObjectTemplate template : rawType.getCollision()){ + switch(template.getType()){ + case CollisionObjectTemplate.TYPE_CUBE: + Matrix4f rotationTransform = new Matrix4f().rotate(rotation); + Vector4f rotatedPosition = rotationTransform.transform(new Vector4f(template.getPositionX(),template.getPositionY(),template.getPositionZ(),1.0f)); + Vector3f cubePosition = new Vector3f(position).add(rotatedPosition.x,rotatedPosition.y,rotatedPosition.z); + Quaternionf cubeRotation = new Quaternionf(rotation).mul(new Quaternionf(template.getRotationX(),template.getRotationY(),template.getRotationZ(),template.getRotationW())).normalize(); + CollisionObjUtils.spawnCollisionCube(new Vector3f(template.getScaleX(),template.getScaleY(),template.getScaleZ()), cubePosition, cubeRotation); +// Collidable collidable = new Collidable(rVal, Collidable.TYPE_STRUCTURE); +// Globals.collisionEngine.registerPhysicsEntity(rVal); +// Globals.collisionEngine.registerCollisionObject(rigidBody, collidable); + break; + case CollisionObjectTemplate.TYPE_PLANE: + throw new UnsupportedOperationException("Haven't implemented plane collision on structures"); + } + } + return rVal; + } +} diff --git a/src/main/java/electrosphere/game/client/drawcell/DrawCell.java b/src/main/java/electrosphere/game/client/drawcell/DrawCell.java index 492d3638..096f412a 100644 --- a/src/main/java/electrosphere/game/client/drawcell/DrawCell.java +++ b/src/main/java/electrosphere/game/client/drawcell/DrawCell.java @@ -2,6 +2,7 @@ package electrosphere.game.client.drawcell; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; +import electrosphere.game.collision.PhysicsUtils; import electrosphere.game.terrain.processing.TerrainInterpolator; import electrosphere.logger.LoggerInterface; import electrosphere.main.Globals; @@ -30,6 +31,7 @@ public class DrawCell { + DrawCell(){ } @@ -80,6 +82,8 @@ public class DrawCell { modelEntity = EntityUtils.spawnDrawableEntity(terrainModelPath); LoggerInterface.loggerRenderer.INFO("New cell @ " + cellX * dynamicInterpolationRatio + "," + cellY * dynamicInterpolationRatio); EntityUtils.getPosition(modelEntity).set(new Vector3f(cellX * dynamicInterpolationRatio, 0.0f, cellY * dynamicInterpolationRatio)); + PhysicsUtils.attachTerrainRigidBody(modelEntity,heightmap); + Globals.collisionEngine.registerPhysicsEntity(modelEntity); } public void retireCell(){ diff --git a/src/main/java/electrosphere/game/client/drawcell/DrawCellManager.java b/src/main/java/electrosphere/game/client/drawcell/DrawCellManager.java index 9ecaef19..6b19ed93 100644 --- a/src/main/java/electrosphere/game/client/drawcell/DrawCellManager.java +++ b/src/main/java/electrosphere/game/client/drawcell/DrawCellManager.java @@ -45,6 +45,8 @@ public class DrawCellManager { int drawStepdownInterval = 3; int drawStepdownValue = 5; + int worldBoundDiscreteMin = 0; + int worldBoundDiscreteMax = 0; //metadata about the game world ClientWorldData clientWorldData; @@ -60,6 +62,7 @@ public class DrawCellManager { public DrawCellManager(ClientWorldData clientWorldData, ClientTerrainManager clientTerrainManager, int discreteX, int discreteY){ this.clientWorldData = clientWorldData; + worldBoundDiscreteMax = (int)(this.clientWorldData.getWorldBoundMax().x / this.clientWorldData.getDynamicInterpolationRatio() * 1.0f); this.clientTerrainManager = clientTerrainManager; this.miniCellWidth = miniCellWidth; cells = new DrawCell[drawRadius * 2 + 1][drawRadius * 2 + 1]; @@ -280,7 +283,9 @@ public class DrawCellManager { public void shiftChunksNegX(){ for(int y = 0; y < drawRadius * 2 + 1; y++){ - cells[drawRadius * 2][y].retireCell(); + if(cells[drawRadius * 2][y] != null){ + cells[drawRadius * 2][y].retireCell(); + } } for(int x = drawRadius * 2; x > 0; x--){ for(int y = 0; y < drawRadius * 2 + 1; y++){ @@ -297,7 +302,9 @@ public class DrawCellManager { public void shiftChunksPosX(){ for(int y = 0; y < drawRadius * 2 + 1; y++){ - cells[0][y].retireCell(); + if(cells[0][y] != null){ + cells[0][y].retireCell(); + } } for(int x = 0; x < drawRadius * 2; x++){ for(int y = 0; y < drawRadius * 2 + 1; y++){ @@ -314,7 +321,9 @@ public class DrawCellManager { public void shiftChunksNegY(){ for(int x = 0; x < drawRadius * 2 + 1; x++){ - cells[x][drawRadius * 2].retireCell(); + if(cells[x][drawRadius * 2] != null){ + cells[x][drawRadius * 2].retireCell(); + } } for(int x = 0; x < drawRadius * 2 + 1; x++){ for(int y = drawRadius * 2; y > 0; y--){ @@ -331,7 +340,9 @@ public class DrawCellManager { public void shiftChunksPosY(){ for(int x = 0; x < drawRadius * 2 + 1; x++){ - cells[x][0].retireCell(); + if(cells[x][0] != null){ + cells[x][0].retireCell(); + } } for(int x = 0; x < drawRadius * 2 + 1; x++){ for(int y = 0; y < drawRadius * 2; y++){ diff --git a/src/main/java/electrosphere/game/collision/CollisionEngine.java b/src/main/java/electrosphere/game/collision/CollisionEngine.java index df09f16d..7b8a46cd 100644 --- a/src/main/java/electrosphere/game/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/game/collision/CollisionEngine.java @@ -2,12 +2,17 @@ package electrosphere.game.collision; import com.bulletphysics.collision.broadphase.BroadphaseInterface; import com.bulletphysics.collision.broadphase.DbvtBroadphase; +import com.bulletphysics.collision.broadphase.Dispatcher; import com.bulletphysics.collision.dispatch.CollisionDispatcher; import com.bulletphysics.collision.dispatch.CollisionObject; import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration; +import com.bulletphysics.collision.narrowphase.ManifoldPoint; +import com.bulletphysics.collision.narrowphase.PersistentManifold; import com.bulletphysics.collision.shapes.BoxShape; import com.bulletphysics.collision.shapes.CollisionShape; import com.bulletphysics.dynamics.DiscreteDynamicsWorld; +import com.bulletphysics.dynamics.DynamicsWorld; +import com.bulletphysics.dynamics.InternalTickCallback; import com.bulletphysics.dynamics.RigidBody; import com.bulletphysics.dynamics.RigidBodyConstructionInfo; import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver; @@ -16,7 +21,9 @@ import com.bulletphysics.linearmath.Transform; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; +import electrosphere.entity.state.movement.Impulse; import electrosphere.entity.types.hitbox.HitboxData; +import electrosphere.game.collision.collidable.Collidable; import electrosphere.game.server.world.ServerWorldData; import java.util.ArrayList; import java.util.List; @@ -28,27 +35,66 @@ import org.joml.Vector3f; */ public class CollisionEngine { + DiscreteDynamicsWorld world; + SequentialImpulseConstraintSolver solver; BroadphaseInterface broadphase; DefaultCollisionConfiguration collisionConfiguration; CollisionDispatcher dispatcher; List collisionEntities = new ArrayList(); + List physicsEntities = new ArrayList(); + List dynamicPhysicsEntities = new ArrayList(); + List structurePhysicsEntities = new ArrayList(); + List collisionObject = new ArrayList(); public CollisionEngine(){ broadphase = new DbvtBroadphase(); collisionConfiguration = new DefaultCollisionConfiguration(); dispatcher = new CollisionDispatcher(collisionConfiguration); - SequentialImpulseConstraintSolver solver = new SequentialImpulseConstraintSolver(); - DiscreteDynamicsWorld world = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); + solver = new SequentialImpulseConstraintSolver(); + world = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); - CollisionShape boxShape = new BoxShape(new javax.vecmath.Vector3f(1,1,1)); - float mass = 1.0f; - DefaultMotionState fallMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,0,0,1),new javax.vecmath.Vector3f(0,50,0),1.0f))); - RigidBodyConstructionInfo boxShapeRigidBodyCI = new RigidBodyConstructionInfo(mass, fallMotionState, boxShape); - RigidBody boxRigidBody = new RigidBody(boxShapeRigidBodyCI); +// CollisionShape boxShape = new BoxShape(new javax.vecmath.Vector3f(1,1,1)); +// float mass = 1.0f; +// DefaultMotionState fallMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,0,0,1),new javax.vecmath.Vector3f(0,50,0),1.0f))); +// RigidBodyConstructionInfo boxShapeRigidBodyCI = new RigidBodyConstructionInfo(mass, fallMotionState, boxShape); +// RigidBody boxRigidBody = new RigidBody(boxShapeRigidBodyCI); +// +// world.addRigidBody(boxRigidBody); - - world.addRigidBody(boxRigidBody); + world.setInternalTickCallback(new InternalTickCallback(){ + @Override + public void internalTick(DynamicsWorld dw, float f) { + Dispatcher dispatcher = dw.getDispatcher(); + int manifoldCount = dispatcher.getNumManifolds(); + for (int i = 0; i < manifoldCount; i++) { + PersistentManifold manifold = dispatcher.getManifoldByIndexInternal(i); + // The following two lines are optional. + CollisionObject object1 = (CollisionObject)manifold.getBody0(); + CollisionObject object2 = (CollisionObject)manifold.getBody1(); + Collidable physicsObject1 = (Collidable)object1.getUserPointer(); + Collidable physicsObject2 = (Collidable)object2.getUserPointer(); + boolean hit = false; + Vector3f normal = null; + float magnitude = 0.0f; + for (int j = 0; j < manifold.getNumContacts(); j++) { + ManifoldPoint contactPoint = manifold.getContactPoint(j); + if (contactPoint.getDistance() < 0.0f) { + magnitude = contactPoint.getDistance(); + hit = true; + normal = new Vector3f(contactPoint.normalWorldOnB.x,contactPoint.normalWorldOnB.y,contactPoint.normalWorldOnB.z); + break; + } + } + if (hit) { + resolveCollision(physicsObject1,physicsObject2, normal, magnitude); + resolveCollision(physicsObject2,physicsObject1, new Vector3f(normal).mul(-1.0f), magnitude); +// System.out.println("HIT + " + normal); + // Collision happened between physicsObject1 and physicsObject2. Collision normal is in variable 'normal'. + } + } + } + }, world); //https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=11507 //https://www.google.com/search?client=firefox-b-1-d&q=bullet+set+position+and+check+if+collision @@ -56,6 +102,26 @@ public class CollisionEngine { } + public static void resolveCollision(Collidable impactor, Collidable receiver, Vector3f normal, float magnitude){ + switch(receiver.getType()){ + case Collidable.TYPE_CREATURE: + switch(impactor.getType()){ + case Collidable.TYPE_TERRAIN: +// System.out.println("Terrain-creature collision: " + normal + " mag:" + realMagnitude); + receiver.addImpulse(new Impulse(new Vector3f(0,1.0f,0), -1.0f * magnitude)); + break; + case Collidable.TYPE_CREATURE: + receiver.addImpulse(new Impulse(normal,magnitude)); + break; + case Collidable.TYPE_STRUCTURE: + receiver.addImpulse(new Impulse(normal, magnitude)); + System.out.println("Structure-creature collision"); + break; + } + break; + } + } + /** * @@ -76,15 +142,19 @@ public class CollisionEngine { ){ return false; } - // - // are we below the terrain? - // - if(w.getElevationAtPoint(positionToCheck) > positionToCheck.y){ - return false; - } +// // +// // are we below the terrain? +// // +// if(w.getElevationAtPoint(positionToCheck) > positionToCheck.y){ +// return false; +// } return rVal; } + public void simulatePhysics(float time){ + world.stepSimulation(time); + } + /** * * @param e the entity that's trying to move @@ -143,6 +213,54 @@ public class CollisionEngine { } } + public void registerPhysicsEntity(Entity physicsEntity){ + physicsEntities.add(physicsEntity); + } + + public List getPhysicsEntities(){ + return physicsEntities; + } + + public void registerDynamicPhysicsEntity(Entity dynamicEntity){ + dynamicPhysicsEntities.add(dynamicEntity); + } + + public List getDynamicPhysicsEntities(){ + return dynamicPhysicsEntities; + } + + public void registerStructurePhysicsEntity(Entity structureEntity){ + structurePhysicsEntities.add(structureEntity); + } + + public List getStructurePhysicsEntities(){ + return structurePhysicsEntities; + } + + public void updateDynamicObjectTransforms(){ + for(Entity dynamicEntity : dynamicPhysicsEntities){ + CollisionObject rigidBody = (CollisionObject)dynamicEntity.getData(EntityDataStrings.PHYSICS_RIGID_BODY); + Vector3f offset = (Vector3f)dynamicEntity.getData(EntityDataStrings.PHYSICS_RIGID_BODY_OFFSET); + Vector3f newPosition = PhysicsUtils.getRigidBodyPosition(rigidBody).add(offset); +// System.out.println(rigidBody + " position " + newPosition); +// System.out.println("Linear velocity: " + rigidBody.getLinearVelocity(new javax.vecmath.Vector3f())); + EntityUtils.getPosition(dynamicEntity).set(newPosition); + } + } + + public void registerCollisionObject(CollisionObject object, Collidable collidable){ + world.addCollisionObject(object); + object.setUserPointer(collidable); + collisionObject.add(object); + } + + public void listBodyPositions(){ + for(CollisionObject body : collisionObject){ + System.out.println(body); + System.out.println(PhysicsUtils.getRigidBodyPosition(body)); + } + } + /* Check if the entity is being accelerated by gravity */ diff --git a/src/main/java/electrosphere/game/collision/PhysicsUtils.java b/src/main/java/electrosphere/game/collision/PhysicsUtils.java new file mode 100644 index 00000000..647077f3 --- /dev/null +++ b/src/main/java/electrosphere/game/collision/PhysicsUtils.java @@ -0,0 +1,361 @@ +package electrosphere.game.collision; + +import com.bulletphysics.collision.dispatch.CollisionObject; +import com.bulletphysics.collision.shapes.BoxShape; +import com.bulletphysics.collision.shapes.BvhTriangleMeshShape; +import com.bulletphysics.collision.shapes.CylinderShape; +import com.bulletphysics.collision.shapes.IndexedMesh; +import com.bulletphysics.collision.shapes.TriangleIndexVertexArray; +import com.bulletphysics.dynamics.RigidBody; +import com.bulletphysics.dynamics.RigidBodyConstructionInfo; +import com.bulletphysics.linearmath.DefaultMotionState; +import com.bulletphysics.linearmath.Transform; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; +import electrosphere.game.collision.collidable.Collidable; +import electrosphere.main.Globals; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +/** + * + * @author amaterasu + */ +public class PhysicsUtils { + + /** + * Constructor for rigid body: + * http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/CylinderShape.html + * Creates a sphere where the dimensions are twice the size of the vector passed in + * ie diameter = x * 2 or z * 2 + * @param entity + * @param halfDimensions + */ + public static void attachCylinderRigidBody(Entity entity, Vector3f halfDimensions){ + new com.bulletphysics.collision.shapes.CylinderShape(new javax.vecmath.Vector3f(halfDimensions.x,halfDimensions.y,halfDimensions.z)); + } + + + public static RigidBody attachTerrainRigidBody(Entity terrain, float[][] heightfield){ + + Vector3f position = EntityUtils.getPosition(terrain); + + int arrayLength = heightfield.length; + int arrayWidth = heightfield[0].length; + + /* + Traditional buffer code not working for some reason + the approach of + https://stackoverflow.com/questions/40855945/lwjgl-mesh-to-jbullet-collider + works much better + IDK why + */ + + int numberTriangles = (arrayLength - 1) * (arrayWidth - 1) * 2; + int triangleStride = 0; + + int numberVertices = arrayLength * arrayWidth; + int vertexStride = 0; + + float[] vertices = new float[numberVertices * 3]; + int vertexInserterPos = 0; + int[] indices = new int[numberTriangles * 3]; + int indexInserterPos = 0; + + for(int x = 0; x < arrayLength; x++){ + for(int y = 0; y < arrayWidth; y++){ + vertices[vertexInserterPos] = x; + vertexInserterPos++; + vertices[vertexInserterPos] = heightfield[x][y] - 0.4f; + vertexInserterPos++; + vertices[vertexInserterPos] = y; + vertexInserterPos++; + if(x < arrayLength - 1 && y < arrayWidth - 1){ + //if we should also add a triangle index + /* + as copied from ModelUtil's terrain mesh generation function + faces.put((x / stride + 0) * actualHeight + (y / stride + 0)); + faces.put((x / stride + 0) * actualHeight + (y / stride + 1)); + faces.put((x / stride + 1) * actualHeight + (y / stride + 0)); + faces.put((x / stride + 1) * actualHeight + (y / stride + 0)); + faces.put((x / stride + 0) * actualHeight + (y / stride + 1)); + faces.put((x / stride + 1) * actualHeight + (y / stride + 1)); + */ + indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 0); + indexInserterPos++; + indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 1); + indexInserterPos++; + indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 0); + indexInserterPos++; + indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 0); + indexInserterPos++; + indices[indexInserterPos] = (x + 0) * arrayWidth + (y + 1); + indexInserterPos++; + indices[indexInserterPos] = (x + 1) * arrayWidth + (y + 1); + indexInserterPos++; + } + } + } + + + javax.vecmath.Vector3f aabbMin = new javax.vecmath.Vector3f(); + javax.vecmath.Vector3f aabbMax = new javax.vecmath.Vector3f(); + + + + + //http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/IndexedMesh.html + com.bulletphysics.collision.shapes.IndexedMesh indexedMesh = new com.bulletphysics.collision.shapes.IndexedMesh(); + + indexedMesh.numTriangles = indices.length / 3; + indexedMesh.triangleIndexBase = ByteBuffer.allocateDirect(indices.length*Float.BYTES).order(ByteOrder.nativeOrder()); + indexedMesh.triangleIndexBase.asIntBuffer().put(indices); + indexedMesh.triangleIndexStride = 3 * Float.BYTES; + + indexedMesh.numVertices = vertices.length / 3; + indexedMesh.vertexBase = ByteBuffer.allocateDirect(vertices.length*Float.BYTES).order(ByteOrder.nativeOrder()); + indexedMesh.vertexBase.asFloatBuffer().put(vertices); + indexedMesh.vertexStride = 3 * Float.BYTES; + + + + + + + //http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shpaes/TriangleIndexVertexArray.html + com.bulletphysics.collision.shapes.TriangleIndexVertexArray triangleIndexArray = new com.bulletphysics.collision.shapes.TriangleIndexVertexArray(); + triangleIndexArray.addIndexedMesh(indexedMesh); //this assumes the scalar type is integer (assumes bytebuffer is actually integer +// triangleIndexArray.calculateAabbBruteForce(aabbMin, aabbMax); + + + + //http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/BvhTriangleMeshShape.html + com.bulletphysics.collision.shapes.BvhTriangleMeshShape terrainShape = new com.bulletphysics.collision.shapes.BvhTriangleMeshShape( + triangleIndexArray, + true // "useQuantizedAabbCompression" -- apparently means better memory usage ( http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/BvhTriangleMeshShape.html ) + ); + +// terrainShape.localGetSupportingVertex(new javax.vecmath.Vector3f(1,0,0), aabbMin); +// terrainShape.recalcLocalAabb(); +// terrainShape.getLocalAabbMin(aabbMin); +// terrainShape.getLocalAabbMax(aabbMax); + + + DefaultMotionState defaultMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,0,0,1),new javax.vecmath.Vector3f(position.x,position.y,position.z),1.0f))); + RigidBodyConstructionInfo terrainRigidBodyCI = new RigidBodyConstructionInfo(0, defaultMotionState, terrainShape); + RigidBody terrainRigidBody = new RigidBody(terrainRigidBodyCI); + + terrainRigidBody.setFriction(1f); + + + Globals.collisionEngine.registerCollisionObject(terrainRigidBody, new Collidable(terrain,Collidable.TYPE_TERRAIN)); + +// terrainRigidBody.getAabb(aabbMin, aabbMax); + +// System.out.println("aabbMin: " + aabbMin + " aabbMax: " + aabbMax); + + + terrain.putData(EntityDataStrings.PHYSICS_RIGID_BODY, terrainRigidBody); + + return terrainRigidBody; + + } + + + public static void addTestPlaneRigidBody(){ + + /* + Traditional buffer code not working for some reason + the approach of + https://stackoverflow.com/questions/40855945/lwjgl-mesh-to-jbullet-collider + works much better + IDK why + */ + + int[] indices = { + 0, 1, 3, + 1, 2, 3 + }; + + float[] coords = { + 0, 0, 0, + 100, 0, 0, + 0, 0, 100, + 100, 0, 100 + }; + + + IndexedMesh indexedMesh = new IndexedMesh(); + indexedMesh.numTriangles = indices.length / 3; + indexedMesh.triangleIndexBase = ByteBuffer.allocateDirect(indices.length*Float.BYTES).order(ByteOrder.nativeOrder()); + indexedMesh.triangleIndexBase.asIntBuffer().put(indices); + indexedMesh.triangleIndexStride = 3 * Float.BYTES; + indexedMesh.numVertices = coords.length / 3; + indexedMesh.vertexBase = ByteBuffer.allocateDirect(coords.length*Float.BYTES).order(ByteOrder.nativeOrder()); + indexedMesh.vertexBase.asFloatBuffer().put(coords); + indexedMesh.vertexStride = 3 * Float.BYTES; + + + + javax.vecmath.Vector3f aabbMin = new javax.vecmath.Vector3f(); + javax.vecmath.Vector3f aabbMax = new javax.vecmath.Vector3f(); + + + //http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shpaes/TriangleIndexVertexArray.html + com.bulletphysics.collision.shapes.TriangleIndexVertexArray triangleIndexArray = new com.bulletphysics.collision.shapes.TriangleIndexVertexArray( +// numberTriangles, +// triangleData, +// triangleStride, +// numberVertices, +// vertexData, +// vertexStride + ); + triangleIndexArray.addIndexedMesh(indexedMesh); +// triangleIndexArray.setScaling(new javax.vecmath.Vector3f(1.0f,1.0f,1.0f)); + + + + //http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/BvhTriangleMeshShape.html + com.bulletphysics.collision.shapes.BvhTriangleMeshShape terrainShape = new com.bulletphysics.collision.shapes.BvhTriangleMeshShape( + triangleIndexArray, + true // "useQuantizedAabbCompression" -- apparently means better memory usage ( http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/BvhTriangleMeshShape.html ) + ); + + + DefaultMotionState defaultMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,0,0,1),new javax.vecmath.Vector3f(0,0,0),1.0f))); + RigidBodyConstructionInfo terrainRigidBodyCI = new RigidBodyConstructionInfo(0, defaultMotionState, terrainShape); + RigidBody terrainRigidBody = new RigidBody(terrainRigidBodyCI); + + + +// Globals.collisionEngine.registerCollisionObject(terrainRigidBody, new Collidable(terrain,Collidable.TYPE_TERRAIN)); + + terrainRigidBody.getAabb(aabbMin, aabbMax); + + } + + public static Vector3f getRigidBodyPosition(CollisionObject body){ + javax.vecmath.Vector3f transform = new javax.vecmath.Vector3f(0,0,0); + body.getWorldTransform(new com.bulletphysics.linearmath.Transform()).transform(transform); +// body.getMotionState().getWorldTransform(new com.bulletphysics.linearmath.Transform()).transform(transform); + return vecmathToJomlVector3f(transform); + } + + public static Quaternionf getRigidBodyRotation(RigidBody body){ + return vecmathtoJomlQuaternionf(body.getMotionState().getWorldTransform(new com.bulletphysics.linearmath.Transform()).getRotation(new javax.vecmath.Quat4f())); + } + + public static Vector3f vecmathToJomlVector3f(javax.vecmath.Vector3f vector){ + return new Vector3f(vector.x,vector.y,vector.z); + } + + public static Quaternionf vecmathtoJomlQuaternionf(javax.vecmath.Quat4f quaternion){ + return new Quaternionf(quaternion.x, quaternion.y, quaternion.z, quaternion.w); + } + + public static javax.vecmath.Vector3f jomlToVecmathVector3f(Vector3f vector){ + return new javax.vecmath.Vector3f(vector.x, vector.y, vector.z); + } + + public static javax.vecmath.Quat4f jomlToVecmathQuaternionf(Quaternionf quaternion){ + return new javax.vecmath.Quat4f(quaternion.x, quaternion.y, quaternion.z, quaternion.w); + } + + public static void setRigidBodyTransform(Vector3f position, Quaternionf rotation, CollisionObject body){ + com.bulletphysics.linearmath.Transform transform = new com.bulletphysics.linearmath.Transform(); + + javax.vecmath.Matrix4f transformMatrix = new javax.vecmath.Matrix4f(); + transformMatrix.setIdentity(); + + transformMatrix.setTranslation(new javax.vecmath.Vector3f(position.x, position.y, position.z)); + transformMatrix.setRotation(new javax.vecmath.Quat4f(rotation.x,rotation.y,rotation.z,rotation.w)); + transform.set(transformMatrix); + + + //https://docs.oracle.com/cd/E17802_01/j2se/javase/technologies/desktop/java3d/forDevelopers/j3dapi/javax/vecmath/Quat4f.html + //constructor is x,y,z,w +// transform.setRotation(new javax.vecmath.Quat4f(rotation.x,rotation.y,rotation.z,rotation.w)); + + body.setWorldTransform(transform); + } + +// public static RigidBody getUnitCylinderRigidBody(float mass){ +// CylinderShape cylinderShape = new CylinderShape(jomlToVecmathVector3f(new Vector3f(1.0f,1.0f,1.0f))); +// DefaultMotionState defaultMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,1,0,1),new javax.vecmath.Vector3f(0,0,0),1.0f))); +// RigidBodyConstructionInfo cylinderRigidBodyCI = new RigidBodyConstructionInfo(mass, defaultMotionState, cylinderShape); +// RigidBody cylinderRigidBody = new RigidBody(cylinderRigidBodyCI); +//// cylinderRigidBody.setMassProps(mass, PhysicsUtils.jomlToVecmathVector3f(new Vector3f(1.0f,1.0f,1.0f))); +// cylinderRigidBody.clearForces(); +// return cylinderRigidBody; +// } + + public static CollisionObject getCylinderBody(float mass, Vector3f dimensions){ + CylinderShape cylinderShape = new CylinderShape(jomlToVecmathVector3f(dimensions)); +// DefaultMotionState defaultMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,1,0,1),new javax.vecmath.Vector3f(0,0,0),1.0f))); +// RigidBodyConstructionInfo cylinderRigidBodyCI = new RigidBodyConstructionInfo(mass, defaultMotionState, cylinderShape); +// RigidBody cylinderRigidBody = new RigidBody(cylinderRigidBodyCI); + CollisionObject cylinderCollisionObject = new CollisionObject(); + cylinderCollisionObject.setCollisionShape(cylinderShape); +// cylinderRigidBody.setAngularFactor(1.0f); +// cylinderRigidBody.setFriction(0.8f); + return cylinderCollisionObject; + } + + public static CollisionObject getPlaneObject(Vector3f dimensions){ + int[] indices = { + 0, 1, 3, + 1, 2, 3 + }; + + float[] coords = { + -1 * dimensions.x, 0, -1 * dimensions.z, + 1 * dimensions.x, 0, -1 * dimensions.z, + -1 * dimensions.x, 0, 1 * dimensions.z, + 1 * dimensions.x, 0, 1 * dimensions.z + }; + + IndexedMesh indexedMesh = new IndexedMesh(); + indexedMesh.numTriangles = indices.length / 3; + indexedMesh.triangleIndexBase = ByteBuffer.allocateDirect(indices.length*Float.BYTES).order(ByteOrder.nativeOrder()); + indexedMesh.triangleIndexBase.asIntBuffer().put(indices); + indexedMesh.triangleIndexStride = 3 * Float.BYTES; + indexedMesh.numVertices = coords.length / 3; + indexedMesh.vertexBase = ByteBuffer.allocateDirect(coords.length*Float.BYTES).order(ByteOrder.nativeOrder()); + indexedMesh.vertexBase.asFloatBuffer().put(coords); + indexedMesh.vertexStride = 3 * Float.BYTES; + + + + //http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shpaes/TriangleIndexVertexArray.html + TriangleIndexVertexArray triangleIndexArray = new TriangleIndexVertexArray(); + triangleIndexArray.addIndexedMesh(indexedMesh); +// triangleIndexArray.setScaling(new javax.vecmath.Vector3f(1.0f,1.0f,1.0f)); + + + + //http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/BvhTriangleMeshShape.html + BvhTriangleMeshShape planeShape = new BvhTriangleMeshShape( + triangleIndexArray, + true // "useQuantizedAabbCompression" -- apparently means better memory usage ( http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/BvhTriangleMeshShape.html ) + ); + + + CollisionObject planeCollisionObject = new CollisionObject(); + planeCollisionObject.setCollisionShape(planeShape); + + return planeCollisionObject; + } + + + public static CollisionObject getCubeObject(Vector3f dimensions){ + BoxShape boxShape = new BoxShape(jomlToVecmathVector3f(dimensions)); + CollisionObject boxCollisionObject = new CollisionObject(); + boxCollisionObject.setCollisionShape(boxShape); + return boxCollisionObject; + } +} diff --git a/src/main/java/electrosphere/game/collision/collidable/Collidable.java b/src/main/java/electrosphere/game/collision/collidable/Collidable.java index 93afa638..2e47b517 100644 --- a/src/main/java/electrosphere/game/collision/collidable/Collidable.java +++ b/src/main/java/electrosphere/game/collision/collidable/Collidable.java @@ -1,5 +1,14 @@ package electrosphere.game.collision.collidable; +import com.bulletphysics.collision.shapes.CollisionShape; +import com.bulletphysics.dynamics.RigidBody; +import electrosphere.entity.Entity; +import electrosphere.entity.state.movement.Impulse; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.joml.Vector3f; + /** * @@ -7,6 +16,37 @@ package electrosphere.game.collision.collidable; */ public class Collidable { + Entity parent; + String type; + + List impulses = new CopyOnWriteArrayList(); + + public static final String TYPE_TERRAIN = "terrain"; + public static final String TYPE_CREATURE = "creature"; + public static final String TYPE_STRUCTURE = "structure"; + + + public Collidable(Entity parent, String type){ + this.parent = parent; + this.type = type; + } + + public List getImpulses() { + return impulses; + } + + public void addImpulse(Impulse impulse) { + impulses.add(impulse); + } + + public Entity getParent() { + return parent; + } + + public String getType() { + return type; + } + } diff --git a/src/main/java/electrosphere/game/server/ai/creature/MindlessAttacker.java b/src/main/java/electrosphere/game/server/ai/creature/MindlessAttacker.java index d605bdab..9b223236 100644 --- a/src/main/java/electrosphere/game/server/ai/creature/MindlessAttacker.java +++ b/src/main/java/electrosphere/game/server/ai/creature/MindlessAttacker.java @@ -4,7 +4,7 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.AttackTree; -import electrosphere.entity.state.MovementTree; +import electrosphere.entity.state.movement.MovementTree; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.game.server.ai.AI; import electrosphere.main.Globals; diff --git a/src/main/java/electrosphere/game/server/creature/type/CreatureType.java b/src/main/java/electrosphere/game/server/creature/type/CreatureType.java index a6471b77..5a67cc9c 100644 --- a/src/main/java/electrosphere/game/server/creature/type/CreatureType.java +++ b/src/main/java/electrosphere/game/server/creature/type/CreatureType.java @@ -9,6 +9,7 @@ public class CreatureType { List hitboxes; List tokens; List movementSystems; + PhysicsObject physicsObject; List attackMoves; HealthSystem healthSystem; String modelPath; @@ -44,6 +45,10 @@ public class CreatureType { public HealthSystem getHealthSystem() { return healthSystem; } + + public PhysicsObject getPhysicsObject() { + return physicsObject; + } diff --git a/src/main/java/electrosphere/game/server/creature/type/PhysicsObject.java b/src/main/java/electrosphere/game/server/creature/type/PhysicsObject.java new file mode 100644 index 00000000..6b459593 --- /dev/null +++ b/src/main/java/electrosphere/game/server/creature/type/PhysicsObject.java @@ -0,0 +1,49 @@ +package electrosphere.game.server.creature.type; + +/** + * + * @author amaterasu + */ +public class PhysicsObject { + + String type; + + float dimension1; + float dimension2; + float dimension3; + + float offsetX; + float offsetY; + float offsetZ; + + public String getType() { + return type; + } + + public float getDimension1() { + return dimension1; + } + + public float getDimension2() { + return dimension2; + } + + public float getDimension3() { + return dimension3; + } + + public float getOffsetX() { + return offsetX; + } + + public float getOffsetY() { + return offsetY; + } + + public float getOffsetZ() { + return offsetZ; + } + + + +} diff --git a/src/main/java/electrosphere/game/server/structure/model/CollisionObjectTemplate.java b/src/main/java/electrosphere/game/server/structure/model/CollisionObjectTemplate.java new file mode 100644 index 00000000..edd12839 --- /dev/null +++ b/src/main/java/electrosphere/game/server/structure/model/CollisionObjectTemplate.java @@ -0,0 +1,74 @@ +package electrosphere.game.server.structure.model; + +/** + * + * @author amaterasu + */ +public class CollisionObjectTemplate { + + String type; + public static final String TYPE_CUBE = "CUBE"; + public static final String TYPE_PLANE = "PLANE"; + + + float positionX; + float positionY; + float positionZ; + + float scaleX; + float scaleY; + float scaleZ; + + float rotationW; + float rotationX; + float rotationY; + float rotationZ; + + public String getType() { + return type; + } + + public float getPositionX() { + return positionX; + } + + public float getPositionY() { + return positionY; + } + + public float getPositionZ() { + return positionZ; + } + + public float getScaleX() { + return scaleX; + } + + public float getScaleY() { + return scaleY; + } + + public float getScaleZ() { + return scaleZ; + } + + public float getRotationW() { + return rotationW; + } + + public float getRotationX() { + return rotationX; + } + + public float getRotationY() { + return rotationY; + } + + public float getRotationZ() { + return rotationZ; + } + + + + +} diff --git a/src/main/java/electrosphere/game/server/structure/model/StructureType.java b/src/main/java/electrosphere/game/server/structure/model/StructureType.java new file mode 100644 index 00000000..31d31b66 --- /dev/null +++ b/src/main/java/electrosphere/game/server/structure/model/StructureType.java @@ -0,0 +1,30 @@ +package electrosphere.game.server.structure.model; + +import java.util.List; + +/** + * + * @author amaterasu + */ +public class StructureType { + + String modelPath; + String name; + + List collision; + + public String getModelPath() { + return modelPath; + } + + public List getCollision() { + return collision; + } + + public String getName() { + return name; + } + + + +} diff --git a/src/main/java/electrosphere/game/server/structure/model/StructureTypeMap.java b/src/main/java/electrosphere/game/server/structure/model/StructureTypeMap.java new file mode 100644 index 00000000..520efb30 --- /dev/null +++ b/src/main/java/electrosphere/game/server/structure/model/StructureTypeMap.java @@ -0,0 +1,28 @@ +package electrosphere.game.server.structure.model; + +import java.util.List; + +/** + * + * @author amaterasu + */ +public class StructureTypeMap { + List structures; + + public List getStructures() { + return structures; + } + + public StructureType getType(String name){ + StructureType rVal = null; + for(StructureType type : structures){ + if(type.getName().equals(name)){ + rVal = type; + break; + } + } + return rVal; + } + + +} diff --git a/src/main/java/electrosphere/game/server/world/datacell/DataCellManager.java b/src/main/java/electrosphere/game/server/world/datacell/DataCellManager.java index 56553926..026d853b 100644 --- a/src/main/java/electrosphere/game/server/world/datacell/DataCellManager.java +++ b/src/main/java/electrosphere/game/server/world/datacell/DataCellManager.java @@ -8,8 +8,11 @@ import java.util.List; * @author satellite */ public class DataCellManager { + List loadedDataCells = new LinkedList(); + DataCell[][] dataCells; + public DataCellManager() { } diff --git a/src/main/java/electrosphere/game/server/world/virtualcell/VirtualCellManager.java b/src/main/java/electrosphere/game/server/world/virtualcell/VirtualCellManager.java new file mode 100644 index 00000000..4f122c68 --- /dev/null +++ b/src/main/java/electrosphere/game/server/world/virtualcell/VirtualCellManager.java @@ -0,0 +1,15 @@ +package electrosphere.game.server.world.virtualcell; + +/** + * + * @author amaterasu + */ +public class VirtualCellManager { + + VirtualCell[][] virtualCells; + + public VirtualCellManager() { + + } + +} diff --git a/src/main/java/electrosphere/game/state/MicroSimulation.java b/src/main/java/electrosphere/game/state/MicroSimulation.java index 2766b243..00bd1f39 100644 --- a/src/main/java/electrosphere/game/state/MicroSimulation.java +++ b/src/main/java/electrosphere/game/state/MicroSimulation.java @@ -5,7 +5,7 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.AttackTree; import electrosphere.entity.state.IdleTree; -import electrosphere.entity.state.MovementTree; +import electrosphere.entity.state.movement.MovementTree; import electrosphere.entity.state.ParticleTree; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.hitbox.HitboxUtils; @@ -30,6 +30,12 @@ public class MicroSimulation { } public void simulate(){ + //simulate bullet physics engine step + Globals.collisionEngine.simulatePhysics(deltaTime); + //update dynamic entity positions calculated by bullet +// Globals.collisionEngine.updateDynamicObjectTransforms(); + //list dynamic object positions +// Globals.collisionEngine.listBodyPositions(); //simulate ai Globals.aiManager.simulate(); //update actor animations diff --git a/src/main/java/electrosphere/main/Globals.java b/src/main/java/electrosphere/main/Globals.java index 98b1c201..07a4ad70 100644 --- a/src/main/java/electrosphere/main/Globals.java +++ b/src/main/java/electrosphere/main/Globals.java @@ -21,9 +21,12 @@ import electrosphere.engine.LoadingThread; import electrosphere.game.server.ai.AIManager; import electrosphere.game.server.creature.type.model.CreatureTypeMap; import electrosphere.game.server.item.type.model.ItemTypeMap; +import electrosphere.game.server.structure.model.StructureTypeMap; import electrosphere.game.state.MacroSimulation; import electrosphere.game.server.terrain.manager.ServerTerrainManager; import electrosphere.game.server.world.ServerWorldData; +import electrosphere.game.server.world.datacell.DataCellManager; +import electrosphere.game.server.world.virtualcell.VirtualCellManager; import electrosphere.game.state.MicroSimulation; import electrosphere.menu.Menu; import electrosphere.net.client.ClientNetworking; @@ -100,7 +103,10 @@ public class Globals { // public static ServerWorldData serverWorldData; public static CreatureTypeMap creatureMap; + public static StructureTypeMap structureTypeMap; public static ItemTypeMap itemMap; + VirtualCellManager virtualCellManager; + DataCellManager dataCellManager; @@ -246,6 +252,8 @@ public class Globals { // } catch (IOException ex) { ex.printStackTrace(); } //TODO: handle better :tm: //init creature type map initCreatureTypeMap(); + //init structure type map + initStructureTypeMap(); //init item type map initItemTypeMap(); //create entity manager @@ -260,6 +268,8 @@ public class Globals { hitboxManager = new HitboxManager(); //ai manager aiManager = new AIManager(); + //collision engine + collisionEngine = new CollisionEngine(); } public static void initDefaultGraphicalResources(){ @@ -293,6 +303,12 @@ public class Globals { assetManager.addModelPathToQueue("Models/unitsphere_grey.fbx"); //init smallcube assetManager.addModelPathToQueue("Models/SmallCube.fbx"); + //init unit cylinder + assetManager.addModelPathToQueue("Models/unitcylinder.fbx"); + //init unit plane + assetManager.addModelPathToQueue("Models/unitplane.fbx"); + //init unit plane + assetManager.addModelPathToQueue("Models/unitcube.fbx"); //as these assets are required for the renderer to work, we go ahead and //load them into memory now. The loading time penalty is worth it I think. @@ -306,4 +322,8 @@ public class Globals { static void initItemTypeMap(){ itemMap = FileLoadingUtils.loadObjectFromAssetPath("Data/items.json", ItemTypeMap.class); } + + static void initStructureTypeMap(){ + structureTypeMap = FileLoadingUtils.loadObjectFromAssetPath("Data/structures.json", StructureTypeMap.class); + } } diff --git a/src/main/java/electrosphere/main/Main.java b/src/main/java/electrosphere/main/Main.java index de8b1fec..03257f25 100644 --- a/src/main/java/electrosphere/main/Main.java +++ b/src/main/java/electrosphere/main/Main.java @@ -9,7 +9,7 @@ import electrosphere.renderer.Model; import electrosphere.renderer.RenderUtils; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; -import electrosphere.entity.state.MovementTree; +import electrosphere.entity.state.movement.MovementTree; import electrosphere.entity.types.hitbox.HitboxUtils; import electrosphere.entity.types.item.ItemUtils; import electrosphere.entity.types.attach.AttachUtils; diff --git a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java index 1b6e303e..3322a4c8 100644 --- a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java +++ b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java @@ -32,6 +32,7 @@ import java.util.logging.Logger; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.crypto.stream.CryptoInputStream; import org.apache.commons.crypto.stream.CryptoOutputStream; +import org.joml.Vector3f; /** * @@ -95,7 +96,7 @@ public class ServerConnectionHandler implements Runnable { //spawn player in world Entity newPlayerCharacter = CreatureUtils.spawnBasicCreature("Human"); playerCharacterID = newPlayerCharacter.getId(); - EntityUtils.getPosition(newPlayerCharacter).set(Globals.spawnPoint.x,0,Globals.spawnPoint.z); + CreatureUtils.positionCharacter(newPlayerCharacter, new Vector3f(Globals.spawnPoint.x,3,Globals.spawnPoint.z)); //spawn player sword Entity sword = ItemUtils.spawnBasicItem("Katana"); AttachUtils.attachEntityToEntityAtBone(newPlayerCharacter, sword, "Bone.031"); diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index b995a515..88db4d7b 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -6,6 +6,7 @@ import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.types.hitbox.HitboxData; import electrosphere.entity.types.hitbox.HitboxUtils; +import electrosphere.game.server.creature.type.PhysicsObject; import electrosphere.logger.LoggerInterface; import electrosphere.main.Globals; import static electrosphere.main.Main.deltaTime; @@ -17,6 +18,7 @@ import electrosphere.renderer.framebuffer.Renderbuffer; import electrosphere.renderer.texture.Texture; import electrosphere.renderer.ui.Widget; import org.joml.Matrix4f; +import org.joml.Quaternionf; import org.joml.Vector3f; import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR; import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR; @@ -80,6 +82,7 @@ public class RenderingEngine { static Framebuffer lightDepthBuffer; public static boolean renderHitboxes = false; + public static boolean renderPhysics = true; ShaderProgram activeProgram; @@ -362,6 +365,55 @@ public class RenderingEngine { } } + if(renderPhysics){ + Model physicsGraphicsModel; + for(Entity physicsEntity : Globals.collisionEngine.getDynamicPhysicsEntities()){ + PhysicsObject template = (PhysicsObject)physicsEntity.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE); + switch(template.getType()){ + case "CYLINDER": + if((physicsGraphicsModel = Globals.assetManager.fetchModel("Models/unitcylinder.fbx")) != null){ + Vector3f position = EntityUtils.getPosition(physicsEntity); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(new Vector3f(position)); +// modelTransformMatrix.translate(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()); //center sphere + modelTransformMatrix.scale(template.getDimension1(),template.getDimension2(),template.getDimension3()); + physicsGraphicsModel.modelMatrix = modelTransformMatrix; + physicsGraphicsModel.draw(true, true, false, true, true, true, true); + } + break; + } + } + for(Entity physicsEntity : Globals.collisionEngine.getStructurePhysicsEntities()){ + if(physicsEntity.getDataKeys().contains(EntityDataStrings.COLLISION_ENTITY_TYPE_PLANE)){ + if((physicsGraphicsModel = Globals.assetManager.fetchModel("Models/unitplane.fbx")) != null){ + Vector3f position = EntityUtils.getPosition(physicsEntity); + Vector3f scale = EntityUtils.getScale(physicsEntity); + Quaternionf rotation = EntityUtils.getRotation(physicsEntity); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(new Vector3f(position)); + modelTransformMatrix.rotate(rotation); +// modelTransformMatrix.translate(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()); //center sphere + modelTransformMatrix.scale(scale); + physicsGraphicsModel.modelMatrix = modelTransformMatrix; + physicsGraphicsModel.draw(true, true, false, true, true, true, true); + } + } else if(physicsEntity.getDataKeys().contains(EntityDataStrings.COLLISION_ENTITY_TYPE_CUBE)){ + if((physicsGraphicsModel = Globals.assetManager.fetchModel("Models/unitcube.fbx")) != null){ + Vector3f position = EntityUtils.getPosition(physicsEntity); + Vector3f scale = EntityUtils.getScale(physicsEntity); + Quaternionf rotation = EntityUtils.getRotation(physicsEntity); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(new Vector3f(position)); + modelTransformMatrix.rotate(rotation); +// modelTransformMatrix.translate(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()); //center sphere + modelTransformMatrix.scale(scale); + physicsGraphicsModel.modelMatrix = modelTransformMatrix; + physicsGraphicsModel.draw(true, true, false, true, true, true, true); + } + } + } + } + // glBindVertexArray(0); diff --git a/src/main/java/electrosphere/renderer/ui/UIUtils.java b/src/main/java/electrosphere/renderer/ui/UIUtils.java new file mode 100644 index 00000000..bff8b574 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/UIUtils.java @@ -0,0 +1,8 @@ +package electrosphere.renderer.ui; + +public class UIUtils { + + public static void initUIComponents(){ + + } +} diff --git a/src/main/java/electrosphere/renderer/ui/transparenttextbox/TextBox.java b/src/main/java/electrosphere/renderer/ui/transparenttextbox/TextBox.java new file mode 100644 index 00000000..84161888 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/transparenttextbox/TextBox.java @@ -0,0 +1,91 @@ +package electrosphere.renderer.ui.transparenttextbox; + +import electrosphere.main.Globals; +import electrosphere.renderer.Model; +import electrosphere.renderer.assetmanager.AssetDataStrings; +import electrosphere.renderer.ui.Widget; +import electrosphere.renderer.ui.font.FontUtils; +import org.joml.Vector3f; + +public class TextBox extends Widget{ + + + int positionX; + int positionY; + int width; + int height; + String text; + boolean editable; + + Vector3f scalar; + + public TextBox(int positionX, int positionY, int width, int height, String text, boolean render, boolean editable) { + super(positionX,positionY,width,height,render,Widget.WidgetType.TEXT_BOX); + this.positionX = positionX; + this.positionY = positionY; + this.width = width; + this.height = height; + this.text = text; + scalar = new Vector3f(1,1,1); + } + + public TextBox(int positionX, int positionY, int width, int height, String text, boolean render, boolean editable, Vector3f scalar) { + super(positionX,positionY,width,height,render,Widget.WidgetType.TEXT_BOX); + this.positionX = positionX; + this.positionY = positionY; + this.width = width; + this.height = height; + this.text = text; + this.scalar = scalar; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public boolean isEditable() { + return editable; + } + + public void setEditable(boolean editable) { + this.editable = editable; + } + + + @Override + public void draw(){ + throw new UnsupportedOperationException("Transparent Text box draw function not implemented yet oop"); +// float ndcX = (float)positionX/Globals.WINDOW_WIDTH; +// float ndcY = (float)positionY/Globals.WINDOW_HEIGHT; +// float ndcWidth = (float)width/Globals.WINDOW_WIDTH; +// float ndcHeight = (float)height/Globals.WINDOW_HEIGHT; +// //monowidth for the moment +// float charWidth = ndcWidth/cols; +// float charHeight = ndcHeight/rows; +// for(int y = 0; y < rows; y++){ +// for(int x = 0; x < cols; x++){ +// char toDraw = ' '; +// if(x + y * cols < text.length()){ +// toDraw = text.charAt(x + y * cols); +// } +// Vector3f characterPosition = new Vector3f(ndcX + x * charWidth,ndcY + y * charHeight,0); +// Vector3f characterDimensions = new Vector3f(charWidth,charHeight,0); +// Vector3f bitMapPosition = FontUtils.getPositionOfCharacter(toDraw); +// Vector3f bitMapDimension = FontUtils.getDimensionOfCharacter(toDraw); +// Model charModel = Globals.assetManager.fetchModel(AssetDataStrings.ASSET_STRING_BITMAP_FONT); +// if(charModel != null && toDraw != ' '){ +// charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "mPosition", characterPosition); +// charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "mDimension", characterDimensions); +// charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "tPosition", bitMapPosition); +// charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "tDimension", bitMapDimension); +// charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "color", color); +// charModel.drawUI(); +// } +// } +// } + } +} diff --git a/src/main/java/electrosphere/renderer/ui/transparenttextbox/TextBoxUtils.java b/src/main/java/electrosphere/renderer/ui/transparenttextbox/TextBoxUtils.java new file mode 100644 index 00000000..ce50c48e --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/transparenttextbox/TextBoxUtils.java @@ -0,0 +1,9 @@ +package electrosphere.renderer.ui.transparenttextbox; + +/** + * + * @author amaterasu + */ +public class TextBoxUtils { + +}