diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java index 6a4e93f80..2576912c7 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java @@ -246,11 +246,26 @@ public class CommandStudio implements DecreeExecutor { engine().getExecution().execute(script.getLoadKey()); } - @Decree(description = "Open the noise explorer (External GUI)", aliases = {"nmap", "n"}) + @Decree(description = "Open the map on its noise explorer (External GUI)", aliases = {"nmap", "n"}) public void noise() { if (noGUI()) return; - sender().sendMessage(C.GREEN + "Opening Noise Explorer!"); - NoiseExplorerGUI.launch(engine()); + Engine playerEngine = engine(); + Engine engine = playerEngine != null ? playerEngine : GuiHost.findActiveEngine(); + World world = engine == null ? null : engine.getWorld().realWorld(); + if (world == null) { + sender().sendMessage(C.RED + "Open a studio or stand in an Iris world to explore its noise!"); + return; + } + Location center = playerEngine != null ? player().getLocation() : null; + sender().sendMessage(C.GREEN + "Opening the map on its noise!"); + MapWindow.openNoise(new PaletteExplorerStart( + engine, + world.getUID(), + sender().isPlayer() ? player().getUniqueId() : null, + null, + center == null ? 0D : center.getX(), + center == null ? 0D : center.getZ() + )); } @Decree(description = "Preview the palettes around you (External GUI)", aliases = "pal", origin = DecreeOrigin.PLAYER) diff --git a/core/src/main/java/com/volmit/iris/core/gui/MapView.java b/core/src/main/java/com/volmit/iris/core/gui/MapView.java index a35d361e9..3f4067dd3 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/MapView.java +++ b/core/src/main/java/com/volmit/iris/core/gui/MapView.java @@ -43,7 +43,9 @@ enum MapView { LAYER_LOAD("Layers", "How many block layers each biome stacks, brighter is more", Group.LOAD, RenderType.LAYER_LOAD, KeyEvent.VK_0), PALETTES("Palettes", "The blocks each biome's palette layers place, with their textures", - Group.BLOCKS, null, KeyEvent.VK_P); + Group.BLOCKS, null, KeyEvent.VK_P), + NOISE("Noise", "The pack's noise generators and Iris' built-in noise styles", + Group.GENERATORS, null, KeyEvent.VK_N); private final String label; private final String description; @@ -72,7 +74,7 @@ enum MapView { } /** - * The tile renderer's mode for this view, or null for the palette view, which draws itself. + * The tile renderer's mode for this view, or null for the palette and noise views, which draw themselves. */ RenderType renderType() { return renderType; @@ -103,7 +105,8 @@ enum MapView { WORLD("World"), BIOME_STREAMS("Biome layers"), LOAD("Generation load"), - BLOCKS("Blocks"); + BLOCKS("Blocks"), + GENERATORS("Generators"); private final String label; diff --git a/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java b/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java index cd3f2cf03..eb72b1773 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java +++ b/core/src/main/java/com/volmit/iris/core/gui/MapWindow.java @@ -47,16 +47,18 @@ import java.util.ArrayList; import java.util.List; /** - * The studio map: one window showing the world as tiles in several views, with the palette explorer as its Palettes view. + * The studio map: one window showing the world as tiles in several views, with the palette and noise explorers as its + * Palettes and Noise views. */ public final class MapWindow implements MapCanvas.Listener { private static final String MAP_CARD = "map"; private static final String PALETTES_CARD = "palettes"; + private static final String NOISE_CARD = "noise"; private static final int SIDEBAR_WIDTH = 270; private static final int SIDEBAR_MINIMUM_WIDTH = 200; private static final int CONTENT_MINIMUM_WIDTH = 320; private static final double PALETTE_RECENTER_DISTANCE_BLOCKS = 1D; - private static final double VIEW_LIST_SHARE_WITH_PALETTES = 0.34D; + private static final double VIEW_LIST_SHARE_WITH_EXPLORER = 0.34D; private final JFrame frame; private final PaletteExplorerStart start; @@ -69,7 +71,11 @@ public final class MapWindow implements MapCanvas.Listener { private final JPanel inspectorSlot = new JPanel(new BorderLayout()); private final JComponent viewListScroll; private final List mapControls = new ArrayList<>(); - private final List paletteControls = new ArrayList<>(); + private final List explorerControls = new ArrayList<>(); + private final List noiseControls = new ArrayList<>(); + private final JToolBar toolbar = StudioToolbar.create(); + private final JComponent resetViewButton = StudioToolbar.button("Reset view", "Go back to the starting position and zoom (R)", + this::resetExplorerView); private final JToggleButton gridToggle = StudioToolbar.toggle("Grid", "Show chunk and region borders (G)"); private final JToggleButton entitiesToggle = StudioToolbar.toggle("Entities", "Show mobs and other entities (E)"); private final JToggleButton followToggle = StudioToolbar.toggle("Follow", "Keep your player in the middle (F)"); @@ -78,6 +84,8 @@ public final class MapWindow implements MapCanvas.Listener { private final MapLegendPanel legendPanel; private NoiseExplorerGUI paletteExplorer; + private NoiseExplorerGUI noiseExplorer; + private JComponent noiseSidebar; private MapView currentView; private double paletteCenterX; private double paletteCenterZ; @@ -101,6 +109,10 @@ public final class MapWindow implements MapCanvas.Listener { EventQueue.invokeLater(() -> open(start, MapView.PALETTES)); } + public static void openNoise(PaletteExplorerStart start) { + EventQueue.invokeLater(() -> open(start, MapView.NOISE)); + } + private static void open(PaletteExplorerStart start, MapView initialView) { StudioTheme.install(); MapWindow window = new MapWindow(start); @@ -133,7 +145,6 @@ public final class MapWindow implements MapCanvas.Listener { } private JToolBar buildToolbar() { - JToolBar toolbar = StudioToolbar.create(); viewTitle.setFont(StudioTheme.titleFont()); viewTitle.setBorder(StudioTheme.emptyBorder(0, 6, 0, 14)); toolbar.add(viewTitle); @@ -153,8 +164,8 @@ public final class MapWindow implements MapCanvas.Listener { bordersToggle.addActionListener(event -> canvas.toggleRegionBorders()); mapControls.forEach(toolbar::add); - paletteControls.add(StudioToolbar.button("Reset view", "Go back to the starting position and zoom (R)", this::resetPaletteView)); - paletteControls.forEach(toolbar::add); + explorerControls.add(resetViewButton); + explorerControls.forEach(toolbar::add); toolbar.add(Box.createHorizontalGlue()); JComponent help = StudioToolbar.button("Shortcuts", "Show every mouse and key shortcut (/)", canvas::toggleHelp); @@ -180,8 +191,10 @@ public final class MapWindow implements MapCanvas.Listener { viewTitle.setToolTipText(view.description()); if (view.isTileMap()) { showMap(previousView, view); - } else { + } else if (view == MapView.PALETTES) { showPalettes(previousView); + } else { + showNoise(); } frame.getRootPane().revalidate(); frame.getRootPane().repaint(); @@ -193,8 +206,7 @@ public final class MapWindow implements MapCanvas.Listener { } canvas.showView(view); canvas.setActive(true); - setControlsVisible(mapControls, true); - setControlsVisible(paletteControls, false); + showControls(mapControls); useSidebar(viewListScroll); inspectorSlot.removeAll(); inspectorSlot.add(legendPanel, BorderLayout.CENTER); @@ -210,13 +222,8 @@ public final class MapWindow implements MapCanvas.Listener { followMapIntoPalettes(); } canvas.setActive(false); - setControlsVisible(mapControls, false); - setControlsVisible(paletteControls, true); - JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, viewListScroll, paletteExplorer.paletteSidebar()); - split.setBorder(BorderFactory.createEmptyBorder()); - split.setResizeWeight(0D); - split.setDividerLocation(viewListHeightBesidePalettes()); - useSidebar(split); + showControls(explorerControls); + useSidebar(viewListAbove(paletteExplorer.paletteSidebar())); inspectorSlot.removeAll(); inspectorSlot.add(paletteExplorer.paletteInspector(), BorderLayout.CENTER); showCard(PALETTES_CARD); @@ -226,13 +233,46 @@ public final class MapWindow implements MapCanvas.Listener { } } - // The palettes need the room more than the view list, which scrolls once it is squeezed. - private int viewListHeightBesidePalettes() { + private void showNoise() { + boolean firstOpen = noiseExplorer == null; + if (firstOpen) { + createNoiseExplorer(); + } + canvas.setActive(false); + showControls(noiseControls, explorerControls); + useSidebar(viewListAbove(noiseSidebar)); + inspectorSlot.removeAll(); + showCard(NOISE_CARD); + EventQueue.invokeLater(() -> viewList.select(currentView)); + if (firstOpen) { + noiseExplorer.openViewer(); + } + } + + private JSplitPane viewListAbove(JComponent explorerSidebar) { + JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, viewListScroll, explorerSidebar); + split.setBorder(BorderFactory.createEmptyBorder()); + split.setResizeWeight(0D); + split.setDividerLocation(viewListHeightAboveExplorer()); + return split; + } + + // The explorer needs the room more than the view list, which scrolls once it is squeezed. + private int viewListHeightAboveExplorer() { int fullHeight = viewList.getPreferredSize().height + 4; if (sidebar.getHeight() < 1) { return fullHeight; } - return Math.min(fullHeight, (int) (sidebar.getHeight() * VIEW_LIST_SHARE_WITH_PALETTES)); + return Math.min(fullHeight, (int) (sidebar.getHeight() * VIEW_LIST_SHARE_WITH_EXPLORER)); + } + + private void createNoiseExplorer() { + noiseExplorer = NoiseExplorerGUI.createNoiseExplorer(start.engine()); + noiseSidebar = noiseExplorer.noiseSidebar(); + noiseControls.addAll(noiseExplorer.noiseControls()); + noiseControls.forEach(control -> toolbar.add(control, toolbar.getComponentIndex(resetViewButton))); + content.add(noiseExplorer, NOISE_CARD); + statusBars.add(noiseExplorer.statusBar(), NOISE_CARD); } private void createPaletteExplorer(boolean openedOnPalettes) { @@ -293,9 +333,10 @@ public final class MapWindow implements MapCanvas.Listener { legendPanel.show(title, shares, highlight); } - private void resetPaletteView() { - if (paletteExplorer != null) { - paletteExplorer.resetViewport(); + private void resetExplorerView() { + NoiseExplorerGUI explorer = currentView == MapView.NOISE ? noiseExplorer : paletteExplorer; + if (explorer != null) { + explorer.resetViewport(); } } @@ -309,8 +350,14 @@ public final class MapWindow implements MapCanvas.Listener { ((CardLayout) statusBars.getLayout()).show(statusBars, card); } - private static void setControlsVisible(List controls, boolean visible) { - controls.forEach(control -> control.setVisible(visible)); + @SafeVarargs + private void showControls(List... visibleControls) { + for (List controls : List.of(mapControls, explorerControls, noiseControls)) { + controls.forEach(control -> control.setVisible(false)); + } + for (List controls : visibleControls) { + controls.forEach(control -> control.setVisible(true)); + } } private void installShortcuts(JRootPane rootPane) { @@ -370,7 +417,7 @@ public final class MapWindow implements MapCanvas.Listener { if (currentView.isTileMap()) { canvas.refreshContent(); } else { - resetPaletteView(); + resetExplorerView(); } } @@ -379,5 +426,8 @@ public final class MapWindow implements MapCanvas.Listener { if (paletteExplorer != null) { paletteExplorer.close(); } + if (noiseExplorer != null) { + noiseExplorer.close(); + } } } diff --git a/core/src/main/java/com/volmit/iris/core/gui/NoiseExplorerGUI.java b/core/src/main/java/com/volmit/iris/core/gui/NoiseExplorerGUI.java index 6a927bb11..956e50500 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/NoiseExplorerGUI.java +++ b/core/src/main/java/com/volmit/iris/core/gui/NoiseExplorerGUI.java @@ -209,18 +209,6 @@ public final class NoiseExplorerGUI extends JPanel { GuiHost.registerHotloadHook(hotloadHook); } - /** - * @param engine the engine whose pack generators to list, or null for the open studio or first Iris world - */ - public static void launch(Engine engine) { - Engine sourceEngine = engine != null ? engine : GuiHost.findActiveEngine(); - EventQueue.invokeLater(() -> { - StudioTheme.install(); - NoiseExplorerGUI explorer = new NoiseExplorerGUI(ExplorerMode.NOISE, sourceEngine, null, null, null, DEFAULT_SEED, null); - buildNoiseFrame("Noise Explorer", explorer); - }); - } - public static void launchGeneratorKey(Engine engine, String generatorKey, IrisGenerator fallbackGenerator, long initialSeed) { Engine sourceEngine = engine != null ? engine : GuiHost.findActiveEngine(); String displayName = generatorKey == null || generatorKey.isBlank() ? "Custom Generator" : generatorKey; @@ -244,11 +232,19 @@ public final class NoiseExplorerGUI extends JPanel { return new NoiseExplorerGUI(ExplorerMode.PALETTES, start.engine(), null, null, null, DEFAULT_SEED, start); } + // The map window shows the noise explorer as its Noise view. + static NoiseExplorerGUI createNoiseExplorer(Engine engine) { + return new NoiseExplorerGUI(ExplorerMode.NOISE, engine, null, null, null, DEFAULT_SEED, null); + } + private static void buildNoiseFrame(String title, NoiseExplorerGUI explorer) { JFrame frame = GuiHost.createFrame(title); frame.setLayout(new BorderLayout()); frame.add(explorer.buildNoiseToolbar(), BorderLayout.NORTH); - frame.add(explorer.buildNoiseSidebar(), BorderLayout.WEST); + JComponent sidebar = explorer.noiseSidebar(); + sidebar.setPreferredSize(StudioTheme.scaledSize(SIDEBAR_WIDTH, 0)); + sidebar.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, StudioTheme.SEPARATOR)); + frame.add(sidebar, BorderLayout.WEST); frame.add(explorer, BorderLayout.CENTER); frame.add(explorer.statusBar, BorderLayout.SOUTH); frame.getRootPane().getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('R'), "reset-view"); @@ -299,17 +295,17 @@ public final class NoiseExplorerGUI extends JPanel { private JToolBar buildNoiseToolbar() { JToolBar toolbar = StudioToolbar.create(); + noiseControls().forEach(toolbar::add); + toolbar.add(Box.createHorizontalGlue()); + toolbar.add(StudioToolbar.button("Reset view", "Go back to the starting position and zoom (R)", this::resetViewport)); + return toolbar; + } + + List noiseControls() { selectedLabel.setFont(StudioTheme.boldFont()); selectedLabel.setBorder(StudioTheme.emptyBorder(0, 4, 0, 10)); - toolbar.add(selectedLabel); - toolbar.addSeparator(); - toolbar.add(StudioToolbar.label("Seed")); seedField.setMaximumSize(seedField.getPreferredSize()); seedField.addActionListener(event -> applySeed()); - toolbar.add(seedField); - toolbar.add(StudioToolbar.button("Apply", "Use this seed", this::applySeed)); - toolbar.addSeparator(); - toolbar.add(StudioToolbar.label("Colours")); palettePicker.setSelectedItem(palette); palettePicker.setMaximumSize(palettePicker.getPreferredSize()); palettePicker.addActionListener(event -> { @@ -319,16 +315,20 @@ public final class NoiseExplorerGUI extends JPanel { requestPreview(); } }); - toolbar.add(palettePicker); - toolbar.add(Box.createHorizontalGlue()); - toolbar.add(StudioToolbar.button("Reset view", "Go back to the starting position and zoom (R)", this::resetViewport)); - return toolbar; + return List.of( + selectedLabel, + new JToolBar.Separator(), + StudioToolbar.label("Seed"), + seedField, + StudioToolbar.button("Apply", "Use this seed", this::applySeed), + new JToolBar.Separator(), + StudioToolbar.label("Colours"), + palettePicker + ); } - private JPanel buildNoiseSidebar() { + JComponent noiseSidebar() { JPanel sidebar = new JPanel(new BorderLayout()); - sidebar.setPreferredSize(StudioTheme.scaledSize(SIDEBAR_WIDTH, 0)); - sidebar.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, StudioTheme.SEPARATOR)); StudioToolbar.makeSearchField(searchField, "Search noise..."); searchField.getDocument().addDocumentListener(new DocumentListener() { @Override