diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 8015949a3..46e602dc4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -41,8 +41,6 @@ public class Backend { } public static void refresh() { - OptifineHandler.refresh(); - engine = chooseEngine(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/OptifineHandler.java b/src/main/java/com/jozufozu/flywheel/backend/OptifineHandler.java index 4462359da..1e49c7270 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/OptifineHandler.java +++ b/src/main/java/com/jozufozu/flywheel/backend/OptifineHandler.java @@ -1,25 +1,43 @@ package com.jozufozu.flywheel.backend; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.function.BooleanSupplier; -import net.minecraft.client.Minecraft; +import javax.annotation.Nullable; + +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.culling.Frustum; public final class OptifineHandler { public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine"; public static final String SHADER_PACKAGE = "net.optifine.shaders"; private static boolean isOptifineInstalled; - private static boolean isUsingShaders; + private static BooleanSupplier shadersEnabledSupplier; private static BooleanSupplier shadowPassSupplier; + private static FrustumConstructor shadowFrustumConstructor; private OptifineHandler() { } + private static FrustumConstructor createShadowFrustumConstructor() { + try { + Class ofShaders = Class.forName("net.optifine.shaders.ShadersRender"); + Method method = ofShaders.getDeclaredMethod("makeShadowFrustum", Camera.class, Float.TYPE); + method.setAccessible(true); + return (cam, pt) -> { + try { + return (Frustum) method.invoke(null, cam, pt); + } catch (Exception ignored) { + return null; + } + }; + } catch (Exception ignored) { + return ($, $$) -> null; + } + } + private static BooleanSupplier createShadowPassSupplier() { try { Class ofShaders = Class.forName("net.optifine.shaders.Shaders"); @@ -37,28 +55,21 @@ public final class OptifineHandler { } } - private static boolean areShadersDisabledInOptifineConfigFile() { - File dir = Minecraft.getInstance().gameDirectory; - - File shaderOptions = new File(dir, "optionsshaders.txt"); - - boolean shadersOff = true; - try (BufferedReader reader = new BufferedReader(new FileReader(shaderOptions))) { - - shadersOff = reader.lines() - .anyMatch(it -> { - String line = it.replaceAll("\\s", ""); - if (line.startsWith("shaderPack=")) { - String setting = line.substring("shaderPack=".length()); - - return setting.equals("OFF") || setting.equals("(internal)"); - } - return false; - }); - } catch (IOException e) { - Backend.LOGGER.info("No shader config found."); + private static BooleanSupplier createShadersEnabledSupplier() { + try { + Class ofShaders = Class.forName("net.optifine.shaders.Shaders"); + Field field = ofShaders.getDeclaredField("shaderPackLoaded"); + field.setAccessible(true); + return () -> { + try { + return field.getBoolean(null); + } catch (IllegalAccessException ignored) { + return false; + } + }; + } catch (Exception ignored) { + return () -> false; } - return shadersOff; } public static void init() { @@ -67,21 +78,13 @@ public final class OptifineHandler { if (isOptifineInstalled) { Backend.LOGGER.info("Optifine detected."); - - refresh(); } else { Backend.LOGGER.info("Optifine not detected."); } + shadersEnabledSupplier = createShadersEnabledSupplier(); shadowPassSupplier = createShadowPassSupplier(); - } - - public static void refresh() { - if (!isOptifineInstalled) return; - - boolean shadersOff = areShadersDisabledInOptifineConfigFile(); - - isUsingShaders = !shadersOff; + shadowFrustumConstructor = createShadowFrustumConstructor(); } public static boolean isOptifineInstalled() { @@ -89,10 +92,26 @@ public final class OptifineHandler { } public static boolean isUsingShaders() { - return isUsingShaders; + return shadersEnabledSupplier.getAsBoolean(); } public static boolean isShadowPass() { return shadowPassSupplier.getAsBoolean(); } + + @Nullable + public static Frustum createShadowFrustum(Camera camera, float partialTicks) { + var frustum = shadowFrustumConstructor.create(camera, partialTicks); + if (frustum != null) { + var position = camera.getPosition(); + frustum.prepare(position.x, position.y, position.z); + } + return frustum; + } + + @FunctionalInterface + public interface FrustumConstructor { + @Nullable + Frustum create(Camera camera, float partialTicks); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 02ec9ceac..23d248b8a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -12,6 +12,7 @@ import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.jozufozu.flywheel.util.ClientLevelExtension; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; @@ -130,7 +131,8 @@ public class InstanceWorld { public void loadEntities(ClientLevel world) { // Block entities are loaded while chunks are baked. // Entities are loaded with the world, so when chunks are reloaded they need to be re-added. - world.entitiesForRendering() + ClientLevelExtension.cast(world) + .flywheel$getAllLoadedEntities() .forEach(entityInstanceManager::add); } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/CancelEntityRenderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/CancelEntityRenderMixin.java deleted file mode 100644 index ed1a41e6e..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/CancelEntityRenderMixin.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.jozufozu.flywheel.mixin; - -import java.util.ArrayList; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Group; -import org.spongepowered.asm.mixin.injection.Redirect; - -import com.google.common.collect.Lists; -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; - -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.world.entity.Entity; - -@Mixin(LevelRenderer.class) -public class CancelEntityRenderMixin { - - // TODO: Don't use redirect - @Group(name = "entityFilter", min = 1, max = 1) - @Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;entitiesForRendering()Ljava/lang/Iterable;")) - private Iterable filterEntities(ClientLevel world) { - Iterable entities = world.entitiesForRendering(); - if (Backend.isOn()) { - ArrayList filtered = Lists.newArrayList(entities); - - filtered.removeIf(InstancedRenderRegistry::shouldSkipRender); - - return filtered; - } - return entities; - } - -// @Group(name = "entityFilter") -// @Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/ClassInstanceMultiMap;iterator()Ljava/util/Iterator;")) -// private Iterator filterEntitiesOF(ClassInstanceMultiMap classInheritanceMultiMap) { -// if (Backend.getInstance() -// .canUseInstancing()) { -// -// ArrayList filtered = Lists.newArrayList(classInheritanceMultiMap); -// -// InstancedRenderRegistry r = InstancedRenderRegistry.getInstance(); -// filtered.removeIf(r::shouldSkipRender); -// -// return filtered.iterator(); -// } -// return classInheritanceMultiMap.iterator(); -// } -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ClientLevelMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/ClientLevelMixin.java new file mode 100644 index 000000000..ce1a9c161 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/ClientLevelMixin.java @@ -0,0 +1,42 @@ +package com.jozufozu.flywheel.mixin; + +import java.util.ArrayList; + +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.CallbackInfoReturnable; + +import com.google.common.collect.Lists; +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; +import com.jozufozu.flywheel.util.ClientLevelExtension; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.entity.LevelEntityGetter; + +@Mixin(ClientLevel.class) +public abstract class ClientLevelMixin implements ClientLevelExtension { + + @Shadow + protected abstract LevelEntityGetter getEntities(); + + @Override + public Iterable flywheel$getAllLoadedEntities() { + return getEntities().getAll(); + } + + @Inject(method = "entitiesForRendering", at = @At("RETURN"), cancellable = true) + private void filterEntities(CallbackInfoReturnable> cir) { + if (Backend.isOn()) { + Iterable entities = cir.getReturnValue(); + ArrayList filtered = Lists.newArrayList(entities); + + filtered.removeIf(InstancedRenderRegistry::shouldSkipRender); + + cir.setReturnValue(filtered); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/ShaderCloseMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/ShaderCloseMixin.java deleted file mode 100644 index eea2f6a0e..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/ShaderCloseMixin.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.jozufozu.flywheel.mixin; - -import javax.annotation.Nullable; - -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 com.jozufozu.flywheel.backend.OptifineHandler; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.VideoSettingsScreen; - -@Mixin(Minecraft.class) -public class ShaderCloseMixin { - - @Shadow - @Nullable - public Screen screen; - - @Inject(at = @At("HEAD"), method = "setScreen") - private void whenScreenChanges(Screen screen, CallbackInfo info) { - if (OptifineHandler.isOptifineInstalled() && screen instanceof VideoSettingsScreen) { - Screen old = this.screen; - if (old != null && old.getClass() - .getName() - .startsWith(OptifineHandler.SHADER_PACKAGE)) { - OptifineHandler.refresh(); - } - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/util/ClientLevelExtension.java b/src/main/java/com/jozufozu/flywheel/util/ClientLevelExtension.java new file mode 100644 index 000000000..c4bb29bba --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/ClientLevelExtension.java @@ -0,0 +1,22 @@ +package com.jozufozu.flywheel.util; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.Entity; + +public interface ClientLevelExtension { + + /** + * Get an iterator over all entities in this level. + * + *

+ * Normally, this would be accomplished by {@link ClientLevel#entitiesForRendering()}, but the output of that + * method is filtered of entities that are rendered by flywheel. This interface provides a workaround. + *

+ * @return An iterator over all entities in the level, including entities that are rendered by flywheel. + */ + Iterable flywheel$getAllLoadedEntities(); + + static ClientLevelExtension cast(ClientLevel level) { + return (ClientLevelExtension) level; + } +} diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 19f34505b..cccc7ef58 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -10,7 +10,7 @@ "BufferBuilderMixin", "BufferUploaderMixin", "CameraMixin", - "CancelEntityRenderMixin", + "ClientLevelMixin", "ChunkRebuildHooksMixin", "EntityTypeMixin", "FixFabulousDepthMixin", @@ -23,7 +23,6 @@ "PausedPartialTickAccessor", "RenderTexturesMixin", "RenderTypeMixin", - "ShaderCloseMixin", "atlas.AtlasDataMixin", "atlas.SheetDataAccessor", "light.LightUpdateMixin",