Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c295a1e22 | ||
|
|
9fd02900ef | ||
|
|
27e9a4d4fd | ||
|
|
dfc08053d5 | ||
|
|
1af03fa369 | ||
|
|
3ae3b2527d | ||
|
|
fd2a14a000 | ||
|
|
9f0f6a6232 | ||
|
|
2942c7eb84 | ||
|
|
47762f921e | ||
|
|
bdc4431760 | ||
|
|
7e1beadfb5 | ||
|
|
59a4b84023 | ||
|
|
8d53e25e0a | ||
|
|
bbf83db379 | ||
|
|
ec82280b9b | ||
|
|
d7588e4c14 |
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
|
||||
```
|
||||
|
||||
---
|
||||
@@ -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")
|
||||
|
||||
@@ -949,10 +949,11 @@ public class GLStateManager {
|
||||
|
||||
switch (pname) {
|
||||
case GL11.GL_COLOR_WRITEMASK -> {
|
||||
params.put((byte) (colorMask.red ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.green ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.blue ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put((byte) (colorMask.alpha ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
final int pos = params.position();
|
||||
params.put(pos, (byte) (colorMask.red ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 1, (byte) (colorMask.green ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 2, (byte) (colorMask.blue ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
params.put(pos + 3, (byte) (colorMask.alpha ? GL11.GL_TRUE : GL11.GL_FALSE));
|
||||
}
|
||||
default -> {
|
||||
if (!HAS_MULTIPLE_SET.contains(pname)) {
|
||||
@@ -1063,7 +1064,7 @@ public class GLStateManager {
|
||||
case GL11.GL_DIFFUSE -> state.diffuse.get(0, params);
|
||||
case GL11.GL_SPECULAR -> state.specular.get(0, params);
|
||||
case GL11.GL_EMISSION -> state.emission.get(0, params);
|
||||
case GL11.GL_SHININESS -> params.put(state.shininess);
|
||||
case GL11.GL_SHININESS -> params.put(params.position(), state.shininess);
|
||||
case GL11.GL_COLOR_INDEXES -> state.colorIndexes.get(0, params);
|
||||
default -> {
|
||||
}
|
||||
@@ -1078,11 +1079,11 @@ public class GLStateManager {
|
||||
case GL11.GL_SPECULAR -> state.specular.get(0, params);
|
||||
case GL11.GL_POSITION -> state.position.get(0, params);
|
||||
case GL11.GL_SPOT_DIRECTION -> state.spotDirection.get(0, params);
|
||||
case GL11.GL_SPOT_EXPONENT -> params.put(state.spotExponent);
|
||||
case GL11.GL_SPOT_CUTOFF -> params.put(state.spotCutoff);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> params.put(state.constantAttenuation);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> params.put(state.linearAttenuation);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> params.put(state.quadraticAttenuation);
|
||||
case GL11.GL_SPOT_EXPONENT -> params.put(params.position(), state.spotExponent);
|
||||
case GL11.GL_SPOT_CUTOFF -> params.put(params.position(), state.spotCutoff);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> params.put(params.position(), state.constantAttenuation);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> params.put(params.position(), state.linearAttenuation);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> params.put(params.position(), state.quadraticAttenuation);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
@@ -1101,21 +1102,39 @@ public class GLStateManager {
|
||||
case GL11.GL_COLOR_CLEAR_VALUE -> clearColor.get(params);
|
||||
case GL11.GL_CURRENT_COLOR -> color.get(params);
|
||||
case GL11.GL_DEPTH_RANGE -> {
|
||||
params.put((float) viewportState.depthRangeNear);
|
||||
params.put((float) viewportState.depthRangeFar);
|
||||
final int pos = params.position();
|
||||
params.put(pos, (float) viewportState.depthRangeNear);
|
||||
params.put(pos + 1, (float) viewportState.depthRangeFar);
|
||||
}
|
||||
case GL14.GL_BLEND_COLOR -> {
|
||||
final int pos = params.position();
|
||||
params.put(pos, blendState.getBlendColorR());
|
||||
params.put(pos + 1, blendState.getBlendColorG());
|
||||
params.put(pos + 2, blendState.getBlendColorB());
|
||||
params.put(pos + 3, blendState.getBlendColorA());
|
||||
}
|
||||
case GL14.GL_BLEND_COLOR -> params.put(blendState.getBlendColorR()).put(blendState.getBlendColorG()).put(blendState.getBlendColorB()).put(blendState.getBlendColorA());
|
||||
case GL11.GL_CURRENT_NORMAL -> {
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
params.put(normal.x).put(normal.y).put(normal.z);
|
||||
final int pos = params.position();
|
||||
params.put(pos, normal.x);
|
||||
params.put(pos + 1, normal.y);
|
||||
params.put(pos + 2, normal.z);
|
||||
}
|
||||
case GL11.GL_CURRENT_TEXTURE_COORDS -> {
|
||||
final Vector4f tc = ShaderManager.getCurrentTexCoord();
|
||||
params.put(tc.x).put(tc.y).put(tc.z).put(tc.w);
|
||||
final int pos = params.position();
|
||||
params.put(pos, tc.x);
|
||||
params.put(pos + 1, tc.y);
|
||||
params.put(pos + 2, tc.z);
|
||||
params.put(pos + 3, tc.w);
|
||||
}
|
||||
case GL11.GL_FOG_COLOR -> {
|
||||
final FloatBuffer fogBuf = fogState.getFogColorBuffer();
|
||||
params.put(fogBuf.get(0)).put(fogBuf.get(1)).put(fogBuf.get(2)).put(fogBuf.get(3));
|
||||
final int pos = params.position();
|
||||
params.put(pos, fogBuf.get(0));
|
||||
params.put(pos + 1, fogBuf.get(1));
|
||||
params.put(pos + 2, fogBuf.get(2));
|
||||
params.put(pos + 3, fogBuf.get(3));
|
||||
}
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> lightModel.ambient.get(0, params);
|
||||
default -> {
|
||||
|
||||
@@ -36,9 +36,10 @@ 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(
|
||||
// Always rename sample, glsl-transformation-lib doesn't like it
|
||||
// Some reserved words to always rename
|
||||
0, List.of(
|
||||
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample")
|
||||
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();
|
||||
}
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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);}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
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 |
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -108,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -8,6 +8,8 @@ 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;
|
||||
@@ -91,30 +93,25 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
.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");
|
||||
for (String exclusion : EcosystemNarrowRules.LWJGL3IFY_EXCLUSIONS_SHARED) {
|
||||
handle.exclusions().add(exclusion);
|
||||
}
|
||||
handle.exclusions().add("com.gtnewhorizons.angelica.vulkan");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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,79 +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("Ears") && exceptions.remove("com.unascribed.ears")) {
|
||||
exceptions.add("com.unascribed.ears.asm");
|
||||
exceptions.add("com.unascribed.ears.common.agent");
|
||||
FMLRelaunchLog.info("[Angelica] Narrowed com.unascribed.ears 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;
|
||||
|
||||
@@ -16,4 +16,8 @@ public interface StateAwareTessellator {
|
||||
void angelica$setCeleritasMeshing(boolean active);
|
||||
|
||||
int[] angelica$getVertexStates();
|
||||
|
||||
int[] angelica$getShaderOverrideBlockIds();
|
||||
|
||||
void angelica$setShaderOverrideBlockId(short blockId);
|
||||
}
|
||||
@@ -115,6 +115,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void copyRawBuffer(int[] rawBuffer, int vertexCount, int[] vertexStates,
|
||||
int[] shaderOverrideBlockIds,
|
||||
ChunkBuildBuffers buffers, Material material, boolean isShaderPackOverride,
|
||||
boolean blockAllowsSmoothLighting) {
|
||||
if (vertexCount == 0) {
|
||||
@@ -145,6 +146,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
final int worldY = originY + blockY;
|
||||
final int worldZ = originZ + blockZ;
|
||||
|
||||
final short originalBlockId = blockRenderContext.blockId;
|
||||
|
||||
int stateIdx = 0;
|
||||
int facesAtBaseMaterial = 0;
|
||||
|
||||
@@ -239,6 +242,9 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
}
|
||||
final var builder = buffers.get(correctMaterial);
|
||||
|
||||
final int shaderOverrideBlockId = shaderOverrideBlockIds[quadIdx * 4];
|
||||
blockRenderContext.blockId = shaderOverrideBlockId != -1 ? (short) shaderOverrideBlockId : originalBlockId;
|
||||
|
||||
if (correctMaterial != material && builder.getEncoder() instanceof IrisExtendedChunkVertexEncoder iris) {
|
||||
iris.setContext(blockRenderContext);
|
||||
}
|
||||
|
||||
@@ -270,6 +270,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
&& block.getLightValue() == 0; // does not emit real block light
|
||||
buildContext.copyRawBuffer(tessellator.rawBuffer, tessellator.vertexCount,
|
||||
((StateAwareTessellator)tessellator).angelica$getVertexStates(),
|
||||
((StateAwareTessellator)tessellator).angelica$getShaderOverrideBlockIds(),
|
||||
buffers, passMaterial, isShaderPackOverride, blockAllowsSmoothLighting);
|
||||
tessellator.reset();
|
||||
tessellator.isDrawing = false;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,20 @@ public class CTMUtils {
|
||||
private static Overrides newOverrides = null;
|
||||
private static TileLoader tileLoader;
|
||||
|
||||
private static final ThreadLocal<TileOverrideImpl.CTMCompact> CURRENT_COMPACT = new ThreadLocal<>();
|
||||
|
||||
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact) {
|
||||
CURRENT_COMPACT.set(compact);
|
||||
}
|
||||
|
||||
public static TileOverrideImpl.CTMCompact getCurrentCompact() {
|
||||
return CURRENT_COMPACT.get();
|
||||
}
|
||||
|
||||
public static void clearCurrentCompact() {
|
||||
CURRENT_COMPACT.remove();
|
||||
}
|
||||
|
||||
private static final StampedLock lock = new StampedLock();
|
||||
|
||||
private static class StateAndIterator {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
|
||||
public class CompactConnectingCtmProperties {
|
||||
|
||||
private final boolean innerSeams;
|
||||
private final String orientation;
|
||||
private final Int2IntMap tileReplacementMap = new Int2IntOpenHashMap();
|
||||
|
||||
public CompactConnectingCtmProperties(PropertiesFile properties) {
|
||||
this.innerSeams = properties.getBoolean("innerSeams", false);
|
||||
this.orientation = properties.getString("orientation", "none").toLowerCase();
|
||||
|
||||
for (int i = 0; i < 47; i++) {
|
||||
String val = properties.getString("ctm." + i, "");
|
||||
if (!val.isEmpty()) {
|
||||
try {
|
||||
int tileIdx = Integer.parseInt(val.trim());
|
||||
tileReplacementMap.put(i, tileIdx);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getInnerSeams() {
|
||||
return innerSeams;
|
||||
}
|
||||
|
||||
public String getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public Int2IntMap getTileReplacementMap() {
|
||||
return tileReplacementMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
public final class CompactCtmQuadProcessor {
|
||||
|
||||
private static final int LEFT = 0;
|
||||
private static final int UP_LEFT = 1;
|
||||
private static final int UP = 2;
|
||||
private static final int UP_RIGHT = 3;
|
||||
private static final int RIGHT = 4;
|
||||
private static final int DOWN_RIGHT = 5;
|
||||
private static final int DOWN = 6;
|
||||
private static final int DOWN_LEFT = 7;
|
||||
|
||||
private static final int SPRITE_DEFAULT = 0;
|
||||
private static final int SPRITE_ALL = 1;
|
||||
private static final int SPRITE_HORIZONTAL = 2;
|
||||
private static final int SPRITE_VERTICAL = 3;
|
||||
private static final int SPRITE_MIXED = 4;
|
||||
|
||||
private static final int[] CTM_47_BY_CONNECTIONS = {
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
16,18,16,18,6,46,6,21,16,18,16,18,28,9,28,22,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
37,40,37,40,30,8,30,34,37,40,37,40,25,23,25,45,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
16,42,16,42,6,20,6,10,16,42,16,42,28,35,28,44,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
37,38,37,38,30,11,30,32,37,38,37,38,25,33,25,26
|
||||
};
|
||||
|
||||
/**
|
||||
* Face-local basis:
|
||||
* right = increasing U on the face
|
||||
* down = increasing V on the face
|
||||
*/
|
||||
private static final FaceBasis[] FACE_BASIS = {
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 0: Y-
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 1: Y+
|
||||
new FaceBasis(-1, 0, 0, 0, -1, 0), // 2: Z-
|
||||
new FaceBasis( 1, 0, 0, 0, -1, 0), // 3: Z+
|
||||
new FaceBasis( 0, 0, 1, 0, -1, 0), // 4: X-
|
||||
new FaceBasis( 0, 0, -1, 0, -1, 0) // 5: X+
|
||||
};
|
||||
|
||||
/**
|
||||
* Neighbor offsets in face-local coordinates:
|
||||
* 0 = left, 1 = up-left, 2 = up, 3 = up-right,
|
||||
* 4 = right, 5 = down-right, 6 = down, 7 = down-left
|
||||
*/
|
||||
private static final int[] RIGHT_FACTOR = {-1, -1, 0, 1, 1, 1, 0, -1};
|
||||
private static final int[] DOWN_FACTOR = { 0, -1, -1, -1, 0, 1, 1, 1};
|
||||
|
||||
/**
|
||||
* Quadrant side references:
|
||||
* 0 = top-left
|
||||
* 1 = top-right
|
||||
* 2 = bottom-right
|
||||
* 3 = bottom-left
|
||||
*/
|
||||
private static final int[] QUAD_A = {LEFT, UP, RIGHT, DOWN};
|
||||
private static final int[] QUAD_B = {UP, RIGHT, DOWN, LEFT};
|
||||
private static final int[] QUAD_DIAG = {UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT};
|
||||
|
||||
private final IIcon[] sprites;
|
||||
private final CompactConnectingCtmProperties props;
|
||||
|
||||
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props) {
|
||||
this.sprites = new IIcon[5];
|
||||
if (sprites != null) {
|
||||
System.arraycopy(sprites, 0, this.sprites, 0, Math.min(sprites.length, this.sprites.length));
|
||||
}
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public boolean processFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon origIcon) {
|
||||
int connections = CtmConnectionHelper.getConnections(world, x, y, z, face, props.getInnerSeams());
|
||||
|
||||
int ctmIndex = CTM_47_BY_CONNECTIONS[connections & 0xFF];
|
||||
IIcon replacement = getReplacementIcon(ctmIndex);
|
||||
if (replacement != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connections == 0 || connections == 0xFF) {
|
||||
IIcon sprite = sprites[getCompactSpriteIndex(connections)];
|
||||
if (sprite != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, sprite);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
renderSplitFace(rb, world, x, y, z, face, connections);
|
||||
return true;
|
||||
}
|
||||
|
||||
private IIcon getReplacementIcon(int ctmIndex) {
|
||||
Int2IntMap map = props.getTileReplacementMap();
|
||||
if (map == null || !map.containsKey(ctmIndex)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int spriteIndex = map.get(ctmIndex);
|
||||
if (spriteIndex < 0 || spriteIndex >= sprites.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprites[spriteIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact 5-sprite variant:
|
||||
* 0 = default / isolated / mixed
|
||||
* 1 = all neighbors
|
||||
* 2 = horizontal line
|
||||
* 3 = vertical line
|
||||
*/
|
||||
private int getCompactSpriteIndex(int connections) {
|
||||
if (connections == 0xFF) {
|
||||
return SPRITE_ALL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << LEFT) | (1 << RIGHT))) == ((1 << LEFT) | (1 << RIGHT))) {
|
||||
return SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << UP) | (1 << DOWN))) == ((1 << UP) | (1 << DOWN))) {
|
||||
return SPRITE_VERTICAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
private void renderSplitFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, int connections) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
|
||||
double minX = rb.renderMinX;
|
||||
double minY = rb.renderMinY;
|
||||
double minZ = rb.renderMinZ;
|
||||
double maxX = rb.renderMaxX;
|
||||
double maxY = rb.renderMaxY;
|
||||
double maxZ = rb.renderMaxZ;
|
||||
|
||||
for (int q = 0; q < 4; q++) {
|
||||
IIcon icon = sprites[getQuadrantSprite(q, connections)];
|
||||
if (icon == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setQuadrantBounds(rb, face, q, minX, minY, minZ, maxX, maxY, maxZ);
|
||||
rb.overrideBlockTexture = icon;
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
}
|
||||
|
||||
rb.overrideBlockTexture = null;
|
||||
rb.setRenderBounds(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
private void renderWholeFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon icon) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
IIcon saved = rb.overrideBlockTexture;
|
||||
rb.overrideBlockTexture = icon;
|
||||
try {
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
} finally {
|
||||
rb.overrideBlockTexture = saved;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderFace(RenderBlocks rb, Block block, int x, int y, int z, int face, IIcon icon) {
|
||||
boolean originalFlip = rb.field_152631_f;
|
||||
rb.field_152631_f = (face == 2 || face == 5);
|
||||
try {
|
||||
switch (face) {
|
||||
case 0 -> rb.renderFaceYNeg(block, x, y, z, icon);
|
||||
case 1 -> rb.renderFaceYPos(block, x, y, z, icon);
|
||||
case 2 -> rb.renderFaceZNeg(block, x, y, z, icon);
|
||||
case 3 -> rb.renderFaceZPos(block, x, y, z, icon);
|
||||
case 4 -> rb.renderFaceXNeg(block, x, y, z, icon);
|
||||
case 5 -> rb.renderFaceXPos(block, x, y, z, icon);
|
||||
}
|
||||
} finally {
|
||||
rb.field_152631_f = originalFlip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quadrants are always interpreted as:
|
||||
* 0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
|
||||
* in the face-local view.
|
||||
*/
|
||||
private void setQuadrantBounds(RenderBlocks rb, int face, int quadrant,
|
||||
double minX, double minY, double minZ,
|
||||
double maxX, double maxY, double maxZ) {
|
||||
double midX = (minX + maxX) * 0.5;
|
||||
double midY = (minY + maxY) * 0.5;
|
||||
double midZ = (minZ + maxZ) * 0.5;
|
||||
|
||||
switch (face) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, minY, minZ, midX, maxY, midZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(midX, minY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(midX, minY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, midZ, midX, maxY, maxZ);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (quadrant == 0) rb.setRenderBounds(midX, midY, minZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, minZ, midX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, minZ, midX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(midX, minY, minZ, maxX, midY, maxZ);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, minZ, midX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(midX, midY, minZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(midX, minY, minZ, maxX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, minZ, midX, midY, maxZ);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, midZ, maxX, midY, maxZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, minZ, maxX, midY, midZ);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (quadrant == 0) rb.setRenderBounds(minX, midY, midZ, maxX, maxY, maxZ);
|
||||
if (quadrant == 1) rb.setRenderBounds(minX, midY, minZ, maxX, maxY, midZ);
|
||||
if (quadrant == 2) rb.setRenderBounds(minX, minY, minZ, maxX, midY, midZ);
|
||||
if (quadrant == 3) rb.setRenderBounds(minX, minY, midZ, maxX, midY, maxZ);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A quadrant is built from two cardinal sides and the diagonal between them.
|
||||
*/
|
||||
private int getQuadrantSprite(int quadrant, int connections) {
|
||||
int sideA = QUAD_A[quadrant];
|
||||
int sideB = QUAD_B[quadrant];
|
||||
int diag = QUAD_DIAG[quadrant];
|
||||
|
||||
boolean connA = (connections & (1 << sideA)) != 0;
|
||||
boolean connB = (connections & (1 << sideB)) != 0;
|
||||
|
||||
if (connA && connB) {
|
||||
return (connections & (1 << diag)) != 0 ? SPRITE_ALL : SPRITE_MIXED;
|
||||
}
|
||||
|
||||
if (connA ^ connB) {
|
||||
int connectedBit = connA ? sideA : sideB;
|
||||
return (connectedBit == LEFT || connectedBit == RIGHT) ? SPRITE_VERTICAL : SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
@Desugar
|
||||
private record FaceBasis(int rightX, int rightY, int rightZ, int downX, int downY, int downZ) { }
|
||||
|
||||
private static final class CtmConnectionHelper {
|
||||
|
||||
public static int getConnections(IBlockAccess world, int x, int y, int z, int face, boolean innerSeams) {
|
||||
Block self = world.getBlock(x, y, z);
|
||||
FaceBasis basis = FACE_BASIS[face];
|
||||
|
||||
int bits = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int dx = RIGHT_FACTOR[i] * basis.rightX + DOWN_FACTOR[i] * basis.downX;
|
||||
int dy = RIGHT_FACTOR[i] * basis.rightY + DOWN_FACTOR[i] * basis.downY;
|
||||
int dz = RIGHT_FACTOR[i] * basis.rightZ + DOWN_FACTOR[i] * basis.downZ;
|
||||
|
||||
int nx = x + dx;
|
||||
int ny = y + dy;
|
||||
int nz = z + dz;
|
||||
|
||||
if (world.getBlock(nx, ny, nz) != self) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (innerSeams && !shouldRenderInnerSeam(world, nx, ny, nz, face)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bits |= (1 << i);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static boolean shouldRenderInnerSeam(IBlockAccess world, int x, int y, int z, int face) {
|
||||
int[] normal = RenderBlockState.NORMALS[face];
|
||||
int sx = x + normal[0];
|
||||
int sy = y + normal[1];
|
||||
int sz = z + normal[2];
|
||||
return world.getBlock(sx, sy, sz).shouldSideBeRendered(world, sx, sy, sz, face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.resource.TexturePackAPI;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
|
||||
abstract class TileOverride implements Comparable<TileOverride> {
|
||||
public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
@@ -80,6 +80,7 @@ abstract class TileOverride implements Comparable<TileOverride> {
|
||||
tileLoader);
|
||||
case "sandstone", "top" -> override = new TileOverrideImpl.Top(properties, tileLoader);
|
||||
case "repeat", "pattern" -> override = new TileOverrideImpl.Repeat(properties, tileLoader);
|
||||
case "ctm_compact", "compact" -> override = new TileOverrideImpl.CTMCompact(properties, tileLoader);
|
||||
default -> properties.error("unknown method \"%s\"", method);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import com.prupe.mcpatcher.mal.resource.PropertiesFile;
|
||||
import com.prupe.mcpatcher.mal.tile.TileLoader;
|
||||
import com.prupe.mcpatcher.mal.util.WeightedIndex;
|
||||
|
||||
class TileOverrideImpl {
|
||||
public class TileOverrideImpl {
|
||||
|
||||
final static class CTM extends TileOverride {
|
||||
|
||||
@@ -481,4 +481,49 @@ class TileOverrideImpl {
|
||||
return icons[0];
|
||||
}
|
||||
}
|
||||
|
||||
final public static class CTMCompact extends TileOverride {
|
||||
|
||||
private final CompactConnectingCtmProperties properties;
|
||||
private volatile CompactCtmQuadProcessor processor;
|
||||
|
||||
CTMCompact(PropertiesFile propertiesFile, TileLoader tileLoader) {
|
||||
super(propertiesFile, tileLoader);
|
||||
this.properties = new CompactConnectingCtmProperties(propertiesFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethod() {
|
||||
return "compact";
|
||||
}
|
||||
|
||||
@Override
|
||||
String checkTileMap() {
|
||||
return getNumberOfTiles() == 5 ? null : "requires exactly 5 tiles";
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean requiresFace() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
IIcon getTileWorld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
return origIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
IIcon getTileHeld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
return icons.length > 0 ? icons[0] : origIcon;
|
||||
}
|
||||
|
||||
public CompactCtmQuadProcessor getProcessor() {
|
||||
CompactCtmQuadProcessor p = this.processor;
|
||||
if (p == null) {
|
||||
p = new CompactCtmQuadProcessor(icons, properties);
|
||||
this.processor = p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,8 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
}
|
||||
|
||||
public TileOverride go(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
|
||||
this.renderBlockState = renderBlockState;
|
||||
renderBlockState.setFilter(null);
|
||||
currentIcon = origIcon;
|
||||
@@ -121,6 +123,9 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
TileOverride override = next();
|
||||
IIcon newIcon = getTile(override, renderBlockState, origIcon);
|
||||
if (newIcon != null) {
|
||||
if (override instanceof TileOverrideImpl.CTMCompact compact) {
|
||||
CTMUtils.setCurrentCompact(compact);
|
||||
}
|
||||
lastMatchedOverride = override;
|
||||
skipOverrides.add(override);
|
||||
currentIcon = newIcon;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package net.coderbot.iris;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.Tags;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
@@ -13,7 +13,6 @@ import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.block_context.BlockContextHolder;
|
||||
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
|
||||
import net.coderbot.iris.celeritas.IrisCeleritasShaderProvider;
|
||||
import net.coderbot.iris.compat.dh.DHCompat;
|
||||
@@ -915,35 +914,25 @@ public class Iris {
|
||||
ClientRegistry.registerKeyBinding(shaderpackScreenKeybind);
|
||||
}
|
||||
|
||||
static BlockContextHolder contextHolder;
|
||||
|
||||
private static int getShaderMaterialOverrideId(Block block, int meta) {
|
||||
if (contextHolder == null) {
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
|
||||
if (blockMetaMatches == null) {
|
||||
return -1;
|
||||
}
|
||||
contextHolder = new BlockContextHolder(blockMetaMatches);
|
||||
|
||||
}
|
||||
contextHolder.set(block, meta, (short) block.getRenderType());
|
||||
return contextHolder.blockId;
|
||||
}
|
||||
|
||||
public static void setShaderMaterialOverride(Block block, int meta) {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
int blockId = getShaderMaterialOverrideId(block, meta);
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches = BlockRenderingSettings.INSTANCE.getBlockMetaMatches();
|
||||
if (blockMetaMatches == null)
|
||||
return;
|
||||
|
||||
if (TessellatorManager.get() instanceof CapturingTessellator tess)
|
||||
tess.setShaderBlockId(blockId);
|
||||
final Int2IntMap metaMap = blockMetaMatches.get(block);
|
||||
final int blockId = metaMap != null ? metaMap.get(meta) : -1;
|
||||
|
||||
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
|
||||
tess.angelica$setShaderOverrideBlockId((short) blockId);
|
||||
}
|
||||
|
||||
public static void resetShaderMaterialOverride() {
|
||||
if (!enabled)
|
||||
return;
|
||||
if (TessellatorManager.get() instanceof CapturingTessellator tess)
|
||||
tess.setShaderBlockId(-1);
|
||||
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
|
||||
tess.angelica$setShaderOverrideBlockId((short) -1);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package net.coderbot.iris.block_context;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
/**
|
||||
* Holds block context for shader material ID lookups.
|
||||
*/
|
||||
public class BlockContextHolder {
|
||||
private final Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches;
|
||||
public int blockId;
|
||||
public short renderType;
|
||||
|
||||
public BlockContextHolder(Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches) {
|
||||
this.blockMetaMatches = blockMetaMatches;
|
||||
this.blockId = -1;
|
||||
this.renderType = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the material ID for a block.
|
||||
* @param block The block
|
||||
* @param meta The block metadata
|
||||
* @param renderType The render type
|
||||
*/
|
||||
public void set(Block block, int meta, short renderType) {
|
||||
Int2IntMap metaMap = this.blockMetaMatches != null ? this.blockMetaMatches.get(block) : null;
|
||||
int id = metaMap != null ? metaMap.get(meta) : -1;
|
||||
|
||||
this.blockId = id;
|
||||
this.renderType = renderType;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.blockId = -1;
|
||||
this.renderType = -1;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -75,7 +75,7 @@ public class HandRenderer {
|
||||
|
||||
return mc.entityRenderer.debugViewDirection <= 0 &&
|
||||
mc.gameSettings.thirdPersonView == 0 &&
|
||||
!camera.getEntity().isPlayerSleeping() &&
|
||||
!camera.isSleeping() &&
|
||||
!mc.gameSettings.hideGUI &&
|
||||
!GameModeUtil.isSpectator() &&
|
||||
!mc.playerController.enableEverythingIsScrewedUpMode();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.coderbot.iris.shaderpack;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@@ -58,14 +59,42 @@ public class IdMap {
|
||||
private Map<NamespacedId, BlockRenderType> blockRenderTypeMap;
|
||||
|
||||
IdMap(Path shaderPath, ShaderPackOptions shaderPackOptions, Iterable<StringPair> environmentDefines) {
|
||||
itemIdMap = loadProperties(shaderPath, "item.properties", shaderPackOptions, environmentDefines).map(IdMap::parseItemIdMap).orElse(Object2IntMaps.emptyMap());
|
||||
// Check if block.properties has a dedicated 1.7.10 section
|
||||
String rawBlockProperties = readProperties(shaderPath, "block.properties");
|
||||
boolean hasLegacySection = rawBlockProperties != null
|
||||
&& rawBlockProperties.contains("MC_VERSION") && rawBlockProperties.contains("10710");
|
||||
|
||||
entityIdMap = loadProperties(shaderPath, "entity.properties", shaderPackOptions, environmentDefines).map(IdMap::parseEntityIdMap).orElse(Object2IntMaps.emptyMap());
|
||||
Iterable<StringPair> resolvedDefines;
|
||||
if (hasLegacySection) {
|
||||
// Pack has a 1.7.10 section
|
||||
resolvedDefines = environmentDefines;
|
||||
loadProperties(shaderPath, "block.properties", shaderPackOptions, environmentDefines).ifPresent(blockProperties -> {
|
||||
blockPropertiesMap = parseBlockMap(blockProperties, "block.", "block.properties");
|
||||
blockRenderTypeMap = parseRenderTypeMap(blockProperties, "layer.", "block.properties");
|
||||
});
|
||||
} else {
|
||||
// No 1.7.10 section, use modern MC_VERSION and convert entries
|
||||
ArrayList<StringPair> modernDefines = new ArrayList<>();
|
||||
for (StringPair define : environmentDefines) {
|
||||
if (!"MC_VERSION".equals(define.getKey())) {
|
||||
modernDefines.add(define);
|
||||
}
|
||||
}
|
||||
|
||||
loadProperties(shaderPath, "block.properties", shaderPackOptions, environmentDefines).ifPresent(blockProperties -> {
|
||||
blockPropertiesMap = parseBlockMap(blockProperties, "block.", "block.properties");
|
||||
blockRenderTypeMap = parseRenderTypeMap(blockProperties, "layer.", "block.properties");
|
||||
});
|
||||
String modernVersion = AngelicaConfig.modernFallbackMcVersion > 0
|
||||
? String.valueOf(AngelicaConfig.modernFallbackMcVersion) : "260101";
|
||||
modernDefines.add(new StringPair("MC_VERSION", modernVersion));
|
||||
resolvedDefines = modernDefines;
|
||||
|
||||
loadProperties(shaderPath, "block.properties", shaderPackOptions, modernDefines).ifPresent(blockProperties -> {
|
||||
blockPropertiesMap = parseBlockMap(blockProperties, "block.", "block.properties");
|
||||
blockRenderTypeMap = parseRenderTypeMap(blockProperties, "layer.", "block.properties");
|
||||
blockPropertiesMap = LegacyIdMap.convertModernBlockEntries(blockPropertiesMap);
|
||||
});
|
||||
}
|
||||
|
||||
itemIdMap = loadProperties(shaderPath, "item.properties", shaderPackOptions, resolvedDefines).map(IdMap::parseItemIdMap).orElse(Object2IntMaps.emptyMap());
|
||||
entityIdMap = loadProperties(shaderPath, "entity.properties", shaderPackOptions, resolvedDefines).map(IdMap::parseEntityIdMap).orElse(Object2IntMaps.emptyMap());
|
||||
|
||||
// TODO: Properly override block render layers
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.coderbot.iris.shaderpack;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.Function;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.coderbot.iris.shaderpack.materialmap.BlockEntry;
|
||||
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
|
||||
|
||||
@@ -10,6 +11,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LegacyIdMap {
|
||||
private static final ImmutableList<String> COLORS =
|
||||
@@ -19,6 +21,11 @@ public class LegacyIdMap {
|
||||
private static final ImmutableList<String> WOOD_TYPES =
|
||||
ImmutableList.of("oak", "birch", "jungle", "spruce", "acacia", "dark_oak");
|
||||
|
||||
private static final Map<String, String> MODERN_TO_LEGACY_BLOCK_IDS = Map.of(
|
||||
"grass", "tallgrass",
|
||||
"grass_block", "grass"
|
||||
);
|
||||
|
||||
public static void addLegacyValues(Int2ObjectMap<List<BlockEntry>> blockIdMap) {
|
||||
add(blockIdMap, 1, block("stone"), block("granite"), block("diorite"), block("andesite"));
|
||||
add(blockIdMap, 2, block("grass_block"));
|
||||
@@ -78,6 +85,32 @@ public class LegacyIdMap {
|
||||
// TODO: 76 -> redstone_torch (on)
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<List<BlockEntry>> convertModernBlockEntries(Int2ObjectMap<List<BlockEntry>> blockIdMap) {
|
||||
Int2ObjectMap<List<BlockEntry>> converted = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
blockIdMap.forEach((intId, entries) -> {
|
||||
List<BlockEntry> convertedEntries = new ArrayList<>(entries.size());
|
||||
for (BlockEntry entry : entries) {
|
||||
NamespacedId id = entry.getId();
|
||||
if (!"minecraft".equals(id.getNamespace())) {
|
||||
convertedEntries.add(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
String convertedName = MODERN_TO_LEGACY_BLOCK_IDS.get(id.getName());
|
||||
if (convertedName == null) {
|
||||
convertedEntries.add(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
convertedEntries.add(new BlockEntry(new NamespacedId(id.getNamespace(), convertedName), entry.getMetas()));
|
||||
}
|
||||
converted.put(intId, Collections.unmodifiableList(convertedEntries));
|
||||
});
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
private static BlockEntry block(String name) {
|
||||
return new BlockEntry(new NamespacedId("minecraft", name), Collections.emptySet());
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ options.chunks=%s 区块
|
||||
options.entityShadows=实体阴影
|
||||
options.droppedItems=%s 个物品
|
||||
options.droppedItems.max=无限制
|
||||
|
||||
sodium.option_impact.low=低
|
||||
sodium.option_impact.medium=中
|
||||
sodium.option_impact.high=高
|
||||
@@ -12,19 +13,20 @@ sodium.option_impact.varies=视情况而定
|
||||
sodium.options.pages.quality=质量
|
||||
sodium.options.pages.performance=性能
|
||||
sodium.options.pages.advanced=高级
|
||||
sodium.options.pages.appearance=外观
|
||||
sodium.options.view_distance.tooltip=可视距离 - 控制地形的渲染距离。距离越短,地形渲染越少,帧率(FPS)提升越明显。
|
||||
sodium.options.simulation_distance.tooltip=模拟距离 - 控制地形和实体加载和更新的距离。更短的距离可以减少内部服务器的负载并提高帧率(FPS)。
|
||||
sodium.options.brightness.tooltip=控制游戏的亮度(伽玛值)。
|
||||
sodium.options.clouds.name=云
|
||||
sodium.options.clouds.tooltip=控制云是否可见。
|
||||
sodium.options.gui_scale.tooltip=设置要用于用户界面的最大缩放倍率。如果设为“自动(0)”,则始终使用最大的缩放倍率。
|
||||
sodium.options.gui_scale.tooltip=设置要用于用户界面的最大缩放倍率。如果设为"自动",则始终使用最大的缩放倍率。
|
||||
sodium.options.fullscreen.tooltip=如果启用,游戏将以全屏显示(如果支持)。
|
||||
sodium.options.v_sync.tooltip=如果启用,游戏的帧率(FPS)将与显示器的刷新率同步,从而以整体输入延迟为代价获得更流畅的体验。如果你的系统性能较差,此设置反而可能会降低帧率。
|
||||
sodium.options.fps_limit.tooltip=限制帧率(FPS)。这有助于降低多任务处理时的功耗和一般系统负载。如果启用了垂直同步,除非它低于显示器的刷新率,否则此设置将被忽略。
|
||||
sodium.options.fps_limit.tooltip=限制最大帧率(FPS)。这有助于降低多任务处理时的功耗和一般系统负载。如果启用了垂直同步,除非它低于显示器的刷新率,否则此设置将被忽略。
|
||||
sodium.options.view_bobbing.tooltip=如果启用,玩家的视角将在移动时不断摇晃。在游戏时感到晕眩的玩家可以禁用此项以提升游戏体验。
|
||||
sodium.options.attack_indicator.tooltip=控制攻击指示器在屏幕上的显示位置。
|
||||
sodium.options.autosave_indicator.tooltip=如果启用,当游戏将存档保存到硬盘时将显示一个提示。
|
||||
sodium.options.graphics_quality.tooltip=默认图形质量控制一些传统选项,并且是模组兼容性所必需的。如果下面的选项保留为“默认”,则它们将使用此设置。
|
||||
sodium.options.graphics_quality.tooltip=默认图形质量控制一些传统选项,并且是模组兼容性所必需的。如果下面的选项保留为"默认",则它们将使用此设置。
|
||||
sodium.options.clouds_quality.tooltip=控制天空中云渲染的质量。
|
||||
sodium.options.weather_quality.tooltip=控制雨雪效果的质量。
|
||||
sodium.options.leaves_quality.name=树叶质量
|
||||
@@ -38,7 +40,7 @@ sodium.options.particle_quality.tooltip=控制可同时在画面上渲染的最
|
||||
sodium.options.smooth_lighting.tooltip=控制方块是否平滑光照和渲染。这会略微增加重新加载区块所需的时间,但不会影响帧率(FPS)。
|
||||
sodium.options.biome_blend.value=%s 个方块
|
||||
sodium.options.biome_blend.tooltip=生物群系颜色互相平滑混合的距离,单位为方块。使用更高的值会极大增加区块加载或更新的用时,而对画质的提升会逐渐递减。
|
||||
sodium.options.entity_distance.tooltip=实体渲染的距离修正乘数。更小的值会减少能被渲染的实体离玩家的最大距离,更大的值则会增加这个距离。
|
||||
sodium.options.entity_distance.tooltip=实体渲染距离的修正系数。更小的值会减少可被渲染的实体离玩家的最大距离,更大的值则会增加这个距离。
|
||||
sodium.options.entity_shadows.tooltip=如果启用,将在生物和其他实体下方渲染简单的阴影。
|
||||
sodium.options.vignette.name=晕影
|
||||
sodium.options.vignette.tooltip=如果启用,将在玩家的画面上显示晕影效果。除非你的显卡像素填充率有限,否则这不太可能对帧率产生影响。
|
||||
@@ -46,11 +48,11 @@ sodium.options.mipmap_levels.tooltip=控制多级纹理的生成等级。较高
|
||||
sodium.options.use_chunk_multidraw.name=使用区块多重绘制
|
||||
sodium.options.use_chunk_multidraw.tooltip=多重绘制允许通过更少的绘制请求来进行多区块渲染,在大大减少了渲染世界时的CPU负载的同时也可能提高了GPU利用率。此选项可能会让一些图形驱动程序出现问题,因此如果遇到相关问题,应尝试禁用它。
|
||||
sodium.options.use_vertex_objects.name=使用顶点数组对象
|
||||
sodium.options.use_vertex_objects.tooltip=通过将如何渲染有关顶点数据的信息移动到驱动程序中,让游戏可以优化相同对象的重复渲染,有助于提高性能。通常不应该禁此选项,除非你使用了不兼容的模组。
|
||||
sodium.options.use_vertex_objects.tooltip=通过将与顶点数据渲染方式相关的信息存储到驱动程序中,以优化对相同对象的重复渲染并提高性能。通常应保持开启,除非你使用了不兼容的模组
|
||||
sodium.options.use_block_face_culling.name=使用方块面剔除
|
||||
sodium.options.use_block_face_culling.tooltip=如果启用,则只会渲染面向相机一侧的方块面。这可以在早期忽略大量方块面的渲染,从而节省GPU上的内存带宽和时间。某些资源包可能会在此选项上出现问题,因此如果你在方块中看到空洞,请尝试禁用它。
|
||||
sodium.options.use_compact_vertex_format.name=使用压缩顶点格式
|
||||
sodium.options.use_compact_vertex_format.tooltip=如果启用,将使用更紧凑的顶点格式来渲染方块。这可以显著降低显存使用量和带宽要求,尤其是集显,但由于降低了位置和纹理坐标属性的精度,可能会与一些资源包发生冲突。
|
||||
sodium.options.use_compact_vertex_format.tooltip=如果启用,将使用更紧凑的顶点格式来渲染方块。这能显著降低显存占用和带宽需求,对集成显卡尤其有效。但由于降低了顶点位置和纹理坐标的精度,可能与部分纹理包不兼容
|
||||
sodium.options.use_fog_occlusion.name=使用雾遮挡剔除
|
||||
sodium.options.use_fog_occlusion.tooltip=如果启用,确定被雾效果完全遮挡的块将不会被渲染,有助于提升性能。当雾效果较重时(例如在水下时),提升可能会更加显著,但在某些情况下可能会导致天空和雾之间出现不良的视觉瑕疵。
|
||||
sodium.options.use_entity_culling.name=使用实体剔除
|
||||
@@ -67,7 +69,7 @@ sodium.options.cpu_render_ahead_limit.value=%s 帧
|
||||
sodium.options.allow_direct_memory_access.name=允许直接内存访问
|
||||
sodium.options.allow_direct_memory_access.tooltip=如果启用,一些关键代码将被允许通过直接读写内存来提高性能。这通常会大大减少区块和实体渲染的CPU开销,但会使诊断某些错误和崩溃变得更加困难。只有您被要求这么做或知道在做什么时,才应该禁用它。
|
||||
sodium.options.ignore_driver_blacklist.name=忽略驱动程序冲突
|
||||
sodium.options.ignore_driver_blacklist.tooltip=如果启用,已知的硬件/驱动程序配置不兼容性将被忽略,以允许您启用那些可能导致游戏问题的配置.请勿更改此项除非您确切知道您在做什么.更改此选项后,必须保存、关闭,然后重新打开设置菜单。
|
||||
sodium.options.ignore_driver_blacklist.tooltip=如果启用,已知的硬件/驱动程序配置不兼容性将被忽略,以允许您启用那些可能导致游戏问题的配置。除非您确切知道您在做什么,否则请勿更改此项。更改此选项后,必须保存、关闭,然后重新打开设置菜单。
|
||||
sodium.options.enable_memory_tracing.name=启用内存跟踪
|
||||
sodium.options.enable_memory_tracing.tooltip=调试功能。如果启用,堆栈跟踪将与内存分配一起收集,有助于在检测到内存泄漏时改进诊断信息。
|
||||
sodium.options.performance_impact_string=性能影响:%s
|
||||
@@ -83,16 +85,23 @@ sodium.options.always_defer_chunk_updates.name=始终推迟区块更新
|
||||
sodium.options.always_defer_chunk_updates.tooltip=如果启用,渲染将永远不会等待区块更新完成,即使它们很重要。这在某些情况下可以极大地提高帧率(FPS),但可能会在游戏时产生明显的视觉滞后。
|
||||
sodium.options.use_no_error_context.name=使用禁用错误检查的上下文
|
||||
sodium.options.use_no_error_context.tooltip=如果启用,OpenGL日志将不包含详细错误信息。这可能略微提高性能,但也可能因为无法精确处理OpenGL错误而导致游戏崩溃。如遇到不明原因的突然崩溃,则应禁用此选项。
|
||||
sodium.options.async_occlusion_mode.name=异步遮挡剔除
|
||||
sodium.options.async_occlusion_mode.tooltip=控制哪些渲染通道使用异步的遮挡剔除。ONLY_SHADOW(默认)仅对阴影通道进行异步遮挡,保持主渲染列表同步。EVERYTHING 对所有通道进行异步,在快速移动视角时可能导致1帧的可见性延迟。NONE 完全禁用异步遮挡
|
||||
sodium.options.use_occlusion_culling.name=遮挡剔除
|
||||
sodium.options.use_occlusion_culling.tooltip=如果启用,被其他区块遮挡的区块将不会被渲染。禁用此选项会显著降低性能,但可用于调试。
|
||||
sodium.options.threads.value=%s 线程
|
||||
sodium.options.default=默认
|
||||
sodium.options.buttons.undo=撤销
|
||||
sodium.options.buttons.apply=应用
|
||||
sodium.options.buttons.donate=给JellySquid买杯咖啡!
|
||||
|
||||
options.dynamic_fov=动态视场
|
||||
options.dynamic_fov.tooltip=如果启用,游戏视场将根据玩家速度发生变化.
|
||||
options.dynamic_fov.tooltip=如果启用,游戏视场将根据玩家速度发生变化。
|
||||
options.framerate=%s FPS
|
||||
|
||||
iris.shaders.toggled=光影包已切换为 %s!
|
||||
iris.shaders.disabled=光影包已禁用!
|
||||
|
||||
options.iris.apply=应用
|
||||
options.iris.refresh=刷新
|
||||
options.iris.openShaderPackFolder=打开光影包文件夹……
|
||||
@@ -101,15 +110,15 @@ options.iris.shaderPackList=光影包列表……
|
||||
options.iris.refreshShaderPacks=刷新光影包
|
||||
options.iris.shaderPackSelection=光影包……
|
||||
options.iris.shaderPackSelection.title=光影
|
||||
options.iris.shaderPackSelection.addedPack=已添加光影包“%s”
|
||||
options.iris.shaderPackSelection.addedPack=已添加光影包"%s"
|
||||
options.iris.shaderPackSelection.addedPacks=已添加 %s 个光影包
|
||||
options.iris.shaderPackSelection.failedAdd=文件不是有效的光影包
|
||||
options.iris.shaderPackSelection.failedAddSingle=“%s”不是有效的光影包
|
||||
options.iris.shaderPackSelection.copyError=无法添加光影包“%s”
|
||||
options.iris.shaderPackSelection.copyErrorAlreadyExists=“%s”已经在光影包文件夹里了!
|
||||
options.iris.shaderPackSelection.failedAddSingle="%s"不是有效的光影包
|
||||
options.iris.shaderPackSelection.copyError=无法添加光影包"%s"
|
||||
options.iris.shaderPackSelection.copyErrorAlreadyExists="%s"已经在光影包文件夹里了!
|
||||
options.iris.shaderPackOptions.tooManyFiles=无法导入多个光影设置文件!
|
||||
options.iris.shaderPackOptions.failedImport=导入光影设置文件“%s”失败
|
||||
options.iris.shaderPackOptions.importedSettings=已从“%s”导入光影设置
|
||||
options.iris.shaderPackOptions.failedImport=导入光影设置文件"%s"失败
|
||||
options.iris.shaderPackOptions.importedSettings=已从"%s"导入光影设置
|
||||
options.iris.shaders.disabled=光影:已禁用
|
||||
options.iris.shaders.enabled=光影:已启用
|
||||
options.iris.shaders.nonePresent=光影:无光影包
|
||||
@@ -129,19 +138,46 @@ options.iris.shadowDistance.enabled=更改渲染阴影的最远距离。超出
|
||||
options.iris.shadowDistance.disabled=当前光影包已重设阴影渲染距离,无法更改
|
||||
options.iris.gui.hide=隐藏 GUI
|
||||
options.iris.gui.show=显示 GUI
|
||||
|
||||
options.dynamic_lights=动态光源
|
||||
options.dynamic_lights.tooltip=启用动态光源,模式选择:OFF-关闭,FASTEST-500ms延迟,FAST-250ms延迟,FANCY-无延迟
|
||||
options.dynamic_lights.tooltip=启用动态光源,模式选择:OFF-关闭,FASTEST-500ms延迟,FAST-250ms延迟,FANCY-50ms延迟,REALTIME-无延迟
|
||||
options.dynamic_lights_shader_force=强制启用动态光源
|
||||
options.dynamic_lights_shader_force.tooltip=启用-启用光影时仍显示动态光源;禁用-启用光影时禁用动态光源。
|
||||
options.dynamic_lights_frustum_culling=视锥剔除
|
||||
options.dynamic_lights_frustum_culling.tooltip=仅为屏幕上可见的光源重建区块。减少相机后方光源的GPU工作负载。
|
||||
options.dynamic_lights_adaptive_ticking=自适应更新
|
||||
options.dynamic_lights_adaptive_ticking.tooltip=降低远处和屏幕外光源的更新频率。与全局模式设置协同工作。
|
||||
options.dynamic_lights_slow_dist=低频更新距离
|
||||
options.dynamic_lights_slow_dist.tooltip=此距离外的光源更新频率降为每0.25秒一次。推荐:32格(约2个区块)
|
||||
options.dynamic_lights_slower_dist=更低频更新距离
|
||||
options.dynamic_lights_slower_dist.tooltip=此距离外的光源更新频率降为每0.5秒一次。推荐:64格(约4个区块)
|
||||
options.dynamic_lights_background_dist=视野外更新距离
|
||||
options.dynamic_lights_background_dist.tooltip=超出此距离后,光源更新频率降为每秒一次。同时适用于玩家镜头后方的光源。
|
||||
options.dynamic_lights_cull_timeout=剔除超时(刻)
|
||||
options.dynamic_lights_cull_timeout.tooltip=不可见的区块在强制重建前可等待的最大刻数。值越低响应越快但GPU负载越高
|
||||
options.dynamiclights.page=动态光源
|
||||
options.dynamiclights.entities=配置实体……
|
||||
options.dynamiclights.entities.title=动态光源 - 实体
|
||||
options.dynamiclights.entities.info=切换哪些实体类型发射动态光。部分条目用于模组兼容性。
|
||||
options.dynamiclights.entities.search=搜索(@模组, $开启, $关闭)
|
||||
options.dynamiclights.entity.tooltip=切换 %s 实体的动态光照。如果此类型实体产生不必要光照,禁用以提升性能。
|
||||
|
||||
options.angelica.search.no_results=无匹配选项
|
||||
options.angelica.fontconfig=字体配置...
|
||||
options.angelica.fontconfig.title=字体配置
|
||||
options.angelica.fontconfig.currentfonts=主字体:§e%s§r 备用字体:§b%s
|
||||
options.angelica.fontconfig.searchprompt=§7§o搜索字体...
|
||||
options.angelica.fontconfig.testareaprompt=§7§o测试区域...
|
||||
options.angelica.fontconfig.testareainfo=在下方输入文本以预览渲染效果。可使用&进行格式化(如&l代表粗体)
|
||||
options.angelica.fontconfig.testareaexample=&d&lHerobrine&r was &7&m&oNOT&r &nhere
|
||||
options.angelica.fontconfig.disable_custom_font=禁用自定义字体
|
||||
options.angelica.fontconfig.enable_custom_font=启用自定义字体
|
||||
options.angelica.fontconfig.reset_values=恢复默认配置
|
||||
options.angelica.fontconfig.no_fonts_warning=自定义字体在您的系统上不可用,查看日志以获取详情
|
||||
options.angelica.fontconfig.font_quality=字体渲染质量:%s
|
||||
options.angelica.fontconfig.shadow_offset=阴影偏移:%s
|
||||
options.angelica.fontconfig.shadow_copies=阴影副本数:%s
|
||||
options.angelica.fontconfig.bold_copies=粗体副本数:%s
|
||||
options.angelica.fontconfig.glyph_scale=字体缩放:%s
|
||||
options.angelica.fontconfig.glyph_aspect=字体宽高比:%s
|
||||
options.angelica.fontconfig.whitespace_scale=空白字符大小:%s
|
||||
@@ -150,21 +186,53 @@ options.angelica.fontconfig.font_aa_strength=字体抗锯齿强度:%s
|
||||
options.angelica.fontconfig.aamode.aa_none=抗锯齿:关闭
|
||||
options.angelica.fontconfig.aamode.aa_4x=抗锯齿:4x MSAA
|
||||
options.angelica.fontconfig.aamode.aa_16x=抗锯齿:16x MSAA
|
||||
options.angelica.fontconfig.custom_font_scale=自定义字体缩放:%s
|
||||
options.angelica.fontconfig.shadow_offset_unicode=Unicode阴影偏移:%s
|
||||
options.angelica.fontconfig.font_quality.tooltip=自定义字体渲染质量,使其不那么像素化,但会牺牲一些性能与内存占用。
|
||||
options.angelica.fontconfig.shadow_offset.tooltip=字体衬底阴影偏移的距离。同时影响§l粗体§r文本的绘制方式;如果看到重影的文本,请调低此值。
|
||||
options.angelica.fontconfig.glyph_scale.tooltip=字体大小的缩放比例。极端值可能使游戏难以游玩。
|
||||
options.angelica.fontconfig.glyph_aspect.tooltip=字体的的宽高比例
|
||||
options.angelica.fontconfig.shadow_offset.tooltip=字体衬底阴影偏移的距离。同时影响§l粗体§r文本的绘制方式。
|
||||
options.angelica.fontconfig.shadow_copies.tooltip=在文本后方绘制的阴影数量,逐层偏移以营造伪3D效果
|
||||
options.angelica.fontconfig.bold_copies.tooltip=渲染粗体文本时绘制的文本副本数量。使用较细的自定义字体时可改善粗体文本的外观
|
||||
options.angelica.fontconfig.glyph_scale.tooltip=每个字符的缩放比例。极端值可能使游戏难以游玩
|
||||
options.angelica.fontconfig.glyph_aspect.tooltip=每个字符的宽高比例
|
||||
options.angelica.fontconfig.whitespace_scale.tooltip=空白字符(如空格)的缩放比例
|
||||
options.angelica.fontconfig.glyph_spacing.tooltip=字符之间的间距
|
||||
options.angelica.fontconfig.font_aa_strength.tooltip=抗锯齿所使用的采样间距。过大可能导致字体看起来模糊,过小可能导致锯齿。请根据您的实际情况选择最佳值。
|
||||
options.angelica.fontconfig.aamode.tooltip=抗锯齿模式。可以显著改善缩放或斜角观看文本的外观和可读性,但会牺牲一些性能。
|
||||
options.angelica.fontconfig.custom_font_scale.tooltip=影响自定义字体缩放的系数。影响自定义字体缩放的系数。如果使用自定义字体后,文本相较于默认字体看起来过小或过大,请调整此项
|
||||
options.angelica.fontconfig.shadow_offset_unicode.tooltip=仅应用于Unicode字体的阴影偏移系数。例如,如果您了使用替换Unicode字体为仿原版风格的纹理包,请将此项设为1.0x
|
||||
options.angelica.hudCaching=HUD 缓存
|
||||
options.angelica.hudCaching.tooltip=将HUD缓存到帧缓冲区并在帧间复用以减少渲染开销。需要在启动时启用HUD缓存。[实验性]
|
||||
options.angelica.droppedItemLimit=掉落物渲染数量上限
|
||||
options.angelica.droppedItemLimit.tooltip=将被渲染的掉落物的最大数量。较小的值可以提高性能。
|
||||
options.angelica.mobSpawnerRenderDistance=刷怪笼渲染距离
|
||||
options.angelica.mobSpawnerRenderDistance.tooltip=刷怪笼内旋转实体的渲染距离
|
||||
options.angelica.itemdisplaylistcount=物品渲染器缓存限制
|
||||
options.angelica.itemdisplaylistcount.tooltip=优化物品渲染器中的缓存数量上限。较高的值将显著提高性能,但可能导致显存占用增加
|
||||
options.angelica.itemdisplaylistcount.tooltip=优化物品渲染器中的VBO缓存数量上限。较高的值将显著提高性能,但可能导致显存占用增加
|
||||
options.angelica.threadedChunkBuilding=多线程区块构建
|
||||
options.angelica.threadedChunkBuilding.tooltip=如果启用,区块网格化将多线程并行执行,以显著提高区块构建速度。如果您遇到使用自定义方块渲染的模组出现渲染问题,请禁用此项。需要重新加载渲染器才能生效
|
||||
|
||||
pack.iris.select.title=选择
|
||||
pack.iris.configure.title=配置
|
||||
label.iris.true=开启
|
||||
label.iris.false=关闭
|
||||
|
||||
sodium.options.use_render_pass_optimization.name=渲染通道优化
|
||||
sodium.options.use_render_pass_optimization.tooltip=分析贴图透明度,当贴图无需复杂渲染时自动降级渲染通道(例如:半透明降级为镂空,镂空降级为实体)。减少绘制调用和过度绘制开销
|
||||
|
||||
options.angelica.aggressiveChunkLoading=更激进地区块加载
|
||||
options.angelica.aggressiveChunkLoading.tooltip=区块加载后立即渲染,即使相邻区块尚未加载完毕。将提升最大渲染距离处区块的加载速度。在相邻区块加载后会自动重建。适用于渲染距离较低的服务器。
|
||||
options.angelica.disablef3=禁用F3附加信息显示
|
||||
options.angelica.disablef3.tooltip=禁用Angelica在F3调试界面的附加信息。
|
||||
|
||||
sodium.options.multidraw_mode.name=批量绘制模式
|
||||
sodium.options.multidraw_mode.tooltip=控制地形渲染的绘制命令的调度方式。§l直接§r使用glMultiDrawElementsBaseVertex(默认,GL 3.2+)。§l间接§r使用GPU间接渲染(GL 4.3+),可能在某些硬件上减少CPU开销。§l单独§r发出单独的绘制调用。
|
||||
sodium.options.multidraw_mode.direct=直接
|
||||
sodium.options.multidraw_mode.indirect=间接
|
||||
sodium.options.multidraw_mode.individual=单独
|
||||
sodium.options.upload_method.name=流式上传方法
|
||||
sodium.options.upload_method.tooltip=更改数据流式传输到GPU的方式。某些选项可能对某些驱动程序带来更好的性能
|
||||
sodium.options.upload_method.buffer_data=完整更新 (BufferData)
|
||||
sodium.options.upload_method.buffer_subdata=部分更新 (BufferSubData)
|
||||
sodium.options.upload_method.map_buffer=内存映射 (MapBufferRange)
|
||||
sodium.options.enable_deferred_batching.name=延迟粒子渲染
|
||||
sodium.options.enable_deferred_batching.tooltip=按层合并粒子绘制调用,减少绘制次数。如果粒子渲染不正确请禁用此项
|
||||
@@ -142,8 +142,12 @@ public abstract class MixinForgeHooksClient_CoreProfile {
|
||||
private static void angelica$reportContextFailure(Exception e) {
|
||||
LOGGER.error("FATAL: Failed to create OpenGL core profile context.");
|
||||
LOGGER.error("Error: {}", e != null ? e.getMessage() : "unknown");
|
||||
try {
|
||||
LOGGER.error("GPU: {}, Driver: {}", GLStateManager.glGetString(GL11.GL_RENDERER), GLStateManager.glGetString(GL11.GL_VERSION));
|
||||
} catch (Exception ignored) {}
|
||||
if (Display.isCreated()) {
|
||||
try {
|
||||
LOGGER.error("GPU: {}, Driver: {}", GLStateManager.glGetString(GL11.GL_RENDERER), GLStateManager.glGetString(GL11.GL_VERSION));
|
||||
} catch (Exception ignored) {}
|
||||
} else {
|
||||
LOGGER.error("GPU info: not available (no GL context)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.spongepowered.asm.mixin.Intrinsic;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
@@ -13,12 +16,43 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
* except when GTNHLib needs to intercept (display list compilation, DirectTessellator capture).
|
||||
*/
|
||||
@Mixin(Tessellator.class)
|
||||
public class MixinTessellator_CoreProfile {
|
||||
public class MixinTessellator_CoreProfile implements ITessellatorData {
|
||||
|
||||
@Shadow public boolean isDrawing;
|
||||
@Shadow public int vertexCount;
|
||||
@Shadow public int[] rawBuffer;
|
||||
@Shadow public int rawBufferIndex;
|
||||
@Shadow public int rawBufferSize;
|
||||
@Shadow public int drawMode;
|
||||
@Shadow public boolean hasTexture;
|
||||
@Shadow public boolean hasColor;
|
||||
@Shadow public boolean hasNormals;
|
||||
@Shadow public boolean hasBrightness;
|
||||
|
||||
@Shadow public void reset() {}
|
||||
|
||||
public void angelica$reset() {
|
||||
reset();
|
||||
}
|
||||
|
||||
@Inject(method = "draw", at = @At("HEAD"), cancellable = true)
|
||||
private void angelica$coreProfileDraw(CallbackInfoReturnable<Integer> cir) {
|
||||
// Let GTNHLib handle display list compilation and DirectTessellator capture
|
||||
if (TessellatorManager.shouldInterceptDraw((Tessellator)(Object)this)) return;
|
||||
cir.setReturnValue(TessellatorStreamingDrawer.draw((Tessellator)(Object)this));
|
||||
cir.setReturnValue(TessellatorStreamingDrawer.draw((ITessellatorData) this));
|
||||
}
|
||||
|
||||
@Override public boolean isDrawing() { return isDrawing; }
|
||||
@Override public void setDrawing(boolean drawing) { isDrawing = drawing; }
|
||||
@Override public int getVertexCount() { return vertexCount; }
|
||||
@Override public int[] getRawBuffer() { return rawBuffer; }
|
||||
@Override public int getRawBufferIndex() { return rawBufferIndex; }
|
||||
@Override public int getRawBufferSize() { return rawBufferSize; }
|
||||
@Override public void setRawBufferSize(int size) { rawBufferSize = size; }
|
||||
@Override public void setRawBuffer(int[] buffer) { rawBuffer = buffer; }
|
||||
@Override public int getDrawMode() { return drawMode; }
|
||||
@Override public boolean hasTexture() { return hasTexture; }
|
||||
@Override public boolean hasColor() { return hasColor; }
|
||||
@Override public boolean hasNormals() { return hasNormals; }
|
||||
@Override public boolean hasBrightness() { return hasBrightness; }
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
|
||||
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.compat.DriverCompatabilityCheck;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
|
||||
@@ -10,12 +10,6 @@ import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
|
||||
@Mixin(value = EntityRenderer.class, priority = 990)
|
||||
public abstract class MixinEntityRenderer {
|
||||
|
||||
@ModifyConstant(method = "renderWorld", constant = @Constant(doubleValue = 128.0D), expect = 2)
|
||||
double notFine$modifyCloudHeightCheck(double original) {
|
||||
return SettingsManager.cloudTranslucencyCheck;
|
||||
}
|
||||
|
||||
@ModifyArg(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false), index = 3)
|
||||
private float notFine$modifyFarPlane(float original) {
|
||||
return Math.max(original, SettingsManager.minimumFarPlaneDistance);
|
||||
|
||||
@@ -1,34 +1,48 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.notfine.optimization;
|
||||
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.renderer.tileentity.RenderItemFrame;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = RenderItemFrame.class)
|
||||
public abstract class MixinRenderItemFrame extends Render {
|
||||
@Mixin(RenderItemFrame.class)
|
||||
public class MixinRenderItemFrame {
|
||||
|
||||
private EntityItem cachedEntityItem;
|
||||
@Unique
|
||||
private EntityItem notfine$cachedEntityItem;
|
||||
|
||||
@Redirect(
|
||||
method = "func_82402_b(Lnet/minecraft/entity/item/EntityItemFrame;)V",
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "(Lnet/minecraft/world/World;DDDLnet/minecraft/item/ItemStack;)Lnet/minecraft/entity/item/EntityItem;",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
public EntityItem cacheEntityItem(World world, double x, double y, double z, ItemStack itemstack) {
|
||||
if(cachedEntityItem == null) {
|
||||
cachedEntityItem = new EntityItem(world, 0.0D, 0.0D, 0.0D, itemstack);
|
||||
ordinal = 0))
|
||||
private EntityItem cacheEntityItem(World world, double x, double y, double z, ItemStack itemstack) {
|
||||
if (notfine$cachedEntityItem == null) {
|
||||
notfine$cachedEntityItem = new EntityItem(world, x, y, z, itemstack);
|
||||
} else {
|
||||
cachedEntityItem.setEntityItemStack(itemstack);
|
||||
notfine$cachedEntityItem.setWorld(world);
|
||||
notfine$cachedEntityItem.setEntityItemStack(itemstack);
|
||||
}
|
||||
return cachedEntityItem;
|
||||
return notfine$cachedEntityItem;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "func_82402_b(Lnet/minecraft/entity/item/EntityItemFrame;)V",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lorg/lwjgl/opengl/GL11;glPopMatrix()V",
|
||||
remap = false,
|
||||
shift = At.Shift.AFTER))
|
||||
private void clearWorldRef(CallbackInfo ci, @Local(name = "entityitem") EntityItem entityitem) {
|
||||
entityitem.setWorld(null);
|
||||
entityitem.setEntityItemStack(null);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.prupe.mcpatcher.ctm.CTMUtils;
|
||||
import com.prupe.mcpatcher.ctm.CompactCtmQuadProcessor;
|
||||
import com.prupe.mcpatcher.ctm.TileOverrideImpl;
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
@@ -17,6 +20,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -177,4 +181,89 @@ public abstract class MixinRenderBlocks {
|
||||
@Local(ordinal = 0) Tessellator tessellator) {
|
||||
((StateAwareTessellator)tessellator).angelica$setAppliedAo(false);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public IBlockAccess blockAccess;
|
||||
|
||||
@Unique
|
||||
private boolean compactCtmRendering = false;
|
||||
|
||||
@Unique
|
||||
private CompactCtmQuadProcessor getCompactProcessor() {
|
||||
TileOverrideImpl.CTMCompact compact = CTMUtils.getCurrentCompact();
|
||||
if (compact == null) {
|
||||
return null;
|
||||
}
|
||||
return compact.getProcessor();
|
||||
}
|
||||
|
||||
private void handleCompactCtmFace(int face, Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
// recursion protection
|
||||
if (compactCtmRendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.blockAccess == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompactCtmQuadProcessor processor = getCompactProcessor();
|
||||
if (processor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
compactCtmRendering = true;
|
||||
try {
|
||||
boolean handled = processor.processFace(
|
||||
(RenderBlocks)(Object)this,
|
||||
this.blockAccess,
|
||||
MathHelper.floor_double(x),
|
||||
MathHelper.floor_double(y),
|
||||
MathHelper.floor_double(z),
|
||||
face,
|
||||
icon
|
||||
);
|
||||
|
||||
if (handled) {
|
||||
ci.cancel();
|
||||
}
|
||||
} finally {
|
||||
compactCtmRendering = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(0, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(1, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(2, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(3, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(4, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(5, block, x, y, z, icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderStandardBlock(Lnet/minecraft/block/Block;III)Z", at = @At("RETURN"))
|
||||
private void compactCtm_resetAfterBlock(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,12 @@ public class MixinTessellator implements StateAwareTessellator {
|
||||
@Unique
|
||||
private final IntArrayList vertexStates = new IntArrayList();
|
||||
|
||||
@Unique
|
||||
private final IntArrayList shaderOverrideBlockIds = new IntArrayList();
|
||||
|
||||
@Unique
|
||||
private short currentShaderOverrideBlockId = -1;
|
||||
|
||||
@Unique
|
||||
private boolean appliedAo;
|
||||
|
||||
@@ -33,11 +39,14 @@ public class MixinTessellator implements StateAwareTessellator {
|
||||
state |= StateAwareTessellator.RENDERED_WITH_VANILLA_AO;
|
||||
}
|
||||
this.vertexStates.add(state);
|
||||
this.shaderOverrideBlockIds.add(currentShaderOverrideBlockId);
|
||||
}
|
||||
|
||||
@Inject(method = "reset", at = @At("RETURN"))
|
||||
private void resetVertexStates(CallbackInfo ci) {
|
||||
this.vertexStates.clear();
|
||||
this.shaderOverrideBlockIds.clear();
|
||||
this.currentShaderOverrideBlockId = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,4 +58,14 @@ public class MixinTessellator implements StateAwareTessellator {
|
||||
public int[] angelica$getVertexStates() {
|
||||
return this.vertexStates.elements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] angelica$getShaderOverrideBlockIds() {
|
||||
return this.shaderOverrideBlockIds.elements();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$setShaderOverrideBlockId(short blockId) {
|
||||
this.currentShaderOverrideBlockId = blockId;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Share;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||
import jss.notfine.core.SettingsManager;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.shaderpack.CloudSetting;
|
||||
import net.coderbot.iris.compat.dh.DHCompat;
|
||||
import net.coderbot.iris.gl.program.Program;
|
||||
import net.coderbot.iris.pipeline.HandRenderer;
|
||||
@@ -26,7 +28,9 @@ import net.minecraft.entity.EntityLivingBase;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@@ -79,6 +83,12 @@ public abstract class MixinEntityRenderer implements IResourceManagerReloadListe
|
||||
private int iris$alwaysRenderSky(GameSettings instance) {
|
||||
return Math.max(instance.renderDistanceChunks, 4);
|
||||
}
|
||||
|
||||
@ModifyConstant(method = "renderWorld(FJ)V", constant = @Constant(doubleValue = 128.0D), expect = 2)
|
||||
private double iris$alwaysRenderCloudsLate(double cloudHeightCheck) {
|
||||
return IrisApi.getInstance().isShaderPackInUse() ? Double.NEGATIVE_INFINITY : SettingsManager.cloudTranslucencyCheck;
|
||||
}
|
||||
|
||||
@Inject(method = "renderWorld(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;renderSky(F)V"))
|
||||
private void iris$beginSky(float partialTicks, long startTime, CallbackInfo ci, @Share("pipeline") LocalRef<WorldRenderingPipeline> pipeline) {
|
||||
// Use CUSTOM_SKY until levelFogColor is called as a heuristic to catch FabricSkyboxes.
|
||||
@@ -94,7 +104,9 @@ public abstract class MixinEntityRenderer implements IResourceManagerReloadListe
|
||||
@WrapOperation(method = "renderWorld(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/EntityRenderer;renderCloudsCheck(Lnet/minecraft/client/renderer/RenderGlobal;F)V"))
|
||||
private void iris$clouds(EntityRenderer instance, RenderGlobal rg, float partialTicks, Operation<Void> original, @Share("pipeline") LocalRef<WorldRenderingPipeline> pipeline) {
|
||||
pipeline.get().setPhase(WorldRenderingPhase.CLOUDS);
|
||||
original.call(instance, rg, partialTicks);
|
||||
if (pipeline.get().getCloudSetting() != CloudSetting.OFF) {
|
||||
original.call(instance, rg, partialTicks);
|
||||
}
|
||||
pipeline.get().setPhase(WorldRenderingPhase.NONE);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhconvention")
|
||||
}
|
||||
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dumbra.dumpClass=true")
|
||||
}
|
||||
|
||||
|
||||
tasks.processResources {
|
||||
val projectVersion = project.version.toString()
|
||||
inputs.property("version", projectVersion)
|
||||
filesMatching("META-INF/rfb-plugin/*") {
|
||||
expand("version" to projectVersion)
|
||||
}
|
||||
}
|
||||
|
||||
val embedOnly: Configuration by configurations
|
||||
val shadowImplementation: Configuration by configurations
|
||||
|
||||
tasks.shadowJar {
|
||||
dependsOn(embedOnly)
|
||||
from(embedOnly.map(::zipTree))
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
mergeServiceFiles()
|
||||
|
||||
minimize {
|
||||
exclude(project(":glsm"))
|
||||
exclude(project(":lwjgl3-backend"))
|
||||
}
|
||||
|
||||
relocate("com.mitchej123", "com.mitchej123")
|
||||
relocate("org.embeddedt", "org.embeddedt")
|
||||
relocate("com.gtnewhorizons.angelica.glsm", "com.gtnewhorizons.angelica.glsm")
|
||||
relocate("com.gtnewhorizons.angelica.lwjgl3", "com.gtnewhorizons.angelica.lwjgl3")
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
configurations {
|
||||
// Embed in JAR without relocation (compile only, shadowed into JAR)
|
||||
embedOnly
|
||||
embedOnly.canBeConsumed = false
|
||||
embedOnly.canBeResolved = true
|
||||
compileClasspath.extendsFrom(embedOnly)
|
||||
runtimeClasspath.extendsFrom(embedOnly)
|
||||
testCompileClasspath.extendsFrom(embedOnly)
|
||||
testRuntimeClasspath.extendsFrom(embedOnly)
|
||||
}
|
||||
|
||||
// Exclude self-reference from all configurations
|
||||
configurations.all {
|
||||
exclude group: "com.github.GTNewHorizons", module: "Angelica"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.7") { transitive = false }
|
||||
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
compileOnly("org.projectlombok:lombok:1.18.42") { transitive = false }
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.42")
|
||||
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.9.36:dev")
|
||||
|
||||
shadowImplementation(project(":glsm")) { transitive = false }
|
||||
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
|
||||
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-32.g7dd88a4-GTNH") {
|
||||
exclude module: "antlr4" // we only want to shadow the runtime module
|
||||
}
|
||||
shadowImplementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
runtimeOnlyNonPublishable("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
|
||||
runtimeOnlyNonPublishable("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
gtnh.settings.blowdryerTag = 0.2.2
|
||||
|
||||
modName = Umbra
|
||||
modId = umbra
|
||||
modGroup = com.gtnewhorizons.umbra
|
||||
|
||||
useModGroupForPublishing = false
|
||||
autoUpdateBuildScript = false
|
||||
|
||||
minecraftVersion = 1.7.10
|
||||
forgeVersion = 10.13.4.1614
|
||||
channel = stable
|
||||
mappingsVersion = 12
|
||||
remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/
|
||||
|
||||
developmentEnvironmentUserName = Developer
|
||||
|
||||
enableModernJavaSyntax = jvmDowngrader
|
||||
jvmDowngraderStubsProvider = gtnhlib
|
||||
jvmDowngraderMultiReleaseVersions = 17, 21, 25
|
||||
|
||||
enableGenericInjection = true
|
||||
|
||||
generateGradleTokenClass = com.gtnewhorizons.umbra.Tags
|
||||
gradleTokenVersion = VERSION
|
||||
|
||||
coreModClass = loading.UmbraTweaker
|
||||
|
||||
containsMixinsAndOrCoreModOnly = false
|
||||
|
||||
usesMixins = true
|
||||
separateMixinSourceSet = mixin
|
||||
mixinsPackage = mixins
|
||||
|
||||
usesShadowedDependencies = true
|
||||
minimizeShadowedDependencies = true
|
||||
relocateShadowedDependencies = true
|
||||
|
||||
includeWellKnownRepositories = true
|
||||
|
||||
usesMavenPublishing = true
|
||||
|
||||
disableSpotless = true
|
||||
|
||||
org.gradle.configuration-cache = true
|
||||
org.gradle.jvmargs = -Xmx2g
|
||||
org.gradle.parallel = true
|
||||
@@ -0,0 +1,12 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e1118b4c7c2858d28154d086dbff010e/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/59a9771cad43219260d9aac9a8ec4d6a/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
|
||||
toolchainVersion=25
|
||||
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,248 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
@@ -0,0 +1,93 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,26 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
// RetroFuturaGradle
|
||||
name = "GTNH Maven"
|
||||
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
|
||||
mavenContent {
|
||||
includeGroup("com.gtnewhorizons")
|
||||
includeGroupByRegex("com\\.gtnewhorizons\\..+")
|
||||
}
|
||||
}
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.24")
|
||||
}
|
||||
|
||||
include("glsm")
|
||||
project(":glsm").projectDir = file("../glsm")
|
||||
|
||||
include("lwjgl3-backend")
|
||||
project(":lwjgl3-backend").projectDir = file("../lwjgl3-backend")
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.gtnewhorizons.umbra;
|
||||
|
||||
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 com.gtnewhorizons.umbra.proxy.CommonProxy;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@Mod(modid = UmbraMod.MOD_ID, name = "Umbra", version = Tags.VERSION,
|
||||
dependencies = "required-after:gtnhlib@[0.8.21,);before:lwjgl3ify@[1.5.3,)",
|
||||
acceptableRemoteVersions = "*")
|
||||
public class UmbraMod {
|
||||
|
||||
public static final String MOD_ID = "umbra";
|
||||
public static final Logger LOGGER = LogManager.getLogger("Umbra");
|
||||
|
||||
@SidedProxy(
|
||||
clientSide = "com.gtnewhorizons.umbra.proxy.ClientProxy",
|
||||
serverSide = "com.gtnewhorizons.umbra.proxy.CommonProxy")
|
||||
public static CommonProxy proxy;
|
||||
|
||||
private static boolean isDisabled() {
|
||||
return Launch.blackboard != null && Boolean.TRUE.equals(Launch.blackboard.get("umbra.disabled"));
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
if (isDisabled()) {
|
||||
LOGGER.info("Umbra is disabled (Angelica detected), skipping preInit");
|
||||
return;
|
||||
}
|
||||
proxy.preInit(event);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void init(FMLInitializationEvent event) {
|
||||
if (isDisabled()) return;
|
||||
proxy.init(event);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void postInit(FMLPostInitializationEvent event) {
|
||||
if (isDisabled()) return;
|
||||
proxy.postInit(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.gtnewhorizons.umbra.client.rendering;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.mitchej123.glsm.GLStateManagerService;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class UmbraGLStateManagerService implements GLStateManagerService {
|
||||
|
||||
private IntBuffer intArrayBuffer = BufferUtils.createIntBuffer(16);
|
||||
|
||||
private IntBuffer getIntArrayBuffer(int size) {
|
||||
if (intArrayBuffer.capacity() < size) {
|
||||
intArrayBuffer = BufferUtils.createIntBuffer(size);
|
||||
}
|
||||
intArrayBuffer.clear();
|
||||
return intArrayBuffer;
|
||||
}
|
||||
|
||||
@Override public int getPriority() { return 100; }
|
||||
|
||||
@Override public int glGetInteger(int pname) { return GLStateManager.glGetInteger(pname); }
|
||||
@Override public String glGetString(int pname) { return GLStateManager.glGetString(pname); }
|
||||
|
||||
@Override public int glGenFramebuffers() { return GLStateManager.glGenFramebuffers(); }
|
||||
@Override public void glDeleteFramebuffers(int fb) { GLStateManager.glDeleteFramebuffers(fb); }
|
||||
@Override public void glBindFramebuffer(int target, int fb) { GLStateManager.glBindFramebuffer(target, fb); }
|
||||
@Override public int glCheckFramebufferStatus(int target) { return GLStateManager.glCheckFramebufferStatus(target); }
|
||||
@Override public void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
|
||||
GLStateManager.glFramebufferTexture2D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
@Override public int glCreateShader(int type) { return GLStateManager.glCreateShader(type); }
|
||||
@Override public void glCompileShader(int shader) { GLStateManager.glCompileShader(shader); }
|
||||
@Override public int glGetShaderi(int shader, int pname) { return GLStateManager.glGetShaderi(shader, pname); }
|
||||
@Override public String glGetShaderInfoLog(int shader, int maxLen) { return GLStateManager.glGetShaderInfoLog(shader, maxLen); }
|
||||
@Override public void glDeleteShader(int shader) { GLStateManager.glDeleteShader(shader); }
|
||||
|
||||
@Override public int glCreateProgram() { return GLStateManager.glCreateProgram(); }
|
||||
@Override public void glAttachShader(int program, int shader) { GLStateManager.glAttachShader(program, shader); }
|
||||
@Override public void glLinkProgram(int program) { GLStateManager.glLinkProgram(program); }
|
||||
@Override public int glGetProgrami(int program, int pname) { return GLStateManager.glGetProgrami(program, pname); }
|
||||
@Override public void glUseProgram(int program) { GLStateManager.glUseProgram(program); }
|
||||
@Override public void glDeleteProgram(int program) { GLStateManager.glDeleteProgram(program); }
|
||||
|
||||
@Override public int glGetUniformLocation(int program, CharSequence name) { return GLStateManager.glGetUniformLocation(program, name); }
|
||||
@Override public void glUniform1i(int location, int value) { GLStateManager.glUniform1i(location, value); }
|
||||
@Override public int glGetAttribLocation(int program, CharSequence name) { return GLStateManager.glGetAttribLocation(program, name); }
|
||||
@Override public void glBindAttribLocation(int program, int index, CharSequence name) { GLStateManager.glBindAttribLocation(program, index, name); }
|
||||
|
||||
@Override public int glGenVertexArrays() { return UniversalVAO.genVertexArrays(); }
|
||||
@Override public void glBindVertexArray(int array) { GLStateManager.glBindVertexArray(array); }
|
||||
@Override public void glDeleteVertexArrays(int array) { GLStateManager.glDeleteVertexArrays(array); }
|
||||
|
||||
@Override public int glGenBuffers() { return GLStateManager.glGenBuffers(); }
|
||||
@Override public void glBindBuffer(int target, int buffer) { GLStateManager.glBindBuffer(target, buffer); }
|
||||
@Override public void glDeleteBuffers(int buffer) { GLStateManager.glDeleteBuffers(buffer); }
|
||||
|
||||
@Override public int glGenTextures() { return GLStateManager.glGenTextures(); }
|
||||
@Override public void glGenTextures(int[] textures) {
|
||||
final IntBuffer buffer = getIntArrayBuffer(textures.length);
|
||||
GLStateManager.glGenTextures(buffer);
|
||||
buffer.get(textures);
|
||||
}
|
||||
@Override public void glDeleteTextures(int texture) { GLStateManager.glDeleteTextures(texture); }
|
||||
@Override public void glDeleteTextures(int[] textures) {
|
||||
final IntBuffer buffer = getIntArrayBuffer(textures.length);
|
||||
buffer.put(textures);
|
||||
buffer.flip();
|
||||
GLStateManager.glDeleteTextures(buffer);
|
||||
}
|
||||
@Override public void glActiveTexture(int texture) { GLStateManager.glActiveTexture(texture); }
|
||||
@Override public int glGetTexLevelParameteri(int target, int level, int pname) { return GLStateManager.glGetTexLevelParameteri(target, level, pname); }
|
||||
@Override public int glGetTexLevelParameter(int target, int level, int pname) { return GLStateManager.glGetTexLevelParameteri(target, level, pname); }
|
||||
@Override public void glCopyTexSubImage2D(int target, int level, int xo, int yo, int x, int y, int w, int h) {
|
||||
GLStateManager.glCopyTexSubImage2D(target, level, xo, yo, x, y, w, h);
|
||||
}
|
||||
@Override public void glPixelStorei(int pname, int param) { GLStateManager.glPixelStorei(pname, param); }
|
||||
|
||||
@Override public void enableCullFace() { GLStateManager.enableCull(); }
|
||||
@Override public void disableCullFace() { GLStateManager.disableCull(); }
|
||||
@Override public void enableBlend() { GLStateManager.enableBlend(); }
|
||||
@Override public void disableBlend() { GLStateManager.disableBlend(); }
|
||||
@Override public void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcA, int dstA) { GLStateManager.tryBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA); }
|
||||
@Override public void enableDepthTest() { GLStateManager.enableDepthTest(); }
|
||||
@Override public void disableDepthTest() { GLStateManager.disableDepthTest(); }
|
||||
@Override public void glDepthFunc(int func) { GLStateManager.glDepthFunc(func); }
|
||||
@Override public void glDepthMask(boolean flag) { GLStateManager.glDepthMask(flag); }
|
||||
@Override public void glViewport(int x, int y, int w, int h) { GLStateManager.glViewport(x, y, w, h); }
|
||||
@Override public void glColorMask(boolean r, boolean g, boolean b, boolean a) { GLStateManager.glColorMask(r, g, b, a); }
|
||||
@Override public void glClearColor(float r, float g, float b, float a) { GLStateManager.glClearColor(r, g, b, a); }
|
||||
@Override public void glClear(int mask) { GLStateManager.glClear(mask); }
|
||||
|
||||
@Override public void clear(int mask, boolean checkError) { GLStateManager.glClear(mask); }
|
||||
@Override public void bindTexture(int texture) { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); }
|
||||
@Override public int getActiveTexture() { return GLStateManager.getActiveTextureUnit() + GL13.GL_TEXTURE0; }
|
||||
@Override public int getActiveTextureAccessor() { return GLStateManager.getActiveTextureUnit(); }
|
||||
@Override public int getBoundTexture(int internalUnit) { return GLStateManager.getBoundTextureForServerState(internalUnit); }
|
||||
@Override public int getActiveBoundTexture() { return GLStateManager.getBoundTextureForServerState(); }
|
||||
@Override public int getViewportWidth() { return GLStateManager.getViewportState().width; }
|
||||
@Override public int getViewportHeight() { return GLStateManager.getViewportState().height; }
|
||||
@Override public boolean getDepthStateMask() { return GLStateManager.getDepthState().isEnabled(); }
|
||||
@Override public boolean isBlendEnabled() { return GLStateManager.getBlendMode().isEnabled(); }
|
||||
@Override public void setBoundTexture(int unit, int texture) {
|
||||
final int currentUnit = GLStateManager.getActiveTextureUnit();
|
||||
if (currentUnit != unit) GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
if (currentUnit != unit) GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + currentUnit);
|
||||
}
|
||||
@Override public int getTextureBinding(int unit) { return GLStateManager.getBoundTextureForServerState(unit); }
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.gtnewhorizons.umbra.client.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.mitchej123.glsm.RenderSystemService;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public class UmbraRenderSystemService implements RenderSystemService {
|
||||
|
||||
private final int[] shaderTextures = new int[16];
|
||||
private final float[] shaderColor = {1f, 1f, 1f, 1f};
|
||||
private final float[] shaderFogColor = {0f, 0f, 0f, 1f};
|
||||
private float shaderFogStart = 0f;
|
||||
private float shaderFogEnd = 0f;
|
||||
private int fogShape = 0;
|
||||
private float shaderLineWidth = 1f;
|
||||
private final Matrix4f projectionMatrix = new Matrix4f();
|
||||
private final FloatBuffer projectionMatrixBuffer = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
@Override public int getPriority() { return 100; }
|
||||
|
||||
@Override public void glActiveTexture(int texture) { GLStateManager.glActiveTexture(texture); }
|
||||
@Override public void bindTexture(int texture) { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); }
|
||||
|
||||
@Override public void enableCullFace() { GLStateManager.enableCull(); }
|
||||
@Override public void disableCullFace() { GLStateManager.disableCull(); }
|
||||
@Override public void enableBlend() { GLStateManager.enableBlend(); }
|
||||
@Override public void disableBlend() { GLStateManager.disableBlend(); }
|
||||
@Override public void defaultBlendFunc() { GLStateManager.defaultBlendFunc(); }
|
||||
@Override public void blendFuncSeparate(int srcRGB, int dstRGB, int srcA, int dstA) { GLStateManager.tryBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA); }
|
||||
@Override public void setUnknownBlendState() { }
|
||||
@Override public void enableDepthTest() { GLStateManager.enableDepthTest(); }
|
||||
@Override public void disableDepthTest() { GLStateManager.disableDepthTest(); }
|
||||
@Override public void depthFunc(int func) { GLStateManager.glDepthFunc(func); }
|
||||
@Override public void depthMask(boolean flag) { GLStateManager.glDepthMask(flag); }
|
||||
@Override public void glViewport(int x, int y, int w, int h) { GLStateManager.glViewport(x, y, w, h); }
|
||||
@Override public void glClearColor(float r, float g, float b, float a) { GLStateManager.glClearColor(r, g, b, a); }
|
||||
@Override public void clear(int mask, boolean checkError) { GLStateManager.glClear(mask); }
|
||||
|
||||
@Override public void glUniform1i(int loc, int val) { GLStateManager.glUniform1i(loc, val); }
|
||||
@Override public void glUniformMatrix3(int loc, boolean transpose, FloatBuffer val) { GLStateManager.glUniformMatrix3(loc, transpose, val); }
|
||||
@Override public void glUniformMatrix4(int loc, boolean transpose, FloatBuffer val) { GLStateManager.glUniformMatrix4(loc, transpose, val); }
|
||||
|
||||
@Override public void assertOnRenderThread() { }
|
||||
@Override public void assertOnRenderThreadOrInit() { }
|
||||
|
||||
@Override public void setShaderTexture(int slot, int id) { if (slot >= 0 && slot < 16) shaderTextures[slot] = id; }
|
||||
@Override public int getShaderTexture(int slot) { return (slot >= 0 && slot < 16) ? shaderTextures[slot] : 0; }
|
||||
@Override public void setShaderColor(float r, float g, float b, float a) { shaderColor[0]=r; shaderColor[1]=g; shaderColor[2]=b; shaderColor[3]=a; }
|
||||
@Override public float[] getShaderColor() { return shaderColor.clone(); }
|
||||
@Override public void setShaderLineWidth(float w) { shaderLineWidth = w; }
|
||||
@Override public float getShaderLineWidth() { return shaderLineWidth; }
|
||||
|
||||
@Override public void setShaderFogColor(float r, float g, float b, float a) { shaderFogColor[0]=r; shaderFogColor[1]=g; shaderFogColor[2]=b; shaderFogColor[3]=a; }
|
||||
@Override public float[] getShaderFogColor() { return shaderFogColor.clone(); }
|
||||
@Override public void setShaderFogStart(float start) { shaderFogStart = start; }
|
||||
@Override public float getShaderFogStart() { return shaderFogStart; }
|
||||
@Override public void setShaderFogEnd(float end) { shaderFogEnd = end; }
|
||||
@Override public float getShaderFogEnd() { return shaderFogEnd; }
|
||||
@Override public void setFogShape(int shape) { fogShape = shape; }
|
||||
@Override public int getFogShape() { return fogShape; }
|
||||
|
||||
@Override public Matrix4f getProjectionMatrix() { return new Matrix4f(projectionMatrix); }
|
||||
@Override public void setProjectionMatrixOrth(Matrix4f m) {
|
||||
projectionMatrix.set(m);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
projectionMatrix.get(projectionMatrixBuffer);
|
||||
projectionMatrixBuffer.rewind();
|
||||
GLStateManager.glLoadMatrix(projectionMatrixBuffer);
|
||||
}
|
||||
@Override public void setProjectionMatrixOrigin(Matrix4f m) { setProjectionMatrixOrth(m); }
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.gtnewhorizons.umbra.loading;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
|
||||
import com.gtnewhorizons.umbra.loading.shared.AngelicaDetector;
|
||||
import com.gtnewhorizons.umbra.mixins.Mixins;
|
||||
import cpw.mods.fml.relauncher.FMLRelaunchLog;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Client-side coremod logic. Handles Angelica detection, transformer exclusions,
|
||||
* mixin registration, dependency verification, and late tweaker registration.
|
||||
*/
|
||||
public class UmbraClientTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
private static Boolean OBF_ENV;
|
||||
private final boolean disabled;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public UmbraClientTweaker() {
|
||||
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.umbra.loading");
|
||||
Launch.classLoader.addClassLoaderExclusion("com.gtnewhorizons.angelica.glsm.redirect");
|
||||
|
||||
// Detect Angelica -- if present, disable Umbra entirely
|
||||
if (AngelicaDetector.isPresent()) {
|
||||
FMLRelaunchLog.info("[Umbra] Angelica detected, Disabling Umbra");
|
||||
Launch.blackboard.put("umbra.disabled", Boolean.TRUE);
|
||||
disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
disabled = false;
|
||||
|
||||
if (Boolean.TRUE.equals(Launch.blackboard.get("umbra.rfbPluginLoaded"))) {
|
||||
addLwjgl3ifyExclusions();
|
||||
}
|
||||
|
||||
verifyDependencies();
|
||||
|
||||
// Register early redirector if RFB is not loaded (RFB handles its own registration)
|
||||
if (!Boolean.TRUE.equals(Launch.blackboard.get("umbra.rfbPluginLoaded"))) {
|
||||
final String earlyTransformer = "com.gtnewhorizons.umbra.loading.fml.transformers.EarlyRedirectorTransformer";
|
||||
Launch.classLoader.registerTransformer(earlyTransformer);
|
||||
FMLRelaunchLog.info("[Umbra] Registered early redirector transformer");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyDependencies() {
|
||||
DependencyVerifier.verify(UmbraClientTweaker.class, List.of(
|
||||
new DependencyVerifier.Check(
|
||||
"/com/gtnewhorizon/gtnhlib/client/renderer/PrimitiveExtractor.class",
|
||||
"Missing dependency: Umbra requires GTNHLib 0.8.21+! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/xyz/wagyourtail/jvmdg/exc/MissingStubError.class",
|
||||
"GTNHLib is outdated: Umbra requires GTNHLib 0.9.0+! Download: https://modrinth.com/mod/gtnhlib")
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
if (disabled) return new String[0];
|
||||
return new String[0]; // Redirector registered via late tweaker, not here
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModContainerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetupClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void injectData(Map<String, Object> data) {
|
||||
if (disabled) return;
|
||||
|
||||
OBF_ENV = (Boolean) data.get("runtimeDeobfuscationEnabled");
|
||||
|
||||
// Register the late tweaker (handles narrowing + full redirector registration)
|
||||
if (!Boolean.TRUE.equals(Launch.blackboard.get("umbra.rfbPluginLoaded"))) {
|
||||
final List<String> tweakClasses = org.spongepowered.asm.launch.GlobalProperties.get(
|
||||
MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
|
||||
if (tweakClasses != null) {
|
||||
tweakClasses.add("com.gtnewhorizons.umbra.loading.fml.tweakers.UmbraLateTweaker");
|
||||
FMLRelaunchLog.info("[Umbra] Registered late tweaker");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
if (disabled) return null;
|
||||
return "mixins.umbra.early.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
if (disabled) return Collections.emptyList();
|
||||
return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods);
|
||||
}
|
||||
|
||||
public static boolean isObfEnv() {
|
||||
if (OBF_ENV == null) throw new IllegalStateException("OBF_ENV not yet initialized");
|
||||
return OBF_ENV;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.gtnewhorizons.umbra.loading;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.FMLRelaunchLog;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Umbra coremod entry point. Detects Angelica and delegates to client tweaker.
|
||||
*/
|
||||
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||
public class UmbraTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
private final IFMLLoadingPlugin loadingPlugin;
|
||||
private final IEarlyMixinLoader mixinLoader;
|
||||
|
||||
public UmbraTweaker() {
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
final UmbraClientTweaker clientTweaker = new UmbraClientTweaker();
|
||||
loadingPlugin = clientTweaker;
|
||||
mixinLoader = clientTweaker;
|
||||
} else {
|
||||
loadingPlugin = null;
|
||||
mixinLoader = null;
|
||||
FMLRelaunchLog.info("[Umbra] Server side detected, skipping client initialization");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
return loadingPlugin != null ? loadingPlugin.getASMTransformerClass() : new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModContainerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetupClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectData(Map<String, Object> data) {
|
||||
if (loadingPlugin != null) {
|
||||
loadingPlugin.injectData(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
if (mixinLoader != null) {
|
||||
return mixinLoader.getMixinConfig();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
if (mixinLoader != null) {
|
||||
return mixinLoader.getMixins(loadedCoreMods);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.gtnewhorizons.umbra.loading.fml.transformers;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EarlyRedirectorCore;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
|
||||
/**
|
||||
* A scoped redirector that only transforms classes from known-misbehaving (core)mod packages.
|
||||
* <p>
|
||||
* Some mods prematurely load classes that call GL functions during coremod discovery/injectData.
|
||||
* Those classes get missed by the late-registered {@link UmbraRedirectorTransformer}, so their
|
||||
* GL calls permanently bypass GLSM.
|
||||
* <p>
|
||||
* Registered in {@code UmbraClientTweaker} constructor. Removed by {@code UmbraLateTweaker}
|
||||
* once the full redirector is in its proper post-mixin position.
|
||||
*
|
||||
* @see EcosystemNarrowRules#EARLY_REDIRECTOR_TARGETS
|
||||
*/
|
||||
public class EarlyRedirectorTransformer implements IClassTransformer {
|
||||
|
||||
private final EarlyRedirectorCore impl = new EarlyRedirectorCore(
|
||||
"com.gtnewhorizons.umbra.loading"
|
||||
);
|
||||
|
||||
@Override
|
||||
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||
return impl.transform(transformedName, basicClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.gtnewhorizons.umbra.loading.fml.transformers;
|
||||
|
||||
import com.gtnewhorizons.umbra.loading.shared.UmbraClassDump;
|
||||
import com.gtnewhorizons.umbra.loading.shared.transformers.UmbraRedirector;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
/** IClassTransformer wrapper for {@link UmbraRedirector} */
|
||||
public class UmbraRedirectorTransformer implements IClassTransformer {
|
||||
|
||||
private final UmbraRedirector inner;
|
||||
private final String[] exclusions;
|
||||
|
||||
public UmbraRedirectorTransformer() {
|
||||
inner = new UmbraRedirector();
|
||||
exclusions = inner.getTransformerExclusions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(final String className, String transformedName, byte[] basicClass) {
|
||||
if (basicClass == null) return null;
|
||||
|
||||
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);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
UmbraClassDump.dumpClass(transformedName, basicClass, bytes, this);
|
||||
return bytes;
|
||||
}
|
||||
return basicClass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.gtnewhorizons.umbra.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;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Late tweaker that:
|
||||
* 1. Narrows overly-broad transformer exclusions from other coremods
|
||||
* 2. Removes the scoped early redirector
|
||||
* 3. Registers the full redirector in its proper post-mixin position
|
||||
*/
|
||||
public class UmbraLateTweaker implements ITweaker {
|
||||
|
||||
private static final String EARLY_REDIRECTOR_CLASS = "com.gtnewhorizons.umbra.loading.fml.transformers.EarlyRedirectorTransformer";
|
||||
|
||||
@Override
|
||||
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
|
||||
narrowTransformerExclusions(classLoader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void narrowTransformerExclusions(LaunchClassLoader classLoader) {
|
||||
try {
|
||||
final Field exceptionsField = Launch.classLoader.getClass().getDeclaredField("transformerExceptions");
|
||||
exceptionsField.setAccessible(true);
|
||||
final Set<String> exceptions = (Set<String>) exceptionsField.get(classLoader);
|
||||
|
||||
TransformerNarrower.narrow(exceptions, Launch.blackboard, "umbra", "Umbra",
|
||||
EcosystemNarrowRules.ALL);
|
||||
|
||||
} catch (Exception e) {
|
||||
FMLRelaunchLog.warning("[Umbra] Failed to narrow transformer exclusions: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLaunchTarget() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public String[] getLaunchArguments() {
|
||||
// Remove the scoped early redirector and register the full one after mixins
|
||||
try {
|
||||
final Field transformersField = Launch.classLoader.getClass().getDeclaredField("transformers");
|
||||
transformersField.setAccessible(true);
|
||||
final List<IClassTransformer> transformers =
|
||||
(List<IClassTransformer>) transformersField.get(Launch.classLoader);
|
||||
transformers.removeIf(t -> t.getClass().getName().equals(EARLY_REDIRECTOR_CLASS));
|
||||
} catch (Exception e) {
|
||||
FMLRelaunchLog.warning("[Umbra] Failed to remove EarlyRedirectorTransformer: %s", e.getMessage());
|
||||
}
|
||||
|
||||
final String transformer =
|
||||
"com.gtnewhorizons.umbra.loading.fml.transformers.UmbraRedirectorTransformer";
|
||||
FMLRelaunchLog.finer("[Umbra] Registering transformer %s", transformer);
|
||||
Launch.classLoader.registerTransformer(transformer);
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.gtnewhorizons.umbra.loading.rfb;
|
||||
|
||||
import com.gtnewhorizons.umbra.loading.rfb.transformers.RFBUmbraRedirector;
|
||||
import com.gtnewhorizons.umbra.loading.shared.AngelicaDetector;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.PluginContext;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RetroFuturaBootstrap;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbPlugin;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class UmbraRfbPlugin implements RfbPlugin {
|
||||
|
||||
@Override
|
||||
public void onConstruction(@NotNull PluginContext ctx) {
|
||||
Launch.blackboard.put("umbra.rfbPluginLoaded", Boolean.TRUE);
|
||||
Launch.classLoader.addClassLoaderExclusion("com.gtnewhorizons.angelica.glsm.redirect.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RfbClassTransformer @Nullable [] makeTransformers() {
|
||||
// Detect Angelica -- if present, don't register any transformers
|
||||
if (AngelicaDetector.isPresent()) {
|
||||
Launch.blackboard.put("umbra.disabled", Boolean.TRUE);
|
||||
return null;
|
||||
}
|
||||
|
||||
final boolean isServer = (null == RetroFuturaBootstrap.API.launchClassLoader()
|
||||
.findClassMetadata("net.minecraft.client.main.Main"));
|
||||
if (isServer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RfbClassTransformer[] {
|
||||
new RFBUmbraRedirector()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.gtnewhorizons.umbra.loading.rfb.transformers;
|
||||
|
||||
import com.gtnewhorizons.umbra.loading.shared.UmbraClassDump;
|
||||
import com.gtnewhorizons.umbra.loading.shared.transformers.UmbraRedirector;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/** RfbClassTransformer wrapper for {@link UmbraRedirector} */
|
||||
public class RFBUmbraRedirector implements RfbClassTransformer {
|
||||
|
||||
private final UmbraRedirector inner;
|
||||
|
||||
public RFBUmbraRedirector() {
|
||||
inner = new UmbraRedirector();
|
||||
}
|
||||
|
||||
@Pattern("[a-z0-9-]+")
|
||||
@Override
|
||||
public @NotNull String id() {
|
||||
return "umbra-redirector";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortAfter() {
|
||||
return new String[] {"*", "mixin:mixin"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortBefore() {
|
||||
return new String[] {"lwjgl3ify:redirect"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] additionalExclusions() {
|
||||
return inner.getTransformerExclusions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldTransformClass(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest, @NotNull String className,
|
||||
@NotNull ClassNodeHandle classNode) {
|
||||
if (!classNode.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
return inner.shouldTransform(classNode.getOriginalBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
final boolean changed = inner.transformClassNode(className, classNode.getNode());
|
||||
if (changed) {
|
||||
classNode.computeMaxs();
|
||||
UmbraClassDump.dumpRFBClass(className, classNode, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.gtnewhorizons.umbra.loading.shared;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
|
||||
/**
|
||||
* Detects whether Angelica is present. If so, Umbra should disable itself entirely since Angelica provides its own GLSM integration.
|
||||
*/
|
||||
public final class AngelicaDetector {
|
||||
|
||||
private static Boolean result;
|
||||
|
||||
public static boolean isPresent() {
|
||||
if (result != null) return result;
|
||||
|
||||
boolean found = AngelicaDetector.class.getClassLoader().getResource("com/gtnewhorizons/angelica/loading/AngelicaTweaker.class") != null;
|
||||
|
||||
if (!found && Launch.blackboard != null) {
|
||||
found = Boolean.TRUE.equals(Launch.blackboard.get("angelica.rfbPluginLoaded"));
|
||||
}
|
||||
|
||||
result = found;
|
||||
return found;
|
||||
}
|
||||
|
||||
private AngelicaDetector() {}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.gtnewhorizons.umbra.loading.shared;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
|
||||
public class UmbraClassDump {
|
||||
|
||||
private static final boolean DUMP_CLASS = Boolean.getBoolean("umbra.dumpClass");
|
||||
|
||||
public static void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
|
||||
if (DUMP_CLASS) {
|
||||
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
|
||||
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void dumpRFBClass(String className, ClassNodeHandle classNode, Object transformer) {
|
||||
if (DUMP_CLASS) {
|
||||
final byte[] originalBytes = classNode.getOriginalBytes();
|
||||
final byte[] transformedBytes = classNode.computeBytes();
|
||||
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
|
||||
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.gtnewhorizons.umbra.loading.shared.transformers;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mod-level wrapper around {@link GLSMRedirector}. Adds Umbra-specific transformer exclusions
|
||||
* and delegates all redirect logic to GLSM.
|
||||
* <p>
|
||||
* THIS CLASS MIGHT BE LOADED ON A DIFFERENT CLASS LOADER,
|
||||
* IT SHOULD NOT CALL ANY CODE FROM THE MAIN MOD
|
||||
*/
|
||||
public final class UmbraRedirector {
|
||||
|
||||
private final GLSMRedirector core = new GLSMRedirector();
|
||||
private final String[] allExclusions;
|
||||
|
||||
public UmbraRedirector() {
|
||||
final List<String> exclusions = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
|
||||
exclusions.add("com.gtnewhorizons.umbra.loading");
|
||||
exclusions.add("com.gtnewhorizons.angelica.lwjgl3.");
|
||||
allExclusions = exclusions.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public String[] getTransformerExclusions() {
|
||||
return allExclusions;
|
||||
}
|
||||
|
||||
public boolean shouldTransform(byte[] basicClass) {
|
||||
return core.shouldTransform(basicClass);
|
||||
}
|
||||
|
||||
/** @return Was the class changed? */
|
||||
public boolean transformClassNode(String transformedName, ClassNode cn) {
|
||||
return core.transformClassNode(transformedName, cn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.gtnewhorizons.umbra.mixins;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum Mixins implements IMixins {
|
||||
|
||||
UMBRA_STARTUP(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins(
|
||||
"early.startup.MixinInitGLStateManager"
|
||||
)
|
||||
),
|
||||
|
||||
UMBRA_CORE_PROFILE(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins(
|
||||
"early.ffp.MixinTessellator_CoreProfile"
|
||||
)
|
||||
),
|
||||
|
||||
UMBRA_GLSM(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins(
|
||||
"early.glsm.MixinForgeHooksClient_CoreProfile",
|
||||
"early.glsm.MixinSplashProgressCaching",
|
||||
"early.glsm.MixinMinecraft_FrameHook",
|
||||
"early.glsm.MixinWorldRenderer_VertexState"
|
||||
)
|
||||
),
|
||||
|
||||
;
|
||||
|
||||
private final MixinBuilder builder;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.gtnewhorizons.umbra.proxy;
|
||||
|
||||
import com.gtnewhorizons.umbra.UmbraMod;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
|
||||
@Override
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
UmbraMod.LOGGER.info("Umbra client preInit - GLSM initialized via mixin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FMLInitializationEvent event) {
|
||||
UmbraMod.LOGGER.info("Umbra client init");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(FMLPostInitializationEvent event) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.gtnewhorizons.umbra.proxy;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
|
||||
public class CommonProxy {
|
||||
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
}
|
||||
|
||||
public void init(FMLInitializationEvent event) {
|
||||
}
|
||||
|
||||
public void postInit(FMLPostInitializationEvent event) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
name=Umbra
|
||||
version=${version}
|
||||
additionalVersions=
|
||||
className=com.gtnewhorizons.umbra.loading.rfb.UmbraRfbPlugin
|
||||
transformerExclusions=com.gtnewhorizons.umbra.loading.shared;com.gtnewhorizon.gtnhlib.asm
|
||||
versionConstraints=
|
||||
loadBefore=
|
||||
loadAfter=mixin; lwjgl3ify
|
||||
loadRequires=
|
||||
@@ -0,0 +1 @@
|
||||
com.gtnewhorizons.umbra.client.rendering.UmbraGLStateManagerService
|
||||
@@ -0,0 +1 @@
|
||||
com.gtnewhorizons.umbra.client.rendering.UmbraRenderSystemService
|
||||
@@ -0,0 +1,2 @@
|
||||
com.mitchej123.lwjgl.lwjgl3.LWJGL3Service
|
||||
com.mitchej123.lwjgl.lwjgl2.LWJGL2Service
|
||||
@@ -0,0 +1,13 @@
|
||||
[{
|
||||
"modid": "umbra",
|
||||
"name": "Umbra",
|
||||
"description": "Umbra replaces Minecraft 1.7.10’s legacy rendering pipeline with a modern, backend-agnostic core.",
|
||||
"version": "${modVersion}",
|
||||
"mcversion": "${minecraftVersion}",
|
||||
"url": "",
|
||||
"authorList": ["GTNH"],
|
||||
"credits": "",
|
||||
"logoFile": "",
|
||||
"screenshots": [],
|
||||
"dependencies": ["gtnhlib"]
|
||||
}]
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8.3-GTNH",
|
||||
"refmap": "mixins.umbra.refmap.json",
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"package": "com.gtnewhorizons.umbra.mixins",
|
||||
"overwrites": {
|
||||
"conformVisibility": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8.3-GTNH",
|
||||
"refmap": "mixins.umbra.refmap.json",
|
||||
"target": "@env(DEFAULT)",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"package": "com.gtnewhorizons.umbra.mixins",
|
||||
"overwrites": {
|
||||
"conformVisibility": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.gtnewhorizons.umbra.mixins.early.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* Intercepts Tessellator.draw() to use streaming VBO+VAO for core profile rendering.
|
||||
* Implements ITessellatorData so TessellatorStreamingDrawer can access Tessellator fields
|
||||
* without depending on Minecraft classes.
|
||||
*/
|
||||
@Mixin(Tessellator.class)
|
||||
public class MixinTessellator_CoreProfile implements ITessellatorData {
|
||||
|
||||
@Shadow public boolean isDrawing;
|
||||
@Shadow public int vertexCount;
|
||||
@Shadow public int[] rawBuffer;
|
||||
@Shadow public int rawBufferIndex;
|
||||
@Shadow public int rawBufferSize;
|
||||
@Shadow public int drawMode;
|
||||
@Shadow public boolean hasTexture;
|
||||
@Shadow public boolean hasColor;
|
||||
@Shadow public boolean hasNormals;
|
||||
@Shadow public boolean hasBrightness;
|
||||
|
||||
@Shadow public void reset() {}
|
||||
|
||||
public void angelica$reset() {
|
||||
reset();
|
||||
}
|
||||
|
||||
@Inject(method = "draw", at = @At("HEAD"), cancellable = true)
|
||||
private void umbra$coreProfileDraw(CallbackInfoReturnable<Integer> cir) {
|
||||
if (TessellatorManager.shouldInterceptDraw((Tessellator)(Object)this)) return;
|
||||
cir.setReturnValue(TessellatorStreamingDrawer.draw((ITessellatorData) this));
|
||||
}
|
||||
|
||||
@Override public boolean isDrawing() { return isDrawing; }
|
||||
@Override public void setDrawing(boolean drawing) { isDrawing = drawing; }
|
||||
@Override public int getVertexCount() { return vertexCount; }
|
||||
@Override public int[] getRawBuffer() { return rawBuffer; }
|
||||
@Override public int getRawBufferIndex() { return rawBufferIndex; }
|
||||
@Override public int getRawBufferSize() { return rawBufferSize; }
|
||||
@Override public void setRawBufferSize(int size) { rawBufferSize = size; }
|
||||
@Override public void setRawBuffer(int[] buffer) { rawBuffer = buffer; }
|
||||
@Override public int getDrawMode() { return drawMode; }
|
||||
@Override public boolean hasTexture() { return hasTexture; }
|
||||
@Override public boolean hasColor() { return hasColor; }
|
||||
@Override public boolean hasNormals() { return hasNormals; }
|
||||
@Override public boolean hasBrightness() { return hasBrightness; }
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.gtnewhorizons.umbra.mixins.early.glsm;
|
||||
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.LWJGLUtil;
|
||||
import org.lwjgl.opengl.ContextAttribs;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Requests highest GL version with core profile context.
|
||||
* Required for GLSM's FFP emulation to work correctly.
|
||||
*/
|
||||
@Mixin(value = ForgeHooksClient.class, remap = false)
|
||||
public abstract class MixinForgeHooksClient_CoreProfile {
|
||||
|
||||
@Unique private static final Logger LOGGER = LogManager.getLogger("Umbra");
|
||||
|
||||
@Shadow static int stencilBits;
|
||||
|
||||
/**
|
||||
* @author Umbra
|
||||
* @reason Request highest GL version with core profile context for GLSM
|
||||
*/
|
||||
@Overwrite
|
||||
public static void createDisplay() throws LWJGLException {
|
||||
ImageIO.setUseCache(false);
|
||||
stencilBits = 8;
|
||||
|
||||
final PixelFormat format = new PixelFormat().withDepthBits(24).withStencilBits(stencilBits);
|
||||
|
||||
final boolean lwjglDebug = Boolean.getBoolean("org.lwjgl.util.Debug");
|
||||
final ContextAttribs attribs = new ContextAttribs(3, 3)
|
||||
.withProfileCore(true).withForwardCompatible(true).withDebug(lwjglDebug);
|
||||
|
||||
final MethodHandle setMajor, setMinor;
|
||||
try {
|
||||
final Field majorField = ContextAttribs.class.getDeclaredField("majorVersion");
|
||||
final Field minorField = ContextAttribs.class.getDeclaredField("minorVersion");
|
||||
majorField.setAccessible(true);
|
||||
minorField.setAccessible(true);
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
setMajor = lookup.unreflectSetter(majorField);
|
||||
setMinor = lookup.unreflectSetter(minorField);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to obtain ContextAttribs version setters", e);
|
||||
}
|
||||
|
||||
final int platformMaxMajor = 4;
|
||||
final int platformMaxMinor = (LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_MACOSX) ? 1 : 6;
|
||||
|
||||
// Probe from highest to lowest
|
||||
Exception lastException = null;
|
||||
for (int major = platformMaxMajor; major >= 3; --major) {
|
||||
final int maxMinor = (major == 4) ? platformMaxMinor : 3;
|
||||
final int minMinor = (major == 3) ? 3 : 0;
|
||||
for (int minor = maxMinor; minor >= minMinor; --minor) {
|
||||
final Exception e = umbra$tryCreate(attribs, format, setMajor, setMinor, major, minor);
|
||||
if (e == null) {
|
||||
LOGGER.info("Created GL {}.{} core profile context", major, minor);
|
||||
return;
|
||||
}
|
||||
lastException = e;
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.error("FATAL: Failed to create OpenGL core profile context. Error: {}", lastException != null ? lastException.getMessage() : "unknown");
|
||||
if (lastException instanceof LWJGLException lwjgl) {
|
||||
throw lwjgl;
|
||||
}
|
||||
throw new LWJGLException("Failed to create OpenGL core profile context", lastException);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Exception umbra$tryCreate(ContextAttribs attribs, PixelFormat format, MethodHandle setMajor, MethodHandle setMinor, int major, int minor) {
|
||||
try {
|
||||
setMajor.invokeExact(attribs, major);
|
||||
setMinor.invokeExact(attribs, minor);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to set ContextAttribs version", t);
|
||||
}
|
||||
try {
|
||||
Display.create(format, attribs);
|
||||
return null;
|
||||
} catch (LWJGLException | RuntimeException e) {
|
||||
try { Display.destroy(); } catch (Exception ignored) {}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.gtnewhorizons.umbra.mixins.early.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Per-frame hook to flush streaming buffer fences.
|
||||
*/
|
||||
@Mixin(Minecraft.class)
|
||||
public class MixinMinecraft_FrameHook {
|
||||
|
||||
@Inject(method = "runGameLoop", at = @At("HEAD"))
|
||||
private void umbra$onFrameStart(CallbackInfo ci) {
|
||||
TessellatorStreamingDrawer.endFrame();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizons.umbra.mixins.early.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Controls GLSM state during splash screen lifecycle.
|
||||
* Initializes splash tessellator on start, marks splash complete on finish.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Mixin(value = cpw.mods.fml.client.SplashProgress.class, remap = false)
|
||||
public class MixinSplashProgressCaching {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Umbra");
|
||||
|
||||
@Inject(method = "start", at = @At("HEAD"))
|
||||
private static void umbra$initSplashTessellator(CallbackInfo ci) {
|
||||
ImmediateModeRecorder.initSplashTessellator();
|
||||
}
|
||||
|
||||
@Inject(method = "finish", at = @At("RETURN"))
|
||||
private static void umbra$enableCachingOnFinish(CallbackInfo ci) {
|
||||
ImmediateModeRecorder.destroySplashTessellator();
|
||||
GLStateManager.markSplashComplete();
|
||||
LOGGER.info("Splash Complete");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizons.umbra.mixins.early.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.shader.TesselatorVertexState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import net.minecraftforge.client.ForgeHooksClient;
|
||||
|
||||
@Mixin(WorldRenderer.class)
|
||||
public class MixinWorldRenderer_VertexState {
|
||||
|
||||
@Shadow private TesselatorVertexState vertexState;
|
||||
|
||||
@Inject(method = "postRenderBlocks", at = @At("HEAD"), cancellable = true)
|
||||
private void umbra$skipVertexStateForDirectTessellator(int pass, EntityLivingBase entity, CallbackInfo ci) {
|
||||
if (Tessellator.instance instanceof DirectTessellator) {
|
||||
// Can't get vertex state from DirectTessellator, draw and return
|
||||
Tessellator.instance.draw();
|
||||
ForgeHooksClient.onPostRenderWorld((WorldRenderer)(Object)this, pass);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glEndList();
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.gtnewhorizons.umbra.mixins.early.startup;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = OpenGlHelper.class, priority = 100)
|
||||
public class MixinInitGLStateManager {
|
||||
|
||||
@Inject(method = "initializeTextures", at = @At("RETURN"))
|
||||
private static void umbra$initializeGLStateManager(CallbackInfo ci) {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
GLStateManager.setDrawableGL(Display.getDrawable());
|
||||
GLStateManager.initialize(GLSMInitConfig.builder()
|
||||
.displaySize(mc.displayWidth, mc.displayHeight)
|
||||
.framebufferSupported(OpenGlHelper.framebufferSupported)
|
||||
.fboEnabled(mc.gameSettings.fboEnable)
|
||||
.directDrawer(TessellatorStreamingDrawer::drawDirect)
|
||||
.streamingDrawerDestroy(TessellatorStreamingDrawer::destroy)
|
||||
.build());
|
||||
|
||||
GLSMHooks.LIGHTMAP_COORDS.addListener(event -> {
|
||||
OpenGlHelper.lastBrightnessX = event.x;
|
||||
OpenGlHelper.lastBrightnessY = event.y;
|
||||
});
|
||||
}
|
||||
}
|
||||