Fix flat item icons on drawers and barrels with shader packs (#2347)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-11-07 12:59:19 -08:00
1 parent 3876218405
commit aee989d23b
11 files changed
+351 -39

No files matched your search

@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
import com.gtnewhorizons.angelica.glsm.GLSMLighting;
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
import com.gtnewhorizons.angelica.glsm.states.LightState;
@@ -15,12 +16,75 @@ public final class FFPVertexLighting {
private static final Vector4f scratchEyePos = new Vector4f();
private static final Matrix3f scratchNormalMat = new Matrix3f();
private static final Vector3f scratchVec = new Vector3f();
private static final Vector3f scratchFactor = new Vector3f();
private static final Vector3f base = new Vector3f();
private static final Vector3f[] lightDirs = new Vector3f[VertexKey.FFP_LIGHT_COUNT];
private static final Vector3f[] lightDiffuse = new Vector3f[VertexKey.FFP_LIGHT_COUNT];
private static int litCount;
private static boolean normalize;
private static float rescale;
static {
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
lightDirs[i] = new Vector3f();
lightDiffuse[i] = new Vector3f();
}
}
private FFPVertexLighting() {}
public static boolean modulatesVertexColor(Vector3f destFactor) {
destFactor.set(1.0f);
if (!prepare(false)) return false;
factor(ShaderManager.getCurrentNormal(), scratchFactor);
final float r = scratchFactor.x;
final float g = scratchFactor.y;
final float b = scratchFactor.z;
if (r == 1.0f && g == 1.0f && b == 1.0f) return false;
if (!Float.isFinite(r + g + b)) return false;
destFactor.set(r, g, b);
return true;
}
public static boolean bakeColors(int[] rawBuffer, int vertexCount, boolean hasNormals, boolean hasColor) {
if (vertexCount <= 0 || !prepare(true)) return false;
if (!hasNormals) factor(ShaderManager.getCurrentNormal(), scratchFactor);
float cr = 0.0f, cg = 0.0f, cb = 0.0f;
int alphaBits = 0;
if (!hasColor) {
final var color = GLSMCurrentAttribs.getColor();
cr = clamp01(color.getRed());
cg = clamp01(color.getGreen());
cb = clamp01(color.getBlue());
alphaBits = toByte(clamp01(color.getAlpha())) << 24;
}
boolean haveNormal = false;
int lastNormal = 0;
for (int v = 0, i = 0; v < vertexCount; v++, i += 8) {
if (hasNormals) {
final int packed = rawBuffer[i + 6];
if (!haveNormal || packed != lastNormal) {
factor((byte) packed / 127.0f, (byte) (packed >> 8) / 127.0f, (byte) (packed >> 16) / 127.0f, scratchFactor);
lastNormal = packed;
haveNormal = true;
}
}
if (hasColor) {
final int packed = rawBuffer[i + 5];
cr = (packed & 0xFF) / 255.0f;
cg = ((packed >>> 8) & 0xFF) / 255.0f;
cb = ((packed >>> 16) & 0xFF) / 255.0f;
alphaBits = packed & 0xFF000000;
}
rawBuffer[i + 5] = alphaBits | (toByte(clamp01(cb * scratchFactor.z)) << 16) | (toByte(clamp01(cg * scratchFactor.y)) << 8) | toByte(clamp01(cr * scratchFactor.x));
}
return true;
}
private static boolean prepare(boolean directionalOnly) {
if (!GLSMLighting.getLightingState().isEnabled()) return false;
if (!GLSMLighting.getColorMaterial().isEnabled()) return false;
if (VertexKey.encodeColorMaterialMode(GLSMLighting.getColorMaterialParameter().getValue()) != VertexKey.CM_AMBIENT_AND_DIFFUSE) return false;
@@ -32,44 +96,58 @@ public final class FFPVertexLighting {
final boolean materialHasSpecular = material.shininess > 0.0f && (material.specular.x != 0.0f || material.specular.y != 0.0f || material.specular.z != 0.0f);
final Vector4f lightModelAmbient = GLSMLighting.getLightModel().ambient;
float r = lightModelAmbient.x;
float g = lightModelAmbient.y;
float b = lightModelAmbient.z;
base.set(lightModelAmbient.x, lightModelAmbient.y, lightModelAmbient.z);
final var lightEnables = GLSMLighting.getLightStates();
final var lightData = GLSMLighting.getLightDataStates();
boolean normalReady = false;
litCount = 0;
for (int i = 0; i < VertexKey.FFP_LIGHT_COUNT; i++) {
if (!lightEnables[i].isEnabled()) continue;
final LightState light = lightData[i];
if (materialHasSpecular && (light.specular.x != 0.0f || light.specular.y != 0.0f || light.specular.z != 0.0f))
return false;
if (!normalReady) {
transformCurrentNormal();
normalReady = true;
}
final float nDotVP = Math.max(scratchNormal.dot(lightDirection(light)), 0.0f);
r += light.ambient.x + nDotVP * light.diffuse.x;
g += light.ambient.y + nDotVP * light.diffuse.y;
b += light.ambient.z + nDotVP * light.diffuse.z;
if (directionalOnly && light.position.w != 0.0f) return false;
base.add(light.ambient.x, light.ambient.y, light.ambient.z);
lightDirs[litCount].set(lightDirection(light));
lightDiffuse[litCount].set(light.diffuse.x, light.diffuse.y, light.diffuse.z);
litCount++;
}
if (r == 1.0f && g == 1.0f && b == 1.0f) return false;
if (!Float.isFinite(r + g + b)) return false;
destFactor.set(r, g, b);
if (litCount > 0) {
GLSMMatrices.getModelViewMatrix().normal(scratchNormalMat);
normalize = GLSMLighting.getNormalizeState().isEnabled();
rescale = !normalize && GLSMLighting.getRescaleNormalState().isEnabled() ? Uniforms.rescaleFactor(scratchNormalMat, scratchVec) : 1.0f;
}
return true;
}
private static void transformCurrentNormal() {
GLSMMatrices.getModelViewMatrix().normal(scratchNormalMat);
scratchNormal.set(ShaderManager.getCurrentNormal());
private static void factor(Vector3f normal, Vector3f dest) {
factor(normal.x, normal.y, normal.z, dest);
}
private static void factor(float nx, float ny, float nz, Vector3f dest) {
dest.set(base);
if (litCount == 0) return;
scratchNormal.set(nx, ny, nz);
scratchNormalMat.transform(scratchNormal);
if (GLSMLighting.getNormalizeState().isEnabled()) {
if (normalize) {
scratchNormal.normalize();
} else if (GLSMLighting.getRescaleNormalState().isEnabled()) {
scratchNormal.mul(Uniforms.rescaleFactor(scratchNormalMat, scratchVec));
} else {
scratchNormal.mul(rescale);
}
for (int i = 0; i < litCount; i++) {
final float nDotVP = Math.max(scratchNormal.dot(lightDirs[i]), 0.0f);
dest.fma(nDotVP, lightDiffuse[i]);
}
}
private static float clamp01(float value) {
return Math.min(Math.max(value, 0.0f), 1.0f);
}
private static int toByte(float unit) {
return (int) (unit * 255.0f + 0.5f);
}
private static Vector3f lightDirection(LightState light) {
@@ -27,10 +27,12 @@ import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.stream.Stream;
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -82,6 +84,7 @@ class FFPVertexLightingGLTest {
GLSMEnable.glDisable(GL11.GL_LIGHT1);
GLSMEnable.glDisable(GL11.GL_LIGHT2);
GLSMEnable.glDisable(GL11.GL_COLOR_MATERIAL);
GLSMCurrentAttribs.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
final ShaderManager sm = ShaderManager.getInstance();
if (sm.isActive()) sm.deactivate();
GLSMVertexInput.glBindVertexArray(0);
@@ -126,7 +129,7 @@ class FFPVertexLightingGLTest {
+ " light1=" + GLSMLighting.getLightStates()[1].isEnabled();
}
private void buildTriangle() {
private void buildTriangle(boolean withColor) {
vao = GLSMVertexInput.glGenVertexArrays();
GLSMVertexInput.glBindVertexArray(vao);
@@ -141,9 +144,13 @@ class FFPVertexLightingGLTest {
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
GLSMVertexInput.glEnableVertexAttribArray(POSITION_LOC);
GLSMVertexInput.glVertexAttribPointer(POSITION_LOC, 3, GL11.GL_FLOAT, false, STRIDE, 0L);
GLSMVertexInput.glEnableVertexAttribArray(COLOR_LOC);
GLSMVertexInput.glVertexAttribPointer(COLOR_LOC, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, 12L);
VAOManager.setCurrentVertexFlags(COLOR_BIT);
if (withColor) {
GLSMVertexInput.glEnableVertexAttribArray(COLOR_LOC);
GLSMVertexInput.glVertexAttribPointer(COLOR_LOC, 4, GL11.GL_UNSIGNED_BYTE, true, STRIDE, 12L);
VAOManager.setCurrentVertexFlags(COLOR_BIT);
} else {
VAOManager.setCurrentVertexFlags(0);
}
}
private static void putVertex(ByteBuffer buf, float x, float y) {
@@ -154,7 +161,11 @@ class FFPVertexLightingGLTest {
}
private float[] drawAndReadCenter() {
buildTriangle();
return drawAndReadCenter(true);
}
private float[] drawAndReadCenter(boolean withColor) {
buildTriangle(withColor);
final ShaderManager sm = ShaderManager.getInstance();
ShaderManager.enable();
sm.activate();
@@ -233,6 +244,64 @@ class FFPVertexLightingGLTest {
state.run();
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), reason);
assertEquals(IDENTITY_FACTOR, factor, "a rejected solve must leave an identity factor");
assertBakeRejected(reason);
}
private static void assertBakeRejected(String reason) {
final int[] raw = new int[8];
Arrays.fill(raw, 0x5A3C1E0F);
final int[] before = raw.clone();
assertFalse(FFPVertexLighting.bakeColors(raw, 1, true, true), reason + ": bake must be rejected");
assertArrayEquals(before, raw, reason + ": a rejected bake must leave the buffer untouched");
}
@Test
void positionalLightIsNotBaked() {
GLSMLighting.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.0f, 0.0f, 1.0f, 1.0f));
assertBakeRejected("a positional light has a per-vertex direction");
assertTrue(FFPVertexLighting.modulatesVertexColor(factor), "text keeps the object-origin approximation for positional lights");
}
static Stream<Arguments> bakeCases() {
return Stream.of(
Arguments.of("flattened face-on", -1.0e-4f, 0.0f, 0.0f, 1.0f, true, true),
Arguments.of("flattened top", -1.0e-4f, 0.0f, 1.0f, 0.0f, true, true),
Arguments.of("flattened side", -1.0e-4f, 1.0f, 0.0f, 0.0f, true, true),
Arguments.of("unflattened tilted", 1.0f, 0.3f, 0.8f, 0.5f, true, true),
Arguments.of("current color", -1.0e-4f, 0.0f, 1.0f, 0.0f, true, false),
Arguments.of("current normal", -1.0e-4f, 0.0f, 1.0f, 0.0f, false, true));
}
private static int packNormal(float x, float y, float z) {
return ((byte) (x * 127.0f) & 0xFF) | (((byte) (y * 127.0f) & 0xFF) << 8) | (((byte) (z * 127.0f) & 0xFF) << 16);
}
private static float unpackNormal(int packed, int shift) {
return (byte) (packed >> shift) / 127.0f;
}
@ParameterizedTest(name = "{0}")
@MethodSource("bakeCases")
void bakedColorMatchesGeneratedShader(String name, float zScale, float nx, float ny, float nz, boolean hasNormals, boolean hasColor) {
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
GLSMMatrices.glLoadIdentity();
GLSMMatrices.glScalef(1.0f, 1.0f, zScale);
standardItemLighting();
if (!hasColor) GLSMCurrentAttribs.glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
final int packedNormal = packNormal(nx, ny, nz);
final int[] raw = new int[8];
raw[5] = VERTEX_COLOR;
if (hasNormals) raw[6] = packedNormal;
GLSMCurrentAttribs.glNormal3f(unpackNormal(packedNormal, 0), unpackNormal(packedNormal, 8), unpackNormal(packedNormal, 16));
assertTrue(FFPVertexLighting.bakeColors(raw, 1, hasNormals, hasColor), name + ": expected a bakeable state, " + gateState());
final float[] actual = drawAndReadCenter(hasColor);
for (int c = 0; c < 3; c++) {
final int baked = (raw[5] >>> (c * 8)) & 0xFF;
final int shader = Math.round(actual[c] * 255.0f);
assertEquals(baked, shader, name + ": channel " + c + " baked=" + baked + " shader=" + shader);
}
}
@Test
@@ -28,6 +28,7 @@ import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
import com.gtnewhorizons.angelica.rendering.tesr.BatchDrawDefaults;
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
import com.gtnewhorizons.angelica.rendering.tesr.FlattenedItemLighting;
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@@ -1194,6 +1195,7 @@ public class BatchingFontRenderer {
final Tessellator tessellator = Tessellator.instance;
int lastTexture = INVALID_TEXTURE;
boolean drawing = false;
FlattenedItemLighting.setPreLitText(true);
try {
for (int i = from; i < to; i++) {
final FontDrawCmd cmd = cmdsData[i];
@@ -1219,8 +1221,12 @@ public class BatchingFontRenderer {
owner.emitCommand(tessellator, cmd);
}
} finally {
if (drawing) {
tessellator.draw();
try {
if (drawing) {
tessellator.draw();
}
} finally {
FlattenedItemLighting.setPreLitText(false);
}
}
}
@@ -17,6 +17,7 @@ import com.gtnewhorizons.angelica.rendering.tesr.EntityMaterials;
import com.gtnewhorizons.angelica.rendering.tesr.GlintCapture;
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
import com.gtnewhorizons.angelica.rendering.tesr.EntityShadowReplay;
import com.gtnewhorizons.angelica.rendering.tesr.FlattenedItemLighting;
import com.gtnewhorizons.angelica.rendering.tesr.TemplateBuffer;
import com.gtnewhorizons.angelica.utils.AnimationsRenderUtils;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
@@ -26,6 +27,8 @@ import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.item.ItemStack;
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.VERTEX_SIZE;
public final class DroppedItemInstancer {
public static final long SKIP = -2L;
@@ -93,7 +96,8 @@ public final class DroppedItemInstancer {
private static boolean glintEligible(ItemStack stack) {
return stack != null && stack.getItem() != null && ModelPartBatcher.INSTANCE.isActive() && !GLStateManager.isRecordingDisplayList()
&& !TessellatorManager.isCurrentlyCapturing() && !TessellatorManager.shouldInterceptDraw(Tessellator.instance);
&& !TessellatorManager.isCurrentlyCapturing() && !TessellatorManager.shouldInterceptDraw(Tessellator.instance)
&& !FlattenedItemLighting.applies();
}
static TesrMaterial material(ItemStack stack, boolean unfilteredAtlas) {
@@ -219,6 +223,29 @@ public final class DroppedItemInstancer {
return material != null ? batchBlock(material, rb, block, meta, brightness) : GLSMDraws.drawCalls;
}
public static boolean drawFlattenedBlock(RenderBlocks rb, Block block, int meta, float brightness) {
final BlockMesh mesh = blockMesh(rb, block, meta, brightness);
final TemplateBuffer template = mesh.template();
if (template == null) return false;
mesh.sprites().markUsed();
final Tessellator tess = Tessellator.instance;
final int ints = template.vertexCount * VERTEX_SIZE;
tess.startDrawing(template.drawMode);
if (tess.rawBuffer == null || tess.rawBufferSize < ints) {
tess.rawBufferSize = Math.max(0x10000, ints);
tess.rawBuffer = new int[tess.rawBufferSize];
}
System.arraycopy(template.data, 0, tess.rawBuffer, 0, ints);
tess.vertexCount = template.vertexCount;
tess.rawBufferIndex = ints;
tess.hasTexture = true;
tess.hasNormals = true;
tess.hasColor = false;
tess.hasBrightness = false;
tess.draw();
return true;
}
private static long itemBlock(ItemStack stack, RenderBlocks rb, Block block, int meta, float brightness, boolean unfilteredAtlas) {
final TesrMaterial material = material(stack, unfilteredAtlas);
if (material == null) {
@@ -236,12 +263,7 @@ public final class DroppedItemInstancer {
static long batchBlock(TesrMaterial material, RenderBlocks rb, Block block, int meta, float brightness) {
final long drawsBefore = GLSMDraws.drawCalls;
if (BatchEligibility.batchingAllowed()) {
BlockMesh mesh = lookupBlock(block, meta);
if (mesh == null) {
mesh = captureBlock(rb, block, meta, brightness);
} else if (mesh.template() != null) {
GLSMCurrentAttribs.glColor4f(mesh.red(), mesh.green(), mesh.blue(), mesh.alpha());
}
final BlockMesh mesh = blockMesh(rb, block, meta, brightness);
if (mesh.template() == null) {
fallback++;
bail(BailReason.TEMPLATE);
@@ -338,6 +360,13 @@ public final class DroppedItemInstancer {
return mesh;
}
private static BlockMesh blockMesh(RenderBlocks rb, Block block, int meta, float brightness) {
final BlockMesh hit = lookupBlock(block, meta);
if (hit == null) return captureBlock(rb, block, meta, brightness);
if (hit.template() != null) GLSMCurrentAttribs.glColor4f(hit.red(), hit.green(), hit.blue(), hit.alpha());
return hit;
}
private static BlockMesh lookupBlock(Block block, int meta) {
blockKey.block = block;
blockKey.meta = meta;
@@ -0,0 +1,39 @@
package com.gtnewhorizons.angelica.rendering.tesr;
import com.gtnewhorizons.angelica.glsm.GLSMLighting;
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import net.coderbot.iris.shadows.ShadowRenderingState;
import org.joml.Matrix4fc;
public final class FlattenedItemLighting {
static final float FLATNESS_CUTOFF = 0.05f;
private static boolean preLitText;
private FlattenedItemLighting() {}
public static void setPreLitText(boolean preLit) {
preLitText = preLit;
}
static boolean isFlattened(Matrix4fc m) {
final float ax = m.m00(), ay = m.m01(), az = m.m02();
final float bx = m.m10(), by = m.m11(), bz = m.m12();
final float cx = m.m20(), cy = m.m21(), cz = m.m22();
final float bcx = by * cz - bz * cy, bcy = bz * cx - bx * cz, bcz = bx * cy - by * cx;
final float cax = cy * az - cz * ay, cay = cz * ax - cx * az, caz = cx * ay - cy * ax;
final float abx = ay * bz - az * by, aby = az * bx - ax * bz, abz = ax * by - ay * bx;
final float det = ax * bcx + ay * bcy + az * bcz;
final float frob2 = ax * ax + ay * ay + az * az + bx * bx + by * by + bz * bz + cx * cx + cy * cy + cz * cz;
final float adj2 = bcx * bcx + bcy * bcy + bcz * bcz + cax * cax + cay * cay + caz * caz + abx * abx + aby * aby + abz * abz;
if (!(adj2 > 0f) || det == 0f) return false;
return Math.abs(det) * (float) Math.sqrt(frob2) < FLATNESS_CUTOFF * adj2;
}
public static boolean applies() {
return GLStateManager.isMainThread() && !preLitText && TesrBatchRenderer.INSTANCE.inScope() && GLSMLighting.getLightingState().isEnabled() && !GLStateManager.isRecordingDisplayList()
&& TesrShaderPrograms.packActive() && !ShadowRenderingState.areShadowsCurrentlyBeingRendered() && isFlattened(GLSMMatrices.getModelViewMatrix());
}
}
@@ -23,6 +23,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.StateSet;
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
import com.gtnewhorizons.angelica.glsm.ffp.CubeParams;
import com.gtnewhorizons.angelica.glsm.ffp.FFPVertexLighting;
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
@@ -850,6 +851,7 @@ public final class ModelPartBatcher {
private enum TessellatorRoute { DRAW_NOW, DRAW_NOW_EXPECTED, QUEUE }
public static int drawTessellator(Tessellator tess) {
if (FlattenedItemLighting.applies() && FFPVertexLighting.bakeColors(tess.rawBuffer, tess.vertexCount, tess.hasNormals, tess.hasColor)) tess.hasColor = true;
switch (INSTANCE.routeTessellatorDraw(tess)) {
case QUEUE -> {
tess.isDrawing = false;
@@ -95,6 +95,10 @@ public final class TesrBatchRenderer {
if (scopeDepth++ == 0) currentScope = ++scopeCounter;
}
public boolean inScope() {
return scopeDepth != 0;
}
public void endScope() {
if (--scopeDepth == 0) currentScope = 0;
}
@@ -30,6 +30,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.experimental.surround.Surround;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.rendering.items.ItemRenderListManager;
import com.gtnewhorizons.angelica.rendering.tesr.FlattenedItemLighting;
import net.minecraft.client.renderer.ItemRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -44,7 +45,7 @@ public abstract class MixinItemRenderer {
@Surround(method = "renderItemIn2D")
private static void angelica$cacheItem(Tessellator tessellator, float minU, float minV, float maxU, float maxV, int widthSubdivisions, int heightSubdivisions, float thickness) {
@Surround.Skip
final boolean cached = !(GLStateManager.isRecordingDisplayList() || TessellatorManager.isCurrentlyCapturing() || TessellatorManager.shouldInterceptDraw(Tessellator.instance));
final boolean cached = !(GLStateManager.isRecordingDisplayList() || TessellatorManager.isCurrentlyCapturing() || TessellatorManager.shouldInterceptDraw(Tessellator.instance) || FlattenedItemLighting.applies());
if (cached) {
final ItemRenderListManager.CachedVBO vbo = ItemRenderListManager.pre(minU, minV, maxU, maxV, widthSubdivisions, heightSubdivisions, thickness);
if (vbo != null) {
@@ -5,6 +5,7 @@ import com.gtnewhorizons.angelica.experimental.surround.Surround;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.rendering.items.BlockRenderListManager;
import com.gtnewhorizons.angelica.rendering.items.DroppedItemInstancer;
import com.gtnewhorizons.angelica.rendering.tesr.FlattenedItemLighting;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
@@ -22,12 +23,15 @@ public abstract class MixinRenderBlocks {
|| GLStateManager.isRecordingDisplayList()
|| TessellatorManager.isCurrentlyCapturing()
|| TessellatorManager.shouldInterceptDraw(Tessellator.instance);
final boolean bake = !uncacheable && FlattenedItemLighting.applies();
final boolean drawn = bake && DroppedItemInstancer.drawFlattenedBlock(self, block, meta, brightness);
final boolean live = uncacheable || bake;
@Surround.Carry
final long part = uncacheable ? DroppedItemInstancer.RUN : DroppedItemInstancer.directBlock(self, block, meta, brightness);
final long part = live ? DroppedItemInstancer.RUN : DroppedItemInstancer.directBlock(self, block, meta, brightness);
@Surround.Carry
final int list = uncacheable ? 0 : part == DroppedItemInstancer.SKIP ? -1 : BlockRenderListManager.callOrStartCompiling(BlockRenderListManager.getDisplayList(block, meta));
final int list = live ? 0 : part == DroppedItemInstancer.SKIP ? -1 : BlockRenderListManager.callOrStartCompiling(BlockRenderListManager.getDisplayList(block, meta));
@Surround.Skip
final boolean cached = list < 0;
final boolean cached = drawn || list < 0;
}
@Surround.Return
@@ -0,0 +1,48 @@
package com.gtnewhorizons.angelica.rendering.tesr;
import org.joml.Matrix4f;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static java.lang.Math.toRadians;
import static org.junit.jupiter.api.Assertions.assertEquals;
class FlattenedItemLightingTest {
private static Matrix4f camera() {
return new Matrix4f().rotateX((float) toRadians(30)).rotateY((float) toRadians(70)).translate(-3.2f, -1.1f, -4.5f);
}
private static Matrix4f align(Matrix4f m) {
return m.translate(.5f, .5f, .5f).rotateZ((float) toRadians(180)).rotateY((float) toRadians(90)).translate(-.5f, -.5f, -.5f);
}
private static Matrix4f item(Matrix4f m) {
return m.translate(-2, 3, -3).scale(10, 10, 10).translate(1, 0.5f, 1).scale(1, 1, -1)
.rotateX((float) toRadians(210)).rotateY((float) toRadians(45)).rotateY((float) toRadians(-90));
}
static Stream<Arguments> cases() {
return Stream.of(
Arguments.of("storagedrawers 4-drawer", item(align(camera()).translate(0, 0, 0.94f).scale(1 / 16f, 1 / 16f, -0.0001f).translate(2, 10, 0).scale(.25f, .25f, 1)), true),
Arguments.of("storagedrawers 1-drawer", item(align(camera()).translate(0, 0, 0.94f).scale(1 / 16f, 1 / 16f, -0.0001f).translate(2, 10, 0).scale(.5f, .5f, 1)), true),
Arguments.of("jabba", item(align(camera()).translate(0, 0, -0.001f).scale(1 / 256f, 1 / 256f, -0.0001f).translate(65, 75, 0).scale(8, 8, 1)), true),
Arguments.of("ae2 storage monitor", item(camera().scale(1 / 42f).translate(-8, -10.2f, -10.4f).scale(1, 1, 0.005f)), true),
Arguments.of("hbm mass storage", item(camera().translate(0, 0, -0.005f).scale(1 / 16f, 1 / 16f, -0.0001f).translate(4, 2.5f, 0).scale(.5f, .5f, 1)), true),
Arguments.of("identity", camera(), false),
Arguments.of("sign text", camera().translate(.5f, .75f, .5f).scale(0.016666668f, -0.016666668f, 0.016666668f), false),
Arguments.of("chest model", camera().translate(.5f, .5f, .5f).scale(1, -1, -1), false),
Arguments.of("10:1 model", camera().scale(1, 1, 0.1f), false),
Arguments.of("needle", camera().scale(1, 0.1f, 0.1f), false),
Arguments.of("singular", camera().scale(1, 1, 0), false));
}
@ParameterizedTest
@MethodSource("cases")
void classifiesFlatness(String name, Matrix4f m, boolean expected) {
assertEquals(expected, FlattenedItemLighting.isFlattened(m), name);
}
}
@@ -386,6 +386,38 @@ class TesrLayerStateTest {
});
}
@Test
void flattenedLitDrawsBakeOnlyWhenThePackDrawsAndTheTextIsNotPreLit() {
withPack(true, drawing -> {
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
GLSMMatrices.glPushMatrix();
try {
GLSMMatrices.glScalef(1.0f, 1.0f, -1.0e-4f);
assertFalse(FlattenedItemLighting.applies(), "no bake outside a TESR dispatch");
TesrBatchRenderer.INSTANCE.beginScope();
try {
assertTrue(FlattenedItemLighting.applies(), "a lit, flattened draw inside a TESR while the pack draws the world is baked");
FlattenedItemLighting.setPreLitText(true);
try {
assertFalse(FlattenedItemLighting.applies(), "text the font already lit must not be lit a second time");
} finally {
FlattenedItemLighting.setPreLitText(false);
}
assertTrue(FlattenedItemLighting.applies(), "clearing the pre-lit flag restores the bake");
drawing[0] = false;
assertFalse(FlattenedItemLighting.applies(), "no bake while the pack is not drawing the world");
} finally {
TesrBatchRenderer.INSTANCE.endScope();
}
} finally {
GLSMMatrices.glPopMatrix();
}
});
}
private static void withPack(boolean initiallyDrawing, Consumer<boolean[]> body) {
final PipelineManager manager = Iris.getPipelineManager();
final WorldRenderingPipeline previous = manager.getPipelineNullable();