├── gradlew ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ ├── smoothbeta │ │ │ └── icon.png │ │ └── minecraft │ │ │ └── smoothbeta │ │ │ └── shaders │ │ │ ├── core │ │ │ ├── terrain.fsh │ │ │ ├── terrain.vsh │ │ │ └── terrain.json │ │ │ └── include │ │ │ └── fog.glsl │ ├── smoothbeta.mixins.json │ └── fabric.mod.json │ └── java │ └── net │ └── mine_diver │ └── smoothbeta │ ├── client │ ├── render │ │ ├── SmoothWorldRenderer.java │ │ ├── SmoothChunkRenderer.java │ │ ├── SmoothTessellator.java │ │ ├── gl │ │ │ ├── GlShader.java │ │ │ ├── Uniform.java │ │ │ ├── VertexBuffer.java │ │ │ ├── GlProgramManager.java │ │ │ ├── ShaderParseException.java │ │ │ ├── Program.java │ │ │ ├── GlBlendState.java │ │ │ ├── GlStateManager.java │ │ │ ├── GLImportProcessor.java │ │ │ └── GlUniform.java │ │ ├── VertexFormats.java │ │ ├── RenderRegion.java │ │ ├── VertexFormatElement.java │ │ ├── Shaders.java │ │ ├── VertexFormat.java │ │ ├── IndexBuffer.java │ │ ├── VboPool.java │ │ └── Shader.java │ └── SmoothBetaClient.java │ ├── mixin │ ├── client │ │ ├── MinecraftAccessor.java │ │ ├── multidraw │ │ │ ├── RenderListAccessor.java │ │ │ ├── compat │ │ │ │ ├── arsenic │ │ │ │ │ └── ArsenicTessellatorMixin.java │ │ │ │ └── stationrendererapi │ │ │ │ │ └── StationTessellatorImplMixin.java │ │ │ ├── nop │ │ │ │ └── ChunkRendererMixin.java │ │ │ ├── TessellatorMixin.java │ │ │ ├── ChunkRendererMixin.java │ │ │ └── WorldRendererMixin.java │ │ ├── MixinEntityRendererDispatcher.java │ │ └── MixinTileEntityRenderDispatcher.java │ ├── entity │ │ ├── compat │ │ │ └── stationentities │ │ │ │ └── EntityRegisterEventMixin.java │ │ └── MixinEntityRegistry.java │ └── MixinServerChunkCache.java │ ├── SmoothBeta.java │ ├── util │ └── StringHelper.java │ └── entity │ └── SmoothEntityRegistry.java ├── jitpack.yml ├── .gitignore ├── settings.gradle ├── README.md ├── gradle.properties ├── .github └── workflows │ └── build.yml ├── gradlew.bat └── LICENSE /gradlew: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mineLdiver/smoothbeta/HEAD/gradlew -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mineLdiver/smoothbeta/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/smoothbeta/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mineLdiver/smoothbeta/HEAD/src/main/resources/assets/smoothbeta/icon.png -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh 3 | - source ./install-jdk.sh --feature 17 --license GPL 4 | jdk: 5 | - openjdk17 -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/SmoothWorldRenderer.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | public interface SmoothWorldRenderer { 4 | VboPool smoothbeta_getTerrainVboPool(); 5 | } 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # fabric 28 | 29 | run/ 30 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/SmoothChunkRenderer.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; 4 | 5 | public interface SmoothChunkRenderer { 6 | VertexBuffer smoothbeta_getBuffer(int pass); 7 | 8 | VertexBuffer smoothbeta_getCurrentBuffer(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/SmoothTessellator.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | public interface SmoothTessellator { 4 | void smoothbeta_startRenderingTerrain(SmoothChunkRenderer chunkRenderer); 5 | 6 | void smoothbeta_stopRenderingTerrain(); 7 | 8 | boolean smoothbeta_isRenderingTerrain(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | maven { 8 | name = 'Babric' 9 | url = 'https://maven.glass-launcher.net/babric' 10 | } 11 | mavenCentral() 12 | gradlePluginPortal() 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/SmoothBetaClient.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client; 2 | 3 | import net.modificationstation.stationapi.api.mod.entrypoint.Entrypoint; 4 | import net.modificationstation.stationapi.api.mod.entrypoint.EventBusPolicy; 5 | 6 | @Entrypoint(eventBus = @EventBusPolicy(registerStatic = false, registerInstance = false)) 7 | public class SmoothBetaClient { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlShader.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | 6 | @Environment(EnvType.CLIENT) 7 | public interface GlShader { 8 | int getProgramRef(); 9 | 10 | Program getVertexShader(); 11 | 12 | Program getFragmentShader(); 13 | 14 | void attachReferencedShaders(); 15 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/MinecraftAccessor.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(Minecraft.class) 8 | public interface MinecraftAccessor { 9 | @Invoker("logGlError") 10 | void smoothbeta_printOpenGLError(String location); 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fabric Example Mod with StationAPI and BIN Mappings for beta 1.7.3 server + client 2 | 3 | ## Setup 4 | 5 | [See the StationAPI wiki.](https://github.com/ModificationStation/StationAPI) 6 | 7 | ## Common Issues 8 | 9 | [Here.](https://github.com/calmilamsy/BIN-fabric-example-mod#common-issues) 10 | 11 | ## License 12 | 13 | This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects. 14 | -------------------------------------------------------------------------------- /src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.fsh: -------------------------------------------------------------------------------- 1 | #version 150 compatibility 2 | 3 | #moj_import 4 | 5 | uniform sampler2D Sampler0; 6 | 7 | uniform int FogMode; 8 | 9 | in float vertexDistance; 10 | in vec2 texCoord0; 11 | in vec4 vertexColor; 12 | in vec4 normal; 13 | 14 | out vec4 fragColor; 15 | 16 | void main() { 17 | vec4 color = texture(Sampler0, texCoord0) * vertexColor; 18 | fragColor = fog(FogMode, color, vertexDistance, gl_Fog.density, gl_Fog.start, gl_Fog.end, gl_Fog.color.rgba); 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | org.gradle.daemon=false 4 | 5 | # Fabric Properties 6 | # check these on https://fabricmc.net/develop 7 | minecraft_version=b1.7.3 8 | yarn_mappings=b1.7.3+c6a9668 9 | loader_version=0.15.6-babric.1 10 | 11 | # Mod Properties 12 | mod_version=1.1.7 13 | maven_group=net.mine_diver 14 | archives_base_name=SmoothBeta 15 | 16 | # Dependencies 17 | stapi_version=2.0.0-alpha.4 18 | 19 | # Extra Dependencies 20 | glass_networking_version=1.0.4 21 | gcapi_version=3.0.4 22 | alwaysmoreitems_version=1.5.3 23 | modmenu_version=1.8.5-beta.11 -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/SmoothBeta.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta; 2 | 3 | import net.modificationstation.stationapi.api.mod.entrypoint.Entrypoint; 4 | import net.modificationstation.stationapi.api.mod.entrypoint.EventBusPolicy; 5 | import net.modificationstation.stationapi.api.util.Namespace; 6 | import org.apache.logging.log4j.Logger; 7 | 8 | @Entrypoint(eventBus = @EventBusPolicy(registerStatic = false, registerInstance = false)) 9 | public class SmoothBeta { 10 | @SuppressWarnings("UnstableApiUsage") 11 | public static final Namespace NAMESPACE = Namespace.resolve(); 12 | 13 | public static final Logger LOGGER = NAMESPACE.getLogger(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/Uniform.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.modificationstation.stationapi.api.util.math.Vec3f; 6 | 7 | @Environment(EnvType.CLIENT) 8 | public class Uniform { 9 | public void set(float value1, float value2, float value3) {} 10 | 11 | public void setForDataType(float value1, float value2, float value3, float value4) {} 12 | 13 | public void setForDataType(int value1, int value2, int value3, int value4) {} 14 | 15 | public void set(int value) {} 16 | 17 | public void set(float[] values) {} 18 | 19 | public void set(Vec3f vector) {} 20 | } -------------------------------------------------------------------------------- /src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | #moj_import 4 | 5 | in vec3 Position; 6 | in vec2 UV0; 7 | in vec4 Color; 8 | in vec3 Normal; 9 | 10 | uniform mat4 ModelViewMat; 11 | uniform mat4 ProjMat; 12 | uniform vec3 ChunkOffset; 13 | 14 | out float vertexDistance; 15 | out vec2 texCoord0; 16 | out vec4 vertexColor; 17 | out vec4 normal; 18 | 19 | void main() { 20 | vec3 pos = Position + ChunkOffset; 21 | gl_Position = ProjMat * ModelViewMat * vec4(pos, 1.0); 22 | 23 | vertexDistance = fog_distance(ModelViewMat, pos, 0); 24 | texCoord0 = UV0; 25 | vertexColor = Color; 26 | normal = ProjMat * ModelViewMat * vec4(Normal, 0.0); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/VertexBuffer.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import net.mine_diver.smoothbeta.client.render.VboPool; 4 | import net.mine_diver.smoothbeta.client.render.VertexFormat; 5 | 6 | import java.nio.ByteBuffer; 7 | 8 | public class VertexBuffer { 9 | private final VboPool pool; 10 | private final VboPool.Pos poolPos = new VboPool.Pos(); 11 | 12 | public VertexBuffer(VboPool pool) { 13 | this.pool = pool; 14 | } 15 | 16 | public void upload(ByteBuffer buffer) { 17 | pool.bufferData(buffer, poolPos); 18 | } 19 | 20 | public void uploadToPool() { 21 | pool.upload(VertexFormat.DrawMode.QUADS, poolPos); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/smoothbeta.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "net.mine_diver.smoothbeta.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "client.MinecraftAccessor", 8 | "client.MixinEntityRendererDispatcher", 9 | "client.MixinTileEntityRenderDispatcher", 10 | "client.multidraw.ChunkRendererMixin", 11 | "client.multidraw.RenderListAccessor", 12 | "client.multidraw.TessellatorMixin", 13 | "client.multidraw.WorldRendererMixin", 14 | "client.multidraw.compat.arsenic.ArsenicTessellatorMixin", 15 | "client.multidraw.compat.stationrendererapi.StationTessellatorImplMixin", 16 | "client.multidraw.nop.ChunkRendererMixin" 17 | ], 18 | "mixins": [ 19 | "MixinServerChunkCache", 20 | "entity.MixinEntityRegistry", 21 | "entity.compat.stationentities.EntityRegisterEventMixin" 22 | ], 23 | "injectors": { 24 | "defaultRequire": 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/entity/compat/stationentities/EntityRegisterEventMixin.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.entity.compat.stationentities; 2 | 3 | import net.mine_diver.smoothbeta.entity.SmoothEntityRegistry; 4 | import net.modificationstation.stationapi.api.event.entity.EntityRegister; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 8 | 9 | @Mixin(EntityRegister.EntityRegisterBuilder.class) 10 | public class EntityRegisterEventMixin { 11 | @ModifyVariable( 12 | method = "registerNoID", 13 | at = @At("HEAD"), 14 | index = 1, 15 | argsOnly = true, 16 | remap = false 17 | ) 18 | private EntityRegister.RegisterFunctionNoId smoothbeta_registerNoID(EntityRegister.RegisterFunctionNoId value) { 19 | return SmoothEntityRegistry::registerNoID; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/assets/minecraft/smoothbeta/shaders/core/terrain.json: -------------------------------------------------------------------------------- 1 | { 2 | "blend": { 3 | "func": "add", 4 | "srcrgb": "srcalpha", 5 | "dstrgb": "1-srcalpha" 6 | }, 7 | "vertex": "terrain", 8 | "fragment": "terrain", 9 | "attributes": [ 10 | "Position", 11 | "UV0", 12 | "Color", 13 | "Normal" 14 | ], 15 | "samplers": [ 16 | { "name": "Sampler0" } 17 | ], 18 | "uniforms": [ 19 | { "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 ] }, 20 | { "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 ] }, 21 | { "name": "ChunkOffset", "type": "float", "count": 3, "values": [ 0.0, 0.0, 0.0 ] }, 22 | { "name": "FogMode", "type": "int", "count": 1, "values": [ 0 ] } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/multidraw/RenderListAccessor.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client.multidraw; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Accessor; 5 | 6 | import java.nio.IntBuffer; 7 | import net.minecraft.client.render.world.ChunkRenderer; 8 | 9 | @Mixin(ChunkRenderer.class) 10 | public interface RenderListAccessor { 11 | @Accessor("glListBuffer") 12 | void smoothbeta_setGlListBuffer(IntBuffer buffer); 13 | 14 | @Accessor("initialized") 15 | boolean smoothbeta_getInitialized(); 16 | 17 | @Accessor("x") 18 | int smoothbeta_getX(); 19 | 20 | @Accessor("y") 21 | int smoothbeta_getY(); 22 | 23 | @Accessor("z") 24 | int smoothbeta_getZ(); 25 | 26 | @Accessor("offsetX") 27 | float smoothbeta_getOffsetX(); 28 | 29 | @Accessor("offsetY") 30 | float smoothbeta_getOffsetY(); 31 | 32 | @Accessor("offsetZ") 33 | float smoothbeta_getOffsetZ(); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "smoothbeta", 4 | "version": "${version}", 5 | 6 | "name": "SmoothBeta", 7 | "description": "Additional settings and optimizations for beta that allow for smoother experience!", 8 | "authors": [ 9 | "mine_diver" 10 | ], 11 | "contact": { 12 | "homepage": "https://glass-repo.net/repo/mod/smoothbeta", 13 | "sources": "https://github.com/mineLdiver/smoothbeta", 14 | "issues": "https://github.com/mineLdiver/smoothbeta/issues" 15 | }, 16 | 17 | "license": "CC0-1.0", 18 | "icon": "assets/smoothbeta/icon.png", 19 | 20 | "environment": "*", 21 | "entrypoints": { 22 | "stationapi:event_bus": [ 23 | "net.mine_diver.smoothbeta.SmoothBeta" 24 | ], 25 | "stationapi:event_bus_client": [ 26 | "net.mine_diver.smoothbeta.client.SmoothBetaClient", 27 | "net.mine_diver.smoothbeta.client.render.Shaders" 28 | ] 29 | }, 30 | "mixins": [ 31 | "smoothbeta.mixins.json" 32 | ], 33 | 34 | "depends": { 35 | "minecraft": "1.0.0-beta.7.3", 36 | "stationapi": ">=2.0.0-alpha.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/multidraw/compat/arsenic/ArsenicTessellatorMixin.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client.multidraw.compat.arsenic; 2 | 3 | 4 | import net.mine_diver.smoothbeta.client.render.SmoothTessellator; 5 | import net.minecraft.client.render.Tessellator; 6 | import net.modificationstation.stationapi.impl.client.arsenic.renderer.render.ArsenicTessellator; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.Constant; 11 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 12 | 13 | @Mixin(ArsenicTessellator.class) 14 | class ArsenicTessellatorMixin { 15 | @Shadow @Final private Tessellator tessellator; 16 | 17 | @ModifyConstant( 18 | method = "afterVertex", 19 | constant = @Constant(intValue = 48), 20 | remap = false 21 | ) 22 | private int smoothbeta_compactVertices(int constant) { 23 | return ((SmoothTessellator) tessellator).smoothbeta_isRenderingTerrain() ? 28 : constant; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/MixinEntityRendererDispatcher.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client; 2 | 3 | import net.minecraft.client.render.entity.EntityRenderDispatcher; 4 | import net.minecraft.client.render.entity.EntityRenderer; 5 | import net.minecraft.entity.Entity; 6 | import org.objectweb.asm.Opcodes; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | import java.util.IdentityHashMap; 13 | import java.util.Map; 14 | 15 | @Mixin(EntityRenderDispatcher.class) 16 | class MixinEntityRendererDispatcher { 17 | 18 | @Shadow private Map, EntityRenderer> renderers; 19 | 20 | @Redirect( 21 | method = "()V", 22 | at = @At( 23 | value = "FIELD", 24 | target = "Lnet/minecraft/client/render/entity/EntityRenderDispatcher;renderers:Ljava/util/Map;", 25 | opcode = Opcodes.PUTFIELD 26 | ) 27 | ) 28 | private void overrideMap(EntityRenderDispatcher entityRenderDispatcher, Map, EntityRenderer> value) { 29 | renderers = new IdentityHashMap<>(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/MixinTileEntityRenderDispatcher.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client; 2 | 3 | import net.minecraft.block.entity.BlockEntity; 4 | import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; 5 | import net.minecraft.client.render.block.entity.BlockEntityRenderer; 6 | import org.objectweb.asm.Opcodes; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | import java.util.IdentityHashMap; 13 | import java.util.Map; 14 | 15 | @Mixin(BlockEntityRenderDispatcher.class) 16 | class MixinTileEntityRenderDispatcher { 17 | 18 | @Shadow private Map, BlockEntityRenderer> renderers; 19 | 20 | @Redirect( 21 | method = "()V", 22 | at = @At( 23 | value = "FIELD", 24 | target = "Lnet/minecraft/client/render/block/entity/BlockEntityRenderDispatcher;renderers:Ljava/util/Map;", 25 | opcode = Opcodes.PUTFIELD 26 | ) 27 | ) 28 | private void overrideMap(BlockEntityRenderDispatcher instance, Map, BlockEntityRenderer> value) { 29 | renderers = new IdentityHashMap<>(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormats.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | 7 | @Environment(EnvType.CLIENT) 8 | public class VertexFormats { 9 | public static final VertexFormatElement POSITION_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.FLOAT, VertexFormatElement.Type.POSITION, 3); 10 | public static final VertexFormatElement COLOR_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.UBYTE, VertexFormatElement.Type.COLOR, 4); 11 | public static final VertexFormatElement TEXTURE_0_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.FLOAT, VertexFormatElement.Type.UV, 2); 12 | public static final VertexFormatElement NORMAL_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.BYTE, VertexFormatElement.Type.NORMAL, 3); 13 | public static final VertexFormatElement PADDING_ELEMENT = new VertexFormatElement(0, VertexFormatElement.ComponentType.BYTE, VertexFormatElement.Type.PADDING, 1); 14 | 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()); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlProgramManager.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import org.lwjgl.opengl.GL20; 6 | 7 | import java.io.IOException; 8 | 9 | import static net.modificationstation.stationapi.impl.client.texture.StationRenderImpl.LOGGER; 10 | 11 | @Environment(EnvType.CLIENT) 12 | public class GlProgramManager { 13 | public static void useProgram(int program) { 14 | GL20.glUseProgram(program); 15 | } 16 | 17 | public static void deleteProgram(GlShader shader) { 18 | shader.getFragmentShader().release(); 19 | shader.getVertexShader().release(); 20 | GL20.glDeleteProgram(shader.getProgramRef()); 21 | } 22 | 23 | public static int createProgram() throws IOException { 24 | int i = GL20.glCreateProgram(); 25 | if (i <= 0) throw new IOException("Could not create shader program (returned program ID " + i + ")"); 26 | else return i; 27 | } 28 | 29 | public static void linkProgram(GlShader shader) { 30 | shader.attachReferencedShaders(); 31 | GL20.glLinkProgram(shader.getProgramRef()); 32 | int i = GL20.glGetProgrami(shader.getProgramRef(), GL20.GL_LINK_STATUS); 33 | if (i == 0) { 34 | LOGGER.warn("Error encountered when linking program containing VS {} and FS {}. Log output:", shader.getVertexShader().getName(), shader.getFragmentShader().getName()); 35 | LOGGER.warn(GL20.glGetProgramInfoLog(shader.getProgramRef(), 0x8000)); 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: build 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | # Use these Java versions 14 | java: [ 15 | 17, # Current Java LTS & minimum supported by Minecraft 16 | ] 17 | # and run on both Linux and Windows 18 | os: [ubuntu-22.04, windows-2022] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: checkout repository 22 | uses: actions/checkout@v3 23 | - name: validate gradle wrapper 24 | uses: gradle/wrapper-validation-action@v1 25 | - name: setup jdk ${{ matrix.java }} 26 | uses: actions/setup-java@v3 27 | with: 28 | java-version: ${{ matrix.java }} 29 | distribution: 'microsoft' 30 | - name: make gradle wrapper executable 31 | if: ${{ runner.os != 'Windows' }} 32 | run: chmod +x ./gradlew 33 | - name: build 34 | run: ./gradlew build 35 | - name: capture build artifacts 36 | if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: Artifacts 40 | path: build/libs/ -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/entity/MixinEntityRegistry.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.entity; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.mine_diver.smoothbeta.entity.SmoothEntityRegistry; 6 | import net.minecraft.entity.Entity; 7 | import net.minecraft.entity.EntityRegistry; 8 | import net.minecraft.nbt.NbtCompound; 9 | import net.minecraft.world.World; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Overwrite; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(EntityRegistry.class) 17 | class MixinEntityRegistry { 18 | 19 | @Inject( 20 | method = "register", 21 | at = @At("RETURN") 22 | ) 23 | private static void registerConstructors(Class entityClass, String id, int rawId, CallbackInfo ci) { 24 | SmoothEntityRegistry.register(entityClass, id, rawId); 25 | } 26 | 27 | /** 28 | * @reason There really isn't a better way to make it optimized and not use an {@link Overwrite}. 29 | * @author mine_diver 30 | */ 31 | @Overwrite 32 | public static Entity create(String id, World world) { 33 | return SmoothEntityRegistry.create(id, world); 34 | } 35 | 36 | /** 37 | * @reason There really isn't a better way to make it optimized and not use an {@link Overwrite}. 38 | * @author mine_diver 39 | */ 40 | @Overwrite 41 | public static Entity getEntityFromNbt(NbtCompound nbt, World world) { 42 | return SmoothEntityRegistry.create(nbt, world); 43 | } 44 | 45 | /** 46 | * @reason There really isn't a better way to make it optimized and not use an {@link Overwrite}. 47 | * @author mine_diver 48 | */ 49 | @Overwrite 50 | @Environment(EnvType.CLIENT) 51 | public static Entity create(int rawId, World world) { 52 | return SmoothEntityRegistry.create(rawId, world); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/resources/assets/minecraft/smoothbeta/shaders/include/fog.glsl: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | vec4 exp_fog(vec4 inColor, float vertexDistance, float density, vec4 fogColor) { 4 | float fogValue = exp(-density*vertexDistance); 5 | return vec4(mix(fogColor.rgb, inColor.rgb, fogValue * fogColor.a), inColor.a); 6 | } 7 | 8 | vec4 exp2_fog(vec4 inColor, float vertexDistance, float density, vec4 fogColor) { 9 | float fogValue = exp(-density*pow(vertexDistance, 2.0)); 10 | return vec4(mix(fogColor.rgb, inColor.rgb, fogValue * fogColor.a), inColor.a); 11 | } 12 | 13 | vec4 linear_fog(vec4 inColor, float vertexDistance, float fogStart, float fogEnd, vec4 fogColor) { 14 | if (vertexDistance <= fogStart) { 15 | return inColor; 16 | } 17 | 18 | float fogValue = vertexDistance < fogEnd ? smoothstep(fogStart, fogEnd, vertexDistance) : 1.0; 19 | return vec4(mix(inColor.rgb, fogColor.rgb, fogValue * fogColor.a), inColor.a); 20 | } 21 | 22 | vec4 fog(int mode, vec4 inColor, float vertexDistance, float density, float fogStart, float fogEnd, vec4 fogColor) { 23 | switch (mode) { 24 | case 0: 25 | return exp_fog(inColor, vertexDistance, density, fogColor); 26 | case 1: 27 | return exp2_fog(inColor, vertexDistance, density, fogColor); 28 | case 2: 29 | return linear_fog(inColor, vertexDistance, fogStart, fogEnd, fogColor); 30 | default: 31 | return vec4(0, 0, 0, 0); 32 | } 33 | } 34 | 35 | float linear_fog_fade(float vertexDistance, float fogStart, float fogEnd) { 36 | if (vertexDistance <= fogStart) { 37 | return 1.0; 38 | } else if (vertexDistance >= fogEnd) { 39 | return 0.0; 40 | } 41 | 42 | return smoothstep(fogEnd, fogStart, vertexDistance); 43 | } 44 | 45 | float fog_distance(mat4 modelViewMat, vec3 pos, int shape) { 46 | if (shape == 0) { 47 | return length((modelViewMat * vec4(pos, 1.0)).xyz); 48 | } else { 49 | float distXZ = length((modelViewMat * vec4(pos.x, 0.0, pos.z, 1.0)).xyz); 50 | float distY = length((modelViewMat * vec4(0.0, pos.y, 0.0, 1.0)).xyz); 51 | return max(distXZ, distY); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/RenderRegion.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import net.mine_diver.smoothbeta.client.render.gl.GlUniform; 4 | import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; 5 | import net.mine_diver.smoothbeta.mixin.client.multidraw.RenderListAccessor; 6 | import net.minecraft.client.render.WorldRenderer; 7 | import net.minecraft.client.render.world.ChunkRenderer; 8 | import net.modificationstation.stationapi.api.util.math.Vec3f; 9 | 10 | import java.nio.IntBuffer; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class RenderRegion extends ChunkRenderer { 15 | 16 | private final RenderListAccessor _super = (RenderListAccessor) this; 17 | private final SmoothWorldRenderer stationWorldRenderer; 18 | private final List buffers = new ArrayList<>(); 19 | 20 | public RenderRegion(WorldRenderer worldRenderer) { 21 | _super.smoothbeta_setGlListBuffer(IntBuffer.allocate(0)); 22 | stationWorldRenderer = ((SmoothWorldRenderer) worldRenderer); 23 | } 24 | 25 | @Override 26 | public void init(int i, int j, int k, double d, double e, double f) { 27 | super.init(i, j, k, d, e, f); 28 | buffers.clear(); 29 | } 30 | 31 | @Override 32 | public void addGlList(int i) { 33 | throw new UnsupportedOperationException("Call lists can't be added to VBO regions!"); 34 | } 35 | 36 | public void addBuffer(VertexBuffer buffer) { 37 | buffers.add(buffer); 38 | } 39 | 40 | public void render() { 41 | if (!_super.smoothbeta_getInitialized() || buffers.isEmpty()) return; 42 | Shader shader = Shaders.getTerrainShader(); 43 | GlUniform chunkOffset = shader.chunkOffset; 44 | chunkOffset.set(_super.smoothbeta_getX() - _super.smoothbeta_getOffsetX(), _super.smoothbeta_getY() - _super.smoothbeta_getOffsetY(), _super.smoothbeta_getZ() - _super.smoothbeta_getOffsetZ()); 45 | chunkOffset.upload(); 46 | for (VertexBuffer vertexBuffer : buffers) vertexBuffer.uploadToPool(); 47 | stationWorldRenderer.smoothbeta_getTerrainVboPool().drawAll(); 48 | chunkOffset.set(Vec3f.ZERO); 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/MixinServerChunkCache.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin; 2 | 3 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 4 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 5 | import net.minecraft.util.math.ChunkPos; 6 | import net.minecraft.world.World; 7 | import net.minecraft.world.chunk.Chunk; 8 | import net.minecraft.world.chunk.ChunkCache; 9 | import net.minecraft.world.chunk.ChunkSource; 10 | import net.minecraft.world.chunk.storage.ChunkStorage; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Overwrite; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.Unique; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | import java.util.Map; 20 | 21 | @Mixin(ChunkCache.class) 22 | abstract class MixinServerChunkCache { 23 | 24 | @Shadow private Map chunkByPos; 25 | 26 | @Shadow public abstract Chunk loadChunk(int chunkX, int chunkZ); 27 | 28 | @Unique 29 | private Int2ObjectMap smoothbeta$serverChunkCache; 30 | 31 | @Inject( 32 | method = "", 33 | at = @At("RETURN") 34 | ) 35 | private void getMap(World level, ChunkStorage arg1, ChunkSource arg2, CallbackInfo ci) { 36 | smoothbeta$serverChunkCache = new Int2ObjectOpenHashMap<>(); 37 | chunkByPos = smoothbeta$serverChunkCache; 38 | } 39 | 40 | // TODO: replace with ASM 41 | /** 42 | * @reason Redirecting {@code serverChunkCache.containsKey(Vec2i.hash(chunkX, chunkZ))} still boxes the integer, adding unnecessary memory usage. 43 | * @author mine_diver 44 | */ 45 | @Overwrite 46 | public boolean isChunkLoaded(int chunkX, int chunkZ) { 47 | return smoothbeta$serverChunkCache.containsKey(ChunkPos.hashCode(chunkX, chunkZ)); 48 | } 49 | 50 | // TODO: replace with ASM 51 | /** 52 | * @reason This is the only way to avoid integer boxing here. 53 | * @author mine_diver 54 | */ 55 | @Overwrite 56 | public Chunk getChunk(int chunkX, int chunkZ) { 57 | Chunk var3 = smoothbeta$serverChunkCache.get(ChunkPos.hashCode(chunkX, chunkZ)); 58 | return var3 == null ? loadChunk(chunkX, chunkZ) : var3; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/ShaderParseException.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import com.google.common.collect.Lists; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.io.FileNotFoundException; 8 | import java.io.IOException; 9 | import java.util.List; 10 | 11 | public class ShaderParseException extends IOException { 12 | private final List traces = Lists.newArrayList(); 13 | private final String message; 14 | 15 | public ShaderParseException(String message) { 16 | this.traces.add(new JsonStackTrace()); 17 | this.message = message; 18 | } 19 | 20 | public ShaderParseException(String message, Throwable cause) { 21 | super(cause); 22 | this.traces.add(new JsonStackTrace()); 23 | this.message = message; 24 | } 25 | 26 | public void addFaultyElement(String jsonKey) { 27 | this.traces.get(0).add(jsonKey); 28 | } 29 | 30 | public void addFaultyFile(String path) { 31 | this.traces.get(0).fileName = path; 32 | this.traces.add(0, new JsonStackTrace()); 33 | } 34 | 35 | public String getMessage() { 36 | return "Invalid " + this.traces.get(this.traces.size() - 1) + ": " + this.message; 37 | } 38 | 39 | public static ShaderParseException wrap(Exception cause) { 40 | if (cause instanceof ShaderParseException) { 41 | return (ShaderParseException)cause; 42 | } else { 43 | String string = cause.getMessage(); 44 | if (cause instanceof FileNotFoundException) { 45 | string = "File not found"; 46 | } 47 | 48 | return new ShaderParseException(string, cause); 49 | } 50 | } 51 | 52 | public static class JsonStackTrace { 53 | @Nullable 54 | String fileName; 55 | private final List faultyElements = Lists.newArrayList(); 56 | 57 | JsonStackTrace() {} 58 | 59 | void add(String element) { 60 | this.faultyElements.add(0, element); 61 | } 62 | 63 | public String joinStackTrace() { 64 | return StringUtils.join(this.faultyElements, "->"); 65 | } 66 | 67 | public String toString() { 68 | if (this.fileName != null) { 69 | if (this.faultyElements.isEmpty()) { 70 | return this.fileName; 71 | } else { 72 | String var10000 = this.fileName; 73 | return var10000 + " " + this.joinStackTrace(); 74 | } 75 | } else { 76 | return this.faultyElements.isEmpty() ? "(Unknown file)" : "(Unknown file) " + this.joinStackTrace(); 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/multidraw/nop/ChunkRendererMixin.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client.multidraw.nop; 2 | 3 | import net.minecraft.client.render.chunk.ChunkBuilder; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(ChunkBuilder.class) 9 | public class ChunkRendererMixin { 10 | @Redirect( 11 | method = "rebuild", 12 | at = @At( 13 | value = "INVOKE", 14 | target = "Lorg/lwjgl/opengl/GL11;glNewList(II)V", 15 | remap = false 16 | ) 17 | ) 18 | private void smoothbeta_nop_GL11_glNewList(int list, int mode) {} 19 | 20 | @Redirect( 21 | method = "rebuild", 22 | at = @At( 23 | value = "INVOKE", 24 | target = "Lorg/lwjgl/opengl/GL11;glEndList()V", 25 | remap = false 26 | ) 27 | ) 28 | private void smoothbeta_nop_GL11_glEndList() {} 29 | 30 | @Redirect( 31 | method = { 32 | "translateToRenderPosition", 33 | "rebuild" 34 | }, 35 | at = @At( 36 | value = "INVOKE", 37 | target = "Lorg/lwjgl/opengl/GL11;glTranslatef(FFF)V", 38 | remap = false 39 | ) 40 | ) 41 | private void smoothbeta_nop_GL11_glTranslatef(float x, float y, float z) {} 42 | 43 | @Redirect( 44 | method = "rebuild", 45 | at = @At( 46 | value = "INVOKE", 47 | target = "Lorg/lwjgl/opengl/GL11;glPushMatrix()V", 48 | remap = false 49 | ) 50 | ) 51 | private void smoothbeta_nop_GL11_glPushMatrix() {} 52 | 53 | @Redirect( 54 | method = "rebuild", 55 | at = @At( 56 | value = "INVOKE", 57 | target = "Lorg/lwjgl/opengl/GL11;glScalef(FFF)V", 58 | remap = false 59 | ) 60 | ) 61 | private void smoothbeta_nop_GL11_glScalef(float x, float y, float z) {} 62 | 63 | @Redirect( 64 | method = "rebuild", 65 | at = @At( 66 | value = "INVOKE", 67 | target = "Lorg/lwjgl/opengl/GL11;glPopMatrix()V", 68 | remap = false 69 | ) 70 | ) 71 | private void smoothbeta_nop_GL11_glPopMatrix() {} 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/multidraw/TessellatorMixin.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client.multidraw; 2 | 3 | import net.mine_diver.smoothbeta.client.render.SmoothChunkRenderer; 4 | import net.mine_diver.smoothbeta.client.render.SmoothTessellator; 5 | import net.minecraft.client.render.Tessellator; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.Unique; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Constant; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | import java.nio.ByteBuffer; 16 | 17 | @Mixin(Tessellator.class) 18 | abstract class TessellatorMixin implements SmoothTessellator { 19 | @Shadow protected abstract void reset(); 20 | 21 | @Shadow private ByteBuffer byteBuffer; 22 | @Unique 23 | private boolean smoothbeta_renderingTerrain; 24 | @Unique 25 | private SmoothChunkRenderer smoothbeta_chunkRenderer; 26 | 27 | @Override 28 | @Unique 29 | public void smoothbeta_startRenderingTerrain(SmoothChunkRenderer chunkRenderer) { 30 | smoothbeta_renderingTerrain = true; 31 | smoothbeta_chunkRenderer = chunkRenderer; 32 | } 33 | 34 | @Override 35 | @Unique 36 | public void smoothbeta_stopRenderingTerrain() { 37 | smoothbeta_renderingTerrain = false; 38 | smoothbeta_chunkRenderer = null; 39 | } 40 | 41 | @Override 42 | public boolean smoothbeta_isRenderingTerrain() { 43 | return smoothbeta_renderingTerrain; 44 | } 45 | 46 | @Inject( 47 | method = "draw", 48 | at = @At( 49 | value = "INVOKE", 50 | target = "Ljava/nio/ByteBuffer;limit(I)Ljava/nio/Buffer;", 51 | shift = At.Shift.AFTER 52 | ), 53 | cancellable = true 54 | ) 55 | private void smoothbeta_uploadTerrain(CallbackInfo ci) { 56 | if (!smoothbeta_renderingTerrain) return; 57 | smoothbeta_chunkRenderer.smoothbeta_getCurrentBuffer().upload(byteBuffer); 58 | reset(); 59 | ci.cancel(); 60 | } 61 | 62 | @ModifyConstant( 63 | method = "vertex(DDD)V", 64 | constant = @Constant(intValue = 7) 65 | ) 66 | private int smoothbeta_prohibitExtraVertices(int constant) { 67 | return smoothbeta_renderingTerrain ? -1 : constant; 68 | } 69 | 70 | @ModifyConstant( 71 | method = "vertex(DDD)V", 72 | constant = @Constant( 73 | intValue = 8, 74 | ordinal = 2 75 | ) 76 | ) 77 | private int smoothbeta_compactVertices(int constant) { 78 | return smoothbeta_renderingTerrain ? 7 : 8; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/util/StringHelper.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | public class StringHelper { 10 | private static final Pattern FORMATTING_CODE = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]"); 11 | private static final Pattern LINE_BREAK = Pattern.compile("\\r\\n|\\v"); 12 | private static final Pattern ENDS_WITH_LINE_BREAK = Pattern.compile("(?:\\r\\n|\\v)$"); 13 | 14 | /** 15 | * {@return the length of the {@code tick} in the MM:SS format, where 16 | * the MM is the minutes and SS is the seconds (optionally zero-padded)} 17 | */ 18 | public static String formatTicks(int ticks) { 19 | int i = ticks / 20; 20 | int j = i / 60; 21 | if ((i %= 60) < 10) { 22 | return j + ":0" + i; 23 | } 24 | return j + ":" + i; 25 | } 26 | 27 | /** 28 | * {@return the {@code text} with all formatting codes removed} 29 | * 30 | *

A formatting code is the character {@code \u00a7} followed by 31 | * a numeric character or a letter A to F, K to O, or R. 32 | * 33 | * @see Formatting#strip 34 | */ 35 | public static String stripTextFormat(String text) { 36 | return FORMATTING_CODE.matcher(text).replaceAll(""); 37 | } 38 | 39 | /** 40 | * {@return true if {@code text} is {@code null} or empty, false otherwise} 41 | */ 42 | public static boolean isEmpty(@Nullable String text) { 43 | return StringUtils.isEmpty(text); 44 | } 45 | 46 | /** 47 | * {@return {@code text} truncated to at most {@code maxLength} characters, 48 | * optionally with ellipsis} 49 | */ 50 | public static String truncate(String text, int maxLength, boolean addEllipsis) { 51 | if (text.length() <= maxLength) { 52 | return text; 53 | } 54 | if (addEllipsis && maxLength > 3) { 55 | return text.substring(0, maxLength - 3) + "..."; 56 | } 57 | return text.substring(0, maxLength); 58 | } 59 | 60 | /** 61 | * {@return the number of linebreaks in {@code text}} 62 | * 63 | *

A linebreak is either a CRLF sequence or a vertical tab (U+000B). 64 | */ 65 | public static int countLines(String text) { 66 | if (text.isEmpty()) { 67 | return 0; 68 | } 69 | Matcher matcher = LINE_BREAK.matcher(text); 70 | int i = 1; 71 | while (matcher.find()) { 72 | ++i; 73 | } 74 | return i; 75 | } 76 | 77 | /** 78 | * {@return true if {@code text} ends with a linebreak, false otherwise} 79 | * 80 | *

A linebreak is either a CRLF sequence or a vertical tab (U+000B). 81 | */ 82 | public static boolean endsWithLineBreak(String text) { 83 | return ENDS_WITH_LINE_BREAK.matcher(text).find(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/entity/SmoothEntityRegistry.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.entity; 2 | 3 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 4 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.nbt.NbtCompound; 9 | import net.minecraft.world.World; 10 | import net.modificationstation.stationapi.api.util.Util; 11 | 12 | import java.lang.invoke.LambdaMetafactory; 13 | import java.lang.invoke.MethodHandle; 14 | import java.lang.invoke.MethodHandles; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.function.Function; 18 | 19 | import static java.lang.invoke.MethodType.methodType; 20 | 21 | public class SmoothEntityRegistry { 22 | 23 | private static final Function EMPTY_CONSTRUCTOR = level -> null; 24 | private static final Map> STRING_ID_TO_CONSTRUCTOR = new HashMap<>(); 25 | private static final Int2ObjectMap> ID_TO_CONSTRUCTOR = Util.make(new Int2ObjectOpenHashMap<>(), map -> map.defaultReturnValue(EMPTY_CONSTRUCTOR)); 26 | 27 | private static Function findConstructor(Class entityClass) throws Throwable { 28 | MethodHandles.Lookup lookup = MethodHandles.lookup(); 29 | MethodHandle mh = lookup.findConstructor(entityClass, methodType(void.class, World.class)); 30 | //noinspection unchecked 31 | return (Function) LambdaMetafactory.metafactory( 32 | lookup, 33 | "apply", methodType(Function.class), 34 | mh.type().generic(), mh, mh.type() 35 | ).getTarget().invokeExact(); 36 | } 37 | 38 | public static void register(Class entityClass, String identifier, int id) { 39 | Function constructor; 40 | try { 41 | constructor = findConstructor(entityClass); 42 | } catch (Throwable e) { 43 | throw new RuntimeException(e); 44 | } 45 | STRING_ID_TO_CONSTRUCTOR.put(identifier, constructor); 46 | ID_TO_CONSTRUCTOR.put(id, constructor); 47 | } 48 | 49 | public static void registerNoID(Class entityClass, String identifier) { 50 | try { 51 | STRING_ID_TO_CONSTRUCTOR.put(identifier, findConstructor(entityClass)); 52 | } catch (Throwable e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | public static Entity create(String identifier, World level) { 58 | return STRING_ID_TO_CONSTRUCTOR.getOrDefault(identifier, EMPTY_CONSTRUCTOR).apply(level); 59 | } 60 | 61 | public static Entity create(NbtCompound tag, World level) { 62 | Entity var2 = STRING_ID_TO_CONSTRUCTOR.getOrDefault(tag.getString("id"), EMPTY_CONSTRUCTOR).apply(level); 63 | if (var2 == null) 64 | System.out.println("Skipping Entity with id " + tag.getString("id")); 65 | else 66 | var2.read(tag); 67 | return var2; 68 | } 69 | 70 | @Environment(EnvType.CLIENT) 71 | public static Entity create(int id, World level) { 72 | Entity var2 = ID_TO_CONSTRUCTOR.get(id).apply(level); 73 | if (var2 == null) 74 | System.out.println("Skipping Entity with id " + id); 75 | return var2; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/Program.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import com.google.common.collect.Maps; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.modificationstation.stationapi.api.client.texture.TextureUtil; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.lwjgl.opengl.GL20; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.Map; 13 | 14 | @Environment(EnvType.CLIENT) 15 | public class Program { 16 | 17 | private static final int MAX_LOG_LENGTH = 0x8000; 18 | private final Type shaderType; 19 | private final String name; 20 | private int shaderRef; 21 | 22 | protected Program(Type shaderType, int shaderRef, String name) { 23 | this.shaderType = shaderType; 24 | this.shaderRef = shaderRef; 25 | this.name = name; 26 | } 27 | 28 | public void attachTo(GlShader program) { 29 | GL20.glAttachShader(program.getProgramRef(), this.getShaderRef()); 30 | } 31 | 32 | public void release() { 33 | if (this.shaderRef != -1) { 34 | GL20.glDeleteShader(this.shaderRef); 35 | this.shaderRef = -1; 36 | this.shaderType.getProgramCache().remove(this.name); 37 | } 38 | } 39 | 40 | public String getName() { 41 | return this.name; 42 | } 43 | 44 | public static Program createFromResource(Type type, String name, InputStream stream, String domain, GLImportProcessor loader) throws IOException { 45 | int i = loadProgram(type, name, stream, domain, loader); 46 | Program program = new Program(type, i, name); 47 | type.getProgramCache().put(name, program); 48 | return program; 49 | } 50 | 51 | protected static int loadProgram(Type type, String name, InputStream stream, String domain, GLImportProcessor loader) throws IOException { 52 | String string = TextureUtil.readResourceAsString(stream); 53 | if (string == null) throw new IOException("Could not load program " + type.getName()); 54 | else { 55 | int i = GL20.glCreateShader(type.getGlType()); 56 | GlStateManager.glShaderSource(i, loader.readSource(string)); 57 | GL20.glCompileShader(i); 58 | if (GL20.glGetShaderi(i, GL20.GL_COMPILE_STATUS) == 0) { 59 | String string2 = StringUtils.trim(GL20.glGetShaderInfoLog(i, MAX_LOG_LENGTH)); 60 | throw new IOException("Couldn't compile " + type.getName() + " program (" + domain + ", " + name + ") : " + string2); 61 | } else return i; 62 | } 63 | } 64 | 65 | protected int getShaderRef() { 66 | return this.shaderRef; 67 | } 68 | 69 | @Environment(EnvType.CLIENT) 70 | public enum Type { 71 | VERTEX("vertex", ".vsh", GL20.GL_VERTEX_SHADER), 72 | FRAGMENT("fragment", ".fsh", GL20.GL_FRAGMENT_SHADER); 73 | 74 | private final String name; 75 | private final String fileExtension; 76 | private final int glType; 77 | private final Map programCache = Maps.newHashMap(); 78 | 79 | Type(String name, String extension, int glType) { 80 | this.name = name; 81 | this.fileExtension = extension; 82 | this.glType = glType; 83 | } 84 | 85 | public String getName() { 86 | return this.name; 87 | } 88 | 89 | public String getFileExtension() { 90 | return this.fileExtension; 91 | } 92 | 93 | int getGlType() { 94 | return this.glType; 95 | } 96 | 97 | /** 98 | * Gets a map of loaded shaders. 99 | */ 100 | public Map getProgramCache() { 101 | return this.programCache; 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormatElement.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | 6 | /** 7 | * Represents a singular field within a larger {@link 8 | * VertexFormat vertex format}. 9 | * 10 | *

This element comprises a component type, the number of components, 11 | * and a type that describes how the components should be interpreted. 12 | */ 13 | @Environment(EnvType.CLIENT) 14 | public class VertexFormatElement { 15 | private final ComponentType componentType; 16 | private final Type type; 17 | private final int uvIndex; 18 | private final int componentCount; 19 | /** 20 | * The total length of this element (in bytes). 21 | */ 22 | private final int byteLength; 23 | 24 | public VertexFormatElement(int uvIndex, ComponentType componentType, Type type, int componentCount) { 25 | if (!this.isValidType(uvIndex, type)) 26 | throw new IllegalStateException("Multiple vertex elements of the same type other than UVs are not supported"); 27 | this.type = type; 28 | this.componentType = componentType; 29 | this.uvIndex = uvIndex; 30 | this.componentCount = componentCount; 31 | this.byteLength = componentType.getByteLength() * this.componentCount; 32 | } 33 | 34 | private boolean isValidType(int uvIndex, Type type) { 35 | return uvIndex == 0 || type == Type.UV; 36 | } 37 | 38 | public String toString() { 39 | return this.componentCount + "," + this.type.getName() + "," + this.componentType.getName(); 40 | } 41 | 42 | public final int getByteLength() { 43 | return this.byteLength; 44 | } 45 | 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (o == null || this.getClass() != o.getClass()) return false; 49 | VertexFormatElement vertexFormatElement = (VertexFormatElement)o; 50 | if (this.componentCount != vertexFormatElement.componentCount) return false; 51 | if (this.uvIndex != vertexFormatElement.uvIndex) return false; 52 | if (this.componentType != vertexFormatElement.componentType) return false; 53 | return this.type == vertexFormatElement.type; 54 | } 55 | 56 | public int hashCode() { 57 | int i = this.componentType.hashCode(); 58 | i = 31 * i + this.type.hashCode(); 59 | i = 31 * i + this.uvIndex; 60 | i = 31 * i + this.componentCount; 61 | return i; 62 | } 63 | 64 | @Environment(value=EnvType.CLIENT) 65 | public enum Type { 66 | POSITION("Position"), 67 | NORMAL("Normal"), 68 | COLOR("Vertex Color"), 69 | UV("UV"), 70 | PADDING("Padding"); 71 | 72 | private final String name; 73 | 74 | Type(String name) { 75 | this.name = name; 76 | } 77 | 78 | public String getName() { 79 | return this.name; 80 | } 81 | } 82 | 83 | @Environment(value=EnvType.CLIENT) 84 | public enum ComponentType { 85 | FLOAT(4, "Float"), 86 | UBYTE(1, "Unsigned Byte"), 87 | BYTE(1, "Byte"); 88 | 89 | private final int byteLength; 90 | private final String name; 91 | 92 | ComponentType(int byteLength, String name) { 93 | this.byteLength = byteLength; 94 | this.name = name; 95 | } 96 | 97 | public int getByteLength() { 98 | return this.byteLength; 99 | } 100 | 101 | public String getName() { 102 | return this.name; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/Shaders.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import net.mine_diver.smoothbeta.client.render.gl.Program; 4 | import net.mine_diver.unsafeevents.listener.EventListener; 5 | import net.modificationstation.stationapi.api.client.event.resource.AssetsResourceReloaderRegisterEvent; 6 | import net.modificationstation.stationapi.api.mod.entrypoint.EntrypointManager; 7 | import net.modificationstation.stationapi.api.resource.IdentifiableResourceReloadListener; 8 | import net.modificationstation.stationapi.api.resource.ResourceManager; 9 | import net.modificationstation.stationapi.api.util.Identifier; 10 | import net.modificationstation.stationapi.api.util.profiler.Profiler; 11 | 12 | import java.io.IOException; 13 | import java.lang.invoke.MethodHandles; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.concurrent.Executor; 18 | import java.util.function.Supplier; 19 | 20 | import static net.mine_diver.smoothbeta.SmoothBeta.NAMESPACE; 21 | 22 | public class Shaders implements IdentifiableResourceReloadListener { 23 | static { 24 | EntrypointManager.registerLookup(MethodHandles.lookup()); 25 | } 26 | 27 | public static final Identifier ID = NAMESPACE.id("shaders"); 28 | 29 | private static Shader terrainShader; 30 | 31 | @EventListener 32 | void registerShaderReloader(AssetsResourceReloaderRegisterEvent event) { 33 | event.resourceManager.registerReloader(this); 34 | } 35 | 36 | private record Application( 37 | Runnable clearCache, 38 | Supplier shaderFactory 39 | ) {} 40 | 41 | private static Application loadShaders(ResourceManager manager, Profiler profiler) { 42 | profiler.startTick(); 43 | 44 | profiler.push("cache_release"); 45 | List list = new ArrayList<>(); 46 | list.addAll(Program.Type.FRAGMENT.getProgramCache().values()); 47 | list.addAll(Program.Type.VERTEX.getProgramCache().values()); 48 | 49 | profiler.swap("shader_factory"); 50 | Supplier shaderFactory = () -> { 51 | try { 52 | return new Shader(manager, "terrain", VertexFormats.POSITION_TEXTURE_COLOR_NORMAL); 53 | } catch (IOException e) { 54 | throw new RuntimeException("Could not reload terrain shader", e); 55 | } 56 | }; 57 | 58 | profiler.pop(); 59 | profiler.endTick(); 60 | return new Application(() -> list.forEach(Program::release), shaderFactory); 61 | } 62 | 63 | private static void applyShader(Application application, Profiler profiler) { 64 | profiler.startTick(); 65 | 66 | profiler.push("cache_release"); 67 | application.clearCache.run(); 68 | 69 | if (terrainShader != null) { 70 | profiler.swap("delete_shader"); 71 | terrainShader.close(); 72 | } 73 | 74 | profiler.swap("load_shader"); 75 | terrainShader = application.shaderFactory.get(); 76 | 77 | profiler.pop(); 78 | profiler.endTick(); 79 | } 80 | 81 | public static Shader getTerrainShader() { 82 | return terrainShader; 83 | } 84 | 85 | @Override 86 | public CompletableFuture reload(Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor) { 87 | return CompletableFuture 88 | .supplyAsync(() -> loadShaders(manager, prepareProfiler), prepareExecutor) 89 | .thenCompose(synchronizer::whenPrepared) 90 | .thenAcceptAsync(shaderFactory -> applyShader(shaderFactory, applyProfiler), applyExecutor); 91 | } 92 | 93 | @Override 94 | public Identifier getId() { 95 | return ID; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/VertexFormat.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.ImmutableMap; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import org.lwjgl.opengl.GL11; 8 | 9 | import java.util.stream.Collectors; 10 | 11 | @Environment(EnvType.CLIENT) 12 | public class VertexFormat { 13 | private final ImmutableMap elementMap; 14 | private final int vertexSizeByte; 15 | 16 | public VertexFormat(ImmutableMap elementMap) { 17 | this.elementMap = elementMap; 18 | int i = 0; 19 | for (VertexFormatElement vertexFormatElement : elementMap.values()) 20 | i += vertexFormatElement.getByteLength(); 21 | this.vertexSizeByte = i; 22 | } 23 | 24 | public String toString() { 25 | return "format: " + this.elementMap.size() + " elements: " + this.elementMap.entrySet().stream().map(Object::toString).collect(Collectors.joining(" ")); 26 | } 27 | 28 | public int getVertexSizeByte() { 29 | return this.vertexSizeByte; 30 | } 31 | 32 | public ImmutableList getAttributeNames() { 33 | return this.elementMap.keySet().asList(); 34 | } 35 | 36 | public boolean equals(Object o) { 37 | if (this == o) { 38 | return true; 39 | } 40 | if (o == null || this.getClass() != o.getClass()) { 41 | return false; 42 | } 43 | VertexFormat vertexFormat = (VertexFormat)o; 44 | if (this.vertexSizeByte != vertexFormat.vertexSizeByte) { 45 | return false; 46 | } 47 | return this.elementMap.equals(vertexFormat.elementMap); 48 | } 49 | 50 | public int hashCode() { 51 | return this.elementMap.hashCode(); 52 | } 53 | 54 | @Environment(value=EnvType.CLIENT) 55 | public enum DrawMode { 56 | LINES(4, 2, 2, false), 57 | LINE_STRIP(5, 2, 1, true), 58 | DEBUG_LINES(1, 2, 2, false), 59 | DEBUG_LINE_STRIP(3, 2, 1, true), 60 | TRIANGLES(4, 3, 3, false), 61 | TRIANGLE_STRIP(5, 3, 1, true), 62 | TRIANGLE_FAN(6, 3, 1, true), 63 | QUADS(4, 4, 4, false); 64 | 65 | public final int glMode; 66 | public final int firstVertexCount; 67 | public final int additionalVertexCount; 68 | public final boolean shareVertices; 69 | 70 | DrawMode(int glMode, int firstVertexCount, int additionalVertexCount, boolean shareVertices) { 71 | this.glMode = glMode; 72 | this.firstVertexCount = firstVertexCount; 73 | this.additionalVertexCount = additionalVertexCount; 74 | this.shareVertices = shareVertices; 75 | } 76 | 77 | public int getIndexCount(int vertexCount) { 78 | return switch (this) { 79 | case LINE_STRIP, DEBUG_LINES, DEBUG_LINE_STRIP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN -> vertexCount; 80 | case LINES, QUADS -> vertexCount / 4 * 6; 81 | }; 82 | } 83 | } 84 | 85 | @Environment(value=EnvType.CLIENT) 86 | public enum IndexType { 87 | BYTE(GL11.GL_UNSIGNED_BYTE, 1), 88 | SHORT(GL11.GL_UNSIGNED_SHORT, 2), 89 | INT(GL11.GL_UNSIGNED_INT, 4); 90 | 91 | public final int glType; 92 | public final int size; 93 | 94 | IndexType(int glType, int size) { 95 | this.glType = glType; 96 | this.size = size; 97 | } 98 | 99 | public static IndexType smallestFor(int indexCount) { 100 | if ((indexCount & 0xFFFF0000) != 0) { 101 | return INT; 102 | } 103 | if ((indexCount & 0xFF00) != 0) { 104 | return SHORT; 105 | } 106 | return BYTE; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/IndexBuffer.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.modificationstation.stationapi.api.util.math.MathHelper; 6 | import org.lwjgl.opengl.GL15; 7 | 8 | import java.nio.ByteBuffer; 9 | import java.util.function.IntConsumer; 10 | 11 | import static net.mine_diver.smoothbeta.SmoothBeta.LOGGER; 12 | 13 | @Environment(EnvType.CLIENT) 14 | public final class IndexBuffer { 15 | private static final IndexBuffer sharedSequential = new IndexBuffer(1, 1, IntConsumer::accept); 16 | private static final IndexBuffer sharedSequentialQuad = new IndexBuffer(4, 6, (intConsumer, i) -> { 17 | intConsumer.accept(i); 18 | intConsumer.accept(i + 1); 19 | intConsumer.accept(i + 2); 20 | intConsumer.accept(i + 2); 21 | intConsumer.accept(i + 3); 22 | intConsumer.accept(i); 23 | }); 24 | private static final IndexBuffer sharedSequentialLines = new IndexBuffer(4, 6, (intConsumer, i) -> { 25 | intConsumer.accept(i); 26 | intConsumer.accept(i + 1); 27 | intConsumer.accept(i + 2); 28 | intConsumer.accept(i + 3); 29 | intConsumer.accept(i + 2); 30 | intConsumer.accept(i + 1); 31 | }); 32 | 33 | public static IndexBuffer getSequentialBuffer(VertexFormat.DrawMode drawMode) { 34 | return switch (drawMode) { 35 | case QUADS -> sharedSequentialQuad; 36 | case LINES -> sharedSequentialLines; 37 | default -> sharedSequential; 38 | }; 39 | } 40 | 41 | private final int sizeMultiplier; 42 | private final int increment; 43 | private final IndexMapper indexMapper; 44 | private int id; 45 | private VertexFormat.IndexType indexType = VertexFormat.IndexType.BYTE; 46 | private int size; 47 | 48 | IndexBuffer(int sizeMultiplier, int increment, IndexMapper indexMapper) { 49 | this.sizeMultiplier = sizeMultiplier; 50 | this.increment = increment; 51 | this.indexMapper = indexMapper; 52 | } 53 | 54 | public boolean isSizeLessThanOrEqual(int size) { 55 | return size <= this.size; 56 | } 57 | 58 | public void bindAndGrow(int newSize) { 59 | if (this.id == 0) this.id = GL15.glGenBuffers(); 60 | GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.id); 61 | this.grow(newSize); 62 | } 63 | 64 | private void grow(int newSize) { 65 | if (this.isSizeLessThanOrEqual(newSize)) return; 66 | newSize = MathHelper.roundUpToMultiple(newSize * 2, this.increment); 67 | LOGGER.debug("Growing IndexBuffer: Old limit {}, new limit {}.", this.size, newSize); 68 | VertexFormat.IndexType indexType = VertexFormat.IndexType.smallestFor(newSize); 69 | int i = MathHelper.roundUpToMultiple(newSize * indexType.size, 4); 70 | GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, i, GL15.GL_DYNAMIC_DRAW); 71 | ByteBuffer byteBuffer = GL15.glMapBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_WRITE_ONLY, null); 72 | if (byteBuffer == null) throw new RuntimeException("Failed to map GL buffer"); 73 | this.indexType = indexType; 74 | IntConsumer intConsumer = this.getIndexConsumer(byteBuffer); 75 | for (int j = 0; j < newSize; j += this.increment) 76 | this.indexMapper.accept(intConsumer, j * this.sizeMultiplier / this.increment); 77 | GL15.glUnmapBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER); 78 | this.size = newSize; 79 | } 80 | 81 | private IntConsumer getIndexConsumer(ByteBuffer indicesBuffer) { 82 | return switch (this.indexType) { 83 | case BYTE -> index -> indicesBuffer.put((byte) index); 84 | case SHORT -> index -> indicesBuffer.putShort((short) index); 85 | default -> indicesBuffer::putInt; 86 | }; 87 | } 88 | 89 | public VertexFormat.IndexType getIndexType() { 90 | return this.indexType; 91 | } 92 | 93 | @Environment(EnvType.CLIENT) 94 | interface IndexMapper { 95 | void accept(IntConsumer var1, int var2); 96 | } 97 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/multidraw/ChunkRendererMixin.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client.multidraw; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | import net.mine_diver.smoothbeta.client.render.SmoothChunkRenderer; 5 | import net.mine_diver.smoothbeta.client.render.SmoothTessellator; 6 | import net.mine_diver.smoothbeta.client.render.SmoothWorldRenderer; 7 | import net.mine_diver.smoothbeta.client.render.VboPool; 8 | import net.mine_diver.smoothbeta.client.render.gl.VertexBuffer; 9 | import net.minecraft.block.entity.BlockEntity; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.client.render.Tessellator; 12 | import net.minecraft.client.render.block.BlockRenderManager; 13 | import net.minecraft.client.render.chunk.ChunkBuilder; 14 | import net.minecraft.world.WorldRegion; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Shadow; 17 | import org.spongepowered.asm.mixin.Unique; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 21 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 22 | 23 | import java.util.HashSet; 24 | 25 | @Mixin(ChunkBuilder.class) 26 | class ChunkRendererMixin implements SmoothChunkRenderer { 27 | @Shadow private static Tessellator tessellator; 28 | 29 | @Shadow public boolean[] renderLayerEmpty; 30 | @Shadow public int renderX; 31 | @Shadow public int renderY; 32 | @Shadow public int renderZ; 33 | @Unique 34 | private VertexBuffer[] smoothbeta_buffers; 35 | @Unique 36 | private int smoothbeta_currentBufferIndex = -1; 37 | 38 | @Override 39 | @Unique 40 | public VertexBuffer smoothbeta_getBuffer(int pass) { 41 | return smoothbeta_buffers[pass]; 42 | } 43 | 44 | @Override 45 | @Unique 46 | public VertexBuffer smoothbeta_getCurrentBuffer() { 47 | return smoothbeta_buffers[smoothbeta_currentBufferIndex]; 48 | } 49 | 50 | @Inject( 51 | method = "", 52 | at = @At("RETURN") 53 | ) 54 | private void smoothbeta_init(CallbackInfo ci) { 55 | smoothbeta_buffers = new VertexBuffer[renderLayerEmpty.length]; 56 | //noinspection deprecation 57 | VboPool pool = ((SmoothWorldRenderer) ((Minecraft) FabricLoader.getInstance().getGameInstance()).worldRenderer).smoothbeta_getTerrainVboPool(); 58 | for (int i = 0; i < smoothbeta_buffers.length; i++) 59 | smoothbeta_buffers[i] = new VertexBuffer(pool); 60 | } 61 | 62 | @Inject( 63 | method = "rebuild", 64 | at = @At( 65 | value = "INVOKE", 66 | target = "Lnet/minecraft/client/render/Tessellator;startQuads()V" 67 | ), 68 | locals = LocalCapture.CAPTURE_FAILHARD 69 | ) 70 | private void smoothbeta_startRenderingTerrain( 71 | CallbackInfo ci, 72 | int var1, int var2, int var3, int var4, int var5, int var6, HashSet var7, int var8, WorldRegion var9, BlockRenderManager var10, int var11 73 | ) { 74 | smoothbeta_currentBufferIndex = var11; 75 | ((SmoothTessellator) tessellator).smoothbeta_startRenderingTerrain(this); 76 | } 77 | 78 | @Inject( 79 | method = "rebuild", 80 | at = @At( 81 | value = "INVOKE", 82 | target = "Lnet/minecraft/client/render/Tessellator;translate(DDD)V", 83 | shift = At.Shift.AFTER, 84 | ordinal = 0 85 | ) 86 | ) 87 | private void smoothbeta_offsetBufferData(CallbackInfo ci) { 88 | tessellator.translate(this.renderX, this.renderY, this.renderZ); 89 | } 90 | 91 | @Inject( 92 | method = "rebuild", 93 | at = @At( 94 | value = "INVOKE", 95 | target = "Lnet/minecraft/client/render/Tessellator;draw()V", 96 | shift = At.Shift.AFTER 97 | ) 98 | ) 99 | private void smoothbeta_stopRenderingTerrain(CallbackInfo ci) { 100 | smoothbeta_currentBufferIndex = -1; 101 | ((SmoothTessellator) tessellator).smoothbeta_stopRenderingTerrain(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlBlendState.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.lwjgl.opengl.GL11; 7 | import org.lwjgl.opengl.GL14; 8 | 9 | import java.util.Locale; 10 | 11 | @Environment(EnvType.CLIENT) 12 | public class GlBlendState { 13 | @Nullable 14 | private static GlBlendState activeBlendState; 15 | private final int srcRgb; 16 | private final int srcAlpha; 17 | private final int dstRgb; 18 | private final int dstAlpha; 19 | private final int func; 20 | private final boolean separateBlend; 21 | private final boolean blendDisabled; 22 | 23 | private GlBlendState(boolean separateBlend, boolean blendDisabled, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha, int func) { 24 | this.separateBlend = separateBlend; 25 | this.srcRgb = srcRgb; 26 | this.dstRgb = dstRgb; 27 | this.srcAlpha = srcAlpha; 28 | this.dstAlpha = dstAlpha; 29 | this.blendDisabled = blendDisabled; 30 | this.func = func; 31 | } 32 | 33 | public GlBlendState() { 34 | this(false, true, 1, 0, 1, 0, GL14.GL_FUNC_ADD); 35 | } 36 | 37 | public GlBlendState(int srcRgb, int dstRgb, int func) { 38 | this(false, false, srcRgb, dstRgb, srcRgb, dstRgb, func); 39 | } 40 | 41 | public GlBlendState(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha, int func) { 42 | this(true, false, srcRgb, dstRgb, srcAlpha, dstAlpha, func); 43 | } 44 | 45 | public void enable() { 46 | if (!this.equals(activeBlendState)) { 47 | if (activeBlendState == null || this.blendDisabled != activeBlendState.isBlendDisabled()) { 48 | activeBlendState = this; 49 | if (this.blendDisabled) { 50 | GlStateManager._disableBlend(); 51 | return; 52 | } 53 | 54 | GlStateManager._enableBlend(); 55 | } 56 | 57 | GL14.glBlendEquation(this.func); 58 | if (this.separateBlend) 59 | GlStateManager._blendFuncSeparate(this.srcRgb, this.dstRgb, this.srcAlpha, this.dstAlpha); 60 | else GlStateManager._blendFunc(this.srcRgb, this.dstRgb); 61 | 62 | } 63 | } 64 | 65 | public boolean equals(Object o) { 66 | if (this == o) return true; 67 | else if (!(o instanceof GlBlendState glBlendState)) return false; 68 | else if (this.func != glBlendState.func) return false; 69 | else if (this.dstAlpha != glBlendState.dstAlpha) return false; 70 | else if (this.dstRgb != glBlendState.dstRgb) return false; 71 | else if (this.blendDisabled != glBlendState.blendDisabled) return false; 72 | else if (this.separateBlend != glBlendState.separateBlend) return false; 73 | else if (this.srcAlpha != glBlendState.srcAlpha) return false; 74 | else return this.srcRgb == glBlendState.srcRgb; 75 | } 76 | 77 | public int hashCode() { 78 | int i = this.srcRgb; 79 | i = 31 * i + this.srcAlpha; 80 | i = 31 * i + this.dstRgb; 81 | i = 31 * i + this.dstAlpha; 82 | i = 31 * i + this.func; 83 | i = 31 * i + (this.separateBlend ? 1 : 0); 84 | i = 31 * i + (this.blendDisabled ? 1 : 0); 85 | return i; 86 | } 87 | 88 | public boolean isBlendDisabled() { 89 | return this.blendDisabled; 90 | } 91 | 92 | public static int getFuncFromString(String name) { 93 | String string = name.trim().toLowerCase(Locale.ROOT); 94 | return switch (string) { 95 | case "add" -> GL14.GL_FUNC_ADD; 96 | case "subtract" -> GL14.GL_FUNC_SUBTRACT; 97 | case "reversesubtract", "reverse_subtract" -> GL14.GL_FUNC_REVERSE_SUBTRACT; 98 | case "min" -> GL14.GL_MIN; 99 | default -> "max".equals(string) ? GL14.GL_MAX : GL14.GL_FUNC_ADD; 100 | }; 101 | } 102 | 103 | public static int getComponentFromString(String expression) { 104 | String string = expression.trim().toLowerCase(Locale.ROOT); 105 | string = string.replaceAll("_", ""); 106 | string = string.replaceAll("one", "1"); 107 | string = string.replaceAll("zero", "0"); 108 | string = string.replaceAll("minus", "-"); 109 | return switch (string) { 110 | case "0" -> 0; 111 | case "1" -> 1; 112 | case "srccolor" -> GL11.GL_SRC_COLOR; 113 | case "1-srccolor" -> GL11.GL_ONE_MINUS_SRC_COLOR; 114 | case "dstcolor" -> GL11.GL_DST_COLOR; 115 | case "1-dstcolor" -> GL11.GL_ONE_MINUS_DST_COLOR; 116 | case "srcalpha" -> GL11.GL_SRC_ALPHA; 117 | case "1-srcalpha" -> GL11.GL_ONE_MINUS_SRC_ALPHA; 118 | case "dstalpha" -> GL11.GL_DST_ALPHA; 119 | default -> "1-dstalpha".equals(string) ? GL11.GL_ONE_MINUS_DST_ALPHA : -1; 120 | }; 121 | } 122 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/multidraw/compat/stationrendererapi/StationTessellatorImplMixin.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client.multidraw.compat.stationrendererapi; 2 | 3 | import net.mine_diver.smoothbeta.client.render.SmoothTessellator; 4 | import net.minecraft.client.render.Tessellator; 5 | import net.modificationstation.stationapi.api.client.render.model.BakedQuad; 6 | import net.modificationstation.stationapi.impl.client.render.StationTessellatorImpl; 7 | import net.modificationstation.stationapi.mixin.render.client.TessellatorAccessor; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(StationTessellatorImpl.class) 16 | abstract class StationTessellatorImplMixin { 17 | @Shadow @Final private Tessellator self; 18 | 19 | @Shadow @Final private int[] fastVertexData; 20 | @Shadow @Final private TessellatorAccessor access; 21 | 22 | @Shadow public abstract void ensureBufferCapacity(int criticalCapacity); 23 | 24 | @Inject( 25 | method = "quad", 26 | at = @At("HEAD"), 27 | cancellable = true, 28 | remap = false 29 | ) 30 | private void smoothbeta_renderTerrain(BakedQuad quad, float x, float y, float z, int colour0, int colour1, int colour2, int colour3, float normalX, float normalY, float normalZ, boolean spreadUV, CallbackInfo ci) { 31 | if (((SmoothTessellator) self).smoothbeta_isRenderingTerrain()) { 32 | byte by = (byte)(normalX * 128.0f); 33 | byte by2 = (byte)(normalY * 127.0f); 34 | byte by3 = (byte)(normalZ * 127.0f); 35 | int normal = by | by2 << 8 | by3 << 16; 36 | int[] vertexData = quad.getVertexData(); 37 | System.arraycopy(vertexData, 0, fastVertexData, 0, 7); 38 | System.arraycopy(vertexData, 8, fastVertexData, 7, 7); 39 | System.arraycopy(vertexData, 16, fastVertexData, 14, 7); 40 | System.arraycopy(vertexData, 24, fastVertexData, 21, 7); 41 | fastVertexData[0] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[0]) + x + access.getXOffset())); 42 | fastVertexData[1] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[1]) + y + access.getYOffset())); 43 | fastVertexData[2] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[2]) + z + access.getZOffset())); 44 | fastVertexData[6] = normal; 45 | fastVertexData[7] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[7]) + x + access.getXOffset())); 46 | fastVertexData[8] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[8]) + y + access.getYOffset())); 47 | fastVertexData[9] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[9]) + z + access.getZOffset())); 48 | fastVertexData[13] = normal; 49 | fastVertexData[14] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[14]) + x + access.getXOffset())); 50 | fastVertexData[15] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[15]) + y + access.getYOffset())); 51 | fastVertexData[16] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[16]) + z + access.getZOffset())); 52 | fastVertexData[20] = normal; 53 | fastVertexData[21] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[21]) + x + access.getXOffset())); 54 | fastVertexData[22] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[22]) + y + access.getYOffset())); 55 | fastVertexData[23] = Float.floatToRawIntBits((float) (Float.intBitsToFloat(fastVertexData[23]) + z + access.getZOffset())); 56 | fastVertexData[27] = normal; 57 | if (!access.getColorDisabled()) { 58 | fastVertexData[5] = colour0; 59 | fastVertexData[12] = colour1; 60 | fastVertexData[19] = colour2; 61 | fastVertexData[26] = colour3; 62 | access.setHasColor(true); 63 | } 64 | access.setHasTexture(true); 65 | access.setHasNormals(true); 66 | System.arraycopy(fastVertexData, 0, access.stationapi$getBuffer(), access.stationapi$getBufferPosition(), 28); 67 | access.stationapi$setAddedVertexCount(access.stationapi$getAddedVertexCount() + 4); 68 | access.stationapi$setBufferPosition(access.stationapi$getBufferPosition() + 28); 69 | access.stationapi$setVertexCount(access.stationapi$getVertexCount() + 4); 70 | ensureBufferCapacity(28); 71 | ci.cancel(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/mixin/client/multidraw/WorldRendererMixin.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.mixin.client.multidraw; 2 | 3 | import net.mine_diver.smoothbeta.client.render.*; 4 | import net.minecraft.client.render.WorldRenderer; 5 | import net.minecraft.client.render.chunk.ChunkBuilder; 6 | import net.minecraft.client.render.world.ChunkRenderer; 7 | import net.minecraft.client.util.GlAllocationUtils; 8 | import net.minecraft.entity.LivingEntity; 9 | import org.lwjgl.opengl.GL11; 10 | import org.lwjgl.opengl.GL15; 11 | import org.lwjgl.opengl.GL20; 12 | import org.lwjgl.opengl.GL30; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | import org.spongepowered.asm.mixin.Unique; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.Redirect; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 21 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 22 | 23 | import java.nio.FloatBuffer; 24 | 25 | @Mixin(WorldRenderer.class) 26 | abstract class WorldRendererMixin implements SmoothWorldRenderer { 27 | @Shadow private ChunkRenderer[] chunkRenderers; 28 | 29 | @Unique 30 | private VboPool smoothbeta_vboPool; 31 | 32 | @Override 33 | @Unique 34 | public VboPool smoothbeta_getTerrainVboPool() { 35 | return smoothbeta_vboPool; 36 | } 37 | 38 | @Inject( 39 | method = "reload()V", 40 | at = @At("HEAD") 41 | ) 42 | private void smoothbeta_resetVboPool(CallbackInfo ci) { 43 | if (smoothbeta_vboPool != null) 44 | smoothbeta_vboPool.deleteGlBuffers(); 45 | smoothbeta_vboPool = new VboPool(VertexFormats.POSITION_TEXTURE_COLOR_NORMAL); 46 | } 47 | 48 | @Redirect( 49 | method = "", 50 | at = @At( 51 | value = "NEW", 52 | target = "()Lnet/minecraft/client/render/world/ChunkRenderer;" 53 | ) 54 | ) 55 | private ChunkRenderer smoothbeta_injectRenderRegion() { 56 | return new RenderRegion((WorldRenderer) (Object) this); 57 | } 58 | 59 | @Inject( 60 | method = "renderChunks(IIID)I", 61 | at = @At( 62 | value = "INVOKE", 63 | target = "Lnet/minecraft/client/render/world/ChunkRenderer;addGlList(I)V", 64 | shift = At.Shift.BEFORE 65 | ), 66 | locals = LocalCapture.CAPTURE_FAILHARD 67 | ) 68 | private void smoothbeta_addBufferToRegion(int j, int k, int d, double par4, CallbackInfoReturnable cir, int var6, LivingEntity var7, double var8, double var10, double var12, int var14, int var15, ChunkBuilder var16, int var17) { 69 | ((RenderRegion) this.chunkRenderers[var17]).addBuffer(((SmoothChunkRenderer) var16).smoothbeta_getBuffer(d)); 70 | } 71 | 72 | @Redirect( 73 | method = "renderChunks(IIID)I", 74 | at = @At( 75 | value = "INVOKE", 76 | target = "Lnet/minecraft/client/render/world/ChunkRenderer;addGlList(I)V" 77 | ) 78 | ) 79 | private void smoothbeta_stopCallingRenderList(ChunkRenderer instance, int i) {} 80 | 81 | @Unique 82 | private final FloatBuffer 83 | smoothbeta_modelViewMatrix = GlAllocationUtils.allocateFloatBuffer(16), 84 | smoothbeta_projectionMatrix = GlAllocationUtils.allocateFloatBuffer(16); 85 | 86 | @Inject( 87 | method = "renderLastChunks(ID)V", 88 | at = @At("HEAD") 89 | ) 90 | public void smoothbeta_beforeRenderRegion(int d, double par2, CallbackInfo ci) { 91 | Shader shader = Shaders.getTerrainShader(); 92 | 93 | shader.addSampler("Sampler0", 0); 94 | 95 | GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, smoothbeta_modelViewMatrix.clear()); 96 | shader.modelViewMat.set(smoothbeta_modelViewMatrix.position(0)); 97 | 98 | GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, smoothbeta_projectionMatrix.clear()); 99 | shader.projectionMat.set(smoothbeta_projectionMatrix.position(0)); 100 | 101 | shader.fogMode.set(switch (GL11.glGetInteger(GL11.GL_FOG_MODE)) { 102 | case GL11.GL_EXP -> 0; 103 | case GL11.GL_EXP2 -> 1; 104 | case GL11.GL_LINEAR -> 2; 105 | default -> throw new IllegalStateException("Unexpected value: " + GL11.glGetInteger(GL11.GL_FOG_MODE)); 106 | }); 107 | 108 | shader.bind(); 109 | } 110 | 111 | @Inject( 112 | method = "renderLastChunks(ID)V", 113 | at = @At("RETURN") 114 | ) 115 | public void smoothbeta_afterRenderRegion(int d, double par2, CallbackInfo ci) { 116 | Shaders.getTerrainShader().unbind(); 117 | 118 | GL20.glDisableVertexAttribArray(0); // pos 119 | GL20.glDisableVertexAttribArray(1); // texture 120 | GL20.glDisableVertexAttribArray(2); // color 121 | GL20.glDisableVertexAttribArray(3); // normal 122 | 123 | GL30.glBindVertexArray(0); 124 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); 125 | GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlStateManager.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import com.google.common.base.Charsets; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.minecraft.client.util.GlAllocationUtils; 7 | import net.modificationstation.stationapi.api.util.Util; 8 | import org.lwjgl.opengl.*; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.util.List; 12 | import java.util.stream.IntStream; 13 | 14 | public class GlStateManager { 15 | private static final boolean ON_LINUX = Util.getOperatingSystem() == Util.OperatingSystem.LINUX; 16 | private static final BlendFuncState BLEND = new BlendFuncState(); 17 | private static int activeTexture; 18 | private static final Texture2DState[] TEXTURES = IntStream.range(0, 12).mapToObj(i -> new Texture2DState()).toArray(Texture2DState[]::new); 19 | 20 | public static void _glDeleteBuffers(int buffer) { 21 | if (ON_LINUX) { 22 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer); 23 | GL15.glBufferData(GL15.GL_ARRAY_BUFFER, 0L, GL15.GL_DYNAMIC_DRAW); 24 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); 25 | } 26 | GL15.glDeleteBuffers(buffer); 27 | } 28 | 29 | public static void glShaderSource(int shader, List strings) { 30 | StringBuilder stringBuilder = new StringBuilder(); 31 | for (String string : strings) { 32 | stringBuilder.append(string); 33 | } 34 | byte[] bs = stringBuilder.toString().getBytes(Charsets.UTF_8); 35 | ByteBuffer byteBuffer = GlAllocationUtils.allocateByteBuffer(bs.length + 1); 36 | byteBuffer.put(bs); 37 | byteBuffer.put((byte)0); 38 | byteBuffer.flip(); 39 | GL20.glShaderSource(shader, byteBuffer); 40 | } 41 | 42 | public static void _disableBlend() { 43 | GlStateManager.BLEND.capState.disable(); 44 | } 45 | 46 | public static void _enableBlend() { 47 | GlStateManager.BLEND.capState.enable(); 48 | } 49 | 50 | public static void _blendFunc(int srcFactor, int dstFactor) { 51 | if (srcFactor != GlStateManager.BLEND.srcFactorRGB || dstFactor != GlStateManager.BLEND.dstFactorRGB) { 52 | GlStateManager.BLEND.srcFactorRGB = srcFactor; 53 | GlStateManager.BLEND.dstFactorRGB = dstFactor; 54 | GL11.glBlendFunc(srcFactor, dstFactor); 55 | } 56 | } 57 | 58 | public static void _blendFuncSeparate(int srcFactorRGB, int dstFactorRGB, int srcFactorAlpha, int dstFactorAlpha) { 59 | if (srcFactorRGB != GlStateManager.BLEND.srcFactorRGB || dstFactorRGB != GlStateManager.BLEND.dstFactorRGB || srcFactorAlpha != GlStateManager.BLEND.srcFactorAlpha || dstFactorAlpha != GlStateManager.BLEND.dstFactorAlpha) { 60 | GlStateManager.BLEND.srcFactorRGB = srcFactorRGB; 61 | GlStateManager.BLEND.dstFactorRGB = dstFactorRGB; 62 | GlStateManager.BLEND.srcFactorAlpha = srcFactorAlpha; 63 | GlStateManager.BLEND.dstFactorAlpha = dstFactorAlpha; 64 | GL14.glBlendFuncSeparate(srcFactorRGB, dstFactorRGB, srcFactorAlpha, dstFactorAlpha); 65 | } 66 | } 67 | 68 | public static void _bindTexture(int texture) { 69 | if (texture != GlStateManager.TEXTURES[GlStateManager.activeTexture].boundTexture) { 70 | GlStateManager.TEXTURES[GlStateManager.activeTexture].boundTexture = texture; 71 | GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture); 72 | } 73 | } 74 | 75 | public static int _getActiveTexture() { 76 | return activeTexture + GL13.GL_TEXTURE0; 77 | } 78 | 79 | public static void _activeTexture(int texture) { 80 | if (activeTexture != texture - GL13.GL_TEXTURE0) { 81 | activeTexture = texture - GL13.GL_TEXTURE0; 82 | GL13.glActiveTexture(texture); 83 | } 84 | } 85 | 86 | public static void _enableTexture() { 87 | GlStateManager.TEXTURES[GlStateManager.activeTexture].capState = true; 88 | } 89 | 90 | @Environment(EnvType.CLIENT) 91 | static class CapabilityTracker { 92 | private final int cap; 93 | private boolean state; 94 | 95 | public CapabilityTracker(int cap) { 96 | this.cap = cap; 97 | } 98 | 99 | public void disable() { 100 | this.setState(false); 101 | } 102 | 103 | public void enable() { 104 | this.setState(true); 105 | } 106 | 107 | public void setState(boolean state) { 108 | if (state != this.state) { 109 | this.state = state; 110 | if (state) { 111 | GL11.glEnable(this.cap); 112 | } else { 113 | GL11.glDisable(this.cap); 114 | } 115 | } 116 | } 117 | } 118 | 119 | @Environment(EnvType.CLIENT) 120 | static class BlendFuncState { 121 | public final CapabilityTracker capState = new CapabilityTracker(GL11.GL_BLEND); 122 | public int srcFactorRGB = 1; 123 | public int dstFactorRGB = 0; 124 | public int srcFactorAlpha = 1; 125 | public int dstFactorAlpha = 0; 126 | 127 | BlendFuncState() {} 128 | } 129 | 130 | @Environment(EnvType.CLIENT) 131 | static class Texture2DState { 132 | public boolean capState; 133 | public int boundTexture; 134 | 135 | Texture2DState() {} 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/GLImportProcessor.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.common.collect.Lists; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import net.mine_diver.smoothbeta.util.StringHelper; 8 | import net.modificationstation.stationapi.api.util.PathUtil; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.List; 13 | import java.util.Locale; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * Handles the flattening of "moj_" import strings in the loaded GLSL shader file. 19 | * Instances of an import are replaced by the contents of the referenced file 20 | * prefixed by a comment describing the line position and original file location 21 | * of the import. 22 | */ 23 | @Environment(EnvType.CLIENT) 24 | public abstract class GLImportProcessor { 25 | private static final String MULTI_LINE_COMMENT_PATTERN = "/\\*(?:[^*]|\\*+[^*/])*\\*+/"; 26 | private static final String SINGLE_LINE_COMMENT_PATTERN = "//\\V*"; 27 | private static final Pattern MOJ_IMPORT_PATTERN = Pattern.compile("(#(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*moj_import(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*(?:\"(.*)\"|<(.*)>))"); 28 | private static final Pattern IMPORT_VERSION_PATTERN = Pattern.compile("(#(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*version(?:" + MULTI_LINE_COMMENT_PATTERN + "|\\h)*(\\d+))\\b"); 29 | private static final Pattern TRAILING_WHITESPACE_PATTERN = Pattern.compile("(?:^|\\v)(?:\\s|" + MULTI_LINE_COMMENT_PATTERN + "|(" + SINGLE_LINE_COMMENT_PATTERN + "))*\\z"); 30 | 31 | /** 32 | * Reads the source code supplied into a list of lines suitable for uploading to 33 | * the GL Shader cache. 34 | * 35 | *

Imports are processed as per the description of this class. 36 | */ 37 | public List readSource(String source) { 38 | Context context = new Context(); 39 | List list = this.parseImports(source, context, ""); 40 | list.set(0, this.readImport(list.get(0), context.column)); 41 | return list; 42 | } 43 | 44 | private List parseImports(String source, Context context, String path) { 45 | int i = context.line; 46 | int j = 0; 47 | String string = ""; 48 | List list = Lists.newArrayList(); 49 | Matcher matcher = MOJ_IMPORT_PATTERN.matcher(source); 50 | 51 | String string2; 52 | while(matcher.find()) { 53 | if (method_36424(source, matcher, j)) { 54 | string2 = matcher.group(2); 55 | boolean bl = string2 != null; 56 | if (!bl) { 57 | string2 = matcher.group(3); 58 | } 59 | 60 | if (string2 != null) { 61 | String string3 = source.substring(j, matcher.start(1)); 62 | String string4 = path + string2; 63 | String string5 = this.loadImport(bl, string4); 64 | int k; 65 | if (!Strings.isNullOrEmpty(string5)) { 66 | if (!StringHelper.endsWithLineBreak(string5)) { 67 | string5 = string5 + System.lineSeparator(); 68 | } 69 | 70 | ++context.line; 71 | k = context.line; 72 | List list2 = this.parseImports(string5, context, bl ? PathUtil.getPosixFullPath(string4) : ""); 73 | list2.set(0, String.format(Locale.ROOT, "#line %d %d\n%s", 0, k, this.extractVersion(list2.get(0), context))); 74 | if (!StringUtils.isBlank(string3)) { 75 | list.add(string3); 76 | } 77 | 78 | list.addAll(list2); 79 | } else { 80 | String string6 = bl ? String.format("/*#moj_import \"%s\"*/", string2) : String.format("/*#moj_import <%s>*/", string2); 81 | list.add(string + string3 + string6); 82 | } 83 | 84 | k = StringHelper.countLines(source.substring(0, matcher.end(1))); 85 | string = String.format(Locale.ROOT, "#line %d %d", k, i); 86 | j = matcher.end(1); 87 | } 88 | } 89 | } 90 | 91 | string2 = source.substring(j); 92 | if (!StringUtils.isBlank(string2)) { 93 | list.add(string + string2); 94 | } 95 | 96 | return list; 97 | } 98 | 99 | /** 100 | * Converts a line known to contain an import into a fully-qualified 101 | * version of itself for insertion as a comment. 102 | */ 103 | private String extractVersion(String line, Context context) { 104 | Matcher matcher = IMPORT_VERSION_PATTERN.matcher(line); 105 | if (matcher.find() && method_36423(line, matcher)) { 106 | context.column = Math.max(context.column, Integer.parseInt(matcher.group(2))); 107 | String var10000 = line.substring(0, matcher.start(1)); 108 | return var10000 + "/*" + line.substring(matcher.start(1), matcher.end(1)) + "*/" + line.substring(matcher.end(1)); 109 | } else { 110 | return line; 111 | } 112 | } 113 | 114 | private String readImport(String line, int start) { 115 | Matcher matcher = IMPORT_VERSION_PATTERN.matcher(line); 116 | if (matcher.find() && method_36423(line, matcher)) { 117 | String var10000 = line.substring(0, matcher.start(2)); 118 | return var10000 + Math.max(start, Integer.parseInt(matcher.group(2))) + line.substring(matcher.end(2)); 119 | } else { 120 | return line; 121 | } 122 | } 123 | 124 | private static boolean method_36423(String string, Matcher matcher) { 125 | return method_36424(string, matcher, 0); 126 | } 127 | 128 | private static boolean method_36424(String string, Matcher matcher, int i) { 129 | int j = matcher.start() - i; 130 | if (j == 0) { 131 | return true; 132 | } else { 133 | Matcher matcher2 = TRAILING_WHITESPACE_PATTERN.matcher(string.substring(i, matcher.start())); 134 | if (!matcher2.find()) { 135 | return false; 136 | } else { 137 | int k = matcher2.end(1); 138 | return k != matcher.start(); 139 | } 140 | } 141 | } 142 | 143 | /** 144 | * Called to load an import reference's source code. 145 | */ 146 | @Nullable 147 | public abstract String loadImport(boolean inline, String name); 148 | 149 | /** 150 | * A context for the parser to keep track of its current line and caret position in the file. 151 | */ 152 | @Environment(EnvType.CLIENT) 153 | static final class Context { 154 | int column; 155 | int line; 156 | } 157 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/gl/GlUniform.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render.gl; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.minecraft.client.util.GlAllocationUtils; 6 | import net.modificationstation.stationapi.api.util.math.Vec3f; 7 | import org.lwjgl.opengl.GL20; 8 | 9 | import java.nio.FloatBuffer; 10 | import java.nio.IntBuffer; 11 | 12 | import static net.modificationstation.stationapi.impl.client.texture.StationRenderImpl.LOGGER; 13 | 14 | @Environment(EnvType.CLIENT) 15 | public class GlUniform extends Uniform implements AutoCloseable { 16 | public static final int INT1 = 0; 17 | public static final int INT2 = 1; 18 | public static final int INT3 = 2; 19 | public static final int INT4 = 3; 20 | public static final int FLOAT1 = 4; 21 | public static final int FLOAT2 = 5; 22 | public static final int FLOAT3 = 6; 23 | public static final int FLOAT4 = 7; 24 | public static final int MAT2X2 = 8; 25 | public static final int MAT3X3 = 9; 26 | public static final int MAT4X4 = 10; 27 | private static final boolean TRANSPOSE = false; 28 | private int location; 29 | private final int count; 30 | private final int dataType; 31 | private final IntBuffer intData; 32 | private final FloatBuffer floatData; 33 | private final String name; 34 | 35 | public GlUniform(String name, int dataType, int count) { 36 | this.name = name; 37 | this.count = count; 38 | this.dataType = dataType; 39 | if (dataType <= INT4) { 40 | this.intData = GlAllocationUtils.allocateIntBuffer(count); 41 | this.floatData = null; 42 | } else { 43 | this.intData = null; 44 | this.floatData = GlAllocationUtils.allocateFloatBuffer(count); 45 | } 46 | 47 | this.location = -1; 48 | this.markStateDirty(); 49 | } 50 | 51 | public static int getUniformLocation(int program, CharSequence name) { 52 | return GL20.glGetUniformLocation(program, name); 53 | } 54 | 55 | public static void uniform1(int location, int value) { 56 | GL20.glUniform1i(location, value); 57 | } 58 | 59 | public static void bindAttribLocation(int program, int index, CharSequence name) { 60 | GL20.glBindAttribLocation(program, index, name); 61 | } 62 | 63 | public void close() {} 64 | 65 | private void markStateDirty() {} 66 | 67 | public static int getTypeIndex(String typeName) { 68 | int i = -1; 69 | if ("int".equals(typeName)) i = INT1; 70 | else if ("float".equals(typeName)) i = FLOAT1; 71 | else if (typeName.startsWith("matrix")) if (typeName.endsWith("2x2")) i = MAT2X2; 72 | else if (typeName.endsWith("3x3")) i = MAT3X3; 73 | else if (typeName.endsWith("4x4")) i = MAT4X4; 74 | 75 | return i; 76 | } 77 | 78 | public void setLocation(int location) { 79 | this.location = location; 80 | } 81 | 82 | public String getName() { 83 | return this.name; 84 | } 85 | 86 | public final void set(float value1, float value2, float value3) { 87 | this.floatData.position(0); 88 | this.floatData.put(0, value1); 89 | this.floatData.put(1, value2); 90 | this.floatData.put(2, value3); 91 | this.markStateDirty(); 92 | } 93 | 94 | public final void set(Vec3f vector) { 95 | this.floatData.position(0); 96 | this.floatData.put(0, vector.getX()); 97 | this.floatData.put(1, vector.getY()); 98 | this.floatData.put(2, vector.getZ()); 99 | this.markStateDirty(); 100 | } 101 | 102 | public final void setForDataType(float value1, float value2, float value3, float value4) { 103 | this.floatData.position(0); 104 | if (this.dataType >= FLOAT1) this.floatData.put(0, value1); 105 | 106 | if (this.dataType >= FLOAT2) this.floatData.put(1, value2); 107 | 108 | if (this.dataType >= FLOAT3) this.floatData.put(2, value3); 109 | 110 | if (this.dataType >= FLOAT4) this.floatData.put(3, value4); 111 | 112 | this.markStateDirty(); 113 | } 114 | 115 | public final void setForDataType(int value1, int value2, int value3, int value4) { 116 | this.intData.position(0); 117 | if (this.dataType >= INT1) this.intData.put(0, value1); 118 | 119 | if (this.dataType >= INT2) this.intData.put(1, value2); 120 | 121 | if (this.dataType >= INT3) this.intData.put(2, value3); 122 | 123 | if (this.dataType >= INT4) this.intData.put(3, value4); 124 | 125 | this.markStateDirty(); 126 | } 127 | 128 | public final void set(int value) { 129 | this.intData.position(0); 130 | this.intData.put(0, value); 131 | this.markStateDirty(); 132 | } 133 | 134 | public final void set(float[] values) { 135 | if (values.length < this.count) 136 | LOGGER.warn("Uniform.set called with a too-small value array (expected {}, got {}). Ignoring.", this.count, values.length); 137 | else { 138 | this.floatData.position(0); 139 | this.floatData.put(values); 140 | this.floatData.position(0); 141 | this.markStateDirty(); 142 | } 143 | } 144 | 145 | public final void set(FloatBuffer matrix) { 146 | floatData.position(0); 147 | floatData.put(matrix); 148 | markStateDirty(); 149 | } 150 | 151 | public void upload() { 152 | if (this.dataType <= INT4) this.uploadInts(); 153 | else if (this.dataType <= FLOAT4) this.uploadFloats(); 154 | else { 155 | if (this.dataType > MAT4X4) { 156 | LOGGER.warn("Uniform.upload called, but type value ({}) is not a valid type. Ignoring.", this.dataType); 157 | return; 158 | } 159 | 160 | this.uploadMatrix(); 161 | } 162 | } 163 | 164 | private void uploadInts() { 165 | this.intData.rewind(); 166 | switch (this.dataType) { 167 | case INT1 -> GL20.glUniform1(this.location, this.intData); 168 | case INT2 -> GL20.glUniform2(this.location, this.intData); 169 | case INT3 -> GL20.glUniform3(this.location, this.intData); 170 | case INT4 -> GL20.glUniform4(this.location, this.intData); 171 | default -> LOGGER.warn("Uniform.upload called, but count value ({}) is not in the range of 1 to 4. Ignoring.", this.count); 172 | } 173 | } 174 | 175 | private void uploadFloats() { 176 | this.floatData.rewind(); 177 | switch (this.dataType) { 178 | case FLOAT1 -> GL20.glUniform1(this.location, this.floatData); 179 | case FLOAT2 -> GL20.glUniform2(this.location, this.floatData); 180 | case FLOAT3 -> GL20.glUniform3(this.location, this.floatData); 181 | case FLOAT4 -> GL20.glUniform4(this.location, this.floatData); 182 | default -> LOGGER.warn("Uniform.upload called, but count value ({}) is not in the range of 1 to 4. Ignoring.", this.count); 183 | } 184 | } 185 | 186 | private void uploadMatrix() { 187 | this.floatData.clear(); 188 | switch (this.dataType) { 189 | case MAT2X2 -> GL20.glUniformMatrix2(this.location, TRANSPOSE, this.floatData); 190 | case MAT3X3 -> GL20.glUniformMatrix3(this.location, TRANSPOSE, this.floatData); 191 | case MAT4X4 -> GL20.glUniformMatrix4(this.location, TRANSPOSE, this.floatData); 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/VboPool.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import net.fabricmc.loader.api.FabricLoader; 4 | import net.mine_diver.smoothbeta.client.render.gl.GlStateManager; 5 | import net.mine_diver.smoothbeta.mixin.client.MinecraftAccessor; 6 | import net.minecraft.client.util.GlAllocationUtils; 7 | import net.modificationstation.stationapi.api.util.collection.LinkedList; 8 | import org.lwjgl.opengl.*; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.nio.IntBuffer; 12 | 13 | @SuppressWarnings("removal") 14 | public class VboPool implements AutoCloseable { 15 | @SuppressWarnings("deprecation") 16 | private static final MinecraftAccessor mc = ((MinecraftAccessor) FabricLoader.getInstance().getGameInstance()); 17 | 18 | private int vertexArrayId = GL30.glGenVertexArrays(); 19 | private int vertexBufferId = GL15.glGenBuffers(); 20 | private int capacity = 4096; 21 | private int nextPos = 0; 22 | private int size; 23 | private final LinkedList posList = new LinkedList<>(); 24 | private Pos compactPosLast = null; 25 | private int curBaseInstance; 26 | 27 | private IntBuffer bufferIndirect = GlAllocationUtils.allocateIntBuffer(this.capacity * 5); 28 | private final int vertexBytes; 29 | private VertexFormat.DrawMode drawMode = VertexFormat.DrawMode.QUADS; 30 | 31 | public VboPool(VertexFormat format) { 32 | vertexBytes = format.getVertexSizeByte(); 33 | this.bindBuffer(); 34 | long i = this.toBytes(this.capacity); 35 | GL15.glBufferData(GL15.GL_ARRAY_BUFFER, i, GL15.GL_STATIC_DRAW); 36 | this.unbindBuffer(); 37 | } 38 | 39 | @Override 40 | public void close() { 41 | if (this.vertexBufferId > 0) { 42 | GlStateManager._glDeleteBuffers(this.vertexBufferId); 43 | this.vertexBufferId = 0; 44 | } 45 | } 46 | 47 | public void bufferData(ByteBuffer data, Pos poolPos) { 48 | if (this.vertexBufferId >= 0) { 49 | int position = poolPos.getPosition(); 50 | int size = poolPos.getSize(); 51 | int bufferSize = this.toVertex(data.limit()); 52 | 53 | if (bufferSize <= 0) { 54 | if (position >= 0) { 55 | poolPos.setPosition(-1); 56 | poolPos.setSize(0); 57 | this.posList.remove(poolPos.getNode()); 58 | this.size -= size; 59 | } 60 | } else { 61 | if (bufferSize > size) { 62 | poolPos.setPosition(this.nextPos); 63 | poolPos.setSize(bufferSize); 64 | this.nextPos += bufferSize; 65 | 66 | if (position >= 0) this.posList.remove(poolPos.getNode()); 67 | 68 | this.posList.addLast(poolPos.getNode()); 69 | } 70 | 71 | poolPos.setSize(bufferSize); 72 | this.size += bufferSize - size; 73 | this.checkVboSize(poolPos.getPositionNext()); 74 | long l = this.toBytes(poolPos.getPosition()); 75 | this.bindVertexArray(); 76 | this.bindBuffer(); 77 | GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, l, data); 78 | this.unbindBuffer(); 79 | unbindVertexArray(); 80 | 81 | if (this.nextPos > this.size * 11 / 10) this.compactRanges(); 82 | } 83 | } 84 | } 85 | 86 | private void compactRanges() { 87 | if (!this.posList.isEmpty()) { 88 | Pos vborange = this.compactPosLast; 89 | 90 | if (vborange == null || !this.posList.contains(vborange.getNode())) 91 | vborange = this.posList.getFirst().getItem(); 92 | 93 | int i; 94 | Pos vborange1 = vborange.getPrev(); 95 | 96 | if (vborange1 == null) i = 0; 97 | else i = vborange1.getPositionNext(); 98 | 99 | int j = 0; 100 | 101 | while (vborange != null && j < 1) { 102 | ++j; 103 | 104 | if (vborange.getPosition() == i) { 105 | i += vborange.getSize(); 106 | vborange = vborange.getNext(); 107 | } else { 108 | int k = vborange.getPosition() - i; 109 | 110 | if (vborange.getSize() <= k) { 111 | this.copyVboData(vborange.getPosition(), i, vborange.getSize()); 112 | vborange.setPosition(i); 113 | i += vborange.getSize(); 114 | vborange = vborange.getNext(); 115 | } else { 116 | this.checkVboSize(this.nextPos + vborange.getSize()); 117 | this.copyVboData(vborange.getPosition(), this.nextPos, vborange.getSize()); 118 | vborange.setPosition(this.nextPos); 119 | this.nextPos += vborange.getSize(); 120 | Pos vborange2 = vborange.getNext(); 121 | this.posList.remove(vborange.getNode()); 122 | this.posList.addLast(vborange.getNode()); 123 | vborange = vborange2; 124 | } 125 | } 126 | } 127 | 128 | if (vborange == null) this.nextPos = this.posList.getLast().getItem().getPositionNext(); 129 | 130 | this.compactPosLast = vborange; 131 | } 132 | } 133 | 134 | private long toBytes(int vertex) 135 | { 136 | return (long)vertex * (long)this.vertexBytes; 137 | } 138 | 139 | private int toVertex(long bytes) { 140 | return (int)(bytes / (long)this.vertexBytes); 141 | } 142 | 143 | private void checkVboSize(int sizeMin) { 144 | if (this.capacity < sizeMin) this.expandVbo(sizeMin); 145 | } 146 | 147 | private void copyVboData(int posFrom, int posTo, int size) { 148 | long i = this.toBytes(posFrom); 149 | long j = this.toBytes(posTo); 150 | long k = this.toBytes(size); 151 | GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, this.vertexBufferId); 152 | GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, this.vertexBufferId); 153 | GL31.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, i, j, k); 154 | mc.smoothbeta_printOpenGLError("Copy VBO range"); 155 | GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0); 156 | GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0); 157 | } 158 | 159 | private void expandVbo(int sizeMin) { 160 | int i; 161 | 162 | i = this.capacity * 6 / 4; 163 | while (i < sizeMin) i = i * 6 / 4; 164 | 165 | long j = this.toBytes(this.capacity); 166 | long k = this.toBytes(i); 167 | int l = GL15.glGenBuffers(); 168 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, l); 169 | GL15.glBufferData(GL15.GL_ARRAY_BUFFER, k, GL15.GL_STATIC_DRAW); 170 | mc.smoothbeta_printOpenGLError("Expand VBO"); 171 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); 172 | GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, this.vertexBufferId); 173 | GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, l); 174 | GL31.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L, j); 175 | mc.smoothbeta_printOpenGLError("Copy VBO: " + k); 176 | GL15.glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0); 177 | GL15.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0); 178 | GL15.glDeleteBuffers(this.vertexBufferId); 179 | this.bufferIndirect = GlAllocationUtils.allocateIntBuffer(i * 5); 180 | this.vertexBufferId = l; 181 | this.capacity = i; 182 | } 183 | 184 | public void bindVertexArray() { 185 | GL30.glBindVertexArray(this.vertexArrayId); 186 | } 187 | 188 | public void bindBuffer() { 189 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vertexBufferId); 190 | } 191 | 192 | public void upload(VertexFormat.DrawMode drawMode, Pos range) { 193 | if (this.drawMode != drawMode) { 194 | if (this.bufferIndirect.position() > 0) 195 | throw new IllegalArgumentException("Mixed region draw modes: " + this.drawMode + " != " + drawMode); 196 | 197 | this.drawMode = drawMode; 198 | } 199 | 200 | this.bufferIndirect.put(drawMode.getIndexCount(range.getSize())); 201 | bufferIndirect.put(1); 202 | this.bufferIndirect.put(0); 203 | bufferIndirect.put(range.getPosition()); 204 | bufferIndirect.put(curBaseInstance++); 205 | } 206 | 207 | public void drawAll() { 208 | GL30.glBindVertexArray(this.vertexArrayId); 209 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vertexBufferId); 210 | 211 | GL20.glEnableVertexAttribArray(0); 212 | GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 28, 0); 213 | GL20.glEnableVertexAttribArray(1); 214 | GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 28, 12); 215 | GL20.glEnableVertexAttribArray(2); 216 | GL20.glVertexAttribPointer(2, 4, GL11.GL_UNSIGNED_BYTE, true, 28, 20); 217 | GL20.glEnableVertexAttribArray(3); 218 | GL20.glVertexAttribPointer(3, 3, GL11.GL_BYTE, true, 28, 24); 219 | 220 | IndexBuffer autostorageindexbuffer = IndexBuffer.getSequentialBuffer(this.drawMode); 221 | VertexFormat.IndexType indextype = autostorageindexbuffer.getIndexType(); 222 | autostorageindexbuffer.bindAndGrow(nextPos / 4 * 6); 223 | this.bufferIndirect.flip(); 224 | GL43.glMultiDrawElementsIndirect(this.drawMode.glMode, indextype.glType, this.bufferIndirect, bufferIndirect.limit() / 5, 0); 225 | this.bufferIndirect.limit(this.bufferIndirect.capacity()); 226 | 227 | if (this.nextPos > this.size * 11 / 10) this.compactRanges(); 228 | curBaseInstance = 0; 229 | } 230 | 231 | public void unbindBuffer() { 232 | GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); 233 | } 234 | 235 | public static void unbindVertexArray() { 236 | GL30.glBindVertexArray(0); 237 | } 238 | 239 | public void deleteGlBuffers() { 240 | if (this.vertexArrayId >= 0) { 241 | GL30.glDeleteVertexArrays(this.vertexArrayId); 242 | this.vertexArrayId = -1; 243 | } 244 | if (this.vertexBufferId >= 0) { 245 | GlStateManager._glDeleteBuffers(this.vertexBufferId); 246 | this.vertexBufferId = -1; 247 | } 248 | } 249 | 250 | public static class Pos { 251 | private int position = -1; 252 | private int size = 0; 253 | private final LinkedList.Node node = new LinkedList.Node<>(this); 254 | 255 | public int getPosition() { 256 | return this.position; 257 | } 258 | 259 | public int getSize() { 260 | return this.size; 261 | } 262 | 263 | public int getPositionNext() { 264 | return this.position + this.size; 265 | } 266 | 267 | public void setPosition(int position) { 268 | this.position = position; 269 | } 270 | 271 | public void setSize(int size) { 272 | this.size = size; 273 | } 274 | 275 | public LinkedList.Node getNode() { 276 | return this.node; 277 | } 278 | 279 | public Pos getPrev() { 280 | LinkedList.Node node = this.node.getPrev(); 281 | return node == null ? null : node.getItem(); 282 | } 283 | 284 | public Pos getNext() { 285 | LinkedList.Node node = this.node.getNext(); 286 | return node == null ? null : node.getItem(); 287 | } 288 | 289 | public String toString() { 290 | return this.position + "/" + this.size + "/" + (this.position + this.size); 291 | } 292 | } 293 | } -------------------------------------------------------------------------------- /src/main/java/net/mine_diver/smoothbeta/client/render/Shader.java: -------------------------------------------------------------------------------- 1 | package net.mine_diver.smoothbeta.client.render; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.common.collect.Sets; 5 | import com.google.gson.JsonArray; 6 | import com.google.gson.JsonElement; 7 | import com.google.gson.JsonObject; 8 | import it.unimi.dsi.fastutil.ints.IntArrayList; 9 | import it.unimi.dsi.fastutil.ints.IntList; 10 | import net.fabricmc.api.EnvType; 11 | import net.fabricmc.api.Environment; 12 | import net.mine_diver.smoothbeta.client.render.gl.*; 13 | import net.modificationstation.stationapi.api.client.texture.AbstractTexture; 14 | import net.modificationstation.stationapi.api.resource.Resource; 15 | import net.modificationstation.stationapi.api.resource.ResourceFactory; 16 | import net.modificationstation.stationapi.api.util.Identifier; 17 | import net.modificationstation.stationapi.api.util.JsonHelper; 18 | import net.modificationstation.stationapi.api.util.PathUtil; 19 | import org.apache.commons.io.IOUtils; 20 | import org.lwjgl.opengl.GL13; 21 | 22 | import java.io.BufferedReader; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.Reader; 26 | import java.util.*; 27 | 28 | import static net.mine_diver.smoothbeta.SmoothBeta.LOGGER; 29 | import static net.mine_diver.smoothbeta.SmoothBeta.NAMESPACE; 30 | 31 | @Environment(EnvType.CLIENT) 32 | public class Shader implements GlShader, AutoCloseable { 33 | private static final String CORE_DIRECTORY = NAMESPACE + "/shaders/core/"; 34 | private static final String INCLUDE_DIRECTORY = NAMESPACE + "/shaders/include/"; 35 | private static int activeShaderId; 36 | private final Map samplers = new HashMap<>(); 37 | private final List samplerNames = new ArrayList<>(); 38 | private final IntList loadedSamplerIds = new IntArrayList(); 39 | private final List uniforms = new ArrayList<>(); 40 | private final Map loadedUniforms = new HashMap<>(); 41 | private final int programId; 42 | private final String name; 43 | private final GlBlendState blendState; 44 | private final Program vertexShader; 45 | private final Program fragmentShader; 46 | public final GlUniform 47 | modelViewMat, 48 | projectionMat, 49 | fogMode, 50 | chunkOffset; 51 | 52 | public Shader(ResourceFactory factory, String name, VertexFormat format) throws IOException { 53 | this.name = name; 54 | Identifier identifier = Identifier.of(CORE_DIRECTORY + name + ".json"); 55 | try (BufferedReader reader = factory.openAsReader(identifier);){ 56 | JsonArray jsonArray3; 57 | JsonArray jsonArray2; 58 | JsonObject jsonObject = JsonHelper.deserialize(reader); 59 | String string = JsonHelper.getString(jsonObject, "vertex"); 60 | String string2 = JsonHelper.getString(jsonObject, "fragment"); 61 | JsonArray jsonArray = JsonHelper.getArray(jsonObject, "samplers", null); 62 | if (jsonArray != null) { 63 | int i = 0; 64 | for (JsonElement jsonElement : jsonArray) { 65 | try { 66 | this.readSampler(jsonElement); 67 | } 68 | catch (Exception exception) { 69 | ShaderParseException shaderParseException = ShaderParseException.wrap(exception); 70 | shaderParseException.addFaultyElement("samplers[" + i + "]"); 71 | throw shaderParseException; 72 | } 73 | ++i; 74 | } 75 | } 76 | List attributeNames; 77 | List loadedAttributeIds; 78 | if ((jsonArray2 = JsonHelper.getArray(jsonObject, "attributes", null)) != null) { 79 | int j = 0; 80 | loadedAttributeIds = Lists.newArrayListWithCapacity(jsonArray2.size()); 81 | attributeNames = Lists.newArrayListWithCapacity(jsonArray2.size()); 82 | for (JsonElement jsonElement2 : jsonArray2) { 83 | try { 84 | attributeNames.add(JsonHelper.asString(jsonElement2, "attribute")); 85 | } 86 | catch (Exception exception2) { 87 | ShaderParseException shaderParseException2 = ShaderParseException.wrap(exception2); 88 | shaderParseException2.addFaultyElement("attributes[" + j + "]"); 89 | throw shaderParseException2; 90 | } 91 | ++j; 92 | } 93 | } else { 94 | loadedAttributeIds = null; 95 | attributeNames = null; 96 | } 97 | if ((jsonArray3 = JsonHelper.getArray(jsonObject, "uniforms", null)) != null) { 98 | int k = 0; 99 | for (JsonElement jsonElement3 : jsonArray3) { 100 | try { 101 | this.addUniform(jsonElement3); 102 | } 103 | catch (Exception exception3) { 104 | ShaderParseException shaderParseException3 = ShaderParseException.wrap(exception3); 105 | shaderParseException3.addFaultyElement("uniforms[" + k + "]"); 106 | throw shaderParseException3; 107 | } 108 | ++k; 109 | } 110 | } 111 | this.blendState = Shader.readBlendState(JsonHelper.getObject(jsonObject, "blend", null)); 112 | this.vertexShader = Shader.loadProgram(factory, Program.Type.VERTEX, string); 113 | this.fragmentShader = Shader.loadProgram(factory, Program.Type.FRAGMENT, string2); 114 | this.programId = GlProgramManager.createProgram(); 115 | if (attributeNames != null) { 116 | int k = 0; 117 | for (String string3 : format.getAttributeNames()) { 118 | GlUniform.bindAttribLocation(this.programId, k, string3); 119 | loadedAttributeIds.add(k); 120 | ++k; 121 | } 122 | } 123 | GlProgramManager.linkProgram(this); 124 | this.loadReferences(); 125 | } 126 | catch (Exception exception4) { 127 | ShaderParseException shaderParseException4 = ShaderParseException.wrap(exception4); 128 | shaderParseException4.addFaultyFile(identifier.path); 129 | throw shaderParseException4; 130 | } 131 | this.modelViewMat = this.getUniform("ModelViewMat"); 132 | this.projectionMat = this.getUniform("ProjMat"); 133 | this.fogMode = this.getUniform("FogMode"); 134 | this.chunkOffset = this.getUniform("ChunkOffset"); 135 | } 136 | 137 | private static Program loadProgram(ResourceFactory factory, Program.Type type, String name) throws IOException { 138 | Program program2; 139 | Program program = type.getProgramCache().get(name); 140 | if (program == null) { 141 | String string = CORE_DIRECTORY + name + type.getFileExtension(); 142 | Resource resource = factory.getResourceOrThrow(Identifier.of(string)); 143 | try (InputStream inputStream = resource.getInputStream()) { 144 | final String string2 = PathUtil.getPosixFullPath(string); 145 | program2 = Program.createFromResource(type, name, inputStream, resource.getResourcePackName(), new GLImportProcessor() { 146 | private final Set visitedImports = Sets.newHashSet(); 147 | 148 | @Override 149 | public String loadImport(boolean inline, String name) { 150 | String string; 151 | name = PathUtil.normalizeToPosix((inline ? string2 : Shader.INCLUDE_DIRECTORY) + name); 152 | if (!this.visitedImports.add(name)) return null; 153 | Identifier identifier = Identifier.of(name); 154 | BufferedReader reader; 155 | try { 156 | reader = factory.openAsReader(identifier); 157 | } catch (IOException e) { 158 | throw new RuntimeException(e); 159 | } 160 | try { 161 | string = IOUtils.toString(reader); 162 | } catch (Throwable throwable) { 163 | try { 164 | if (reader != null) try { 165 | ((Reader) reader).close(); 166 | } catch (Throwable throwable2) { 167 | throwable.addSuppressed(throwable2); 168 | } 169 | throw throwable; 170 | } catch (IOException iOException) { 171 | LOGGER.error("Could not open GLSL import {}: {}", name, iOException.getMessage()); 172 | return "#error " + iOException.getMessage(); 173 | } 174 | } 175 | try { 176 | ((Reader) reader).close(); 177 | } catch (IOException e) { 178 | throw new RuntimeException(e); 179 | } 180 | return string; 181 | } 182 | }); 183 | } 184 | } else program2 = program; 185 | return program2; 186 | } 187 | 188 | public static GlBlendState readBlendState(JsonObject json) { 189 | if (json == null) return new GlBlendState(); 190 | else { 191 | int i = 32774; 192 | int j = 1; 193 | int k = 0; 194 | int l = 1; 195 | int m = 0; 196 | boolean bl = true; 197 | boolean bl2 = false; 198 | if (JsonHelper.hasString(json, "func")) { 199 | i = GlBlendState.getFuncFromString(json.get("func").getAsString()); 200 | if (i != 32774) bl = false; 201 | } 202 | 203 | if (JsonHelper.hasString(json, "srcrgb")) { 204 | j = GlBlendState.getComponentFromString(json.get("srcrgb").getAsString()); 205 | if (j != 1) bl = false; 206 | } 207 | 208 | if (JsonHelper.hasString(json, "dstrgb")) { 209 | k = GlBlendState.getComponentFromString(json.get("dstrgb").getAsString()); 210 | if (k != 0) bl = false; 211 | } 212 | 213 | if (JsonHelper.hasString(json, "srcalpha")) { 214 | l = GlBlendState.getComponentFromString(json.get("srcalpha").getAsString()); 215 | if (l != 1) bl = false; 216 | 217 | bl2 = true; 218 | } 219 | 220 | if (JsonHelper.hasString(json, "dstalpha")) { 221 | m = GlBlendState.getComponentFromString(json.get("dstalpha").getAsString()); 222 | if (m != 0) bl = false; 223 | 224 | bl2 = true; 225 | } 226 | 227 | if (bl) return new GlBlendState(); 228 | else return bl2 ? new GlBlendState(j, k, l, m, i) : new GlBlendState(j, k, i); 229 | } 230 | } 231 | 232 | public void close() { 233 | 234 | for (GlUniform glUniform : this.uniforms) glUniform.close(); 235 | 236 | GlProgramManager.deleteProgram(this); 237 | } 238 | 239 | public void unbind() { 240 | GlProgramManager.useProgram(0); 241 | activeShaderId = -1; 242 | int i = GlStateManager._getActiveTexture(); 243 | 244 | for(int j = 0; j < this.loadedSamplerIds.size(); ++j) 245 | if (this.samplers.get(this.samplerNames.get(j)) != null) { 246 | GlStateManager._activeTexture(GL13.GL_TEXTURE0 + j); 247 | GlStateManager._bindTexture(0); 248 | } 249 | 250 | GlStateManager._activeTexture(i); 251 | } 252 | 253 | public void bind() { 254 | this.blendState.enable(); 255 | if (this.programId != activeShaderId) { 256 | GlProgramManager.useProgram(this.programId); 257 | activeShaderId = this.programId; 258 | } 259 | 260 | int i = GlStateManager._getActiveTexture(); 261 | 262 | for(int j = 0; j < this.loadedSamplerIds.size(); ++j) { 263 | String string = this.samplerNames.get(j); 264 | if (this.samplers.get(string) != null) { 265 | int k = GlUniform.getUniformLocation(this.programId, string); 266 | GlUniform.uniform1(k, j); 267 | GlStateManager._activeTexture(GL13.GL_TEXTURE0 + j); 268 | GlStateManager._enableTexture(); 269 | Object object = this.samplers.get(string); 270 | int l = -1; 271 | if (object instanceof AbstractTexture) l = ((AbstractTexture) object).getGlId(); 272 | else if (object instanceof Integer) l = (Integer) object; 273 | 274 | if (l != -1) GlStateManager._bindTexture(l); 275 | } 276 | } 277 | 278 | GlStateManager._activeTexture(i); 279 | 280 | for (GlUniform glUniform : this.uniforms) glUniform.upload(); 281 | 282 | } 283 | 284 | public GlUniform getUniform(String name) { 285 | return this.loadedUniforms.get(name); 286 | } 287 | 288 | private void loadReferences() { 289 | IntList intList = new IntArrayList(); 290 | 291 | int i; 292 | for(i = 0; i < this.samplerNames.size(); ++i) { 293 | String string = this.samplerNames.get(i); 294 | int j = GlUniform.getUniformLocation(this.programId, string); 295 | if (j == -1) { 296 | LOGGER.warn("Shader {} could not find sampler named {} in the specified shader program.", this.name, string); 297 | this.samplers.remove(string); 298 | intList.add(i); 299 | } else this.loadedSamplerIds.add(j); 300 | } 301 | 302 | for(i = intList.size() - 1; i >= 0; --i) { 303 | int k = intList.getInt(i); 304 | this.samplerNames.remove(k); 305 | } 306 | 307 | for (GlUniform glUniform : this.uniforms) { 308 | String string2 = glUniform.getName(); 309 | int l = GlUniform.getUniformLocation(this.programId, string2); 310 | if (l == -1) 311 | LOGGER.warn("Shader {} could not find uniform named {} in the specified shader program.", this.name, string2); 312 | else { 313 | glUniform.setLocation(l); 314 | this.loadedUniforms.put(string2, glUniform); 315 | } 316 | } 317 | 318 | } 319 | 320 | private void readSampler(JsonElement json) { 321 | JsonObject jsonObject = JsonHelper.asObject(json, "sampler"); 322 | String string = JsonHelper.getString(jsonObject, "name"); 323 | if (!JsonHelper.hasString(jsonObject, "file")) { 324 | this.samplers.put(string, null); 325 | this.samplerNames.add(string); 326 | } else this.samplerNames.add(string); 327 | } 328 | 329 | public void addSampler(String name, Object sampler) { 330 | this.samplers.put(name, sampler); 331 | } 332 | 333 | private void addUniform(JsonElement json) throws ShaderParseException { 334 | JsonObject jsonObject = JsonHelper.asObject(json, "uniform"); 335 | String string = JsonHelper.getString(jsonObject, "name"); 336 | int i = GlUniform.getTypeIndex(JsonHelper.getString(jsonObject, "type")); 337 | int j = JsonHelper.getInt(jsonObject, "count"); 338 | float[] fs = new float[Math.max(j, 16)]; 339 | JsonArray jsonArray = JsonHelper.getArray(jsonObject, "values"); 340 | if (jsonArray.size() != j && jsonArray.size() > 1) 341 | throw new ShaderParseException("Invalid amount of values specified (expected " + j + ", found " + jsonArray.size() + ")"); 342 | else { 343 | int k = 0; 344 | 345 | for(Iterator var9 = jsonArray.iterator(); var9.hasNext(); ++k) { 346 | JsonElement jsonElement = var9.next(); 347 | 348 | try { 349 | fs[k] = JsonHelper.asFloat(jsonElement, "value"); 350 | } catch (Exception var13) { 351 | ShaderParseException shaderParseException = ShaderParseException.wrap(var13); 352 | shaderParseException.addFaultyElement("values[" + k + "]"); 353 | throw shaderParseException; 354 | } 355 | } 356 | 357 | if (j > 1 && jsonArray.size() == 1) while (k < j) { 358 | fs[k] = fs[0]; 359 | ++k; 360 | } 361 | 362 | int l = j > 1 && j <= 4 && i < 8 ? j - 1 : 0; 363 | GlUniform glUniform = new GlUniform(string, i + l, j); 364 | if (i <= 3) glUniform.setForDataType((int) fs[0], (int) fs[1], (int) fs[2], (int) fs[3]); 365 | else if (i <= 7) glUniform.setForDataType(fs[0], fs[1], fs[2], fs[3]); 366 | else glUniform.set(Arrays.copyOfRange(fs, 0, j)); 367 | 368 | this.uniforms.add(glUniform); 369 | } 370 | } 371 | 372 | public Program getVertexShader() { 373 | return this.vertexShader; 374 | } 375 | 376 | public Program getFragmentShader() { 377 | return this.fragmentShader; 378 | } 379 | 380 | public void attachReferencedShaders() { 381 | this.fragmentShader.attachTo(this); 382 | this.vertexShader.attachTo(this); 383 | } 384 | 385 | public int getProgramRef() { 386 | return this.programId; 387 | } 388 | } --------------------------------------------------------------------------------