diff --git a/build.gradle b/build.gradle index a76f47374..ddef385ef 100644 --- a/build.gradle +++ b/build.gradle @@ -294,11 +294,6 @@ subprojects { p -> // Netty implementation("io.netty:netty-buffer:${rootProject.netty_version}") - // Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing - forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") { - exclude group: "org.lwjgl", module: "lwjgl" // This module is imported by Minecraft so exclude it - } - //==========================// @@ -346,11 +341,7 @@ subprojects { p -> relocate "com.seibel.distanthorizons.common", "loaderCommon.${p.name}.com.seibel.distanthorizons.common" // Move the loader files to a different location } def librariesLocation = "DistantHorizons.libraries" - - // LWJGL - // Only ever shadow the dependencies we use otherwise some stuff would break when running on an external client - relocate "org.lwjgl.system.jawt", "${librariesLocation}.lwjgl.system.jawt" - + // Compression (LZ4) relocate "net.jpountz", "${librariesLocation}.jpountz" @@ -653,26 +644,30 @@ allprojects { p -> if (p == project(":core")) { OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem; - // Set the OS lwjgl is using to the current os + // Set the OS lwjgl is using to the current os project.ext.lwjglNatives = "natives-" + os.toFamilyName() - dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core - // Imports most of lwjgl's libraries (well, only the ones that we need) + dependencies { + // All of these dependencies are in Vanilla Minecraft, but we need to depend on them as we arent importing Minecraft in the core + + // Imports most of lwjgl's libraries implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") - // REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use + // REMEMBER: Don't shadow stuff here, these are just the libs that are included in Minecraft so that the core can use them implementation "org.lwjgl:lwjgl" implementation "org.lwjgl:lwjgl-assimp" implementation "org.lwjgl:lwjgl-glfw" - implementation "org.lwjgl:lwjgl-openal" - implementation "org.lwjgl:lwjgl-opengl" + // OpenGL is removed since DH now handles rendering in the "Common" project + // so we can use OpenGL for old MC versions and Blaze3D (IE Vulkan) for newer ones +// implementation "org.lwjgl:lwjgl-openal" +// implementation "org.lwjgl:lwjgl-opengl" implementation "org.lwjgl:lwjgl-stb" implementation "org.lwjgl:lwjgl-tinyfd" runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" +// runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" +// runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives" implementation "org.joml:joml:${rootProject.joml_version}" diff --git a/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java b/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java index 659a30c8c..6c5434815 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java @@ -1,8 +1,10 @@ package com.seibel.distanthorizons.common; +import com.mojang.brigadier.CommandDispatcher; +import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent; -//import com.seibel.distanthorizons.common.commands.CommandInitializer; +import com.seibel.distanthorizons.common.commands.CommandInitializer; import com.seibel.distanthorizons.common.wrappers.DependencySetup; import com.seibel.distanthorizons.common.wrappers.gui.DhDebugScreenEntry; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper; @@ -17,15 +19,15 @@ import com.seibel.distanthorizons.core.enums.MinecraftTextFormat; import com.seibel.distanthorizons.core.jar.ModJarInfo; import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.NativeDialogUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.ModInfo; -#if MC_VER <= MC_1_12_2 -#else -import com.mojang.brigadier.CommandDispatcher; import net.minecraft.commands.CommandSourceStack; -#endif import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; import com.seibel.distanthorizons.core.logging.DhLogger; @@ -41,13 +43,14 @@ public abstract class AbstractModInitializer { protected static final DhLogger LOGGER = new DhLoggerBuilder().build(); - //private CommandInitializer commandInitializer; + private CommandInitializer commandInitializer; //==================// // abstract methods // //==================// + //region protected abstract void createInitialSharedBindings(); protected abstract void createInitialClientBindings(); @@ -55,17 +58,20 @@ public abstract class AbstractModInitializer protected abstract IEventProxy createServerProxy(boolean isDedicated); protected abstract void initializeModCompat(); - //protected abstract void subscribeRegisterCommandsEvent(Consumer> eventHandler); + protected abstract void subscribeRegisterCommandsEvent(Consumer> eventHandler); protected abstract void subscribeClientStartedEvent(Runnable eventHandler); protected abstract void subscribeServerStartingEvent(Consumer eventHandler); protected abstract void runDelayedSetup(); + //endregion + //===================// // initialize events // //===================// + //region public void onInitializeClient() { @@ -96,6 +102,7 @@ public abstract class AbstractModInitializer #endif this.subscribeClientStartedEvent(this::postInit); + this.subscribeClientStartedEvent(this::postClientInit); } public void onInitializeServer() @@ -118,8 +125,8 @@ public abstract class AbstractModInitializer this.initializeModCompat(); LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers..."); - //this.commandInitializer = new CommandInitializer(); - //this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer.initCommands(dispatcher); }); + this.commandInitializer = new CommandInitializer(); + this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer.initCommands(dispatcher); }); this.subscribeServerStartingEvent(server -> { @@ -127,19 +134,22 @@ public abstract class AbstractModInitializer this.initConfig(); this.postInit(); - //this.commandInitializer.onServerReady(); + this.commandInitializer.onServerReady(); this.checkForUpdates(); - LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server.#if MC_VER <= MC_1_12_2 getDataDirectory() #else getServerDirectory() #endif); + LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server.getServerDirectory()); }); } + //endregion + //===========================// // inner initializer methods // //===========================// + //region private void startup() { @@ -212,11 +222,16 @@ public abstract class AbstractModInitializer ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); } + private void postClientInit() { DependencySetup.setRenderingApiBindings(); } + + //endregion + //==================================// // mod partial compatibility checks // //==================================// + //region /** * Some mods will work with a few tweaks @@ -234,6 +249,7 @@ public abstract class AbstractModInitializer // Alex's caves + //region if (modChecker.isModLoaded("alexscaves")) { // There've been a few reports about this mod breaking DH at a few different points in time @@ -250,8 +266,10 @@ public abstract class AbstractModInitializer LOGGER.warn(startingString + "[Alex's Caves] may require some config changes in order to render Distant Horizons correctly."); } + //endregion // William Wythers' Overhauled Overworld (WWOO) + //region if (modChecker.isModLoaded("wwoo")) { // WWOO has a bug with it's world gen that can't be fixed by DH or WWOO @@ -272,8 +290,11 @@ public abstract class AbstractModInitializer LOGGER.warn(startingString + "[WWOO] "+ wwooWarning); } + //endregion + + // Chunky // + //region - // Chunky boolean chunkyPresent = false; try { @@ -303,17 +324,56 @@ public abstract class AbstractModInitializer LOGGER.warn(startingString + "[Chunky] "+ chunkyWarning); } + //endregion + + // iris // + //region + + IIrisAccessor iris = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class); + if (iris != null) + { + // get the currently selected rendering API + EDhApiRenderApi renderApi = Config.Client.Advanced.Graphics.Experimental.renderingApi.get(); + if (renderApi == EDhApiRenderApi.AUTO) + { + IVersionConstants versionConstants = SingletonInjector.INSTANCE.get(IVersionConstants.class); + renderApi = versionConstants.getDefaultRenderingApi(); + } + + // Iris only supports nataive OpenGL + if (renderApi != EDhApiRenderApi.OPEN_GL) + { + String irisUnsupportedMessage = "Iris doesn't support DH when using the ["+EDhApiRenderApi.BLAZE_3D+"] rendering API, please change it to ["+EDhApiRenderApi.OPEN_GL+"] in DH's config file."; + LOGGER.fatal(irisUnsupportedMessage); + NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, irisUnsupportedMessage, "ok", "error"); + + IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + String errorMessage = "loading Distant Horizons. "+irisUnsupportedMessage; + String exceptionError = "Distant Horizons conditional mod config Exception"; + mc.crashMinecraft(errorMessage, new Exception(exceptionError)); + } + } + + //endregion + } + //endregion + //================// // helper classes // //================// + //region public interface IEventProxy { void registerEvents(); } + //endregion + + + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDebugWireframeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDebugWireframeRenderer.java new file mode 100644 index 000000000..37d46b48b --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDebugWireframeRenderer.java @@ -0,0 +1,330 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze; + +#if MC_VER <= MC_1_21_10 +public class BlazeDebugWireframeRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import net.minecraft.resources.Identifier; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Handles rendering the wireframe particles + * that are used for seeing what the system's doing. + */ +public class BlazeDebugWireframeRenderer extends AbstractDebugWireframeRenderer +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + public static BlazeDebugWireframeRenderer INSTANCE = new BlazeDebugWireframeRenderer(); + + + + /** A box from 0,0,0 to 1,1,1 */ + private static final float[] BOX_VERTICES = { + //region + // Pos x y z + 0, 0, 0, + 1, 0, 0, + 1, 1, 0, + 0, 1, 0, + 0, 0, 1, + 1, 0, 1, + 1, 1, 1, + 0, 1, 1, + //endregion + }; + + private static final int[] BOX_OUTLINE_INDICES = { + //region + 0, 1, + 1, 2, + 2, 3, + 3, 0, + + 4, 5, + 5, 6, + 6, 7, + 7, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7, + //endregion + }; + + + + + // rendering setup + private boolean init = false; + + private RenderPipeline pipeline; + + private GpuBuffer boxVertexBuffer; + private GpuBuffer boxIndexBuffer; + + private GpuBuffer uniformBuffer; + + + + //=============// + // constructor // + //=============// + //region + + public BlazeDebugWireframeRenderer() { } + + public void init() + { + if (this.init) + { + return; + } + this.init = true; + + this.createPipelines(); + this.createBuffers(); + + } + private void createPipelines() + { + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS) + .build(); + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.LESS_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withoutBlend(); + pipelineBuilder.withPolygonMode(PolygonMode.WIREFRAME); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:debug_wireframe_renderer")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "debug/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "debug/blaze/frag")); + + pipelineBuilder.withUniform("uniformBlock", UniformType.UNIFORM_BUFFER); + + pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.DEBUG_LINES); + } + this.pipeline = pipelineBuilder.build(); + + } + private void createBuffers() + { + GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + + // box vertices + ByteBuffer boxVerticesBuffer = MemoryUtil.memAlloc(BOX_VERTICES.length * Float.BYTES); + boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); + boxVerticesBuffer.rewind(); + MemoryUtil.memFree(boxVerticesBuffer); + + // upload vertex data + { + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX; + int size = BOX_VERTICES.length * Float.BYTES; + this.boxVertexBuffer = GPU_DEVICE.createBuffer(() -> "distantHorizons:McDebugWireframeBox", usage, size); + + { + int length = BOX_VERTICES.length * Float.BYTES; + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.boxVertexBuffer, /*offset*/ 0, length); + + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES); + byteBuffer.order(ByteOrder.nativeOrder()); + byteBuffer.asFloatBuffer().put(BOX_VERTICES); + byteBuffer.rewind(); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, byteBuffer); + } + } + + // box vertex indexes + { + ByteBuffer buffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES); + buffer.order(ByteOrder.nativeOrder()); + buffer.asIntBuffer().put(BOX_OUTLINE_INDICES); + buffer.rewind(); + + + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX + | GpuBuffer.USAGE_INDEX + | GpuBuffer.USAGE_UNIFORM; + this.boxIndexBuffer = GPU_DEVICE.createBuffer(() -> "DH Debug Index Buffer", usage, buffer.capacity()); + + int offset = 0; + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.boxIndexBuffer, offset, buffer.capacity()); + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + } + + //endregion + + + + //===========// + // rendering // + //===========// + //region + + @Override + public void renderBox(Box box) + { + this.init(); + + if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty() + || BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()) + { + return; + } + + // shouldn't happen, but just in case + if (box == null) + { + return; + } + + // delayed getters since this class may be initialized before + // the GPU device has been set + GpuDevice gpuDevice = RenderSystem.getDevice(); + CommandEncoder commandEncoder = gpuDevice.createCommandEncoder(); + + + + // uniforms + { + int uniformBufferSize = new Std140SizeCalculator() + .putMat4f() // uTransform + .putVec4() // uColor + .get(); + + + // create data // + + Vec3d camPos = MC_RENDER.getCameraExactPosition(); + Vec3f camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z); + + Mat4f boxTransform = Mat4f.createTranslateMatrix( + box.minPos.x - camPosFloatThisFrame.x, + box.minPos.y - camPosFloatThisFrame.y, + box.minPos.z - camPosFloatThisFrame.z); + boxTransform.multiply(Mat4f.createScaleMatrix( + box.maxPos.x - box.minPos.x, + box.maxPos.y - box.minPos.y, + box.maxPos.z - box.minPos.z)); + + Mat4f transformMatrix = this.dhMvmProjMatrixThisFrame.copy(); + transformMatrix.multiply(boxTransform); + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putMat4f(transformMatrix.createJomlMatrix()) // uTransform + .putVec4( + box.color.getRed() / 255.0f, + box.color.getGreen() / 255.0f, + box.color.getBlue() / 255.0f, + box.color.getAlpha() / 255.0f) // uColor + .get() + ; + + this.uniformBuffer = BlazeUniformUtil.createBuffer("uniformBlock", uniformBufferSize, this.uniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.uniformBuffer, 0, uniformBufferSize); + + commandEncoder.writeToBuffer(bufferSlice, buffer); + } + + + + // render // + + try (RenderPass renderPass = commandEncoder.createRenderPass( + this::getRenderPassName, + BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + // Bind instance data // + renderPass.setUniform("uniformBlock", this.uniformBuffer); + + renderPass.setPipeline(this.pipeline); + renderPass.setIndexBuffer(this.boxIndexBuffer, VertexFormat.IndexType.INT); + + renderPass.setVertexBuffer(0, this.boxVertexBuffer); + + renderPass.drawIndexed( + /*indexStart*/ 0, + /*firstIndex*/0, + /*indexCount*/BOX_OUTLINE_INDICES.length, + /*instanceCount*/1); + } + } + private String getRenderPassName() { return "distantHorizons:McDebugRenderer"; } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhGenericObjectRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhGenericObjectRenderer.java new file mode 100644 index 000000000..867dad491 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhGenericObjectRenderer.java @@ -0,0 +1,618 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhGenericObjectRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeGenericObjectRenderEvent; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeGenericRenderCleanupEvent; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeGenericRenderSetupEvent; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.common.render.blaze.objects.BlazeGenericObjectVertexContainer; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; +import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import com.seibel.distanthorizons.coreapi.ModInfo; +import net.minecraft.resources.Identifier; + +import java.awt.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Handles rendering generic groups of {@link DhApiRenderableBox}. + * + * @see IDhApiCustomRenderRegister + * @see DhApiRenderableBox + */ +public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + private static final DhApiRenderableBoxGroupShading DEFAULT_SHADING = DhApiRenderableBoxGroupShading.getUnshaded(); + + /** + * Can be used to troubleshoot the renderer. + * If enabled several debug objects will render around (0,150,0). + */ + public static final boolean RENDER_DEBUG_OBJECTS = false; + + private final ConcurrentHashMap boxGroupById = new ConcurrentHashMap<>(); + + + // rendering setup + private boolean init = false; + + private VertexFormat vertexFormat; + + private RenderPipeline pipeline; + + private GpuBuffer vertUniformBuffer; + + + + //=============// + // constructor // + //=============// + //region + + public BlazeDhGenericObjectRenderer() { } + + public void init() + { + if (this.init) + { + return; + } + this.init = true; + + this.vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS) + .add("aColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR) + .add("aMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL) + .build(); + + this.createPipelines(); + + if (RENDER_DEBUG_OBJECTS) + { + this.addGenericDebugObjects(); + } + } + private void createPipelines() + { + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(true); + pipelineBuilder.withDepthWrite(true); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.LESS_DEPTH_TEST); + pipelineBuilder.withBlend(BlendFunction.TRANSLUCENT); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:generic")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "generic/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "generic/blaze/frag")); + + pipelineBuilder.withSampler("uLightMap"); + + pipelineBuilder.withUniform("vertUniformBlock", UniformType.UNIFORM_BUFFER); + + pipelineBuilder.withVertexFormat(this.vertexFormat, VertexFormat.Mode.TRIANGLES); + this.pipeline = pipelineBuilder.build(); + } + } + private void addGenericDebugObjects() + { + GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE; + + + // single giant box + IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox( + ModInfo.NAME + ":CyanChunkBox", + new DhApiRenderableBox( + new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16), + new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125), + EDhApiBlockMaterial.WATER) + ); + singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.add(singleGiantBoxGroup); + + + // single slender box + IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox( + ModInfo.NAME + ":GreenBeacon", + new DhApiRenderableBox( + new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32), + new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125), + EDhApiBlockMaterial.ILLUMINATED) + ); + singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.add(singleTallBoxGroup); + + + // absolute box group + ArrayList absBoxList = new ArrayList<>(); + for (int i = 0; i < 18; i++) + { + absBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25), + new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()), + EDhApiBlockMaterial.LAVA + ) + ); + } + IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList); + this.add(absolutePosBoxGroup); + + + // relative box group + ArrayList relBoxList = new ArrayList<>(); + for (int i = 0; i < 8; i+=2) + { + relBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1), + new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()), + EDhApiBlockMaterial.METAL + ) + ); + } + IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup( + ModInfo.NAME + ":MovingMagentaGroup", + new DhApiVec3d(24, 140, 24), + relBoxList); + relativePosBoxGroup.setPreRenderFunc((event) -> + { + DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos(); + pos.x += event.partialTicks / 2; + pos.x %= 32; + relativePosBoxGroup.setOriginBlockPos(pos); + }); + this.add(relativePosBoxGroup); + + + // massive relative box group + ArrayList massRelBoxList = new ArrayList<>(); + for (int x = 0; x < 50*2; x+=2) + { + for (int z = 0; z < 50*2; z+=2) + { + massRelBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z), + new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()), + EDhApiBlockMaterial.TERRACOTTA + ) + ); + } + } + IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup( + ModInfo.NAME + ":MassRedGroup", + new DhApiVec3d(-25, 140, 0), + massRelBoxList); + massRelativePosBoxGroup.setPreRenderFunc((event) -> + { + DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos(); + blockPos.y += event.partialTicks / 4; + if (blockPos.y > 150f) + { + blockPos.y = 140f; + + Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED; + massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; }); + massRelativePosBoxGroup.triggerBoxChange(); + } + + massRelativePosBoxGroup.setOriginBlockPos(blockPos); + }); + this.add(massRelativePosBoxGroup); + } + + //endregion + + + + //==============// + // registration // + //==============// + //region + + @Override + public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException + { + if (!(iBoxGroup instanceof RenderableBoxGroup)) + { + throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"]."); + } + RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup; + + + long id = boxGroup.getId(); + if (this.boxGroupById.containsKey(id)) + { + throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present."); + } + + this.boxGroupById.put(id, boxGroup); + } + + @Override + public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); } + + public void clear() { this.boxGroupById.clear(); } + + //endregion + + + + //===========// + // rendering // + //===========// + //region + + /** + * @param renderingWithSsao + * if true that means this render call is happening before the SSAO pass + * and any objects rendered in this pass will have SSAO applied to them. + */ + @Override + public void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao) + { + //==============// + // render setup // + //==============// + //#region + + profiler.push("setup"); + + this.init(); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); + + Vec3d camPos = MC_RENDER.getCameraExactPosition(); + + //#endregion + + if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty() + || BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()) + { + return; + } + + + + //===========// + // rendering // + //===========// + //#region + + Collection boxList = this.boxGroupById.values(); + for (RenderableBoxGroup boxGroup : boxList) + { + // validation // + + // shouldn't happen, but just in case + if (boxGroup == null) + { + continue; + } + + // skip boxes that shouldn't render this pass + if (boxGroup.ssaoEnabled != renderingWithSsao) + { + continue; + } + + profiler.popPush("render prep"); + boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired + + // ignore inactive groups + if (!boxGroup.active) + { + continue; + } + + // allow API users to cancel this object's rendering + boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); + if (cancelRendering) + { + continue; + } + + // update instanced data if needed + { + boxGroup.tryUpdateInstancedDataAsync(); + + // skip groups that haven't been uploaded yet + if (boxGroup.vertexBufferContainer.getState() != IDhGenericObjectVertexBufferContainer.EState.RENDER) + { + continue; + } + } + + + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DEFAULT_SHADING; + } + + // uniforms + { + int uniformBufferSize = new Std140SizeCalculator() + .putIVec3() // uOffsetChunk + .putVec3() // uOffsetSubChunk + .putIVec3() // uCameraPosChunk + .putVec3() // uCameraPosSubChunk + + .putVec3() // aTranslateChunk + .putVec3() // aTranslateSubChunk + + .putMat4f() // uProjectionMvm + .putInt() // uSkyLight + .putInt() // uBlockLight + + .putFloat() // uNorthShading + .putFloat() // uSouthShading + .putFloat() // uEastShading + .putFloat() // uWestShading + .putFloat() // uTopShading + .putFloat() // uBottomShading + .get(); + + + // create data // + + Mat4f projectionMvmMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); + projectionMvmMatrix.multiply(renderEventParam.dhModelViewMatrix); + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putIVec3( + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + ) // uOffsetChunk + .putVec3( + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + ) // uOffsetSubChunk + .putIVec3( + LodUtil.getChunkPosFromDouble(camPos.x), + LodUtil.getChunkPosFromDouble(camPos.y), + LodUtil.getChunkPosFromDouble(camPos.z) + ) // uCameraPosChunk + .putVec3( + LodUtil.getSubChunkPosFromDouble(camPos.x), + LodUtil.getSubChunkPosFromDouble(camPos.y), + LodUtil.getSubChunkPosFromDouble(camPos.z) + ) // uCameraPosSubChunk + + .putMat4f(projectionMvmMatrix.createJomlMatrix()) // uProjectionMvm + .putInt(boxGroup.getSkyLight()) // uSkyLight + .putInt(boxGroup.getBlockLight()) // uBlockLight + + .putFloat(shading.north) + .putFloat(shading.south) + .putFloat(shading.east) + .putFloat(shading.west) + .putFloat(shading.top) + .putFloat(shading.bottom) + + .get() + ; + + this.vertUniformBuffer = BlazeUniformUtil.createBuffer("vertUniformBlock", uniformBufferSize, this.vertUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + + + + // render // + + profiler.popPush("rendering"); + profiler.push(boxGroup.getResourceLocationNamespace()); + profiler.push(boxGroup.getResourceLocationPath()); + + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + this.renderBoxGroupInstanced(renderPass, renderEventParam, boxGroup, camPos, profiler); + } + + profiler.pop(); // resource path + profiler.pop(); // resource namespace + + boxGroup.postRender(renderEventParam); + } + + //#endregion + + + + //==========// + // clean up // + //==========// + //region + + profiler.popPush("cleanup"); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); + + profiler.pop(); + + //endregion + } + private String getRenderPassName() { return "distantHorizons:McGenericObjectRenderer"; } + + //endregion + + + + //=====================// + // instanced rendering // + //=====================// + //region + + private void renderBoxGroupInstanced( + RenderPass renderPass, RenderParams renderEventParam, + RenderableBoxGroup boxGroup, Vec3d camPos, + IProfilerWrapper profiler) + { + // update instance data // + + profiler.push("vertex setup"); + + BlazeGenericObjectVertexContainer container = (BlazeGenericObjectVertexContainer) boxGroup.vertexBufferContainer; + + LightMapWrapper lightMapWrapper = (LightMapWrapper) renderEventParam.lightmap; + BlazeTextureViewWrapper lightmapTextureViewWrapper = lightMapWrapper.getTextureViewWrapper(); + renderPass.bindTexture("uLightMap", lightmapTextureViewWrapper.textureView, lightmapTextureViewWrapper.textureSampler); + + + + // Bind instance data // + profiler.popPush("binding"); + + + renderPass.setUniform("vertUniformBlock", this.vertUniformBuffer); + + // set pipeline + renderPass.setPipeline(this.pipeline); + renderPass.setIndexBuffer(container.indexGpuBuffer, VertexFormat.IndexType.INT); + + renderPass.setVertexBuffer(0, container.vboGpuBuffer); + + // Draw instanced + profiler.popPush("render"); + if (container.uploadedBoxCount > 0) + { + renderPass.drawIndexed( + /*indexStart*/ 0, + /*firstIndex*/0, + /*indexCount*/container.uploadedBoxCount * 24, // TODO? + /*instanceCount*/1); + + } + + profiler.pop(); + } + + //endregion + + + + //=========// + // F3 menu // + //=========// + //region + + public String getVboRenderDebugMenuString() + { + // get counts + int totalGroupCount = this.boxGroupById.size(); + int totalBoxCount = 0; + + int activeGroupCount = 0; + int activeBoxCount = 0; + + for (long key : this.boxGroupById.keySet()) + { + RenderableBoxGroup renderGroup = this.boxGroupById.get(key); + if (renderGroup.active) + { + activeGroupCount++; + activeBoxCount += renderGroup.size(); + } + totalBoxCount += renderGroup.size(); + } + + + return "Generic Obj #: " + F3Screen.NUMBER_FORMAT.format(activeGroupCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalGroupCount) + ", " + + "Cube #: " + F3Screen.NUMBER_FORMAT.format(activeBoxCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalBoxCount); + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhMetaRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhMetaRenderer.java new file mode 100644 index 000000000..e71e91dab --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhMetaRenderer.java @@ -0,0 +1,94 @@ +package com.seibel.distanthorizons.common.render.blaze; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhMetaRenderer {} + +#else + +import com.mojang.blaze3d.textures.GpuTexture; +import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer; +import net.minecraft.client.Minecraft; + +public class BlazeDhMetaRenderer implements IDhMetaRenderer +{ + public static final BlazeDhMetaRenderer INSTANCE = new BlazeDhMetaRenderer(); + + + private BlazeDhApplyRenderer applyRenderer; + + public final BlazeTextureWrapper dhDepthTextureWrapper = BlazeTextureWrapper.createDepth("DhDepthTexture"); + public final BlazeTextureWrapper dhColorTextureWrapper = BlazeTextureWrapper.createColor("DhColorTexture"); + + + + //=============// + // constructor // + //=============// + //region + + private BlazeDhMetaRenderer() + { + this.applyRenderer = new BlazeDhApplyRenderer( + "dh_apply_to_mc", + null, + "apply/blaze/vert", "apply/blaze/frag" + ); + } + + //endregion + + + + //=================// + // pre/post render // + //=================// + //region + + @Override + public void runRenderPassSetup(RenderParams renderParams) + { + // textures + this.dhDepthTextureWrapper.tryCreateOrResize(); + this.dhColorTextureWrapper.tryCreateOrResize(); + } + + @Override + public void runRenderPassCleanup(RenderParams renderParams) {} + + @Override + public void applyToMcTexture(RenderParams renderParams) + { + GpuTexture mcColorTexture = Minecraft.getInstance().getMainRenderTarget().getColorTexture(); + this.applyRenderer.render(this.dhColorTextureWrapper.texture, this.dhDepthTextureWrapper.texture, mcColorTexture); + } + + //endregion + + + + //================// + // clear textures // + //================// + //region + + @Override + public void clearDhDepthAndColorTextures(RenderParams renderParams) + { + // TODO use for clear color + //IMinecraftRenderWrapper r; + //r.getSkyColor() + + this.dhDepthTextureWrapper.clearDepth(1.0f); + this.dhColorTextureWrapper.clearColor(ColorUtil.argbToInt(1, 1, 1, 1)); + } + + //endregion + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhRenderApiDefinition.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhRenderApiDefinition.java new file mode 100644 index 000000000..4c40669a4 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhRenderApiDefinition.java @@ -0,0 +1,71 @@ +package com.seibel.distanthorizons.common.render.blaze; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhRenderApiDefinition {} + +#else + +import com.seibel.distanthorizons.common.render.blaze.objects.BlazeGenericObjectVertexContainer; +import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeDhFarFadeRenderer; +import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeDhFogRenderer; +import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeDhSsaoRenderer; +import com.seibel.distanthorizons.common.render.blaze.postProcessing.BlazeVanillaFadeRenderer; +import com.seibel.distanthorizons.common.render.blaze.test.BlazeDhTestTriangleRenderer; +import com.seibel.distanthorizons.common.render.blaze.wrappers.buffer.BlazeVertexBufferWrapper; +import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeLodUniformBufferWrapper; +import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*; + +public class BlazeDhRenderApiDefinition extends AbstractDhRenderApiDefinition +{ + //=========// + // getters // + //=========// + //region + + public String getApiName() { return "Blaze3D"; } + + //endregion + + + + //============// + // singletons // + //============// + //region + + @Override public IDhMetaRenderer getMetaRenderer() { return BlazeDhMetaRenderer.INSTANCE; } + @Override public IDhTerrainRenderer getTerrainRenderer() { return BlazeDhTerrainRenderer.INSTANCE; } + @Override public IDhSsaoRenderer getSsaoRenderer() { return BlazeDhSsaoRenderer.INSTANCE; } + @Override public IDhFogRenderer getFogRenderer() { return BlazeDhFogRenderer.INSTANCE; } + @Override public IDhFarFadeRenderer getFarFadeRenderer() { return BlazeDhFarFadeRenderer.INSTANCE; } + @Override public AbstractDebugWireframeRenderer getDebugWireframeRenderer() { return BlazeDebugWireframeRenderer.INSTANCE; } + + @Override public IDhVanillaFadeRenderer getVanillaFadeRenderer() { return BlazeVanillaFadeRenderer.INSTANCE; } + @Override public IDhTestTriangleRenderer getTestTriangleRenderer() { return BlazeDhTestTriangleRenderer.INSTANCE; } + + //endregion + + + + //===========// + // factories // + //===========// + //region + + @Override public IDhGenericRenderer createGenericRenderer() { return new BlazeDhGenericObjectRenderer(); } + + @Override public IVertexBufferWrapper createVboWrapper(String name) { return new BlazeVertexBufferWrapper(name); } + @Override public ILodContainerUniformBufferWrapper createLodContainerUniformWrapper() { return new BlazeLodUniformBufferWrapper(); } + @Override public IDhGenericObjectVertexBufferContainer createGenericVboContainer() { return new BlazeGenericObjectVertexContainer(); } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhTerrainRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhTerrainRenderer.java new file mode 100644 index 000000000..27513a919 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhTerrainRenderer.java @@ -0,0 +1,394 @@ +package com.seibel.distanthorizons.common.render.blaze; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhTerrainRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeBufferRenderEvent; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; +import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeLodUniformBufferWrapper; +import com.seibel.distanthorizons.common.render.blaze.wrappers.buffer.BlazeVertexBufferWrapper; +import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GlQuadElementBuffer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.objects.SortedArraySet; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import net.minecraft.resources.Identifier; +import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** Renders rendering DH's LOD terrain. */ +public class BlazeDhTerrainRenderer implements IDhTerrainRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + public static final BlazeDhTerrainRenderer INSTANCE = new BlazeDhTerrainRenderer(); + + + private RenderPipeline opaquePipeline; + private RenderPipeline transparentPipeline; + private boolean init = false; + + private GpuBuffer indexBuffer; + + private GpuBuffer fragUniformBuffer; + private GpuBuffer vertSharedUniformBuffer; + + + + //=============// + // constructor // + //=============// + //region + + private BlazeDhTerrainRenderer() { } + + private void tryInit() + { + if (this.init) + { + return; + } + this.init = true; // todo only set when succeeded (in case of exception) + + + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.SHORT_XYZ_POS) + .add("meta", BlazeDhVertexFormatUtil.META) + .add("vColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR) + .add("irisMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL) + .add("irisNormal", BlazeDhVertexFormatUtil.IRIS_NORMAL) + .add("paddingTwo", BlazeDhVertexFormatUtil.BYTE_PAD) + .add("paddingThree", BlazeDhVertexFormatUtil.BYTE_PAD) // padding is to make sure the format is a multiple of 4 + .build(); + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(true); + pipelineBuilder.withDepthWrite(true); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.LESS_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:lod_render")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "lod/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "lod/blaze/frag")); + + pipelineBuilder.withSampler("uLightMap"); + + pipelineBuilder.withUniform("vertUniqueUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniform("vertSharedUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + + pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.TRIANGLES); + } + + // opaque + { + pipelineBuilder.withoutBlend(); + this.opaquePipeline = pipelineBuilder.build(); + } + + // transparent + { + pipelineBuilder.withBlend(BlendFunction.TRANSLUCENT); + this.transparentPipeline = pipelineBuilder.build(); + } + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render( + RenderParams renderEventParam, + boolean opaquePass, + SortedArraySet bufferContainers, + IProfilerWrapper profiler) + { + this.tryInit(); + + + profiler.push("vert unique uniforms"); + { + // create data // + + for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) + { + LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); + bufferContainer.uniformContainer.tryUpload(); + } + } + + profiler.popPush("vert share uniforms"); + { + Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); + combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); + + float earthCurveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get(); + if (earthCurveRatio < -1.0f || earthCurveRatio > 1.0f) + { + earthCurveRatio = /*6371KM*/ 6371000.0f / earthCurveRatio; + } + else + { + // disable curvature if the config value is between -1 and 1 + earthCurveRatio = 0.0f; + } + + + // upload data // + + int uniformBufferSize = new Std140SizeCalculator() + .putInt() // uIsWhiteWorld + + .putFloat() // uWorldYOffset + .putFloat() // uMircoOffset + .putFloat() // uEarthRadius + + .putVec3() // uCameraPos + .putMat4f() // uCombinedMatrix + .get(); + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + Std140Builder.intoBuffer(buffer) + .putInt(0) // uIsWhiteWorld + + .putFloat((float) renderEventParam.worldYOffset) // uWorldYOffset + .putFloat(0.01f) // uMircoOffset // 0.01 block offset + .putFloat(earthCurveRatio) // uEarthRadius + + .putVec3( + (float)renderEventParam.exactCameraPosition.x, + (float)renderEventParam.exactCameraPosition.y, + (float)renderEventParam.exactCameraPosition.z) // uCameraPos + .putMat4f(combinedMatrix.createJomlMatrix()) // uCombinedMatrix + .get(); + + this.vertSharedUniformBuffer = BlazeUniformUtil.createBuffer("vertSharedUniformBlock", uniformBufferSize, this.vertSharedUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertSharedUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + profiler.popPush("set frag uniforms"); + { + int uniformBufferSize = new Std140SizeCalculator() + .putFloat() // uClipDistance + .putFloat() // uNoiseIntensity + .putInt() // uNoiseSteps + .putInt() // uNoiseDropoff + .putInt() // uDitherDhRendering + .putInt() // uNoiseEnabled + .get(); + + + // create data // + + float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks(); + if (!Config.Client.Advanced.Debugging.lodOnlyMode.get()) + { + // this added value prevents the near clip plane and discard circle from touching, which looks bad + dhNearClipDistance += 16f; + } + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putFloat(dhNearClipDistance) // uClipDistance + .putFloat(Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get()) // uNoiseIntensity + .putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get()) // uNoiseSteps + .putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get()) // uNoiseDropoff + .putInt(Config.Client.Advanced.Graphics.Quality.ditherDhFade.get() ? 1 : 0) // uDitherDhRendering + .putInt(Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get() ? 1 : 0) // uNoiseEnabled + .get() + ; + + this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + // create index buffer + { + if (this.indexBuffer == null) + { + ByteBuffer buffer = MemoryUtil.memAlloc(LodQuadBuilder.getMaxBufferByteSize() * GLEnums.getTypeSize(GL32.GL_UNSIGNED_INT) * 6); + GlQuadElementBuffer.buildBuffer(LodQuadBuilder.getMaxBufferByteSize(), buffer, GL32.GL_UNSIGNED_INT); + + + // create buffer if needed + if (this.indexBuffer == null + || this.indexBuffer.size() < buffer.capacity()) + { + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX + | GpuBuffer.USAGE_INDEX + | GpuBuffer.USAGE_UNIFORM; + this.indexBuffer = GPU_DEVICE.createBuffer(this::getIndexBufferName, usage, buffer.capacity()); + } + + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.indexBuffer, /*offset*/ 0, buffer.capacity()); + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + } + + + + // render pass setup + { + profiler.popPush("setup"); + + // create a render pass + OptionalInt optionalClearColorAsInt = OptionalInt.empty(); + OptionalDouble optionalDepthValueAsDouble = OptionalDouble.empty(); + + try(RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, + optionalClearColorAsInt, + BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, optionalDepthValueAsDouble) + ) + { + // bind MC Lightmap + //renderPass.bindTexture("uLightMap", this.mcLightTextureViewWrapper.textureView, this.mcLightTextureViewWrapper.textureSampler); + LightMapWrapper lightMapWrapper = (LightMapWrapper) renderEventParam.lightmap; + BlazeTextureViewWrapper lightmapTextureViewWrapper = lightMapWrapper.getTextureViewWrapper(); + renderPass.bindTexture("uLightMap", lightmapTextureViewWrapper.textureView, lightmapTextureViewWrapper.textureSampler); + + // set pipeline + renderPass.setPipeline(opaquePass ? this.opaquePipeline : this.transparentPipeline); + renderPass.setIndexBuffer(this.indexBuffer, VertexFormat.IndexType.INT); + + // shared uniforms + renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer); + renderPass.setUniform("vertSharedUniformBlock", this.vertSharedUniformBuffer); + + + + for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) + { + profiler.popPush("binding"); + + LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); + BlazeLodUniformBufferWrapper uniformWrapper = (BlazeLodUniformBufferWrapper)bufferContainer.uniformContainer; + + boolean columnBuilderDebugEnabled = Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugEnable.get(); + if (columnBuilderDebugEnabled) + { + if (DhSectionPos.getDetailLevel(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugDetailLevel.get() + && DhSectionPos.getX(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugXPos.get() + && DhSectionPos.getZ(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugZPos.get()) + { + int breakpoint = 0; + } + else + { + continue; + } + } + + renderPass.setUniform("vertUniqueUniformBlock", uniformWrapper.gpuBuffer); + + + + profiler.popPush("rendering"); + + // render each buffer + IVertexBufferWrapper[] bufferWrapperList = opaquePass ? bufferContainer.vbos : bufferContainer.vbosTransparent; + for (int i = 0; i < bufferWrapperList.length; i++) + { + BlazeVertexBufferWrapper bufferWrapper = (BlazeVertexBufferWrapper) bufferWrapperList[i]; + if (!bufferWrapper.uploaded + || bufferWrapper.vertexCount == 0) + { + continue; + } + + // fire render event + { + Vec3d camPos = renderEventParam.exactCameraPosition; + Vec3f modelPos = new Vec3f( + (float) (bufferContainer.minCornerBlockPos.getX() - camPos.x), + (float) (bufferContainer.minCornerBlockPos.getY() - camPos.y), + (float) (bufferContainer.minCornerBlockPos.getZ() - camPos.z)); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); + } + + renderPass.setVertexBuffer(0, bufferWrapper.vboGpuBuffer); // vertex buffer can only be "0" lol + + if (!bufferWrapper.vboGpuBuffer.isClosed()) + { + renderPass.drawIndexed( + /*indexStart*/ 0, + /*firstIndex*/0, + /*indexCount*/bufferWrapper.indexCount, + /*instanceCount*/1); + } + } + } + + } + } + + profiler.pop(); + } + private String getIndexBufferName() { return "distantHorizons:LodIndexBuffer"; } + private String getRenderPassName() { return "distantHorizons:McLodRenderer"; } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhApplyRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhApplyRenderer.java new file mode 100644 index 000000000..83accc661 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhApplyRenderer.java @@ -0,0 +1,268 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze.apply; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhApplyRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.*; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; +import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.coreapi.ModInfo; +import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Copies the given color texture + * where the depth (or another attribute) is valid. + * Often used to apply post processing effects or + * the DH texture to MC's color texture.

+ * + * @see BlazeDhCopyRenderer + */ +public class BlazeDhApplyRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + private RenderPipeline pipeline; + + protected GpuBuffer vboGpuBuffer; + + protected final String name; + protected final String identifierName; + public String getIdentifierName() { return this.identifierName; } + + @Nullable + private final BlendFunction blendFunction; + private final String vertexShaderPath; + private final String fragmentShaderPath; + + private final BlazeTextureViewWrapper sourceColorTextureViewWrapper = new BlazeTextureViewWrapper(); + private final BlazeTextureViewWrapper sourceDepthTextureViewWrapper = new BlazeTextureViewWrapper(); + + private final BlazeTextureViewWrapper destinationColorTextureViewWrapper = new BlazeTextureViewWrapper(); + + /** + * Can be set for special application shaders that need + * extra information.

+ * + * will be an empty array if unneeded + */ + private final String[] uniformNames; + /** will be an empty array if unneeded */ + private final GpuBuffer[] uniformBuffers; + + + + //=============// + // constructor // + //=============// + //region + public BlazeDhApplyRenderer( + String name, + @Nullable BlendFunction blendFunction, + String vertexShaderPath, String fragmentShaderPath + ) + { + this( + name, + blendFunction, + vertexShaderPath, fragmentShaderPath, + new String[0] // no extra uniforms + ); + } + public BlazeDhApplyRenderer( + String name, + @Nullable BlendFunction blendFunction, + String vertexShaderPath, String fragmentShaderPath, + String[] uniformNames + ) + { + this.name = name; + this.identifierName = "distanthorizons:"+this.name; + this.blendFunction = blendFunction; + + this.vertexShaderPath = vertexShaderPath; + this.fragmentShaderPath = fragmentShaderPath; + + this.uniformNames = uniformNames; + this.uniformBuffers = new GpuBuffer[this.uniformNames.length]; + } + + private void tryInit( + GpuTexture sourceColorTexture, + GpuTexture sourceDepthTexture, + GpuTexture destinationColorTexture) + { + this.createPipeline(); + this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData(this.name); + + this.sourceColorTextureViewWrapper.tryWrap(sourceColorTexture); + this.sourceDepthTextureViewWrapper.tryWrap(sourceDepthTexture); + + this.destinationColorTextureViewWrapper.tryWrap(destinationColorTexture); + + } + private void createPipeline() + { + if (this.pipeline != null) + { + return; + } + + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS) + .build(); + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + + if (this.blendFunction != null) + { + pipelineBuilder.withBlend(this.blendFunction); + } + else + { + pipelineBuilder.withoutBlend(); + } + + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse(this.identifierName)); // TODO will complain if capital letters are included + + // TODO manually validate paths to confirm they exist and end with ".fsh" or ".vsh", MC silently fails if the files are missing/improperly named + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", this.vertexShaderPath)); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", this.fragmentShaderPath)); + + for (int i = 0; i < this.uniformNames.length; i++) + { + String uniformName = this.uniformNames[i]; + pipelineBuilder.withUniform(uniformName, UniformType.UNIFORM_BUFFER); + } + + pipelineBuilder.withSampler("uSourceColorTexture"); + pipelineBuilder.withSampler("uSourceDepthTexture"); + + pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.TRIANGLE_FAN); + } + this.pipeline = pipelineBuilder.build(); + } + + + //endregion + + + + //========// + // render // + //========// + //region + + public void setUniform(String uniformName, GpuBuffer uniformBuffer) + { + // the uniform array should be short enough (less than 10 items) + // where a sequential search should be plenty fast + for (int i = 0; i < this.uniformNames.length; i++) + { + String nameAtIndex = this.uniformNames[i]; + if (nameAtIndex.equals(uniformName)) + { + this.uniformBuffers[i] = uniformBuffer; + break; + } + } + } + + public void render( + GpuTexture sourceColorTexture, + GpuTexture sourceDepthTexture, + GpuTexture destinationColorTexture) + { + this.tryInit(sourceColorTexture, sourceDepthTexture, destinationColorTexture); + + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getIdentifierName, + this.destinationColorTextureViewWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + /*depthTexture*/ null, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + renderPass.bindTexture("uSourceColorTexture", this.sourceColorTextureViewWrapper.textureView, this.sourceColorTextureViewWrapper.textureSampler); + renderPass.bindTexture("uSourceDepthTexture", this.sourceDepthTextureViewWrapper.textureView, this.sourceDepthTextureViewWrapper.textureSampler); + + for (int i = 0; i < this.uniformNames.length; i++) + { + String uniformName = this.uniformNames[i]; + GpuBuffer uniformBuffer = this.uniformBuffers[i]; + if (uniformBuffer == null) + { + throw new IllegalStateException("Missing uniform ["+uniformName+"], please set the uniform before rendering."); + } + + renderPass.setUniform(uniformName, uniformBuffer); + } + + renderPass.setVertexBuffer(0, this.vboGpuBuffer); + renderPass.setPipeline(this.pipeline); + + renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4); + } + + + // clear the uniforms after rendering + // so we can check if they're missing during next frame's rendering + if (ModInfo.IS_DEV_BUILD) + { + Arrays.fill(this.uniformBuffers, null); + } + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhCopyRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhCopyRenderer.java new file mode 100644 index 000000000..7286a0758 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhCopyRenderer.java @@ -0,0 +1,165 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze.apply; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhCopyRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.*; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; +import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import net.minecraft.resources.Identifier; + +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Blindly copies one texture into another. + * + * @see BlazeDhApplyRenderer + */ +public class BlazeDhCopyRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + public static final BlazeDhCopyRenderer INSTANCE = new BlazeDhCopyRenderer(); + + private RenderPipeline pipeline; + private boolean init = false; + + private GpuBuffer vboGpuBuffer; + + + + //=============// + // constructor // + //=============// + //region + + private BlazeDhCopyRenderer() { } + + private void tryInit() + { + if (this.init) + { + return; + } + this.init = true; + + + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withoutBlend(); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:copy_render")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "copy/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "copy/blaze/frag")); + + pipelineBuilder.withSampler("uCopyTexture"); + + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + } + this.pipeline = pipelineBuilder.build(); + + + this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McCopyRenderer"); + + } + + //endregion + + + + //========// + // render // + //========// + //region + + public void render( + BlazeTextureWrapper sourceColorTextureWrapper, + BlazeTextureViewWrapper destinationColorTextureWrapper) + { + this.render( + sourceColorTextureWrapper.textureView, sourceColorTextureWrapper.textureSampler, + destinationColorTextureWrapper.textureView); + } + public void render( + BlazeTextureWrapper sourceColorTextureWrapper, + BlazeTextureWrapper destinationColorTextureWrapper) + { + this.render( + sourceColorTextureWrapper.textureView, sourceColorTextureWrapper.textureSampler, + destinationColorTextureWrapper.textureView); + } + + private void render( + GpuTextureView sourceTextureView, + GpuSampler sourceTextureSampler, + GpuTextureView destinationTextureView) + { + this.tryInit(); + + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + destinationTextureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + /*depthTexture*/ null, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + renderPass.bindTexture("uCopyTexture", sourceTextureView, sourceTextureSampler); + + renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol + + renderPass.setPipeline(this.pipeline); + renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4); + } + } + + private String getRenderPassName() { return "distantHorizons:McCopyRenderer"; } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/objects/BlazeGenericObjectVertexContainer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/objects/BlazeGenericObjectVertexContainer.java new file mode 100644 index 000000000..34b16009f --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/objects/BlazeGenericObjectVertexContainer.java @@ -0,0 +1,285 @@ +package com.seibel.distanthorizons.common.render.blaze.objects; + +#if MC_VER <= MC_1_21_10 +public class BlazeGenericObjectVertexContainer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; +import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; +import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup; +import com.seibel.distanthorizons.core.util.ColorUtil; +import org.lwjgl.opengl.GL32; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.List; + +/** + * For use by {@link RenderableBoxGroup} + * + * @see RenderableBoxGroup + */ +public class BlazeGenericObjectVertexContainer implements IDhGenericObjectVertexBufferContainer +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + private static final int[] BOX_INDICES = { + //region + // min X, vertical face + 2, 1, 0, + 0, 3, 2, + // max X, vertical face + 6, 5, 4, + 4, 7, 6, + + // min Z, vertical face + 10, 9, 8, + 8, 11, 10, + // max Z, vertical face + 14, 13, 12, + 12, 15, 14, + + // min Y, horizontal face + 18, 17, 16, + 16, 19, 18, + // max Y, horizontal face + 20, 21, 22, + 22, 23, 20, + //endregion + }; + + + + public GpuBuffer vboGpuBuffer; + public GpuBuffer indexGpuBuffer; + + private ByteBuffer vertexBuffer = ByteBuffer.allocateDirect(0); + private ByteBuffer indexBuffer = ByteBuffer.allocateDirect(0); + + public int uploadedBoxCount = 0; + + private EState state = EState.NEW; + @Override + public IDhGenericObjectVertexBufferContainer.EState getState() { return this.state; } + @Override + public void setState(IDhGenericObjectVertexBufferContainer.EState state) { this.state = state; } + + + + //===========================// + // render building/uploading // + //===========================// + //region + + public void updateVertexData(List uploadBoxList) + { + int boxCount = uploadBoxList.size(); + if (boxCount == 0) + { + return; // TODO done just to fix a buffer empty crash + } + + + // recreate the data arrays if their size is different + if (this.uploadedBoxCount != boxCount) + { + this.uploadedBoxCount = boxCount; + + int vertexBufferSize = this.vertexBufferSize(); + this.vertexBuffer = ByteBuffer.allocateDirect(vertexBufferSize); + this.vertexBuffer.order(ByteOrder.nativeOrder()); + + int indexBufferSize = this.indexBufferSize(); + this.indexBuffer = ByteBuffer.allocateDirect(indexBufferSize); + this.indexBuffer.order(ByteOrder.nativeOrder()); + } + this.vertexBuffer.position(0); + this.indexBuffer.position(0); + + + + for (int boxIndex = 0; boxIndex < boxCount; boxIndex++) + { + // index + int indexOffset = (boxIndex * 24 /*24 is the number of vertices in a box*/); + for (int i = 0; i < BOX_INDICES.length; i++) + { + this.indexBuffer.putInt(BOX_INDICES[i] + indexOffset); + } + + + + + // vertex + DhApiRenderableBox box = uploadBoxList.get(boxIndex); + + final double[] boxVertices = + { + //region + // Pos x y z + + // min X, vertical face + box.minPos.x, box.minPos.y, box.minPos.z, + box.maxPos.x, box.minPos.y, box.minPos.z, + box.maxPos.x, box.maxPos.y, box.minPos.z, + box.minPos.x, box.maxPos.y, box.minPos.z, + // max X, vertical face + box.minPos.x, box.maxPos.y, box.maxPos.z, + box.maxPos.x, box.maxPos.y, box.maxPos.z, + box.maxPos.x, box.minPos.y, box.maxPos.z, + box.minPos.x, box.minPos.y, box.maxPos.z, + + // min Z, vertical face + box.minPos.x, box.minPos.y, box.maxPos.z, + box.minPos.x, box.minPos.y, box.minPos.z, + box.minPos.x, box.maxPos.y, box.minPos.z, + box.minPos.x, box.maxPos.y, box.maxPos.z, + // max Z, vertical face + box.maxPos.x, box.minPos.y, box.maxPos.z, + box.maxPos.x, box.maxPos.y, box.maxPos.z, + box.maxPos.x, box.maxPos.y, box.minPos.z, + box.maxPos.x, box.minPos.y, box.minPos.z, + + // min Y, horizontal face + box.minPos.x, box.minPos.y, box.maxPos.z, + box.maxPos.x, box.minPos.y, box.maxPos.z, + box.maxPos.x, box.minPos.y, box.minPos.z, + box.minPos.x, box.minPos.y, box.minPos.z, + // max Y, horizontal face + box.minPos.x, box.maxPos.y, box.maxPos.z, + box.maxPos.x, box.maxPos.y, box.maxPos.z, + box.maxPos.x, box.maxPos.y, box.minPos.z, + box.minPos.x, box.maxPos.y, box.minPos.z, + //endregion + }; + + for (int vertexIndex = 0; vertexIndex < boxVertices.length; vertexIndex+=3) + { + this.vertexBuffer.putFloat((float)boxVertices[vertexIndex]); // x + this.vertexBuffer.putFloat((float)boxVertices[vertexIndex+1]); // y + this.vertexBuffer.putFloat((float)boxVertices[vertexIndex+2]); // z + + int color = ColorUtil.toColorInt(box.color); + byte r = (byte) ColorUtil.getRed(color); + byte g = (byte) ColorUtil.getGreen(color); + byte b = (byte) ColorUtil.getBlue(color); + byte a = (byte) ColorUtil.getAlpha(color); + this.vertexBuffer.put(r); + this.vertexBuffer.put(g); + this.vertexBuffer.put(b); + this.vertexBuffer.put(a); + + this.vertexBuffer.put(box.material); + // TODO make sure this all is a multiple of 4 like LodQuadBuilder (might cause issues with AMD/Mac otherwise) + } + } + this.vertexBuffer.flip(); + this.indexBuffer.flip(); + + + this.state = BlazeGenericObjectVertexContainer.EState.READY_TO_UPLOAD; + } + + private int vertexBufferSize() + { + int faceCount = this.uploadedBoxCount * 6; + int vertexCount = faceCount * 6; + + int byteSize = vertexCount * 3 * Float.BYTES; // x,y,z + byteSize += vertexCount * 4; // r,g,b,a + byteSize += 1; // material + return byteSize; + } + private int indexBufferSize() + { + int quadCount = this.uploadedBoxCount * 36; + int byteSize = quadCount * GLEnums.getTypeSize(GL32.GL_UNSIGNED_INT) * 6; + return byteSize; + } + + public void uploadDataToGpu() + { + // vertex + { + int totalVertexByteSize = this.vertexBufferSize(); + if (this.vboGpuBuffer == null + || this.vboGpuBuffer.size() < totalVertexByteSize) + { + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX; + this.vboGpuBuffer = GPU_DEVICE.createBuffer(this::getVertexBufferName, usage, totalVertexByteSize); + } + + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vboGpuBuffer, /*offset*/0, totalVertexByteSize); + COMMAND_ENCODER.writeToBuffer(bufferSlice, this.vertexBuffer); + } + + // index + { + int totalVertexByteSize = this.indexBufferSize(); + if (this.indexGpuBuffer == null + || this.indexGpuBuffer.size() < totalVertexByteSize) + { + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX + | GpuBuffer.USAGE_INDEX + | GpuBuffer.USAGE_UNIFORM; + this.indexGpuBuffer = GPU_DEVICE.createBuffer(this::getIndexBufferName, usage, totalVertexByteSize); + } + + int offset = 0; + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.indexGpuBuffer, offset, totalVertexByteSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, this.indexBuffer); + } + + this.state = EState.RENDER; + } + private String getVertexBufferName() { return "distantHorizons:GenericVertexBuffer"; } + private String getIndexBufferName() { return "distantHorizons:GenericIndexBuffer"; } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + @Override + public void close() + { + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() -> + { + if (this.vboGpuBuffer != null) + { + this.vboGpuBuffer.close(); + } + + if (this.indexGpuBuffer != null) + { + this.indexGpuBuffer.close(); + } + }); + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFarFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFarFadeRenderer.java new file mode 100644 index 000000000..8d1d0a451 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFarFadeRenderer.java @@ -0,0 +1,235 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze.postProcessing; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhFarFadeRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; +import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhCopyRenderer; +import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFarFadeRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Fades out DH's far clip plane + */ +public class BlazeDhFarFadeRenderer implements IDhFarFadeRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + public static final BlazeDhFarFadeRenderer INSTANCE = new BlazeDhFarFadeRenderer(); + + private RenderPipeline pipeline; + private boolean init = false; + + private GpuBuffer fragUniformBuffer; + + private GpuBuffer vboGpuBuffer; + + public final BlazeTextureWrapper dhFadeColorTextureWrapper = BlazeTextureWrapper.createColor("DhFadeColorTexture"); + public final BlazeTextureViewWrapper mcColorTextureViewWrapper = new BlazeTextureViewWrapper(); + + + + //=============// + // constructor // + //=============// + //region + + private BlazeDhFarFadeRenderer() { } + + private void tryInit() + { + if (this.init) + { + return; + } + this.init = true; + + + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withoutBlend(); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:far_fade")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/dh_fade")); + + pipelineBuilder.withSampler("uMcColorTexture"); + + pipelineBuilder.withSampler("uDhDepthTexture"); + pipelineBuilder.withSampler("uDhColorTexture"); + + pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + } + this.pipeline = pipelineBuilder.build(); + + + this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McFadeRenderer"); + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + this.tryInit(); + + + if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty() + || BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty()) + { + return; + } + + + + // textures + this.dhFadeColorTextureWrapper.tryCreateOrResize(); + this.mcColorTextureViewWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture()); + + { + int uniformBufferSize = new Std140SizeCalculator() + .putFloat() // uStartFadeBlockDistance + .putFloat() // uEndFadeBlockDistance + .putMat4f() // uDhInvMvmProj + .get(); + + + // create data // + + float dhFarClipDistance = RenderUtil.getFarClipPlaneDistanceInBlocks(); + float fadeStartDistance = dhFarClipDistance * 0.5f; + float fadeEndDistance = dhFarClipDistance * 0.9f; + + + Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(renderParams.mcProjectionMatrix); + Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(renderParams.mcModelViewMatrix); + + Mat4f inverseDhMvmProjMatrix = new Mat4f(dhProjectionMatrix); + inverseDhMvmProjMatrix.multiply(dhModelViewMatrix); + inverseDhMvmProjMatrix.invert(); + + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putFloat(fadeStartDistance) // uStartFadeBlockDistance + .putFloat(fadeEndDistance) // uEndFadeBlockDistance + .putMat4f(inverseDhMvmProjMatrix.createJomlMatrix()) // uDhInvMvmProj + .get() + ; + + this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + + this.renderFadeToTexture(); + BlazeDhCopyRenderer.INSTANCE.render(this.dhFadeColorTextureWrapper, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper); + + } + + private void renderFadeToTexture() + { + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + this.dhFadeColorTextureWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + /*depthTexture*/ null, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + // MC texture + renderPass.bindTexture("uMcColorTexture", this.mcColorTextureViewWrapper.textureView, this.mcColorTextureViewWrapper.textureSampler); + + // DH textures + renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler); + renderPass.bindTexture("uDhColorTexture", BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureSampler); + + renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer); + + renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol + renderPass.setPipeline(this.pipeline); + + renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4); + } + } + private String getRenderPassName() { return "distantHorizons:McFadeRenderer"; } + + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFogRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFogRenderer.java new file mode 100644 index 000000000..4d20e9c65 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFogRenderer.java @@ -0,0 +1,363 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze.postProcessing; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhFogRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.DestFactor; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.platform.SourceFactor; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; +import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; +import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; +import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFogRenderer; +import net.minecraft.resources.Identifier; + +import java.awt.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Renders fog onto the LODs. + */ +public class BlazeDhFogRenderer implements IDhFogRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + public static final BlazeDhFogRenderer INSTANCE = new BlazeDhFogRenderer(); + + + private BlazeDhApplyRenderer applyRenderer; + + private RenderPipeline pipeline; + private boolean init = false; + + private GpuBuffer fragUniformBuffer; + + private GpuBuffer vboGpuBuffer; + + public BlazeTextureWrapper fogColorTextureWrapper = BlazeTextureWrapper.createColor("DhFogColorTexture"); + + + + //=============// + // constructor // + //=============// + //region + + private BlazeDhFogRenderer() { } + + private void tryInit() + { + if (this.init) + { + return; + } + this.init = true; + + + + + this.applyRenderer = new BlazeDhApplyRenderer( + "fog_apply_to_dh", + new BlendFunction(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ONE_MINUS_SRC_ALPHA), + "apply/blaze/vert", "apply/blaze/frag" + ); + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withoutBlend(); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:fog_render")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "fog/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "fog/blaze/frag")); + + pipelineBuilder.withSampler("uDhDepthTexture"); + + pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + } + this.pipeline = pipelineBuilder.build(); + + + this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McFogRenderer"); + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + this.tryInit(); + + + if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty() + || BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty()) + { + return; + } + + + + this.fogColorTextureWrapper.tryCreateOrResize(); + + + { + int uniformBufferSize = new Std140SizeCalculator() + + // fog uniforms + .putVec4() // uFogColor + .putFloat() //uFogScale + .putFloat() //uFogVerticalScale + // only used for debugging + .putInt() //uFogDebugMode // 1 = render everything with fog color // 7 = use debug rendering + .putInt() //uFogFalloffType + + // fog config + .putFloat() // uFarFogStart + .putFloat() // uFarFogLength + .putFloat() // uFarFogMin + .putFloat() // uFarFogRange + .putFloat() // uFarFogDensity + + // height fog config + .putFloat() // uHeightFogStart + .putFloat() // uHeightFogLength + .putFloat() // uHeightFogMin + .putFloat() // uHeightFogRange + .putFloat() // uHeightFogDensity + + // ?? + .putInt() // uHeightFogEnabled + .putInt() // uHeightFogFalloffType + .putInt() // uHeightBasedOnCamera + .putFloat() // uHeightFogBaseHeight + .putInt() // uHeightFogAppliesUp + .putInt() // uHeightFogAppliesDown + .putInt() // uUseSphericalFog + .putInt() // uHeightFogMixingMode + .putFloat() // uCameraBlockYPos + + .putMat4f() // uInvMvmProj + + .get(); + + + // create data // + + + int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; + + + Mat4f inverseMvmProjMatrix = new Mat4f(renderParams.dhMvmProjMatrix); + inverseMvmProjMatrix.invert(); + + if (renderParams.dhMvmProjMatrix == null) + { + return; + } + + + Color fogColor = this.getFogColor(renderParams.partialTicks); + + // fog config + float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get(); + float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get(); + float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get(); + float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get(); + float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get(); + + // override fog if underwater + if (MC_RENDER.isFogStateSpecial()) + { + // hide everything behind fog + farFogStart = 0.0f; + farFogEnd = 0.0f; + } + + + // height config + EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get(); + boolean heightFogEnabled = heightFogMixingMode != EDhApiHeightFogMixMode.SPHERICAL && heightFogMixingMode != EDhApiHeightFogMixMode.CYLINDRICAL; + boolean useSphericalFog = heightFogMixingMode == EDhApiHeightFogMixMode.SPHERICAL; + EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get(); + + float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get(); + float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get(); + float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get(); + float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get(); + float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get(); + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + + // fog uniforms + .putVec4( + fogColor.getRed() / 255.0f, + fogColor.getGreen() / 255.0f, + fogColor.getBlue() / 255.0f, + fogColor.getAlpha() / 255.0f) // uFogColor + .putFloat(1.f / lodDrawDistance) //uFogScale + .putFloat(1.f / MC.getWrappedClientLevel().getMaxHeight()) //uFogVerticalScale + // only used for debugging + .putInt(0) //uFogDebugMode // 1 = render everything with fog color // 7 = use debug rendering + .putInt(Config.Client.Advanced.Graphics.Fog.farFogFalloff.get().value) //uFogFalloffType + + // fog config + .putFloat(farFogStart) // uFarFogStart + .putFloat(farFogEnd - farFogStart) // uFarFogLength + .putFloat(farFogMin) // uFarFogMin + .putFloat(farFogMax - farFogMin) // uFarFogRange + .putFloat(farFogDensity) // uFarFogDensity + + // height fog config + .putFloat(heightFogStart) // uHeightFogStart + .putFloat(heightFogEnd - heightFogStart) // uHeightFogLength + .putFloat(heightFogMin) // uHeightFogMin + .putFloat(heightFogMax - heightFogMin) // uHeightFogRange + .putFloat(heightFogDensity) // uHeightFogDensity + + // ?? + .putInt(heightFogEnabled ? 1 : 0) // uHeightFogEnabled + .putInt(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get().value) // uHeightFogFalloffType + .putInt(heightFogCameraDirection.basedOnCamera ? 1 : 0) // uHeightBasedOnCamera + .putFloat(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get()) // uHeightFogBaseHeight + .putInt(heightFogCameraDirection.fogAppliesUp ? 1 : 0) // uHeightFogAppliesUp + .putInt(heightFogCameraDirection.fogAppliesDown ? 1 : 0) // uHeightFogAppliesDown + .putInt(useSphericalFog ? 1 : 0) // uUseSphericalFog + .putInt(heightFogMixingMode.value) // uHeightFogMixingMode + .putFloat((float)MC_RENDER.getCameraExactPosition().y) // uCameraBlockYPos + + .putMat4f(inverseMvmProjMatrix.createJomlMatrix()) // uInvMvmProj + + .get() + ; + + this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + + this.renderFogToTexture(); + this.applyRenderer.render(this.fogColorTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.texture); + + } + + private Color getFogColor(float partialTicks) + { + Color fogColor; + + if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EDhApiFogColorMode.USE_SKY_COLOR) + { + fogColor = MC_RENDER.getSkyColor(); + } + else + { + fogColor = MC_RENDER.getFogColor(partialTicks); + } + + return fogColor; + } + + private void renderFogToTexture() + { + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + this.fogColorTextureWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + /*depthTexture*/ null, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler); + + renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer); + + renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol + renderPass.setPipeline(this.pipeline); + + renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4); + } + } + private String getRenderPassName() { return "distantHorizons:McFogRenderer"; } + + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhSsaoRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhSsaoRenderer.java new file mode 100644 index 000000000..87f0e91b1 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhSsaoRenderer.java @@ -0,0 +1,283 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze.postProcessing; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhSsaoRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.DestFactor; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.platform.SourceFactor; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; +import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; +import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhSsaoRenderer; +import net.minecraft.resources.Identifier; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** Renders SSAO to the DH LODs. */ +public class BlazeDhSsaoRenderer implements IDhSsaoRenderer +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + public static final BlazeDhSsaoRenderer INSTANCE = new BlazeDhSsaoRenderer(); + + + private BlazeDhApplyRenderer applyRenderer; + + private RenderPipeline pipeline; + private boolean init = false; + + private GpuBuffer fragUniformBuffer; + private GpuBuffer applyFragUniformBuffer; + + private GpuBuffer vboGpuBuffer; + + public BlazeTextureWrapper ssaoColorTextureWrapper = BlazeTextureWrapper.createColor("DhSsaoTexture"); + + + + //=============// + // constructor // + //=============// + //region + + private BlazeDhSsaoRenderer() { } + + private void tryInit() + { + if (this.init) + { + return; + } + this.init = true; + + + this.applyRenderer = new BlazeDhApplyRenderer( + "ssao_apply_to_dh", + new BlendFunction(SourceFactor.ZERO, DestFactor.SRC_ALPHA, SourceFactor.ZERO, DestFactor.ONE), + "apply/blaze/vert", "ssao/blaze/apply", + /*uniforms*/ new String[] { "applyFragUniformBlock" } + ); + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withoutBlend(); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:ssao_render")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "ssao/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "ssao/blaze/frag")); + + pipelineBuilder.withSampler("uDhDepthTexture"); + + pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + } + this.pipeline = pipelineBuilder.build(); + + + this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McSsao"); + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + this.tryInit(); + + + if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty() + || BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty()) + { + return; + } + + + + // textures + this.ssaoColorTextureWrapper.tryCreateOrResize(); + + // frag uniforms + { + int uniformBufferSize = new Std140SizeCalculator() + .putInt() // uSampleCount\ + + .putFloat() // uRadius + .putFloat() // uStrength + .putFloat() // uMinLight + .putFloat() // uBias + .putFloat() // uFadeDistanceInBlocks + + .putMat4f() // uInvProj + .putMat4f() // uProj + .get(); + + + // create data // + + Mat4f projMatrix = new Mat4f(renderParams.dhProjectionMatrix); + Mat4f invertedProjMatrix = new Mat4f(renderParams.dhProjectionMatrix); + invertedProjMatrix.invert(); + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putInt(6) // uSampleCount + + .putFloat(4.0f) // uRadius + .putFloat(0.2f) // uStrength + .putFloat(0.25f) // uMinLight + .putFloat(0.02f) // uBias + .putFloat(1_600.0f) // uFadeDistanceInBlocks + + .putMat4f(invertedProjMatrix.createJomlMatrix()) + .putMat4f(projMatrix.createJomlMatrix()) + .get() + ; + + this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + // apply frag uniforms + { + int uniformBufferSize = new Std140SizeCalculator() + .putVec2() // uViewSize + .putInt() // uBlurRadius + .putFloat() // uNearClipPlane + .putFloat() // uFarClipPlane + .get(); + + + // create data // + + float viewWidth = (float)MC_RENDER.getTargetFramebufferViewportWidth(); + float viewHeight = (float)MC_RENDER.getTargetFramebufferViewportHeight(); + + float nearClipPlane = RenderUtil.getNearClipPlaneInBlocks(); + float farClipPlane = RenderUtil.getFarClipPlaneDistanceInBlocks(); + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putVec2(viewWidth, viewHeight) // uViewSize + .putInt(2) // uBlurRadius + .putFloat(nearClipPlane) // uNearClipPlane + .putFloat(farClipPlane) // uFarClipPlane + .get() + ; + + this.applyFragUniformBuffer = BlazeUniformUtil.createBuffer("applyFragUniformBlock", uniformBufferSize, this.applyFragUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.applyFragUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + + this.renderSsaoToTexture(); + + this.applyRenderer.setUniform("applyFragUniformBlock", this.applyFragUniformBuffer); + this.applyRenderer.render(this.ssaoColorTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.texture, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.texture); + + } + + private void renderSsaoToTexture() + { + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + this.ssaoColorTextureWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + /*depthTexture*/ null, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler); + + renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer); + + renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol + + renderPass.setPipeline(this.pipeline); + renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4); + } + } + private String getRenderPassName() { return "distantHorizons:McSsaoRenderer"; } + + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeVanillaFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeVanillaFadeRenderer.java new file mode 100644 index 000000000..f1d7166c2 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeVanillaFadeRenderer.java @@ -0,0 +1,262 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze.postProcessing; + +#if MC_VER <= MC_1_21_10 +public class BlazeVanillaFadeRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; +import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhCopyRenderer; +import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Fades the vanilla chunks + * into DH's LODs. + */ +public class BlazeVanillaFadeRenderer implements IDhVanillaFadeRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + public static final BlazeVanillaFadeRenderer INSTANCE = new BlazeVanillaFadeRenderer(); + + private RenderPipeline pipeline; + private boolean init = false; + + private GpuBuffer fragUniformBuffer; + + private GpuBuffer vboGpuBuffer; + + public final BlazeTextureWrapper fadeColorTextureWrapper = BlazeTextureWrapper.createColor("DhVanillaFadeTexture"); + + public final BlazeTextureViewWrapper mcDepthTextureWrapper = new BlazeTextureViewWrapper(); + public final BlazeTextureViewWrapper mcColorTextureWrapper = new BlazeTextureViewWrapper(); + + + + //=============// + // constructor // + //=============// + //region + + private BlazeVanillaFadeRenderer() { } + + private void tryInit() + { + if (this.init) + { + return; + } + this.init = true; + + + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withoutBlend(); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:mc_vanilla_fade_render")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/vanilla_fade")); + + pipelineBuilder.withSampler("uMcDepthTexture"); + pipelineBuilder.withSampler("uCombinedMcDhColorTexture"); + + pipelineBuilder.withSampler("uDhDepthTexture"); + pipelineBuilder.withSampler("uDhColorTexture"); + + pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + } + this.pipeline = pipelineBuilder.build(); + + + this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData("McFadeRenderer"); + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + this.tryInit(); + + if (BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty() + || BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty()) + { + return; + } + + + // textures + this.fadeColorTextureWrapper.tryCreateOrResize(); + + this.mcDepthTextureWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getDepthTexture()); + this.mcColorTextureWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture()); + + + { + int uniformBufferSize = new Std140SizeCalculator() + .putInt() // uOnlyRenderLods + .putFloat() // uStartFadeBlockDistance + .putFloat() // uEndFadeBlockDistance + .putFloat() // uMaxLevelHeight + .putMat4f() // uDhInvMvmProj + .putMat4f() // uMcInvMvmProj + .get(); + + + // create data // + + float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks(); + // this added value prevents the near clip plane and discard circle from touching, which looks bad + dhNearClipDistance += 16f; + + // measured in blocks + // these multipliers in James' tests should provide a fairly smooth transition + // without having underdraw issues + float fadeStartDistance = dhNearClipDistance * 1.5f; + float fadeEndDistance = dhNearClipDistance * 1.9f; + + + Mat4f inverseMcModelViewProjectionMatrix = new Mat4f(renderParams.mcProjectionMatrix); + inverseMcModelViewProjectionMatrix.multiply(renderParams.mcModelViewMatrix); + inverseMcModelViewProjectionMatrix.invert(); + Mat4f inverseMcMvmProjMatrix = inverseMcModelViewProjectionMatrix; + + + Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(renderParams.mcProjectionMatrix); + Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(renderParams.mcModelViewMatrix); + + Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix); + inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix); + inverseDhModelViewProjectionMatrix.invert(); + Mat4f inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix; + + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putInt(Config.Client.Advanced.Debugging.lodOnlyMode.get() ? 1 : 0) // uOnlyRenderLods + .putFloat(fadeStartDistance) // uStartFadeBlockDistance + .putFloat(fadeEndDistance) // uEndFadeBlockDistance + .putFloat(renderParams.clientLevelWrapper.getMaxHeight()) // uMaxLevelHeight + .putMat4f(inverseDhMvmProjMatrix.createJomlMatrix()) // uDhInvMvmProj + .putMat4f(inverseMcMvmProjMatrix.createJomlMatrix()) // uMcInvMvmProj + .get() + ; + + this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + + this.renderFadeToTexture(); + BlazeDhCopyRenderer.INSTANCE.render(this.fadeColorTextureWrapper, this.mcColorTextureWrapper); + + } + + private void renderFadeToTexture() + { + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + this.fadeColorTextureWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + /*depthTexture*/ null, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + renderPass.bindTexture("uMcDepthTexture", this.mcDepthTextureWrapper.textureView, this.mcDepthTextureWrapper.textureSampler); + renderPass.bindTexture("uCombinedMcDhColorTexture", this.mcColorTextureWrapper.textureView, this.mcColorTextureWrapper.textureSampler); + + renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler); + renderPass.bindTexture("uDhColorTexture", BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureSampler); + + renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer); + + renderPass.setVertexBuffer(0, this.vboGpuBuffer); // vertex buffer can only be "0" lol + + renderPass.setPipeline(this.pipeline); + renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 4); + } + } + private String getRenderPassName() { return "distantHorizons:McFadeRenderer"; } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/test/BlazeDhTestTriangleRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/test/BlazeDhTestTriangleRenderer.java new file mode 100644 index 000000000..aaa295de2 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/test/BlazeDhTestTriangleRenderer.java @@ -0,0 +1,192 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.blaze.test; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhTestTriangleRenderer {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.DepthTestFunction; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.*; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.Identifier; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Renders the OpenGL/Vulkan triangle + * to the center of the screen to confirm DH's + * apply shader is running correctly + */ +public class BlazeDhTestTriangleRenderer implements IDhTestTriangleRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + public static final BlazeDhTestTriangleRenderer INSTANCE = new BlazeDhTestTriangleRenderer(); + + private RenderPipeline pipeline; + private boolean init = false; + + private GpuTextureView mcColorTextureView; + + private GpuBuffer vboGpuBuffer; + + + + //=============// + // constructor // + //=============// + //region + + private BlazeDhTestTriangleRenderer() { } + + private void tryInit() + { + if (this.init) + { + return; + } + this.init = true; + + + + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS) + .add("vColor", BlazeDhVertexFormatUtil.RGBA_FLOAT_COLOR) + .build(); + + //int breakpointOne = 0; + // needs to manually be set if the VertexFormatElement isn't registered + //this.vertexFormat.getOffsetsByElement()[this.posForm.id()] = 0; + //this.vertexFormat.getOffsetsByElement()[this.colForm.id()] = Float.BYTES * 2; + // + //int breakpointTwo = 0; + + + RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + { + pipelineBuilder.withCull(false); + pipelineBuilder.withDepthWrite(false); + pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withColorWrite(true); + pipelineBuilder.withoutBlend(); + pipelineBuilder.withPolygonMode(PolygonMode.FILL); + pipelineBuilder.withLocation(Identifier.parse("distanthorizons:test_render")); + + pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "test/blaze/vert")); + pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "test/blaze/frag")); + + pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.TRIANGLES); + } + this.pipeline = pipelineBuilder.build(); + + + this.mcColorTextureView = GPU_DEVICE.createTextureView(Minecraft.getInstance().getMainRenderTarget().getColorTexture()); + + + this.uploadVertexData(); + } + private void uploadVertexData() + { + // vertices for the OpenGL/Vulkan Triangle + float[] vertices = new float[] + { + // PosX,Y, ColorR,G,B,A + -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, + }; + + + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX; + int size = vertices.length * Float.BYTES; + this.vboGpuBuffer = GPU_DEVICE.createBuffer(this::getRenderPassName, usage, size); + + { + int offset = 0; + int length = vertices.length * Float.BYTES; + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vboGpuBuffer, offset, length); + + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * Float.BYTES); + // Fill buffer with vertices. + byteBuffer.order(ByteOrder.nativeOrder()); + byteBuffer.asFloatBuffer().put(vertices); + byteBuffer.rewind(); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, byteBuffer); + } + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + this.tryInit(); + + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + this.mcColorTextureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + /*mcDepthTextureView*/ null, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) + { + renderPass.setVertexBuffer(0, this.vboGpuBuffer); + renderPass.setPipeline(this.pipeline); + renderPass.draw(/*indexStart*/ 0, /*indexCount*/ 3); + } + } + private String getRenderPassName() { return "distantHorizons:DhTestRenderer"; } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeDhVertexFormatUtil.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeDhVertexFormatUtil.java new file mode 100644 index 000000000..d4cea9b20 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeDhVertexFormatUtil.java @@ -0,0 +1,105 @@ +package com.seibel.distanthorizons.common.render.blaze.util; + +#if MC_VER <= MC_1_21_10 +public class BlazeDhVertexFormatUtil {} + +#else + +import com.mojang.blaze3d.vertex.VertexFormatElement; +import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; + +/** + * @see LodQuadBuilder + */ +public class BlazeDhVertexFormatUtil +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + + + public static final VertexFormatElement SCREEN_POS; + public static final VertexFormatElement RGBA_FLOAT_COLOR; + + public static final VertexFormatElement SHORT_XYZ_POS; + public static final VertexFormatElement BYTE_PAD; + /** contains light and micro-offset */ + public static final VertexFormatElement META; + public static final VertexFormatElement RGBA_UBYTE_COLOR; + public static final VertexFormatElement IRIS_MATERIAL; + public static final VertexFormatElement IRIS_NORMAL; + + public static final VertexFormatElement FLOAT_XYZ_POS; + + + + + static + { + EDhApiRenderApi renderingApi = Config.Client.Advanced.Graphics.Experimental.renderingApi.get(); + if (renderingApi == EDhApiRenderApi.AUTO) + { + IVersionConstants versionConstants = SingletonInjector.INSTANCE.get(IVersionConstants.class); + renderingApi = versionConstants.getDefaultRenderingApi(); + } + + boolean register = (renderingApi == EDhApiRenderApi.BLAZE_3D); + if (register) + { + LOGGER.debug("Attempting to register ["+VertexFormatElement.class.getSimpleName()+"]..."); + + try + { + SCREEN_POS = VertexFormatElement.register(/*id*/22, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.POSITION, /*count*/ 2); + RGBA_FLOAT_COLOR = VertexFormatElement.register(/*id*/23, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.COLOR, /*count*/ 4); + + SHORT_XYZ_POS = VertexFormatElement.register(/*id*/24, /*index*/0, VertexFormatElement.Type.USHORT, VertexFormatElement.Usage.POSITION, /*count*/ 3); + BYTE_PAD = VertexFormatElement.register(/*id*/25, /*index*/0, VertexFormatElement.Type.BYTE, VertexFormatElement.Usage.GENERIC, /*count*/ 1); + + META = VertexFormatElement.register(/*id*/26, /*index*/0, VertexFormatElement.Type.USHORT, VertexFormatElement.Usage.GENERIC, /*count*/ 1); + RGBA_UBYTE_COLOR = VertexFormatElement.register(/*id*/27, /*index*/0, VertexFormatElement.Type.UBYTE, VertexFormatElement.Usage.COLOR, /*count*/ 4); + IRIS_MATERIAL = VertexFormatElement.register(/*id*/28, /*index*/0, VertexFormatElement.Type.BYTE, VertexFormatElement.Usage.GENERIC, /*count*/ 1); + IRIS_NORMAL = VertexFormatElement.register(/*id*/29, /*index*/0, VertexFormatElement.Type.BYTE, VertexFormatElement.Usage.GENERIC, /*count*/ 1); + + FLOAT_XYZ_POS = VertexFormatElement.register(/*id*/30, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.POSITION, /*count*/ 3); + } + catch (Exception e) + { + String message = "Unable to register one or more ["+VertexFormatElement.class.getSimpleName()+"] this is likely caused by another mod registering their own custom ["+VertexFormatElement.class.getSimpleName()+"]'s. This should be fixed in the next major Minecraft version."; + + IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + mc.crashMinecraft(message, new Exception(message, e)); + + // here to make the compiler happy, the process should shut down before this + throw new RuntimeException(e); + } + + LOGGER.debug("Successfully registered ["+VertexFormatElement.class.getSimpleName()+"]."); + } + else + { + SCREEN_POS = null; + RGBA_FLOAT_COLOR = null; + + SHORT_XYZ_POS = null; + BYTE_PAD = null; + + META = null; + RGBA_UBYTE_COLOR = null; + IRIS_MATERIAL = null; + IRIS_NORMAL = null; + + FLOAT_XYZ_POS = null; + } + } + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazePostProcessUtil.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazePostProcessUtil.java new file mode 100644 index 000000000..c1a29951f --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazePostProcessUtil.java @@ -0,0 +1,81 @@ +package com.seibel.distanthorizons.common.render.blaze.util; + +#if MC_VER <= MC_1_21_10 +public class BlazePostProcessUtil {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.function.Supplier; + +/** Contains code that's used by all post-processing effects. */ +public class BlazePostProcessUtil +{ + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + // vertices for a full-screen quad + private static final float[] VERTICES = new float[] + { + // PosX,Y, + -1f, -1f, + 1f, -1f, + 1f, 1f, + -1f, 1f, + }; + + + + //=========// + // methods // + //=========// + //region + + public static GpuBuffer createAndUploadScreenVertexData(String name) + { + Supplier labelSupplier = () -> "distantHorizons:"+name; + + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX; + int size = VERTICES.length * Float.BYTES; + GpuBuffer vboGpuBuffer = GPU_DEVICE.createBuffer(labelSupplier, usage, size); + + { + int length = VERTICES.length * Float.BYTES; + GpuBufferSlice bufferSlice = new GpuBufferSlice(vboGpuBuffer, /*offset*/ 0, length); + + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(VERTICES.length * Float.BYTES); + // Fill buffer with vertices. + byteBuffer.order(ByteOrder.nativeOrder()); + byteBuffer.asFloatBuffer().put(VERTICES); + byteBuffer.rewind(); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, byteBuffer); + } + + return vboGpuBuffer; + } + + public static VertexFormat createVertexFormat() + { + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS) + .build(); + return vertexFormat; + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeUniformUtil.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeUniformUtil.java new file mode 100644 index 000000000..6b40aaa11 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeUniformUtil.java @@ -0,0 +1,38 @@ +package com.seibel.distanthorizons.common.render.blaze.util; + +#if MC_VER <= MC_1_21_10 +public class BlazeUniformUtil {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; + +public class BlazeUniformUtil +{ + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + + public static GpuBuffer createBuffer(String uniformName, int size, GpuBuffer vboGpuBuffer) + { + // create VBO if needed + if (vboGpuBuffer == null + || vboGpuBuffer.size() < size) + { + // GpuBuffer.USAGE_UNIFORM = 128 + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX + | GpuBuffer.USAGE_UNIFORM; + vboGpuBuffer = GPU_DEVICE.createBuffer(() -> uniformName, usage, size); + } + + return vboGpuBuffer; + } + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/buffer/BlazeVertexBufferWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/buffer/BlazeVertexBufferWrapper.java new file mode 100644 index 000000000..ea707adfc --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/buffer/BlazeVertexBufferWrapper.java @@ -0,0 +1,90 @@ +package com.seibel.distanthorizons.common.render.blaze.wrappers.buffer; + +#if MC_VER <= MC_1_21_10 +public class BlazeVertexBufferWrapper {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; + +import java.nio.ByteBuffer; + +public class BlazeVertexBufferWrapper implements IVertexBufferWrapper +{ + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + + public final String name; + public String getName() { return this.name; } + + public GpuBuffer vboGpuBuffer = null; + public int vertexCount = -1; + public int indexCount = -1; + public boolean uploaded = false; + + + + //=============// + // constructor // + //=============// + //region + + public BlazeVertexBufferWrapper(String name) { this.name = name; } + + //endregion + + + + //========// + // upload // + //========// + //region + + @Override + public void upload(ByteBuffer buffer, int vertexCount) + { + this.vertexCount = vertexCount; + // 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5 + this.indexCount = (int)(vertexCount * 1.5); + this.uploaded = true; + + + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX; + int byteSize = (buffer.limit() - buffer.position()); + this.vboGpuBuffer = GPU_DEVICE.createBuffer(this::getName, usage, byteSize); + + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vboGpuBuffer, /*offset*/0, byteSize); + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + @Override + public void close() + { + if (this.vboGpuBuffer != null) + { + this.vboGpuBuffer.close(); + } + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureViewWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureViewWrapper.java new file mode 100644 index 000000000..5f5a33d2b --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureViewWrapper.java @@ -0,0 +1,72 @@ +package com.seibel.distanthorizons.common.render.blaze.wrappers.texture; + +#if MC_VER <= MC_1_21_10 +public class BlazeTextureViewWrapper {} + +#else + +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.*; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; + +import java.util.OptionalDouble; + +public class BlazeTextureViewWrapper +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + + public GpuTextureView textureView = null; + public GpuSampler textureSampler = null; + + + + //=======// + // setup // + //=======// + //region + + /** does nothing if the texture is already wrapped */ + public void tryWrap(GpuTexture texture) + { + this.tryRecreateTextureView(texture); + this.tryCreateSampler(); + } + private void tryRecreateTextureView(GpuTexture texture) + { + if (this.textureView == null + || this.textureView.texture() != texture) + { + if (this.textureView != null) + { + this.textureView.close(); + } + + this.textureView = GPU_DEVICE.createTextureView(texture); + } + } + private void tryCreateSampler() + { + if (this.textureSampler == null) + { + this.textureSampler = GPU_DEVICE.createSampler( + AddressMode.CLAMP_TO_EDGE, AddressMode.CLAMP_TO_EDGE, // U,V + FilterMode.LINEAR, FilterMode.LINEAR, // minFilter, magFilter + 1, // maxAnisotropy + OptionalDouble.empty() // maxLod + ); + } + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureWrapper.java new file mode 100644 index 000000000..fe1a3a8f2 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureWrapper.java @@ -0,0 +1,167 @@ +package com.seibel.distanthorizons.common.render.blaze.wrappers.texture; + +#if MC_VER <= MC_1_21_10 +public class BlazeTextureWrapper {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.*; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; + +import java.util.OptionalDouble; + +public class BlazeTextureWrapper +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + + public final String name; + public final TextureFormat textureFormat; + + public GpuTexture texture = null; + public GpuTextureView textureView = null; + public GpuSampler textureSampler = null; + + private int width = -1; + private int height = -1; + + + + //==============// + // constructors // + //==============// + //region + + public static BlazeTextureWrapper createDepth(String name) { return new BlazeTextureWrapper(name, TextureFormat.DEPTH32); } + public static BlazeTextureWrapper createColor(String name) { return new BlazeTextureWrapper(name, TextureFormat.RGBA8); } + + private BlazeTextureWrapper(String name, TextureFormat textureFormat) + { + this.name = name; + this.textureFormat = textureFormat; + } + + //endregion + + + + //=========// + // getters // + //=========// + //region + + public boolean isEmpty() { return this.texture == null; } + + /** @return -1 if the texture is null */ + public int getWidth() { return this.width; } + /** @return -1 if the texture is null */ + public int getHeight() { return this.height; } + + //endregion + + + + //=======// + // setup // + //=======// + //region + + /** does nothing if the texture is already created and the correct size */ + public void tryCreateOrResize() + { + this.tryCreateTexture(); + this.tryCreateSampler(); + } + private void tryCreateTexture() + { + int viewWidth = MC_RENDER.getTargetFramebufferViewportWidth(); + int viewHeight = MC_RENDER.getTargetFramebufferViewportHeight(); + + if (this.texture == null + || this.width != viewWidth + || this.height != viewHeight) + { + if (this.texture != null) + { + this.texture.close(); + this.textureView.close(); + } + + this.width = viewWidth; + this.height = viewHeight; + + int usage = GpuBuffer.USAGE_HINT_CLIENT_STORAGE + | GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX + | GpuBuffer.USAGE_UNIFORM; + this.texture = GPU_DEVICE.createTexture(this.name, + usage, + this.textureFormat, + viewWidth, viewHeight, + /*depthOrLayers*/ 1, /*mipLevels*/ 1 + ); + this.textureView = GPU_DEVICE.createTextureView(this.texture); + } + } + private void tryCreateSampler() + { + if (this.textureSampler == null) + { + this.textureSampler = GPU_DEVICE.createSampler( + AddressMode.CLAMP_TO_EDGE, AddressMode.CLAMP_TO_EDGE, // U,V + FilterMode.LINEAR, FilterMode.LINEAR, // minFilter, magFilter + 1, // maxAnisotropy + OptionalDouble.empty() // maxLod + ); + } + } + + //endregion + + + + //==========// + // clearing // + //==========// + //region + + /** + * Will throw an exception if not a color texture. + * @see ColorUtil#argbToInt + */ + public void clearColor(int clearArgbColor) + { + if (this.texture != null) + { + COMMAND_ENCODER.clearColorTexture(this.texture, clearArgbColor); + } + } + + /** Will throw an exception if not a depth texture. */ + public void clearDepth(float depth) + { + if (this.texture != null) + { + COMMAND_ENCODER.clearDepthTexture(this.texture, depth); + } + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/uniform/BlazeLodUniformBufferWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/uniform/BlazeLodUniformBufferWrapper.java new file mode 100644 index 000000000..ba39f5018 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/uniform/BlazeLodUniformBufferWrapper.java @@ -0,0 +1,76 @@ +package com.seibel.distanthorizons.common.render.blaze.wrappers.uniform; + +#if MC_VER <= MC_1_21_10 +public class BlazeLodUniformBufferWrapper {} + +#else + +import com.mojang.blaze3d.buffers.Std140Builder; +import com.mojang.blaze3d.buffers.Std140SizeCalculator; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; +import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper; + +import java.nio.ByteBuffer; + +public class BlazeLodUniformBufferWrapper extends BlazeUniformBufferWrapper implements ILodContainerUniformBufferWrapper +{ + + private boolean uploaded = false; + + + + //=============// + // constructor // + //=============// + //region + + public BlazeLodUniformBufferWrapper() { super(BlazeLodUniformBufferWrapper.class.getName()); } + + //endregion + + + + //========// + // ??? // + //========// + //region + + @Override + public void createUniformData(LodBufferContainer bufferContainer) + { + Vec3f modelOffset = new Vec3f( + (float) (bufferContainer.minCornerBlockPos.getX()), + (float) (bufferContainer.minCornerBlockPos.getY()), + (float) (bufferContainer.minCornerBlockPos.getZ())); + + // upload data // + + int uniformBufferSize = new Std140SizeCalculator() + .putVec3() // uModelOffset + .get(); + + ByteBuffer buffer = this.getOrCreateBuffer(uniformBufferSize); + Std140Builder.intoBuffer(buffer) + .putVec3(modelOffset.x, modelOffset.y, modelOffset.z) // uModelOffset + .get(); + + } + + @Override + public void tryUpload() + { + if (this.uploaded) + { + return; + } + + this.upload(); + + this.uploaded = true; + } + + //endregion + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/uniform/BlazeUniformBufferWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/uniform/BlazeUniformBufferWrapper.java new file mode 100644 index 000000000..a487b0bde --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/uniform/BlazeUniformBufferWrapper.java @@ -0,0 +1,130 @@ +package com.seibel.distanthorizons.common.render.blaze.wrappers.uniform; + +#if MC_VER <= MC_1_21_10 +public class BlazeUniformBufferWrapper {} + +#else + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderSystem; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IUniformBufferWrapper; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class BlazeUniformBufferWrapper implements IUniformBufferWrapper +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); + private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + + + private final String name; + + private int cpuBufferSize = 0; + private int gpuBufferSize = 0; + + private ByteBuffer cpuBuffer = null; + public GpuBuffer gpuBuffer = null; + + + + //=============// + // constructor // + //=============// + //region + + public BlazeUniformBufferWrapper(String name) { this.name = name; } + + //endregion + + + + //========// + // render // + //========// + //region + + protected ByteBuffer getOrCreateBuffer(int size) + { + if (this.cpuBuffer == null + || this.cpuBufferSize != size) + { + this.cpuBuffer = ByteBuffer.allocateDirect(size); + this.cpuBuffer.order(ByteOrder.nativeOrder()); + + this.cpuBufferSize = size; + } + + return this.cpuBuffer; + } + + @Override + public void upload() throws IllegalStateException + { + if (this.cpuBuffer == null) + { + throw new IllegalStateException("Upload called before buffer was created"); + } + + if (this.gpuBuffer == null + || this.gpuBufferSize != this.cpuBufferSize) + { + if (this.gpuBuffer != null) + { + this.gpuBuffer.close(); + } + + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_VERTEX + | GpuBuffer.USAGE_UNIFORM; + this.gpuBuffer = GPU_DEVICE.createBuffer(this::getBufferName, usage, this.cpuBufferSize); + + this.gpuBufferSize = this.cpuBufferSize; + } + + + + int byteSize = (this.cpuBuffer.limit() - this.cpuBuffer.position()); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.gpuBuffer, /*offset*/0, byteSize); + if (!bufferSlice.buffer().isClosed()) + { + COMMAND_ENCODER.writeToBuffer(bufferSlice, this.cpuBuffer); + } + else + { + LOGGER.warn("Uploading to buffer ["+this.name+"] failed due to already being closed"); + } + } + private String getBufferName() { return this.name; } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + @Override + public void close() + { + if (this.gpuBuffer != null) + { + this.gpuBuffer.close(); + } + } + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhDebugWireframeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhDebugWireframeRenderer.java new file mode 100644 index 000000000..6399a7d54 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhDebugWireframeRenderer.java @@ -0,0 +1,197 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLElementBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import org.lwjgl.opengl.GL32; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Handles rendering the wireframe particles + * that are used for seeing what the system's doing. + */ +public class GlDhDebugWireframeRenderer extends AbstractDebugWireframeRenderer +{ + public static GlDhDebugWireframeRenderer INSTANCE = new GlDhDebugWireframeRenderer(); + + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + + // rendering setup + private GlShaderProgram basicShader; + private GLVertexBuffer vertexBuffer; + private GLElementBuffer indexBuffer; + private GlAbstractVertexAttribute va; + private boolean init = false; + + + + /** A box from 0,0,0 to 1,1,1 */ + private static final float[] BOX_VERTICES = { + //region + // Pos x y z + 0, 0, 0, + 1, 0, 0, + 1, 1, 0, + 0, 1, 0, + 0, 0, 1, + 1, 0, 1, + 1, 1, 1, + 0, 1, 1, + //endregion + }; + + private static final int[] BOX_OUTLINE_INDICES = { + //region + 0, 1, + 1, 2, + 2, 3, + 3, 0, + + 4, 5, + 5, 6, + 6, 7, + 7, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7, + //endregion + }; + + + + //=============// + // constructor // + //=============// + //region + + private GlDhDebugWireframeRenderer() { } + + public void init() + { + if (this.init) + { + return; + } + this.init = true; + + this.va = GlAbstractVertexAttribute.create(); + this.va.bind(); + // Pos + this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec3Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 3); + this.basicShader = new GlShaderProgram( + "assets/distanthorizons/shaders/debug/gl/vert.vert", + "assets/distanthorizons/shaders/debug/gl/frag.frag", + "vPosition" + ); + this.createBuffer(); + } + + private void createBuffer() + { + // box vertices + ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES); + boxVerticesBuffer.order(ByteOrder.nativeOrder()); + boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); + boxVerticesBuffer.rewind(); + this.vertexBuffer = new GLVertexBuffer(false); + this.vertexBuffer.bind(); + this.vertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); + + + // outline vertex indexes + ByteBuffer boxOutlineBuffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES); + boxOutlineBuffer.order(ByteOrder.nativeOrder()); + boxOutlineBuffer.asIntBuffer().put(BOX_OUTLINE_INDICES); + boxOutlineBuffer.rewind(); + this.indexBuffer = new GLElementBuffer(false); + this.indexBuffer.uploadBuffer(boxOutlineBuffer, EDhApiGpuUploadMethod.DATA, BOX_OUTLINE_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); + + } + + + //endregion + + + + //===========// + // rendering // + //===========// + //region + + @Override + public void render(RenderParams renderParams) + { + this.init(); + + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); + GLMC.enableDepthTest(); + + this.basicShader.bind(); + this.va.bind(); + this.va.bindBufferToAllBindingPoints(this.vertexBuffer.getId()); + + this.indexBuffer.bind(); + + super.render(renderParams); + + // revert to prevent issues with the following passes + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + } + + @Override + public void renderBox(Box box) + { + Mat4f boxTransform = Mat4f.createTranslateMatrix(box.minPos.x - this.camPosFloatThisFrame.x, box.minPos.y - this.camPosFloatThisFrame.y, box.minPos.z - this.camPosFloatThisFrame.z); + boxTransform.multiply(Mat4f.createScaleMatrix(box.maxPos.x - box.minPos.x, box.maxPos.y - box.minPos.y, box.maxPos.z - box.minPos.z)); + + Mat4f transformMatrix = this.dhMvmProjMatrixThisFrame.copy(); + transformMatrix.multiply(boxTransform); + this.basicShader.setUniform(this.basicShader.getUniformLocation("uTransform"), transformMatrix); + + this.basicShader.setUniform(this.basicShader.getUniformLocation("uColor"), box.color); + + GL32.glDrawElements(GL32.GL_LINES, BOX_OUTLINE_INDICES.length, GL32.GL_UNSIGNED_INT, 0); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhMetaRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhMetaRenderer.java new file mode 100644 index 000000000..eba0bae3d --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhMetaRenderer.java @@ -0,0 +1,474 @@ +package com.seibel.distanthorizons.common.render.openGl; + +import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass; +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer; +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam; +import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; +import com.seibel.distanthorizons.common.render.openGl.glObject.GlDhFramebuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GlQuadElementBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.texture.*; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.DhApiRenderProxy; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL32; + +public class GlDhMetaRenderer implements IDhMetaRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererEventToFile) + .build(); + + public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererEventToFile) + .maxCountPerSecond(4) + .build(); + + public static final GlDhMetaRenderer INSTANCE = new GlDhMetaRenderer(); + + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + private int activeFramebufferId = -1; + private int activeColorTextureId = -1; + private int activeDepthTextureId = -1; + private int textureWidth; + private int textureHeight; + + + // framebuffer and texture ID's for this renderer + private IDhApiFramebuffer framebuffer; + /** will be null if MC's framebuffer is being used since MC already has a color texture */ + @Nullable + private GlDhColorTexture nullableColorTexture; + private GlDhDepthTexture depthTexture; + /** + * If true the {@link GlDhMetaRenderer#framebuffer} is the same as MC's. + * This should only be true in the case of Optifine so LODs won't be overwritten when shaders are enabled. + */ + private boolean usingMcFramebuffer = false; + + private boolean renderObjectsCreated = false; + /** used in case there's an API override */ + public IDhApiShaderProgram shaderProgramForThisFrame; + + + + //============// + // pre render // + //============// + //region + + @Override + public void runRenderPassSetup(RenderParams renderParams) + { + boolean firstPass = + (renderParams.renderPass == EDhApiRenderPass.OPAQUE + || renderParams.renderPass == EDhApiRenderPass.OPAQUE_AND_TRANSPARENT); + + if (!this.renderObjectsCreated) + { + boolean setupSuccess = this.createRenderObjects(); + if (!setupSuccess) + { + // shouldn't normally happen, but just in case + return; + } + + this.renderObjectsCreated = true; + } + + this.shaderProgramForThisFrame = GlDhTerrainShaderProgram.INSTANCE; + IDhApiShaderProgram lodShaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); + if (lodShaderProgramOverride != null && this.shaderProgramForThisFrame.overrideThisFrame()) + { + this.shaderProgramForThisFrame = lodShaderProgramOverride; + } + + + this.setGLState(renderParams, firstPass); + + GlDhTerrainShaderProgram.INSTANCE.quadIBO.bind(); + this.bindLightmap(renderParams.lightmap); + } + private void setGLState( + DhApiRenderParam renderEventParam, + boolean firstPass) + { + //===================// + // framebuffer setup // + //===================// + + // get the active framebuffer + IDhApiFramebuffer framebuffer = this.framebuffer; + IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class); + if (framebufferOverride != null && framebufferOverride.overrideThisFrame()) + { + framebuffer = framebufferOverride; + } + this.setActiveFramebufferId(framebuffer.getId()); + framebuffer.bind(); + + + + //==========// + // bindings // + //==========// + + // by default draw everything as triangles + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + + GLMC.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA); + GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ZERO); + + GL32.glDisable(GL32.GL_SCISSOR_TEST); + + // Enable depth test and depth mask + GLMC.enableDepthTest(); + GLMC.glDepthFunc(GL32.GL_LESS); + GLMC.enableDepthMask(); + + // This is required for MC versions 1.21.5+ + // due to MC updating the lightmap by changing the viewport size + GL32.glViewport(0, 0, this.textureWidth, this.textureHeight); + + this.shaderProgramForThisFrame.bind(); + + + + //==========// + // uniforms // + //==========// + + IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); + if (shaderProgramOverride != null) + { + shaderProgramOverride.fillUniformData(renderEventParam); + } + + this.shaderProgramForThisFrame.fillUniformData(renderEventParam); + + + + //===============// + // texture setup // + //===============// + + // resize the textures if needed + if (MC_RENDER.getTargetFramebufferViewportWidth() != this.textureWidth + || MC_RENDER.getTargetFramebufferViewportHeight() != this.textureHeight) + { + // just resizing the textures doesn't work when Optifine is present, + // so recreate the textures with the new size instead + this.createAndBindTextures(); + } + + + // set the active textures + int depthTextureId = this.depthTexture.getTextureId(); + this.setActiveDepthTextureId(depthTextureId); + + if (this.nullableColorTexture != null) + { + int colorTextureId = this.nullableColorTexture.getTextureId(); + this.setActiveColorTextureId(colorTextureId); + } + else + { + // get MC's color texture + int colorTextureId = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + this.setActiveColorTextureId(colorTextureId); + } + + + // needs to be fired after all the textures have been created/bound + boolean clearTextures = !ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeTextureClearEvent.class, renderEventParam); + if (clearTextures) + { + GL32.glClearDepth(1.0); + + float[] clearColorValues = new float[4]; + GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues); + GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 1.0f); + + if (this.usingMcFramebuffer && framebufferOverride == null) + { + // Due to using MC/Optifine's framebuffer we need to re-bind the depth texture, + // otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues + framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EGlDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); + + + // don't clear the color texture, that removes the sky + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + } + else if (firstPass) + { + GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); + } + } + } + + private boolean createRenderObjects() + { + if (this.renderObjectsCreated) + { + LOGGER.warn("Renderer setup called but it has already completed setup!"); + return false; + } + + // GLProxy should have already been created by this point, but just in case create it now + GLProxy.getInstance(); + + + + LOGGER.info("Setting up renderer"); + + GlDhTerrainShaderProgram.INSTANCE.quadIBO = new GlQuadElementBuffer(); + GlDhTerrainShaderProgram.INSTANCE.quadIBO.reserve(LodQuadBuilder.getMaxBufferByteSize()); + + + // create or get the frame buffer + if (AbstractOptifineAccessor.optifinePresent()) + { + // use MC/Optifine's default Framebuffer so shaders won't remove the LODs + int currentFramebufferId = MC_RENDER.getTargetFramebuffer(); + this.framebuffer = new GlDhFramebuffer(currentFramebufferId); + this.usingMcFramebuffer = true; + } + else + { + // normal use case + this.framebuffer = new GlDhFramebuffer(); + this.usingMcFramebuffer = false; + } + + // create and bind the necessary textures + this.createAndBindTextures(); + + if(this.framebuffer.getStatus() != GL32.GL_FRAMEBUFFER_COMPLETE) + { + // This generally means something wasn't bound, IE missing either the color or depth texture + LOGGER.warn("Framebuffer ["+this.framebuffer.getId()+"] isn't complete."); + return false; + } + + + + LOGGER.info("Renderer setup complete"); + return true; + } + + @SuppressWarnings( "deprecation" ) // done to ignore DhApiColorDepthTextureCreatedEvent + private void createAndBindTextures() + { + int oldWidth = this.textureWidth; + int oldHeight = this.textureHeight; + this.textureWidth = MC_RENDER.getTargetFramebufferViewportWidth(); + this.textureHeight = MC_RENDER.getTargetFramebufferViewportHeight(); + + DhApiTextureCreatedParam textureCreatedParam = new DhApiTextureCreatedParam( + oldWidth, oldHeight, + this.textureWidth, this.textureHeight + ); + + + // DhApiColorDepthTextureCreatedEvent needs to be kept around since old versions of Iris need it + ApiEventInjector.INSTANCE.fireAllEvents(DhApiColorDepthTextureCreatedEvent.class, new DhApiColorDepthTextureCreatedEvent.EventParam(textureCreatedParam)); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeColorDepthTextureCreatedEvent.class, textureCreatedParam); + + + // also update the framebuffer override if present + IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class); + + + this.depthTexture = new GlDhDepthTexture(this.textureWidth, this.textureHeight, EGlDhDepthBufferFormat.DEPTH32F); + this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EGlDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); + if (framebufferOverride != null) + { + framebufferOverride.addDepthAttachment(this.depthTexture.getTextureId(), EGlDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); + } + + + // if we are using MC's frame buffer, a color texture is already present and shouldn't need to be bound + if (!this.usingMcFramebuffer) + { + this.nullableColorTexture = GlDhColorTexture.builder() + .setDimensions(this.textureWidth, this.textureHeight) + .setInternalFormat(EGlDhInternalTextureFormat.RGBA8) + .setPixelType(EGlDhPixelType.UNSIGNED_BYTE) + .setPixelFormat(EGlDhPixelFormat.RGBA) + .build(); + + this.framebuffer.addColorAttachment(0, this.nullableColorTexture.getTextureId()); + if (framebufferOverride != null) + { + framebufferOverride.addColorAttachment(0, this.nullableColorTexture.getTextureId()); + } + } + else + { + this.nullableColorTexture = null; + } + + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterColorDepthTextureCreatedEvent.class, textureCreatedParam); + } + + //endregion + + + + //=============// + // post render // + //=============// + //region + + @Override + public void runRenderPassCleanup(RenderParams renderParams) + { + boolean runningDeferredPass = (renderParams.renderPass == EDhApiRenderPass.TRANSPARENT); + if (!runningDeferredPass) + { + //===================// + // optifine clean up // + //===================// + + if (this.usingMcFramebuffer) + { + // If MC's framebuffer is being used the depth needs to be cleared to prevent rendering on top of MC. + // This should only happen when Optifine shaders are being used. + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + } + } + + + this.unbindLightmap(); + GlDhTerrainShaderProgram.INSTANCE.quadIBO.unbind(); + this.shaderProgramForThisFrame.unbind(); + } + + @Override + public void applyToMcTexture(RenderParams renderParams) { GlDhApplyShader.INSTANCE.render(renderParams); } + + //endregion + + + + //================// + // clear textures // + //================// + //region + + @Override + public void clearDhDepthAndColorTextures(RenderParams renderParams) + { + IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class); + + boolean firstPass = + (renderParams.renderPass == EDhApiRenderPass.OPAQUE + || renderParams.renderPass == EDhApiRenderPass.OPAQUE_AND_TRANSPARENT); + + + + // needs to be fired after all the textures have been created/bound + boolean clearTextures = !ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeTextureClearEvent.class, renderParams); + if (clearTextures) + { + GL32.glClearDepth(1.0); + + float[] clearColorValues = new float[4]; + GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues); + GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 1.0f); + + if (this.usingMcFramebuffer + && framebufferOverride == null) + { + //// Due to using MC/Optifine's framebuffer we need to re-bind the depth texture, + //// otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues + //this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); + + + // don't clear the color texture, that removes the sky + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + } + else if (firstPass) + { + GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); + } + } + + } + + //endregion + + + + //===============// + // API functions // + //===============// + //region + + public void setActiveFramebufferId(int id) { this.activeFramebufferId = id; } + /** @return -1 if no frame buffer has been bound yet */ + public int getActiveFramebufferId() { return this.activeFramebufferId; } + + public void setActiveColorTextureId(int id) + { + this.activeColorTextureId = id; + DhApiRenderProxy.activeOpenGlDhColorTextureId = id; + } + /** @return -1 if no texture has been bound yet */ + public int getActiveColorTextureId() { return this.activeColorTextureId; } + + public void setActiveDepthTextureId(int id) + { + this.activeDepthTextureId = id; + DhApiRenderProxy.activeOpenGlDhDepthTextureId = id; + } + /** @return -1 if no texture has been bound yet */ + public int getActiveDepthTextureId() { return this.activeDepthTextureId; } + + //endregion + + + //================// + // helper methods // + //================// + //region + + public void bindLightmap(ILightMapWrapper lightMapWrapper) + { + LightMapWrapper lightMap = (LightMapWrapper)lightMapWrapper; + GLMC.glActiveTexture(GL32.GL_TEXTURE0 + LightMapWrapper.GL_BOUND_INDEX); + GLMC.glBindTexture(lightMap.getOpenGlId()); + } + + public void unbindLightmap() + { + // strange that we don't call "glActiveTexture" here but since it's working James isn't going to change it right now (2026-03-10) + GLMC.glBindTexture(0); + } + + //endregion + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhRenderApiDefinition.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhRenderApiDefinition.java new file mode 100644 index 000000000..7c6f0afdc --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhRenderApiDefinition.java @@ -0,0 +1,66 @@ +package com.seibel.distanthorizons.common.render.openGl; + +import com.seibel.distanthorizons.common.render.openGl.generic.GlGenericObjectRenderer; +import com.seibel.distanthorizons.common.render.openGl.generic.GlGenericObjectVertexContainer; +import com.seibel.distanthorizons.common.render.openGl.glObject.GlDummyUniformData; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.fade.GlDhFarFadeRenderer; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.fade.GlVanillaFadeRenderer; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.fog.GlDhFogRenderer; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao.GlDhSSAORenderer; +import com.seibel.distanthorizons.common.render.openGl.test.GlTestTriangleRenderer; +import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*; + +public class GlDhRenderApiDefinition extends AbstractDhRenderApiDefinition +{ + //=========// + // getters // + //=========// + //region + + public String getApiName() { return "OpenGL"; } + + //endregion + + + + //============// + // singletons // + //============// + //region + + @Override public IDhMetaRenderer getMetaRenderer() { return GlDhMetaRenderer.INSTANCE; } + @Override public IDhTerrainRenderer getTerrainRenderer() { return GlDhTerrainShaderProgram.INSTANCE; } + @Override public IDhSsaoRenderer getSsaoRenderer() { return GlDhSSAORenderer.INSTANCE; } + @Override public IDhFogRenderer getFogRenderer() { return GlDhFogRenderer.INSTANCE; } + @Override public IDhFarFadeRenderer getFarFadeRenderer() { return GlDhFarFadeRenderer.INSTANCE; } + @Override public AbstractDebugWireframeRenderer getDebugWireframeRenderer() { return GlDhDebugWireframeRenderer.INSTANCE; } + + @Override public IDhVanillaFadeRenderer getVanillaFadeRenderer() { return GlVanillaFadeRenderer.INSTANCE; } + @Override public IDhTestTriangleRenderer getTestTriangleRenderer() { return GlTestTriangleRenderer.INSTANCE; } + + //endregion + + + + //===========// + // factories // + //===========// + //region + + @Override public IDhGenericRenderer createGenericRenderer() { return GlGenericObjectRenderer.INSTANCE; } + + @Override public IVertexBufferWrapper createVboWrapper(String name) { return new GLVertexBuffer(); } + @Override public ILodContainerUniformBufferWrapper createLodContainerUniformWrapper() { return new GlDummyUniformData(); } + @Override public IDhGenericObjectVertexBufferContainer createGenericVboContainer() { return new GlGenericObjectVertexContainer(); } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainShaderProgram.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainShaderProgram.java new file mode 100644 index 000000000..2d051735a --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainShaderProgram.java @@ -0,0 +1,387 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl; + +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GlQuadElementBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexAttributePostGL43; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexAttributePreGL43; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer; +import com.seibel.distanthorizons.common.render.openGl.util.vertexFormat.GlLodVertexFormat; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.objects.SortedArraySet; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import org.lwjgl.opengl.GL32; + +/** + * Handles rendering the normal LOD terrain. + * @see LodQuadBuilder + */ +public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiShaderProgram, IDhTerrainRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererEventToFile) + .build(); + + public static final GlDhTerrainShaderProgram INSTANCE = new GlDhTerrainShaderProgram(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class); + + + + public GlQuadElementBuffer quadIBO = null; + public final GlAbstractVertexAttribute vao; + + // uniforms // + //region + + public int uCombinedMatrix = -1; + public int uModelOffset = -1; + public int uWorldYOffset = -1; + + public int uMircoOffset = -1; + public int uEarthRadius = -1; + public int uLightMap = -1; + + // fragment shader uniforms + public int uClipDistance = -1; + public int uDitherDhRendering = -1; + + // Noise Uniforms + public int uNoiseEnabled = -1; + public int uNoiseSteps = -1; + public int uNoiseIntensity = -1; + public int uNoiseDropoff = -1; + + // Debug Uniform + public int uIsWhiteWorld = -1; + + //endregion + + + + //=============// + // constructor // + //=============// + //region + + private GlDhTerrainShaderProgram() + { + super( + "assets/distanthorizons/shaders/shared/gl/standard.vert", + "assets/distanthorizons/shaders/shared/gl/flat_shaded.frag", + new String[]{"vPosition", "color"} + ); + + this.uCombinedMatrix = this.getUniformLocation("uCombinedMatrix"); + this.uModelOffset = this.getUniformLocation("uModelOffset"); + this.uWorldYOffset = this.getUniformLocation("uWorldYOffset"); + this.uDitherDhRendering = this.getUniformLocation("uDitherDhRendering"); + this.uMircoOffset = this.getUniformLocation("uMircoOffset"); + this.uEarthRadius = this.getUniformLocation("uEarthRadius"); + + this.uLightMap = this.getUniformLocation("uLightMap"); + + // Fog/Clip Uniforms + this.uClipDistance = this.getUniformLocation("uClipDistance"); + + // Noise Uniforms + this.uNoiseEnabled = this.getUniformLocation("uNoiseEnabled"); + this.uNoiseSteps = this.getUniformLocation("uNoiseSteps"); + this.uNoiseIntensity = this.getUniformLocation("uNoiseIntensity"); + this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff"); + + // Debug Uniform + this.uIsWhiteWorld = this.getUniformLocation("uIsWhiteWorld"); + + + if (GLProxy.getInstance().vertexAttributeBufferBindingSupported) + { + this.vao = new GlVertexAttributePostGL43(); // also binds AbstractVertexAttribute + } + else + { + this.vao = new GlVertexAttributePreGL43(); // also binds AbstractVertexAttribute + } + this.vao.bind(); + + // short: x, y, z, meta + // meta: byte skylight, byte blocklight, byte microOffset + this.vao.setVertexAttribute(0, 0, GlVertexPointer.addUnsignedShortsPointer(4, false, true)); + // byte: r, g, b, a + this.vao.setVertexAttribute(0, 1, GlVertexPointer.addUnsignedBytesPointer(4, true, false)); + // byte: iris material ID, normal index, 2 spacers + this.vao.setVertexAttribute(0, 2, GlVertexPointer.addUnsignedBytesPointer(4, true, true)); + + try + { + int vertexByteCount = GlLodVertexFormat.DH_VERTEX_FORMAT.getByteSize(); + this.vao.completeAndCheck(vertexByteCount); + } + catch (RuntimeException e) + { + System.out.println(GlLodVertexFormat.DH_VERTEX_FORMAT); + throw e; + } + + } + + //endregion + + + + //=============// + // API methods // + //=============// + //region + + @Override + public void bind() + { + super.bind(); + this.vao.bind(); + } + @Override + public void unbind() + { + super.unbind(); + this.vao.unbind(); + } + + @Override + public void free() + { + this.vao.free(); + super.free(); + } + + @Override + public void bindVertexBuffer(int vbo) { this.vao.bindBufferToAllBindingPoints(vbo); } + + @Override + public void fillUniformData(DhApiRenderParam renderParameters) + { + Mat4f combinedMatrix = new Mat4f(renderParameters.dhProjectionMatrix); + combinedMatrix.multiply(renderParameters.dhModelViewMatrix); + + super.bind(); + + // uniforms + this.setUniform(this.uCombinedMatrix, combinedMatrix); + this.setUniform(this.uMircoOffset, 0.01f); // 0.01 block offset + + this.setUniform(this.uLightMap, LightMapWrapper.GL_BOUND_INDEX); + + this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset); + + this.setUniform(this.uDitherDhRendering, Config.Client.Advanced.Graphics.Quality.ditherDhFade.get()); + + float curveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get(); + if (curveRatio < -1.0f || curveRatio > 1.0f) + { + curveRatio = /*6371KM*/ 6371000.0f / curveRatio; + } + else + { + // disable curvature if the config value is between -1 and 1 + curveRatio = 0.0f; + } + this.setUniform(this.uEarthRadius, curveRatio); + + // Noise Uniforms + this.setUniform(this.uNoiseEnabled, Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get()); + this.setUniform(this.uNoiseSteps, Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get()); + this.setUniform(this.uNoiseIntensity, Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get()); + this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get()); + + // Debug + this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get()); + + // Clip Uniform + float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks(); + if (!Config.Client.Advanced.Debugging.lodOnlyMode.get()) + { + // this added value prevents the near clip plane and discard circle from touching, which looks bad + dhNearClipDistance += 16f; + } + this.setUniform(this.uClipDistance, dhNearClipDistance); + } + + @Override + public void setModelOffsetPos(DhApiVec3f modelOffsetPos) { this.setUniform(this.uModelOffset, new Vec3f(modelOffsetPos)); } + + @Override + public int getId() { return this.id; } + + /** The base DH render program should always render */ + @Override + public boolean overrideThisFrame() { return true; } + + //endregion + + + + //===========// + // rendering // + //===========// + //region + + @Override + public void render(RenderParams renderEventParam, boolean opaquePass, SortedArraySet bufferContainers, IProfilerWrapper profiler) + { + //=======================// + // debug wireframe setup // + //=======================// + + boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); + if (renderWireframe) + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); + GLMC.disableFaceCulling(); + } + else + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + } + + if (!opaquePass) + { + GLMC.enableBlend(); + GLMC.enableDepthTest(); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + } + else + { + GLMC.disableBlend(); + } + + + + + //===========// + // rendering // + //===========// + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); + + if (IRIS_ACCESSOR != null) + { + // done to fix a bug with Iris where face culling isn't properly set or reverted in the MC state manager + // which causes Sodium to render some water chunks with their normals inverted + // https://github.com/IrisShaders/Iris/issues/2582 + // https://github.com/IrisShaders/Iris/blob/1.21.9/common/src/main/java/net/irisshaders/iris/compat/dh/LodRendererEvents.java#L346 + GLMC.enableFaceCulling(); + } + + + if (bufferContainers != null) + { + for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) + { + LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); + + // set uniforms and fire events + { + Vec3d camPos = renderEventParam.exactCameraPosition; + Vec3f modelPos = new Vec3f( + (float) (bufferContainer.minCornerBlockPos.getX() - camPos.x), + (float) (bufferContainer.minCornerBlockPos.getY() - camPos.y), + (float) (bufferContainer.minCornerBlockPos.getZ() - camPos.z)); + + GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bind(); + GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.setModelOffsetPos(modelPos); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); + } + + IVertexBufferWrapper[] vertexBuffers = (opaquePass ? bufferContainer.vbos : bufferContainer.vbosTransparent); + for (int vboIndex = 0; vboIndex < vertexBuffers.length; vboIndex++) + { + GLVertexBuffer vbo = (GLVertexBuffer) vertexBuffers[vboIndex]; + if (vbo == null) + { + continue; + } + + if (vbo.getVertexCount() == 0) + { + continue; + } + + // 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5 + int indexCount = (int)(vbo.getVertexCount() * 1.5); + + vbo.bind(); + GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId()); + GL32.glDrawElements( + GL32.GL_TRIANGLES, + indexCount, + this.quadIBO.getType(), 0); + vbo.unbind(); + } + } + } + + + + //=========================// + // debug wireframe cleanup // + //=========================// + + if (renderWireframe) + { + // default back to GL_FILL since all other rendering uses it + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + } + + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectRenderer.java new file mode 100644 index 000000000..7e38e00e7 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectRenderer.java @@ -0,0 +1,738 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.generic; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLElementBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.jar.EPlatform; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory; +import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; +import com.seibel.distanthorizons.coreapi.ModInfo; +import org.lwjgl.opengl.ARBInstancedArrays; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL33; +import org.lwjgl.system.MemoryUtil; + +import java.awt.*; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Handles rendering generic groups of {@link DhApiRenderableBox}. + * + * @see IDhApiCustomRenderRegister + * @see DhApiRenderableBox + */ +public class GlGenericObjectRenderer implements IDhGenericRenderer +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + private static final DhApiRenderableBoxGroupShading DEFAULT_SHADING = DhApiRenderableBoxGroupShading.getUnshaded(); + + + public static final GlGenericObjectRenderer INSTANCE = new GlGenericObjectRenderer(); + + /** + * Can be used to troubleshoot the renderer. + * If enabled several debug objects will render around (0,150,0). + */ + public static final boolean RENDER_DEBUG_OBJECTS = false; + + + // rendering setup + private boolean init = false; + + private IDhApiGenericObjectShaderProgram instancedShaderProgram; + private IDhApiGenericObjectShaderProgram directShaderProgram; + private GLVertexBuffer boxVertexBuffer; + private GLElementBuffer boxIndexBuffer; + + private boolean instancedRenderingAvailable; + private boolean vertexAttribDivisorSupported; + private boolean instancedArraysSupported; + + + + private final ConcurrentHashMap boxGroupById = new ConcurrentHashMap<>(); + + + + /** A box from 0,0,0 to 1,1,1 */ + private static final float[] BOX_VERTICES = { + //region + // Pos x y z + + // min X, vertical face + 0, 0, 0, + 1, 0, 0, + 1, 1, 0, + 0, 1, 0, + // max X, vertical face + 0, 1, 1, + 1, 1, 1, + 1, 0, 1, + 0, 0, 1, + + // min Z, vertical face + 0, 0, 1, + 0, 0, 0, + 0, 1, 0, + 0, 1, 1, + // max Z, vertical face + 1, 0, 1, + 1, 1, 1, + 1, 1, 0, + 1, 0, 0, + + // min Y, horizontal face + 0, 0, 1, + 1, 0, 1, + 1, 0, 0, + 0, 0, 0, + // max Y, horizontal face + 0, 1, 1, + 1, 1, 1, + 1, 1, 0, + 0, 1, 0, + //endregion + }; + + + private static final int[] BOX_INDICES = { + //region + // min X, vertical face + 2, 1, 0, + 0, 3, 2, + // max X, vertical face + 6, 5, 4, + 4, 7, 6, + + // min Z, vertical face + 10, 9, 8, + 8, 11, 10, + // max Z, vertical face + 14, 13, 12, + 12, 15, 14, + + // min Y, horizontal face + 18, 17, 16, + 16, 19, 18, + // max Y, horizontal face + 20, 21, 22, + 22, 23, 20, + //endregion + }; + + + + //=============// + // constructor // + //=============// + //region + + private GlGenericObjectRenderer() { } + + public void init() + { + if (this.init) + { + return; + } + this.init = true; + + + + //===================================// + // is instanced rendering available? // + //===================================// + + this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported; + this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported; + boolean isMac = (EPlatform.get() == EPlatform.MACOS); + this.instancedRenderingAvailable = (this.vertexAttribDivisorSupported || this.instancedArraysSupported) && !isMac; + if (!this.instancedRenderingAvailable) + { + LOGGER.warn("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering will be slow and some effects may be disabled."); + } + + + + //======================// + // startup the renderer // + //======================// + + this.instancedShaderProgram = new GlGenericObjectShaderProgram(true); + this.directShaderProgram = new GlGenericObjectShaderProgram(false); + + this.createBuffers(); + + if (RENDER_DEBUG_OBJECTS) + { + this.addGenericDebugObjects(); + } + } + private void createBuffers() + { + // box vertices + ByteBuffer boxVerticesBuffer = MemoryUtil.memAlloc(BOX_VERTICES.length * Float.BYTES); + boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); + boxVerticesBuffer.rewind(); + this.boxVertexBuffer = new GLVertexBuffer(false); + this.boxVertexBuffer.bind(); + this.boxVertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); + MemoryUtil.memFree(boxVerticesBuffer); + + // box vertex indexes + ByteBuffer solidIndexBuffer = MemoryUtil.memAlloc(BOX_INDICES.length * Integer.BYTES); + solidIndexBuffer.asIntBuffer().put(BOX_INDICES); + solidIndexBuffer.rewind(); + this.boxIndexBuffer = new GLElementBuffer(false); + this.boxIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); + this.boxIndexBuffer.bind(); + MemoryUtil.memFree(solidIndexBuffer); + } + private void addGenericDebugObjects() + { + GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE; + + + // single giant box + IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox( + ModInfo.NAME + ":CyanChunkBox", + new DhApiRenderableBox( + new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16), + new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125), + EDhApiBlockMaterial.WATER) + ); + singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.add(singleGiantBoxGroup); + + + // single slender box + IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox( + ModInfo.NAME + ":GreenBeacon", + new DhApiRenderableBox( + new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32), + new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125), + EDhApiBlockMaterial.ILLUMINATED) + ); + singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.add(singleTallBoxGroup); + + + // absolute box group + ArrayList absBoxList = new ArrayList<>(); + for (int i = 0; i < 18; i++) + { + absBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25), + new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()), + EDhApiBlockMaterial.LAVA + ) + ); + } + IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList); + this.add(absolutePosBoxGroup); + + + // relative box group + ArrayList relBoxList = new ArrayList<>(); + for (int i = 0; i < 8; i+=2) + { + relBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1), + new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()), + EDhApiBlockMaterial.METAL + ) + ); + } + IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup( + ModInfo.NAME + ":MovingMagentaGroup", + new DhApiVec3d(24, 140, 24), + relBoxList); + relativePosBoxGroup.setPreRenderFunc((event) -> + { + DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos(); + pos.x += event.partialTicks / 2; + pos.x %= 32; + relativePosBoxGroup.setOriginBlockPos(pos); + }); + this.add(relativePosBoxGroup); + + + // massive relative box group + ArrayList massRelBoxList = new ArrayList<>(); + for (int x = 0; x < 50*2; x+=2) + { + for (int z = 0; z < 50*2; z+=2) + { + massRelBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z), + new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()), + EDhApiBlockMaterial.TERRACOTTA + ) + ); + } + } + IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup( + ModInfo.NAME + ":MassRedGroup", + new DhApiVec3d(-25, 140, 0), + massRelBoxList); + massRelativePosBoxGroup.setPreRenderFunc((event) -> + { + DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos(); + blockPos.y += event.partialTicks / 4; + if (blockPos.y > 150f) + { + blockPos.y = 140f; + + Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED; + massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; }); + massRelativePosBoxGroup.triggerBoxChange(); + } + + massRelativePosBoxGroup.setOriginBlockPos(blockPos); + }); + this.add(massRelativePosBoxGroup); + } + + //endregion + + + + //==============// + // registration // + //==============// + //region + + @Override + public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException + { + if (!(iBoxGroup instanceof RenderableBoxGroup)) + { + throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"]."); + } + RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup; + + + long id = boxGroup.getId(); + if (this.boxGroupById.containsKey(id)) + { + throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present."); + } + + this.boxGroupById.put(id, boxGroup); + } + + @Override + public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); } + + public void clear() { this.boxGroupById.clear(); } + + //endregion + + + + //===========// + // rendering // + //===========// + //region + + /** + * @param renderingWithSsao + * if true that means this render call is happening before the SSAO pass + * and any objects rendered in this pass will have SSAO applied to them. + */ + @Override + public void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao) + { + // render setup // + profiler.push("setup"); + + this.init(); + + boolean useInstancedRendering = this.instancedRenderingAvailable + && Config.Client.Advanced.Graphics.GenericRendering.enableInstancedRendering.get(); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); + + + boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); + if (renderWireframe) + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); + GLMC.disableFaceCulling(); + } + else + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + } + + GLMC.enableBlend(); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + + IDhApiGenericObjectShaderProgram shaderProgram = useInstancedRendering ? this.instancedShaderProgram : this.directShaderProgram; + IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class); + if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + { + shaderProgram = shaderProgramOverride; + } + + shaderProgram.bind(renderEventParam); + shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId()); + + this.boxIndexBuffer.bind(); + + Vec3d camPos = MC_RENDER.getCameraExactPosition(); + + + + // rendering // + + Collection boxList = this.boxGroupById.values(); + for (RenderableBoxGroup boxGroup : boxList) + { + // validation // + + // shouldn't happen, but just in case + if (boxGroup == null) + { + continue; + } + + // skip boxes that shouldn't render this pass + if (boxGroup.ssaoEnabled != renderingWithSsao) + { + continue; + } + + profiler.popPush("render prep"); + boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired + + // ignore inactive groups + if (!boxGroup.active) + { + continue; + } + + // allow API users to cancel this object's rendering + boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); + if (cancelRendering) + { + continue; + } + + // update instanced data if needed + if (useInstancedRendering) + { + boxGroup.tryUpdateInstancedDataAsync(); + + // skip groups that haven't been uploaded yet + if (boxGroup.vertexBufferContainer.getState() != GlGenericObjectVertexContainer.EState.RENDER) + { + continue; + } + } + + + + // render // + + profiler.popPush("rendering"); + profiler.push(boxGroup.getResourceLocationNamespace()); + profiler.push(boxGroup.getResourceLocationPath()); + if (useInstancedRendering) + { + this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos, profiler); + } + else + { + this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos, profiler); + } + profiler.pop(); // resource path + profiler.pop(); // resource namespace + + boxGroup.postRender(renderEventParam); + } + + + //==========// + // clean up // + //==========// + + profiler.popPush("cleanup"); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); + + if (renderWireframe) + { + // default back to GL_FILL since all other rendering uses it + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + } + + shaderProgram.unbind(); + + profiler.pop(); + } + + //endregion + + + + //=====================// + // instanced rendering // + //=====================// + //region + + private void renderBoxGroupInstanced( + IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam, + RenderableBoxGroup boxGroup, Vec3d camPos, + IProfilerWrapper profiler) + { + // update instance data // + + profiler.push("vertex setup"); + + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DEFAULT_SHADING; + } + + shaderProgram.fillIndirectUniformData( + renderEventParam, + shading, boxGroup, + camPos); + + + + // Bind instance data // + profiler.popPush("binding"); + + GlGenericObjectVertexContainer container = (GlGenericObjectVertexContainer)(boxGroup.vertexBufferContainer); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.color); + GL32.glEnableVertexAttribArray(1); + GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0); + this.vertexAttribDivisor(1, 1); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.scale); + GL32.glEnableVertexAttribArray(2); + this.vertexAttribDivisor(2, 1); + GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.chunkPos); + GL32.glEnableVertexAttribArray(3); + this.vertexAttribDivisor(3, 1); + GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.subChunkPos); + GL32.glEnableVertexAttribArray(4); + this.vertexAttribDivisor(4, 1); + GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.material); + GL32.glEnableVertexAttribArray(5); + this.vertexAttribDivisor(5, 1); + GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0); + + + // Draw instanced + profiler.popPush("render"); + if (container.uploadedBoxCount > 0) + { + GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, container.uploadedBoxCount); + } + + + // Clean up + profiler.popPush("cleanup"); + + GL32.glDisableVertexAttribArray(1); + GL32.glDisableVertexAttribArray(2); + GL32.glDisableVertexAttribArray(3); + GL32.glDisableVertexAttribArray(4); + GL32.glDisableVertexAttribArray(5); + + profiler.pop(); + } + /** + * Clean way to handle both {@link GL33#glVertexAttribDivisor} and {@link ARBInstancedArrays#glVertexAttribDivisorARB} + * based on which one is supported. + */ + private void vertexAttribDivisor(int index, int divisor) + { + if (this.vertexAttribDivisorSupported) + { + GL33.glVertexAttribDivisor(index, divisor); + } + else if(this.instancedArraysSupported) + { + ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor); + } + else + { + throw new IllegalStateException("Instanced rendering isn't supported by this machine. Direct rendering should have been used instead."); + } + } + + //endregion + + + + //==================// + // direct rendering // + //==================// + //region + + private void renderBoxGroupDirect( + IDhApiGenericObjectShaderProgram shaderProgram, + DhApiRenderParam renderEventParam, + RenderableBoxGroup boxGroup, Vec3d camPos, + IProfilerWrapper profiler) + { + profiler.popPush("shared uniforms"); + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DhApiRenderableBoxGroupShading.getUnshaded(); + } + + shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos); + + for (int i = 0; i < boxGroup.size(); i++) + { + try + { + DhApiRenderableBox box = boxGroup.get(i); + if (box != null) + { + profiler.popPush("direct uniforms"); + shaderProgram.fillDirectUniformData(renderEventParam, boxGroup, box, camPos); + + profiler.popPush("render"); + GL32.glDrawElements(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0); + } + } + catch (IndexOutOfBoundsException e) + { + // Concurrency issue, the list was modified while rendering + // this can probably be ignored. + // However, if it does become a problem we can add locks to the box group. + break; + } + } + + profiler.pop(); + } + + //endregion + + + + //=========// + // getters // + //=========// + //region + + /** @throws IllegalStateException if {@link #init()} function hasn't been called yet */ + public boolean getInstancedRenderingAvailable() throws IllegalStateException + { + if (!this.init) + { + throw new IllegalStateException("GL initialization hasn't been completed."); + } + + return this.instancedRenderingAvailable; + } + + //endregion + + + + //=========// + // F3 menu // + //=========// + //region + + public String getVboRenderDebugMenuString() + { + // get counts + int totalGroupCount = this.boxGroupById.size(); + int totalBoxCount = 0; + + int activeGroupCount = 0; + int activeBoxCount = 0; + + for (long key : this.boxGroupById.keySet()) + { + RenderableBoxGroup renderGroup = this.boxGroupById.get(key); + if (renderGroup.active) + { + activeGroupCount++; + activeBoxCount += renderGroup.size(); + } + totalBoxCount += renderGroup.size(); + } + + + return "Generic Obj #: " + F3Screen.NUMBER_FORMAT.format(activeGroupCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalGroupCount) + ", " + + "Cube #: " + F3Screen.NUMBER_FORMAT.format(activeBoxCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalBoxCount); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectShaderProgram.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectShaderProgram.java new file mode 100644 index 000000000..768c8a7c6 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectShaderProgram.java @@ -0,0 +1,231 @@ +package com.seibel.distanthorizons.common.render.openGl.generic; + +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer; +import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3f; + +public class GlGenericObjectShaderProgram extends GlShaderProgram implements IDhApiGenericObjectShaderProgram +{ + public static final String VERTEX_SHADER_INSTANCED_PATH = "assets/distanthorizons/shaders/generic/gl/instanced/vert.vert"; + public static final String VERTEX_SHADER_DIRECT_PATH = "assets/distanthorizons/shaders/generic/gl/direct/vert.vert"; + public static final String FRAGMENT_SHADER_INSTANCED_PATH = "assets/distanthorizons/shaders/generic/gl/instanced/frag.frag"; + public static final String FRAGMENT_SHADER_DIRECT_PATH = "assets/distanthorizons/shaders/generic/gl/direct/frag.frag"; + + public final GlAbstractVertexAttribute va; + + + // shader uniforms + private final int directShaderTransformUniform; + private final int directShaderColorUniform; + + private final int instancedShaderOffsetChunkUniform; + private final int instancedShaderOffsetSubChunkUniform; + private final int instancedShaderCameraChunkPosUniform; + private final int instancedShaderCameraSubChunkPosUniform; + private final int instancedShaderProjectionModelViewMatrixUniform; + + private final int lightMapUniform; + private final int skyLightUniform; + private final int blockLightUniform; + + private final int northShadingUniform; + private final int southShadingUniform; + private final int eastShadingUniform; + private final int westShadingUniform; + private final int topShadingUniform; + private final int bottomShadingUniform; + + + + //=============// + // constructor // + //=============// + + public GlGenericObjectShaderProgram(boolean useInstancedRendering) + { + super( + useInstancedRendering ? VERTEX_SHADER_INSTANCED_PATH : VERTEX_SHADER_DIRECT_PATH, + useInstancedRendering ? FRAGMENT_SHADER_INSTANCED_PATH : FRAGMENT_SHADER_DIRECT_PATH, + "vPosition" + ); + + this.va = GlAbstractVertexAttribute.create(); + this.va.bind(); + // Pos + this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec3Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 3); + + this.directShaderTransformUniform = this.tryGetUniformLocation("uTransform"); + this.directShaderColorUniform = this.tryGetUniformLocation("uColor"); + + this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation("uOffsetChunk"); + this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation("uOffsetSubChunk"); + this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation("uCameraPosChunk"); + this.instancedShaderCameraSubChunkPosUniform = this.tryGetUniformLocation("uCameraPosSubChunk"); + this.instancedShaderProjectionModelViewMatrixUniform = this.tryGetUniformLocation("uProjectionMvm"); + + this.lightMapUniform = this.getUniformLocation("uLightMap"); + this.skyLightUniform = this.getUniformLocation("uSkyLight"); + this.blockLightUniform = this.getUniformLocation("uBlockLight"); + this.northShadingUniform = this.getUniformLocation("uNorthShading"); + this.southShadingUniform = this.getUniformLocation("uSouthShading"); + this.eastShadingUniform = this.getUniformLocation("uEastShading"); + this.westShadingUniform = this.getUniformLocation("uWestShading"); + this.topShadingUniform = this.getUniformLocation("uTopShading"); + this.bottomShadingUniform = this.getUniformLocation("uBottomShading"); + + } + + + + //=========// + // methods // + //=========// + + @Override + public void bind(DhApiRenderParam renderEventParam) + { + super.bind(); + this.va.bind(); + } + @Override + public void unbind() + { + super.unbind(); + this.va.unbind(); + } + + @Override + public void free() + { + this.va.free(); + super.free(); + } + + @Override + public void bindVertexBuffer(int vbo) { this.va.bindBufferToAllBindingPoints(vbo); } + + @Override + public void fillIndirectUniformData( + DhApiRenderParam renderParameters, + DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, + DhApiVec3d camPos + ) + { + Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix); + projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix); + + super.bind(); + + + + + this.setUniform(this.instancedShaderOffsetChunkUniform, + new DhApiVec3i( + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + )); + this.setUniform(this.instancedShaderOffsetSubChunkUniform, + new Vec3f( + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + )); + + this.setUniform(this.instancedShaderCameraChunkPosUniform, + new DhApiVec3i( + LodUtil.getChunkPosFromDouble(camPos.x), + LodUtil.getChunkPosFromDouble(camPos.y), + LodUtil.getChunkPosFromDouble(camPos.z) + )); + this.setUniform(this.instancedShaderCameraSubChunkPosUniform, + new Vec3f( + LodUtil.getSubChunkPosFromDouble(camPos.x), + LodUtil.getSubChunkPosFromDouble(camPos.y), + LodUtil.getSubChunkPosFromDouble(camPos.z) + )); + + this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, projectionMvmMatrix); + + this.setUniform(this.lightMapUniform, LightMapWrapper.GL_BOUND_INDEX); + this.setUniform(this.skyLightUniform, boxGroup.getSkyLight()); + this.setUniform(this.blockLightUniform, boxGroup.getBlockLight()); + + + this.setUniform(this.northShadingUniform, shading.north); + this.setUniform(this.southShadingUniform, shading.south); + this.setUniform(this.eastShadingUniform, shading.east); + this.setUniform(this.westShadingUniform, shading.west); + this.setUniform(this.topShadingUniform, shading.top); + this.setUniform(this.bottomShadingUniform, shading.bottom); + + + } + + + @Override + public void fillSharedDirectUniformData( + DhApiRenderParam renderParameters, + DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, + DhApiVec3d camPos) + { + + this.setUniform(this.lightMapUniform, LightMapWrapper.GL_BOUND_INDEX); + this.setUniform(this.skyLightUniform, boxGroup.getSkyLight()); + this.setUniform(this.blockLightUniform, boxGroup.getBlockLight()); + + + this.setUniform(this.northShadingUniform, shading.north); + this.setUniform(this.southShadingUniform, shading.south); + this.setUniform(this.eastShadingUniform, shading.east); + this.setUniform(this.westShadingUniform, shading.west); + this.setUniform(this.topShadingUniform, shading.top); + this.setUniform(this.bottomShadingUniform, shading.bottom); + + } + + public void fillDirectUniformData( + DhApiRenderParam renderParameters, + IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box, + DhApiVec3d camPos) + { + Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix); + projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix); + + Mat4f boxTransform = Mat4f.createTranslateMatrix( + (float) (box.minPos.x + boxGroup.getOriginBlockPos().x - camPos.x), + (float) (box.minPos.y + boxGroup.getOriginBlockPos().y - camPos.y), + (float) (box.minPos.z + boxGroup.getOriginBlockPos().z - camPos.z)); + boxTransform.multiply(Mat4f.createScaleMatrix( + (float) (box.maxPos.x - box.minPos.x), + (float) (box.maxPos.y - box.minPos.y), + (float) (box.maxPos.z - box.minPos.z))); + projectionMvmMatrix.multiply(boxTransform); + this.setUniform(this.directShaderTransformUniform, projectionMvmMatrix); + + this.setUniform(this.directShaderColorUniform, box.color); + + } + + + + @Override + public int getId() { return this.id; } + + /** The base DH render program should always render */ + @Override + public boolean overrideThisFrame() { return true; } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectVertexContainer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectVertexContainer.java new file mode 100644 index 000000000..48eb20f31 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectVertexContainer.java @@ -0,0 +1,180 @@ +package com.seibel.distanthorizons.common.render.openGl.generic; + +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; +import org.lwjgl.opengl.GL32; + +import java.awt.*; +import java.util.List; + +/** + * For use by {@link RenderableBoxGroup} + * + * @see RenderableBoxGroup + */ +public class GlGenericObjectVertexContainer implements IDhGenericObjectVertexBufferContainer +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + + public int chunkPos = 0; + public int subChunkPos = 0; + public int scale = 0; + public int color = 0; + public int material = 0; + + public int[] chunkPosData = new int[0]; + public float[] subChunkPosData = new float[0]; + public float[] scalingData = new float[0]; + public float[] colorData = new float[0]; + public int[] materialData = new int[0]; + + public int uploadedBoxCount = 0; + + private EState state = EState.NEW; + @Override + public EState getState() { return this.state; } + @Override + public void setState(EState state) { this.state = state; } + + + + //===========================// + // render building/uploading // + //===========================// + //region + + public void updateVertexData(List uploadBoxList) + { + int boxCount = uploadBoxList.size(); + + + // recreate the data arrays if their size is different + if (this.uploadedBoxCount != boxCount) + { + this.uploadedBoxCount = boxCount; + + this.chunkPosData = new int[boxCount * 3]; // 3 elements XYZ + this.subChunkPosData = new float[boxCount * 3]; // 3 elements XYZ + this.scalingData = new float[boxCount * 3]; // 3 elements XYZ + + this.colorData = new float[boxCount * 4]; // 4 elements, RGBA + this.materialData = new int[boxCount]; + } + + + // transformation / scaling // + for (int i = 0; i < boxCount; i++) + { + DhApiRenderableBox box = uploadBoxList.get(i); + + int dataIndex = i * 3; + + this.chunkPosData[dataIndex] = LodUtil.getChunkPosFromDouble(box.minPos.x); + this.chunkPosData[dataIndex + 1] = LodUtil.getChunkPosFromDouble(box.minPos.y); + this.chunkPosData[dataIndex + 2] = LodUtil.getChunkPosFromDouble(box.minPos.z); + + this.subChunkPosData[dataIndex] = LodUtil.getSubChunkPosFromDouble(box.minPos.x); + this.subChunkPosData[dataIndex + 1] = LodUtil.getSubChunkPosFromDouble(box.minPos.y); + this.subChunkPosData[dataIndex + 2] = LodUtil.getSubChunkPosFromDouble(box.minPos.z); + + this.scalingData[dataIndex] = (float) (box.maxPos.x - box.minPos.x); + this.scalingData[dataIndex + 1] = (float) (box.maxPos.y - box.minPos.y); + this.scalingData[dataIndex + 2] = (float) (box.maxPos.z - box.minPos.z); + } + + + // colors/materials // + for (int i = 0; i < boxCount; i++) + { + DhApiRenderableBox box = uploadBoxList.get(i); + Color color = box.color; + int colorIndex = i * 4; + this.colorData[colorIndex] = color.getRed() / 255.0f; + this.colorData[colorIndex + 1] = color.getGreen() / 255.0f; + this.colorData[colorIndex + 2] = color.getBlue() / 255.0f; + this.colorData[colorIndex + 3] = color.getAlpha() / 255.0f; + + this.materialData[i] = box.material; + } + + this.state = GlGenericObjectVertexContainer.EState.READY_TO_UPLOAD; + } + + public void uploadDataToGpu() + { + this.tryCreateBuffers(); + + // Upload transformation matrices + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.chunkPos); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.chunkPosData, GL32.GL_DYNAMIC_DRAW); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.subChunkPos); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.subChunkPosData, GL32.GL_DYNAMIC_DRAW); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.scale); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.scalingData, GL32.GL_DYNAMIC_DRAW); + + // Upload colors + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.color); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.colorData, GL32.GL_DYNAMIC_DRAW); + + // Upload materials + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.material); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.materialData, GL32.GL_DYNAMIC_DRAW); + + this.state = EState.RENDER; + } + /** needs to be done on the render thread */ + private void tryCreateBuffers() + { + if (this.chunkPos == 0) + { + this.chunkPos = GLMC.glGenBuffers(); + this.subChunkPos = GLMC.glGenBuffers(); + this.scale = GLMC.glGenBuffers(); + this.color = GLMC.glGenBuffers(); + this.material = GLMC.glGenBuffers(); + } + } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + @Override + public void close() + { + tryDeleteBuffer(this.chunkPos); + tryDeleteBuffer(this.subChunkPos); + tryDeleteBuffer(this.scale); + tryDeleteBuffer(this.color); + tryDeleteBuffer(this.material); + } + private static void tryDeleteBuffer(int bufferId) + { + // usually unnecessary, but just in case + if (bufferId != 0) + { + GLMC.glDeleteBuffers(bufferId); + } + } + + //endregion + + + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLProxy.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLProxy.java new file mode 100644 index 000000000..5a3b2af90 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLProxy.java @@ -0,0 +1,347 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGLErrorHandlingMode; +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.jar.EPlatform; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.objects.GLMessages.*; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.coreapi.ModInfo; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GLCapabilities; +import org.lwjgl.opengl.GLUtil; + +import java.io.PrintStream; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A singleton that holds references to different openGL contexts + * and GPU capabilities. + */ +public class GLProxy +{ + public static final DhLogger LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile) + .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat) + .build(); + + public static final Set LOGGED_GL_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap()); + + + + private static GLProxy instance = null; + + + /** Minecraft's GL capabilities */ + public final GLCapabilities glCapabilities; + + public boolean namedObjectSupported = false; // ~OpenGL 4.5 (UNUSED CURRENTLY) + public boolean bufferStorageSupported = false; // ~OpenGL 4.4 + public boolean vertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3 + public boolean instancedArraysSupported = false; + public boolean vertexAttribDivisorSupported = false; // OpenGL 3.3 or newer + + private final EDhApiGpuUploadMethod preferredUploadMethod; + + public final GLMessageBuilder vanillaDebugMessageBuilder = + new GLMessageBuilder( + (type) -> + { + if (type == EGLMessageType.POP_GROUP) + return false; + else if (type == EGLMessageType.PUSH_GROUP) + return false; + else if (type == EGLMessageType.MARKER) + return false; + else + return true; + }, + (severity) -> + { + // notifications can generally be ignored (if they are logged at all) + if (severity == EGLMessageSeverity.NOTIFICATION) + return false; + else + return true; + }, + null + ); + + + + //=============// + // constructor // + //=============// + //region + + private GLProxy() throws IllegalStateException + { + // this must be created on minecraft's render context to work correctly + if (GLFW.glfwGetCurrentContext() == 0L) + { + throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!"); + } + + LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error."); + LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "]."); + + + + + //============================// + // get Minecraft's GL context // + //============================// + + // get Minecraft's capabilities + this.glCapabilities = GL.getCapabilities(); + + // crash the game if the GPU doesn't support OpenGL 3.2 + if (!this.glCapabilities.OpenGL32) + { + String supportedVersionInfo = this.getFailedVersionInfo(this.glCapabilities); + + // See full requirement at above. + String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName() + + " and discovered this GPU doesn't meet the OpenGL requirements. Sorry I couldn't tell you sooner :(\n" + + "Additional info:\n" + supportedVersionInfo; + IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + MC.crashMinecraft(errorMessage, new UnsupportedOperationException("Distant Horizon OpenGL requirements not met")); + } + LOGGER.info("minecraftGlCapabilities:\n" + this.versionInfoToString(this.glCapabilities)); + + if (Config.Client.Advanced.Debugging.OpenGl.overrideVanillaGLLogger.get()) + { + GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, this.vanillaDebugMessageBuilder), true)); + } + + + + //======================// + // get GPU capabilities // + //======================// + + // UNUSED currently + // Check if we can use the named version of all calls, which is available in GL4.5 or after + this.namedObjectSupported = this.glCapabilities.glNamedBufferData != 0L; //Nullptr + + // Check if we can use the Buffer Storage, which is available in GL4.4 or after + this.bufferStorageSupported = this.glCapabilities.glBufferStorage != 0L; // Nullptr + if (!this.bufferStorageSupported) + { + LOGGER.info("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods."); + } + + // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after + this.vertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr + + // used by instanced rendering + this.vertexAttribDivisorSupported = this.glCapabilities.OpenGL33; + // denotes if ARBInstancedArrays.glVertexAttribDivisorARB() is available or not + // can be used as a backup if MC didn't create a GL 3.3+ context + this.instancedArraysSupported = this.glCapabilities.GL_ARB_instanced_arrays; + + // get the best automatic upload method + String vendor = GL32.glGetString(GL32.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION" + if (EPlatform.get() != EPlatform.MACOS) + { + if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE")) + { + // NVIDIA card + this.preferredUploadMethod = this.bufferStorageSupported ? EDhApiGpuUploadMethod.BUFFER_STORAGE : EDhApiGpuUploadMethod.SUB_DATA; + } + else + { + // AMD or Intel card + this.preferredUploadMethod = this.bufferStorageSupported ? EDhApiGpuUploadMethod.BUFFER_STORAGE : EDhApiGpuUploadMethod.DATA; + } + } + else + { + // Mac may have an issue with Buffer Storage, so default to the most basic + // form of uploading + this.preferredUploadMethod = EDhApiGpuUploadMethod.DATA; + } + LOGGER.info("GPU Vendor [" + vendor + "] with OS [" + EPlatform.get().getName() + "], Preferred upload method is [" + this.preferredUploadMethod + "]."); + + + + //==========// + // clean up // + //==========// + + // GLProxy creation success + LOGGER.info(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day."); + } + + //endregion + + + + //=========// + // getters // + //=========// + //region + + public static boolean hasInstance() { return instance != null; } + /** @throws IllegalStateException if the Proxy hasn't been created yet and this is called outside the render thread */ + public static GLProxy getInstance() throws IllegalStateException + { + if (instance == null) + { + instance = new GLProxy(); + } + + return instance; + } + + public EDhApiGpuUploadMethod getGpuUploadMethod() + { + EDhApiGpuUploadMethod uploadOverride = Config.Client.Advanced.Debugging.OpenGl.glUploadMode.get(); + if (uploadOverride == EDhApiGpuUploadMethod.AUTO) + { + return this.preferredUploadMethod; + } + + return uploadOverride; + } + + public static boolean runningOnRenderThread() + { + long currentContext = GLFW.glfwGetCurrentContext(); + return currentContext != 0L; // if the context isn't null, it's the MC context + } + + //endregion + + + + //=========// + // logging // + //=========// + //region + + /** this method is called on the render thread at the point of the GL Error */ + private static void logMessage(GLMessage msg) + { + EDhApiGLErrorHandlingMode errorHandlingMode = Config.Client.Advanced.Debugging.OpenGl.glErrorHandlingMode.get(); + if (errorHandlingMode == EDhApiGLErrorHandlingMode.IGNORE) + { + return; + } + + + + boolean onlyLogOnce = Config.Client.Advanced.Debugging.OpenGl.onlyLogGlErrorsOnce.get(); + String errorMessage = "GL ERROR [" + msg.id + "] from [" + msg.source + "]: [" + msg.message + "]"+(onlyLogOnce ? " this message will only be logged once" : "")+"."; + if (onlyLogOnce + && !LOGGED_GL_MESSAGES.add(errorMessage)) + { + // this message has already been logged + return; + } + + + // create an exception so we get a stacktrace of where the message was triggered from + RuntimeException exception = new RuntimeException(errorMessage); + + if (msg.type == EGLMessageType.ERROR || msg.type == EGLMessageType.UNDEFINED_BEHAVIOR) + { + // critical error + + LOGGER.error(exception.getMessage(), exception); + + if (errorHandlingMode == EDhApiGLErrorHandlingMode.LOG_THROW) + { + // will probably crash the game, + // good for quickly checking if there's a problem while preventing log spam + throw exception; + } + } + else + { + // non-critical log + + EGLMessageSeverity severity = msg.severity; + if (severity == null) + { + // just in case the message was malformed + severity = EGLMessageSeverity.LOW; + } + + switch (severity) + { + case HIGH: + LOGGER.error(exception.getMessage(), exception); + break; + case MEDIUM: + LOGGER.warn(exception.getMessage(), exception); + break; + case LOW: + LOGGER.info(exception.getMessage(), exception); + break; + case NOTIFICATION: + LOGGER.debug(exception.getMessage(), exception); + break; + } + } + } + + //endregion + + + + //================// + // helper methods // + //================// + //region + + private String getFailedVersionInfo(GLCapabilities c) + { + return "Your OpenGL support:\n" + + "openGL version 3.2+: [" + c.OpenGL32 + "] <- REQUIRED\n" + + "Vertex Attribute Buffer Binding: [" + (c.glVertexAttribBinding != 0) + "] <- optional improvement\n" + + "Buffer Storage: [" + (c.glBufferStorage != 0) + "] <- optional improvement\n" + + "If you noticed that your computer supports higher OpenGL versions" + + " but not the required version, try running the game in compatibility mode." + + " (How you turn that on, I have no clue~)"; + } + + private String versionInfoToString(GLCapabilities c) + { + return "Your OpenGL support:\n" + + "openGL version 3.2+: [" + c.OpenGL32 + "] <- REQUIRED\n" + + "Vertex Attribute Buffer Binding: [" + (c.glVertexAttribBinding != 0) + "] <- optional improvement\n" + + "Buffer Storage: [" + (c.glBufferStorage != 0) + "] <- optional improvement\n"; + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLState.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLState.java new file mode 100644 index 000000000..6107a7a24 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLState.java @@ -0,0 +1,259 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject; + +import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import org.lwjgl.opengl.GL32; + +public class GLState implements AutoCloseable +{ + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + public int program; + public int vao; + public int vbo; + public int ebo; + public int fbo; + public int texture2D; + /** IE: GL_TEXTURE0, GL_TEXTURE1, etc. */ + public int activeTextureNumber; + public int texture0; + public int texture1; + public int texture2; + public int texture3; + public int frameBufferTexture0; + public int frameBufferTexture1; + public int frameBufferDepthTexture; + public boolean blend; + public boolean scissor; + public int blendEqRGB; + public int blendEqAlpha; + public int blendSrcColor; + public int blendSrcAlpha; + public int blendDstColor; + public int blendDstAlpha; + public boolean depth; + public boolean writeToDepthBuffer; + public int depthFunc; + public boolean stencil; + public int stencilFunc; + public int stencilRef; + public int stencilMask; + public int[] view; + public boolean cull; + public int cullMode; + public int polyMode; + + + + public GLState() { this.saveState(); } + + public void saveState() + { + this.program = GL32.glGetInteger(GL32.GL_CURRENT_PROGRAM); + this.vao = GL32.glGetInteger(GL32.GL_VERTEX_ARRAY_BINDING); + this.vbo = GL32.glGetInteger(GL32.GL_ARRAY_BUFFER_BINDING); + this.ebo = GL32.glGetInteger(GL32.GL_ELEMENT_ARRAY_BUFFER_BINDING); + + this.fbo = GL32.glGetInteger(GL32.GL_FRAMEBUFFER_BINDING); + + this.texture2D = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); + this.activeTextureNumber = GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE); + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + this.texture0 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + this.texture1 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); + + GLMC.glActiveTexture(GL32.GL_TEXTURE2); // problem with Iris + this.texture2 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); + + GLMC.glActiveTexture(GL32.GL_TEXTURE3); + this.texture3 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); + + GLMC.glActiveTexture(this.activeTextureNumber); + + if (this.fbo != 0) + { + this.frameBufferTexture0 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + this.frameBufferTexture1 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + this.frameBufferDepthTexture = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + } + else + { + // attempting to get values from the default framebuffer can throw errors on Linux + this.frameBufferTexture0 = 0; + this.frameBufferTexture1 = 0; + this.frameBufferDepthTexture = 0; + } + + this.blend = GL32.glIsEnabled(GL32.GL_BLEND); + this.scissor = GL32.glIsEnabled(GL32.GL_SCISSOR_TEST); + this.blendEqRGB = GL32.glGetInteger(GL32.GL_BLEND_EQUATION_RGB); + this.blendEqAlpha = GL32.glGetInteger(GL32.GL_BLEND_EQUATION_ALPHA); + this.blendSrcColor = GL32.glGetInteger(GL32.GL_BLEND_SRC_RGB); + this.blendSrcAlpha = GL32.glGetInteger(GL32.GL_BLEND_SRC_ALPHA); + this.blendDstColor = GL32.glGetInteger(GL32.GL_BLEND_DST_RGB); + this.blendDstAlpha = GL32.glGetInteger(GL32.GL_BLEND_DST_ALPHA); + this.depth = GL32.glIsEnabled(GL32.GL_DEPTH_TEST); + this.writeToDepthBuffer = GL32.glGetInteger(GL32.GL_DEPTH_WRITEMASK) == GL32.GL_TRUE; + this.depthFunc = GL32.glGetInteger(GL32.GL_DEPTH_FUNC); + this.stencil = GL32.glIsEnabled(GL32.GL_STENCIL_TEST); + this.stencilFunc = GL32.glGetInteger(GL32.GL_STENCIL_FUNC); + this.stencilRef = GL32.glGetInteger(GL32.GL_STENCIL_REF); + this.stencilMask = GL32.glGetInteger(GL32.GL_STENCIL_VALUE_MASK); + this.view = new int[4]; + GL32.glGetIntegerv(GL32.GL_VIEWPORT, this.view); + this.cull = GL32.glIsEnabled(GL32.GL_CULL_FACE); + this.cullMode = GL32.glGetInteger(GL32.GL_CULL_FACE_MODE); + this.polyMode = GL32.glGetInteger(GL32.GL_POLYGON_MODE); + } + + @Override + public void close() + { + // explicitly unbinding the frame buffer is necessary to prevent GL_CLEAR calls from hitting the wrong buffer + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, 0); + boolean frameBufferSet = false; + + if (this.fbo != 0 && GL32.glIsFramebuffer(this.fbo)) + { + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fbo); + frameBufferSet = true; + } + + + if (this.blend) + { + GLMC.enableBlend(); + } + else + { + GLMC.disableBlend(); + } + + if (this.scissor) + { + GLMC.enableScissorTest(); + } + else + { + GLMC.disableScissorTest(); + } + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(GL32.glIsTexture(this.texture0) ? this.texture0 : 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + GLMC.glBindTexture(GL32.glIsTexture(this.texture1) ? this.texture1 : 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE2); + GLMC.glBindTexture(GL32.glIsTexture(this.texture2) ? this.texture2 : 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE3); + GLMC.glBindTexture(GL32.glIsTexture(this.texture3) ? this.texture3 : 0); + + GLMC.glActiveTexture(this.activeTextureNumber); + GLMC.glBindTexture(GL32.glIsTexture(this.texture2D) ? this.texture2D : 0); + + // attempting to set textures on the default frame buffer (ID 0) will throw errors + if (frameBufferSet) + { + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.frameBufferTexture0, 0); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_TEXTURE_2D, this.frameBufferTexture1, 0); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_TEXTURE_2D, this.frameBufferDepthTexture, 0); + } + + GL32.glBindVertexArray(GL32.glIsVertexArray(this.vao) ? this.vao : 0); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, GL32.glIsBuffer(this.vbo) ? this.vbo : 0); + GL32.glBindBuffer(GL32.GL_ELEMENT_ARRAY_BUFFER, GL32.glIsBuffer(this.ebo) ? this.ebo: 0); + GL32.glUseProgram(GL32.glIsProgram(this.program) ? this.program : 0); + + if (this.writeToDepthBuffer) + { + GLMC.enableDepthMask(); + } + else + { + GLMC.disableDepthMask(); + } + + GLMC.glBlendFunc(this.blendSrcColor, this.blendDstColor); + GL32.glBlendEquationSeparate(this.blendEqRGB, this.blendEqAlpha); + GLMC.glBlendFuncSeparate(this.blendSrcColor, this.blendDstColor, this.blendSrcAlpha, this.blendDstAlpha); + + if (this.depth) + { + GLMC.enableDepthTest(); + } + else + { + GLMC.disableDepthTest(); + } + GLMC.glDepthFunc(this.depthFunc); + + if (this.stencil) + { + GL32.glEnable(GL32.GL_STENCIL_TEST); + } + else + { + GL32.glDisable(GL32.GL_STENCIL_TEST); + } + GL32.glStencilFunc(this.stencilFunc, this.stencilRef, this.stencilMask); + + GL32.glViewport(this.view[0], this.view[1], this.view[2], this.view[3]); + if (this.cull) + { + GLMC.enableFaceCulling(); + } + else + { + GLMC.disableFaceCulling(); + } + GL32.glCullFace(this.cullMode); + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, this.polyMode); + } + + @Override + public String toString() + { + return "GLState{" + + "program=" + this.program + ", vao=" + this.vao + ", vbo=" + this.vbo + ", ebo=" + this.ebo + ", fbo=" + this.fbo + + ", text=" + GLEnums.getString(this.texture2D) + "@" + this.activeTextureNumber + ", text0=" + GLEnums.getString(this.texture0) + + ", FB text0=" + this.frameBufferTexture0 + + ", FB text1=" + this.frameBufferTexture1 + + ", FB depth=" + this.frameBufferDepthTexture + + ", blend=" + this.blend + ", scissor=" + this.scissor + ", blendMode=" + GLEnums.getString(this.blendSrcColor) + "," + GLEnums.getString(this.blendDstColor) + + ", depth=" + this.depth + + ", depthFunc=" + GLEnums.getString(this.depthFunc) + ", stencil=" + this.stencil + + ", stencilFunc=" + GLEnums.getString(this.stencilFunc) + ", stencilRef=" + this.stencilRef + ", stencilMask=" + this.stencilMask + + ", view={x:" + this.view[0] + ", y:" + this.view[1] + + ", w:" + this.view[2] + ", h:" + this.view[3] + "}" + ", cull=" + this.cull + + ", cullMode=" + GLEnums.getString(this.cullMode) + ", polyMode=" + GLEnums.getString(this.polyMode) + + '}'; + } + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GlDhFramebuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GlDhFramebuffer.java new file mode 100644 index 000000000..24fd69a46 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GlDhFramebuffer.java @@ -0,0 +1,94 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject; + +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import org.lwjgl.opengl.GL32; + +public class GlDhFramebuffer implements IDhApiFramebuffer +{ + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + private int id; + + + + //=============// + // constructor // + //=============// + //region + + public GlDhFramebuffer() { this.id = GL32.glGenFramebuffers(); } + + /** For internal use by Iris, do not remove. */ + public GlDhFramebuffer(int id) { this.id = id; } + + //endregion + + + + //=========// + // methods // + //=========// + //region + + @Override + public void addDepthAttachment(int textureId, boolean isCombinedStencil) + { + this.bind(); + + int depthAttachment = isCombinedStencil ? GL32.GL_DEPTH_STENCIL_ATTACHMENT : GL32.GL_DEPTH_ATTACHMENT; + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, depthAttachment, GL32.GL_TEXTURE_2D, textureId, 0); + } + + @Override + public void addColorAttachment(int textureIndex, int textureId) + { + this.bind(); + + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0 + textureIndex, GL32.GL_TEXTURE_2D, textureId, 0); + } + + @Override + public void bind() + { + if (this.id == -1) + { + throw new IllegalStateException("Framebuffer does not exist!"); + } + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.id); + } + + @Override + public void destroy() + { + GL32.glDeleteFramebuffers(this.id); + this.id = -1; + } + + @Override + public int getStatus() + { + this.bind(); + int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER); + return status; + } + + @Override + public int getId() { return this.id; } + + //endregion + + + + //=============// + // API methods // + //=============// + //region + + public boolean overrideThisFrame() { return true; } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GlDummyUniformData.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GlDummyUniformData.java new file mode 100644 index 000000000..43f0f35c8 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GlDummyUniformData.java @@ -0,0 +1,17 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject; + +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper; + +/** + * With OpenGL all uniform data is uploaded during the rendering phase + * so nothing is needed here. + */ +public class GlDummyUniformData implements ILodContainerUniformBufferWrapper +{ + @Override public void createUniformData(LodBufferContainer bufferContainer) { } + @Override public void tryUpload() { } + @Override public void upload() { } + @Override public void close() { } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java new file mode 100644 index 000000000..8b62b5d37 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java @@ -0,0 +1,367 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.buffer; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.ThreadUtil; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL44; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.nio.ByteBuffer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicInteger; + +public class GLBuffer implements AutoCloseable +{ + private static final DhLogger LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile) + .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat) + .build(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3; + public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER; + /** the number of active buffers, can be used for debugging */ + public static AtomicInteger bufferCount = new AtomicInteger(0); + + private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000; + private static final ConcurrentHashMap, Integer> PHANTOM_TO_BUFFER_ID = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap> BUFFER_ID_TO_PHANTOM = new ConcurrentHashMap<>(); + private static final ReferenceQueue PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>(); + private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("GLBuffer Cleanup"); + + + protected int id; + public final int getId() { return this.id; } + protected int size = 0; + public int getSize() { return this.size; } + protected boolean bufferStorage; + public final boolean isBufferStorage() { return this.bufferStorage; } + protected boolean isMapped = false; + + + + //==============// + // constructors // + //==============// + //region + + static { CLEANUP_THREAD.execute(() -> runPhantomReferenceCleanupLoop()); } + + public GLBuffer(boolean isBufferStorage) { this.create(isBufferStorage); } + + //endregion + + + + //=========// + // methods // + //=========// + //region + + // Should be override by subclasses + public int getBufferBindingTarget() { return GL32.GL_COPY_READ_BUFFER; } + + public void bind() { GL32.glBindBuffer(this.getBufferBindingTarget(), this.id); } + public void unbind() { GL32.glBindBuffer(this.getBufferBindingTarget(), 0); } + + //endregion + + + + //====================// + // create and destroy // + //====================// + //region + + protected void create(boolean asBufferStorage) + { + if (!GLProxy.runningOnRenderThread()) + { + LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread."); + } + + // destroy the old buffer if one is present + // (as of 2024-12-31 James didn't see this happen, but just in case) + if (this.id != 0) + { + destroyBufferIdAsync(this.id); + } + + this.id = GLMC.glGenBuffers(); + this.bufferStorage = asBufferStorage; + bufferCount.getAndIncrement(); + + PhantomReference phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE); + PHANTOM_TO_BUFFER_ID.put(phantom, this.id); + BUFFER_ID_TO_PHANTOM.put(this.id, phantom); + + } + + protected void destroyAsync() + { + if (this.id == 0) + { + // the buffer has already been closed + return; + } + + destroyBufferIdAsync(this.id); + + this.id = 0; + this.size = 0; + } + private static void destroyBufferIdAsync(int id) + { + // remove and clear the phantom reference if present + if (BUFFER_ID_TO_PHANTOM.containsKey(id)) + { + Reference phantom = BUFFER_ID_TO_PHANTOM.get(id); + + // if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again + // this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA + // to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it + phantom.clear(); + + PHANTOM_TO_BUFFER_ID.remove(phantom); + BUFFER_ID_TO_PHANTOM.remove(id); + } + + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() -> + { + // destroy the buffer if it exists, + // the buffer may not exist if the destroy method is called twice + if (GL32.glIsBuffer(id)) + { + GLMC.glDeleteBuffers(id); + bufferCount.decrementAndGet(); + + if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get()) + { + LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]"); + } + } + }); + } + + //endregion + + + + //==================// + // buffer uploading // + //==================// + //region + + /** + * Assumes the GL Context is already bound.
+ * Will create the VBO if one exist. + */ + public void uploadBuffer(ByteBuffer bb, EDhApiGpuUploadMethod uploadMethod, int maxExpansionSize, int bufferHint) + { + LodUtil.assertTrue(!uploadMethod.useEarlyMapping, "UploadMethod signal that this should use Mapping instead of uploadBuffer!"); + int bbSize = bb.limit() - bb.position(); + if (bbSize > maxExpansionSize) + { + LodUtil.assertNotReach("maxExpansionSize is [" + maxExpansionSize + "] but buffer size is [" + bbSize + "]!"); + } + + // Don't upload an empty buffer + if (bbSize == 0) + { + return; + } + + // make sure the buffer is ready for uploading + this.createOrChangeBufferTypeForUpload(uploadMethod); + + switch (uploadMethod) + { + //case NONE: + // return; + case AUTO: + LodUtil.assertNotReach("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!"); + case BUFFER_STORAGE: + this.uploadBufferStorage(bb, bufferHint); + break; + case DATA: + this.uploadBufferData(bb, bufferHint); + break; + case SUB_DATA: + this.uploadSubData(bb, maxExpansionSize, bufferHint); + break; + default: + LodUtil.assertNotReach("Unknown GpuUploadMethod!"); + } + } + /** Requires the buffer to be bound */ + protected void uploadBufferStorage(ByteBuffer bb, int bufferStorageHint) + { + LodUtil.assertTrue(this.bufferStorage, "Buffer is not bufferStorage but its trying to use bufferStorage upload method!"); + + int bbSize = bb.limit() - bb.position(); + this.destroyAsync(); + this.create(true); + this.bind(); + GL44.glBufferStorage(this.getBufferBindingTarget(), bb, 0); + this.size = bbSize; + } + /** Requires the buffer to be bound */ + protected void uploadBufferData(ByteBuffer bb, int bufferDataHint) + { + LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use bufferData upload method!"); + + int bbSize = bb.limit() - bb.position(); + GL32.glBufferData(this.getBufferBindingTarget(), bb, bufferDataHint); + this.size = bbSize; + } + /** Requires the buffer to be bound */ + protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint) + { + LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use subData upload method!"); + + int bbSize = bb.limit() - bb.position(); + if (this.size < bbSize || this.size > bbSize * BUFFER_SHRINK_TRIGGER) + { + int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER); + if (newSize > maxExpansionSize) newSize = maxExpansionSize; + GL32.glBufferData(this.getBufferBindingTarget(), newSize, bufferDataHint); + this.size = newSize; + } + GL32.glBufferSubData(this.getBufferBindingTarget(), 0, bb); + } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + @Override + public void close() { this.destroyAsync(); } + + @Override + public String toString() + { + return (this.bufferStorage ? "" : "Static-") + this.getClass().getSimpleName() + + "[id:" + this.id + ",size:" + this.size + (this.isMapped ? ",MAPPED" : "") + "]"; + } + + //endregion + + + + //================// + // helper methods // + //================// + //region + + /** + * Makes sure the buffer exists and is of the correct format + * before uploading. + */ + private void createOrChangeBufferTypeForUpload(EDhApiGpuUploadMethod uploadMethod) + { + // create/change the buffer type if necessary + if (uploadMethod.useBufferStorage != this.bufferStorage) + { + // recreate if the buffer storage type changed + this.bind(); + this.destroyAsync(); + this.create(uploadMethod.useBufferStorage); + this.bind(); + } + else + { + // Prevent uploading to the null buffer (ID 0). + // This can happen if the buffer was deleted previously. + if (this.id == 0) + { + this.create(this.bufferStorage); + } + + this.bind(); + } + } + + //endregion + + + + //================// + // static cleanup // + //================// + //region + + private static void runPhantomReferenceCleanupLoop() + { + while (true) + { + try + { + try + { + Thread.sleep(PHANTOM_REF_CHECK_TIME_IN_MS); + } + catch (InterruptedException ignore) { } + + + Reference phantomRef = PHANTOM_REFERENCE_QUEUE.poll(); + while (phantomRef != null) + { + // destroy the buffer if it hasn't been cleared yet + if (PHANTOM_TO_BUFFER_ID.containsKey(phantomRef)) + { + int id = PHANTOM_TO_BUFFER_ID.get(phantomRef); + destroyBufferIdAsync(id); + //LOGGER.warn("Buffer Phantom collected, ID: ["+id+"]"); + } + + phantomRef = PHANTOM_REFERENCE_QUEUE.poll(); + } + } + catch (Exception e) + { + LOGGER.error("Unexpected error in buffer cleanup thread: [" + e.getMessage() + "].", e); + } + } + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLElementBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLElementBuffer.java new file mode 100644 index 000000000..2f6c042bf --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLElementBuffer.java @@ -0,0 +1,60 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.buffer; + +import org.lwjgl.opengl.GL32; + +/** + * This is a container for a OpenGL + * VBO (Vertex Buffer Object). + * + * @author James Seibel + * @version 11-20-2021 + */ +public class GLElementBuffer extends GLBuffer +{ + /** + * When uploading to a buffer that is too small, recreate it this many times + * bigger than the upload payload + */ + protected int indicesCount = 0; + public int getIndicesCount() { return this.indicesCount; } + protected int type = GL32.GL_UNSIGNED_INT; + public int getType() { return type; } + + public GLElementBuffer(boolean isBufferStorage) + { + super(isBufferStorage); + } + + @Override + public void destroyAsync() + { + super.destroyAsync(); + this.indicesCount = 0; + } + + @Override + public int getBufferBindingTarget() + { + return GL32.GL_ELEMENT_ARRAY_BUFFER; + } + +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLVertexBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLVertexBuffer.java new file mode 100644 index 000000000..5567c7cf2 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLVertexBuffer.java @@ -0,0 +1,112 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.buffer; + +import java.nio.ByteBuffer; + +import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; +import org.lwjgl.opengl.GL32; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; + +/** + * This is a container for a OpenGL + * VBO (Vertex Buffer Object). + * + * @author James Seibel + * @version 11-20-2021 + */ +public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper +{ + /** + * When uploading to a buffer that is too small, recreate it this many times + * bigger than the upload payload + */ + protected int vertexCount = 0; + public int getVertexCount() { return this.vertexCount; } + + + + //=============// + // constructor // + //=============// + //region + + public GLVertexBuffer() { this(GLProxy.getInstance().getGpuUploadMethod() == EDhApiGpuUploadMethod.BUFFER_STORAGE); } + public GLVertexBuffer(boolean isBufferStorage) { super(isBufferStorage); } + + //endregion + + + + //======================// + // uploading/destroying // + //======================// + //region + + @Override + public int getBufferBindingTarget() { return GL32.GL_ARRAY_BUFFER; } + + @Override + public void upload(ByteBuffer buffer, int vertexCount) + { + EDhApiGpuUploadMethod uploadMethod = GLProxy.getInstance().getGpuUploadMethod(); + int maxBufferSize = LodQuadBuilder.getMaxBufferByteSize(); + this.uploadBuffer(buffer, vertexCount, uploadMethod, maxBufferSize); + } + + /** + * bufferSize is the number of shared verticies.
+ * This number will be higher when actually rendered since each box's face needs 2 triangles + * with 2 shared verticies. + */ + public void uploadBuffer(ByteBuffer byteBuffer, int vertexCount, EDhApiGpuUploadMethod uploadMethod, int maxExpansionSize) + { + if (vertexCount < 0) + { + throw new IllegalArgumentException("vertexCount is negative!"); + } + + // If size is zero, just ignore it. + if (byteBuffer.limit() - byteBuffer.position() != 0) + { + boolean useBuffStorage = uploadMethod.useBufferStorage; + super.uploadBuffer(byteBuffer, uploadMethod, maxExpansionSize, useBuffStorage ? 0 : GL32.GL_STATIC_DRAW); + } + this.vertexCount = vertexCount; + } + + + @Override + public void close() { this.destroyAsync(); } + @Override + public void destroyAsync() + { + super.destroyAsync(); + this.vertexCount = 0; + } + + //endregion + + + +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GlQuadElementBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GlQuadElementBuffer.java new file mode 100644 index 000000000..c0bcca307 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GlQuadElementBuffer.java @@ -0,0 +1,192 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.buffer; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +/** AKA Index Buffer TODO RENAME */ +public class GlQuadElementBuffer extends GLElementBuffer +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + + + //=============// + // constructor // + //=============// + //region + + public GlQuadElementBuffer() { super(false); } + + public void reserve(int quadCount) + { + if (quadCount < 0) + { + throw new IllegalArgumentException("quadCount must be greater than 0"); + } + if (quadCount == 0) + { + // shouldn't happen, but just in case + return; + } + + this.indicesCount = quadCount * 6; // 2 triangles per quad + if (this.indicesCount >= this.getCapacity() + && this.indicesCount < this.getCapacity() * BUFFER_SHRINK_TRIGGER) + { + return; + } + int vertexCount = quadCount * 4; // 4 vertices per quad + + if (vertexCount < 255) + { + // Reserve 1 for the reset index + this.type = GL32.GL_UNSIGNED_BYTE; + } + else if (vertexCount < 65535) + { + // Reserve 1 for the reset index + this.type = GL32.GL_UNSIGNED_SHORT; + } + else + { + this.type = GL32.GL_UNSIGNED_INT; + } + + ByteBuffer buffer = MemoryUtil.memAlloc(this.indicesCount * GLEnums.getTypeSize(this.type)); + buildBuffer(quadCount, buffer, this.type); + this.bind(); + super.uploadBuffer(buffer, EDhApiGpuUploadMethod.DATA, + this.indicesCount * GLEnums.getTypeSize(this.type), GL32.GL_STATIC_DRAW); + + MemoryUtil.memFree(buffer); + } + + //endregion + + + + //=========// + // getters // + //=========// + //region + + public int getCapacity() { return super.getSize() / GLEnums.getTypeSize(this.getType()); } + + //endregion + + + + //==========// + // building // + //==========// + //region + + public static void buildBuffer(int quadCount, ByteBuffer buffer, int type) + { + switch (type) + { + case GL32.GL_UNSIGNED_BYTE: + buildBufferByte(quadCount, buffer); + break; + case GL32.GL_UNSIGNED_SHORT: + buildBufferShort(quadCount, buffer); + break; + case GL32.GL_UNSIGNED_INT: + buildBufferInt(quadCount, buffer); + break; + default: + throw new IllegalStateException("Unknown buffer type: [" + type + "]."); + } + } + + private static void buildBufferByte(int quadCount, ByteBuffer buffer) + { + for (int i = 0; i < quadCount; i++) + { + int vIndex = i * 4; + // First triangle + buffer.put((byte) (vIndex)); + buffer.put((byte) (vIndex + 1)); + buffer.put((byte) (vIndex + 2)); + // Second triangle + buffer.put((byte) (vIndex + 2)); + buffer.put((byte) (vIndex + 3)); + buffer.put((byte) (vIndex)); + } + if (buffer.hasRemaining()) + { + throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); + } + buffer.rewind(); + } + private static void buildBufferShort(int quadCount, ByteBuffer buffer) + { + for (int i = 0; i < quadCount; i++) + { + int vIndex = i * 4; + // First triangle + buffer.putShort((short) (vIndex)); + buffer.putShort((short) (vIndex + 1)); + buffer.putShort((short) (vIndex + 2)); + // Second triangle + buffer.putShort((short) (vIndex + 2)); + buffer.putShort((short) (vIndex + 3)); + buffer.putShort((short) (vIndex)); + } + if (buffer.hasRemaining()) + { + throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); + } + buffer.rewind(); + } + private static void buildBufferInt(int quadCount, ByteBuffer buffer) + { + for (int i = 0; i < quadCount; i++) + { + int vIndex = i * 4; + // First triangle + buffer.putInt(vIndex); + buffer.putInt(vIndex + 1); + buffer.putInt(vIndex + 2); + // Second triangle + buffer.putInt(vIndex + 2); + buffer.putInt(vIndex + 3); + buffer.putInt(vIndex); + } + if (buffer.hasRemaining()) + { + throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); + } + buffer.rewind(); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/enums/EGlVersion.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/enums/EGlVersion.java new file mode 100644 index 000000000..2387ee8e8 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/enums/EGlVersion.java @@ -0,0 +1,9 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject.enums; + +public enum EGlVersion +{ + GL_11, + GL_12, + GL_30, + GL_31 +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/enums/GLEnums.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/enums/GLEnums.java new file mode 100644 index 000000000..36cdd09d2 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/enums/GLEnums.java @@ -0,0 +1,261 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.enums; + +import static org.lwjgl.opengl.GL46.*; + +// Turns GL int enums back to readable strings +public class GLEnums +{ + + public static String getString(int glEnum) + { + // blend stuff + switch (glEnum) + { + case GL_ZERO: + return "GL_ZERO"; + case GL_ONE: + return "GL_ONE"; + case GL_SRC_COLOR: + return "GL_SRC_COLOR"; + case GL_ONE_MINUS_SRC_COLOR: + return "GL_ONE_MINUS_SRC_COLOR"; + case GL_DST_COLOR: + return "GL_DST_COLOR"; + case GL_ONE_MINUS_DST_COLOR: + return "GL_ONE_MINUS_DST_COLOR"; + case GL_SRC_ALPHA: + return "GL_SRC_ALPHA"; + case GL_ONE_MINUS_SRC_ALPHA: + return "GL_ONE_MINUS_SRC_ALPHA"; + case GL_DST_ALPHA: + return "GL_DST_ALPHA"; + case GL_ONE_MINUS_DST_ALPHA: + return "GL_ONE_MINUS_DST_ALPHA"; + case GL_CONSTANT_COLOR: + return "GL_CONSTANT_COLOR"; + case GL_ONE_MINUS_CONSTANT_COLOR: + return "GL_ONE_MINUS_CONSTANT_COLOR"; + case GL_CONSTANT_ALPHA: + return "GL_CONSTANT_ALPHA"; + case GL_ONE_MINUS_CONSTANT_ALPHA: + return "GL_ONE_MINUS_CONSTANT_ALPHA"; + default: + } + + // shader stuff + switch (glEnum) + { + case GL_VERTEX_SHADER: + return "GL_VERTEX_SHADER"; + case GL_GEOMETRY_SHADER: + return "GL_GEOMETRY_SHADER"; + case GL_FRAGMENT_SHADER: + return "GL_FRAGMENT_SHADER"; + default: + } + + // stencil stuff + switch (glEnum) + { + case GL_KEEP: + return "GL_KEEP"; + case GL_ZERO: + return "GL_ZERO"; + case GL_REPLACE: + return "GL_REPLACE"; + case GL_INCR: + return "GL_INCR"; + case GL_DECR: + return "GL_DECR"; + case GL_INVERT: + return "GL_INVERT"; + case GL_INCR_WRAP: + return "GL_INCR_WRAP"; + case GL_DECR_WRAP: + return "GL_DECR_WRAP"; + default: + } + + // depth stuff + switch (glEnum) + { + case GL_NEVER: + return "GL_NEVER"; + case GL_LESS: + return "GL_LESS"; + case GL_EQUAL: + return "GL_EQUAL"; + case GL_LEQUAL: + return "GL_LEQUAL"; + case GL_GREATER: + return "GL_GREATER"; + case GL_NOTEQUAL: + return "GL_NOTEQUAL"; + case GL_GEQUAL: + return "GL_GEQUAL"; + case GL_ALWAYS: + return "GL_ALWAYS"; + default: + } + + // Texture binding points + switch (glEnum) + { + case GL_TEXTURE0: + return "GL_TEXTURE0"; + case GL_TEXTURE1: + return "GL_TEXTURE1"; + case GL_TEXTURE2: + return "GL_TEXTURE2"; + case GL_TEXTURE3: + return "GL_TEXTURE3"; + case GL_TEXTURE4: + return "GL_TEXTURE4"; + case GL_TEXTURE5: + return "GL_TEXTURE5"; + case GL_TEXTURE6: + return "GL_TEXTURE6"; + case GL_TEXTURE7: + return "GL_TEXTURE7"; + case GL_TEXTURE8: + return "GL_TEXTURE8"; + case GL_TEXTURE9: + return "GL_TEXTURE9"; + case GL_TEXTURE10: + return "GL_TEXTURE10"; + case GL_TEXTURE11: + return "GL_TEXTURE11"; + case GL_TEXTURE12: + return "GL_TEXTURE12"; + case GL_TEXTURE13: + return "GL_TEXTURE13"; + case GL_TEXTURE14: + return "GL_TEXTURE14"; + case GL_TEXTURE15: + return "GL_TEXTURE15"; + case GL_TEXTURE16: + return "GL_TEXTURE16"; + case GL_TEXTURE17: + return "GL_TEXTURE17"; + case GL_TEXTURE18: + return "GL_TEXTURE18"; + case GL_TEXTURE19: + return "GL_TEXTURE19"; + case GL_TEXTURE20: + return "GL_TEXTURE20"; + case GL_TEXTURE21: + return "GL_TEXTURE21"; + case GL_TEXTURE22: + return "GL_TEXTURE22"; + case GL_TEXTURE23: + return "GL_TEXTURE23"; + case GL_TEXTURE24: + return "GL_TEXTURE24"; + case GL_TEXTURE25: + return "GL_TEXTURE25"; + case GL_TEXTURE26: + return "GL_TEXTURE26"; + case GL_TEXTURE27: + return "GL_TEXTURE27"; + case GL_TEXTURE28: + return "GL_TEXTURE28"; + case GL_TEXTURE29: + return "GL_TEXTURE29"; + case GL_TEXTURE30: + return "GL_TEXTURE30"; + case GL_TEXTURE31: + return "GL_TEXTURE31"; + default: + } + + // Polygon modes + switch (glEnum) + { + case GL_POINT: + return "GL_POINT"; + case GL_LINE: + return "GL_LINE"; + case GL_FILL: + return "GL_FILL"; + default: + } + + // Culling modes + switch (glEnum) + { + case GL_FRONT: + return "GL_FRONT"; + case GL_BACK: + return "GL_BACK"; + case GL_FRONT_AND_BACK: + return "GL_FRONT_AND_BACK"; + default: + } + + // Types + switch (glEnum) + { + case GL_BYTE: + return "GL_BYTE"; + case GL_UNSIGNED_BYTE: + return "GL_UNSIGNED_BYTE"; + case GL_SHORT: + return "GL_SHORT"; + case GL_UNSIGNED_SHORT: + return "GL_UNSIGNED_SHORT"; + case GL_INT: + return "GL_INT"; + case GL_UNSIGNED_INT: + return "GL_UNSIGNED_INT"; + case GL_FLOAT: + return "GL_FLOAT"; + case GL_DOUBLE: + return "GL_DOUBLE"; + default: + } + + return "GL_UNKNOWN(" + glEnum + ")"; + } + + public static int getTypeSize(int glTypeEnum) + { + switch (glTypeEnum) + { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + return 1; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + return 2; + case GL_INT: + case GL_UNSIGNED_INT: + return 4; + case GL_FLOAT: + return 4; + case GL_DOUBLE: + return 8; + default: + throw new IllegalArgumentException("Unknown type enum: " + getString(glTypeEnum)); + } + } + +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/shader/GlShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/shader/GlShader.java new file mode 100644 index 000000000..52c568245 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/shader/GlShader.java @@ -0,0 +1,184 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.shader; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import org.lwjgl.PointerBuffer; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL32C; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.NativeType; + +/** + * This object holds a OpenGL reference to a shader + * and allows for reading in and compiling a shader file. + */ +public class GlShader +{ + private static final DhLogger LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile) + .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat) + .build(); + + + /** OpenGL shader ID */ + public final int id; + + + + //==============// + // constructors // + //==============// + //region + + /** + * Creates a shader with specified type. + * + * @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. + * @param sourceString File path of the shader + * @throws RuntimeException if the shader fails to compile + */ + public GlShader(int type, String sourceString) + { + LOGGER.info("Loading shader with type: ["+type+"]"); + LOGGER.debug("Source: \n["+sourceString+"]"); + if (sourceString == null || sourceString.isEmpty()) + { + throw new IllegalArgumentException("No shader source given."); + } + + // Create an empty shader object + this.id = GL32.glCreateShader(type); + if (this.id == 0) + { + throw new IllegalArgumentException("Failed to create shader with type ["+type+"] and Source: \n["+sourceString+"]."); + } + + safeShaderSource(this.id, sourceString); + GL32.glCompileShader(this.id); + // check if the shader compiled + int status = GL32.glGetShaderi(this.id, GL32.GL_COMPILE_STATUS); + if (status != GL32.GL_TRUE) + { + + String message = "Shader compiler error. Details: [" + GL32.glGetShaderInfoLog(this.id) + "]\n"; + message += "Source: \n[" + sourceString + "]"; + this.free(); // important! + throw new RuntimeException(message); + } + LOGGER.info("Shader loaded sucessfully."); + } + + //endregion + + + + //=========// + // helpers // + //=========// + //region + + /** + * Identical in function to {@link GL32C#glShaderSource(int, CharSequence)} but + * passes a null pointer for string length to force the driver to rely on the null + * terminator for string length. This is a workaround for an apparent flaw with some + * AMD drivers that don't receive or interpret the length correctly, resulting in + * an access violation when the driver tries to read past the string memory. + * + *

Hat tip to fewizz for the find and the fix. + * + *

Source: https://github.com/vram-guild/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96 + */ + private static void safeShaderSource(@NativeType("GLuint") int glId, @NativeType("GLchar const **") CharSequence source) + { + final MemoryStack stack = MemoryStack.stackGet(); + final int stackPointer = stack.getPointer(); + + try + { + final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true); + final PointerBuffer pointers = stack.mallocPointer(1); + pointers.put(sourceBuffer); + + GL32.nglShaderSource(glId, 1, pointers.address0(), 0); + org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1); + } + finally + { + stack.setPointer(stackPointer); + } + } + + public void free() { GL32.glDeleteShader(this.id); } + + public static String loadFile(String path, boolean absoluteFilePath) + { + StringBuilder stringBuilder = new StringBuilder(); + + try + { + // open the file + InputStream in; + if (absoluteFilePath) + { + // Throws FileNotFoundException + in = new FileInputStream(path); // Note: this should use OS path seperator + } + else + { + in = GlShader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/' + if (in == null) + { + throw new FileNotFoundException("Shader file not found in resource: " + path); + } + } + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + + // read in the file + String line; + while ((line = reader.readLine()) != null) + { + stringBuilder.append(line).append("\n"); + } + } + catch (IOException e) + { + throw new RuntimeException("Unable to load shader from file [" + path + "]. Error: " + e.getMessage()); + } + + return stringBuilder.toString(); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/shader/GlShaderProgram.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/shader/GlShaderProgram.java new file mode 100644 index 000000000..011de6d41 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/shader/GlShaderProgram.java @@ -0,0 +1,225 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.shader; + +import java.awt.Color; +import java.nio.FloatBuffer; + +import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; +import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryStack; + +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3f; + + +/** + * This object holds the reference to a OpenGL shader program + * and contains a few methods that can be used with OpenGL shader programs. + * The reason for many of these simple wrapper methods is as reminders of what + * can (and needs to be) done with a shader program. + */ +public class GlShaderProgram +{ + /** Stores the handle of the program. */ + public final int id; + + + + //=============// + // constructor // + //=============// + //region + + public GlShaderProgram(String vertResourcePath, String fragResourcePath, String attribute) { this(vertResourcePath, fragResourcePath, new String[]{ attribute }); } + /** + * @param vertResourcePath the relative path the vertex shader should be found + * @param fragResourcePath the relative path the fragment shader should be found + */ + public GlShaderProgram(String vertResourcePath, String fragResourcePath, String[] attributes) + { + this.id = GL32.glCreateProgram(); + + { + String shaderString = GlShader.loadFile(vertResourcePath, false); + GlShader vertShader = new GlShader(GL32.GL_VERTEX_SHADER, shaderString); + GL32.glAttachShader(this.id, vertShader.id); + vertShader.free(); + } + + { + String shaderString = GlShader.loadFile(fragResourcePath, false); + GlShader fragShader = new GlShader(GL32.GL_FRAGMENT_SHADER, shaderString); + GL32.glAttachShader(this.id, fragShader.id); + fragShader.free(); + } + + for (int i = 0; i < attributes.length; i++) + { + GL32.glBindAttribLocation(this.id, i, attributes[i]); + } + GL32.glLinkProgram(this.id); + + int status = GL32.glGetProgrami(this.id, GL32.GL_LINK_STATUS); + if (status != GL32.GL_TRUE) + { + String message = "Shader Link Error. Details: " + GL32.glGetProgramInfoLog(this.id); + this.free(); // important! + throw new RuntimeException(message); + } + GL32.glUseProgram(this.id); // This HAVE to be a direct call to prevent calling the overloaded version + } + + //endregion + + + + //=========// + // binding // + //=========// + //region + + public void bind() { GL32.glUseProgram(this.id); } + public void unbind() { GL32.glUseProgram(0); } + + public void free() { GL32.glDeleteProgram(this.id); } + + //endregion + + + + //============// + // attributes // + //============// + //region + + /** + * WARNING: Slow native call! Cache it if possible! + * Gets the location of an attribute variable with specified name. + * Calls GL20.glGetAttribLocation(id, name) + * + * @param name Attribute name + * @return Location of the attribute + * @throws RuntimeException if attribute not found + */ + public int getAttributeLocation(CharSequence name) + { + int i = GL32.glGetAttribLocation(id, name); + if (i == -1) throw new RuntimeException("Attribute name not found: " + name); + return i; + } + /** + * Same as above but without throwing errors.
+ * Returns -1 if the attribute doesn't exist or has been optimized out. + */ + public int tryGetAttributeLocation(CharSequence name) + { return GL32.glGetAttribLocation(this.id, name); } + + //endregion + + + + //==========// + // uniforms // + //==========// + //region + + /** + * WARNING: Slow native call! Cache it if possible! + * Gets the location of a uniform variable with specified name. + * Calls GL20.glGetUniformLocation(id, name) + * + * @param name Uniform name + * @return Location of the Uniform + * @throws RuntimeException if uniform not found + */ + public int getUniformLocation(CharSequence name) throws RuntimeException + { + int i = GL32.glGetUniformLocation(id, name); + if (i == -1) + { + throw new RuntimeException("Uniform name not found: " + name); + } + return i; + } + + // Same as above but without throwing errors. + // Return -1 if uniform doesn't exist or has been optimized out + public int tryGetUniformLocation(CharSequence name) + { return GL32.glGetUniformLocation(this.id, name); } + + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, boolean value) { GL32.glUniform1i(location, value ? 1 : 0); } + /** @see GlShaderProgram#setUniform(int, boolean) */ + public void trySetUniform(int location, boolean value) { if (location != -1) { this.setUniform(location, value); } } + + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, int value) { GL32.glUniform1i(location, value); } + /** @see GlShaderProgram#setUniform(int, int) */ + public void trySetUniform(int location, int value) { if (location != -1) { this.setUniform(location, value); } } + + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, float value) { GL32.glUniform1f(location, value); } + /** @see GlShaderProgram#setUniform(int, float) */ + public void trySetUniform(int location, float value) { if (location != -1) { this.setUniform(location, value); } } + + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, Vec3f value) { GL32.glUniform3f(location, value.x, value.y, value.z); } + /** @see GlShaderProgram#setUniform(int, Vec3f) */ + public void trySetUniform(int location, Vec3f value) { if (location != -1) { this.setUniform(location, value); } } + + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, DhApiVec3i value) { GL32.glUniform3i(location, value.x, value.y, value.z); } + /** @see GlShaderProgram#setUniform(int, Mat4f) */ + public void trySetUniform(int location, DhApiVec3i value) { if (location != -1) { this.setUniform(location, value); } } + + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, Mat4f value) + { + try (MemoryStack stack = MemoryStack.stackPush()) + { + FloatBuffer buffer = stack.mallocFloat(4 * 4); + value.store(buffer); + GL32.glUniformMatrix4fv(location, false, buffer); + } + } + /** @see GlShaderProgram#setUniform(int, Mat4f) */ + public void trySetUniform(int location, Mat4f value) { if (location != -1) { this.setUniform(location, value); } } + + /** + * Converts the color's RGBA values into values between 0 and 1.
+ * Requires a bound ShaderProgram. + */ + public void setUniform(int location, Color value) + { + GL32.glUniform4f(location, + value.getRed() / 256.0f, + value.getGreen() / 256.0f, + value.getBlue() / 256.0f, + value.getAlpha() / 256.0f); + } + /** @see GlShaderProgram#setUniform(int, Color) */ + public void trySetUniform(int location, Color value) { if (location != -1) { this.setUniform(location, value); } } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhDepthBufferFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhDepthBufferFormat.java new file mode 100644 index 000000000..8b2dc5dcb --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhDepthBufferFormat.java @@ -0,0 +1,114 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject.texture; + +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL30C; +import org.lwjgl.opengl.GL43C; + +public enum EGlDhDepthBufferFormat +{ + DEPTH(false), + DEPTH16(false), + DEPTH24(false), + DEPTH32(false), + DEPTH32F(false), + DEPTH_STENCIL(true), + DEPTH24_STENCIL8(true), + DEPTH32F_STENCIL8(true); + + + + private final boolean combinedStencil; + + EGlDhDepthBufferFormat(boolean combinedStencil) { this.combinedStencil = combinedStencil; } + + + + @Nullable + public static EGlDhDepthBufferFormat fromGlEnum(int glenum) + { + switch (glenum) + { + case GL30C.GL_DEPTH_COMPONENT: + return EGlDhDepthBufferFormat.DEPTH; + case GL30C.GL_DEPTH_COMPONENT16: + return EGlDhDepthBufferFormat.DEPTH16; + case GL30C.GL_DEPTH_COMPONENT24: + return EGlDhDepthBufferFormat.DEPTH24; + case GL30C.GL_DEPTH_COMPONENT32: + return EGlDhDepthBufferFormat.DEPTH32; + case GL30C.GL_DEPTH_COMPONENT32F: + return EGlDhDepthBufferFormat.DEPTH32F; + case GL30C.GL_DEPTH_STENCIL: + return EGlDhDepthBufferFormat.DEPTH_STENCIL; + case GL30C.GL_DEPTH24_STENCIL8: + return EGlDhDepthBufferFormat.DEPTH24_STENCIL8; + case GL30C.GL_DEPTH32F_STENCIL8: + return EGlDhDepthBufferFormat.DEPTH32F_STENCIL8; + default: + return null; + } + } + + public static EGlDhDepthBufferFormat fromGlEnumOrDefault(int glenum) + { + EGlDhDepthBufferFormat format = fromGlEnum(glenum); + if (format == null) + { + // yolo, just assume it's GL_DEPTH_COMPONENT + return EGlDhDepthBufferFormat.DEPTH; + } + return format; + } + + public int getGlInternalFormat() + { + switch (this) + { + case DEPTH: + return GL30C.GL_DEPTH_COMPONENT; + case DEPTH16: + return GL30C.GL_DEPTH_COMPONENT16; + case DEPTH24: + return GL30C.GL_DEPTH_COMPONENT24; + case DEPTH32: + return GL30C.GL_DEPTH_COMPONENT32; + case DEPTH32F: + return GL30C.GL_DEPTH_COMPONENT32F; + case DEPTH_STENCIL: + return GL30C.GL_DEPTH_STENCIL; + case DEPTH24_STENCIL8: + return GL30C.GL_DEPTH24_STENCIL8; + case DEPTH32F_STENCIL8: + return GL30C.GL_DEPTH32F_STENCIL8; + } + + throw new AssertionError("unreachable"); + } + + public int getGlType() { return isCombinedStencil() ? GL30C.GL_DEPTH_STENCIL : GL30C.GL_DEPTH_COMPONENT; } + + public int getGlFormat() + { + switch (this) + { + case DEPTH: + case DEPTH16: + return GL43C.GL_UNSIGNED_SHORT; + case DEPTH24: + case DEPTH32: + return GL43C.GL_UNSIGNED_INT; + case DEPTH32F: + return GL30C.GL_FLOAT; + case DEPTH_STENCIL: + case DEPTH24_STENCIL8: + return GL30C.GL_UNSIGNED_INT_24_8; + case DEPTH32F_STENCIL8: + return GL30C.GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + } + + throw new AssertionError("unreachable"); + } + + public boolean isCombinedStencil() { return combinedStencil; } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhInternalTextureFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhInternalTextureFormat.java new file mode 100644 index 000000000..c37999e6c --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhInternalTextureFormat.java @@ -0,0 +1,131 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject.texture; + +import com.seibel.distanthorizons.common.render.openGl.glObject.enums.EGlVersion; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL30C; +import org.lwjgl.opengl.GL31C; + +import java.util.Locale; +import java.util.Optional; + +public enum EGlDhInternalTextureFormat +{ + RGBA(GL11C.GL_RGBA, EGlVersion.GL_11, EGlDhPixelFormat.RGBA), + + // 8-bit normalized + R8(GL30C.GL_R8, EGlVersion.GL_30, EGlDhPixelFormat.RED), + RG8(GL30C.GL_RG8, EGlVersion.GL_30, EGlDhPixelFormat.RG), + RGB8(GL11C.GL_RGB8, EGlVersion.GL_11, EGlDhPixelFormat.RGB), + RGBA8(GL11C.GL_RGBA8, EGlVersion.GL_11, EGlDhPixelFormat.RGBA), + + // 8-bit signed normalized + R8_SNORM(GL31C.GL_R8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RED), + RG8_SNORM(GL31C.GL_RG8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RG), + RGB8_SNORM(GL31C.GL_RGB8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGB), + RGBA8_SNORM(GL31C.GL_RGBA8_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGBA), + + // 16-bit normalized + R16(GL30C.GL_R16, EGlVersion.GL_30, EGlDhPixelFormat.RED), + RG16(GL30C.GL_RG16, EGlVersion.GL_30, EGlDhPixelFormat.RG), + RGB16(GL11C.GL_RGB16, EGlVersion.GL_11, EGlDhPixelFormat.RGB), + RGBA16(GL11C.GL_RGBA16, EGlVersion.GL_11, EGlDhPixelFormat.RGBA), + + // 16-bit signed normalized + R16_SNORM(GL31C.GL_R16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RED), + RG16_SNORM(GL31C.GL_RG16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RG), + RGB16_SNORM(GL31C.GL_RGB16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGB), + RGBA16_SNORM(GL31C.GL_RGBA16_SNORM, EGlVersion.GL_31, EGlDhPixelFormat.RGBA), + + // 16-bit float + R16F(GL30C.GL_R16F, EGlVersion.GL_30, EGlDhPixelFormat.RED), + RG16F(GL30C.GL_RG16F, EGlVersion.GL_30, EGlDhPixelFormat.RG), + RGB16F(GL30C.GL_RGB16F, EGlVersion.GL_30, EGlDhPixelFormat.RGB), + RGBA16F(GL30C.GL_RGBA16F, EGlVersion.GL_30, EGlDhPixelFormat.RGBA), + + // 32-bit float + R32F(GL30C.GL_R32F, EGlVersion.GL_30, EGlDhPixelFormat.RED), + RG32F(GL30C.GL_RG32F, EGlVersion.GL_30, EGlDhPixelFormat.RG), + RGB32F(GL30C.GL_RGB32F, EGlVersion.GL_30, EGlDhPixelFormat.RGB), + RGBA32F(GL30C.GL_RGBA32F, EGlVersion.GL_30, EGlDhPixelFormat.RGBA), + + // 8-bit integer + R8I(GL30C.GL_R8I, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER), + RG8I(GL30C.GL_RG8I, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER), + RGB8I(GL30C.GL_RGB8I, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER), + RGBA8I(GL30C.GL_RGBA8I, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER), + + // 8-bit unsigned integer + R8UI(GL30C.GL_R8UI, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER), + RG8UI(GL30C.GL_RG8UI, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER), + RGB8UI(GL30C.GL_RGB8UI, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER), + RGBA8UI(GL30C.GL_RGBA8UI, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER), + + // 16-bit integer + R16I(GL30C.GL_R16I, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER), + RG16I(GL30C.GL_RG16I, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER), + RGB16I(GL30C.GL_RGB16I, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER), + RGBA16I(GL30C.GL_RGBA16I, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER), + + // 16-bit unsigned integer + R16UI(GL30C.GL_R16UI, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER), + RG16UI(GL30C.GL_RG16UI, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER), + RGB16UI(GL30C.GL_RGB16UI, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER), + RGBA16UI(GL30C.GL_RGBA16UI, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER), + + // 32-bit integer + R32I(GL30C.GL_R32I, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER), + RG32I(GL30C.GL_RG32I, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER), + RGB32I(GL30C.GL_RGB32I, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER), + RGBA32I(GL30C.GL_RGBA32I, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER), + + // 32-bit unsigned integer + R32UI(GL30C.GL_R32UI, EGlVersion.GL_30, EGlDhPixelFormat.RED_INTEGER), + RG32UI(GL30C.GL_RG32UI, EGlVersion.GL_30, EGlDhPixelFormat.RG_INTEGER), + RGB32UI(GL30C.GL_RGB32UI, EGlVersion.GL_30, EGlDhPixelFormat.RGB_INTEGER), + RGBA32UI(GL30C.GL_RGBA32UI, EGlVersion.GL_30, EGlDhPixelFormat.RGBA_INTEGER), + + // Mixed + R3_G3_B2(GL11C.GL_R3_G3_B2, EGlVersion.GL_11, EGlDhPixelFormat.RGB), + RGB5_A1(GL11C.GL_RGB5_A1, EGlVersion.GL_11, EGlDhPixelFormat.RGBA), + RGB10_A2(GL11C.GL_RGB10_A2, EGlVersion.GL_11, EGlDhPixelFormat.RGBA), + R11F_G11F_B10F(GL30C.GL_R11F_G11F_B10F, EGlVersion.GL_30, EGlDhPixelFormat.RGB), + RGB9_E5(GL30C.GL_RGB9_E5, EGlVersion.GL_30, EGlDhPixelFormat.RGB); + + + + private final int glFormat; + private final EGlVersion minimumGlVersion; + private final EGlDhPixelFormat expectedPixelFormat; + + + + EGlDhInternalTextureFormat(int glFormat, EGlVersion minimumGlVersion, EGlDhPixelFormat expectedPixelFormat) + { + this.glFormat = glFormat; + this.minimumGlVersion = minimumGlVersion; + this.expectedPixelFormat = expectedPixelFormat; + } + + + + public static Optional fromString(String name) + { + try + { + return Optional.of(EGlDhInternalTextureFormat.valueOf(name.toUpperCase(Locale.US))); + } + catch (IllegalArgumentException e) + { + return Optional.empty(); + } + } + + public int getGlFormat() { return this.glFormat; } + + public EGlDhPixelFormat getPixelFormat() { return this.expectedPixelFormat; } + + public EGlVersion getMinimumGlVersion() { return this.minimumGlVersion; } + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhPixelFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhPixelFormat.java new file mode 100644 index 000000000..989575037 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhPixelFormat.java @@ -0,0 +1,61 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject.texture; + +import com.seibel.distanthorizons.common.render.openGl.glObject.enums.EGlVersion; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL12C; +import org.lwjgl.opengl.GL30C; + +import java.util.Locale; +import java.util.Optional; + +public enum EGlDhPixelFormat +{ + RED(GL11C.GL_RED, EGlVersion.GL_11, false), + RG(GL30C.GL_RG, EGlVersion.GL_30, false), + RGB(GL11C.GL_RGB, EGlVersion.GL_11, false), + BGR(GL12C.GL_BGR, EGlVersion.GL_12, false), + RGBA(GL11C.GL_RGBA, EGlVersion.GL_11, false), + BGRA(GL12C.GL_BGRA, EGlVersion.GL_12, false), + RED_INTEGER(GL30C.GL_RED_INTEGER, EGlVersion.GL_30, true), + RG_INTEGER(GL30C.GL_RG_INTEGER, EGlVersion.GL_30, true), + RGB_INTEGER(GL30C.GL_RGB_INTEGER, EGlVersion.GL_30, true), + BGR_INTEGER(GL30C.GL_BGR_INTEGER, EGlVersion.GL_30, true), + RGBA_INTEGER(GL30C.GL_RGBA_INTEGER, EGlVersion.GL_30, true), + BGRA_INTEGER(GL30C.GL_BGRA_INTEGER, EGlVersion.GL_30, true); + + + + private final int glFormat; + private final EGlVersion minimumGlVersion; + private final boolean isInteger; + + + + EGlDhPixelFormat(int glFormat, EGlVersion minimumGlVersion, boolean isInteger) + { + this.glFormat = glFormat; + this.minimumGlVersion = minimumGlVersion; + this.isInteger = isInteger; + } + + + + public static Optional fromString(String name) + { + try + { + return Optional.of(EGlDhPixelFormat.valueOf(name.toUpperCase(Locale.US))); + } + catch (IllegalArgumentException e) + { + return Optional.empty(); + } + } + + public int getGlFormat() { return this.glFormat; } + + public EGlVersion getMinimumGlVersion() { return this.minimumGlVersion; } + + public boolean isInteger() { return this.isInteger; } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhPixelType.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhPixelType.java new file mode 100644 index 000000000..0dd223131 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/EGlDhPixelType.java @@ -0,0 +1,65 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject.texture; + +import com.seibel.distanthorizons.common.render.openGl.glObject.enums.EGlVersion; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL12C; +import org.lwjgl.opengl.GL30C; + +import java.util.Locale; +import java.util.Optional; + +public enum EGlDhPixelType +{ + BYTE(GL11C.GL_BYTE, EGlVersion.GL_11), + SHORT(GL11C.GL_SHORT, EGlVersion.GL_11), + INT(GL11C.GL_INT, EGlVersion.GL_11), + HALF_FLOAT(GL30C.GL_HALF_FLOAT, EGlVersion.GL_30), + FLOAT(GL11C.GL_FLOAT, EGlVersion.GL_11), + UNSIGNED_BYTE(GL11C.GL_UNSIGNED_BYTE, EGlVersion.GL_11), + UNSIGNED_BYTE_3_3_2(GL12C.GL_UNSIGNED_BYTE_3_3_2, EGlVersion.GL_12), + UNSIGNED_BYTE_2_3_3_REV(GL12C.GL_UNSIGNED_BYTE_2_3_3_REV, EGlVersion.GL_12), + UNSIGNED_SHORT(GL11C.GL_UNSIGNED_SHORT, EGlVersion.GL_11), + UNSIGNED_SHORT_5_6_5(GL12C.GL_UNSIGNED_SHORT_5_6_5, EGlVersion.GL_12), + UNSIGNED_SHORT_5_6_5_REV(GL12C.GL_UNSIGNED_SHORT_5_6_5_REV, EGlVersion.GL_12), + UNSIGNED_SHORT_4_4_4_4(GL12C.GL_UNSIGNED_SHORT_4_4_4_4, EGlVersion.GL_12), + UNSIGNED_SHORT_4_4_4_4_REV(GL12C.GL_UNSIGNED_SHORT_4_4_4_4_REV, EGlVersion.GL_12), + UNSIGNED_SHORT_5_5_5_1(GL12C.GL_UNSIGNED_SHORT_5_5_5_1, EGlVersion.GL_12), + UNSIGNED_SHORT_1_5_5_5_REV(GL12C.GL_UNSIGNED_SHORT_1_5_5_5_REV, EGlVersion.GL_12), + UNSIGNED_INT(GL11C.GL_UNSIGNED_INT, EGlVersion.GL_11), + UNSIGNED_INT_8_8_8_8(GL12C.GL_UNSIGNED_INT_8_8_8_8, EGlVersion.GL_12), + UNSIGNED_INT_8_8_8_8_REV(GL12C.GL_UNSIGNED_INT_8_8_8_8_REV, EGlVersion.GL_12), + UNSIGNED_INT_10_10_10_2(GL12C.GL_UNSIGNED_INT_10_10_10_2, EGlVersion.GL_12), + UNSIGNED_INT_2_10_10_10_REV(GL12C.GL_UNSIGNED_INT_2_10_10_10_REV, EGlVersion.GL_12); + + + + private final int glFormat; + private final EGlVersion minimumGlVersion; + + + + EGlDhPixelType(int glFormat, EGlVersion minimumGlVersion) + { + this.glFormat = glFormat; + this.minimumGlVersion = minimumGlVersion; + } + + + + public static Optional fromString(String name) + { + try + { + return Optional.of(EGlDhPixelType.valueOf(name.toUpperCase(Locale.US))); + } + catch (IllegalArgumentException e) + { + return Optional.empty(); + } + } + + public int getGlFormat() { return glFormat; } + + public EGlVersion getMinimumGlVersion() { return minimumGlVersion; } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/GlDhColorTexture.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/GlDhColorTexture.java new file mode 100644 index 000000000..226022126 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/GlDhColorTexture.java @@ -0,0 +1,183 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject.texture; + +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import org.joml.Vector2i; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL13C; +import org.lwjgl.opengl.GL43C; + +import java.nio.ByteBuffer; + +public class GlDhColorTexture +{ + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + private final EGlDhInternalTextureFormat internalFormat; + private final EGlDhPixelFormat format; + private final EGlDhPixelType type; + private int width; + private int height; + + private boolean isValid; + /** AKA, the OpenGL name of this texture */ + private final int id; + + private static final ByteBuffer NULL_BUFFER = null; + + + + //=============// + // constructor // + //=============// + + public GlDhColorTexture(Builder builder) + { + this.isValid = true; + + this.internalFormat = builder.internalFormat; + this.format = builder.format; + this.type = builder.type; + + this.width = builder.width; + this.height = builder.height; + + this.id = GL43C.glGenTextures(); + + boolean isPixelFormatInteger = builder.internalFormat.getPixelFormat().isInteger(); + this.setupTexture(this.id, builder.width, builder.height, !isPixelFormatInteger); // this binds the texture + + // Clean up after ourselves + // This is strictly defensive to ensure that other buggy code doesn't tamper with our textures + GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0); + } + + + + //=========// + // methods // + //=========// + + private void setupTexture(int id, int width, int height, boolean allowsLinear) + { + this.resizeTexture(id, width, height); + + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST); + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST); + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE); + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE); + + // disable mip-mapping since DH is just going to draw straight to the screen + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); + } + + private void resizeTexture(int texture, int width, int height) + { + GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, texture); + GL43C.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, this.internalFormat.getGlFormat(), width, height, 0, this.format.getGlFormat(), this.type.getGlFormat(), NULL_BUFFER); + } + + void resize(Vector2i textureScaleOverride) { this.resize(textureScaleOverride.x, textureScaleOverride.y); } + + // Package private, call CompositeRenderTargets#resizeIfNeeded instead. + public void resize(int width, int height) + { + this.throwIfInvalid(); + + this.width = width; + this.height = height; + + this.resizeTexture(this.id, width, height); + } + + public EGlDhInternalTextureFormat getInternalFormat() { return this.internalFormat; } + + public int getTextureId() + { + this.throwIfInvalid(); + return this.id; + } + + public int getWidth() { return this.width; } + + public int getHeight() { return this.height; } + + public void destroy() + { + this.throwIfInvalid(); + this.isValid = false; + + GLMC.glDeleteTextures(this.id); + } + + /** @throws IllegalStateException if the texture isn't valid */ + private void throwIfInvalid() + { + if (!this.isValid) + { + throw new IllegalStateException("Attempted to use a deleted composite render target"); + } + } + + public static Builder builder() { return new Builder(); } + + + + //================// + // helper classes // + //================// + + public static class Builder + { + private EGlDhInternalTextureFormat internalFormat = EGlDhInternalTextureFormat.RGBA8; + private int width = 0; + private int height = 0; + private EGlDhPixelFormat format = EGlDhPixelFormat.RGBA; + private EGlDhPixelType type = EGlDhPixelType.UNSIGNED_BYTE; + + private Builder() + { + // No-op + } + + public Builder setInternalFormat(EGlDhInternalTextureFormat format) + { + this.internalFormat = format; + return this; + } + + public Builder setDimensions(int width, int height) + { + if (width <= 0) + { + throw new IllegalArgumentException("Width must be greater than zero"); + } + + if (height <= 0) + { + throw new IllegalArgumentException("Height must be greater than zero"); + } + + this.width = width; + this.height = height; + + return this; + } + + public Builder setPixelFormat(EGlDhPixelFormat pixelFormat) + { + this.format = pixelFormat; + return this; + } + + public Builder setPixelType(EGlDhPixelType pixelType) + { + this.type = pixelType; + return this; + } + + public GlDhColorTexture build() { return new GlDhColorTexture(this); } + + } +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/GlDhDepthTexture.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/GlDhDepthTexture.java new file mode 100644 index 000000000..0de544062 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/texture/GlDhDepthTexture.java @@ -0,0 +1,62 @@ +package com.seibel.distanthorizons.common.render.openGl.glObject.texture; + +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL13C; +import org.lwjgl.opengl.GL43C; + +import java.nio.ByteBuffer; + +public class GlDhDepthTexture +{ + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + private int id; + public GlDhDepthTexture(int width, int height, EGlDhDepthBufferFormat format) + { + this.id = GL43C.glGenTextures(); + + this.resize(width, height, format); + + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST); + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST); + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE); + GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE); + + // disable mip-mapping since DH is just going to draw straight to the screen + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); + + GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0); + } + + // For internal use by Iris for copying data. Do not use this in DH. + public GlDhDepthTexture(int id) { this.id = id; } + + public void resize(int width, int height, EGlDhDepthBufferFormat format) + { + GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, this.getTextureId()); + GL43C.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, format.getGlInternalFormat(), width, height, 0, + format.getGlType(), format.getGlFormat(), (ByteBuffer) null); + } + + public int getTextureId() + { + if (this.id == -1) + { + throw new IllegalStateException("Depth texture does not exist!"); + } + + return this.id; + } + + public void destroy() + { + GLMC.glDeleteTextures(this.getTextureId()); + this.id = -1; + } + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlAbstractVertexAttribute.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlAbstractVertexAttribute.java new file mode 100644 index 000000000..a599dc96d --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlAbstractVertexAttribute.java @@ -0,0 +1,92 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute; + +import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; +import org.lwjgl.opengl.GL32; + +/** + * Base for binding/unbinding Vertex Attribute objects (VAO's). + * + * @see GlVertexAttributePostGL43 + * @see GlVertexAttributePreGL43 + */ +public abstract class GlAbstractVertexAttribute +{ + /** Stores the handle of the AbstractVertexAttribute. */ + public final int id; + + + + //==============// + // constructors // + //==============// + + // This will bind AbstractVertexAttribute + protected GlAbstractVertexAttribute() + { + this.id = GL32.glGenVertexArrays(); + GL32.glBindVertexArray(this.id); + } + + public static GlAbstractVertexAttribute create() + { + if (GLProxy.getInstance().vertexAttributeBufferBindingSupported) + { + return new GlVertexAttributePostGL43(); + } + else + { + return new GlVertexAttributePreGL43(); + } + } + + + + //=========// + // binding // + //=========// + + public void bind() { GL32.glBindVertexArray(this.id); } + public void unbind() { GL32.glBindVertexArray(0); } + + /** Always remember to always free your resources! */ + public void free() { GL32.glDeleteVertexArrays(this.id); } + + + + //==================// + // abstract methods // + //==================// + + /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */ + public abstract void bindBufferToAllBindingPoints(int buffer); + /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */ + public abstract void bindBufferToBindingPoint(int buffer, int bindingPoint); + /** Requires both AbstractVertexAttribute to be bound */ + public abstract void unbindBuffersFromAllBindingPoint(); + /** Requires both AbstractVertexAttribute to be bound */ + public abstract void unbindBuffersFromBindingPoint(int bindingPoint); + /** Requires both AbstractVertexAttribute to be bound */ + public abstract void setVertexAttribute(int bindingPoint, int attributeIndex, GlVertexPointer attribute); + /** Requires both AbstractVertexAttribute to be bound */ + public abstract void completeAndCheck(int expectedStrideSize); + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexAttributePostGL43.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexAttributePostGL43.java new file mode 100644 index 000000000..9a2328212 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexAttributePostGL43.java @@ -0,0 +1,155 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import org.lwjgl.opengl.GL43; + +/** + * In OpenGL 4.3 and later, Vertex Attribute got a make-over. + * Now it provides support for buffer binding points natively. + * This means that setting up the VAO is just use ONE native call when + * binding to a buffer.

+ * + * Since I no longer need to implement binding points, I also no + * longer needs to keep track of Pointers. + */ +public final class GlVertexAttributePostGL43 extends GlAbstractVertexAttribute +{ + private static final DhLogger LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile) + .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat) + .build(); + + + int numberOfBindingPoints = 0; + int strideSize = 0; + + + + //=============// + // constructor // + //=============// + + /** This will bind the {@link GlAbstractVertexAttribute} */ + public GlVertexAttributePostGL43() + { + super(); // also bind AbstractVertexAttribute + } + + + + //=========// + // binding // + //=========// + + /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */ + @Override + public void bindBufferToAllBindingPoints(int buffer) + { + for (int i = 0; i < this.numberOfBindingPoints; i++) + { + GL43.glBindVertexBuffer(i, buffer, 0, this.strideSize); + } + } + + /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */ + @Override + public void bindBufferToBindingPoint(int buffer, int bindingPoint) + { + GL43.glBindVertexBuffer(bindingPoint, buffer, 0, this.strideSize); + } + + + + //===========// + // unbinding // + //===========// + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void unbindBuffersFromAllBindingPoint() + { + for (int i = 0; i < this.numberOfBindingPoints; i++) + { + GL43.glBindVertexBuffer(i, 0, 0, 0); + } + } + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void unbindBuffersFromBindingPoint(int bindingPoint) + { + GL43.glBindVertexBuffer(bindingPoint, 0, 0, 0); + } + + + + //==========================// + // manual attribute setting // + //==========================// + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void setVertexAttribute(int bindingPoint, int attributeIndex, GlVertexPointer attribute) + { + if (attribute.useInteger) + { + GL43.glVertexAttribIFormat(attributeIndex, attribute.elementCount, attribute.glType, this.strideSize); + } + else + { + GL43.glVertexAttribFormat(attributeIndex, attribute.elementCount, attribute.glType, + attribute.normalized, this.strideSize); // Here strideSize is new attrib offset + } + + this.strideSize += attribute.byteSize; + if (this.numberOfBindingPoints <= bindingPoint) + { + this.numberOfBindingPoints = bindingPoint + 1; + } + GL43.glVertexAttribBinding(attributeIndex, bindingPoint); + GL43.glEnableVertexAttribArray(attributeIndex); + } + + + + //============// + // validation // + //============// + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void completeAndCheck(int expectedStrideSize) + { + if (this.strideSize != expectedStrideSize) + { + LOGGER.error("Vertex Attribute calculated stride size " + this.strideSize + + " does not match the provided expected stride size " + expectedStrideSize + "!"); + throw new IllegalArgumentException("Vertex Attribute Incorrect Format"); + } + + LOGGER.info("Vertex Attribute (GL43+) completed. It contains " + this.numberOfBindingPoints + + " binding points and a stride size of " + this.strideSize); + } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexAttributePreGL43.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexAttributePreGL43.java new file mode 100644 index 000000000..49ded30cc --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexAttributePreGL43.java @@ -0,0 +1,253 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import org.lwjgl.opengl.GL32; + + +public final class GlVertexAttributePreGL43 extends GlAbstractVertexAttribute +{ + private static final DhLogger LOGGER = new DhLoggerBuilder() + .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile) + .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat) + .build(); + + + // I tried to use raw arrays as much as possible since those lookups + // happen every frame, and the speed directly affects fps + int strideSize = 0; + int[][] bindingPointsToIndex; + GlVertexPointer[] pointers; + int[] pointersOffset; + + TreeMap> bindingPointsToIndexBuilder; + ArrayList pointersBuilder; + + + + //=============// + // constructor // + //=============// + + /** This will bind the {@link GlAbstractVertexAttribute} */ + public GlVertexAttributePreGL43() + { + super(); // also bind AbstractVertexAttribute + this.bindingPointsToIndexBuilder = new TreeMap<>(); + this.pointersBuilder = new ArrayList<>(); + } + + + + //=========// + // binding // + //=========// + + /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */ + @Override + public void bindBufferToAllBindingPoints(int buffer) + { + for (int i = 0; i < this.pointers.length; i++) + { + GL32.glEnableVertexAttribArray(i); + } + + for (int i = 0; i < this.pointers.length; i++) + { + GlVertexPointer pointer = this.pointers[i]; + if (pointer == null) + { + continue; + } + + if (pointer.useInteger) + { + GL32.glVertexAttribIPointer(i, pointer.elementCount, pointer.glType, + this.strideSize, this.pointersOffset[i]); + } + else + { + GL32.glVertexAttribPointer(i, pointer.elementCount, pointer.glType, + pointer.normalized, this.strideSize, this.pointersOffset[i]); + } + } + } + + /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */ + @Override + public void bindBufferToBindingPoint(int buffer, int bindingPoint) + { + int[] bindingPointIndexes = this.bindingPointsToIndex[bindingPoint]; + + for (int bindingPointIndex : bindingPointIndexes) + { + GL32.glEnableVertexAttribArray(bindingPointIndex); + } + + for (int bindingPointIndex : bindingPointIndexes) + { + GlVertexPointer pointer = this.pointers[bindingPointIndex]; + if (pointer == null) + { + continue; + } + + if (pointer.useInteger) + { + GL32.glVertexAttribIPointer(bindingPointIndex, pointer.elementCount, pointer.glType, + this.strideSize, this.pointersOffset[bindingPointIndex]); + } + else + { + GL32.glVertexAttribPointer(bindingPointIndex, pointer.elementCount, pointer.glType, + pointer.normalized, this.strideSize, this.pointersOffset[bindingPointIndex]); + } + } + + } + + + + //===========// + // unbinding // + //===========// + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void unbindBuffersFromAllBindingPoint() + { + for (int i = 0; i < this.pointers.length; i++) + { + GL32.glDisableVertexAttribArray(i); + } + } + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void unbindBuffersFromBindingPoint(int bindingPoint) + { + int[] bindingPointIndexes = this.bindingPointsToIndex[bindingPoint]; + for (int bindingPointIndex : bindingPointIndexes) + { + GL32.glDisableVertexAttribArray(bindingPointIndex); + } + } + + + + //==========================// + // manual attribute setting // + //==========================// + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void setVertexAttribute(int bindingPoint, int attributeIndex, GlVertexPointer attribute) + { + TreeSet intArray = this.bindingPointsToIndexBuilder.computeIfAbsent(bindingPoint, k -> new TreeSet<>()); + intArray.add(attributeIndex); + + while (this.pointersBuilder.size() <= attributeIndex) + { + // This is dumb, but ArrayList doesn't have a resize, And this code + // should only be run when it's building the Vertex Attribute anyway. + this.pointersBuilder.add(null); + } + this.pointersBuilder.set(attributeIndex, attribute); + } + + + + //============// + // validation // + //============// + + /** Requires AbstractVertexAttribute to be bound */ + @Override + public void completeAndCheck(int expectedStrideSize) + { + int maxBindPointNumber = this.bindingPointsToIndexBuilder.lastKey(); + this.bindingPointsToIndex = new int[maxBindPointNumber + 1][]; + + this.bindingPointsToIndexBuilder.forEach((Integer i, TreeSet set) -> + { + this.bindingPointsToIndex[i] = new int[set.size()]; + Iterator iter = set.iterator(); + for (int j = 0; j < set.size(); j++) + { + this.bindingPointsToIndex[i][j] = iter.next(); + } + }); + + this.pointers = this.pointersBuilder.toArray(new GlVertexPointer[this.pointersBuilder.size()]); + this.pointersOffset = new int[this.pointers.length]; + this.pointersBuilder = null; // Release the builder + this.bindingPointsToIndexBuilder = null; // Release the builder + + // Check if all pointers are valid + int currentOffset = 0; + for (int i = 0; i < this.pointers.length; i++) + { + GlVertexPointer pointer = this.pointers[i]; + if (pointer == null) + { + LOGGER.warn("Vertex Attribute index " + i + " is not set! No index should be skipped normally!"); + continue; + } + this.pointersOffset[i] = currentOffset; + currentOffset += pointer.byteSize; + } + + if (currentOffset != expectedStrideSize) + { + LOGGER.error("Vertex Attribute calculated stride size " + currentOffset + + " does not match the provided expected stride size " + expectedStrideSize + "!"); + throw new IllegalArgumentException("Vertex Attribute Incorrect Format"); + } + this.strideSize = currentOffset; + LOGGER.info("Vertex Attribute (pre GL43) completed."); + + // Debug logging + LOGGER.debug("AttributeIndex: ElementCount, glType, normalized, strideSize, offset"); + + for (int i = 0; i < this.pointers.length; i++) + { + GlVertexPointer pointer = this.pointers[i]; + if (pointer == null) + { + LOGGER.debug(i + ": Null!!!!"); + } + else + { + LOGGER.debug(i + ": " + pointer.elementCount + ", " + + pointer.glType + ", " + pointer.normalized + ", " + this.strideSize + ", " + this.pointersOffset[i]); + } + } + + } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexPointer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexPointer.java new file mode 100644 index 000000000..6fca75b10 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/vertexAttribute/GlVertexPointer.java @@ -0,0 +1,72 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute; + +import com.seibel.distanthorizons.coreapi.util.MathUtil; +import org.lwjgl.opengl.GL32; + +public final class GlVertexPointer +{ + public final int elementCount; + public final int glType; + public final boolean normalized; + public final int byteSize; + public final boolean useInteger; + + + + // basic constructors // + + public GlVertexPointer(int elementCount, int glType, boolean normalized, int byteSize, boolean useInteger) + { + this.elementCount = elementCount; + this.glType = glType; + this.normalized = normalized; + this.byteSize = byteSize; + this.useInteger = useInteger; + } + public GlVertexPointer(int elementCount, int glType, boolean normalized, int byteSize) + { + this(elementCount, glType, normalized, byteSize, false); + } + private static int _align(int bytes) { return MathUtil.ceilDiv(bytes, 4) * 4; } + + + + // named constructors // + + public static GlVertexPointer addFloatPointer(boolean normalized) { return new GlVertexPointer(1, GL32.GL_FLOAT, normalized, Float.BYTES); } + public static GlVertexPointer addVec2Pointer(boolean normalized) { return new GlVertexPointer(2, GL32.GL_FLOAT, normalized, Float.BYTES * 2); } + public static GlVertexPointer addVec3Pointer(boolean normalized) { return new GlVertexPointer(3, GL32.GL_FLOAT, normalized, Float.BYTES * 3); } + public static GlVertexPointer addVec4Pointer(boolean normalized) { return new GlVertexPointer(4, GL32.GL_FLOAT, normalized, Float.BYTES * 4); } + /** Always aligned to 4 bytes */ + public static GlVertexPointer addUnsignedBytePointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 4, useInteger); } + /** aligned to 4 bytes */ + public static GlVertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized, boolean useInteger) + { return new GlVertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount), useInteger); } + public static GlVertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized, boolean useInteger) + { return new GlVertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount * 2), useInteger); } + public static GlVertexPointer addShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new GlVertexPointer(elementCount, GL32.GL_SHORT, normalized, _align(elementCount * 2), useInteger); } + public static GlVertexPointer addIntPointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(1, GL32.GL_INT, normalized, 4, useInteger); } + public static GlVertexPointer addIVec2Pointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(2, GL32.GL_INT, normalized, 8, useInteger); } + public static GlVertexPointer addIVec3Pointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(3, GL32.GL_INT, normalized, 12, useInteger); } + public static GlVertexPointer addIVec4Pointer(boolean normalized, boolean useInteger) { return new GlVertexPointer(4, GL32.GL_INT, normalized, 16, useInteger); } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/GlScreenQuad.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/GlScreenQuad.java new file mode 100644 index 000000000..5441d59d9 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/GlScreenQuad.java @@ -0,0 +1,116 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer; +import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +/** + * Renders a full-screen textured quad to the screen. + * Used in composite / deferred rendering (IE fog). + */ +public class GlScreenQuad +{ + public static GlScreenQuad INSTANCE = new GlScreenQuad(); + + private static final float[] BOX_VERTICES = { + -1, -1, + 1, -1, + 1, 1, + + -1, -1, + 1, 1, + -1, 1, + }; + + private GLVertexBuffer boxBuffer; + private GlAbstractVertexAttribute va; + private boolean init = false; + + + + //=============// + // constructor // + //=============// + //region + + private GlScreenQuad() { } + + public void init() + { + if (this.init) return; + this.init = true; + + this.va = GlAbstractVertexAttribute.create(); + this.va.bind(); + + // Pos + this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec2Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 2); + + // Framebuffer + this.createBuffer(); + } + private void createBuffer() + { + ByteBuffer buffer = MemoryUtil.memAlloc(BOX_VERTICES.length * Float.BYTES); + buffer.asFloatBuffer().put(BOX_VERTICES); + buffer.rewind(); + + this.boxBuffer = new GLVertexBuffer(false); + this.boxBuffer.bind(); + this.boxBuffer.uploadBuffer(buffer, BOX_VERTICES.length, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); + MemoryUtil.memFree(buffer); + } + + //endregion + + + + //===========// + // rendering // + //===========// + //region + + public void render() + { + this.init(); + + this.boxBuffer.bind(); + + this.va.bind(); + this.va.bindBufferToAllBindingPoints(this.boxBuffer.getId()); + + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + + GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/apply/GlDhApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/apply/GlDhApplyShader.java new file mode 100644 index 000000000..4a4f4b04b --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/apply/GlDhApplyShader.java @@ -0,0 +1,190 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.apply; + +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.GLState; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.logging.DhLogger; +import org.lwjgl.opengl.GL32; + +/** + * Copies {@link com.seibel.distanthorizons.core.render.renderer.LodRenderer}'s currently active color and depth texture to Minecraft's framebuffer. + */ +public class GlDhApplyShader extends GlAbstractShaderRenderer +{ + public static GlDhApplyShader INSTANCE = new GlDhApplyShader(); + + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + // uniforms + public int gDhColorTextureUniform; + public int gDepthMapUniform; + + + + //=======// + // setup // + //=======// + //region + + private GlDhApplyShader() { } + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/shared/gl/apply.frag", + "vPosition" + ); + + // uniform setup + this.gDhColorTextureUniform = this.shader.getUniformLocation("gDhColorTexture"); + this.gDepthMapUniform = this.shader.getUniformLocation("gDhDepthTexture"); + + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + protected void onRender() + { + if (MC_RENDER.mcRendersToFrameBuffer()) + { + this.renderToFrameBuffer(); + } + else + { + this.renderToMcTexture(); + } + } + private void renderToFrameBuffer() + { + int targetFrameBuffer = MC_RENDER.getTargetFramebuffer(); + if (targetFrameBuffer == -1) + { + return; + } + + + try (GLState state = new GLState()) + { + + GLMC.disableDepthTest(); + + // blending isn't needed, we're manually merging the MC and DH textures + // Note: this prevents the sun/moon and stars from rendering through transparent LODs, + // however this also fixes transparent LODs from glowing when rendered against the sky during the day + GLMC.disableBlend(); + + // old blending logic in case it's ever needed: + //GLMC.enableBlend(); + //GL32.glBlendEquation(GL32.GL_FUNC_ADD); + //GLMC.glBlendFunc(GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveColorTextureId()); + GL32.glUniform1i(this.gDhColorTextureUniform, 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId()); + GL32.glUniform1i(this.gDepthMapUniform, 1); + + // Copy to MC's framebuffer + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, targetFrameBuffer); + + GlScreenQuad.INSTANCE.render(); + } + // everything's been restored, except at this point the MC framebuffer should now be used instead + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, targetFrameBuffer); + + } + private void renderToMcTexture() + { + int targetColorTextureId = MC_RENDER.getColorTextureId(); + if (targetColorTextureId == -1) + { + return; + } + + int dhFrameBufferId = GlDhMetaRenderer.INSTANCE.getActiveFramebufferId(); + if (dhFrameBufferId == -1) + { + return; + } + + int mcFrameBufferId = MC_RENDER.getTargetFramebuffer(); + if (mcFrameBufferId == -1) + { + return; + } + + + + try (GLState state = new GLState()) + { + GLMC.disableDepthTest(); + + // blending isn't needed, we're just directly merging the MC and DH textures + // Note: this prevents the sun/moon and stars from rendering through transparent LODs, + // but it also resolves some other issues, so it's likely not an issue + GLMC.disableBlend(); + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveColorTextureId()); + GL32.glUniform1i(this.gDhColorTextureUniform, 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId()); + GL32.glUniform1i(this.gDepthMapUniform, 1); + + + + GL32.glFramebufferTexture(GL32.GL_DRAW_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, targetColorTextureId, 0); + + // Copy to MC's texture via MC's framebuffer + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, dhFrameBufferId); + + GlScreenQuad.INSTANCE.render(); + } + // everything's been restored, except at this point the MC framebuffer should now be used instead + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, mcFrameBufferId); + + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeApplyShader.java new file mode 100644 index 000000000..83e30f08e --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeApplyShader.java @@ -0,0 +1,116 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade; + +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.render.RenderParams; +import org.lwjgl.opengl.GL32; + +/** + * Draws the Fade texture onto Minecraft's FrameBuffer.

+ * + * See Also:
+ * {@link GlVanillaFadeRenderer} - Parent to this shader.
+ * {@link GlDhVanillaFadeShader} - draws the Fade texture.
+ */ +public class GlDhFarFadeApplyShader extends GlAbstractShaderRenderer +{ + public static GlDhFarFadeApplyShader INSTANCE = new GlDhFarFadeApplyShader(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + + public int fadeTexture; + + public int readFramebuffer; + public int drawFramebuffer; + + // uniforms + public int uFadeColorTextureUniform = -1; + + + + //=============// + // constructor // + //=============// + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/fade/gl/apply.frag", + "vPosition" + ); + + // uniform setup + this.uFadeColorTextureUniform = this.shader.getUniformLocation("uFadeColorTextureUniform"); + + } + + + + //=============// + // render prep // + //=============// + + @Override + protected void onApplyUniforms(RenderParams renderParams) + { + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(this.fadeTexture); + GL32.glUniform1i(this.uFadeColorTextureUniform, 0); + + } + + + + //========// + // render // + //========// + + @Override + protected void onRender() + { + GLMC.disableBlend(); + + // Depth testing must be disabled otherwise this application shader won't apply anything. + // setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually, + // it should be automatically restored after rendering is complete. + GLMC.disableDepthTest(); + + + // apply the rendered Fade to Minecraft's framebuffer + GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, this.readFramebuffer); + GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, this.drawFramebuffer); + + GlScreenQuad.INSTANCE.render(); + + GLMC.enableDepthTest(); + + } + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeRenderer.java new file mode 100644 index 000000000..f9a7d7960 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeRenderer.java @@ -0,0 +1,165 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade; + +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFarFadeRenderer; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL43C; + +import java.nio.ByteBuffer; + +/** + * Handles fading MC and DH together via {@link GlDhFarFadeShader} and {@link GlDhFarFadeApplyShader}.

+ * + * {@link GlDhFarFadeShader} - draws the Fade to a texture.
+ * {@link GlDhFarFadeApplyShader} - draws the Fade texture to DH's framebuffer.
+ */ +public class GlDhFarFadeRenderer implements IDhFarFadeRenderer +{ + + public static GlDhFarFadeRenderer INSTANCE = new GlDhFarFadeRenderer(); + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + private boolean init = false; + + private int width = -1; + private int height = -1; + private int fadeFramebuffer = -1; + + private int fadeTexture = -1; + + + + //=============// + // constructor // + //=============// + //region + + private GlDhFarFadeRenderer() { } + + public void init() + { + if (this.init) return; + this.init = true; + + GlDhFarFadeShader.INSTANCE.init(); + GlDhFarFadeApplyShader.INSTANCE.init(); + } + + private void createFramebuffer(int width, int height) + { + if (this.fadeFramebuffer != -1) + { + GL32.glDeleteFramebuffers(this.fadeFramebuffer); + this.fadeFramebuffer = -1; + } + + this.fadeFramebuffer = GL32.glGenFramebuffers(); + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fadeFramebuffer); + + + if (this.fadeTexture != -1) + { + GLMC.glDeleteTextures(this.fadeTexture); + this.fadeTexture = -1; + } + + this.fadeTexture = GL32.glGenTextures(); + { + GLMC.glBindTexture(this.fadeTexture); + GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); + + // disable mip-mapping since DH is just going to draw straight to the screen + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); + } + + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0); + + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + try + { + //profiler.push("Fade Generate"); + + this.init(); + + // resize the framebuffer if necessary + int width = MC_RENDER.getTargetFramebufferViewportWidth(); + int height = MC_RENDER.getTargetFramebufferViewportHeight(); + if (this.width != width || this.height != height) + { + this.width = width; + this.height = height; + this.createFramebuffer(width, height); + } + + + GlDhFarFadeShader.INSTANCE.frameBuffer = this.fadeFramebuffer; + GlDhFarFadeShader.INSTANCE.setProjectionMatrix(renderParams.mcModelViewMatrix, renderParams.mcProjectionMatrix); + GlDhFarFadeShader.INSTANCE.render(renderParams); + + //profiler.popPush("Fade Apply"); + + GlDhFarFadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture; + GlDhFarFadeApplyShader.INSTANCE.readFramebuffer = GlDhFarFadeShader.INSTANCE.frameBuffer; + GlDhFarFadeApplyShader.INSTANCE.drawFramebuffer = GlDhMetaRenderer.INSTANCE.getActiveFramebufferId(); + GlDhFarFadeApplyShader.INSTANCE.render(renderParams); + } + catch (Exception e) + { + LOGGER.error("Unexpected error during fade render, error: ["+e.getMessage()+"].", e); + } + finally + { + //profiler.pop(); + } + } + + //emdregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeShader.java new file mode 100644 index 000000000..ae9a511ff --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeShader.java @@ -0,0 +1,167 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade; + +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import org.lwjgl.opengl.GL32; + +public class GlDhFarFadeShader extends GlAbstractShaderRenderer +{ + public static GlDhFarFadeShader INSTANCE = new GlDhFarFadeShader(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + public int frameBuffer = -1; + + private Mat4f inverseDhMvmProjMatrix; + + + // Uniforms + + /** Inverted Model View Projection matrix */ + public int uDhInvMvmProj = -1; + + public int uDhDepthTexture = -1; + public int uMcColorTexture = -1; + public int uDhColorTexture = -1; + + public int uStartFadeBlockDistance = -1; + public int uEndFadeBlockDistance = -1; + + + + //=============// + // constructor // + //=============// + + public GlDhFarFadeShader() { } + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/fade/gl/dhFade.frag", + "vPosition" + ); + + // all uniforms should be tryGet... + // because disabling fade can cause the GLSL to optimize out most (if not all) uniforms + + // near fade + this.uDhInvMvmProj = this.shader.tryGetUniformLocation("uDhInvMvmProj"); + + this.uDhDepthTexture = this.shader.tryGetUniformLocation("uDhDepthTexture"); + this.uMcColorTexture = this.shader.tryGetUniformLocation("uMcColorTexture"); + this.uDhColorTexture = this.shader.tryGetUniformLocation("uDhColorTexture"); + + this.uStartFadeBlockDistance = this.shader.tryGetUniformLocation("uStartFadeBlockDistance"); + this.uEndFadeBlockDistance = this.shader.tryGetUniformLocation("uEndFadeBlockDistance"); + + } + + + + //=============// + // render prep // + //=============// + + @Override + protected void onApplyUniforms(RenderParams renderParams) + { + this.shader.setUniform(this.uDhInvMvmProj, this.inverseDhMvmProjMatrix); + + + float dhFarClipDistance = RenderUtil.getFarClipPlaneDistanceInBlocks(); + float fadeStartDistance = dhFarClipDistance * 0.5f; + float fadeEndDistance = dhFarClipDistance * 0.9f; + + this.shader.setUniform(this.uStartFadeBlockDistance, fadeStartDistance); + this.shader.setUniform(this.uEndFadeBlockDistance, fadeEndDistance); + + } + + public void setProjectionMatrix(DhApiMat4f mcModelViewMatrix, DhApiMat4f mcProjectionMatrix) + { + Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(mcProjectionMatrix); + Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(mcModelViewMatrix); + + Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix); + inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix); + inverseDhModelViewProjectionMatrix.invert(); + this.inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix; + } + + + //========// + // render // + //========// + + @Override + protected void onRender() + { + int depthTextureId = GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId(); + int colorTextureId = GlDhMetaRenderer.INSTANCE.getActiveColorTextureId(); + + if (depthTextureId == -1 + || colorTextureId == -1) + { + // the renderer is currently being re-built and/or inactive, + // we don't need to/can't render fading + return; + } + + + + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer); + GLMC.disableScissorTest(); + GLMC.disableDepthTest(); + GLMC.disableBlend(); + + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(depthTextureId); + GL32.glUniform1i(this.uDhDepthTexture, 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + GLMC.glBindTexture(MC_RENDER.getColorTextureId()); + GL32.glUniform1i(this.uMcColorTexture, 1); + + GLMC.glActiveTexture(GL32.GL_TEXTURE2); + GLMC.glBindTexture(colorTextureId); + GL32.glUniform1i(this.uDhColorTexture, 2); + + + GlScreenQuad.INSTANCE.render(); + } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhVanillaFadeShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhVanillaFadeShader.java new file mode 100644 index 000000000..15a1ba43c --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhVanillaFadeShader.java @@ -0,0 +1,211 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade; + +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import org.lwjgl.opengl.GL32; + +public class GlDhVanillaFadeShader extends GlAbstractShaderRenderer +{ + public static GlDhVanillaFadeShader INSTANCE = new GlDhVanillaFadeShader(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + public int frameBuffer = -1; + + private Mat4f inverseMcMvmProjMatrix; + private Mat4f inverseDhMvmProjMatrix; + private float levelMaxHeight; + + + // Uniforms + public int uMcDepthTexture = -1; + public int uDhDepthTexture = -1; + public int uCombinedMcDhColorTexture = -1; + public int uDhColorTexture = -1; + + /** Inverted Model View Projection matrix */ + public int uDhInvMvmProj = -1; + public int uMcInvMvmProj = -1; + + public int uStartFadeBlockDistance = -1; + public int uEndFadeBlockDistance = -1; + public int uMaxLevelHeight = -1; + + public int uOnlyRenderLods = -1; + + + + //=============// + // constructor // + //=============// + //region + + public GlDhVanillaFadeShader() { } + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/fade/gl/vanillaFade.frag", + "vPosition" + ); + + // all uniforms should be tryGet... + // because disabling fade can cause the GLSL to optimize out most (if not all) uniforms + + // near fade + this.uDhInvMvmProj = this.shader.tryGetUniformLocation("uDhInvMvmProj"); + this.uMcInvMvmProj = this.shader.tryGetUniformLocation("uMcInvMvmProj"); + + this.uMcDepthTexture = this.shader.tryGetUniformLocation("uMcDepthTexture"); + this.uDhDepthTexture = this.shader.tryGetUniformLocation("uDhDepthTexture"); + this.uCombinedMcDhColorTexture = this.shader.tryGetUniformLocation("uCombinedMcDhColorTexture"); + this.uDhColorTexture = this.shader.tryGetUniformLocation("uDhColorTexture"); + + this.uStartFadeBlockDistance = this.shader.tryGetUniformLocation("uStartFadeBlockDistance"); + this.uEndFadeBlockDistance = this.shader.tryGetUniformLocation("uEndFadeBlockDistance"); + this.uMaxLevelHeight = this.shader.tryGetUniformLocation("uMaxLevelHeight"); + + this.uOnlyRenderLods = this.shader.tryGetUniformLocation("uOnlyRenderLods"); + + } + + //endregion + + + + //=============// + // render prep // + //=============// + //region + + @Override + protected void onApplyUniforms(RenderParams renderParams) + { + this.shader.setUniform(this.uMcInvMvmProj, this.inverseMcMvmProjMatrix); + this.shader.setUniform(this.uDhInvMvmProj, this.inverseDhMvmProjMatrix); + + + float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks(); + // this added value prevents the near clip plane and discard circle from touching, which looks bad + dhNearClipDistance += 16f; + + // measured in blocks + // these multipliers in James' tests should provide a fairly smooth transition + // without having underdraw issues + float fadeStartDistance = dhNearClipDistance * 1.5f; + float fadeEndDistance = dhNearClipDistance * 1.9f; + + this.shader.setUniform(this.uStartFadeBlockDistance, fadeStartDistance); + this.shader.setUniform(this.uEndFadeBlockDistance, fadeEndDistance); + + this.shader.setUniform(this.uMaxLevelHeight, this.levelMaxHeight); + + this.shader.setUniform(this.uOnlyRenderLods, Config.Client.Advanced.Debugging.lodOnlyMode.get()); + } + + public void setProjectionMatrix(DhApiMat4f mcModelViewMatrix, DhApiMat4f mcProjectionMatrix) + { + Mat4f inverseMcModelViewProjectionMatrix = new Mat4f(mcProjectionMatrix); + inverseMcModelViewProjectionMatrix.multiply(mcModelViewMatrix); + inverseMcModelViewProjectionMatrix.invert(); + this.inverseMcMvmProjMatrix = inverseMcModelViewProjectionMatrix; + + + Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(mcProjectionMatrix); + Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(mcModelViewMatrix); + + Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix); + inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix); + inverseDhModelViewProjectionMatrix.invert(); + this.inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix; + } + public void setLevelMaxHeight(int levelMaxHeight) { this.levelMaxHeight = levelMaxHeight; } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + protected void onRender() + { + int depthTextureId = GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId(); + int colorTextureId = GlDhMetaRenderer.INSTANCE.getActiveColorTextureId(); + + if (depthTextureId == -1 + || colorTextureId == -1) + { + // the renderer is currently being re-built and/or inactive, + // we don't need to/can't render fading + return; + } + + + + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer); + GLMC.disableScissorTest(); + GLMC.disableDepthTest(); + GLMC.disableBlend(); + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(MC_RENDER.getDepthTextureId()); + GL32.glUniform1i(this.uMcDepthTexture, 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + GLMC.glBindTexture(depthTextureId); + GL32.glUniform1i(this.uDhDepthTexture, 1); + + GLMC.glActiveTexture(GL32.GL_TEXTURE2); + GLMC.glBindTexture(MC_RENDER.getColorTextureId()); + GL32.glUniform1i(this.uCombinedMcDhColorTexture, 2); + + GLMC.glActiveTexture(GL32.GL_TEXTURE3); + GLMC.glBindTexture(colorTextureId); + GL32.glUniform1i(this.uDhColorTexture, 3); + + + GlScreenQuad.INSTANCE.render(); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlVanillaFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlVanillaFadeRenderer.java new file mode 100644 index 000000000..c8c9524c5 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlVanillaFadeRenderer.java @@ -0,0 +1,203 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fade; + +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.GLState; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer; +import com.seibel.distanthorizons.core.logging.DhLogger; +import org.lwjgl.opengl.GL32; + +import java.nio.ByteBuffer; + +/** + * Handles fading MC and DH together via {@link GlDhVanillaFadeShader} and {@link GlDhFarFadeApplyShader}.

+ * + * {@link GlDhVanillaFadeShader} - draws the Fade to a texture.
+ * {@link GlDhFarFadeApplyShader} - draws the Fade texture to MC's FrameBuffer.
+ */ +public class GlVanillaFadeRenderer implements IDhVanillaFadeRenderer +{ + public static GlVanillaFadeRenderer INSTANCE = new GlVanillaFadeRenderer(); + + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + private boolean init = false; + + private int width = -1; + private int height = -1; + private int fadeFramebuffer = -1; + + private int fadeTexture = -1; + + + + //=============// + // constructor // + //=============// + //region + + private GlVanillaFadeRenderer() { } + + public void init() + { + if (this.init) return; + this.init = true; + + GlDhVanillaFadeShader.INSTANCE.init(); + GlDhFarFadeApplyShader.INSTANCE.init(); + } + + private void createFramebuffer(int width, int height) + { + if (this.fadeFramebuffer != -1) + { + GL32.glDeleteFramebuffers(this.fadeFramebuffer); + this.fadeFramebuffer = -1; + } + + this.fadeFramebuffer = GL32.glGenFramebuffers(); + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fadeFramebuffer); + + + // Applying the fade texture is only needed if MC is drawing to their own frame buffer, + // otherwise we can directly render to their texture + if (MC_RENDER.mcRendersToFrameBuffer()) + { + if (this.fadeTexture != -1) + { + GLMC.glDeleteTextures(this.fadeTexture); + this.fadeTexture = -1; + } + + this.fadeTexture = GL32.glGenTextures(); + GLMC.glBindTexture(this.fadeTexture); + GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0); + } + else + { + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, MC_RENDER.getColorTextureId(), 0); + } + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + int depthTextureId = GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId(); + if (depthTextureId == -1) + { + // the renderer hasn't been set up yet + // trying to render fading may cause GL errors + return; + } + + + + IProfilerWrapper profiler = MC_CLIENT.getProfiler(); + profiler.pop(); // get out of "terrain" + profiler.push("DH-Vanilla Fade"); + + + try(GLState mcState = new GLState()) + { + profiler.push("Vanilla Fade Generate"); + + this.init(); + + // resize the framebuffer if necessary + int width = MC_RENDER.getTargetFramebufferViewportWidth(); + int height = MC_RENDER.getTargetFramebufferViewportHeight(); + if (this.width != width || this.height != height) + { + this.width = width; + this.height = height; + this.createFramebuffer(width, height); + } + + + GlDhVanillaFadeShader.INSTANCE.frameBuffer = this.fadeFramebuffer; + GlDhVanillaFadeShader.INSTANCE.setProjectionMatrix(renderParams.mcModelViewMatrix, renderParams.mcProjectionMatrix); + GlDhVanillaFadeShader.INSTANCE.setLevelMaxHeight(renderParams.clientLevelWrapper.getMaxHeight()); + GlDhVanillaFadeShader.INSTANCE.render(renderParams); + + // Applying the fade texture is only needed if MC is drawing to their own frame buffer, + // otherwise we can directly render to their texture + if (MC_RENDER.mcRendersToFrameBuffer()) + { + profiler.popPush("Vanilla Fade Apply"); + + GlDhFarFadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture; + GlDhFarFadeApplyShader.INSTANCE.readFramebuffer = GlDhFarFadeShader.INSTANCE.frameBuffer; + GlDhFarFadeApplyShader.INSTANCE.drawFramebuffer = MC_RENDER.getTargetFramebuffer(); + GlDhFarFadeApplyShader.INSTANCE.render(renderParams); + } + + profiler.pop(); + } + catch (Exception e) + { + LOGGER.error("Unexpected error during fade render, error: ["+e.getMessage()+"].", e); + } + } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + public void free() + { + GlDhVanillaFadeShader.INSTANCE.free(); + GlDhFarFadeApplyShader.INSTANCE.free(); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogApplyShader.java new file mode 100644 index 000000000..affbce795 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogApplyShader.java @@ -0,0 +1,118 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fog; + +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.render.RenderParams; +import org.lwjgl.opengl.GL32; + +/** + * Draws the Fog texture onto DH's FrameBuffer.

+ * + * See Also:
+ * {@link GlDhFogRenderer} - Parent to this shader.
+ * {@link GlDhFogShader} - draws the Fog texture.
+ */ +public class GlDhFogApplyShader extends GlAbstractShaderRenderer +{ + public static GlDhFogApplyShader INSTANCE = new GlDhFogApplyShader(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + public int fogTexture; + + // uniforms + public int colorTextureUniform; + public int depthTextureUniform; + + + + //=============// + // constructor // + //=============// + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/fog/gl/apply.frag", + "vPosition" + ); + + // uniform setup + this.colorTextureUniform = this.shader.getUniformLocation("uColorTexture"); + this.depthTextureUniform = this.shader.getUniformLocation("uDepthTexture"); + + } + + + + //=============// + // render prep // + //=============// + + @Override + protected void onApplyUniforms(RenderParams renderParams) + { + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(this.fogTexture); + GL32.glUniform1i(this.colorTextureUniform, 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId()); + GL32.glUniform1i(this.depthTextureUniform, 1); + + } + + + + //========// + // render // + //========// + + @Override + protected void onRender() + { + GLMC.enableBlend(); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + + // Depth testing must be disabled otherwise this application shader won't apply anything. + // setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually, + // it should be automatically restored after rendering is complete. + GLMC.disableDepthTest(); + + + // apply the rendered Fog to DH's framebuffer + GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, GlDhFogShader.INSTANCE.frameBuffer); + GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, GlDhMetaRenderer.INSTANCE.getActiveFramebufferId()); + + GlScreenQuad.INSTANCE.render(); + + GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, 0); + } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogRenderer.java new file mode 100644 index 000000000..dd54a20f0 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogRenderer.java @@ -0,0 +1,156 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fog; + +import com.seibel.distanthorizons.common.render.openGl.glObject.GLState; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFogRenderer; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL43C; + +import java.nio.ByteBuffer; + +/** + * Handles adding SSAO via {@link GlDhFogShader} and {@link GlDhFogApplyShader}.

+ * + * {@link GlDhFogShader} - draws the Fog to a texture.
+ * {@link GlDhFogApplyShader} - draws the Fog texture to DH's FrameBuffer.
+ */ +public class GlDhFogRenderer implements IDhFogRenderer +{ + public static GlDhFogRenderer INSTANCE = new GlDhFogRenderer(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + private boolean init = false; + + private int width = -1; + private int height = -1; + private int fogFramebuffer = -1; + + private int fogTexture = -1; + + + + //=============// + // constructor // + //=============// + + private GlDhFogRenderer() { } + + public void init() + { + if (this.init) return; + this.init = true; + + GlDhFogShader.INSTANCE.init(); + GlDhFogApplyShader.INSTANCE.init(); + } + + private void createFramebuffer(int width, int height) + { + if (this.fogFramebuffer != -1) + { + GL32.glDeleteFramebuffers(this.fogFramebuffer); + this.fogFramebuffer = -1; + } + + if (this.fogTexture != -1) + { + GLMC.glDeleteTextures(this.fogTexture); + this.fogTexture = -1; + } + + this.fogFramebuffer = GL32.glGenFramebuffers(); + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer); + + this.fogTexture = GLMC.glGenTextures(); + { + GLMC.glBindTexture(this.fogTexture); + GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0); + + // disable mip-mapping since DH is just going to draw straight to the screen + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); + } + } + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + // GLState needed in MC 1.16.5 probably due to MC not manually setting each GL state they need before the next rendering step + try (GLState state = new GLState()) + { + this.init(); + + // resize the framebuffer if necessary + int width = MC_RENDER.getTargetFramebufferViewportWidth(); + int height = MC_RENDER.getTargetFramebufferViewportHeight(); + if (this.width != width || this.height != height) + { + this.width = width; + this.height = height; + this.createFramebuffer(width, height); + } + + GlDhFogShader.INSTANCE.frameBuffer = this.fogFramebuffer; + GlDhFogShader.INSTANCE.setProjectionMatrix(renderParams.dhMvmProjMatrix); + GlDhFogShader.INSTANCE.render(renderParams); + + GlDhFogApplyShader.INSTANCE.fogTexture = this.fogTexture; + GlDhFogApplyShader.INSTANCE.render(renderParams); + } + } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + public void free() + { + GlDhFogShader.INSTANCE.free(); + GlDhFogApplyShader.INSTANCE.free(); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogShader.java new file mode 100644 index 000000000..05efafdfb --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fog/GlDhFogShader.java @@ -0,0 +1,301 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.fog; + +import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import org.lwjgl.opengl.GL32; + +import java.awt.*; + +public class GlDhFogShader extends GlAbstractShaderRenderer +{ + public static final GlDhFogShader INSTANCE = new GlDhFogShader(); + + private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + + + public int frameBuffer; + private Mat4f inverseMvmProjMatrix; + + + + //==========// + // Uniforms // + //==========// + //region + + public int uDepthMap; + /** Inverted Model View Projection matrix */ + public int uInvMvmProj; + + // fog uniforms + public int uFogColor; + public int uFogScale; + public int uFogVerticalScale; + public int uFogDebugMode; + public int uFogFalloffType; + + // far fog + public int uFarFogStart; + public int uFarFogLength; + public int uFarFogMin; + public int uFarFogRange; + public int uFarFogDensity; + + // height fog + public int uHeightFogStart; + public int uHeightFogLength; + public int uHeightFogMin; + public int uHeightFogRange; + public int uHeightFogDensity; + + public int uHeightFogEnabled; + public int uHeightFogFalloffType; + public int uHeightBasedOnCamera; + public int uHeightFogBaseHeight; + public int uHeightFogAppliesUp; + public int uHeightFogAppliesDown; + public int uUseSphericalFog; + public int uHeightFogMixingMode; + public int uCameraBlockYPos; + + //endregion + + + + //=============// + // constructor // + //=============// + //region + + public GlDhFogShader() { } + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/fog/gl/fog.frag", + "vPosition" + ); + + // all uniforms should be tryGet... + // because disabling fog can cause the GLSL to optimize out most (if not all) uniforms + + this.uDepthMap = this.shader.getUniformLocation("uDepthMap"); + this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj"); + + // Fog uniforms + this.uFogScale = this.shader.getUniformLocation("uFogScale"); + this.uFogVerticalScale = this.shader.getUniformLocation("uFogVerticalScale"); + this.uFogColor = this.shader.getUniformLocation("uFogColor"); + this.uFogDebugMode = this.shader.getUniformLocation("uFogDebugMode"); + this.uFogFalloffType = this.shader.getUniformLocation("uFogFalloffType"); + + // fog config + this.uFarFogStart = this.shader.getUniformLocation("uFarFogStart"); + this.uFarFogLength = this.shader.getUniformLocation("uFarFogLength"); + this.uFarFogMin = this.shader.getUniformLocation("uFarFogMin"); + this.uFarFogRange = this.shader.getUniformLocation("uFarFogRange"); + this.uFarFogDensity = this.shader.getUniformLocation("uFarFogDensity"); + + // height fog + this.uHeightFogStart = this.shader.getUniformLocation("uHeightFogStart"); + this.uHeightFogLength = this.shader.getUniformLocation("uHeightFogLength"); + this.uHeightFogMin = this.shader.getUniformLocation("uHeightFogMin"); + this.uHeightFogRange = this.shader.getUniformLocation("uHeightFogRange"); + this.uHeightFogDensity = this.shader.getUniformLocation("uHeightFogDensity"); + + this.uHeightFogEnabled = this.shader.getUniformLocation("uHeightFogEnabled"); + this.uHeightFogFalloffType = this.shader.getUniformLocation("uHeightFogFalloffType"); + this.uHeightBasedOnCamera = this.shader.getUniformLocation("uHeightBasedOnCamera"); + this.uHeightFogBaseHeight = this.shader.getUniformLocation("uHeightFogBaseHeight"); + this.uHeightFogAppliesUp = this.shader.getUniformLocation("uHeightFogAppliesUp"); + this.uHeightFogAppliesDown = this.shader.getUniformLocation("uHeightFogAppliesDown"); + this.uUseSphericalFog = this.shader.getUniformLocation("uUseSphericalFog"); + this.uHeightFogMixingMode = this.shader.getUniformLocation("uHeightFogMixingMode"); + this.uCameraBlockYPos = this.shader.getUniformLocation("uCameraBlockYPos"); + + } + + //endregion + + + + //=============// + // render prep // + //=============// + //region + + @Override + protected void onApplyUniforms(RenderParams renderParams) + { + int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; + + + + if (this.inverseMvmProjMatrix != null) + { + this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix); + } + + + // Fog uniforms + this.shader.setUniform(this.uFogColor, this.getFogColor(renderParams.partialTicks)); + this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); + this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); + // only used for debugging + this.shader.setUniform(this.uFogDebugMode, 0); // 1 = render everything with fog color // 7 = use debug rendering + this.shader.setUniform(this.uFogFalloffType, Config.Client.Advanced.Graphics.Fog.farFogFalloff.get().value); + + + // fog config + float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get(); + float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get(); + float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get(); + float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get(); + float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get(); + + // override fog if underwater + if (MC_RENDER.isFogStateSpecial()) + { + // hide everything behind fog + farFogStart = 0.0f; + farFogEnd = 0.0f; + } + + this.shader.setUniform(this.uFarFogStart, farFogStart); + this.shader.setUniform(this.uFarFogLength, farFogEnd - farFogStart); + this.shader.setUniform(this.uFarFogMin, farFogMin); + this.shader.setUniform(this.uFarFogRange, farFogMax - farFogMin); + this.shader.setUniform(this.uFarFogDensity, farFogDensity); + + + // height config + EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get(); + boolean heightFogEnabled = heightFogMixingMode != EDhApiHeightFogMixMode.SPHERICAL && heightFogMixingMode != EDhApiHeightFogMixMode.CYLINDRICAL; + boolean useSphericalFog = heightFogMixingMode == EDhApiHeightFogMixMode.SPHERICAL; + EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get(); + + float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get(); + float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get(); + float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get(); + float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get(); + float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get(); + + this.shader.setUniform(this.uHeightFogStart, heightFogStart); + this.shader.setUniform(this.uHeightFogLength, heightFogEnd - heightFogStart); + this.shader.setUniform(this.uHeightFogMin, heightFogMin); + this.shader.setUniform(this.uHeightFogRange, heightFogMax - heightFogMin); + this.shader.setUniform(this.uHeightFogDensity, heightFogDensity); + + + this.shader.setUniform(this.uHeightFogEnabled, heightFogEnabled); + this.shader.setUniform(this.uHeightFogFalloffType, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get().value); + this.shader.setUniform(this.uHeightFogBaseHeight, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get()); + this.shader.setUniform(this.uHeightBasedOnCamera, heightFogCameraDirection.basedOnCamera); + this.shader.setUniform(this.uHeightFogAppliesUp, heightFogCameraDirection.fogAppliesUp); + this.shader.setUniform(this.uHeightFogAppliesDown, heightFogCameraDirection.fogAppliesDown); + this.shader.setUniform(this.uUseSphericalFog, useSphericalFog); + this.shader.setUniform(this.uHeightFogMixingMode, heightFogMixingMode.value); + this.shader.setUniform(this.uCameraBlockYPos, (float)MC_RENDER.getCameraExactPosition().y); + + } + private Color getFogColor(float partialTicks) + { + Color fogColor; + + if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EDhApiFogColorMode.USE_SKY_COLOR) + { + fogColor = MC_RENDER.getSkyColor(); + } + else + { + fogColor = MC_RENDER.getFogColor(partialTicks); + } + + return fogColor; + } + + public void setProjectionMatrix(DhApiMat4f modelViewProjectionMatrix) + { + this.inverseMvmProjMatrix = new Mat4f(modelViewProjectionMatrix); + this.inverseMvmProjMatrix.invert(); + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + protected void onRender() + { + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer); + GLMC.disableScissorTest(); + GLMC.disableDepthTest(); + GLMC.disableBlend(); + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId()); + GL32.glUniform1i(this.uDepthMap, 0); + + // this is necessary for MC 1.16 (IE Legacy OpenGL) + // otherwise the framebuffer isn't cleared correctly and the fog smears across the screen + if (MC_RENDER.runningLegacyOpenGL()) + { + // in another part of the DH code we set the fog color to opaque, here it needs to be transparent + float[] clearColorValues = new float[4]; + GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues); + GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 0.0f); + + GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); + } + + + GlScreenQuad.INSTANCE.render(); + } + + //endregion + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAOApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAOApplyShader.java new file mode 100644 index 000000000..d12663de2 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAOApplyShader.java @@ -0,0 +1,146 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao; + +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.RenderUtil; +import org.lwjgl.opengl.GL32; + +/** + * Draws the SSAO texture onto DH's FrameBuffer.

+ * + * See Also:
+ * {@link GlDhSSAORenderer} - Parent to this shader.
+ * {@link GlDhSSAOShader} - draws the SSAO texture.
+ */ +public class GlDhSSAOApplyShader extends GlAbstractShaderRenderer +{ + public static GlDhSSAOApplyShader INSTANCE = new GlDhSSAOApplyShader(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + public int ssaoTexture; + + // uniforms + public int gSSAOMapUniform; + public int gDepthMapUniform; + public int gViewSizeUniform; + public int gBlurRadiusUniform; + public int gNearUniform; + public int gFarUniform; + + + + //=============// + // constructor // + //=============// + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/ssao/gl/apply.frag", + "vPosition" + ); + + // uniform setup + this.gSSAOMapUniform = this.shader.getUniformLocation("gSSAOMap"); + this.gDepthMapUniform = this.shader.getUniformLocation("gDepthMap"); + this.gViewSizeUniform = this.shader.tryGetUniformLocation("gViewSize"); + this.gBlurRadiusUniform = this.shader.tryGetUniformLocation("gBlurRadius"); + this.gNearUniform = this.shader.tryGetUniformLocation("gNear"); + this.gFarUniform = this.shader.tryGetUniformLocation("gFar"); + } + + + + //=============// + // render prep // + //=============// + + @Override + protected void onApplyUniforms(RenderParams renderParams) + { + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId()); + GL32.glUniform1i(this.gDepthMapUniform, 0); + + GLMC.glActiveTexture(GL32.GL_TEXTURE1); + GLMC.glBindTexture(this.ssaoTexture); + GL32.glUniform1i(this.gSSAOMapUniform, 1); + + GL32.glUniform1i(this.gBlurRadiusUniform, 2); + + if (this.gViewSizeUniform >= 0) + { + GL32.glUniform2f(this.gViewSizeUniform, + MC_RENDER.getTargetFramebufferViewportWidth(), + MC_RENDER.getTargetFramebufferViewportHeight()); + } + + if (this.gNearUniform >= 0) + { + GL32.glUniform1f(this.gNearUniform, + RenderUtil.getNearClipPlaneInBlocks()); + } + + if (this.gFarUniform >= 0) + { + float farClipPlane = RenderUtil.getFarClipPlaneDistanceInBlocks(); + GL32.glUniform1f(this.gFarUniform, farClipPlane); + } + } + + + + //========// + // render // + //========// + + @Override + protected void onRender() + { + GLMC.enableBlend(); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GLMC.glBlendFuncSeparate(GL32.GL_ZERO, GL32.GL_SRC_ALPHA, GL32.GL_ZERO, GL32.GL_ONE); + + // Depth testing must be disabled otherwise this application shader won't apply anything. + // setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually, + // it should be automatically restored after rendering is complete. + GLMC.disableDepthTest(); + + // apply the rendered SSAO to the LODs + GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, GlDhSSAOShader.INSTANCE.frameBuffer); + GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, GlDhMetaRenderer.INSTANCE.getActiveFramebufferId()); + + + GlScreenQuad.INSTANCE.render(); + } + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAORenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAORenderer.java new file mode 100644 index 000000000..a6730453e --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAORenderer.java @@ -0,0 +1,156 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao; + +import com.seibel.distanthorizons.common.render.openGl.glObject.GLState; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhSsaoRenderer; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL43C; + +import java.nio.ByteBuffer; + +/** + * Handles adding SSAO via {@link GlDhSSAOShader} and {@link GlDhSSAOApplyShader}.

+ * + * {@link GlDhSSAOShader} - draws the SSAO to a texture.
+ * {@link GlDhSSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer.
+ */ +public class GlDhSSAORenderer implements IDhSsaoRenderer +{ + public static GlDhSSAORenderer INSTANCE = new GlDhSSAORenderer(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + private boolean init = false; + + private int width = -1; + private int height = -1; + private int ssaoFramebuffer = -1; + + private int ssaoTexture = -1; + + + + //=============// + // constructor // + //=============// + + private GlDhSSAORenderer() { } + + public void init() + { + if (this.init) return; + this.init = true; + + GlDhSSAOShader.INSTANCE.init(); + GlDhSSAOApplyShader.INSTANCE.init(); + } + + private void createFramebuffer(int width, int height) + { + if (this.ssaoFramebuffer != -1) + { + GL32.glDeleteFramebuffers(this.ssaoFramebuffer); + this.ssaoFramebuffer = -1; + } + + if (this.ssaoTexture != -1) + { + GLMC.glDeleteTextures(this.ssaoTexture); + this.ssaoTexture = -1; + } + + this.ssaoFramebuffer = GL32.glGenFramebuffers(); + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer); + + this.ssaoTexture = GLMC.glGenTextures(); + { + GLMC.glBindTexture(this.ssaoTexture); + GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_R16F, width, height, 0, GL32.GL_RED, GL32.GL_HALF_FLOAT, (ByteBuffer) null); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); + + // disable mip-mapping since DH is just going to draw straight to the screen + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); + } + + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.ssaoTexture, 0); + } + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + try(GLState state = new GLState()) + { + this.init(); + + // resize the framebuffer if necessary + int width = MC_RENDER.getTargetFramebufferViewportWidth(); + int height = MC_RENDER.getTargetFramebufferViewportHeight(); + if (this.width != width || this.height != height) + { + this.width = width; + this.height = height; + this.createFramebuffer(width, height); + } + + GlDhSSAOShader.INSTANCE.frameBuffer = this.ssaoFramebuffer; + GlDhSSAOShader.INSTANCE.setProjectionMatrix(renderParams.dhProjectionMatrix); + GlDhSSAOShader.INSTANCE.render(renderParams); + + GlDhSSAOApplyShader.INSTANCE.ssaoTexture = this.ssaoTexture; + GlDhSSAOApplyShader.INSTANCE.render(renderParams); + } + } + + //endregion + + + + //================// + // base overrides // + //================// + //region + + public void free() + { + GlDhSSAOShader.INSTANCE.free(); + GlDhSSAOApplyShader.INSTANCE.free(); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAOShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAOShader.java new file mode 100644 index 000000000..77cd454af --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/ssao/GlDhSSAOShader.java @@ -0,0 +1,144 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao; + +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.GlScreenQuad; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.common.render.openGl.util.GlAbstractShaderRenderer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import org.lwjgl.opengl.GL32; + +/** + * Draws the SSAO to a texture.

+ * + * See Also:
+ * {@link GlDhSSAORenderer} - Parent to this shader.
+ * {@link GlDhSSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer.
+ */ +public class GlDhSSAOShader extends GlAbstractShaderRenderer +{ + public static GlDhSSAOShader INSTANCE = new GlDhSSAOShader(); + + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + + public int frameBuffer; + + private Mat4f projection; + private Mat4f invertedProjection; + + + // uniforms + public int uProj; + public int uInvProj; + public int uSampleCount; + public int uRadius; + public int uStrength; + public int uMinLight; + public int uBias; + public int uDepthMap; + public int uFadeDistanceInBlocks; + + + + //=============// + // constructor // + //=============// + + @Override + public void onInit() + { + this.shader = new GlShaderProgram( + "assets/distanthorizons/shaders/shared/gl/quadApply.vert", + "assets/distanthorizons/shaders/ssao/gl/ao.frag", + "vPosition" + ); + + // uniform setup + this.uProj = this.shader.getUniformLocation("uProj"); + this.uInvProj = this.shader.getUniformLocation("uInvProj"); + this.uSampleCount = this.shader.getUniformLocation("uSampleCount"); + this.uRadius = this.shader.getUniformLocation("uRadius"); + this.uStrength = this.shader.getUniformLocation("uStrength"); + this.uMinLight = this.shader.getUniformLocation("uMinLight"); + this.uBias = this.shader.getUniformLocation("uBias"); + this.uDepthMap = this.shader.getUniformLocation("uDepthMap"); + this.uFadeDistanceInBlocks = this.shader.getUniformLocation("uFadeDistanceInBlocks"); + } + + + + //=============// + // render prep // + //=============// + + public void setProjectionMatrix(DhApiMat4f projectionMatrix) + { + this.projection = new Mat4f(projectionMatrix); + + this.invertedProjection = new Mat4f(projectionMatrix); + this.invertedProjection.invert(); + } + + @Override + protected void onApplyUniforms(RenderParams renderParams) + { + this.shader.setUniform(this.uProj, this.projection); + + this.shader.setUniform(this.uInvProj, this.invertedProjection); + + this.shader.setUniform(this.uSampleCount, 6); + this.shader.setUniform(this.uRadius, 4.0f); + this.shader.setUniform(this.uStrength, 0.2f); + this.shader.setUniform(this.uMinLight, 0.25f); + this.shader.setUniform(this.uBias, 0.02f); + this.shader.setUniform(this.uFadeDistanceInBlocks, 1_600.0f); + + GL32.glUniform1i(this.uDepthMap, 0); + + } + + + + //========// + // render // + //========// + + @Override + protected void onRender() + { + GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer); + GLMC.disableScissorTest(); + GLMC.disableDepthTest(); + GLMC.disableBlend(); + + GLMC.glActiveTexture(GL32.GL_TEXTURE0); + GLMC.glBindTexture(GlDhMetaRenderer.INSTANCE.getActiveDepthTextureId()); + + GlScreenQuad.INSTANCE.render(); + } + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/test/GlTestTriangleRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/test/GlTestTriangleRenderer.java new file mode 100644 index 000000000..c2cb3fbc0 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/test/GlTestTriangleRenderer.java @@ -0,0 +1,146 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.test; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute; +import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexPointer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; + +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer; +import org.lwjgl.opengl.GL32; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Renders a UV colored quad + * to the center of the screen to confirm DH's + * apply shader is running correctly + */ +public class GlTestTriangleRenderer implements IDhTestTriangleRenderer +{ + public static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + + public static final GlTestTriangleRenderer INSTANCE = new GlTestTriangleRenderer(); + + // Render a square with uv color + private static final float[] VERTICES = + { + // PosX,Y, ColorR,G,B,A + -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, + }; + + + + GlShaderProgram basicShader; + GLVertexBuffer vbo; + GlAbstractVertexAttribute va; + boolean init = false; + + + + //=============// + // constructor // + //=============// + //region + + private GlTestTriangleRenderer() { } + + public void init() + { + if (this.init) + { + return; + } + + LOGGER.info("init"); + this.init = true; + this.va = GlAbstractVertexAttribute.create(); + this.va.bind(); + // Pos + this.va.setVertexAttribute(0, 0, GlVertexPointer.addVec2Pointer(false)); + // Color + this.va.setVertexAttribute(0, 1, GlVertexPointer.addVec4Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 6); + this.basicShader = new GlShaderProgram( + "assets/distanthorizons/shaders/test/gl/vert.vert", + "assets/distanthorizons/shaders/test/gl/frag.frag", + new String[]{"vPosition", "color"}); + + this.createBuffer(); + } + + private void createBuffer() + { + ByteBuffer buffer = ByteBuffer.allocateDirect(VERTICES.length * Float.BYTES); + // Fill buffer with vertices. + buffer.order(ByteOrder.nativeOrder()); + buffer.asFloatBuffer().put(VERTICES); + buffer.rewind(); + + this.vbo = new GLVertexBuffer(false); + this.vbo.bind(); + this.vbo.uploadBuffer(buffer, 3, EDhApiGpuUploadMethod.DATA, VERTICES.length * Float.BYTES); + } + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderParams) + { + this.init(); + + this.basicShader.bind(); + this.va.bind(); + + this.vbo.bind(); + this.va.bindBufferToAllBindingPoints(this.vbo.getId()); + + GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 3); + + GlDhApplyShader.INSTANCE.render(renderParams); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/GlAbstractShaderRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/GlAbstractShaderRenderer.java new file mode 100644 index 000000000..ac8f8c900 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/GlAbstractShaderRenderer.java @@ -0,0 +1,112 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.util; + +import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import org.lwjgl.opengl.GL32; + +public abstract class GlAbstractShaderRenderer +{ + protected static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + + protected GlShaderProgram shader; + protected boolean init = false; + + + //=======// + // setup // + //=======// + //region + + protected GlAbstractShaderRenderer() {} + + public void init() + { + if (this.init) return; + this.init = true; + + this.onInit(); + } + + //endregion + + + //==================// + // abstract methods // + //==================// + //region + + protected void onInit() {} + + protected void onApplyUniforms(RenderParams renderParams) {} + + protected void onRender() {} + + //endregion + + + + //===========// + // rendering // + //===========// + //region + + public void render(RenderParams renderParams) + { + this.init(); + + this.shader.bind(); + + this.onApplyUniforms(renderParams); + + int width = MC_RENDER.getTargetFramebufferViewportWidth(); + int height = MC_RENDER.getTargetFramebufferViewportHeight(); + GL32.glViewport(0, 0, width, height); + + this.onRender(); + + this.shader.unbind(); + } + + //endregion + + + //================// + // base overrides // + //================// + //region + + public void free() + { + if (this.shader != null) + { + this.shader.free(); + } + } + + // endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlLodVertexFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlLodVertexFormat.java new file mode 100644 index 000000000..049b9d5d4 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlLodVertexFormat.java @@ -0,0 +1,110 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.util.vertexFormat; + +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; + +/** + * This is used to represent a single vertex + * stored in GPU memory, + *

+ * A (almost) exact copy of Minecraft's + * VertexFormat class, several methods + * were commented out since we didn't need them. + * + * @author James Seibel + * @version 12-9-2021 + */ +public class GlLodVertexFormat +{ + /** the format of data stored in the GPU buffers */ + public static final GlLodVertexFormat DH_VERTEX_FORMAT = GlVertexFormats.POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT_MATERIAL_ID_NORMAL_INDEX; + + + private final ImmutableList elements; + private final IntList offsets = new IntArrayList(); + private final int byteSize; + + public GlLodVertexFormat(ImmutableList elementList) + { + this.elements = elementList; + int i = 0; + + for (GlLodVertexFormatElement LodVertexFormatElement : elementList) + { + this.offsets.add(i); + i += LodVertexFormatElement.getByteSize(); + } + + this.byteSize = i; + } + + public int getByteSize() + { + return this.byteSize; + } + + public ImmutableList getElements() + { + return this.elements; + } + + + // Forge added method + public int getOffset(int index) + { + return offsets.getInt(index); + } + + + + @Override + public String toString() { return "format: " + this.elements.size() + " elements: " + this.elements.stream().map(Object::toString).collect(Collectors.joining(" ")); } + + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + GlLodVertexFormat vertexFormat = (GlLodVertexFormat) obj; + return this.byteSize == vertexFormat.byteSize && this.elements.equals(vertexFormat.elements); + } + else + { + return false; + } + } + + @Override + public int hashCode() { return this.elements.hashCode(); } + + + +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlLodVertexFormatElement.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlLodVertexFormatElement.java new file mode 100644 index 000000000..cfc692d53 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlLodVertexFormatElement.java @@ -0,0 +1,168 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.util.vertexFormat; + +import org.lwjgl.opengl.GL32; + +/** + * This object is used to build LodVertexFormats. + *

+ * A (almost) exact copy of Minecraft's + * VertexFormatElement class.
+ * A number of things were removed from the original + * object since we didn't need them, specifically "usage". + * + * @author James Seibel + * @version 11-13-2021 + */ +public class GlLodVertexFormatElement +{ + private final GlLodVertexFormatElement.DataType dataType; + /** James isn't sure what index is for */ + private final int index; + private final int count; + private final int byteSize; + private final boolean isPadding; + + public GlLodVertexFormatElement(int newIndex, GlLodVertexFormatElement.DataType newType, int newCount, boolean isPadding) + { + this.dataType = newType; + this.index = newIndex; + this.count = newCount; + this.byteSize = newType.getSize() * this.count; + this.isPadding = isPadding; + } + + public final boolean getIsPadding() + { + return isPadding; + } + + public final GlLodVertexFormatElement.DataType getType() + { + return this.dataType; + } + + public final int getIndex() + { + return this.index; + } + + public final int getByteSize() + { + return this.byteSize; + } + + // added by Forge + public int getElementCount() + { + return count; + } + + + + public enum DataType + { + FLOAT(4, "Float", GL32.GL_FLOAT), + UBYTE(1, "Unsigned Byte", GL32.GL_UNSIGNED_BYTE), + BYTE(1, "Byte", GL32.GL_BYTE), + USHORT(2, "Unsigned Short", GL32.GL_UNSIGNED_SHORT), + SHORT(2, "Short", GL32.GL_SHORT), + UINT(4, "Unsigned Int", GL32.GL_UNSIGNED_INT), + INT(4, "Int", GL32.GL_INT); + + private final int size; + private final String name; + private final int glType; + + DataType(int sizeInBytes, String newName, int openGlDataType) + { + this.size = sizeInBytes; + this.name = newName; + this.glType = openGlDataType; + } + + public int getSize() + { + return this.size; + } + + public String getName() + { + return this.name; + } + + public int getGlType() + { + return this.glType; + } + } + + + + + @Override + public int hashCode() + { + int i = this.dataType.hashCode(); + i = 31 * i + this.index; + return 31 * i + this.count; + } + + @Override + public String toString() + { + return this.count + "," + this.dataType.getName(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + GlLodVertexFormatElement LodVertexFormatElement = (GlLodVertexFormatElement) obj; + if (this.count != LodVertexFormatElement.count) + { + return false; + } + else if (this.index != LodVertexFormatElement.index) + { + return false; + } + else if (this.dataType != LodVertexFormatElement.dataType) + { + return false; + } + else + { + return false; + } + } + else + { + return false; + } + } + +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlVertexFormats.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlVertexFormats.java new file mode 100644 index 000000000..24bb3dbab --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/util/vertexFormat/GlVertexFormats.java @@ -0,0 +1,50 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.common.render.openGl.util.vertexFormat; + +import com.google.common.collect.ImmutableList; + +/** + * A (almost) exact copy of MC's + * DefaultVertexFormats class. + */ +public class GlVertexFormats +{ + public static final GlLodVertexFormatElement ELEMENT_POSITION = new GlLodVertexFormatElement(3, GlLodVertexFormatElement.DataType.USHORT, 3, false); + public static final GlLodVertexFormatElement ELEMENT_COLOR = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.UBYTE, 4, false); + public static final GlLodVertexFormatElement ELEMENT_BYTE_PADDING = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.BYTE, 1, true); + + public static final GlLodVertexFormatElement ELEMENT_LIGHT = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.UBYTE, 1, false); + public static final GlLodVertexFormatElement ELEMENT_IRIS_MATERIAL_INDEX = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.BYTE, 1, false); + public static final GlLodVertexFormatElement ELEMENT_IRIS_NORMAL_INDEX = new GlLodVertexFormatElement(0, GlLodVertexFormatElement.DataType.BYTE, 1, false); + + + public static final GlLodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT_MATERIAL_ID_NORMAL_INDEX = new GlLodVertexFormat(ImmutableList.builder() + .add(ELEMENT_POSITION) + .add(ELEMENT_BYTE_PADDING) + .add(ELEMENT_LIGHT) + .add(ELEMENT_COLOR) + .add(ELEMENT_IRIS_MATERIAL_INDEX) + .add(ELEMENT_IRIS_NORMAL_INDEX) + .add(ELEMENT_BYTE_PADDING) + .add(ELEMENT_BYTE_PADDING) // padding is to make sure the format is a multiple of 4 + .build()); + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/util/ILightTextureMarker.java b/common/src/main/java/com/seibel/distanthorizons/common/util/ILightTextureMarker.java deleted file mode 100644 index 3e92b5ffb..000000000 --- a/common/src/main/java/com/seibel/distanthorizons/common/util/ILightTextureMarker.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.common.util; - -/** - * Added to MC's dynamic textures via mixins - * in order to denote whether a texture is a lightmap or not.

- * - * If not done any dynamic texture could be used as the lightmap - * which causes some weird rendering bugs. - */ -public interface ILightTextureMarker -{ - void markLightTexture(); -} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java index b8fb6a99f..caa9b8a64 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java @@ -19,12 +19,19 @@ package com.seibel.distanthorizons.common.wrappers; +import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory; +import com.seibel.distanthorizons.common.render.blaze.BlazeDhRenderApiDefinition; +import com.seibel.distanthorizons.common.render.openGl.GlDhRenderApiDefinition; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory; import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI; import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper; import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager; -import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui; import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper; @@ -33,9 +40,9 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; /** * Binds all necessary dependencies, so we @@ -49,6 +56,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSha */ public class DependencySetup { + protected static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + public static void createSharedBindings() { @@ -56,6 +66,7 @@ public class DependencySetup SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE); SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE); + SingletonInjector.INSTANCE.bind(IDhApiCustomRenderObjectFactory.class, GenericRenderObjectFactory.INSTANCE); } public static void createServerBindings() @@ -66,8 +77,43 @@ public class DependencySetup SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); - SingletonInjector.INSTANCE.bind(IMinecraftGLWrapper.class, MinecraftGLWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE); } + public static void setRenderingApiBindings() + { + EDhApiRenderApi renderingApiEnum = Config.Client.Advanced.Graphics.Experimental.renderingApi.get(); + if (renderingApiEnum == EDhApiRenderApi.AUTO) + { + #if MC_VER < MC_1_21_11 + renderingApiEnum = EDhApiRenderApi.OPEN_GL; + #else + renderingApiEnum = EDhApiRenderApi.BLAZE_3D; + #endif + } + + LOGGER.info("Setting DH Rendering API to: ["+renderingApiEnum+"]."); + + AbstractDhRenderApiDefinition renderDefinition; + if (renderingApiEnum == EDhApiRenderApi.OPEN_GL) + { + renderDefinition = new GlDhRenderApiDefinition(); + } + else if (renderingApiEnum == EDhApiRenderApi.BLAZE_3D) + { + #if MC_VER <= MC_1_21_10 + throw new IllegalStateException("["+renderingApiEnum+"] is not supported on this version of Minecraft."); + #else + renderDefinition = new BlazeDhRenderApiDefinition(); + #endif + } + else + { + throw new IllegalStateException("No ["+ AbstractDhRenderApiDefinition.class.getSimpleName()+"] concrete implementation found for the value: ["+renderingApiEnum+"]."); + } + renderDefinition.bindRenderers(); + } + + + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java index 2c265107f..930f4e1f7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java @@ -19,22 +19,30 @@ package com.seibel.distanthorizons.common.wrappers; +import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; -/** - * @author James Seibel - * @version 12-11-2021 - */ public class VersionConstants implements IVersionConstants { public static final VersionConstants INSTANCE = new VersionConstants(); - private VersionConstants() - { - - } + //=============// + // constructor // + //=============// + //region + + private VersionConstants() { } + + //endregion + + + + //=========// + // methods // + //=========// + //region @Override public String getMinecraftVersion() @@ -94,4 +102,18 @@ public class VersionConstants implements IVersionConstants } + @Override + public EDhApiRenderApi getDefaultRenderingApi() + { + #if MC_VER <= MC_1_21_11 + return EDhApiRenderApi.OPEN_GL; + #else + ERROR MC version constant missing + #endif + } + + //endregion + + + } \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java index d91359bdb..73fafbbe3 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java @@ -29,13 +29,19 @@ import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; -//import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhServerLevel; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper; @@ -73,9 +79,31 @@ public class WrapperFactory implements IWrapperFactory + //=====================// + // internal properties // + //=====================// + //region + + private AbstractDhRenderApiDefinition renderDefinition; + private AbstractDhRenderApiDefinition getRenderDefinition() + { + // delayed get to make sure we don't accidentally set the variable before it's bound + if (this.renderDefinition != null) + { + return this.renderDefinition; + } + + this.renderDefinition = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class); + return this.renderDefinition; + } + //endregion + + + //==============// // core methods // //==============// + //region @Override public IBatchGeneratorEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel) @@ -99,7 +127,7 @@ public class WrapperFactory implements IWrapperFactory throw new ClassCastException("levelWrapper must be returned by DH and of type ["+ILevelWrapper.class.getName()+"]."); } - return BiomeWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper); + return BiomeWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper); } @Override public IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException @@ -109,19 +137,19 @@ public class WrapperFactory implements IWrapperFactory throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"]."); } - return BlockStateWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper); + return BlockStateWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper); } @Override public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); } - @Override + @Override public IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper) { try { return BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, levelWrapper); } - catch (IOException e) + catch (IOException e) { throw new LodUtil.AssertFailureException("Unable to parse plains resource string ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], error:\n " + e.getMessage()); } @@ -190,8 +218,8 @@ public class WrapperFactory implements IWrapperFactory // level wrapper ILevelWrapper levelWrapper = #if MC_VER <= MC_1_12_2 !level.isRemote #else level.isClientSide() #endif - ? ClientLevelWrapper.getWrapper((#if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif)level) - : ServerLevelWrapper.getWrapper((#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif)level); + ? ClientLevelWrapper.getWrapper((#if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif)level) + : ServerLevelWrapper.getWrapper((#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif)level); return new ChunkWrapper(chunk, levelWrapper); @@ -212,25 +240,34 @@ public class WrapperFactory implements IWrapperFactory String[] expectedClassNames; //#if MC_VER <= MC_1_XX_X - expectedClassNames = new String[] - { - #if MC_VER <= MC_1_12_2 Chunk #else ChunkAccess #endif.class.getName(), - "[ServerLevel] or [ClientLevel]" // Classes are not referenced by names to avoid exception when one of them is missing - }; + expectedClassNames = new String[] + { + #if MC_VER <= MC_1_12_2 Chunk #else ChunkAccess #endif.class.getName(), + "[ServerLevel] or [ClientLevel]" // Classes are not referenced by names to avoid exception when one of them is missing + }; //#endif return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray); } + @Override public IVertexBufferWrapper createVboWrapper(String name) { return this.getRenderDefinition().createVboWrapper(name); } + @Override public ILodContainerUniformBufferWrapper createLodContainerUniformWrapper() { return this.getRenderDefinition().createLodContainerUniformWrapper(); } + @Override public IDhGenericObjectVertexBufferContainer createGenericObjectVboContainer() { return this.getRenderDefinition().createGenericVboContainer(); } + @Override public IDhGenericRenderer createGenericRenderer() { return this.getRenderDefinition().createGenericRenderer(); } + + //endregion + + //=============// // api methods // //=============// + //region // documentation should be in the API interface - public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper) + public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper) { // confirm the API level wrapper is also a Core wrapper if (!(levelWrapper instanceof ILevelWrapper)) @@ -325,19 +362,22 @@ public class WrapperFactory implements IWrapperFactory return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray); } + //endregion + //================// // helper methods // //================// + //region private static String createWrapperErrorMessage(String wrapperName, String[] expectedClassNames, Object[] objectArray) { // error header StringBuilder message = new StringBuilder( - wrapperName + " creation failed. \n" + - "Expected object array parameters: \n"); + wrapperName + " creation failed. \n" + + "Expected object array parameters: \n"); // expected parameters @@ -366,4 +406,8 @@ public class WrapperFactory implements IWrapperFactory return message.toString(); } + //endregion + + + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java index 3161ae83e..2b1a6b597 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java @@ -98,25 +98,15 @@ public class BlockStateWrapper implements IBlockStateWrapper // must be defined before AIR, otherwise a null pointer will be thrown private static final DhLogger LOGGER = new DhLoggerBuilder().build(); - public static final ConcurrentHashMap<#if MC_VER >= MC_1_12_2 IBlockState #else BlockState #endif, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); - public static final ConcurrentHashMap WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap<#if MC_VER >= MC_1_12_2 IBlockState #else BlockState #endif, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>(); public static final String AIR_STRING = "AIR"; + public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null); public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt"; public static final String WATER_RESOURCE_LOCATION_STRING = "minecraft:water"; - /** Used to handle older MC versions that don't have an simple way of getting the block's tags */ - public static final List OLD_BEACON_BASE_BLOCK_NAME_LIST = Arrays.asList( - "iron_block", - "gold_block", - "diamond_block", - "emerald_block", - "netherite_block" - ); - - public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null); - public static ObjectOpenHashSet rendererIgnoredBlocks = null; public static ObjectOpenHashSet rendererIgnoredCaveBlocks = null; @@ -141,11 +131,11 @@ public class BlockStateWrapper implements IBlockStateWrapper /** used by the Iris shader mod to determine how each LOD should be rendered */ private byte blockMaterialId = 0; - private final boolean isBeaconBlock; + private final boolean isBeaconBlock; private final boolean isBeaconBaseBlock; private final boolean allowsBeaconBeamPassage; /** null if this block can't tint beacons */ - private final Color beaconTintColor; + private final Color beaconTintColor; private final Color mapColor; @@ -176,8 +166,8 @@ public class BlockStateWrapper implements IBlockStateWrapper } #if MC_VER <= MC_1_12_2 - /** - * Can be faster than {@link BlockStateWrapper#fromBlockState(IBlockState, ILevelWrapper)} + /** + * Can be faster than {@link BlockStateWrapper#fromBlockState(IBlockState, ILevelWrapper)} * in cases where the same block state is expected to be referenced multiple times. */ #else @@ -216,11 +206,21 @@ public class BlockStateWrapper implements IBlockStateWrapper // beacon base blocks #if MC_VER <= MC_1_18_2 + + // Used to handle older MC versions that don't have an simple way of getting the block's tags + List oldBeaconBaseBlockNameList = Arrays.asList( + "iron_block", + "gold_block", + "diamond_block", + "emerald_block", + "netherite_block" + ); + // Older MC versions are harder to get block tags, so just use a static list to determine beacon blocks boolean isBeaconBaseBlock = false; - for (int i = 0; i < OLD_BEACON_BASE_BLOCK_NAME_LIST.size(); i++) + for (int i = 0; i < oldBeaconBaseBlockNameList.size(); i++) { - String baseBlockName = OLD_BEACON_BASE_BLOCK_NAME_LIST.get(i); + String baseBlockName = oldBeaconBaseBlockNameList.get(i); if (lowercaseSerial.contains(baseBlockName)) { isBeaconBaseBlock = true; @@ -338,7 +338,7 @@ public class BlockStateWrapper implements IBlockStateWrapper //====================// //region - /** + /** * Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one. * This way the method won't accidentally be called before the deserialization can be completed. */ @@ -391,7 +391,7 @@ public class BlockStateWrapper implements IBlockStateWrapper ObjectOpenHashSet blockStringList = new ObjectOpenHashSet<>(); if (baseResourceLocations != null) { - blockStringList.addAll(baseResourceLocations); + blockStringList.addAll(baseResourceLocations); } // get the config blocks @@ -570,7 +570,7 @@ public class BlockStateWrapper implements IBlockStateWrapper } @Override - public int hashCode() { return this.hashCode; } + public int hashCode() { return this.hashCode; } @Override @@ -584,7 +584,7 @@ public class BlockStateWrapper implements IBlockStateWrapper @Override public boolean isSolid() { - if (this.isAir() + if (this.isAir() || this.blockState == null) // == null isn't necessary since its handled in isAir() but is here to prevent intellij from complaining { return false; @@ -695,7 +695,7 @@ public class BlockStateWrapper implements IBlockStateWrapper } this.serialString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath() - + STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState); + + STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState); return this.serialString; } @@ -707,7 +707,7 @@ public class BlockStateWrapper implements IBlockStateWrapper // we need the final string for the concurrent hash map later final String finalResourceStateString = resourceStateString; - if (finalResourceStateString.equals(AIR_STRING) + if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case { return AIR; @@ -906,7 +906,7 @@ public class BlockStateWrapper implements IBlockStateWrapper //==============// //region - private EDhApiBlockMaterial calculateEDhApiBlockMaterialId() + private EDhApiBlockMaterial calculateEDhApiBlockMaterialId() { if (this.blockState == null) { @@ -916,11 +916,11 @@ public class BlockStateWrapper implements IBlockStateWrapper String serialString = this.getSerialString().toLowerCase(); if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock() instanceof BlockLeaves #else this.blockState.is(BlockTags.LEAVES) #endif - || serialString.contains("bamboo") + || serialString.contains("bamboo") || serialString.contains("cactus") || serialString.contains("chorus_flower") || serialString.contains("mushroom") - ) + ) { return EDhApiBlockMaterial.LEAVES; } @@ -933,11 +933,11 @@ public class BlockStateWrapper implements IBlockStateWrapper return EDhApiBlockMaterial.WATER; } else if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock().getSoundType() #else this.blockState.getSoundType() #endif == SoundType.WOOD - || serialString.contains("root") + || serialString.contains("root") #if MC_VER >= MC_1_19_4 || this.blockState.getSoundType() == SoundType.CHERRY_WOOD #endif - ) + ) { return EDhApiBlockMaterial.WOOD; } @@ -949,24 +949,24 @@ public class BlockStateWrapper implements IBlockStateWrapper || this.blockState.getSoundType() == SoundType.COPPER_BULB || this.blockState.getSoundType() == SoundType.COPPER_GRATE #endif - ) + ) { return EDhApiBlockMaterial.METAL; } else if ( serialString.contains("grass_block") - || serialString.contains("grass_slab") - ) + || serialString.contains("grass_slab") + ) { return EDhApiBlockMaterial.GRASS; } else if ( serialString.contains("dirt") - || serialString.contains("gravel") - || serialString.contains("mud") - || serialString.contains("podzol") - || serialString.contains("mycelium") - ) + || serialString.contains("gravel") + || serialString.contains("mud") + || serialString.contains("podzol") + || serialString.contains("mycelium") + ) { return EDhApiBlockMaterial.DIRT; } @@ -983,7 +983,7 @@ public class BlockStateWrapper implements IBlockStateWrapper else if (this.serialString.contains("snow")) { return EDhApiBlockMaterial.SNOW; - } + } else if (serialString.contains("sand")) { return EDhApiBlockMaterial.SAND; @@ -991,17 +991,17 @@ public class BlockStateWrapper implements IBlockStateWrapper else if (serialString.contains("terracotta")) { return EDhApiBlockMaterial.TERRACOTTA; - } - else if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock() == Blocks.NETHERRACK #else this.blockState.is(BlockTags.BASE_STONE_NETHER) #endif) + } + else if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock() == Blocks.NETHERRACK #else this.blockState.is(BlockTags.BASE_STONE_NETHER) #endif) { return EDhApiBlockMaterial.NETHER_STONE; - } + } else if (serialString.contains("stone") - || serialString.contains("ore")) + || serialString.contains("ore")) { return EDhApiBlockMaterial.STONE; } - else if (#if MC_VER <= MC_1_12_2 this.blockState.getLightValue() #else this.blockState.getLightEmission() #endif > 0) + else if (#if MC_VER <= MC_1_12_2 this.blockState.getLightValue() #else this.blockState.getLightEmission() #endif > 0) { return EDhApiBlockMaterial.ILLUMINATED; } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java index 0e997a989..ebf404981 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java @@ -23,7 +23,7 @@ import java.io.File; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; -import com.seibel.distanthorizons.core.render.glObject.GLProxy; +import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -239,7 +239,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false); #else - GLProxy.queueRunningOnRenderThread(() -> + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() -> { player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false); }); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java index 00192d5c2..160853ba0 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java @@ -29,10 +29,8 @@ import com.mojang.blaze3d.opengl.GlStateManager; import com.seibel.distanthorizons.core.jar.EPlatform; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.logging.DhLogger; -import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL32; @@ -40,7 +38,7 @@ import org.lwjgl.opengl.GL32; * Why does DH often call GL methods twice?
* Once using the base {@link GL32} function and a second time using * Minecraft's {@link GlStateManager}?

- * + * * Answer:
* Compatibility and robustness
* In general all MC rendering should go through MC's {@link GlStateManager}, @@ -54,7 +52,7 @@ import org.lwjgl.opengl.GL32; * This may slow down some low end GPUs that are driver limited, * however James would rather have slow correct rendering vs fast broken rendering. */ -public class MinecraftGLWrapper implements IMinecraftGLWrapper +public class MinecraftGLWrapper { public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper(); @@ -70,8 +68,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper // scissor // /** @see GL32#GL_SCISSOR_TEST */ - @Override - public void enableScissorTest() + public void enableScissorTest() { GL32.glEnable(GL32.GL_SCISSOR_TEST); #if MC_VER > MC_1_12_2 @@ -79,9 +76,8 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper #endif } /** @see GL32#GL_SCISSOR_TEST */ - @Override - public void disableScissorTest() - { + public void disableScissorTest() + { GL32.glDisable(GL32.GL_SCISSOR_TEST); #if MC_VER > MC_1_12_2 GlStateManager._disableScissorTest(); @@ -100,8 +96,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper // depth // /** @see GL32#GL_DEPTH_TEST */ - @Override - public void enableDepthTest() + public void enableDepthTest() { GL32.glEnable(GL32.GL_DEPTH_TEST); #if MC_VER <= MC_1_12_2 @@ -111,8 +106,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper #endif } /** @see GL32#GL_DEPTH_TEST */ - @Override - public void disableDepthTest() + public void disableDepthTest() { GL32.glDisable(GL32.GL_DEPTH_TEST); #if MC_VER <= MC_1_12_2 @@ -123,9 +117,8 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper } /** @see GL32#glDepthFunc(int) */ - @Override - public void glDepthFunc(int func) - { + public void glDepthFunc(int func) + { GL32.glDepthFunc(func); #if MC_VER <= MC_1_12_2 GlStateManager.depthFunc(func); @@ -135,8 +128,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper } /** @see GL32#glDepthMask(boolean) */ - @Override - public void enableDepthMask() + public void enableDepthMask() { GL32.glDepthMask(true); #if MC_VER <= MC_1_12_2 @@ -146,8 +138,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper #endif } /** @see GL32#glDepthMask(boolean) */ - @Override - public void disableDepthMask() + public void disableDepthMask() { GL32.glDepthMask(false); #if MC_VER <= MC_1_12_2 @@ -161,8 +152,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper // blending // /** @see GL32#GL_BLEND */ - @Override - public void enableBlend() + public void enableBlend() { GL32.glEnable(GL32.GL_BLEND); #if MC_VER <= MC_1_12_2 @@ -172,8 +162,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper #endif } /** @see GL32#GL_BLEND */ - @Override - public void disableBlend() + public void disableBlend() { GL32.glDisable(GL32.GL_BLEND); #if MC_VER <= MC_1_12_2 @@ -184,8 +173,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper } /** @see GL32#glBlendFunc */ - @Override - public void glBlendFunc(int sfactor, int dfactor) + public void glBlendFunc(int sfactor, int dfactor) { GL32.glBlendFunc(sfactor, dfactor); #if MC_VER <= MC_1_12_2 @@ -195,8 +183,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper #endif } /** @see GL32#glBlendFuncSeparate */ - @Override - public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha) + public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha) { GL32.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); #if MC_VER <= MC_1_12_2 @@ -210,8 +197,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper // frame buffers // /** @see GL32#glBindFramebuffer */ - @Override - public void glBindFramebuffer(int target, int framebuffer) + public void glBindFramebuffer(int target, int framebuffer) { GL32.glBindFramebuffer(target, framebuffer); #if MC_VER > MC_1_12_2 @@ -223,12 +209,10 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper // buffers // /** @see GL32#glGenBuffers() */ - @Override public int glGenBuffers() { return GL32.glGenBuffers(); } - /** @see GL32#glDeleteBuffers(int) */ - @Override + /** @see GL32#glDeleteBuffers(int) */ public void glDeleteBuffers(int buffer) { GL32.glDeleteBuffers(buffer); @@ -259,8 +243,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper // culling // /** @see GL32#GL_CULL_FACE */ - @Override - public void enableFaceCulling() + public void enableFaceCulling() { GL32.glEnable(GL32.GL_CULL_FACE); #if MC_VER <= MC_1_12_2 @@ -270,8 +253,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper #endif } /** @see GL32#GL_CULL_FACE */ - @Override - public void disableFaceCulling() + public void disableFaceCulling() { GL32.glDisable(GL32.GL_CULL_FACE); #if MC_VER <= MC_1_12_2 @@ -285,8 +267,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper // textures // /** @see GL32#glGenTextures() */ - @Override - public int glGenTextures() + public int glGenTextures() { #if MC_VER <= MC_1_12_2 return GlStateManager.generateTexture(); @@ -295,7 +276,6 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper #endif } /** @see GL32#glDeleteTextures(int) */ - @Override public void glDeleteTextures(int texture) { #if MC_VER <= MC_1_12_2 @@ -306,8 +286,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper } /** @see GL32#glActiveTexture(int) */ - @Override - public void glActiveTexture(int textureId) + public void glActiveTexture(int textureId) { GL32.glActiveTexture(textureId); #if MC_VER <= MC_1_12_2 @@ -316,15 +295,13 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper GlStateManager._activeTexture(textureId); #endif } - @Override public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); } /** * Always binds to {@link GL32#GL_TEXTURE_2D} * @see GL32#glBindTexture(int, int) */ - @Override - public void glBindTexture(int texture) + public void glBindTexture(int texture) { GL32.glBindTexture(GL32.GL_TEXTURE_2D, texture); #if MC_VER <= MC_1_12_2 @@ -336,5 +313,4 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper - } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java index d09fa4b9f..c9a3e7176 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java @@ -26,9 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading; import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; -import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -99,6 +97,7 @@ import com.mojang.blaze3d.opengl.GlTexture; #if MC_VER <= MC_1_21_10 #else import net.minecraft.world.attribute.EnvironmentAttributes; +import com.mojang.blaze3d.textures.GpuTexture; #endif /** @@ -554,6 +553,20 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper wrapper.setLightmapId(tetxureId); } + #if MC_VER <= MC_1_21_10 + #else + public void setLightmapGpuTexture(GpuTexture gpuTexture, IClientLevelWrapper level) + { + // Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same + // object for the same MC level and/or the same hash, + // so this will have to do for now + IDimensionTypeWrapper dimensionType = level.getDimensionType(); + + LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper()); + wrapper.setLightmapGpuTexture(gpuTexture); + } + #endif + @Override public float getShade(EDhDirection lodDirection) { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/misc/LightMapWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/misc/LightMapWrapper.java index 6f4d42c19..52d618493 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/misc/LightMapWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/misc/LightMapWrapper.java @@ -21,10 +21,11 @@ package com.seibel.distanthorizons.common.wrappers.misc; #if MC_VER > MC_1_12_2 import com.mojang.blaze3d.platform.NativeImage; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; #endif +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.logging.DhLogger; import org.lwjgl.opengl.GL32; @@ -34,17 +35,36 @@ import java.nio.ByteBuffer; #else #endif +#if MC_VER <= MC_1_21_10 && MC_VER > MC_1_12_2 +#else +import com.mojang.blaze3d.textures.GpuTexture; +#endif + public class LightMapWrapper implements ILightMapWrapper { - private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class); + private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + /** + * which texture index IE 0,1,2... the lightmap will be bound to.
+ * Related to but different from {@link GL32#GL_TEXTURE0}. + */ + public static final int GL_BOUND_INDEX = 0; + private int textureId = 0; #if MC_VER <= MC_1_12_2 private int lastTextureId = 0; private int lastTextureUnit = GL32.GL_TEXTURE0; #endif + + #if MC_VER <= MC_1_21_10 + #else + private GpuTexture gpuTexture = null; + #endif + + private final BlazeTextureViewWrapper lightmapTextureWrapper = new BlazeTextureViewWrapper(); + //==============// // constructors // @@ -106,6 +126,16 @@ public class LightMapWrapper implements ILightMapWrapper this.textureId = minecraftLightmapTextureId; } + #if MC_VER <= MC_1_21_10 && MC_VER > MC_1_12_2 + #else + public void setLightmapGpuTexture(GpuTexture gpuTexture) + { + this.gpuTexture = gpuTexture; + this.lightmapTextureWrapper.tryWrap(this.gpuTexture); + } + #endif + + //endregion @@ -140,6 +170,10 @@ public class LightMapWrapper implements ILightMapWrapper #endif } + public BlazeTextureViewWrapper getTextureViewWrapper() { return this.lightmapTextureWrapper; } + + public int getOpenGlId() { return this.textureId; } + //endregion diff --git a/coreSubProjects b/coreSubProjects index ed0e94ccb..0eba376e7 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit ed0e94ccb7c9de90b7b130b349b56f367a62a92c +Subproject commit 0eba376e7056389d28943ed39ed114ee1de72e37 diff --git a/fabric/build.gradle b/fabric/build.gradle index 77fabeb80..dbe0e6311 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -23,7 +23,8 @@ loom { "-XX:+UseZGC", "-XX:+ZGenerational" ) - programArgs("--username", "Dev") + // "--renderDebugLabels" is a Mojang command to show render names in RenderDoc + programArgs("--username", "Dev", "--renderDebugLabels") } server { server() diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinClientLevel.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinClientLevel.java index 405348409..883cd7115 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinClientLevel.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinClientLevel.java @@ -51,7 +51,7 @@ public class MixinClientLevel if (chunk != null && !chunk.isClientLightReady()) { - SharedApi.INSTANCE.chunkLoadEvent( + SharedApi.INSTANCE.applyChunkUpdate( new ChunkWrapper(chunk, ClientLevelWrapper.getWrapper(clientLevel)), ClientLevelWrapper.getWrapper(clientLevel)); } diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java index 7b5814da9..8329d4148 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java @@ -93,9 +93,15 @@ public class MixinLightTexture this.renderWrapper.updateLightmap(this.lightPixels, clientLevel); #elif MC_VER < MC_1_21_5 this.renderWrapper.setLightmapId(this.target.getColorTextureId(), clientLevel); - #else + #elif MC_VER <= MC_1_21_10 GlTexture glTexture = (GlTexture) this.texture; this.renderWrapper.setLightmapId(glTexture.glId(), clientLevel); + #else + // both options are available since the renderer can be changed to either Blaze3D or OpenGL + GlTexture glTexture = (GlTexture) this.texture; + this.renderWrapper.setLightmapId(glTexture.glId(), clientLevel); + + this.renderWrapper.setLightmapGpuTexture(this.texture, clientLevel); #endif } diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinSharedConstants.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinSharedConstants.java new file mode 100644 index 000000000..c747d8335 --- /dev/null +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinSharedConstants.java @@ -0,0 +1,35 @@ +package com.seibel.distanthorizons.fabric.mixins.client; + +import com.seibel.distanthorizons.common.commonMixins.DhUpdateScreenBase; +import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; +import com.seibel.distanthorizons.core.api.internal.ClientApi; +import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import net.minecraft.SharedConstants; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +/** + * At the moment this is only used for the auto updater + * + * @author coolGi + */ +@Mixin(SharedConstants.class) +public abstract class MixinSharedConstants +{ + @Mutable + @Shadow @Final public static boolean IS_RUNNING_IN_IDE; + + @Inject(method = "", at = @At("TAIL")) + private static void setIsRunningInIde(CallbackInfo ci) + { + IS_RUNNING_IN_IDE = true; + } + +} diff --git a/fabric/src/main/resources/DistantHorizons.fabric.mixins.json b/fabric/src/main/resources/DistantHorizons.fabric.mixins.json index 9faea69bf..92f07d61c 100644 --- a/fabric/src/main/resources/DistantHorizons.fabric.mixins.json +++ b/fabric/src/main/resources/DistantHorizons.fabric.mixins.json @@ -20,7 +20,8 @@ "client.MixinChunkSectionsToRender", "client.MixinLightTexture", "client.MixinMinecraft", - "client.MixinOptionsScreen" + "client.MixinOptionsScreen", + "client.MixinSharedConstants" ], "server": [], "injectors": { diff --git a/fabric/src/main/resources/pack.mcmeta b/fabric/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..f48d006ff --- /dev/null +++ b/fabric/src/main/resources/pack.mcmeta @@ -0,0 +1,12 @@ +{ + "pack": { + "pack_format": 64, + "supported_formats": { + "min_inclusive": 64, + "max_inclusive": 90000 + }, + "description": "Distant Horizons", + "min_format": 64, + "max_format": 90000 + } +} diff --git a/gradle.properties b/gradle.properties index 850de47ff..1ff806ea4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,8 +5,8 @@ org.gradle.caching=true # Mod Info mod_name=DistantHorizons -mod_version=2.4.6-b-dev -api_version=5.1.0 +mod_version=3.0.0-b-dev +api_version=6.0.0 maven_group=com.seibel.distanthorizons mod_readable_name=Distant Horizons mod_id=distanthorizons diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 20c8f3f27..b623e98cc 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -32,7 +32,8 @@ loom { ideConfigGenerated(false) // When true a run configuration file will be generated for IDE's. By default only set to true for the root project. runDir("../run/client") vmArgs("-Dio.netty.leakDetection.level=advanced") // https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels - programArgs("--username", "Dev") + // "--renderDebugLabels" is a Mojang command to show render names in RenderDoc + programArgs("--username", "Dev", "--renderDebugLabels") } server { server() diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java index aec713f00..417b030c1 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java @@ -85,9 +85,15 @@ public class MixinLightTexture #elif MC_VER < MC_1_21_9 GlTexture glTexture = (GlTexture) this.texture; renderWrapper.setLightmapId(glTexture.glId(), clientLevel); + #elif MC_VER <= MC_1_21_10 + GlTexture glTexture = (GlTexture) this.texture; + this.renderWrapper.setLightmapId(glTexture.glId(), clientLevel); #else - int id = NeoforgeTextureUnwrapper.getGlTextureIdFromGpuTexture(this.texture); - renderWrapper.setLightmapId(id, clientLevel); + // both options are available since the renderer can be changed to either Blaze3D or OpenGL + GlTexture glTexture = (GlTexture) this.texture; + renderWrapper.setLightmapId(glTexture.glId(), clientLevel); + + renderWrapper.setLightmapGpuTexture(this.texture, clientLevel); #endif } diff --git a/versionProperties/1.21.11.properties b/versionProperties/1.21.11.properties index 92a14048f..24be330c0 100644 --- a/versionProperties/1.21.11.properties +++ b/versionProperties/1.21.11.properties @@ -45,7 +45,7 @@ fabric_api_version=0.139.4+1.21.11 # NeoForge loader forge_version= -neoforge_version=21.11.0-beta +neoforge_version=21.11.38-beta neoforge_version_range=[*,) # NeoForge mod versions