mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-12 00:17:54 +00:00
+4








992644c20f
* ThreadedTessellator -> RenderBlocks instance Tessellator * Plus a CME workaround until I can figure out why it's happening * One inject with multiple methods instead of two injects * Add AF's Tessellator#reset mixin Fixes screen flashing when chunks are being built * Thread Saftey * Make each tesselator use its own ByteBuffer * Don't make RecyclingList a static variable 🤦 * Add some additions via ITessellatorInstance * Stop using builtin updateRenderers * Remove a few unused things in MixinTessellator * Remove useless casting due to implementing the interface * Overwrite some more rendering methods to ensure they're not used * Implement IBlockAccess on WorldSlice * Currently just proxies to the World... need to get it to use the local cache * Make WorldSlice use its copied values instead of using the world * block, meta, and light. Still need TODO biome * Finish making WorldSlice thread safe * Remove last unsafe this.world accesses * Copy biomeData * Implement getTileEntity(), and getBiomeGenForCoords() * Remove unused 1.16+ classes - Biome, Colorizer, and ColorBlender related * Cache worldHeight instead of asking the provider every time * Debug Screen updates * Disable Culling for now (until it works) * Tessellator Updates * Use one instance on the main thread, and individual instances on every other thread * Prevent draw() and buffer.reset() on non singleton instance * Misc cleanup, maybe better growBuffer? * Fixing splash screen and GUI issues by excluding SplashProgress from GLStateManager transformer * Update docs * Fixing a one line in SodiumWorldRenderer that allows the game setting for the distance at which entities will render. Hope it's not too bad mitch :) * TileEntites now render * Less silly transparency * Removing clamp implementation because I missed the one in mathhelper * Initial sodium entity smooth lighting * Fix entity smooth lighting * Fix lerp uses and use render layers properly (#17) * Remove some uses of blockstate + patch FluidState * Fix lerp * Enable Sodium fluid rendering * Update FluidRenderer.java * Rewind buffers when copying * Fix multidraw * Force cutout on all non-translucent blocks * Let vanilla handle render pass state * Rewrite render layer check to use Forge 1.7 hook * Fix skylight handling * Fix flickering * Clone TEs correctly * Fixing entityDistMulti (I think) * Fix TEs rendering on the wrong pass * Buff max render distance to 32 Pretty useless since the integrated server stalls instantly, but our renderer supports it * Fix fog * Fix render distance changes being ignored * Implement entity culling * Block face culling * Re-enable occlusion culling * Fix x/z coordinate mixup when indexing biome arrays Partially fixes wrong biome colors, but at chunk edges it's still wrong because neighboring chunks aren't accessed * Make a copy of biome data The old code seemed to work fine but this should be safer * Make CompatMemoryUtil#memReallocDirect preserve buffer position LWJGL3's MemoryUtil#memRealloc does this, see its javadoc. Fixes screen flashing and this error getting printed when indirect buffer grows: GL_INVALID_OPERATION error generated. Bound draw indirect buffer is not large enough. * Use biome data of neighboring chunk sections Fixes wrong colors at chunk edges * Re-enable fog culling * Tile entity culling * Remove some unneeded Sodium code * Massive code cleanup * Avoids errors when usin lwjglxdebug * Bump hodgepodge dep * Add AngelicaConfig * Add Mixin to disable MC's builtin checkGLError * Add configs for iris/sodium * Iris work * Move some StateNotifiers to GLStateManager * Remove some redundant mixins based on sodium/angelica work and rendering state captures * Add Iris DebugScreenHandler * rm archaicfix's occlusion renderer and threaded updates * Implemented Sodium's GUI and a couple of setting fixes (#20) * Ported Sodium's GUI * Correct AO * Fixed Vignette * Re-enable DirectMemoryAccess * Adds some code from lwjgl3 in CompatMemoryUtil.java * Re-add *Unsafe classes * Remove MixinWorldRenderer * Add NEID (mixin pr) support * Only shadow runtime module of antlr4 Reduces jar size by 11 mb * Update NotFine * Make LongHashMap actually perform like a hash map * Options GUI Fixes and some option implementations (#22) * Correct useNoErrorGLContext option * Added translucency sorting option * Implemented Particle Culling gives real fps boost, however, it's not as big as i thought * Begin implementing animateOnlyVisibleTextures * Correct Donation page opening * Fixed Shift + P Now it uses NotFine's GUI, but it think that NotFine's GUI should be rewrited for using other config options * Correct URL's for Linux * second code cleanup in particle culling * Fix incorrect TE map population * Add optional assertMainThread behind `-Dangelica.assertMainThread=true` * Add back air optimization, but only apply to vanilla air * Implement more options (#23) * Combine free regions in GlBufferArena * Clear global block entity list when initializing renderer * AF, CompileStubs, and SplashProgress Remove most of ArchaicFix * Move a few mixins over to angelica * Disable if org.lwjgl.util.Debug is set * Only enable Iris/lwjgl debug callbacks if org.lwjgl.util.Debug is set * Rewrite Tessellator hacks - now using a ThreadLocal instead of only patching RenderBlocks * Reenable AF mixins, switch to cached debug var, disable incompatible mixins * Remove AF deps and a late mixin that was missed * Add Iris Sodium Compat impl * Fix Sodium GLProgram to use ResourceLocation instead of Identifier * Start applying Iris -> Sodium Mixins * Make NotFine menu open if holding shift * Fixed Tessellators in production env * Finish Sodium Fluid Renderer (#24) * Init and pass Sodium LightPipelines * Remove registry lookup and refactor WorldUtil * Hook up lighting pipeline * Patch fluid velocity * It's alive! Sodium Fluid Renderer is alive! * Fix face culling not taking third person camera offset into account * rewrite ACTEntityRenderer to stop transforming ALL the classes * use ClassConstantPoolParser in TessellatorTransformer to speed up search for Tessellator references * use ClassConstantPoolParser in GLStateManagerTransformer to speed up search for class references * rewrite GLStateManagerTransformer and TessellatorTransformer * merge GLStateManagerTransfor and TessellatorTransformer into one Transformer * Fixed smooth lighting rendering * Iris block_id Mixins & BlockStateIdMap initial impl for 1.7.10 * Fix running with lwjgl debug flag & Cleanup old ShadersMod transformers * Initialize GLStateManager via Mixins instead of all in the static block * Clean up chunk load/unload tracking, fix chunks rendering without neighbors sometimes * Fix occlusion culler not re-running after important uploads Backport of https://github.com/CaffeineMC/sodium-fabric/pull/755 * Add chunks to queues without going through occlusion culler Partially reverts the previous chunk tracking commit, because that logic will reintroduce https://github.com/CaffeineMC/sodium-fabric/issues/605 As long as we only rebuild with neighbors present, we shouldn't see the visual seams * Extremely primitive synchronization approach for ISBRH * Use factory for Spdium menu createElement outputs This will allow NotFine to use Sodium menu elements and vice versa in a future commit. * Splash Screen Compat * Separate remaining NamedState enums * NotFine but it loads Sodium pages * Hook up NotFine optimizations * Hook up some NotFine features * copy over hud caching code from hodgepodge * backport glstatemanager mixin * Track thread that currently owns the GL Context in addition to the main thread. * First pass over FontRenderer, 1300->1400 FPS on my GPU when showing F3 Done: - A batching vertex buffer builder for font output - Reduction in drawcalls from 1-3 per character to 1 per font atlas switch - Mixin to draw the entire F3 screen in one batch, instead of separate batches per line TODO: - Switch drawing to a VBO - Cache string meshes across frames - Cache string width calculations used for layouts - Multidraw to reduce draw calls even further * Add JVM flag to bypass GL state caching * Push/Pop Attrib Hacks -- to be evolved later * Fix FontRenderer+SplashScreen combo * HUDCaching tweaks * Add license * Jabba Compat, and reset tesselator state even if the chunk render thread blows up * Initial Xaero's World Map + Minimap Support * Disable Fastcraft & Optifine if present * Iris Progress * Wire up more Iris mixins, informing pipeline stages. Still not rendering anything visible. * Don't try to transform the server * Avoid running biome generator on client in singleplayer * Call world biome retrieval methods instead of copying array directly (#25) * Shaders now do something! * Shadow Pass work * First shadow pass renders; however it moves all around when moving the camera... * Second shadow pass not running because it makes everything very very dark instead of just rendering translucents * Actually use the ShadowRenderer Model View when running the shadow pass... * Enable Sodium SmoothLightPipeline & Iris useSeparateAo * Avoid repeatedly copying biome data on each Y level (#27) * Celestial Rotation fixes * Water progress * Uniforms audit * Lightmap workaround * PBR compiling, stuck on Normal and Specular textures being added in * Shaderpack selection "works"; ShaderOptions TODO * Hud caching angelica (#28) * Potential hud-caching fix * Shader Options almost working ** Clicking on everything doesn't yet work, and the visuals are slightly off * Entity shadows seem to be working * Move glint fix to Mixin * Rework mouse handling for ShaderPackConfig * Stop centering shader options vertically * Angelica pbr (#31) * Only use Sodium light pipeline if useSeparateAo is on Sodium pipeline is still very buggy, so isolate it to Iris-only for now * Fix region sorting for translucency * Open Shaderpack now works on Linux * RM Shadersmod * Initial iris scrollbar clicking * Punt unsupported ISBRHs to the main thread * Delete the ISBRH lock, shouldn't be needed anymore * Render GT blocks on main thread * Require CCC 1.2.0+ * Depend on CCC 1.2 * Fix TESRs not being tracked for blocks rendered on main thread * Cut down number of runtime dependencies, enable CoreTweaks in dev by default * Add modern F3 background * Pull over some hodgepodge mixins --------- Co-authored-by: makamys <makamys@outlook.com> Co-authored-by: embeddedt <42941056+embeddedt@users.noreply.github.com> Co-authored-by: rjnasers <rjnasers@iastate.edu> Co-authored-by: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Co-authored-by: Ryan Nasers <ryannasersgig@gmail.com> Co-authored-by: Rad <radoslaw.ryszka006@gmail.com> Co-authored-by: Bogdan <64613894+Asek3@users.noreply.github.com> Co-authored-by: Caedis <Caedis@users.noreply.github.com> Co-authored-by: jss2a98aj <jss2a98aj@gmail.com> Co-authored-by: Alexdoru <57050655+Alexdoru@users.noreply.github.com> Co-authored-by: Raven Szewczyk <git@eigenraven.me> Co-authored-by: mist475 <70655895+mist475@users.noreply.github.com> Co-authored-by: Ryan Nasers <42074409+Cardinalstars@users.noreply.github.com>
684 lines
26 KiB
Java
684 lines
26 KiB
Java
package net.coderbot.iris;
|
|
|
|
import com.google.common.base.Throwables;
|
|
import com.gtnewhorizons.angelica.AngelicaMod;
|
|
import com.gtnewhorizons.angelica.Tags;
|
|
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
|
import com.mitchej123.hodgepodge.Common;
|
|
import com.mitchej123.hodgepodge.client.HodgepodgeClient;
|
|
import com.mojang.realmsclient.gui.ChatFormatting;
|
|
import cpw.mods.fml.client.registry.ClientRegistry;
|
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
|
import cpw.mods.fml.common.gameevent.InputEvent;
|
|
import lombok.Getter;
|
|
import net.coderbot.iris.config.IrisConfig;
|
|
import net.coderbot.iris.gl.GLDebug;
|
|
import net.coderbot.iris.gl.shader.StandardMacros;
|
|
import net.coderbot.iris.gui.screen.ShaderPackScreen;
|
|
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
|
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
|
|
import net.coderbot.iris.pipeline.PipelineManager;
|
|
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
|
import net.coderbot.iris.shaderpack.DimensionId;
|
|
import net.coderbot.iris.shaderpack.OptionalBoolean;
|
|
import net.coderbot.iris.shaderpack.ProgramSet;
|
|
import net.coderbot.iris.shaderpack.ShaderPack;
|
|
import net.coderbot.iris.shaderpack.discovery.ShaderpackDirectoryManager;
|
|
import net.coderbot.iris.shaderpack.option.OptionSet;
|
|
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.minecraft.client.Minecraft;
|
|
import net.minecraft.client.gui.GuiScreen;
|
|
import net.minecraft.client.multiplayer.WorldClient;
|
|
import net.minecraft.client.resources.I18n;
|
|
import net.minecraft.client.settings.KeyBinding;
|
|
import net.minecraft.launchwrapper.Launch;
|
|
import net.minecraft.util.ChatComponentText;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.lwjgl.input.Keyboard;
|
|
import org.lwjgl.opengl.ContextCapabilities;
|
|
import org.lwjgl.opengl.GLContext;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.nio.file.FileSystem;
|
|
import java.nio.file.FileSystemNotFoundException;
|
|
import java.nio.file.FileSystems;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.InvalidPathException;
|
|
import java.nio.file.NoSuchFileException;
|
|
import java.nio.file.Path;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Properties;
|
|
import java.util.stream.Stream;
|
|
import java.util.zip.ZipError;
|
|
import java.util.zip.ZipException;
|
|
|
|
public class Iris {
|
|
public final boolean isDevelopmentEnvironment;
|
|
public static ContextCapabilities capabilities;
|
|
|
|
/**
|
|
* The user-facing name of the mod. Moved into a constant to facilitate easy branding changes (for forks). You'll still need to change this separately in
|
|
* mixin plugin classes & the language files.
|
|
*/
|
|
public static final String MODNAME = "AngelicaShaders";
|
|
|
|
public static final IrisLogging logger = new IrisLogging(MODNAME);
|
|
|
|
private static Path shaderpacksDirectory;
|
|
private static ShaderpackDirectoryManager shaderpacksDirectoryManager;
|
|
|
|
private static ShaderPack currentPack;
|
|
@Getter
|
|
private static String currentPackName;
|
|
@Getter
|
|
private static boolean initialized;
|
|
|
|
private static PipelineManager pipelineManager;
|
|
@Getter
|
|
private static IrisConfig irisConfig;
|
|
private static FileSystem zipFileSystem;
|
|
|
|
@Getter
|
|
private static final Map<String, String> shaderPackOptionQueue = new HashMap<>();
|
|
// Flag variable used when reloading
|
|
// Used in favor of queueDefaultShaderPackOptionValues() for resetting as the
|
|
// behavior is more concrete and therefore is more likely to repair a user's issues
|
|
private static boolean resetShaderPackOptions = false;
|
|
|
|
private static String IRIS_VERSION;
|
|
@Getter
|
|
private static boolean fallback;
|
|
|
|
private static KeyBinding reloadKeybind;
|
|
private static KeyBinding toggleShadersKeybind;
|
|
private static KeyBinding shaderpackScreenKeybind;
|
|
|
|
public static Iris INSTANCE = new Iris();
|
|
|
|
private Iris() {
|
|
isDevelopmentEnvironment = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public void onKeypress(InputEvent.KeyInputEvent event) {
|
|
if (reloadKeybind.isPressed()) {
|
|
final Minecraft mc = Minecraft.getMinecraft();
|
|
try {
|
|
reload();
|
|
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText("Shaders Reloaded!"));
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Error while reloading Shaders for Iris!", e);
|
|
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo reload shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
|
|
}
|
|
} else if (toggleShadersKeybind.isPressed()) {
|
|
final Minecraft mc = Minecraft.getMinecraft();
|
|
try {
|
|
toggleShaders(mc, !irisConfig.areShadersEnabled());
|
|
} catch (Exception e) {
|
|
logger.error("Error while toggling shaders!", e);
|
|
|
|
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo toggle shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
|
|
setShadersDisabled();
|
|
fallback = true;
|
|
}
|
|
} else if (shaderpackScreenKeybind.isPressed()) {
|
|
final Minecraft mc = Minecraft.getMinecraft();
|
|
mc.displayGuiScreen(new ShaderPackScreen(null));
|
|
}
|
|
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public void keyUp(InputEvent.KeyInputEvent event) {
|
|
final int key = Keyboard.getEventKey();
|
|
final boolean released = !Keyboard.getEventKeyState();
|
|
if (Minecraft.getMinecraft().gameSettings.showDebugInfo && GuiScreen.isShiftKeyDown() && GuiScreen.isCtrlKeyDown() && released) {
|
|
if (key == Keyboard.KEY_N) {
|
|
AngelicaMod.animationsMode.next();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called very early on in Minecraft initialization. At this point we *cannot* safely access OpenGL, but we can do some very basic setup, config loading,
|
|
* and environment checks.
|
|
*
|
|
* <p>This is roughly equivalent to Fabric Loader's ClientModInitializer#onInitializeClient entrypoint, except
|
|
* it's entirely cross platform & we get to decide its exact semantics.</p>
|
|
*
|
|
* <p>This is called right before options are loaded, so we can add key bindings here.</p>
|
|
*/
|
|
public void onEarlyInitialize() {
|
|
try {
|
|
if (!Files.exists(getShaderpacksDirectory())) {
|
|
Files.createDirectories(getShaderpacksDirectory());
|
|
}
|
|
} catch (IOException e) {
|
|
logger.warn("Failed to create the shaderpacks directory!");
|
|
logger.warn("", e);
|
|
}
|
|
|
|
irisConfig = new IrisConfig(Minecraft.getMinecraft().mcDataDir.toPath().resolve("config").resolve("shaders.properties"));
|
|
|
|
try {
|
|
irisConfig.initialize();
|
|
} catch (IOException e) {
|
|
logger.error("Failed to initialize Angelica configuration, default values will be used instead");
|
|
logger.error("", e);
|
|
}
|
|
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
public static void identifyCapabilities() {
|
|
capabilities = GLContext.getCapabilities();
|
|
}
|
|
/**
|
|
* Called once RenderSystem#initRenderer has completed. This means that we can safely access OpenGL.
|
|
*/
|
|
public static void onRenderSystemInit() {
|
|
if (!initialized) {
|
|
Iris.logger.warn("Iris::onRenderSystemInit was called, but Iris::onEarlyInitialize was not called."
|
|
+ " Trying to avoid a crash but this is an odd state.");
|
|
return;
|
|
}
|
|
|
|
setDebug(irisConfig.areDebugOptionsEnabled());
|
|
|
|
PBRTextureManager.INSTANCE.init();
|
|
|
|
// Only load the shader pack when we can access OpenGL
|
|
loadShaderpack();
|
|
}
|
|
|
|
/**
|
|
* Called when the title screen is initialized for the first time.
|
|
*/
|
|
public static void onLoadingComplete() {
|
|
if (!initialized) {
|
|
Iris.logger.warn("Iris::onLoadingComplete was called, but Iris::onEarlyInitialize was not called."
|
|
+ " Trying to avoid a crash but this is an odd state.");
|
|
return;
|
|
}
|
|
|
|
// Initialize the pipeline now so that we don't increase world loading time. Just going to guess that
|
|
// the player is in the overworld.
|
|
// See: https://github.com/IrisShaders/Iris/issues/323
|
|
lastDimension = DimensionId.OVERWORLD;
|
|
Iris.getPipelineManager().preparePipeline(DimensionId.OVERWORLD);
|
|
}
|
|
|
|
public static void toggleShaders(Minecraft minecraft, boolean enabled) throws IOException {
|
|
irisConfig.setShadersEnabled(enabled);
|
|
irisConfig.save();
|
|
|
|
reload();
|
|
if (minecraft.thePlayer != null) {
|
|
minecraft.thePlayer.addChatMessage(new ChatComponentText(enabled ? I18n.format("iris.shaders.toggled", currentPackName) : I18n.format("iris.shaders.disabled")));
|
|
}
|
|
}
|
|
|
|
public static void loadShaderpack() {
|
|
if (irisConfig == null) {
|
|
if (!initialized) {
|
|
throw new IllegalStateException("Iris::loadShaderpack was called, but Iris::onInitializeClient wasn't" + " called yet. How did this happen?");
|
|
} else {
|
|
throw new NullPointerException("Iris.irisConfig was null unexpectedly");
|
|
}
|
|
}
|
|
|
|
if (!irisConfig.areShadersEnabled()) {
|
|
logger.info("Shaders are disabled because enableShaders is set to false in shaders.properties");
|
|
|
|
setShadersDisabled();
|
|
|
|
return;
|
|
}
|
|
|
|
// Attempt to load an external shaderpack if it is available
|
|
final Optional<String> externalName = irisConfig.getShaderPackName();
|
|
|
|
if (!externalName.isPresent()) {
|
|
logger.info("Shaders are disabled because no valid shaderpack is selected");
|
|
|
|
setShadersDisabled();
|
|
|
|
return;
|
|
}
|
|
|
|
if (!loadExternalShaderpack(externalName.get())) {
|
|
logger.warn("Falling back to normal rendering without shaders because the shaderpack could not be loaded");
|
|
setShadersDisabled();
|
|
fallback = true;
|
|
}
|
|
}
|
|
|
|
private static boolean loadExternalShaderpack(String name) {
|
|
final Path shaderPackRoot;
|
|
final Path shaderPackConfigTxt;
|
|
|
|
try {
|
|
shaderPackRoot = getShaderpacksDirectory().resolve(name);
|
|
shaderPackConfigTxt = getShaderpacksDirectory().resolve(name + ".txt");
|
|
} catch (InvalidPathException e) {
|
|
logger.error("Failed to load the shaderpack \"{}\" because it contains invalid characters in its path", name);
|
|
|
|
return false;
|
|
}
|
|
|
|
final Path shaderPackPath;
|
|
|
|
if (shaderPackRoot.toString().endsWith(".zip")) {
|
|
final Optional<Path> optionalPath;
|
|
|
|
try {
|
|
optionalPath = loadExternalZipShaderpack(shaderPackRoot);
|
|
} catch (FileSystemNotFoundException | NoSuchFileException e) {
|
|
logger.error("Failed to load the shaderpack \"{}\" because it does not exist in your shaderpacks folder!", name);
|
|
|
|
return false;
|
|
} catch (ZipException e) {
|
|
logger.error("The shaderpack \"{}\" appears to be corrupted, please try downloading it again!", name);
|
|
|
|
return false;
|
|
} catch (IOException e) {
|
|
logger.error("Failed to load the shaderpack \"{}\"!", name);
|
|
logger.error("", e);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (optionalPath.isPresent()) {
|
|
shaderPackPath = optionalPath.get();
|
|
} else {
|
|
logger.error("Could not load the shaderpack \"{}\" because it appears to lack a \"shaders\" directory", name);
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!Files.exists(shaderPackRoot)) {
|
|
logger.error("Failed to load the shaderpack \"{}\" because it does not exist!", name);
|
|
return false;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
final Map<String, String> changedConfigs = tryReadConfigProperties(shaderPackConfigTxt).map(properties -> (Map<String, String>) (Map) properties)
|
|
.orElse(new HashMap<>());
|
|
|
|
changedConfigs.putAll(shaderPackOptionQueue);
|
|
clearShaderPackOptionQueue();
|
|
|
|
if (resetShaderPackOptions) {
|
|
changedConfigs.clear();
|
|
}
|
|
resetShaderPackOptions = false;
|
|
|
|
try {
|
|
currentPack = new ShaderPack(shaderPackPath, changedConfigs, StandardMacros.createStandardEnvironmentDefines());
|
|
|
|
final MutableOptionValues changedConfigsValues = currentPack.getShaderPackOptions().getOptionValues().mutableCopy();
|
|
|
|
// Store changed values from those currently in use by the shader pack
|
|
final Properties configsToSave = new Properties();
|
|
changedConfigsValues.getBooleanValues().forEach((k, v) -> configsToSave.setProperty(k, Boolean.toString(v)));
|
|
changedConfigsValues.getStringValues().forEach(configsToSave::setProperty);
|
|
|
|
tryUpdateConfigPropertiesFile(shaderPackConfigTxt, configsToSave);
|
|
} catch (Exception e) {
|
|
logger.error("Failed to load the shaderpack \"{}\"!", name);
|
|
logger.error("", e);
|
|
|
|
return false;
|
|
}
|
|
|
|
fallback = false;
|
|
currentPackName = name;
|
|
|
|
logger.info("Using shaderpack: " + name);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static Optional<Path> loadExternalZipShaderpack(Path shaderpackPath) throws IOException {
|
|
final FileSystem zipSystem = FileSystems.newFileSystem(shaderpackPath, Iris.class.getClassLoader());
|
|
zipFileSystem = zipSystem;
|
|
|
|
// Should only be one root directory for a zip shaderpack
|
|
final Path root = zipSystem.getRootDirectories().iterator().next();
|
|
|
|
final Path potentialShaderDir = zipSystem.getPath("shaders");
|
|
|
|
// If the shaders dir was immediately found return it
|
|
// Otherwise, manually search through each directory path until it ends with "shaders"
|
|
if (Files.exists(potentialShaderDir)) {
|
|
return Optional.of(potentialShaderDir);
|
|
}
|
|
|
|
// Sometimes shaderpacks have their shaders directory within another folder in the shaderpack
|
|
// For example Sildurs-Vibrant-Shaders.zip/shaders
|
|
// While other packs have Trippy-Shaderpack-master.zip/Trippy-Shaderpack-master/shaders
|
|
// This makes it hard to determine what is the actual shaders dir
|
|
try (Stream<Path> stream = Files.walk(root)) {
|
|
return stream.filter(Files::isDirectory).filter(path -> path.endsWith("shaders")).findFirst();
|
|
}
|
|
}
|
|
|
|
private static void setShadersDisabled() {
|
|
currentPack = null;
|
|
fallback = false;
|
|
currentPackName = "(off)";
|
|
|
|
logger.info("Shaders are disabled");
|
|
}
|
|
|
|
// Temp escalation
|
|
public static void setDebug(boolean enable) {
|
|
GLStateManager.assertMainThread();
|
|
final int success;
|
|
if (enable) {
|
|
success = GLDebug.setupDebugMessageCallback();
|
|
} else {
|
|
success = GLDebug.disableDebugMessages();
|
|
}
|
|
logger.info("Debug functionality is " + (enable ? "enabled, logging will be more verbose!" : "disabled."));
|
|
if (Minecraft.getMinecraft().thePlayer != null) {
|
|
Minecraft.getMinecraft().thePlayer.sendChatMessage(I18n.format(success != 0 ? (enable
|
|
? "iris.shaders.debug.enabled"
|
|
: "iris.shaders.debug.disabled") : "iris.shaders.debug.failure"));
|
|
if (success == 2) {
|
|
Minecraft.getMinecraft().thePlayer.sendChatMessage(I18n.format("iris.shaders.debug.restart"));
|
|
}
|
|
}
|
|
if(Iris.isInitialized()) {
|
|
try {
|
|
irisConfig.setDebugEnabled(enable);
|
|
irisConfig.save();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Optional<Properties> tryReadConfigProperties(Path path) {
|
|
final Properties properties = new Properties();
|
|
|
|
if (Files.exists(path)) {
|
|
try (InputStream is = Files.newInputStream(path)) {
|
|
// NB: config properties are specified to be encoded with ISO-8859-1 by OptiFine,
|
|
// so we don't need to do the UTF-8 workaround here.
|
|
properties.load(is);
|
|
} catch (IOException e) {
|
|
// TODO: Better error handling
|
|
return Optional.empty();
|
|
}
|
|
}
|
|
|
|
return Optional.of(properties);
|
|
}
|
|
|
|
private static void tryUpdateConfigPropertiesFile(Path path, Properties properties) {
|
|
try {
|
|
if (properties.isEmpty()) {
|
|
// Delete the file or don't create it if there are no changed configs
|
|
if (Files.exists(path)) {
|
|
Files.delete(path);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
try (OutputStream out = Files.newOutputStream(path)) {
|
|
properties.store(out, null);
|
|
}
|
|
} catch (IOException e) {
|
|
// TODO: Better error handling
|
|
}
|
|
}
|
|
|
|
public static boolean isValidShaderpack(Path pack) {
|
|
if (Files.isDirectory(pack)) {
|
|
// Sometimes the shaderpack directory itself can be
|
|
// identified as a shader pack due to it containing
|
|
// folders which contain "shaders" folders, this is
|
|
// necessary to check against that
|
|
if (pack.equals(getShaderpacksDirectory())) {
|
|
return false;
|
|
}
|
|
try (Stream<Path> stream = Files.walk(pack)) {
|
|
return stream.filter(Files::isDirectory)
|
|
// Prevent a pack simply named "shaders" from being
|
|
// identified as a valid pack
|
|
.filter(path -> !path.equals(pack)).anyMatch(path -> path.endsWith("shaders"));
|
|
} catch (IOException ignored) {
|
|
// ignored, not a valid shader pack.
|
|
}
|
|
}
|
|
|
|
if (pack.toString().endsWith(".zip")) {
|
|
try (FileSystem zipSystem = FileSystems.newFileSystem(pack, Iris.class.getClassLoader())) {
|
|
final Path root = zipSystem.getRootDirectories().iterator().next();
|
|
try (Stream<Path> stream = Files.walk(root)) {
|
|
return stream.filter(Files::isDirectory).anyMatch(path -> path.endsWith("shaders"));
|
|
}
|
|
} catch (ZipError zipError) {
|
|
// Java 8 seems to throw a ZipError instead of a subclass of IOException
|
|
Iris.logger.warn("The ZIP at " + pack + " is corrupt");
|
|
} catch (IOException ignored) {
|
|
// ignored, not a valid shader pack.
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static void queueShaderPackOptionsFromProfile(Profile profile) {
|
|
getShaderPackOptionQueue().putAll(profile.optionValues);
|
|
}
|
|
|
|
public static void queueShaderPackOptionsFromProperties(Properties properties) {
|
|
queueDefaultShaderPackOptionValues();
|
|
|
|
properties.stringPropertyNames().forEach(key -> getShaderPackOptionQueue().put(key, properties.getProperty(key)));
|
|
}
|
|
|
|
// Used in favor of resetShaderPackOptions as the aforementioned requires the pack to be reloaded
|
|
public static void queueDefaultShaderPackOptionValues() {
|
|
clearShaderPackOptionQueue();
|
|
|
|
getCurrentPack().ifPresent(pack -> {
|
|
final OptionSet options = pack.getShaderPackOptions().getOptionSet();
|
|
final OptionValues values = pack.getShaderPackOptions().getOptionValues();
|
|
|
|
options.getStringOptions().forEach((key, mOpt) -> {
|
|
if (values.getStringValue(key).isPresent()) {
|
|
getShaderPackOptionQueue().put(key, mOpt.getOption().getDefaultValue());
|
|
}
|
|
});
|
|
options.getBooleanOptions().forEach((key, mOpt) -> {
|
|
if (values.getBooleanValue(key) != OptionalBoolean.DEFAULT) {
|
|
getShaderPackOptionQueue().put(key, Boolean.toString(mOpt.getOption().getDefaultValue()));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
public static void clearShaderPackOptionQueue() {
|
|
getShaderPackOptionQueue().clear();
|
|
}
|
|
|
|
public static void resetShaderPackOptionsOnNextReload() {
|
|
resetShaderPackOptions = true;
|
|
}
|
|
|
|
public static boolean shouldResetShaderPackOptionsOnNextReload() {
|
|
return resetShaderPackOptions;
|
|
}
|
|
|
|
public static void reload() throws IOException {
|
|
// allows shaderpacks to be changed at runtime
|
|
irisConfig.initialize();
|
|
|
|
// Destroy all allocated resources
|
|
destroyEverything();
|
|
|
|
// Load the new shaderpack
|
|
loadShaderpack();
|
|
|
|
// Very important - we need to re-create the pipeline straight away.
|
|
// https://github.com/IrisShaders/Iris/issues/1330
|
|
if (Minecraft.getMinecraft().theWorld != null) {
|
|
Iris.getPipelineManager().preparePipeline(Iris.getCurrentDimension());
|
|
}
|
|
// Reload Sodium/Invalidate Chunk Cache
|
|
Minecraft.getMinecraft().renderGlobal.loadRenderers();
|
|
}
|
|
|
|
/**
|
|
* Destroys and deallocates all created OpenGL resources. Useful as part of a reload.
|
|
*/
|
|
private static void destroyEverything() {
|
|
currentPack = null;
|
|
|
|
getPipelineManager().destroyPipeline();
|
|
|
|
// Close the zip filesystem that the shaderpack was loaded from
|
|
//
|
|
// This prevents a FileSystemAlreadyExistsException when reloading shaderpacks.
|
|
if (zipFileSystem != null) {
|
|
try {
|
|
zipFileSystem.close();
|
|
} catch (NoSuchFileException e) {
|
|
logger.warn("Failed to close the shaderpack zip when reloading because it was deleted, proceeding anyways.");
|
|
} catch (IOException e) {
|
|
logger.error("Failed to close zip file system?", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static DimensionId lastDimension = null;
|
|
|
|
public static DimensionId getCurrentDimension() {
|
|
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
|
|
|
if (level != null) {
|
|
if (level.provider == null) return DimensionId.OVERWORLD;
|
|
|
|
if ((level.provider.isHellWorld || level.provider.hasNoSky) || level.provider.dimensionId == -1) {
|
|
return DimensionId.NETHER;
|
|
} else if (level.provider.dimensionId == 1) {
|
|
return DimensionId.END;
|
|
} else {
|
|
return DimensionId.OVERWORLD;
|
|
}
|
|
} else {
|
|
// This prevents us from reloading the shaderpack unless we need to. Otherwise, if the player is in the
|
|
// nether and quits the game, we might end up reloading the shaders on exit and on entry to the level
|
|
// because the code thinks that the dimension changed.
|
|
return lastDimension;
|
|
}
|
|
}
|
|
|
|
private static WorldRenderingPipeline createPipeline(DimensionId dimensionId) {
|
|
if (currentPack == null) {
|
|
// Completely disables shader-based rendering
|
|
return new FixedFunctionWorldRenderingPipeline();
|
|
}
|
|
|
|
final ProgramSet programs = currentPack.getProgramSet(dimensionId);
|
|
|
|
|
|
try {
|
|
return new DeferredWorldRenderingPipeline(programs);
|
|
} catch (Exception e) {
|
|
logger.error("Failed to create shader rendering pipeline, disabling shaders!", e);
|
|
// TODO: This should be reverted if a dimension change causes shaders to compile again
|
|
fallback = true;
|
|
|
|
return new FixedFunctionWorldRenderingPipeline();
|
|
}
|
|
}
|
|
|
|
@NotNull
|
|
public static PipelineManager getPipelineManager() {
|
|
if (pipelineManager == null) {
|
|
pipelineManager = new PipelineManager(Iris::createPipeline);
|
|
}
|
|
|
|
return pipelineManager;
|
|
}
|
|
|
|
@NotNull
|
|
public static Optional<ShaderPack> getCurrentPack() {
|
|
return Optional.ofNullable(currentPack);
|
|
}
|
|
|
|
public static String getVersion() {
|
|
if (IRIS_VERSION == null) {
|
|
return "Version info unknown!";
|
|
}
|
|
|
|
return IRIS_VERSION;
|
|
}
|
|
|
|
public static String getFormattedVersion() {
|
|
final ChatFormatting color;
|
|
String version = getVersion();
|
|
|
|
if (version.endsWith("-development-environment")) {
|
|
color = ChatFormatting.GOLD;
|
|
version = version.replace("-development-environment", " (Development Environment)");
|
|
} else if (version.endsWith("-dirty") || version.contains("unknown") || version.endsWith("-nogit")) {
|
|
color = ChatFormatting.RED;
|
|
} else if (version.contains("+rev.")) {
|
|
color = ChatFormatting.LIGHT_PURPLE;
|
|
} else {
|
|
color = ChatFormatting.GREEN;
|
|
}
|
|
|
|
return color + version;
|
|
}
|
|
|
|
public static Path getShaderpacksDirectory() {
|
|
if (shaderpacksDirectory == null) {
|
|
shaderpacksDirectory = Minecraft.getMinecraft().mcDataDir.toPath().resolve("shaderpacks");
|
|
}
|
|
|
|
return shaderpacksDirectory;
|
|
}
|
|
|
|
public static ShaderpackDirectoryManager getShaderpacksDirectoryManager() {
|
|
if (shaderpacksDirectoryManager == null) {
|
|
shaderpacksDirectoryManager = new ShaderpackDirectoryManager(getShaderpacksDirectory());
|
|
}
|
|
|
|
return shaderpacksDirectoryManager;
|
|
}
|
|
|
|
public void fmlInitEvent() {
|
|
IRIS_VERSION = Tags.VERSION;
|
|
reloadKeybind = new KeyBinding("Reload Shaders", 0, "Iris Keybinds");
|
|
toggleShadersKeybind = new KeyBinding("Toggle Shaders", 0, "Iris Keybinds");
|
|
shaderpackScreenKeybind = new KeyBinding("Shaderpack Selection Screen", 0, "Iris Keybinds");
|
|
|
|
ClientRegistry.registerKeyBinding(reloadKeybind);
|
|
ClientRegistry.registerKeyBinding(toggleShadersKeybind);
|
|
ClientRegistry.registerKeyBinding(shaderpackScreenKeybind);
|
|
}
|
|
}
|