diff --git a/Readme.md b/Readme.md index 668aca089..1e82c25af 100644 --- a/Readme.md +++ b/Readme.md @@ -20,8 +20,5 @@ https://github.com/lz4/lz4-java NightConfig for Json & Toml (config handling)\ https://github.com/TheElectronWill/night-config -SVG Salamander for SVG's\ +SVG Salamander for SVG support (not being used atm)\ https://github.com/blackears/svgSalamander - -FlatLaf for theming (for development testing, may remove later)\ -https://www.formdev.com/flatlaf/ diff --git a/api/build.gradle b/api/build.gradle index cb3195792..9172e5bf8 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -77,3 +77,13 @@ task addSourcesToCompiledJar(type: ShadowJar) { } } + +javadoc { + options { + // Don't log warnings. + // There are a lot of warnings related to missing @param and @return javadocs + // that aren't necessary and would clutter up said javadocs. + // For more info see: https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html + addStringOption('Xdoclint:all,-missing', '-quiet') + } +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java index 5abdee02b..527f93ada 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java @@ -20,18 +20,43 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverri * For example: you can access singletons which handle the config or event binding.

* * Q: Why should I use this class instead of just getting the API singleton I need?
- * * A: This way there is a lower chance of your code breaking if we change something on our end. * For example, if we realized there is a much better way of handling dependency injection we would keep the * interface the same so your code doesn't have to change. Whereas if you were directly referencing * the concrete object we replaced, there would be issues. * * @author James Seibel - * @version 2023-6-29 + * @version 2023-8-26 * @since API 1.0.0 */ public class DhApi { + /** + * If you can see this Java Doc, this can be ignored.
+ * This is just to you know that Javadocs are available and that you should use the API jar + * instead of the full mod jar.

+ * + * Note: Don't use this string in your code. It may change and is only for reference. + */ + public static String READ_ME = + "If you don't see Javadocs something is wrong. \n" + + "If you are only using the full DH Mod in your build script, you won't have access to our javadocs and could potentially call into unsafe code. \n" + + "\n" + + "Please use the API jar in your build script as a compile time dependency " + + "and the full DH jar as a runtime dependency. \n" + + "\n" + + "Please refer to the example API project or the DH Developer Wiki for additional information " + + "and suggested setup. \n" + // DH Dev note: no links were included to prevent link rot. + ""; + public static String readMe() { return READ_ME; } + /** + * This is just a humorous way to reference the {@link DhApi#READ_ME} constant string and hopefully peak a few people's attention + * vs the relatively boring "readMe". + */ + public static String heyYou_YoureFinallyAwake() { return READ_ME; } + + + /** * WARNING: * All objects in this class will be null until after DH initializes for the first time.

@@ -73,6 +98,7 @@ public class DhApi } + // always available // /** diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/IDhApiConfigValue.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/IDhApiConfigValue.java index fc91e534a..b5ac00375 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/IDhApiConfigValue.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/IDhApiConfigValue.java @@ -1,5 +1,7 @@ package com.seibel.distanthorizons.api.interfaces.config; +import java.util.function.Consumer; + /** * An interface for Distant Horizon's Config. * @@ -16,17 +18,17 @@ public interface IDhApiConfigValue * Returns the True value if either the config cannot be overridden by * the API or if it hasn't been set by the API. */ - public T getValue(); + T getValue(); /** * Returns the value held by this config.
* This is the value stored in the config file. */ - public T getTrueValue(); - /** + T getTrueValue(); + /* * Returns the value of the config if it was set by the API. * Returns null if the config wasn't set by the API. */ - public T getApiValue(); + //T getApiValue(); // not currently implemented /** * Sets the config's value.
@@ -36,16 +38,20 @@ public interface IDhApiConfigValue * * @return true if the value was set, false otherwise. */ - public boolean setValue(T newValue); + boolean setValue(T newValue); /** Returns true if this config can be set via the API, false otherwise. */ - public boolean getCanBeOverrodeByApi(); + boolean getCanBeOverrodeByApi(); /** Returns the default value for this config. */ - public T getDefaultValue(); + T getDefaultValue(); /** Returns the max value for this config, null if there is no max. */ - public T getMaxValue(); + T getMaxValue(); /** Returns the min value for this config, null if there is no min. */ - public T getMinValue(); + T getMinValue(); + + /** Adds a {@link Consumer} that will be called whenever the config changes to a new value. */ + void addChangeListener(Consumer onValueChangeFunc); + //void removeListener(Consumer onValueChangeFunc); // not currently implemented } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/events/IDhApiEventInjector.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/events/IDhApiEventInjector.java index 79cdd31c0..9be0d8559 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/events/IDhApiEventInjector.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/events/IDhApiEventInjector.java @@ -35,6 +35,8 @@ public interface IDhApiEventInjector extends IDependencyInjector /** * Unlinks the given event handler, preventing the handler from being called in the future. * + * @param dependencyInterface the base interface for the {@link IDhApiEvent} + * @param dependencyClassToRemove the concrete {@link IDhApiEvent} class to remove * @return true if the handler was unbound, false if the handler wasn't bound. * @throws IllegalArgumentException if the implementation object doesn't implement the interface */ @@ -48,6 +50,7 @@ public interface IDhApiEventInjector extends IDependencyInjector * @param abstractEvent event type * @param eventParameterObject event parameter * @param the parameter type taken by the event handlers. + * @param the {@link IDhApiEvent}'s class * @return if any of bound event handlers returned that this event should be canceled. */ > boolean fireAllEvents(Class abstractEvent, T eventParameterObject); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/IDhApiOverrideable.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/IDhApiOverrideable.java index 1736e52d7..493ceaada 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/IDhApiOverrideable.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/IDhApiOverrideable.java @@ -13,8 +13,10 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverri public interface IDhApiOverrideable extends IBindable { /** - * Higher (larger numerical) priorities override lower (smaller numerical) priorities .
+ * Higher (larger numerical) priorities override lower (smaller numerical) priorities.
* For most developers this can be left at the default. + * + * @return The priority of this interface, the lowest legal value is {@link IOverrideInjector#MIN_NON_CORE_OVERRIDE_PRIORITY}. */ default int getPriority() { return IOverrideInjector.DEFAULT_NON_CORE_OVERRIDE_PRIORITY; } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java index 2698c2d06..32892ae9e 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java @@ -61,10 +61,14 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh /** * This method is called to generate terrain over a given area * from a thread defined by Distant Horizons.

- * - * See {@link IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator.generateChunks} - * for the list of Object's this method should return along with additional documentation. - * + * + * @param chunkPosX the chunk X position in the level (not to be confused with the chunk's BlockPos in the level) + * @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level) + * @param generatorMode how far into the world gen pipeline this method run. See {@link EDhApiDistantGeneratorMode} for additional documentation. + * + * @return See {@link IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator.generateChunks} + * for the list of Object's this method should return along with additional documentation. + * * @see IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator#generateChunks */ public abstract Object[] generateChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java index 8396ff340..3c412e96b 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java @@ -28,7 +28,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * * TODO: System currently only supports 1x1 block per data. * - * @see EDhApiDetailLevel + * @see EDhApiDetailLevel */ default byte getSmallestDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; } /** @@ -37,7 +37,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * Default detail level is 0
* For more information on what detail levels represent see: {@link EDhApiDetailLevel}. * - * @see EDhApiDetailLevel + * @see EDhApiDetailLevel */ default byte getLargestDataDetailLevel() { return EDhApiDetailLevel.BLOCK.detailLevel; } @@ -49,7 +49,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * Default detail level is 4
* For more information on what detail levels represent see: {@link EDhApiDetailLevel}. * - * @see EDhApiDetailLevel + * @see EDhApiDetailLevel */ default byte getMinGenerationGranularity() { return EDhApiDetailLevel.CHUNK.detailLevel; } @@ -61,11 +61,11 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * Default detail level is 6 (4x4 chunks)
* For more information on what detail levels represent see: {@link EDhApiDetailLevel}. * - * @see EDhApiDetailLevel + * @see EDhApiDetailLevel */ default byte getMaxGenerationGranularity() { return (byte) (EDhApiDetailLevel.CHUNK.detailLevel + 2); } - /** Returns true if the generator is unable to accept new generation requests. */ + /** @return true if the generator is unable to accept new generation requests. */ boolean isBusy(); @@ -81,13 +81,22 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * After a chunk has been generated it (and any necessary supporting objects as listed below) should be passed into the * resultConsumer's {@link Consumer#accept} method. If the Consumer is given the wrong data * type(s) it will disable the world generator and log an error with a list of objects it was expecting.
- * Note: these objects are minecraft version dependent and will change without notice! + * Note: these objects are minecraft version dependent and will change without notice! * Please run your generator in game at least once to confirm the objects you are returning are correct.

* * Consumer expected inputs for each minecraft version (in order):
* 1.16, 1.17, 1.18, 1.19, 1.20:
* - [net.minecraft.world.level.chunk.ChunkAccess]
* - [net.minecraft.world.level.ServerLevel] or [net.minecraft.world.level.ClientLevel]
+ * + * @param chunkPosMinX the chunk X position closest to negative infinity + * @param chunkPosMinZ the chunk Z position closest to negative infinity + * @param granularity TODO find a central location to store the definition of granularity. For now it is stored in the Core method: WorldGenerationQueue#startGenerationEvent + * @param targetDataDetail the LOD Detail level requested to generate. See {@link EDhApiDetailLevel} for additional information. + * @param generatorMode how far into the world gen pipeline this method run. See {@link EDhApiDistantGeneratorMode} for additional documentation. + * @param worldGeneratorThreadPool the thread pool that should be used when generating the returned {@link CompletableFuture}. + * @param resultConsumer the consumer that should be fired whenever a chunk finishes generating. + * @return a future that should run on the worldGeneratorThreadPool and complete once the given generation task has completed. */ CompletableFuture generateChunks( int chunkPosMinX, int chunkPosMinZ, diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/config/DhApiConfigValue.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/config/DhApiConfigValue.java index e1d16c485..2931cb9fa 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/config/DhApiConfigValue.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/config/DhApiConfigValue.java @@ -5,6 +5,8 @@ import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry; import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter; import com.seibel.distanthorizons.coreapi.util.converters.DefaultConverter; +import java.util.function.Consumer; + /** * A wrapper used to interface with Distant Horizon's Config.

* @@ -68,8 +70,18 @@ public class DhApiConfigValue implements IDhApiConfigValue onValueChangeFunc) + { + this.configEntry.addValueChangeListener((coreValue) -> + { + apiType apiValue = this.configConverter.convertToApiType(coreValue); + onValueChangeFunc.accept(apiValue); + }); + } + } diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java index 36325d085..e268c7c2d 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java @@ -172,6 +172,15 @@ public class ApiEventInjector extends DependencyInjector implements } + /** + * Wraps the event parameter object in a {@link DhApiCancelableEventParam} or {@link DhApiEventParam} depending on + * if it should allow cancellation or not. + * + * @param event the event instance + * @param parameter the event's parameter object + * @param the event parameter type + * @return the event parameter wrapped in a {@link DhApiCancelableEventParam} or {@link DhApiEventParam} + */ public static DhApiEventParam createEventParamWrapper(IDhApiEvent event, T parameter) { return (event instanceof IDhApiCancelableEvent) ? new DhApiCancelableEventParam<>(parameter) : new DhApiEventParam<>(parameter); diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java index fb137f3bc..afb6dad1e 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java @@ -1,6 +1,8 @@ package com.seibel.distanthorizons.coreapi.interfaces.config; +import java.util.function.Consumer; + /** * Use for making the config variables * @@ -56,4 +58,6 @@ public interface IConfigEntry /** Is the value of this equal to another */ boolean equals(IConfigEntry obj); + void addValueChangeListener(Consumer onValueChangeFunc); + } diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IDependencyInjector.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IDependencyInjector.java index 266c43e66..3024405b1 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IDependencyInjector.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IDependencyInjector.java @@ -25,7 +25,7 @@ public interface IDependencyInjector /** - * This does not return incomplete dependencies.
+ * Does not return incomplete dependencies.
* See {@link #get(Class, boolean) get(Class, boolean)} for full documentation. * * @see #get(Class, boolean) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IOverrideInjector.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IOverrideInjector.java index 15f9dcbc4..bf1d3d6ce 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IOverrideInjector.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/dependencyInjection/IOverrideInjector.java @@ -7,12 +7,12 @@ public interface IOverrideInjector { /** * All core overrides should have this priority.
- * Should be lower than MIN_OVERRIDE_PRIORITY. + * Should be lower than {@link IOverrideInjector#MIN_NON_CORE_OVERRIDE_PRIORITY}. */ public static final int CORE_PRIORITY = -1; /** * The lowest priority non-core overrides can have. - * Should be higher than CORE_PRIORITY. + * Should be higher than {@link IOverrideInjector#CORE_PRIORITY}. */ public static final int MIN_NON_CORE_OVERRIDE_PRIORITY = 0; /** The priority given to overrides that don't explicitly define a priority. */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java index 3684e61a6..22483c81b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java @@ -29,7 +29,6 @@ public class Initializer Class compressor = LZ4Compressor.class; Class networking = ByteBuf.class; Class toml = com.electronwill.nightconfig.core.Config.class; - Class flatlaf = com.formdev.flatlaf.FlatDarculaLaf.class; } catch (NoClassDefFoundError e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index e6e28024c..3731c5a75 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -152,7 +152,6 @@ public class Config + "\n" + "Lowest Quality: " + EMaxHorizontalResolution.CHUNK + "\n" + "Highest Quality: " + EMaxHorizontalResolution.BLOCK) - .addListener(RenderCacheConfigEventHandler.INSTANCE) .setPerformance(EConfigEntryPerformance.MEDIUM) .build(); @@ -173,7 +172,6 @@ public class Config + "Lowest Quality: " + EVerticalQuality.HEIGHT_MAP + "\n" + "Highest Quality: " + EVerticalQuality.EXTREME) .setPerformance(EConfigEntryPerformance.VERY_HIGH) - .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry ssao = new ConfigEntry.Builder() @@ -200,7 +198,6 @@ public class Config + ETransparency.DISABLED + ": LODs will be opaque. \n" + "") .setPerformance(EConfigEntryPerformance.MEDIUM) - .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry blocksToIgnore = new ConfigEntry.Builder() @@ -212,7 +209,6 @@ public class Config + EBlocksToAvoid.NON_COLLIDING + ": Only represent solid blocks in the LODs (tall grass, torches, etc. won't count for a LOD's height) \n" + "") .setPerformance(EConfigEntryPerformance.NONE) - .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry tintWithAvoidedBlocks = new ConfigEntry.Builder() @@ -224,7 +220,6 @@ public class Config + "False: skipped blocks will not change color of surface below them. " + "") .setPerformance(EConfigEntryPerformance.NONE) - .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); // TODO fixme @@ -532,7 +527,6 @@ public class Config + "0 = black \n" + "1 = normal \n" + "2 = near white") - .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry saturationMultiplier = new ConfigEntry.Builder() // TODO: Make this a float (the ClassicConfigGUI doesnt support floats) @@ -543,7 +537,6 @@ public class Config + "0 = black and white \n" + "1 = normal \n" + "2 = very saturated") - .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry enableCaveCulling = new ConfigEntry.Builder() @@ -601,7 +594,6 @@ public class Config + ELodShading.NONE + ": All LOD sides will be rendered with the same brightness. \n" + "") .setPerformance(EConfigEntryPerformance.NONE) - .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); } @@ -1242,6 +1234,7 @@ public class Config ThreadPresetConfigEventHandler.INSTANCE.setUiOnlyConfigValues(); RenderQualityPresetConfigEventHandler.INSTANCE.setUiOnlyConfigValues(); QuickRenderToggleConfigEventHandler.INSTANCE.setUiOnlyConfigValues(); + RenderCacheConfigEventHandler.getInstance(); } catch (Exception e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java index 4ad052173..ac3eb4cb1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java @@ -1,9 +1,12 @@ package com.seibel.distanthorizons.core.config.eventHandlers; import com.seibel.distanthorizons.api.DhApi; +import com.seibel.distanthorizons.api.enums.config.EBlocksToAvoid; import com.seibel.distanthorizons.api.enums.config.ELodShading; import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution; import com.seibel.distanthorizons.api.enums.config.EVerticalQuality; +import com.seibel.distanthorizons.api.enums.rendering.ETransparency; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.listeners.IConfigListener; import com.seibel.distanthorizons.core.config.Config; @@ -16,28 +19,52 @@ import java.util.TimerTask; * * Note: if additional settings should clear the render cache, add those to this listener, don't create a new listener */ -public class RenderCacheConfigEventHandler implements IConfigListener +public class RenderCacheConfigEventHandler { - public static RenderCacheConfigEventHandler INSTANCE = new RenderCacheConfigEventHandler(); + private static RenderCacheConfigEventHandler INSTANCE; + + + // previous values used to check if a watched setting was actually modified + private final ConfigChangeListener horizontalResolutionChangeListener; + private final ConfigChangeListener verticalQualityChangeListener; + private final ConfigChangeListener transparencyChangeListener; + private final ConfigChangeListener blocksToIgnoreChangeListener; + private final ConfigChangeListener tintWithAvoidedBlocksChangeListener; + + private final ConfigChangeListener brightnessMultiplierChangeListener; + private final ConfigChangeListener saturationMultiplierChangeListener; + private final ConfigChangeListener lodShadingChangeListener; /** how long to wait in milliseconds before applying the config changes */ private static final long TIMEOUT_IN_MS = 4_000L; private Timer cacheClearingTimer; - /** private since we only ever need one handler at a time */ - private RenderCacheConfigEventHandler() { } - - - @Override - public void onConfigValueSet() + public static RenderCacheConfigEventHandler getInstance() { - this.refreshRenderDataAfterTimeout(); + if (INSTANCE == null) + { + INSTANCE = new RenderCacheConfigEventHandler(); + } + + return INSTANCE; } - @Override - public void onUiModify() { /* do nothing, we only care about modified config values */ } + /** private since we only ever need one handler at a time */ + private RenderCacheConfigEventHandler() + { + this.horizontalResolutionChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution, (newValue) -> this.refreshRenderDataAfterTimeout()); + this.verticalQualityChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.verticalQuality, (newValue) -> this.refreshRenderDataAfterTimeout()); + this.transparencyChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.transparency, (newValue) -> this.refreshRenderDataAfterTimeout()); + this.blocksToIgnoreChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.blocksToIgnore, (newValue) -> this.refreshRenderDataAfterTimeout()); + this.tintWithAvoidedBlocksChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks, (newValue) -> this.refreshRenderDataAfterTimeout()); + + this.brightnessMultiplierChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.AdvancedGraphics.brightnessMultiplier, (newValue) -> this.refreshRenderDataAfterTimeout()); + this.saturationMultiplierChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.AdvancedGraphics.saturationMultiplier, (newValue) -> this.refreshRenderDataAfterTimeout()); + this.lodShadingChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading, (newValue) -> this.refreshRenderDataAfterTimeout()); + + } /** Calling this method multiple times will reset the timer */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/IConfigListener.java b/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/IConfigListener.java index 5d643db5b..79ea44005 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/IConfigListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/IConfigListener.java @@ -3,9 +3,9 @@ package com.seibel.distanthorizons.core.config.listeners; public interface IConfigListener { /** Called whenever the value is set (including in core DH code) */ - default void onConfigValueSet() {}; + default void onConfigValueSet() {} /** Called whenever the value is changed through the UI */ - default void onUiModify() {}; + default void onUiModify() {} } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java index 2fbd85471..92b6d26a1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.config.types; import com.seibel.distanthorizons.core.config.NumberUtil; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.listeners.IConfigListener; import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance; import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryPerformance; @@ -9,6 +10,7 @@ import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry; import java.util.ArrayList; import java.util.Arrays; +import java.util.function.Consumer; /** * Use for making the config variables @@ -166,7 +168,16 @@ public class ConfigEntry extends AbstractConfigType> implem /** Gets the performance impact of an option */ public EConfigEntryPerformance getPerformance() { return this.performance; } + /** Fired whenever the config value changes to a new value. */ + public void addValueChangeListener(Consumer onValueChangeFunc) + { + ConfigChangeListener changeListener = new ConfigChangeListener<>(this, onValueChangeFunc); + this.addListener(changeListener); + } + /** Fired whenever the config value is updated, including when the value doesn't change (IE when the UI changes state or the config is reloaded). */ public void addListener(IConfigListener newListener) { this.listenerList.add(newListener); } + + //public void removeValueChangeListener(Consumer onValueChangeFunc) { } // not currently implemented public void removeListener(IConfigListener oldListener) { this.listenerList.remove(oldListener); } public void clearListeners() { this.listenerList.clear(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index b0164d2f7..b87e664bd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -146,7 +146,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler this.incompleteDataSources.put(pos, dataSource); // queue this section to be generated GenTask genTask = new GenTask(pos, new WeakReference<>(dataSource)); - worldGenQueue.submitGenTask(new DhLodPos(pos), dataSource.getDataDetailLevel(), genTask) + worldGenQueue.submitGenTask(pos, dataSource.getDataDetailLevel(), genTask) .whenComplete((genTaskResult, ex) -> { if (genTaskResult.success) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java index 30c5e64b9..95db143bc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java @@ -447,7 +447,7 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider // Skip updating the cache if the data file is already up-to-date FullDataMetaFile dataFile = this.fullDataSourceProvider.getFileIfExist(file.pos); - if (!ALWAYS_INVALIDATE_CACHE && dataFile != null && dataFile.baseMetaData.checksum == file.baseMetaData.dataVersion.get()) { + if (!ALWAYS_INVALIDATE_CACHE && dataFile != null && dataFile.baseMetaData != null && dataFile.baseMetaData.checksum == file.baseMetaData.dataVersion.get()) { LOGGER.debug("Skipping render cache update for {}", file.pos); renderSource.localVersion.incrementAndGet(); return CompletableFuture.completedFuture(null); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java index 7181ae80e..100c6d9fb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java @@ -14,7 +14,7 @@ public interface IWorldGenerationQueue extends Closeable /** the largest numerical detail level */ byte largestDataDetail(); - CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); + CompletableFuture submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); void cancelGenTasks(Iterable positions); /** @param targetPos the position that world generation should be centered around, generally this will be the player's position. */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 51e34783f..33e3f981b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -41,9 +41,9 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender /** contains the positions that need to be generated */ //private final QuadTree waitingTaskQuadTree; - private final ConcurrentHashMap waitingTasks = new ConcurrentHashMap<>(); + private final ConcurrentHashMap waitingTasks = new ConcurrentHashMap<>(); - private final ConcurrentHashMap inProgressGenTasksByLodPos = new ConcurrentHashMap<>(); + private final ConcurrentHashMap inProgressGenTasksByLodPos = new ConcurrentHashMap<>(); // granularity is the detail level for batching world generator requests together public final byte maxGranularity; @@ -73,8 +73,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // debug variables to test for duplicate world generator requests // /** limits how many of the previous world gen requests we should track */ private static final int MAX_ALREADY_GENERATED_COUNT = 100; - private final HashMap alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT); - private final Queue alreadyGeneratedPosQueue = new LinkedList<>(); + private final HashMap alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT); + private final Queue alreadyGeneratedPosQueue = new LinkedList<>(); private static RateLimitedThreadPoolExecutor worldGeneratorThreadPool; private static ConfigChangeListener configListener; @@ -119,7 +119,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // task handling // //=================// - public CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker) + @Override + public CompletableFuture submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { // the generator is shutting down, don't add new tasks if (this.generatorClosingFuture != null) @@ -139,9 +140,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender } // Assert that the data at least can fill in 1 single ChunkSizedFullDataAccessor - LodUtil.assertTrue(pos.detailLevel > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL); - - DhSectionPos requestPos = new DhSectionPos(pos.detailLevel, pos.x, pos.z); + LodUtil.assertTrue(pos.sectionDetailLevel > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL); //if (this.waitingTaskQuadTree.isSectionPosInBounds(requestPos)) @@ -330,7 +329,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender } Mapper closestTaskMap = waitingTasks.reduceEntries(1024, - v -> new Mapper(v.getValue(), v.getValue().pos.getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())), + v -> new Mapper(v.getValue(), v.getValue().pos.getSectionBBoxPos().getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())), (a, b) -> a.dist < b.dist ? a : b); closestTask = closestTaskMap.task; @@ -375,14 +374,14 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // split up the task and add each one to the tree LinkedList> childFutures = new LinkedList<>(); - DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z); + DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.sectionDetailLevel, closestTask.pos.sectionX, closestTask.pos.sectionZ); WorldGenTask finalClosestTask = closestTask; sectionPos.forEachChild((childDhSectionPos) -> { CompletableFuture newFuture = new CompletableFuture<>(); childFutures.add(newFuture); - WorldGenTask newGenTask = new WorldGenTask(new DhLodPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), childDhSectionPos.sectionDetailLevel, finalClosestTask.taskTracker, newFuture); + WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, childDhSectionPos.sectionDetailLevel, finalClosestTask.taskTracker, newFuture); waitingTasks.put(newGenTask.pos, newGenTask); //this.waitingTaskQuadTree.setValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), newGenTask); @@ -402,12 +401,12 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender private void startWorldGenTaskGroup(InProgressWorldGenTaskGroup inProgressTaskGroup) { byte taskDetailLevel = inProgressTaskGroup.group.dataDetail; - DhLodPos taskPos = inProgressTaskGroup.group.pos; - byte granularity = (byte) (taskPos.detailLevel - taskDetailLevel); + DhSectionPos taskPos = inProgressTaskGroup.group.pos; + byte granularity = (byte) (taskPos.sectionDetailLevel - taskDetailLevel); LodUtil.assertTrue(granularity >= this.minGranularity && granularity <= this.maxGranularity); LodUtil.assertTrue(taskDetailLevel >= this.smallestDataDetail && taskDetailLevel <= this.largestDataDetail); - DhChunkPos chunkPosMin = new DhChunkPos(taskPos.getCornerBlockPos()); + DhChunkPos chunkPosMin = new DhChunkPos(taskPos.getSectionBBoxPos().getCornerBlockPos()); // check if this is a duplicate generation task if (this.alreadyGeneratedPosHashSet.containsKey(inProgressTaskGroup.group.pos)) @@ -418,7 +417,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender //StackTraceElement[] stackTrace = this.alreadyGeneratedPosHashSet.get(inProgressTaskGroup.group.pos); // sending a success result is necessary to make sure the render sections are reloaded correctly - inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos)))); + inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.sectionX, taskPos.sectionZ)))); return; } this.alreadyGeneratedPosHashSet.put(inProgressTaskGroup.group.pos, Thread.currentThread().getStackTrace()); @@ -427,7 +426,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // remove extra tracked duplicate positions while (this.alreadyGeneratedPosQueue.size() > MAX_ALREADY_GENERATED_COUNT) { - DhLodPos posToRemove = this.alreadyGeneratedPosQueue.poll(); + DhSectionPos posToRemove = this.alreadyGeneratedPosQueue.poll(); this.alreadyGeneratedPosHashSet.remove(posToRemove); } @@ -452,7 +451,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender else { //LOGGER.info("Section generation at "+pos+" completed"); - inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos)))); + inProgressTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.sectionX, taskPos.sectionZ)))); } boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, inProgressTaskGroup); LodUtil.assertTrue(worked); @@ -666,9 +665,9 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // helper methods // //================// - private boolean canGeneratePos(byte worldGenTaskGroupDetailLevel /*when in doubt use 0*/ , DhLodPos taskPos) + private boolean canGeneratePos(byte worldGenTaskGroupDetailLevel /*when in doubt use 0*/ , DhSectionPos taskPos) { - byte granularity = (byte) (taskPos.detailLevel - worldGenTaskGroupDetailLevel); + byte granularity = (byte) (taskPos.sectionDetailLevel - worldGenTaskGroupDetailLevel); return (granularity >= this.minGranularity && granularity <= this.maxGranularity); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java index 427bd7396..78fa9a24b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTask.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.core.generation.tasks; import com.seibel.distanthorizons.core.pos.DhLodPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import java.util.concurrent.CompletableFuture; @@ -10,14 +11,14 @@ import java.util.concurrent.CompletableFuture; */ public final class WorldGenTask { - public final DhLodPos pos; + public final DhSectionPos pos; public final byte dataDetailLevel; public final IWorldGenTaskTracker taskTracker; public final CompletableFuture future; - public WorldGenTask(DhLodPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture future) + public WorldGenTask(DhSectionPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture future) { this.dataDetailLevel = dataDetail; this.pos = pos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java index 3eda6d9b7..209677419 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/WorldGenTaskGroup.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.generation.tasks; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.pos.DhLodPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import java.util.Iterator; import java.util.LinkedList; @@ -13,14 +14,14 @@ import java.util.function.Consumer; */ public final class WorldGenTaskGroup { - public final DhLodPos pos; + public final DhSectionPos pos; public byte dataDetail; /** Only accessed by the generator polling thread */ public final LinkedList worldGenTasks = new LinkedList<>(); - public WorldGenTaskGroup(DhLodPos pos, byte dataDetail) + public WorldGenTaskGroup(DhSectionPos pos, byte dataDetail) { this.pos = pos; this.dataDetail = dataDetail; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/jar/gui/BaseJFrame.java b/core/src/main/java/com/seibel/distanthorizons/core/jar/gui/BaseJFrame.java index a230fec5e..8445c5d54 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/jar/gui/BaseJFrame.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/jar/gui/BaseJFrame.java @@ -1,8 +1,5 @@ package com.seibel.distanthorizons.core.jar.gui; -import com.formdev.flatlaf.FlatDarkLaf; -import com.formdev.flatlaf.FlatLightLaf; -import com.formdev.flatlaf.extras.FlatSVGIcon; import com.seibel.distanthorizons.core.jar.JarUtils; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper; @@ -13,6 +10,7 @@ import java.awt.*; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.InputStreamReader; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.List; @@ -41,7 +39,7 @@ public class BaseJFrame extends JFrame setTitle(SingletonInjector.INSTANCE.get(ILangWrapper.class).getLang("lod.title")); try { - setIconImage(new FlatSVGIcon(JarUtils.accessFile("iconLegacy.svg")).getImage()); // SVG Salamander (the library which we use for svg files) doesn't support css class colors + setIconImage(ImageIO.read(JarUtils.accessFile("icon.png"))); } catch (Exception e) { @@ -96,7 +94,8 @@ public class BaseJFrame extends JFrame add(languageBox); - // ========== THEMING ========== + // ========== THEMING ========== // + /** // TODO: Change the theme to a toggle switch rather than having 2 buttons int themeButtonSize = 25; JButton lightMode = null; @@ -132,6 +131,7 @@ public class BaseJFrame extends JFrame // Finally add the buttons add(lightMode); add(darkMode); + */ } public BaseJFrame addLogo() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 4e8b1cbe9..dd6eafac6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -287,9 +287,6 @@ public class LodRenderer { profiler.popPush("LOD SSAO"); SSAORenderer.INSTANCE.render(partialTicks); - - // TODO: Fix this file (or check the result is the same) so that SSAORenderer could be deleted - //SSAOShader.INSTANCE.render(partialTicks); // For some reason this looks slightly different :/ } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java index 5abae934e..4fe99dcc8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java @@ -30,7 +30,6 @@ public class FogShader extends AbstractShaderRenderer public final int fogVerticalScaleUniform; public final int nearFogStartUniform; public final int nearFogLengthUniform; - ; public final int fullFogModeUniform; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java index 102cf4c5f..553150f8a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAORenderer.java @@ -16,18 +16,13 @@ import org.lwjgl.opengl.GL32; import java.nio.ByteBuffer; import java.nio.ByteOrder; -// TODO: Move over to SSAOShader -// For some reason this version looks slightly different to that, even tough there isnt much visible change in the code public class SSAORenderer { public static SSAORenderer INSTANCE = new SSAORenderer(); - public SSAORenderer() - { - } - private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final int MAX_KERNEL_SIZE = 128; private static final float[] box_vertices = { -1, -1, 1, -1, @@ -37,67 +32,115 @@ public class SSAORenderer -1, 1, }; - ShaderProgram ssaoShader; - ShaderProgram applyShader; - GLVertexBuffer boxBuffer; - VertexAttribute va; - boolean init = false; - private static final int MAX_KERNEL_SIZE = 32; + + private ShaderProgram ssaoShader; + private ShaderProgram applyShader; + private GLVertexBuffer boxBuffer; + private VertexAttribute va; + private boolean init = false; + private float[] kernel = new float[MAX_KERNEL_SIZE * 3]; - public void init() - { - if (init) return; - - init = true; - va = VertexAttribute.create(); - va.bind(); - // Pos - va.setVertexAttribute(0, 0, VertexAttribute.VertexPointer.addVec2Pointer(false)); - va.completeAndCheck(Float.BYTES * 2); - ssaoShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag", - "fragColor", new String[]{"vPosition"}); - - applyShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/apply-frag.frag", - "fragColor", new String[]{"vPosition"}); - - - // Generate kernel - kernel = genKernel(); - // Framebuffer - createBuffer(); - } - private int width = -1; private int height = -1; private int ssaoFramebuffer = -1; private int ssaoTexture = -1; + // ssao uniforms + private final SsaoShaderUniforms ssaoShaderUniforms = new SsaoShaderUniforms(); + private static class SsaoShaderUniforms + { + public int gProjUniform; + public int gSampleRadUniform; + public int gFactorUniform; + public int gPowerUniform; + public int gKernelUniform; + public int gDepthMapUniform; + } + + // apply uniforms + private final ApplyShaderUniforms applyShaderUniforms = new ApplyShaderUniforms(); + private static class ApplyShaderUniforms + { + public int gSSAOMapUniform; + public int gDepthMapUniform; + } + + + + //=============// + // constructor // + //=============// + + private SSAORenderer() { } + + public void init() + { + if (this.init) + { + return; + } + + + this.init = true; + this.va = VertexAttribute.create(); + this.va.bind(); + // Pos + this.va.setVertexAttribute(0, 0, VertexAttribute.VertexPointer.addVec2Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 2); + this.ssaoShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag", + "fragColor", new String[]{"vPosition"}); + + this.applyShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/apply-frag.frag", + "fragColor", new String[]{"vPosition"}); + + + + // SSAO uniform setup + this.ssaoShaderUniforms.gProjUniform = this.ssaoShader.getUniformLocation("gProj"); + this.ssaoShaderUniforms.gSampleRadUniform = this.ssaoShader.getUniformLocation("gSampleRad"); + this.ssaoShaderUniforms.gFactorUniform = this.ssaoShader.getUniformLocation("gFactor"); + this.ssaoShaderUniforms.gPowerUniform = this.ssaoShader.getUniformLocation("gPower"); + this.ssaoShaderUniforms.gKernelUniform = this.ssaoShader.getUniformLocation("gKernel"); + this.ssaoShaderUniforms.gDepthMapUniform = this.ssaoShader.getUniformLocation("gDepthMap"); + + // Apply uniform setup + this.applyShaderUniforms.gSSAOMapUniform = this.applyShader.getUniformLocation("gSSAOMap"); + this.applyShaderUniforms.gDepthMapUniform = this.applyShader.getUniformLocation("gDepthMap"); + + + + // Generate kernel + this.kernel = genKernel(); + // Framebuffer + this.createBuffer(); + } + private void createFramebuffer(int width, int height) { - if (ssaoFramebuffer != -1) + if (this.ssaoFramebuffer != -1) { - GL32.glDeleteFramebuffers(ssaoFramebuffer); - ssaoFramebuffer = -1; + GL32.glDeleteFramebuffers(this.ssaoFramebuffer); + this.ssaoFramebuffer = -1; } - if (ssaoTexture != -1) + if (this.ssaoTexture != -1) { - GL32.glDeleteTextures(ssaoTexture); - ssaoTexture = -1; + GL32.glDeleteTextures(this.ssaoTexture); + this.ssaoTexture = -1; } - ssaoFramebuffer = GL32.glGenFramebuffers(); - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, ssaoFramebuffer); + this.ssaoFramebuffer = GL32.glGenFramebuffers(); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer); - ssaoTexture = GL32.glGenTextures(); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, ssaoTexture); + this.ssaoTexture = GL32.glGenTextures(); + GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.ssaoTexture); GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RED, width, height, 0, GL32.GL_RED, GL32.GL_FLOAT, (ByteBuffer) null); GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST); GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST); - GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, ssaoTexture, 0); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.ssaoTexture, 0); } private void createBuffer() @@ -106,9 +149,9 @@ public class SSAORenderer buffer.order(ByteOrder.nativeOrder()); buffer.asFloatBuffer().put(box_vertices); buffer.rewind(); - boxBuffer = new GLVertexBuffer(false); - boxBuffer.bind(); - boxBuffer.uploadBuffer(buffer, box_vertices.length, EGpuUploadMethod.DATA, box_vertices.length * Float.BYTES); + this.boxBuffer = new GLVertexBuffer(false); + this.boxBuffer.bind(); + this.boxBuffer.uploadBuffer(buffer, box_vertices.length, EGpuUploadMethod.DATA, box_vertices.length * Float.BYTES); } private static float[] genKernel() @@ -140,11 +183,16 @@ public class SSAORenderer return kernel; } + + + //========// + // render // + //========// + public void render(float partialTicks) { GLState state = new GLState(); - init(); - //GL32.glDepthMask(false); + this.init(); int width = MC_RENDER.getTargetFrameBufferViewportWidth(); int height = MC_RENDER.getTargetFrameBufferViewportHeight(); @@ -152,10 +200,10 @@ public class SSAORenderer { this.width = width; this.height = height; - createFramebuffer(width, height); + this.createFramebuffer(width, height); } - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, ssaoFramebuffer); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer); GL32.glViewport(0, 0, width, height); GL32.glDisable(GL32.GL_DEPTH_TEST); GL32.glDisable(GL32.GL_BLEND); @@ -168,35 +216,36 @@ public class SSAORenderer RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks), (float) ((RenderUtil.getFarClipPlaneDistanceInBlocks() + LodUtil.REGION_WIDTH) * Math.sqrt(2))); - ssaoShader.bind(); - ssaoShader.setUniform(ssaoShader.getUniformLocation("gProj"), perspective); - ssaoShader.setUniform(ssaoShader.getUniformLocation("gSampleRad"), 3.0f); - ssaoShader.setUniform(ssaoShader.getUniformLocation("gFactor"), 0.8f); - ssaoShader.setUniform(ssaoShader.getUniformLocation("gPower"), 1.0f); - va.bind(); - va.bindBufferToAllBindingPoint(boxBuffer.getId()); + this.ssaoShader.bind(); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gProjUniform, perspective); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gSampleRadUniform, 3.0f); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gFactorUniform, 0.8f); + this.ssaoShader.setUniform(this.ssaoShaderUniforms.gPowerUniform, 1.0f); + + this.va.bind(); + this.va.bindBufferToAllBindingPoint(this.boxBuffer.getId()); GL32.glActiveTexture(GL32.GL_TEXTURE0); GL32.glBindTexture(GL32.GL_TEXTURE_2D, MC_RENDER.getDepthTextureId()); - GL32.glUniform3fv(ssaoShader.getUniformLocation("gKernel"), kernel); - GL32.glUniform1i(ssaoShader.getUniformLocation("gDepthMap"), 0); + GL32.glUniform3fv(this.ssaoShaderUniforms.gKernelUniform, this.kernel); + GL32.glUniform1i(this.ssaoShaderUniforms.gDepthMapUniform, 0); GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6); - applyShader.bind(); + this.applyShader.bind(); GL32.glEnable(GL11.GL_BLEND); GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA); GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer()); GL32.glActiveTexture(GL32.GL_TEXTURE0); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, ssaoTexture); - GL32.glUniform1i(applyShader.getUniformLocation("gSSAOMap"), 0); + GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.ssaoTexture); + GL32.glUniform1i(this.applyShaderUniforms.gSSAOMapUniform, 0); GL32.glActiveTexture(GL32.GL_TEXTURE1); GL32.glBindTexture(GL32.GL_TEXTURE_2D, MC_RENDER.getDepthTextureId()); - GL32.glUniform1i(applyShader.getUniformLocation("gDepthMap"), 1); + GL32.glUniform1i(this.applyShaderUniforms.gDepthMapUniform, 1); GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6); @@ -206,8 +255,8 @@ public class SSAORenderer public void free() { - ssaoShader.free(); - applyShader.free(); + this.ssaoShader.free(); + this.applyShader.free(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java deleted file mode 100644 index ce357c140..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.seibel.distanthorizons.core.render.renderer.shaders; - -import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; -import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexAttribute; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.RenderUtil; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import org.lwjgl.opengl.GL32; - -public class SSAOShader extends AbstractShaderRenderer -{ - public static SSAOShader INSTANCE = new SSAOShader(); - - private static final int MAX_KERNEL_SIZE = 32; - private float[] kernel = new float[MAX_KERNEL_SIZE * 3]; - - - public SSAOShader() - { - super( - new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag", - "fragColor", new String[]{"vPosition"}), - new ShaderProgram("shaders/normal.vert", "shaders/ssao/apply-frag.frag", - "fragColor", new String[]{"vPosition"}) - ); - - } - @Override - void postInit() - { - // Generate kernel - kernel = genKernel(); - } - - @Override - void setVertexAttributes() - { - va.setVertexAttribute(0, 0, VertexAttribute.VertexPointer.addVec2Pointer(false)); - } - - @Override - void setShaderUniforms(float partialTicks) - { - Mat4f perspective = Mat4f.perspective( - (float) MC_RENDER.getFov(partialTicks), - MC_RENDER.getTargetFrameBufferViewportWidth() / (float) MC_RENDER.getTargetFrameBufferViewportHeight(), - RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks), - (float) ((RenderUtil.getFarClipPlaneDistanceInBlocks() + LodUtil.REGION_WIDTH) * Math.sqrt(2))); - - this.shader.setUniform(this.shader.getUniformLocation("gProj"), perspective); - this.shader.setUniform(this.shader.getUniformLocation("gSampleRad"), 3.0f); - this.shader.setUniform(this.shader.getUniformLocation("gFactor"), 0.8f); - this.shader.setUniform(this.shader.getUniformLocation("gPower"), 1.0f); - - GL32.glUniform3fv(this.shader.getUniformLocation("gKernel"), kernel); - GL32.glUniform1i(this.shader.getUniformLocation("gDepthMap"), 0); - } - - private static float[] genKernel() - { - float[] kernel = new float[MAX_KERNEL_SIZE * 3]; - for (int i = 0; i < MAX_KERNEL_SIZE; i++) - { - float sampleX = (float) (Math.random() * 2.0 - 1.0); - float sampleY = (float) (Math.random() * 2.0 - 1.0); - float sampleZ = (float) Math.random(); - - - // Normalize - float magnitude = (float) Math.sqrt(Math.pow(sampleX, 2) + Math.pow(sampleY, 2) + Math.pow(sampleZ, 2)); - sampleX /= magnitude; - sampleY /= magnitude; - sampleZ /= magnitude; - - float scale = i / (float) MAX_KERNEL_SIZE; - float interpolatedScale = (float) (0.1 + (scale * scale) * (0.9)); - - sampleX *= interpolatedScale; - sampleY *= interpolatedScale; - sampleZ *= interpolatedScale; - kernel[i * 3] = sampleX; - kernel[i * 3 + 1] = sampleY; - kernel[i * 3 + 2] = sampleZ; - } - return kernel; - } - -} diff --git a/core/src/main/resources/shaders/ssao/ao.frag b/core/src/main/resources/shaders/ssao/ao.frag index 9538f54c4..0e5501870 100644 --- a/core/src/main/resources/shaders/ssao/ao.frag +++ b/core/src/main/resources/shaders/ssao/ao.frag @@ -10,7 +10,7 @@ uniform float gFactor; uniform float gPower; uniform mat4 gProj; -const int MAX_KERNEL_SIZE = 32; +const int MAX_KERNEL_SIZE = 128; const float INV_MAX_KERNEL_SIZE_F = 1.0 / float(MAX_KERNEL_SIZE); const vec2 HALF_2 = vec2(0.5); uniform vec3 gKernel[MAX_KERNEL_SIZE]; @@ -55,7 +55,7 @@ void main() float rangeCheck = smoothstep(0.0, 1.0, gSampleRad / abs(viewPos.z - geometryDepth)); // the number added to the samplePos.z can be used to reduce noise in the SSAO application at the cost of reducing the overall affect - occlusion_factor += float(geometryDepth >= samplePos.z + 1.0) * rangeCheck; + occlusion_factor += float(geometryDepth >= samplePos.z + 0.1) * rangeCheck; }