Compare commits

...
20 Commits
Author SHA1 Message Date
0e1fd9d0e9 add auto emissive overlay for any block (#1710)
Co-authored-by: VortexSo4 <[email protected]>
Co-authored-by: ah-OOG-ah <[email protected]>
2026-05-10 02:12:08 -04:00
Ruling-0 58938e82a7 Compact CTM Fixes (#1726) 2026-05-09 11:42:42 -07:00
Branan Riley be6c8c85b5 Detect vanilla AO at a higher level (#1733) 2026-05-09 11:41:59 -07:00
ABKQPOandCaedis 39bca37d0d Adjust IDynamicLightProducer to allow passing an ItemStack parameter (#1734)
Co-authored-by: Caedis <[email protected]>
2026-05-08 20:31:22 -07:00
czqwq 1c61be04a7 Fix Already tesselating logs (#1729) 2026-05-08 20:30:08 -07:00
7a97cd9386 Add RGB color system (#1669)
Co-authored-by: Death Fuel <[email protected]>
Co-authored-by: Jason Mitchell <[email protected]>
2026-05-08 18:36:51 -07:00
Kotmatross 5b09bde7a0 [NTM:Space] Fix mixins in obfuscated environment (#1738) 2026-05-08 18:24:29 +02:00
Ruling-0 edc0081b7c Fix CTM inventory texture bleed (#1721) 2026-05-04 12:25:51 -07:00
Alexdoru 913256b032 fix world client leak in CTMUtils (#1724) 2026-05-04 12:24:25 -07:00
sisyphussy e1f5083d3e Fix intel detection (#1722) 2026-05-04 12:23:50 -07:00
sisyphussy b55e693ad2 Remove DSA from GLSM & other stuff (#1723) 2026-05-04 12:23:38 -07:00
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
57 changed files with 1261 additions and 759 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
```
---
+3 -1
View File
@@ -76,7 +76,7 @@ dependencies {
// Iris Shaders
compileOnly('org.jetbrains:annotations:26.0.2')
api("com.github.GTNewHorizons:GTNHLib:0.9.36:dev")
api("com.github.GTNewHorizons:GTNHLib:0.9.52:dev")
shadowImplementation(project(":glsm")) { transitive = false }
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
@@ -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")
@@ -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
@@ -518,6 +520,12 @@ public class GLStateManager {
// After Display.create(), viewport is (0, 0, width, height)
viewportState.setViewPort(0, 0, displayWidth, displayHeight);
}
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");
}
static void init(Runnable initCallback) {
@@ -525,9 +533,6 @@ public class GLStateManager {
DEFAULT_DRAW_BUFFER = RENDER_BACKEND.getInteger(GL11.GL_DRAW_BUFFER);
final String glVendor = RENDER_BACKEND.getString(GL11.GL_VENDOR);
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
// 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);
@@ -621,16 +626,6 @@ public class GLStateManager {
return BYPASS_CACHE || !isCachingEnabled();
}
/**
* Returns true if we should use DSA for texture operations. DSA relies on cached texture bindings, which aren't tracked when caching is disabled. Also,
* proxy textures are special query textures that shouldn't use DSA.
*/
private static boolean shouldUseDSA(int target) {
if (!isCachingEnabled()) return false;
// Proxy textures are for capability queries, not real textures
return target != GL11.GL_PROXY_TEXTURE_1D && target != GL11.GL_PROXY_TEXTURE_2D && target != GL12.GL_PROXY_TEXTURE_3D;
}
// LWJGL Overrides
public static void glEnable(int cap) {
final RecordMode mode = DisplayListManager.getRecordMode();
@@ -1828,13 +1823,7 @@ public class GLStateManager {
}
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
// Use DSA to upload directly to the texture
RenderSystem.textureImage2D(getBoundTextureForServerState(), target, level, internalformat, width, height, border, format, type, pixels);
} else {
// Non-main thread or proxy texture - use direct GL call
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -1879,11 +1868,7 @@ public class GLStateManager {
}
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels != null ? pixels.asIntBuffer() : null);
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
RenderSystem.textureImage2D(getBoundTextureForServerState(), target, level, internalformat, width, height, border, format, type, pixels);
} else {
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -4295,6 +4280,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 +4301,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 +4310,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);
}
}
}
@@ -4399,6 +4392,7 @@ public class GLStateManager {
public static int glGetError() {
if (!RENDER_BACKEND.hasContext()) return 0;
if (initConfig.noErrorChecks()) return 0;
return RENDER_BACKEND.getError();
}
@@ -4453,13 +4447,7 @@ public class GLStateManager {
}
}
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
// Use DSA to upload directly to the texture - keeps GL binding state unchanged
RenderSystem.textureSubImage2D(getBoundTextureForServerState(), target, level, xoffset, yoffset, width, height, format, type, pixels);
} else {
// Non-main thread or proxy texture - use direct GL call
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
}
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -4472,13 +4460,7 @@ public class GLStateManager {
}
}
suspendPixelUnpackBuffer();
if (shouldUseDSA(target)) {
// Use DSA to upload directly to the texture
RenderSystem.textureSubImage2D(getBoundTextureForServerState(), target, level, xoffset, yoffset, width, height, format, type, pixels);
} else {
// Non-main thread or proxy texture - use direct GL call
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
}
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
}
@@ -5198,7 +5180,7 @@ public class GLStateManager {
}
public static boolean isFramebufferEnabled() {
return initConfig != null && initConfig.isFramebufferSupported() && initConfig.isFboEnabled();
return true;
}
private static final boolean FFP_WARN_ON_UNSUPPORTED = Boolean.parseBoolean(System.getProperty("angelica.ffp.warnOnUnsupported", "false"));
@@ -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.");
}
@@ -8,24 +8,24 @@ import java.util.function.Consumer;
public final class GLSMInitConfig {
private final boolean lwjglDebug;
private final StreamingUploader.UploadStrategy streamingUploadStrategy;
private final boolean framebufferSupported;
private final boolean fboEnabled;
private final Consumer<DirectTessellator> directDrawer;
private final Runnable streamingDrawerDestroy;
private final int displayWidth;
private final int displayHeight;
private final Runnable postInitCallback;
private final boolean enableDSA;
private final boolean noErrorChecks;
private GLSMInitConfig(Builder builder) {
this.lwjglDebug = builder.lwjglDebug;
this.streamingUploadStrategy = builder.streamingUploadStrategy;
this.framebufferSupported = builder.framebufferSupported;
this.fboEnabled = builder.fboEnabled;
this.noErrorChecks = builder.noErrorChecks;
this.directDrawer = builder.directDrawer;
this.streamingDrawerDestroy = builder.streamingDrawerDestroy;
this.displayWidth = builder.displayWidth;
this.displayHeight = builder.displayHeight;
this.postInitCallback = builder.postInitCallback;
this.enableDSA = builder.enableDSA;
}
public static Builder builder() {
@@ -34,24 +34,24 @@ public final class GLSMInitConfig {
public boolean isLwjglDebug() { return lwjglDebug; }
public StreamingUploader.UploadStrategy getStreamingUploadStrategy() { return streamingUploadStrategy; }
public boolean isFramebufferSupported() { return framebufferSupported; }
public boolean isFboEnabled() { return fboEnabled; }
public Consumer<DirectTessellator> getDirectDrawer() { return directDrawer; }
public Runnable getStreamingDrawerDestroy() { return streamingDrawerDestroy; }
public int getDisplayWidth() { return displayWidth; }
public int getDisplayHeight() { return displayHeight; }
public Runnable getPostInitCallback() { return postInitCallback; }
public boolean isDSAEnabled() { return enableDSA; }
public boolean noErrorChecks() { return noErrorChecks; }
public static final class Builder {
private boolean lwjglDebug = false;
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
private boolean framebufferSupported = false;
private boolean fboEnabled = false;
private Consumer<DirectTessellator> directDrawer = null;
private Runnable streamingDrawerDestroy = null;
private int displayWidth = 0;
private int displayHeight = 0;
private Runnable postInitCallback = null;
private boolean enableDSA = false;
private boolean noErrorChecks;
private Builder() {}
@@ -65,13 +65,8 @@ public final class GLSMInitConfig {
return this;
}
public Builder framebufferSupported(boolean framebufferSupported) {
this.framebufferSupported = framebufferSupported;
return this;
}
public Builder fboEnabled(boolean fboEnabled) {
this.fboEnabled = fboEnabled;
public Builder noErrorChecks(boolean noErrorChecks) {
this.noErrorChecks = noErrorChecks;
return this;
}
@@ -96,6 +91,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;
}
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

@@ -1,8 +1,10 @@
package com.gtnewhorizons.angelica.api;
import net.minecraft.item.ItemStack;
/**
* To be used for modded items that don't have blocks. Baubles for example.
*/
public interface IDynamicLightProducer {
int getLuminance();
int getLuminance(ItemStack stack);
}
@@ -4,7 +4,14 @@ import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
import com.gtnewhorizon.gtnhlib.util.font.GlyphReplacements;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_PAYLOAD;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_PAYLOAD;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
@@ -213,12 +220,14 @@ public class BatchingFontRenderer {
pushQuadIdx();
}
private void pushTexRect(float x, float y, float w, float h, float itOff, int rgba, float uStart, float vStart, float uSz, float vSz) {
private void pushTexRect(float x, float y, float w, float h, float itOff, int rgba, float uStart, float vStart, float uSz, float vSz, boolean flipV) {
ensureCapacity();
pushVtx(x + itOff, y, rgba, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff, y + h, rgba, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x + itOff + w, y, rgba, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff + w, y + h, rgba, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
float vTop = flipV ? vStart + vSz : vStart;
float vBot = flipV ? vStart : vStart + vSz;
pushVtx(x + itOff, y, rgba, uStart, vTop, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff, y + h, rgba, uStart, vBot, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x + itOff + w, y, rgba, uStart + uSz, vTop, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(x - itOff + w, y + h, rgba, uStart + uSz, vBot, uStart, uStart + uSz, vStart, vStart + vSz);
pushQuadIdx();
}
@@ -442,6 +451,55 @@ public class BatchingFontRenderer {
return (what >= fromInclusive) && (what <= toInclusive);
}
/**
* Count visible characters from {@code start} to {@code end}, skipping format codes.
* Stops at §r, any color code (§0-f, §x), or any color effect (§q, §g).
* Style codes (§k-o) and non-terminating effects (§z wave, §v dinnerbone) are skipped.
*/
private static int countVisibleChars(CharSequence str, int start, int end) {
int count = 0;
for (int i = start; i < end; i++) {
char ch = str.charAt(i);
if (ch == FORMATTING_CHAR && i + 1 < end) {
char code = Character.toLowerCase(str.charAt(i + 1));
if (code == 'r' || (code >= '0' && code <= '9') || (code >= 'a' && code <= 'f')
|| code == 'x' || code == 'q' || code == 'g') {
break;
}
i++;
} else {
count++;
}
}
return Math.max(count, 1);
}
private static int hsvToRgb(float hue, float sat, float val) {
int h = (int)(hue / 60f) % 6;
float f = hue / 60f - h;
float p = val * (1 - sat);
float q = val * (1 - f * sat);
float t = val * (1 - (1 - f) * sat);
float r, g, b;
switch (h) {
case 0: r=val; g=t; b=p; break;
case 1: r=q; g=val; b=p; break;
case 2: r=p; g=val; b=t; break;
case 3: r=p; g=q; b=val; break;
case 4: r=t; g=p; b=val; break;
default: r=val; g=p; b=q; break;
}
return ((int)(r*255) << 16) | ((int)(g*255) << 8) | (int)(b*255);
}
private static final int RAINBOW_LUT_SIZE = 24;
private static final int[] RAINBOW_LUT = new int[RAINBOW_LUT_SIZE];
static {
for (int i = 0; i < RAINBOW_LUT_SIZE; i++) {
RAINBOW_LUT[i] = hsvToRgb(i * 15f, 1f, 1f);
}
}
public boolean forceDefaults() {
return this.bookMode || this.isSGA || this.isSplash;
}
@@ -466,7 +524,8 @@ public class BatchingFontRenderer {
return forceDefaults() ? 1 : FontConfig.fontShadowOffset;
}
private static final char FORMATTING_CHAR = 167; // §
private static final double WAVE_TIME_SCALE = 5e-9;
private static final float WAVE_FREQUENCY = 0.5f;
public float drawString(final float anchorX, final float anchorY, final int color, final boolean enableShadow,
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
@@ -497,6 +556,15 @@ public class BatchingFontRenderer {
boolean curBold = false;
boolean curStrikethrough = false;
boolean curUnderline = false;
boolean curRainbow = false;
boolean curWave = false;
boolean curDinnerbone = false;
boolean curGradient = false;
int gradientStartRgb = 0, gradientEndRgb = 0;
int gradientCharIndex = 0, gradientTotalChars = 0;
float gradientStep = 0f;
int rainbowCharIndex = 0;
int visibleCharIndex = 0;
float glyphScaleY = getGlyphScaleY();
float glyphScaleX = getGlyphScaleX();
@@ -534,6 +602,16 @@ public class BatchingFontRenderer {
strikethroughStartX = strikethroughEndX;
}
if (fmtCode == 'x' && AngelicaConfig.enableRGBColors && charIdx + SECTION_X_PAYLOAD < stringEnd) {
int rgb = ColorCodeUtils.parseHexPairs(string, charIdx + 1, 6);
if (rgb != -1) {
curRainbow = false;
curGradient = false;
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
curShadowColor = (curShadowColor & 0xFF000000) | ((rgb & 0xFCFCFC) >> 2);
charIdx += SECTION_X_PAYLOAD;
}
} else {
final boolean is09 = charInRange(fmtCode, '0', '9');
final boolean isAF = charInRange(fmtCode, 'a', 'f');
if (is09 || isAF) {
@@ -542,6 +620,9 @@ public class BatchingFontRenderer {
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curGradient = false;
// wave/dinnerbone NOT reset — they're positional effects, independent of color
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
final int rgb = this.colorCode[colorIdx];
@@ -562,15 +643,41 @@ public class BatchingFontRenderer {
underlineEndX = underlineStartX;
} else if (fmtCode == 'o') {
curItalic = true;
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
curRainbow = true;
curGradient = false;
rainbowCharIndex = 0;
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
curWave = !curWave;
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
curDinnerbone = !curDinnerbone;
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
if (color1 != -1 && color2 != -1) {
curGradient = true;
curRainbow = false;
gradientStartRgb = color1;
gradientEndRgb = color2;
gradientCharIndex = 0;
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
charIdx += GRADIENT_PAYLOAD;
}
} else if (fmtCode == 'r') {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curWave = false;
curDinnerbone = false;
curGradient = false;
curColor = color;
curShadowColor = shadowColor;
}
} // close else block for non-§x codes
continue;
}
@@ -597,12 +704,31 @@ public class BatchingFontRenderer {
heightNorth = anchorY + (underlying.FONT_HEIGHT - 1.0f) * (0.5f - glyphScaleY * fontProvider.getYScaleMultiplier() / 2);
float heightSouth = (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY * fontProvider.getYScaleMultiplier();
visibleCharIndex++;
// Check ASCII space, NBSP, NNBSP
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
curX += 4 * this.getWhitespaceScale() + (curBold ? 1 : 0);
continue;
}
if (curRainbow) {
int rgbEffect = RAINBOW_LUT[rainbowCharIndex % RAINBOW_LUT_SIZE];
curColor = (curColor & 0xFF000000) | rgbEffect;
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
rainbowCharIndex++;
}
if (curGradient && gradientTotalChars > 0) {
float t = Math.min(gradientCharIndex * gradientStep, 1f);
int gr = (int)((gradientStartRgb >> 16 & 0xFF) * (1-t) + (gradientEndRgb >> 16 & 0xFF) * t);
int gg = (int)((gradientStartRgb >> 8 & 0xFF) * (1-t) + (gradientEndRgb >> 8 & 0xFF) * t);
int gb = (int)((gradientStartRgb & 0xFF) * (1-t) + (gradientEndRgb & 0xFF) * t);
int rgbEffect = (gr << 16) | (gg << 8) | gb;
curColor = (curColor & 0xFF000000) | rgbEffect;
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
gradientCharIndex++;
}
final float uStart = fontProvider.getUStart(chr);
final float vStart = fontProvider.getVStart(chr);
final float xAdvance = fontProvider.getXAdvance(chr) * glyphScaleX;
@@ -616,23 +742,30 @@ public class BatchingFontRenderer {
final ResourceLocation texture = fontProvider.getTexture(chr);
final int idxId = idxWriterIndex;
// Wave: Y offset via sine wave
float renderY = heightNorth;
if (curWave) {
float time = HUDCaching.renderingCacheOverride ? 0f : (float)((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
renderY += (float) Math.sin(visibleCharIndex * WAVE_FREQUENCY + time) * AngelicaConfig.waveAmplitude;
}
if (enableShadow) {
for (int n = 1; n <= shadowCopies; n++) {
final float shadowOffsetPart = shadowOffset * ((float) n / shadowCopies);
pushTexRect(curX + shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
pushTexRect(curX + shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
if (curBold) {
pushTexRect(curX + 2.0f * shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
pushTexRect(curX + 2.0f * shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
}
}
}
pushTexRect(curX, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
pushTexRect(curX, renderY, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz, curDinnerbone);
if (curBold) {
for (int n = 1; n <= boldCopies; n++) {
final float shadowOffsetPart = shadowOffset * ((float) n / boldCopies);
pushTexRect(curX + shadowOffsetPart, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
pushTexRect(curX + shadowOffsetPart, renderY, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz, curDinnerbone);
}
}
@@ -653,6 +786,19 @@ public class BatchingFontRenderer {
if (bookMode) { curX = (int) curX; }
underlineEndX = curX;
strikethroughEndX = curX;
if ((curRainbow || curGradient) && curUnderline && underlineStartX != underlineEndX) {
final int ulIdx = idxWriterIndex;
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
pushDrawCmd(ulIdx, 6, null, false);
underlineStartX = underlineEndX;
}
if ((curRainbow || curGradient) && curStrikethrough && strikethroughStartX != strikethroughEndX) {
final int stIdx = idxWriterIndex;
pushUntexRect(strikethroughStartX, strikethroughY, strikethroughEndX - strikethroughStartX, glyphScaleY, curColor);
pushDrawCmd(stIdx, 6, null, false);
strikethroughStartX = strikethroughEndX;
}
}
if (curUnderline && underlineStartX != underlineEndX) {
@@ -0,0 +1,144 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
/**
* Utility for converting {@code &}-based shorthand into {@code §}-based
* format codes understood by the batched font renderer.
* <p>
* {@code &#RRGGBB} -> {@code §x§R§R§G§G§B§B} (RGB color)<br>
* {@code &g&#RRGGBB&#RRGGBB} -> {@code §g§x§R§R§G§G§B§B§x§R§R§G§G§B§B} (gradient)<br>
* {@code &c}, {@code &l}, {@code &r}, {@code &q}, etc. -> {@code §c}, {@code §l}, {@code §r}, {@code §q}
* <p>
* Effect codes use low-collision letters to avoid false positives on natural text:
* rainbow = {@code &q}, wave = {@code &z}, dinnerbone = {@code &v}, gradient = {@code &g} (only with &#-prefixed colors).
*/
public final class ColorCodeUtils {
private ColorCodeUtils() {}
public static final char FORMATTING_CHAR = '§';
public static final int SECTION_X_PAYLOAD = 12;
public static final int SECTION_X_LENGTH = 14;
public static final int GRADIENT_PAYLOAD = 28;
public static final int GRADIENT_LENGTH = 30;
/** Valid single {@code &} codes. {@code g} is excluded; {@code &g} only converts as part of a gradient. */
public static final String VALID_SINGLE_CODES = "0123456789abcdefklmnorqzv";
private static final String[] SECTION_PREFIX = new String[128];
static {
for (int i = 0; i < VALID_SINGLE_CODES.length(); i++) {
char c = VALID_SINGLE_CODES.charAt(i);
SECTION_PREFIX[c] = String.valueOf(FORMATTING_CHAR) + c;
}
SECTION_PREFIX['x'] = String.valueOf(FORMATTING_CHAR) + 'x';
SECTION_PREFIX['g'] = String.valueOf(FORMATTING_CHAR) + 'g';
}
public static String sectionPrefix(char code) {
char c = (code < 128) ? Character.toLowerCase(code) : code;
return (c < 128) ? SECTION_PREFIX[c] : null;
}
public static int parseHexPairs(CharSequence str, int start, int count) {
int result = 0;
for (int i = 0; i < count; i++) {
int p = start + i * 2;
if (str.charAt(p) != FORMATTING_CHAR) return -1;
int digit = Character.digit(str.charAt(p + 1), 16);
if (digit == -1) return -1;
result = (result << 4) | digit;
}
return result;
}
public static int parseSectionXAt(CharSequence str, int start) {
if (start + SECTION_X_LENGTH > str.length()) return -1;
if (str.charAt(start) != FORMATTING_CHAR) return -1;
if (Character.toLowerCase(str.charAt(start + 1)) != 'x') return -1;
return parseHexPairs(str, start + 2, 6);
}
public static boolean isValidSectionX(CharSequence str, int start) {
return parseSectionXAt(str, start) != -1;
}
private static String lastConversionInput;
private static String lastConversionOutput;
public static String convertAmpersandToSectionX(String text) {
if (text == null || !AngelicaConfig.enableAmpersandConversion) return text;
if (text == lastConversionInput) return lastConversionOutput;
final String out = convertImpl(text);
lastConversionInput = text;
lastConversionOutput = out;
return out;
}
private static String convertImpl(String text) {
int idx = text.indexOf('&');
if (idx == -1) return text;
final int len = text.length();
StringBuilder sb = null;
int last = 0;
while (idx != -1 && idx + 1 < len) {
if (text.charAt(idx + 1) == '#' && idx + 7 < len) {
boolean validHex = true;
for (int i = 2; i <= 7; i++) {
if (Character.digit(text.charAt(idx + i), 16) == -1) {
validHex = false;
break;
}
}
if (validHex) {
if (sb == null) sb = new StringBuilder(len + 16);
sb.append(text, last, idx);
sb.append(FORMATTING_CHAR).append('x');
for (int i = 2; i <= 7; i++) {
sb.append(FORMATTING_CHAR).append(text.charAt(idx + i));
}
last = idx + 8;
idx = text.indexOf('&', last);
continue;
}
}
if (Character.toLowerCase(text.charAt(idx + 1)) == 'g' && idx + 17 < len
&& text.charAt(idx + 2) == '&' && text.charAt(idx + 3) == '#'
&& text.charAt(idx + 10) == '&' && text.charAt(idx + 11) == '#') {
boolean valid1 = true, valid2 = true;
for (int i = 4; i <= 9; i++) {
if (Character.digit(text.charAt(idx + i), 16) == -1) { valid1 = false; break; }
}
if (valid1) {
for (int i = 12; i <= 17; i++) {
if (Character.digit(text.charAt(idx + i), 16) == -1) { valid2 = false; break; }
}
}
if (valid1 && valid2) {
if (sb == null) sb = new StringBuilder(len + 16);
sb.append(text, last, idx);
sb.append(FORMATTING_CHAR).append('g');
last = idx + 2;
idx = text.indexOf('&', last);
continue;
}
}
char code = Character.toLowerCase(text.charAt(idx + 1));
if (VALID_SINGLE_CODES.indexOf(code) != -1) {
if (sb == null) sb = new StringBuilder(len + 16);
sb.append(text, last, idx);
sb.append(FORMATTING_CHAR).append(text.charAt(idx + 1));
last = idx + 2;
}
idx = text.indexOf('&', idx + 1);
}
if (sb == null) return text;
sb.append(text, last, len);
return sb.toString();
}
}
@@ -0,0 +1,39 @@
package com.gtnewhorizons.angelica.compat;
import java.util.function.Function;
import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
public class GTNHLibCompat {
public static final boolean HAS_TEXT_PREPROCESSOR;
static {
boolean found = false;
try {
FontRendering.class.getMethod("setTextPreprocessor", Function.class);
found = true;
} catch (NoSuchMethodException ignored) {}
HAS_TEXT_PREPROCESSOR = found;
}
/** Only call when {@link #HAS_TEXT_PREPROCESSOR} is true. */
public static void registerPreprocessor() {
FontRendering.setTextPreprocessor(new FontRendering.TextPreprocessor() {
@Override
public String apply(String text) {
return AngelicaConfig.enableAmpersandConversion
? ColorCodeUtils.convertAmpersandToSectionX(text)
: text;
}
@Override
public boolean handlesAmpCodes() {
return AngelicaConfig.enableAmpersandConversion;
}
});
}
}
@@ -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");
@@ -2,9 +2,11 @@ package com.gtnewhorizons.angelica.compat.bettercrashes;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
import cpw.mods.fml.common.Optional;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import vfyjxf.bettercrashes.utils.StateManager;
@Optional.Interface(modid = "angelica", iface = "vfyjxf.bettercrashes.utils.StateManager.IResettable")
@@ -25,6 +27,7 @@ public class BetterCrashesCompat implements StateManager.IResettable {
@Override
public void resetState() {
((ITessellatorData) Tessellator.instance).setDrawing(false);
AngelicaMod.LOGGER.info("Reloading SodiumRenderer");
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).angelica$reload();
AngelicaMod.LOGGER.info("Resetting GLStateManager");
@@ -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
@@ -107,6 +112,35 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean enableFontRenderer;
@Config.Comment("Enable full RGB color support (16.7M colors) using &#RRGGBB syntax in text")
@Config.DefaultBoolean(true)
public static boolean enableRGBColors;
@Config.Comment("Enable gradient text (&g&#start&#end)")
@Config.DefaultBoolean(true)
public static boolean enableGradients;
@Config.Comment("Enable rainbow cycling text (&q)")
@Config.DefaultBoolean(true)
public static boolean enableRainbow;
@Config.Comment("Enable wave/bounce animated text (&z)")
@Config.DefaultBoolean(true)
public static boolean enableWaveText;
@Config.Comment("Enable upside-down text (&v)")
@Config.DefaultBoolean(true)
public static boolean enableDinnerboneText;
@Config.Comment("Wave text amplitude (how far characters bounce)")
@Config.DefaultFloat(2.0f)
@Config.RangeFloat(min = 1.0f, max = 8.0f)
public static float waveAmplitude;
@Config.Comment("Convert &-prefix format codes (&#RRGGBB, &c, &l, etc.) at render time")
@Config.DefaultBoolean(true)
public static boolean enableAmpersandConversion;
@Config.Comment("Enable Dynamic Lights")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
@@ -297,4 +331,9 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean disableGLVersionPinning;
@Config.Comment("Disables GL Error checks. Always set to false in dev env or if LWJGL debug is on. Improves performance.")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean disableErrorChecks;
}
@@ -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.
*
@@ -559,13 +573,13 @@ public class DynamicLights {
*/
public static int getLuminanceFromItemStack(@NotNull ItemStack stack) {
final Item item = stack.getItem();
if (item instanceof ItemBlock itemBlock) {
if (item instanceof IDynamicLightProducer lightProducer) {
return lightProducer.getLuminance(stack);
} else if (item instanceof ItemBlock itemBlock) {
final Block block = itemBlock.field_150939_a;
if (block != null) {
return block.getLightValue();
}
} else if (item instanceof IDynamicLightProducer lightProducer){
return lightProducer.getLuminance();
}
if (item == Items.lava_bucket) return Blocks.lava.getLightValue();
@@ -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.
@@ -76,7 +76,7 @@ public class HUDCaching {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.enableTexture();
if (!AngelicaConfig.hudCachingActive || !GLStateManager.isFramebufferEnabled()) {
if (!AngelicaConfig.hudCachingActive) {
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
return;
}
@@ -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();
}
}
});
}
}
@@ -194,6 +194,7 @@ public enum Mixins implements IMixins {
, "rendering.MixinTileEntity"
, "rendering.MixinTileEntityMobSpawner"
, "rendering.MixinTileEntityRendererDispatcher"
, "rendering.MixinRenderBlocksEmissive"
)
),
@@ -388,7 +389,7 @@ public enum Mixins implements IMixins {
.addRequiredMod(TargetedMod.MINEFACTORY_RELOADED)
.setApplyIf(() -> CompatConfig.fixMinefactoryReloaded)
.addClientMixins("client.minefactoryreloaded.MixinRedNetCableRenderer")),
NTM_SPACE_COMPAT(new MixinBuilder("Multiple fixes for NTM:Space")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.NTM_SPACE)
@@ -22,6 +22,7 @@ import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
import com.gtnewhorizons.angelica.render.CloudRenderer;
import com.gtnewhorizons.angelica.render.EmissiveTextureAutoloader;
import com.gtnewhorizons.angelica.rendering.AngelicaBlockSafetyRegistry;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
@@ -102,30 +103,11 @@ public final class ClientProxy extends CommonProxy {
super.preInit(event);
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(new EmissiveTextureAutoloader());
ModelRegistry.registerModid(MOD_ID);
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 +152,6 @@ public final class ClientProxy extends CommonProxy {
}
}
@Override
public void postInit(FMLPostInitializationEvent event) {
super.postInit(event);
@@ -190,11 +171,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];
@@ -0,0 +1,60 @@
package com.gtnewhorizons.angelica.render;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.TextureStitchEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@SideOnly(Side.CLIENT)
public class EmissiveTextureAutoloader {
@SubscribeEvent
public void onTextureStitchPre(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() != 0) return;
TextureMap map = event.map;
EmissiveTextureHelper.clearCache();
@SuppressWarnings("unchecked")
Map<String, ?> registeredSprites = map.mapRegisteredSprites;
List<String> toRegister = new ArrayList<>();
for (String baseName : registeredSprites.keySet()) {
if (baseName.endsWith("_emissive")) continue;
String emissiveName = baseName + "_emissive";
if (resourceExists(emissiveName)) {
toRegister.add(baseName);
}
}
for (String baseName : toRegister) {
String emissiveName = baseName + "_emissive";
IIcon emissiveIcon = map.registerIcon(emissiveName);
EmissiveTextureHelper.cacheEmissive(baseName, emissiveIcon);
}
}
private boolean resourceExists(String iconName) {
ResourceLocation loc = new ResourceLocation(iconName);
ResourceLocation full = new ResourceLocation(
loc.getResourceDomain(),
"textures/blocks/" + loc.getResourcePath() + ".png"
);
try {
Minecraft.getMinecraft().getResourceManager().getResource(full);
return true;
} catch (Exception ignored) {
return false;
}
}
}
@@ -0,0 +1,54 @@
package com.gtnewhorizons.angelica.render;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.util.IIcon;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.concurrent.ConcurrentHashMap;
@SideOnly(Side.CLIENT)
public final class EmissiveTextureHelper {
private static final ConcurrentHashMap<String, IIcon> EMISSIVE_ICON_CACHE = new ConcurrentHashMap<>();
public static IIcon getEmissiveIcon(Block block, int side, int metadata) {
IIcon baseIcon = block.getIcon(side, metadata);
if (baseIcon == null) return null;
String baseIconName = baseIcon.getIconName();
if (baseIconName == null) return null;
return EMISSIVE_ICON_CACHE.get(baseIconName);
}
public static void clearCache() {
EMISSIVE_ICON_CACHE.clear();
}
public static void cacheEmissive(String baseIconName, IIcon emissiveIcon) {
EMISSIVE_ICON_CACHE.put(baseIconName, emissiveIcon);
}
public static void renderEmissiveBlockOverlay(RenderBlocks renderer, Block block, int x, int y, int z, int metadata) {
Tessellator tess = Tessellator.instance;
for (int side = 0; side < 6; side++) {
IIcon icon = getEmissiveIcon(block, side, metadata);
if (icon == null) continue;
tess.setColorOpaque_F(1f, 1f, 1f);
switch (side) {
case 0 -> renderer.renderFaceYNeg(block, x, y, z, icon);
case 1 -> renderer.renderFaceYPos(block, x, y, z, icon);
case 2 -> renderer.renderFaceZNeg(block, x, y, z, icon);
case 3 -> renderer.renderFaceZPos(block, x, y, z, icon);
case 4 -> renderer.renderFaceXNeg(block, x, y, z, icon);
case 5 -> renderer.renderFaceXPos(block, x, y, z, icon);
}
}
}
}
@@ -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);
}
}
@@ -158,6 +158,34 @@ final class BlockOrientation extends RenderBlockState {
return origIcon == neighbor.getIcon(blockAccess, neighborX, neighborY, neighborZ, getTextureFaceOrig());
}
@Override
public BlockOrientation copy() {
BlockOrientation copy = new BlockOrientation();
copy.x = x;
copy.y = y;
copy.z = z;
copy.metadata = metadata;
copy.altMetadata = altMetadata;
copy.metadataBits = metadataBits;
copy.renderType = renderType;
copy.blockFace = blockFace;
copy.textureFace = textureFace;
copy.textureFaceOrig = textureFaceOrig;
copy.rotateUV = rotateUV;
copy.blockAccess = blockAccess;
copy.block = block;
copy.useAO = useAO;
copy.inWorld = inWorld;
copy.matcher = matcher;
copy.offsetsComputed = offsetsComputed;
copy.haveOffsets = haveOffsets;
copy.dx = dx;
copy.dy = dy;
copy.dz = dz;
return copy;
}
void setBlock(Block block, IBlockAccess blockAccess, int x, int y, int z) {
this.block = block;
this.blockAccess = blockAccess;
@@ -15,6 +15,7 @@ import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.IBlockAccess;
import com.github.bsideup.jabel.Desugar;
import com.prupe.mcpatcher.MCLogger;
import com.prupe.mcpatcher.MCPatcherUtils;
import com.prupe.mcpatcher.mal.block.BlockAPI;
@@ -43,13 +44,16 @@ public class CTMUtils {
private static Overrides newOverrides = null;
private static TileLoader tileLoader;
private static final ThreadLocal<TileOverrideImpl.CTMCompact> CURRENT_COMPACT = new ThreadLocal<>();
@Desugar
public record CTMCompactContext(TileOverrideImpl.CTMCompact compact, RenderBlockState renderBlockState) {}
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact) {
CURRENT_COMPACT.set(compact);
private static final ThreadLocal<CTMCompactContext> CURRENT_COMPACT = new ThreadLocal<>();
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact, RenderBlockState renderBlockState) {
CURRENT_COMPACT.set(new CTMCompactContext(compact, renderBlockState.copy()));
}
public static TileOverrideImpl.CTMCompact getCurrentCompact() {
public static CTMCompactContext getCurrentCompact() {
return CURRENT_COMPACT.get();
}
@@ -180,6 +184,7 @@ public class CTMUtils {
local.renderBlockState.setBlock(block, blockAccess, x, y, z);
local.renderBlockState.setFace(face);
TileOverride lastOverride = local.ijkIterator.go(local.renderBlockState, icon);
local.renderBlockState.blockAccess = null;
if (lastOverride != null) {
return skipDefaultRendering(block) ? RenderBlocksUtils.blankIcon : local.ijkIterator.getIcon();
}
@@ -1,70 +1,18 @@
package com.prupe.mcpatcher.ctm;
import com.github.bsideup.jabel.Desugar;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
public final class CompactCtmQuadProcessor {
private static final int LEFT = 0;
private static final int UP_LEFT = 1;
private static final int UP = 2;
private static final int UP_RIGHT = 3;
private static final int RIGHT = 4;
private static final int DOWN_RIGHT = 5;
private static final int DOWN = 6;
private static final int DOWN_LEFT = 7;
private static final int SPRITE_DEFAULT = 0;
private static final int SPRITE_ALL = 1;
private static final int SPRITE_HORIZONTAL = 2;
private static final int SPRITE_VERTICAL = 3;
private static final int SPRITE_MIXED = 4;
private static final int[] CTM_47_BY_CONNECTIONS = {
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
16,18,16,18,6,46,6,21,16,18,16,18,28,9,28,22,
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
37,40,37,40,30,8,30,34,37,40,37,40,25,23,25,45,
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
16,42,16,42,6,20,6,10,16,42,16,42,28,35,28,44,
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
37,38,37,38,30,11,30,32,37,38,37,38,25,33,25,26
};
/**
* Face-local basis:
* right = increasing U on the face
* down = increasing V on the face
*/
private static final FaceBasis[] FACE_BASIS = {
new FaceBasis( 1, 0, 0, 0, 0, 1), // 0: Y-
new FaceBasis( 1, 0, 0, 0, 0, 1), // 1: Y+
new FaceBasis(-1, 0, 0, 0, -1, 0), // 2: Z-
new FaceBasis( 1, 0, 0, 0, -1, 0), // 3: Z+
new FaceBasis( 0, 0, 1, 0, -1, 0), // 4: X-
new FaceBasis( 0, 0, -1, 0, -1, 0) // 5: X+
};
/**
* Neighbor offsets in face-local coordinates:
* 0 = left, 1 = up-left, 2 = up, 3 = up-right,
* 4 = right, 5 = down-right, 6 = down, 7 = down-left
*/
private static final int[] RIGHT_FACTOR = {-1, -1, 0, 1, 1, 1, 0, -1};
private static final int[] DOWN_FACTOR = { 0, -1, -1, -1, 0, 1, 1, 1};
/**
* Quadrant side references:
* 0 = top-left
@@ -72,41 +20,58 @@ public final class CompactCtmQuadProcessor {
* 2 = bottom-right
* 3 = bottom-left
*/
private static final int[] QUAD_A = {LEFT, UP, RIGHT, DOWN};
private static final int[] QUAD_B = {UP, RIGHT, DOWN, LEFT};
private static final int[] QUAD_DIAG = {UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT};
private static final int[] QUAD_A = {RenderBlockState.REL_L, RenderBlockState.REL_U,
RenderBlockState.REL_R, RenderBlockState.REL_D};
private static final int[] QUAD_B = {RenderBlockState.REL_U, RenderBlockState.REL_R,
RenderBlockState.REL_D, RenderBlockState.REL_L};
private static final int[] QUAD_DIAG = {RenderBlockState.REL_UL, RenderBlockState.REL_UR,
RenderBlockState.REL_DR, RenderBlockState.REL_DL};
/** 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;
private final TileOverrideImpl.CTMCompact override;
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props) {
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props, TileOverrideImpl.CTMCompact compact) {
this.sprites = new IIcon[5];
if (sprites != null) {
System.arraycopy(sprites, 0, this.sprites, 0, Math.min(sprites.length, this.sprites.length));
}
this.props = props;
this.override = compact;
}
public boolean processFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon origIcon) {
int connections = CtmConnectionHelper.getConnections(world, x, y, z, face, props.getInnerSeams());
public boolean processFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon origIcon) {
int neighborBits = override.getNeighborBits(renderBlockState, origIcon);
int ctmIndex = CTM_47_BY_CONNECTIONS[connections & 0xFF];
int ctmIndex = TileOverride.neighborMap[neighborBits & 0xFF];
IIcon replacement = getReplacementIcon(ctmIndex);
if (replacement != null) {
renderWholeFace(rb, world, x, y, z, face, replacement);
renderWholeFace(rb, renderBlockState, replacement);
return true;
}
if (connections == 0 || connections == 0xFF) {
IIcon sprite = sprites[getCompactSpriteIndex(connections)];
if (neighborBits == 0 || neighborBits == 0xFF) {
IIcon sprite = sprites[getCompactSpriteIndex(neighborBits)];
if (sprite != null) {
renderWholeFace(rb, world, x, y, z, face, sprite);
renderWholeFace(rb, renderBlockState, sprite);
return true;
}
return false;
}
renderSplitFace(rb, world, x, y, z, face, connections);
renderSplitFace(rb, renderBlockState, neighborBits);
return true;
}
@@ -136,54 +101,137 @@ public final class CompactCtmQuadProcessor {
return SPRITE_ALL;
}
if ((connections & ((1 << LEFT) | (1 << RIGHT))) == ((1 << LEFT) | (1 << RIGHT))) {
if ((connections & ((1 << RenderBlockState.REL_L) | (1 << RenderBlockState.REL_R))) ==
((1 << RenderBlockState.REL_L) | (1 << RenderBlockState.REL_R))) {
return SPRITE_HORIZONTAL;
}
if ((connections & ((1 << UP) | (1 << DOWN))) == ((1 << UP) | (1 << DOWN))) {
if ((connections & ((1 << RenderBlockState.REL_U) | (1 << RenderBlockState.REL_D))) ==
((1 << RenderBlockState.REL_U) | (1 << RenderBlockState.REL_D))) {
return SPRITE_VERTICAL;
}
return SPRITE_DEFAULT;
}
private void renderSplitFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, int connections) {
Block block = world.getBlock(x, y, z);
private void renderSplitFace(RenderBlocks rb, RenderBlockState renderBlockState, int connections) {
final int face = renderBlockState.getBlockFace();
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);
renderFace(rb, renderBlockState, 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 void renderWholeFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon icon) {
Block block = world.getBlock(x, y, z);
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, RenderBlockState renderBlockState, IIcon icon) {
IIcon saved = rb.overrideBlockTexture;
rb.overrideBlockTexture = icon;
try {
renderFace(rb, block, x, y, z, face, icon);
renderFace(rb, renderBlockState, icon);
} finally {
rb.overrideBlockTexture = saved;
}
}
private void renderFace(RenderBlocks rb, Block block, int x, int y, int z, int face, IIcon icon) {
private void renderFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon) {
int face = renderBlockState.getBlockFace();
Block block = renderBlockState.getBlock();
int x = renderBlockState.getX();
int y = renderBlockState.getY();
int z = renderBlockState.getZ();
boolean originalFlip = rb.field_152631_f;
rb.field_152631_f = (face == 2 || face == 5);
try {
@@ -271,52 +319,10 @@ public final class CompactCtmQuadProcessor {
if (connA ^ connB) {
int connectedBit = connA ? sideA : sideB;
return (connectedBit == LEFT || connectedBit == RIGHT) ? SPRITE_VERTICAL : SPRITE_HORIZONTAL;
return (connectedBit == RenderBlockState.REL_L || connectedBit == RenderBlockState.REL_R) ? SPRITE_VERTICAL
: SPRITE_HORIZONTAL;
}
return SPRITE_DEFAULT;
}
@Desugar
private record FaceBasis(int rightX, int rightY, int rightZ, int downX, int downY, int downZ) { }
private static final class CtmConnectionHelper {
public static int getConnections(IBlockAccess world, int x, int y, int z, int face, boolean innerSeams) {
Block self = world.getBlock(x, y, z);
FaceBasis basis = FACE_BASIS[face];
int bits = 0;
for (int i = 0; i < 8; i++) {
int dx = RIGHT_FACTOR[i] * basis.rightX + DOWN_FACTOR[i] * basis.downX;
int dy = RIGHT_FACTOR[i] * basis.rightY + DOWN_FACTOR[i] * basis.downY;
int dz = RIGHT_FACTOR[i] * basis.rightZ + DOWN_FACTOR[i] * basis.downZ;
int nx = x + dx;
int ny = y + dy;
int nz = z + dz;
if (world.getBlock(nx, ny, nz) != self) {
continue;
}
if (innerSeams && !shouldRenderInnerSeam(world, nx, ny, nz, face)) {
continue;
}
bits |= (1 << i);
}
return bits;
}
private static boolean shouldRenderInnerSeam(IBlockAccess world, int x, int y, int z, int face) {
int[] normal = RenderBlockState.NORMALS[face];
int sx = x + normal[0];
int sy = y + normal[1];
int sz = z + normal[2];
return world.getBlock(sx, sy, sz).shouldSideBeRendered(world, sx, sy, sz, face);
}
}
}
@@ -139,4 +139,6 @@ abstract public class RenderBlockState {
abstract public boolean shouldConnectByTile(Block neighbor, IIcon origIcon, int neighborX, int neighborY,
int neighborZ);
abstract public RenderBlockState copy();
}
@@ -48,6 +48,28 @@ public abstract class TileOverride implements Comparable<TileOverride> {
private final List<ResourceLocation> tileNames = new ArrayList<>();
protected IIcon[] icons;
// Index into this array is formed from these bit values:
// 128 64 32
// 1 * 16
// 2 4 8
protected static final int[] neighborMap = new int[] {
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24, 43,
16, 18, 16, 18, 6, 46, 6, 21, 16, 18, 16, 18, 28, 9, 28, 22,
36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24, 43,
37, 40, 37, 40, 30, 8, 30, 34, 37, 40, 37, 40, 25, 23, 25, 45,
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
36, 39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27,
16, 42, 16, 42, 6, 20, 6, 10, 16, 42, 16, 42, 28, 35, 28, 44,
36, 39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27,
37, 38, 37, 38, 30, 11, 30, 32, 37, 38, 37, 38, 25, 33, 25, 26, };
static TileOverride create(ResourceLocation propertiesFile, TileLoader tileLoader) {
if (propertiesFile == null) {
return null;
@@ -353,6 +375,16 @@ public abstract class TileOverride implements Comparable<TileOverride> {
return shouldConnect(renderBlockState, icon, renderBlockState.getOffset(blockFace, relativeDirection));
}
protected final int getNeighborBits(RenderBlockState renderBlockState, IIcon origIcon) {
int neighborBits = 0;
for (int bit = 0; bit < 8; bit++) {
if (shouldConnect(renderBlockState, origIcon, bit)) {
neighborBits |= (1 << bit);
}
}
return neighborBits;
}
private boolean shouldConnect(RenderBlockState renderBlockState, IIcon icon, int[] offset) {
IBlockAccess blockAccess = renderBlockState.getBlockAccess();
Block block = renderBlockState.getBlock();
@@ -22,21 +22,6 @@ public class TileOverrideImpl {
final static class CTM extends TileOverride {
// Index into this array is formed from these bit values:
// 128 64 32
// 1 * 16
// 2 4 8
private static final int[] neighborMap = new int[] { 0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2,
1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2,
1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24,
43, 16, 18, 16, 18, 6, 46, 6, 21, 16, 18, 16, 18, 28, 9, 28, 22, 36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36,
17, 24, 19, 24, 43, 37, 40, 37, 40, 30, 8, 30, 34, 37, 40, 37, 40, 25, 23, 25, 45, 0, 3, 0, 3, 12, 5, 12,
15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 0, 3, 0, 3, 12, 5, 12,
15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 36, 39, 36, 39, 24, 41,
24, 27, 36, 39, 36, 39, 24, 41, 24, 27, 16, 42, 16, 42, 6, 20, 6, 10, 16, 42, 16, 42, 28, 35, 28, 44, 36,
39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27, 37, 38, 37, 38, 30, 11, 30, 32, 37, 38, 37, 38,
25, 33, 25, 26, };
CTM(PropertiesFile properties, TileLoader tileLoader) {
super(properties, tileLoader);
}
@@ -62,13 +47,7 @@ public class TileOverrideImpl {
@Override
IIcon getTileWorld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
int neighborBits = 0;
for (int bit = 0; bit < 8; bit++) {
if (shouldConnect(renderBlockState, origIcon, bit)) {
neighborBits |= (1 << bit);
}
}
return icons[neighborMap[neighborBits]];
return icons[neighborMap[getNeighborBits(renderBlockState, origIcon)]];
}
@Override
@@ -520,7 +499,7 @@ public class TileOverrideImpl {
public CompactCtmQuadProcessor getProcessor() {
CompactCtmQuadProcessor p = this.processor;
if (p == null) {
p = new CompactCtmQuadProcessor(icons, properties);
p = new CompactCtmQuadProcessor(icons, properties, this);
this.processor = p;
}
return p;
@@ -123,7 +123,7 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
IIcon newIcon = getTile(override, renderBlockState, origIcon);
if (newIcon != null) {
if (override instanceof TileOverrideImpl.CTMCompact compact) {
CTMUtils.setCurrentCompact(compact);
CTMUtils.setCurrentCompact(compact, renderBlockState);
}
lastMatchedOverride = override;
skipOverrides.add(override);
@@ -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;
}
@@ -534,4 +534,61 @@ public class SodiumGameOptionPages {
return new OptionPage(I18n.format("sodium.options.pages.appearance"), ImmutableList.copyOf(groups));
}
public static OptionPage text() {
final List<OptionGroup> groups = new ArrayList<>();
groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("RGB Colors")
.setTooltip("Enable hex colors via &#RRGGBB syntax")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableRGBColors = value,
opts -> AngelicaConfig.enableRGBColors)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Gradients")
.setTooltip("Smooth color transitions via &g&#start&#end")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableGradients = value,
opts -> AngelicaConfig.enableGradients)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Rainbow")
.setTooltip("Cycling rainbow colors via &q")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableRainbow = value,
opts -> AngelicaConfig.enableRainbow)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Wave Text")
.setTooltip("Animated bouncing text via &z")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableWaveText = value,
opts -> AngelicaConfig.enableWaveText)
.build())
.add(OptionImpl.createBuilder(int.class, angelicaOpts)
.setName("Wave Amplitude")
.setTooltip("How far wave text bounces (default 2.0)")
.setControl(option -> new SliderControl(option, 10, 80, 5, v -> (v / 10) + "." + (v % 10)))
.setBinding((opts, value) -> AngelicaConfig.waveAmplitude = value / 10f, opts -> Math.round(AngelicaConfig.waveAmplitude * 10f))
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Dinnerbone Text")
.setTooltip("Flipped upside down text via &v")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableDinnerboneText = value,
opts -> AngelicaConfig.enableDinnerboneText)
.build())
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName("Ampersand Conversion")
.setTooltip("Convert & prefix codes to formatting codes at render time")
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> AngelicaConfig.enableAmpersandConversion = value,
opts -> AngelicaConfig.enableAmpersandConversion)
.build())
.build());
return new OptionPage("Text Effects", ImmutableList.copyOf(groups));
}
}
@@ -63,6 +63,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
this.pages.add(SodiumGameOptionPages.advanced());
this.pages.add(SodiumGameOptionPages.performance());
this.pages.add(SodiumGameOptionPages.appearance());
this.pages.add(SodiumGameOptionPages.text());
// Add Dynamic Lights page if enabled
if (DynamicLights.configEnabled) {
@@ -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);
}
}
@@ -2,6 +2,13 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.fontrenderer;
import com.gtnewhorizon.gtnhlib.util.font.IFontParameters;
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_LENGTH;
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
import com.gtnewhorizons.angelica.compat.GTNHLibCompat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
import net.minecraft.client.gui.FontRenderer;
@@ -16,6 +23,7 @@ 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;
@@ -110,16 +118,13 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
@Unique
public BatchingFontRenderer angelica$batcher;
@Unique
private static final char angelica$FORMATTING_CHAR = 167; // §
@Unique
private static final float angelica$1_over_255 = 1.0f/255.0f; // §
@Inject(method = "<init>", at = @At("TAIL"))
private void angelica$injectBatcher(GameSettings settings, ResourceLocation fontLocation, TextureManager texManager,
boolean unicodeMode, CallbackInfo ci) {
angelica$batcher = new BatchingFontRenderer((FontRenderer) (Object) this, this.charWidth, this.colorCode, this.locationFontTexture);
if (GTNHLibCompat.HAS_TEXT_PREPROCESSOR) {
GTNHLibCompat.registerPreprocessor();
}
}
@Unique
@@ -158,6 +163,8 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
}
else
{
text = ColorCodeUtils.convertAmpersandToSectionX(text);
if (this.bidiFlag)
{
text = this.bidiReorder(text);
@@ -226,4 +233,91 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
public float getCharWidthFine(char chr) {
return angelica$getBatcher().getCharWidthFine(chr);
}
@Inject(method = "getFormatFromString", at = @At("HEAD"), cancellable = true)
private static void angelica$getFormatFromString(String text, CallbackInfoReturnable<String> cir) {
if (!AngelicaConfig.enableRGBColors) return;
cir.setReturnValue(angelica$extractFormat(text));
}
@Unique private static final StringBuilder EXTRACT_STYLES = new StringBuilder();
@Unique private static final StringBuilder EXTRACT_EFFECTS = new StringBuilder();
@Unique private static final StringBuilder EXTRACT_COLOR = new StringBuilder();
@Unique private static final StringBuilder EXTRACT_RESULT = new StringBuilder();
@Unique
private static String angelica$extractFormat(String text) {
text = ColorCodeUtils.convertAmpersandToSectionX(text);
final int len = text.length();
if (len < 2) return "";
String lastColor = "";
final StringBuilder styles = EXTRACT_STYLES; styles.setLength(0);
final StringBuilder effects = EXTRACT_EFFECTS; effects.setLength(0);
for (int i = 0; i < len - 1; i++) {
if (text.charAt(i) != FORMATTING_CHAR) continue;
final char code = Character.toLowerCase(text.charAt(i + 1));
if (code == 'g' && i + GRADIENT_LENGTH <= len && ColorCodeUtils.isValidSectionX(text, i + 2) && ColorCodeUtils.isValidSectionX(text, i + 2 + SECTION_X_LENGTH)) {
final StringBuilder cb = EXTRACT_COLOR; cb.setLength(0);
for (int j = 0; j < GRADIENT_LENGTH; j++) cb.append(text.charAt(i + j));
lastColor = cb.toString();
styles.setLength(0);
effects.setLength(0);
i += GRADIENT_LENGTH - 1;
continue;
}
if (code == 'x' && i + SECTION_X_LENGTH <= len && ColorCodeUtils.isValidSectionX(text, i)) {
final StringBuilder cb = EXTRACT_COLOR; cb.setLength(0);
for (int j = 0; j < SECTION_X_LENGTH; j++) cb.append(text.charAt(i + j));
lastColor = cb.toString();
styles.setLength(0);
effects.setLength(0);
i += SECTION_X_LENGTH - 1;
continue;
}
if ((code >= '0' && code <= '9') || (code >= 'a' && code <= 'f')) {
lastColor = ColorCodeUtils.sectionPrefix(code);
styles.setLength(0);
effects.setLength(0);
i++;
} else if (code == 'r') {
lastColor = "";
styles.setLength(0);
effects.setLength(0);
i++;
} else if (code >= 'k' && code <= 'o') {
styles.append(ColorCodeUtils.sectionPrefix(code));
i++;
} else if (code == 'q' || code == 'z' || code == 'v') {
effects.append(ColorCodeUtils.sectionPrefix(code));
i++;
} else {
i++;
}
}
if (styles.length() == 0 && effects.length() == 0) return lastColor;
final StringBuilder result = EXTRACT_RESULT; result.setLength(0);
result.append(lastColor).append(styles).append(effects);
return result.toString();
}
@ModifyVariable(method = "getStringWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private String angelica$convertBeforeWidth(String str) {
return ColorCodeUtils.convertAmpersandToSectionX(str);
}
@ModifyVariable(method = "listFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private String angelica$convertBeforeListWrap(String str) {
return ColorCodeUtils.convertAmpersandToSectionX(str);
}
@ModifyVariable(method = "wrapFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
private String angelica$convertBeforeWrap(String str) {
return ColorCodeUtils.convertAmpersandToSectionX(str);
}
}
@@ -1,7 +1,9 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
import com.gtnewhorizon.gtnhlib.core.GTNHLibCore;
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;
@@ -24,13 +26,14 @@ public class MixinInitGLStateManager {
@Inject(method = "initializeTextures", at = @At("RETURN"))
private static void angelica$initializeGLStateManager(CallbackInfo ci) {
final Minecraft mc = Minecraft.getMinecraft();
mc.gameSettings.fboEnable = true; // Angelica & many other GTNH features require FBO's
GLStateManager.setDrawableGL(Display.getDrawable());
GLStateManager.initialize(GLSMInitConfig.builder()
.displaySize(mc.displayWidth, mc.displayHeight)
.lwjglDebug(AngelicaMod.lwjglDebug)
.streamingUploadStrategy(ClientProxy.options().advanced.streamingUploadStrategy)
.framebufferSupported(OpenGlHelper.framebufferSupported)
.fboEnabled(mc.gameSettings.fboEnable)
.noErrorChecks(AngelicaConfig.disableErrorChecks && GTNHLibCore.isObf() && !AngelicaMod.lwjglDebug)
.enableDSA(AngelicaConfig.enableDSA)
.directDrawer(TessellatorStreamingDrawer::drawDirect)
.streamingDrawerDestroy(TessellatorStreamingDrawer::destroy)
.postInitCallback(SelectionBoxRenderer::init)
@@ -8,7 +8,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import com.prupe.mcpatcher.ctm.CTMUtils;
import com.prupe.mcpatcher.ctm.CompactCtmQuadProcessor;
import com.prupe.mcpatcher.ctm.TileOverrideImpl;
import com.prupe.mcpatcher.ctm.RenderBlockState;
import cpw.mods.fml.client.registry.RenderingRegistry;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
@@ -20,7 +20,6 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
@@ -153,6 +152,7 @@ public abstract class MixinRenderBlocks {
try {
return RenderingRegistry.instance().renderWorldBlock(rb, world, x, y, z, block, modelId);
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
CTMUtils.clearCurrentCompact();
// Render Error Block
int meta = exceptionErrorBlockMap.getOrDefault(e.getClass(), 0);
rb.overrideBlockTexture = BlockError.icons[exceptionErrorBlockMap.getOrDefault(e.getClass(), 0)];
@@ -168,19 +168,16 @@ public abstract class MixinRenderBlocks {
return false;
}
@ModifyExpressionValue(method = { "renderFaceXPos", "renderFaceYPos", "renderFaceZPos",
"renderFaceXNeg", "renderFaceYNeg", "renderFaceZNeg" },
at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/renderer/RenderBlocks;enableAO:Z"))
@ModifyExpressionValue(method = { "renderStandardBlockWithColorMultiplier" },
at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/renderer/RenderBlocks;renderAllFaces:Z"))
private boolean applyAOBrightness(boolean original, @Local(ordinal = 0) Tessellator tessellator) {
((StateAwareTessellator)tessellator).angelica$setAppliedAo(this.applyingCeleritasAO);
return original;
}
@Inject(method = { "renderFaceXPos", "renderFaceYPos", "renderFaceZPos",
"renderFaceXNeg", "renderFaceYNeg", "renderFaceZNeg" },
@Inject(method = { "renderStandardBlockWithColorMultiplier" },
at = @At("RETURN"))
private void resetAOFlag(Block p_147764_1_, double p_147764_2_, double p_147764_4_, double p_147764_6_, IIcon p_147764_8_, CallbackInfo ci,
@Local(ordinal = 0) Tessellator tessellator) {
private void resetAOFlag(Block p_147736_1_, int p_147736_2_, int p_147736_3_, int p_147736_4_, float p_147736_5_, float p_147736_6_, float p_147736_7_, CallbackInfoReturnable<Boolean> cir, @Local(ordinal = 0) Tessellator tessellator) {
((StateAwareTessellator)tessellator).angelica$setAppliedAo(false);
}
@@ -188,80 +185,51 @@ public abstract class MixinRenderBlocks {
public IBlockAccess blockAccess;
@Unique
private boolean compactCtmRendering = false;
@Unique
private CompactCtmQuadProcessor getCompactProcessor() {
TileOverrideImpl.CTMCompact compact = CTMUtils.getCurrentCompact();
if (compact == null) {
return null;
}
return compact.getProcessor();
}
private void handleCompactCtmFace(int face, Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
// recursion protection
if (compactCtmRendering) {
private void angelica$handleCompactCtmFace(IIcon icon, CallbackInfo ci) {
CTMUtils.CTMCompactContext ctx = CTMUtils.getCurrentCompact();
if (ctx == null) {
return;
}
CompactCtmQuadProcessor processor = ctx.compact().getProcessor();
RenderBlockState renderBlockState = ctx.renderBlockState();
CTMUtils.clearCurrentCompact();
if (this.blockAccess == null) {
return;
}
CompactCtmQuadProcessor processor = getCompactProcessor();
if (processor == null) {
return;
}
compactCtmRendering = true;
try {
boolean handled = processor.processFace(
(RenderBlocks)(Object)this,
this.blockAccess,
MathHelper.floor_double(x),
MathHelper.floor_double(y),
MathHelper.floor_double(z),
face,
icon
);
if (handled) {
ci.cancel();
}
} finally {
compactCtmRendering = false;
if (processor.processFace((RenderBlocks)(Object)this, renderBlockState, icon)) {
ci.cancel();
}
}
@Inject(method = "renderFaceYNeg", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceYNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(0, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceYPos", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceYPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(1, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceZNeg", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceZNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(2, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceZPos", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceZPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(3, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceXNeg", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceXNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(4, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderFaceXPos", at = @At("HEAD"), cancellable = true)
private void compactCtm_onRenderFaceXPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
handleCompactCtmFace(5, block, x, y, z, icon, ci);
angelica$handleCompactCtmFace(icon, ci);
}
@Inject(method = "renderStandardBlock(Lnet/minecraft/block/Block;III)Z", at = @At("RETURN"))
@@ -0,0 +1,38 @@
package com.gtnewhorizons.angelica.mixins.early.rendering;
import com.gtnewhorizons.angelica.render.EmissiveTextureHelper;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.world.IBlockAccess;
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.CallbackInfoReturnable;
@Mixin(RenderBlocks.class)
public class MixinRenderBlocksEmissive {
@Inject(method = "renderStandardBlock", at = @At("TAIL"))
private void angelica$renderEmissiveOverlay(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir) {
RenderBlocks self = (RenderBlocks)(Object)this;
IBlockAccess world = self.blockAccess;
if (world == null) return;
int meta = world.getBlockMetadata(x, y, z);
boolean hasEmissive = false;
for (int side = 0; side < 6; side++) {
if (EmissiveTextureHelper.getEmissiveIcon(block, side, meta) != null) {
hasEmissive = true;
break;
}
}
if (!hasEmissive) return;
Tessellator tess = Tessellator.instance;
tess.setBrightness(0xF000F0);
EmissiveTextureHelper.renderEmissiveBlockOverlay(self, block, x, y, z, meta);
}
}
@@ -28,7 +28,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* Ported with additional fixes from the ShaderFixer mod
* @author kotmatross28729
*/
@Mixin(value = SkyProviderCelestial.class, priority = 100)
@Mixin(value = SkyProviderCelestial.class, priority = 100, remap = false)
public class MixinSkyProviderCelestial {
/**
@@ -38,7 +38,9 @@ public class MixinSkyProviderCelestial {
"renderSunset",
"renderCelestials",
"renderAtmosphereGlow",
}, at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
}
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$common$redirectTex2D(int cap) {
return this.iris$common$redirectTexture(cap);
}
@@ -46,9 +48,11 @@ public class MixinSkyProviderCelestial {
/**
* Avoid program rebinding due to pipeline.setInputs | Doesn't affect texture disabling before glSkyList
*/
@WrapWithCondition(method = "render",
slice = @Slice(from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V", ordinal = 1)),
at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
@WrapWithCondition(method = "render"
, slice = @Slice(from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"
, ordinal = 1))
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$common$redirectTex2DException(int cap) {
return this.iris$common$redirectTexture(cap);
}
@@ -65,66 +69,98 @@ public class MixinSkyProviderCelestial {
/**
* Force skybox to render with the default program | Fix various issues with shaders enabled
*/
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"), remap = false)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"
, remap = true))
public void iris$main$renderInDefaultProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
main$previousProgram.set(GLStateManager.getActiveProgram());
GLStateManager.glUseProgram(0);
}
@WrapOperation(method = "render",
at = @At(value = "INVOKE", target = "Lcom/hbm/dim/SkyProviderCelestial;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"), remap = false)
@WrapOperation(method = "render"
, at = @At(value = "INVOKE"
, target = "Lcom/hbm/dim/SkyProviderCelestial;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"))
private void iris$main$renderSunInShaderProgram(SkyProviderCelestial instance, float partialTicks, WorldClient world, Minecraft mc, CelestialBody sun, double sunSize, double coronaSize, float visibility, float pressure, Operation<Void> original, @Share("main$previousProgram") LocalIntRef main$previousProgram){
GLStateManager.glUseProgram(main$previousProgram.get());
original.call(instance, partialTicks, world, mc, sun, sunSize, coronaSize, visibility, pressure);
GLStateManager.glUseProgram(0);
}
@Inject(method = "render", at = @At(value = "TAIL"), remap = false)
@Inject(method = "render"
, at = @At(value = "TAIL"))
public void iris$main$restorePreviousProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
GLStateManager.glUseProgram(main$previousProgram.get());
}
@Inject(method = "renderSun", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"),
slice = @Slice(
from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V", ordinal = 0),
to = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V", ordinal = 1)
@Inject(method = "renderSun"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"
, remap = true)
, slice = @Slice(
from = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V"
, ordinal = 0),
to = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V"
, ordinal = 1)
)
)
private void iris$sun$disableBlanking(CallbackInfo ci) {
Tessellator.instance.vertexCount = 0;
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"),
slice = @Slice(
from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 1),
to = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 2)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"
, remap = true)
, slice = @Slice(
from = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 1),
to = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 2)
)
)
private void iris$main$disableVoid(CallbackInfo ci) {
Tessellator.instance.vertexCount = 0;
}
@WrapWithCondition(method = "render", at = {
@At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 1),
@At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 2)
}, remap = false)
@WrapWithCondition(method = "render"
, at = {
@At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 1),
@At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
, ordinal = 2)
})
private boolean angelica$renderHorizon(int i) {
return (boolean) Settings.HORIZON.option.getStore();
}
@Inject(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/Tessellator;instance:Lnet/minecraft/client/renderer/Tessellator;", opcode = Opcodes.GETSTATIC))
@Inject(method = "render"
, at = @At(value = "FIELD"
, target = "Lnet/minecraft/client/renderer/Tessellator;instance:Lnet/minecraft/client/renderer/Tessellator;"
, remap = true
, opcode = Opcodes.GETSTATIC))
private void iris$renderSky$beginNormalSky(CallbackInfo ci) {
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setPhase(WorldRenderingPhase.SKY));
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glRotatef(FFFF)V", ordinal = 2), remap = false)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glRotatef(FFFF)V"
, ordinal = 2))
private void iris$renderSky$tiltSun(CallbackInfo ci) {
Iris.getPipelineManager().getPipeline().ifPresent(
p -> GLStateManager.glRotatef(p.getSunPathRotation(), 0.0F, 0.0F, 1.0F)
);
}
@Inject(method = "renderSun", at = @At(value = "HEAD"), remap = false)
@Inject(method = "renderSun"
, at = @At(value = "HEAD"))
private void iris$setSunRenderStage(CallbackInfo ci) {
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setPhase(WorldRenderingPhase.SUN));
}
@@ -8,13 +8,15 @@ import org.lwjgl.opengl.GL11;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(value = SkyProviderLaytheSunset.class, priority = 100)
@Mixin(value = SkyProviderLaytheSunset.class, priority = 100, remap = false)
public class MixinSkyProviderLaytheSunset {
/**
* Avoid program rebinding due to pipeline.setInputs
*/
@WrapWithCondition(method = "renderSunset", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
@WrapWithCondition(method = "renderSunset"
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$sunset$redirectTex2D(int cap) {
if(cap == GL11.GL_TEXTURE_2D) {
Minecraft.getMinecraft().renderEngine.bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
@@ -17,13 +17,15 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = SkyProviderOrbit.class, priority = 100)
@Mixin(value = SkyProviderOrbit.class, priority = 100, remap = false)
public class MixinSkyProviderOrbit {
/**
* Avoid program rebinding due to pipeline.setInputs
*/
@WrapWithCondition(method = "render", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
@WrapWithCondition(method = "render"
, at = @At(value = "INVOKE"
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
private boolean iris$common$redirectTex2D(int cap) {
if (cap == GL11.GL_TEXTURE_2D) {
Minecraft.getMinecraft().renderEngine.bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
@@ -35,24 +37,26 @@ public class MixinSkyProviderOrbit {
/**
* Force skybox to render with the default program | Fix various issues with shaders enabled
*/
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"), remap = false)
@Inject(method = "render"
, at = @At(value = "INVOKE"
, target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"
, remap = true))
public void iris$main$renderInDefaultProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
main$previousProgram.set(GLStateManager.getActiveProgram());
GLStateManager.glUseProgram(0);
}
@WrapOperation(method = "render",
at = @At(value = "INVOKE", target = "Lcom/hbm/dim/orbit/SkyProviderOrbit;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"), remap = false)
@WrapOperation(method = "render"
, at = @At(value = "INVOKE"
, target = "Lcom/hbm/dim/orbit/SkyProviderOrbit;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"))
private void iris$main$renderSunInShaderProgram(SkyProviderOrbit instance, float partialTicks, WorldClient world, Minecraft mc, CelestialBody sun, double sunSize, double coronaSize, float visibility, float pressure, Operation<Void> original, @Share("main$previousProgram") LocalIntRef main$previousProgram){
GLStateManager.glUseProgram(main$previousProgram.get());
original.call(instance, partialTicks, world, mc, sun, sunSize, coronaSize, visibility, pressure);
GLStateManager.glUseProgram(0);
}
/**
* Sets the program back before {@link #iris$main$renderInDefaultProgram(CallbackInfo, LocalIntRef)} call
*/
@Inject(method = "render", at = @At(value = "TAIL"), remap = false)
@Inject(method = "render"
, at = @At(value = "TAIL"))
public void iris$main$restorePreviousProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
GLStateManager.glUseProgram(main$previousProgram.get());
}