Compare commits

...

2 Commits

Author SHA1 Message Date
austin
3e5cade90e searchable voxel selection ui
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
2024-04-10 22:11:02 -04:00
austin
9d914e51c4 move window class 2024-04-06 15:59:22 -04:00
43 changed files with 926 additions and 463 deletions

View File

@ -3,145 +3,6 @@
{
"creatureId" : "goblin",
"hitboxes" : [
{
"type": "hurt",
"bone": "Bone.031",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Bone.012",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Bone.003",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Bone.010",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone.001",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone.014",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone",
"radius": 0.08
},
{
"type": "hurt",
"bone": "Bone.014",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone.019",
"radius": 0.04
}
],
"tokens" : [
"BLENDER_TRANSFORM",
"SENTIENT",
"ATTACKER",
"GRAVITY",
"TARGETABLE",
"CAN_EQUIP"
],
"movementSystems" : [
{
"type" : "GROUND",
"acceleration" : 13.0,
"maxVelocity" : 1.8,
"animationStartup" : {
"name" : "Walk",
"length" : 1,
"loops" : false
},
"animationLoop" : {
"name" : "Walk",
"length" : 1,
"loops" : false
},
"animationWindDown" : {
"name" : "Armature|WalkForwardStart",
"length" : 1,
"loops" : false
},
"sprintSystem" : {
"maxVelocity" : 3.8,
"staminaMax" : 500,
"animationStartUp" : {
"name" : "Run",
"length" : 1,
"loops" : false
},
"animationMain" : {
"name" : "Run",
"length" : 1,
"loops" : false
}
}
}
],
"collidable" : {
"type" : "CYLINDER",
"dimension1" : 0.1,
"dimension2" : 0.2,
"dimension3" : 0.1,
"rotX": 0,
"rotY": 0,
"rotZ": 0,
"rotW": 1,
"offsetX" : 0,
"offsetY" : 0.2,
"offsetZ" : 0
},
"attackMoves" : [
{
"type" : "MELEE_WEAPON_SWING_ONE_HAND",
"animationName" : "Armature|SwingWeapon",
"damageStartFrame" : 10,
"damageEndFrame" : 30,
"nextMoveId" : "",
"nextAttackMoveWindowStart" : 0,
"nextAttackMoveWindowEnd" : 1,
"movementStart" : 0,
"movementEnd" : 0,
"movementGoal" : 0,
"initialMove" : true
}
],
"healthSystem" : {
"maxHealth" : 100,
"onDamageIFrames" : 30
},
"modelPath" : "Models/goblin1.fbx"
},
{
"creatureId" : "CUBE_MAN",
@ -176,59 +37,6 @@
"onDamageIFrames" : 30
},
"modelPath" : "Models/unitcube.fbx"
},
{
"creatureId" : "Deer",
"hitboxes" : [
{
"type": "hurt",
"bone": "Bone",
"radius": 0.04
}
],
"tokens" : [
"BLENDER_TRANSFORM",
"GRAVITY"
],
"movementSystems" : [
{
"type" : "GROUND",
"acceleration" : 0.001,
"maxVelocity" : 0.025
}
],
"collidable" : {
"type" : "CYLINDER",
"dimension1" : 0.1,
"dimension2" : 0.2,
"dimension3" : 0.1,
"rotX": 0,
"rotY": 0,
"rotZ": 0,
"rotW": 1,
"offsetX" : 0,
"offsetY" : 0.2,
"offsetZ" : 0
},
"healthSystem" : {
"maxHealth" : 100,
"onDamageIFrames" : 30
},
"modelPath" : "Models/deer1.fbx"
}
@ -236,6 +44,8 @@
"files" : [
"Data/creatures/human.json",
"Data/creatures/elf.json",
"Data/creatures/goblin.json",
"Data/creatures/animals.json",
"Data/creatures/test.json"
]
}

View File

@ -0,0 +1,44 @@
{
"creatures" : [
{
"creatureId" : "Deer",
"hitboxes" : [
{
"type": "hurt",
"bone": "Bone",
"radius": 0.04
}
],
"tokens" : [
"BLENDER_TRANSFORM",
"GRAVITY"
],
"movementSystems" : [
{
"type" : "GROUND",
"acceleration" : 0.001,
"maxVelocity" : 0.025
}
],
"collidable" : {
"type" : "CYLINDER",
"dimension1" : 0.1,
"dimension2" : 0.2,
"dimension3" : 0.1,
"rotX": 0,
"rotY": 0,
"rotZ": 0,
"rotW": 1,
"offsetX" : 0,
"offsetY" : 0.2,
"offsetZ" : 0
},
"healthSystem" : {
"maxHealth" : 100,
"onDamageIFrames" : 30
},
"modelPath" : "Models/deer1.fbx"
}
],
"files" : []
}

View File

@ -0,0 +1,132 @@
{
"creatures" : [
{
"creatureId" : "goblin",
"hitboxes" : [
{
"type": "hurt",
"bone": "Bone.031",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Bone.012",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Bone.003",
"radius": 0.04
},
{
"type": "hurt",
"bone": "Bone.010",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone.001",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone.014",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone",
"radius": 0.08
},
{
"type": "hurt",
"bone": "Bone.014",
"radius": 0.06
},
{
"type": "hurt",
"bone": "Bone.019",
"radius": 0.04
}
],
"tokens" : [
"BLENDER_TRANSFORM",
"SENTIENT",
"ATTACKER",
"GRAVITY",
"TARGETABLE",
"CAN_EQUIP"
],
"movementSystems" : [
{
"type" : "GROUND",
"acceleration" : 13.0,
"maxVelocity" : 1.8,
"animationStartup" : {
"name" : "Walk",
"length" : 1,
"loops" : false
},
"animationLoop" : {
"name" : "Walk",
"length" : 1,
"loops" : false
},
"animationWindDown" : {
"name" : "Armature|WalkForwardStart",
"length" : 1,
"loops" : false
},
"sprintSystem" : {
"maxVelocity" : 3.8,
"staminaMax" : 500,
"animationStartUp" : {
"name" : "Run",
"length" : 1,
"loops" : false
},
"animationMain" : {
"name" : "Run",
"length" : 1,
"loops" : false
}
}
}
],
"collidable" : {
"type" : "CYLINDER",
"dimension1" : 0.1,
"dimension2" : 0.2,
"dimension3" : 0.1,
"rotX": 0,
"rotY": 0,
"rotZ": 0,
"rotW": 1,
"offsetX" : 0,
"offsetY" : 0.2,
"offsetZ" : 0
},
"attackMoves" : [
{
"type" : "MELEE_WEAPON_SWING_ONE_HAND",
"animationName" : "Armature|SwingWeapon",
"damageStartFrame" : 10,
"damageEndFrame" : 30,
"nextMoveId" : "",
"nextAttackMoveWindowStart" : 0,
"nextAttackMoveWindowEnd" : 1,
"movementStart" : 0,
"movementEnd" : 0,
"movementGoal" : 0,
"initialMove" : true
}
],
"healthSystem" : {
"maxHealth" : 100,
"onDamageIFrames" : 30
},
"modelPath" : "Models/goblin1.fbx"
}
],
"files" : []
}

View File

@ -1,163 +1,5 @@
{
"creatures" : [
{
"creatureId" : "tank",
"hitboxes" : [
{
"type": "hurt",
"bone": "Bone",
"radius": 0.04
}
],
"tokens" : [
"ATTACKER",
"GRAVITY",
"TARGETABLE",
"OUTLINE",
"PLAYABLE"
],
"visualAttributes" : [
],
"movementSystems" : [
{
"type" : "GROUND",
"acceleration" : 10.0,
"maxVelocity" : 0.035,
"animationStartup" : {
"name" : "Idle1",
"length" : 1,
"loops" : false
},
"animationLoop" : {
"name" : "Idle1",
"length" : 1,
"loops" : false
},
"animationWindDown" : {
"name" : "Idle1",
"length" : 1,
"loops" : false
}
},
{
"type" : "FALL",
"animationFall" : {
"name" : "Idle1",
"length" : 1,
"loops" : true
},
"animationLand" : {
"name" : "Idle1",
"length" : 1,
"loops" : true
}
}
],
"rotatorSystem" : {
"rotatorItems" : [
{
"boneName" : "Bone",
"constraints" : [
{
"followsView" : true,
"followsBone" : false,
"parentBone" : "",
"allowedMarginPitch" : 0.2
}
]
}
]
},
"equipPoints" : [
],
"collidable" : {
"type" : "CYLINDER",
"dimension1" : 0.1,
"dimension2" : 0.45,
"dimension3" : 0.1,
"rotX": 0,
"rotY": 0,
"rotZ": 0,
"rotW": 1,
"offsetX" : 0,
"offsetY" : 0.45,
"offsetZ" : 0
},
"attackMoves" : [
],
"healthSystem" : {
"maxHealth" : 100,
"onDamageIFrames" : 30
},
"modelPath" : "Models/tank1.fbx"
},
{
"creatureId" : "fighter",
"hitboxes" : [
{
"type": "hurt",
"bone": "Bone",
"radius": 0.6
}
],
"tokens" : [
"SHOOTER",
"GRAVITY",
"TARGETABLE",
"OUTLINE",
"PLAYABLE"
],
"visualAttributes" : [
],
"movementSystems" : [
{
"type" : "AIRPLANE",
"acceleration" : 10.0,
"maxVelocity" : 0.035,
"minVelocity": 0.001,
"maxRotationSpeed" : 0.01
}
],
"rotatorSystem" : {
"rotatorItems" : [
]
},
"equipPoints" : [
],
"collidable" : {
"type" : "CYLINDER",
"dimension1" : 0.1,
"dimension2" : 0.45,
"dimension3" : 0.1,
"offsetX" : 0,
"offsetY" : 0.45,
"offsetZ" : 0
},
"attackMoves" : [
{
"attackMoveId" : "ProjectileFire",
"type" : "PROJECTILE",
"windupAnimationName" : "Idle1",
"holdAnimationName" : "Idle1",
"attackAnimationName" : "Idle1",
"damageStartFrame" : 1,
"damageEndFrame" : 2,
"firesProjectile" : true,
"nextMoveId" : "",
"nextAttackMoveWindowStart" : 0,
"nextAttackMoveWindowEnd" : 1,
"movementStart" : 0,
"movementEnd" : 0,
"movementGoal" : 0,
"initialMove" : true
}
],
"healthSystem" : {
"maxHealth" : 100,
"onDamageIFrames" : 30
},
"modelPath" : "Models/f15.fbx"
}
],
"files" : []
}

View File

@ -6,14 +6,16 @@
},
{
"id" : 1,
"name" : "dirt"
"name" : "dirt",
"texture" : "/Textures/Ground/Dirt1.png"
},
{
"id" : 2,
"name" : "grass",
"ambientFoliage" : [
"Green Grass"
]
],
"texture" : "/Textures/Ground/GrassTileable.png"
}
]
}

View File

@ -0,0 +1,9 @@
@page mainui Main UI Framework
It's kind of a mess. One of the main libraries underpinning the ui framework is Yoga. It handles laying out all elements in a flexbox fashion.
Grafting yoga onto the initial implementation after the fact has confused many parts of the classes.
A chief pain point currently is handing position, dimensions (width, height), and the different version of each.
IE there's a position relative to the screen, position relative to the parent, position relative to the yoga parent, etc.
Yoga typically returns the position of the yoga element relative to its parent yoga element.
The original implementation of the ui framework had absolute positions stored in all elements at all times.
This is something that will need to be untangled in the future most likely.

View File

@ -1,4 +1,5 @@
@page uiarch UI Architecture
[TOC]
- @subpage mainui
- @subpage imgui

View File

@ -200,8 +200,11 @@ Level loading/saving + Basic Editor
- Spin up voxel level (think arena mode)
- Save voxel level
# TODO
(04/10/2024)
Level loading/saving + Basic Editor
- Basic editor functionality
- Menu of types of entities to spawn
- Button to spawn them at cursor
UI Work
- Make ui feel more responsive, whatever that means (answer is make hover-over styling work lol)
@ -209,10 +212,10 @@ UI Work
- Need to figure out style-wise what we want to do here (generally minimalist)
- Fix scrollable handling
Level loading/saving + Basic Editor
- Basic editor functionality
- Menu of types of entities to spawn
- Button to spawn them at cursor
Terrain editing UI
- Menu to select palette to generate, populated based on data
# TODO
More Debug menus
- Screen that shows the overall status of client scene
@ -336,6 +339,8 @@ Upgrade terrain editing user experience further
Upgrade terrain generation algorithms
- This one should be an ongoing process in general as it is a matter of taste
- Make a route hard coded that throws you straight into a generated world
- This makes it easier to tweak algo and immediately get results

View File

@ -97,7 +97,7 @@ import electrosphere.menu.WindowUtils;
import electrosphere.menu.ingame.MenuGeneratorsInGame;
import electrosphere.menu.ingame.MenuGeneratorsInventory;
import electrosphere.menu.mainmenu.MenuGeneratorsDebug;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.KeyboardEvent;
import electrosphere.renderer.ui.events.MenuEvent;
@ -1252,10 +1252,9 @@ public class ControlHandler {
for(String currentKey : typeKeybinds){
typingControlList.add(controls.get(currentKey));
controls.get(currentKey).setOnPress(new ControlMethod(){public void execute(){
Globals.elementManager.fireEvent(
new KeyboardEvent(convertKeycodeToName(controls.get(currentKey).keyValue)),
Globals.elementManager.getFocusedElement().getInternalX() + 1,
Globals.elementManager.getFocusedElement().getInternalY() + 1
Globals.elementManager.fireEventNoPosition(
new KeyboardEvent(convertKeycodeToName(controls.get(currentKey).keyValue)),
Globals.elementManager.getFocusedElement()
);
// MenuCallbacks.menuHandleKeypress(Globals.currentMenu,currentKey);
}});

View File

@ -37,6 +37,7 @@ import electrosphere.entity.Entity;
import electrosphere.entity.Scene;
import electrosphere.entity.types.hitbox.HitboxManager;
import electrosphere.game.config.UserSettings;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.game.server.structure.virtual.StructureManager;
import electrosphere.game.server.world.MacroData;
import electrosphere.game.server.world.ServerWorldData;
@ -362,6 +363,9 @@ public class Globals {
//the creature the player camera will orbit and will receive controlHandler movementTree updates
public static Entity playerEntity;
//client current selected voxel type
public static VoxelType clientSelectedVoxelType = null;
//skybox entity
public static Entity skybox;

View File

@ -36,7 +36,7 @@ import electrosphere.menu.WindowUtils;
import electrosphere.menu.mainmenu.MenuGeneratorsMultiplayer;
import electrosphere.net.NetUtils;
import electrosphere.net.client.ClientNetworking;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.server.datacell.EntityDataCellMapper;
public class ClientLoading {

View File

@ -15,7 +15,7 @@ import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.server.content.ServerContentManager;
import electrosphere.server.fluid.generation.ArenaFluidGenerator;
import electrosphere.server.fluid.manager.ServerFluidManager;

View File

@ -11,7 +11,7 @@ import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.server.content.ServerContentManager;
import electrosphere.server.fluid.generation.ArenaFluidGenerator;
import electrosphere.server.fluid.manager.ServerFluidManager;

View File

@ -10,7 +10,7 @@ import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.server.content.ServerContentManager;
import electrosphere.server.saves.SaveUtils;

View File

@ -26,8 +26,16 @@ public class EntityCreationUtils {
Entity rVal = EntityUtils.spawnSpatialEntity();
//register to global entity id lookup table
EntityLookupUtils.registerServerEntity(rVal);
//position entity
ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);
//assign to realm
Globals.realmManager.mapEntityToRealm(rVal, realm);
//init data cell if it doesn't exist
ServerDataCell cell = realm.getDataCellManager().getDataCellAtPoint(position);
if(cell == null){
//initialize server datacell tracking of this entity
cell = realm.getDataCellManager().tryCreateCellAtPoint(position);
}
//register to entity data cell mapper
realm.getEntityDataCellMapper().registerEntity(rVal, cell);
//enable behavior tree tracking
ServerBehaviorTreeUtils.registerEntity(rVal);

View File

@ -15,12 +15,19 @@ import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
public class ServerEntityUtils {
/**
* Called when the creature is first spawned to serialize to all people in its initial chunk
* Called when the creature is first spawned to serialize to all people in its initial chunk.
* <p>
* !!NOTE!!: This function must be called after the entity has fully been created.
* The initializeServerSideEntity logic requires knowing the type of entity (creature, foliage, etc)
* which is typically set further in the function than the initial "spawnServerEntity" that returns
* the actual Entity() object.
* </p>
* @param entity
* @param position
*/
public static void initiallyPositionEntity(Realm realm, Entity entity, Vector3d position){
Globals.realmManager.mapEntityToRealm(entity, realm);
//reposition entity, if the position isn't correct then it will spawn at 0,0,0 when the synchronization part is called
CollisionObjUtils.serverPositionCharacter(entity, position);
//get current server data cell
ServerDataCell cell = realm.getDataCellManager().getDataCellAtPoint(position);
if(cell != null){
@ -32,10 +39,6 @@ public class ServerEntityUtils {
//initialize server datacell tracking of this entity
realm.initializeServerSideEntity(entity, cell);
}
//register to entity data cell mapper
realm.getEntityDataCellMapper().registerEntity(entity, cell);
//reposition entity
CollisionObjUtils.serverPositionCharacter(entity, position);
}
/**

View File

@ -18,6 +18,7 @@ import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.attack.AttackTree;
import electrosphere.entity.state.attack.ServerAttackTree;
@ -279,9 +280,9 @@ public class CreatureUtils {
}
}
//variants
CreatureTemplate storedTemplate = new CreatureTemplate(rawType.getCreatureId());
if(rawType.getVisualAttributes() != null){
ActorStaticMorph staticMorph = null;
CreatureTemplate storedTemplate = new CreatureTemplate(rawType.getCreatureId());
for(VisualAttribute attributeType : rawType.getVisualAttributes()){
if(attributeType.getType().equals("remesh")){
if(attributeType.getVariants() != null && attributeType.getVariants().size() > 0){
@ -342,11 +343,11 @@ public class CreatureUtils {
}
}
}
//set race
storedTemplate.creatureType = rawType.getCreatureId();
//store template on creature
CreatureUtils.setCreatureTemplate(rVal, storedTemplate);
}
//set race
storedTemplate.creatureType = rawType.getCreatureId();
//store template on creature
CreatureUtils.setCreatureTemplate(rVal, storedTemplate);
//rotator system
if(rawType.getRotatorSystem() != null){
RotatorSystem system = rawType.getRotatorSystem();
@ -575,9 +576,9 @@ public class CreatureUtils {
}
}
//variants
CreatureTemplate storedTemplate = new CreatureTemplate(rawType.getCreatureId());
if(rawType.getVisualAttributes() != null){
ActorStaticMorph staticMorph = null;
CreatureTemplate storedTemplate = new CreatureTemplate(rawType.getCreatureId());
for(VisualAttribute attributeType : rawType.getVisualAttributes()){
if(attributeType.getType().equals("remesh")){
if(attributeType.getVariants() != null && attributeType.getVariants().size() > 0){
@ -639,11 +640,11 @@ public class CreatureUtils {
}
}
}
//set race
storedTemplate.creatureType = rawType.getCreatureId();
//store template on creature
CreatureUtils.setCreatureTemplate(rVal, storedTemplate);
}
//set race
storedTemplate.creatureType = rawType.getCreatureId();
//store template on creature
CreatureUtils.setCreatureTemplate(rVal, storedTemplate);
//rotator system
if(rawType.getRotatorSystem() != null){
RotatorSystem system = rawType.getRotatorSystem();
@ -674,6 +675,14 @@ public class CreatureUtils {
EntityUtils.setEntitySubtype(rVal, type);
CreatureUtils.setFacingVector(rVal, new Vector3d(0,0,1));
rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
//position entity
//this needs to be called at the end of this function.
//Burried underneath this is function call to initialize a server side entity.
//The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);
return rVal;
}

View File

@ -10,6 +10,7 @@ import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.ambientaudio.ClientAmbientAudioTree;
import electrosphere.entity.state.idle.IdleTree;
import electrosphere.entity.types.collision.CollisionObjUtils;
@ -114,6 +115,14 @@ public class FoliageUtils {
rVal.putData(EntityDataStrings.FOLIAGE_SEED, seed);
rVal.putData(EntityDataStrings.FOLIAGE_IS_SEEDED, true);
rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
//position entity
//this needs to be called at the end of this function.
//Burried underneath this is function call to initialize a server side entity.
//The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);
return rVal;
}

View File

@ -20,6 +20,7 @@ import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.collidable.ClientCollidableTree;
import electrosphere.entity.state.collidable.ServerCollidableTree;
import electrosphere.entity.state.gravity.ClientGravityTree;
@ -185,6 +186,16 @@ public class ItemUtils {
// rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(0.005f,0.005f,0.005f));
// rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().identity().rotateY((float)(-Math.PI/2)).rotateZ(-(float)(Math.PI/2)));
ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.ITEM);
//position entity
//this needs to be called at the end of this function.
//Burried underneath this is function call to initialize a server side entity.
//The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);
return rVal;
}

View File

@ -16,6 +16,7 @@ import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.collidable.ClientCollidableTree;
import electrosphere.entity.state.collidable.ServerCollidableTree;
@ -133,15 +134,6 @@ public class ObjectUtils {
ObjectData rawType = Globals.gameConfigCurrent.getObjectTypeLoader().getObject(type);
Entity rVal = EntityCreationUtils.createServerEntity(realm, position);
EntityCreationUtils.makeEntityPoseable(rVal, rawType.getModelPath());
//register to global entity id lookup table
EntityLookupUtils.registerServerEntity(rVal);
//register to data cell
ServerDataCell entityDataCell = realm.getDataCellManager().tryCreateCellAtPoint(position);
entityDataCell.getScene().registerEntity(rVal);
//maps this entity to its realm
Globals.realmManager.mapEntityToRealm(rVal, realm);
//enable behavior tree tracking
ServerBehaviorTreeUtils.registerEntity(rVal);
PoseActor creatureActor = EntityUtils.getPoseActor(rVal);
//forward-searching tokens
@ -213,6 +205,15 @@ public class ObjectUtils {
//idle tree & generic stuff all objects have
rVal.putData(EntityDataStrings.TREE_IDLE, new IdleTree(rVal));
rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
//position entity
//this needs to be called at the end of this function.
//Burried underneath this is function call to initialize a server side entity.
//The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);
return rVal;
}

View File

@ -13,6 +13,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.movement.ProjectileTree;
import electrosphere.entity.types.hitbox.HitboxUtils;
import electrosphere.entity.types.hitbox.HitboxUtils.HitboxPositionCallback;
@ -127,6 +128,15 @@ public class ProjectileUtils {
return EntityUtils.getPosition(rVal);
}
}, rawType.getHitboxRadius(), false, filter);
//position entity
//this needs to be called at the end of this function.
//Burried underneath this is function call to initialize a server side entity.
//The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,initialPosition);
return rVal;
}

View File

@ -66,6 +66,14 @@ public class TerrainChunk {
}
//position entity
//this needs to be called at the end of this function.
//Burried underneath this is function call to initialize a server side entity.
//The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);
return rVal;
}

View File

@ -1,5 +1,6 @@
package electrosphere.game.data.creature.type;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -26,6 +27,14 @@ public class CreatureTypeLoader {
return creatureMap.get(name);
}
/**
* Gets the collection of all creature data
* @return the collection of all creature data
*/
public Collection<CreatureType> getCreatures(){
return creatureMap.values();
}
public List<String> getPlayableRaces(){
List<String> races = null;
playableRaceLock.acquireUninterruptibly();

View File

@ -12,6 +12,8 @@ public class VoxelType {
String name;
//any ambient foliage that can be placed on this voxel type
List<String> ambientFoliage;
//the texture for the voxel type
String texture;
/**
* Gets the id of the voxel type
@ -36,4 +38,12 @@ public class VoxelType {
public List<String> getAmbientFoliage(){
return ambientFoliage;
}
/**
* Gets the texture of this voxel types
* @return the texture
*/
public String getTexture(){
return texture;
}
}

View File

@ -31,7 +31,7 @@ public class LoggerInterface {
loggerAuth = new Logger(LogLevel.WARNING);
loggerDB = new Logger(LogLevel.WARNING);
loggerAudio = new Logger(LogLevel.WARNING);
loggerUI = new Logger(LogLevel.INFO);
loggerUI = new Logger(LogLevel.DEBUG);
loggerStartup.INFO("Initialized loggers");
}
}

View File

@ -23,7 +23,6 @@ import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.ActorStaticMorph;
import electrosphere.renderer.actor.ActorUtils;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.ActorPanel;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Div;
@ -33,6 +32,7 @@ import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.ScrollableContainer;
import electrosphere.renderer.ui.elements.Slider;
import electrosphere.renderer.ui.elements.TextInput;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ClickableElement;
import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.elementtypes.NavigableElement.NavigationEventCallback;

View File

@ -11,5 +11,7 @@ public class WindowStrings {
public static final String WINDOW_ITEM_DRAG_CONTAINER = "itemDragContainer";
public static final String WINDOW_CHARACTER = "windowCharacter";
public static final String WINDOW_DEBUG = "windowDebug";
public static final String LEVEL_EDTIOR_SIDE_PANEL = "levelEditor";
public static final String VOXEL_TYPE_SELECTION = "voxelTypeSelection";
}

View File

@ -6,8 +6,8 @@ import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.menu.ingame.MenuGeneratorsInventory;
import electrosphere.menu.mainmenu.MenuGeneratorsTitleMenu;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ContainerElement;
import electrosphere.renderer.ui.elementtypes.DrawableElement;
import electrosphere.renderer.ui.elementtypes.Element;
@ -72,6 +72,16 @@ public class WindowUtils {
Globals.elementManager.focusFirstElement();
}
/**
* Tries to close a window
* @param window the window to close
*/
public static void closeWindow(String window){
Element windowEl = Globals.elementManager.getWindow(window);
recursiveSetVisible(windowEl, false);
Globals.elementManager.unregisterWindow(window);
}
/**

View File

@ -18,7 +18,6 @@ import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.ActorStaticMorph;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.ImagePanel;
@ -27,6 +26,7 @@ import electrosphere.renderer.ui.elements.ScrollableContainer;
import electrosphere.renderer.ui.elements.Slider;
import electrosphere.renderer.ui.elements.TextInput;
import electrosphere.renderer.ui.elements.VirtualScrollable;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ClickableElement;
import electrosphere.renderer.ui.elementtypes.NavigableElement.NavigationEventCallback;
import electrosphere.renderer.ui.elementtypes.ValueElement.ValueChangeEventCallback;
@ -113,17 +113,22 @@ public class MenuGeneratorsInGame {
//checking macro data is a poor man's check for whether we're arena or full gamemode
// if(Globals.server != null && Globals.macroData == null){
//label 4 (debug)
Button debugButton = new Button();
Label debugLabel = new Label(1.0f);
debugLabel.setText("Debug");
debugButton.addChild(debugLabel);
div.addChild(debugButton);
debugButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
WindowUtils.replaceWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN, createInGameDebugMainMenu());
return false;
}});
Button debugButton = new Button();
Label debugLabel = new Label(1.0f);
debugLabel.setText("Debug");
debugButton.addChild(debugLabel);
div.addChild(debugButton);
debugButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
WindowUtils.replaceWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN, createInGameDebugMainMenu());
return false;
}});
// }
div.addChild(Button.createButton("Open Level Editor Tools", new ClickableElement.ClickEventCallback() {public boolean execute(ClickEvent event){
WindowUtils.replaceWindow(WindowStrings.LEVEL_EDTIOR_SIDE_PANEL,MenuGeneratorsLevelEditor.createLevelEditorSidePanel());
return false;
}}));
rVal.applyYoga();
return rVal;

View File

@ -19,6 +19,7 @@ import electrosphere.menu.WindowUtils;
import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.ImagePanel;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.elementtypes.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.elementtypes.DraggableElement.DragEventCallback;
@ -28,7 +29,6 @@ import electrosphere.renderer.ui.elementtypes.ValueElement.ValueChangeEventCallb
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.DragEvent;
import electrosphere.renderer.ui.events.NavigationEvent;
import electrosphere.renderer.ui.Window;
public class MenuGeneratorsInventory {

View File

@ -0,0 +1,159 @@
package electrosphere.menu.ingame;
import java.util.Random;
import org.joml.Vector3d;
import org.lwjgl.util.yoga.Yoga;
import electrosphere.engine.Globals;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.foliage.FoliageUtils;
import electrosphere.game.data.creature.type.CreatureType;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.logger.LoggerInterface;
import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.VirtualScrollable;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.elementtypes.NavigableElement.NavigationEventCallback;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.NavigationEvent;
import electrosphere.server.datacell.Realm;
/**
* Menu generators for level editor
*/
public class MenuGeneratorsLevelEditor {
static Window rVal;
//width of the side panel
static final int SIDE_PANEL_WIDTH = 500;
/**
* Creates the level editor side panel top view
* @return
*/
public static Window createLevelEditorSidePanel(){
//setup window
rVal = new Window(0,0,SIDE_PANEL_WIDTH,Globals.WINDOW_HEIGHT,true);
rVal.setParentAlignContent(Yoga.YGAlignFlexEnd);
rVal.setParentJustifyContent(Yoga.YGJustifyFlexEnd);
rVal.setParentAlignItem(Yoga.YGAlignFlexEnd);
rVal.setAlignContent(Yoga.YGAlignFlexStart);
rVal.setAlignItems(Yoga.YGAlignFlexStart);
rVal.setJustifyContent(Yoga.YGJustifyFlexStart);
//scrollable
VirtualScrollable scrollable = new VirtualScrollable(SIDE_PANEL_WIDTH, Globals.WINDOW_HEIGHT);
rVal.addChild(scrollable);
rVal.setOnNavigationCallback(new NavigationEventCallback() {public boolean execute(NavigationEvent event){
WindowUtils.closeWindow(WindowStrings.LEVEL_EDTIOR_SIDE_PANEL);
return false;
}});
fillInDefaultContent(scrollable);
rVal.applyYoga();
return rVal;
}
/**
* Fills in the default content for the scrollable
* @param scrollable
*/
private static void fillInDefaultContent(VirtualScrollable scrollable){
scrollable.clearChildren();
//close button
scrollable.addChild(Button.createButton("Close", new ClickEventCallback() {public boolean execute(ClickEvent event){
WindowUtils.closeWindow(WindowStrings.LEVEL_EDTIOR_SIDE_PANEL);
return false;
}}));
//spawn creature button
scrollable.addChild(Button.createButton("Spawn Creature", new ClickEventCallback() {public boolean execute(ClickEvent event){
fillInSpawnCreatureContent(scrollable);
return false;
}}));
//spawn foliage button
scrollable.addChild(Button.createButton("Spawn Foliage", new ClickEventCallback() {public boolean execute(ClickEvent event){
fillInSpawnFoliageContent(scrollable);
return false;
}}));
//select voxel button
scrollable.addChild(Button.createButton("Select Voxel Type", new ClickEventCallback() {public boolean execute(ClickEvent event){
WindowUtils.replaceWindow(WindowStrings.VOXEL_TYPE_SELECTION,MenuGeneratorsTerrainEditing.createVoxelTypeSelectionPanel());
return false;
}}));
rVal.applyYoga();
}
/**
* Fills in the content for spawning entities
* @param scrollable
*/
private static void fillInSpawnCreatureContent(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 creatures
for(CreatureType data : Globals.gameConfigCurrent.getCreatureTypeLoader().getCreatures()){
//spawn creature button
scrollable.addChild(Button.createButton("Spawn " + data.getCreatureId(), new ClickEventCallback() {public boolean execute(ClickEvent event){
LoggerInterface.loggerEngine.INFO("spawn " + data.getCreatureId() + "!");
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);
CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, data.getCreatureId(), null);
return false;
}}));
}
rVal.applyYoga();
}
private static void fillInSpawnFoliageContent(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(FoliageType data : Globals.gameConfigCurrent.getFoliageMap().getFoliageList()){
//spawn foliage button
scrollable.addChild(Button.createButton("Spawn " + data.getName(), new ClickEventCallback() {public boolean execute(ClickEvent event){
LoggerInterface.loggerEngine.INFO("spawn " + data.getName() + "!");
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);
FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, data.getName(), new Random().nextLong());
return false;
}}));
}
rVal.applyYoga();
}
}

View File

@ -0,0 +1,144 @@
package electrosphere.menu.ingame;
import java.util.List;
import org.lwjgl.util.yoga.Yoga;
import electrosphere.engine.Globals;
import electrosphere.game.data.voxel.VoxelData;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.ImagePanel;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.TextInput;
import electrosphere.renderer.ui.elements.VirtualScrollable;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.elementtypes.KeyEventElement.KeyboardEventCallback;
import electrosphere.renderer.ui.elementtypes.NavigableElement.NavigationEventCallback;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.KeyboardEvent;
import electrosphere.renderer.ui.events.NavigationEvent;
/**
* Menu generators for terrain editing controls
*/
public class MenuGeneratorsTerrainEditing {
static Window rVal;
//text input
static final int TEXT_INPUT_HEIGHT = 50;
static final int TEXT_INPUT_WIDTH = 200;
//single voxel button
static final int VOXEL_BUTTON_WIDTH = 90;
static final int VOXEL_BUTTON_HEIGHT = 90;
static final int VOXEL_BUTTON_TEXTURE_DIM = 70;
static final int MARGIN_EACH_SIDE = 5;
//voxel selection
static final int VOXEL_SCROLLABLE_WIDTH = VOXEL_BUTTON_WIDTH * 5;
static final int VOXEL_SCROLLABLE_HEIGHT = VOXEL_BUTTON_HEIGHT * 5;
//width of the side panel
static final int WINDOW_WIDTH = VOXEL_SCROLLABLE_WIDTH;
static final int WINDOW_HEIGHT = VOXEL_SCROLLABLE_HEIGHT + TEXT_INPUT_HEIGHT;
/**
* Creates the level editor side panel top view
* @return
*/
public static Window createVoxelTypeSelectionPanel(){
//setup window
rVal = new Window(0,0,WINDOW_WIDTH,WINDOW_HEIGHT,true);
rVal.setParentAlignContent(Yoga.YGAlignCenter);
rVal.setParentJustifyContent(Yoga.YGJustifyCenter);
rVal.setParentAlignItem(Yoga.YGAlignCenter);
rVal.setAlignContent(Yoga.YGAlignCenter);
rVal.setAlignItems(Yoga.YGAlignCenter);
rVal.setJustifyContent(Yoga.YGJustifyCenter);
rVal.setFlexDirection(Yoga.YGFlexDirectionColumn);
//scrollable that contains all the voxel types
VirtualScrollable scrollable = new VirtualScrollable(VOXEL_SCROLLABLE_WIDTH, VOXEL_SCROLLABLE_HEIGHT);
scrollable.setFlexDirection(Yoga.YGFlexDirectionRow);
scrollable.setAlignItems(Yoga.YGAlignFlexStart);
rVal.setOnNavigationCallback(new NavigationEventCallback() {public boolean execute(NavigationEvent event){
WindowUtils.closeWindow(WindowStrings.LEVEL_EDTIOR_SIDE_PANEL);
return false;
}});
//search input
TextInput searchInput = TextInput.createTextInput(1.0f);
searchInput.setWidth(TEXT_INPUT_WIDTH);
searchInput.setMinWidth(TEXT_INPUT_WIDTH);
searchInput.setMinHeight(20);
searchInput.setOnPress(new KeyboardEventCallback() {public boolean execute(KeyboardEvent event){
boolean rVal = searchInput.defaultKeyHandling(event);
fillInVoxelSelectors(scrollable, searchInput.getText());
return rVal;
}});
rVal.addChild(searchInput);
//attach scrollable after search input for organzation purposes
rVal.addChild(scrollable);
//final step
fillInVoxelSelectors(scrollable, searchInput.getText());
rVal.applyYoga();
return rVal;
}
/**
* Fills in the voxels to display based on the contents of the search string
* @param scrollable the scrollable to drop selection buttons in to
* @param searchString the string to search based on
*/
static void fillInVoxelSelectors(VirtualScrollable scrollable, String searchString){
scrollable.clearChildren();
VoxelData voxelData = Globals.gameConfigCurrent.getVoxelData();
List<VoxelType> matchingVoxels = voxelData.getTypes().stream().filter((type)->type.getName().toLowerCase().contains(searchString.toLowerCase())).toList();
//generate voxel buttons
for(VoxelType type : matchingVoxels){
Button newButton = new Button();
newButton.setAlignItems(Yoga.YGAlignCenter);
//dimensions
newButton.setWidth(VOXEL_BUTTON_WIDTH);
newButton.setMinWidth(VOXEL_BUTTON_WIDTH);
newButton.setHeight(VOXEL_BUTTON_HEIGHT);
newButton.setMinHeight(VOXEL_BUTTON_HEIGHT);
//margin
newButton.setMarginBottom(MARGIN_EACH_SIDE);
newButton.setMarginLeft(MARGIN_EACH_SIDE);
newButton.setMarginRight(MARGIN_EACH_SIDE);
newButton.setMarginTop(MARGIN_EACH_SIDE);
//label
Label voxelLabel = new Label(1.0f);
voxelLabel.setText(type.getName());
//icon/model
ImagePanel texturePanel = ImagePanel.createImagePanel(type.getTexture());
texturePanel.setWidth(VOXEL_BUTTON_TEXTURE_DIM);
texturePanel.setHeight(VOXEL_BUTTON_TEXTURE_DIM);
newButton.addChild(texturePanel);
//button handling
newButton.addChild(voxelLabel);
newButton.setOnClick(new ClickEventCallback() {public boolean execute(ClickEvent event){
//set voxel type to this type
Globals.clientSelectedVoxelType = type;
return false;
}});
scrollable.addChild(newButton);
}
rVal.applyYoga();
}
}

View File

@ -8,9 +8,9 @@ import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.renderer.debug.DebugRendering;
import electrosphere.renderer.ui.WidgetUtils;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.ScrollableContainer;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ClickableElement;
import electrosphere.renderer.ui.elementtypes.ContainerElement;
import electrosphere.renderer.ui.elementtypes.Element;

View File

@ -7,7 +7,6 @@ import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.texture.Texture;
import electrosphere.renderer.ui.Window;
import electrosphere.renderer.ui.elementtypes.DrawableElement;
import electrosphere.renderer.ui.elementtypes.Element;
@ -68,7 +67,9 @@ public class UIPipeline implements RenderPipeline {
int parentPosX = 0;
int parentPosY = 0;
//set opengl state
openGLState.glDepthTest(false);
for(Element currentElement : Globals.elementManager.getWindowList()){
if(currentElement instanceof DrawableElement){
DrawableElement drawable = (DrawableElement) currentElement;

View File

@ -8,6 +8,7 @@ import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ContainerElement;
import electrosphere.renderer.ui.elementtypes.DraggableElement;
import electrosphere.renderer.ui.elementtypes.Element;
@ -36,6 +37,11 @@ public class ElementManager {
// the element currently hovered over
HoverableElement currentHoveredElement = null;
/**
* Registers a window
* @param name the name associated with the window
* @param w The window element
*/
public void registerWindow(String name, Element w){
elementMap.put(name,w);
if(!elementList.contains(w)){
@ -46,6 +52,11 @@ public class ElementManager {
}
}
/**
* Gets a window element by the name associated with it
* @param name The associated name
* @return The window element if it exists, null otherwise
*/
public Element getWindow(String name){
return elementMap.get(name);
}
@ -175,6 +186,70 @@ public class ElementManager {
}
}
/**
* Fires an event that does not have a screen position on a given element. Event propagation works by element ancestry regardless of position.
* @param event The event
* @param el The element to handle the event
*/
public void fireEventNoPosition(Event event, Element el){
List<Element> ancestryList = constructNonPositionalAncestryList(el);
boolean propagate = true;
while(ancestryList.size() > 0 && propagate == true){
Element currentElement = ancestryList.remove(0);
propagate = currentElement.handleEvent(event);
}
}
/**
* Constructs a list of elements starting with el and containing all its ancestor elements, parent, grandparent, etc
* @param el The target element
* @return The ancestry list
*/
private List<Element> constructNonPositionalAncestryList(Element el){
//a list of elements with 0 being the target of the event, 1 being the parent of the target, 2 being the parent of 1, etc
List<Element> elementPropagation = new LinkedList<Element>();
elementPropagation.add(el);
Element parentElement = null;
Element targetElement = el;
while(true){
for(Element window : this.getWindowList()){
if(window instanceof ContainerElement){
parentElement = recursivelySearchParent((ContainerElement)window,targetElement);
}
}
if(parentElement == null){
break;
} else {
elementPropagation.add(parentElement);
targetElement = parentElement;
parentElement = null;
}
}
return elementPropagation;
}
/**
* Recursively searched for a target element starting at the searchRoot
* @param searchRoot The root element to search from propagating down the tree
* @param target The target to search for
* @return The parent of target if it exists, null otherwise
*/
public Element recursivelySearchParent(ContainerElement searchRoot, Element target){
if(searchRoot instanceof ContainerElement){
for(Element child : searchRoot.getChildren()){
if(child == target){
return searchRoot;
} else if(child instanceof ContainerElement){
Element result = recursivelySearchParent((ContainerElement)child, target);
if(result != null){
return result;
}
}
}
}
return null;
}
/**
* Recursively uilds a stack of elements at a given position based on their depth into the tree
* @param inputStack The empty stack to fill

View File

@ -4,6 +4,7 @@ import electrosphere.controls.ControlHandler;
import electrosphere.engine.Globals;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.ClickableElement;
import electrosphere.renderer.ui.elementtypes.DrawableElement;

View File

@ -44,6 +44,20 @@ public class Button extends StandardContainerElement implements DrawableElement,
super();
}
/**
* Creates a button with
* @param text
* @return
*/
public static Button createButton(String text, ClickableElement.ClickEventCallback callback){
Button rVal = new Button();
Label rValLabel = new Label(1.0f);
rValLabel.setText(text);
rVal.addChild(rValLabel);
rVal.setOnClick(callback);
return rVal;
}
public boolean getVisible() {
return visible;
}

View File

@ -35,12 +35,41 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag
Vector3f texPosition = new Vector3f(0,0,0);
Vector3f texScale = new Vector3f(1,1,0);
Vector3f boxPosition = new Vector3f();
Vector3f boxDimensions = new Vector3f();
DragEventCallback onDragStart;
DragEventCallback onDrag;
DragEventCallback onDragRelease;
static final Vector3f windowDrawDebugColor = new Vector3f(0.0f,0.5f,1.0f);
/**
* Creates an image panel
* @param texturePath the path to the texture
* @return The image panel
*/
public static ImagePanel createImagePanel(String texturePath){
ImagePanel rVal = new ImagePanel(texturePath);
return rVal;
}
/**
* Private constructor
*/
private ImagePanel(String texturePath){
super();
this.texturePath = texturePath;
if(texturePath != null){
texture = Globals.assetManager.fetchTexture(this.texturePath);
}
if(texture != null){
customMat.setTexturePointer(texture.getTexturePointer());
hasLoadedTexture = true;
} else {
customMat.setTexturePointer(Globals.assetManager.fetchTexture(Globals.blackTexture).getTexturePointer());
}
}
public ImagePanel(int x, int y, int width, int height, String texturePath){
super();
@ -78,26 +107,24 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag
int parentWidth,
int parentHeight
) {
if(!hasLoadedTexture){
texture = Globals.assetManager.fetchTexture(this.texturePath);
if(texture != null){
customMat.setTexturePointer(texture.getTexturePointer());
hasLoadedTexture = true;
}
float ndcWidth = (float)getInternalWidth()/parentWidth;
float ndcHeight = (float)getInternalHeight()/parentHeight;
float ndcX = (float)(getInternalX() + parentPosX)/parentWidth;
float ndcY = (float)(getInternalY() + parentPosY)/parentHeight;
boxPosition = new Vector3f(ndcX,ndcY,0);
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID);
if(texture != null){
customMat.setTexturePointer(texture.getTexturePointer());
}
//this call binds the screen as the "texture" we're rendering to
//have to call before actually rendering
glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer);
float ndcX = (float)positionX/parentWidth;
float ndcY = (float)positionY/parentHeight;
float ndcWidth = (float)width/parentWidth;
float ndcHeight = (float)height/parentHeight;
Vector3f boxPosition = new Vector3f(ndcX,ndcY,0);
Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
Model planeModel = Globals.assetManager.fetchModel(imagePanelModelPath);
openGLState.glViewport(parentWidth, parentHeight);
if(planeModel != null){
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);

View File

@ -191,6 +191,12 @@ public class StandardContainerElement extends StandardElement implements Contain
childList.remove(child);
}
@Override
public void clearChildren(){
Yoga.YGNodeRemoveAllChildren(yogaNode);
childList.clear();
}
@Override
public int getChildOffsetX(){
return 0;

View File

@ -51,7 +51,31 @@ public class TextInput extends StandardContainerElement implements DrawableEleme
Font font;
/**
* Creates a text input element
* @param fontSize the size of the font in the text element
* @return The text input
*/
public static TextInput createTextInput(float fontSize){
return new TextInput(fontSize);
}
/**
* Private constructor
* @param fontSize
*/
private TextInput(float fontSize){
super();
this.font = Globals.fontManager.getFont("default");
this.fontSize = fontSize;
this.color = new Vector3f(1,1,1);
setHeight((int)(font.getFontHeight() * fontSize));
Yoga.YGNodeStyleSetFlexDirection(this.yogaNode, Yoga.YGFlexDirectionRow);
Yoga.YGNodeStyleSetMinHeight(this.yogaNode, font.imageHeight * fontSize);
Yoga.YGNodeStyleSetMinWidth(this.yogaNode, 1);
}
/**
* Constructor
* @param x
@ -196,14 +220,7 @@ public class TextInput extends StandardContainerElement implements DrawableEleme
if(onKeyPressCallback != null){
onKeyPressCallback.execute(keyEvent);
} else {
if(keyEvent.getKey().matches(Pattern.quote("bs"))){
if(this.text.length() > 0){
this.setText(this.text.substring(0, this.text.length() - 1));
}
} else {
this.setText(this.text + keyEvent.getKey());
}
propagate = false;
propagate = defaultKeyHandling(keyEvent);
}
} else if(event instanceof ClickEvent){
ClickEvent clickEvent = (ClickEvent)event;
@ -217,6 +234,22 @@ public class TextInput extends StandardContainerElement implements DrawableEleme
return propagate;
}
/**
* The default handling for a keyboard event
* @param keyEvent the event
* @return whether to propagate or not
*/
public boolean defaultKeyHandling(KeyboardEvent keyEvent){
if(keyEvent.getKey().matches(Pattern.quote("bs"))){
if(this.text.length() > 0){
this.setText(this.text.substring(0, this.text.length() - 1));
}
} else {
this.setText(this.text + keyEvent.getKey());
}
return false;
}
@Override
public boolean isFocused() {
return focused;

View File

@ -1,4 +1,4 @@
package electrosphere.renderer.ui;
package electrosphere.renderer.ui.elements;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
@ -51,6 +51,13 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme
//controls whether to show window decorations (ie the frame)
boolean showDecorations = true;
//Yoga node for controlling placement of the window on the screen
//IE, if you want to place a window in the upper right hand side of the screen,
//this node can be used to control placement alignment to accomplish that
//NOTE: It should always be set to the current size of the window (width, height)
//NOTE: It is updated every time the applyYoga function is called
long parentWindowYogaNode = -1;
/**
* Constructor
@ -70,8 +77,15 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme
boxPosition = new Vector3f(ndcX,ndcY,0);
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
this.showDecorations = showDecorations;
//yoga node for the actually visible part
this.yogaNode = Yoga.YGNodeNew();
this.layout = YGNode.create(this.yogaNode).layout();
//yoga node for placement
this.parentWindowYogaNode = Yoga.YGNodeNew();
Yoga.YGNodeInsertChild(this.parentWindowYogaNode, this.yogaNode, 0);
setParentAlignContent(Yoga.YGAlignFlexStart);
setParentAlignItem(Yoga.YGAlignFlexStart);
setParentJustifyContent(Yoga.YGJustifyFlexStart);
this.setWidth(width);
this.setHeight(height);
}
@ -86,6 +100,13 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme
int parentWidth,
int parentHeight
) {
float ndcWidth = (float)this.getInternalWidth()/Globals.WINDOW_WIDTH;
float ndcHeight = (float)this.getInternalHeight()/Globals.WINDOW_HEIGHT;
float ndcX = (float)this.getInternalX()/Globals.WINDOW_WIDTH;
float ndcY = (float)this.getInternalY()/Globals.WINDOW_HEIGHT;
boxPosition = new Vector3f(ndcX,ndcY,0);
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
widgetBuffer.bind();
openGLState.glViewport(width, height);
@ -322,23 +343,25 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme
@Override
public void applyYoga() {
Yoga.YGNodeStyleSetWidth(parentWindowYogaNode, Globals.WINDOW_WIDTH);
Yoga.YGNodeStyleSetHeight(parentWindowYogaNode, Globals.WINDOW_HEIGHT);
//calculate yoga layout
Yoga.YGNodeCalculateLayout(yogaNode, width, height, Yoga.YGFlexDirectionColumn);
Yoga.YGNodeCalculateLayout(parentWindowYogaNode, width, height, Yoga.YGFlexDirectionColumn);
//apply yoga values to all children
LoggerInterface.loggerUI.INFO("==Apply yoga to windoow==");
for(Element child : this.getChildren()){
child.applyYoga();
}
// //get the values from yoga
// float leftRaw = layout.positions(Yoga.YGEdgeLeft);
// float topRaw = layout.positions(Yoga.YGEdgeTop);
// float widthRaw = layout.dimensions(Yoga.YGDimensionWidth);
// float heightRaw = layout.dimensions(Yoga.YGDimensionHeight);
float leftRaw = Yoga.YGNodeLayoutGetLeft(yogaNode);
float topRaw = Yoga.YGNodeLayoutGetTop(yogaNode);
float widthRaw = Yoga.YGNodeLayoutGetWidth(yogaNode);
float heightRaw = Yoga.YGNodeLayoutGetHeight(yogaNode);
// //apply the values to this component
// this.setPositionX((int)leftRaw + this.getMarginLeft());
// this.setPositionY((int)topRaw + this.getMarginTop());
// this.setWidth((int)widthRaw - this.getMarginLeft() - this.getMarginRight());
// this.setHeight((int)heightRaw - this.getMarginTop() - this.getMarginBottom());
this.positionX = (int)leftRaw;
this.positionY = (int)topRaw;
this.width = (int)widthRaw;
this.height = (int)heightRaw;
}
@Override
@ -389,6 +412,12 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme
Yoga.YGNodeRemoveChild(yogaNode, child.getYogaNode());
}
@Override
public void clearChildren(){
Yoga.YGNodeRemoveAllChildren(yogaNode);
childList.clear();
}
public boolean handleEvent(Event event){
boolean propagate = true;
if(event instanceof NavigationEvent && navCallback != null){
@ -444,4 +473,28 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme
return 1;
}
/**
* Sets the alignment of items on the parent to the window yoga node
* @param alignment The alignment value
*/
public void setParentAlignItem(int alignment){
Yoga.YGNodeStyleSetAlignItems(this.parentWindowYogaNode, alignment);
}
/**
* Sets the alignment of content on the parent to the window yoga node
* @param alignment The alignment
*/
public void setParentAlignContent(int alignment){
Yoga.YGNodeStyleSetAlignContent(this.parentWindowYogaNode, alignment);
}
/**
* Sets the justification of the parent yoga node containing this window
* @param justification The justification mode
*/
public void setParentJustifyContent(int justification){
Yoga.YGNodeStyleSetJustifyContent(this.parentWindowYogaNode, justification);
}
}

View File

@ -25,6 +25,11 @@ public interface ContainerElement extends Element {
*/
public void removeChild(Element child);
/**
* Clears all children attached to this element
*/
public void clearChildren();
//gets the offset applied to all children
//ie if you scrolled up, how much are the children offset by that
public int getChildOffsetX();

View File

@ -28,25 +28,27 @@ public class ContentSerialization {
public static ContentSerialization constructContentSerialization(List<Entity> entities){
ContentSerialization rVal = new ContentSerialization();
for(Entity entity : entities){
if(CreatureUtils.isCreature(entity)){
EntitySerialization serializedEntity = new EntitySerialization();
serializedEntity.setPosition(EntityUtils.getPosition(entity));
serializedEntity.setRotation(EntityUtils.getRotation(entity));
serializedEntity.setType(CreatureUtils.ENTITY_TYPE_CREATURE);
serializedEntity.setSubtype(CreatureUtils.getType(entity));
rVal.serializedEntities.add(serializedEntity);
}
if(ItemUtils.isItem(entity)){
throw new UnsupportedOperationException();
}
if(ObjectUtils.isObject(entity)){
throw new UnsupportedOperationException();
}
if(StructureUtils.isStructure(entity)){
throw new UnsupportedOperationException();
}
if(FoliageUtils.isFoliage(entity)){
throw new UnsupportedOperationException();
if(!CreatureUtils.hasControllerPlayerId(entity)){
if(CreatureUtils.isCreature(entity)){
EntitySerialization serializedEntity = new EntitySerialization();
serializedEntity.setPosition(EntityUtils.getPosition(entity));
serializedEntity.setRotation(EntityUtils.getRotation(entity));
serializedEntity.setType(CreatureUtils.ENTITY_TYPE_CREATURE);
serializedEntity.setSubtype(CreatureUtils.getType(entity));
rVal.serializedEntities.add(serializedEntity);
}
if(ItemUtils.isItem(entity)){
throw new UnsupportedOperationException();
}
if(ObjectUtils.isObject(entity)){
throw new UnsupportedOperationException();
}
if(StructureUtils.isStructure(entity)){
throw new UnsupportedOperationException();
}
if(FoliageUtils.isFoliage(entity)){
throw new UnsupportedOperationException();
}
}
}
return rVal;