This commit is contained in:
s809
2023-08-27 19:10:25 +05:00
30 changed files with 315 additions and 245 deletions
+1 -4
View File
@@ -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/
+10
View File
@@ -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')
}
}
@@ -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. <br><br>
*
* <strong>Q:</strong> Why should I use this class instead of just getting the API singleton I need? <br>
*
* <strong>A:</strong> 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. <br>
* This is just to you know that Javadocs are available and that you should use the API jar
* instead of the full mod jar. <br><br>
*
* 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; }
/**
* <strong>WARNING:</strong>
* All objects in this class will be null until after DH initializes for the first time. <br><br>
@@ -73,6 +98,7 @@ public class DhApi
}
// always available //
/**
@@ -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<T>
* 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. <br>
* 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. <br>
@@ -36,16 +38,20 @@ public interface IDhApiConfigValue<T>
*
* @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<T> onValueChangeFunc);
//void removeListener(Consumer<T> onValueChangeFunc); // not currently implemented
}
@@ -35,6 +35,8 @@ public interface IDhApiEventInjector extends IDependencyInjector<IDhApiEvent>
/**
* 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<IDhApiEvent>
* @param abstractEvent event type
* @param eventParameterObject event parameter
* @param <T> the parameter type taken by the event handlers.
* @param <U> the {@link IDhApiEvent}'s class
* @return if any of bound event handlers returned that this event should be canceled.
*/
<T, U extends IDhApiEvent<T>> boolean fireAllEvents(Class<U> abstractEvent, T eventParameterObject);
@@ -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 . <br>
* Higher (larger numerical) priorities override lower (smaller numerical) priorities. <br>
* 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; }
@@ -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. <br><br>
*
* 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);
@@ -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 <br>
* 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 <br>
* 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) <br>
* 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. <br>
* <strong>Note:</strong> these objects are minecraft version dependent and will change without notice!
* <strong>Note:</strong> these objects are minecraft version dependent and <i>will</i> change without notice!
* Please run your generator in game at least once to confirm the objects you are returning are correct. <br><br>
*
* Consumer expected inputs for each minecraft version (in order): <br>
* <strong>1.16</strong>, <strong>1.17</strong>, <strong>1.18</strong>, <strong>1.19</strong>, <strong>1.20</strong>: <br>
* - [net.minecraft.world.level.chunk.ChunkAccess] <br>
* - [net.minecraft.world.level.ServerLevel] or [net.minecraft.world.level.ClientLevel] <br>
*
* @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<Void> generateChunks(
int chunkPosMinX, int chunkPosMinZ,
@@ -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. <br> <br>
*
@@ -68,8 +70,18 @@ public class DhApiConfigValue<coreType, apiType> implements IDhApiConfigValue<ap
public boolean getCanBeOverrodeByApi() { return this.configEntry.getAllowApiOverride(); }
public apiType getDefaultValue() { return this.configConverter.convertToApiType(configEntry.getDefaultValue()); }
public apiType getDefaultValue() { return this.configConverter.convertToApiType(this.configEntry.getDefaultValue()); }
public apiType getMaxValue() { return this.configConverter.convertToApiType(this.configEntry.getMax()); }
public apiType getMinValue() { return this.configConverter.convertToApiType(this.configEntry.getMin()); }
public void addChangeListener(Consumer<apiType> onValueChangeFunc)
{
this.configEntry.addValueChangeListener((coreValue) ->
{
apiType apiValue = this.configConverter.convertToApiType(coreValue);
onValueChangeFunc.accept(apiValue);
});
}
}
@@ -172,6 +172,15 @@ public class ApiEventInjector extends DependencyInjector<IDhApiEvent> 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 <T> the event parameter type
* @return the event parameter wrapped in a {@link DhApiCancelableEventParam} or {@link DhApiEventParam}
*/
public static <T> DhApiEventParam<T> createEventParamWrapper(IDhApiEvent<T> event, T parameter)
{
return (event instanceof IDhApiCancelableEvent) ? new DhApiCancelableEventParam<>(parameter) : new DhApiEventParam<>(parameter);
@@ -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<T>
/** Is the value of this equal to another */
boolean equals(IConfigEntry<?> obj);
void addValueChangeListener(Consumer<T> onValueChangeFunc);
}
@@ -25,7 +25,7 @@ public interface IDependencyInjector<BindableType extends IBindable>
/**
* This does not return incomplete dependencies. <Br>
* Does not return incomplete dependencies. <Br>
* See {@link #get(Class, boolean) get(Class, boolean)} for full documentation.
*
* @see #get(Class, boolean)
@@ -7,12 +7,12 @@ public interface IOverrideInjector<BindableType extends IBindable>
{
/**
* All core overrides should have this priority. <Br>
* 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. */
@@ -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)
{
@@ -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<Boolean> ssao = new ConfigEntry.Builder<Boolean>()
@@ -200,7 +198,6 @@ public class Config
+ ETransparency.DISABLED + ": LODs will be opaque. \n"
+ "")
.setPerformance(EConfigEntryPerformance.MEDIUM)
.addListener(RenderCacheConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<EBlocksToAvoid> blocksToIgnore = new ConfigEntry.Builder<EBlocksToAvoid>()
@@ -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<Boolean> tintWithAvoidedBlocks = new ConfigEntry.Builder<Boolean>()
@@ -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<Double> saturationMultiplier = new ConfigEntry.Builder<Double>() // 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<Boolean> enableCaveCulling = new ConfigEntry.Builder<Boolean>()
@@ -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)
{
@@ -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<EMaxHorizontalResolution> horizontalResolutionChangeListener;
private final ConfigChangeListener<EVerticalQuality> verticalQualityChangeListener;
private final ConfigChangeListener<ETransparency> transparencyChangeListener;
private final ConfigChangeListener<EBlocksToAvoid> blocksToIgnoreChangeListener;
private final ConfigChangeListener<Boolean> tintWithAvoidedBlocksChangeListener;
private final ConfigChangeListener<Double> brightnessMultiplierChangeListener;
private final ConfigChangeListener<Double> saturationMultiplierChangeListener;
private final ConfigChangeListener<ELodShading> 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 */
@@ -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() {}
}
@@ -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<T> extends AbstractConfigType<T, ConfigEntry<T>> 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<T> onValueChangeFunc)
{
ConfigChangeListener<T> 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<T> onValueChangeFunc) { } // not currently implemented
public void removeListener(IConfigListener oldListener) { this.listenerList.remove(oldListener); }
public void clearListeners() { this.listenerList.clear(); }
@@ -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)
@@ -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);
@@ -14,7 +14,7 @@ public interface IWorldGenerationQueue extends Closeable
/** the largest numerical detail level */
byte largestDataDetail();
CompletableFuture<WorldGenResult> submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker);
CompletableFuture<WorldGenResult> submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker);
void cancelGenTasks(Iterable<DhSectionPos> positions);
/** @param targetPos the position that world generation should be centered around, generally this will be the player's position. */
@@ -41,9 +41,9 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
/** contains the positions that need to be generated */
//private final QuadTree<WorldGenTask> waitingTaskQuadTree;
private final ConcurrentHashMap<DhLodPos, WorldGenTask> waitingTasks = new ConcurrentHashMap<>();
private final ConcurrentHashMap<DhSectionPos, WorldGenTask> waitingTasks = new ConcurrentHashMap<>();
private final ConcurrentHashMap<DhLodPos, InProgressWorldGenTaskGroup> inProgressGenTasksByLodPos = new ConcurrentHashMap<>();
private final ConcurrentHashMap<DhSectionPos, InProgressWorldGenTaskGroup> 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<DhLodPos, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
private final Queue<DhLodPos> alreadyGeneratedPosQueue = new LinkedList<>();
private final HashMap<DhSectionPos, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
private final Queue<DhSectionPos> alreadyGeneratedPosQueue = new LinkedList<>();
private static RateLimitedThreadPoolExecutor worldGeneratorThreadPool;
private static ConfigChangeListener<Integer> configListener;
@@ -119,7 +119,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
// task handling //
//=================//
public CompletableFuture<WorldGenResult> submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker)
@Override
public CompletableFuture<WorldGenResult> 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<CompletableFuture<WorldGenResult>> 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<WorldGenResult> 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);
}
@@ -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<WorldGenResult> future;
public WorldGenTask(DhLodPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture<WorldGenResult> future)
public WorldGenTask(DhSectionPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture<WorldGenResult> future)
{
this.dataDetailLevel = dataDetail;
this.pos = pos;
@@ -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<WorldGenTask> worldGenTasks = new LinkedList<>();
public WorldGenTaskGroup(DhLodPos pos, byte dataDetail)
public WorldGenTaskGroup(DhSectionPos pos, byte dataDetail)
{
this.pos = pos;
this.dataDetail = dataDetail;
@@ -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()
@@ -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 :/
}
@@ -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;
@@ -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();
}
}
@@ -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;
}
}
+2 -2
View File
@@ -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;
}