Compare commits

..
11 Commits
Author SHA1 Message Date
0e1fd9d0e9 add auto emissive overlay for any block (#1710)
Co-authored-by: VortexSo4 <[email protected]>
Co-authored-by: ah-OOG-ah <[email protected]>
2026-05-10 02:12:08 -04:00
Ruling-0 58938e82a7 Compact CTM Fixes (#1726) 2026-05-09 11:42:42 -07:00
Branan Riley be6c8c85b5 Detect vanilla AO at a higher level (#1733) 2026-05-09 11:41:59 -07:00
ABKQPOandCaedis 39bca37d0d Adjust IDynamicLightProducer to allow passing an ItemStack parameter (#1734)
Co-authored-by: Caedis <[email protected]>
2026-05-08 20:31:22 -07:00
czqwq 1c61be04a7 Fix Already tesselating logs (#1729) 2026-05-08 20:30:08 -07:00
7a97cd9386 Add RGB color system (#1669)
Co-authored-by: Death Fuel <[email protected]>
Co-authored-by: Jason Mitchell <[email protected]>
2026-05-08 18:36:51 -07:00
Kotmatross 5b09bde7a0 [NTM:Space] Fix mixins in obfuscated environment (#1738) 2026-05-08 18:24:29 +02:00
Ruling-0 edc0081b7c Fix CTM inventory texture bleed (#1721) 2026-05-04 12:25:51 -07:00
Alexdoru 913256b032 fix world client leak in CTMUtils (#1724) 2026-05-04 12:24:25 -07:00
sisyphussy e1f5083d3e Fix intel detection (#1722) 2026-05-04 12:23:50 -07:00
sisyphussy b55e693ad2 Remove DSA from GLSM & other stuff (#1723) 2026-05-04 12:23:38 -07:00
31 changed files with 925 additions and 317 deletions

No files matched your search

+1 -1
View File
@@ -76,7 +76,7 @@ dependencies {
// Iris Shaders
compileOnly('org.jetbrains:annotations:26.0.2')
api("com.github.GTNewHorizons:GTNHLib:0.9.36:dev")
api("com.github.GTNewHorizons:GTNHLib:0.9.52:dev")
shadowImplementation(project(":glsm")) { transitive = false }
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
@@ -520,6 +520,12 @@ public class GLStateManager {
// After Display.create(), viewport is (0, 0, width, height)
viewportState.setViewPort(0, 0, displayWidth, displayHeight);
}
final String glVendor = RENDER_BACKEND.getString(GL11.GL_VENDOR);
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
final String os = System.getProperty("os.name").toLowerCase();
windows = os.contains("win");
}
static void init(Runnable initCallback) {
@@ -527,13 +533,6 @@ public class GLStateManager {
DEFAULT_DRAW_BUFFER = RENDER_BACKEND.getInteger(GL11.GL_DRAW_BUFFER);
final String glVendor = RENDER_BACKEND.getString(GL11.GL_VENDOR);
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
final String os = System.getProperty("os.name").toLowerCase();
windows = os.contains("win");
// The initial mask value should be defined as all 1's. However, some drivers have it set to 0's.
// To ensure consistency & correctness across all drivers, we're setting them to 0xFF.
RENDER_BACKEND.stencilFunc(stencilState.getFuncFront(), stencilState.getRefFront(), 0xFF);
@@ -627,16 +626,6 @@ public class GLStateManager {
return BYPASS_CACHE || !isCachingEnabled();
}
/**
* Returns true if we should use DSA for texture operations. DSA relies on cached texture bindings, which aren't tracked when caching is disabled. Also,
* proxy textures are special query textures that shouldn't use DSA.
*/
private static boolean shouldUseDSA(int target) {
if (!isCachingEnabled()) return false;
// Proxy textures are for capability queries, not real textures
return target != GL11.GL_PROXY_TEXTURE_1D && target != GL11.GL_PROXY_TEXTURE_2D && target != GL12.GL_PROXY_TEXTURE_3D;
}
// LWJGL Overrides
public static void glEnable(int cap) {
final RecordMode mode = DisplayListManager.getRecordMode();
@@ -1834,13 +1823,7 @@ public class GLStateManager {
}
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
// Use DSA to upload directly to the texture
RenderSystem.textureImage2D(getBoundTextureForServerState(), target, level, internalformat, width, height, border, format, type, pixels);
} else {
// Non-main thread or proxy texture - use direct GL call
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -1885,11 +1868,7 @@ public class GLStateManager {
}
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels != null ? pixels.asIntBuffer() : null);
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
RenderSystem.textureImage2D(getBoundTextureForServerState(), target, level, internalformat, width, height, border, format, type, pixels);
} else {
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -4413,6 +4392,7 @@ public class GLStateManager {
public static int glGetError() {
if (!RENDER_BACKEND.hasContext()) return 0;
if (initConfig.noErrorChecks()) return 0;
return RENDER_BACKEND.getError();
}
@@ -4467,13 +4447,7 @@ public class GLStateManager {
}
}
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
// Use DSA to upload directly to the texture - keeps GL binding state unchanged
RenderSystem.textureSubImage2D(getBoundTextureForServerState(), target, level, xoffset, yoffset, width, height, format, type, pixels);
} else {
// Non-main thread or proxy texture - use direct GL call
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
}
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -4486,13 +4460,7 @@ public class GLStateManager {
}
}
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
// Use DSA to upload directly to the texture
RenderSystem.textureSubImage2D(getBoundTextureForServerState(), target, level, xoffset, yoffset, width, height, format, type, pixels);
} else {
// Non-main thread or proxy texture - use direct GL call
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
}
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -5212,7 +5180,7 @@ public class GLStateManager {
}
public static boolean isFramebufferEnabled() {
return initConfig != null && initConfig.isFramebufferSupported() && initConfig.isFboEnabled();
return true;
}
private static final boolean FFP_WARN_ON_UNSUPPORTED = Boolean.parseBoolean(System.getProperty("angelica.ffp.warnOnUnsupported", "false"));
@@ -8,20 +8,18 @@ import java.util.function.Consumer;
public final class GLSMInitConfig {
private final boolean lwjglDebug;
private final StreamingUploader.UploadStrategy streamingUploadStrategy;
private final boolean framebufferSupported;
private final boolean fboEnabled;
private final Consumer<DirectTessellator> directDrawer;
private final Runnable streamingDrawerDestroy;
private final int displayWidth;
private final int displayHeight;
private final Runnable postInitCallback;
private final boolean enableDSA;
private final boolean noErrorChecks;
private GLSMInitConfig(Builder builder) {
this.lwjglDebug = builder.lwjglDebug;
this.streamingUploadStrategy = builder.streamingUploadStrategy;
this.framebufferSupported = builder.framebufferSupported;
this.fboEnabled = builder.fboEnabled;
this.noErrorChecks = builder.noErrorChecks;
this.directDrawer = builder.directDrawer;
this.streamingDrawerDestroy = builder.streamingDrawerDestroy;
this.displayWidth = builder.displayWidth;
@@ -36,26 +34,24 @@ public final class GLSMInitConfig {
public boolean isLwjglDebug() { return lwjglDebug; }
public StreamingUploader.UploadStrategy getStreamingUploadStrategy() { return streamingUploadStrategy; }
public boolean isFramebufferSupported() { return framebufferSupported; }
public boolean isFboEnabled() { return fboEnabled; }
public Consumer<DirectTessellator> getDirectDrawer() { return directDrawer; }
public Runnable getStreamingDrawerDestroy() { return streamingDrawerDestroy; }
public int getDisplayWidth() { return displayWidth; }
public int getDisplayHeight() { return displayHeight; }
public Runnable getPostInitCallback() { return postInitCallback; }
public boolean isDSAEnabled() { return enableDSA; }
public boolean noErrorChecks() { return noErrorChecks; }
public static final class Builder {
private boolean lwjglDebug = false;
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
private boolean framebufferSupported = false;
private boolean fboEnabled = false;
private Consumer<DirectTessellator> directDrawer = null;
private Runnable streamingDrawerDestroy = null;
private int displayWidth = 0;
private int displayHeight = 0;
private Runnable postInitCallback = null;
private boolean enableDSA = false;
private boolean noErrorChecks;
private Builder() {}
@@ -69,13 +65,8 @@ public final class GLSMInitConfig {
return this;
}
public Builder framebufferSupported(boolean framebufferSupported) {
this.framebufferSupported = framebufferSupported;
return this;
}
public Builder fboEnabled(boolean fboEnabled) {
this.fboEnabled = fboEnabled;
public Builder noErrorChecks(boolean noErrorChecks) {
this.noErrorChecks = noErrorChecks;
return this;
}
@@ -1,8 +1,10 @@
package com.gtnewhorizons.angelica.api;
import net.minecraft.item.ItemStack;
/**
* To be used for modded items that don't have blocks. Baubles for example.
*/
public interface IDynamicLightProducer {
int getLuminance();
int getLuminance(ItemStack stack);
}
@@ -4,7 +4,14 @@ import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
import com.gtnewhorizon.gtnhlib.util.font.GlyphReplacements;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_PAYLOAD;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_PAYLOAD;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
@@ -213,12 +220,14 @@ public class BatchingFontRenderer {
pushQuadIdx();
}
private void pushTexRect(float x, float y, float w, float h, float itOff, int rgba, float uStart, float vStart, float uSz, float vSz) {
private void pushTexRect(float x, float y, float w, float h, float itOff, int rgba, float uStart, float vStart, float uSz, float vSz, boolean flipV) {
ensureCapacity();
pushVtx(x + itOff, y, rgba, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff, y + h, rgba, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x + itOff + w, y, rgba, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff + w, y + h, rgba, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
float vTop = flipV ? vStart + vSz : vStart;
float vBot = flipV ? vStart : vStart + vSz;
pushVtx(x + itOff, y, rgba, uStart, vTop, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff, y + h, rgba, uStart, vBot, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x + itOff + w, y, rgba, uStart + uSz, vTop, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff + w, y + h, rgba, uStart + uSz, vBot, uStart, uStart + uSz, vStart, vStart + vSz);
pushQuadIdx();
}
@@ -442,6 +451,55 @@ public class BatchingFontRenderer {
return (what >= fromInclusive) && (what <= toInclusive);
}
/**
* Count visible characters from {@code start} to {@code end}, skipping format codes.
* Stops at §r, any color code (§0-f, §x), or any color effect (§q, §g).
* Style codes (§k-o) and non-terminating effects (§z wave, §v dinnerbone) are skipped.
*/
private static int countVisibleChars(CharSequence str, int start, int end) {
int count = 0;
for (int i = start; i < end; i++) {
char ch = str.charAt(i);
if (ch == FORMATTING_CHAR && i + 1 < end) {
char code = Character.toLowerCase(str.charAt(i + 1));
if (code == 'r' || (code >= '0' && code <= '9') || (code >= 'a' && code <= 'f')
|| code == 'x' || code == 'q' || code == 'g') {
break;
}
i++;
} else {
count++;
}
}
return Math.max(count, 1);
}
private static int hsvToRgb(float hue, float sat, float val) {
int h = (int)(hue / 60f) % 6;
float f = hue / 60f - h;
float p = val * (1 - sat);
float q = val * (1 - f * sat);
float t = val * (1 - (1 - f) * sat);
float r, g, b;
switch (h) {
case 0: r=val; g=t; b=p; break;
case 1: r=q; g=val; b=p; break;
case 2: r=p; g=val; b=t; break;
case 3: r=p; g=q; b=val; break;
case 4: r=t; g=p; b=val; break;
default: r=val; g=p; b=q; break;
}
return ((int)(r*255) << 16) | ((int)(g*255) << 8) | (int)(b*255);
}
private static final int RAINBOW_LUT_SIZE = 24;
private static final int[] RAINBOW_LUT = new int[RAINBOW_LUT_SIZE];
static {
for (int i = 0; i < RAINBOW_LUT_SIZE; i++) {
RAINBOW_LUT[i] = hsvToRgb(i * 15f, 1f, 1f);
}
}
public boolean forceDefaults() {
return this.bookMode || this.isSGA || this.isSplash;
}
@@ -466,7 +524,8 @@ public class BatchingFontRenderer {
return forceDefaults() ? 1 : FontConfig.fontShadowOffset;
}
private static final char FORMATTING_CHAR = 167; // §
private static final double WAVE_TIME_SCALE = 5e-9;
private static final float WAVE_FREQUENCY = 0.5f;
public float drawString(final float anchorX, final float anchorY, final int color, final boolean enableShadow,
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
@@ -497,6 +556,15 @@ public class BatchingFontRenderer {
boolean curBold = false;
boolean curStrikethrough = false;
boolean curUnderline = false;
boolean curRainbow = false;
boolean curWave = false;
boolean curDinnerbone = false;
boolean curGradient = false;
int gradientStartRgb = 0, gradientEndRgb = 0;
int gradientCharIndex = 0, gradientTotalChars = 0;
float gradientStep = 0f;
int rainbowCharIndex = 0;
int visibleCharIndex = 0;
float glyphScaleY = getGlyphScaleY();
float glyphScaleX = getGlyphScaleX();
@@ -534,6 +602,16 @@ public class BatchingFontRenderer {
strikethroughStartX = strikethroughEndX;
}
if (fmtCode == 'x' && AngelicaConfig.enableRGBColors && charIdx + SECTION_X_PAYLOAD < stringEnd) {
int rgb = ColorCodeUtils.parseHexPairs(string, charIdx + 1, 6);
if (rgb != -1) {
curRainbow = false;
curGradient = false;
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
curShadowColor = (curShadowColor & 0xFF000000) | ((rgb & 0xFCFCFC) >> 2);
charIdx += SECTION_X_PAYLOAD;
}
} else {
final boolean is09 = charInRange(fmtCode, '0', '9');
final boolean isAF = charInRange(fmtCode, 'a', 'f');
if (is09 || isAF) {
@@ -542,6 +620,9 @@ public class BatchingFontRenderer {
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curGradient = false;
// wave/dinnerbone NOT reset — they're positional effects, independent of color
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
final int rgb = this.colorCode[colorIdx];
@@ -562,15 +643,41 @@ public class BatchingFontRenderer {
underlineEndX = underlineStartX;
} else if (fmtCode == 'o') {
curItalic = true;
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
curRainbow = true;
curGradient = false;
rainbowCharIndex = 0;
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
curWave = !curWave;
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
curDinnerbone = !curDinnerbone;
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
if (color1 != -1 && color2 != -1) {
curGradient = true;
curRainbow = false;
gradientStartRgb = color1;
gradientEndRgb = color2;
gradientCharIndex = 0;
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
charIdx += GRADIENT_PAYLOAD;
}
} else if (fmtCode == 'r') {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curWave = false;
curDinnerbone = false;
curGradient = false;
curColor = color;
curShadowColor = shadowColor;
}
} // close else block for non-§x codes
continue;
}
@@ -597,12 +704,31 @@ public class BatchingFontRenderer {
heightNorth = anchorY + (underlying.FONT_HEIGHT - 1.0f) * (0.5f - glyphScaleY * fontProvider.getYScaleMultiplier() / 2);
float heightSouth = (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY * fontProvider.getYScaleMultiplier();
visibleCharIndex++;
// Check ASCII space, NBSP, NNBSP
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
curX += 4 * this.getWhitespaceScale() + (curBold ? 1 : 0);
continue;
}
if (curRainbow) {
int rgbEffect = RAINBOW_LUT[rainbowCharIndex % RAINBOW_LUT_SIZE];
curColor = (curColor & 0xFF000000) | rgbEffect;
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
rainbowCharIndex++;
}
if (curGradient && gradientTotalChars > 0) {
float t = Math.min(gradientCharIndex * gradientStep, 1f);
int gr = (int)((gradientStartRgb >> 16 & 0xFF) * (1-t) + (gradientEndRgb >> 16 & 0xFF) * t);
int gg = (int)((gradientStartRgb >> 8 & 0xFF) * (1-t) + (gradientEndRgb >> 8 & 0xFF) * t);
int gb = (int)((gradientStartRgb & 0xFF) * (1-t) + (gradientEndRgb & 0xFF) * t);
int rgbEffect = (gr << 16) | (gg << 8) | gb;
curColor = (curColor & 0xFF000000) | rgbEffect;
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
gradientCharIndex++;
}
final float uStart = fontProvider.getUStart(chr);
final float vStart = fontProvider.getVStart(chr);
final float xAdvance = fontProvider.getXAdvance(chr) * glyphScaleX;
@@ -616,23 +742,30 @@ public class BatchingFontRenderer {
final ResourceLocation texture = fontProvider.getTexture(chr);
final int idxId = idxWriterIndex;
// Wave: Y offset via sine wave
float renderY = heightNorth;
if (curWave) {
float time = HUDCaching.renderingCacheOverride ? 0f : (float)((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
renderY += (float) Math.sin(visibleCharIndex * WAVE_FREQUENCY + time) * AngelicaConfig.waveAmplitude;
}
if (enableShadow) {
for (int n = 1; n <= shadowCopies; n++) {
final float shadowOffsetPart = shadowOffset * ((float) n / shadowCopies);
pushTexRect(curX + shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
pushTexRect(curX + shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
if (curBold) {
pushTexRect(curX + 2.0f * shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
pushTexRect(curX + 2.0f * shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
}
}
}
pushTexRect(curX, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
pushTexRect(curX, renderY, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz, curDinnerbone);
if (curBold) {
for (int n = 1; n <= boldCopies; n++) {
final float shadowOffsetPart = shadowOffset * ((float) n / boldCopies);
pushTexRect(curX + shadowOffsetPart, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
pushTexRect(curX + shadowOffsetPart, renderY, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz, curDinnerbone);
}
}
@@ -653,6 +786,19 @@ public class BatchingFontRenderer {
if (bookMode) { curX = (int) curX; }
underlineEndX = curX;
strikethroughEndX = curX;
if ((curRainbow || curGradient) && curUnderline && underlineStartX != underlineEndX) {
final int ulIdx = idxWriterIndex;
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
pushDrawCmd(ulIdx, 6, null, false);
underlineStartX = underlineEndX;
}
if ((curRainbow || curGradient) && curStrikethrough && strikethroughStartX != strikethroughEndX) {
final int stIdx = idxWriterIndex;
pushUntexRect(strikethroughStartX, strikethroughY, strikethroughEndX - strikethroughStartX, glyphScaleY, curColor);
pushDrawCmd(stIdx, 6, null, false);
strikethroughStartX = strikethroughEndX;
}
}
if (curUnderline && underlineStartX != underlineEndX) {
@@ -0,0 +1,144 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
/**
* Utility for converting {@code &}-based shorthand into {@code §}-based
* format codes understood by the batched font renderer.
* <p>
* {@code &#RRGGBB} -> {@code §x§R§R§G§G§B§B} (RGB color)<br>
* {@code &g&#RRGGBB&#RRGGBB} -> {@code §g§x§R§R§G§G§B§B§x§R§R§G§G§B§B} (gradient)<br>
* {@code &c}, {@code &l}, {@code &r}, {@code &q}, etc. -> {@code §c}, {@code §l}, {@code §r}, {@code §q}
* <p>
* Effect codes use low-collision letters to avoid false positives on natural text:
* rainbow = {@code &q}, wave = {@code &z}, dinnerbone = {@code &v}, gradient = {@code &g} (only with &#-prefixed colors).
*/
public final class ColorCodeUtils {
private ColorCodeUtils() {}
public static final char FORMATTING_CHAR = '§';
public static final int SECTION_X_PAYLOAD = 12;
public static final int SECTION_X_LENGTH = 14;
public static final int GRADIENT_PAYLOAD = 28;
public static final int GRADIENT_LENGTH = 30;
/** Valid single {@code &} codes. {@code g} is excluded; {@code &g} only converts as part of a gradient. */
public static final String VALID_SINGLE_CODES = "0123456789abcdefklmnorqzv";
private static final String[] SECTION_PREFIX = new String[128];
static {
for (int i = 0; i < VALID_SINGLE_CODES.length(); i++) {
char c = VALID_SINGLE_CODES.charAt(i);
SECTION_PREFIX[c] = String.valueOf(FORMATTING_CHAR) + c;
}
SECTION_PREFIX['x'] = String.valueOf(FORMATTING_CHAR) + 'x';
SECTION_PREFIX['g'] = String.valueOf(FORMATTING_CHAR) + 'g';
}
public static String sectionPrefix(char code) {
char c = (code < 128) ? Character.toLowerCase(code) : code;
return (c < 128) ? SECTION_PREFIX[c] : null;
}
public static int parseHexPairs(CharSequence str, int start, int count) {
int result = 0;
for (int i = 0; i < count; i++) {
int p = start + i * 2;
if (str.charAt(p) != FORMATTING_CHAR) return -1;
int digit = Character.digit(str.charAt(p + 1), 16);
if (digit == -1) return -1;
result = (result << 4) | digit;
}
return result;
}
public static int parseSectionXAt(CharSequence str, int start) {
if (start + SECTION_X_LENGTH > str.length()) return -1;
if (str.charAt(start) != FORMATTING_CHAR) return -1;
if (Character.toLowerCase(str.charAt(start + 1)) != 'x') return -1;
return parseHexPairs(str, start + 2, 6);
}
public static boolean isValidSectionX(CharSequence str, int start) {
return parseSectionXAt(str, start) != -1;
}
private static String lastConversionInput;
private static String lastConversionOutput;
public static String convertAmpersandToSectionX(String text) {
if (text == null || !AngelicaConfig.enableAmpersandConversion) return text;
if (text == lastConversionInput) return lastConversionOutput;
final String out = convertImpl(text);
lastConversionInput = text;
lastConversionOutput = out;
return out;
}
private static String convertImpl(String text) {
int idx = text.indexOf('&');
if (idx == -1) return text;
final int len = text.length();
StringBuilder sb = null;
int last = 0;
while (idx != -1 && idx + 1 < len) {
if (text.charAt(idx + 1) == '#' && idx + 7 < len) {
boolean validHex = true;
for (int i = 2; i <= 7; i++) {
if (Character.digit(text.charAt(idx + i), 16) == -1) {
validHex = false;
break;
}
}
if (validHex) {
if (sb == null) sb = new StringBuilder(len + 16);
sb.append(text, last, idx);
sb.append(FORMATTING_CHAR).append('x');
for (int i = 2; i <= 7; i++) {
sb.append(FORMATTING_CHAR).append(text.charAt(idx + i));
}
last = idx + 8;
idx = text.indexOf('&', last);
continue;
}
}
if (Character.toLowerCase(text.charAt(idx + 1)) == 'g' && idx + 17 < len
&& text.charAt(idx + 2) == '&' && text.charAt(idx + 3) == '#'
&& text.charAt(idx + 10) == '&' && text.charAt(idx + 11) == '#') {
boolean valid1 = true, valid2 = true;
for (int i = 4; i <= 9; i++) {
if (Character.digit(text.charAt(idx + i), 16) == -1) { valid1 = false; break; }
}
if (valid1) {
for (int i = 12; i <= 17; i++) {
if (Character.digit(text.charAt(idx + i), 16) == -1) { valid2 = false; break; }
}
}
if (valid1 && valid2) {
if (sb == null) sb = new StringBuilder(len + 16);
sb.append(text, last, idx);
sb.append(FORMATTING_CHAR).append('g');
last = idx + 2;
idx = text.indexOf('&', last);
continue;
}
}
char code = Character.toLowerCase(text.charAt(idx + 1));
if (VALID_SINGLE_CODES.indexOf(code) != -1) {
if (sb == null) sb = new StringBuilder(len + 16);
sb.append(text, last, idx);
sb.append(FORMATTING_CHAR).append(text.charAt(idx + 1));
last = idx + 2;
}
idx = text.indexOf('&', idx + 1);
}
if (sb == null) return text;
sb.append(text, last, len);
return sb.toString();
}
}
@@ -0,0 +1,39 @@
package com.gtnewhorizons.angelica.compat;
import java.util.function.Function;
import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
public class GTNHLibCompat {
public static final boolean HAS_TEXT_PREPROCESSOR;
static {
boolean found = false;
try {
FontRendering.class.getMethod("setTextPreprocessor", Function.class);
found = true;
} catch (NoSuchMethodException ignored) {}
HAS_TEXT_PREPROCESSOR = found;
}
/** Only call when {@link #HAS_TEXT_PREPROCESSOR} is true. */
public static void registerPreprocessor() {
FontRendering.setTextPreprocessor(new FontRendering.TextPreprocessor() {
@Override
public String apply(String text) {
return AngelicaConfig.enableAmpersandConversion
? ColorCodeUtils.convertAmpersandToSectionX(text)
: text;
}
@Override
public boolean handlesAmpCodes() {
return AngelicaConfig.enableAmpersandConversion;
}
});
}
}
@@ -2,9 +2,11 @@ package com.gtnewhorizons.angelica.compat.bettercrashes;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
import cpw.mods.fml.common.Optional;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import vfyjxf.bettercrashes.utils.StateManager;
@Optional.Interface(modid = "angelica", iface = "vfyjxf.bettercrashes.utils.StateManager.IResettable")
@@ -25,6 +27,7 @@ public class BetterCrashesCompat implements StateManager.IResettable {
@Override
public void resetState() {
((ITessellatorData) Tessellator.instance).setDrawing(false);
AngelicaMod.LOGGER.info("Reloading SodiumRenderer");
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).angelica$reload();
AngelicaMod.LOGGER.info("Resetting GLStateManager");
@@ -112,6 +112,35 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean enableFontRenderer;
@Config.Comment("Enable full RGB color support (16.7M colors) using &#RRGGBB syntax in text")
@Config.DefaultBoolean(true)
public static boolean enableRGBColors;
@Config.Comment("Enable gradient text (&g&#start&#end)")
@Config.DefaultBoolean(true)
public static boolean enableGradients;
@Config.Comment("Enable rainbow cycling text (&q)")
@Config.DefaultBoolean(true)
public static boolean enableRainbow;
@Config.Comment("Enable wave/bounce animated text (&z)")
@Config.DefaultBoolean(true)
public static boolean enableWaveText;
@Config.Comment("Enable upside-down text (&v)")
@Config.DefaultBoolean(true)
public static boolean enableDinnerboneText;
@Config.Comment("Wave text amplitude (how far characters bounce)")
@Config.DefaultFloat(2.0f)
@Config.RangeFloat(min = 1.0f, max = 8.0f)
public static float waveAmplitude;
@Config.Comment("Convert &-prefix format codes (&#RRGGBB, &c, &l, etc.) at render time")
@Config.DefaultBoolean(true)
public static boolean enableAmpersandConversion;
@Config.Comment("Enable Dynamic Lights")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
@@ -302,4 +331,9 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean disableGLVersionPinning;
@Config.Comment("Disables GL Error checks. Always set to false in dev env or if LWJGL debug is on. Improves performance.")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean disableErrorChecks;
}
@@ -573,13 +573,13 @@ public class DynamicLights {
*/
public static int getLuminanceFromItemStack(@NotNull ItemStack stack) {
final Item item = stack.getItem();
if (item instanceof ItemBlock itemBlock) {
if (item instanceof IDynamicLightProducer lightProducer) {
return lightProducer.getLuminance(stack);
} else if (item instanceof ItemBlock itemBlock) {
final Block block = itemBlock.field_150939_a;
if (block != null) {
return block.getLightValue();
}
} else if (item instanceof IDynamicLightProducer lightProducer){
return lightProducer.getLuminance();
}
if (item == Items.lava_bucket) return Blocks.lava.getLightValue();
@@ -76,7 +76,7 @@ public class HUDCaching {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.enableTexture();
if (!AngelicaConfig.hudCachingActive || !GLStateManager.isFramebufferEnabled()) {
if (!AngelicaConfig.hudCachingActive) {
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
return;
}
@@ -194,6 +194,7 @@ public enum Mixins implements IMixins {
, "rendering.MixinTileEntity"
, "rendering.MixinTileEntityMobSpawner"
, "rendering.MixinTileEntityRendererDispatcher"
, "rendering.MixinRenderBlocksEmissive"
)
),
@@ -388,7 +389,7 @@ public enum Mixins implements IMixins {
.addRequiredMod(TargetedMod.MINEFACTORY_RELOADED)
.setApplyIf(() -> CompatConfig.fixMinefactoryReloaded)
.addClientMixins("client.minefactoryreloaded.MixinRedNetCableRenderer")),
NTM_SPACE_COMPAT(new MixinBuilder("Multiple fixes for NTM:Space")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.NTM_SPACE)
@@ -22,6 +22,7 @@ import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
import com.gtnewhorizons.angelica.render.CloudRenderer;
import com.gtnewhorizons.angelica.render.EmissiveTextureAutoloader;
import com.gtnewhorizons.angelica.rendering.AngelicaBlockSafetyRegistry;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
@@ -102,6 +103,7 @@ public final class ClientProxy extends CommonProxy {
super.preInit(event);
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(new EmissiveTextureAutoloader());
ModelRegistry.registerModid(MOD_ID);
blockError = new BlockError();
}
@@ -0,0 +1,60 @@
package com.gtnewhorizons.angelica.render;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@SideOnly(Side.CLIENT)
public class EmissiveTextureAutoloader {
@SubscribeEvent
public void onTextureStitchPre(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
TextureMap map = event.map;
EmissiveTextureHelper.clearCache();
@SuppressWarnings("unchecked")
Map<String, ?> registeredSprites = map.mapRegisteredSprites;
List<String> toRegister = new ArrayList<>();
for (String baseName : registeredSprites.keySet()) {
if (baseName.endsWith("_emissive")) continue;
String emissiveName = baseName + "_emissive";
if (resourceExists(emissiveName)) {
toRegister.add(baseName);
}
}
for (String baseName : toRegister) {
String emissiveName = baseName + "_emissive";
IIcon emissiveIcon = map.registerIcon(emissiveName);
EmissiveTextureHelper.cacheEmissive(baseName, emissiveIcon);
}
}
private boolean resourceExists(String iconName) {
ResourceLocation loc = new ResourceLocation(iconName);
ResourceLocation full = new ResourceLocation(
loc.getResourceDomain(),
"textures/blocks/" + loc.getResourcePath() + ".png"
);
try {
Minecraft.getMinecraft().getResourceManager().getResource(full);
return true;
} catch (Exception ignored) {
return false;
}
}
}
@@ -0,0 +1,54 @@
package com.gtnewhorizons.angelica.render;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.util.IIcon;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.concurrent.ConcurrentHashMap;
@SideOnly(Side.CLIENT)
public final class EmissiveTextureHelper {
private static final ConcurrentHashMap<String, IIcon> EMISSIVE_ICON_CACHE = new ConcurrentHashMap<>();
public static IIcon getEmissiveIcon(Block block, int side, int metadata) {
IIcon baseIcon = block.getIcon(side, metadata);
if (baseIcon == null) return null;
String baseIconName = baseIcon.getIconName();
if (baseIconName == null) return null;
return EMISSIVE_ICON_CACHE.get(baseIconName);
}
public static void clearCache() {
EMISSIVE_ICON_CACHE.clear();
}
public static void cacheEmissive(String baseIconName, IIcon emissiveIcon) {
EMISSIVE_ICON_CACHE.put(baseIconName, emissiveIcon);
}
public static void renderEmissiveBlockOverlay(RenderBlocks renderer, Block block, int x, int y, int z, int metadata) {
Tessellator tess = Tessellator.instance;
for (int side = 0; side < 6; side++) {
IIcon icon = getEmissiveIcon(block, side, metadata);
if (icon == null) continue;
tess.setColorOpaque_F(1f, 1f, 1f);
switch (side) {
case 0 -> renderer.renderFaceYNeg(block, x, y, z, icon);
case 1 -> renderer.renderFaceYPos(block, x, y, z, icon);
case 2 -> renderer.renderFaceZNeg(block, x, y, z, icon);
case 3 -> renderer.renderFaceZPos(block, x, y, z, icon);
case 4 -> renderer.renderFaceXNeg(block, x, y, z, icon);
case 5 -> renderer.renderFaceXPos(block, x, y, z, icon);
}
}
}
}
@@ -158,6 +158,34 @@ final class BlockOrientation extends RenderBlockState {
return origIcon == neighbor.getIcon(blockAccess, neighborX, neighborY, neighborZ, getTextureFaceOrig());
}
@Override
public BlockOrientation copy() {
BlockOrientation copy = new BlockOrientation();
copy.x = x;
copy.y = y;
copy.z = z;
copy.metadata = metadata;
copy.altMetadata = altMetadata;
copy.metadataBits = metadataBits;
copy.renderType = renderType;
copy.blockFace = blockFace;
copy.textureFace = textureFace;
copy.textureFaceOrig = textureFaceOrig;
copy.rotateUV = rotateUV;
copy.blockAccess = blockAccess;
copy.block = block;
copy.useAO = useAO;
copy.inWorld = inWorld;
copy.matcher = matcher;
copy.offsetsComputed = offsetsComputed;
copy.haveOffsets = haveOffsets;
copy.dx = dx;
copy.dy = dy;
copy.dz = dz;
return copy;
}
void setBlock(Block block, IBlockAccess blockAccess, int x, int y, int z) {
this.block = block;
this.blockAccess = blockAccess;
@@ -15,6 +15,7 @@ import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IBlockAccess;
import com.github.bsideup.jabel.Desugar;
import com.prupe.mcpatcher.MCLogger;
import com.prupe.mcpatcher.MCPatcherUtils;
import com.prupe.mcpatcher.mal.block.BlockAPI;
@@ -43,13 +44,16 @@ public class CTMUtils {
private static Overrides newOverrides = null;
private static TileLoader tileLoader;
private static final ThreadLocal<TileOverrideImpl.CTMCompact> CURRENT_COMPACT = new ThreadLocal<>();
@Desugar
public record CTMCompactContext(TileOverrideImpl.CTMCompact compact, RenderBlockState renderBlockState) {}
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact) {
CURRENT_COMPACT.set(compact);
private static final ThreadLocal<CTMCompactContext> CURRENT_COMPACT = new ThreadLocal<>();
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact, RenderBlockState renderBlockState) {
CURRENT_COMPACT.set(new CTMCompactContext(compact, renderBlockState.copy()));
}
public static TileOverrideImpl.CTMCompact getCurrentCompact() {
public static CTMCompactContext getCurrentCompact() {
return CURRENT_COMPACT.get();
}
@@ -180,6 +184,7 @@ public class CTMUtils {
local.renderBlockState.setBlock(block, blockAccess, x, y, z);
local.renderBlockState.setFace(face);
TileOverride lastOverride = local.ijkIterator.go(local.renderBlockState, icon);
local.renderBlockState.blockAccess = null;
if (lastOverride != null) {
return skipDefaultRendering(block) ? RenderBlocksUtils.blankIcon : local.ijkIterator.getIcon();
}
@@ -1,70 +1,18 @@
package com.prupe.mcpatcher.ctm;
import com.github.bsideup.jabel.Desugar;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
public final class CompactCtmQuadProcessor {
private static final int LEFT = 0;
private static final int UP_LEFT = 1;
private static final int UP = 2;
private static final int UP_RIGHT = 3;
private static final int RIGHT = 4;
private static final int DOWN_RIGHT = 5;
private static final int DOWN = 6;
private static final int DOWN_LEFT = 7;
private static final int SPRITE_DEFAULT = 0;
private static final int SPRITE_ALL = 1;
private static final int SPRITE_HORIZONTAL = 2;
private static final int SPRITE_VERTICAL = 3;
private static final int SPRITE_MIXED = 4;
private static final int[] CTM_47_BY_CONNECTIONS = {
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
16,18,16,18,6,46,6,21,16,18,16,18,28,9,28,22,
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
37,40,37,40,30,8,30,34,37,40,37,40,25,23,25,45,
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
16,42,16,42,6,20,6,10,16,42,16,42,28,35,28,44,
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
37,38,37,38,30,11,30,32,37,38,37,38,25,33,25,26
};
/**
* Face-local basis:
* right = increasing U on the face
* down = increasing V on the face
*/
private static final FaceBasis[] FACE_BASIS = {
new FaceBasis( 1, 0, 0, 0, 0, 1), // 0: Y-
new FaceBasis( 1, 0, 0, 0, 0, 1), // 1: Y+
new FaceBasis(-1, 0, 0, 0, -1, 0), // 2: Z-
new FaceBasis( 1, 0, 0, 0, -1, 0), // 3: Z+
new FaceBasis( 0, 0, 1, 0, -1, 0), // 4: X-
new FaceBasis( 0, 0, -1, 0, -1, 0) // 5: X+
};
/**
* Neighbor offsets in face-local coordinates:
* 0 = left, 1 = up-left, 2 = up, 3 = up-right,
* 4 = right, 5 = down-right, 6 = down, 7 = down-left
*/
private static final int[] RIGHT_FACTOR = {-1, -1, 0, 1, 1, 1, 0, -1};
private static final int[] DOWN_FACTOR = { 0, -1, -1, -1, 0, 1, 1, 1};
/**
* Quadrant side references:
* 0 = top-left
@@ -72,9 +20,12 @@ public final class CompactCtmQuadProcessor {
* 2 = bottom-right
* 3 = bottom-left
*/
private static final int[] QUAD_A = {LEFT, UP, RIGHT, DOWN};
private static final int[] QUAD_B = {UP, RIGHT, DOWN, LEFT};
private static final int[] QUAD_DIAG = {UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT};
private static final int[] QUAD_A = {RenderBlockState.REL_L, RenderBlockState.REL_U,
RenderBlockState.REL_R, RenderBlockState.REL_D};
private static final int[] QUAD_B = {RenderBlockState.REL_U, RenderBlockState.REL_R,
RenderBlockState.REL_D, RenderBlockState.REL_L};
private static final int[] QUAD_DIAG = {RenderBlockState.REL_UL, RenderBlockState.REL_UR,
RenderBlockState.REL_DR, RenderBlockState.REL_DL};
/** Maps split-face quadrant (0=TL,1=TR,2=BR,3=BL face-local) to RenderBlocks per-face TL/TR/BR/BL orientation. */
private static final int[][] RENDER_FACE_QUADRANT_MAP = {
@@ -90,35 +41,37 @@ public final class CompactCtmQuadProcessor {
private final IIcon[] sprites;
private final CompactConnectingCtmProperties props;
private final TileOverrideImpl.CTMCompact override;
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props) {
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props, TileOverrideImpl.CTMCompact compact) {
this.sprites = new IIcon[5];
if (sprites != null) {
System.arraycopy(sprites, 0, this.sprites, 0, Math.min(sprites.length, this.sprites.length));
}
this.props = props;
this.override = compact;
}
public boolean processFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon origIcon) {
int connections = CtmConnectionHelper.getConnections(world, x, y, z, face, props.getInnerSeams());
public boolean processFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon origIcon) {
int neighborBits = override.getNeighborBits(renderBlockState, origIcon);
int ctmIndex = CTM_47_BY_CONNECTIONS[connections & 0xFF];
int ctmIndex = TileOverride.neighborMap[neighborBits & 0xFF];
IIcon replacement = getReplacementIcon(ctmIndex);
if (replacement != null) {
renderWholeFace(rb, world, x, y, z, face, replacement);
renderWholeFace(rb, renderBlockState, replacement);
return true;
}
if (connections == 0 || connections == 0xFF) {
IIcon sprite = sprites[getCompactSpriteIndex(connections)];
if (neighborBits == 0 || neighborBits == 0xFF) {
IIcon sprite = sprites[getCompactSpriteIndex(neighborBits)];
if (sprite != null) {
renderWholeFace(rb, world, x, y, z, face, sprite);
renderWholeFace(rb, renderBlockState, sprite);
return true;
}
return false;
}
renderSplitFace(rb, world, x, y, z, face, connections);
renderSplitFace(rb, renderBlockState, neighborBits);
return true;
}
@@ -148,19 +101,21 @@ public final class CompactCtmQuadProcessor {
return SPRITE_ALL;
}
if ((connections & ((1 << LEFT) | (1 << RIGHT))) == ((1 << LEFT) | (1 << RIGHT))) {
if ((connections & ((1 << RenderBlockState.REL_L) | (1 << RenderBlockState.REL_R))) ==
((1 << RenderBlockState.REL_L) | (1 << RenderBlockState.REL_R))) {
return SPRITE_HORIZONTAL;
}
if ((connections & ((1 << UP) | (1 << DOWN))) == ((1 << UP) | (1 << DOWN))) {
if ((connections & ((1 << RenderBlockState.REL_U) | (1 << RenderBlockState.REL_D))) ==
((1 << RenderBlockState.REL_U) | (1 << RenderBlockState.REL_D))) {
return SPRITE_VERTICAL;
}
return SPRITE_DEFAULT;
}
private void renderSplitFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, int connections) {
final Block block = world.getBlock(x, y, z);
private void renderSplitFace(RenderBlocks rb, RenderBlockState renderBlockState, int connections) {
final int face = renderBlockState.getBlockFace();
final double minX = rb.renderMinX, minY = rb.renderMinY, minZ = rb.renderMinZ;
final double maxX = rb.renderMaxX, maxY = rb.renderMaxY, maxZ = rb.renderMaxZ;
@@ -197,7 +152,7 @@ public final class CompactCtmQuadProcessor {
}
setQuadrantBounds(rb, face, q, minX, minY, minZ, maxX, maxY, maxZ);
rb.overrideBlockTexture = icon;
renderFace(rb, block, x, y, z, face, icon);
renderFace(rb, renderBlockState, icon);
}
if (ao) {
@@ -261,18 +216,22 @@ public final class CompactCtmQuadProcessor {
return (sky << 16) | (block & 0xFFFF);
}
private void renderWholeFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon icon) {
Block block = world.getBlock(x, y, z);
private void renderWholeFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon) {
IIcon saved = rb.overrideBlockTexture;
rb.overrideBlockTexture = icon;
try {
renderFace(rb, block, x, y, z, face, icon);
renderFace(rb, renderBlockState, icon);
} finally {
rb.overrideBlockTexture = saved;
}
}
private void renderFace(RenderBlocks rb, Block block, int x, int y, int z, int face, IIcon icon) {
private void renderFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon) {
int face = renderBlockState.getBlockFace();
Block block = renderBlockState.getBlock();
int x = renderBlockState.getX();
int y = renderBlockState.getY();
int z = renderBlockState.getZ();
boolean originalFlip = rb.field_152631_f;
rb.field_152631_f = (face == 2 || face == 5);
try {
@@ -360,52 +319,10 @@ public final class CompactCtmQuadProcessor {
if (connA ^ connB) {
int connectedBit = connA ? sideA : sideB;
return (connectedBit == LEFT || connectedBit == RIGHT) ? SPRITE_VERTICAL : SPRITE_HORIZONTAL;
return (connectedBit == RenderBlockState.REL_L || connectedBit == RenderBlockState.REL_R) ? SPRITE_VERTICAL
: SPRITE_HORIZONTAL;
}
return SPRITE_DEFAULT;
}
@Desugar
private record FaceBasis(int rightX, int rightY, int rightZ, int downX, int downY, int downZ) { }
private static final class CtmConnectionHelper {
public static int getConnections(IBlockAccess world, int x, int y, int z, int face, boolean innerSeams) {
Block self = world.getBlock(x, y, z);
FaceBasis basis = FACE_BASIS[face];
int bits = 0;
for (int i = 0; i < 8; i++) {
int dx = RIGHT_FACTOR[i] * basis.rightX + DOWN_FACTOR[i] * basis.downX;
int dy = RIGHT_FACTOR[i] * basis.rightY + DOWN_FACTOR[i] * basis.downY;
int dz = RIGHT_FACTOR[i] * basis.rightZ + DOWN_FACTOR[i] * basis.downZ;
int nx = x + dx;
int ny = y + dy;
int nz = z + dz;
if (world.getBlock(nx, ny, nz) != self) {
continue;
}
if (innerSeams && !shouldRenderInnerSeam(world, nx, ny, nz, face)) {
continue;
}
bits |= (1 << i);
}
return bits;
}
private static boolean shouldRenderInnerSeam(IBlockAccess world, int x, int y, int z, int face) {
int[] normal = RenderBlockState.NORMALS[face];
int sx = x + normal[0];
int sy = y + normal[1];
int sz = z + normal[2];
return world.getBlock(sx, sy, sz).shouldSideBeRendered(world, sx, sy, sz, face);
}
}
}
@@ -139,4 +139,6 @@ abstract public class RenderBlockState {
abstract public boolean shouldConnectByTile(Block neighbor, IIcon origIcon, int neighborX, int neighborY,
int neighborZ);
abstract public RenderBlockState copy();
}
@@ -48,6 +48,28 @@ public abstract class TileOverride implements Comparable<TileOverride> {
private final List<ResourceLocation> tileNames = new ArrayList<>();
protected IIcon[] icons;
// Index into this array is formed from these bit values:
// 128 64 32
// 1 * 16
// 2 4 8
protected static final int[] neighborMap = new int[] {
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24, 43,
16, 18, 16, 18, 6, 46, 6, 21, 16, 18, 16, 18, 28, 9, 28, 22,
36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24, 43,
37, 40, 37, 40, 30, 8, 30, 34, 37, 40, 37, 40, 25, 23, 25, 45,
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
36, 39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27,
16, 42, 16, 42, 6, 20, 6, 10, 16, 42, 16, 42, 28, 35, 28, 44,
36, 39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27,
37, 38, 37, 38, 30, 11, 30, 32, 37, 38, 37, 38, 25, 33, 25, 26, };
static TileOverride create(ResourceLocation propertiesFile, TileLoader tileLoader) {
if (propertiesFile == null) {
return null;
@@ -353,6 +375,16 @@ public abstract class TileOverride implements Comparable<TileOverride> {
return shouldConnect(renderBlockState, icon, renderBlockState.getOffset(blockFace, relativeDirection));
}
protected final int getNeighborBits(RenderBlockState renderBlockState, IIcon origIcon) {
int neighborBits = 0;
for (int bit = 0; bit < 8; bit++) {
if (shouldConnect(renderBlockState, origIcon, bit)) {
neighborBits |= (1 << bit);
}
}
return neighborBits;
}
private boolean shouldConnect(RenderBlockState renderBlockState, IIcon icon, int[] offset) {
IBlockAccess blockAccess = renderBlockState.getBlockAccess();
Block block = renderBlockState.getBlock();
@@ -22,21 +22,6 @@ public class TileOverrideImpl {
final static class CTM extends TileOverride {
// Index into this array is formed from these bit values:
// 128 64 32
// 1 * 16
// 2 4 8
private static final int[] neighborMap = new int[] { 0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2,
1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2,
1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24,
43, 16, 18, 16, 18, 6, 46, 6, 21, 16, 18, 16, 18, 28, 9, 28, 22, 36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36,
17, 24, 19, 24, 43, 37, 40, 37, 40, 30, 8, 30, 34, 37, 40, 37, 40, 25, 23, 25, 45, 0, 3, 0, 3, 12, 5, 12,
15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 0, 3, 0, 3, 12, 5, 12,
15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 36, 39, 36, 39, 24, 41,
24, 27, 36, 39, 36, 39, 24, 41, 24, 27, 16, 42, 16, 42, 6, 20, 6, 10, 16, 42, 16, 42, 28, 35, 28, 44, 36,
39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27, 37, 38, 37, 38, 30, 11, 30, 32, 37, 38, 37, 38,
25, 33, 25, 26, };
CTM(PropertiesFile properties, TileLoader tileLoader) {
super(properties, tileLoader);
}
@@ -62,13 +47,7 @@ public class TileOverrideImpl {
@Override
IIcon getTileWorld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
int neighborBits = 0;
for (int bit = 0; bit < 8; bit++) {
if (shouldConnect(renderBlockState, origIcon, bit)) {
neighborBits |= (1 << bit);
}
}
return icons[neighborMap[neighborBits]];
return icons[neighborMap[getNeighborBits(renderBlockState, origIcon)]];
}
@Override
@@ -520,7 +499,7 @@ public class TileOverrideImpl {
public CompactCtmQuadProcessor getProcessor() {
CompactCtmQuadProcessor p = this.processor;
if (p == null) {
p = new CompactCtmQuadProcessor(icons, properties);
p = new CompactCtmQuadProcessor(icons, properties, this);
this.processor = p;
}
return p;
@@ -123,7 +123,7 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
IIcon newIcon = getTile(override, renderBlockState, origIcon);
if (newIcon != null) {
if (override instanceof TileOverrideImpl.CTMCompact compact) {
CTMUtils.setCurrentCompact(compact);
CTMUtils.setCurrentCompact(compact, renderBlockState);
}
lastMatchedOverride = override;
skipOverrides.add(override);
@@ -534,4 +534,61 @@ public class SodiumGameOptionPages {
return new OptionPage(I18n.format("sodium.options.pages.appearance"), ImmutableList.copyOf(groups));
}
public static OptionPage text() {
final List<OptionGroup> groups = new ArrayList<>();
groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("RGB Colors")
.setTooltip("Enable hex colors via &#RRGGBB syntax")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableRGBColors = value,
opts -> AngelicaConfig.enableRGBColors)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Gradients")
.setTooltip("Smooth color transitions via &g&#start&#end")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableGradients = value,
opts -> AngelicaConfig.enableGradients)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Rainbow")
.setTooltip("Cycling rainbow colors via &q")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableRainbow = value,
opts -> AngelicaConfig.enableRainbow)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Wave Text")
.setTooltip("Animated bouncing text via &z")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableWaveText = value,
opts -> AngelicaConfig.enableWaveText)
.build())
.add(OptionImpl.createBuilder(int.class, angelicaOpts)
.setName("Wave Amplitude")
.setTooltip("How far wave text bounces (default 2.0)")
.setControl(option -> new SliderControl(option, 10, 80, 5, v -> (v / 10) + "." + (v % 10)))
.setBinding((opts, value) -> AngelicaConfig.waveAmplitude = value / 10f, opts -> Math.round(AngelicaConfig.waveAmplitude * 10f))
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Dinnerbone Text")
.setTooltip("Flipped upside down text via &v")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableDinnerboneText = value,
opts -> AngelicaConfig.enableDinnerboneText)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Ampersand Conversion")
.setTooltip("Convert & prefix codes to formatting codes at render time")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableAmpersandConversion = value,
opts -> AngelicaConfig.enableAmpersandConversion)
.build())
.build());
return new OptionPage("Text Effects", ImmutableList.copyOf(groups));
}
}
@@ -63,6 +63,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
this.pages.add(SodiumGameOptionPages.advanced());
this.pages.add(SodiumGameOptionPages.performance());
this.pages.add(SodiumGameOptionPages.appearance());
this.pages.add(SodiumGameOptionPages.text());
// Add Dynamic Lights page if enabled
if (DynamicLights.configEnabled) {
@@ -2,6 +2,13 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.fontrenderer;
import com.gtnewhorizon.gtnhlib.util.font.IFontParameters;
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_LENGTH;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
import com.gtnewhorizons.angelica.compat.GTNHLibCompat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
import net.minecraft.client.gui.FontRenderer;
@@ -16,6 +23,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -110,16 +118,13 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
@Unique
public BatchingFontRenderer angelica$batcher;
@Unique
private static final char angelica$FORMATTING_CHAR = 167; // §
@Unique
private static final float angelica$1_over_255 = 1.0f/255.0f; // §
@Inject(method = "<init>", at = @At("TAIL"))
private void angelica$injectBatcher(GameSettings settings, ResourceLocation fontLocation, TextureManager texManager,
boolean unicodeMode, CallbackInfo ci) {
angelica$batcher = new BatchingFontRenderer((FontRenderer) (Object) this, this.charWidth, this.colorCode, this.locationFontTexture);
if (GTNHLibCompat.HAS_TEXT_PREPROCESSOR) {
GTNHLibCompat.registerPreprocessor();
}
}
@Unique
@@ -158,6 +163,8 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
}
else
{
text = ColorCodeUtils.convertAmpersandToSectionX(text);
if (this.bidiFlag)
{
text = this.bidiReorder(text);
@@ -226,4 +233,91 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
public float getCharWidthFine(char chr) {
return angelica$getBatcher().getCharWidthFine(chr);
}
@Inject(method = "getFormatFromString", at = @At("HEAD"), cancellable = true)
private static void angelica$getFormatFromString(String text, CallbackInfoReturnable<String> cir) {
if (!AngelicaConfig.enableRGBColors) return;
cir.setReturnValue(angelica$extractFormat(text));
}
@Unique private static final StringBuilder EXTRACT_STYLES = new StringBuilder();
@Unique private static final StringBuilder EXTRACT_EFFECTS = new StringBuilder();
@Unique private static final StringBuilder EXTRACT_COLOR = new StringBuilder();
@Unique private static final StringBuilder EXTRACT_RESULT = new StringBuilder();
@Unique
private static String angelica$extractFormat(String text) {
text = ColorCodeUtils.convertAmpersandToSectionX(text);
final int len = text.length();
if (len < 2) return "";
String lastColor = "";
final StringBuilder styles = EXTRACT_STYLES; styles.setLength(0);
final StringBuilder effects = EXTRACT_EFFECTS; effects.setLength(0);
for (int i = 0; i < len - 1; i++) {
if (text.charAt(i) != FORMATTING_CHAR) continue;
final char code = Character.toLowerCase(text.charAt(i + 1));
if (code == 'g' && i + GRADIENT_LENGTH <= len && ColorCodeUtils.isValidSectionX(text, i + 2) && ColorCodeUtils.isValidSectionX(text, i + 2 + SECTION_X_LENGTH)) {
final StringBuilder cb = EXTRACT_COLOR; cb.setLength(0);
for (int j = 0; j < GRADIENT_LENGTH; j++) cb.append(text.charAt(i + j));
lastColor = cb.toString();
styles.setLength(0);
effects.setLength(0);
i += GRADIENT_LENGTH - 1;
continue;
}
if (code == 'x' && i + SECTION_X_LENGTH <= len && ColorCodeUtils.isValidSectionX(text, i)) {
final StringBuilder cb = EXTRACT_COLOR; cb.setLength(0);
for (int j = 0; j < SECTION_X_LENGTH; j++) cb.append(text.charAt(i + j));
lastColor = cb.toString();
styles.setLength(0);
effects.setLength(0);
i += SECTION_X_LENGTH - 1;
continue;
}
if ((code >= '0' && code <= '9') || (code >= 'a' && code <= 'f')) {
lastColor = ColorCodeUtils.sectionPrefix(code);
styles.setLength(0);
effects.setLength(0);
i++;
} else if (code == 'r') {
lastColor = "";
styles.setLength(0);
effects.setLength(0);
i++;
} else if (code >= 'k' && code <= 'o') {
styles.append(ColorCodeUtils.sectionPrefix(code));
i++;
} else if (code == 'q' || code == 'z' || code == 'v') {
effects.append(ColorCodeUtils.sectionPrefix(code));
i++;
} else {
i++;
}
}
if (styles.length() == 0 && effects.length() == 0) return lastColor;
final StringBuilder result = EXTRACT_RESULT; result.setLength(0);
result.append(lastColor).append(styles).append(effects);
return result.toString();
}
@ModifyVariable(method = "getStringWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private String angelica$convertBeforeWidth(String str) {
return ColorCodeUtils.convertAmpersandToSectionX(str);
}
@ModifyVariable(method = "listFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private String angelica$convertBeforeListWrap(String str) {
return ColorCodeUtils.convertAmpersandToSectionX(str);
}
@ModifyVariable(method = "wrapFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private String angelica$convertBeforeWrap(String str) {
return ColorCodeUtils.convertAmpersandToSectionX(str);
}
}
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
import com.gtnewhorizon.gtnhlib.core.GTNHLibCore;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
@@ -25,13 +26,13 @@ public class MixinInitGLStateManager {
@Inject(method = "initializeTextures", at = @At("RETURN"))
private static void angelica$initializeGLStateManager(CallbackInfo ci) {
final Minecraft mc = Minecraft.getMinecraft();
mc.gameSettings.fboEnable = true; // Angelica & many other GTNH features require FBO's
GLStateManager.setDrawableGL(Display.getDrawable());
GLStateManager.initialize(GLSMInitConfig.builder()
.displaySize(mc.displayWidth, mc.displayHeight)
.lwjglDebug(AngelicaMod.lwjglDebug)
.streamingUploadStrategy(ClientProxy.options().advanced.streamingUploadStrategy)
.framebufferSupported(OpenGlHelper.framebufferSupported)
.fboEnabled(mc.gameSettings.fboEnable)
.noErrorChecks(AngelicaConfig.disableErrorChecks && GTNHLibCore.isObf() && !AngelicaMod.lwjglDebug)
.enableDSA(AngelicaConfig.enableDSA)
.directDrawer(TessellatorStreamingDrawer::drawDirect)
.streamingDrawerDestroy(TessellatorStreamingDrawer::destroy)
@@ -8,7 +8,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import com.prupe.mcpatcher.ctm.CTMUtils;
import com.prupe.mcpatcher.ctm.CompactCtmQuadProcessor;
import com.prupe.mcpatcher.ctm.TileOverrideImpl;
import com.prupe.mcpatcher.ctm.RenderBlockState;
import cpw.mods.fml.client.registry.RenderingRegistry;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
@@ -20,7 +20,6 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
@@ -153,6 +152,7 @@ public abstract class MixinRenderBlocks {
try {
return RenderingRegistry.instance().renderWorldBlock(rb, world, x, y, z, block, modelId);
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
CTMUtils.clearCurrentCompact();
// Render Error Block
int meta = exceptionErrorBlockMap.getOrDefault(e.getClass(), 0);
rb.overrideBlockTexture = BlockError.icons[exceptionErrorBlockMap.getOrDefault(e.getClass(), 0)];
@@ -168,19 +168,16 @@ public abstract class MixinRenderBlocks {
return false;
}
@ModifyExpressionValue(method = { "renderFaceXPos", "renderFaceYPos", "renderFaceZPos",
"renderFaceXNeg", "renderFaceYNeg", "renderFaceZNeg" },
at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/renderer/RenderBlocks;enableAO:Z"))
@ModifyExpressionValue(method = { "renderStandardBlockWithColorMultiplier" },
at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/renderer/RenderBlocks;renderAllFaces:Z"))
private boolean applyAOBrightness(boolean original, @Local(ordinal = 0) Tessellator tessellator) {
((StateAwareTessellator)tessellator).angelica$setAppliedAo(this.applyingCeleritasAO);
return original;
}
@Inject(method = { "renderFaceXPos", "renderFaceYPos", "renderFaceZPos",
"renderFaceXNeg", "renderFaceYNeg", "renderFaceZNeg" },
@Inject(method = { "renderStandardBlockWithColorMultiplier" },
at = @At("RETURN"))
private void resetAOFlag(Block p_147764_1_, double p_147764_2_, double p_147764_4_, double p_147764_6_, IIcon p_147764_8_, CallbackInfo ci,
@Local(ordinal = 0) Tessellator tessellator) {
private void resetAOFlag(Block p_147736_1_, int p_147736_2_, int p_147736_3_, int p_147736_4_, float p_147736_5_, float p_147736_6_, float p_147736_7_, CallbackInfoReturnable<Boolean> cir, @Local(ordinal = 0) Tessellator tessellator) {
((StateAwareTessellator)tessellator).angelica$setAppliedAo(false);
}
@@ -188,80 +185,51 @@ public abstract class MixinRenderBlocks {
public IBlockAccess blockAccess;
@Unique
private boolean compactCtmRendering = false;
@Unique
private CompactCtmQuadProcessor getCompactProcessor() {
TileOverrideImpl.CTMCompact compact = CTMUtils.getCurrentCompact();
if (compact == null) {
return null;
}
return compact.getProcessor();
}
private void handleCompactCtmFace(int face, Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
// recursion protection
if (compactCtmRendering) {
private void angelica$handleCompactCtmFace(IIcon icon, CallbackInfo ci) {
CTMUtils.CTMCompactContext ctx = CTMUtils.getCurrentCompact();
if (ctx == null) {
return;
}
CompactCtmQuadProcessor processor = ctx.compact().getProcessor();
RenderBlockState renderBlockState = ctx.renderBlockState();
CTMUtils.clearCurrentCompact();
if (this.blockAccess == null) {
return;
}
CompactCtmQuadProcessor processor = getCompactProcessor();
if (processor == null) {
return;
}
compactCtmRendering = true;
try {
boolean handled = processor.processFace(
(RenderBlocks)(Object)this,
this.blockAccess,
MathHelper.floor_double(x),
MathHelper.floor_double(y),
MathHelper.floor_double(z),
face,
icon
);
if (handled) {
ci.cancel();
}
} finally {
compactCtmRendering = false;
if (processor.processFace((RenderBlocks)(Object)this, renderBlockState, icon)) {
ci.cancel();
}
}
@Inject(method = "renderFaceYNeg", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceYNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(0, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceYPos", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceYPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(1, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceZNeg", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceZNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(2, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceZPos", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceZPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(3, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceXNeg", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceXNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(4, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceXPos", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceXPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(5, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderStandardBlock(Lnet/minecraft/block/Block;III)Z", at = @At("RETURN"))
@@ -0,0 +1,38 @@
package com.gtnewhorizons.angelica.mixins.early.rendering;
import com.gtnewhorizons.angelica.render.EmissiveTextureHelper;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.world.IBlockAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(RenderBlocks.class)
public class MixinRenderBlocksEmissive {
@Inject(method = "renderStandardBlock", at = @At("TAIL"))
private void angelica$renderEmissiveOverlay(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir) {
RenderBlocks self = (RenderBlocks)(Object)this;
IBlockAccess world = self.blockAccess;
if (world == null) return;
int meta = world.getBlockMetadata(x, y, z);
boolean hasEmissive = false;
for (int side = 0; side < 6; side++) {
if (EmissiveTextureHelper.getEmissiveIcon(block, side, meta) != null) {
hasEmissive = true;
break;
}
}
if (!hasEmissive) return;
Tessellator tess = Tessellator.instance;
tess.setBrightness(0xF000F0);
EmissiveTextureHelper.renderEmissiveBlockOverlay(self, block, x, y, z, meta);
}
}
@@ -28,7 +28,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* Ported with additional fixes from the ShaderFixer mod
* @author kotmatross28729
*/
@Mixin(value = SkyProviderCelestial.class, priority = 100)
@Mixin(value = SkyProviderCelestial.class, priority = 100, remap = false)
public class MixinSkyProviderCelestial {
/**
@@ -38,7 +38,9 @@ public class MixinSkyProviderCelestial {
"renderSunset",
"renderCelestials",
"renderAtmosphereGlow",
}, at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
}
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$common$redirectTex2D(int cap) {
return this.iris$common$redirectTexture(cap);
}
@@ -46,9 +48,11 @@ public class MixinSkyProviderCelestial {
/**
* Avoid program rebinding due to pipeline.setInputs | Doesn't affect texture disabling before glSkyList
*/
@WrapWithCondition(method = "render",
slice = @Slice(from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V", ordinal = 1)),
at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
@WrapWithCondition(method = "render"
, slice = @Slice(from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"
, ordinal = 1))
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$common$redirectTex2DException(int cap) {
return this.iris$common$redirectTexture(cap);
}
@@ -65,66 +69,98 @@ public class MixinSkyProviderCelestial {
/**
* Force skybox to render with the default program | Fix various issues with shaders enabled
*/
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"), remap = false)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"
, remap = true))
public void iris$main$renderInDefaultProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
main$previousProgram.set(GLStateManager.getActiveProgram());
GLStateManager.glUseProgram(0);
}
@WrapOperation(method = "render",
at = @At(value = "INVOKE", target = "Lcom/hbm/dim/SkyProviderCelestial;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"), remap = false)
@WrapOperation(method = "render"
, at = @At(value = "INVOKE"
, target = "Lcom/hbm/dim/SkyProviderCelestial;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"))
private void iris$main$renderSunInShaderProgram(SkyProviderCelestial instance, float partialTicks, WorldClient world, Minecraft mc, CelestialBody sun, double sunSize, double coronaSize, float visibility, float pressure, Operation<Void> original, @Share("main$previousProgram") LocalIntRef main$previousProgram){
GLStateManager.glUseProgram(main$previousProgram.get());
original.call(instance, partialTicks, world, mc, sun, sunSize, coronaSize, visibility, pressure);
GLStateManager.glUseProgram(0);
}
@Inject(method = "render", at = @At(value = "TAIL"), remap = false)
@Inject(method = "render"
, at = @At(value = "TAIL"))
public void iris$main$restorePreviousProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
GLStateManager.glUseProgram(main$previousProgram.get());
}
@Inject(method = "renderSun", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"),
slice = @Slice(
from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V", ordinal = 0),
to = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V", ordinal = 1)
@Inject(method = "renderSun"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"
, remap = true)
, slice = @Slice(
from = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V"
, ordinal = 0),
to = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V"
, ordinal = 1)
)
)
private void iris$sun$disableBlanking(CallbackInfo ci) {
Tessellator.instance.vertexCount = 0;
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"),
slice = @Slice(
from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 1),
to = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 2)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"
, remap = true)
, slice = @Slice(
from = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 1),
to = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 2)
)
)
private void iris$main$disableVoid(CallbackInfo ci) {
Tessellator.instance.vertexCount = 0;
}
@WrapWithCondition(method = "render", at = {
@At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 1),
@At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 2)
}, remap = false)
@WrapWithCondition(method = "render"
, at = {
@At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 1),
@At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 2)
})
private boolean angelica$renderHorizon(int i) {
return (boolean) Settings.HORIZON.option.getStore();
}
@Inject(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/Tessellator;instance:Lnet/minecraft/client/renderer/Tessellator;", opcode = Opcodes.GETSTATIC))
@Inject(method = "render"
, at = @At(value = "FIELD"
, target = "Lnet/minecraft/client/renderer/Tessellator;instance:Lnet/minecraft/client/renderer/Tessellator;"
, remap = true
, opcode = Opcodes.GETSTATIC))
private void iris$renderSky$beginNormalSky(CallbackInfo ci) {
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setPhase(WorldRenderingPhase.SKY));
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glRotatef(FFFF)V", ordinal = 2), remap = false)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glRotatef(FFFF)V"
, ordinal = 2))
private void iris$renderSky$tiltSun(CallbackInfo ci) {
Iris.getPipelineManager().getPipeline().ifPresent(
p -> GLStateManager.glRotatef(p.getSunPathRotation(), 0.0F, 0.0F, 1.0F)
);
}
@Inject(method = "renderSun", at = @At(value = "HEAD"), remap = false)
@Inject(method = "renderSun"
, at = @At(value = "HEAD"))
private void iris$setSunRenderStage(CallbackInfo ci) {
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setPhase(WorldRenderingPhase.SUN));
}
@@ -8,13 +8,15 @@ import org.lwjgl.opengl.GL11;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(value = SkyProviderLaytheSunset.class, priority = 100)
@Mixin(value = SkyProviderLaytheSunset.class, priority = 100, remap = false)
public class MixinSkyProviderLaytheSunset {
/**
* Avoid program rebinding due to pipeline.setInputs
*/
@WrapWithCondition(method = "renderSunset", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
@WrapWithCondition(method = "renderSunset"
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$sunset$redirectTex2D(int cap) {
if(cap == GL11.GL_TEXTURE_2D) {
Minecraft.getMinecraft().renderEngine.bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
@@ -17,13 +17,15 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = SkyProviderOrbit.class, priority = 100)
@Mixin(value = SkyProviderOrbit.class, priority = 100, remap = false)
public class MixinSkyProviderOrbit {
/**
* Avoid program rebinding due to pipeline.setInputs
*/
@WrapWithCondition(method = "render", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
@WrapWithCondition(method = "render"
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$common$redirectTex2D(int cap) {
if (cap == GL11.GL_TEXTURE_2D) {
Minecraft.getMinecraft().renderEngine.bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
@@ -35,24 +37,26 @@ public class MixinSkyProviderOrbit {
/**
* Force skybox to render with the default program | Fix various issues with shaders enabled
*/
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"), remap = false)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"
, remap = true))
public void iris$main$renderInDefaultProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
main$previousProgram.set(GLStateManager.getActiveProgram());
GLStateManager.glUseProgram(0);
}
@WrapOperation(method = "render",
at = @At(value = "INVOKE", target = "Lcom/hbm/dim/orbit/SkyProviderOrbit;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"), remap = false)
@WrapOperation(method = "render"
, at = @At(value = "INVOKE"
, target = "Lcom/hbm/dim/orbit/SkyProviderOrbit;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"))
private void iris$main$renderSunInShaderProgram(SkyProviderOrbit instance, float partialTicks, WorldClient world, Minecraft mc, CelestialBody sun, double sunSize, double coronaSize, float visibility, float pressure, Operation<Void> original, @Share("main$previousProgram") LocalIntRef main$previousProgram){
GLStateManager.glUseProgram(main$previousProgram.get());
original.call(instance, partialTicks, world, mc, sun, sunSize, coronaSize, visibility, pressure);
GLStateManager.glUseProgram(0);
}
/**
* Sets the program back before {@link #iris$main$renderInDefaultProgram(CallbackInfo, LocalIntRef)} call
*/
@Inject(method = "render", at = @At(value = "TAIL"), remap = false)
@Inject(method = "render"
, at = @At(value = "TAIL"))
public void iris$main$restorePreviousProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
GLStateManager.glUseProgram(main$previousProgram.get());
}