mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-27 22:40:13 +00:00
Give procedural objects their own tab
Variant picker, grouped by type.
This commit is contained in:
1 parent
8d12019246
commit
dc7615219b
12 files changed
+420
-116
No files matched your search
@@ -40,6 +40,8 @@ enum MapView {
|
|||||||
Group.BLOCKS, null, KeyEvent.VK_P),
|
Group.BLOCKS, null, KeyEvent.VK_P),
|
||||||
OBJECTS("Objects", "Every object in the pack in 3D, with the blocks it is made of",
|
OBJECTS("Objects", "Every object in the pack in 3D, with the blocks it is made of",
|
||||||
Group.BLOCKS, null, KeyEvent.VK_O),
|
Group.BLOCKS, null, KeyEvent.VK_O),
|
||||||
|
PROCEDURAL("Procedural", "Trees, rock formations and more that Iris generates from settings, every variant in 3D",
|
||||||
|
Group.BLOCKS, null, KeyEvent.VK_T),
|
||||||
NOISE("Noise", "The pack's noise generators and Iris' built-in noise styles",
|
NOISE("Noise", "The pack's noise generators and Iris' built-in noise styles",
|
||||||
Group.GENERATORS, null, KeyEvent.VK_N);
|
Group.GENERATORS, null, KeyEvent.VK_N);
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ enum MapView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tile renderer's mode for this view, or null for the palette, object and noise views, which draw themselves.
|
* The tile renderer's mode for this view, or null for the palette, object, procedural and noise views, which draw themselves.
|
||||||
*/
|
*/
|
||||||
RenderType renderType() {
|
RenderType renderType() {
|
||||||
return renderType;
|
return renderType;
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ import java.awt.event.KeyEvent;
|
|||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,7 +58,6 @@ import java.util.function.IntConsumer;
|
|||||||
public final class MapWindow implements MapCanvas.Listener {
|
public final class MapWindow implements MapCanvas.Listener {
|
||||||
private static final String MAP_CARD = "map";
|
private static final String MAP_CARD = "map";
|
||||||
private static final String PALETTES_CARD = "palettes";
|
private static final String PALETTES_CARD = "palettes";
|
||||||
private static final String OBJECTS_CARD = "objects";
|
|
||||||
private static final String NOISE_CARD = "noise";
|
private static final String NOISE_CARD = "noise";
|
||||||
private static final int SIDEBAR_WIDTH = 270;
|
private static final int SIDEBAR_WIDTH = 270;
|
||||||
private static final int SIDEBAR_MINIMUM_WIDTH = 200;
|
private static final int SIDEBAR_MINIMUM_WIDTH = 200;
|
||||||
@@ -91,7 +92,7 @@ public final class MapWindow implements MapCanvas.Listener {
|
|||||||
private final MapLegendPanel legendPanel;
|
private final MapLegendPanel legendPanel;
|
||||||
|
|
||||||
private NoiseExplorerGUI paletteExplorer;
|
private NoiseExplorerGUI paletteExplorer;
|
||||||
private ObjectExplorer objectExplorer;
|
private final Map<MapView, ObjectExplorer> objectExplorers = new EnumMap<>(MapView.class);
|
||||||
private NoiseExplorerGUI noiseExplorer;
|
private NoiseExplorerGUI noiseExplorer;
|
||||||
private JComponent noiseSidebar;
|
private JComponent noiseSidebar;
|
||||||
private MapView currentView;
|
private MapView currentView;
|
||||||
@@ -204,8 +205,8 @@ public final class MapWindow implements MapCanvas.Listener {
|
|||||||
showMap(previousView, view);
|
showMap(previousView, view);
|
||||||
} else if (view == MapView.PALETTES) {
|
} else if (view == MapView.PALETTES) {
|
||||||
showPalettes(previousView);
|
showPalettes(previousView);
|
||||||
} else if (view == MapView.OBJECTS) {
|
} else if (view == MapView.OBJECTS || view == MapView.PROCEDURAL) {
|
||||||
showObjects();
|
showObjectExplorer(view);
|
||||||
} else {
|
} else {
|
||||||
showNoise();
|
showNoise();
|
||||||
}
|
}
|
||||||
@@ -246,20 +247,18 @@ public final class MapWindow implements MapCanvas.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showObjects() {
|
private void showObjectExplorer(MapView view) {
|
||||||
boolean firstOpen = objectExplorer == null;
|
boolean firstOpen = !objectExplorers.containsKey(view);
|
||||||
if (firstOpen) {
|
ObjectExplorer explorer = objectExplorers.computeIfAbsent(view, this::createObjectExplorer);
|
||||||
createObjectExplorer();
|
|
||||||
}
|
|
||||||
canvas.setActive(false);
|
canvas.setActive(false);
|
||||||
showControls(explorerControls);
|
showControls(explorerControls);
|
||||||
useSidebar(viewListAbove(objectExplorer.browser()));
|
useSidebar(viewListAbove(explorer.browser()));
|
||||||
inspectorSlot.removeAll();
|
inspectorSlot.removeAll();
|
||||||
inspectorSlot.add(objectExplorer.overview(), BorderLayout.CENTER);
|
inspectorSlot.add(explorer.overview(), BorderLayout.CENTER);
|
||||||
showCard(OBJECTS_CARD);
|
showCard(view.name());
|
||||||
EventQueue.invokeLater(() -> viewList.select(currentView));
|
EventQueue.invokeLater(() -> viewList.select(currentView));
|
||||||
if (firstOpen) {
|
if (firstOpen) {
|
||||||
objectExplorer.open();
|
explorer.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,11 +272,13 @@ public final class MapWindow implements MapCanvas.Listener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createObjectExplorer() {
|
private ObjectExplorer createObjectExplorer(MapView view) {
|
||||||
objectExplorer = new ObjectExplorer(start.engine(), start.worldId());
|
ObjectSource source = view == MapView.PROCEDURAL ? new ProceduralObjectSource() : new PackObjectSource();
|
||||||
keepClearOfIsland(objectExplorer.viewport()::setCoveredLeftWidth);
|
ObjectExplorer explorer = new ObjectExplorer(start.engine(), start.worldId(), source);
|
||||||
content.add(objectExplorer.view(), OBJECTS_CARD);
|
keepClearOfIsland(explorer::setCoveredLeftWidth);
|
||||||
statusBars.add(objectExplorer.statusBar(), OBJECTS_CARD);
|
content.add(explorer.view(), view.name());
|
||||||
|
statusBars.add(explorer.statusBar(), view.name());
|
||||||
|
return explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNoise() {
|
private void showNoise() {
|
||||||
@@ -391,7 +392,8 @@ public final class MapWindow implements MapCanvas.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetExplorerView() {
|
private void resetExplorerView() {
|
||||||
if (currentView == MapView.OBJECTS) {
|
ObjectExplorer objectExplorer = objectExplorers.get(currentView);
|
||||||
|
if (objectExplorer != null) {
|
||||||
objectExplorer.resetCamera();
|
objectExplorer.resetCamera();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -487,9 +489,7 @@ public final class MapWindow implements MapCanvas.Listener {
|
|||||||
if (paletteExplorer != null) {
|
if (paletteExplorer != null) {
|
||||||
paletteExplorer.close();
|
paletteExplorer.close();
|
||||||
}
|
}
|
||||||
if (objectExplorer != null) {
|
objectExplorers.values().forEach(ObjectExplorer::close);
|
||||||
objectExplorer.close();
|
|
||||||
}
|
|
||||||
if (noiseExplorer != null) {
|
if (noiseExplorer != null) {
|
||||||
noiseExplorer.close();
|
noiseExplorer.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,15 +51,21 @@ final class ObjectBrowser extends JPanel {
|
|||||||
private final JTextField searchField = new JTextField();
|
private final JTextField searchField = new JTextField();
|
||||||
private final DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
|
private final DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
|
||||||
private final JTree tree = new JTree(treeModel);
|
private final JTree tree = new JTree(treeModel);
|
||||||
private final JLabel countLabel = new JLabel("Loading objects...");
|
private final JLabel countLabel = new JLabel();
|
||||||
|
private final String noun;
|
||||||
private List<String> objectKeys = List.of();
|
private List<String> objectKeys = List.of();
|
||||||
private String chosenKey;
|
private String chosenKey;
|
||||||
private boolean updatingSelection;
|
private boolean updatingSelection;
|
||||||
|
|
||||||
ObjectBrowser(Consumer<String> onObjectChosen) {
|
/**
|
||||||
|
* @param noun what one entry is called in the browser's texts, such as "object"
|
||||||
|
*/
|
||||||
|
ObjectBrowser(String noun, Consumer<String> onObjectChosen) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
|
this.noun = noun;
|
||||||
this.onObjectChosen = onObjectChosen;
|
this.onObjectChosen = onObjectChosen;
|
||||||
StudioToolbar.makeSearchField(searchField, "Search objects...");
|
countLabel.setText("Loading " + noun + "s...");
|
||||||
|
StudioToolbar.makeSearchField(searchField, "Search " + noun + "s...");
|
||||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(DocumentEvent event) {
|
public void insertUpdate(DocumentEvent event) {
|
||||||
@@ -97,7 +103,7 @@ final class ObjectBrowser extends JPanel {
|
|||||||
|
|
||||||
void showObjects(List<String> keys) {
|
void showObjects(List<String> keys) {
|
||||||
objectKeys = keys.stream().sorted(Comparator.comparing(key -> key.toLowerCase(Locale.ROOT))).toList();
|
objectKeys = keys.stream().sorted(Comparator.comparing(key -> key.toLowerCase(Locale.ROOT))).toList();
|
||||||
countLabel.setText(objectKeys.size() == 1 ? "1 object" : objectKeys.size() + " objects");
|
countLabel.setText(objectKeys.size() == 1 ? "1 " + noun : objectKeys.size() + " " + noun + "s");
|
||||||
rebuildTree();
|
rebuildTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +134,7 @@ final class ObjectBrowser extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (root.getChildCount() == 0) {
|
if (root.getChildCount() == 0) {
|
||||||
root.add(new DefaultMutableTreeNode(Entry.note("No matching objects")));
|
root.add(new DefaultMutableTreeNode(Entry.note("No matching " + noun + "s")));
|
||||||
}
|
}
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,8 @@
|
|||||||
package com.volmit.iris.core.gui;
|
package com.volmit.iris.core.gui;
|
||||||
|
|
||||||
import com.volmit.iris.core.gui.BlockTextures.BlockTexture;
|
import com.volmit.iris.core.gui.BlockTextures.BlockTexture;
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.object.IrisObject;
|
import com.volmit.iris.engine.object.IrisObject;
|
||||||
import com.volmit.iris.engine.object.procedural.ProceduralObjectCatalog;
|
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.util.BlockVector;
|
import org.bukkit.util.BlockVector;
|
||||||
@@ -31,8 +29,6 @@ import javax.swing.JComponent;
|
|||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -45,8 +41,8 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The map window's Objects view: every object in the pack in a folder tree, the chosen one in 3D, and what it is made
|
* The map window's Objects and Procedural views: everything its source offers in a folder tree, the chosen one in 3D,
|
||||||
* of. Inspired by the Procedural Objects workbench.
|
* and what it is made of. Inspired by the Procedural Objects workbench.
|
||||||
*/
|
*/
|
||||||
final class ObjectExplorer {
|
final class ObjectExplorer {
|
||||||
private static final NamespacedKey TINT_BIOME = NamespacedKey.minecraft("plains");
|
private static final NamespacedKey TINT_BIOME = NamespacedKey.minecraft("plains");
|
||||||
@@ -55,10 +51,12 @@ final class ObjectExplorer {
|
|||||||
|
|
||||||
private final Engine startEngine;
|
private final Engine startEngine;
|
||||||
private final UUID worldId;
|
private final UUID worldId;
|
||||||
private final ObjectBrowser browser = new ObjectBrowser(this::showObject);
|
private final ObjectSource source;
|
||||||
|
private final ObjectBrowser browser;
|
||||||
private final ObjectViewport viewport = new ObjectViewport();
|
private final ObjectViewport viewport = new ObjectViewport();
|
||||||
private final ObjectLayerCut layerCut = new ObjectLayerCut(shownLayers -> showLayerCut());
|
private final ObjectLayerCut layerCut = new ObjectLayerCut(shownLayers -> showLayerCut());
|
||||||
private final JComponent viewWithLayerCut = layerCut.floatingOver(viewport);
|
private final ObjectVariantPicker variantPicker = new ObjectVariantPicker(this::showVariant);
|
||||||
|
private final JComponent viewWithOverlays = variantPicker.floatingOver(layerCut.floatingOver(viewport));
|
||||||
private final ObjectOverviewPanel overview = new ObjectOverviewPanel(this::showSeeThrough);
|
private final ObjectOverviewPanel overview = new ObjectOverviewPanel(this::showSeeThrough);
|
||||||
private final StudioStatusBar statusBar = new StudioStatusBar();
|
private final StudioStatusBar statusBar = new StudioStatusBar();
|
||||||
private final ExecutorService loader = Executors.newSingleThreadExecutor(runnable -> {
|
private final ExecutorService loader = Executors.newSingleThreadExecutor(runnable -> {
|
||||||
@@ -74,33 +72,39 @@ final class ObjectExplorer {
|
|||||||
private volatile BlockTextures textures;
|
private volatile BlockTextures textures;
|
||||||
private volatile BiomeTintColors tintColors;
|
private volatile BiomeTintColors tintColors;
|
||||||
private String shownKey;
|
private String shownKey;
|
||||||
|
private List<IrisObject> shownVariants = List.of();
|
||||||
private LoadedObject shown;
|
private LoadedObject shown;
|
||||||
private ObjectMesh wholeMesh;
|
private ObjectMesh wholeMesh;
|
||||||
private Set<String> seeThroughTypes = Set.of();
|
private Set<String> seeThroughTypes = Set.of();
|
||||||
|
|
||||||
ObjectExplorer(Engine engine, UUID worldId) {
|
ObjectExplorer(Engine engine, UUID worldId, ObjectSource source) {
|
||||||
this.startEngine = engine;
|
this.startEngine = engine;
|
||||||
this.worldId = worldId;
|
this.worldId = worldId;
|
||||||
|
this.source = source;
|
||||||
|
this.browser = new ObjectBrowser(source.noun(), this::showObject);
|
||||||
GuiHost.interruptOnPluginShutdown(loader);
|
GuiHost.interruptOnPluginShutdown(loader);
|
||||||
GuiHost.registerHotloadHook(hotloadHook);
|
GuiHost.registerHotloadHook(hotloadHook);
|
||||||
statusBar.showPosition(CAMERA_HINT);
|
statusBar.showPosition(CAMERA_HINT);
|
||||||
viewport.bindKey(KeyEvent.VK_PAGE_UP, "show-layer-above", () -> layerCut.stepBy(1));
|
viewport.bindKey(KeyEvent.VK_PAGE_UP, "show-layer-above", () -> layerCut.stepBy(1));
|
||||||
viewport.bindKey(KeyEvent.VK_PAGE_DOWN, "hide-top-layer", () -> layerCut.stepBy(-1));
|
viewport.bindKey(KeyEvent.VK_PAGE_DOWN, "hide-top-layer", () -> layerCut.stepBy(-1));
|
||||||
|
viewport.bindKey(KeyEvent.VK_OPEN_BRACKET, "previous-variant", () -> variantPicker.stepBy(-1));
|
||||||
|
viewport.bindKey(KeyEvent.VK_CLOSE_BRACKET, "next-variant", () -> variantPicker.stepBy(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
JComponent browser() {
|
JComponent browser() {
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectViewport viewport() {
|
|
||||||
return viewport;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 3D view with the slider that cuts away its top layers.
|
* The 3D view with the slider that cuts away its top layers and the bar that steps through variants.
|
||||||
*/
|
*/
|
||||||
JComponent view() {
|
JComponent view() {
|
||||||
return viewWithLayerCut;
|
return viewWithOverlays;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCoveredLeftWidth(int width) {
|
||||||
|
viewport.setCoveredLeftWidth(width);
|
||||||
|
variantPicker.setCoveredLeftWidth(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
JComponent overview() {
|
JComponent overview() {
|
||||||
@@ -143,10 +147,7 @@ final class ObjectExplorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<String> objectKeys() {
|
private List<String> objectKeys() {
|
||||||
IrisData data = engine().getData();
|
return source.keys(engine().getData());
|
||||||
List<String> keys = new ArrayList<>(Arrays.asList(data.getObjectLoader().getPossibleKeys()));
|
|
||||||
keys.addAll(data.getProceduralObjects().variantKeys());
|
|
||||||
return keys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showCatalog(List<String> keys) {
|
private void showCatalog(List<String> keys) {
|
||||||
@@ -154,9 +155,10 @@ final class ObjectExplorer {
|
|||||||
browser.showObjects(keys);
|
browser.showObjects(keys);
|
||||||
String key = shownKey != null && keys.contains(shownKey) ? shownKey : browser.firstObject();
|
String key = shownKey != null && keys.contains(shownKey) ? shownKey : browser.firstObject();
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
viewport.showMessage("This pack has no objects");
|
viewport.showMessage("This pack has no " + source.noun() + "s");
|
||||||
overview.clear();
|
overview.clear();
|
||||||
layerCut.showHeight(1);
|
layerCut.showHeight(1);
|
||||||
|
variantPicker.showVariants(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
browser.select(key);
|
browser.select(key);
|
||||||
@@ -165,31 +167,54 @@ final class ObjectExplorer {
|
|||||||
|
|
||||||
private void showObject(String key) {
|
private void showObject(String key) {
|
||||||
shownKey = key;
|
shownKey = key;
|
||||||
shown = null;
|
shownVariants = List.of();
|
||||||
wholeMesh = null;
|
variantPicker.showVariants(0);
|
||||||
meshRevision.incrementAndGet();
|
long revision = startLoading(key);
|
||||||
long revision = objectRevision.incrementAndGet();
|
|
||||||
viewport.showMessage("Loading " + key + "...");
|
|
||||||
statusBar.showActivity("Loading " + key + "...");
|
|
||||||
loader.execute(() -> {
|
loader.execute(() -> {
|
||||||
if (revision != objectRevision.get()) {
|
if (revision != objectRevision.get()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
LoadedObject loaded = load(key);
|
List<IrisObject> variants = source.variants(engine().getData(), key);
|
||||||
EventQueue.invokeLater(() -> showLoaded(revision, loaded));
|
if (variants.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Nothing could be loaded for " + key);
|
||||||
|
}
|
||||||
|
LoadedObject loaded = load(variants.get(0));
|
||||||
|
EventQueue.invokeLater(() -> {
|
||||||
|
if (revision == objectRevision.get()) {
|
||||||
|
shownVariants = variants;
|
||||||
|
variantPicker.showVariants(variants.size());
|
||||||
|
}
|
||||||
|
showLoaded(revision, loaded);
|
||||||
|
});
|
||||||
} catch (RuntimeException error) {
|
} catch (RuntimeException error) {
|
||||||
EventQueue.invokeLater(() -> showFailure(revision, key, error));
|
EventQueue.invokeLater(() -> showFailure(revision, key, error));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoadedObject load(String key) {
|
private void showVariant(int index) {
|
||||||
IrisObject object = objectFor(key);
|
IrisObject variant = shownVariants.get(index);
|
||||||
if (object == null) {
|
long revision = startLoading(variant.getLoadKey());
|
||||||
throw new IllegalStateException(ProceduralObjectCatalog.isVariantKey(key)
|
loader.execute(() -> {
|
||||||
? "This variant has no blocks" : "The object file could not be read");
|
if (revision != objectRevision.get()) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
LoadedObject loaded = load(variant);
|
||||||
|
EventQueue.invokeLater(() -> showLoaded(revision, loaded));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private long startLoading(String key) {
|
||||||
|
shown = null;
|
||||||
|
wholeMesh = null;
|
||||||
|
meshRevision.incrementAndGet();
|
||||||
|
viewport.showMessage("Loading " + key + "...");
|
||||||
|
statusBar.showActivity("Loading " + key + "...");
|
||||||
|
return objectRevision.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadedObject load(IrisObject object) {
|
||||||
Map<BlockVector, BlockData> blocks = new HashMap<>();
|
Map<BlockVector, BlockData> blocks = new HashMap<>();
|
||||||
object.getBlocks().forEach((position, block) -> {
|
object.getBlocks().forEach((position, block) -> {
|
||||||
if (!block.getMaterial().isAir()) {
|
if (!block.getMaterial().isAir()) {
|
||||||
@@ -197,17 +222,10 @@ final class ObjectExplorer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
ObjectMesh mesh = ObjectMesh.build(blocks, this::modelOf);
|
ObjectMesh mesh = ObjectMesh.build(blocks, this::modelOf);
|
||||||
ObjectSummary summary = ObjectSummary.of(key, mesh.bounds(), blocks.values(), this::iconOf);
|
ObjectSummary summary = ObjectSummary.of(object.getLoadKey(), mesh.bounds(), blocks.values(), this::iconOf);
|
||||||
return new LoadedObject(blocks, mesh, summary);
|
return new LoadedObject(blocks, mesh, summary);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IrisObject objectFor(String key) {
|
|
||||||
IrisData data = engine().getData();
|
|
||||||
if (ProceduralObjectCatalog.isVariantKey(key)) {
|
|
||||||
return data.getProceduralObjects().variant(key);
|
|
||||||
}
|
|
||||||
return data.getObjectLoader().load(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showLoaded(long revision, LoadedObject loaded) {
|
private void showLoaded(long revision, LoadedObject loaded) {
|
||||||
if (revision != objectRevision.get()) {
|
if (revision != objectRevision.get()) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.volmit.iris.core.gui;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.engine.object.IrisObject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where an object explorer gets what it shows: the pack's object files, or the objects Iris generates from settings.
|
||||||
|
* Both are called on the explorer's loader thread only.
|
||||||
|
*/
|
||||||
|
interface ObjectSource {
|
||||||
|
/** What one entry is called, such as "object", for the browser's texts. */
|
||||||
|
String noun();
|
||||||
|
|
||||||
|
/** The key of every entry, where a slash separates folders. */
|
||||||
|
List<String> keys(IrisData data);
|
||||||
|
|
||||||
|
/** The objects of the entry, one for an object file and one per variant for a procedural object; empty when none load. */
|
||||||
|
List<IrisObject> variants(IrisData data, String key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.volmit.iris.core.gui;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small bar floating at the bottom of the 3D view that steps through the variants of a procedural object. It hides
|
||||||
|
* itself for anything with a single variant.
|
||||||
|
*/
|
||||||
|
final class ObjectVariantPicker extends JPanel {
|
||||||
|
private static final long serialVersionUID = -2716301839488217455L;
|
||||||
|
private static final int BOTTOM_MARGIN = 14;
|
||||||
|
private static final int LABEL_WIDTH = 110;
|
||||||
|
private static final String SHORTCUT_HINT = " · [ and ]";
|
||||||
|
|
||||||
|
private final JLabel shownVariant = new JLabel(" ", SwingConstants.CENTER);
|
||||||
|
private final IntConsumer onVariantChosen;
|
||||||
|
private int variantCount;
|
||||||
|
private int shownIndex;
|
||||||
|
private int coveredLeftWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param onVariantChosen told the index of the variant to show whenever the user steps to another one
|
||||||
|
*/
|
||||||
|
ObjectVariantPicker(IntConsumer onVariantChosen) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
this.onVariantChosen = onVariantChosen;
|
||||||
|
setOpaque(false);
|
||||||
|
setBorder(StudioTheme.emptyBorder(3, 4, 3, 4));
|
||||||
|
shownVariant.setFont(StudioTheme.smallFont());
|
||||||
|
shownVariant.setForeground(StudioTheme.TEXT);
|
||||||
|
shownVariant.setPreferredSize(new Dimension(StudioTheme.scale(LABEL_WIDTH), shownVariant.getPreferredSize().height));
|
||||||
|
add(StudioToolbar.button("‹", "Previous variant" + SHORTCUT_HINT, () -> stepBy(-1)), BorderLayout.WEST);
|
||||||
|
add(shownVariant, BorderLayout.CENTER);
|
||||||
|
add(StudioToolbar.button("›", "Next variant" + SHORTCUT_HINT, () -> stepBy(1)), BorderLayout.EAST);
|
||||||
|
showVariants(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The view with this bar floating over its bottom edge, centred on the part the left island leaves uncovered.
|
||||||
|
*/
|
||||||
|
JComponent floatingOver(JComponent view) {
|
||||||
|
ObjectVariantPicker picker = this;
|
||||||
|
JPanel stack = new JPanel(null) {
|
||||||
|
private static final long serialVersionUID = 1609842937565014832L;
|
||||||
|
|
||||||
|
// The bar overlaps the view, so the view may not repaint without the bar on top of it.
|
||||||
|
@Override
|
||||||
|
public boolean isOptimizedDrawingEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doLayout() {
|
||||||
|
view.setBounds(0, 0, getWidth(), getHeight());
|
||||||
|
Dimension size = picker.getPreferredSize();
|
||||||
|
int centerX = coveredLeftWidth + (getWidth() - coveredLeftWidth) / 2;
|
||||||
|
picker.setBounds(centerX - size.width / 2, getHeight() - size.height - StudioTheme.scale(BOTTOM_MARGIN), size.width, size.height);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stack.add(picker);
|
||||||
|
stack.add(view);
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCoveredLeftWidth(int width) {
|
||||||
|
coveredLeftWidth = width;
|
||||||
|
if (getParent() != null) {
|
||||||
|
getParent().revalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts again at the first of {@code count} variants without telling anyone, since the first is what loads anyway.
|
||||||
|
*/
|
||||||
|
void showVariants(int count) {
|
||||||
|
variantCount = count;
|
||||||
|
shownIndex = 0;
|
||||||
|
showLabel();
|
||||||
|
setVisible(count > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stepBy(int step) {
|
||||||
|
if (variantCount < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shownIndex = Math.floorMod(shownIndex + step, variantCount);
|
||||||
|
showLabel();
|
||||||
|
onVariantChosen.accept(shownIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLabel() {
|
||||||
|
shownVariant.setText("Variant " + (shownIndex + 1) + " of " + variantCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics graphics) {
|
||||||
|
Graphics2D canvas = (Graphics2D) graphics.create();
|
||||||
|
try {
|
||||||
|
canvas.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
double arc = StudioTheme.scale(StudioTheme.OVERLAY_ARC);
|
||||||
|
RoundRectangle2D shape = new RoundRectangle2D.Double(0.5, 0.5, getWidth() - 1D, getHeight() - 1D, arc, arc);
|
||||||
|
canvas.setColor(StudioTheme.OVERLAY_BACKGROUND);
|
||||||
|
canvas.fill(shape);
|
||||||
|
canvas.setColor(StudioTheme.OVERLAY_BORDER);
|
||||||
|
canvas.draw(shape);
|
||||||
|
} finally {
|
||||||
|
canvas.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.volmit.iris.core.gui;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.engine.object.IrisObject;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pack's object files, by their load keys.
|
||||||
|
*/
|
||||||
|
final class PackObjectSource implements ObjectSource {
|
||||||
|
@Override
|
||||||
|
public String noun() {
|
||||||
|
return "object";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> keys(IrisData data) {
|
||||||
|
return Arrays.asList(data.getObjectLoader().getPossibleKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IrisObject> variants(IrisData data, String key) {
|
||||||
|
IrisObject object = data.getObjectLoader().load(key);
|
||||||
|
return object == null ? List.of() : List.of(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.volmit.iris.core.gui;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.engine.object.IrisObject;
|
||||||
|
import com.volmit.iris.engine.object.procedural.ResolvedProceduralPlacement;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The objects the pack's procedural settings generate, one entry per definition in a folder per type.
|
||||||
|
*/
|
||||||
|
final class ProceduralObjectSource implements ObjectSource {
|
||||||
|
private static final Map<String, String> FOLDER_BY_KIND = Map.of(
|
||||||
|
"tree", "Trees",
|
||||||
|
"formation", "Formations",
|
||||||
|
"crystal", "Crystals",
|
||||||
|
"coral", "Coral",
|
||||||
|
"fungus", "Fungi",
|
||||||
|
"ruin", "Ruins");
|
||||||
|
|
||||||
|
private Map<String, ResolvedProceduralPlacement> definitionsByKey = Map.of();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String noun() {
|
||||||
|
return "procedural object";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> keys(IrisData data) {
|
||||||
|
Map<String, ResolvedProceduralPlacement> byKey = new LinkedHashMap<>();
|
||||||
|
for (ResolvedProceduralPlacement definition : data.getProceduralObjects().definitions()) {
|
||||||
|
byKey.put(FOLDER_BY_KIND.getOrDefault(definition.kind(), definition.kind()) + "/" + definition.name(), definition);
|
||||||
|
}
|
||||||
|
definitionsByKey = byKey;
|
||||||
|
return List.copyOf(byKey.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IrisObject> variants(IrisData data, String key) {
|
||||||
|
ResolvedProceduralPlacement definition = definitionsByKey.get(key);
|
||||||
|
return definition == null ? List.of() : definition.variants();
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-37
@@ -20,7 +20,6 @@ package com.volmit.iris.engine.object.procedural;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.engine.object.IrisObject;
|
|
||||||
import com.volmit.iris.engine.object.annotations.Snippet;
|
import com.volmit.iris.engine.object.annotations.Snippet;
|
||||||
import com.volmit.iris.engine.object.procedural.coral.Coral;
|
import com.volmit.iris.engine.object.procedural.coral.Coral;
|
||||||
import com.volmit.iris.engine.object.procedural.coral.ResolvedCoral;
|
import com.volmit.iris.engine.object.procedural.coral.ResolvedCoral;
|
||||||
@@ -41,15 +40,14 @@ import java.io.Reader;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The procedural objects of one pack: those its regions and biomes declare, and those saved on their own as snippets,
|
* The procedural objects of one pack: those its regions and biomes declare, and those saved on their own as snippets,
|
||||||
* such as {@code snippet/procedural-tree/oak.json}. A definition several places share is baked once, and every variant
|
* such as {@code snippet/procedural-tree/oak.json}. A definition several places share is baked once.
|
||||||
* can be found again by its key.
|
|
||||||
*/
|
*/
|
||||||
public final class ProceduralObjectCatalog {
|
public final class ProceduralObjectCatalog {
|
||||||
private static final Gson DEFINITION_JSON = new Gson();
|
private static final Gson DEFINITION_JSON = new Gson();
|
||||||
@@ -57,7 +55,6 @@ public final class ProceduralObjectCatalog {
|
|||||||
|
|
||||||
private final IrisData data;
|
private final IrisData data;
|
||||||
private final KMap<String, ResolvedProceduralPlacement> byDefinition = new KMap<>();
|
private final KMap<String, ResolvedProceduralPlacement> byDefinition = new KMap<>();
|
||||||
private final KMap<String, ResolvedProceduralPlacement> byVariantKey = new KMap<>();
|
|
||||||
|
|
||||||
public ProceduralObjectCatalog(IrisData data) {
|
public ProceduralObjectCatalog(IrisData data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@@ -74,7 +71,7 @@ public final class ProceduralObjectCatalog {
|
|||||||
|
|
||||||
private ResolvedProceduralPlacement placementOf(Object definition) {
|
private ResolvedProceduralPlacement placementOf(Object definition) {
|
||||||
String identity = definition.getClass().getName() + DEFINITION_JSON.toJson(definition);
|
String identity = definition.getClass().getName() + DEFINITION_JSON.toJson(definition);
|
||||||
return byDefinition.computeIfAbsent(identity, ignored -> register(resolvedFor(definition)));
|
return byDefinition.computeIfAbsent(identity, ignored -> resolvedFor(definition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvedProceduralPlacement resolvedFor(Object definition) {
|
private ResolvedProceduralPlacement resolvedFor(Object definition) {
|
||||||
@@ -89,22 +86,29 @@ public final class ProceduralObjectCatalog {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvedProceduralPlacement register(ResolvedProceduralPlacement placement) {
|
/**
|
||||||
for (String key : placement.variantKeys()) {
|
* Every procedural object the pack defines, in snippets or in regions and biomes, whether or not its dimensions
|
||||||
byVariantKey.putIfAbsent(key, placement);
|
* reach them. Where two definitions share a type and name, the first one found stands for both.
|
||||||
|
*/
|
||||||
|
public List<ResolvedProceduralPlacement> definitions() {
|
||||||
|
Map<String, ResolvedProceduralPlacement> byTypeAndName = new LinkedHashMap<>();
|
||||||
|
for (Class<?> type : DEFINITION_TYPES) {
|
||||||
|
snippetDefinitions(type).forEach(definition -> addDefinition(byTypeAndName, placementOf(definition)));
|
||||||
}
|
}
|
||||||
return placement;
|
data.getRegionLoader().streamAll().forEach(region -> addDefinitions(byTypeAndName, region.getProceduralObjects()));
|
||||||
|
data.getBiomeLoader().streamAll().forEach(biome -> addDefinitions(byTypeAndName, biome.getProceduralObjects()));
|
||||||
|
return List.copyOf(byTypeAndName.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The key of every variant the pack defines, in snippets or in regions and biomes, whether or not its dimensions reach them. */
|
private void addDefinitions(Map<String, ResolvedProceduralPlacement> byTypeAndName, ProceduralObjectSettings settings) {
|
||||||
public List<String> variantKeys() {
|
if (settings == null) {
|
||||||
Set<String> keys = new LinkedHashSet<>();
|
return;
|
||||||
for (Class<?> type : DEFINITION_TYPES) {
|
|
||||||
snippetDefinitions(type).forEach(definition -> keys.addAll(placementOf(definition).variantKeys()));
|
|
||||||
}
|
}
|
||||||
data.getRegionLoader().streamAll().forEach(region -> addVariantKeys(keys, region.getProceduralObjects()));
|
settings.resolved(data).forEach(placement -> addDefinition(byTypeAndName, placement));
|
||||||
data.getBiomeLoader().streamAll().forEach(biome -> addVariantKeys(keys, biome.getProceduralObjects()));
|
}
|
||||||
return List.copyOf(keys);
|
|
||||||
|
private static void addDefinition(Map<String, ResolvedProceduralPlacement> byTypeAndName, ResolvedProceduralPlacement placement) {
|
||||||
|
byTypeAndName.putIfAbsent(placement.kind() + "/" + placement.name(), placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Object> snippetDefinitions(Class<?> type) {
|
private List<Object> snippetDefinitions(Class<?> type) {
|
||||||
@@ -136,22 +140,4 @@ public final class ProceduralObjectCatalog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addVariantKeys(Set<String> keys, ProceduralObjectSettings settings) {
|
|
||||||
if (settings == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (ResolvedProceduralPlacement placement : settings.resolved(data)) {
|
|
||||||
keys.addAll(placement.variantKeys());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The baked variant loaded under {@code key}; null when no definition resolved so far has it, or it baked no blocks. */
|
|
||||||
public IrisObject variant(String key) {
|
|
||||||
ResolvedProceduralPlacement placement = byVariantKey.get(key);
|
|
||||||
return placement == null ? null : placement.variant(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isVariantKey(String key) {
|
|
||||||
return key.startsWith("procedural/");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+5
-9
@@ -68,6 +68,11 @@ public abstract class ResolvedProceduralPlacement {
|
|||||||
return shape.name();
|
return shape.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The type of object, as the variant keys name it: tree, ruin, formation, coral, fungus or crystal. */
|
||||||
|
public String kind() {
|
||||||
|
return shape.kind();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the world generator can place it. A place mode this version does not know, such as v4's CEILING_HANG,
|
* Whether the world generator can place it. A place mode this version does not know, such as v4's CEILING_HANG,
|
||||||
* reads as null, and placing such an object on the surface would put it in the wrong place.
|
* reads as null, and placing such an object on the surface would put it in the wrong place.
|
||||||
@@ -130,15 +135,6 @@ public abstract class ResolvedProceduralPlacement {
|
|||||||
return baked.get(rng.i(baked.size()));
|
return baked.get(rng.i(baked.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The baked variant loaded under {@code key}; null when it is not one of these, or baked no blocks. */
|
|
||||||
public IrisObject variant(String key) {
|
|
||||||
for (IrisObject variant : variants()) {
|
|
||||||
if (variant.getLoadKey().equals(key)) {
|
|
||||||
return variant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Bakes every variant; the default draws variant {@code i} from {@code seed + i * stride}. */
|
/** Bakes every variant; the default draws variant {@code i} from {@code seed + i * stride}. */
|
||||||
protected List<IrisObject> bake(ObjectBuilder blocks) {
|
protected List<IrisObject> bake(ObjectBuilder blocks) {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.79289 13.7929L2.20711 13.2071C1.81658 12.8166 1.81658 12.1834 2.20711 11.7929L8.79289 5.20711C9.18342 4.81658 9.81658 4.81658 10.2071 5.20711L10.7929 5.79289C11.1834 6.18342 11.1834 6.81658 10.7929 7.20711L4.20711 13.7929C3.81658 14.1834 3.18342 14.1834 2.79289 13.7929Z" stroke="#6C707E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9.5 8.5L7.5 6.5" stroke="#6C707E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10.5 2.5V1.5" stroke="#6C707E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13.5 5.5H14.5M11.5 9.5L12.5 10.5M6.5 4.5L5.5 3.5" stroke="#6C707E" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 889 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.79289 13.7929L2.20711 13.2071C1.81658 12.8166 1.81658 12.1834 2.20711 11.7929L8.79289 5.20711C9.18342 4.81658 9.81658 4.81658 10.2071 5.20711L10.7929 5.79289C11.1834 6.18342 11.1834 6.81658 10.7929 7.20711L4.20711 13.7929C3.81658 14.1834 3.18342 14.1834 2.79289 13.7929Z" stroke="#CED0D6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9.5 8.5L7.5 6.5" stroke="#CED0D6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M10.5 2.5V1.5" stroke="#CED0D6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M13.5 5.5H14.5M11.5 9.5L12.5 10.5M6.5 4.5L5.5 3.5" stroke="#CED0D6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 889 B |
Reference in new issue
Block a user