From 702d57417ca3282a9af0d846fad7b6afd3094b90 Mon Sep 17 00:00:00 2001 From: Jenya Date: Mon, 13 Apr 2026 04:23:56 +0300 Subject: [PATCH 1/3] colored end portal shader (vanilla) --- .../client/render/MoniRenderTypes.java | 13 +++ .../monilabs/client/render/MoniShaders.java | 5 + .../core/rendertype_colored_endportal.fsh | 93 +++++++++++++++++++ .../core/rendertype_colored_endportal.json | 20 ++++ .../core/rendertype_colored_endportal.vsh | 28 ++++++ 5 files changed, 159 insertions(+) create mode 100644 src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.fsh create mode 100644 src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.json create mode 100644 src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.vsh diff --git a/src/main/java/net/neganote/monilabs/client/render/MoniRenderTypes.java b/src/main/java/net/neganote/monilabs/client/render/MoniRenderTypes.java index a4d6c8c3..b4f73ceb 100644 --- a/src/main/java/net/neganote/monilabs/client/render/MoniRenderTypes.java +++ b/src/main/java/net/neganote/monilabs/client/render/MoniRenderTypes.java @@ -3,6 +3,7 @@ import net.minecraft.client.renderer.RenderStateShard; import net.minecraft.client.renderer.RenderStateShard.ShaderStateShard; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.blockentity.TheEndPortalRenderer; import com.mojang.blaze3d.vertex.DefaultVertexFormat; @@ -23,4 +24,16 @@ public class MoniRenderTypes { .add(TheEndPortalRenderer.END_SKY_LOCATION, false, false) .add(TheEndPortalRenderer.END_PORTAL_LOCATION, false, false).build()) .createCompositeState(false)); + public static RenderType END_PORTAL_COLORED = RenderType.create("end_portal_colored", + DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, false, false, + RenderType.CompositeState.builder() + .setShaderState(new ShaderStateShard(() -> { + ShaderInstance shader = MoniShaders.ENDPORTAL_COLORED_SHADER; + shader.safeGetUniform("EndPortalLayers").set(15); + return shader; + })) + .setTextureState(RenderStateShard.MultiTextureStateShard.builder() + .add(TheEndPortalRenderer.END_SKY_LOCATION, false, false) + .add(TheEndPortalRenderer.END_PORTAL_LOCATION, false, false).build()) + .createCompositeState(false)); } diff --git a/src/main/java/net/neganote/monilabs/client/render/MoniShaders.java b/src/main/java/net/neganote/monilabs/client/render/MoniShaders.java index fb56f5df..4c988cff 100644 --- a/src/main/java/net/neganote/monilabs/client/render/MoniShaders.java +++ b/src/main/java/net/neganote/monilabs/client/render/MoniShaders.java @@ -15,6 +15,7 @@ public class MoniShaders { public static ShaderInstance WORMHOLE_SHADER; + public static ShaderInstance ENDPORTAL_COLORED_SHADER; @SubscribeEvent public static void shaderRegistry(RegisterShadersEvent event) { @@ -22,6 +23,10 @@ public static void shaderRegistry(RegisterShadersEvent event) { event.registerShader(new ShaderInstance(event.getResourceProvider(), MoniLabs.id("rendertype_wormhole"), DefaultVertexFormat.POSITION), (shaderInstance -> WORMHOLE_SHADER = shaderInstance)); + + event.registerShader(new ShaderInstance(event.getResourceProvider(), + MoniLabs.id("rendertype_colored_endportal"), DefaultVertexFormat.POSITION_COLOR), + (shaderInstance -> ENDPORTAL_COLORED_SHADER = shaderInstance)); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.fsh b/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.fsh new file mode 100644 index 00000000..c9d53c99 --- /dev/null +++ b/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.fsh @@ -0,0 +1,93 @@ +#version 150 + +#line 0 1 +/*#version 150*/ + +mat2 mat2_rotate_z(float radians) { + return mat2( + cos(radians), -sin(radians), + sin(radians), cos(radians) + ); +} +#line 3 0 + +uniform sampler2D Sampler0; +uniform sampler2D Sampler1; + +uniform float GameTime; +uniform int EndPortalLayers; + +in vec4 texProj0; +in vec3 vColor; + +const vec3[] COLORS = vec3[]( +vec3(0.022087, 0.098399, 0.110818), +vec3(0.011892, 0.095924, 0.089485), +vec3(0.027636, 0.101689, 0.100326), +vec3(0.046564, 0.109883, 0.114838), +vec3(0.064901, 0.117696, 0.097189), +vec3(0.063761, 0.086895, 0.123646), +vec3(0.084817, 0.111994, 0.166380), +vec3(0.097489, 0.154120, 0.091064), +vec3(0.106152, 0.131144, 0.195191), +vec3(0.097721, 0.110188, 0.187229), +vec3(0.133516, 0.138278, 0.148582), +vec3(0.070006, 0.243332, 0.235792), +vec3(0.196766, 0.142899, 0.214696), +vec3(0.047281, 0.315338, 0.321970), +vec3(0.204675, 0.390010, 0.302066), +vec3(0.080955, 0.314821, 0.661491) +); + +const mat4 SCALE_TRANSLATE = mat4( +0.5, 0.0, 0.0, 0.25, +0.0, 0.5, 0.0, 0.25, +0.0, 0.0, 1.0, 0.0, +0.0, 0.0, 0.0, 1.0 +); + +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +mat4 end_portal_layer(float layer) { + mat4 translate = mat4( + 1.0, 0.0, 0.0, 17.0 / layer, + 0.0, 1.0, 0.0, (2.0 + layer / 1.5) * (GameTime * 1.5), + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + mat2 rotate = mat2_rotate_z(radians((layer * layer * 4321.0 + layer * 9.0) * 2.0)); + + mat2 scale = mat2((4.5 - layer / 4.0) * 2.0); + + return mat4(scale * rotate) * translate * SCALE_TRANSLATE; +} + +out vec4 fragColor; + +void main() { + vec3 color = textureProj(Sampler0, texProj0).rgb * COLORS[0]; + for (int i = 0; i < EndPortalLayers; i++) { + color += textureProj(Sampler1, texProj0 * end_portal_layer(float(i + 1))).rgb * COLORS[i]; + } + vec3 origHsv = rgb2hsv(color); + vec3 destHsv = rgb2hsv(vColor); + //copy hue and saturation + origHsv.x = destHsv.x; + origHsv.y = destHsv.y; + color.rgb = hsv2rgb(origHsv); + fragColor = vec4(color, 1.0); +} diff --git a/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.json b/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.json new file mode 100644 index 00000000..65e72255 --- /dev/null +++ b/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.json @@ -0,0 +1,20 @@ +{ + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha" + }, + "vertex": "monilabs:rendertype_colored_endportal", + "fragment": "monilabs:rendertype_colored_endportal", + "attributes": [], + "samplers": [ + { "name": "Sampler0" }, + { "name": "Sampler1" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "GameTime", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "EndPortalLayers", "type": "int", "count": 1, "values": [ 0.0 ] } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.vsh b/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.vsh new file mode 100644 index 00000000..68dff3d7 --- /dev/null +++ b/src/main/resources/assets/monilabs/shaders/core/rendertype_colored_endportal.vsh @@ -0,0 +1,28 @@ +#version 150 + +#line 0 1 +/*#version 150*/ + +vec4 projection_from_position(vec4 position) { + vec4 projection = position * 0.5; + projection.xy = vec2(projection.x + projection.w, projection.y + projection.w); + projection.zw = position.zw; + return projection; +} +#line 3 0 + +in vec3 Position; +in vec3 Color; + +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; + +out vec4 texProj0; +out vec3 vColor; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); + + texProj0 = projection_from_position(gl_Position); + vColor = Color; +} From fd5347e643403eb38b82ff685ee11f9537f5d68e Mon Sep 17 00:00:00 2001 From: Jenya Date: Mon, 13 Apr 2026 04:28:26 +0300 Subject: [PATCH 2/3] Shader support, spotless, yadda yadda --- .../render/MicroverseProjectorRender.java | 19 ++- .../client/render/ShaderAnalysisResult.java | 3 + .../neganote/monilabs/mixin/ProgramMixin.java | 126 ++++++++++++++++++ src/main/resources/monilabs.mixins.json | 1 + 4 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/neganote/monilabs/client/render/ShaderAnalysisResult.java create mode 100644 src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java diff --git a/src/main/java/net/neganote/monilabs/client/render/MicroverseProjectorRender.java b/src/main/java/net/neganote/monilabs/client/render/MicroverseProjectorRender.java index 0bc78992..75b8c4e8 100644 --- a/src/main/java/net/neganote/monilabs/client/render/MicroverseProjectorRender.java +++ b/src/main/java/net/neganote/monilabs/client/render/MicroverseProjectorRender.java @@ -8,11 +8,15 @@ import com.gregtechceu.gtceu.client.util.RenderBufferHelper; import net.irisshaders.iris.Iris; +import net.irisshaders.iris.shaderpack.materialmap.WorldRenderingSettings; +import net.irisshaders.iris.uniforms.CapturedRenderingState; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.TheEndPortalRenderer; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.textures.UnitTextureAtlasSprite; @@ -24,6 +28,7 @@ import com.mojang.serialization.Codec; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import org.jetbrains.annotations.NotNull; // @SuppressWarnings("unused") @@ -154,9 +159,19 @@ private void renderCuboid(PoseStack stack, MultiBufferSource buffer, Direction u if (GTCEu.isModLoaded(GTValues.MODID_OCULUS) && Iris.getCurrentPack().isPresent()) { consumer = buffer.getBuffer(RenderType.entitySolid(TheEndPortalRenderer.END_PORTAL_LOCATION)); } else { - consumer = buffer.getBuffer(RenderType.endPortal()); + consumer = buffer.getBuffer(MoniRenderTypes.END_PORTAL_COLORED); } - RenderBufferHelper.renderCube(consumer, pose, 0xFFFFFF00, combinedLight, UnitTextureAtlasSprite.INSTANCE, + Iris.getCurrentPack().ifPresent(pack -> { + Object2IntMap stateIds = WorldRenderingSettings.INSTANCE.getBlockStateIds(); + if (stateIds != null) { + CapturedRenderingState.INSTANCE + .setCurrentBlockEntity(stateIds.getOrDefault(Blocks.END_PORTAL.defaultBlockState(), -1)); + CapturedRenderingState.INSTANCE.setCurrentEntity(6767); // Should be unclaimed hopefully :) + } + }); + + RenderBufferHelper.renderCube(consumer, pose, 0xFF0000FF, + combinedLight, UnitTextureAtlasSprite.INSTANCE, minX, minY, minZ, maxX, maxY, maxZ); stack.popPose(); } diff --git a/src/main/java/net/neganote/monilabs/client/render/ShaderAnalysisResult.java b/src/main/java/net/neganote/monilabs/client/render/ShaderAnalysisResult.java new file mode 100644 index 00000000..5d53100f --- /dev/null +++ b/src/main/java/net/neganote/monilabs/client/render/ShaderAnalysisResult.java @@ -0,0 +1,3 @@ +package net.neganote.monilabs.client.render; + +public record ShaderAnalysisResult(String firstWriteLine, String referencedVariable, boolean valid) {} diff --git a/src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java b/src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java new file mode 100644 index 00000000..47f0997d --- /dev/null +++ b/src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java @@ -0,0 +1,126 @@ +package net.neganote.monilabs.mixin; + +import net.neganote.monilabs.MoniLabs; +import net.neganote.monilabs.client.render.ShaderAnalysisResult; + +import com.mojang.blaze3d.shaders.Program; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Mixin(Program.class) +public class ProgramMixin { + + private static final Pattern MAIN_PATTERN = Pattern.compile("void\\s+main\\s*\\(\\s*\\)\\s*\\{([\\s\\S]*)}"); + private static final Pattern DECL_PATTERN = Pattern + .compile("\\b((?:[iu]?vec\\d|float|int|bool))\\b\\s+([\\w\\d_]+)"); + private static final Pattern TARGET_PATTERN = Pattern + .compile("layout\\s*\\(\\s*location\\s*=\\s*0\\s*\\)\\s*out\\s+vec4\\s+(\\w+)"); + private static final Pattern VAR_PATTERN = Pattern.compile("\\b([a-zA-Z_]\\w*)\\b"); + + // Works for all popular shaders + // Basically what this does is find the last write to the color variable before it gets actually written to the + // framebuffer + @Unique + private static ShaderAnalysisResult moniLabs$analyzeShaderForColorWrite(String content) { + Matcher mainMatcher = MAIN_PATTERN.matcher(content); + if (!mainMatcher.find()) return new ShaderAnalysisResult("", "", false); + + String mainBody = mainMatcher.group(1); + + Matcher targetMatcher = TARGET_PATTERN.matcher(content); + String targetOut = targetMatcher.find() ? targetMatcher.group(1) : "gl_FragColor"; + + Map knownTypes = new HashMap<>(); + String[] lines = mainBody.split("\n"); + + for (String line : lines) { + line = line.trim().replaceAll("//.*", ""); + if (line.isEmpty()) { + continue; + } + + Matcher declMatch = DECL_PATTERN.matcher(line); + while (declMatch.find()) { + knownTypes.put(declMatch.group(2), declMatch.group(1)); + } + + if (line.contains(targetOut) && line.contains("=")) { + String rightSide = line.substring(line.indexOf('=') + 1); + Matcher varMatch = VAR_PATTERN.matcher(rightSide); + + while (varMatch.find()) { + String candidate = varMatch.group(1); + if (Character.isDigit(candidate.charAt(0))) continue; + + if (knownTypes.containsKey(candidate)) { + return new ShaderAnalysisResult(line, candidate, true); + } + } + } + } + return new ShaderAnalysisResult("", "", false); + } + + @Unique + private static String moniLabs$modifyShader(String code, ShaderAnalysisResult result) { + if (!result.valid()) { + MoniLabs.LOGGER.log(Level.ERROR, + "Could not successfully analyze current shader for colored ender portal inject"); + return code; + } + String newSource = code.replace("void main", """ + vec3 moni_rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); + } + vec3 moni_hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + } + void main"""); + + newSource = newSource.replace(result.firstWriteLine(), """ + if (iris_entityInfo.x == 6767) { + vec3 _origHsv = moni_rgb2hsv(%s.rgb); + vec3 _destHsv = moni_rgb2hsv(iris_vertexColor.rgb); + //copy hue and saturation + _origHsv.x = _destHsv.x; + _origHsv.y = _destHsv.y; + %s.rgb = moni_hsv2rgb(_origHsv); + } + """.formatted(result.referencedVariable(), result.referencedVariable()) + "\n\t" + + result.firstWriteLine()); + return newSource; + } + + @Redirect(method = "compileShaderInternal", + at = @At(value = "INVOKE", + target = "Lorg/apache/commons/io/IOUtils;toString(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/lang/String;"), + remap = false) + private static String test(InputStream sw, Charset input, Program.Type type, String name) throws IOException { + String original = IOUtils.toString(sw, input); + if (name.contains("block_entity_diffuse") && type == Program.Type.FRAGMENT) { + var analysisResult = moniLabs$analyzeShaderForColorWrite(original); + return moniLabs$modifyShader(original, analysisResult); + } + + return original; + } +} diff --git a/src/main/resources/monilabs.mixins.json b/src/main/resources/monilabs.mixins.json index 417af85b..c9af420b 100644 --- a/src/main/resources/monilabs.mixins.json +++ b/src/main/resources/monilabs.mixins.json @@ -25,6 +25,7 @@ "DefaultChunkRendererMixin", "ShaderLoaderMixin", "GameRendererMixin", + "ProgramMixin", "accessor.TerrainRenderPassAccessor", "accessor.ShaderChunkRendererAccessor" ], From 280090799541cd92b23389c959f7af5028377470 Mon Sep 17 00:00:00 2001 From: Jenya Date: Mon, 13 Apr 2026 04:40:20 +0300 Subject: [PATCH 3/3] fixes for mixin --- .../java/net/neganote/monilabs/mixin/ProgramMixin.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java b/src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java index 47f0997d..1202eea5 100644 --- a/src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java +++ b/src/main/java/net/neganote/monilabs/mixin/ProgramMixin.java @@ -21,11 +21,15 @@ @Mixin(Program.class) public class ProgramMixin { + @Unique private static final Pattern MAIN_PATTERN = Pattern.compile("void\\s+main\\s*\\(\\s*\\)\\s*\\{([\\s\\S]*)}"); + @Unique private static final Pattern DECL_PATTERN = Pattern .compile("\\b((?:[iu]?vec\\d|float|int|bool))\\b\\s+([\\w\\d_]+)"); + @Unique private static final Pattern TARGET_PATTERN = Pattern .compile("layout\\s*\\(\\s*location\\s*=\\s*0\\s*\\)\\s*out\\s+vec4\\s+(\\w+)"); + @Unique private static final Pattern VAR_PATTERN = Pattern.compile("\\b([a-zA-Z_]\\w*)\\b"); // Works for all popular shaders @@ -112,9 +116,9 @@ vec3 moni_hsv2rgb(vec3 c) { @Redirect(method = "compileShaderInternal", at = @At(value = "INVOKE", - target = "Lorg/apache/commons/io/IOUtils;toString(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/lang/String;"), - remap = false) - private static String test(InputStream sw, Charset input, Program.Type type, String name) throws IOException { + target = "Lorg/apache/commons/io/IOUtils;toString(Ljava/io/InputStream;Ljava/nio/charset/Charset;)Ljava/lang/String;")) + private static String moniLabs$modifyIrisEntityDiffuseShader(InputStream sw, Charset input, Program.Type type, + String name) throws IOException { String original = IOUtils.toString(sw, input); if (name.contains("block_entity_diffuse") && type == Program.Type.FRAGMENT) { var analysisResult = moniLabs$analyzeShaderForColorWrite(original);