Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e1fd9d0e9 | ||
|
|
58938e82a7 | ||
|
|
be6c8c85b5 | ||
|
|
39bca37d0d | ||
|
|
1c61be04a7 | ||
|
|
7a97cd9386 | ||
|
|
5b09bde7a0 | ||
|
|
edc0081b7c | ||
|
|
913256b032 | ||
|
|
e1f5083d3e | ||
|
|
b55e693ad2 | ||
|
|
21c01a1aa4 | ||
|
|
83a26b2291 | ||
|
|
7e44c1efcb | ||
|
|
155f209db5 | ||
|
|
c4a6c165a0 | ||
|
|
624fe6b453 | ||
|
|
fd45fda0ab | ||
|
|
00fc97a611 | ||
|
|
445dc487b9 | ||
|
|
f43fbd4d9f | ||
|
|
dbabc4b3dc |
No files matched your search
@@ -1,302 +0,0 @@
|
||||
# CTM Guide<img src="img/logo-readme.png" align="right">
|
||||
###### by Vortex [@vortex_so in discord, any suggestions are accepted]<br>
|
||||
|
||||
This file describes how to use CTM, structure of `.properties` file and some useful (or not) techniques
|
||||
|
||||
### Note:
|
||||
- All textures for CTM textures are in `[modname]/mcpatcher/ctm` folder<br>
|
||||
the overrides apply globally so it doesn't matter in which directory you put them<br>
|
||||
|
||||
- in the `ctm` folder you must have a folder with your block name (or any name actually)<br>
|
||||
in that folder you will have all texture files (animations allowed via mcmeta file for every texture) and `.properties` file<br>
|
||||
multiple `.properties` files are allowed for a single folder and set of textures for more complex behaviour (see examples)
|
||||
|
||||
## Quick Links
|
||||
- **[Method](#method)**
|
||||
- **[MatchBlocks](#matchblocks)**
|
||||
- **[MatchTiles](#matchtiles)**
|
||||
- **[Tiles](#tiles)**
|
||||
- **[Connect](#connect)**
|
||||
- **[Faces](#faces)**
|
||||
- **[Biomes](#biomes)**
|
||||
- **[Heights](#heights)**
|
||||
- **[RenderPass](#renderpass)**
|
||||
- **[InnerSeams](#innerseams)**
|
||||
- **[Weights](#weights)**<br>
|
||||
- **[Examples](#examples)**
|
||||
|
||||
## Structure of `.properties` files
|
||||
|
||||
---
|
||||
|
||||
## `matchBlocks`
|
||||
Override for a specific block, you can get the value from the tooltip if you have this feature enabled.<br>
|
||||
Can accept several values which allows to apply one CTM for different blocks.<br>
|
||||
if no value for this property was provided, Angelica will fallback to name of the `.property` file
|
||||
|
||||
examples:<br>
|
||||
`matchBlocks=gregtech:gt.blockcasings4`<br>
|
||||
`matchBlocks=minecraft:glass minecraft:glass_pane`
|
||||
|
||||
---
|
||||
|
||||
## `matchTiles`
|
||||
Override for a specific texture, main difference from matchBlocks is that this will only match a certain texture, not the block itself<br>
|
||||
|
||||
example:<br>
|
||||
`matchTiles:minecraft:blocks/stone`
|
||||
|
||||
---
|
||||
|
||||
## `tiles`
|
||||
The list of texture names for CTM. Supports ranges and explicit names.<br>
|
||||
You probably would want to use texture names like `0.png`, `1.png` as it is easier, however this allows for any names
|
||||
|
||||
examples:<br>
|
||||
`tiles=0-46` - classic for full CTM, expects files from `0.png` to `46.png`<br>
|
||||
`tiles=0-3,5, 8` - expects files `0.png`, `1.png`, `2.png`, `3.png`, `5.png`, `8.png`<br>
|
||||
`tiles=sometext,somemoretext` - expects files `sometext.png`, `somemoretext.png`
|
||||
|
||||
---
|
||||
|
||||
## `connect`
|
||||
Sets how the blocks are connected, can accept values: `block`, `tile`, `material`<br>
|
||||
`block` connects based on block (who would guess)<br>
|
||||
`tile` connects based on texture<br>
|
||||
`material` connects based on material (didn't really understand this part, ig it is for stone, iron, wood etc)<br>
|
||||
Fallbacks to `block` if no value for `matchTiles` is set, otherwise fallbacks to `tile`<br>
|
||||
Most of the time you don't need to set this value because of that fallback rule
|
||||
|
||||
example:<br>
|
||||
`connect=block`
|
||||
|
||||
---
|
||||
|
||||
## `faces`
|
||||
Limit to specific faces of a block, can accept several values<br>
|
||||
Fallbacks to `all` if value not present in the `.properties` file
|
||||
|
||||
examples:<br>
|
||||
`faces=top`<br>
|
||||
`faces=all`<br>
|
||||
`faces=bottom`<br>
|
||||
`faces=sides`<br>
|
||||
`faces=north south east west`<br>
|
||||
`faces=top bottom sides`
|
||||
|
||||
---
|
||||
|
||||
## `biomes`
|
||||
Limit to specific biomes, can accept several values<br>
|
||||
Fallbacks to all if value not present in the `.properties` file
|
||||
|
||||
examples:<br>
|
||||
`biomes=plains`<br>
|
||||
`biomes=desert forest`<br>
|
||||
`biomes=taiga jungle savanna`
|
||||
|
||||
---
|
||||
|
||||
## `heights`
|
||||
Limit to certain Y levels, can only accept several values<br>
|
||||
can be set through either `minHeight` and `maxHeight` or `heights`
|
||||
|
||||
examples:<br>
|
||||
`heights=0-64`<br>
|
||||
`heights=64-255`<br>
|
||||
example of maxHeight and minHeight:<br>
|
||||
`minHeight=64`<br>
|
||||
`maxHeight=128`
|
||||
|
||||
---
|
||||
|
||||
## `renderPass`
|
||||
Controls which rendering layer a CTM rule is applied in<br>
|
||||
In most cases you only need it to avoid rendering issues with transparency
|
||||
|
||||
`renderPass=solid` used for fully opaque blocks<br>
|
||||
`renderPass=cutout` used for fences, glass CTM, textured blocks with hard transparency<br>
|
||||
`renderPass=translucent` used for transparent blocks like glass or water<br>
|
||||
`renderPass=cutout_mipped` no idea for what<br>
|
||||
`renderPass=overlay` used for blocks with an overlay, for example GT hatches or damaged anvils<br>
|
||||
`renderPass=backface` used for blocks with a backface, however it is very rare to use this one
|
||||
|
||||
---
|
||||
|
||||
## `innerSeams`
|
||||
Adds internal seams, fallback to `innerSeams=false` if not set<br>
|
||||
|
||||
example:<br>
|
||||
`innerSeams=true`
|
||||
|
||||
---
|
||||
|
||||
## `weights`
|
||||
Priority, used for `Random` type to determine which textures have certain chances to be used for render<br>
|
||||
accepts several values, assigns values correspondingly to `tiles` list values
|
||||
|
||||
examples:<br>
|
||||
`weight=10`<br>
|
||||
`weights=1 3 3 2 1`
|
||||
|
||||
---
|
||||
|
||||
# `Method`
|
||||
|
||||
Selects CTM mode:
|
||||
- [LINK](#full-ctm-ctm) `ctm` / `glass` / `default`
|
||||
- [LINK](#compact-ctm) `compact` / `ctm_compact`
|
||||
- [LINK](#horizontal)`horizontal` / `bookshelf`
|
||||
- [LINK](#vertical)`vertical`
|
||||
- [LINK](#horizontalvertical-hv)`horizontal+vertical` / `h+v`
|
||||
- [LINK](#verticalhorizontal-vh)`vertical+horizontal` / `v+h`
|
||||
- [LINK](#top)`top` / `sandstone`
|
||||
- [LINK](#repeat)`repeat` / `pattern`
|
||||
- [LINK](#random)`random`
|
||||
- [LINK](#fixed)`fixed` / `static`
|
||||
|
||||
example:<br>
|
||||
`method=ctm`
|
||||
|
||||
### Full CTM (ctm)
|
||||
|
||||
Requires exactly 47 tiles, the most versatile but the most size-heavy method
|
||||
|
||||
Here's a visual explanation of how the textures should be numbered:
|
||||
|
||||
<img src="img/CTM/CTM-pattern.png" width="624" style="image-rendering: pixelated;">
|
||||
|
||||
---
|
||||
|
||||
### Compact CTM
|
||||
|
||||
Requires exactly 5 tiles.
|
||||
|
||||
<img src="img/CTM/CCTM-pattern.png" width="284" style="image-rendering: pixelated;">
|
||||
|
||||
Splits face into 4 quadrants and builds final texture per quadrant.
|
||||
|
||||
Here's a visual explanation of how it works on an example of 3x3 blocks pattern with a cutout:
|
||||
|
||||
<img src="img/CTM/CCTM-principle.png" width="400" style="image-rendering: pixelated;">
|
||||
|
||||
as you can see, compact CTM has a single limitation:<br>
|
||||
the size of any part of texture, such as an edge or a corner, can't be larger than `8x8` pixel<br>
|
||||
this is the main case where only full CTM can be used.
|
||||
|
||||
---
|
||||
|
||||
### Horizontal
|
||||
|
||||
Requires exactly 4 tiles. connects only horizontally, used for bookshelves for example
|
||||
|
||||
<img src="img/CTM/HCTM-pattern.png" width="284" style="image-rendering: pixelated;">
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Vertical
|
||||
|
||||
Requires exactly 4 tiles.
|
||||
|
||||
<img src="img/CTM/VCTM-pattern.png" height="284" style="image-rendering: pixelated;">
|
||||
|
||||
---
|
||||
|
||||
### Horizontal+Vertical (h+v)
|
||||
|
||||
Requires exactly 7 tiles.
|
||||
|
||||
Two-stage: horizontal first, then vertical if none horizontal variations are found.
|
||||
|
||||
<img src="img/CTM/HVCTM-pattern.png" height="284" style="image-rendering: pixelated;">
|
||||
|
||||
---
|
||||
|
||||
### Vertical+Horizontal (v+h)
|
||||
|
||||
Requires exactly 7 tiles.
|
||||
|
||||
Two-stage: vertical first, then horizontal if none vertical variations are found.
|
||||
|
||||
<img src="img/CTM/VHCTM-pattern.png" height="284" style="image-rendering: pixelated;">
|
||||
|
||||
---
|
||||
|
||||
### Top
|
||||
|
||||
Requires exactly 1 tile.
|
||||
|
||||
Applies tile on side faces when block above matches, for example used for smooth sandstone
|
||||
|
||||
---
|
||||
|
||||
### Repeat
|
||||
|
||||
Requires width * height tiles.
|
||||
|
||||
Properties:
|
||||
- `width` - defines the number of tiles in width
|
||||
- `height` - defines the number of tiles in height
|
||||
- `symmetry` - defines the symmetry of the pattern, can be either `none` or `opposite`
|
||||
|
||||
Just uses N*M grid, for example<br>
|
||||
`0 1 2 3 4`<br>
|
||||
`5 6 7 8 9`
|
||||
|
||||
---
|
||||
|
||||
### Random
|
||||
|
||||
Any number of tiles.
|
||||
|
||||
Properties:
|
||||
- `weights` - described [here](#weights)
|
||||
- `symmetry` - accepts `all`, `opposite`, `none`, groups opposite faces together, otherwise keeps them independent
|
||||
- `linked` - makes the textures consistent between render states, not needed much
|
||||
|
||||
---
|
||||
|
||||
### Fixed
|
||||
|
||||
Requires exactly 1 tile. Always uses the same tile.<br>
|
||||
useful only if combined with height or other properties (see examples)
|
||||
|
||||
---
|
||||
|
||||
# Examples
|
||||
|
||||
## Glass via full CTM
|
||||
```properties
|
||||
matchBlocks=minecraft:glass minecraft:glass_pane
|
||||
metadata=0
|
||||
method=ctm
|
||||
faces=all
|
||||
tiles=0-46
|
||||
connect=block
|
||||
```
|
||||
|
||||
## Multiple properties files usage
|
||||
### Use multiple .properties files with different heights for more detailed worldgen
|
||||
this one will use one texture for lower part of the world and another for the rest of the world
|
||||
```properties
|
||||
matchBlocks=minecraft:dirt
|
||||
metadata=0
|
||||
method=fixed
|
||||
faces=all
|
||||
tiles=0
|
||||
connect=block
|
||||
maxHeight=40
|
||||
```
|
||||
|
||||
```properties
|
||||
matchBlocks=minecraft:dirt
|
||||
metadata=0
|
||||
method=fixed
|
||||
faces=all
|
||||
tiles=0
|
||||
connect=block
|
||||
minHeight=41
|
||||
```
|
||||
|
||||
---
|
||||
@@ -76,7 +76,7 @@ dependencies {
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.9.36:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.9.52:dev")
|
||||
|
||||
shadowImplementation(project(":glsm")) { transitive = false }
|
||||
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
|
||||
@@ -129,6 +129,8 @@ dependencies {
|
||||
compileOnly(rfg.deobf("curse.maven:et-futurum-requiem-441392:5873837"))
|
||||
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.3-GTNH:dev")
|
||||
|
||||
// For testing alternative splash screens — https://github.com/MalTeeez/ModernSplash/releases
|
||||
// devOnlyNonPublishable("com.github.MalTeeez:modernsplash:1.7.10-1.2.4:dev")
|
||||
|
||||
|
||||
@@ -146,6 +146,10 @@ tasks.test {
|
||||
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")
|
||||
&& System.getProperty("os.arch") == "aarch64") {
|
||||
vendor = JvmVendorSpec.AZUL
|
||||
}
|
||||
}
|
||||
|
||||
dependsOn(downgradeMainClasses, downgradeTestClasses, downgradeDepsForTest)
|
||||
|
||||
@@ -9,13 +9,10 @@ import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
|
||||
@@ -188,6 +188,8 @@ public class GLStateManager {
|
||||
|
||||
@Getter private static Vendor VENDOR;
|
||||
|
||||
@Getter private static boolean windows;
|
||||
|
||||
// This setting varies depending on driver, so it gets queried at runtime
|
||||
public static int DEFAULT_DRAW_BUFFER = 0x0405; // GL_BACK
|
||||
|
||||
@@ -518,6 +520,12 @@ public class GLStateManager {
|
||||
// After Display.create(), viewport is (0, 0, width, height)
|
||||
viewportState.setViewPort(0, 0, displayWidth, displayHeight);
|
||||
}
|
||||
|
||||
final String glVendor = RENDER_BACKEND.getString(GL11.GL_VENDOR);
|
||||
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
|
||||
|
||||
final String os = System.getProperty("os.name").toLowerCase();
|
||||
windows = os.contains("win");
|
||||
}
|
||||
|
||||
static void init(Runnable initCallback) {
|
||||
@@ -525,9 +533,6 @@ public class GLStateManager {
|
||||
|
||||
DEFAULT_DRAW_BUFFER = RENDER_BACKEND.getInteger(GL11.GL_DRAW_BUFFER);
|
||||
|
||||
final String glVendor = RENDER_BACKEND.getString(GL11.GL_VENDOR);
|
||||
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
|
||||
|
||||
// The initial mask value should be defined as all 1's. However, some drivers have it set to 0's.
|
||||
// To ensure consistency & correctness across all drivers, we're setting them to 0xFF.
|
||||
RENDER_BACKEND.stencilFunc(stencilState.getFuncFront(), stencilState.getRefFront(), 0xFF);
|
||||
@@ -621,16 +626,6 @@ public class GLStateManager {
|
||||
return BYPASS_CACHE || !isCachingEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should use DSA for texture operations. DSA relies on cached texture bindings, which aren't tracked when caching is disabled. Also,
|
||||
* proxy textures are special query textures that shouldn't use DSA.
|
||||
*/
|
||||
private static boolean shouldUseDSA(int target) {
|
||||
if (!isCachingEnabled()) return false;
|
||||
// Proxy textures are for capability queries, not real textures
|
||||
return target != GL11.GL_PROXY_TEXTURE_1D && target != GL11.GL_PROXY_TEXTURE_2D && target != GL12.GL_PROXY_TEXTURE_3D;
|
||||
}
|
||||
|
||||
// LWJGL Overrides
|
||||
public static void glEnable(int cap) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
@@ -1828,13 +1823,7 @@ public class GLStateManager {
|
||||
}
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
suspendPixelUnpackBuffer();
|
||||
if (shouldUseDSA(target)) {
|
||||
// Use DSA to upload directly to the texture
|
||||
RenderSystem.textureImage2D(getBoundTextureForServerState(), target, level, internalformat, width, height, border, format, type, pixels);
|
||||
} else {
|
||||
// Non-main thread or proxy texture - use direct GL call
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
@@ -1879,11 +1868,7 @@ public class GLStateManager {
|
||||
}
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels != null ? pixels.asIntBuffer() : null);
|
||||
suspendPixelUnpackBuffer();
|
||||
if (shouldUseDSA(target)) {
|
||||
RenderSystem.textureImage2D(getBoundTextureForServerState(), target, level, internalformat, width, height, border, format, type, pixels);
|
||||
} else {
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
@@ -4295,6 +4280,7 @@ public class GLStateManager {
|
||||
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = 0;
|
||||
GLSMHooks.programChangeEvent.postBind = false;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
ffp.activate();
|
||||
@@ -4315,6 +4301,7 @@ public class GLStateManager {
|
||||
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = program;
|
||||
GLSMHooks.programChangeEvent.postBind = false;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
if (initConfig != null && initConfig.isLwjglDebug()) {
|
||||
@@ -4323,6 +4310,12 @@ public class GLStateManager {
|
||||
}
|
||||
RENDER_BACKEND.useProgram(program);
|
||||
CompatUniformManager.onUseProgram(program);
|
||||
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = program;
|
||||
GLSMHooks.programChangeEvent.postBind = true;
|
||||
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4399,6 +4392,7 @@ public class GLStateManager {
|
||||
|
||||
public static int glGetError() {
|
||||
if (!RENDER_BACKEND.hasContext()) return 0;
|
||||
if (initConfig.noErrorChecks()) return 0;
|
||||
return RENDER_BACKEND.getError();
|
||||
}
|
||||
|
||||
@@ -4453,13 +4447,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
if (shouldUseDSA(target)) {
|
||||
// Use DSA to upload directly to the texture - keeps GL binding state unchanged
|
||||
RenderSystem.textureSubImage2D(getBoundTextureForServerState(), target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
} else {
|
||||
// Non-main thread or proxy texture - use direct GL call
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
@@ -4472,13 +4460,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
if (shouldUseDSA(target)) {
|
||||
// Use DSA to upload directly to the texture
|
||||
RenderSystem.textureSubImage2D(getBoundTextureForServerState(), target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
} else {
|
||||
// Non-main thread or proxy texture - use direct GL call
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
@@ -5198,7 +5180,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static boolean isFramebufferEnabled() {
|
||||
return initConfig != null && initConfig.isFramebufferSupported() && initConfig.isFboEnabled();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final boolean FFP_WARN_ON_UNSUPPORTED = Boolean.parseBoolean(System.getProperty("angelica.ffp.warnOnUnsupported", "false"));
|
||||
@@ -5726,6 +5708,14 @@ public class GLStateManager {
|
||||
return RENDER_BACKEND.getProgramInfoLog(program);
|
||||
}
|
||||
|
||||
public static boolean glIsProgram(int obj) {
|
||||
return RENDER_BACKEND.isProgram(obj);
|
||||
}
|
||||
|
||||
public static boolean glIsShader(int obj) {
|
||||
return RENDER_BACKEND.isShader(obj);
|
||||
}
|
||||
|
||||
public static void glBindImageTexture(int unit, int texture, int level, boolean layered, int layer, int access, int format) { RENDER_BACKEND.bindImageTexture(unit, texture, level, layered, layer, access, format); }
|
||||
public static void glMemoryBarrier(int barriers) { RENDER_BACKEND.memoryBarrier(barriers); }
|
||||
public static void glCopyImageSubData(int srcName, int srcTarget, int srcLevel, int srcX, int srcY, int srcZ,
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.glsm.dsa.DSACore;
|
||||
import com.gtnewhorizons.angelica.glsm.dsa.DSAEXT;
|
||||
import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -61,7 +62,13 @@ public class RenderSystem {
|
||||
if (rendererInitialized) return;
|
||||
rendererInitialized = true;
|
||||
try {
|
||||
if (GLStateManager.capabilities.OpenGL45) {
|
||||
if (GLStateManager.vendorIsIntel() && GLStateManager.isWindows()) {
|
||||
dsaState = new DSAUnsupported();
|
||||
GLStateManager.LOGGER.info("Detected Intel drivers on Windows, disabling DSA.");
|
||||
} else if (!GLStateManager.getInitConfig().isDSAEnabled()) {
|
||||
dsaState = new DSAUnsupported();
|
||||
GLStateManager.LOGGER.info("enableDSA is set to false, disabling DSA.");
|
||||
} else if (GLStateManager.capabilities.OpenGL45) {
|
||||
dsaState = (Runtime.version().feature() > 8 && GLStateManager.capabilities.GL_EXT_direct_state_access) ? new DSAEXT() : new DSACore();
|
||||
GLStateManager.LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
|
||||
}
|
||||
|
||||
@@ -8,24 +8,24 @@ import java.util.function.Consumer;
|
||||
public final class GLSMInitConfig {
|
||||
private final boolean lwjglDebug;
|
||||
private final StreamingUploader.UploadStrategy streamingUploadStrategy;
|
||||
private final boolean framebufferSupported;
|
||||
private final boolean fboEnabled;
|
||||
private final Consumer<DirectTessellator> directDrawer;
|
||||
private final Runnable streamingDrawerDestroy;
|
||||
private final int displayWidth;
|
||||
private final int displayHeight;
|
||||
private final Runnable postInitCallback;
|
||||
private final boolean enableDSA;
|
||||
private final boolean noErrorChecks;
|
||||
|
||||
private GLSMInitConfig(Builder builder) {
|
||||
this.lwjglDebug = builder.lwjglDebug;
|
||||
this.streamingUploadStrategy = builder.streamingUploadStrategy;
|
||||
this.framebufferSupported = builder.framebufferSupported;
|
||||
this.fboEnabled = builder.fboEnabled;
|
||||
this.noErrorChecks = builder.noErrorChecks;
|
||||
this.directDrawer = builder.directDrawer;
|
||||
this.streamingDrawerDestroy = builder.streamingDrawerDestroy;
|
||||
this.displayWidth = builder.displayWidth;
|
||||
this.displayHeight = builder.displayHeight;
|
||||
this.postInitCallback = builder.postInitCallback;
|
||||
this.enableDSA = builder.enableDSA;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
@@ -34,24 +34,24 @@ public final class GLSMInitConfig {
|
||||
|
||||
public boolean isLwjglDebug() { return lwjglDebug; }
|
||||
public StreamingUploader.UploadStrategy getStreamingUploadStrategy() { return streamingUploadStrategy; }
|
||||
public boolean isFramebufferSupported() { return framebufferSupported; }
|
||||
public boolean isFboEnabled() { return fboEnabled; }
|
||||
public Consumer<DirectTessellator> getDirectDrawer() { return directDrawer; }
|
||||
public Runnable getStreamingDrawerDestroy() { return streamingDrawerDestroy; }
|
||||
public int getDisplayWidth() { return displayWidth; }
|
||||
public int getDisplayHeight() { return displayHeight; }
|
||||
public Runnable getPostInitCallback() { return postInitCallback; }
|
||||
public boolean isDSAEnabled() { return enableDSA; }
|
||||
public boolean noErrorChecks() { return noErrorChecks; }
|
||||
|
||||
public static final class Builder {
|
||||
private boolean lwjglDebug = false;
|
||||
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
|
||||
private boolean framebufferSupported = false;
|
||||
private boolean fboEnabled = false;
|
||||
private Consumer<DirectTessellator> directDrawer = null;
|
||||
private Runnable streamingDrawerDestroy = null;
|
||||
private int displayWidth = 0;
|
||||
private int displayHeight = 0;
|
||||
private Runnable postInitCallback = null;
|
||||
private boolean enableDSA = false;
|
||||
private boolean noErrorChecks;
|
||||
|
||||
private Builder() {}
|
||||
|
||||
@@ -65,13 +65,8 @@ public final class GLSMInitConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder framebufferSupported(boolean framebufferSupported) {
|
||||
this.framebufferSupported = framebufferSupported;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fboEnabled(boolean fboEnabled) {
|
||||
this.fboEnabled = fboEnabled;
|
||||
public Builder noErrorChecks(boolean noErrorChecks) {
|
||||
this.noErrorChecks = noErrorChecks;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -96,6 +91,11 @@ public final class GLSMInitConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enableDSA(boolean enableDSA) {
|
||||
this.enableDSA = enableDSA;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GLSMInitConfig build() {
|
||||
return new GLSMInitConfig(this);
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
public final class ProgramChangeEvent extends MutableEvent {
|
||||
public int previousProgram;
|
||||
public int newProgram;
|
||||
public boolean postBind;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.ContextAttribs;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
import org.lwjgl.opengl.SharedDrawable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
class SharedDrawableVaoTest {
|
||||
|
||||
private static final String VERT_330 = "#version 330 core\nvoid main() { gl_Position = vec4(0.0); }\n";
|
||||
private static final String FRAG_330 = "#version 330 core\nout vec4 o;\nvoid main() { o = vec4(0.0); }\n";
|
||||
|
||||
@BeforeAll
|
||||
static void createContext() throws LWJGLException {
|
||||
assumeTrue(!Display.isCreated(), "inherited Display -- skipping to avoid profile drift");
|
||||
Display.setDisplayModeAndFullscreen(new DisplayMode(800, 600));
|
||||
Display.setResizable(false);
|
||||
Display.setFullscreen(false);
|
||||
Display.create(
|
||||
new PixelFormat().withDepthBits(24).withStencilBits(8),
|
||||
new ContextAttribs(4, 1).withProfileCore(true).withForwardCompatible(true));
|
||||
GL30.glBindVertexArray(GL30.glGenVertexArrays());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void destroyContext() {
|
||||
if (Display.isCreated()) Display.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateProgramSucceedsOnSharedDrawableWithVao() throws LWJGLException {
|
||||
SharedDrawable shared = new SharedDrawable(Display.getDrawable());
|
||||
Display.getDrawable().releaseContext();
|
||||
shared.makeCurrent();
|
||||
try {
|
||||
GL30.glBindVertexArray(GL30.glGenVertexArrays());
|
||||
int program = linkTrivialProgram();
|
||||
GL20.glValidateProgram(program);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(program, GL20.GL_VALIDATE_STATUS), () -> GL20.glGetProgramInfoLog(program, 4096));
|
||||
GL20.glDeleteProgram(program);
|
||||
} finally {
|
||||
shared.releaseContext();
|
||||
Display.getDrawable().makeCurrent();
|
||||
shared.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private static int linkTrivialProgram() {
|
||||
int vs = compile(GL20.GL_VERTEX_SHADER, VERT_330);
|
||||
int fs = compile(GL20.GL_FRAGMENT_SHADER, FRAG_330);
|
||||
int program = GL20.glCreateProgram();
|
||||
GL20.glAttachShader(program, vs);
|
||||
GL20.glAttachShader(program, fs);
|
||||
GL20.glLinkProgram(program);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(program, GL20.GL_LINK_STATUS), () -> GL20.glGetProgramInfoLog(program, 4096));
|
||||
GL20.glDeleteShader(vs);
|
||||
GL20.glDeleteShader(fs);
|
||||
return program;
|
||||
}
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
int shader = GL20.glCreateShader(type);
|
||||
GL20.glShaderSource(shader, src);
|
||||
GL20.glCompileShader(shader);
|
||||
assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS), () -> GL20.glGetShaderInfoLog(shader, 4096));
|
||||
return shader;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 216 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 310 B |
|
Before Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 316 B |
@@ -1,8 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* To be used for modded items that don't have blocks. Baubles for example.
|
||||
*/
|
||||
public interface IDynamicLightProducer {
|
||||
int getLuminance();
|
||||
int getLuminance(ItemStack stack);
|
||||
}
|
||||
@@ -4,7 +4,14 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IndexBuffer;
|
||||
import com.gtnewhorizon.gtnhlib.util.font.GlyphReplacements;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_PAYLOAD;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_PAYLOAD;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
|
||||
@@ -213,12 +220,14 @@ public class BatchingFontRenderer {
|
||||
pushQuadIdx();
|
||||
}
|
||||
|
||||
private void pushTexRect(float x, float y, float w, float h, float itOff, int rgba, float uStart, float vStart, float uSz, float vSz) {
|
||||
private void pushTexRect(float x, float y, float w, float h, float itOff, int rgba, float uStart, float vStart, float uSz, float vSz, boolean flipV) {
|
||||
ensureCapacity();
|
||||
pushVtx(x + itOff, y, rgba, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x - itOff, y + h, rgba, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x + itOff + w, y, rgba, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x - itOff + w, y + h, rgba, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
float vTop = flipV ? vStart + vSz : vStart;
|
||||
float vBot = flipV ? vStart : vStart + vSz;
|
||||
pushVtx(x + itOff, y, rgba, uStart, vTop, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x - itOff, y + h, rgba, uStart, vBot, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x + itOff + w, y, rgba, uStart + uSz, vTop, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushVtx(x - itOff + w, y + h, rgba, uStart + uSz, vBot, uStart, uStart + uSz, vStart, vStart + vSz);
|
||||
pushQuadIdx();
|
||||
}
|
||||
|
||||
@@ -442,6 +451,55 @@ public class BatchingFontRenderer {
|
||||
return (what >= fromInclusive) && (what <= toInclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count visible characters from {@code start} to {@code end}, skipping format codes.
|
||||
* Stops at §r, any color code (§0-f, §x), or any color effect (§q, §g).
|
||||
* Style codes (§k-o) and non-terminating effects (§z wave, §v dinnerbone) are skipped.
|
||||
*/
|
||||
private static int countVisibleChars(CharSequence str, int start, int end) {
|
||||
int count = 0;
|
||||
for (int i = start; i < end; i++) {
|
||||
char ch = str.charAt(i);
|
||||
if (ch == FORMATTING_CHAR && i + 1 < end) {
|
||||
char code = Character.toLowerCase(str.charAt(i + 1));
|
||||
if (code == 'r' || (code >= '0' && code <= '9') || (code >= 'a' && code <= 'f')
|
||||
|| code == 'x' || code == 'q' || code == 'g') {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return Math.max(count, 1);
|
||||
}
|
||||
|
||||
private static int hsvToRgb(float hue, float sat, float val) {
|
||||
int h = (int)(hue / 60f) % 6;
|
||||
float f = hue / 60f - h;
|
||||
float p = val * (1 - sat);
|
||||
float q = val * (1 - f * sat);
|
||||
float t = val * (1 - (1 - f) * sat);
|
||||
float r, g, b;
|
||||
switch (h) {
|
||||
case 0: r=val; g=t; b=p; break;
|
||||
case 1: r=q; g=val; b=p; break;
|
||||
case 2: r=p; g=val; b=t; break;
|
||||
case 3: r=p; g=q; b=val; break;
|
||||
case 4: r=t; g=p; b=val; break;
|
||||
default: r=val; g=p; b=q; break;
|
||||
}
|
||||
return ((int)(r*255) << 16) | ((int)(g*255) << 8) | (int)(b*255);
|
||||
}
|
||||
|
||||
private static final int RAINBOW_LUT_SIZE = 24;
|
||||
private static final int[] RAINBOW_LUT = new int[RAINBOW_LUT_SIZE];
|
||||
static {
|
||||
for (int i = 0; i < RAINBOW_LUT_SIZE; i++) {
|
||||
RAINBOW_LUT[i] = hsvToRgb(i * 15f, 1f, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean forceDefaults() {
|
||||
return this.bookMode || this.isSGA || this.isSplash;
|
||||
}
|
||||
@@ -466,7 +524,8 @@ public class BatchingFontRenderer {
|
||||
return forceDefaults() ? 1 : FontConfig.fontShadowOffset;
|
||||
}
|
||||
|
||||
private static final char FORMATTING_CHAR = 167; // §
|
||||
private static final double WAVE_TIME_SCALE = 5e-9;
|
||||
private static final float WAVE_FREQUENCY = 0.5f;
|
||||
|
||||
public float drawString(final float anchorX, final float anchorY, final int color, final boolean enableShadow,
|
||||
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
|
||||
@@ -497,6 +556,15 @@ public class BatchingFontRenderer {
|
||||
boolean curBold = false;
|
||||
boolean curStrikethrough = false;
|
||||
boolean curUnderline = false;
|
||||
boolean curRainbow = false;
|
||||
boolean curWave = false;
|
||||
boolean curDinnerbone = false;
|
||||
boolean curGradient = false;
|
||||
int gradientStartRgb = 0, gradientEndRgb = 0;
|
||||
int gradientCharIndex = 0, gradientTotalChars = 0;
|
||||
float gradientStep = 0f;
|
||||
int rainbowCharIndex = 0;
|
||||
int visibleCharIndex = 0;
|
||||
|
||||
float glyphScaleY = getGlyphScaleY();
|
||||
float glyphScaleX = getGlyphScaleX();
|
||||
@@ -534,6 +602,16 @@ public class BatchingFontRenderer {
|
||||
strikethroughStartX = strikethroughEndX;
|
||||
}
|
||||
|
||||
if (fmtCode == 'x' && AngelicaConfig.enableRGBColors && charIdx + SECTION_X_PAYLOAD < stringEnd) {
|
||||
int rgb = ColorCodeUtils.parseHexPairs(string, charIdx + 1, 6);
|
||||
if (rgb != -1) {
|
||||
curRainbow = false;
|
||||
curGradient = false;
|
||||
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
|
||||
curShadowColor = (curShadowColor & 0xFF000000) | ((rgb & 0xFCFCFC) >> 2);
|
||||
charIdx += SECTION_X_PAYLOAD;
|
||||
}
|
||||
} else {
|
||||
final boolean is09 = charInRange(fmtCode, '0', '9');
|
||||
final boolean isAF = charInRange(fmtCode, 'a', 'f');
|
||||
if (is09 || isAF) {
|
||||
@@ -542,6 +620,9 @@ public class BatchingFontRenderer {
|
||||
curStrikethrough = false;
|
||||
curUnderline = false;
|
||||
curItalic = false;
|
||||
curRainbow = false;
|
||||
curGradient = false;
|
||||
// wave/dinnerbone NOT reset — they're positional effects, independent of color
|
||||
|
||||
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
|
||||
final int rgb = this.colorCode[colorIdx];
|
||||
@@ -562,15 +643,41 @@ public class BatchingFontRenderer {
|
||||
underlineEndX = underlineStartX;
|
||||
} else if (fmtCode == 'o') {
|
||||
curItalic = true;
|
||||
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
|
||||
curRainbow = true;
|
||||
curGradient = false;
|
||||
rainbowCharIndex = 0;
|
||||
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
|
||||
curWave = !curWave;
|
||||
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
|
||||
curDinnerbone = !curDinnerbone;
|
||||
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
|
||||
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
|
||||
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
|
||||
if (color1 != -1 && color2 != -1) {
|
||||
curGradient = true;
|
||||
curRainbow = false;
|
||||
gradientStartRgb = color1;
|
||||
gradientEndRgb = color2;
|
||||
gradientCharIndex = 0;
|
||||
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
|
||||
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
|
||||
charIdx += GRADIENT_PAYLOAD;
|
||||
}
|
||||
} else if (fmtCode == 'r') {
|
||||
curRandom = false;
|
||||
curBold = false;
|
||||
curStrikethrough = false;
|
||||
curUnderline = false;
|
||||
curItalic = false;
|
||||
curRainbow = false;
|
||||
curWave = false;
|
||||
curDinnerbone = false;
|
||||
curGradient = false;
|
||||
curColor = color;
|
||||
curShadowColor = shadowColor;
|
||||
}
|
||||
} // close else block for non-§x codes
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -597,12 +704,31 @@ public class BatchingFontRenderer {
|
||||
heightNorth = anchorY + (underlying.FONT_HEIGHT - 1.0f) * (0.5f - glyphScaleY * fontProvider.getYScaleMultiplier() / 2);
|
||||
float heightSouth = (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY * fontProvider.getYScaleMultiplier();
|
||||
|
||||
visibleCharIndex++;
|
||||
|
||||
// Check ASCII space, NBSP, NNBSP
|
||||
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
|
||||
curX += 4 * this.getWhitespaceScale() + (curBold ? 1 : 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curRainbow) {
|
||||
int rgbEffect = RAINBOW_LUT[rainbowCharIndex % RAINBOW_LUT_SIZE];
|
||||
curColor = (curColor & 0xFF000000) | rgbEffect;
|
||||
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
|
||||
rainbowCharIndex++;
|
||||
}
|
||||
if (curGradient && gradientTotalChars > 0) {
|
||||
float t = Math.min(gradientCharIndex * gradientStep, 1f);
|
||||
int gr = (int)((gradientStartRgb >> 16 & 0xFF) * (1-t) + (gradientEndRgb >> 16 & 0xFF) * t);
|
||||
int gg = (int)((gradientStartRgb >> 8 & 0xFF) * (1-t) + (gradientEndRgb >> 8 & 0xFF) * t);
|
||||
int gb = (int)((gradientStartRgb & 0xFF) * (1-t) + (gradientEndRgb & 0xFF) * t);
|
||||
int rgbEffect = (gr << 16) | (gg << 8) | gb;
|
||||
curColor = (curColor & 0xFF000000) | rgbEffect;
|
||||
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
|
||||
gradientCharIndex++;
|
||||
}
|
||||
|
||||
final float uStart = fontProvider.getUStart(chr);
|
||||
final float vStart = fontProvider.getVStart(chr);
|
||||
final float xAdvance = fontProvider.getXAdvance(chr) * glyphScaleX;
|
||||
@@ -616,23 +742,30 @@ public class BatchingFontRenderer {
|
||||
final ResourceLocation texture = fontProvider.getTexture(chr);
|
||||
final int idxId = idxWriterIndex;
|
||||
|
||||
// Wave: Y offset via sine wave
|
||||
float renderY = heightNorth;
|
||||
if (curWave) {
|
||||
float time = HUDCaching.renderingCacheOverride ? 0f : (float)((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
|
||||
renderY += (float) Math.sin(visibleCharIndex * WAVE_FREQUENCY + time) * AngelicaConfig.waveAmplitude;
|
||||
}
|
||||
|
||||
if (enableShadow) {
|
||||
for (int n = 1; n <= shadowCopies; n++) {
|
||||
final float shadowOffsetPart = shadowOffset * ((float) n / shadowCopies);
|
||||
pushTexRect(curX + shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
|
||||
pushTexRect(curX + shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
|
||||
if (curBold) {
|
||||
pushTexRect(curX + 2.0f * shadowOffsetPart, heightNorth + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz);
|
||||
pushTexRect(curX + 2.0f * shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pushTexRect(curX, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
|
||||
pushTexRect(curX, renderY, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
|
||||
if (curBold) {
|
||||
for (int n = 1; n <= boldCopies; n++) {
|
||||
final float shadowOffsetPart = shadowOffset * ((float) n / boldCopies);
|
||||
pushTexRect(curX + shadowOffsetPart, heightNorth, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz);
|
||||
pushTexRect(curX + shadowOffsetPart, renderY, glyphW - 1.0f, heightSouth, itOff, curColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,6 +786,19 @@ public class BatchingFontRenderer {
|
||||
if (bookMode) { curX = (int) curX; }
|
||||
underlineEndX = curX;
|
||||
strikethroughEndX = curX;
|
||||
|
||||
if ((curRainbow || curGradient) && curUnderline && underlineStartX != underlineEndX) {
|
||||
final int ulIdx = idxWriterIndex;
|
||||
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
|
||||
pushDrawCmd(ulIdx, 6, null, false);
|
||||
underlineStartX = underlineEndX;
|
||||
}
|
||||
if ((curRainbow || curGradient) && curStrikethrough && strikethroughStartX != strikethroughEndX) {
|
||||
final int stIdx = idxWriterIndex;
|
||||
pushUntexRect(strikethroughStartX, strikethroughY, strikethroughEndX - strikethroughStartX, glyphScaleY, curColor);
|
||||
pushDrawCmd(stIdx, 6, null, false);
|
||||
strikethroughStartX = strikethroughEndX;
|
||||
}
|
||||
}
|
||||
|
||||
if (curUnderline && underlineStartX != underlineEndX) {
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
|
||||
/**
|
||||
* Utility for converting {@code &}-based shorthand into {@code §}-based
|
||||
* format codes understood by the batched font renderer.
|
||||
* <p>
|
||||
* {@code &#RRGGBB} -> {@code §x§R§R§G§G§B§B} (RGB color)<br>
|
||||
* {@code &g&#RRGGBB&#RRGGBB} -> {@code §g§x§R§R§G§G§B§B§x§R§R§G§G§B§B} (gradient)<br>
|
||||
* {@code &c}, {@code &l}, {@code &r}, {@code &q}, etc. -> {@code §c}, {@code §l}, {@code §r}, {@code §q}
|
||||
* <p>
|
||||
* Effect codes use low-collision letters to avoid false positives on natural text:
|
||||
* rainbow = {@code &q}, wave = {@code &z}, dinnerbone = {@code &v}, gradient = {@code &g} (only with &#-prefixed colors).
|
||||
*/
|
||||
public final class ColorCodeUtils {
|
||||
|
||||
private ColorCodeUtils() {}
|
||||
|
||||
public static final char FORMATTING_CHAR = '§';
|
||||
|
||||
public static final int SECTION_X_PAYLOAD = 12;
|
||||
public static final int SECTION_X_LENGTH = 14;
|
||||
public static final int GRADIENT_PAYLOAD = 28;
|
||||
public static final int GRADIENT_LENGTH = 30;
|
||||
|
||||
/** Valid single {@code &} codes. {@code g} is excluded; {@code &g} only converts as part of a gradient. */
|
||||
public static final String VALID_SINGLE_CODES = "0123456789abcdefklmnorqzv";
|
||||
|
||||
private static final String[] SECTION_PREFIX = new String[128];
|
||||
static {
|
||||
for (int i = 0; i < VALID_SINGLE_CODES.length(); i++) {
|
||||
char c = VALID_SINGLE_CODES.charAt(i);
|
||||
SECTION_PREFIX[c] = String.valueOf(FORMATTING_CHAR) + c;
|
||||
}
|
||||
SECTION_PREFIX['x'] = String.valueOf(FORMATTING_CHAR) + 'x';
|
||||
SECTION_PREFIX['g'] = String.valueOf(FORMATTING_CHAR) + 'g';
|
||||
}
|
||||
|
||||
public static String sectionPrefix(char code) {
|
||||
char c = (code < 128) ? Character.toLowerCase(code) : code;
|
||||
return (c < 128) ? SECTION_PREFIX[c] : null;
|
||||
}
|
||||
|
||||
public static int parseHexPairs(CharSequence str, int start, int count) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
int p = start + i * 2;
|
||||
if (str.charAt(p) != FORMATTING_CHAR) return -1;
|
||||
int digit = Character.digit(str.charAt(p + 1), 16);
|
||||
if (digit == -1) return -1;
|
||||
result = (result << 4) | digit;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int parseSectionXAt(CharSequence str, int start) {
|
||||
if (start + SECTION_X_LENGTH > str.length()) return -1;
|
||||
if (str.charAt(start) != FORMATTING_CHAR) return -1;
|
||||
if (Character.toLowerCase(str.charAt(start + 1)) != 'x') return -1;
|
||||
return parseHexPairs(str, start + 2, 6);
|
||||
}
|
||||
|
||||
public static boolean isValidSectionX(CharSequence str, int start) {
|
||||
return parseSectionXAt(str, start) != -1;
|
||||
}
|
||||
|
||||
private static String lastConversionInput;
|
||||
private static String lastConversionOutput;
|
||||
|
||||
public static String convertAmpersandToSectionX(String text) {
|
||||
if (text == null || !AngelicaConfig.enableAmpersandConversion) return text;
|
||||
|
||||
if (text == lastConversionInput) return lastConversionOutput;
|
||||
|
||||
final String out = convertImpl(text);
|
||||
lastConversionInput = text;
|
||||
lastConversionOutput = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
private static String convertImpl(String text) {
|
||||
int idx = text.indexOf('&');
|
||||
if (idx == -1) return text;
|
||||
|
||||
final int len = text.length();
|
||||
StringBuilder sb = null;
|
||||
int last = 0;
|
||||
while (idx != -1 && idx + 1 < len) {
|
||||
if (text.charAt(idx + 1) == '#' && idx + 7 < len) {
|
||||
boolean validHex = true;
|
||||
for (int i = 2; i <= 7; i++) {
|
||||
if (Character.digit(text.charAt(idx + i), 16) == -1) {
|
||||
validHex = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (validHex) {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx);
|
||||
sb.append(FORMATTING_CHAR).append('x');
|
||||
for (int i = 2; i <= 7; i++) {
|
||||
sb.append(FORMATTING_CHAR).append(text.charAt(idx + i));
|
||||
}
|
||||
last = idx + 8;
|
||||
idx = text.indexOf('&', last);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (Character.toLowerCase(text.charAt(idx + 1)) == 'g' && idx + 17 < len
|
||||
&& text.charAt(idx + 2) == '&' && text.charAt(idx + 3) == '#'
|
||||
&& text.charAt(idx + 10) == '&' && text.charAt(idx + 11) == '#') {
|
||||
boolean valid1 = true, valid2 = true;
|
||||
for (int i = 4; i <= 9; i++) {
|
||||
if (Character.digit(text.charAt(idx + i), 16) == -1) { valid1 = false; break; }
|
||||
}
|
||||
if (valid1) {
|
||||
for (int i = 12; i <= 17; i++) {
|
||||
if (Character.digit(text.charAt(idx + i), 16) == -1) { valid2 = false; break; }
|
||||
}
|
||||
}
|
||||
if (valid1 && valid2) {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx);
|
||||
sb.append(FORMATTING_CHAR).append('g');
|
||||
last = idx + 2;
|
||||
idx = text.indexOf('&', last);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
char code = Character.toLowerCase(text.charAt(idx + 1));
|
||||
if (VALID_SINGLE_CODES.indexOf(code) != -1) {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx);
|
||||
sb.append(FORMATTING_CHAR).append(text.charAt(idx + 1));
|
||||
last = idx + 2;
|
||||
}
|
||||
idx = text.indexOf('&', idx + 1);
|
||||
}
|
||||
if (sb == null) return text;
|
||||
sb.append(text, last, len);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.gtnewhorizons.angelica.compat;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
|
||||
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
|
||||
public class GTNHLibCompat {
|
||||
|
||||
public static final boolean HAS_TEXT_PREPROCESSOR;
|
||||
|
||||
static {
|
||||
boolean found = false;
|
||||
try {
|
||||
FontRendering.class.getMethod("setTextPreprocessor", Function.class);
|
||||
found = true;
|
||||
} catch (NoSuchMethodException ignored) {}
|
||||
HAS_TEXT_PREPROCESSOR = found;
|
||||
}
|
||||
|
||||
/** Only call when {@link #HAS_TEXT_PREPROCESSOR} is true. */
|
||||
public static void registerPreprocessor() {
|
||||
FontRendering.setTextPreprocessor(new FontRendering.TextPreprocessor() {
|
||||
|
||||
@Override
|
||||
public String apply(String text) {
|
||||
return AngelicaConfig.enableAmpersandConversion
|
||||
? ColorCodeUtils.convertAmpersandToSectionX(text)
|
||||
: text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesAmpCodes() {
|
||||
return AngelicaConfig.enableAmpersandConversion;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public class ModStatus {
|
||||
public static boolean isThaumcraftLoaded;
|
||||
public static boolean isThaumicHorizonsLoaded;
|
||||
public static boolean isBaublesLoaded;
|
||||
public static boolean isCosmeticArmorReworkedLoaded;
|
||||
public static boolean isFluidLoggedLoaded;
|
||||
public static boolean isBOPLoaded;
|
||||
|
||||
@@ -43,6 +44,7 @@ public class ModStatus {
|
||||
isThaumcraftLoaded = Loader.isModLoaded("Thaumcraft");
|
||||
isThaumicHorizonsLoaded = Loader.isModLoaded("ThaumicHorizons");
|
||||
isBaublesLoaded = Loader.isModLoaded("Baubles");
|
||||
isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked");
|
||||
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
|
||||
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.gtnewhorizons.angelica.compat.bettercrashes;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
|
||||
import cpw.mods.fml.common.Optional;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import vfyjxf.bettercrashes.utils.StateManager;
|
||||
|
||||
@Optional.Interface(modid = "angelica", iface = "vfyjxf.bettercrashes.utils.StateManager.IResettable")
|
||||
@@ -25,6 +27,7 @@ public class BetterCrashesCompat implements StateManager.IResettable {
|
||||
|
||||
@Override
|
||||
public void resetState() {
|
||||
((ITessellatorData) Tessellator.instance).setDrawing(false);
|
||||
AngelicaMod.LOGGER.info("Reloading SodiumRenderer");
|
||||
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).angelica$reload();
|
||||
AngelicaMod.LOGGER.info("Resetting GLStateManager");
|
||||
|
||||
@@ -63,6 +63,11 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableVAO;
|
||||
|
||||
@Config.Comment("Enables DSA (Direct State Access) for faster bindings. Disable if you notice terrible performance.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableDSA;
|
||||
|
||||
@Config.Comment("Enable NotFine features")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
@@ -107,6 +112,35 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableFontRenderer;
|
||||
|
||||
@Config.Comment("Enable full RGB color support (16.7M colors) using &#RRGGBB syntax in text")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableRGBColors;
|
||||
|
||||
@Config.Comment("Enable gradient text (&g&#start&#end)")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableGradients;
|
||||
|
||||
@Config.Comment("Enable rainbow cycling text (&q)")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableRainbow;
|
||||
|
||||
@Config.Comment("Enable wave/bounce animated text (&z)")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableWaveText;
|
||||
|
||||
@Config.Comment("Enable upside-down text (&v)")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableDinnerboneText;
|
||||
|
||||
@Config.Comment("Wave text amplitude (how far characters bounce)")
|
||||
@Config.DefaultFloat(2.0f)
|
||||
@Config.RangeFloat(min = 1.0f, max = 8.0f)
|
||||
public static float waveAmplitude;
|
||||
|
||||
@Config.Comment("Convert &-prefix format codes (&#RRGGBB, &c, &l, etc.) at render time")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableAmpersandConversion;
|
||||
|
||||
@Config.Comment("Enable Dynamic Lights")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
@@ -297,4 +331,9 @@ public class AngelicaConfig {
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean disableGLVersionPinning;
|
||||
|
||||
@Config.Comment("Disables GL Error checks. Always set to false in dev env or if LWJGL debug is on. Improves performance.")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean disableErrorChecks;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights;
|
||||
|
||||
import baubles.common.lib.PlayerHandler;
|
||||
import lain.mods.cos.CosmeticArmorReworked;
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.IBlockPos;
|
||||
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
|
||||
@@ -180,6 +181,19 @@ public class DynamicLights {
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all tracked light sources, to call when unloading the client world
|
||||
*/
|
||||
public void removeAllLightSources() {
|
||||
this.lightSourcesLock.writeLock().lock();
|
||||
|
||||
this.dynamicLightSources.clear();
|
||||
this.lightSourceCount = 0;
|
||||
rebuildLightSourceArray();
|
||||
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes light sources if the filter matches.
|
||||
*
|
||||
@@ -559,13 +573,13 @@ public class DynamicLights {
|
||||
*/
|
||||
public static int getLuminanceFromItemStack(@NotNull ItemStack stack) {
|
||||
final Item item = stack.getItem();
|
||||
if (item instanceof ItemBlock itemBlock) {
|
||||
if (item instanceof IDynamicLightProducer lightProducer) {
|
||||
return lightProducer.getLuminance(stack);
|
||||
} else if (item instanceof ItemBlock itemBlock) {
|
||||
final Block block = itemBlock.field_150939_a;
|
||||
if (block != null) {
|
||||
return block.getLightValue();
|
||||
}
|
||||
} else if (item instanceof IDynamicLightProducer lightProducer){
|
||||
return lightProducer.getLuminance();
|
||||
}
|
||||
|
||||
if (item == Items.lava_bucket) return Blocks.lava.getLightValue();
|
||||
@@ -625,6 +639,18 @@ public class DynamicLights {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ModStatus.isCosmeticArmorReworkedLoaded) {
|
||||
final var cosArmor = CosmeticArmorReworked.invMan.getCosArmorInventoryClient(player.getUniqueID());
|
||||
if (cosArmor != null) {
|
||||
for (int i = 0; i < cosArmor.getSizeInventory(); i++) {
|
||||
final var stack = cosArmor.getStackInSlot(i);
|
||||
if (stack != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return luminance;
|
||||
|
||||
@@ -76,7 +76,7 @@ public class HUDCaching {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
|
||||
if (!AngelicaConfig.hudCachingActive || !GLStateManager.isFramebufferEnabled()) {
|
||||
if (!AngelicaConfig.hudCachingActive) {
|
||||
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.samplers.IrisSamplers;
|
||||
import net.coderbot.iris.texture.pbr.PBRTextureManager;
|
||||
import net.coderbot.iris.vertices.ImmediateState;
|
||||
|
||||
public class IrisGLSMBridge {
|
||||
|
||||
@@ -149,20 +148,37 @@ public class IrisGLSMBridge {
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> ProgramUniforms.clearActiveUniforms());
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (event.postBind) return;
|
||||
ProgramUniforms.clearActiveUniforms();
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (event.postBind) return;
|
||||
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
|
||||
if (!drp.shouldOverrideShaders()) return;
|
||||
if (drp.getActivePassProgramId() == -1) return;
|
||||
|
||||
if (!ImmediateState.isRenderingLevel || DepthColorStorage.isOwnedProgram(event.newProgram)) {
|
||||
|
||||
if (DepthColorStorage.isOwnedProgram(event.newProgram)) {
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
} else {
|
||||
drp.onModProgramOverride();
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (!event.postBind) return;
|
||||
WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline instanceof DeferredWorldRenderingPipeline drp) {
|
||||
DeferredWorldRenderingPipeline.Pass activePass = drp.getActivePassProgram();
|
||||
if (activePass != null && activePass.getProgram() != null && activePass.getProgram().getProgramId() == event.newProgram) {
|
||||
activePass.getProgram().getUniforms().update();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,7 @@ public enum Mixins implements IMixins {
|
||||
, "rendering.MixinTileEntity"
|
||||
, "rendering.MixinTileEntityMobSpawner"
|
||||
, "rendering.MixinTileEntityRendererDispatcher"
|
||||
, "rendering.MixinRenderBlocksEmissive"
|
||||
)
|
||||
),
|
||||
|
||||
@@ -287,6 +288,14 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
DRAGONAPI_SHADER_REGISTRY_PARSE_ERROR(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.addRequiredMod(TargetedMod.DRAGON_API)
|
||||
.addClientMixins(
|
||||
"dragonapi.MixinShaderRegistry_ParseError"
|
||||
)
|
||||
),
|
||||
|
||||
IRIS_RENDERING_NOBACKHAND(new MixinBuilder("Iris Hand Shaders")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
@@ -380,7 +389,7 @@ public enum Mixins implements IMixins {
|
||||
.addRequiredMod(TargetedMod.MINEFACTORY_RELOADED)
|
||||
.setApplyIf(() -> CompatConfig.fixMinefactoryReloaded)
|
||||
.addClientMixins("client.minefactoryreloaded.MixinRedNetCableRenderer")),
|
||||
|
||||
|
||||
NTM_SPACE_COMPAT(new MixinBuilder("Multiple fixes for NTM:Space")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.NTM_SPACE)
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
|
||||
import com.gtnewhorizons.angelica.render.CloudRenderer;
|
||||
import com.gtnewhorizons.angelica.render.EmissiveTextureAutoloader;
|
||||
import com.gtnewhorizons.angelica.rendering.AngelicaBlockSafetyRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
|
||||
@@ -102,30 +103,11 @@ public final class ClientProxy extends CommonProxy {
|
||||
super.preInit(event);
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
MinecraftForge.EVENT_BUS.register(new EmissiveTextureAutoloader());
|
||||
ModelRegistry.registerModid(MOD_ID);
|
||||
blockError = new BlockError();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldLoad(WorldEvent.Load event) {
|
||||
if (GLStateManager.isRunningSplash()) {
|
||||
GLStateManager.setRunningSplash(false);
|
||||
LOGGER.info("World loaded - Enabling GLSM Cache");
|
||||
}
|
||||
|
||||
if (AngelicaConfig.enableCeleritas) {
|
||||
ChunkTaskRegistry.reset();
|
||||
ChunkTaskRegistry.registerProvider(DefaultChunkTaskProvider.INSTANCE);
|
||||
ChunkTaskRegistry.registerProvider(ThreadedChunkTaskProvider.INSTANCE);
|
||||
|
||||
// Register all blocks. Because blockids are unique to a world, this must be done each load
|
||||
GameData.getBlockRegistry().typeSafeIterable().forEach(o -> {
|
||||
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, true, true);
|
||||
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, false, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FMLInitializationEvent event) {
|
||||
super.init(event);
|
||||
@@ -170,7 +152,6 @@ public final class ClientProxy extends CommonProxy {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void postInit(FMLPostInitializationEvent event) {
|
||||
super.postInit(event);
|
||||
@@ -190,11 +171,38 @@ public final class ClientProxy extends CommonProxy {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldLoad(WorldEvent.Load event) {
|
||||
if (!event.world.isRemote) return;
|
||||
if (GLStateManager.isRunningSplash()) {
|
||||
GLStateManager.setRunningSplash(false);
|
||||
LOGGER.info("World loaded - Enabling GLSM Cache");
|
||||
}
|
||||
|
||||
if (AngelicaConfig.enableCeleritas) {
|
||||
ChunkTaskRegistry.reset();
|
||||
ChunkTaskRegistry.registerProvider(DefaultChunkTaskProvider.INSTANCE);
|
||||
ChunkTaskRegistry.registerProvider(ThreadedChunkTaskProvider.INSTANCE);
|
||||
|
||||
// Register all blocks. Because blockids are unique to a world, this must be done each load
|
||||
GameData.getBlockRegistry().typeSafeIterable().forEach(o -> {
|
||||
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, true, true);
|
||||
AngelicaBlockSafetyRegistry.canBlockRenderOffThread(o, false, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onWorldUnload(WorldEvent.Unload event) {
|
||||
if (!event.world.isRemote) return;
|
||||
DynamicLights.get().removeAllLightSources();
|
||||
}
|
||||
|
||||
float lastIntegratedTickTime;
|
||||
|
||||
@SubscribeEvent
|
||||
public void onTick(TickEvent.ServerTickEvent event) {
|
||||
if (FMLCommonHandler.instance().getSide().isClient() && event.phase == TickEvent.Phase.END) {
|
||||
if (event.phase == TickEvent.Phase.END) {
|
||||
final IntegratedServer srv = Minecraft.getMinecraft().getIntegratedServer();
|
||||
if (srv != null) {
|
||||
final long currentTickTime = srv.tickTimeArray[srv.getTickCounter() % 100];
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.gtnewhorizons.angelica.render;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class EmissiveTextureAutoloader {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onTextureStitchPre(TextureStitchEvent.Pre event) {
|
||||
if (event.map.getTextureType() != 0) return;
|
||||
|
||||
TextureMap map = event.map;
|
||||
EmissiveTextureHelper.clearCache();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> registeredSprites = map.mapRegisteredSprites;
|
||||
|
||||
List<String> toRegister = new ArrayList<>();
|
||||
|
||||
for (String baseName : registeredSprites.keySet()) {
|
||||
if (baseName.endsWith("_emissive")) continue;
|
||||
|
||||
String emissiveName = baseName + "_emissive";
|
||||
if (resourceExists(emissiveName)) {
|
||||
toRegister.add(baseName);
|
||||
}
|
||||
}
|
||||
|
||||
for (String baseName : toRegister) {
|
||||
String emissiveName = baseName + "_emissive";
|
||||
IIcon emissiveIcon = map.registerIcon(emissiveName);
|
||||
EmissiveTextureHelper.cacheEmissive(baseName, emissiveIcon);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean resourceExists(String iconName) {
|
||||
ResourceLocation loc = new ResourceLocation(iconName);
|
||||
ResourceLocation full = new ResourceLocation(
|
||||
loc.getResourceDomain(),
|
||||
"textures/blocks/" + loc.getResourcePath() + ".png"
|
||||
);
|
||||
try {
|
||||
Minecraft.getMinecraft().getResourceManager().getResource(full);
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.gtnewhorizons.angelica.render;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.util.IIcon;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public final class EmissiveTextureHelper {
|
||||
private static final ConcurrentHashMap<String, IIcon> EMISSIVE_ICON_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
public static IIcon getEmissiveIcon(Block block, int side, int metadata) {
|
||||
IIcon baseIcon = block.getIcon(side, metadata);
|
||||
if (baseIcon == null) return null;
|
||||
|
||||
String baseIconName = baseIcon.getIconName();
|
||||
if (baseIconName == null) return null;
|
||||
|
||||
return EMISSIVE_ICON_CACHE.get(baseIconName);
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
EMISSIVE_ICON_CACHE.clear();
|
||||
}
|
||||
|
||||
public static void cacheEmissive(String baseIconName, IIcon emissiveIcon) {
|
||||
EMISSIVE_ICON_CACHE.put(baseIconName, emissiveIcon);
|
||||
}
|
||||
|
||||
public static void renderEmissiveBlockOverlay(RenderBlocks renderer, Block block, int x, int y, int z, int metadata) {
|
||||
Tessellator tess = Tessellator.instance;
|
||||
|
||||
for (int side = 0; side < 6; side++) {
|
||||
IIcon icon = getEmissiveIcon(block, side, metadata);
|
||||
if (icon == null) continue;
|
||||
|
||||
tess.setColorOpaque_F(1f, 1f, 1f);
|
||||
|
||||
switch (side) {
|
||||
case 0 -> renderer.renderFaceYNeg(block, x, y, z, icon);
|
||||
case 1 -> renderer.renderFaceYPos(block, x, y, z, icon);
|
||||
case 2 -> renderer.renderFaceZNeg(block, x, y, z, icon);
|
||||
case 3 -> renderer.renderFaceZPos(block, x, y, z, icon);
|
||||
case 4 -> renderer.renderFaceXNeg(block, x, y, z, icon);
|
||||
case 5 -> renderer.renderFaceXPos(block, x, y, z, icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,10 @@ import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.BlockRenderContext;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.iris.ContextAwareChunkVertexEncoder;
|
||||
import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
|
||||
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
|
||||
import net.coderbot.iris.vertices.ExtendedDataHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -261,7 +264,13 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
if (isFluid) {
|
||||
contextEncoder.prepareToRenderFluid(blockRenderContext, block, lightValue);
|
||||
} else {
|
||||
contextEncoder.prepareToRenderBlock(blockRenderContext, block, metadata,
|
||||
int effectiveMeta = metadata;
|
||||
if (BlockRenderingSettings.INSTANCE.hasSnowyEntries()
|
||||
&& BlockRenderingSettings.INSTANCE.getSnowyBlocks().contains(block)
|
||||
&& RenderBlocksUtils.isSnowCovered(renderBlocks.blockAccess, x, y, z)) {
|
||||
effectiveMeta |= BlockMaterialMapping.SNOWY_META_BIT;
|
||||
}
|
||||
contextEncoder.prepareToRenderBlock(blockRenderContext, block, effectiveMeta,
|
||||
ExtendedDataHelper.BLOCK_RENDER_TYPE, lightValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,34 @@ final class BlockOrientation extends RenderBlockState {
|
||||
return origIcon == neighbor.getIcon(blockAccess, neighborX, neighborY, neighborZ, getTextureFaceOrig());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockOrientation copy() {
|
||||
BlockOrientation copy = new BlockOrientation();
|
||||
copy.x = x;
|
||||
copy.y = y;
|
||||
copy.z = z;
|
||||
copy.metadata = metadata;
|
||||
copy.altMetadata = altMetadata;
|
||||
copy.metadataBits = metadataBits;
|
||||
copy.renderType = renderType;
|
||||
copy.blockFace = blockFace;
|
||||
copy.textureFace = textureFace;
|
||||
copy.textureFaceOrig = textureFaceOrig;
|
||||
copy.rotateUV = rotateUV;
|
||||
|
||||
copy.blockAccess = blockAccess;
|
||||
copy.block = block;
|
||||
copy.useAO = useAO;
|
||||
copy.inWorld = inWorld;
|
||||
copy.matcher = matcher;
|
||||
copy.offsetsComputed = offsetsComputed;
|
||||
copy.haveOffsets = haveOffsets;
|
||||
copy.dx = dx;
|
||||
copy.dy = dy;
|
||||
copy.dz = dz;
|
||||
return copy;
|
||||
}
|
||||
|
||||
void setBlock(Block block, IBlockAccess blockAccess, int x, int y, int z) {
|
||||
this.block = block;
|
||||
this.blockAccess = blockAccess;
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import com.prupe.mcpatcher.MCLogger;
|
||||
import com.prupe.mcpatcher.MCPatcherUtils;
|
||||
import com.prupe.mcpatcher.mal.block.BlockAPI;
|
||||
@@ -43,13 +44,16 @@ public class CTMUtils {
|
||||
private static Overrides newOverrides = null;
|
||||
private static TileLoader tileLoader;
|
||||
|
||||
private static final ThreadLocal<TileOverrideImpl.CTMCompact> CURRENT_COMPACT = new ThreadLocal<>();
|
||||
@Desugar
|
||||
public record CTMCompactContext(TileOverrideImpl.CTMCompact compact, RenderBlockState renderBlockState) {}
|
||||
|
||||
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact) {
|
||||
CURRENT_COMPACT.set(compact);
|
||||
private static final ThreadLocal<CTMCompactContext> CURRENT_COMPACT = new ThreadLocal<>();
|
||||
|
||||
public static void setCurrentCompact(TileOverrideImpl.CTMCompact compact, RenderBlockState renderBlockState) {
|
||||
CURRENT_COMPACT.set(new CTMCompactContext(compact, renderBlockState.copy()));
|
||||
}
|
||||
|
||||
public static TileOverrideImpl.CTMCompact getCurrentCompact() {
|
||||
public static CTMCompactContext getCurrentCompact() {
|
||||
return CURRENT_COMPACT.get();
|
||||
}
|
||||
|
||||
@@ -180,6 +184,7 @@ public class CTMUtils {
|
||||
local.renderBlockState.setBlock(block, blockAccess, x, y, z);
|
||||
local.renderBlockState.setFace(face);
|
||||
TileOverride lastOverride = local.ijkIterator.go(local.renderBlockState, icon);
|
||||
local.renderBlockState.blockAccess = null;
|
||||
if (lastOverride != null) {
|
||||
return skipDefaultRendering(block) ? RenderBlocksUtils.blankIcon : local.ijkIterator.getIcon();
|
||||
}
|
||||
@@ -233,6 +238,7 @@ public class CTMUtils {
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
clearCurrentCompact();
|
||||
}
|
||||
|
||||
private static boolean checkFace(int face) {
|
||||
|
||||
@@ -1,70 +1,18 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
public final class CompactCtmQuadProcessor {
|
||||
|
||||
private static final int LEFT = 0;
|
||||
private static final int UP_LEFT = 1;
|
||||
private static final int UP = 2;
|
||||
private static final int UP_RIGHT = 3;
|
||||
private static final int RIGHT = 4;
|
||||
private static final int DOWN_RIGHT = 5;
|
||||
private static final int DOWN = 6;
|
||||
private static final int DOWN_LEFT = 7;
|
||||
|
||||
private static final int SPRITE_DEFAULT = 0;
|
||||
private static final int SPRITE_ALL = 1;
|
||||
private static final int SPRITE_HORIZONTAL = 2;
|
||||
private static final int SPRITE_VERTICAL = 3;
|
||||
private static final int SPRITE_MIXED = 4;
|
||||
|
||||
private static final int[] CTM_47_BY_CONNECTIONS = {
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
16,18,16,18,6,46,6,21,16,18,16,18,28,9,28,22,
|
||||
36,17,36,17,24,19,24,43,36,17,36,17,24,19,24,43,
|
||||
37,40,37,40,30,8,30,34,37,40,37,40,25,23,25,45,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
0,3,0,3,12,5,12,15,0,3,0,3,12,5,12,15,
|
||||
1,2,1,2,4,7,4,29,1,2,1,2,13,31,13,14,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
16,42,16,42,6,20,6,10,16,42,16,42,28,35,28,44,
|
||||
36,39,36,39,24,41,24,27,36,39,36,39,24,41,24,27,
|
||||
37,38,37,38,30,11,30,32,37,38,37,38,25,33,25,26
|
||||
};
|
||||
|
||||
/**
|
||||
* Face-local basis:
|
||||
* right = increasing U on the face
|
||||
* down = increasing V on the face
|
||||
*/
|
||||
private static final FaceBasis[] FACE_BASIS = {
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 0: Y-
|
||||
new FaceBasis( 1, 0, 0, 0, 0, 1), // 1: Y+
|
||||
new FaceBasis(-1, 0, 0, 0, -1, 0), // 2: Z-
|
||||
new FaceBasis( 1, 0, 0, 0, -1, 0), // 3: Z+
|
||||
new FaceBasis( 0, 0, 1, 0, -1, 0), // 4: X-
|
||||
new FaceBasis( 0, 0, -1, 0, -1, 0) // 5: X+
|
||||
};
|
||||
|
||||
/**
|
||||
* Neighbor offsets in face-local coordinates:
|
||||
* 0 = left, 1 = up-left, 2 = up, 3 = up-right,
|
||||
* 4 = right, 5 = down-right, 6 = down, 7 = down-left
|
||||
*/
|
||||
private static final int[] RIGHT_FACTOR = {-1, -1, 0, 1, 1, 1, 0, -1};
|
||||
private static final int[] DOWN_FACTOR = { 0, -1, -1, -1, 0, 1, 1, 1};
|
||||
|
||||
/**
|
||||
* Quadrant side references:
|
||||
* 0 = top-left
|
||||
@@ -72,41 +20,58 @@ public final class CompactCtmQuadProcessor {
|
||||
* 2 = bottom-right
|
||||
* 3 = bottom-left
|
||||
*/
|
||||
private static final int[] QUAD_A = {LEFT, UP, RIGHT, DOWN};
|
||||
private static final int[] QUAD_B = {UP, RIGHT, DOWN, LEFT};
|
||||
private static final int[] QUAD_DIAG = {UP_LEFT, UP_RIGHT, DOWN_RIGHT, DOWN_LEFT};
|
||||
private static final int[] QUAD_A = {RenderBlockState.REL_L, RenderBlockState.REL_U,
|
||||
RenderBlockState.REL_R, RenderBlockState.REL_D};
|
||||
private static final int[] QUAD_B = {RenderBlockState.REL_U, RenderBlockState.REL_R,
|
||||
RenderBlockState.REL_D, RenderBlockState.REL_L};
|
||||
private static final int[] QUAD_DIAG = {RenderBlockState.REL_UL, RenderBlockState.REL_UR,
|
||||
RenderBlockState.REL_DR, RenderBlockState.REL_DL};
|
||||
|
||||
/** Maps split-face quadrant (0=TL,1=TR,2=BR,3=BL face-local) to RenderBlocks per-face TL/TR/BR/BL orientation. */
|
||||
private static final int[][] RENDER_FACE_QUADRANT_MAP = {
|
||||
{3, 2, 1, 0}, // Y-
|
||||
{2, 3, 0, 1}, // Y+
|
||||
{3, 0, 1, 2}, // Z-
|
||||
{0, 1, 2, 3}, // Z+
|
||||
{3, 0, 1, 2}, // X-
|
||||
{1, 2, 3, 0} // X+
|
||||
};
|
||||
private static final float[] QUADRANT_MIN_U = {0.0f, 0.5f, 0.5f, 0.0f};
|
||||
private static final float[] QUADRANT_MIN_V = {0.0f, 0.0f, 0.5f, 0.5f};
|
||||
|
||||
private final IIcon[] sprites;
|
||||
private final CompactConnectingCtmProperties props;
|
||||
private final TileOverrideImpl.CTMCompact override;
|
||||
|
||||
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props) {
|
||||
CompactCtmQuadProcessor(IIcon[] sprites, CompactConnectingCtmProperties props, TileOverrideImpl.CTMCompact compact) {
|
||||
this.sprites = new IIcon[5];
|
||||
if (sprites != null) {
|
||||
System.arraycopy(sprites, 0, this.sprites, 0, Math.min(sprites.length, this.sprites.length));
|
||||
}
|
||||
this.props = props;
|
||||
this.override = compact;
|
||||
}
|
||||
|
||||
public boolean processFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon origIcon) {
|
||||
int connections = CtmConnectionHelper.getConnections(world, x, y, z, face, props.getInnerSeams());
|
||||
public boolean processFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
int neighborBits = override.getNeighborBits(renderBlockState, origIcon);
|
||||
|
||||
int ctmIndex = CTM_47_BY_CONNECTIONS[connections & 0xFF];
|
||||
int ctmIndex = TileOverride.neighborMap[neighborBits & 0xFF];
|
||||
IIcon replacement = getReplacementIcon(ctmIndex);
|
||||
if (replacement != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, replacement);
|
||||
renderWholeFace(rb, renderBlockState, replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connections == 0 || connections == 0xFF) {
|
||||
IIcon sprite = sprites[getCompactSpriteIndex(connections)];
|
||||
if (neighborBits == 0 || neighborBits == 0xFF) {
|
||||
IIcon sprite = sprites[getCompactSpriteIndex(neighborBits)];
|
||||
if (sprite != null) {
|
||||
renderWholeFace(rb, world, x, y, z, face, sprite);
|
||||
renderWholeFace(rb, renderBlockState, sprite);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
renderSplitFace(rb, world, x, y, z, face, connections);
|
||||
renderSplitFace(rb, renderBlockState, neighborBits);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,54 +101,137 @@ public final class CompactCtmQuadProcessor {
|
||||
return SPRITE_ALL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << LEFT) | (1 << RIGHT))) == ((1 << LEFT) | (1 << RIGHT))) {
|
||||
if ((connections & ((1 << RenderBlockState.REL_L) | (1 << RenderBlockState.REL_R))) ==
|
||||
((1 << RenderBlockState.REL_L) | (1 << RenderBlockState.REL_R))) {
|
||||
return SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
if ((connections & ((1 << UP) | (1 << DOWN))) == ((1 << UP) | (1 << DOWN))) {
|
||||
if ((connections & ((1 << RenderBlockState.REL_U) | (1 << RenderBlockState.REL_D))) ==
|
||||
((1 << RenderBlockState.REL_U) | (1 << RenderBlockState.REL_D))) {
|
||||
return SPRITE_VERTICAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
private void renderSplitFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, int connections) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
private void renderSplitFace(RenderBlocks rb, RenderBlockState renderBlockState, int connections) {
|
||||
final int face = renderBlockState.getBlockFace();
|
||||
|
||||
double minX = rb.renderMinX;
|
||||
double minY = rb.renderMinY;
|
||||
double minZ = rb.renderMinZ;
|
||||
double maxX = rb.renderMaxX;
|
||||
double maxY = rb.renderMaxY;
|
||||
double maxZ = rb.renderMaxZ;
|
||||
final double minX = rb.renderMinX, minY = rb.renderMinY, minZ = rb.renderMinZ;
|
||||
final double maxX = rb.renderMaxX, maxY = rb.renderMaxY, maxZ = rb.renderMaxZ;
|
||||
|
||||
// Without AO the corner color/brightness fields are unused; skip the lighting dance.
|
||||
final boolean ao = rb.enableAO;
|
||||
|
||||
final float rTL, rTR, rBL, rBR, gTL, gTR, gBL, gBR, bTL, bTR, bBL, bBR;
|
||||
final int brTL, brTR, brBL, brBR;
|
||||
if (ao) {
|
||||
rTL = rb.colorRedTopLeft; rTR = rb.colorRedTopRight;
|
||||
rBL = rb.colorRedBottomLeft; rBR = rb.colorRedBottomRight;
|
||||
gTL = rb.colorGreenTopLeft; gTR = rb.colorGreenTopRight;
|
||||
gBL = rb.colorGreenBottomLeft; gBR = rb.colorGreenBottomRight;
|
||||
bTL = rb.colorBlueTopLeft; bTR = rb.colorBlueTopRight;
|
||||
bBL = rb.colorBlueBottomLeft; bBR = rb.colorBlueBottomRight;
|
||||
brTL = rb.brightnessTopLeft; brTR = rb.brightnessTopRight;
|
||||
brBL = rb.brightnessBottomLeft; brBR = rb.brightnessBottomRight;
|
||||
} else {
|
||||
rTL = rTR = rBL = rBR = 0f;
|
||||
gTL = gTR = gBL = gBR = 0f;
|
||||
bTL = bTR = bBL = bBR = 0f;
|
||||
brTL = brTR = brBL = brBR = 0;
|
||||
}
|
||||
|
||||
for (int q = 0; q < 4; q++) {
|
||||
IIcon icon = sprites[getQuadrantSprite(q, connections)];
|
||||
final IIcon icon = sprites[getQuadrantSprite(q, connections)];
|
||||
if (icon == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ao) {
|
||||
applyQuadrantLighting(rb, face, q, rTL, rTR, rBL, rBR, gTL, gTR, gBL, gBR, bTL, bTR, bBL, bBR, brTL, brTR, brBL, brBR);
|
||||
}
|
||||
setQuadrantBounds(rb, face, q, minX, minY, minZ, maxX, maxY, maxZ);
|
||||
rb.overrideBlockTexture = icon;
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
renderFace(rb, renderBlockState, icon);
|
||||
}
|
||||
|
||||
if (ao) {
|
||||
rb.colorRedTopLeft = rTL; rb.colorRedTopRight = rTR;
|
||||
rb.colorRedBottomLeft = rBL; rb.colorRedBottomRight = rBR;
|
||||
rb.colorGreenTopLeft = gTL; rb.colorGreenTopRight = gTR;
|
||||
rb.colorGreenBottomLeft = gBL; rb.colorGreenBottomRight = gBR;
|
||||
rb.colorBlueTopLeft = bTL; rb.colorBlueTopRight = bTR;
|
||||
rb.colorBlueBottomLeft = bBL; rb.colorBlueBottomRight = bBR;
|
||||
rb.brightnessTopLeft = brTL; rb.brightnessTopRight = brTR;
|
||||
rb.brightnessBottomLeft = brBL; rb.brightnessBottomRight = brBR;
|
||||
}
|
||||
rb.overrideBlockTexture = null;
|
||||
rb.setRenderBounds(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
private void renderWholeFace(RenderBlocks rb, IBlockAccess world, int x, int y, int z, int face, IIcon icon) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
private static void applyQuadrantLighting(RenderBlocks rb, int face, int quadrant, float rTL, float rTR, float rBL, float rBR, float gTL, float gTR, float gBL, float gBR, float bTL, float bTR, float bBL, float bBR, int brTL, int brTR, int brBL, int brBR) {
|
||||
final int rq = RENDER_FACE_QUADRANT_MAP[face][quadrant];
|
||||
final float u0 = QUADRANT_MIN_U[rq], u1 = u0 + 0.5f;
|
||||
final float v0 = QUADRANT_MIN_V[rq], v1 = v0 + 0.5f;
|
||||
|
||||
rb.colorRedTopLeft = bilerp(rTL, rTR, rBL, rBR, u0, v0);
|
||||
rb.colorRedTopRight = bilerp(rTL, rTR, rBL, rBR, u1, v0);
|
||||
rb.colorRedBottomLeft = bilerp(rTL, rTR, rBL, rBR, u0, v1);
|
||||
rb.colorRedBottomRight = bilerp(rTL, rTR, rBL, rBR, u1, v1);
|
||||
|
||||
rb.colorGreenTopLeft = bilerp(gTL, gTR, gBL, gBR, u0, v0);
|
||||
rb.colorGreenTopRight = bilerp(gTL, gTR, gBL, gBR, u1, v0);
|
||||
rb.colorGreenBottomLeft = bilerp(gTL, gTR, gBL, gBR, u0, v1);
|
||||
rb.colorGreenBottomRight = bilerp(gTL, gTR, gBL, gBR, u1, v1);
|
||||
|
||||
rb.colorBlueTopLeft = bilerp(bTL, bTR, bBL, bBR, u0, v0);
|
||||
rb.colorBlueTopRight = bilerp(bTL, bTR, bBL, bBR, u1, v0);
|
||||
rb.colorBlueBottomLeft = bilerp(bTL, bTR, bBL, bBR, u0, v1);
|
||||
rb.colorBlueBottomRight = bilerp(bTL, bTR, bBL, bBR, u1, v1);
|
||||
|
||||
rb.brightnessTopLeft = bilerpLight(brTL, brTR, brBL, brBR, u0, v0);
|
||||
rb.brightnessTopRight = bilerpLight(brTL, brTR, brBL, brBR, u1, v0);
|
||||
rb.brightnessBottomLeft = bilerpLight(brTL, brTR, brBL, brBR, u0, v1);
|
||||
rb.brightnessBottomRight = bilerpLight(brTL, brTR, brBL, brBR, u1, v1);
|
||||
}
|
||||
|
||||
private static float bilerp(float c00, float c10, float c01, float c11, float u, float v) {
|
||||
final float top = c00 + (c10 - c00) * u;
|
||||
final float bot = c01 + (c11 - c01) * u;
|
||||
return top + (bot - top) * v;
|
||||
}
|
||||
|
||||
/** Brightness packs sky in upper 16 bits, block in lower; bilerp each independently then repack. */
|
||||
private static int bilerpLight(int c00, int c10, int c01, int c11, float u, float v) {
|
||||
final int b00 = c00 & 0xFFFF, b10 = c10 & 0xFFFF, b01 = c01 & 0xFFFF, b11 = c11 & 0xFFFF;
|
||||
final int s00 = c00 >>> 16, s10 = c10 >>> 16, s01 = c01 >>> 16, s11 = c11 >>> 16;
|
||||
|
||||
final float bTop = b00 + (b10 - b00) * u;
|
||||
final float bBot = b01 + (b11 - b01) * u;
|
||||
final float sTop = s00 + (s10 - s00) * u;
|
||||
final float sBot = s01 + (s11 - s01) * u;
|
||||
|
||||
final int block = Math.round(bTop + (bBot - bTop) * v);
|
||||
final int sky = Math.round(sTop + (sBot - sTop) * v);
|
||||
return (sky << 16) | (block & 0xFFFF);
|
||||
}
|
||||
|
||||
private void renderWholeFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon) {
|
||||
IIcon saved = rb.overrideBlockTexture;
|
||||
rb.overrideBlockTexture = icon;
|
||||
try {
|
||||
renderFace(rb, block, x, y, z, face, icon);
|
||||
renderFace(rb, renderBlockState, icon);
|
||||
} finally {
|
||||
rb.overrideBlockTexture = saved;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderFace(RenderBlocks rb, Block block, int x, int y, int z, int face, IIcon icon) {
|
||||
private void renderFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon) {
|
||||
int face = renderBlockState.getBlockFace();
|
||||
Block block = renderBlockState.getBlock();
|
||||
int x = renderBlockState.getX();
|
||||
int y = renderBlockState.getY();
|
||||
int z = renderBlockState.getZ();
|
||||
boolean originalFlip = rb.field_152631_f;
|
||||
rb.field_152631_f = (face == 2 || face == 5);
|
||||
try {
|
||||
@@ -271,52 +319,10 @@ public final class CompactCtmQuadProcessor {
|
||||
|
||||
if (connA ^ connB) {
|
||||
int connectedBit = connA ? sideA : sideB;
|
||||
return (connectedBit == LEFT || connectedBit == RIGHT) ? SPRITE_VERTICAL : SPRITE_HORIZONTAL;
|
||||
return (connectedBit == RenderBlockState.REL_L || connectedBit == RenderBlockState.REL_R) ? SPRITE_VERTICAL
|
||||
: SPRITE_HORIZONTAL;
|
||||
}
|
||||
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
@Desugar
|
||||
private record FaceBasis(int rightX, int rightY, int rightZ, int downX, int downY, int downZ) { }
|
||||
|
||||
private static final class CtmConnectionHelper {
|
||||
|
||||
public static int getConnections(IBlockAccess world, int x, int y, int z, int face, boolean innerSeams) {
|
||||
Block self = world.getBlock(x, y, z);
|
||||
FaceBasis basis = FACE_BASIS[face];
|
||||
|
||||
int bits = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int dx = RIGHT_FACTOR[i] * basis.rightX + DOWN_FACTOR[i] * basis.downX;
|
||||
int dy = RIGHT_FACTOR[i] * basis.rightY + DOWN_FACTOR[i] * basis.downY;
|
||||
int dz = RIGHT_FACTOR[i] * basis.rightZ + DOWN_FACTOR[i] * basis.downZ;
|
||||
|
||||
int nx = x + dx;
|
||||
int ny = y + dy;
|
||||
int nz = z + dz;
|
||||
|
||||
if (world.getBlock(nx, ny, nz) != self) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (innerSeams && !shouldRenderInnerSeam(world, nx, ny, nz, face)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bits |= (1 << i);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static boolean shouldRenderInnerSeam(IBlockAccess world, int x, int y, int z, int face) {
|
||||
int[] normal = RenderBlockState.NORMALS[face];
|
||||
int sx = x + normal[0];
|
||||
int sy = y + normal[1];
|
||||
int sz = z + normal[2];
|
||||
return world.getBlock(sx, sy, sz).shouldSideBeRendered(world, sx, sy, sz, face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,4 +139,6 @@ abstract public class RenderBlockState {
|
||||
|
||||
abstract public boolean shouldConnectByTile(Block neighbor, IIcon origIcon, int neighborX, int neighborY,
|
||||
int neighborZ);
|
||||
|
||||
abstract public RenderBlockState copy();
|
||||
}
|
||||
@@ -48,6 +48,28 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
private final List<ResourceLocation> tileNames = new ArrayList<>();
|
||||
protected IIcon[] icons;
|
||||
|
||||
// Index into this array is formed from these bit values:
|
||||
// 128 64 32
|
||||
// 1 * 16
|
||||
// 2 4 8
|
||||
protected static final int[] neighborMap = new int[] {
|
||||
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
|
||||
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
|
||||
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
|
||||
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
|
||||
36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24, 43,
|
||||
16, 18, 16, 18, 6, 46, 6, 21, 16, 18, 16, 18, 28, 9, 28, 22,
|
||||
36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24, 43,
|
||||
37, 40, 37, 40, 30, 8, 30, 34, 37, 40, 37, 40, 25, 23, 25, 45,
|
||||
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
|
||||
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
|
||||
0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15,
|
||||
1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14,
|
||||
36, 39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27,
|
||||
16, 42, 16, 42, 6, 20, 6, 10, 16, 42, 16, 42, 28, 35, 28, 44,
|
||||
36, 39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27,
|
||||
37, 38, 37, 38, 30, 11, 30, 32, 37, 38, 37, 38, 25, 33, 25, 26, };
|
||||
|
||||
static TileOverride create(ResourceLocation propertiesFile, TileLoader tileLoader) {
|
||||
if (propertiesFile == null) {
|
||||
return null;
|
||||
@@ -353,6 +375,16 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
return shouldConnect(renderBlockState, icon, renderBlockState.getOffset(blockFace, relativeDirection));
|
||||
}
|
||||
|
||||
protected final int getNeighborBits(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
int neighborBits = 0;
|
||||
for (int bit = 0; bit < 8; bit++) {
|
||||
if (shouldConnect(renderBlockState, origIcon, bit)) {
|
||||
neighborBits |= (1 << bit);
|
||||
}
|
||||
}
|
||||
return neighborBits;
|
||||
}
|
||||
|
||||
private boolean shouldConnect(RenderBlockState renderBlockState, IIcon icon, int[] offset) {
|
||||
IBlockAccess blockAccess = renderBlockState.getBlockAccess();
|
||||
Block block = renderBlockState.getBlock();
|
||||
|
||||
@@ -22,21 +22,6 @@ public class TileOverrideImpl {
|
||||
|
||||
final static class CTM extends TileOverride {
|
||||
|
||||
// Index into this array is formed from these bit values:
|
||||
// 128 64 32
|
||||
// 1 * 16
|
||||
// 2 4 8
|
||||
private static final int[] neighborMap = new int[] { 0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2,
|
||||
1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 0, 3, 0, 3, 12, 5, 12, 15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2,
|
||||
1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36, 17, 24, 19, 24,
|
||||
43, 16, 18, 16, 18, 6, 46, 6, 21, 16, 18, 16, 18, 28, 9, 28, 22, 36, 17, 36, 17, 24, 19, 24, 43, 36, 17, 36,
|
||||
17, 24, 19, 24, 43, 37, 40, 37, 40, 30, 8, 30, 34, 37, 40, 37, 40, 25, 23, 25, 45, 0, 3, 0, 3, 12, 5, 12,
|
||||
15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 0, 3, 0, 3, 12, 5, 12,
|
||||
15, 0, 3, 0, 3, 12, 5, 12, 15, 1, 2, 1, 2, 4, 7, 4, 29, 1, 2, 1, 2, 13, 31, 13, 14, 36, 39, 36, 39, 24, 41,
|
||||
24, 27, 36, 39, 36, 39, 24, 41, 24, 27, 16, 42, 16, 42, 6, 20, 6, 10, 16, 42, 16, 42, 28, 35, 28, 44, 36,
|
||||
39, 36, 39, 24, 41, 24, 27, 36, 39, 36, 39, 24, 41, 24, 27, 37, 38, 37, 38, 30, 11, 30, 32, 37, 38, 37, 38,
|
||||
25, 33, 25, 26, };
|
||||
|
||||
CTM(PropertiesFile properties, TileLoader tileLoader) {
|
||||
super(properties, tileLoader);
|
||||
}
|
||||
@@ -62,13 +47,7 @@ public class TileOverrideImpl {
|
||||
|
||||
@Override
|
||||
IIcon getTileWorld_Impl(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
int neighborBits = 0;
|
||||
for (int bit = 0; bit < 8; bit++) {
|
||||
if (shouldConnect(renderBlockState, origIcon, bit)) {
|
||||
neighborBits |= (1 << bit);
|
||||
}
|
||||
}
|
||||
return icons[neighborMap[neighborBits]];
|
||||
return icons[neighborMap[getNeighborBits(renderBlockState, origIcon)]];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -520,7 +499,7 @@ public class TileOverrideImpl {
|
||||
public CompactCtmQuadProcessor getProcessor() {
|
||||
CompactCtmQuadProcessor p = this.processor;
|
||||
if (p == null) {
|
||||
p = new CompactCtmQuadProcessor(icons, properties);
|
||||
p = new CompactCtmQuadProcessor(icons, properties, this);
|
||||
this.processor = p;
|
||||
}
|
||||
return p;
|
||||
|
||||
@@ -105,7 +105,6 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
|
||||
public TileOverride go(RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
|
||||
this.renderBlockState = renderBlockState;
|
||||
renderBlockState.setFilter(null);
|
||||
currentIcon = origIcon;
|
||||
@@ -124,7 +123,7 @@ abstract public class TileOverrideIterator implements Iterator<TileOverride> {
|
||||
IIcon newIcon = getTile(override, renderBlockState, origIcon);
|
||||
if (newIcon != null) {
|
||||
if (override instanceof TileOverrideImpl.CTMCompact compact) {
|
||||
CTMUtils.setCurrentCompact(compact);
|
||||
CTMUtils.setCurrentCompact(compact, renderBlockState);
|
||||
}
|
||||
lastMatchedOverride = override;
|
||||
skipOverrides.add(override);
|
||||
|
||||
@@ -157,7 +157,7 @@ public class RenderBlocksUtils {
|
||||
return isSnow ? Blocks.snow_layer.getIcon(blockAccess, x, y, z, face) : topIcon;
|
||||
}
|
||||
|
||||
private static boolean isSnowCovered(IBlockAccess blockAccess, int x, int y, int z) {
|
||||
public static boolean isSnowCovered(IBlockAccess blockAccess, int x, int y, int z) {
|
||||
Block topBlock = blockAccess.getBlock(x, y + 1, z);
|
||||
return topBlock == Blocks.snow_layer || topBlock == Blocks.snow;
|
||||
}
|
||||
|
||||
@@ -534,4 +534,61 @@ public class SodiumGameOptionPages {
|
||||
return new OptionPage(I18n.format("sodium.options.pages.appearance"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
public static OptionPage text() {
|
||||
final List<OptionGroup> groups = new ArrayList<>();
|
||||
|
||||
groups.add(OptionGroup.createBuilder()
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName("RGB Colors")
|
||||
.setTooltip("Enable hex colors via &#RRGGBB syntax")
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableRGBColors = value,
|
||||
opts -> AngelicaConfig.enableRGBColors)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName("Gradients")
|
||||
.setTooltip("Smooth color transitions via &g&#start&#end")
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableGradients = value,
|
||||
opts -> AngelicaConfig.enableGradients)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName("Rainbow")
|
||||
.setTooltip("Cycling rainbow colors via &q")
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableRainbow = value,
|
||||
opts -> AngelicaConfig.enableRainbow)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName("Wave Text")
|
||||
.setTooltip("Animated bouncing text via &z")
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableWaveText = value,
|
||||
opts -> AngelicaConfig.enableWaveText)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(int.class, angelicaOpts)
|
||||
.setName("Wave Amplitude")
|
||||
.setTooltip("How far wave text bounces (default 2.0)")
|
||||
.setControl(option -> new SliderControl(option, 10, 80, 5, v -> (v / 10) + "." + (v % 10)))
|
||||
.setBinding((opts, value) -> AngelicaConfig.waveAmplitude = value / 10f, opts -> Math.round(AngelicaConfig.waveAmplitude * 10f))
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName("Dinnerbone Text")
|
||||
.setTooltip("Flipped upside down text via &v")
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableDinnerboneText = value,
|
||||
opts -> AngelicaConfig.enableDinnerboneText)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName("Ampersand Conversion")
|
||||
.setTooltip("Convert & prefix codes to formatting codes at render time")
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableAmpersandConversion = value,
|
||||
opts -> AngelicaConfig.enableAmpersandConversion)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
return new OptionPage("Text Effects", ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -63,6 +63,7 @@ public class SodiumOptionsGUI extends ScrollableGuiScreen {
|
||||
this.pages.add(SodiumGameOptionPages.advanced());
|
||||
this.pages.add(SodiumGameOptionPages.performance());
|
||||
this.pages.add(SodiumGameOptionPages.appearance());
|
||||
this.pages.add(SodiumGameOptionPages.text());
|
||||
|
||||
// Add Dynamic Lights page if enabled
|
||||
if (DynamicLights.configEnabled) {
|
||||
|
||||
@@ -7,7 +7,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import net.coderbot.iris.Iris;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
||||
import net.coderbot.iris.shaderpack.materialmap.BlockEntry;
|
||||
import net.coderbot.iris.shaderpack.materialmap.BlockRenderType;
|
||||
import net.coderbot.iris.shaderpack.materialmap.FlatteningMap;
|
||||
@@ -21,45 +22,46 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class BlockMaterialMapping {
|
||||
|
||||
/** Meta-key bit OR'd in at runtime when a snowy-tagged block has snow above it. */
|
||||
public static final int SNOWY_META_BIT = 0x10;
|
||||
|
||||
/**
|
||||
* Creates a two-level map structure for block material IDs.
|
||||
* Based on Iris's BlockState mapping approach adapted for 1.7.10's metadata system.
|
||||
*/
|
||||
public static Reference2ObjectMap<Block, Int2IntMap> createBlockMetaIdMap(Int2ObjectMap<List<BlockEntry>> blockPropertiesMap) {
|
||||
Reference2ObjectMap<Block, Int2IntMap> blockMatches = new Reference2ObjectOpenHashMap<>();
|
||||
public static Reference2ObjectMap<Block, Int2IntMap> createBlockMetaIdMap(Int2ObjectMap<List<BlockEntry>> blockPropertiesMap, boolean skipFlattening) {
|
||||
final Reference2ObjectMap<Block, Int2IntMap> blockMatches = new Reference2ObjectOpenHashMap<>();
|
||||
final ReferenceSet<Block> snowyBlocks = new ReferenceOpenHashSet<>();
|
||||
|
||||
blockPropertiesMap.forEach((intId, entries) -> {
|
||||
for (BlockEntry entry : entries) {
|
||||
addBlockMetas(entry, blockMatches, intId);
|
||||
addBlockMetas(entry, blockMatches, intId, skipFlattening, snowyBlocks);
|
||||
}
|
||||
});
|
||||
|
||||
BlockRenderingSettings.INSTANCE.setHasSnowyEntries(!snowyBlocks.isEmpty());
|
||||
BlockRenderingSettings.INSTANCE.setSnowyBlocks(snowyBlocks);
|
||||
return blockMatches;
|
||||
}
|
||||
|
||||
public static Map<Block, BlockRenderLayer> createBlockTypeMap(Map<NamespacedId, BlockRenderType> blockPropertiesMap) {
|
||||
Map<Block, BlockRenderLayer> blockTypeIds = new Reference2ReferenceOpenHashMap<>();
|
||||
final Map<Block, BlockRenderLayer> blockTypeIds = new Reference2ReferenceOpenHashMap<>();
|
||||
|
||||
blockPropertiesMap.forEach((id, blockType) -> {
|
||||
Block block = resolveBlock(id);
|
||||
Block block = resolveBlockOrNull(id);
|
||||
|
||||
// Try flattening map for modern names
|
||||
if ((block == null || block == Blocks.air) && "minecraft".equals(id.getNamespace())) {
|
||||
List<BlockEntry> legacyEntries = FlatteningMap.toLegacy(id.getName(), Map.of());
|
||||
// Modern names like "grass_block" don't exist in 1.7.10's registry; fall back to flattening.
|
||||
if (block == null && "minecraft".equals(id.getNamespace())) {
|
||||
final List<BlockEntry> legacyEntries = FlatteningMap.toLegacy(id.getName(), Map.of());
|
||||
if (legacyEntries != null) {
|
||||
// Use the first entry for render type (render type applies per-block)
|
||||
block = resolveBlock(legacyEntries.getFirst().getId());
|
||||
block = resolveBlockOrNull(legacyEntries.getFirst().getId());
|
||||
}
|
||||
}
|
||||
|
||||
if (block == null || block == Blocks.air) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (block == null) return;
|
||||
final BlockRenderLayer layer = convertBlockToRenderLayer(blockType);
|
||||
if (layer != null) {
|
||||
blockTypeIds.put(block, layer);
|
||||
}
|
||||
if (layer != null) blockTypeIds.put(block, layer);
|
||||
});
|
||||
|
||||
return blockTypeIds;
|
||||
@@ -82,33 +84,29 @@ public class BlockMaterialMapping {
|
||||
* Adds block+metadata combinations to the material ID map.
|
||||
* Based on Iris's addBlockStates method, adapted for 1.7.10 metadata system.
|
||||
*/
|
||||
private static void addBlockMetas(BlockEntry entry, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId) {
|
||||
NamespacedId id = entry.getId();
|
||||
String name = id.getName();
|
||||
boolean hasStateProps = !entry.getStateProperties().isEmpty();
|
||||
boolean hasExplicitMetas = !entry.getMetas().isEmpty();
|
||||
private static void addBlockMetas(BlockEntry entry, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId, boolean skipFlattening, ReferenceSet<Block> snowyBlocks) {
|
||||
final NamespacedId id = entry.getId();
|
||||
final Map<String, String> stateProps = entry.getStateProperties();
|
||||
final String snowy = stateProps.get("snowy");
|
||||
final int snowyBit = "true".equals(snowy) ? SNOWY_META_BIT : 0;
|
||||
|
||||
if ("minecraft".equals(id.getNamespace()) && (hasStateProps || !hasExplicitMetas)) {
|
||||
List<BlockEntry> legacyEntries = FlatteningMap.toLegacy(name, entry.getStateProperties());
|
||||
if (legacyEntries != null) {
|
||||
for (BlockEntry legacy : legacyEntries) {
|
||||
applyMetas(resolveBlock(legacy.getId()), legacy.getMetas(), idMap, intId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Vanilla modern names go through FlatteningMap; legacy-section packs and modded blocks
|
||||
// resolve directly from the registry.
|
||||
List<BlockEntry> targets = null;
|
||||
if (!skipFlattening && "minecraft".equals(id.getNamespace()) && (!stateProps.isEmpty() || entry.getMetas().isEmpty())) {
|
||||
targets = FlatteningMap.toLegacy(id.getName(), stateProps);
|
||||
}
|
||||
if (targets == null) targets = List.of(entry);
|
||||
|
||||
// Fall back to registry with the entry's own metas
|
||||
Block block = resolveBlock(id);
|
||||
applyMetas(block, entry.getMetas(), idMap, intId);
|
||||
for (BlockEntry target : targets) {
|
||||
final Block block = resolveBlockOrNull(target.getId());
|
||||
if (block == null) continue;
|
||||
applyMetas(block, target.getMetas(), idMap, intId, snowyBit);
|
||||
if (snowy != null) snowyBlocks.add(block);
|
||||
}
|
||||
}
|
||||
// If the block doesn't exist, by default the registry will return AIR. That probably isn't what we want.
|
||||
// TODO: Assuming that Registry.BLOCK.getDefaultId() == "minecraft:air" here
|
||||
private static void applyMetas(Block block, Set<Integer> metas, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId) {
|
||||
if (block == null || block == Blocks.air) {
|
||||
return;
|
||||
}
|
||||
|
||||
private static void applyMetas(Block block, Set<Integer> metas, Reference2ObjectMap<Block, Int2IntMap> idMap, int intId, int snowyBit) {
|
||||
Int2IntMap metaMap = idMap.get(block);
|
||||
if (metaMap == null) {
|
||||
metaMap = new Int2IntOpenHashMap();
|
||||
@@ -117,18 +115,18 @@ public class BlockMaterialMapping {
|
||||
}
|
||||
|
||||
if (metas.isEmpty()) {
|
||||
for (int meta = 0; meta < 16; meta++) {
|
||||
metaMap.putIfAbsent(meta, intId);
|
||||
}
|
||||
for (int meta = 0; meta < 16; meta++) metaMap.putIfAbsent(meta | snowyBit, intId);
|
||||
} else {
|
||||
for (int meta : metas) {
|
||||
metaMap.putIfAbsent(meta, intId);
|
||||
}
|
||||
for (int meta : metas) metaMap.putIfAbsent(meta | snowyBit, intId);
|
||||
}
|
||||
}
|
||||
|
||||
private static Block resolveBlock(NamespacedId id) {
|
||||
final ResourceLocation resourceLocation = new ResourceLocation(id.getNamespace(), id.getName());
|
||||
return (Block) Block.blockRegistry.getObject(resourceLocation.toString());
|
||||
/**
|
||||
* Returns the registered Block for an id, or null if unknown.
|
||||
* The registry returns Blocks.air (its default) for missing keys, which we coerce to null.
|
||||
*/
|
||||
private static Block resolveBlockOrNull(NamespacedId id) {
|
||||
final Block block = (Block) Block.blockRegistry.getObject(new ResourceLocation(id.getNamespace(), id.getName()).toString());
|
||||
return block == Blocks.air ? null : block;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,10 @@ import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntFunction;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSets;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -19,12 +22,21 @@ public class BlockRenderingSettings {
|
||||
private boolean reloadRequired;
|
||||
private Reference2ObjectMap<Block, Int2IntMap> blockMetaMatches;
|
||||
private Map<Block, BlockRenderLayer> blockTypeIds;
|
||||
private Object2IntFunction<NamespacedId> entityIds;
|
||||
private Object2IntFunction<NamespacedId> itemIds;
|
||||
private float ambientOcclusionLevel;
|
||||
// note: no reload needed, entities are rebuilt every frame.
|
||||
@Setter
|
||||
private Object2IntFunction<NamespacedId> entityIds;
|
||||
// note: no reload needed, items are rendered every frame.
|
||||
@Setter
|
||||
private Object2IntFunction<NamespacedId> itemIds;
|
||||
@Getter
|
||||
private float ambientOcclusionLevel;
|
||||
private boolean disableDirectionalShading;
|
||||
private boolean useSeparateAo;
|
||||
private boolean useExtendedVertexFormat;
|
||||
@Setter
|
||||
private boolean hasSnowyEntries;
|
||||
@Getter
|
||||
private ReferenceSet<Block> snowyBlocks = ReferenceSets.emptySet();
|
||||
|
||||
public BlockRenderingSettings() {
|
||||
reloadRequired = false;
|
||||
@@ -34,6 +46,7 @@ public class BlockRenderingSettings {
|
||||
disableDirectionalShading = false;
|
||||
useSeparateAo = false;
|
||||
useExtendedVertexFormat = false;
|
||||
hasSnowyEntries = false;
|
||||
}
|
||||
|
||||
public void clearReloadRequired() {
|
||||
@@ -74,6 +87,14 @@ public class BlockRenderingSettings {
|
||||
this.blockMetaMatches = blockMetaIds;
|
||||
}
|
||||
|
||||
public boolean hasSnowyEntries() {
|
||||
return hasSnowyEntries;
|
||||
}
|
||||
|
||||
public void setSnowyBlocks(ReferenceSet<Block> snowyBlocks) {
|
||||
this.snowyBlocks = snowyBlocks != null ? snowyBlocks : ReferenceSets.emptySet();
|
||||
}
|
||||
|
||||
public void setBlockTypeIds(Map<Block, BlockRenderLayer> blockTypeIds) {
|
||||
if (this.blockTypeIds != null && this.blockTypeIds.equals(blockTypeIds)) {
|
||||
return;
|
||||
@@ -83,21 +104,7 @@ public class BlockRenderingSettings {
|
||||
this.blockTypeIds = blockTypeIds;
|
||||
}
|
||||
|
||||
public void setEntityIds(Object2IntFunction<NamespacedId> entityIds) {
|
||||
// note: no reload needed, entities are rebuilt every frame.
|
||||
this.entityIds = entityIds;
|
||||
}
|
||||
|
||||
public void setItemIds(Object2IntFunction<NamespacedId> itemIds) {
|
||||
// note: no reload needed, items are rendered every frame.
|
||||
this.itemIds = itemIds;
|
||||
}
|
||||
|
||||
public float getAmbientOcclusionLevel() {
|
||||
return ambientOcclusionLevel;
|
||||
}
|
||||
|
||||
public void setAmbientOcclusionLevel(float ambientOcclusionLevel) {
|
||||
public void setAmbientOcclusionLevel(float ambientOcclusionLevel) {
|
||||
if (ambientOcclusionLevel == this.ambientOcclusionLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package net.coderbot.iris.gl.program;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.gl.GlResource;
|
||||
import net.coderbot.iris.gl.blending.DepthColorStorage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
public final class Program extends GlResource {
|
||||
|
||||
@Getter
|
||||
private final ProgramUniforms uniforms;
|
||||
|
||||
@Getter
|
||||
private final ProgramSamplers samplers;
|
||||
|
||||
@Getter
|
||||
private final ProgramImages images;
|
||||
|
||||
Program(int program, ProgramUniforms uniforms, ProgramSamplers samplers, ProgramImages images) {
|
||||
@@ -18,15 +25,15 @@ public final class Program extends GlResource {
|
||||
|
||||
DepthColorStorage.registerOwnedProgram(program);
|
||||
}
|
||||
|
||||
|
||||
public void use() {
|
||||
GLStateManager.glUseProgram(getGlId());
|
||||
|
||||
|
||||
uniforms.update();
|
||||
samplers.update();
|
||||
images.update();
|
||||
}
|
||||
|
||||
|
||||
public static void unbind() {
|
||||
ProgramUniforms.clearActiveUniforms();
|
||||
ProgramSamplers.clearActiveSamplers();
|
||||
@@ -38,12 +45,7 @@ public final class Program extends GlResource {
|
||||
DepthColorStorage.unregisterOwnedProgram(getGlId());
|
||||
GLStateManager.glDeleteProgram(getGlId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the OpenGL ID of this program.
|
||||
* @deprecated this should be encapsulated eventually
|
||||
*/
|
||||
@Deprecated
|
||||
|
||||
public int getProgramId() {
|
||||
return getGlId();
|
||||
}
|
||||
|
||||
@@ -181,9 +181,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
private final boolean allowConcurrentCompute;
|
||||
private final OptionalInt forcedShadowRenderDistanceChunks;
|
||||
private final CloudSetting dhCloudSetting;
|
||||
|
||||
private Pass current = null;
|
||||
|
||||
private WorldRenderingPhase overridePhase = null;
|
||||
private WorldRenderingPhase phase = WorldRenderingPhase.NONE;
|
||||
private boolean isBeforeTranslucent;
|
||||
@@ -279,7 +277,9 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
holder -> CommonUniforms.addNonDynamicUniforms(holder, programs.getPack().getIdMap(), programs.getPackDirectives(), this.updateNotifier)
|
||||
);
|
||||
|
||||
BlockRenderingSettings.INSTANCE.setBlockMetaMatches(BlockMaterialMapping.createBlockMetaIdMap(programs.getPack().getIdMap().getBlockProperties()));
|
||||
BlockRenderingSettings.INSTANCE.setBlockMetaMatches(BlockMaterialMapping.createBlockMetaIdMap(
|
||||
programs.getPack().getIdMap().getBlockProperties(),
|
||||
programs.getPack().getIdMap().hasLegacySection()));
|
||||
BlockRenderingSettings.INSTANCE.setBlockTypeIds(BlockMaterialMapping.createBlockTypeMap(programs.getPack().getIdMap().getBlockRenderTypeMap()));
|
||||
|
||||
BlockRenderingSettings.INSTANCE.setEntityIds(programs.getPack().getIdMap().getEntityIdMap());
|
||||
@@ -767,7 +767,11 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
public boolean shouldOverrideShaders() {
|
||||
return isRenderingWorld && !isRenderingFullScreenPass && !isPostChain && isMainBound;
|
||||
}
|
||||
|
||||
|
||||
public Pass getActivePassProgram() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public int getActivePassProgramId() {
|
||||
if (current == null) return -1;
|
||||
final Program p = current.getProgram();
|
||||
@@ -1048,7 +1052,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
return shadowRenderTargets != null;
|
||||
}
|
||||
|
||||
private final class Pass {
|
||||
public final class Pass {
|
||||
@Nullable
|
||||
private final Program program;
|
||||
private final GlFramebuffer framebufferBeforeTranslucents;
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A utility class for parsing entries in item.properties, block.properties, and entities.properties files in shaderpacks
|
||||
@@ -61,14 +62,24 @@ public class IdMap {
|
||||
@Getter
|
||||
private Map<NamespacedId, BlockRenderType> blockRenderTypeMap;
|
||||
|
||||
/** True when block.properties contained a preprocessor directive guarding a 1.7.10-specific section. */
|
||||
private final boolean hasLegacySection;
|
||||
|
||||
public boolean hasLegacySection() {
|
||||
return hasLegacySection;
|
||||
}
|
||||
|
||||
private static final Pattern LEGACY_DIRECTIVE_PATTERN = Pattern.compile(
|
||||
"(?m)^\\s*#\\s*(?:if|elif|ifdef|ifndef)\\b[^\\n]*\\bMC_VERSION\\b[^\\n]*\\b10710\\b");
|
||||
|
||||
IdMap(Path shaderPath, ShaderPackOptions shaderPackOptions, Iterable<StringPair> environmentDefines) {
|
||||
// Check if block.properties has a dedicated 1.7.10 section
|
||||
String rawBlockProperties = readProperties(shaderPath, "block.properties");
|
||||
boolean hasLegacySection = rawBlockProperties != null
|
||||
&& rawBlockProperties.contains("MC_VERSION") && rawBlockProperties.contains("10710");
|
||||
this.hasLegacySection = rawBlockProperties != null
|
||||
&& LEGACY_DIRECTIVE_PATTERN.matcher(rawBlockProperties).find();
|
||||
|
||||
Iterable<StringPair> resolvedDefines;
|
||||
if (hasLegacySection) {
|
||||
if (this.hasLegacySection) {
|
||||
// Pack has a 1.7.10 section
|
||||
resolvedDefines = environmentDefines;
|
||||
loadProperties(shaderPath, "block.properties", shaderPackOptions, environmentDefines).ifPresent(blockProperties -> {
|
||||
|
||||
@@ -8,5 +8,5 @@ public class IrisLimits {
|
||||
* It's not recommended to raise this higher than 16 until code for avoiding allocation of unused color textures
|
||||
* is implemented.
|
||||
*/
|
||||
public static final int MAX_COLOR_BUFFERS = 16;
|
||||
public static final int MAX_COLOR_BUFFERS = 32;
|
||||
}
|
||||
@@ -128,9 +128,9 @@ public class IrisExclusiveUniforms {
|
||||
|
||||
public static class WorldInfoUniforms {
|
||||
public static void addWorldInfoUniforms(UniformHolder uniforms) {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "bedrockLevel", () -> 0);
|
||||
uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "cloudHeight", () -> {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
if (level != null && level.provider != null) {
|
||||
return level.provider.getCloudHeight();
|
||||
} else {
|
||||
@@ -138,6 +138,7 @@ public class IrisExclusiveUniforms {
|
||||
}
|
||||
});
|
||||
uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "heightLimit", () -> {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
if (level != null && level.provider != null) {
|
||||
return level.provider.getHeight();
|
||||
} else {
|
||||
@@ -145,6 +146,7 @@ public class IrisExclusiveUniforms {
|
||||
}
|
||||
});
|
||||
uniforms.uniform1b(UniformUpdateFrequency.PER_FRAME, "hasCeiling", () -> {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
if (level != null && level.provider != null) {
|
||||
return level.provider.hasNoSky;
|
||||
} else {
|
||||
@@ -152,6 +154,7 @@ public class IrisExclusiveUniforms {
|
||||
}
|
||||
});
|
||||
uniforms.uniform1b(UniformUpdateFrequency.PER_FRAME, "hasSkylight", () -> {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
if (level != null && level.provider != null) {
|
||||
return !level.provider.hasNoSky;
|
||||
} else {
|
||||
@@ -159,6 +162,7 @@ public class IrisExclusiveUniforms {
|
||||
}
|
||||
});
|
||||
uniforms.uniform1f(UniformUpdateFrequency.PER_FRAME, "ambientLight", () -> {
|
||||
final WorldClient level = Minecraft.getMinecraft().theWorld;
|
||||
if (level != null && level.provider != null) {
|
||||
return level.provider.lightBrightnessTable[0];
|
||||
} else {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package net.coderbot.iris.vertices;
|
||||
|
||||
/**
|
||||
* Some annoying global state needed for the extended vertex format disabling optimization.
|
||||
*/
|
||||
public class ImmediateState {
|
||||
public static boolean isRenderingLevel = false;
|
||||
public static boolean renderWithExtendedVertexFormat = true;
|
||||
}
|
||||
@@ -42,8 +42,6 @@ public abstract class MixinEntity implements IDynamicLightSource {
|
||||
@Unique
|
||||
private int angelica$lastLuminance = 0;
|
||||
@Unique
|
||||
private static long angelica$lastUpdate = 0;
|
||||
@Unique
|
||||
private double angelica$prevX;
|
||||
@Unique
|
||||
private double angelica$prevY;
|
||||
@@ -54,7 +52,6 @@ public abstract class MixinEntity implements IDynamicLightSource {
|
||||
@Unique
|
||||
private LongOpenHashSet angelica$prevTrackedLitChunkPos = null;
|
||||
|
||||
|
||||
@Override
|
||||
public double angelica$getDynamicLightX() {
|
||||
return posX;
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldSettings;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -16,14 +15,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(World.class)
|
||||
public abstract class MixinWorld {
|
||||
|
||||
@Shadow
|
||||
protected abstract void initialize(WorldSettings p_72963_1_);
|
||||
@Shadow
|
||||
public abstract Block getBlock(int p_147439_1_, int p_147439_2_, int p_147439_3_);
|
||||
|
||||
@Inject(method = "onEntityRemoved", at = @At("HEAD"))
|
||||
private void angelica$removeEntity(Entity entity, CallbackInfo ci){
|
||||
if (entity instanceof IDynamicLightSource lightSource){
|
||||
private void angelica$removeEntity(Entity entity, CallbackInfo ci) {
|
||||
if (entity.worldObj.isRemote && entity instanceof IDynamicLightSource lightSource) {
|
||||
lightSource.angelica$setDynamicLightEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,13 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.fontrenderer;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.font.IFontParameters;
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_LENGTH;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
|
||||
import com.gtnewhorizons.angelica.compat.GTNHLibCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
@@ -16,6 +23,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@@ -110,16 +118,13 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
|
||||
@Unique
|
||||
public BatchingFontRenderer angelica$batcher;
|
||||
|
||||
@Unique
|
||||
private static final char angelica$FORMATTING_CHAR = 167; // §
|
||||
|
||||
@Unique
|
||||
private static final float angelica$1_over_255 = 1.0f/255.0f; // §
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void angelica$injectBatcher(GameSettings settings, ResourceLocation fontLocation, TextureManager texManager,
|
||||
boolean unicodeMode, CallbackInfo ci) {
|
||||
angelica$batcher = new BatchingFontRenderer((FontRenderer) (Object) this, this.charWidth, this.colorCode, this.locationFontTexture);
|
||||
if (GTNHLibCompat.HAS_TEXT_PREPROCESSOR) {
|
||||
GTNHLibCompat.registerPreprocessor();
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -158,6 +163,8 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
|
||||
}
|
||||
else
|
||||
{
|
||||
text = ColorCodeUtils.convertAmpersandToSectionX(text);
|
||||
|
||||
if (this.bidiFlag)
|
||||
{
|
||||
text = this.bidiReorder(text);
|
||||
@@ -226,4 +233,91 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
|
||||
public float getCharWidthFine(char chr) {
|
||||
return angelica$getBatcher().getCharWidthFine(chr);
|
||||
}
|
||||
|
||||
@Inject(method = "getFormatFromString", at = @At("HEAD"), cancellable = true)
|
||||
private static void angelica$getFormatFromString(String text, CallbackInfoReturnable<String> cir) {
|
||||
if (!AngelicaConfig.enableRGBColors) return;
|
||||
cir.setReturnValue(angelica$extractFormat(text));
|
||||
}
|
||||
|
||||
@Unique private static final StringBuilder EXTRACT_STYLES = new StringBuilder();
|
||||
@Unique private static final StringBuilder EXTRACT_EFFECTS = new StringBuilder();
|
||||
@Unique private static final StringBuilder EXTRACT_COLOR = new StringBuilder();
|
||||
@Unique private static final StringBuilder EXTRACT_RESULT = new StringBuilder();
|
||||
|
||||
@Unique
|
||||
private static String angelica$extractFormat(String text) {
|
||||
text = ColorCodeUtils.convertAmpersandToSectionX(text);
|
||||
final int len = text.length();
|
||||
if (len < 2) return "";
|
||||
|
||||
String lastColor = "";
|
||||
final StringBuilder styles = EXTRACT_STYLES; styles.setLength(0);
|
||||
final StringBuilder effects = EXTRACT_EFFECTS; effects.setLength(0);
|
||||
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
if (text.charAt(i) != FORMATTING_CHAR) continue;
|
||||
final char code = Character.toLowerCase(text.charAt(i + 1));
|
||||
|
||||
if (code == 'g' && i + GRADIENT_LENGTH <= len && ColorCodeUtils.isValidSectionX(text, i + 2) && ColorCodeUtils.isValidSectionX(text, i + 2 + SECTION_X_LENGTH)) {
|
||||
final StringBuilder cb = EXTRACT_COLOR; cb.setLength(0);
|
||||
for (int j = 0; j < GRADIENT_LENGTH; j++) cb.append(text.charAt(i + j));
|
||||
lastColor = cb.toString();
|
||||
styles.setLength(0);
|
||||
effects.setLength(0);
|
||||
i += GRADIENT_LENGTH - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code == 'x' && i + SECTION_X_LENGTH <= len && ColorCodeUtils.isValidSectionX(text, i)) {
|
||||
final StringBuilder cb = EXTRACT_COLOR; cb.setLength(0);
|
||||
for (int j = 0; j < SECTION_X_LENGTH; j++) cb.append(text.charAt(i + j));
|
||||
lastColor = cb.toString();
|
||||
styles.setLength(0);
|
||||
effects.setLength(0);
|
||||
i += SECTION_X_LENGTH - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((code >= '0' && code <= '9') || (code >= 'a' && code <= 'f')) {
|
||||
lastColor = ColorCodeUtils.sectionPrefix(code);
|
||||
styles.setLength(0);
|
||||
effects.setLength(0);
|
||||
i++;
|
||||
} else if (code == 'r') {
|
||||
lastColor = "";
|
||||
styles.setLength(0);
|
||||
effects.setLength(0);
|
||||
i++;
|
||||
} else if (code >= 'k' && code <= 'o') {
|
||||
styles.append(ColorCodeUtils.sectionPrefix(code));
|
||||
i++;
|
||||
} else if (code == 'q' || code == 'z' || code == 'v') {
|
||||
effects.append(ColorCodeUtils.sectionPrefix(code));
|
||||
i++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (styles.length() == 0 && effects.length() == 0) return lastColor;
|
||||
final StringBuilder result = EXTRACT_RESULT; result.setLength(0);
|
||||
result.append(lastColor).append(styles).append(effects);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "getStringWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||
private String angelica$convertBeforeWidth(String str) {
|
||||
return ColorCodeUtils.convertAmpersandToSectionX(str);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "listFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||
private String angelica$convertBeforeListWrap(String str) {
|
||||
return ColorCodeUtils.convertAmpersandToSectionX(str);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "wrapFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||
private String angelica$convertBeforeWrap(String str) {
|
||||
return ColorCodeUtils.convertAmpersandToSectionX(str);
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,13 @@ public class MixinSplashProgressCaching {
|
||||
ImmediateModeRecorder.initSplashTessellator();
|
||||
}
|
||||
|
||||
// VAOs aren't shared across GL contexts; the SharedDrawable the client thread just swapped
|
||||
// onto has none. Core profile (macOS) rejects glValidateProgram without one.
|
||||
@Inject(method = "start", at = @At("RETURN"))
|
||||
private static void angelica$bindSharedDrawableVAO(CallbackInfo ci) {
|
||||
GLStateManager.glBindVertexArray(GLStateManager.glGenVertexArrays());
|
||||
}
|
||||
|
||||
/**
|
||||
* On return from finish() - destroy splash tessellator and mark splash complete
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.core.GTNHLibCore;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.compat.DriverCompatabilityCheck;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -24,13 +26,14 @@ public class MixinInitGLStateManager {
|
||||
@Inject(method = "initializeTextures", at = @At("RETURN"))
|
||||
private static void angelica$initializeGLStateManager(CallbackInfo ci) {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
mc.gameSettings.fboEnable = true; // Angelica & many other GTNH features require FBO's
|
||||
GLStateManager.setDrawableGL(Display.getDrawable());
|
||||
GLStateManager.initialize(GLSMInitConfig.builder()
|
||||
.displaySize(mc.displayWidth, mc.displayHeight)
|
||||
.lwjglDebug(AngelicaMod.lwjglDebug)
|
||||
.streamingUploadStrategy(ClientProxy.options().advanced.streamingUploadStrategy)
|
||||
.framebufferSupported(OpenGlHelper.framebufferSupported)
|
||||
.fboEnabled(mc.gameSettings.fboEnable)
|
||||
.noErrorChecks(AngelicaConfig.disableErrorChecks && GTNHLibCore.isObf() && !AngelicaMod.lwjglDebug)
|
||||
.enableDSA(AngelicaConfig.enableDSA)
|
||||
.directDrawer(TessellatorStreamingDrawer::drawDirect)
|
||||
.streamingDrawerDestroy(TessellatorStreamingDrawer::destroy)
|
||||
.postInitCallback(SelectionBoxRenderer::init)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.dragonapi;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
|
||||
@Pseudo
|
||||
@Mixin(targets = { "Reika/DragonAPI/IO/Shaders/ShaderRegistry" }, remap = false)
|
||||
public class MixinShaderRegistry_ParseError {
|
||||
|
||||
@Redirect(method = "parseError",
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL20;glGetShaderInfoLog(II)Ljava/lang/String;"))
|
||||
private static String angelica$routeInfoLogByType(int id, int maxLen) {
|
||||
return GLStateManager.glIsProgram(id) ? GLStateManager.glGetProgramInfoLog(id, maxLen) : GLStateManager.glGetShaderInfoLog(id, maxLen);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.prupe.mcpatcher.ctm.CTMUtils;
|
||||
import com.prupe.mcpatcher.ctm.CompactCtmQuadProcessor;
|
||||
import com.prupe.mcpatcher.ctm.TileOverrideImpl;
|
||||
import com.prupe.mcpatcher.ctm.RenderBlockState;
|
||||
import cpw.mods.fml.client.registry.RenderingRegistry;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
@@ -20,7 +20,6 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -55,11 +54,13 @@ public abstract class MixinRenderBlocks {
|
||||
|
||||
@Inject(method = "renderBlockByRenderType", at = @At("HEAD"))
|
||||
private void renderingByTypeEnable(CallbackInfoReturnable<Boolean> ci) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
this.isRenderingByType = true;
|
||||
}
|
||||
|
||||
@Inject(method = "renderBlockByRenderType", at = @At("TAIL"))
|
||||
private void renderingByTypeDisable(CallbackInfoReturnable<Boolean> ci) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
this.isRenderingByType = false;
|
||||
}
|
||||
|
||||
@@ -151,6 +152,7 @@ public abstract class MixinRenderBlocks {
|
||||
try {
|
||||
return RenderingRegistry.instance().renderWorldBlock(rb, world, x, y, z, block, modelId);
|
||||
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
// Render Error Block
|
||||
int meta = exceptionErrorBlockMap.getOrDefault(e.getClass(), 0);
|
||||
rb.overrideBlockTexture = BlockError.icons[exceptionErrorBlockMap.getOrDefault(e.getClass(), 0)];
|
||||
@@ -166,19 +168,16 @@ public abstract class MixinRenderBlocks {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = { "renderFaceXPos", "renderFaceYPos", "renderFaceZPos",
|
||||
"renderFaceXNeg", "renderFaceYNeg", "renderFaceZNeg" },
|
||||
at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/renderer/RenderBlocks;enableAO:Z"))
|
||||
@ModifyExpressionValue(method = { "renderStandardBlockWithColorMultiplier" },
|
||||
at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/renderer/RenderBlocks;renderAllFaces:Z"))
|
||||
private boolean applyAOBrightness(boolean original, @Local(ordinal = 0) Tessellator tessellator) {
|
||||
((StateAwareTessellator)tessellator).angelica$setAppliedAo(this.applyingCeleritasAO);
|
||||
return original;
|
||||
}
|
||||
|
||||
@Inject(method = { "renderFaceXPos", "renderFaceYPos", "renderFaceZPos",
|
||||
"renderFaceXNeg", "renderFaceYNeg", "renderFaceZNeg" },
|
||||
@Inject(method = { "renderStandardBlockWithColorMultiplier" },
|
||||
at = @At("RETURN"))
|
||||
private void resetAOFlag(Block p_147764_1_, double p_147764_2_, double p_147764_4_, double p_147764_6_, IIcon p_147764_8_, CallbackInfo ci,
|
||||
@Local(ordinal = 0) Tessellator tessellator) {
|
||||
private void resetAOFlag(Block p_147736_1_, int p_147736_2_, int p_147736_3_, int p_147736_4_, float p_147736_5_, float p_147736_6_, float p_147736_7_, CallbackInfoReturnable<Boolean> cir, @Local(ordinal = 0) Tessellator tessellator) {
|
||||
((StateAwareTessellator)tessellator).angelica$setAppliedAo(false);
|
||||
}
|
||||
|
||||
@@ -186,80 +185,51 @@ public abstract class MixinRenderBlocks {
|
||||
public IBlockAccess blockAccess;
|
||||
|
||||
@Unique
|
||||
private boolean compactCtmRendering = false;
|
||||
|
||||
@Unique
|
||||
private CompactCtmQuadProcessor getCompactProcessor() {
|
||||
TileOverrideImpl.CTMCompact compact = CTMUtils.getCurrentCompact();
|
||||
if (compact == null) {
|
||||
return null;
|
||||
}
|
||||
return compact.getProcessor();
|
||||
}
|
||||
|
||||
private void handleCompactCtmFace(int face, Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
// recursion protection
|
||||
if (compactCtmRendering) {
|
||||
private void angelica$handleCompactCtmFace(IIcon icon, CallbackInfo ci) {
|
||||
CTMUtils.CTMCompactContext ctx = CTMUtils.getCurrentCompact();
|
||||
if (ctx == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompactCtmQuadProcessor processor = ctx.compact().getProcessor();
|
||||
RenderBlockState renderBlockState = ctx.renderBlockState();
|
||||
CTMUtils.clearCurrentCompact();
|
||||
if (this.blockAccess == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompactCtmQuadProcessor processor = getCompactProcessor();
|
||||
if (processor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
compactCtmRendering = true;
|
||||
try {
|
||||
boolean handled = processor.processFace(
|
||||
(RenderBlocks)(Object)this,
|
||||
this.blockAccess,
|
||||
MathHelper.floor_double(x),
|
||||
MathHelper.floor_double(y),
|
||||
MathHelper.floor_double(z),
|
||||
face,
|
||||
icon
|
||||
);
|
||||
|
||||
if (handled) {
|
||||
ci.cancel();
|
||||
}
|
||||
} finally {
|
||||
compactCtmRendering = false;
|
||||
if (processor.processFace((RenderBlocks)(Object)this, renderBlockState, icon)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(0, block, x, y, z, icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(1, block, x, y, z, icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(2, block, x, y, z, icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(3, block, x, y, z, icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(4, block, x, y, z, icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
handleCompactCtmFace(5, block, x, y, z, icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderStandardBlock(Lnet/minecraft/block/Block;III)Z", at = @At("RETURN"))
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.render.EmissiveTextureHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(RenderBlocks.class)
|
||||
public class MixinRenderBlocksEmissive {
|
||||
|
||||
@Inject(method = "renderStandardBlock", at = @At("TAIL"))
|
||||
private void angelica$renderEmissiveOverlay(Block block, int x, int y, int z, CallbackInfoReturnable<Boolean> cir) {
|
||||
RenderBlocks self = (RenderBlocks)(Object)this;
|
||||
IBlockAccess world = self.blockAccess;
|
||||
if (world == null) return;
|
||||
|
||||
int meta = world.getBlockMetadata(x, y, z);
|
||||
|
||||
boolean hasEmissive = false;
|
||||
for (int side = 0; side < 6; side++) {
|
||||
if (EmissiveTextureHelper.getEmissiveIcon(block, side, meta) != null) {
|
||||
hasEmissive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasEmissive) return;
|
||||
|
||||
Tessellator tess = Tessellator.instance;
|
||||
tess.setBrightness(0xF000F0);
|
||||
|
||||
EmissiveTextureHelper.renderEmissiveBlockOverlay(self, block, x, y, z, meta);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import com.prupe.mcpatcher.ctm.CTMUtils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
|
||||
@@ -17,6 +18,7 @@ public class MixinTileEntityRendererDispatcher {
|
||||
|
||||
@Inject(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntity;DDDF)V", at = @At("HEAD"))
|
||||
private void iris$setBlockEntityId(TileEntity te, double x, double y, double z, float partialTicks, CallbackInfo ci) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
if (te == null) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
|
||||
return;
|
||||
@@ -46,6 +48,7 @@ public class MixinTileEntityRendererDispatcher {
|
||||
|
||||
@Inject(method = "renderTileEntityAt(Lnet/minecraft/tileentity/TileEntity;DDDF)V", at = @At("RETURN"))
|
||||
private void iris$resetBlockEntityId(TileEntity te, double x, double y, double z, float partialTicks, CallbackInfo ci) {
|
||||
CTMUtils.clearCurrentCompact();
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
* Ported with additional fixes from the ShaderFixer mod
|
||||
* @author kotmatross28729
|
||||
*/
|
||||
@Mixin(value = SkyProviderCelestial.class, priority = 100)
|
||||
@Mixin(value = SkyProviderCelestial.class, priority = 100, remap = false)
|
||||
public class MixinSkyProviderCelestial {
|
||||
|
||||
/**
|
||||
@@ -38,7 +38,9 @@ public class MixinSkyProviderCelestial {
|
||||
"renderSunset",
|
||||
"renderCelestials",
|
||||
"renderAtmosphereGlow",
|
||||
}, at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
|
||||
}
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
|
||||
private boolean iris$common$redirectTex2D(int cap) {
|
||||
return this.iris$common$redirectTexture(cap);
|
||||
}
|
||||
@@ -46,9 +48,11 @@ public class MixinSkyProviderCelestial {
|
||||
/**
|
||||
* Avoid program rebinding due to pipeline.setInputs | Doesn't affect texture disabling before glSkyList
|
||||
*/
|
||||
@WrapWithCondition(method = "render",
|
||||
slice = @Slice(from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V", ordinal = 1)),
|
||||
at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
|
||||
@WrapWithCondition(method = "render"
|
||||
, slice = @Slice(from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"
|
||||
, ordinal = 1))
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
|
||||
private boolean iris$common$redirectTex2DException(int cap) {
|
||||
return this.iris$common$redirectTexture(cap);
|
||||
}
|
||||
@@ -65,66 +69,98 @@ public class MixinSkyProviderCelestial {
|
||||
/**
|
||||
* Force skybox to render with the default program | Fix various issues with shaders enabled
|
||||
*/
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"), remap = false)
|
||||
@Inject(method = "render"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"
|
||||
, remap = true))
|
||||
public void iris$main$renderInDefaultProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
|
||||
main$previousProgram.set(GLStateManager.getActiveProgram());
|
||||
GLStateManager.glUseProgram(0);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "render",
|
||||
at = @At(value = "INVOKE", target = "Lcom/hbm/dim/SkyProviderCelestial;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"), remap = false)
|
||||
@WrapOperation(method = "render"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lcom/hbm/dim/SkyProviderCelestial;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"))
|
||||
private void iris$main$renderSunInShaderProgram(SkyProviderCelestial instance, float partialTicks, WorldClient world, Minecraft mc, CelestialBody sun, double sunSize, double coronaSize, float visibility, float pressure, Operation<Void> original, @Share("main$previousProgram") LocalIntRef main$previousProgram){
|
||||
GLStateManager.glUseProgram(main$previousProgram.get());
|
||||
original.call(instance, partialTicks, world, mc, sun, sunSize, coronaSize, visibility, pressure);
|
||||
GLStateManager.glUseProgram(0);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "TAIL"), remap = false)
|
||||
@Inject(method = "render"
|
||||
, at = @At(value = "TAIL"))
|
||||
public void iris$main$restorePreviousProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
|
||||
GLStateManager.glUseProgram(main$previousProgram.get());
|
||||
}
|
||||
|
||||
@Inject(method = "renderSun", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"),
|
||||
slice = @Slice(
|
||||
from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V", ordinal = 0),
|
||||
to = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V", ordinal = 1)
|
||||
@Inject(method = "renderSun"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"
|
||||
, remap = true)
|
||||
, slice = @Slice(
|
||||
from = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V"
|
||||
, ordinal = 0),
|
||||
to = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glColor4f(FFFF)V"
|
||||
, ordinal = 1)
|
||||
)
|
||||
)
|
||||
private void iris$sun$disableBlanking(CallbackInfo ci) {
|
||||
Tessellator.instance.vertexCount = 0;
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"),
|
||||
slice = @Slice(
|
||||
from = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 1),
|
||||
to = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 2)
|
||||
@Inject(method = "render"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lnet/minecraft/client/renderer/Tessellator;draw()I"
|
||||
, remap = true)
|
||||
, slice = @Slice(
|
||||
from = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
|
||||
, ordinal = 1),
|
||||
to = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
|
||||
, ordinal = 2)
|
||||
)
|
||||
)
|
||||
private void iris$main$disableVoid(CallbackInfo ci) {
|
||||
Tessellator.instance.vertexCount = 0;
|
||||
}
|
||||
|
||||
@WrapWithCondition(method = "render", at = {
|
||||
@At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 1),
|
||||
@At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V", ordinal = 2)
|
||||
}, remap = false)
|
||||
@WrapWithCondition(method = "render"
|
||||
, at = {
|
||||
@At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
|
||||
, ordinal = 1),
|
||||
@At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glCallList(I)V"
|
||||
, ordinal = 2)
|
||||
})
|
||||
private boolean angelica$renderHorizon(int i) {
|
||||
return (boolean) Settings.HORIZON.option.getStore();
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/Tessellator;instance:Lnet/minecraft/client/renderer/Tessellator;", opcode = Opcodes.GETSTATIC))
|
||||
@Inject(method = "render"
|
||||
, at = @At(value = "FIELD"
|
||||
, target = "Lnet/minecraft/client/renderer/Tessellator;instance:Lnet/minecraft/client/renderer/Tessellator;"
|
||||
, remap = true
|
||||
, opcode = Opcodes.GETSTATIC))
|
||||
private void iris$renderSky$beginNormalSky(CallbackInfo ci) {
|
||||
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setPhase(WorldRenderingPhase.SKY));
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glRotatef(FFFF)V", ordinal = 2), remap = false)
|
||||
@Inject(method = "render"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glRotatef(FFFF)V"
|
||||
, ordinal = 2))
|
||||
private void iris$renderSky$tiltSun(CallbackInfo ci) {
|
||||
Iris.getPipelineManager().getPipeline().ifPresent(
|
||||
p -> GLStateManager.glRotatef(p.getSunPathRotation(), 0.0F, 0.0F, 1.0F)
|
||||
);
|
||||
}
|
||||
|
||||
@Inject(method = "renderSun", at = @At(value = "HEAD"), remap = false)
|
||||
@Inject(method = "renderSun"
|
||||
, at = @At(value = "HEAD"))
|
||||
private void iris$setSunRenderStage(CallbackInfo ci) {
|
||||
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setPhase(WorldRenderingPhase.SUN));
|
||||
}
|
||||
|
||||
@@ -8,13 +8,15 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(value = SkyProviderLaytheSunset.class, priority = 100)
|
||||
@Mixin(value = SkyProviderLaytheSunset.class, priority = 100, remap = false)
|
||||
public class MixinSkyProviderLaytheSunset {
|
||||
|
||||
/**
|
||||
* Avoid program rebinding due to pipeline.setInputs
|
||||
*/
|
||||
@WrapWithCondition(method = "renderSunset", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
|
||||
@WrapWithCondition(method = "renderSunset"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
|
||||
private boolean iris$sunset$redirectTex2D(int cap) {
|
||||
if(cap == GL11.GL_TEXTURE_2D) {
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
|
||||
|
||||
@@ -17,13 +17,15 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = SkyProviderOrbit.class, priority = 100)
|
||||
@Mixin(value = SkyProviderOrbit.class, priority = 100, remap = false)
|
||||
public class MixinSkyProviderOrbit {
|
||||
|
||||
/**
|
||||
* Avoid program rebinding due to pipeline.setInputs
|
||||
*/
|
||||
@WrapWithCondition(method = "render", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"), remap = false)
|
||||
@WrapWithCondition(method = "render"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lorg/lwjgl/opengl/GL11;glDisable(I)V"))
|
||||
private boolean iris$common$redirectTex2D(int cap) {
|
||||
if (cap == GL11.GL_TEXTURE_2D) {
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(WorkaroundUtils.GL_TEXTURE_2D_WORKAROUND);
|
||||
@@ -35,24 +37,26 @@ public class MixinSkyProviderOrbit {
|
||||
/**
|
||||
* Force skybox to render with the default program | Fix various issues with shaders enabled
|
||||
*/
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"), remap = false)
|
||||
@Inject(method = "render"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lnet/minecraft/client/renderer/RenderHelper;disableStandardItemLighting()V"
|
||||
, remap = true))
|
||||
public void iris$main$renderInDefaultProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
|
||||
main$previousProgram.set(GLStateManager.getActiveProgram());
|
||||
GLStateManager.glUseProgram(0);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "render",
|
||||
at = @At(value = "INVOKE", target = "Lcom/hbm/dim/orbit/SkyProviderOrbit;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"), remap = false)
|
||||
@WrapOperation(method = "render"
|
||||
, at = @At(value = "INVOKE"
|
||||
, target = "Lcom/hbm/dim/orbit/SkyProviderOrbit;renderSun(FLnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/Minecraft;Lcom/hbm/dim/CelestialBody;DDFF)V"))
|
||||
private void iris$main$renderSunInShaderProgram(SkyProviderOrbit instance, float partialTicks, WorldClient world, Minecraft mc, CelestialBody sun, double sunSize, double coronaSize, float visibility, float pressure, Operation<Void> original, @Share("main$previousProgram") LocalIntRef main$previousProgram){
|
||||
GLStateManager.glUseProgram(main$previousProgram.get());
|
||||
original.call(instance, partialTicks, world, mc, sun, sunSize, coronaSize, visibility, pressure);
|
||||
GLStateManager.glUseProgram(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the program back before {@link #iris$main$renderInDefaultProgram(CallbackInfo, LocalIntRef)} call
|
||||
*/
|
||||
@Inject(method = "render", at = @At(value = "TAIL"), remap = false)
|
||||
|
||||
@Inject(method = "render"
|
||||
, at = @At(value = "TAIL"))
|
||||
public void iris$main$restorePreviousProgram(CallbackInfo ci, @Share("main$previousProgram") LocalIntRef main$previousProgram) {
|
||||
GLStateManager.glUseProgram(main$previousProgram.get());
|
||||
}
|
||||
|
||||