mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2e491ed47 | ||
|
|
bccc5b00ca |
No files matched your search
+1
-1
@@ -90,7 +90,7 @@ dependencies {
|
||||
compileOnly "org.apache.ant:ant:1.8.2"
|
||||
|
||||
// Celeritas (transitive=false: deps provided by GTNHLib/runtime)
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.3-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
|
||||
@@ -72,6 +72,7 @@ import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
@@ -418,6 +419,8 @@ public class GLStateManager {
|
||||
@Getter protected static int boundVBO;
|
||||
@Getter protected static int boundEBO;
|
||||
@Getter protected static int boundVAO;
|
||||
private static int boundPixelUnpackBuffer;
|
||||
private static int boundPixelPackBuffer;
|
||||
private static final Int2IntOpenHashMap vaoEboMap = new Int2IntOpenHashMap();
|
||||
|
||||
static {
|
||||
@@ -1005,6 +1008,8 @@ public class GLStateManager {
|
||||
|
||||
case GL15.GL_ARRAY_BUFFER_BINDING -> boundVBO;
|
||||
case GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING -> boundEBO;
|
||||
case GL21.GL_PIXEL_UNPACK_BUFFER_BINDING -> boundPixelUnpackBuffer;
|
||||
case GL21.GL_PIXEL_PACK_BUFFER_BINDING -> boundPixelPackBuffer;
|
||||
|
||||
case GL20.GL_CURRENT_PROGRAM -> activeProgram;
|
||||
|
||||
@@ -1789,6 +1794,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);
|
||||
@@ -1796,6 +1802,7 @@ public class GLStateManager {
|
||||
// Non-main thread or proxy texture - use direct GL call
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
|
||||
@@ -1808,7 +1815,9 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
|
||||
@@ -1821,7 +1830,9 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
@@ -1834,13 +1845,13 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels != null ? pixels.asIntBuffer() : null);
|
||||
suspendPixelUnpackBuffer();
|
||||
if (shouldUseDSA(target)) {
|
||||
// Use DSA to upload directly to the texture - keeps GL binding state unchanged
|
||||
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);
|
||||
}
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, long pixels_buffer_offset) {
|
||||
@@ -3780,12 +3791,32 @@ public class GLStateManager {
|
||||
};
|
||||
}
|
||||
|
||||
public static void glGetTexImage(int target, int level, int format, int type, java.nio.ByteBuffer pixels) { RENDER_BACKEND.getTexImage(target, level, format, type, pixels); }
|
||||
public static void glGetTexImage(int target, int level, int format, int type, java.nio.IntBuffer pixels) { RENDER_BACKEND.getTexImage(target, level, format, type, pixels); }
|
||||
public static void glGetTexImage(int target, int level, int format, int type, java.nio.ByteBuffer pixels) {
|
||||
suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.getTexImage(target, level, format, type, pixels);
|
||||
restorePixelPackBuffer();
|
||||
}
|
||||
public static void glGetTexImage(int target, int level, int format, int type, java.nio.IntBuffer pixels) {
|
||||
suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.getTexImage(target, level, format, type, pixels);
|
||||
restorePixelPackBuffer();
|
||||
}
|
||||
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels) { RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels); }
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) { RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels); }
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels) { RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels); }
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
restorePixelPackBuffer();
|
||||
}
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
restorePixelPackBuffer();
|
||||
}
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
restorePixelPackBuffer();
|
||||
}
|
||||
public static void glTexStorage2D(int target, int levels, int internalFormat, int width, int height) { RENDER_BACKEND.texStorage2D(target, levels, internalFormat, width, height); }
|
||||
public static void glClearTexImage(int texture, int level, int format, int type, java.nio.ByteBuffer data) { RENDER_BACKEND.clearTexImage(texture, level, format, type); }
|
||||
|
||||
@@ -4201,7 +4232,9 @@ public class GLStateManager {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw new UnsupportedOperationException("glTexImage1D in display lists not yet implemented");
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||
@@ -4209,7 +4242,9 @@ public class GLStateManager {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw new UnsupportedOperationException("glTexImage3D in display lists not yet implemented");
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {
|
||||
@@ -4217,14 +4252,18 @@ public class GLStateManager {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw new UnsupportedOperationException("glTexImage3D in display lists not yet implemented");
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexSubImage1D(int target, int level, int xoffset, int width, int format, int type, ByteBuffer pixels) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw new UnsupportedOperationException("glTexSubImage1D in display lists not yet implemented");
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texSubImage1D(target, level, xoffset, width, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glLineWidth(float width) {
|
||||
@@ -4302,6 +4341,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
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);
|
||||
@@ -4309,6 +4349,7 @@ public class GLStateManager {
|
||||
// Non-main thread or proxy texture - use direct GL call
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
@@ -4319,6 +4360,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
if (shouldUseDSA(target)) {
|
||||
// Use DSA to upload directly to the texture
|
||||
RenderSystem.textureSubImage2D(getBoundTextureForServerState(), target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
@@ -4326,6 +4368,7 @@ public class GLStateManager {
|
||||
// Non-main thread or proxy texture - use direct GL call
|
||||
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
}
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, long pixels_buffer_offset) {
|
||||
@@ -4339,7 +4382,9 @@ public class GLStateManager {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw new UnsupportedOperationException("glTexSubImage3D in display lists not yet implemented - if you see this, please report!");
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glCopyTexImage1D(int target, int level, int internalFormat, int x, int y, int width, int border) {
|
||||
@@ -4816,10 +4861,38 @@ public class GLStateManager {
|
||||
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
|
||||
boundEBO = buffer;
|
||||
vaoEboMap.put(boundVAO, buffer);
|
||||
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
|
||||
boundPixelUnpackBuffer = buffer;
|
||||
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
|
||||
boundPixelPackBuffer = buffer;
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(target, buffer);
|
||||
}
|
||||
|
||||
private static void suspendPixelUnpackBuffer() {
|
||||
if (boundPixelUnpackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void restorePixelUnpackBuffer() {
|
||||
if (boundPixelUnpackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, boundPixelUnpackBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void suspendPixelPackBuffer() {
|
||||
if (boundPixelPackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void restorePixelPackBuffer() {
|
||||
if (boundPixelPackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_PACK_BUFFER, boundPixelPackBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static int glGenBuffers() {
|
||||
return RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
@@ -4832,6 +4905,7 @@ public class GLStateManager {
|
||||
public static void glBufferData(int target, java.nio.ShortBuffer data, int usage) { RENDER_BACKEND.bufferData(target, data, usage); }
|
||||
public static void glBufferSubData(int target, long offset, java.nio.ByteBuffer data) { RENDER_BACKEND.bufferSubData(target, offset, data); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static boolean glUnmapBuffer(int target) { return RENDER_BACKEND.unmapBuffer(target); }
|
||||
public static void glGetBufferSubData(int target, long offset, java.nio.ByteBuffer data) { RENDER_BACKEND.getBufferSubData(target, offset, data); }
|
||||
public static int glGetBufferParameteri(int target, int pname) { return RENDER_BACKEND.getBufferParameteri(target, pname); }
|
||||
|
||||
@@ -928,6 +928,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return GL15.glMapBuffer(target, access, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer mapBuffer(int target, int access, long length, ByteBuffer old_buffer) {
|
||||
return GL15.glMapBuffer(target, access, length, old_buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unmapBuffer(int target) {
|
||||
return GL15.glUnmapBuffer(target);
|
||||
|
||||
@@ -212,6 +212,7 @@ public abstract class RenderBackend {
|
||||
public abstract void bufferData(int target, float[] data, int usage);
|
||||
public abstract void bufferSubData(int target, long offset, ByteBuffer data);
|
||||
public abstract ByteBuffer mapBuffer(int target, int access);
|
||||
public abstract ByteBuffer mapBuffer(int target, int access, long length, ByteBuffer old_buffer);
|
||||
public abstract boolean unmapBuffer(int target);
|
||||
public abstract void bufferStorage(int target, ByteBuffer data, int flags);
|
||||
public abstract void bufferStorage(int target, long size, int flags);
|
||||
|
||||
+5
@@ -913,6 +913,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
return GL15C.glMapBuffer(target, access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer mapBuffer(int target, int access, long length, ByteBuffer old_buffer) {
|
||||
return GL15C.glMapBuffer(target, access, length, old_buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unmapBuffer(int target) {
|
||||
return GL15C.glUnmapBuffer(target);
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
/**
|
||||
* Provides per-block RGB light color data for the rendering pipeline. Mods that replace the lighting engine (e.g. Supernova) implement this
|
||||
* and register via {@link #register(BlockLightProvider)}.
|
||||
*
|
||||
*/
|
||||
public interface BlockLightProvider {
|
||||
|
||||
/**
|
||||
* Get RGB block light at the given world coordinates.
|
||||
*
|
||||
* @param blockAccess the block access context (WorldSlice on builder threads, World on main thread)
|
||||
* @return packed RGB (see class Javadoc), or {@code -1} if no data is available
|
||||
*/
|
||||
int getBlockLightRGB(IBlockAccess blockAccess, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Get RGB sky light at the given world coordinates.
|
||||
*
|
||||
* @param blockAccess the block access context (WorldSlice on builder threads, World on main thread)
|
||||
* @return packed RGB (see class Javadoc), or {@code -1} if no data
|
||||
*/
|
||||
default int getSkyLightRGB(IBlockAccess blockAccess, int x, int y, int z) { return -1; }
|
||||
|
||||
/**
|
||||
* Capture per-section light data on the main thread for use by builder threads.
|
||||
* Called during {@code ClonedChunkSection.init()}.
|
||||
*
|
||||
* @return a {@link SectionLightData} snapshot, or {@code null} if not supported
|
||||
*/
|
||||
default SectionLightData prepareSectionData(Chunk chunk, int sectionY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Pack three 0-15 channels into a single int. */
|
||||
static int packRGB(int r, int g, int b) {
|
||||
return (r << 8) | (g << 4) | b;
|
||||
}
|
||||
|
||||
/** Extract the red channel (0-15) from a packed RGB word. */
|
||||
static int unpackR(int word) {
|
||||
return (word >> 8) & 0xF;
|
||||
}
|
||||
|
||||
/** Extract the green channel (0-15) from a packed RGB word. */
|
||||
static int unpackG(int word) {
|
||||
return (word >> 4) & 0xF;
|
||||
}
|
||||
|
||||
/** Extract the blue channel (0-15) from a packed RGB word. */
|
||||
static int unpackB(int word) {
|
||||
return word & 0xF;
|
||||
}
|
||||
|
||||
static BlockLightProvider getInstance() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
static boolean isRegistered() {
|
||||
return Holder.INSTANCE != Holder.DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the global block light provider. Must be called exactly once.
|
||||
*
|
||||
* @throws IllegalStateException if a provider has already been registered
|
||||
*/
|
||||
static void register(BlockLightProvider provider) {
|
||||
if (Holder.INSTANCE != Holder.DEFAULT) {
|
||||
throw new IllegalStateException("BlockLightProvider already registered: " + Holder.INSTANCE.getClass().getName() + ". Cannot register " + provider.getClass().getName());
|
||||
}
|
||||
Holder.INSTANCE = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call from your {@code IFMLLoadingPlugin} static initializer to enable colored light support.
|
||||
* Must be called before mixin application (i.e. during coremod loading). Calling after mixins are frozen will throw.
|
||||
*/
|
||||
static void enableColoredLight() {
|
||||
if (EarlyFlags.MIXINS_FROZEN) {
|
||||
throw new IllegalStateException("Too late to enable colored light -- mixin configs have already been evaluated. Call BlockLightProvider.enableColoredLight() from your IFMLLoadingPlugin.");
|
||||
}
|
||||
EarlyFlags.COLORED_LIGHT = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze the mixin config gate. After this call, {@link #enableColoredLight()} will throw.
|
||||
* Called by the mixin config evaluation path before reading {@link #coloredLightEnabled()}.
|
||||
*/
|
||||
static void freezeMixinConfig() {
|
||||
EarlyFlags.MIXINS_FROZEN = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether colored light support was enabled via {@link #enableColoredLight()}.
|
||||
* Call {@link #freezeMixinConfig()} separately to close the enablement window.
|
||||
*/
|
||||
static boolean coloredLightEnabled() {
|
||||
return EarlyFlags.COLORED_LIGHT;
|
||||
}
|
||||
|
||||
final class EarlyFlags {
|
||||
static boolean COLORED_LIGHT = false;
|
||||
static boolean MIXINS_FROZEN = false;
|
||||
}
|
||||
|
||||
final class Holder {
|
||||
static final BlockLightProvider DEFAULT = (_, _, _, _) -> -1;
|
||||
static BlockLightProvider INSTANCE = DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
/**
|
||||
* Mixin interface for {@code AoFaceData} providing per-corner RGB light values.
|
||||
* Each array has 4 elements corresponding to the quad's corner vertices.
|
||||
*/
|
||||
public interface ExtAoFaceData {
|
||||
|
||||
/** Per-corner red light values (block light, 0-15 range). */
|
||||
float[] angelica$getRl();
|
||||
|
||||
/** Per-corner green light values (block light, 0-15 range). */
|
||||
float[] angelica$getGl();
|
||||
|
||||
/** Per-corner blue light values (block light, 0-15 range). */
|
||||
float[] angelica$getBl();
|
||||
|
||||
float angelica$getBlendedR(float[] w);
|
||||
float angelica$getBlendedG(float[] w);
|
||||
float angelica$getBlendedB(float[] w);
|
||||
|
||||
/** Per-corner red light values (sky light, 0-15 range). */
|
||||
float[] angelica$getSkyRl();
|
||||
|
||||
/** Per-corner green light values (sky light, 0-15 range). */
|
||||
float[] angelica$getSkyGl();
|
||||
|
||||
/** Per-corner blue light values (sky light, 0-15 range). */
|
||||
float[] angelica$getSkyBl();
|
||||
|
||||
float angelica$getBlendedSkyR(float[] w);
|
||||
float angelica$getBlendedSkyG(float[] w);
|
||||
float angelica$getBlendedSkyB(float[] w);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
|
||||
/**
|
||||
* Mixin interface for {@code LightDataAccess} providing per-block RGB light cache.
|
||||
*
|
||||
* <h3>Channel format</h3>
|
||||
* Individual RGB words use the {@link BlockLightProvider} packing: {@code (r << 8) | (g << 4) | b}, each channel 0-15.
|
||||
*
|
||||
*/
|
||||
public interface ExtLightDataAccess {
|
||||
|
||||
/** RGB block light at exact world coordinates. */
|
||||
int angelica$getRGB(int x, int y, int z);
|
||||
|
||||
/** RGB block light offset one block in {@code dir} (face neighbor lookup). */
|
||||
int angelica$getRGB(int x, int y, int z, ModelQuadFacing dir);
|
||||
|
||||
/** RGB block light offset by two directions (edge/corner neighbor for AO). */
|
||||
int angelica$getRGB(int x, int y, int z, ModelQuadFacing d1, ModelQuadFacing d2);
|
||||
|
||||
boolean angelica$isRGBEnabled();
|
||||
|
||||
/** RGB sky light at exact world coordinates. */
|
||||
int angelica$getSkyRGB(int x, int y, int z);
|
||||
|
||||
/** RGB sky light offset one block in {@code dir} (face neighbor lookup). */
|
||||
int angelica$getSkyRGB(int x, int y, int z, ModelQuadFacing dir);
|
||||
|
||||
/** RGB sky light offset by two directions (edge/corner neighbor for AO). */
|
||||
int angelica$getSkyRGB(int x, int y, int z, ModelQuadFacing d1, ModelQuadFacing d2);
|
||||
|
||||
boolean angelica$isSkyRGBEnabled();
|
||||
|
||||
/**
|
||||
* Compute cache array index for the given world coordinates. Use with
|
||||
* {@link #angelica$getRGBByIndex}, {@link #angelica$getSkyRGBByIndex}, or
|
||||
* {@link #angelica$getFusedByIndex}
|
||||
*/
|
||||
int angelica$getIndex(int x, int y, int z);
|
||||
|
||||
/** Like {@link #angelica$getRGB(int, int, int)} but with a pre-computed cache index. */
|
||||
int angelica$getRGBByIndex(int idx, int x, int y, int z);
|
||||
|
||||
/** Like {@link #angelica$getSkyRGB(int, int, int)} but with a pre-computed cache index. */
|
||||
int angelica$getSkyRGBByIndex(int idx, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Single cache lookup returning both block and sky RGB in the <b>cache format</b>.
|
||||
* Upper 32 bits = block word (packed RGB | SENTINEL), lower 32 bits = sky word (packed RGB | SENTINEL).
|
||||
*/
|
||||
long angelica$getFusedByIndex(int idx, int x, int y, int z);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
/**
|
||||
* Interface for {@code LightDataCache} exposing its block access reference.
|
||||
*/
|
||||
public interface ExtLightDataCache {
|
||||
|
||||
IBlockAccess angelica$getBlockAccess();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
/**
|
||||
* Interface for {@code QuadLightData} providing per-vertex RGB light values.
|
||||
*/
|
||||
public interface ExtQuadLightData {
|
||||
|
||||
float[] angelica$getR();
|
||||
float[] angelica$getG();
|
||||
float[] angelica$getB();
|
||||
|
||||
boolean angelica$isRGBValid();
|
||||
void angelica$setRGBValid(boolean valid);
|
||||
|
||||
float[] angelica$getSkyR();
|
||||
float[] angelica$getSkyG();
|
||||
float[] angelica$getSkyB();
|
||||
|
||||
boolean angelica$isSkyRGBValid();
|
||||
void angelica$setSkyRGBValid(boolean valid);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
/**
|
||||
* Provides RGB light data for a single chunk section. Implemented by external providers (e.g. Supernova)
|
||||
* and returned from {@link BlockLightProvider#prepareSectionData}.
|
||||
*
|
||||
* <h3>Transport format</h3>
|
||||
* {@link #getRGBAndSkyRGB} returns a compact <b>transport format</b>: {@code ((block & 0xFFFF) << 16) | (sky & 0xFFFF)}.
|
||||
*/
|
||||
public interface SectionLightData {
|
||||
|
||||
/**
|
||||
* Get RGB block light at section-local coordinates.
|
||||
*
|
||||
* @param localX 0-15
|
||||
* @param localY 0-15
|
||||
* @param localZ 0-15
|
||||
* @return packed {@code (r << 8) | (g << 4) | b}, each channel 0-15, or {@code -1} if unavailable
|
||||
*/
|
||||
int getRGB(int localX, int localY, int localZ);
|
||||
|
||||
/**
|
||||
* Get RGB sky light at section-local coordinates.
|
||||
*
|
||||
* @param localX 0-15
|
||||
* @param localY 0-15
|
||||
* @param localZ 0-15
|
||||
* @return packed {@code (r << 8) | (g << 4) | b}, each channel 0-15, or {@code -1} if unavailable
|
||||
*/
|
||||
default int getSkyRGB(int localX, int localY, int localZ) { return -1; }
|
||||
|
||||
/**
|
||||
* Get both block and sky RGB in a single call (transport format). Implementations can override
|
||||
* to share index computation and apply section-level fast paths.
|
||||
*
|
||||
* @return {@code ((block & 0xFFFF) << 16) | (sky & 0xFFFF)}
|
||||
*/
|
||||
default long getRGBAndSkyRGB(int localX, int localY, int localZ) {
|
||||
return ((long)(getRGB(localX, localY, localZ) & 0xFFFF) << 16) | (getSkyRGB(localX, localY, localZ) & 0xFFFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
/**
|
||||
* Computes an RGB tint from block light and sky light channel values. The lightmap handles brightness; the tint only controls color.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TintComputer {
|
||||
|
||||
/**
|
||||
* Blend block light RGB and sky light RGB into a tint color.
|
||||
*
|
||||
* @param br block light red (0-15)
|
||||
* @param bg block light green (0-15)
|
||||
* @param bb block light blue (0-15)
|
||||
* @param sr sky light red (0-15, already adjusted for sky subtraction)
|
||||
* @param sg sky light green (0-15)
|
||||
* @param sb sky light blue (0-15)
|
||||
* @param out float[3] output: R, G, B tint multipliers (typically 0-1)
|
||||
*/
|
||||
void computeTint(float br, float bg, float bb, float sr, float sg, float sb, float[] out);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.gtnewhorizons.angelica.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Registry for tint blend modes used by the colored light pipeline. Provides a default no-tint mode.
|
||||
* External mods (e.g. Supernova) can register additional modes via {@link #registerMode(String, TintComputer)}.
|
||||
*
|
||||
*/
|
||||
public final class TintRegistry {
|
||||
|
||||
private static final TintComputer NO_TINT = (br, bg, bb, sr, sg, sb, out) -> {
|
||||
out[0] = 1f;
|
||||
out[1] = 1f;
|
||||
out[2] = 1f;
|
||||
};
|
||||
|
||||
private static final List<TintComputer> computers = new ArrayList<>();
|
||||
private static final List<String> names = new ArrayList<>();
|
||||
private static TintComputer current;
|
||||
private static volatile int currentOrdinal;
|
||||
|
||||
static {
|
||||
registerMode("NONE", NO_TINT);
|
||||
current = NO_TINT;
|
||||
currentOrdinal = 0;
|
||||
}
|
||||
|
||||
private TintRegistry() {}
|
||||
|
||||
/** Register a named tint blend mode. Returns the ordinal assigned. */
|
||||
public static int registerMode(String name, TintComputer computer) {
|
||||
final int ordinal = computers.size();
|
||||
computers.add(computer);
|
||||
names.add(name);
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
/** Returns the currently active tint computer. */
|
||||
public static TintComputer getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
/** Returns the ordinal of the current mode, or -1 if a custom (non-registered) computer is active. */
|
||||
public static int getCurrentOrdinal() {
|
||||
return currentOrdinal;
|
||||
}
|
||||
|
||||
/** Switch to a registered mode by ordinal. */
|
||||
public static void setCurrentByOrdinal(int ordinal) {
|
||||
if (ordinal < 0 || ordinal >= computers.size()) {
|
||||
throw new IllegalArgumentException("Invalid tint mode ordinal: " + ordinal + " (registered modes: " + computers.size() + ")");
|
||||
}
|
||||
current = computers.get(ordinal);
|
||||
currentOrdinal = ordinal;
|
||||
}
|
||||
|
||||
/** Set a custom tint computer directly. */
|
||||
public static void setCurrent(TintComputer computer) {
|
||||
current = computer;
|
||||
// Resolve ordinal; custom computers not in the registry get -1
|
||||
for (int i = 0; i < computers.size(); i++) {
|
||||
if (computers.get(i) == computer) {
|
||||
currentOrdinal = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentOrdinal = -1;
|
||||
}
|
||||
|
||||
/** Returns the number of registered blend modes. */
|
||||
public static int getModeCount() {
|
||||
return computers.size();
|
||||
}
|
||||
|
||||
/** Returns the display name of a registered mode by ordinal. */
|
||||
public static String getModeName(int ordinal) {
|
||||
if (ordinal < 0 || ordinal >= names.size()) return "UNKNOWN";
|
||||
return names.get(ordinal);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import baubles.common.lib.PlayerHandler;
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.IBlockPos;
|
||||
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.api.IDynamicLightProducer;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
|
||||
@@ -52,12 +53,18 @@ public class DynamicLights {
|
||||
private static final double MAX_RADIUS = 7.75;
|
||||
private static final double MAX_RADIUS_SQUARED = MAX_RADIUS * MAX_RADIUS;
|
||||
private final Set<IDynamicLightSource> dynamicLightSources = new ObjectOpenHashSet<>();
|
||||
private IDynamicLightSource[] lightSourceArray = new IDynamicLightSource[0];
|
||||
private final ReentrantReadWriteLock lightSourcesLock = new ReentrantReadWriteLock();
|
||||
private volatile int lightSourceCount = 0;
|
||||
private final ChunkRebuildManager chunkRebuildManager = new ChunkRebuildManager();
|
||||
private long lastUpdate = System.currentTimeMillis();
|
||||
private int lastUpdateCount = 0;
|
||||
|
||||
/** Must be called under write lock. */
|
||||
private void rebuildLightSourceArray() {
|
||||
this.lightSourceArray = this.dynamicLightSources.toArray(new IDynamicLightSource[0]);
|
||||
}
|
||||
|
||||
public static DynamicLights get() {
|
||||
if (instance == null)
|
||||
instance = new DynamicLights();
|
||||
@@ -93,7 +100,7 @@ public class DynamicLights {
|
||||
if (!isEnabled())
|
||||
return;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
final long now = System.currentTimeMillis();
|
||||
if (now >= this.lastUpdate + Mode.getDelay()) {
|
||||
this.lastUpdate = now;
|
||||
this.lastUpdateCount = 0;
|
||||
@@ -104,7 +111,7 @@ public class DynamicLights {
|
||||
int worldTick = 0;
|
||||
|
||||
if (AdaptiveTickingEnabled) {
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc.theWorld != null && mc.renderViewEntity != null) {
|
||||
cameraX = mc.renderViewEntity.posX;
|
||||
cameraY = mc.renderViewEntity.posY + mc.renderViewEntity.getEyeHeight();
|
||||
@@ -112,15 +119,15 @@ public class DynamicLights {
|
||||
worldTick = (int) (mc.theWorld.getTotalWorldTime() & 0x7FFFFFFF);
|
||||
|
||||
// Calculate look direction from yaw (normalized horizontal direction)
|
||||
float yaw = mc.renderViewEntity.rotationYaw;
|
||||
double yawRad = Math.toRadians(yaw);
|
||||
final float yaw = mc.renderViewEntity.rotationYaw;
|
||||
final double yawRad = Math.toRadians(yaw);
|
||||
lookDirX = -Math.sin(yawRad);
|
||||
lookDirZ = Math.cos(yawRad);
|
||||
}
|
||||
}
|
||||
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
for (var lightSource : this.dynamicLightSources) {
|
||||
for (var lightSource : this.lightSourceArray) {
|
||||
// Entity type filter
|
||||
if (!EntityLightConfig.isEntityTypeEnabled(lightSource)) {
|
||||
continue;
|
||||
@@ -128,7 +135,7 @@ public class DynamicLights {
|
||||
|
||||
// Adaptive ticking filter
|
||||
if (AdaptiveTickingEnabled) {
|
||||
AdaptiveTickMode mode = AdaptiveTickCalculator.calculate(
|
||||
final AdaptiveTickMode mode = AdaptiveTickCalculator.calculate(
|
||||
lightSource, cameraX, cameraY, cameraZ, lookDirX, lookDirZ);
|
||||
if (!mode.shouldTickThisFrame(worldTick, lightSource.hashCode())) {
|
||||
continue;
|
||||
@@ -150,6 +157,7 @@ public class DynamicLights {
|
||||
this.lightSourcesLock.writeLock().lock();
|
||||
if (this.dynamicLightSources.add(lightSource)) {
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
rebuildLightSourceArray();
|
||||
}
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
}
|
||||
@@ -162,17 +170,11 @@ public class DynamicLights {
|
||||
public void removeLightSource(@NotNull IDynamicLightSource lightSource) {
|
||||
this.lightSourcesLock.writeLock().lock();
|
||||
|
||||
var dynamicLightSources = this.dynamicLightSources.iterator();
|
||||
IDynamicLightSource it;
|
||||
while (dynamicLightSources.hasNext()) {
|
||||
it = dynamicLightSources.next();
|
||||
if (it.equals(lightSource)) {
|
||||
dynamicLightSources.remove();
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
if (activeRenderer != null)
|
||||
lightSource.angelica$scheduleTrackedChunksRebuild(activeRenderer);
|
||||
break;
|
||||
}
|
||||
if (this.dynamicLightSources.remove(lightSource)) {
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
rebuildLightSourceArray();
|
||||
if (activeRenderer != null)
|
||||
lightSource.angelica$scheduleTrackedChunksRebuild(activeRenderer);
|
||||
}
|
||||
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
@@ -186,13 +188,14 @@ public class DynamicLights {
|
||||
public void removeLightSources(@NotNull Predicate<IDynamicLightSource> filter) {
|
||||
this.lightSourcesLock.writeLock().lock();
|
||||
|
||||
var dynamicLightSources = this.dynamicLightSources.iterator();
|
||||
final var dynamicLightSources = this.dynamicLightSources.iterator();
|
||||
IDynamicLightSource it;
|
||||
boolean changed = false;
|
||||
while (dynamicLightSources.hasNext()) {
|
||||
it = dynamicLightSources.next();
|
||||
if (filter.test(it)) {
|
||||
dynamicLightSources.remove();
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
changed = true;
|
||||
if (activeRenderer != null) {
|
||||
if (it.angelica$getLuminance() > 0)
|
||||
it.angelica$resetDynamicLight();
|
||||
@@ -201,6 +204,10 @@ public class DynamicLights {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
this.lightSourceCount = this.dynamicLightSources.size();
|
||||
rebuildLightSourceArray();
|
||||
}
|
||||
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
}
|
||||
@@ -211,7 +218,7 @@ public class DynamicLights {
|
||||
public void clearLightSources() {
|
||||
this.lightSourcesLock.writeLock().lock();
|
||||
|
||||
var dynamicLightSources = this.dynamicLightSources.iterator();
|
||||
final var dynamicLightSources = this.dynamicLightSources.iterator();
|
||||
IDynamicLightSource it;
|
||||
while (dynamicLightSources.hasNext()) {
|
||||
it = dynamicLightSources.next();
|
||||
@@ -223,6 +230,7 @@ public class DynamicLights {
|
||||
}
|
||||
}
|
||||
this.lightSourceCount = 0;
|
||||
this.lightSourceArray = new IDynamicLightSource[0];
|
||||
|
||||
this.lightSourcesLock.writeLock().unlock();
|
||||
}
|
||||
@@ -234,7 +242,7 @@ public class DynamicLights {
|
||||
* @return {@code true} if the light source is tracked, else {@code false}
|
||||
*/
|
||||
public boolean containsLightSource(@NotNull IDynamicLightSource lightSource) {
|
||||
boolean result;
|
||||
final boolean result;
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
result = this.dynamicLightSources.contains(lightSource);
|
||||
this.lightSourcesLock.readLock().unlock();
|
||||
@@ -247,7 +255,7 @@ public class DynamicLights {
|
||||
* @return the number of dynamic light sources emitting light
|
||||
*/
|
||||
public int getLightSourcesCount() {
|
||||
int result;
|
||||
final int result;
|
||||
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
result = this.dynamicLightSources.size();
|
||||
@@ -259,7 +267,7 @@ public class DynamicLights {
|
||||
public double getDynamicLightLevel(int x, int y, int z) {
|
||||
double result = 0;
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
for (var lightSource : this.dynamicLightSources) {
|
||||
for (var lightSource : this.lightSourceArray) {
|
||||
result = maxDynamicLightLevel(x, y, z, lightSource, result);
|
||||
}
|
||||
this.lightSourcesLock.readLock().unlock();
|
||||
@@ -273,7 +281,7 @@ public class DynamicLights {
|
||||
public double getDynamicLightLevel(double x, double y, double z) {
|
||||
double result = 0;
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
for (var lightSource : this.dynamicLightSources) {
|
||||
for (var lightSource : this.lightSourceArray) {
|
||||
result = maxDynamicLightLevelExact(x, y, z, lightSource, result);
|
||||
}
|
||||
this.lightSourcesLock.readLock().unlock();
|
||||
@@ -286,19 +294,19 @@ public class DynamicLights {
|
||||
}
|
||||
|
||||
public static double maxDynamicLightLevel(int x, int y, int z, @NotNull IDynamicLightSource lightSource, double currentLightLevel) {
|
||||
int luminance = lightSource.angelica$getLuminance();
|
||||
final int luminance = lightSource.angelica$getLuminance();
|
||||
if (luminance > 0) {
|
||||
// Can't use Entity#squaredDistanceTo because of eye Y coordinate.
|
||||
double dx = x - lightSource.angelica$getDynamicLightX() + 0.5;
|
||||
double dy = y - lightSource.angelica$getDynamicLightY() + 0.5;
|
||||
double dz = z - lightSource.angelica$getDynamicLightZ() + 0.5;
|
||||
final double dx = x - lightSource.angelica$getDynamicLightX() + 0.5;
|
||||
final double dy = y - lightSource.angelica$getDynamicLightY() + 0.5;
|
||||
final double dz = z - lightSource.angelica$getDynamicLightZ() + 0.5;
|
||||
|
||||
double distanceSquared = dx * dx + dy * dy + dz * dz;
|
||||
final double distanceSquared = dx * dx + dy * dy + dz * dz;
|
||||
// 7.75 because else we would have to update more chunks and that's not a good idea.
|
||||
// 15 (max range for blocks) would be too much and a bit cheaty.
|
||||
if (distanceSquared <= MAX_RADIUS_SQUARED) {
|
||||
double multiplier = 1.0 - Math.sqrt(distanceSquared) / MAX_RADIUS;
|
||||
double lightLevel = multiplier * (double) luminance;
|
||||
final double multiplier = 1.0 - Math.sqrt(distanceSquared) / MAX_RADIUS;
|
||||
final double lightLevel = multiplier * (double) luminance;
|
||||
if (lightLevel > currentLightLevel) {
|
||||
return lightLevel;
|
||||
}
|
||||
@@ -312,16 +320,16 @@ public class DynamicLights {
|
||||
* Used for smooth per-vertex lighting.
|
||||
*/
|
||||
public static double maxDynamicLightLevelExact(double x, double y, double z, @NotNull IDynamicLightSource lightSource, double currentLightLevel) {
|
||||
int luminance = lightSource.angelica$getLuminance();
|
||||
final int luminance = lightSource.angelica$getLuminance();
|
||||
if (luminance > 0) {
|
||||
double dx = x - lightSource.angelica$getDynamicLightX();
|
||||
double dy = y - lightSource.angelica$getDynamicLightY();
|
||||
double dz = z - lightSource.angelica$getDynamicLightZ();
|
||||
final double dx = x - lightSource.angelica$getDynamicLightX();
|
||||
final double dy = y - lightSource.angelica$getDynamicLightY();
|
||||
final double dz = z - lightSource.angelica$getDynamicLightZ();
|
||||
|
||||
double distanceSquared = dx * dx + dy * dy + dz * dz;
|
||||
final double distanceSquared = dx * dx + dy * dy + dz * dz;
|
||||
if (distanceSquared <= MAX_RADIUS_SQUARED) {
|
||||
double multiplier = 1.0 - Math.sqrt(distanceSquared) / MAX_RADIUS;
|
||||
double lightLevel = multiplier * (double) luminance;
|
||||
final double multiplier = 1.0 - Math.sqrt(distanceSquared) / MAX_RADIUS;
|
||||
final double lightLevel = multiplier * (double) luminance;
|
||||
if (lightLevel > currentLightLevel) {
|
||||
return lightLevel;
|
||||
}
|
||||
@@ -360,11 +368,11 @@ public class DynamicLights {
|
||||
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
try {
|
||||
for (IDynamicLightSource source : this.dynamicLightSources) {
|
||||
double dx = source.angelica$getDynamicLightX() - centerX;
|
||||
double dy = source.angelica$getDynamicLightY() - centerY;
|
||||
double dz = source.angelica$getDynamicLightZ() - centerZ;
|
||||
double distSq = dx * dx + dy * dy + dz * dz;
|
||||
for (var source : this.lightSourceArray) {
|
||||
final double dx = source.angelica$getDynamicLightX() - centerX;
|
||||
final double dy = source.angelica$getDynamicLightY() - centerY;
|
||||
final double dz = source.angelica$getDynamicLightZ() - centerZ;
|
||||
final double distSq = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
if (distSq <= searchRadiusSq) {
|
||||
if (result == null) {
|
||||
@@ -380,6 +388,53 @@ public class DynamicLights {
|
||||
return result != null ? result : Collections.emptyList();
|
||||
}
|
||||
|
||||
public int getDynamicLightRGB(double x, double y, double z) {
|
||||
int accum = 0;
|
||||
this.lightSourcesLock.readLock().lock();
|
||||
try {
|
||||
for (var lightSource : this.lightSourceArray) {
|
||||
accum = accumulateSourceRGB(x, y, z, lightSource, accum);
|
||||
}
|
||||
} finally {
|
||||
this.lightSourcesLock.readLock().unlock();
|
||||
}
|
||||
return accum;
|
||||
}
|
||||
|
||||
public int getDynamicLightRGBFromSources(double x, double y, double z, List<IDynamicLightSource> sources) {
|
||||
if (sources == null || sources.isEmpty()) return 0;
|
||||
int accum = 0;
|
||||
for (int i = 0, size = sources.size(); i < size; i++) {
|
||||
accum = accumulateSourceRGB(x, y, z, sources.get(i), accum);
|
||||
}
|
||||
return accum;
|
||||
}
|
||||
|
||||
private static int accumulateSourceRGB(double x, double y, double z, IDynamicLightSource source, int accum) {
|
||||
final int luminance = source.angelica$getLuminance();
|
||||
if (luminance <= 0) return accum;
|
||||
|
||||
final int rgb = source.angelica$getLuminanceRGB();
|
||||
if (rgb == 0) return accum;
|
||||
|
||||
final double dx = x - source.angelica$getDynamicLightX();
|
||||
final double dy = y - source.angelica$getDynamicLightY();
|
||||
final double dz = z - source.angelica$getDynamicLightZ();
|
||||
final double distSq = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
if (distSq > MAX_RADIUS_SQUARED) return accum;
|
||||
|
||||
final double multiplier = 1.0 - Math.sqrt(distSq) / MAX_RADIUS;
|
||||
final int r = (int) (BlockLightProvider.unpackR(rgb) * multiplier);
|
||||
final int g = (int) (BlockLightProvider.unpackG(rgb) * multiplier);
|
||||
final int b = (int) (BlockLightProvider.unpackB(rgb) * multiplier);
|
||||
|
||||
return BlockLightProvider.packRGB(
|
||||
Math.max(BlockLightProvider.unpackR(accum), r),
|
||||
Math.max(BlockLightProvider.unpackG(accum), g),
|
||||
Math.max(BlockLightProvider.unpackB(accum), b));
|
||||
}
|
||||
|
||||
public double getDynamicLightLevelFromSources(double x, double y, double z, List<IDynamicLightSource> sources) {
|
||||
if (sources == null || sources.isEmpty()) {
|
||||
return 0;
|
||||
@@ -403,10 +458,10 @@ public class DynamicLights {
|
||||
// lightmap is (skyLevel << 20 | blockLevel << 4)
|
||||
|
||||
// Get vanilla block light level.
|
||||
int blockLevel = (lightmap & 0xFFFF) >> 4;
|
||||
final int blockLevel = (lightmap & 0xFFFF) >> 4;
|
||||
if (dynamicLightLevel > blockLevel) {
|
||||
// Equivalent to a << 4 bitshift with a little quirk: this one ensure more precision (more decimals are saved).
|
||||
int luminance = (int) (dynamicLightLevel * 16.0);
|
||||
final int luminance = (int) (dynamicLightLevel * 16.0);
|
||||
lightmap &= 0xfff00000;
|
||||
lightmap |= luminance & 0x000fffff;
|
||||
}
|
||||
@@ -485,8 +540,8 @@ public class DynamicLights {
|
||||
* @param lightSource the light source
|
||||
*/
|
||||
public static void updateTracking(@NotNull IDynamicLightSource lightSource) {
|
||||
boolean enabled = lightSource.angelica$isDynamicLightEnabled();
|
||||
int luminance = lightSource.angelica$getLuminance();
|
||||
final boolean enabled = lightSource.angelica$isDynamicLightEnabled();
|
||||
final int luminance = lightSource.angelica$getLuminance();
|
||||
|
||||
if (!enabled && luminance > 0) {
|
||||
lightSource.angelica$setDynamicLightEnabled(true);
|
||||
@@ -503,9 +558,9 @@ public class DynamicLights {
|
||||
* @return the luminance of the item
|
||||
*/
|
||||
public static int getLuminanceFromItemStack(@NotNull ItemStack stack) {
|
||||
Item item = stack.getItem();
|
||||
final Item item = stack.getItem();
|
||||
if (item instanceof ItemBlock itemBlock) {
|
||||
Block block = itemBlock.field_150939_a;
|
||||
final Block block = itemBlock.field_150939_a;
|
||||
if (block != null) {
|
||||
return block.getLightValue();
|
||||
}
|
||||
@@ -522,7 +577,7 @@ public class DynamicLights {
|
||||
|
||||
if (entity.isBurning()) return 15;
|
||||
|
||||
boolean inWater = entity.isInsideOfMaterial(Material.water);
|
||||
final boolean inWater = entity.isInsideOfMaterial(Material.water);
|
||||
|
||||
// TODO only have certain items not glow in water?
|
||||
if (inWater) return 0;
|
||||
@@ -547,23 +602,23 @@ public class DynamicLights {
|
||||
player instanceof IBattlePlayer battlePlayer &&
|
||||
battlePlayer.battlegear2$isBattlemode()
|
||||
) {
|
||||
ItemStack offhand = Battlegear2Compat.getBattlegear2Offhand(player);
|
||||
final ItemStack offhand = Battlegear2Compat.getBattlegear2Offhand(player);
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand));
|
||||
}
|
||||
}
|
||||
else if (ModStatus.isBackhandLoaded){
|
||||
ItemStack offhand = BackhandReflectionCompat.getOffhandItem(player);
|
||||
final ItemStack offhand = BackhandReflectionCompat.getOffhandItem(player);
|
||||
if (offhand != null) {
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand));
|
||||
}
|
||||
}
|
||||
|
||||
if (ModStatus.isBaublesLoaded){
|
||||
var playerBaubles = PlayerHandler.getPlayerBaubles(player);
|
||||
final var playerBaubles = PlayerHandler.getPlayerBaubles(player);
|
||||
if (playerBaubles != null){
|
||||
for (int i = 0; i < playerBaubles.getSizeInventory(); i++){
|
||||
var stack = playerBaubles.getStackInSlot(i);
|
||||
final var stack = playerBaubles.getStackInSlot(i);
|
||||
if (stack != null){
|
||||
luminance = Math.max(luminance, getLuminanceFromItemStack(stack));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.dynamiclights;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -78,4 +79,13 @@ public interface IDynamicLightSource {
|
||||
boolean angelica$updateDynamicLight(@NotNull IDynamicLightWorldRenderer renderer);
|
||||
|
||||
void angelica$scheduleTrackedChunksRebuild(@NotNull IDynamicLightWorldRenderer renderer);
|
||||
|
||||
/**
|
||||
* Returns the RGB luminance of the light source. Each channel 0-15, packed as {@code (r << 8) | (g << 4) | b}.
|
||||
* Default implementation returns white light at the scalar luminance level.
|
||||
*/
|
||||
default int angelica$getLuminanceRGB() {
|
||||
final int l = angelica$getLuminance();
|
||||
return BlockLightProvider.packRGB(l, l, l);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.mixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import jss.notfine.config.MCPatcherForgeConfig;
|
||||
@@ -214,6 +215,22 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
CELERITAS_COLORED_LIGHT(new MixinBuilder("Colored light infrastructure for celeritas light pipeline")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> {
|
||||
BlockLightProvider.freezeMixinConfig();
|
||||
return AngelicaConfig.enableCeleritas && BlockLightProvider.coloredLightEnabled();
|
||||
})
|
||||
.addClientMixins(
|
||||
"celeritas.light.MixinQuadLightData",
|
||||
"celeritas.light.MixinLightDataAccess",
|
||||
"celeritas.light.MixinLightDataCache",
|
||||
"celeritas.light.MixinAoFaceData",
|
||||
"celeritas.light.MixinSmoothLightPipeline",
|
||||
"celeritas.light.MixinFlatLightPipeline"
|
||||
)
|
||||
),
|
||||
|
||||
IRIS_SHADERS(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
@@ -698,7 +715,7 @@ public enum Mixins implements IMixins {
|
||||
private final MixinBuilder builder;
|
||||
|
||||
private static String[] addPrefix(String prefix, String... values) {
|
||||
List<String> list = new ArrayList<>(values.length);
|
||||
final List<String> list = new ArrayList<>(values.length);
|
||||
for (String s : values) {
|
||||
list.add(prefix + s);
|
||||
}
|
||||
|
||||
@@ -46,9 +46,12 @@ import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.client.IrisDebugScreenHandler;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiVideoSettings;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
@@ -164,6 +167,8 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onKeypress(TickEvent.ClientTickEvent event) {
|
||||
if (event.phase != TickEvent.Phase.END) return;
|
||||
|
||||
final boolean isPressed = glsmKeyBinding.getKeyCode() != 0 && GameSettings.isKeyDown(glsmKeyBinding);
|
||||
if (isPressed && !wasGLSMKeyPressed) {
|
||||
OpenGLDebugging.checkGLSM();
|
||||
@@ -177,7 +182,7 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
if (ModStatus.isLotrLoaded && AngelicaConfig.enableCeleritas && CompatConfig.fixLotr) {
|
||||
try {
|
||||
Class<?> lotrRendering = Class.forName("lotr.common.coremod.LOTRReplacedMethods$BlockRendering");
|
||||
final Class<?> lotrRendering = Class.forName("lotr.common.coremod.LOTRReplacedMethods$BlockRendering");
|
||||
ReflectionHelper.setPrivateValue(lotrRendering, null, new ConcurrentHashMap<>(), "naturalBlockClassTable");
|
||||
ReflectionHelper.setPrivateValue(lotrRendering, null, new ConcurrentHashMap<>(), "naturalBlockTable");
|
||||
ReflectionHelper.setPrivateValue(lotrRendering, null, new ConcurrentHashMap<>(), "cachedNaturalBlocks");
|
||||
@@ -192,9 +197,9 @@ public class ClientProxy extends CommonProxy {
|
||||
@SubscribeEvent
|
||||
public void onTick(TickEvent.ServerTickEvent event) {
|
||||
if (FMLCommonHandler.instance().getSide().isClient() && event.phase == TickEvent.Phase.END) {
|
||||
IntegratedServer srv = Minecraft.getMinecraft().getIntegratedServer();
|
||||
final IntegratedServer srv = Minecraft.getMinecraft().getIntegratedServer();
|
||||
if (srv != null) {
|
||||
long currentTickTime = srv.tickTimeArray[srv.getTickCounter() % 100];
|
||||
final long currentTickTime = srv.tickTimeArray[srv.getTickCounter() % 100];
|
||||
lastIntegratedTickTime = lastIntegratedTickTime * 0.8F + (float) currentTickTime / 1000000.0F * 0.2F;
|
||||
} else lastIntegratedTickTime = 0;
|
||||
}
|
||||
@@ -211,30 +216,30 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onRenderOverlay(RenderGameOverlayEvent.Text event) {
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (!mc.gameSettings.showDebugInfo || event.left.isEmpty()) return;
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
if (event.isCanceled() || !mc.gameSettings.showDebugInfo || event.left.isEmpty()) return;
|
||||
|
||||
NetHandlerPlayClient cl = mc.getNetHandler();
|
||||
final NetHandlerPlayClient cl = mc.getNetHandler();
|
||||
if (cl != null) {
|
||||
IntegratedServer srv = mc.getIntegratedServer();
|
||||
final IntegratedServer srv = mc.getIntegratedServer();
|
||||
|
||||
if (srv != null) {
|
||||
String s = String.format("Integrated server @ %.0f ms ticks", lastIntegratedTickTime);
|
||||
final String s = String.format("Integrated server @ %.0f ms ticks", lastIntegratedTickTime);
|
||||
event.left.add(Math.min(event.left.size(), 1), s);
|
||||
}
|
||||
}
|
||||
|
||||
if (AngelicaConfig.showBlockDebugInfo && mc.objectMouseOver != null && mc.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
|
||||
if (!event.right.isEmpty() && !Objects.firstNonNull(event.right.get(event.right.size() - 1), "").isEmpty()) event.right.add("");
|
||||
Block block = mc.theWorld.getBlock(mc.objectMouseOver.blockX, mc.objectMouseOver.blockY, mc.objectMouseOver.blockZ);
|
||||
int meta = mc.theWorld.getBlockMetadata(mc.objectMouseOver.blockX, mc.objectMouseOver.blockY, mc.objectMouseOver.blockZ);
|
||||
final Block block = mc.theWorld.getBlock(mc.objectMouseOver.blockX, mc.objectMouseOver.blockY, mc.objectMouseOver.blockZ);
|
||||
final int meta = mc.theWorld.getBlockMetadata(mc.objectMouseOver.blockX, mc.objectMouseOver.blockY, mc.objectMouseOver.blockZ);
|
||||
event.right.add(Block.blockRegistry.getNameForObject(block));
|
||||
event.right.add("meta: " + meta);
|
||||
}
|
||||
|
||||
if (DynamicLights.isEnabled()) {
|
||||
var builder = new StringBuilder("Dynamic Light Sources: ");
|
||||
DynamicLights dl = DynamicLights.get();
|
||||
final var builder = new StringBuilder("Dynamic Light Sources: ");
|
||||
final DynamicLights dl = DynamicLights.get();
|
||||
builder.append(dl.getLightSourcesCount()).append(" (U: ").append(dl.getLastUpdateCount()).append(')');
|
||||
|
||||
event.right.add(builder.toString());
|
||||
@@ -248,8 +253,8 @@ public class ClientProxy extends CommonProxy {
|
||||
.startsWith("y:") && Objects.firstNonNull(event.left.get(i + 2), "").startsWith("z:") && Objects.firstNonNull(event.left.get(i + 3), "")
|
||||
.startsWith("f:")) {
|
||||
hasReplacedXYZ = true;
|
||||
int heading = MathHelper.floor_double((double) (mc.thePlayer.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
|
||||
String heading_str = switch (heading) {
|
||||
final int heading = MathHelper.floor_double((double) (mc.thePlayer.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
|
||||
final String heading_str = switch (heading) {
|
||||
case 0 -> "Towards positive Z";
|
||||
case 1 -> "Towards negative X";
|
||||
case 2 -> "Towards negative Z";
|
||||
@@ -257,9 +262,9 @@ public class ClientProxy extends CommonProxy {
|
||||
default -> throw new RuntimeException();
|
||||
};
|
||||
event.left.set(i, String.format("XYZ: %.3f / %.5f / %.3f", mc.thePlayer.posX, mc.thePlayer.boundingBox.minY, mc.thePlayer.posZ));
|
||||
int bX = MathHelper.floor_double(mc.thePlayer.posX);
|
||||
int bY = MathHelper.floor_double(mc.thePlayer.boundingBox.minY);
|
||||
int bZ = MathHelper.floor_double(mc.thePlayer.posZ);
|
||||
final int bX = MathHelper.floor_double(mc.thePlayer.posX);
|
||||
final int bY = MathHelper.floor_double(mc.thePlayer.boundingBox.minY);
|
||||
final int bZ = MathHelper.floor_double(mc.thePlayer.posZ);
|
||||
event.left.set(i + 1, String.format("Block: %d %d %d [%d %d %d]", bX, bY, bZ, bX & 15, bY & 15, bZ & 15));
|
||||
event.left.set(i + 2, String.format("Chunk: %d %d %d", bX >> 4, bY >> 4, bZ >> 4));
|
||||
event.left.set(
|
||||
@@ -270,15 +275,14 @@ public class ClientProxy extends CommonProxy {
|
||||
MathHelper.wrapAngleTo180_float(mc.thePlayer.rotationYaw),
|
||||
MathHelper.wrapAngleTo180_float(mc.thePlayer.rotationPitch)));
|
||||
|
||||
Chunk chunk = this.mc.theWorld.getChunkFromBlockCoords(bX, bZ);
|
||||
event.left.set(
|
||||
i + 4, String.format(
|
||||
"lc: %d b: %s bl: %d sl: %d rl: %d",
|
||||
chunk.getTopFilledSegment() + 15,
|
||||
chunk.getBiomeGenForWorldCoords(bX & 15, bZ & 15, mc.theWorld.getWorldChunkManager()).biomeName,
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Block, bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15),
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Sky, bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15),
|
||||
chunk.getBlockLightValue(bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15, 0)));
|
||||
final Chunk chunk = this.mc.theWorld.getChunkFromBlockCoords(bX, bZ);
|
||||
event.left.set(i + 4, String.format(
|
||||
"lc: %d b: %s bl: %d sl: %d rl: %d",
|
||||
chunk.getTopFilledSegment() + 15,
|
||||
chunk.getBiomeGenForWorldCoords(bX & 15, bZ & 15, mc.theWorld.getWorldChunkManager()).biomeName,
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Block, bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15),
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Sky, bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15),
|
||||
chunk.getBlockLightValue(bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15, 0)));
|
||||
}
|
||||
}
|
||||
// Draw a frametime graph
|
||||
|
||||
+58
-5
@@ -1,6 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
|
||||
import com.gtnewhorizons.angelica.api.TintComputer;
|
||||
import com.gtnewhorizons.angelica.api.TintRegistry;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightSource;
|
||||
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
|
||||
@@ -17,8 +20,8 @@ import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.embeddedt.embeddium.api.util.ColorABGR;
|
||||
import org.embeddedt.embeddium.impl.model.light.LightPipeline;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
|
||||
import org.embeddedt.embeddium.impl.model.light.flat.FlatLightPipeline;
|
||||
import org.embeddedt.embeddium.impl.model.light.smooth.SmoothLightPipeline;
|
||||
@@ -33,7 +36,6 @@ import org.embeddedt.embeddium.impl.render.chunk.terrain.material.Material;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexEncoder;
|
||||
import org.embeddedt.embeddium.impl.util.QuadUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
@@ -56,7 +58,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
private final VertexArrayQuadView quadView;
|
||||
private boolean lightPipelineReady = false;
|
||||
private int originX, originY, originZ;
|
||||
private IBlockAccess blockAccess;
|
||||
private float cachedSkylightSubtracted;
|
||||
private final boolean hasColoredLight;
|
||||
|
||||
public AngelicaChunkBuildContext(RenderPassConfiguration<?> renderPassConfiguration, WorldClient world) {
|
||||
super(renderPassConfiguration);
|
||||
@@ -65,6 +68,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
this.smoothLightPipeline = new SmoothLightPipeline(lightDataCache, VanillaDiffuseProvider.INSTANCE, false);
|
||||
this.flatLightPipeline = new FlatLightPipeline(lightDataCache, VanillaDiffuseProvider.INSTANCE, false);
|
||||
this.quadView = new VertexArrayQuadView(vertices);
|
||||
this.hasColoredLight = quadLightData instanceof ExtQuadLightData;
|
||||
}
|
||||
|
||||
public void setupLightPipeline(int minBlockX, int minBlockY, int minBlockZ) {
|
||||
@@ -72,7 +76,6 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
}
|
||||
|
||||
public void setupLightPipeline(IBlockAccess blockAccess, int minBlockX, int minBlockY, int minBlockZ) {
|
||||
this.blockAccess = blockAccess;
|
||||
this.originX = minBlockX;
|
||||
this.originY = minBlockY;
|
||||
this.originZ = minBlockZ;
|
||||
@@ -81,6 +84,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
smoothLightPipeline.reset();
|
||||
flatLightPipeline.reset();
|
||||
lightPipelineReady = true;
|
||||
cachedSkylightSubtracted = Minecraft.getMinecraft().theWorld.skylightSubtracted;
|
||||
}
|
||||
|
||||
public void setupDynamicLights(int chunkOriginX, int chunkOriginY, int chunkOriginZ) {
|
||||
@@ -219,10 +223,13 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply RGB block light tint from provider
|
||||
applyBlockLightTint(worldX, worldY, worldZ, vertices);
|
||||
|
||||
final int faceBit = 1 << facing.ordinal();
|
||||
final Material correctMaterial;
|
||||
if ((facesAtBaseMaterial & faceBit) != 0) {
|
||||
// Face already has a non-demoted quad — skip selectMaterial entirely
|
||||
// Face already has a non-demoted quad, skip selectMaterial entirely
|
||||
correctMaterial = material;
|
||||
} else {
|
||||
correctMaterial = selectMaterial(material, sprite, isShaderPackOverride);
|
||||
@@ -239,4 +246,50 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
|
||||
builder.getVertexBuffer(facing).push(vertices, correctMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
private final float[] tintResult = new float[3];
|
||||
|
||||
private void applyBlockLightTint(int x, int y, int z, ChunkVertexEncoder.Vertex[] vertices) {
|
||||
if (!hasColoredLight) return;
|
||||
final ExtQuadLightData ext = (ExtQuadLightData) quadLightData;
|
||||
final boolean hasBlock = ext.angelica$isRGBValid();
|
||||
final boolean hasSky = ext.angelica$isSkyRGBValid();
|
||||
if (!hasBlock && !hasSky) return;
|
||||
if (hasBlock) ext.angelica$setRGBValid(false);
|
||||
if (hasSky) ext.angelica$setSkyRGBValid(false);
|
||||
|
||||
final float[] blockR = hasBlock ? ext.angelica$getR() : null;
|
||||
final float[] blockG = hasBlock ? ext.angelica$getG() : null;
|
||||
final float[] blockB = hasBlock ? ext.angelica$getB() : null;
|
||||
final float[] skyR = hasSky ? ext.angelica$getSkyR() : null;
|
||||
final float[] skyG = hasSky ? ext.angelica$getSkyG() : null;
|
||||
final float[] skyB = hasSky ? ext.angelica$getSkyB() : null;
|
||||
|
||||
final float sub = cachedSkylightSubtracted;
|
||||
final TintComputer tintComputer = TintRegistry.getCurrent();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final float br = hasBlock ? blockR[i] : 0;
|
||||
final float bg = hasBlock ? blockG[i] : 0;
|
||||
final float bb = hasBlock ? blockB[i] : 0;
|
||||
final float sr = hasSky ? Math.max(0, skyR[i] - sub) : 0;
|
||||
final float sg = hasSky ? Math.max(0, skyG[i] - sub) : 0;
|
||||
final float sb = hasSky ? Math.max(0, skyB[i] - sub) : 0;
|
||||
|
||||
// Fast-path: full white sky with no block light, no tint needed
|
||||
if (br == 0 && bg == 0 && bb == 0 && sr >= 15f && sg >= 15f && sb >= 15f) continue;
|
||||
|
||||
tintComputer.computeTint(br, bg, bb, sr, sg, sb, tintResult);
|
||||
final float rTint = tintResult[0];
|
||||
final float gTint = tintResult[1];
|
||||
final float bTint = tintResult[2];
|
||||
if (rTint >= 1.0f && gTint >= 1.0f && bTint >= 1.0f) continue;
|
||||
|
||||
final int color = vertices[i].color;
|
||||
final int vr = Math.min(255, (int) (ColorABGR.unpackRed(color) * rTint));
|
||||
final int vg = Math.min(255, (int) (ColorABGR.unpackGreen(color) * gTint));
|
||||
final int vb = Math.min(255, (int) (ColorABGR.unpackBlue(color) * bTint));
|
||||
vertices[i].color = ColorABGR.pack(vr, vg, vb, ColorABGR.unpackAlpha(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas.compat;
|
||||
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
|
||||
/**
|
||||
* Inlined data from package-private {@code AoNeighborInfo} -- edge face directions,
|
||||
* bilinear corner weight formulas, and depth formulas per face direction.
|
||||
*/
|
||||
public final class AoHelper {
|
||||
|
||||
private static final ModelQuadFacing[][] FACES = {
|
||||
{ ModelQuadFacing.NEG_Y, ModelQuadFacing.POS_Y, ModelQuadFacing.NEG_Z, ModelQuadFacing.POS_Z }, // POS_X
|
||||
{ ModelQuadFacing.POS_X, ModelQuadFacing.NEG_X, ModelQuadFacing.NEG_Z, ModelQuadFacing.POS_Z }, // POS_Y
|
||||
{ ModelQuadFacing.NEG_X, ModelQuadFacing.POS_X, ModelQuadFacing.NEG_Y, ModelQuadFacing.POS_Y }, // POS_Z
|
||||
{ ModelQuadFacing.POS_Y, ModelQuadFacing.NEG_Y, ModelQuadFacing.NEG_Z, ModelQuadFacing.POS_Z }, // NEG_X
|
||||
{ ModelQuadFacing.NEG_X, ModelQuadFacing.POS_X, ModelQuadFacing.NEG_Z, ModelQuadFacing.POS_Z }, // NEG_Y
|
||||
{ ModelQuadFacing.POS_Y, ModelQuadFacing.NEG_Y, ModelQuadFacing.POS_X, ModelQuadFacing.NEG_X }, // NEG_Z
|
||||
};
|
||||
|
||||
/**
|
||||
* Corner remap table derived from each AoNeighborInfo.mapCorners.
|
||||
* remap[outIdx] = inIdx.
|
||||
*/
|
||||
public static final int[][] CORNER_REMAP = {
|
||||
{ 3, 0, 1, 2 }, // POS_X
|
||||
{ 2, 3, 0, 1 }, // POS_Y
|
||||
{ 0, 1, 2, 3 }, // POS_Z
|
||||
{ 1, 2, 3, 0 }, // NEG_X
|
||||
{ 0, 1, 2, 3 }, // NEG_Y
|
||||
{ 1, 2, 3, 0 }, // NEG_Z
|
||||
};
|
||||
|
||||
private AoHelper() {}
|
||||
|
||||
public static ModelQuadFacing[] getFaces(ModelQuadFacing direction) {
|
||||
return FACES[direction.ordinal()];
|
||||
}
|
||||
|
||||
public static void calculateCornerWeights(ModelQuadFacing direction, float x, float y, float z, float[] out) {
|
||||
final float u, v;
|
||||
switch (direction) {
|
||||
case POS_X -> {
|
||||
u = z;
|
||||
v = 1.0f - y;
|
||||
}
|
||||
case POS_Y -> {
|
||||
u = z;
|
||||
v = x;
|
||||
}
|
||||
case POS_Z -> {
|
||||
u = y;
|
||||
v = 1.0f - x;
|
||||
}
|
||||
case NEG_X -> {
|
||||
u = z;
|
||||
v = y;
|
||||
}
|
||||
case NEG_Y -> {
|
||||
u = z;
|
||||
v = 1.0f - x;
|
||||
}
|
||||
case NEG_Z -> {
|
||||
u = 1.0f - x;
|
||||
v = y;
|
||||
}
|
||||
default -> throw new IllegalArgumentException();
|
||||
}
|
||||
out[0] = v * u;
|
||||
out[1] = v * (1.0f - u);
|
||||
out[2] = (1.0f - v) * (1.0f - u);
|
||||
out[3] = (1.0f - v) * u;
|
||||
}
|
||||
|
||||
public static float getDepth(ModelQuadFacing direction, float x, float y, float z) {
|
||||
return switch (direction) {
|
||||
case POS_X -> 1.0f - x;
|
||||
case POS_Y -> 1.0f - y;
|
||||
case POS_Z -> 1.0f - z;
|
||||
case NEG_X -> x;
|
||||
case NEG_Y -> y;
|
||||
case NEG_Z -> z;
|
||||
default -> throw new IllegalArgumentException();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas.world;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.SectionLightData;
|
||||
import com.gtnewhorizons.angelica.api.IBlockAccessExtended;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.ChunkSectionPos;
|
||||
@@ -84,11 +85,14 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
// Local Section->Light table
|
||||
private final NibbleArray[][] lightArrays;
|
||||
|
||||
// Per-section RGB light data (captured from BlockLightProvider on main thread)
|
||||
private final SectionLightData[] sectionLightData;
|
||||
|
||||
// Local section copies
|
||||
private ClonedChunkSection[] sections;
|
||||
|
||||
// Biome data for each chunk section
|
||||
private BiomeGenBase[][] biomeData;
|
||||
private final BiomeGenBase[][] biomeData;
|
||||
|
||||
// The starting point from which this slice captures blocks
|
||||
private int baseX, baseY, baseZ;
|
||||
@@ -158,6 +162,7 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
this.metadataArrays = new int[SECTION_TABLE_ARRAY_SIZE][];
|
||||
this.biomeData = new BiomeGenBase[SECTION_TABLE_ARRAY_SIZE][];
|
||||
this.lightArrays = new NibbleArray[SECTION_TABLE_ARRAY_SIZE][LIGHT_TYPES.length];
|
||||
this.sectionLightData = new SectionLightData[SECTION_TABLE_ARRAY_SIZE];
|
||||
|
||||
if (ModStatus.isFluidLoggedLoaded) {
|
||||
this.fluidArrays = new Fluid[SECTION_TABLE_ARRAY_SIZE][];
|
||||
@@ -220,6 +225,7 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
|
||||
this.lightArrays[idx][EnumSkyBlock.Block.ordinal()] = section.getLightArray(EnumSkyBlock.Block);
|
||||
this.lightArrays[idx][EnumSkyBlock.Sky.ordinal()] = section.getLightArray(EnumSkyBlock.Sky);
|
||||
this.sectionLightData[idx] = section.getSectionLightData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,6 +386,24 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
return lightArray.get(relX & 15, relY & 15, relZ & 15);
|
||||
}
|
||||
|
||||
public SectionLightData getSectionLightData(int x, int y, int z) {
|
||||
if (!blockBoxContains(this.volume, x, y, z)) {
|
||||
return null;
|
||||
}
|
||||
final int relX = x - this.baseX;
|
||||
final int relY = y - this.baseY;
|
||||
final int relZ = z - this.baseZ;
|
||||
return this.sectionLightData[getLocalSectionIndex(relX >> 4, relY >> 4, relZ >> 4)];
|
||||
}
|
||||
|
||||
public List<IDynamicLightSource> getChunkLightSources() {
|
||||
return chunkLightSources;
|
||||
}
|
||||
|
||||
public DynamicLights getDynamicLightsInstance() {
|
||||
return dynamicLightsInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return this.world;
|
||||
|
||||
+9
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas.world.cloned;
|
||||
|
||||
import com.falsepattern.endlessids.mixin.helpers.ChunkBiomeHook;
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.api.SectionLightData;
|
||||
import com.gtnewhorizons.angelica.compat.ExtendedBlockStorageExt;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.ChunkSectionPos;
|
||||
@@ -33,6 +35,7 @@ public class ClonedChunkSection {
|
||||
private ChunkSectionPos pos;
|
||||
private ExtendedBlockStorageExt data;
|
||||
private BiomeGenBase[] biomeData;
|
||||
private SectionLightData sectionLightData;
|
||||
private final Short2ObjectOpenHashMap<TileEntity> tileEntities;
|
||||
|
||||
private long lastUsedTimestamp = Long.MAX_VALUE;
|
||||
@@ -75,6 +78,8 @@ public class ClonedChunkSection {
|
||||
this.biomeData[(x & 15) | ((z & 15) << 4)] = world.getBiomeGenForCoords(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
this.sectionLightData = BlockLightProvider.getInstance().prepareSectionData(chunk, pos.y);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -127,6 +132,10 @@ public class ClonedChunkSection {
|
||||
return data.getBlocklightArray();
|
||||
}
|
||||
|
||||
public SectionLightData getSectionLightData() {
|
||||
return this.sectionLightData;
|
||||
}
|
||||
|
||||
public BiomeGenBase[] getBiomeData() {
|
||||
return this.biomeData;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@ public class SodiumGameOptions {
|
||||
|
||||
config.configPath = path;
|
||||
|
||||
if (config.advanced.streamingUploadStrategy == null) {
|
||||
config.advanced.streamingUploadStrategy = StreamingUploader.UploadStrategy.MAP_BUFFER_RANGE;
|
||||
}
|
||||
|
||||
// Clamp render-ahead to 0 if GL 3.2 fences aren't available
|
||||
if (GLStateManager.capabilities == null || !GLStateManager.capabilities.OpenGL32) {
|
||||
config.performance.cpuRenderAheadLimit = 0;
|
||||
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.light;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.api.ExtAoFaceData;
|
||||
import com.gtnewhorizons.angelica.api.ExtLightDataAccess;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.compat.AoHelper;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
|
||||
import org.embeddedt.embeddium.impl.model.light.smooth.AoFaceData;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = AoFaceData.class, remap = false)
|
||||
public class MixinAoFaceData implements ExtAoFaceData {
|
||||
|
||||
@Unique private final float[] angelica$rl = new float[4];
|
||||
@Unique private final float[] angelica$gl = new float[4];
|
||||
@Unique private final float[] angelica$bl = new float[4];
|
||||
@Unique private final float[] angelica$skyRl = new float[4];
|
||||
@Unique private final float[] angelica$skyGl = new float[4];
|
||||
@Unique private final float[] angelica$skyBl = new float[4];
|
||||
|
||||
@Override
|
||||
public float[] angelica$getRl() {
|
||||
return angelica$rl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getGl() {
|
||||
return angelica$gl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getBl() {
|
||||
return angelica$bl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float angelica$getBlendedR(float[] w) {
|
||||
return angelica$rl[0] * w[0] + angelica$rl[1] * w[1] + angelica$rl[2] * w[2] + angelica$rl[3] * w[3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float angelica$getBlendedG(float[] w) {
|
||||
return angelica$gl[0] * w[0] + angelica$gl[1] * w[1] + angelica$gl[2] * w[2] + angelica$gl[3] * w[3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float angelica$getBlendedB(float[] w) {
|
||||
return angelica$bl[0] * w[0] + angelica$bl[1] * w[1] + angelica$bl[2] * w[2] + angelica$bl[3] * w[3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getSkyRl() {
|
||||
return angelica$skyRl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getSkyGl() {
|
||||
return angelica$skyGl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getSkyBl() {
|
||||
return angelica$skyBl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float angelica$getBlendedSkyR(float[] w) {
|
||||
return angelica$skyRl[0] * w[0] + angelica$skyRl[1] * w[1] + angelica$skyRl[2] * w[2] + angelica$skyRl[3] * w[3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float angelica$getBlendedSkyG(float[] w) {
|
||||
return angelica$skyGl[0] * w[0] + angelica$skyGl[1] * w[1] + angelica$skyGl[2] * w[2] + angelica$skyGl[3] * w[3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float angelica$getBlendedSkyB(float[] w) {
|
||||
return angelica$skyBl[0] * w[0] + angelica$skyBl[1] * w[1] + angelica$skyBl[2] * w[2] + angelica$skyBl[3] * w[3];
|
||||
}
|
||||
|
||||
@Inject(method = "initLightData", at = @At("TAIL"))
|
||||
private void angelica$initRGBData(LightDataAccess cache, int x, int y, int z, ModelQuadFacing direction, boolean offset, CallbackInfo ci) {
|
||||
final ExtLightDataAccess rgbCache = (ExtLightDataAccess) cache;
|
||||
final boolean doBlock = rgbCache.angelica$isRGBEnabled();
|
||||
final boolean doSky = rgbCache.angelica$isSkyRGBEnabled();
|
||||
if (!doBlock && !doSky) return;
|
||||
|
||||
final int adjX, adjY, adjZ;
|
||||
|
||||
if (offset) {
|
||||
adjX = x + direction.getStepX();
|
||||
adjY = y + direction.getStepY();
|
||||
adjZ = z + direction.getStepZ();
|
||||
} else {
|
||||
adjX = x;
|
||||
adjY = y;
|
||||
adjZ = z;
|
||||
}
|
||||
|
||||
final ModelQuadFacing[] faces = AoHelper.getFaces(direction);
|
||||
|
||||
// Edge opacity
|
||||
final boolean e0op = LightDataAccess.unpackOP(cache.get(adjX, adjY, adjZ, faces[0]));
|
||||
final boolean e1op = LightDataAccess.unpackOP(cache.get(adjX, adjY, adjZ, faces[1]));
|
||||
final boolean e2op = LightDataAccess.unpackOP(cache.get(adjX, adjY, adjZ, faces[2]));
|
||||
final boolean e3op = LightDataAccess.unpackOP(cache.get(adjX, adjY, adjZ, faces[3]));
|
||||
|
||||
// Center
|
||||
final int adjWord = cache.get(adjX, adjY, adjZ);
|
||||
final boolean useFallback = offset && LightDataAccess.unpackFO(adjWord);
|
||||
final int caX = useFallback ? x : adjX;
|
||||
final int caY = useFallback ? y : adjY;
|
||||
final int caZ = useFallback ? z : adjZ;
|
||||
final int caIdx = rgbCache.angelica$getIndex(caX, caY, caZ);
|
||||
|
||||
// Edge world coords
|
||||
final int e0x = adjX + faces[0].getStepX(), e0y = adjY + faces[0].getStepY(), e0z = adjZ + faces[0].getStepZ();
|
||||
final int e1x = adjX + faces[1].getStepX(), e1y = adjY + faces[1].getStepY(), e1z = adjZ + faces[1].getStepZ();
|
||||
final int e2x = adjX + faces[2].getStepX(), e2y = adjY + faces[2].getStepY(), e2z = adjZ + faces[2].getStepZ();
|
||||
final int e3x = adjX + faces[3].getStepX(), e3y = adjY + faces[3].getStepY(), e3z = adjZ + faces[3].getStepZ();
|
||||
|
||||
// Edge indices
|
||||
final int e0Idx = rgbCache.angelica$getIndex(e0x, e0y, e0z);
|
||||
final int e1Idx = rgbCache.angelica$getIndex(e1x, e1y, e1z);
|
||||
final int e2Idx = rgbCache.angelica$getIndex(e2x, e2y, e2z);
|
||||
final int e3Idx = rgbCache.angelica$getIndex(e3x, e3y, e3z);
|
||||
|
||||
// Corner world coords + indices; only compute if not occluded
|
||||
final int c0x, c0y, c0z, c0Idx;
|
||||
if (e2op && e0op) { c0x = c0y = c0z = c0Idx = 0; }
|
||||
else {
|
||||
c0x = e0x + faces[2].getStepX(); c0y = e0y + faces[2].getStepY(); c0z = e0z + faces[2].getStepZ();
|
||||
c0Idx = rgbCache.angelica$getIndex(c0x, c0y, c0z);
|
||||
}
|
||||
|
||||
final int c1x, c1y, c1z, c1Idx;
|
||||
if (e3op && e0op) { c1x = c1y = c1z = c1Idx = 0; }
|
||||
else {
|
||||
c1x = e0x + faces[3].getStepX(); c1y = e0y + faces[3].getStepY(); c1z = e0z + faces[3].getStepZ();
|
||||
c1Idx = rgbCache.angelica$getIndex(c1x, c1y, c1z);
|
||||
}
|
||||
|
||||
final int c2x, c2y, c2z, c2Idx;
|
||||
if (e2op && e1op) { c2x = c2y = c2z = c2Idx = 0; }
|
||||
else {
|
||||
c2x = e1x + faces[2].getStepX(); c2y = e1y + faces[2].getStepY(); c2z = e1z + faces[2].getStepZ();
|
||||
c2Idx = rgbCache.angelica$getIndex(c2x, c2y, c2z);
|
||||
}
|
||||
|
||||
final int c3x, c3y, c3z, c3Idx;
|
||||
if (e3op && e1op) { c3x = c3y = c3z = c3Idx = 0; }
|
||||
else {
|
||||
c3x = e1x + faces[3].getStepX(); c3y = e1y + faces[3].getStepY(); c3z = e1z + faces[3].getStepZ();
|
||||
c3Idx = rgbCache.angelica$getIndex(c3x, c3y, c3z);
|
||||
}
|
||||
|
||||
final long caF = rgbCache.angelica$getFusedByIndex(caIdx, caX, caY, caZ);
|
||||
final long e0F = rgbCache.angelica$getFusedByIndex(e0Idx, e0x, e0y, e0z);
|
||||
final long e1F = rgbCache.angelica$getFusedByIndex(e1Idx, e1x, e1y, e1z);
|
||||
final long e2F = rgbCache.angelica$getFusedByIndex(e2Idx, e2x, e2y, e2z);
|
||||
final long e3F = rgbCache.angelica$getFusedByIndex(e3Idx, e3x, e3y, e3z);
|
||||
final long c0F = (e2op && e0op) ? e0F : rgbCache.angelica$getFusedByIndex(c0Idx, c0x, c0y, c0z);
|
||||
final long c1F = (e3op && e0op) ? e0F : rgbCache.angelica$getFusedByIndex(c1Idx, c1x, c1y, c1z);
|
||||
final long c2F = (e2op && e1op) ? e1F : rgbCache.angelica$getFusedByIndex(c2Idx, c2x, c2y, c2z);
|
||||
final long c3F = (e3op && e1op) ? e1F : rgbCache.angelica$getFusedByIndex(c3Idx, c3x, c3y, c3z);
|
||||
|
||||
if (doBlock) {
|
||||
final int caRGB = (int)(caF >>> 32);
|
||||
final int e0RGB = (int)(e0F >>> 32);
|
||||
final int e1RGB = (int)(e1F >>> 32);
|
||||
final int e2RGB = (int)(e2F >>> 32);
|
||||
final int e3RGB = (int)(e3F >>> 32);
|
||||
final int c0RGB = (int)(c0F >>> 32);
|
||||
final int c1RGB = (int)(c1F >>> 32);
|
||||
final int c2RGB = (int)(c2F >>> 32);
|
||||
final int c3RGB = (int)(c3F >>> 32);
|
||||
|
||||
final int caR = BlockLightProvider.unpackR(caRGB), caG = BlockLightProvider.unpackG(caRGB), caB = BlockLightProvider.unpackB(caRGB);
|
||||
final int e0R = BlockLightProvider.unpackR(e0RGB), e0G = BlockLightProvider.unpackG(e0RGB), e0B = BlockLightProvider.unpackB(e0RGB);
|
||||
final int e1R = BlockLightProvider.unpackR(e1RGB), e1G = BlockLightProvider.unpackG(e1RGB), e1B = BlockLightProvider.unpackB(e1RGB);
|
||||
final int e2R = BlockLightProvider.unpackR(e2RGB), e2G = BlockLightProvider.unpackG(e2RGB), e2B = BlockLightProvider.unpackB(e2RGB);
|
||||
final int e3R = BlockLightProvider.unpackR(e3RGB), e3G = BlockLightProvider.unpackG(e3RGB), e3B = BlockLightProvider.unpackB(e3RGB);
|
||||
final int c0R = BlockLightProvider.unpackR(c0RGB), c0G = BlockLightProvider.unpackG(c0RGB), c0B = BlockLightProvider.unpackB(c0RGB);
|
||||
final int c1R = BlockLightProvider.unpackR(c1RGB), c1G = BlockLightProvider.unpackG(c1RGB), c1B = BlockLightProvider.unpackB(c1RGB);
|
||||
final int c2R = BlockLightProvider.unpackR(c2RGB), c2G = BlockLightProvider.unpackG(c2RGB), c2B = BlockLightProvider.unpackB(c2RGB);
|
||||
final int c3R = BlockLightProvider.unpackR(c3RGB), c3G = BlockLightProvider.unpackG(c3RGB), c3B = BlockLightProvider.unpackB(c3RGB);
|
||||
|
||||
angelica$rl[0] = (e3R + e0R + c1R + caR) * 0.25f;
|
||||
angelica$gl[0] = (e3G + e0G + c1G + caG) * 0.25f;
|
||||
angelica$bl[0] = (e3B + e0B + c1B + caB) * 0.25f;
|
||||
|
||||
angelica$rl[1] = (e2R + e0R + c0R + caR) * 0.25f;
|
||||
angelica$gl[1] = (e2G + e0G + c0G + caG) * 0.25f;
|
||||
angelica$bl[1] = (e2B + e0B + c0B + caB) * 0.25f;
|
||||
|
||||
angelica$rl[2] = (e2R + e1R + c2R + caR) * 0.25f;
|
||||
angelica$gl[2] = (e2G + e1G + c2G + caG) * 0.25f;
|
||||
angelica$bl[2] = (e2B + e1B + c2B + caB) * 0.25f;
|
||||
|
||||
angelica$rl[3] = (e3R + e1R + c3R + caR) * 0.25f;
|
||||
angelica$gl[3] = (e3G + e1G + c3G + caG) * 0.25f;
|
||||
angelica$bl[3] = (e3B + e1B + c3B + caB) * 0.25f;
|
||||
}
|
||||
|
||||
if (doSky) {
|
||||
final int caSky = (int)(caF & 0xFFFFFFFFL);
|
||||
final int e0Sky = (int)(e0F & 0xFFFFFFFFL);
|
||||
final int e1Sky = (int)(e1F & 0xFFFFFFFFL);
|
||||
final int e2Sky = (int)(e2F & 0xFFFFFFFFL);
|
||||
final int e3Sky = (int)(e3F & 0xFFFFFFFFL);
|
||||
final int c0Sky = (int)(c0F & 0xFFFFFFFFL);
|
||||
final int c1Sky = (int)(c1F & 0xFFFFFFFFL);
|
||||
final int c2Sky = (int)(c2F & 0xFFFFFFFFL);
|
||||
final int c3Sky = (int)(c3F & 0xFFFFFFFFL);
|
||||
|
||||
final int caR = BlockLightProvider.unpackR(caSky), caG = BlockLightProvider.unpackG(caSky), caB = BlockLightProvider.unpackB(caSky);
|
||||
final int e0R = BlockLightProvider.unpackR(e0Sky), e0G = BlockLightProvider.unpackG(e0Sky), e0B = BlockLightProvider.unpackB(e0Sky);
|
||||
final int e1R = BlockLightProvider.unpackR(e1Sky), e1G = BlockLightProvider.unpackG(e1Sky), e1B = BlockLightProvider.unpackB(e1Sky);
|
||||
final int e2R = BlockLightProvider.unpackR(e2Sky), e2G = BlockLightProvider.unpackG(e2Sky), e2B = BlockLightProvider.unpackB(e2Sky);
|
||||
final int e3R = BlockLightProvider.unpackR(e3Sky), e3G = BlockLightProvider.unpackG(e3Sky), e3B = BlockLightProvider.unpackB(e3Sky);
|
||||
final int c0R = BlockLightProvider.unpackR(c0Sky), c0G = BlockLightProvider.unpackG(c0Sky), c0B = BlockLightProvider.unpackB(c0Sky);
|
||||
final int c1R = BlockLightProvider.unpackR(c1Sky), c1G = BlockLightProvider.unpackG(c1Sky), c1B = BlockLightProvider.unpackB(c1Sky);
|
||||
final int c2R = BlockLightProvider.unpackR(c2Sky), c2G = BlockLightProvider.unpackG(c2Sky), c2B = BlockLightProvider.unpackB(c2Sky);
|
||||
final int c3R = BlockLightProvider.unpackR(c3Sky), c3G = BlockLightProvider.unpackG(c3Sky), c3B = BlockLightProvider.unpackB(c3Sky);
|
||||
|
||||
angelica$skyRl[0] = (e3R + e0R + c1R + caR) * 0.25f;
|
||||
angelica$skyGl[0] = (e3G + e0G + c1G + caG) * 0.25f;
|
||||
angelica$skyBl[0] = (e3B + e0B + c1B + caB) * 0.25f;
|
||||
|
||||
angelica$skyRl[1] = (e2R + e0R + c0R + caR) * 0.25f;
|
||||
angelica$skyGl[1] = (e2G + e0G + c0G + caG) * 0.25f;
|
||||
angelica$skyBl[1] = (e2B + e0B + c0B + caB) * 0.25f;
|
||||
|
||||
angelica$skyRl[2] = (e2R + e1R + c2R + caR) * 0.25f;
|
||||
angelica$skyGl[2] = (e2G + e1G + c2G + caG) * 0.25f;
|
||||
angelica$skyBl[2] = (e2B + e1B + c2B + caB) * 0.25f;
|
||||
|
||||
angelica$skyRl[3] = (e3R + e1R + c3R + caR) * 0.25f;
|
||||
angelica$skyGl[3] = (e3G + e1G + c3G + caG) * 0.25f;
|
||||
angelica$skyBl[3] = (e3B + e1B + c3B + caB) * 0.25f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.light;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.api.ExtLightDataAccess;
|
||||
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
|
||||
import org.embeddedt.embeddium.impl.model.light.flat.FlatLightPipeline;
|
||||
import org.embeddedt.embeddium.impl.model.quad.ModelQuadView;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = FlatLightPipeline.class, remap = false)
|
||||
public class MixinFlatLightPipeline {
|
||||
|
||||
@Shadow @Final private LightDataAccess lightCache;
|
||||
|
||||
@Inject(method = "calculate", at = @At("TAIL"))
|
||||
private void angelica$populateRGBFlat(ModelQuadView quad, int x, int y, int z, QuadLightData out, ModelQuadFacing cullFace, ModelQuadFacing lightFace, boolean shade, boolean applyAoDepthBlending, CallbackInfo ci) {
|
||||
final ExtLightDataAccess rgbCache = (ExtLightDataAccess) this.lightCache;
|
||||
final ExtQuadLightData outExt = (ExtQuadLightData) out;
|
||||
|
||||
final boolean blockEnabled = rgbCache.angelica$isRGBEnabled();
|
||||
final boolean skyEnabled = rgbCache.angelica$isSkyRGBEnabled();
|
||||
|
||||
if (!blockEnabled && !skyEnabled) {
|
||||
outExt.angelica$setRGBValid(false);
|
||||
outExt.angelica$setSkyRGBValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final int lx, ly, lz;
|
||||
if (cullFace.isDirection()) {
|
||||
lx = x + cullFace.getStepX();
|
||||
ly = y + cullFace.getStepY();
|
||||
lz = z + cullFace.getStepZ();
|
||||
} else {
|
||||
final int flags = quad.getFlags();
|
||||
if ((flags & ModelQuadFlags.IS_ALIGNED) != 0 || ((flags & ModelQuadFlags.IS_PARALLEL) != 0 && LightDataAccess.unpackFC(this.lightCache.get(x, y, z)))) {
|
||||
lx = x + lightFace.getStepX();
|
||||
ly = y + lightFace.getStepY();
|
||||
lz = z + lightFace.getStepZ();
|
||||
} else {
|
||||
lx = x;
|
||||
ly = y;
|
||||
lz = z;
|
||||
}
|
||||
}
|
||||
|
||||
// Block RGB
|
||||
if (blockEnabled) {
|
||||
final int rgb = rgbCache.angelica$getRGB(lx, ly, lz);
|
||||
final float r = BlockLightProvider.unpackR(rgb), g = BlockLightProvider.unpackG(rgb), b = BlockLightProvider.unpackB(rgb);
|
||||
final float[] ra = outExt.angelica$getR(), ga = outExt.angelica$getG(), ba = outExt.angelica$getB();
|
||||
ra[0] = ra[1] = ra[2] = ra[3] = r;
|
||||
ga[0] = ga[1] = ga[2] = ga[3] = g;
|
||||
ba[0] = ba[1] = ba[2] = ba[3] = b;
|
||||
outExt.angelica$setRGBValid(true);
|
||||
} else {
|
||||
outExt.angelica$setRGBValid(false);
|
||||
}
|
||||
|
||||
// Sky RGB
|
||||
if (skyEnabled) {
|
||||
final int skyRgb = rgbCache.angelica$getSkyRGB(lx, ly, lz);
|
||||
final float r = BlockLightProvider.unpackR(skyRgb), g = BlockLightProvider.unpackG(skyRgb), b = BlockLightProvider.unpackB(skyRgb);
|
||||
final float[] ra = outExt.angelica$getSkyR(), ga = outExt.angelica$getSkyG(), ba = outExt.angelica$getSkyB();
|
||||
ra[0] = ra[1] = ra[2] = ra[3] = r;
|
||||
ga[0] = ga[1] = ga[2] = ga[3] = g;
|
||||
ba[0] = ba[1] = ba[2] = ba[3] = b;
|
||||
outExt.angelica$setSkyRGBValid(true);
|
||||
} else {
|
||||
outExt.angelica$setSkyRGBValid(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.light;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.api.ExtLightDataAccess;
|
||||
import com.gtnewhorizons.angelica.api.ExtLightDataCache;
|
||||
import com.gtnewhorizons.angelica.api.SectionLightData;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Mixin(value = LightDataAccess.class, remap = false)
|
||||
public abstract class MixinLightDataAccess implements ExtLightDataAccess {
|
||||
|
||||
@Unique private static final int ANGELICA$CACHE_SIZE = 20 * 20 * 20;
|
||||
@Unique private static final int ANGELICA$SENTINEL = 0x1000;
|
||||
|
||||
@Shadow
|
||||
private int index(int x, int y, int z) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/** Fused cache: upper 32 bits = block RGB word, lower 32 bits = sky RGB word. Zero = uncached. */
|
||||
@Unique private long[] angelica$fusedLight;
|
||||
|
||||
@Unique private boolean angelica$rgbEnabled;
|
||||
|
||||
@Unique private boolean angelica$skyRgbEnabled;
|
||||
|
||||
@Unique private BlockLightProvider angelica$provider;
|
||||
|
||||
@Unique private IBlockAccess angelica$blockAccess;
|
||||
|
||||
@Inject(method = "reset", at = @At("TAIL"))
|
||||
private void angelica$resetRGB(int minBlockX, int minBlockY, int minBlockZ, CallbackInfo ci) {
|
||||
angelica$rgbEnabled = BlockLightProvider.isRegistered();
|
||||
angelica$provider = BlockLightProvider.getInstance();
|
||||
angelica$blockAccess = ((ExtLightDataCache) this).angelica$getBlockAccess();
|
||||
angelica$skyRgbEnabled = angelica$rgbEnabled;
|
||||
if (angelica$rgbEnabled) {
|
||||
if (angelica$fusedLight == null) {
|
||||
angelica$fusedLight = new long[ANGELICA$CACHE_SIZE];
|
||||
} else {
|
||||
Arrays.fill(angelica$fusedLight, 0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getRGB(int x, int y, int z) {
|
||||
if (!angelica$rgbEnabled) return 0;
|
||||
final int idx = this.index(x, y, z);
|
||||
return angelica$getBlockFromFused(idx, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getRGB(int x, int y, int z, ModelQuadFacing dir) {
|
||||
return angelica$getRGB(
|
||||
x + dir.getStepX(),
|
||||
y + dir.getStepY(),
|
||||
z + dir.getStepZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getRGB(int x, int y, int z, ModelQuadFacing d1, ModelQuadFacing d2) {
|
||||
return angelica$getRGB(
|
||||
x + d1.getStepX() + d2.getStepX(),
|
||||
y + d1.getStepY() + d2.getStepY(),
|
||||
z + d1.getStepZ() + d2.getStepZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean angelica$isRGBEnabled() {
|
||||
return angelica$rgbEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getSkyRGB(int x, int y, int z) {
|
||||
if (!angelica$skyRgbEnabled) return 0;
|
||||
final int idx = this.index(x, y, z);
|
||||
return angelica$getSkyFromFused(idx, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getSkyRGB(int x, int y, int z, ModelQuadFacing dir) {
|
||||
return angelica$getSkyRGB(
|
||||
x + dir.getStepX(),
|
||||
y + dir.getStepY(),
|
||||
z + dir.getStepZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getSkyRGB(int x, int y, int z, ModelQuadFacing d1, ModelQuadFacing d2) {
|
||||
return angelica$getSkyRGB(
|
||||
x + d1.getStepX() + d2.getStepX(),
|
||||
y + d1.getStepY() + d2.getStepY(),
|
||||
z + d1.getStepZ() + d2.getStepZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean angelica$isSkyRGBEnabled() {
|
||||
return angelica$skyRgbEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getIndex(int x, int y, int z) {
|
||||
return this.index(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getRGBByIndex(int idx, int x, int y, int z) {
|
||||
if (!angelica$rgbEnabled) return 0;
|
||||
return angelica$getBlockFromFused(idx, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$getSkyRGBByIndex(int idx, int x, int y, int z) {
|
||||
if (!angelica$skyRgbEnabled) return 0;
|
||||
return angelica$getSkyFromFused(idx, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long angelica$getFusedByIndex(int idx, int x, int y, int z) {
|
||||
long fused = angelica$fusedLight[idx];
|
||||
if (fused == 0L) {
|
||||
fused = angelica$fusedLight[idx] = angelica$computeFused(x, y, z);
|
||||
}
|
||||
return fused;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private int angelica$getBlockFromFused(int idx, int x, int y, int z) {
|
||||
long fused = angelica$fusedLight[idx];
|
||||
if (fused == 0L) {
|
||||
fused = angelica$fusedLight[idx] = angelica$computeFused(x, y, z);
|
||||
}
|
||||
return (int)(fused >>> 32);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private int angelica$getSkyFromFused(int idx, int x, int y, int z) {
|
||||
long fused = angelica$fusedLight[idx];
|
||||
if (fused == 0L) {
|
||||
fused = angelica$fusedLight[idx] = angelica$computeFused(x, y, z);
|
||||
}
|
||||
return (int)(fused & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private long angelica$computeFused(int x, int y, int z) {
|
||||
final IBlockAccess blockAccess = angelica$blockAccess;
|
||||
|
||||
if (blockAccess instanceof WorldSlice ws) {
|
||||
int blockRGB = 0xFFFF; // sentinel: no data
|
||||
int skyRGB = 0xFFFF;
|
||||
|
||||
final SectionLightData data = ws.getSectionLightData(x, y, z);
|
||||
if (data != null) {
|
||||
final long fused = data.getRGBAndSkyRGB(x & 15, y & 15, z & 15);
|
||||
blockRGB = (int)(fused >>> 16) & 0xFFFF;
|
||||
skyRGB = (int)fused & 0xFFFF;
|
||||
}
|
||||
|
||||
// Overlay dynamic light RGB (thread-safe: uses pre-filtered chunk-local sources)
|
||||
final var dlSources = ws.getChunkLightSources();
|
||||
if (dlSources != null && !dlSources.isEmpty()) {
|
||||
final int dlRGB = ws.getDynamicLightsInstance()
|
||||
.getDynamicLightRGBFromSources(x + 0.5, y + 0.5, z + 0.5, dlSources);
|
||||
if (dlRGB != 0) {
|
||||
blockRGB = angelica$mergeMaxRGB(blockRGB, dlRGB);
|
||||
}
|
||||
}
|
||||
|
||||
final long blockWord = (blockRGB == 0xFFFF) ? ANGELICA$SENTINEL : (blockRGB | ANGELICA$SENTINEL);
|
||||
final long skyWord = (skyRGB == 0xFFFF) ? ANGELICA$SENTINEL : (skyRGB | ANGELICA$SENTINEL);
|
||||
return (blockWord << 32) | (skyWord & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
final BlockLightProvider provider = angelica$provider;
|
||||
int brgb = provider.getBlockLightRGB(blockAccess, x, y, z);
|
||||
final int srgb = provider.getSkyLightRGB(blockAccess, x, y, z);
|
||||
|
||||
if (DynamicLights.isEnabled()) {
|
||||
final int dlRGB = DynamicLights.get().getDynamicLightRGB(x + 0.5, y + 0.5, z + 0.5);
|
||||
if (dlRGB != 0) {
|
||||
brgb = angelica$mergeMaxRGB(brgb, dlRGB);
|
||||
}
|
||||
}
|
||||
|
||||
final long blockWord = (brgb == -1) ? ANGELICA$SENTINEL : (brgb | ANGELICA$SENTINEL);
|
||||
final long skyWord = (srgb == -1) ? ANGELICA$SENTINEL : (srgb | ANGELICA$SENTINEL);
|
||||
return (blockWord << 32) | (skyWord & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
/** Max-per-channel merge. Treats sentinel (0xFFFF or -1) as (0,0,0). */
|
||||
@Unique
|
||||
private static int angelica$mergeMaxRGB(int staticRGB, int dynamicRGB) {
|
||||
final int sR = (staticRGB == 0xFFFF || staticRGB == -1) ? 0 : BlockLightProvider.unpackR(staticRGB);
|
||||
final int sG = (staticRGB == 0xFFFF || staticRGB == -1) ? 0 : BlockLightProvider.unpackG(staticRGB);
|
||||
final int sB = (staticRGB == 0xFFFF || staticRGB == -1) ? 0 : BlockLightProvider.unpackB(staticRGB);
|
||||
return BlockLightProvider.packRGB(
|
||||
Math.max(sR, BlockLightProvider.unpackR(dynamicRGB)),
|
||||
Math.max(sG, BlockLightProvider.unpackG(dynamicRGB)),
|
||||
Math.max(sB, BlockLightProvider.unpackB(dynamicRGB)));
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.light;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.ExtLightDataCache;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.light.LightDataCache;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(value = LightDataCache.class, remap = false)
|
||||
public abstract class MixinLightDataCache implements ExtLightDataCache {
|
||||
|
||||
@Shadow private IBlockAccess world;
|
||||
|
||||
@Override
|
||||
public IBlockAccess angelica$getBlockAccess() {
|
||||
return this.world;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.light;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(value = QuadLightData.class, remap = false)
|
||||
public class MixinQuadLightData implements ExtQuadLightData {
|
||||
|
||||
@Unique private final float[] angelica$r = new float[4];
|
||||
@Unique private final float[] angelica$g = new float[4];
|
||||
@Unique private final float[] angelica$b = new float[4];
|
||||
@Unique private boolean angelica$rgbValid;
|
||||
@Unique private final float[] angelica$skyR = new float[4];
|
||||
@Unique private final float[] angelica$skyG = new float[4];
|
||||
@Unique private final float[] angelica$skyB = new float[4];
|
||||
@Unique private boolean angelica$skyRGBValid;
|
||||
|
||||
@Override
|
||||
public float[] angelica$getR() {
|
||||
return angelica$r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getG() {
|
||||
return angelica$g;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getB() {
|
||||
return angelica$b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean angelica$isRGBValid() {
|
||||
return angelica$rgbValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$setRGBValid(boolean valid) {
|
||||
this.angelica$rgbValid = valid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getSkyR() {
|
||||
return angelica$skyR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getSkyG() {
|
||||
return angelica$skyG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] angelica$getSkyB() {
|
||||
return angelica$skyB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean angelica$isSkyRGBValid() {
|
||||
return angelica$skyRGBValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$setSkyRGBValid(boolean valid) {
|
||||
this.angelica$skyRGBValid = valid;
|
||||
}
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.light;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.ExtAoFaceData;
|
||||
import com.gtnewhorizons.angelica.api.ExtLightDataAccess;
|
||||
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.compat.AoHelper;
|
||||
import org.embeddedt.embeddium.api.util.NormI8;
|
||||
import org.embeddedt.embeddium.impl.common.util.MathUtil;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
|
||||
import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
|
||||
import org.embeddedt.embeddium.impl.model.light.smooth.AoFaceData;
|
||||
import org.embeddedt.embeddium.impl.model.light.smooth.SmoothLightPipeline;
|
||||
import org.embeddedt.embeddium.impl.model.quad.ModelQuadView;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = SmoothLightPipeline.class, remap = false)
|
||||
public abstract class MixinSmoothLightPipeline {
|
||||
|
||||
@Shadow @Final private LightDataAccess lightCache;
|
||||
@Shadow @Final private float[] weights;
|
||||
@Shadow @Final private AoFaceData[] cachedFaceData;
|
||||
|
||||
@Unique private float angelica$lastR;
|
||||
@Unique private float angelica$lastG;
|
||||
@Unique private float angelica$lastB;
|
||||
|
||||
@Unique private float angelica$lastSkyR;
|
||||
@Unique private float angelica$lastSkyG;
|
||||
@Unique private float angelica$lastSkyB;
|
||||
|
||||
@Unique
|
||||
private ExtAoFaceData angelica$getFaceData(ModelQuadFacing face, boolean offset) {
|
||||
final int idx = offset ? face.ordinal() : face.ordinal() + 6;
|
||||
return (ExtAoFaceData) cachedFaceData[idx];
|
||||
}
|
||||
|
||||
@Inject(method = "calculate", at = @At("TAIL"))
|
||||
private void angelica$populateRGB(ModelQuadView quad, int x, int y, int z, QuadLightData out, ModelQuadFacing cullFace, ModelQuadFacing lightFace, boolean shade, boolean applyAoDepthBlending, CallbackInfo ci) {
|
||||
final ExtLightDataAccess rgbCache = (ExtLightDataAccess) lightCache;
|
||||
final ExtQuadLightData outExt = (ExtQuadLightData) out;
|
||||
final boolean doBlock = rgbCache.angelica$isRGBEnabled();
|
||||
final boolean doSky = rgbCache.angelica$isSkyRGBEnabled();
|
||||
|
||||
if (!doBlock && !doSky) {
|
||||
outExt.angelica$setRGBValid(false);
|
||||
outExt.angelica$setSkyRGBValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final int flags = quad.getFlags();
|
||||
final boolean isAligned = (flags & ModelQuadFlags.IS_ALIGNED) != 0;
|
||||
final boolean isParallel = (flags & ModelQuadFlags.IS_PARALLEL) != 0;
|
||||
final boolean isPartial = (flags & ModelQuadFlags.IS_PARTIAL) != 0;
|
||||
final boolean fullCube = isParallel && LightDataAccess.unpackFC(this.lightCache.get(x, y, z));
|
||||
|
||||
if (isAligned || fullCube) {
|
||||
if (!isPartial) {
|
||||
angelica$alignedFullFace(lightFace, outExt, doBlock, doSky);
|
||||
} else {
|
||||
angelica$perVertexFace(quad, lightFace, outExt, false, true, doBlock, doSky);
|
||||
}
|
||||
} else {
|
||||
if ((flags & ModelQuadFlags.IS_VANILLA_SHADED) == 0
|
||||
&& quad.getNormalFace() == ModelQuadFacing.UNASSIGNED) {
|
||||
angelica$irregularFace(quad, outExt, applyAoDepthBlending, doBlock, doSky);
|
||||
} else {
|
||||
angelica$perVertexFace(quad, lightFace, outExt, applyAoDepthBlending, false, doBlock, doSky);
|
||||
}
|
||||
}
|
||||
|
||||
outExt.angelica$setRGBValid(doBlock);
|
||||
outExt.angelica$setSkyRGBValid(doSky);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$alignedFullFace(ModelQuadFacing dir, ExtQuadLightData out, boolean doBlock, boolean doSky) {
|
||||
final ExtAoFaceData ext = angelica$getFaceData(dir, true);
|
||||
final int[] remap = AoHelper.CORNER_REMAP[dir.ordinal()];
|
||||
|
||||
if (doBlock) {
|
||||
final float[] rl = ext.angelica$getRl(), gl = ext.angelica$getGl(), bbl = ext.angelica$getBl();
|
||||
final float[] r = out.angelica$getR(), g = out.angelica$getG(), b = out.angelica$getB();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
r[i] = rl[remap[i]];
|
||||
g[i] = gl[remap[i]];
|
||||
b[i] = bbl[remap[i]];
|
||||
}
|
||||
}
|
||||
if (doSky) {
|
||||
final float[] rl = ext.angelica$getSkyRl(), gl = ext.angelica$getSkyGl(), bbl = ext.angelica$getSkyBl();
|
||||
final float[] r = out.angelica$getSkyR(), g = out.angelica$getSkyG(), b = out.angelica$getSkyB();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
r[i] = rl[remap[i]];
|
||||
g[i] = gl[remap[i]];
|
||||
b[i] = bbl[remap[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$perVertexFace(ModelQuadView quad, ModelQuadFacing dir, ExtQuadLightData out,
|
||||
boolean useDepthBlending, boolean alignedPartial, boolean doBlock, boolean doSky) {
|
||||
final float[] br = doBlock ? out.angelica$getR() : null;
|
||||
final float[] bg = doBlock ? out.angelica$getG() : null;
|
||||
final float[] bb = doBlock ? out.angelica$getB() : null;
|
||||
final float[] sr = doSky ? out.angelica$getSkyR() : null;
|
||||
final float[] sg = doSky ? out.angelica$getSkyG() : null;
|
||||
final float[] sb = doSky ? out.angelica$getSkyB() : null;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final float cx = angelica$clamp(quad.getX(i));
|
||||
final float cy = angelica$clamp(quad.getY(i));
|
||||
final float cz = angelica$clamp(quad.getZ(i));
|
||||
|
||||
AoHelper.calculateCornerWeights(dir, cx, cy, cz, this.weights);
|
||||
final float depth = AoHelper.getDepth(dir, cx, cy, cz);
|
||||
|
||||
if (useDepthBlending) {
|
||||
angelica$blendInset(dir, depth, 1.0f - depth, this.weights, doBlock, doSky);
|
||||
} else {
|
||||
angelica$blendAligned(dir, this.weights,
|
||||
alignedPartial || MathUtil.roughlyEqual(depth, 0.0f), doBlock, doSky);
|
||||
}
|
||||
|
||||
if (doBlock) {
|
||||
br[i] = this.angelica$lastR;
|
||||
bg[i] = this.angelica$lastG;
|
||||
bb[i] = this.angelica$lastB;
|
||||
}
|
||||
if (doSky) {
|
||||
sr[i] = this.angelica$lastSkyR;
|
||||
sg[i] = this.angelica$lastSkyG;
|
||||
sb[i] = this.angelica$lastSkyB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$irregularFace(ModelQuadView quad, ExtQuadLightData out,
|
||||
boolean applyAoDepthBlending, boolean doBlock, boolean doSky) {
|
||||
final float[] br = doBlock ? out.angelica$getR() : null;
|
||||
final float[] bg = doBlock ? out.angelica$getG() : null;
|
||||
final float[] bb = doBlock ? out.angelica$getB() : null;
|
||||
final float[] sr = doSky ? out.angelica$getSkyR() : null;
|
||||
final float[] sg = doSky ? out.angelica$getSkyG() : null;
|
||||
final float[] sb = doSky ? out.angelica$getSkyB() : null;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final float cx = angelica$clamp(quad.getX(i));
|
||||
final float cy = angelica$clamp(quad.getY(i));
|
||||
final float cz = angelica$clamp(quad.getZ(i));
|
||||
|
||||
int normal = quad.getForgeNormal(i);
|
||||
if (normal == 0) {
|
||||
normal = quad.getComputedFaceNormal();
|
||||
}
|
||||
|
||||
float bWeightedR = 0, bWeightedG = 0, bWeightedB = 0;
|
||||
float bMaxR = 0, bMaxG = 0, bMaxB = 0;
|
||||
float sWeightedR = 0, sWeightedG = 0, sWeightedB = 0;
|
||||
float sMaxR = 0, sMaxG = 0, sMaxB = 0;
|
||||
|
||||
for (int axis = 0; axis < 3; axis++) {
|
||||
final float projectedNormal = NormI8.unpackX(normal >> (axis * 8));
|
||||
if (projectedNormal == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ModelQuadFacing dir = ModelQuadFacing.AXES[axis].getFacing(projectedNormal > 0);
|
||||
|
||||
AoHelper.calculateCornerWeights(dir, cx, cy, cz, this.weights);
|
||||
final float depth = AoHelper.getDepth(dir, cx, cy, cz);
|
||||
|
||||
if (applyAoDepthBlending) {
|
||||
angelica$blendInset(dir, depth, 1.0f - depth, this.weights, doBlock, doSky);
|
||||
} else {
|
||||
angelica$blendAligned(dir, this.weights,
|
||||
MathUtil.roughlyEqual(depth, 0.0f), doBlock, doSky);
|
||||
}
|
||||
|
||||
final float combineWeight = projectedNormal * projectedNormal;
|
||||
|
||||
if (doBlock) {
|
||||
bWeightedR += this.angelica$lastR * combineWeight;
|
||||
bWeightedG += this.angelica$lastG * combineWeight;
|
||||
bWeightedB += this.angelica$lastB * combineWeight;
|
||||
bMaxR = Math.max(this.angelica$lastR, bMaxR);
|
||||
bMaxG = Math.max(this.angelica$lastG, bMaxG);
|
||||
bMaxB = Math.max(this.angelica$lastB, bMaxB);
|
||||
}
|
||||
if (doSky) {
|
||||
sWeightedR += this.angelica$lastSkyR * combineWeight;
|
||||
sWeightedG += this.angelica$lastSkyG * combineWeight;
|
||||
sWeightedB += this.angelica$lastSkyB * combineWeight;
|
||||
sMaxR = Math.max(this.angelica$lastSkyR, sMaxR);
|
||||
sMaxG = Math.max(this.angelica$lastSkyG, sMaxG);
|
||||
sMaxB = Math.max(this.angelica$lastSkyB, sMaxB);
|
||||
}
|
||||
}
|
||||
|
||||
if (doBlock) {
|
||||
br[i] = bWeightedR * 0.75f + bMaxR * 0.25f;
|
||||
bg[i] = bWeightedG * 0.75f + bMaxG * 0.25f;
|
||||
bb[i] = bWeightedB * 0.75f + bMaxB * 0.25f;
|
||||
}
|
||||
if (doSky) {
|
||||
sr[i] = sWeightedR * 0.75f + sMaxR * 0.25f;
|
||||
sg[i] = sWeightedG * 0.75f + sMaxG * 0.25f;
|
||||
sb[i] = sWeightedB * 0.75f + sMaxB * 0.25f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$blendAligned(ModelQuadFacing dir, float[] w, boolean offset, boolean doBlock, boolean doSky) {
|
||||
final ExtAoFaceData ext = angelica$getFaceData(dir, offset);
|
||||
if (doBlock) {
|
||||
this.angelica$lastR = ext.angelica$getBlendedR(w);
|
||||
this.angelica$lastG = ext.angelica$getBlendedG(w);
|
||||
this.angelica$lastB = ext.angelica$getBlendedB(w);
|
||||
}
|
||||
if (doSky) {
|
||||
this.angelica$lastSkyR = ext.angelica$getBlendedSkyR(w);
|
||||
this.angelica$lastSkyG = ext.angelica$getBlendedSkyG(w);
|
||||
this.angelica$lastSkyB = ext.angelica$getBlendedSkyB(w);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$blendInset(ModelQuadFacing dir, float n1d, float n2d, float[] w, boolean doBlock, boolean doSky) {
|
||||
if (MathUtil.roughlyEqual(n1d, 0.0f)) {
|
||||
angelica$blendAligned(dir, w, true, doBlock, doSky);
|
||||
return;
|
||||
}
|
||||
if (MathUtil.roughlyEqual(n1d, 1.0f)) {
|
||||
angelica$blendAligned(dir, w, false, doBlock, doSky);
|
||||
return;
|
||||
}
|
||||
|
||||
final ExtAoFaceData n1e = angelica$getFaceData(dir, false);
|
||||
final ExtAoFaceData n2e = angelica$getFaceData(dir, true);
|
||||
|
||||
if (doBlock) {
|
||||
this.angelica$lastR = n1e.angelica$getBlendedR(w) * n1d + n2e.angelica$getBlendedR(w) * n2d;
|
||||
this.angelica$lastG = n1e.angelica$getBlendedG(w) * n1d + n2e.angelica$getBlendedG(w) * n2d;
|
||||
this.angelica$lastB = n1e.angelica$getBlendedB(w) * n1d + n2e.angelica$getBlendedB(w) * n2d;
|
||||
}
|
||||
if (doSky) {
|
||||
this.angelica$lastSkyR = n1e.angelica$getBlendedSkyR(w) * n1d + n2e.angelica$getBlendedSkyR(w) * n2d;
|
||||
this.angelica$lastSkyG = n1e.angelica$getBlendedSkyG(w) * n1d + n2e.angelica$getBlendedSkyG(w) * n2d;
|
||||
this.angelica$lastSkyB = n1e.angelica$getBlendedSkyB(w) * n1d + n2e.angelica$getBlendedSkyB(w) * n2d;
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static float angelica$clamp(float v) {
|
||||
if (v < 0.0f) {
|
||||
return 0.0f;
|
||||
} else if (v > 1.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user