mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 09:44:52 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e1fd9d0e9 | ||
|
|
58938e82a7 | ||
|
|
be6c8c85b5 | ||
|
|
39bca37d0d | ||
|
|
1c61be04a7 | ||
|
|
7a97cd9386 | ||
|
|
5b09bde7a0 | ||
|
|
edc0081b7c | ||
|
|
913256b032 | ||
|
|
e1f5083d3e | ||
|
|
b55e693ad2 |
No files matched your search
+1
-1
@@ -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) {
|
||||
|
||||
+100
-6
@@ -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);
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -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)
|
||||
|
||||
+20
-52
@@ -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"))
|
||||
|
||||
+38
@@ -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);
|
||||
}
|
||||
}
|
||||
+60
-24
@@ -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));
|
||||
}
|
||||
|
||||
+4
-2
@@ -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);
|
||||
|
||||
+14
-10
@@ -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());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user