mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61b5301f1a | ||
|
|
0e539ae034 | ||
|
|
e581326641 | ||
|
|
c707cc51f1 | ||
|
|
7d890bf3e7 | ||
|
|
ffc324bab6 | ||
|
|
0dfffca88e | ||
|
|
a66db1517a | ||
|
|
269b7245d3 | ||
|
|
67f2484976 | ||
|
|
1c8687e6fd | ||
|
|
e6bbd155da | ||
|
|
2f64fef6a6 | ||
|
|
b0fd1ea46e | ||
|
|
6d8cbf9c2f | ||
|
|
096dd01446 | ||
|
|
22c9479946 |
No files matched your search
@@ -187,6 +187,9 @@ fun Test.configureAngelicaJava8() {
|
||||
}
|
||||
dependsOn(tasks.extractNatives2)
|
||||
jvmArgs("-Djava.library.path=${tasks.extractNatives2.get().destinationFolder.asFile.get().path}")
|
||||
if (isMacOs) {
|
||||
jvmArgs("-Dapple.awt.UIElement=true")
|
||||
}
|
||||
testLogging { events("passed", "skipped", "failed") }
|
||||
|
||||
val swaps = mapOf(
|
||||
|
||||
@@ -161,6 +161,8 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
|
||||
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
|
||||
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:OpenBlocks:1.12.19-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:Chisel:2.17.31-GTNH:dev")
|
||||
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.6-GTNH:dev")
|
||||
|
||||
@@ -224,6 +224,9 @@ fun Test.configureGlsmJava8() {
|
||||
val extractNatives = rootProject.tasks.named("extractNatives2")
|
||||
dependsOn(extractNatives)
|
||||
jvmArgs("-Djava.library.path=${extractNatives.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}")
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")) {
|
||||
jvmArgs("-Dapple.awt.UIElement=true")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
||||
@@ -47,6 +47,7 @@ public final class SystemProperties {
|
||||
// Flyby
|
||||
public static final String FLYBY_ROUTE = System.getProperty("angelica.flyby.route", "");
|
||||
public static final boolean FLYBY_WAIT_FOR_TRACY = Boolean.getBoolean("angelica.flyby.waitForTracy");
|
||||
public static final boolean FLYBY_WAIT_FOR_FOCUS = Boolean.getBoolean("angelica.flyby.waitForFocus");
|
||||
public static final int FLYBY_WARMUP_TICKS = Integer.getInteger("angelica.flyby.warmupTicks", 400);
|
||||
public static final int FLYBY_LENGTH = Integer.getInteger("angelica.flyby.length", 0); // Blocks for moving routes, ticks for stationary
|
||||
public static final double FLYBY_SPEED = parseDouble("angelica.flyby.speed"); // Travel speed in blocks per tick
|
||||
|
||||
@@ -307,6 +307,11 @@ public class DisplayListManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordSecondaryColor(float r, float g, float b) {
|
||||
drawBarrier();
|
||||
currentRecorder.writeSecondaryColor(r, g, b);
|
||||
}
|
||||
|
||||
public static void recordColorMask(boolean r, boolean g, boolean b, boolean a) {
|
||||
drawBarrier();
|
||||
currentRecorder.writeColorMask(r, g, b, a);
|
||||
@@ -841,7 +846,12 @@ public class DisplayListManager {
|
||||
if (hasCommands) {
|
||||
DisplayListVBO compiledBuffers = null;
|
||||
try {
|
||||
compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
|
||||
final CommandRecorder pausedVbo = pauseRecording();
|
||||
try {
|
||||
compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
|
||||
} finally {
|
||||
resumeRecording(pausedVbo);
|
||||
}
|
||||
final IndexedDrawBatchBuilder indexedBuilder = commandBuffer.getIndexedDraws();
|
||||
final List<IndexedDrawBatch> indexedBatches;
|
||||
if (indexedBuilder.isEmpty()) {
|
||||
|
||||
@@ -99,6 +99,7 @@ public class Feature {
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_CURRENT_BIT, ImmutableSet.of(
|
||||
GLStateManager.getColor()
|
||||
, GLStateManager.getSecondaryColor() // Current secondary color
|
||||
, ShaderManager.getNormalStack() // Current normal vector
|
||||
, ShaderManager.getTexCoordStack() // Current texture coordinates
|
||||
// Current color index
|
||||
@@ -122,6 +123,7 @@ public class Feature {
|
||||
, GLStateManager.getBlendMode()
|
||||
, GLStateManager.getColorLogicOpState()
|
||||
, GLStateManager.getColorMaterial()
|
||||
, GLStateManager.getColorSumState()
|
||||
, GLStateManager.getCullState()
|
||||
, GLStateManager.getDepthTest()
|
||||
, GLStateManager.getDitherState()
|
||||
@@ -197,6 +199,7 @@ public class Feature {
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_FOG_BIT, ImmutableSet.of(
|
||||
GLStateManager.getFogMode()
|
||||
, GLStateManager.getColorSumState()
|
||||
, GLStateManager.getFogState()
|
||||
// ^^ Fog density
|
||||
// ^^ Linear fog start
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
@@ -18,6 +22,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
@@ -30,6 +35,7 @@ import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -103,6 +109,9 @@ class GLContextState {
|
||||
public final FogStateStack fogState = new FogStateStack();
|
||||
public final BooleanStateStack fogMode = track(new BooleanStateStack(GL11.GL_FOG, false, true));
|
||||
public final Color4Stack color = new Color4Stack();
|
||||
// GL_CURRENT_SECONDARY_COLOR - alpha is always 1 and unused, the color sum only adds RGB
|
||||
public final Color4Stack secondaryColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 1.0F));
|
||||
public final BooleanStateStack colorSumState = track(new BooleanStateStack(GL14.GL_COLOR_SUM, false, true));
|
||||
public final Color4Stack clearColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 0.0F));
|
||||
public final ColorMaskStack colorMask = new ColorMaskStack();
|
||||
public final IntegerStateStack drawBuffer = new IntegerStateStack(GLStateManager.DEFAULT_DRAW_BUFFER);
|
||||
@@ -175,12 +184,19 @@ class GLContextState {
|
||||
public int boundVAO;
|
||||
public int boundPixelUnpackBuffer;
|
||||
public int boundPixelPackBuffer;
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
public final IntSet writeMappedBuffers = new IntOpenHashSet();
|
||||
public PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
|
||||
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public final boolean[] restoreUnitChanged = new boolean[GLStateManager.MAX_TEXTURE_UNITS];
|
||||
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged;
|
||||
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged, restoreSecondaryColorChanged;
|
||||
public final BlendState vanillaBlendBefore = new BlendState();
|
||||
public final BlendState vanillaBlendAfter = new BlendState();
|
||||
public boolean vanillaBlendEnabledBefore;
|
||||
public int drawFramebuffer = 0;
|
||||
public int readFramebuffer = 0;
|
||||
public int texGenGeneration;
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
@@ -45,6 +46,8 @@ import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
@@ -54,6 +57,7 @@ import com.gtnewhorizons.angelica.glsm.states.TextureBinding;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntStack;
|
||||
@@ -502,6 +506,7 @@ public class GLStateManager {
|
||||
.addFeature(GL11.GL_COLOR_WRITEMASK)
|
||||
.addFeature(GL11.GL_CURRENT_COLOR)
|
||||
.addFeature(GL11.GL_CURRENT_NORMAL)
|
||||
.addFeature(GL14.GL_CURRENT_SECONDARY_COLOR)
|
||||
.addFeature(GL11.GL_CURRENT_RASTER_COLOR)
|
||||
.addFeature(GL11.GL_CURRENT_RASTER_POSITION)
|
||||
.addFeature(GL11.GL_CURRENT_RASTER_TEXTURE_COORDS)
|
||||
@@ -657,6 +662,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> enableBlend();
|
||||
case GL11.GL_COLOR_MATERIAL -> enableColorMaterial();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.enable();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.enable();
|
||||
case GL11.GL_CULL_FACE -> enableCull();
|
||||
case GL11.GL_DEPTH_TEST -> enableDepthTest();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.enable();
|
||||
@@ -738,6 +744,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> disableBlend();
|
||||
case GL11.GL_COLOR_MATERIAL -> disableColorMaterial();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.disable();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.disable();
|
||||
case GL11.GL_CULL_FACE -> disableCull();
|
||||
case GL11.GL_DEPTH_TEST -> disableDepthTest();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.disable();
|
||||
@@ -813,6 +820,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
|
||||
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
|
||||
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
|
||||
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
|
||||
case GL11.GL_DITHER -> glCtx.ditherState.isEnabled();
|
||||
@@ -888,6 +896,7 @@ public class GLStateManager {
|
||||
case GL11.GL_BLEND -> glCtx.blendMode.isEnabled();
|
||||
case GL11.GL_COLOR_MATERIAL -> glCtx.colorMaterial.isEnabled();
|
||||
case GL11.GL_COLOR_LOGIC_OP -> glCtx.colorLogicOpState.isEnabled();
|
||||
case GL14.GL_COLOR_SUM -> glCtx.colorSumState.isEnabled();
|
||||
case GL11.GL_CULL_FACE -> glCtx.cullState.isEnabled();
|
||||
case GL11.GL_DEPTH_TEST -> glCtx.depthTest.isEnabled();
|
||||
case GL11.GL_DEPTH_WRITEMASK -> glCtx.depthState.isEnabled();
|
||||
@@ -1131,6 +1140,7 @@ public class GLStateManager {
|
||||
case GL11.GL_TEXTURE_MATRIX -> glCtx.textures.getTextureUnitMatrix(getActiveTextureUnit()).get(0, params);
|
||||
case GL11.GL_COLOR_CLEAR_VALUE -> glCtx.clearColor.get(params);
|
||||
case GL11.GL_CURRENT_COLOR -> glCtx.color.get(params);
|
||||
case GL14.GL_CURRENT_SECONDARY_COLOR -> glCtx.secondaryColor.get(params);
|
||||
case GL11.GL_DEPTH_RANGE -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, (float) glCtx.viewportState.depthRangeNear);
|
||||
@@ -1229,7 +1239,7 @@ public class GLStateManager {
|
||||
}
|
||||
case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> widenFromFloat(pname, params, 16);
|
||||
case GL11.GL_COLOR_CLEAR_VALUE, GL11.GL_CURRENT_COLOR, GL11.GL_CURRENT_TEXTURE_COORDS, GL11.GL_FOG_COLOR,
|
||||
GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR -> widenFromFloat(pname, params, 4);
|
||||
GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR -> widenFromFloat(pname, params, 4);
|
||||
case GL11.GL_CURRENT_NORMAL -> widenFromFloat(pname, params, 3);
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
@@ -1302,12 +1312,16 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final BooleanStateStack blend = ctx().blendMode;
|
||||
final boolean wasEnabled = blend.isEffectivelyEnabled();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
blend.beforeModify();
|
||||
bh.deferBlendModeToggle(true);
|
||||
return;
|
||||
} else {
|
||||
blend.enable();
|
||||
}
|
||||
ctx().blendMode.enable();
|
||||
if (!wasEnabled && blend.isEffectivelyEnabled()) postVanillaBlendChange();
|
||||
}
|
||||
|
||||
public static void disableBlend() {
|
||||
@@ -1318,12 +1332,80 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final BooleanStateStack blend = ctx().blendMode;
|
||||
final boolean wasEnabled = blend.isEffectivelyEnabled();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
blend.beforeModify();
|
||||
bh.deferBlendModeToggle(false);
|
||||
return;
|
||||
} else {
|
||||
blend.disable();
|
||||
}
|
||||
ctx().blendMode.disable();
|
||||
if (wasEnabled && !blend.isEffectivelyEnabled()) postVanillaBlendChange();
|
||||
}
|
||||
|
||||
private static void postVanillaBlendChange() {
|
||||
if (GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) {
|
||||
GLSMHooks.VANILLA_BLEND_CHANGE.post(GLSMHooks.vanillaBlendChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean snapshotVanillaBlendFunc() {
|
||||
if (!GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) return false;
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.blendState.readEffective(glCtx.vanillaBlendBefore);
|
||||
glCtx.vanillaBlendEnabledBefore = glCtx.blendMode.isEffectivelyEnabled();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void postVanillaBlendChangeIfMoved(boolean snapshotted) {
|
||||
if (!snapshotted) return;
|
||||
final GLContextState glCtx = ctx();
|
||||
final BlendState before = glCtx.vanillaBlendBefore;
|
||||
final BlendState after = glCtx.blendState.readEffective(glCtx.vanillaBlendAfter);
|
||||
if (glCtx.blendMode.isEffectivelyEnabled() != glCtx.vanillaBlendEnabledBefore
|
||||
|| after.getSrcRgb() != before.getSrcRgb()
|
||||
|| after.getDstRgb() != before.getDstRgb()
|
||||
|| after.getSrcAlpha() != before.getSrcAlpha()
|
||||
|| after.getDstAlpha() != before.getDstAlpha()) {
|
||||
postVanillaBlendChange();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEffectiveBlendEnabled() {
|
||||
return ctx().blendMode.isEffectivelyEnabled();
|
||||
}
|
||||
|
||||
public static BlendState getEffectiveBlendState(BlendState out) {
|
||||
return ctx().blendState.readEffective(out);
|
||||
}
|
||||
|
||||
public static boolean isEffectiveDepthMaskEnabled() {
|
||||
return ctx().depthState.isEffectiveMaskEnabled();
|
||||
}
|
||||
|
||||
public static boolean isEffectiveAlphaTestEnabled() {
|
||||
return ctx().alphaTest.isEffectivelyEnabled();
|
||||
}
|
||||
|
||||
public static AlphaState getEffectiveAlphaState(AlphaState out) {
|
||||
return ctx().alphaState.readEffective(out);
|
||||
}
|
||||
|
||||
private static int foreignDrawDepth;
|
||||
|
||||
public static void beginForeignDraw() {
|
||||
foreignDrawDepth++;
|
||||
}
|
||||
|
||||
public static void endForeignDraw() {
|
||||
if (foreignDrawDepth > 0 && --foreignDrawDepth == 0 && GLSMHooks.FOREIGN_DRAW_END.hasListeners()) {
|
||||
GLSMHooks.FOREIGN_DRAW_END.post(GLSMHooks.foreignDrawEndEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isForeignDraw() {
|
||||
return foreignDrawDepth > 0;
|
||||
}
|
||||
|
||||
public static void enableScissorTest() {
|
||||
@@ -1357,9 +1439,11 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean snapshotted = snapshotVanillaBlendFunc();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
bh.deferBlendFunc(srcFactor, dstFactor, srcFactor, dstFactor);
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
return;
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
@@ -1373,6 +1457,7 @@ public class GLStateManager {
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
return;
|
||||
}
|
||||
final boolean bypass = !caching;
|
||||
@@ -1391,6 +1476,7 @@ public class GLStateManager {
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
}
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
}
|
||||
|
||||
public static void glBlendEquation(int mode) {
|
||||
@@ -1430,9 +1516,11 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean snapshotted = snapshotVanillaBlendFunc();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
bh.deferBlendFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
return;
|
||||
}
|
||||
if (GLSMConfig.hudCacheOverride && dstAlpha != GL11.GL_ONE_MINUS_SRC_ALPHA) {
|
||||
@@ -1452,6 +1540,7 @@ public class GLStateManager {
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
}
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
}
|
||||
|
||||
public static void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
|
||||
@@ -1673,6 +1762,40 @@ public class GLStateManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3f(float red, float green, float blue) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordSecondaryColor(red, green, blue);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
changeSecondaryColor(red, green, blue);
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3d(double red, double green, double blue) {
|
||||
glSecondaryColor3f((float) red, (float) green, (float) blue);
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3b(byte red, byte green, byte blue) {
|
||||
glSecondaryColor3f(b2f(red), b2f(green), b2f(blue));
|
||||
}
|
||||
|
||||
public static void glSecondaryColor3ub(byte red, byte green, byte blue) {
|
||||
glSecondaryColor3f(ub2f(red), ub2f(green), ub2f(blue));
|
||||
}
|
||||
|
||||
/** Helper for glSecondaryColor* - the color sum is emulated by the FFP shaders, nothing is sent to the driver. */
|
||||
private static void changeSecondaryColor(float red, float green, float blue) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled() || red != glCtx.secondaryColor.getRed() || green != glCtx.secondaryColor.getGreen() || blue != glCtx.secondaryColor.getBlue()) {
|
||||
glCtx.secondaryColor.setRed(red);
|
||||
glCtx.secondaryColor.setGreen(green);
|
||||
glCtx.secondaryColor.setBlue(blue);
|
||||
glCtx.fragmentGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Color4 DirtyColor = new Color4(-1.0F, -1.0F, -1.0F, -1.0F);
|
||||
|
||||
public static void clearCurrentColor() {
|
||||
@@ -1746,6 +1869,7 @@ public class GLStateManager {
|
||||
}
|
||||
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
|
||||
if (ah != null && ah.isAlphaTestLocked()) {
|
||||
glCtx.alphaTest.beforeModify();
|
||||
ah.deferAlphaTestToggle(true);
|
||||
return;
|
||||
}
|
||||
@@ -1767,6 +1891,7 @@ public class GLStateManager {
|
||||
}
|
||||
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
|
||||
if (ah != null && ah.isAlphaTestLocked()) {
|
||||
glCtx.alphaTest.beforeModify();
|
||||
ah.deferAlphaTestToggle(false);
|
||||
return;
|
||||
}
|
||||
@@ -2454,12 +2579,17 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
final boolean ffpExt = FfpExtendedAttribs.maybeBindIndexed(mode, indices_count, type, indices_buffer_offset);
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (ffpExt) FfpExtendedAttribs.unbind();
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
@@ -2569,19 +2699,24 @@ public class GLStateManager {
|
||||
FeedbackManager.processDrawArrays(mode, first, count);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(first, count);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
|
||||
} else {
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawArrays(mode, first, count);
|
||||
final boolean ffpExt = FfpExtendedAttribs.maybeBind(mode, first, count);
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(first, count);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
preDraw();
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
|
||||
} else {
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawArrays(mode, first, count);
|
||||
}
|
||||
} finally {
|
||||
if (ffpExt) FfpExtendedAttribs.unbind();
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
|
||||
@@ -3532,6 +3667,9 @@ public class GLStateManager {
|
||||
final int mask = glCtx.attribs.popInt();
|
||||
glCtx.attribDepth--;
|
||||
|
||||
// Snapshot before the stacks pop, so applyRestoredState can tell whether vanilla blend actually moved.
|
||||
final boolean blendSnapshotted = (mask & (GL11.GL_COLOR_BUFFER_BIT | GL11.GL_ENABLE_BIT)) != 0 && snapshotVanillaBlendFunc();
|
||||
|
||||
// First: restore BooleanStateStack states that were actually modified (fast path)
|
||||
// These use lazy copy-on-write with global depth tracking
|
||||
final List<IStateStack<?>> modified = glCtx.modifiedAtDepth[glCtx.attribDepth];
|
||||
@@ -3550,7 +3688,12 @@ public class GLStateManager {
|
||||
|
||||
// Third: apply restored state to the GL driver. BooleanStateStacks already issue GL calls via setEnabled(); non-boolean stacks are pure data
|
||||
// containers, so we must explicitly drive GL here.
|
||||
applyRestoredState(mask);
|
||||
applyRestoredState(mask, blendSnapshotted);
|
||||
}
|
||||
|
||||
private static boolean isDepthColorOverridden() {
|
||||
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
|
||||
return dch != null && dch.isOverrideHeld();
|
||||
}
|
||||
|
||||
private static void captureRestoreChanges(int mask) {
|
||||
@@ -3565,6 +3708,9 @@ public class GLStateManager {
|
||||
glCtx.restoreDrawBufferChanged = glCtx.drawBuffer.topChanged();
|
||||
glCtx.restoreLogicOpChanged = glCtx.logicOpMode.topChanged();
|
||||
}
|
||||
if ((mask & GL11.GL_CURRENT_BIT) != 0) {
|
||||
glCtx.restoreSecondaryColorChanged = glCtx.secondaryColor.topChanged();
|
||||
}
|
||||
if ((mask & GL11.GL_STENCIL_BUFFER_BIT) != 0) {
|
||||
glCtx.restoreStencilChanged = glCtx.stencilState.topChanged();
|
||||
}
|
||||
@@ -3639,20 +3785,25 @@ public class GLStateManager {
|
||||
/**
|
||||
* After popping GLSM stacks, apply restored state to the GL driver.
|
||||
*/
|
||||
private static void applyRestoredState(int mask) {
|
||||
private static void applyRestoredState(int mask, boolean blendSnapshotted) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if ((mask & GL11.GL_DEPTH_BUFFER_BIT) != 0 && glCtx.restoreDepthChanged) {
|
||||
RENDER_BACKEND.depthFunc(glCtx.depthState.getFunc());
|
||||
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
|
||||
if (!isDepthColorOverridden()) {
|
||||
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
|
||||
}
|
||||
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
|
||||
}
|
||||
if ((mask & GL11.GL_COLOR_BUFFER_BIT) != 0) {
|
||||
if (glCtx.restoreBlendChanged) {
|
||||
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
|
||||
final DeferredBlendHandler restoreBlendHandler = GLSMHooks.blendHandler;
|
||||
if (restoreBlendHandler == null || !restoreBlendHandler.isOverrideHeld()) {
|
||||
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
|
||||
}
|
||||
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
|
||||
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
|
||||
}
|
||||
if (glCtx.restoreColorMaskChanged) {
|
||||
if (glCtx.restoreColorMaskChanged && !isDepthColorOverridden()) {
|
||||
RENDER_BACKEND.colorMask(glCtx.colorMask.red, glCtx.colorMask.green, glCtx.colorMask.blue, glCtx.colorMask.alpha);
|
||||
}
|
||||
if (glCtx.restoreClearColorChanged) {
|
||||
@@ -3736,7 +3887,11 @@ public class GLStateManager {
|
||||
ShaderManager.bumpTexCoordGeneration();
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
}
|
||||
// The secondary color is a fragment stage uniform, not a vertex attribute
|
||||
if (glCtx.restoreSecondaryColorChanged) glCtx.fragmentGeneration++;
|
||||
}
|
||||
|
||||
postVanillaBlendChangeIfMoved(blendSnapshotted);
|
||||
}
|
||||
|
||||
public static void glClear(int mask) {
|
||||
@@ -4925,16 +5080,22 @@ public class GLStateManager {
|
||||
final ShaderManager ffp = ShaderManager.getInstance();
|
||||
if (program == 0 && ffp.isEnabled()) {
|
||||
final int prev = glCtx.activeProgram;
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (prev != 0) glCtx.programGeneration++;
|
||||
final boolean changed = prev != 0;
|
||||
if (changed) glCtx.programGeneration++;
|
||||
glCtx.activeProgram = 0; // Track that FFP was requested
|
||||
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = 0;
|
||||
GLSMHooks.programChangeEvent.postBind = false;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
ffp.activate();
|
||||
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = 0;
|
||||
GLSMHooks.programChangeEvent.postBind = true;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5737,10 +5898,19 @@ public class GLStateManager {
|
||||
if (buffer == 0) return;
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
FfpExtendedAttribs.onDeleteBuffer(buffer);
|
||||
if (glCtx.boundVBO == buffer) glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
if (VAOManager.boundEBO == buffer) glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
if (glCtx.boundPixelUnpackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
if (glCtx.boundPixelPackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
if (glCtx.boundCopyReadBuffer == buffer) glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0);
|
||||
if (glCtx.boundCopyWriteBuffer == buffer) glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0);
|
||||
for (Int2IntMap.Entry e : glCtx.boundOtherBuffers.int2IntEntrySet()) {
|
||||
if (e.getIntValue() != buffer) continue;
|
||||
e.setValue(0);
|
||||
RENDER_BACKEND.bindBuffer(e.getIntKey(), 0);
|
||||
}
|
||||
glCtx.writeMappedBuffers.remove(buffer);
|
||||
|
||||
VAOManager.onDeleteBuffer(buffer);
|
||||
} finally {
|
||||
@@ -5748,20 +5918,30 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** glBindBufferBase/Range also set the generic binding point, so they share this. */
|
||||
private static void trackBufferBinding(GLContextState glCtx, int target, int buffer) {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) {
|
||||
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
|
||||
glCtx.boundVBO = buffer;
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
VAOManager.onBindEBO(buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
glCtx.boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
glCtx.boundPixelPackBuffer = buffer;
|
||||
} else if (target == GL31.GL_COPY_READ_BUFFER) {
|
||||
glCtx.boundCopyReadBuffer = buffer;
|
||||
} else if (target == GL31.GL_COPY_WRITE_BUFFER) {
|
||||
glCtx.boundCopyWriteBuffer = buffer;
|
||||
} else {
|
||||
glCtx.boundOtherBuffers.put(target, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindBuffer(int target, int buffer) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) {
|
||||
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
|
||||
glCtx.boundVBO = buffer;
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
VAOManager.onBindEBO(buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
glCtx.boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
glCtx.boundPixelPackBuffer = buffer;
|
||||
}
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
RENDER_BACKEND.bindBuffer(target, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
@@ -5840,15 +6020,78 @@ public class GLStateManager {
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
public static void glBufferData(int target, long size, int usage) { RENDER_BACKEND.bufferData(target, size, usage); }
|
||||
public static void glBufferData(int target, ByteBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, ShortBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, IntBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, FloatBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, DoubleBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
private static final int MAP_PERSISTENT_BIT = 0x40;
|
||||
|
||||
private static int bufferForTarget(int target) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL15.GL_ARRAY_BUFFER) return glCtx.boundVBO;
|
||||
if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) return VAOManager.boundEBO;
|
||||
if (target == GL31.GL_COPY_READ_BUFFER) return glCtx.boundCopyReadBuffer;
|
||||
if (target == GL31.GL_COPY_WRITE_BUFFER) return glCtx.boundCopyWriteBuffer;
|
||||
if (target == GL21.GL_PIXEL_UNPACK_BUFFER) return glCtx.boundPixelUnpackBuffer;
|
||||
if (target == GL21.GL_PIXEL_PACK_BUFFER) return glCtx.boundPixelPackBuffer;
|
||||
return glCtx.boundOtherBuffers.get(target);
|
||||
}
|
||||
|
||||
private static void onBufferRespec(int target) {
|
||||
if (FfpExtendedAttribs.isEmpty()) return;
|
||||
final int buffer = bufferForTarget(target);
|
||||
if (buffer != 0) FfpExtendedAttribs.onBufferRespecified(buffer);
|
||||
}
|
||||
|
||||
private static void onBufferMap(int target, int access) {
|
||||
if (access == GL15.GL_READ_ONLY) return;
|
||||
markWriteMapped(target);
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void onBufferMapRange(int target, int access) {
|
||||
if ((access & GL30.GL_MAP_WRITE_BIT) == 0) return;
|
||||
if ((access & MAP_PERSISTENT_BIT) != 0) {
|
||||
markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
markWriteMapped(target);
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void markWriteMapped(int target) {
|
||||
final int buffer = bufferForTarget(target);
|
||||
if (buffer != 0) ctx().writeMappedBuffers.add(buffer);
|
||||
}
|
||||
|
||||
private static void onBufferUnmap(int target) {
|
||||
final int buffer = bufferForTarget(target);
|
||||
if (buffer == 0 || !ctx().writeMappedBuffers.remove(buffer)) return;
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void onBufferStorage(int target, int flags) {
|
||||
if ((flags & MAP_PERSISTENT_BIT) != 0) {
|
||||
markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
onBufferRespec(target);
|
||||
}
|
||||
|
||||
private static void markPersistentlyMapped(int target) {
|
||||
FfpExtendedAttribs.markPersistent(bufferForTarget(target));
|
||||
}
|
||||
|
||||
private static void onNamedBufferRespec(int buffer) {
|
||||
if (FfpExtendedAttribs.isEmpty()) return;
|
||||
FfpExtendedAttribs.onBufferRespecified(buffer);
|
||||
}
|
||||
|
||||
public static void glBufferData(int target, long size, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, size, usage); }
|
||||
public static void glBufferData(int target, ByteBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, ShortBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, IntBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, FloatBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, DoubleBuffer data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, LongBuffer data, int usage) { glBufferData(target, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glBufferData(int target, int[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, float[] data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, int[] data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, float[] data, int usage) { onBufferRespec(target); RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferData(int target, short[] data, int usage) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
glBufferData(target, copy, usage);
|
||||
@@ -5873,11 +6116,11 @@ public class GLStateManager {
|
||||
glBufferData(target, MemoryUtilities.memByteBuffer(data, checkedSize(size)), usage);
|
||||
}
|
||||
|
||||
public static void glBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, IntBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, FloatBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, DoubleBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, ShortBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, IntBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, FloatBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, DoubleBuffer data) { onBufferRespec(target); RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static void glBufferSubData(int target, long offset, LongBuffer data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glBufferSubData(int target, long offset, short[] data) {
|
||||
final ByteBuffer copy = copyOf(data);
|
||||
@@ -5907,13 +6150,13 @@ public class GLStateManager {
|
||||
|
||||
public static void nglBufferSubData(int target, long offset, long size, long data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, ByteBuffer old_buffer) { return glMapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { onBufferMapRange(target, access); return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access, ByteBuffer old_buffer) { return glMapBufferRange(target, offset, length, access); }
|
||||
public static void glFlushMappedBufferRange(int target, long offset, long length) { RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
|
||||
public static boolean glUnmapBuffer(int target) { return RENDER_BACKEND.unmapBuffer(target); }
|
||||
public static boolean glUnmapBuffer(int target) { onBufferUnmap(target); return RENDER_BACKEND.unmapBuffer(target); }
|
||||
|
||||
public static void glGetBufferSubData(int target, long offset, ByteBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static void glGetBufferSubData(int target, long offset, ShortBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
@@ -5960,8 +6203,8 @@ public class GLStateManager {
|
||||
public static void glGetBufferParameteriv(int target, int pname, IntBuffer params) { params.put(params.position(), glGetBufferParameteri(target, pname)); }
|
||||
public static void glGetBufferParameteriv(int target, int pname, int[] params) { params[0] = glGetBufferParameteri(target, pname); }
|
||||
|
||||
public static void glBufferStorage(int target, long size, int flags) { RENDER_BACKEND.bufferStorage(target, size, flags); }
|
||||
public static void glBufferStorage(int target, ByteBuffer data, int flags) { RENDER_BACKEND.bufferStorage(target, data, flags); }
|
||||
public static void glBufferStorage(int target, long size, int flags) { onBufferStorage(target, flags); RENDER_BACKEND.bufferStorage(target, size, flags); }
|
||||
public static void glBufferStorage(int target, ByteBuffer data, int flags) { onBufferStorage(target, flags); RENDER_BACKEND.bufferStorage(target, data, flags); }
|
||||
public static void glBufferStorage(int target, ShortBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, IntBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
public static void glBufferStorage(int target, FloatBuffer data, int flags) { glBufferStorage(target, MemoryUtilities.memByteBuffer(data), flags); }
|
||||
@@ -5987,9 +6230,9 @@ public class GLStateManager {
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferData(int buffer, long size, int usage) { RENDER_BACKEND.namedBufferData(buffer, size, usage); }
|
||||
public static void glNamedBufferData(int buffer, ByteBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, FloatBuffer data, int usage) { RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, long size, int usage) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferData(buffer, size, usage); }
|
||||
public static void glNamedBufferData(int buffer, ByteBuffer data, int usage) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, FloatBuffer data, int usage) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferData(buffer, data, usage); }
|
||||
public static void glNamedBufferData(int buffer, ShortBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, IntBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
public static void glNamedBufferData(int buffer, LongBuffer data, int usage) { glNamedBufferData(buffer, MemoryUtilities.memByteBuffer(data), usage); }
|
||||
@@ -6020,7 +6263,7 @@ public class GLStateManager {
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ByteBuffer data) { RENDER_BACKEND.namedBufferSubData(buffer, offset, data); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ByteBuffer data) { onNamedBufferRespec(buffer); RENDER_BACKEND.namedBufferSubData(buffer, offset, data); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, ShortBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, IntBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
public static void glNamedBufferSubData(int buffer, long offset, LongBuffer data) { glNamedBufferSubData(buffer, offset, MemoryUtilities.memByteBuffer(data)); }
|
||||
@@ -6052,15 +6295,15 @@ public class GLStateManager {
|
||||
MemoryUtilities.memFree(copy);
|
||||
}
|
||||
|
||||
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
|
||||
public static void glClearBufferData(int target, int internalformat, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearBufferData(target, internalformat, format, type, data); }
|
||||
public static void glClearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.clearBufferSubData(target, internalFormat, offset, size, format, type, data); }
|
||||
public static void glClearBufferData(int target, int internalformat, int format, int type, ByteBuffer data) { onBufferRespec(target); RENDER_BACKEND.clearBufferData(target, internalformat, format, type, data); }
|
||||
public static boolean glIsBuffer(int buffer) { return RENDER_BACKEND.isBuffer(buffer); }
|
||||
public static boolean glIsTexture(int texture) { return RENDER_BACKEND.isTexture(texture); }
|
||||
public static boolean glIsFramebuffer(int framebuffer) { return RENDER_BACKEND.isFramebuffer(framebuffer); }
|
||||
public static boolean glIsRenderbuffer(int renderbuffer) { return RENDER_BACKEND.isRenderbuffer(renderbuffer); }
|
||||
public static boolean glIsSampler(int sampler) { return RENDER_BACKEND.isSampler(sampler); }
|
||||
public static boolean glIsQuery(int query) { return RENDER_BACKEND.isQuery(query); }
|
||||
public static void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
|
||||
public static void glCopyBufferSubData(int readTarget, int writeTarget, long readOffset, long writeOffset, long size) { onBufferRespec(writeTarget); RENDER_BACKEND.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); }
|
||||
|
||||
private static int checkedSize(long size) {
|
||||
if (size < 0 || size > Integer.MAX_VALUE) {
|
||||
@@ -6224,7 +6467,26 @@ public class GLStateManager {
|
||||
public static void glGetQueryObjectui(int id, int pname, IntBuffer params) { RENDER_BACKEND.getQueryObjectui(id, pname, params); }
|
||||
public static int glGetQueryObjecti(int id, int pname) { return RENDER_BACKEND.getQueryObjecti(id, pname); }
|
||||
|
||||
public static void glBindBufferBase(int target, int index, int buffer) { RENDER_BACKEND.bindBufferBase(target, index, buffer); }
|
||||
public static void glBindBufferBase(int target, int index, int buffer) {
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
RENDER_BACKEND.bindBufferBase(target, index, buffer);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindBufferRange(int target, int index, int buffer, long offset, long size) {
|
||||
final boolean locked = acquireDrawLock();
|
||||
try {
|
||||
trackBufferBinding(ctx(), target, buffer);
|
||||
RENDER_BACKEND.bindBufferRange(target, index, buffer, offset, size);
|
||||
} finally {
|
||||
if (locked) releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGetUniformBlockIndex(int program, CharSequence name) { return RENDER_BACKEND.getUniformBlockIndex(program, name); }
|
||||
public static void glUniformBlockBinding(int program, int blockIndex, int binding) { RENDER_BACKEND.uniformBlockBinding(program, blockIndex, binding); }
|
||||
public static void glBindFragDataLocation(int program, int colorNumber, CharSequence name) { RENDER_BACKEND.bindFragDataLocation(program, colorNumber, name); }
|
||||
@@ -6262,6 +6524,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
public static long nglMapBuffer(int target, int access) {
|
||||
onBufferMap(target, access);
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
return buf == null ? 0L : MemoryUtilities.memAddress0(buf);
|
||||
}
|
||||
@@ -7307,6 +7570,14 @@ public class GLStateManager {
|
||||
return ctx().color;
|
||||
}
|
||||
|
||||
public static Color4Stack getSecondaryColor() {
|
||||
return ctx().secondaryColor;
|
||||
}
|
||||
|
||||
public static BooleanStateStack getColorSumState() {
|
||||
return ctx().colorSumState;
|
||||
}
|
||||
|
||||
public static Color4Stack getClearColor() {
|
||||
return ctx().clearColor;
|
||||
}
|
||||
@@ -7571,6 +7842,14 @@ public class GLStateManager {
|
||||
return ctx().boundVBO;
|
||||
}
|
||||
|
||||
public static int getBoundCopyReadBuffer() {
|
||||
return ctx().boundCopyReadBuffer;
|
||||
}
|
||||
|
||||
public static int getBoundCopyWriteBuffer() {
|
||||
return ctx().boundCopyWriteBuffer;
|
||||
}
|
||||
|
||||
public static int getBoundVAO() {
|
||||
return ctx().boundVAO;
|
||||
}
|
||||
|
||||
@@ -559,7 +559,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
RENDER_BACKEND.bindBufferBase(target, index, buffer);
|
||||
GLStateManager.glBindBufferBase(target, index, buffer);
|
||||
}
|
||||
|
||||
public static void bufferStorage(int target, long size, int flags) {
|
||||
|
||||
@@ -103,6 +103,7 @@ public final class FFPUniformBlock {
|
||||
public static final int TEX_ENV_COLOR_2 = vec4("u_TexEnvColor2");
|
||||
public static final int TEX_ENV_COLOR_3 = vec4("u_TexEnvColor3");
|
||||
public static final int OVERLAY_COLOR = vec4("u_OverlayColor");
|
||||
public static final int SECONDARY_COLOR = vec3("u_SecondaryColor");
|
||||
public static final int FOG_PARAMS = vec4("u_FogParams");
|
||||
public static final int FOG_COLOR = vec4("u_FogColor");
|
||||
|
||||
|
||||
@@ -0,0 +1,583 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetShort;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Supplies {@code mc_midTexCoord}/{@code at_tangent} for mod geometry drawn through the FFPclient-array path
|
||||
* ({@code glVertexPointer}/{@code glTexCoordPointer} + {@code glDrawArrays}) out of a VBO, which bypasses the
|
||||
* Tessellator and display-list ext-injection paths.
|
||||
*
|
||||
* <p>Tangents are reconstructed from the flat face normal (handler's {@code normalOffset < 0} path).
|
||||
*/
|
||||
public final class FfpExtendedAttribs {
|
||||
|
||||
private FfpExtendedAttribs() {}
|
||||
|
||||
private static final int POSITION_LOC = Usage.POSITION.getAttributeLocation();
|
||||
private static final int UV_LOC = Usage.PRIMARY_UV.getAttributeLocation();
|
||||
private static final int NORMAL_LOC = Usage.NORMAL.getAttributeLocation();
|
||||
private static final int SRC_TEX_OFFSET = 12;
|
||||
private static final int SRC_NORMAL_OFFSET = 20;
|
||||
private static final int SRC_STRIDE_FLAT = 20;
|
||||
private static final int SRC_STRIDE_SMOOTH = 24;
|
||||
private static final int MAX_CACHE = 1024;
|
||||
private static final int MAX_EXT_VERTS = 1 << 20;
|
||||
private static final int MAX_RESPECS = 8;
|
||||
private static final int RESPEC_DECAY_FRAMES = 2;
|
||||
private static final int MIN_STREAM_SPAN_FRAMES = 4;
|
||||
private static final int STREAM_EXPIRE_FRAMES = 60;
|
||||
|
||||
private static final HashMap<Key, ExtBuffer> cache = new HashMap<>();
|
||||
private static final Key lookup = new Key();
|
||||
private static int internalGlDepth = 0;
|
||||
|
||||
private static final HashMap<EboKey, Boolean> eboPatternCache = new HashMap<>();
|
||||
private static final EboKey eboLookup = new EboKey();
|
||||
|
||||
private static final Int2ObjectMap<ObjectSet<Key>> keysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<ObjectSet<EboKey>> eboKeysBySource = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2IntMap respecCounts = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecFrames = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap respecRunStart = new Int2IntOpenHashMap();
|
||||
private static final IntSet streamingSources = new IntOpenHashSet();
|
||||
private static final IntSet persistentSources = new IntOpenHashSet();
|
||||
private static int frame;
|
||||
|
||||
public static void endFrame() { frame++; }
|
||||
|
||||
public static boolean isEmpty() {
|
||||
return cache.isEmpty() && eboPatternCache.isEmpty();
|
||||
}
|
||||
|
||||
private static int internalDrawDepth = 0;
|
||||
|
||||
public static void beginInternalDraw() { internalDrawDepth++; }
|
||||
|
||||
public static void endInternalDraw() { if (internalDrawDepth > 0) internalDrawDepth--; }
|
||||
|
||||
private static void beginInternalGl() { internalGlDepth++; }
|
||||
|
||||
private static void endInternalGl() { if (internalGlDepth > 0) internalGlDepth--; }
|
||||
|
||||
private static boolean isNotConventionalTexturedArray(VAOManager.Attrib a) {
|
||||
return a == null || !a.enabled || a.genericPointer || a.vboId == 0 || a.size < 2;
|
||||
}
|
||||
|
||||
private static boolean isStreaming(int vboId) {
|
||||
if (vboId == 0 || !streamingSources.contains(vboId)) return false;
|
||||
if (persistentSources.contains(vboId)) return true;
|
||||
if (frame - respecFrames.get(vboId) > STREAM_EXPIRE_FRAMES) {
|
||||
streamingSources.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean maybeBind(int mode, int first, int count) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(mode, count);
|
||||
if (extPrim == 0 || first < 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId)) return false;
|
||||
|
||||
VAOManager.Attrib normal = extPrim == 3 ? VAOManager.get(NORMAL_LOC) : null;
|
||||
if (normal != null && (!normal.enabled || normal.genericPointer || normal.vboId == 0 || normal.size < 3
|
||||
|| isStreaming(normal.vboId))) {
|
||||
normal = null;
|
||||
}
|
||||
|
||||
return bindExt(h, mode, first, count, extPrim, pos, uv, normal);
|
||||
}
|
||||
|
||||
public static boolean maybeBindIndexed(int mode, int indexCount, int indexType, long indicesOffset) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
if (mode != GL11.GL_TRIANGLES || indexCount <= 0 || indexCount % 6 != 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId) || isStreaming(VAOManager.boundEBO)) return false;
|
||||
|
||||
final int indexSize = indexTypeBytes(indexType);
|
||||
if (indexSize == 0) return false;
|
||||
if (indicesOffset % (6L * indexSize) != 0) return false;
|
||||
final int first = (int) (indicesOffset / indexSize) / 6 * 4;
|
||||
final int count = indexCount / 6 * 4;
|
||||
|
||||
if (!isQuadPatternEbo(VAOManager.boundEBO, indicesOffset, indexCount, indexType, indexSize, first)) return false;
|
||||
|
||||
// Build/bind as GL_QUADS (the real primitive): face-normal tangents, 4-vertex tiles, no per-vertex normal.
|
||||
return bindExt(h, GL11.GL_QUADS, first, count, 4, pos, uv, null);
|
||||
}
|
||||
|
||||
private static boolean isQuadPatternEbo(int ebo, long offset, int indexCount, int indexType, int indexSize, int firstVertex) {
|
||||
if (ebo == 0) return false;
|
||||
eboLookup.set(ebo, offset, indexCount, indexType);
|
||||
final Boolean cached = eboPatternCache.get(eboLookup);
|
||||
if (cached != null) return cached;
|
||||
if (eboPatternCache.size() >= MAX_CACHE) {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
}
|
||||
|
||||
final ByteBuffer buf = memCalloc(indexCount, indexSize);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, buf);
|
||||
final long addr = memAddress0(buf);
|
||||
boolean match = true;
|
||||
for (int q = 0, quadCount = indexCount / 6; q < quadCount; q++) {
|
||||
final int base = firstVertex + q * 4;
|
||||
final long p = addr + (long) q * 6 * indexSize;
|
||||
if (readIndex(p, 0, indexSize) != base
|
||||
|| readIndex(p, 1, indexSize) != base + 1
|
||||
|| readIndex(p, 2, indexSize) != base + 2
|
||||
|| readIndex(p, 3, indexSize) != base + 2
|
||||
|| readIndex(p, 4, indexSize) != base + 3
|
||||
|| readIndex(p, 5, indexSize) != base) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
memFree(buf);
|
||||
final EboKey stored = eboLookup.copy();
|
||||
eboPatternCache.put(stored, match);
|
||||
ObjectSet<EboKey> ebos = eboKeysBySource.get(stored.ebo);
|
||||
if (ebos == null) eboKeysBySource.put(stored.ebo, ebos = new ObjectOpenHashSet<>());
|
||||
ebos.add(stored);
|
||||
return match;
|
||||
}
|
||||
|
||||
private static long readIndex(long base, int i, int indexSize) {
|
||||
return switch (indexSize) {
|
||||
case 1 -> memGetByte(base + i) & 0xFFL;
|
||||
case 2 -> memGetShort(base + (long) i * 2) & 0xFFFFL;
|
||||
default -> memGetInt(base + (long) i * 4) & 0xFFFFFFFFL;
|
||||
};
|
||||
}
|
||||
|
||||
private static int indexTypeBytes(int indexType) {
|
||||
return switch (indexType) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> 1;
|
||||
case GL11.GL_UNSIGNED_SHORT -> 2;
|
||||
case GL11.GL_UNSIGNED_INT -> 4;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean bindExt(ImmediateExtendedAttribHandler h, int mode, int first, int count, int extPrim,
|
||||
VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal) {
|
||||
final int extVbo = getOrBuild(h, pos, uv, normal, first, count, extPrim, mode);
|
||||
if (extVbo == 0) return false;
|
||||
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void unbind() {
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
|
||||
setNeutralCurrentValues();
|
||||
}
|
||||
|
||||
public static void setNeutralCurrentValues() {
|
||||
GLStateManager.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
|
||||
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
dropDerivedFrom(vboId);
|
||||
respecCounts.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
streamingSources.remove(vboId);
|
||||
persistentSources.remove(vboId);
|
||||
}
|
||||
|
||||
public static void onBufferRespecified(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
if (streamingSources.contains(vboId)) {
|
||||
respecFrames.put(vboId, frame);
|
||||
return;
|
||||
}
|
||||
if (!dropDerivedFrom(vboId)) return;
|
||||
final boolean sameRun = respecCounts.containsKey(vboId) && frame - respecFrames.get(vboId) <= RESPEC_DECAY_FRAMES;
|
||||
if (!sameRun) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
final int respecs = respecCounts.get(vboId) + 1;
|
||||
final int span = frame - respecRunStart.get(vboId);
|
||||
if (respecs >= MAX_RESPECS && span >= MIN_STREAM_SPAN_FRAMES && span <= respecs) {
|
||||
markStreaming(vboId);
|
||||
return;
|
||||
}
|
||||
if (span > respecs) {
|
||||
startRespecRun(vboId);
|
||||
return;
|
||||
}
|
||||
respecCounts.put(vboId, respecs);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
private static void startRespecRun(int vboId) {
|
||||
respecCounts.put(vboId, 1);
|
||||
respecRunStart.put(vboId, frame);
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
public static void markStreaming(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
respecCounts.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
respecFrames.put(vboId, frame);
|
||||
if (streamingSources.add(vboId)) dropDerivedFrom(vboId);
|
||||
}
|
||||
|
||||
public static void markPersistent(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
markStreaming(vboId);
|
||||
persistentSources.add(vboId);
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
invalidateAll();
|
||||
respecCounts.clear();
|
||||
respecFrames.clear();
|
||||
respecRunStart.clear();
|
||||
streamingSources.clear();
|
||||
persistentSources.clear();
|
||||
}
|
||||
|
||||
private static boolean dropDerivedFrom(int srcId) {
|
||||
final ObjectSet<EboKey> ebos = eboKeysBySource.remove(srcId);
|
||||
if (ebos != null) {
|
||||
for (EboKey k : ebos) eboPatternCache.remove(k);
|
||||
}
|
||||
final ObjectSet<Key> keys = keysBySource.get(srcId);
|
||||
if (keys == null || keys.isEmpty()) return ebos != null;
|
||||
IntArrayList toFree = null;
|
||||
for (Key k : keys.toArray(new Key[0])) {
|
||||
final ExtBuffer buf = cache.remove(k);
|
||||
if (buf != null && buf.vbo != 0) {
|
||||
if (toFree == null) toFree = new IntArrayList();
|
||||
toFree.add(buf.vbo);
|
||||
}
|
||||
unindexKey(k);
|
||||
}
|
||||
freeExtVbos(toFree);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void indexKey(Key k) {
|
||||
addToBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) addToBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) addToBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void unindexKey(Key k) {
|
||||
removeFromBucket(k.posVBO, k);
|
||||
if (k.uvVBO != k.posVBO) removeFromBucket(k.uvVBO, k);
|
||||
if (k.normalVBO != k.posVBO && k.normalVBO != k.uvVBO) removeFromBucket(k.normalVBO, k);
|
||||
}
|
||||
|
||||
private static void addToBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set == null) keysBySource.put(src, set = new ObjectOpenHashSet<>());
|
||||
set.add(k);
|
||||
}
|
||||
|
||||
private static void removeFromBucket(int src, Key k) {
|
||||
if (src == 0) return;
|
||||
final ObjectSet<Key> set = keysBySource.get(src);
|
||||
if (set != null && set.remove(k) && set.isEmpty()) keysBySource.remove(src);
|
||||
}
|
||||
|
||||
private static int getOrBuild(ImmediateExtendedAttribHandler h, VAOManager.Attrib pos, VAOManager.Attrib uv,
|
||||
VAOManager.Attrib normal, int first, int count, int extPrim, int mode) {
|
||||
if (first + count > MAX_EXT_VERTS) return 0;
|
||||
lookup.set(pos, uv, normal, mode, first % extPrim);
|
||||
ExtBuffer buf = cache.get(lookup);
|
||||
if (buf == null) {
|
||||
if (cache.size() >= MAX_CACHE) invalidateExtBuffers();
|
||||
buf = new ExtBuffer();
|
||||
final Key stored = lookup.copy();
|
||||
cache.put(stored, buf);
|
||||
indexKey(stored);
|
||||
}
|
||||
|
||||
final long range = (long) first << 32 | (count & 0xFFFFFFFFL);
|
||||
if (buf.vbo != 0 && buf.builtRanges.contains(range)) return buf.vbo;
|
||||
if (!ensureCapacity(buf, first + count)) return 0;
|
||||
buildRange(h, buf.vbo, pos, uv, normal, first, count, extPrim);
|
||||
buf.builtRanges.add(range);
|
||||
return buf.vbo;
|
||||
}
|
||||
|
||||
private static boolean ensureCapacity(ExtBuffer buf, int neededVerts) {
|
||||
if (buf.vbo != 0 && buf.capacityVerts >= neededVerts) return true;
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int newCap = Math.min(MAX_EXT_VERTS, Math.max(neededVerts, buf.capacityVerts * 2));
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int vbo = GLStateManager.glGenBuffers();
|
||||
if (vbo == 0) return false;
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, (long) newCap * extStride, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
if (buf.vbo != 0) {
|
||||
final int savedRead = GLStateManager.getBoundCopyReadBuffer();
|
||||
final int savedWrite = GLStateManager.getBoundCopyWriteBuffer();
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, buf.vbo);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, vbo);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0L, 0L,
|
||||
(long) buf.capacityVerts * extStride);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, savedRead);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, savedWrite);
|
||||
GLStateManager.glDeleteBuffers(buf.vbo);
|
||||
}
|
||||
buf.vbo = vbo;
|
||||
buf.capacityVerts = newCap;
|
||||
return true;
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildRange(ImmediateExtendedAttribHandler h, int extVbo, VAOManager.Attrib pos,
|
||||
VAOManager.Attrib uv, VAOManager.Attrib normal, int first, int count, int extPrim) {
|
||||
final int posStride = pos.effectiveStride();
|
||||
final int uvStride = uv.effectiveStride();
|
||||
final boolean smooth = normal != null;
|
||||
final int normalStride = smooth ? normal.effectiveStride() : 0;
|
||||
final int srcStride = smooth ? SRC_STRIDE_SMOOTH : SRC_STRIDE_FLAT;
|
||||
final int srcNormalOffset = smooth ? SRC_NORMAL_OFFSET : -1;
|
||||
|
||||
final ByteBuffer posBuf = readRange(pos, first, count, posStride);
|
||||
final ByteBuffer uvBuf = readRange(uv, first, count, uvStride);
|
||||
final ByteBuffer normalBuf = smooth ? readRange(normal, first, count, normalStride) : null;
|
||||
|
||||
final ByteBuffer src = memAlloc(count * srcStride).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int pb = i * posStride, ub = i * uvStride, d = i * srcStride;
|
||||
src.putFloat(d, pos.readComponent(posBuf, pb, 0));
|
||||
src.putFloat(d + 4, pos.readComponent(posBuf, pb, 1));
|
||||
src.putFloat(d + 8, pos.size >= 3 ? pos.readComponent(posBuf, pb, 2) : 0f);
|
||||
src.putFloat(d + SRC_TEX_OFFSET, uv.readComponent(uvBuf, ub, 0));
|
||||
src.putFloat(d + SRC_TEX_OFFSET + 4, uv.readComponent(uvBuf, ub, 1));
|
||||
if (smooth) {
|
||||
final int nb = i * normalStride;
|
||||
src.putInt(d + SRC_NORMAL_OFFSET, NormI8.pack(
|
||||
normal.readComponent(normalBuf, nb, 0),
|
||||
normal.readComponent(normalBuf, nb, 1),
|
||||
normal.readComponent(normalBuf, nb, 2)));
|
||||
}
|
||||
}
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final ByteBuffer ext = memCalloc(count, extStride);
|
||||
h.buildPacked(memAddress0(src), srcStride, 0, SRC_TEX_OFFSET, srcNormalOffset, count, extPrim, memAddress0(ext), extStride);
|
||||
|
||||
beginInternalGl();
|
||||
try {
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(count * extStride);
|
||||
GLStateManager.glBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * extStride, ext);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
|
||||
memFree(posBuf);
|
||||
memFree(uvBuf);
|
||||
if (normalBuf != null) memFree(normalBuf);
|
||||
memFree(src);
|
||||
memFree(ext);
|
||||
}
|
||||
|
||||
private static ByteBuffer readRange(VAOManager.Attrib a, int first, int count, int stride) {
|
||||
final int elementBytes = a.size * a.typeSizeBytes();
|
||||
final int bytes = (count - 1) * stride + elementBytes;
|
||||
final ByteBuffer dst = memAlloc(bytes).order(ByteOrder.nativeOrder());
|
||||
dst.position(0).limit(bytes);
|
||||
final int savedVBO = GLStateManager.getBoundVBO();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, a.offset + (long) first * stride, dst);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
dst.position(0).limit(bytes);
|
||||
return dst;
|
||||
}
|
||||
|
||||
private static void invalidateAll() {
|
||||
eboPatternCache.clear();
|
||||
eboKeysBySource.clear();
|
||||
invalidateExtBuffers();
|
||||
}
|
||||
|
||||
private static void invalidateExtBuffers() {
|
||||
if (cache.isEmpty()) return;
|
||||
final IntArrayList toFree = new IntArrayList(cache.size());
|
||||
for (ExtBuffer buf : cache.values()) {
|
||||
if (buf.vbo != 0) toFree.add(buf.vbo);
|
||||
}
|
||||
cache.clear();
|
||||
keysBySource.clear();
|
||||
freeExtVbos(toFree);
|
||||
}
|
||||
|
||||
private static void freeExtVbos(IntArrayList vbos) {
|
||||
if (vbos == null || vbos.isEmpty()) return;
|
||||
beginInternalGl();
|
||||
try {
|
||||
for (int i = 0; i < vbos.size(); i++) {
|
||||
GLStateManager.glDeleteBuffers(vbos.getInt(i));
|
||||
}
|
||||
} finally {
|
||||
endInternalGl();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ExtBuffer {
|
||||
int vbo;
|
||||
int capacityVerts;
|
||||
final LongOpenHashSet builtRanges = new LongOpenHashSet();
|
||||
}
|
||||
|
||||
private static final class EboKey {
|
||||
int ebo, count, type;
|
||||
long offset;
|
||||
|
||||
void set(int ebo, long offset, int count, int type) {
|
||||
this.ebo = ebo;
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
EboKey copy() {
|
||||
final EboKey k = new EboKey();
|
||||
k.set(ebo, offset, count, type);
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof EboKey k)) return false;
|
||||
return ebo == k.ebo && count == k.count && type == k.type && offset == k.offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = ebo;
|
||||
h = h * 31 + count;
|
||||
h = h * 31 + type;
|
||||
h = h * 31 + Long.hashCode(offset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Key {
|
||||
int posVBO, uvVBO, normalVBO, posStride, uvStride, normalStride, posFmt, uvFmt, normalFmt, mode, phase;
|
||||
long posOffset, uvOffset, normalOffset;
|
||||
|
||||
void set(VAOManager.Attrib pos, VAOManager.Attrib uv, VAOManager.Attrib normal, int mode, int phase) {
|
||||
posVBO = pos.vboId; posOffset = pos.offset; posStride = pos.effectiveStride(); posFmt = fmt(pos);
|
||||
uvVBO = uv.vboId; uvOffset = uv.offset; uvStride = uv.effectiveStride(); uvFmt = fmt(uv);
|
||||
if (normal != null) {
|
||||
normalVBO = normal.vboId; normalOffset = normal.offset;
|
||||
normalStride = normal.effectiveStride(); normalFmt = fmt(normal);
|
||||
} else {
|
||||
normalVBO = 0; normalOffset = 0; normalStride = 0; normalFmt = 0;
|
||||
}
|
||||
this.mode = mode;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
private static int fmt(VAOManager.Attrib a) {
|
||||
return (a.size & 0x7F) | (a.normalized ? 0x80 : 0) | (a.type << 8);
|
||||
}
|
||||
|
||||
Key copy() {
|
||||
final Key k = new Key();
|
||||
k.posVBO = posVBO; k.posOffset = posOffset; k.posStride = posStride; k.posFmt = posFmt;
|
||||
k.uvVBO = uvVBO; k.uvOffset = uvOffset; k.uvStride = uvStride; k.uvFmt = uvFmt;
|
||||
k.normalVBO = normalVBO; k.normalOffset = normalOffset; k.normalStride = normalStride; k.normalFmt = normalFmt;
|
||||
k.mode = mode; k.phase = phase;
|
||||
return k;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Key k)) return false;
|
||||
return posVBO == k.posVBO && uvVBO == k.uvVBO && normalVBO == k.normalVBO
|
||||
&& posStride == k.posStride && uvStride == k.uvStride && normalStride == k.normalStride
|
||||
&& posFmt == k.posFmt && uvFmt == k.uvFmt && normalFmt == k.normalFmt
|
||||
&& mode == k.mode && phase == k.phase
|
||||
&& posOffset == k.posOffset && uvOffset == k.uvOffset && normalOffset == k.normalOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int h = posVBO;
|
||||
h = h * 31 + uvVBO;
|
||||
h = h * 31 + normalVBO;
|
||||
h = h * 31 + posStride;
|
||||
h = h * 31 + uvStride;
|
||||
h = h * 31 + normalStride;
|
||||
h = h * 31 + posFmt;
|
||||
h = h * 31 + uvFmt;
|
||||
h = h * 31 + normalFmt;
|
||||
h = h * 31 + mode;
|
||||
h = h * 31 + phase;
|
||||
h = h * 31 + Long.hashCode(posOffset);
|
||||
h = h * 31 + Long.hashCode(uvOffset);
|
||||
h = h * 31 + Long.hashCode(normalOffset);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (12) + unit 0 (47 bits at offset 12)
|
||||
* long[0]: global bits (13) + unit 0 (47 bits at offset 13)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
@@ -63,7 +63,7 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 12;
|
||||
private static final int GLOBAL_BITS = 13;
|
||||
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
|
||||
@@ -71,6 +71,7 @@ public final class FragmentKey {
|
||||
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
private static final int BIT_LINE_STIPPLE = 11; // 1 bit
|
||||
private static final int BIT_COLOR_SUM = 12; // 1 bit
|
||||
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
@@ -130,10 +131,12 @@ public final class FragmentKey {
|
||||
global |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
// Separate specular
|
||||
// Separate specular / color sum
|
||||
if (GLStateManager.getLightingState().isEnabled()
|
||||
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
global |= (1L << BIT_SEPARATE_SPECULAR);
|
||||
} else if (GLStateManager.getColorSumState().isEnabled()) {
|
||||
global |= (1L << BIT_COLOR_SUM);
|
||||
}
|
||||
|
||||
// Per-unit state
|
||||
@@ -238,6 +241,7 @@ public final class FragmentKey {
|
||||
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; }
|
||||
public boolean colorSum() { return ((packed[0] >> BIT_COLOR_SUM) & 1) != 0; }
|
||||
public boolean lineStipple() { return ((packed[0] >> BIT_LINE_STIPPLE) & 1) != 0; }
|
||||
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
|
||||
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
|
||||
@@ -374,10 +378,10 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b overlay=%b units=%d",
|
||||
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(), overlayEnabled(), nrEnabledUnits()));
|
||||
separateSpecular(), colorSum(), overlayEnabled(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
@@ -276,6 +276,10 @@ public final class FragmentShaderGenerator {
|
||||
sb.append(" // Add separate specular\n");
|
||||
sb.append(" color.rgb += v_SpecularColor;\n");
|
||||
}
|
||||
if (key.colorSum()) {
|
||||
sb.append(" // Color sum - GL_COLOR_SUM with the current secondary color\n");
|
||||
sb.append(" color.rgb += u_SecondaryColor;\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitAlphaTest(StringBuilder sb, FragmentKey key) {
|
||||
|
||||
@@ -2,12 +2,12 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
public final class InstancedAttribs {
|
||||
|
||||
public static final int LOC_MATRIX_COL0 = 7;
|
||||
public static final int LOC_MATRIX_COL1 = 8;
|
||||
public static final int LOC_MATRIX_COL2 = 9;
|
||||
public static final int LOC_MATRIX_COL3 = 10;
|
||||
public static final int LOC_COLOR = 11;
|
||||
public static final int LOC_LIGHTMAP = 12;
|
||||
public static final int LOC_MATRIX_COL0 = 5;
|
||||
public static final int LOC_MATRIX_COL1 = 6;
|
||||
public static final int LOC_MATRIX_COL2 = 7;
|
||||
public static final int LOC_MATRIX_COL3 = 8;
|
||||
public static final int LOC_COLOR = 9;
|
||||
public static final int LOC_LIGHTMAP = 10;
|
||||
|
||||
public static final int OFFSET_MATRIX = 0;
|
||||
public static final int OFFSET_COLOR = 64;
|
||||
|
||||
@@ -179,6 +179,7 @@ public final class ShaderManager {
|
||||
sm.lastFramePreDrawCalls = sm.preDrawCalls;
|
||||
sm.preDrawCalls = 0;
|
||||
sm.uniforms.endFrame();
|
||||
FfpExtendedAttribs.endFrame();
|
||||
}
|
||||
|
||||
public static void setCurrentNormal(float x, float y, float z) {
|
||||
|
||||
@@ -27,7 +27,6 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Maintains the shared {@link FFPUniformBlock} image from GLSM cached state and streams it to the UBO ring.
|
||||
@@ -209,7 +208,7 @@ public class Uniforms {
|
||||
ring = new UniformRingBuffer(RING_CAPACITY, FFPUniformBlock.SIZE);
|
||||
}
|
||||
final int offset = ring.writeBlock(staging);
|
||||
RENDER_BACKEND.bindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
|
||||
GLStateManager.glBindBufferRange(GL31.GL_UNIFORM_BUFFER, FFPUniformBlock.BINDING_POINT,
|
||||
ring.getBufferId(), offset, FFPUniformBlock.SIZE);
|
||||
bound = true;
|
||||
blockWrites++;
|
||||
@@ -331,6 +330,9 @@ public class Uniforms {
|
||||
GLStateManager.getOverlayR(), GLStateManager.getOverlayG(),
|
||||
GLStateManager.getOverlayB(), GLStateManager.getOverlayA());
|
||||
|
||||
final var secondary = GLStateManager.getSecondaryColor();
|
||||
putVec3(FFPUniformBlock.SECONDARY_COLOR, secondary.getRed(), secondary.getGreen(), secondary.getBlue());
|
||||
|
||||
// Mesa STATE_FOG_PARAMS_OPTIMIZED
|
||||
final FogState fog = GLStateManager.getFogState();
|
||||
final float start = fog.getStart();
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredBlendHandler {
|
||||
boolean isBlendLocked();
|
||||
boolean isOverrideHeld();
|
||||
void deferBlendModeToggle(boolean enabled);
|
||||
void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha);
|
||||
void flushDeferredBlend();
|
||||
|
||||
+1
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredDepthColorHandler {
|
||||
boolean isDepthColorLocked();
|
||||
boolean isOverrideHeld();
|
||||
void deferDepthEnable(boolean enabled);
|
||||
void deferColorMask(boolean r, boolean g, boolean b, boolean a);
|
||||
}
|
||||
@@ -6,8 +6,17 @@ public final class GLSMConfig {
|
||||
public static float lastBrightnessX;
|
||||
public static float lastBrightnessY;
|
||||
|
||||
public static int packBrightness(float x, float y) {
|
||||
return ((int) y << 16) | ((int) x & 0xFFFF);
|
||||
}
|
||||
|
||||
public static int packedLastBrightness() {
|
||||
return packBrightness(lastBrightnessX, lastBrightnessY);
|
||||
}
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
|
||||
public static boolean extendedAttribsExpected;
|
||||
public static volatile boolean expandVertexFormats;
|
||||
|
||||
private GLSMConfig() {}
|
||||
|
||||
@@ -9,6 +9,7 @@ public final class GLSMHooks {
|
||||
public static DeferredAlphaHandler alphaHandler;
|
||||
public static DeferredDepthColorHandler depthColorHandler;
|
||||
public static GlintColorHandler glintColorHandler;
|
||||
public static ImmediateExtendedAttribHandler immediateExtendedHandler;
|
||||
public static ShaderWorkSubmitter shaderWorkSubmitter;
|
||||
public static ShaderTransformPostProcessor postTransformProcessor;
|
||||
public static PerFrameUniformBlock perFrameUniformBlock;
|
||||
@@ -25,6 +26,8 @@ public final class GLSMHooks {
|
||||
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
|
||||
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
|
||||
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
|
||||
public static final EventBus<ForeignDrawEndEvent> FOREIGN_DRAW_END = EventBus.create(ForeignDrawEndEvent.class);
|
||||
public static final EventBus<VanillaBlendChangeEvent> VANILLA_BLEND_CHANGE = EventBus.create(VanillaBlendChangeEvent.class);
|
||||
public static final EventBus<LoadingCheckpointEvent> LOADING_CHECKPOINT = EventBus.create(LoadingCheckpointEvent.class);
|
||||
|
||||
// Reusable event instances
|
||||
@@ -38,6 +41,8 @@ public final class GLSMHooks {
|
||||
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
|
||||
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
|
||||
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
|
||||
public static final ForeignDrawEndEvent foreignDrawEndEvent = new ForeignDrawEndEvent();
|
||||
public static final VanillaBlendChangeEvent vanillaBlendChangeEvent = new VanillaBlendChangeEvent();
|
||||
public static final LoadingCheckpointEvent loadingCheckpointEvent = new LoadingCheckpointEvent();
|
||||
|
||||
private GLSMHooks() {}
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public interface ImmediateExtendedAttribHandler {
|
||||
|
||||
int EXT_STRIDE = 12;
|
||||
int LOC_MC_ENTITY = 11;
|
||||
int LOC_MID_TEX = 12;
|
||||
int LOC_TANGENT = 13;
|
||||
int LOC_MIDBLOCK = 14;
|
||||
|
||||
int RAW_NORMAL_INDEX = 6;
|
||||
|
||||
boolean wantsExtended();
|
||||
|
||||
boolean wantsTangent();
|
||||
|
||||
boolean wantsExtendedCapture();
|
||||
|
||||
void build(int[] rawBuffer, int vertexCount, int vertsPerPrim, int normalIntIndex, long dstAddr, int dstStride);
|
||||
|
||||
void buildPacked(long srcBase, int stride, int posOffset, int texOffset, int normalOffset,
|
||||
int vertexCount, int vertsPerPrim, long dstAddr, int dstStride);
|
||||
|
||||
static int extPrimVerts(int drawMode) {
|
||||
if (drawMode == GL11.GL_QUADS) return 4;
|
||||
if (drawMode == GL11.GL_TRIANGLES) return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extPrimVerts(int drawMode, int vertexCount) {
|
||||
final int p = extPrimVerts(drawMode);
|
||||
return (p != 0 && vertexCount > 0 && vertexCount % p == 0) ? p : 0;
|
||||
}
|
||||
|
||||
static int texOffset(VertexFormat format) {
|
||||
int offset = 0;
|
||||
for (VertexFormatElement element : format.elementsArray) {
|
||||
if (element.getUsage() == VertexFormatElement.Usage.PRIMARY_UV) return offset;
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int normalOffset(VertexFormat format) {
|
||||
int offset = 0;
|
||||
for (VertexFormatElement element : format.elementsArray) {
|
||||
if (element.getUsage() == VertexFormatElement.Usage.NORMAL) return offset;
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setupExtAttribPointers(long baseOffset, int stride) {
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_MID_TEX);
|
||||
GLStateManager.glVertexAttribPointer(LOC_MID_TEX, 2, GL11.GL_FLOAT, false, stride, baseOffset);
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_TANGENT);
|
||||
GLStateManager.glVertexAttribPointer(LOC_TANGENT, 4, GL11.GL_BYTE, true, stride, baseOffset + 8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface VanillaBooleanLayer {
|
||||
|
||||
boolean isOverrideHeld();
|
||||
boolean getVanilla();
|
||||
void setVanilla(boolean enabled);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface VanillaStateLayer<T> {
|
||||
|
||||
boolean isOverrideHeld();
|
||||
void readVanilla(T into);
|
||||
void writeVanilla(T from);
|
||||
|
||||
static boolean isHeld(VanillaStateLayer<?> layer) {
|
||||
return layer != null && layer.isOverrideHeld();
|
||||
}
|
||||
|
||||
static <T> void capture(VanillaStateLayer<T> layer, T slot) {
|
||||
if (isHeld(layer)) layer.readVanilla(slot);
|
||||
}
|
||||
|
||||
static <T> boolean restore(VanillaStateLayer<T> layer, T saved) {
|
||||
if (!isHeld(layer)) return false;
|
||||
layer.writeVanilla(saved);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ForeignDrawEndEvent extends MutableEvent {
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class VanillaBlendChangeEvent extends MutableEvent {
|
||||
}
|
||||
+7
@@ -274,6 +274,13 @@ public final class CommandBufferExecutor {
|
||||
ptr += 16;
|
||||
GLStateManager.glColor4f(r, g, b, a);
|
||||
}
|
||||
case GLCommand.SECONDARY_COLOR -> {
|
||||
final float r = memGetFloat(ptr);
|
||||
final float g = memGetFloat(ptr + 4);
|
||||
final float b = memGetFloat(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glSecondaryColor3f(r, g, b);
|
||||
}
|
||||
case GLCommand.CLEAR_COLOR -> {
|
||||
final float r = memGetFloat(ptr);
|
||||
final float g = memGetFloat(ptr + 4);
|
||||
|
||||
@@ -356,6 +356,14 @@ public final class CommandRecorder {
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void writeSecondaryColor(float r, float g, float b) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.SECONDARY_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
}
|
||||
|
||||
public void writeClearColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.CLEAR_COLOR);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
|
||||
/**
|
||||
@@ -9,15 +13,26 @@ import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
public final class DisplayListVBO {
|
||||
|
||||
private final SubVBO[] vbos;
|
||||
private final int[] extVbos;
|
||||
|
||||
DisplayListVBO(SubVBO[] vbos) {
|
||||
this(vbos, null);
|
||||
}
|
||||
|
||||
DisplayListVBO(SubVBO[] vbos, int[] extVbos) {
|
||||
this.vbos = vbos;
|
||||
this.extVbos = extVbos;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
for (SubVBO vbo : vbos) {
|
||||
vbo.delete();
|
||||
}
|
||||
if (extVbos != null) {
|
||||
for (int extVbo : extVbos) {
|
||||
if (extVbo != 0) GLStateManager.glDeleteBuffers(extVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SubVBO[] getVBOs() {
|
||||
@@ -39,6 +54,9 @@ public final class DisplayListVBO {
|
||||
private final int start;
|
||||
private final int count;
|
||||
private final int vertexFlags;
|
||||
private int pendingExtVbo = 0;
|
||||
|
||||
void setPendingExtVbo(int extVbo) { this.pendingExtVbo = extVbo; }
|
||||
|
||||
public SubVBO(IVertexArrayObject vao, int drawMode, int start, int count, int vertexFlags) {
|
||||
this.vao = vao;
|
||||
@@ -67,9 +85,20 @@ public final class DisplayListVBO {
|
||||
public void render() {
|
||||
if (vao == null) return;
|
||||
vao.bind();
|
||||
if (pendingExtVbo != 0) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, pendingExtVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
pendingExtVbo = 0;
|
||||
}
|
||||
if (count <= 0) { vao.unbind(); return; }
|
||||
VAOManager.setCurrentVertexFlags(vertexFlags);
|
||||
vao.draw(drawMode, start, count);
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
vao.draw(drawMode, start, count);
|
||||
} finally {
|
||||
FfpExtendedAttribs.endInternalDraw();
|
||||
}
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
|
||||
+84
-1
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.BaseVAO;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexedVAO;
|
||||
@@ -8,7 +9,13 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vbo.IVertexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
@@ -16,6 +23,7 @@ import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.nmemFree;
|
||||
@@ -48,15 +56,29 @@ public final class DisplayListVBOBuilder {
|
||||
}
|
||||
|
||||
public DisplayListVBO build() {
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
final DisplayListVBO.SubVBO[] vbos = new DisplayListVBO.SubVBO[count];
|
||||
final ArrayList<ByteBuffer> allBuffers = new ArrayList<>();
|
||||
final IntArrayList extVbos = new IntArrayList();
|
||||
for (int i = 0; i < VertexFlags.BITSET_SIZE; i++) {
|
||||
List<FormatData> formatData = formats[i];
|
||||
if (formatData == null) continue;
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[i];
|
||||
final boolean wantExt = extHandler != null && format.hasTexture();
|
||||
if (extHandler == null && GLSMConfig.extendedAttribsExpected && format.hasTexture()) {
|
||||
GLStateManager.warnOnce("dl-ext-no-handler",
|
||||
"Textured display list compiled before the immediate ext-attribute handler was installed! POM data will be missing for this geometry.");
|
||||
}
|
||||
int start = 0;
|
||||
final IVertexArrayObject vao = VAOManager.createStorageVAO(format, -1, 0); // drawMode will be ignored
|
||||
final IVertexBuffer vbo = vao.getVBO();
|
||||
|
||||
// SubVBO slots that need the ext attributes attached on their first draw.
|
||||
final IntArrayList extDrawIndexes = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extStarts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extCounts = wantExt ? new IntArrayList() : null;
|
||||
final IntArrayList extPrims = wantExt ? new IntArrayList() : null;
|
||||
|
||||
for (FormatData data : formatData) {
|
||||
int vertexCount;
|
||||
final List<ByteBuffer> drawBuffers = data.buffers;
|
||||
@@ -76,6 +98,23 @@ public final class DisplayListVBOBuilder {
|
||||
if (data.drawMode == GL11.GL_QUADS) {
|
||||
final IVertexArrayObject indexedVAO = new IndexedVAO(vbo, IndexBuffer.convertQuadsToTrigs(start, start + vertexCount));
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6, flags);
|
||||
if (wantExt) {
|
||||
extDrawIndexes.add(data.drawIndex);
|
||||
extStarts.add(start);
|
||||
extCounts.add(vertexCount);
|
||||
extPrims.add(4);
|
||||
}
|
||||
} else if (data.drawMode == GL11.GL_TRIANGLES) {
|
||||
if (wantExt) {
|
||||
final IVertexArrayObject triVao = new BaseVAO(vbo);
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(triVao, GL11.GL_TRIANGLES, start, vertexCount, flags);
|
||||
extDrawIndexes.add(data.drawIndex);
|
||||
extStarts.add(start);
|
||||
extCounts.add(vertexCount);
|
||||
extPrims.add(3);
|
||||
} else {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLES, start, vertexCount, flags);
|
||||
}
|
||||
} else if (data.drawMode == GL11.GL_QUAD_STRIP) {
|
||||
// GL_QUAD_STRIP is removed in core profile; an even-length GL_TRIANGLE_STRIP produces the same quads
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_STRIP, start, vertexCount & ~1, flags);
|
||||
@@ -91,11 +130,55 @@ public final class DisplayListVBOBuilder {
|
||||
}
|
||||
ByteBuffer bigBuffer = mergeAndDelete(allBuffers);
|
||||
vbo.allocate(bigBuffer, start);
|
||||
|
||||
if (wantExt && !extDrawIndexes.isEmpty()) {
|
||||
final int extVbo = buildExt(extHandler, format, bigBuffer, extStarts, extCounts, extPrims);
|
||||
if (extVbo != 0) {
|
||||
extVbos.add(extVbo);
|
||||
for (int e = 0; e < extDrawIndexes.size(); e++) {
|
||||
vbos[extDrawIndexes.getInt(e)].setPendingExtVbo(extVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memFree(bigBuffer);
|
||||
|
||||
allBuffers.clear();
|
||||
}
|
||||
return new DisplayListVBO(vbos);
|
||||
return new DisplayListVBO(vbos, extVbos.toIntArray());
|
||||
}
|
||||
|
||||
private static int buildExt(ImmediateExtendedAttribHandler handler, VertexFormat format, ByteBuffer bigBuffer,
|
||||
IntArrayList extStarts, IntArrayList extCounts, IntArrayList extPrims) {
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int stride = format.getVertexSize();
|
||||
final int posOffset = 0;
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
|
||||
int extVerts = 0;
|
||||
for (int q = 0; q < extStarts.size(); q++) {
|
||||
extVerts = Math.max(extVerts, extStarts.getInt(q) + extCounts.getInt(q));
|
||||
}
|
||||
|
||||
final ByteBuffer ext = memCalloc(extVerts, extStride);
|
||||
final long extAddr = memAddress0(ext);
|
||||
final long srcBase = memAddress0(bigBuffer);
|
||||
for (int q = 0; q < extStarts.size(); q++) {
|
||||
final int rStart = extStarts.getInt(q);
|
||||
final int rCount = extCounts.getInt(q);
|
||||
final int rPrim = extPrims.getInt(q);
|
||||
handler.buildPacked(srcBase + (long) rStart * stride, stride, posOffset, texOffset, normalOffset, rCount, rPrim,
|
||||
extAddr + (long) rStart * extStride, extStride);
|
||||
}
|
||||
|
||||
final int extVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(extVerts * extStride);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, ext, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
memFree(ext);
|
||||
return extVbo;
|
||||
}
|
||||
|
||||
private static ByteBuffer mergeAndDelete(List<ByteBuffer> buffers) {
|
||||
|
||||
@@ -61,6 +61,7 @@ public final class GLCommand {
|
||||
public static final int COLOR = 54; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int CLEAR_COLOR = 55; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int BLEND_COLOR = 56; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int SECONDARY_COLOR = 57; // [cmd:4][r:4f][g:4f][b:4f]
|
||||
|
||||
// === Mixed int+float commands ===
|
||||
public static final int ALPHA_FUNC = 60; // [cmd:4][func:4][ref:4f]
|
||||
@@ -155,6 +156,7 @@ public final class GLCommand {
|
||||
case COLOR -> "COLOR";
|
||||
case CLEAR_COLOR -> "CLEAR_COLOR";
|
||||
case BLEND_COLOR -> "BLEND_COLOR";
|
||||
case SECONDARY_COLOR -> "SECONDARY_COLOR";
|
||||
case ALPHA_FUNC -> "ALPHA_FUNC";
|
||||
case FOGF -> "FOGF";
|
||||
case LIGHTF -> "LIGHTF";
|
||||
@@ -211,7 +213,7 @@ public final class GLCommand {
|
||||
// Three int commands (16 bytes)
|
||||
case GLCommand.STENCIL_FUNC, GLCommand.STENCIL_OP, GLCommand.TEX_PARAMETERI,
|
||||
GLCommand.LIGHTF, GLCommand.LIGHTI,
|
||||
GLCommand.MATERIALF, GLCommand.TEX_PARAMETERF -> 16;
|
||||
GLCommand.MATERIALF, GLCommand.TEX_PARAMETERF, GLCommand.SECONDARY_COLOR -> 16;
|
||||
|
||||
// Four int commands (20 bytes)
|
||||
case GLCommand.VIEWPORT, GLCommand.BLEND_FUNC, GLCommand.COLOR_MASK,
|
||||
|
||||
@@ -142,6 +142,7 @@ public class GLSMRedirector {
|
||||
private static final String GLU = "org/lwjgl/util/glu/GLU";
|
||||
private static final String OpenGlHelper = "net/minecraft/client/renderer/OpenGlHelper";
|
||||
private static final String EXTBlendFunc = "org/lwjgl/opengl/EXTBlendFuncSeparate";
|
||||
private static final String EXTSecondaryColor = "org/lwjgl/opengl/EXTSecondaryColor";
|
||||
private static final String ARBMultiTexture = "org/lwjgl/opengl/ARBMultitexture";
|
||||
private static final String ARBShaderObjects = "org/lwjgl/opengl/ARBShaderObjects";
|
||||
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
|
||||
@@ -403,7 +404,12 @@ public class GLSMRedirector {
|
||||
.add("glBlendEquation")
|
||||
.add("glMultiDrawArrays")
|
||||
.add("glPointParameterf")
|
||||
.add("glPointParameteri");
|
||||
.add("glPointParameteri")
|
||||
// Removed in core profile - emulated by the FFP shaders, calling through would abort the JVM
|
||||
.add("glSecondaryColor3f")
|
||||
.add("glSecondaryColor3d")
|
||||
.add("glSecondaryColor3b")
|
||||
.add("glSecondaryColor3ub");
|
||||
final var gl15 = RedirectMap.newMap()
|
||||
.add("glGenBuffers")
|
||||
.add("glBindBuffer")
|
||||
@@ -511,7 +517,8 @@ public class GLSMRedirector {
|
||||
.add("glFramebufferRenderbuffer")
|
||||
.add("glIsFramebuffer")
|
||||
.add("glIsRenderbuffer")
|
||||
.add("glBindBufferBase");
|
||||
.add("glBindBufferBase")
|
||||
.add("glBindBufferRange");
|
||||
final var gl31 = RedirectMap.newMap()
|
||||
.add("glDrawElementsInstanced")
|
||||
.add("glDrawArraysInstanced")
|
||||
@@ -589,6 +596,11 @@ public class GLSMRedirector {
|
||||
.add("isFramebufferEnabled")
|
||||
);
|
||||
methodRedirects.put(EXTBlendFunc, RedirectMap.newMap().add("glBlendFuncSeparateEXT", "tryBlendFuncSeparate"));
|
||||
methodRedirects.put(EXTSecondaryColor, RedirectMap.newMap()
|
||||
.add("glSecondaryColor3bEXT", "glSecondaryColor3b")
|
||||
.add("glSecondaryColor3fEXT", "glSecondaryColor3f")
|
||||
.add("glSecondaryColor3dEXT", "glSecondaryColor3d")
|
||||
.add("glSecondaryColor3ubEXT", "glSecondaryColor3ub"));
|
||||
|
||||
// ARB
|
||||
methodRedirects.put(ARBMultiTexture, RedirectMap.newMap()
|
||||
@@ -711,6 +723,7 @@ public class GLSMRedirector {
|
||||
);
|
||||
final var uniformBufferObject = RedirectMap.newMap()
|
||||
.add("glBindBufferBase")
|
||||
.add("glBindBufferRange")
|
||||
.add("glGetUniformBlockIndex")
|
||||
.add("glUniformBlockBinding");
|
||||
methodRedirects.put(ARBUniformBufferObject, uniformBufferObject);
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import lombok.Setter;
|
||||
|
||||
public class AlphaStateStack extends AlphaState implements IStateStack<AlphaState> {
|
||||
|
||||
@@ -10,6 +12,8 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
|
||||
|
||||
protected int pointer;
|
||||
|
||||
@Setter private VanillaStateLayer<AlphaState> vanillaLayer;
|
||||
|
||||
public AlphaStateStack() {
|
||||
stack = new AlphaState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
@@ -22,7 +26,7 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -31,10 +35,19 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
final AlphaState saved = stack[--pointer];
|
||||
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlphaState readEffective(AlphaState out) {
|
||||
out.set(this);
|
||||
VanillaStateLayer.capture(vanillaLayer, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import lombok.Setter;
|
||||
|
||||
public class BlendStateStack extends BlendState implements IStateStack<BlendStateStack> {
|
||||
|
||||
@@ -10,6 +12,8 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
|
||||
|
||||
protected int pointer;
|
||||
|
||||
@Setter private VanillaStateLayer<BlendState> vanillaLayer;
|
||||
|
||||
public BlendStateStack() {
|
||||
stack = new BlendState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
@@ -22,7 +26,7 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -31,10 +35,21 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
final BlendState saved = stack[--pointer];
|
||||
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
setExceptFunc(saved);
|
||||
} else {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlendState readEffective(BlendState out) {
|
||||
out.set(this);
|
||||
VanillaStateLayer.capture(vanillaLayer, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BooleanState;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* A stack for boolean GL state with lazy copy-on-write optimization.
|
||||
@@ -14,6 +16,7 @@ import com.gtnewhorizons.angelica.glsm.states.BooleanState;
|
||||
public class BooleanStateStack extends BooleanState implements IStateStack<BooleanStateStack> {
|
||||
|
||||
protected final boolean[] stack;
|
||||
@Setter private VanillaBooleanLayer vanillaLayer;
|
||||
|
||||
/**
|
||||
* The depth at which state has been saved. Compared against GLStateManager.getAttribDepth()
|
||||
@@ -56,7 +59,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
|
||||
if (savedDepth >= stack.length) {
|
||||
throw new IllegalStateException("Stack overflow size " + (savedDepth + 1) + " reached");
|
||||
}
|
||||
stack[savedDepth++] = enabled;
|
||||
stack[savedDepth++] = vanillaValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -65,12 +68,26 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
|
||||
if (savedDepth == 0) {
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
final boolean oldValue = stack[--savedDepth];
|
||||
// Call setEnabledDirect to avoid triggering beforeModify during restore
|
||||
setEnabledDirect(oldValue);
|
||||
restore(stack[--savedDepth]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isEffectivelyEnabled() {
|
||||
return (vanillaLayer != null && vanillaLayer.isOverrideHeld()) ? vanillaLayer.getVanilla() : enabled;
|
||||
}
|
||||
|
||||
private boolean vanillaValue() {
|
||||
return isEffectivelyEnabled();
|
||||
}
|
||||
|
||||
private void restore(boolean value) {
|
||||
if (vanillaLayer != null && vanillaLayer.isOverrideHeld()) {
|
||||
vanillaLayer.setVanilla(value);
|
||||
} else {
|
||||
setEnabledDirect(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return savedDepth == 0;
|
||||
@@ -92,8 +109,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
|
||||
public BooleanStateStack popDepth() {
|
||||
// We're only called if we were modified, so savedDepth should match
|
||||
if (savedDepth > 0) {
|
||||
final boolean oldValue = stack[--savedDepth];
|
||||
setEnabledDirect(oldValue);
|
||||
restore(stack[--savedDepth]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -106,7 +122,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
|
||||
public void beforeModify() {
|
||||
final int globalDepth = GLStateManager.getAttribDepth();
|
||||
if (savedDepth < globalDepth) {
|
||||
stack[savedDepth++] = enabled;
|
||||
stack[savedDepth++] = vanillaValue();
|
||||
GLStateManager.registerModifiedState(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskStack> {
|
||||
|
||||
@@ -10,6 +12,8 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
|
||||
|
||||
protected int pointer;
|
||||
|
||||
@Setter private VanillaStateLayer<ColorMask> vanillaLayer;
|
||||
|
||||
public ColorMaskStack() {
|
||||
stack = new ColorMask[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
@@ -22,7 +26,7 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -31,7 +35,10 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
final ColorMask saved = stack[--pointer];
|
||||
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.DepthState;
|
||||
import lombok.Setter;
|
||||
|
||||
public class DepthStateStack extends DepthState implements IStateStack<DepthStateStack> {
|
||||
|
||||
@@ -10,6 +12,10 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
|
||||
|
||||
protected int pointer;
|
||||
|
||||
@Setter private VanillaStateLayer<DepthState> vanillaLayer;
|
||||
|
||||
private final DepthState effective = new DepthState();
|
||||
|
||||
public DepthStateStack() {
|
||||
stack = new DepthState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
@@ -22,7 +28,7 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
|
||||
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
|
||||
}
|
||||
|
||||
stack[pointer++].set(this);
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -31,10 +37,21 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
|
||||
throw new IllegalStateException("Stack underflow");
|
||||
}
|
||||
|
||||
set(stack[--pointer]);
|
||||
final DepthState saved = stack[--pointer];
|
||||
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
|
||||
setExceptMask(saved);
|
||||
} else {
|
||||
set(saved);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isEffectiveMaskEnabled() {
|
||||
if (!VanillaStateLayer.isHeld(vanillaLayer)) return isEnabled();
|
||||
vanillaLayer.readVanilla(effective);
|
||||
return effective.isEnabled();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pointer == 0;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,15 @@ public class BlendState implements ISettableState<BlendState> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setExceptFunc(BlendState state) {
|
||||
final int sRgb = srcRgb, dRgb = dstRgb, sAlpha = srcAlpha, dAlpha = dstAlpha;
|
||||
set(state);
|
||||
this.srcRgb = sRgb;
|
||||
this.dstRgb = dRgb;
|
||||
this.srcAlpha = sAlpha;
|
||||
this.dstAlpha = dAlpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sameAs(Object state) {
|
||||
if (this == state) return true;
|
||||
|
||||
@@ -18,6 +18,12 @@ public class DepthState implements ISettableState<DepthState> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setExceptMask(DepthState state) {
|
||||
final boolean mask = enabled;
|
||||
set(state);
|
||||
this.enabled = mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sameAs(Object state) {
|
||||
if (this == state) return true;
|
||||
|
||||
+151
-5
@@ -4,12 +4,16 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -22,6 +26,7 @@ import java.nio.ByteBuffer;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static org.joml.Math.clamp;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCopy;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
/**
|
||||
@@ -36,6 +41,7 @@ public class TessellatorStreamingDrawer {
|
||||
private static final Tracy.ZoneId Z_SDL_STREAM_DRAW = Tracy.zoneId("sdlStreamDraw", Tracy.COLOR_FFP);
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("TessellatorStreamingDrawer");
|
||||
|
||||
private static final int FORMAT_COUNT = VertexFlags.BITSET_SIZE; // 16
|
||||
|
||||
private static PersistentStreamingBuffer persistentBuffer;
|
||||
@@ -44,6 +50,12 @@ public class TessellatorStreamingDrawer {
|
||||
private static final int[] persistentVAOs = new int[FORMAT_COUNT];
|
||||
private static final int[] orphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
private static final int[] extendedPersistentVAOs = new int[FORMAT_COUNT];
|
||||
private static final int[] extendedOrphanVAOs = new int[FORMAT_COUNT];
|
||||
private static ByteBuffer extScratch;
|
||||
private static long extScratchAddress;
|
||||
private static int extScratchCapacity;
|
||||
|
||||
private static ByteBuffer repackBuffer;
|
||||
private static long repackAddress;
|
||||
private static int repackCapacity;
|
||||
@@ -108,7 +120,7 @@ public class TessellatorStreamingDrawer {
|
||||
|
||||
final int defaultBrightness;
|
||||
if (!tess.hasBrightness) {
|
||||
defaultBrightness = ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
|
||||
defaultBrightness = GLSMConfig.packedLastBrightness();
|
||||
} else {
|
||||
defaultBrightness = 0;
|
||||
}
|
||||
@@ -153,7 +165,25 @@ public class TessellatorStreamingDrawer {
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int)(writePtr - repackAddress));
|
||||
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
final int extPrim = extHandler == null ? 0 : ImmediateExtendedAttribHandler.extPrimVerts(tess.drawMode, vertexCount);
|
||||
final boolean wantsExt = extHandler != null && extHandler.wantsExtended();
|
||||
final boolean doExt = extPrim != 0 && tess.hasTexture && wantsExt;
|
||||
if (doExt) {
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(repackAddress, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
// format expansion backfills a normal into rawBuffer, so trust effectiveFlags rather than tess.hasNormals
|
||||
final int normalIndex = (effectiveFlags & VertexFlags.NORMAL_BIT) != 0 ? ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX : -1;
|
||||
extHandler.build(tess.rawBuffer, vertexCount, extPrim, normalIndex, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, effectiveFlags, format, combinedStride, tess.drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} else {
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
@@ -184,6 +214,7 @@ public class TessellatorStreamingDrawer {
|
||||
final ByteBuffer buffer = dt.getWriteBuffer();
|
||||
final int vertexSize = format.getVertexSize();
|
||||
|
||||
if (tryExtendedPacked(buffer, flags, format, vertexSize, drawMode, vertexCount)) return;
|
||||
uploadAndDraw(buffer, flags, format, vertexSize, drawMode, vertexCount);
|
||||
}
|
||||
|
||||
@@ -197,9 +228,37 @@ public class TessellatorStreamingDrawer {
|
||||
public static void drawPacked(ByteBuffer packedData, int drawMode, int flags, int vertexCount) {
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
|
||||
final int vertexSize = format.getVertexSize();
|
||||
if (tryExtendedPacked(packedData, flags, format, vertexSize, drawMode, vertexCount)) return;
|
||||
uploadAndDraw(packedData, flags, format, vertexSize, drawMode, vertexCount);
|
||||
}
|
||||
|
||||
private static boolean tryExtendedPacked(ByteBuffer packed, int flags, VertexFormat format, int vertexSize, int drawMode, int vertexCount) {
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
if (extHandler == null || !extHandler.wantsExtended()) return false;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(drawMode, vertexCount);
|
||||
if (extPrim == 0 || !format.hasTexture()) return false;
|
||||
|
||||
final long srcBase = memAddress0(packed) + packed.position();
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(srcBase, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
extHandler.buildPacked(srcBase, vertexSize, 0, texOffset, normalOffset, vertexCount, extPrim, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, flags, format, combinedStride, drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void interleaveBase(long src, long dst, int vertexCount, int vertexSize, int combinedStride) {
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
memCopy(src + (long) i * vertexSize, dst + (long) i * combinedStride, vertexSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static String cachedDebugInfo = "Stream: not initialized";
|
||||
private static long lastDebugUpdateNanos;
|
||||
private static final long DEBUG_UPDATE_INTERVAL_NS = 500_000_000L; // 500ms
|
||||
@@ -294,10 +353,89 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
|
||||
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
|
||||
if (drawMode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(firstVertex, vertexCount);
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
if (drawMode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(firstVertex, vertexCount);
|
||||
} else {
|
||||
GLStateManager.glDrawArrays(drawMode, firstVertex, vertexCount);
|
||||
}
|
||||
} finally {
|
||||
FfpExtendedAttribs.endInternalDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int combinedStride, int drawMode, int vertexCount) {
|
||||
ensureVAO(flags, format);
|
||||
ensureExtendedVAOs(flags, format);
|
||||
|
||||
int firstVertex = -1;
|
||||
|
||||
if (persistentBuffer != null) {
|
||||
firstVertex = persistentBuffer.upload(combined, combinedStride);
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
GLStateManager.glBindVertexArray(extendedPersistentVAOs[flags]);
|
||||
} else {
|
||||
GLStateManager.glDrawArrays(drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(combined);
|
||||
firstVertex = 0;
|
||||
}
|
||||
|
||||
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static void ensureExtendedVAOs(int flags, VertexFormat format) {
|
||||
if (extendedOrphanVAOs[flags] == 0) {
|
||||
extendedOrphanVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, orphanBuffers[flags].getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
if (persistentBuffer != null && extendedPersistentVAOs[flags] == 0) {
|
||||
extendedPersistentVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedPersistentVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, persistentBuffer.getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupExtendedBufferState(VertexFormat format) {
|
||||
final int combinedStride = format.getVertexSize() + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
long offset = 0;
|
||||
for (final VertexFormatElement element : format.elementsArray) {
|
||||
element.setupBufferState(offset, combinedStride);
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(offset, combinedStride);
|
||||
}
|
||||
|
||||
private static void ensureExtScratch(int requiredBytes) {
|
||||
if (extScratch != null && requiredBytes <= extScratchCapacity) return;
|
||||
|
||||
int newCapacity = Math.max(0x4000, extScratchCapacity);
|
||||
while (newCapacity < requiredBytes) {
|
||||
newCapacity *= 2;
|
||||
}
|
||||
if (extScratch != null) memFree(extScratch);
|
||||
extScratch = memAlloc(newCapacity);
|
||||
extScratchAddress = memAddress0(extScratch);
|
||||
extScratchCapacity = newCapacity;
|
||||
}
|
||||
|
||||
private static void shrinkExtScratchIfOversized(int lastRequiredBytes) {
|
||||
if (extScratchCapacity > 0x20000 && lastRequiredBytes < (extScratchCapacity >> 3)) {
|
||||
memFree(extScratch);
|
||||
extScratchCapacity = 0x4000;
|
||||
extScratch = memAlloc(extScratchCapacity);
|
||||
extScratchAddress = memAddress0(extScratch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,8 +496,16 @@ public class TessellatorStreamingDrawer {
|
||||
for (int i = 0; i < FORMAT_COUNT; i++) {
|
||||
if (persistentVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(persistentVAOs[i]); persistentVAOs[i] = 0; }
|
||||
if (orphanVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(orphanVAOs[i]); orphanVAOs[i] = 0; }
|
||||
if (extendedPersistentVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(extendedPersistentVAOs[i]); extendedPersistentVAOs[i] = 0; }
|
||||
if (extendedOrphanVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(extendedOrphanVAOs[i]); extendedOrphanVAOs[i] = 0; }
|
||||
if (orphanBuffers[i] != null) { orphanBuffers[i].destroy(); orphanBuffers[i] = null; }
|
||||
}
|
||||
if (extScratch != null) {
|
||||
memFree(extScratch);
|
||||
extScratch = null;
|
||||
extScratchAddress = 0;
|
||||
extScratchCapacity = 0;
|
||||
}
|
||||
if (persistentBuffer != null) {
|
||||
persistentBuffer.destroy();
|
||||
persistentBuffer = null;
|
||||
|
||||
@@ -4,8 +4,11 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -22,6 +25,7 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
cap("GL_POLYGON_STIPPLE", GL11.GL_POLYGON_STIPPLE),
|
||||
cap("GL_INDEX_LOGIC_OP", GL11.GL_INDEX_LOGIC_OP),
|
||||
cap("GL_AUTO_NORMAL", GL11.GL_AUTO_NORMAL),
|
||||
cap("GL_COLOR_SUM", GL14.GL_COLOR_SUM),
|
||||
cap("GL_MAP1_COLOR_4", GL11.GL_MAP1_COLOR_4),
|
||||
cap("GL_MAP1_INDEX", GL11.GL_MAP1_INDEX),
|
||||
cap("GL_MAP1_NORMAL", GL11.GL_MAP1_NORMAL),
|
||||
@@ -46,6 +50,14 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
return Arguments.of(name, value);
|
||||
}
|
||||
|
||||
private static void assertSecondaryColor(float red, float green, float blue) {
|
||||
final FloatBuffer params = BufferUtils.createFloatBuffer(4);
|
||||
GLStateManager.glGetFloat(GL14.GL_CURRENT_SECONDARY_COLOR, params);
|
||||
assertEquals(red, params.get(0), "red must round-trip through the cache");
|
||||
assertEquals(green, params.get(1), "green must round-trip through the cache");
|
||||
assertEquals(blue, params.get(2), "blue must round-trip through the cache");
|
||||
}
|
||||
|
||||
private static void assertNoGlError(String what) {
|
||||
final int error = GL11.glGetError();
|
||||
assertEquals(GL11.GL_NO_ERROR, error, () -> what + " raised 0x" + Integer.toHexString(error));
|
||||
@@ -83,6 +95,71 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorIsCacheOnly() {
|
||||
try {
|
||||
// Calling through to the driver here aborts the JVM in a core profile - the color sum is emulated by the FFP shaders
|
||||
GLStateManager.glSecondaryColor3f(0.25F, 0.5F, 0.75F);
|
||||
assertNoGlError("glSecondaryColor3f");
|
||||
|
||||
assertSecondaryColor(0.25F, 0.5F, 0.75F);
|
||||
} finally {
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorIsRestoredByPopAttrib() {
|
||||
try {
|
||||
GLStateManager.glSecondaryColor3f(0.125F, 0.25F, 0.375F);
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glSecondaryColor3f(1.0F, 1.0F, 1.0F);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertNoGlError("glPushAttrib/glSecondaryColor3f/glPopAttrib");
|
||||
|
||||
assertSecondaryColor(0.125F, 0.25F, 0.375F);
|
||||
} finally {
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorSumIsRestoredByFogBitPopAttrib() {
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_FOG_BIT);
|
||||
GLStateManager.glEnable(GL14.GL_COLOR_SUM);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertNoGlError("glPushAttrib(GL_FOG_BIT)/glEnable(GL_COLOR_SUM)/glPopAttrib");
|
||||
assertFalse(GLStateManager.glIsEnabled(GL14.GL_COLOR_SUM));
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL14.GL_COLOR_SUM);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorReplaysFromDisplayList() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glSecondaryColor3f(0.5F, 0.625F, 0.75F);
|
||||
GLStateManager.glEndList();
|
||||
assertNoGlError("glSecondaryColor3f display list compile");
|
||||
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
GLStateManager.glCallList(list);
|
||||
assertNoGlError("glSecondaryColor3f display list replay");
|
||||
|
||||
assertSecondaryColor(0.5F, 0.625F, 0.75F);
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void displayListReplayIsCacheOnly() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDLHints;
|
||||
import org.lwjgl.sdl.SDLInit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -67,8 +68,13 @@ public final class SdlTestRig {
|
||||
|
||||
private static final int SHADER_FORMATS = SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_DXBC | SDL_GPU_SHADERFORMAT_DXIL;
|
||||
|
||||
public static void initSdlVideo() {
|
||||
SDLHints.SDL_SetHint(SDLHints.SDL_HINT_MAC_BACKGROUND_APP, "1");
|
||||
assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||
}
|
||||
|
||||
public static SdlTestRig acquireRealDevice() throws ReflectiveOperationException {
|
||||
assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||
initSdlVideo();
|
||||
realSdlDevice = SDL_CreateGPUDevice(SHADER_FORMATS, true, (CharSequence) null);
|
||||
assumeTrue(realSdlDevice != 0, "No SDL GPU device: " + SDLError.SDL_GetError());
|
||||
|
||||
|
||||
+2
-4
@@ -1,16 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.device;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDLInit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
import static org.lwjgl.sdl.SDLInit.SDL_INIT_VIDEO;
|
||||
|
||||
class DeviceLifecycleGpuTest {
|
||||
|
||||
@@ -18,7 +16,7 @@ class DeviceLifecycleGpuTest {
|
||||
|
||||
@BeforeAll
|
||||
static void initSdl() {
|
||||
assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||
SdlTestRig.initSdlVideo();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.frame;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class AcquireAffinityTest {
|
||||
|
||||
private static final Thread WINDOW = new Thread("window");
|
||||
private static final Thread OTHER = new Thread("other");
|
||||
|
||||
@Test
|
||||
void acquiringOnTheWindowThreadIsTheOnlyAcceptedCase() {
|
||||
assertFalse(FrameManager.acquireAffinityViolated(WINDOW, WINDOW, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void acquiringOnAnyOtherThreadViolatesTheSdlContract() {
|
||||
assertTrue(FrameManager.acquireAffinityViolated(OTHER, WINDOW, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void aLaterAcquireFromASecondThreadViolatesEvenWhenTheFirstWasCorrect() {
|
||||
assertTrue(FrameManager.acquireAffinityViolated(WINDOW, WINDOW, 1),
|
||||
"drift after a correct first acquire must still be caught");
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUnknownWindowThreadCannotBeJudged() {
|
||||
assertFalse(FrameManager.acquireAffinityViolated(OTHER, null, 3));
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.splash;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class SplashDispatcherTest {
|
||||
|
||||
private static final long INTERVAL = 16_000_000L;
|
||||
|
||||
@Test
|
||||
void theIntervalGateSwallowsFramesArrivingTooSoon() {
|
||||
assertFalse(SplashDispatcher.intervalElapsed(INTERVAL - 1, 0L, false));
|
||||
assertTrue(SplashDispatcher.intervalElapsed(INTERVAL, 0L, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void theFinalFrameIsNotSwallowedByTheIntervalGate() {
|
||||
assertTrue(SplashDispatcher.intervalElapsed(1L, 0L, true), "teardown must present the last splash frame whenever it arrives");
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@ import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FFPVertexLighting;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
|
||||
@@ -17,9 +19,15 @@ import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import lombok.Setter;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gl.program.Program;
|
||||
import net.coderbot.iris.gl.program.ProgramBuilder;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.PipelineManager;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
@@ -41,6 +49,8 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack.stackPush;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutShort;
|
||||
@@ -87,6 +97,7 @@ public class BatchingFontRenderer {
|
||||
private static class FontAAShader {
|
||||
|
||||
private static Program fontShader = null;
|
||||
|
||||
public static Program getProgram() {
|
||||
if (fontShader == null) {
|
||||
final String vsh = ShaderLoader.getShaderSource("angelica:fontFilter.vsh");
|
||||
@@ -172,6 +183,9 @@ public class BatchingFontRenderer {
|
||||
private int blendSrcRGB = GL11.GL_SRC_ALPHA;
|
||||
private int blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
|
||||
private final BlendState blendStateBefore = new BlendState();
|
||||
private static final BlendState deferredBlendStateBefore = new BlendState();
|
||||
|
||||
private float lightmapU = 0.0f;
|
||||
private float lightmapV = 0.0f;
|
||||
private boolean lightmapActive = false;
|
||||
@@ -190,10 +204,19 @@ public class BatchingFontRenderer {
|
||||
return (argb & 0xFF000000) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* True only for a perspective projection (world-space text: signs, nameplates, etc).
|
||||
* GUI/HUD text uses an orthographic projection and must never be shaded by world lighting -
|
||||
* mods rely on getting back the exact color they requested (e.g. GTNHLib's ColorResource).
|
||||
*/
|
||||
private static boolean isWorldSpaceText() {
|
||||
return GLStateManager.getProjectionMatrix().m33() == 0.0f;
|
||||
}
|
||||
|
||||
private void captureLightmapState() {
|
||||
final int unitBinding = GLStateManager.getTextures().getTextureUnitBindings(LIGHTMAP_TEX_UNIT).getBinding();
|
||||
|
||||
final boolean active = GLStateManager.getProjectionMatrix().m33() == 0.0f && unitBinding != 0;
|
||||
final boolean active = isWorldSpaceText() && unitBinding != 0;
|
||||
|
||||
float u = 0.0f;
|
||||
float v = 0.0f;
|
||||
@@ -217,6 +240,7 @@ public class BatchingFontRenderer {
|
||||
|
||||
private static final class TextSegment {
|
||||
final Matrix4f mvp = new Matrix4f();
|
||||
final Matrix4f modelView = new Matrix4f();
|
||||
BatchingFontRenderer owner;
|
||||
int cmdStart;
|
||||
int cmdEnd;
|
||||
@@ -224,6 +248,9 @@ public class BatchingFontRenderer {
|
||||
float lightmapV;
|
||||
boolean lightmapActive;
|
||||
int lightmapTexture;
|
||||
int packedLight;
|
||||
float normalX, normalY, normalZ;
|
||||
int blockEntityId;
|
||||
}
|
||||
|
||||
// 16-bit EBO index range
|
||||
@@ -235,7 +262,6 @@ public class BatchingFontRenderer {
|
||||
private int deferredCmdWatermark;
|
||||
private static final ObjectArrayList<TextSegment> deferredSegments = ObjectArrayList.wrap(new TextSegment[16], 0);
|
||||
private static final ObjectArrayList<TextSegment> deferredSegmentPool = ObjectArrayList.wrap(new TextSegment[16], 0);
|
||||
private boolean deferCurrentBatch;
|
||||
|
||||
private final Matrix4f batchProj = new Matrix4f();
|
||||
private final Matrix4f batchModelView = new Matrix4f();
|
||||
@@ -467,6 +493,10 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveModelView(Matrix4f dest) {
|
||||
dest.set(batchMatrixValid ? batchModelView : GLStateManager.getModelViewMatrix());
|
||||
}
|
||||
|
||||
private void sealBatchSegment() {
|
||||
final int end = batchCommands.size();
|
||||
if (end == batchSealedEnd) return;
|
||||
@@ -475,10 +505,17 @@ public class BatchingFontRenderer {
|
||||
segment.cmdStart = batchSealedEnd;
|
||||
segment.cmdEnd = end;
|
||||
resolveMvp(segment.mvp);
|
||||
resolveModelView(segment.modelView);
|
||||
segment.lightmapActive = lightmapActive;
|
||||
segment.lightmapU = lightmapU;
|
||||
segment.lightmapV = lightmapV;
|
||||
segment.lightmapTexture = lightmapTextureId;
|
||||
segment.packedLight = GLSMConfig.packedLastBrightness();
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
segment.normalX = normal.x;
|
||||
segment.normalY = normal.y;
|
||||
segment.normalZ = normal.z;
|
||||
segment.blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
batchSegments.add(segment);
|
||||
batchSealedEnd = end;
|
||||
}
|
||||
@@ -499,27 +536,29 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
batchDepth--;
|
||||
if (batchDepth == 0) {
|
||||
// We finished any nested batches
|
||||
flushBatch();
|
||||
// We finished any nested batches.
|
||||
if (shouldDeferNow()) {
|
||||
deferBatch();
|
||||
} else {
|
||||
flushBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldDeferNow() {
|
||||
if (!GLStateManager.isMainThread()) return false;
|
||||
return TesrBatchRenderer.INSTANCE.hasPendingGeometry() || ModelPartBatcher.INSTANCE.isActive();
|
||||
}
|
||||
|
||||
public void beginDeferredBatch() {
|
||||
deferCurrentBatch = batchDepth == 0 && TesrBatchRenderer.INSTANCE.hasActivePass() || ModelPartBatcher.INSTANCE.isActive();
|
||||
if (deferCurrentBatch && deferredIdxPos / 6 > QUAD_BUDGET / 2) {
|
||||
if (batchDepth == 0 && shouldDeferNow() && deferredIdxPos / 6 > QUAD_BUDGET / 2) {
|
||||
flushDeferredText();
|
||||
}
|
||||
beginBatch();
|
||||
}
|
||||
|
||||
public void endDeferredBatch() {
|
||||
if (deferCurrentBatch && batchDepth == 1) {
|
||||
deferCurrentBatch = false;
|
||||
batchDepth = 0;
|
||||
deferBatch();
|
||||
} else {
|
||||
endBatch();
|
||||
}
|
||||
endBatch();
|
||||
}
|
||||
|
||||
private void deferBatch() {
|
||||
@@ -544,38 +583,123 @@ public class BatchingFontRenderer {
|
||||
public static void flushDeferredText() {
|
||||
if (deferredSegments.isEmpty()) return;
|
||||
|
||||
if (deferredSegments.getFirst().owner.shouldDrawThroughPipeline()) {
|
||||
flushDeferredThroughPipeline();
|
||||
return;
|
||||
}
|
||||
|
||||
GLStateManager.beginForeignDraw();
|
||||
try {
|
||||
flushDeferredTextInner();
|
||||
} finally {
|
||||
GLStateManager.endForeignDraw();
|
||||
}
|
||||
}
|
||||
|
||||
private static void flushDeferredThroughPipeline() {
|
||||
final BatchingFontRenderer first = deferredSegments.getFirst().owner;
|
||||
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
|
||||
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
|
||||
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
|
||||
GLStateManager.getEffectiveBlendState(deferredBlendStateBefore);
|
||||
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
|
||||
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
boolean textureChanged = false;
|
||||
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(first.blendSrcRGB, first.blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glDepthMask(false);
|
||||
|
||||
final Boolean prevTranslucency = GbufferPrograms.beginTranslucencyDeclaration(Boolean.TRUE);
|
||||
GbufferPrograms.setBlockEntityDefaults();
|
||||
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
try {
|
||||
for (final TextSegment segment : deferredSegments) {
|
||||
final BatchingFontRenderer owner = segment.owner;
|
||||
if (segment.cmdStart == segment.cmdEnd) continue;
|
||||
|
||||
GLStateManager.setModelViewMatrix(segment.modelView);
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
|
||||
|
||||
final FontDrawCmd[] cmdsData = owner.batchCommands.elements();
|
||||
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
|
||||
|
||||
textureChanged |= emitRangeThroughPipeline(owner, cmdsData, segment.cmdStart, segment.cmdEnd,
|
||||
segment.packedLight, segment.normalX, segment.normalY, segment.normalZ);
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glPopMatrix();
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
|
||||
GbufferPrograms.endTranslucencyDeclaration(prevTranslucency);
|
||||
|
||||
GLStateManager.glDepthMask(depthMaskBefore);
|
||||
if (isTextureEnabledBefore) {
|
||||
GLStateManager.enableTexture();
|
||||
} else {
|
||||
GLStateManager.disableTexture();
|
||||
}
|
||||
if (isBlendEnabledBefore) {
|
||||
GLStateManager.enableBlend();
|
||||
} else {
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
GLStateManager.tryBlendFuncSeparate(deferredBlendStateBefore.getSrcRgb(), deferredBlendStateBefore.getDstRgb(),
|
||||
deferredBlendStateBefore.getSrcAlpha(), deferredBlendStateBefore.getDstAlpha());
|
||||
if (isAlphaTestEnabledBefore) {
|
||||
GLStateManager.enableAlphaTest();
|
||||
} else {
|
||||
GLStateManager.disableAlphaTest();
|
||||
}
|
||||
if (textureChanged) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
|
||||
}
|
||||
discardDeferredText();
|
||||
}
|
||||
}
|
||||
|
||||
private static void flushDeferredTextInner() {
|
||||
uploadVertexData(deferredVertexPos);
|
||||
|
||||
final int prevProgram = GLStateManager.glGetInteger(GL20.GL_CURRENT_PROGRAM);
|
||||
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
|
||||
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
|
||||
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
|
||||
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
|
||||
GLStateManager.getEffectiveBlendState(deferredBlendStateBefore);
|
||||
final int shadeModelBefore = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
|
||||
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
final int lightmapBindingBefore = boundLightmapTexture();
|
||||
|
||||
final boolean depthTestBefore = GLStateManager.getDepthTest().isEnabled();
|
||||
final boolean depthMaskBefore = GLStateManager.getDepthState().isEnabled();
|
||||
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
|
||||
|
||||
deferredSegments.get(0).owner.setupFontDrawState();
|
||||
GLStateManager.glDepthMask(false);
|
||||
flushLastTexture = DUMMY_RESOURCE_LOCATION;
|
||||
flushTextureChanged = false;
|
||||
resetFlushLightmap();
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final FloatBuffer mvpBuf = stack.mallocFloat(16);
|
||||
for (int i = 0, n = deferredSegments.size(); i < n; i++) {
|
||||
final TextSegment segment = deferredSegments.get(i);
|
||||
mvpBuf.clear();
|
||||
segment.mvp.get(mvpBuf);
|
||||
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, mvpBuf);
|
||||
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
|
||||
segment.lightmapV, segment.lightmapTexture);
|
||||
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
|
||||
try {
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final FloatBuffer mvpBuf = stack.mallocFloat(16);
|
||||
for (final TextSegment segment : deferredSegments) {
|
||||
mvpBuf.clear();
|
||||
segment.mvp.get(mvpBuf);
|
||||
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, mvpBuf);
|
||||
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
|
||||
segment.lightmapV, segment.lightmapTexture);
|
||||
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore,
|
||||
lightmapBindingBefore, isAlphaTestEnabledBefore, deferredBlendStateBefore, shadeModelBefore);
|
||||
restoreDepth(depthTestBefore, depthMaskBefore);
|
||||
discardDeferredText();
|
||||
}
|
||||
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore, lightmapBindingBefore);
|
||||
restoreDepth(depthTestBefore, depthMaskBefore);
|
||||
|
||||
discardDeferredText();
|
||||
}
|
||||
|
||||
public static void discardDeferredText() {
|
||||
@@ -614,18 +738,47 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
|
||||
private static final Matrix4f scratchMvp = new Matrix4f();
|
||||
private static final Matrix4f scratchModelView = new Matrix4f();
|
||||
private int fontAAModeLast = -1;
|
||||
private int fontAAStrengthLast = -1;
|
||||
|
||||
private void flushBatch() {
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
flushBatchInner();
|
||||
if (shouldDrawThroughPipeline()) {
|
||||
flushBatchThroughPipeline();
|
||||
} else {
|
||||
GLStateManager.beginForeignDraw();
|
||||
try {
|
||||
flushBatchInner();
|
||||
} finally {
|
||||
GLStateManager.endForeignDraw();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldDrawThroughPipeline() {
|
||||
if (FontConfig.fontAAMode != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GLStateManager.isMainThread()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PipelineManager pipelines = Iris.getPipelineManagerNullable();
|
||||
if (pipelines == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !Tessellator.instance.isDrawing
|
||||
&& pipelines.getPipelineNullable() instanceof DeferredWorldRenderingPipeline drp
|
||||
&& drp.shouldOverrideShaders();
|
||||
}
|
||||
|
||||
private void flushBatchInner() {
|
||||
final int cmdCount = batchCommands.size();
|
||||
final int segmentCount = batchSegments.size();
|
||||
@@ -646,11 +799,14 @@ public class BatchingFontRenderer {
|
||||
Arrays.sort(cmds, batchSealedEnd, cmdCount, FontDrawCmd.DRAW_ORDER_COMPARATOR);
|
||||
|
||||
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
|
||||
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
|
||||
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
|
||||
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
|
||||
GLStateManager.getEffectiveBlendState(blendStateBefore);
|
||||
final int shadeModelBefore = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
|
||||
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
final int lightmapBindingBefore = boundLightmapTexture();
|
||||
final boolean depthTestBefore = GLStateManager.getDepthTest().isEnabled();
|
||||
final boolean depthMaskBefore = GLStateManager.getDepthState().isEnabled();
|
||||
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
|
||||
|
||||
setupFontDrawState();
|
||||
flushLastTexture = DUMMY_RESOURCE_LOCATION;
|
||||
@@ -675,7 +831,8 @@ public class BatchingFontRenderer {
|
||||
drawCommands(cmds, batchSealedEnd, cmdCount, this);
|
||||
}
|
||||
}
|
||||
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore, lightmapBindingBefore);
|
||||
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore,
|
||||
lightmapBindingBefore, isAlphaTestEnabledBefore, blendStateBefore, shadeModelBefore);
|
||||
restoreDepth(depthTestBefore, depthMaskBefore);
|
||||
|
||||
truncateBatchToWatermark();
|
||||
@@ -783,7 +940,9 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private static void restoreFontDrawState(int prevProgram, boolean textureEnabledBefore, boolean blendEnabledBefore, int boundTextureBefore, int lightmapBindingBefore) {
|
||||
private static void restoreFontDrawState(int prevProgram, boolean textureEnabledBefore, boolean blendEnabledBefore,
|
||||
int boundTextureBefore, int lightmapBindingBefore, boolean alphaTestEnabledBefore, BlendState blendBefore,
|
||||
int shadeModelBefore) {
|
||||
GLStateManager.glUseProgram(prevProgram);
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
@@ -792,9 +951,19 @@ public class BatchingFontRenderer {
|
||||
if (textureEnabledBefore) {
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
if (!blendEnabledBefore) {
|
||||
if (blendEnabledBefore) {
|
||||
GLStateManager.enableBlend();
|
||||
} else {
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
GLStateManager.tryBlendFuncSeparate(blendBefore.getSrcRgb(), blendBefore.getDstRgb(),
|
||||
blendBefore.getSrcAlpha(), blendBefore.getDstAlpha());
|
||||
if (alphaTestEnabledBefore) {
|
||||
GLStateManager.enableAlphaTest();
|
||||
} else {
|
||||
GLStateManager.disableAlphaTest();
|
||||
}
|
||||
GLStateManager.glShadeModel(shadeModelBefore);
|
||||
if (flushTextureChanged) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
|
||||
}
|
||||
@@ -803,11 +972,156 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private void flushBatchThroughPipeline() {
|
||||
final int cmdCount = batchCommands.size();
|
||||
final int segmentCount = batchSegments.size();
|
||||
if (segmentCount == 0 && (vertexDataPos == deferredVertexPos || cmdCount == deferredCmdWatermark)) {
|
||||
truncateBatchToWatermark();
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
|
||||
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
|
||||
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
|
||||
GLStateManager.getEffectiveBlendState(blendStateBefore);
|
||||
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
boolean textureChanged = false;
|
||||
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(blendSrcRGB, blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
final Boolean prevTranslucency = GbufferPrograms.beginTranslucencyDeclaration(Boolean.TRUE);
|
||||
GbufferPrograms.setBlockEntityDefaults();
|
||||
|
||||
final FontDrawCmd[] cmdsData = batchCommands.elements();
|
||||
for (int i = 0; i < segmentCount; i++) {
|
||||
final TextSegment segment = batchSegments.get(i);
|
||||
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
|
||||
}
|
||||
Arrays.sort(cmdsData, batchSealedEnd, cmdCount, FontDrawCmd.DRAW_ORDER_COMPARATOR);
|
||||
|
||||
// The unsealed tail still belongs to the modelview live at flush time; capture it before the segments move it.
|
||||
resolveModelView(scratchModelView);
|
||||
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
try {
|
||||
for (int i = 0; i < segmentCount; i++) {
|
||||
final TextSegment segment = batchSegments.get(i);
|
||||
if (segment.cmdStart == segment.cmdEnd) continue;
|
||||
GLStateManager.setModelViewMatrix(segment.modelView);
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
|
||||
textureChanged |= emitRangeThroughPipeline(this, cmdsData, segment.cmdStart, segment.cmdEnd,
|
||||
segment.packedLight, segment.normalX, segment.normalY, segment.normalZ);
|
||||
}
|
||||
if (batchSealedEnd < cmdCount) {
|
||||
GLStateManager.setModelViewMatrix(scratchModelView);
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
textureChanged |= emitRangeThroughPipeline(this, cmdsData, batchSealedEnd, cmdCount,
|
||||
GLSMConfig.packedLastBrightness(), normal.x, normal.y, normal.z);
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glPopMatrix();
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
|
||||
GbufferPrograms.endTranslucencyDeclaration(prevTranslucency);
|
||||
|
||||
if (isTextureEnabledBefore) {
|
||||
GLStateManager.enableTexture();
|
||||
} else {
|
||||
GLStateManager.disableTexture();
|
||||
}
|
||||
if (isBlendEnabledBefore) {
|
||||
GLStateManager.enableBlend();
|
||||
} else {
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
GLStateManager.tryBlendFuncSeparate(blendStateBefore.getSrcRgb(), blendStateBefore.getDstRgb(),
|
||||
blendStateBefore.getSrcAlpha(), blendStateBefore.getDstAlpha());
|
||||
if (isAlphaTestEnabledBefore) {
|
||||
GLStateManager.enableAlphaTest();
|
||||
} else {
|
||||
GLStateManager.disableAlphaTest();
|
||||
}
|
||||
if (textureChanged) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
|
||||
}
|
||||
|
||||
truncateBatchToWatermark();
|
||||
}
|
||||
}
|
||||
|
||||
/** Replays one sealed command range through the Tessellator so Iris sees it as ordinary geometry. */
|
||||
private static boolean emitRangeThroughPipeline(BatchingFontRenderer owner, FontDrawCmd[] cmdsData, int from, int to,
|
||||
int packedLight, float normalX, float normalY, float normalZ) {
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
ResourceLocation lastTexture = DUMMY_RESOURCE_LOCATION;
|
||||
boolean textureChanged = false;
|
||||
boolean drawing = false;
|
||||
try {
|
||||
for (int i = from; i < to; i++) {
|
||||
final FontDrawCmd cmd = cmdsData[i];
|
||||
if (!Objects.equals(lastTexture, cmd.texture)) {
|
||||
if (drawing) {
|
||||
tessellator.draw();
|
||||
drawing = false;
|
||||
}
|
||||
if (cmd.texture == null) {
|
||||
GLStateManager.disableTexture();
|
||||
} else {
|
||||
GLStateManager.enableTexture();
|
||||
((FontRendererAccessor) owner.underlying).angelica$bindTexture(cmd.texture);
|
||||
textureChanged = true;
|
||||
}
|
||||
lastTexture = cmd.texture;
|
||||
}
|
||||
if (!drawing) {
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setBrightness(packedLight);
|
||||
tessellator.setNormal(normalX, normalY, normalZ);
|
||||
drawing = true;
|
||||
}
|
||||
owner.emitCommand(tessellator, cmd);
|
||||
}
|
||||
} finally {
|
||||
if (drawing) {
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
return textureChanged;
|
||||
}
|
||||
|
||||
private void emitCommand(Tessellator tessellator, FontDrawCmd cmd) {
|
||||
final int firstVertex = cmd.startVtx / 6 * 4;
|
||||
final int quadCount = cmd.idxCount / 6;
|
||||
|
||||
for (int q = 0; q < quadCount; q++) {
|
||||
final long base = vertexDataAddress + (long) (firstVertex + q * 4) * VERTEX_SIZE;
|
||||
emitVertex(tessellator, base);
|
||||
emitVertex(tessellator, base + VERTEX_SIZE);
|
||||
emitVertex(tessellator, base + 3L * VERTEX_SIZE);
|
||||
emitVertex(tessellator, base + 2L * VERTEX_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
private void emitVertex(Tessellator tessellator, long ptr) {
|
||||
tessellator.setColorRGBA(
|
||||
memGetByte(ptr + 16) & 0xFF,
|
||||
memGetByte(ptr + 17) & 0xFF,
|
||||
memGetByte(ptr + 18) & 0xFF,
|
||||
memGetByte(ptr + 19) & 0xFF);
|
||||
tessellator.addVertexWithUV(memGetFloat(ptr), memGetFloat(ptr + 4), 0.0D,
|
||||
memGetFloat(ptr + 8), memGetFloat(ptr + 12));
|
||||
}
|
||||
|
||||
private static void restoreDepth(boolean testBefore, boolean maskBefore) {
|
||||
if (testBefore != GLStateManager.getDepthTest().isEnabled()) {
|
||||
if (testBefore) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
|
||||
}
|
||||
if (maskBefore != GLStateManager.getDepthState().isEnabled()) {
|
||||
if (maskBefore != GLStateManager.isEffectiveDepthMaskEnabled()) {
|
||||
GLStateManager.glDepthMask(maskBefore);
|
||||
}
|
||||
}
|
||||
@@ -865,7 +1179,7 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
|
||||
private static int hsvToRgb(float hue, float sat, float val) {
|
||||
int h = (int)(hue / 60f) % 6;
|
||||
int h = (int) (hue / 60f) % 6;
|
||||
float f = hue / 60f - h;
|
||||
float p = val * (1 - sat);
|
||||
float q = val * (1 - f * sat);
|
||||
@@ -879,11 +1193,12 @@ public class BatchingFontRenderer {
|
||||
case 4: r=t; g=p; b=val; break;
|
||||
default: r=val; g=p; b=q; break;
|
||||
}
|
||||
return ((int)(r*255) << 16) | ((int)(g*255) << 8) | (int)(b*255);
|
||||
return ((int) (r * 255) << 16) | ((int) (g * 255) << 8) | (int) (b * 255);
|
||||
}
|
||||
|
||||
private static final int RAINBOW_LUT_SIZE = 24;
|
||||
private static final int[] RAINBOW_LUT = new int[RAINBOW_LUT_SIZE];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < RAINBOW_LUT_SIZE; i++) {
|
||||
RAINBOW_LUT[i] = hsvToRgb(i * 15f, 1f, 1f);
|
||||
@@ -918,7 +1233,7 @@ public class BatchingFontRenderer {
|
||||
private static final float WAVE_FREQUENCY = 0.5f;
|
||||
|
||||
public float drawString(final float anchorX, final float anchorY, final int color, final boolean enableShadow,
|
||||
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
|
||||
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
|
||||
// noinspection SizeReplaceableByIsEmpty
|
||||
if (string == null || string.length() == 0) {
|
||||
return anchorX + (enableShadow ? 1.0f : 0.0f);
|
||||
@@ -930,7 +1245,7 @@ public class BatchingFontRenderer {
|
||||
|
||||
this.beginBatch();
|
||||
this.captureLightmapState();
|
||||
this.lightingFactorActive = FFPVertexLighting.modulatesVertexColor(this.lightingFactor);
|
||||
this.lightingFactorActive = isWorldSpaceText() && FFPVertexLighting.modulatesVertexColor(this.lightingFactor);
|
||||
float curX = anchorX;
|
||||
try {
|
||||
final int totalStringLength = string.length();
|
||||
@@ -1007,86 +1322,86 @@ public class BatchingFontRenderer {
|
||||
charIdx += SECTION_X_PAYLOAD;
|
||||
}
|
||||
} else {
|
||||
final boolean is09 = charInRange(fmtCode, '0', '9');
|
||||
final boolean isAF = charInRange(fmtCode, 'a', 'f');
|
||||
if (is09 || isAF) {
|
||||
curRandom = false;
|
||||
curBold = false;
|
||||
curStrikethrough = false;
|
||||
curUnderline = false;
|
||||
curItalic = false;
|
||||
curRainbow = false;
|
||||
curGradient = false;
|
||||
// wave/dinnerbone NOT reset — they're positional effects, independent of color
|
||||
|
||||
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
|
||||
final int rgb = this.colorCode[colorIdx];
|
||||
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
|
||||
final int shadowRgb = this.colorCode[colorIdx + 16];
|
||||
curShadowColor = (curShadowColor & 0xFF000000) | (shadowRgb & 0x00FFFFFF);
|
||||
} else if (fmtCode == 'k') {
|
||||
curRandom = true;
|
||||
} else if (fmtCode == 'l') {
|
||||
curBold = true;
|
||||
} else if (fmtCode == 'm') {
|
||||
curStrikethrough = true;
|
||||
strikethroughStartX = curX - 1.0f;
|
||||
strikethroughEndX = strikethroughStartX;
|
||||
} else if (fmtCode == 'n') {
|
||||
curUnderline = true;
|
||||
underlineStartX = curX - 1.0f;
|
||||
underlineEndX = underlineStartX;
|
||||
} else if (fmtCode == 'o') {
|
||||
curItalic = true;
|
||||
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
|
||||
curRainbow = true;
|
||||
curGradient = false;
|
||||
rainbowCharIndex = 0;
|
||||
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
|
||||
curWave = !curWave;
|
||||
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
|
||||
curDinnerbone = !curDinnerbone;
|
||||
} else if (fmtCode == 'u' && AngelicaConfig.enableDropShadow) {
|
||||
int customRgb = (charIdx + SECTION_X_LENGTH < stringEnd)
|
||||
? ColorCodeUtils.parseSectionXAt(string, charIdx + 1)
|
||||
: -1;
|
||||
if (customRgb != -1) {
|
||||
curShadow = true;
|
||||
curShadowCustomColor = true;
|
||||
curShadowColorOverride = customRgb;
|
||||
charIdx += SECTION_X_LENGTH;
|
||||
} else {
|
||||
curShadow = !curShadow;
|
||||
if (!curShadow) curShadowCustomColor = false;
|
||||
}
|
||||
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
|
||||
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
|
||||
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
|
||||
if (color1 != -1 && color2 != -1) {
|
||||
curGradient = true;
|
||||
final boolean is09 = charInRange(fmtCode, '0', '9');
|
||||
final boolean isAF = charInRange(fmtCode, 'a', 'f');
|
||||
if (is09 || isAF) {
|
||||
curRandom = false;
|
||||
curBold = false;
|
||||
curStrikethrough = false;
|
||||
curUnderline = false;
|
||||
curItalic = false;
|
||||
curRainbow = false;
|
||||
gradientStartRgb = color1;
|
||||
gradientEndRgb = color2;
|
||||
gradientCharIndex = 0;
|
||||
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
|
||||
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
|
||||
charIdx += GRADIENT_PAYLOAD;
|
||||
curGradient = false;
|
||||
// wave/dinnerbone NOT reset — they're positional effects, independent of color
|
||||
|
||||
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
|
||||
final int rgb = this.colorCode[colorIdx];
|
||||
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
|
||||
final int shadowRgb = this.colorCode[colorIdx + 16];
|
||||
curShadowColor = (curShadowColor & 0xFF000000) | (shadowRgb & 0x00FFFFFF);
|
||||
} else if (fmtCode == 'k') {
|
||||
curRandom = true;
|
||||
} else if (fmtCode == 'l') {
|
||||
curBold = true;
|
||||
} else if (fmtCode == 'm') {
|
||||
curStrikethrough = true;
|
||||
strikethroughStartX = curX - 1.0f;
|
||||
strikethroughEndX = strikethroughStartX;
|
||||
} else if (fmtCode == 'n') {
|
||||
curUnderline = true;
|
||||
underlineStartX = curX - 1.0f;
|
||||
underlineEndX = underlineStartX;
|
||||
} else if (fmtCode == 'o') {
|
||||
curItalic = true;
|
||||
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
|
||||
curRainbow = true;
|
||||
curGradient = false;
|
||||
rainbowCharIndex = 0;
|
||||
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
|
||||
curWave = !curWave;
|
||||
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
|
||||
curDinnerbone = !curDinnerbone;
|
||||
} else if (fmtCode == 'u' && AngelicaConfig.enableDropShadow) {
|
||||
int customRgb = (charIdx + SECTION_X_LENGTH < stringEnd)
|
||||
? ColorCodeUtils.parseSectionXAt(string, charIdx + 1)
|
||||
: -1;
|
||||
if (customRgb != -1) {
|
||||
curShadow = true;
|
||||
curShadowCustomColor = true;
|
||||
curShadowColorOverride = customRgb;
|
||||
charIdx += SECTION_X_LENGTH;
|
||||
} else {
|
||||
curShadow = !curShadow;
|
||||
if (!curShadow) curShadowCustomColor = false;
|
||||
}
|
||||
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
|
||||
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
|
||||
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
|
||||
if (color1 != -1 && color2 != -1) {
|
||||
curGradient = true;
|
||||
curRainbow = false;
|
||||
gradientStartRgb = color1;
|
||||
gradientEndRgb = color2;
|
||||
gradientCharIndex = 0;
|
||||
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
|
||||
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
|
||||
charIdx += GRADIENT_PAYLOAD;
|
||||
}
|
||||
} else if (fmtCode == 'r') {
|
||||
curRandom = false;
|
||||
curBold = false;
|
||||
curStrikethrough = false;
|
||||
curUnderline = false;
|
||||
curItalic = false;
|
||||
curRainbow = false;
|
||||
curWave = false;
|
||||
curDinnerbone = false;
|
||||
curGradient = false;
|
||||
curShadow = false;
|
||||
curShadowCustomColor = false;
|
||||
curColor = color;
|
||||
curShadowColor = shadowColor;
|
||||
}
|
||||
} else if (fmtCode == 'r') {
|
||||
curRandom = false;
|
||||
curBold = false;
|
||||
curStrikethrough = false;
|
||||
curUnderline = false;
|
||||
curItalic = false;
|
||||
curRainbow = false;
|
||||
curWave = false;
|
||||
curDinnerbone = false;
|
||||
curGradient = false;
|
||||
curShadow = false;
|
||||
curShadowCustomColor = false;
|
||||
curColor = color;
|
||||
curShadowColor = shadowColor;
|
||||
}
|
||||
} // close else block for non-§x codes
|
||||
|
||||
continue;
|
||||
@@ -1130,9 +1445,9 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
if (curGradient && gradientTotalChars > 0) {
|
||||
float t = Math.min(gradientCharIndex * gradientStep, 1f);
|
||||
int gr = (int)((gradientStartRgb >> 16 & 0xFF) * (1-t) + (gradientEndRgb >> 16 & 0xFF) * t);
|
||||
int gg = (int)((gradientStartRgb >> 8 & 0xFF) * (1-t) + (gradientEndRgb >> 8 & 0xFF) * t);
|
||||
int gb = (int)((gradientStartRgb & 0xFF) * (1-t) + (gradientEndRgb & 0xFF) * t);
|
||||
int gr = (int) ((gradientStartRgb >> 16 & 0xFF) * (1 - t) + (gradientEndRgb >> 16 & 0xFF) * t);
|
||||
int gg = (int) ((gradientStartRgb >> 8 & 0xFF) * (1 - t) + (gradientEndRgb >> 8 & 0xFF) * t);
|
||||
int gb = (int) ((gradientStartRgb & 0xFF) * (1 - t) + (gradientEndRgb & 0xFF) * t);
|
||||
int rgbEffect = (gr << 16) | (gg << 8) | gb;
|
||||
curColor = (curColor & 0xFF000000) | rgbEffect;
|
||||
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
|
||||
@@ -1155,7 +1470,7 @@ public class BatchingFontRenderer {
|
||||
// Wave: Y offset via sine wave
|
||||
float renderY = heightNorth;
|
||||
if (curWave) {
|
||||
float time = HUDCaching.renderingCacheOverride ? 0f : (float)((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
|
||||
float time = HUDCaching.renderingCacheOverride ? 0f : (float) ((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
|
||||
renderY += (float) Math.sin(visibleCharIndex * WAVE_FREQUENCY + time) * AngelicaConfig.waveAmplitude;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public record DynamicLightsEntityFilter(List<String> modPrefixes, List<String> nameTerms, DynamicLightsEntityFilter.State state) {
|
||||
|
||||
public enum State {
|
||||
ANY,
|
||||
ENABLED,
|
||||
DISABLED
|
||||
}
|
||||
|
||||
private static final Pattern WHITESPACE = Pattern.compile("\\s+");
|
||||
private static final DynamicLightsEntityFilter MATCH_ALL = new DynamicLightsEntityFilter(List.of(), List.of(), State.ANY);
|
||||
|
||||
public DynamicLightsEntityFilter {
|
||||
modPrefixes = List.copyOf(modPrefixes);
|
||||
nameTerms = List.copyOf(nameTerms);
|
||||
}
|
||||
|
||||
public static DynamicLightsEntityFilter parse(String query) {
|
||||
if (query == null) {
|
||||
return MATCH_ALL;
|
||||
}
|
||||
|
||||
final var normalized = query.strip().toLowerCase(Locale.ROOT);
|
||||
if (normalized.isEmpty()) {
|
||||
return MATCH_ALL;
|
||||
}
|
||||
|
||||
final var modPrefixes = new ArrayList<String>();
|
||||
final var nameTerms = new ArrayList<String>();
|
||||
var wantEnabled = false;
|
||||
var wantDisabled = false;
|
||||
|
||||
for (final var token : WHITESPACE.split(normalized)) {
|
||||
switch (token) {
|
||||
case "$on" -> wantEnabled = true;
|
||||
case "$off" -> wantDisabled = true;
|
||||
default -> {
|
||||
if (token.length() > 1 && token.charAt(0) == '@') {
|
||||
modPrefixes.add(token.substring(1));
|
||||
} else {
|
||||
nameTerms.add(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final var state = wantEnabled == wantDisabled ? State.ANY : wantEnabled ? State.ENABLED : State.DISABLED;
|
||||
if (modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY) {
|
||||
return MATCH_ALL;
|
||||
}
|
||||
return new DynamicLightsEntityFilter(modPrefixes, nameTerms, state);
|
||||
}
|
||||
|
||||
public boolean isMatchAll() {
|
||||
return modPrefixes.isEmpty() && nameTerms.isEmpty() && state == State.ANY;
|
||||
}
|
||||
|
||||
public boolean matches(String lowerName, String lowerModId, boolean enabled) {
|
||||
return matchesState(enabled) && matchesAnyMod(lowerModId) && containsAllTerms(lowerName);
|
||||
}
|
||||
|
||||
private boolean matchesState(boolean enabled) {
|
||||
return switch (state) {
|
||||
case ANY -> true;
|
||||
case ENABLED -> enabled;
|
||||
case DISABLED -> !enabled;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean matchesAnyMod(String lowerModId) {
|
||||
if (modPrefixes.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (final var prefix : modPrefixes) {
|
||||
if (lowerModId.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsAllTerms(String lowerName) {
|
||||
for (final var term : nameTerms) {
|
||||
if (!lowerName.contains(term)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
|
||||
|
||||
import net.coderbot.iris.gui.element.IrisGuiSlot;
|
||||
import net.coderbot.iris.gui.element.widget.IrisButton;
|
||||
|
||||
public class DynamicLightsEntityScreen extends GuiScreen {
|
||||
|
||||
private static final String ENABLED_PREFIX = EnumChatFormatting.GREEN + "[x] " + EnumChatFormatting.WHITE;
|
||||
private static final String DISABLED_PREFIX = EnumChatFormatting.DARK_GRAY + "[ ] " + EnumChatFormatting.GRAY;
|
||||
|
||||
private static List<EntityTypeEntry> entityTypes;
|
||||
|
||||
private final GuiScreen parent;
|
||||
private final String title = I18n.format("options.dynamiclights.entities.title");
|
||||
private final String info = I18n.format("options.dynamiclights.entities.info");
|
||||
|
||||
private List<EntityTypeEntry> displayed;
|
||||
private String countLabel;
|
||||
private String search = "";
|
||||
private boolean dirty;
|
||||
|
||||
private GuiTextField searchBox;
|
||||
private EntityRowList rowList;
|
||||
private IrisButton toggleButton;
|
||||
|
||||
public DynamicLightsEntityScreen(GuiScreen parent) {
|
||||
this.parent = parent;
|
||||
this.refilter();
|
||||
}
|
||||
|
||||
private static List<EntityTypeEntry> entityTypes() {
|
||||
if (entityTypes == null) {
|
||||
entityTypes = EntityLightConfig.getAllEntityTypes();
|
||||
}
|
||||
return entityTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
this.searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 150, 30, 300, 20);
|
||||
this.searchBox.setMaxStringLength(64);
|
||||
this.searchBox.setText(this.search);
|
||||
this.rowList = new EntityRowList();
|
||||
|
||||
this.toggleButton = new IrisButton(this.width / 2 - 154, this.height - 27, 150, 20, "", button -> this.toggleVisible());
|
||||
this.updateToggleButton();
|
||||
|
||||
this.buttonList.add(this.toggleButton);
|
||||
this.buttonList.add(new IrisButton(this.width / 2 + 4, this.height - 27, 150, 20, I18n.format("gui.done"), button -> this.onClose()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed() {
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
private void onClose() {
|
||||
if (this.dirty) {
|
||||
EntityLightConfig.save();
|
||||
this.dirty = false;
|
||||
}
|
||||
this.mc.displayGuiScreen(this.parent);
|
||||
}
|
||||
|
||||
private void refilter() {
|
||||
final var filter = DynamicLightsEntityFilter.parse(this.search);
|
||||
if (filter.isMatchAll()) {
|
||||
this.displayed = entityTypes();
|
||||
} else {
|
||||
final var matches = new ArrayList<EntityTypeEntry>();
|
||||
for (final var entry : entityTypes()) {
|
||||
if (filter.matches(entry.getSearchName(), entry.getSearchModId(), EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()))) {
|
||||
matches.add(entry);
|
||||
}
|
||||
}
|
||||
this.displayed = matches;
|
||||
}
|
||||
this.countLabel = this.displayed.size() + " / " + entityTypes().size();
|
||||
this.updateToggleButton();
|
||||
}
|
||||
|
||||
private boolean anyDisplayedEnabled() {
|
||||
for (final var entry : this.displayed) {
|
||||
if (EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateToggleButton() {
|
||||
if (this.toggleButton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var subset = this.displayed.size() != entityTypes().size();
|
||||
final var key = this.anyDisplayedEnabled()
|
||||
? (subset ? "options.dynamiclights.entities.disableshown" : "options.dynamiclights.entities.disableall")
|
||||
: (subset ? "options.dynamiclights.entities.enableshown" : "options.dynamiclights.entities.enableall");
|
||||
|
||||
this.toggleButton.displayString = I18n.format(key);
|
||||
this.toggleButton.enabled = !this.displayed.isEmpty();
|
||||
}
|
||||
|
||||
private void toggleVisible() {
|
||||
final var enable = !this.anyDisplayedEnabled();
|
||||
for (final var entry : this.displayed) {
|
||||
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), enable);
|
||||
}
|
||||
this.dirty = true;
|
||||
this.refilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||
this.drawDefaultBackground();
|
||||
this.rowList.drawScreen(mouseX, mouseY, partialTicks);
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
|
||||
drawCenteredString(this.fontRendererObj, this.title, this.width / 2, 8, 0xFFFFFF);
|
||||
drawCenteredString(this.fontRendererObj, this.info, this.width / 2, 20, 0xA0A0A0);
|
||||
this.searchBox.drawTextBox();
|
||||
if (!this.searchBox.isFocused() && this.search.isEmpty()) {
|
||||
this.drawString(this.fontRendererObj, I18n.format("options.dynamiclights.entities.search"), this.width / 2 - 144, 36, 0x808080);
|
||||
}
|
||||
drawCenteredString(this.fontRendererObj, this.countLabel, this.width / 2, this.height - 39, 0x808080);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScreen() {
|
||||
this.searchBox.updateCursorCounter();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
|
||||
if (!this.rowList.mouseClicked(mouseX, mouseY, mouseButton)) {
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
this.searchBox.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseMovedOrUp(int mouseX, int mouseY, int state) {
|
||||
if (state == -1 || !this.rowList.mouseReleased(mouseX, mouseY, Mouse.getEventButton())) {
|
||||
super.mouseMovedOrUp(mouseX, mouseY, state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button) {
|
||||
if (button.enabled && button instanceof IrisButton irisButton) {
|
||||
irisButton.onPress();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char typedChar, int keyCode) {
|
||||
if (this.searchBox.isFocused()) {
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.searchBox.setFocused(false);
|
||||
return;
|
||||
}
|
||||
this.searchBox.textboxKeyTyped(typedChar, keyCode);
|
||||
if (!this.searchBox.getText().equals(this.search)) {
|
||||
this.search = this.searchBox.getText();
|
||||
this.refilter();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (keyCode == Keyboard.KEY_ESCAPE) {
|
||||
this.onClose();
|
||||
return;
|
||||
}
|
||||
super.keyTyped(typedChar, keyCode);
|
||||
}
|
||||
|
||||
private class EntityRowList extends IrisGuiSlot {
|
||||
|
||||
EntityRowList() {
|
||||
super(DynamicLightsEntityScreen.this.mc, DynamicLightsEntityScreen.this.width, DynamicLightsEntityScreen.this.height, 56, DynamicLightsEntityScreen.this.height - 44, 18);
|
||||
this.setRenderBackground(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSize() {
|
||||
return displayed.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSelected(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawBackground() {}
|
||||
|
||||
@Override
|
||||
public int getListWidth() {
|
||||
return Math.min(400, DynamicLightsEntityScreen.this.width - 40);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean elementClicked(int index, boolean doubleClick, int mouseX, int mouseY, int button) {
|
||||
if (button != 0 || index >= displayed.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final var entry = displayed.get(index);
|
||||
EntityLightConfig.setEntityTypeEnabled(entry.getEntityClass(), !EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass()));
|
||||
dirty = true;
|
||||
updateToggleButton();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawSlot(int index, int x, int y, int slotHeight, Tessellator tessellator, int mouseX, int mouseY) {
|
||||
final var entry = displayed.get(index);
|
||||
final var enabled = EntityLightConfig.isEntityTypeEnabled(entry.getEntityClass());
|
||||
final var label = (enabled ? ENABLED_PREFIX : DISABLED_PREFIX) + entry.getDisplayName() + EnumChatFormatting.DARK_GRAY + " (" + entry.getModId() + ")";
|
||||
|
||||
drawString(fontRendererObj, fontRendererObj.trimStringToWidth(label, this.getListWidth() - 8), x, y + 5, 0xFFFFFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +1,32 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfigStorage;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.config.EntityTypeEntry;
|
||||
import jss.notfine.core.Settings;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.Option;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionGroup;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionImpl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionImpact;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.SubScreenOption;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DynamicLightsOptionPages {
|
||||
|
||||
public static OptionPage dynamicLights() {
|
||||
if (!DynamicLights.configEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<OptionGroup> groups = new ArrayList<>();
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
|
||||
.build());
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
|
||||
.build());
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
|
||||
.build());
|
||||
|
||||
// Entity type toggles - grouped by mod ID
|
||||
List<EntityTypeEntry> allEntities = EntityLightConfig.getAllEntityTypes();
|
||||
Map<String, List<EntityTypeEntry>> byMod = new LinkedHashMap<>();
|
||||
|
||||
for (EntityTypeEntry entry : allEntities) {
|
||||
byMod.computeIfAbsent(entry.getModId(), _ -> new ArrayList<>()).add(entry);
|
||||
}
|
||||
|
||||
// Create a group for each mod's entities
|
||||
for (Map.Entry<String, List<EntityTypeEntry>> modEntry : byMod.entrySet()) {
|
||||
String modId = modEntry.getKey();
|
||||
List<EntityTypeEntry> entities = modEntry.getValue();
|
||||
|
||||
OptionGroup.Builder groupBuilder = OptionGroup.createBuilder();
|
||||
|
||||
for (EntityTypeEntry entity : entities) {
|
||||
Option<Boolean> option = createEntityOption(entity);
|
||||
if (option != null) {
|
||||
groupBuilder.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
// Only add group if it has options
|
||||
OptionGroup group = groupBuilder.build();
|
||||
if (!group.getOptions().isEmpty()) {
|
||||
groups.add(group);
|
||||
}
|
||||
}
|
||||
|
||||
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
private static Option<Boolean> createEntityOption(EntityTypeEntry entry) {
|
||||
final String className = entry.getClassName();
|
||||
|
||||
// Resolve class once during option creation, not on every get/set
|
||||
Class<?> resolvedClass;
|
||||
try {
|
||||
resolvedClass = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Class not loaded, skip this option
|
||||
return null;
|
||||
}
|
||||
|
||||
final Class<?> entityClass = resolvedClass;
|
||||
|
||||
return OptionImpl.createBuilder(Boolean.class, EntityLightConfigStorage.INSTANCE)
|
||||
.setName(entry.getDisplayName() + " (" + entry.getModId() + ")")
|
||||
.setTooltip(I18n.format("options.dynamiclights.entity.tooltip", entry.getDisplayName()))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding(
|
||||
(_, value) -> EntityLightConfig.setEntityTypeEnabled(entityClass, value),
|
||||
storage -> EntityLightConfig.isEntityTypeEnabled(entityClass)
|
||||
)
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.build();
|
||||
return new OptionPage(I18n.format("options.dynamiclights.page"), ImmutableList.of(
|
||||
OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SHADER_FORCE.option)
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_FRUSTUM_CULLING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_ADAPTIVE_TICKING.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_CULL_TIMEOUT.option)
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOW_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_SLOWER_DIST.option)
|
||||
.add(Settings.DYNAMIC_LIGHTS_BACKGROUND_DIST.option)
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(new SubScreenOption(I18n.format("options.dynamiclights.entities"), I18n.format("options.dynamiclights.entities.tooltip"), DynamicLightsEntityScreen::new))
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,10 @@ public final class BopFogColorSource implements BopFogBlend.ColorSource {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void freeWorld() {
|
||||
this.world = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fogColour(int x, int y, int z) {
|
||||
final BiomeGenBase biome = FogBiomeCache.get(world, x, z);
|
||||
|
||||
@@ -9,7 +9,7 @@ public final class FogBiomeCache {
|
||||
private static final int MAX_ENTRIES = 16384;
|
||||
|
||||
private static final Long2ObjectOpenHashMap<BiomeGenBase> CACHE = new Long2ObjectOpenHashMap<>();
|
||||
private static World lastWorld;
|
||||
private static World cachedWorld;
|
||||
private static int generation;
|
||||
|
||||
private FogBiomeCache() {}
|
||||
@@ -18,10 +18,14 @@ public final class FogBiomeCache {
|
||||
return generation;
|
||||
}
|
||||
|
||||
public static void invalidate() {
|
||||
cachedWorld = null;
|
||||
}
|
||||
|
||||
public static BiomeGenBase get(World world, int x, int z) {
|
||||
if (world != lastWorld) {
|
||||
if (world != cachedWorld) {
|
||||
CACHE.clear();
|
||||
lastWorld = world;
|
||||
cachedWorld = world;
|
||||
generation++;
|
||||
}
|
||||
final long key = ((long) x << 32) | (z & 0xFFFFFFFFL);
|
||||
|
||||
@@ -61,13 +61,17 @@ public class NativeImage extends BufferedImage {
|
||||
// final int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_WIDTH);
|
||||
// final int height = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_HEIGHT);
|
||||
|
||||
final IntBuffer buffer = MemoryUtilities.memAllocInt(size);
|
||||
final int pixels = width * height;
|
||||
final IntBuffer buffer = MemoryUtilities.memAllocInt(pixels);
|
||||
|
||||
try {
|
||||
GLStateManager.glGetTexImage(GL11.GL_TEXTURE_2D, level, format.glFormat, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
|
||||
int[] data = new int[size];
|
||||
buffer.get(data);
|
||||
int[] data = new int[pixels];
|
||||
buffer.get(data, 0, pixels);
|
||||
for (int i = 0; i < pixels; i++) {
|
||||
data[i] = swapRedBlue(data[i]);
|
||||
}
|
||||
setRGB(0, 0, width, height, data, 0, width);
|
||||
} finally {
|
||||
MemoryUtilities.memFree(buffer);
|
||||
@@ -90,7 +94,7 @@ public class NativeImage extends BufferedImage {
|
||||
}
|
||||
|
||||
public static int getR(int i) {
|
||||
return i >> 0 & 255;
|
||||
return i & 255;
|
||||
}
|
||||
|
||||
public static int getG(int i) {
|
||||
@@ -102,11 +106,15 @@ public class NativeImage extends BufferedImage {
|
||||
}
|
||||
|
||||
public int getPixelRGBA(int x, int y) {
|
||||
return getRGB(x, y);
|
||||
return swapRedBlue(getRGB(x, y));
|
||||
}
|
||||
|
||||
public void setPixelRGBA(int x, int y, int rgb) {
|
||||
setRGB(x, y, rgb);
|
||||
setRGB(x, y, swapRedBlue(rgb));
|
||||
}
|
||||
|
||||
private static int swapRedBlue(int c) {
|
||||
return (c & 0xFF00FF00) | ((c >>> 16) & 0xFF) | ((c & 0xFF) << 16);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.coderbot.batchedentityrendering.impl.BlendingStateHolder;
|
||||
import net.coderbot.batchedentityrendering.impl.TransparencyType;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -68,7 +69,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
return TRANSLUCENT;
|
||||
}
|
||||
|
||||
private static MultiPhaseParameters.Builder tesrMaterialPhases(ResourceLocation texture, TesrMaterial material) {
|
||||
private static MultiPhaseParameters.Builder tesrMaterialPhases(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
|
||||
final MultiPhaseParameters.Builder b = MultiPhaseParameters.builder();
|
||||
b.texture(texture != null ? new RenderPhase.Texture(texture, false, false) : NO_TEXTURE);
|
||||
if (material.isNoCull()) {
|
||||
@@ -92,6 +93,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
case GLINT -> b.transparency(GLINT_TRANSPARENCY);
|
||||
case OPAQUE -> b.transparency(NO_TRANSPARENCY);
|
||||
}
|
||||
boolean shaderPhaseTaken = true;
|
||||
switch (material.special()) {
|
||||
case GLINT -> b.shader(SPECIAL_GLINT);
|
||||
case BEACON_BEAM -> b.shader(SPECIAL_BEACON_BEAM);
|
||||
@@ -99,9 +101,15 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
final TesrShader shader = material.shader();
|
||||
if (shader != null) {
|
||||
b.shader(new RenderPhase.Shader("angelica_tesr_shader_" + shader.name(), shader.bind(), shader.release()));
|
||||
} else {
|
||||
shaderPhaseTaken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shaderPhaseTaken && pass != PassOverride.NONE) {
|
||||
b.shader(new RenderPhase.Shader("angelica_tesr_pass" + pass.nameSuffix(), pass::apply, pass::clear));
|
||||
}
|
||||
b.layering(polygonOffset(offsetFactor, offsetUnits));
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -113,12 +121,23 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
GbufferPrograms::teardownSpecialRenderCondition);
|
||||
|
||||
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material) {
|
||||
final MultiPhaseParameters.Builder b = tesrMaterialPhases(texture, material).shadeModel(SMOOTH_SHADE_MODEL);
|
||||
return tesr(texture, material, PassOverride.NONE, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
|
||||
final MultiPhaseParameters.Builder b = tesrMaterialPhases(texture, material, pass, offsetFactor, offsetUnits).shadeModel(SMOOTH_SHADE_MODEL);
|
||||
final boolean sortsTranslucent = material.transparency() != TesrMaterial.Transparency.OPAQUE;
|
||||
return of("angelica_tesr_" + material.transparency().name().toLowerCase(Locale.ROOT),
|
||||
return of("angelica_tesr_" + material.transparency().name().toLowerCase(Locale.ROOT) + pass.nameSuffix()
|
||||
+ (offsetFactor == 0.0f && offsetUnits == 0.0f ? "" : "_offset" + offsetFactor + "_" + offsetUnits),
|
||||
BatchVertexFormats.POSITION_COLOR_TEXTURE_LIGHTF_NORMAL, GL11.GL_QUADS, 65536, true, sortsTranslucent, b.build(false));
|
||||
}
|
||||
|
||||
public static RenderLayer tesrNoPass(ResourceLocation texture, TesrMaterial material) {
|
||||
return of("angelica_tesr_nopass_" + material.transparency().name().toLowerCase(Locale.ROOT),
|
||||
BatchVertexFormats.POSITION_COLOR_TEXTURE_LIGHTF_NORMAL, GL11.GL_QUADS, 65536, true, false,
|
||||
tesrMaterialPhases(texture, material, PassOverride.NONE, 0.0f, 0.0f).build(false));
|
||||
}
|
||||
|
||||
public static RenderLayer getOutline(ResourceLocation texture, RenderPhase.Cull cull) {
|
||||
return of("outline", DefaultVertexFormat.POSITION_COLOR_TEXTURE, 7, 256, RenderLayer.MultiPhaseParameters.builder().texture(new RenderPhase.Texture(texture, false, false)).cull(cull).depthTest(ALWAYS_DEPTH_TEST).alpha(ONE_TENTH_ALPHA).fog(NO_FOG).target(OUTLINE_TARGET).build(RenderLayer.OutlineMode.IS_OUTLINE));
|
||||
}
|
||||
@@ -313,6 +332,10 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
|
||||
return this;
|
||||
}
|
||||
|
||||
public void layering(Layering layering) {
|
||||
this.layering = layering;
|
||||
}
|
||||
|
||||
public Builder shadeModel(RenderPhase.ShadeModel shadeModel) {
|
||||
this.shadeModel = shadeModel;
|
||||
return this;
|
||||
|
||||
@@ -9,7 +9,9 @@ import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@@ -77,6 +79,7 @@ public abstract class RenderPhase {
|
||||
protected static final WriteMaskState COLOR_MASK;
|
||||
protected static final WriteMaskState DEPTH_MASK;
|
||||
protected static final Layering NO_LAYERING;
|
||||
|
||||
protected static final Fog NO_FOG;
|
||||
protected static final Fog FOG;
|
||||
protected static final Fog BLACK_FOG;
|
||||
@@ -85,6 +88,22 @@ public abstract class RenderPhase {
|
||||
protected static final Target TRANSLUCENT_TARGET;
|
||||
protected static final Shader NO_SHADER = new Shader("no_shader", () -> {}, () -> {});
|
||||
|
||||
private static final Map<Long, Layering> POLYGON_OFFSET_LAYERINGS = new ConcurrentHashMap<>();
|
||||
|
||||
public static Layering polygonOffset(float factor, float units) {
|
||||
if (factor == 0.0f && units == 0.0f) {
|
||||
return NO_LAYERING;
|
||||
}
|
||||
final long key = ((long) Float.floatToIntBits(factor) << 32) | (Float.floatToIntBits(units) & 0xFFFFFFFFL);
|
||||
return POLYGON_OFFSET_LAYERINGS.computeIfAbsent(key, k -> new Layering("polygon_offset_" + factor + "_" + units, () -> {
|
||||
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(factor, units);
|
||||
}, () -> {
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(0.0f, 0.0f);
|
||||
}));
|
||||
}
|
||||
|
||||
public RenderPhase(String name, Runnable beginAction, Runnable endAction) {
|
||||
this.name = name;
|
||||
this.beginAction = beginAction;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.config;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.config.Config;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
import net.coderbot.iris.shadows.ShadowGraphGate;
|
||||
|
||||
@Config(modid = "angelica", filename = "angelica-modules")
|
||||
public class AngelicaConfig {
|
||||
@@ -347,6 +348,20 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean shadowSkipInMeshTileEntities;
|
||||
|
||||
@Config.Comment({
|
||||
"How much a celestial body can move before the shadow map can update.",
|
||||
"",
|
||||
"Note this option is disabled when a shader pack utilizes voxelization features."
|
||||
})
|
||||
@Config.DefaultFloat(ShadowGraphGate.DEFAULT_ANGLE_DELTA_DEGREES)
|
||||
@Config.RangeFloat(min = 0f, max = 0.1f)
|
||||
public static float shadowGraphAngleDelta;
|
||||
|
||||
@Config.Comment("Adjusts the rate the shadow map rebuilds when when a celestial body is near the horizon. Rate is sinusoidal")
|
||||
@Config.DefaultFloat(ShadowGraphGate.DEFAULT_HORIZON_SCALE)
|
||||
@Config.RangeFloat(min = 0f, max = 1.0f)
|
||||
public static float shadowGraphHorizonScale;
|
||||
|
||||
@Config.Comment("ASM transformer exclusion narrowing for mod compatibility. Disable per-mod if narrowing causes class loading issues.")
|
||||
public static TransformerCompat transformerCompat = new TransformerCompat();
|
||||
|
||||
@@ -437,6 +452,18 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static GLProfile glProfile;
|
||||
|
||||
@Config.Comment("Enable random top-face texture orientation for configured blocks")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableNaturalTextures = true;
|
||||
|
||||
@Config.Comment("List of block registry names to apply random top-face texture rotation to")
|
||||
@Config.DefaultStringList({
|
||||
"minecraft:andesite", "minecraft:dirt", "minecraft:granite", "minecraft:grass", "minecraft:mycelium", "minecraft:sand", "minecraft:soul_sand", "etfuturum:calcite", "etfuturum:coarse_dirt", "etfuturum:cobbled_deepslate", "etfuturum:concrete_powder", "etfuturum:deepslate", "etfuturum:polished_deepslate", "etfuturum:soul_soil", "etfuturum:tuff", "BiomesOPlenty:ash", "BiomesOPlenty:cragRock", "BiomesOPlenty:driedDirt", "BiomesOPlenty:hardDirt", "BiomesOPlenty:hardSand", "BiomesOPlenty:mud", "BiomesOPlenty:newBopDirt", "Botania:dirtPath", "Botania:enchantedSoil", "Botania:livingrock", "Botania:prismarine", "Botania:shimmerrock", "Botany:loam", "Botany:loamNoWeed", "Botany:soil", "Botany:soilNoWeed", "chisel:moss", "chisel:moss_carpet", "DraconicEvolution:earth", "dreamcraft:SandClayMix", "ExtraUtilities:color_hellsand", "ExtraUtilities:cursedearthside", "GalaxySpace:acentauribbgrunt", "GalaxySpace:acentauribbsubgrunt", "GalaxySpace:barnardaCdirt", "GalaxySpace:barnardaEgrunt", "GalaxySpace:barnardaEsubgrunt", "GalaxySpace:barnardaFgrunt", "GalaxySpace:barnardaFsubgrunt", "GalaxySpace:callistoblocks", "GalaxySpace:ceresblocks", "GalaxySpace:deimosblocks", "GalaxySpace:europagrunt", "GalaxySpace:ganymedeblocks", "GalaxySpace:haumeablocks", "GalaxySpace:ioblocks", "GalaxySpace:makemakegrunt", "GalaxySpace:mercuryblocks", "GalaxySpace:mirandablocks", "GalaxySpace:oberonblocks", "GalaxySpace:phobosblocks", "GalaxySpace:proteusblocks", "GalaxySpace:tcetieblocks", "GalaxySpace:titanblocks", "GalaxySpace:tritonblocks", "GalaxySpace:vegabgrunt", "GalaxySpace:vegabsubgrunt", "GalaxySpace:venusblocks", "gregtech:gt.blockgranites", "IC2:blockBasalt", "MagicBees:magicbees.enchantedEarth", "Natura:heatsand", "RandomThings:fertilizedDirt", "TConstruct:CraftedSoil", "TConstruct:SpeedBlock", "thaumicbases:oldCobble", "thaumicbases:oldCobbleMossy", "ToxicEverglades:blockDarkWorldGround2", "VillageNames:concretePowder", "witchery:pitdirt"
|
||||
})
|
||||
public static String[] naturalTextureBlocks = new String[] {
|
||||
"minecraft:andesite", "minecraft:dirt", "minecraft:granite", "minecraft:grass", "minecraft:mycelium", "minecraft:sand", "minecraft:soul_sand", "etfuturum:calcite", "etfuturum:coarse_dirt", "etfuturum:cobbled_deepslate", "etfuturum:concrete_powder", "etfuturum:deepslate", "etfuturum:polished_deepslate", "etfuturum:soul_soil", "etfuturum:tuff", "BiomesOPlenty:ash", "BiomesOPlenty:cragRock", "BiomesOPlenty:driedDirt", "BiomesOPlenty:hardDirt", "BiomesOPlenty:hardSand", "BiomesOPlenty:mud", "BiomesOPlenty:newBopDirt", "Botania:dirtPath", "Botania:enchantedSoil", "Botania:livingrock", "Botania:prismarine", "Botania:shimmerrock", "Botany:loam", "Botany:loamNoWeed", "Botany:soil", "Botany:soilNoWeed", "chisel:moss", "chisel:moss_carpet", "DraconicEvolution:earth", "dreamcraft:SandClayMix", "ExtraUtilities:color_hellsand", "ExtraUtilities:cursedearthside", "GalaxySpace:acentauribbgrunt", "GalaxySpace:acentauribbsubgrunt", "GalaxySpace:barnardaCdirt", "GalaxySpace:barnardaEgrunt", "GalaxySpace:barnardaEsubgrunt", "GalaxySpace:barnardaFgrunt", "GalaxySpace:barnardaFsubgrunt", "GalaxySpace:callistoblocks", "GalaxySpace:ceresblocks", "GalaxySpace:deimosblocks", "GalaxySpace:europagrunt", "GalaxySpace:ganymedeblocks", "GalaxySpace:haumeablocks", "GalaxySpace:ioblocks", "GalaxySpace:makemakegrunt", "GalaxySpace:mercuryblocks", "GalaxySpace:mirandablocks", "GalaxySpace:oberonblocks", "GalaxySpace:phobosblocks", "GalaxySpace:proteusblocks", "GalaxySpace:tcetieblocks", "GalaxySpace:titanblocks", "GalaxySpace:tritonblocks", "GalaxySpace:vegabgrunt", "GalaxySpace:vegabsubgrunt", "GalaxySpace:venusblocks", "gregtech:gt.blockgranites", "IC2:blockBasalt", "MagicBees:magicbees.enchantedEarth", "Natura:heatsand", "RandomThings:fertilizedDirt", "TConstruct:CraftedSoil", "TConstruct:SpeedBlock", "thaumicbases:oldCobble", "thaumicbases:oldCobbleMossy", "ToxicEverglades:blockDarkWorldGround2", "VillageNames:concretePowder", "witchery:pitdirt"
|
||||
};
|
||||
|
||||
public static void applyGpuCullingMode() {
|
||||
GpuCulling.setMode(gpuCullingMode == null ? GpuCullingMode.CPU_ONLY : gpuCullingMode);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,9 @@ public class FontConfig {
|
||||
@Config.RangeFloat(min = -2F, max = 2F)
|
||||
public static float glyphSpacing;
|
||||
|
||||
@Config.Comment("Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.")
|
||||
@Config.Comment({
|
||||
"Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.",
|
||||
"Turning off AA lets shaders pick up the text."})
|
||||
@Config.DefaultInt(2)
|
||||
@Config.RangeInt(min = 0, max = 2)
|
||||
public static int fontAAMode;
|
||||
|
||||
@@ -13,6 +13,7 @@ import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -41,9 +42,12 @@ public final class FlybyRunner {
|
||||
private int runTicks;
|
||||
private int tick;
|
||||
private boolean waitForTracy;
|
||||
private boolean waitForFocus;
|
||||
private boolean exitWhenDone;
|
||||
private boolean startedFromProperties;
|
||||
private volatile boolean freezeRequested;
|
||||
private boolean pauseOnLostFocusSaved;
|
||||
private boolean pauseOnLostFocusOverridden;
|
||||
|
||||
private double originX, originY, originZ;
|
||||
private float originYaw, originPitch;
|
||||
@@ -83,10 +87,11 @@ public final class FlybyRunner {
|
||||
this.startedFromProperties = true;
|
||||
this.start(configured, SystemProperties.FLYBY_LENGTH, SystemProperties.FLYBY_WARMUP_TICKS, SystemProperties.FLYBY_SPEED);
|
||||
this.waitForTracy = SystemProperties.FLYBY_WAIT_FOR_TRACY;
|
||||
this.waitForFocus = SystemProperties.FLYBY_WAIT_FOR_FOCUS;
|
||||
this.exitWhenDone = SystemProperties.FLYBY_EXIT_WHEN_DONE;
|
||||
LOGGER.info("Flyby started from properties: route={} warmup={} length={} {} ({} ticks) waitForTracy={} exitWhenDone={}",
|
||||
LOGGER.info("Flyby started from properties: route={} warmup={} length={} {} ({} ticks) waitForTracy={} waitForFocus={} exitWhenDone={}",
|
||||
configured.id(), this.warmupTicks, this.runLength, configured.lengthUnit(), this.runTicks,
|
||||
this.waitForTracy, this.exitWhenDone);
|
||||
this.waitForTracy, this.waitForFocus, this.exitWhenDone);
|
||||
}
|
||||
|
||||
public void start(FlybyRoute route, int length, int warmupTicks, double speed) {
|
||||
@@ -99,6 +104,7 @@ public final class FlybyRunner {
|
||||
this.frameCount = 0;
|
||||
this.framesTruncated = false;
|
||||
this.waitForTracy = false;
|
||||
this.waitForFocus = false;
|
||||
this.exitWhenDone = false;
|
||||
this.state = State.WAITING;
|
||||
}
|
||||
@@ -123,6 +129,7 @@ public final class FlybyRunner {
|
||||
switch (this.state) {
|
||||
case WAITING -> {
|
||||
if (this.waitForTracy && !Tracy.isConnected()) return;
|
||||
if (this.waitForFocus && !Display.isActive()) return;
|
||||
this.begin(mc, player);
|
||||
}
|
||||
case WARMUP -> {
|
||||
@@ -172,6 +179,15 @@ public final class FlybyRunner {
|
||||
}
|
||||
|
||||
private void begin(Minecraft mc, EntityClientPlayerMP player) {
|
||||
if (mc.currentScreen != null) {
|
||||
mc.displayGuiScreen(null);
|
||||
}
|
||||
if (!this.pauseOnLostFocusOverridden) {
|
||||
this.pauseOnLostFocusSaved = mc.gameSettings.pauseOnLostFocus;
|
||||
this.pauseOnLostFocusOverridden = true;
|
||||
mc.gameSettings.pauseOnLostFocus = false;
|
||||
}
|
||||
|
||||
this.originX = player.posX;
|
||||
this.originY = player.posY;
|
||||
this.originZ = player.posZ;
|
||||
@@ -397,11 +413,18 @@ public final class FlybyRunner {
|
||||
Tracy.message("flyby teardown");
|
||||
|
||||
this.returnToOrigin(player);
|
||||
this.restorePauseOnLostFocus(mc);
|
||||
|
||||
this.tick = 0;
|
||||
this.state = this.exitWhenDone ? State.EXITING : State.DONE;
|
||||
}
|
||||
|
||||
private void restorePauseOnLostFocus(Minecraft mc) {
|
||||
if (!this.pauseOnLostFocusOverridden) return;
|
||||
mc.gameSettings.pauseOnLostFocus = this.pauseOnLostFocusSaved;
|
||||
this.pauseOnLostFocusOverridden = false;
|
||||
}
|
||||
|
||||
private void returnToOrigin(EntityClientPlayerMP player) {
|
||||
player.motionX = 0.0D;
|
||||
player.motionY = 0.0D;
|
||||
@@ -448,6 +471,7 @@ public final class FlybyRunner {
|
||||
Tracy.sectionLeave(this.runSection);
|
||||
this.runSection = 0L;
|
||||
FramePacer.endStats();
|
||||
this.restorePauseOnLostFocus(Minecraft.getMinecraft());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -71,7 +71,6 @@ public final class EntityLightConfig {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<EntityTypeEntry> getAllEntityTypes() {
|
||||
List<EntityTypeEntry> entries = new ArrayList<>();
|
||||
Set<String> seenClasses = new HashSet<>();
|
||||
@@ -88,9 +87,8 @@ public final class EntityLightConfig {
|
||||
|
||||
String modId = getModIdForEntity(entityClass, entityId);
|
||||
String displayName = getDisplayName(entityId, entityClass);
|
||||
boolean enabled = !disabledClassNames.contains(className);
|
||||
|
||||
entries.add(new EntityTypeEntry(displayName, className, modId, enabled));
|
||||
entries.add(new EntityTypeEntry(displayName, entityClass, modId));
|
||||
}
|
||||
|
||||
// Sort by mod ID, then by display name
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights.config;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
|
||||
|
||||
public class EntityLightConfigStorage implements OptionStorage<EntityLightConfigStorage> {
|
||||
|
||||
public static final EntityLightConfigStorage INSTANCE = new EntityLightConfigStorage();
|
||||
|
||||
private EntityLightConfigStorage() {}
|
||||
|
||||
@Override
|
||||
public EntityLightConfigStorage getData() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
EntityLightConfig.save();
|
||||
}
|
||||
}
|
||||
+20
-15
@@ -1,42 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights.config;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EntityTypeEntry {
|
||||
private final String displayName;
|
||||
private final String className;
|
||||
private final String modId;
|
||||
private boolean enabled;
|
||||
public final class EntityTypeEntry {
|
||||
|
||||
public EntityTypeEntry(@NotNull String displayName, @NotNull String className, @NotNull String modId, boolean enabled) {
|
||||
private final String displayName;
|
||||
private final Class<?> entityClass;
|
||||
private final String modId;
|
||||
private final String searchName;
|
||||
private final String searchModId;
|
||||
|
||||
public EntityTypeEntry(@NotNull String displayName, @NotNull Class<?> entityClass, @NotNull String modId) {
|
||||
this.displayName = displayName;
|
||||
this.className = className;
|
||||
this.entityClass = entityClass;
|
||||
this.modId = modId;
|
||||
this.enabled = enabled;
|
||||
this.searchName = displayName.toLowerCase(Locale.ROOT);
|
||||
this.searchModId = modId.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
public Class<?> getEntityClass() {
|
||||
return entityClass;
|
||||
}
|
||||
|
||||
public String getModId() {
|
||||
return modId;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
public String getSearchName() {
|
||||
return searchName;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
public String getSearchModId() {
|
||||
return searchModId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return displayName + " (" + modId + ") [" + (enabled ? "ON" : "OFF") + "]";
|
||||
return displayName + " (" + modId + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package com.gtnewhorizons.angelica.iris;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.rendering.items.ItemRenderListManager;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.coderbot.iris.vertices.IrisQuadView;
|
||||
import net.coderbot.iris.vertices.NormI8;
|
||||
import net.coderbot.iris.vertices.NormalHelper;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
|
||||
|
||||
/**
|
||||
* Supplies per-quad {@code mc_midTexCoord} + {@code at_tangent} for immediate-mode draws so parallax/POM shader packs
|
||||
* reconstruct sprite bounds correctly.
|
||||
*/
|
||||
public final class ImmediateExtendedAttribs implements ImmediateExtendedAttribHandler {
|
||||
|
||||
private static final int VERTEX_SIZE = 8;
|
||||
private static final int X = 0, Y = 1, Z = 2, U = 3, V = 4;
|
||||
|
||||
private static final float UV_EPS = 1.0e-5f;
|
||||
private static final float POM_SUPPRESS_EPS = 1.0e-4f;
|
||||
|
||||
private static final float POM_MAX_TILE_EXTENT = 0.125f;
|
||||
|
||||
private static final int WANTS_MID = 1;
|
||||
private static final int WANTS_TANGENT = 2;
|
||||
|
||||
private static final int NEUTRAL_TANGENT = NormI8.pack(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
private static volatile int currentProgramWants = 0;
|
||||
|
||||
private static final Int2IntOpenHashMap programWantsExtCache = new Int2IntOpenHashMap();
|
||||
|
||||
public static void onProgramBound(int program) {
|
||||
if (program == 0) {
|
||||
currentProgramWants = 0;
|
||||
return;
|
||||
}
|
||||
int wants;
|
||||
if (programWantsExtCache.containsKey(program)) {
|
||||
wants = programWantsExtCache.get(program);
|
||||
} else {
|
||||
wants = 0;
|
||||
if (GLStateManager.glGetAttribLocation(program, "mc_midTexCoord") >= 0) wants |= WANTS_MID;
|
||||
if (GLStateManager.glGetAttribLocation(program, "at_tangent") >= 0) wants |= WANTS_TANGENT;
|
||||
programWantsExtCache.put(program, wants);
|
||||
}
|
||||
currentProgramWants = wants;
|
||||
}
|
||||
|
||||
public static void onProgramDeleted(int program) {
|
||||
programWantsExtCache.remove(program);
|
||||
}
|
||||
|
||||
public static void onShaderPackChanged() {
|
||||
currentProgramWants = 0;
|
||||
programWantsExtCache.clear();
|
||||
ItemRenderListManager.clearCache();
|
||||
FfpExtendedAttribs.reset();
|
||||
TesrBatchRenderer.INSTANCE.clearRetained();
|
||||
}
|
||||
|
||||
private final Vector3f normal = new Vector3f();
|
||||
private final QuadView quad = new QuadView();
|
||||
private final PackedQuadView packedQuad = new PackedQuadView();
|
||||
|
||||
private boolean limitTileExtent = true;
|
||||
private boolean needTangent = true;
|
||||
|
||||
@Override
|
||||
public boolean wantsExtended() {
|
||||
return Iris.enabled && currentProgramWants != 0 && GLStateManager.getActiveProgram() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsTangent() {
|
||||
return (currentProgramWants & WANTS_TANGENT) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsExtendedCapture() {
|
||||
return Iris.enabled && Iris.getCurrentPack().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(int[] rawBuffer, int vertexCount, int vertsPerPrim, int normalIntIndex, long dstAddr, int dstStride) {
|
||||
final boolean smooth = normalIntIndex >= 0;
|
||||
limitTileExtent = isAtlasNotBound();
|
||||
needTangent = wantsTangent();
|
||||
long ptr = dstAddr;
|
||||
for (int v = 0; v < vertexCount; v += vertsPerPrim) {
|
||||
quad.setup(rawBuffer, v, normalIntIndex);
|
||||
if (vertsPerPrim == 4) {
|
||||
writeQuadExt(quad, ptr, dstStride);
|
||||
} else {
|
||||
writeTriExt(quad, smooth, ptr, dstStride);
|
||||
}
|
||||
ptr += (long) dstStride * vertsPerPrim;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildPacked(long srcBase, int stride, int posOffset, int texOffset, int normalOffset,
|
||||
int vertexCount, int vertsPerPrim, long dstAddr, int dstStride) {
|
||||
final boolean smooth = normalOffset >= 0;
|
||||
limitTileExtent = isAtlasNotBound();
|
||||
needTangent = wantsTangent();
|
||||
long ptr = dstAddr;
|
||||
for (int v = 0; v < vertexCount; v += vertsPerPrim) {
|
||||
packedQuad.setup(srcBase + (long) v * stride, stride, posOffset, texOffset, normalOffset);
|
||||
if (vertsPerPrim == 4) {
|
||||
writeQuadExt(packedQuad, ptr, dstStride);
|
||||
} else {
|
||||
writeTriExt(packedQuad, smooth, ptr, dstStride);
|
||||
}
|
||||
ptr += (long) dstStride * vertsPerPrim;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeQuadExt(IrisQuadView quad, long dstAddr, int dstStride) {
|
||||
final float u0 = quad.u(0), u1 = quad.u(1), u2 = quad.u(2), u3 = quad.u(3);
|
||||
final float v0 = quad.v(0), v1 = quad.v(1), v2 = quad.v(2), v3 = quad.v(3);
|
||||
|
||||
final float umin = min4(u0, u1, u2, u3), umax = max4(u0, u1, u2, u3);
|
||||
final float vmin = min4(v0, v1, v2, v3), vmax = max4(v0, v1, v2, v3);
|
||||
|
||||
final boolean tileable = formsRectAxisQuad(u0, u1, u2, u3) && formsRectAxisQuad(v0, v1, v2, v3)
|
||||
&& (!limitTileExtent || ((umax - umin) <= POM_MAX_TILE_EXTENT && (vmax - vmin) <= POM_MAX_TILE_EXTENT));
|
||||
|
||||
final int tangent;
|
||||
if (needTangent) {
|
||||
NormalHelper.computeFaceNormal(normal, quad);
|
||||
tangent = NormalHelper.computeTangent(normal.x, normal.y, normal.z, quad);
|
||||
} else {
|
||||
tangent = NEUTRAL_TANGENT;
|
||||
}
|
||||
|
||||
long ptr = dstAddr;
|
||||
if (!tileable) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
memPutFloat(ptr, quad.u(i) + POM_SUPPRESS_EPS);
|
||||
memPutFloat(ptr + 4, quad.v(i) + POM_SUPPRESS_EPS);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final float midU = (umin + umax) * 0.5f;
|
||||
final float midV = (vmin + vmax) * 0.5f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
memPutFloat(ptr, midU);
|
||||
memPutFloat(ptr + 4, midV);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTriExt(NormalSource view, boolean smooth, long dstAddr, int dstStride) {
|
||||
final float u0 = view.u(0), u1 = view.u(1), u2 = view.u(2);
|
||||
final float v0 = view.v(0), v1 = view.v(1), v2 = view.v(2);
|
||||
|
||||
final float umin = Math.min(u0, Math.min(u1, u2)), umax = Math.max(u0, Math.max(u1, u2));
|
||||
final float vmin = Math.min(v0, Math.min(v1, v2)), vmax = Math.max(v0, Math.max(v1, v2));
|
||||
|
||||
final boolean tileable = formsRectAxis(u0, u1, u2) && formsRectAxis(v0, v1, v2)
|
||||
&& (!limitTileExtent || ((umax - umin) <= POM_MAX_TILE_EXTENT && (vmax - vmin) <= POM_MAX_TILE_EXTENT));
|
||||
if (!tileable) {
|
||||
writeTriSuppressed(view, dstAddr, dstStride);
|
||||
return;
|
||||
}
|
||||
|
||||
final float midU = (umin + umax) * 0.5f;
|
||||
final float midV = (vmin + vmax) * 0.5f;
|
||||
|
||||
long ptr = dstAddr;
|
||||
if (smooth) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final int n = view.packedNormal(i);
|
||||
final int tangent = needTangent
|
||||
? NormalHelper.computeTangentSmooth(NormI8.unpackX(n), NormI8.unpackY(n), NormI8.unpackZ(n), view)
|
||||
: NEUTRAL_TANGENT;
|
||||
memPutFloat(ptr, midU);
|
||||
memPutFloat(ptr + 4, midV);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
} else {
|
||||
final int tangent;
|
||||
if (needTangent) {
|
||||
NormalHelper.computeFaceNormalTri(normal, view);
|
||||
tangent = NormalHelper.computeTangent(normal.x, normal.y, normal.z, view);
|
||||
} else {
|
||||
tangent = NEUTRAL_TANGENT;
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
memPutFloat(ptr, midU);
|
||||
memPutFloat(ptr + 4, midV);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTriSuppressed(NormalSource view, long dstAddr, int dstStride) {
|
||||
final int tangent;
|
||||
if (needTangent) {
|
||||
NormalHelper.computeFaceNormalTri(normal, view);
|
||||
tangent = NormalHelper.computeTangent(normal.x, normal.y, normal.z, view);
|
||||
} else {
|
||||
tangent = NEUTRAL_TANGENT;
|
||||
}
|
||||
long ptr = dstAddr;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
memPutFloat(ptr, view.u(i) + POM_SUPPRESS_EPS);
|
||||
memPutFloat(ptr + 4, view.v(i) + POM_SUPPRESS_EPS);
|
||||
memPutInt(ptr + 8, tangent);
|
||||
ptr += dstStride;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean formsRectAxis(float a, float b, float c) {
|
||||
final boolean ab = Math.abs(a - b) <= UV_EPS;
|
||||
final boolean ac = Math.abs(a - c) <= UV_EPS;
|
||||
final boolean bc = Math.abs(b - c) <= UV_EPS;
|
||||
return (ab || ac || bc) && !(ab && ac && bc);
|
||||
}
|
||||
|
||||
private static boolean formsRectAxisQuad(float a, float b, float c, float d) {
|
||||
final float lo = min4(a, b, c, d), hi = max4(a, b, c, d);
|
||||
if (hi - lo <= UV_EPS) return false;
|
||||
return atEdge(a, lo, hi) && atEdge(b, lo, hi) && atEdge(c, lo, hi) && atEdge(d, lo, hi);
|
||||
}
|
||||
|
||||
private static boolean atEdge(float x, float lo, float hi) {
|
||||
return Math.abs(x - lo) <= UV_EPS || Math.abs(x - hi) <= UV_EPS;
|
||||
}
|
||||
|
||||
private static boolean isAtlasNotBound() {
|
||||
final int tex = GLStateManager.getBoundTextureForServerState(0);
|
||||
if (tex <= 0) return true;
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc == null || mc.getTextureManager() == null) return true;
|
||||
final ITextureObject blocks = mc.getTextureManager().getTexture(TextureMap.locationBlocksTexture);
|
||||
if (blocks != null && blocks.getGlTextureId() == tex) return false;
|
||||
final ITextureObject items = mc.getTextureManager().getTexture(TextureMap.locationItemsTexture);
|
||||
return items == null || items.getGlTextureId() != tex;
|
||||
}
|
||||
|
||||
private static float min4(float a, float b, float c, float d) {
|
||||
return Math.min(Math.min(a, b), Math.min(c, d));
|
||||
}
|
||||
|
||||
private static float max4(float a, float b, float c, float d) {
|
||||
return Math.max(Math.max(a, b), Math.max(c, d));
|
||||
}
|
||||
|
||||
private interface NormalSource extends IrisQuadView {
|
||||
int packedNormal(int i);
|
||||
}
|
||||
|
||||
private static final class QuadView implements NormalSource {
|
||||
private int[] raw;
|
||||
private int base;
|
||||
private int normalIndex;
|
||||
|
||||
void setup(int[] raw, int firstVertex, int normalIndex) {
|
||||
this.raw = raw;
|
||||
this.base = firstVertex;
|
||||
this.normalIndex = normalIndex;
|
||||
}
|
||||
|
||||
@Override public float x(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + X]); }
|
||||
@Override public float y(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + Y]); }
|
||||
@Override public float z(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + Z]); }
|
||||
@Override public float u(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + U]); }
|
||||
@Override public float v(int i) { return Float.intBitsToFloat(raw[(base + i) * VERTEX_SIZE + V]); }
|
||||
@Override public int packedNormal(int i) { return raw[(base + i) * VERTEX_SIZE + normalIndex]; }
|
||||
}
|
||||
|
||||
private static final class PackedQuadView implements NormalSource {
|
||||
private long base;
|
||||
private int stride;
|
||||
private int posOffset;
|
||||
private int texOffset;
|
||||
private int normalOffset;
|
||||
|
||||
void setup(long base, int stride, int posOffset, int texOffset, int normalOffset) {
|
||||
this.base = base;
|
||||
this.stride = stride;
|
||||
this.posOffset = posOffset;
|
||||
this.texOffset = texOffset;
|
||||
this.normalOffset = normalOffset;
|
||||
}
|
||||
|
||||
@Override public float x(int i) { return memGetFloat(base + (long) i * stride + posOffset); }
|
||||
@Override public float y(int i) { return memGetFloat(base + (long) i * stride + posOffset + 4); }
|
||||
@Override public float z(int i) { return memGetFloat(base + (long) i * stride + posOffset + 8); }
|
||||
@Override public float u(int i) { return memGetFloat(base + (long) i * stride + texOffset); }
|
||||
@Override public float v(int i) { return memGetFloat(base + (long) i * stride + texOffset + 4); }
|
||||
@Override public int packedNormal(int i) { return memGetInt(base + (long) i * stride + normalOffset); }
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@ import com.gtnewhorizons.angelica.glsm.hooks.ShaderTransformPostProcessor;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ShaderWorkSubmitter;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
|
||||
import net.coderbot.iris.Iris;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -38,7 +41,20 @@ public class IrisGLSMBridge {
|
||||
private static Runnable fogDensityListener = null;
|
||||
private static Runnable colorModulatorListener = null;
|
||||
|
||||
private static boolean inputsDeferred = false;
|
||||
|
||||
private static boolean blendDeferred = false;
|
||||
|
||||
private static boolean programRestoreDeferred = false;
|
||||
|
||||
private static final Int2IntOpenHashMap programLastUpdatedFrame = new Int2IntOpenHashMap();
|
||||
|
||||
private static void refreshBlendCondition() {
|
||||
if (Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline drp) {
|
||||
drp.onVanillaBlendChanged();
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
programLastUpdatedFrame.defaultReturnValue(-1);
|
||||
StateUpdateNotifiers.alphaFuncNotifier = listener -> alphaFuncListener = listener;
|
||||
@@ -51,6 +67,10 @@ public class IrisGLSMBridge {
|
||||
StateUpdateNotifiers.colorModulatorNotifier = listener -> colorModulatorListener = listener;
|
||||
}
|
||||
|
||||
public static void installImmediateExtendedHandler() {
|
||||
GLSMHooks.immediateExtendedHandler = new ImmediateExtendedAttribs();
|
||||
}
|
||||
|
||||
public static void installPostTransformHook() {
|
||||
if (GLSMHooks.postTransformProcessor != null) return;
|
||||
GLSMHooks.postTransformProcessor = new ShaderTransformPostProcessor() {
|
||||
@@ -72,6 +92,44 @@ public class IrisGLSMBridge {
|
||||
};
|
||||
}
|
||||
|
||||
private static VanillaBooleanLayer gated(VanillaBooleanLayer layer) {
|
||||
return new VanillaBooleanLayer() {
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return Iris.enabled && layer.isOverrideHeld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getVanilla() {
|
||||
return layer.getVanilla();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVanilla(boolean enabled) {
|
||||
layer.setVanilla(enabled);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static <T> VanillaStateLayer<T> gated(VanillaStateLayer<T> layer) {
|
||||
return new VanillaStateLayer<>() {
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return Iris.enabled && layer.isOverrideHeld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readVanilla(T into) {
|
||||
layer.readVanilla(into);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeVanilla(T from) {
|
||||
layer.writeVanilla(from);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
GLSMConfig.expandVertexFormats = Iris.enabled;
|
||||
IrisSamplers.initRenderer();
|
||||
@@ -88,6 +146,11 @@ public class IrisGLSMBridge {
|
||||
return Iris.enabled && BlendModeStorage.isBlendLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return Iris.enabled && BlendModeStorage.isOverrideHeld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deferBlendModeToggle(boolean enabled) {
|
||||
BlendModeStorage.deferBlendModeToggle(enabled);
|
||||
@@ -104,6 +167,13 @@ public class IrisGLSMBridge {
|
||||
}
|
||||
};
|
||||
|
||||
GLStateManager.getBlendMode().setVanillaLayer(gated(BlendModeStorage.ENABLE_LAYER));
|
||||
GLStateManager.getBlendState().setVanillaLayer(gated(BlendModeStorage.FUNC_LAYER));
|
||||
GLStateManager.getAlphaTest().setVanillaLayer(gated(AlphaTestStorage.ENABLE_LAYER));
|
||||
GLStateManager.getAlphaState().setVanillaLayer(gated(AlphaTestStorage.FUNC_LAYER));
|
||||
GLStateManager.getDepthState().setVanillaLayer(gated(DepthColorStorage.DEPTH_LAYER));
|
||||
GLStateManager.getColorMask().setVanillaLayer(gated(DepthColorStorage.COLOR_LAYER));
|
||||
|
||||
GLSMHooks.alphaHandler = new DeferredAlphaHandler() {
|
||||
@Override
|
||||
public boolean isAlphaTestLocked() {
|
||||
@@ -127,6 +197,11 @@ public class IrisGLSMBridge {
|
||||
return Iris.enabled && DepthColorStorage.isDepthColorLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverrideHeld() {
|
||||
return Iris.enabled && DepthColorStorage.isOverrideHeld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deferDepthEnable(boolean enabled) {
|
||||
DepthColorStorage.deferDepthEnable(enabled);
|
||||
@@ -190,11 +265,51 @@ public class IrisGLSMBridge {
|
||||
StateTracker.INSTANCE.lightmapSampler = event.enabled;
|
||||
updatePipeline = true;
|
||||
}
|
||||
if (updatePipeline) {
|
||||
if (!updatePipeline) return;
|
||||
|
||||
if (GLStateManager.isForeignDraw()) {
|
||||
inputsDeferred = true;
|
||||
return;
|
||||
}
|
||||
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setInputs(StateTracker.INSTANCE.getInputs()));
|
||||
});
|
||||
|
||||
GLSMHooks.VANILLA_BLEND_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (GLStateManager.isForeignDraw()) {
|
||||
blendDeferred = true;
|
||||
return;
|
||||
}
|
||||
refreshBlendCondition();
|
||||
});
|
||||
|
||||
GLSMHooks.FOREIGN_DRAW_END.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (inputsDeferred) {
|
||||
inputsDeferred = false;
|
||||
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setInputs(StateTracker.INSTANCE.getInputs()));
|
||||
}
|
||||
if (blendDeferred) {
|
||||
blendDeferred = false;
|
||||
refreshBlendCondition();
|
||||
}
|
||||
if (programRestoreDeferred) {
|
||||
programRestoreDeferred = false;
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline instanceof DeferredWorldRenderingPipeline drp && drp.shouldOverrideShaders()) {
|
||||
drp.restorePassAfterModProgram(GLStateManager.getActiveProgram());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (Iris.enabled && event.postBind) {
|
||||
ImmediateExtendedAttribs.onProgramBound(event.newProgram);
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_DELETE.addListener(event -> ImmediateExtendedAttribs.onProgramDeleted(event.program));
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (event.postBind) return;
|
||||
@@ -208,15 +323,29 @@ public class IrisGLSMBridge {
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
|
||||
if (!drp.shouldOverrideShaders()) return;
|
||||
if (drp.getActivePassProgramId() == -1) return;
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
|
||||
if (DepthColorStorage.isOwnedProgram(event.newProgram)) {
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
} else {
|
||||
if (event.newProgram != 0 && !DepthColorStorage.isOwnedProgram(event.newProgram)) {
|
||||
drp.onModProgramOverride();
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (!event.postBind) return;
|
||||
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
|
||||
if (!drp.shouldOverrideShaders()) return;
|
||||
|
||||
if (GLStateManager.isForeignDraw()) {
|
||||
programRestoreDeferred = true;
|
||||
return;
|
||||
}
|
||||
|
||||
drp.restorePassAfterModProgram(event.newProgram);
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (!event.postBind) return;
|
||||
|
||||
@@ -159,6 +159,7 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.enableTESRChestCache)
|
||||
.addClientMixins(
|
||||
"angelica.tesr.MixinTileEntityChestRenderer",
|
||||
"angelica.tesr.MixinTileEntityChestRenderer_BindTexture",
|
||||
"angelica.tesr.MixinTileEntityEnderChestRenderer"
|
||||
)
|
||||
),
|
||||
@@ -395,6 +396,12 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
CELERITAS_CUBIC_CHUNKS(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addRequiredMod(TargetedMod.CUBIC_CHUNKS)
|
||||
.addClientMixins("celeritas.terrain.MixinCubicChunksCube")
|
||||
),
|
||||
|
||||
CELERITAS_COLORED_LIGHT(new MixinBuilder("Colored light infrastructure for celeritas light pipeline")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> {
|
||||
@@ -417,11 +424,13 @@ public enum Mixins implements IMixins {
|
||||
.addClientMixins(
|
||||
"shaders.MixinDroppedItemGlintEdges"
|
||||
, "shaders.MixinHeldItemGlintEdges"
|
||||
, "shaders.MixinEffectRenderer_Particles"
|
||||
, "shaders.MixinEntityPickupFX"
|
||||
, "shaders.MixinEntityRenderer"
|
||||
, "shaders.MixinGuiIngameForge"
|
||||
, "shaders.MixinFramebuffer"
|
||||
, "shaders.MixinItem"
|
||||
, "shaders.MixinItemRenderer_ItemId"
|
||||
, "shaders.MixinLocale"
|
||||
, "shaders.MixinRender"
|
||||
, "shaders.MixinRenderBiped"
|
||||
@@ -744,6 +753,24 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins("client.etfuturum.MixinLayerBetterElytra")
|
||||
),
|
||||
ET_FUTURUM_BEACON_BEAM(new MixinBuilder("Iris Et Futurum Beacon Beam")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.ET_FUTURUM_REQUIEM)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins("client.etfuturum.MixinTileEntityNewBeaconRenderer")
|
||||
),
|
||||
OPENBLOCKS_TROPHY_ENTITY_GBUFFER(new MixinBuilder("Render OpenBlocks trophies with the entity gbuffer programs")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.OPENBLOCKS)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins("client.openblocks.MixinTileEntityTrophyRenderer")
|
||||
),
|
||||
DRAGONAPI_PARTICLE_SPLIT(new MixinBuilder("Split DragonAPI's replacement particle renderer between the particle gbuffer programs")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.DRAGON_API)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins("client.dragonapi.MixinThrottleableEffectRenderer")
|
||||
),
|
||||
MCPATCHER_FORGE(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures)
|
||||
|
||||
@@ -12,6 +12,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
CAMPFIRE_BACKPORT(null, "campfirebackport"),
|
||||
CHISEL(null, "chisel"),
|
||||
COFHCORE( "cofh.asm.LoadingPlugin", "CoFHCore"),
|
||||
CUBIC_CHUNKS("com.cardinalstar.cubicchunks.asm.coremod.CubicChunksCoreMod", "cubicchunks"),
|
||||
DYNAMIC_SURROUNDINGS_MIST("org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround"),
|
||||
DYNAMIC_SURROUNDINGS_ORIGINAL("org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround"),
|
||||
DRAGON_API("Reika.DragonAPI.Auxiliary.DragonAPIASMHandler", "DragonAPI"),
|
||||
@@ -23,6 +24,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
NTM_SPACE("com.hbm.dim.SolarSystem"),
|
||||
IC2("ic2.core.coremod.IC2core", "IC2"),
|
||||
NATURA(null, "Natura"),
|
||||
OPENBLOCKS(null, "OpenBlocks"),
|
||||
SECURITYCRAFT(null, "securitycraft"),
|
||||
STARMINER("jp.mc.ancientred.starminer.core.SMPlugin", "modJ_StarMiner"),
|
||||
SUPERNOVA("com.mitchej123.supernova.core.SupernovaCore", "supernova"),
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
public interface ItemRendererAccessor {
|
||||
|
||||
ItemStack angelica$getItemToRender();
|
||||
}
|
||||
@@ -97,6 +97,12 @@ import net.coderbot.iris.client.IrisDebugScreenHandler;
|
||||
|
||||
public final class ClientProxy extends CommonProxy {
|
||||
|
||||
/**
|
||||
* So you don't need to be in a dev env to have access to the commands.
|
||||
* Do not commit with this set to true.
|
||||
*/
|
||||
private static final boolean FORCE_DEBUG_MODE = false;
|
||||
|
||||
public static BlockError blockError;
|
||||
public static final ManagedEnum<AnimationMode> animationsMode = new ManagedEnum<>(AnimationMode.VISIBLE_ONLY);
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
@@ -136,6 +142,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
ModelRegistry.registerModid(MOD_ID);
|
||||
blockError = new BlockError();
|
||||
if (AngelicaConfig.enableIris) {
|
||||
IrisGLSMBridge.installImmediateExtendedHandler();
|
||||
Iris.warmupShaderTransforms();
|
||||
}
|
||||
}
|
||||
@@ -190,7 +197,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
}
|
||||
|
||||
// Debug tooling
|
||||
if (!AngelicaClientTweaker.isObfEnv()) {
|
||||
if (FORCE_DEBUG_MODE || !AngelicaClientTweaker.isObfEnv()) {
|
||||
ClientCommandHandler.instance.registerCommand(new AngelicaCommand());
|
||||
|
||||
FMLCommonHandler.instance().bus().register(FlybyRunner.INSTANCE);
|
||||
|
||||
@@ -73,6 +73,9 @@ public final class SelectionBoxRenderer {
|
||||
public static void draw(AxisAlignedBB aabb, int color) {
|
||||
if (program == null || vao == null) return;
|
||||
|
||||
GLStateManager.enableDepthTest();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
|
||||
final float minX = (float) aabb.minX, minY = (float) aabb.minY, minZ = (float) aabb.minZ;
|
||||
final float extX = (float) (aabb.maxX - aabb.minX);
|
||||
final float extY = (float) (aabb.maxY - aabb.minY);
|
||||
|
||||
@@ -123,11 +123,11 @@ public class DeferredEntityOverlay {
|
||||
GbufferPrograms.beginEntities();
|
||||
try {
|
||||
for (DeferredEntry entry : deferred) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(EntityIdHelper.getEntityId(entry.entity));
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(EntityIdHelper.getEntityId(entry.entity), 0);
|
||||
renderOverlay(entry);
|
||||
}
|
||||
} finally {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(-1);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(-1, 0);
|
||||
GbufferPrograms.endEntities();
|
||||
}
|
||||
recycle();
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.rendering.DeferredDrawBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.SpriteExtension;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.minecraft.client.particle.EntityCloudFX;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.client.particle.EntityFireworkOverlayFX;
|
||||
import net.minecraft.client.particle.EntityFireworkSparkFX;
|
||||
import net.minecraft.client.particle.EntityFireworkStarterFX;
|
||||
import net.minecraft.client.particle.EntitySpellParticleFX;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevel;
|
||||
|
||||
/**
|
||||
* Splits a particle run between {@code gbuffers_particles} and {@code gbuffers_particles_translucent}.
|
||||
*/
|
||||
public final class ParticleRunSplitter {
|
||||
|
||||
private static boolean runTranslucent;
|
||||
|
||||
private ParticleRunSplitter() {}
|
||||
|
||||
public static void beginRun() {
|
||||
runTranslucent = false;
|
||||
GbufferPrograms.setTranslucencyDeclaration(Boolean.FALSE);
|
||||
}
|
||||
|
||||
public static void splitIfNeeded(EntityFX particle, Tessellator tessellator, float partialTicks) {
|
||||
final boolean translucent = isTranslucent(particle);
|
||||
if (translucent == runTranslucent) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean batcherWasActive = DeferredDrawBatcher.isActive();
|
||||
if (batcherWasActive) {
|
||||
DeferredDrawBatcher.exitAndFlush();
|
||||
}
|
||||
tessellator.draw();
|
||||
|
||||
runTranslucent = translucent;
|
||||
GbufferPrograms.setTranslucencyDeclaration(translucent);
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setBrightness(particle.getBrightnessForRender(partialTicks));
|
||||
if (batcherWasActive) {
|
||||
DeferredDrawBatcher.enter();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isTranslucent(EntityFX particle) {
|
||||
if (particle.particleIcon instanceof TextureAtlasSprite sprite) {
|
||||
return ((SpriteExtension) sprite).celeritas$getTransparencyLevel() == SpriteTransparencyLevel.TRANSLUCENT;
|
||||
}
|
||||
|
||||
return particle instanceof EntityFireworkSparkFX
|
||||
|| particle instanceof EntityFireworkOverlayFX
|
||||
|| particle instanceof EntityFireworkStarterFX
|
||||
|| particle instanceof EntitySpellParticleFX
|
||||
|| particle instanceof EntityCloudFX;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@ public final class PlayerReflectionCapture {
|
||||
private static final Matrix4f pendingModelView = new Matrix4f();
|
||||
private static int pendingSkin;
|
||||
private static int pendingEntityId;
|
||||
private static int pendingItemId;
|
||||
|
||||
private static int vbo;
|
||||
private static int vao;
|
||||
@@ -134,6 +135,7 @@ public final class PlayerReflectionCapture {
|
||||
pendingModelView.set(GLStateManager.getModelViewMatrix());
|
||||
pendingSkin = GLStateManager.getBoundTextureForServerState(0);
|
||||
pendingEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
|
||||
pendingItemId = CapturedRenderingState.INSTANCE.getCurrentRenderedItem();
|
||||
pending = true;
|
||||
}
|
||||
|
||||
@@ -328,9 +330,10 @@ public final class PlayerReflectionCapture {
|
||||
final boolean prevDepthWrite = GLStateManager.getDepthState().isEnabled();
|
||||
|
||||
final int prevEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
|
||||
final int prevItemId = CapturedRenderingState.INSTANCE.getCurrentRenderedItem();
|
||||
savedModelView.set(GLStateManager.getModelViewMatrix());
|
||||
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(pendingEntityId);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(pendingEntityId, pendingItemId);
|
||||
GLStateManager.setModelViewMatrix(pendingModelView);
|
||||
if (maskable) {
|
||||
GLStateManager.glColorMask(false, false, false, false);
|
||||
@@ -344,7 +347,7 @@ public final class PlayerReflectionCapture {
|
||||
GLStateManager.glColorMask(pr, pg, pb, pa);
|
||||
}
|
||||
GLStateManager.setModelViewMatrix(savedModelView);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(prevEntityId);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(prevEntityId, prevItemId);
|
||||
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, prevTexture);
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
/// Despite not in the `api` package, this interface is used by GT5uNH and GTNHLib, possibly others. Be careful what you
|
||||
/// change!
|
||||
@SuppressWarnings("unused")
|
||||
public interface StateAwareTessellator {
|
||||
/**
|
||||
* True if the vertex originated from a RenderBlocks method call with the enableAO flag set.
|
||||
*/
|
||||
|
||||
/// True if the vertex originated from a RenderBlocks method call with the enableAO flag set.
|
||||
int RENDERED_WITH_VANILLA_AO = 0x1;
|
||||
int NO_DIRECTIONAL_SHADING = 0x2;
|
||||
|
||||
void angelica$setAppliedAo(boolean flag);
|
||||
void angelica$setNoDirectionalShading(boolean flag);
|
||||
|
||||
/**
|
||||
* Sets whether we're doing terrain meshing as part of celeritas -- collects additional information.
|
||||
*
|
||||
* Enables per-vertex AO state collection into {@link #angelica$getVertexStates()}.
|
||||
*/
|
||||
/// Sets whether we're doing terrain meshing as part of celeritas -- collects additional information.
|
||||
///
|
||||
/// Enables per-vertex AO state collection into [#angelica$getVertexStates()].
|
||||
void angelica$setCeleritasMeshing(boolean active);
|
||||
|
||||
int[] angelica$getVertexStates();
|
||||
|
||||
+49
-10
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import static com.gtnewhorizons.angelica.rendering.StateAwareTessellator.NO_DIRECTIONAL_SHADING;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
|
||||
import com.gtnewhorizons.angelica.api.TintComputer;
|
||||
import com.gtnewhorizons.angelica.api.TintRegistry;
|
||||
@@ -28,6 +30,7 @@ import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
|
||||
import org.embeddedt.embeddium.impl.model.light.flat.FlatLightPipeline;
|
||||
import org.embeddedt.embeddium.impl.model.light.smooth.SmoothLightPipeline;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.ChunkColorWriter;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderPassConfiguration;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildBuffers;
|
||||
@@ -43,6 +46,9 @@ import java.util.List;
|
||||
public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
public static final int NUM_PASSES = 2;
|
||||
|
||||
// Yoinked from Celeritas
|
||||
private static final int[] REVERSED_QUAD_ORDER = { 0, 3, 2, 1 };
|
||||
|
||||
private final TextureMapExtension textureAtlas;
|
||||
private final ChunkVertexEncoder.Vertex[] vertices = ChunkVertexEncoder.Vertex.uninitializedQuad();
|
||||
@Getter
|
||||
@@ -62,6 +68,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
private boolean lightPipelineReady = false;
|
||||
private int originX, originY, originZ;
|
||||
private float cachedSkylightSubtracted;
|
||||
private final float[] fluidQuadPos = new float[12];
|
||||
private ModelQuadFacing fluidQuadLightFace;
|
||||
|
||||
public AngelicaChunkBuildContext(RenderPassConfiguration<?> renderPassConfiguration, WorldClient world) {
|
||||
super(renderPassConfiguration);
|
||||
@@ -160,6 +168,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
boolean hasCachedMaterial = false;
|
||||
TextureAtlasSprite lastSprite = null;
|
||||
Material lastMaterial = null;
|
||||
this.fluidQuadLightFace = null;
|
||||
|
||||
for (int quadIdx = 0; quadIdx < numQuads; quadIdx++) {
|
||||
float uSum = 0, vSum = 0;
|
||||
@@ -201,11 +210,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
// Otherwise, we need to use the block's lightmap values as they are as the ISBRH may have
|
||||
// specified its own lightmaps for visual purposes (e.g. fullbright)
|
||||
if (useAoCalculation && (quadState & StateAwareTessellator.RENDERED_WITH_VANILLA_AO) != 0) {
|
||||
quadView.setup(trueNormal, blockX, blockY, blockZ);
|
||||
final ModelQuadFacing lightFace = quadView.getLightFace();
|
||||
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
|
||||
final ModelQuadFacing cullFace = quadView.getCullFace();
|
||||
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
|
||||
calculateQuadLight(trueNormal, blockX, blockY, blockZ, worldX, worldY, worldZ, isFluid,
|
||||
blockAllowsSmoothLighting, shouldApplyDiffuse && (quadState & NO_DIRECTIONAL_SHADING) == 0);
|
||||
|
||||
for (int vIdx = 0; vIdx < 4; vIdx++) {
|
||||
final var vertex = vertices[vIdx];
|
||||
@@ -227,11 +233,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
}
|
||||
|
||||
if (useAoCalculation && isFluid) {
|
||||
quadView.setup(trueNormal, blockX, blockY, blockZ);
|
||||
final ModelQuadFacing lightFace = quadView.getLightFace();
|
||||
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
|
||||
final ModelQuadFacing cullFace = quadView.getCullFace();
|
||||
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
|
||||
calculateQuadLight(trueNormal, blockX, blockY, blockZ, worldX, worldY, worldZ, true,
|
||||
blockAllowsSmoothLighting, shouldApplyDiffuse);
|
||||
for (int vIdx = 0; vIdx < 4; vIdx++) {
|
||||
vertices[vIdx].light = quadLightData.lm[vIdx];
|
||||
}
|
||||
@@ -274,6 +277,42 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateQuadLight(int trueNormal, int blockX, int blockY, int blockZ, int worldX, int worldY,
|
||||
int worldZ, boolean isFluid, boolean blockAllowsSmoothLighting,
|
||||
boolean shouldApplyDiffuse) {
|
||||
final boolean isFlippedFluidCopy = isFluid && fluidQuadLightFace != null && isReversedFluidQuad();
|
||||
|
||||
quadView.setup(trueNormal, blockX, blockY, blockZ, isFluid ? ModelQuadFlags.IS_VANILLA_SHADED : 0,
|
||||
isFlippedFluidCopy ? fluidQuadLightFace : null);
|
||||
|
||||
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
|
||||
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, quadView.getCullFace(),
|
||||
quadView.getLightFace(), shouldApplyDiffuse, true);
|
||||
|
||||
if (isFluid && !isFlippedFluidCopy) {
|
||||
fluidQuadLightFace = quadView.getLightFace();
|
||||
for (int vIdx = 0; vIdx < 4; vIdx++) {
|
||||
final var vertex = vertices[vIdx];
|
||||
fluidQuadPos[vIdx * 3] = vertex.x;
|
||||
fluidQuadPos[vIdx * 3 + 1] = vertex.y;
|
||||
fluidQuadPos[vIdx * 3 + 2] = vertex.z;
|
||||
}
|
||||
} else if (!isFluid) {
|
||||
fluidQuadLightFace = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReversedFluidQuad() {
|
||||
for (int vIdx = 0; vIdx < 4; vIdx++) {
|
||||
final int src = REVERSED_QUAD_ORDER[vIdx] * 3;
|
||||
final var vertex = vertices[vIdx];
|
||||
if (vertex.x != fluidQuadPos[src] || vertex.y != fluidQuadPos[src + 1] || vertex.z != fluidQuadPos[src + 2]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void applyBlockLightTint(int x, int y, int z, ChunkVertexEncoder.Vertex[] vertices) {
|
||||
if (!hasColoredLight) return;
|
||||
final ExtQuadLightData ext = (ExtQuadLightData) quadLightData;
|
||||
|
||||
+10
@@ -11,6 +11,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHold
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.BlockRenderContext;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.ContextAwareChunkVertexEncoder;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.utils.NaturalTextureUtils;
|
||||
import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
@@ -361,8 +363,16 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
setRenderPass(pass);
|
||||
// Trigger side effects from canRenderInPass (some ISBRHs like BuildCraft set global state in this method that gets read elsewhere renderWorldBlock)
|
||||
block.canRenderInPass(pass);
|
||||
|
||||
// Some blocks get randomly rotated top faces
|
||||
final boolean applyNaturalTex = AngelicaConfig.enableNaturalTextures && NaturalTextureUtils.isNaturalBlock(block);
|
||||
if (applyNaturalTex) { renderBlocks.uvRotateTop = NaturalTextureUtils.getTopRotation(x, y, z); }
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
renderBlocks.renderBlockByRenderType(block, x, y, z);
|
||||
|
||||
if (applyNaturalTex) { renderBlocks.uvRotateTop = 0; } // Reset
|
||||
|
||||
boolean blockAllowsSmoothLighting = Minecraft.isAmbientOcclusionEnabled() // smooth lighting on
|
||||
&& block.getLightValue() == 0; // does not emit real block light
|
||||
buildContext.copyRawBuffer(tessellator.rawBuffer, tessellator.vertexCount,
|
||||
|
||||
+6
@@ -6,6 +6,8 @@ import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.bop.BopFogBlend;
|
||||
import com.gtnewhorizons.angelica.compat.bop.FogBiomeCache;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
@@ -43,6 +45,7 @@ import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.OverridesShouldRenderInPass;
|
||||
@@ -166,6 +169,9 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
TesrBatchRenderer.INSTANCE.clearRetained();
|
||||
ModelPartBatcher.INSTANCE.clear();
|
||||
GpuCulling.onWorldUnload();
|
||||
FfpExtendedAttribs.reset();
|
||||
BopFogBlend.invalidate();
|
||||
FogBiomeCache.invalidate();
|
||||
super.unloadWorld();
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -13,18 +13,24 @@ public class VertexArrayQuadView implements ModelQuadView {
|
||||
private int blockX, blockY, blockZ;
|
||||
private ModelQuadFacing lightFace;
|
||||
private int flags;
|
||||
private int extraFlags;
|
||||
|
||||
public VertexArrayQuadView(ChunkVertexEncoder.Vertex[] vertices) {
|
||||
this.vertices = vertices;
|
||||
}
|
||||
|
||||
public void setup(int trueNormal, int blockX, int blockY, int blockZ) {
|
||||
setup(trueNormal, blockX, blockY, blockZ, 0, null);
|
||||
}
|
||||
|
||||
public void setup(int trueNormal, int blockX, int blockY, int blockZ, int extraFlags, ModelQuadFacing forcedLightFace) {
|
||||
this.trueNormal = trueNormal;
|
||||
this.blockX = blockX;
|
||||
this.blockY = blockY;
|
||||
this.blockZ = blockZ;
|
||||
this.lightFace = null; // Reset cached lightFace
|
||||
this.lightFace = forcedLightFace;
|
||||
this.flags = 0;
|
||||
this.extraFlags = extraFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,7 +71,7 @@ public class VertexArrayQuadView implements ModelQuadView {
|
||||
@Override
|
||||
public int getFlags() {
|
||||
if (flags == 0) {
|
||||
flags = ModelQuadFlags.getQuadFlags(this, getLightFace());
|
||||
flags = ModelQuadFlags.getQuadFlags(this, getLightFace(), extraFlags);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
+12
-1
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas.light;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
@@ -9,6 +10,16 @@ import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
|
||||
public class LightDataCache extends LightDataAccess {
|
||||
private IBlockAccess world;
|
||||
|
||||
/**
|
||||
* Doing this to force packs like Steadfast's water to work properly.
|
||||
*/
|
||||
private static int skyLight(IBlockAccess world, Block block, int x, int y, int z, int neighborMaxed) {
|
||||
if (world instanceof WorldSlice slice && block.getMaterial().isLiquid()) {
|
||||
return slice.getRawSkyLight(x, y, z);
|
||||
}
|
||||
return neighborMaxed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int compute(int x, int y, int z) {
|
||||
final IBlockAccess world = this.world;
|
||||
@@ -26,7 +37,7 @@ public class LightDataCache extends LightDataAccess {
|
||||
} else {
|
||||
final int packed = world.getLightBrightnessForSkyBlocks(x, y, z, 0);
|
||||
bl = (packed >> 4) & 0xF;
|
||||
sl = (packed >> 20) & 0xF;
|
||||
sl = skyLight(world, block, x, y, z, (packed >> 20) & 0xF);
|
||||
}
|
||||
|
||||
// AO value - blocks that emit light don't contribute to AO
|
||||
|
||||
@@ -276,6 +276,17 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
return skyBrightness << 20 | blockBrightness << 4;
|
||||
}
|
||||
|
||||
public int getRawSkyLight(int x, int y, int z) {
|
||||
final int minHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMinHeight(this.world) : 0;
|
||||
final int maxHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMaxHeight(this.world) : 256;
|
||||
|
||||
if (y < minHeight || y >= maxHeight || x < -30_000_000 || z < -30_000_000 || x >= 30_000_000 || z >= 30_000_000) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
return this.getLightLevel(EnumSkyBlock.Sky, x, y, z);
|
||||
}
|
||||
|
||||
private int getSkyBlockTypeBrightness(EnumSkyBlock skyBlock, int x, int y, int z) {
|
||||
if (this.getBlock(x, y, z).getUseNeighborBrightness()) {
|
||||
int yp = this.getLightLevel(skyBlock, x, y + 1, z);
|
||||
|
||||
+77
-6
@@ -31,7 +31,11 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VAOManager;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import lombok.Data;
|
||||
@@ -39,6 +43,13 @@ import lombok.NoArgsConstructor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
public class ItemRenderListManager {
|
||||
// Least used element is at position 0. This is in theory slightly faster.
|
||||
@@ -102,18 +113,21 @@ public class ItemRenderListManager {
|
||||
public static void registerReloadListener(){
|
||||
IReloadableResourceManager resourceManager = (IReloadableResourceManager) Minecraft.getMinecraft()
|
||||
.getResourceManager();
|
||||
resourceManager.registerReloadListener(_ -> {
|
||||
for (CachedVBO value : vboCache.values()) {
|
||||
value.delete();
|
||||
}
|
||||
vboCache.clear();
|
||||
});
|
||||
resourceManager.registerReloadListener(_ -> clearCache());
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
for (CachedVBO value : vboCache.values()) {
|
||||
value.delete();
|
||||
}
|
||||
vboCache.clear();
|
||||
}
|
||||
|
||||
public static final class CachedVBO {
|
||||
private final IVertexArrayObject vertexBuffer;
|
||||
private final IndexBuffer ebo;
|
||||
private int expiry;
|
||||
private int extVbo = 0;
|
||||
|
||||
public CachedVBO() {
|
||||
this.ebo = new IndexBuffer();
|
||||
@@ -126,6 +140,59 @@ public class ItemRenderListManager {
|
||||
|
||||
private void allocate(DirectTessellator tessellator) {
|
||||
tessellator.allocateToVBO(vertexBuffer, ebo);
|
||||
attachExtAttribs(tessellator);
|
||||
}
|
||||
|
||||
private void attachExtAttribs(DirectTessellator tessellator) {
|
||||
final ImmediateExtendedAttribHandler handler = GLSMHooks.immediateExtendedHandler;
|
||||
final VertexFormat format = tessellator.getVertexFormat();
|
||||
final int vertexCount = tessellator.getVertexCount();
|
||||
final int extPrim = handler == null ? 0 : ImmediateExtendedAttribHandler.extPrimVerts(tessellator.getDrawMode(), vertexCount);
|
||||
final boolean capture = handler != null && format != null && format.hasTexture()
|
||||
&& extPrim != 0
|
||||
&& (extVbo != 0 || handler.wantsExtendedCapture());
|
||||
if (!capture) {
|
||||
detachExtAttribs();
|
||||
return;
|
||||
}
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int stride = format.getVertexSize();
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
final ByteBuffer packed = tessellator.getWriteBuffer();
|
||||
final long srcBase = memAddress0(packed) + packed.position();
|
||||
|
||||
final ByteBuffer ext = memCalloc(vertexCount, extStride);
|
||||
handler.buildPacked(srcBase, stride, 0, texOffset, normalOffset, vertexCount, extPrim, memAddress0(ext), extStride);
|
||||
|
||||
final boolean firstAttach = extVbo == 0;
|
||||
if (firstAttach) {
|
||||
extVbo = GLStateManager.glGenBuffers();
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(vertexCount * extStride);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, ext, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
memFree(ext);
|
||||
|
||||
if (firstAttach) {
|
||||
vertexBuffer.bind();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, extStride);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
vertexBuffer.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
private void detachExtAttribs() {
|
||||
if (extVbo == 0) return;
|
||||
vertexBuffer.bind();
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_MID_TEX);
|
||||
GLStateManager.glDisableVertexAttribArray(ImmediateExtendedAttribHandler.LOC_TANGENT);
|
||||
vertexBuffer.unbind();
|
||||
GLStateManager.glDeleteBuffers(extVbo);
|
||||
extVbo = 0;
|
||||
}
|
||||
|
||||
private void render(int elapsedTicks) {
|
||||
@@ -136,6 +203,10 @@ public class ItemRenderListManager {
|
||||
private void delete() {
|
||||
vertexBuffer.delete();
|
||||
// EBO gets deleted by vertexBuffer.delete()
|
||||
if (extVbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(extVbo);
|
||||
extVbo = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
@@ -2,6 +2,9 @@ package com.gtnewhorizons.angelica.rendering.tesr;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.OrphanStreamingBuffer;
|
||||
@@ -17,6 +20,8 @@ import org.lwjgl.opengl.GL15;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memCalloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.COLOR_INDEX;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.NORMAL_INDEX;
|
||||
import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.TEX_X_INDEX;
|
||||
@@ -34,6 +39,7 @@ final class InstancedTemplateRenderer {
|
||||
private static final class TemplateMesh {
|
||||
int vao;
|
||||
int vbo;
|
||||
int extVbo;
|
||||
int vertexCount;
|
||||
int drawMode;
|
||||
long lastUsedMs;
|
||||
@@ -96,6 +102,10 @@ final class InstancedTemplateRenderer {
|
||||
int draws = 0;
|
||||
long recordOffset = ringBase;
|
||||
final int ringId = uploadBufferId;
|
||||
final ImmediateExtendedAttribHandler extHandler = GLSMHooks.immediateExtendedHandler;
|
||||
if (extHandler != null && extHandler.wantsExtended()) {
|
||||
FfpExtendedAttribs.setNeutralCurrentValues();
|
||||
}
|
||||
GLStateManager.instancedFfpDrawActive = true;
|
||||
for (int b = 0, n = liveBuckets.size(); b < n; b++) {
|
||||
final Bucket bucket = liveBuckets.get(b);
|
||||
@@ -179,6 +189,13 @@ final class InstancedTemplateRenderer {
|
||||
GLStateManager.glVertexAttribPointer(4, 3, GL11.GL_BYTE, true, TEMPLATE_STRIDE, NORMAL_INDEX * 4L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
mesh.extVbo = buildExt(template);
|
||||
if (mesh.extVbo != 0) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, mesh.extVbo);
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(0L, ImmediateExtendedAttribHandler.EXT_STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
for (int loc = InstancedAttribs.LOC_MATRIX_COL0; loc <= InstancedAttribs.LOC_LIGHTMAP; loc++) {
|
||||
GLStateManager.glEnableVertexAttribArray(loc);
|
||||
GLStateManager.glVertexAttribDivisor(loc, 1);
|
||||
@@ -188,6 +205,25 @@ final class InstancedTemplateRenderer {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private static int buildExt(TemplateBuffer template) {
|
||||
final ImmediateExtendedAttribHandler handler = GLSMHooks.immediateExtendedHandler;
|
||||
if (handler == null || !handler.wantsExtendedCapture()) return 0;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(template.drawMode, template.vertexCount);
|
||||
if (extPrim == 0) return 0;
|
||||
|
||||
final int extStride = ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final ByteBuffer ext = memCalloc(template.vertexCount, extStride);
|
||||
handler.build(template.data, template.vertexCount, extPrim,
|
||||
ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX, memAddress0(ext), extStride);
|
||||
|
||||
final int extVbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, extVbo);
|
||||
ext.position(0).limit(template.vertexCount * extStride);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, ext, GL15.GL_STATIC_DRAW);
|
||||
memFree(ext);
|
||||
return extVbo;
|
||||
}
|
||||
|
||||
void endFrame() {
|
||||
if (ringUsedThisFrame && persistentRing != null) {
|
||||
persistentRing.postDraw();
|
||||
@@ -215,6 +251,7 @@ final class InstancedTemplateRenderer {
|
||||
|
||||
private static void delete(TemplateMesh mesh) {
|
||||
GLStateManager.glDeleteBuffers(mesh.vbo);
|
||||
if (mesh.extVbo != 0) GLStateManager.glDeleteBuffers(mesh.extVbo);
|
||||
GLStateManager.glDeleteVertexArrays(mesh.vao);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
|
||||
import com.gtnewhorizons.angelica.profiling.BailClassCounts;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
@@ -24,6 +25,7 @@ import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.batchedentityrendering.impl.TransparencyType;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.minecraft.client.model.ModelBox;
|
||||
@@ -105,22 +107,34 @@ public final class ModelPartBatcher {
|
||||
private static final class LayerKey {
|
||||
ResourceLocation texture;
|
||||
TesrMaterial material;
|
||||
PassOverride pass;
|
||||
float offsetFactor;
|
||||
float offsetUnits;
|
||||
|
||||
LayerKey set(ResourceLocation texture, TesrMaterial material) {
|
||||
LayerKey set(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
|
||||
this.texture = texture;
|
||||
this.material = material;
|
||||
this.pass = pass;
|
||||
this.offsetFactor = offsetFactor;
|
||||
this.offsetUnits = offsetUnits;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof LayerKey other)) return false;
|
||||
return Objects.equals(texture, other.texture) && Objects.equals(material, other.material);
|
||||
return Objects.equals(texture, other.texture) && Objects.equals(material, other.material)
|
||||
&& Objects.equals(pass, other.pass)
|
||||
&& Float.floatToIntBits(offsetFactor) == Float.floatToIntBits(other.offsetFactor)
|
||||
&& Float.floatToIntBits(offsetUnits) == Float.floatToIntBits(other.offsetUnits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(texture) * 31 + Objects.hashCode(material);
|
||||
int h = (Objects.hashCode(texture) * 31 + Objects.hashCode(material)) * 31 + Objects.hashCode(pass);
|
||||
h = h * 31 + Float.floatToIntBits(offsetFactor);
|
||||
h = h * 31 + Float.floatToIntBits(offsetUnits);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +142,9 @@ public final class ModelPartBatcher {
|
||||
private final LayerKey scratchKey = new LayerKey();
|
||||
private ResourceLocation lastLayerTexture;
|
||||
private TesrMaterial lastLayerMaterial;
|
||||
private PassOverride lastLayerPass;
|
||||
private float lastLayerOffsetFactor;
|
||||
private float lastLayerOffsetUnits;
|
||||
private RenderLayer lastLayer;
|
||||
|
||||
private static ResourceLocation lastBoundLocation;
|
||||
@@ -177,7 +194,9 @@ public final class ModelPartBatcher {
|
||||
if (instancedThisCycle) {
|
||||
TesrBatchRenderer.INSTANCE.instancedRenderer.endFrame();
|
||||
}
|
||||
BatchingFontRenderer.flushDeferredText();
|
||||
if (!TesrBatchRenderer.INSTANCE.hasPendingGeometry()) {
|
||||
BatchingFontRenderer.flushDeferredText();
|
||||
}
|
||||
return;
|
||||
}
|
||||
final boolean inEntityLoop = mode == Mode.ENTITIES && Iris.enabled && GbufferPrograms.isEntityLoopActive();
|
||||
@@ -193,6 +212,9 @@ public final class ModelPartBatcher {
|
||||
if (instancedThisCycle) {
|
||||
TesrBatchRenderer.INSTANCE.instancedRenderer.endFrame();
|
||||
}
|
||||
if (!TesrBatchRenderer.INSTANCE.hasPendingGeometry()) {
|
||||
BatchingFontRenderer.flushDeferredText();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
@@ -278,11 +300,19 @@ public final class ModelPartBatcher {
|
||||
}
|
||||
}
|
||||
}
|
||||
final RenderLayer layer = layerFor(texture, material);
|
||||
final int entityId = mode == Mode.ENTITIES ? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity()) : CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
final PassOverride pass = shaderPipeline != null ? PassOverride.capture() : PassOverride.NONE;
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final float offsetFactor = offset ? polygon.getOffsetFactor() : 0.0f;
|
||||
final float offsetUnits = offset ? polygon.getOffsetUnits() : 0.0f;
|
||||
final RenderLayer layer = layerFor(texture, material, pass, offsetFactor, offsetUnits);
|
||||
// Entities nested inside a TESR (mob spawner, OpenBlocks trophy) run in the block entity pass but carry an entity id
|
||||
final int entityId = mode == Mode.ENTITIES || pass.isEntityPhase()
|
||||
? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity())
|
||||
: CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
final Color4 color = GLStateManager.getColor();
|
||||
final int colorABGR = ColorABGR.pack(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
|
||||
final int packedLight = ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
|
||||
final int packedLight = GLSMConfig.packedLastBrightness();
|
||||
final int mvGen = GLStateManager.getMvGeneration();
|
||||
if (!mvCaptured || mvGen != lastMvGeneration) {
|
||||
modelView.set(GLStateManager.getModelViewMatrix());
|
||||
@@ -349,17 +379,21 @@ public final class ModelPartBatcher {
|
||||
return entry;
|
||||
}
|
||||
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material) {
|
||||
if (texture == lastLayerTexture && material == lastLayerMaterial) {
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
|
||||
if (texture == lastLayerTexture && material == lastLayerMaterial && pass.equals(lastLayerPass)
|
||||
&& offsetFactor == lastLayerOffsetFactor && offsetUnits == lastLayerOffsetUnits) {
|
||||
return lastLayer;
|
||||
}
|
||||
RenderLayer layer = layers.get(scratchKey.set(texture, material));
|
||||
RenderLayer layer = layers.get(scratchKey.set(texture, material, pass, offsetFactor, offsetUnits));
|
||||
if (layer == null) {
|
||||
layer = RenderLayer.tesr(texture, material);
|
||||
layers.put(new LayerKey().set(texture, material), layer);
|
||||
layer = RenderLayer.tesr(texture, material, pass, offsetFactor, offsetUnits);
|
||||
layers.put(new LayerKey().set(texture, material, pass, offsetFactor, offsetUnits), layer);
|
||||
}
|
||||
lastLayerTexture = texture;
|
||||
lastLayerMaterial = material;
|
||||
lastLayerPass = pass;
|
||||
lastLayerOffsetFactor = offsetFactor;
|
||||
lastLayerOffsetUnits = offsetUnits;
|
||||
lastLayer = layer;
|
||||
return layer;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
@@ -19,6 +20,7 @@ import net.coderbot.batchedentityrendering.impl.SegmentedBufferBuilder;
|
||||
import net.coderbot.batchedentityrendering.impl.TransparencyType;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPhase;
|
||||
@@ -129,13 +131,23 @@ public final class TesrBatchRenderer {
|
||||
return activePass >= 0;
|
||||
}
|
||||
|
||||
public boolean hasPendingGeometry() {
|
||||
return activePass >= 0 || deferredFlushPending;
|
||||
}
|
||||
|
||||
public void queue(TemplateBuffer template, ResourceLocation texture, TesrMaterial material) {
|
||||
modelView.set(GLStateManager.getModelViewMatrix());
|
||||
final int packedLight = currentPackedLight(material);
|
||||
final int colorABGR = resolveColor(material);
|
||||
if (activePass >= 0) {
|
||||
final RenderLayer layer = layerFor(texture, material);
|
||||
final int blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
final PolygonState polygon = GLStateManager.getPolygonState();
|
||||
final PassOverride pass = PassOverride.capture();
|
||||
final RenderLayer layer = layerFor(texture, material, pass,
|
||||
offset ? polygon.getOffsetFactor() : 0.0f, offset ? polygon.getOffsetUnits() : 0.0f);
|
||||
final int blockEntityId = pass.isEntityPhase()
|
||||
? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity())
|
||||
: CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
|
||||
retained[activePass].queue(template, layer, material, modelView, packedLight, colorABGR, blockEntityId, captureTextureMatrix(), false);
|
||||
} else {
|
||||
drawImmediate(template, texture, material, packedLight, colorABGR);
|
||||
@@ -158,8 +170,12 @@ public final class TesrBatchRenderer {
|
||||
boolean depthEqual;
|
||||
TesrMaterial.SpecialRender special;
|
||||
TesrShader shader;
|
||||
boolean noPass;
|
||||
PassOverride pass;
|
||||
float offsetFactor;
|
||||
float offsetUnits;
|
||||
|
||||
LayerKey set(ResourceLocation texture, TesrMaterial.Transparency transparency, boolean noCull, boolean unlit, boolean noDepthWrite, boolean depthOnly, float cutoutAlpha, boolean depthEqual, TesrMaterial.SpecialRender special, TesrShader shader) {
|
||||
LayerKey set(ResourceLocation texture, TesrMaterial.Transparency transparency, boolean noCull, boolean unlit, boolean noDepthWrite, boolean depthOnly, float cutoutAlpha, boolean depthEqual, TesrMaterial.SpecialRender special, TesrShader shader, boolean noPass, PassOverride pass, float offsetFactor, float offsetUnits) {
|
||||
this.texture = texture;
|
||||
this.transparency = transparency;
|
||||
this.noCull = noCull;
|
||||
@@ -170,11 +186,15 @@ public final class TesrBatchRenderer {
|
||||
this.depthEqual = depthEqual;
|
||||
this.special = special;
|
||||
this.shader = shader;
|
||||
this.noPass = noPass;
|
||||
this.pass = pass;
|
||||
this.offsetFactor = offsetFactor;
|
||||
this.offsetUnits = offsetUnits;
|
||||
return this;
|
||||
}
|
||||
|
||||
LayerKey copy() {
|
||||
return new LayerKey().set(texture, transparency, noCull, unlit, noDepthWrite, depthOnly, cutoutAlpha, depthEqual, special, shader);
|
||||
return new LayerKey().set(texture, transparency, noCull, unlit, noDepthWrite, depthOnly, cutoutAlpha, depthEqual, special, shader, noPass, pass, offsetFactor, offsetUnits);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,7 +204,10 @@ public final class TesrBatchRenderer {
|
||||
&& noCull == other.noCull && unlit == other.unlit && noDepthWrite == other.noDepthWrite
|
||||
&& depthOnly == other.depthOnly
|
||||
&& Float.floatToIntBits(cutoutAlpha) == Float.floatToIntBits(other.cutoutAlpha)
|
||||
&& depthEqual == other.depthEqual && special == other.special && shader == other.shader;
|
||||
&& depthEqual == other.depthEqual && special == other.special && shader == other.shader && noPass == other.noPass
|
||||
&& Objects.equals(pass, other.pass)
|
||||
&& Float.floatToIntBits(offsetFactor) == Float.floatToIntBits(other.offsetFactor)
|
||||
&& Float.floatToIntBits(offsetUnits) == Float.floatToIntBits(other.offsetUnits);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,6 +222,10 @@ public final class TesrBatchRenderer {
|
||||
h = h * 31 + (depthEqual ? 1 : 0);
|
||||
h = h * 31 + Objects.hashCode(special);
|
||||
h = h * 31 + System.identityHashCode(shader);
|
||||
h = h * 31 + (noPass ? 1 : 0);
|
||||
h = h * 31 + Objects.hashCode(pass);
|
||||
h = h * 31 + Float.floatToIntBits(offsetFactor);
|
||||
h = h * 31 + Float.floatToIntBits(offsetUnits);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
@@ -207,24 +234,45 @@ public final class TesrBatchRenderer {
|
||||
private final LayerKey scratchKey = new LayerKey();
|
||||
private ResourceLocation lastLayerTexture;
|
||||
private TesrMaterial lastLayerMaterial;
|
||||
private PassOverride lastLayerPass;
|
||||
private float lastLayerOffsetFactor;
|
||||
private float lastLayerOffsetUnits;
|
||||
private RenderLayer lastLayer;
|
||||
private ResourceLocation lastImmediateTexture;
|
||||
private TesrMaterial lastImmediateMaterial;
|
||||
private RenderLayer lastImmediateLayer;
|
||||
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material) {
|
||||
if (texture == lastLayerTexture && material == lastLayerMaterial) {
|
||||
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
|
||||
if (texture == lastLayerTexture && material == lastLayerMaterial && pass.equals(lastLayerPass)
|
||||
&& offsetFactor == lastLayerOffsetFactor && offsetUnits == lastLayerOffsetUnits) {
|
||||
return lastLayer;
|
||||
}
|
||||
final RenderLayer layer = layerLookup(texture, material);
|
||||
final RenderLayer layer = layerLookup(texture, material, false, pass, offsetFactor, offsetUnits);
|
||||
lastLayerTexture = texture;
|
||||
lastLayerMaterial = material;
|
||||
lastLayerPass = pass;
|
||||
lastLayerOffsetFactor = offsetFactor;
|
||||
lastLayerOffsetUnits = offsetUnits;
|
||||
lastLayer = layer;
|
||||
return layer;
|
||||
}
|
||||
|
||||
private RenderLayer layerLookup(ResourceLocation texture, TesrMaterial material) {
|
||||
final LayerKey key = scratchKey.set(texture, material.transparency(), material.isNoCull(), material.isUnlit(), material.isNoDepthWrite(), material.isDepthOnly(), material.cutoutAlpha(), material.isDepthEqual(), material.special(), material.shader());
|
||||
private RenderLayer noPassLayerFor(ResourceLocation texture, TesrMaterial material) {
|
||||
if (texture == lastImmediateTexture && material == lastImmediateMaterial) {
|
||||
return lastImmediateLayer;
|
||||
}
|
||||
final RenderLayer layer = layerLookup(texture, material, true, PassOverride.NONE, 0.0f, 0.0f);
|
||||
lastImmediateTexture = texture;
|
||||
lastImmediateMaterial = material;
|
||||
lastImmediateLayer = layer;
|
||||
return layer;
|
||||
}
|
||||
|
||||
private RenderLayer layerLookup(ResourceLocation texture, TesrMaterial material, boolean noPass, PassOverride pass, float offsetFactor, float offsetUnits) {
|
||||
final LayerKey key = scratchKey.set(texture, material.transparency(), material.isNoCull(), material.isUnlit(), material.isNoDepthWrite(), material.isDepthOnly(), material.cutoutAlpha(), material.isDepthEqual(), material.special(), material.shader(), noPass, pass, offsetFactor, offsetUnits);
|
||||
RenderLayer layer = layers.get(key);
|
||||
if (layer == null) {
|
||||
layer = RenderLayer.tesr(texture, material);
|
||||
layer = noPass ? RenderLayer.tesrNoPass(texture, material) : RenderLayer.tesr(texture, material, pass, offsetFactor, offsetUnits);
|
||||
layers.put(key.copy(), layer);
|
||||
}
|
||||
return layer;
|
||||
@@ -232,9 +280,9 @@ public final class TesrBatchRenderer {
|
||||
|
||||
private static int currentPackedLight(TesrMaterial material) {
|
||||
if (material.hasLightmap()) {
|
||||
return ((int) material.lightmapY() << 16) | ((int) material.lightmapX() & 0xFFFF);
|
||||
return GLSMConfig.packBrightness(material.lightmapX(), material.lightmapY());
|
||||
}
|
||||
return ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
|
||||
return GLSMConfig.packedLastBrightness();
|
||||
}
|
||||
|
||||
private static int resolveColor(TesrMaterial material) {
|
||||
@@ -290,6 +338,7 @@ public final class TesrBatchRenderer {
|
||||
try {
|
||||
final EntityRenderer entityRenderer = Minecraft.getMinecraft().entityRenderer;
|
||||
final boolean savedDepthMask = GLStateManager.getDepthState().isEnabled();
|
||||
final boolean savedDepthTest = GLStateManager.getDepthTest().isEnabled();
|
||||
final boolean savedBlend = GLStateManager.getBlendMode().isEnabled();
|
||||
final int savedSrcRgb = GLStateManager.getBlendState().getSrcRgb();
|
||||
final int savedDstRgb = GLStateManager.getBlendState().getDstRgb();
|
||||
@@ -312,11 +361,12 @@ public final class TesrBatchRenderer {
|
||||
bufferSource.endBatch(hook);
|
||||
instancedRenderer.endFrame();
|
||||
AngelicaBufferSource.rebindPass();
|
||||
BatchingFontRenderer.flushDeferredText();
|
||||
BatchingFontRenderer.flushDeferredText();
|
||||
} finally {
|
||||
if (wrap) GbufferPrograms.endBlockEntities();
|
||||
entityRenderer.disableLightmap(0);
|
||||
GLStateManager.glDepthMask(savedDepthMask);
|
||||
if (savedDepthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
|
||||
if (savedBlend) GLStateManager.enableBlend(); else GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(savedSrcRgb, savedDstRgb, savedSrcAlpha, savedDstAlpha);
|
||||
if (savedAlphaTest) GLStateManager.enableAlphaTest(); else GLStateManager.disableAlphaTest();
|
||||
@@ -452,7 +502,7 @@ public final class TesrBatchRenderer {
|
||||
final long end = VertexTransform.writeInstance(addr, format, template, identity, scratchVec, colorABGR, packedLight, null);
|
||||
immediateScratch.position((int) (end - addr));
|
||||
immediateScratch.flip();
|
||||
final RenderLayer layer = layerFor(texture, material);
|
||||
final RenderLayer layer = noPassLayerFor(texture, material);
|
||||
GLStateManager.glPushAttrib(AngelicaBufferSource.SAVED_STATE_BITS);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
layer.startDrawing();
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
|
||||
public class NaturalTextureUtils {
|
||||
|
||||
// Yes this is supposed to be volatile
|
||||
private volatile static Set<Block> cachedWhitelistedBlocks = null;
|
||||
|
||||
public static int getTopRotation(int x, int y, int z) {
|
||||
long hash = (x * 3129871L) ^ (z * 116586811L) ^ y;
|
||||
hash = hash * hash * 42317861L + hash * 11L;
|
||||
return (int) (hash & 3);
|
||||
}
|
||||
|
||||
// This method gets called from worker threads
|
||||
public static boolean isNaturalBlock(Block block) {
|
||||
if (cachedWhitelistedBlocks == null) {
|
||||
// If list is empty add back all the stuff from config
|
||||
initCache();
|
||||
}
|
||||
return cachedWhitelistedBlocks.contains(block);
|
||||
}
|
||||
|
||||
// On reload clear the list
|
||||
public static void reloadCache() {
|
||||
cachedWhitelistedBlocks = null;
|
||||
}
|
||||
|
||||
private static void initCache() {
|
||||
Set<Block> set = new ReferenceOpenHashSet<>();
|
||||
if (AngelicaConfig.naturalTextureBlocks != null) {
|
||||
for (String name : AngelicaConfig.naturalTextureBlocks) {
|
||||
Block b = (Block) Block.blockRegistry.getObject(name);
|
||||
if (b != null && b != Blocks.air) {
|
||||
set.add(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
cachedWhitelistedBlocks = set;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class Zoom {
|
||||
private static long zoomLerpLastNano = 0L;
|
||||
|
||||
@Getter
|
||||
private static final KeyBinding zoomKey = new KeyBinding("Zoom", 0, "key.categories.misc");
|
||||
private static final KeyBinding zoomKey = new KeyBinding("angelica.keybind.zoom", 0, "key.categories.misc");
|
||||
|
||||
private static boolean zoomEnabled = false;
|
||||
|
||||
|
||||
@@ -250,8 +250,8 @@ public class ColorizeBlock {
|
||||
}
|
||||
colorMap = wrapBlockMap(colorMap);
|
||||
for (String idString : idList.split("\\s+")) {
|
||||
BlockStateMatcher blockMatcher = BlockAPI.createMatcher(new PropertiesFile(logger, resource), idString);
|
||||
if (blockMatcher != null) {
|
||||
for (BlockStateMatcher blockMatcher : BlockAPI
|
||||
.createMatchers(new PropertiesFile(logger, resource), idString)) {
|
||||
List<BlockStateMatcher> maps = blockColorMaps
|
||||
.computeIfAbsent(blockMatcher.getBlock(), k -> new ArrayList<>());
|
||||
blockMatcher.setData(colorMap);
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.resources.IResourcePack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
@@ -25,6 +26,7 @@ import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
|
||||
import com.prupe.mcpatcher.mal.block.RenderPassAPI;
|
||||
import com.prupe.mcpatcher.mal.resource.BlendMethod;
|
||||
import com.prupe.mcpatcher.mal.resource.ResourceList;
|
||||
import com.prupe.mcpatcher.mal.resource.ResourceLocationWithSource;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackChangeHandler;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
@@ -71,7 +73,7 @@ public class CTMUtils {
|
||||
}
|
||||
|
||||
public static void clearCurrentCompact() {
|
||||
CURRENT_COMPACT.remove();
|
||||
CURRENT_COMPACT.set(null);
|
||||
}
|
||||
|
||||
private static final StampedLock lock = new StampedLock();
|
||||
@@ -128,8 +130,14 @@ public class CTMUtils {
|
||||
logger.info("loading CTM overrides");
|
||||
newOverrides = new Overrides();
|
||||
if (MCPatcherForgeConfig.ConnectedTextures.standard || MCPatcherForgeConfig.ConnectedTextures.nonStandard) {
|
||||
for (ResourceLocation resource : ResourceList.getInstance()
|
||||
.listResources(TexturePackAPI.MCPATCHER_SUBDIR + "ctm", ".properties", true)) {
|
||||
final List<ResourceLocation> ctmResources = new ArrayList<>();
|
||||
for (String subdir : new String[] { TexturePackAPI.MCPATCHER_SUBDIR, TexturePackAPI.OPTIFINE_SUBDIR }) {
|
||||
ctmResources.addAll(ResourceList.getInstance()
|
||||
.listResources(subdir + "ctm", ".properties", true));
|
||||
}
|
||||
final Map<String, Map<IResourcePack, Integer>> packPriority = new HashMap<>();
|
||||
ctmResources.sort((a, b) -> packPriorityOf(packPriority, b) - packPriorityOf(packPriority, a));
|
||||
for (ResourceLocation resource : ctmResources) {
|
||||
registerOverrideWithoutLock(newOverrides, TileOverride.create(resource, tileLoader));
|
||||
}
|
||||
for (Consumer<Overrides> callback : ctmRegistrationCallbacks) {
|
||||
@@ -294,9 +302,7 @@ public class CTMUtils {
|
||||
Set<String> matchingTiles = override.getMatchingTiles();
|
||||
if (!MCPatcherUtils.isNullOrEmpty(matchingTiles)) {
|
||||
for (String name : matchingTiles) {
|
||||
List<TileOverride> list = overrides.tile.computeIfAbsent(name, k -> new ArrayList<>());
|
||||
list.add(override);
|
||||
logger.fine("using %s for tile %s", override, name);
|
||||
registerTileWithoutLock(overrides, name, override);
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
@@ -306,6 +312,30 @@ public class CTMUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTileWithoutLock(Overrides overrides, String name, TileOverride override) {
|
||||
overrides.tile.computeIfAbsent(name, k -> new ArrayList<>())
|
||||
.add(override);
|
||||
logger.fine("using %s for tile %s", override, name);
|
||||
}
|
||||
|
||||
private static Map<IResourcePack, Integer> getPackPriority(String namespace) {
|
||||
final List<IResourcePack> packs = TexturePackAPI.getResourcePacks(namespace);
|
||||
final Map<IResourcePack, Integer> priority = new IdentityHashMap<>(packs.size());
|
||||
for (int i = 0; i < packs.size(); i++) {
|
||||
priority.put(packs.get(i), i);
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
|
||||
private static int packPriorityOf(Map<String, Map<IResourcePack, Integer>> byDomain, ResourceLocation resource) {
|
||||
if (!(resource instanceof ResourceLocationWithSource source)) return -1;
|
||||
final Integer rank = byDomain
|
||||
.computeIfAbsent(resource.getResourceDomain(), CTMUtils::getPackPriority)
|
||||
.get(source.getSource());
|
||||
return rank == null ? -1 : rank;
|
||||
}
|
||||
|
||||
private static void setBlankResourceWithoutLock() {
|
||||
RenderBlocksUtils.blankIcon = tileLoader.getIcon(RenderPassAPI.instance.getBlankResource());
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ abstract public class RenderBlockState {
|
||||
public static final int CONNECT_BY_BLOCK = 0;
|
||||
public static final int CONNECT_BY_TILE = 1;
|
||||
public static final int CONNECT_BY_MATERIAL = 2;
|
||||
public static final int CONNECT_BY_LIST = 3;
|
||||
|
||||
protected static int[] add(int[] a, int[] b) {
|
||||
if (a.length != b.length) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_BLOCK;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_LIST;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_MATERIAL;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.CONNECT_BY_TILE;
|
||||
import static com.prupe.mcpatcher.ctm.RenderBlockState.NORMALS;
|
||||
@@ -33,13 +34,18 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
private static int nextRegistrationOrder;
|
||||
|
||||
public final PropertiesFile properties;
|
||||
private final String baseFilename;
|
||||
protected final TileLoader tileLoader;
|
||||
protected final int renderPass;
|
||||
private final int weight;
|
||||
private boolean lostScope;
|
||||
private final int registrationOrder = nextRegistrationOrder++;
|
||||
private final List<BlockStateMatcher> matchBlocks;
|
||||
private final Set<String> matchTiles;
|
||||
private final List<BlockStateMatcher> connectBlocks;
|
||||
private final BlockFaceMatcher faceMatcher;
|
||||
private final int connectType;
|
||||
private final boolean innerSeams;
|
||||
@@ -157,6 +163,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
if (matchBlocks.isEmpty() && matchTiles.isEmpty()) {
|
||||
matchTiles.add(baseFilename);
|
||||
}
|
||||
connectBlocks = getBlockList(properties.getString("connectBlocks", ""), properties.getString("connectMetadata", ""));
|
||||
|
||||
faceMatcher = BlockFaceMatcher.create(properties.getString("faces", ""));
|
||||
|
||||
@@ -167,6 +174,14 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
case "block" -> connectType = CONNECT_BY_BLOCK;
|
||||
case "tile" -> connectType = CONNECT_BY_TILE;
|
||||
case "material" -> connectType = CONNECT_BY_MATERIAL;
|
||||
case "list" -> {
|
||||
if (connectBlocks.isEmpty()) {
|
||||
properties.error("connect=list requires a non-empty connectBlocks= list");
|
||||
connectType = CONNECT_BY_BLOCK;
|
||||
} else {
|
||||
connectType = CONNECT_BY_LIST;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
properties.error("invalid connect type %s", connectType1);
|
||||
connectType = CONNECT_BY_BLOCK;
|
||||
@@ -274,19 +289,21 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
// nothing
|
||||
} else if (token.matches("\\d+-\\d+")) {
|
||||
for (int id : MCPatcherUtils.parseIntegerList(token, 0, 65535)) {
|
||||
BlockStateMatcher matcher = BlockAPI.createMatcher(properties, id + defaultMetadata);
|
||||
if (matcher == null) {
|
||||
properties.warning("unknown block id %d", id);
|
||||
List<BlockStateMatcher> matchers = BlockAPI.createMatchers(properties, id + defaultMetadata);
|
||||
if (matchers.isEmpty()) {
|
||||
properties.fine("unknown block id %d", id);
|
||||
lostScope = true;
|
||||
} else {
|
||||
blocks.add(matcher);
|
||||
blocks.addAll(matchers);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BlockStateMatcher matcher = BlockAPI.createMatcher(properties, token + defaultMetadata);
|
||||
if (matcher == null) {
|
||||
properties.warning("unknown block %s", token);
|
||||
List<BlockStateMatcher> matchers = BlockAPI.createMatchers(properties, token + defaultMetadata);
|
||||
if (matchers.isEmpty()) {
|
||||
properties.fine("unknown block %s", token);
|
||||
lostScope = true;
|
||||
} else {
|
||||
blocks.add(matcher);
|
||||
blocks.addAll(matchers);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +351,11 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
public final void registerIcons() {
|
||||
icons = new IIcon[tileNames.size()];
|
||||
for (int i = 0; i < icons.length; i++) {
|
||||
icons[i] = tileLoader.getIcon(tileNames.get(i));
|
||||
final ResourceLocation tileName = tileNames.get(i);
|
||||
icons[i] = tileLoader.getIcon(tileName);
|
||||
if (icons[i] == null && tileName != null) {
|
||||
properties.warning("tile %s did not resolve to an icon, tile %d will not render", tileName, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,11 +389,11 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
if (o instanceof TileOverride) {
|
||||
return baseFilename.compareTo(((TileOverride) o).baseFilename);
|
||||
} else {
|
||||
return -1;
|
||||
result = Boolean.compare(lostScope, o.lostScope);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return Integer.compare(registrationOrder, o.registrationOrder);
|
||||
}
|
||||
|
||||
final boolean shouldConnect(RenderBlockState renderBlockState, IIcon icon, int relativeDirection) {
|
||||
@@ -433,6 +454,16 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
case CONNECT_BY_TILE -> renderBlockState.shouldConnectByTile(neighbor, icon, x, y, z);
|
||||
case CONNECT_BY_BLOCK -> renderBlockState.shouldConnectByBlock(neighbor, x, y, z);
|
||||
case CONNECT_BY_MATERIAL -> block.blockMaterial == neighbor.blockMaterial;
|
||||
case CONNECT_BY_LIST -> {
|
||||
boolean matched = false;
|
||||
for (BlockStateMatcher matcher : connectBlocks) {
|
||||
if (matcher.match(blockAccess, x, y, z)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
yield matched;
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,15 +4,20 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.coderbot.iris.shaderpack.materialmap.BlockEntry;
|
||||
import net.coderbot.iris.shaderpack.materialmap.FlatteningMap;
|
||||
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
import com.prupe.mcpatcher.MCPatcherUtils;
|
||||
import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
|
||||
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
|
||||
import cpw.mods.fml.common.registry.GameData;
|
||||
@@ -64,7 +69,7 @@ public class BlockAPI {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public static BlockStateMatcher createMatcher(PropertiesFile source, String matchString) {
|
||||
public static List<BlockStateMatcher> createMatchers(PropertiesFile source, String matchString) {
|
||||
Map<String, String> propertyMap = new HashMap<>();
|
||||
String namespace = null;
|
||||
String blockName = null;
|
||||
@@ -90,7 +95,7 @@ public class BlockAPI {
|
||||
appendThis = true;
|
||||
} else {
|
||||
source.warning("invalid token '%s' in %s", source, s, matchString);
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (appendThis) {
|
||||
metaString.append(':');
|
||||
@@ -103,19 +108,73 @@ public class BlockAPI {
|
||||
}
|
||||
if (MCPatcherUtils.isNullOrEmpty(blockName)) {
|
||||
source.warning("cannot parse namespace/block name from %s", matchString);
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Block block = GameData.getBlockRegistry()
|
||||
.getObject(namespace + ':' + blockName);
|
||||
if (block == null) {
|
||||
source.warning("unknown block %s:%s", namespace, blockName);
|
||||
return null;
|
||||
final Block block = GameData.getBlockRegistry()
|
||||
.getRaw(namespace + ':' + blockName);
|
||||
if (block != null) {
|
||||
return Collections.singletonList(
|
||||
new BlockStateMatcher(
|
||||
metaString.toString(),
|
||||
block,
|
||||
metadata.toString()
|
||||
.trim()));
|
||||
}
|
||||
return new BlockStateMatcher(
|
||||
metaString.toString(),
|
||||
block,
|
||||
metadata.toString()
|
||||
.trim());
|
||||
if (!TexturePackAPI.DEFAULT_NAMESPACE.equals(namespace)) {
|
||||
source.fine("unknown block %s:%s", namespace, blockName);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<BlockEntry> entries = FlatteningMap.toLegacy(blockName, propertyMap);
|
||||
if (entries == null) {
|
||||
source.fine("unknown block %s:%s", namespace, blockName);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (!propertyMap.isEmpty() && entries.equals(FlatteningMap.toLegacy(blockName, Collections.emptyMap()))) {
|
||||
source.fine("block %s has no 1.7.10 equivalent for %s, ignoring this match", blockName, propertyMap);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final String explicitMetadata = metadata.toString()
|
||||
.trim();
|
||||
final List<BlockStateMatcher> matchers = new ArrayList<>(entries.size());
|
||||
for (BlockEntry entry : entries) {
|
||||
final NamespacedId id = entry.id();
|
||||
final Block legacy = GameData.getBlockRegistry()
|
||||
.getRaw(id.getNamespace() + ':' + id.getName());
|
||||
final String entryMetadata = intersectMetadata(explicitMetadata, entry.metas());
|
||||
if (entryMetadata == null) {
|
||||
continue;
|
||||
}
|
||||
matchers.add(new BlockStateMatcher(metaString.toString(), legacy, entryMetadata));
|
||||
}
|
||||
if (matchers.isEmpty()) {
|
||||
source.fine("unknown block %s:%s", namespace, blockName);
|
||||
}
|
||||
return matchers;
|
||||
}
|
||||
|
||||
private static String intersectMetadata(String explicitMetadata, Set<Integer> entryMetas) {
|
||||
if (MCPatcherUtils.isNullOrEmpty(explicitMetadata)) {
|
||||
if (entryMetas.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int meta : entryMetas) {
|
||||
sb.append(' ')
|
||||
.append(meta);
|
||||
}
|
||||
return sb.toString()
|
||||
.trim();
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int meta : MCPatcherUtils.parseIntegerList(explicitMetadata, 0, 15)) {
|
||||
if (entryMetas.isEmpty() || entryMetas.contains(meta)) {
|
||||
sb.append(' ')
|
||||
.append(meta);
|
||||
}
|
||||
}
|
||||
return sb.length() == 0 ? null
|
||||
: sb.toString()
|
||||
.trim();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,6 +43,7 @@ public class TexturePackAPI {
|
||||
public static final String DEFAULT_NAMESPACE = "minecraft";
|
||||
|
||||
public static final String MCPATCHER_SUBDIR = "mcpatcher/";
|
||||
public static final String OPTIFINE_SUBDIR = "optifine/";
|
||||
public static final ResourceLocation ITEMS_PNG = new ResourceLocation("textures/atlas/items.png");
|
||||
|
||||
private static final String ASSETS = "assets/";
|
||||
@@ -204,6 +205,11 @@ public class TexturePackAPI {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String subdirOf(ResourceLocation baseResource) {
|
||||
return baseResource != null && baseResource.getResourcePath()
|
||||
.startsWith(OPTIFINE_SUBDIR) ? OPTIFINE_SUBDIR : MCPATCHER_SUBDIR;
|
||||
}
|
||||
|
||||
public static ResourceLocation parseResourceLocation(ResourceLocation baseResource, String path) {
|
||||
if (MCPatcherUtils.isNullOrEmpty(path)) {
|
||||
return null;
|
||||
@@ -232,8 +238,8 @@ public class TexturePackAPI {
|
||||
ResourceLocation resource;
|
||||
if (path.startsWith("~/")) {
|
||||
// Relative to namespace mcpatcher dir:
|
||||
// ~/path -> assets/(namespace of base file)/mcpatcher/path
|
||||
resource = new ResourceLocation(baseResource.getResourceDomain(), MCPATCHER_SUBDIR + path.substring(2));
|
||||
// ~/path -> assets/(namespace of base file)/(mcpatcher|optifine)/path
|
||||
resource = new ResourceLocation(baseResource.getResourceDomain(), subdirOf(baseResource) + path.substring(2));
|
||||
} else if (path.startsWith("./")) {
|
||||
// Relative to properties file:
|
||||
// ./path -> (dir of base file)/path
|
||||
|
||||
@@ -98,6 +98,7 @@ public class TileLoader {
|
||||
logger.severe("beforeChange was not called, invoking directly");
|
||||
changeHandler.beforeChange();
|
||||
}
|
||||
long currentSize = Long.MIN_VALUE;
|
||||
for (TileLoader loader : loaders) {
|
||||
if (loader.isForThisMap(mapName)) {
|
||||
if (loader.baseTextureMap == null) {
|
||||
@@ -105,10 +106,13 @@ public class TileLoader {
|
||||
loader.baseTexturesByName.putAll(map);
|
||||
}
|
||||
if (!loader.tilesToRegister.isEmpty()) {
|
||||
if (currentSize == Long.MIN_VALUE) currentSize = getTextureSize(map.values());
|
||||
loader.subLogger
|
||||
.fine("adding icons to %s (%d remaining)", mapName, loader.tilesToRegister.size(), mapName);
|
||||
while (!loader.tilesToRegister.isEmpty() && loader.registerOneIcon(textureMap, mapName, map)) {
|
||||
// nothing
|
||||
while (!loader.tilesToRegister.isEmpty()) {
|
||||
long updatedSize = loader.registerOneIcon(textureMap, mapName, map, currentSize);
|
||||
if (updatedSize < 0) break;
|
||||
currentSize = updatedSize;
|
||||
}
|
||||
loader.subLogger.fine(
|
||||
"done adding icons to %s (%d remaining)",
|
||||
@@ -275,42 +279,44 @@ public class TileLoader {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map) {
|
||||
protected long registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map,
|
||||
long currentSize) {
|
||||
ResourceLocation resource = tilesToRegister.iterator()
|
||||
.next();
|
||||
String name = resource.toString();
|
||||
if (registerDefaultIcon(name)) {
|
||||
tilesToRegister.remove(resource);
|
||||
return true;
|
||||
return currentSize;
|
||||
}
|
||||
BufferedImage image = tileImages.get(resource);
|
||||
if (image == null) {
|
||||
subLogger.error("tile for %s unexpectedly missing", resource);
|
||||
tilesToRegister.remove(resource);
|
||||
return true;
|
||||
return currentSize;
|
||||
}
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
long currentSize = getTextureSize(map.values());
|
||||
long newSize = 4 * width * width;
|
||||
if (newSize + currentSize > MAX_TILESHEET_SIZE) {
|
||||
float sizeMB = (float) currentSize / 1048576.0f;
|
||||
if (currentSize <= 0) {
|
||||
subLogger.error("%s too big for any tilesheet (%.1fMB), dropping", name, sizeMB);
|
||||
tilesToRegister.remove(resource);
|
||||
return true;
|
||||
return currentSize;
|
||||
} else {
|
||||
subLogger.warning("%s nearly full (%.1fMB), will start a new tilesheet", mapName, sizeMB);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
TextureAtlasSprite previous = map.get(name);
|
||||
TextureAtlasSprite icon = registerIconToMap(textureMap, resource, name);
|
||||
map.put(name, icon);
|
||||
currentSize += getTextureSize(icon) - getTextureSize(previous);
|
||||
iconMap.put(name, icon);
|
||||
String extra = (width == height ? "" : ", " + (height / width) + " frames");
|
||||
subLogger.finer("%s -> %s icon %dx%d%s", name, mapName, width, width, extra);
|
||||
tilesToRegister.remove(resource);
|
||||
return true;
|
||||
return currentSize;
|
||||
}
|
||||
|
||||
protected TextureAtlasSprite registerIconToMap(TextureMap textureMap, ResourceLocation resource, String name) {
|
||||
|
||||
@@ -12,9 +12,7 @@ public class NotFineConfig {
|
||||
private static final String CATEGORY_TOGGLE = "toggle";
|
||||
private static final String CATEGORY_UNFINISHED = "unfinished";
|
||||
|
||||
public static boolean allowAdvancedOpenGL;
|
||||
public static boolean allowToggle3DAnaglyph;
|
||||
public static boolean allowToggleFBO;
|
||||
|
||||
public static boolean betterBlockFaceCulling;
|
||||
public static boolean renderPass;
|
||||
@@ -23,12 +21,7 @@ public class NotFineConfig {
|
||||
File configFile = new File(Launch.minecraftHome + File.separator + "config" + File.separator + NotFine.MODID + File.separator + "notfine.cfg");
|
||||
Configuration config = new Configuration(configFile);
|
||||
|
||||
allowAdvancedOpenGL = config.getBoolean("allowAdvancedOpenGL", CATEGORY_GENERAL, false,
|
||||
"Allow Advanced OpenGL to be enabled when it might be supported.");
|
||||
allowToggle3DAnaglyph = config.getBoolean("allowToggle3DAnaglyph", CATEGORY_GENERAL, true,
|
||||
"Allow 3D Anaglyph to be enabled.");
|
||||
allowToggleFBO = config.getBoolean("allowToggleFBO", CATEGORY_GENERAL, false,
|
||||
"Allow FBOs to be disabled.");
|
||||
allowToggle3DAnaglyph = config.getBoolean("allowToggle3DAnaglyph", CATEGORY_GENERAL, true, "Allow 3D Anaglyph to be enabled.");
|
||||
|
||||
config.setCategoryComment(CATEGORY_TOGGLE, "Toggle mod features.");
|
||||
betterBlockFaceCulling = config.getBoolean("betterBlockFaceCulling", CATEGORY_TOGGLE, true,
|
||||
@@ -43,10 +36,7 @@ public class NotFineConfig {
|
||||
config.save();
|
||||
}
|
||||
|
||||
//These will probably break hard with Angelica.
|
||||
//AdvancedOpenGL even breaks hard on its own.
|
||||
allowAdvancedOpenGL = false;
|
||||
allowToggleFBO = false;
|
||||
//This will probably break hard with Angelica.
|
||||
renderPass = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -243,21 +243,6 @@ public class NotFineGameOptionPages {
|
||||
List<OptionGroup> groups = new ArrayList<>();
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
|
||||
.setName(I18n.format("options.advancedOpengl"))
|
||||
.setTooltip(I18n.format("sodium.options.advanced_opengl.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> opts.advancedOpengl = value, opts -> opts.advancedOpengl)
|
||||
.setImpact(OptionImpact.VARIES)
|
||||
.setEnabled(NotFineConfig.allowAdvancedOpenGL)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
|
||||
.setName(I18n.format("options.fboEnable"))
|
||||
.setTooltip(I18n.format("sodium.options.fbo.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> opts.fboEnable = value, opts -> opts.fboEnable)
|
||||
.setEnabled(NotFineConfig.allowToggleFBO)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, vanillaOpts)
|
||||
.setName(I18n.format("options.anaglyph"))
|
||||
.setTooltip(I18n.format("sodium.options.anaglyph.tooltip"))
|
||||
|
||||
@@ -18,15 +18,9 @@ public class ClientProxy extends CommonProxy {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
if(!NotFineConfig.allowAdvancedOpenGL) {
|
||||
Minecraft.getMinecraft().gameSettings.advancedOpengl = false;
|
||||
}
|
||||
if(!NotFineConfig.allowToggle3DAnaglyph) {
|
||||
Minecraft.getMinecraft().gameSettings.anaglyph = false;
|
||||
}
|
||||
if(!NotFineConfig.allowToggleFBO) {
|
||||
Minecraft.getMinecraft().gameSettings.fboEnable = true;
|
||||
}
|
||||
|
||||
for(Settings setting : Settings.values()) {
|
||||
setting.ready();
|
||||
|
||||
+4
-13
@@ -105,9 +105,9 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
|
||||
final Dim2i applyButtonDim = new Dim2i(tabFrameDim.getLimitX() - 134, tabFrameDim.getLimitY() + 5, 65, 20);
|
||||
final Dim2i closeButtonDim = new Dim2i(tabFrameDim.getLimitX() - 65, tabFrameDim.getLimitY() + 5, 65, 20);
|
||||
|
||||
this.undoButton = new FlatButtonWidget(undoButtonDim, "Undo", this::undoChanges);
|
||||
this.applyButton = new FlatButtonWidget(applyButtonDim, "Apply", this::applyChanges);
|
||||
this.closeButton = new FlatButtonWidget(closeButtonDim, "Close", this::onClose);
|
||||
this.undoButton = new FlatButtonWidget(undoButtonDim, I18n.format("sodium.options.buttons.undo"), this::undoChanges);
|
||||
this.applyButton = new FlatButtonWidget(applyButtonDim, I18n.format("sodium.options.buttons.apply"), this::applyChanges);
|
||||
this.closeButton = new FlatButtonWidget(closeButtonDim, I18n.format("gui.done"), this::onClose);
|
||||
|
||||
// Pre-compute button text and widths to avoid duplicate calculations
|
||||
final String irisText = Iris.enabled ? I18n.format(IrisApi.getInstance().getMainScreenLanguageKey()) : null;
|
||||
@@ -202,16 +202,7 @@ public class ReeseSodiumVideoOptionsScreen extends SodiumOptionsGUI {
|
||||
}
|
||||
|
||||
private void updateControls() {
|
||||
boolean hasChanges = this.getAllOptions()
|
||||
.anyMatch(Option::hasChanged);
|
||||
|
||||
for (OptionPage page : this.pages) {
|
||||
for (Option<?> option : page.getOptions()) {
|
||||
if (option.hasChanged()) {
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
|
||||
|
||||
this.applyButton.setEnabled(hasChanges);
|
||||
this.undoButton.setVisible(hasChanges);
|
||||
|
||||
+1
-5
@@ -93,11 +93,7 @@ public class SearchTextFieldComponent extends AbstractWidget {
|
||||
return;
|
||||
}
|
||||
if (!this.isFocused() && this.text.isEmpty()) {
|
||||
final String key = "rso.search_bar_empty";
|
||||
String text = I18n.format(key);
|
||||
if (text.equals(key))
|
||||
text = "Search options...";
|
||||
this.textRenderer.drawString(text, this.dim.getOriginX() + 6, this.dim.getOriginY() + 6, 0xFFAAAAAA);
|
||||
this.textRenderer.drawString(I18n.format("rso.search_bar_empty"), this.dim.getOriginX() + 6, this.dim.getOriginY() + 6, 0xFFAAAAAA);
|
||||
}
|
||||
|
||||
drawRect(this.dim.getOriginX(), this.dim.getOriginY(), this.dim.getLimitX(), this.dim.getLimitY(), this.isFocused() ? 0xE0000000 : 0x90000000);
|
||||
|
||||
@@ -11,8 +11,6 @@ import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.MathHelper;
|
||||
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import com.cardinalstar.cubicchunks.api.compat.CubicChunksVideoSettings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
@@ -21,7 +19,6 @@ import com.gtnewhorizons.angelica.rendering.celeritas.MultiDrawModeResolver;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
import com.gtnewhorizons.angelica.config.GpuCullingMode;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import cpw.mods.fml.common.Optional.Method;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import jss.notfine.core.Settings;
|
||||
@@ -48,6 +45,7 @@ import me.jellysquid.mods.sodium.client.gui.options.storage.MinecraftOptionsStor
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gui.option.IrisVideoSettings;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.occlusion.AsyncOcclusionMode;
|
||||
|
||||
public class SodiumGameOptionPages {
|
||||
@@ -305,9 +303,10 @@ public class SodiumGameOptionPages {
|
||||
.add(OptionImpl.createBuilder(StreamingUploader.UploadStrategy.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.upload_method.name"))
|
||||
.setTooltip(I18n.format("sodium.options.upload_method.tooltip"))
|
||||
.setControl(o ->
|
||||
new CyclingControl<>(o, StreamingUploader.UploadStrategy.class, StreamingUploader.UploadStrategy.values())
|
||||
)
|
||||
.setControl(o -> new CyclingControl<>(o, StreamingUploader.UploadStrategy.class, new String[]{
|
||||
I18n.format("sodium.options.upload_method.buffer_data"),
|
||||
I18n.format("sodium.options.upload_method.buffer_subdata"),
|
||||
I18n.format("sodium.options.upload_method.map_buffer") }))
|
||||
.setBinding((opts, value) -> opts.advanced.streamingUploadStrategy = value, opts -> opts.advanced.streamingUploadStrategy)
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
.setImpact(OptionImpact.VARIES)
|
||||
@@ -411,7 +410,10 @@ public class SodiumGameOptionPages {
|
||||
.add(OptionImpl.createBuilder(AsyncOcclusionMode.class, sodiumOpts)
|
||||
.setName(I18n.format("sodium.options.async_occlusion_mode.name"))
|
||||
.setTooltip(I18n.format("sodium.options.async_occlusion_mode.tooltip"))
|
||||
.setControl(o -> new CyclingControl<>(o, AsyncOcclusionMode.class, new String[]{"None", "Only Shadow", "Everything"}))
|
||||
.setControl(o -> new CyclingControl<>(o, AsyncOcclusionMode.class, new String[]{
|
||||
I18n.format("sodium.options.async_occlusion_mode.none"),
|
||||
I18n.format("sodium.options.async_occlusion_mode.only_shadow"),
|
||||
I18n.format("sodium.options.async_occlusion_mode.everything") }))
|
||||
.setImpact(OptionImpact.MEDIUM)
|
||||
.setBinding((opts, value) -> opts.performance.asyncOcclusionMode = value, opts -> opts.performance.asyncOcclusionMode)
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
@@ -519,6 +521,36 @@ public class SodiumGameOptionPages {
|
||||
|
||||
.build());
|
||||
|
||||
if (Iris.enabled) {
|
||||
final OptionImpl<AngelicaConfig, Integer> shadowGraphAngleDelta =
|
||||
OptionImpl.createBuilder(int.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.shadowGraphAngleDelta"))
|
||||
.setTooltip(I18n.format("options.angelica.shadowGraphAngleDelta.tooltip"))
|
||||
.setControl(o -> new SliderControl(o, 0, 100, 1, ControlValueFormatter.shadowGraphAngleDelta()))
|
||||
.setImpact(OptionImpact.MEDIUM)
|
||||
.setBinding((opts, value) -> AngelicaConfig.shadowGraphAngleDelta = value * 0.001f,
|
||||
opts -> Math.round(AngelicaConfig.shadowGraphAngleDelta * 1000.0f))
|
||||
.build();
|
||||
shadowGraphAngleDelta.iris$dynamicallyEnable(ShadowRenderer::shadowGraphGateIsNotBypassed);
|
||||
|
||||
final OptionImpl<AngelicaConfig, Integer> shadowGraphHorizonScale =
|
||||
OptionImpl.createBuilder(int.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.shadowGraphHorizonScale"))
|
||||
.setTooltip(I18n.format("options.angelica.shadowGraphHorizonScale.tooltip"))
|
||||
.setControl(o -> new SliderControl(o, 0, 100, 1, ControlValueFormatter.shadowGraphHorizonScale()))
|
||||
.setImpact(OptionImpact.LOW)
|
||||
.setBinding((opts, value) -> AngelicaConfig.shadowGraphHorizonScale = value * 0.01f,
|
||||
opts -> Math.round(AngelicaConfig.shadowGraphHorizonScale * 100.0f))
|
||||
.build();
|
||||
shadowGraphHorizonScale.iris$dynamicallyEnable(
|
||||
() -> ShadowRenderer.shadowGraphGateIsNotBypassed() && shadowGraphAngleDelta.getValue() != 0);
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(shadowGraphAngleDelta)
|
||||
.add(shadowGraphHorizonScale)
|
||||
.build());
|
||||
}
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.hudCaching"))
|
||||
|
||||
@@ -54,7 +54,6 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
|
||||
|
||||
protected OptionPage shaderPacks;
|
||||
|
||||
|
||||
public SodiumOptionsGUI(GuiScreen prevScreen) {
|
||||
this.prevScreen = prevScreen;
|
||||
|
||||
@@ -67,10 +66,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
|
||||
|
||||
// Add Dynamic Lights page if enabled
|
||||
if (DynamicLights.configEnabled) {
|
||||
var dlPage = DynamicLightsOptionPages.dynamicLights();
|
||||
if (dlPage != null) {
|
||||
this.pages.add(dlPage);
|
||||
}
|
||||
this.pages.add(DynamicLightsOptionPages.dynamicLights());
|
||||
}
|
||||
|
||||
if (Iris.enabled) {
|
||||
@@ -194,15 +190,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
|
||||
|
||||
for (OptionPage page : this.pages) {
|
||||
for (Option<?> option : page.getOptions()) {
|
||||
if (option.hasChanged()) {
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
final boolean hasChanges = this.getAllOptions().anyMatch(Option::hasChanged);
|
||||
|
||||
this.applyButton.setEnabled(hasChanges);
|
||||
this.undoButton.setVisible(hasChanges);
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package me.jellysquid.mods.sodium.client.gui.options;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.Control;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.element.ControlElementFactory;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.element.SodiumControlElement;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage;
|
||||
import me.jellysquid.mods.sodium.client.util.Dim2i;
|
||||
|
||||
/** "Option" that opens a sub screen */
|
||||
public class SubScreenOption implements Option<Void> {
|
||||
|
||||
private static final int ARROW_HOVERED = 0xFF94E4D3;
|
||||
private static final int ARROW_IDLE = 0xFFAAAAAA;
|
||||
private static final String ARROW = ">";
|
||||
|
||||
private static final OptionStorage<Void> NO_STORAGE = new OptionStorage<Void>() {
|
||||
|
||||
@Override
|
||||
public Void getData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {}
|
||||
};
|
||||
|
||||
private final String name;
|
||||
private final String tooltip;
|
||||
private final Function<GuiScreen, GuiScreen> destination;
|
||||
private final Control<Void> control = new SubScreenControl(this);
|
||||
|
||||
public SubScreenOption(String name, String tooltip, Function<GuiScreen, GuiScreen> destination) {
|
||||
this.name = name;
|
||||
this.tooltip = tooltip;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
private void open() {
|
||||
final var mc = Minecraft.getMinecraft();
|
||||
mc.displayGuiScreen(this.destination.apply(mc.currentScreen));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTooltip() {
|
||||
return this.tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionImpact getImpact() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Control<Void> getControl() {
|
||||
return this.control;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Void value) {}
|
||||
|
||||
@Override
|
||||
public void reset() {}
|
||||
|
||||
@Override
|
||||
public OptionStorage<?> getStorage() {
|
||||
return NO_STORAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyChanges() {}
|
||||
|
||||
@Override
|
||||
public Collection<OptionFlag> getFlags() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
private record SubScreenControl(SubScreenOption target) implements Control<Void> {
|
||||
|
||||
@Override
|
||||
public Option<Void> getOption() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlElement<Void> createElement(Dim2i dim, ControlElementFactory factory) {
|
||||
return new SubScreenElement(this.target, dim);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxWidth() {
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SubScreenElement extends SodiumControlElement<Void> {
|
||||
|
||||
private final SubScreenOption target;
|
||||
|
||||
SubScreenElement(SubScreenOption option, Dim2i dim) {
|
||||
super(option, dim);
|
||||
this.target = option;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int mouseX, int mouseY, float delta) {
|
||||
super.render(mouseX, mouseY, delta);
|
||||
|
||||
final var width = this.font.getStringWidth(ARROW);
|
||||
this.drawString(ARROW, this.dim.getLimitX() - width - 6, this.dim.getCenterY() - 4, this.hovered ? ARROW_HOVERED : ARROW_IDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (button != 0 || !this.dim.containsCursor(mouseX, mouseY)) return false;
|
||||
|
||||
this.playClickSound();
|
||||
this.target.open();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -2,6 +2,8 @@ package me.jellysquid.mods.sodium.client.gui.options.control;
|
||||
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public interface ControlValueFormatter {
|
||||
static ControlValueFormatter guiScale() {
|
||||
return (v) -> (v == 0) ? I18n.format("options.guiScale.auto") : I18n.format(v + "x");
|
||||
@@ -47,4 +49,14 @@ public interface ControlValueFormatter {
|
||||
static ControlValueFormatter number() {
|
||||
return String::valueOf;
|
||||
}
|
||||
|
||||
static ControlValueFormatter shadowGraphAngleDelta() {
|
||||
return (v) -> v == 0
|
||||
? I18n.format("options.angelica.shadowGraphAngleDelta.always")
|
||||
: String.format(Locale.ROOT, "%.3f°", v * 0.001f);
|
||||
}
|
||||
|
||||
static ControlValueFormatter shadowGraphHorizonScale() {
|
||||
return (v) -> v == 100 ? I18n.format("options.angelica.shadowGraphHorizonScale.off") : v + "%";
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.coderbot.batchedentityrendering.impl.ordering.SimpleRenderOrderManager;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPhase;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -159,15 +161,17 @@ public class AngelicaBufferSource implements Groupable {
|
||||
|
||||
private void clearCurrentId() {
|
||||
if (!anyIdSet) return;
|
||||
if (idKind == GroupIdKind.ENTITY) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(-1);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0f, 0f, 0f, 0f);
|
||||
} else {
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
|
||||
}
|
||||
// A pass can set either kind: nested entities inside a TESR write the entity id during a block entity pass
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(-1, 0);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0f, 0f, 0f, 0f);
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
|
||||
anyIdSet = false;
|
||||
}
|
||||
|
||||
private GroupIdKind effectiveIdKind() {
|
||||
return GbufferPrograms.getCurrentPhase() == WorldRenderingPhase.ENTITIES ? GroupIdKind.ENTITY : idKind;
|
||||
}
|
||||
|
||||
private void drawLayer(RenderLayer layer, LayerDrawHook hook) {
|
||||
final List<BufferSegment> segments = typeToSegment.get(layer);
|
||||
final boolean hasDynamic = segments != null && !segments.isEmpty();
|
||||
@@ -184,7 +188,7 @@ public class AngelicaBufferSource implements Groupable {
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
SegmentedBufferBuilder.LayerBuffer bound = null;
|
||||
final boolean entityKind = idKind == GroupIdKind.ENTITY;
|
||||
final boolean entityKind = effectiveIdKind() == GroupIdKind.ENTITY;
|
||||
int currentId = Integer.MIN_VALUE;
|
||||
int currentColor = 0;
|
||||
boolean colorSet = false;
|
||||
@@ -271,8 +275,8 @@ public class AngelicaBufferSource implements Groupable {
|
||||
|
||||
public void applyIdAndRebind(int id) {
|
||||
anyIdSet = true;
|
||||
if (idKind == GroupIdKind.ENTITY) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(id);
|
||||
if (effectiveIdKind() == GroupIdKind.ENTITY) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(id, 0);
|
||||
rebindPass();
|
||||
} else {
|
||||
setBlockEntityAndRebind(id);
|
||||
@@ -281,8 +285,8 @@ public class AngelicaBufferSource implements Groupable {
|
||||
|
||||
public void applyIdNoRebind(int id) {
|
||||
anyIdSet = true;
|
||||
if (idKind == GroupIdKind.ENTITY) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(id);
|
||||
if (effectiveIdKind() == GroupIdKind.ENTITY) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(id, 0);
|
||||
} else {
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(id);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
|
||||
import com.gtnewhorizons.angelica.iris.ImmediateExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
@@ -54,6 +55,7 @@ import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -342,11 +344,11 @@ public class Iris {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
try {
|
||||
reload();
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText("Shaders Reloaded!"));
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.reloaded")));
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Error while reloading Shaders for Iris!", e);
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo reload shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.reloaded.failure", Throwables.getRootCause(e).getMessage())));
|
||||
}
|
||||
} else if (toggleShadersKeybind.isPressed()) {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
@@ -355,7 +357,7 @@ public class Iris {
|
||||
} catch (Exception e) {
|
||||
logger.error("Error while toggling shaders!", e);
|
||||
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText( "Failed tgo toggle shaders! Reason: " + Throwables.getRootCause(e).getMessage()));
|
||||
if (mc.thePlayer != null) mc.thePlayer.addChatMessage(new ChatComponentText(I18n.format("iris.shaders.toggled.failure", Throwables.getRootCause(e).getMessage())));
|
||||
setShadersDisabled();
|
||||
fallback = true;
|
||||
}
|
||||
@@ -801,6 +803,9 @@ public class Iris {
|
||||
// Load the new shaderpack
|
||||
loadShaderpack();
|
||||
|
||||
// Drop the stale program probe and cached item geometry
|
||||
ImmediateExtendedAttribs.onShaderPackChanged();
|
||||
|
||||
// Very important - we need to re-create the pipeline straight away.
|
||||
// https://github.com/IrisShaders/Iris/issues/1330
|
||||
if (Minecraft.getMinecraft().theWorld != null) {
|
||||
@@ -919,6 +924,11 @@ public class Iris {
|
||||
return pipelineManager;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PipelineManager getPipelineManagerNullable() {
|
||||
return pipelineManager;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Optional<ShaderPack> getCurrentPack() {
|
||||
return Optional.ofNullable(currentPack);
|
||||
@@ -968,9 +978,9 @@ public class Iris {
|
||||
|
||||
public void fmlInitEvent() {
|
||||
IRIS_VERSION = Tags.VERSION;
|
||||
reloadKeybind = new KeyBinding("Reload Shaders", 0, "Iris Keybinds");
|
||||
toggleShadersKeybind = new KeyBinding("Toggle Shaders", 0, "Iris Keybinds");
|
||||
shaderpackScreenKeybind = new KeyBinding("Shaderpack Selection Screen", 0, "Iris Keybinds");
|
||||
reloadKeybind = new KeyBinding("iris.keybind.reload", 0, "key.category.iris.keybinds");
|
||||
toggleShadersKeybind = new KeyBinding("iris.keybind.toggleShaders", 0, "key.category.iris.keybinds");
|
||||
shaderpackScreenKeybind = new KeyBinding("iris.keybind.shaderPackSelection", 0, "key.category.iris.keybinds");
|
||||
|
||||
ClientRegistry.registerKeyBinding(reloadKeybind);
|
||||
ClientRegistry.registerKeyBinding(toggleShadersKeybind);
|
||||
|
||||
Loaded 100 of 167 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user