Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve octree, bvh, renderer and output mode dropdowns. #1624

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions chunky/src/java/se/llbit/chunky/renderer/RenderManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public interface RenderManager {
/**
* Get all available {@code Renderer}s.
*/
Collection<? extends Registerable> getRenderers();
Collection<Renderer> getRenderers();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make it impossible for plugins to implement RenderManager and have their own type of swappable renderer that is incompatible with Chunky's renderers. (Which kinda defeats the point of changing the RenderManager implementation).

Copy link
Member Author

@leMaik leMaik Sep 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh… Okay, so a renderer doesn't have to be a Renderer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, Renderer is just the interface to work with Chunky's DefaultRenderManager.


/**
* Get all available preview {@code Renderer}s.
*/
Collection<? extends Registerable> getPreviewRenderers();
Collection<Renderer> getPreviewRenderers();

/**
* Instructs the renderer to change its CPU load.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import se.llbit.chunky.plugin.PluginApi;
import se.llbit.chunky.resources.BitmapImage;
import se.llbit.util.Registerable;
import se.llbit.util.TaskTracker;

/**
Expand All @@ -14,7 +15,7 @@
* PixelPostProcessingFilter} instead.
*/
@PluginApi
public interface PostProcessingFilter {
public interface PostProcessingFilter extends Registerable {
/**
* Post process the entire frame
* @param width The width of the image
Expand All @@ -30,12 +31,14 @@ public interface PostProcessingFilter {
* Get name of the post processing filter
* @return The name of the post processing filter
*/
@Override
String getName();

/**
* Get description of the post processing filter
* @return The description of the post processing filter
*/
@Override
default String getDescription() {
return null;
}
Expand All @@ -44,5 +47,6 @@ default String getDescription() {
* Get id of the post processing filter
* @return The id of the post processing filter
*/
@Override
String getId();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,6 @@ public static Optional<PostProcessingFilter> getPostProcessingFilterFromId(Strin
return Optional.ofNullable(filters.get(id));
}

// TODO Create a ChoiceBox that can use different string as ID and as visual representation
// so this isn't needed
@Deprecated
public static Optional<PostProcessingFilter> getPostProcessingFilterFromName(String name) {
return Optional.ofNullable(filtersByName.get(name));
}

public static Collection<PostProcessingFilter> getFilters() {
return filtersByName.values();
}
Expand Down
26 changes: 26 additions & 0 deletions chunky/src/java/se/llbit/chunky/ui/RegisterableCellAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package se.llbit.chunky.ui;

import javafx.scene.control.Tooltip;
import se.llbit.fxutil.CustomizedListCellFactory;
import se.llbit.util.Registerable;

public class RegisterableCellAdapter implements CustomizedListCellFactory.Adapter<Registerable> {
public static final RegisterableCellAdapter INSTANCE = new RegisterableCellAdapter();

private RegisterableCellAdapter() {
}

@Override
public String getLabel(Registerable item) {
return item.getName();
}

@Override
public Tooltip getTooltip(Registerable item) {
String description = item.getDescription();
if (description != null && !description.isEmpty()) {
return new Tooltip(item.getDescription());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return new Tooltip(item.getDescription());
return new Tooltip(description);

}
return null;
}
}
95 changes: 45 additions & 50 deletions chunky/src/java/se/llbit/chunky/ui/render/tabs/AdvancedTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@
import se.llbit.chunky.PersistentSettings;
import se.llbit.chunky.launcher.LauncherSettings;
import se.llbit.chunky.main.Chunky;
import se.llbit.chunky.renderer.EmitterSamplingStrategy;
import se.llbit.chunky.renderer.RenderController;
import se.llbit.chunky.renderer.RenderManager;
import se.llbit.chunky.renderer.*;
import se.llbit.chunky.renderer.export.PictureExportFormat;
import se.llbit.chunky.renderer.export.PictureExportFormats;
import se.llbit.chunky.renderer.scene.AsynchronousSceneManager;
Expand All @@ -38,9 +36,11 @@
import se.llbit.chunky.ui.Adjuster;
import se.llbit.chunky.ui.DoubleAdjuster;
import se.llbit.chunky.ui.IntegerAdjuster;
import se.llbit.chunky.ui.RegisterableCellAdapter;
import se.llbit.chunky.ui.controller.RenderControlsFxController;
import se.llbit.chunky.ui.dialogs.ShutdownAlert;
import se.llbit.chunky.ui.render.RenderControlsTab;
import se.llbit.fxutil.CustomizedListCellFactory;
import se.llbit.fxutil.Dialogs;
import se.llbit.log.Log;
import se.llbit.math.Octree;
Expand Down Expand Up @@ -72,16 +72,16 @@ public class AdvancedTab extends ScrollPane implements RenderControlsTab, Initia
@FXML private DoubleAdjuster transmissivityCap;
@FXML private IntegerAdjuster cacheResolution;
@FXML private DoubleAdjuster animationTime;
@FXML private ChoiceBox<PictureExportFormat> outputMode;
@FXML private ChoiceBox<String> octreeImplementation;
@FXML private ComboBox<PictureExportFormat> outputMode;
@FXML private ComboBox<Octree.ImplementationFactory> octreeImplementation;
@FXML private Button octreeSwitchImplementation;
@FXML private ChoiceBox<String> bvhMethod;
@FXML private ChoiceBox<String> biomeStructureImplementation;
@FXML private ComboBox<BVH.Factory.BVHBuilder> bvhMethod;
@FXML private ComboBox<BiomeStructure.Factory> biomeStructureImplementation;
@FXML private IntegerAdjuster gridSize;
@FXML private CheckBox preventNormalEmitterWithSampling;
@FXML private CheckBox hideUnknownBlocks;
@FXML private ChoiceBox<String> rendererSelect;
@FXML private ChoiceBox<String> previewSelect;
@FXML private ComboBox<Renderer> rendererSelect;
@FXML private ComboBox<Renderer> previewSelect;
@FXML private CheckBox showLauncher;

public AdvancedTab() throws IOException {
Expand All @@ -95,6 +95,7 @@ public AdvancedTab() throws IOException {
public void initialize(URL location, ResourceBundle resources) {
outputMode.getItems().addAll(PictureExportFormats.getFormats());
outputMode.getSelectionModel().select(PictureExportFormats.PNG);
CustomizedListCellFactory.install(outputMode, PictureExportFormat::getDescription);
cpuLoad.setName("CPU utilization");
cpuLoad.setTooltip("CPU utilization percentage per render thread.");
cpuLoad.setRange(1, 100);
Expand Down Expand Up @@ -196,77 +197,76 @@ public PictureExportFormat fromString(String string) {
renderControls.showPopup("This change takes effect after restarting Chunky.", renderThreads);
});

ArrayList<String> octreeNames = new ArrayList<>();
ArrayList<Octree.ImplementationFactory> octreeImplementations = new ArrayList<>();
StringBuilder tooltipTextBuilder = new StringBuilder();
for(Map.Entry<String, Octree.ImplementationFactory> entry : Octree.getEntries()) {
octreeNames.add(entry.getKey());
octreeImplementations.add(entry.getValue());
tooltipTextBuilder.append(entry.getKey());
tooltipTextBuilder.append(": ");
tooltipTextBuilder.append(entry.getValue().getDescription());
tooltipTextBuilder.append('\n');
}
tooltipTextBuilder.append("Requires reloading chunks to take effect.");
octreeImplementation.getItems().addAll(octreeNames);
octreeImplementation.getItems().addAll(octreeImplementations);
octreeImplementation.getSelectionModel().selectedItemProperty()
.addListener((observable, oldvalue, newvalue) -> {
PersistentSettings.setOctreeImplementation(newvalue);
if (!scene.getOctreeImplementation().equals(newvalue)) {
scene.setOctreeImplementation(newvalue);
PersistentSettings.setOctreeImplementation(newvalue.getId());
if (!scene.getOctreeImplementation().equals(getId())) {
scene.setOctreeImplementation(newvalue.getId());
scene.softRefresh();
}
});
CustomizedListCellFactory.install(octreeImplementation, RegisterableCellAdapter.INSTANCE);
octreeImplementation.setTooltip(new Tooltip(tooltipTextBuilder.toString()));

octreeSwitchImplementation.setOnAction(event -> Chunky.getCommonThreads().submit(() -> {
TaskTracker tracker = controller.getSceneManager().getTaskTracker();
try {
try (TaskTracker.Task task = tracker.task("(1/2) Converting world octree", 1000)) {
scene.getWorldOctree().switchImplementation(octreeImplementation.getValue(), task);
scene.getWorldOctree().switchImplementation(octreeImplementation.getValue().getId(), task);
}
try (TaskTracker.Task task = tracker.task("(2/2) Converting water octree")) {
scene.getWaterOctree().switchImplementation(octreeImplementation.getValue(), task);
scene.getWaterOctree().switchImplementation(octreeImplementation.getValue().getId(), task);
}
} catch (IOException e) {
Log.error("Switching octrees failed. Reload the scene.\n", e);
}
}));

ArrayList<String> bvhNames = new ArrayList<>();
StringBuilder bvhMethodBuilder = new StringBuilder();
for (BVH.Factory.BVHBuilder builder : BVH.Factory.getImplementations()) {
bvhNames.add(builder.getName());
bvhMethodBuilder.append(builder.getName());
bvhMethodBuilder.append(": ");
bvhMethodBuilder.append(builder.getDescription());
bvhMethodBuilder.append('\n');
}
bvhMethodBuilder.append("Requires reloading chunks to take effect.");
bvhMethod.getItems().addAll(bvhNames);
bvhMethod.getSelectionModel().select(PersistentSettings.getBvhMethod());
bvhMethod.getItems().addAll(BVH.Factory.getImplementations());
bvhMethod.getSelectionModel().select(BVH.Factory.getImplementation(PersistentSettings.getBvhMethod()));
bvhMethod.getSelectionModel().selectedItemProperty()
.addListener(((observable, oldValue, newValue) -> {
PersistentSettings.setBvhMethod(newValue);
scene.setBvhImplementation(newValue);
PersistentSettings.setBvhMethod(newValue.getId());
scene.setBvhImplementation(newValue.getId());
scene.softRefresh();
}));
CustomizedListCellFactory.install(bvhMethod, RegisterableCellAdapter.INSTANCE);
bvhMethod.setTooltip(new Tooltip(bvhMethodBuilder.toString()));

ArrayList<String> biomeStructureIds = new ArrayList<>();
StringBuilder biomeStructureTooltipBuilder = new StringBuilder();
for (Registerable entry : BiomeStructure.REGISTRY.values()) {
biomeStructureIds.add(entry.getId());
biomeStructureTooltipBuilder.append(entry.getName());
biomeStructureTooltipBuilder.append(": ");
biomeStructureTooltipBuilder.append(entry.getDescription());
biomeStructureTooltipBuilder.append('\n');
}
biomeStructureTooltipBuilder.append("Requires reloading chunks to take effect.");
biomeStructureImplementation.getItems().addAll(biomeStructureIds);
biomeStructureImplementation.getItems().addAll(BiomeStructure.REGISTRY.values());
biomeStructureImplementation.getSelectionModel().selectedItemProperty()
.addListener((observable, oldvalue, newvalue) -> {
scene.setBiomeStructureImplementation(newvalue);
PersistentSettings.setBiomeStructureImplementation(newvalue);
scene.setBiomeStructureImplementation(newvalue.getId());
PersistentSettings.setBiomeStructureImplementation(newvalue.getId());
});
CustomizedListCellFactory.install(biomeStructureImplementation, RegisterableCellAdapter.INSTANCE);
biomeStructureImplementation.setTooltip(new Tooltip(biomeStructureTooltipBuilder.toString()));

gridSize.setRange(4, 64);
Expand Down Expand Up @@ -306,11 +306,13 @@ public PictureExportFormat fromString(String string) {

rendererSelect.setTooltip(new Tooltip("The renderer to use for rendering."));
rendererSelect.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) ->
scene.setRenderer(newValue));
scene.setRenderer(newValue.getId()));
CustomizedListCellFactory.install(rendererSelect, RegisterableCellAdapter.INSTANCE);

previewSelect.setTooltip(new Tooltip("The renderer to use for the preview."));
previewSelect.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) ->
scene.setPreviewRenderer(newValue));
scene.setPreviewRenderer(newValue.getId()));
CustomizedListCellFactory.install(previewSelect, RegisterableCellAdapter.INSTANCE);

LauncherSettings settings = new LauncherSettings();
settings.load();
Expand Down Expand Up @@ -339,15 +341,15 @@ public void update(Scene scene) {
cpuLoad.set(PersistentSettings.getCPULoad());
rayDepth.set(scene.getRayDepth());
branchCount.set(scene.getBranchCount());
octreeImplementation.getSelectionModel().select(scene.getOctreeImplementation());
bvhMethod.getSelectionModel().select(scene.getBvhImplementation());
biomeStructureImplementation.getSelectionModel().select(scene.getBiomeStructureImplementation());
octreeImplementation.getSelectionModel().select(Octree.getImplementation(scene.getOctreeImplementation()));
bvhMethod.getSelectionModel().select(BVH.Factory.getImplementation(scene.getBvhImplementation()));
biomeStructureImplementation.getSelectionModel().select(BiomeStructure.REGISTRY.get(scene.getBiomeStructureImplementation()));
gridSize.set(scene.getGridSize());
preventNormalEmitterWithSampling.setSelected(scene.isPreventNormalEmitterWithSampling());
animationTime.set(scene.getAnimationTime());
hideUnknownBlocks.setSelected(scene.getHideUnknownBlocks());
rendererSelect.getSelectionModel().select(scene.getRenderer());
previewSelect.getSelectionModel().select(scene.getPreviewRenderer());
rendererSelect.getSelectionModel().select(DefaultRenderManager.renderers.get(scene.getRenderer()));
previewSelect.getSelectionModel().select(DefaultRenderManager.previewRenderers.get(scene.getPreviewRenderer()));
}

@Override
Expand All @@ -372,24 +374,17 @@ public void setController(RenderControlsFxController controls) {
}
});

// Set the renderers
rendererSelect.getItems().clear();
RenderManager renderManager = controller.getRenderManager();
ArrayList<String> ids = new ArrayList<>();

for (Registerable renderer : renderManager.getRenderers())
ids.add(renderer.getId());
RenderManager renderManager = controller.getRenderManager();

rendererSelect.getItems().addAll(ids);
rendererSelect.getSelectionModel().select(scene.getRenderer());
// Set the renderers
rendererSelect.getItems().clear();
rendererSelect.getItems().addAll(renderManager.getRenderers());
rendererSelect.getSelectionModel().select(renderManager.getRenderers().stream().filter(r -> r.getId().equals(scene.getRenderer())).findFirst().orElse(null));

// Set the preview renderers, reuse the `ids` ArrayList
// Set the preview renderers
previewSelect.getItems().clear();
ids.clear();
for (Registerable render : renderManager.getPreviewRenderers())
ids.add(render.getId());

previewSelect.getItems().addAll(ids);
previewSelect.getSelectionModel().select(scene.getPreviewRenderer());
previewSelect.getItems().addAll(renderManager.getPreviewRenderers());
previewSelect.getSelectionModel().select(renderManager.getPreviewRenderers().stream().filter(r -> r.getId().equals(scene.getPreviewRenderer())).findFirst().orElse(null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.control.Tooltip;
import javafx.scene.control.SingleSelectionModel;
import javafx.util.StringConverter;
import se.llbit.chunky.renderer.postprocessing.PostProcessingFilter;
import se.llbit.chunky.renderer.postprocessing.PostProcessingFilters;
Expand All @@ -32,20 +33,22 @@
import se.llbit.chunky.ui.DoubleAdjuster;
import se.llbit.chunky.ui.controller.RenderControlsFxController;
import se.llbit.chunky.ui.render.RenderControlsTab;
import se.llbit.chunky.ui.RegisterableCellAdapter;
import se.llbit.fxutil.CustomizedListCellFactory;
import se.llbit.util.ProgressListener;
import se.llbit.util.TaskTracker;
import se.llbit.util.TaskTracker.Task;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import se.llbit.util.TaskTracker.Task;

public class PostprocessingTab extends ScrollPane implements RenderControlsTab, Initializable {
private Scene scene;
private RenderControlsFxController controller;

@FXML private DoubleAdjuster exposure;
@FXML private ChoiceBox<PostProcessingFilter> postprocessingFilter;
@FXML private ComboBox<PostProcessingFilter> postprocessingFilter;

public PostprocessingTab() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("PostprocessingTab.fxml"));
Expand Down Expand Up @@ -84,21 +87,13 @@ public PostprocessingTab() throws IOException {
postprocessingFilter.getSelectionModel().select(Scene.DEFAULT_POSTPROCESSING_FILTER);
postprocessingFilter.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
scene.setPostprocess(newValue);
scene.postProcessFrame(new TaskTracker(ProgressListener.NONE));
controller.getCanvas().forceRepaint();
if (!(newValue instanceof Separator)) {
scene.setPostprocess(newValue);
scene.postProcessFrame(new TaskTracker(ProgressListener.NONE));
controller.getCanvas().forceRepaint();
}
});
postprocessingFilter.setConverter(new StringConverter<PostProcessingFilter>() {
@Override
public String toString(PostProcessingFilter object) {
return object == null ? null : object.getName();
}

@Override
public PostProcessingFilter fromString(String string) {
return PostProcessingFilters.getPostProcessingFilterFromName(string).orElse(Scene.DEFAULT_POSTPROCESSING_FILTER);
}
});
CustomizedListCellFactory.install(postprocessingFilter, RegisterableCellAdapter.INSTANCE);
exposure.setName("Exposure");
exposure.setTooltip("Linear exposure of the image.");
exposure.setRange(Scene.MIN_EXPOSURE, Scene.MAX_EXPOSURE);
Expand Down
Loading