From 92eb11a61728d65d3af4f5c653340c867c6776cb Mon Sep 17 00:00:00 2001 From: johni0702 Date: Tue, 18 Oct 2016 21:57:26 +0200 Subject: [PATCH 1/4] Change video preview to be compatible with Optifine --- .../replaymod/render/gui/GuiVideoRenderer.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java b/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java index 7777bf6f..0f60f3b4 100644 --- a/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java +++ b/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java @@ -14,7 +14,6 @@ import de.johni0702.minecraft.gui.element.advanced.GuiProgressBar; import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import net.minecraft.client.renderer.texture.DynamicTexture; -import net.minecraft.client.renderer.texture.TextureUtil; import net.minecraft.client.resources.I18n; import net.minecraft.util.ResourceLocation; import org.lwjgl.util.Dimension; @@ -23,8 +22,6 @@ import org.lwjgl.util.ReadablePoint; import java.nio.ByteBuffer; -import static net.minecraft.client.renderer.GlStateManager.bindTexture; - public class GuiVideoRenderer extends GuiScreen { private static final ResourceLocation NO_PREVIEW_TEXTURE = new ResourceLocation("replaymod", "logo.jpg"); @@ -232,13 +229,7 @@ public class GuiVideoRenderer extends GuiScreen { final int videoHeight = videoSize.getHeight(); if (previewTexture == null) { - previewTexture = new DynamicTexture(videoWidth, videoHeight) { - @Override - public void updateDynamicTexture() { - bindTexture(getGlTextureId()); - TextureUtil.uploadTextureSub(0, getTextureData(), videoWidth, videoHeight, 0, 0, true, false, false); - } - }; + previewTexture = new DynamicTexture(videoWidth, videoHeight); } if (previewTextureDirty) { @@ -273,7 +264,10 @@ public class GuiVideoRenderer extends GuiScreen { buffer.mark(); synchronized (this) { int[] data = previewTexture.getTextureData(); - for (int i = 0; i < data.length; i++) { + // Optifine changes the texture data array to be three times as long (for use by shaders), + // we only want to initialize the first third which is why we use the length of the buffer instead + // of the length of the data array + for (int i = 0; buffer.remaining() > 0; i++) { data[i] = 0xff << 24 | (buffer.get() & 0xff) << 16 | (buffer.get() & 0xff) << 8 | (buffer.get() & 0xff); } previewTextureDirty = true; From 636ce7bdd4c91cbed2d76b18cbe509b4075fcd46 Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Sat, 5 Nov 2016 18:22:07 +0100 Subject: [PATCH 2/4] Add compatibility for GLSL Shaders --- .../mixin/MixinShaderEntityRenderer.java | 52 +++++++++++++++++++ .../com/replaymod/core/LoadingPlugin.java | 1 + .../render/capturer/ODSFrameCapturer.java | 8 ++- .../render/capturer/OpenGlFrameCapturer.java | 22 ++++++-- .../render/capturer/WorldRenderer.java | 4 +- .../render/hooks/EntityRendererHandler.java | 49 +++++------------ .../render/mixin/MixinEntityRenderer.java | 9 ---- .../mixins.compat.shaders.replaymod.json | 10 ++++ 8 files changed, 101 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java create mode 100644 src/main/resources/mixins.compat.shaders.replaymod.json diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java new file mode 100644 index 00000000..875b9703 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java @@ -0,0 +1,52 @@ +package com.replaymod.compat.shaders.mixin; + +import com.replaymod.replay.ReplayHandler; +import com.replaymod.replay.ReplayModReplay; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.EntityRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.lang.reflect.Field; + +@Mixin(EntityRenderer.class) +public abstract class MixinShaderEntityRenderer { + + @Shadow + public Minecraft mc; + + private static Field replayModCompat_frameTimeCounterField = null; + + static { + replayModCompat_initFrameTimeCounterField(); + } + + private static void replayModCompat_initFrameTimeCounterField() { + try { + replayModCompat_frameTimeCounterField = Class.forName("shadersmod.client.Shaders") + .getDeclaredField("frameTimeCounter"); + replayModCompat_frameTimeCounterField.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + @Inject(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;enableDepth()V")) + private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) { + if (replayModCompat_frameTimeCounterField != null) { + ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); + float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f; + try { + replayModCompat_frameTimeCounterField.set(null, timestamp); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/replaymod/core/LoadingPlugin.java b/src/main/java/com/replaymod/core/LoadingPlugin.java index af86c1ee..6ed89aa4 100755 --- a/src/main/java/com/replaymod/core/LoadingPlugin.java +++ b/src/main/java/com/replaymod/core/LoadingPlugin.java @@ -19,6 +19,7 @@ public class LoadingPlugin implements IFMLLoadingPlugin { MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.recording.replaymod.json"); MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.render.replaymod.json"); MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.replay.replaymod.json"); + MixinEnvironment.getDefaultEnvironment().addConfiguration("mixins.compat.shaders.replaymod.json"); CodeSource codeSource = getClass().getProtectionDomain().getCodeSource(); if (codeSource != null) { diff --git a/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java b/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java index 33c9ae7a..01a480f0 100644 --- a/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java +++ b/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java @@ -6,11 +6,11 @@ import com.replaymod.render.frame.ODSOpenGlFrame; import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.rendering.FrameCapturer; import com.replaymod.render.shader.Program; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.crash.CrashReport; import net.minecraft.util.ReportedException; import net.minecraft.util.ResourceLocation; -import org.lwjgl.util.Dimension; import org.lwjgl.util.ReadableDimension; import java.io.IOException; @@ -31,6 +31,8 @@ public class ODSFrameCapturer implements FrameCapturer { private final BooleanState[] previousStates = new BooleanState[3]; private final BooleanState previousFogState; + private final Minecraft mc = Minecraft.getMinecraft(); + public ODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) { RenderInfo fakeInfo = new RenderInfo() { private int call; @@ -143,6 +145,8 @@ public class ODSFrameCapturer implements FrameCapturer { @Override protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) { + resize(getFrameWidth(), getFrameHeight()); + pushMatrix(); frameBuffer().bindFramebuffer(true); @@ -150,7 +154,7 @@ public class ODSFrameCapturer implements FrameCapturer { enableTexture2D(); directionVariable.set(captureData.ordinal()); - worldRenderer.renderWorld(new Dimension(getFrameWidth(), getFrameHeight()), partialTicks, null); + worldRenderer.renderWorld(partialTicks, null); frameBuffer().unbindFramebuffer(); popMatrix(); diff --git a/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java b/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java index 4513f2ae..d0968582 100644 --- a/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java +++ b/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java @@ -4,6 +4,7 @@ import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.rendering.Frame; import com.replaymod.render.rendering.FrameCapturer; import com.replaymod.render.utils.ByteBufferPool; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.shader.Framebuffer; import org.lwjgl.opengl.GL11; @@ -24,6 +25,8 @@ public abstract class OpenGlFrameCapturer Date: Sat, 5 Nov 2016 18:22:41 +0100 Subject: [PATCH 3/4] Draw the GuiVideoRenderer on a separate framebuffer that has the actual size of the window instead of MC's framebuffer which may have a different size --- .../render/rendering/VideoRenderer.java | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java index 05a5a3d2..640eb8c0 100644 --- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java +++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java @@ -20,6 +20,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.audio.SoundCategory; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.shader.Framebuffer; import net.minecraft.util.Timer; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; @@ -62,6 +63,9 @@ public class VideoRenderer implements RenderInfo { private boolean paused; private boolean cancelled; + private Framebuffer guiFramebuffer; + private int displayWidth, displayHeight; + public VideoRenderer(RenderSettings settings, ReplayHandler replayHandler, Timeline timeline) throws IOException { this.settings = settings; this.replayHandler = replayHandler; @@ -130,6 +134,13 @@ public class VideoRenderer implements RenderInfo { @Override public float updateForNextFrame() { + // because the jGui lib uses Minecraft's displayWidth and displayHeight values, update these temporarily + int displayWidthBefore = mc.displayWidth; + int displayHeightBefore = mc.displayHeight; + + mc.displayWidth = displayWidth; + mc.displayHeight = displayHeight; + if (!settings.isHighPerformance() || framesDone % fps == 0) { drawGui(); } @@ -142,6 +153,10 @@ public class VideoRenderer implements RenderInfo { tick(); } + // change Minecraft's display size back + mc.displayWidth = displayWidthBefore; + mc.displayHeight = displayHeightBefore; + framesDone++; return mc.timer.renderPartialTicks; } @@ -192,10 +207,15 @@ public class VideoRenderer implements RenderInfo { totalFrames = (int) (duration*fps/1000); - ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + updateDisplaySize(); + + ScaledResolution scaled = new ScaledResolution(mc, displayWidth, displayHeight); gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.renderGlobal); + + // Set up our own framebuffer to render the GUI to + guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true); } private void finish() { @@ -219,6 +239,9 @@ public class VideoRenderer implements RenderInfo { ReplayMod.soundHandler.playRenderSuccessSound(); new GuiRenderingDone(ReplayModRender.instance, videoWriter.getVideoFile(), totalFrames, settings).display(); + + // Finally, resize the Minecraft framebuffer to the actual width/height of the window + mc.resize(displayWidth, displayHeight); } private void tick() { @@ -238,12 +261,22 @@ public class VideoRenderer implements RenderInfo { public void drawGui() { do { + // Resize the GUI framebuffer if the display size changed + if (!settings.isHighPerformance() && displaySizeChanged()) { + updateDisplaySize(); + guiFramebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight); + } + pushMatrix(); clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); enableTexture2D(); - mc.getFramebuffer().bindFramebuffer(true); + guiFramebuffer.bindFramebuffer(true); + mc.entityRenderer.setupOverlayRendering(); + ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); + try { gui.toMinecraft().handleInput(); } catch (IOException e) { @@ -252,18 +285,17 @@ public class VideoRenderer implements RenderInfo { throw new RuntimeException(e); } - ScaledResolution scaled = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth; int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1; + gui.toMinecraft().drawScreen(mouseX, mouseY, 0); - mc.getFramebuffer().unbindFramebuffer(); + guiFramebuffer.unbindFramebuffer(); popMatrix(); pushMatrix(); - mc.getFramebuffer().framebufferRender(mc.displayWidth, mc.displayHeight); + guiFramebuffer.framebufferRender(displayWidth, displayHeight); popMatrix(); - // if not in high performance mode, update the gui size if screen size changed // otherwise just swap the progress gui to screen if (settings.isHighPerformance()) { @@ -285,6 +317,15 @@ public class VideoRenderer implements RenderInfo { } while (paused); } + private boolean displaySizeChanged() { + return displayWidth != Display.getWidth() || displayHeight != Display.getHeight(); + } + + private void updateDisplaySize() { + displayWidth = Display.getWidth(); + displayHeight = Display.getHeight(); + } + public int getFramesDone() { return framesDone; } From e5eb982b984bf7f7471fee00db63899a6c21dc43 Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Wed, 9 Nov 2016 04:28:56 +0100 Subject: [PATCH 4/4] Add Optifine compatibility to rendering by disabling their separate chunk loading queue Add compatibility to shaders that utilize the Shadow Map by manually setting displayListEntitiesDirty to false Call Shaders.beginRender in an Event Handler to avoid calling all of EntityRenderer#renderWorld --- .../com/replaymod/compat/ReplayModCompat.java | 23 ++++ .../compat/shaders/ShaderBeginRender.java | 39 +++++++ .../compat/shaders/ShaderReflection.java | 102 ++++++++++++++++++ .../mixin/MixinShaderEntityRenderer.java | 42 ++------ .../shaders/mixin/MixinShaderRenderChunk.java | 28 +++++ .../mixin/MixinShaderRenderGlobal.java | 40 +++++++ .../render/hooks/EntityRendererHandler.java | 16 +-- .../render/mixin/MixinRenderGlobal.java | 2 + src/main/resources/mcmod.info | 19 +++- .../mixins.compat.shaders.replaymod.json | 6 +- 10 files changed, 270 insertions(+), 47 deletions(-) create mode 100644 src/main/java/com/replaymod/compat/ReplayModCompat.java create mode 100644 src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java create mode 100644 src/main/java/com/replaymod/compat/shaders/ShaderReflection.java create mode 100644 src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java create mode 100644 src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java diff --git a/src/main/java/com/replaymod/compat/ReplayModCompat.java b/src/main/java/com/replaymod/compat/ReplayModCompat.java new file mode 100644 index 00000000..0587764f --- /dev/null +++ b/src/main/java/com/replaymod/compat/ReplayModCompat.java @@ -0,0 +1,23 @@ +package com.replaymod.compat; + +import com.replaymod.compat.shaders.ShaderBeginRender; +import com.replaymod.core.ReplayMod; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.EventBus; + +@Mod(modid = ReplayModCompat.MOD_ID, useMetadata = true) +public class ReplayModCompat { + public static final String MOD_ID = "replaymod-compat"; + + @Mod.Instance(ReplayMod.MOD_ID) + private static ReplayMod core; + + @Mod.EventHandler + public void init(FMLInitializationEvent event) { + EventBus bus = FMLCommonHandler.instance().bus(); + bus.register(new ShaderBeginRender()); + } + +} diff --git a/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java b/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java new file mode 100644 index 00000000..8404801b --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java @@ -0,0 +1,39 @@ +package com.replaymod.compat.shaders; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +import java.lang.reflect.InvocationTargetException; + +public class ShaderBeginRender { + + private final Minecraft mc = Minecraft.getMinecraft(); + + /** + * Invokes Shaders#beginRender when rendering a video, + * as this would usually get called by EntityRenderer#renderWorld, + * which we're not calling during rendering. + */ + @SubscribeEvent + public void onRenderTickStart(TickEvent.RenderTickEvent event) { + if (event.phase != TickEvent.Phase.START) return; + if (ShaderReflection.shaders_beginRender == null) return; + if (ShaderReflection.config_isShaders == null) return; + + try { + // check if video is being rendered + if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) + return; + + // check if Shaders are enabled + if (!(boolean) (ShaderReflection.config_isShaders.invoke(null))) return; + + ShaderReflection.shaders_beginRender.invoke(null, mc, mc.timer.elapsedPartialTicks, 0); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/replaymod/compat/shaders/ShaderReflection.java b/src/main/java/com/replaymod/compat/shaders/ShaderReflection.java new file mode 100644 index 00000000..55d785b2 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/ShaderReflection.java @@ -0,0 +1,102 @@ +package com.replaymod.compat.shaders; + +import net.minecraft.client.Minecraft; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ShaderReflection { + + // Shaders.frameTimeCounter + public static Field shaders_frameTimeCounter; + + // Shaders.isShadowPass + public static Field shaders_isShadowPass; + + // Shaders.beginRender() + public static Method shaders_beginRender; + + // RenderGlobal.chunksToUpdateForced (Optifine only) + public static Field renderGlobal_chunksToUpdateForced; + + // Config.isShaders() (Optifine only) + public static Method config_isShaders; + + static { + initFrameTimeCounter(); + + initIsShadowPass(); + + initBeginRender(); + + initChunksToUpdateForced(); + + initConfigIsShaders(); + } + + private static void initFrameTimeCounter() { + try { + shaders_frameTimeCounter = Class.forName("shadersmod.client.Shaders") + .getDeclaredField("frameTimeCounter"); + shaders_frameTimeCounter.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initIsShadowPass() { + try { + shaders_isShadowPass = Class.forName("shadersmod.client.Shaders") + .getDeclaredField("isShadowPass"); + shaders_isShadowPass.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initBeginRender() { + try { + shaders_beginRender = Class.forName("shadersmod.client.Shaders") + .getDeclaredMethod("beginRender", Minecraft.class, float.class, long.class); + shaders_frameTimeCounter.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchMethodException e) { + // the method wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initChunksToUpdateForced() { + try { + renderGlobal_chunksToUpdateForced = Class.forName("net.minecraft.client.renderer.RenderGlobal") + .getDeclaredField("chunksToUpdateForced"); + renderGlobal_chunksToUpdateForced.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchFieldException e) { + // the field wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + + private static void initConfigIsShaders() { + try { + config_isShaders = Class.forName("Config") + .getDeclaredMethod("isShaders"); + config_isShaders.setAccessible(true); + } catch (ClassNotFoundException ignore) { + // no shaders mod installed + } catch (NoSuchMethodException e) { + // the method wasn't found. Has it been renamed? + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java index 875b9703..358ff118 100644 --- a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderEntityRenderer.java @@ -1,52 +1,28 @@ package com.replaymod.compat.shaders.mixin; +import com.replaymod.compat.shaders.ShaderReflection; import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayModReplay; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.EntityRenderer; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.lang.reflect.Field; - @Mixin(EntityRenderer.class) public abstract class MixinShaderEntityRenderer { - @Shadow - public Minecraft mc; + @Inject(method = "renderWorldPass", at = @At("HEAD")) + private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) { + if (ReplayModReplay.instance.getReplayHandler() == null) return; + if (ShaderReflection.shaders_frameTimeCounter == null) return; - private static Field replayModCompat_frameTimeCounterField = null; - - static { - replayModCompat_initFrameTimeCounterField(); - } - - private static void replayModCompat_initFrameTimeCounterField() { + ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); + float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f; try { - replayModCompat_frameTimeCounterField = Class.forName("shadersmod.client.Shaders") - .getDeclaredField("frameTimeCounter"); - replayModCompat_frameTimeCounterField.setAccessible(true); - } catch (ClassNotFoundException ignore) { - // no shaders mod installed - } catch (NoSuchFieldException e) { - // the field wasn't found. Has it been renamed? + ShaderReflection.shaders_frameTimeCounter.set(null, timestamp); + } catch (Exception e) { e.printStackTrace(); } } - - @Inject(method = "renderWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;enableDepth()V")) - private void replayModCompat_updateShaderFrameTimeCounter(CallbackInfo ignore) { - if (replayModCompat_frameTimeCounterField != null) { - ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); - float timestamp = replayHandler.getReplaySender().currentTimeStamp() / 1000f % 3600f; - try { - replayModCompat_frameTimeCounterField.set(null, timestamp); - } catch (Exception e) { - e.printStackTrace(); - } - } - } } diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java new file mode 100644 index 00000000..3b4e9bc2 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderChunk.java @@ -0,0 +1,28 @@ +package com.replaymod.compat.shaders.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.chunk.RenderChunk; +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.CallbackInfoReturnable; + +@Mixin(RenderChunk.class) +public abstract class MixinShaderRenderChunk { + + private final Minecraft mc = Minecraft.getMinecraft(); + + /** + * Changes the RenderChunk#isPlayerUpdate method that Optifine adds + * to always return true while rendering so no chunks are being added + * to a separate rendering queue + */ + @Inject(method = "isPlayerUpdate", at = @At("HEAD"), cancellable = true) + private void replayModCompat_disableIsPlayerUpdate(CallbackInfoReturnable ci) { + if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return; + ci.setReturnValue(true); + } + + +} diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java new file mode 100644 index 00000000..be69cf00 --- /dev/null +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaderRenderGlobal.java @@ -0,0 +1,40 @@ +package com.replaymod.compat.shaders.mixin; + +import com.replaymod.compat.shaders.ShaderReflection; +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.RenderGlobal; +import net.minecraft.client.renderer.culling.ICamera; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(RenderGlobal.class) +public abstract class MixinShaderRenderGlobal { + + private final Minecraft mc = Minecraft.getMinecraft(); + + @Shadow + public boolean displayListEntitiesDirty; + + @Inject(method = "setupTerrain", at = @At("HEAD"), cancellable = true) + public void replayModCompat_setupTerrain(Entity viewEntity, double partialTicks, ICamera camera, + int frameCount, boolean playerSpectator, CallbackInfo ci) { + if (((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler() == null) return; + if (ShaderReflection.shaders_isShadowPass == null) return; + + // when called by the shadow pass, displayListEntitiesDirty can't be set to false, as no chunk updates + // are being processed. As it's being set to true by ChunkLoadingRenderGlobal#updateChunks, we have to + // set it to false manually to exit the loop imposed by MixinRenderGlobal#replayModRender_setupTerrain. + try { + if ((boolean) ShaderReflection.shaders_isShadowPass.get(null) == true) { + displayListEntitiesDirty = false; + } + } catch (IllegalAccessException ignore) {} + + } + +} diff --git a/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java b/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java index c38ba636..f27ada50 100644 --- a/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java +++ b/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java @@ -6,7 +6,6 @@ import com.replaymod.render.capturer.WorldRenderer; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; -import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.common.FMLCommonHandler; import java.io.IOException; @@ -36,18 +35,13 @@ public class EntityRendererHandler implements WorldRenderer { public void renderWorld(float partialTicks, long finishTimeNano) { FMLCommonHandler.instance().onRenderTickStart(partialTicks); - // the Shaders Mod does an initializing call in the EntityRenderer#renderWorld method - if (FMLClientHandler.instance().hasOptifine()) { - mc.entityRenderer.renderWorld(partialTicks, finishTimeNano); - } else { - mc.entityRenderer.updateLightmap(partialTicks); + mc.entityRenderer.updateLightmap(partialTicks); - GlStateManager.enableDepth(); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(516, 0.5F); + GlStateManager.enableDepth(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(516, 0.5F); - mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano); - } + mc.entityRenderer.renderWorldPass(2, partialTicks, finishTimeNano); FMLCommonHandler.instance().onRenderTickEnd(partialTicks); } diff --git a/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java b/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java index 34ee45e0..7f5cae0a 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java +++ b/src/main/java/com/replaymod/render/mixin/MixinRenderGlobal.java @@ -39,6 +39,8 @@ public abstract class MixinRenderGlobal { setupTerrain(viewEntity, partialTicks, camera, replayModRender_hook.nextFrameId(), playerSpectator); } while (displayListEntitiesDirty); + displayListEntitiesDirty = true; + replayModRender_passThroughSetupTerrain = false; ci.cancel(); } diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index bf81b322..f6ac199a 100755 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -116,5 +116,22 @@ "parent": "replaymod", "screenshots": [], "dependencies": [] - } + }, + { + "modid": "replaymod-compat", + "name": "Replay Mod - Compatibility", + "description": "Compatibility Module of the ReplayMod - Adds compatibility with other mods", + "version": "${version}", + "mcversion": "${mcversion}", + "url": "https://replaymod.com", + "updateUrl": "https://replaymod.com/download", + "authorList": [ + "CrushedPixel", + "johni0702" + ], + "logoFile": "replaymod_logo.png", + "parent": "replaymod", + "screenshots": [], + "dependencies": [] + } ] diff --git a/src/main/resources/mixins.compat.shaders.replaymod.json b/src/main/resources/mixins.compat.shaders.replaymod.json index 656c092b..eea17922 100644 --- a/src/main/resources/mixins.compat.shaders.replaymod.json +++ b/src/main/resources/mixins.compat.shaders.replaymod.json @@ -1,10 +1,12 @@ { - "required": true, + "required": false, "package": "com.replaymod.compat.shaders.mixin", "mixins": [], "server": [], "client": [ - "MixinShaderEntityRenderer" + "MixinShaderEntityRenderer", + "MixinShaderRenderChunk", + "MixinShaderRenderGlobal" ], "refmap": "mixins.replaymod.refmap.json" } \ No newline at end of file