diff --git a/assets/Data/creatures/human.json b/assets/Data/creatures/human.json index b5c7bded..bc3c3059 100644 --- a/assets/Data/creatures/human.json +++ b/assets/Data/creatures/human.json @@ -241,10 +241,10 @@ "equipPoints" : [ { "equipPointId" : "handLeft", - "bone" : "MiddleLower.L", + "bone" : "Hand.L", "firstPersonBone" : "hand.L", - "offsetVector" : [], - "offsetRotation" : [], + "offsetVector" : [0,0,0], + "offsetRotation" : [0,0,0,1], "equipClassWhitelist" : [ "tool", "shield", @@ -253,10 +253,10 @@ }, { "equipPointId" : "handRight", - "bone" : "MiddleLower.R", + "bone" : "Hand.R", "firstPersonBone" : "hand.R", - "offsetVector" : [], - "offsetRotation" : [0.3057,0.2926,0.09933,0.9006], + "offsetVector" : [0,0,0], + "offsetRotation" : [-0.334,0.145,-0.28,0.89], "equipClassWhitelist" : [ "tool", "weapon", diff --git a/assets/Data/items.json b/assets/Data/items.json index 69c53fb3..d6e3ea84 100644 --- a/assets/Data/items.json +++ b/assets/Data/items.json @@ -11,17 +11,17 @@ "damage" : 10, "hitboxes" : [ { - "type": "hit", + "type": "hit_connected", "bone": "Blade1", "radius": 0.04 }, { - "type": "hit", + "type": "hit_connected", "bone": "Blade2", "radius": 0.04 }, { - "type": "hit", + "type": "hit_connected", "bone": "Blade3", "radius": 0.04 } diff --git a/assets/Data/objects.json b/assets/Data/objects.json index cfd614b3..27eccda6 100644 --- a/assets/Data/objects.json +++ b/assets/Data/objects.json @@ -26,6 +26,7 @@ ], "files" : [ "Data/objects/floatingisland.json", - "Data/objects/testscene1objects.json" + "Data/objects/testscene1objects.json", + "Data/objects/debug_objects.json" ] } \ No newline at end of file diff --git a/assets/Data/objects/debug_objects.json b/assets/Data/objects/debug_objects.json new file mode 100644 index 00000000..be609fd4 --- /dev/null +++ b/assets/Data/objects/debug_objects.json @@ -0,0 +1,18 @@ +{ + "objects" : [ + + { + "objectId" : "hitboxTester", + "hitboxData" : [ + { + "type": "static_capsule", + "radius": 2, + "length": 5 + } + ], + "tokens": [] + } + + ], + "files" : [] +} \ No newline at end of file diff --git a/assets/Data/tutorial/hints.json b/assets/Data/tutorial/hints.json new file mode 100644 index 00000000..c6d5a846 --- /dev/null +++ b/assets/Data/tutorial/hints.json @@ -0,0 +1,10 @@ +{ + "hints": [ + { + "id": "Basic Navigation", + "titleString": "Navigation", + "descriptionString": "You can move the mouse to move the camera around. Also, you can use the W, A, S, and D keys to move your character.", + "image": "" + } + ] +} \ No newline at end of file diff --git a/assets/Models/basic/geometry/unitcapsule.glb b/assets/Models/basic/geometry/unitcapsule.glb new file mode 100644 index 00000000..d55758ac Binary files /dev/null and b/assets/Models/basic/geometry/unitcapsule.glb differ diff --git a/assets/Models/creatures/person2/person2_1.glb b/assets/Models/creatures/person2/person2_1.glb index da9c8e0a..3c861e6e 100644 Binary files a/assets/Models/creatures/person2/person2_1.glb and b/assets/Models/creatures/person2/person2_1.glb differ diff --git a/buildNumber.properties b/buildNumber.properties index e573ac48..14c7a866 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Sat Jun 01 15:22:11 EDT 2024 -buildNumber=134 +#Fri Jun 14 13:45:11 EDT 2024 +buildNumber=137 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index f641d48a..7a44f5cf 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -334,16 +334,83 @@ Redo hitboxes to have capsules and also chaining between frames (but not between Redo hitboxes to have capsules and also chaining between frames (but not between swinging the camera around) - Write custom callback for the collision engine for just hitboxes +(06/04/2024) +Redo hitboxes to have capsules and also chaining between frames (but not between swinging the camera around) + - Need to have an object attached to creature that stores the rigid body + - When creating the creature, for each hitbox, create shapes for the rigid body + - Attach the overall object to the creature entity + +(06/07/2024) +Hitboxes work to properly use capsules (constantly destroy/recreate every frane because od4j doesn't allow rescaling :<) + +(06/10/2024) +Add flow for demo menu/level loading + +(06/11/2024) +Redo hitboxes to have capsules and also chaining between frames (but not between swinging the camera around) + - Properly calculate the capsule that bridges from previous frame to current frame hitbox location + - Write custom callback for the collision engine for just hitboxes +Fix player model side-jog animations + +(06/13/2024) +Fix newly exported model not rendering correctly + - All bones are passed into the shader every render call, the bone values must be corrupted + - NIGHTMARE BUG +Fix equipping an item spawning two items + +(06/14/2024) +Fix inventory ui not closing when you hit 'i' key (will need to update utility functions to manage input mode so you're not doing it in callback) +Develop debug ui for equip points + # TODO + +Demo requirements: + = Assets = +Block animation in first person +Block animation in third person +Fix attack animation bone rotations for hand +Clean up equip state data +Audio FX for everything + + + = Coding = +Fix items falling below the ground +Control rebinding menu from title screen +Redo hitboxes to have capsules and also chaining between frames (but not between swinging the camera around) + - Introduce block hitbox (blockbox) type +Enemy AI +Probably some kind of tutorial text +Network-able ui messages +Ability to display video both on title screen as well as in game windows for tutorials +better scaffolding for scripting engine with hooks for equipping items, spawning entities, pausing/resuming play, etc +Ability for private realms to have time start/stop based on the player's feedback <-- sync this up to tutorial ui via script + + + + + +BIG BIG BIG BIG IMMEDIATE TO DO: +always enforce opengl interface across all opengl calls jesus christ the bone uniform bug was impossible + + + + + + Redo hitboxes to have capsules and also chaining between frames (but not between swinging the camera around) - Introduce block hitbox (blockbox) type +Fix voxel type selection menu not showing textures + - The quads are off screen because the calculation for ndcX/ndcY are putting it wayyy to the right -- will need to revisit calcs for all that + Fix being able to walk off far side of the world (ie in level editor) Grass System properly LOD - Have foliage dynamically time out cells to be reconsidered based on distance from player (if close, short cooldown, if far long cooldown) +Would be nice to be able to cut clients that stream their logs to my server + Data Cleanup - Clean up creatures - Remove unused ones diff --git a/src/main/java/electrosphere/audio/AudioListener.java b/src/main/java/electrosphere/audio/AudioListener.java index 1ddf4e50..8ea0440d 100644 --- a/src/main/java/electrosphere/audio/AudioListener.java +++ b/src/main/java/electrosphere/audio/AudioListener.java @@ -2,6 +2,9 @@ package electrosphere.audio; import org.joml.Vector3d; import org.joml.Vector3f; + +import electrosphere.util.MathUtils; + import static org.lwjgl.openal.AL10.*; /** @@ -13,7 +16,7 @@ public class AudioListener { Vector3d position; //eye vector for listener - Vector3f eye = new Vector3f(1,0,0); + Vector3f eye = MathUtils.getOriginVectorf(); //up vector for listener Vector3f up = new Vector3f(0,1,0); diff --git a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java index 37a265e3..30ad425d 100644 --- a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java +++ b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java @@ -154,7 +154,7 @@ public class ClientSceneWrapper { CollisionResolutionCallback resolutionCallback = new CollisionResolutionCallback() { @Override public void resolve(DContactGeom geom, Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude) { - HitboxUtils.clientDamageHitboxColision(impactor.getParent(), receiver.getParent()); + HitboxUtils.clientDamageHitboxColision(geom, impactor, receiver, normal, localPosition, worldPos, magnitude); } }; diff --git a/src/main/java/electrosphere/client/sim/ClientSimulation.java b/src/main/java/electrosphere/client/sim/ClientSimulation.java index 3b94060d..ac5704db 100644 --- a/src/main/java/electrosphere/client/sim/ClientSimulation.java +++ b/src/main/java/electrosphere/client/sim/ClientSimulation.java @@ -54,7 +54,7 @@ public class ClientSimulation { //simulate bullet physics engine step Globals.clientSceneWrapper.getCollisionEngine().simulatePhysics((float)Globals.timekeeper.getSimFrameTime()); Globals.clientSceneWrapper.getCollisionEngine().updateDynamicObjectTransforms(); - // + //update actor animations Globals.profiler.beginCpuSample("update actor animations"); for(Entity currentEntity : Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.DRAWABLE)){ diff --git a/src/main/java/electrosphere/collision/CollisionBodyCreation.java b/src/main/java/electrosphere/collision/CollisionBodyCreation.java index 5ca9aae4..6f8db4f4 100644 --- a/src/main/java/electrosphere/collision/CollisionBodyCreation.java +++ b/src/main/java/electrosphere/collision/CollisionBodyCreation.java @@ -11,6 +11,7 @@ import org.lwjgl.assimp.AIVector3D; import org.ode4j.math.DMatrix3; import org.ode4j.ode.DBody; import org.ode4j.ode.DBox; +import org.ode4j.ode.DCapsule; import org.ode4j.ode.DCylinder; import org.ode4j.ode.DGeom; import org.ode4j.ode.DSphere; @@ -24,6 +25,7 @@ import electrosphere.entity.types.terrain.TerrainChunkData; public class CollisionBodyCreation { //Matrix for correcting initial axis of eg cylinders or capsules + //this rotates by 90 degrees along the x axis public static final DMatrix3 AXIS_CORRECTION_MATRIX = new DMatrix3( 1.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, -1.0000000, @@ -95,12 +97,25 @@ public class CollisionBodyCreation { * Creates a sphere shape * @param collisionEngine the collision engine * @param radius the radius of the sphere + * @param categoryBits the category bits for the shape * @return the sphere shape */ public static DSphere createShapeSphere(CollisionEngine collisionEngine, double radius, long categoryBits){ return collisionEngine.createSphereGeom(radius, categoryBits); } + /** + * Creates a capsule shape + * @param collisionEngine The collision engine + * @param radius the radius of the capsule + * @param length the length of the capsule + * @param categoryBits the category bits for the shape + * @return the capsule shape + */ + public static DCapsule createCapsuleShape(CollisionEngine collisionEngine, double radius, double length, long categoryBits){ + return collisionEngine.createCapsuleGeom(radius, length, categoryBits); + } + /** * Sets the provided body to be a kinematic body (no gravity applied) * @param collisionEngine The collision engine @@ -130,6 +145,35 @@ public class CollisionBodyCreation { collisionEngine.setOffsetPosition(body, offsetPosition); } + /** + * Removes a geom from a body + * @param collisionEngine the collision engine + * @param body the body + * @param geom the geometry + */ + public static void removeShapeFromBody(CollisionEngine collisionEngine, DBody body, DGeom geom){ + collisionEngine.removeGeometryFromBody(body, geom); + } + + /** + * Destroys a geometry + * @param collisionEngine The collision engine + * @param geom the geometry + */ + public static void destroyShape(CollisionEngine collisionEngine, DGeom geom){ + collisionEngine.destroyGeom(geom); + } + + /** + * Attaches a geom to a body + * @param collisionEngine the collision engine + * @param body the body + * @param geom the geometry + */ + public static void attachGeomToBody(CollisionEngine collisionEngine, DBody body, DGeom geom){ + collisionEngine.attachGeomToBody(body, geom); + } + /** * Creates an ode DBody from a terrain chunk data object diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index 9597fd6e..e06b46cb 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -842,6 +842,32 @@ public class CollisionEngine { body.getGeomIterator().next().setOffsetPosition(offsetVector.x,offsetVector.y,offsetVector.z); spaceLock.release(); } + + /** + * Removes the geometry from the body + * @param body the body + * @param geom the geometry + */ + protected void removeGeometryFromBody(DBody body, DGeom geom){ + geom.setBody(null); + } + + /** + * Destroys a geometry + * @param geom The geometry + */ + protected void destroyGeom(DGeom geom){ + geom.destroy(); + } + + /** + * Attaches a geom to a body + * @param body the body + * @param geom the geom + */ + protected void attachGeomToBody(DBody body, DGeom geom){ + geom.setBody(body); + } /** diff --git a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java index c65368b1..6f03d7bc 100644 --- a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java @@ -6,7 +6,6 @@ import org.joml.Quaterniond; import org.joml.Vector3d; import org.joml.Vector3f; import org.ode4j.ode.DBody; -import org.ode4j.ode.DCylinder; import org.ode4j.ode.DGeom; import org.ode4j.ode.DTriMesh; diff --git a/src/main/java/electrosphere/collision/hitbox/HitboxManager.java b/src/main/java/electrosphere/collision/hitbox/HitboxManager.java index 0ec8e9e0..3c978c68 100644 --- a/src/main/java/electrosphere/collision/hitbox/HitboxManager.java +++ b/src/main/java/electrosphere/collision/hitbox/HitboxManager.java @@ -3,7 +3,7 @@ package electrosphere.collision.hitbox; import electrosphere.collision.CollisionEngine; import electrosphere.collision.CollisionEngine.CollisionResolutionCallback; import electrosphere.engine.Globals; -import electrosphere.entity.state.hitbox.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -12,7 +12,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class HitboxManager { //the list of all hitboxes - CopyOnWriteArrayList hitboxes = new CopyOnWriteArrayList(); + CopyOnWriteArrayList hitboxes = new CopyOnWriteArrayList(); //the collision engine for this hitbox manager CollisionEngine collisionEngine; @@ -33,7 +33,7 @@ public class HitboxManager { * Registers a hitbox to the manager * @param hitbox the hitbox to register */ - public void registerHitbox(HitboxState hitbox){ + public void registerHitbox(HitboxCollectionState hitbox){ hitboxes.add(hitbox); idIncrementer++; } @@ -42,7 +42,7 @@ public class HitboxManager { * Gets all hitboxes in the manager * @return all hitboxes in the manager */ - public CopyOnWriteArrayList getAllHitboxes(){ + public CopyOnWriteArrayList getAllHitboxes(){ return hitboxes; } @@ -50,7 +50,7 @@ public class HitboxManager { * Deregisters a hitbox from the manager * @param hitbox the hitbox to deregister */ - public void deregisterHitbox(HitboxState hitbox){ + public void deregisterHitbox(HitboxCollectionState hitbox){ hitboxes.remove(hitbox); } @@ -68,7 +68,8 @@ public class HitboxManager { public void simulate(){ //update all positions Globals.profiler.beginCpuSample("Update hitbox positions"); - for(HitboxState state : hitboxes){ + for(HitboxCollectionState state : hitboxes){ + state.clearCollisions(); state.updateHitboxPositions(this.collisionEngine); } Globals.profiler.endCpuSample(); diff --git a/src/main/java/electrosphere/collision/hitbox/HitboxUtils.java b/src/main/java/electrosphere/collision/hitbox/HitboxUtils.java index 52458823..4eb0fb68 100644 --- a/src/main/java/electrosphere/collision/hitbox/HitboxUtils.java +++ b/src/main/java/electrosphere/collision/hitbox/HitboxUtils.java @@ -1,25 +1,20 @@ package electrosphere.collision.hitbox; -import electrosphere.engine.Globals; +import electrosphere.collision.collidable.Collidable; import electrosphere.entity.Entity; -import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.types.attach.AttachUtils; -import electrosphere.entity.types.creature.CreatureUtils; -import electrosphere.entity.types.item.ItemUtils; -import electrosphere.entity.state.life.LifeState; -import electrosphere.entity.state.life.LifeUtils; -import electrosphere.entity.state.movement.ProjectileTree; +import electrosphere.entity.state.hitbox.HitboxCollectionState; +import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxType; import electrosphere.game.data.collidable.HitboxData; -import electrosphere.game.server.effects.ParticleEffects; -import electrosphere.server.datacell.Realm; - -import java.util.List; import org.joml.Quaterniond; import org.joml.Vector3d; import org.joml.Vector3f; +import org.ode4j.ode.DContactGeom; +import org.ode4j.ode.DGeom; /** * Utilities for working with hitboxes @@ -237,8 +232,38 @@ public class HitboxUtils { * @param impactor the entity initiating the collision * @param receiver the entity receiving the collision */ - public static void clientDamageHitboxColision(Entity impactor, Entity receiver){ + public static void clientDamageHitboxColision(DContactGeom contactGeom, Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude){ + + Entity impactorParent = impactor.getParent(); + Entity receiverParent = receiver.getParent(); + HitboxCollectionState impactorState = HitboxCollectionState.getHitboxState(impactorParent); + HitboxCollectionState receiverState = HitboxCollectionState.getHitboxState(receiverParent); + DGeom impactorGeom = contactGeom.g1; + DGeom receiverGeom = contactGeom.g2; + HitboxState impactorShapeStatus = impactorState.getShapeStatus(impactorGeom); + HitboxState receiverShapeStatus = receiverState.getShapeStatus(receiverGeom); + + //currently, impactor needs to be an item, and the receiver must not be an item + boolean isDamageEvent = + impactorShapeStatus != null && + receiverShapeStatus != null && + impactorShapeStatus.getType() == HitboxType.HIT && + receiverShapeStatus.getType() == HitboxType.HURT && + AttachUtils.getParent(impactorParent) != receiverParent + ; + + if(impactorShapeStatus != null){ + impactorShapeStatus.setHadCollision(true); + } + if(receiverShapeStatus != null){ + receiverShapeStatus.setHadCollision(true); + } + + if(isDamageEvent){ + //TODO: client logic for audio etc + } + // Entity hitboxParent = (Entity)impactor.getData(EntityDataStrings.COLLISION_ENTITY_DATA_PARENT); // Entity hurtboxParent = (Entity)receiver.getData(EntityDataStrings.COLLISION_ENTITY_DATA_PARENT); diff --git a/src/main/java/electrosphere/controls/CameraHandler.java b/src/main/java/electrosphere/controls/CameraHandler.java index 2702652f..27bfc4b9 100644 --- a/src/main/java/electrosphere/controls/CameraHandler.java +++ b/src/main/java/electrosphere/controls/CameraHandler.java @@ -12,18 +12,34 @@ import electrosphere.entity.EntityUtils; import electrosphere.entity.types.camera.CameraEntityUtils; import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.renderer.ui.events.MouseEvent; +import electrosphere.util.MathUtils; +/** + * Handler for camera-related events and controls + */ public class CameraHandler { + //the horizontal mouse sensitivity float mouseSensitivityHorizontal = .1f; + //the vertical mouse sensitivity float mouseSensitivityVertical = .08f; + //the speed of the freecam float cameraSpeed; + //the current yaw float yaw = 150; + //the current pitch float pitch = 50; + //the camera's rotation vector Vector3f cameraRotationVector = new Vector3f(); + //the radial offset of the camera Vector3f radialOffset = new Vector3f(0,1,0); + //if set to true, the camera will track the player's entity boolean trackPlayerEntity = true; + /** + * Handles a mouse event + * @param event The mouse event + */ public void handleMouseEvent(MouseEvent event){ if(Globals.controlHandler != null && !Globals.controlHandler.isMouseVisible()){ @@ -41,10 +57,17 @@ public class CameraHandler { updateGlobalCamera(); } + /** + * Updates the radial offset + * @param offset the radial offset + */ public void updateRadialOffset(Vector3f offset){ radialOffset = offset; } + /** + * Updates the global camera + */ public void updateGlobalCamera(){ Globals.profiler.beginCpuSample("updateGlobalCamera"); if(Globals.playerCamera != null){ @@ -83,7 +106,7 @@ public class CameraHandler { // float pitchRad = pitch / 180.0f * (float)Math.PI; // float rollRad = 0.0f; // pitchQuat.mul(yawQuat); - cameraRotationVector = pitchQuat.transform(new Vector3f(0,0,1)); + cameraRotationVector = pitchQuat.transform(MathUtils.getOriginVectorf()); cameraRotationVector = yawQuat.transform(cameraRotationVector); cameraRotationVector.normalize(); @@ -150,10 +173,18 @@ public class CameraHandler { } } + /** + * Gets the yaw of the camera handler + * @return the yaw + */ public float getYaw(){ return yaw; } + /** + * Gets the pitch of the camera handler + * @return the pitch + */ public float getPitch(){ return pitch; } diff --git a/src/main/java/electrosphere/controls/ControlHandler.java b/src/main/java/electrosphere/controls/ControlHandler.java index 1d74d694..ee8ef12c 100644 --- a/src/main/java/electrosphere/controls/ControlHandler.java +++ b/src/main/java/electrosphere/controls/ControlHandler.java @@ -61,6 +61,7 @@ import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_RIGHT; import static org.lwjgl.glfw.GLFW.glfwGetCursorPos; import static org.lwjgl.glfw.GLFW.glfwSetInputMode; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -107,8 +108,7 @@ import electrosphere.renderer.ui.events.MouseEvent; import electrosphere.renderer.ui.events.ScrollEvent; /** - * - * @author amaterasu + * Main handler for controls */ public class ControlHandler { @@ -204,7 +204,9 @@ public class ControlHandler { public static final String FREECAM_RIGHT = "freecamRight"; public static final String FREECAM_MOUSE = "freecamMouse"; - + /** + * The different buckets of inputs that the control handler be configured to scan for each frame + */ public static enum ControlsState { TITLE_PAGE, TITLE_MENU, @@ -215,15 +217,40 @@ public class ControlHandler { NO_INPUT, } + //The bucket of inputs that the control handler is currently scanning for ControlsState state = ControlsState.TITLE_MENU; + + + /** + * The list of control states that have the mouse visible and enabled + */ + static ControlsState[] mouseEnabledStates = new ControlsState[]{ + ControlsState.TITLE_PAGE, + ControlsState.TITLE_MENU, + ControlsState.IN_GAME_MAIN_MENU, + ControlsState.INVENTORY, + }; + + //controls whether the mouse is visible or not boolean mouseIsVisible = true; + //if set to true, opengl will try to capture the screen next frame boolean shouldRecaptureScreen = false; //controls whether the camera is first or third person boolean cameraIsThirdPerson = true; + //The list of window strings that would block main game controls + static String[] controlBlockingWindows = new String[]{ + WindowStrings.LEVEL_EDTIOR_SIDE_PANEL, + WindowStrings.VOXEL_TYPE_SELECTION, + WindowStrings.WINDOW_CHARACTER, + WindowStrings.WINDOW_DEBUG, + WindowStrings.WINDOW_MENU_INGAME_MAIN, + WindowStrings.WINDOW_MENU_INVENTORY, + }; + /* Mouse event parsing related stuff @@ -234,10 +261,10 @@ public class ControlHandler { double ypos = 300; double mouse_X_Buffer[] = new double[1]; double mouse_Y_Buffer[] = new double[1]; - boolean dragging = false; + boolean dragging = false; //tracks whether the mouse is doing a drag input or not - + //The map of data string -> control object HashMap controls; List mainGameControlList = new LinkedList(); @@ -248,10 +275,17 @@ public class ControlHandler { List alwaysOnDebugControlList = new LinkedList(); List freeCameraControlList = new LinkedList(); + /** + * Constructor + */ ControlHandler(){ controls = new HashMap(); } + /** + * Generates an example controls map + * @return the example controls map object + */ public static ControlHandler generateExampleControlsMap(){ ControlHandler handler = new ControlHandler(); /* @@ -355,11 +389,6 @@ public class ControlHandler { framestep controls */ handler.addControl(DEBUG_FRAMESTEP, new Control(ControlType.KEY, GLFW_KEY_P)); - - /* - set state - */ - handler.setHandlerState(ControlsState.TITLE_MENU); /* * Free camera @@ -393,7 +422,9 @@ public class ControlHandler { - + /** + * Polls the currently set bucket of controls + */ public void pollControls(){ switch(state){ @@ -446,7 +477,9 @@ public class ControlHandler { Globals.scrollCallback.clear(); } - + /** + * Attaches callbacks to each of the control objects + */ public void setCallbacks(){ setMainGameControls(); setInGameDebugControls(); @@ -457,6 +490,10 @@ public class ControlHandler { setFreecamControls(); } + + /** + * Sets callbacks for the main game controls + */ void setMainGameControls(){ /* Camera rotation @@ -909,7 +946,7 @@ public class ControlHandler { mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU)); controls.get(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU).setOnClick(new ControlMethod(){public void execute(){ // Globals.elementManager.registerWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN, MenuGenerators.createInGameMainMenu()); - // Globals.controlHandler.setHandlerState(ControlsState.IN_GAME_MAIN_MENU); + // Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); // Window mainMenuWindow = new Window(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); Window mainMenuInGame = MenuGeneratorsInGame.createInGameMainMenu(); @@ -917,8 +954,7 @@ public class ControlHandler { Globals.elementManager.registerWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN, mainMenuInGame); WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN), true); Globals.elementManager.focusFirstElement(); - Globals.controlHandler.setHandlerState(ControlsState.IN_GAME_MAIN_MENU); - Globals.controlHandler.showMouse(); + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); //play sound effect Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/openMenu.ogg", VirtualAudioSourceType.UI, false); }}); @@ -939,12 +975,13 @@ public class ControlHandler { //make visible WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowUtils.getInventoryWindowID(inventory.getId())), true); //controls - Globals.controlHandler.setHandlerState(ControlsState.INVENTORY); - Globals.controlHandler.showMouse(); + Globals.controlHandler.hintUpdateControlState(ControlsState.INVENTORY); //play sound effect Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/openMenu.ogg", VirtualAudioSourceType.UI, false); // Globals.openInventoriesCount++; + } else if(InventoryUtils.hasNaturalInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowUtils.getInventoryWindowID(InventoryUtils.getNaturalInventory(Globals.playerEntity).getId())) != null){ + Globals.elementManager.closeWindow(WindowUtils.getInventoryWindowID(InventoryUtils.getNaturalInventory(Globals.playerEntity).getId())); } }}); controls.get(INPUT_CODE_INVENTORY_OPEN).setRepeatTimeout(0.5f * Main.targetFrameRate); @@ -957,18 +994,19 @@ public class ControlHandler { controls.get(INPUT_CODE_CHARACTER_OPEN).setOnClick(new ControlMethod(){public void execute(){ if(InventoryUtils.hasEquipInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowStrings.WINDOW_CHARACTER) == null){ //create window - Window mainMenuWindow = MenuGeneratorsInventory.createCharacterInventoryMenu(InventoryUtils.getEquipInventory(Globals.playerEntity)); + Window characterInventoryMenu = MenuGeneratorsInventory.createCharacterInventoryMenu(InventoryUtils.getEquipInventory(Globals.playerEntity)); //register - Globals.elementManager.registerWindow(WindowStrings.WINDOW_CHARACTER, mainMenuWindow); + Globals.elementManager.registerWindow(WindowStrings.WINDOW_CHARACTER, characterInventoryMenu); //make visible WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_CHARACTER), true); //controls - Globals.controlHandler.setHandlerState(ControlsState.INVENTORY); - Globals.controlHandler.showMouse(); + Globals.controlHandler.hintUpdateControlState(ControlsState.INVENTORY); //play sound effect Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/openMenu.ogg", VirtualAudioSourceType.UI, false); // Globals.openInventoriesCount++; + } else if(InventoryUtils.hasEquipInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowStrings.WINDOW_CHARACTER) != null){ + Globals.elementManager.closeWindow(WindowStrings.WINDOW_CHARACTER); } }}); controls.get(INPUT_CODE_CHARACTER_OPEN).setRepeatTimeout(0.5f * Main.targetFrameRate); @@ -1037,6 +1075,10 @@ public class ControlHandler { } + + /** + * Sets the in game debug control callbacks + */ void setInGameDebugControls(){ mainGameDebugControlList.add(controls.get(DATA_STRING_INPUT_CODE_DEBUG_SPAWN_ITEM)); controls.get(DATA_STRING_INPUT_CODE_DEBUG_SPAWN_ITEM).setOnPress(new ControlMethod(){public void execute(){ @@ -1050,6 +1092,10 @@ public class ControlHandler { controls.get(DEBUG_FRAMESTEP).setOnRelease(new ControlMethod(){public void execute(){ Main.setFramestep(1); }}); + controls.get(DEBUG_FRAMESTEP).setOnRepeat(new ControlMethod(){public void execute(){ + Main.setFramestep(1); + }}); + controls.get(DEBUG_FRAMESTEP).setRepeatTimeout(0.5f * Main.targetFrameRate); // RenderingEngine.incrementOutputFramebuffer(); } @@ -1197,6 +1243,9 @@ public class ControlHandler { } + /** + * Sets the freecam control callbacks + */ void setFreecamControls(){ freeCameraControlList.add(controls.get(FREECAM_UP)); controls.get(FREECAM_UP).setOnRepeat(new ControlMethod(){public void execute(){ @@ -1269,6 +1318,9 @@ public class ControlHandler { } + /** + * Sets the typing control callbacks + */ void setTypingControls(){ String[] typeKeybinds = { @@ -1332,6 +1384,9 @@ public class ControlHandler { } } + /** + * Sets the inventory control callbacks + */ void setInventoryControls(){ /* Close inventory @@ -1380,6 +1435,10 @@ public class ControlHandler { } + /** + * Checks a list of controls to see if the corresponding key/mouse event is firing this frame + * @param controls The list of controls to check + */ public void runHandlers(List controls){ //construct mouse event @@ -1461,6 +1520,55 @@ public class ControlHandler { } } + /** + * Checks if any menus are open that would intercept player input (main menu, inventory, debug, etc) + * @return true if such a menu is open, false otherwise + */ + private boolean hasControlBlockingMenuOpen(){ + boolean rVal = false; + //check main ui framework windows + for(String windowString : controlBlockingWindows){ + rVal = rVal || WindowUtils.windowIsOpen(windowString); + } + //check imgui windows + rVal = rVal || Globals.renderingEngine.getImGuiPipeline().shouldCaptureControls(); + return rVal; + } + + /** + * Hints to the engine that it should update the control state + * The provided control state will be overwritten if, for instance, + * there is a menu open that demands mouse input and you are trying + * to tell the engine to convert to immediate player control + * @param desiredState The desired control state + */ + public void hintUpdateControlState(ControlsState desiredState){ + ControlsState properState = desiredState; + //correct for freecam or actual ingame control based on value of getTrackPlayerEntity + if(desiredState == ControlsState.IN_GAME_FREE_CAMERA && Globals.cameraHandler.getTrackPlayerEntity()){ + properState = ControlsState.MAIN_GAME; + } + if(desiredState == ControlsState.MAIN_GAME && !Globals.cameraHandler.getTrackPlayerEntity()){ + properState = ControlsState.IN_GAME_FREE_CAMERA; + } + + //set to menu state if a menu is open, otherwise use the hinted control scheme + if(hasControlBlockingMenuOpen()){ + setHandlerState(ControlsState.IN_GAME_MAIN_MENU); + } else { + setHandlerState(properState); + } + //checks if the current handler state should have mouse enabled or not + if(Arrays.binarySearch(mouseEnabledStates,getHandlerState()) >= 0){ + showMouse(); + } else { + hideMouse(); + } + } + + /** + * Transfers the mouse position from the glfw buffer to variables stored inside the control handler + */ void getMousePositionInBuffer(){ //only if not headless, gather position if(!Globals.HEADLESS){ @@ -1470,6 +1578,10 @@ public class ControlHandler { } } + /** + * Checks if the mouse button 1 is currently pressed + * @return true if pressed, false otherwise + */ boolean getButton1Raw(){ if(Globals.HEADLESS){ return false; @@ -1478,6 +1590,10 @@ public class ControlHandler { } } + /** + * Checks if the mouse button 2 is currently pressed + * @return true if pressed, false otherwise + */ boolean getButton2Raw(){ if(Globals.HEADLESS){ return false; @@ -1502,33 +1618,51 @@ public class ControlHandler { controls.put(controlName, c); } - public void setHandlerState(ControlsState state){ + /** + * Sets the state of the controls handler + * @param state the state + */ + private void setHandlerState(ControlsState state){ this.state = state; } + /** + * Gets the current state of the controls handler + * @return the state + */ public ControlsState getHandlerState(){ return state; } - public ControlsState getState(){ - return state; - } - + /** + * Hides the mouse + */ public void hideMouse(){ glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); mouseIsVisible = false; } + /** + * Shows the mouse + */ public void showMouse(){ glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); mouseIsVisible = true; } + /** + * Gets whether the mouse is visible or not + * @return true if visible, false otherwise + */ public boolean isMouseVisible(){ return mouseIsVisible; } + /** + * Gets the mouse position as a vector2f + * @return The vector containing the mouse position + */ public Vector2f getMousePosition(){ double posX[] = new double[1]; double posY[] = new double[1]; @@ -1537,6 +1671,11 @@ public class ControlHandler { return rVal; } + /** + * Converts a keycode to a string containing a code related to the keycode (ie "A" for 65, "Escape" for 256, etc) + * @param code The keycode + * @return The corresponding string code + */ public static String convertKeycodeToName(int code){ String rVal = ""; switch(code){ @@ -1667,10 +1806,18 @@ public class ControlHandler { return rVal; } + /** + * Sets whether the engine should try to recapture window focus next frame or not + * @param shouldRecapture true if should try to recapture next frame, false otherwise + */ public void setRecapture(boolean shouldRecapture){ this.shouldRecaptureScreen = shouldRecapture; } + /** + * Returns whether the engine should try to recapture window focus next frame or not + * @return true if it should try to recapture, false otherwise + */ public boolean shouldRecapture(){ return this.shouldRecaptureScreen; } diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 91cb4c76..f1b95a1a 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -138,6 +138,7 @@ public class Globals { //Client connection to server // public static ClientNetworking clientConnection; + public static boolean RUN_DEMO = false; public static boolean RUN_CLIENT = true; public static int clientCharacterID; @@ -526,6 +527,7 @@ public class Globals { assetManager.addModelPathToQueue("Models/basic/geometry/unitsphere_grey.fbx"); assetManager.addModelPathToQueue("Models/basic/geometry/SmallCube.fbx"); assetManager.addModelPathToQueue("Models/basic/geometry/unitcylinder.fbx"); + assetManager.addModelPathToQueue("Models/basic/geometry/unitcapsule.glb"); assetManager.addModelPathToQueue("Models/basic/geometry/unitplane.fbx"); assetManager.addModelPathToQueue("Models/basic/geometry/unitcube.fbx"); imagePlaneModelID = assetManager.registerModel(RenderUtils.createPlaneModel("Shaders/plane/plane.vs", "Shaders/plane/plane.fs")); diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index c8c95f36..400ee7ae 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -11,6 +11,7 @@ import org.ode4j.ode.OdeHelper; import electrosphere.audio.AudioEngine; import electrosphere.audio.VirtualAudioSourceManager; import electrosphere.controls.ControlHandler; +import electrosphere.controls.ControlHandler.ControlsState; import electrosphere.engine.cli.CLIParser; import electrosphere.engine.loadingthreads.LoadingThread; import electrosphere.engine.time.Timekeeper; @@ -22,7 +23,9 @@ import electrosphere.renderer.RenderingEngine; import electrosphere.server.simulation.MacroSimulation; - +/** + * The main class + */ public class Main { @@ -140,6 +143,9 @@ public class Main { Globals.initDefaultGraphicalResources(); ImGuiWindowMacros.initImGuiWindows(); + //inits the controls state of the control handler + Globals.controlHandler.hintUpdateControlState(ControlsState.TITLE_MENU); + //start initial asset loading new Thread(Globals.initialAssetLoadingThread).start(); } @@ -186,7 +192,11 @@ public class Main { //fire off a loading thread for the title menus/screen LoggerInterface.loggerStartup.INFO("Fire off loading thread"); - if(Globals.RUN_CLIENT){ + if(Globals.RUN_DEMO){ + LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_DEMO_MENU); + Globals.loadingThreadsList.add(serverThread); + serverThread.start(); + } else if(Globals.RUN_CLIENT){ LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_TITLE_MENU); Globals.loadingThreadsList.add(serverThread); serverThread.start(); @@ -217,7 +227,6 @@ public class Main { */ public static void mainLoop(long maxFrames){ - double functionTrackTimeStart = 0; //main loop while (running) { @@ -400,6 +409,11 @@ public class Main { } catch (NullPointerException ex){ LoggerInterface.loggerEngine.ERROR("Main frame uncaught NPE", ex); + //after a while, jvm will stop reporting stack traces with errors + //need to explicitly kill the vm if you want to see the stack trace + if(Globals.ENGINE_DEBUG){ + System.exit(1); + } } } diff --git a/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java index d5480c50..06303f4d 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ArenaLoading.java @@ -209,7 +209,7 @@ public class ArenaLoading { // public void simulate(){ // if(i < 100){ // i++; - // CollisionObjUtils.getCollidable(sword).addImpulse(new Impulse(new Vector3d(0,0,1), new Vector3d(-1,0,0), 0.001, Collidable.TYPE_CREATURE)); + // CollisionObjUtils.getCollidable(sword).addImpulse(new Impulse(MathUtils.ORIGIN_VECTOR, new Vector3d(-1,0,0), 0.001, Collidable.TYPE_CREATURE)); // EntityUtils.getPosition(sword).set(1,0.2f,2); // } // }}); diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index 7b518b4a..90a5e094 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -38,6 +38,7 @@ import electrosphere.net.NetUtils; import electrosphere.net.client.ClientNetworking; import electrosphere.renderer.ui.elements.Window; import electrosphere.server.datacell.EntityDataCellMapper; +import electrosphere.util.MathUtils; public class ClientLoading { @@ -55,7 +56,7 @@ public class ClientLoading { WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu()); loadingWindow.setVisible(true); //disable menu input - Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT); + Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT); //initialize the client thread (client) initClientThread(); //while we don't know what races are playable, wait @@ -77,7 +78,7 @@ public class ClientLoading { //log LoggerInterface.loggerEngine.INFO("[Client]Finished loading character creation menu"); //set menu controls again - Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.TITLE_MENU); + Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.TITLE_MENU); } @@ -87,7 +88,7 @@ public class ClientLoading { WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu()); loadingWindow.setVisible(true); //disable menu input - Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT); + Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT); //initialize the "real" objects simulation initClientSimulation(); //init foliage manager @@ -104,8 +105,6 @@ public class ClientLoading { setSimulationsToReady(); //init culling manager and other graphics-focused non-simulation items initEntityCullingManager(); - //hide cursor - Globals.controlHandler.hideMouse(); //make loading window disappear loadingWindow.setVisible(false); //recapture screen @@ -119,7 +118,7 @@ public class ClientLoading { Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; LoggerInterface.loggerEngine.INFO("[Client]Finished loading main game"); //set controls state - Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.MAIN_GAME); + Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.MAIN_GAME); } @@ -177,9 +176,9 @@ public class ClientLoading { */ if(Globals.controlHandler.cameraIsThirdPerson()){ - Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,1)); + Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraEntity(new Vector3f(1,0,1), MathUtils.getOriginVectorf()); } else { - Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraFirstPersonEntity(new Vector3f(1,0,1), new Vector3f(0,0,1)); + Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraFirstPersonEntity(new Vector3f(1,0,1), MathUtils.getOriginVectorf()); } diff --git a/src/main/java/electrosphere/engine/loadingthreads/DemoLoading.java b/src/main/java/electrosphere/engine/loadingthreads/DemoLoading.java new file mode 100644 index 00000000..935a820c --- /dev/null +++ b/src/main/java/electrosphere/engine/loadingthreads/DemoLoading.java @@ -0,0 +1,31 @@ +package electrosphere.engine.loadingthreads; + +import electrosphere.engine.Globals; +import electrosphere.menu.WindowStrings; +import electrosphere.menu.WindowUtils; +import electrosphere.menu.mainmenu.MenuGeneratorsDemo; +import electrosphere.renderer.ui.elements.Window; + +/** + * Loading routines for the demo version of the game + */ +public class DemoLoading { + + //the name of the save for the demo version of the game + public static final String DEMO_LEVEL_PATH = "demo"; + + /** + * Loads the title menu elements for the demo version of the engine + */ + public static void loadDemoMenu(){ + Globals.currentSaveName = DEMO_LEVEL_PATH; + + WindowUtils.replaceMainMenuContents(MenuGeneratorsDemo.createTitleMenu()); + + Window loadingWindow = (Window)Globals.elementManager.getWindow(WindowStrings.WINDOW_LOADING); + WindowUtils.recursiveSetVisible(loadingWindow,false); + WindowUtils.focusWindow(WindowStrings.WINDOW_MENU_MAIN); + WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN), true); + } + +} diff --git a/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java b/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java index 385d2a8f..0ca8dce0 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java +++ b/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java @@ -3,24 +3,30 @@ package electrosphere.engine.loadingthreads; import java.util.concurrent.Semaphore; /** - * - * @author amaterasu + * Threads for loading engine state */ public class LoadingThread extends Thread { - public static final int LOAD_TITLE_MENU = 0; - public static final int LOAD_MAIN_GAME = 1; - public static final int LOAD_ARENA = 2; - public static final int LOAD_CHARACTER_SERVER = 3; - public static final int LOAD_CLIENT_WORLD = 4; - public static final int LOAD_DEBUG_RANDOM_SP_WORLD = 5; - public static final int LOAD_LEVEL_EDITOR = 6; - public static final int LOAD_LEVEL = 7; + public static final int LOAD_TITLE_MENU = 0; //loads the main game title menu + public static final int LOAD_MAIN_GAME = 1; //loads the main game + public static final int LOAD_ARENA = 2; //loads the arena + public static final int LOAD_CHARACTER_SERVER = 3; //loads the character creation menus on the client + public static final int LOAD_CLIENT_WORLD = 4; //loads the client world + public static final int LOAD_DEBUG_RANDOM_SP_WORLD = 5; //loads a random singleplayer debug world + public static final int LOAD_LEVEL_EDITOR = 6; //loads the level editor + public static final int LOAD_LEVEL = 7; //loads a level + public static final int LOAD_DEMO_MENU = 8; //loads the main menu ui for the demo version of the client + //the type of loading to do int threadType; + //a lock to track when the loading had completed and block until then Semaphore lock; + /** + * Creates the work for a loading thread + * @param type The type of thread + */ public LoadingThread(int type){ threadType = type; lock = new Semaphore(1); @@ -52,7 +58,7 @@ public class LoadingThread extends Thread { } break; - //intended to act like you went through the steps of setting up a vanilla settings SP world + //intended to act like you went through the steps of setting up a vanilla settings SP world case LOAD_DEBUG_RANDOM_SP_WORLD: { DebugSPWorldLoading.loadDebugSPWorld(); } break; @@ -66,12 +72,20 @@ public class LoadingThread extends Thread { case LOAD_LEVEL: { LevelLoading.loadLevel(); } break; + + //the demo menu ui + case LOAD_DEMO_MENU: { + DemoLoading.loadDemoMenu(); + } break; } lock.release(); } - + /** + * Checks if the thread has finished loading + * @return true if it has finished, false otherwise + */ public boolean isDone(){ boolean rVal = lock.tryAcquire(); if(rVal == true){ diff --git a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java index 4220128f..8ef711f8 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java +++ b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java @@ -198,8 +198,15 @@ public class LoadingUtils { } + /** + * Loads graphics assets necessary for the client of the game engine. This should be stuff that is used essentially universally (ie textures for debugging). + */ static void initGameGraphicalEntities(){ + Globals.assetManager.addTexturePathtoQueue("Textures/transparent_red.png"); + Globals.assetManager.addTexturePathtoQueue("Textures/transparent_blue.png"); + Globals.assetManager.addTexturePathtoQueue("Textures/transparent_grey.png"); + float skyR = 100; float skyG = 150; diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index 207f332e..19cacca8 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -256,7 +256,6 @@ public class EntityDataStrings { /* Equip state */ - public static final String EQUIP_STATE = "equipState"; public static final String TREE_CLIENTEQUIPSTATE = "treeClientEquipState"; public static final String EQUIP_INVENTORY = "equipInventory"; public static final String TREE_SERVEREQUIPSTATE = "treeServerEquipState"; diff --git a/src/main/java/electrosphere/entity/EntityUtils.java b/src/main/java/electrosphere/entity/EntityUtils.java index 2b594e63..6352f4eb 100644 --- a/src/main/java/electrosphere/entity/EntityUtils.java +++ b/src/main/java/electrosphere/entity/EntityUtils.java @@ -8,6 +8,7 @@ import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.datacell.utils.EntityLookupUtils; import electrosphere.server.poseactor.PoseActor; import electrosphere.server.poseactor.PoseActorUtils; +import electrosphere.util.MathUtils; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -62,7 +63,7 @@ public class EntityUtils { Entity rVal = new Entity(); rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorOfLoadingModel(modelPath)); rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0)); - rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, new Vector3d(1,0,0))); + rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, MathUtils.getOriginVector())); rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1)); rVal.putData(EntityDataStrings.DATA_STRING_DRAW, true); rVal.putData(EntityDataStrings.DRAW_SOLID_PASS, true); @@ -92,7 +93,7 @@ public class EntityUtils { Entity rVal = new Entity(); rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(modelPath)); rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0)); - rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, new Vector3d(1,0,0))); + rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().rotateAxis(0, MathUtils.getOriginVector())); rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1)); rVal.putData(EntityDataStrings.DATA_STRING_UI_ELEMENT, true); Globals.clientScene.registerEntity(rVal); diff --git a/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java b/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java index 0770cefc..140fbe76 100644 --- a/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java +++ b/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java @@ -11,7 +11,7 @@ import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.client.firstPerson.FirstPersonTree; import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.state.equip.ClientEquipState; -import electrosphere.entity.state.hitbox.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState; import electrosphere.entity.state.rotator.RotatorTree; import electrosphere.entity.types.attach.AttachUtils; import electrosphere.entity.types.collision.CollisionObjUtils; @@ -267,8 +267,8 @@ public class ClientAttackTree implements BehaviorTree { //activate hitboxes List attachedEntities = AttachUtils.getChildrenList(parent); for(Entity currentAttached : attachedEntities){ - if(HitboxState.hasHitboxState(currentAttached)){ - HitboxState currentState = HitboxState.getHitboxState(currentAttached); + if(HitboxCollectionState.hasHitboxState(currentAttached)){ + HitboxCollectionState currentState = HitboxCollectionState.getHitboxState(currentAttached); currentState.setActive(true); } } @@ -277,8 +277,8 @@ public class ClientAttackTree implements BehaviorTree { //deactive hitboxes List attachedEntities = AttachUtils.getChildrenList(parent); for(Entity currentAttached : attachedEntities){ - if(HitboxState.hasHitboxState(currentAttached)){ - HitboxState currentState = HitboxState.getHitboxState(currentAttached); + if(HitboxCollectionState.hasHitboxState(currentAttached)){ + HitboxCollectionState currentState = HitboxCollectionState.getHitboxState(currentAttached); currentState.setActive(false); } } @@ -308,7 +308,7 @@ public class ClientAttackTree implements BehaviorTree { String rVal = null; if(ClientEquipState.hasEquipState(parent)){ ClientEquipState equipState = ClientEquipState.getEquipState(parent); - for(String point : equipState.equippedPoints()){ + for(String point : equipState.getEquippedPoints()){ Entity item = equipState.getEquippedItemAtPoint(point); if(ItemUtils.isWeapon(item)){ attackingPoint = point; diff --git a/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java b/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java index f9482baa..32008038 100644 --- a/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java +++ b/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java @@ -19,7 +19,7 @@ import electrosphere.entity.state.attack.ClientAttackTree.AttackTreeState; import electrosphere.entity.state.client.firstPerson.FirstPersonTree; import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.state.equip.ServerEquipState; -import electrosphere.entity.state.hitbox.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState; import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree; import electrosphere.entity.state.rotator.ServerRotatorTree; import electrosphere.entity.types.attach.AttachUtils; @@ -35,6 +35,7 @@ import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; import electrosphere.renderer.actor.Actor; import electrosphere.server.datacell.Realm; import electrosphere.server.poseactor.PoseActor; +import electrosphere.util.MathUtils; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -139,7 +140,7 @@ public class ServerAttackTree implements BehaviorTree { } } Vector3d movementVector = CreatureUtils.getFacingVector(parent); - EntityUtils.getRotation(parent).rotationTo(new Vector3d(0,0,1), new Vector3d(movementVector.x,movementVector.y,movementVector.z)); + EntityUtils.getRotation(parent).rotationTo(MathUtils.getOriginVector(), new Vector3d(movementVector.x,movementVector.y,movementVector.z)); //set initial stuff (this alerts the client as well) setCurrentMoveId(currentMove.getAttackMoveId()); setState(AttackTreeState.WINDUP); @@ -267,8 +268,8 @@ public class ServerAttackTree implements BehaviorTree { //activate hitboxes List attachedEntities = AttachUtils.getChildrenList(parent); for(Entity currentAttached : attachedEntities){ - if(HitboxState.hasHitboxState(currentAttached)){ - HitboxState currentState = HitboxState.getHitboxState(currentAttached); + if(HitboxCollectionState.hasHitboxState(currentAttached)){ + HitboxCollectionState currentState = HitboxCollectionState.getHitboxState(currentAttached); currentState.setActive(true); } } @@ -299,7 +300,7 @@ public class ServerAttackTree implements BehaviorTree { // EntityUtils.getRotation(currentEntity).set(rotation).normalize(); // Vector3d facingAngle = CreatureUtils.getFacingVector(parent); arrowRotation = parentActor.getBoneRotation(targetBone); - // EntityUtils.getRotation(currentEntity).rotationTo(new Vector3f(0,0,1), new Vector3f((float)facingAngle.x,(float)facingAngle.y,(float)facingAngle.z)).mul(parentActor.getBoneRotation(targetBone)).normalize(); + // EntityUtils.getRotation(currentEntity).rotationTo(MathUtils.ORIGIN_VECTORF, new Vector3f((float)facingAngle.x,(float)facingAngle.y,(float)facingAngle.z)).mul(parentActor.getBoneRotation(targetBone)).normalize(); } Vector3f initialVector = new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z).normalize(); Realm parentRealm = Globals.realmManager.getEntityRealm(parent); @@ -328,8 +329,8 @@ public class ServerAttackTree implements BehaviorTree { //deactive hitboxes List attachedEntities = AttachUtils.getChildrenList(parent); for(Entity currentAttached : attachedEntities){ - if(HitboxState.hasHitboxState(currentAttached)){ - HitboxState currentState = HitboxState.getHitboxState(currentAttached); + if(HitboxCollectionState.hasHitboxState(currentAttached)){ + HitboxCollectionState currentState = HitboxCollectionState.getHitboxState(currentAttached); currentState.setActive(false); } } diff --git a/src/main/java/electrosphere/entity/state/block/ClientBlockTree.java b/src/main/java/electrosphere/entity/state/block/ClientBlockTree.java new file mode 100644 index 00000000..427a03f5 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/block/ClientBlockTree.java @@ -0,0 +1,27 @@ +package electrosphere.entity.state.block; + +import electrosphere.net.synchronization.annotation.SyncedField; +import electrosphere.net.synchronization.annotation.SynchronizableEnum; +import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; + +@SynchronizedBehaviorTree(name = "clientBlockTree", isServer = false, correspondingTree="serverBlockTree") +/** + * Client block tree + */ +public class ClientBlockTree { + + @SynchronizableEnum + /** + * The state of the block tree + */ + public enum BlockState { + BLOCKING, + NOT_BLOCKING, + } + + @SyncedField + BlockState state; + + + +} diff --git a/src/main/java/electrosphere/entity/state/block/ServerBlockTree.java b/src/main/java/electrosphere/entity/state/block/ServerBlockTree.java new file mode 100644 index 00000000..afb31283 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/block/ServerBlockTree.java @@ -0,0 +1,16 @@ +package electrosphere.entity.state.block; + +import electrosphere.entity.state.block.ClientBlockTree.BlockState; +import electrosphere.net.synchronization.annotation.SyncedField; +import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; + +@SynchronizedBehaviorTree(name = "serverBlockTree", isServer = true, correspondingTree="clientBlockTree") +/** + * Server block tree + */ +public class ServerBlockTree { + + @SyncedField + BlockState state; + +} diff --git a/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java b/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java index b5f1c996..4b4eee3c 100644 --- a/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java +++ b/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java @@ -48,7 +48,7 @@ public class ClientEquipState implements BehaviorTree { * @param parent the entity this is attached to * @param equipPoints the list of available points */ - public ClientEquipState(Entity parent, List equipPoints){ + private ClientEquipState(Entity parent, List equipPoints){ this.parent = parent; for(EquipPoint point : equipPoints){ this.equipPoints.add(point); @@ -59,10 +59,19 @@ public class ClientEquipState implements BehaviorTree { * Gets the list of equipped points * @return the list */ - public List equippedPoints(){ + public List getEquippedPoints(){ return new LinkedList(equipMap.keySet()); } + + /** + * Gets the list of all equip points + * @return The list of all equip points + */ + public List getAllEquipPoints(){ + return equipPoints; + } + /** * Attempts to equip the item * @param toEquip the item to equip @@ -221,7 +230,7 @@ public class ClientEquipState implements BehaviorTree { * @return True if the entity contains an equip state, false otherwise */ public static boolean hasEquipState(Entity entity){ - return entity.containsKey(EntityDataStrings.EQUIP_STATE); + return entity.containsKey(EntityDataStrings.TREE_CLIENTEQUIPSTATE); } /** @@ -230,7 +239,7 @@ public class ClientEquipState implements BehaviorTree { * @return The equip state on the entity */ public static ClientEquipState getEquipState(Entity entity){ - return (ClientEquipState)entity.getData(EntityDataStrings.EQUIP_STATE); + return (ClientEquipState)entity.getData(EntityDataStrings.TREE_CLIENTEQUIPSTATE); } /** @@ -239,7 +248,7 @@ public class ClientEquipState implements BehaviorTree { * @param equipState The equip state to attach */ public static void setEquipState(Entity entity, ClientEquipState equipState){ - entity.putData(EntityDataStrings.EQUIP_STATE, equipState); + entity.putData(EntityDataStrings.TREE_CLIENTEQUIPSTATE, equipState); } // public void drop(Entity entity){ diff --git a/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java b/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java index b4f8374b..b2cc1752 100644 --- a/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java +++ b/src/main/java/electrosphere/entity/state/equip/ServerEquipState.java @@ -190,7 +190,7 @@ public class ServerEquipState implements BehaviorTree { * @return True if the entity contains an equip state, false otherwise */ public static boolean hasEquipState(Entity entity){ - return entity.containsKey(EntityDataStrings.EQUIP_STATE); + return entity.containsKey(EntityDataStrings.TREE_SERVEREQUIPSTATE); } /** @@ -199,7 +199,7 @@ public class ServerEquipState implements BehaviorTree { * @return The equip state on the entity */ public static ServerEquipState getEquipState(Entity entity){ - return (ServerEquipState)entity.getData(EntityDataStrings.EQUIP_STATE); + return (ServerEquipState)entity.getData(EntityDataStrings.TREE_SERVEREQUIPSTATE); } /** @@ -208,7 +208,7 @@ public class ServerEquipState implements BehaviorTree { * @param equipState The equip state to attach */ public static void setEquipState(Entity entity, ServerEquipState equipState){ - entity.putData(EntityDataStrings.EQUIP_STATE, equipState); + entity.putData(EntityDataStrings.TREE_SERVEREQUIPSTATE, equipState); } // public void drop(Entity entity){ diff --git a/src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java b/src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java index 5819f378..e3fd74c8 100644 --- a/src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java +++ b/src/main/java/electrosphere/entity/state/foliage/AmbientFoliage.java @@ -52,7 +52,7 @@ public class AmbientFoliage implements BehaviorTree { //rotate to face cameras // Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera); - // EntityUtils.getRotation(parent).rotateTo(new Vector3d(1,0,0), new Vector3d(cameraEyeVector)); + // EntityUtils.getRotation(parent).rotateTo(MathUtils.ORIGIN_VECTOR, new Vector3d(cameraEyeVector)); //TODO: simulate wind offset diff --git a/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java new file mode 100644 index 00000000..6666eee4 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java @@ -0,0 +1,594 @@ +package electrosphere.entity.state.hitbox; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.joml.Quaterniond; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.ode4j.ode.DBody; +import org.ode4j.ode.DGeom; + +import electrosphere.collision.CollisionBodyCreation; +import electrosphere.collision.CollisionEngine; +import electrosphere.collision.PhysicsEntityUtils; +import electrosphere.collision.PhysicsUtils; +import electrosphere.collision.collidable.Collidable; +import electrosphere.collision.hitbox.HitboxManager; +import electrosphere.collision.hitbox.HitboxUtils.HitboxPositionCallback; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState.HitboxShapeType; +import electrosphere.game.data.collidable.HitboxData; +import electrosphere.util.MathUtils; + +/** + * The state of the collection of all hitboxes on this entity + * Ie, it stores the state of each hitbox that is attached to this entity + */ +public class HitboxCollectionState { + + /** + * Types of hitboxes + */ + public enum HitboxType { + HIT, // damages another entity + HURT, // receives damage from another entity + BLOCK, // blocks a hit from another entity + } + + //the parent entity of the hitbox state + Entity parent; + + //the body that contains all the hitbox shapes + DBody body; + + //The collidable associated with the body + Collidable collidable; + + //the list of all geoms in the collection state + List geoms = new LinkedList(); + //the map of bone -> hitbox shape in ode4j + Map hitboxGeomMap = new HashMap(); + //the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision + Map geomStateMap = new HashMap(); + + //callback to provide a position for the hitbox each frame + HitboxPositionCallback positionCallback; + + //controls whether the hitbox state is active or not + boolean active = true; + + //the associated manager + HitboxManager manager; + + //controls whether this hitbox collection thinks its on the server or client + boolean isServer = true; + + + /** + * Create hitbox state for an entity + * @param collisionEngine the collision engine + * @param entity The entity to attach the state to + * @param hitboxListRaw The list of hitbox data to apply + * @return The hitbox state that has been attached to the entity + */ + public static HitboxCollectionState attachHitboxState(HitboxManager manager, boolean isServer, Entity entity, List hitboxListRaw){ + HitboxCollectionState rVal = new HitboxCollectionState(); + + rVal.isServer = isServer; + //create the shapes + for(HitboxData hitboxDataRaw : hitboxListRaw){ + DGeom geom = null; + HitboxType type = HitboxType.HIT; + HitboxShapeType shapeType = HitboxShapeType.SPHERE; + switch(hitboxDataRaw.getType()){ + case HitboxData.HITBOX_TYPE_HIT: { + type = HitboxType.HIT; + shapeType = HitboxShapeType.SPHERE; + geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_HURT: { + type = HitboxType.HURT; + shapeType = HitboxShapeType.SPHERE; + geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_HIT_CONNECTED: { + type = HitboxType.HIT; + shapeType = HitboxShapeType.CAPSULE; + geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_HURT_CONNECTED: { + type = HitboxType.HURT; + shapeType = HitboxShapeType.CAPSULE; + geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); + } break; + case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: { + type = HitboxType.HURT; + shapeType = HitboxShapeType.STATIC_CAPSULE; + geom = CollisionBodyCreation.createCapsuleShape(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), hitboxDataRaw.getLength(), Collidable.TYPE_OBJECT_BIT); + } break; + } + if(hitboxDataRaw.getBone() != null){ + rVal.hitboxGeomMap.put(hitboxDataRaw.getBone(),geom); + } + rVal.geoms.add(geom); + rVal.geomStateMap.put(geom,new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, shapeType, true)); + } + + //create body with all the shapes + DGeom[] geomArray = rVal.geoms.toArray(new DGeom[rVal.geoms.size()]); + rVal.body = CollisionBodyCreation.createBodyWithShapes(manager.getCollisionEngine(), geomArray); + + //register collidable with collision engine + Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT); + manager.getCollisionEngine().registerCollisionObject(rVal.body, collidable); + + //attach + entity.putData(EntityDataStrings.HITBOX_DATA, rVal); + rVal.parent = entity; + + //register + manager.registerHitbox(rVal); + rVal.manager = manager; + + return rVal; + } + + /** + * Create hitbox state for an entity + * @param collisionEngine the collision engine + * @param entity The entity to attach the state to + * @param data The hitbox data to apply + * @param callback The callback that provides a position for the hitbox each frame + * @return The hitbox state that has been attached to the entity + */ + public static HitboxCollectionState attachHitboxStateWithCallback(HitboxManager manager, CollisionEngine collisionEngine, Entity entity, HitboxData data, HitboxPositionCallback callback){ + HitboxCollectionState rVal = new HitboxCollectionState(); + + //create the shapes + rVal.hitboxGeomMap.put(data.getBone(),CollisionBodyCreation.createShapeSphere(collisionEngine, data.getRadius(), Collidable.TYPE_OBJECT_BIT)); + + //create body with all the shapes + DGeom[] geomArray = rVal.hitboxGeomMap.values().toArray(new DGeom[rVal.hitboxGeomMap.values().size()]); + rVal.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray); + + //register collidable with collision engine + Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT); + collisionEngine.registerCollisionObject(rVal.body, collidable); + + //attach + entity.putData(EntityDataStrings.HITBOX_DATA, rVal); + rVal.parent = entity; + + //register + manager.registerHitbox(rVal); + rVal.manager = manager; + + return rVal; + } + + /** + * Clears the collision status of all shapes + */ + public void clearCollisions(){ + for(DGeom geom : this.geoms){ + HitboxState shapeStatus = this.geomStateMap.get(geom); + shapeStatus.setHadCollision(false); + } + } + + /** + * Updates the positions of all hitboxes + */ + public void updateHitboxPositions(CollisionEngine collisionEngine){ + if(parent != null && !isServer && EntityUtils.getActor(parent) != null){ + if(!this.hitboxGeomMap.isEmpty()){ + Vector3d entityPosition = EntityUtils.getPosition(parent); + this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); + for(String boneName : this.hitboxGeomMap.keySet()){ + Vector3f bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName); + DGeom geom = this.hitboxGeomMap.get(boneName); + HitboxState shapeStatus = this.geomStateMap.get(geom); + switch(shapeStatus.shapeType){ + case SPHERE: { + this.updateSphereShapePosition(collisionEngine,boneName,bonePosition); + } break; + case CAPSULE: { + this.updateCapsuleShapePosition(collisionEngine,boneName,bonePosition); + } break; + case STATIC_CAPSULE: { + } break; + } + } + } else if(positionCallback != null){ + DGeom geom = body.getGeomIterator().next(); + Vector3d worldPosition = this.positionCallback.getPosition(); + Quaterniond rotation = new Quaterniond().identity(); + + PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); + } + } else if(parent != null && isServer && EntityUtils.getPoseActor(parent) != null){ + if(!this.hitboxGeomMap.isEmpty()){ + Vector3d entityPosition = EntityUtils.getPosition(parent); + this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); + for(String boneName : this.hitboxGeomMap.keySet()){ + Vector3f bonePosition = EntityUtils.getPoseActor(parent).getBonePosition(boneName); + DGeom geom = this.hitboxGeomMap.get(boneName); + HitboxState shapeStatus = this.geomStateMap.get(geom); + switch(shapeStatus.shapeType){ + case SPHERE: { + this.updateSphereShapePosition(collisionEngine,boneName,bonePosition); + } break; + case CAPSULE: { + this.updateCapsuleShapePosition(collisionEngine,boneName,bonePosition); + } break; + case STATIC_CAPSULE: { + } break; + } + } + } else if(positionCallback != null){ + DGeom geom = body.getGeomIterator().next(); + Vector3d worldPosition = this.positionCallback.getPosition(); + Quaterniond rotation = new Quaterniond().identity(); + + PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); + } + } else if(parent != null && isServer){ + for(DGeom geom : this.geoms){ + HitboxState shapeStatus = this.geomStateMap.get(geom); + switch(shapeStatus.shapeType){ + case SPHERE: { + } break; + case CAPSULE: { + } break; + case STATIC_CAPSULE: { + this.updateStaticCapsulePosition(collisionEngine, geom, shapeStatus); + } break; + } + } + } + } + + /** + * Updates the position of the geom for a static capsule + * @param collisionEngine The collision engine + * @param boneName The name of the bone the static capsule is attached to + * @param bonePosition The position of the bone + */ + private void updateStaticCapsulePosition(CollisionEngine collisionEngine, DGeom geom, HitboxState shapeStatus){ + Vector3d parentPos = EntityUtils.getPosition(parent); + PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, parentPos, new Quaterniond(0.707,0,0,0.707)); + } + + /** + * Updates the position of a sphere-shape-type hitbox + * @param collisionEngine The collision engine + * @param boneName The name of the bone + * @param bonePosition the position of the bone + */ + private void updateSphereShapePosition(CollisionEngine collisionEngine, String boneName, Vector3f bonePosition){ + DGeom geom = this.hitboxGeomMap.get(boneName); + Quaterniond parentRotation = EntityUtils.getRotation(parent); + Vector3f positionScale = EntityUtils.getScale(parent); + Vector3d worldPosition = new Vector3d(); + Vector3d parentPos = EntityUtils.getPosition(parent); + Quaterniond rotation = new Quaterniond(parentRotation); + + //calculate new world pos + worldPosition.set(bonePosition.x,bonePosition.y,bonePosition.z); + worldPosition = worldPosition.mul(positionScale); + worldPosition = worldPosition.rotate(rotation); + worldPosition.add(new Vector3f((float)parentPos.x,(float)parentPos.y,(float)parentPos.z)); + + PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); + } + + /** + * Updates the position of a capsule-shape hitbox + * @param collisionEngine + * @param boneName + * @param bonePosition + */ + private void updateCapsuleShapePosition(CollisionEngine collisionEngine, String boneName, Vector3f bonePosition){ + DGeom geom = this.hitboxGeomMap.get(boneName); + HitboxState shapeStatus = this.geomStateMap.get(geom); + Quaterniond parentRotation = EntityUtils.getRotation(parent); + Vector3f positionScale = EntityUtils.getScale(parent); + Vector3d worldPosition = new Vector3d(); + Vector3d parentPos = EntityUtils.getPosition(parent); + Quaterniond rotation = new Quaterniond(parentRotation); + Vector3d previousWorldPos = shapeStatus.getPreviousWorldPos(); + + //calculate new world pos + worldPosition.set(bonePosition.x,bonePosition.y,bonePosition.z); + worldPosition = worldPosition.mul(positionScale); + worldPosition = worldPosition.rotate(rotation); + worldPosition.add(new Vector3f((float)parentPos.x,(float)parentPos.y,(float)parentPos.z)); + double length = shapeStatus.getHitboxData().getRadius(); + double radius = shapeStatus.getHitboxData().getRadius(); + + if(previousWorldPos != null){ + //called all subsequent updates to hitbox position + + //destroy old capsule + this.geomStateMap.remove(geom); + this.geoms.remove(geom); + CollisionBodyCreation.destroyShape(collisionEngine, geom); + + //calculate position between new world point and old world point + Vector3d bodyPosition = new Vector3d(worldPosition).lerp(previousWorldPos, 0.5); + + //calculate rotation from old position to new position + //the second quaternion is a rotation along the x axis. This is used to put the hitbox rotation into ode's space + //ode is Z-axis-up + rotation = MathUtils.calculateRotationFromPointToPoint(previousWorldPos,worldPosition).mul(new Quaterniond(0,0,0.707,0.707)); + + //create new capsule + length = previousWorldPos.distance(worldPosition) / 2.0; + if(length > 5000 || Double.isNaN(length) || Double.isInfinite(length) || length <= 0){ + length = 0.1; + System.out.println("HitboxState --- THIS IS NAN WHEN YOU SPAWN A KATANA BECAUSE THE BONE POSITION IS NAN???"); + } + geom = CollisionBodyCreation.createCapsuleShape(manager.getCollisionEngine(), shapeStatus.getHitboxData().getRadius(), length, Collidable.TYPE_OBJECT_BIT); + CollisionBodyCreation.attachGeomToBody(collisionEngine,body,geom); + PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, bodyPosition, rotation); + } else { + //called first time the hitbox updates position + this.geomStateMap.remove(geom); + this.geoms.remove(geom); + CollisionBodyCreation.destroyShape(collisionEngine, geom); + + //create new capsule + geom = CollisionBodyCreation.createCapsuleShape(manager.getCollisionEngine(), shapeStatus.getHitboxData().getRadius(), length, Collidable.TYPE_OBJECT_BIT); + CollisionBodyCreation.attachGeomToBody(collisionEngine,body,geom); + } + //update maps and other variables for next frame + this.hitboxGeomMap.put(boneName,geom); + this.geomStateMap.put(geom,shapeStatus); + this.geoms.add(geom); + shapeStatus.setPreviousWorldPos(worldPosition); + } + + /** + * Gets the status of a shape in the hitbox object + * @param geom The geometry that is the shape within the hitbox data + * @return The status of the shape + */ + public HitboxState getShapeStatus(DGeom geom){ + return this.geomStateMap.get(geom); + } + + /** + * Gets the hitbox state of the entity + * @param entity the entity + * @return the hitbox state if it exists + */ + public static HitboxCollectionState getHitboxState(Entity entity){ + return (HitboxCollectionState)entity.getData(EntityDataStrings.HITBOX_DATA); + } + + /** + * Checks whether the entity has hitbox state or not + * @param entity the entity to check + * @return true if there is hitbox state, false otherwise + */ + public static boolean hasHitboxState(Entity entity){ + return entity.containsKey(EntityDataStrings.HITBOX_DATA); + } + + /** + * Destroys the hitbox state and removes it from the entity + * @param entity the entity + * @return The hitbox state if it exists, null otherwise + */ + public static HitboxCollectionState destroyHitboxState(Entity entity){ + HitboxCollectionState state = null; + if(hasHitboxState(entity)){ + state = getHitboxState(entity); + state.manager.deregisterHitbox(state); + } + return state; + } + + /** + * Gets whether the hitbox state is active or not + * @return true if active, false otherwise + */ + public boolean isActive(){ + return active; + } + + /** + * Sets the active state of the hitbox + * @param state true to make it active, false otherwise + */ + public void setActive(boolean state){ + this.active = state; + } + + /** + * Gets the list of all DGeoms in the data + * @return the list of all DGeoms + */ + public List getGeometries(){ + return this.geoms; + } + + /** + * Gets the set of bone names in the state data + * @return The set of bone names in the state data + */ + public Set getBones(){ + return this.hitboxGeomMap.keySet(); + } + + /** + * Gets geometry on a single hitbox based on its bone name + * @param boneName the bone name + * @return the hitbox geometry + */ + public DGeom getGeometry(String boneName){ + return this.hitboxGeomMap.get(boneName); + } + + /** + * The status of a single shape inside the overall hitbox data + * IE a single sphere on the overall body + */ + public static class HitboxState { + + /** + * Types of geometry that can be used as individual shapes within a hitbox + */ + public enum HitboxShapeType { + //this is a true sphere. It will teleport every frame to its new position + SPHERE, + //for this one, the shape is a capsule in the collision engine, however + //the capsule is used to have continuity between the last position the hitbox occupied and the current one + CAPSULE, + //this is a true static capsule, it doesn't act as two connected spheres but is instead a capsule that teleports between frames + STATIC_CAPSULE, + } + + //the name of the bone the hitbox is attached to + String boneName; + + //the type of hitbox + HitboxType type; + + //the type of geometry + HitboxShapeType shapeType; + + //controls whether the hitbox is active + boolean isActive; + + //the previous position of this hitbox shape + Vector3d previousWorldPos = null; + + //if true, just had a collision + boolean hadCollision = false; + + //the data of the hitbox + HitboxData data; + + /** + * Creates a status object for a hitbox + * @param boneName The name of the bone the hitbox is attached to, if any + * @param data the hitbox data object + * @param type The type of hitbox + * @param shapeType The type of shape the hitbox is + * @param isActive if the hitbox is active or not + */ + public HitboxState(String boneName, HitboxData data, HitboxType type, HitboxShapeType shapeType, boolean isActive){ + this.boneName = boneName; + this.data = data; + this.type = type; + this.shapeType = shapeType; + this.isActive = isActive; + } + + /** + * Gets the name of the bone the hitbox is attached to + * @return The name of the bone + */ + public String getBoneName(){ + return boneName; + } + + /** + * Sets the name of the bone the hitbox is attached to + * @param boneName The bone name + */ + public void setBoneName(String boneName){ + this.boneName = boneName; + } + + /** + * Gets the hitbox data for this shape + * @return The data + */ + public HitboxData getHitboxData(){ + return this.data; + } + + /** + * Sets the hitbox data for this shape + * @param data The data + */ + public void setHitboxData(HitboxData data){ + this.data = data; + } + + /** + * Gets the type of hitbox + * @return The type + */ + public HitboxType getType(){ + return type; + } + + /** + * Sets the type of hitbox + * @param type The type + */ + public void setType(HitboxType type){ + this.type = type; + } + + /** + * Gets whether the hitbox is active or not + * @return true if active, false otherwise + */ + public boolean isActive(){ + return isActive; + } + + /** + * Sets whether the hitbox is active or not + * @param active true for active, false otherwise + */ + public void setActive(boolean active){ + this.isActive = active; + } + + /** + * Gets the previous world position of this hitbox + * @return The previous world position + */ + public Vector3d getPreviousWorldPos(){ + return this.previousWorldPos; + } + + /** + * sets the previous world position of this hitbox shape + * @param previousWorldPos The previous world position + */ + public void setPreviousWorldPos(Vector3d previousWorldPos){ + this.previousWorldPos = previousWorldPos; + } + + /** + * Sets the status of whether this hitbox just had a collision or not + * @param hadCollision true if had a collision, false otherwise + */ + public void setHadCollision(boolean hadCollision){ + this.hadCollision = hadCollision; + } + + /** + * Gets the collision status of the hitbox + * @return true if had a collision, false otherwise + */ + public boolean getHadCollision(){ + return this.hadCollision; + } + + + } + +} diff --git a/src/main/java/electrosphere/entity/state/hitbox/HitboxState.java b/src/main/java/electrosphere/entity/state/hitbox/HitboxState.java deleted file mode 100644 index 427cfb4f..00000000 --- a/src/main/java/electrosphere/entity/state/hitbox/HitboxState.java +++ /dev/null @@ -1,370 +0,0 @@ -package electrosphere.entity.state.hitbox; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.joml.Quaterniond; -import org.joml.Vector3d; -import org.joml.Vector3f; -import org.ode4j.ode.DBody; -import org.ode4j.ode.DGeom; - -import electrosphere.collision.CollisionBodyCreation; -import electrosphere.collision.CollisionEngine; -import electrosphere.collision.PhysicsEntityUtils; -import electrosphere.collision.PhysicsUtils; -import electrosphere.collision.collidable.Collidable; -import electrosphere.collision.hitbox.HitboxManager; -import electrosphere.collision.hitbox.HitboxUtils.HitboxPositionCallback; -import electrosphere.entity.Entity; -import electrosphere.entity.EntityDataStrings; -import electrosphere.entity.EntityUtils; -import electrosphere.game.data.collidable.HitboxData; - -/** - * The hitbox state of this entity - */ -public class HitboxState { - - /** - * Types of hitboxes - */ - public enum HitboxType { - HIT, // damages another entity - HURT, // receives damage from another entity - BLOCK, // blocks a hit from another entity - } - - //the parent entity of the hitbox state - Entity parent; - - //the body that contains all the hitbox shapes - DBody body; - - //The collidable associated with the body - Collidable collidable; - - //the map of bone -> hitbox shape in ode4j - Map hitboxGeomMap = new HashMap(); - //the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision - Map geomBoneMap = new HashMap(); - - //callback to provide a position for the hitbox each frame - HitboxPositionCallback positionCallback; - - //controls whether the hitbox state is active or not - boolean active = true; - - //the associated manager - HitboxManager manager; - - - /** - * Create hitbox state for an entity - * @param collisionEngine the collision engine - * @param entity The entity to attach the state to - * @param hitboxListRaw The list of hitbox data to apply - * @return The hitbox state that has been attached to the entity - */ - public static HitboxState attachHitboxState(HitboxManager manager, Entity entity, List hitboxListRaw){ - HitboxState rVal = new HitboxState(); - - //create the shapes - for(HitboxData hitboxDataRaw : hitboxListRaw){ - DGeom geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT); - rVal.hitboxGeomMap.put(hitboxDataRaw.getBone(),geom); - HitboxType type = HitboxType.HIT; - switch(hitboxDataRaw.getType()){ - case "HIT": { - type = HitboxType.HIT; - } break; - case "HURT": { - type = HitboxType.HURT; - } break; - } - rVal.geomBoneMap.put(geom,new HitboxShapeStatus(hitboxDataRaw.getBone(), type, true)); - } - - //create body with all the shapes - DGeom[] geomArray = rVal.hitboxGeomMap.values().toArray(new DGeom[rVal.hitboxGeomMap.values().size()]); - rVal.body = CollisionBodyCreation.createBodyWithShapes(manager.getCollisionEngine(), geomArray); - - //register collidable with collision engine - Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT); - manager.getCollisionEngine().registerCollisionObject(rVal.body, collidable); - - //attach - entity.putData(EntityDataStrings.HITBOX_DATA, rVal); - rVal.parent = entity; - - //register - manager.registerHitbox(rVal); - rVal.manager = manager; - - return rVal; - } - - /** - * Create hitbox state for an entity - * @param collisionEngine the collision engine - * @param entity The entity to attach the state to - * @param data The hitbox data to apply - * @param callback The callback that provides a position for the hitbox each frame - * @return The hitbox state that has been attached to the entity - */ - public static HitboxState attachHitboxStateWithCallback(HitboxManager manager, CollisionEngine collisionEngine, Entity entity, HitboxData data, HitboxPositionCallback callback){ - HitboxState rVal = new HitboxState(); - - //create the shapes - rVal.hitboxGeomMap.put(data.getBone(),CollisionBodyCreation.createShapeSphere(collisionEngine, data.getRadius(), Collidable.TYPE_OBJECT_BIT)); - - //create body with all the shapes - DGeom[] geomArray = rVal.hitboxGeomMap.values().toArray(new DGeom[rVal.hitboxGeomMap.values().size()]); - rVal.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray); - - //register collidable with collision engine - Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT); - collisionEngine.registerCollisionObject(rVal.body, collidable); - - //attach - entity.putData(EntityDataStrings.HITBOX_DATA, rVal); - rVal.parent = entity; - - //register - manager.registerHitbox(rVal); - rVal.manager = manager; - - return rVal; - } - - /** - * Updates the positions of all hitboxes - */ - public void updateHitboxPositions(CollisionEngine collisionEngine){ - if(parent != null && EntityUtils.getActor(parent) != null){ - if(!this.hitboxGeomMap.isEmpty()){ - Vector3d entityPosition = EntityUtils.getPosition(parent); - this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); - for(String boneName : this.hitboxGeomMap.keySet()){ - DGeom geom = this.hitboxGeomMap.get(boneName); - Quaterniond parentRotation = EntityUtils.getRotation(parent); - Vector3f positionScale = EntityUtils.getScale(parent); - Vector3d worldPosition = new Vector3d(); - Vector3f bonePosition = EntityUtils.getActor(parent).getBonePosition(boneName); - Vector3d parentPos = EntityUtils.getPosition(parent); - worldPosition.set(bonePosition.x,bonePosition.y,bonePosition.z); - Quaterniond rotation = new Quaterniond(parentRotation); - - worldPosition = worldPosition.mul(positionScale); - - worldPosition = worldPosition.rotate(rotation); - - - worldPosition.add(new Vector3f((float)parentPos.x,(float)parentPos.y,(float)parentPos.z)); - - PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); - } - } else if(positionCallback != null){ - DGeom geom = body.getGeomIterator().next(); - Vector3d worldPosition = this.positionCallback.getPosition(); - Quaterniond rotation = new Quaterniond().identity(); - - PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); - } - } else if(parent != null && EntityUtils.getPoseActor(parent) != null){ - if(!this.hitboxGeomMap.isEmpty()){ - Vector3d entityPosition = EntityUtils.getPosition(parent); - this.body.setPosition(PhysicsUtils.jomlVecToOdeVec(entityPosition)); - for(String boneName : this.hitboxGeomMap.keySet()){ - DGeom geom = this.hitboxGeomMap.get(boneName); - Quaterniond parentRotation = EntityUtils.getRotation(parent); - Vector3f positionScale = EntityUtils.getScale(parent); - Vector3d worldPosition = new Vector3d(); - Vector3f bonePosition = EntityUtils.getPoseActor(parent).getBonePosition(boneName); - Vector3d parentPos = EntityUtils.getPosition(parent); - worldPosition.set(bonePosition.x,bonePosition.y,bonePosition.z); - Quaterniond rotation = new Quaterniond(parentRotation); - - worldPosition = worldPosition.mul(positionScale); - - worldPosition = worldPosition.rotate(rotation); - - - worldPosition.add(new Vector3f((float)parentPos.x,(float)parentPos.y,(float)parentPos.z)); - - PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); - } - } else if(positionCallback != null){ - DGeom geom = body.getGeomIterator().next(); - Vector3d worldPosition = this.positionCallback.getPosition(); - Quaterniond rotation = new Quaterniond().identity(); - - PhysicsEntityUtils.setGeometryPosition(collisionEngine, geom, worldPosition, rotation); - } - } - } - - /** - * Gets the status of a shape in the hitbox object - * @param geom The geometry that is the shape within the hitbox data - * @return The status of the shape - */ - public HitboxShapeStatus getShapeStatus(DGeom geom){ - return this.geomBoneMap.get(geom); - } - - /** - * Gets the hitbox state of the entity - * @param entity the entity - * @return the hitbox state if it exists - */ - public static HitboxState getHitboxState(Entity entity){ - return (HitboxState)entity.getData(EntityDataStrings.HITBOX_DATA); - } - - /** - * Checks whether the entity has hitbox state or not - * @param entity the entity to check - * @return true if there is hitbox state, false otherwise - */ - public static boolean hasHitboxState(Entity entity){ - return entity.containsKey(EntityDataStrings.HITBOX_DATA); - } - - /** - * Destroys the hitbox state and removes it from the entity - * @param entity the entity - * @return The hitbox state if it exists, null otherwise - */ - public static HitboxState destroyHitboxState(Entity entity){ - HitboxState state = null; - if(hasHitboxState(entity)){ - state = getHitboxState(entity); - state.manager.deregisterHitbox(state); - } - return state; - } - - /** - * Gets whether the hitbox state is active or not - * @return true if active, false otherwise - */ - public boolean isActive(){ - return active; - } - - /** - * Sets the active state of the hitbox - * @param state true to make it active, false otherwise - */ - public void setActive(boolean state){ - this.active = state; - } - - /** - * Gets the collection of all DGeoms in the data - * @return the collection of all DGeoms - */ - public Collection getGeometries(){ - return this.hitboxGeomMap.values(); - } - - /** - * Gets the set of bone names in the state data - * @return The set of bone names in the state data - */ - public Set getBones(){ - return this.hitboxGeomMap.keySet(); - } - - /** - * Gets geometry on a single hitbox based on its bone name - * @param boneName the bone name - * @return the hitbox geometry - */ - public DGeom getGeometry(String boneName){ - return this.hitboxGeomMap.get(boneName); - } - - /** - * The status of a single shape inside the overall hitbox data - * IE a single sphere on the overall body - */ - public static class HitboxShapeStatus { - - //the name of the bone the hitbox is attached to - String boneName; - - //the type of hitbox - HitboxType type; - - //controls whether the hitbox is active - boolean isActive; - - /** - * Creates a status object for a hitbox - * @param boneName The name of the bone the hitbox is attached to, if any - * @param type The type of hitbox - * @param isActive if the hitbox is active or not - */ - public HitboxShapeStatus(String boneName, HitboxType type, boolean isActive){ - this.boneName = boneName; - this.type = type; - this.isActive = isActive; - } - - /** - * Gets the name of the bone the hitbox is attached to - * @return The name of the bone - */ - public String getBoneName(){ - return boneName; - } - - /** - * Sets the name of the bone the hitbox is attached to - * @param boneName The bone name - */ - public void setBoneName(String boneName){ - this.boneName = boneName; - } - - /** - * Gets the type of hitbox - * @return The type - */ - public HitboxType getType(){ - return type; - } - - /** - * Sets the type of hitbox - * @param type The type - */ - public void setType(HitboxType type){ - this.type = type; - } - - /** - * Gets whether the hitbox is active or not - * @return true if active, false otherwise - */ - public boolean isActive(){ - return isActive; - } - - /** - * Sets whether the hitbox is active or not - * @param active true for active, false otherwise - */ - public void setActive(boolean active){ - this.isActive = active; - } - - - } - -} diff --git a/src/main/java/electrosphere/entity/state/idle/IdleTree.java b/src/main/java/electrosphere/entity/state/idle/ClientIdleTree.java similarity index 90% rename from src/main/java/electrosphere/entity/state/idle/IdleTree.java rename to src/main/java/electrosphere/entity/state/idle/ClientIdleTree.java index f5e5cc5f..cc567f65 100644 --- a/src/main/java/electrosphere/entity/state/idle/IdleTree.java +++ b/src/main/java/electrosphere/entity/state/idle/ClientIdleTree.java @@ -28,7 +28,7 @@ import electrosphere.renderer.anim.Animation; /** * Tree for playing an idle animation when an entity isn't doing anything */ -public class IdleTree implements BehaviorTree { +public class ClientIdleTree implements BehaviorTree { @SynchronizableEnum public static enum IdleTreeState { @@ -42,11 +42,19 @@ public class IdleTree implements BehaviorTree { Entity parent; IdleData idleData; - public IdleTree(Entity e){ + /** + * Creates an idle tree + * @param e the entity to attach the tree to + */ + public ClientIdleTree(Entity e){ state = IdleTreeState.IDLE; parent = e; + //check if this is a creature, if so add its idle data CreatureType creatureType = Globals.gameConfigCurrent.getCreatureTypeLoader().getCreature(CreatureUtils.getType(parent)); - idleData = creatureType.getIdleData(); + if(creatureType != null){ + idleData = creatureType.getIdleData(); + } + //TODO: if object, check if object has idle data and add accordingly } /** @@ -139,8 +147,8 @@ public class IdleTree implements BehaviorTree { * @param entity The entity to attach to * @param tree The behavior tree to attach */ - public static IdleTree attachTree(Entity parent){ - IdleTree rVal = new IdleTree(parent); + public static ClientIdleTree attachTree(Entity parent){ + ClientIdleTree rVal = new ClientIdleTree(parent); //put manual code here (setting params, etc) @@ -194,8 +202,8 @@ public class IdleTree implements BehaviorTree { * @param entity the entity * @return The IdleTree */ - public static IdleTree getIdleTree(Entity entity){ - return (IdleTree)entity.getData(EntityDataStrings.TREE_IDLE); + public static ClientIdleTree getIdleTree(Entity entity){ + return (ClientIdleTree)entity.getData(EntityDataStrings.TREE_IDLE); } /** *

Automatically generated

diff --git a/src/main/java/electrosphere/entity/state/idle/ServerIdleTree.java b/src/main/java/electrosphere/entity/state/idle/ServerIdleTree.java index 3b2b11fb..5867cd92 100644 --- a/src/main/java/electrosphere/entity/state/idle/ServerIdleTree.java +++ b/src/main/java/electrosphere/entity/state/idle/ServerIdleTree.java @@ -2,7 +2,7 @@ package electrosphere.entity.state.idle; import electrosphere.entity.state.attack.ClientAttackTree.AttackTreeState; import electrosphere.entity.state.attack.ServerAttackTree; -import electrosphere.entity.state.idle.IdleTree.IdleTreeState; +import electrosphere.entity.state.idle.ClientIdleTree.IdleTreeState; import electrosphere.entity.state.movement.AirplaneMovementTree; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementTreeState; import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree; @@ -12,6 +12,7 @@ import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.types.creature.CreatureUtils; +import electrosphere.game.data.creature.type.CreatureType; import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.SynchronizationMessage; import electrosphere.net.synchronization.BehaviorTreeIdEnums; @@ -39,6 +40,10 @@ public class ServerIdleTree implements BehaviorTree { CopyOnWriteArrayList networkMessageQueue = new CopyOnWriteArrayList(); + /** + * Creates a server idle tree + * @param e The entity to attach it to + */ public ServerIdleTree(Entity e){ state = IdleTreeState.IDLE; parent = e; @@ -68,7 +73,7 @@ public class ServerIdleTree implements BehaviorTree { } public void simulate(float deltaTime){ - PoseActor entityActor = EntityUtils.getPoseActor(parent); + PoseActor poseActor = EntityUtils.getPoseActor(parent); boolean movementTreeIsIdle = movementTreeIsIdle(); @@ -83,12 +88,12 @@ public class ServerIdleTree implements BehaviorTree { //state machine switch(state){ case IDLE: - if(entityActor != null){ + if(poseActor != null){ if( - (!entityActor.isPlayingAnimation() || !entityActor.isPlayingAnimation(Animation.ANIMATION_IDLE_1)) + (!poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(Animation.ANIMATION_IDLE_1)) ){ - entityActor.playAnimation(Animation.ANIMATION_IDLE_1,3); - entityActor.incrementAnimationTime(0.0001); + poseActor.playAnimation(Animation.ANIMATION_IDLE_1,3); + poseActor.incrementAnimationTime(0.0001); } } isIdle = true; @@ -184,7 +189,7 @@ public class ServerIdleTree implements BehaviorTree { */ public void setState(IdleTreeState state){ this.state = state; - int value = IdleTree.getIdleTreeStateEnumAsShort(state); + int value = ClientIdleTree.getIdleTreeStateEnumAsShort(state); DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 7, 9, value)); } diff --git a/src/main/java/electrosphere/entity/state/life/LifeState.java b/src/main/java/electrosphere/entity/state/life/LifeState.java index efdf3714..3dbe341c 100644 --- a/src/main/java/electrosphere/entity/state/life/LifeState.java +++ b/src/main/java/electrosphere/entity/state/life/LifeState.java @@ -5,7 +5,6 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.joml.Vector3d; import electrosphere.engine.Globals; -import electrosphere.engine.Main; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; @@ -17,6 +16,9 @@ import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.renderer.actor.Actor; import electrosphere.server.datacell.utils.DataCellSearchUtils; +/** + * The status of the life value of a given entity + */ public class LifeState implements BehaviorTree { @@ -108,7 +110,6 @@ public class LifeState implements BehaviorTree { public void damage(int damage){ if(!isInvincible){ lifeCurrent = lifeCurrent - damage; - System.out.println(lifeCurrent); isInvincible = true; if(lifeCurrent < 0){ lifeCurrent = 0; diff --git a/src/main/java/electrosphere/entity/state/movement/AirplaneMovementTree.java b/src/main/java/electrosphere/entity/state/movement/AirplaneMovementTree.java index cdc827ec..98ca49b9 100644 --- a/src/main/java/electrosphere/entity/state/movement/AirplaneMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/AirplaneMovementTree.java @@ -20,6 +20,7 @@ import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.anim.Animation; import electrosphere.server.datacell.utils.DataCellSearchUtils; +import electrosphere.util.MathUtils; @Deprecated public class AirplaneMovementTree implements BehaviorTree { @@ -83,7 +84,7 @@ public class AirplaneMovementTree implements BehaviorTree { Actor entityActor = EntityUtils.getActor(parent); Vector3d position = EntityUtils.getPosition(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent); - Quaterniond movementQuaternion = new Quaterniond().rotationTo(new Vector3d(0,0,1), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); + Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); Quaterniond rotation = EntityUtils.getRotation(parent); // //handle network messages @@ -187,15 +188,15 @@ public class AirplaneMovementTree implements BehaviorTree { Quaterniond yawQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(0,1,0), yaw); - Quaterniond pitchQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(1,0,0), pitch); - Quaterniond rollQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(0,0,1), rollVal); + Quaterniond pitchQuat = new Quaterniond().fromAxisAngleRad(MathUtils.getOriginVector(), pitch); + Quaterniond rollQuat = new Quaterniond().fromAxisAngleRad(MathUtils.getOriginVector(), rollVal); rotation.slerp(yawQuat.mul(pitchQuat).mul(rollQuat),0.1); //rotate thrust vector - rotationVector.set(rotation.transform(new Vector3d(0,0,1))); + rotationVector.set(rotation.transform(MathUtils.getOriginVector())); // rotationVector.set(new Vector3f((float)rotationVector.x,(float)rotationVector.y,(float)rotationVector.z).mul(1.0f - this.maxRotationSpeed).add(new Vector3f(cameraEyeVector).mul(-this.maxRotationSpeed))); @@ -211,7 +212,7 @@ public class AirplaneMovementTree implements BehaviorTree { * @param collidable The collidable of the entity */ void addMovementForce(float velocity, Quaterniond rotation, Collidable collidable){ - Vector3d impulseDir = rotation.transform(new Vector3d(0,0,1)); + Vector3d impulseDir = rotation.transform(MathUtils.getOriginVector()); collidable.addImpulse(new Impulse(new Vector3d(impulseDir), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Globals.timekeeper.getSimFrameTime(), "movement")); } diff --git a/src/main/java/electrosphere/entity/state/movement/ServerFallTree.java b/src/main/java/electrosphere/entity/state/movement/ServerFallTree.java index 4af60a03..07af598e 100644 --- a/src/main/java/electrosphere/entity/state/movement/ServerFallTree.java +++ b/src/main/java/electrosphere/entity/state/movement/ServerFallTree.java @@ -5,6 +5,7 @@ import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.renderer.actor.Actor; +import electrosphere.server.poseactor.PoseActor; public class ServerFallTree implements BehaviorTree { @@ -28,17 +29,17 @@ public class ServerFallTree implements BehaviorTree { @Override public void simulate(float deltaTime) { - Actor entityActor = EntityUtils.getActor(parent); + PoseActor poseActor = EntityUtils.getPoseActor(parent); switch(state){ case ACTIVE: - if(entityActor != null){ + if(poseActor != null){ String animationToPlay = determineCorrectAnimation(); if( - !entityActor.isPlayingAnimation() || !entityActor.isPlayingAnimation(animationToPlay) && + !poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay) && (jumpTree == null || !jumpTree.isJumping()) ){ - entityActor.playAnimation(animationToPlay,1); - entityActor.incrementAnimationTime(0.0001); + poseActor.playAnimation(animationToPlay,1); + poseActor.incrementAnimationTime(0.0001); } } break; @@ -58,14 +59,14 @@ public class ServerFallTree implements BehaviorTree { public void land(){ if(state != FallState.INACTIVE){ state = FallState.INACTIVE; - Actor entityActor = EntityUtils.getActor(parent); - if(entityActor != null){ + PoseActor poseActor = EntityUtils.getPoseActor(parent); + if(poseActor != null){ String animationToPlay = determineCorrectAnimation(); if( - !entityActor.isPlayingAnimation() || !entityActor.isPlayingAnimation(animationToPlay) + !poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay) ){ - entityActor.playAnimation(animationToPlay,1); - entityActor.incrementAnimationTime(0.0001); + poseActor.playAnimation(animationToPlay,1); + poseActor.incrementAnimationTime(0.0001); } } } diff --git a/src/main/java/electrosphere/entity/state/movement/ServerJumpTree.java b/src/main/java/electrosphere/entity/state/movement/ServerJumpTree.java index a720ec0d..85015876 100644 --- a/src/main/java/electrosphere/entity/state/movement/ServerJumpTree.java +++ b/src/main/java/electrosphere/entity/state/movement/ServerJumpTree.java @@ -14,6 +14,7 @@ import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.state.gravity.GravityUtils; import electrosphere.entity.types.collision.CollisionObjUtils; import electrosphere.renderer.actor.Actor; +import electrosphere.server.poseactor.PoseActor; public class ServerJumpTree implements BehaviorTree { @@ -53,14 +54,14 @@ public class ServerJumpTree implements BehaviorTree { @Override public void simulate(float deltaTime) { - Actor entityActor = EntityUtils.getActor(parent); + PoseActor poseActor = EntityUtils.getPoseActor(parent); switch(state){ case ACTIVE: - if(entityActor != null){ + if(poseActor != null){ String animationToPlay = determineCorrectAnimation(); - if(!entityActor.isPlayingAnimation() || !entityActor.isPlayingAnimation(animationToPlay)){ - entityActor.playAnimation(animationToPlay,1); - entityActor.incrementAnimationTime(0.0001); + if(!poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay)){ + poseActor.playAnimation(animationToPlay,1); + poseActor.incrementAnimationTime(0.0001); } } currentFrame++; diff --git a/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java b/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java index f1843113..85362dfb 100644 --- a/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java @@ -26,6 +26,7 @@ import electrosphere.net.synchronization.annotation.SyncedField; import electrosphere.net.synchronization.annotation.SynchronizableEnum; import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; import electrosphere.renderer.anim.Animation; +import electrosphere.util.MathUtils; import electrosphere.renderer.actor.Actor; import java.util.concurrent.CopyOnWriteArrayList; @@ -217,7 +218,7 @@ public class ClientGroundMovementTree implements BehaviorTree { break; } // float movementYaw = CameraEntityUtils.getCameraYaw(Globals.playerCamera); - Quaterniond movementQuaternion = new Quaterniond().rotationTo(new Vector3d(0,0,1), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); + Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); Quaterniond rotation = EntityUtils.getRotation(parent); //TODO: optimize away and document (I know for the moment if this exception isn't here it will bite me in the ass later) if(facingVector.length() == 0){ @@ -258,7 +259,6 @@ public class ClientGroundMovementTree implements BehaviorTree { //this should only fire on the client, we don't want the server snap updating due to client position reporting lastServerPosition = new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ()); if(position.distance(lastServerPosition) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){ - System.out.println(position + " - " + lastServerPosition); EntityUtils.getPosition(parent).set(lastServerPosition); } else if(position.distance(lastServerPosition) > STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD){ EntityUtils.getPosition(parent).lerp(lastServerPosition,SOFT_UPDATE_MULTIPLIER); diff --git a/src/main/java/electrosphere/entity/state/movement/groundmove/ServerGroundMovementTree.java b/src/main/java/electrosphere/entity/state/movement/groundmove/ServerGroundMovementTree.java index e0200f35..d2ff7452 100644 --- a/src/main/java/electrosphere/entity/state/movement/groundmove/ServerGroundMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/groundmove/ServerGroundMovementTree.java @@ -28,6 +28,7 @@ import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; import electrosphere.renderer.anim.Animation; import electrosphere.server.datacell.utils.DataCellSearchUtils; import electrosphere.server.poseactor.PoseActor; +import electrosphere.util.MathUtils; import java.util.concurrent.CopyOnWriteArrayList; @@ -153,7 +154,7 @@ public class ServerGroundMovementTree implements BehaviorTree { acceleration = CreatureUtils.getAcceleration(parent); maxNaturalVelocity = sprintTree != null && sprintTree.getState() == SprintTreeState.SPRINTING ? sprintTree.getMaxVelocity() : CreatureUtils.getMaxNaturalVelocity(parent); } - PoseActor entityPoseActor = EntityUtils.getPoseActor(parent); + PoseActor poseActor = EntityUtils.getPoseActor(parent); // Model entityModel = Globals.assetManager.fetchModel(EntityUtils.getEntityModelPath(parent)); Vector3d position = EntityUtils.getPosition(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent); @@ -189,7 +190,7 @@ public class ServerGroundMovementTree implements BehaviorTree { break; } // float movementYaw = CameraEntityUtils.getCameraYaw(Globals.playerCamera); - Quaterniond movementQuaternion = new Quaterniond().rotationTo(new Vector3d(0,0,1), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); + Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); Quaterniond rotation = EntityUtils.getRotation(parent); //TODO: optimize away and document (I know for the moment if this exception isn't here it will bite me in the ass later) if(facingVector.length() == 0){ @@ -233,15 +234,15 @@ public class ServerGroundMovementTree implements BehaviorTree { //state machine switch(state){ case STARTUP: { - if(entityPoseActor != null){ + if(poseActor != null){ String animationToPlay = determineCorrectAnimation(); if( - !entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationToPlay) && + !poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay) && (jumpTree == null || !jumpTree.isJumping()) && (fallTree == null || !fallTree.isFalling()) ){ - entityPoseActor.playAnimation(animationToPlay,1); - entityPoseActor.incrementAnimationTime(0.0001); + poseActor.playAnimation(animationToPlay,1); + poseActor.incrementAnimationTime(0.0001); } } //run startup code @@ -289,15 +290,15 @@ public class ServerGroundMovementTree implements BehaviorTree { case MOVE: { //check if can restart animation //if yes, restart animation - if(entityPoseActor != null){ + if(poseActor != null){ String animationToPlay = determineCorrectAnimation(); if( - !entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationToPlay) && + !poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay) && (jumpTree == null || !jumpTree.isJumping()) && (fallTree == null || !fallTree.isFalling()) ){ - entityPoseActor.playAnimation(animationToPlay,1); - entityPoseActor.incrementAnimationTime(0.0001); + poseActor.playAnimation(animationToPlay,1); + poseActor.incrementAnimationTime(0.0001); } } if(velocity != maxNaturalVelocity){ @@ -339,15 +340,15 @@ public class ServerGroundMovementTree implements BehaviorTree { } break; case SLOWDOWN: { //run slowdown code - if(entityPoseActor != null){ + if(poseActor != null){ String animationToPlay = determineCorrectAnimation(); if( - !entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationToPlay) && + !poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay) && (jumpTree == null || !jumpTree.isJumping()) && (fallTree == null || !fallTree.isFalling()) ){ - entityPoseActor.playAnimation(animationToPlay,1); - entityPoseActor.incrementAnimationTime(0.0001); + poseActor.playAnimation(animationToPlay,1); + poseActor.incrementAnimationTime(0.0001); } } //velocity stuff @@ -356,10 +357,10 @@ public class ServerGroundMovementTree implements BehaviorTree { if(velocity <= 0){ velocity = 0; state = MovementTreeState.IDLE; - if(entityPoseActor != null){ + if(poseActor != null){ String animationToPlay = determineCorrectAnimation(); - if(entityPoseActor.isPlayingAnimation() && entityPoseActor.isPlayingAnimation(animationToPlay)){ - entityPoseActor.stopAnimation(animationToPlay); + if(poseActor.isPlayingAnimation() && poseActor.isPlayingAnimation(animationToPlay)){ + poseActor.stopAnimation(animationToPlay); } } } diff --git a/src/main/java/electrosphere/entity/types/attach/AttachUtils.java b/src/main/java/electrosphere/entity/types/attach/AttachUtils.java index 1d67fa18..35affb4c 100644 --- a/src/main/java/electrosphere/entity/types/attach/AttachUtils.java +++ b/src/main/java/electrosphere/entity/types/attach/AttachUtils.java @@ -10,6 +10,7 @@ import electrosphere.renderer.actor.Actor; import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.datacell.utils.ServerEntityTagUtils; import electrosphere.server.poseactor.PoseActor; +import electrosphere.util.MathUtils; import java.util.LinkedList; import java.util.List; @@ -81,7 +82,7 @@ public class AttachUtils { Vector3d facingAngle = CreatureUtils.getFacingVector(parent); //calculate rotation of model EntityUtils.getRotation(currentEntity) - .rotationTo(new Vector3d(0,0,1), new Vector3d(facingAngle.x,facingAngle.y,facingAngle.z)) + .rotationTo(MathUtils.getOriginVector(), new Vector3d(facingAngle.x,facingAngle.y,facingAngle.z)) .mul(parentActor.getBoneRotation(targetBone)) .mul(offsetRotation) .normalize(); @@ -200,11 +201,11 @@ public class AttachUtils { // EntityUtils.getRotation(currentEntity).set(rotation).normalize(); Vector3d facingAngle = CreatureUtils.getFacingVector(parent); if(facingAngle == null){ - facingAngle = new Vector3d(0,0,1); + facingAngle = MathUtils.getOriginVector(); } //calculate rotation of model EntityUtils.getRotation(currentEntity) - .rotationTo(new Vector3d(0,0,1), new Vector3d(facingAngle.x,facingAngle.y,facingAngle.z)) + .rotationTo(MathUtils.getOriginVector(), new Vector3d(facingAngle.x,facingAngle.y,facingAngle.z)) .mul(parentActor.getBoneRotation(targetBone)) .mul(offsetRotation) .normalize(); @@ -305,6 +306,13 @@ public class AttachUtils { } + /** + * Attaches an entity to another entity at a given bone + * @param parent The parent entity + * @param toAttach The entity that will be attached + * @param boneName The name of the bone + * @param rotation The rotation applied + */ public static void clientAttachEntityToEntityAtBone(Entity parent, Entity toAttach, String boneName, Quaterniond rotation){ Globals.clientSceneWrapper.getScene().registerEntityToTag(toAttach, EntityTags.BONE_ATTACHED); toAttach.putData(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED, true); @@ -488,6 +496,11 @@ public class AttachUtils { // GETTERS // + /** + * Checks whether this entity is attached to another entity or not + * @param e The entity + * @return true if attached, false otherwise + */ public static boolean isAttached(Entity e){ return e.containsKey(EntityDataStrings.ATTACH_ENTITY_IS_ATTACHED); } @@ -500,10 +513,24 @@ public class AttachUtils { return (Entity)e.getData(EntityDataStrings.ATTACH_PARENT); } + /** + * Gets the rotation offset of a given entity + * @param e The entity + * @return The rotation offset + */ protected static Quaterniond getRotationOffset(Entity e){ return (Quaterniond)e.getData(EntityDataStrings.ATTACH_ROTATION_OFFSET); } + /** + * Sets the attached rotation offset + * @param e the attached entity + * @param rotation The rotation offset + */ + public static void setRotationOffset(Entity e, Quaterniond rotation){ + e.putData(EntityDataStrings.ATTACH_ROTATION_OFFSET, rotation); + } + /** * Gets the transform for a transform attached entity * @param e The entity diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java index 05dbb078..ce0b26b2 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java @@ -30,8 +30,8 @@ import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.equip.ServerEquipState; import electrosphere.entity.state.gravity.ClientGravityTree; import electrosphere.entity.state.gravity.ServerGravityTree; -import electrosphere.entity.state.hitbox.HitboxState; -import electrosphere.entity.state.idle.IdleTree; +import electrosphere.entity.state.hitbox.HitboxCollectionState; +import electrosphere.entity.state.idle.ClientIdleTree; import electrosphere.entity.state.idle.ServerIdleTree; import electrosphere.entity.state.inventory.ClientInventoryState; import electrosphere.entity.state.inventory.InventoryUtils; @@ -81,6 +81,7 @@ import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils; import electrosphere.server.datacell.utils.ServerEntityTagUtils; import electrosphere.server.poseactor.PoseActor; import electrosphere.server.poseactor.PoseActorUtils; +import electrosphere.util.MathUtils; import electrosphere.util.Utilities; /** @@ -111,7 +112,7 @@ public class CreatureUtils { /// HITBOX DATA /// /// - HitboxState.attachHitboxState(Globals.clientSceneWrapper.getHitboxManager(), rVal, rawType.getHitboxes()); + HitboxCollectionState.attachHitboxState(Globals.clientSceneWrapper.getHitboxManager(), false, rVal, rawType.getHitboxes()); // @@ -166,7 +167,7 @@ public class CreatureUtils { } //round out end of move system rVal.putData(EntityDataStrings.CLIENT_MOVEMENT_BT, moveTree); - CreatureUtils.setFacingVector(rVal, new Vector3d(0,0,1)); + CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); rVal.putData(EntityDataStrings.DATA_STRING_MAX_NATURAL_VELOCITY, groundMovementSystem.getMaxVelocity()); rVal.putData(EntityDataStrings.DATA_STRING_ACCELERATION, groundMovementSystem.getAcceleration()); rVal.putData(EntityDataStrings.DATA_STRING_VELOCITY, 0f); @@ -218,14 +219,14 @@ public class CreatureUtils { airplaneMovementTree.setMaxRotationSpeed(airplaneMovementSystem.getMaxRotationSpeed()); //register misc stuff rVal.putData(EntityDataStrings.CLIENT_MOVEMENT_BT, airplaneMovementTree); - CreatureUtils.setFacingVector(rVal, new Vector3d(0,0,1)); + CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); Globals.clientScene.registerBehaviorTree(airplaneMovementTree); Globals.clientScene.registerEntityToTag(rVal, EntityTags.MOVEABLE); } break; } } if(rawType.getEquipPoints() != null && rawType.getEquipPoints().size() > 0){ - ClientEquipState.setEquipState(rVal, new ClientEquipState(rVal,rawType.getEquipPoints())); + ClientEquipState.attachTree(rVal, rawType.getEquipPoints()); rVal.putData(EntityDataStrings.EQUIP_INVENTORY, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints())); } for(String token : rawType.getTokens()){ @@ -367,11 +368,11 @@ public class CreatureUtils { rVal.putData(EntityDataStrings.LIFE_STATE, new LifeState(rVal, rawType.getHealthSystem())); Globals.clientScene.registerEntityToTag(rVal, EntityTags.LIFE_STATE); //idle tree & generic stuff all creatures have - IdleTree idleTree = new IdleTree(rVal); + ClientIdleTree idleTree = new ClientIdleTree(rVal); rVal.putData(EntityDataStrings.TREE_IDLE, idleTree); Globals.clientScene.registerBehaviorTree(idleTree); Globals.clientScene.registerEntityToTag(rVal, EntityTags.CREATURE); - CreatureUtils.setFacingVector(rVal, new Vector3d(0,0,1)); + CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); return rVal; } @@ -396,7 +397,7 @@ public class CreatureUtils { // Hitbox stuff // // - HitboxState.attachHitboxState(realm.getHitboxManager(), rVal, rawType.getHitboxes()); + HitboxCollectionState.attachHitboxState(realm.getHitboxManager(), true, rVal, rawType.getHitboxes()); // // // Physics stuff @@ -448,7 +449,7 @@ public class CreatureUtils { } //round out end of move system rVal.putData(EntityDataStrings.SERVER_MOVEMENT_BT, moveTree); - CreatureUtils.setFacingVector(rVal, new Vector3d(0,0,1)); + CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); rVal.putData(EntityDataStrings.DATA_STRING_MAX_NATURAL_VELOCITY, groundMovementSystem.getMaxVelocity()); rVal.putData(EntityDataStrings.DATA_STRING_ACCELERATION, groundMovementSystem.getAcceleration()); rVal.putData(EntityDataStrings.DATA_STRING_VELOCITY, 0f); @@ -506,7 +507,7 @@ public class CreatureUtils { airplaneMovementTree.setMaxRotationSpeed(airplaneMovementSystem.getMaxRotationSpeed()); //register misc stuff rVal.putData(EntityDataStrings.SERVER_MOVEMENT_BT, airplaneMovementTree); - CreatureUtils.setFacingVector(rVal, new Vector3d(0,0,1)); + CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); ServerBehaviorTreeUtils.attachBTreeToEntity(rVal, airplaneMovementTree); ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.MOVEABLE); } break; @@ -661,7 +662,7 @@ public class CreatureUtils { ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.CREATURE); EntityUtils.setEntityType(rVal, ENTITY_TYPE_CREATURE); EntityUtils.setEntitySubtype(rVal, type); - CreatureUtils.setFacingVector(rVal, new Vector3d(0,0,1)); + CreatureUtils.setFacingVector(rVal, MathUtils.getOriginVector()); rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); //position entity @@ -796,8 +797,8 @@ public class CreatureUtils { return (ServerAttackTree)e.getData(EntityDataStrings.TREE_SERVERATTACKTREE); } - public static IdleTree getIdleTree(Entity e){ - return (IdleTree)e.getData(EntityDataStrings.TREE_IDLE); + public static ClientIdleTree getIdleTree(Entity e){ + return (ClientIdleTree)e.getData(EntityDataStrings.TREE_IDLE); } public static SprintTree clientGetSprintTree(Entity e){ diff --git a/src/main/java/electrosphere/entity/types/foliage/FoliageUtils.java b/src/main/java/electrosphere/entity/types/foliage/FoliageUtils.java index 0fe0cea1..e02c6431 100644 --- a/src/main/java/electrosphere/entity/types/foliage/FoliageUtils.java +++ b/src/main/java/electrosphere/entity/types/foliage/FoliageUtils.java @@ -12,7 +12,7 @@ import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.state.client.ambientaudio.ClientAmbientAudioTree; -import electrosphere.entity.state.idle.IdleTree; +import electrosphere.entity.state.idle.ClientIdleTree; import electrosphere.entity.types.collision.CollisionObjUtils; import electrosphere.entity.types.tree.ProceduralTree; import electrosphere.game.data.foliage.type.AmbientAudio; diff --git a/src/main/java/electrosphere/entity/types/item/ItemUtils.java b/src/main/java/electrosphere/entity/types/item/ItemUtils.java index 22f6028d..ec6932b9 100644 --- a/src/main/java/electrosphere/entity/types/item/ItemUtils.java +++ b/src/main/java/electrosphere/entity/types/item/ItemUtils.java @@ -26,7 +26,7 @@ import electrosphere.entity.state.collidable.ClientCollidableTree; import electrosphere.entity.state.collidable.ServerCollidableTree; import electrosphere.entity.state.gravity.ClientGravityTree; import electrosphere.entity.state.gravity.ServerGravityTree; -import electrosphere.entity.state.hitbox.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState; import electrosphere.game.data.collidable.CollidableTemplate; import electrosphere.game.data.collidable.HitboxData; import electrosphere.game.data.item.type.EquipWhitelist; @@ -61,7 +61,7 @@ public class ItemUtils { rVal.putData(EntityDataStrings.ITEM_IS_WEAPON, true); WeaponData weaponData = item.getWeaponData(); if(weaponData.getHitboxes() != null){ - HitboxState.attachHitboxState(Globals.clientSceneWrapper.getHitboxManager(), rVal, weaponData.getHitboxes()); + HitboxCollectionState.attachHitboxState(Globals.clientSceneWrapper.getHitboxManager(), false, rVal, weaponData.getHitboxes()); } rVal.putData(EntityDataStrings.ITEM_WEAPON_CLASS,weaponData.getWeaponClass()); rVal.putData(EntityDataStrings.ITEM_WEAPON_DATA_RAW,weaponData); @@ -134,7 +134,7 @@ public class ItemUtils { rVal.putData(EntityDataStrings.ITEM_IS_WEAPON, true); WeaponData weaponData = item.getWeaponData(); if(weaponData.getHitboxes() != null){ - HitboxState.attachHitboxState(realm.getHitboxManager(), rVal, weaponData.getHitboxes()); + HitboxCollectionState.attachHitboxState(realm.getHitboxManager(), true, rVal, weaponData.getHitboxes()); } rVal.putData(EntityDataStrings.ITEM_WEAPON_CLASS,weaponData.getWeaponClass()); rVal.putData(EntityDataStrings.ITEM_WEAPON_DATA_RAW,weaponData); @@ -233,6 +233,7 @@ public class ItemUtils { String idleAnim = (String)item.getData(EntityDataStrings.ANIM_IDLE); if(!actor.isPlayingAnimation(idleAnim)){ actor.playAnimation(idleAnim,1); + actor.incrementAnimationTime(0.0001); } } } @@ -247,6 +248,7 @@ public class ItemUtils { String idleAnim = (String)item.getData(EntityDataStrings.ANIM_IDLE); if(!actor.isPlayingAnimation(idleAnim)){ actor.playAnimation(idleAnim,1); + actor.incrementAnimationTime(0.0001); } } } @@ -386,7 +388,7 @@ public class ItemUtils { //this deregisters from all four & unhooks rigid bodies from the physics runtime Globals.clientSceneWrapper.getCollisionEngine().destroyEntityThatHasPhysics(item); //destroy hitboxes - HitboxState.destroyHitboxState(item); + HitboxCollectionState.destroyHitboxState(item); //destroy graphics EntityUtils.cleanUpEntity(item); } @@ -403,7 +405,7 @@ public class ItemUtils { if(itemRealm != null){ itemRealm.getCollisionEngine().destroyEntityThatHasPhysics(item); //destroy hitboxes - HitboxState.destroyHitboxState(item); + HitboxCollectionState.destroyHitboxState(item); } //destroy graphics EntityUtils.cleanUpEntity(item); diff --git a/src/main/java/electrosphere/entity/types/object/ObjectUtils.java b/src/main/java/electrosphere/entity/types/object/ObjectUtils.java index 307d35fb..ffe54a8e 100644 --- a/src/main/java/electrosphere/entity/types/object/ObjectUtils.java +++ b/src/main/java/electrosphere/entity/types/object/ObjectUtils.java @@ -22,7 +22,9 @@ import electrosphere.entity.state.collidable.ClientCollidableTree; import electrosphere.entity.state.collidable.ServerCollidableTree; import electrosphere.entity.state.gravity.ClientGravityTree; import electrosphere.entity.state.gravity.ServerGravityTree; -import electrosphere.entity.state.idle.IdleTree; +import electrosphere.entity.state.hitbox.HitboxCollectionState; +import electrosphere.entity.state.idle.ClientIdleTree; +import electrosphere.entity.state.idle.ServerIdleTree; import electrosphere.entity.state.inventory.ClientInventoryState; import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.ServerInventoryState; @@ -44,11 +46,17 @@ public class ObjectUtils { //the entity type value public static final int ENTITY_TYPE_OBJECT = 2; + /** + * Spawns an object in the client scene + * @param type The type of object + * @return The object entity + */ public static Entity clientSpawnBasicObject(String type){ ObjectData rawType = Globals.gameConfigCurrent.getObjectTypeLoader().getObject(type); Entity rVal = EntityCreationUtils.createClientSpatialEntity(); - EntityCreationUtils.makeEntityPoseable(rVal, rawType.getModelPath()); - Actor creatureActor = EntityUtils.getActor(rVal); + if(rawType.getModelPath() != null){ + EntityCreationUtils.makeEntityDrawable(rVal, rawType.getModelPath()); + } //forward-searching tokens boolean collisionMakeDynamic = true; for(String token : rawType.getTokens()){ @@ -112,11 +120,14 @@ public class ObjectUtils { } break; } } + if(rawType.getHitboxData() != null){ + HitboxCollectionState.attachHitboxState(Globals.clientSceneWrapper.getHitboxManager(), false, rVal, rawType.getHitboxData()); + } //add health system // rVal.putData(EntityDataStrings.LIFE_STATE, new LifeState(rVal, rawType.getHealthSystem())); // Globals.entityManager.registerLifeStateEntity(rVal); //idle tree & generic stuff all objects have - rVal.putData(EntityDataStrings.TREE_IDLE, new IdleTree(rVal)); + rVal.putData(EntityDataStrings.TREE_IDLE, new ClientIdleTree(rVal)); rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); rVal.putData(EntityDataStrings.ENTITY_TYPE, ENTITY_TYPE_OBJECT); rVal.putData(EntityDataStrings.ENTITY_SUBTYPE, type); @@ -133,9 +144,10 @@ public class ObjectUtils { public static Entity serverSpawnBasicObject(Realm realm, Vector3d position, String type){ ObjectData rawType = Globals.gameConfigCurrent.getObjectTypeLoader().getObject(type); Entity rVal = EntityCreationUtils.createServerEntity(realm, position); - EntityCreationUtils.makeEntityPoseable(rVal, rawType.getModelPath()); - PoseActor creatureActor = EntityUtils.getPoseActor(rVal); + if(rawType.getModelPath() != null){ + EntityCreationUtils.makeEntityPoseable(rVal, rawType.getModelPath()); + } //forward-searching tokens boolean collisionMakeDynamic = true; for(String token : rawType.getTokens()){ @@ -199,12 +211,11 @@ public class ObjectUtils { } break; } } - //add health system - // rVal.putData(EntityDataStrings.LIFE_STATE, new LifeState(rVal, rawType.getHealthSystem())); - // Globals.entityManager.registerLifeStateEntity(rVal); + if(rawType.getHitboxData() != null){ + HitboxCollectionState.attachHitboxState(realm.getHitboxManager(), true, rVal, rawType.getHitboxData()); + } //idle tree & generic stuff all objects have - rVal.putData(EntityDataStrings.TREE_IDLE, new IdleTree(rVal)); - rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); + ServerIdleTree.attachTree(rVal); //position entity diff --git a/src/main/java/electrosphere/entity/types/projectile/ProjectileUtils.java b/src/main/java/electrosphere/entity/types/projectile/ProjectileUtils.java index ae74f4e3..8fb59431 100644 --- a/src/main/java/electrosphere/entity/types/projectile/ProjectileUtils.java +++ b/src/main/java/electrosphere/entity/types/projectile/ProjectileUtils.java @@ -16,12 +16,13 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityUtils; import electrosphere.entity.ServerEntityUtils; -import electrosphere.entity.state.hitbox.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState; import electrosphere.entity.state.movement.ProjectileTree; import electrosphere.game.data.collidable.HitboxData; import electrosphere.game.data.projectile.ProjectileType; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils; +import electrosphere.util.MathUtils; public class ProjectileUtils { @@ -41,7 +42,7 @@ public class ProjectileUtils { Globals.assetManager.addModelPathToQueue(model); ProjectileTree tree = new ProjectileTree(rVal,maxLife,new Vector3d(initialVector),velocity); EntityUtils.getPosition(rVal).set(initialPosition); - EntityUtils.getRotation(rVal).rotationTo(new Vector3d(0,0,1), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); + EntityUtils.getRotation(rVal).rotationTo(MathUtils.getOriginVector(), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); Globals.clientSceneWrapper.getScene().registerBehaviorTree(tree); return rVal; } @@ -61,8 +62,8 @@ public class ProjectileUtils { Globals.assetManager.addModelPathToQueue(model); ProjectileTree tree = new ProjectileTree(rVal,maxLife,new Vector3d(initialVector),velocity); EntityUtils.getPosition(rVal).set(initialPosition); - // EntityUtils.getRotation(currentEntity).rotationTo(new Vector3f(0,0,1), new Vector3f((float)facingAngle.x,(float)facingAngle.y,(float)facingAngle.z)).mul(parentActor.getBoneRotation(targetBone)).normalize(); - EntityUtils.getRotation(rVal).rotationTo(new Vector3d(0,0,1), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); + // EntityUtils.getRotation(currentEntity).rotationTo(MathUtils.ORIGIN_VECTORF, new Vector3f((float)facingAngle.x,(float)facingAngle.y,(float)facingAngle.z)).mul(parentActor.getBoneRotation(targetBone)).normalize(); + EntityUtils.getRotation(rVal).rotationTo(MathUtils.getOriginVector(), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); // ParticleTree particleTree = new ParticleTree(rVal, maxLife, destination, velocity, acceleration, true); // rVal.putData(EntityDataStrings.PARTICLE_TREE, particleTree); // rVal.putData(EntityDataStrings.IS_PARTICLE, true); @@ -84,7 +85,7 @@ public class ProjectileUtils { Entity rVal = EntityCreationUtils.createClientSpatialEntity(); EntityCreationUtils.makeEntityDrawable(rVal, rawType.getModelPath()); //initial coordinates - EntityUtils.getRotation(rVal).rotationTo(new Vector3d(0,0,1), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); + EntityUtils.getRotation(rVal).rotationTo(MathUtils.getOriginVector(), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); EntityUtils.getPosition(rVal).set(initialPosition); //projectile behavior tree ProjectileTree tree = new ProjectileTree(rVal,rawType.getMaxLife(),initialVector,rawType.getVelocity(), rawType.getDamage()); @@ -96,7 +97,7 @@ public class ProjectileUtils { //collidable HitboxData hitboxData = new HitboxData(); hitboxData.setRadius(rawType.getHitboxRadius()); - HitboxState.attachHitboxStateWithCallback(Globals.clientSceneWrapper.getHitboxManager(), Globals.clientSceneWrapper.getCollisionEngine(), rVal, hitboxData, + HitboxCollectionState.attachHitboxStateWithCallback(Globals.clientSceneWrapper.getHitboxManager(), Globals.clientSceneWrapper.getCollisionEngine(), rVal, hitboxData, new HitboxPositionCallback() { public Vector3d getPosition(){ return EntityUtils.getPosition(rVal); @@ -119,7 +120,7 @@ public class ProjectileUtils { ProjectileType rawType = Globals.gameConfigCurrent.getProjectileMap().getType(projectileType); Entity rVal = EntityCreationUtils.createServerEntity(realm, initialPosition); //initial coordinates - EntityUtils.getRotation(rVal).rotationTo(new Vector3d(0,0,1), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); + EntityUtils.getRotation(rVal).rotationTo(MathUtils.getOriginVector(), new Vector3d(initialVector.x,initialVector.y,initialVector.z)).normalize(); EntityUtils.getPosition(rVal).set(initialPosition); //projectile behavior tree ProjectileTree tree = new ProjectileTree(rVal,rawType.getMaxLife(),initialVector,rawType.getVelocity(), rawType.getDamage()); @@ -131,7 +132,7 @@ public class ProjectileUtils { //collidable HitboxData hitboxData = new HitboxData(); hitboxData.setRadius(rawType.getHitboxRadius()); - HitboxState.attachHitboxStateWithCallback(realm.getHitboxManager(), realm.getCollisionEngine(), rVal, hitboxData, + HitboxCollectionState.attachHitboxStateWithCallback(realm.getHitboxManager(), realm.getCollisionEngine(), rVal, hitboxData, new HitboxPositionCallback() { public Vector3d getPosition(){ return EntityUtils.getPosition(rVal); diff --git a/src/main/java/electrosphere/game/data/collidable/HitboxData.java b/src/main/java/electrosphere/game/data/collidable/HitboxData.java index e59fe669..a3218e07 100644 --- a/src/main/java/electrosphere/game/data/collidable/HitboxData.java +++ b/src/main/java/electrosphere/game/data/collidable/HitboxData.java @@ -10,6 +10,20 @@ import electrosphere.entity.Entity; */ public class HitboxData { + //a hitbox sphere that teleports to its new position between frames + public static final String HITBOX_TYPE_HIT = "hit"; + //a hurtbox sphere that teleports to its new position between frames + public static final String HITBOX_TYPE_HURT = "hurt"; + //a hitbox sphere that is connected to its previous position by a capsule. The capsule is used for collision checks + public static final String HITBOX_TYPE_HIT_CONNECTED = "hit_connected"; + //a hurtbox sphere that is connected to its previous position by a capsule. The capsule is used for collision checks + public static final String HITBOX_TYPE_HURT_CONNECTED = "hurt_connected"; + //a block sphere that is connected to its previous position by a capsule. The capsule is used for collision checks + public static final String HITBOX_TYPE_BLOCK_CONNECTED = "block_connected"; + + //used for debugging -- to show whether a hitbox is colliding with it or not + public static final String HITBOX_TYPE_STATIC_CAPSULE = "static_capsule"; + //the type of hitbox String type; @@ -19,6 +33,9 @@ public class HitboxData { //the radius of the hitbox float radius; + //the length of a static capsule hitbox + float length; + //controls whether the hitbox is active or not boolean active = false; @@ -52,6 +69,14 @@ public class HitboxData { return radius; } + /** + * Gets the length of hitbox if applicable + * @return The length + */ + public float getLength(){ + return length; + } + /** * Returns whether the hitbox is active or not * @return true if the hitbox is active, false otherwise diff --git a/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java b/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java index 928daf7e..e48fbc63 100644 --- a/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java +++ b/src/main/java/electrosphere/game/data/creature/type/equip/EquipPoint.java @@ -60,6 +60,14 @@ public class EquipPoint { return offsetRotation; } + /** + * Sets the offset rotation (used primarily for debug and engine testing) + * @param offsetRotation The new offset rotation + */ + public void setOffsetRotation(List offsetRotation){ + this.offsetRotation = offsetRotation; + } + /** * Gets the equip classes that are whitelisted for this equip point * @return the classes diff --git a/src/main/java/electrosphere/game/data/graphics/GraphicsTemplate.java b/src/main/java/electrosphere/game/data/graphics/GraphicsTemplate.java index 8e0a87b9..d8e552ef 100644 --- a/src/main/java/electrosphere/game/data/graphics/GraphicsTemplate.java +++ b/src/main/java/electrosphere/game/data/graphics/GraphicsTemplate.java @@ -3,9 +3,14 @@ package electrosphere.game.data.graphics; import java.util.List; import java.util.Map; +/** + * A graphics template for an entity + */ public class GraphicsTemplate { + //a list of shader overrides List shaderOverrideMeshList; + //??? TODO: investigate Map shaderMap; public List getShaderOverrideMeshList(){ diff --git a/src/main/java/electrosphere/game/data/object/type/ObjectData.java b/src/main/java/electrosphere/game/data/object/type/ObjectData.java index d3d49b65..89423470 100644 --- a/src/main/java/electrosphere/game/data/object/type/ObjectData.java +++ b/src/main/java/electrosphere/game/data/object/type/ObjectData.java @@ -6,32 +6,70 @@ import electrosphere.game.data.graphics.GraphicsTemplate; import java.util.List; +/** + * Metadata about a type of object + */ public class ObjectData { + //the id of the object String objectId; + //the path for the model for this object String modelPath; + //tokens associated with this object List tokens; + //the collidable template for this object CollidableTemplate collidable; + //the graphics template for this object GraphicsTemplate graphicsTemplate; + //the hitbox data for this object + List hitboxData; + /** + * Gets the id of the object + * @return the id + */ public String getObjectId() { return objectId; } + /** + * Gets the model path of the object + * @return the model path + */ public String getModelPath() { return modelPath; } + /** + * Gets all tokens associated with this object + * @return the list of all tokens + */ public List getTokens() { return tokens; } + /** + * Gets the collidable data for this object + * @return the collidable data + */ public CollidableTemplate getCollidable(){ return collidable; } + /** + * Gets the graphics template for this object + * @return the graphics template + */ public GraphicsTemplate getGraphicsTemplate(){ return graphicsTemplate; } + + /** + * Gets the hitbox data for this object + * @return the hitbox data + */ + public List getHitboxData(){ + return this.hitboxData; + } } diff --git a/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeLoader.java b/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeLoader.java index eab3e161..bd26cca6 100644 --- a/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeLoader.java +++ b/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeLoader.java @@ -7,14 +7,40 @@ import java.util.Map; import electrosphere.game.data.object.type.ObjectData; +/** + * An interface for grabbing data about objects available to the game engine + */ public class ObjectTypeLoader { + //the map that stores all object types by name Map objectMap = new HashMap(); - public void putObject(String name, ObjectData type){ - objectMap.put(name,type); + //the list of all object data + List objectList = new LinkedList(); + + /** + * Gets the list of all object types loaded into memory + * @return The list + */ + public List getAllObjectTypes(){ + return objectList; } + /** + * Puts an object in the map + * @param name The name of the object + * @param type The object type data + */ + public void putObject(String name, ObjectData type){ + objectMap.put(name,type); + objectList.add(type); + } + + /** + * Gets object data by its name + * @param name The name of the object type + * @return The object data if it exists, otherwise null + */ public ObjectData getObject(String name){ return objectMap.get(name); } diff --git a/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeMap.java b/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeMap.java index da122692..ff2dbfb4 100644 --- a/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeMap.java +++ b/src/main/java/electrosphere/game/data/object/type/model/ObjectTypeMap.java @@ -3,15 +3,29 @@ package electrosphere.game.data.object.type.model; import electrosphere.game.data.object.type.ObjectData; import java.util.List; +/** + * The raw list of object data read from disk + */ public class ObjectTypeMap { + //the objects stored in this file List objects; + //all children files to recursively parse for more object data List files; + /** + * Gets the list of all objects in this file + * @return the list + */ public List getObjects() { return objects; } + /** + * Gets the object data for an object in this file by its name + * @param name the name of the object + * @return The object data + */ public ObjectData getObject(String name){ ObjectData rVal = null; for(ObjectData item : objects){ @@ -23,6 +37,10 @@ public class ObjectTypeMap { return rVal; } + /** + * Gets the list of all children files of this file + * @return The list of all children files + */ public List getFiles(){ return files; } diff --git a/src/main/java/electrosphere/logger/Logger.java b/src/main/java/electrosphere/logger/Logger.java index 2a07265a..65b0d4f7 100644 --- a/src/main/java/electrosphere/logger/Logger.java +++ b/src/main/java/electrosphere/logger/Logger.java @@ -1,46 +1,90 @@ package electrosphere.logger; /** - * - * @author amaterasu + * A channel for logging messages */ public class Logger { + /** + * The different logging levels + */ public enum LogLevel { + LOOP_DEBUG, //this should be used for debugging messages that are executed very rapidly/every frame DEBUG, INFO, WARNING, ERROR, } + //the level of this log LogLevel level; + /** + * Creates a logger channel + * @param level The level of message to report on this channel + */ public Logger(LogLevel level){ this.level = level; } + /** + * Logs a loop debug message. + * This should be used for debugging messages that are executed very rapidly/every frame + * @param message The message to report + */ + public void DEBUG_LOOP(String message){ + if(level == LogLevel.LOOP_DEBUG){ + System.out.println(message); + } + } + + + /** + * Logs a debug message. + * This should be used for debugging messages that are executed on a given condition that won't necessarily be every loop (ie all network messages) + * @param message The message to report + */ public void DEBUG(String message){ - if(level == LogLevel.DEBUG){ + if(level == LogLevel.LOOP_DEBUG || level == LogLevel.DEBUG){ System.out.println(message); } } + + /** + * Logs an info message. + * This should be used for messages that would have interest to someone running a server (ie specific network messages, account creation, etc) + * @param message The message to report + */ public void INFO(String message){ - if(level == LogLevel.DEBUG || level == LogLevel.INFO){ + if(level == LogLevel.LOOP_DEBUG || level == LogLevel.DEBUG || level == LogLevel.INFO){ System.out.println(message); } } + + /** + * Logs a warning message. + * This should be used for reporting events that happen in the engine that are concerning but don't mean the engine has failed to execute (ie a texture failed to load) + * @param message The message to report + */ public void WARNING(String message){ - if(level == LogLevel.DEBUG || level == LogLevel.INFO || level == LogLevel.WARNING){ + if(level == LogLevel.LOOP_DEBUG || level == LogLevel.DEBUG || level == LogLevel.INFO || level == LogLevel.WARNING){ System.out.println(message); } } + + /** + * Logs an error message. + * This should be used every time we throw any kind of error in the engine + * @param message The message to report + */ public void ERROR(String message, Exception e){ - if(level == LogLevel.DEBUG || level == LogLevel.INFO || level == LogLevel.WARNING || level == LogLevel.ERROR){ - System.out.println(message); + if(level == LogLevel.LOOP_DEBUG || level == LogLevel.DEBUG || level == LogLevel.INFO || level == LogLevel.WARNING || level == LogLevel.ERROR){ + System.err.println(message); e.printStackTrace(); } } + } diff --git a/src/main/java/electrosphere/menu/WindowUtils.java b/src/main/java/electrosphere/menu/WindowUtils.java index 9fd84bbb..9ee4e88f 100644 --- a/src/main/java/electrosphere/menu/WindowUtils.java +++ b/src/main/java/electrosphere/menu/WindowUtils.java @@ -12,8 +12,15 @@ import electrosphere.renderer.ui.elementtypes.ContainerElement; import electrosphere.renderer.ui.elementtypes.DrawableElement; import electrosphere.renderer.ui.elementtypes.Element; +/** + * Utils for native windowing framework + */ public class WindowUtils { + /** + * Replaces the main menu contents + * @param newMenu The new contents + */ public static void replaceMainMenuContents(Element newMenu){ Element mainMenuEl = Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_MAIN); if(mainMenuEl != null && mainMenuEl instanceof Window){ @@ -26,6 +33,11 @@ public class WindowUtils { } } + /** + * Recursively sets a window as visible or not + * @param topLevelMenu The window element + * @param visible true for visible, false for invisible + */ public static void recursiveSetVisible(Element topLevelMenu, boolean visible){ if(topLevelMenu instanceof DrawableElement){ ((DrawableElement)topLevelMenu).setVisible(visible); @@ -40,6 +52,29 @@ public class WindowUtils { } } + /** + * Checks whether the window registered to the provided string is open + * @param windowString The window string + * @return true if the window is open, false otherwise + */ + public static boolean windowIsOpen(String windowString){ + Element windowElement = Globals.elementManager.getWindow(windowString); + return Globals.elementManager.getWindowList().contains(windowElement); + } + + /** + * Checks if a window is open or not + * @return The window + */ + public static boolean controlBlockingWindowIsOpen(){ + return windowIsOpen(WindowStrings.LEVEL_EDTIOR_SIDE_PANEL); + } + + /** + * Gets an inventory window string by the id of the inventory + * @param id the id + * @return the window string for said inventory window + */ public static String getInventoryWindowID(int id){ return "INVENTORY-" + id; } @@ -76,8 +111,8 @@ public class WindowUtils { } /** - * Tries to close a window - * @param window the window to close + * Cleans up a window visually and removes it from the element manager + * @param window the window to clean up */ public static void closeWindow(String window){ Element windowEl = Globals.elementManager.getWindow(window); diff --git a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java index c5fa1763..fe97942e 100644 --- a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java +++ b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java @@ -1,15 +1,27 @@ package electrosphere.menu.debug; +import java.util.LinkedList; +import java.util.List; + +import org.joml.Quaterniond; + import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; +import electrosphere.entity.state.equip.ClientEquipState; +import electrosphere.entity.types.attach.AttachUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.foliage.FoliageUtils; import electrosphere.entity.types.item.ItemUtils; -import electrosphere.renderer.RenderingEngine; +import electrosphere.game.data.creature.type.equip.EquipPoint; +import electrosphere.logger.LoggerInterface; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.ActorMeshMask; +import electrosphere.renderer.anim.AnimChannel; +import electrosphere.renderer.anim.Animation; +import electrosphere.renderer.model.Bone; import electrosphere.renderer.model.Mesh; +import electrosphere.renderer.model.Model; import electrosphere.renderer.ui.imgui.ImGuiWindow; import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; import imgui.ImGui; @@ -19,11 +31,17 @@ import imgui.ImGui; */ public class ImGuiEntityMacros { + //window for viewing main player entity's stats on both client and server protected static ImGuiWindow clientEntityWindow; - private static boolean filterToCreatures = false; + private static boolean filterToCreatures = false; //filters the entity list to just creatures + //views stats about an actor protected static ImGuiWindow actorView; - static Entity actorViewEntity; + static Entity actorViewEntity; //the entity whose actor we're viewing in the actor window + + //views stats about equip state + protected static ImGuiWindow equipStateView; + static Entity equipViewEntity; //the entity whose equip state we're viewing in the equip window /** * Creates the windows in this file @@ -31,6 +49,7 @@ public class ImGuiEntityMacros { protected static void createClientEntityWindows(){ createClientEntityDebugWindow(); createActorViewDebugWindow(); + createEquipStateDebugWindow(); } /** @@ -54,9 +73,17 @@ public class ImGuiEntityMacros { ImGui.beginGroup(); ImGui.text("Id: " + entity.getId() + " (" + getEntityName(entity) + ")"); if(CreatureUtils.isCreature(entity)){ - if(ImGui.button("Actor View")){ - actorViewEntity = entity; - actorView.setOpen(true); + if(EntityUtils.getActor(entity) != null){ + if(ImGui.button("Actor View")){ + actorViewEntity = entity; + actorView.setOpen(true); + } + } + if(ClientEquipState.getClientEquipState(entity) != null){ + if(ImGui.button("Client Equip State View")){ + equipViewEntity = entity; + equipStateView.setOpen(true); + } } } ImGui.endGroup(); @@ -64,7 +91,7 @@ public class ImGuiEntityMacros { } }); clientEntityWindow.setOpen(false); - RenderingEngine.addImGuiWindow(clientEntityWindow); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(clientEntityWindow); } /** @@ -90,11 +117,88 @@ public class ImGuiEntityMacros { ImGui.text(blocked); } } + + //print bone values + if(ImGui.button("Print current bone values")){ + for(Bone bone : actor.getBoneValues()){ + LoggerInterface.loggerRenderer.DEBUG(bone.boneID); + LoggerInterface.loggerRenderer.DEBUG("" + bone.getFinalTransform()); + } + } + + //print animation keys + if(ImGui.button("Print animation keys")){ + Model model = Globals.assetManager.fetchModel(actor.getModelPath()); + model.describeAllAnimations(); + } + + //Browsable list of all animations with their data + if(ImGui.collapsingHeader("Animation Channel Data")){ + Model model = Globals.assetManager.fetchModel(actor.getModelPath()); + for(Animation animation : model.getAnimations()){ + ImGui.text(" - " + animation.name); + for(AnimChannel channel : animation.channels){ + ImGui.text("=" + channel.getNodeID() + "="); + ImGui.text("" + channel.getCurrentPosition()); + ImGui.text("" + channel.getCurrentRotation()); + ImGui.text("" + channel.getCurrentScale()); + } + } + } } } }); actorView.setOpen(false); - RenderingEngine.addImGuiWindow(actorView); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(actorView); + } + + + /** + * Client scene equip state view + */ + protected static void createEquipStateDebugWindow(){ + equipStateView = new ImGuiWindow("Client Equip State View"); + equipStateView.setCallback(new ImGuiWindowCallback() { + + //stores the edited rotation values + float[] rotationValues = new float[]{ + 0,0,0 + }; + + @Override + public void exec() { + if(equipViewEntity != null && ClientEquipState.getClientEquipState(equipViewEntity) != null){ + ClientEquipState clientEquipState = ClientEquipState.getClientEquipState(equipViewEntity); + + if(ImGui.collapsingHeader("All Equip Points")){ + for(EquipPoint point : clientEquipState.getAllEquipPoints()){ + if(ImGui.collapsingHeader(point.getEquipPointId())){ + ImGui.text("Has item equipped: " + (clientEquipState.getEquippedItemAtPoint(point.getEquipPointId()) != null)); + ImGui.text("Bone (Third Person): " + point.getBone()); + ImGui.text("Bone (First Person): " + point.getFirstPersonBone()); + ImGui.text("Rotation: " + AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation())); + if(ImGui.sliderFloat3("Rotation (In Euler along x,y,z)", rotationValues, 0, (float)(Math.PI * 2))){ + Quaterniond rotation = new Quaterniond().rotateXYZ(rotationValues[0], rotationValues[1], rotationValues[2]); + List newValues = new LinkedList(); + newValues.add((float)rotation.x); + newValues.add((float)rotation.y); + newValues.add((float)rotation.z); + newValues.add((float)rotation.w); + point.setOffsetRotation(newValues); + Entity equippedEntity = clientEquipState.getEquippedItemAtPoint(point.getEquipPointId()); + if(equippedEntity != null){ + AttachUtils.setRotationOffset(equippedEntity, AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation())); + } + } + } + } + } + + } + } + }); + equipStateView.setOpen(false); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(equipStateView); } /** diff --git a/src/main/java/electrosphere/menu/debug/ImGuiUtils.java b/src/main/java/electrosphere/menu/debug/ImGuiUtils.java new file mode 100644 index 00000000..b4620093 --- /dev/null +++ b/src/main/java/electrosphere/menu/debug/ImGuiUtils.java @@ -0,0 +1,8 @@ +package electrosphere.menu.debug; + +/** + * Utilities for dealing with imgui + */ +public class ImGuiUtils { + +} diff --git a/src/main/java/electrosphere/menu/debug/ImGuiWindowMacros.java b/src/main/java/electrosphere/menu/debug/ImGuiWindowMacros.java index 13d04559..7badf53f 100644 --- a/src/main/java/electrosphere/menu/debug/ImGuiWindowMacros.java +++ b/src/main/java/electrosphere/menu/debug/ImGuiWindowMacros.java @@ -7,13 +7,13 @@ import org.ode4j.ode.DBody; import electrosphere.audio.VirtualAudioSource; import electrosphere.collision.PhysicsEntityUtils; +import electrosphere.controls.ControlHandler.ControlsState; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.attack.ClientAttackTree; import electrosphere.entity.state.server.ServerPlayerViewDirTree; import electrosphere.entity.types.creature.CreatureUtils; -import electrosphere.renderer.RenderingEngine; import electrosphere.renderer.ui.imgui.ImGuiLinePlot; import electrosphere.renderer.ui.imgui.ImGuiWindow; import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset; @@ -75,7 +75,15 @@ public class ImGuiWindowMacros { initFramerateGraphSeries("controls"); globalFrametimeWindow.addElement(globalFrametimePlot); globalFrametimeWindow.setOpen(false); - RenderingEngine.addImGuiWindow(globalFrametimeWindow); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(globalFrametimeWindow); + } + + /** + * Gets the main debug window + * @return the main debug window + */ + public static ImGuiWindow getMainDebugWindow(){ + return mainDebugWindow; } /** @@ -158,7 +166,7 @@ public class ImGuiWindowMacros { } }); audioDebugMenu.setOpen(false); - RenderingEngine.addImGuiWindow(audioDebugMenu); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(audioDebugMenu); } @@ -210,7 +218,7 @@ public class ImGuiWindowMacros { } }); playerEntityWindow.setOpen(false); - RenderingEngine.addImGuiWindow(playerEntityWindow); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(playerEntityWindow); } /** @@ -229,7 +237,7 @@ public class ImGuiWindowMacros { } }); fluidWindow.setOpen(false); - RenderingEngine.addImGuiWindow(fluidWindow); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(fluidWindow); } @@ -268,7 +276,7 @@ public class ImGuiWindowMacros { } }); mainDebugWindow.setOpen(false); - RenderingEngine.addImGuiWindow(mainDebugWindow); + Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(mainDebugWindow); } /** @@ -276,6 +284,11 @@ public class ImGuiWindowMacros { */ public static void toggleMainDebugMenu(){ mainDebugWindow.setOpen(!mainDebugWindow.isOpen()); + if(mainDebugWindow.isOpen()){ + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); + } else { + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); + } } } diff --git a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInGame.java b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInGame.java index 21688ab6..4b76614c 100644 --- a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInGame.java +++ b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInGame.java @@ -51,11 +51,10 @@ public class MenuGeneratorsInGame { WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN), false); Globals.elementManager.unregisterWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN); if(Globals.cameraHandler.getTrackPlayerEntity()){ - Globals.controlHandler.setHandlerState(ControlsState.MAIN_GAME); + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); } else { - Globals.controlHandler.setHandlerState(ControlsState.IN_GAME_FREE_CAMERA); + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_FREE_CAMERA); } - Globals.controlHandler.hideMouse(); return false; }}); @@ -76,11 +75,10 @@ public class MenuGeneratorsInGame { WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN), false); Globals.elementManager.unregisterWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN); if(Globals.cameraHandler.getTrackPlayerEntity()){ - Globals.controlHandler.setHandlerState(ControlsState.MAIN_GAME); + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); } else { - Globals.controlHandler.setHandlerState(ControlsState.IN_GAME_FREE_CAMERA); + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_FREE_CAMERA); } - Globals.controlHandler.hideMouse(); return false; }}); diff --git a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInventory.java b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInventory.java index 74e84e96..a0bc2347 100644 --- a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInventory.java +++ b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsInventory.java @@ -50,8 +50,7 @@ public class MenuGeneratorsInventory { // Globals.openInventoriesCount--; if(Globals.openInventoriesCount == 0){ - Globals.controlHandler.setHandlerState(ControlsState.MAIN_GAME); - Globals.controlHandler.hideMouse(); + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); } //play sound effect Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/closeMenu.ogg", VirtualAudioSourceType.UI, false); @@ -223,8 +222,7 @@ public class MenuGeneratorsInventory { // Globals.openInventoriesCount--; if(Globals.openInventoriesCount == 0){ - Globals.controlHandler.setHandlerState(ControlsState.MAIN_GAME); - Globals.controlHandler.hideMouse(); + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); } //play sound effect Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/closeMenu.ogg", VirtualAudioSourceType.UI, false); diff --git a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java index f7d2f283..406882ef 100644 --- a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java +++ b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java @@ -17,6 +17,7 @@ import electrosphere.entity.types.object.ObjectUtils; import electrosphere.game.data.creature.type.CreatureType; import electrosphere.game.data.foliage.type.FoliageType; import electrosphere.game.data.item.type.Item; +import electrosphere.game.data.object.type.ObjectData; import electrosphere.logger.LoggerInterface; import electrosphere.menu.WindowStrings; import electrosphere.menu.WindowUtils; @@ -114,6 +115,12 @@ public class MenuGeneratorsLevelEditor { return false; }})); + //spawn object button + scrollable.addChild(Button.createButton("Spawn Object", new ClickEventCallback() {public boolean execute(ClickEvent event){ + fillInSpawnObjectContent(scrollable); + return false; + }})); + //select voxel button scrollable.addChild(Button.createButton("Select Voxel Type", new ClickEventCallback() {public boolean execute(ClickEvent event){ if(voxelWindowOpen){ @@ -235,6 +242,37 @@ public class MenuGeneratorsLevelEditor { } + /** + * Level editor menu content for spawning objects + * @param scrollable + */ + private static void fillInSpawnObjectContent(VirtualScrollable scrollable){ + scrollable.clearChildren(); + + //back button + scrollable.addChild(Button.createButton("Back", new ClickEventCallback() {public boolean execute(ClickEvent event){ + fillInDefaultContent(scrollable); + return false; + }})); + + //button for spawning all foliage types + for(ObjectData object : Globals.gameConfigCurrent.getObjectTypeLoader().getAllObjectTypes()){ + //spawn foliage button + scrollable.addChild(Button.createButton("Spawn " + object.getObjectId(), new ClickEventCallback() {public boolean execute(ClickEvent event){ + LoggerInterface.loggerEngine.INFO("spawn " + object.getObjectId() + "!"); + Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera)); + Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera)); + Realm realm = Globals.realmManager.getRealms().iterator().next(); + Vector3d cursorPos = realm.getCollisionEngine().rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0); + ObjectUtils.serverSpawnBasicObject(realm, cursorPos, object.getObjectId()); + return false; + }})); + } + + mainSidePanel.applyYoga(0,0); + } + + /** * Creates tree view of entities in server * @param scrollable diff --git a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsTerrainEditing.java b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsTerrainEditing.java index 474511b6..5f73ee5c 100644 --- a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsTerrainEditing.java +++ b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsTerrainEditing.java @@ -125,6 +125,9 @@ public class MenuGeneratorsTerrainEditing { voxelLabel.setText(type.getName()); //icon/model ImagePanel texturePanel = ImagePanel.createImagePanel(type.getTexture()); + if(type.getTexture() != null){ + Globals.assetManager.addTexturePathtoQueue(type.getTexture()); + } texturePanel.setWidth(VOXEL_BUTTON_TEXTURE_DIM); texturePanel.setHeight(VOXEL_BUTTON_TEXTURE_DIM); newButton.addChild(texturePanel); diff --git a/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsDemo.java b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsDemo.java new file mode 100644 index 00000000..cb4019f0 --- /dev/null +++ b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsDemo.java @@ -0,0 +1,57 @@ +package electrosphere.menu.mainmenu; + +import org.lwjgl.util.yoga.Yoga; + +import electrosphere.engine.Globals; +import electrosphere.engine.loadingthreads.LoadingThread; +import electrosphere.menu.WindowUtils; +import electrosphere.renderer.ui.elements.Button; +import electrosphere.renderer.ui.elements.FormElement; +import electrosphere.renderer.ui.elements.Label; +import electrosphere.renderer.ui.elementtypes.ClickableElement; +import electrosphere.renderer.ui.elementtypes.Element; +import electrosphere.renderer.ui.events.ClickEvent; + +/** + * Generates menu items for the demo version of the engine + */ +public class MenuGeneratorsDemo { + + /** + * Creates the title menu for the demo + * @return The content element to embed in a title window + */ + public static Element createTitleMenu(){ + FormElement rVal = new FormElement(); + //top-bottom + rVal.setJustifyContent(Yoga.YGJustifyCenter); + //left-right + rVal.setAlignItems(Yoga.YGAlignCenter); + rVal.setAlignContent(Yoga.YGAlignFlexStart); + + //label (title) + Label titleLabel = new Label(1.0f); + titleLabel.setText("ORPG"); + rVal.addChild(titleLabel); + + //button (arena) + Button arenaButton = new Button(); + Label arenaLabel = new Label(1.0f); + arenaLabel.setText("Start"); + arenaButton.addChild(arenaLabel); + rVal.addChild(arenaButton); + arenaButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ + LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_LEVEL); + Globals.loadingThreadsList.add(serverThread); + Globals.RUN_CLIENT = true; + Globals.RUN_SERVER = true; + serverThread.start(); + WindowUtils.replaceMainMenuContents(MenuGeneratorsArena.createArenaHostLoginMenu()); + return false; + }}); + arenaButton.setMarginTop(50); + + return rVal; + } + +} diff --git a/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java b/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java index 13a3ba9f..b86d70dc 100644 --- a/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/InventoryProtocol.java @@ -27,7 +27,7 @@ public class InventoryProtocol { //translate equipper id Entity equipper = Globals.clientSceneWrapper.getEntityFromServerId(message.getequipperId()); //spawn in world id - Entity inWorldEntity = ItemUtils.clientSpawnBasicItem(message.getitemTemplate()); + Entity inWorldEntity = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()); if(inWorldEntity != null){ //translate id Globals.clientSceneWrapper.mapIdToId(inWorldEntity.getId(), message.getentityId()); diff --git a/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java b/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java index f2d1f5d1..e9e0c233 100644 --- a/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java +++ b/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java @@ -4,7 +4,7 @@ package electrosphere.net.synchronization; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree; import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree; - +import electrosphere.logger.LoggerInterface; import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.attack.ClientAttackTree; @@ -17,7 +17,7 @@ import electrosphere.entity.state.gravity.ServerGravityTree; import electrosphere.entity.state.idle.ServerIdleTree; -import electrosphere.entity.state.idle.IdleTree; +import electrosphere.entity.state.idle.ClientIdleTree; import java.util.LinkedList; import java.util.List; @@ -49,7 +49,7 @@ public class ClientSynchronizationManager { public void processMessages(){ List messagesToClear = new LinkedList(); for(SynchronizationMessage message : messages){ - if(Globals.clientSceneWrapper.containsServerId(message.getentityId())){ + if(Globals.clientSceneWrapper.containsServerId(message.getentityId()) && Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()) != null){ messagesToClear.add(message); switch(message.getMessageSubtype()){ case UPDATECLIENTSTATE:{ @@ -74,6 +74,8 @@ public class ClientSynchronizationManager { int entityId = message.getentityId(); } break; } + } else if(Globals.clientSceneWrapper.getEntityFromServerId(message.getentityId()) == null){ + LoggerInterface.loggerNetworking.WARNING("Client received synchronization packet for entity that no longer exists on client!"); } } for(SynchronizationMessage message : messagesToClear){ @@ -123,8 +125,8 @@ public class ClientSynchronizationManager { case BehaviorTreeIdEnums.BTREE_SERVERIDLE_ID: { switch(message.getfieldId()){ case 9:{ - IdleTree tree = IdleTree.getIdleTree(entity); - tree.setState(IdleTree.getIdleTreeStateShortAsEnum((short)message.getbTreeValue())); + ClientIdleTree tree = ClientIdleTree.getIdleTree(entity); + tree.setState(ClientIdleTree.getIdleTreeStateShortAsEnum((short)message.getbTreeValue())); } break; } } break; diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index d6d6b452..47d268db 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -80,21 +80,8 @@ import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; -import electrosphere.collision.collidable.Collidable; -import electrosphere.collision.hitbox.HitboxUtils; import electrosphere.engine.Globals; -import electrosphere.entity.Entity; -import electrosphere.entity.EntityDataStrings; -import electrosphere.entity.EntityTags; -import electrosphere.entity.EntityUtils; -import electrosphere.entity.types.camera.CameraEntityUtils; -import electrosphere.game.data.collidable.CollidableTemplate; -import electrosphere.game.data.collidable.HitboxData; import electrosphere.logger.LoggerInterface; -import electrosphere.renderer.RenderPipelineState.SelectedShaderEnum; -import electrosphere.renderer.actor.Actor; -import electrosphere.renderer.actor.instance.InstancedActor; -import electrosphere.renderer.buffer.ShaderAttribute; import electrosphere.renderer.debug.DebugRendering; import electrosphere.renderer.framebuffer.Framebuffer; import electrosphere.renderer.framebuffer.FramebufferUtils; @@ -104,6 +91,7 @@ import electrosphere.renderer.model.Model; import electrosphere.renderer.pipelines.CompositePipeline; import electrosphere.renderer.pipelines.DebugContentPipeline; import electrosphere.renderer.pipelines.FirstPersonItemsPipeline; +import electrosphere.renderer.pipelines.ImGuiPipeline; import electrosphere.renderer.pipelines.MainContentNoOITPipeline; import electrosphere.renderer.pipelines.MainContentPipeline; import electrosphere.renderer.pipelines.NormalsForOutlinePipeline; @@ -114,19 +102,6 @@ import electrosphere.renderer.pipelines.UIPipeline; import electrosphere.renderer.pipelines.VolumeBufferPipeline; import electrosphere.renderer.shader.ShaderProgram; import electrosphere.renderer.texture.Texture; -import electrosphere.renderer.ui.elementtypes.DrawableElement; -import electrosphere.renderer.ui.elementtypes.Element; -import electrosphere.renderer.ui.imgui.ImGuiLinePlot; -import electrosphere.renderer.ui.imgui.ImGuiWindow; -import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset; -import electrosphere.server.pathfinding.navmesh.NavCube; -import electrosphere.server.pathfinding.navmesh.NavMesh; -import electrosphere.server.pathfinding.navmesh.NavShape; -import imgui.ImGui; -import imgui.extension.implot.ImPlot; -import imgui.gl3.ImGuiImplGl3; -import imgui.glfw.ImGuiImplGlfw; -import imgui.internal.ImGuiContext; public class RenderingEngine { @@ -145,20 +120,9 @@ public class RenderingEngine { - // - //imgui related - // - //imgui internal objects - private static final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw(); - private static final ImGuiImplGl3 imGuiGl13 = new ImGuiImplGl3(); + //the version of glsl to init imgui with private static String glslVersion = null; - //the context pointer for the core imgui objects - private static ImGuiContext imGuiContext = null; - //if set to true, will render imgui windows - private static boolean imGuiShouldRender = true; - //All imgui windows that should be displayed - private static List imGuiWindows = new CopyOnWriteArrayList(); @@ -253,6 +217,7 @@ public class RenderingEngine { CompositePipeline compositePipeline = new CompositePipeline(); UIPipeline uiPipeline = new UIPipeline(); RenderScreenPipeline renderScreenPipeline = new RenderScreenPipeline(); + ImGuiPipeline imGuiPipeline; public void createOpenglContext(){ @@ -321,11 +286,8 @@ public class RenderingEngine { //Creates the OpenGL capabilities for the program. GL.createCapabilities(); - //init imgui (must happen after gl.createCapabilities) - imGuiContext = ImGui.createContext(); - ImPlot.createContext(); - imGuiGlfw.init(Globals.window,true); - imGuiGl13.init(glslVersion); + //init imgui pipeline + imGuiPipeline = new ImGuiPipeline(Globals.window, glslVersion); //This enables Z-buffering so that farther-back polygons are not drawn over nearer ones glEnable(GL_DEPTH_TEST); @@ -534,22 +496,14 @@ public class RenderingEngine { /** * Render imgui */ - if(imGuiShouldRender){ - imGuiGlfw.newFrame(); - ImGui.newFrame(); - for(ImGuiWindow window : imGuiWindows){ - window.draw(); - } - ImGui.render(); - imGuiGl13.renderDrawData(ImGui.getDrawData()); - } + imGuiPipeline.render(openGLState, renderPipelineState); //check for errors // checkError(); //check and call events and swap the buffers - LoggerInterface.loggerRenderer.DEBUG("Swap buffers"); + LoggerInterface.loggerRenderer.DEBUG_LOOP("Swap buffers"); glfwSwapBuffers(Globals.window); glfwPollEvents(); } @@ -613,19 +567,11 @@ public class RenderingEngine { } /** - * Adds a window to the rendering engine - * @param window The window + * Gets the imgui pipeline + * @return The imgui pipeline */ - public static void addImGuiWindow(ImGuiWindow window){ - imGuiWindows.add(window); - } - - /** - * Removes an imgui window from the rendering engine - * @param window The window - */ - public static void removeImGuiWindow(ImGuiWindow window){ - imGuiWindows.remove(window); + public ImGuiPipeline getImGuiPipeline(){ + return this.imGuiPipeline; } /** diff --git a/src/main/java/electrosphere/renderer/actor/Actor.java b/src/main/java/electrosphere/renderer/actor/Actor.java index f80cb33c..4086d291 100644 --- a/src/main/java/electrosphere/renderer/actor/Actor.java +++ b/src/main/java/electrosphere/renderer/actor/Actor.java @@ -281,7 +281,7 @@ public class Actor { // model.updateNodeTransform(); Bone currentBone = model.getBoneMap().get(boneName); if(currentBone != null){ - Vector4d result = currentBone.final_transform.transform(new Matrix4d(currentBone.inverseBindPoseMatrix).invert().transform(new Vector4d(rVal.x,rVal.y,rVal.z,1))); + Vector4d result = new Matrix4d(currentBone.getFinalTransform()).transform(currentBone.getMOffset().invert().transform(new Vector4d(rVal.x,rVal.y,rVal.z,1))); // currentBone.inverseBindPoseMatrix rVal.x = (float)result.x; rVal.y = (float)result.y; @@ -304,7 +304,7 @@ public class Actor { Bone currentBone = model.getBoneMap().get(boneName); if(currentBone != null){ AxisAngle4f axisAngle = new AxisAngle4f(); - new Matrix4f(currentBone.final_transform).getRotation(axisAngle); + new Matrix4f(currentBone.getFinalTransform()).getRotation(axisAngle); Quaterniond rotation = new Quaterniond(axisAngle); rVal.set(rotation); } @@ -325,7 +325,7 @@ public class Actor { // model.updateNodeTransform(); Bone currentBone = model.getBoneMap().get(boneName); if(currentBone != null){ - rVal = currentBone.final_transform; + rVal = currentBone.getFinalTransform(); // currentBone.inverseBindPoseMatrix } // } @@ -333,6 +333,20 @@ public class Actor { return rVal; } + /** + * Gets the list of all bones + * @return the list of all bones + */ + public List getBoneValues(){ + Model model = Globals.assetManager.fetchModel(modelPath); + if(model != null){ + applyAnimationMasks(model); + calculateNodeTransforms(model); + return model.getBones(); + } + return null; + } + public boolean modelIsLoaded(){ Model model = Globals.assetManager.fetchModel(modelPath); if(model != null){ diff --git a/src/main/java/electrosphere/renderer/anim/AnimNode.java b/src/main/java/electrosphere/renderer/anim/AnimNode.java index 8ee09bd5..8b5710f7 100644 --- a/src/main/java/electrosphere/renderer/anim/AnimNode.java +++ b/src/main/java/electrosphere/renderer/anim/AnimNode.java @@ -12,7 +12,7 @@ import org.lwjgl.assimp.AINode; */ public class AnimNode { public String id; - public Matrix4d transform; + private Matrix4d transform; public AnimNode parent; public List children; public boolean is_bone; @@ -25,4 +25,12 @@ public class AnimNode { is_bone = false; this.raw_data = raw_data; } + + public Matrix4d getTransform(){ + return new Matrix4d(transform); + } + + public void setTransform(Matrix4d transform){ + this.transform = new Matrix4d(transform); + } } diff --git a/src/main/java/electrosphere/renderer/loading/ModelLoader.java b/src/main/java/electrosphere/renderer/loading/ModelLoader.java index 21063834..35d2ae01 100644 --- a/src/main/java/electrosphere/renderer/loading/ModelLoader.java +++ b/src/main/java/electrosphere/renderer/loading/ModelLoader.java @@ -25,7 +25,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.lwjgl.assimp.AIScene; import static org.lwjgl.assimp.Assimp.*; -import static org.lwjgl.assimp.Assimp.aiImportFile; public class ModelLoader { public static AIScene loadAIScene(String path){ diff --git a/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java b/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java index dd7eeb3f..5f54dbb1 100644 --- a/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java +++ b/src/main/java/electrosphere/renderer/meshgen/MeshLoader.java @@ -180,25 +180,26 @@ public class MeshLoader { if(mesh.mTextureCoords().capacity() > 0){ AIVector3D.Buffer texturecoords = mesh.mTextureCoords(0); try { - int textureCoordCount = mesh.mTextureCoords(0).capacity(); - FloatBuffer TextureArrayBufferData; - if(textureCoordCount > 0){ - TextureArrayBufferData = BufferUtils.createFloatBuffer(textureCoordCount * 2); - float[] temp = new float[2]; - for (int i = 0; i < textureCoordCount; i++) { - AIVector3D normal = texturecoords.get(i); - temp[0] = normal.x(); - temp[1] = normal.y(); -// temp[2] = normal.z(); - TextureArrayBufferData.put(temp); + if(texturecoords != null){ + int textureCoordCount = texturecoords.capacity(); + FloatBuffer TextureArrayBufferData; + if(textureCoordCount > 0){ + TextureArrayBufferData = BufferUtils.createFloatBuffer(textureCoordCount * 2); + float[] temp = new float[2]; + for (int i = 0; i < textureCoordCount; i++) { + AIVector3D normal = texturecoords.get(i); + temp[0] = normal.x(); + temp[1] = normal.y(); + // temp[2] = normal.z(); + TextureArrayBufferData.put(temp); + } + TextureArrayBufferData.flip(); + rVal.bufferTextureCoords(TextureArrayBufferData, 2); } - TextureArrayBufferData.flip(); - rVal.bufferTextureCoords(TextureArrayBufferData, 2); } } catch (NullPointerException ex){ - ex.printStackTrace(); + LoggerInterface.loggerRenderer.ERROR("Error reading texture coordinates", ex); } - //System.out.println("Enabled texture coordinates"); } @@ -226,7 +227,7 @@ public class MeshLoader { // System.out.println("Num weights: " + currentBoneData.mNumWeights()); Bone currentBone = new Bone(currentBoneData); currentBone.boneID = currentBoneData.mName().dataString(); - currentBone.inverseBindPoseMatrix = electrosphere.util.Utilities.convertAIMatrixd(currentBoneData.mOffsetMatrix()); + currentBone.setMOffset(electrosphere.util.Utilities.convertAIMatrixd(currentBoneData.mOffsetMatrix())); Iterator weightIterator = currentBoneData.mWeights().iterator(); while(weightIterator.hasNext()){ AIVertexWeight currentWeightData = weightIterator.next(); diff --git a/src/main/java/electrosphere/renderer/model/Bone.java b/src/main/java/electrosphere/renderer/model/Bone.java index b4892932..3717a250 100644 --- a/src/main/java/electrosphere/renderer/model/Bone.java +++ b/src/main/java/electrosphere/renderer/model/Bone.java @@ -11,25 +11,45 @@ import org.lwjgl.assimp.AIBone; * Keeps track of bone data */ public class Bone { + + //the name of the bone public String boneID; + + //the number of vertices affected by this bone int numWeights; + + //The map of index of vertex to weight of this bone on that vertex Map weights = new HashMap(); - public Matrix4d inverseBindPoseMatrix; - public Matrix4d deform; - public Matrix4d transform; - public Matrix4d final_transform; + + //the mOffsetMatrix -- transforms from mesh space to bone space in bind pose + private Matrix4d mOffsetMatrix; + + //the current deform value of the bone + private Matrix4d deform; + + //the final transform that is used for drawing, data, etc + private Matrix4d finalTransform; + + //the raw data for the bone public AIBone raw_data; + + /** + * Cnostructor + */ public Bone(){ - transform = new Matrix4d(); deform = new Matrix4d(); - final_transform = new Matrix4d(); + finalTransform = new Matrix4d(); } + + /** + * Constructor + * @param raw_data The raw assimp data + */ public Bone(AIBone raw_data){ - transform = new Matrix4d(); deform = new Matrix4d(); - final_transform = new Matrix4d(); + finalTransform = new Matrix4d(); boneID = raw_data.mName().dataString(); - inverseBindPoseMatrix = electrosphere.util.Utilities.convertAIMatrixd(raw_data.mOffsetMatrix()); + mOffsetMatrix = electrosphere.util.Utilities.convertAIMatrixd(raw_data.mOffsetMatrix()); numWeights = raw_data.mNumWeights(); this.raw_data = raw_data; } @@ -49,4 +69,36 @@ public class Bone { public Map getWeights(){ return weights; } + + /** + * Gets the offset matrix for this bone + * @return The offset matrix + */ + public Matrix4d getMOffset(){ + return new Matrix4d(mOffsetMatrix); + } + + /** + * Sets the offset matrix for this bone + * @param mTransform the offset matrix + */ + public void setMOffset(Matrix4d mOffset){ + this.mOffsetMatrix = new Matrix4d(mOffset); + } + + public Matrix4d getDeform(){ + return new Matrix4d(deform); + } + + public void setDeform(Matrix4d deform){ + this.deform = new Matrix4d(deform); + } + + public Matrix4d getFinalTransform(){ + return new Matrix4d(finalTransform); + } + + public void setFinalTransform(Matrix4d finalTransform){ + this.finalTransform = new Matrix4d(finalTransform); + } } diff --git a/src/main/java/electrosphere/renderer/model/Mesh.java b/src/main/java/electrosphere/renderer/model/Mesh.java index 1831a469..ee3cf853 100644 --- a/src/main/java/electrosphere/renderer/model/Mesh.java +++ b/src/main/java/electrosphere/renderer/model/Mesh.java @@ -29,6 +29,27 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; + +import static org.lwjgl.opengl.GL11.GL_FLOAT; +import static org.lwjgl.opengl.GL11.GL_INT; +import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT; +import static org.lwjgl.opengl.GL13.GL_TEXTURE3; +import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW; +import static org.lwjgl.opengl.GL15.glBindBuffer; +import static org.lwjgl.opengl.GL15.glGenBuffers; +import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray; +import static org.lwjgl.opengl.GL20.glGetUniformLocation; +import static org.lwjgl.opengl.GL20.glUniform1f; +import static org.lwjgl.opengl.GL20.glUniform1i; +import static org.lwjgl.opengl.GL20.glUniform3fv; +import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; +import static org.lwjgl.opengl.GL20.glVertexAttribPointer; +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL30.glGenVertexArrays; import static org.lwjgl.opengl.GL40.*; import org.lwjgl.opengl.GL45; @@ -418,7 +439,7 @@ public class Mesh { Bone currentBone = parent.getBoneMap().get(boneName); String currentUniform = "bones[" + incrementer + "]"; if(currentBone != null){ - Matrix4d currentMat = new Matrix4d(currentBone.final_transform); + Matrix4d currentMat = currentBone.getFinalTransform(); // currentMat.get(bufferarray); // if(boneName.equals("Torso")){ // System.out.println("Found torso bone"); @@ -430,7 +451,8 @@ public class Mesh { } else { // System.out.println("Bonename: " + boneName); // System.exit(1); - GL45.glUniformMatrix4fv(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), currentUniform), false, new float[16]); + openGLState.getActiveShader().setUniform(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), currentUniform), new float[16]); + // GL45.glUniformMatrix4fv(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), currentUniform), false, new float[16]); } incrementer++; } diff --git a/src/main/java/electrosphere/renderer/model/Model.java b/src/main/java/electrosphere/renderer/model/Model.java index 8a7e50a1..b7f78f6b 100644 --- a/src/main/java/electrosphere/renderer/model/Model.java +++ b/src/main/java/electrosphere/renderer/model/Model.java @@ -24,7 +24,6 @@ import org.lwjgl.PointerBuffer; import org.lwjgl.assimp.AIMaterial; import org.lwjgl.assimp.AIMesh; import org.lwjgl.assimp.AIScene; -import org.lwjgl.assimp.Assimp; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -48,6 +47,7 @@ public class Model { private Vector3d worldPos = new Vector3d(); //an optional global transform applied to the parent bone. Typically found in models loaded from files + private Matrix4d rootTransform = new Matrix4d(); private Matrix4d globalInverseTransform = new Matrix4d(); //the meshes in the model @@ -104,7 +104,6 @@ public class Model { // //load meshes // - int meshCount = s.mNumMeshes(); PointerBuffer meshesBuffer = s.mMeshes(); rVal.meshes = new ArrayList(); while(meshesBuffer.hasRemaining()){ @@ -170,12 +169,15 @@ public class Model { //parse animation nodes and form hierarchy // AINode rootNode = s.mRootNode(); - rVal.globalInverseTransform = electrosphere.util.Utilities.convertAIMatrixd(rootNode.mTransformation()); + //The mOffsetMatrix, inverted, is the bind pose matrix that we want to apply at the top of all anims: https://github.com/assimp/assimp/issues/4364 + //This version of assimp doesn't support it, unfortunately + rVal.rootTransform = electrosphere.util.Utilities.convertAIMatrixd(rootNode.mTransformation()); if(globalTransform != null){ - rVal.globalInverseTransform.scale(globalTransform.getScale()); + rVal.globalInverseTransform = new Matrix4d(rVal.rootTransform).invert().scale(globalTransform.getScale()); + rVal.rootTransform.scale(globalTransform.getScale()); } LoggerInterface.loggerRenderer.DEBUG("Global Inverse Transform"); - LoggerInterface.loggerRenderer.DEBUG(rVal.globalInverseTransform + ""); + LoggerInterface.loggerRenderer.DEBUG(rVal.rootTransform + ""); rVal.rootAnimNode = rVal.buildAnimNodeMap(s.mRootNode(),null); // //load animations @@ -233,10 +235,13 @@ public class Model { for(Mesh toDraw : meshMask.getToDrawMeshes()){ toDraw.setBones(bones); toDraw.setParent(this); + //set shader ShaderProgram original = toDraw.getShader(); ShaderProgram shader = getCorrectShader(shaderMask, toDraw, toDraw.getShader()); toDraw.setShader(shader); + //draw toDraw.complexDraw(renderPipelineState, openGLState); + //reset shader toDraw.setShader(original); } } @@ -279,10 +284,11 @@ public class Model { if(currentBone != null){ // System.out.println("Applying to bone"); //T * S * R - currentBone.deform = new Matrix4d(); - currentBone.deform.translate(currentChannel.getCurrentPosition()); - currentBone.deform.rotate(currentChannel.getCurrentRotation()); - currentBone.deform.scale(new Vector3d(currentChannel.getCurrentScale())); + Matrix4d deform = new Matrix4d(); + deform.translate(currentChannel.getCurrentPosition()); + deform.rotate(currentChannel.getCurrentRotation()); + deform.scale(new Vector3d(currentChannel.getCurrentScale())); + currentBone.setDeform(deform); } } } @@ -302,6 +308,21 @@ public class Model { } } } + + /** + * Logs all animations for a given model including individual key values + */ + public void describeAllAnimationsFully(){ + if(animations.size() > 0){ + LoggerInterface.loggerRenderer.DEBUG("====================="); + LoggerInterface.loggerRenderer.DEBUG(animations.size() + " animations available in model!"); + Iterator animIterator = animations.iterator(); + while(animIterator.hasNext()){ + Animation currentAnim = animIterator.next(); + currentAnim.fullDescribeAnimation(); + } + } + } /** * Recursively builds the bone tree @@ -332,6 +353,7 @@ public class Model { if(this.rootAnimNode != null){ updateNodeTransform(this.rootAnimNode,boneRotators,staticMorph); } + //if the model doesn't have bones, rootAnimNode won't be defined (think terrain) } /** @@ -341,33 +363,35 @@ public class Model { * @param staticMorph The static morph to apply */ void updateNodeTransform(AnimNode n, Map boneRotators, ActorStaticMorph staticMorph){ + Matrix4d parentTransform = new Matrix4d(); if(n.parent != null){ - n.transform = new Matrix4d(n.parent.transform); - } else { - n.transform = new Matrix4d(); + parentTransform = new Matrix4d(n.parent.getTransform()); } if(n.is_bone){ // //bone rotators (turrets, hair, etc) Bone target_bone = boneMap.get(n.id); - n.transform = n.transform.mul(target_bone.deform); + Matrix4d currentTransform = parentTransform.mul(target_bone.getDeform()); if(boneRotators.containsKey(target_bone.boneID)){ - n.transform.rotate(boneRotators.get(target_bone.boneID).getRotation()); + currentTransform.rotate(boneRotators.get(target_bone.boneID).getRotation()); } // //static morph (changing nose size, eye distance, etc) - Matrix4d bone_matrix = new Matrix4d(n.transform); if(staticMorph != null && staticMorph.getBoneTransforms(n.id) != null){ - bone_matrix.mul(staticMorph.getBoneTransforms(n.id).getTransform()); - n.transform.mul(staticMorph.getBoneTransforms(n.id).getTransform()); + currentTransform.mul(staticMorph.getBoneTransforms(n.id).getTransform()); } + + // + Matrix4d bone_matrix = new Matrix4d(currentTransform); + n.setTransform(currentTransform); // //Calculate final offset from initial bone - bone_matrix.mul(target_bone.inverseBindPoseMatrix); + //https://stackoverflow.com/a/59869381 + bone_matrix.mul(target_bone.getMOffset()); bone_matrix = new Matrix4d(globalInverseTransform).mul(bone_matrix); - target_bone.final_transform = bone_matrix; + target_bone.setFinalTransform(bone_matrix); } else { - n.transform = n.transform.mul(electrosphere.util.Utilities.convertAIMatrix(n.raw_data.mTransformation())); + n.setTransform(parentTransform.mul(electrosphere.util.Utilities.convertAIMatrix(n.raw_data.mTransformation()))); } Iterator node_iterator = n.children.iterator(); while(node_iterator.hasNext()){ @@ -571,4 +595,12 @@ public class Model { public void setBoundingSphere(Sphered boundingSphere){ this.boundingSphere = boundingSphere; } + + /** + * Gets the list of all animations + * @return The list of all animations + */ + public List getAnimations(){ + return this.animations; + } } diff --git a/src/main/java/electrosphere/renderer/pipelines/DebugContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/DebugContentPipeline.java index 8e6143bb..7c1aee3c 100644 --- a/src/main/java/electrosphere/renderer/pipelines/DebugContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/DebugContentPipeline.java @@ -6,6 +6,7 @@ import org.joml.Quaternionf; import org.joml.Vector3d; import org.joml.Vector3f; import org.lwjgl.opengl.GL40; +import org.ode4j.ode.DCapsule; import org.ode4j.ode.DGeom; import org.ode4j.ode.DSphere; @@ -15,19 +16,24 @@ import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; -import electrosphere.entity.state.hitbox.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState; +import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState; import electrosphere.entity.types.camera.CameraEntityUtils; import electrosphere.game.data.collidable.CollidableTemplate; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.RenderingEngine; import electrosphere.renderer.model.Model; +import electrosphere.renderer.texture.Texture; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.utils.EntityLookupUtils; import electrosphere.server.pathfinding.navmesh.NavCube; import electrosphere.server.pathfinding.navmesh.NavMesh; import electrosphere.server.pathfinding.navmesh.NavShape; +/** + * Pipeline for rendering content to assist debugging + */ public class DebugContentPipeline implements RenderPipeline { @Override @@ -53,7 +59,7 @@ public class DebugContentPipeline implements RenderPipeline { renderPipelineState.setUseMeshShader(true); renderPipelineState.setBufferStandardUniforms(true); renderPipelineState.setBufferNonStandardUniforms(false); - renderPipelineState.setUseMaterial(true); + renderPipelineState.setUseMaterial(false); renderPipelineState.setUseShadowMap(true); renderPipelineState.setUseBones(true); renderPipelineState.setUseLight(true); @@ -62,12 +68,22 @@ public class DebugContentPipeline implements RenderPipeline { if(Globals.userSettings.getGraphicsDebugDrawCollisionSpheresClient()){ Model hitboxModel; - for(HitboxState hitboxState : Globals.clientSceneWrapper.getHitboxManager().getAllHitboxes()){ - for(String boneName : hitboxState.getBones()){ - DGeom geom = hitboxState.getGeometry(boneName); + for(HitboxCollectionState hitboxState : Globals.clientSceneWrapper.getHitboxManager().getAllHitboxes()){ + for(DGeom geom : hitboxState.getGeometries()){ if(geom instanceof DSphere){ DSphere sphereView = (DSphere)geom; + HitboxState shapeStatus = hitboxState.getShapeStatus(geom); if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitsphere.glb")) != null){ + //set color based on collision status, type, etc + Texture texture = null; + if(shapeStatus.getHadCollision()){ + texture = Globals.assetManager.fetchTexture("Textures/transparent_red.png"); + } else { + texture = Globals.assetManager.fetchTexture("Textures/transparent_grey.png"); + } + if(texture != null){ + texture.bind(openGLState); + } Vector3d position = PhysicsUtils.odeVecToJomlVec(sphereView.getPosition()); //calculate camera-modified vector3f Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)); @@ -79,6 +95,32 @@ public class DebugContentPipeline implements RenderPipeline { hitboxModel.draw(renderPipelineState,openGLState); } } + if(geom instanceof DCapsule){ + DCapsule capsuleView = (DCapsule)geom; + HitboxState shapeStatus = hitboxState.getShapeStatus(geom); + if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitcapsule.glb")) != null){ + //set color based on collision status, type, etc + Texture texture = null; + if(shapeStatus.getHadCollision()){ + texture = Globals.assetManager.fetchTexture("Textures/transparent_red.png"); + } else { + texture = Globals.assetManager.fetchTexture("Textures/transparent_grey.png"); + } + if(texture != null){ + texture.bind(openGLState); + } + Vector3d position = PhysicsUtils.odeVecToJomlVec(capsuleView.getPosition()); + //calculate camera-modified vector3f + Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + //since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation + modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707))); + modelTransformMatrix.scale(capsuleView.getRadius(),capsuleView.getLength(),capsuleView.getRadius()); + hitboxModel.setModelMatrix(modelTransformMatrix); + hitboxModel.draw(renderPipelineState,openGLState); + } + } } } // for(Entity currentHitbox : Globals.clientHitboxManager.getAllHitboxes()){ @@ -130,12 +172,22 @@ public class DebugContentPipeline implements RenderPipeline { int serverIdForClientEntity = Globals.clientSceneWrapper.mapClientToServerId(Globals.playerEntity.getId()); Entity serverPlayerEntity = EntityLookupUtils.getEntityById(serverIdForClientEntity); Realm playerRealm = Globals.realmManager.getEntityRealm(serverPlayerEntity); - for(HitboxState hitboxState : playerRealm.getHitboxManager().getAllHitboxes()){ - for(String boneName : hitboxState.getBones()){ - DGeom geom = hitboxState.getGeometry(boneName); + for(HitboxCollectionState hitboxState : playerRealm.getHitboxManager().getAllHitboxes()){ + for(DGeom geom : hitboxState.getGeometries()){ if(geom instanceof DSphere){ DSphere sphereView = (DSphere)geom; + HitboxState shapeStatus = hitboxState.getShapeStatus(geom); if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitsphere.glb")) != null){ + //set color based on collision status, type, etc + Texture texture = null; + if(shapeStatus.getHadCollision()){ + texture = Globals.assetManager.fetchTexture("Textures/transparent_red.png"); + } else { + texture = Globals.assetManager.fetchTexture("Textures/transparent_grey.png"); + } + if(texture != null){ + texture.bind(openGLState); + } Vector3d position = PhysicsUtils.odeVecToJomlVec(sphereView.getPosition()); //calculate camera-modified vector3f Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)); @@ -147,10 +199,39 @@ public class DebugContentPipeline implements RenderPipeline { hitboxModel.draw(renderPipelineState,openGLState); } } + if(geom instanceof DCapsule){ + DCapsule capsuleView = (DCapsule)geom; + HitboxState shapeStatus = hitboxState.getShapeStatus(geom); + if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitcapsule.glb")) != null){ + //set color based on collision status, type, etc + Texture texture = null; + if(shapeStatus.getHadCollision()){ + texture = Globals.assetManager.fetchTexture("Textures/transparent_red.png"); + } else { + texture = Globals.assetManager.fetchTexture("Textures/transparent_grey.png"); + } + if(texture != null){ + texture.bind(openGLState); + } + Vector3d position = PhysicsUtils.odeVecToJomlVec(capsuleView.getPosition()); + //calculate camera-modified vector3f + Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + //since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation + modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707))); + modelTransformMatrix.scale(capsuleView.getRadius(),capsuleView.getLength(),capsuleView.getRadius()); + hitboxModel.setModelMatrix(modelTransformMatrix); + hitboxModel.draw(renderPipelineState,openGLState); + } + } } } } + //update pipeline state to use mats again + renderPipelineState.setUseMaterial(true); + if(Globals.userSettings.graphicsDebugDrawPhysicsObjects()){ Model physicsGraphicsModel; for(Collidable collidable : Globals.clientSceneWrapper.getCollisionEngine().getCollidables()){ diff --git a/src/main/java/electrosphere/renderer/pipelines/ImGuiPipeline.java b/src/main/java/electrosphere/renderer/pipelines/ImGuiPipeline.java new file mode 100644 index 00000000..37fde464 --- /dev/null +++ b/src/main/java/electrosphere/renderer/pipelines/ImGuiPipeline.java @@ -0,0 +1,97 @@ +package electrosphere.renderer.pipelines; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import electrosphere.engine.Globals; +import electrosphere.menu.debug.ImGuiWindowMacros; +import electrosphere.renderer.OpenGLState; +import electrosphere.renderer.RenderPipelineState; +import electrosphere.renderer.ui.imgui.ImGuiWindow; +import imgui.ImGui; +import imgui.extension.implot.ImPlot; +import imgui.gl3.ImGuiImplGl3; +import imgui.glfw.ImGuiImplGlfw; +import imgui.internal.ImGuiContext; + +/** + * ImGui rendering pipeline + */ +public class ImGuiPipeline implements RenderPipeline { + + // + //imgui related + // + //imgui internal objects + private final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw(); + private final ImGuiImplGl3 imGuiGl13 = new ImGuiImplGl3(); + + //the context pointer for the core imgui objects + private ImGuiContext imGuiContext = null; + //if set to true, will render imgui windows + private boolean imGuiShouldRender = true; + //All imgui windows that should be displayed + private List imGuiWindows = new CopyOnWriteArrayList(); + + /** + * Constructor for the pipeline + * @param windowId The glfw window id + * @param glfwVersion the glfw version + */ + public ImGuiPipeline(long windowId, String glslVersion){ + //init imgui (must happen after gl.createCapabilities) + imGuiContext = ImGui.createContext(); + ImPlot.createContext(); + imGuiGlfw.init(Globals.window,true); + imGuiGl13.init(glslVersion); + } + + @Override + public void render(OpenGLState openGLState, RenderPipelineState renderPipelineState) { + /** + * Render imgui + */ + if(imGuiShouldRender){ + imGuiGlfw.newFrame(); + ImGui.newFrame(); + for(ImGuiWindow window : imGuiWindows){ + window.draw(); + } + ImGui.render(); + imGuiGl13.renderDrawData(ImGui.getDrawData()); + } + } + + + /** + * Adds a n imgui window to the pipeline + * @param window The window + */ + public void addImGuiWindow(ImGuiWindow window){ + imGuiWindows.add(window); + } + + /** + * Removes an imgui window from the pipeline + * @param window The window + */ + public void removeImGuiWindow(ImGuiWindow window){ + imGuiWindows.remove(window); + } + + /** + * Checks if an imgui window is open that should capture controls + * @return true if there is a control-capturing window open, false otherwise + */ + public boolean shouldCaptureControls(){ + for(ImGuiWindow window : imGuiWindows){ + if(window == ImGuiWindowMacros.getMainDebugWindow() && window.isOpen()){ + return true; + } + } + return false; + } + + + +} diff --git a/src/main/java/electrosphere/renderer/ui/ElementManager.java b/src/main/java/electrosphere/renderer/ui/ElementManager.java index 7040755f..d8c5594c 100644 --- a/src/main/java/electrosphere/renderer/ui/ElementManager.java +++ b/src/main/java/electrosphere/renderer/ui/ElementManager.java @@ -10,6 +10,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.joml.Vector2i; +import electrosphere.engine.Globals; import electrosphere.renderer.ui.elements.Window; import electrosphere.renderer.ui.elementtypes.ContainerElement; import electrosphere.renderer.ui.elementtypes.DraggableElement; @@ -90,6 +91,17 @@ public class ElementManager { elementList.add(window); } + /** + * Tries to navigate-close the window at a window string + * @param windowString The window string + */ + public void closeWindow(String windowString){ + Element windowEl = Globals.elementManager.getWindow(windowString); + if(windowEl instanceof Window){ + ((Window)windowEl).handleEvent(new NavigationEvent(NavigationEventType.BACKWARD)); + } + } + List getFocusableList(Element topLevel, List input){ if(topLevel instanceof FocusableElement){ input.add((FocusableElement)topLevel); diff --git a/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java b/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java index 925a1fbe..d69e71b8 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java @@ -20,24 +20,30 @@ import electrosphere.renderer.ui.events.DragEvent.DragEventType; import electrosphere.renderer.ui.events.Event; /** - * - * @author amaterasu + * A UI element that is a single, uninteractable image */ public class ImagePanel extends StandardElement implements DrawableElement, DraggableElement { + //Asset path for the model data that is used to draw the image panel public static String imagePanelModelPath; + //the path to the texture to use for this panel String texturePath; + //the material that links the texture to draw Material customMat = new Material(); + //tracks whether the texture has been loaded or not boolean hasLoadedTexture = false; + //the texture to use Texture texture = null; + //rendering data for positioning the model Vector3f texPosition = new Vector3f(0,0,0); Vector3f texScale = new Vector3f(1,1,0); Vector3f boxPosition = new Vector3f(); Vector3f boxDimensions = new Vector3f(); + //callbacks for different events this can accept DragEventCallback onDragStart; DragEventCallback onDrag; DragEventCallback onDragRelease; @@ -71,6 +77,15 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag } } + @Deprecated + /** + * Public constructor used for legacy usage + * @param x + * @param y + * @param width + * @param height + * @param texturePath + */ public ImagePanel(int x, int y, int width, int height, String texturePath){ super(); this.texturePath = texturePath; @@ -91,10 +106,18 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag this.internalHeight = height; } + /** + * Sets the texture for this image panel + * @param texture The texture to use + */ public void setTexture(Texture texture){ customMat.setTexturePointer(texture.getTexturePointer()); } + /** + * Gets the texture being used by this image panel + * @return The texture + */ public Texture getTexture(){ return texture; } @@ -122,6 +145,8 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); if(texture != null){ customMat.setTexturePointer(texture.getTexturePointer()); + } else if(this.texturePath != null){ + texture = Globals.assetManager.fetchTexture(this.texturePath); } //this call binds the screen as the "texture" we're rendering to @@ -145,16 +170,20 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag } } + //controls whether the image panel is visible or not public boolean visible = false; + @Override public boolean getVisible() { return visible; } + @Override public void setVisible(boolean draw) { this.visible = draw; } + @Override public boolean handleEvent(Event event){ boolean propagate = true; if(event instanceof DragEvent){ diff --git a/src/main/java/electrosphere/server/ai/creature/OpportunisticAttacker.java b/src/main/java/electrosphere/server/ai/creature/OpportunisticAttacker.java index fd224f21..591af8d9 100644 --- a/src/main/java/electrosphere/server/ai/creature/OpportunisticAttacker.java +++ b/src/main/java/electrosphere/server/ai/creature/OpportunisticAttacker.java @@ -15,6 +15,7 @@ import electrosphere.entity.types.item.ItemUtils; import electrosphere.server.ai.AI; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.utils.DataCellSearchUtils; +import electrosphere.util.MathUtils; import org.joml.Vector3f; import org.joml.Quaterniond; @@ -260,7 +261,7 @@ public class OpportunisticAttacker extends AI { Vector3d position = EntityUtils.getPosition(character); Vector3d targetPosition = EntityUtils.getPosition(target); Vector3d movementVector = new Vector3d(targetPosition).sub(position).normalize(); - Quaterniond movementQuaternion = new Quaterniond().rotationTo(new Vector3d(0,0,1), new Vector3d(movementVector.x,0,movementVector.z)).normalize(); + Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(movementVector.x,0,movementVector.z)).normalize(); CreatureUtils.setFacingVector(character, movementVector); EntityUtils.getRotation(character).set(movementQuaternion); } diff --git a/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java b/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java index 049a04aa..4b81e511 100644 --- a/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java +++ b/src/main/java/electrosphere/server/datacell/physics/ServerHitboxResolutionCallback.java @@ -9,15 +9,14 @@ import electrosphere.collision.collidable.Collidable; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; -import electrosphere.entity.state.hitbox.HitboxState; -import electrosphere.entity.state.hitbox.HitboxState.HitboxShapeStatus; -import electrosphere.entity.state.hitbox.HitboxState.HitboxType; +import electrosphere.entity.state.hitbox.HitboxCollectionState; +import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState; +import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxType; import electrosphere.entity.state.life.LifeUtils; import electrosphere.entity.state.movement.ProjectileTree; import electrosphere.entity.types.attach.AttachUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.item.ItemUtils; -import electrosphere.server.datacell.Realm; /** * Callback for managing collisions on the server @@ -28,13 +27,12 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba public void resolve(DContactGeom contactGeom, Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude) { Entity impactorParent = impactor.getParent(); Entity receiverParent = receiver.getParent(); - HitboxState impactorState = HitboxState.getHitboxState(impactorParent); - HitboxState receiverState = HitboxState.getHitboxState(receiverParent); + HitboxCollectionState impactorState = HitboxCollectionState.getHitboxState(impactorParent); + HitboxCollectionState receiverState = HitboxCollectionState.getHitboxState(receiverParent); DGeom impactorGeom = contactGeom.g1; DGeom receiverGeom = contactGeom.g2; - HitboxShapeStatus impactorShapeStatus = impactorState.getShapeStatus(impactorGeom); - HitboxShapeStatus receiverShapeStatus = receiverState.getShapeStatus(receiverGeom); - Realm receiverRealm = Globals.realmManager.getEntityRealm(receiverParent); + HitboxState impactorShapeStatus = impactorState.getShapeStatus(impactorGeom); + HitboxState receiverShapeStatus = receiverState.getShapeStatus(receiverGeom); //currently, impactor needs to be an item, and the receiver must not be an item @@ -47,6 +45,13 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba ; + if(impactorShapeStatus != null){ + impactorShapeStatus.setHadCollision(true); + } + if(receiverShapeStatus != null){ + receiverShapeStatus.setHadCollision(true); + } + if(isDamageEvent){ //if the entity is attached to is an item, we need to compare with the parent of the item //to make sure you don't stab yourself for instance @@ -58,6 +63,7 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba int damage = ItemUtils.getWeaponDataRaw(impactorParent).getDamage(); LifeUtils.getLifeState(receiverParent).damage(damage); if(!LifeUtils.getLifeState(receiverParent).isIsAlive()){ + System.out.println("ServerHitboxResolutionCallback - Unimplemented!!"); EntityUtils.getPosition(receiverParent).set(Globals.spawnPoint); LifeUtils.getLifeState(receiverParent).revive(); } @@ -75,6 +81,7 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba } LifeUtils.getLifeState(receiverParent).damage(damage); if(!LifeUtils.getLifeState(receiverParent).isIsAlive()){ + System.out.println("ServerHitboxResolutionCallback - Unimplemented!!"); EntityUtils.getPosition(receiverParent).set(Globals.spawnPoint); LifeUtils.getLifeState(receiverParent).revive(); } diff --git a/src/main/java/electrosphere/server/poseactor/PoseActor.java b/src/main/java/electrosphere/server/poseactor/PoseActor.java index e492c9fd..5ea0eaed 100644 --- a/src/main/java/electrosphere/server/poseactor/PoseActor.java +++ b/src/main/java/electrosphere/server/poseactor/PoseActor.java @@ -123,7 +123,7 @@ public class PoseActor { */ public void playAnimation(String animationName, int priority){ PoseModel model = Globals.assetManager.fetchPoseModel(modelPath); - if(model != null){ + if(model != null && model.getAnimation(animationName) != null){ double length = model.getAnimation(animationName).duration; ActorAnimationMask animMask = new ActorAnimationMask(priority, animationName, 0, length); for(Bone bone : model.bones){ @@ -243,7 +243,7 @@ public class PoseActor { Bone currentBone = model.boneMap.get(boneName); if(currentBone != null){ AxisAngle4f axisAngle = new AxisAngle4f(); - new Matrix4f(currentBone.final_transform).getRotation(axisAngle); + new Matrix4f(currentBone.getFinalTransform()).getRotation(axisAngle); Quaterniond rotation = new Quaterniond(axisAngle); rVal.set(rotation); } @@ -268,7 +268,7 @@ public class PoseActor { // model.updateNodeTransform(); Bone currentBone = model.boneMap.get(boneName); if(currentBone != null){ - Vector4d result = currentBone.final_transform.transform(new Matrix4d(currentBone.inverseBindPoseMatrix).invert().transform(new Vector4d(rVal.x,rVal.y,rVal.z,1))); + Vector4d result = new Matrix4d(currentBone.getFinalTransform()).transform(currentBone.getMOffset().invert().transform(new Vector4d(rVal.x,rVal.y,rVal.z,1))); // currentBone.inverseBindPoseMatrix rVal.x = (float)result.x; rVal.y = (float)result.y; diff --git a/src/main/java/electrosphere/server/poseactor/PoseModel.java b/src/main/java/electrosphere/server/poseactor/PoseModel.java index 586a87c6..352c5c06 100644 --- a/src/main/java/electrosphere/server/poseactor/PoseModel.java +++ b/src/main/java/electrosphere/server/poseactor/PoseModel.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.Map; import org.joml.Matrix4d; -import org.joml.Matrix4f; import org.joml.Vector3d; import org.lwjgl.PointerBuffer; import org.lwjgl.assimp.AIAnimation; @@ -119,10 +118,11 @@ public class PoseModel { if(currentBone != null){ // System.out.println("Applying to bone"); //T * S * R - currentBone.deform = new Matrix4d(); - currentBone.deform.translate(currentChannel.getCurrentPosition()); - currentBone.deform.rotate(currentChannel.getCurrentRotation()); - currentBone.deform.scale(new Vector3d(currentChannel.getCurrentScale())); + Matrix4d deform = new Matrix4d(); + deform.translate(currentChannel.getCurrentPosition()); + deform.rotate(currentChannel.getCurrentRotation()); + deform.scale(new Vector3d(currentChannel.getCurrentScale())); + currentBone.setDeform(deform); } } } @@ -168,29 +168,31 @@ public class PoseModel { */ void updateNodeTransform(AnimNode n, Map boneRotators, ActorStaticMorph staticMorph){ //grab parent transform if exists + Matrix4d parentTransform = new Matrix4d(); if(n.parent != null){ - n.transform = new Matrix4d(n.parent.transform); - } else { - n.transform = new Matrix4d(); + parentTransform = new Matrix4d(n.parent.getTransform()); } //if this is a bone, calculate the transform for the bone if(n.is_bone){ Bone target_bone = boneMap.get(n.id); - n.transform = n.transform.mul(target_bone.deform); + Matrix4d deformTransform = parentTransform.mul(target_bone.getDeform()); if(boneRotators.containsKey(target_bone.boneID)){ - n.transform.rotate(boneRotators.get(target_bone.boneID).getRotation()); + deformTransform.rotate(boneRotators.get(target_bone.boneID).getRotation()); } - Matrix4d bone_matrix = new Matrix4d(n.transform); + Matrix4d bone_matrix = new Matrix4d(deformTransform); if(staticMorph != null && staticMorph.getBoneTransforms(n.id) != null){ bone_matrix.mul(staticMorph.getBoneTransforms(n.id).getTransform()); - n.transform.mul(staticMorph.getBoneTransforms(n.id).getTransform()); } - bone_matrix.mul(target_bone.inverseBindPoseMatrix); + n.setTransform(deformTransform); + // + //Calculate final offset from initial bone + //https://stackoverflow.com/a/59869381 + bone_matrix.mul(target_bone.getMOffset()); bone_matrix = new Matrix4d(globalInverseTransform).mul(bone_matrix); - target_bone.final_transform = bone_matrix; + target_bone.setFinalTransform(bone_matrix); } else { //not a bone, so use transform directly from data - n.transform = n.transform.mul(electrosphere.util.Utilities.convertAIMatrix(n.raw_data.mTransformation())); + n.setTransform(parentTransform.mul(electrosphere.util.Utilities.convertAIMatrix(n.raw_data.mTransformation()))); } //update all children accordingly Iterator node_iterator = n.children.iterator(); diff --git a/src/main/java/electrosphere/util/MathUtils.java b/src/main/java/electrosphere/util/MathUtils.java new file mode 100644 index 00000000..079cf2f2 --- /dev/null +++ b/src/main/java/electrosphere/util/MathUtils.java @@ -0,0 +1,41 @@ +package electrosphere.util; + +import org.joml.Quaterniond; +import org.joml.Vector3d; +import org.joml.Vector3f; + +/** + * Utility functions for doing math + */ +public class MathUtils { + + + /** + * Gets the origin vector of the engine + * @return The origin vector + */ + public static Vector3d getOriginVector(){ + return new Vector3d(0,0,1); + } + + /** + * Gets the origin vector of the engine, in Vector3f format + * @return The origin vector + */ + public static Vector3f getOriginVectorf(){ + return new Vector3f(0,0,1); + } + + + /** + * Calculates the quaternion that rotates the origin vector to point from origin to destination + * @param originPoint The point to begin at + * @param destinationPoint The point end at + * @return The quaternion + */ + public static Quaterniond calculateRotationFromPointToPoint(Vector3d originPoint, Vector3d destinationPoint){ + return getOriginVector().rotationTo(new Vector3d(originPoint).sub(destinationPoint).normalize(), new Quaterniond()); + } + + +}