Compare commits

..
6 Commits
Author SHA1 Message Date
Jason Mitchell 996fb54cb0 Add a weather API for mods and instance their weather (#2364)
* Mods can supply their own weather columns, drawn alongside vanilla rain and snow
* Mod weather renders instanced with and without shader packs
* NotFine leaf settings apply to Biomes O' Plenty leaves
* Flyby can start in a named biome and fly in survival
2026-11-13 15:03:24 -08:00
Eldrinn Elantey d1e7838cf9 Render fluids stored by LiquidLogged in the chunk mesher (#2356)
* Render fluids stored by LiquidLogged in the chunk mesher

* Update dependencies.gradle.kts
2026-11-13 14:05:32 -08:00
Alexdoru a12d2bb4b0 fix world client leak in MixinRenderBlockConnectedTexturesEthereal (#2360) 2026-11-11 12:33:01 -08:00
Eldrinn Elantey 60d1a3de3c Restore FluidLogged fluid rendering (#2354)
* Restore FluidLogged fluid rendering

* Check region type before reading logged fluid
2026-11-08 18:39:33 -08:00
DarkShadow44 367f9d9161 Fix compatibility with LOTR mod (#2353) 2026-11-08 14:56:07 -08:00
Jason Mitchell d69fd20a8b Tag profiler captures with the Angelica version (#2351)
* RenderDoc, Tracy, async-profiler, spark
2026-11-07 22:49:01 -08:00
44 changed files with 1510 additions and 341 deletions

No files matched your search

+3
View File
@@ -186,6 +186,8 @@ DependencyHandlerScope.of(dependencies).apply {
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
compileOnly("com.github.GTNewHorizons:LiquidLogged:0.2.0:dev") { isTransitive = false }
compileOnly("com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.5-alpha:dev") { isTransitive = false }
}
@@ -210,6 +212,7 @@ afterEvaluate {
glsmTestFixtures(testFixtures(project(":glsm")) as ModuleDependency) { isTransitive = false }
testCompileOnly(testFixtures(project(":glsm")))
testImplementation("mega:fluidlogged-mc1.7.10:0.1.2")
testImplementation("com.github.GTNewHorizons:LiquidLogged:0.2.0:dev") { isTransitive = false }
testRuntimeOnly(libs.fastutil)
testRuntimeOnly(libs.joml)
@@ -66,6 +66,8 @@ public final class SystemProperties {
public static final String FLYBY_COMMANDS = System.getProperty("angelica.flyby.commands", "");
public static final String FLYBY_ORIGIN = System.getProperty("angelica.flyby.origin", "");
public static final Integer FLYBY_DIMENSION = Integer.getInteger("angelica.flyby.dimension");
public static final String FLYBY_BIOME = System.getProperty("angelica.flyby.biome", "");
public static final boolean FLYBY_SURVIVAL = Boolean.getBoolean("angelica.flyby.survival");
public static final FlybyPacing FLYBY_PACING = parseEnum("angelica.flyby.pacing", FlybyPacing.UNCAPPED, FlybyPacing.UNCAPPED, FlybyPacing.class);
public static final FlybyWeather FLYBY_WEATHER = parseEnum("angelica.flyby.weather", FlybyWeather.CLEAR, FlybyWeather.CLEAR, FlybyWeather.class);
public static final FlybyCamera FLYBY_CAMERA = parseEnum("angelica.flyby.camera", FlybyCamera.FIRST_PERSON, FlybyCamera.FIRST_PERSON, FlybyCamera.class);
@@ -26,12 +26,20 @@ public final class CaptureGate {
public static final boolean TOOL_ATTACHED = detectCaptureTool();
public static boolean markersThisFrame = true;
public static boolean tracyConnected;
private static boolean toolAttached = TOOL_ATTACHED;
private CaptureGate() {}
public static void refresh() {
tracyConnected = Tracy.isConnected();
markersThisFrame = gateValue(FORCED, TOOL_ATTACHED, tracyConnected);
markersThisFrame = gateValue(FORCED, toolAttached, tracyConnected);
}
static void probeGlTool() {
if (RenderSystem.hasExtension("GL_EXT_debug_tool")) {
toolAttached = true;
LOGGER.info("Capture tool detected via GL_EXT_debug_tool; instrumentation enabled");
}
}
static boolean gateValue(boolean forced, boolean toolAttached, boolean tracyConnected) {
@@ -39,7 +47,7 @@ public final class CaptureGate {
}
public static boolean enabledAtStartup() {
return FORCED || TOOL_ATTACHED;
return FORCED || toolAttached;
}
private static boolean detectCaptureTool() {
@@ -458,6 +458,7 @@ public class GLStateManager {
static void init(Runnable initCallback) {
final GLContextState glCtx = ctx();
RenderSystem.initRenderer();
CaptureGate.probeGlTool();
GLSMFramebuffer.queryDefaultDrawBuffer();
@@ -443,7 +443,7 @@ public class RenderSystem {
return clipDistanceEmulated;
}
private static boolean hasExtension(String name) {
static boolean hasExtension(String name) {
final int count = GLSMQueries.glGetInteger(GL30.GL_NUM_EXTENSIONS);
for (int i = 0; i < count; i++) {
if (name.equals(GLSMQueries.glGetStringi(GL11.GL_EXTENSIONS, i))) return true;
@@ -108,11 +108,13 @@ public final class FFPUniformBlock {
public static final int FOG_PARAMS = vec4("u_FogParams");
public static final int FOG_COLOR = vec4("u_FogColor");
// Rain and snow, for Instancing.WEATHER. 0 = (translate.xyz, 1/radius),
// 1 = (cameraFrac.xy, partialTicks, age), 2 = (rainScroll, snowScroll, rainStrength, unused).
// Weather columns, for Instancing.WEATHER. 0 = (translate.xyz, 1/radius),
// 1 = (cameraFrac.xy, partialTicks, age), 2 = (vanillaTick, providerTick, rainStrength, providerAge),
// 3 = (provider 1/radius, unused, unused, unused).
public static final int WEATHER_PARAMS_0 = vec4("u_WeatherParams0");
public static final int WEATHER_PARAMS_1 = vec4("u_WeatherParams1");
public static final int WEATHER_PARAMS_2 = vec4("u_WeatherParams2");
public static final int WEATHER_PARAMS_3 = vec4("u_WeatherParams3");
public static final int SIZE;
public static final String GLSL_DECL;
@@ -39,17 +39,16 @@ public final class InstancedGlslHelpers {
vec2 {p}wColumn = {span}.xy;
float {p}wBottom = {span}.z;
float {p}wTop = {span}.w;
vec2 {p}wLightmap = {params}.xy;
float {p}wHash = {params}.z;
bool {p}wSnow = {params}.w > 0.5;
float {p}wLightV = floor({params}.x / 65536.0);
vec2 {p}wLightmap = vec2({params}.x - {p}wLightV * 65536.0, {p}wLightV);
bool {p}wProvider = {params}.w > 0.5;
vec3 {p}wTranslate = {p}WeatherParams0.xyz;
float {p}wInvRadius = {p}WeatherParams0.w;
float {p}wInvRadius = {p}wProvider ? {p}WeatherParams3.x : {p}WeatherParams0.w;
vec2 {p}wCameraFrac = {p}WeatherParams1.xy;
float {p}wPartialTicks = {p}WeatherParams1.z;
float {p}wAge = {p}WeatherParams1.w;
float {p}wRainScroll = {p}WeatherParams2.x;
float {p}wSnowScroll = {p}WeatherParams2.y;
float {p}wStrength = {p}WeatherParams2.z;
float {p}wAge = {p}wProvider ? {p}WeatherParams2.w : {p}WeatherParams1.w;
float {p}wTick = {p}wProvider ? {p}WeatherParams2.y : {p}WeatherParams2.x;
float {p}wStrength = {p}wProvider ? 1.0 : {p}WeatherParams2.z;
vec2 {p}wHalfWidth = vec2(-{p}wColumn.y, {p}wColumn.x) * (0.5 / sqrt(dot({p}wColumn, {p}wColumn)));
vec2 {p}wCornerXZ = {p}wColumn + vec2(0.5) + ({corner}.x * 2.0 - 1.0) * {p}wHalfWidth;
@@ -58,22 +57,13 @@ public final class InstancedGlslHelpers {
float {p}wDistance = length({p}wColumn + vec2(0.5) - {p}wCameraFrac) * {p}wInvRadius;
float {p}wFade = 1.0 - {p}wDistance * {p}wDistance;
float {p}wScroll;
vec2 {p}wDrift;
float {p}wAlpha;
if ({p}wSnow) {
{p}wScroll = ({p}wSnowScroll + {p}wPartialTicks) / 512.0;
{p}wDrift = vec2({jitter}.x + {p}wAge * 0.01 * {jitter}.y, {jitter}.z + {p}wAge * {jitter}.w * 0.001);
{p}wAlpha = {p}wFade * 0.3 + 0.5;
} else {
{p}wScroll = (mod({p}wRainScroll + {p}wHash, 32.0) + {p}wPartialTicks) / 32.0 * (3.0 + {jitter}.x);
{p}wDrift = vec2(0.0);
{p}wAlpha = {p}wFade * 0.5 + 0.5;
}
float {p}wScroll = (mod({p}wTick + {params}.y, {layer}.x) + {p}wPartialTicks) / {layer}.x * {params}.z;
vec2 {p}wDrift = vec2({jitter}.x + {p}wAge * {layer}.y * {jitter}.y, {jitter}.z + {p}wAge * {layer}.z * {jitter}.w);
float {p}wAlpha = {p}wFade * {layer}.w + 0.5;
{tex0} = vec4({corner}.x + {p}wDrift.x, {p}wCornerY * 0.25 + {p}wScroll + {p}wDrift.y, 0.0, 1.0);
{tex1} = vec4({p}wLightmap, 0.0, 1.0);
{color} = vec4(1.0, 1.0, 1.0, {p}wAlpha * {p}wStrength);""";
{color} = floor(clamp(vec4({colorAttr}.rgb, {p}wAlpha * {colorAttr}.a * {p}wStrength), 0.0, 1.0) * 255.0) / 255.0;""";
public static String weatherPrologue(String uniformPrefix, String attribPrefix, String corner,
String vertex, String color, String tex0, String tex1, String sep) {
@@ -82,6 +72,8 @@ public final class InstancedGlslHelpers {
.replace("{span}", attribPrefix + "InstColumnSpan")
.replace("{jitter}", attribPrefix + "InstJitter")
.replace("{params}", attribPrefix + "InstWeatherParams")
.replace("{layer}", attribPrefix + "InstWeatherLayer")
.replace("{colorAttr}", attribPrefix + "InstWeatherColor")
.replace("{corner}", corner)
.replace("{vertex}", vertex)
.replace("{color}", color)
@@ -125,6 +117,8 @@ public final class InstancedGlslHelpers {
decls.add(decl(WeatherInstancedAttribs.LOC_COLUMN_SPAN, "vec4", prefix + "InstColumnSpan"));
decls.add(decl(WeatherInstancedAttribs.LOC_JITTER, "vec4", prefix + "InstJitter"));
decls.add(decl(WeatherInstancedAttribs.LOC_PARAMS, "vec4", prefix + "InstWeatherParams"));
decls.add(decl(WeatherInstancedAttribs.LOC_COLOR, "vec4", prefix + "InstWeatherColor"));
decls.add(decl(WeatherInstancedAttribs.LOC_LAYER, "vec4", prefix + "InstWeatherLayer"));
}
}
return decls.toArray(String[]::new);
@@ -391,8 +391,9 @@ public class Uniforms {
WeatherParams.translateZ, WeatherParams.invRadius);
putVec4(FFPUniformBlock.WEATHER_PARAMS_1, WeatherParams.cameraFracX, WeatherParams.cameraFracZ,
WeatherParams.partialTicks, WeatherParams.age);
putVec4(FFPUniformBlock.WEATHER_PARAMS_2, WeatherParams.rainScroll, WeatherParams.snowScroll,
WeatherParams.rainStrength, 0.0f);
putVec4(FFPUniformBlock.WEATHER_PARAMS_2, WeatherParams.vanillaTick, WeatherParams.providerTick,
WeatherParams.rainStrength, WeatherParams.providerAge);
putVec4(FFPUniformBlock.WEATHER_PARAMS_3, WeatherParams.providerInvRadius, 0.0f, 0.0f, 0.0f);
}
private void putVec4(int offset, Vector4f v) {
@@ -3,31 +3,76 @@ package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
/**
* One rain or snow column per instance.
* One weather column per instance.
*/
public final class WeatherInstancedAttribs {
public static final int LOC_COLUMN_SPAN = ParticleInstancedAttribs.LOC_CENTER_HALF;
public static final int LOC_JITTER = ParticleInstancedAttribs.LOC_UV;
public static final int LOC_PARAMS = InstancedAttribs.LOC_COLOR;
public static final int LOC_COLOR = InstancedAttribs.LOC_ROW2;
public static final int LOC_LAYER = InstancedAttribs.LOC_OVERLAY;
public static final int OFFSET_COLUMN_SPAN = 0;
public static final int OFFSET_JITTER = 16;
public static final int OFFSET_PARAMS = 32;
public static final int STRIDE = 48;
public static final int OFFSET_COLOR = 48;
public static final int OFFSET_LAYER = 64;
public static final int STRIDE = 80;
public static final int RAIN_PERIOD = 32;
public static final float RAIN_SCROLL_BASE = 3.0F;
public static final float RAIN_FADE = 0.5F;
public static final int SNOW_PERIOD = 512;
public static final float SNOW_U_DRIFT = 0.01F;
public static final float SNOW_V_DRIFT = 0.001F;
public static final float SNOW_FADE = 0.3F;
public static final float SOURCE_VANILLA = 0.0F;
public static final float SOURCE_PROVIDER = 1.0F;
public static void putColumn(ByteBuffer data, int index, float x, float z, float bottom, float top, float uBase, float uDrift,
float vBase, float vDrift, float brightness, int scrollOffset, float scrollScale, float source, float r, float g, float b,
float a, int period, float uDriftScale, float vDriftScale, float fadeScale) {
final int base = index * STRIDE;
data.putFloat(base + OFFSET_COLUMN_SPAN, x);
data.putFloat(base + OFFSET_COLUMN_SPAN + 4, z);
data.putFloat(base + OFFSET_COLUMN_SPAN + 8, bottom);
data.putFloat(base + OFFSET_COLUMN_SPAN + 12, top);
data.putFloat(base + OFFSET_JITTER, uBase);
data.putFloat(base + OFFSET_JITTER + 4, uDrift);
data.putFloat(base + OFFSET_JITTER + 8, vBase);
data.putFloat(base + OFFSET_JITTER + 12, vDrift);
data.putFloat(base + OFFSET_PARAMS, brightness);
data.putFloat(base + OFFSET_PARAMS + 4, scrollOffset & (period - 1));
data.putFloat(base + OFFSET_PARAMS + 8, scrollScale);
data.putFloat(base + OFFSET_PARAMS + 12, source);
data.putFloat(base + OFFSET_COLOR, r);
data.putFloat(base + OFFSET_COLOR + 4, g);
data.putFloat(base + OFFSET_COLOR + 8, b);
data.putFloat(base + OFFSET_COLOR + 12, a);
data.putFloat(base + OFFSET_LAYER, period);
data.putFloat(base + OFFSET_LAYER + 4, uDriftScale);
data.putFloat(base + OFFSET_LAYER + 8, vDriftScale);
data.putFloat(base + OFFSET_LAYER + 12, fadeScale);
}
public static void pointInstanceAttribs(long base) {
GLSMVertexInput.glVertexAttribPointer(LOC_COLUMN_SPAN, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_COLUMN_SPAN);
GLSMVertexInput.glVertexAttribPointer(LOC_JITTER, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_JITTER);
GLSMVertexInput.glVertexAttribPointer(LOC_PARAMS, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_PARAMS);
GLSMVertexInput.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_COLOR);
GLSMVertexInput.glVertexAttribPointer(LOC_LAYER, 4, GL11.GL_FLOAT, false, STRIDE, base + OFFSET_LAYER);
}
static void enableInstanceArrays() {
InstancedAttribs.enableInstanced(LOC_COLUMN_SPAN);
InstancedAttribs.enableInstanced(LOC_JITTER);
InstancedAttribs.enableInstanced(LOC_PARAMS);
InstancedAttribs.enableInstanced(LOC_COLOR);
InstancedAttribs.enableInstanced(LOC_LAYER);
}
private WeatherInstancedAttribs() {}
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm.ffp;
/**
* The per-frame half of the rain and snow instancing, set once per weather draw.
* The per-frame half of the weather column instancing, set once per frame.
*/
public final class WeatherParams {
@@ -15,9 +15,14 @@ public final class WeatherParams {
public static float partialTicks;
public static float age;
public static float rainScroll;
public static float snowScroll;
public static float vanillaTick;
public static float providerTick;
public static float rainStrength;
public static float providerAge;
public static float providerInvRadius;
public static final int TICK_WRAP = 1024;
public static int generation;
@@ -25,7 +30,7 @@ public final class WeatherParams {
public static void set(float translateX, float translateY, float translateZ, float invRadius,
float cameraFracX, float cameraFracZ, float partialTicks, float age,
float rainScroll, float snowScroll, float rainStrength) {
int vanillaTick, float rainStrength, int providerTick, float providerAge, float providerInvRadius) {
WeatherParams.translateX = translateX;
WeatherParams.translateY = translateY;
WeatherParams.translateZ = translateZ;
@@ -34,9 +39,11 @@ public final class WeatherParams {
WeatherParams.cameraFracZ = cameraFracZ;
WeatherParams.partialTicks = partialTicks;
WeatherParams.age = age;
WeatherParams.rainScroll = rainScroll;
WeatherParams.snowScroll = snowScroll;
WeatherParams.vanillaTick = vanillaTick & (TICK_WRAP - 1);
WeatherParams.providerTick = providerTick & (TICK_WRAP - 1);
WeatherParams.rainStrength = rainStrength;
WeatherParams.providerAge = providerAge;
WeatherParams.providerInvRadius = providerInvRadius;
generation++;
}
}
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* The four corners every rain and snow column is expanded from. x picks the end of the billboard, y picks
* The four corners every weather column is expanded from. x picks the end of the billboard, y picks
* the bottom or top edge.
*/
public final class WeatherQuadMesh {
@@ -49,14 +49,6 @@ public final class WeatherQuadMesh {
return vao;
}
public static void bindInstanceBuffer(int bufferId) {
GLSMVertexInput.glBindVertexArray(vao());
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferId);
WeatherInstancedAttribs.pointInstanceAttribs(0L);
GLSMVertexInput.glBindVertexArray(0);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
public static void delete() {
if (vbo != 0) {
GLSMBuffers.glDeleteBuffers(vbo);
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLSMTextureLifetime;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
@@ -16,11 +17,18 @@ public final class FrameHooks {
private static boolean gameLoopStarted;
private static long frameCounter;
private static boolean frameGroupActive;
private static String framePrefix = "frame:";
private static String buildLabel = "";
public static void bootstrapFirstFrame() {
DisplayListManager.abortIfLeaked();
if (!gameLoopStarted) {
gameLoopStarted = true;
final GLSMInitConfig config = GLStateManager.getInitConfig();
if (config != null && !config.getBuildLabel().isEmpty()) {
buildLabel = config.getBuildLabel();
framePrefix = buildLabel + " frame:";
}
RENDER_BACKEND.onFrameBegin();
pushFrameGroup();
}
@@ -48,8 +56,10 @@ public final class FrameHooks {
}
private static void pushFrameGroup() {
final boolean wasConnected = CaptureGate.tracyConnected;
CaptureGate.refresh();
GLDebug.pushGroup("frame:", frameCounter);
if (CaptureGate.tracyConnected && !wasConnected && !buildLabel.isEmpty()) Tracy.message(buildLabel);
GLDebug.pushGroup(framePrefix, frameCounter);
frameGroupActive = true;
}
@@ -17,6 +17,7 @@ public final class GLSMInitConfig {
@Getter private final Runnable postInitCallback;
private final boolean dsaEnabled;
@Getter private final boolean noErrorChecks;
@Getter private final String buildLabel;
private GLSMInitConfig(Builder builder) {
this.lwjglDebug = builder.lwjglDebug;
@@ -27,6 +28,7 @@ public final class GLSMInitConfig {
this.displayHeight = builder.displayHeight;
this.postInitCallback = builder.postInitCallback;
this.dsaEnabled = builder.dsaEnabled;
this.buildLabel = builder.buildLabel;
}
public boolean isDSAEnabled() {
@@ -46,6 +48,7 @@ public final class GLSMInitConfig {
private Runnable postInitCallback = null;
private boolean dsaEnabled = true;
private boolean noErrorChecks;
private String buildLabel = "";
private Builder() {}
@@ -85,6 +88,11 @@ public final class GLSMInitConfig {
return this;
}
public Builder buildLabel(String buildLabel) {
this.buildLabel = buildLabel == null ? "" : buildLabel;
return this;
}
public GLSMInitConfig build() {
return new GLSMInitConfig(this);
}
@@ -100,6 +100,7 @@ class InstancedFFPDrawGLTest {
CubeParityFixture.disableDirectionalLight();
CubeParityFixture.deleteResources();
ParticleParityFixture.deleteResources();
WeatherParityFixture.deleteResources();
if (templateVbo != 0) { GLSMBuffers.glDeleteBuffers(templateVbo); templateVbo = 0; }
if (instanceVbo != 0) { GLSMBuffers.glDeleteBuffers(instanceVbo); instanceVbo = 0; }
if (vao != 0) { GLSMVertexInput.glDeleteVertexArrays(vao); vao = 0; }
@@ -196,6 +197,27 @@ class InstancedFFPDrawGLTest {
ParticleParityFixture.assertPixelParity(reference, instanced, PARITY_SIZE, 0xFF000000, pixel -> "0x" + Integer.toHexString(pixel));
}
@Test
void weatherColumnsMatchTessellatedReference() {
GLSMRasterizer.glViewport(0, 0, PARITY_SIZE, PARITY_SIZE);
WeatherParityFixture.setupScene();
FfpFixture.activateFfp();
final WeatherParityFixture.Spec[] specs = WeatherParityFixture.specs();
FfpFixture.clear();
WeatherParityFixture.drawReference(specs);
final int[] reference = FfpFixture.readRegion(PARITY_SIZE);
FfpFixture.clear();
WeatherParityFixture.drawInstanced(specs);
final int[] instanced = FfpFixture.readRegion(PARITY_SIZE);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "parity draws must not raise a GL error");
WeatherParityFixture.assertPixelParity(reference, instanced, PARITY_SIZE, 0xFF000000, pixel -> "0x" + Integer.toHexString(pixel));
}
@Test
void entityAttribSelectsColorPerInstance() {
GLSMRasterizer.glViewport(0, 0, INTEGER_WIDTH, INTEGER_HEIGHT);
@@ -0,0 +1,292 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
import com.gtnewhorizons.angelica.glsm.GLSMBuffers;
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
import com.gtnewhorizons.angelica.glsm.GLSMMatrices;
import com.gtnewhorizons.angelica.glsm.GLSMPrograms;
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.util.function.IntFunction;
public final class WeatherParityFixture {
public record Spec(float x, float z, float bottom, float top, float uBase, float uDrift, float vBase, float vDrift,
int brightness, int scrollOffset, float scrollScale, float source,
float r, float g, float b, float a, int period, float uDriftScale, float vDriftScale, float fadeScale) {}
private static final int TICK = 2650;
private static final float PARTIAL_TICKS = 0.375f;
private static final float TRANSLATE_X = 0.37f;
private static final float TRANSLATE_Y = -62.25f;
private static final float TRANSLATE_Z = -0.61f;
private static final float INV_RADIUS = 1.0f / 6.0f;
private static final float CAMERA_FRAC_X = 0.3f;
private static final float CAMERA_FRAC_Z = 0.7f;
private static final float STRENGTH = 0.8f;
private static final int PROVIDER_TICK = 1777;
private static final float PROVIDER_INV_RADIUS = 1.0f / 9.0f;
private static final float[] CORNER_X = { 0.0f, 1.0f, 1.0f, 0.0f };
private static final float[] CORNER_Y = { 0.0f, 0.0f, 1.0f, 1.0f };
private static final int REF_STRIDE = 44;
private static int referenceVao;
private static int referenceVbo;
private static int instanceVbo;
private static int weatherTexture;
private static int lightmapTexture;
private WeatherParityFixture() {}
private static Spec rain(float x, float z, float bottom, float top, float j0, int hash, int brightness) {
return new Spec(x, z, bottom, top, 0f, 0f, 0f, 0f, brightness, hash, WeatherInstancedAttribs.RAIN_SCROLL_BASE + j0, WeatherInstancedAttribs.SOURCE_VANILLA,
1f, 1f, 1f, 1f, WeatherInstancedAttribs.RAIN_PERIOD, 0f, 0f, WeatherInstancedAttribs.RAIN_FADE);
}
private static Spec snow(float x, float z, float bottom, float top, float j0, float j1, float j2, float j3, int brightness) {
return new Spec(x, z, bottom, top, j0, j1, j2, j3, brightness, 0, 1f, WeatherInstancedAttribs.SOURCE_VANILLA,
1f, 1f, 1f, 1f, WeatherInstancedAttribs.SNOW_PERIOD, WeatherInstancedAttribs.SNOW_U_DRIFT, WeatherInstancedAttribs.SNOW_V_DRIFT,
WeatherInstancedAttribs.SNOW_FADE);
}
private static Spec provider(float x, float z, float bottom, float top, float uBase, float uDrift, float vBase, float vDrift,
int brightness, int scrollOffset, float scrollScale, float r, float g, float b, float a,
int period, float uDriftScale, float vDriftScale, float fadeScale) {
return new Spec(x, z, bottom, top, uBase, uDrift, vBase, vDrift, brightness, scrollOffset, scrollScale, WeatherInstancedAttribs.SOURCE_PROVIDER, r, g, b, a,
period, uDriftScale, vDriftScale, fadeScale);
}
public static Spec[] specs() {
return new Spec[] {
rain(-4f, -3f, 58f, 66f, 0.31f, 5, 0x00F00030),
rain(2f, -4f, 60f, 67f, 0.77f, 19, 0x00A000D0),
rain(-2f, 3f, 57f, 65f, 0.05f, 62, 0x0060F0A0),
rain(4f, 2f, 59f, 64f, 0.58f, 11, 0x00D01090),
rain(1f, 4f, 56f, 66f, 0.93f, 24, 0x0080C0F0),
snow(-5f, 1f, 58f, 66f, 0.12f, 0.47f, 0.83f, 0.29f, 0x00E02070),
snow(3f, -2f, 60f, 68f, 0.66f, 0.91f, 0.18f, 0.74f, 0x00B0B0B0),
snow(-1f, -5f, 57f, 63f, 0.39f, 0.25f, 0.52f, 0.96f, 0x0040E0C0),
snow(5f, 4f, 59f, 67f, 0.84f, 0.63f, 0.07f, 0.41f, 0x00F0F0F0),
snow(-3f, 5f, 55f, 65f, 0.22f, 0.78f, 0.35f, 0.58f, 0x00109050),
provider(-5f, -4f, 58f, 66f, 0.14f, 0.52f, 0.71f, 0.33f, 0x00C06040, 0, 1f, 1f, 1f, 1f, 1f, 512, 0.03f, 0.001f, 0.3f),
provider(4f, -3f, 59f, 67f, 0.61f, 0.27f, 0.09f, 0.88f, 0x00F020B0, 0, 1f, 1f, 1f, 1f, 1f, 512, 0.03f, 0.001f, 0.3f),
provider(-4f, 2f, 57f, 65f, 0.42f, 0.36f, 0.64f, 0.19f, 0x0090D070, 0, 0f, 0.12f, 0.27f, 0.18f, 2.0f, 512, 0.03f, 0.003f, 0f),
provider(2f, 5f, 60f, 66f, 0.83f, 0.74f, 0.25f, 0.57f, 0x0050A0E0, 0, 0f, 0.29f, 0.14f, 0.21f, 2.0f, 512, 0.03f, 0.003f, 0f),
provider(-2f, -5f, 56f, 64f, 0.07f, 0.45f, 0.93f, 0.62f, 0x00B0F040, 0, -1f, 0.83f, 0.92f, 0.88f, 1f, 1024, 0.001f, 0.001f, 0.3f),
provider(5f, 1f, 58f, 68f, 0.55f, 0.16f, 0.38f, 0.81f, 0x007070F0, 0, -1f, 0.97f, 0.86f, 0.81f, 1f, 1024, 0.001f, 0.001f, 0.3f),
provider(-5f, 4f, 59f, 66f, 0f, 0.68f, 0f, 0.23f, 0x00E0E050, 0, -1f, 1f, 1f, 1f, 0.35f, 512, 0f, 0.01f, 0.3f),
provider(3f, 3f, 57f, 67f, 0f, 0.49f, 0f, 0.91f, 0x0030C0A0, 0, -1f, 1f, 1f, 1f, 0.82f, 512, 0f, 0.01f, 0.3f),
provider(0f, -4f, 58f, 65f, 0.26f, 0.12f, 0.67f, 0.44f, 0x00A0A0A0, 21, 3.46f, 1f, 1f, 1f, 1f, 32, 0f, 0f, 0.3f),
provider(1f, 3f, 60f, 66f, 0.89f, 0.31f, 0.15f, 0.76f, 0x00F0A040, 7, 3.81f, 1f, 1f, 1f, 1f, 32, 0f, 0f, 0.3f),
provider(-3f, 0f, 56f, 67f, 0.33f, 0.85f, 0.48f, 0.13f, 0x00D0D0D0, 0, 1f, 1f, 1f, 1f, 0.24f, 512, 0.01f, 0.01f, 0.3f),
provider(0f, 5f, 58f, 64f, 0.72f, 0.04f, 0.59f, 0.97f, 0x0010F0F0, 0, 1f, 1f, 1f, 1f, 0.67f, 512, 0.01f, 0.01f, 0.3f),
};
}
private static float mod(float x, float y) {
return x - y * (float) Math.floor(x / y);
}
public static void setupScene() {
GLSMEnable.glDisable(GL11.GL_FOG);
GLSMEnable.glDisable(GL11.GL_ALPHA_TEST);
GLSMEnable.glDisable(GL11.GL_DEPTH_TEST);
GLSMEnable.glDisable(GL11.GL_CULL_FACE);
GLSMEnable.glDisable(GL11.GL_LIGHTING);
GLSMEnable.glEnable(GL11.GL_BLEND);
GLSMBlend.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GLSMMatrices.glMatrixMode(GL11.GL_PROJECTION);
GLSMMatrices.glLoadIdentity();
GLSMMatrices.glOrtho(-8.0, 8.0, -8.0, 8.0, -64.0, 64.0);
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
GLSMMatrices.glLoadIdentity();
GLSMMatrices.glRotatef(30.0f, 1.0f, 0.0f, 0.0f);
GLSMMatrices.glRotatef(35.0f, 0.0f, 1.0f, 0.0f);
lightmapTexture = createTexture(GL13.GL_TEXTURE1, 16, GL11.GL_LINEAR, GL11.GL_CLAMP);
GLSMMatrices.glMatrixMode(GL11.GL_TEXTURE);
GLSMMatrices.glLoadIdentity();
GLSMMatrices.glScalef(0.00390625f, 0.00390625f, 0.00390625f);
GLSMMatrices.glTranslatef(8.0f, 8.0f, 8.0f);
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
weatherTexture = createTexture(GL13.GL_TEXTURE0, 4, GL11.GL_NEAREST, GL11.GL_REPEAT);
GLSMMatrices.glMatrixMode(GL11.GL_TEXTURE);
GLSMMatrices.glLoadIdentity();
GLSMMatrices.glMatrixMode(GL11.GL_MODELVIEW);
}
private static int createTexture(int unit, int size, int filter, int wrap) {
final int id = GLSMTextures.glGenTextures();
GLSMTextures.glActiveTexture(unit);
GLSMTextures.glBindTexture(GL11.GL_TEXTURE_2D, id);
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter);
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter);
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap);
GLSMTextures.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap);
final ByteBuffer texels = BufferUtils.createByteBuffer(size * size * 4);
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
texels.put((byte) (40 + x * (200 / size)));
texels.put((byte) (40 + y * (200 / size)));
texels.put((byte) (255 - (x + y) * (100 / size)));
texels.put((byte) 255);
}
}
texels.flip();
GLSMTextures.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
GLSMEnable.glEnable(GL11.GL_TEXTURE_2D);
return id;
}
private static void putVertex(ByteBuffer data, float px, float py, float pz, float r, float g, float b, float a,
float u, float v, float lmX, float lmY) {
final int base = data.position();
data.putFloat(base, px);
data.putFloat(base + 4, py);
data.putFloat(base + 8, pz);
data.putFloat(base + 12, r);
data.putFloat(base + 16, g);
data.putFloat(base + 20, b);
data.putFloat(base + 24, a);
data.putFloat(base + 28, u);
data.putFloat(base + 32, v);
data.putFloat(base + 36, lmX);
data.putFloat(base + 40, lmY);
data.position(base + REF_STRIDE);
}
private static float tessellatorByte(float channel) {
final int value = (int) (channel * 255.0F);
return Math.max(0, Math.min(255, value)) / 255.0f;
}
private static void addColumn(ByteBuffer data, Spec s) {
final boolean provider = s.source() == WeatherInstancedAttribs.SOURCE_PROVIDER;
final int tick = provider ? PROVIDER_TICK : TICK;
final float age = tick + PARTIAL_TICKS;
final float invRadius = provider ? PROVIDER_INV_RADIUS : INV_RADIUS;
final float strength = provider ? 1.0f : STRENGTH;
final float halfScale = 0.5f / (float) Math.sqrt(s.x() * s.x() + s.z() * s.z());
final float halfX = -s.z() * halfScale;
final float halfZ = s.x() * halfScale;
final float dx = s.x() + 0.5f - CAMERA_FRAC_X;
final float dz = s.z() + 0.5f - CAMERA_FRAC_Z;
final float distance = (float) Math.sqrt(dx * dx + dz * dz) * invRadius;
final float fade = 1.0f - distance * distance;
final float scroll = (mod((float) tick + s.scrollOffset(), s.period()) + PARTIAL_TICKS) / s.period() * s.scrollScale();
final float driftU = s.uBase() + age * s.uDriftScale() * s.uDrift();
final float driftV = s.vBase() + age * s.vDriftScale() * s.vDrift();
final float alpha = fade * s.fadeScale() + 0.5f;
final float r = tessellatorByte(s.r());
final float g = tessellatorByte(s.g());
final float b = tessellatorByte(s.b());
final float a = tessellatorByte(alpha * s.a() * strength);
final float lmX = s.brightness() & 0xFFFF;
final float lmY = s.brightness() >> 16 & 0xFFFF;
for (int i = 0; i < 4; i++) {
final float side = CORNER_X[i] * 2.0f - 1.0f;
final float cornerX = s.x() + 0.5f + side * halfX;
final float cornerZ = s.z() + 0.5f + side * halfZ;
final float cornerY = s.bottom() * (1.0f - CORNER_Y[i]) + s.top() * CORNER_Y[i];
putVertex(data, cornerX + TRANSLATE_X, cornerY + TRANSLATE_Y, cornerZ + TRANSLATE_Z, r, g, b, a,
CORNER_X[i] + driftU, cornerY * 0.25f + scroll + driftV, lmX, lmY);
}
}
private static ByteBuffer buildReferenceData(Spec[] specs) {
final ByteBuffer data = BufferUtils.createByteBuffer(specs.length * 4 * REF_STRIDE);
for (Spec s : specs) {
addColumn(data, s);
}
data.flip();
return data;
}
public static void drawReference(Spec[] specs) {
final ByteBuffer data = buildReferenceData(specs);
if (referenceVao == 0) referenceVao = GLSMVertexInput.glGenVertexArrays();
GLSMVertexInput.glBindVertexArray(referenceVao);
if (referenceVbo == 0) referenceVbo = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, referenceVbo);
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STREAM_DRAW);
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, REF_STRIDE, 0);
FfpFixture.attrib(1, 4, GL11.GL_FLOAT, false, REF_STRIDE, 12);
FfpFixture.attrib(2, 2, GL11.GL_FLOAT, false, REF_STRIDE, 28);
FfpFixture.attrib(3, 2, GL11.GL_FLOAT, false, REF_STRIDE, 36);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
VAOManager.setCurrentVertexFlags(VertexFlags.COLOR_BIT | VertexFlags.TEXTURE_BIT | VertexFlags.BRIGHTNESS_BIT);
GLSMDraws.glDrawArrays(GL11.GL_QUADS, 0, specs.length * 4);
GLSMVertexInput.glBindVertexArray(0);
}
public static void drawInstanced(Spec[] specs) {
final ByteBuffer records = BufferUtils.createByteBuffer(specs.length * WeatherInstancedAttribs.STRIDE);
for (int i = 0; i < specs.length; i++) {
final Spec s = specs[i];
WeatherInstancedAttribs.putColumn(records, i, s.x(), s.z(), s.bottom(), s.top(), s.uBase(), s.uDrift(), s.vBase(), s.vDrift(),
s.brightness(), s.scrollOffset(), s.scrollScale(), s.source(), s.r(), s.g(), s.b(), s.a(),
s.period(), s.uDriftScale(), s.vDriftScale(), s.fadeScale());
}
WeatherParams.set(TRANSLATE_X, TRANSLATE_Y, TRANSLATE_Z, INV_RADIUS, CAMERA_FRAC_X, CAMERA_FRAC_Z,
PARTIAL_TICKS, TICK + PARTIAL_TICKS, TICK, STRENGTH, PROVIDER_TICK, PROVIDER_TICK + PARTIAL_TICKS, PROVIDER_INV_RADIUS);
GLSMVertexInput.glBindVertexArray(WeatherQuadMesh.vao());
if (instanceVbo == 0) instanceVbo = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVbo);
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, records, GL15.GL_STREAM_DRAW);
WeatherInstancedAttribs.pointInstanceAttribs(0L);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
VAOManager.setCurrentVertexFlags(WeatherQuadMesh.VERTEX_FLAGS);
GLSMPrograms.setFfpInstancing(Instancing.WEATHER);
GLSMDraws.glDrawArraysInstanced(GL11.GL_TRIANGLE_FAN, 0, WeatherQuadMesh.VERTEX_COUNT, specs.length);
GLSMPrograms.setFfpInstancing(Instancing.NONE);
GLSMVertexInput.glBindVertexArray(0);
}
public static void assertPixelParity(int[] reference, int[] instanced, int size, int background, IntFunction<String> describe) {
FfpFixture.assertPixelParity("instanced weather columns", reference, instanced, size, background, 16, 0, describe);
}
public static void deleteResources() {
if (instanceVbo != 0) {
GLSMBuffers.glDeleteBuffers(instanceVbo);
instanceVbo = 0;
}
if (referenceVbo != 0) {
GLSMBuffers.glDeleteBuffers(referenceVbo);
referenceVbo = 0;
}
if (referenceVao != 0) {
GLSMVertexInput.glDeleteVertexArrays(referenceVao);
referenceVao = 0;
}
if (weatherTexture != 0) {
GLSMTextures.glDeleteTextures(weatherTexture);
weatherTexture = 0;
}
if (lightmapTexture != 0) {
GLSMTextures.glDeleteTextures(lightmapTexture);
lightmapTexture = 0;
}
WeatherQuadMesh.delete();
}
}
+1
View File
@@ -36,6 +36,7 @@ tasks.withType<JavaExec>().matching { it.name.startsWith("runClient") }.configur
for (name in forwardedRunProperties) {
jvmArgs("-D$name=${System.getProperty(name)}")
}
jvmArgs("-Dangelica.buildVersion=${project.version}")
}
val isMacOs = org.gradle.internal.os.OperatingSystem.current().isMacOsX
+2
View File
@@ -11,6 +11,8 @@
# extra - passed through to gradle; -Dangelica.* reaches the client (e.g. -Dangelica.flyby.timeOfDay=1500).
# -Dangelica.flyby.camera=FIRST_PERSON|THIRD_PERSON_BACK|THIRD_PERSON_FRONT (default FIRST_PERSON).
# -Dangelica.flyby.dimension=<id> moves the player there first (1 = End), after world saving is off.
# -Dangelica.flyby.biome=<name> pins the origin to the 44x44-block area of that biome nearest to 0,0 (BiomeGenBase.biomeName), searched in the target dimension.
# -Dangelica.flyby.survival=true runs the flight in survival with flying and no damage, restoring the original game mode afterwards.
# -PclientJvmVendor=adoptium (macOS) picks the JVM vendor for runClient17/21/25.
#
# FLYBY_SPEED - blocks per tick. (0.5 ~= creative flight).
@@ -0,0 +1,21 @@
package com.gtnewhorizons.angelica.api.weather;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.EntityLivingBase;
public interface WeatherColumnProvider {
// False suppresses vanilla rain and snow in this dimension.
boolean rendersVanillaWeather();
// Called each instanced weather frame before columnRevision and collectColumns; returns the tick counter that drives scroll and drift.
int beginFrame(WorldClient world, EntityLivingBase view, float partialTicks);
// Must change whenever any collected column changes; columns are re-collected only then or when the dimension or the player's block X/Z changes.
int columnRevision();
// Finite positive block distance d0 in alpha = ((1 - (d / d0)^2) * fadeScale + 0.5) * a.
float fadeRadius();
void collectColumns(WeatherColumnSink sink);
}
@@ -0,0 +1,12 @@
package com.gtnewhorizons.angelica.api.weather;
import net.minecraft.util.ResourceLocation;
public interface WeatherColumnSink {
// scrollPeriod is a power of two no greater than 1024.
void beginLayer(ResourceLocation texture, int scrollPeriod, float uDriftScale, float vDriftScale, float fadeScale);
// x and z are world block coordinates; brightness is a packed lightmap coordinate below 2^24.
void column(int x, int z, float bottom, float top, float uBase, float uDrift, float vBase, float vDrift, int brightness, int scrollOffset, float scrollScale, float r, float g, float b, float a);
}
@@ -0,0 +1,4 @@
@API(owner = "angelica", provides = "angelica|weather", apiVersion = "1")
package com.gtnewhorizons.angelica.api.weather;
import cpw.mods.fml.common.API;
@@ -30,6 +30,7 @@ public class ModStatus {
public static boolean isBaublesLoaded;
public static boolean isCosmeticArmorReworkedLoaded;
public static boolean isFluidLoggedLoaded;
public static boolean isLiquidLoggedLoaded;
public static boolean isCubicChunksLoaded;
public static boolean isBOPLoaded;
public static boolean isEtFuturumLoaded;
@@ -51,6 +52,7 @@ public class ModStatus {
isBaublesLoaded = Loader.isModLoaded("Baubles");
isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked");
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
isLiquidLoggedLoaded = Loader.isModLoaded("liquidlogged");
isCubicChunksLoaded = Loader.isModLoaded("cubicchunks");
isEtFuturumLoaded = Loader.isModLoaded("etfuturum");
@@ -0,0 +1,77 @@
package com.gtnewhorizons.angelica.compat.spark;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.InputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public final class SparkKnownSources {
private static final Logger LOGGER = LogManager.getLogger("Angelica");
private static final String PLUGIN = "me/lucko/spark/forge/plugin/Forge1710SparkPlugin";
private static final String SOURCE_METADATA = "me.lucko.spark.common.sampler.source.SourceMetadata";
private static MethodHandle constructor;
private static boolean failed;
private SparkKnownSources() {}
public static boolean mixinApplies() {
try (InputStream stream = SparkKnownSources.class.getResourceAsStream("/" + PLUGIN + ".class")) {
if (stream == null) {
return false;
}
final boolean[] declared = new boolean[1];
new ClassReader(stream).accept(new ClassVisitor(Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if ("getKnownSources".equals(name) && "()Ljava/util/Collection;".equals(desc)) {
declared[0] = true;
}
return null;
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
if (declared[0]) {
LOGGER.info("spark already reports mod sources; skipping");
return false;
}
return true;
} catch (Throwable t) {
LOGGER.warn("Could not inspect spark plugin class; not adding mod sources", t);
return false;
}
}
public static Collection<Object> gather(ClassLoader loader) {
if (failed) {
return Collections.emptyList();
}
try {
if (constructor == null) constructor = MethodHandles.publicLookup().findConstructor(Class.forName(SOURCE_METADATA, false, loader),
MethodType.methodType(void.class, String.class, String.class, String.class, String.class));
final List<ModContainer> mods = Loader.instance().getActiveModList();
final List<Object> sources = new ArrayList<>(mods.size());
for (int i = 0; i < mods.size(); i++) {
final ModContainer mod = mods.get(i);
sources.add(constructor.invoke(mod.getModId(), mod.getVersion(), (String) null, (String) null));
}
return sources;
} catch (Throwable t) {
failed = true;
LOGGER.warn("Could not build spark mod sources; profiles will omit mod versions", t);
return Collections.emptyList();
}
}
}
@@ -14,6 +14,7 @@ import com.gtnewhorizons.angelica.rendering.FpsReducer;
import com.gtnewhorizons.angelica.rendering.FramePacer;
import com.gtnewhorizons.angelica.rendering.IsbrhTestRenderer;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
import com.prupe.mcpatcher.mal.biome.BiomeAPI;
import com.sun.management.OperatingSystemMXBean;
import com.sun.management.ThreadMXBean;
import cpw.mods.fml.client.FMLClientHandler;
@@ -24,6 +25,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityClientPlayerMP;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiSelectWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.server.MinecraftServer;
@@ -34,8 +36,12 @@ import net.minecraft.util.IChatComponent;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ScreenShotHelper;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.Teleporter;
import net.minecraft.world.World;
import net.minecraft.world.WorldSettings;
import net.minecraft.world.WorldServer;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.biome.WorldChunkManager;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.storage.ThreadedFileIOBase;
@@ -66,6 +72,8 @@ public final class FlybyRunner {
private enum State { IDLE, WAITING, PREPARING, WARMUP, RUNNING, SETTLE, EXITING, MENU, DONE }
private static final int BIOME_WINDOW = 11;
private static final int BIOME_SEARCH_CELLS = 512;
private static final int EXIT_TICKS = 20;
private static final int SETTLE_TICKS = 40;
private static final int MENU_SETTLE_TICKS = 20;
@@ -93,6 +101,16 @@ public final class FlybyRunner {
private final AtomicReference<String> pendingDimensionPlayer = new AtomicReference<>();
private boolean dimensionRequested;
private volatile boolean dimensionAbandoned;
private final AtomicReference<String> pendingBiomePlayer = new AtomicReference<>();
private boolean biomeRequested;
private volatile boolean biomeAbandoned;
private volatile FlybyOrigin biomeOrigin;
private BiomeGenBase[] biomeScratch;
private volatile boolean survivalRestoreRequested;
private boolean survivalApplied;
private String survivalPlayerName;
private String survivalWorld;
private WorldSettings.GameType survivalRestoreType;
private FlybyRequest activeRequest;
private String[] sceneCommands = NO_COMMANDS;
private volatile boolean sceneClearRequested;
@@ -229,6 +247,10 @@ public final class FlybyRunner {
this.exitWhenDone = false;
this.dimensionRequested = false;
this.dimensionAbandoned = false;
this.biomeRequested = false;
this.biomeAbandoned = false;
this.biomeOrigin = null;
if (SystemProperties.FLYBY_SURVIVAL) this.survivalRestoreRequested = true;
this.jfr = false;
this.armed = false;
this.fixedOrigin = null;
@@ -305,6 +327,7 @@ public final class FlybyRunner {
return;
}
if (this.awaitingDimension(mc, player)) return;
if (this.awaitingBiome(mc, player)) return;
this.begin(mc, player);
}
case PREPARING -> {
@@ -390,6 +413,26 @@ public final class FlybyRunner {
return true;
}
private boolean awaitingBiome(Minecraft mc, EntityClientPlayerMP player) {
final String biome = SystemProperties.FLYBY_BIOME;
if (!this.startedFromProperties || biome.isEmpty() || this.biomeAbandoned) return false;
final FlybyOrigin found = this.biomeOrigin;
if (found != null) {
this.fixedOrigin = this.fixedOrigin == null ? found : new FlybyOrigin(found.x(), found.z(), this.fixedOrigin.yaw());
return false;
}
if (mc.getIntegratedServer() == null) {
LOGGER.warn("Flyby: not singleplayer, ignoring biome '{}'", biome);
this.biomeAbandoned = true;
return false;
}
if (!this.biomeRequested) {
this.biomeRequested = true;
this.pendingBiomePlayer.set(player.getCommandSenderName());
}
return true;
}
private void overridePauseOnLostFocus(Minecraft mc) {
if (this.pauseOnLostFocusOverridden) return;
this.pauseOnLostFocusSaved = mc.gameSettings.pauseOnLostFocus;
@@ -657,7 +700,7 @@ public final class FlybyRunner {
public void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase != TickEvent.Phase.END) return;
final boolean clear = this.sceneClearRequested;
if (!clear && this.pendingRequest.get() == null && this.pendingDimensionPlayer.get() == null && this.sceneDeferred.isEmpty()) return;
if (!clear && this.pendingRequest.get() == null && this.pendingDimensionPlayer.get() == null && this.pendingBiomePlayer.get() == null && !this.survivalRestoreRequested && this.sceneDeferred.isEmpty()) return;
final MinecraftServer server = MinecraftServer.getServer();
if (server == null) return;
@@ -670,10 +713,23 @@ public final class FlybyRunner {
LOGGER.warn("Flyby: no server player named '{}', staying in the current dimension", traveller);
this.dimensionAbandoned = true;
} else {
server.getConfigurationManager().transferPlayerToDimension(player, SystemProperties.FLYBY_DIMENSION);
final int dimension = SystemProperties.FLYBY_DIMENSION;
final WorldServer target = server.worldServerForDimension(dimension);
server.getConfigurationManager().transferPlayerToDimension(player, dimension,
target.provider.hasNoSky ? target.getDefaultTeleporter() : new SurfaceTeleporter(target));
}
}
final String seeker = this.pendingBiomePlayer.getAndSet(null);
if (seeker != null) {
this.findBiome(server, seeker);
}
if (this.survivalRestoreRequested) {
this.survivalRestoreRequested = false;
this.restoreGameType(server);
}
if (clear) {
this.sceneClearRequested = false;
this.clearScene(server);
@@ -688,11 +744,104 @@ public final class FlybyRunner {
final double feetY = this.flightFeetY(server, request);
request.feetY().complete(feetY);
this.applyFreeze(server);
if (SystemProperties.FLYBY_SURVIVAL) {
if (this.survivalApplied) {
LOGGER.warn("Flyby: game mode restore pending for world '{}', flying without survival", this.survivalWorld);
} else {
this.applySurvival(server, request.playerName());
}
}
if (request.scene()) {
this.runScene(server, request, feetY);
}
}
private void applySurvival(MinecraftServer server, String playerName) {
final EntityPlayerMP player = server.getConfigurationManager().func_152612_a(playerName);
if (player == null) {
LOGGER.warn("Flyby: no server player named '{}', staying in the current game mode", playerName);
return;
}
this.survivalRestoreType = player.theItemInWorldManager.getGameType();
player.setGameType(WorldSettings.GameType.SURVIVAL);
player.capabilities.allowFlying = player.capabilities.isFlying = player.capabilities.disableDamage = true;
player.sendPlayerAbilities();
this.survivalApplied = true;
this.survivalPlayerName = playerName;
this.survivalWorld = server.getFolderName();
}
private void restoreGameType(MinecraftServer server) {
if (!this.survivalApplied) return;
final EntityPlayerMP player = server.getFolderName().equals(this.survivalWorld)
? server.getConfigurationManager().func_152612_a(this.survivalPlayerName) : null;
if (player == null) {
this.survivalRestoreRequested = true;
return;
}
player.setGameType(this.survivalRestoreType);
this.survivalApplied = false;
}
private void findBiome(MinecraftServer server, String playerName) {
final String name = SystemProperties.FLYBY_BIOME;
final EntityPlayerMP player = server.getConfigurationManager().func_152612_a(playerName);
if (player == null) {
LOGGER.warn("Flyby: no server player named '{}', ignoring biome '{}'", playerName, name);
this.biomeAbandoned = true;
return;
}
final BiomeGenBase target = BiomeAPI.findBiomeByName(name);
if (target == null) {
LOGGER.error("Flyby: unknown biome '{}'", name);
this.biomeAbandoned = true;
return;
}
final FlybyOrigin found = this.scanForBiome(player.worldObj.getWorldChunkManager(), target);
this.biomeScratch = null;
if (found == null) {
LOGGER.error("Flyby: biome '{}' not found within {} blocks", name, BIOME_SEARCH_CELLS * 4);
this.biomeAbandoned = true;
return;
}
LOGGER.info("Flyby: biome '{}' found at {} {}", name, found.x(), found.z());
this.biomeOrigin = found;
}
private FlybyOrigin scanForBiome(WorldChunkManager manager, BiomeGenBase target) {
FlybyOrigin found = this.scanStrip(manager, target, 0, 0, 0, 0);
for (int r = 1; found == null && r <= BIOME_SEARCH_CELLS; r++) {
found = this.scanStrip(manager, target, -r, -r, r, -r);
if (found == null) found = this.scanStrip(manager, target, -r, r, r, r);
if (found == null) found = this.scanStrip(manager, target, -r, 1 - r, -r, r - 1);
if (found == null) found = this.scanStrip(manager, target, r, 1 - r, r, r - 1);
}
return found;
}
private FlybyOrigin scanStrip(WorldChunkManager manager, BiomeGenBase target, int minX, int minZ, int maxX, int maxZ) {
final int half = BIOME_WINDOW / 2;
final int width = maxX - minX + BIOME_WINDOW;
this.biomeScratch = manager.getBiomesForGeneration(this.biomeScratch, minX - half, minZ - half, width, maxZ - minZ + BIOME_WINDOW);
for (int cz = minZ; cz <= maxZ; cz++) {
for (int cx = minX; cx <= maxX; cx++) {
if (this.windowMatches(target, width, minX - half, minZ - half, cx, cz)) return new FlybyOrigin(cx * 4 + 2, cz * 4 + 2, 0.0F);
}
}
return null;
}
private boolean windowMatches(BiomeGenBase target, int width, int originX, int originZ, int cx, int cz) {
final int half = BIOME_WINDOW / 2;
for (int dz = -half; dz <= half; dz++) {
final int row = (cz + dz - originZ) * width - originX + cx;
for (int dx = -half; dx <= half; dx++) {
if (this.biomeScratch[row + dx] != target) return false;
}
}
return true;
}
private void discardWorldChanges(MinecraftServer server) {
if (worldChangesDiscarded) return;
@@ -864,6 +1013,7 @@ public final class FlybyRunner {
private void finish(Minecraft mc, EntityClientPlayerMP player) {
this.state = State.SETTLE;
if (SystemProperties.FLYBY_SURVIVAL) this.survivalRestoreRequested = true;
this.tick = 0;
final long elapsedNs = System.nanoTime() - this.runStartNs;
Tracy.sectionLeave(this.legSection);
@@ -1140,6 +1290,7 @@ public final class FlybyRunner {
this.sceneClearRequested = this.sceneCommands.length > 0;
this.restorePacing(mc);
}
if (SystemProperties.FLYBY_SURVIVAL) this.survivalRestoreRequested = true;
}
public boolean startedFromProperties() {
@@ -1147,4 +1298,20 @@ public final class FlybyRunner {
}
private record FlybyRequest(double[] pathX, double[] pathZ, double originX, double originZ, double parkedFeetY, String playerName, boolean sampleTerrain, boolean scene, CompletableFuture<Double> feetY) {}
private static final class SurfaceTeleporter extends Teleporter {
private final WorldServer world;
SurfaceTeleporter(WorldServer world) {
super(world);
this.world = world;
}
@Override
public void placeInPortal(Entity entity, double x, double y, double z, float yaw) {
final int top = this.world.getTopSolidOrLiquidBlock(MathHelper.floor_double(entity.posX), MathHelper.floor_double(entity.posZ));
entity.setLocationAndAngles(entity.posX, top > 0 ? top : entity.posY, entity.posZ, entity.rotationYaw, entity.rotationPitch);
entity.motionX = entity.motionY = entity.motionZ = 0.0;
}
}
}
@@ -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.api.BlockLightProvider;
import com.gtnewhorizons.angelica.compat.spark.SparkKnownSources;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.config.SystemProperties;
@@ -641,6 +642,13 @@ public enum Mixins implements IMixins {
)
),
SPARK_MOD_SOURCES(new MixinBuilder("Mod versions in spark 1.7.10 profiles")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.SPARK)
.setApplyIf(SparkKnownSources::mixinApplies)
.addClientMixins("spark.MixinForge1710SparkPlugin")
),
ANGELICA_TEXTURE(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
@@ -892,6 +900,18 @@ public enum Mixins implements IMixins {
"MixinBlockTFMagicLeaves"
))
),
NOTFINE_BIOMES_O_PLENTY_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addRequiredMod(TargetedMod.BIOMES_O_PLENTY)
.addClientMixins(addPrefix("notfine.leaves.biomesoplenty.",
"MixinBlockBOPLeaves",
"MixinBlockBOPColorizedLeaves",
"MixinBlockBOPAppleLeaves",
"MixinBlockBOPPersimmonLeaves",
"MixinColorizedLeavesRenderer"
))
),
ET_FUTURUM_ELYTRA_CAPE(new MixinBuilder("Set custom item ID for elytra with cape texture")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.ET_FUTURUM_REQUIEM)
@@ -32,6 +32,7 @@ public enum TargetedMod implements ITargetMod {
OPENBLOCKS(null, "OpenBlocks"),
RAILCRAFT(null, "Railcraft"),
SECURITYCRAFT(null, "securitycraft"),
SPARK(null, "spark"),
STARMINER("jp.mc.ancientred.starminer.core.SMPlugin", "modJ_StarMiner"),
SUPERNOVA("com.mitchej123.supernova.core.SupernovaCore", "supernova"),
THAUMCRAFT(null, "Thaumcraft"),
@@ -134,29 +134,12 @@ public final class WeatherColumns {
return height[slotOf(x, z)];
}
public int hashAt(int x, int z) {
return hash[slotOf(x, z)];
}
public float temperatureAt(int x, int z, int y) {
final int slot = slotOf(x, z);
if (y <= 64) return temperatureBelow65[slot];
return temperatureAtReference[slot] - (y - TEMPERATURE_REFERENCE_Y) * TEMPERATURE_PER_BLOCK;
}
public int lightAt(WorldClient world, int x, int z, int y) {
final int slot = slotOf(x, z);
if (lightY[slot] != y) {
lightY[slot] = y;
light[slot] = world.getLightBrightnessForSkyBlocks(x, y, z, 0);
}
return light[slot];
}
public float jitterAt(int x, int z, int index) {
return jitter[slotOf(x, z) * 4 + index];
}
private int slotOf(int x, int z) {
return Math.floorMod(x, size) * size + Math.floorMod(z, size);
}
@@ -1,5 +1,7 @@
package com.gtnewhorizons.angelica.render;
import com.gtnewhorizons.angelica.api.weather.WeatherColumnProvider;
import com.gtnewhorizons.angelica.api.weather.WeatherColumnSink;
import com.gtnewhorizons.angelica.compat.dragonapi.DragonAPICompat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.SystemProperties;
@@ -27,27 +29,30 @@ import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.particle.EntityRainFX;
import net.minecraft.client.particle.EntitySmokeFX;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.biome.WorldChunkManager;
import net.minecraftforge.client.IRenderHandler;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Random;
/**
* Replaces EntityRenderer.renderRainSnow.
*
* <p>The whole grid is one instanced draw per precipitation type. Everything that varies per frame
* <p>The whole grid is one instanced draw per layer: vanilla rain, vanilla snow, and each provider layer. Everything that varies per frame
* (billboard axis, distance falloff, texture scroll, snow drift) happens in the vertex shader, and the
* instance buffer is only rebuilt when the camera changes block or {@link WeatherColumns} actually
* resamples something. Per-column data comes from that cache rather than being recomputed every frame.
* {@link WeatherColumnProvider} columns are collected from the provider instead, and re-collected only when its revision,
* the dimension, or the player's block X/Z changes.
*/
public final class WeatherRenderer {
@@ -63,17 +68,18 @@ public final class WeatherRenderer {
private static final long P_REBUILDS = Tracy.plotHandle("weather.rebuilds");
private static final long P_PARTICLE_MICROS = Tracy.plotHandle("weather.particleMicros");
private static final long P_PARTICLES_SPAWNED = Tracy.plotHandle("weather.particlesSpawned");
private static final long P_PROVIDER_COLUMNS = Tracy.plotHandle("weather.providerColumns");
private static final long P_PROVIDER_DRAWS = Tracy.plotHandle("weather.providerDraws");
private static final long P_PROVIDER_REBUILDS = Tracy.plotHandle("weather.providerRebuilds");
private static final long P_LEGACY_HANDLER = Tracy.plotHandle("weather.legacyHandler");
private static final WeatherColumns columns = new WeatherColumns();
private static float[] axisX;
private static float[] axisZ;
private static int instanceBuffer;
private static ByteBuffer instanceData;
private static int instanceCapacity;
private static int instanceCount;
private static int rainInstances;
private static final WeatherBatch VANILLA = new WeatherBatch();
private static final WeatherBatch PROVIDER = new WeatherBatch();
private static int builtRevision = Integer.MIN_VALUE;
private static int builtX = Integer.MIN_VALUE;
@@ -84,6 +90,32 @@ public final class WeatherRenderer {
private static int builtCameraY = Integer.MIN_VALUE;
private static boolean rebuiltThisFrame;
private static int builtProviderRevision = Integer.MIN_VALUE;
private static int builtProviderDimension = Integer.MIN_VALUE;
private static int builtProviderX = Integer.MIN_VALUE;
private static int builtProviderZ = Integer.MIN_VALUE;
private static boolean providerRebuiltThisFrame;
private static int frameQuads;
private static int frameDrawCalls;
private static int providerColumns;
private static int providerDraws;
private static boolean legacyHandler;
private static int entityBlockX;
private static int entityBlockY;
private static int entityBlockZ;
private static double cameraX;
private static double cameraY;
private static double cameraZ;
private static int cameraBlockY;
private static double viewPosX;
private static double viewPosZ;
private static int updateCount;
private static int radius;
private static float rainStrength;
private static float partialTicks;
private static final int PARTICLE_RADIUS = 10;
private static final Random particleRandom = new Random();
private static int rainSoundCounter;
@@ -181,180 +213,180 @@ public final class WeatherRenderer {
}
public static boolean render(float partialTicks) {
final Minecraft mc = Minecraft.getMinecraft();
final WorldClient world = mc.theWorld;
final long startNanos = System.nanoTime();
if (!AngelicaConfig.enableInstancedWeather) return false;
if (world.provider.getWeatherRenderer() != null) {
Tracy.plot(P_STRENGTH, 0.0);
report(0, 0, startNanos);
return false;
}
if (DragonAPICompat.forcingSeasonalSnow) {
report(0, 0, startNanos);
return false;
}
final float rainStrength = world.getRainStrength(partialTicks);
Tracy.plot(P_STRENGTH, rainStrength);
frameQuads = 0;
frameDrawCalls = 0;
providerColumns = 0;
providerDraws = 0;
legacyHandler = false;
final boolean handled = renderFrame(partialTicks);
report(startNanos);
return handled;
}
final int radius = SettingsManager.downfallDistance;
if (rainStrength <= 0.0F || radius <= 0) {
report(0, 0, startNanos);
private static boolean renderFrame(float partialTicks) {
final Minecraft mc = Minecraft.getMinecraft();
final WorldClient world = mc.theWorld;
final IRenderHandler handler = world.provider.getWeatherRenderer();
final WeatherColumnProvider provider;
if (handler == null) {
if (DragonAPICompat.forcingSeasonalSnow) return false;
provider = null;
} else if (handler instanceof WeatherColumnProvider columnProvider) {
provider = columnProvider;
} else {
return useLegacyHandler();
}
rainStrength = provider == null || provider.rendersVanillaWeather() ? world.getRainStrength(partialTicks) : 0.0F;
radius = SettingsManager.downfallDistance;
final boolean vanillaActive = rainStrength > 0.0F && radius > 0;
if (provider == null && !vanillaActive) {
Tracy.plot(P_STRENGTH, rainStrength);
return true;
}
buildAxisTable();
final EntityLivingBase view = mc.renderViewEntity;
final int entityBlockX = MathHelper.floor_double(view.posX);
final int entityBlockY = MathHelper.floor_double(view.posY);
final int entityBlockZ = MathHelper.floor_double(view.posZ);
final double cameraX = view.lastTickPosX + (view.posX - view.lastTickPosX) * partialTicks;
final double cameraY = view.lastTickPosY + (view.posY - view.lastTickPosY) * partialTicks;
final double cameraZ = view.lastTickPosZ + (view.posZ - view.lastTickPosZ) * partialTicks;
final int cameraBlockY = MathHelper.floor_double(cameraY);
final int updateCount = ((EntityRendererAccessor) mc.entityRenderer).getRendererUpdateCount();
columns.update(world, entityBlockX, entityBlockZ, radius, updateCount);
WeatherRenderer.partialTicks = partialTicks;
entityBlockX = MathHelper.floor_double(view.posX);
entityBlockY = MathHelper.floor_double(view.posY);
entityBlockZ = MathHelper.floor_double(view.posZ);
cameraX = view.lastTickPosX + (view.posX - view.lastTickPosX) * partialTicks;
cameraY = view.lastTickPosY + (view.posY - view.lastTickPosY) * partialTicks;
cameraZ = view.lastTickPosZ + (view.posZ - view.lastTickPosZ) * partialTicks;
cameraBlockY = MathHelper.floor_double(cameraY);
viewPosX = view.posX;
viewPosZ = view.posZ;
updateCount = ((EntityRendererAccessor) mc.entityRenderer).getRendererUpdateCount();
final DeferredWorldRenderingPipeline deferred = deferredPipeline();
mc.entityRenderer.enableLightmap(partialTicks);
GLSMRasterizer.disableCull();
GLSMCurrentAttribs.glNormal3f(0.0F, 1.0F, 0.0F);
GLSMBlend.enableBlend();
GLSMBlend.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLSMDepthStencilAlpha.glAlphaFunc(GL11.GL_GREATER, 0.1F);
GLSMCurrentAttribs.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
if (deferred != null && !deferred.supportsInstancing(Instancing.WEATHER)) {
renderTessellator(mc, world, view, partialTicks, rainStrength, radius, updateCount,
entityBlockX, entityBlockY, entityBlockZ, cameraX, cameraY, cameraZ, cameraBlockY, startNanos);
return true;
final boolean instanced;
if (deferred == null) {
instanced = true;
} else if (deferred.supportsInstancing(Instancing.WEATHER)) {
deferred.rebindCurrentPass();
instanced = deferred.hasInstancedVariant(Instancing.WEATHER);
} else {
instanced = false;
}
if (provider != null && !instanced) {
exitState(mc, partialTicks);
return useLegacyHandler();
}
Tracy.plot(P_STRENGTH, rainStrength);
if (vanillaActive) {
buildAxisTable();
columns.update(world, entityBlockX, entityBlockZ, radius, updateCount);
if (instanced) {
final boolean rebuild = SystemProperties.WEATHER_REBUILD_ALWAYS
|| updateCount - builtUpdateCount >= REBUILD_TICKS
|| columns.revision() != builtRevision
|| entityBlockX != builtX || entityBlockY != builtY || entityBlockZ != builtZ
|| cameraBlockY != builtCameraY
|| radius != builtRadius;
if (rebuild) {
VANILLA.begin(entityBlockX, entityBlockZ, WeatherInstancedAttribs.SOURCE_VANILLA);
VANILLA.beginLayer(RAIN_TEXTURE, WeatherInstancedAttribs.RAIN_PERIOD, 0.0F, 0.0F, WeatherInstancedAttribs.RAIN_FADE);
writeColumns(world, true);
VANILLA.beginLayer(SNOW_TEXTURE, WeatherInstancedAttribs.SNOW_PERIOD, WeatherInstancedAttribs.SNOW_U_DRIFT, WeatherInstancedAttribs.SNOW_V_DRIFT, WeatherInstancedAttribs.SNOW_FADE);
writeColumns(world, false);
VANILLA.upload();
builtCameraY = cameraBlockY;
builtRevision = columns.revision();
builtX = entityBlockX;
builtY = entityBlockY;
builtZ = entityBlockZ;
builtRadius = radius;
builtUpdateCount = updateCount;
}
rebuiltThisFrame = rebuild;
} else {
renderTessellator(mc, world);
}
}
renderInstanced(mc, world, view, deferred, partialTicks, rainStrength, radius, updateCount,
entityBlockX, entityBlockY, entityBlockZ, cameraX, cameraY, cameraZ, cameraBlockY, startNanos);
int providerTick = 0;
float providerAge = 0.0F;
float providerInvRadius = 0.0F;
if (provider != null) {
providerTick = provider.beginFrame(world, view, partialTicks);
final int revision = provider.columnRevision();
final float fadeRadius = provider.fadeRadius();
if (!Float.isFinite(fadeRadius) || fadeRadius <= 0.0F) {
throw new IllegalArgumentException("Weather fade radius must be finite and positive");
}
final int dimension = world.provider.dimensionId;
if (SystemProperties.WEATHER_REBUILD_ALWAYS
|| revision != builtProviderRevision || dimension != builtProviderDimension
|| entityBlockX != builtProviderX || entityBlockZ != builtProviderZ) {
PROVIDER.begin(entityBlockX, entityBlockZ, WeatherInstancedAttribs.SOURCE_PROVIDER);
provider.collectColumns(PROVIDER);
PROVIDER.upload();
builtProviderRevision = revision;
builtProviderDimension = dimension;
builtProviderX = entityBlockX;
builtProviderZ = entityBlockZ;
providerRebuiltThisFrame = true;
}
providerAge = providerTick + partialTicks;
providerInvRadius = 1.0F / fadeRadius;
}
if (instanced && (vanillaActive && VANILLA.count > 0 || provider != null && PROVIDER.count > 0)) {
WeatherParams.set((float) (entityBlockX - cameraX), (float) -cameraY, (float) (entityBlockZ - cameraZ),
radius > 0 ? 1.0F / radius : 0.0F,
(float) (viewPosX - entityBlockX), (float) (viewPosZ - entityBlockZ), partialTicks,
updateCount + partialTicks,
updateCount, rainStrength, providerTick, providerAge, providerInvRadius);
GLSMVertexInput.glBindVertexArray(WeatherQuadMesh.vao());
if (deferred == null) GLSMPrograms.setFfpInstancing(Instancing.WEATHER);
if (vanillaActive) {
frameDrawCalls += VANILLA.draw(mc, deferred);
frameQuads += VANILLA.count;
}
if (provider != null) {
providerDraws = PROVIDER.draw(mc, deferred);
providerColumns = PROVIDER.count;
}
if (deferred == null) GLSMPrograms.setFfpInstancing(Instancing.NONE);
GLSMVertexInput.glBindVertexArray(0);
if (deferred != null) deferred.rebindCurrentPass();
}
exitState(mc, partialTicks);
return true;
}
private static boolean useLegacyHandler() {
Tracy.plot(P_STRENGTH, 0.0);
legacyHandler = true;
return false;
}
private static void exitState(Minecraft mc, float partialTicks) {
GLSMRasterizer.enableCull();
GLSMBlend.disableBlend();
GLSMDepthStencilAlpha.glAlphaFunc(GL11.GL_GREATER, 0.1F);
mc.entityRenderer.disableLightmap(partialTicks);
}
private static DeferredWorldRenderingPipeline deferredPipeline() {
if (!AngelicaConfig.enableIris || !Iris.enabled) return null;
return Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline pipeline
? pipeline : null;
}
private static void renderInstanced(Minecraft mc, WorldClient world, EntityLivingBase view,
DeferredWorldRenderingPipeline deferred, float partialTicks, float rainStrength, int radius,
int updateCount, int entityBlockX, int entityBlockY, int entityBlockZ, double cameraX,
double cameraY, double cameraZ, int cameraBlockY, long startNanos) {
final boolean rebuild = SystemProperties.WEATHER_REBUILD_ALWAYS
|| updateCount - builtUpdateCount >= REBUILD_TICKS
|| columns.revision() != builtRevision
|| entityBlockX != builtX || entityBlockY != builtY || entityBlockZ != builtZ
|| cameraBlockY != builtCameraY
|| radius != builtRadius;
if (rebuild) {
rebuildInstances(world, radius, entityBlockX, entityBlockY, entityBlockZ, cameraBlockY);
builtCameraY = cameraBlockY;
builtRevision = columns.revision();
builtX = entityBlockX;
builtY = entityBlockY;
builtZ = entityBlockZ;
builtRadius = radius;
builtUpdateCount = updateCount;
}
rebuiltThisFrame = rebuild;
if (instanceCount == 0) {
report(0, 0, startNanos);
return;
}
if (deferred != null) {
deferred.rebindCurrentPass();
if (!deferred.hasInstancedVariant(Instancing.WEATHER)) {
renderTessellator(mc, world, view, partialTicks, rainStrength, radius, updateCount,
entityBlockX, entityBlockY, entityBlockZ, cameraX, cameraY, cameraZ, cameraBlockY, startNanos);
return;
}
}
WeatherParams.set((float) (entityBlockX - cameraX), (float) -cameraY, (float) (entityBlockZ - cameraZ),
1.0F / radius,
(float) (view.posX - entityBlockX), (float) (view.posZ - entityBlockZ), partialTicks,
updateCount + partialTicks,
updateCount & 31, updateCount & 511, rainStrength);
mc.entityRenderer.enableLightmap(partialTicks);
GLSMRasterizer.disableCull();
GLSMCurrentAttribs.glNormal3f(0.0F, 1.0F, 0.0F);
GLSMBlend.enableBlend();
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
GLSMDepthStencilAlpha.glAlphaFunc(GL11.GL_GREATER, 0.1F);
GLSMCurrentAttribs.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
int drawCalls = 0;
drawCalls += drawRange(mc, deferred, RAIN_TEXTURE, 0, rainInstances);
drawCalls += drawRange(mc, deferred, SNOW_TEXTURE, rainInstances, instanceCount - rainInstances);
GLSMRasterizer.enableCull();
GLSMBlend.disableBlend();
GLSMDepthStencilAlpha.glAlphaFunc(GL11.GL_GREATER, 0.1F);
mc.entityRenderer.disableLightmap(partialTicks);
report(instanceCount, drawCalls, startNanos);
}
private static int drawRange(Minecraft mc, DeferredWorldRenderingPipeline deferred,
ResourceLocation texture, int first, int count) {
if (count <= 0) return 0;
mc.getTextureManager().bindTexture(texture);
if (deferred != null) {
deferred.rebindCurrentPass();
if (!deferred.hasInstancedVariant(Instancing.WEATHER)) return 0;
deferred.bindInstancedVariant(Instancing.WEATHER);
}
GLSMVertexInput.glBindVertexArray(WeatherQuadMesh.vao());
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceBuffer);
WeatherInstancedAttribs.pointInstanceAttribs((long) first * WeatherInstancedAttribs.STRIDE);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
VAOManager.setCurrentVertexFlags(WeatherQuadMesh.VERTEX_FLAGS);
if (deferred == null) GLSMPrograms.setFfpInstancing(Instancing.WEATHER);
GLSMDraws.glDrawArraysInstanced(GL11.GL_TRIANGLE_FAN, 0, WeatherQuadMesh.VERTEX_COUNT, count);
GLSMPrograms.setFfpInstancing(Instancing.NONE);
GLSMVertexInput.glBindVertexArray(0);
if (deferred != null) deferred.rebindCurrentPass();
return 1;
}
private static void rebuildInstances(WorldClient world, int radius, int entityBlockX, int entityBlockY,
int entityBlockZ, int cameraBlockY) {
final int slots = (radius * 2 + 1) * (radius * 2 + 1);
final int stride = WeatherInstancedAttribs.STRIDE;
if (instanceCapacity < slots) {
instanceCapacity = slots;
instanceData = ByteBuffer.allocateDirect(slots * stride).order(ByteOrder.nativeOrder());
if (instanceBuffer == 0) instanceBuffer = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceBuffer);
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, (long) slots * stride, GL15.GL_DYNAMIC_DRAW);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
WeatherQuadMesh.bindInstanceBuffer(instanceBuffer);
}
int count = writeColumns(world, radius, entityBlockX, entityBlockY, entityBlockZ, cameraBlockY, true, 0);
rainInstances = count;
count = writeColumns(world, radius, entityBlockX, entityBlockY, entityBlockZ, cameraBlockY, false, count);
instanceCount = count;
if (count == 0) return;
instanceData.position(0);
instanceData.limit(count * stride);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceBuffer);
GLSMBuffers.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0L, instanceData);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
instanceData.clear();
}
private static int writeColumns(WorldClient world, int radius, int entityBlockX, int entityBlockY,
int entityBlockZ, int cameraBlockY, boolean wantRain, int count) {
final int stride = WeatherInstancedAttribs.STRIDE;
private static void writeColumns(WorldClient world, boolean wantRain) {
final WorldChunkManager chunkManager = world.getWorldChunkManager();
for (int z = entityBlockZ - radius; z <= entityBlockZ + radius; z++) {
for (int x = entityBlockX - radius; x <= entityBlockX + radius; x++) {
@@ -371,68 +403,48 @@ public final class WeatherRenderer {
if (rain != wantRain) continue;
final int lightY = Math.max(precipitationHeight, cameraBlockY);
int brightness = columns.lightAtSlot(world, slot, x, z, lightY);
if (!rain) brightness = (brightness * 3 + 15728880) / 4;
final int light = columns.lightAtSlot(world, slot, x, z, lightY);
final int base = count * stride;
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_COLUMN_SPAN, x - entityBlockX);
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_COLUMN_SPAN + 4, z - entityBlockZ);
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_COLUMN_SPAN + 8, bottom);
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_COLUMN_SPAN + 12, top);
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_JITTER, columns.jitterAtSlot(slot, 0));
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_JITTER + 4, columns.jitterAtSlot(slot, 1));
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_JITTER + 8, columns.jitterAtSlot(slot, 2));
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_JITTER + 12, columns.jitterAtSlot(slot, 3));
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_PARAMS, brightness & 0xFFFF);
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_PARAMS + 4, brightness >> 16 & 0xFFFF);
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_PARAMS + 8, columns.hashAtSlot(slot) & 31);
instanceData.putFloat(base + WeatherInstancedAttribs.OFFSET_PARAMS + 12, rain ? 0.0F : 1.0F);
count++;
final float j0 = columns.jitterAtSlot(slot, 0);
VANILLA.put(x, z, bottom, top,
rain ? 0.0F : j0, rain ? 0.0F : columns.jitterAtSlot(slot, 1),
rain ? 0.0F : columns.jitterAtSlot(slot, 2), rain ? 0.0F : columns.jitterAtSlot(slot, 3),
rain ? light : snowBrightness(light), rain ? columns.hashAtSlot(slot) : 0,
rain ? WeatherInstancedAttribs.RAIN_SCROLL_BASE + j0 : 1.0F,
1.0F, 1.0F, 1.0F, 1.0F);
}
}
return count;
}
private static void renderTessellator(Minecraft mc, WorldClient world, EntityLivingBase view,
float partialTicks, float rainStrength, int radius, int updateCount, int entityBlockX,
int entityBlockY, int entityBlockZ, double cameraX, double cameraY, double cameraZ,
int cameraBlockY, long startNanos) {
mc.entityRenderer.enableLightmap(partialTicks);
private static void renderTessellator(Minecraft mc, WorldClient world) {
final float age = updateCount + partialTicks;
GLSMRasterizer.disableCull();
GLSMCurrentAttribs.glNormal3f(0.0F, 1.0F, 0.0F);
GLSMBlend.enableBlend();
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
GLSMDepthStencilAlpha.glAlphaFunc(GL11.GL_GREATER, 0.1F);
GLSMCurrentAttribs.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
final Tessellator tessellator = Tessellator.instance;
final WorldChunkManager chunkManager = world.getWorldChunkManager();
int bound = -1;
int quads = 0;
int drawCalls = 0;
for (int z = entityBlockZ - radius; z <= entityBlockZ + radius; z++) {
for (int x = entityBlockX - radius; x <= entityBlockX + radius; x++) {
if (!columns.precipitates(x, z)) continue;
final int slot = columns.slotAt(x, z);
if (!columns.precipitatesAtSlot(slot)) continue;
final int precipitationHeight = columns.heightAt(x, z);
final int precipitationHeight = columns.heightAtSlot(slot);
final int bottom = Math.max(entityBlockY - radius, precipitationHeight);
final int top = Math.max(entityBlockY + radius, precipitationHeight);
if (bottom == top) continue;
final int lightY = Math.max(precipitationHeight, cameraBlockY);
final float temperature = columns.temperatureAt(x, z, bottom);
final boolean rain = world.getWorldChunkManager()
.getTemperatureAtHeight(temperature, precipitationHeight) >= 0.15F;
final int light = columns.lightAtSlot(world, slot, x, z, lightY);
final float temperature = columns.temperatureAtSlot(slot, bottom);
final boolean rain = chunkManager.getTemperatureAtHeight(temperature, precipitationHeight) >= 0.15F;
final int axis = (z - entityBlockZ + 16) * 32 + x - entityBlockX + 16;
final float halfX = axisX[axis] * 0.5F;
final float halfZ = axisZ[axis] * 0.5F;
final double offsetX = (x + 0.5F) - view.posX;
final double offsetZ = (z + 0.5F) - view.posZ;
final double offsetX = (x + 0.5F) - viewPosX;
final double offsetZ = (z + 0.5F) - viewPosZ;
final float distance = MathHelper.sqrt_double(offsetX * offsetX + offsetZ * offsetZ) / radius;
if (rain) {
@@ -445,13 +457,12 @@ public final class WeatherRenderer {
mc.getTextureManager().bindTexture(RAIN_TEXTURE);
tessellator.startDrawingQuads();
}
final float scroll = ((updateCount + columns.hashAt(x, z) & 31) + partialTicks) / 32.0F
* (3.0F + columns.jitterAt(x, z, 0));
tessellator.setBrightness(columns.lightAt(world, x, z, lightY));
final float scroll = ((updateCount + columns.hashAtSlot(slot) & WeatherInstancedAttribs.RAIN_PERIOD - 1) + partialTicks)
/ WeatherInstancedAttribs.RAIN_PERIOD * (WeatherInstancedAttribs.RAIN_SCROLL_BASE + columns.jitterAtSlot(slot, 0));
tessellator.setBrightness(light);
tessellator.setColorRGBA_F(1.0F, 1.0F, 1.0F,
((1.0F - distance * distance) * 0.5F + 0.5F) * rainStrength);
emit(tessellator, x, z, bottom, top, halfX, halfZ, scroll, 0.0F, 0.0F,
cameraX, cameraY, cameraZ);
((1.0F - distance * distance) * WeatherInstancedAttribs.RAIN_FADE + 0.5F) * rainStrength);
emit(tessellator, x, z, bottom, top, halfX, halfZ, scroll, 0.0F, 0.0F);
} else {
if (bound != 1) {
if (bound == 0) {
@@ -462,14 +473,14 @@ public final class WeatherRenderer {
mc.getTextureManager().bindTexture(SNOW_TEXTURE);
tessellator.startDrawingQuads();
}
final float scroll = ((updateCount & 511) + partialTicks) / 512.0F;
final float driftU = columns.jitterAt(x, z, 0) + age * 0.01F * columns.jitterAt(x, z, 1);
final float driftV = columns.jitterAt(x, z, 2) + age * columns.jitterAt(x, z, 3) * 0.001F;
tessellator.setBrightness((columns.lightAt(world, x, z, lightY) * 3 + 15728880) / 4);
final float scroll = ((updateCount & WeatherInstancedAttribs.SNOW_PERIOD - 1) + partialTicks)
/ WeatherInstancedAttribs.SNOW_PERIOD;
final float driftU = columns.jitterAtSlot(slot, 0) + age * WeatherInstancedAttribs.SNOW_U_DRIFT * columns.jitterAtSlot(slot, 1);
final float driftV = columns.jitterAtSlot(slot, 2) + age * columns.jitterAtSlot(slot, 3) * WeatherInstancedAttribs.SNOW_V_DRIFT;
tessellator.setBrightness(snowBrightness(light));
tessellator.setColorRGBA_F(1.0F, 1.0F, 1.0F,
((1.0F - distance * distance) * 0.3F + 0.5F) * rainStrength);
emit(tessellator, x, z, bottom, top, halfX, halfZ, scroll, driftU, driftV,
cameraX, cameraY, cameraZ);
((1.0F - distance * distance) * WeatherInstancedAttribs.SNOW_FADE + 0.5F) * rainStrength);
emit(tessellator, x, z, bottom, top, halfX, halfZ, scroll, driftU, driftV);
}
quads++;
}
@@ -480,16 +491,16 @@ public final class WeatherRenderer {
drawCalls++;
}
GLSMRasterizer.enableCull();
GLSMBlend.disableBlend();
GLSMDepthStencilAlpha.glAlphaFunc(GL11.GL_GREATER, 0.1F);
mc.entityRenderer.disableLightmap(partialTicks);
frameQuads += quads;
frameDrawCalls += drawCalls;
}
report(quads, drawCalls, startNanos);
private static int snowBrightness(int light) {
return (light * 3 + 15728880) / 4;
}
private static void emit(Tessellator tessellator, int x, int z, int bottom, int top, float halfX,
float halfZ, float scroll, float driftU, float driftV, double cameraX, double cameraY, double cameraZ) {
float halfZ, float scroll, float driftU, float driftV) {
tessellator.setTranslation(-cameraX, -cameraY, -cameraZ);
tessellator.addVertexWithUV(x - halfX + 0.5D, bottom, z - halfZ + 0.5D, driftU, bottom / 4.0F + scroll + driftV);
tessellator.addVertexWithUV(x + halfX + 0.5D, bottom, z + halfZ + 0.5D, 1.0F + driftU, bottom / 4.0F + scroll + driftV);
@@ -498,12 +509,17 @@ public final class WeatherRenderer {
tessellator.setTranslation(0.0D, 0.0D, 0.0D);
}
private static void report(int quads, int drawCalls, long startNanos) {
Tracy.plotInt(P_QUADS, quads);
Tracy.plotInt(P_DRAW_CALLS, drawCalls);
private static void report(long startNanos) {
Tracy.plotInt(P_QUADS, frameQuads);
Tracy.plotInt(P_DRAW_CALLS, frameDrawCalls);
Tracy.plotInt(P_REBUILDS, rebuiltThisFrame ? 1 : 0);
Tracy.plotInt(P_PROVIDER_COLUMNS, providerColumns);
Tracy.plotInt(P_PROVIDER_DRAWS, providerDraws);
Tracy.plotInt(P_PROVIDER_REBUILDS, providerRebuiltThisFrame ? 1 : 0);
Tracy.plotInt(P_LEGACY_HANDLER, legacyHandler ? 1 : 0);
Tracy.plot(P_MICROS, (System.nanoTime() - startNanos) / 1000.0);
rebuiltThisFrame = false;
providerRebuiltThisFrame = false;
}
private static void buildAxisTable() {
@@ -522,14 +538,145 @@ public final class WeatherRenderer {
}
public static void clear() {
VANILLA.release();
PROVIDER.release();
WeatherQuadMesh.delete();
if (instanceBuffer != 0) {
GLSMBuffers.glDeleteBuffers(instanceBuffer);
instanceBuffer = 0;
builtRevision = Integer.MIN_VALUE;
builtX = Integer.MIN_VALUE;
builtY = Integer.MIN_VALUE;
builtZ = Integer.MIN_VALUE;
builtRadius = -1;
builtUpdateCount = Integer.MIN_VALUE;
builtCameraY = Integer.MIN_VALUE;
builtProviderRevision = Integer.MIN_VALUE;
builtProviderDimension = Integer.MIN_VALUE;
builtProviderX = Integer.MIN_VALUE;
builtProviderZ = Integer.MIN_VALUE;
}
private static final class WeatherBatch implements WeatherColumnSink {
private static final int STRIDE = WeatherInstancedAttribs.STRIDE;
private static final int MIN_CAPACITY = 256;
private ByteBuffer data;
private int capacity;
private int count;
private ResourceLocation[] textures = new ResourceLocation[4];
private int[] firsts = new int[4];
private int layers;
private int originX;
private int originZ;
private float source;
private int period;
private float uDriftScale;
private float vDriftScale;
private float fadeScale;
private int buffer;
private int bufferCapacity;
void begin(int originX, int originZ, float source) {
this.originX = originX;
this.originZ = originZ;
this.source = source;
Arrays.fill(textures, 0, layers, null);
layers = 0;
count = 0;
}
void upload() {
if (count == 0) return;
if (buffer == 0) buffer = GLSMBuffers.glGenBuffers();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer);
if (bufferCapacity < count) {
bufferCapacity = capacity;
GLSMBuffers.glBufferData(GL15.GL_ARRAY_BUFFER, (long) bufferCapacity * STRIDE, GL15.GL_DYNAMIC_DRAW);
}
data.position(0);
data.limit(count * STRIDE);
GLSMBuffers.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0L, data);
data.clear();
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
int draw(Minecraft mc, DeferredWorldRenderingPipeline deferred) {
int draws = 0;
for (int layer = 0; layer < layers; layer++) {
final int first = firsts[layer];
final int layerCount = (layer + 1 < layers ? firsts[layer + 1] : count) - first;
if (layerCount == 0) continue;
mc.getTextureManager().bindTexture(textures[layer]);
if (deferred != null) deferred.bindInstancedVariant(Instancing.WEATHER);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer);
WeatherInstancedAttribs.pointInstanceAttribs((long) first * STRIDE);
GLSMBuffers.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
VAOManager.setCurrentVertexFlags(WeatherQuadMesh.VERTEX_FLAGS);
GLSMDraws.glDrawArraysInstanced(GL11.GL_TRIANGLE_FAN, 0, WeatherQuadMesh.VERTEX_COUNT, layerCount);
draws++;
}
return draws;
}
void release() {
begin(0, 0, 0.0F);
data = null;
capacity = 0;
if (buffer != 0) {
GLSMBuffers.glDeleteBuffers(buffer);
buffer = 0;
}
bufferCapacity = 0;
}
@Override
public void beginLayer(ResourceLocation texture, int scrollPeriod, float uDriftScale, float vDriftScale, float fadeScale) {
if (texture == null) throw new IllegalArgumentException("Weather layer texture must not be null");
if (scrollPeriod < 1 || scrollPeriod > WeatherParams.TICK_WRAP || (scrollPeriod & scrollPeriod - 1) != 0) {
throw new IllegalArgumentException("Weather layer scroll period must be a power of two between 1 and " + WeatherParams.TICK_WRAP);
}
if (!Float.isFinite(uDriftScale) || !Float.isFinite(vDriftScale) || !Float.isFinite(fadeScale)) {
throw new IllegalArgumentException("Weather layer scales must be finite");
}
if (layers == textures.length) {
textures = Arrays.copyOf(textures, layers * 2);
firsts = Arrays.copyOf(firsts, layers * 2);
}
textures[layers] = texture;
firsts[layers] = count;
layers++;
period = scrollPeriod;
this.uDriftScale = uDriftScale;
this.vDriftScale = vDriftScale;
this.fadeScale = fadeScale;
}
@Override
public void column(int x, int z, float bottom, float top, float uBase, float uDrift, float vBase, float vDrift,
int brightness, int scrollOffset, float scrollScale, float r, float g, float b, float a) {
if (layers == 0) throw new IllegalStateException("Weather column written before beginLayer");
if (brightness < 0 || brightness >= 1 << 24) {
throw new IllegalArgumentException("Weather column brightness must be a packed lightmap coordinate below 2^24");
}
put(x, z, bottom, top, uBase, uDrift, vBase, vDrift, brightness, scrollOffset, scrollScale, r, g, b, a);
}
void put(int x, int z, float bottom, float top, float uBase, float uDrift, float vBase, float vDrift, int brightness, int scrollOffset, float scrollScale, float r, float g, float b, float a) {
if (count == capacity) grow();
WeatherInstancedAttribs.putColumn(data, count, x - originX, z - originZ, bottom, top, uBase, uDrift, vBase, vDrift, brightness, scrollOffset, scrollScale, source, r, g, b, a, period, uDriftScale, vDriftScale, fadeScale);
count++;
}
private void grow() {
final int grownCapacity = Math.max(MIN_CAPACITY, capacity * 2);
final ByteBuffer grown = ByteBuffer.allocateDirect(grownCapacity * STRIDE).order(ByteOrder.nativeOrder());
if (count > 0) {
data.position(0);
data.limit(count * STRIDE);
grown.put(data);
grown.clear();
}
data = grown;
capacity = grownCapacity;
}
instanceData = null;
instanceCapacity = 0;
instanceCount = 0;
rainInstances = 0;
}
}
@@ -14,12 +14,16 @@ import com.gtnewhorizons.angelica.rendering.celeritas.iris.ContextAwareChunkVert
import com.gtnewhorizons.angelica.rendering.celeritas.threading.RenderPassHelper;
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
import com.gtnewhorizons.angelica.rendering.tesr.SectionBuiltBlockEntity;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.utils.NaturalTextureUtils;
import com.gtnewhorizons.liquidlogged.api.LiquidLoggedAPI;
import com.gtnewhorizons.liquidlogged.api.LiquidLoggedRenderAPI;
import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
import it.unimi.dsi.fastutil.floats.FloatArrayList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import mega.fluidlogged.api.FLBlockAccess;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.block_rendering.NbtConditionalIdMap;
@@ -39,6 +43,7 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ReportedException;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.fluids.Fluid;
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildBuffers;
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildContext;
@@ -254,39 +259,13 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
final boolean canRenderOffThread = !handoff || !isDeferred(deferredMask, x, y, z);
// Check for shader pack override
final BlockRenderLayer override = blockTypeIds != null ? blockTypeIds.get(block) : null;
int deferredPasses = 0;
Material deferredOverride = null;
renderBlockPasses(block, meta, x, y, z, canRenderOffThread, blockTypeIds, deferredBlocks, tessellator, renderBlocks, buffers, buildContext, blockRenderContext, minX, minY, minZ, teMap, currentTick);
for (int pass = 0; pass < AngelicaChunkBuildContext.NUM_PASSES; pass++) {
final boolean canRender;
Material materialOverride = null;
if (override != null) {
// Shader pack override controls both pass and material
canRender = (pass == override.toVanillaPass());
if (canRender) {
materialOverride = buffers.getRenderPassConfiguration().getMaterialForRenderType(override);
}
} else {
// Normal block rendering
canRender = !canRenderOffThread || block.canRenderInPass(pass);
}
if (canRender) {
final boolean isShaderPackOverride = materialOverride != null;
if (!canRenderOffThread) {
deferredPasses |= 1 << pass;
deferredOverride = materialOverride;
continue;
}
renderBlock(block, meta, x, y, z, pass, tessellator, renderBlocks, buffers, buildContext, blockRenderContext, minX, minY, minZ, materialOverride, isShaderPackOverride, teMap, currentTick);
}
}
if (deferredPasses != 0) {
deferredBlocks.add(new DeferredBlock(x, y, z, block, meta, deferredPasses, deferredOverride));
// FluidLogged stores the fluid apart from the block, and this loop only sees the block
final Block loggedFluid = getLoggedFluidBlock(region, x, y, z);
if (loggedFluid != null) {
if (reportingSlice != null) reportingSlice.setRenderingBlock(loggedFluid);
renderBlockPasses(loggedFluid, 0, x, y, z, canRenderOffThread, blockTypeIds, deferredBlocks, tessellator, renderBlocks, buffers, buildContext, blockRenderContext, minX, minY, minZ, teMap, currentTick);
}
if (block.isOpaqueCube()) {
@@ -345,7 +324,11 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
final Block block = region.getBlock(minX + x, minY + y, minZ + z);
if (block != Blocks.air && !canRenderOffThread(block)) {
if (block == Blocks.air) {
continue;
}
final Block loggedFluid = getLoggedFluidBlock(region, minX + x, minY + y, minZ + z);
if (!canRenderOffThread(block) || (loggedFluid != null && !canRenderOffThread(loggedFluid))) {
final int bit = (y << 8) | (z << 4) | x;
mask[bit >>> 6] |= 1L << bit;
any = true;
@@ -361,6 +344,18 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
return (mask[bit >>> 6] & (1L << bit)) != 0;
}
private static Block getLoggedFluidBlock(IBlockAccess region, int x, int y, int z) {
final Fluid fluid;
if (ModStatus.isLiquidLoggedLoaded) {
fluid = LiquidLoggedAPI.getFluid(region, x, y, z);
} else if (ModStatus.isFluidLoggedLoaded && region instanceof FLBlockAccess fluidAccess) {
fluid = fluidAccess.fl$getFluid(x, y, z);
} else {
return null;
}
return fluid != null ? fluid.getBlock() : null;
}
ChunkJobResult<ChunkBuildOutput> captureDeferred(DeferredSectionMesh m) {
final long start = System.nanoTime();
@@ -497,6 +492,43 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
return new ChunkBuildOutput(this.render, renderData, meshes, this.buildTime);
}
private void renderBlockPasses(Block block, int meta, int x, int y, int z, boolean canRenderOffThread, Map<Block, BlockRenderLayer> blockTypeIds, List<DeferredBlock> deferredBlocks, Tessellator tessellator, RenderBlocks renderBlocks, ChunkBuildBuffers buffers, AngelicaChunkBuildContext buildContext, BlockRenderContext blockRenderContext, int originX, int originY, int originZ, NbtConditionalIdMap<Block> teMap, long currentTick) {
// Check for shader pack override
final BlockRenderLayer override = blockTypeIds != null ? blockTypeIds.get(block) : null;
int deferredPasses = 0;
Material deferredOverride = null;
for (int pass = 0; pass < AngelicaChunkBuildContext.NUM_PASSES; pass++) {
final boolean canRender;
Material materialOverride = null;
if (override != null) {
// Shader pack override controls both pass and material
canRender = (pass == override.toVanillaPass());
if (canRender) {
materialOverride = buffers.getRenderPassConfiguration().getMaterialForRenderType(override);
}
} else {
// Normal block rendering
canRender = !canRenderOffThread || block.canRenderInPass(pass);
}
if (canRender) {
final boolean isShaderPackOverride = materialOverride != null;
if (!canRenderOffThread) {
deferredPasses |= 1 << pass;
deferredOverride = materialOverride;
continue;
}
renderBlock(block, meta, x, y, z, pass, tessellator, renderBlocks, buffers, buildContext, blockRenderContext, originX, originY, originZ, materialOverride, isShaderPackOverride, teMap, currentTick);
}
}
if (deferredPasses != 0) {
deferredBlocks.add(new DeferredBlock(x, y, z, block, meta, deferredPasses, deferredOverride));
}
}
protected void renderBlock(Block block, int metadata, int x, int y, int z, int pass, Tessellator tessellator, RenderBlocks renderBlocks, ChunkBuildBuffers buffers, AngelicaChunkBuildContext buildContext, BlockRenderContext blockRenderContext, int originX, int originY, int originZ, Material materialOverride, boolean isShaderPackOverride, NbtConditionalIdMap<Block> teMap, long currentTick) {
final Material passMaterial = resolvePassMaterial(block, pass, buffers, materialOverride);
final int teBlockId = resolveTeBlockId(teMap, renderBlocks.blockAccess, block, metadata, x, y, z, buffers, pass, materialOverride, currentTick);
@@ -587,7 +619,14 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
if (applyNaturalTex) { renderBlocks.uvRotateTop = NaturalTextureUtils.getTopRotation(x, y, z); }
tessellator.startDrawingQuads();
renderBlocks.renderBlockByRenderType(block, x, y, z);
// A block that differs from the cell's own block is the fluid stored in it
final boolean storedFluid = ModStatus.isLiquidLoggedLoaded && renderBlocks.blockAccess.getBlock(x, y, z) != block;
if (storedFluid) LiquidLoggedRenderAPI.beginStoredFluid();
try {
renderBlocks.renderBlockByRenderType(block, x, y, z);
} finally {
if (storedFluid) LiquidLoggedRenderAPI.endStoredFluid();
}
if (applyNaturalTex) { renderBlocks.uvRotateTop = 0; } // Reset
}
@@ -32,6 +32,7 @@ import com.gtnewhorizons.angelica.rendering.celeritas.SmoothBiomeColorCache;
import com.gtnewhorizons.angelica.rendering.celeritas.world.cloned.ChunkRenderContext;
import com.gtnewhorizons.angelica.rendering.celeritas.world.cloned.ClonedChunkSection;
import com.gtnewhorizons.angelica.rendering.celeritas.world.cloned.ClonedChunkSectionCache;
import com.gtnewhorizons.liquidlogged.api.LiquidLoggedBlockAccess;
import cpw.mods.fml.common.Optional;
import mega.fluidlogged.api.FLBlockAccess;
@@ -45,8 +46,11 @@ import mega.fluidlogged.api.FLBlockAccess;
*
* Object pooling should be used to avoid huge allocations as this class contains many large arrays.
*/
@Optional.Interface(iface = "mega.fluidlogged.api.FLBlockAccess", modid = "fluidlogged")
public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
@Optional.InterfaceList({
@Optional.Interface(iface = "mega.fluidlogged.api.FLBlockAccess", modid = "fluidlogged"),
@Optional.Interface(iface = "com.gtnewhorizons.liquidlogged.api.LiquidLoggedBlockAccess", modid = "liquidlogged")
})
public class WorldSlice implements IBlockAccessExtended, FLBlockAccess, LiquidLoggedBlockAccess {
private static final EnumSkyBlock[] LIGHT_TYPES = EnumSkyBlock.values();
@@ -179,7 +183,7 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
this.lightArrays = new NibbleArray[SECTION_TABLE_ARRAY_SIZE][LIGHT_TYPES.length];
this.sectionLightData = new SectionLightData[SECTION_TABLE_ARRAY_SIZE];
if (ModStatus.isFluidLoggedLoaded) {
if (ModStatus.isFluidLoggedLoaded || ModStatus.isLiquidLoggedLoaded) {
this.fluidArrays = new Fluid[SECTION_TABLE_ARRAY_SIZE][];
} else {
this.fluidArrays = null;
@@ -193,7 +197,7 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
Arrays.fill(this.blockArrays[i], Blocks.air);
this.metadataArrays[i] = new int[SECTION_BLOCK_COUNT];
if (ModStatus.isFluidLoggedLoaded && this.fluidArrays != null) {
if (this.fluidArrays != null) {
this.fluidArrays[i] = new Fluid[SECTION_BLOCK_COUNT];
}
}
@@ -239,7 +243,7 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
this.unpackBlockData(this.blockArrays[idx], this.metadataArrays[idx], section, context.getVolume());
if (ModStatus.isFluidLoggedLoaded && this.fluidArrays != null) {
if (this.fluidArrays != null) {
this.unpackFluidLoggedData(this.fluidArrays[idx], section, context.getVolume());
}
@@ -457,6 +461,12 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
return this.fluidArrays[getLocalSectionIndex(relX >> 4, relY >> 4, relZ >> 4)][getLocalBlockIndex(relX & 15, relY & 15, relZ & 15)];
}
// LiquidLogged API support
public @Nullable Fluid liquidlogged$getFluid(int x, int y, int z) {
return fl$getFluid(x, y, z);
}
public void fl$setFluid(int x, int y, int z, @Nullable Fluid fluid) {
throw new UnsupportedOperationException("Cannot set fluids in a world slice. WorldSlice is read-only");
}
@@ -9,6 +9,8 @@ import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
import com.gtnewhorizons.angelica.compat.mojang.ChunkSectionPos;
import com.gtnewhorizons.angelica.mixins.interfaces.IChunkTileEntityMapHolder;
import com.gtnewhorizons.angelica.utils.ConcurrentTileEntityMap;
import com.gtnewhorizons.liquidlogged.api.LiquidLoggedAPI;
import com.gtnewhorizons.liquidlogged.api.SectionFluidsView;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import mega.fluidlogged.internal.mixin.hook.FLSubChunk;
@@ -41,6 +43,9 @@ public class ClonedChunkSection {
private SectionLightData sectionLightData;
private final Short2ObjectOpenHashMap<TileEntity> tileEntities;
// Fluids stored in blocks by LiquidLogged, indexed y << 8 | z << 4 | x; null when the section holds none
private Fluid[] liquidLoggedFluids;
private long lastUsedTimestamp = Long.MAX_VALUE;
int refs;
@@ -79,6 +84,10 @@ public class ClonedChunkSection {
this.data.copyFrom(chunk, section);
}
if (ModStatus.isLiquidLoggedLoaded) {
copyLiquidLoggedFluids(section);
}
copyBlockEntities(chunk, pos);
fillBiomeData(chunk);
@@ -130,7 +139,22 @@ public class ClonedChunkSection {
return data.getExtBlockMetadata(x, y, z);
}
private void copyLiquidLoggedFluids(ExtendedBlockStorage section) {
final SectionFluidsView view = LiquidLoggedAPI.getSection(section);
if (view == null || view.isEmpty()) {
this.liquidLoggedFluids = null;
return;
}
if (this.liquidLoggedFluids == null) {
this.liquidLoggedFluids = new Fluid[SECTION_BLOCK_LENGTH * SECTION_BLOCK_LENGTH * SECTION_BLOCK_LENGTH];
}
view.copyTo(this.liquidLoggedFluids);
}
public Fluid getFluid(int x, int y, int z) {
if (ModStatus.isLiquidLoggedLoaded) {
return this.liquidLoggedFluids != null ? this.liquidLoggedFluids[y << 8 | z << 4 | x] : null;
}
if (ModStatus.isFluidLoggedLoaded) {
return ((FLSubChunk) data).fl$getFluid(x, y, z);
}
@@ -41,6 +41,7 @@ class CoreTransformHelper {
transformer.injectVariable("uniform vec4 iris_WeatherParams0;");
transformer.injectVariable("uniform vec4 iris_WeatherParams1;");
transformer.injectVariable("uniform vec4 iris_WeatherParams2;");
transformer.injectVariable("uniform vec4 iris_WeatherParams3;");
transformer.injectVariable("vec4 iris_Vertex;");
transformer.injectVariable("vec4 iris_Color;");
transformer.injectVariable("vec4 iris_MultiTexCoord0;");
@@ -44,6 +44,7 @@ public class IrisInternalUniforms {
private static final Vector4f WEATHER_0 = new Vector4f();
private static final Vector4f WEATHER_1 = new Vector4f();
private static final Vector4f WEATHER_2 = new Vector4f();
private static final Vector4f WEATHER_3 = new Vector4f();
public static void addWeatherUniforms(DynamicUniformHolder uniforms) {
uniforms.uniform4f(PER_FRAME, "iris_WeatherParams0", () -> WEATHER_0.set(
@@ -51,7 +52,9 @@ public class IrisInternalUniforms {
uniforms.uniform4f(PER_FRAME, "iris_WeatherParams1", () -> WEATHER_1.set(
WeatherParams.cameraFracX, WeatherParams.cameraFracZ, WeatherParams.partialTicks, WeatherParams.age));
uniforms.uniform4f(PER_FRAME, "iris_WeatherParams2", () -> WEATHER_2.set(
WeatherParams.rainScroll, WeatherParams.snowScroll, WeatherParams.rainStrength, 0.0f));
WeatherParams.vanillaTick, WeatherParams.providerTick, WeatherParams.rainStrength, WeatherParams.providerAge));
uniforms.uniform4f(PER_FRAME, "iris_WeatherParams3", () -> WEATHER_3.set(
WeatherParams.providerInvRadius, 0.0f, 0.0f, 0.0f));
}
public static void addFogUniforms(DynamicUniformHolder uniforms, FogMode fogMode) {
@@ -48,7 +48,7 @@ public final class PerFrameUniformBlockHarvester {
"gbufferModelView", "gbufferModelViewInverse", "gbufferPreviousModelView",
"gbufferProjection", "gbufferProjectionInverse", "gbufferPreviousProjection",
"fogColor", "iris_FogColor", "iris_FogStart", "iris_FogEnd", "iris_FogDensity",
"iris_WeatherParams0", "iris_WeatherParams1", "iris_WeatherParams2");
"iris_WeatherParams0", "iris_WeatherParams1", "iris_WeatherParams2", "iris_WeatherParams3");
/**
* @param primary manifest whose entry order defines the block layout
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
import com.gtnewhorizon.gtnhlib.core.GTNHLibCore;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.Tags;
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.SystemProperties;
@@ -47,6 +48,7 @@ public class MixinInitGLStateManager {
.noErrorChecks(AngelicaConfig.disableErrorChecks && GTNHLibCore.isObf() && !AngelicaMod.lwjglDebug)
.enableDSA(AngelicaConfig.enableDSA)
.postInitCallback(SelectionBoxRenderer::init)
.buildLabel("Angelica " + Tags.VERSION)
.build());
BackendStartGuard.disarm();
@@ -68,7 +68,10 @@ public abstract class MixinRenderBlocks implements ExtCeleritasRenderBlocks {
private boolean renderingPlainCube;
@Surround(method = "renderBlockByRenderType", id = "plainCube",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderStandardBlock(Lnet/minecraft/block/Block;III)Z"),
at = {
@At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderStandardBlock(Lnet/minecraft/block/Block;III)Z"),
// LOTR's coremod swaps the call for its natural-blocks wrapper, which ends in renderStandardBlock
@At(value = "INVOKE", target = "Llotr/common/coremod/LOTRReplacedMethods$BlockRendering;renderStandardBlock(Lnet/minecraft/client/renderer/RenderBlocks;Lnet/minecraft/block/Block;III)Z", remap = false) },
require = 1)
private void angelica$enterPlainCube() {
@Surround.Carry final boolean wasPlainCube = this.renderingPlainCube;
@@ -33,12 +33,17 @@ public abstract class MixinRenderBlockConnectedTexturesEthereal extends RenderBl
block.setBlockBoundsBasedOnState(getFakeRender().blockAccess, x, y, z);
getFakeRender().setRenderBoundsFromBlock(block);
boolean render = getFakeRender().renderStandardBlock(block, x, y, z);
newFakeRenderEthereal.setWorld(renderer.blockAccess);
newFakeRenderEthereal.curBlock = getFakeRender().curBlock;
newFakeRenderEthereal.curMeta = getFakeRender().curMeta;
double h = 0.05D;
newFakeRenderEthereal.setRenderBounds(h, h, h, 1.0D - h, 1.0D - h, 1.0D - h);
render &= newFakeRenderEthereal.renderStandardBlock(block, x, y, z);
try {
newFakeRenderEthereal.setWorld(renderer.blockAccess);
newFakeRenderEthereal.curBlock = getFakeRender().curBlock;
newFakeRenderEthereal.curMeta = getFakeRender().curMeta;
double h = 0.05D;
newFakeRenderEthereal.setRenderBounds(h, h, h, 1.0D - h, 1.0D - h, 1.0D - h);
render &= newFakeRenderEthereal.renderStandardBlock(block, x, y, z);
} finally {
getFakeRender().setWorld(null);
newFakeRenderEthereal.setWorld(null);
}
return render;
}
}
@@ -0,0 +1,39 @@
package com.gtnewhorizons.angelica.mixins.late.notfine.leaves.biomesoplenty;
import biomesoplenty.common.blocks.BlockBOPAppleLeaves;
import jss.notfine.util.LeafRenderUtil;
import net.minecraft.block.BlockLeavesBase;
import net.minecraft.block.material.Material;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(value = BlockBOPAppleLeaves.class)
public abstract class MixinBlockBOPAppleLeaves extends BlockLeavesBase {
@Shadow(remap = false)
private IIcon[][] textures;
@Override
public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
final int meta = world.getBlockMetadata(x, y, z);
final int type = meta & 3;
return textures[LeafRenderUtil.selectRenderMode(world, x, y, z, side) ? 1 : 0][type];
}
/**
* @author NotFine
* @reason Support new leaf rendering modes on Biomes O' Plenty leaves.
*/
@Overwrite
public boolean shouldSideBeRendered(IBlockAccess world, int x, int y, int z, int side) {
return LeafRenderUtil.shouldSideBeRendered(world, x, y, z, side);
}
private MixinBlockBOPAppleLeaves(Material material, boolean fancy) {
super(material, fancy);
}
}
@@ -0,0 +1,51 @@
package com.gtnewhorizons.angelica.mixins.late.notfine.leaves.biomesoplenty;
import biomesoplenty.common.blocks.BlockBOPColorizedLeaves;
import biomesoplenty.common.blocks.BlockBOPColorizedLeaves.ColourizedLeafCategory;
import jss.notfine.util.LeafRenderUtil;
import net.minecraft.block.BlockLeavesBase;
import net.minecraft.block.material.Material;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(value = BlockBOPColorizedLeaves.class)
public abstract class MixinBlockBOPColorizedLeaves extends BlockLeavesBase {
@Shadow(remap = false)
private IIcon[][] textures;
@Shadow(remap = false)
@Final
private static String[] leaves;
@Shadow(remap = false)
@Final
private ColourizedLeafCategory category;
@Override
public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
final int meta = world.getBlockMetadata(x, y, z);
int type = (meta & 3) + category.ordinal() * 4;
if (type >= leaves.length) {
type = 0;
}
return textures[LeafRenderUtil.selectRenderMode(world, x, y, z, side) ? 1 : 0][type];
}
/**
* @author NotFine
* @reason Support new leaf rendering modes on Biomes O' Plenty leaves.
*/
@Overwrite
public boolean shouldSideBeRendered(IBlockAccess world, int x, int y, int z, int side) {
return LeafRenderUtil.shouldSideBeRendered(world, x, y, z, side);
}
private MixinBlockBOPColorizedLeaves(Material material, boolean fancy) {
super(material, fancy);
}
}
@@ -0,0 +1,51 @@
package com.gtnewhorizons.angelica.mixins.late.notfine.leaves.biomesoplenty;
import biomesoplenty.common.blocks.BlockBOPLeaves;
import biomesoplenty.common.blocks.BlockBOPLeaves.LeafCategory;
import jss.notfine.util.LeafRenderUtil;
import net.minecraft.block.BlockLeavesBase;
import net.minecraft.block.material.Material;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(value = BlockBOPLeaves.class)
public abstract class MixinBlockBOPLeaves extends BlockLeavesBase {
@Shadow(remap = false)
private IIcon[][] textures;
@Shadow(remap = false)
@Final
private static String[] leaves;
@Shadow(remap = false)
@Final
private LeafCategory category;
@Override
public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
final int meta = world.getBlockMetadata(x, y, z);
int type = (meta & 3) + category.ordinal() * 4;
if (type >= leaves.length) {
type = 0;
}
return textures[LeafRenderUtil.selectRenderMode(world, x, y, z, side) ? 1 : 0][type];
}
/**
* @author NotFine
* @reason Support new leaf rendering modes on Biomes O' Plenty leaves.
*/
@Overwrite
public boolean shouldSideBeRendered(IBlockAccess world, int x, int y, int z, int side) {
return LeafRenderUtil.shouldSideBeRendered(world, x, y, z, side);
}
private MixinBlockBOPLeaves(Material material, boolean fancy) {
super(material, fancy);
}
}
@@ -0,0 +1,39 @@
package com.gtnewhorizons.angelica.mixins.late.notfine.leaves.biomesoplenty;
import biomesoplenty.common.blocks.BlockBOPPersimmonLeaves;
import jss.notfine.util.LeafRenderUtil;
import net.minecraft.block.BlockLeavesBase;
import net.minecraft.block.material.Material;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(value = BlockBOPPersimmonLeaves.class)
public abstract class MixinBlockBOPPersimmonLeaves extends BlockLeavesBase {
@Shadow(remap = false)
private IIcon[][] textures;
@Override
public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
final int meta = world.getBlockMetadata(x, y, z);
final int type = meta & 3;
return textures[LeafRenderUtil.selectRenderMode(world, x, y, z, side) ? 1 : 0][type];
}
/**
* @author NotFine
* @reason Support new leaf rendering modes on Biomes O' Plenty leaves.
*/
@Overwrite
public boolean shouldSideBeRendered(IBlockAccess world, int x, int y, int z, int side) {
return LeafRenderUtil.shouldSideBeRendered(world, x, y, z, side);
}
private MixinBlockBOPPersimmonLeaves(Material material, boolean fancy) {
super(material, fancy);
}
}
@@ -0,0 +1,33 @@
package com.gtnewhorizons.angelica.mixins.late.notfine.leaves.biomesoplenty;
import biomesoplenty.client.render.blocks.ColorizedLeavesRenderer;
import com.llamalad7.mixinextras.sugar.Local;
import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.init.Blocks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = ColorizedLeavesRenderer.class, remap = false)
public abstract class MixinColorizedLeavesRenderer {
@Redirect(
method = "renderWorldBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;renderStandardBlock(Lnet/minecraft/block/Block;III)Z", remap = true))
private boolean angelica$renderOverlayWithLeafCulling(RenderBlocks renderer, Block block, int x, int y, int z, @Local(argsOnly = true) Block leaf) {
if (block != Blocks.stone) {
return renderer.renderStandardBlock(block, x, y, z);
}
RenderBlocksUtils.setupColorMultiplier(block, renderer.blockAccess, x, y, z, renderer.hasOverrideBlockTexture(), 1f, 1f, 1f);
if (Minecraft.isAmbientOcclusionEnabled() && leaf.getLightValue() == 0) {
return renderer.partialRenderBounds
? renderer.renderStandardBlockWithAmbientOcclusionPartial(leaf, x, y, z, 1f, 1f, 1f)
: renderer.renderStandardBlockWithAmbientOcclusion(leaf, x, y, z, 1f, 1f, 1f);
}
return renderer.renderStandardBlockWithColorMultiplier(leaf, x, y, z, 1f, 1f, 1f);
}
}
@@ -0,0 +1,17 @@
package com.gtnewhorizons.angelica.mixins.late.spark;
import com.gtnewhorizons.angelica.compat.spark.SparkKnownSources;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.Unique;
import java.util.Collection;
@Pseudo
@Mixin(targets = "me.lucko.spark.forge.plugin.Forge1710SparkPlugin", remap = false)
public abstract class MixinForge1710SparkPlugin {
@Unique
public Collection<Object> getKnownSources() {
return SparkKnownSources.gather(getClass().getClassLoader());
}
}
@@ -322,6 +322,16 @@ class IrisInstancingGLTest {
link("particleVariant", result);
}
@Test
void weatherVariantLinks() {
final Map<PatchShaderType, String> result = TransformPatcher.patchAttributesInstanced(PARTICLE_VERTEX, null, null, null, PARTICLE_FRAGMENT, TEX_LM, false, Instancing.WEATHER);
assertNotNull(result);
final String v = result.get(PatchShaderType.VERTEX);
assertNotNull(v);
assertTrue(v.startsWith("#version 330"), () -> "expected a 3.3 core vertex shader:\n" + v);
link("weatherVariant", result);
}
@Test
void templateVariantCompilesAndLinksWithGeometryStage() {
final Map<PatchShaderType, String> result = TransformPatcher.patchAttributesInstanced(TEMPLATE_VERTEX, TEMPLATE_GEOMETRY, null, null, TEMPLATE_FRAGMENT, TEX_LM, false, Instancing.TEMPLATE);
@@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.CubeInstancedAttribs;
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
import com.gtnewhorizons.angelica.glsm.ffp.ParticleInstancedAttribs;
import com.gtnewhorizons.angelica.glsm.ffp.WeatherInstancedAttribs;
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
import com.gtnewhorizons.angelica.glsm.testutil.ClipTestSupport;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
@@ -427,7 +428,24 @@ class AttributeTransformerInstancedTest {
assertFalse(byLocation.containsKey(7), "location 7 is unused");
for (final int loc : byLocation.keySet()) {
assertTrue(loc < 11 || loc == ImmediateExtendedAttribHandler.LOC_ENTITY_IDS, "unexpected attribute location " + loc + " declared for the particle variant");
assertTrue(loc < ImmediateExtendedAttribHandler.LOC_MC_ENTITY || loc == ImmediateExtendedAttribHandler.LOC_ENTITY_IDS, "unexpected attribute location " + loc + " declared for the particle variant");
}
}
@Test
void weatherVariantDeclaresTheFiveColumnAttributesAtTheStatedLocations() {
final String v = instancedVertex(PARTICLE_VERTEX, Instancing.WEATHER);
final Map<Integer, String> byLocation = attributeLocations(v);
assertEquals(VertexFormatElement.Usage.PRIMARY_UV.getAttributeLocation(), locationOf(byLocation, "iris_WeatherCorner"));
assertEquals(WeatherInstancedAttribs.LOC_COLUMN_SPAN, locationOf(byLocation, "iris_InstColumnSpan"));
assertEquals(WeatherInstancedAttribs.LOC_JITTER, locationOf(byLocation, "iris_InstJitter"));
assertEquals(WeatherInstancedAttribs.LOC_COLOR, locationOf(byLocation, "iris_InstWeatherColor"));
assertEquals(WeatherInstancedAttribs.LOC_PARAMS, locationOf(byLocation, "iris_InstWeatherParams"));
assertEquals(WeatherInstancedAttribs.LOC_LAYER, locationOf(byLocation, "iris_InstWeatherLayer"));
for (final int loc : byLocation.keySet()) {
assertTrue(loc < ImmediateExtendedAttribHandler.LOC_MC_ENTITY || loc == ImmediateExtendedAttribHandler.LOC_ENTITY_IDS, "unexpected attribute location " + loc + " declared for the weather variant");
}
}