From 6c0abcf8c27ce7a204c3b3ddeadf191edbaa3f22 Mon Sep 17 00:00:00 2001 From: mineLdiver Date: Wed, 12 Jul 2023 14:49:20 +0500 Subject: [PATCH] Added multidraw terrain renderer --- build.gradle | 67 +-- gradle.properties | 14 +- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 9 +- .../net/mine_diver/smoothbeta/SmoothBeta.java | 5 +- .../client/render/BufferRenderer.java | 9 + .../smoothbeta/client/render/IndexBuffer.java | 97 +++++ .../client/render/RenderRegion.java | 59 +++ .../smoothbeta/client/render/Shader.java | 405 ++++++++++++++++++ .../smoothbeta/client/render/Shaders.java | 94 ++++ .../client/render/SmoothChunkRenderer.java | 7 + .../client/render/SmoothTessellator.java | 7 + .../client/render/SmoothWorldRenderer.java | 5 + .../client/render/TerrainContext.java | 11 + .../smoothbeta/client/render/VboPool.java | 291 +++++++++++++ .../client/render/VertexFormat.java | 147 +++++++ .../client/render/VertexFormatElement.java | 170 ++++++++ .../client/render/VertexFormats.java | 15 + .../client/render/gl/GLImportProcessor.java | 157 +++++++ .../client/render/gl/GlBlendState.java | 122 ++++++ .../client/render/gl/GlProgramManager.java | 39 ++ .../smoothbeta/client/render/gl/GlShader.java | 15 + .../client/render/gl/GlStateManager.java | 137 ++++++ .../client/render/gl/GlUniform.java | 205 +++++++++ .../smoothbeta/client/render/gl/Program.java | 104 +++++ .../render/gl/ShaderParseException.java | 80 ++++ .../smoothbeta/client/render/gl/Uniform.java | 22 + .../client/render/gl/VertexBuffer.java | 31 ++ .../smoothbeta/mixin/MixinBiomeSource.java | 151 ------- .../smoothbeta/mixin/MixinChunk.java | 40 -- .../smoothbeta/mixin/MixinLevel.java | 22 - .../mixin/client/ChunkRendererMixin.java | 108 +++++ .../mixin/client/MinecraftAccessor.java | 11 + .../mixin/client/RenderListAccessor.java | 34 ++ .../mixin/client/TessellatorMixin.java | 118 +++++ .../mixin/client/WorldRendererMixin.java | 140 ++++++ .../smoothbeta/util/ChunkCustomAccessor.java | 14 - .../smoothbeta/shaders/core/terrain.fsh | 23 + .../smoothbeta/shaders/core/terrain.json | 28 ++ .../smoothbeta/shaders/core/terrain.vsh | 27 ++ .../smoothbeta/shaders/include/fog.glsl | 53 +++ src/main/resources/fabric.mod.json | 3 +- src/main/resources/smoothbeta.mixins.json | 10 +- 43 files changed, 2834 insertions(+), 274 deletions(-) create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/BufferRenderer.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/IndexBuffer.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/RenderRegion.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/Shader.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/Shaders.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/SmoothChunkRenderer.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/SmoothTessellator.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/SmoothWorldRenderer.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/TerrainContext.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/VboPool.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormat.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormatElement.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormats.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/GLImportProcessor.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlBlendState.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlProgramManager.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlShader.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlStateManager.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlUniform.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/Program.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/ShaderParseException.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/Uniform.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/client/render/gl/VertexBuffer.java delete mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/MixinBiomeSource.java delete mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/MixinChunk.java delete mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/MixinLevel.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/client/ChunkRendererMixin.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/client/MinecraftAccessor.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/client/RenderListAccessor.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/client/TessellatorMixin.java create mode 100644 src/main/java/net/mine_diver/smoothbeta/mixin/client/WorldRendererMixin.java delete mode 100644 src/main/java/net/mine_diver/smoothbeta/util/ChunkCustomAccessor.java create mode 100644 src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.fsh create mode 100644 src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.json create mode 100644 src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.vsh create mode 100644 src/main/resources/assets/minecraft/smoothbeta/shaders/include/fog.glsl diff --git a/build.gradle b/build.gradle index e93ebdc..004aa49 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '0.12.cursed_1' + id 'babric-loom' version '1.1.7' id 'maven-publish' } @@ -10,59 +10,67 @@ archivesBaseName = project.archives_base_name version = project.mod_version group = project.maven_group +loom { + gluedMinecraftJar() + customMinecraftManifest.set("https://babric.github.io/manifest-polyfill/${minecraft_version}.json") +} + repositories { // Add repositories to retrieve artifacts from in here. // You should only use this when depending on other mods because // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. // See https://docs.gradle.org/current/userguide/declaring_repositories.html // for more information about repositories. + + // Used for the fabric toolchain in this project. maven { - name = 'Jitpack' - url = 'https://jitpack.io' + name = 'Babric' + url = 'https://maven.glass-launcher.net/babric' } + // Used for mappings. maven { - name = 'Glass' - url = 'https://maven.glass-launcher.net/snapshots' + name = 'Glass Releases' + url = 'https://maven.glass-launcher.net/releases' } + // Used for StationAPI and HowManyItems. maven { - name = 'HalfOf2' - url = 'https://storage.googleapis.com/devan-maven/' + name = 'Glass Snapshots' + url = 'https://maven.glass-launcher.net/snapshots' } + // Used for a StationAPI dependency. maven { name = 'Froge' url 'https://maven.minecraftforge.net/' } -} - -loom { - customMinecraftManifest = "https://pymcl.net/b1.7.3.json" - intermediaryUrl = "https://pymcl.net/b1.7.3-int.jar" - cursedMinecraftJar() + // Used for projects that do not have a maven repository, but do have a GitHub repository with working build scripts. + maven { + name = 'Jitpack' + url = 'https://jitpack.io' + } + mavenCentral() } dependencies { // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "com.github.calmilamsy:BIN-Mappings:${project.yarn_mappings}" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + mappings "net.glasslauncher:bin:${project.yarn_mappings}" + modImplementation "babric:fabric-loader:${project.loader_version}" - modImplementation ("net.modificationstation:StationAPI:99ccdc3") { - exclude module: "cursed-fabric-loader" - } - - implementation "com.google.code.gson:gson:2.9.0" + implementation "org.slf4j:slf4j-api:1.8.0-beta4" + implementation 'org.apache.logging.log4j:log4j-slf4j18-impl:2.17.2' - compileOnly 'org.projectlombok:lombok:1.18.22' - annotationProcessor 'org.projectlombok:lombok:1.18.22' + modImplementation "net.modificationstation:StationAPI:${project.stapi_version}" - implementation "blue.endless:jankson:1.2.1" - modImplementation("com.github.calmilamsy:HowManyItems-Fabric-Unofficial:1c6873a") { - exclude module: "cursed-fabric-loader" - exclude module: "StationAPI" + // Optional, but convenient mods for mod creators and users alike. + modImplementation("com.github.calmilamsy:ModMenu:${project.modmenu_version}") { + transitive false + } + modImplementation("net.glasslauncher.mods:GlassConfigAPI:${project.gcapi_version}") { + transitive false + } + modImplementation("net.glasslauncher:HowManyItems-Fabric-Unofficial:${project.howmanyitems_version}") { + transitive false } - - // Fabric API. This is technically optional, but you probably want it anyway. - // modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" } processResources { @@ -75,6 +83,7 @@ processResources { tasks.withType(JavaCompile).configureEach { // Minecraft 1.18 (1.18-pre2) upwards uses Java 17. + // Loom also requires J17. it.options.release = 17 } diff --git a/gradle.properties b/gradle.properties index 95afa2c..0f9e808 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,12 +5,18 @@ org.gradle.daemon=false # Fabric Properties # check these on https://fabricmc.net/develop minecraft_version=b1.7.3 -yarn_mappings=43d39fa -loader_version=0.12.12 +yarn_mappings=b1.7.3-build.2 +loader_version=0.14.19-babric.1 # Mod Properties -mod_version = 1.0.0 +mod_version = 1.1 maven_group = net.mine_diver archives_base_name = SmoothBeta -# Dependencies \ No newline at end of file +# Dependencies +stapi_version=2c6d948 + +# Extra Dependencies +gcapi_version=1.1.5 +howmanyitems_version=5.0.13 +modmenu_version=v1.8.5-beta.3 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0b77c26..d0b7eaa 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Sun Jun 14 19:21:24 BST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 363a2f4..591ddf2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,14 +5,9 @@ pluginManagement { url = 'https://maven.fabricmc.net/' } maven { - name = 'Jitpack' - url = 'https://jitpack.io' + name = 'Babric' + url = 'https://maven.glass-launcher.net/babric' } - maven { - name = "Glass" - url = "https://maven.glass-launcher.net/snapshots" - } - mavenLocal() mavenCentral() gradlePluginPortal() } diff --git a/src/main/java/net/mine_diver/smoothbeta/SmoothBeta.java b/src/main/java/net/mine_diver/smoothbeta/SmoothBeta.java index bba12bd..74f8423 100644 --- a/src/main/java/net/mine_diver/smoothbeta/SmoothBeta.java +++ b/src/main/java/net/mine_diver/smoothbeta/SmoothBeta.java @@ -4,10 +4,13 @@ import net.modificationstation.stationapi.api.mod.entrypoint.EventBusPolicy; import net.modificationstation.stationapi.api.registry.ModID; import net.modificationstation.stationapi.api.util.Null; +import org.apache.logging.log4j.Logger; @Entrypoint(eventBus = @EventBusPolicy(registerStatic = false, registerInstance = false)) public class SmoothBeta { - @Entrypoint.ModID public static final ModID MODID = Null.get(); + + @Entrypoint.Logger + public static final Logger LOGGER = Null.get(); } diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/BufferRenderer.java b/src/main/java/net/mine_diver/smoothbeta/client/render/BufferRenderer.java new file mode 100644 index 0000000..80529d9 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/BufferRenderer.java @@ -0,0 +1,9 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; + +public class BufferRenderer { + public static void unbindAll() { + VertexBuffer.unbind(); + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/IndexBuffer.java b/src/main/java/net/mine_diver/smoothbeta/client/render/IndexBuffer.java new file mode 100644 index 0000000..83d790e --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/IndexBuffer.java @@ -0,0 +1,97 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.modificationstation.stationapi.api.util.math.MathHelper; +import org.lwjgl.opengl.GL15; + +import java.nio.ByteBuffer; +import java.util.function.IntConsumer; + +import static net.mine_diver.smoothbeta.SmoothBeta.LOGGER; + +@Environment(EnvType.CLIENT) +public final class IndexBuffer { + private static final IndexBuffer sharedSequential = new IndexBuffer(1, 1, java.util.function.IntConsumer::accept);; + private static final IndexBuffer sharedSequentialQuad = new IndexBuffer(4, 6, (intConsumer, i) -> { + intConsumer.accept(i); + intConsumer.accept(i + 1); + intConsumer.accept(i + 2); + intConsumer.accept(i + 2); + intConsumer.accept(i + 3); + intConsumer.accept(i); + }); + private static final IndexBuffer sharedSequentialLines = new IndexBuffer(4, 6, (intConsumer, i) -> { + intConsumer.accept(i); + intConsumer.accept(i + 1); + intConsumer.accept(i + 2); + intConsumer.accept(i + 3); + intConsumer.accept(i + 2); + intConsumer.accept(i + 1); + }); + + public static IndexBuffer getSequentialBuffer(VertexFormat.DrawMode drawMode) { + return switch (drawMode) { + case QUADS -> sharedSequentialQuad; + case LINES -> sharedSequentialLines; + default -> sharedSequential; + }; + } + + private final int sizeMultiplier; + private final int increment; + private final IndexMapper indexMapper; + private int id; + private VertexFormat.IndexType indexType = VertexFormat.IndexType.BYTE; + private int size; + + IndexBuffer(int sizeMultiplier, int increment, IndexMapper indexMapper) { + this.sizeMultiplier = sizeMultiplier; + this.increment = increment; + this.indexMapper = indexMapper; + } + + public boolean isSizeLessThanOrEqual(int size) { + return size <= this.size; + } + + public void bindAndGrow(int newSize) { + if (this.id == 0) this.id = GL15.glGenBuffers(); + GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.id); + this.grow(newSize); + } + + private void grow(int newSize) { + if (this.isSizeLessThanOrEqual(newSize)) return; + newSize = MathHelper.roundUpToMultiple(newSize * 2, this.increment); + LOGGER.debug("Growing IndexBuffer: Old limit {}, new limit {}.", this.size, newSize); + VertexFormat.IndexType indexType = VertexFormat.IndexType.smallestFor(newSize); + int i = MathHelper.roundUpToMultiple(newSize * indexType.size, 4); + GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, i, GL15.GL_DYNAMIC_DRAW); + ByteBuffer byteBuffer = GL15.glMapBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_WRITE_ONLY, null); + if (byteBuffer == null) throw new RuntimeException("Failed to map GL buffer"); + this.indexType = indexType; + IntConsumer intConsumer = this.getIndexConsumer(byteBuffer); + for (int j = 0; j < newSize; j += this.increment) + this.indexMapper.accept(intConsumer, j * this.sizeMultiplier / this.increment); + GL15.glUnmapBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER); + this.size = newSize; + } + + private IntConsumer getIndexConsumer(ByteBuffer indicesBuffer) { + return switch (this.indexType) { + case BYTE -> index -> indicesBuffer.put((byte) index); + case SHORT -> index -> indicesBuffer.putShort((short) index); + default -> indicesBuffer::putInt; + }; + } + + public VertexFormat.IndexType getIndexType() { + return this.indexType; + } + + @Environment(EnvType.CLIENT) + interface IndexMapper { + void accept(IntConsumer var1, int var2); + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/RenderRegion.java b/src/main/java/net/mine_diver/smoothbeta/client/render/RenderRegion.java new file mode 100644 index 0000000..1e35fc9 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/RenderRegion.java @@ -0,0 +1,59 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.mine_diver.smoothbeta.client.render.gl.GlUniform; +import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; +import net.mine_diver.smoothbeta.mixin.client.RenderListAccessor; +import net.minecraft.client.render.RenderList; +import net.minecraft.client.render.WorldRenderer; +import net.modificationstation.stationapi.api.util.math.Vec3f; + +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.List; + +public class RenderRegion extends RenderList { + + private final RenderListAccessor _super = (RenderListAccessor) this; + private final SmoothWorldRenderer stationWorldRenderer; + private final List buffers = new ArrayList<>(); + + public RenderRegion(WorldRenderer worldRenderer) { + _super.smoothbeta_setField_2486(IntBuffer.allocate(0)); + stationWorldRenderer = ((SmoothWorldRenderer) worldRenderer); + } + + @Override + public void method_1912(int i, int j, int k, double d, double e, double f) { + super.method_1912(i, j, k, d, e, f); + buffers.clear(); + } + + @Override + public void method_1910(int i) { + throw new UnsupportedOperationException("Call lists can't be added to VBO regions!"); + } + + public void addBuffer(VertexBuffer buffer) { + buffers.add(buffer); + } + + public void method_1909() { + if (!_super.smoothbeta_getField_2487()) { + return; + } + if (!buffers.isEmpty()) { + Shader shader = Shaders.getTerrainShader(); + GlUniform chunkOffset = null; + if (shader != null) + chunkOffset = shader.chunkOffset; + if (chunkOffset != null) { + chunkOffset.set(_super.smoothbeta_getField_2480() - _super.smoothbeta_getField_2483(), _super.smoothbeta_getField_2481() - _super.smoothbeta_getField_2484(), _super.smoothbeta_getField_2482() - _super.smoothbeta_getField_2485()); + chunkOffset.upload(); + } + for (VertexBuffer vertexBuffer : buffers) vertexBuffer.uploadToPool(); + stationWorldRenderer.smoothbeta_getTerrainVboPool().drawAll(); + if (chunkOffset != null) + chunkOffset.set(Vec3f.ZERO); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/Shader.java b/src/main/java/net/mine_diver/smoothbeta/client/render/Shader.java new file mode 100644 index 0000000..70c75a3 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/Shader.java @@ -0,0 +1,405 @@ +package net.mine_diver.smoothbeta.client.render; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.mine_diver.smoothbeta.client.render.gl.*; +import net.modificationstation.stationapi.api.client.texture.AbstractTexture; +import net.modificationstation.stationapi.api.registry.Identifier; +import net.modificationstation.stationapi.api.resource.Resource; +import net.modificationstation.stationapi.api.resource.ResourceFactory; +import net.modificationstation.stationapi.api.util.FileNameUtil; +import net.modificationstation.stationapi.api.util.JsonHelper; +import org.apache.commons.io.IOUtils; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL13; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.*; + +import static net.mine_diver.smoothbeta.SmoothBeta.LOGGER; +import static net.mine_diver.smoothbeta.SmoothBeta.MODID; + +@Environment(EnvType.CLIENT) +public class Shader implements GlShader, AutoCloseable { + private static final String CORE_DIRECTORY = MODID + "/shaders/core/"; + private static final String INCLUDE_DIRECTORY = MODID + "/shaders/include/"; + private static int activeShaderId; + private final Map samplers = new HashMap<>(); + private final List samplerNames = new ArrayList<>(); + private final IntList loadedSamplerIds = new IntArrayList(); + private final List uniforms = new ArrayList<>(); + private final Map loadedUniforms = new HashMap<>(); + private final int programId; + private final String name; + private final GlBlendState blendState; + private final Program vertexShader; + private final Program fragmentShader; + @Nullable + public final GlUniform modelViewMat; + @Nullable + public final GlUniform projectionMat; + @Nullable + public final GlUniform fogMode; + @Nullable + public final GlUniform fogDensity; + @Nullable + public final GlUniform fogStart; + @Nullable + public final GlUniform fogEnd; + @Nullable + public final GlUniform fogColor; + @Nullable + public final GlUniform chunkOffset; + + public Shader(ResourceFactory factory, String name, VertexFormat format) throws IOException { + this.name = name; + Identifier identifier = Identifier.of(CORE_DIRECTORY + name + ".json"); + try (BufferedReader reader = factory.openAsReader(identifier);){ + JsonArray jsonArray3; + JsonArray jsonArray2; + JsonObject jsonObject = JsonHelper.deserialize(reader); + String string = JsonHelper.getString(jsonObject, "vertex"); + String string2 = JsonHelper.getString(jsonObject, "fragment"); + JsonArray jsonArray = JsonHelper.getArray(jsonObject, "samplers", null); + if (jsonArray != null) { + int i = 0; + for (JsonElement jsonElement : jsonArray) { + try { + this.readSampler(jsonElement); + } + catch (Exception exception) { + ShaderParseException shaderParseException = ShaderParseException.wrap(exception); + shaderParseException.addFaultyElement("samplers[" + i + "]"); + throw shaderParseException; + } + ++i; + } + } + List attributeNames; + List loadedAttributeIds; + if ((jsonArray2 = JsonHelper.getArray(jsonObject, "attributes", null)) != null) { + int j = 0; + loadedAttributeIds = Lists.newArrayListWithCapacity(jsonArray2.size()); + attributeNames = Lists.newArrayListWithCapacity(jsonArray2.size()); + for (JsonElement jsonElement2 : jsonArray2) { + try { + attributeNames.add(JsonHelper.asString(jsonElement2, "attribute")); + } + catch (Exception exception2) { + ShaderParseException shaderParseException2 = ShaderParseException.wrap(exception2); + shaderParseException2.addFaultyElement("attributes[" + j + "]"); + throw shaderParseException2; + } + ++j; + } + } else { + loadedAttributeIds = null; + attributeNames = null; + } + if ((jsonArray3 = JsonHelper.getArray(jsonObject, "uniforms", null)) != null) { + int k = 0; + for (JsonElement jsonElement3 : jsonArray3) { + try { + this.addUniform(jsonElement3); + } + catch (Exception exception3) { + ShaderParseException shaderParseException3 = ShaderParseException.wrap(exception3); + shaderParseException3.addFaultyElement("uniforms[" + k + "]"); + throw shaderParseException3; + } + ++k; + } + } + this.blendState = Shader.readBlendState(JsonHelper.getObject(jsonObject, "blend", null)); + this.vertexShader = Shader.loadProgram(factory, Program.Type.VERTEX, string); + this.fragmentShader = Shader.loadProgram(factory, Program.Type.FRAGMENT, string2); + this.programId = GlProgramManager.createProgram(); + if (attributeNames != null) { + int k = 0; + for (String string3 : format.getAttributeNames()) { + GlUniform.bindAttribLocation(this.programId, k, string3); + loadedAttributeIds.add(k); + ++k; + } + } + GlProgramManager.linkProgram(this); + this.loadReferences(); + } + catch (Exception exception4) { + ShaderParseException shaderParseException4 = ShaderParseException.wrap(exception4); + shaderParseException4.addFaultyFile(identifier.id); + throw shaderParseException4; + } + this.modelViewMat = this.getUniform("ModelViewMat"); + this.projectionMat = this.getUniform("ProjMat"); + this.fogMode = this.getUniform("FogMode"); + this.fogDensity = this.getUniform("FogDensity"); + this.fogStart = this.getUniform("FogStart"); + this.fogEnd = this.getUniform("FogEnd"); + this.fogColor = this.getUniform("FogColor"); + this.chunkOffset = this.getUniform("ChunkOffset"); + } + + private static Program loadProgram(ResourceFactory factory, Program.Type type, String name) throws IOException { + Program program2; + Program program = type.getProgramCache().get(name); + if (program == null) { + String string = CORE_DIRECTORY + name + type.getFileExtension(); + Resource resource = factory.getResourceOrThrow(Identifier.of(string)); + try (InputStream inputStream = resource.getInputStream()) { + final String string2 = FileNameUtil.getPosixFullPath(string); + program2 = Program.createFromResource(type, name, inputStream, resource.getResourcePackName(), new GLImportProcessor() { + private final Set visitedImports = Sets.newHashSet(); + + @Override + public String loadImport(boolean inline, String name) { + String string; + name = FileNameUtil.normalizeToPosix((inline ? string2 : Shader.INCLUDE_DIRECTORY) + name); + if (!this.visitedImports.add(name)) return null; + Identifier identifier = Identifier.of(name); + BufferedReader reader; + try { + reader = factory.openAsReader(identifier); + } catch (IOException e) { + throw new RuntimeException(e); + } + try { + string = IOUtils.toString(reader); + } catch (Throwable throwable) { + try { + if (reader != null) try { + ((Reader) reader).close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + throw throwable; + } catch (IOException iOException) { + LOGGER.error("Could not open GLSL import {}: {}", name, iOException.getMessage()); + return "#error " + iOException.getMessage(); + } + } + try { + ((Reader) reader).close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return string; + } + }); + } + } else program2 = program; + return program2; + } + + public static GlBlendState readBlendState(JsonObject json) { + if (json == null) return new GlBlendState(); + else { + int i = 32774; + int j = 1; + int k = 0; + int l = 1; + int m = 0; + boolean bl = true; + boolean bl2 = false; + if (JsonHelper.hasString(json, "func")) { + i = GlBlendState.getFuncFromString(json.get("func").getAsString()); + if (i != 32774) bl = false; + } + + if (JsonHelper.hasString(json, "srcrgb")) { + j = GlBlendState.getComponentFromString(json.get("srcrgb").getAsString()); + if (j != 1) bl = false; + } + + if (JsonHelper.hasString(json, "dstrgb")) { + k = GlBlendState.getComponentFromString(json.get("dstrgb").getAsString()); + if (k != 0) bl = false; + } + + if (JsonHelper.hasString(json, "srcalpha")) { + l = GlBlendState.getComponentFromString(json.get("srcalpha").getAsString()); + if (l != 1) bl = false; + + bl2 = true; + } + + if (JsonHelper.hasString(json, "dstalpha")) { + m = GlBlendState.getComponentFromString(json.get("dstalpha").getAsString()); + if (m != 0) bl = false; + + bl2 = true; + } + + if (bl) return new GlBlendState(); + else return bl2 ? new GlBlendState(j, k, l, m, i) : new GlBlendState(j, k, i); + } + } + + public void close() { + + for (GlUniform glUniform : this.uniforms) glUniform.close(); + + GlProgramManager.deleteProgram(this); + } + + public void unbind() { + GlProgramManager.useProgram(0); + activeShaderId = -1; + int i = GlStateManager._getActiveTexture(); + + for(int j = 0; j < this.loadedSamplerIds.size(); ++j) + if (this.samplers.get(this.samplerNames.get(j)) != null) { + GlStateManager._activeTexture(GL13.GL_TEXTURE0 + j); + GlStateManager._bindTexture(0); + } + + GlStateManager._activeTexture(i); + } + + public void bind() { + this.blendState.enable(); + if (this.programId != activeShaderId) { + GlProgramManager.useProgram(this.programId); + activeShaderId = this.programId; + } + + int i = GlStateManager._getActiveTexture(); + + for(int j = 0; j < this.loadedSamplerIds.size(); ++j) { + String string = this.samplerNames.get(j); + if (this.samplers.get(string) != null) { + int k = GlUniform.getUniformLocation(this.programId, string); + GlUniform.uniform1(k, j); + GlStateManager._activeTexture(GL13.GL_TEXTURE0 + j); + GlStateManager._enableTexture(); + Object object = this.samplers.get(string); + int l = -1; + if (object instanceof AbstractTexture) l = ((AbstractTexture) object).getGlId(); + else if (object instanceof Integer) l = (Integer) object; + + if (l != -1) GlStateManager._bindTexture(l); + } + } + + GlStateManager._activeTexture(i); + + for (GlUniform glUniform : this.uniforms) glUniform.upload(); + + } + + @Nullable + public GlUniform getUniform(String name) { + return this.loadedUniforms.get(name); + } + + private void loadReferences() { + IntList intList = new IntArrayList(); + + int i; + for(i = 0; i < this.samplerNames.size(); ++i) { + String string = this.samplerNames.get(i); + int j = GlUniform.getUniformLocation(this.programId, string); + if (j == -1) { + LOGGER.warn("Shader {} could not find sampler named {} in the specified shader program.", this.name, string); + this.samplers.remove(string); + intList.add(i); + } else this.loadedSamplerIds.add(j); + } + + for(i = intList.size() - 1; i >= 0; --i) { + int k = intList.getInt(i); + this.samplerNames.remove(k); + } + + for (GlUniform glUniform : this.uniforms) { + String string2 = glUniform.getName(); + int l = GlUniform.getUniformLocation(this.programId, string2); + if (l == -1) + LOGGER.warn("Shader {} could not find uniform named {} in the specified shader program.", this.name, string2); + else { + glUniform.setLocation(l); + this.loadedUniforms.put(string2, glUniform); + } + } + + } + + private void readSampler(JsonElement json) { + JsonObject jsonObject = JsonHelper.asObject(json, "sampler"); + String string = JsonHelper.getString(jsonObject, "name"); + if (!JsonHelper.hasString(jsonObject, "file")) { + this.samplers.put(string, null); + this.samplerNames.add(string); + } else this.samplerNames.add(string); + } + + public void addSampler(String name, Object sampler) { + this.samplers.put(name, sampler); + } + + private void addUniform(JsonElement json) throws ShaderParseException { + JsonObject jsonObject = JsonHelper.asObject(json, "uniform"); + String string = JsonHelper.getString(jsonObject, "name"); + int i = GlUniform.getTypeIndex(JsonHelper.getString(jsonObject, "type")); + int j = JsonHelper.getInt(jsonObject, "count"); + float[] fs = new float[Math.max(j, 16)]; + JsonArray jsonArray = JsonHelper.getArray(jsonObject, "values"); + if (jsonArray.size() != j && jsonArray.size() > 1) + throw new ShaderParseException("Invalid amount of values specified (expected " + j + ", found " + jsonArray.size() + ")"); + else { + int k = 0; + + for(Iterator var9 = jsonArray.iterator(); var9.hasNext(); ++k) { + JsonElement jsonElement = var9.next(); + + try { + fs[k] = JsonHelper.asFloat(jsonElement, "value"); + } catch (Exception var13) { + ShaderParseException shaderParseException = ShaderParseException.wrap(var13); + shaderParseException.addFaultyElement("values[" + k + "]"); + throw shaderParseException; + } + } + + if (j > 1 && jsonArray.size() == 1) while (k < j) { + fs[k] = fs[0]; + ++k; + } + + int l = j > 1 && j <= 4 && i < 8 ? j - 1 : 0; + GlUniform glUniform = new GlUniform(string, i + l, j); + if (i <= 3) glUniform.setForDataType((int) fs[0], (int) fs[1], (int) fs[2], (int) fs[3]); + else if (i <= 7) glUniform.setForDataType(fs[0], fs[1], fs[2], fs[3]); + else glUniform.set(Arrays.copyOfRange(fs, 0, j)); + + this.uniforms.add(glUniform); + } + } + + public Program getVertexShader() { + return this.vertexShader; + } + + public Program getFragmentShader() { + return this.fragmentShader; + } + + public void attachReferencedShaders() { + this.fragmentShader.attachTo(this); + this.vertexShader.attachTo(this); + } + + public int getProgramRef() { + return this.programId; + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/Shaders.java b/src/main/java/net/mine_diver/smoothbeta/client/render/Shaders.java new file mode 100644 index 0000000..9a5ba34 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/Shaders.java @@ -0,0 +1,94 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.mine_diver.smoothbeta.client.render.gl.Program; +import net.mine_diver.unsafeevents.listener.EventListener; +import net.modificationstation.stationapi.api.client.event.resource.AssetsResourceReloaderRegisterEvent; +import net.modificationstation.stationapi.api.registry.Identifier; +import net.modificationstation.stationapi.api.resource.IdentifiableResourceReloadListener; +import net.modificationstation.stationapi.api.resource.ResourceManager; +import net.modificationstation.stationapi.api.util.profiler.Profiler; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +import static net.mine_diver.smoothbeta.SmoothBeta.MODID; + +public class Shaders implements IdentifiableResourceReloadListener { + public static final Identifier ID = MODID.id("shaders"); + + @Nullable + private static Shader terrainShader; + + @EventListener + void registerShaderReloader(AssetsResourceReloaderRegisterEvent event) { + event.resourceManager.registerReloader(this); + } + + private record Application( + Runnable clearCache, + Supplier shaderFactory + ) {} + + private static Application loadShaders(ResourceManager manager, Profiler profiler) { + profiler.startTick(); + + profiler.push("cache_release"); + List list = new ArrayList<>(); + list.addAll(Program.Type.FRAGMENT.getProgramCache().values()); + list.addAll(Program.Type.VERTEX.getProgramCache().values()); + + profiler.swap("shader_factory"); + Supplier shaderFactory = () -> { + try { + return new Shader(manager, "terrain", VertexFormats.POSITION_TEXTURE_COLOR_NORMAL); + } catch (IOException e) { + throw new RuntimeException("Could not reload terrain shader", e); + } + }; + + profiler.pop(); + profiler.endTick(); + return new Application(() -> list.forEach(Program::release), shaderFactory); + } + + private static void applyShader(Application application, Profiler profiler) { + profiler.startTick(); + + profiler.push("cache_release"); + application.clearCache.run(); + + if (terrainShader != null) { + profiler.swap("delete_shader"); + terrainShader.close(); + } + + profiler.swap("load_shader"); + terrainShader = application.shaderFactory.get(); + + profiler.pop(); + profiler.endTick(); + } + + @Nullable + public static Shader getTerrainShader() { + return terrainShader; + } + + @Override + public CompletableFuture reload(Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor) { + return CompletableFuture + .supplyAsync(() -> loadShaders(manager, prepareProfiler), prepareExecutor) + .thenCompose(synchronizer::whenPrepared) + .thenAcceptAsync(shaderFactory -> applyShader(shaderFactory, applyProfiler), applyExecutor); + } + + @Override + public Identifier getId() { + return ID; + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothChunkRenderer.java b/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothChunkRenderer.java new file mode 100644 index 0000000..61572e0 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothChunkRenderer.java @@ -0,0 +1,7 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; + +public interface SmoothChunkRenderer { + VertexBuffer smoothbeta_getBuffer(int pass); +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothTessellator.java b/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothTessellator.java new file mode 100644 index 0000000..80ae806 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothTessellator.java @@ -0,0 +1,7 @@ +package net.mine_diver.smoothbeta.client.render; + +public interface SmoothTessellator { + void smoothbeta_startRenderingTerrain(TerrainContext context); + + void smoothbeta_stopRenderingTerrain(); +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothWorldRenderer.java b/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothWorldRenderer.java new file mode 100644 index 0000000..9573a60 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/SmoothWorldRenderer.java @@ -0,0 +1,5 @@ +package net.mine_diver.smoothbeta.client.render; + +public interface SmoothWorldRenderer { + VboPool smoothbeta_getTerrainVboPool(); +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/TerrainContext.java b/src/main/java/net/mine_diver/smoothbeta/client/render/TerrainContext.java new file mode 100644 index 0000000..0202a8e --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/TerrainContext.java @@ -0,0 +1,11 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.modificationstation.stationapi.api.util.math.MatrixStack; + +import java.nio.ByteBuffer; +import java.util.function.Consumer; + +public record TerrainContext( + MatrixStack matrices, + Consumer terrainConsumer +) {} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/VboPool.java b/src/main/java/net/mine_diver/smoothbeta/client/render/VboPool.java new file mode 100644 index 0000000..a396b5a --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/VboPool.java @@ -0,0 +1,291 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.fabricmc.loader.api.FabricLoader; +import net.mine_diver.smoothbeta.client.render.gl.GlStateManager; +import net.mine_diver.smoothbeta.mixin.client.MinecraftAccessor; +import net.minecraft.class_214; +import net.modificationstation.stationapi.api.util.collection.LinkedList; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL31; +import org.lwjgl.opengl.GL43; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +public class VboPool implements AutoCloseable { + @SuppressWarnings("deprecation") + private static final MinecraftAccessor mc = ((MinecraftAccessor) FabricLoader.getInstance().getGameInstance()); + + private final VertexFormat format; + private int vertexArrayId = GL30.glGenVertexArrays(); + private int vertexBufferId = GL15.glGenBuffers(); + private int capacity = 4096; + private int nextPos = 0; + private int size; + private final LinkedList posList = new LinkedList<>(); + private Pos compactPosLast = null; + private int curBaseInstance; + + private IntBuffer bufferIndirect = class_214.method_745(this.capacity * 5); + private final int vertexBytes; + private VertexFormat.DrawMode drawMode = VertexFormat.DrawMode.QUADS; + + public VboPool(VertexFormat format) { + this.format = format; + vertexBytes = format.getVertexSizeByte(); + this.bindBuffer(); + long i = this.toBytes(this.capacity); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, i, GL15.GL_STATIC_DRAW); + this.unbindBuffer(); + } + + @Override + public void close() throws Exception { + if (this.vertexBufferId > 0) { + GlStateManager._glDeleteBuffers(this.vertexBufferId); + this.vertexBufferId = 0; + } + } + + public void bufferData(ByteBuffer data, Pos poolPos) { + if (this.vertexBufferId >= 0) { + int position = poolPos.getPosition(); + int size = poolPos.getSize(); + int bufferSize = this.toVertex(data.limit()); + + if (bufferSize <= 0) { + if (position >= 0) { + poolPos.setPosition(-1); + poolPos.setSize(0); + this.posList.remove(poolPos.getNode()); + this.size -= size; + } + } else { + if (bufferSize > size) { + poolPos.setPosition(this.nextPos); + poolPos.setSize(bufferSize); + this.nextPos += bufferSize; + + if (position >= 0) this.posList.remove(poolPos.getNode()); + + this.posList.addLast(poolPos.getNode()); + } + + poolPos.setSize(bufferSize); + this.size += bufferSize - size; + this.checkVboSize(poolPos.getPositionNext()); + long l = this.toBytes(poolPos.getPosition()); + this.bindVertexArray(); + this.bindBuffer(); + GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, l, data); + this.unbindBuffer(); + unbindVertexArray(); + + if (this.nextPos > this.size * 11 / 10) this.compactRanges(); + } + } + } + + private void compactRanges() { + if (!this.posList.isEmpty()) { + Pos vborange = this.compactPosLast; + + if (vborange == null || !this.posList.contains(vborange.getNode())) + vborange = this.posList.getFirst().getItem(); + + int i; + Pos vborange1 = vborange.getPrev(); + + if (vborange1 == null) i = 0; + else i = vborange1.getPositionNext(); + + int j = 0; + + while (vborange != null && j < 1) { + ++j; + + if (vborange.getPosition() == i) { + i += vborange.getSize(); + vborange = vborange.getNext(); + } else { + int k = vborange.getPosition() - i; + + if (vborange.getSize() <= k) { + this.copyVboData(vborange.getPosition(), i, vborange.getSize()); + vborange.setPosition(i); + i += vborange.getSize(); + vborange = vborange.getNext(); + } else { + this.checkVboSize(this.nextPos + vborange.getSize()); + this.copyVboData(vborange.getPosition(), this.nextPos, vborange.getSize()); + vborange.setPosition(this.nextPos); + this.nextPos += vborange.getSize(); + Pos vborange2 = vborange.getNext(); + this.posList.remove(vborange.getNode()); + this.posList.addLast(vborange.getNode()); + vborange = vborange2; + } + } + } + + if (vborange == null) this.nextPos = this.posList.getLast().getItem().getPositionNext(); + + this.compactPosLast = vborange; + } + } + + private long toBytes(int vertex) + { + return (long)vertex * (long)this.vertexBytes; + } + + private int toVertex(long bytes) { + return (int)(bytes / (long)this.vertexBytes); + } + + private void checkVboSize(int sizeMin) { + if (this.capacity < sizeMin) this.expandVbo(sizeMin); + } + + private void copyVboData(int posFrom, int posTo, int size) { + long i = this.toBytes(posFrom); + long j = this.toBytes(posTo); + long k = this.toBytes(size); + GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, this.vertexBufferId); + GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, this.vertexBufferId); + GL31.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, i, j, k); + mc.smoothbeta_printOpenGLError("Copy VBO range"); + GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0); + GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0); + } + + private void expandVbo(int sizeMin) { + int i; + + i = this.capacity * 6 / 4; + while (i < sizeMin) i = i * 6 / 4; + + long j = this.toBytes(this.capacity); + long k = this.toBytes(i); + int l = GL15.glGenBuffers(); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, l); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, k, GL15.GL_STATIC_DRAW); + mc.smoothbeta_printOpenGLError("Expand VBO"); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, this.vertexBufferId); + GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, l); + GL31.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L, j); + mc.smoothbeta_printOpenGLError("Copy VBO: " + k); + GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0); + GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0); + GL15.glDeleteBuffers(this.vertexBufferId); + this.bufferIndirect = class_214.method_745(i * 5); + this.vertexBufferId = l; + this.capacity = i; + } + + public void bindVertexArray() { + GL30.glBindVertexArray(this.vertexArrayId); + } + + public void bindBuffer() { + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vertexBufferId); + } + + public void upload(VertexFormat.DrawMode drawMode, Pos range) { + if (this.drawMode != drawMode) { + if (this.bufferIndirect.position() > 0) + throw new IllegalArgumentException("Mixed region draw modes: " + this.drawMode + " != " + drawMode); + + this.drawMode = drawMode; + } + + this.bufferIndirect.put(drawMode.getIndexCount(range.getSize())); + bufferIndirect.put(1); + this.bufferIndirect.put(0); + bufferIndirect.put(range.getPosition()); + bufferIndirect.put(curBaseInstance++); + } + + public void drawAll() { + this.bindVertexArray(); + this.bindBuffer(); + format.setupState(); + + int i = this.drawMode.getIndexCount(this.nextPos); + IndexBuffer autostorageindexbuffer = IndexBuffer.getSequentialBuffer(this.drawMode); + VertexFormat.IndexType indextype = autostorageindexbuffer.getIndexType(); + autostorageindexbuffer.bindAndGrow(i); +// GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, autostorageindexbuffer.getId()); + this.bufferIndirect.flip(); + GL43.glMultiDrawElementsIndirect(this.drawMode.glMode, indextype.glType, this.bufferIndirect, bufferIndirect.limit() / 5, 0); + this.bufferIndirect.limit(this.bufferIndirect.capacity()); + + if (this.nextPos > this.size * 11 / 10) this.compactRanges(); + curBaseInstance = 0; + } + + public void unbindBuffer() { + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + + public static void unbindVertexArray() { + GL30.glBindVertexArray(0); + } + + public void deleteGlBuffers() { + if (this.vertexArrayId >= 0) { + GL30.glDeleteVertexArrays(this.vertexArrayId); + this.vertexArrayId = -1; + } + if (this.vertexBufferId >= 0) { + GlStateManager._glDeleteBuffers(this.vertexBufferId); + this.vertexBufferId = -1; + } + } + + public static class Pos { + private int position = -1; + private int size = 0; + private final LinkedList.Node node = new LinkedList.Node<>(this); + + public int getPosition() { + return this.position; + } + + public int getSize() { + return this.size; + } + + public int getPositionNext() { + return this.position + this.size; + } + + public void setPosition(int position) { + this.position = position; + } + + public void setSize(int size) { + this.size = size; + } + + public LinkedList.Node getNode() { + return this.node; + } + + public Pos getPrev() { + LinkedList.Node node = this.node.getPrev(); + return node == null ? null : node.getItem(); + } + + public Pos getNext() { + LinkedList.Node node = this.node.getNext(); + return node == null ? null : node.getItem(); + } + + public String toString() { + return this.position + "/" + this.size + "/" + (this.position + this.size); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormat.java b/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormat.java new file mode 100644 index 0000000..6fdcc02 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormat.java @@ -0,0 +1,147 @@ +package net.mine_diver.smoothbeta.client.render; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.lwjgl.opengl.GL11; + +import java.util.stream.Collectors; + +@Environment(EnvType.CLIENT) +public class VertexFormat { + private final ImmutableList elements; + private final ImmutableMap elementMap; + private final IntList offsets = new IntArrayList(); + private final int vertexSizeByte; + + public VertexFormat(ImmutableMap elementMap) { + this.elementMap = elementMap; + this.elements = elementMap.values().asList(); + int i = 0; + for (VertexFormatElement vertexFormatElement : elementMap.values()) { + this.offsets.add(i); + i += vertexFormatElement.getByteLength(); + } + this.vertexSizeByte = i; + } + + public String toString() { + return "format: " + this.elementMap.size() + " elements: " + this.elementMap.entrySet().stream().map(Object::toString).collect(Collectors.joining(" ")); + } + + public int getVertexSizeByte() { + return this.vertexSizeByte; + } + + public ImmutableList getElements() { + return this.elements; + } + + public ImmutableList getAttributeNames() { + return this.elementMap.keySet().asList(); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + VertexFormat vertexFormat = (VertexFormat)o; + if (this.vertexSizeByte != vertexFormat.vertexSizeByte) { + return false; + } + return this.elementMap.equals(vertexFormat.elementMap); + } + + public int hashCode() { + return this.elementMap.hashCode(); + } + + /** + * Specifies for OpenGL how the vertex data should be interpreted. + */ + public void setupState() { + this.setupStateInternal(); + } + + private void setupStateInternal() { + int i = this.getVertexSizeByte(); + ImmutableList list = this.getElements(); + for (int j = 0; j < list.size(); ++j) { + list.get(j).setupState(j, this.offsets.getInt(j), i); + } + } + + public void clearState() { + this.clearStateInternal(); + } + + private void clearStateInternal() { + ImmutableList immutableList = this.getElements(); + for (int i = 0; i < immutableList.size(); ++i) { + VertexFormatElement vertexFormatElement = immutableList.get(i); + vertexFormatElement.clearState(i); + } + } + + @Environment(value=EnvType.CLIENT) + public enum DrawMode { + LINES(4, 2, 2, false), + LINE_STRIP(5, 2, 1, true), + DEBUG_LINES(1, 2, 2, false), + DEBUG_LINE_STRIP(3, 2, 1, true), + TRIANGLES(4, 3, 3, false), + TRIANGLE_STRIP(5, 3, 1, true), + TRIANGLE_FAN(6, 3, 1, true), + QUADS(4, 4, 4, false); + + public final int glMode; + public final int firstVertexCount; + public final int additionalVertexCount; + public final boolean shareVertices; + + DrawMode(int glMode, int firstVertexCount, int additionalVertexCount, boolean shareVertices) { + this.glMode = glMode; + this.firstVertexCount = firstVertexCount; + this.additionalVertexCount = additionalVertexCount; + this.shareVertices = shareVertices; + } + + public int getIndexCount(int vertexCount) { + return switch (this) { + case LINE_STRIP, DEBUG_LINES, DEBUG_LINE_STRIP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN -> vertexCount; + case LINES, QUADS -> vertexCount / 4 * 6; + }; + } + } + + @Environment(value=EnvType.CLIENT) + public enum IndexType { + BYTE(GL11.GL_UNSIGNED_BYTE, 1), + SHORT(GL11.GL_UNSIGNED_SHORT, 2), + INT(GL11.GL_UNSIGNED_INT, 4); + + public final int glType; + public final int size; + + IndexType(int glType, int size) { + this.glType = glType; + this.size = size; + } + + public static IndexType smallestFor(int indexCount) { + if ((indexCount & 0xFFFF0000) != 0) { + return INT; + } + if ((indexCount & 0xFF00) != 0) { + return SHORT; + } + return BYTE; + } + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormatElement.java b/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormatElement.java new file mode 100644 index 0000000..aced3d2 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormatElement.java @@ -0,0 +1,170 @@ +package net.mine_diver.smoothbeta.client.render; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; + +/** + * Represents a singular field within a larger {@link + * VertexFormat vertex format}. + * + *

This element comprises a component type, the number of components, + * and a type that describes how the components should be interpreted. + */ +@Environment(EnvType.CLIENT) +public class VertexFormatElement { + private final ComponentType componentType; + private final Type type; + private final int uvIndex; + private final int componentCount; + /** + * The total length of this element (in bytes). + */ + private final int byteLength; + + public VertexFormatElement(int uvIndex, ComponentType componentType, Type type, int componentCount) { + if (!this.isValidType(uvIndex, type)) + throw new IllegalStateException("Multiple vertex elements of the same type other than UVs are not supported"); + this.type = type; + this.componentType = componentType; + this.uvIndex = uvIndex; + this.componentCount = componentCount; + this.byteLength = componentType.getByteLength() * this.componentCount; + } + + private boolean isValidType(int uvIndex, Type type) { + return uvIndex == 0 || type == Type.UV; + } + + public String toString() { + return this.componentCount + "," + this.type.getName() + "," + this.componentType.getName(); + } + + public final int getByteLength() { + return this.byteLength; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || this.getClass() != o.getClass()) return false; + VertexFormatElement vertexFormatElement = (VertexFormatElement)o; + if (this.componentCount != vertexFormatElement.componentCount) return false; + if (this.uvIndex != vertexFormatElement.uvIndex) return false; + if (this.componentType != vertexFormatElement.componentType) return false; + return this.type == vertexFormatElement.type; + } + + public int hashCode() { + int i = this.componentType.hashCode(); + i = 31 * i + this.type.hashCode(); + i = 31 * i + this.uvIndex; + i = 31 * i + this.componentCount; + return i; + } + + /** + * Specifies for OpenGL how the vertex data corresponding to this element + * should be interpreted. + * + * @param elementIndex the index of the element in a vertex format + * @param offset the distance between the start of the buffer and the first instance of + * the element in the buffer + * @param stride the distance between consecutive instances of the element in the buffer + */ + public void setupState(int elementIndex, long offset, int stride) { + this.type.setupState(this.componentCount, this.componentType.getGlType(), stride, offset, this.uvIndex, elementIndex); + } + + public void clearState(int elementIndex) { + this.type.clearState(this.uvIndex, elementIndex); + } + + @Environment(value=EnvType.CLIENT) + public enum Type { + POSITION("Position", (componentCount, componentType, stride, offset, uvIndex, elementIndex) -> { + + GL20.glEnableVertexAttribArray(elementIndex); + GL20.glVertexAttribPointer(elementIndex, componentCount, componentType, false, stride, offset); + }, (uvIndex, elementIndex) -> GL20.glDisableVertexAttribArray(elementIndex)), + NORMAL("Normal", (componentCount, componentType, stride, offset, uvIndex, elementIndex) -> { + GL20.glEnableVertexAttribArray(elementIndex); + GL20.glVertexAttribPointer(elementIndex, componentCount, componentType, true, stride, offset); + }, (uvIndex, elementIndex) -> GL20.glDisableVertexAttribArray(elementIndex)), + COLOR("Vertex Color", (componentCount, componentType, stride, offset, uvIndex, elementIndex) -> { + GL20.glEnableVertexAttribArray(elementIndex); + GL20.glVertexAttribPointer(elementIndex, componentCount, componentType, true, stride, offset); + }, (uvIndex, elementIndex) -> GL20.glDisableVertexAttribArray(elementIndex)), + UV("UV", (componentCount, componentType, stride, offset, uvIndex, elementIndex) -> { + GL20.glEnableVertexAttribArray(elementIndex); + if (componentType == GL11.GL_FLOAT) + GL20.glVertexAttribPointer(elementIndex, componentCount, componentType, false, stride, offset); + else GL30.glVertexAttribIPointer(elementIndex, componentCount, componentType, stride, offset); + }, (uvIndex, elementIndex) -> GL20.glDisableVertexAttribArray(elementIndex)), + PADDING("Padding", (componentCount, componentType, stride, offset, uvIndex, elementIndex) -> {}, (uvIndex, elementIndex) -> {}); + + private final String name; + private final SetupTask setupTask; + private final ClearTask clearTask; + + Type(String name, SetupTask setupTask, ClearTask clearTask) { + this.name = name; + this.setupTask = setupTask; + this.clearTask = clearTask; + } + + void setupState(int componentCount, int componentType, int stride, long offset, int uvIndex, int elementIndex) { + this.setupTask.setupBufferState(componentCount, componentType, stride, offset, uvIndex, elementIndex); + } + + public void clearState(int uvIndex, int elementIndex) { + this.clearTask.clearBufferState(uvIndex, elementIndex); + } + + public String getName() { + return this.name; + } + + @FunctionalInterface + @Environment(value=EnvType.CLIENT) + interface SetupTask { + void setupBufferState(int var1, int var2, int var3, long var4, int var6, int var7); + } + + @FunctionalInterface + @Environment(value=EnvType.CLIENT) + interface ClearTask { + void clearBufferState(int var1, int var2); + } + } + + @Environment(value=EnvType.CLIENT) + public enum ComponentType { + FLOAT(4, "Float", GL11.GL_FLOAT), + UBYTE(1, "Unsigned Byte", GL11.GL_UNSIGNED_BYTE), + BYTE(1, "Byte", GL11.GL_BYTE); + + private final int byteLength; + private final String name; + private final int glType; + + ComponentType(int byteLength, String name, int glType) { + this.byteLength = byteLength; + this.name = name; + this.glType = glType; + } + + public int getByteLength() { + return this.byteLength; + } + + public String getName() { + return this.name; + } + + public int getGlType() { + return this.glType; + } + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormats.java b/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormats.java new file mode 100644 index 0000000..ad7ea25 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormats.java @@ -0,0 +1,15 @@ +package net.mine_diver.smoothbeta.client.render; + +import com.google.common.collect.ImmutableMap; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public class VertexFormats { + public static final VertexFormatElement POSITION_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.FLOAT, VertexFormatElement.Type.POSITION, 3); + public static final VertexFormatElement COLOR_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.UBYTE, VertexFormatElement.Type.COLOR, 4); + public static final VertexFormatElement TEXTURE_0_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.FLOAT, VertexFormatElement.Type.UV, 2); + public static final VertexFormatElement NORMAL_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.BYTE, VertexFormatElement.Type.NORMAL, 3); + public static final VertexFormatElement PADDING_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.BYTE, VertexFormatElement.Type.PADDING, 1); + public static final VertexFormat POSITION_TEXTURE_COLOR_NORMAL = new VertexFormat(ImmutableMap.builder().put("Position", POSITION_ELEMENT).put("UV0", TEXTURE_0_ELEMENT).put("Color", COLOR_ELEMENT).put("Normal", NORMAL_ELEMENT).put("Padding", PADDING_ELEMENT).build()); +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GLImportProcessor.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GLImportProcessor.java new file mode 100644 index 0000000..b3d53f3 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GLImportProcessor.java @@ -0,0 +1,157 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.modificationstation.stationapi.api.util.FileNameUtil; +import net.modificationstation.stationapi.api.util.StringHelper; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Handles the flattening of "moj_" import strings in the loaded GLSL shader file. + * Instances of an import are replaced by the contents of the referenced file + * prefixed by a comment describing the line position and original file location + * of the import. + */ +@Environment(EnvType.CLIENT) +public abstract class GLImportProcessor { + private static final String MULTI_LINE_COMMENT_PATTERN = "/\\*(?:[^*]|\\*+[^*/])*\\*+/"; + private static final String SINGLE_LINE_COMMENT_PATTERN = "//\\V*"; + private static final Pattern MOJ_IMPORT_PATTERN = Pattern.compile("(#(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*moj_import(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*(?:\"(.*)\"|<(.*)>))"); + private static final Pattern IMPORT_VERSION_PATTERN = Pattern.compile("(#(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*version(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*(\\d+))\\b"); + private static final Pattern TRAILING_WHITESPACE_PATTERN = Pattern.compile("(?:^|\\v)(?:\\s|" + MULTI_LINE_COMMENT_PATTERN + "|(" + SINGLE_LINE_COMMENT_PATTERN + "))*\\z"); + + /** + * Reads the source code supplied into a list of lines suitable for uploading to + * the GL Shader cache. + * + *

Imports are processed as per the description of this class. + */ + public List readSource(String source) { + GLImportProcessor.Context context = new GLImportProcessor.Context(); + List list = this.parseImports(source, context, ""); + list.set(0, this.readImport(list.get(0), context.column)); + return list; + } + + private List parseImports(String source, GLImportProcessor.Context context, String path) { + int i = context.line; + int j = 0; + String string = ""; + List list = Lists.newArrayList(); + Matcher matcher = MOJ_IMPORT_PATTERN.matcher(source); + + String string2; + while(matcher.find()) { + if (method_36424(source, matcher, j)) { + string2 = matcher.group(2); + boolean bl = string2 != null; + if (!bl) { + string2 = matcher.group(3); + } + + if (string2 != null) { + String string3 = source.substring(j, matcher.start(1)); + String string4 = path + string2; + String string5 = this.loadImport(bl, string4); + int k; + if (!Strings.isNullOrEmpty(string5)) { + if (!StringHelper.endsWithLineBreak(string5)) { + string5 = string5 + System.lineSeparator(); + } + + ++context.line; + k = context.line; + List list2 = this.parseImports(string5, context, bl ? FileNameUtil.getPosixFullPath(string4) : ""); + list2.set(0, String.format(Locale.ROOT, "#line %d %d\n%s", 0, k, this.extractVersion(list2.get(0), context))); + if (!StringUtils.isBlank(string3)) { + list.add(string3); + } + + list.addAll(list2); + } else { + String string6 = bl ? String.format("/*#moj_import \"%s\"*/", string2) : String.format("/*#moj_import <%s>*/", string2); + list.add(string + string3 + string6); + } + + k = StringHelper.countLines(source.substring(0, matcher.end(1))); + string = String.format(Locale.ROOT, "#line %d %d", k, i); + j = matcher.end(1); + } + } + } + + string2 = source.substring(j); + if (!StringUtils.isBlank(string2)) { + list.add(string + string2); + } + + return list; + } + + /** + * Converts a line known to contain an import into a fully-qualified + * version of itself for insertion as a comment. + */ + private String extractVersion(String line, GLImportProcessor.Context context) { + Matcher matcher = IMPORT_VERSION_PATTERN.matcher(line); + if (matcher.find() && method_36423(line, matcher)) { + context.column = Math.max(context.column, Integer.parseInt(matcher.group(2))); + String var10000 = line.substring(0, matcher.start(1)); + return var10000 + "/*" + line.substring(matcher.start(1), matcher.end(1)) + "*/" + line.substring(matcher.end(1)); + } else { + return line; + } + } + + private String readImport(String line, int start) { + Matcher matcher = IMPORT_VERSION_PATTERN.matcher(line); + if (matcher.find() && method_36423(line, matcher)) { + String var10000 = line.substring(0, matcher.start(2)); + return var10000 + Math.max(start, Integer.parseInt(matcher.group(2))) + line.substring(matcher.end(2)); + } else { + return line; + } + } + + private static boolean method_36423(String string, Matcher matcher) { + return method_36424(string, matcher, 0); + } + + private static boolean method_36424(String string, Matcher matcher, int i) { + int j = matcher.start() - i; + if (j == 0) { + return true; + } else { + Matcher matcher2 = TRAILING_WHITESPACE_PATTERN.matcher(string.substring(i, matcher.start())); + if (!matcher2.find()) { + return false; + } else { + int k = matcher2.end(1); + return k != matcher.start(); + } + } + } + + /** + * Called to load an import reference's source code. + */ + @Nullable + public abstract String loadImport(boolean inline, String name); + + /** + * A context for the parser to keep track of its current line and caret position in the file. + */ + @Environment(EnvType.CLIENT) + static final class Context { + int column; + int line; + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlBlendState.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlBlendState.java new file mode 100644 index 0000000..f250e4d --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlBlendState.java @@ -0,0 +1,122 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL14; + +import java.util.Locale; + +@Environment(EnvType.CLIENT) +public class GlBlendState { + @Nullable + private static GlBlendState activeBlendState; + private final int srcRgb; + private final int srcAlpha; + private final int dstRgb; + private final int dstAlpha; + private final int func; + private final boolean separateBlend; + private final boolean blendDisabled; + + private GlBlendState(boolean separateBlend, boolean blendDisabled, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha, int func) { + this.separateBlend = separateBlend; + this.srcRgb = srcRgb; + this.dstRgb = dstRgb; + this.srcAlpha = srcAlpha; + this.dstAlpha = dstAlpha; + this.blendDisabled = blendDisabled; + this.func = func; + } + + public GlBlendState() { + this(false, true, 1, 0, 1, 0, GL14.GL_FUNC_ADD); + } + + public GlBlendState(int srcRgb, int dstRgb, int func) { + this(false, false, srcRgb, dstRgb, srcRgb, dstRgb, func); + } + + public GlBlendState(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha, int func) { + this(true, false, srcRgb, dstRgb, srcAlpha, dstAlpha, func); + } + + public void enable() { + if (!this.equals(activeBlendState)) { + if (activeBlendState == null || this.blendDisabled != activeBlendState.isBlendDisabled()) { + activeBlendState = this; + if (this.blendDisabled) { + GlStateManager._disableBlend(); + return; + } + + GlStateManager._enableBlend(); + } + + GL14.glBlendEquation(this.func); + if (this.separateBlend) + GlStateManager._blendFuncSeparate(this.srcRgb, this.dstRgb, this.srcAlpha, this.dstAlpha); + else GlStateManager._blendFunc(this.srcRgb, this.dstRgb); + + } + } + + public boolean equals(Object o) { + if (this == o) return true; + else if (!(o instanceof GlBlendState glBlendState)) return false; + else if (this.func != glBlendState.func) return false; + else if (this.dstAlpha != glBlendState.dstAlpha) return false; + else if (this.dstRgb != glBlendState.dstRgb) return false; + else if (this.blendDisabled != glBlendState.blendDisabled) return false; + else if (this.separateBlend != glBlendState.separateBlend) return false; + else if (this.srcAlpha != glBlendState.srcAlpha) return false; + else return this.srcRgb == glBlendState.srcRgb; + } + + public int hashCode() { + int i = this.srcRgb; + i = 31 * i + this.srcAlpha; + i = 31 * i + this.dstRgb; + i = 31 * i + this.dstAlpha; + i = 31 * i + this.func; + i = 31 * i + (this.separateBlend ? 1 : 0); + i = 31 * i + (this.blendDisabled ? 1 : 0); + return i; + } + + public boolean isBlendDisabled() { + return this.blendDisabled; + } + + public static int getFuncFromString(String name) { + String string = name.trim().toLowerCase(Locale.ROOT); + return switch (string) { + case "add" -> GL14.GL_FUNC_ADD; + case "subtract" -> GL14.GL_FUNC_SUBTRACT; + case "reversesubtract", "reverse_subtract" -> GL14.GL_FUNC_REVERSE_SUBTRACT; + case "min" -> GL14.GL_MIN; + default -> "max".equals(string) ? GL14.GL_MAX : GL14.GL_FUNC_ADD; + }; + } + + public static int getComponentFromString(String expression) { + String string = expression.trim().toLowerCase(Locale.ROOT); + string = string.replaceAll("_", ""); + string = string.replaceAll("one", "1"); + string = string.replaceAll("zero", "0"); + string = string.replaceAll("minus", "-"); + return switch (string) { + case "0" -> 0; + case "1" -> 1; + case "srccolor" -> GL11.GL_SRC_COLOR; + case "1-srccolor" -> GL11.GL_ONE_MINUS_SRC_COLOR; + case "dstcolor" -> GL11.GL_DST_COLOR; + case "1-dstcolor" -> GL11.GL_ONE_MINUS_DST_COLOR; + case "srcalpha" -> GL11.GL_SRC_ALPHA; + case "1-srcalpha" -> GL11.GL_ONE_MINUS_SRC_ALPHA; + case "dstalpha" -> GL11.GL_DST_ALPHA; + default -> "1-dstalpha".equals(string) ? GL11.GL_ONE_MINUS_DST_ALPHA : -1; + }; + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlProgramManager.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlProgramManager.java new file mode 100644 index 0000000..f46181e --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlProgramManager.java @@ -0,0 +1,39 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.lwjgl.opengl.GL20; + +import java.io.IOException; + +import static net.modificationstation.stationapi.impl.client.texture.StationRenderImpl.LOGGER; + +@Environment(EnvType.CLIENT) +public class GlProgramManager { + public static void useProgram(int program) { + GL20.glUseProgram(program); + } + + public static void deleteProgram(GlShader shader) { + shader.getFragmentShader().release(); + shader.getVertexShader().release(); + GL20.glDeleteProgram(shader.getProgramRef()); + } + + public static int createProgram() throws IOException { + int i = GL20.glCreateProgram(); + if (i <= 0) throw new IOException("Could not create shader program (returned program ID " + i + ")"); + else return i; + } + + public static void linkProgram(GlShader shader) { + shader.attachReferencedShaders(); + GL20.glLinkProgram(shader.getProgramRef()); + int i = GL20.glGetProgrami(shader.getProgramRef(), GL20.GL_LINK_STATUS); + if (i == 0) { + LOGGER.warn("Error encountered when linking program containing VS {} and FS {}. Log output:", shader.getVertexShader().getName(), shader.getFragmentShader().getName()); + LOGGER.warn(GL20.glGetProgramInfoLog(shader.getProgramRef(), 0x8000)); + } + + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlShader.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlShader.java new file mode 100644 index 0000000..6373fdb --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlShader.java @@ -0,0 +1,15 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public interface GlShader { + int getProgramRef(); + + Program getVertexShader(); + + Program getFragmentShader(); + + void attachReferencedShaders(); +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlStateManager.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlStateManager.java new file mode 100644 index 0000000..330ab06 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlStateManager.java @@ -0,0 +1,137 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import com.google.common.base.Charsets; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.class_214; +import net.modificationstation.stationapi.api.util.Util; +import org.lwjgl.opengl.*; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.stream.IntStream; + +public class GlStateManager { + private static final boolean ON_LINUX = Util.getOperatingSystem() == Util.OperatingSystem.LINUX; + private static final BlendFuncState BLEND = new BlendFuncState(); + private static int activeTexture; + private static final Texture2DState[] TEXTURES = IntStream.range(0, 12).mapToObj(i -> new Texture2DState()).toArray(Texture2DState[]::new); + + public static void _glDeleteBuffers(int buffer) { + if (ON_LINUX) { + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, 0L, GL15.GL_DYNAMIC_DRAW); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + GL15.glDeleteBuffers(buffer); + } + + public static void glShaderSource(int shader, List strings) { + StringBuilder stringBuilder = new StringBuilder(); + for (String string : strings) { + stringBuilder.append(string); + } + byte[] bs = stringBuilder.toString().getBytes(Charsets.UTF_8); + ByteBuffer byteBuffer = class_214.method_744(bs.length + 1); + byteBuffer.put(bs); + byteBuffer.put((byte)0); + byteBuffer.flip(); + GL20.glShaderSource(shader, byteBuffer); + } + + public static void _disableBlend() { + GlStateManager.BLEND.capState.disable(); + } + + public static void _enableBlend() { + GlStateManager.BLEND.capState.enable(); + } + + public static void _blendFunc(int srcFactor, int dstFactor) { + if (srcFactor != GlStateManager.BLEND.srcFactorRGB || dstFactor != GlStateManager.BLEND.dstFactorRGB) { + GlStateManager.BLEND.srcFactorRGB = srcFactor; + GlStateManager.BLEND.dstFactorRGB = dstFactor; + GL11.glBlendFunc(srcFactor, dstFactor); + } + } + + public static void _blendFuncSeparate(int srcFactorRGB, int dstFactorRGB, int srcFactorAlpha, int dstFactorAlpha) { + if (srcFactorRGB != GlStateManager.BLEND.srcFactorRGB || dstFactorRGB != GlStateManager.BLEND.dstFactorRGB || srcFactorAlpha != GlStateManager.BLEND.srcFactorAlpha || dstFactorAlpha != GlStateManager.BLEND.dstFactorAlpha) { + GlStateManager.BLEND.srcFactorRGB = srcFactorRGB; + GlStateManager.BLEND.dstFactorRGB = dstFactorRGB; + GlStateManager.BLEND.srcFactorAlpha = srcFactorAlpha; + GlStateManager.BLEND.dstFactorAlpha = dstFactorAlpha; + GL14.glBlendFuncSeparate(srcFactorRGB, dstFactorRGB, srcFactorAlpha, dstFactorAlpha); + } + } + + public static void _bindTexture(int texture) { + if (texture != GlStateManager.TEXTURES[GlStateManager.activeTexture].boundTexture) { + GlStateManager.TEXTURES[GlStateManager.activeTexture].boundTexture = texture; + GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture); + } + } + + public static int _getActiveTexture() { + return activeTexture + GL13.GL_TEXTURE0; + } + + public static void _activeTexture(int texture) { + if (activeTexture != texture - GL13.GL_TEXTURE0) { + activeTexture = texture - GL13.GL_TEXTURE0; + GL13.glActiveTexture(texture); + } + } + + public static void _enableTexture() { + GlStateManager.TEXTURES[GlStateManager.activeTexture].capState = true; + } + + @Environment(EnvType.CLIENT) + static class CapabilityTracker { + private final int cap; + private boolean state; + + public CapabilityTracker(int cap) { + this.cap = cap; + } + + public void disable() { + this.setState(false); + } + + public void enable() { + this.setState(true); + } + + public void setState(boolean state) { + if (state != this.state) { + this.state = state; + if (state) { + GL11.glEnable(this.cap); + } else { + GL11.glDisable(this.cap); + } + } + } + } + + @Environment(EnvType.CLIENT) + static class BlendFuncState { + public final CapabilityTracker capState = new CapabilityTracker(GL11.GL_BLEND); + public int srcFactorRGB = 1; + public int dstFactorRGB = 0; + public int srcFactorAlpha = 1; + public int dstFactorAlpha = 0; + + BlendFuncState() {} + } + + @Environment(EnvType.CLIENT) + static class Texture2DState { + public boolean capState; + public int boundTexture; + + Texture2DState() {} + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlUniform.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlUniform.java new file mode 100644 index 0000000..f6b4ccb --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlUniform.java @@ -0,0 +1,205 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.class_214; +import net.modificationstation.stationapi.api.util.math.Vec3f; +import org.lwjgl.opengl.GL20; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import static net.modificationstation.stationapi.impl.client.texture.StationRenderImpl.LOGGER; + +@Environment(EnvType.CLIENT) +public class GlUniform extends Uniform implements AutoCloseable { + public static final int INT1 = 0; + public static final int INT2 = 1; + public static final int INT3 = 2; + public static final int INT4 = 3; + public static final int FLOAT1 = 4; + public static final int FLOAT2 = 5; + public static final int FLOAT3 = 6; + public static final int FLOAT4 = 7; + public static final int MAT2X2 = 8; + public static final int MAT3X3 = 9; + public static final int MAT4X4 = 10; + private static final boolean TRANSPOSE = false; + private int location; + private final int count; + private final int dataType; + private final IntBuffer intData; + private final FloatBuffer floatData; + private final String name; + + public GlUniform(String name, int dataType, int count) { + this.name = name; + this.count = count; + this.dataType = dataType; + if (dataType <= INT4) { + this.intData = class_214.method_745(count); + this.floatData = null; + } else { + this.intData = null; + this.floatData = class_214.method_746(count); + } + + this.location = -1; + this.markStateDirty(); + } + + public static int getUniformLocation(int program, CharSequence name) { + return GL20.glGetUniformLocation(program, name); + } + + public static void uniform1(int location, int value) { + GL20.glUniform1i(location, value); + } + + public static void bindAttribLocation(int program, int index, CharSequence name) { + GL20.glBindAttribLocation(program, index, name); + } + + public void close() {} + + private void markStateDirty() {} + + public static int getTypeIndex(String typeName) { + int i = -1; + if ("int".equals(typeName)) i = INT1; + else if ("float".equals(typeName)) i = FLOAT1; + else if (typeName.startsWith("matrix")) if (typeName.endsWith("2x2")) i = MAT2X2; + else if (typeName.endsWith("3x3")) i = MAT3X3; + else if (typeName.endsWith("4x4")) i = MAT4X4; + + return i; + } + + public void setLocation(int location) { + this.location = location; + } + + public String getName() { + return this.name; + } + + public final void set(float value1) { + this.floatData.position(0); + this.floatData.put(0, value1); + this.markStateDirty(); + } + + public final void set(float value1, float value2, float value3) { + this.floatData.position(0); + this.floatData.put(0, value1); + this.floatData.put(1, value2); + this.floatData.put(2, value3); + this.markStateDirty(); + } + + public final void set(Vec3f vector) { + this.floatData.position(0); + this.floatData.put(0, vector.getX()); + this.floatData.put(1, vector.getY()); + this.floatData.put(2, vector.getZ()); + this.markStateDirty(); + } + + public final void setForDataType(float value1, float value2, float value3, float value4) { + this.floatData.position(0); + if (this.dataType >= FLOAT1) this.floatData.put(0, value1); + + if (this.dataType >= FLOAT2) this.floatData.put(1, value2); + + if (this.dataType >= FLOAT3) this.floatData.put(2, value3); + + if (this.dataType >= FLOAT4) this.floatData.put(3, value4); + + this.markStateDirty(); + } + + public final void setForDataType(int value1, int value2, int value3, int value4) { + this.intData.position(0); + if (this.dataType >= INT1) this.intData.put(0, value1); + + if (this.dataType >= INT2) this.intData.put(1, value2); + + if (this.dataType >= INT3) this.intData.put(2, value3); + + if (this.dataType >= INT4) this.intData.put(3, value4); + + this.markStateDirty(); + } + + public final void set(int value) { + this.intData.position(0); + this.intData.put(0, value); + this.markStateDirty(); + } + + public final void set(float[] values) { + if (values.length < this.count) + LOGGER.warn("Uniform.set called with a too-small value array (expected {}, got {}). Ignoring.", this.count, values.length); + else { + this.floatData.position(0); + this.floatData.put(values); + this.floatData.position(0); + this.markStateDirty(); + } + } + + public final void set(FloatBuffer matrix) { + floatData.position(0); + floatData.put(matrix); + markStateDirty(); + } + + public void upload() { + if (this.dataType <= INT4) this.uploadInts(); + else if (this.dataType <= FLOAT4) this.uploadFloats(); + else { + if (this.dataType > MAT4X4) { + LOGGER.warn("Uniform.upload called, but type value ({}) is not a valid type. Ignoring.", this.dataType); + return; + } + + this.uploadMatrix(); + } + } + + private void uploadInts() { + this.intData.rewind(); + switch (this.dataType) { + case INT1 -> GL20.glUniform1(this.location, this.intData); + case INT2 -> GL20.glUniform2(this.location, this.intData); + case INT3 -> GL20.glUniform3(this.location, this.intData); + case INT4 -> GL20.glUniform4(this.location, this.intData); + default -> LOGGER.warn("Uniform.upload called, but count value ({}) is not in the range of 1 to 4. Ignoring.", this.count); + } + } + + private void uploadFloats() { + this.floatData.rewind(); + switch (this.dataType) { + case FLOAT1 -> GL20.glUniform1(this.location, this.floatData); + case FLOAT2 -> GL20.glUniform2(this.location, this.floatData); + case FLOAT3 -> GL20.glUniform3(this.location, this.floatData); + case FLOAT4 -> GL20.glUniform4(this.location, this.floatData); + default -> LOGGER.warn("Uniform.upload called, but count value ({}) is not in the range of 1 to 4. Ignoring.", this.count); + } + } + + private void uploadMatrix() { + this.floatData.clear(); + switch (this.dataType) { + case MAT2X2 -> GL20.glUniformMatrix2(this.location, TRANSPOSE, this.floatData); + case MAT3X3 -> GL20.glUniformMatrix3(this.location, TRANSPOSE, this.floatData); + case MAT4X4 -> GL20.glUniformMatrix4(this.location, TRANSPOSE, this.floatData); + } + + } + + public int getCount() { + return this.count; + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/Program.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/Program.java new file mode 100644 index 0000000..884e211 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/Program.java @@ -0,0 +1,104 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import com.google.common.collect.Maps; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.modificationstation.stationapi.api.client.texture.TextureUtil; +import org.apache.commons.lang3.StringUtils; +import org.lwjgl.opengl.GL20; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +@Environment(EnvType.CLIENT) +public class Program { + + private static final int MAX_LOG_LENGTH = 0x8000; + private final Program.Type shaderType; + private final String name; + private int shaderRef; + + protected Program(Program.Type shaderType, int shaderRef, String name) { + this.shaderType = shaderType; + this.shaderRef = shaderRef; + this.name = name; + } + + public void attachTo(GlShader program) { + GL20.glAttachShader(program.getProgramRef(), this.getShaderRef()); + } + + public void release() { + if (this.shaderRef != -1) { + GL20.glDeleteShader(this.shaderRef); + this.shaderRef = -1; + this.shaderType.getProgramCache().remove(this.name); + } + } + + public String getName() { + return this.name; + } + + public static Program createFromResource(Program.Type type, String name, InputStream stream, String domain, GLImportProcessor loader) throws IOException { + int i = loadProgram(type, name, stream, domain, loader); + Program program = new Program(type, i, name); + type.getProgramCache().put(name, program); + return program; + } + + protected static int loadProgram(Program.Type type, String name, InputStream stream, String domain, GLImportProcessor loader) throws IOException { + String string = TextureUtil.readResourceAsString(stream); + if (string == null) throw new IOException("Could not load program " + type.getName()); + else { + int i = GL20.glCreateShader(type.getGlType()); + GlStateManager.glShaderSource(i, loader.readSource(string)); + GL20.glCompileShader(i); + if (GL20.glGetShaderi(i, GL20.GL_COMPILE_STATUS) == 0) { + String string2 = StringUtils.trim(GL20.glGetShaderInfoLog(i, MAX_LOG_LENGTH)); + throw new IOException("Couldn't compile " + type.getName() + " program (" + domain + ", " + name + ") : " + string2); + } else return i; + } + } + + protected int getShaderRef() { + return this.shaderRef; + } + + @Environment(EnvType.CLIENT) + public enum Type { + VERTEX("vertex", ".vsh", GL20.GL_VERTEX_SHADER), + FRAGMENT("fragment", ".fsh", GL20.GL_FRAGMENT_SHADER); + + private final String name; + private final String fileExtension; + private final int glType; + private final Map programCache = Maps.newHashMap(); + + Type(String name, String extension, int glType) { + this.name = name; + this.fileExtension = extension; + this.glType = glType; + } + + public String getName() { + return this.name; + } + + public String getFileExtension() { + return this.fileExtension; + } + + int getGlType() { + return this.glType; + } + + /** + * Gets a map of loaded shaders. + */ + public Map getProgramCache() { + return this.programCache; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/ShaderParseException.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/ShaderParseException.java new file mode 100644 index 0000000..e398eca --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/ShaderParseException.java @@ -0,0 +1,80 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import com.google.common.collect.Lists; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; + +public class ShaderParseException extends IOException { + private final List traces = Lists.newArrayList(); + private final String message; + + public ShaderParseException(String message) { + this.traces.add(new ShaderParseException.JsonStackTrace()); + this.message = message; + } + + public ShaderParseException(String message, Throwable cause) { + super(cause); + this.traces.add(new ShaderParseException.JsonStackTrace()); + this.message = message; + } + + public void addFaultyElement(String jsonKey) { + this.traces.get(0).add(jsonKey); + } + + public void addFaultyFile(String path) { + this.traces.get(0).fileName = path; + this.traces.add(0, new ShaderParseException.JsonStackTrace()); + } + + public String getMessage() { + return "Invalid " + this.traces.get(this.traces.size() - 1) + ": " + this.message; + } + + public static ShaderParseException wrap(Exception cause) { + if (cause instanceof ShaderParseException) { + return (ShaderParseException)cause; + } else { + String string = cause.getMessage(); + if (cause instanceof FileNotFoundException) { + string = "File not found"; + } + + return new ShaderParseException(string, cause); + } + } + + public static class JsonStackTrace { + @Nullable + String fileName; + private final List faultyElements = Lists.newArrayList(); + + JsonStackTrace() {} + + void add(String element) { + this.faultyElements.add(0, element); + } + + public String joinStackTrace() { + return StringUtils.join(this.faultyElements, "->"); + } + + public String toString() { + if (this.fileName != null) { + if (this.faultyElements.isEmpty()) { + return this.fileName; + } else { + String var10000 = this.fileName; + return var10000 + " " + this.joinStackTrace(); + } + } else { + return this.faultyElements.isEmpty() ? "(Unknown file)" : "(Unknown file) " + this.joinStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/Uniform.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/Uniform.java new file mode 100644 index 0000000..f185a8f --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/Uniform.java @@ -0,0 +1,22 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.modificationstation.stationapi.api.util.math.Vec3f; + +@Environment(EnvType.CLIENT) +public class Uniform { + public void set(float value1) {} + + public void set(float value1, float value2, float value3) {} + + public void setForDataType(float value1, float value2, float value3, float value4) {} + + public void setForDataType(int value1, int value2, int value3, int value4) {} + + public void set(int value) {} + + public void set(float[] values) {} + + public void set(Vec3f vector) {} +} \ No newline at end of file diff --git a/src/main/java/net/mine_diver/smoothbeta/client/render/gl/VertexBuffer.java b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/VertexBuffer.java new file mode 100644 index 0000000..fa1cae8 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/client/render/gl/VertexBuffer.java @@ -0,0 +1,31 @@ +package net.mine_diver.smoothbeta.client.render.gl; + +import net.mine_diver.smoothbeta.client.render.VboPool; +import net.mine_diver.smoothbeta.client.render.VertexFormat; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL30; + +import java.nio.ByteBuffer; + +public class VertexBuffer { + public static void unbind() { + GL30.glBindVertexArray(0); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + private final VboPool pool; + private final VboPool.Pos poolPos = new VboPool.Pos(); + + public VertexBuffer(VboPool pool) { + this.pool = pool; + } + + public void upload(ByteBuffer buffer) { + pool.bufferData(buffer, poolPos); + } + + public void uploadToPool() { + pool.upload(VertexFormat.DrawMode.QUADS, poolPos); + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/MixinBiomeSource.java b/src/main/java/net/mine_diver/smoothbeta/mixin/MixinBiomeSource.java deleted file mode 100644 index bee97c2..0000000 --- a/src/main/java/net/mine_diver/smoothbeta/mixin/MixinBiomeSource.java +++ /dev/null @@ -1,151 +0,0 @@ -package net.mine_diver.smoothbeta.mixin; - -import net.mine_diver.smoothbeta.util.ChunkCustomAccessor; -import net.minecraft.level.Level; -import net.minecraft.level.biome.Biome; -import net.minecraft.level.gen.BiomeSource; -import net.minecraft.util.maths.MathHelper; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(BiomeSource.class) -public class MixinBiomeSource { - - @Shadow public double[] temperatureNoises; - @Shadow public double[] rainfallNoises; - @Shadow public double[] detailNoises; - - @Unique - private Level level; - - @Inject( - method = "(Lnet/minecraft/level/Level;)V", - at = @At("RETURN") - ) - private void captureLevel(Level level, CallbackInfo ci) { - this.level = level; - } - - @Inject( - method = "getTemperature(II)D", - at = @At("HEAD"), - cancellable = true - ) - private void getTemperature(int x, int z, CallbackInfoReturnable cir) { - if (level.getCache() != null && level.getCache().isChunkLoaded(x >> 4, z >> 4)) - cir.setReturnValue(temperatureNoises[0] = ((ChunkCustomAccessor) level.getChunk(x, z)).getTemperature()[((x & 15) << 4) + (z & 15)]); - } - - @Inject( - method = "getTemperatures([DIIII)[D", - at = @At("HEAD"), - cancellable = true - ) - private void getTemperatures(double[] temperatures, int x, int z, int xSize, int zSize, CallbackInfoReturnable cir) { - if (level.getCache() != null && level.getCache().isChunkLoaded(x >> 4, z >> 4)) { - if (temperatures == null || temperatures.length < xSize * zSize) - temperatures = new double[xSize * zSize]; - if (detailNoises == null || detailNoises.length < xSize * zSize) - detailNoises = new double[xSize * zSize]; - ChunkCustomAccessor chunk = null; - int cCoords; - if (xSize == 1 && zSize == 1) { - chunk = (ChunkCustomAccessor) level.getChunk(x, z); - cCoords = ((x & 15) << 4) + (z & 15); - temperatureNoises[0] = chunk.getTemperature()[cCoords]; - detailNoises[0] = chunk.getDetail()[cCoords]; - } else { - int xChunk = x / 16; - int xChunkEnd = (int) Math.ceil((x + xSize) / 16D); - int xChunkSize = xChunkEnd - xChunk; - -// double[] cTemperature = null; -// double[] cDetail = null; -// int counter = 0; -// for (int xd = 0; xd < xSize; xd++) { -// if (((x + xd) & 15) == 0 || chunk == null) { -// chunk = (ChunkCustomAccessor) level.getChunk(x + xd, z); -// cTemperature = chunk.getTemperature(); -// cDetail = chunk.getDetail(); -// } -// for (int zd = 0; zd < zSize; zd++) { -// if (((z + zd) & 15) == 0) { -// chunk = (ChunkCustomAccessor) level.getChunk(x + xd, z + zd); -// cTemperature = chunk.getTemperature(); -// cDetail = chunk.getDetail(); -// } -// cCoords = (((x + xd) & 15) << 4) + ((z + zd) & 15); -// temperatures[counter] = cTemperature[cCoords]; -// detailNoises[counter] = cDetail[cCoords]; -// counter++; -// } -// } - } - cir.setReturnValue(temperatures); - } - } - - @Inject( - method = "getBiomes([Lnet/minecraft/level/biome/Biome;IIII)[Lnet/minecraft/level/biome/Biome;", - at = @At("HEAD"), - cancellable = true - ) - private void getBiomes(Biome[] biomes, int x, int z, int xSize, int zSize, CallbackInfoReturnable cir) { - if (level.getCache() != null && level.getCache().isChunkLoaded(x >> 4, z >> 4)) { - if (biomes == null || biomes.length < xSize * zSize) - biomes = new Biome[xSize * zSize]; - if (temperatureNoises == null || temperatureNoises.length < xSize * zSize) - temperatureNoises = new double[xSize * zSize]; - if (rainfallNoises == null || rainfallNoises.length < xSize * zSize) - rainfallNoises = new double[xSize * zSize]; - if (detailNoises == null || detailNoises.length < xSize * zSize) - detailNoises = new double[xSize * zSize]; - ChunkCustomAccessor chunk = null; - int cCoords; - if (xSize == 1 && zSize == 1) { - chunk = (ChunkCustomAccessor) level.getChunk(x, z); - cCoords = ((x & 15) << 4) + (z & 15); - biomes[0] = chunk.getBiomes()[cCoords]; - temperatureNoises[0] = chunk.getTemperature()[cCoords]; - rainfallNoises[0] = chunk.getRainfall()[cCoords]; - detailNoises[0] = chunk.getDetail()[cCoords]; - } else { - Biome[] cBiomes = null; - double[] cTemperature = null; - double[] cRainfall = null; - double[] cDetail = null; - int counter = 0; - for (int xd = 0; xd < xSize; xd++) { - if (((x + xd) & 15) == 0 || chunk == null) { - chunk = (ChunkCustomAccessor) level.getChunk(x + xd, z); - cBiomes = chunk.getBiomes(); - cTemperature = chunk.getTemperature(); - cRainfall = chunk.getRainfall(); - cDetail = chunk.getDetail(); - } - for (int zd = 0; zd < zSize; zd++) { - if (((z + zd) & 15) == 0) { - chunk = (ChunkCustomAccessor) level.getChunk(x + xd, z + zd); - cBiomes = chunk.getBiomes(); - cTemperature = chunk.getTemperature(); - cRainfall = chunk.getRainfall(); - cDetail = chunk.getDetail(); - } - cCoords = (((x + xd) & 15) << 4) + ((z + zd) & 15); - biomes[counter] = cBiomes[cCoords]; - temperatureNoises[counter] = cTemperature[cCoords]; - rainfallNoises[counter] = cRainfall[cCoords]; - detailNoises[counter] = cDetail[cCoords]; - counter++; - } - } - } - cir.setReturnValue(biomes); - } - } -} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/MixinChunk.java b/src/main/java/net/mine_diver/smoothbeta/mixin/MixinChunk.java deleted file mode 100644 index 4742400..0000000 --- a/src/main/java/net/mine_diver/smoothbeta/mixin/MixinChunk.java +++ /dev/null @@ -1,40 +0,0 @@ -package net.mine_diver.smoothbeta.mixin; - -import lombok.Getter; -import net.mine_diver.smoothbeta.util.ChunkCustomAccessor; -import net.minecraft.level.Level; -import net.minecraft.level.biome.Biome; -import net.minecraft.level.chunk.Chunk; -import net.minecraft.level.gen.BiomeSource; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Chunk.class) -public class MixinChunk implements ChunkCustomAccessor { - - @Unique @Getter - private final Biome[] biomes = new Biome[256]; - @Unique @Getter - private final double[] temperature = new double[256]; - @Unique @Getter - private final double[] rainfall = new double[256]; - @Unique @Getter - private final double[] detail = new double[256]; - - @Inject( - method = "(Lnet/minecraft/level/Level;II)V", - at = @At("RETURN") - ) - private void generateCache(Level level, int x, int z, CallbackInfo ci) { - BiomeSource source = level.getBiomeSource(); -// ((BiomeSourceCustomAccessor) source).setCaching(true); - source.getBiomes(biomes, x << 4, z << 4, 16, 16); - System.arraycopy(source.temperatureNoises, 0, temperature, 0, 256); - System.arraycopy(source.rainfallNoises, 0, rainfall, 0, 256); - System.arraycopy(source.detailNoises, 0, detail, 0, 256); -// ((BiomeSourceCustomAccessor) source).setCaching(false); - } -} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/MixinLevel.java b/src/main/java/net/mine_diver/smoothbeta/mixin/MixinLevel.java deleted file mode 100644 index 4a8dffb..0000000 --- a/src/main/java/net/mine_diver/smoothbeta/mixin/MixinLevel.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.mine_diver.smoothbeta.mixin; - -import net.minecraft.level.Level; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.Constant; -import org.spongepowered.asm.mixin.injection.ModifyConstant; - -@Mixin(Level.class) -public class MixinLevel { - - @ModifyConstant( - method = { - "(Lnet/minecraft/level/dimension/DimensionData;Ljava/lang/String;Lnet/minecraft/level/dimension/Dimension;J)V", - "(Lnet/minecraft/level/Level;Lnet/minecraft/level/dimension/Dimension;)V", - "(Lnet/minecraft/level/dimension/DimensionData;Ljava/lang/String;JLnet/minecraft/level/dimension/Dimension;)V" - }, - constant = @Constant(intValue = 40) - ) - private int modSaveTime(int constant) { - return 3600; - } -} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/client/ChunkRendererMixin.java b/src/main/java/net/mine_diver/smoothbeta/mixin/client/ChunkRendererMixin.java new file mode 100644 index 0000000..4616eb8 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/mixin/client/ChunkRendererMixin.java @@ -0,0 +1,108 @@ +package net.mine_diver.smoothbeta.mixin.client; + +import net.fabricmc.loader.api.FabricLoader; +import net.mine_diver.smoothbeta.client.render.*; +import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; +import net.minecraft.class_66; +import net.minecraft.client.Minecraft; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.block.BlockRenderer; +import net.minecraft.level.WorldPopulationRegion; +import net.minecraft.tileentity.TileEntityBase; +import net.modificationstation.stationapi.api.util.math.MatrixStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +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; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.HashSet; + +@Mixin(class_66.class) +public class ChunkRendererMixin implements SmoothChunkRenderer { + @Shadow private static Tessellator tesselator; + + @Shadow public boolean[] field_244; + @Shadow public int field_236; + @Shadow public int field_235; + @Shadow public int field_240; + @Shadow public int field_241; + @Shadow public int field_242; + @Shadow public int field_231; + @Shadow public int field_233; + @Shadow public int field_232; + @Unique + private VertexBuffer[] smoothbeta_buffers; + @Unique + private final MatrixStack smoothbeta_matrices = new MatrixStack(); + + @Override + @Unique + public VertexBuffer smoothbeta_getBuffer(int pass) { + return smoothbeta_buffers[pass]; + } + + @Inject( + method = "", + at = @At("RETURN") + ) + private void smoothbeta_init(CallbackInfo ci) { + smoothbeta_buffers = new VertexBuffer[field_244.length]; + //noinspection deprecation + VboPool pool = ((SmoothWorldRenderer) ((Minecraft) FabricLoader.getInstance().getGameInstance()).worldRenderer).smoothbeta_getTerrainVboPool(); + for (int i = 0; i < smoothbeta_buffers.length; i++) + smoothbeta_buffers[i] = new VertexBuffer(pool); + } + + @Inject( + method = "method_296", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/render/Tessellator;start()V" + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void smoothbeta_startRenderingTerrain( + CallbackInfo ci, + int var1, int var2, int var3, int var4, int var5, int var6, HashSet var7, int var8, WorldPopulationRegion var9, BlockRenderer var10, int var11 + ) { + smoothbeta_matrices.push(); + smoothbeta_matrices.translate(this.field_240, this.field_241, this.field_242); + smoothbeta_matrices.translate((float)(-this.field_236) / 2.0f, (float)(-this.field_235) / 2.0f, (float)(-this.field_236) / 2.0f); + float f = 1.000001f; + smoothbeta_matrices.scale(f, f, f); + smoothbeta_matrices.translate((float)this.field_236 / 2.0f, (float)this.field_235 / 2.0f, (float)this.field_236 / 2.0f); + smoothbeta_matrices.translate(-this.field_231, -this.field_232, -this.field_233); + ((SmoothTessellator) tesselator).smoothbeta_startRenderingTerrain( + new TerrainContext( + smoothbeta_matrices, + smoothbeta_buffers[var11]::upload + ) + ); + } + + @Inject( + method = "method_296", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/render/Tessellator;draw()V", + shift = At.Shift.AFTER + ) + ) + private void smoothbeta_stopRenderingTerrain(CallbackInfo ci) { + ((SmoothTessellator) tesselator).smoothbeta_stopRenderingTerrain(); + smoothbeta_matrices.pop(); + } + + @Redirect( + method = "method_296", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/render/Tessellator;setOffset(DDD)V" + ) + ) + private void smoothbeta_disableTessellatorOffset(Tessellator instance, double e, double f, double v) {} +} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/client/MinecraftAccessor.java b/src/main/java/net/mine_diver/smoothbeta/mixin/client/MinecraftAccessor.java new file mode 100644 index 0000000..c1cc0a7 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/mixin/client/MinecraftAccessor.java @@ -0,0 +1,11 @@ +package net.mine_diver.smoothbeta.mixin.client; + +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(Minecraft.class) +public interface MinecraftAccessor { + @Invoker("printOpenGLError") + void smoothbeta_printOpenGLError(String location); +} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/client/RenderListAccessor.java b/src/main/java/net/mine_diver/smoothbeta/mixin/client/RenderListAccessor.java new file mode 100644 index 0000000..174192c --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/mixin/client/RenderListAccessor.java @@ -0,0 +1,34 @@ +package net.mine_diver.smoothbeta.mixin.client; + +import net.minecraft.client.render.RenderList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.nio.IntBuffer; + +@Mixin(RenderList.class) +public interface RenderListAccessor { + @Accessor("field_2486") + void smoothbeta_setField_2486(IntBuffer buffer); + + @Accessor("field_2487") + boolean smoothbeta_getField_2487(); + + @Accessor("field_2480") + int smoothbeta_getField_2480(); + + @Accessor("field_2481") + int smoothbeta_getField_2481(); + + @Accessor("field_2482") + int smoothbeta_getField_2482(); + + @Accessor("field_2483") + float smoothbeta_getField_2483(); + + @Accessor("field_2484") + float smoothbeta_getField_2484(); + + @Accessor("field_2485") + float smoothbeta_getField_2485(); +} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/client/TessellatorMixin.java b/src/main/java/net/mine_diver/smoothbeta/mixin/client/TessellatorMixin.java new file mode 100644 index 0000000..f20f498 --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/mixin/client/TessellatorMixin.java @@ -0,0 +1,118 @@ +package net.mine_diver.smoothbeta.mixin.client; + +import net.mine_diver.smoothbeta.client.render.SmoothTessellator; +import net.mine_diver.smoothbeta.client.render.TerrainContext; +import net.minecraft.client.render.Tessellator; +import net.modificationstation.stationapi.api.util.math.Vector4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.nio.ByteBuffer; + +@Mixin(Tessellator.class) +public abstract class TessellatorMixin implements SmoothTessellator { + @Shadow protected abstract void clear(); + + @Shadow private ByteBuffer byteBuffer; + @Unique + private boolean smoothbeta_renderingTerrain; + @Unique + private TerrainContext smoothbeta_terrainConext; + @Unique + private final Vector4f smoothbeta_pos = new Vector4f(); + + @Override + @Unique + public void smoothbeta_startRenderingTerrain(TerrainContext context) { + smoothbeta_renderingTerrain = true; + smoothbeta_terrainConext = context; + } + + @Override + @Unique + public void smoothbeta_stopRenderingTerrain() { + smoothbeta_renderingTerrain = false; + smoothbeta_terrainConext = null; + } + + @Inject( + method = "draw", + at = @At( + value = "INVOKE", + target = "Ljava/nio/ByteBuffer;limit(I)Ljava/nio/Buffer;", + shift = At.Shift.AFTER + ), + cancellable = true + ) + private void smoothbeta_uploadTerrain(CallbackInfo ci) { + if (smoothbeta_renderingTerrain) { + smoothbeta_terrainConext.terrainConsumer().accept(byteBuffer); + clear(); + ci.cancel(); + } + } + + @ModifyVariable( + method = "addVertex", + at = @At("HEAD"), + index = 1, + argsOnly = true + ) + private double smoothbeta_transformTerrainX(double value, double x, double y, double z) { + if (!smoothbeta_renderingTerrain) + return value; + smoothbeta_pos.set((float) x, (float) y, (float) z, 1.0f); + smoothbeta_pos.transform(smoothbeta_terrainConext.matrices().peek().getPositionMatrix()); + return smoothbeta_pos.getX(); + } + + @ModifyVariable( + method = "addVertex", + at = @At("HEAD"), + index = 3, + argsOnly = true + ) + private double smoothbeta_transformTerrainY(double value, double x, double y, double z) { + if (!smoothbeta_renderingTerrain) + return value; + smoothbeta_pos.set((float) x, (float) y, (float) z, 1.0f); + smoothbeta_pos.transform(smoothbeta_terrainConext.matrices().peek().getPositionMatrix()); + return smoothbeta_pos.getY(); + } + + @ModifyVariable( + method = "addVertex", + at = @At("HEAD"), + index = 5, + argsOnly = true + ) + private double smoothbeta_transformTerrainZ(double value, double x, double y, double z) { + if (!smoothbeta_renderingTerrain) + return value; + smoothbeta_pos.set((float) x, (float) y, (float) z, 1.0f); + smoothbeta_pos.transform(smoothbeta_terrainConext.matrices().peek().getPositionMatrix()); + return smoothbeta_pos.getZ(); + } + + @ModifyConstant( + method = "addVertex", + constant = @Constant(intValue = 7) + ) + private int smoothbeta_prohibitExtraVertices(int constant) { + return smoothbeta_renderingTerrain ? -1 : constant; + } + + @ModifyConstant( + method = "addVertex", + constant = @Constant( + intValue = 8, + ordinal = 2 + ) + ) + private int smoothbeta_compactVertices(int constant) { + return smoothbeta_renderingTerrain ? 7 : 8; + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/mixin/client/WorldRendererMixin.java b/src/main/java/net/mine_diver/smoothbeta/mixin/client/WorldRendererMixin.java new file mode 100644 index 0000000..45f120a --- /dev/null +++ b/src/main/java/net/mine_diver/smoothbeta/mixin/client/WorldRendererMixin.java @@ -0,0 +1,140 @@ +package net.mine_diver.smoothbeta.mixin.client; + +import net.mine_diver.smoothbeta.client.render.*; +import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; +import net.minecraft.class_214; +import net.minecraft.class_66; +import net.minecraft.client.render.RenderList; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.entity.Living; +import org.lwjgl.opengl.GL11; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.nio.FloatBuffer; + +@Mixin(WorldRenderer.class) +public abstract class WorldRendererMixin implements SmoothWorldRenderer { + @Shadow private RenderList[] field_1794; + + @Unique + private VboPool smoothbeta_vboPool; + + @Override + @Unique + public VboPool smoothbeta_getTerrainVboPool() { + return smoothbeta_vboPool; + } + + @Inject( + method = "method_1537()V", + at = @At("HEAD") + ) + private void smoothbeta_resetVboPool(CallbackInfo ci) { + if (smoothbeta_vboPool != null) + smoothbeta_vboPool.deleteGlBuffers(); + smoothbeta_vboPool = new VboPool(VertexFormats.POSITION_TEXTURE_COLOR_NORMAL); + } + + @Redirect( + method = "", + at = @At( + value = "NEW", + target = "()Lnet/minecraft/client/render/RenderList;" + ) + ) + private RenderList smoothbeta_injectRenderRegion() { + return new RenderRegion((WorldRenderer) (Object) this); + } + + @Inject( + method = "method_1542(IIID)I", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/render/RenderList;method_1910(I)V", + shift = At.Shift.BEFORE + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void smoothbeta_addBufferToRegion(int j, int k, int d, double par4, CallbackInfoReturnable cir, int var6, Living var7, double var8, double var10, double var12, int var14, int var15, class_66 var16, int var17) { + ((RenderRegion) this.field_1794[var17]).addBuffer(((SmoothChunkRenderer) var16).smoothbeta_getBuffer(d)); + } + + @Redirect( + method = "method_1542(IIID)I", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/render/RenderList;method_1910(I)V" + ) + ) + private void smoothbeta_stopCallingRenderList(RenderList instance, int i) {} + + @Unique + private final FloatBuffer + smoothbeta_modelViewMatrix = class_214.method_746(16), + smoothbeta_projectionMatrix = class_214.method_746(16), + smoothbeta_fogColor = class_214.method_746(16); + + @Inject( + method = "method_1540(ID)V", + at = @At("HEAD") + ) + public void smoothbeta_beforeRenderRegion(int d, double par2, CallbackInfo ci) { + Shader shader = Shaders.getTerrainShader(); + BufferRenderer.unbindAll(); + + if (shader != null) { + shader.addSampler("Sampler0", 0); + + if (shader.modelViewMat != null) { + GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, smoothbeta_modelViewMatrix.clear()); + shader.modelViewMat.set(smoothbeta_modelViewMatrix.position(0)); + } + + if (shader.projectionMat != null) { + GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, smoothbeta_projectionMatrix.clear()); + shader.projectionMat.set(smoothbeta_projectionMatrix.position(0)); + } + + if (shader.fogMode != null) shader.fogMode.set(switch (GL11.glGetInteger(GL11.GL_FOG_MODE)) { + case GL11.GL_EXP -> 0; + case GL11.GL_EXP2 -> 1; + case GL11.GL_LINEAR -> 2; + default -> throw new IllegalStateException("Unexpected value: " + GL11.glGetInteger(GL11.GL_FOG_MODE)); + }); + + if (shader.fogDensity != null) shader.fogDensity.set(GL11.glGetFloat(GL11.GL_FOG_DENSITY)); + + if (shader.fogStart != null) shader.fogStart.set(GL11.glGetFloat(GL11.GL_FOG_START)); + + if (shader.fogEnd != null) shader.fogEnd.set(GL11.glGetFloat(GL11.GL_FOG_END)); + + if (shader.fogColor != null) { + GL11.glGetFloat(GL11.GL_FOG_COLOR, smoothbeta_fogColor.clear()); + shader.fogColor.set(smoothbeta_fogColor.position(0).limit(4)); + } + + shader.bind(); + } + } + + @Inject( + method = "method_1540(ID)V", + at = @At("RETURN") + ) + public void smoothbeta_afterRenderRegion(int d, double par2, CallbackInfo ci) { + Shader shader = Shaders.getTerrainShader(); + if (shader != null) shader.unbind(); + + VertexFormats.POSITION_TEXTURE_COLOR_NORMAL.clearState(); + VertexBuffer.unbind(); + GL11.glEnable(GL11.GL_DEPTH_TEST); + } +} diff --git a/src/main/java/net/mine_diver/smoothbeta/util/ChunkCustomAccessor.java b/src/main/java/net/mine_diver/smoothbeta/util/ChunkCustomAccessor.java deleted file mode 100644 index cff56c2..0000000 --- a/src/main/java/net/mine_diver/smoothbeta/util/ChunkCustomAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.mine_diver.smoothbeta.util; - -import net.minecraft.level.biome.Biome; - -public interface ChunkCustomAccessor { - - Biome[] getBiomes(); - - double[] getTemperature(); - - double[] getRainfall(); - - double[] getDetail(); -} diff --git a/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.fsh b/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.fsh new file mode 100644 index 0000000..c1fea9e --- /dev/null +++ b/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.fsh @@ -0,0 +1,23 @@ +#version 150 + +#moj_import + +uniform sampler2D Sampler0; + +uniform int FogMode; +uniform float FogDensity; +uniform float FogStart; +uniform float FogEnd; +uniform vec4 FogColor; + +in float vertexDistance; +in vec2 texCoord0; +in vec4 vertexColor; +in vec4 normal; + +out vec4 fragColor; + +void main() { + vec4 color = texture(Sampler0, texCoord0) * vertexColor; + fragColor = fog(FogMode, color, vertexDistance, FogDensity, FogStart, FogEnd, FogColor); +} diff --git a/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.json b/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.json new file mode 100644 index 0000000..cfa7888 --- /dev/null +++ b/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.json @@ -0,0 +1,28 @@ +{ + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha" + }, + "vertex": "terrain", + "fragment": "terrain", + "attributes": [ + "Position", + "UV0", + "Color", + "Normal" + ], + "samplers": [ + { "name": "Sampler0" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ChunkOffset", "type": "float", "count": 3, "values": [ 0.0, 0.0, 0.0 ] }, + { "name": "FogMode", "type": "int", "count": 1, "values": [ 0 ] }, + { "name": "FogDensity", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] } + ] +} diff --git a/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.vsh b/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.vsh new file mode 100644 index 0000000..a494114 --- /dev/null +++ b/src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.vsh @@ -0,0 +1,27 @@ +#version 150 + +#moj_import + +in vec3 Position; +in vec2 UV0; +in vec4 Color; +in vec3 Normal; + +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; +uniform vec3 ChunkOffset; + +out float vertexDistance; +out vec2 texCoord0; +out vec4 vertexColor; +out vec4 normal; + +void main() { + vec3 pos = Position + ChunkOffset; + gl_Position = ProjMat * ModelViewMat * vec4(pos, 1.0); + + vertexDistance = fog_distance(ModelViewMat, pos, 0); + texCoord0 = UV0; + vertexColor = Color; + normal = ProjMat * ModelViewMat * vec4(Normal, 0.0); +} diff --git a/src/main/resources/assets/minecraft/smoothbeta/shaders/include/fog.glsl b/src/main/resources/assets/minecraft/smoothbeta/shaders/include/fog.glsl new file mode 100644 index 0000000..f7e928c --- /dev/null +++ b/src/main/resources/assets/minecraft/smoothbeta/shaders/include/fog.glsl @@ -0,0 +1,53 @@ +#version 150 + +vec4 exp_fog(vec4 inColor, float vertexDistance, float density, vec4 fogColor) { + float fogValue = exp(-density*vertexDistance); + return vec4(mix(fogColor.rgb, inColor.rgb, fogValue * fogColor.a), inColor.a); +} + +vec4 exp2_fog(vec4 inColor, float vertexDistance, float density, vec4 fogColor) { + float fogValue = exp(-density*pow(vertexDistance, 2.0)); + return vec4(mix(fogColor.rgb, inColor.rgb, fogValue * fogColor.a), inColor.a); +} + +vec4 linear_fog(vec4 inColor, float vertexDistance, float fogStart, float fogEnd, vec4 fogColor) { + if (vertexDistance <= fogStart) { + return inColor; + } + + float fogValue = vertexDistance < fogEnd ? smoothstep(fogStart, fogEnd, vertexDistance) : 1.0; + return vec4(mix(inColor.rgb, fogColor.rgb, fogValue * fogColor.a), inColor.a); +} + +vec4 fog(int mode, vec4 inColor, float vertexDistance, float density, float fogStart, float fogEnd, vec4 fogColor) { + switch (mode) { + case 0: + return exp_fog(inColor, vertexDistance, density, fogColor); + case 1: + return exp2_fog(inColor, vertexDistance, density, fogColor); + case 2: + return linear_fog(inColor, vertexDistance, fogStart, fogEnd, fogColor); + default: + return vec4(0, 0, 0, 0); + } +} + +float linear_fog_fade(float vertexDistance, float fogStart, float fogEnd) { + if (vertexDistance <= fogStart) { + return 1.0; + } else if (vertexDistance >= fogEnd) { + return 0.0; + } + + return smoothstep(fogEnd, fogStart, vertexDistance); +} + +float fog_distance(mat4 modelViewMat, vec3 pos, int shape) { + if (shape == 0) { + return length((modelViewMat * vec4(pos, 1.0)).xyz); + } else { + float distXZ = length((modelViewMat * vec4(pos.x, 0.0, pos.z, 1.0)).xyz); + float distY = length((modelViewMat * vec4(0.0, pos.y, 0.0, 1.0)).xyz); + return max(distXZ, distY); + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index a8dd368..fdab93a 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -23,7 +23,8 @@ "net.mine_diver.smoothbeta.SmoothBeta" ], "stationapi:event_bus_client": [ - "net.mine_diver.smoothbeta.client.SmoothBetaClient" + "net.mine_diver.smoothbeta.client.SmoothBetaClient", + "net.mine_diver.smoothbeta.client.render.Shaders" ] }, "mixins": [ diff --git a/src/main/resources/smoothbeta.mixins.json b/src/main/resources/smoothbeta.mixins.json index 579ed42..fec7e26 100644 --- a/src/main/resources/smoothbeta.mixins.json +++ b/src/main/resources/smoothbeta.mixins.json @@ -4,14 +4,16 @@ "package": "net.mine_diver.smoothbeta.mixin", "compatibilityLevel": "JAVA_8", "client": [ + "client.ChunkRendererMixin", + "client.MinecraftAccessor", "client.MixinEntityRendererDispatcher", - "client.MixinTileEntityRenderDispatcher" + "client.MixinTileEntityRenderDispatcher", + "client.RenderListAccessor", + "client.TessellatorMixin", + "client.WorldRendererMixin" ], "mixins": [ - "MixinBiomeSource", - "MixinChunk", "MixinEntityRegistry", - "MixinLevel", "MixinServerChunkCache" ], "injectors": {