hitboxes, ui, bug fixes, network fixes, etc
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-06-14 13:58:10 -04:00
parent 4ae09e7afb
commit e671cda62d
86 changed files with 2313 additions and 773 deletions

View File

@ -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",

View File

@ -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
}

View File

@ -26,6 +26,7 @@
],
"files" : [
"Data/objects/floatingisland.json",
"Data/objects/testscene1objects.json"
"Data/objects/testscene1objects.json",
"Data/objects/debug_objects.json"
]
}

View File

@ -0,0 +1,18 @@
{
"objects" : [
{
"objectId" : "hitboxTester",
"hitboxData" : [
{
"type": "static_capsule",
"radius": 2,
"length": 5
}
],
"tokens": []
}
],
"files" : []
}

View File

@ -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": ""
}
]
}

Binary file not shown.

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
}
};

View File

@ -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)){

View File

@ -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

View File

@ -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);
}
/**

View File

@ -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;

View File

@ -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<HitboxState> hitboxes = new CopyOnWriteArrayList<HitboxState>();
CopyOnWriteArrayList<HitboxCollectionState> hitboxes = new CopyOnWriteArrayList<HitboxCollectionState>();
//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<HitboxState> getAllHitboxes(){
public CopyOnWriteArrayList<HitboxCollectionState> 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();

View File

@ -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);

View File

@ -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;
}

View File

@ -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<String, Control> controls;
List<Control> mainGameControlList = new LinkedList<Control>();
@ -248,10 +275,17 @@ public class ControlHandler {
List<Control> alwaysOnDebugControlList = new LinkedList<Control>();
List<Control> freeCameraControlList = new LinkedList<Control>();
/**
* Constructor
*/
ControlHandler(){
controls = new HashMap<String, Control>();
}
/**
* 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<Control> 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;
}

View File

@ -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"));

View File

@ -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);
}
}
}

View File

@ -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);
// }
// }});

View File

@ -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());
}

View File

@ -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);
}
}

View File

@ -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){

View File

@ -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;

View File

@ -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";

View File

@ -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);

View File

@ -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<Entity> 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<Entity> 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;

View File

@ -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<Entity> 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<Entity> 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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<EquipPoint> equipPoints){
private ClientEquipState(Entity parent, List<EquipPoint> 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<String> equippedPoints(){
public List<String> getEquippedPoints(){
return new LinkedList<String>(equipMap.keySet());
}
/**
* Gets the list of all equip points
* @return The list of all equip points
*/
public List<EquipPoint> 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){

View File

@ -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){

View File

@ -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

View File

@ -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<DGeom> geoms = new LinkedList<DGeom>();
//the map of bone -> hitbox shape in ode4j
Map<String,DGeom> hitboxGeomMap = new HashMap<String,DGeom>();
//the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision
Map<DGeom,HitboxState> geomStateMap = new HashMap<DGeom,HitboxState>();
//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<HitboxData> 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<DGeom> 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<String> 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;
}
}
}

View File

@ -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<String,DGeom> hitboxGeomMap = new HashMap<String,DGeom>();
//the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision
Map<DGeom,HitboxShapeStatus> geomBoneMap = new HashMap<DGeom,HitboxShapeStatus>();
//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<HitboxData> 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<DGeom> 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<String> 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;
}
}
}

View File

@ -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);
}
/**
* <p> Automatically generated </p>

View File

@ -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<EntityMessage> networkMessageQueue = new CopyOnWriteArrayList<EntityMessage>();
/**
* 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));
}

View File

@ -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;

View File

@ -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"));
}

View File

@ -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);
}
}
}

View File

@ -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++;

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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){

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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<Float> offsetRotation){
this.offsetRotation = offsetRotation;
}
/**
* Gets the equip classes that are whitelisted for this equip point
* @return the classes

View File

@ -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<String> shaderOverrideMeshList;
//??? TODO: investigate
Map<String,ShaderSet> shaderMap;
public List<String> getShaderOverrideMeshList(){

View File

@ -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<String> 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> 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<String> 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<HitboxData> getHitboxData(){
return this.hitboxData;
}
}

View File

@ -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<String,ObjectData> objectMap = new HashMap<String,ObjectData>();
public void putObject(String name, ObjectData type){
objectMap.put(name,type);
//the list of all object data
List<ObjectData> objectList = new LinkedList<ObjectData>();
/**
* Gets the list of all object types loaded into memory
* @return The list
*/
public List<ObjectData> 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);
}

View File

@ -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<ObjectData> objects;
//all children files to recursively parse for more object data
List<String> files;
/**
* Gets the list of all objects in this file
* @return the list
*/
public List<ObjectData> 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<String> getFiles(){
return files;
}

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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<Float> newValues = new LinkedList<Float>();
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);
}
/**

View File

@ -0,0 +1,8 @@
package electrosphere.menu.debug;
/**
* Utilities for dealing with imgui
*/
public class ImGuiUtils {
}

View File

@ -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);
}
}
}

View File

@ -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;
}});

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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<SynchronizationMessage> messagesToClear = new LinkedList<SynchronizationMessage>();
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;

View File

@ -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<ImGuiWindow> imGuiWindows = new CopyOnWriteArrayList<ImGuiWindow>();
@ -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;
}
/**

View File

@ -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<Bone> 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){

View File

@ -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<AnimNode> 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);
}
}

View File

@ -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){

View File

@ -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<AIVertexWeight> weightIterator = currentBoneData.mWeights().iterator();
while(weightIterator.hasNext()){
AIVertexWeight currentWeightData = weightIterator.next();

View File

@ -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<Integer,Float> weights = new HashMap<Integer,Float>();
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<Integer,Float> 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);
}
}

View File

@ -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++;
}

View File

@ -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<Mesh>();
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<Animation> 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<String,ActorBoneRotator> 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<AnimNode> 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<Animation> getAnimations(){
return this.animations;
}
}

View File

@ -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()){

View File

@ -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<ImGuiWindow> imGuiWindows = new CopyOnWriteArrayList<ImGuiWindow>();
/**
* 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;
}
}

View File

@ -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<FocusableElement> getFocusableList(Element topLevel, List<FocusableElement> input){
if(topLevel instanceof FocusableElement){
input.add((FocusableElement)topLevel);

View File

@ -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){

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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<String,ActorBoneRotator> 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<AnimNode> node_iterator = n.children.iterator();

View File

@ -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());
}
}