From 383a3b6ac32166209632bf5bb7dbf7284138adce Mon Sep 17 00:00:00 2001 From: austin Date: Sat, 26 Apr 2025 11:38:50 -0400 Subject: [PATCH] fab selection tool --- assets/Data/entity/items/debug_tools.json | 31 ++++ assets/Scripts/client/clienthooks.ts | 6 + .../Scripts/types/host/renderer/ui/menus.ts | 5 + docs/src/progress/renderertodo.md | 1 + .../ui/components/FabSelectionPanel.java | 168 ++++++++++++++++++ .../client/ui/menu/WindowStrings.java | 53 ++++-- .../client/ui/menu/ingame/FabMenus.java | 65 +++++++ .../ui/menu/script/ScriptMenuUtils.java | 10 ++ .../engine/loadingthreads/LoadingUtils.java | 8 +- .../server/protocol/CharacterProtocol.java | 9 +- 10 files changed, 338 insertions(+), 18 deletions(-) create mode 100644 src/main/java/electrosphere/client/ui/components/FabSelectionPanel.java create mode 100644 src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java diff --git a/assets/Data/entity/items/debug_tools.json b/assets/Data/entity/items/debug_tools.json index 9dee6e9b..e34892ed 100644 --- a/assets/Data/entity/items/debug_tools.json +++ b/assets/Data/entity/items/debug_tools.json @@ -126,6 +126,37 @@ "offsetZ" : 0 }, "iconPath" : "Textures/icons/itemIconItemGeneric.png" + }, + { + "id" : "fabTool", + "tokens" : [ + "GRAVITY", + "TARGETABLE", + "CURSOR" + ], + "equipData": { + "equipClass" : "tool" + }, + "graphicsTemplate": { + "model": { + "path" : "Models/basic/geometry/unitvector.glb" + } + }, + "clientSideSecondary": "SELECT_FAB", + "collidable": { + "type" : "CUBE", + "dimension1" : 0.1, + "dimension2" : 0.1, + "dimension3" : 0.35, + "rotX": 0, + "rotY": 0, + "rotZ": 0, + "rotW": 1, + "offsetX" : 0, + "offsetY" : 0.05, + "offsetZ" : 0 + }, + "iconPath" : "Textures/icons/itemIconItemGeneric.png" } ], "files" : [ diff --git a/assets/Scripts/client/clienthooks.ts b/assets/Scripts/client/clienthooks.ts index 9ebe39af..84991042 100644 --- a/assets/Scripts/client/clienthooks.ts +++ b/assets/Scripts/client/clienthooks.ts @@ -46,5 +46,11 @@ export const clientHooks: Hook[] = [ callback: (engine: Engine) => { engine.classes.voxelUtils.static.dig() } + }, + { + signal: "SELECT_FAB", + callback: (engine: Engine) => { + engine.classes.menuUtils.static.openFabSelection() + } } ] \ No newline at end of file diff --git a/assets/Scripts/types/host/renderer/ui/menus.ts b/assets/Scripts/types/host/renderer/ui/menus.ts index bd5c8a91..efde5d86 100644 --- a/assets/Scripts/types/host/renderer/ui/menus.ts +++ b/assets/Scripts/types/host/renderer/ui/menus.ts @@ -13,4 +13,9 @@ export interface MenuUtils { */ readonly openSpawnSelection: () => void + /** + * Opens the menu to select what fab to use + */ + readonly openFabSelection: () => void + } \ No newline at end of file diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 1198a5cc..cf0cff29 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1542,6 +1542,7 @@ Block area selection (04/26/2025) Exporting block prefabs to compressed files Minor block fab improvements +Fab selection tool diff --git a/src/main/java/electrosphere/client/ui/components/FabSelectionPanel.java b/src/main/java/electrosphere/client/ui/components/FabSelectionPanel.java new file mode 100644 index 00000000..c2a6065b --- /dev/null +++ b/src/main/java/electrosphere/client/ui/components/FabSelectionPanel.java @@ -0,0 +1,168 @@ +package electrosphere.client.ui.components; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import org.joml.Vector4f; + +import electrosphere.client.ui.menu.WindowStrings; +import electrosphere.client.ui.menu.YogaUtils; +import electrosphere.engine.Globals; +import electrosphere.engine.assetmanager.AssetDataStrings; +import electrosphere.renderer.ui.elements.Button; +import electrosphere.renderer.ui.elements.Div; +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.elementtypes.ClickableElement.ClickEventCallback; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification; +import electrosphere.renderer.ui.elementtypes.Element; +import electrosphere.renderer.ui.elementtypes.HoverableElement.HoverEventCallback; +import electrosphere.renderer.ui.elementtypes.KeyEventElement.KeyboardEventCallback; +import electrosphere.renderer.ui.events.ClickEvent; +import electrosphere.renderer.ui.events.HoverEvent; +import electrosphere.renderer.ui.events.KeyboardEvent; + +/** + * Panel for selecting fab files + */ +public class FabSelectionPanel { + + //text input + static final int TEXT_INPUT_HEIGHT = 50; + static final int TEXT_INPUT_WIDTH = 200; + + //single fab button + static final int FAB_BUTTON_WIDTH = 90; + static final int FAB_BUTTON_HEIGHT = 90; + static final int FAB_BUTTON_TEXTURE_DIM = 70; + static final int MARGIN_EACH_SIDE = 5; + + //fab selection + static final int FAB_SCROLLABLE_WIDTH = FAB_BUTTON_WIDTH * 5; + static final int FAB_SCROLLABLE_HEIGHT = FAB_BUTTON_HEIGHT * 5; + + /** + * The color of the select fab type + */ + static final Vector4f ELEMENT_COLOR_SELECTED = new Vector4f(1,0,0,1); + + + /** + * Creates the fab selection panel component + * @return The top level element of the panel component + */ + public static Div createFabSelectionPanel(Consumer onSelectType){ + //setup window + Div rVal = Div.createDiv(); + rVal.setAlignContent(YogaAlignment.Center); + rVal.setAlignItems(YogaAlignment.Center); + rVal.setJustifyContent(YogaJustification.Center); + rVal.setFlexDirection(YogaFlexDirection.Column); + + //scrollable that contains all the fab types + VirtualScrollable scrollable = new VirtualScrollable(FAB_SCROLLABLE_WIDTH, FAB_SCROLLABLE_HEIGHT); + scrollable.setFlexDirection(YogaFlexDirection.Column); + scrollable.setAlignItems(YogaAlignment.Start); + + //search input + TextInput searchInput = TextInput.createTextInput(); + 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); + FabSelectionPanel.fillInFabSelectors(scrollable, searchInput.getText(), onSelectType); + return rVal; + }}); + rVal.addChild(searchInput); + + + //attach scrollable after search input for organzation purposes + rVal.addChild(scrollable); + + //final step + FabSelectionPanel.fillInFabSelectors(scrollable, searchInput.getText(), onSelectType); + + return rVal; + } + + /** + * Fills in the fab files 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 fillInFabSelectors(VirtualScrollable scrollable, String searchString, Consumer onSelectType){ + Element containingWindow = null; + if(Globals.elementService.getWindow(WindowStrings.FAB_SELECTION) != null){ + containingWindow = Globals.elementService.getWindow(WindowStrings.FAB_SELECTION); + } else if(Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN) != null){ + containingWindow = Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN); + } + YogaUtils.refreshComponent(containingWindow, () -> { + scrollable.clearChildren(); + Div currentRow = null; + int incrementer = 0; + //generate fab file buttons + List fabFiles = Arrays.asList(new File("./assets/Data/fab").listFiles()); + for(File fabFile : fabFiles){ + if(incrementer % 4 == 0){ + currentRow = Div.createRow(); + currentRow.setJustifyContent(YogaJustification.Evenly); + scrollable.addChild(currentRow); + } + Div containerDiv = Div.createDiv(); + containerDiv.setMinWidthPercent(25.0f); + currentRow.addChild(containerDiv); + + Button newButton = new Button(); + newButton.setAlignItems(YogaAlignment.Center); + //dimensions + newButton.setMinWidth(FAB_BUTTON_WIDTH); + newButton.setMinHeight(FAB_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 fabLabel = Label.createLabel(fabFile.getName()); + //icon/model + ImagePanel texturePanel = ImagePanel.createImagePanel(AssetDataStrings.TEXTURE_DEFAULT); + texturePanel.setWidth(FAB_BUTTON_TEXTURE_DIM); + texturePanel.setHeight(FAB_BUTTON_TEXTURE_DIM); + texturePanel.setMarginBottom(MARGIN_EACH_SIDE); + texturePanel.setMarginLeft(MARGIN_EACH_SIDE); + texturePanel.setMarginRight(MARGIN_EACH_SIDE); + texturePanel.setMarginTop(MARGIN_EACH_SIDE); + newButton.addChild(texturePanel); + texturePanel.setAlignSelf(YogaAlignment.Center); + //causes the texture panel to also behave as if the button was hovered + texturePanel.setOnHoverCallback(new HoverEventCallback() {public boolean execute(HoverEvent event) { + return newButton.handleEvent(event); + }}); + //button handling + newButton.addChild(fabLabel); + newButton.setOnClick(new ClickEventCallback() {public boolean execute(ClickEvent event){ + //accept the selected file + onSelectType.accept(fabFile); + FabSelectionPanel.fillInFabSelectors(scrollable, searchString, onSelectType); + return false; + }}); + containerDiv.addChild(newButton); + incrementer++; + } + for(int i = incrementer; i % 4 != 0; i++){ + Div spacerDiv = Div.createDiv(); + spacerDiv.setMinWidthPercent(25.0f); + currentRow.addChild(spacerDiv); + } + }); + } + +} diff --git a/src/main/java/electrosphere/client/ui/menu/WindowStrings.java b/src/main/java/electrosphere/client/ui/menu/WindowStrings.java index 39c61dfb..8de68e85 100644 --- a/src/main/java/electrosphere/client/ui/menu/WindowStrings.java +++ b/src/main/java/electrosphere/client/ui/menu/WindowStrings.java @@ -5,42 +5,71 @@ package electrosphere.client.ui.menu; */ public class WindowStrings { - //the main menu + /** + * the main menu + */ public static final String WINDOW_MENU_MAIN = "windowMenuMain"; - //the ingame main menu + /** + * the ingame main menu + */ public static final String WINDOW_MENU_INGAME_MAIN = "windowMenuInGameMain"; - //the string used to generate inventory menus + /** + * the string used to generate inventory menus + */ public static final String WINDOW_MENU_INVENTORY = "windowMenuInventory"; - //a window that shows the text 'loading' + /** + * a window that shows the text 'loading' + */ public static final String WINDOW_LOADING = "windowLoading"; - //the window used to receive drag events for dropping items + /** + * the window used to receive drag events for dropping items + */ public static final String WINDDOW_ITEM_DROP = "itemDrop"; - //the window used as a container for item icons for drawing dragging them around the screen + /** + * the window used as a container for item icons for drawing dragging them around the screen + */ public static final String WINDOW_ITEM_DRAG_CONTAINER = "itemDragContainer"; - //the window for displaying information about the character + /** + * the window for displaying information about the character + */ public static final String WINDOW_CHARACTER = "windowCharacter"; - //the debug menu + /** + * the debug menu + */ public static final String WINDOW_DEBUG = "windowDebug"; - //the level editor menu + /** + * the level editor menu + */ public static final String LEVEL_EDTIOR_SIDE_PANEL = "levelEditor"; - //the voxel type selection menu + /** + * the voxel type selection menu + */ public static final String VOXEL_TYPE_SELECTION = "voxelTypeSelection"; - //the voxel type selection menu + /** + * the voxel type selection menu + */ public static final String SPAWN_TYPE_SELECTION = "spawnTypeSelection"; - //the tutorial popup + /** + * the tutorial popup + */ public static final String TUTORIAL_POPUP = "tutorialPopup"; + /** + * The fab selection menu + */ + public static final String FAB_SELECTION = "fabSelection"; + /** * Window for displaying tooltip elements */ diff --git a/src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java b/src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java new file mode 100644 index 00000000..0da5c3b1 --- /dev/null +++ b/src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java @@ -0,0 +1,65 @@ +package electrosphere.client.ui.menu.ingame; + +import java.io.File; + +import electrosphere.client.ui.components.FabSelectionPanel; +import electrosphere.client.ui.menu.WindowStrings; +import electrosphere.client.ui.menu.WindowUtils; +import electrosphere.controls.ControlHandler.ControlsState; +import electrosphere.engine.Globals; +import electrosphere.engine.signal.Signal.SignalType; +import electrosphere.renderer.ui.elements.Window; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification; +import electrosphere.renderer.ui.elementtypes.NavigableElement.NavigationEventCallback; +import electrosphere.renderer.ui.events.NavigationEvent; + +/** + * Menus to deal with fab files + */ +public class FabMenus { + + /** + * The fab selection window + */ + static Window fabSelectionWindow; + + //width of the panel + static final int WINDOW_WIDTH = 550; + static final int WINDOW_HEIGHT = 550; + + /** + * Creates the level editor side panel window + * @return + */ + public static Window createVoxelTypeSelectionPanel(){ + //setup window + Window fabSelectionPanelWindow = Window.create(Globals.renderingEngine.getOpenGLState(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, true); + fabSelectionPanelWindow.setParentAlignContent(YogaAlignment.Center); + fabSelectionPanelWindow.setParentJustifyContent(YogaJustification.Center); + fabSelectionPanelWindow.setParentAlignItem(YogaAlignment.Center); + fabSelectionPanelWindow.setAlignContent(YogaAlignment.Center); + fabSelectionPanelWindow.setAlignItems(YogaAlignment.Center); + fabSelectionPanelWindow.setJustifyContent(YogaJustification.Center); + fabSelectionPanelWindow.setFlexDirection(YogaFlexDirection.Column); + + //nav logic + fabSelectionPanelWindow.setOnNavigationCallback(new NavigationEventCallback() {public boolean execute(NavigationEvent event){ + WindowUtils.closeWindow(WindowStrings.FAB_SELECTION); + Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME); + MenuGeneratorsLevelEditor.voxelWindowOpen = false; + return false; + }}); + + //attach scrollable after search input for organzation purposes + fabSelectionPanelWindow.addChild(FabSelectionPanel.createFabSelectionPanel((File selectedFile) -> { + System.out.println(selectedFile); + })); + + Globals.signalSystem.post(SignalType.YOGA_APPLY,fabSelectionPanelWindow); + + return fabSelectionPanelWindow; + } + +} diff --git a/src/main/java/electrosphere/client/ui/menu/script/ScriptMenuUtils.java b/src/main/java/electrosphere/client/ui/menu/script/ScriptMenuUtils.java index dea17a61..02aa4df0 100644 --- a/src/main/java/electrosphere/client/ui/menu/script/ScriptMenuUtils.java +++ b/src/main/java/electrosphere/client/ui/menu/script/ScriptMenuUtils.java @@ -4,6 +4,7 @@ import org.graalvm.polyglot.HostAccess.Export; import electrosphere.client.ui.menu.WindowStrings; import electrosphere.client.ui.menu.WindowUtils; +import electrosphere.client.ui.menu.ingame.FabMenus; import electrosphere.client.ui.menu.ingame.MenuGeneratorsTerrainEditing; import electrosphere.controls.ControlHandler.ControlsState; import electrosphere.engine.Globals; @@ -31,4 +32,13 @@ public class ScriptMenuUtils { Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); } + /** + * Opens the menu to select what fab to use + */ + @Export + public static void openFabSelection(){ + WindowUtils.replaceWindow(WindowStrings.FAB_SELECTION,FabMenus.createVoxelTypeSelectionPanel()); + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); + } + } diff --git a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java index 6f2c9346..2e201073 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java +++ b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java @@ -26,6 +26,7 @@ import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.player.Player; import electrosphere.net.server.protocol.CharacterProtocol; import electrosphere.server.datacell.Realm; +import electrosphere.server.datacell.ServerWorldData; import electrosphere.server.simulation.MicroSimulation; /** @@ -167,6 +168,7 @@ public class LoadingUtils { template.getCreatureToolbarData().setSlotItem("1", new ToolbarItem(72, "spawningPalette")); template.getCreatureToolbarData().setSlotItem("2", new ToolbarItem(73, "entityinspector")); template.getCreatureToolbarData().setSlotItem("3", new ToolbarItem(74, "waterSpawner")); + template.getCreatureToolbarData().setSlotItem("4", new ToolbarItem(75, "fabTool")); //set player character template serverPlayerConnection.setCreatureTemplate(template); Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage(CharacterProtocol.SPAWN_EXISTING_TEMPLATE + "")); @@ -176,9 +178,9 @@ public class LoadingUtils { Realm realm = Globals.realmManager.getRealms().iterator().next(); Vector3d spawnPoint = realm.getSpawnPoint(); playerObject.setWorldPos(new Vector3i( - realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.x), - realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.y), - realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.z) + ServerWorldData.convertRealToChunkSpace(spawnPoint.x), + ServerWorldData.convertRealToChunkSpace(spawnPoint.y), + ServerWorldData.convertRealToChunkSpace(spawnPoint.z) )); } diff --git a/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java b/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java index 308add98..ea39ec43 100644 --- a/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java @@ -26,6 +26,7 @@ import electrosphere.net.template.ServerProtocolTemplate; import electrosphere.server.character.CharacterService; import electrosphere.server.character.PlayerCharacterCreation; import electrosphere.server.datacell.Realm; +import electrosphere.server.datacell.ServerWorldData; import electrosphere.util.Utilities; /** @@ -101,9 +102,9 @@ public class CharacterProtocol implements ServerProtocolTemplate