diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java index 77ed3f9e7..0cbadf1ee 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java @@ -16,6 +16,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler; import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig; import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks; import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig; +import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler; import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder; import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList; import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder; @@ -1613,6 +1614,11 @@ public class GLStateManager { ImmediateModeRecorder.setColor(red, green, blue, alpha); } dirtyColorAttrib = true; + // Only set while an enchantment-glint span is active + final GlintColorHandler glintHandler = GLSMHooks.glintColorHandler; + if (glintHandler != null) { + glintHandler.onColorChanged(red, green, blue, alpha); + } return true; } return false; diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/hooks/GLSMHooks.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/hooks/GLSMHooks.java index fc1550af5..ff9a603aa 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/hooks/GLSMHooks.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/hooks/GLSMHooks.java @@ -8,6 +8,7 @@ public final class GLSMHooks { public static DeferredBlendHandler blendHandler; public static DeferredAlphaHandler alphaHandler; public static DeferredDepthColorHandler depthColorHandler; + public static GlintColorHandler glintColorHandler; // EventBus instances public static final EventBus TEXTURE_BIND = EventBus.create(TextureBindEvent.class); diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/hooks/GlintColorHandler.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/hooks/GlintColorHandler.java new file mode 100644 index 000000000..557821795 --- /dev/null +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/hooks/GlintColorHandler.java @@ -0,0 +1,5 @@ +package com.gtnewhorizons.angelica.glsm.hooks; + +public interface GlintColorHandler { + void onColorChanged(float red, float green, float blue, float alpha); +} diff --git a/src/main/java/com/gtnewhorizons/angelica/compat/ModStatus.java b/src/main/java/com/gtnewhorizons/angelica/compat/ModStatus.java index ad87924a1..289d1f077 100644 --- a/src/main/java/com/gtnewhorizons/angelica/compat/ModStatus.java +++ b/src/main/java/com/gtnewhorizons/angelica/compat/ModStatus.java @@ -30,6 +30,7 @@ public class ModStatus { public static boolean isCosmeticArmorReworkedLoaded; public static boolean isFluidLoggedLoaded; public static boolean isBOPLoaded; + public static boolean isEtFuturumLoaded; public static void preInit() { if (Loader.isModLoaded("backhand")) { @@ -49,6 +50,7 @@ public class ModStatus { isBaublesLoaded = Loader.isModLoaded("Baubles"); isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked"); isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged"); + isEtFuturumLoaded = Loader.isModLoaded("etfuturum"); isHoloInventoryLoaded = Loader.isModLoaded("holoinventory"); isBOPLoaded = Loader.isModLoaded("BiomesOPlenty"); diff --git a/src/main/java/com/gtnewhorizons/angelica/compat/etfuturum/EtFuturumCompat.java b/src/main/java/com/gtnewhorizons/angelica/compat/etfuturum/EtFuturumCompat.java new file mode 100644 index 000000000..175c3af7e --- /dev/null +++ b/src/main/java/com/gtnewhorizons/angelica/compat/etfuturum/EtFuturumCompat.java @@ -0,0 +1,51 @@ +package com.gtnewhorizons.angelica.compat.etfuturum; + +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.relauncher.ReflectionHelper; +import lombok.Getter; +import net.minecraft.entity.player.EntityPlayer; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER; + +public final class EtFuturumCompat { + private static final String IELYTRA_PLAYER = "ganymedes01.etfuturum.api.elytra.IElytraPlayer"; + + @Getter + private static boolean loaded; + private static Class elytraPlayerClass; + private static final MethodHandle isElytraFlyingHandle; + + static { + MethodHandle handle = null; + try { + if (Loader.isModLoaded("etfuturum")) { + elytraPlayerClass = ReflectionHelper.getClass(EtFuturumCompat.class.getClassLoader(), IELYTRA_PLAYER); + handle = MethodHandles.lookup().findVirtual(elytraPlayerClass, "etfu$isElytraFlying", + MethodType.methodType(boolean.class)); + loaded = true; + } + } catch (Throwable t) { + LOGGER.error("Failed to load Et Futurum Requiem elytra compat", t); + loaded = false; + } + isElytraFlyingHandle = handle; + } + + private EtFuturumCompat() { + } + + public static boolean isElytraFlying(EntityPlayer player) { + if (!loaded || isElytraFlyingHandle == null || !elytraPlayerClass.isInstance(player)) { + return false; + } + try { + return (boolean) isElytraFlyingHandle.invoke(player); + } catch (Throwable t) { + return false; + } + } +} diff --git a/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java b/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java index b9de08dac..350f5eb77 100644 --- a/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java +++ b/src/main/java/com/gtnewhorizons/angelica/mixins/Mixins.java @@ -298,6 +298,7 @@ public enum Mixins implements IMixins { , "shaders.MixinRenderEndPortal" , "shaders.MixinTileEntityRendererDispatcher" , "shaders.MixinGlProgram" + , "shaders.MixinTextureManager_ReloadCount" ) ), diff --git a/src/main/java/com/gtnewhorizons/angelica/shadercompat/ShaderGlint.java b/src/main/java/com/gtnewhorizons/angelica/shadercompat/ShaderGlint.java new file mode 100644 index 000000000..e00393a56 --- /dev/null +++ b/src/main/java/com/gtnewhorizons/angelica/shadercompat/ShaderGlint.java @@ -0,0 +1,166 @@ +package com.gtnewhorizons.angelica.shadercompat; + +import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks; +import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler; +import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; +import net.coderbot.iris.pipeline.ShadowRenderer; +import net.irisshaders.iris.api.v0.IrisApi; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.nio.ByteBuffer; + +/** + * Maybe overcomplicated, but it's required to bridge the gap between the modern glint texture and white texture 1.7.10 + * has. Some shaders are able to render the glint just fine as they seem to have some amount of support for older + * Minecraft, but others like AstraLex do *not* render correctly. It expects the color to be within the texture. + * This might introduce some oddities that I'm not seeing, but for now this appears to fix the issue. + */ +public final class ShaderGlint { + + private static final ResourceLocation GLINT_MASK = new ResourceLocation("textures/misc/enchanted_item_glint.png"); + private static final int MAX_CACHE = 64; + + private static boolean maskLoaded; + private static boolean maskFailed; + private static int maskW; + private static int maskH; + private static int[] maskLum; + + private static final Int2IntLinkedOpenHashMap colorToTexture = new Int2IntLinkedOpenHashMap(); + static { + colorToTexture.defaultReturnValue(0); + } + + private static final GlintColorHandler COLOR_HANDLER = ShaderGlint::onColorChanged; + + private static boolean injecting; + private static boolean swapped; + private static int depth; + + private static int prevTexture; + + private static ByteBuffer tintBuffer; + + private ShaderGlint() {} + + public static void beginGlint() { + if (ShadowRenderer.ACTIVE || !IrisApi.getInstance().isShaderPackInUse()) return; + + // If a glint span is already active, keep the outer span's saved state and + // let the nested render ride on its handler. + if (depth++ > 0) return; + + swapped = false; + injecting = false; + + // Save the texture bound before the glint section so endGlint can put it back if we swap + prevTexture = GLStateManager.getBoundTextureForServerState(0); + + // Observe vertex-color changes for the rest of the glint section + GLSMHooks.glintColorHandler = COLOR_HANDLER; + } + + public static void endGlint() { + if (depth == 0 || --depth > 0) return; + if (GLSMHooks.glintColorHandler != COLOR_HANDLER) return; + GLSMHooks.glintColorHandler = null; + + if (swapped) { + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, prevTexture); + } + swapped = false; + injecting = false; + } + + private static void onColorChanged(float red, float green, float blue, float alpha) { + if (injecting) return; + + final int texture = getTintedTexture(red, green, blue); + if (texture <= 0) return; + + injecting = true; + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); + GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, alpha); + injecting = false; + swapped = true; + } + + private static int getTintedTexture(float r, float g, float b) { + ensureMaskLoaded(); + if (!maskLoaded) return -1; + + final int ri = clamp8(r); + final int gi = clamp8(g); + final int bi = clamp8(b); + final int key = (ri << 16) | (gi << 8) | bi; + + final int cached = colorToTexture.getAndMoveToLast(key); + if (cached != 0) return cached; + + if (colorToTexture.size() >= MAX_CACHE) { + GLStateManager.glDeleteTextures(colorToTexture.remove(colorToTexture.firstIntKey())); + } + + final int texture = uploadTinted(ri / 255.0F, gi / 255.0F, bi / 255.0F); + colorToTexture.putAndMoveToLast(key, texture); + return texture; + } + + private static int uploadTinted(float r, float g, float b) { + final ByteBuffer pixels = tintBuffer; + pixels.clear(); + for (final int l : maskLum) { + pixels.put((byte) Math.round(l * r)); + pixels.put((byte) Math.round(l * g)); + pixels.put((byte) Math.round(l * b)); + pixels.put((byte) 0xFF); + } + pixels.flip(); + + final int texture = GLStateManager.glGenTextures(); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); + // Match the vanilla glint texture + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT); + GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, maskW, maskH, 0, + GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels); + return texture; + } + + private static void ensureMaskLoaded() { + if (maskLoaded || maskFailed) return; + try (final InputStream in = Minecraft.getMinecraft().getResourceManager().getResource(GLINT_MASK).getInputStream()) { + final BufferedImage img = ImageIO.read(in); + maskW = img.getWidth(); + maskH = img.getHeight(); + maskLum = new int[maskW * maskH]; + final int[] argb = img.getRGB(0, 0, maskW, maskH, null, 0, maskW); + for (int i = 0; i < argb.length; i++) { + final int p = argb[i]; + final int rr = (p >> 16) & 0xFF; + final int gg = (p >> 8) & 0xFF; + final int bb = p & 0xFF; + maskLum[i] = (rr + gg + bb) / 3; + } + tintBuffer = BufferUtils.createByteBuffer(maskW * maskH * 4); + maskLoaded = true; + } catch (final Exception e) { + maskFailed = true; + } + } + + private static int clamp8(float v) { + final int i = Math.round(v * 255.0F); + if (i < 0) return 0; + return Math.min(i, 255); + } +} diff --git a/src/main/java/net/coderbot/iris/Iris.java b/src/main/java/net/coderbot/iris/Iris.java index d9acd4c8b..f3561779f 100644 --- a/src/main/java/net/coderbot/iris/Iris.java +++ b/src/main/java/net/coderbot/iris/Iris.java @@ -36,6 +36,7 @@ import net.coderbot.iris.shaderpack.option.Profile; import net.coderbot.iris.shaderpack.option.values.MutableOptionValues; import net.coderbot.iris.shaderpack.option.values.OptionValues; import net.coderbot.iris.texture.pbr.PBRTextureManager; +import net.coderbot.iris.uniforms.CapturedRenderingState; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -567,11 +568,11 @@ public class Iris { // If it's a folder-based shaderpack, just use the shaders subdirectory shaderPackPath = shaderPackRoot.resolve("shaders"); - } - if (!Files.exists(shaderPackPath)) { - logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name); - return false; + if (!Files.exists(shaderPackPath)) { + logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name); + return false; + } } @SuppressWarnings("unchecked") @@ -762,6 +763,9 @@ public class Iris { // allows shaderpacks to be changed at runtime irisConfig.initialize(); + // Reset the texture reload counter + CapturedRenderingState.INSTANCE.resetTextureReloadCount(); + // Destroy all allocated resources destroyEverything(); diff --git a/src/main/java/net/coderbot/iris/features/FeatureFlags.java b/src/main/java/net/coderbot/iris/features/FeatureFlags.java index b461de997..871f9fdbf 100644 --- a/src/main/java/net/coderbot/iris/features/FeatureFlags.java +++ b/src/main/java/net/coderbot/iris/features/FeatureFlags.java @@ -10,13 +10,13 @@ import java.util.function.BooleanSupplier; public enum FeatureFlags { SEPARATE_HARDWARE_SAMPLERS(() -> true, () -> true), PER_BUFFER_BLENDING(() -> true, RenderSystem::supportsBufferBlending), - COMPUTE_SHADERS(() -> true, RenderSystem::supportsCompute), + COMPUTE_SHADERS(() -> false, RenderSystem::supportsCompute), ENTITY_TRANSLUCENT(() -> true, () -> true), BLOCK_EMISSION_ATTRIBUTE(() -> true, () -> true), CUSTOM_IMAGES(() -> true, RenderSystem::supportsImageLoadStore), SSBO(() -> true, RenderSystem::supportsSSBO), HIGHER_SHADOWCOLOR(() -> true, () -> true), - TESSELLATION_SHADERS(() -> true, RenderSystem::supportsTesselation), + TESSELLATION_SHADERS(() -> false, RenderSystem::supportsTesselation), REVERSED_CULLING(() -> true, () -> true), CAN_DISABLE_WEATHER(() -> true, () -> true), UNKNOWN(() -> false, () -> false); diff --git a/src/main/java/net/coderbot/iris/gl/GlVersion.java b/src/main/java/net/coderbot/iris/gl/GlVersion.java index 7a72e007a..f67d0cbcd 100644 --- a/src/main/java/net/coderbot/iris/gl/GlVersion.java +++ b/src/main/java/net/coderbot/iris/gl/GlVersion.java @@ -4,5 +4,7 @@ public enum GlVersion { GL_11, GL_12, GL_30, - GL_31 + GL_31, + GL_33, + GL_41 } diff --git a/src/main/java/net/coderbot/iris/gl/shader/StandardMacros.java b/src/main/java/net/coderbot/iris/gl/shader/StandardMacros.java index 2e7221d0e..90246902d 100644 --- a/src/main/java/net/coderbot/iris/gl/shader/StandardMacros.java +++ b/src/main/java/net/coderbot/iris/gl/shader/StandardMacros.java @@ -132,6 +132,11 @@ public class StandardMacros { getBiomeCategoryDefines().forEach((category, ordinal) -> define(standardDefines, category, ordinal)); getVanillaBiomeDefines().forEach((biome, id) -> define(standardDefines, biome, id)); + // rain type constants + define(standardDefines, "PPT_NONE", "0"); + define(standardDefines, "PPT_RAIN", "1"); + define(standardDefines, "PPT_SNOW", "2"); + return ImmutableList.copyOf(standardDefines); } diff --git a/src/main/java/net/coderbot/iris/gl/texture/InternalTextureFormat.java b/src/main/java/net/coderbot/iris/gl/texture/InternalTextureFormat.java index 44c4f2fa7..755e5609c 100644 --- a/src/main/java/net/coderbot/iris/gl/texture/InternalTextureFormat.java +++ b/src/main/java/net/coderbot/iris/gl/texture/InternalTextureFormat.java @@ -5,6 +5,8 @@ import net.coderbot.iris.gl.GlVersion; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL31; +import org.lwjgl.opengl.GL33; +import org.lwjgl.opengl.GL41; import java.util.Optional; @@ -72,10 +74,16 @@ public enum InternalTextureFormat { RG32UI(GL30.GL_RG32UI, GlVersion.GL_30, PixelFormat.RG_INTEGER), RGB32UI(GL30.GL_RGB32UI, GlVersion.GL_30, PixelFormat.RGB_INTEGER), RGBA32UI(GL30.GL_RGBA32UI, GlVersion.GL_30, PixelFormat.RGBA_INTEGER), + // 2-bit normalized + RGBA2(GL11.GL_RGBA2, GlVersion.GL_11, PixelFormat.RGBA), + // 4-bit normalized + RGBA4(GL11.GL_RGBA4, GlVersion.GL_11, PixelFormat.RGBA), // Mixed R3_G3_B2(GL11.GL_R3_G3_B2, GlVersion.GL_11, PixelFormat.RGB), RGB5_A1(GL11.GL_RGB5_A1, GlVersion.GL_11, PixelFormat.RGBA), + RGB565(GL41.GL_RGB565, GlVersion.GL_41, PixelFormat.RGB), RGB10_A2(GL11.GL_RGB10_A2, GlVersion.GL_11, PixelFormat.RGBA), + RGB10_A2UI(GL33.GL_RGB10_A2UI, GlVersion.GL_33, PixelFormat.RGBA_INTEGER), R11F_G11F_B10F(GL30.GL_R11F_G11F_B10F, GlVersion.GL_30, PixelFormat.RGB), RGB9_E5(GL30.GL_RGB9_E5, GlVersion.GL_30, PixelFormat.RGB); diff --git a/src/main/java/net/coderbot/iris/pipeline/DeferredWorldRenderingPipeline.java b/src/main/java/net/coderbot/iris/pipeline/DeferredWorldRenderingPipeline.java index c343701ca..f17d4f2f9 100644 --- a/src/main/java/net/coderbot/iris/pipeline/DeferredWorldRenderingPipeline.java +++ b/src/main/java/net/coderbot/iris/pipeline/DeferredWorldRenderingPipeline.java @@ -42,6 +42,7 @@ import net.coderbot.iris.gl.texture.DepthBufferFormat; import net.coderbot.iris.gl.texture.TextureType; import net.coderbot.iris.helpers.Tri; import net.coderbot.iris.layer.GbufferPrograms; +import net.coderbot.iris.pipeline.transform.GlintScrollInjector; import net.coderbot.iris.pipeline.transform.PatchShaderType; import net.coderbot.iris.pipeline.transform.TransformPatcher; import net.coderbot.iris.postprocess.BufferFlipper; @@ -838,13 +839,16 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R if (transformed == null) { // Fallback to synchronous transform if not pre-computed + String vertex = source.getVertexSource().orElseThrow(NullPointerException::new); + final boolean scrollGlint = GlintScrollInjector.shouldInject(id, source); transformed = TransformPatcher.patchAttributes( - source.getVertexSource().orElseThrow(NullPointerException::new), + vertex, source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElseThrow(NullPointerException::new), - availability); + availability, + scrollGlint); } String vertex = transformed.get(PatchShaderType.VERTEX); @@ -1784,9 +1788,12 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R ProgramSource source = resolver.resolveNullable(id); if (source != null && !processedSourceNames.contains(source.getName())) { processedSourceNames.add(source.getName()); + final ProgramSource src = source; + final String vertexSource = src.getVertexSource().orElse(null); + final boolean scrollGlint = GlintScrollInjector.shouldInject(id, src); for (InputAvailability avail : INPUT_AVAILABILITIES) { - Pair key = Pair.of(source.getName(), avail); - futures.put(key, Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchAttributes(source.getVertexSource().orElse(null), source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElse(null), avail))); + Pair key = Pair.of(src.getName(), avail); + futures.put(key, Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchAttributes(vertexSource, src.getGeometrySource().orElse(null), src.getTessControlSource().orElse(null), src.getTessEvalSource().orElse(null), src.getFragmentSource().orElse(null), avail, scrollGlint))); } } } diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/AttributeTransformer.java b/src/main/java/net/coderbot/iris/pipeline/transform/AttributeTransformer.java index e5dc10b61..aa06e0532 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/AttributeTransformer.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/AttributeTransformer.java @@ -3,6 +3,7 @@ package net.coderbot.iris.pipeline.transform; import net.coderbot.iris.gl.shader.ShaderType; import net.coderbot.iris.pipeline.transform.parameter.AttributeParameters; import org.taumc.glsl.Transformer; +import org.taumc.glsl.grammar.GLSLParser; import java.util.HashMap; import java.util.Map; @@ -13,6 +14,10 @@ import java.util.Map; */ class AttributeTransformer { public static void transform(Transformer transformer, AttributeParameters parameters, int version) { + if (parameters.scrollGlint && parameters.type == ShaderType.VERTEX) { + transformer.replaceExpression("gl_MultiTexCoord0", "(gl_TextureMatrix[0] * gl_MultiTexCoord0)", GLSLParser::postfix_expression); + } + // Always core profile — minimum GLSL version is 330 (see ShaderTransformer.getStageMinimumVersion) CommonTransformer.transform(transformer, parameters, true, version); diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/CeleritasTransformer.java b/src/main/java/net/coderbot/iris/pipeline/transform/CeleritasTransformer.java index a8bb4300e..8c5783297 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/CeleritasTransformer.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/CeleritasTransformer.java @@ -46,6 +46,10 @@ class CeleritasTransformer { vertexReplacements.put("gl_MultiTexCoord2", "iris_LightTexCoord"); vertexReplacements.forEach(transformer::replaceExpression); + if (transformer.hasVariable("chunkOffset")) { + transformer.removeVariable("chunkOffset"); + } + final Map vertexRenames = new HashMap<>(); vertexRenames.put("gl_Color", "_vert_color"); vertexRenames.put("ftransform", "iris_ftransform"); diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/CommonTransformer.java b/src/main/java/net/coderbot/iris/pipeline/transform/CommonTransformer.java index fed34cd02..ded7984fe 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/CommonTransformer.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/CommonTransformer.java @@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GlslTransformUtils; import net.coderbot.iris.gl.shader.ShaderType; import net.coderbot.iris.pipeline.transform.parameter.Parameters; import org.taumc.glsl.Transformer; +import org.taumc.glsl.grammar.GLSLParser; import java.util.HashSet; import java.util.Set; @@ -29,6 +30,16 @@ public class CommonTransformer { root.rename("gl_Color", "iris_FrontColor"); } + if (root.hasVariable("gl_TexCoord")) { + if (parameters.type == ShaderType.VERTEX) { + root.rename("gl_TexCoord", "irs_texCoords"); + root.injectVariable("out vec4 irs_texCoords[3];"); + } else if (parameters.type == ShaderType.FRAGMENT) { + root.rename("gl_TexCoord", "irs_texCoords"); + root.injectVariable("in vec4 irs_texCoords[3];"); + } + } + if (parameters.type == ShaderType.FRAGMENT) { if (root.containsCall("gl_FragColor")) { root.replaceExpression("gl_FragColor", "gl_FragData[0]"); @@ -91,6 +102,27 @@ public class CommonTransformer { root.renameAndWrapShadow("shadow2D", "texture"); root.renameAndWrapShadow("shadow2DLod", "textureLod"); + if (root.containsCall("textureLodOffset")) { + root.injectFunction("vec4 iris_textureLodOffset(sampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }"); + root.injectFunction("float iris_textureLodOffset(sampler2DShadow iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, vec3(iris_tlo_c.xy + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_c.z), iris_tlo_l); }"); + root.injectFunction("vec4 iris_textureLodOffset(sampler3D iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec3 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec3(iris_tlo_o) / vec3(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }"); + root.injectFunction("vec4 iris_textureLodOffset(sampler2DArray iris_tlo_s, vec3 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, vec3(iris_tlo_c.xy + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l)).xy), iris_tlo_c.z), iris_tlo_l); }"); + root.injectFunction("ivec4 iris_textureLodOffset(isampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }"); + root.injectFunction("uvec4 iris_textureLodOffset(usampler2D iris_tlo_s, vec2 iris_tlo_c, float iris_tlo_l, ivec2 iris_tlo_o) { return textureLod(iris_tlo_s, iris_tlo_c + vec2(iris_tlo_o) / vec2(textureSize(iris_tlo_s, int(iris_tlo_l))), iris_tlo_l); }"); + root.renameFunctionCall("textureLodOffset", "iris_textureLodOffset"); + } + + if (parameters.type == ShaderType.VERTEX) { + root.mutateTree(tree -> { + if (tree.children != null) { + tree.children.removeIf(child -> + child instanceof GLSLParser.External_declarationContext + && ((GLSLParser.External_declarationContext) child).declaration() != null + && child.getText().contains("gl_PerVertex")); + } + }); + } + if (parameters.patch == Patch.ATTRIBUTES && parameters.type == ShaderType.VERTEX) { root.injectVariable("uniform bool angelica_ClipPlanesEnabled;"); root.injectVariable("uniform vec4 angelica_ClipPlane[8];"); diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/CompatibilityTransformer.java b/src/main/java/net/coderbot/iris/pipeline/transform/CompatibilityTransformer.java index 228196101..ccf461775 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/CompatibilityTransformer.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/CompatibilityTransformer.java @@ -3,6 +3,7 @@ package net.coderbot.iris.pipeline.transform; import net.coderbot.iris.gl.shader.ShaderType; import net.coderbot.iris.pipeline.transform.parameter.Parameters; import org.antlr.v4.runtime.tree.ParseTree; +import org.taumc.glsl.ShaderPrinter; import org.taumc.glsl.Transformer; import org.taumc.glsl.grammar.GLSLLexer; import org.taumc.glsl.grammar.GLSLParser; @@ -109,7 +110,10 @@ public class CompatibilityTransformer { continue; } - prevTransformer.makeOutDeclaration(inDec.get(in), in); + String outDeclaration = ShaderPrinter.getFormattedShader(inDec.get(in).fully_specified_type()) + + " " + in + ";"; + prevTransformer.variable = null; + prevTransformer.injectVariable(outDeclaration.replaceFirst("in", "out")); if (!prevTransformer.hasAssigment(in)) { prevTransformer.initialize(inDec.get(in), in); @@ -126,6 +130,23 @@ public class CompatibilityTransformer { if (!prevTransformer.hasAssigment(in)) { prevTransformer.initialize(inDec.get(in), in); } + } else { + int outComp = vecComponents(outType.getText()); + int inComp = vecComponents(inType.getText()); + if (outComp < 0 || inComp < 0) { + continue; + } + + String temp = "iris_template_" + in; + prevTransformer.rename(in, temp); + String newOut = ShaderPrinter.getFormattedShader(inDec.get(in).fully_specified_type()) + + " " + in + ";"; + prevTransformer.variable = null; + prevTransformer.injectVariable(newOut.replaceFirst("in", "out")); + String cast = outComp < inComp + ? in + " = " + inType.getText() + "(" + temp + ", vec4(0));" + : in + " = " + inType.getText() + "(" + temp + ");"; + prevTransformer.appendMain(cast); } } } @@ -133,4 +154,13 @@ public class CompatibilityTransformer { prevType = type; } } + + private static int vecComponents(String type) { + return switch (type) { + case "vec2" -> 2; + case "vec3" -> 3; + case "vec4" -> 4; + default -> -1; + }; + } } diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/CompositeDepthTransformer.java b/src/main/java/net/coderbot/iris/pipeline/transform/CompositeDepthTransformer.java index 0a4746289..933a49098 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/CompositeDepthTransformer.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/CompositeDepthTransformer.java @@ -18,7 +18,7 @@ class CompositeDepthTransformer { final int type = transformer.findType("centerDepthSmooth"); if (type != 0) { transformer.injectVariable("uniform sampler2D iris_centerDepthSmooth;"); - transformer.replaceExpression("centerDepthSmooth", "texture2D(iris_centerDepthSmooth, vec2(0.5)).r"); + transformer.replaceExpression("centerDepthSmooth", "texture(iris_centerDepthSmooth, vec2(0.5)).r"); } } } diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/CoreTransformHelper.java b/src/main/java/net/coderbot/iris/pipeline/transform/CoreTransformHelper.java index 2a4930a97..d0d7083e9 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/CoreTransformHelper.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/CoreTransformHelper.java @@ -51,6 +51,13 @@ class CoreTransformHelper { transformer.rename("gl_Vertex", "iris_Vertex"); transformer.rename("gl_MultiTexCoord0", "iris_MultiTexCoord0"); + for (int i = 1; i <= 7; i++) { + transformer.replaceExpression("gl_MultiTexCoord" + i, "vec4(0.0, 0.0, 0.0, 1.0)"); + } + + transformer.replaceExpression("gl_Color", "vec4(1.0, 1.0, 1.0, 1.0)"); + transformer.replaceExpression("gl_Normal", "vec3(0.0, 0.0, 1.0)"); + transformer.renameFunctionCall("ftransform", "iris_ftransform"); transformer.injectFunction("vec4 iris_ftransform() { return (iris_ProjectionMatrix * iris_ModelViewMatrix) * iris_Vertex; }"); diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/GlintScrollInjector.java b/src/main/java/net/coderbot/iris/pipeline/transform/GlintScrollInjector.java new file mode 100644 index 000000000..a6a442070 --- /dev/null +++ b/src/main/java/net/coderbot/iris/pipeline/transform/GlintScrollInjector.java @@ -0,0 +1,24 @@ +package net.coderbot.iris.pipeline.transform; + +import net.coderbot.iris.shaderpack.ProgramSource; +import net.coderbot.iris.shaderpack.loading.ProgramId; + +/** + * For some reason not all shaders read from gl_MultiTexCoord0 through the texture matrix to scroll the enchant glint? + * Maybe a modern thing but a few shaders just look wrong without it. Technically not in spec, but it looks better. + */ +public final class GlintScrollInjector { + + private GlintScrollInjector() {} + + public static boolean shouldInject(ProgramId id, ProgramSource source) { + if (id != ProgramId.ArmorGlint || source == null) return false; + if (!ProgramId.ArmorGlint.getSourceName().equals(source.getName())) return false; + + final String vertex = source.getVertexSource().orElse(null); + if (vertex == null) return false; + // Already scrolls through the texture matrix itself + if (vertex.contains("gl_TextureMatrix[0]")) return false; + return vertex.contains("gl_MultiTexCoord0"); + } +} diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/TransformPatcher.java b/src/main/java/net/coderbot/iris/pipeline/transform/TransformPatcher.java index 365b584d1..3e71f3adc 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/TransformPatcher.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/TransformPatcher.java @@ -104,8 +104,12 @@ public class TransformPatcher { return result; } + public static Map patchAttributes(String vertex, String geometry, String tessControl, String tessEval, String fragment, InputAvailability inputs, boolean scrollGlint) { + return transform(vertex, geometry, tessControl, tessEval, fragment, new AttributeParameters(Patch.ATTRIBUTES, geometry != null, inputs, scrollGlint)); + } + public static Map patchAttributes(String vertex, String geometry, String tessControl, String tessEval, String fragment, InputAvailability inputs) { - return transform(vertex, geometry, tessControl, tessEval, fragment, new AttributeParameters(Patch.ATTRIBUTES, geometry != null, inputs)); + return patchAttributes(vertex, geometry, tessControl, tessEval, fragment, inputs, false); } public static Map patchAttributes(String vertex, String geometry, String fragment, InputAvailability inputs) { diff --git a/src/main/java/net/coderbot/iris/pipeline/transform/parameter/AttributeParameters.java b/src/main/java/net/coderbot/iris/pipeline/transform/parameter/AttributeParameters.java index 284cec99e..9e98a41ba 100644 --- a/src/main/java/net/coderbot/iris/pipeline/transform/parameter/AttributeParameters.java +++ b/src/main/java/net/coderbot/iris/pipeline/transform/parameter/AttributeParameters.java @@ -7,12 +7,14 @@ import net.coderbot.iris.shaderpack.texture.TextureStage; public class AttributeParameters extends Parameters { public final boolean hasGeometry; public final InputAvailability inputs; + public final boolean scrollGlint; // WARNING: adding new fields requires updating hashCode and equals methods! - public AttributeParameters(Patch patch, boolean hasGeometry, InputAvailability inputs) { + public AttributeParameters(Patch patch, boolean hasGeometry, InputAvailability inputs, boolean scrollGlint) { super(patch, null); this.hasGeometry = hasGeometry; this.inputs = inputs; + this.scrollGlint = scrollGlint; } @Override @@ -25,6 +27,7 @@ public class AttributeParameters extends Parameters { final int prime = 31; int result = super.hashCode(); result = prime * result + (hasGeometry ? 1231 : 1237); + result = prime * result + (scrollGlint ? 1231 : 1237); result = prime * result + ((inputs == null) ? 0 : inputs.hashCode()); return result; } @@ -40,6 +43,8 @@ public class AttributeParameters extends Parameters { AttributeParameters other = (AttributeParameters) obj; if (hasGeometry != other.hasGeometry) return false; + if (scrollGlint != other.scrollGlint) + return false; if (inputs == null) { if (other.inputs != null) return false; diff --git a/src/main/java/net/coderbot/iris/shaderpack/IdMap.java b/src/main/java/net/coderbot/iris/shaderpack/IdMap.java index 7272d6039..8b049d28e 100644 --- a/src/main/java/net/coderbot/iris/shaderpack/IdMap.java +++ b/src/main/java/net/coderbot/iris/shaderpack/IdMap.java @@ -77,11 +77,22 @@ public class IdMap { private static final Pattern LEGACY_DIRECTIVE_PATTERN = Pattern.compile( "(?m)^\\s*#\\s*(?:if|elif|ifdef|ifndef)\\b[^\\n]*\\bMC_VERSION\\b[^\\n]*\\b10710\\b"); + /** + * Detects whether a pack's block.properties has a dedicated 1.7.10 section. + */ + public static boolean detectLegacySection(Path shaderPath) { + String rawBlockProperties = readProperties(shaderPath, "block.properties"); + return rawBlockProperties != null && LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find(); + } + + public static String modernFallbackMcVersion() { + return AngelicaConfig.modernFallbackMcVersion > 0 + ? String.valueOf(AngelicaConfig.modernFallbackMcVersion) : "260101"; + } + IdMap(Path shaderPath, ShaderPackOptions shaderPackOptions, Iterable environmentDefines) { // Check if block.properties has a dedicated 1.7.10 section - String rawBlockProperties = readProperties(shaderPath, "block.properties"); - this.hasLegacySection = rawBlockProperties != null - && LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find(); + this.hasLegacySection = detectLegacySection(shaderPath); Iterable resolvedDefines; if (this.hasLegacySection) { @@ -100,9 +111,7 @@ public class IdMap { } } - String modernVersion = AngelicaConfig.modernFallbackMcVersion > 0 - ? String.valueOf(AngelicaConfig.modernFallbackMcVersion) : "260101"; - modernDefines.add(new StringPair("MC_VERSION", modernVersion)); + modernDefines.add(new StringPair("MC_VERSION", modernFallbackMcVersion())); resolvedDefines = modernDefines; loadProperties(shaderPath, "block.properties", shaderPackOptions, modernDefines).ifPresent(blockProperties -> { diff --git a/src/main/java/net/coderbot/iris/shaderpack/ProgramSet.java b/src/main/java/net/coderbot/iris/shaderpack/ProgramSet.java index 2fe6b12db..8348892ea 100644 --- a/src/main/java/net/coderbot/iris/shaderpack/ProgramSet.java +++ b/src/main/java/net/coderbot/iris/shaderpack/ProgramSet.java @@ -1,6 +1,7 @@ package net.coderbot.iris.shaderpack; import net.coderbot.iris.Iris; +import net.coderbot.iris.features.FeatureFlags; import net.coderbot.iris.gl.blending.BlendModeOverride; import net.coderbot.iris.shaderpack.include.AbsolutePackPath; import net.coderbot.iris.shaderpack.loading.ProgramId; @@ -529,7 +530,8 @@ public class ProgramSet { String tessControlSource = null; String tessEvalSource = null; - if (programSet.pack.hasFeature(net.coderbot.iris.features.FeatureFlags.TESSELLATION_SHADERS)) { + if (programSet.pack.hasFeature(FeatureFlags.TESSELLATION_SHADERS) + && FeatureFlags.TESSELLATION_SHADERS.isUsable()) { AbsolutePackPath tessControlPath = directory.resolve(program + ".tcs"); tessControlSource = sourceProvider.apply(tessControlPath); @@ -547,6 +549,10 @@ public class ProgramSet { private static ComputeSource readComputeSource(AbsolutePackPath directory, Function sourceProvider, String program, ProgramSet programSet) { + if (!FeatureFlags.COMPUTE_SHADERS.isUsable()) { + return null; + } + AbsolutePackPath computePath = directory.resolve(program + ".csh"); String computeSource = sourceProvider.apply(computePath); diff --git a/src/main/java/net/coderbot/iris/shaderpack/ShaderPack.java b/src/main/java/net/coderbot/iris/shaderpack/ShaderPack.java index f0de1f1d8..11c3f6f50 100644 --- a/src/main/java/net/coderbot/iris/shaderpack/ShaderPack.java +++ b/src/main/java/net/coderbot/iris/shaderpack/ShaderPack.java @@ -207,8 +207,16 @@ public class ShaderPack { this.shaderPackOptions = new ShaderPackOptions(graph, changedConfigs); graph = this.shaderPackOptions.getIncludes(); + final String mcVersionOverride = IdMap.detectLegacySection(root) ? null : IdMap.modernFallbackMcVersion(); + List finalEnvironmentDefines = new ArrayList<>(); - environmentDefines.forEach(finalEnvironmentDefines::add); + for (StringPair define : environmentDefines) { + if (mcVersionOverride != null && "MC_VERSION".equals(define.getKey())) { + finalEnvironmentDefines.add(new StringPair("MC_VERSION", mcVersionOverride)); + } else { + finalEnvironmentDefines.add(define); + } + } for (FeatureFlags flag : FeatureFlags.values()) { boolean usable = flag.isUsable(); Iris.logger.info("FeatureFlag {} usable: {}", flag.name(), usable); diff --git a/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramArrayId.java b/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramArrayId.java index 1a1d0a941..97178276a 100644 --- a/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramArrayId.java +++ b/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramArrayId.java @@ -1,6 +1,8 @@ package net.coderbot.iris.shaderpack.loading; public enum ProgramArrayId { + Setup(ProgramGroup.Setup, 100), + Begin(ProgramGroup.Begin, 100), ShadowComposite(ProgramGroup.ShadowComposite, 99), Prepare(ProgramGroup.Prepare, 99), Deferred(ProgramGroup.Deferred, 99), diff --git a/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramGroup.java b/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramGroup.java index cf74efb0b..42bb64d1b 100644 --- a/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramGroup.java +++ b/src/main/java/net/coderbot/iris/shaderpack/loading/ProgramGroup.java @@ -1,6 +1,11 @@ package net.coderbot.iris.shaderpack.loading; +import lombok.Getter; + +@Getter public enum ProgramGroup { + Setup("setup"), + Begin("begin"), Shadow("shadow"), ShadowComposite("shadowcomp"), Prepare("prepare"), @@ -17,7 +22,4 @@ public enum ProgramGroup { this.baseName = baseName; } - public String getBaseName() { - return baseName; - } } diff --git a/src/main/java/net/coderbot/iris/shaderpack/materialmap/PropertiesTokenizer.java b/src/main/java/net/coderbot/iris/shaderpack/materialmap/PropertiesTokenizer.java index 5958e6b48..ab6d3ae05 100644 --- a/src/main/java/net/coderbot/iris/shaderpack/materialmap/PropertiesTokenizer.java +++ b/src/main/java/net/coderbot/iris/shaderpack/materialmap/PropertiesTokenizer.java @@ -1,9 +1,9 @@ package net.coderbot.iris.shaderpack.materialmap; import net.coderbot.iris.Iris; -import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; @@ -263,7 +263,19 @@ public final class PropertiesTokenizer { ); } - final String[] splitStates = baseEntry.split(":"); + // Drop empty segments produced by consecutive colons (like "betterendforge::lumecorn") + final String[] splitStates = Arrays.stream(baseEntry.split(":")) + .filter(s -> !s.isEmpty()) + .toArray(String[]::new); + + if (splitStates.length == 0) { + return new ParsedBlockIdentifier( + new NamespacedId("minecraft", baseEntry), + Collections.emptySet(), + Collections.emptyMap(), + nbtProperties + ); + } // Vanilla block (minecraft namespaced) if (splitStates.length == 1) { @@ -277,7 +289,7 @@ public final class PropertiesTokenizer { // Two-segment with no metas/state properties: "minecraft:stone" if (splitStates.length == 2 - && !StringUtils.isNumeric(splitStates[1].substring(0, 1)) + && !startsWithDigit(splitStates[1]) && !splitStates[1].contains("=")) { return new ParsedBlockIdentifier( new NamespacedId(splitStates[0], splitStates[1]), @@ -296,7 +308,7 @@ public final class PropertiesTokenizer { id = new NamespacedId("minecraft", splitStates[0]); statesStart = 1; - } else if (StringUtils.isNumeric(splitStates[1].substring(0, 1)) || splitStates[1].contains("=")) { + } else if (startsWithDigit(splitStates[1]) || splitStates[1].contains("=")) { // "stone:0:something" or "stone:lit=true:something", likely will need this for snowy blocks in the future. id = new NamespacedId("minecraft", splitStates[0]); statesStart = 1; @@ -339,6 +351,10 @@ public final class PropertiesTokenizer { ); } + private static boolean startsWithDigit(String s) { + return !s.isEmpty() && Character.isDigit(s.charAt(0)); + } + private static void appendCurrent(boolean inBracket, boolean readingValue, StringBuilder blockIdBuilder, StringBuilder nbtKey, StringBuilder nbtValue, char c) { diff --git a/src/main/java/net/coderbot/iris/shaderpack/preprocessor/PropertiesPreprocessor.java b/src/main/java/net/coderbot/iris/shaderpack/preprocessor/PropertiesPreprocessor.java index 1ee57ae96..f2fb0e53d 100644 --- a/src/main/java/net/coderbot/iris/shaderpack/preprocessor/PropertiesPreprocessor.java +++ b/src/main/java/net/coderbot/iris/shaderpack/preprocessor/PropertiesPreprocessor.java @@ -80,7 +80,7 @@ public class PropertiesPreprocessor { // Not super efficient, but this removes trailing whitespace on lines, fixing an issue with whitespace after // line continuations (see PreprocessorTest#testWeirdPropertiesLineContinuation) // Required for Voyager Shader - source = Arrays.stream(source.split("\\R")).map(String::trim).filter(s -> !s.isEmpty()) + source = Arrays.stream(source.split("\\r\\n|\\n|\\r")).map(String::trim).filter(s -> !s.isEmpty()) .map(line -> { if (line.startsWith("#")) { for (PreprocessorCommand command : PreprocessorCommand.values()) { diff --git a/src/main/java/net/coderbot/iris/uniforms/CapturedRenderingState.java b/src/main/java/net/coderbot/iris/uniforms/CapturedRenderingState.java index 817b9269b..7772352b1 100644 --- a/src/main/java/net/coderbot/iris/uniforms/CapturedRenderingState.java +++ b/src/main/java/net/coderbot/iris/uniforms/CapturedRenderingState.java @@ -13,6 +13,9 @@ public class CapturedRenderingState { @Getter private float tickDelta; @Getter + private int textureReloadCount; + private Runnable textureReloadListener = null; + @Getter private int currentRenderedBlockEntity; private Runnable blockEntityIdListener = null; @@ -31,6 +34,18 @@ public class CapturedRenderingState { private CapturedRenderingState() { } + public void incrementTextureReloadCount() { + this.textureReloadCount++; + + if (this.textureReloadListener != null) { + this.textureReloadListener.run(); + } + } + + public void resetTextureReloadCount() { + this.textureReloadCount = 0; + } + public void setCurrentBlockEntity(int entity) { this.currentRenderedBlockEntity = entity; @@ -80,5 +95,9 @@ public class CapturedRenderingState { return listener -> this.itemIdListener = listener; } + public ValueUpdateNotifier getTextureReloadNotifier() { + return listener -> this.textureReloadListener = listener; + } + public ValueUpdateNotifier getEntityColorNotifier() { return listener -> this.entityColorListener = listener; } } diff --git a/src/main/java/net/coderbot/iris/uniforms/CommonUniforms.java b/src/main/java/net/coderbot/iris/uniforms/CommonUniforms.java index 67358e4c2..f1da55d93 100644 --- a/src/main/java/net/coderbot/iris/uniforms/CommonUniforms.java +++ b/src/main/java/net/coderbot/iris/uniforms/CommonUniforms.java @@ -96,6 +96,12 @@ public final class CommonUniforms { }, StateUpdateNotifiers.bindTextureNotifier); + uniforms.uniform1i("gtextureId", () -> GLStateManager.getBoundTextureForServerState(0), + StateUpdateNotifiers.bindTextureNotifier); + + uniforms.uniform1i("textureReloadCount", CapturedRenderingState.INSTANCE::getTextureReloadCount, + CapturedRenderingState.INSTANCE.getTextureReloadNotifier()); + uniforms.uniform4i("blendFunc", () -> { if(GLStateManager.getBlendMode().isEnabled()) { final BlendState blend = GLStateManager.getBlendState(); diff --git a/src/main/java/net/coderbot/iris/uniforms/IrisExclusiveUniforms.java b/src/main/java/net/coderbot/iris/uniforms/IrisExclusiveUniforms.java index 8a14c2bdc..ceb534177 100644 --- a/src/main/java/net/coderbot/iris/uniforms/IrisExclusiveUniforms.java +++ b/src/main/java/net/coderbot/iris/uniforms/IrisExclusiveUniforms.java @@ -1,18 +1,27 @@ package net.coderbot.iris.uniforms; +import com.gtnewhorizons.angelica.compat.etfuturum.EtFuturumCompat; import com.gtnewhorizons.angelica.compat.mojang.Camera; import com.gtnewhorizons.angelica.compat.mojang.GameModeUtil; import com.gtnewhorizons.angelica.render.CloudRenderer; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import net.coderbot.iris.block_rendering.BlockMaterialMapping; +import net.coderbot.iris.block_rendering.BlockRenderingSettings; import net.coderbot.iris.gl.uniform.UniformHolder; import net.coderbot.iris.gl.uniform.UniformUpdateFrequency; +import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.MovingObjectPosition; import org.joml.Math; import org.joml.Vector3d; import org.joml.Vector3dc; +import org.joml.Vector3f; import org.joml.Vector4f; import java.util.List; @@ -21,9 +30,17 @@ public class IrisExclusiveUniforms { // Reusable vectors to avoid allocations every frame private static final Vector3d eyePositionCache = new Vector3d(); private static final Vector3d relativeEyePositionCache = new Vector3d(); + private static final Vector3d playerLookCache = new Vector3d(); + private static final Vector3d playerBodyCache = new Vector3d(); + private static final Vector3d vehicleLookCache = new Vector3d(); + private static final Vector3d relativeVehiclePositionCache = new Vector3d(); + private static final Vector3f selectedBlockPosCache = new Vector3f(); private static final Vector4f lightningBoltPositionCache = new Vector4f(); private static final Vector4f ZERO_VECTOR_4f = new Vector4f(0, 0, 0, 0); + private static int cachedSelectedFrame = -1; + private static int cachedSelectedBlockId; + public static void addIrisExclusiveUniforms(UniformHolder uniforms) { WorldInfoUniforms.addWorldInfoUniforms(uniforms); @@ -42,6 +59,21 @@ public class IrisExclusiveUniforms { uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "relativeEyePosition", IrisExclusiveUniforms::getRelativeEyePosition); uniforms.uniform4f(UniformUpdateFrequency.PER_TICK, "lightningBoltPosition", IrisExclusiveUniforms::getLightningBoltPosition); uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "cloudTime", IrisExclusiveUniforms::getCloudTime); + uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "feetInWater", IrisExclusiveUniforms::isFeetInWater); + uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "isRiding", IrisExclusiveUniforms::isRiding); + uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "vehicleInWater", IrisExclusiveUniforms::isVehicleInWater); + uniforms.uniform1i(UniformUpdateFrequency.PER_TICK, "vehicleId", IrisExclusiveUniforms::getVehicleId); + uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "vehicleLookVector", IrisExclusiveUniforms::getVehicleLookVector); + uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "relativeVehiclePosition", IrisExclusiveUniforms::getRelativeVehiclePosition); + uniforms.uniform1f(UniformUpdateFrequency.PER_TICK, "currentPlayerArmor", IrisExclusiveUniforms::getCurrentArmor); + uniforms.uniform1f(UniformUpdateFrequency.PER_TICK, "maxPlayerArmor", () -> 50); + uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "currentSelectedBlockId", IrisExclusiveUniforms::getCurrentSelectedBlockId); + uniforms.uniform3f(UniformUpdateFrequency.PER_FRAME, "currentSelectedBlockPos", IrisExclusiveUniforms::getCurrentSelectedBlockPos); + uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "seaLevel", IrisExclusiveUniforms::getSeaLevel); + uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "playerLookVector", IrisExclusiveUniforms::getPlayerLookVector); + uniforms.uniform3d(UniformUpdateFrequency.PER_FRAME, "playerBodyVector", IrisExclusiveUniforms::getPlayerBodyVector); + // Target the Elytra from EFR + uniforms.uniform1b(UniformUpdateFrequency.PER_TICK, "isElytraFlying", IrisExclusiveUniforms::isElytraFlying); } private static float getCloudTime() { @@ -137,6 +169,149 @@ public class IrisExclusiveUniforms { return ZERO_VECTOR_4f; } + private static EntityLivingBase cameraEntity() { + return Minecraft.getMinecraft().renderViewEntity; + } + + private static Vector3d forwardVector(Vector3d dest, float yawDeg, float pitchDeg) { + final double yr = -yawDeg * 0.017453292 - Math.PI; + final double pr = -pitchDeg * 0.017453292; + final double cy = Math.cos(yr); + final double sy = Math.sin(yr); + final double cp = -Math.cos(pr); + final double sp = Math.sin(pr); + return dest.set(sy * cp, sp, cy * cp); + } + + private static boolean isFeetInWater() { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + return player != null && player.isInWater(); + } + + private static boolean isElytraFlying() { + return EtFuturumCompat.isElytraFlying(Minecraft.getMinecraft().thePlayer); + } + + private static boolean isRiding() { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + return player != null && player.ridingEntity != null; + } + + private static boolean isVehicleInWater() { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + return player != null && player.ridingEntity != null && player.ridingEntity.isInWater(); + } + + private static int getVehicleId() { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + if (player == null || player.ridingEntity == null) return 0; + final int id = EntityIdHelper.getEntityId(player.ridingEntity); + return Math.max(id, 0); + } + + private static Vector3d getVehicleLookVector() { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + if (player == null || player.ridingEntity == null) return vehicleLookCache.set(0, 0, 0); + final Entity v = player.ridingEntity; + return forwardVector(vehicleLookCache, v.rotationYaw, v.rotationPitch); + } + + private static Vector3d getRelativeVehiclePosition() { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + if (player == null || player.ridingEntity == null) return relativeVehiclePositionCache.set(0, 0, 0); + final Entity v = player.ridingEntity; + final float delta = CapturedRenderingState.INSTANCE.getTickDelta(); + final double vx = v.prevPosX + (v.posX - v.prevPosX) * delta; + final double vy = v.prevPosY + (v.posY - v.prevPosY) * delta; + final double vz = v.prevPosZ + (v.posZ - v.prevPosZ) * delta; + // Match Angelica's relativeEyePosition convention (entity - camera). + final Vector3dc cam = CameraUniforms.getUnshiftedCameraPosition(); + return relativeVehiclePositionCache.set(vx - cam.x(), vy - cam.y(), vz - cam.z()); + } + + private static float getCurrentArmor() { + final Minecraft mc = Minecraft.getMinecraft(); + if (mc.thePlayer == null || !mc.playerController.gameIsSurvivalOrAdventure()) { + return -1; + } + return mc.thePlayer.getTotalArmorValue() / 50.0f; + } + + private static Vector3d getPlayerLookVector() { + final EntityLivingBase e = cameraEntity(); + if (e == null) return playerLookCache.set(0, 0, 0); + final float delta = CapturedRenderingState.INSTANCE.getTickDelta(); + final float yaw = e.prevRotationYaw + (e.rotationYaw - e.prevRotationYaw) * delta; + final float pitch = e.prevRotationPitch + (e.rotationPitch - e.prevRotationPitch) * delta; + return forwardVector(playerLookCache, yaw, pitch); + } + + private static Vector3d getPlayerBodyVector() { + final EntityLivingBase e = cameraEntity(); + if (e == null) return playerBodyCache.set(0, 0, 0); + return forwardVector(playerBodyCache, e.renderYawOffset, 0.0f); + } + + private static int getSeaLevel() { + // 1.7.10 has no per-world sea level accessor, best to guess 63? + return 63; + } + + private static void updateSelectedBlock() { + final int frame = SystemTimeUniforms.COUNTER.getAsInt(); + if (frame == cachedSelectedFrame) { + return; + } + cachedSelectedFrame = frame; + + final Minecraft mc = Minecraft.getMinecraft(); + final MovingObjectPosition hit = mc.objectMouseOver; + if (mc.theWorld == null || hit == null || hit.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) { + cachedSelectedBlockId = 0; + selectedBlockPosCache.set(-256.0f, -256.0f, -256.0f); + return; + } + + final Vector3dc cam = CameraUniforms.getUnshiftedCameraPosition(); + selectedBlockPosCache.set( + (float) ((hit.blockX + 0.5) - cam.x()), + (float) ((hit.blockY + 0.5) - cam.y()), + (float) ((hit.blockZ + 0.5) - cam.z())); + + final Block block = mc.theWorld.getBlock(hit.blockX, hit.blockY, hit.blockZ); + if (block == null || block.isAir(mc.theWorld, hit.blockX, hit.blockY, hit.blockZ)) { + cachedSelectedBlockId = 0; + return; + } + final Reference2ObjectMap blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches(); + if (blockMetaMatches == null) { + cachedSelectedBlockId = 0; + return; + } + final Int2IntMap metaMap = blockMetaMatches.get(block); + if (metaMap == null) { + cachedSelectedBlockId = 0; + return; + } + final int meta = mc.theWorld.getBlockMetadata(hit.blockX, hit.blockY, hit.blockZ); + cachedSelectedBlockId = Math.max(BlockMaterialMapping.resolveId(metaMap, meta), 0); + } + + private static int getCurrentSelectedBlockId() { + updateSelectedBlock(); + return cachedSelectedBlockId; + } + + private static Vector3f getCurrentSelectedBlockPos() { + updateSelectedBlock(); + return selectedBlockPosCache; + } + + private static int worldHeight() { + final WorldClient level = Minecraft.getMinecraft().theWorld; + return (level != null && level.provider != null) ? level.provider.getHeight() : 256; + } + public static class WorldInfoUniforms { public static void addWorldInfoUniforms(UniformHolder uniforms) { uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "bedrockLevel", () -> 0); @@ -148,14 +323,8 @@ public class IrisExclusiveUniforms { return 192.0; } }); - uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "heightLimit", () -> { - final WorldClient level = Minecraft.getMinecraft().theWorld; - if (level != null && level.provider != null) { - return level.provider.getHeight(); - } else { - return 256; - } - }); + uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "heightLimit", IrisExclusiveUniforms::worldHeight); + uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "logicalHeightLimit", IrisExclusiveUniforms::worldHeight); uniforms.uniform1b(UniformUpdateFrequency.PER_FRAME, "hasCeiling", () -> { final WorldClient level = Minecraft.getMinecraft().theWorld; if (level != null && level.provider != null) { diff --git a/src/main/java/net/coderbot/iris/uniforms/SystemTimeUniforms.java b/src/main/java/net/coderbot/iris/uniforms/SystemTimeUniforms.java index fcf8195a4..fa7936e42 100644 --- a/src/main/java/net/coderbot/iris/uniforms/SystemTimeUniforms.java +++ b/src/main/java/net/coderbot/iris/uniforms/SystemTimeUniforms.java @@ -3,7 +3,10 @@ package net.coderbot.iris.uniforms; import lombok.Getter; import net.coderbot.iris.gl.uniform.UniformHolder; import net.coderbot.iris.gl.uniform.UniformUpdateFrequency; +import org.joml.Vector2i; +import org.joml.Vector3i; +import java.time.LocalDateTime; import java.util.OptionalLong; import java.util.function.IntSupplier; @@ -16,19 +19,47 @@ public final class SystemTimeUniforms { public static final Timer TIMER = new Timer(); public static final FrameCounter COUNTER = new FrameCounter(); + private static LocalDateTime cachedDateTime = LocalDateTime.now(); + private static long cachedDateTimeAtMs = 0L; + private SystemTimeUniforms() { } + private static LocalDateTime dateTimeSnapshot() { + final long now = System.currentTimeMillis(); + if (now - cachedDateTimeAtMs >= 50L) { + cachedDateTime = LocalDateTime.now(); + cachedDateTimeAtMs = now; + } + return cachedDateTime; + } + /** * Makes system time uniforms available to the given program * * @param uniforms the program to make the uniforms available to */ public static void addSystemTimeUniforms(UniformHolder uniforms) { + final Vector3i date = new Vector3i(); + final Vector3i time = new Vector3i(); + final Vector2i yearTime = new Vector2i(); uniforms .uniform1i(UniformUpdateFrequency.PER_FRAME, "frameCounter", COUNTER) .uniform1f(UniformUpdateFrequency.PER_FRAME, "frameTime", TIMER::getLastFrameTime) - .uniform1f(UniformUpdateFrequency.PER_FRAME, "frameTimeCounter", TIMER::getFrameTimeCounter); + .uniform1f(UniformUpdateFrequency.PER_FRAME, "frameTimeCounter", TIMER::getFrameTimeCounter) + .uniform3i(UniformUpdateFrequency.PER_TICK, "currentDate", () -> { + final LocalDateTime dt = dateTimeSnapshot(); + return date.set(dt.getYear(), dt.getMonthValue(), dt.getDayOfMonth()); + }) + .uniform3i(UniformUpdateFrequency.PER_TICK, "currentTime", () -> { + final LocalDateTime dt = dateTimeSnapshot(); + return time.set(dt.getHour(), dt.getMinute(), dt.getSecond()); + }) + .uniform2i(UniformUpdateFrequency.PER_TICK, "currentYearTime", () -> { + final LocalDateTime dt = dateTimeSnapshot(); + final int elapsed = ((dt.getDayOfYear() - 1) * 86400) + (dt.getHour() * 3600) + (dt.getMinute() * 60) + dt.getSecond(); + return yearTime.set(elapsed, (dt.toLocalDate().lengthOfYear() * 86400) - elapsed); + }); } public static void addFloatFrameMod8Uniform(UniformHolder uniforms) { diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinItemRenderer.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinItemRenderer.java index efa1ad6d0..d6c4c88ed 100644 --- a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinItemRenderer.java +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinItemRenderer.java @@ -1,6 +1,7 @@ package com.gtnewhorizons.angelica.mixins.early.shaders; import com.gtnewhorizons.angelica.compat.mojang.InteractionHand; +import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition; import net.coderbot.iris.layer.GbufferPrograms; import net.coderbot.iris.pipeline.HandRenderer; @@ -63,6 +64,7 @@ public class MixinItemRenderer { private void iris$glintStart(CallbackInfo ci) { ItemIdManager.resetItemId(); GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.GLINT); + ShaderGlint.beginGlint(); } /** @@ -75,5 +77,6 @@ public class MixinItemRenderer { ) private void iris$glintEnd(CallbackInfo ci) { GbufferPrograms.teardownSpecialRenderCondition(); + ShaderGlint.endGlint(); } } diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRenderItem.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRenderItem.java index 9d19a9981..52009917d 100644 --- a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRenderItem.java +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRenderItem.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.mixins.early.shaders; +import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition; import net.coderbot.iris.layer.GbufferPrograms; import net.coderbot.iris.uniforms.ItemIdManager; @@ -41,6 +42,7 @@ public class MixinRenderItem { private void iris$glintStart(CallbackInfo ci) { ItemIdManager.resetItemId(); GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.GLINT); + ShaderGlint.beginGlint(); } /** @@ -53,5 +55,6 @@ public class MixinRenderItem { ) private void iris$glintEnd(CallbackInfo ci) { GbufferPrograms.teardownSpecialRenderCondition(); + ShaderGlint.endGlint(); } } diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRendererLivingEntity.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRendererLivingEntity.java index bc3d64e65..a96045c5c 100644 --- a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRendererLivingEntity.java +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinRendererLivingEntity.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.mixins.early.shaders; +import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition; @@ -80,6 +81,7 @@ public class MixinRendererLivingEntity { ) private void iris$glintStart(CallbackInfo ci) { GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.GLINT); + ShaderGlint.beginGlint(); } /** @@ -92,6 +94,7 @@ public class MixinRendererLivingEntity { ) private void iris$glintEnd(CallbackInfo ci) { GbufferPrograms.teardownSpecialRenderCondition(); + ShaderGlint.endGlint(); } } diff --git a/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinTextureManager_ReloadCount.java b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinTextureManager_ReloadCount.java new file mode 100644 index 000000000..21fcbd94f --- /dev/null +++ b/src/mixin/java/com/gtnewhorizons/angelica/mixins/early/shaders/MixinTextureManager_ReloadCount.java @@ -0,0 +1,21 @@ +package com.gtnewhorizons.angelica.mixins.early.shaders; + +import net.coderbot.iris.uniforms.CapturedRenderingState; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.resources.IResourceManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +/** + * Hook into TextureManger to increment the reload count. + */ +@Mixin(TextureManager.class) +public class MixinTextureManager_ReloadCount { + + @Inject(method = "onResourceManagerReload", at = @At("TAIL")) + private void angelica$bumpTextureReloadCount(IResourceManager resourceManager, CallbackInfo ci) { + CapturedRenderingState.INSTANCE.incrementTextureReloadCount(); + } +}