mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-08 12:35:01 +00:00
Fix flat item icons on drawers and barrels with shader packs (#2347)
This commit is contained in:
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) {
|
||||
|
||||
+74
-5
@@ -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;
|
||||
}
|
||||
|
||||
+2
-1
@@ -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) {
|
||||
|
||||
+7
-3
@@ -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
|
||||
|
||||
+48
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user