mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d53e25e0a | ||
|
|
bbf83db379 | ||
|
|
ec82280b9b | ||
|
|
d7588e4c14 | ||
|
|
54c99386d4 | ||
|
|
465abf889a | ||
|
|
4acb83be6a | ||
|
|
80f9724ee6 | ||
|
|
8cf55bb216 |
No files matched your search
@@ -345,6 +345,17 @@ public class FeedbackManager {
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_QUAD_STRIP:
|
||||
for (int i = 0; i + 3 < count; i += 2) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(4);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
emitVertex(reader.read(i + 3), stride);
|
||||
emitVertex(reader.read(i + 2), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_POLYGON:
|
||||
if (count >= 3) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
|
||||
@@ -949,10 +949,11 @@ public class GLStateManager {
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_COLOR_WRITEMASK -> {
|
||||
params.put((byte) (colorMask.red ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.green ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.blue ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.alpha ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
final int pos = params.position();
|
||||
params.put(pos, (byte) (colorMask.red ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 1, (byte) (colorMask.green ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 2, (byte) (colorMask.blue ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 3, (byte) (colorMask.alpha ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
}
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
@@ -1063,7 +1064,7 @@ public class GLStateManager {
|
||||
case GL11.GL_DIFFUSE -> state.diffuse.get(0, params);
|
||||
case GL11.GL_SPECULAR -> state.specular.get(0, params);
|
||||
case GL11.GL_EMISSION -> state.emission.get(0, params);
|
||||
case GL11.GL_SHININESS -> params.put(state.shininess);
|
||||
case GL11.GL_SHININESS -> params.put(params.position(), state.shininess);
|
||||
case GL11.GL_COLOR_INDEXES -> state.colorIndexes.get(0, params);
|
||||
default -> {
|
||||
}
|
||||
@@ -1078,11 +1079,11 @@ public class GLStateManager {
|
||||
case GL11.GL_SPECULAR -> state.specular.get(0, params);
|
||||
case GL11.GL_POSITION -> state.position.get(0, params);
|
||||
case GL11.GL_SPOT_DIRECTION -> state.spotDirection.get(0, params);
|
||||
case GL11.GL_SPOT_EXPONENT -> params.put(state.spotExponent);
|
||||
case GL11.GL_SPOT_CUTOFF -> params.put(state.spotCutoff);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> params.put(state.constantAttenuation);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> params.put(state.linearAttenuation);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> params.put(state.quadraticAttenuation);
|
||||
case GL11.GL_SPOT_EXPONENT -> params.put(params.position(), state.spotExponent);
|
||||
case GL11.GL_SPOT_CUTOFF -> params.put(params.position(), state.spotCutoff);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> params.put(params.position(), state.constantAttenuation);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> params.put(params.position(), state.linearAttenuation);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> params.put(params.position(), state.quadraticAttenuation);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
@@ -1101,21 +1102,39 @@ public class GLStateManager {
|
||||
case GL11.GL_COLOR_CLEAR_VALUE -> clearColor.get(params);
|
||||
case GL11.GL_CURRENT_COLOR -> color.get(params);
|
||||
case GL11.GL_DEPTH_RANGE -> {
|
||||
params.put((float) viewportState.depthRangeNear);
|
||||
params.put((float) viewportState.depthRangeFar);
|
||||
final int pos = params.position();
|
||||
params.put(pos, (float) viewportState.depthRangeNear);
|
||||
params.put(pos + 1, (float) viewportState.depthRangeFar);
|
||||
}
|
||||
case GL14.GL_BLEND_COLOR -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, blendState.getBlendColorR());
|
||||
params.put(pos + 1, blendState.getBlendColorG());
|
||||
params.put(pos + 2, blendState.getBlendColorB());
|
||||
params.put(pos + 3, blendState.getBlendColorA());
|
||||
}
|
||||
case GL14.GL_BLEND_COLOR -> params.put(blendState.getBlendColorR()).put(blendState.getBlendColorG()).put(blendState.getBlendColorB()).put(blendState.getBlendColorA());
|
||||
case GL11.GL_CURRENT_NORMAL -> {
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
params.put(normal.x).put(normal.y).put(normal.z);
|
||||
final int pos = params.position();
|
||||
params.put(pos, normal.x);
|
||||
params.put(pos + 1, normal.y);
|
||||
params.put(pos + 2, normal.z);
|
||||
}
|
||||
case GL11.GL_CURRENT_TEXTURE_COORDS -> {
|
||||
final Vector4f tc = ShaderManager.getCurrentTexCoord();
|
||||
params.put(tc.x).put(tc.y).put(tc.z).put(tc.w);
|
||||
final int pos = params.position();
|
||||
params.put(pos, tc.x);
|
||||
params.put(pos + 1, tc.y);
|
||||
params.put(pos + 2, tc.z);
|
||||
params.put(pos + 3, tc.w);
|
||||
}
|
||||
case GL11.GL_FOG_COLOR -> {
|
||||
final FloatBuffer fogBuf = fogState.getFogColorBuffer();
|
||||
params.put(fogBuf.get(0)).put(fogBuf.get(1)).put(fogBuf.get(2)).put(fogBuf.get(3));
|
||||
final int pos = params.position();
|
||||
params.put(pos, fogBuf.get(0));
|
||||
params.put(pos + 1, fogBuf.get(1));
|
||||
params.put(pos + 2, fogBuf.get(2));
|
||||
params.put(pos + 3, fogBuf.get(3));
|
||||
}
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> lightModel.ambient.get(0, params);
|
||||
default -> {
|
||||
@@ -2341,6 +2360,8 @@ public class GLStateManager {
|
||||
prepareClientArrays();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(first, count);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
|
||||
} else {
|
||||
@@ -4368,9 +4389,20 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.finish();
|
||||
}
|
||||
|
||||
public static int glGetError() { return RENDER_BACKEND.getError(); }
|
||||
public static String glGetString(int pname) { return RENDER_BACKEND.getString(pname); }
|
||||
public static String glGetStringi(int name, int index) { return RENDER_BACKEND.getStringi(name, index); }
|
||||
public static int glGetError() {
|
||||
if (!RENDER_BACKEND.hasContext()) return 0;
|
||||
return RENDER_BACKEND.getError();
|
||||
}
|
||||
|
||||
public static String glGetString(int pname) {
|
||||
if (!RENDER_BACKEND.hasContext()) return "no valid GL/render context";
|
||||
return RENDER_BACKEND.getString(pname);
|
||||
}
|
||||
|
||||
public static String glGetStringi(int name, int index) {
|
||||
if (!RENDER_BACKEND.hasContext()) return "no valid GL/render context";
|
||||
return RENDER_BACKEND.getStringi(name, index);
|
||||
}
|
||||
|
||||
public static void glShaderSource(int shader, CharSequence source) {
|
||||
String src = source.toString();
|
||||
|
||||
+11
-1
@@ -9,6 +9,7 @@ import org.lwjgl.opengl.ARBDebugOutputCallback;
|
||||
import org.lwjgl.opengl.ARBDirectStateAccess;
|
||||
import org.lwjgl.opengl.ContextCapabilities;
|
||||
import org.lwjgl.opengl.EXTDirectStateAccess;
|
||||
import org.lwjgl.opengl.GLContext;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
@@ -67,7 +68,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
caps = org.lwjgl.opengl.GLContext.getCapabilities();
|
||||
caps = GLContext.getCapabilities();
|
||||
activeDebugExtension = 0;
|
||||
}
|
||||
|
||||
@@ -86,6 +87,15 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return "OpenGL (LWJGL2)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContext() {
|
||||
try {
|
||||
return GLContext.getCapabilities() != null;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinGLSLVersion() {return 330;}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ public abstract class RenderBackend {
|
||||
public abstract boolean isAvailable();
|
||||
public abstract String getName();
|
||||
|
||||
/** Returns true if the current thread has a valid render context. Safe to call from any thread. */
|
||||
public abstract boolean hasContext();
|
||||
|
||||
/** Higher priority backends are preferred. */
|
||||
public int getPriority() { return 0; }
|
||||
|
||||
|
||||
+3
@@ -75,6 +75,9 @@ 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);
|
||||
} 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);
|
||||
} else if (data.drawMode == GL11.GL_POLYGON) {
|
||||
// GL_POLYGON is removed in core profile; convert to GL_TRIANGLE_FAN which is equivalent for convex polygons
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_FAN, start, vertexCount);
|
||||
|
||||
@@ -207,6 +207,57 @@ class FeedbackManagerTest {
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQuadStripFeedback_GL2D() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final float[] verts = {
|
||||
0f, 0f, 0f,
|
||||
0f, 1f, 0f,
|
||||
1f, 0f, 0f,
|
||||
1f, 1f, 0f
|
||||
};
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_QUAD_STRIP, 0, 4);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
|
||||
assertEquals(10, count, "Quad strip in GL_2D should produce 10 floats");
|
||||
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
|
||||
assertEquals(4.0f, buf.get(1), 0.001f);
|
||||
assertEquals(400.0f, buf.get(2), 0.01f);
|
||||
assertEquals(300.0f, buf.get(3), 0.01f);
|
||||
assertEquals(400.0f, buf.get(4), 0.01f);
|
||||
assertEquals(600.0f, buf.get(5), 0.01f);
|
||||
assertEquals(800.0f, buf.get(6), 0.01f);
|
||||
assertEquals(600.0f, buf.get(7), 0.01f);
|
||||
assertEquals(800.0f, buf.get(8), 0.01f);
|
||||
assertEquals(300.0f, buf.get(9), 0.01f);
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIdentityTransform_viewportMapping() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(64);
|
||||
|
||||
+10
@@ -68,6 +68,16 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
return "OpenGL (LWJGL3)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContext() {
|
||||
try {
|
||||
GL.getCapabilities();
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() { return 50; }
|
||||
|
||||
|
||||
@@ -244,6 +244,7 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins(
|
||||
"shaders.MixinDroppedItemGlintEdges"
|
||||
, "shaders.MixinHeldItemGlintEdges"
|
||||
, "shaders.MixinEntityPickupFX"
|
||||
, "shaders.MixinEntityRenderer"
|
||||
, "shaders.MixinGuiIngameForge"
|
||||
|
||||
@@ -16,4 +16,8 @@ public interface StateAwareTessellator {
|
||||
void angelica$setCeleritasMeshing(boolean active);
|
||||
|
||||
int[] angelica$getVertexStates();
|
||||
|
||||
int[] angelica$getShaderOverrideBlockIds();
|
||||
|
||||
void angelica$setShaderOverrideBlockId(short blockId);
|
||||
}
|
||||
+6
@@ -115,6 +115,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void copyRawBuffer(int[] rawBuffer, int vertexCount, int[] vertexStates,
|
||||
int[] shaderOverrideBlockIds,
|
||||
ChunkBuildBuffers buffers, Material material, boolean isShaderPackOverride,
|
||||
boolean blockAllowsSmoothLighting) {
|
||||
if (vertexCount == 0) {
|
||||
@@ -145,6 +146,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
final int worldY = originY + blockY;
|
||||
final int worldZ = originZ + blockZ;
|
||||
|
||||
final short originalBlockId = blockRenderContext.blockId;
|
||||
|
||||
int stateIdx = 0;
|
||||
int facesAtBaseMaterial = 0;
|
||||
|
||||
@@ -239,6 +242,9 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
}
|
||||
final var builder = buffers.get(correctMaterial);
|
||||
|
||||
final int shaderOverrideBlockId = shaderOverrideBlockIds[quadIdx * 4];
|
||||
blockRenderContext.blockId = shaderOverrideBlockId != -1 ? (short) shaderOverrideBlockId : originalBlockId;
|
||||
|
||||
if (correctMaterial != material && builder.getEncoder() instanceof IrisExtendedChunkVertexEncoder iris) {
|
||||
iris.setContext(blockRenderContext);
|
||||
}
|
||||
|
||||
+1
@@ -270,6 +270,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
&& block.getLightValue() == 0; // does not emit real block light
|
||||
buildContext.copyRawBuffer(tessellator.rawBuffer, tessellator.vertexCount,
|
||||
((StateAwareTessellator)tessellator).angelica$getVertexStates(),
|
||||
((StateAwareTessellator)tessellator).angelica$getShaderOverrideBlockIds(),
|
||||
buffers, passMaterial, isShaderPackOverride, blockAllowsSmoothLighting);
|
||||
tessellator.reset();
|
||||
tessellator.isDrawing = false;
|
||||
|
||||
+3
-8
@@ -79,13 +79,13 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
|
||||
|
||||
baseEncoder.write(ptr, material, vertex, sectionIndex);
|
||||
|
||||
// Per-vertex: renderType, midBlock, lightValue
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET + 2, ctx.renderType);
|
||||
// Per-vertex: mc_Entity (packed blockId + renderType), midBlock, lightValue
|
||||
memPutInt(ptr + MC_ENTITY_OFFSET, ((ctx.blockId + 1) << 1) | (ctx.renderType & 1));
|
||||
final int midBlock = ExtendedDataHelper.computeMidBlock(vertex.x, vertex.y, vertex.z, ctx.localPosX, ctx.localPosY, ctx.localPosZ);
|
||||
memPutInt(ptr + MID_BLOCK_OFFSET, midBlock);
|
||||
memPutByte(ptr + MID_BLOCK_OFFSET + 3, ctx.lightValue);
|
||||
|
||||
// Per-quad: midTexCoord, blockId, normal, tangent
|
||||
// Per-quad: midTexCoord, normal, tangent
|
||||
if (vertexCount == 4) {
|
||||
vertexCount = 0;
|
||||
|
||||
@@ -98,11 +98,6 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
|
||||
memPutInt(ptr + MID_TEX_OFFSET - STRIDE * 2, midUV);
|
||||
memPutInt(ptr + MID_TEX_OFFSET - STRIDE * 3, midUV);
|
||||
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET, ctx.blockId);
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET - STRIDE, ctx.blockId);
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET - STRIDE * 2, ctx.blockId);
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET - STRIDE * 3, ctx.blockId);
|
||||
|
||||
quad.setup(ptr, STRIDE);
|
||||
NormalHelper.computeFaceNormal(normal, quad);
|
||||
final int packedNormal = NormI8.pack(normal);
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexType;
|
||||
* mc_midTexCoord ushort[2] - quad center UV (average of 4 vertices)
|
||||
* at_tangent byte[4] - tangent vector, normalized
|
||||
* iris_Normal byte[3]+1 - face normal, normalized
|
||||
* mc_Entity short[2] - (blockId, renderType)
|
||||
* mc_Entity uint - packed as ((blockId + 1) << 1) | (renderType & 1)
|
||||
* at_midBlock byte[4] - (xyz offset from block center, lightValue)
|
||||
*/
|
||||
public class IrisExtendedChunkVertexType implements ChunkVertexType {
|
||||
@@ -25,7 +25,7 @@ public class IrisExtendedChunkVertexType implements ChunkVertexType {
|
||||
.addElement("mc_midTexCoord", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.UNSIGNED_SHORT, 2, false, false)
|
||||
.addElement("at_tangent", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.BYTE, 4, true, false)
|
||||
.addElement("iris_Normal", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.BYTE, 3, true, false)
|
||||
.addElement("mc_Entity", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.SHORT, 2, false, false)
|
||||
.addElement("mc_Entity", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.UNSIGNED_INT, 1, false, true)
|
||||
.addElement("at_midBlock", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.BYTE, 4, false, false)
|
||||
.build();
|
||||
|
||||
|
||||
@@ -43,6 +43,20 @@ public class CTMUtils {
|
||||
private static Overrides newOverrides = null;
|
||||
private static TileLoader tileLoader;
|
||||
|
||||
private static final ThreadLocal<TileOverrideImpl.CTMCompact> CURRENT_COMPACT = new ThreadLocal<>();
|
||||
|
||||
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact) {
|
||||
CURRENT_COMPACT.set(compact);
|
||||
}
|
||||
|
||||
public static TileOverrideImpl.CTMCompact getCurrentCompact() {
|
||||
return CURRENT_COMPACT.get();
|
||||
}
|
||||
|
||||
public static void clearCurrentCompact() {
|
||||
CURRENT_COMPACT.remove();
|
||||
}
|
||||
|
||||
private static final StampedLock lock = new StampedLock();
|
||||
|
||||
private static class StateAndIterator {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
|
||||
public class CompactConnectingCtmProperties {
|
||||
|
||||
private final boolean innerSeams;
|
||||
private final String orientation;
|
||||
private final Int2IntMap tileReplacementMap = new Int2IntOpenHashMap();
|
||||
|
||||
public CompactConnectingCtmProperties(PropertiesFile properties) {
|
||||
this.innerSeams = properties.getBoolean("innerSeams", false);
|
||||
this.orientation = properties.getString("orientation", "none").toLowerCase();
|
||||
|
||||
for (int i = 0; i < 47; i++) {
|
||||
String val = properties.getString("ctm." + i, "");
|
||||
if (!val.isEmpty()) {
|
||||
try {
|
||||
int tileIdx = Integer.parseInt(val.trim());
|
||||
tileReplacementMap.put(i, tileIdx);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getInnerSeams() {
|
||||
return innerSeams;
|
||||
}
|
||||
|
||||
public String getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public Int2IntMap getTileReplacementMap() {
|
||||
return tileReplacementMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
public final class CompactCtmQuadProcessor {
|
||||
|
||||
private static final int LEFT = 0;
|
||||
private static final int UP_LEFT = 1;
|
||||
private static final int UP = 2;
|
||||
private static final int UP_RIGHT = 3;
|
||||
private static final int RIGHT = 4;
|
||||
private static final int DOWN_RIGHT = 5;
|
||||
private static final int DOWN = 6;
|
||||
private static final int DOWN_LEFT = 7;
|
||||
|
||||
private static final int SPRITE_DEFAULT = 0;
|
||||
private static final int SPRITE_ALL = 1;
|
||||
private static final int SPRITE_HORIZONTAL = 2;
|
||||
private static final int SPRITE_VERTICAL = 3;
|
||||
private static final int SPRITE_MIXED = 4;
|
||||
|
||||
private static final int[] CTM_47_BY_CONNECTIONS = {
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
16,18,16,18,6,46,6,21,16,18,16,18,28,9,28,22,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
37,40,37,40,30,8,30,34,37,40,37,40,25,23,25,45,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
16,42,16,42,6,20,6,10,16,42,16,42,28,35,28,44,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
37,38,37,38,30,11,30,32,37,38,37,38,25,33,25,26
|
||||
};
|
||||
|
||||
/**
|
||||
* Face-local basis:
|
||||
* right = increasing U on the face
|
||||
* down = increasing V on the face
|
||||
*/
|
||||
private static final FaceBasis[] FACE_BASIS = {
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 0: Y-
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 1: Y+
|
||||
new FaceBasis(-1, 0, 0, 0, -1, 0), // 2: Z-
|
||||
new FaceBasis( 1, 0, 0, 0, -1, 0), // 3: Z+
|
||||
new FaceBasis( 0, 0, 1, 0, -1, 0), // 4: X-
|
||||
new FaceBasis( 0, 0, -1, 0, -1, 0) // 5: X+
|
||||
};
|
||||
|
||||
/**
|
||||
* Neighbor offsets in face-local coordinates:
|
||||
* 0 = left, 1 = up-left, 2 = up, 3 = up-right,
|
||||
* 4 = right, 5 = down-right, 6 = down, 7 = down-left
|
||||
*/
|
||||
private static final int[] RIGHT_FACTOR = {-1, -1, 0, 1, 1, 1, 0, -1};
|
||||
private static final int[] DOWN_FACTOR = { 0, -1, -1, -1, 0, 1, 1, 1};
|
||||
|
||||
/**
|
||||
* Quadrant side references:
|
||||
* 0 = top-left
|
||||
* 1 = top-right
|
||||
* 2 = bottom-right
|
||||
* 3 = bottom-left
|
||||
*/
|
||||
private static final int[] QUAD_A = {LEFT, UP, RIGHT, DOWN};
|
||||
private static final int[] QUAD_B = {UP, RIGHT, DOWN, LEFT};
|
||||
private static final int[] QUAD_DIAG = {UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT};
|
||||
|
||||
private final IIcon[] sprites;
|
||||
private final CompactConnectingCtmProperties props;
|
||||
|
||||
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props) {
|
||||
this.sprites = new IIcon[5];
|
||||
if (sprites != null) {
|
||||
System.arraycopy(sprites, 0, this.sprites, 0, Math.min(sprites.length, this.sprites.length));
|
||||
}
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public boolean processFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon origIcon) {
|
||||
int connections = CtmConnectionHelper.getConnections(world, x, y, z, face, props.getInnerSeams());
|
||||
|
||||
int ctmIndex = CTM_47_BY_CONNECTIONS[connections & 0xFF];
|
||||
IIcon replacement = getReplacementIcon(ctmIndex);
|
||||
if (replacement != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connections == 0 || connections == 0xFF) {
|
||||
IIcon sprite = sprites[getCompactSpriteIndex(connections)];
|
||||
if (sprite != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, sprite);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
renderSplitFace(rb, world, x, y, z, face, connections);
|
||||
return true;
|
||||
}
|
||||
|
||||
private IIcon getReplacementIcon(int ctmIndex) {
|
||||
Int2IntMap map = props.getTileReplacementMap();
|
||||
if (map == null || !map.containsKey(ctmIndex)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int spriteIndex = map.get(ctmIndex);
|
||||
if (spriteIndex < 0 || spriteIndex >= sprites.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprites[spriteIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact 5-sprite variant:
|
||||
* 0 = default / isolated / mixed
|
||||
* 1 = all neighbors
|
||||
* 2 = horizontal line
|
||||
* 3 = vertical line
|
||||
*/
|
||||
private int getCompactSpriteIndex(int connections) {
|
||||
if (connections == 0xFF) {
|
||||
return SPRITE_ALL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << LEFT) | (1 << RIGHT))) == ((1 << LEFT) | (1 << RIGHT))) {
|
||||
return SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << UP) | (1 << DOWN))) == ((1 << UP) | (1 << DOWN))) {
|
||||
return SPRITE_VERTICAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
private void renderSplitFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, int connections) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
|
||||
double minX = rb.renderMinX;
|
||||
double minY = rb.renderMinY;
|
||||
double minZ = rb.renderMinZ;
|
||||
double maxX = rb.renderMaxX;
|
||||
double maxY = rb.renderMaxY;
|
||||
double maxZ = rb.renderMaxZ;
|
||||
|
||||
for (int q = 0; q < 4; q++) {
|
||||
IIcon icon = sprites[getQuadrantSprite(q, connections)];
|
||||
if (icon == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setQuadrantBounds(rb, face, q, minX, minY, minZ, maxX, maxY, maxZ);
|
||||
rb.overrideBlockTexture = icon;
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
}
|
||||
|
||||
rb.overrideBlockTexture = null;
|
||||
rb.setRenderBounds(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
private void renderWholeFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon icon) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
IIcon saved = rb.overrideBlockTexture;
|
||||
rb.overrideBlockTexture = icon;
|
||||
try {
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
} finally {
|
||||
rb.overrideBlockTexture = saved;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderFace(RenderBlocks rb, Block block, int x, int y, int z, int face, IIcon icon) {
|
||||
boolean originalFlip = rb.field_152631_f;
|
||||
rb.field_152631_f = (face == 2 || face == 5);
|
||||
try {
|
||||
switch (face) {
|
||||
case 0 -> rb.renderFaceYNeg(block, x, y, z, icon);
|
||||
case 1 -> rb.renderFaceYPos(block, x, y, z, icon);
|
||||
case 2 -> rb.renderFaceZNeg(block, x, y, z, icon);
|
||||
case 3 -> rb.renderFaceZPos(block, x, y, z, icon);
|
||||
case 4 -> rb.renderFaceXNeg(block, x, y, z, icon);
|
||||
case 5 -> rb.renderFaceXPos(block, x, y, z, icon);
|
||||
}
|
||||
} finally {
|
||||
rb.field_152631_f = originalFlip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quadrants are always interpreted as:
|
||||
* 0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
|
||||
* in the face-local view.
|
||||
*/
|
||||
private void setQuadrantBounds(RenderBlocks rb, int face, int quadrant,
|
||||
double minX, double minY, double minZ,
|
||||
double maxX, double maxY, double maxZ) {
|
||||
double midX = (minX + maxX) * 0.5;
|
||||
double midY = (minY + maxY) * 0.5;
|
||||
double midZ = (minZ + maxZ) * 0.5;
|
||||
|
||||
switch (face) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, minY, minZ, midX, maxY, midZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(midX, minY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(midX, minY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, midZ, midX, maxY, maxZ);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (quadrant == 0) rb.setRenderBounds(midX, midY, minZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, minZ, midX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, minZ, midX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(midX, minY, minZ, maxX, midY, maxZ);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, minZ, midX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(midX, midY, minZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(midX, minY, minZ, maxX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, minZ, midX, midY, maxZ);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, midZ, maxX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, minZ, maxX, midY, midZ);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, minZ, maxX, midY, midZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, midZ, maxX, midY, maxZ);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A quadrant is built from two cardinal sides and the diagonal between them.
|
||||
*/
|
||||
private int getQuadrantSprite(int quadrant, int connections) {
|
||||
int sideA = QUAD_A[quadrant];
|
||||
int sideB = QUAD_B[quadrant];
|
||||
int diag = QUAD_DIAG[quadrant];
|
||||
|
||||
boolean connA = (connections & (1 << sideA)) != 0;
|
||||
boolean connB = (connections & (1 << sideB)) != 0;
|
||||
|
||||
if (connA && connB) {
|
||||
return (connections & (1 << diag)) != 0 ? SPRITE_ALL : SPRITE_MIXED;
|
||||
}
|
||||
|
||||
if (connA ^ connB) {
|
||||
int connectedBit = connA ? sideA : sideB;
|
||||
return (connectedBit == LEFT || connectedBit == RIGHT) ? SPRITE_VERTICAL : SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
@Desugar
|
||||
private record FaceBasis(int rightX, int rightY, int rightZ, int downX, int downY, int downZ) { }
|
||||
|
||||
private static final class CtmConnectionHelper {
|
||||
|
||||
public static int getConnections(IBlockAccess world, int x, int y, int z, int face, boolean innerSeams) {
|
||||
Block self = world.getBlock(x, y, z);
|
||||
FaceBasis basis = FACE_BASIS[face];
|
||||
|
||||
int bits = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int dx = RIGHT_FACTOR[i] * basis.rightX + DOWN_FACTOR[i] * basis.downX;
|
||||
int dy = RIGHT_FACTOR[i] * basis.rightY + DOWN_FACTOR[i] * basis.downY;
|
||||
int dz = RIGHT_FACTOR[i] * basis.rightZ + DOWN_FACTOR[i] * basis.downZ;
|
||||
|
||||
int nx = x + dx;
|
||||
int ny = y + dy;
|
||||
int nz = z + dz;
|
||||
|
||||
if (world.getBlock(nx, ny, nz) != self) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (innerSeams && !shouldRenderInnerSeam(world, nx, ny, nz, face)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bits |= (1 << i);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static boolean shouldRenderInnerSeam(IBlockAccess world, int x, int y, int z, int face) {
|
||||
int[] normal = RenderBlockState.NORMALS[face];
|
||||
int sx = x + normal[0];
|
||||
int sy = y + normal[1];
|
||||
int sz = z + normal[2];
|
||||
return world.getBlock(sx, sy, sz).shouldSideBeRendered(world, sx, sy, sz, face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
|
||||
abstract class TileOverride implements Comparable<TileOverride> {
|
||||
public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
@@ -80,6 +80,7 @@ abstract class TileOverride implements Comparable<TileOverride> {
|
||||
tileLoader);
|
||||
case "sandstone", "top" -> override = new TileOverrideImpl.Top(properties, tileLoader);
|
||||
case "repeat", "pattern" -> override = new TileOverrideImpl.Repeat(properties, tileLoader);
|
||||
case "ctm_compact", "compact" -> override = new TileOverrideImpl.CTMCompact(properties, tileLoader);
|
||||
default -> properties.error("unknown method \"%s\"", method);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
import com.prupe.mcpatcher.mal.util.WeightedIndex;
|
||||
|
||||
class TileOverrideImpl {
|
||||
public class TileOverrideImpl {
|
||||
|
||||
final static class CTM extends TileOverride {
|
||||
|
||||
@@ -481,4 +481,49 @@ class TileOverrideImpl {
|
||||
return icons[0];
|
||||
}
|
||||
}
|
||||
|
||||
final public static class CTMCompact extends TileOverride {
|
||||
|
||||
private final CompactConnectingCtmProperties properties;
|
||||
private volatile CompactCtmQuadProcessor processor;
|
||||
|
||||
CTMCompact(PropertiesFile propertiesFile, TileLoader tileLoader) {
|
||||
super(propertiesFile, tileLoader);
|
||||
this.properties = new CompactConnectingCtmProperties(propertiesFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethod() {
|
||||
return "compact";
|
||||
}
|
||||
|
||||
@Override
|
||||
String checkTileMap() {
|
||||
return getNumberOfTiles() == 5 ? null : "requires exactly 5 tiles";
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean requiresFace() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
IIcon getTileWorld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
return origIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
IIcon getTileHeld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
return icons.length > 0 ? icons[0] : origIcon;
|
||||
}
|
||||
|
||||
public CompactCtmQuadProcessor getProcessor() {
|
||||
CompactCtmQuadProcessor p = this.processor;
|
||||
if (p == null) {
|
||||
p = new CompactCtmQuadProcessor(icons, properties);
|
||||
this.processor = p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,8 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
}
|
||||
|
||||
public TileOverride go(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
|
||||
this.renderBlockState = renderBlockState;
|
||||
renderBlockState.setFilter(null);
|
||||
currentIcon = origIcon;
|
||||
@@ -121,6 +123,9 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
TileOverride override = next();
|
||||
IIcon newIcon = getTile(override, renderBlockState, origIcon);
|
||||
if (newIcon != null) {
|
||||
if (override instanceof TileOverrideImpl.CTMCompact compact) {
|
||||
CTMUtils.setCurrentCompact(compact);
|
||||
}
|
||||
lastMatchedOverride = override;
|
||||
skipOverrides.add(override);
|
||||
currentIcon = newIcon;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package net.coderbot.iris;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.Tags;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
@@ -13,7 +13,6 @@ import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.block_context.BlockContextHolder;
|
||||
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
|
||||
import net.coderbot.iris.celeritas.IrisCeleritasShaderProvider;
|
||||
import net.coderbot.iris.compat.dh.DHCompat;
|
||||
@@ -915,35 +914,25 @@ public class Iris {
|
||||
ClientRegistry.registerKeyBinding(shaderpackScreenKeybind);
|
||||
}
|
||||
|
||||
static BlockContextHolder contextHolder;
|
||||
|
||||
private static int getShaderMaterialOverrideId(Block block, int meta) {
|
||||
if (contextHolder == null) {
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
|
||||
if (blockMetaMatches == null) {
|
||||
return -1;
|
||||
}
|
||||
contextHolder = new BlockContextHolder(blockMetaMatches);
|
||||
|
||||
}
|
||||
contextHolder.set(block, meta, (short) block.getRenderType());
|
||||
return contextHolder.blockId;
|
||||
}
|
||||
|
||||
public static void setShaderMaterialOverride(Block block, int meta) {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
int blockId = getShaderMaterialOverrideId(block, meta);
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
|
||||
if (blockMetaMatches == null)
|
||||
return;
|
||||
|
||||
if (TessellatorManager.get() instanceof CapturingTessellator tess)
|
||||
tess.setShaderBlockId(blockId);
|
||||
final Int2IntMap metaMap = blockMetaMatches.get(block);
|
||||
final int blockId = metaMap != null ? metaMap.get(meta) : -1;
|
||||
|
||||
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
|
||||
tess.angelica$setShaderOverrideBlockId((short) blockId);
|
||||
}
|
||||
|
||||
public static void resetShaderMaterialOverride() {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (TessellatorManager.get() instanceof CapturingTessellator tess)
|
||||
tess.setShaderBlockId(-1);
|
||||
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
|
||||
tess.angelica$setShaderOverrideBlockId((short) -1);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package net.coderbot.iris.block_context;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
/**
|
||||
* Holds block context for shader material ID lookups.
|
||||
*/
|
||||
public class BlockContextHolder {
|
||||
private final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches;
|
||||
public int blockId;
|
||||
public short renderType;
|
||||
|
||||
public BlockContextHolder(Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches) {
|
||||
this.blockMetaMatches = blockMetaMatches;
|
||||
this.blockId = -1;
|
||||
this.renderType = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the material ID for a block.
|
||||
* @param block The block
|
||||
* @param meta The block metadata
|
||||
* @param renderType The render type
|
||||
*/
|
||||
public void set(Block block, int meta, short renderType) {
|
||||
Int2IntMap metaMap = this.blockMetaMatches != null ? this.blockMetaMatches.get(block) : null;
|
||||
int id = metaMap != null ? metaMap.get(meta) : -1;
|
||||
|
||||
this.blockId = id;
|
||||
this.renderType = renderType;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.blockId = -1;
|
||||
this.renderType = -1;
|
||||
}
|
||||
}
|
||||
@@ -72,9 +72,11 @@ public class CeleritasTerrainPipeline {
|
||||
Optional<ProgramSource> terrainSource,
|
||||
Optional<ProgramSource> translucentSource,
|
||||
Optional<ProgramSource> shadowSource,
|
||||
Optional<ProgramSource> shadowTranslucentSource,
|
||||
CompletableFuture<Map<PatchShaderType, String>> terrainFuture,
|
||||
CompletableFuture<Map<PatchShaderType, String>> translucentFuture,
|
||||
CompletableFuture<Map<PatchShaderType, String>> shadowFuture,
|
||||
CompletableFuture<Map<PatchShaderType, String>> shadowTranslucentFuture,
|
||||
RenderTargets renderTargets,
|
||||
ImmutableSet<Integer> flippedAfterPrepare,
|
||||
ImmutableSet<Integer> flippedAfterTranslucent,
|
||||
@@ -92,6 +94,7 @@ public class CeleritasTerrainPipeline {
|
||||
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_CUTOUT, terrainSource);
|
||||
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_TRANSLUCENT, translucentSource.isPresent() ? translucentSource : terrainSource);
|
||||
gbufferProgramSource.put(IrisTerrainPass.SHADOW, shadowSource);
|
||||
gbufferProgramSource.put(IrisTerrainPass.SHADOW_TRANSLUCENT, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource);
|
||||
|
||||
// Initialize PassInfo, framebuffers, blend modes, and alpha in single pass
|
||||
for (IrisTerrainPass pass : IrisTerrainPass.VALUES) {
|
||||
@@ -101,11 +104,12 @@ public class CeleritasTerrainPipeline {
|
||||
// Set up framebuffer, blend mode, and buffer blend overrides
|
||||
final ProgramId programId = switch (pass) {
|
||||
case GBUFFER_TRANSLUCENT -> ProgramId.Water;
|
||||
case SHADOW_TRANSLUCENT -> ProgramId.ShadowWater;
|
||||
case SHADOW, SHADOW_CUTOUT -> ProgramId.Shadow;
|
||||
default -> ProgramId.Terrain;
|
||||
};
|
||||
|
||||
if (pass == IrisTerrainPass.SHADOW || pass == IrisTerrainPass.SHADOW_CUTOUT) {
|
||||
if (pass.isShadow()) {
|
||||
passInfo.framebuffer = shadowFramebuffer;
|
||||
passInfo.blendModeOverride = programId.getBlendModeOverride();
|
||||
passInfo.bufferBlendOverrides = Collections.emptyList();
|
||||
@@ -145,6 +149,7 @@ public class CeleritasTerrainPipeline {
|
||||
processShaderFuture(terrainFuture, terrainSource, passInfoMap.get(IrisTerrainPass.GBUFFER_SOLID), passInfoMap.get(IrisTerrainPass.GBUFFER_CUTOUT));
|
||||
processShaderFuture(translucentFuture, translucentSource, passInfoMap.get(IrisTerrainPass.GBUFFER_TRANSLUCENT));
|
||||
processShaderFuture(shadowFuture, shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW), passInfoMap.get(IrisTerrainPass.SHADOW_CUTOUT));
|
||||
processShaderFuture(shadowTranslucentFuture, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW_TRANSLUCENT));
|
||||
}
|
||||
|
||||
private void processShaderFuture(@Nullable CompletableFuture<Map<PatchShaderType, String>> future, Optional<ProgramSource> source, PassInfo... targets) {
|
||||
|
||||
@@ -136,8 +136,10 @@ public class IrisCeleritasChunkProgramOverrides {
|
||||
if (celeritasPipeline != null && !celeritasPipeline.hasShadowPass()) {
|
||||
throw new IllegalStateException("Shadow program requested, but shader pack has no shadow pass");
|
||||
}
|
||||
return programs.get(pass.supportsFragmentDiscard() ?
|
||||
IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW);
|
||||
if (pass.isReverseOrder()) {
|
||||
return programs.get(IrisTerrainPass.SHADOW_TRANSLUCENT);
|
||||
}
|
||||
return programs.get(pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW);
|
||||
} else {
|
||||
if (pass.supportsFragmentDiscard()) {
|
||||
return programs.get(IrisTerrainPass.GBUFFER_CUTOUT);
|
||||
|
||||
@@ -187,7 +187,11 @@ public class IrisCeleritasChunkShaderInterface implements ChunkShaderInterface {
|
||||
final boolean isShadow = ShadowRenderingState.areShadowsCurrentlyBeingRendered();
|
||||
final IrisTerrainPass irisPass;
|
||||
if (isShadow) {
|
||||
irisPass = pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW;
|
||||
if (pass.isReverseOrder()) {
|
||||
irisPass = IrisTerrainPass.SHADOW_TRANSLUCENT;
|
||||
} else {
|
||||
irisPass = pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW;
|
||||
}
|
||||
} else if (pass.isReverseOrder()) {
|
||||
irisPass = IrisTerrainPass.GBUFFER_TRANSLUCENT;
|
||||
} else if (pass.supportsFragmentDiscard()) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.embeddedt.embeddium.impl.render.chunk.terrain.TerrainRenderPass;
|
||||
public enum IrisTerrainPass {
|
||||
SHADOW("shadow"),
|
||||
SHADOW_CUTOUT("shadow"),
|
||||
SHADOW_TRANSLUCENT("shadow_water"),
|
||||
GBUFFER_SOLID("gbuffers_terrain"),
|
||||
GBUFFER_CUTOUT("gbuffers_terrain_cutout"),
|
||||
GBUFFER_TRANSLUCENT("gbuffers_water");
|
||||
@@ -23,19 +24,22 @@ public enum IrisTerrainPass {
|
||||
}
|
||||
|
||||
public boolean isShadow() {
|
||||
return this == SHADOW || this == SHADOW_CUTOUT;
|
||||
return this == SHADOW || this == SHADOW_CUTOUT || this == SHADOW_TRANSLUCENT;
|
||||
}
|
||||
|
||||
public TerrainRenderPass toTerrainPass(RenderPassConfiguration<?> config) {
|
||||
return switch (this) {
|
||||
case SHADOW, GBUFFER_SOLID -> config.defaultSolidMaterial().pass;
|
||||
case SHADOW_CUTOUT, GBUFFER_CUTOUT -> config.defaultCutoutMippedMaterial().pass;
|
||||
case GBUFFER_TRANSLUCENT -> config.defaultTranslucentMaterial().pass;
|
||||
case SHADOW_TRANSLUCENT, GBUFFER_TRANSLUCENT -> config.defaultTranslucentMaterial().pass;
|
||||
};
|
||||
}
|
||||
|
||||
public static IrisTerrainPass fromTerrainPass(TerrainRenderPass pass, boolean isShadow) {
|
||||
if (isShadow) {
|
||||
if (pass.isReverseOrder()) {
|
||||
return SHADOW_TRANSLUCENT;
|
||||
}
|
||||
return pass.supportsFragmentDiscard() ? SHADOW_CUTOUT : SHADOW;
|
||||
} else if (pass.supportsFragmentDiscard()) {
|
||||
return GBUFFER_CUTOUT;
|
||||
|
||||
@@ -18,6 +18,7 @@ public enum RenderCondition {
|
||||
HAND_TRANSLUCENT,
|
||||
RAIN_SNOW,
|
||||
WORLD_BORDER,
|
||||
SHADOW_TRANSLUCENT,
|
||||
// NB: Must be last due to implementation details of DeferredWorldRenderingPipeline
|
||||
SHADOW
|
||||
}
|
||||
@@ -220,11 +220,13 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
final Optional<ProgramSource> terrainSource = first(programs.getGbuffersTerrain(), programs.getGbuffersTexturedLit(), programs.getGbuffersTextured(), programs.getGbuffersBasic());
|
||||
final Optional<ProgramSource> translucentSource = first(programs.getGbuffersWater(), terrainSource);
|
||||
final Optional<ProgramSource> shadowSource = programs.getShadow();
|
||||
final Optional<ProgramSource> shadowTranslucentSource = first(programs.getShadowWater(), shadowSource);
|
||||
|
||||
// Celeritas terrain transform futures
|
||||
final CompletableFuture<Map<PatchShaderType, String>> celeritasTerrainFuture = terrainSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
|
||||
final CompletableFuture<Map<PatchShaderType, String>> celeritasTranslucentFuture = translucentSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
|
||||
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowFuture = shadowSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
|
||||
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowTranslucentFuture = shadowTranslucentSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
|
||||
|
||||
this.cloudSetting = programs.getPackDirectives().getCloudSetting();
|
||||
this.shouldRenderUnderwaterOverlay = programs.getPackDirectives().underwaterOverlay();
|
||||
@@ -388,6 +390,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
null, null, ProgramId.Weather,
|
||||
// world border uses textured_lit even though it has no lightmap :/
|
||||
null, ProgramId.TexturedLit, ProgramId.TexturedLit,
|
||||
ProgramId.ShadowWater, ProgramId.ShadowWater, ProgramId.ShadowWater,
|
||||
ProgramId.Shadow, ProgramId.Shadow, ProgramId.Shadow
|
||||
};
|
||||
|
||||
@@ -431,7 +434,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
return cachedPasses.computeIfAbsent(Pair.of(id, availability), p -> {
|
||||
final ProgramSource source = resolver.resolveNullable(p.getLeft());
|
||||
|
||||
if (condition == RenderCondition.SHADOW) {
|
||||
if (condition == RenderCondition.SHADOW || condition == RenderCondition.SHADOW_TRANSLUCENT) {
|
||||
if (!shadowDirectives.isShadowEnabled().orElse(shadowRenderTargets != null)) {
|
||||
// shadow is not used
|
||||
return null;
|
||||
@@ -448,7 +451,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
}
|
||||
|
||||
try {
|
||||
return createPass(source, availability, condition == RenderCondition.SHADOW, finalId);
|
||||
return createPass(source, availability,
|
||||
condition == RenderCondition.SHADOW || condition == RenderCondition.SHADOW_TRANSLUCENT, finalId);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create pass for " + source.getName() + " for rendering condition "
|
||||
+ condition + " specialized to input availability " + availability, e);
|
||||
@@ -475,7 +479,9 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
this.shadowRenderer = new ShadowRenderer(programs.getShadow().orElse(null),
|
||||
programs.getPackDirectives(), shadowRenderTargets, shadowCompositeRenderer);
|
||||
Program shadowProgram = table.match(RenderCondition.SHADOW, new InputAvailability(true, true)).getProgram();
|
||||
shadowRenderer.setUsesImages(shadowProgram != null && shadowProgram.getActiveImages() > 0);
|
||||
Program shadowWaterProgram = table.match(RenderCondition.SHADOW_TRANSLUCENT, new InputAvailability(true, true)).getProgram();
|
||||
shadowRenderer.setUsesImages((shadowProgram != null && shadowProgram.getActiveImages() > 0)
|
||||
|| (shadowWaterProgram != null && shadowWaterProgram.getActiveImages() > 0));
|
||||
} else {
|
||||
shadowRenderer = null;
|
||||
}
|
||||
@@ -592,7 +598,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
terrainSource,
|
||||
translucentSource,
|
||||
shadowSource,
|
||||
celeritasTerrainFuture, celeritasTranslucentFuture, celeritasShadowFuture,
|
||||
shadowTranslucentSource,
|
||||
celeritasTerrainFuture, celeritasTranslucentFuture, celeritasShadowFuture, celeritasShadowTranslucentFuture,
|
||||
renderTargets, flippedAfterPrepare, flippedAfterTranslucent,
|
||||
celeritasShadowFb);
|
||||
|
||||
@@ -699,7 +706,10 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
|
||||
private RenderCondition getCondition(WorldRenderingPhase phase) {
|
||||
if (isRenderingShadow) {
|
||||
return RenderCondition.SHADOW;
|
||||
return switch (phase) {
|
||||
case TERRAIN_TRANSLUCENT, TRIPWIRE -> RenderCondition.SHADOW_TRANSLUCENT;
|
||||
default -> RenderCondition.SHADOW;
|
||||
};
|
||||
}
|
||||
|
||||
if (special != null) {
|
||||
|
||||
@@ -428,6 +428,7 @@ public class ShaderTransformer {
|
||||
// Handle mc_midTexCoord for Celeritas
|
||||
patchMultiTexCoord3(transformer, parameters);
|
||||
replaceMidTexCoord(transformer, IrisExtendedChunkVertexType.MID_TEX_SCALE);
|
||||
replaceMCEntity(transformer, parameters);
|
||||
applyIntelHd4000Workaround(transformer);
|
||||
break;
|
||||
case COMPOSITE:
|
||||
@@ -494,6 +495,55 @@ public class ShaderTransformer {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces shader-declared mc_Entity (vec2/ivec2/float/int/etc.) with upstream-compatible unpacking
|
||||
* from a single uint attribute. The uint is packed as ((blockId + 1) << 1) | (renderType & 1).
|
||||
*/
|
||||
public static void replaceMCEntity(Transformer transformer, Parameters parameters) {
|
||||
if (parameters.type != ShaderType.VERTEX) return;
|
||||
|
||||
final int type = transformer.findType("mc_Entity");
|
||||
if (type != 0) {
|
||||
transformer.removeVariable("mc_Entity");
|
||||
}
|
||||
transformer.replaceExpression("mc_Entity", "iris_Entity");
|
||||
switch (type) {
|
||||
case 0:
|
||||
case GLSLLexer.BOOL:
|
||||
return;
|
||||
case GLSLLexer.FLOAT:
|
||||
transformer.injectFunction("float iris_Entity = float(int(mc_Entity >> 1u) - 1);");
|
||||
break;
|
||||
case GLSLLexer.VEC2:
|
||||
transformer.injectFunction("vec2 iris_Entity = vec2(int(mc_Entity >> 1u) - 1, mc_Entity & 1u);");
|
||||
break;
|
||||
case GLSLLexer.VEC3:
|
||||
transformer.injectFunction("vec3 iris_Entity = vec3(int(mc_Entity >> 1u) - 1, mc_Entity & 1u, 0.0);");
|
||||
break;
|
||||
case GLSLLexer.VEC4:
|
||||
transformer.injectFunction("vec4 iris_Entity = vec4(int(mc_Entity >> 1u) - 1, mc_Entity & 1u, 0.0, 1.0);");
|
||||
break;
|
||||
case GLSLLexer.UINT:
|
||||
transformer.injectFunction("uint iris_Entity = uint(int(mc_Entity >> 1u) - 1);");
|
||||
break;
|
||||
case GLSLLexer.INT:
|
||||
transformer.injectFunction("int iris_Entity = int(mc_Entity >> 1u) - 1;");
|
||||
break;
|
||||
case GLSLLexer.IVEC2:
|
||||
transformer.injectFunction("ivec2 iris_Entity = ivec2(int(mc_Entity >> 1u) - 1, mc_Entity & 1u);");
|
||||
break;
|
||||
case GLSLLexer.IVEC3:
|
||||
transformer.injectFunction("ivec3 iris_Entity = ivec3(int(mc_Entity >> 1u) - 1, mc_Entity & 1u, 0);");
|
||||
break;
|
||||
case GLSLLexer.IVEC4:
|
||||
transformer.injectFunction("ivec4 iris_Entity = ivec4(int(mc_Entity >> 1u) - 1, mc_Entity & 1u, 0, 1);");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Got an invalid format mc_Entity (type token " + type + ").");
|
||||
}
|
||||
transformer.injectVariable("in uint mc_Entity;");
|
||||
}
|
||||
|
||||
public static void addIfNotExists(Transformer transformer, String name, String code) {
|
||||
if (!transformer.hasVariable(name)) {
|
||||
transformer.injectVariable(code);
|
||||
|
||||
@@ -16,6 +16,7 @@ public class ProgramSet {
|
||||
private final PackDirectives packDirectives;
|
||||
|
||||
private final ProgramSource shadow;
|
||||
private final ProgramSource shadowWater;
|
||||
private final ComputeSource[] shadowCompute;
|
||||
|
||||
private final ProgramSource[] shadowcomp;
|
||||
@@ -80,6 +81,8 @@ public class ProgramSet {
|
||||
// - https://github.com/IrisShaders/Iris/issues/987
|
||||
this.shadow = readProgramSource(directory, sourceProvider, "shadow", this, shaderProperties,
|
||||
BlendModeOverride.OFF);
|
||||
this.shadowWater = readProgramSource(directory, sourceProvider, "shadow_water", this, shaderProperties,
|
||||
BlendModeOverride.OFF);
|
||||
this.shadowCompute = readComputeArray(directory, sourceProvider, "shadow");
|
||||
|
||||
this.shadowcomp = readProgramArray(directory, sourceProvider, "shadowcomp", shaderProperties);
|
||||
@@ -218,6 +221,7 @@ public class ProgramSet {
|
||||
List<ComputeSource> computes = new ArrayList<>();
|
||||
|
||||
programs.add(shadow);
|
||||
programs.add(shadowWater);
|
||||
programs.addAll(Arrays.asList(shadowcomp));
|
||||
programs.addAll(Arrays.asList(begin));
|
||||
programs.addAll(Arrays.asList(prepare));
|
||||
@@ -302,6 +306,10 @@ public class ProgramSet {
|
||||
return shadow.requireValid();
|
||||
}
|
||||
|
||||
public Optional<ProgramSource> getShadowWater() {
|
||||
return shadowWater.requireValid();
|
||||
}
|
||||
|
||||
public ProgramSource[] getShadowComposite() {
|
||||
return shadowcomp;
|
||||
}
|
||||
@@ -413,6 +421,7 @@ public class ProgramSet {
|
||||
public Optional<ProgramSource> get(ProgramId programId) {
|
||||
return switch (programId) {
|
||||
case Shadow -> getShadow();
|
||||
case ShadowWater -> first(getShadowWater(), getShadow());
|
||||
case Basic -> getGbuffersBasic();
|
||||
case Line -> gbuffersLine.requireValid();
|
||||
case Textured -> getGbuffersTextured();
|
||||
|
||||
@@ -14,6 +14,7 @@ public enum ProgramId {
|
||||
Shadow(ProgramGroup.Shadow, ""),
|
||||
ShadowSolid(ProgramGroup.Shadow, "solid", Shadow),
|
||||
ShadowCutout(ProgramGroup.Shadow, "cutout", Shadow),
|
||||
ShadowWater(ProgramGroup.Shadow, "water", Shadow, BlendModeOverride.OFF),
|
||||
|
||||
Basic(ProgramGroup.Gbuffers, "basic"),
|
||||
Line(ProgramGroup.Gbuffers, "line", Basic),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package net.coderbot.iris.vertices;
|
||||
|
||||
public final class ExtendedDataHelper {
|
||||
// TODO: Resolve render types for normal blocks?
|
||||
public static final short BLOCK_RENDER_TYPE = -1;
|
||||
// Packed mc_Entity only preserves one terrain/fluid bit, so normal blocks must encode as 0.
|
||||
public static final short BLOCK_RENDER_TYPE = 0;
|
||||
/** All fluids have a ShadersMod render type of 1, to match behavior of Minecraft 1.7 and earlier. */
|
||||
public static final short FLUID_RENDER_TYPE = 1;
|
||||
|
||||
|
||||
+89
@@ -6,6 +6,9 @@ import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.prupe.mcpatcher.ctm.CTMUtils;
|
||||
import com.prupe.mcpatcher.ctm.CompactCtmQuadProcessor;
|
||||
import com.prupe.mcpatcher.ctm.TileOverrideImpl;
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
@@ -17,6 +20,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -177,4 +181,89 @@ public abstract class MixinRenderBlocks {
|
||||
@Local(ordinal = 0) Tessellator tessellator) {
|
||||
((StateAwareTessellator)tessellator).angelica$setAppliedAo(false);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public IBlockAccess blockAccess;
|
||||
|
||||
@Unique
|
||||
private boolean compactCtmRendering = false;
|
||||
|
||||
@Unique
|
||||
private CompactCtmQuadProcessor getCompactProcessor() {
|
||||
TileOverrideImpl.CTMCompact compact = CTMUtils.getCurrentCompact();
|
||||
if (compact == null) {
|
||||
return null;
|
||||
}
|
||||
return compact.getProcessor();
|
||||
}
|
||||
|
||||
private void handleCompactCtmFace(int face, Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
// recursion protection
|
||||
if (compactCtmRendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.blockAccess == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompactCtmQuadProcessor processor = getCompactProcessor();
|
||||
if (processor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
compactCtmRendering = true;
|
||||
try {
|
||||
boolean handled = processor.processFace(
|
||||
(RenderBlocks)(Object)this,
|
||||
this.blockAccess,
|
||||
MathHelper.floor_double(x),
|
||||
MathHelper.floor_double(y),
|
||||
MathHelper.floor_double(z),
|
||||
face,
|
||||
icon
|
||||
);
|
||||
|
||||
if (handled) {
|
||||
ci.cancel();
|
||||
}
|
||||
} finally {
|
||||
compactCtmRendering = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(0, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(1, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(2, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(3, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(4, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(5, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderStandardBlock(Lnet/minecraft/block/Block;III)Z", at = @At("RETURN"))
|
||||
private void compactCtm_resetAfterBlock(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
}
|
||||
}
|
||||
+19
@@ -14,6 +14,12 @@ public class MixinTessellator implements StateAwareTessellator {
|
||||
@Unique
|
||||
private final IntArrayList vertexStates = new IntArrayList();
|
||||
|
||||
@Unique
|
||||
private final IntArrayList shaderOverrideBlockIds = new IntArrayList();
|
||||
|
||||
@Unique
|
||||
private short currentShaderOverrideBlockId = -1;
|
||||
|
||||
@Unique
|
||||
private boolean appliedAo;
|
||||
|
||||
@@ -33,11 +39,14 @@ public class MixinTessellator implements StateAwareTessellator {
|
||||
state |= StateAwareTessellator.RENDERED_WITH_VANILLA_AO;
|
||||
}
|
||||
this.vertexStates.add(state);
|
||||
this.shaderOverrideBlockIds.add(currentShaderOverrideBlockId);
|
||||
}
|
||||
|
||||
@Inject(method = "reset", at = @At("RETURN"))
|
||||
private void resetVertexStates(CallbackInfo ci) {
|
||||
this.vertexStates.clear();
|
||||
this.shaderOverrideBlockIds.clear();
|
||||
this.currentShaderOverrideBlockId = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,4 +58,14 @@ public class MixinTessellator implements StateAwareTessellator {
|
||||
public int[] angelica$getVertexStates() {
|
||||
return this.vertexStates.elements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] angelica$getShaderOverrideBlockIds() {
|
||||
return this.shaderOverrideBlockIds.elements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$setShaderOverrideBlockId(short blockId) {
|
||||
this.currentShaderOverrideBlockId = blockId;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArgs;
|
||||
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
|
||||
|
||||
/**
|
||||
* Fix enchantment glint not rendering on item edges by using the actual icon dimensions
|
||||
* instead of hardcoded 256x256. This ensures the edge geometry calculation matches the
|
||||
* main item render, allowing GL_EQUAL depth test to pass on all edges.
|
||||
*/
|
||||
@Mixin(ItemRenderer.class)
|
||||
public class MixinHeldItemGlintEdges {
|
||||
|
||||
/**
|
||||
* First glint pass (ordinal 1)
|
||||
*/
|
||||
@ModifyArgs(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItemIn2D(Lnet/minecraft/client/renderer/Tessellator;FFFFIIF)V", ordinal = 1)
|
||||
)
|
||||
private void iris$fixFirstGlintDimensions(Args args, EntityLivingBase entity, ItemStack stack, int pass, ItemRenderType type) {
|
||||
IIcon icon = entity.getItemIcon(stack, pass);
|
||||
if (icon != null) {
|
||||
args.set(5, icon.getIconWidth());
|
||||
args.set(6, icon.getIconHeight());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Second glint pass (ordinal 2)
|
||||
*/
|
||||
@ModifyArgs(
|
||||
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItemIn2D(Lnet/minecraft/client/renderer/Tessellator;FFFFIIF)V", ordinal = 2)
|
||||
)
|
||||
private void iris$fixSecondGlintDimensions(Args args, EntityLivingBase entity, ItemStack stack, int pass, ItemRenderType type) {
|
||||
IIcon icon = entity.getItemIcon(stack, pass);
|
||||
if (icon != null) {
|
||||
args.set(5, icon.getIconWidth());
|
||||
args.set(6, icon.getIconHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user