Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c295a1e22 | ||
|
|
9fd02900ef | ||
|
|
27e9a4d4fd | ||
|
|
dfc08053d5 | ||
|
|
1af03fa369 | ||
|
|
3ae3b2527d | ||
|
|
fd2a14a000 | ||
|
|
9f0f6a6232 | ||
|
|
2942c7eb84 | ||
|
|
47762f921e | ||
|
|
bdc4431760 | ||
|
|
7e1beadfb5 | ||
|
|
59a4b84023 | ||
|
|
8d53e25e0a | ||
|
|
bbf83db379 | ||
|
|
ec82280b9b | ||
|
|
d7588e4c14 | ||
|
|
54c99386d4 | ||
|
|
465abf889a | ||
|
|
4acb83be6a | ||
|
|
80f9724ee6 | ||
|
|
8cf55bb216 | ||
|
|
3ebc8868c7 | ||
|
|
0938e6d975 | ||
|
|
d3022a40d2 | ||
|
|
2645c803f9 | ||
|
|
d91c356a99 | ||
|
|
189ff4a80f | ||
|
|
ffb4ca9472 | ||
|
|
7d8c8d306d | ||
|
|
117ff0cacf | ||
|
|
83fdd57534 | ||
|
|
951be04e9a | ||
|
|
0ad69f3433 | ||
|
|
0dabecc784 | ||
|
|
9b3fad4c6d | ||
|
|
e1e8256861 | ||
|
|
e65c01c2a3 | ||
|
|
b9de4df18e | ||
|
|
a5d31cd1b6 | ||
|
|
29f8d718cc | ||
|
|
52b0db2566 | ||
|
|
e9eb45f4f2 | ||
|
|
92e28a4cd5 | ||
|
|
f46a86889c | ||
|
|
c827d446d5 | ||
|
|
00ef355ce8 | ||
|
|
1282056fff | ||
|
|
6c422d47a2 |
No files matched your search
@@ -3,7 +3,7 @@
|
||||
/.idea/
|
||||
/.vs/
|
||||
/.vscode/
|
||||
/run/
|
||||
run/
|
||||
build/
|
||||
/eclipse/
|
||||
.classpath
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
# 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
|
||||
```
|
||||
|
||||
---
|
||||
@@ -83,7 +83,7 @@ dependencies {
|
||||
shadowImplementation("net.minecraftforge:eventbus:7.0-beta.15")
|
||||
|
||||
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
|
||||
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-26.g5ee7d96-GTNH") {
|
||||
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-32.g7dd88a4-GTNH") {
|
||||
exclude module: "antlr4" // we only want to shadow the runtime module
|
||||
}
|
||||
shadowImplementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
@@ -97,6 +97,8 @@ dependencies {
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.35-GTNH:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
|
||||
|
||||
//devOnlyNonPublishable('com.github.GTNewHorizons:worldedit-gtnh:v0.0.9')
|
||||
|
||||
// Notfine Deps
|
||||
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.2.2-GTNH:dev")
|
||||
@@ -119,7 +121,7 @@ dependencies {
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.10:dev")
|
||||
|
||||
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5593_H261"))
|
||||
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5634_H261"))
|
||||
|
||||
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
|
||||
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
|
||||
|
||||
@@ -76,7 +76,7 @@ dependencies {
|
||||
compileOnly("org.lwjgl.lwjgl:lwjgl:${property("lwjglVersion")}")
|
||||
compileOnly("org.lwjgl.lwjgl:lwjgl_util:${property("lwjglVersion")}")
|
||||
|
||||
compileOnly("org.ow2.asm:asm-tree:9.7")
|
||||
compileOnly("org.ow2.asm:asm-tree:5.0.3")
|
||||
|
||||
// Our Deps
|
||||
api("com.github.GTNewHorizons:GTNHLib:${property("gtnhlibVersion")}:dev")
|
||||
@@ -100,6 +100,7 @@ dependencies {
|
||||
testImplementation(sourceSets["stubs"].output)
|
||||
testImplementation(platform("org.junit:junit-bom:${property("junitBomVersion")}"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testImplementation("org.ow2.asm:asm-tree:9.7")
|
||||
testImplementation("org.lwjgl.lwjgl:lwjgl:${property("lwjglVersion")}")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testRuntimeOnly("org.apache.logging.log4j:log4j-core:${property("log4jVersion")}")
|
||||
|
||||
@@ -345,6 +345,27 @@ public class FeedbackManager {
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_QUAD_STRIP:
|
||||
for (int i = 0; i + 3 < count; i += 2) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(4);
|
||||
emitVertex(reader.read(i), stride);
|
||||
emitVertex(reader.read(i + 1), stride);
|
||||
emitVertex(reader.read(i + 3), stride);
|
||||
emitVertex(reader.read(i + 2), stride);
|
||||
}
|
||||
break;
|
||||
|
||||
case GL11.GL_POLYGON:
|
||||
if (count >= 3) {
|
||||
feedbackToken(GL11.GL_POLYGON_TOKEN);
|
||||
feedbackToken(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
emitVertex(reader.read(i), stride);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("Feedback mode: unsupported primitive mode 0x" + Integer.toHexString(mode));
|
||||
}
|
||||
|
||||
@@ -419,6 +419,9 @@ public class GLStateManager {
|
||||
@Getter protected static int boundVBO;
|
||||
@Getter protected static int boundEBO;
|
||||
@Getter protected static int boundVAO;
|
||||
private static int clientArraysVBO = 0;
|
||||
private static int clientArraysVBOCapacity = 0;
|
||||
private static final int[] clientArraysVBOOffsets = new int[VertexAttribState.MAX_ATTRIBS];
|
||||
private static int boundPixelUnpackBuffer;
|
||||
private static int boundPixelPackBuffer;
|
||||
private static final Int2IntOpenHashMap vaoEboMap = new Int2IntOpenHashMap();
|
||||
@@ -946,10 +949,11 @@ public class GLStateManager {
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_COLOR_WRITEMASK -> {
|
||||
params.put((byte) (colorMask.red ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.green ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.blue ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.alpha ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
final int pos = params.position();
|
||||
params.put(pos, (byte) (colorMask.red ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 1, (byte) (colorMask.green ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 2, (byte) (colorMask.blue ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 3, (byte) (colorMask.alpha ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
}
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
@@ -1060,7 +1064,7 @@ public class GLStateManager {
|
||||
case GL11.GL_DIFFUSE -> state.diffuse.get(0, params);
|
||||
case GL11.GL_SPECULAR -> state.specular.get(0, params);
|
||||
case GL11.GL_EMISSION -> state.emission.get(0, params);
|
||||
case GL11.GL_SHININESS -> params.put(state.shininess);
|
||||
case GL11.GL_SHININESS -> params.put(params.position(), state.shininess);
|
||||
case GL11.GL_COLOR_INDEXES -> state.colorIndexes.get(0, params);
|
||||
default -> {
|
||||
}
|
||||
@@ -1075,11 +1079,11 @@ public class GLStateManager {
|
||||
case GL11.GL_SPECULAR -> state.specular.get(0, params);
|
||||
case GL11.GL_POSITION -> state.position.get(0, params);
|
||||
case GL11.GL_SPOT_DIRECTION -> state.spotDirection.get(0, params);
|
||||
case GL11.GL_SPOT_EXPONENT -> params.put(state.spotExponent);
|
||||
case GL11.GL_SPOT_CUTOFF -> params.put(state.spotCutoff);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> params.put(state.constantAttenuation);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> params.put(state.linearAttenuation);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> params.put(state.quadraticAttenuation);
|
||||
case GL11.GL_SPOT_EXPONENT -> params.put(params.position(), state.spotExponent);
|
||||
case GL11.GL_SPOT_CUTOFF -> params.put(params.position(), state.spotCutoff);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> params.put(params.position(), state.constantAttenuation);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> params.put(params.position(), state.linearAttenuation);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> params.put(params.position(), state.quadraticAttenuation);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
@@ -1098,21 +1102,39 @@ public class GLStateManager {
|
||||
case GL11.GL_COLOR_CLEAR_VALUE -> clearColor.get(params);
|
||||
case GL11.GL_CURRENT_COLOR -> color.get(params);
|
||||
case GL11.GL_DEPTH_RANGE -> {
|
||||
params.put((float) viewportState.depthRangeNear);
|
||||
params.put((float) viewportState.depthRangeFar);
|
||||
final int pos = params.position();
|
||||
params.put(pos, (float) viewportState.depthRangeNear);
|
||||
params.put(pos + 1, (float) viewportState.depthRangeFar);
|
||||
}
|
||||
case GL14.GL_BLEND_COLOR -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, blendState.getBlendColorR());
|
||||
params.put(pos + 1, blendState.getBlendColorG());
|
||||
params.put(pos + 2, blendState.getBlendColorB());
|
||||
params.put(pos + 3, blendState.getBlendColorA());
|
||||
}
|
||||
case GL14.GL_BLEND_COLOR -> params.put(blendState.getBlendColorR()).put(blendState.getBlendColorG()).put(blendState.getBlendColorB()).put(blendState.getBlendColorA());
|
||||
case GL11.GL_CURRENT_NORMAL -> {
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
params.put(normal.x).put(normal.y).put(normal.z);
|
||||
final int pos = params.position();
|
||||
params.put(pos, normal.x);
|
||||
params.put(pos + 1, normal.y);
|
||||
params.put(pos + 2, normal.z);
|
||||
}
|
||||
case GL11.GL_CURRENT_TEXTURE_COORDS -> {
|
||||
final Vector4f tc = ShaderManager.getCurrentTexCoord();
|
||||
params.put(tc.x).put(tc.y).put(tc.z).put(tc.w);
|
||||
final int pos = params.position();
|
||||
params.put(pos, tc.x);
|
||||
params.put(pos + 1, tc.y);
|
||||
params.put(pos + 2, tc.z);
|
||||
params.put(pos + 3, tc.w);
|
||||
}
|
||||
case GL11.GL_FOG_COLOR -> {
|
||||
final FloatBuffer fogBuf = fogState.getFogColorBuffer();
|
||||
params.put(fogBuf.get(0)).put(fogBuf.get(1)).put(fogBuf.get(2)).put(fogBuf.get(3));
|
||||
final int pos = params.position();
|
||||
params.put(pos, fogBuf.get(0));
|
||||
params.put(pos + 1, fogBuf.get(1));
|
||||
params.put(pos + 2, fogBuf.get(2));
|
||||
params.put(pos + 3, fogBuf.get(3));
|
||||
}
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> lightModel.ambient.get(0, params);
|
||||
default -> {
|
||||
@@ -2145,6 +2167,7 @@ public class GLStateManager {
|
||||
}
|
||||
prepareWideLineEmulation(mode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
@@ -2158,6 +2181,7 @@ public class GLStateManager {
|
||||
}
|
||||
prepareWideLineEmulation(mode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
} else {
|
||||
@@ -2175,6 +2199,7 @@ public class GLStateManager {
|
||||
}
|
||||
prepareWideLineEmulation(mode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
} else {
|
||||
@@ -2192,6 +2217,7 @@ public class GLStateManager {
|
||||
}
|
||||
prepareWideLineEmulation(mode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
|
||||
} else {
|
||||
@@ -2223,6 +2249,7 @@ public class GLStateManager {
|
||||
}
|
||||
prepareWideLineEmulation(mode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
} else {
|
||||
@@ -2245,20 +2272,72 @@ public class GLStateManager {
|
||||
|
||||
public static void glMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
RENDER_BACKEND.multiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
|
||||
}
|
||||
|
||||
public static void glMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
RENDER_BACKEND.multiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
|
||||
}
|
||||
|
||||
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
|
||||
}
|
||||
|
||||
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) { RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount); }
|
||||
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount);
|
||||
}
|
||||
|
||||
private static void prepareClientArrays() {
|
||||
if (ShaderManager.getInstance().isEnabled() && VertexAttribState.hasAnyClientSideEnabledAttrib()) {
|
||||
uploadClientArraysToVBO();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If any enabled vertex attribute uses a client-side pointer (no VBO), upload all such
|
||||
* attribs into a shared stream VBO so the draw succeeds under core profile.
|
||||
*/
|
||||
private static void uploadClientArraysToVBO() {
|
||||
int totalBytes = 0;
|
||||
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
|
||||
clientArraysVBOOffsets[i] = -1;
|
||||
final VertexAttribState.Attrib a = VertexAttribState.get(i);
|
||||
if (!a.enabled || a.clientPointer == null) continue;
|
||||
clientArraysVBOOffsets[i] = totalBytes;
|
||||
totalBytes += a.clientPointer.remaining();
|
||||
}
|
||||
if (totalBytes == 0) return;
|
||||
|
||||
if (clientArraysVBO == 0) {
|
||||
clientArraysVBO = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
final int savedVBO = boundVBO;
|
||||
glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
|
||||
|
||||
if (totalBytes > clientArraysVBOCapacity) {
|
||||
int newCap = Math.max(4096, clientArraysVBOCapacity);
|
||||
while (newCap < totalBytes) newCap *= 2;
|
||||
clientArraysVBOCapacity = newCap;
|
||||
}
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, clientArraysVBOCapacity, GL15.GL_STREAM_DRAW);
|
||||
|
||||
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
|
||||
if (clientArraysVBOOffsets[i] < 0) continue;
|
||||
final VertexAttribState.Attrib a = VertexAttribState.get(i);
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
|
||||
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
|
||||
}
|
||||
|
||||
glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
|
||||
}
|
||||
|
||||
public static void glDrawArrays(int mode, int first, int count) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
@@ -2278,8 +2357,13 @@ public class GLStateManager {
|
||||
}
|
||||
prepareWideLineEmulation(mode);
|
||||
ShaderManager.getInstance().preDraw();
|
||||
prepareClientArrays();
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(first, count);
|
||||
} else if (mode == GL11.GL_QUAD_STRIP) {
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
|
||||
} else if (mode == GL11.GL_POLYGON) {
|
||||
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
|
||||
} else {
|
||||
RENDER_BACKEND.drawArrays(mode, first, count);
|
||||
}
|
||||
@@ -2298,6 +2382,10 @@ public class GLStateManager {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glVertexPointer(int size, int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer);
|
||||
}
|
||||
@@ -2339,6 +2427,10 @@ public class GLStateManager {
|
||||
glNormalPointer(GL11.GL_INT, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int stride, DoubleBuffer pointer) {
|
||||
glNormalPointer(GL11.GL_DOUBLE, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, ByteBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer);
|
||||
}
|
||||
@@ -2355,6 +2447,10 @@ public class GLStateManager {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, DoubleBuffer pointer) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glNormalPointer(int type, int stride, long pointer_buffer_offset) {
|
||||
glVertexAttribPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset);
|
||||
}
|
||||
@@ -2377,6 +2473,10 @@ public class GLStateManager {
|
||||
glTexCoordPointer(size, GL11.GL_SHORT, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int stride, DoubleBuffer pointer) {
|
||||
glTexCoordPointer(size, GL11.GL_DOUBLE, stride, pointer);
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, FloatBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
@@ -2395,6 +2495,12 @@ public class GLStateManager {
|
||||
glVertexAttribPointer(loc, size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, DoubleBuffer pointer) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
glVertexAttribPointer(loc, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer));
|
||||
}
|
||||
|
||||
public static void glTexCoordPointer(int size, int type, int stride, long pointer_buffer_offset) {
|
||||
final int loc = texCoordAttributeLocation();
|
||||
if (loc < 0) return;
|
||||
@@ -2441,32 +2547,23 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, FloatBuffer pointer) {
|
||||
final ByteBuffer bb = MemoryUtilities.memByteBuffer(pointer);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, bb, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, bb);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, DoubleBuffer pointer) {
|
||||
final ByteBuffer bb = MemoryUtilities.memByteBuffer(pointer);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, bb, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, bb);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, IntBuffer pointer) {
|
||||
final ByteBuffer bb = MemoryUtilities.memByteBuffer(pointer);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, bb, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, bb);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ShortBuffer pointer) {
|
||||
final ByteBuffer bb = MemoryUtilities.memByteBuffer(pointer);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, bb, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, bb);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
|
||||
VertexAttribState.set(index, size, type, normalized, stride, pointer, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, pointer);
|
||||
VertexAttribState.set(index, size, type, normalized, stride, pointer, 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, long offset) {
|
||||
@@ -2475,20 +2572,15 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ByteBuffer pointer) {
|
||||
VertexAttribState.set(index, size, type, false, stride, pointer, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, pointer);
|
||||
VertexAttribState.set(index, size, type, false, stride, pointer, 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, IntBuffer pointer) {
|
||||
final ByteBuffer bb = MemoryUtilities.memByteBuffer(pointer);
|
||||
VertexAttribState.set(index, size, type, false, stride, bb, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, bb);
|
||||
VertexAttribState.set(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer), 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ShortBuffer pointer) {
|
||||
final ByteBuffer bb = MemoryUtilities.memByteBuffer(pointer);
|
||||
VertexAttribState.set(index, size, type, false, stride, bb, boundVBO);
|
||||
RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, bb);
|
||||
VertexAttribState.set(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer), 0);
|
||||
}
|
||||
|
||||
public static void glVertexAttribDivisor(int index, int divisor) { RENDER_BACKEND.vertexAttribDivisor(index, divisor); }
|
||||
@@ -4297,9 +4389,20 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.finish();
|
||||
}
|
||||
|
||||
public static int glGetError() { return RENDER_BACKEND.getError(); }
|
||||
public static String glGetString(int pname) { return RENDER_BACKEND.getString(pname); }
|
||||
public static String glGetStringi(int name, int index) { return RENDER_BACKEND.getStringi(name, index); }
|
||||
public static int glGetError() {
|
||||
if (!RENDER_BACKEND.hasContext()) return 0;
|
||||
return RENDER_BACKEND.getError();
|
||||
}
|
||||
|
||||
public static String glGetString(int pname) {
|
||||
if (!RENDER_BACKEND.hasContext()) return "no valid GL/render context";
|
||||
return RENDER_BACKEND.getString(pname);
|
||||
}
|
||||
|
||||
public static String glGetStringi(int name, int index) {
|
||||
if (!RENDER_BACKEND.hasContext()) return "no valid GL/render context";
|
||||
return RENDER_BACKEND.getStringi(name, index);
|
||||
}
|
||||
|
||||
public static void glShaderSource(int shader, CharSequence source) {
|
||||
String src = source.toString();
|
||||
@@ -5814,5 +5917,7 @@ public class GLStateManager {
|
||||
public static void glGetUniform(int program, int location, IntBuffer params) {
|
||||
RENDER_BACKEND.getUniformiv(program, location, params);
|
||||
}
|
||||
|
||||
public static void glGetTexImage(int target, int level, int format, int type, long pixels) {
|
||||
GL11.glGetTexImage(target, level, format, type, pixels);
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,12 @@ public class GlslTransformUtils {
|
||||
/** Reserved words added in later GLSL versions that may appear as identifiers in older shaders. */
|
||||
private record ReservedWordRename(Pattern pattern, String replacement) {}
|
||||
private static final Map<Integer, List<ReservedWordRename>> VERSIONED_RESERVED_WORDS = Map.of(
|
||||
400, List.of(
|
||||
// Some reserved words to always rename
|
||||
0, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample"),
|
||||
new ReservedWordRename(Pattern.compile("\\bnew\\b"), RENAMED_PREFIX + "new")
|
||||
),
|
||||
400, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsampler\\b(?!\\d)"), RENAMED_PREFIX + "sampler")
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
/**
|
||||
* Tessellator data needed by {@link com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer}.
|
||||
*/
|
||||
public interface ITessellatorData {
|
||||
|
||||
boolean isDrawing();
|
||||
void setDrawing(boolean drawing);
|
||||
int getVertexCount();
|
||||
int[] getRawBuffer();
|
||||
int getRawBufferIndex();
|
||||
int getRawBufferSize();
|
||||
void setRawBufferSize(int size);
|
||||
void setRawBuffer(int[] buffer);
|
||||
int getDrawMode();
|
||||
boolean hasTexture();
|
||||
boolean hasColor();
|
||||
boolean hasNormals();
|
||||
boolean hasBrightness();
|
||||
void angelica$reset();
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.lwjgl.opengl.ARBDebugOutputCallback;
|
||||
import org.lwjgl.opengl.ARBDirectStateAccess;
|
||||
import org.lwjgl.opengl.ContextCapabilities;
|
||||
import org.lwjgl.opengl.EXTDirectStateAccess;
|
||||
import org.lwjgl.opengl.GLContext;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
@@ -67,7 +68,7 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
caps = org.lwjgl.opengl.GLContext.getCapabilities();
|
||||
caps = GLContext.getCapabilities();
|
||||
activeDebugExtension = 0;
|
||||
}
|
||||
|
||||
@@ -86,6 +87,15 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return "OpenGL (LWJGL2)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContext() {
|
||||
try {
|
||||
return GLContext.getCapabilities() != null;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinGLSLVersion() {return 330;}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ public abstract class RenderBackend {
|
||||
public abstract boolean isAvailable();
|
||||
public abstract String getName();
|
||||
|
||||
/** Returns true if the current thread has a valid render context. Safe to call from any thread. */
|
||||
public abstract boolean hasContext();
|
||||
|
||||
/** Higher priority backends are preferred. */
|
||||
public int getPriority() { return 0; }
|
||||
|
||||
|
||||
@@ -273,21 +273,27 @@ public final class FragmentShaderGenerator {
|
||||
sb.append(" // Alpha test\n");
|
||||
final int func = FragmentKey.decodeAlphaFunc(key.alphaTestFunc());
|
||||
|
||||
|
||||
sb.append(" float qa = round(color.a * 255.0);\n");
|
||||
switch (func) {
|
||||
case GL11.GL_NEVER ->
|
||||
sb.append(" discard;\n");
|
||||
case GL11.GL_LESS ->
|
||||
sb.append(" if (color.a >= u_AlphaRef) discard;\n");
|
||||
case GL11.GL_EQUAL ->
|
||||
sb.append(" if (color.a != u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa >= u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_EQUAL -> {
|
||||
sb.append(" float qr = round(u_AlphaRef * 255.0);\n");
|
||||
sb.append(" if (qa != qr) discard;\n");
|
||||
}
|
||||
case GL11.GL_LEQUAL ->
|
||||
sb.append(" if (color.a > u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa > u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_GREATER ->
|
||||
sb.append(" if (color.a <= u_AlphaRef) discard;\n");
|
||||
case GL11.GL_NOTEQUAL ->
|
||||
sb.append(" if (color.a == u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa <= u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_NOTEQUAL -> {
|
||||
sb.append(" float qr = round(u_AlphaRef * 255.0);\n");
|
||||
sb.append(" if (qa == qr) discard;\n");
|
||||
}
|
||||
case GL11.GL_GEQUAL ->
|
||||
sb.append(" if (color.a < u_AlphaRef) discard;\n");
|
||||
sb.append(" if (qa < u_AlphaRef * 255.0) discard;\n");
|
||||
case GL11.GL_ALWAYS -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Verifies required classes/resources are present on the classpath at load time.
|
||||
* Fails fast with a descriptive error message if a dependency is missing.
|
||||
*/
|
||||
public final class DependencyVerifier {
|
||||
|
||||
/**
|
||||
* A single dependency check: a classpath resource that must exist.
|
||||
* @param resourcePath Resource path to check (e.g. "/com/example/SomeClass.class")
|
||||
* @param errorMessage Human-readable error message if missing (include download URL)
|
||||
*/
|
||||
public record Check(String resourcePath, String errorMessage) {}
|
||||
|
||||
/**
|
||||
* Verify all checks pass. Throws RuntimeException on first failure.
|
||||
* @param anchor Class whose classloader to use for resource lookups
|
||||
* @param checks Dependency checks to run
|
||||
*/
|
||||
public static void verify(Class<?> anchor, List<Check> checks) {
|
||||
for (Check check : checks) {
|
||||
if (anchor.getResource(check.resourcePath()) == null) {
|
||||
throw new RuntimeException(check.errorMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DependencyVerifier() {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class EarlyRedirectorCore {
|
||||
|
||||
private final GLSMRedirector core;
|
||||
private final String[] targets;
|
||||
private final String[] exclusions;
|
||||
|
||||
public EarlyRedirectorCore(String... extraExclusions) {
|
||||
this.core = new GLSMRedirector();
|
||||
this.targets = EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS.clone();
|
||||
|
||||
final List<String> excl = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
|
||||
if (extraExclusions != null && extraExclusions.length > 0) {
|
||||
Collections.addAll(excl, extraExclusions);
|
||||
}
|
||||
this.exclusions = excl.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public byte[] transform(String transformedName, byte[] basicClass) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
if (!startsWithAny(transformedName, targets)) return basicClass;
|
||||
if (startsWithAny(transformedName, exclusions)) return basicClass;
|
||||
if (!core.shouldTransform(basicClass)) return basicClass;
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
if (core.transformClassNode(transformedName, cn)) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
private static boolean startsWithAny(String name, String[] prefixes) {
|
||||
for (String p : prefixes) {
|
||||
if (name.startsWith(p)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.loading.TransformerNarrower.NarrowRule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class EcosystemNarrowRules {
|
||||
|
||||
public static final List<NarrowRule> ALL = List.of(
|
||||
new NarrowRule("DragonAPI", "Reika.DragonAPI.ASM", List.of(
|
||||
"Reika.DragonAPI.ASM.Patchers",
|
||||
"Reika.DragonAPI.ASM.Profiling",
|
||||
"Reika.DragonAPI.ASM.APIStripper",
|
||||
"Reika.DragonAPI.ASM.ClassReparenter",
|
||||
"Reika.DragonAPI.ASM.DependentMethodStripper",
|
||||
"Reika.DragonAPI.ASM.DragonAPIClassTransformer",
|
||||
"Reika.DragonAPI.ASM.FMLItemBlockPatch",
|
||||
"Reika.DragonAPI.ASM.FluidNamePatch",
|
||||
"Reika.DragonAPI.ASM.InterfaceInjector",
|
||||
"Reika.DragonAPI.ASM.SpecialBiomePlacement",
|
||||
"Reika.DragonAPI.ASM.StructureLootHooks"
|
||||
)),
|
||||
|
||||
new NarrowRule("Xaeros_common", "xaero.common.core", List.of(
|
||||
"xaero.common.core.transformer"
|
||||
)),
|
||||
|
||||
new NarrowRule("Xaeros_map", "xaero.map.core", List.of(
|
||||
"xaero.map.core.transformer"
|
||||
)),
|
||||
|
||||
new NarrowRule("AdvancedLightsabers", "com.fiskmods.lightsabers.asm", List.of(
|
||||
"com.fiskmods.lightsabers.asm.ALLoadingPlugin",
|
||||
"com.fiskmods.lightsabers.asm.ASMHooks",
|
||||
"com.fiskmods.lightsabers.asm.transformers"
|
||||
)),
|
||||
|
||||
new NarrowRule("Ears", "com.unascribed.ears", List.of(
|
||||
"com.unascribed.ears.asm",
|
||||
"com.unascribed.ears.common.agent"
|
||||
)),
|
||||
|
||||
new NarrowRule("Alfheim", "alfheim.common.core.asm.hook", List.of(
|
||||
"alfheim.common.core.asm.hook.AlfheimFieldHookHandler",
|
||||
"alfheim.common.core.asm.hook.AlfheimHPHooks",
|
||||
"alfheim.common.core.asm.hook.Botania18AndUpBackport",
|
||||
"alfheim.common.core.asm.hook.ElementalDamageAdapter",
|
||||
"alfheim.common.core.asm.hook.integration",
|
||||
"alfheim.common.core.asm.hook.extender.FurnaceExtender",
|
||||
"alfheim.common.core.asm.hook.extender.ItemAuraRingExtender",
|
||||
"alfheim.common.core.asm.hook.extender.ItemLensExtender",
|
||||
"alfheim.common.core.asm.hook.extender.ItemTwigWandExtender",
|
||||
"alfheim.common.core.asm.hook.extender.LightRelayExtender",
|
||||
"alfheim.common.core.asm.hook.extender.PureDaisyExtender",
|
||||
"alfheim.common.core.asm.hook.extender.QuartzExtender",
|
||||
"alfheim.common.core.asm.hook.extender.RelicHooks",
|
||||
"alfheim.common.core.asm.hook.extender.SparkExtender",
|
||||
"alfheim.common.core.asm.hook.fixes.BotaniaGlowingRenderFixes",
|
||||
"alfheim.common.core.asm.hook.fixes.CorporeaInputFix",
|
||||
"alfheim.common.core.asm.hook.fixes.GodAttributesHooks",
|
||||
"alfheim.common.core.asm.hook.fixes.RecipeAncientWillsFix"
|
||||
))
|
||||
);
|
||||
|
||||
public static final String[] LWJGL3IFY_EXCLUSIONS_SHARED = {
|
||||
"org.embeddedt.embeddium",
|
||||
"org.taumc.celeritas",
|
||||
"com.mitchej123.lwjgl.lwjgl3",
|
||||
"com.mitchej123.glsm",
|
||||
"com.gtnewhorizons.angelica.lwjgl3",
|
||||
};
|
||||
|
||||
public static final String[] EARLY_REDIRECTOR_TARGETS = {
|
||||
"cn.tesseract.mycelium.",
|
||||
};
|
||||
|
||||
private EcosystemNarrowRules() {}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Narrows overly-broad transformer exclusions that prevent the GLSM redirector from
|
||||
* transforming GL calls in mod code. Some coremods register exclusions covering their
|
||||
* entire package tree, which inadvertently shields rendering code from redirection.
|
||||
* <p>
|
||||
* This utility removes the broad exclusion and re-adds narrower ones that cover only
|
||||
* the coremod's own transformer/ASM classes, leaving rendering code exposed.
|
||||
* <p>
|
||||
* Operates on a {@code Set<String>} (the transformer exceptions set from LaunchClassLoader)
|
||||
* and a {@code Map<String, Object>} (the Launch blackboard). Callers extract these from
|
||||
* the Forge/launchwrapper infrastructure.
|
||||
*/
|
||||
public final class TransformerNarrower {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("GLSM");
|
||||
|
||||
/**
|
||||
* A rule that replaces a broad transformer exclusion with narrower ones.
|
||||
* @param name Human-readable name for logging (e.g. "DragonAPI")
|
||||
* @param broad The broad exclusion to remove (e.g. "Reika.DragonAPI.ASM")
|
||||
* @param narrow Narrower exclusions to add in its place
|
||||
*/
|
||||
public record NarrowRule(String name, String broad, List<String> narrow) {}
|
||||
|
||||
/**
|
||||
* Apply narrowing rules to the transformer exceptions set.
|
||||
*
|
||||
* @param exceptions The mutable Set from LaunchClassLoader.transformerExceptions
|
||||
* @param blackboard The Launch.blackboard map (checked for disable flags)
|
||||
* @param keyPrefix Blackboard key prefix for per-rule opt-out (e.g. "angelica" or "umbra").
|
||||
* Rule "DragonAPI" checks key "{prefix}.narrow.DragonAPI". If explicitly
|
||||
* set to Boolean.FALSE, the rule is skipped.
|
||||
* @param callerName Name for log messages (e.g. "Angelica" or "Umbra")
|
||||
* @param rules The narrowing rules to apply
|
||||
* @return number of rules that were applied
|
||||
*/
|
||||
public static int narrow(Set<String> exceptions, Map<String, Object> blackboard,
|
||||
String keyPrefix, String callerName, List<NarrowRule> rules) {
|
||||
int applied = 0;
|
||||
for (NarrowRule rule : rules) {
|
||||
final String key = keyPrefix + ".narrow." + rule.name();
|
||||
if (Boolean.FALSE.equals(blackboard.get(key))) {
|
||||
continue;
|
||||
}
|
||||
if (exceptions.remove(rule.broad())) {
|
||||
for (String narrow : rule.narrow()) {
|
||||
exceptions.add(narrow);
|
||||
}
|
||||
LOGGER.info("[{}] Narrowed {} transformer exclusion to allow GL redirection", callerName, rule.broad());
|
||||
applied++;
|
||||
}
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
private TransformerNarrower() {}
|
||||
}
|
||||
@@ -75,6 +75,12 @@ public final class DisplayListVBOBuilder {
|
||||
if (data.drawMode == GL11.GL_QUADS) {
|
||||
final IVertexArrayObject indexedVAO = new IndexedVAO(vbo, IndexBuffer.convertQuadsToTrigs(start, start + vertexCount));
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6);
|
||||
} else if (data.drawMode == GL11.GL_QUAD_STRIP) {
|
||||
// GL_QUAD_STRIP is removed in core profile; an even-length GL_TRIANGLE_STRIP produces the same quads
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_STRIP, start, vertexCount & ~1);
|
||||
} else if (data.drawMode == GL11.GL_POLYGON) {
|
||||
// GL_POLYGON is removed in core profile; convert to GL_TRIANGLE_FAN which is equivalent for convex polygons
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_FAN, start, vertexCount);
|
||||
} else {
|
||||
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, data.drawMode, start, vertexCount);
|
||||
}
|
||||
|
||||
@@ -4,16 +4,19 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.gtnhlib.asm.ClassConstantPoolParser;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.IntInsnNode;
|
||||
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TypeInsnNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -79,6 +82,9 @@ public class GLSMRedirector {
|
||||
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_SCISSOR_TEST, "ScissorTest");
|
||||
|
||||
final var gl11 = RedirectMap.newMap()
|
||||
// glEnable/Disable - Special cased in GLSMRedirector, but included here for the external API
|
||||
.add("glEnable")
|
||||
.add("glDisable")
|
||||
.add("glAlphaFunc")
|
||||
.add("glBegin")
|
||||
.add("glBindTexture")
|
||||
@@ -495,6 +501,22 @@ public class GLSMRedirector {
|
||||
return CORE_EXCLUSIONS.clone();
|
||||
}
|
||||
|
||||
/** The internal-name prefix used to match GL version classes (e.g., {@code "org/lwjgl/opengl/GL"}). */
|
||||
public static String getGLPrefix() { return GL_PREFIX; }
|
||||
|
||||
/** The internal name of the redirect target class. */
|
||||
public static String getTargetClassName() { return GLStateManager; }
|
||||
|
||||
/** Method redirects for GL-prefix classes. Key: original method name, Value: GLStateManager method name. */
|
||||
public static Map<String, String> getGLPrefixMethodRedirects() {
|
||||
return Collections.unmodifiableMap(glMethodRedirects);
|
||||
}
|
||||
|
||||
/** Method redirects for named (non-GL-prefix) classes. Key: internal class name, Value: per-method redirect map. */
|
||||
public static Map<String, Map<String, String>> getNamedClassMethodRedirects() {
|
||||
return Collections.unmodifiableMap(methodRedirects);
|
||||
}
|
||||
|
||||
public boolean shouldTransform(byte[] basicClass) {
|
||||
return cstPoolParser.find(basicClass, true);
|
||||
}
|
||||
@@ -504,7 +526,7 @@ public class GLSMRedirector {
|
||||
boolean changed = false;
|
||||
final boolean isOpenGlHelper = transformedName.equals("net.minecraft.client.renderer.OpenGlHelper");
|
||||
|
||||
for (MethodNode mn : cn.methods) {
|
||||
for (MethodNode mn : (List<MethodNode>) cn.methods) {
|
||||
if (isOpenGlHelper && (mn.name.equals("glBlendFunc") || mn.name.equals("func_148821_a"))) {
|
||||
continue;
|
||||
}
|
||||
@@ -590,6 +612,22 @@ public class GLSMRedirector {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
} else if (node instanceof InvokeDynamicInsnNode dynNode) {
|
||||
// Redirect method handles in invokedynamic bootstrap arguments
|
||||
for (int i = 0; i < dynNode.bsmArgs.length; i++) {
|
||||
if (!(dynNode.bsmArgs[i] instanceof Handle handle)) continue;
|
||||
final Map<String, String> redirects = handle.getOwner().startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(handle.getOwner());
|
||||
if (redirects == null) continue;
|
||||
final String glsmName = redirects.get(handle.getName());
|
||||
if (glsmName == null) continue;
|
||||
if (LOG_SPAM) {
|
||||
final String shortOwner = handle.getOwner().substring(handle.getOwner().lastIndexOf("/") + 1);
|
||||
LOGGER.info("Redirecting invokedynamic handle in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, handle.getName(), handle.getDesc(), glsmName, handle.getDesc());
|
||||
}
|
||||
dynNode.bsmArgs[i] = new Handle(handle.getTag(), GLStateManager, glsmName, handle.getDesc());
|
||||
changed = true;
|
||||
redirectInMethod = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ASSERT_MAIN_THREAD && redirectInMethod && !transformedName.startsWith(SplashProgress) && !(transformedName.startsWith(MinecraftClient) && ExcludedMinecraftMainThreadChecks.contains(mn.name))) {
|
||||
|
||||
@@ -103,9 +103,12 @@ public class MaterialState implements ISettableState<MaterialState> {
|
||||
}
|
||||
}
|
||||
|
||||
public void setShininess(FloatBuffer newBuffer) { setShininess(newBuffer.get()); }
|
||||
// On the buffer versions of shininess, we need to call `get(0)` explicitly specifying the index
|
||||
// otherwise calling `get()` advances the buffer position, and some mods may re-use the same buffer
|
||||
// in subsequent calls, resulting in a BufferUnderflowException.
|
||||
public void setShininess(FloatBuffer newBuffer) { setShininess(newBuffer.get(0)); }
|
||||
|
||||
public void setShininess(IntBuffer newBuffer) { setShininess((float) newBuffer.get());}
|
||||
public void setShininess(IntBuffer newBuffer) { setShininess((float) newBuffer.get(0)); }
|
||||
|
||||
public void setShininess(int val) { setShininess((float) val);}
|
||||
|
||||
|
||||
@@ -16,16 +16,19 @@ public class VertexAttribState {
|
||||
private static final Int2ObjectOpenHashMap<Attrib[]> vaoAttribs = new Int2ObjectOpenHashMap<>();
|
||||
private static Attrib[] current;
|
||||
private static final ArrayDeque<Attrib[]> pool = new ArrayDeque<>();
|
||||
private static int clientSideEnabledCount;
|
||||
|
||||
public static void init(int defaultVAO) {
|
||||
vaoAttribs.clear();
|
||||
pool.clear();
|
||||
current = allocAttribArray();
|
||||
vaoAttribs.put(defaultVAO, current);
|
||||
clientSideEnabledCount = 0;
|
||||
}
|
||||
|
||||
public static void onBindVertexArray(int newVAO) {
|
||||
current = vaoAttribs.computeIfAbsent(newVAO, k -> allocAttribArray());
|
||||
recomputeClientSideCount();
|
||||
}
|
||||
|
||||
public static void onDeleteVertexArray(int vaoId) {
|
||||
@@ -39,6 +42,7 @@ public class VertexAttribState {
|
||||
vaoAttribs.clear();
|
||||
pool.clear();
|
||||
current = null;
|
||||
clientSideEnabledCount = 0;
|
||||
}
|
||||
|
||||
private static Attrib[] allocAttribArray() {
|
||||
@@ -55,6 +59,7 @@ public class VertexAttribState {
|
||||
public static void set(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = current[index];
|
||||
final boolean was = a.isClientSide();
|
||||
a.size = size;
|
||||
a.type = type;
|
||||
a.normalized = normalized;
|
||||
@@ -62,11 +67,13 @@ public class VertexAttribState {
|
||||
a.offset = offset;
|
||||
a.vboId = vboId;
|
||||
a.clientPointer = null;
|
||||
if (was) clientSideEnabledCount--;
|
||||
}
|
||||
|
||||
public static void set(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer, int vboId) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = current[index];
|
||||
final boolean was = a.isClientSide();
|
||||
a.size = size;
|
||||
a.type = type;
|
||||
a.normalized = normalized;
|
||||
@@ -74,17 +81,33 @@ public class VertexAttribState {
|
||||
a.offset = 0;
|
||||
a.vboId = vboId;
|
||||
a.clientPointer = (vboId == 0) ? pointer : null;
|
||||
final boolean now = a.isClientSide();
|
||||
if (was != now) clientSideEnabledCount += now ? 1 : -1;
|
||||
}
|
||||
|
||||
public static void setEnabled(int index, boolean enabled) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
current[index].enabled = enabled;
|
||||
final Attrib a = current[index];
|
||||
if (a.clientPointer != null && a.enabled != enabled) {
|
||||
clientSideEnabledCount += enabled ? 1 : -1;
|
||||
}
|
||||
a.enabled = enabled;
|
||||
}
|
||||
|
||||
public static Attrib get(int index) {
|
||||
return current[index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void recomputeClientSideCount() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < MAX_ATTRIBS; i++) {
|
||||
if (current[i].isClientSide()) count++;
|
||||
}
|
||||
clientSideEnabledCount = count;
|
||||
}
|
||||
|
||||
public static boolean hasVBOBoundAttrib() {
|
||||
for (int i = 0; i < MAX_ATTRIBS; i++) {
|
||||
if (current[i].enabled && current[i].vboId != 0) return true;
|
||||
@@ -92,6 +115,14 @@ public class VertexAttribState {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any currently enabled vertex attribute was registered without a VBO
|
||||
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
|
||||
* offsets into a non-existent buffer, causing a native crash at draw time.
|
||||
*/
|
||||
public static boolean hasAnyClientSideEnabledAttrib() {
|
||||
return clientSideEnabledCount > 0;
|
||||
}
|
||||
public static class Attrib {
|
||||
public boolean enabled;
|
||||
public int size;
|
||||
@@ -102,6 +133,10 @@ public class VertexAttribState {
|
||||
public int vboId;
|
||||
public ByteBuffer clientPointer;
|
||||
|
||||
public boolean isClientSide() {
|
||||
return enabled && clientPointer != null;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
enabled = false;
|
||||
size = 0;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.client.rendering;
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.OrphanStreamingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -71,42 +69,42 @@ public class TessellatorStreamingDrawer {
|
||||
/**
|
||||
* Draw the vanilla Tessellator's data using streaming VBO+VAO instead of FFP client arrays.
|
||||
*/
|
||||
public static int draw(Tessellator tess) {
|
||||
if (!tess.isDrawing) {
|
||||
public static int draw(ITessellatorData tess) {
|
||||
if (!tess.isDrawing()) {
|
||||
throw new IllegalStateException("Not tesselating!");
|
||||
}
|
||||
|
||||
tess.isDrawing = false;
|
||||
tess.setDrawing(false);
|
||||
|
||||
final int vertexCount = tess.vertexCount;
|
||||
final int vertexCount = tess.getVertexCount();
|
||||
if (vertexCount == 0) {
|
||||
final int result = tess.rawBufferIndex * 4;
|
||||
tess.reset();
|
||||
final int result = tess.getRawBufferIndex() * 4;
|
||||
tess.angelica$reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Determine the optimal vertex format from the tessellator's flags
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture(), tess.hasColor(), tess.hasNormals(), tess.hasBrightness());
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
|
||||
final int vertexSize = format.getVertexSize();
|
||||
|
||||
final int requiredBytes = vertexCount * vertexSize;
|
||||
ensureRepackCapacity(requiredBytes);
|
||||
|
||||
final long writePtr = format.writeToBuffer0(repackAddress, tess.rawBuffer, tess.rawBufferIndex);
|
||||
final long writePtr = format.writeToBuffer0(repackAddress, tess.getRawBuffer(), tess.getRawBufferIndex());
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int)(writePtr - repackAddress));
|
||||
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.getDrawMode(), vertexCount);
|
||||
|
||||
// Shrink rawBuffer if oversized
|
||||
if (tess.rawBufferSize > 0x20000 && tess.rawBufferIndex < (tess.rawBufferSize << 3)) {
|
||||
tess.rawBufferSize = 0x10000;
|
||||
tess.rawBuffer = new int[tess.rawBufferSize];
|
||||
if (tess.getRawBufferSize() > 0x20000 && tess.getRawBufferIndex() < (tess.getRawBufferSize() << 3)) {
|
||||
tess.setRawBufferSize(0x10000);
|
||||
tess.setRawBuffer(new int[tess.getRawBufferSize()]);
|
||||
}
|
||||
|
||||
final int result = tess.rawBufferIndex * 4;
|
||||
tess.reset();
|
||||
final int result = tess.getRawBufferIndex() * 4;
|
||||
tess.angelica$reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -128,6 +126,19 @@ public class TessellatorStreamingDrawer {
|
||||
uploadAndDraw(buffer, flags, format, vertexSize, drawMode, vertexCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload pre-packed vertex data and draw. Public API for external batch systems.
|
||||
* @param packedData buffer positioned at 0 with limit set to total bytes
|
||||
* @param drawMode GL draw mode (GL_QUADS, GL_TRIANGLES, etc.)
|
||||
* @param flags vertex format flags (from VertexFlags)
|
||||
* @param vertexCount number of vertices
|
||||
*/
|
||||
public static void drawPacked(ByteBuffer packedData, int drawMode, int flags, int vertexCount) {
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
|
||||
final int vertexSize = format.getVertexSize();
|
||||
uploadAndDraw(packedData, flags, format, vertexSize, drawMode, vertexCount);
|
||||
}
|
||||
|
||||
private static String cachedDebugInfo = "Stream: not initialized";
|
||||
private static long lastDebugUpdateNanos;
|
||||
private static final long DEBUG_UPDATE_INTERVAL_NS = 500_000_000L; // 500ms
|
||||
@@ -172,34 +183,6 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a merged batch of pre-packed vertex data from a DeferredBatchTessellator.
|
||||
* Copies byte ranges into repackBuffer, then uploads and draws.
|
||||
*/
|
||||
static void drawPackedBatch(DeferredBatchTessellator source,
|
||||
java.util.List<DeferredBatchTessellator.DrawRange> ranges,
|
||||
int from, int to,
|
||||
int totalBytes, int totalVertices,
|
||||
int drawMode, int flags) {
|
||||
if (totalVertices == 0) return;
|
||||
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
|
||||
final int vertexSize = format.getVertexSize();
|
||||
|
||||
ensureRepackCapacity(totalBytes);
|
||||
|
||||
long writePos = repackAddress;
|
||||
for (int j = from; j < to; j++) {
|
||||
final DeferredBatchTessellator.DrawRange r = ranges.get(j);
|
||||
source.copyRange(r.byteOffset(), r.byteLength(), writePos);
|
||||
writePos += r.byteLength();
|
||||
}
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit(totalBytes);
|
||||
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, drawMode, totalVertices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload packed vertex data to a streaming buffer and issue the draw call.
|
||||
* Tries the persistent ring buffer first, falls back to orphan buffer on overflow.
|
||||
@@ -239,7 +222,11 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureRepackCapacity(int requiredBytes) {
|
||||
/**
|
||||
* Ensure the repack buffer is large enough for the given byte count.
|
||||
* Public for use by external batch systems that need to pack data before calling {@link #drawPacked}.
|
||||
*/
|
||||
public static void ensureRepackCapacity(int requiredBytes) {
|
||||
if (requiredBytes <= repackCapacity) return;
|
||||
|
||||
int newCapacity = repackCapacity;
|
||||
@@ -253,6 +240,16 @@ public class TessellatorStreamingDrawer {
|
||||
repackCapacity = newCapacity;
|
||||
}
|
||||
|
||||
/** Get the repack buffer's native address. Valid until next {@link #ensureRepackCapacity} call. */
|
||||
public static long getRepackAddress() {
|
||||
return repackAddress;
|
||||
}
|
||||
|
||||
/** Get the repack ByteBuffer. Caller must set position/limit before passing to {@link #drawPacked}. */
|
||||
public static ByteBuffer getRepackBuffer() {
|
||||
return repackBuffer;
|
||||
}
|
||||
|
||||
private static void ensureVAO(int flags, VertexFormat format) {
|
||||
init();
|
||||
|
||||
@@ -596,4 +596,21 @@ class CompatShaderTransformerTest {
|
||||
assertFalse(fragResult.contains("varying"), "no 'varying' keyword in fragment");
|
||||
assertTrue(fragResult.contains("in vec2 v_TexCoord"), "varying → in in fragment");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReservedWordNewIsRenamed() {
|
||||
String src = """
|
||||
#version 120
|
||||
uniform sampler2D tex;
|
||||
void main() {
|
||||
vec4 color = texture2D(tex, vec2(0.5));
|
||||
float new = color.r > 0.5 ? 1.0 : 0.0;
|
||||
gl_FragColor = vec4(new, new, new, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertFalse(result.matches("(?s).*\\bnew\\b(?!\\s*\\().*"), "'new' as identifier must be renamed in core profile output\n\n" + result);
|
||||
assertTrue(result.contains("angelica_renamed_new"), "'new' should be renamed to angelica_renamed_new\n\n" + result);
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,108 @@ class FeedbackManagerTest {
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPolygonFeedback_GL2D() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final float[] verts = {
|
||||
0f, 0f, 0f,
|
||||
1f, 0f, 0f,
|
||||
1f, 1f, 0f,
|
||||
0f, 1f, 0f
|
||||
};
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_POLYGON, 0, 4);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
|
||||
assertEquals(10, count, "Polygon in GL_2D should produce 10 floats");
|
||||
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
|
||||
assertEquals(4.0f, buf.get(1), 0.001f);
|
||||
assertEquals(400.0f, buf.get(2), 0.01f);
|
||||
assertEquals(300.0f, buf.get(3), 0.01f);
|
||||
assertEquals(800.0f, buf.get(4), 0.01f);
|
||||
assertEquals(300.0f, buf.get(5), 0.01f);
|
||||
assertEquals(800.0f, buf.get(6), 0.01f);
|
||||
assertEquals(600.0f, buf.get(7), 0.01f);
|
||||
assertEquals(400.0f, buf.get(8), 0.01f);
|
||||
assertEquals(600.0f, buf.get(9), 0.01f);
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQuadStripFeedback_GL2D() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final float[] verts = {
|
||||
0f, 0f, 0f,
|
||||
0f, 1f, 0f,
|
||||
1f, 0f, 0f,
|
||||
1f, 1f, 0f
|
||||
};
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_QUAD_STRIP, 0, 4);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
|
||||
assertEquals(10, count, "Quad strip in GL_2D should produce 10 floats");
|
||||
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
|
||||
assertEquals(4.0f, buf.get(1), 0.001f);
|
||||
assertEquals(400.0f, buf.get(2), 0.01f);
|
||||
assertEquals(300.0f, buf.get(3), 0.01f);
|
||||
assertEquals(400.0f, buf.get(4), 0.01f);
|
||||
assertEquals(600.0f, buf.get(5), 0.01f);
|
||||
assertEquals(800.0f, buf.get(6), 0.01f);
|
||||
assertEquals(600.0f, buf.get(7), 0.01f);
|
||||
assertEquals(800.0f, buf.get(8), 0.01f);
|
||||
assertEquals(300.0f, buf.get(9), 0.01f);
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIdentityTransform_viewportMapping() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(64);
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.gtnewhorizons.angelica.glsm.redirect;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class GLSMRedirectorTest {
|
||||
|
||||
private static final String GLSM = GLSMRedirector.getTargetClassName();
|
||||
|
||||
private static final Handle LAMBDA_METAFACTORY = new Handle(
|
||||
Opcodes.H_INVOKESTATIC,
|
||||
"java/lang/invoke/LambdaMetafactory",
|
||||
"metafactory",
|
||||
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;"
|
||||
+ "Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)"
|
||||
+ "Ljava/lang/invoke/CallSite;",
|
||||
false);
|
||||
|
||||
private static ClassNode makeClassWithInvokeDynamic(Handle implHandle) {
|
||||
final ClassNode cn = new ClassNode();
|
||||
cn.version = Opcodes.V1_8;
|
||||
cn.access = Opcodes.ACC_PUBLIC;
|
||||
cn.name = "com/example/TestClass";
|
||||
cn.superName = "java/lang/Object";
|
||||
cn.methods = new ArrayList<>();
|
||||
|
||||
final MethodNode mn = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "test", "()V", null, null);
|
||||
mn.instructions.add(new InvokeDynamicInsnNode(
|
||||
"run",
|
||||
"()Ljava/lang/Runnable;",
|
||||
LAMBDA_METAFACTORY,
|
||||
Type.getType("()V"),
|
||||
implHandle,
|
||||
Type.getType("()V")));
|
||||
mn.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||
cn.methods.add(mn);
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
private static Handle getResultHandle(ClassNode cn) {
|
||||
final InvokeDynamicInsnNode dyn = (InvokeDynamicInsnNode) cn.methods.get(0).instructions.get(0);
|
||||
return (Handle) dyn.bsmArgs[1];
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicGL11HandleIsRedirected() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals(GLSM, result.getOwner());
|
||||
assertEquals("glPopMatrix", result.getName());
|
||||
assertEquals("()V", result.getDesc());
|
||||
assertEquals(Opcodes.H_INVOKESTATIC, result.getTag());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicNamedClassHandleIsRedirected() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/EXTBlendFuncSeparate", "glBlendFuncSeparateEXT", "(IIII)V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals(GLSM, result.getOwner());
|
||||
assertEquals("tryBlendFuncSeparate", result.getName());
|
||||
assertEquals("(IIII)V", result.getDesc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicNonGLHandleIsNotTouched() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertFalse(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals("java/lang/System", result.getOwner());
|
||||
assertEquals("currentTimeMillis", result.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicUnknownGLMethodIsNotTouched() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glNonexistent", "()V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertFalse(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals("org/lwjgl/opengl/GL11", result.getOwner());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokeDynamicGLCVariantIsRedirected() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11C", "glDrawArrays", "(III)V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
final Handle result = getResultHandle(cn);
|
||||
assertEquals(GLSM, result.getOwner());
|
||||
assertEquals("glDrawArrays", result.getName());
|
||||
assertEquals("(III)V", result.getDesc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void transformedClassProducesValidBytecode() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
assertDoesNotThrow(() -> {
|
||||
cn.accept(cw);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
assertTrue(bytes.length > 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 216 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 187 B |
|
After Width: | Height: | Size: 310 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 316 B |
@@ -68,6 +68,16 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
return "OpenGL (LWJGL3)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContext() {
|
||||
try {
|
||||
GL.getCapabilities();
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() { return 50; }
|
||||
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
package com.gtnewhorizons.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.config.ConfigMigrator;
|
||||
import com.gtnewhorizons.angelica.proxy.CommonProxy;
|
||||
import com.gtnewhorizons.angelica.utils.AnimationMode;
|
||||
import com.gtnewhorizons.angelica.utils.ManagedEnum;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -32,32 +26,19 @@ import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
acceptableRemoteVersions = "*",
|
||||
guiFactory = "com.gtnewhorizons.angelica.config.AngelicaGuiConfigFactory")
|
||||
public class AngelicaMod {
|
||||
|
||||
public static final String MOD_ID = "angelica";
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
public static final boolean lwjglDebug = Boolean.getBoolean("org.lwjgl.util.Debug");
|
||||
|
||||
private static SodiumGameOptions CONFIG;
|
||||
|
||||
public static SodiumGameOptions options() {
|
||||
if (CONFIG == null) {
|
||||
CONFIG = SodiumGameOptions.load(ConfigMigrator.handleConfigMigration("angelica-options.json"));
|
||||
}
|
||||
return CONFIG;
|
||||
}
|
||||
|
||||
@SidedProxy(clientSide = "com.gtnewhorizons.angelica.proxy.ClientProxy", serverSide = "com.gtnewhorizons.angelica.proxy.CommonProxy")
|
||||
@SidedProxy(
|
||||
clientSide = "com.gtnewhorizons.angelica.proxy.ClientProxy",
|
||||
serverSide = "com.gtnewhorizons.angelica.proxy.CommonProxy")
|
||||
public static CommonProxy proxy;
|
||||
|
||||
public static final boolean lwjglDebug = Boolean.parseBoolean(System.getProperty("org.lwjgl.util.Debug", "false"));
|
||||
|
||||
public static final ManagedEnum<AnimationMode> animationsMode = new ManagedEnum<>(AnimationMode.VISIBLE_ONLY);
|
||||
|
||||
public static BlockError blockError;
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
ModStatus.preInit();
|
||||
proxy.preInit(event);
|
||||
blockError = new BlockError();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Public API for querying Angelica's GL call redirections.
|
||||
* <p>
|
||||
* Angelica's bytecode transformer redirects GL calls (e.g., {@code GL11.glPushMatrix()}) to
|
||||
* {@link com.gtnewhorizons.angelica.glsm.GLStateManager GLStateManager} for state tracking and
|
||||
* core profile compatibility. This works for normal Java bytecode loaded through the Forge classloader,
|
||||
* but callers that bypass class transformation (e.g., Nashorn/script engines, reflection, MethodHandles)
|
||||
* need to perform their own redirection.
|
||||
* <p>
|
||||
* This class provides the redirect mapping data and a precompiled regex pattern for transforming
|
||||
* script source text before evaluation.
|
||||
*
|
||||
* <h3>Example: transforming a Nashorn script before eval</h3>
|
||||
* Given a script that uses {@code importPackage(Packages.org.lwjgl.opengl)} and calls
|
||||
* {@code GL11.glPushMatrix()}, the text transformation rewrites those calls before evaluation:
|
||||
* <pre>{@code
|
||||
* // Make GLStateManager available to the script -- either via importPackage:
|
||||
* script = "importPackage(Packages.com.gtnewhorizons.angelica.glsm);\n" + script;
|
||||
* // ... or via Java.type for an explicit binding:
|
||||
* // script = "var GLStateManager = Java.type('" + GLRedirects.TARGET_CLASS + "');\n" + script;
|
||||
*
|
||||
* // Rewrite: GL11.glPushMatrix() -> GLStateManager.glPushMatrix()
|
||||
* script = GLRedirects.getMethodRedirectPattern().matcher(script).replaceAll(mr -> {
|
||||
* String target = GLRedirects.getTargetMethodName(mr.group(0));
|
||||
* return target != null ? "GLStateManager." + target : mr.group(0);
|
||||
* });
|
||||
*
|
||||
* engine.eval(script);
|
||||
* }</pre>
|
||||
*/
|
||||
public final class GLRedirects {
|
||||
/** Fully qualified name of the GLStateManager class that all methods redirect to. */
|
||||
@SuppressWarnings("unused")
|
||||
public static final String TARGET_CLASS = "com.gtnewhorizons.angelica.glsm.GLStateManager";
|
||||
|
||||
private static final String SHORT_GL_PREFIX;
|
||||
private static final Map<String, Map<String, String>> NAMED_CLASS_SHORT_NAME_MAP;
|
||||
private static final Map<String, String> GL_PREFIX_METHOD_REDIRECTS;
|
||||
private static final Pattern METHOD_REDIRECT_PATTERN;
|
||||
|
||||
static {
|
||||
SHORT_GL_PREFIX = buildShortGLPrefix();
|
||||
NAMED_CLASS_SHORT_NAME_MAP = buildNamedClassShortNameMap();
|
||||
GL_PREFIX_METHOD_REDIRECTS = GLSMRedirector.getGLPrefixMethodRedirects();
|
||||
METHOD_REDIRECT_PATTERN = buildMethodRedirectPattern();
|
||||
}
|
||||
|
||||
private GLRedirects() {}
|
||||
|
||||
/**
|
||||
* Returns a precompiled pattern that matches redirected {@code ClassName.methodName} references
|
||||
* using short (simple) class names, as they might typically appear in script text.
|
||||
*
|
||||
* @return compiled regex pattern
|
||||
*/
|
||||
public static Pattern getMethodRedirectPattern() {
|
||||
return METHOD_REDIRECT_PATTERN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the GLStateManager target method name for a short-name reference.
|
||||
*
|
||||
* @param classAndMethod short class name and method, e.g., {@code "GL11.glPushMatrix"}
|
||||
* @return the target method name on GLStateManager (e.g., {@code "glPushMatrix"}), or {@code null} if this reference is not redirected
|
||||
*/
|
||||
public static String getTargetMethodName(String classAndMethod) {
|
||||
final int dot = classAndMethod.indexOf('.');
|
||||
if (dot < 0) return null;
|
||||
|
||||
final String className = classAndMethod.substring(0, dot);
|
||||
final String methodName = classAndMethod.substring(dot + 1);
|
||||
|
||||
final Map<String, String> classRedirects = NAMED_CLASS_SHORT_NAME_MAP.get(className);
|
||||
if (classRedirects != null) {
|
||||
return classRedirects.get(methodName);
|
||||
}
|
||||
|
||||
if (className.startsWith(SHORT_GL_PREFIX)) {
|
||||
return GL_PREFIX_METHOD_REDIRECTS.get(methodName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String buildShortGLPrefix() {
|
||||
final String glPrefix = GLSMRedirector.getGLPrefix();
|
||||
return glPrefix.substring(glPrefix.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
private static Map<String, Map<String, String>> buildNamedClassShortNameMap() {
|
||||
final Map<String, Map<String, String>> named = new HashMap<>();
|
||||
for (final var entry : GLSMRedirector.getNamedClassMethodRedirects().entrySet()) {
|
||||
final String internalName = entry.getKey();
|
||||
final String shortName = internalName.substring(internalName.lastIndexOf('/') + 1);
|
||||
named.put(shortName, entry.getValue());
|
||||
}
|
||||
return named;
|
||||
}
|
||||
|
||||
private static Pattern buildMethodRedirectPattern() {
|
||||
final TreeSet<String> alternatives = new TreeSet<>();
|
||||
alternatives.add(Pattern.quote(SHORT_GL_PREFIX) + "\\w+");
|
||||
for (final String shortName : NAMED_CLASS_SHORT_NAME_MAP.keySet()) {
|
||||
alternatives.add(Pattern.quote(shortName));
|
||||
}
|
||||
final String classAlt = String.join("|", alternatives);
|
||||
return Pattern.compile("\\b(" + classAlt + ")\\.\\w+\\b");
|
||||
}
|
||||
}
|
||||
@@ -334,6 +334,7 @@ public class BatchingFontRenderer {
|
||||
batchCommands.sort(FontDrawCmd.DRAW_ORDER_COMPARATOR);
|
||||
|
||||
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
|
||||
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
|
||||
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
boolean textureChanged = false;
|
||||
|
||||
@@ -417,6 +418,9 @@ public class BatchingFontRenderer {
|
||||
if (isTextureEnabledBefore) {
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
if (!isBlendEnabledBefore) {
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
if (textureChanged) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.client.rendering;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.ITessellatorInstance;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -33,10 +34,30 @@ class DeferredBatchTessellator extends DirectTessellator {
|
||||
private final Vector3f scratch = new Vector3f();
|
||||
private Matrix4fc currentTransform;
|
||||
|
||||
private Tessellator parentTessellator;
|
||||
private boolean inIntercept;
|
||||
|
||||
DeferredBatchTessellator(ByteBuffer buffer) {
|
||||
super(buffer, false); // deleteAfter=false: keep buffer across resets for reuse
|
||||
}
|
||||
|
||||
void setParentTessellator(Tessellator parent) {
|
||||
this.parentTessellator = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int draw() {
|
||||
if (!inIntercept) {
|
||||
final Tessellator parent = parentTessellator;
|
||||
if (parent != null && parent.isDrawing) {
|
||||
interceptDraw(parent);
|
||||
((ITessellatorInstance) parent).discard();
|
||||
}
|
||||
}
|
||||
isDrawing = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long writeVertexData(VertexFormat format, int[] rawBuffer, int rawBufferIndex) {
|
||||
if (currentTransform != null) {
|
||||
@@ -53,27 +74,32 @@ class DeferredBatchTessellator extends DirectTessellator {
|
||||
|
||||
@Override
|
||||
protected int interceptDraw(Tessellator tess) {
|
||||
// Compute delta: if current MV differs from default, pre-transform vertices
|
||||
final Matrix4f currentMV = GLStateManager.getModelViewMatrix();
|
||||
if (!currentMV.equals(defaultModelview)) {
|
||||
defaultModelviewInverse.mul(currentMV, deltaMatrix);
|
||||
this.currentTransform = deltaMatrix;
|
||||
} else {
|
||||
this.currentTransform = null;
|
||||
inIntercept = true;
|
||||
try {
|
||||
// Compute delta: if current MV differs from default, pre-transform vertices
|
||||
final Matrix4f currentMV = GLStateManager.getModelViewMatrix();
|
||||
if (!currentMV.equals(defaultModelview)) {
|
||||
defaultModelviewInverse.mul(currentMV, deltaMatrix);
|
||||
this.currentTransform = deltaMatrix;
|
||||
} else {
|
||||
this.currentTransform = null;
|
||||
}
|
||||
|
||||
final int byteOffsetBefore = bufferLimit();
|
||||
final int vertCount = tess.vertexCount;
|
||||
final int drawMode = tess.drawMode;
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
|
||||
final long stateKey = DeferredDrawBatcher.captureStateKey();
|
||||
|
||||
final int result = super.interceptDraw(tess); // repacks int[] -> ByteBuffer + transforms if vertexTransform set
|
||||
|
||||
final int byteOffsetAfter = bufferLimit();
|
||||
ranges.add(new DrawRange(stateKey, byteOffsetBefore, byteOffsetAfter - byteOffsetBefore, vertCount, drawMode, flags));
|
||||
this.currentTransform = null; // clear for next draw
|
||||
return result;
|
||||
} finally {
|
||||
inIntercept = false;
|
||||
}
|
||||
|
||||
final int byteOffsetBefore = bufferLimit();
|
||||
final int vertCount = tess.vertexCount;
|
||||
final int drawMode = tess.drawMode;
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
|
||||
final long stateKey = DeferredDrawBatcher.captureStateKey();
|
||||
|
||||
final int result = super.interceptDraw(tess); // repacks int[] → ByteBuffer + transforms if vertexTransform set
|
||||
|
||||
final int byteOffsetAfter = bufferLimit();
|
||||
ranges.add(new DrawRange(stateKey, byteOffsetBefore, byteOffsetAfter - byteOffsetBefore, vertCount, drawMode, flags));
|
||||
this.currentTransform = null; // clear for next draw
|
||||
return result;
|
||||
}
|
||||
|
||||
List<DrawRange> getRanges() { return ranges; }
|
||||
@@ -86,6 +112,7 @@ class DeferredBatchTessellator extends DirectTessellator {
|
||||
|
||||
@Override
|
||||
protected void onRemovedFromStack() {
|
||||
parentTessellator = null;
|
||||
clearRanges();
|
||||
super.onRemovedFromStack(); // calls reset(), keeps baseBuffer (deleteAfter=false)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.client.rendering;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Comparator;
|
||||
@@ -37,6 +39,7 @@ public class DeferredDrawBatcher {
|
||||
batchTessellator = new DeferredBatchTessellator(memAlloc(INITIAL_BUFFER_SIZE));
|
||||
}
|
||||
batchTessellator.clearRanges();
|
||||
batchTessellator.setParentTessellator(Tessellator.instance);
|
||||
batchTessellator.snapshotDefaultModelview();
|
||||
TessellatorManager.startCapturingDirect(batchTessellator);
|
||||
}
|
||||
@@ -106,12 +109,33 @@ public class DeferredDrawBatcher {
|
||||
break;
|
||||
}
|
||||
|
||||
TessellatorStreamingDrawer.drawPackedBatch(batchTessellator, ranges, i, subEnd, totalBytes, totalVertices, drawMode, flags);
|
||||
drawPackedBatch(batchTessellator, ranges, i, subEnd, totalBytes, totalVertices, drawMode, flags);
|
||||
|
||||
i = subEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack ranges from a DeferredBatchTessellator into the streaming drawer's repack buffer
|
||||
* and issue a single draw call for the merged batch.
|
||||
*/
|
||||
private static void drawPackedBatch(DeferredBatchTessellator source, List<DeferredBatchTessellator.DrawRange> ranges, int from, int to, int totalBytes, int totalVertices, int drawMode, int flags) {
|
||||
if (totalVertices == 0) return;
|
||||
|
||||
TessellatorStreamingDrawer.ensureRepackCapacity(totalBytes);
|
||||
long writePos = TessellatorStreamingDrawer.getRepackAddress();
|
||||
for (int j = from; j < to; j++) {
|
||||
final DeferredBatchTessellator.DrawRange r = ranges.get(j);
|
||||
source.copyRange(r.byteOffset(), r.byteLength(), writePos);
|
||||
writePos += r.byteLength();
|
||||
}
|
||||
final java.nio.ByteBuffer buf = TessellatorStreamingDrawer.getRepackBuffer();
|
||||
buf.position(0);
|
||||
buf.limit(totalBytes);
|
||||
|
||||
TessellatorStreamingDrawer.drawPacked(buf, drawMode, flags, totalVertices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack current GLSM state into a long key for grouping.
|
||||
* Layout: [tex1En:1][tex0En:1][textureId:20][srcRgb:12][dstRgb:12][blendEnabled:1][depthMask:1] = 48 bits
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.client.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
|
||||
import net.minecraft.client.renderer.texture.AbstractTexture;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -15,11 +13,15 @@ public class TextureTracker {
|
||||
*/
|
||||
|
||||
public static final TextureTracker INSTANCE = new TextureTracker();
|
||||
|
||||
|
||||
private static Runnable bindTextureListener;
|
||||
|
||||
static {
|
||||
StateUpdateNotifiers.bindTextureNotifier = listener -> bindTextureListener = listener;
|
||||
}
|
||||
|
||||
private final Int2ObjectMap<AbstractTexture> textures = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private boolean lockBindCallback;
|
||||
|
||||
private TextureTracker() {
|
||||
}
|
||||
|
||||
@@ -31,23 +33,13 @@ public class TextureTracker {
|
||||
public AbstractTexture getTexture(int id) {
|
||||
return textures.get(id);
|
||||
}
|
||||
|
||||
public void onBindTexture(int id) {
|
||||
if (lockBindCallback) {
|
||||
return;
|
||||
}
|
||||
if (GLStateManager.getActiveTextureUnit() == 0) {
|
||||
lockBindCallback = true;
|
||||
if (GLSMHooks.TEXTURE_BIND.hasListeners()) {
|
||||
GLSMHooks.textureBindEvent.textureId = id;
|
||||
GLSMHooks.TEXTURE_BIND.post(GLSMHooks.textureBindEvent);
|
||||
}
|
||||
// Reset texture state
|
||||
RenderSystem.bindTextureToUnit(0, id);
|
||||
lockBindCallback = false;
|
||||
|
||||
public void onBindTexture() {
|
||||
if (bindTextureListener != null) {
|
||||
bindTextureListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onDeleteTexture(int id) {
|
||||
textures.remove(id);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public class ModStatus {
|
||||
public static boolean isThaumicHorizonsLoaded;
|
||||
public static boolean isBaublesLoaded;
|
||||
public static boolean isFluidLoggedLoaded;
|
||||
public static boolean isBOPLoaded;
|
||||
|
||||
public static void preInit() {
|
||||
if (Loader.isModLoaded("backhand")) {
|
||||
@@ -45,6 +46,7 @@ public class ModStatus {
|
||||
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
|
||||
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
isBOPLoaded = Loader.isModLoaded("BiomesOPlenty");
|
||||
|
||||
// remove compat with original release of BG2
|
||||
if (isBattlegearLoaded){
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
package com.gtnewhorizons.angelica.compat.bettercrashes;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
|
||||
import cpw.mods.fml.common.Optional;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import vfyjxf.bettercrashes.utils.StateManager;
|
||||
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
|
||||
@Optional.Interface(modid = "angelica", iface = "vfyjxf.bettercrashes.utils.StateManager.IResettable")
|
||||
public class BetterCrashesCompat implements StateManager.IResettable {
|
||||
|
||||
private static BetterCrashesCompat INSTANCE;
|
||||
|
||||
public BetterCrashesCompat() {
|
||||
AngelicaMod.LOGGER.info("BetterCrashesCompat initialized");
|
||||
this.register();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new BetterCrashesCompat();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BetterCrashesCompat() {
|
||||
LOGGER.info("BetterCrashesCompat initialized");
|
||||
this.register();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetState() {
|
||||
LOGGER.info("Reloading SodiumRenderer");
|
||||
AngelicaMod.LOGGER.info("Reloading SodiumRenderer");
|
||||
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).angelica$reload();
|
||||
LOGGER.info("Resetting GLStateManager");
|
||||
AngelicaMod.LOGGER.info("Resetting GLStateManager");
|
||||
GLStateManager.reset();
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,11 @@ public class Camera {
|
||||
|
||||
private float pitch;
|
||||
private float yaw;
|
||||
private EntityLivingBase entity;
|
||||
private boolean isSleeping;
|
||||
private boolean thirdPerson;
|
||||
private float partialTicks;
|
||||
|
||||
private Camera() {
|
||||
}
|
||||
private Camera() {}
|
||||
|
||||
/**
|
||||
* Updates the camera position and orientation based on the current entity and partial ticks.
|
||||
@@ -41,7 +40,7 @@ public class Camera {
|
||||
*/
|
||||
public void update(EntityLivingBase entity, float partialTicks) {
|
||||
this.partialTicks = partialTicks;
|
||||
this.entity = entity;
|
||||
this.isSleeping = entity.isPlayerSleeping();
|
||||
|
||||
// Entity position (interpolated, at eye level)
|
||||
final double entityX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks;
|
||||
|
||||
@@ -235,6 +235,11 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean enableHardcodedCustomUniforms;
|
||||
|
||||
@Config.Comment("Modern MC_VERSION to try if shader pack has no 1.7.10 section. 0 = default (260101)")
|
||||
@Config.DefaultInt(0)
|
||||
@Config.RangeInt(min = 0)
|
||||
public static int modernFallbackMcVersion;
|
||||
|
||||
@Config.Comment("Define IS_IRIS in shader macros.")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean defineIsIris;
|
||||
@@ -262,6 +267,11 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public boolean narrowAlfheim;
|
||||
|
||||
@Config.Comment("Narrow Ears transformer exclusions to allow GL redirection")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public boolean narrowEars;
|
||||
}
|
||||
|
||||
@Config.Comment("Renders chunks before neighbors are ready. Improves loading at render distance edges, useful for low render distance servers.")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.iris;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
@@ -10,6 +11,7 @@ import net.coderbot.iris.gl.blending.AlphaTestStorage;
|
||||
import net.coderbot.iris.gl.blending.BlendModeStorage;
|
||||
import net.coderbot.iris.gl.blending.DepthColorStorage;
|
||||
import net.coderbot.iris.gl.program.ProgramUniforms;
|
||||
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.samplers.IrisSamplers;
|
||||
@@ -17,7 +19,21 @@ import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.coderbot.iris.vertices.ImmediateState;
|
||||
|
||||
public class IrisGLSMBridge {
|
||||
|
||||
|
||||
private static Runnable blendFuncListener = null;
|
||||
private static Runnable fogModeListener = null;
|
||||
private static Runnable fogStartListener = null;
|
||||
private static Runnable fogEndListener = null;
|
||||
private static Runnable fogDensityListener = null;
|
||||
|
||||
static {
|
||||
StateUpdateNotifiers.blendFuncNotifier = listener -> blendFuncListener = listener;
|
||||
StateUpdateNotifiers.fogModeNotifier = listener -> fogModeListener = listener;
|
||||
StateUpdateNotifiers.fogStartNotifier = listener -> fogStartListener = listener;
|
||||
StateUpdateNotifiers.fogEndNotifier = listener -> fogEndListener = listener;
|
||||
StateUpdateNotifiers.fogDensityNotifier = listener -> fogDensityListener = listener;
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
IrisSamplers.initRenderer();
|
||||
GLSMHooks.blendHandler = new DeferredBlendHandler() {
|
||||
@@ -75,9 +91,25 @@ public class IrisGLSMBridge {
|
||||
DepthColorStorage.deferColorMask(r, g, b, a);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.addListener(event -> {
|
||||
if (Iris.enabled) {
|
||||
if (blendFuncListener != null) blendFuncListener.run();
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.FOG_STATE_CHANGE.addListener(event -> {
|
||||
if (Iris.enabled) {
|
||||
if (fogModeListener != null) fogModeListener.run();
|
||||
if (fogStartListener != null) fogStartListener.run();
|
||||
if (fogEndListener != null) fogEndListener.run();
|
||||
if (fogDensityListener != null) fogDensityListener.run();
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.TEXTURE_BIND.addListener(event -> {
|
||||
if (Iris.enabled) {
|
||||
TextureTracker.INSTANCE.onBindTexture();
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline != null) {
|
||||
pipeline.onBindTexture(event.textureId);
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
package com.gtnewhorizons.angelica.loading;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigException;
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
|
||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.ITransformers;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
|
||||
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
|
||||
import com.gtnewhorizons.angelica.mixins.Mixins;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
import jss.notfine.asm.AsmTransformers;
|
||||
import jss.notfine.asm.mappings.Namer;
|
||||
import jss.notfine.config.MCPatcherForgeConfig;
|
||||
import jss.notfine.config.NotFineConfig;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.config.LoggerConfig;
|
||||
import org.spongepowered.asm.launch.GlobalProperties;
|
||||
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
// this is not the real IFMLLoadingPlugin, if you put any annotations
|
||||
// here it won't do anything, that's why we register transformer
|
||||
// exclusions manually in the constructor
|
||||
public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
private static Boolean OBF_ENV;
|
||||
private String[] transformerClasses;
|
||||
|
||||
public AngelicaClientTweaker() {
|
||||
Launch.classLoader.addTransformerExclusion("jss.notfine.asm");
|
||||
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.angelica.loading");
|
||||
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.angelica.glsm.GLStateManager");
|
||||
|
||||
if (Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE) {
|
||||
addLwjgl3ifyExclusions();
|
||||
}
|
||||
|
||||
try {
|
||||
// Angelica Config
|
||||
ConfigurationManager.registerConfig(AngelicaConfig.class);
|
||||
ConfigurationManager.registerConfig(CompatConfig.class);
|
||||
ConfigurationManager.registerConfig(FontConfig.class);
|
||||
MCPatcherForgeConfig.registerConfig();
|
||||
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
|
||||
final Configuration config = ctx.getConfiguration();
|
||||
final LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
|
||||
if (AngelicaConfig.enableDebugLogging) {
|
||||
loggerConfig.setLevel(Level.DEBUG);
|
||||
}
|
||||
ctx.updateLoggers();
|
||||
|
||||
// Debug features
|
||||
AngelicaConfig.enableTestBlocks = Boolean.getBoolean("angelica.enableTestBlocks");
|
||||
|
||||
} catch (ConfigException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
verifyDependencies();
|
||||
|
||||
// Register a scoped redirector early so classes prematurely loaded by other coremods during discovery/injectData
|
||||
// (e.g. Mycelium using HookLoader.class.getName()) still get GL calls redirected to GLSM. Only targets specific mod
|
||||
// packages to avoid interfering with mixin delegation on game classes. AngelicaLateTweaker removes this registration once
|
||||
// mixins are setup.
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (!rfbLoaded) {
|
||||
Launch.classLoader.registerTransformer("com.gtnewhorizons.angelica.loading.fml.transformers.EarlyRedirectorTransformer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addLwjgl3ifyExclusions() {
|
||||
final var handle = SharedConfig.getRfbTransformers().stream()
|
||||
.filter(transformer -> transformer.id().equals("lwjgl3ify:redirect"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (handle != null) {
|
||||
for (String exclusion : EcosystemNarrowRules.LWJGL3IFY_EXCLUSIONS_SHARED) {
|
||||
handle.exclusions().add(exclusion);
|
||||
}
|
||||
handle.exclusions().add("com.gtnewhorizons.angelica.vulkan");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyDependencies() {
|
||||
DependencyVerifier.verify(AngelicaTweaker.class, List.of(
|
||||
new DependencyVerifier.Check(
|
||||
"/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class",
|
||||
"Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/com/gtnewhorizon/gtnhlib/client/renderer/PrimitiveExtractor.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.8.21 or newer! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/xyz/wagyourtail/jvmdg/exc/MissingStubError.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.9.0 or newer! Download: https://modrinth.com/mod/gtnhlib")
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
if (transformerClasses == null) {
|
||||
final List<String> transformers = new ArrayList<>();
|
||||
transformers.addAll(CompatHandlers.getTransformers());
|
||||
// Add NotFine transformers
|
||||
final List<String> notFineTransformers = Arrays.asList(ITransformers.getTransformers(AsmTransformers.class));
|
||||
if (!notFineTransformers.isEmpty()) Namer.initNames();
|
||||
transformers.addAll(notFineTransformers);
|
||||
transformerClasses = transformers.toArray(new String[0]);
|
||||
}
|
||||
return transformerClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModContainerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetupClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectData(Map<String, Object> data) {
|
||||
OBF_ENV = (Boolean) data.get("runtimeDeobfuscationEnabled");
|
||||
// Directly add this to the MixinServiceLaunchWrapper tweaker's list of Tweak Classes
|
||||
final List<String> tweaks = GlobalProperties.get(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
|
||||
if (tweaks != null) {
|
||||
// Bridge transformer compat config to blackboard for AngelicaLateTweaker
|
||||
narrowTransformerConfig("DragonAPI", AngelicaConfig.transformerCompat.narrowDragonAPI);
|
||||
narrowTransformerConfig("Xaeros", AngelicaConfig.transformerCompat.narrowXaeros);
|
||||
narrowTransformerConfig("AdvancedLightsabers", AngelicaConfig.transformerCompat.narrowAdvancedLightsabers);
|
||||
narrowTransformerConfig("Alfheim", AngelicaConfig.transformerCompat.narrowAlfheim);
|
||||
narrowTransformerConfig("Ears", AngelicaConfig.transformerCompat.narrowEars);
|
||||
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.IncompatibleModsDisablerTweaker");
|
||||
if (AngelicaConfig.enableHudCaching) {
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.XaerosTransformerDisablerTweaker");
|
||||
}
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
// We register ITweakers that will run last in order to register
|
||||
// specific IClassTransformers that will run last in the transformer chain.
|
||||
// If we were to register them normally in getASMTransformerClass(),
|
||||
// they would be sorted at index 0 which we do not want.
|
||||
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (!rfbLoaded) {
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.AngelicaLateTweaker");
|
||||
if (AngelicaConfig.enableCeleritas) {
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.CeleritasLateTweaker");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Publish a transformer-narrowing toggle to the blackboard; read by AngelicaLateTweaker.narrowEnabled(). */
|
||||
private static void narrowTransformerConfig(String mod, boolean enabled) {
|
||||
Launch.blackboard.put("angelica.narrow." + mod, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
final int v = Runtime.version().feature();
|
||||
if (v >= 21) return "mixins.angelica.early.j21.json";
|
||||
if (v >= 17) return "mixins.angelica.early.j17.json";
|
||||
return "mixins.angelica.early.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
NotFineConfig.loadSettings();
|
||||
//This may be possible to handle differently or fix.
|
||||
if (loadedCoreMods.contains("cofh.asm.LoadingPlugin")) {
|
||||
MCPatcherForgeConfig.ExtendedHD.hdFont = false;
|
||||
}
|
||||
return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we are in an obfuscated environment, returns false in dev environment.
|
||||
*/
|
||||
public static boolean isObfEnv() {
|
||||
if (OBF_ENV == null) {
|
||||
throw new IllegalStateException("Obfuscation state has been accessed too early!");
|
||||
}
|
||||
return OBF_ENV;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate name according to current environment's obfuscation
|
||||
*/
|
||||
public static String obf(String deobf, String obf) {
|
||||
if (OBF_ENV == null) {
|
||||
throw new IllegalStateException("Obfuscation state has been accessed too early!");
|
||||
}
|
||||
if (OBF_ENV) {
|
||||
return obf;
|
||||
}
|
||||
return deobf;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.loading;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigException;
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
|
||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.ITransformers;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.Mixins;
|
||||
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
import jss.notfine.asm.AsmTransformers;
|
||||
import jss.notfine.asm.mappings.Namer;
|
||||
import jss.notfine.config.MCPatcherForgeConfig;
|
||||
import jss.notfine.config.NotFineConfig;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.config.LoggerConfig;
|
||||
import org.spongepowered.asm.launch.GlobalProperties;
|
||||
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -41,195 +19,76 @@ import java.util.Set;
|
||||
// They will instead use default index 0 which means they will see
|
||||
// obfuscated mappings and not SRG mappings when running outside of dev env.
|
||||
// ===============================================
|
||||
//@IFMLLoadingPlugin.SortingIndex(Integer.MAX_VALUE - 5)
|
||||
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||
@IFMLLoadingPlugin.TransformerExclusions({
|
||||
"jss.notfine.asm",
|
||||
"com.gtnewhorizons.angelica.loading",
|
||||
"com.gtnewhorizons.angelica.glsm.GLStateManager"
|
||||
})
|
||||
public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
private static Boolean OBF_ENV;
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
private String[] transformerClasses;
|
||||
private final IFMLLoadingPlugin loadingPlugin;
|
||||
private final IEarlyMixinLoader mixinLoader;
|
||||
|
||||
public AngelicaTweaker() {
|
||||
if (Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE) {
|
||||
addLwjgl3ifyExclusions();
|
||||
}
|
||||
|
||||
try {
|
||||
// Angelica Config
|
||||
ConfigurationManager.registerConfig(AngelicaConfig.class);
|
||||
ConfigurationManager.registerConfig(CompatConfig.class);
|
||||
ConfigurationManager.registerConfig(FontConfig.class);
|
||||
MCPatcherForgeConfig.registerConfig();
|
||||
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
|
||||
final Configuration config = ctx.getConfiguration();
|
||||
final LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
|
||||
if (AngelicaConfig.enableDebugLogging) {
|
||||
loggerConfig.setLevel(Level.DEBUG);
|
||||
}
|
||||
ctx.updateLoggers();
|
||||
|
||||
// Debug features
|
||||
AngelicaConfig.enableTestBlocks = Boolean.getBoolean("angelica.enableTestBlocks");
|
||||
|
||||
} catch (ConfigException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
verifyDependencies();
|
||||
|
||||
// Register a scoped redirector early so classes prematurely loaded by other coremods during discovery/injectData
|
||||
// (e.g. Mycelium using HookLoader.class.getName()) still get GL calls redirected to GLSM. Only targets specific mod
|
||||
// packages to avoid interfering with mixin delegation on game classes. AngelicaLateTweaker removes this registration once
|
||||
// mixins are setup.
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (!rfbLoaded) {
|
||||
Launch.classLoader.registerTransformer("com.gtnewhorizons.angelica.loading.fml.transformers.EarlyRedirectorTransformer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyDependencies() {
|
||||
// Check for fastutil (bundled with GTNHLib since 0.2.1)
|
||||
if (AngelicaTweaker.class.getResource("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class") == null) {
|
||||
throw new RuntimeException("Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib");
|
||||
}
|
||||
|
||||
// Check for PrimitiveExtractor/cel.model classes (added in GTNHLib 0.8.21)
|
||||
if (AngelicaTweaker.class.getResource("/com/gtnewhorizon/gtnhlib/client/renderer/PrimitiveExtractor.class") == null) {
|
||||
throw new RuntimeException("GTNHLib is outdated: Angelica requires GTNHLib 0.8.21 or newer! Download: https://modrinth.com/mod/gtnhlib");
|
||||
}
|
||||
|
||||
// Check for jvmdowngrader stubs (bundled with GTNHLib since 0.9.0)
|
||||
if (AngelicaTweaker.class.getResource("/xyz/wagyourtail/jvmdg/exc/MissingStubError.class") == null) {
|
||||
throw new RuntimeException("GTNHLib is outdated: Angelica requires GTNHLib 0.9.0 or newer! Download: https://modrinth.com/mod/gtnhlib");
|
||||
final AngelicaClientTweaker clientTweaker = new AngelicaClientTweaker();
|
||||
loadingPlugin = clientTweaker;
|
||||
mixinLoader = clientTweaker;
|
||||
} else {
|
||||
loadingPlugin = null;
|
||||
mixinLoader = null;
|
||||
LogManager.getLogger("Angelica").info("Trying to get more FPS on your server ? :kekw:");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
if (transformerClasses == null) {
|
||||
final List<String> transformers = new ArrayList<>();
|
||||
transformers.addAll(CompatHandlers.getTransformers());
|
||||
// Add NotFine transformers
|
||||
final List<String> notFineTransformers = Arrays.asList(ITransformers.getTransformers(AsmTransformers.class));
|
||||
if (!notFineTransformers.isEmpty()) Namer.initNames();
|
||||
transformers.addAll(notFineTransformers);
|
||||
transformerClasses = transformers.toArray(new String[0]);
|
||||
if (this.loadingPlugin != null) {
|
||||
return this.loadingPlugin.getASMTransformerClass();
|
||||
}
|
||||
return transformerClasses;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModContainerClass() {
|
||||
if (this.loadingPlugin != null) {
|
||||
return this.loadingPlugin.getModContainerClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetupClass() {
|
||||
if (this.loadingPlugin != null) {
|
||||
return this.loadingPlugin.getSetupClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Publish a transformer-narrowing toggle to the blackboard; read by AngelicaLateTweaker.narrowEnabled(). */
|
||||
private static void narrowTransformerConfig(String mod, boolean enabled) {
|
||||
Launch.blackboard.put("angelica.narrow." + mod, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectData(Map<String, Object> data) {
|
||||
OBF_ENV = (Boolean) data.get("runtimeDeobfuscationEnabled");
|
||||
// Directly add this to the MixinServiceLaunchWrapper tweaker's list of Tweak Classes
|
||||
final List<String> tweaks = GlobalProperties.get(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
|
||||
if (tweaks != null) {
|
||||
// Bridge transformer compat config to blackboard for AngelicaLateTweaker
|
||||
narrowTransformerConfig("DragonAPI", AngelicaConfig.transformerCompat.narrowDragonAPI);
|
||||
narrowTransformerConfig("Xaeros", AngelicaConfig.transformerCompat.narrowXaeros);
|
||||
narrowTransformerConfig("AdvancedLightsabers", AngelicaConfig.transformerCompat.narrowAdvancedLightsabers);
|
||||
narrowTransformerConfig("Alfheim", AngelicaConfig.transformerCompat.narrowAlfheim);
|
||||
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.IncompatibleModsDisablerTweaker");
|
||||
if (AngelicaConfig.enableHudCaching) {
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.XaerosTransformerDisablerTweaker");
|
||||
}
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
// We register ITweakers that will run last in order to register
|
||||
// specific IClassTransformers that will run last in the transformer chain.
|
||||
// If we were to register them normally in getASMTransformerClass(),
|
||||
// they would be sorted at index 0 which we do not want.
|
||||
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (!rfbLoaded) {
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.AngelicaLateTweaker");
|
||||
if (AngelicaConfig.enableCeleritas) {
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.CeleritasLateTweaker");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.loadingPlugin != null) {
|
||||
this.loadingPlugin.injectData(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessTransformerClass() {
|
||||
if (this.loadingPlugin != null) {
|
||||
return this.loadingPlugin.getAccessTransformerClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
final int v = Runtime.version().feature();
|
||||
if (v >= 21) return "mixins.angelica.early.j21.json";
|
||||
if (v >= 17) return "mixins.angelica.early.j17.json";
|
||||
return "mixins.angelica.early.json";
|
||||
if (this.mixinLoader != null) {
|
||||
return this.mixinLoader.getMixinConfig();
|
||||
}
|
||||
return "mixins.angelica.server.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
NotFineConfig.loadSettings();
|
||||
//This may be possible to handle differently or fix.
|
||||
if (loadedCoreMods.contains("cofh.asm.LoadingPlugin")) {
|
||||
MCPatcherForgeConfig.ExtendedHD.hdFont = false;
|
||||
if (this.mixinLoader != null) {
|
||||
return this.mixinLoader.getMixins(loadedCoreMods);
|
||||
}
|
||||
return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods);
|
||||
}
|
||||
|
||||
private static void addLwjgl3ifyExclusions() {
|
||||
final var handle = SharedConfig.getRfbTransformers().stream()
|
||||
.filter(transformer -> transformer.id().equals("lwjgl3ify:redirect"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (handle != null) {
|
||||
handle.exclusions().add("org.embeddedt.embeddium");
|
||||
handle.exclusions().add("org.taumc.celeritas");
|
||||
handle.exclusions().add("com.mitchej123.lwjgl.lwjgl3");
|
||||
handle.exclusions().add("com.mitchej123.glsm");
|
||||
handle.exclusions().add("com.gtnewhorizons.angelica.lwjgl3");
|
||||
handle.exclusions().add("com.gtnewhorizons.angelica.vulkan");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we are in an obfuscated environment, returns false in dev environment.
|
||||
*/
|
||||
public static boolean isObfEnv() {
|
||||
if (OBF_ENV == null) {
|
||||
throw new IllegalStateException("Obfuscation state has been accessed too early!");
|
||||
}
|
||||
return OBF_ENV;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate name according to current environment's obfuscation
|
||||
*/
|
||||
public static String obf(String deobf, String obf) {
|
||||
if (OBF_ENV == null) {
|
||||
throw new IllegalStateException("Obfuscation state has been accessed too early!");
|
||||
}
|
||||
if (OBF_ENV) {
|
||||
return obf;
|
||||
}
|
||||
return deobf;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
@@ -62,7 +62,7 @@ public class FieldLevelTessellatorTransformer {
|
||||
new FieldInsnNode(
|
||||
Opcodes.GETSTATIC,
|
||||
"net/minecraft/client/renderer/Tessellator",
|
||||
AngelicaTweaker.obf("instance", "field_78398_a"),
|
||||
AngelicaClientTweaker.obf("instance", "field_78398_a"),
|
||||
"Lnet/minecraft/client/renderer/Tessellator;"));
|
||||
list.add(new VarInsnNode(Opcodes.ASTORE, localIndex));
|
||||
mn.instructions.insert(list);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
import org.spongepowered.asm.lib.tree.InsnList;
|
||||
@@ -29,7 +29,7 @@ public class HUDCachingEarlyReturnTransformer {
|
||||
} else if (method.desc.endsWith("V")) {
|
||||
list.add(new InsnNode(Opcodes.RETURN));
|
||||
} else {
|
||||
AngelicaTweaker.LOGGER.warn(
|
||||
AngelicaClientTweaker.LOGGER.warn(
|
||||
"HUDCaching Conditional Return - Unknown return type: {}#{}:{}", cn.name, method.name, method.desc
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
|
||||
import org.spongepowered.asm.lib.tree.ClassNode;
|
||||
@@ -59,7 +59,7 @@ public class TileEntityNullGuardTransformer {
|
||||
AbstractInsnNode in = mn.instructions.get(i);
|
||||
if (in instanceof MethodInsnNode min) {
|
||||
if (min.getOpcode() == Opcodes.INVOKEINTERFACE
|
||||
&& min.name.equals(AngelicaTweaker.obf("getTileEntity", "func_147438_o"))
|
||||
&& min.name.equals(AngelicaClientTweaker.obf("getTileEntity", "func_147438_o"))
|
||||
&& min.owner.equals("net/minecraft/world/IBlockAccess")) {
|
||||
AbstractInsnNode castNodeAbstract = min.getNext();
|
||||
if (castNodeAbstract instanceof TypeInsnNode castNodeType) {
|
||||
@@ -77,7 +77,7 @@ public class TileEntityNullGuardTransformer {
|
||||
} else if (mn.desc.endsWith("V")) {
|
||||
list.add(new InsnNode(Opcodes.RETURN));
|
||||
} else {
|
||||
AngelicaTweaker.LOGGER.warn("TileEntityNullGuard - Unknown Return Type: {}:{}", mn.name, mn.desc);
|
||||
AngelicaClientTweaker.LOGGER.warn("TileEntityNullGuard - Unknown Return Type: {}:{}", mn.name, mn.desc);
|
||||
return;
|
||||
}
|
||||
list.add(exit);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.spongepowered.asm.lib.ClassReader;
|
||||
@@ -60,7 +60,7 @@ public class ImmersiveEngineeringTransformer implements IClassTransformer {
|
||||
MixinClassWriter cw = new MixinClassWriter(MixinClassWriter.COMPUTE_FRAMES);
|
||||
cn.accept(cw);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
AngelicaTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied ImmersiveEngineeringTransformer");
|
||||
AngelicaClientTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied ImmersiveEngineeringTransformer");
|
||||
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.spongepowered.asm.lib.ClassReader;
|
||||
@@ -47,7 +47,7 @@ public class StacksOnStacksTransformer implements IClassTransformer {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cn.accept(cw);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
AngelicaTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied StacksOnStacksTransformer");
|
||||
AngelicaClientTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied StacksOnStacksTransformer");
|
||||
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.transformers;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
|
||||
import com.gtnewhorizons.angelica.loading.shared.transformers.CeleritasBlockTransform;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
@@ -14,7 +14,7 @@ public class CeleritasBlockTransformer implements IClassTransformer {
|
||||
private final String[] exclusions;
|
||||
|
||||
public CeleritasBlockTransformer() {
|
||||
this.inner = new CeleritasBlockTransform(AngelicaTweaker.isObfEnv());
|
||||
this.inner = new CeleritasBlockTransform(AngelicaClientTweaker.isObfEnv());
|
||||
this.exclusions = inner.getTransformerExclusions();
|
||||
this.inner.setCeleritasSetting();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.transformers;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EarlyRedirectorCore;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
/**
|
||||
* A scoped redirector that only transforms classes from known-misbehaving (core)mod packages.
|
||||
@@ -13,52 +11,20 @@ import org.objectweb.asm.tree.ClassNode;
|
||||
* Those classes get missed by the late-registered {@link AngelicaRedirectorTransformer}, so their
|
||||
* GL calls permanently bypass GLSM.
|
||||
* <p>
|
||||
* This transformer is registered in the {@code AngelicaTweaker} constructor (before any
|
||||
* coremod's {@code injectData}) and is removed by {@code AngelicaLateTweaker} once the
|
||||
* full redirector is in its proper post-mixin position.
|
||||
* <p>
|
||||
* The mixin service will add this to its delegation list, but it no-ops on game classes
|
||||
* since it only targets specific mod packages.
|
||||
* Registered in {@code AngelicaClientTweaker} constructor. Removed by {@code AngelicaLateTweaker}
|
||||
* once the full redirector is in its proper post-mixin position.
|
||||
*
|
||||
* @see EcosystemNarrowRules#EARLY_REDIRECTOR_TARGETS
|
||||
*/
|
||||
public class EarlyRedirectorTransformer implements IClassTransformer {
|
||||
|
||||
private static final String[] TARGET_PACKAGES = {
|
||||
"cn.tesseract.mycelium.",
|
||||
};
|
||||
|
||||
private final AngelicaRedirector inner = new AngelicaRedirector();
|
||||
private final String[] exclusions = inner.getTransformerExclusions();
|
||||
private final EarlyRedirectorCore impl = new EarlyRedirectorCore(
|
||||
"com.gtnewhorizons.angelica.lwjgl3.",
|
||||
"com.gtnewhorizons.angelica.transform"
|
||||
);
|
||||
|
||||
@Override
|
||||
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
boolean targeted = false;
|
||||
for (String pkg : TARGET_PACKAGES) {
|
||||
if (transformedName.startsWith(pkg)) {
|
||||
targeted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!targeted) return basicClass;
|
||||
|
||||
for (String exclusion : exclusions) {
|
||||
if (transformedName.startsWith(exclusion)) return basicClass;
|
||||
}
|
||||
|
||||
if (!inner.shouldTransform(basicClass)) {
|
||||
return basicClass;
|
||||
}
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
final boolean changed = inner.transformClassNode(transformedName, cn);
|
||||
if (changed) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
return basicClass;
|
||||
return impl.transform(transformedName, basicClass);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.tweakers;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.TransformerNarrower;
|
||||
import cpw.mods.fml.relauncher.FMLRelaunchLog;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import net.minecraft.launchwrapper.ITweaker;
|
||||
@@ -27,8 +29,6 @@ public class AngelicaLateTweaker implements ITweaker {
|
||||
|
||||
/**
|
||||
* Reduces overly broad transformer exclusions that prevent AngelicaRedirector from doing its job.
|
||||
*
|
||||
* Looking at you, DragonAPI.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void narrowTransformerExclusions(LaunchClassLoader classLoader) {
|
||||
@@ -37,73 +37,14 @@ public class AngelicaLateTweaker implements ITweaker {
|
||||
exceptionsField.setAccessible(true);
|
||||
final Set<String> exceptions = (Set<String>) exceptionsField.get(classLoader);
|
||||
|
||||
if (narrowEnabled("DragonAPI") && exceptions.remove("Reika.DragonAPI.ASM")) {
|
||||
// Re-add non-GL classes; ASMCallsClient and utility classes with GL calls are left exposed
|
||||
exceptions.add("Reika.DragonAPI.ASM.Patchers");
|
||||
exceptions.add("Reika.DragonAPI.ASM.Profiling");
|
||||
exceptions.add("Reika.DragonAPI.ASM.APIStripper");
|
||||
exceptions.add("Reika.DragonAPI.ASM.ClassReparenter");
|
||||
exceptions.add("Reika.DragonAPI.ASM.DependentMethodStripper");
|
||||
exceptions.add("Reika.DragonAPI.ASM.DragonAPIClassTransformer");
|
||||
exceptions.add("Reika.DragonAPI.ASM.FMLItemBlockPatch");
|
||||
exceptions.add("Reika.DragonAPI.ASM.FluidNamePatch");
|
||||
exceptions.add("Reika.DragonAPI.ASM.InterfaceInjector");
|
||||
exceptions.add("Reika.DragonAPI.ASM.SpecialBiomePlacement");
|
||||
exceptions.add("Reika.DragonAPI.ASM.StructureLootHooks");
|
||||
FMLRelaunchLog.info("[Angelica] Narrowed Reika.DragonAPI.ASM transformer exclusion to allow GL redirection");
|
||||
}
|
||||
TransformerNarrower.narrow(exceptions, Launch.blackboard, "angelica", "Angelica",
|
||||
EcosystemNarrowRules.ALL);
|
||||
|
||||
if (narrowEnabled("Xaeros")) {
|
||||
if (exceptions.remove("xaero.common.core")) {
|
||||
exceptions.add("xaero.common.core.transformer");
|
||||
FMLRelaunchLog.info("[Angelica] Narrowed xaero.common.core transformer exclusion to allow GL redirection");
|
||||
}
|
||||
if (exceptions.remove("xaero.map.core")) {
|
||||
exceptions.add("xaero.map.core.transformer");
|
||||
FMLRelaunchLog.info("[Angelica] Narrowed xaero.map.core transformer exclusion to allow GL redirection");
|
||||
}
|
||||
}
|
||||
|
||||
if (narrowEnabled("AdvancedLightsabers") && exceptions.remove("com.fiskmods.lightsabers.asm")) {
|
||||
exceptions.add("com.fiskmods.lightsabers.asm.ALLoadingPlugin");
|
||||
exceptions.add("com.fiskmods.lightsabers.asm.ASMHooks");
|
||||
exceptions.add("com.fiskmods.lightsabers.asm.transformers");
|
||||
FMLRelaunchLog.info("[Angelica] Narrowed com.fiskmods.lightsabers.asm transformer exclusion to allow GL redirection");
|
||||
}
|
||||
|
||||
if (narrowEnabled("Alfheim") && exceptions.remove("alfheim.common.core.asm.hook")) {
|
||||
exceptions.add("alfheim.common.core.asm.hook.AlfheimFieldHookHandler");
|
||||
exceptions.add("alfheim.common.core.asm.hook.AlfheimHPHooks");
|
||||
exceptions.add("alfheim.common.core.asm.hook.Botania18AndUpBackport");
|
||||
exceptions.add("alfheim.common.core.asm.hook.ElementalDamageAdapter");
|
||||
|
||||
exceptions.add("alfheim.common.core.asm.hook.integration");
|
||||
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.FurnaceExtender");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.ItemAuraRingExtender");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.ItemLensExtender");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.ItemTwigWandExtender");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.LightRelayExtender");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.PureDaisyExtender");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.QuartzExtender");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.RelicHooks");
|
||||
exceptions.add("alfheim.common.core.asm.hook.extender.SparkExtender");
|
||||
|
||||
exceptions.add("alfheim.common.core.asm.hook.fixes.BotaniaGlowingRenderFixes");
|
||||
exceptions.add("alfheim.common.core.asm.hook.fixes.CorporeaInputFix");
|
||||
exceptions.add("alfheim.common.core.asm.hook.fixes.GodAttributesHooks");
|
||||
exceptions.add("alfheim.common.core.asm.hook.fixes.RecipeAncientWillsFix");
|
||||
FMLRelaunchLog.info("[Angelica] Narrowed alfheim.common.core.asm.hook transformer exclusion to allow GL redirection");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FMLRelaunchLog.warning("[Angelica] Failed to narrow transformer exclusions: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean narrowEnabled(String mod) {
|
||||
return !Boolean.FALSE.equals(Launch.blackboard.get("angelica.narrow." + mod));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLaunchTarget() {
|
||||
return null;
|
||||
|
||||
@@ -15,6 +15,7 @@ public class AngelicaRfbPlugin implements RfbPlugin {
|
||||
@Override
|
||||
public void onConstruction(@NotNull PluginContext ctx) {
|
||||
Launch.blackboard.put("angelica.rfbPluginLoaded", Boolean.TRUE);
|
||||
Launch.classLoader.addClassLoaderExclusion("com.gtnewhorizons.angelica.glsm.redirect.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,7 +37,14 @@ public enum Mixins implements IMixins {
|
||||
, "angelica.MixinMinecraft_FrameHook"
|
||||
, "angelica.MixinMinecraftServer"
|
||||
, "angelica.bugfixes.MixinItemRenderer_EdgeDepth"
|
||||
, "angelica.bugfixes.MixinModelCreeper_AuraBodyInflate"
|
||||
, "angelica.bugfixes.MixinModelWither_ArmorCentering"
|
||||
, "angelica.bugfixes.MixinRenderBlocks_CrossedSquaresNormal"
|
||||
, "angelica.bugfixes.MixinRenderCreeper_AuraDepth"
|
||||
, "angelica.bugfixes.MixinRenderGlobal_DeferredEntityOverlay"
|
||||
, "angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_DeferredEntityOverlay"
|
||||
, "angelica.bugfixes.MixinRenderWither_ArmorCentering"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_EyeDepth"
|
||||
, "angelica.debug.MixinMinecraft_FPSCap"
|
||||
, "angelica.ffp.MixinTessellator_CoreProfile"
|
||||
@@ -237,6 +244,7 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins(
|
||||
"shaders.MixinDroppedItemGlintEdges"
|
||||
, "shaders.MixinHeldItemGlintEdges"
|
||||
, "shaders.MixinEntityPickupFX"
|
||||
, "shaders.MixinEntityRenderer"
|
||||
, "shaders.MixinGuiIngameForge"
|
||||
@@ -249,6 +257,7 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinRenderEntityFlame"
|
||||
, "shaders.MixinRendererLivingEntity"
|
||||
, "shaders.MixinRenderGlobal"
|
||||
, "shaders.AccessorEntityHorse"
|
||||
, "shaders.MixinRenderHorse"
|
||||
, "shaders.MixinRenderItem"
|
||||
, "shaders.MixinRenderNameTag"
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.gtnewhorizons.angelica.proxy;
|
||||
|
||||
import biomesoplenty.api.content.BOPCBlocks;
|
||||
import com.google.common.base.Objects;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VAOManager;
|
||||
import com.gtnewhorizons.angelica.commands.AngelicaCommand;
|
||||
import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.bettercrashes.BetterCrashesCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.ConfigMigrator;
|
||||
import com.gtnewhorizons.angelica.debug.F3Direction;
|
||||
import com.gtnewhorizons.angelica.debug.FrametimeGraph;
|
||||
import com.gtnewhorizons.angelica.debug.TPSGraph;
|
||||
@@ -16,7 +19,7 @@ import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
|
||||
import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
|
||||
import com.gtnewhorizons.angelica.render.CloudRenderer;
|
||||
import com.gtnewhorizons.angelica.rendering.AngelicaBlockSafetyRegistry;
|
||||
@@ -25,6 +28,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ChunkTaskRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.DefaultChunkTaskProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ThreadedChunkTaskProvider;
|
||||
import com.gtnewhorizons.angelica.utils.AnimationMode;
|
||||
import com.gtnewhorizons.angelica.utils.ManagedEnum;
|
||||
import com.gtnewhorizons.angelica.zoom.Zoom;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
@@ -39,17 +44,15 @@ import jss.notfine.core.Settings;
|
||||
import jss.notfine.gui.GuiCustomMenu;
|
||||
import jss.notfine.gui.NotFineGameOptionPages;
|
||||
import me.flashyreese.mods.reeses_sodium_options.client.gui.ReeseSodiumVideoOptionsScreen;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.client.IrisDebugScreenHandler;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiVideoSettings;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.Direction;
|
||||
@@ -65,29 +68,41 @@ import net.minecraftforge.client.event.GuiScreenEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
public final class ClientProxy extends CommonProxy {
|
||||
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
final FrametimeGraph frametimeGraph = new FrametimeGraph();
|
||||
final TPSGraph tpsGraph = new TPSGraph();
|
||||
public static BlockError blockError;
|
||||
public static final ManagedEnum<AnimationMode> animationsMode = new ManagedEnum<>(AnimationMode.VISIBLE_ONLY);
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
private static SodiumGameOptions CONFIG;
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
private final FrametimeGraph frametimeGraph = new FrametimeGraph();
|
||||
private final TPSGraph tpsGraph = new TPSGraph();
|
||||
public static Block bopGrass;
|
||||
|
||||
public static SodiumGameOptions options() {
|
||||
if (CONFIG == null) {
|
||||
CONFIG = SodiumGameOptions.load(ConfigMigrator.handleConfigMigration("angelica-options.json"));
|
||||
}
|
||||
return CONFIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
ModStatus.preInit();
|
||||
super.preInit(event);
|
||||
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
ModelRegistry.registerModid(MOD_ID);
|
||||
blockError = new BlockError();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -138,7 +153,6 @@ public class ClientProxy extends CommonProxy {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
|
||||
if (ModStatus.isBetterCrashesLoaded) {
|
||||
BetterCrashesCompat.init();
|
||||
}
|
||||
@@ -150,7 +164,7 @@ public class ClientProxy extends CommonProxy {
|
||||
}
|
||||
|
||||
// Register debug commands in dev environment only
|
||||
if (!AngelicaTweaker.isObfEnv()) {
|
||||
if (!AngelicaClientTweaker.isObfEnv()) {
|
||||
ClientCommandHandler.instance.registerCommand(new AngelicaCommand());
|
||||
}
|
||||
}
|
||||
@@ -170,6 +184,9 @@ public class ClientProxy extends CommonProxy {
|
||||
LOGGER.error("Could not replace LOTR handle render code with thread safe version");
|
||||
}
|
||||
}
|
||||
if(ModStatus.isBOPLoaded) {
|
||||
bopGrass = BOPCBlocks.newBopGrass;
|
||||
}
|
||||
}
|
||||
|
||||
float lastIntegratedTickTime;
|
||||
|
||||
@@ -10,8 +10,7 @@ import cpw.mods.fml.common.registry.GameRegistry;
|
||||
public class CommonProxy {
|
||||
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
|
||||
if (AngelicaConfig.enableTestBlocks){
|
||||
if (AngelicaConfig.enableTestBlocks) {
|
||||
GameRegistry.registerBlock(new BlockTest(), "test_block");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.coderbot.iris.uniforms.EntityIdHelper;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Defers entity overlay rendering (auras, armor effects, etc.) to after all entities have been drawn.
|
||||
* This lets us fix z-fighting between overlay's own coplanar faces by disabling the depth mask,
|
||||
* and ensures the overlay composites correctly on top of all opaque geometry.
|
||||
*
|
||||
* Used by both the charged creeper aura and the Wither armor overlay.
|
||||
*
|
||||
* The deferred render replays shouldRenderPass on the original renderer instance so that any
|
||||
* mod mixins targeting that method still fire during the deferred pass.
|
||||
*/
|
||||
public class DeferredEntityOverlay {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ShouldRenderPassFn {
|
||||
int invoke(EntityLivingBase entity, int pass, float partialTick);
|
||||
}
|
||||
|
||||
private static final int INITIAL_CAPACITY = 4;
|
||||
private static final List<DeferredEntry> deferred = new ArrayList<>(INITIAL_CAPACITY);
|
||||
private static final List<DeferredEntry> pool = new ArrayList<>(INITIAL_CAPACITY);
|
||||
|
||||
private static final FloatBuffer MATRIX_BUF = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
// Set by the shouldRenderPass inject, consumed by the doRender WrapOperation.
|
||||
@Getter
|
||||
private static boolean overlayPassActive = false;
|
||||
|
||||
// Set during deferred flush to prevent re-deferring when shouldRenderPass is replayed.
|
||||
@Getter
|
||||
private static boolean replaying = false;
|
||||
|
||||
// Pending context from the shouldRenderPass inject, consumed by deferRender.
|
||||
private static ShouldRenderPassFn pendingShouldRenderPass;
|
||||
private static RendererLivingEntity pendingRenderer;
|
||||
private static EntityLivingBase pendingEntity;
|
||||
private static float pendingPartialTick;
|
||||
|
||||
/**
|
||||
* Called from shouldRenderPass HEAD injects. Marks that the upcoming renderPassModel.render()
|
||||
* should be deferred. If a previous mark was not consumed (shouldRenderPass returned <= 0,
|
||||
* so render() was never called).
|
||||
*/
|
||||
public static void markOverlayPass(ShouldRenderPassFn shouldRenderPass, RendererLivingEntity renderer,
|
||||
EntityLivingBase entity, float partialTick) {
|
||||
overlayPassActive = true;
|
||||
pendingShouldRenderPass = shouldRenderPass;
|
||||
pendingRenderer = renderer;
|
||||
pendingEntity = entity;
|
||||
pendingPartialTick = partialTick;
|
||||
}
|
||||
|
||||
public static void clearStaleOverlayFlag() {
|
||||
overlayPassActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the WrapOperation on renderPassModel.render() in doRender.
|
||||
* Captures the modelview matrix and animation params, skipping the actual render.
|
||||
*/
|
||||
public static void deferRender(float limbSwing, float limbSwingAmount, float ageInTicks,
|
||||
float headYaw, float headPitch, float scale) {
|
||||
overlayPassActive = false;
|
||||
|
||||
DeferredEntry entry = acquireEntry();
|
||||
entry.set(pendingShouldRenderPass, pendingRenderer, pendingEntity, pendingPartialTick,
|
||||
limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch, scale);
|
||||
|
||||
MATRIX_BUF.clear();
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MATRIX_BUF);
|
||||
MATRIX_BUF.get(entry.matrix);
|
||||
|
||||
deferred.add(entry);
|
||||
|
||||
pendingShouldRenderPass = null;
|
||||
pendingRenderer = null;
|
||||
pendingEntity = null;
|
||||
}
|
||||
|
||||
private static DeferredEntry acquireEntry() {
|
||||
int size = pool.size();
|
||||
if (size > 0) {
|
||||
return pool.remove(size - 1);
|
||||
}
|
||||
return new DeferredEntry();
|
||||
}
|
||||
|
||||
private static void recycle() {
|
||||
for (DeferredEntry entry : deferred) {
|
||||
entry.shouldRenderPass = null;
|
||||
entry.renderer = null;
|
||||
entry.entity = null;
|
||||
pool.add(entry);
|
||||
}
|
||||
deferred.clear();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
recycle();
|
||||
overlayPassActive = false;
|
||||
replaying = false;
|
||||
}
|
||||
|
||||
public static void renderAll() {
|
||||
if (deferred.isEmpty()) return;
|
||||
|
||||
GbufferPrograms.beginEntities();
|
||||
try {
|
||||
for (DeferredEntry entry : deferred) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(EntityIdHelper.getEntityId(entry.entity));
|
||||
renderOverlay(entry);
|
||||
}
|
||||
} finally {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntity(-1);
|
||||
GbufferPrograms.endEntities();
|
||||
}
|
||||
recycle();
|
||||
}
|
||||
|
||||
private static void renderOverlay(DeferredEntry entry) {
|
||||
EntityLivingBase entity = entry.entity;
|
||||
|
||||
// Restore the entity's lightmap coords
|
||||
int brightness = entity.getBrightnessForRender(entry.partialTick);
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit,
|
||||
brightness % 65536, (float) brightness / 65536);
|
||||
|
||||
// Restore the saved modelview matrix
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
boolean depthMaskDisabled = false;
|
||||
try {
|
||||
MATRIX_BUF.clear();
|
||||
MATRIX_BUF.put(entry.matrix);
|
||||
MATRIX_BUF.flip();
|
||||
GLStateManager.glLoadMatrix(MATRIX_BUF);
|
||||
|
||||
// Replay shouldRenderPass pass 1
|
||||
replaying = true;
|
||||
int result = entry.shouldRenderPass.invoke(entity, 1, entry.partialTick);
|
||||
if (result > 0) {
|
||||
// Disable depth writes so coplanar faces don't z-fight each other
|
||||
GLStateManager.glDepthMask(false);
|
||||
depthMaskDisabled = true;
|
||||
|
||||
// Disable backface culling so both sides of the overlay shell are visible
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
|
||||
// Render using the model set by shouldRenderPass
|
||||
entry.renderer.renderPassModel.setLivingAnimations(entity,
|
||||
entry.limbSwing, entry.limbSwingAmount, entry.partialTick);
|
||||
|
||||
entry.renderer.renderPassModel.render(entity,
|
||||
entry.limbSwing, entry.limbSwingAmount, entry.ageInTicks,
|
||||
entry.headYaw, entry.headPitch, entry.scale);
|
||||
}
|
||||
|
||||
// Replay shouldRenderPass pass 2
|
||||
entry.shouldRenderPass.invoke(entity, 2, entry.partialTick);
|
||||
} finally {
|
||||
replaying = false;
|
||||
if (depthMaskDisabled) {
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
}
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeferredEntry {
|
||||
final float[] matrix = new float[16];
|
||||
ShouldRenderPassFn shouldRenderPass;
|
||||
RendererLivingEntity renderer;
|
||||
EntityLivingBase entity;
|
||||
float partialTick;
|
||||
float limbSwing;
|
||||
float limbSwingAmount;
|
||||
float ageInTicks;
|
||||
float headYaw;
|
||||
float headPitch;
|
||||
float scale;
|
||||
|
||||
void set(ShouldRenderPassFn shouldRenderPass, RendererLivingEntity renderer,
|
||||
EntityLivingBase entity, float partialTick,
|
||||
float limbSwing, float limbSwingAmount, float ageInTicks,
|
||||
float headYaw, float headPitch, float scale) {
|
||||
this.shouldRenderPass = shouldRenderPass;
|
||||
this.renderer = renderer;
|
||||
this.entity = entity;
|
||||
this.partialTick = partialTick;
|
||||
this.limbSwing = limbSwing;
|
||||
this.limbSwingAmount = limbSwingAmount;
|
||||
this.ageInTicks = ageInTicks;
|
||||
this.headYaw = headYaw;
|
||||
this.headPitch = headPitch;
|
||||
this.scale = scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import net.minecraft.client.model.ModelBox;
|
||||
import net.minecraft.client.model.ModelHorse;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Horse model with all boxes inflated outward, used as a separate armor layer.
|
||||
* Extends ModelHorse to inherit all animation and render logic. The constructor
|
||||
* builds the default geometry, then we rebuild every box with inflate applied.
|
||||
* This mirrors how modern MC uses a separate ModelLayers.HORSE_ARMOR.
|
||||
*/
|
||||
public class ModelHorseArmor extends ModelHorse {
|
||||
|
||||
public ModelHorseArmor(float inflate) {
|
||||
super();
|
||||
for (ModelRenderer renderer : this.boxList) {
|
||||
List<ModelBox> originalBoxes = new ArrayList<>(renderer.cubeList);
|
||||
renderer.cubeList.clear();
|
||||
for (ModelBox box : originalBoxes) {
|
||||
int width = Math.round(box.posX2 - box.posX1);
|
||||
int height = Math.round(box.posY2 - box.posY1);
|
||||
int depth = Math.round(box.posZ2 - box.posZ1);
|
||||
renderer.addBox(box.posX1, box.posY1, box.posZ1, width, height, depth, inflate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,8 @@ public interface StateAwareTessellator {
|
||||
void angelica$setCeleritasMeshing(boolean active);
|
||||
|
||||
int[] angelica$getVertexStates();
|
||||
|
||||
int[] angelica$getShaderOverrideBlockIds();
|
||||
|
||||
void angelica$setShaderOverrideBlockId(short blockId);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
public class WitherArmorState {
|
||||
public static boolean pendingInflate = false;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
|
||||
import com.gtnewhorizons.angelica.api.TintComputer;
|
||||
import com.gtnewhorizons.angelica.api.TintRegistry;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightSource;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.BlockRenderContext;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.IrisExtendedChunkVertexEncoder;
|
||||
@@ -99,7 +99,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
|
||||
private Material selectMaterial(Material material, TextureAtlasSprite sprite, boolean isShaderPackOverride) {
|
||||
// Don't apply transparency-based optimization when shader pack explicitly overrides the material
|
||||
if (!AngelicaMod.options().performance.useRenderPassOptimization || isShaderPackOverride) {
|
||||
if (!ClientProxy.options().performance.useRenderPassOptimization || isShaderPackOverride) {
|
||||
return material;
|
||||
}
|
||||
if (sprite != null && sprite.getClass() == TextureAtlasSprite.class && !sprite.hasAnimationMetadata()) {
|
||||
@@ -115,6 +115,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void copyRawBuffer(int[] rawBuffer, int vertexCount, int[] vertexStates,
|
||||
int[] shaderOverrideBlockIds,
|
||||
ChunkBuildBuffers buffers, Material material, boolean isShaderPackOverride,
|
||||
boolean blockAllowsSmoothLighting) {
|
||||
if (vertexCount == 0) {
|
||||
@@ -129,7 +130,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
|
||||
final boolean hasDynamicLights = chunkLightSources != null && !chunkLightSources.isEmpty();
|
||||
final boolean separateAo = BlockRenderingSettings.INSTANCE.shouldUseSeparateAo();
|
||||
final boolean celeritasSmoothLighting = AngelicaMod.options().quality.useCeleritasSmoothLighting;
|
||||
final boolean celeritasSmoothLighting = ClientProxy.options().quality.useCeleritasSmoothLighting;
|
||||
final boolean shaderActive = IrisApi.getInstance().isShaderPackInUse();
|
||||
final boolean useAoCalculation = lightPipelineReady && (separateAo || celeritasSmoothLighting || shaderActive);
|
||||
final boolean shouldApplyDiffuse = !BlockRenderingSettings.INSTANCE.shouldDisableDirectionalShading();
|
||||
@@ -145,6 +146,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
final int worldY = originY + blockY;
|
||||
final int worldZ = originZ + blockZ;
|
||||
|
||||
final short originalBlockId = blockRenderContext.blockId;
|
||||
|
||||
int stateIdx = 0;
|
||||
int facesAtBaseMaterial = 0;
|
||||
|
||||
@@ -239,6 +242,9 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
}
|
||||
final var builder = buffers.get(correctMaterial);
|
||||
|
||||
final int shaderOverrideBlockId = shaderOverrideBlockIds[quadIdx * 4];
|
||||
blockRenderContext.blockId = shaderOverrideBlockId != -1 ? (short) shaderOverrideBlockId : originalBlockId;
|
||||
|
||||
if (correctMaterial != material && builder.getEncoder() instanceof IrisExtendedChunkVertexEncoder iris) {
|
||||
iris.setContext(blockRenderContext);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.crash.CrashReportCategory;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.ReportedException;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
|
||||
@@ -139,7 +140,20 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
if (block.hasTileEntity(meta)) {
|
||||
final TileEntity tileEntity = region.getTileEntity(x, y, z);
|
||||
if (tileEntity != null && TileEntityRendererDispatcher.instance.hasSpecialRenderer(tileEntity)) {
|
||||
(TileEntityRenderBoundsRegistry.isAlwaysInfiniteExtent(tileEntity) ? renderData.globalBlockEntities : renderData.culledBlockEntities).add(tileEntity);
|
||||
final boolean isGlobal;
|
||||
if (TileEntityRenderBoundsRegistry.isAlwaysInfiniteExtent(tileEntity)) {
|
||||
isGlobal = true;
|
||||
} else {
|
||||
final AxisAlignedBB aabb = tileEntity.getRenderBoundingBox();
|
||||
if (aabb != null) {
|
||||
final int secMinX = x & ~15, secMinY = y & ~15, secMinZ = z & ~15;
|
||||
isGlobal = aabb.minX < secMinX || aabb.minY < secMinY || aabb.minZ < secMinZ
|
||||
|| aabb.maxX > secMinX + 16 || aabb.maxY > secMinY + 16 || aabb.maxZ > secMinZ + 16;
|
||||
} else {
|
||||
isGlobal = false;
|
||||
}
|
||||
}
|
||||
(isGlobal ? renderData.globalBlockEntities : renderData.culledBlockEntities).add(tileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,6 +270,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
&& block.getLightValue() == 0; // does not emit real block light
|
||||
buildContext.copyRawBuffer(tessellator.rawBuffer, tessellator.vertexCount,
|
||||
((StateAwareTessellator)tessellator).angelica$getVertexStates(),
|
||||
((StateAwareTessellator)tessellator).angelica$getShaderOverrideBlockIds(),
|
||||
buffers, passMaterial, isShaderPackOverride, blockAllowsSmoothLighting);
|
||||
tessellator.reset();
|
||||
tessellator.isDrawing = false;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
@@ -30,7 +31,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
}
|
||||
|
||||
private static MultiDrawEmitter createEmitter() {
|
||||
MultiDrawMode mode = AngelicaMod.options().advanced.multiDrawMode;
|
||||
MultiDrawMode mode = ClientProxy.options().advanced.multiDrawMode;
|
||||
|
||||
if (mode == MultiDrawMode.INDIRECT) {
|
||||
boolean supported = GLStateManager.capabilities != null && (GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_multi_draw_indirect);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.RenderSectionManagerAccessor;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.AngelicaRenderQueue;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ChunkTaskProvider;
|
||||
@@ -87,7 +87,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
|
||||
@Override
|
||||
protected AsyncOcclusionMode getAsyncOcclusionMode() {
|
||||
return AngelicaMod.options().performance.asyncOcclusionMode;
|
||||
return ClientProxy.options().performance.asyncOcclusionMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,7 +97,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
|
||||
@Override
|
||||
protected boolean useFogOcclusion() {
|
||||
return AngelicaMod.options().performance.useFogOcclusion && !IrisShaderProviderHolder.isActive();
|
||||
return ClientProxy.options().performance.useFogOcclusion && !IrisShaderProviderHolder.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,7 +113,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return AngelicaMod.options().performance.useOcclusionCulling;
|
||||
return ClientProxy.options().performance.useOcclusionCulling;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,7 +152,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
|
||||
@Override
|
||||
protected boolean allowImportantRebuilds() {
|
||||
return !AngelicaMod.options().performance.alwaysDeferChunkUpdates;
|
||||
return !ClientProxy.options().performance.alwaysDeferChunkUpdates;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import org.embeddedt.embeddium.impl.gl.device.GLRenderDevice;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
|
||||
|
||||
public class CeleritasSetup {
|
||||
private static boolean initialized = false;
|
||||
@@ -12,7 +12,7 @@ public class CeleritasSetup {
|
||||
public static void ensureInitialized() {
|
||||
if (!initialized && AngelicaConfig.enableCeleritas) {
|
||||
GLRenderDevice.VANILLA_STATE_RESETTER = () -> GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
LOGGER.debug("Celeritas init");
|
||||
AngelicaClientTweaker.LOGGER.debug("Celeritas init");
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderingState;
|
||||
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
@@ -52,10 +52,10 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
private static final double MAX_ENTITY_CHECK_VOLUME = 16 * 16 * 16 * 15;
|
||||
|
||||
// For sorting transparent TESRs
|
||||
private RenderSectionOrderer renderSectionOrderer = new RenderSectionOrderer();
|
||||
private TileEntityOrderer tileEntityOrderer = new TileEntityOrderer();
|
||||
private ArrayList<RenderSection> sortedRenderSections = new ArrayList<>();
|
||||
private ArrayList<TileEntity> sortedTileEntities = new ArrayList<>();
|
||||
private final RenderSectionOrderer renderSectionOrderer = new RenderSectionOrderer();
|
||||
private final TileEntityOrderer tileEntityOrderer = new TileEntityOrderer();
|
||||
private final ArrayList<RenderSection> sortedRenderSections = new ArrayList<>();
|
||||
private final ArrayList<TileEntity> sortedTileEntities = new ArrayList<>();
|
||||
|
||||
private CeleritasWorldRenderer(Minecraft mc) {
|
||||
// Private constructor for singleton
|
||||
@@ -92,6 +92,10 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
@Override
|
||||
protected void unloadWorld() {
|
||||
DynamicLights.setActiveRenderer(null);
|
||||
this.sortedRenderSections.clear();
|
||||
this.sortedTileEntities.clear();
|
||||
ShadowRenderer.visibleTileEntities.clear();
|
||||
ShadowRenderer.globalTileEntities.clear();
|
||||
super.unloadWorld();
|
||||
}
|
||||
|
||||
@@ -127,7 +131,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
}
|
||||
}
|
||||
|
||||
if (AngelicaMod.options().performance.useCompactVertexFormat) {
|
||||
if (ClientProxy.options().performance.useCompactVertexFormat) {
|
||||
return ChunkMeshFormats.COMPACT;
|
||||
}
|
||||
return ChunkMeshFormats.VANILLA_LIKE;
|
||||
@@ -155,7 +159,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
|
||||
renderSectionManager.setCameraPosition(transform.x, transform.y, transform.z);
|
||||
|
||||
this.useEntityCulling = AngelicaMod.options().performance.useEntityCulling;
|
||||
this.useEntityCulling = ClientProxy.options().performance.useEntityCulling;
|
||||
|
||||
super.setupTerrain(viewport, cameraState, frame, spectator, updateChunksImmediately);
|
||||
|
||||
@@ -313,7 +317,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
public int renderBlockEntities(float partialTicks) {
|
||||
final int pass = MinecraftForgeClient.getRenderPass();
|
||||
teRenderContext.set(partialTicks, pass);
|
||||
if (pass == 0 || !AngelicaMod.options().performance.translucencySorting) {
|
||||
if (pass == 0 || !ClientProxy.options().performance.translucencySorting) {
|
||||
return super.renderBlockEntities(teRenderContext);
|
||||
}
|
||||
int count = 0;
|
||||
|
||||
@@ -79,13 +79,13 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
|
||||
|
||||
baseEncoder.write(ptr, material, vertex, sectionIndex);
|
||||
|
||||
// Per-vertex: renderType, midBlock, lightValue
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET + 2, ctx.renderType);
|
||||
// Per-vertex: mc_Entity (packed blockId + renderType), midBlock, lightValue
|
||||
memPutInt(ptr + MC_ENTITY_OFFSET, ((ctx.blockId + 1) << 1) | (ctx.renderType & 1));
|
||||
final int midBlock = ExtendedDataHelper.computeMidBlock(vertex.x, vertex.y, vertex.z, ctx.localPosX, ctx.localPosY, ctx.localPosZ);
|
||||
memPutInt(ptr + MID_BLOCK_OFFSET, midBlock);
|
||||
memPutByte(ptr + MID_BLOCK_OFFSET + 3, ctx.lightValue);
|
||||
|
||||
// Per-quad: midTexCoord, blockId, normal, tangent
|
||||
// Per-quad: midTexCoord, normal, tangent
|
||||
if (vertexCount == 4) {
|
||||
vertexCount = 0;
|
||||
|
||||
@@ -98,11 +98,6 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
|
||||
memPutInt(ptr + MID_TEX_OFFSET - STRIDE * 2, midUV);
|
||||
memPutInt(ptr + MID_TEX_OFFSET - STRIDE * 3, midUV);
|
||||
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET, ctx.blockId);
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET - STRIDE, ctx.blockId);
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET - STRIDE * 2, ctx.blockId);
|
||||
memPutShort(ptr + MC_ENTITY_OFFSET - STRIDE * 3, ctx.blockId);
|
||||
|
||||
quad.setup(ptr, STRIDE);
|
||||
NormalHelper.computeFaceNormal(normal, quad);
|
||||
final int packedNormal = NormI8.pack(normal);
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexType;
|
||||
* mc_midTexCoord ushort[2] - quad center UV (average of 4 vertices)
|
||||
* at_tangent byte[4] - tangent vector, normalized
|
||||
* iris_Normal byte[3]+1 - face normal, normalized
|
||||
* mc_Entity short[2] - (blockId, renderType)
|
||||
* mc_Entity uint - packed as ((blockId + 1) << 1) | (renderType & 1)
|
||||
* at_midBlock byte[4] - (xyz offset from block center, lightValue)
|
||||
*/
|
||||
public class IrisExtendedChunkVertexType implements ChunkVertexType {
|
||||
@@ -25,7 +25,7 @@ public class IrisExtendedChunkVertexType implements ChunkVertexType {
|
||||
.addElement("mc_midTexCoord", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.UNSIGNED_SHORT, 2, false, false)
|
||||
.addElement("at_tangent", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.BYTE, 4, true, false)
|
||||
.addElement("iris_Normal", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.BYTE, 3, true, false)
|
||||
.addElement("mc_Entity", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.SHORT, 2, false, false)
|
||||
.addElement("mc_Entity", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.UNSIGNED_INT, 1, false, true)
|
||||
.addElement("at_midBlock", GlVertexFormat.NEXT_ALIGNED_POINTER, GlVertexAttributeFormat.BYTE, 4, false, false)
|
||||
.build();
|
||||
|
||||
|
||||
@@ -43,6 +43,20 @@ public class CTMUtils {
|
||||
private static Overrides newOverrides = null;
|
||||
private static TileLoader tileLoader;
|
||||
|
||||
private static final ThreadLocal<TileOverrideImpl.CTMCompact> CURRENT_COMPACT = new ThreadLocal<>();
|
||||
|
||||
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact) {
|
||||
CURRENT_COMPACT.set(compact);
|
||||
}
|
||||
|
||||
public static TileOverrideImpl.CTMCompact getCurrentCompact() {
|
||||
return CURRENT_COMPACT.get();
|
||||
}
|
||||
|
||||
public static void clearCurrentCompact() {
|
||||
CURRENT_COMPACT.remove();
|
||||
}
|
||||
|
||||
private static final StampedLock lock = new StampedLock();
|
||||
|
||||
private static class StateAndIterator {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
|
||||
public class CompactConnectingCtmProperties {
|
||||
|
||||
private final boolean innerSeams;
|
||||
private final String orientation;
|
||||
private final Int2IntMap tileReplacementMap = new Int2IntOpenHashMap();
|
||||
|
||||
public CompactConnectingCtmProperties(PropertiesFile properties) {
|
||||
this.innerSeams = properties.getBoolean("innerSeams", false);
|
||||
this.orientation = properties.getString("orientation", "none").toLowerCase();
|
||||
|
||||
for (int i = 0; i < 47; i++) {
|
||||
String val = properties.getString("ctm." + i, "");
|
||||
if (!val.isEmpty()) {
|
||||
try {
|
||||
int tileIdx = Integer.parseInt(val.trim());
|
||||
tileReplacementMap.put(i, tileIdx);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getInnerSeams() {
|
||||
return innerSeams;
|
||||
}
|
||||
|
||||
public String getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public Int2IntMap getTileReplacementMap() {
|
||||
return tileReplacementMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
public final class CompactCtmQuadProcessor {
|
||||
|
||||
private static final int LEFT = 0;
|
||||
private static final int UP_LEFT = 1;
|
||||
private static final int UP = 2;
|
||||
private static final int UP_RIGHT = 3;
|
||||
private static final int RIGHT = 4;
|
||||
private static final int DOWN_RIGHT = 5;
|
||||
private static final int DOWN = 6;
|
||||
private static final int DOWN_LEFT = 7;
|
||||
|
||||
private static final int SPRITE_DEFAULT = 0;
|
||||
private static final int SPRITE_ALL = 1;
|
||||
private static final int SPRITE_HORIZONTAL = 2;
|
||||
private static final int SPRITE_VERTICAL = 3;
|
||||
private static final int SPRITE_MIXED = 4;
|
||||
|
||||
private static final int[] CTM_47_BY_CONNECTIONS = {
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
16,18,16,18,6,46,6,21,16,18,16,18,28,9,28,22,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
37,40,37,40,30,8,30,34,37,40,37,40,25,23,25,45,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
16,42,16,42,6,20,6,10,16,42,16,42,28,35,28,44,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
37,38,37,38,30,11,30,32,37,38,37,38,25,33,25,26
|
||||
};
|
||||
|
||||
/**
|
||||
* Face-local basis:
|
||||
* right = increasing U on the face
|
||||
* down = increasing V on the face
|
||||
*/
|
||||
private static final FaceBasis[] FACE_BASIS = {
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 0: Y-
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 1: Y+
|
||||
new FaceBasis(-1, 0, 0, 0, -1, 0), // 2: Z-
|
||||
new FaceBasis( 1, 0, 0, 0, -1, 0), // 3: Z+
|
||||
new FaceBasis( 0, 0, 1, 0, -1, 0), // 4: X-
|
||||
new FaceBasis( 0, 0, -1, 0, -1, 0) // 5: X+
|
||||
};
|
||||
|
||||
/**
|
||||
* Neighbor offsets in face-local coordinates:
|
||||
* 0 = left, 1 = up-left, 2 = up, 3 = up-right,
|
||||
* 4 = right, 5 = down-right, 6 = down, 7 = down-left
|
||||
*/
|
||||
private static final int[] RIGHT_FACTOR = {-1, -1, 0, 1, 1, 1, 0, -1};
|
||||
private static final int[] DOWN_FACTOR = { 0, -1, -1, -1, 0, 1, 1, 1};
|
||||
|
||||
/**
|
||||
* Quadrant side references:
|
||||
* 0 = top-left
|
||||
* 1 = top-right
|
||||
* 2 = bottom-right
|
||||
* 3 = bottom-left
|
||||
*/
|
||||
private static final int[] QUAD_A = {LEFT, UP, RIGHT, DOWN};
|
||||
private static final int[] QUAD_B = {UP, RIGHT, DOWN, LEFT};
|
||||
private static final int[] QUAD_DIAG = {UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT};
|
||||
|
||||
private final IIcon[] sprites;
|
||||
private final CompactConnectingCtmProperties props;
|
||||
|
||||
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props) {
|
||||
this.sprites = new IIcon[5];
|
||||
if (sprites != null) {
|
||||
System.arraycopy(sprites, 0, this.sprites, 0, Math.min(sprites.length, this.sprites.length));
|
||||
}
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public boolean processFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon origIcon) {
|
||||
int connections = CtmConnectionHelper.getConnections(world, x, y, z, face, props.getInnerSeams());
|
||||
|
||||
int ctmIndex = CTM_47_BY_CONNECTIONS[connections & 0xFF];
|
||||
IIcon replacement = getReplacementIcon(ctmIndex);
|
||||
if (replacement != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connections == 0 || connections == 0xFF) {
|
||||
IIcon sprite = sprites[getCompactSpriteIndex(connections)];
|
||||
if (sprite != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, sprite);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
renderSplitFace(rb, world, x, y, z, face, connections);
|
||||
return true;
|
||||
}
|
||||
|
||||
private IIcon getReplacementIcon(int ctmIndex) {
|
||||
Int2IntMap map = props.getTileReplacementMap();
|
||||
if (map == null || !map.containsKey(ctmIndex)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int spriteIndex = map.get(ctmIndex);
|
||||
if (spriteIndex < 0 || spriteIndex >= sprites.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprites[spriteIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact 5-sprite variant:
|
||||
* 0 = default / isolated / mixed
|
||||
* 1 = all neighbors
|
||||
* 2 = horizontal line
|
||||
* 3 = vertical line
|
||||
*/
|
||||
private int getCompactSpriteIndex(int connections) {
|
||||
if (connections == 0xFF) {
|
||||
return SPRITE_ALL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << LEFT) | (1 << RIGHT))) == ((1 << LEFT) | (1 << RIGHT))) {
|
||||
return SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << UP) | (1 << DOWN))) == ((1 << UP) | (1 << DOWN))) {
|
||||
return SPRITE_VERTICAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
private void renderSplitFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, int connections) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
|
||||
double minX = rb.renderMinX;
|
||||
double minY = rb.renderMinY;
|
||||
double minZ = rb.renderMinZ;
|
||||
double maxX = rb.renderMaxX;
|
||||
double maxY = rb.renderMaxY;
|
||||
double maxZ = rb.renderMaxZ;
|
||||
|
||||
for (int q = 0; q < 4; q++) {
|
||||
IIcon icon = sprites[getQuadrantSprite(q, connections)];
|
||||
if (icon == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setQuadrantBounds(rb, face, q, minX, minY, minZ, maxX, maxY, maxZ);
|
||||
rb.overrideBlockTexture = icon;
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
}
|
||||
|
||||
rb.overrideBlockTexture = null;
|
||||
rb.setRenderBounds(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
private void renderWholeFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon icon) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
IIcon saved = rb.overrideBlockTexture;
|
||||
rb.overrideBlockTexture = icon;
|
||||
try {
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
} finally {
|
||||
rb.overrideBlockTexture = saved;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderFace(RenderBlocks rb, Block block, int x, int y, int z, int face, IIcon icon) {
|
||||
boolean originalFlip = rb.field_152631_f;
|
||||
rb.field_152631_f = (face == 2 || face == 5);
|
||||
try {
|
||||
switch (face) {
|
||||
case 0 -> rb.renderFaceYNeg(block, x, y, z, icon);
|
||||
case 1 -> rb.renderFaceYPos(block, x, y, z, icon);
|
||||
case 2 -> rb.renderFaceZNeg(block, x, y, z, icon);
|
||||
case 3 -> rb.renderFaceZPos(block, x, y, z, icon);
|
||||
case 4 -> rb.renderFaceXNeg(block, x, y, z, icon);
|
||||
case 5 -> rb.renderFaceXPos(block, x, y, z, icon);
|
||||
}
|
||||
} finally {
|
||||
rb.field_152631_f = originalFlip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quadrants are always interpreted as:
|
||||
* 0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
|
||||
* in the face-local view.
|
||||
*/
|
||||
private void setQuadrantBounds(RenderBlocks rb, int face, int quadrant,
|
||||
double minX, double minY, double minZ,
|
||||
double maxX, double maxY, double maxZ) {
|
||||
double midX = (minX + maxX) * 0.5;
|
||||
double midY = (minY + maxY) * 0.5;
|
||||
double midZ = (minZ + maxZ) * 0.5;
|
||||
|
||||
switch (face) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, minY, minZ, midX, maxY, midZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(midX, minY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(midX, minY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, midZ, midX, maxY, maxZ);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (quadrant == 0) rb.setRenderBounds(midX, midY, minZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, minZ, midX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, minZ, midX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(midX, minY, minZ, maxX, midY, maxZ);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, minZ, midX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(midX, midY, minZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(midX, minY, minZ, maxX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, minZ, midX, midY, maxZ);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, midZ, maxX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, minZ, maxX, midY, midZ);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, minZ, maxX, midY, midZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, midZ, maxX, midY, maxZ);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A quadrant is built from two cardinal sides and the diagonal between them.
|
||||
*/
|
||||
private int getQuadrantSprite(int quadrant, int connections) {
|
||||
int sideA = QUAD_A[quadrant];
|
||||
int sideB = QUAD_B[quadrant];
|
||||
int diag = QUAD_DIAG[quadrant];
|
||||
|
||||
boolean connA = (connections & (1 << sideA)) != 0;
|
||||
boolean connB = (connections & (1 << sideB)) != 0;
|
||||
|
||||
if (connA && connB) {
|
||||
return (connections & (1 << diag)) != 0 ? SPRITE_ALL : SPRITE_MIXED;
|
||||
}
|
||||
|
||||
if (connA ^ connB) {
|
||||
int connectedBit = connA ? sideA : sideB;
|
||||
return (connectedBit == LEFT || connectedBit == RIGHT) ? SPRITE_VERTICAL : SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
@Desugar
|
||||
private record FaceBasis(int rightX, int rightY, int rightZ, int downX, int downY, int downZ) { }
|
||||
|
||||
private static final class CtmConnectionHelper {
|
||||
|
||||
public static int getConnections(IBlockAccess world, int x, int y, int z, int face, boolean innerSeams) {
|
||||
Block self = world.getBlock(x, y, z);
|
||||
FaceBasis basis = FACE_BASIS[face];
|
||||
|
||||
int bits = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int dx = RIGHT_FACTOR[i] * basis.rightX + DOWN_FACTOR[i] * basis.downX;
|
||||
int dy = RIGHT_FACTOR[i] * basis.rightY + DOWN_FACTOR[i] * basis.downY;
|
||||
int dz = RIGHT_FACTOR[i] * basis.rightZ + DOWN_FACTOR[i] * basis.downZ;
|
||||
|
||||
int nx = x + dx;
|
||||
int ny = y + dy;
|
||||
int nz = z + dz;
|
||||
|
||||
if (world.getBlock(nx, ny, nz) != self) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (innerSeams && !shouldRenderInnerSeam(world, nx, ny, nz, face)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bits |= (1 << i);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static boolean shouldRenderInnerSeam(IBlockAccess world, int x, int y, int z, int face) {
|
||||
int[] normal = RenderBlockState.NORMALS[face];
|
||||
int sx = x + normal[0];
|
||||
int sy = y + normal[1];
|
||||
int sz = z + normal[2];
|
||||
return world.getBlock(sx, sy, sz).shouldSideBeRendered(world, sx, sy, sz, face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
|
||||
abstract class TileOverride implements Comparable<TileOverride> {
|
||||
public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
@@ -80,6 +80,7 @@ abstract class TileOverride implements Comparable<TileOverride> {
|
||||
tileLoader);
|
||||
case "sandstone", "top" -> override = new TileOverrideImpl.Top(properties, tileLoader);
|
||||
case "repeat", "pattern" -> override = new TileOverrideImpl.Repeat(properties, tileLoader);
|
||||
case "ctm_compact", "compact" -> override = new TileOverrideImpl.CTMCompact(properties, tileLoader);
|
||||
default -> properties.error("unknown method \"%s\"", method);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
import com.prupe.mcpatcher.mal.util.WeightedIndex;
|
||||
|
||||
class TileOverrideImpl {
|
||||
public class TileOverrideImpl {
|
||||
|
||||
final static class CTM extends TileOverride {
|
||||
|
||||
@@ -481,4 +481,49 @@ class TileOverrideImpl {
|
||||
return icons[0];
|
||||
}
|
||||
}
|
||||
|
||||
final public static class CTMCompact extends TileOverride {
|
||||
|
||||
private final CompactConnectingCtmProperties properties;
|
||||
private volatile CompactCtmQuadProcessor processor;
|
||||
|
||||
CTMCompact(PropertiesFile propertiesFile, TileLoader tileLoader) {
|
||||
super(propertiesFile, tileLoader);
|
||||
this.properties = new CompactConnectingCtmProperties(propertiesFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethod() {
|
||||
return "compact";
|
||||
}
|
||||
|
||||
@Override
|
||||
String checkTileMap() {
|
||||
return getNumberOfTiles() == 5 ? null : "requires exactly 5 tiles";
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean requiresFace() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
IIcon getTileWorld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
return origIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
IIcon getTileHeld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
return icons.length > 0 ? icons[0] : origIcon;
|
||||
}
|
||||
|
||||
public CompactCtmQuadProcessor getProcessor() {
|
||||
CompactCtmQuadProcessor p = this.processor;
|
||||
if (p == null) {
|
||||
p = new CompactCtmQuadProcessor(icons, properties);
|
||||
this.processor = p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,8 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
}
|
||||
|
||||
public TileOverride go(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
|
||||
this.renderBlockState = renderBlockState;
|
||||
renderBlockState.setFilter(null);
|
||||
currentIcon = origIcon;
|
||||
@@ -121,6 +123,9 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
TileOverride override = next();
|
||||
IIcon newIcon = getTile(override, renderBlockState, origIcon);
|
||||
if (newIcon != null) {
|
||||
if (override instanceof TileOverrideImpl.CTMCompact compact) {
|
||||
CTMUtils.setCurrentCompact(compact);
|
||||
}
|
||||
lastMatchedOverride = override;
|
||||
skipOverrides.add(override);
|
||||
currentIcon = newIcon;
|
||||
|
||||
@@ -2,7 +2,7 @@ package jss.notfine.asm;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.FrameNode;
|
||||
@@ -133,7 +133,7 @@ public class ASMUtils {
|
||||
yield false;
|
||||
}
|
||||
default -> {
|
||||
AngelicaTweaker.LOGGER.warn("Unchecked node found: " + node1.getClass());
|
||||
AngelicaClientTweaker.LOGGER.warn("Unchecked node found: " + node1.getClass());
|
||||
yield node1.toString()
|
||||
.equals(node2.toString());
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package jss.notfine.asm.mappings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class und inherited classes are adapted from
|
||||
* <a href=
|
||||
@@ -48,7 +48,7 @@ public class Namer {
|
||||
|
||||
public static void initNames() {
|
||||
final boolean obfuscated = !(Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
|
||||
AngelicaTweaker.LOGGER.info("Environment obfuscated: {}", obfuscated);
|
||||
AngelicaClientTweaker.LOGGER.info("Environment obfuscated: {}", obfuscated);
|
||||
if (obfuscated) {
|
||||
new NamerObf().setNames();
|
||||
} else {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package jss.notfine.asm.mappings;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import jss.notfine.asm.mappings.Names.Clas;
|
||||
import jss.notfine.asm.mappings.Names.Fiel;
|
||||
import jss.notfine.asm.mappings.Names.Meth;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import jss.notfine.asm.mappings.Names.Clas;
|
||||
import jss.notfine.asm.mappings.Names.Fiel;
|
||||
import jss.notfine.asm.mappings.Names.Meth;
|
||||
|
||||
public class NamerMcp extends NamerSrg {
|
||||
|
||||
@Override
|
||||
@@ -71,15 +71,15 @@ public class NamerMcp extends NamerSrg {
|
||||
nameMap = loadReobfMap(confPath + "packaged.srg");
|
||||
for (Clas c : ac) {
|
||||
String s = nameMap.get(c.name);
|
||||
AngelicaTweaker.LOGGER.trace("C {} {}", c.name, s);
|
||||
AngelicaClientTweaker.LOGGER.trace("C {} {}", c.name, s);
|
||||
}
|
||||
for (Fiel f : af) {
|
||||
String s = nameMap.get(f.clas + "/" + f.name);
|
||||
AngelicaTweaker.LOGGER.trace("F {} {}", f.name, s);
|
||||
AngelicaClientTweaker.LOGGER.trace("F {} {}", f.name, s);
|
||||
}
|
||||
for (Meth m : am) {
|
||||
String s = nameMap.get(m.clas + "/" + m.name + m.desc);
|
||||
AngelicaTweaker.LOGGER.trace("M {} {}", m.name, s);
|
||||
AngelicaClientTweaker.LOGGER.trace("M {} {}", m.name, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.jellysquid.mods.sodium.client.gui.options.storage;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -9,7 +10,7 @@ public class SodiumOptionsStorage implements OptionStorage<SodiumGameOptions> {
|
||||
private final SodiumGameOptions options;
|
||||
|
||||
public SodiumOptionsStorage() {
|
||||
this.options = AngelicaMod.options();
|
||||
this.options = ClientProxy.options();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
package net.coderbot.iris;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.Tags;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import net.coderbot.iris.celeritas.IrisCeleritasShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import net.coderbot.iris.compat.dh.DHCompat;
|
||||
import net.coderbot.iris.config.IrisConfig;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
|
||||
import net.coderbot.iris.gl.shader.StandardMacros;
|
||||
import net.coderbot.iris.gui.screen.ShaderPackScreen;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.transform.ShaderTransformer;
|
||||
import net.coderbot.iris.pipeline.transform.TransformPatcher;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
|
||||
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.PipelineManager;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.transform.ShaderTransformer;
|
||||
import net.coderbot.iris.pipeline.transform.TransformPatcher;
|
||||
import net.coderbot.iris.shaderpack.OptionalBoolean;
|
||||
import net.coderbot.iris.shaderpack.ProgramSet;
|
||||
import net.coderbot.iris.shaderpack.ShaderPack;
|
||||
@@ -37,7 +34,6 @@ import net.coderbot.iris.shaderpack.option.OptionSet;
|
||||
import net.coderbot.iris.shaderpack.option.Profile;
|
||||
import net.coderbot.iris.shaderpack.option.values.MutableOptionValues;
|
||||
import net.coderbot.iris.shaderpack.option.values.OptionValues;
|
||||
import net.coderbot.iris.block_context.BlockContextHolder;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -66,7 +62,6 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -74,6 +69,7 @@ import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinWorkerThread;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipError;
|
||||
@@ -357,7 +353,7 @@ public class Iris {
|
||||
final boolean released = !Keyboard.getEventKeyState();
|
||||
if (Minecraft.getMinecraft().gameSettings.showDebugInfo && GuiScreen.isShiftKeyDown() && GuiScreen.isCtrlKeyDown() && released) {
|
||||
if (key == Keyboard.KEY_N) {
|
||||
AngelicaMod.animationsMode.next();
|
||||
ClientProxy.animationsMode.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -482,11 +478,9 @@ public class Iris {
|
||||
// Attempt to load an external shaderpack if it is available
|
||||
final Optional<String> externalName = irisConfig.getShaderPackName();
|
||||
|
||||
if (!externalName.isPresent()) {
|
||||
if (externalName.isEmpty()) {
|
||||
logger.info("Shaders are disabled because no valid shaderpack is selected");
|
||||
|
||||
setShadersDisabled();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -920,35 +914,25 @@ public class Iris {
|
||||
ClientRegistry.registerKeyBinding(shaderpackScreenKeybind);
|
||||
}
|
||||
|
||||
static BlockContextHolder contextHolder;
|
||||
|
||||
private static int getShaderMaterialOverrideId(Block block, int meta) {
|
||||
if (contextHolder == null) {
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
|
||||
if (blockMetaMatches == null) {
|
||||
return -1;
|
||||
}
|
||||
contextHolder = new BlockContextHolder(blockMetaMatches);
|
||||
|
||||
}
|
||||
contextHolder.set(block, meta, (short) block.getRenderType());
|
||||
return contextHolder.blockId;
|
||||
}
|
||||
|
||||
public static void setShaderMaterialOverride(Block block, int meta) {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
int blockId = getShaderMaterialOverrideId(block, meta);
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
|
||||
if (blockMetaMatches == null)
|
||||
return;
|
||||
|
||||
if (TessellatorManager.get() instanceof CapturingTessellator tess)
|
||||
tess.setShaderBlockId(blockId);
|
||||
final Int2IntMap metaMap = blockMetaMatches.get(block);
|
||||
final int blockId = metaMap != null ? metaMap.get(meta) : -1;
|
||||
|
||||
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
|
||||
tess.angelica$setShaderOverrideBlockId((short) blockId);
|
||||
}
|
||||
|
||||
public static void resetShaderMaterialOverride() {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (TessellatorManager.get() instanceof CapturingTessellator tess)
|
||||
tess.setShaderBlockId(-1);
|
||||
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
|
||||
tess.angelica$setShaderOverrideBlockId((short) -1);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package net.coderbot.iris.block_context;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
/**
|
||||
* Holds block context for shader material ID lookups.
|
||||
*/
|
||||
public class BlockContextHolder {
|
||||
private final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches;
|
||||
public int blockId;
|
||||
public short renderType;
|
||||
|
||||
public BlockContextHolder(Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches) {
|
||||
this.blockMetaMatches = blockMetaMatches;
|
||||
this.blockId = -1;
|
||||
this.renderType = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the material ID for a block.
|
||||
* @param block The block
|
||||
* @param meta The block metadata
|
||||
* @param renderType The render type
|
||||
*/
|
||||
public void set(Block block, int meta, short renderType) {
|
||||
Int2IntMap metaMap = this.blockMetaMatches != null ? this.blockMetaMatches.get(block) : null;
|
||||
int id = metaMap != null ? metaMap.get(meta) : -1;
|
||||
|
||||
this.blockId = id;
|
||||
this.renderType = renderType;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.blockId = -1;
|
||||
this.renderType = -1;
|
||||
}
|
||||
}
|
||||
@@ -72,9 +72,11 @@ public class CeleritasTerrainPipeline {
|
||||
Optional<ProgramSource> terrainSource,
|
||||
Optional<ProgramSource> translucentSource,
|
||||
Optional<ProgramSource> shadowSource,
|
||||
Optional<ProgramSource> shadowTranslucentSource,
|
||||
CompletableFuture<Map<PatchShaderType, String>> terrainFuture,
|
||||
CompletableFuture<Map<PatchShaderType, String>> translucentFuture,
|
||||
CompletableFuture<Map<PatchShaderType, String>> shadowFuture,
|
||||
CompletableFuture<Map<PatchShaderType, String>> shadowTranslucentFuture,
|
||||
RenderTargets renderTargets,
|
||||
ImmutableSet<Integer> flippedAfterPrepare,
|
||||
ImmutableSet<Integer> flippedAfterTranslucent,
|
||||
@@ -92,6 +94,7 @@ public class CeleritasTerrainPipeline {
|
||||
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_CUTOUT, terrainSource);
|
||||
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_TRANSLUCENT, translucentSource.isPresent() ? translucentSource : terrainSource);
|
||||
gbufferProgramSource.put(IrisTerrainPass.SHADOW, shadowSource);
|
||||
gbufferProgramSource.put(IrisTerrainPass.SHADOW_TRANSLUCENT, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource);
|
||||
|
||||
// Initialize PassInfo, framebuffers, blend modes, and alpha in single pass
|
||||
for (IrisTerrainPass pass : IrisTerrainPass.VALUES) {
|
||||
@@ -101,11 +104,12 @@ public class CeleritasTerrainPipeline {
|
||||
// Set up framebuffer, blend mode, and buffer blend overrides
|
||||
final ProgramId programId = switch (pass) {
|
||||
case GBUFFER_TRANSLUCENT -> ProgramId.Water;
|
||||
case SHADOW_TRANSLUCENT -> ProgramId.ShadowWater;
|
||||
case SHADOW, SHADOW_CUTOUT -> ProgramId.Shadow;
|
||||
default -> ProgramId.Terrain;
|
||||
};
|
||||
|
||||
if (pass == IrisTerrainPass.SHADOW || pass == IrisTerrainPass.SHADOW_CUTOUT) {
|
||||
if (pass.isShadow()) {
|
||||
passInfo.framebuffer = shadowFramebuffer;
|
||||
passInfo.blendModeOverride = programId.getBlendModeOverride();
|
||||
passInfo.bufferBlendOverrides = Collections.emptyList();
|
||||
@@ -145,6 +149,7 @@ public class CeleritasTerrainPipeline {
|
||||
processShaderFuture(terrainFuture, terrainSource, passInfoMap.get(IrisTerrainPass.GBUFFER_SOLID), passInfoMap.get(IrisTerrainPass.GBUFFER_CUTOUT));
|
||||
processShaderFuture(translucentFuture, translucentSource, passInfoMap.get(IrisTerrainPass.GBUFFER_TRANSLUCENT));
|
||||
processShaderFuture(shadowFuture, shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW), passInfoMap.get(IrisTerrainPass.SHADOW_CUTOUT));
|
||||
processShaderFuture(shadowTranslucentFuture, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW_TRANSLUCENT));
|
||||
}
|
||||
|
||||
private void processShaderFuture(@Nullable CompletableFuture<Map<PatchShaderType, String>> future, Optional<ProgramSource> source, PassInfo... targets) {
|
||||
|
||||
@@ -136,8 +136,10 @@ public class IrisCeleritasChunkProgramOverrides {
|
||||
if (celeritasPipeline != null && !celeritasPipeline.hasShadowPass()) {
|
||||
throw new IllegalStateException("Shadow program requested, but shader pack has no shadow pass");
|
||||
}
|
||||
return programs.get(pass.supportsFragmentDiscard() ?
|
||||
IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW);
|
||||
if (pass.isReverseOrder()) {
|
||||
return programs.get(IrisTerrainPass.SHADOW_TRANSLUCENT);
|
||||
}
|
||||
return programs.get(pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW);
|
||||
} else {
|
||||
if (pass.supportsFragmentDiscard()) {
|
||||
return programs.get(IrisTerrainPass.GBUFFER_CUTOUT);
|
||||
|
||||
@@ -187,7 +187,11 @@ public class IrisCeleritasChunkShaderInterface implements ChunkShaderInterface {
|
||||
final boolean isShadow = ShadowRenderingState.areShadowsCurrentlyBeingRendered();
|
||||
final IrisTerrainPass irisPass;
|
||||
if (isShadow) {
|
||||
irisPass = pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW;
|
||||
if (pass.isReverseOrder()) {
|
||||
irisPass = IrisTerrainPass.SHADOW_TRANSLUCENT;
|
||||
} else {
|
||||
irisPass = pass.supportsFragmentDiscard() ? IrisTerrainPass.SHADOW_CUTOUT : IrisTerrainPass.SHADOW;
|
||||
}
|
||||
} else if (pass.isReverseOrder()) {
|
||||
irisPass = IrisTerrainPass.GBUFFER_TRANSLUCENT;
|
||||
} else if (pass.supportsFragmentDiscard()) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.embeddedt.embeddium.impl.render.chunk.terrain.TerrainRenderPass;
|
||||
public enum IrisTerrainPass {
|
||||
SHADOW("shadow"),
|
||||
SHADOW_CUTOUT("shadow"),
|
||||
SHADOW_TRANSLUCENT("shadow_water"),
|
||||
GBUFFER_SOLID("gbuffers_terrain"),
|
||||
GBUFFER_CUTOUT("gbuffers_terrain_cutout"),
|
||||
GBUFFER_TRANSLUCENT("gbuffers_water");
|
||||
@@ -23,19 +24,22 @@ public enum IrisTerrainPass {
|
||||
}
|
||||
|
||||
public boolean isShadow() {
|
||||
return this == SHADOW || this == SHADOW_CUTOUT;
|
||||
return this == SHADOW || this == SHADOW_CUTOUT || this == SHADOW_TRANSLUCENT;
|
||||
}
|
||||
|
||||
public TerrainRenderPass toTerrainPass(RenderPassConfiguration<?> config) {
|
||||
return switch (this) {
|
||||
case SHADOW, GBUFFER_SOLID -> config.defaultSolidMaterial().pass;
|
||||
case SHADOW_CUTOUT, GBUFFER_CUTOUT -> config.defaultCutoutMippedMaterial().pass;
|
||||
case GBUFFER_TRANSLUCENT -> config.defaultTranslucentMaterial().pass;
|
||||
case SHADOW_TRANSLUCENT, GBUFFER_TRANSLUCENT -> config.defaultTranslucentMaterial().pass;
|
||||
};
|
||||
}
|
||||
|
||||
public static IrisTerrainPass fromTerrainPass(TerrainRenderPass pass, boolean isShadow) {
|
||||
if (isShadow) {
|
||||
if (pass.isReverseOrder()) {
|
||||
return SHADOW_TRANSLUCENT;
|
||||
}
|
||||
return pass.supportsFragmentDiscard() ? SHADOW_CUTOUT : SHADOW;
|
||||
} else if (pass.supportsFragmentDiscard()) {
|
||||
return GBUFFER_CUTOUT;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.coderbot.iris.client;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.coderbot.iris.Iris;
|
||||
@@ -47,7 +47,7 @@ public class IrisDebugScreenHandler {
|
||||
event.right.add("[" + Iris.MODNAME + "] Shaders are disabled");
|
||||
}
|
||||
if (AngelicaConfig.speedupAnimations) {
|
||||
event.right.add(Math.min(event.right.size(), 9), "animationsMode: " + AngelicaMod.animationsMode);
|
||||
event.right.add(Math.min(event.right.size(), 9), "animationsMode: " + ClientProxy.animationsMode);
|
||||
}
|
||||
|
||||
Iris.getPipelineManager().getPipeline().ifPresent(pipeline -> pipeline.addDebugText(event.left));
|
||||
|
||||
@@ -131,7 +131,7 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
|
||||
ProgramImages.Builder builder = ProgramImages.builder(id);
|
||||
pipeline.addGbufferOrShadowSamplers(samplerBuilder, builder,
|
||||
isShadowPass ? pipeline::getFlippedBeforeShadow : () -> translucent ? pipeline.getFlippedAfterTranslucent() : pipeline.getFlippedAfterPrepare(),
|
||||
isShadowPass, true, true, false);
|
||||
isShadowPass, false, true, false);
|
||||
customUniforms.mapholderToPass(uniformBuilder, this);
|
||||
this.uniforms = uniformBuilder.buildUniforms();
|
||||
this.customUniforms = customUniforms;
|
||||
|
||||
@@ -122,7 +122,7 @@ public class IrisLodRenderProgram {
|
||||
ProgramImages.Builder builder = ProgramImages.builder(id);
|
||||
pipeline.addGbufferOrShadowSamplers(samplerBuilder, builder,
|
||||
isShadowPass ? pipeline::getFlippedBeforeShadow : () -> translucent ? pipeline.getFlippedAfterTranslucent() : pipeline.getFlippedAfterPrepare(),
|
||||
isShadowPass, true, true, false);
|
||||
isShadowPass, false, true, false);
|
||||
customUniforms.mapholderToPass(uniformBuilder, this);
|
||||
this.uniforms = uniformBuilder.buildUniforms();
|
||||
this.customUniforms = customUniforms;
|
||||
|
||||
@@ -18,6 +18,7 @@ public enum RenderCondition {
|
||||
HAND_TRANSLUCENT,
|
||||
RAIN_SNOW,
|
||||
WORLD_BORDER,
|
||||
SHADOW_TRANSLUCENT,
|
||||
// NB: Must be last due to implementation details of DeferredWorldRenderingPipeline
|
||||
SHADOW
|
||||
}
|
||||
@@ -1,9 +1,16 @@
|
||||
package net.coderbot.iris.gl.state;
|
||||
|
||||
/**
|
||||
* Holds update notifiers for PBR texture changes.
|
||||
* Holds some standard update notifiers for various elements of GL state.
|
||||
*/
|
||||
public class StateUpdateNotifiers {
|
||||
public static ValueUpdateNotifier fogModeNotifier;
|
||||
public static ValueUpdateNotifier fogStartNotifier;
|
||||
public static ValueUpdateNotifier fogEndNotifier;
|
||||
public static ValueUpdateNotifier fogDensityNotifier;
|
||||
public static ValueUpdateNotifier blendFuncNotifier;
|
||||
public static ValueUpdateNotifier bindTextureNotifier;
|
||||
public static ValueUpdateNotifier normalTextureChangeNotifier;
|
||||
public static ValueUpdateNotifier specularTextureChangeNotifier;
|
||||
public static ValueUpdateNotifier phaseChangeNotifier;
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package net.coderbot.iris.gl.uniform;
|
||||
|
||||
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector3i;
|
||||
import org.joml.Vector2ic;
|
||||
import org.joml.Vector3ic;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4i;
|
||||
import org.joml.Vector4fc;
|
||||
import org.joml.Vector4ic;
|
||||
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.IntSupplier;
|
||||
@@ -38,7 +37,7 @@ public interface DynamicLocationalUniformHolder extends LocationalUniformHolder,
|
||||
return this;
|
||||
}
|
||||
|
||||
default DynamicLocationalUniformHolder uniform2i(String name, Supplier<Vector2i> value, ValueUpdateNotifier notifier) {
|
||||
default DynamicLocationalUniformHolder uniform2i(String name, Supplier<Vector2ic> value, ValueUpdateNotifier notifier) {
|
||||
location(name, UniformType.VEC2I).ifPresent(id -> addDynamicUniform(new Vector2IntegerJomlUniform(id, value, notifier), notifier));
|
||||
|
||||
return this;
|
||||
@@ -50,7 +49,7 @@ public interface DynamicLocationalUniformHolder extends LocationalUniformHolder,
|
||||
return this;
|
||||
}
|
||||
|
||||
default DynamicUniformHolder uniform4f(String name, Supplier<Vector4f> value, ValueUpdateNotifier notifier) {
|
||||
default DynamicUniformHolder uniform4f(String name, Supplier<Vector4fc> value, ValueUpdateNotifier notifier) {
|
||||
location(name, UniformType.VEC4).ifPresent(id -> addDynamicUniform(new Vector4Uniform(id, value, notifier), notifier));
|
||||
|
||||
return this;
|
||||
@@ -61,7 +60,7 @@ public interface DynamicLocationalUniformHolder extends LocationalUniformHolder,
|
||||
return this;
|
||||
}
|
||||
|
||||
default DynamicUniformHolder uniform4i(String name, Supplier<Vector4i> value, ValueUpdateNotifier notifier) {
|
||||
default DynamicUniformHolder uniform4i(String name, Supplier<Vector4ic> value, ValueUpdateNotifier notifier) {
|
||||
location(name, UniformType.VEC4I).ifPresent(id -> addDynamicUniform(new Vector4IntegerJomlUniform(id, value, notifier), notifier));
|
||||
|
||||
return this;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package net.coderbot.iris.gl.uniform;
|
||||
|
||||
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector3i;
|
||||
import org.joml.Vector2ic;
|
||||
import org.joml.Vector3ic;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4i;
|
||||
import org.joml.Vector4fc;
|
||||
import org.joml.Vector4ic;
|
||||
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.IntSupplier;
|
||||
@@ -16,9 +15,9 @@ public interface DynamicUniformHolder extends UniformHolder {
|
||||
DynamicUniformHolder uniform1f(String name, IntSupplier value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform1f(String name, DoubleSupplier value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform1i(String name, IntSupplier value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform2i(String name, Supplier<Vector2i> value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform2i(String name, Supplier<Vector2ic> value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform3i(String name, Supplier<Vector3ic> value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform4f(String name, Supplier<Vector4f> value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform4f(String name, Supplier<Vector4fc> value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform4fArray(String name, Supplier<float[]> value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform4i(String name, Supplier<Vector4i> value, ValueUpdateNotifier notifier);
|
||||
DynamicUniformHolder uniform4i(String name, Supplier<Vector4ic> value, ValueUpdateNotifier notifier);
|
||||
}
|
||||
@@ -4,13 +4,13 @@ import net.minecraft.util.Vec3;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector2ic;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3fc;
|
||||
import org.joml.Vector3i;
|
||||
import org.joml.Vector3ic;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import java.util.function.BooleanSupplier;
|
||||
@@ -66,7 +66,7 @@ public interface LocationalUniformHolder extends UniformHolder {
|
||||
}
|
||||
|
||||
@Override
|
||||
default LocationalUniformHolder uniform2i(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector2i> value) {
|
||||
default LocationalUniformHolder uniform2i(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector2ic> value) {
|
||||
location(name, UniformType.VEC2I).ifPresent(id -> addUniform(updateFrequency, new Vector2IntegerJomlUniform(id, value)));
|
||||
|
||||
return this;
|
||||
@@ -94,7 +94,7 @@ public interface LocationalUniformHolder extends UniformHolder {
|
||||
}
|
||||
|
||||
@Override
|
||||
default LocationalUniformHolder uniformTruncated3f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4f> value) {
|
||||
default LocationalUniformHolder uniformTruncated3f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4fc> value) {
|
||||
location(name, UniformType.VEC3).ifPresent(id -> addUniform(updateFrequency, Vector3Uniform.truncated(id, value)));
|
||||
|
||||
return this;
|
||||
@@ -108,7 +108,7 @@ public interface LocationalUniformHolder extends UniformHolder {
|
||||
}
|
||||
|
||||
@Override
|
||||
default LocationalUniformHolder uniform4f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4f> value) {
|
||||
default LocationalUniformHolder uniform4f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4fc> value) {
|
||||
location(name, UniformType.VEC4).ifPresent(id -> addUniform(updateFrequency, new Vector4Uniform(id, value)));
|
||||
|
||||
return this;
|
||||
|
||||
@@ -3,12 +3,12 @@ package net.coderbot.iris.gl.uniform;
|
||||
import net.minecraft.util.Vec3;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector2ic;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3fc;
|
||||
import org.joml.Vector3i;
|
||||
import org.joml.Vector3ic;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.DoubleSupplier;
|
||||
@@ -28,7 +28,7 @@ public interface UniformHolder {
|
||||
|
||||
UniformHolder uniform2f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector2f> value);
|
||||
|
||||
UniformHolder uniform2i(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector2i> value);
|
||||
UniformHolder uniform2i(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector2ic> value);
|
||||
|
||||
UniformHolder uniform3f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector3fc> value);
|
||||
|
||||
@@ -36,11 +36,11 @@ public interface UniformHolder {
|
||||
|
||||
UniformHolder uniformVanilla3f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vec3> value);
|
||||
|
||||
UniformHolder uniformTruncated3f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4f> value);
|
||||
UniformHolder uniformTruncated3f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4fc> value);
|
||||
|
||||
UniformHolder uniform3d(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector3d> value);
|
||||
|
||||
UniformHolder uniform4f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4f> value);
|
||||
UniformHolder uniform4f(UniformUpdateFrequency updateFrequency, String name, Supplier<Vector4fc> value);
|
||||
|
||||
UniformHolder uniform4fArray(UniformUpdateFrequency updateFrequency, String name, Supplier<float[]> value);
|
||||
|
||||
|
||||
@@ -3,21 +3,22 @@ package net.coderbot.iris.gl.uniform;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector2ic;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Vector2IntegerJomlUniform extends Uniform {
|
||||
private Vector2i cachedValue;
|
||||
private final Supplier<Vector2i> value;
|
||||
private final Vector2i cachedValue;
|
||||
private final Supplier<Vector2ic> value;
|
||||
|
||||
Vector2IntegerJomlUniform(int location, Supplier<Vector2i> value) {
|
||||
Vector2IntegerJomlUniform(int location, Supplier<Vector2ic> value) {
|
||||
this(location, value, null);
|
||||
}
|
||||
|
||||
Vector2IntegerJomlUniform(int location, Supplier<Vector2i> value, ValueUpdateNotifier notifier) {
|
||||
Vector2IntegerJomlUniform(int location, Supplier<Vector2ic> value, ValueUpdateNotifier notifier) {
|
||||
super(location, notifier);
|
||||
|
||||
this.cachedValue = null;
|
||||
this.cachedValue = new Vector2i();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@@ -31,11 +32,11 @@ public class Vector2IntegerJomlUniform extends Uniform {
|
||||
}
|
||||
|
||||
private void updateValue() {
|
||||
Vector2i newValue = value.get();
|
||||
Vector2ic newValue = value.get();
|
||||
|
||||
if (cachedValue == null || !newValue.equals(cachedValue)) {
|
||||
cachedValue = newValue;
|
||||
RenderSystem.uniform2i(this.location, newValue.x, newValue.y);
|
||||
if (!newValue.equals(cachedValue)) {
|
||||
cachedValue.set(newValue.x(), newValue.y());
|
||||
RenderSystem.uniform2i(this.location, cachedValue.x, cachedValue.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3fc;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -31,11 +31,11 @@ public class Vector3Uniform extends Uniform {
|
||||
});
|
||||
}
|
||||
|
||||
static Vector3Uniform truncated(int location, Supplier<Vector4f> value) {
|
||||
static Vector3Uniform truncated(int location, Supplier<Vector4fc> value) {
|
||||
Vector3f held = new Vector3f();
|
||||
|
||||
return new Vector3Uniform(location, () -> {
|
||||
Vector4f updated = value.get();
|
||||
Vector4fc updated = value.get();
|
||||
|
||||
held.set(updated.x(), updated.y(), updated.z());
|
||||
|
||||
|
||||
@@ -3,21 +3,22 @@ package net.coderbot.iris.gl.uniform;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
|
||||
import org.joml.Vector4i;
|
||||
import org.joml.Vector4ic;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Vector4IntegerJomlUniform extends Uniform {
|
||||
private Vector4i cachedValue;
|
||||
private final Supplier<Vector4i> value;
|
||||
private final Vector4i cachedValue;
|
||||
private final Supplier<Vector4ic> value;
|
||||
|
||||
Vector4IntegerJomlUniform(int location, Supplier<Vector4i> value) {
|
||||
Vector4IntegerJomlUniform(int location, Supplier<Vector4ic> value) {
|
||||
this(location, value, null);
|
||||
}
|
||||
|
||||
Vector4IntegerJomlUniform(int location, Supplier<Vector4i> value, ValueUpdateNotifier notifier) {
|
||||
Vector4IntegerJomlUniform(int location, Supplier<Vector4ic> value, ValueUpdateNotifier notifier) {
|
||||
super(location, notifier);
|
||||
|
||||
this.cachedValue = null;
|
||||
this.cachedValue = new Vector4i();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@@ -31,11 +32,11 @@ public class Vector4IntegerJomlUniform extends Uniform {
|
||||
}
|
||||
|
||||
private void updateValue() {
|
||||
Vector4i newValue = value.get();
|
||||
Vector4ic newValue = value.get();
|
||||
|
||||
if (cachedValue == null || !newValue.equals(cachedValue)) {
|
||||
cachedValue = newValue;
|
||||
RenderSystem.uniform4i(this.location, newValue.x, newValue.y, newValue.z, newValue.w);
|
||||
if (!newValue.equals(cachedValue)) {
|
||||
cachedValue.set(newValue.x(), newValue.y(), newValue.z(), newValue.w());
|
||||
RenderSystem.uniform4i(this.location, cachedValue.x, cachedValue.y, cachedValue.z, cachedValue.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,19 @@ package net.coderbot.iris.gl.uniform;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Vector4Uniform extends Uniform {
|
||||
private final Vector4f cachedValue;
|
||||
private final Supplier<Vector4f> value;
|
||||
private final Supplier<Vector4fc> value;
|
||||
|
||||
Vector4Uniform(int location, Supplier<Vector4f> value) {
|
||||
Vector4Uniform(int location, Supplier<Vector4fc> value) {
|
||||
this(location, value, null);
|
||||
}
|
||||
|
||||
Vector4Uniform(int location, Supplier<Vector4f> value, ValueUpdateNotifier notifier) {
|
||||
Vector4Uniform(int location, Supplier<Vector4fc> value, ValueUpdateNotifier notifier) {
|
||||
super(location, notifier);
|
||||
|
||||
this.cachedValue = new Vector4f();
|
||||
@@ -31,7 +32,7 @@ public class Vector4Uniform extends Uniform {
|
||||
}
|
||||
|
||||
private void updateValue() {
|
||||
Vector4f newValue = value.get();
|
||||
Vector4fc newValue = value.get();
|
||||
|
||||
if (!newValue.equals(cachedValue)) {
|
||||
cachedValue.set(newValue.x(), newValue.y(), newValue.z(), newValue.w());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.coderbot.iris.gui.screen;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gui.GuiUtil;
|
||||
import net.coderbot.iris.gui.NavigationController;
|
||||
@@ -460,7 +460,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
|
||||
}
|
||||
|
||||
if (openViaSystemClass) {
|
||||
AngelicaTweaker.LOGGER.debug("Opening via system class!");
|
||||
AngelicaMod.LOGGER.debug("Opening via system class!");
|
||||
Sys.openURL("file://" + uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import net.coderbot.iris.gl.shader.ProgramCreator;
|
||||
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPhase;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
|
||||
@@ -11,7 +12,12 @@ public class GbufferPrograms {
|
||||
private static boolean entities;
|
||||
private static boolean blockEntities;
|
||||
private static boolean outline;
|
||||
|
||||
private static Runnable phaseChangeListener;
|
||||
|
||||
static {
|
||||
StateUpdateNotifiers.phaseChangeNotifier = listener -> phaseChangeListener = listener;
|
||||
}
|
||||
|
||||
private static void checkReentrancy() {
|
||||
if (entities || blockEntities || outline) {
|
||||
throw new IllegalStateException("GbufferPrograms in weird state, tried to call begin function when entities = "
|
||||
@@ -106,7 +112,13 @@ public class GbufferPrograms {
|
||||
public static void teardownSpecialRenderCondition() {
|
||||
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setSpecialCondition(null));
|
||||
}
|
||||
|
||||
|
||||
public static void runPhaseChangeNotifier() {
|
||||
if (phaseChangeListener != null) {
|
||||
phaseChangeListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// Empty initializer to run static
|
||||
}
|
||||
|
||||