mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-10-25 20:22:22 +00:00
Optimize Enchant Glint Rendering (#2140)
* initial commit for enchant-glint-optimizations * Fix spacing issues and remove GlintCompatibility.java
This commit is contained in:
1 parent
7f3fb35ebb
commit
7c28a69bfe
31 files changed
+1685
-294
No files matched your search
@@ -0,0 +1,55 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
public final class CombinedGlint {
|
||||
private static final Matrix4f savedMatrix = new Matrix4f();
|
||||
private static final Matrix4f inverse = new Matrix4f();
|
||||
private static final Matrix4f relative = new Matrix4f();
|
||||
private static boolean active;
|
||||
private static boolean replaceAlpha;
|
||||
|
||||
private CombinedGlint() {}
|
||||
|
||||
public static boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public static boolean replacesAlpha() {
|
||||
return active && replaceAlpha;
|
||||
}
|
||||
|
||||
public static boolean begin(Matrix4fc secondTextureMatrix) {
|
||||
if (active || GLStateManager.getActiveProgram() != 0 || GLStateManager.getLightingState().isEnabled() || GLStateManager.getColorSumState().isEnabled() || !GLStateManager.glIsEnabled(GL11.GL_BLEND)) return false;
|
||||
final var blend = GLStateManager.getBlendState();
|
||||
final boolean lastAlpha = blend.getSrcAlpha() == GL11.GL_ONE && blend.getDstAlpha() == GL11.GL_ZERO && blend.getEquationAlpha() == GL14.GL_FUNC_ADD;
|
||||
final boolean preserveAlpha = blend.getSrcAlpha() == GL11.GL_ZERO && blend.getDstAlpha() == GL11.GL_ONE && (blend.getEquationAlpha() == GL14.GL_FUNC_ADD || blend.getEquationAlpha() == GL14.GL_FUNC_REVERSE_SUBTRACT);
|
||||
if (blend.getSrcRgb() != GL11.GL_SRC_COLOR || blend.getDstRgb() != GL11.GL_ONE
|
||||
|| (!lastAlpha && !preserveAlpha && (blend.getSrcAlpha() != GL11.GL_SRC_COLOR || blend.getDstAlpha() != GL11.GL_ONE || blend.getEquationAlpha() != blend.getEquationRgb()))
|
||||
|| (blend.getEquationRgb() != GL14.GL_FUNC_ADD && blend.getEquationRgb() != GL14.GL_FUNC_REVERSE_SUBTRACT)) return false;
|
||||
final var textures = GLStateManager.getTextures();
|
||||
if (!textures.getTextureUnitStates(0).isEnabled() || textures.getTextureUnitBindings(0).getBinding() == 0 || textures.getTexEnvState(0).mode != GL11.GL_MODULATE) return false;
|
||||
if (textures.getTextureUnitStates(1).isEnabled() && textures.getTextureUnitBindings(1).getBinding() != 0 && textures.getTexEnvState(1).mode != GL11.GL_MODULATE) return false;
|
||||
for (int unit = 2; unit < 4; unit++) {
|
||||
if (textures.getTextureUnitStates(unit).isEnabled() && textures.getTextureUnitBindings(unit).getBinding() != 0) return false;
|
||||
}
|
||||
inverse.set(textures.getTextureUnitMatrix(0)).invert();
|
||||
relative.set(secondTextureMatrix).mul(inverse);
|
||||
if (!relative.isFinite()) return false;
|
||||
savedMatrix.set(textures.getTextureUnitMatrix(3));
|
||||
GLStateManager.setTextureMatrix(3, relative);
|
||||
replaceAlpha = lastAlpha;
|
||||
active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void end() {
|
||||
if (!active) return;
|
||||
active = false;
|
||||
GLStateManager.setTextureMatrix(3, savedMatrix);
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,8 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 14;
|
||||
private static final int BIT_COMBINED_GLINT = 61;
|
||||
private static final int BIT_GLINT_REPLACE_ALPHA = 62;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
@@ -111,6 +113,8 @@ public final class FragmentKey {
|
||||
|
||||
public static int packFromState(long[] out, GLContextState glCtx) {
|
||||
long global = 0;
|
||||
if (CombinedGlint.isActive()) global |= 1L << BIT_COMBINED_GLINT;
|
||||
if (CombinedGlint.replacesAlpha()) global |= 1L << BIT_GLINT_REPLACE_ALPHA;
|
||||
|
||||
// Fog
|
||||
if (glCtx.fogMode.isEnabled()) {
|
||||
@@ -248,6 +252,8 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
public int fogMode() { return (int) (packed[0] & 0x3); }
|
||||
public boolean combinedGlint() { return (packed[0] & (1L << BIT_COMBINED_GLINT)) != 0; }
|
||||
public boolean glintReplaceAlpha() { return (packed[0] & (1L << BIT_GLINT_REPLACE_ALPHA)) != 0; }
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
|
||||
@@ -389,7 +395,9 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(), alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(), overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(),
|
||||
overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
|
||||
/**
|
||||
* Generates GLSL 330 core fragment shaders for FFP emulation.
|
||||
@@ -17,6 +18,15 @@ public final class FragmentShaderGenerator {
|
||||
|
||||
emitInputs(sb, key);
|
||||
emitUniforms(sb, key);
|
||||
if (key.combinedGlint()) {
|
||||
sb.append("#define GLINT_REPLACE_ALPHA ").append(key.glintReplaceAlpha() ? 1 : 0).append('\n');
|
||||
sb.append("#define GLINT_FOG_MODE ").append(key.fogMode()).append('\n');
|
||||
sb.append("#define GLINT_ALPHA_FUNC ").append(key.alphaTestEnabled() ? key.alphaTestFunc() : 7).append('\n');
|
||||
sb.append("#define GLINT_OVERLAY ").append(key.overlayInstanced() ? 2 : key.overlayEnabled() ? 1 : 0).append('\n');
|
||||
sb.append("#define GLINT_LIGHTMAP ").append(key.nrEnabledUnits() > 1 && key.unitEnabled(1) ? 1 : 0).append('\n');
|
||||
sb.append(ShaderLoader.getShaderSource("angelica:combined_glint.frag.glsl"));
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append("out vec4 fragColor;\n\n");
|
||||
|
||||
sb.append("void main() {\n");
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 glintLayer(vec4 coord, out bool visible) {
|
||||
vec4 color = textureProj(u_Sampler0, coord) * v_Color;
|
||||
#if GLINT_OVERLAY == 2
|
||||
color.rgb = mix(color.rgb, v_Overlay.rgb, v_Overlay.a);
|
||||
#elif GLINT_OVERLAY == 1
|
||||
color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);
|
||||
#endif
|
||||
#if GLINT_LIGHTMAP
|
||||
color *= textureProj(u_Sampler1, v_TexCoord1);
|
||||
#endif
|
||||
float qa = round(color.a * 255.0);
|
||||
float ref = u_AlphaRef * 255.0;
|
||||
#if GLINT_ALPHA_FUNC == 0
|
||||
visible = false;
|
||||
#elif GLINT_ALPHA_FUNC == 1
|
||||
visible = qa < ref;
|
||||
#elif GLINT_ALPHA_FUNC == 2
|
||||
visible = qa == round(ref);
|
||||
#elif GLINT_ALPHA_FUNC == 3
|
||||
visible = qa <= ref;
|
||||
#elif GLINT_ALPHA_FUNC == 4
|
||||
visible = qa > ref;
|
||||
#elif GLINT_ALPHA_FUNC == 5
|
||||
visible = qa != round(ref);
|
||||
#elif GLINT_ALPHA_FUNC == 6
|
||||
visible = qa >= ref;
|
||||
#else
|
||||
visible = true;
|
||||
#endif
|
||||
if (!visible) return vec4(0.0);
|
||||
#if GLINT_FOG_MODE == 1
|
||||
float fog = v_FogCoord * u_FogParams.x + u_FogParams.y;
|
||||
#elif GLINT_FOG_MODE == 2
|
||||
float fog = exp2(-(v_FogCoord * u_FogParams.z));
|
||||
#elif GLINT_FOG_MODE == 3
|
||||
float fogDistance = v_FogCoord * u_FogParams.w;
|
||||
float fog = exp2(-(fogDistance * fogDistance));
|
||||
#endif
|
||||
#if GLINT_FOG_MODE != 0
|
||||
color.rgb = mix(u_FogColor.rgb, color.rgb, clamp(fog, 0.0, 1.0));
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
|
||||
void main() {
|
||||
bool visible0, visible1;
|
||||
vec4 a = glintLayer(v_TexCoord0, visible0);
|
||||
vec4 b = glintLayer(u_TextureMatrix3 * v_TexCoord0, visible1);
|
||||
if (!visible0 && !visible1) discard;
|
||||
fragColor = sqrt(a * a + b * b);
|
||||
#if GLINT_REPLACE_ALPHA
|
||||
fragColor.a = visible1 ? b.a : a.a;
|
||||
#endif
|
||||
}
|
||||
@@ -680,6 +680,7 @@ public enum Mixins implements IMixins {
|
||||
"gui.MixinGuiSlot",
|
||||
|
||||
"glint.MixinRenderBiped",
|
||||
"glint.MixinEntityLiving_ArmorEnchantCache",
|
||||
"glint.MixinRenderPlayer",
|
||||
|
||||
"optimization.MixinRenderItemFrame",
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Whether a mob's worn armor piece is enchanted, cached per stack instance.
|
||||
*/
|
||||
public interface ArmorEnchantCache {
|
||||
boolean angelica$isArmorEnchanted(int slot, ItemStack stack);
|
||||
}
|
||||
@@ -91,6 +91,11 @@ public final class TracyFramePlots {
|
||||
private static final PlotDelta P_TESR_CACHE_HITS = new PlotDelta("tesr.cacheHits");
|
||||
private static final PlotDelta P_TESR_CACHE_MISSES = new PlotDelta("tesr.cacheMisses");
|
||||
private static final PlotDelta P_TESR_MODEL_PARTS = new PlotDelta("tesr.modelParts");
|
||||
private static final PlotDelta P_ARMOR_BASE_REUSES = new PlotDelta("glint.armorBaseReuses");
|
||||
private static final PlotDelta P_GLINT_PASS_REUSES = new PlotDelta("glint.passReuses");
|
||||
private static final PlotDelta P_ARMOR_LOOP_SKIPS = new PlotDelta("glint.armorLoopSkips");
|
||||
private static final PlotDelta P_ARMOR_SECTION_SKIPS = new PlotDelta("glint.armorSectionSkips");
|
||||
private static final PlotDelta P_REDRAWN_INSTANCES = new PlotDelta("glint.redrawnInstances");
|
||||
private static final PlotDelta P_TESR_LIVE_FALLBACKS = new PlotDelta("tesr.liveFallbacks");
|
||||
private static final PlotDelta P_ITEMS_INSTANCED = new PlotDelta("items.instanced");
|
||||
private static final PlotDelta P_ITEMS_FALLBACK = new PlotDelta("items.fallback");
|
||||
@@ -239,6 +244,11 @@ public final class TracyFramePlots {
|
||||
P_TESR_CACHE_HITS.plot(AngelicaTesrMeshCache.cacheHits);
|
||||
P_TESR_CACHE_MISSES.plot(AngelicaTesrMeshCache.cacheMisses);
|
||||
P_TESR_MODEL_PARTS.plot(batcher.statParts());
|
||||
P_ARMOR_BASE_REUSES.plot(batcher.statArmorBaseReuses());
|
||||
P_GLINT_PASS_REUSES.plot(batcher.statGlintPassReuses());
|
||||
P_ARMOR_LOOP_SKIPS.plot(batcher.statArmorLoopSkips());
|
||||
P_ARMOR_SECTION_SKIPS.plot(batcher.statArmorSectionSkips());
|
||||
P_REDRAWN_INSTANCES.plot(batcher.statRedrawnInstances());
|
||||
P_TESR_LIVE_FALLBACKS.plot(batcher.statLiveFallbacks());
|
||||
P_ITEMS_INSTANCED.plot(DroppedItemInstancer.statInstanced());
|
||||
P_ITEMS_FALLBACK.plot(DroppedItemInstancer.statFallback());
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
public final class GlintClock {
|
||||
|
||||
@@ -14,6 +15,7 @@ public final class GlintClock {
|
||||
|
||||
private static long millis;
|
||||
private static float armorU0, armorV0, armorU1, armorV1;
|
||||
private static final Matrix4f armorFirst = new Matrix4f(), armorSecond = new Matrix4f();
|
||||
|
||||
public static void beginFrame(long nowMillis) {
|
||||
millis = nowMillis;
|
||||
@@ -30,6 +32,20 @@ public final class GlintClock {
|
||||
final double dv1 = frac(f1 * COS1 / 3.0);
|
||||
armorU1 = (float) (3.0 * (COS1 * du1 + SIN1 * dv1));
|
||||
armorV1 = (float) (3.0 * (-SIN1 * du1 + COS1 * dv1));
|
||||
armorFirst.scaling(0.33333334f).rotateZ((float) Math.toRadians(30)).translate(armorU0, armorV0, 0);
|
||||
armorSecond.scaling(0.33333334f).rotateZ((float) Math.toRadians(-30)).translate(armorU1, armorV1, 0);
|
||||
}
|
||||
|
||||
public static Matrix4f secondArmorMatrix(Matrix4fc first) {
|
||||
return armorFirst.equals(first, 0.000001f) ? armorSecond : null;
|
||||
}
|
||||
|
||||
public static Matrix4f firstArmorMatrix() {
|
||||
return armorFirst;
|
||||
}
|
||||
|
||||
public static Matrix4f secondArmorMatrix() {
|
||||
return armorSecond;
|
||||
}
|
||||
|
||||
public static long millis() {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.MatrixHelper;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* The state a vanilla glint block leaves behind, for callers that skip the block. GLSM is only called where its effective state differs.
|
||||
*/
|
||||
public final class SkippedGlintBlock {
|
||||
|
||||
private static final BlendState blend = new BlendState();
|
||||
|
||||
private SkippedGlintBlock() {}
|
||||
|
||||
public static void applyArmorExitState() {
|
||||
applyExitState(1.0F, 1.0F, 1.0F, 1.0F, GL11.GL_SRC_COLOR, GL11.GL_ONE, GL11.GL_SRC_COLOR, GL11.GL_ONE, true, true);
|
||||
}
|
||||
|
||||
public static void applyHeldItemExitState() {
|
||||
applyExitState(0.5F * 0.76F, 0.25F * 0.76F, 0.8F * 0.76F, 1.0F, GL11.GL_SRC_COLOR, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, false, false);
|
||||
}
|
||||
|
||||
private static void applyExitState(float red, float green, float blue, float alpha, int srcRgb, int dstRgb, int srcAlpha, int dstAlpha, boolean resetTextureMatrix, boolean restoreDepthMask) {
|
||||
final Color4 color = GLStateManager.getColor();
|
||||
if (color.getRed() != red || color.getGreen() != green || color.getBlue() != blue || color.getAlpha() != alpha) {
|
||||
GLStateManager.glColor4f(red, green, blue, alpha);
|
||||
}
|
||||
if (resetTextureMatrix && !MatrixHelper.isIdentity(GLStateManager.getTextures().getTextureUnitMatrix(0))) {
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
if (GLStateManager.getMatrixMode().getMode() != GL11.GL_MODELVIEW) GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
if (restoreDepthMask && !GLStateManager.isEffectiveDepthMaskEnabled()) GLStateManager.glDepthMask(true);
|
||||
if (!GLStateManager.getLightingState().isEnabled()) GLStateManager.glEnable(GL11.GL_LIGHTING);
|
||||
GLStateManager.getEffectiveBlendState(blend);
|
||||
if (GLStateManager.getBlendState().isFuncUnknown() || blend.getSrcRgb() != srcRgb || blend.getDstRgb() != dstRgb || blend.getSrcAlpha() != srcAlpha || blend.getDstAlpha() != dstAlpha) {
|
||||
GLStateManager.tryBlendFuncSeparate(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
}
|
||||
if (GLStateManager.isEffectiveBlendEnabled()) GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
if (GLStateManager.getDepthState().getFunc() != GL11.GL_LEQUAL) GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import com.gtnewhorizons.angelica.rendering.tesr.AngelicaTesrMeshCache;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BakedTransformCapture;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
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.TemplateBuffer;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
@@ -34,6 +35,12 @@ public final class DroppedItemInstancer {
|
||||
private static final Object[] BLOCK_ARGS = new Object[4];
|
||||
private static BakedTransformCapture blockBackend;
|
||||
private static boolean basePart;
|
||||
private static final GlintCapture glintCapture = new GlintCapture();
|
||||
private static boolean glintSeen;
|
||||
private static final ItemPropCache.ItemProp glintProperties = new ItemPropCache.ItemProp();
|
||||
private static final ItemPropCache.ItemProp currentGlintProperties = new ItemPropCache.ItemProp();
|
||||
private static TemplateBuffer glintTemplate;
|
||||
private static int glintTemplateWidth, glintTemplateHeight;
|
||||
|
||||
private static long instanced;
|
||||
private static long glintInstanced;
|
||||
@@ -81,7 +88,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);
|
||||
return stack != null && stack.getItem() != null && ModelPartBatcher.INSTANCE.isActive() && !GLStateManager.isRecordingDisplayList()
|
||||
&& !TessellatorManager.isCurrentlyCapturing() && !TessellatorManager.shouldInterceptDraw(Tessellator.instance);
|
||||
}
|
||||
|
||||
static TesrMaterial material(ItemStack stack, boolean unfilteredAtlas) {
|
||||
@@ -100,6 +108,8 @@ public final class DroppedItemInstancer {
|
||||
}
|
||||
|
||||
public static void icon(ItemStack stack, Tessellator t, float maxU, float minV, float minU, float maxV, int width, int height, float thickness, Operation<Void> original) {
|
||||
glintCapture.reset();
|
||||
glintSeen = false;
|
||||
final TesrMaterial material = material(stack, true);
|
||||
if (material == null) {
|
||||
basePart = false;
|
||||
@@ -131,10 +141,11 @@ public final class DroppedItemInstancer {
|
||||
}
|
||||
callIcon(ICON_ARGS, original, t, maxU, minV, minU, maxV, width, height, thickness);
|
||||
BatchEligibility.onPartFallback(drawsBefore, GLStateManager.drawCalls);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void glint(Tessellator t, float maxU, float minV, float minU, float maxV, int width, int height, float thickness, Operation<Void> original) {
|
||||
/** Held glints use ITEM_GLINT for its stencil guard; dropped items draw with culling on and need none. */
|
||||
public static void glint(Tessellator t, float maxU, float minV, float minU, float maxV, int width, int height, float thickness, TesrMaterial material, Operation<Void> original) {
|
||||
if (!basePart) {
|
||||
callIcon(GLINT_ARGS, original, t, maxU, minV, minU, maxV, width, height, thickness);
|
||||
return;
|
||||
@@ -146,8 +157,22 @@ public final class DroppedItemInstancer {
|
||||
}
|
||||
final long drawsBefore = GLStateManager.drawCalls;
|
||||
if (allowed) {
|
||||
currentGlintProperties.set(maxU, minV, minU, maxV, width, height, thickness);
|
||||
if (glintSeen && glintProperties.equals(currentGlintProperties) && ModelPartBatcher.INSTANCE.replayGlint(glintCapture)) {
|
||||
glintInstanced++;
|
||||
return;
|
||||
}
|
||||
final TemplateBuffer template = iconTemplate(GLINT_ARGS, maxU, minV, minU, maxV, width, height, thickness, original);
|
||||
if (template != null && ModelPartBatcher.INSTANCE.queueTemplate(template, EntityMaterials.GLINT)) {
|
||||
final boolean capture = !glintSeen && ModelPartBatcher.INSTANCE.beginGlintCapture(glintCapture);
|
||||
if (capture) glintProperties.set(currentGlintProperties);
|
||||
glintSeen = true;
|
||||
final boolean queued;
|
||||
try {
|
||||
queued = template != null && ModelPartBatcher.INSTANCE.queueTemplate(template, material);
|
||||
} finally {
|
||||
if (capture) glintCapture.end();
|
||||
}
|
||||
if (queued) {
|
||||
glintInstanced++;
|
||||
BatchEligibility.onPartQueued();
|
||||
return;
|
||||
@@ -161,6 +186,22 @@ public final class DroppedItemInstancer {
|
||||
BatchEligibility.onPartFallback(drawsBefore, GLStateManager.drawCalls);
|
||||
}
|
||||
|
||||
public static boolean queueSkippedGlint(int iconWidth, int iconHeight) {
|
||||
if (!basePart || !BatchEligibility.batchingAllowed()) return false;
|
||||
if (glintTemplate == null || glintTemplateWidth != iconWidth || glintTemplateHeight != iconHeight) {
|
||||
final IconMesh hit = icons.get(0.0F, 0.0F, 1.0F, 1.0F, iconWidth, iconHeight, 0.0625F);
|
||||
if (hit == null || hit.template == null) return false;
|
||||
glintTemplate = hit.template;
|
||||
glintTemplateWidth = iconWidth;
|
||||
glintTemplateHeight = iconHeight;
|
||||
}
|
||||
if (!ModelPartBatcher.INSTANCE.queueSkippedHeldGlint(glintTemplate)) return false;
|
||||
glintInstanced += 2;
|
||||
BatchEligibility.onPartQueued();
|
||||
BatchEligibility.onPartQueued();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void block(ItemStack stack, RenderBlocks rb, Block block, int meta, float brightness, boolean unfilteredAtlas, Operation<Void> original) {
|
||||
final TesrMaterial material = material(stack, unfilteredAtlas);
|
||||
if (material == null) {
|
||||
@@ -211,6 +252,9 @@ public final class DroppedItemInstancer {
|
||||
blocks.clear();
|
||||
blockKey.block = null;
|
||||
basePart = false;
|
||||
glintCapture.reset();
|
||||
glintSeen = false;
|
||||
glintTemplate = null;
|
||||
if (blockBackend != null) {
|
||||
blockBackend.delete();
|
||||
blockBackend = null;
|
||||
@@ -292,7 +336,9 @@ public final class DroppedItemInstancer {
|
||||
|
||||
private static BailReason blockStateBail(RenderBlocks renderBlocks, Block block, float brightness) {
|
||||
if (BlockRenderListManager.isISBRH(block.getRenderType())) return BailReason.ISBRH;
|
||||
if (renderBlocks.enableAO || renderBlocks.overrideBlockTexture != null || brightness != 1.0F || !renderBlocks.useInventoryTint || renderBlocks.blockAccess != null || (renderBlocks.uvRotateEast | renderBlocks.uvRotateWest | renderBlocks.uvRotateSouth | renderBlocks.uvRotateNorth | renderBlocks.uvRotateTop | renderBlocks.uvRotateBottom) != 0) return BailReason.BLOCK_STATE;
|
||||
if (renderBlocks.enableAO || renderBlocks.overrideBlockTexture != null || brightness != 1.0F || !renderBlocks.useInventoryTint
|
||||
|| renderBlocks.blockAccess != null || (renderBlocks.uvRotateEast | renderBlocks.uvRotateWest | renderBlocks.uvRotateSouth
|
||||
| renderBlocks.uvRotateNorth | renderBlocks.uvRotateTop | renderBlocks.uvRotateBottom) != 0) return BailReason.BLOCK_STATE;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.gtnewhorizons.angelica.rendering.items;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CombinedGlint;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import jss.notfine.core.Settings;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.IOException;
|
||||
|
||||
/** One draw for the two vanilla held-item layers; modified glint sequences retain their original draws. */
|
||||
public final class HeldItemGlint {
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/misc/enchanted_item_glint.png");
|
||||
private static final Matrix4f first = new Matrix4f();
|
||||
private static final Matrix4f second = new Matrix4f();
|
||||
private static long matrixTime = Long.MIN_VALUE;
|
||||
private static boolean listening, checked, opaque;
|
||||
|
||||
private HeldItemGlint() {}
|
||||
|
||||
private static boolean batchedGlintGuarded() {
|
||||
return ModelPartBatcher.INSTANCE.entityPassActive() && ModelPartBatcher.INSTANCE.canCaptureGlint() && canResetStencil();
|
||||
}
|
||||
|
||||
public static boolean needsImmediateBase(ItemStack stack, int pass) {
|
||||
return stack.hasEffect(pass) && (boolean) Settings.MODE_GLINT_WORLD.option.getStore() && !ModelPartBatcher.INSTANCE.isShadowPass() && !batchedGlintGuarded();
|
||||
}
|
||||
|
||||
public static boolean layersCoverSamePixels() {
|
||||
return opaqueTexture(Minecraft.getMinecraft());
|
||||
}
|
||||
|
||||
public static boolean canResetStencil() {
|
||||
if (!GLStateManager.getStencilTest().isEnabled()) return true;
|
||||
final var stencil = GLStateManager.getStencilState();
|
||||
return stencil.getFuncFront() == GL11.GL_ALWAYS && stencil.getFuncBack() == GL11.GL_ALWAYS && stencil.getFailOpFront() == GL11.GL_KEEP && stencil.getFailOpBack() == GL11.GL_KEEP && stencil.getZFailOpFront() == GL11.GL_KEEP
|
||||
&& stencil.getZFailOpBack() == GL11.GL_KEEP && stencil.getZPassOpFront() == GL11.GL_KEEP && stencil.getZPassOpBack() == GL11.GL_KEEP;
|
||||
}
|
||||
|
||||
public static boolean eligible() {
|
||||
return AngelicaConfig.enableEntityBatching && GLStateManager.getActiveProgram() == 0 && (boolean) Settings.MODE_GLINT_WORLD.option.getStore() && opaqueTexture(Minecraft.getMinecraft());
|
||||
}
|
||||
|
||||
public static boolean begin() {
|
||||
if (!eligible()) return false;
|
||||
final var mc = Minecraft.getMinecraft();
|
||||
final var texture = mc.getTextureManager().getTexture(TEXTURE);
|
||||
if (texture == null || texture.getGlTextureId() != GLStateManager.getTextures().getTextureUnitBindings(0).getBinding()) return false;
|
||||
return firstMatrix().equals(GLStateManager.getTextures().getTextureUnitMatrix(0), 0.000001f) && CombinedGlint.begin(secondMatrix());
|
||||
}
|
||||
|
||||
public static ResourceLocation texture() {
|
||||
return TEXTURE;
|
||||
}
|
||||
|
||||
public static Matrix4f firstMatrix() {
|
||||
updateMatrices();
|
||||
return first;
|
||||
}
|
||||
|
||||
public static Matrix4f secondMatrix() {
|
||||
updateMatrices();
|
||||
return second;
|
||||
}
|
||||
|
||||
private static void updateMatrices() {
|
||||
final long time = GlintClock.millis();
|
||||
if (matrixTime == time) return;
|
||||
matrixTime = time;
|
||||
first.scaling(0.125f).translate((float) (time % 3000L) / 3000.0f * 8.0f, 0, 0).rotate((float) Math.toRadians(-50), 0, 0, 1);
|
||||
second.scaling(0.125f).translate(-(float) (time % 4873L) / 4873.0f * 8.0f, 0, 0).rotate((float) Math.toRadians(10), 0, 0, 1);
|
||||
}
|
||||
|
||||
private static boolean opaqueTexture(Minecraft mc) {
|
||||
if (!listening) {
|
||||
listening = true;
|
||||
((IReloadableResourceManager) mc.getResourceManager()).registerReloadListener(manager -> checked = false);
|
||||
}
|
||||
if (checked) return opaque;
|
||||
checked = true;
|
||||
opaque = false;
|
||||
try (var stream = mc.getResourceManager().getResource(TEXTURE).getInputStream()) {
|
||||
final var image = ImageIO.read(stream);
|
||||
if (image == null) return false;
|
||||
for (int y = 0; y < image.getHeight(); y++) for (int x = 0; x < image.getWidth(); x++) {
|
||||
if ((image.getRGB(x, y) >>> 24) != 255) return false;
|
||||
}
|
||||
opaque = true;
|
||||
} catch (IOException | RuntimeException e) {
|
||||
GLStateManager.LOGGER.debug("Could not inspect held glint texture; keeping separate layers", e);
|
||||
}
|
||||
return opaque;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public final class BatchEligibility {
|
||||
expectedDraws = 0L;
|
||||
foreignDraws = 0L;
|
||||
parts = 0;
|
||||
bracketDepth = 0;
|
||||
allowed = state == SAFE;
|
||||
return allowed;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ public final class EntityMaterials {
|
||||
public static final TesrMaterial HELD_BLOCK_CUTOUT = TesrMaterial.builder().cutout(0.1f).stream().build();
|
||||
public static final TesrMaterial HELD_BLOCK_TRANSLUCENT = TesrMaterial.builder().translucent().cutout(0.1f).stream().build();
|
||||
static final TesrMaterial OVERLAY = TesrMaterial.builder().translucent().depthEqual().stream().build();
|
||||
public static final TesrMaterial GLINT = TesrMaterial.builder().glint().depthEqual().noDepthWrite().unlit().stream()
|
||||
.special(TesrMaterial.SpecialRender.GLINT).build();
|
||||
public static final TesrMaterial GLINT = TesrMaterial.builder().glint().depthEqual().noDepthWrite().unlit().stream().special(TesrMaterial.SpecialRender.GLINT).build();
|
||||
public static final TesrMaterial ITEM_GLINT = TesrMaterial.builder().glint().depthEqual().noDepthWrite().unlit().stream().special(TesrMaterial.SpecialRender.GLINT).build();
|
||||
|
||||
private static final BlendState effectiveBlend = new BlendState();
|
||||
private static final AlphaState effectiveAlpha = new AlphaState();
|
||||
@@ -39,13 +39,17 @@ public final class EntityMaterials {
|
||||
final boolean textured = GLStateManager.getTextures().getTextureUnitStates(0).isEnabled();
|
||||
final BlendState blend = GLStateManager.getEffectiveBlendState(effectiveBlend);
|
||||
final AlphaState alpha = GLStateManager.getEffectiveAlphaState(effectiveAlpha);
|
||||
return fromState(textured, textured && !texIdentity, GLStateManager.isEffectiveBlendEnabled(), blend.getSrcRgb(), blend.getDstRgb(), GLStateManager.isEffectiveAlphaTestEnabled(), alpha.getFunction(), alpha.getReference(), GLStateManager.getDepthState().getFunc(),GLStateManager.isEffectiveDepthMaskEnabled());
|
||||
return fromState(textured, textured && !texIdentity, GLStateManager.isEffectiveBlendEnabled(), blend.getSrcRgb(), blend.getDstRgb(),
|
||||
GLStateManager.isEffectiveAlphaTestEnabled(), alpha.getFunction(), alpha.getReference(), GLStateManager.getDepthState().getFunc(),
|
||||
GLStateManager.isEffectiveDepthMaskEnabled());
|
||||
}
|
||||
|
||||
public static TesrMaterial itemFromCurrentState(boolean unfilteredAtlas) {
|
||||
final BlendState blend = GLStateManager.getEffectiveBlendState(effectiveBlend);
|
||||
final AlphaState alpha = GLStateManager.getEffectiveAlphaState(effectiveAlpha);
|
||||
return itemFromState(unfilteredAtlas, GLStateManager.isEffectiveBlendEnabled(), blend.getSrcRgb(), blend.getDstRgb(), GLStateManager.isEffectiveAlphaTestEnabled(), alpha.getFunction(), alpha.getReference(), GLStateManager.getDepthState().getFunc(),GLStateManager.isEffectiveDepthMaskEnabled());
|
||||
return itemFromState(unfilteredAtlas, GLStateManager.isEffectiveBlendEnabled(), blend.getSrcRgb(), blend.getDstRgb(),
|
||||
GLStateManager.isEffectiveAlphaTestEnabled(), alpha.getFunction(), alpha.getReference(), GLStateManager.getDepthState().getFunc(),
|
||||
GLStateManager.isEffectiveDepthMaskEnabled());
|
||||
}
|
||||
|
||||
static TesrMaterial itemFromState(boolean unfilteredAtlas, boolean blend, int srcRgb, int dstRgb, boolean alphaTest, int alphaFunc, float alphaRef, int depthFunc, boolean depthMask) {
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FragmentKey;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Reuses captured instance records across armor and item glint material passes. */
|
||||
public final class GlintCapture {
|
||||
private static final class Range {
|
||||
RetainedTesrGroups.Group group;
|
||||
RetainedTesrGroups.InstanceColumns columns;
|
||||
RetainedTesrGroups.TexRun source;
|
||||
int start, end, instances;
|
||||
}
|
||||
|
||||
private final ObjectArrayList<Range> ranges = new ObjectArrayList<>();
|
||||
private final Matrix4f parent = new Matrix4f();
|
||||
private final long[] fragment = new long[4], scratch = new long[4];
|
||||
private final BlendState blend = new BlendState(), scratchBlend = new BlendState();
|
||||
private boolean blendEnabled, depthMask;
|
||||
private int depthFunc;
|
||||
private int used, texture, color, light, overlay, fragmentLength;
|
||||
private long draws;
|
||||
private long pass;
|
||||
private RetainedTesrGroups owner;
|
||||
private boolean valid;
|
||||
private boolean base;
|
||||
private boolean trusted;
|
||||
private boolean poseRestored;
|
||||
private int restoredGeneration;
|
||||
private boolean uniformLight;
|
||||
|
||||
public void reset() {
|
||||
if (owner != null && owner.glintCapture == this) owner.glintCapture = null;
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
range.group = null;
|
||||
range.columns = null;
|
||||
range.source = null;
|
||||
}
|
||||
used = 0;
|
||||
valid = false;
|
||||
owner = null;
|
||||
base = false;
|
||||
trusted = false;
|
||||
poseRestored = false;
|
||||
}
|
||||
|
||||
boolean begin(RetainedTesrGroups groups) {
|
||||
if (!open(groups)) return false;
|
||||
parent.set(GLStateManager.getModelViewMatrix());
|
||||
texture = GLStateManager.getBoundTextureForServerState();
|
||||
color = currentColor();
|
||||
light = GLSMConfig.packedLastBrightness();
|
||||
overlay = currentOverlay();
|
||||
fragmentLength = FragmentKey.packFromState(fragment);
|
||||
GLStateManager.getEffectiveBlendState(blend);
|
||||
blendEnabled = GLStateManager.isEffectiveBlendEnabled();
|
||||
depthMask = GLStateManager.isEffectiveDepthMaskEnabled();
|
||||
depthFunc = GLStateManager.getDepthState().getFunc();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base passes are only copied under the same parent pose. Light and overlay are recorded so the glint can draw the base instances in place when every part still carries them.
|
||||
*/
|
||||
boolean beginBase(RetainedTesrGroups groups) {
|
||||
if (!open(groups)) return false;
|
||||
parent.set(GLStateManager.getModelViewMatrix());
|
||||
light = GLSMConfig.packedLastBrightness();
|
||||
overlay = currentOverlay();
|
||||
base = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** For records queued without running model or mod code; the caller replays before any GL state changes. */
|
||||
boolean beginTrusted(RetainedTesrGroups groups) {
|
||||
if (!open(groups)) return false;
|
||||
trusted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean open(RetainedTesrGroups groups) {
|
||||
if (groups.glintCapture != null) return false;
|
||||
reset();
|
||||
owner = groups;
|
||||
pass = groups.captureVersion();
|
||||
groups.glintCapture = this;
|
||||
draws = GLStateManager.drawCalls;
|
||||
valid = true;
|
||||
uniformLight = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void invalidate() { valid = false; }
|
||||
void allowBase() { base = true; }
|
||||
boolean capturesBase() { return base && valid; }
|
||||
|
||||
void record(RetainedTesrGroups.Group group, RetainedTesrGroups.InstanceColumns columns, RetainedTesrGroups.TexRun source, int start, int end, int instances) {
|
||||
if (!valid) return;
|
||||
if (!base && group.material != EntityMaterials.GLINT && group.material != EntityMaterials.ITEM_GLINT) {
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
Range range = used > 0 ? ranges.get(used - 1) : null;
|
||||
if (range == null || range.group != group || range.columns != columns || range.source != source || range.end != start) {
|
||||
if (used == ranges.size()) ranges.add(new Range());
|
||||
range = ranges.get(used++);
|
||||
range.group = group;
|
||||
range.columns = columns;
|
||||
range.source = source;
|
||||
range.start = start;
|
||||
range.instances = 0;
|
||||
}
|
||||
range.end = end;
|
||||
range.instances += instances;
|
||||
if (base && uniformLight) {
|
||||
for (int i = start; i < end; i++) {
|
||||
if (columns.lights.getInt(i) != light || columns.overlays.getInt(i) != overlay) {
|
||||
uniformLight = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void end() {
|
||||
close(false);
|
||||
}
|
||||
|
||||
public void endWithRestoredPose() {
|
||||
close(true);
|
||||
}
|
||||
|
||||
private void close(boolean poseRestored) {
|
||||
if (owner == null || owner.glintCapture != this) return;
|
||||
owner.glintCapture = null;
|
||||
valid &= GLStateManager.drawCalls == draws && used > 0;
|
||||
this.poseRestored = poseRestored;
|
||||
restoredGeneration = GLStateManager.getMvGeneration();
|
||||
}
|
||||
|
||||
private boolean sameParentPose() {
|
||||
return poseRestored && restoredGeneration == GLStateManager.getMvGeneration() || parent.equals(GLStateManager.getModelViewMatrix());
|
||||
}
|
||||
|
||||
boolean replay(RetainedTesrGroups groups) {
|
||||
return replay(groups, GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
}
|
||||
|
||||
boolean replay(RetainedTesrGroups groups, Matrix4f textureMatrix) {
|
||||
if (base || !valid || owner != groups || pass != groups.captureVersion() || groups.glintCapture != null || GLStateManager.drawCalls != draws) return false;
|
||||
if (!trusted && (blendEnabled != GLStateManager.isEffectiveBlendEnabled() || depthMask != GLStateManager.isEffectiveDepthMaskEnabled() || depthFunc != GLStateManager.getDepthState().getFunc()
|
||||
|| !blend.sameAs(GLStateManager.getEffectiveBlendState(scratchBlend)) || !sameParentPose() || texture != GLStateManager.getBoundTextureForServerState() || color != currentColor() || light != GLSMConfig.packedLastBrightness()
|
||||
|| fragmentLength != FragmentKey.packFromState(scratch) || !Arrays.equals(fragment, 0, fragmentLength, scratch, 0, fragmentLength))) return false;
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
if (!groups.captureStillValid(range.group)) return false;
|
||||
}
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
range.group.replayRange(range.columns, range.source, range.start, range.end, range.instances, textureMatrix);
|
||||
}
|
||||
valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean copyBase(ModelPartBatcher batcher, RetainedTesrGroups groups) {
|
||||
if (!baseStillValid(groups) || EntityMaterials.fromCurrentState(false) != EntityMaterials.GLINT || !batcher.prepareArmorGlint()) return false;
|
||||
final boolean inPlace = sameLightAndOverlay();
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
batcher.copyArmorGlint(range.columns, range.start, range.end, inPlace);
|
||||
}
|
||||
valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean queueBothLayers(ModelPartBatcher batcher, RetainedTesrGroups groups, ResourceLocation glintTexture) {
|
||||
if (!baseStillValid(groups) || !sameLightAndOverlay() || !batcher.prepareSkippedArmorGlint(glintTexture)) return false;
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
batcher.queueArmorGlintLayers(range.columns, range.source, range.start, range.end);
|
||||
}
|
||||
valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean baseStillValid(RetainedTesrGroups groups) {
|
||||
if (!valid || !base || owner != groups || pass != groups.captureVersion() || !groups.canCopyInstances(EntityMaterials.GLINT) || !sameParentPose()) return false;
|
||||
for (int i = 0; i < used; i++) {
|
||||
final Range range = ranges.get(i);
|
||||
if (!groups.captureStillValid(range.group) || range.end > range.columns.size) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean sameLightAndOverlay() {
|
||||
return uniformLight && light == GLSMConfig.packedLastBrightness() && overlay == currentOverlay();
|
||||
}
|
||||
|
||||
private static int currentColor() {
|
||||
final var c = GLStateManager.getColor();
|
||||
return AngelicaBufferSource.packAbgr(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
|
||||
}
|
||||
|
||||
private static int currentOverlay() {
|
||||
return AngelicaBufferSource.packAbgr(GLStateManager.getOverlayR(), GLStateManager.getOverlayG(), GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public final class InstanceRing {
|
||||
private OrphanStreamingBuffer orphanRing;
|
||||
private boolean initialized;
|
||||
private boolean usedThisFrame;
|
||||
private boolean lastUploadKept;
|
||||
private int bufferId;
|
||||
|
||||
public long upload(ByteBuffer data, int stride) {
|
||||
@@ -23,15 +24,21 @@ public final class InstanceRing {
|
||||
final int index = persistentRing.upload(data, stride);
|
||||
if (index >= 0) {
|
||||
bufferId = persistentRing.getBufferId();
|
||||
lastUploadKept = true;
|
||||
return (long) index * stride;
|
||||
}
|
||||
}
|
||||
if (orphanRing == null) orphanRing = new OrphanStreamingBuffer();
|
||||
orphanRing.upload(data);
|
||||
bufferId = orphanRing.getBufferId();
|
||||
lastUploadKept = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int keptUploadsEpoch() {
|
||||
return lastUploadKept && persistentRing != null ? persistentRing.getWraps() : -1;
|
||||
}
|
||||
|
||||
public int bufferId() {
|
||||
return bufferId;
|
||||
}
|
||||
@@ -54,6 +61,7 @@ public final class InstanceRing {
|
||||
}
|
||||
initialized = false;
|
||||
usedThisFrame = false;
|
||||
lastUploadKept = false;
|
||||
bufferId = 0;
|
||||
}
|
||||
}
|
||||
+83
-11
@@ -65,10 +65,15 @@ final class InstancedTemplateRenderer {
|
||||
}
|
||||
|
||||
int drawTemplates(InstanceColumns cols, TexRun run, long nowMs) {
|
||||
return drawTemplates(cols, run, nowMs, false);
|
||||
}
|
||||
|
||||
int drawTemplates(InstanceColumns cols, TexRun run, long nowMs, boolean recordUpload) {
|
||||
final InstanceColumns data = run.source != null ? run.source : cols;
|
||||
final long epoch = ++bucketEpochSource;
|
||||
for (TexRun seg = run; seg != null; seg = seg.next) {
|
||||
for (int i = seg.start, end = seg.end; i < end; i++) {
|
||||
final TemplateBuffer template = cols.templates.get(i);
|
||||
final TemplateBuffer template = data.templates.get(i);
|
||||
final Bucket bucket;
|
||||
if (template.bucketEpoch == epoch) {
|
||||
bucket = liveBuckets.get(template.bucketIndex);
|
||||
@@ -90,12 +95,19 @@ final class InstancedTemplateRenderer {
|
||||
final IntArrayList indices = liveBuckets.get(b).indices;
|
||||
for (int j = 0, m = indices.size(); j < m; j++) {
|
||||
final int i = indices.getInt(j);
|
||||
InstancedAttribs.writeHead(ptr, cols.matrices, i * 16, cols.colors.getInt(i), cols.overlays.getInt(i), cols.infos.getLong(i));
|
||||
InstancedAttribs.writeLightmap(ptr + InstancedAttribs.OFFSET_LIGHTMAP, cols.lights.getInt(i));
|
||||
final int color = run.source != null ? run.color : data.colors.getInt(i);
|
||||
final long info = run.source != null ? 0L : data.infos.getLong(i);
|
||||
InstancedAttribs.writeHead(ptr, data.matrices, i * 16, color, data.overlays.getInt(i), info);
|
||||
InstancedAttribs.writeLightmap(ptr + InstancedAttribs.OFFSET_LIGHTMAP, data.lights.getInt(i));
|
||||
ptr += InstancedAttribs.STRIDE;
|
||||
}
|
||||
}
|
||||
final long ringBase = uploadStaging(base, ptr, InstancedAttribs.STRIDE);
|
||||
if (recordUpload) {
|
||||
run.uploadOffset = ringBase;
|
||||
run.uploadMeshes.clear();
|
||||
run.uploadCounts.clear();
|
||||
}
|
||||
|
||||
int draws = 0;
|
||||
long recordOffset = ringBase;
|
||||
@@ -104,6 +116,10 @@ final class InstancedTemplateRenderer {
|
||||
for (int b = 0, n = liveBuckets.size(); b < n; b++) {
|
||||
final Bucket bucket = liveBuckets.get(b);
|
||||
final TemplateMesh mesh = bucket.mesh;
|
||||
if (recordUpload) {
|
||||
run.uploadMeshes.add(mesh);
|
||||
run.uploadCounts.add(bucket.indices.size());
|
||||
}
|
||||
bindRingTo(mesh.vao, TEMPLATE_FLAGS);
|
||||
InstancedAttribs.pointTemplate(recordOffset);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
@@ -120,22 +136,52 @@ final class InstancedTemplateRenderer {
|
||||
return draws;
|
||||
}
|
||||
|
||||
void drawCubes(InstanceColumns cols, TexRun run) {
|
||||
/**
|
||||
* Draws a base run's recorded template upload again, every instance in one color.
|
||||
*/
|
||||
int redrawTemplates(TexRun base, int colorABGR) {
|
||||
long recordOffset = base.uploadOffset;
|
||||
neutralizeExtendedAttribs();
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
for (int b = 0, n = base.uploadMeshes.size(); b < n; b++) {
|
||||
final TemplateMesh mesh = (TemplateMesh) base.uploadMeshes.get(b);
|
||||
final int count = base.uploadCounts.getInt(b);
|
||||
bindRingTo(mesh.vao, TEMPLATE_FLAGS);
|
||||
InstancedAttribs.pointTemplate(recordOffset);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
setConstantColor(colorABGR);
|
||||
GLStateManager.glDrawArraysInstanced(mesh.drawMode, 0, mesh.vertexCount, count);
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
recordOffset += (long) count * InstancedAttribs.STRIDE;
|
||||
}
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
return base.uploadMeshes.size();
|
||||
}
|
||||
|
||||
private static void setConstantColor(int colorABGR) {
|
||||
GLStateManager.glDisableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
GLStateManager.glVertexAttrib4f(InstancedAttribs.LOC_COLOR, (colorABGR & 0xFF) / 255.0f, (colorABGR >>> 8 & 0xFF) / 255.0f, (colorABGR >>> 16 & 0xFF) / 255.0f, (colorABGR >>> 24) / 255.0f);
|
||||
}
|
||||
|
||||
/** Returns the ring offset of the uploaded instances, for {@link #redrawCubes}. */
|
||||
long drawCubes(InstanceColumns cols, TexRun run) {
|
||||
final InstanceColumns data = run.source != null ? run.source : cols;
|
||||
staging = MeshBuffer.ensureCapacity(staging, run.instances * CubeInstancedAttribs.STRIDE, false);
|
||||
final long base = memAddress0(staging);
|
||||
long ptr = base;
|
||||
for (TexRun seg = run; seg != null; seg = seg.next) {
|
||||
for (int i = seg.start, end = seg.end; i < end; i++) {
|
||||
final int off = i * 16;
|
||||
final CubeParams[] partCubes = cols.cubes.get(i);
|
||||
final float scale = cols.scales.getFloat(i);
|
||||
final int color = cols.colors.getInt(i);
|
||||
final int light = cols.lights.getInt(i);
|
||||
final int overlay = cols.overlays.getInt(i);
|
||||
final long info = cols.infos.getLong(i);
|
||||
final CubeParams[] partCubes = data.cubes.get(i);
|
||||
final float scale = data.scales.getFloat(i);
|
||||
final int color = run.source != null ? run.color : data.colors.getInt(i);
|
||||
final int light = data.lights.getInt(i);
|
||||
final int overlay = data.overlays.getInt(i);
|
||||
final long info = run.source != null ? 0L : data.infos.getLong(i);
|
||||
for (int p = 0, n = partCubes.length; p < n; p++) {
|
||||
final CubeParams cube = partCubes[p];
|
||||
cube.writeRows(ptr, cols.matrices, off, scale);
|
||||
cube.writeRows(ptr, data.matrices, off, scale);
|
||||
InstancedAttribs.writeTail(ptr, color, overlay, info);
|
||||
InstancedAttribs.writeLightmap(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE, light);
|
||||
cube.writeTexture(ptr);
|
||||
@@ -153,6 +199,32 @@ final class InstancedTemplateRenderer {
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, UnitCubeMesh.VERTEX_COUNT, run.instances);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
return ringBase;
|
||||
}
|
||||
|
||||
boolean canRedraw(int ringEpoch, int buffer) {
|
||||
return ringEpoch >= 0 && ringEpoch == ring.keptUploadsEpoch() && buffer == ring.bufferId();
|
||||
}
|
||||
|
||||
int ringEpoch() {
|
||||
return ring.keptUploadsEpoch();
|
||||
}
|
||||
|
||||
int ringBuffer() {
|
||||
return ring.bufferId();
|
||||
}
|
||||
|
||||
void redrawCubes(long offset, int instances, int colorABGR) {
|
||||
neutralizeExtendedAttribs();
|
||||
bindRingTo(UnitCubeMesh.vao(), UnitCubeMesh.VERTEX_FLAGS);
|
||||
CubeInstancedAttribs.pointInstanceAttribs(offset);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
setConstantColor(colorABGR);
|
||||
GLStateManager.ffpInstancing = Instancing.CUBE;
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, UnitCubeMesh.VERTEX_COUNT, instances);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private long uploadStaging(long base, long end, int stride) {
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ModelBoxData;
|
||||
import com.gtnewhorizons.angelica.profiling.BailClassCounts;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.rendering.items.HeldItemGlint;
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
@@ -41,6 +43,7 @@ import net.minecraft.world.World;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -91,17 +94,27 @@ public final class ModelPartBatcher {
|
||||
private boolean instancedThisCycle;
|
||||
private DeferredWorldRenderingPipeline shaderPipeline;
|
||||
private long lastSweepMs;
|
||||
private RenderLayer armorGlintLayer;
|
||||
private final Matrix4f armorGlintTexture = new Matrix4f();
|
||||
private int armorGlintColor, armorGlintLight, armorGlintOverlay;
|
||||
private static final int VANILLA_GLINT_COLOR = AngelicaBufferSource.packAbgr(0.5F * 0.76F, 0.25F * 0.76F, 0.8F * 0.76F, 1.0F);
|
||||
|
||||
long parts;
|
||||
long liveFallbacks;
|
||||
private long armorBaseReuses, glintPassReuses, armorLoopSkips, armorSectionSkips;
|
||||
private final long[] bails = new long[BailReason.VALUES.length];
|
||||
private long lastParts, lastLiveFallbacks;
|
||||
|
||||
public long statParts() { return parts; }
|
||||
public long statArmorBaseReuses() { return armorBaseReuses; }
|
||||
public long statGlintPassReuses() { return glintPassReuses; }
|
||||
public long statArmorLoopSkips() { return armorLoopSkips; }
|
||||
public long statArmorSectionSkips() { return armorSectionSkips; }
|
||||
public long statLiveFallbacks() { return liveFallbacks; }
|
||||
public long statInstancedDraws() { return groups.instancedDraws + shadowGroups.instancedDraws; }
|
||||
public long statInstancedInstances() { return groups.instancedInstances + shadowGroups.instancedInstances; }
|
||||
public long statCubeInstances() { return groups.cubeInstances + shadowGroups.cubeInstances; }
|
||||
public long statRedrawnInstances() { return groups.redrawnInstances + shadowGroups.redrawnInstances; }
|
||||
public long statTexMatrixRuns() { return groups.texMatrixRuns + shadowGroups.texMatrixRuns; }
|
||||
public long statBail(BailReason reason) { return bails[reason.ordinal()]; }
|
||||
public long statShadowQuads() { return shadows.statQuads(); }
|
||||
@@ -131,6 +144,11 @@ public final class ModelPartBatcher {
|
||||
int cull;
|
||||
boolean lit;
|
||||
|
||||
boolean matches(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit) {
|
||||
return this.texture == texture && this.material == material && pass.equals(this.pass) && this.offsetFactor == offsetFactor
|
||||
&& this.offsetUnits == offsetUnits && this.glintSlot == glintSlot && this.cull == cull && this.lit == lit;
|
||||
}
|
||||
|
||||
LayerKey set(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit) {
|
||||
this.texture = texture;
|
||||
this.material = material;
|
||||
@@ -167,14 +185,10 @@ public final class ModelPartBatcher {
|
||||
|
||||
private final Object2ObjectOpenHashMap<LayerKey, RenderLayer> layers = new Object2ObjectOpenHashMap<>();
|
||||
private final LayerKey scratchKey = new LayerKey();
|
||||
private ResourceLocation lastLayerTexture;
|
||||
private TesrMaterial lastLayerMaterial;
|
||||
private PassOverride lastLayerPass;
|
||||
private float lastLayerOffsetFactor;
|
||||
private float lastLayerOffsetUnits;
|
||||
private int lastLayerGlintSlot;
|
||||
private int lastLayerCull;
|
||||
private boolean lastLayerLit;
|
||||
private static final int RECENT_LAYERS = 8;
|
||||
private final LayerKey[] recentKeys = new LayerKey[RECENT_LAYERS];
|
||||
private final RenderLayer[] recentLayers = new RenderLayer[RECENT_LAYERS];
|
||||
private int recentNext;
|
||||
private RenderLayer lastLayer;
|
||||
|
||||
private static ResourceLocation lastBoundLocation;
|
||||
@@ -262,6 +276,126 @@ public final class ModelPartBatcher {
|
||||
return active;
|
||||
}
|
||||
|
||||
public boolean beginGlintCapture(GlintCapture capture) {
|
||||
return canCaptureGlint() && capture.begin(activeGroups);
|
||||
}
|
||||
|
||||
public boolean canCaptureGlint() {
|
||||
return active && instancedThisCycle && shaderPipeline == null && !shadow && BatchEligibility.batchingAllowed() && GLStateManager.getActiveProgram() == 0 && !GLStateManager.isRecordingDisplayList();
|
||||
}
|
||||
|
||||
public boolean replayGlint(GlintCapture capture) {
|
||||
return replayGlint(capture, GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
}
|
||||
|
||||
public boolean queueSecondArmorGlint(GlintCapture capture) {
|
||||
final Matrix4f second = GlintClock.secondArmorMatrix(GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
if (second == null || !replayGlint(capture, second)) return false;
|
||||
armorLoopSkips++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean replayGlint(GlintCapture capture, Matrix4f textureMatrix) {
|
||||
if (!active || shaderPipeline != null || shadow || !BatchEligibility.batchingAllowed() || GLStateManager.getActiveProgram() != 0 || GLStateManager.isRecordingDisplayList()) return false;
|
||||
if (!capture.replay(activeGroups, textureMatrix)) return false;
|
||||
glintPassReuses++;
|
||||
BatchEligibility.onPartQueued();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean beginArmorCapture(GlintCapture capture) {
|
||||
return canCaptureGlint() && capture.beginBase(activeGroups);
|
||||
}
|
||||
|
||||
public boolean reuseArmorBase(GlintCapture base, GlintCapture glint) {
|
||||
if (!canCaptureGlint() || !glint.beginTrusted(activeGroups)) return false;
|
||||
final boolean copied;
|
||||
try {
|
||||
copied = base.copyBase(this, activeGroups);
|
||||
} finally {
|
||||
glint.end();
|
||||
}
|
||||
if (!copied) {
|
||||
glint.reset();
|
||||
return false;
|
||||
}
|
||||
armorBaseReuses++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean queueSkippedArmorGlint(GlintCapture base, ResourceLocation glintTexture) {
|
||||
if (!canCaptureGlint() || GLStateManager.getActiveTextureUnit() != 0 || !GLStateManager.getTextures().getTextureUnitStates(0).isEnabled() || !base.queueBothLayers(this, activeGroups, glintTexture)) return false;
|
||||
armorBaseReuses++;
|
||||
glintPassReuses++;
|
||||
armorSectionSkips++;
|
||||
BatchEligibility.onPartQueued();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean queueSkippedHeldGlint(TemplateBuffer template) {
|
||||
if (!canCaptureGlint() || GLStateManager.getActiveTextureUnit() != 0 || !activeGroups.canCopyInstances(EntityMaterials.ITEM_GLINT) || !MatrixHelper.isIdentity(GLStateManager.getTextures().getTextureUnitMatrix(0))) return false;
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final int cullCode = EntityMaterials.ITEM_GLINT.isNoCull() ? DrawState.DISABLED : DrawState.liveCull();
|
||||
final RenderLayer layer = layerFor(HeldItemGlint.texture(), EntityMaterials.ITEM_GLINT, PassOverride.NONE, offset ? polygon.getOffsetFactor() : 0, offset ? polygon.getOffsetUnits() : 0, ShaderGlint.NO_TINT, cullCode, false);
|
||||
activeGroups.queueGlintLayers(template, layer, EntityMaterials.ITEM_GLINT, currentModelView(), GLSMConfig.packedLastBrightness(), VANILLA_GLINT_COLOR, currentOverlay(), HeldItemGlint.firstMatrix(), HeldItemGlint.secondMatrix());
|
||||
parts++;
|
||||
glintPassReuses++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private Matrix4f currentModelView() {
|
||||
final int mvGen = GLStateManager.getMvGeneration();
|
||||
if (!mvCaptured || mvGen != lastMvGeneration) {
|
||||
modelView.set(GLStateManager.getModelViewMatrix());
|
||||
lastMvGeneration = mvGen;
|
||||
mvCaptured = true;
|
||||
}
|
||||
return modelView;
|
||||
}
|
||||
|
||||
private static int currentOverlay() {
|
||||
return AngelicaBufferSource.packAbgr(GLStateManager.getOverlayR(), GLStateManager.getOverlayG(), GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
}
|
||||
|
||||
boolean prepareArmorGlint() {
|
||||
if (GLStateManager.getBoundTextureForServerState() != lastBoundGlId || lastBoundLocation == null) return false;
|
||||
final Color4 color = GLStateManager.getColor();
|
||||
setArmorGlint(lastBoundLocation, lastBoundGlintSlot, DrawState.liveLit(EntityMaterials.GLINT), GLStateManager.getTextures().getTextureUnitMatrix(0),
|
||||
AngelicaBufferSource.packAbgr(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()));
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean prepareSkippedArmorGlint(ResourceLocation glintTexture) {
|
||||
setArmorGlint(glintTexture, ShaderGlint.NO_TINT, false, GlintClock.firstArmorMatrix(), VANILLA_GLINT_COLOR);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setArmorGlint(ResourceLocation texture, int glintSlot, boolean lit, Matrix4f textureMatrix, int color) {
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final int cullCode = EntityMaterials.GLINT.isNoCull() ? DrawState.DISABLED : DrawState.liveCull();
|
||||
armorGlintLayer = layerFor(texture, EntityMaterials.GLINT, PassOverride.NONE, offset ? polygon.getOffsetFactor() : 0, offset ? polygon.getOffsetUnits() : 0, glintSlot, cullCode, lit);
|
||||
armorGlintTexture.set(textureMatrix);
|
||||
armorGlintColor = color;
|
||||
armorGlintLight = GLSMConfig.packedLastBrightness();
|
||||
armorGlintOverlay = currentOverlay();
|
||||
}
|
||||
|
||||
void copyArmorGlint(RetainedTesrGroups.InstanceColumns base, int start, int end, boolean inPlace) {
|
||||
if (inPlace) {
|
||||
activeGroups.referenceInstances(base, start, end, armorGlintLayer, EntityMaterials.GLINT, armorGlintColor, armorGlintTexture);
|
||||
} else {
|
||||
activeGroups.copyInstances(base, start, end, armorGlintLayer, EntityMaterials.GLINT, armorGlintLight, armorGlintColor, armorGlintOverlay, armorGlintTexture);
|
||||
}
|
||||
parts += end - start;
|
||||
}
|
||||
|
||||
void queueArmorGlintLayers(RetainedTesrGroups.InstanceColumns base, RetainedTesrGroups.TexRun baseRun, int start, int end) {
|
||||
activeGroups.referenceGlintLayers(base, baseRun, start, end, armorGlintLayer, EntityMaterials.GLINT, armorGlintColor, GlintClock.firstArmorMatrix(), GlintClock.secondArmorMatrix());
|
||||
parts += end - start;
|
||||
}
|
||||
|
||||
public boolean entityPassActive() {
|
||||
return active && mode == Mode.ENTITIES && !shadow;
|
||||
}
|
||||
@@ -359,16 +493,12 @@ public final class ModelPartBatcher {
|
||||
final int entityId = groupId(shaderPipeline != null, mode == Mode.ENTITIES || pass.isEntityPhase(), CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity());
|
||||
final Color4 color = GLStateManager.getColor();
|
||||
final int colorABGR = AngelicaBufferSource.packAbgr(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
|
||||
final int overlayABGR = shaderPipeline != null ? AngelicaBufferSource.packEntityColor(CapturedRenderingState.INSTANCE.getCurrentEntityColor()) : AngelicaBufferSource.packAbgr(GLStateManager.getOverlayR(), GLStateManager.getOverlayG(), GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
final long entityInfo = shaderPipeline != null ? InstancedAttribs.packEntityInfo(CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedItem()) : 0L;
|
||||
final int overlayABGR = shaderPipeline != null ? AngelicaBufferSource.packEntityColor(CapturedRenderingState.INSTANCE.getCurrentEntityColor())
|
||||
: AngelicaBufferSource.packAbgr(GLStateManager.getOverlayR(), GLStateManager.getOverlayG(), GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
final long entityInfo = shaderPipeline != null ? InstancedAttribs.packEntityInfo(CapturedRenderingState.INSTANCE.getCurrentRenderedEntity(),
|
||||
CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity(), CapturedRenderingState.INSTANCE.getCurrentRenderedItem()) : 0L;
|
||||
final int packedLight = GLSMConfig.packedLastBrightness();
|
||||
final int mvGen = GLStateManager.getMvGeneration();
|
||||
if (!mvCaptured || mvGen != lastMvGeneration) {
|
||||
modelView.set(GLStateManager.getModelViewMatrix());
|
||||
lastMvGeneration = mvGen;
|
||||
mvCaptured = true;
|
||||
}
|
||||
activeGroups.queue(template, cubes, scale, layer, material, modelView, packedLight, colorABGR, overlayABGR, entityInfo, entityId, texMatrix);
|
||||
activeGroups.queue(template, cubes, scale, layer, material, currentModelView(), packedLight, colorABGR, overlayABGR, entityInfo, entityId, texMatrix);
|
||||
parts++;
|
||||
return true;
|
||||
}
|
||||
@@ -383,7 +513,10 @@ public final class ModelPartBatcher {
|
||||
final boolean textured = GLStateManager.getTextures().getTextureUnitStates(0).isEnabled();
|
||||
final BlendState blendState = GLStateManager.getEffectiveBlendState(bailBlend);
|
||||
final AlphaState alphaState = GLStateManager.getEffectiveAlphaState(bailAlpha);
|
||||
AngelicaMod.LOGGER.info("TESR MATERIAL bail: cls={} shadow={} textured={} texAnimated={} blend={} src={} dst={} alphaTest={} alphaFunc={} alphaRef={} depthFunc={} depthMask={}", cls.getName(), shadow, textured, textured && !unitTexIdentity(), GLStateManager.isEffectiveBlendEnabled(), blendState.getSrcRgb(), blendState.getDstRgb(), GLStateManager.isEffectiveAlphaTestEnabled(), alphaState.getFunction(), alphaState.getReference(), GLStateManager.getDepthState().getFunc(),GLStateManager.isEffectiveDepthMaskEnabled());
|
||||
AngelicaMod.LOGGER.info("TESR MATERIAL bail: cls={} shadow={} textured={} texAnimated={} blend={} src={} dst={} alphaTest={} alphaFunc={} alphaRef={} depthFunc={} depthMask={}",
|
||||
cls.getName(), shadow, textured, textured && !unitTexIdentity(), GLStateManager.isEffectiveBlendEnabled(), blendState.getSrcRgb(),
|
||||
blendState.getDstRgb(), GLStateManager.isEffectiveAlphaTestEnabled(), alphaState.getFunction(), alphaState.getReference(),
|
||||
GLStateManager.getDepthState().getFunc(), GLStateManager.isEffectiveDepthMaskEnabled());
|
||||
}
|
||||
|
||||
static int groupId(boolean piped, boolean entityPhase, int entityId, int blockEntityId) {
|
||||
@@ -458,26 +591,22 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cullCode, boolean lit) {
|
||||
if (texture == lastLayerTexture && material == lastLayerMaterial && pass.equals(lastLayerPass)
|
||||
&& offsetFactor == lastLayerOffsetFactor && offsetUnits == lastLayerOffsetUnits && glintSlot == lastLayerGlintSlot
|
||||
&& cullCode == lastLayerCull && lit == lastLayerLit) {
|
||||
return lastLayer;
|
||||
for (int i = 0; i < RECENT_LAYERS; i++) {
|
||||
final RenderLayer recent = recentLayers[i];
|
||||
if (recent != null && recentKeys[i].matches(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit)) {
|
||||
return lastLayer = recent;
|
||||
}
|
||||
}
|
||||
RenderLayer layer = layers.get(scratchKey.set(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit));
|
||||
if (layer == null) {
|
||||
layer = RenderLayer.tesr(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit);
|
||||
layers.put(new LayerKey().set(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit), layer);
|
||||
}
|
||||
lastLayerTexture = texture;
|
||||
lastLayerMaterial = material;
|
||||
lastLayerPass = pass;
|
||||
lastLayerOffsetFactor = offsetFactor;
|
||||
lastLayerOffsetUnits = offsetUnits;
|
||||
lastLayerGlintSlot = glintSlot;
|
||||
lastLayerCull = cullCode;
|
||||
lastLayerLit = lit;
|
||||
lastLayer = layer;
|
||||
return layer;
|
||||
if (recentKeys[recentNext] == null) recentKeys[recentNext] = new LayerKey();
|
||||
recentKeys[recentNext].set(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cullCode, lit);
|
||||
recentLayers[recentNext] = layer;
|
||||
recentNext = (recentNext + 1) % RECENT_LAYERS;
|
||||
return lastLayer = layer;
|
||||
}
|
||||
|
||||
private void sweepTemplates(long now) {
|
||||
@@ -501,12 +630,8 @@ public final class ModelPartBatcher {
|
||||
shaderPipeline = null;
|
||||
layers.clear();
|
||||
scratchKey.set(null, null, null, 0.0f, 0.0f, ShaderGlint.NO_TINT, DrawState.DISABLED, false);
|
||||
lastLayerTexture = null;
|
||||
lastLayerMaterial = null;
|
||||
lastLayerPass = null;
|
||||
lastLayerGlintSlot = ShaderGlint.NO_TINT;
|
||||
lastLayerCull = DrawState.DISABLED;
|
||||
lastLayerLit = false;
|
||||
Arrays.fill(recentLayers, null);
|
||||
recentNext = 0;
|
||||
lastLayer = null;
|
||||
loggedMaterialBails.clear();
|
||||
lastBoundLocation = null;
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.gtnewhorizons.angelica.api.tesr.TesrMaterial;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.RenderLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CubeParams;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CombinedGlint;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
||||
@@ -19,6 +20,7 @@ import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
@@ -51,6 +53,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
private final float[] matScratch = new float[16];
|
||||
private double camX, camY, camZ;
|
||||
private long frameMark;
|
||||
GlintCapture glintCapture;
|
||||
|
||||
long rebuilds;
|
||||
long retainedDraws;
|
||||
@@ -59,6 +62,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
long instancedDraws;
|
||||
long instancedInstances;
|
||||
long cubeInstances;
|
||||
long redrawnInstances;
|
||||
long texMatrixRuns;
|
||||
|
||||
private InstancedTemplateRenderer instanced;
|
||||
@@ -73,6 +77,17 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
int start, end;
|
||||
int parts, instances;
|
||||
TexRun next, last;
|
||||
TexRun reusedFrom;
|
||||
InstanceColumns source;
|
||||
int color;
|
||||
Matrix4fc matrixSource;
|
||||
boolean glintBase;
|
||||
TexRun sourceRun;
|
||||
long uploadOffset;
|
||||
long uploadPass = -1;
|
||||
int uploadRingEpoch, uploadBuffer;
|
||||
final ObjectArrayList<Object> uploadMeshes = new ObjectArrayList<>();
|
||||
final IntArrayList uploadCounts = new IntArrayList();
|
||||
}
|
||||
|
||||
static final class InstanceColumns {
|
||||
@@ -86,6 +101,11 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
final ObjectArrayList<TexRun> runs = new ObjectArrayList<>();
|
||||
float[] matrices = new float[16 * 64];
|
||||
int size;
|
||||
int referencedParts;
|
||||
|
||||
boolean hasInstances() {
|
||||
return size > 0 || referencedParts > 0;
|
||||
}
|
||||
|
||||
void add(TemplateBuffer templateOrNull, Matrix4fc mv, int light, int color, int overlay, long info) {
|
||||
final int base = size * 16;
|
||||
@@ -107,6 +127,33 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
add(templateOrNull, mv, light, color, overlay, info);
|
||||
}
|
||||
|
||||
void appendRange(InstanceColumns src, int start, int end, int light, int color, int overlay) {
|
||||
final int n = end - start;
|
||||
final int need = (size + n) * 16;
|
||||
if (need > matrices.length) {
|
||||
matrices = Arrays.copyOf(matrices, Math.max(need, matrices.length << 1));
|
||||
}
|
||||
System.arraycopy(src.matrices, start * 16, matrices, size * 16, n * 16);
|
||||
if (src.templates.size() == src.size) {
|
||||
for (int i = start; i < end; i++) templates.add(src.templates.get(i));
|
||||
}
|
||||
if (src.cubes.size() == src.size) {
|
||||
for (int i = start; i < end; i++) cubes.add(src.cubes.get(i));
|
||||
scales.addElements(scales.size(), src.scales.elements(), start, n);
|
||||
}
|
||||
fill(lights, n, light);
|
||||
fill(colors, n, color);
|
||||
fill(overlays, n, overlay);
|
||||
infos.size(infos.size() + n);
|
||||
size += n;
|
||||
}
|
||||
|
||||
private static void fill(IntArrayList list, int n, int value) {
|
||||
final int from = list.size();
|
||||
list.size(from + n);
|
||||
Arrays.fill(list.elements(), from, from + n, value);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
templates.clear();
|
||||
cubes.clear();
|
||||
@@ -117,6 +164,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
infos.clear();
|
||||
runs.clear();
|
||||
size = 0;
|
||||
referencedParts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,14 +216,23 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
TexRun openOrExtendRun(InstanceColumns cols, Matrix4f texMatrix) {
|
||||
return openOrExtendRun(cols, texMatrix, false, false);
|
||||
}
|
||||
|
||||
TexRun openOrExtendRun(InstanceColumns cols, Matrix4f texMatrix, boolean stableMatrix, boolean glintBase) {
|
||||
final ObjectArrayList<TexRun> list = cols.runs;
|
||||
final int part = cols.size;
|
||||
final boolean identity = texMatrix == null;
|
||||
final int size = list.size();
|
||||
final int floor = Math.max(0, mergeRuns ? size - MERGE_SCAN : size - 1);
|
||||
int floor = Math.max(0, mergeRuns ? size - MERGE_SCAN : size - 1);
|
||||
for (int i = size - 1; i >= floor; i--) {
|
||||
final TexRun head = list.get(i);
|
||||
if (head.identity != identity || (!identity && !head.matrix.equals(texMatrix))) continue;
|
||||
if (head.reusedFrom != null || head.source != null) continue;
|
||||
if (head.glintBase != glintBase) {
|
||||
if (opaque) floor = Math.max(0, floor - 1);
|
||||
continue;
|
||||
}
|
||||
if (head.identity != identity || (!identity && head.matrixSource != texMatrix && !head.matrix.equals(texMatrix))) continue;
|
||||
if (head.last.end != part) {
|
||||
final TexRun segment = pooledRun(part);
|
||||
head.last.next = segment;
|
||||
@@ -186,10 +243,69 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
final TexRun head = pooledRun(part);
|
||||
head.identity = identity;
|
||||
if (!identity) head.matrix.set(texMatrix);
|
||||
if (stableMatrix) head.matrixSource = texMatrix;
|
||||
head.glintBase = glintBase;
|
||||
list.add(head);
|
||||
return head;
|
||||
}
|
||||
|
||||
TexRun referenceRun(InstanceColumns cols, InstanceColumns src, TexRun sourceRun, int color, Matrix4f texMatrix, boolean stableMatrix, int start) {
|
||||
final ObjectArrayList<TexRun> list = cols.runs;
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
final TexRun head = list.get(i);
|
||||
if (head.source != src || head.reusedFrom != null || head.color != color || head.matrixSource != texMatrix && !head.matrix.equals(texMatrix)) continue;
|
||||
if (head.last.end != start) {
|
||||
final TexRun segment = pooledRun(start);
|
||||
head.last.next = segment;
|
||||
head.last = segment;
|
||||
}
|
||||
if (head.sourceRun != sourceRun) head.sourceRun = null;
|
||||
return head;
|
||||
}
|
||||
final TexRun head = pooledRun(start);
|
||||
head.identity = false;
|
||||
head.matrix.set(texMatrix);
|
||||
head.source = src;
|
||||
head.sourceRun = sourceRun;
|
||||
head.color = color;
|
||||
if (stableMatrix) head.matrixSource = texMatrix;
|
||||
list.add(head);
|
||||
return head;
|
||||
}
|
||||
|
||||
void replayRange(InstanceColumns cols, TexRun original, int start, int end, int instances, Matrix4f matrix) {
|
||||
replayRange(cols, original, start, end, instances, matrix, false);
|
||||
}
|
||||
|
||||
void replayRange(InstanceColumns cols, TexRun original, int start, int end, int instances, Matrix4f matrix, boolean stableMatrix) {
|
||||
TexRun head = null;
|
||||
for (int i = cols.runs.size() - 1; i >= 0; i--) {
|
||||
final TexRun candidate = cols.runs.get(i);
|
||||
if (candidate.reusedFrom == original && (candidate.matrixSource == matrix || candidate.matrix.equals(matrix))) {
|
||||
head = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (head == null) {
|
||||
head = pooledRun(start);
|
||||
head.matrix.set(matrix);
|
||||
head.identity = false;
|
||||
head.reusedFrom = original;
|
||||
head.source = original.source;
|
||||
head.color = original.color;
|
||||
if (stableMatrix) head.matrixSource = matrix;
|
||||
cols.runs.add(head);
|
||||
} else if (head.last.end != start) {
|
||||
final TexRun segment = pooledRun(start);
|
||||
head.last.next = segment;
|
||||
head.last = segment;
|
||||
}
|
||||
head.sourceRun = original.sourceRun;
|
||||
head.last.end = end;
|
||||
head.parts += end - start;
|
||||
head.instances += instances;
|
||||
}
|
||||
|
||||
private TexRun pooledRun(int part) {
|
||||
if (runPoolUsed == runPool.size()) {
|
||||
runPool.add(new TexRun());
|
||||
@@ -200,6 +316,12 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
run.parts = 0;
|
||||
run.instances = 0;
|
||||
run.next = null;
|
||||
run.reusedFrom = null;
|
||||
run.source = null;
|
||||
run.matrixSource = null;
|
||||
run.glintBase = false;
|
||||
run.sourceRun = null;
|
||||
run.uploadPass = -1;
|
||||
run.last = run;
|
||||
return run;
|
||||
}
|
||||
@@ -225,7 +347,9 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
private final Reference2ObjectOpenHashMap<RenderLayer, Reference2ObjectOpenHashMap<TesrMaterial, Int2ObjectOpenHashMap<Group>>> groups = new Reference2ObjectOpenHashMap<>();
|
||||
private final Reference2ObjectOpenHashMap<RenderLayer, ObjectArrayList<Group>> byLayer = new Reference2ObjectOpenHashMap<>();
|
||||
private Group lastGroup;
|
||||
private static final int RECENT_GROUPS = 4;
|
||||
private final Group[] recentGroups = new Group[RECENT_GROUPS];
|
||||
private int recentGroupNext;
|
||||
|
||||
RetainedTesrGroups(AngelicaBufferSource source) {
|
||||
this(source, System::currentTimeMillis);
|
||||
@@ -245,6 +369,8 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void beginPass(Matrix4f base, double camX, double camY, double camZ, InstancedTemplateRenderer instancedRenderer, TesrInstancingPipeline deferred) {
|
||||
if (glintCapture != null) glintCapture.invalidate();
|
||||
glintCapture = null;
|
||||
baseMV.set(base);
|
||||
baseMVInv.set(base).invert();
|
||||
this.camX = camX;
|
||||
@@ -257,10 +383,23 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
nowMs = clock.getAsLong();
|
||||
}
|
||||
|
||||
boolean captureStillValid(Group group) {
|
||||
return group.frameMark == frameMark;
|
||||
}
|
||||
|
||||
long captureVersion() { return frameMark; }
|
||||
|
||||
private Group groupFor(RenderLayer layer, TesrMaterial material, int id) {
|
||||
final int key = instanceableMaterial(material) ? 0 : id;
|
||||
Group group = lastGroup;
|
||||
if (group == null || group.layer != layer || group.material != material || group.blockEntityId != key) {
|
||||
Group group = null;
|
||||
for (int i = 0; i < RECENT_GROUPS; i++) {
|
||||
final Group recent = recentGroups[i];
|
||||
if (recent != null && recent.layer == layer && recent.material == material && recent.blockEntityId == key) {
|
||||
group = recent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (group == null) {
|
||||
Reference2ObjectOpenHashMap<TesrMaterial, Int2ObjectOpenHashMap<Group>> byMaterial = groups.get(layer);
|
||||
if (byMaterial == null) {
|
||||
byMaterial = new Reference2ObjectOpenHashMap<>();
|
||||
@@ -278,7 +417,8 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
byId.put(key, group);
|
||||
byLayer.computeIfAbsent(layer, l -> new ObjectArrayList<>()).add(group);
|
||||
}
|
||||
lastGroup = group;
|
||||
recentGroups[recentGroupNext] = group;
|
||||
recentGroupNext = (recentGroupNext + 1) % RECENT_GROUPS;
|
||||
}
|
||||
if (group.renderableClass == null) {
|
||||
group.renderableClass = TesrAttribution.currentRenderable;
|
||||
@@ -296,6 +436,124 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
void queue(TemplateBuffer template, CubeParams[] cubes, float scale, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, long entityInfo, int id, Matrix4f texMatrix) {
|
||||
final Group group = groupFor(layer, material, id);
|
||||
touch(group, layer);
|
||||
if (group.stream) {
|
||||
final boolean instanceable = instanceable(group);
|
||||
final boolean capturingBase = glintCapture != null && glintCapture.capturesBase();
|
||||
if (instanceable && cubes != null) {
|
||||
final TexRun run = group.openOrExtendRun(group.cubeColumns, texMatrix, false, capturingBase && group.opaque);
|
||||
group.cubeColumns.addCubes(deferred != null ? template : null, cubes, scale, currentMV, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
run.last.end = group.cubeColumns.size;
|
||||
run.parts++;
|
||||
run.instances += cubes.length;
|
||||
if (glintCapture != null) glintCapture.record(group, group.cubeColumns, run, group.cubeColumns.size - 1, group.cubeColumns.size, cubes.length);
|
||||
return;
|
||||
}
|
||||
if (instanceable) {
|
||||
final TexRun run = group.openOrExtendRun(group.templateColumns, texMatrix, false, capturingBase && group.opaque);
|
||||
group.templateColumns.add(template, currentMV, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
run.last.end = group.templateColumns.size;
|
||||
run.parts++;
|
||||
run.instances++;
|
||||
if (glintCapture != null) glintCapture.record(group, group.templateColumns, run, group.templateColumns.size - 1, group.templateColumns.size, 1);
|
||||
return;
|
||||
}
|
||||
if (glintCapture != null) glintCapture.invalidate();
|
||||
if (template.drawMode == layer.getDrawMode()) {
|
||||
source.getBuffer(layer, id).addTemplateInstance(template, currentMV, scratchVec, colorABGR, packedLight, texMatrix);
|
||||
streamedInstances++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
scratchMat.set(baseMVInv).mul(currentMV);
|
||||
TesrAnchorMath.toAnchorRelative(scratchMat, camX, camY, camZ, group.anchorX, group.anchorY, group.anchorZ);
|
||||
long hash = TesrAnchorMath.instanceHash(System.identityHashCode(template), scratchMat, packedLight, colorABGR);
|
||||
if (texMatrix != null) {
|
||||
hash += TesrAnchorMath.texMatrixHash(texMatrix);
|
||||
}
|
||||
if (group.streaming && template.drawMode == layer.getDrawMode()) {
|
||||
group.frameHash += hash;
|
||||
source.getBuffer(layer, id).addTemplateInstance(template, currentMV, scratchVec, colorABGR, packedLight, texMatrix);
|
||||
streamedInstances++;
|
||||
return;
|
||||
}
|
||||
group.hashAcc += hash;
|
||||
group.templateColumns.add(template, scratchMat, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
group.addTexMatrix(texMatrix);
|
||||
}
|
||||
|
||||
boolean canCopyInstances(TesrMaterial material) {
|
||||
return material.isStream() && instanceableMaterial(material);
|
||||
}
|
||||
|
||||
void copyInstances(InstanceColumns src, int start, int end, RenderLayer layer, TesrMaterial material, int packedLight, int colorABGR, int overlayABGR, Matrix4f texMatrix) {
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final boolean cubes = !src.cubes.isEmpty();
|
||||
final InstanceColumns dst = cubes ? group.cubeColumns : group.templateColumns;
|
||||
final TexRun run = group.openOrExtendRun(dst, texMatrix);
|
||||
final int first = dst.size;
|
||||
dst.appendRange(src, start, end, packedLight, colorABGR, overlayABGR);
|
||||
int instances = end - start;
|
||||
if (cubes) {
|
||||
instances = 0;
|
||||
for (int i = start; i < end; i++) instances += src.cubes.get(i).length;
|
||||
}
|
||||
run.last.end = dst.size;
|
||||
run.parts += end - start;
|
||||
run.instances += instances;
|
||||
if (glintCapture != null) glintCapture.record(group, dst, run, first, dst.size, instances);
|
||||
}
|
||||
|
||||
void referenceInstances(InstanceColumns src, int start, int end, RenderLayer layer, TesrMaterial material, int colorABGR, Matrix4f texMatrix) {
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final boolean cubes = !src.cubes.isEmpty();
|
||||
final InstanceColumns dst = cubes ? group.cubeColumns : group.templateColumns;
|
||||
final int instances = instanceCount(src, start, end, cubes);
|
||||
final TexRun run = group.referenceRun(dst, src, null, colorABGR, texMatrix, false, start);
|
||||
run.last.end = end;
|
||||
run.parts += end - start;
|
||||
run.instances += instances;
|
||||
dst.referencedParts += end - start;
|
||||
if (glintCapture != null) glintCapture.record(group, dst, run, start, end, instances);
|
||||
}
|
||||
|
||||
void referenceGlintLayers(InstanceColumns src, TexRun sourceRun, int start, int end, RenderLayer layer, TesrMaterial material, int colorABGR, Matrix4f first, Matrix4f second) {
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final boolean cubes = !src.cubes.isEmpty();
|
||||
final InstanceColumns dst = cubes ? group.cubeColumns : group.templateColumns;
|
||||
final int instances = instanceCount(src, start, end, cubes);
|
||||
final TexRun run = group.referenceRun(dst, src, sourceRun, colorABGR, first, true, start);
|
||||
run.last.end = end;
|
||||
run.parts += end - start;
|
||||
run.instances += instances;
|
||||
group.replayRange(dst, run, start, end, instances, second, true);
|
||||
dst.referencedParts += end - start;
|
||||
}
|
||||
|
||||
void queueGlintLayers(TemplateBuffer template, RenderLayer layer, TesrMaterial material, Matrix4f currentMV, int packedLight, int colorABGR, int overlayABGR, Matrix4f first, Matrix4f second) {
|
||||
final Group group = groupFor(layer, material, 0);
|
||||
touch(group, layer);
|
||||
final InstanceColumns dst = group.templateColumns;
|
||||
final int index = dst.size;
|
||||
final TexRun run = group.openOrExtendRun(dst, first, true, false);
|
||||
dst.add(template, currentMV, packedLight, colorABGR, overlayABGR, 0L);
|
||||
run.last.end = dst.size;
|
||||
run.parts++;
|
||||
run.instances++;
|
||||
group.replayRange(dst, run, index, index + 1, 1, second, true);
|
||||
}
|
||||
|
||||
private static int instanceCount(InstanceColumns src, int start, int end, boolean cubes) {
|
||||
if (!cubes) return end - start;
|
||||
int instances = 0;
|
||||
for (int i = start; i < end; i++) instances += src.cubes.get(i).length;
|
||||
return instances;
|
||||
}
|
||||
|
||||
private void touch(Group group, RenderLayer layer) {
|
||||
if (group.frameMark != frameMark) {
|
||||
if (frameMark - group.frameMark > IDLE_RESET_FRAMES) {
|
||||
group.streaming = group.stream;
|
||||
@@ -330,45 +588,6 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
source.declareUse(layer);
|
||||
}
|
||||
if (group.stream) {
|
||||
final boolean instanceable = instanceable(group);
|
||||
if (instanceable && cubes != null) {
|
||||
final TexRun run = group.openOrExtendRun(group.cubeColumns, texMatrix);
|
||||
group.cubeColumns.addCubes(deferred != null ? template : null, cubes, scale, currentMV, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
run.last.end = group.cubeColumns.size;
|
||||
run.parts++;
|
||||
run.instances += cubes.length;
|
||||
return;
|
||||
}
|
||||
if (instanceable) {
|
||||
final TexRun run = group.openOrExtendRun(group.templateColumns, texMatrix);
|
||||
group.templateColumns.add(template, currentMV, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
run.last.end = group.templateColumns.size;
|
||||
run.parts++;
|
||||
run.instances++;
|
||||
return;
|
||||
}
|
||||
if (template.drawMode == layer.getDrawMode()) {
|
||||
source.getBuffer(layer, id).addTemplateInstance(template, currentMV, scratchVec, colorABGR, packedLight, texMatrix);
|
||||
streamedInstances++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
scratchMat.set(baseMVInv).mul(currentMV);
|
||||
TesrAnchorMath.toAnchorRelative(scratchMat, camX, camY, camZ, group.anchorX, group.anchorY, group.anchorZ);
|
||||
long hash = TesrAnchorMath.instanceHash(System.identityHashCode(template), scratchMat, packedLight, colorABGR);
|
||||
if (texMatrix != null) {
|
||||
hash += TesrAnchorMath.texMatrixHash(texMatrix);
|
||||
}
|
||||
if (group.streaming && template.drawMode == layer.getDrawMode()) {
|
||||
group.frameHash += hash;
|
||||
source.getBuffer(layer, id).addTemplateInstance(template, currentMV, scratchVec, colorABGR, packedLight, texMatrix);
|
||||
streamedInstances++;
|
||||
return;
|
||||
}
|
||||
group.hashAcc += hash;
|
||||
group.templateColumns.add(template, scratchMat, packedLight, colorABGR, overlayABGR, entityInfo);
|
||||
group.addTexMatrix(texMatrix);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -383,6 +602,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
|
||||
@Override
|
||||
public void drawLayer(RenderLayer layer) {
|
||||
if (glintCapture != null) glintCapture.invalidate();
|
||||
final ObjectArrayList<Group> list = byLayer.get(layer);
|
||||
if (list == null) return;
|
||||
boolean matrixAvailable = true;
|
||||
@@ -411,7 +631,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
private static final int NEED_CUBE = 2;
|
||||
|
||||
private boolean live(Group g) {
|
||||
return g.frameMark == frameMark && (g.templateColumns.size > 0 || g.cubeColumns.size > 0);
|
||||
return g.frameMark == frameMark && (g.templateColumns.hasInstances() || g.cubeColumns.hasInstances());
|
||||
}
|
||||
|
||||
private boolean anyInstanceable(ObjectArrayList<Group> list) {
|
||||
@@ -431,14 +651,14 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
drawRetained(group);
|
||||
continue;
|
||||
}
|
||||
if (group.templateColumns.size > 0) {
|
||||
if (group.templateColumns.hasInstances()) {
|
||||
if (matrixAvailable) {
|
||||
need |= NEED_MATRIX;
|
||||
} else {
|
||||
drawInstancesCpu(group, group.templateColumns);
|
||||
}
|
||||
}
|
||||
if (group.cubeColumns.size > 0) {
|
||||
if (group.cubeColumns.hasInstances()) {
|
||||
if (cubeAvailable) {
|
||||
need |= NEED_CUBE;
|
||||
} else if (matrixAvailable) {
|
||||
@@ -455,11 +675,12 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
for (int i = 0, n = list.size(); i < n; i++) {
|
||||
final Group group = list.get(i);
|
||||
if (!live(group) || !instanceable(group)) continue;
|
||||
if (group.templateColumns.size > 0) {
|
||||
drawTemplateRuns(group.templateColumns);
|
||||
if (group.templateColumns.hasInstances()) {
|
||||
final boolean fixedFunction = deferred == null && GLStateManager.getActiveProgram() == 0;
|
||||
drawTemplateRuns(group.templateColumns, group.material.special() == TesrMaterial.SpecialRender.GLINT && fixedFunction, group.material == EntityMaterials.ITEM_GLINT && fixedFunction);
|
||||
}
|
||||
if (!cubeAvailable && group.cubeColumns.size > 0) {
|
||||
drawTemplateRuns(group.cubeColumns);
|
||||
if (!cubeAvailable && group.cubeColumns.hasInstances()) {
|
||||
drawTemplateRuns(group.cubeColumns, group.material == EntityMaterials.GLINT && deferred == null, false);
|
||||
}
|
||||
}
|
||||
restoreTexMatrix();
|
||||
@@ -468,7 +689,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
private void drawCubeVariant(ObjectArrayList<Group> list) {
|
||||
for (int i = 0, n = list.size(); i < n; i++) {
|
||||
final Group group = list.get(i);
|
||||
if (!live(group) || !instanceable(group) || group.cubeColumns.size == 0) continue;
|
||||
if (!live(group) || !instanceable(group) || !group.cubeColumns.hasInstances()) continue;
|
||||
drawCubeInstances(group);
|
||||
}
|
||||
restoreTexMatrix();
|
||||
@@ -497,12 +718,34 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
}
|
||||
|
||||
private void drawTemplateRuns(InstanceColumns cols) {
|
||||
private void drawTemplateRuns(InstanceColumns cols, boolean combineGlint, boolean itemStencil) {
|
||||
for (int r = 0, n = cols.runs.size(); r < n; r++) {
|
||||
final TexRun run = cols.runs.get(r);
|
||||
applyTexMatrix(run);
|
||||
instancedDraws += instanced.drawTemplates(cols, run, nowMs);
|
||||
instancedInstances += run.parts;
|
||||
final boolean combined = combineGlint && r + 1 < n && combineRuns(cols, run, cols.runs.get(r + 1));
|
||||
if (itemStencil) {
|
||||
GLStateManager.glPushAttrib(GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilMask(1);
|
||||
GLStateManager.glClearStencil(0);
|
||||
GLStateManager.glClear(GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 0, 1);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_INCR);
|
||||
}
|
||||
try {
|
||||
if (canRedraw(run)) {
|
||||
instancedDraws += instanced.redrawTemplates(run.sourceRun, run.color);
|
||||
redrawnInstances += run.parts;
|
||||
} else {
|
||||
instancedDraws += instanced.drawTemplates(cols, run, nowMs, run.glintBase);
|
||||
if (run.glintBase) recordUpload(run);
|
||||
}
|
||||
instancedInstances += run.parts;
|
||||
} finally {
|
||||
if (itemStencil) GLStateManager.glPopAttrib();
|
||||
if (combined) CombinedGlint.end();
|
||||
}
|
||||
if (combined) r++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,12 +754,43 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
for (int r = 0, n = cols.runs.size(); r < n; r++) {
|
||||
final TexRun run = cols.runs.get(r);
|
||||
applyTexMatrix(run);
|
||||
instanced.drawCubes(cols, run);
|
||||
instancedDraws++;
|
||||
cubeInstances += run.instances;
|
||||
final boolean combined = group.material == EntityMaterials.GLINT && deferred == null && r + 1 < n && combineRuns(cols, run, cols.runs.get(r + 1));
|
||||
try {
|
||||
if (canRedraw(run)) {
|
||||
instanced.redrawCubes(run.sourceRun.uploadOffset, run.instances, run.color);
|
||||
redrawnInstances += run.instances;
|
||||
} else {
|
||||
run.uploadOffset = instanced.drawCubes(cols, run);
|
||||
if (run.glintBase) recordUpload(run);
|
||||
}
|
||||
instancedDraws++;
|
||||
cubeInstances += run.instances;
|
||||
} finally {
|
||||
if (combined) CombinedGlint.end();
|
||||
}
|
||||
if (combined) r++;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canRedraw(TexRun run) {
|
||||
final TexRun base = run.sourceRun;
|
||||
return base != null && base.uploadPass == frameMark && base.parts == run.parts && base.instances == run.instances && instanced.canRedraw(base.uploadRingEpoch, base.uploadBuffer);
|
||||
}
|
||||
|
||||
private void recordUpload(TexRun run) {
|
||||
run.uploadPass = frameMark;
|
||||
run.uploadRingEpoch = instanced.ringEpoch();
|
||||
run.uploadBuffer = instanced.ringBuffer();
|
||||
}
|
||||
|
||||
private static boolean combineRuns(InstanceColumns cols, TexRun first, TexRun second) {
|
||||
return matchingGlintGeometry(cols, first, second) && CombinedGlint.begin(second.identity ? IDENTITY : second.matrix);
|
||||
}
|
||||
|
||||
static boolean matchingGlintGeometry(InstanceColumns cols, TexRun first, TexRun second) {
|
||||
return second.reusedFrom == first && first.parts == second.parts && first.instances == second.instances;
|
||||
}
|
||||
|
||||
private void applyTexMatrix(TexRun run) {
|
||||
if (run.identity && !texApplied) return;
|
||||
if (!texApplied) {
|
||||
@@ -551,18 +825,19 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
saveMV();
|
||||
GLStateManager.setModelViewMatrix(IDENTITY);
|
||||
final VertexFormat format = group.layer.getVertexFormat();
|
||||
final ObjectArrayList<TemplateBuffer> templates = cols.templates;
|
||||
for (int r = 0, rn = cols.runs.size(); r < rn; r++) {
|
||||
final TexRun head = cols.runs.get(r);
|
||||
final Matrix4f texMatrix = head.identity ? null : head.matrix;
|
||||
final InstanceColumns data = head.source != null ? head.source : cols;
|
||||
final ObjectArrayList<TemplateBuffer> templates = data.templates;
|
||||
for (TexRun seg = head; seg != null; seg = seg.next) {
|
||||
final int runEnd = seg.end;
|
||||
int sub = seg.start;
|
||||
while (sub < runEnd) {
|
||||
final long info = cols.infos.getLong(sub);
|
||||
final int overlay = cols.overlays.getInt(sub);
|
||||
final long info = head.source != null ? 0L : data.infos.getLong(sub);
|
||||
final int overlay = data.overlays.getInt(sub);
|
||||
int subEnd = sub;
|
||||
while (subEnd < runEnd && cols.infos.getLong(subEnd) == info && cols.overlays.getInt(subEnd) == overlay) {
|
||||
while (subEnd < runEnd && (head.source != null || data.infos.getLong(subEnd) == info) && data.overlays.getInt(subEnd) == overlay) {
|
||||
subEnd++;
|
||||
}
|
||||
AngelicaBufferSource.setEntityColor(overlay);
|
||||
@@ -581,9 +856,10 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
final long base = memAddress0(fallbackScratch);
|
||||
long ptr = base;
|
||||
for (int i = start; i < end; i++) {
|
||||
System.arraycopy(cols.matrices, i * 16, matScratch, 0, 16);
|
||||
System.arraycopy(data.matrices, i * 16, matScratch, 0, 16);
|
||||
scratchMat.set(matScratch);
|
||||
ptr = VertexTransform.writeInstance(ptr, format, templates.get(i), scratchMat, scratchVec, cols.colors.getInt(i), cols.lights.getInt(i), texMatrix);
|
||||
final int color = head.source != null ? head.color : data.colors.getInt(i);
|
||||
ptr = VertexTransform.writeInstance(ptr, format, templates.get(i), scratchMat, scratchVec, color, data.lights.getInt(i), texMatrix);
|
||||
}
|
||||
fallbackScratch.position((int) (ptr - base));
|
||||
fallbackScratch.flip();
|
||||
@@ -612,7 +888,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
|
||||
void sweep(long now) {
|
||||
lastGroup = null;
|
||||
Arrays.fill(recentGroups, null);
|
||||
for (final ObjectArrayList<Group> list : byLayer.values()) {
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
final Group group = list.get(i);
|
||||
@@ -638,7 +914,7 @@ final class RetainedTesrGroups implements AngelicaBufferSource.LayerDrawHook {
|
||||
}
|
||||
byLayer.clear();
|
||||
groups.clear();
|
||||
lastGroup = null;
|
||||
Arrays.fill(recentGroups, null);
|
||||
fallbackMesh.delete();
|
||||
fallbackScratch = null;
|
||||
texApplied = false;
|
||||
|
||||
+114
-101
@@ -1,59 +1,88 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.CombinedGlint;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.rendering.items.HeldItemGlint;
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Fix enchantment glint z-fighting on held item edges using stencil masking.
|
||||
*
|
||||
* Vanilla uses GL_EQUAL to restrict the glint to item pixels, but GPU depth
|
||||
* non-determinism between draw calls causes GL_EQUAL to fail on edge quads.
|
||||
*
|
||||
* This mixin uses a stencil-based check instead of a GL_EQUAL check.
|
||||
* 1. During item render: write stencil value 1 wherever the item draws.
|
||||
* 2. During each glint renderItemIn2D call:
|
||||
* a. Pre-pass: no color/depth writes, GL_LEQUAL depth test, increment the stencil 1→2
|
||||
* where depth passes. This determines pixel ownership without corrupting the depth buffer (damn potions...)
|
||||
* b. Color pass: draw only where stencil == 2, GL_LEQUAL depth test, decrement the stencil
|
||||
* 2→1 to reset for the next glint pass.
|
||||
*
|
||||
* For the shadow pass, all stencil operations are skipped. Shadow FBOs have no stencil attachment.
|
||||
* On AMD Windows, glClear(GL_STENCIL_BUFFER_BIT) on a depth-only FBO causes the driver to
|
||||
* promote it to GL_DEPTH24_STENCIL8 internally, corrupting depth values and breaking shadows.
|
||||
* Someone needs to yell at AMD to fix this decade old bug.
|
||||
*/
|
||||
/** Match item depth and prevent coplanar extrusion faces from blending the same glint layer twice. */
|
||||
@Mixin(value = ItemRenderer.class, priority = 1100)
|
||||
public class MixinItemRenderer_EdgeDepth {
|
||||
|
||||
@Unique private static boolean angelica$glintMode = false;
|
||||
@Unique private static boolean angelica$inPrepass = false;
|
||||
@Unique private static final Tracy.ZoneId angelica$Z_HELD_ITEM = Tracy.zoneId("heldItem", Tracy.COLOR_CLIENT);
|
||||
@Unique private static IIcon angelica$glintIcon;
|
||||
@Unique private static boolean angelica$combinedGlint;
|
||||
@Unique private static boolean angelica$stencilPrepared;
|
||||
@Unique private static boolean angelica$preparingStencil;
|
||||
@Unique private static boolean angelica$firstLayerMarked;
|
||||
@Unique private static int angelica$nesting;
|
||||
@Unique private static final IIcon[] angelica$savedIcons = new IIcon[8];
|
||||
@Unique private static final boolean[] angelica$savedCombined = new boolean[8];
|
||||
@Unique private static final boolean[] angelica$savedPrepared = new boolean[8];
|
||||
|
||||
// Item render: write stencil value 1 at all visible item pixels
|
||||
@Inject(method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At("HEAD"), remap = false)
|
||||
private void angelica$enterRenderItem(CallbackInfo ci) {
|
||||
final int depth = angelica$nesting++;
|
||||
if (depth < angelica$savedIcons.length) {
|
||||
angelica$savedIcons[depth] = angelica$glintIcon;
|
||||
angelica$savedCombined[depth] = angelica$combinedGlint;
|
||||
angelica$savedPrepared[depth] = angelica$stencilPrepared;
|
||||
}
|
||||
angelica$glintIcon = null;
|
||||
angelica$combinedGlint = false;
|
||||
angelica$stencilPrepared = false;
|
||||
angelica$firstLayerMarked = false;
|
||||
if (Tracy.FINE_ZONES) Tracy.beginZone(angelica$Z_HELD_ITEM);
|
||||
}
|
||||
|
||||
@Inject(method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At("RETURN"), remap = false)
|
||||
private void angelica$exitRenderItem(CallbackInfo ci) {
|
||||
if (Tracy.FINE_ZONES) Tracy.endZone();
|
||||
final int depth = --angelica$nesting;
|
||||
final boolean saved = depth >= 0 && depth < angelica$savedIcons.length;
|
||||
angelica$glintIcon = saved ? angelica$savedIcons[depth] : null;
|
||||
angelica$combinedGlint = saved && angelica$savedCombined[depth];
|
||||
angelica$stencilPrepared = saved && angelica$savedPrepared[depth];
|
||||
angelica$firstLayerMarked = false;
|
||||
if (saved) angelica$savedIcons[depth] = null;
|
||||
if (depth < 0) angelica$nesting = 0;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItemIn2D(Lnet/minecraft/client/renderer/Tessellator;FFFFIIF)V", ordinal = 0, remap = true),
|
||||
remap = false
|
||||
)
|
||||
private void angelica$stencilWriteStart(CallbackInfo ci) {
|
||||
if (ShadowRenderer.ACTIVE) return;
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilMask(0x03);
|
||||
GLStateManager.glClearStencil(0);
|
||||
GLStateManager.glClear(GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glStencilFunc(GL11.GL_ALWAYS, 1, 0x03);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItemIn2D(Lnet/minecraft/client/renderer/Tessellator;FFFFIIF)V", ordinal = 0, remap = true), remap = false)
|
||||
private void angelica$prepareGlintStencil(EntityLivingBase entity, ItemStack stack, int pass, ItemRenderType type, CallbackInfo ci,
|
||||
@Local Tessellator tess) {
|
||||
angelica$preparingStencil = !ShadowRenderer.ACTIVE && !GLStateManager.isRecordingDisplayList()
|
||||
&& !TessellatorManager.isCurrentlyCapturing() && !TessellatorManager.shouldInterceptDraw(tess)
|
||||
&& GLStateManager.getDepthTest().isEnabled() && GLStateManager.getDepthState().isEnabled()
|
||||
&& HeldItemGlint.canResetStencil() && HeldItemGlint.eligible() && HeldItemGlint.needsImmediateBase(stack, pass);
|
||||
if (angelica$preparingStencil) {
|
||||
GLStateManager.glPushAttrib(GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilMask(1);
|
||||
GLStateManager.glStencilFunc(GL11.GL_ALWAYS, 0, 1);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
@@ -61,81 +90,65 @@ public class MixinItemRenderer_EdgeDepth {
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItemIn2D(Lnet/minecraft/client/renderer/Tessellator;FFFFIIF)V", ordinal = 0, remap = true, shift = At.Shift.AFTER),
|
||||
remap = false
|
||||
)
|
||||
private void angelica$stencilWriteEnd(CallbackInfo ci) {
|
||||
if (ShadowRenderer.ACTIVE) return;
|
||||
GLStateManager.glStencilMask(0x00);
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
private void angelica$finishGlintStencil(CallbackInfo ci) {
|
||||
if (angelica$preparingStencil) GLStateManager.glPopAttrib();
|
||||
angelica$stencilPrepared = angelica$preparingStencil;
|
||||
angelica$preparingStencil = false;
|
||||
}
|
||||
|
||||
// Glint section: replace GL_EQUAL with stencil-based masking
|
||||
|
||||
@Redirect(
|
||||
@Inject(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDepthFunc(I)V", ordinal = 0, remap = false),
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItemIn2D(Lnet/minecraft/client/renderer/Tessellator;FFFFIIF)V", ordinal = 0, remap = true, shift = At.Shift.AFTER),
|
||||
remap = false
|
||||
)
|
||||
private void angelica$glintStart(int func) {
|
||||
if (ShadowRenderer.ACTIVE) {
|
||||
GLStateManager.glDepthFunc(func);
|
||||
return;
|
||||
}
|
||||
angelica$glintMode = true;
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 1, 0x03);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
private void angelica$rememberGlintIcon(CallbackInfo ci, @Local IIcon icon) {
|
||||
angelica$glintIcon = icon;
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDepthFunc(I)V", ordinal = 1, remap = false),
|
||||
remap = false
|
||||
)
|
||||
private void angelica$glintEnd(int func) {
|
||||
if (ShadowRenderer.ACTIVE) {
|
||||
GLStateManager.glDepthFunc(func);
|
||||
return;
|
||||
}
|
||||
angelica$glintMode = false;
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilMask(0x03);
|
||||
}
|
||||
|
||||
// Stencil pre-pass inside renderItemIn2D when in glint mode
|
||||
|
||||
// Wrap the geometry method so mods redirecting the caller retain their glint color and blending.
|
||||
@WrapMethod(method = "renderItemIn2D")
|
||||
private static void angelica$glintPrepass(Tessellator tess, float minU, float minV, float maxU, float maxV,
|
||||
int w, int h, float thickness, Operation<Void> original) {
|
||||
if (ShadowRenderer.ACTIVE || !angelica$glintMode || angelica$inPrepass) {
|
||||
original.call(tess, minU, minV, maxU, maxV, w, h, thickness);
|
||||
return;
|
||||
private static void angelica$matchGlintGeometry(Tessellator tess, float minU, float minV, float maxU, float maxV,
|
||||
int width, int height, float thickness, Operation<Void> original) {
|
||||
if (angelica$glintIcon != null && thickness == 0.0625F
|
||||
&& ((width == 256 && height == 256)
|
||||
|| (width == angelica$glintIcon.getIconWidth() && height == angelica$glintIcon.getIconHeight()))
|
||||
&& minU == 0 && minV == 0 && maxU == 1 && maxV == 1) {
|
||||
width = angelica$glintIcon.getIconWidth();
|
||||
height = angelica$glintIcon.getIconHeight();
|
||||
if (!ShadowRenderer.ACTIVE && !GLStateManager.isRecordingDisplayList()
|
||||
&& !TessellatorManager.isCurrentlyCapturing() && !TessellatorManager.shouldInterceptDraw(tess)) {
|
||||
if (angelica$combinedGlint) return;
|
||||
final boolean secondLayer = angelica$firstLayerMarked;
|
||||
final boolean combined = !secondLayer && HeldItemGlint.begin();
|
||||
// Depth selects the surface, stencil limits it to one blend.
|
||||
// The base draw's stencil reset or the first layer's marks avoid a masked full-screen clear.
|
||||
GLStateManager.glPushAttrib(GL11.GL_STENCIL_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
try {
|
||||
GLStateManager.glDepthMask(false);
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilMask(1);
|
||||
if (secondLayer) {
|
||||
// Same pixels as the first layer, so removing its marks leaves the stencil reset.
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 1, 1);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_DECR);
|
||||
} else {
|
||||
if (!angelica$stencilPrepared) {
|
||||
GLStateManager.glClearStencil(0);
|
||||
GLStateManager.glClear(GL11.GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 0, 1);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_INCR);
|
||||
}
|
||||
original.call(tess, minU, minV, maxU, maxV, width, height, thickness);
|
||||
} finally {
|
||||
GLStateManager.glPopAttrib();
|
||||
if (combined) CombinedGlint.end();
|
||||
}
|
||||
angelica$combinedGlint = combined;
|
||||
angelica$firstLayerMarked = !combined && !secondLayer && HeldItemGlint.layersCoverSamePixels();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
angelica$inPrepass = true;
|
||||
|
||||
// Pre-pass: determine pixel ownership via GL_LEQUAL depth test.
|
||||
GLStateManager.glColorMask(false, false, false, false);
|
||||
GLStateManager.glDepthMask(false);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glStencilMask(0x03);
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 1, 0x03);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_INCR);
|
||||
original.call(tess, minU, minV, maxU, maxV, w, h, thickness);
|
||||
|
||||
// Color pass: draw only at pre-pass pixels.
|
||||
GLStateManager.glColorMask(true, true, true, true);
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 2, 0x03);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_DECR);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
original.call(tess, minU, minV, maxU, maxV, w, h, thickness);
|
||||
|
||||
// Restore state for the next glint renderItemIn2D call
|
||||
GLStateManager.glStencilMask(0x00);
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 1, 0x03);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
|
||||
angelica$inPrepass = false;
|
||||
original.call(tess, minU, minV, maxU, maxV, width, height, thickness);
|
||||
}
|
||||
}
|
||||
+55
-6
@@ -1,22 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.SkippedGlintBlock;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.rendering.items.DroppedItemInstancer;
|
||||
import com.gtnewhorizons.angelica.rendering.items.HeldItemGlint;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityMaterials;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ItemRenderer.class)
|
||||
public abstract class MixinItemRenderer_Instanced {
|
||||
@Unique private static final Tracy.ZoneId angelica$Z_HELD_ICON = Tracy.zoneId("heldIcon", Tracy.COLOR_CLIENT);
|
||||
@Unique private static final Tracy.ZoneId angelica$Z_HELD_GLINT = Tracy.zoneId("heldGlint", Tracy.COLOR_CLIENT);
|
||||
@Unique private static ItemStack angelica$stack;
|
||||
@Unique private static int angelica$pass;
|
||||
@Unique private static ItemRenderType angelica$type;
|
||||
@Unique private static int angelica$iconWidth, angelica$iconHeight;
|
||||
|
||||
@Inject(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At("HEAD"),
|
||||
remap = false
|
||||
)
|
||||
private void angelica$rememberItem(EntityLivingBase entity, ItemStack stack, int pass, ItemRenderType type, CallbackInfo ci) {
|
||||
angelica$stack = stack;
|
||||
angelica$pass = pass;
|
||||
angelica$type = type;
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
@@ -26,8 +51,27 @@ public abstract class MixinItemRenderer_Instanced {
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private void angelica$instanceHeldIcon(Tessellator tessellator, float maxU, float minV, float minU, float maxV, int width, int height, float thickness, Operation<Void> original, @Local(argsOnly = true) ItemStack stack, @Local(argsOnly = true) ItemRenderType type) {
|
||||
DroppedItemInstancer.icon(angelica$batchable(type) ? stack : null, tessellator, maxU, minV, minU, maxV, width, height, thickness, original);
|
||||
private void angelica$instanceHeldIcon(Tessellator tessellator, float maxU, float minV, float minU, float maxV, int width, int height, float thickness, Operation<Void> original) {
|
||||
angelica$iconWidth = width;
|
||||
angelica$iconHeight = height;
|
||||
final ItemStack stack = angelica$stack;
|
||||
if (Tracy.FINE_ZONES) Tracy.beginZone(angelica$Z_HELD_ICON);
|
||||
try {
|
||||
DroppedItemInstancer.icon(angelica$batchable(angelica$type) && !HeldItemGlint.needsImmediateBase(stack, angelica$pass) ? stack : null, tessellator, maxU, minV, minU, maxV, width, height, thickness, original);
|
||||
} finally {
|
||||
if (Tracy.FINE_ZONES) Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;hasEffect(I)Z"),
|
||||
remap = false
|
||||
)
|
||||
private boolean angelica$queueHeldGlintBlock(boolean hasEffect) {
|
||||
if (!hasEffect || !HeldItemGlint.eligible() || !DroppedItemInstancer.queueSkippedGlint(angelica$iconWidth, angelica$iconHeight)) return hasEffect;
|
||||
SkippedGlintBlock.applyHeldItemExitState();
|
||||
return false;
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
@@ -46,7 +90,12 @@ public abstract class MixinItemRenderer_Instanced {
|
||||
}
|
||||
)
|
||||
private void angelica$instanceHeldGlint(Tessellator tessellator, float maxU, float minV, float minU, float maxV, int width, int height, float thickness, Operation<Void> original) {
|
||||
DroppedItemInstancer.glint(tessellator, maxU, minV, minU, maxV, width, height, thickness, original);
|
||||
if (Tracy.FINE_ZONES) Tracy.beginZone(angelica$Z_HELD_GLINT);
|
||||
try {
|
||||
DroppedItemInstancer.glint(tessellator, maxU, minV, minU, maxV, angelica$iconWidth, angelica$iconHeight, thickness, EntityMaterials.ITEM_GLINT, original);
|
||||
} finally {
|
||||
if (Tracy.FINE_ZONES) Tracy.endZone();
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
@@ -56,8 +105,8 @@ public abstract class MixinItemRenderer_Instanced {
|
||||
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderBlockAsItem(Lnet/minecraft/block/Block;IF)V"
|
||||
)
|
||||
)
|
||||
private void angelica$instanceHeldBlock(RenderBlocks renderBlocks, Block block, int meta, float brightness, Operation<Void> original, @Local(argsOnly = true) ItemStack stack, @Local(argsOnly = true) ItemRenderType type) {
|
||||
DroppedItemInstancer.block(angelica$batchable(type) ? stack : null, renderBlocks, block, meta, brightness, false, original);
|
||||
private void angelica$instanceHeldBlock(RenderBlocks renderBlocks, Block block, int meta, float brightness, Operation<Void> original) {
|
||||
DroppedItemInstancer.block(angelica$batchable(angelica$type) ? angelica$stack : null, renderBlocks, block, meta, brightness, false, original);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
|
||||
+6
-3
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.rendering.items.DroppedItemInstancer;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.EntityMaterials;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
@@ -49,7 +50,7 @@ public abstract class MixinRenderItem_Instanced {
|
||||
}
|
||||
)
|
||||
private void angelica$instanceDroppedGlint(Tessellator tessellator, float maxU, float minV, float minU, float maxV, int width, int height, float thickness, Operation<Void> original) {
|
||||
DroppedItemInstancer.glint(tessellator, maxU, minV, minU, maxV, width, height, thickness, original);
|
||||
DroppedItemInstancer.glint(tessellator, maxU, minV, minU, maxV, width, height, thickness, EntityMaterials.GLINT, original);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
@@ -64,7 +65,8 @@ public abstract class MixinRenderItem_Instanced {
|
||||
method = "doRender(Lnet/minecraft/entity/item/EntityItem;DDDFF)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraftforge/client/ForgeHooksClient;renderEntityItem(Lnet/minecraft/entity/item/EntityItem;Lnet/minecraft/item/ItemStack;FFLjava/util/Random;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/renderer/RenderBlocks;I)Z",
|
||||
target = "Lnet/minecraftforge/client/ForgeHooksClient;renderEntityItem(Lnet/minecraft/entity/item/EntityItem;Lnet/minecraft/item/ItemStack;"
|
||||
+ "FFLjava/util/Random;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/renderer/RenderBlocks;I)Z",
|
||||
remap = false
|
||||
)
|
||||
)
|
||||
@@ -76,7 +78,8 @@ public abstract class MixinRenderItem_Instanced {
|
||||
method = "doRender(Lnet/minecraft/entity/item/EntityItem;DDDFF)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraftforge/client/ForgeHooksClient;renderEntityItem(Lnet/minecraft/entity/item/EntityItem;Lnet/minecraft/item/ItemStack;FFLjava/util/Random;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/renderer/RenderBlocks;I)Z",
|
||||
target = "Lnet/minecraftforge/client/ForgeHooksClient;renderEntityItem(Lnet/minecraft/entity/item/EntityItem;Lnet/minecraft/item/ItemStack;"
|
||||
+ "FFLjava/util/Random;Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/renderer/RenderBlocks;I)Z",
|
||||
shift = At.Shift.AFTER,
|
||||
remap = false
|
||||
)
|
||||
|
||||
+13
-24
@@ -1,40 +1,29 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.OperationArgs;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RendererLivingEntity.class)
|
||||
public abstract class MixinRendererLivingEntity_EquippedDraws {
|
||||
|
||||
@Unique private static final Object[] angelica$equippedArgs = new Object[3];
|
||||
|
||||
@WrapOperation(
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderEquippedItems(Lnet/minecraft/entity/EntityLivingBase;F)V"
|
||||
)
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderEquippedItems(Lnet/minecraft/entity/EntityLivingBase;F)V")
|
||||
)
|
||||
private void angelica$countEquippedDraws(RendererLivingEntity self, EntityLivingBase entity, float partialTicks, Operation<Void> original) {
|
||||
final Object[] args = angelica$equippedArgs;
|
||||
args[0] = self;
|
||||
args[1] = entity;
|
||||
args[2] = OperationArgs.boxed(args[2], partialTicks);
|
||||
private void angelica$beginEquippedDraws(CallbackInfo ci) {
|
||||
BatchEligibility.beginExpectedDraws(GLStateManager.drawCalls);
|
||||
try {
|
||||
original.call(args);
|
||||
} finally {
|
||||
args[0] = null;
|
||||
args[1] = null;
|
||||
BatchEligibility.endExpectedDraws(GLStateManager.drawCalls);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderEquippedItems(Lnet/minecraft/entity/EntityLivingBase;F)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void angelica$endEquippedDraws(CallbackInfo ci) {
|
||||
BatchEligibility.endExpectedDraws(GLStateManager.drawCalls);
|
||||
}
|
||||
}
|
||||
+142
@@ -1,14 +1,156 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.SkippedGlintBlock;
|
||||
import com.gtnewhorizons.angelica.helpers.RendererLivingEntityHelper;
|
||||
import com.gtnewhorizons.angelica.rendering.GlintClock;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.GlintCapture;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
import org.spongepowered.asm.mixin.injection.Group;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.Slice;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RendererLivingEntity.class)
|
||||
public abstract class MixinRendererLivingEntity_GlintClock {
|
||||
@Shadow @Final private static ResourceLocation RES_ITEM_GLINT;
|
||||
@Unique private static final Tracy.ZoneId angelica$Z_ARMOR_BASE = Tracy.zoneId("armorBase", Tracy.COLOR_CLIENT);
|
||||
@Unique private static final Tracy.ZoneId angelica$Z_ARMOR_GLINT = Tracy.zoneId("armorGlint", Tracy.COLOR_CLIENT);
|
||||
@Unique private final GlintCapture angelica$armorGlint = new GlintCapture();
|
||||
@Unique private final GlintCapture angelica$armorBase = new GlintCapture();
|
||||
@Unique private static final int GLINT_LAYER_QUEUED = 0, GLINT_LAYER_DRAWN = 1, GLINT_LAYER_CAPTURED = 2;
|
||||
@Unique private ModelBase angelica$baseModel;
|
||||
@Unique private boolean angelica$capturingBase;
|
||||
@Unique private int angelica$armorPassFlags;
|
||||
@Unique private int angelica$glintLayer;
|
||||
@Unique private boolean angelica$secondArmorQueued;
|
||||
@Unique private boolean angelica$zoneOpen;
|
||||
|
||||
@ModifyConstant(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;RES_ITEM_GLINT:Lnet/minecraft/util/ResourceLocation;")),
|
||||
constant = @Constant(intValue = 2, ordinal = 0))
|
||||
private int angelica$armorGlintLayers(int vanilla) {
|
||||
return angelica$secondArmorQueued ? 1 : vanilla;
|
||||
}
|
||||
|
||||
@ModifyConstant(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
slice = @Slice(
|
||||
from = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", ordinal = 0),
|
||||
to = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;RES_ITEM_GLINT:Lnet/minecraft/util/ResourceLocation;")),
|
||||
constant = @Constant(intValue = 15, ordinal = 0))
|
||||
private int angelica$queueArmorGlintBlock(int mask) {
|
||||
final ModelBase model = ((RendererLivingEntity) (Object) this).renderPassModel;
|
||||
if ((angelica$armorPassFlags & 15) != 15 || model != angelica$baseModel || GLStateManager.getOverlayA() != 0.0F || RendererLivingEntityHelper.hasEyePass(this)
|
||||
|| !ModelPartBatcher.INSTANCE.queueSkippedArmorGlint(angelica$armorBase, RES_ITEM_GLINT)) return mask;
|
||||
SkippedGlintBlock.applyArmorExitState();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Inject(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = {
|
||||
@At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", ordinal = 0),
|
||||
@At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", ordinal = 1)
|
||||
})
|
||||
private void angelica$beginArmorBase(CallbackInfo ci, @Local(ordinal = 0) int passFlags) {
|
||||
if (Tracy.FINE_ZONES) {
|
||||
Tracy.beginZone(angelica$Z_ARMOR_BASE);
|
||||
angelica$zoneOpen = true;
|
||||
}
|
||||
angelica$armorPassFlags = passFlags;
|
||||
angelica$baseModel = null;
|
||||
angelica$capturingBase = (passFlags & 15) == 15 && ModelPartBatcher.INSTANCE.beginArmorCapture(angelica$armorBase);
|
||||
if (angelica$capturingBase) GLStateManager.glPushMatrix();
|
||||
}
|
||||
|
||||
@Inject(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = {
|
||||
@At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", ordinal = 0, shift = At.Shift.AFTER),
|
||||
@At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", ordinal = 1, shift = At.Shift.AFTER)
|
||||
})
|
||||
private void angelica$endArmorBase(CallbackInfo ci) {
|
||||
if (angelica$capturingBase) {
|
||||
angelica$capturingBase = false;
|
||||
GLStateManager.glPopMatrix();
|
||||
angelica$armorBase.endWithRestoredPose();
|
||||
angelica$baseModel = ((RendererLivingEntity) (Object) this).renderPassModel;
|
||||
}
|
||||
angelica$endZone();
|
||||
}
|
||||
|
||||
@WrapWithCondition(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;RES_ITEM_GLINT:Lnet/minecraft/util/ResourceLocation;")),
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", ordinal = 0))
|
||||
private boolean angelica$renderArmorGlintLayer(ModelBase model, Entity entity, float swing, float amount, float age, float yaw, float pitch, float scale, @Local(ordinal = 2) int layer) {
|
||||
if (Tracy.FINE_ZONES) {
|
||||
Tracy.beginZone(angelica$Z_ARMOR_GLINT);
|
||||
angelica$zoneOpen = true;
|
||||
}
|
||||
final ModelPartBatcher batcher = ModelPartBatcher.INSTANCE;
|
||||
if (layer == 0) angelica$secondArmorQueued = false;
|
||||
angelica$glintLayer = GLINT_LAYER_QUEUED;
|
||||
if (layer == 0 && model == angelica$baseModel && batcher.reuseArmorBase(angelica$armorBase, angelica$armorGlint)) {
|
||||
angelica$secondArmorQueued = batcher.queueSecondArmorGlint(angelica$armorGlint);
|
||||
if (!angelica$secondArmorQueued) angelica$armorGlint.reset();
|
||||
return false;
|
||||
}
|
||||
if (layer == 1 && batcher.replayGlint(angelica$armorGlint)) return false;
|
||||
angelica$glintLayer = layer == 0 && batcher.beginGlintCapture(angelica$armorGlint) ? GLINT_LAYER_CAPTURED : GLINT_LAYER_DRAWN;
|
||||
GLStateManager.glPushMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Inject(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;RES_ITEM_GLINT:Lnet/minecraft/util/ResourceLocation;")),
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", ordinal = 0, shift = At.Shift.AFTER))
|
||||
private void angelica$endArmorGlintLayer(CallbackInfo ci) {
|
||||
if (angelica$glintLayer != GLINT_LAYER_QUEUED) {
|
||||
GLStateManager.glPopMatrix();
|
||||
if (angelica$glintLayer == GLINT_LAYER_CAPTURED) {
|
||||
angelica$armorGlint.endWithRestoredPose();
|
||||
angelica$secondArmorQueued = ModelPartBatcher.INSTANCE.queueSecondArmorGlint(angelica$armorGlint);
|
||||
}
|
||||
angelica$glintLayer = GLINT_LAYER_QUEUED;
|
||||
}
|
||||
angelica$endZone();
|
||||
}
|
||||
|
||||
@Inject(method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;error(Ljava/lang/String;Ljava/lang/Throwable;)V", remap = false))
|
||||
private void angelica$closeAfterFailedRender(CallbackInfo ci) {
|
||||
if (angelica$capturingBase) {
|
||||
angelica$capturingBase = false;
|
||||
GLStateManager.glPopMatrix();
|
||||
angelica$armorBase.reset();
|
||||
}
|
||||
if (angelica$glintLayer != GLINT_LAYER_QUEUED) {
|
||||
angelica$glintLayer = GLINT_LAYER_QUEUED;
|
||||
GLStateManager.glPopMatrix();
|
||||
angelica$armorGlint.reset();
|
||||
}
|
||||
angelica$endZone();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$endZone() {
|
||||
if (!angelica$zoneOpen) return;
|
||||
angelica$zoneOpen = false;
|
||||
Tracy.endZone();
|
||||
}
|
||||
|
||||
@Group(name = "angelica$globalGlintPhase", min = 1, max = 1)
|
||||
@Redirect(
|
||||
|
||||
+12
-26
@@ -1,43 +1,29 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.entity;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.OperationArgs;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RendererLivingEntity.class)
|
||||
public abstract class MixinRendererLivingEntity_ModelPassDraws {
|
||||
|
||||
@Unique private static final Object[] angelica$modelPassArgs = new Object[8];
|
||||
|
||||
@WrapOperation(
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V")
|
||||
)
|
||||
private void angelica$countModelPassDraws(ModelBase model, Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float headYaw, float headPitch, float scale, Operation<Void> original) {
|
||||
final Object[] args = angelica$modelPassArgs;
|
||||
args[0] = model;
|
||||
args[1] = entity;
|
||||
args[2] = OperationArgs.boxed(args[2], limbSwing);
|
||||
args[3] = OperationArgs.boxed(args[3], limbSwingAmount);
|
||||
args[4] = OperationArgs.boxed(args[4], ageInTicks);
|
||||
args[5] = OperationArgs.boxed(args[5], headYaw);
|
||||
args[6] = OperationArgs.boxed(args[6], headPitch);
|
||||
args[7] = OperationArgs.boxed(args[7], scale);
|
||||
private void angelica$beginModelPassDraws(CallbackInfo ci) {
|
||||
BatchEligibility.beginExpectedDraws(GLStateManager.drawCalls);
|
||||
try {
|
||||
original.call(args);
|
||||
} finally {
|
||||
args[0] = null;
|
||||
args[1] = null;
|
||||
BatchEligibility.endExpectedDraws(GLStateManager.drawCalls);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V", shift = At.Shift.AFTER)
|
||||
)
|
||||
private void angelica$endModelPassDraws(CallbackInfo ci) {
|
||||
BatchEligibility.endExpectedDraws(GLStateManager.drawCalls);
|
||||
}
|
||||
}
|
||||
+9
@@ -16,6 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(RenderGlobal.class)
|
||||
public class MixinRenderGlobal_Tracy {
|
||||
private static final Tracy.ZoneId Z_ENTITY_DISPATCH = Tracy.zoneId("entityDispatch", Tracy.COLOR_CLIENT);
|
||||
private static final Tracy.ZoneId Z_ENTITY_RENDER = Tracy.zoneId("entityRender", Tracy.COLOR_CLIENT);
|
||||
|
||||
@Inject(method = "renderEntities", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/profiler/Profiler;endStartSection(Ljava/lang/String;)V", args = "ldc=entities", shift = At.Shift.AFTER))
|
||||
private void angelica$beginEntityDispatchZone(CallbackInfo ci) {
|
||||
@@ -33,9 +34,17 @@ public class MixinRenderGlobal_Tracy {
|
||||
if (TesrAttribution.currentRenderable == null && entity != null) {
|
||||
TesrAttribution.currentRenderable = entity.getClass();
|
||||
}
|
||||
if (Tracy.FINE_ZONES) {
|
||||
Tracy.setGpuZonesEnabled(false);
|
||||
Tracy.beginZone(Z_ENTITY_RENDER);
|
||||
}
|
||||
try {
|
||||
return original.call(instance, entity, partialTicks);
|
||||
} finally {
|
||||
if (Tracy.FINE_ZONES) {
|
||||
Tracy.endZone();
|
||||
Tracy.setGpuZonesEnabled(true);
|
||||
}
|
||||
TesrAttribution.currentRenderable = null;
|
||||
RenderClassTimings.ENTITY.add(entity.getClass(), System.nanoTime() - start);
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.notfine.glint;
|
||||
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ArmorEnchantCache;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(EntityLiving.class)
|
||||
public abstract class MixinEntityLiving_ArmorEnchantCache implements ArmorEnchantCache {
|
||||
@Unique private ItemStack[] angelica$checkedArmor;
|
||||
@Unique private int angelica$enchantedSlots;
|
||||
|
||||
@Override
|
||||
public boolean angelica$isArmorEnchanted(int slot, ItemStack stack) {
|
||||
if (slot < 0 || slot >= 4) return stack.isItemEnchanted();
|
||||
ItemStack[] checked = angelica$checkedArmor;
|
||||
if (checked == null) checked = angelica$checkedArmor = new ItemStack[4];
|
||||
final int bit = 1 << slot;
|
||||
if (checked[slot] != stack) {
|
||||
checked[slot] = stack;
|
||||
angelica$enchantedSlots = stack.isItemEnchanted() ? angelica$enchantedSlots | bit : angelica$enchantedSlots & ~bit;
|
||||
}
|
||||
return (angelica$enchantedSlots & bit) != 0;
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -1,16 +1,15 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.notfine.glint;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import jss.notfine.core.Settings;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = ItemRenderer.class)
|
||||
public abstract class MixinItemRenderer {
|
||||
|
||||
@Redirect(
|
||||
@ModifyExpressionValue(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
@@ -18,8 +17,8 @@ public abstract class MixinItemRenderer {
|
||||
),
|
||||
remap = false
|
||||
)
|
||||
private boolean notFine$toggleGlint(ItemStack stack, int pass) {
|
||||
return (boolean)Settings.MODE_GLINT_WORLD.option.getStore() && stack.hasEffect(pass);
|
||||
private boolean notFine$toggleGlint(boolean hasEffect) {
|
||||
return hasEffect && (boolean) Settings.MODE_GLINT_WORLD.option.getStore();
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -1,7 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.notfine.glint;
|
||||
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ArmorEnchantCache;
|
||||
import jss.notfine.core.Settings;
|
||||
import net.minecraft.client.renderer.entity.RenderBiped;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -17,8 +19,8 @@ public abstract class MixinRenderBiped {
|
||||
target = "Lnet/minecraft/item/ItemStack;isItemEnchanted()Z"
|
||||
)
|
||||
)
|
||||
private boolean notFine$toggleGlint(ItemStack stack) {
|
||||
return (boolean)Settings.MODE_GLINT_WORLD.option.getStore() && stack.isItemEnchanted();
|
||||
private boolean notFine$toggleGlint(ItemStack stack, EntityLiving entity, int slot, float partialTicks) {
|
||||
return (boolean)Settings.MODE_GLINT_WORLD.option.getStore() && ((ArmorEnchantCache) entity).angelica$isArmorEnchanted(slot, stack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,9 +11,23 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class GlintClockTest {
|
||||
|
||||
@Test
|
||||
void cachedSecondLayerMatchesTheLegacyTransformAndRejectsModifiedFirstLayers() {
|
||||
for (long time : new long[] {0, 1000003, 864000000123L}) {
|
||||
GlintClock.beginFrame(time);
|
||||
final Matrix4f first = new Matrix4f().scaling(0.33333334f).rotateZ((float) Math.toRadians(30))
|
||||
.translate(armorU0(), armorV0(), 0);
|
||||
final Matrix4f second = new Matrix4f().scaling(0.33333334f).rotateZ((float) Math.toRadians(-30))
|
||||
.translate(armorU1(), armorV1(), 0);
|
||||
assertTrue(second.equals(GlintClock.secondArmorMatrix(first), 1e-6f));
|
||||
assertNull(GlintClock.secondArmorMatrix(first.translate(0.1f, 0, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void resetArmorPathTable() {
|
||||
Reflect.setStatic(ArmorTexturePaths.class, "table", new String[0]);
|
||||
|
||||
@@ -172,7 +172,7 @@ class BatchEligibilityTest {
|
||||
};
|
||||
|
||||
BatchEligibility.begin(SAFE, 0L);
|
||||
DroppedItemInstancer.glint(new Tessellator(), 1f, 0f, 0f, 1f, 16, 16, 0.0625f, original);
|
||||
DroppedItemInstancer.glint(new Tessellator(), 1f, 0f, 0f, 1f, 16, 16, 0.0625f, EntityMaterials.GLINT, original);
|
||||
assertEquals(SAFE, BatchEligibility.end(SAFE, 0L));
|
||||
|
||||
assertFalse(originalCalled[0], "the shadow-pass early return must not fall back to the original draw");
|
||||
|
||||
@@ -44,6 +44,7 @@ import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.X_
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
@GLCoreTest
|
||||
class RetainedTesrGroupsGLTest {
|
||||
@@ -518,6 +519,102 @@ class RetainedTesrGroupsGLTest {
|
||||
CubeParityFixture.assertPixelParity(reference, cubeCandidate(), SIZE, BACKGROUND, RetainedTesrGroupsGLTest::describe);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayedGlintTemplatesMatchIndependentPasses() {
|
||||
glintState();
|
||||
final int[] reference = templateReference(false);
|
||||
drawBaseTemplates();
|
||||
setGlintBlendState();
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
final GlintCapture capture = new GlintCapture();
|
||||
for (Matrix4f pose : new Matrix4f[] {templateE1, templateE2}) {
|
||||
applyGlintTexMatrix(glintMatrixA);
|
||||
assertTrue(capture.begin(groups));
|
||||
groups.queue(glintTemplate, layer, EntityMaterials.GLINT, pose, 0, -1, 0, 0L, 0, glintMatrixA);
|
||||
capture.end();
|
||||
applyGlintTexMatrix(glintMatrixB);
|
||||
GLStateManager.glColor4f(0.5f, 1, 1, 1);
|
||||
assertFalse(capture.replay(groups), "changed layer color must retain the original render call");
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
assertFalse(capture.replay(groups), "changed layer blending must retain the original render call");
|
||||
setGlintBlendState();
|
||||
assertTrue(capture.replay(groups));
|
||||
assertFalse(capture.replay(groups), "a captured pass is replayed only once");
|
||||
}
|
||||
groups.drawLayer(layer);
|
||||
instanced.endFrame();
|
||||
CubeParityFixture.assertPixelParity(reference, FfpFixture.readRegion(SIZE), SIZE, BACKGROUND, RetainedTesrGroupsGLTest::describe);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replayedGlintCubesMatchIndependentPasses() {
|
||||
glintState();
|
||||
final int[] reference = cubeReference();
|
||||
drawBaseCubes();
|
||||
setGlintBlendState();
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
final GlintCapture capture = new GlintCapture();
|
||||
for (Matrix4f pose : new Matrix4f[] {cubeE1, cubeE2}) {
|
||||
applyGlintTexMatrix(glintMatrixA);
|
||||
assertTrue(capture.begin(groups));
|
||||
groups.queue(null, glintCubes, CUBE_SCALE, layer, EntityMaterials.GLINT, pose, 0, -1, 0, 0L, 0, glintMatrixA);
|
||||
capture.end();
|
||||
applyGlintTexMatrix(glintMatrixB);
|
||||
assertTrue(capture.replay(groups));
|
||||
}
|
||||
groups.drawLayer(layer);
|
||||
instanced.endFrame();
|
||||
CubeParityFixture.assertPixelParity(reference, FfpFixture.readRegion(SIZE), SIZE, BACKGROUND, RetainedTesrGroupsGLTest::describe);
|
||||
}
|
||||
|
||||
@Test
|
||||
void armorGlintUsesTheBasePoseWithTheGlintMaterial() {
|
||||
glintState();
|
||||
drawBaseTemplates();
|
||||
setGlintBlendState();
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE);
|
||||
applyGlintTexMatrix(glintMatrixA);
|
||||
drawSingleInstance(instanced, glintTemplate, templateE1, 0xFFFFFFFF);
|
||||
applyGlintTexMatrix(glintMatrixB);
|
||||
drawSingleInstance(instanced, glintTemplate, templateE1, 0xFFFFFFFF);
|
||||
final int[] reference = FfpFixture.readRegion(SIZE);
|
||||
drawBaseTemplates();
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced);
|
||||
final GlintCapture base = new GlintCapture();
|
||||
assertTrue(base.begin(groups));
|
||||
base.allowBase();
|
||||
groups.queue(glintTemplate, layer, EntityMaterials.CUTOUT, templateE1, 0, 0xFF123456, 0, 0L, 0, null);
|
||||
base.end();
|
||||
setGlintBlendState();
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE);
|
||||
applyGlintTexMatrix(glintMatrixA);
|
||||
ModelPartBatcher.onTextureBind(new ResourceLocation("angelica", "test/glint"), glintTexture);
|
||||
final ModelPartBatcher batcher = ModelPartBatcher.INSTANCE;
|
||||
final RetainedTesrGroups previousGroups = Reflect.get(batcher, "activeGroups");
|
||||
Reflect.set(batcher, "activeGroups", groups);
|
||||
try {
|
||||
final GlintCapture glint = new GlintCapture();
|
||||
assertTrue(glint.begin(groups));
|
||||
GLStateManager.glTranslatef(0.1f, 0, 0);
|
||||
assertFalse(base.copyBase(batcher, groups), "a different parent pose must use the original model call");
|
||||
GLStateManager.glLoadIdentity();
|
||||
assertTrue(base.copyBase(batcher, groups));
|
||||
glint.end();
|
||||
assertTrue(glint.replay(groups, glintMatrixB), "the second layer can be queued without rerunning legacy texture setup");
|
||||
groups.drawLayer(Reflect.get(batcher, "lastLayer"));
|
||||
instanced.endFrame();
|
||||
final int[] actual = FfpFixture.readRegion(SIZE);
|
||||
int error = 0;
|
||||
for (int p = 0; p < actual.length; p++) for (int shift = 0; shift < 24; shift += 8) {
|
||||
error = Math.max(error, Math.abs(((actual[p] >>> shift) & 255) - ((reference[p] >>> shift) & 255)));
|
||||
}
|
||||
assertTrue(error <= 2, "reusing the base pose must retain the glint color; error " + error);
|
||||
} finally {
|
||||
Reflect.set(batcher, "activeGroups", previousGroups);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mirroredModelViewUnderCullFrontBatchesLikeTheImmediateDraw() {
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
|
||||
Reference in new issue
Block a user