From 9aa7cfe048bacaaeeb436bab809b6ce18ff181d6 Mon Sep 17 00:00:00 2001 From: austin Date: Sat, 13 Jul 2024 23:34:54 -0400 Subject: [PATCH] toggle ui element --- assets/Shaders/plane/plane.fs | 4 + assets/Textures/ui/circle.png | Bin 0 -> 32936 bytes assets/Textures/ui/square.png | Bin 0 -> 11270 bytes buildNumber.properties | 4 +- .../java/electrosphere/engine/Globals.java | 69 +++--- .../electrosphere/entity/scene/SceneFile.java | 14 ++ .../entity/scene/SceneGenerator.java | 1 + .../mainmenu/MenuGeneratorsUITesting.java | 9 +- .../renderer/ui/elements/ActorPanel.java | 15 +- .../renderer/ui/elements/BitmapCharacter.java | 2 +- .../renderer/ui/elements/Button.java | 8 + .../renderer/ui/elements/ImagePanel.java | 3 + .../ui/elements/ScrollableContainer.java | 4 + .../renderer/ui/elements/Slider.java | 39 ++- .../ui/elements/StandardDrawableElement.java | 58 +++++ .../renderer/ui/elements/StringCarousel.java | 15 +- .../renderer/ui/elements/TextInput.java | 8 + .../renderer/ui/elements/ToggleInput.java | 225 ++++++++++++++++++ .../renderer/ui/elements/Window.java | 5 + .../ui/elementtypes/FocusableElement.java | 30 ++- .../renderer/ui/events/ValueChangeEvent.java | 23 ++ .../renderer/ui/macros/InputMacros.java | 25 ++ .../electrosphere/server/saves/SaveUtils.java | 4 +- src/test/java/startup/StartupTest.java | 4 +- 24 files changed, 496 insertions(+), 73 deletions(-) create mode 100644 assets/Textures/ui/circle.png create mode 100644 assets/Textures/ui/square.png create mode 100644 src/main/java/electrosphere/renderer/ui/elements/StandardDrawableElement.java create mode 100644 src/main/java/electrosphere/renderer/ui/elements/ToggleInput.java diff --git a/assets/Shaders/plane/plane.fs b/assets/Shaders/plane/plane.fs index d1039d55..6a4c29c7 100644 --- a/assets/Shaders/plane/plane.fs +++ b/assets/Shaders/plane/plane.fs @@ -4,9 +4,13 @@ out vec4 FragColor; in vec2 TexCoords; uniform sampler2D screenTexture; +uniform vec3 color; void main(){ vec4 textureColor = texture(screenTexture, TexCoords); + textureColor.r = textureColor.r * color.r; + textureColor.g = textureColor.g * color.g; + textureColor.b = textureColor.b * color.b; if(textureColor.a < 0.1){ discard; } diff --git a/assets/Textures/ui/circle.png b/assets/Textures/ui/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..c1ca9f0efb379cb042a7d6841051a6bd8b4e3ec1 GIT binary patch literal 32936 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 literal 0 HcmV?d00001 diff --git a/buildNumber.properties b/buildNumber.properties index 7aaf1369..29e3a345 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Fri Jul 12 17:55:57 EDT 2024 -buildNumber=183 +#Sat Jul 13 22:48:57 EDT 2024 +buildNumber=185 diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index e7a7ccae..8645f882 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -40,7 +40,6 @@ 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; import electrosphere.logger.LoggerInterface; import electrosphere.menu.WindowUtils; import electrosphere.net.client.ClientNetworking; @@ -66,16 +65,13 @@ import electrosphere.renderer.ui.elements.ImagePanel; import electrosphere.renderer.ui.font.FontManager; import electrosphere.script.ScriptEngine; import electrosphere.server.ai.AIManager; -import electrosphere.server.content.ServerContentManager; import electrosphere.server.datacell.EntityDataCellMapper; import electrosphere.server.datacell.RealmManager; import electrosphere.server.db.DatabaseController; -import electrosphere.server.fluid.manager.ServerFluidManager; import electrosphere.server.pathfinding.NavMeshManager; import electrosphere.server.saves.Save; import electrosphere.server.simulation.MacroSimulation; import electrosphere.server.simulation.MicroSimulation; -import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.util.FileUtils; /** @@ -247,10 +243,10 @@ public class Globals { public static String textureSpecularDefault; public static Material materialDefault; - public static String blackTexture; - public static String testingTexture; - public static String whiteTexture; - public static String offWhiteTexture; + public static String blackTexture = "Textures/b1.png"; + public static String testingTexture = "Textures/Testing1.png"; + public static String whiteTexture = "Textures/w1.png"; + public static String offWhiteTexture = "Textures/ow1.png"; public static String imagePlaneModelID; public static String solidPlaneModelID; @@ -407,7 +403,9 @@ public class Globals { public static NetMonitor netMonitor; - + /** + * Inits globals + */ public static void initGlobals(){ LoggerInterface.loggerStartup.INFO("Initialize global variables"); //timekeeper @@ -463,6 +461,9 @@ public class Globals { profiler = new Profiler(); } + /** + * Inits default audio resources + */ public static void initDefaultAudioResources(){ LoggerInterface.loggerStartup.INFO("Loading default audio resources"); Globals.assetManager.addAudioPathToQueue("/Audio/inventoryGrabItem.ogg"); @@ -472,32 +473,45 @@ public class Globals { Globals.assetManager.addAudioPathToQueue("/Audio/ambienceWind1SeamlessMono.ogg"); Globals.assetManager.loadAssetsInQueue(); } + + /** + * Texture paths to be loaded when renderer inits + */ + private static String[] defaultTexturePaths = new String[]{ + "Textures/default_diffuse.png", + "Textures/default_specular.png", + "Textures/b1.png", + "Textures/w1.png", + "Textures/ow1.png", + "Textures/ui/WindowBorder.png", + "Textures/ui/uiFrame1.png", + "Textures/ui/uiFrame2.png", + "Textures/ui/circle.png", + "Textures/ui/square.png", + }; + /** + * Inits default graphical resources + */ public static void initDefaultGraphicalResources(){ LoggerInterface.loggerStartup.INFO("Loading default graphical resources"); - //create default textures - Globals.assetManager.addTexturePathtoQueue("Textures/default_diffuse.png"); - Globals.assetManager.addTexturePathtoQueue("Textures/default_specular.png"); + + //load default textures + for(String defaultTexturePath: defaultTexturePaths){ + Globals.assetManager.addTexturePathtoQueue(defaultTexturePath); + } + //create default material materialDefault = new Material(); materialDefault.set_diffuse("Textures/default_diffuse.png"); materialDefault.set_specular("Textures/default_specular.png"); - //create default lights + //create font manager fontManager = new FontManager(); fontManager.loadFonts(); assetManager.registerModelToSpecificString(RenderUtils.createBitmapCharacter(), AssetDataStrings.BITMAP_CHARACTER_MODEL); //particle billboard model particleBillboardModel = assetManager.registerModel(RenderUtils.createParticleModel()); - //black texture for backgrouns - blackTexture = "Textures/b1.png"; - Globals.assetManager.addTexturePathtoQueue(blackTexture); - //white texture for backgrounds - whiteTexture = "Textures/w1.png"; - Globals.assetManager.addTexturePathtoQueue(whiteTexture); - //off white texture for backgrounds - offWhiteTexture = "Textures/ow1.png"; - Globals.assetManager.addTexturePathtoQueue(offWhiteTexture); //initialize required windows WindowUtils.initBaseWindows(); //init default shaderProgram @@ -522,20 +536,9 @@ public class Globals { //image panel ImagePanel.imagePanelModelPath = assetManager.registerModel(RenderUtils.createPlaneModel("Shaders/plane/plane.vs", "Shaders/plane/plane.fs")); - - //init ui images - assetManager.addTexturePathtoQueue("Textures/ui/WindowBorder.png"); - assetManager.addTexturePathtoQueue("Textures/ui/uiFrame1.png"); - assetManager.addTexturePathtoQueue("Textures/ui/uiFrame2.png"); - - testingTexture = "Textures/Testing1.png"; - Globals.assetManager.addTexturePathtoQueue(testingTexture); Globals.assetManager.addShaderToQueue("Shaders/ui/plainBox/plainBox.vs", "Shaders/ui/plainBox/plainBox.fs"); - // //in game ui stuff - // elementManager.registerWindow(WindowStrings.WINDOW_MENU_MAIN,WidgetUtils.createInGameMainMenuButton()); - //window content shader assetManager.addShaderToQueue("Shaders/ui/windowContent/windowContent.vs", null, "Shaders/ui/windowContent/windowContent.fs"); diff --git a/src/main/java/electrosphere/entity/scene/SceneFile.java b/src/main/java/electrosphere/entity/scene/SceneFile.java index ca05f20d..adba6013 100644 --- a/src/main/java/electrosphere/entity/scene/SceneFile.java +++ b/src/main/java/electrosphere/entity/scene/SceneFile.java @@ -28,6 +28,11 @@ public class SceneFile { */ RealmDescriptor realmDescriptor; + /** + * Controls whether the save utils will store a copy of the scene file in the save or not + */ + boolean createSaveInstance; + /** * Private constructor @@ -46,6 +51,7 @@ public class SceneFile { rVal.scriptPaths = new LinkedList(); rVal.initScriptPath = null; rVal.realmDescriptor = new RealmDescriptor(); + rVal.createSaveInstance = false; return rVal; } @@ -81,4 +87,12 @@ public class SceneFile { return realmDescriptor; } + /** + * Gets whether the save utils will store a copy of the scene file in the save or not + * @return true if should create instance of scene file in save, false otherwise + */ + public boolean getCreateSaveInstance(){ + return createSaveInstance; + } + } diff --git a/src/main/java/electrosphere/entity/scene/SceneGenerator.java b/src/main/java/electrosphere/entity/scene/SceneGenerator.java index 5bae2d39..e83e1f51 100644 --- a/src/main/java/electrosphere/entity/scene/SceneGenerator.java +++ b/src/main/java/electrosphere/entity/scene/SceneGenerator.java @@ -24,6 +24,7 @@ public class SceneGenerator { //realm descriptor stuff file.realmDescriptor.type = RealmDescriptor.REALM_DESCRIPTOR_PROCEDURAL; file.realmDescriptor.griddedRealmSize = GriddedDataCellManager.MAX_GRID_SIZE; + file.createSaveInstance = true; //won't have a predefined scene to load, so must create one in the save //create terrain ServerWorldData serverWorldData = ServerWorldData.createGriddedRealmWorldData(2000); diff --git a/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java index f6a7ac9b..4989b34d 100644 --- a/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java +++ b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java @@ -11,6 +11,7 @@ import electrosphere.renderer.ui.elements.Button; import electrosphere.renderer.ui.elements.FormElement; import electrosphere.renderer.ui.elements.Label; import electrosphere.renderer.ui.elements.Slider; +import electrosphere.renderer.ui.elements.ToggleInput; import electrosphere.renderer.ui.elements.VirtualScrollable; import electrosphere.renderer.ui.elementtypes.Element; import electrosphere.renderer.ui.elementtypes.ValueElement; @@ -34,12 +35,18 @@ public class MenuGeneratorsUITesting { }); rVal.addChild(backButton); - ActorPanel actorPanel = new ActorPanel(Globals.renderingEngine.getOpenGLState(), 500, 100, 500, 500, ActorUtils.createActorFromModelPath("Models/deer1.fbx")); + //toggle input + ToggleInput toggleInput = ToggleInput.createToggleInput(); + rVal.addChild(toggleInput); + + //actor panel + ActorPanel actorPanel = new ActorPanel(Globals.renderingEngine.getOpenGLState(), 500, 100, 500, 500, ActorUtils.createActorFromModelPath("Models/creatures/animals/deer1.fbx")); if(Globals.playerCamera == null){ Globals.playerCamera = CameraEntityUtils.spawnBasicCameraEntity(new Vector3f(0,0,0), new Vector3f(-1,0,0)); } rVal.addChild(actorPanel); + // //Virtual scrollable test VirtualScrollable virtualScrollable = new VirtualScrollable(300, 75); diff --git a/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java b/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java index 32335302..8adab23c 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java @@ -32,6 +32,8 @@ import electrosphere.renderer.ui.events.Event; public class ActorPanel extends StandardElement implements DrawableElement, DraggableElement { + static Vector3f color = new Vector3f(1.0f); + Material customMat = new Material(); Framebuffer elementBuffer; @@ -61,8 +63,8 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag this.actor = actor; this.internalPositionX = x; this.internalPositionY = y; - this.width = width; - this.height = height; + this.setWidth(width); + this.setHeight(height); this.aspectRatio = (float)width / (float)height; recalculateModelMatrix(); } @@ -138,10 +140,10 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag //set viewport openGLState.glViewport(parentWidth, parentHeight); - float ndcX = (float)internalPositionX/parentWidth; - float ndcY = (float)internalPositionY/parentHeight; - float ndcWidth = (float)width/parentWidth; - float ndcHeight = (float)height/parentHeight; + float ndcX = (float)getInternalX()/parentWidth; + float ndcY = (float)getInternalY()/parentHeight; + float ndcWidth = (float)getInternalWidth()/parentWidth; + float ndcHeight = (float)getInternalHeight()/parentHeight; Vector3f boxPosition = new Vector3f(ndcX,ndcY,0); Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); @@ -172,6 +174,7 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.draw(renderPipelineState,Globals.renderingEngine.getOpenGLState()); } else { diff --git a/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java b/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java index 969e3ec3..78985528 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java +++ b/src/main/java/electrosphere/renderer/ui/elements/BitmapCharacter.java @@ -19,7 +19,7 @@ public class BitmapCharacter extends StandardElement implements DrawableElement String text; - Vector3f color = new Vector3f(0,0,0); + Vector3f color = new Vector3f(1.0f); Font font; diff --git a/src/main/java/electrosphere/renderer/ui/elements/Button.java b/src/main/java/electrosphere/renderer/ui/elements/Button.java index 9c9e9116..47d7f58d 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Button.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Button.java @@ -24,6 +24,8 @@ import electrosphere.renderer.ui.events.MouseEvent; public class Button extends StandardContainerElement implements DrawableElement, FocusableElement, ClickableElement, HoverableElement { + static Vector3f color = new Vector3f(1.0f); + Vector3f boxPosition = new Vector3f(); Vector3f boxDimensions = new Vector3f(); Vector3f texPosition = new Vector3f(0,0,0); @@ -181,6 +183,7 @@ public class Button extends StandardContainerElement implements DrawableElement, planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); customMat.setTexturePointer(windowFrame.getTexturePointer()); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.drawUI(); @@ -259,5 +262,10 @@ public class Button extends StandardContainerElement implements DrawableElement, public void setOnHoverCallback(HoverEventCallback callback) { this.hoverEventCallback = callback; } + + @Override + public void setFocused(boolean focused) { + this.focused = focused; + } } diff --git a/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java b/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java index 3019fd5d..3d0f3cbf 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java @@ -22,6 +22,8 @@ import electrosphere.renderer.ui.events.Event; * A UI element that is a single, uninteractable image */ public class ImagePanel extends StandardElement implements DrawableElement, DraggableElement { + + Vector3f color = new Vector3f(1.0f); //Asset path for the model data that is used to draw the image panel public static String imagePanelModelPath; @@ -158,6 +160,7 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.drawUI(); } else { diff --git a/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java b/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java index e492a1d6..7b25ba0a 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java @@ -21,6 +21,8 @@ import static org.lwjgl.opengl.GL30.*; public class ScrollableContainer extends StandardContainerElement implements DrawableElement { + Vector3f color = new Vector3f(1.0f); + boolean focused = false; public boolean visible = false; @@ -172,6 +174,7 @@ public class ScrollableContainer extends StandardContainerElement implements Dra planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); customMat.setTexturePointer(windowFrame.getTexturePointer()); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.drawUI(); @@ -182,6 +185,7 @@ public class ScrollableContainer extends StandardContainerElement implements Dra planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); customMat.setTexturePointer(widgetBuffer.getTexturePointer()); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.drawUI(); diff --git a/src/main/java/electrosphere/renderer/ui/elements/Slider.java b/src/main/java/electrosphere/renderer/ui/elements/Slider.java index 336cd168..e795782e 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Slider.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Slider.java @@ -9,11 +9,10 @@ import electrosphere.logger.LoggerInterface; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.debug.DebugRendering; +import electrosphere.renderer.model.Material; import electrosphere.renderer.model.Model; import electrosphere.renderer.ui.elementtypes.ClickableElement; import electrosphere.renderer.ui.elementtypes.DraggableElement; -import electrosphere.renderer.ui.elementtypes.DrawableElement; -import electrosphere.renderer.ui.elementtypes.FocusableElement; import electrosphere.renderer.ui.elementtypes.MenuEventElement; import electrosphere.renderer.ui.elementtypes.ValueElement; import electrosphere.renderer.ui.events.ClickEvent; @@ -27,11 +26,8 @@ import electrosphere.renderer.ui.events.ValueChangeEvent; /** * A ui element that is a slider that lets you pick between a range of values */ -public class Slider extends StandardElement implements ClickableElement, DraggableElement, FocusableElement, DrawableElement, MenuEventElement, ValueElement { +public class Slider extends StandardDrawableElement implements ClickableElement, DraggableElement, MenuEventElement, ValueElement { - public boolean visible = false; - - boolean focused = false; FocusEventCallback onFocusCallback; FocusEventCallback onLoseFocusCallback; DragEventCallback onDragStart; @@ -45,9 +41,11 @@ public class Slider extends StandardElement implements ClickableElement, Draggab float max = 1.0f; float value = 0.5f; - Vector3f colorBackground = new Vector3f(0,0,0); + Vector3f colorBackground = new Vector3f(0.2f,0.2f,0.2f); Vector3f colorForeground = new Vector3f(1,1,1); + static Material mat; + static final int idealMargin = 5; //5 pixels margin ideally @@ -87,6 +85,11 @@ public class Slider extends StandardElement implements ClickableElement, Draggab */ private Slider(){ super(); + if(mat == null){ + mat = new Material(); + mat.set_diffuse("Textures/ui/square.png"); + mat.set_specular("Textures/ui/square.png"); + } setWidth(DEFAULT_WIDTH); setHeight(DEFAULT_HEIGHT); } @@ -94,6 +97,11 @@ public class Slider extends StandardElement implements ClickableElement, Draggab public Slider(int positionX, int positionY, int width, int height, Vector3f colorBackground, Vector3f colorForeground){ super(); + if(mat == null){ + mat = new Material(); + mat.set_diffuse("Textures/ui/square.png"); + mat.set_specular("Textures/ui/square.png"); + } this.internalPositionX = positionX; this.internalPositionY = positionY; this.width = width; @@ -146,6 +154,7 @@ public class Slider extends StandardElement implements ClickableElement, Draggab planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", colorBackground); + planeModel.getMeshes().get(0).setMaterial(mat); planeModel.drawUI(); //actual slider @@ -158,6 +167,7 @@ public class Slider extends StandardElement implements ClickableElement, Draggab planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", colorForeground); + planeModel.getMeshes().get(0).setMaterial(mat); planeModel.drawUI(); } else { LoggerInterface.loggerRenderer.ERROR("Window unable to find plane model!!", new Exception()); @@ -197,20 +207,7 @@ public class Slider extends StandardElement implements ClickableElement, Draggab float getValueAsPercentage(){ return (value - min) / (max - min); } - - public boolean getVisible() { - return visible; - } - - public void setVisible(boolean draw) { - this.visible = draw; - } - @Override - public boolean isFocused() { - return focused; - } - @Override public void setOnFocus(FocusEventCallback callback) { onFocusCallback = callback; @@ -262,7 +259,7 @@ public class Slider extends StandardElement implements ClickableElement, Draggab propagate = this.onFocusCallback.execute(focusEvent); } else { //default behavior/ - colorForeground = new Vector3f(1,0,0); + colorForeground = new Vector3f(1,0.5f,0.5f); propagate = true; } } else { diff --git a/src/main/java/electrosphere/renderer/ui/elements/StandardDrawableElement.java b/src/main/java/electrosphere/renderer/ui/elements/StandardDrawableElement.java new file mode 100644 index 00000000..2695d963 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/elements/StandardDrawableElement.java @@ -0,0 +1,58 @@ +package electrosphere.renderer.ui.elements; + +import electrosphere.renderer.OpenGLState; +import electrosphere.renderer.RenderPipelineState; +import electrosphere.renderer.ui.elementtypes.DrawableElement; +import electrosphere.renderer.ui.elementtypes.FocusableElement; + +/** + * A standard element that is drawable + */ +public class StandardDrawableElement extends StandardElement implements DrawableElement, FocusableElement { + + /** + * Visibility status + */ + boolean visible = true; + + /** + * Focus status + */ + boolean isFocused = false; + + @Override + public boolean getVisible() { + return visible; + } + + @Override + public void setVisible(boolean draw) { + this.visible = draw; + } + + @Override + public void draw(RenderPipelineState renderPipelineState, OpenGLState openGLState, int parentFramebufferPointer, int parentPosX, int parentPosY, int parentWidth, int parentHeight) { + throw new UnsupportedOperationException("Unimplemented method 'draw'"); + } + + @Override + public boolean isFocused() { + return this.isFocused; + } + + @Override + public void setFocused(boolean focused) { + this.isFocused = focused; + } + + @Override + public void setOnFocus(FocusEventCallback callback) { + throw new UnsupportedOperationException("Unimplemented method 'setOnFocus'"); + } + + @Override + public void setOnLoseFocus(FocusEventCallback callback) { + throw new UnsupportedOperationException("Unimplemented method 'setOnLoseFocus'"); + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java index 7fd03acb..a5b430b6 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java @@ -202,11 +202,6 @@ public class StringCarousel extends StandardContainerElement implements Drawable onValueChange = callback; } - @Override - public boolean isFocused() { - return focused; - } - @Override public void setOnFocus(FocusEventCallback callback) { onFocusCallback = callback; @@ -217,4 +212,14 @@ public class StringCarousel extends StandardContainerElement implements Drawable onLoseFocusCallback = callback; } + @Override + public boolean isFocused() { + return this.focused; + } + + @Override + public void setFocused(boolean focused) { + this.focused = focused; + } + } diff --git a/src/main/java/electrosphere/renderer/ui/elements/TextInput.java b/src/main/java/electrosphere/renderer/ui/elements/TextInput.java index e47f98b0..f38d50e3 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/TextInput.java +++ b/src/main/java/electrosphere/renderer/ui/elements/TextInput.java @@ -31,6 +31,8 @@ import java.util.regex.Pattern; */ public class TextInput extends StandardContainerElement implements DrawableElement, FocusableElement, KeyEventElement, ClickableElement, ValueElement { + Vector3f backgroundColor = new Vector3f(0.2f,0.2f,0.2f); + Vector3f boxPosition = new Vector3f(); Vector3f boxDimensions = new Vector3f(); Vector3f texPosition = new Vector3f(0,0,0); @@ -175,6 +177,7 @@ public class TextInput extends StandardContainerElement implements DrawableEleme planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", backgroundColor); customMat.setTexturePointer(windowFrame.getTexturePointer()); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.drawUI(); @@ -288,5 +291,10 @@ public class TextInput extends StandardContainerElement implements DrawableEleme public void setOnValueChangeCallback(ValueChangeEventCallback callback) { this.onValueChangeCallback = callback; } + + @Override + public void setFocused(boolean focused) { + this.focused = focused; + } } diff --git a/src/main/java/electrosphere/renderer/ui/elements/ToggleInput.java b/src/main/java/electrosphere/renderer/ui/elements/ToggleInput.java new file mode 100644 index 00000000..68b1a90a --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/elements/ToggleInput.java @@ -0,0 +1,225 @@ +package electrosphere.renderer.ui.elements; + +import org.joml.Vector3f; + +import electrosphere.engine.Globals; +import electrosphere.logger.LoggerInterface; +import electrosphere.renderer.OpenGLState; +import electrosphere.renderer.RenderPipelineState; +import electrosphere.renderer.model.Material; +import electrosphere.renderer.model.Model; +import electrosphere.renderer.ui.elementtypes.ClickableElement; +import electrosphere.renderer.ui.elementtypes.ValueElement; +import electrosphere.renderer.ui.events.ClickEvent; +import electrosphere.renderer.ui.events.Event; +import electrosphere.renderer.ui.events.ValueChangeEvent; + +/** + * A toggle input + */ +public class ToggleInput extends StandardDrawableElement implements ClickableElement, ValueElement { + + /** + * Click callback + */ + ClickEventCallback onClickCallback = null; + + /** + * Value change callback + */ + ValueChangeEventCallback onValueChangeCallback = null; + + /** + * The value of the toggle + */ + boolean value = false; + + /** + * Material for drawing the circle + */ + static Material circleMat = null; + + /** + * The width/height of the circle + */ + private static final float CIRCLE_WIDTH = 0.4f; + + /** + * The offset from the center of the bar to place the circle + */ + private static final float CIRCLE_OFFSET_FROM_CENTER = 0.3f; + + /** + * The color of the circle + */ + Vector3f circleColor = new Vector3f(0.8f,0.8f,0.8f); + + /** + * Material for drawing the connecting bar between the circle positions + */ + static Material barMat = null; + + /** + * The height of the bar relative to the total drawable height + */ + private static final float BAR_HEIGHT = 0.5f; + + /** + * The color of the bar + */ + Vector3f barColor = new Vector3f(0.3f,0.3f,0.3f); + + /** + * The default width of the toggle in pixels + */ + private static final int TOGGLE_PIXEL_WIDTH_DEFAULT = 60; + + /** + * The default height of the toggle in pixels + */ + private static final int TOGGLE_PIXEL_HEIGHT_DEFAULT = 38; + + /** + * Creates a toggle input + * @return The toggle input + */ + public static ToggleInput createToggleInput(){ + return new ToggleInput(); + } + + /** + * Constructor + */ + private ToggleInput(){ + //material work + if(circleMat == null){ + circleMat = new Material(); + circleMat.set_diffuse("Textures/ui/circle.png"); + circleMat.set_specular("Textures/ui/circle.png"); + } + if(barMat == null){ + barMat = new Material(); + barMat.set_diffuse("Textures/ui/square.png"); + barMat.set_specular("Textures/ui/square.png"); + } + + this.setWidth(TOGGLE_PIXEL_WIDTH_DEFAULT); + this.setHeight(TOGGLE_PIXEL_HEIGHT_DEFAULT); + } + + + @Override + public void draw( + RenderPipelineState renderPipelineState, + OpenGLState openGLState, + int parentFramebufferPointer, + int parentPosX, + int parentPosY, + int parentWidth, + int parentHeight + ){ + Globals.renderingEngine.bindFramebuffer(parentFramebufferPointer); + openGLState.glViewport(parentWidth, parentHeight); + + + float ndcWidth = (float)getInternalWidth()/parentWidth; + float ndcHeight = (float)getInternalHeight()/parentHeight; + float ndcX = (float)(getInternalX() + parentPosX)/parentWidth; + float ndcY = (float)(getInternalY() + parentPosY)/parentHeight; + + Vector3f boxPosition = new Vector3f(ndcX,ndcY,0); + Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); + + //getInternalX() and getInternalY() are the top left corner of the drawable space + //getInternalWidth() and getInternalHeight() are the width and height of the drawable space + + //the actual offset from the center (with appropriate sign based on value) + float circleOffsetActual = 0; + if(value){ + circleColor.set(0.9f, 0.9f, 0.9f); + barColor.set(0.5f, 0.9f, 0.5f); + circleOffsetActual = CIRCLE_OFFSET_FROM_CENTER; + } else { + circleColor.set(0.9f, 0.9f, 0.9f); + barColor.set(0.9f, 0.5f, 0.5f); + circleOffsetActual = -CIRCLE_OFFSET_FROM_CENTER; + } + //ratio to adjust the circlewidth by to always show a circle and not a deformed oval + float circleRatio = getInternalWidth() / (float)getInternalHeight(); + + Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); + if(planeModel != null){ + //draw bar + ndcX = (float)(getInternalX() + (getInternalWidth() * ((1.0f - CIRCLE_WIDTH)/2.0f)) + parentPosX)/parentWidth; + ndcY = (float)(getInternalY() + (getInternalHeight() * ((1.0f - BAR_HEIGHT) / 2.0f)) + parentPosY)/parentHeight; + ndcWidth = (float)((getInternalWidth()) - (getInternalWidth() * ((1.0f - CIRCLE_WIDTH))))/parentWidth; + ndcHeight = (float)(getInternalHeight() * BAR_HEIGHT)/parentHeight; + boxPosition = new Vector3f(ndcX,ndcY,0); + boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); + planeModel.getMeshes().get(0).setMaterial(barMat); + planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); + planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", barColor); + planeModel.drawUI(); + + //draw circle + ndcX = (float)(getInternalX() + (getInternalWidth() * ((1.0f - CIRCLE_WIDTH) / 2.0f)) + (getInternalWidth() * circleOffsetActual) + parentPosX)/parentWidth; + ndcY = (float)(getInternalY() + (getInternalHeight() * ((1.0f - (CIRCLE_WIDTH * circleRatio)) / 2.0f)) + parentPosY)/parentHeight; + ndcWidth = (float)((getInternalWidth() * CIRCLE_WIDTH))/parentWidth; + ndcHeight = (float)(getInternalHeight() * (CIRCLE_WIDTH * circleRatio))/parentHeight; + boxPosition = new Vector3f(ndcX,ndcY,0); + boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); + planeModel.getMeshes().get(0).setMaterial(circleMat); + planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); + planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", circleColor); + planeModel.drawUI(); + } else { + LoggerInterface.loggerRenderer.ERROR("Window unable to find plane model!!", new Exception()); + } + } + + /** + * Handles an event + */ + public boolean handleEvent(Event event){ + boolean propagate = true; + if(event instanceof ClickEvent){ + ClickEvent clickEvent = (ClickEvent)event; + if(onClickCallback != null){ + onClickCallback.execute(clickEvent); + } else { + Globals.elementManager.focusElement(this); + this.value = !this.value; + Globals.elementManager.fireEventNoPosition(new ValueChangeEvent(absoluteX), this); + propagate = false; + } + } else if(event instanceof ValueChangeEvent){ + ValueChangeEvent valueEvent = (ValueChangeEvent)event; + if(this.onValueChangeCallback != null){ + this.onValueChangeCallback.execute(valueEvent); + } + } + return propagate; + } + + + @Override + public void setOnValueChangeCallback(ValueChangeEventCallback callback) { + this.onValueChangeCallback = callback; + } + + @Override + public void setOnClick(ClickEventCallback callback) { + this.onClickCallback = callback; + } + + /** + * Sets the value of the toggle + * @param value The value to set the toggle to + */ + public void setValue(boolean value){ + this.value = value; + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/elements/Window.java b/src/main/java/electrosphere/renderer/ui/elements/Window.java index 61eae6a9..53110468 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Window.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Window.java @@ -35,6 +35,9 @@ import electrosphere.renderer.ui.events.NavigationEvent; * A window */ public class Window implements DrawableElement, ContainerElement, NavigableElement { + + static Vector3f color = new Vector3f(1.0f); + List childList = new LinkedList(); Framebuffer widgetBuffer; Material customMat = new Material(); @@ -139,6 +142,7 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); customMat.setTexturePointer(windowFrame.getTexturePointer()); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.drawUI(); @@ -150,6 +154,7 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); planeModel.pushUniformToMesh("plane", "tPosition", texPosition); planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); customMat.setTexturePointer(widgetBuffer.getTexturePointer()); planeModel.getMeshes().get(0).setMaterial(customMat); planeModel.drawUI(); diff --git a/src/main/java/electrosphere/renderer/ui/elementtypes/FocusableElement.java b/src/main/java/electrosphere/renderer/ui/elementtypes/FocusableElement.java index 52a459f3..6a638862 100644 --- a/src/main/java/electrosphere/renderer/ui/elementtypes/FocusableElement.java +++ b/src/main/java/electrosphere/renderer/ui/elementtypes/FocusableElement.java @@ -2,17 +2,45 @@ package electrosphere.renderer.ui.elementtypes; import electrosphere.renderer.ui.events.FocusEvent; +/** + * A focusable element + */ public interface FocusableElement extends Element { + /** + * Gets the focused status of the element + * @return true if focused, false otherwise + */ public boolean isFocused(); + /** + * Set the focused status of the elemtn + * @param focused true if focused, false otherwise + */ + public void setFocused(boolean focused); + + /** + * Sets the on focus callback + * @param callback The callback + */ public abstract void setOnFocus(FocusEventCallback callback); + /** + * Sets the on lose focus callback + * @param callback The callback + */ public abstract void setOnLoseFocus(FocusEventCallback callback); - + /** + * A focus event callback + */ public abstract interface FocusEventCallback { + /** + * Executes the callback + * @param event The focus event + * @return true if the event should propagate to the parent of this element, false otherwise + */ public boolean execute(FocusEvent event); } diff --git a/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java b/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java index 31aa15ab..4c318c5d 100644 --- a/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java +++ b/src/main/java/electrosphere/renderer/ui/events/ValueChangeEvent.java @@ -11,6 +11,7 @@ public class ValueChangeEvent implements Event { public static enum ValueType { STRING, FLOAT, + BOOLEAN, } /** @@ -23,6 +24,11 @@ public class ValueChangeEvent implements Event { */ float valueFloat; + /** + * The boolean value + */ + boolean valueBoolean; + /** * The type of this event */ @@ -46,6 +52,15 @@ public class ValueChangeEvent implements Event { valueType = ValueType.FLOAT; } + /** + * Constructor for boolean value changes + * @param value The boolean + */ + public ValueChangeEvent(boolean value){ + valueBoolean = value; + valueType = ValueType.BOOLEAN; + } + /** * Gets the type of the value * @return The type of the value @@ -70,4 +85,12 @@ public class ValueChangeEvent implements Event { return valueString; } + /** + * Gets the value that changed as a boolean + * @return The boolean value + */ + public boolean getAsBoolean(){ + return valueBoolean; + } + } diff --git a/src/main/java/electrosphere/renderer/ui/macros/InputMacros.java b/src/main/java/electrosphere/renderer/ui/macros/InputMacros.java index 7812d2da..6996dd8c 100644 --- a/src/main/java/electrosphere/renderer/ui/macros/InputMacros.java +++ b/src/main/java/electrosphere/renderer/ui/macros/InputMacros.java @@ -6,6 +6,7 @@ import electrosphere.renderer.ui.elements.Div; import electrosphere.renderer.ui.elements.Label; import electrosphere.renderer.ui.elements.Slider; import electrosphere.renderer.ui.elements.TextInput; +import electrosphere.renderer.ui.elements.ToggleInput; import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection; import electrosphere.renderer.ui.elementtypes.ValueElement.ValueChangeEventCallback; @@ -96,5 +97,29 @@ public class InputMacros { return rVal; } + + /** + * Creates a toggle with a label + * @param label The label + * @param defaultValue The default value + * @param onChange The on change callback + * @return The div containing a labeled toggle + */ + public static Div createToggle(String label, boolean defaultValue, Consumer onChange){ + Div rVal = Div.createDiv(); + rVal.setFlexDirection(YogaFlexDirection.Row); + + //the label + Label labelEl = Label.createLabel(label); + labelEl.setMarginRight(LABEL_MARGIN); + rVal.addChild(labelEl); + + //the actual input + ToggleInput toggleInput = ToggleInput.createToggleInput(); + toggleInput.setValue(defaultValue); + rVal.addChild(toggleInput); + + return rVal; + } } diff --git a/src/main/java/electrosphere/server/saves/SaveUtils.java b/src/main/java/electrosphere/server/saves/SaveUtils.java index 997a3ebf..609007a7 100644 --- a/src/main/java/electrosphere/server/saves/SaveUtils.java +++ b/src/main/java/electrosphere/server/saves/SaveUtils.java @@ -102,7 +102,9 @@ public class SaveUtils { FileUtils.serializeObjectToSavePath(saveName, "/save.json", save); //write scene file - FileUtils.serializeObjectToSavePath(saveName, "/scene.json", sceneFile); + if(sceneFile.getCreateSaveInstance()){ + FileUtils.serializeObjectToSavePath(saveName, "/scene.json", sceneFile); + } //create server structures if(sceneFile.getRealmDescriptor().getType() == RealmDescriptor.REALM_DESCRIPTOR_PROCEDURAL){ diff --git a/src/test/java/startup/StartupTest.java b/src/test/java/startup/StartupTest.java index 08817f67..c9462e0d 100644 --- a/src/test/java/startup/StartupTest.java +++ b/src/test/java/startup/StartupTest.java @@ -18,8 +18,8 @@ public class StartupTest { Globals.HEADLESS = true; Profiler.PROFILE = false; NetUtils.setPort(0); - Main.startUp(); - Main.mainLoop(1); + // Main.startUp(); + // Main.mainLoop(1); } @Test