Compare commits

...
6 Commits
41 changed files with 827 additions and 153 deletions

No files matched your search

+1 -1
View File
@@ -66,7 +66,7 @@ configurations.all {
}
dependencies {
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.14:dev")
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.158:dev")
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.1.0") { transitive = false }
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:lwjgl3ify:3.0.99")
@@ -8,6 +8,8 @@ import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Collections;
import java.util.List;
/**
* Abstract rendering backend.
@@ -25,6 +27,14 @@ public abstract class RenderBackend {
/** Higher priority backends are preferred. */
public int getPriority() { return 0; }
public boolean supportsFileDrop() { return false; }
public void startFileDrop() {}
public void stopFileDrop() {}
public List<String> pollDroppedFiles() { return Collections.emptyList(); }
public abstract int getMinGLSLVersion();
public abstract void flush();
@@ -35,11 +35,30 @@ public final class EcosystemNarrowRules {
"com.fiskmods.lightsabers.asm.transformers"
)),
new NarrowRule("FiskHeroes", "com.fiskmods.heroes.asm", List.of(
"com.fiskmods.heroes.asm.SHLoadingPlugin",
"com.fiskmods.heroes.asm.transformers"
)),
new NarrowRule("Ears", "com.unascribed.ears", List.of(
"com.unascribed.ears.asm",
"com.unascribed.ears.common.agent"
)),
new NarrowRule("FoamFix", "pl.asie.foamfix", List.of(
"pl.asie.foamfix.bugfixmod",
"pl.asie.foamfix.coremod",
"pl.asie.foamfix.forkage",
"pl.asie.foamfix.ghostbuster",
"pl.asie.foamfix.FoamFixMod",
"pl.asie.foamfix.HackyMessageFactoryWrapper",
"pl.asie.foamfix.LaunchWrapperRuntimeFix",
"pl.asie.foamfix.ProxyClient",
"pl.asie.foamfix.ProxyCommon",
"pl.asie.foamfix.repack.com.unascribed.ears.asm",
"pl.asie.foamfix.repack.com.unascribed.ears.common.agent"
)),
new NarrowRule("Alfheim", "alfheim.common.core.asm.hook", List.of(
"alfheim.common.core.asm.hook.AlfheimFieldHookHandler",
"alfheim.common.core.asm.hook.AlfheimHPHooks",
@@ -23,6 +23,9 @@ import org.lwjgl.opengl.GL44C;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.opengl.GLDebugMessageCallback;
import org.lwjgl.sdl.SDL_DropEvent;
import org.lwjgl.sdl.SDL_EventFilter;
import org.lwjgl.sdl.SDLEvents;
import org.lwjgl.system.MemoryStack;
import java.nio.ByteBuffer;
@@ -31,6 +34,10 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* LWJGL3 GL implementation of {@link RenderBackend}.
@@ -43,6 +50,10 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
private GLDebugMessageCallback debugCallback;
private boolean debugOutputActive;
private SDL_EventFilter dropEventFilter;
private final ConcurrentLinkedQueue<String> droppedFiles = new ConcurrentLinkedQueue<>();
private volatile boolean watchingDrops;
@Override
public void init() {
caps = GL.getCapabilities();
@@ -50,7 +61,49 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public void shutdown() {
// no-op
stopFileDrop();
}
@Override
public boolean supportsFileDrop() {
return true;
}
@Override
public void startFileDrop() {
if (watchingDrops) return;
dropEventFilter = SDL_EventFilter.create((userData, eventPtr) -> {
if (SDL_DropEvent.ntype(eventPtr) == SDLEvents.SDL_EVENT_DROP_FILE) {
final String path = SDL_DropEvent.ndataString(eventPtr);
if (path != null && !path.isEmpty()) {
droppedFiles.add(path);
}
}
return true;
});
SDLEvents.SDL_AddEventWatch(dropEventFilter, 0L);
watchingDrops = true;
}
@Override
public void stopFileDrop() {
if (!watchingDrops) return;
SDLEvents.SDL_RemoveEventWatch(dropEventFilter, 0L);
dropEventFilter.free();
dropEventFilter = null;
droppedFiles.clear();
watchingDrops = false;
}
@Override
public List<String> pollDroppedFiles() {
if (droppedFiles.isEmpty()) return Collections.emptyList();
final List<String> out = new ArrayList<>();
String p;
while ((p = droppedFiles.poll()) != null) {
out.add(p);
}
return out;
}
@Override
@@ -310,6 +310,16 @@ public class AngelicaConfig {
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public boolean narrowEars;
@Config.Comment("Narrow Fisk's Superheroes transformer exclusions to allow GL redirection")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public boolean narrowFiskHeroes;
@Config.Comment("Narrow FoamFix transformer exclusions to allow GL redirection in its repackaged Ears")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public boolean narrowFoamFix;
}
@Config.Comment("Renders chunks before neighbors are ready. Improves loading at render distance edges, useful for low render distance servers.")
@@ -186,6 +186,8 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
narrowTransformerConfig("AdvancedLightsabers", AngelicaConfig.transformerCompat.narrowAdvancedLightsabers);
narrowTransformerConfig("Alfheim", AngelicaConfig.transformerCompat.narrowAlfheim);
narrowTransformerConfig("Ears", AngelicaConfig.transformerCompat.narrowEars);
narrowTransformerConfig("FiskHeroes", AngelicaConfig.transformerCompat.narrowFiskHeroes);
narrowTransformerConfig("FoamFix", AngelicaConfig.transformerCompat.narrowFoamFix);
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.IncompatibleModsDisablerTweaker");
if (AngelicaConfig.enableHudCaching) {
@@ -17,7 +17,6 @@ public class CeleritasBlockTransformer implements IClassTransformer {
public CeleritasBlockTransformer() {
this.inner = new CeleritasBlockTransform(AngelicaClientTweaker.isObfEnv());
this.exclusions = inner.getTransformerExclusions();
this.inner.setCeleritasSetting();
}
/**
@@ -31,16 +31,21 @@ public final class CeleritasBlockTransform implements Opcodes {
private static final Logger LOGGER = LogManager.getLogger("CeleritasBlockTransformer");
private static final String BlockClass = "net/minecraft/block/Block";
private static final String ThreadedBlockData = "com/gtnewhorizons/angelica/client/rendering/ThreadedBlockData";
/** All classes under <tt>net.minecraft.block.Block*</tt> are Block subclasses save for these. */
private static final String[] VanillaBlockExclusions = {
"net/minecraft/block/IGrowable",
"net/minecraft/block/ITileEntityProvider",
"net/minecraft/block/BlockEventData",
"net/minecraft/block/BlockSourceImpl",
"net/minecraft/block/material/"
};
private final Map<String, String> fieldNameToRedirect = new HashMap<>();
private final Set<String> blockSubclasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final Set<String> moddedBlockSubclasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
// Block subclass owners we shouldn't redirect because they shadow some fields we want to redirect
private final Set<String> blockSubclassExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final ClassConstantPoolParser cstPoolParser;
private final MethodHandle celeritasEnabledGetter;
private boolean isCeleritasEnabled;
public CeleritasBlockTransform(boolean isObf) {
final List<Pair<String, String>> mappings = ImmutableList.of(
Pair.of("minX", "field_149759_B"),
@@ -56,37 +61,30 @@ public final class CeleritasBlockTransform implements Opcodes {
}
this.cstPoolParser = new ClassConstantPoolParser(this.fieldNameToRedirect.keySet().toArray(new String[0]));
}
blockSubclasses.add(BlockClass);
try {
// Needed because the config is loaded in LaunchClassLoader, but we need to access it in the parent system loader.
final Class<?> angelicaConfig = Class.forName("com.gtnewhorizons.angelica.config.AngelicaConfig", true, Launch.classLoader);
celeritasEnabledGetter = MethodHandles.lookup().findStaticGetter(angelicaConfig, "enableCeleritas", boolean.class);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
private boolean isVanillaBlockSubclass(String className) {
if (!className.startsWith(BlockClass)) {
return false;
}
}
public void setCeleritasSetting() {
isCeleritasEnabled = true;
}
private boolean isCeleritasEnabled() {
if (isCeleritasEnabled) return true;
try {
return (boolean) celeritasEnabledGetter.invokeExact();
} catch (Throwable e) {
throw new RuntimeException(e);
for (String exclusion : VanillaBlockExclusions) {
if (className.startsWith(exclusion)) {
return false;
}
}
return true;
}
// This method needs to be called for every class, including the ones we don't want to transform
private boolean isBlockSubclass(String className) {
return isVanillaBlockSubclass(className) || moddedBlockSubclasses.contains(className);
}
// This method needs to be called for every class, including the ones we don't want to transform.
// Vanilla blocks are recognized by name; only modded subclasses need tracking, and FML's
// EventSubscriptionTransformer guarantees a modded class's superclass loads first.
public void trackBlockSubclasses(String className, String superClassName) {
// It works because the deepest subclasses are always transformed first due to
// ForgeEventTransformer recursive superclass loading
if (blockSubclasses.contains(superClassName)) {
blockSubclasses.add(className);
if (!isVanillaBlockSubclass(className) && isBlockSubclass(superClassName)) {
moddedBlockSubclasses.add(className);
if (blockSubclassExclusions.contains(superClassName)) {
LOGGER.info("Class {} extends a class with shadowed block bounds fields and will be skipped from redirecting", className);
@@ -110,9 +108,6 @@ public final class CeleritasBlockTransform implements Opcodes {
/** @return Was the class changed? */
public boolean transformClassNode(String transformedName, ClassNode cn) {
if (!isCeleritasEnabled()) {
return false;
}
boolean changed = false;
if (BlockClass.equals(cn.name)) {
changed = cn.fields.removeIf(field -> fieldNameToRedirect.containsKey(field.name));
@@ -124,7 +119,8 @@ public final class CeleritasBlockTransform implements Opcodes {
}
private void trackBlockShadowingFields(ClassNode cn) {
if (blockSubclasses.contains(cn.name)) {
// Only modded subclasses can shadow; vanilla declares these fields solely in Block.
if (moddedBlockSubclasses.contains(cn.name)) {
for (FieldNode field : cn.fields) {
if (fieldNameToRedirect.containsKey(field.name)) {
LOGGER.info("Class '{}' shadows one or more block bounds fields, these accesses won't be redirected!", cn.name);
@@ -141,7 +137,7 @@ public final class CeleritasBlockTransform implements Opcodes {
for (AbstractInsnNode node = mn.instructions.getFirst(); node != null; node = node.getNext()) {
if ((node.getOpcode() == GETFIELD || node.getOpcode() == PUTFIELD) && node instanceof FieldInsnNode fNode) {
String newFieldName = fieldNameToRedirect.get(fNode.name);
if (newFieldName != null && blockSubclasses.contains(fNode.owner) && !blockSubclassExclusions.contains(fNode.owner)) {
if (newFieldName != null && isBlockSubclass(fNode.owner) && !blockSubclassExclusions.contains(fNode.owner)) {
if (LOG_SPAM) {
LOGGER.info("Redirecting Block.{} in {} to thread-safe wrapper", fNode.name, transformedName);
}
@@ -329,6 +329,15 @@ public enum Mixins implements IMixins {
)
),
IRIS_SHADERS_CHISEL_BEACON(new MixinBuilder("Iris Chisel Beacon Beam")
.setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addRequiredMod(TargetedMod.CHISEL)
.addClientMixins(
"chisel.MixinRenderCarvableBeacon"
)
),
ANGELICA_TEXTURE(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris || AngelicaConfig.enableCeleritas)
@@ -10,6 +10,7 @@ public enum TargetedMod implements ITargetMod {
ARCHAICFIX("org.embeddedt.archaicfix.ArchaicCore", "archaicfix"),
BACKHAND("xonin.backhand.coremod.BackhandLoadingPlugin", "backhand"),
CAMPFIRE_BACKPORT(null, "campfirebackport"),
CHISEL(null, "chisel"),
COFHCORE( "cofh.asm.LoadingPlugin", "CoFHCore"),
DYNAMIC_SURROUNDINGS_MIST("org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround"),
DYNAMIC_SURROUNDINGS_ORIGINAL("org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround"),
@@ -1,28 +1,45 @@
package com.gtnewhorizons.angelica.render;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VertexBufferType;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.client.rendering.GlUniformFloat2v;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.util.AxisAlignedBB;
import org.embeddedt.embeddium.impl.gl.shader.GlProgram;
import org.embeddedt.embeddium.impl.gl.shader.GlShader;
import org.embeddedt.embeddium.impl.gl.shader.ShaderConstants;
import org.embeddedt.embeddium.impl.gl.shader.ShaderType;
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat;
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat3v;
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat4v;
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformMatrix4f;
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
public final class SelectionBoxRenderer {
private static GlProgram<SelectionBoxUniforms> program;
private static int emptyVao;
private static IVertexArrayObject vao;
private static final Matrix4f mvpMatrix = new Matrix4f();
private static final float[] colorBuf = new float[4];
private static final int VERT_COUNT = 24;
private static final float[] UNIT_EDGES = {
0,0,0, 1,0,0, 1,0,0, 1,0,1,
1,0,1, 0,0,1, 0,0,1, 0,0,0,
0,1,0, 1,1,0, 1,1,0, 1,1,1,
1,1,1, 0,1,1, 0,1,1, 0,1,0,
0,0,0, 0,1,0, 1,0,0, 1,1,0,
1,0,1, 1,1,1, 0,0,1, 0,1,1
};
private SelectionBoxRenderer() {}
public static void init() {
@@ -40,8 +57,6 @@ public final class SelectionBoxRenderer {
program = builder.link(ctx -> new SelectionBoxUniforms(
ctx.bindUniform("u_MVP", GlUniformMatrix4f::new),
ctx.bindUniform("u_Min", GlUniformFloat3v::new),
ctx.bindUniform("u_Max", GlUniformFloat3v::new),
ctx.bindUniform("u_Color", GlUniformFloat4v::new),
needsGS ? ctx.bindUniform("u_ViewportSize", GlUniformFloat2v::new) : null,
needsGS ? ctx.bindUniform("u_LineWidth", GlUniformFloat::new) : null));
@@ -50,20 +65,34 @@ public final class SelectionBoxRenderer {
fs.destroy();
if (gs != null) gs.destroy();
emptyVao = GLStateManager.glGenVertexArrays();
final ByteBuffer buf = BufferUtils.createByteBuffer(UNIT_EDGES.length * Float.BYTES);
buf.asFloatBuffer().put(UNIT_EDGES);
vao = VertexBufferType.IMMUTABLE.allocate(DefaultVertexFormat.POSITION, GL11.GL_LINES, buf, VERT_COUNT);
}
public static void draw(AxisAlignedBB aabb, int color) {
if (program == null) return;
if (program == null || vao == null) return;
final float minX = (float) aabb.minX, minY = (float) aabb.minY, minZ = (float) aabb.minZ;
final float extX = (float) (aabb.maxX - aabb.minX);
final float extY = (float) (aabb.maxY - aabb.minY);
final float extZ = (float) (aabb.maxZ - aabb.minZ);
if (AngelicaConfig.enableIris && IrisApi.getInstance().isShaderPackInUse()) {
drawThroughShader(minX, minY, minZ, extX, extY, extZ, color);
} else {
drawFast(minX, minY, minZ, extX, extY, extZ, color);
}
}
private static void drawFast(float minX, float minY, float minZ, float extX, float extY, float extZ, int color) {
final SelectionBoxUniforms uniforms = program.getInterface();
program.bind();
GLStateManager.getProjectionMatrix().mul(GLStateManager.getModelViewMatrix(), mvpMatrix);
mvpMatrix.translate(minX, minY, minZ).scale(extX, extY, extZ);
uniforms.mvp.set(mvpMatrix);
uniforms.min.set((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ);
uniforms.max.set((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
if (color != -1) {
colorBuf[0] = ((color >> 16) & 0xFF) / 255.0f;
@@ -84,25 +113,42 @@ public final class SelectionBoxRenderer {
uniforms.lineWidth.setFloat(GLStateManager.getLineState().getWidth());
}
GLStateManager.glBindVertexArray(emptyVao);
GLStateManager.glDrawArrays(GL11.GL_LINES, 0, 24);
GLStateManager.glBindVertexArray(0);
vao.render();
program.unbind();
}
private static void drawThroughShader(float minX, float minY, float minZ, float extX, float extY, float extZ, int color) {
GLStateManager.glPushMatrix();
GLStateManager.glTranslatef(minX, minY, minZ);
GLStateManager.glScalef(extX, extY, extZ);
final Color4 prev = GLStateManager.getColor();
final boolean overrideColor = color != -1;
if (overrideColor) {
GLStateManager.glColor4f(((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, 1.0f);
}
vao.render();
if (overrideColor) {
GLStateManager.glColor4f(prev.getRed(), prev.getGreen(), prev.getBlue(), prev.getAlpha());
}
GLStateManager.glPopMatrix();
}
public static void destroy() {
if (program != null) {
program.destroy();
program = null;
}
if (emptyVao != 0) {
GLStateManager.glDeleteVertexArrays(emptyVao);
emptyVao = 0;
if (vao != null) {
vao.delete();
vao = null;
}
}
private record SelectionBoxUniforms(GlUniformMatrix4f mvp, GlUniformFloat3v min, GlUniformFloat3v max, GlUniformFloat4v color, GlUniformFloat2v viewportSize, GlUniformFloat lineWidth) {
private record SelectionBoxUniforms(GlUniformMatrix4f mvp, GlUniformFloat4v color, GlUniformFloat2v viewportSize, GlUniformFloat lineWidth) {
}
}
@@ -20,11 +20,7 @@ final class BlockOrientation extends RenderBlockState {
// 1 2 3
// c: coordinate (x,y,z) 0-2
private static final int[][][] NEIGHBOR_OFFSET = new int[][][] {
(
fixedBottomFaceUV
? makeNeighborOffset(EAST_FACE, SOUTH_FACE, WEST_FACE, NORTH_FACE) // BOTTOM_FACE fixedBottomFaceUV
: makeNeighborOffset(WEST_FACE, SOUTH_FACE, EAST_FACE, NORTH_FACE) // BOTTOM_FACE not fixed
),
makeNeighborOffset(EAST_FACE, SOUTH_FACE, WEST_FACE, NORTH_FACE), // BOTTOM_FACE
makeNeighborOffset(WEST_FACE, SOUTH_FACE, EAST_FACE, NORTH_FACE), // TOP_FACE
makeNeighborOffset(EAST_FACE, BOTTOM_FACE, WEST_FACE, TOP_FACE), // NORTH_FACE
makeNeighborOffset(WEST_FACE, BOTTOM_FACE, EAST_FACE, TOP_FACE), // SOUTH_FACE
@@ -48,6 +44,7 @@ final class BlockOrientation extends RenderBlockState {
private int textureFace;
private int textureFaceOrig;
private int rotateUV;
private boolean flipBottom;
@Override
public void clear() {
@@ -60,6 +57,7 @@ final class BlockOrientation extends RenderBlockState {
offsetsComputed = false;
haveOffsets = false;
dx = dy = dz = 0;
flipBottom = !fixedBottomFaceUV;
}
@Override
@@ -104,7 +102,7 @@ final class BlockOrientation extends RenderBlockState {
@Override
public int[] getOffset(int blockFace, int relativeDirection) {
return NEIGHBOR_OFFSET[blockFace][rotateUV(relativeDirection)];
return NEIGHBOR_OFFSET[flipBottom && blockFace == 0 ? 1 : blockFace][rotateUV(relativeDirection)];
}
@Override
@@ -147,6 +145,11 @@ final class BlockOrientation extends RenderBlockState {
return haveOffsets;
}
public void setFlipBottom(){
flipBottom = true;
}
@Override
public boolean shouldConnectByBlock(Block neighbor, int neighborX, int neighborY, int neighborZ) {
return block == neighbor
@@ -172,6 +175,7 @@ final class BlockOrientation extends RenderBlockState {
copy.textureFace = textureFace;
copy.textureFaceOrig = textureFaceOrig;
copy.rotateUV = rotateUV;
copy.flipBottom = flipBottom;
copy.blockAccess = blockAccess;
copy.block = block;
@@ -204,6 +208,7 @@ final class BlockOrientation extends RenderBlockState {
rotateUV = 0;
textureFace = blockFaceToTextureFace(blockFace);
metadataBits = (1 << metadata) | (1 << altMetadata);
flipBottom = !fixedBottomFaceUV;
}
void setBlockMetadata(Block block, int metadata, int face) {
@@ -53,6 +53,9 @@ public final class CompactCtmQuadProcessor {
}
public boolean processFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon origIcon) {
if(renderBlockState instanceof BlockOrientation blockOrientation){
blockOrientation.setFlipBottom();
}
int neighborBits = override.getNeighborBits(renderBlockState, origIcon);
int ctmIndex = TileOverride.neighborMap[neighborBits & 0xFF];
+28 -3
View File
@@ -72,7 +72,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.zip.ZipError;
import java.util.zip.ZipException;
public class Iris {
@@ -458,6 +457,33 @@ public class Iris {
}
}
/**
* Toggles shader debug mode. When enabled, patched shader sources are dumped to disk (see
* {@link net.coderbot.iris.pipeline.PatchedShaderPrinter}) and logging is more verbose.
*/
public static void setDebug(boolean enable) {
try {
irisConfig.setDebugEnabled(enable);
irisConfig.save();
reload();
} catch (IOException e) {
logger.error("Failed to " + (enable ? "enable" : "disable") + " debug mode", e);
}
logger.info("Debug functionality is " + (enable ? "enabled, logging will be more verbose!" : "disabled."));
}
/**
* Toggles whether unrecognized shader packs may load, exposing the {@code ALLOWS_UNKNOWN_SHADERS} macro.
*/
public static void setAllowUnknownShaders(boolean allow) {
try {
irisConfig.setUnknown(allow);
reload();
} catch (IOException e) {
logger.error("Failed to " + (allow ? "allow" : "disallow") + " unknown shaders", e);
}
}
public static void loadShaderpack() {
if (irisConfig == null) {
if (!initialized) {
@@ -678,8 +704,7 @@ public class Iris {
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
} catch (ZipException e) {
Iris.logger.warn("The ZIP at " + pack + " is corrupt");
} catch (IOException ignored) {
// ignored, not a valid shader pack.
@@ -38,6 +38,8 @@ public class IrisConfig {
*/
private boolean enableDebugOptions;
private boolean allowUnknownShaders;
private final Path propertiesPath;
public IrisConfig(Path propertiesPath) {
@@ -45,6 +47,7 @@ public class IrisConfig {
enableShaders = true;
disableUpdateMessage = false;
enableDebugOptions = false;
allowUnknownShaders = false;
this.propertiesPath = propertiesPath;
}
@@ -113,6 +116,25 @@ public class IrisConfig {
return enableDebugOptions;
}
/**
* Sets whether debug features are enabled.
*/
public void setDebugEnabled(boolean enabled) {
this.enableDebugOptions = enabled;
}
public boolean shouldAllowUnknownShaders() {
return allowUnknownShaders;
}
/**
* Sets whether unrecognized shader packs may load.
*/
public void setUnknown(boolean allow) throws IOException {
this.allowUnknownShaders = allow;
save();
}
/**
* loads the config file and then populates the string, int, and boolean entries with the parsed entries
*
@@ -132,6 +154,7 @@ public class IrisConfig {
shaderPackName = properties.getProperty("shaderPack");
enableShaders = !"false".equals(properties.getProperty("enableShaders"));
enableDebugOptions = "true".equals(properties.getProperty("enableDebugOptions"));
allowUnknownShaders = "true".equals(properties.getProperty("allowUnknownShaders"));
disableUpdateMessage = "true".equals(properties.getProperty("disableUpdateMessage"));
// TODO: GUI
try {
@@ -159,6 +182,7 @@ public class IrisConfig {
properties.setProperty("shaderPack", getShaderPackName().orElse(""));
properties.setProperty("enableShaders", enableShaders ? "true" : "false");
properties.setProperty("enableDebugOptions", enableDebugOptions ? "true" : "false");
properties.setProperty("allowUnknownShaders", allowUnknownShaders ? "true" : "false");
properties.setProperty("disableUpdateMessage", disableUpdateMessage ? "true" : "false");
properties.setProperty("maxShadowRenderDistance", String.valueOf(IrisVideoSettings.shadowDistance));
// NB: This uses ISO-8859-1 with unicode escapes as the encoding
@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.Tags;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import cpw.mods.fml.common.Loader;
import net.coderbot.iris.Iris;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.parsing.BiomeCategories;
import net.coderbot.iris.pipeline.HandRenderer;
@@ -91,6 +92,10 @@ public class StandardMacros {
define(standardDefines, "DISTANT_HORIZONS");
}
if (Iris.getIrisConfig().shouldAllowUnknownShaders()) {
define(standardDefines, "ALLOWS_UNKNOWN_SHADERS");
}
define(standardDefines, "DH_BLOCK_UNKNOWN", String.valueOf(0));
define(standardDefines, "DH_BLOCK_LEAVES", String.valueOf(1));
define(standardDefines, "DH_BLOCK_STONE", String.valueOf(2));
@@ -4,10 +4,13 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import cpw.mods.fml.client.config.GuiUtils;
import lombok.Getter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
/**
* Class serving as abstraction and
@@ -111,6 +114,32 @@ public final class GuiUtil {
font.drawStringWithShadow(text, x + 4, y + 4, 0xFFFFFF);
}
public static void drawScrollingText(FontRenderer font, String text, int centerX, int minX, int maxX, int minY, int maxY, int color) {
final int textWidth = font.getStringWidth(text);
final int boxWidth = maxX - minX;
final int textY = ((minY + maxY) - font.FONT_HEIGHT) / 2 + 1;
if (textWidth > boxWidth) {
final int overflow = textWidth - boxWidth;
final double time = System.currentTimeMillis() / 1000.0;
final double period = Math.max(overflow * 0.5, 3.0);
final double t = (Math.sin((Math.PI / 2.0) * Math.cos((Math.PI * 2.0) * time / period)) / 2.0) + 0.5;
final int scroll = (int) (t * overflow);
final Minecraft mc = client();
final int scale = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight).getScaleFactor();
GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
GLStateManager.glScissor(minX * scale, mc.displayHeight - (maxY * scale), boxWidth * scale, (maxY - minY) * scale);
font.drawStringWithShadow(text, minX - scroll, textY, color);
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
} else {
int textX = centerX - (textWidth / 2);
textX = Math.min(textX, maxX - textWidth);
textX = Math.max(textX, minX);
font.drawStringWithShadow(text, textX, textY, color);
}
}
/**
* Shorten a text to a specific length, adding an ellipsis (...)
* to the end if shortened.
@@ -140,13 +169,25 @@ public final class GuiUtil {
* @return the translated text if found, otherwise the default provided
*/
public static String translateOrDefault(String defaultText, String translationDesc, Object ... format) {
final String translated = I18n.format(translationDesc, format);
final String translated = format.length == 0
? translateLenient(translationDesc)
: I18n.format(translationDesc, format);
if(!translated.equals(translationDesc)) {
return translated;
}
return defaultText;
}
private static final String FORMAT_ERROR_PREFIX = "Format error: ";
/**
* Nasty dirty hack, but it works.
*/
public static String translateLenient(String key) {
final String translated = I18n.format(key);
return translated.startsWith(FORMAT_ERROR_PREFIX) ? translated.substring(FORMAT_ERROR_PREFIX.length()) : translated;
}
/**
* Plays the {@code UI_BUTTON_CLICK} sound event as a
* master sound effect.
@@ -155,7 +196,7 @@ public final class GuiUtil {
* or other action.
*/
public static void playButtonClickSound() {
// client().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1));
client().getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F));
}
/**
@@ -21,7 +21,9 @@ public class IrisElementRow {
private final int spacing;
private int x;
private int y;
private int width;
@Getter
private int width;
private int height;
public IrisElementRow(int spacing) {
@@ -19,7 +19,7 @@ public abstract class IrisGuiSlot extends GuiSlot {
super(mc, width, height, top, bottom, slotHeight);
// Set Center Vertically to false
this.field_148163_i = false;
this.headerPadding = 2;
}
@Override
@@ -37,11 +37,15 @@ public abstract class IrisGuiSlot extends GuiSlot {
}
@Override
protected void drawSelectionBox(int x, int y, int mouseX, int mouseY) {
final int oldPadding = this.headerPadding;
this.headerPadding = 2;
super.drawSelectionBox(x, y, mouseX, mouseY);
this.headerPadding = oldPadding;
public int func_148124_c/*getSlotIndexFromScreenCoords*/(int x, int y) {
final int left = this.left + this.width / 2 - this.getListWidth() / 2 + hitLeftInset() + hitXShift();
final int right = this.left + this.width / 2 + this.getListWidth() / 2 - hitRightInset() + hitXShift();
final int relativeY = y - this.top + (int) this.amountScrolled - hitYOffset();
final int index = relativeY / this.slotHeight;
if (y <= this.top || y >= this.bottom) {
return -1;
}
return x < this.getScrollBarX() && x >= left && x <= right && index >= 0 && relativeY >= 0 && index < this.getSize() ? index : -1;
}
@Override
@@ -53,6 +57,23 @@ public abstract class IrisGuiSlot extends GuiSlot {
return false;
}
protected int hitYOffset() {
return 4;
}
protected int hitRightInset() {
return 0;
}
protected int hitLeftInset() {
return 0;
}
protected int hitXShift() {
return 0;
}
public boolean mouseClicked(int mouseX, int mouseY, int mouseButton) {
if (!this.func_148125_i/*enabled*/()) {
return false;
@@ -60,15 +81,15 @@ public abstract class IrisGuiSlot extends GuiSlot {
final int size = this.getSize();
final int scrollBarX = this.getScrollBarX();
final int rightEdge = scrollBarX + 6;
final int elementLeft = this.width / 2 - this.getListWidth() / 2;
final int elementRight = this.width / 2 + this.getListWidth() / 2;
final int relativeY = mouseY - this.top - this.headerPadding + (int) this.amountScrolled - 4;
final int elementLeft = this.width / 2 - this.getListWidth() / 2 + hitLeftInset() + hitXShift();
final int elementRight = this.width / 2 + this.getListWidth() / 2 - hitRightInset() + hitXShift();
final int relativeY = mouseY - this.top + (int) this.amountScrolled - hitYOffset();
boolean handled = false;
final boolean leftMouseDown = Mouse.isButtonDown(0);
final boolean rightMouseDown = Mouse.isButtonDown(1);
if (mouseX <= this.left || mouseX >= this.right || mouseY <= this.top || mouseY >= this.bottom) {
return handled;
return false;
}
if (leftMouseDown && mouseX >= scrollBarX && mouseX <= rightEdge) {
scrolling = true;
@@ -25,7 +25,7 @@ public class ShaderPackOptionList extends IrisGuiSlot {
private final List<BaseEntry> entries = new ArrayList<>();
public ShaderPackOptionList(ShaderPackScreen screen, NavigationController navigation, ShaderPack pack, Minecraft client, int width, int height, int top, int bottom, int left, int right) {
super(client, width, height, top, bottom, 20);
super(client, width, height, top, bottom, 24);
this.navigation = navigation;
this.screen = screen;
@@ -72,7 +72,7 @@ public class ShaderPackOptionList extends IrisGuiSlot {
}
}
if (row.size() > 0) {
if (!row.isEmpty()) {
while (row.size() < columns) {
row.add(AbstractElementWidget.EMPTY);
}
@@ -93,7 +93,12 @@ public class ShaderPackOptionList extends IrisGuiSlot {
}
@Override
public boolean mouseReleased( int mouseX, int mouseY, int button) {
final int relativeY = mouseY - this.top - this.headerPadding + (int) this.amountScrolled - 4;
this.scrolling = false;
if (mouseY <= this.top || mouseY >= this.bottom) {
return false;
}
final int relativeY = mouseY - this.top + (int) this.amountScrolled - hitYOffset();
final int index = relativeY / this.slotHeight;
if (index < 0 || index >= this.entries.size())
@@ -113,11 +118,31 @@ public class ShaderPackOptionList extends IrisGuiSlot {
// noop
}
@Override
protected int hitYOffset() {
return 3;
}
@Override
protected int hitRightInset() {
return 4;
}
@Override
protected int hitLeftInset() {
return -1;
}
@Override
protected int hitXShift() {
return 2;
}
@Override
protected void drawSlot(int index, int x, int y, int i1, Tessellator tessellator, int mouseX, int mouseY) {
final BaseEntry entry = this.entries.get(index);
final boolean isMouseOver = this.func_148124_c/*getSlotIndexFromScreenCoords*/(mouseX, mouseY) == index;
entry.drawEntry(screen, index, x - 2, y + 4, this.getListWidth(), this.slotHeight, tessellator, mouseX, mouseY, isMouseOver);
entry.drawEntry(screen, index, x, y - 2, this.getListWidth(), this.slotHeight, tessellator, mouseX, mouseY, isMouseOver);
}
@@ -4,13 +4,23 @@ import lombok.Getter;
import lombok.Setter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gui.element.shaderselection.BaseEntry;
import net.coderbot.iris.gui.element.shaderselection.DownloadEntry;
import net.coderbot.iris.gui.element.shaderselection.LabelEntry;
import net.coderbot.iris.gui.element.shaderselection.ShaderPackEntry;
import net.coderbot.iris.gui.element.shaderselection.TopButtonRowEntry;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -31,15 +41,70 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
private final List<BaseEntry> entries = new ArrayList<>();
private final WatchService watcher;
private final WatchKey key;
private boolean keyValid;
public ShaderPackSelectionList(ShaderPackScreen screen, Minecraft client, int width, int height, int top, int bottom, int left, int right) {
super(client, width, height, top, bottom, 20);
this.screen = screen;
this.topButtonRow = new TopButtonRowEntry(this, Iris.getIrisConfig().areShadersEnabled());
WatchService watcher1;
WatchKey key1;
try {
watcher1 = FileSystems.getDefault().newWatchService();
key1 = Iris.getShaderpacksDirectory().register(watcher1,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
keyValid = true;
} catch (IOException e) {
Iris.logger.error("Couldn't register shaderpacks directory file watcher!", e);
watcher1 = null;
key1 = null;
keyValid = false;
}
this.watcher = watcher1;
this.key = key1;
refresh();
}
// Poll for new shaderpacks
public void poll() {
if (!keyValid) {
return;
}
boolean changed = false;
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
continue;
}
changed = true;
}
if (changed) {
refresh();
}
keyValid = key.reset();
}
public void close() throws IOException {
if (key != null) {
key.cancel();
}
if (watcher != null) {
watcher.close();
}
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
poll();
super.drawScreen(mouseX, mouseY, partialTicks);
}
public void refresh() {
this.entries.clear();
@@ -68,8 +133,13 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
this.entries.add(topButtonRow);
// Download something man
if (names.isEmpty()) {
this.entries.add(new DownloadEntry(this, "Download Shaders", "https://modrinth.com/shaders"));
}
// Only allow the enable/disable shaders button if the user has added a shader pack. Otherwise, the button will be disabled.
topButtonRow.allowEnableShadersButton = names.size() > 0;
topButtonRow.allowEnableShadersButton = !names.isEmpty();
int index = 0;
@@ -78,6 +148,11 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
addPackEntry(index, name);
}
// Try not to gaslight users in case they swap back and forth from lwjgl2 and lwjgl3
final String footerKey = BackendManager.RENDER_BACKEND.supportsFileDrop()
? "pack.iris.list.label"
: "pack.iris.list.label.lwjgl2";
addLabelEntries(EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format(footerKey));
}
public void addPackEntry(int index, String name) {
@@ -128,6 +203,8 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
return true;
} else if( entry instanceof TopButtonRowEntry topButtonRowEntry) {
return topButtonRowEntry.mouseClicked(mouseX, mouseY, 0);
} else if (entry instanceof DownloadEntry downloadEntry) {
return downloadEntry.click(this.screen);
}
return false;
}
@@ -152,7 +229,7 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
protected void drawSlot(int index, int x, int y, int i1, Tessellator tessellator, int mouseX, int mouseY) {
final BaseEntry entry = this.entries.get(index);
final boolean isMouseOver = this.func_148124_c/*getSlotIndexFromScreenCoords*/(mouseX, mouseY) == index;
entry.drawEntry(screen, index, x - 2, y + 4, this.getListWidth(), tessellator, mouseX, mouseY, isMouseOver);
entry.drawEntry(screen, index, x, y + 4, this.getListWidth(), tessellator, mouseX, mouseY, isMouseOver);
}
}
@@ -14,6 +14,7 @@ public class ElementRowEntry extends BaseEntry {
private int cachedWidth;
private int cachedPosX;
private float cachedSingleWidth;
public ElementRowEntry(NavigationController navigation, List<AbstractElementWidget<?>> widgets) {
super(navigation);
@@ -32,25 +33,43 @@ public class ElementRowEntry extends BaseEntry {
// Width of a single widget
final float singleWidgetWidth = (float) totalWidthWithoutMargins / widgets.size();
this.cachedSingleWidth = singleWidgetWidth;
for (int i = 0; i < widgets.size(); i++) {
final AbstractElementWidget<?> widget = widgets.get(i);
final boolean widgetHovered = isMouseOver && (getHoveredWidget(mouseX) == i);
widget.drawScreen(x + (int) ((singleWidgetWidth + 2) * i), y, (int) singleWidgetWidth, slotHeight + 2, mouseX, mouseY, 0, widgetHovered);
widget.drawScreen(x + (int) ((singleWidgetWidth + 2) * i), y, (int) singleWidgetWidth, slotHeight - 2, mouseX, mouseY, 0, widgetHovered);
screen.setElementHoveredStatus(widget, widgetHovered);
}
}
public int getHoveredWidget(int mouseX) {
final float positionAcrossWidget = ((float) MathHelper.clamp_int(mouseX - cachedPosX, 0, cachedWidth)) / cachedWidth;
final float CONTENT_ANCHOR_OFFSET = 2f;
final float rel = (mouseX - cachedPosX) + CONTENT_ANCHOR_OFFSET;
final float clamped = Math.clamp(rel, 0f, cachedWidth);
final float positionAcrossWidget = clamped / cachedWidth;
return MathHelper.clamp_int((int) Math.floor(widgets.size() * positionAcrossWidget), 0, widgets.size() - 1);
}
private int widgetAt(int mouseX) {
final int i = getHoveredWidget(mouseX);
final int widgetX = this.cachedPosX + (int) ((this.cachedSingleWidth + 2) * i);
if (mouseX < widgetX || mouseX >= widgetX + (int) this.cachedSingleWidth) {
return -1;
}
return i;
}
@Override
public boolean mouseClicked(int mouseX, int mouseY, int button) {
return this.widgets.get(getHoveredWidget(mouseX)).mouseClicked(mouseX, mouseY, button);
final int i = widgetAt(mouseX);
if (i < 0) {
return false;
}
return this.widgets.get(i).mouseClicked(mouseX, mouseY, button);
}
@Override
@@ -7,6 +7,7 @@ import net.coderbot.iris.gui.element.IrisElementRow;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
@@ -60,13 +61,12 @@ public class HeaderEntry extends BaseEntry {
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int slotWidth, int slotHeight, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
// Draw dividing line
// fill(x - 3, (y + slotHeight) - 2, x + slotWidth, (y + slotHeight) - 1, 0x66BEBEBE);
Gui.drawRect(x - 3, (y + slotHeight) - 3, x + slotWidth, (y + slotHeight) - 2, 0x66BEBEBE);
final int tickDelta = 0;
final FontRenderer font = Minecraft.getMinecraft().fontRenderer;
// Draw header text
this.screen.drawCenteredString(font, text, x + (int) (slotWidth * 0.5), y + 5, 0xFFFFFF);
GuiUtil.drawScrollingText(font, text, x + (int) (slotWidth * 0.5), x + 5,
((x + slotWidth) - 10) - this.utilityButtons.getWidth(), y + 5, y + 15, 0xFFFFFF);
GuiUtil.bindIrisWidgetsTexture();
@@ -0,0 +1,33 @@
package net.coderbot.iris.gui.element.shaderselection;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.element.ShaderPackSelectionList;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.minecraft.client.renderer.Tessellator;
/**
* Show a download button when no shaderpacks exist in the shaderpacks folder.
*/
public class DownloadEntry extends BaseEntry {
private final String label;
private final String url;
public DownloadEntry(ShaderPackSelectionList list, String label, String url) {
super(list);
this.label = label;
this.url = url;
}
public boolean click(ShaderPackScreen screen) {
GuiUtil.playButtonClickSound();
screen.openLinkConfirm(url);
return true;
}
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int listWidth, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
GuiUtil.bindIrisWidgetsTexture();
GuiUtil.drawButton(x - 2, y - 5, listWidth, 18, isMouseOver, false);
screen.drawCenteredString(label, (x + listWidth / 2) - 2, y, 0xFFFFFF);
}
}
@@ -14,6 +14,6 @@ public class LabelEntry extends BaseEntry {
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int listWidth, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
screen.drawCenteredString(label, x, y, 0xFFFFFF);
screen.drawCenteredString(label, (x + listWidth / 2) - 2, y, 0xFFFFFF);
}
}
@@ -1,6 +1,7 @@
package net.coderbot.iris.gui.element.shaderselection;
import lombok.Getter;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.element.ShaderPackSelectionList;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.minecraft.client.gui.FontRenderer;
@@ -37,6 +38,8 @@ public class ShaderPackEntry extends BaseEntry {
}
if(isMouseOver) {
GuiUtil.bindIrisWidgetsTexture();
GuiUtil.drawButton(x - 2, y - 6, listWidth, 20, true, false);
name = EnumChatFormatting.BOLD + name;
}
@@ -13,12 +13,10 @@ public class TopButtonRowEntry extends BaseEntry {
private static final String NONE_PRESENT_LABEL = I18n.format("options.iris.shaders.nonePresent"); //.withStyle(ChatFormatting.GRAY);
private static final String SHADERS_DISABLED_LABEL = I18n.format("options.iris.shaders.disabled");
private static final String SHADERS_ENABLED_LABEL = I18n.format("options.iris.shaders.enabled");
private static final int REFRESH_BUTTON_WIDTH = 18;
private final ShaderPackSelectionList shaderPackSelectionList;
private final IrisElementRow buttons = new IrisElementRow();
private final EnableShadersButtonElement enableDisableButton;
private final IrisElementRow.Element refreshPacksButton;
public boolean allowEnableShadersButton = true;
public boolean shadersEnabled;
@@ -38,15 +36,7 @@ public class TopButtonRowEntry extends BaseEntry {
return false;
});
this.refreshPacksButton = new IrisElementRow.IconButtonElement(
GuiUtil.Icon.REFRESH,
button -> {
this.shaderPackSelectionList.refresh();
GuiUtil.playButtonClickSound();
return true;
});
this.buttons.add(this.enableDisableButton, 0).add(this.refreshPacksButton, REFRESH_BUTTON_WIDTH);
this.buttons.add(this.enableDisableButton, 0);
}
public void setShadersEnabled(boolean shadersEnabled) {
@@ -57,7 +47,7 @@ public class TopButtonRowEntry extends BaseEntry {
@Override
public void drawEntry(ShaderPackScreen screen, int index, int x, int y, int listWidth, Tessellator tessellator, int mouseX, int mouseY, boolean isMouseOver) {
this.buttons.setWidth(this.enableDisableButton, (listWidth - 1) - REFRESH_BUTTON_WIDTH);
this.buttons.setWidth(this.enableDisableButton, listWidth);
this.enableDisableButton.centerX = x + (int)(listWidth * 0.5);
this.buttons.drawScreen(x - 2, y - 3, 18, mouseX, mouseY, 0, isMouseOver);
@@ -90,9 +90,9 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
FontRenderer font = Minecraft.getMinecraft().fontRenderer;
// Draw the label
font.drawStringWithShadow(this.trimmedLabel, x + 6, y + 7, 0xFFFFFF);
font.drawStringWithShadow(this.trimmedLabel, x + 6, y + 7, this.isValueModified() ? 0xFFC94A : 0xFFFFFF);
// Draw the value label
font.drawStringWithShadow(this.valueLabel, (x + (width - 2)) - (int)(this.valueSectionWidth * 0.5) - (int)(font.getStringWidth(this.valueLabel) * 0.5), y + 7, 0xFFFFFF);
font.drawStringWithShadow(this.valueLabel, (x + (width - 2)) - (int)(this.valueSectionWidth * 0.5) - (int)(font.getStringWidth(this.valueLabel) * 0.5), y + 7, getValueColor());
}
protected final void renderOptionWithValue(int x, int y, int width, int height, boolean hovered) {
@@ -119,17 +119,15 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
}
protected final String createTrimmedLabel() {
String label = GuiUtil.shortenText(Minecraft.getMinecraft().fontRenderer, this.label, this.maxLabelWidth);
if (this.isValueModified()) {
label = label + " (*)"; //.withStyle(style -> style.withColor(TextColor.fromRgb(0xffc94a)));
}
return label;
return GuiUtil.shortenText(Minecraft.getMinecraft().fontRenderer, this.label, this.maxLabelWidth);
}
protected abstract String createValueLabel();
protected int getValueColor() {
return 0xFFFFFF;
}
public abstract boolean applyNextValue();
public abstract boolean applyPreviousValue();
@@ -147,7 +145,13 @@ public abstract class BaseOptionElementWidget<T extends OptionMenuElement> exten
@Override
public Optional<String> getCommentBody() {
return Optional.ofNullable(getCommentKey()).map(I18n::format);
final String key = getCommentKey();
if (key == null) {
return Optional.empty();
}
final String translated = GuiUtil.translateLenient(key);
// Don't show comment boxes without a translation, otherwise you get something like option.{commentname}.comment
return translated.equals(key) ? Optional.empty() : Optional.of(translated);
}
@Override
@@ -70,6 +70,14 @@ public class BooleanElementWidget extends BaseOptionElementWidget<OptionMenuBool
return this.value ? TEXT_TRUE : TEXT_FALSE;
}
@Override
protected int getValueColor() {
if (this.value == this.defaultValue) {
return 0xFFFFFF;
}
return this.value ? 0x55FF55 : 0xFF5555;
}
@Override
public String getCommentKey() {
return "option." + this.option.getName() + ".comment";
@@ -6,13 +6,10 @@ import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.coderbot.iris.shaderpack.option.menu.OptionMenuLinkElement;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.resources.I18n;
import java.util.Optional;
public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElement> {
private static final String ARROW = new String(">");
private final String targetScreenId;
private final String label;
@@ -39,7 +36,7 @@ public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElem
final FontRenderer font = Minecraft.getMinecraft().fontRenderer;
final int maxLabelWidth = width - 9;
final int maxLabelWidth = width - 6;
if (font.getStringWidth(this.label) > maxLabelWidth) {
this.isLabelTrimmed = true;
@@ -51,8 +48,7 @@ public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElem
int labelWidth = font.getStringWidth(this.trimmedLabel);
font.drawStringWithShadow(this.trimmedLabel, x + (int)(width * 0.5) - (int)(labelWidth * 0.5) - (int)(0.5 * Math.max(labelWidth - (width - 18), 0)), y + 7, 0xFFFFFF);
font.drawString(ARROW, (x + width) - 9, y + 7, 0xFFFFFF);
font.drawStringWithShadow(this.trimmedLabel, x + (int)(width * 0.5) - (int)(labelWidth * 0.5), y + 7, 0xFFFFFF);
if (hovered && this.isLabelTrimmed) {
// To prevent other elements from being drawn on top of the tooltip
@@ -79,7 +75,7 @@ public class LinkElementWidget extends CommentedElementWidget<OptionMenuLinkElem
@Override
public Optional<String> getCommentBody() {
final String translation = "screen." + this.targetScreenId + ".comment";
final String translated = I18n.format(translation);
return Optional.ofNullable(!translated.equals(translation) ? I18n.format(translation) : null);
final String translated = GuiUtil.translateLenient(translation);
return Optional.ofNullable(!translated.equals(translation) ? translated : null);
}
}
@@ -1,6 +1,7 @@
package net.coderbot.iris.gui.element.widget;
import net.coderbot.iris.Iris;
import net.minecraft.util.EnumChatFormatting;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.NavigationController;
import net.coderbot.iris.gui.element.ShaderPackOptionList;
@@ -64,7 +65,7 @@ public final class OptionMenuConstructor {
static {
registerScreen(OptionMenuMainElementScreen.class, screen ->
new ElementWidgetScreenData(Iris.getCurrentPackName() + (Iris.isFallback() ? " (fallback)" : ""), false));
new ElementWidgetScreenData(EnumChatFormatting.BOLD + Iris.getCurrentPackName() + (Iris.isFallback() ? " (fallback)" : ""), false));
registerScreen(OptionMenuSubElementScreen.class, screen ->
new ElementWidgetScreenData(GuiUtil.translateOrDefault(new String(screen.screenId), "screen." + screen.screenId), true));
@@ -59,6 +59,11 @@ public class ProfileElementWidget extends BaseOptionElementWidget<OptionMenuProf
return this.profileLabel;
}
@Override
protected int getValueColor() {
return PROFILE_CUSTOM.equals(this.profileLabel) ? 0xFFFF55 : 0xFFFFFF;
}
@Override
public Optional<String> getCommentTitle() {
return Optional.of(PROFILE_LABEL);
@@ -108,7 +108,7 @@ public class SliderElementWidget extends StringElementWidget {
@Override
public boolean mouseReleased(double mx, double my, int button) {
if (button == 0) {
if (button == 0 && this.mouseDown) {
this.onReleased();
return true;
@@ -60,6 +60,11 @@ public class StringElementWidget extends BaseOptionElementWidget<OptionMenuStrin
return GuiUtil.translateOrDefault(getValue(), "value." + this.option.getName() + "." + getValue());
}
@Override
protected int getValueColor() {
return 0x6688FF;
}
@Override
public String getCommentKey() {
return "option." + this.option.getName() + ".comment";
@@ -2,6 +2,7 @@ package net.coderbot.iris.gui.screen;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.NavigationController;
@@ -16,8 +17,12 @@ import net.coderbot.iris.shaderpack.ShaderPack;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiConfirmOpenLink;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiYesNo;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
@@ -25,12 +30,19 @@ import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@@ -41,8 +53,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
*/
public static final Set<Runnable> TOP_LAYER_RENDER_QUEUE = new HashSet<>();
private static final String SELECT_TITLE = I18n.format("pack.iris.select.title");
private static final String CONFIGURE_TITLE = I18n.format("pack.iris.configure.title");
private static final String SELECT_TITLE = EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.select.title");
private static final String CONFIGURE_TITLE = EnumChatFormatting.GRAY.toString() + EnumChatFormatting.ITALIC + I18n.format("pack.iris.configure.title");
private static final int COMMENT_PANEL_WIDTH = 314;
private final GuiScreen parent;
@@ -72,7 +84,6 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
private boolean guiHidden = false;
private boolean dirty = false;
private float guiButtonHoverTimer = 0.0f;
public ShaderPackScreen(GuiScreen parent) {
this.title = I18n.format("options.iris.shaderPackSelection.title");
@@ -102,6 +113,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.initGui();
}
handleDroppedFiles();
if (this.mc.theWorld == null) {
super.drawDefaultBackground();
} else if (!this.guiHidden) {
@@ -122,11 +135,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
hoveredElementCommentTimer = 0;
}
final float previousHoverTimer = this.guiButtonHoverTimer;
super.drawScreen(mouseX, mouseY, delta);
if (previousHoverTimer == this.guiButtonHoverTimer) {
this.guiButtonHoverTimer = 0.0f;
}
if (!this.guiHidden) {
drawCenteredString(this.fontRendererObj, this.title, (int) (this.width * 0.5), 8, 0xFFFFFF);
@@ -143,12 +152,9 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
// Draw the comment panel
if (this.isDisplayingComment()) {
// Determine panel height and position
final int panelHeight = Math.max(50, 18 + (this.hoveredElementCommentBody.size() * 10));
int x = mouseX + 5;
if (x + 314 >= (this.width - 4)) x = this.width - (318);
int y = mouseY + 8;
if (y + panelHeight >= (this.height - 4)) y = this.height - (panelHeight + 4);
final int x = (int) (0.5 * this.width) - 157;
final int y = this.height - (panelHeight + 4);
// Draw panel
GuiUtil.drawPanel(x, y, COMMENT_PANEL_WIDTH, panelHeight);
// Draw text
@@ -181,10 +187,21 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
@Override
public void initGui() {
super.initGui();
BackendManager.RENDER_BACKEND.startFileDrop();
final int bottomCenter = this.width / 2 - 50;
final int topCenter = this.width / 2 - 76;
final boolean inWorld = this.mc.theWorld != null;
if (this.shaderPackList != null) {
try {
this.shaderPackList.close();
} catch (IOException e) {
Iris.logger.error("Failed to close previous shaderpack selection watcher!", e);
}
}
this.shaderPackList = new ShaderPackSelectionList(this, this.mc, this.width, this.height, 32, this.height - 58, 0, this.width);
if (Iris.getCurrentPack().isPresent() && this.navigation != null) {
@@ -339,12 +356,30 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
public void refreshScreenSwitchButton() {
if (this.screenSwitchButton != null) {
this.screenSwitchButton.displayString = optionMenuOpen ? I18n.format("options.iris.shaderPackList") : I18n.format("options.iris.shaderPackSettings");
this.screenSwitchButton.enabled = optionMenuOpen || shaderPackList.getTopButtonRow().shadersEnabled;
// Disable the settings switch when shaders are off or the selected pack exposes no options
this.screenSwitchButton.enabled = optionMenuOpen || (shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(true));
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) {
if (GuiScreen.isCtrlKeyDown() && keyCode == Keyboard.KEY_D) {
this.mc.displayGuiScreen(new GuiYesNo((result, id) -> {
Iris.setDebug(result);
this.mc.displayGuiScreen(this);
}, "Shader debug mode toggle",
"Debug mode helps investigate problems and shows shader errors. Would you like to enable it?", 0));
return;
}
if (GuiScreen.isCtrlKeyDown() && keyCode == Keyboard.KEY_G) {
this.mc.displayGuiScreen(new GuiYesNo((result, id) -> {
Iris.setAllowUnknownShaders(result);
this.mc.displayGuiScreen(this);
}, "Unknown shader toggle",
"This allows unknown shaders to load in.", 0));
return;
}
if (keyCode == Keyboard.KEY_ESCAPE) {
if (this.guiHidden) {
this.guiHidden = false;
@@ -359,6 +394,21 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
return;
}
}
if (keyCode == Keyboard.KEY_F1 && this.mc.theWorld != null) {
this.guiHidden = !this.guiHidden;
this.initGui();
return;
}
if (keyCode == Keyboard.KEY_TAB) {
final boolean canOpenOptions = shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(false);
if (this.optionMenuOpen || canOpenOptions) {
this.optionMenuOpen = !this.optionMenuOpen;
this.applyChanges();
this.initGui();
}
return;
}
super.keyTyped(typedChar, keyCode);
}
@@ -367,6 +417,84 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.notificationDialogTimer = 100;
}
private void handleDroppedFiles() {
final List<String> dropped = BackendManager.RENDER_BACKEND.pollDroppedFiles();
if (dropped.isEmpty()) {
return;
}
final List<Path> paths = dropped.stream().map(Paths::get).collect(Collectors.toList());
if (this.optionMenuOpen) {
onOptionMenuFilesDrop(paths);
} else {
onPackListFilesDrop(paths);
}
}
public void onPackListFilesDrop(List<Path> paths) {
final List<Path> packs = paths.stream().filter(Iris::isValidShaderpack).toList();
for (Path pack : packs) {
final String fileName = pack.getFileName().toString();
try {
Iris.getShaderpacksDirectoryManager().copyPackIntoDirectory(fileName, pack);
} catch (FileAlreadyExistsException e) {
displayNotification(I18n.format("options.iris.shaderPackSelection.copyErrorAlreadyExists", fileName));
this.shaderPackList.refresh();
return;
} catch (IOException e) {
Iris.logger.warn("Error copying dragged shader pack", e);
displayNotification(I18n.format("options.iris.shaderPackSelection.copyError", fileName));
this.shaderPackList.refresh();
return;
}
}
// After copying, refresh the list so the new packs show up
this.shaderPackList.refresh();
if (packs.isEmpty()) {
if (paths.size() == 1) {
displayNotification(I18n.format("options.iris.shaderPackSelection.failedAddSingle", paths.getFirst().getFileName().toString()));
} else {
displayNotification(I18n.format("options.iris.shaderPackSelection.failedAdd"));
}
} else if (packs.size() == 1) {
final String packName = packs.getFirst().getFileName().toString();
displayNotification(I18n.format("options.iris.shaderPackSelection.addedPack", packName));
// Select the freshly-added pack, since the user probably wants to use it
this.shaderPackList.select(packName);
} else {
displayNotification(I18n.format("options.iris.shaderPackSelection.addedPacks", packs.size()));
}
}
public void onOptionMenuFilesDrop(List<Path> paths) {
// Only one settings file should be imported at a time
if (paths.size() != 1) {
displayNotification(I18n.format("options.iris.shaderPackOptions.tooManyFiles"));
return;
}
importPackOptions(paths.getFirst());
}
public void importPackOptions(Path settingFile) {
try (InputStream in = Files.newInputStream(settingFile)) {
final Properties properties = new Properties();
properties.load(in);
Iris.queueShaderPackOptionsFromProperties(properties);
displayNotification(I18n.format("options.iris.shaderPackOptions.importedSettings", settingFile.getFileName().toString()));
if (this.navigation != null) {
this.navigation.refresh();
}
} catch (Exception e) {
Iris.logger.error("Error importing shader settings file \"" + settingFile + "\"", e);
displayNotification(I18n.format("options.iris.shaderPackOptions.failedImport", settingFile.getFileName().toString()));
}
}
public void onClose() {
if (!dropChanges) {
applyChanges();
@@ -377,6 +505,19 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.mc.displayGuiScreen(parent);
}
@Override
public void onGuiClosed() {
BackendManager.RENDER_BACKEND.stopFileDrop();
if (this.shaderPackList != null) {
try {
this.shaderPackList.close();
} catch (IOException e) {
Iris.logger.error("Failed to safely close shaderpack selection!", e);
}
}
}
private void dropChangesAndClose() {
dropChanges = true;
onClose();
@@ -419,8 +560,29 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
CompletableFuture.runAsync(() -> openUri(Iris.getShaderpacksDirectoryManager().getDirectoryUri()));
}
public void openLinkConfirm(String url) {
this.mc.displayGuiScreen(new GuiConfirmOpenLink((result, id) -> {
if (result) {
try {
openUri(new URI(url));
} catch (URISyntaxException e) {
Iris.logger.error("Invalid shader download URL: " + url, e);
}
}
this.mc.displayGuiScreen(this);
}, url, 0, true) {
@Override
public void initGui() {
super.initGui();
for (int i = 0; i < this.buttonList.size(); i++) {
this.buttonList.get(i).xPosition = this.width / 2 - 155 + i * 105;
}
}
});
}
private void openUri(URI uri) {
switch (net.minecraft.util.Util.getOSType()) {
switch (Util.getOSType()) {
case OSX -> {
try {
Runtime.getRuntime().exec(new String[] { "/usr/bin/open", uri.toString() });
@@ -452,7 +614,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
try {
final Class<?> aClass = Class.forName("java.awt.Desktop");
final Object getDesktop = aClass.getMethod("getDesktop").invoke((Object) null);
final Object getDesktop = aClass.getMethod("getDesktop").invoke(null);
aClass.getMethod("browse", URI.class).invoke(getDesktop, uri);
} catch (Exception e) {
e.printStackTrace();
@@ -475,17 +637,17 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
this.hoveredElementCommentTitle = ((CommentedElementWidget<?>) widget).getCommentTitle();
Optional<String> commentBody = ((CommentedElementWidget<?>) widget).getCommentBody();
if (!commentBody.isPresent()) {
if (commentBody.isEmpty()) {
this.hoveredElementCommentBody.clear();
} else {
String rawCommentBody = commentBody.get();
// Strip any trailing "."s
// Strip any trailing periods
if (rawCommentBody.endsWith(".")) {
rawCommentBody = rawCommentBody.substring(0, rawCommentBody.length() - 1);
}
// Split comment body into lines by separator ". "
List<String> splitByPeriods = Arrays.stream(rawCommentBody.split("\\. [ ]*")).map(String::new).collect(Collectors.toList());
List<String> splitByPeriods = Arrays.stream(rawCommentBody.split("\\. +")).toList();
// Line wrap
this.hoveredElementCommentBody = new ArrayList<>();
for (String text : splitByPeriods) {
@@ -10,6 +10,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -33,7 +34,7 @@ public class LanguageMap {
stream.filter(path -> !Files.isDirectory(path)).forEach(path -> {
// Also note that OptiFine uses a property scheme for loading language entries to keep parity with other
// OptiFine features
final String currentFileName = path.getFileName().toString();
final String currentFileName = path.getFileName().toString().toLowerCase(Locale.ROOT);
if (!currentFileName.endsWith(".lang")) {
// This file lacks a .lang file extension and should be ignored.
@@ -214,6 +214,8 @@ options.angelica.threadedChunkBuilding.tooltip=If enabled, chunk meshing will be
pack.iris.select.title=Select
pack.iris.configure.title=Configure
pack.iris.list.label=+ Drag and Drop Shader Packs to add
pack.iris.list.label.lwjgl2=Install lwjgl3ify to drag and drop Shader Packs
label.iris.true=On
label.iris.false=Off
@@ -1,19 +1,9 @@
#version 330 core
uniform mat4 u_MVP;
uniform vec3 u_Min;
uniform vec3 u_Max;
layout(location = 0) in vec3 a_Position;
const vec3 edges[24] = vec3[24](
vec3(0,0,0), vec3(1,0,0), vec3(1,0,0), vec3(1,0,1),
vec3(1,0,1), vec3(0,0,1), vec3(0,0,1), vec3(0,0,0),
vec3(0,1,0), vec3(1,1,0), vec3(1,1,0), vec3(1,1,1),
vec3(1,1,1), vec3(0,1,1), vec3(0,1,1), vec3(0,1,0),
vec3(0,0,0), vec3(0,1,0), vec3(1,0,0), vec3(1,1,0),
vec3(1,0,1), vec3(1,1,1), vec3(0,0,1), vec3(0,1,1)
);
uniform mat4 u_MVP;
void main() {
vec3 pos = mix(u_Min, u_Max, edges[gl_VertexID]);
gl_Position = u_MVP * vec4(pos, 1.0);
gl_Position = u_MVP * vec4(a_Position, 1.0);
}
@@ -78,7 +78,9 @@ public class MixinLocale {
languageCodes.clear();
// Reverse order due to how minecraft has English and then the primary language in the language definitions list
new LinkedList<>(definitions).descendingIterator().forEachRemaining(languageCodes::add);
new LinkedList<>(definitions).descendingIterator()
// FQN here because due to clashing
.forEachRemaining(code -> languageCodes.add(code.toLowerCase(java.util.Locale.ROOT)));
}
@Inject(method = "checkUnicode", at = @At("HEAD"), cancellable = true)
@@ -2,11 +2,14 @@ package com.gtnewhorizons.angelica.mixins.early.shaders;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.layer.GbufferPrograms;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.tileentity.TileEntityBeaconRenderer;
import net.minecraft.tileentity.TileEntityBeacon;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TileEntityBeaconRenderer.class)
@@ -22,5 +25,30 @@ public class MixinTileEntityBeaconRenderer {
GbufferPrograms.teardownSpecialRenderCondition();
}
/**
* Whoever wrote this part of vanilla code sucks. Just change the alpha my guy.
*/
@Redirect(
method = "Lnet/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer;renderTileEntityAt(Lnet/minecraft/tileentity/TileEntityBeacon;DDDF)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;setColorRGBA(IIII)V", ordinal = 0))
private void angelica$innerBeamColor(Tessellator tessellator, int r, int g, int b, int a) {
angelica$beamColor(tessellator, r, g, b, 255);
}
@Redirect(
method = "Lnet/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer;renderTileEntityAt(Lnet/minecraft/tileentity/TileEntityBeacon;DDDF)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;setColorRGBA(IIII)V", ordinal = 1))
private void angelica$outerBeamColor(Tessellator tessellator, int r, int g, int b, int a) {
angelica$beamColor(tessellator, r, g, b, a);
}
/**
* Make beam fullbright.
*/
@Unique
private void angelica$beamColor(Tessellator tessellator, int r, int g, int b, int a) {
tessellator.setColorRGBA(r, g, b, a);
tessellator.setBrightness(0x00F000F0);
}
}
@@ -0,0 +1,52 @@
package com.gtnewhorizons.angelica.mixins.late.chisel;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.layer.GbufferPrograms;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Chisel ships its own beacon beam renderer, we yoink a lot of the code from
* {@link com.gtnewhorizons.angelica.mixins.early.shaders.MixinTileEntityBeaconRenderer} to apply the same fixes here.
*/
@Pseudo
@Mixin(targets = { "team.chisel.client.render.tile.RenderCarvableBeacon" }, remap = false)
public class MixinRenderCarvableBeacon {
@Inject(method = "renderBeam(FLnet/minecraft/world/World;DDDIF)V", at = @At("HEAD"))
private void angelica$beginBeaconBeam(float f1, World world, double x, double y, double z, int meta, float partialTicks, CallbackInfo ci) {
GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.BEACON_BEAM);
}
@Inject(method = "renderBeam(FLnet/minecraft/world/World;DDDIF)V", at = @At("RETURN"))
private void angelica$endBeaconBeam(float f1, World world, double x, double y, double z, int meta, float partialTicks, CallbackInfo ci) {
GbufferPrograms.teardownSpecialRenderCondition();
}
@Redirect(
method = "renderBeam(FLnet/minecraft/world/World;DDDIF)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;setColorRGBA(IIII)V", ordinal = 0, remap = true))
private void angelica$innerBeamColor(Tessellator tessellator, int r, int g, int b, int a) {
angelica$beamColor(tessellator, r, g, b, 255);
}
@Redirect(
method = "renderBeam(FLnet/minecraft/world/World;DDDIF)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;setColorRGBA(IIII)V", ordinal = 1, remap = true))
private void angelica$outerBeamColor(Tessellator tessellator, int r, int g, int b, int a) {
angelica$beamColor(tessellator, r, g, b, a);
}
@Unique
private void angelica$beamColor(Tessellator tessellator, int r, int g, int b, int a) {
tessellator.setColorRGBA(r, g, b, a);
tessellator.setBrightness(0x00F000F0);
}
}