Compare commits

..
11 Commits
27 changed files with 976 additions and 101 deletions

No files matched your search

@@ -1051,6 +1051,7 @@ public class GLStateManager {
case GL11.GL_SHADE_MODEL -> shadeModelState.getValue();
// GL_TEXTURE_2D makes no sense here, but some mod still queries it...
case GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BINDING_2D -> getBoundTextureForServerState();
case GL13.GL_ACTIVE_TEXTURE -> GL13.GL_TEXTURE0 + getActiveTextureUnitForServerState();
case GL11.GL_COLOR_MATERIAL_FACE -> colorMaterialFace.getValue();
case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue();
case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix);
@@ -1059,15 +1060,18 @@ public class GLStateManager {
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> lightModel.colorControl;
case GL14.GL_BLEND_DST_ALPHA -> blendState.getDstAlpha();
case GL14.GL_BLEND_DST_RGB -> blendState.getDstRgb();
case GL11.GL_BLEND_DST, GL14.GL_BLEND_DST_RGB -> blendState.getDstRgb();
case GL14.GL_BLEND_SRC_ALPHA -> blendState.getSrcAlpha();
case GL14.GL_BLEND_SRC_RGB -> blendState.getSrcRgb();
case GL11.GL_BLEND_SRC, GL14.GL_BLEND_SRC_RGB -> blendState.getSrcRgb();
case GL14.GL_BLEND_EQUATION -> blendState.getEquationRgb();
case GL20.GL_BLEND_EQUATION_ALPHA -> blendState.getEquationAlpha();
case GL11.GL_LOGIC_OP_MODE -> logicOpMode.getValue();
case GL11.GL_DRAW_BUFFER -> drawBuffer.getValue();
case GL11.GL_CULL_FACE_MODE -> polygonState.getCullFaceMode();
case GL11.GL_FRONT_FACE -> polygonState.getFrontFace();
case GL11.GL_STENCIL_FUNC -> stencilState.getFuncFront();
case GL11.GL_STENCIL_REF -> stencilState.getRefFront();
case GL11.GL_STENCIL_VALUE_MASK -> stencilState.getValueMaskFront();
@@ -1106,6 +1110,10 @@ public class GLStateManager {
switch (pname) {
case GL11.GL_VIEWPORT -> viewportState.get(params);
case GL11.GL_POLYGON_MODE -> {
params.put(0, polygonState.getFrontMode());
params.put(1, polygonState.getBackMode());
}
default -> {
if (!HAS_MULTIPLE_SET.contains(pname)) {
params.put(0, glGetInteger(pname));
@@ -5214,30 +5222,36 @@ public class GLStateManager {
private static void invalidateDeletedBuffer(int buffer) {
if (buffer == 0) return;
if (boundVBO == buffer) glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (VAOManager.boundEBO == buffer) glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
if (boundPixelUnpackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
if (boundPixelPackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
final boolean locked = acquireDrawLock();
try {
if (boundVBO == buffer) glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (VAOManager.boundEBO == buffer) glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
if (boundPixelUnpackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
if (boundPixelPackBuffer == buffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
// Sweep all VAOs: an unbound VAO may later rebind with a dangling EBO id.
for (VAOManager.VAOData data : VAOManager.vaoMap.values()) {
if (data.ebo == buffer) data.ebo = 0;
VAOManager.onDeleteBuffer(buffer);
} finally {
if (locked) releaseDrawLock();
}
}
public static void glBindBuffer(int target, int buffer) {
if (target == GL15.GL_ARRAY_BUFFER) {
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
boundVBO = buffer;
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
VAOManager.onBindEBO(buffer);
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
boundPixelUnpackBuffer = buffer;
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
boundPixelPackBuffer = buffer;
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
boundVBO = buffer;
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
VAOManager.onBindEBO(buffer);
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
boundPixelUnpackBuffer = buffer;
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
boundPixelPackBuffer = buffer;
}
RENDER_BACKEND.bindBuffer(target, buffer);
} finally {
if (locked) releaseDrawLock();
}
RENDER_BACKEND.bindBuffer(target, buffer);
}
public static void forcePixelUnpackState(PixelUnpackState target) {
@@ -5572,22 +5586,32 @@ public class GLStateManager {
if (array == 0) {
array = defaultVAO;
}
if (boundVAO != array) {
boundVAO = array;
VAOManager.onBindVertexArrayPre(array);
RENDER_BACKEND.bindVertexArray(array);
final boolean locked = acquireDrawLock();
try {
if (boundVAO != array) {
boundVAO = array;
VAOManager.onBindVertexArrayPre(array);
RENDER_BACKEND.bindVertexArray(array);
}
} finally {
if (locked) releaseDrawLock();
}
}
public static void glDeleteVertexArrays(int array) {
VAOManager.onDeleteVertexArray(array);
if (array == boundVAO) {
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
boundVAO = defaultVAO;
VAOManager.onBindVertexArrayPre(defaultVAO);
RENDER_BACKEND.bindVertexArray(defaultVAO);
final boolean locked = acquireDrawLock();
try {
VAOManager.onDeleteVertexArray(array);
if (array == boundVAO) {
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
boundVAO = defaultVAO;
VAOManager.onBindVertexArrayPre(defaultVAO);
RENDER_BACKEND.bindVertexArray(defaultVAO);
}
RENDER_BACKEND.deleteVertexArrays(array);
} finally {
if (locked) releaseDrawLock();
}
RENDER_BACKEND.deleteVertexArrays(array);
}
public static int glGenVertexArrays() {
@@ -15,6 +15,8 @@ public final class VAOManager {
public static final Int2ObjectOpenHashMap<VAOData> vaoMap = new Int2ObjectOpenHashMap<>();
static final Int2ObjectOpenHashMap<VAOData> eboOwners = new Int2ObjectOpenHashMap<>();
private static VAOData current = null;
private static int currentVertexFlags = 0;
private static Attrib[] currentAttribs = null;
@@ -38,7 +40,11 @@ public final class VAOManager {
// Save old VAO data
if (current != null) {
current.vertexFlags = currentVertexFlags;
current.ebo = boundEBO;
if (current.ebo != boundEBO) {
removeEboOwner(current.ebo, current);
current.ebo = boundEBO;
addEboOwner(current.ebo, current);
}
current.attribs = currentAttribs;
current.clientSideEnabledCount = clientSideEnabledCount;
}
@@ -56,7 +62,46 @@ public final class VAOManager {
}
public static void onDeleteVertexArray(int vaoId) {
vaoMap.remove(vaoId);
final VAOData data = vaoMap.remove(vaoId);
if (data == null) return;
removeEboOwner(data.ebo, data);
if (data == current) current = null;
}
public static void onDeleteBuffer(int buffer) {
if (buffer == 0) return;
VAOData owner = eboOwners.remove(buffer);
while (owner != null) {
final VAOData next = owner.nextEboOwner;
owner.nextEboOwner = null;
owner.ebo = 0;
owner = next;
}
}
private static void addEboOwner(int ebo, VAOData data) {
if (ebo == 0) return;
data.nextEboOwner = eboOwners.put(ebo, data);
}
private static void removeEboOwner(int ebo, VAOData data) {
if (ebo == 0) return;
VAOData node = eboOwners.get(ebo);
if (node == data) {
final VAOData next = data.nextEboOwner;
if (next == null) eboOwners.remove(ebo); else eboOwners.put(ebo, next);
data.nextEboOwner = null;
return;
}
while (node != null) {
final VAOData next = node.nextEboOwner;
if (next == data) {
node.nextEboOwner = data.nextEboOwner;
data.nextEboOwner = null;
return;
}
node = next;
}
}
public static void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
@@ -69,6 +114,7 @@ public final class VAOManager {
public int vertexFlags;
public int ebo;
public int clientSideEnabledCount;
private VAOData nextEboOwner;
public VAOData() {
attribs = new Attrib[MAX_ATTRIBS];
@@ -62,11 +62,14 @@ public final class DisplayListVBO {
}
public void render() {
vao.bind();
vao.draw(drawMode, start, count);
vao.unbind();
if (vao == null) return;
vao.bind();
if (count <= 0) { vao.unbind(); return; }
vao.draw(drawMode, start, count);
vao.unbind();
}
public IVertexArrayObject getVAO() {
return vao;
}
@@ -1131,4 +1131,52 @@ class GLSM_PushPop_UnitTest {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
}
@Test
void testBlendFuncGL11AliasesMatchGL14() {
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
verifyState(GL11.GL_BLEND_SRC, GL11.GL_SRC_ALPHA, "Blend Source");
verifyState(GL14.GL_BLEND_SRC_RGB, GL11.GL_SRC_ALPHA, "Blend Source RGB");
verifyState(GL11.GL_BLEND_DST, GL11.GL_ONE_MINUS_SRC_ALPHA, "Blend Destination");
verifyState(GL14.GL_BLEND_DST_RGB, GL11.GL_ONE_MINUS_SRC_ALPHA, "Blend Destination RGB");
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
}
@Test
void testBlendFuncSeparateGL11AliasesTrackRgb() {
GLStateManager.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
verifyState(GL11.GL_BLEND_SRC, GL11.GL_SRC_ALPHA, "Blend Source");
verifyState(GL11.GL_BLEND_DST, GL11.GL_ONE_MINUS_SRC_ALPHA, "Blend Destination");
verifyState(GL14.GL_BLEND_SRC_ALPHA, GL11.GL_ONE, "Blend Source Alpha");
verifyState(GL14.GL_BLEND_DST_ALPHA, GL11.GL_ZERO, "Blend Destination Alpha");
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
}
@Test
void testPolygonStateReadBack() {
GLStateManager.glCullFace(GL11.GL_FRONT);
GLStateManager.glFrontFace(GL11.GL_CW);
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
verifyState(GL11.GL_CULL_FACE_MODE, GL11.GL_FRONT, "Cull Face Mode");
verifyState(GL11.GL_FRONT_FACE, GL11.GL_CW, "Front Face");
verifyState(GL11.GL_POLYGON_MODE, new int[] { GL11.GL_LINE, GL11.GL_LINE }, "Polygon Mode");
GLStateManager.glCullFace(GL11.GL_BACK);
GLStateManager.glFrontFace(GL11.GL_CCW);
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
}
@Test
void testActiveTextureReadBack() {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture");
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture - Reset");
}
}
@@ -0,0 +1,189 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class VAOManagerEboIndexTest {
private static final int DEFAULT_VAO = 0;
private static final int NO_VAO = -1;
private final Int2IntOpenHashMap expectedEbo = new Int2IntOpenHashMap();
private int currentVao;
@BeforeEach
void reset() {
drainManagerState();
expectedEbo.clear();
VAOManager.init(DEFAULT_VAO);
expectedEbo.put(DEFAULT_VAO, 0);
currentVao = DEFAULT_VAO;
}
@AfterEach
void drain() {
drainManagerState();
}
private static void drainManagerState() {
for (int vao : VAOManager.vaoMap.keySet().toIntArray()) {
VAOManager.onDeleteVertexArray(vao);
}
VAOManager.boundEBO = 0;
assertEquals(0, VAOManager.eboOwners.size(), "draining every VAO left entries in the index");
}
private void bindVao(int vao) {
if (currentVao != NO_VAO) expectedEbo.put(currentVao, VAOManager.boundEBO);
VAOManager.onBindVertexArrayPre(vao);
expectedEbo.putIfAbsent(vao, 0);
currentVao = vao;
}
private void bindEbo(int ebo) {
VAOManager.onBindEBO(ebo);
}
private int countOwners(int buffer) {
int owners = 0;
for (int vao : expectedEbo.keySet().toIntArray()) {
if (expectedEbo.get(vao) == buffer) owners++;
}
return owners;
}
private void deleteBuffer(int buffer) {
VAOManager.onDeleteBuffer(buffer);
for (int vao : expectedEbo.keySet().toIntArray()) {
if (expectedEbo.get(vao) == buffer) expectedEbo.put(vao, 0);
}
}
private void deleteVao(int vao) {
VAOManager.onDeleteVertexArray(vao);
expectedEbo.remove(vao);
if (vao == currentVao) currentVao = NO_VAO;
}
private void assertExpectedEbos(int op) {
for (var entry : VAOManager.vaoMap.int2ObjectEntrySet()) {
final int vao = entry.getIntKey();
if (vao == currentVao) continue;
assertEquals(expectedEbo.get(vao), entry.getValue().ebo, () -> "op " + op + ": vao " + vao + " ebo mismatch");
}
}
@Test
void everyVaoKeepsItsExpectedEboUnderRandomOps() {
final Random random = new Random(0xC0FFEE);
final IntArrayList liveVaos = new IntArrayList();
final IntArrayList liveBuffers = new IntArrayList();
int nextVao = 1;
int nextBuffer = 1;
int sharedEboDeletes = 0;
for (int op = 0; op < 200_000; op++) {
switch (random.nextInt(5)) {
case 0 -> {
final int vao = nextVao++;
liveVaos.add(vao);
bindVao(vao);
}
case 1 -> {
if (liveVaos.isEmpty()) break;
bindVao(liveVaos.getInt(random.nextInt(liveVaos.size())));
}
case 2 -> {
if (currentVao == NO_VAO) break;
final int buffer = (random.nextBoolean() && nextBuffer > 8) ? 1 + random.nextInt(nextBuffer - 1) : nextBuffer++;
liveBuffers.add(buffer);
bindEbo(buffer);
}
case 3 -> {
if (liveBuffers.isEmpty()) break;
final int buffer = liveBuffers.removeInt(random.nextInt(liveBuffers.size()));
if (countOwners(buffer) > 1) sharedEboDeletes++;
deleteBuffer(buffer);
}
case 4 -> {
if (liveVaos.isEmpty()) break;
final int vao = liveVaos.removeInt(random.nextInt(liveVaos.size()));
final boolean wasBound = vao == currentVao;
deleteVao(vao);
if (wasBound) bindVao(DEFAULT_VAO);
}
}
assertExpectedEbos(op);
}
assertTrue(sharedEboDeletes > 20, "op stream never freed a multi-owner EBO (hits: " + sharedEboDeletes + ")");
}
@Test
void sharedEboClearsEveryOwner() {
final int ebo = 77;
bindVao(1);
bindEbo(ebo);
bindVao(2);
bindEbo(ebo);
bindVao(3);
bindEbo(ebo);
bindVao(DEFAULT_VAO);
deleteBuffer(ebo);
assertEquals(0, VAOManager.vaoMap.get(1).ebo, "vao 1 kept a freed shared EBO");
assertEquals(0, VAOManager.vaoMap.get(2).ebo, "vao 2 kept a freed shared EBO");
assertEquals(0, VAOManager.vaoMap.get(3).ebo, "vao 3 kept a freed shared EBO");
assertFalse(VAOManager.eboOwners.containsKey(ebo), "index kept owners for a freed EBO");
}
@Test
void sharedEboEntryDropsOnlyAfterItsLastOwnerGoes() {
final int ebo = 88;
bindVao(1);
bindEbo(ebo);
bindVao(2);
bindEbo(ebo);
bindVao(DEFAULT_VAO);
deleteVao(1);
assertTrue(VAOManager.eboOwners.containsKey(ebo), "index dropped a still-live owner");
deleteVao(2);
assertFalse(VAOManager.eboOwners.containsKey(ebo), "index kept an entry after its last owner went away");
}
@Test
void deletingTheBoundVaoDoesNotOrphanAnIndexEntry() {
final int ebo = 99;
bindVao(1);
bindEbo(ebo);
deleteVao(1);
bindVao(DEFAULT_VAO);
assertFalse(VAOManager.eboOwners.containsKey(ebo), "deleting the bound VAO left an unreachable owner in the index");
assertEquals(0, VAOManager.eboOwners.size(), "index leaked an entry for a deleted VAO");
}
@Test
void indexEmptiesOutWhenEverythingIsDeleted() {
for (int vao = 1; vao <= 64; vao++) {
bindVao(vao);
bindEbo(1000 + vao);
}
bindVao(DEFAULT_VAO);
for (int vao = 1; vao <= 64; vao++) {
deleteVao(vao);
}
assertEquals(0, VAOManager.eboOwners.size(), "index leaked entries after every VAO was deleted");
}
}
@@ -0,0 +1,15 @@
package com.gtnewhorizons.angelica.event;
import net.minecraftforge.eventbus.api.bus.EventBus;
import net.minecraftforge.eventbus.api.event.RecordEvent;
public class RenderChunkEvent implements RecordEvent {
public static final EventBus<RenderChunkEvent> BUS = EventBus.create(RenderChunkEvent.class);
private static final RenderChunkEvent INSTANCE = new RenderChunkEvent();
public static void post() {
if (!BUS.hasListeners()) return;
BUS.post(INSTANCE);
}
}
@@ -157,6 +157,7 @@ public enum Mixins implements IMixins {
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.itemrenderer.MixinItemRenderer",
"angelica.itemrenderer.MixinRenderItemFrame",
"angelica.itemrenderer.MixinRenderBlocks"
)
.setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering)),
@@ -292,8 +293,12 @@ public enum Mixins implements IMixins {
, "shaders.MixinLocale"
, "shaders.MixinRender"
, "shaders.MixinRenderBiped"
, "shaders.MixinRenderBlocks_FaceNormals"
, "shaders.MixinRenderBlocks_FallingShading"
, "shaders.MixinRenderBlocks_ItemId"
, "shaders.MixinRenderDragon"
, "shaders.MixinRenderEntityFlame"
, "shaders.MixinRenderFallingBlock"
, "shaders.MixinRendererLivingEntity"
, "shaders.MixinRenderGlobal"
, "shaders.AccessorEntityHorse"
@@ -296,7 +296,7 @@ public final class ClientProxy extends CommonProxy {
event.left.set(i + 4, String.format(
"lc: %d b: %s bl: %d sl: %d rl: %d",
chunk.getTopFilledSegment() + 15,
this.mc.theWorld.getBiomeGenForCoords(bX & 15, bZ & 15).biomeName,
this.mc.theWorld.getBiomeGenForCoords(bX, bZ).biomeName,
chunk.getSavedLightValue(EnumSkyBlock.Block, bX & 15, CompatMathHelper.clampHeight(bY), bZ & 15),
chunk.getSavedLightValue(EnumSkyBlock.Sky, bX & 15, CompatMathHelper.clampHeight(bY), bZ & 15),
chunk.getBlockLightValue(bX & 15, CompatMathHelper.clampHeight(bY), bZ & 15, 0)));
@@ -0,0 +1,83 @@
package com.gtnewhorizons.angelica.rendering;
import net.minecraft.block.Block;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.util.ForgeDirection;
public final class FallingBlockMetaAccess implements IBlockAccess {
private IBlockAccess delegate;
private int x;
private int y;
private int z;
private int metadata;
public FallingBlockMetaAccess set(IBlockAccess delegate, int x, int y, int z, int metadata) {
this.delegate = delegate;
this.x = x;
this.y = y;
this.z = z;
this.metadata = metadata;
return this;
}
public void clear() {
this.delegate = null;
}
@Override
public int getBlockMetadata(int x, int y, int z) {
if (x == this.x && y == this.y && z == this.z) {
return this.metadata;
}
return delegate.getBlockMetadata(x, y, z);
}
@Override
public Block getBlock(int x, int y, int z) {
return delegate.getBlock(x, y, z);
}
@Override
public TileEntity getTileEntity(int x, int y, int z) {
return delegate.getTileEntity(x, y, z);
}
@Override
public int getLightBrightnessForSkyBlocks(int x, int y, int z, int lightValue) {
return delegate.getLightBrightnessForSkyBlocks(x, y, z, lightValue);
}
@Override
public int isBlockProvidingPowerTo(int x, int y, int z, int direction) {
return delegate.isBlockProvidingPowerTo(x, y, z, direction);
}
@Override
public boolean isAirBlock(int x, int y, int z) {
return delegate.isAirBlock(x, y, z);
}
@Override
public BiomeGenBase getBiomeGenForCoords(int x, int z) {
return delegate.getBiomeGenForCoords(x, z);
}
@Override
public int getHeight() {
return delegate.getHeight();
}
@Override
public boolean extendedLevelsInChunkCache() {
return delegate.extendedLevelsInChunkCache();
}
@Override
public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean _default) {
return delegate.isSideSolid(x, y, z, side, _default);
}
}
@@ -0,0 +1,56 @@
package com.gtnewhorizons.angelica.rendering;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.gl.shader.ProgramCreator;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
public final class FallingBlockRendering {
public static boolean active;
private static final FallingBlockMetaAccess META_ACCESS = new FallingBlockMetaAccess();
private FallingBlockRendering() {
}
public static FallingBlockMetaAccess metaAccess(IBlockAccess world, int x, int y, int z, int metadata) {
return META_ACCESS.set(world, x, y, z, metadata);
}
public static void setEntityAttribute(Block block, int metadata) {
if (!shadersActive()) return;
GLStateManager.glVertexAttrib2s(ProgramCreator.MC_ENTITY, (short) blockMaterialId(block, metadata), (short) 0);
}
public static void resetEntityAttribute() {
if (!shadersActive()) return;
GLStateManager.glVertexAttrib2s(ProgramCreator.MC_ENTITY, (short) -1, (short) -1);
}
private static int blockMaterialId(Block block, int metadata) {
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
if (blockMetaMatches == null) return -1;
final Int2IntMap metaMap = blockMetaMatches.get(block);
return metaMap != null ? BlockMaterialMapping.resolveId(metaMap, metadata) : -1;
}
public static boolean isActive() {
return active && shadersActive();
}
private static boolean shadersActive() {
return TessellatorManager.isOnMainThread() && IrisApi.getInstance().isShaderPackInUse();
}
public static boolean skipDirectionalShading() {
return isActive() && BlockRenderingSettings.INSTANCE.shouldDisableDirectionalShading();
}
}
@@ -12,6 +12,7 @@ public class BlockRenderListManager {
private static final BlockMeta key = new BlockMeta();
private static final Object2IntOpenHashMap<BlockMeta> displayListMap = new Object2IntOpenHashMap<>();
private static final int[] itemFrameDisplayLists = new int[8];
private static final int ISBRH_CUTOFF = 40;
@@ -36,6 +37,10 @@ public class BlockRenderListManager {
return displayListMap.getInt(key);
}
public static int getItemFrameDisplayList(int variant) {
return itemFrameDisplayLists[variant];
}
public static int startCompiling() {
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
@@ -43,9 +48,18 @@ public class BlockRenderListManager {
}
public static void endCompiling(int list, Block block, int meta) {
finishCompiling();
displayListMap.put(new BlockMeta(block, meta), list);
}
public static void endItemFrameCompiling(int list, int variant) {
finishCompiling();
itemFrameDisplayLists[variant] = list;
}
private static void finishCompiling() {
DisplayListManager.discardMatrixTransforms();
GLStateManager.glEndList();
displayListMap.put(new BlockMeta(block, meta), list);
}
public static void registerReloadListener(){
@@ -56,6 +70,12 @@ public class BlockRenderListManager {
GLStateManager.glDeleteLists(list, 1);
}
displayListMap.clear();
for (int i = 0; i < itemFrameDisplayLists.length; i++) {
if (itemFrameDisplayLists[i] != 0) {
GLStateManager.glDeleteLists(itemFrameDisplayLists[i], 1);
itemFrameDisplayLists[i] = 0;
}
}
});
}
@@ -7,6 +7,7 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.ChunkPosition;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -16,8 +17,9 @@ import java.util.function.Consumer;
/**
* Thread-safe wrapper around a tile entity map for chunkTileEntityMap.
* May wrap either a plain Object2ObjectOpenHashMap (vanilla) or a ColumnTileEntityMap (cubic chunks).
* Extends HashMap for binary compatibility with mods that cast chunkTileEntityMap to HashMap.
*/
public class ConcurrentTileEntityMap implements Map<ChunkPosition, TileEntity> {
public class ConcurrentTileEntityMap extends HashMap<ChunkPosition, TileEntity> {
private final Object2ObjectOpenHashMap<ChunkPosition, TileEntity> flatDelegate;
private final Map<ChunkPosition, TileEntity> delegate;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
@@ -8,8 +8,10 @@ import net.minecraft.block.BlockLeaves;
import net.minecraft.block.BlockMushroom;
import net.minecraft.block.BlockRedstoneWire;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Fallback {@link MipmapStrategy} assignments for sprites that carry no metadata.
@@ -17,8 +19,8 @@ import java.util.Map;
public final class MipmapStrategies {
private static final Map<Class<?>, MipmapStrategy> CLASS_RULES = new Object2ObjectOpenHashMap<>();
private static final Map<String, MipmapStrategy> SPRITE_RULES = new Object2ObjectOpenHashMap<>();
private static final LinkedList<MipmapStrategy> STRATEGY_STACK = new LinkedList<>();
private static final Map<String, MipmapStrategy> SPRITE_RULES = new ConcurrentHashMap<>();
private static final ThreadLocal<Deque<MipmapStrategy>> STRATEGY_STACK = ThreadLocal.withInitial(LinkedList::new);
static {
CLASS_RULES.put(BlockLeaves.class, MipmapStrategy.DARK_CUTOUT);
@@ -33,16 +35,17 @@ public final class MipmapStrategies {
public static void reset() {
SPRITE_RULES.clear();
STRATEGY_STACK.clear();
STRATEGY_STACK.remove();
}
public static void beginBlock(Block block) {
STRATEGY_STACK.push(resolve(block.getClass()));
STRATEGY_STACK.get().push(resolve(block.getClass()));
}
public static void endBlock() {
if (!STRATEGY_STACK.isEmpty()) {
STRATEGY_STACK.pop();
final Deque<MipmapStrategy> stack = STRATEGY_STACK.get();
if (!stack.isEmpty()) {
stack.pop();
}
}
@@ -51,7 +54,7 @@ public final class MipmapStrategies {
return;
}
MipmapStrategy strategy = STRATEGY_STACK.peek();
MipmapStrategy strategy = STRATEGY_STACK.get().peek();
if (strategy == null && textureType == 0 && iconName.contains("leaves")) {
strategy = MipmapStrategy.DARK_CUTOUT;
}
@@ -59,10 +62,8 @@ public final class MipmapStrategies {
return;
}
final MipmapStrategy existing = SPRITE_RULES.get(iconName);
if (existing == null || strategy.conflictPrecedence() > existing.conflictPrecedence()) {
SPRITE_RULES.put(iconName, strategy);
}
SPRITE_RULES.merge(iconName, strategy,
(existing, incoming) -> incoming.conflictPrecedence() > existing.conflictPrecedence() ? incoming : existing);
}
public static MipmapStrategy inheritedFor(int textureType, String iconName) {
@@ -781,7 +781,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
}
public boolean shouldOverrideShaders() {
return isRenderingWorld && !isRenderingFullScreenPass && !isPostChain && isMainBound;
return isRenderingLevelGeometry() && isMainBound;
}
public boolean isRenderingLevelGeometry() {
return isRenderingWorld && !isRenderingFullScreenPass && !isPostChain;
}
public Pass getActivePassProgram() {
@@ -1,5 +1,9 @@
package net.coderbot.iris.uniforms;
import net.coderbot.iris.Iris;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.minecraft.block.Block;
import net.minecraft.item.ItemStack;
/**
@@ -23,6 +27,13 @@ public class ItemIdManager {
CapturedRenderingState.INSTANCE.setCurrentRenderedItem(id);
}
/**
* Set the item ID for a Block rendered outside of terrain.
*/
public static void setBlockId(Block block, int metadata) {
CapturedRenderingState.INSTANCE.setCurrentRenderedItem(Math.max(0, ItemMaterialHelper.getMaterialId(block, metadata)));
}
/**
* Reset the item ID to 0.
* Used when entering/exiting render sections or before glint rendering.
@@ -30,4 +41,28 @@ public class ItemIdManager {
public static void resetItemId() {
CapturedRenderingState.INSTANCE.setCurrentRenderedItem(0);
}
/**
* The currently bound item ID.
*/
public static int getItemId() {
return CapturedRenderingState.INSTANCE.getCurrentRenderedItem();
}
/**
* Set an already-resolved item ID without performing a lookup.
*/
public static void setItemIdRaw(int id) {
CapturedRenderingState.INSTANCE.setCurrentRenderedItem(id);
}
/**
* Honestly I don't know why there's comments on a lot of these.
* Guess I thought documentation is good but man if you can't figure this out
* you're in trouble.
*/
public static boolean isWorldRenderActive() {
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
return pipeline instanceof DeferredWorldRenderingPipeline drp && drp.isRenderingLevelGeometry();
}
}
@@ -23,6 +23,8 @@ public class ItemMaterialHelper {
private static final Reference2ObjectMap<Item, Int2IntMap> MATERIAL_CACHE = new Reference2ObjectOpenHashMap<>();
private static final int CACHE_MISS_SENTINEL = Integer.MIN_VALUE;
private static final Reference2ObjectMap<Item, NamespacedId> ITEM_NAME_CACHE = new Reference2ObjectOpenHashMap<>();
private static final Reference2ObjectMap<Block, Int2IntMap> BLOCK_MATERIAL_CACHE = new Reference2ObjectOpenHashMap<>();
/**
* Get the material ID for an ItemStack.
* For ItemBlock: checks block.properties first, then item.properties.
@@ -79,6 +81,52 @@ public class ItemMaterialHelper {
return materialId;
}
/**
* Get the material ID for a Block rendered outside of terrain.
*
* @return The material ID, or -1 if not found
*/
public static int getMaterialId(Block block, int metadata) {
if (block == null) {
return -1;
}
Int2IntMap metadataCache = BLOCK_MATERIAL_CACHE.get(block);
if (metadataCache != null) {
int cached = metadataCache.getOrDefault(metadata, CACHE_MISS_SENTINEL);
if (cached != CACHE_MISS_SENTINEL) {
return cached;
}
}
int materialId = lookupBlockMaterialId(block, metadata);
if (metadataCache == null) {
metadataCache = new Int2IntOpenHashMap();
metadataCache.defaultReturnValue(CACHE_MISS_SENTINEL);
BLOCK_MATERIAL_CACHE.put(block, metadataCache);
}
metadataCache.put(metadata, materialId);
return materialId;
}
private static int lookupBlockMaterialId(Block block, int metadata) {
Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
if (blockMetaMatches != null) {
Int2IntMap metaMap = blockMetaMatches.get(block);
if (metaMap != null) {
int id = BlockMaterialMapping.resolveId(metaMap, metadata);
if (id != -1) {
return id;
}
}
}
final Item item = Item.getItemFromBlock(block);
return item != null ? lookupItemPropertiesId(item) : -1;
}
/**
* Perform the actual material ID lookup without caching.
*
@@ -107,16 +155,17 @@ public class ItemMaterialHelper {
}
// Fall back to item.properties
Object2IntFunction<NamespacedId> itemIds = BlockRenderingSettings.INSTANCE.getItemIds();
if (itemIds != null) {
String itemIdString = Item.itemRegistry.getNameForObject(item);
if (itemIdString != null) {
ResourceLocation itemId = new ResourceLocation(itemIdString);
return itemIds.applyAsInt(new NamespacedId(itemId.getResourceDomain(), itemId.getResourcePath()));
}
return lookupItemPropertiesId(item);
}
private static int lookupItemPropertiesId(Item item) {
final Object2IntFunction<NamespacedId> itemIds = BlockRenderingSettings.INSTANCE.getItemIds();
if (itemIds == null) {
return -1;
}
return -1;
final NamespacedId name = getCachedItemName(item);
return name == null ? -1 : itemIds.applyAsInt(name);
}
/**
@@ -126,6 +175,7 @@ public class ItemMaterialHelper {
public static void clearCache() {
MATERIAL_CACHE.clear();
ITEM_NAME_CACHE.clear();
BLOCK_MATERIAL_CACHE.clear();
}
private static NamespacedId getCachedItemName(Item item) {
@@ -20,9 +20,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -194,11 +192,6 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
@Override
public void angelica$bindTexture(ResourceLocation location) { this.bindTexture(location); }
@ModifyConstant(method = "getCharWidth", constant = @Constant(intValue = 7))
private int angelica$maxCharWidth(int original) {
return Integer.MAX_VALUE;
}
@Inject(method = "getCharWidth", at = @At("HEAD"), cancellable = true)
public void getCharWidth(char c, CallbackInfoReturnable<Integer> cir) {
cir.setReturnValue((int) angelica$getBatcher().getCharWidthFine(c));
@@ -0,0 +1,56 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.itemrenderer;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.rendering.items.BlockRenderListManager;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.tileentity.RenderItemFrame;
import net.minecraft.entity.item.EntityItemFrame;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(RenderItemFrame.class)
public class MixinRenderItemFrame {
@WrapOperation(
method = "doRender(Lnet/minecraft/entity/item/EntityItemFrame;DDDFF)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/renderer/tileentity/RenderItemFrame;renderFrameItemAsBlock(Lnet/minecraft/entity/item/EntityItemFrame;)V"
)
)
private void angelica$cacheFrame(RenderItemFrame renderer, EntityItemFrame frame, Operation<Void> original) {
angelica$renderCached(renderer, frame, original, frame.hangingDirection);
}
@WrapOperation(
method = "doRender(Lnet/minecraft/entity/item/EntityItemFrame;DDDFF)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/renderer/tileentity/RenderItemFrame;func_147915_b(Lnet/minecraft/entity/item/EntityItemFrame;)V"
)
)
private void angelica$cacheMapFrame(RenderItemFrame renderer, EntityItemFrame frame, Operation<Void> original) {
angelica$renderCached(renderer, frame, original, 4 + frame.hangingDirection);
}
@Unique
private static void angelica$renderCached(RenderItemFrame renderer, EntityItemFrame frame,
Operation<Void> original, int keyIndex) {
if (GLStateManager.isRecordingDisplayList() || TessellatorManager.isCurrentlyCapturing()
|| TessellatorManager.shouldInterceptDraw(Tessellator.instance)) {
original.call(renderer, frame);
return;
}
int list = BlockRenderListManager.getItemFrameDisplayList(keyIndex);
if (list == 0) {
list = BlockRenderListManager.startCompiling();
original.call(renderer, frame);
BlockRenderListManager.endItemFrameCompiling(list, keyIndex);
}
GLStateManager.glCallList(list);
}
}
@@ -6,6 +6,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.TextureMapExtension;
import com.gtnewhorizons.angelica.utils.MipmapStrategies;
import com.gtnewhorizons.angelica.utils.MipmapStrategy;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
import net.minecraft.block.Block;
@@ -27,7 +29,6 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -74,15 +75,15 @@ public class MixinTextureMap implements TextureMapExtension {
/**
* Brackets each block's icon registration so the sprites it registers can be attributed to it.
*/
@Redirect(
@WrapOperation(
method = "registerIcons",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/Block;registerBlockIcons(Lnet/minecraft/client/renderer/texture/IIconRegister;)V"))
private void angelica$trackBlockIcons(Block block, IIconRegister reg) {
private void angelica$trackBlockIcons(Block block, IIconRegister reg, Operation<Void> original) {
MipmapStrategies.beginBlock(block);
try {
block.registerBlockIcons(reg);
original.call(block, reg);
} finally {
MipmapStrategies.endBlock();
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
import com.gtnewhorizons.angelica.compat.mojang.Camera;
import com.gtnewhorizons.angelica.compat.mojang.GameModeUtil;
import com.gtnewhorizons.angelica.event.RenderChunkEvent;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
import com.gtnewhorizons.angelica.rendering.AngelicaRenderQueue;
@@ -164,6 +165,7 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
} else {
mc.mcProfiler.endStartSection("draw_chunk_layer_translucent");
this.celeritas$renderer.drawChunkLayer(BlockRenderLayer.TRANSLUCENT, camX, camY, camZ);
RenderChunkEvent.post();
}
} finally {
RenderDevice.exitManagedCode();
@@ -8,7 +8,9 @@ import net.coderbot.iris.pipeline.HandRenderer;
import net.coderbot.iris.uniforms.ItemIdManager;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraftforge.client.IItemRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -16,7 +18,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin to handle item rendering in first-person.
* Mixin to handle item rendering in first-person, and to set the currentRenderedItem for every
* item rendered on an entity.
*/
@Mixin(ItemRenderer.class)
public class MixinItemRenderer {
@@ -53,6 +56,30 @@ public class MixinItemRenderer {
ItemIdManager.resetItemId();
}
/**
* Set the item ID for any item rendered on an entity.
*/
@Inject(
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
at = @At("HEAD"),
remap = false
)
private void iris$setEntityItemId(EntityLivingBase entity, ItemStack itemStack, int renderPass, IItemRenderer.ItemRenderType type, CallbackInfo ci) {
ItemIdManager.setItemId(itemStack);
}
/**
* Clear the ID once the item is drawn.
*/
@Inject(
method = "renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;ILnet/minecraftforge/client/IItemRenderer$ItemRenderType;)V",
at = @At("RETURN"),
remap = false
)
private void iris$clearEntityItemId(EntityLivingBase entity, ItemStack itemStack, int renderPass, IItemRenderer.ItemRenderType type, CallbackInfo ci) {
ItemIdManager.resetItemId();
}
/**
* Activate GLINT shader before rendering enchantment glint on held items (third person).
*/
@@ -4,10 +4,8 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.coderbot.iris.uniforms.ItemIdManager;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.entity.RenderBiped;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -39,17 +37,4 @@ public class MixinRenderBiped {
original.call(instance, model);
}
/**
* Set item ID when rendering held items on entities (third person).
*/
@WrapOperation(
method = "renderEquippedItems(Lnet/minecraft/entity/EntityLiving;F)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;I)V")
)
private void iris$setHeldItemId(ItemRenderer itemRenderer, EntityLivingBase entity, ItemStack itemStack, int renderPass, Operation<Void> original) {
ItemIdManager.setItemId(itemStack);
original.call(itemRenderer, entity, itemStack, renderPass);
}
}
@@ -0,0 +1,77 @@
package com.gtnewhorizons.angelica.mixins.early.shaders;
import com.gtnewhorizons.angelica.rendering.FallingBlockRendering;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Emits a per-face normal while a falling block is rendering, so shader packs can light it.
*/
@Mixin(RenderBlocks.class)
public class MixinRenderBlocks_FaceNormals {
@Redirect(
method = {"renderBlockSandFalling", "renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial"},
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderFaceYNeg(Lnet/minecraft/block/Block;DDDLnet/minecraft/util/IIcon;)V")
)
private void angelica$normalYNeg(RenderBlocks instance, Block block, double x, double y, double z, IIcon icon) {
if (FallingBlockRendering.isActive()) Tessellator.instance.setNormal(0.0F, -1.0F, 0.0F);
instance.renderFaceYNeg(block, x, y, z, icon);
}
@Redirect(
method = {"renderBlockSandFalling", "renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial"},
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderFaceYPos(Lnet/minecraft/block/Block;DDDLnet/minecraft/util/IIcon;)V")
)
private void angelica$normalYPos(RenderBlocks instance, Block block, double x, double y, double z, IIcon icon) {
if (FallingBlockRendering.isActive()) Tessellator.instance.setNormal(0.0F, 1.0F, 0.0F);
instance.renderFaceYPos(block, x, y, z, icon);
}
@Redirect(
method = {"renderBlockSandFalling", "renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial"},
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderFaceZNeg(Lnet/minecraft/block/Block;DDDLnet/minecraft/util/IIcon;)V")
)
private void angelica$normalZNeg(RenderBlocks instance, Block block, double x, double y, double z, IIcon icon) {
if (FallingBlockRendering.isActive()) Tessellator.instance.setNormal(0.0F, 0.0F, -1.0F);
instance.renderFaceZNeg(block, x, y, z, icon);
}
@Redirect(
method = {"renderBlockSandFalling", "renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial"},
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderFaceZPos(Lnet/minecraft/block/Block;DDDLnet/minecraft/util/IIcon;)V")
)
private void angelica$normalZPos(RenderBlocks instance, Block block, double x, double y, double z, IIcon icon) {
if (FallingBlockRendering.isActive()) Tessellator.instance.setNormal(0.0F, 0.0F, 1.0F);
instance.renderFaceZPos(block, x, y, z, icon);
}
@Redirect(
method = {"renderBlockSandFalling", "renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial"},
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderFaceXNeg(Lnet/minecraft/block/Block;DDDLnet/minecraft/util/IIcon;)V")
)
private void angelica$normalXNeg(RenderBlocks instance, Block block, double x, double y, double z, IIcon icon) {
if (FallingBlockRendering.isActive()) Tessellator.instance.setNormal(-1.0F, 0.0F, 0.0F);
instance.renderFaceXNeg(block, x, y, z, icon);
}
@Redirect(
method = {"renderBlockSandFalling", "renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial"},
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderFaceXPos(Lnet/minecraft/block/Block;DDDLnet/minecraft/util/IIcon;)V")
)
private void angelica$normalXPos(RenderBlocks instance, Block block, double x, double y, double z, IIcon icon) {
if (FallingBlockRendering.isActive()) Tessellator.instance.setNormal(1.0F, 0.0F, 0.0F);
instance.renderFaceXPos(block, x, y, z, icon);
}
}
@@ -0,0 +1,50 @@
package com.gtnewhorizons.angelica.mixins.early.shaders;
import com.gtnewhorizons.angelica.rendering.FallingBlockRendering;
import net.minecraft.client.renderer.RenderBlocks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
/**
* Replace vanilla's baked per-face directional shading while a falling block is rendering.
*/
@Mixin(RenderBlocks.class)
public class MixinRenderBlocks_FallingShading {
@ModifyConstant(
method = {
"renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion",
"renderStandardBlockWithAmbientOcclusionPartial",
"renderBlockSandFalling"
},
constant = @Constant(floatValue = 0.5F))
private float angelica$flattenBottomShading(float original) {
return FallingBlockRendering.skipDirectionalShading() ? 1.0F : original;
}
@ModifyConstant(
method = {
"renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion",
"renderStandardBlockWithAmbientOcclusionPartial",
"renderBlockSandFalling"
},
constant = @Constant(floatValue = 0.8F))
private float angelica$flattenNorthSouthShading(float original) {
return FallingBlockRendering.skipDirectionalShading() ? 1.0F : original;
}
@ModifyConstant(
method = {
"renderStandardBlockWithColorMultiplier",
"renderStandardBlockWithAmbientOcclusion",
"renderStandardBlockWithAmbientOcclusionPartial",
"renderBlockSandFalling"
},
constant = @Constant(floatValue = 0.6F))
private float angelica$flattenEastWestShading(float original) {
return FallingBlockRendering.skipDirectionalShading() ? 1.0F : original;
}
}
@@ -0,0 +1,42 @@
package com.gtnewhorizons.angelica.mixins.early.shaders;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.coderbot.iris.uniforms.ItemIdManager;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import org.spongepowered.asm.mixin.Mixin;
/**
* Sets the currentRenderedItem ID for blocks drawn outside of terrain.
*/
@Mixin(value = RenderBlocks.class, priority = 1500)
public class MixinRenderBlocks_ItemId {
@WrapMethod(method = "renderBlockAsItem")
private void iris$blockItemId(Block block, int metadata, float brightness, Operation<Void> original) {
// renderBlockAsItem is shared with GUI and HUD item rendering
if (!ItemIdManager.isWorldRenderActive() || !TessellatorManager.isOnMainThread()) {
original.call(block, metadata, brightness);
return;
}
final int prevItemId = ItemIdManager.getItemId();
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(1);
if (prevItemId <= 0) {
ItemIdManager.setBlockId(block, metadata);
}
try {
original.call(block, metadata, brightness);
} finally {
ItemIdManager.setItemIdRaw(prevItemId);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
}
}
}
@@ -0,0 +1,76 @@
package com.gtnewhorizons.angelica.mixins.early.shaders;
import com.gtnewhorizons.angelica.rendering.FallingBlockMetaAccess;
import com.gtnewhorizons.angelica.rendering.FallingBlockRendering;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderFallingBlock;
import net.minecraft.entity.item.EntityFallingBlock;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Renders falling blocks through terrain.
*/
@Mixin(RenderFallingBlock.class)
public class MixinRenderFallingBlock {
@Inject(method = "doRender(Lnet/minecraft/entity/item/EntityFallingBlock;DDDFF)V", at = @At("HEAD"))
private void angelica$beginFallingBlock(EntityFallingBlock entity, double x, double y, double z, float entityYaw, float partialTicks, CallbackInfo ci) {
FallingBlockRendering.active = true;
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
GbufferPrograms.setOverridePhase(WorldRenderingPhase.TERRAIN_SOLID);
FallingBlockRendering.setEntityAttribute(entity.func_145805_f(), entity.field_145814_a);
}
@Inject(method = "doRender(Lnet/minecraft/entity/item/EntityFallingBlock;DDDFF)V", at = @At("RETURN"))
private void angelica$endFallingBlock(EntityFallingBlock entity, double x, double y, double z, float entityYaw, float partialTicks, CallbackInfo ci) {
FallingBlockRendering.active = false;
FallingBlockRendering.resetEntityAttribute();
GbufferPrograms.setOverridePhase(null);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
}
@WrapOperation(
method = "doRender(Lnet/minecraft/entity/item/EntityFallingBlock;DDDFF)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderBlockSandFalling(Lnet/minecraft/block/Block;Lnet/minecraft/world/World;IIII)V")
)
private void angelica$aoFallingBlock(RenderBlocks renderBlocks, Block block, World world, int x, int y, int z, int metadata, Operation<Void> original) {
if (!FallingBlockRendering.isActive()) {
original.call(renderBlocks, block, world, x, y, z, metadata);
return;
}
final IBlockAccess prevAccess = renderBlocks.blockAccess;
final boolean prevRenderAllFaces = renderBlocks.renderAllFaces;
final FallingBlockMetaAccess access = FallingBlockRendering.metaAccess(world, x, y, z, metadata);
renderBlocks.blockAccess = access;
renderBlocks.renderAllFaces = true;
final Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.setTranslation(-x - 0.5D, -y - 0.5D, -z - 0.5D);
try {
renderBlocks.renderStandardBlock(block, x, y, z);
} finally {
tessellator.setTranslation(0.0D, 0.0D, 0.0D);
tessellator.draw();
renderBlocks.renderAllFaces = prevRenderAllFaces;
renderBlocks.blockAccess = prevAccess;
access.clear();
}
}
}
@@ -9,9 +9,7 @@ import net.coderbot.iris.uniforms.ItemIdManager;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -67,17 +65,4 @@ public class MixinRenderPlayerArmor {
original.call(modelBiped, scale);
ItemIdManager.resetItemId();
}
/**
* Set item ID when rendering held items in third person.
*/
@WrapOperation(
method = "renderEquippedItems(Lnet/minecraft/client/entity/AbstractClientPlayer;F)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ItemRenderer;renderItem(Lnet/minecraft/entity/EntityLivingBase;Lnet/minecraft/item/ItemStack;I)V")
)
private void iris$setHeldItemId(ItemRenderer itemRenderer, EntityLivingBase entity, ItemStack itemStack, int renderPass, Operation<Void> original) {
ItemIdManager.setItemId(itemStack);
original.call(itemRenderer, entity, itemStack, renderPass);
}
}