From f73c78349befed5142ef1344693f4e6beeffbbb8 Mon Sep 17 00:00:00 2001 From: piexel Date: Mon, 19 Oct 2026 19:53:07 +0200 Subject: [PATCH] Show the noise explorer as a view in the studio map /iris std noise now opens the map window on a new Noise view under Generators (key N), next to Palettes. The noise sources sit below the view list in the sidebar island, and the seed and colour controls join the map toolbar. Reset view (R) resets whichever explorer is showing. /iris std explore still opens its own noise window for one generator. Model used: Claude Opus 5.5 --- .../iris/core/commands/CommandStudio.java | 21 +++- .../com/volmit/iris/core/gui/MapView.java | 9 +- .../com/volmit/iris/core/gui/MapWindow.java | 100 +++++++++++++----- .../iris/core/gui/NoiseExplorerGUI.java | 54 +++++----- 4 files changed, 126 insertions(+), 58 deletions(-) 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