Compare commits

..
11 Commits
Author SHA1 Message Date
sisyphussy 21c01a1aa4 Fix terrible performance on Intel (#1718) 2026-05-03 14:32:58 -07:00
Caedis 83a26b2291 Add Cosmetic Armor support for Dynamic Lights (#1708) 2026-05-03 14:31:49 -07:00
Kotmatross 7e44c1efcb Fix stale uniforms when switching shader program (#1715) 2026-05-03 14:30:46 -07:00
VortexSo4 155f209db5 remove ctm guide as it is worse than wiki (#1706) 2026-04-30 11:03:18 -07:00
SpacEagle17 c4a6c165a0 Increase MAX_COLOR_BUFFERS limit to 32 (#1704) 2026-04-28 12:33:51 -07:00
Eclipse-Sol 624fe6b453 Add targeting of snow-covered blocks. (#1696) 2026-04-26 15:26:10 -07:00
YoshyandJason Mitchell fd45fda0ab Fix AO Lighting for compact CTM (#1697)
Co-authored-by: Jason Mitchell <[email protected]>
2026-04-26 12:55:24 -07:00
Alexdoru 00fc97a611 Fix dynamic lights world client leaks (#1700) 2026-04-26 10:01:46 -07:00
Alexdoru 445dc487b9 fix world client leak caused by lambda in IrisExclusiveUniforms$WorldInfoUniforms (#1701) 2026-04-26 09:56:08 -07:00
Yoshy f43fbd4d9f Fix CTM to not spread to nearby TEs (#1695) 2026-04-25 13:09:38 -07:00
Jason Mitchell dbabc4b3dc Maybe fix DragonAPI on OSX - take two (#1689)
* Maybe fix DragonAPI on OSX - take two
* Bind a default VAO on the SharedDrawable
* Mixin that tries to swap out glGetProgramInfoLog when appropriate

* no direct gl calls
2026-04-21 15:12:02 -07:00
43 changed files with 472 additions and 448 deletions

No files matched your search

-302
View File
@@ -1,302 +0,0 @@
# CTM Guide<img src="img/logo-readme.png" align="right">
###### by Vortex [@vortex_so in discord, any suggestions are accepted]<br>
This file describes how to use CTM, structure of `.properties` file and some useful (or not) techniques
### Note:
- All textures for CTM textures are in `[modname]/mcpatcher/ctm` folder<br>
the overrides apply globally so it doesn't matter in which directory you put them<br>
- in the `ctm` folder you must have a folder with your block name (or any name actually)<br>
in that folder you will have all texture files (animations allowed via mcmeta file for every texture) and `.properties` file<br>
multiple `.properties` files are allowed for a single folder and set of textures for more complex behaviour (see examples)
## Quick Links
- **[Method](#method)**
- **[MatchBlocks](#matchblocks)**
- **[MatchTiles](#matchtiles)**
- **[Tiles](#tiles)**
- **[Connect](#connect)**
- **[Faces](#faces)**
- **[Biomes](#biomes)**
- **[Heights](#heights)**
- **[RenderPass](#renderpass)**
- **[InnerSeams](#innerseams)**
- **[Weights](#weights)**<br>
- **[Examples](#examples)**
## Structure of `.properties` files
---
## `matchBlocks`
Override for a specific block, you can get the value from the tooltip if you have this feature enabled.<br>
Can accept several values which allows to apply one CTM for different blocks.<br>
if no value for this property was provided, Angelica will fallback to name of the `.property` file
examples:<br>
`matchBlocks=gregtech:gt.blockcasings4`<br>
`matchBlocks=minecraft:glass minecraft:glass_pane`
---
## `matchTiles`
Override for a specific texture, main difference from matchBlocks is that this will only match a certain texture, not the block itself<br>
example:<br>
`matchTiles:minecraft:blocks/stone`
---
## `tiles`
The list of texture names for CTM. Supports ranges and explicit names.<br>
You probably would want to use texture names like `0.png`, `1.png` as it is easier, however this allows for any names
examples:<br>
`tiles=0-46` - classic for full CTM, expects files from `0.png` to `46.png`<br>
`tiles=0-3,5, 8` - expects files `0.png`, `1.png`, `2.png`, `3.png`, `5.png`, `8.png`<br>
`tiles=sometext,somemoretext` - expects files `sometext.png`, `somemoretext.png`
---
## `connect`
Sets how the blocks are connected, can accept values: `block`, `tile`, `material`<br>
`block` connects based on block (who would guess)<br>
`tile` connects based on texture<br>
`material` connects based on material (didn't really understand this part, ig it is for stone, iron, wood etc)<br>
Fallbacks to `block` if no value for `matchTiles` is set, otherwise fallbacks to `tile`<br>
Most of the time you don't need to set this value because of that fallback rule
example:<br>
`connect=block`
---
## `faces`
Limit to specific faces of a block, can accept several values<br>
Fallbacks to `all` if value not present in the `.properties` file
examples:<br>
`faces=top`<br>
`faces=all`<br>
`faces=bottom`<br>
`faces=sides`<br>
`faces=north south east west`<br>
`faces=top bottom sides`
---
## `biomes`
Limit to specific biomes, can accept several values<br>
Fallbacks to all if value not present in the `.properties` file
examples:<br>
`biomes=plains`<br>
`biomes=desert forest`<br>
`biomes=taiga jungle savanna`
---
## `heights`
Limit to certain Y levels, can only accept several values<br>
can be set through either `minHeight` and `maxHeight` or `heights`
examples:<br>
`heights=0-64`<br>
`heights=64-255`<br>
example of maxHeight and minHeight:<br>
`minHeight=64`<br>
`maxHeight=128`
---
## `renderPass`
Controls which rendering layer a CTM rule is applied in<br>
In most cases you only need it to avoid rendering issues with transparency
`renderPass=solid` used for fully opaque blocks<br>
`renderPass=cutout` used for fences, glass CTM, textured blocks with hard transparency<br>
`renderPass=translucent` used for transparent blocks like glass or water<br>
`renderPass=cutout_mipped` no idea for what<br>
`renderPass=overlay` used for blocks with an overlay, for example GT hatches or damaged anvils<br>
`renderPass=backface` used for blocks with a backface, however it is very rare to use this one
---
## `innerSeams`
Adds internal seams, fallback to `innerSeams=false` if not set<br>
example:<br>
`innerSeams=true`
---
## `weights`
Priority, used for `Random` type to determine which textures have certain chances to be used for render<br>
accepts several values, assigns values correspondingly to `tiles` list values
examples:<br>
`weight=10`<br>
`weights=1 3 3 2 1`
---
# `Method`
Selects CTM mode:
- [LINK](#full-ctm-ctm) `ctm` / `glass` / `default`
- [LINK](#compact-ctm) `compact` / `ctm_compact`
- [LINK](#horizontal)`horizontal` / `bookshelf`
- [LINK](#vertical)`vertical`
- [LINK](#horizontalvertical-hv)`horizontal+vertical` / `h+v`
- [LINK](#verticalhorizontal-vh)`vertical+horizontal` / `v+h`
- [LINK](#top)`top` / `sandstone`
- [LINK](#repeat)`repeat` / `pattern`
- [LINK](#random)`random`
- [LINK](#fixed)`fixed` / `static`
example:<br>
`method=ctm`
### Full CTM (ctm)
Requires exactly 47 tiles, the most versatile but the most size-heavy method
Here's a visual explanation of how the textures should be numbered:
<img src="img/CTM/CTM-pattern.png" width="624" style="image-rendering: pixelated;">
---
### Compact CTM
Requires exactly 5 tiles.
<img src="img/CTM/CCTM-pattern.png" width="284" style="image-rendering: pixelated;">
Splits face into 4 quadrants and builds final texture per quadrant.
Here's a visual explanation of how it works on an example of 3x3 blocks pattern with a cutout:
<img src="img/CTM/CCTM-principle.png" width="400" style="image-rendering: pixelated;">
as you can see, compact CTM has a single limitation:<br>
the size of any part of texture, such as an edge or a corner, can't be larger than `8x8` pixel<br>
this is the main case where only full CTM can be used.
---
### Horizontal
Requires exactly 4 tiles. connects only horizontally, used for bookshelves for example
<img src="img/CTM/HCTM-pattern.png" width="284" style="image-rendering: pixelated;">
---
### Vertical
Requires exactly 4 tiles.
<img src="img/CTM/VCTM-pattern.png" height="284" style="image-rendering: pixelated;">
---
### Horizontal+Vertical (h+v)
Requires exactly 7 tiles.
Two-stage: horizontal first, then vertical if none horizontal variations are found.
<img src="img/CTM/HVCTM-pattern.png" height="284" style="image-rendering: pixelated;">
---
### Vertical+Horizontal (v+h)
Requires exactly 7 tiles.
Two-stage: vertical first, then horizontal if none vertical variations are found.
<img src="img/CTM/VHCTM-pattern.png" height="284" style="image-rendering: pixelated;">
---
### Top
Requires exactly 1 tile.
Applies tile on side faces when block above matches, for example used for smooth sandstone
---
### Repeat
Requires width * height tiles.
Properties:
- `width` - defines the number of tiles in width
- `height` - defines the number of tiles in height
- `symmetry` - defines the symmetry of the pattern, can be either `none` or `opposite`
Just uses N*M grid, for example<br>
`0 1 2 3 4`<br>
`5 6 7 8 9`
---
### Random
Any number of tiles.
Properties:
- `weights` - described [here](#weights)
- `symmetry` - accepts `all`, `opposite`, `none`, groups opposite faces together, otherwise keeps them independent
- `linked` - makes the textures consistent between render states, not needed much
---
### Fixed
Requires exactly 1 tile. Always uses the same tile.<br>
useful only if combined with height or other properties (see examples)
---
# Examples
## Glass via full CTM
```properties
matchBlocks=minecraft:glass minecraft:glass_pane
metadata=0
method=ctm
faces=all
tiles=0-46
connect=block
```
## Multiple properties files usage
### Use multiple .properties files with different heights for more detailed worldgen
this one will use one texture for lower part of the world and another for the rest of the world
```properties
matchBlocks=minecraft:dirt
metadata=0
method=fixed
faces=all
tiles=0
connect=block
maxHeight=40
```
```properties
matchBlocks=minecraft:dirt
metadata=0
method=fixed
faces=all
tiles=0
connect=block
minHeight=41
```
---
+2
View File
@@ -129,6 +129,8 @@ dependencies {
compileOnly(rfg.deobf("curse.maven:et-futurum-requiem-441392:5873837"))
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.3-GTNH:dev")
// For testing alternative splash screens — https://github.com/MalTeeez/ModernSplash/releases
// devOnlyNonPublishable("com.github.MalTeeez:modernsplash:1.7.10-1.2.4:dev")
+4
View File
@@ -146,6 +146,10 @@ tasks.test {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(8)
if (System.getProperty("os.name").lowercase().contains("mac")
&& System.getProperty("os.arch") == "aarch64") {
vendor = JvmVendorSpec.AZUL
}
}
dependsOn(downgradeMainClasses, downgradeTestClasses, downgradeDepsForTest)
@@ -9,13 +9,10 @@ import com.gtnewhorizons.angelica.glsm.states.MaterialState;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import java.nio.FloatBuffer;
@@ -188,6 +188,8 @@ public class GLStateManager {
@Getter private static Vendor VENDOR;
@Getter private static boolean windows;
// This setting varies depending on driver, so it gets queried at runtime
public static int DEFAULT_DRAW_BUFFER = 0x0405; // GL_BACK
@@ -528,6 +530,10 @@ public class GLStateManager {
final String glVendor = RENDER_BACKEND.getString(GL11.GL_VENDOR);
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
final String os = System.getProperty("os.name").toLowerCase();
windows = os.contains("win");
// The initial mask value should be defined as all 1's. However, some drivers have it set to 0's.
// To ensure consistency & correctness across all drivers, we're setting them to 0xFF.
RENDER_BACKEND.stencilFunc(stencilState.getFuncFront(), stencilState.getRefFront(), 0xFF);
@@ -4295,6 +4301,7 @@ public class GLStateManager {
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
GLSMHooks.programChangeEvent.previousProgram = prev;
GLSMHooks.programChangeEvent.newProgram = 0;
GLSMHooks.programChangeEvent.postBind = false;
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
}
ffp.activate();
@@ -4315,6 +4322,7 @@ public class GLStateManager {
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
GLSMHooks.programChangeEvent.previousProgram = prev;
GLSMHooks.programChangeEvent.newProgram = program;
GLSMHooks.programChangeEvent.postBind = false;
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
}
if (initConfig != null && initConfig.isLwjglDebug()) {
@@ -4323,6 +4331,12 @@ public class GLStateManager {
}
RENDER_BACKEND.useProgram(program);
CompatUniformManager.onUseProgram(program);
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
GLSMHooks.programChangeEvent.previousProgram = prev;
GLSMHooks.programChangeEvent.newProgram = program;
GLSMHooks.programChangeEvent.postBind = true;
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
}
}
}
@@ -5726,6 +5740,14 @@ public class GLStateManager {
return RENDER_BACKEND.getProgramInfoLog(program);
}
public static boolean glIsProgram(int obj) {
return RENDER_BACKEND.isProgram(obj);
}
public static boolean glIsShader(int obj) {
return RENDER_BACKEND.isShader(obj);
}
public static void glBindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format) { RENDER_BACKEND.bindImageTexture(unit, texture, level, layered, layer, access, format); }
public static void glMemoryBarrier(int barriers) { RENDER_BACKEND.memoryBarrier(barriers); }
public static void glCopyImageSubData(int srcName, int srcTarget, int srcLevel, int srcX, int srcY, int srcZ,
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.glsm.dsa.DSACore;
import com.gtnewhorizons.angelica.glsm.dsa.DSAEXT;
import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
@@ -61,7 +62,13 @@ public class RenderSystem {
if (rendererInitialized) return;
rendererInitialized = true;
try {
if (GLStateManager.capabilities.OpenGL45) {
if (GLStateManager.vendorIsIntel() && GLStateManager.isWindows()) {
dsaState = new DSAUnsupported();
GLStateManager.LOGGER.info("Detected Intel drivers on Windows, disabling DSA.");
} else if (!GLStateManager.getInitConfig().isDSAEnabled()) {
dsaState = new DSAUnsupported();
GLStateManager.LOGGER.info("enableDSA is set to false, disabling DSA.");
} else if (GLStateManager.capabilities.OpenGL45) {
dsaState = (Runtime.version().feature() > 8 && GLStateManager.capabilities.GL_EXT_direct_state_access) ? new DSAEXT() : new DSACore();
GLStateManager.LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
}
@@ -15,6 +15,7 @@ public final class GLSMInitConfig {
private final int displayWidth;
private final int displayHeight;
private final Runnable postInitCallback;
private final boolean enableDSA;
private GLSMInitConfig(Builder builder) {
this.lwjglDebug = builder.lwjglDebug;
@@ -26,6 +27,7 @@ public final class GLSMInitConfig {
this.displayWidth = builder.displayWidth;
this.displayHeight = builder.displayHeight;
this.postInitCallback = builder.postInitCallback;
this.enableDSA = builder.enableDSA;
}
public static Builder builder() {
@@ -41,6 +43,7 @@ public final class GLSMInitConfig {
public int getDisplayWidth() { return displayWidth; }
public int getDisplayHeight() { return displayHeight; }
public Runnable getPostInitCallback() { return postInitCallback; }
public boolean isDSAEnabled() { return enableDSA; }
public static final class Builder {
private boolean lwjglDebug = false;
@@ -52,6 +55,7 @@ public final class GLSMInitConfig {
private int displayWidth = 0;
private int displayHeight = 0;
private Runnable postInitCallback = null;
private boolean enableDSA = false;
private Builder() {}
@@ -96,6 +100,11 @@ public final class GLSMInitConfig {
return this;
}
public Builder enableDSA(boolean enableDSA) {
this.enableDSA = enableDSA;
return this;
}
public GLSMInitConfig build() {
return new GLSMInitConfig(this);
}
@@ -5,4 +5,5 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
public final class ProgramChangeEvent extends MutableEvent {
public int previousProgram;
public int newProgram;
public boolean postBind;
}
@@ -0,0 +1,79 @@
package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.opengl.SharedDrawable;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
class SharedDrawableVaoTest {
private static final String VERT_330 = "#version 330 core\nvoid main() { gl_Position = vec4(0.0); }\n";
private static final String FRAG_330 = "#version 330 core\nout vec4 o;\nvoid main() { o = vec4(0.0); }\n";
@BeforeAll
static void createContext() throws LWJGLException {
assumeTrue(!Display.isCreated(), "inherited Display -- skipping to avoid profile drift");
Display.setDisplayModeAndFullscreen(new DisplayMode(800, 600));
Display.setResizable(false);
Display.setFullscreen(false);
Display.create(
new PixelFormat().withDepthBits(24).withStencilBits(8),
new ContextAttribs(4, 1).withProfileCore(true).withForwardCompatible(true));
GL30.glBindVertexArray(GL30.glGenVertexArrays());
}
@AfterAll
static void destroyContext() {
if (Display.isCreated()) Display.destroy();
}
@Test
void validateProgramSucceedsOnSharedDrawableWithVao() throws LWJGLException {
SharedDrawable shared = new SharedDrawable(Display.getDrawable());
Display.getDrawable().releaseContext();
shared.makeCurrent();
try {
GL30.glBindVertexArray(GL30.glGenVertexArrays());
int program = linkTrivialProgram();
GL20.glValidateProgram(program);
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(program, GL20.GL_VALIDATE_STATUS), () -> GL20.glGetProgramInfoLog(program, 4096));
GL20.glDeleteProgram(program);
} finally {
shared.releaseContext();
Display.getDrawable().makeCurrent();
shared.destroy();
}
}
private static int linkTrivialProgram() {
int vs = compile(GL20.GL_VERTEX_SHADER, VERT_330);
int fs = compile(GL20.GL_FRAGMENT_SHADER, FRAG_330);
int program = GL20.glCreateProgram();
GL20.glAttachShader(program, vs);
GL20.glAttachShader(program, fs);
GL20.glLinkProgram(program);
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(program, GL20.GL_LINK_STATUS), () -> GL20.glGetProgramInfoLog(program, 4096));
GL20.glDeleteShader(vs);
GL20.glDeleteShader(fs);
return program;
}
private static int compile(int type, String src) {
int shader = GL20.glCreateShader(type);
GL20.glShaderSource(shader, src);
GL20.glCompileShader(shader);
assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS), () -> GL20.glGetShaderInfoLog(shader, 4096));
return shader;
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

@@ -24,6 +24,7 @@ public class ModStatus {
public static boolean isThaumcraftLoaded;
public static boolean isThaumicHorizonsLoaded;
public static boolean isBaublesLoaded;
public static boolean isCosmeticArmorReworkedLoaded;
public static boolean isFluidLoggedLoaded;
public static boolean isBOPLoaded;
@@ -43,6 +44,7 @@ public class ModStatus {
isThaumcraftLoaded = Loader.isModLoaded("Thaumcraft");
isThaumicHorizonsLoaded = Loader.isModLoaded("ThaumicHorizons");
isBaublesLoaded = Loader.isModLoaded("Baubles");
isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked");
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
@@ -63,6 +63,11 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean enableVAO;
@Config.Comment("Enables DSA (Direct State Access) for faster bindings. Disable if you notice terrible performance.")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean enableDSA;
@Config.Comment("Enable NotFine features")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.dynamiclights;
import baubles.common.lib.PlayerHandler;
import lain.mods.cos.CosmeticArmorReworked;
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
import com.gtnewhorizon.gtnhlib.blockpos.IBlockPos;
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
@@ -180,6 +181,19 @@ public class DynamicLights {
this.lightSourcesLock.writeLock().unlock();
}
/**
* Removes all tracked light sources, to call when unloading the client world
*/
public void removeAllLightSources() {
this.lightSourcesLock.writeLock().lock();
this.dynamicLightSources.clear();
this.lightSourceCount = 0;
rebuildLightSourceArray();
this.lightSourcesLock.writeLock().unlock();
}
/**
* Removes light sources if the filter matches.
*
@@ -625,6 +639,18 @@ public class DynamicLights {
}
}
}
if (ModStatus.isCosmeticArmorReworkedLoaded) {
final var cosArmor = CosmeticArmorReworked.invMan.getCosArmorInventoryClient(player.getUniqueID());
if (cosArmor != null) {
for (int i = 0; i < cosArmor.getSizeInventory(); i++) {
final var stack = cosArmor.getStackInSlot(i);
if (stack != null) {
luminance = Math.max(luminance, getLuminanceFromItemStack(stack));
}
}
}
}
}
return luminance;
Whitespace-only changes.
@@ -16,7 +16,6 @@ import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.samplers.IrisSamplers;
import net.coderbot.iris.texture.pbr.PBRTextureManager;
import net.coderbot.iris.vertices.ImmediateState;
public class IrisGLSMBridge {
@@ -149,20 +148,37 @@ public class IrisGLSMBridge {
}
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> ProgramUniforms.clearActiveUniforms());
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (event.postBind) return;
ProgramUniforms.clearActiveUniforms();
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (event.postBind) return;
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
if (!drp.shouldOverrideShaders()) return;
if (drp.getActivePassProgramId() == -1) return;
if (!ImmediateState.isRenderingLevel || DepthColorStorage.isOwnedProgram(event.newProgram)) {
if (DepthColorStorage.isOwnedProgram(event.newProgram)) {
DepthColorStorage.unlockDepthColor();
} else {
drp.onModProgramOverride();
}
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (!event.postBind) return;
WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (pipeline instanceof DeferredWorldRenderingPipeline drp) {
DeferredWorldRenderingPipeline.Pass activePass = drp.getActivePassProgram();
if (activePass != null && activePass.getProgram() != null && activePass.getProgram().getProgramId() == event.newProgram) {
activePass.getProgram().getUniforms().update();
}
}
});
}
}
@@ -287,6 +287,14 @@ public enum Mixins implements IMixins {
)
),
DRAGONAPI_SHADER_REGISTRY_PARSE_ERROR(new MixinBuilder()
.setPhase(Phase.EARLY)
.addRequiredMod(TargetedMod.DRAGON_API)
.addClientMixins(
"dragonapi.MixinShaderRegistry_ParseError"
)
),
IRIS_RENDERING_NOBACKHAND(new MixinBuilder("Iris Hand Shaders")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
@@ -106,26 +106,6 @@ public final class ClientProxy extends CommonProxy {
blockError = new BlockError();
}
@SubscribeEvent
public void worldLoad(WorldEvent.Load event) {
if (GLStateManager.isRunningSplash()) {
GLStateManager.setRunningSplash(false);
LOGGER.info("World loaded - Enabling GLSM Cache");
}
if (AngelicaConfig.enableCeleritas) {
ChunkTaskRegistry.reset();
ChunkTaskRegistry.registerProvider(DefaultChunkTaskProvider.INSTANCE);
ChunkTaskRegistry.registerProvider(ThreadedChunkTaskProvider.INSTANCE);
// Register all blocks. Because blockids are unique to a world, this must be done each load
GameData.getBlockRegistry().typeSafeIterable().forEach(o -> {
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, true, true);
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, false, true);
});
}
}
@Override
public void init(FMLInitializationEvent event) {
super.init(event);
@@ -170,7 +150,6 @@ public final class ClientProxy extends CommonProxy {
}
}
@Override
public void postInit(FMLPostInitializationEvent event) {
super.postInit(event);
@@ -190,11 +169,38 @@ public final class ClientProxy extends CommonProxy {
}
}
@SubscribeEvent
public void worldLoad(WorldEvent.Load event) {
if (!event.world.isRemote) return;
if (GLStateManager.isRunningSplash()) {
GLStateManager.setRunningSplash(false);
LOGGER.info("World loaded - Enabling GLSM Cache");
}
if (AngelicaConfig.enableCeleritas) {
ChunkTaskRegistry.reset();
ChunkTaskRegistry.registerProvider(DefaultChunkTaskProvider.INSTANCE);
ChunkTaskRegistry.registerProvider(ThreadedChunkTaskProvider.INSTANCE);
// Register all blocks. Because blockids are unique to a world, this must be done each load
GameData.getBlockRegistry().typeSafeIterable().forEach(o -> {
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, true, true);
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, false, true);
});
}
}
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload event) {
if (!event.world.isRemote) return;
DynamicLights.get().removeAllLightSources();
}
float lastIntegratedTickTime;
@SubscribeEvent
public void onTick(TickEvent.ServerTickEvent event) {
if (FMLCommonHandler.instance().getSide().isClient() && event.phase == TickEvent.Phase.END) {
if (event.phase == TickEvent.Phase.END) {
final IntegratedServer srv = Minecraft.getMinecraft().getIntegratedServer();
if (srv != null) {
final long currentTickTime = srv.tickTimeArray[srv.getTickCounter() % 100];
@@ -8,7 +8,10 @@ import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
import com.gtnewhorizons.angelica.rendering.celeritas.iris.BlockRenderContext;
import com.gtnewhorizons.angelica.rendering.celeritas.iris.ContextAwareChunkVertexEncoder;
import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.vertices.ExtendedDataHelper;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
@@ -261,7 +264,13 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
if (isFluid) {
contextEncoder.prepareToRenderFluid(blockRenderContext, block, lightValue);
} else {
contextEncoder.prepareToRenderBlock(blockRenderContext, block, metadata,
int effectiveMeta = metadata;
if (BlockRenderingSettings.INSTANCE.hasSnowyEntries()
&& BlockRenderingSettings.INSTANCE.getSnowyBlocks().contains(block)
&& RenderBlocksUtils.isSnowCovered(renderBlocks.blockAccess, x, y, z)) {
effectiveMeta |= BlockMaterialMapping.SNOWY_META_BIT;
}
contextEncoder.prepareToRenderBlock(blockRenderContext, block, effectiveMeta,
ExtendedDataHelper.BLOCK_RENDER_TYPE, lightValue);
}
}
@@ -233,6 +233,7 @@ public class CTMUtils {
}
public static void reset() {
clearCurrentCompact();
}
private static boolean checkFace(int face) {
@@ -76,6 +76,18 @@ public final class CompactCtmQuadProcessor {
private static final int[] QUAD_B = {UP, RIGHT, DOWN, LEFT};
private static final int[] QUAD_DIAG = {UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT};
/** Maps split-face quadrant (0=TL,1=TR,2=BR,3=BL face-local) to RenderBlocks per-face TL/TR/BR/BL orientation. */
private static final int[][] RENDER_FACE_QUADRANT_MAP = {
{3, 2, 1, 0}, // Y-
{2, 3, 0, 1}, // Y+
{3, 0, 1, 2}, // Z-
{0, 1, 2, 3}, // Z+
{3, 0, 1, 2}, // X-
{1, 2, 3, 0} // X+
};
private static final float[] QUADRANT_MIN_U = {0.0f, 0.5f, 0.5f, 0.0f};
private static final float[] QUADRANT_MIN_V = {0.0f, 0.0f, 0.5f, 0.5f};
private final IIcon[] sprites;
private final CompactConnectingCtmProperties props;
@@ -148,30 +160,107 @@ public final class CompactCtmQuadProcessor {
}
private void renderSplitFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, int connections) {
Block block = world.getBlock(x, y, z);
final 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;
final double minX = rb.renderMinX, minY = rb.renderMinY, minZ = rb.renderMinZ;
final double maxX = rb.renderMaxX, maxY = rb.renderMaxY, maxZ = rb.renderMaxZ;
// Without AO the corner color/brightness fields are unused; skip the lighting dance.
final boolean ao = rb.enableAO;
final float rTL, rTR, rBL, rBR, gTL, gTR, gBL, gBR, bTL, bTR, bBL, bBR;
final int brTL, brTR, brBL, brBR;
if (ao) {
rTL = rb.colorRedTopLeft; rTR = rb.colorRedTopRight;
rBL = rb.colorRedBottomLeft; rBR = rb.colorRedBottomRight;
gTL = rb.colorGreenTopLeft; gTR = rb.colorGreenTopRight;
gBL = rb.colorGreenBottomLeft; gBR = rb.colorGreenBottomRight;
bTL = rb.colorBlueTopLeft; bTR = rb.colorBlueTopRight;
bBL = rb.colorBlueBottomLeft; bBR = rb.colorBlueBottomRight;
brTL = rb.brightnessTopLeft; brTR = rb.brightnessTopRight;
brBL = rb.brightnessBottomLeft; brBR = rb.brightnessBottomRight;
} else {
rTL = rTR = rBL = rBR = 0f;
gTL = gTR = gBL = gBR = 0f;
bTL = bTR = bBL = bBR = 0f;
brTL = brTR = brBL = brBR = 0;
}
for (int q = 0; q < 4; q++) {
IIcon icon = sprites[getQuadrantSprite(q, connections)];
final IIcon icon = sprites[getQuadrantSprite(q, connections)];
if (icon == null) {
continue;
}
if (ao) {
applyQuadrantLighting(rb, face, q, rTL, rTR, rBL, rBR, gTL, gTR, gBL, gBR, bTL, bTR, bBL, bBR, brTL, brTR, brBL, brBR);
}
setQuadrantBounds(rb, face, q, minX, minY, minZ, maxX, maxY, maxZ);
rb.overrideBlockTexture = icon;
renderFace(rb, block, x, y, z, face, icon);
}
if (ao) {
rb.colorRedTopLeft = rTL; rb.colorRedTopRight = rTR;
rb.colorRedBottomLeft = rBL; rb.colorRedBottomRight = rBR;
rb.colorGreenTopLeft = gTL; rb.colorGreenTopRight = gTR;
rb.colorGreenBottomLeft = gBL; rb.colorGreenBottomRight = gBR;
rb.colorBlueTopLeft = bTL; rb.colorBlueTopRight = bTR;
rb.colorBlueBottomLeft = bBL; rb.colorBlueBottomRight = bBR;
rb.brightnessTopLeft = brTL; rb.brightnessTopRight = brTR;
rb.brightnessBottomLeft = brBL; rb.brightnessBottomRight = brBR;
}
rb.overrideBlockTexture = null;
rb.setRenderBounds(minX, minY, minZ, maxX, maxY, maxZ);
}
private static void applyQuadrantLighting(RenderBlocks rb, int face, int quadrant, float rTL, float rTR, float rBL, float rBR, float gTL, float gTR, float gBL, float gBR, float bTL, float bTR, float bBL, float bBR, int brTL, int brTR, int brBL, int brBR) {
final int rq = RENDER_FACE_QUADRANT_MAP[face][quadrant];
final float u0 = QUADRANT_MIN_U[rq], u1 = u0 + 0.5f;
final float v0 = QUADRANT_MIN_V[rq], v1 = v0 + 0.5f;
rb.colorRedTopLeft = bilerp(rTL, rTR, rBL, rBR, u0, v0);
rb.colorRedTopRight = bilerp(rTL, rTR, rBL, rBR, u1, v0);
rb.colorRedBottomLeft = bilerp(rTL, rTR, rBL, rBR, u0, v1);
rb.colorRedBottomRight = bilerp(rTL, rTR, rBL, rBR, u1, v1);
rb.colorGreenTopLeft = bilerp(gTL, gTR, gBL, gBR, u0, v0);
rb.colorGreenTopRight = bilerp(gTL, gTR, gBL, gBR, u1, v0);
rb.colorGreenBottomLeft = bilerp(gTL, gTR, gBL, gBR, u0, v1);
rb.colorGreenBottomRight = bilerp(gTL, gTR, gBL, gBR, u1, v1);
rb.colorBlueTopLeft = bilerp(bTL, bTR, bBL, bBR, u0, v0);
rb.colorBlueTopRight = bilerp(bTL, bTR, bBL, bBR, u1, v0);
rb.colorBlueBottomLeft = bilerp(bTL, bTR, bBL, bBR, u0, v1);
rb.colorBlueBottomRight = bilerp(bTL, bTR, bBL, bBR, u1, v1);
rb.brightnessTopLeft = bilerpLight(brTL, brTR, brBL, brBR, u0, v0);
rb.brightnessTopRight = bilerpLight(brTL, brTR, brBL, brBR, u1, v0);
rb.brightnessBottomLeft = bilerpLight(brTL, brTR, brBL, brBR, u0, v1);
rb.brightnessBottomRight = bilerpLight(brTL, brTR, brBL, brBR, u1, v1);
}
private static float bilerp(float c00, float c10, float c01, float c11, float u, float v) {
final float top = c00 + (c10 - c00) * u;
final float bot = c01 + (c11 - c01) * u;
return top + (bot - top) * v;
}
/** Brightness packs sky in upper 16 bits, block in lower; bilerp each independently then repack. */
private static int bilerpLight(int c00, int c10, int c01, int c11, float u, float v) {
final int b00 = c00 & 0xFFFF, b10 = c10 & 0xFFFF, b01 = c01 & 0xFFFF, b11 = c11 & 0xFFFF;
final int s00 = c00 >>> 16, s10 = c10 >>> 16, s01 = c01 >>> 16, s11 = c11 >>> 16;
final float bTop = b00 + (b10 - b00) * u;
final float bBot = b01 + (b11 - b01) * u;
final float sTop = s00 + (s10 - s00) * u;
final float sBot = s01 + (s11 - s01) * u;
final int block = Math.round(bTop + (bBot - bTop) * v);
final int sky = Math.round(sTop + (sBot - sTop) * v);
return (sky << 16) | (block & 0xFFFF);
}
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;
@@ -105,7 +105,6 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
public TileOverride go(RenderBlockState renderBlockState, IIcon origIcon) {
CTMUtils.clearCurrentCompact();
this.renderBlockState = renderBlockState;
renderBlockState.setFilter(null);
currentIcon = origIcon;
@@ -157,7 +157,7 @@ public class RenderBlocksUtils {
return isSnow ? Blocks.snow_layer.getIcon(blockAccess, x, y, z, face) : topIcon;
}
private static boolean isSnowCovered(IBlockAccess blockAccess, int x, int y, int z) {
public static boolean isSnowCovered(IBlockAccess blockAccess, int x, int y, int z) {
Block topBlock = blockAccess.getBlock(x, y + 1, z);
return topBlock == Blocks.snow_layer || topBlock == Blocks.snow;
}
@@ -7,7 +7,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import net.coderbot.iris.Iris;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
import net.coderbot.iris.shaderpack.materialmap.BlockEntry;
import net.coderbot.iris.shaderpack.materialmap.BlockRenderType;
import net.coderbot.iris.shaderpack.materialmap.FlatteningMap;
@@ -21,45 +22,46 @@ import java.util.Map;
import java.util.Set;
public class BlockMaterialMapping {
/** Meta-key bit OR'd in at runtime when a snowy-tagged block has snow above it. */
public static final int SNOWY_META_BIT = 0x10;
/**
* Creates a two-level map structure for block material IDs.
* Based on Iris's BlockState mapping approach adapted for 1.7.10's metadata system.
*/
public static Reference2ObjectMap<Block, Int2IntMap> createBlockMetaIdMap(Int2ObjectMap<List<BlockEntry>> blockPropertiesMap) {
Reference2ObjectMap<Block, Int2IntMap> blockMatches = new Reference2ObjectOpenHashMap<>();
public static Reference2ObjectMap<Block, Int2IntMap> createBlockMetaIdMap(Int2ObjectMap<List<BlockEntry>> blockPropertiesMap, boolean skipFlattening) {
final Reference2ObjectMap<Block, Int2IntMap> blockMatches = new Reference2ObjectOpenHashMap<>();
final ReferenceSet<Block> snowyBlocks = new ReferenceOpenHashSet<>();
blockPropertiesMap.forEach((intId, entries) -> {
for (BlockEntry entry : entries) {
addBlockMetas(entry, blockMatches, intId);
addBlockMetas(entry, blockMatches, intId, skipFlattening, snowyBlocks);
}
});
BlockRenderingSettings.INSTANCE.setHasSnowyEntries(!snowyBlocks.isEmpty());
BlockRenderingSettings.INSTANCE.setSnowyBlocks(snowyBlocks);
return blockMatches;
}
public static Map<Block, BlockRenderLayer> createBlockTypeMap(Map<NamespacedId, BlockRenderType> blockPropertiesMap) {
Map<Block, BlockRenderLayer> blockTypeIds = new Reference2ReferenceOpenHashMap<>();
final Map<Block, BlockRenderLayer> blockTypeIds = new Reference2ReferenceOpenHashMap<>();
blockPropertiesMap.forEach((id, blockType) -> {
Block block = resolveBlock(id);
Block block = resolveBlockOrNull(id);
// Try flattening map for modern names
if ((block == null || block == Blocks.air) && "minecraft".equals(id.getNamespace())) {
List<BlockEntry> legacyEntries = FlatteningMap.toLegacy(id.getName(), Map.of());
// Modern names like "grass_block" don't exist in 1.7.10's registry; fall back to flattening.
if (block == null && "minecraft".equals(id.getNamespace())) {
final List<BlockEntry> legacyEntries = FlatteningMap.toLegacy(id.getName(), Map.of());
if (legacyEntries != null) {
// Use the first entry for render type (render type applies per-block)
block = resolveBlock(legacyEntries.getFirst().getId());
block = resolveBlockOrNull(legacyEntries.getFirst().getId());
}
}
if (block == null || block == Blocks.air) {
return;
}
if (block == null) return;
final BlockRenderLayer layer = convertBlockToRenderLayer(blockType);
if (layer != null) {
blockTypeIds.put(block, layer);
}
if (layer != null) blockTypeIds.put(block, layer);
});
return blockTypeIds;
@@ -82,33 +84,29 @@ public class BlockMaterialMapping {
* Adds block+metadata combinations to the material ID map.
* Based on Iris's addBlockStates method, adapted for 1.7.10 metadata system.
*/
private static void addBlockMetas(BlockEntry entry, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId) {
NamespacedId id = entry.getId();
String name = id.getName();
boolean hasStateProps = !entry.getStateProperties().isEmpty();
boolean hasExplicitMetas = !entry.getMetas().isEmpty();
private static void addBlockMetas(BlockEntry entry, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId, boolean skipFlattening, ReferenceSet<Block> snowyBlocks) {
final NamespacedId id = entry.getId();
final Map<String, String> stateProps = entry.getStateProperties();
final String snowy = stateProps.get("snowy");
final int snowyBit = "true".equals(snowy) ? SNOWY_META_BIT : 0;
if ("minecraft".equals(id.getNamespace()) && (hasStateProps || !hasExplicitMetas)) {
List<BlockEntry> legacyEntries = FlatteningMap.toLegacy(name, entry.getStateProperties());
if (legacyEntries != null) {
for (BlockEntry legacy : legacyEntries) {
applyMetas(resolveBlock(legacy.getId()), legacy.getMetas(), idMap, intId);
}
return;
}
// Vanilla modern names go through FlatteningMap; legacy-section packs and modded blocks
// resolve directly from the registry.
List<BlockEntry> targets = null;
if (!skipFlattening && "minecraft".equals(id.getNamespace()) && (!stateProps.isEmpty() || entry.getMetas().isEmpty())) {
targets = FlatteningMap.toLegacy(id.getName(), stateProps);
}
if (targets == null) targets = List.of(entry);
// Fall back to registry with the entry's own metas
Block block = resolveBlock(id);
applyMetas(block, entry.getMetas(), idMap, intId);
for (BlockEntry target : targets) {
final Block block = resolveBlockOrNull(target.getId());
if (block == null) continue;
applyMetas(block, target.getMetas(), idMap, intId, snowyBit);
if (snowy != null) snowyBlocks.add(block);
}
}
// If the block doesn't exist, by default the registry will return AIR. That probably isn't what we want.
// TODO: Assuming that Registry.BLOCK.getDefaultId() == "minecraft:air" here
private static void applyMetas(Block block, Set<Integer> metas, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId) {
if (block == null || block == Blocks.air) {
return;
}
private static void applyMetas(Block block, Set<Integer> metas, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId, int snowyBit) {
Int2IntMap metaMap = idMap.get(block);
if (metaMap == null) {
metaMap = new Int2IntOpenHashMap();
@@ -117,18 +115,18 @@ public class BlockMaterialMapping {
}
if (metas.isEmpty()) {
for (int meta = 0; meta < 16; meta++) {
metaMap.putIfAbsent(meta, intId);
}
for (int meta = 0; meta < 16; meta++) metaMap.putIfAbsent(meta | snowyBit, intId);
} else {
for (int meta : metas) {
metaMap.putIfAbsent(meta, intId);
}
for (int meta : metas) metaMap.putIfAbsent(meta | snowyBit, intId);
}
}
private static Block resolveBlock(NamespacedId id) {
final ResourceLocation resourceLocation = new ResourceLocation(id.getNamespace(), id.getName());
return (Block) Block.blockRegistry.getObject(resourceLocation.toString());
/**
* Returns the registered Block for an id, or null if unknown.
* The registry returns Blocks.air (its default) for missing keys, which we coerce to null.
*/
private static Block resolveBlockOrNull(NamespacedId id) {
final Block block = (Block) Block.blockRegistry.getObject(new ResourceLocation(id.getNamespace(), id.getName()).toString());
return block == Blocks.air ? null : block;
}
}
@@ -4,7 +4,10 @@ import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntFunction;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
import it.unimi.dsi.fastutil.objects.ReferenceSets;
import lombok.Getter;
import lombok.Setter;
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
@@ -19,12 +22,21 @@ public class BlockRenderingSettings {
private boolean reloadRequired;
private Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches;
private Map<Block, BlockRenderLayer> blockTypeIds;
private Object2IntFunction<NamespacedId> entityIds;
private Object2IntFunction<NamespacedId> itemIds;
private float ambientOcclusionLevel;
// note: no reload needed, entities are rebuilt every frame.
@Setter
private Object2IntFunction<NamespacedId> entityIds;
// note: no reload needed, items are rendered every frame.
@Setter
private Object2IntFunction<NamespacedId> itemIds;
@Getter
private float ambientOcclusionLevel;
private boolean disableDirectionalShading;
private boolean useSeparateAo;
private boolean useExtendedVertexFormat;
@Setter
private boolean hasSnowyEntries;
@Getter
private ReferenceSet<Block> snowyBlocks = ReferenceSets.emptySet();
public BlockRenderingSettings() {
reloadRequired = false;
@@ -34,6 +46,7 @@ public class BlockRenderingSettings {
disableDirectionalShading = false;
useSeparateAo = false;
useExtendedVertexFormat = false;
hasSnowyEntries = false;
}
public void clearReloadRequired() {
@@ -74,6 +87,14 @@ public class BlockRenderingSettings {
this.blockMetaMatches = blockMetaIds;
}
public boolean hasSnowyEntries() {
return hasSnowyEntries;
}
public void setSnowyBlocks(ReferenceSet<Block> snowyBlocks) {
this.snowyBlocks = snowyBlocks != null ? snowyBlocks : ReferenceSets.emptySet();
}
public void setBlockTypeIds(Map<Block, BlockRenderLayer> blockTypeIds) {
if (this.blockTypeIds != null && this.blockTypeIds.equals(blockTypeIds)) {
return;
@@ -83,21 +104,7 @@ public class BlockRenderingSettings {
this.blockTypeIds = blockTypeIds;
}
public void setEntityIds(Object2IntFunction<NamespacedId> entityIds) {
// note: no reload needed, entities are rebuilt every frame.
this.entityIds = entityIds;
}
public void setItemIds(Object2IntFunction<NamespacedId> itemIds) {
// note: no reload needed, items are rendered every frame.
this.itemIds = itemIds;
}
public float getAmbientOcclusionLevel() {
return ambientOcclusionLevel;
}
public void setAmbientOcclusionLevel(float ambientOcclusionLevel) {
public void setAmbientOcclusionLevel(float ambientOcclusionLevel) {
if (ambientOcclusionLevel == this.ambientOcclusionLevel) {
return;
}
@@ -1,12 +1,19 @@
package net.coderbot.iris.gl.program;
import lombok.Getter;
import net.coderbot.iris.gl.GlResource;
import net.coderbot.iris.gl.blending.DepthColorStorage;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
public final class Program extends GlResource {
@Getter
private final ProgramUniforms uniforms;
@Getter
private final ProgramSamplers samplers;
@Getter
private final ProgramImages images;
Program(int program, ProgramUniforms uniforms, ProgramSamplers samplers, ProgramImages images) {
@@ -18,15 +25,15 @@ public final class Program extends GlResource {
DepthColorStorage.registerOwnedProgram(program);
}
public void use() {
GLStateManager.glUseProgram(getGlId());
uniforms.update();
samplers.update();
images.update();
}
public static void unbind() {
ProgramUniforms.clearActiveUniforms();
ProgramSamplers.clearActiveSamplers();
@@ -38,12 +45,7 @@ public final class Program extends GlResource {
DepthColorStorage.unregisterOwnedProgram(getGlId());
GLStateManager.glDeleteProgram(getGlId());
}
/**
* @return the OpenGL ID of this program.
* @deprecated this should be encapsulated eventually
*/
@Deprecated
public int getProgramId() {
return getGlId();
}
@@ -181,9 +181,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
private final boolean allowConcurrentCompute;
private final OptionalInt forcedShadowRenderDistanceChunks;
private final CloudSetting dhCloudSetting;
private Pass current = null;
private WorldRenderingPhase overridePhase = null;
private WorldRenderingPhase phase = WorldRenderingPhase.NONE;
private boolean isBeforeTranslucent;
@@ -279,7 +277,9 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
holder -> CommonUniforms.addNonDynamicUniforms(holder, programs.getPack().getIdMap(), programs.getPackDirectives(), this.updateNotifier)
);
BlockRenderingSettings.INSTANCE.setBlockMetaMatches(BlockMaterialMapping.createBlockMetaIdMap(programs.getPack().getIdMap().getBlockProperties()));
BlockRenderingSettings.INSTANCE.setBlockMetaMatches(BlockMaterialMapping.createBlockMetaIdMap(
programs.getPack().getIdMap().getBlockProperties(),
programs.getPack().getIdMap().hasLegacySection()));
BlockRenderingSettings.INSTANCE.setBlockTypeIds(BlockMaterialMapping.createBlockTypeMap(programs.getPack().getIdMap().getBlockRenderTypeMap()));
BlockRenderingSettings.INSTANCE.setEntityIds(programs.getPack().getIdMap().getEntityIdMap());
@@ -767,7 +767,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
public boolean shouldOverrideShaders() {
return isRenderingWorld && !isRenderingFullScreenPass && !isPostChain && isMainBound;
}
public Pass getActivePassProgram() {
return current;
}
public int getActivePassProgramId() {
if (current == null) return -1;
final Program p = current.getProgram();
@@ -1048,7 +1052,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
return shadowRenderTargets != null;
}
private final class Pass {
public final class Pass {
@Nullable
private final Program program;
private final GlFramebuffer framebufferBeforeTranslucents;
@@ -35,6 +35,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.regex.Pattern;
/**
* A utility class for parsing entries in item.properties, block.properties, and entities.properties files in shaderpacks
@@ -61,14 +62,24 @@ public class IdMap {
@Getter
private Map<NamespacedId, BlockRenderType> blockRenderTypeMap;
/** True when block.properties contained a preprocessor directive guarding a 1.7.10-specific section. */
private final boolean hasLegacySection;
public boolean hasLegacySection() {
return hasLegacySection;
}
private static final Pattern LEGACY_DIRECTIVE_PATTERN = Pattern.compile(
"(?m)^\\s*#\\s*(?:if|elif|ifdef|ifndef)\\b[^\\n]*\\bMC_VERSION\\b[^\\n]*\\b10710\\b");
IdMap(Path shaderPath, ShaderPackOptions shaderPackOptions, Iterable<StringPair> environmentDefines) {
// Check if block.properties has a dedicated 1.7.10 section
String rawBlockProperties = readProperties(shaderPath, "block.properties");
boolean hasLegacySection = rawBlockProperties != null
&& rawBlockProperties.contains("MC_VERSION") && rawBlockProperties.contains("10710");
this.hasLegacySection = rawBlockProperties != null
&& LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find();
Iterable<StringPair> resolvedDefines;
if (hasLegacySection) {
if (this.hasLegacySection) {
// Pack has a 1.7.10 section
resolvedDefines = environmentDefines;
loadProperties(shaderPath, "block.properties", shaderPackOptions, environmentDefines).ifPresent(blockProperties -> {
@@ -8,5 +8,5 @@ public class IrisLimits {
* It's not recommended to raise this higher than 16 until code for avoiding allocation of unused color textures
* is implemented.
*/
public static final int MAX_COLOR_BUFFERS = 16;
public static final int MAX_COLOR_BUFFERS = 32;
}
@@ -128,9 +128,9 @@ public class IrisExclusiveUniforms {
public static class WorldInfoUniforms {
public static void addWorldInfoUniforms(UniformHolder uniforms) {
final WorldClient level = Minecraft.getMinecraft().theWorld;
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "bedrockLevel", () -> 0);
uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "cloudHeight", () -> {
final WorldClient level = Minecraft.getMinecraft().theWorld;
if (level != null && level.provider != null) {
return level.provider.getCloudHeight();
} else {
@@ -138,6 +138,7 @@ public class IrisExclusiveUniforms {
}
});
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "heightLimit", () -> {
final WorldClient level = Minecraft.getMinecraft().theWorld;
if (level != null && level.provider != null) {
return level.provider.getHeight();
} else {
@@ -145,6 +146,7 @@ public class IrisExclusiveUniforms {
}
});
uniforms.uniform1b(UniformUpdateFrequency.PER_FRAME, "hasCeiling", () -> {
final WorldClient level = Minecraft.getMinecraft().theWorld;
if (level != null && level.provider != null) {
return level.provider.hasNoSky;
} else {
@@ -152,6 +154,7 @@ public class IrisExclusiveUniforms {
}
});
uniforms.uniform1b(UniformUpdateFrequency.PER_FRAME, "hasSkylight", () -> {
final WorldClient level = Minecraft.getMinecraft().theWorld;
if (level != null && level.provider != null) {
return !level.provider.hasNoSky;
} else {
@@ -159,6 +162,7 @@ public class IrisExclusiveUniforms {
}
});
uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "ambientLight", () -> {
final WorldClient level = Minecraft.getMinecraft().theWorld;
if (level != null && level.provider != null) {
return level.provider.lightBrightnessTable[0];
} else {
@@ -1,9 +0,0 @@
package net.coderbot.iris.vertices;
/**
* Some annoying global state needed for the extended vertex format disabling optimization.
*/
public class ImmediateState {
public static boolean isRenderingLevel = false;
public static boolean renderWithExtendedVertexFormat = true;
}
@@ -42,8 +42,6 @@ public abstract class MixinEntity implements IDynamicLightSource {
@Unique
private int angelica$lastLuminance = 0;
@Unique
private static long angelica$lastUpdate = 0;
@Unique
private double angelica$prevX;
@Unique
private double angelica$prevY;
@@ -54,7 +52,6 @@ public abstract class MixinEntity implements IDynamicLightSource {
@Unique
private LongOpenHashSet angelica$prevTrackedLitChunkPos = null;
@Override
public double angelica$getDynamicLightX() {
return posX;
@@ -6,7 +6,6 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.world.World;
import net.minecraft.world.WorldSettings;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@@ -16,14 +15,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(World.class)
public abstract class MixinWorld {
@Shadow
protected abstract void initialize(WorldSettings p_72963_1_);
@Shadow
public abstract Block getBlock(int p_147439_1_, int p_147439_2_, int p_147439_3_);
@Inject(method = "onEntityRemoved", at = @At("HEAD"))
private void angelica$removeEntity(Entity entity, CallbackInfo ci){
if (entity instanceof IDynamicLightSource lightSource){
private void angelica$removeEntity(Entity entity, CallbackInfo ci) {
if (entity.worldObj.isRemote && entity instanceof IDynamicLightSource lightSource) {
lightSource.angelica$setDynamicLightEnabled(false);
}
}
@@ -35,6 +35,13 @@ public class MixinSplashProgressCaching {
ImmediateModeRecorder.initSplashTessellator();
}
// VAOs aren't shared across GL contexts; the SharedDrawable the client thread just swapped
// onto has none. Core profile (macOS) rejects glValidateProgram without one.
@Inject(method = "start", at = @At("RETURN"))
private static void angelica$bindSharedDrawableVAO(CallbackInfo ci) {
GLStateManager.glBindVertexArray(GLStateManager.glGenVertexArrays());
}
/**
* On return from finish() - destroy splash tessellator and mark splash complete
*/
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.compat.DriverCompatabilityCheck;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
@@ -31,6 +32,7 @@ public class MixinInitGLStateManager {
.streamingUploadStrategy(ClientProxy.options().advanced.streamingUploadStrategy)
.framebufferSupported(OpenGlHelper.framebufferSupported)
.fboEnabled(mc.gameSettings.fboEnable)
.enableDSA(AngelicaConfig.enableDSA)
.directDrawer(TessellatorStreamingDrawer::drawDirect)
.streamingDrawerDestroy(TessellatorStreamingDrawer::destroy)
.postInitCallback(SelectionBoxRenderer::init)
@@ -0,0 +1,19 @@
package com.gtnewhorizons.angelica.mixins.early.dragonapi;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Pseudo
@Mixin(targets = { "Reika/DragonAPI/IO/Shaders/ShaderRegistry" }, remap = false)
public class MixinShaderRegistry_ParseError {
@Redirect(method = "parseError",
at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL20;glGetShaderInfoLog(II)Ljava/lang/String;"))
private static String angelica$routeInfoLogByType(int id, int maxLen) {
return GLStateManager.glIsProgram(id) ? GLStateManager.glGetProgramInfoLog(id, maxLen) : GLStateManager.glGetShaderInfoLog(id, maxLen);
}
}
@@ -55,11 +55,13 @@ public abstract class MixinRenderBlocks {
@Inject(method = "renderBlockByRenderType", at = @At("HEAD"))
private void renderingByTypeEnable(CallbackInfoReturnable<Boolean> ci) {
CTMUtils.clearCurrentCompact();
this.isRenderingByType = true;
}
@Inject(method = "renderBlockByRenderType", at = @At("TAIL"))
private void renderingByTypeDisable(CallbackInfoReturnable<Boolean> ci) {
CTMUtils.clearCurrentCompact();
this.isRenderingByType = false;
}
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.mixins.early.shaders;
import com.prupe.mcpatcher.ctm.CTMUtils;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
@@ -17,6 +18,7 @@ public class MixinTileEntityRendererDispatcher {
@Inject(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntity;DDDF)V", at = @At("HEAD"))
private void iris$setBlockEntityId(TileEntity te, double x, double y, double z, float partialTicks, CallbackInfo ci) {
CTMUtils.clearCurrentCompact();
if (te == null) {
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
return;
@@ -46,6 +48,7 @@ public class MixinTileEntityRendererDispatcher {
@Inject(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntity;DDDF)V", at = @At("RETURN"))
private void iris$resetBlockEntityId(TileEntity te, double x, double y, double z, float partialTicks, CallbackInfo ci) {
CTMUtils.clearCurrentCompact();
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
}
}