mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7017786a7c | ||
|
|
1c49649685 | ||
|
|
2bae79c4ee | ||
|
|
c22935660a | ||
|
|
79c488102b | ||
|
|
cc1353b73a | ||
|
|
d1e6dc8c94 | ||
|
|
ecbf8fea2f | ||
|
|
610209605f | ||
|
|
112aae3735 | ||
|
|
5844b1b5c1 | ||
|
|
05f01c17e6 |
No files matched your search
+3
-1
@@ -90,7 +90,7 @@ dependencies {
|
||||
compileOnly "org.apache.ant:ant:1.8.2"
|
||||
|
||||
// Celeritas (transitive=false: deps provided by GTNHLib/runtime)
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.5-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
|
||||
// Because who doesn't want NEI
|
||||
@@ -180,4 +180,6 @@ dependencies {
|
||||
}
|
||||
|
||||
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
|
||||
|
||||
compileOnly("com.cardinalstar.cubicchunks:CubicChunks1710:v0.1.5-alpha:dev") { transitive = false }
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public final class VAOManager {
|
||||
current.vertexFlags = currentVertexFlags;
|
||||
current.ebo = boundEBO;
|
||||
current.attribs = currentAttribs;
|
||||
current.clientSideEnabledCount = clientSideEnabledCount;
|
||||
}
|
||||
|
||||
VAOData data = vaoMap.get(vaoId);
|
||||
@@ -51,8 +52,7 @@ public final class VAOManager {
|
||||
currentVertexFlags = data.vertexFlags;
|
||||
currentAttribs = data.attribs;
|
||||
boundEBO = data.ebo;
|
||||
|
||||
recomputeClientSideCount();
|
||||
clientSideEnabledCount = data.clientSideEnabledCount;
|
||||
}
|
||||
|
||||
public static void onDeleteVertexArray(int vaoId) {
|
||||
@@ -68,6 +68,7 @@ public final class VAOManager {
|
||||
public Attrib[] attribs;
|
||||
public int vertexFlags;
|
||||
public int ebo;
|
||||
public int clientSideEnabledCount;
|
||||
|
||||
public VAOData() {
|
||||
attribs = new Attrib[MAX_ATTRIBS];
|
||||
@@ -183,14 +184,6 @@ public final class VAOManager {
|
||||
|
||||
|
||||
|
||||
private static void recomputeClientSideCount() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < MAX_ATTRIBS; i++) {
|
||||
if (isClientSideAttrib(i)) count++;
|
||||
}
|
||||
clientSideEnabledCount = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any currently enabled vertex attribute was registered without a VBO
|
||||
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class HugeFarPlaneFrustumTest {
|
||||
|
||||
private static final float SECTION_HALF = 8.0f + 1.0f + 0.125f;
|
||||
private static final float DEGENERATE_FAR = 2.147483648e9f;
|
||||
|
||||
private static Matrix4f combined(float zFar) {
|
||||
final Matrix4f proj = new Matrix4f().perspective((float) Math.toRadians(70.0), 854.0f / 480.0f, 0.05f, zFar);
|
||||
final Matrix4f mv = new Matrix4f()
|
||||
.rotate((float) Math.toRadians(25.0), 1, 0, 0)
|
||||
.rotate((float) Math.toRadians(123.0), 0, 1, 0)
|
||||
.translate(0.0f, -1.62f, 0.0f);
|
||||
return new Matrix4f().set(proj).mul(mv);
|
||||
}
|
||||
|
||||
private static boolean sectionVisible(FrustumIntersection f, int sx, int sy, int sz) {
|
||||
final float cx = sx * 16.0f + 8.0f;
|
||||
final float cy = sy * 16.0f + 8.0f;
|
||||
final float cz = sz * 16.0f + 8.0f;
|
||||
return f.testAab(cx - SECTION_HALF, cy - SECTION_HALF, cz - SECTION_HALF, cx + SECTION_HALF, cy + SECTION_HALF, cz + SECTION_HALF);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnormalizedFrustumMatchesNormalFarPlane() {
|
||||
final FrustumIntersection normal = new FrustumIntersection(combined(512.0f), false);
|
||||
final FrustumIntersection huge = new FrustumIntersection(combined(DEGENERATE_FAR), false);
|
||||
|
||||
int visible = 0;
|
||||
int mismatches = 0;
|
||||
for (int x = -12; x <= 12; x++) {
|
||||
for (int y = -4; y <= 4; y++) {
|
||||
for (int z = -12; z <= 12; z++) {
|
||||
final boolean vNormal = sectionVisible(normal, x, y, z);
|
||||
if (vNormal) visible++;
|
||||
if (vNormal != sectionVisible(huge, x, y, z)) mismatches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertTrue(visible > 100, "sanity check broken: normal frustum accepted only " + visible + " sections");
|
||||
assertEquals(0, mismatches, "sections culled differently under the degenerate far plane");
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalizedSetCullsEverythingOnDegenerateFarPlane() {
|
||||
final FrustumIntersection normalized = new FrustumIntersection();
|
||||
normalized.set(combined(DEGENERATE_FAR));
|
||||
|
||||
int visible = 0;
|
||||
for (int x = -12; x <= 12; x++) {
|
||||
for (int y = -4; y <= 4; y++) {
|
||||
for (int z = -12; z <= 12; z++) {
|
||||
if (sectionVisible(normalized, x, y, z)) visible++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(0, visible, "set(m) with a degenerate far plane no longer culls everything");
|
||||
}
|
||||
|
||||
@Test
|
||||
void identityFrustumOnlyPassesCameraSection() {
|
||||
final FrustumIntersection identity = new FrustumIntersection(new Matrix4f());
|
||||
assertTrue(sectionVisible(identity, 0, 0, 0), "identity frustum rejected the camera's own section");
|
||||
int visible = 0;
|
||||
for (int x = -12; x <= 12; x++) {
|
||||
for (int y = -4; y <= 4; y++) {
|
||||
for (int z = -12; z <= 12; z++) {
|
||||
if (Math.abs(x) <= 1 && Math.abs(y) <= 1 && Math.abs(z) <= 1) continue;
|
||||
if (sectionVisible(identity, x, y, z)) visible++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(0, visible, "non-adjacent sections accepted by the identity frustum");
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ dependencies {
|
||||
api(project(":glsm"))
|
||||
|
||||
compileOnly("com.github.GTNewHorizons:lwjgl3ify:3.0.15:dev") { isTransitive = false }
|
||||
compileOnly("org.embeddedt.celeritas:celeritas-common:2.5.3-GTNH") { isTransitive = false }
|
||||
compileOnly("org.embeddedt.celeritas:celeritas-common:2.5.5-GTNH") { isTransitive = false }
|
||||
|
||||
val osName = System.getProperty("os.name").lowercase()
|
||||
val osArch = System.getProperty("os.arch").lowercase()
|
||||
|
||||
@@ -11,9 +11,13 @@ import net.minecraft.client.resources.DefaultResourcePack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontFormatException;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -27,13 +31,13 @@ public class FontStrategist {
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
static {
|
||||
HashMap<String, Font> fontSet = new HashMap<>();
|
||||
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
LOGGER.warn("GraphicsEnvironment.isHeadless() returned true! Custom fonts will be unavailable. This is likely a MacOS issue.");
|
||||
availableFonts = new Font[0];
|
||||
LOGGER.warn("GraphicsEnvironment.isHeadless() returned true! Only bundled fonts will be available. This is likely a MacOS issue.");
|
||||
} else {
|
||||
// get available fonts without duplicates (250 copies of dialog.plain need not apply)
|
||||
Font[] availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||
HashMap<String, Font> fontSet = new HashMap<>();
|
||||
HashMultiset<String> duplicates = HashMultiset.create(); // for debugging
|
||||
|
||||
for (Font font : availableFontsDirty) {
|
||||
@@ -57,11 +61,14 @@ public class FontStrategist {
|
||||
sb.append(". Some fonts may be missing from the font selection menu.");
|
||||
LOGGER.warn(sb.toString());
|
||||
}
|
||||
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
|
||||
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, availableFonts.length);
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, fontSet.size());
|
||||
}
|
||||
|
||||
loadBundledFonts(fontSet);
|
||||
|
||||
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
|
||||
|
||||
// create and add the resource pack that provides fonts
|
||||
HashMap<String, File> packMap = new HashMap<>();
|
||||
for (int i = 0; i < FontProviderCustom.ATLAS_COUNT; i++) {
|
||||
@@ -74,6 +81,44 @@ public class FontStrategist {
|
||||
defaultResourcePacks.add(fontResourcePack);
|
||||
}
|
||||
|
||||
// Load .ttf/.otf shipped with the pack so customFontName* can point at a font that isn't installed
|
||||
// on the system. fontfiles/ is SmoothFont's folder, kept so packs built for it work unchanged.
|
||||
private static void loadBundledFonts(HashMap<String, Font> fontSet) {
|
||||
File configDir = Loader.instance().getConfigDir();
|
||||
if (configDir == null) {
|
||||
LOGGER.warn("Loader.instance().getConfigDir() returned null. Bundled fonts will not be loaded.");
|
||||
return;
|
||||
}
|
||||
File parent = configDir.getParentFile();
|
||||
File[] fontDirs = { new File(parent, "fontfiles"), new File(configDir, "angelica/fonts") };
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.isHeadless() ? null : GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
int loaded = 0;
|
||||
for (File dir : fontDirs) {
|
||||
File[] files = dir.listFiles((d, name) -> name.toLowerCase().endsWith(".ttf") || name.toLowerCase().endsWith(".otf"));
|
||||
if (files == null) continue;
|
||||
for (File file : files) {
|
||||
try {
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, file);
|
||||
String name = font.getFontName();
|
||||
if (fontSet.containsKey(name)) {
|
||||
String deduped = name;
|
||||
int n = 1;
|
||||
while (fontSet.containsKey(deduped)) deduped = name + "_" + n++;
|
||||
LOGGER.warn("Font name {} (from {}) is already registered; registering this one as {}", name, file.getName(), deduped);
|
||||
name = deduped;
|
||||
}
|
||||
if (ge != null) ge.registerFont(font);
|
||||
fontSet.put(name, font);
|
||||
loaded++;
|
||||
LOGGER.info("Loaded font {} from {}/{}", name, dir.getName(), file.getName());
|
||||
} catch (FontFormatException | IOException e) {
|
||||
LOGGER.error("Couldn't load font {}", file.getPath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loaded > 0) LOGGER.info("Loaded {} bundled font(s) from the pack", loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
Lets you get a FontProvider per char while respecting font priority and fallbacks, the unicode flag, whether
|
||||
SGA is on, if we're in a splash screen, if a font can even display a character in the first place, etc.
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.compat;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import com.falsepattern.endlessids.config.GeneralConfig;
|
||||
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
|
||||
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CubeStatusEvents;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
|
||||
import mods.battlegear2.Battlegear;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class ModStatus {
|
||||
public static final Logger LOGGER = LogManager.getLogger("ModCompat");
|
||||
@@ -29,6 +30,7 @@ public class ModStatus {
|
||||
public static boolean isBaublesLoaded;
|
||||
public static boolean isCosmeticArmorReworkedLoaded;
|
||||
public static boolean isFluidLoggedLoaded;
|
||||
public static boolean isCubicChunksLoaded;
|
||||
public static boolean isBOPLoaded;
|
||||
public static boolean isEtFuturumLoaded;
|
||||
|
||||
@@ -50,6 +52,7 @@ public class ModStatus {
|
||||
isBaublesLoaded = Loader.isModLoaded("Baubles");
|
||||
isCosmeticArmorReworkedLoaded = Loader.isModLoaded("cosmeticarmorreworked");
|
||||
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
|
||||
isCubicChunksLoaded = Loader.isModLoaded("cubicchunks");
|
||||
isEtFuturumLoaded = Loader.isModLoaded("etfuturum");
|
||||
|
||||
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
|
||||
@@ -71,6 +74,10 @@ public class ModStatus {
|
||||
}
|
||||
}
|
||||
|
||||
if (isCubicChunksLoaded) {
|
||||
CubeStatusEvents.init();
|
||||
}
|
||||
|
||||
boolean eidBlockItemExtended = false;
|
||||
boolean eidBiomeExtended = false;
|
||||
if (isEIDLoaded) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.compat.cubicchunks;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
|
||||
import com.cardinalstar.cubicchunks.api.IColumn;
|
||||
import com.cardinalstar.cubicchunks.api.ICube;
|
||||
import com.cardinalstar.cubicchunks.world.ICubicWorld;
|
||||
import com.cardinalstar.cubicchunks.world.cube.Cube;
|
||||
import com.cardinalstar.cubicchunks.world.cube.ICubeProviderInternal;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.ChunkSectionPos;
|
||||
|
||||
public class CubicChunksAPI {
|
||||
|
||||
public static ExtendedBlockStorage getCubeStorage(World world, int x, int y, int z) {
|
||||
Cube cube = ((ICubeProviderInternal) world.getChunkProvider()).getLoadedCube(x, y, z);
|
||||
|
||||
if (cube == null || cube.isEmpty()) return null;
|
||||
|
||||
return cube.getStorage();
|
||||
}
|
||||
|
||||
public static int[] getLoadedCubeLevelsInColumn(World world, int chunkX, int chunkZ) {
|
||||
IColumn column = (IColumn) world.getChunkFromChunkCoords(chunkX, chunkZ);
|
||||
|
||||
return column.getLoadedCubes().stream().mapToInt(ICube::getY).toArray();
|
||||
}
|
||||
|
||||
public static int getMinHeight(World world) {
|
||||
return ((ICubicWorld) world).getMinHeight();
|
||||
}
|
||||
|
||||
public static int getMaxHeight(World world) {
|
||||
return ((ICubicWorld) world).getMaxHeight();
|
||||
}
|
||||
|
||||
public static int getMinSectionY(World world) {
|
||||
return ChunkSectionPos.getSectionCoord(getMinHeight(world));
|
||||
}
|
||||
|
||||
public static int getMaxSectionYExclusive(World world) {
|
||||
return ChunkSectionPos.getSectionCoord(getMaxHeight(world) - 1) + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.compat.lotr;
|
||||
|
||||
import net.minecraft.world.WorldProvider;
|
||||
|
||||
public final class LOTRCompat {
|
||||
|
||||
private static final Class<?> LOTR_WORLD_PROVIDER_CLASS;
|
||||
|
||||
static {
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = Class.forName("lotr.common.world.LOTRWorldProvider", false, LOTRCompat.class.getClassLoader());
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
LOTR_WORLD_PROVIDER_CLASS = clazz;
|
||||
}
|
||||
|
||||
private LOTRCompat() {}
|
||||
|
||||
public static boolean isLotrProvider(WorldProvider provider) {
|
||||
return LOTR_WORLD_PROVIDER_CLASS != null && LOTR_WORLD_PROVIDER_CLASS.isInstance(provider);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.compat.mojang;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
|
||||
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
|
||||
|
||||
// See if we can merge/mixin/extend ChunkPosition maybe?
|
||||
public class ChunkSectionPos extends Vector3i {
|
||||
|
||||
@@ -25,11 +27,8 @@ public class ChunkSectionPos extends Vector3i {
|
||||
}
|
||||
|
||||
public static long asLong(int x, int y, int z) {
|
||||
long l = 0L;
|
||||
l |= ((long)x & 4194303L) << 42;
|
||||
l |= ((long)y & 1048575L) << 0;
|
||||
l |= ((long)z & 4194303L) << 20;
|
||||
return l;
|
||||
// Use CoordinatePacker so that cubic chunks can change the bit packing layout
|
||||
return CoordinatePacker.pack(x, y, z);
|
||||
}
|
||||
|
||||
public static int getLocalCoord(int coord) {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.compat.mojang;
|
||||
|
||||
import net.minecraft.util.MathHelper;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
|
||||
public class CompatMathHelper {
|
||||
public static int smallestEncompassingPowerOfTwo(int value) {
|
||||
int j = value - 1;
|
||||
@@ -24,4 +28,13 @@ public class CompatMathHelper {
|
||||
return k == 0 ? value : value + divisor - k;
|
||||
}
|
||||
}
|
||||
|
||||
public static int clampHeight(int y) {
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
// no clamping
|
||||
return y;
|
||||
} else {
|
||||
return MathHelper.clamp_int(y, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,13 @@ public enum Mixins implements IMixins {
|
||||
.addClientMixins("angelica.vbo.MixinRenderGlobal")
|
||||
),
|
||||
|
||||
ANGELICA_VBO_CLOUDS_FAR_PLANE(
|
||||
new MixinBuilder()
|
||||
.setApplyIf(() -> AngelicaConfig.enableVBOClouds && !AngelicaConfig.enableNotFineFeatures)
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins("notfine.clouds.MixinEntityRenderer")
|
||||
),
|
||||
|
||||
ANGELICA_PANORAMA_BLUR(
|
||||
new MixinBuilder("Replace main menu panorama with modern equivalent")
|
||||
.setPhase(Phase.EARLY)
|
||||
@@ -204,6 +211,8 @@ public enum Mixins implements IMixins {
|
||||
.addClientMixins(
|
||||
"rendering.MixinBlock"
|
||||
, "rendering.MixinBlockFluidBase"
|
||||
, "rendering.MixinBlockLiquid"
|
||||
, "rendering.BlockLiquidFlowInvoker"
|
||||
, "rendering.AccessorBiomeColorEvent"
|
||||
, "rendering.MixinBiomeGenBase"
|
||||
, "rendering.MixinChunk"
|
||||
@@ -231,8 +240,7 @@ public enum Mixins implements IMixins {
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableCeleritas)
|
||||
.addClientMixins(
|
||||
"celeritas.terrain.ChunkTrackerAccessor"
|
||||
, "celeritas.terrain.MixinChunkProviderClient"
|
||||
"celeritas.terrain.MixinChunkProviderClient"
|
||||
, "celeritas.terrain.MixinMinecraft_ChunkUpdates"
|
||||
, "celeritas.terrain.MixinRenderGlobal"
|
||||
, "celeritas.terrain.MixinRenderSectionManager"
|
||||
@@ -299,6 +307,8 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinTileEntityRendererDispatcher"
|
||||
, "shaders.MixinGlProgram"
|
||||
, "shaders.MixinTextureManager_ReloadCount"
|
||||
, "shaders.AccessorModelBox"
|
||||
, "shaders.MixinModelBiped"
|
||||
)
|
||||
),
|
||||
|
||||
@@ -407,6 +417,12 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.removeUnicodeEvenScaling)
|
||||
.addClientMixins("angelica.bugfixes.MixinScaledResolution_UnicodeFix")),
|
||||
|
||||
FARSEEK_WORLDSLICE_COMPAT(new MixinBuilder("Let Farseek resolve celeritas' WorldSlice so Streams' water renders properly")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.FARSEEK)
|
||||
.setApplyIf(() -> AngelicaConfig.enableCeleritas)
|
||||
.addClientMixins("client.farseek.MixinFarseekIBlockAccessValue")),
|
||||
|
||||
SECURITYCRAFT_COMPAT(new MixinBuilder("Fix reflection in SecurityCraft for compat with Angelica")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.SECURITYCRAFT)
|
||||
|
||||
@@ -17,6 +17,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
DRAGON_API("Reika.DragonAPI.Auxiliary.DragonAPIASMHandler", "DragonAPI"),
|
||||
ET_FUTURUM_REQUIEM(null, "etfuturum"),
|
||||
EXTRAUTILS(null, "ExtraUtilities"),
|
||||
FARSEEK("farseek.core.FarseekCoreMod", "farseek"),
|
||||
FALSETWEAKS("com.falsepattern.falsetweaks.asm.CoreLoadingPlugin", "falsetweaks"),
|
||||
MINEFACTORY_RELOADED(null, "MineFactoryReloaded"),
|
||||
NTM_SPACE("com.hbm.dim.SolarSystem"),
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
public interface ChunkTrackerAccessor {
|
||||
Long2IntOpenHashMap angelica$getChunkStatus();
|
||||
LongOpenHashSet angelica$getChunkReady();
|
||||
LongSet angelica$getUnloadQueue();
|
||||
LongSet angelica$getLoadQueue();
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Matrix4fc;
|
||||
|
||||
public interface ClippingHelperExt {
|
||||
FrustumIntersection celeritas$getFrustumIntersection();
|
||||
void celeritas$updateFrustumIntersection();
|
||||
|
||||
Matrix4fc celeritas$getCombinedMatrix();
|
||||
}
|
||||
+4
@@ -1,6 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import com.gtnewhorizons.angelica.utils.ConcurrentTileEntityMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.ChunkPosition;
|
||||
|
||||
public interface IChunkTileEntityMapHolder {
|
||||
ConcurrentTileEntityMap angelica$getConcurrentTEMap();
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
package com.gtnewhorizons.angelica.proxy;
|
||||
|
||||
import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiVideoSettings;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraftforge.client.ClientCommandHandler;
|
||||
import net.minecraftforge.client.event.EntityViewRenderEvent;
|
||||
import net.minecraftforge.client.event.FOVUpdateEvent;
|
||||
import net.minecraftforge.client.event.GuiOpenEvent;
|
||||
import net.minecraftforge.client.event.GuiScreenEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import biomesoplenty.api.content.BOPCBlocks;
|
||||
import com.google.common.base.Objects;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry;
|
||||
@@ -8,6 +38,7 @@ import com.gtnewhorizons.angelica.commands.AngelicaCommand;
|
||||
import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.bettercrashes.BetterCrashesCompat;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.CompatMathHelper;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
@@ -53,34 +84,6 @@ import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
|
||||
import me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.client.IrisDebugScreenHandler;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.GuiVideoSettings;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraftforge.client.ClientCommandHandler;
|
||||
import net.minecraftforge.client.event.EntityViewRenderEvent;
|
||||
import net.minecraftforge.client.event.FOVUpdateEvent;
|
||||
import net.minecraftforge.client.event.GuiOpenEvent;
|
||||
import net.minecraftforge.client.event.GuiScreenEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
|
||||
public final class ClientProxy extends CommonProxy {
|
||||
|
||||
@@ -293,10 +296,10 @@ public final class ClientProxy extends CommonProxy {
|
||||
event.left.set(i + 4, String.format(
|
||||
"lc: %d b: %s bl: %d sl: %d rl: %d",
|
||||
chunk.getTopFilledSegment() + 15,
|
||||
chunk.getBiomeGenForWorldCoords(bX & 15, bZ & 15, mc.theWorld.getWorldChunkManager()).biomeName,
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Block, bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15),
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Sky, bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15),
|
||||
chunk.getBlockLightValue(bX & 15, MathHelper.clamp_int(bY, 0, 255), bZ & 15, 0)));
|
||||
this.mc.theWorld.getBiomeGenForCoords(bX & 15, bZ & 15).biomeName,
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Block, bX & 15, CompatMathHelper.clampHeight(bY), bZ & 15),
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Sky, bX & 15, CompatMathHelper.clampHeight(bY), bZ & 15),
|
||||
chunk.getBlockLightValue(bX & 15, CompatMathHelper.clampHeight(bY), bZ & 15, 0)));
|
||||
}
|
||||
}
|
||||
// Draw a frametime graph
|
||||
|
||||
@@ -26,8 +26,12 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VertexBufferType;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
import jss.notfine.core.Settings;
|
||||
import jss.notfine.gui.options.named.GraphicsQualityOff;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.shaderpack.CloudSetting;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
@@ -45,6 +49,7 @@ import org.embeddedt.embeddium.impl.gl.shader.ShaderType;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Vector3d;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -76,6 +81,8 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
|
||||
private static final float SCROLL_SPEED = 1.0f / 256.0f;
|
||||
private static final float EDGE_OVERLAP = 0.0001f;
|
||||
private static final float INTERIOR_INSET = EDGE_OVERLAP * 8.0f;
|
||||
private static final float MAX_FAR_PLANE_DISTANCE = 65536.0f;
|
||||
|
||||
private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
|
||||
|
||||
@@ -103,11 +110,13 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
private int anchorCellX = Integer.MIN_VALUE, anchorCellZ = Integer.MIN_VALUE;
|
||||
private int anchorMarginCells = 0;
|
||||
private int cachedEdgesGeneration = Integer.MIN_VALUE;
|
||||
private boolean cachedEmitTopFace, cachedEmitBottomFace, cachedCameraInsideCloud;
|
||||
private boolean cachedEmitTopFace, cachedEmitBottomFace;
|
||||
private int cachedRenderRadius = -1;
|
||||
private float cachedCloudInteriorHeight = Float.NaN;
|
||||
private boolean geomBuilt;
|
||||
|
||||
private int cloudMode = -1, renderDistance = -1, cloudElevation = -1, scaleMult = -1;
|
||||
private boolean enabled;
|
||||
|
||||
private ByteBuffer emitBuffer;
|
||||
private long emitBufferAddr;
|
||||
@@ -136,6 +145,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
|
||||
private GlProgram<CloudUniforms> program;
|
||||
private final Matrix4f mvpScratch = new Matrix4f();
|
||||
private final Matrix4f mvScratch = new Matrix4f();
|
||||
|
||||
public CloudRenderer() {
|
||||
((IReloadableResourceManager) mc.getResourceManager()).registerReloadListener(this);
|
||||
@@ -160,10 +170,13 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
final GraphicsQualityOff cloudGraphicsQuality = (GraphicsQualityOff) Settings.MODE_CLOUDS.option.getStore();
|
||||
final int cloudQualitySetting = cloudGraphicsQuality == GraphicsQualityOff.FANCY
|
||||
|| cloudGraphicsQuality == GraphicsQualityOff.DEFAULT && mc.gameSettings.fancyGraphics ? 2 : 1;
|
||||
final float dimCloudHeight = mc.theWorld == null ? 128.0f : mc.theWorld.provider.getCloudHeight();
|
||||
final boolean finiteCloudHeight = Float.isFinite(dimCloudHeight);
|
||||
final boolean newEnabled = cloudGraphicsQuality != GraphicsQualityOff.OFF
|
||||
&& mc.gameSettings.shouldRenderClouds()
|
||||
&& mc.theWorld != null
|
||||
&& mc.theWorld.provider.isSurfaceWorld();
|
||||
&& mc.theWorld.provider.isSurfaceWorld()
|
||||
&& finiteCloudHeight;
|
||||
final int targetDistance = Math.max(mc.gameSettings.renderDistanceChunks, (int) Settings.RENDER_DISTANCE_CLOUDS.option.getStore());
|
||||
final int cloudScaleMult = (int) Settings.CLOUD_SCALE.option.getStore();
|
||||
|
||||
@@ -174,11 +187,12 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
invalidateGeometry();
|
||||
}
|
||||
|
||||
enabled = newEnabled;
|
||||
cloudMode = cloudQualitySetting;
|
||||
renderDistance = targetDistance;
|
||||
scaleMult = cloudScaleMult;
|
||||
|
||||
cloudElevation = mc.theWorld == null ? 128 : (int) mc.theWorld.provider.getCloudHeight();
|
||||
cloudElevation = finiteCloudHeight ? (int) dimCloudHeight : 128;
|
||||
// Allows the setting to work with RFG and similar without hardcoding.
|
||||
// The minimum height check is so stuff like Aether cloud height doesn't get messed up.
|
||||
if (cloudElevation >= 96) {
|
||||
@@ -186,6 +200,26 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Far-plane distance needed so none of the cloud geometry gets clipped.
|
||||
*/
|
||||
public float getRequiredFarPlaneDistance() {
|
||||
if (!enabled || scaleMult <= 0 || renderDistance <= 0) return 0.0f;
|
||||
if (mc.theWorld == null) return 0.0f;
|
||||
final float cloudInteriorWidth = 12.0f * scaleMult;
|
||||
final int renderRadius = (int) Math.ceil(renderDistance * 64.0f / (CELLS_PER_CHUNK * cloudInteriorWidth));
|
||||
final int marginCells = Math.max(CELLS_PER_CHUNK, renderRadius * CELLS_PER_CHUNK / 4);
|
||||
final int marginChunks = (marginCells + CELLS_PER_CHUNK - 1) / CELLS_PER_CHUNK;
|
||||
float radiusBlocks = (renderRadius + marginChunks) * CELLS_PER_CHUNK * cloudInteriorWidth;
|
||||
if (cloudMode != 2) radiusBlocks *= (float) Math.sqrt(2);
|
||||
float verticalSlack = cloudElevation;
|
||||
final Entity view = mc.renderViewEntity;
|
||||
if (view != null) {
|
||||
verticalSlack = Math.abs(cloudElevation + 0.33f - (float) view.posY) + 4.0f * scaleMult;
|
||||
}
|
||||
return Math.min(radiusBlocks + verticalSlack, MAX_FAR_PLANE_DISTANCE);
|
||||
}
|
||||
|
||||
private void invalidateGeometry() {
|
||||
anchorCellX = Integer.MIN_VALUE;
|
||||
anchorCellZ = Integer.MIN_VALUE;
|
||||
@@ -193,6 +227,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
cachedEdgesGeneration = Integer.MIN_VALUE;
|
||||
cachedCloudInteriorHeight = Float.NaN;
|
||||
cachedVertexCount = 0;
|
||||
geomBuilt = false;
|
||||
}
|
||||
|
||||
private void initProgram() {
|
||||
@@ -215,23 +250,30 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
program.unbind();
|
||||
}
|
||||
|
||||
private void uploadFogUniforms(float partialTicks, int renderRadius, float cloudInteriorWidth) {
|
||||
private void uploadFogUniforms() {
|
||||
final CloudUniforms u = program.getInterface();
|
||||
final boolean fogEnabled = GLStateManager.getFogMode().isEnabled();
|
||||
u.setFogEnabled(fogEnabled);
|
||||
if (fogEnabled) {
|
||||
final float start = renderDistance * 16.0f;
|
||||
final float end = renderRadius * CELLS_PER_CHUNK * cloudInteriorWidth;
|
||||
final float range = end - start;
|
||||
u.setFogParams(range != 0.0f ? -1.0f / range : 0.0f, range != 0.0f ? end / range : 1.0f);
|
||||
final Vec3 sky = mc.theWorld.getSkyColor(mc.renderViewEntity, partialTicks);
|
||||
u.setFogColor((float) sky.xCoord, (float) sky.yCoord, (float) sky.zCoord);
|
||||
if (!fogEnabled) return;
|
||||
final FogState fog = GLStateManager.getFogState();
|
||||
final Vector3d color = fog.getFogColor();
|
||||
u.setFogColor((float) color.x, (float) color.y, (float) color.z);
|
||||
switch (fog.getFogMode()) {
|
||||
case GL11.GL_LINEAR -> {
|
||||
final float range = fog.getEnd() - fog.getStart();
|
||||
u.setFogParams(range != 0.0f ? -1.0f / range : 0.0f, range != 0.0f ? fog.getEnd() / range : 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
case GL11.GL_EXP -> u.setFogParams(0.0f, 0.0f, fog.getDensity(), 1.0f);
|
||||
default -> u.setFogParams(0.0f, 0.0f, fog.getDensity(), 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean render(int cloudTicks, float partialTicks) {
|
||||
if (mc.theWorld == null || mc.renderViewEntity == null) return false;
|
||||
if (cloudMode != 2 || scaleMult <= 0) return false;
|
||||
if (!enabled || scaleMult <= 0) return false;
|
||||
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline != null && pipeline.getCloudSetting() == CloudSetting.OFF) return false;
|
||||
|
||||
boolean textureSetupDone = false;
|
||||
if (edges == null) {
|
||||
@@ -266,41 +308,45 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
|
||||
final boolean emitTopFace = cameraRelativeY + cloudInteriorHeight >= 0.0F;
|
||||
final boolean emitBottomFace = cameraRelativeY <= 0.0F;
|
||||
final boolean cameraInsideY = cameraRelativeY <= 0.0F && cameraRelativeY + cloudInteriorHeight >= 0.0F;
|
||||
final boolean cameraInsideCloud = cameraInsideY && edges.cellOpaque(anchorX, anchorZ);
|
||||
|
||||
final int renderRadius = (int) Math.ceil(renderDistance * 64.0f / (CELLS_PER_CHUNK * cloudInteriorWidth));
|
||||
|
||||
final boolean anchorInRange = vao != null
|
||||
&& Math.abs(anchorX - anchorCellX) <= anchorMarginCells
|
||||
&& Math.abs(anchorZ - anchorCellZ) <= anchorMarginCells;
|
||||
final boolean insideBand = cloudMode == 2 && emitTopFace && emitBottomFace;
|
||||
final boolean anchorInRange = geomBuilt
|
||||
&& (insideBand
|
||||
? anchorX == anchorCellX && anchorZ == anchorCellZ
|
||||
: Math.abs(anchorX - anchorCellX) <= anchorMarginCells
|
||||
&& Math.abs(anchorZ - anchorCellZ) <= anchorMarginCells);
|
||||
final boolean geomCacheValid = anchorInRange
|
||||
&& cachedEdgesGeneration == edgesGeneration
|
||||
&& cachedEmitTopFace == emitTopFace
|
||||
&& cachedEmitBottomFace == emitBottomFace
|
||||
&& cachedCameraInsideCloud == cameraInsideCloud
|
||||
&& cachedRenderRadius == renderRadius
|
||||
&& cachedCloudInteriorHeight == cloudInteriorHeight;
|
||||
|
||||
if (!geomCacheValid) {
|
||||
final int marginCells = cameraInsideCloud ? 0 : Math.max(CELLS_PER_CHUNK, renderRadius * CELLS_PER_CHUNK / 4);
|
||||
rebuildGeometry(anchorX, anchorZ, renderRadius, cloudInteriorHeight, emitTopFace, emitBottomFace, cameraInsideCloud, marginCells);
|
||||
final int marginCells = Math.max(CELLS_PER_CHUNK, renderRadius * CELLS_PER_CHUNK / 4);
|
||||
rebuildGeometry(anchorX, anchorZ, renderRadius, cloudInteriorHeight, emitTopFace, emitBottomFace, marginCells);
|
||||
anchorCellX = anchorX;
|
||||
anchorCellZ = anchorZ;
|
||||
anchorMarginCells = marginCells;
|
||||
cachedEdgesGeneration = edgesGeneration;
|
||||
cachedEmitTopFace = emitTopFace;
|
||||
cachedEmitBottomFace = emitBottomFace;
|
||||
cachedCameraInsideCloud = cameraInsideCloud;
|
||||
cachedRenderRadius = renderRadius;
|
||||
cachedCloudInteriorHeight = cloudInteriorHeight;
|
||||
geomBuilt = true;
|
||||
}
|
||||
|
||||
if (vao == null || cachedVertexCount == 0) return true;
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
mc.renderEngine.bindTexture(texture);
|
||||
if (!textureSetupDone) setupCloudTexture();
|
||||
if (cloudTexId != -1 && !textureSetupDone) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, cloudTexId);
|
||||
} else {
|
||||
mc.renderEngine.bindTexture(texture);
|
||||
if (!textureSetupDone) setupCloudTexture();
|
||||
}
|
||||
|
||||
final Vec3 color = mc.theWorld.getCloudColour(partialTicks);
|
||||
float r = (float) color.xCoord;
|
||||
@@ -337,10 +383,12 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
mv.pushMatrix();
|
||||
mv.scale(cloudInteriorWidth, 1.0f, cloudInteriorWidth);
|
||||
mv.translate(-(cameraRelativeX + anchorDriftX), cameraRelativeY, -(cameraRelativeZ + anchorDriftZ));
|
||||
mvScratch.set(mv);
|
||||
GLStateManager.getProjectionMatrix().mul(mv, mvpScratch);
|
||||
mv.popMatrix();
|
||||
u.mvp.set(mvpScratch);
|
||||
uploadFogUniforms(partialTicks, renderRadius, cloudInteriorWidth);
|
||||
u.mv.set(mvScratch);
|
||||
uploadFogUniforms();
|
||||
|
||||
drawAllFactors(r, g, b, false);
|
||||
program.unbind();
|
||||
@@ -362,15 +410,13 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
private void drawAllFactors(float r, float g, float b, boolean shadersActive) {
|
||||
vao.bind();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
GLStateManager.disableCull();
|
||||
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glColorMask(false, false, false, false);
|
||||
// Depth prepass: alpha must match color pass so discard threshold writes same depth coverage.
|
||||
if (shadersActive) {
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, CloudUniforms.ALPHA);
|
||||
} else {
|
||||
program.getInterface().setColorMult(1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
final int totalQuads = cachedVertexCount / 4;
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, totalQuads * 6, GL11.GL_UNSIGNED_INT, 0L);
|
||||
@@ -403,25 +449,32 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
}
|
||||
|
||||
vao.unbind();
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
|
||||
private void rebuildGeometry(int anchorX, int anchorZ, int renderRadius, float cloudInteriorHeight, boolean emitTopFace, boolean emitBottomFace, boolean cameraInsideCloud, int marginCells) {
|
||||
private void rebuildGeometry(int anchorX, int anchorZ, int renderRadius, float cloudInteriorHeight, boolean emitTopFace, boolean emitBottomFace, int marginCells) {
|
||||
prepareEmitBuffer();
|
||||
|
||||
final int marginChunks = (marginCells + CELLS_PER_CHUNK - 1) / CELLS_PER_CHUNK;
|
||||
final int chunkRange = cameraInsideCloud ? 0 : (renderRadius + marginChunks);
|
||||
final int outerLo = cameraInsideCloud ? -1 : -chunkRange;
|
||||
final int outerHi = cameraInsideCloud ? 0 : chunkRange;
|
||||
final int chunkRange = renderRadius + marginChunks;
|
||||
final int outerLo = -chunkRange;
|
||||
final int outerHi = chunkRange;
|
||||
|
||||
final int radiusCellsWithMargin = cameraInsideCloud ? CELLS_PER_CHUNK : (chunkRange * CELLS_PER_CHUNK);
|
||||
final int radiusCellsWithMargin = chunkRange * CELLS_PER_CHUNK;
|
||||
final int radiusCellsSq = radiusCellsWithMargin * radiusCellsWithMargin;
|
||||
|
||||
final float scrollX = anchorX * SCROLL_SPEED;
|
||||
final float scrollZ = anchorZ * SCROLL_SPEED;
|
||||
|
||||
if (cloudMode != 2) {
|
||||
emitFastLayer(chunkRange * CELLS_PER_CHUNK, scrollX, scrollZ);
|
||||
finishGeometry();
|
||||
return;
|
||||
}
|
||||
|
||||
final int rows = (outerHi - outerLo + 1) * CELLS_PER_CHUNK;
|
||||
final int lpr = (rows + 63) >>> 6;
|
||||
final int minX = outerLo * CELLS_PER_CHUNK;
|
||||
@@ -446,7 +499,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
|
||||
for (int chunkX = outerLo; chunkX <= outerHi; chunkX++) {
|
||||
for (int chunkZ = outerLo; chunkZ <= outerHi; chunkZ++) {
|
||||
emitChunk(chunkX, chunkZ, anchorX, anchorZ, radiusCellsSq, cameraInsideCloud, emitTopFace, emitBottomFace, y1, ye, scrollX, scrollZ);
|
||||
emitChunk(chunkX, chunkZ, anchorX, anchorZ, radiusCellsSq, emitTopFace, emitBottomFace, y1, ye, scrollX, scrollZ);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,6 +507,53 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
emitTopBottomGlobalGreedy(emitTopFace, emitBottomFace, ye, scrollX, scrollZ);
|
||||
}
|
||||
|
||||
if (emitTopFace && emitBottomFace) {
|
||||
emitInteriorFaces(anchorX, anchorZ, y1, scrollX, scrollZ);
|
||||
}
|
||||
|
||||
finishGeometry();
|
||||
}
|
||||
|
||||
private void emitInteriorFaces(int anchorX, int anchorZ, double y1, float scrollX, float scrollZ) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
if (!edges.cellOpaque(anchorX + dx, anchorZ + dz)) continue;
|
||||
final double x0 = dx + INTERIOR_INSET;
|
||||
final double x1 = dx + 1 - INTERIOR_INSET;
|
||||
final double z0 = dz + INTERIOR_INSET;
|
||||
final double z1 = dz + 1 - INTERIOR_INSET;
|
||||
final double yb = INTERIOR_INSET;
|
||||
final double yt = y1 - INTERIOR_INSET;
|
||||
final float uL = dx * SCROLL_SPEED + scrollX;
|
||||
final float uR = (dx + 1) * SCROLL_SPEED + scrollX;
|
||||
final float uC = (dx + 0.5F) * SCROLL_SPEED + scrollX;
|
||||
final float vN = dz * SCROLL_SPEED + scrollZ;
|
||||
final float vS = (dz + 1) * SCROLL_SPEED + scrollZ;
|
||||
final float vC = (dz + 0.5F) * SCROLL_SPEED + scrollZ;
|
||||
|
||||
ensureEmitCapacity(6 * QUAD_BYTES);
|
||||
emitCloudQuad(x0, yb, z1, uL, vS, x1, yb, z1, uR, vS, x1, yb, z0, uR, vN, x0, yb, z0, uL, vN, F_TOP, N_POS_Y);
|
||||
emitCloudQuad(x0, yt, z0, uL, vN, x1, yt, z0, uR, vN, x1, yt, z1, uR, vS, x0, yt, z1, uL, vS, F_BOTTOM, N_NEG_Y);
|
||||
emitCloudQuad(x0, yb, z0, uC, vN, x0, yt, z0, uC, vN, x0, yt, z1, uC, vS, x0, yb, z1, uC, vS, F_X, N_POS_X);
|
||||
emitCloudQuad(x1, yb, z1, uC, vS, x1, yt, z1, uC, vS, x1, yt, z0, uC, vN, x1, yb, z0, uC, vN, F_X, N_NEG_X);
|
||||
emitCloudQuad(x0, yb, z0, uL, vC, x1, yb, z0, uR, vC, x1, yt, z0, uR, vC, x0, yt, z0, uL, vC, F_Z, N_POS_Z);
|
||||
emitCloudQuad(x0, yt, z1, uL, vC, x1, yt, z1, uR, vC, x1, yb, z1, uR, vC, x0, yb, z1, uL, vC, F_Z, N_NEG_Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void emitFastLayer(int radiusCells, float scrollX, float scrollZ) {
|
||||
final double r0 = -radiusCells;
|
||||
final double r1 = radiusCells;
|
||||
final float u0 = -radiusCells * SCROLL_SPEED + scrollX;
|
||||
final float u1 = radiusCells * SCROLL_SPEED + scrollX;
|
||||
final float v0 = -radiusCells * SCROLL_SPEED + scrollZ;
|
||||
final float v1 = radiusCells * SCROLL_SPEED + scrollZ;
|
||||
ensureEmitCapacity(QUAD_BYTES);
|
||||
emitCloudQuad(r0, 0.0, r0, u0, v0, r1, 0.0, r0, u1, v0, r1, 0.0, r1, u1, v1, r0, 0.0, r1, u0, v1, F_BOTTOM, N_POS_Y);
|
||||
}
|
||||
|
||||
private void finishGeometry() {
|
||||
final int totalBytes = (int) (writeAddr - emitBufferAddr);
|
||||
final int vertexCount = totalBytes / VERTEX_STRIDE;
|
||||
if (vertexCount == 0) {
|
||||
@@ -514,7 +614,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void emitChunk(int chunkX, int chunkZ, int anchorX, int anchorZ, int radiusCellsSq, boolean cameraInsideCloud, boolean emitTopFace, boolean emitBottomFace, double y1, double ye, float scrollX, float scrollZ) {
|
||||
private void emitChunk(int chunkX, int chunkZ, int anchorX, int anchorZ, int radiusCellsSq, boolean emitTopFace, boolean emitBottomFace, double y1, double ye, float scrollX, float scrollZ) {
|
||||
final float chunkOffsetX = chunkX * (float) CELLS_PER_CHUNK;
|
||||
final float chunkOffsetZ = chunkZ * (float) CELLS_PER_CHUNK;
|
||||
final int baseU = chunkX * CELLS_PER_CHUNK + anchorX;
|
||||
@@ -534,43 +634,11 @@ public class CloudRenderer implements IResourceManagerReloadListener {
|
||||
final int relX = chunkX * CELLS_PER_CHUNK + k;
|
||||
final int relZ = chunkZ * CELLS_PER_CHUNK + j;
|
||||
if (relX * relX + relZ * relZ > radiusCellsSq) continue;
|
||||
if (cameraInsideCloud && (Math.abs(relX) > 1 || Math.abs(relZ) > 1)) continue;
|
||||
emittableBits |= 1L << (j * CELLS_PER_CHUNK + k);
|
||||
}
|
||||
}
|
||||
if (emittableBits == 0L) return;
|
||||
|
||||
if (cameraInsideCloud) {
|
||||
long bits = emittableBits;
|
||||
while (bits != 0L) {
|
||||
final int bit = Long.numberOfTrailingZeros(bits);
|
||||
bits &= bits - 1;
|
||||
final int k = bit & 7;
|
||||
final int j = bit >>> 3;
|
||||
final double x0 = chunkOffsetX + k;
|
||||
final double x1 = chunkOffsetX + k + 1;
|
||||
final double z0 = chunkOffsetZ + j;
|
||||
final double z1 = chunkOffsetZ + j + 1;
|
||||
final double xe = x1 - EDGE_OVERLAP;
|
||||
final double zs = z1 - EDGE_OVERLAP;
|
||||
final float uC = (chunkOffsetX + k + 0.5F) * SCROLL_SPEED + scrollX;
|
||||
final float vC = (chunkOffsetZ + j + 0.5F) * SCROLL_SPEED + scrollZ;
|
||||
final float uL = (chunkOffsetX + k) * SCROLL_SPEED + scrollX;
|
||||
final float uR = (chunkOffsetX + k + 1) * SCROLL_SPEED + scrollX;
|
||||
final float vN = (chunkOffsetZ + j) * SCROLL_SPEED + scrollZ;
|
||||
final float vS = (chunkOffsetZ + j + 1) * SCROLL_SPEED + scrollZ;
|
||||
|
||||
ensureEmitCapacity(6 * QUAD_BYTES);
|
||||
emitCloudQuad(x0, 0.0, z1, uL, vS, x1, 0.0, z1, uR, vS, x1, 0.0, z0, uR, vN, x0, 0.0, z0, uL, vN, F_TOP, N_POS_Y);
|
||||
emitCloudQuad(x0, ye, z0, uL, vN, x1, ye, z0, uR, vN, x1, ye, z1, uR, vS, x0, ye, z1, uL, vS, F_BOTTOM, N_NEG_Y);
|
||||
emitCloudQuad(x0, 0.0, z0, uC, vN, x0, y1, z0, uC, vN, x0, y1, z1, uC, vS, x0, 0.0, z1, uC, vS, F_X, N_POS_X);
|
||||
emitCloudQuad(xe, 0.0, z1, uC, vS, xe, y1, z1, uC, vS, xe, y1, z0, uC, vN, xe, 0.0, z0, uC, vN, F_X, N_NEG_X);
|
||||
emitCloudQuad(x0, 0.0, z0, uL, vC, x1, 0.0, z0, uR, vC, x1, y1, z0, uR, vC, x0, y1, z0, uL, vC, F_Z, N_POS_Z);
|
||||
emitCloudQuad(x0, y1, zs, uL, vC, x1, y1, zs, uR, vC, x1, 0.0, zs, uR, vC, x0, 0.0, zs, uL, vC, F_Z, N_NEG_Z);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (emitTopFace || emitBottomFace) {
|
||||
final int globalColStart = chunkX * CELLS_PER_CHUNK - gridMinX;
|
||||
final int globalRowStart = chunkZ * CELLS_PER_CHUNK - gridMinZ;
|
||||
|
||||
@@ -9,6 +9,7 @@ final class CloudUniforms {
|
||||
static final float ALPHA = 0.8f;
|
||||
|
||||
final GlUniformMatrix4f mvp;
|
||||
final GlUniformMatrix4f mv;
|
||||
final GlUniformFloat4v colorMult;
|
||||
final GlUniformFloat4v fogParams;
|
||||
final GlUniformFloat4v fogColor;
|
||||
@@ -18,11 +19,12 @@ final class CloudUniforms {
|
||||
private int lastFogEnabled = -1;
|
||||
private float lastFogR = Float.NaN, lastFogG = Float.NaN, lastFogB = Float.NaN;
|
||||
private float lastColorR = Float.NaN, lastColorG = Float.NaN, lastColorB = Float.NaN;
|
||||
private float lastFogParamX = Float.NaN, lastFogParamY = Float.NaN;
|
||||
private float lastFogParamX = Float.NaN, lastFogParamY = Float.NaN, lastFogParamZ = Float.NaN, lastFogParamW = Float.NaN;
|
||||
private final float[] uploadBuf = new float[4];
|
||||
|
||||
CloudUniforms(ShaderBindingContext ctx) {
|
||||
mvp = ctx.bindUniform("u_MVPMatrix", GlUniformMatrix4f::new);
|
||||
mv = ctx.bindUniform("u_MVMatrix", GlUniformMatrix4f::new);
|
||||
colorMult = ctx.bindUniform("u_ColorMult", GlUniformFloat4v::new);
|
||||
fogParams = ctx.bindUniform("u_FogParams", GlUniformFloat4v::new);
|
||||
fogColor = ctx.bindUniform("u_FogColor", GlUniformFloat4v::new);
|
||||
@@ -51,14 +53,16 @@ final class CloudUniforms {
|
||||
}
|
||||
}
|
||||
|
||||
void setFogParams(float x, float y) {
|
||||
if (x != lastFogParamX || y != lastFogParamY) {
|
||||
void setFogParams(float x, float y, float z, float w) {
|
||||
if (x != lastFogParamX || y != lastFogParamY || z != lastFogParamZ || w != lastFogParamW) {
|
||||
lastFogParamX = x;
|
||||
lastFogParamY = y;
|
||||
lastFogParamZ = z;
|
||||
lastFogParamW = w;
|
||||
uploadBuf[0] = x;
|
||||
uploadBuf[1] = y;
|
||||
uploadBuf[2] = 0f;
|
||||
uploadBuf[3] = 0f;
|
||||
uploadBuf[2] = z;
|
||||
uploadBuf[3] = w;
|
||||
fogParams.set(uploadBuf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
public class AngelicaRenderQueue {
|
||||
private static final Thread MAIN_THREAD = Thread.currentThread();
|
||||
private static final Queue<Runnable> TASKS = new ConcurrentLinkedQueue<>();
|
||||
|
||||
// Metrics
|
||||
@@ -33,11 +34,11 @@ public class AngelicaRenderQueue {
|
||||
}
|
||||
|
||||
private static final Executor EXECUTOR = (runnable) -> {
|
||||
if(Thread.currentThread() == MAIN_THREAD) {
|
||||
if(GLStateManager.isMainThread()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
TASKS.add(runnable);
|
||||
LockSupport.unpark(MAIN_THREAD);
|
||||
LockSupport.unpark(GLStateManager.getMainThread());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,6 +46,11 @@ public class AngelicaRenderQueue {
|
||||
return EXECUTOR;
|
||||
}
|
||||
|
||||
public static void submit(Runnable runnable) {
|
||||
TASKS.add(runnable);
|
||||
LockSupport.unpark(GLStateManager.getMainThread());
|
||||
}
|
||||
|
||||
public static int processTasks(int max) {
|
||||
int tasksRun = 0;
|
||||
while(tasksRun < max) {
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import lombok.Getter;
|
||||
import net.coderbot.iris.gl.image.GlImage;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
/**
|
||||
* Skins in 1.7.10 are 64x32 while modern has 64x64 textures. We have to address it here.
|
||||
*/
|
||||
public final class PlayerReflectionCapture {
|
||||
|
||||
public static final int VERTEX_COUNT = 288;
|
||||
public static final int FLOATS_PER_VERTEX = 5; // x, y, z, u, v
|
||||
|
||||
private static final int RAW_INTS_PER_VERTEX = 8;
|
||||
|
||||
private static final int VERTEX_FLAGS = VertexFlags.convertToFlags(true, false, true, false);
|
||||
|
||||
private static boolean active;
|
||||
private static boolean drawnThisCapture;
|
||||
@Getter
|
||||
private static Object target;
|
||||
|
||||
@Getter
|
||||
private static EntityPlayer targetEntity;
|
||||
|
||||
private static int vbo;
|
||||
private static int vao;
|
||||
|
||||
private static int[] rawData;
|
||||
private static float[] vertexScratch;
|
||||
private static int skin64Tex;
|
||||
private static ByteBuffer skinPixels;
|
||||
private static int lastCapturedSkin;
|
||||
private static int lastUploadedAtlas;
|
||||
private static int skinCopyWidth = 64;
|
||||
private static int skinCopyHeight = 32;
|
||||
private static ByteBuffer repackBuffer;
|
||||
private static long repackAddress;
|
||||
private static int repackCapacity;
|
||||
|
||||
private PlayerReflectionCapture() {}
|
||||
|
||||
public static void begin(EntityPlayer player) {
|
||||
active = false;
|
||||
drawnThisCapture = false;
|
||||
target = null;
|
||||
targetEntity = null;
|
||||
if (player == null) return;
|
||||
|
||||
final Render render = RenderManager.instance.getEntityRenderObject(player);
|
||||
if (render instanceof RendererLivingEntity living && living.mainModel instanceof ModelBiped model) {
|
||||
target = model;
|
||||
targetEntity = player;
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void end() {
|
||||
active = false;
|
||||
target = null;
|
||||
targetEntity = null;
|
||||
}
|
||||
|
||||
public static boolean shouldCapture() {
|
||||
return active && !drawnThisCapture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the 288-vertex player batch VBO.
|
||||
*/
|
||||
public static void drawPlayerCapture(float[] xyzuv) {
|
||||
if (drawnThisCapture) return;
|
||||
drawnThisCapture = true;
|
||||
if (xyzuv == null || xyzuv.length < VERTEX_COUNT * FLOATS_PER_VERTEX) return;
|
||||
|
||||
// Build the Tessellator layout
|
||||
if (rawData == null) rawData = new int[VERTEX_COUNT * RAW_INTS_PER_VERTEX];
|
||||
final int[] raw = rawData;
|
||||
for (int i = 0; i < VERTEX_COUNT; i++) {
|
||||
final int s = i * FLOATS_PER_VERTEX;
|
||||
final int d = i * RAW_INTS_PER_VERTEX;
|
||||
raw[d] = Float.floatToRawIntBits(xyzuv[s]);
|
||||
raw[d + 1] = Float.floatToRawIntBits(xyzuv[s + 1]);
|
||||
raw[d + 2] = Float.floatToRawIntBits(xyzuv[s + 2]);
|
||||
raw[d + 3] = Float.floatToRawIntBits(xyzuv[s + 3]);
|
||||
raw[d + 4] = Float.floatToRawIntBits(xyzuv[s + 4]);
|
||||
raw[d + 5] = 0xFFFFFFFF; // color
|
||||
raw[d + 6] = 0x00008000; // normal
|
||||
raw[d + 7] = 0x00F000F0; // light
|
||||
}
|
||||
|
||||
// Pack into our own native buffer using the player model's vertex format
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[VERTEX_FLAGS];
|
||||
ensureRepackCapacity(VERTEX_COUNT * format.getVertexSize());
|
||||
|
||||
final long writePtr = format.writeToBuffer0(repackAddress, raw, VERTEX_COUNT * RAW_INTS_PER_VERTEX);
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int) (writePtr - repackAddress));
|
||||
|
||||
// Upload to our dedicated VBO at offset 0
|
||||
ensureVAO(format);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, repackBuffer, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Bind a 64x64 copy of the skin as `tex`
|
||||
final int activeSkin = GLStateManager.getBoundTextureForServerState();
|
||||
final boolean haveSkin = activeSkin != 0 && activeSkin != skin64Tex;
|
||||
if (haveSkin) {
|
||||
ensureSkin64Texture();
|
||||
|
||||
final GlImage atlas = GlImage.BY_NAME.get("playerAtlas_img");
|
||||
final int atlasId = atlas != null ? atlas.getId() : 0;
|
||||
final boolean skinChanged = activeSkin != lastCapturedSkin;
|
||||
|
||||
if (skinChanged) {
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(activeSkin);
|
||||
final int srcW = info.getWidth();
|
||||
final int srcH = info.getHeight();
|
||||
if (srcW > 0 && srcH > 0) {
|
||||
final int required = srcW * srcH * 4;
|
||||
if (skinPixels == null || skinPixels.capacity() < required) {
|
||||
skinPixels = ByteBuffer.allocateDirect(required).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
// The 1.7.10 model UVs only address the top 64x32 region.
|
||||
skinCopyWidth = Math.min(64, srcW);
|
||||
skinCopyHeight = Math.min(32, srcH);
|
||||
|
||||
skinPixels.clear();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, activeSkin);
|
||||
GLStateManager.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, skinPixels);
|
||||
lastCapturedSkin = activeSkin;
|
||||
}
|
||||
}
|
||||
|
||||
if (skinPixels != null && (skinChanged || atlasId != lastUploadedAtlas)) {
|
||||
skinPixels.rewind();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, skin64Tex);
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, skinCopyWidth, skinCopyHeight, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, skinPixels);
|
||||
|
||||
if (atlas != null) {
|
||||
skinPixels.rewind();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, atlasId);
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, skinCopyWidth, skinCopyHeight, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, skinPixels);
|
||||
}
|
||||
lastUploadedAtlas = atlasId;
|
||||
}
|
||||
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, skin64Tex);
|
||||
}
|
||||
|
||||
final ColorMask prevColor = GLStateManager.getColorMask();
|
||||
final boolean pr = prevColor.red, pg = prevColor.green, pb = prevColor.blue, pa = prevColor.alpha;
|
||||
final boolean prevDepthWrite = GLStateManager.getDepthState().isEnabled();
|
||||
|
||||
GLStateManager.glColorMask(false, false, false, false);
|
||||
GLStateManager.glDepthMask(false);
|
||||
QuadConverter.drawQuadsAsTriangles(0, VERTEX_COUNT);
|
||||
GLStateManager.glDepthMask(prevDepthWrite);
|
||||
GLStateManager.glColorMask(pr, pg, pb, pa);
|
||||
|
||||
if (haveSkin) {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, activeSkin);
|
||||
}
|
||||
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public static float[] vertexScratch() {
|
||||
if (vertexScratch == null) vertexScratch = new float[VERTEX_COUNT * FLOATS_PER_VERTEX];
|
||||
return vertexScratch;
|
||||
}
|
||||
|
||||
private static void ensureVAO(VertexFormat format) {
|
||||
if (vao != 0) return;
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
format.setupBufferState(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static void ensureSkin64Texture() {
|
||||
if (skin64Tex != 0) return;
|
||||
final int prev = GLStateManager.getBoundTextureForServerState();
|
||||
skin64Tex = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, skin64Tex);
|
||||
final ByteBuffer zero = ByteBuffer.allocateDirect(64 * 64 * 4);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 64, 64, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, zero);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, prev);
|
||||
}
|
||||
|
||||
private static void ensureRepackCapacity(int requiredBytes) {
|
||||
if (repackBuffer != null && requiredBytes <= repackCapacity) return;
|
||||
int newCapacity = Math.max(0x4000, repackCapacity);
|
||||
while (newCapacity < requiredBytes) newCapacity *= 2;
|
||||
if (repackBuffer != null) memFree(repackBuffer);
|
||||
repackBuffer = memAlloc(newCapacity);
|
||||
repackAddress = memAddress0(repackBuffer);
|
||||
repackCapacity = newCapacity;
|
||||
}
|
||||
}
|
||||
+17
-104
@@ -22,15 +22,14 @@
|
||||
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ChunkTrackerAccessor;
|
||||
import it.unimi.dsi.fastutil.longs.LongCollection;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSets;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSectionManager;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkStatus;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTracker;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTrackerImpl;
|
||||
import org.embeddedt.embeddium.impl.util.PositionUtil;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
|
||||
/**
|
||||
* Configurable chunk tracker supporting "fast" and "aggressive" loading modes.
|
||||
* Based on Beddium's SafeChunkTracker by Ven and FalsePattern.
|
||||
@@ -41,109 +40,23 @@ import org.embeddedt.embeddium.impl.util.PositionUtil;
|
||||
*
|
||||
* Mode can be switched at runtime via options menu - triggers renderer reload.
|
||||
*/
|
||||
public class AngelicaChunkTracker extends ChunkTracker {
|
||||
private final LongOpenHashSet chunkReadyForced = new LongOpenHashSet();
|
||||
public class AngelicaChunkTracker extends ChunkTrackerImpl {
|
||||
|
||||
private ChunkTrackerAccessor self() {
|
||||
return (ChunkTrackerAccessor) this;
|
||||
@Override
|
||||
public void forEachReady(RenderSectionManager sectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
}
|
||||
|
||||
super.forEachReady(sectionManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkStatusAdded(int x, int z, int flags) {
|
||||
final var key = PositionUtil.packChunk(x, z);
|
||||
final var prev = self().angelica$getChunkStatus().get(key);
|
||||
final var cur = prev | flags;
|
||||
|
||||
if (prev == cur) {
|
||||
return;
|
||||
public void forEachEvent(RenderSectionManager sectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
}
|
||||
|
||||
self().angelica$getChunkStatus().put(key, cur);
|
||||
this.updateNeighbors(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkStatusRemoved(int x, int z, int flags) {
|
||||
final var key = PositionUtil.packChunk(x, z);
|
||||
final var prev = self().angelica$getChunkStatus().get(key);
|
||||
final int cur = prev & ~flags;
|
||||
|
||||
if (prev == cur) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cur == self().angelica$getChunkStatus().defaultReturnValue()) {
|
||||
self().angelica$getChunkStatus().remove(key);
|
||||
} else {
|
||||
self().angelica$getChunkStatus().put(key, cur);
|
||||
}
|
||||
|
||||
this.updateNeighbors(x, z);
|
||||
}
|
||||
|
||||
private void updateNeighbors(int x, int z) {
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
for (int oz = -1; oz <= 1; oz++) {
|
||||
this.updateMerged(ox + x, oz + z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMerged(int x, int z) {
|
||||
final long key = PositionUtil.packChunk(x, z);
|
||||
final int selfFlags = self().angelica$getChunkStatus().get(key);
|
||||
|
||||
// If self doesn't have FLAG_ALL, can't be ready at all
|
||||
if (selfFlags != ChunkStatus.FLAG_ALL) {
|
||||
// Remove from ready if present
|
||||
final boolean wasReady = self().angelica$getChunkReady().remove(key) | this.chunkReadyForced.remove(key);
|
||||
if (wasReady && !self().angelica$getLoadQueue().remove(key)) {
|
||||
self().angelica$getUnloadQueue().add(key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all neighbors also have FLAG_ALL
|
||||
int mergedFlags = selfFlags;
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
for (int oz = -1; oz <= 1; oz++) {
|
||||
mergedFlags &= self().angelica$getChunkStatus().get(PositionUtil.packChunk(ox + x, oz + z));
|
||||
}
|
||||
}
|
||||
|
||||
if (mergedFlags == ChunkStatus.FLAG_ALL) {
|
||||
// All neighbors ready - add to both sets -- Trigger load if newly added to chunkReadyForced (first time ready)
|
||||
if ((this.chunkReadyForced.add(key) || self().angelica$getChunkReady().add(key)) && !self().angelica$getUnloadQueue().remove(key)) {
|
||||
self().angelica$getLoadQueue().add(key);
|
||||
}
|
||||
} else {
|
||||
// Self ready but neighbors not - add to forced, remove from proper
|
||||
// Trigger load if state changed (moved from proper to forced, or newly forced)
|
||||
if ((this.chunkReadyForced.add(key) || self().angelica$getChunkReady().remove(key)) && !self().angelica$getUnloadQueue().remove(key)) {
|
||||
self().angelica$getLoadQueue().add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongCollection getReadyChunks() {
|
||||
if (AngelicaConfig.useVanillaChunkTracking) {
|
||||
// Aggressive mode: return both ready sets
|
||||
final LongOpenHashSet combined = new LongOpenHashSet(self().angelica$getChunkReady());
|
||||
combined.addAll(this.chunkReadyForced);
|
||||
return LongSets.unmodifiable(combined);
|
||||
} else {
|
||||
// Default mode: only return chunks with all neighbors ready
|
||||
return LongSets.unmodifiable(self().angelica$getChunkReady());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEvent(ChunkEventHandler loadEventHandler, ChunkEventHandler unloadEventHandler) {
|
||||
ChunkTracker.forEachChunk(self().angelica$getUnloadQueue(), unloadEventHandler);
|
||||
self().angelica$getUnloadQueue().clear();
|
||||
|
||||
ChunkTracker.forEachChunk(self().angelica$getLoadQueue(), loadEventHandler);
|
||||
self().angelica$getLoadQueue().clear();
|
||||
super.forEachEvent(sectionManager);
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -1,5 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.cardinalstar.cubicchunks.world.ICubicWorld;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.RenderSectionManagerAccessor;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.AngelicaRenderQueue;
|
||||
@@ -53,7 +56,18 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
|
||||
public static AngelicaRenderSectionManager create(ChunkVertexType vertexType, WorldClient world, int renderDistance, CommandList commandList) {
|
||||
final ChunkTaskProvider provider = ChunkTaskRegistry.getActiveProvider();
|
||||
return new AngelicaRenderSectionManager(AngelicaRenderPassConfiguration.build(vertexType), world, renderDistance, commandList, 0, 16, provider.threadCount(), provider);
|
||||
final int minSection;
|
||||
final int maxSection;
|
||||
|
||||
if (ModStatus.isCubicChunksLoaded && world instanceof ICubicWorld) {
|
||||
minSection = CubicChunksAPI.getMinSectionY(world);
|
||||
maxSection = CubicChunksAPI.getMaxSectionYExclusive(world);
|
||||
} else {
|
||||
minSection = 0;
|
||||
maxSection = 16;
|
||||
}
|
||||
|
||||
return new AngelicaRenderSectionManager(AngelicaRenderPassConfiguration.build(vertexType), world, renderDistance, commandList, minSection, maxSection, provider.threadCount(), provider);
|
||||
}
|
||||
|
||||
public void setCameraPosition(double x, double y, double z) {
|
||||
@@ -125,6 +139,12 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
if (chunk.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
final var section = CubicChunksAPI.getCubeStorage(this.world, x, y, z);
|
||||
return section == null || section.isEmpty();
|
||||
}
|
||||
|
||||
final var array = chunk.getBlockStorageArray();
|
||||
if (y < 0 || y >= array.length) {
|
||||
return true;
|
||||
@@ -198,6 +218,12 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
return IrisShaderProviderHolder.isShadowPass();
|
||||
}
|
||||
|
||||
public void markShadowGraphDirty() {
|
||||
if (this.shadowRenderListManager != null) {
|
||||
this.shadowRenderListManager.setNeedsUpdate(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getDebugStrings() {
|
||||
List<String> list = new ArrayList<>(super.getDebugStrings());
|
||||
|
||||
+28
-16
@@ -1,15 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderingState;
|
||||
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
@@ -17,6 +12,7 @@ import net.minecraft.entity.Entity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
@@ -32,10 +28,18 @@ import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderingState;
|
||||
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
|
||||
public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, AngelicaRenderSectionManager, BlockRenderLayer, TileEntity, CeleritasWorldRenderer.TileEntityRenderContext> implements IDynamicLightWorldRenderer {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
@@ -110,12 +114,20 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
|
||||
@Override
|
||||
public int getMinimumBuildHeight() {
|
||||
return 0;
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
return CubicChunksAPI.getMinHeight(this.world);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumBuildHeight() {
|
||||
return 256;
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
return CubicChunksAPI.getMaxHeight(this.world);
|
||||
} else {
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTracker;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTrackerHolder;
|
||||
|
||||
import com.cardinalstar.cubicchunks.api.event.CubeEvent;
|
||||
import com.cardinalstar.cubicchunks.util.CubePos;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
public class CubeStatusEvents {
|
||||
|
||||
public static void init() {
|
||||
MinecraftForge.EVENT_BUS.register(new CubeStatusEvents());
|
||||
}
|
||||
|
||||
private ChunkTracker getTracker() {
|
||||
return ChunkTrackerHolder.get(Minecraft.getMinecraft().theWorld);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onCubeLoaded(CubeEvent.Load event) {
|
||||
if (!event.world.isRemote) return;
|
||||
|
||||
if (getTracker() instanceof CubeStatusTracker cubic) {
|
||||
CubePos pos = event.pos;
|
||||
cubic.onCubeLoaded(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onCubeUnloaded(CubeEvent.Unload event) {
|
||||
if (!event.world.isRemote) return;
|
||||
|
||||
if (getTracker() instanceof CubeStatusTracker cubic) {
|
||||
CubePos pos = event.pos;
|
||||
cubic.onCubeUnloaded(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSectionManager;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTracker;
|
||||
|
||||
import com.cardinalstar.cubicchunks.util.HashMap3D;
|
||||
import com.cardinalstar.cubicchunks.util.HashSet3D;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
|
||||
public class CubeStatusTracker implements ChunkTracker {
|
||||
|
||||
/// Cubes that have been synced to the client and are currently loaded.
|
||||
private final HashSet3D loadedCubes = new HashSet3D();
|
||||
/// Cubes that have their neighbours loaded (as needed) and are ready to be rendered.
|
||||
private final HashSet3D validCubes = new HashSet3D();
|
||||
|
||||
private final HashMap3D<PendingState> changedCubes = new HashMap3D<>();
|
||||
|
||||
private enum PendingState {
|
||||
Add,
|
||||
Remove
|
||||
}
|
||||
|
||||
/// When true, adjacent cubes will not be checked.
|
||||
private boolean fastMode = false;
|
||||
|
||||
public CubeStatusTracker() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFastMode(boolean fastMode) {
|
||||
this.fastMode = fastMode;
|
||||
|
||||
this.loadedCubes.forEach(this::pollCube);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFastModeEnabled() {
|
||||
return fastMode;
|
||||
}
|
||||
|
||||
public void onCubeLoaded(int cubeX, int cubeY, int cubeZ) {
|
||||
if (loadedCubes.add(cubeX, cubeY, cubeZ)) {
|
||||
onCubeChanged(cubeX, cubeY, cubeZ);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCubeUnloaded(int cubeX, int cubeY, int cubeZ) {
|
||||
if (loadedCubes.remove(cubeX, cubeY, cubeZ)) {
|
||||
onCubeChanged(cubeX, cubeY, cubeZ);
|
||||
}
|
||||
}
|
||||
|
||||
private void onCubeChanged(int cubeX, int cubeY, int cubeZ) {
|
||||
pollCube(cubeX, cubeY, cubeZ);
|
||||
|
||||
if (!fastMode) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
if (dx == 0 && dy == 0 && dz == 0) continue;
|
||||
|
||||
pollCube(cubeX + dx, cubeY + dy, cubeZ + dz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pollCube(int cubeX, int cubeY, int cubeZ) {
|
||||
boolean renderable = canRender(cubeX, cubeY, cubeZ);
|
||||
boolean isRendering = validCubes.contains(cubeX, cubeY, cubeZ);
|
||||
|
||||
if (renderable != isRendering) {
|
||||
changedCubes.put(cubeX, cubeY, cubeZ, renderable ? PendingState.Add : PendingState.Remove);
|
||||
} else {
|
||||
changedCubes.remove(cubeX, cubeY, cubeZ);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canRender(int cubeX, int cubeY, int cubeZ) {
|
||||
if (!loadedCubes.contains(cubeX, cubeY, cubeZ)) return false;
|
||||
|
||||
if (!fastMode) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
if (dx == 0 && dy == 0 && dz == 0) continue;
|
||||
|
||||
if (!loadedCubes.contains(cubeX + dx, cubeY + dy, cubeZ + dz)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkStatusAdded(int i, int i1, int i2) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkStatusRemoved(int i, int i1, int i2) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachReady(RenderSectionManager renderSectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
}
|
||||
|
||||
changedCubes.forEach((x, y, z, state) -> {
|
||||
boolean ready = state == PendingState.Add;
|
||||
|
||||
if (ready) {
|
||||
validCubes.add(x, y, z);
|
||||
} else {
|
||||
validCubes.remove(x, y, z);
|
||||
}
|
||||
});
|
||||
|
||||
changedCubes.clear();
|
||||
|
||||
for (var pos : validCubes.fastEntryIterable()) {
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
|
||||
renderSectionManager.onSectionAdded(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEvent(RenderSectionManager renderSectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
}
|
||||
|
||||
changedCubes.forEach((x, y, z, state) -> {
|
||||
boolean ready = state == PendingState.Add;
|
||||
|
||||
if (ready) {
|
||||
validCubes.add(x, y, z);
|
||||
renderSectionManager.onSectionAdded(x, y, z);
|
||||
} else {
|
||||
validCubes.remove(x, y, z);
|
||||
renderSectionManager.onSectionRemoved(x, y, z);
|
||||
}
|
||||
});
|
||||
|
||||
changedCubes.clear();
|
||||
}
|
||||
}
|
||||
+30
-20
@@ -1,26 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas.world;
|
||||
|
||||
import com.gtnewhorizons.angelica.api.SectionLightData;
|
||||
import com.gtnewhorizons.angelica.api.IBlockAccessExtended;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.ChunkSectionPos;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.CompatMathHelper;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightSource;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.SmoothBiomeColorCache;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
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 org.embeddedt.embeddium.impl.util.position.SectionPos;
|
||||
import cpw.mods.fml.common.Optional;
|
||||
import mega.fluidlogged.api.FLBlockAccess;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
@@ -30,9 +16,24 @@ import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
import net.minecraft.world.gen.structure.StructureBoundingBox;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
|
||||
import org.embeddedt.embeddium.impl.util.position.SectionPos;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import com.gtnewhorizons.angelica.api.IBlockAccessExtended;
|
||||
import com.gtnewhorizons.angelica.api.SectionLightData;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.ChunkSectionPos;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.CompatMathHelper;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightSource;
|
||||
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 cpw.mods.fml.common.Optional;
|
||||
import mega.fluidlogged.api.FLBlockAccess;
|
||||
|
||||
/**
|
||||
* Takes a slice of world state (block states, biome and light data arrays) and copies the data for use in off-thread
|
||||
@@ -111,7 +112,13 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
|
||||
public static ChunkRenderContext prepare(World world, ChunkSectionPos origin, ClonedChunkSectionCache sectionCache) {
|
||||
final Chunk chunk = world.getChunkFromChunkCoords(origin.x, origin.z);
|
||||
final ExtendedBlockStorage section = chunk.getBlockStorageArray()[origin.y];
|
||||
final ExtendedBlockStorage section;
|
||||
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
section = CubicChunksAPI.getCubeStorage(world, origin.x, origin.y, origin.z);
|
||||
} else {
|
||||
section = chunk.getBlockStorageArray()[origin.y];
|
||||
}
|
||||
|
||||
// If the chunk section is absent or empty, terminate early
|
||||
if (section == null || section.isEmpty()) {
|
||||
@@ -241,7 +248,10 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
|
||||
@Override
|
||||
public int getLightBrightnessForSkyBlocks(int x, int y, int z, int min) {
|
||||
if (y < 0 || y >= 256 || x < -30_000_000 || z < -30_000_000 || x >= 30_000_000 || z >= 30_000_000) {
|
||||
final int minHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMinHeight(this.world) : 0;
|
||||
final int maxHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMaxHeight(this.world) : 256;
|
||||
|
||||
if (y < minHeight || y >= maxHeight || x < -30_000_000 || z < -30_000_000 || x >= 30_000_000 || z >= 30_000_000) {
|
||||
// skyBrightness = 15, blockBrightness = min
|
||||
return (15 << 20) | (min << 4);
|
||||
}
|
||||
@@ -372,7 +382,7 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
|
||||
if (!blockBoxContains(this.volume, x, y, z)) {
|
||||
return 0;
|
||||
}
|
||||
y = MathHelper.clamp_int(y, 0, 255);
|
||||
y = CompatMathHelper.clampHeight(y);
|
||||
|
||||
final int relX = x - this.baseX;
|
||||
final int relY = y - this.baseY;
|
||||
|
||||
+39
-29
@@ -4,12 +4,11 @@ import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.api.SectionLightData;
|
||||
import com.gtnewhorizons.angelica.compat.ExtendedBlockStorageExt;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.lotr.LOTRCompat;
|
||||
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 it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
|
||||
import mega.fluidlogged.internal.mixin.hook.FLSubChunk;
|
||||
|
||||
@@ -25,9 +24,13 @@ import net.minecraft.world.chunk.NibbleArray;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
import net.minecraftforge.fluids.Fluid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ClonedChunkSection {
|
||||
|
||||
private static final ExtendedBlockStorage EMPTY_SECTION = new ExtendedBlockStorage(0, false);
|
||||
private static final int SECTION_BLOCK_LENGTH = 16;
|
||||
private static final int BIOME_DATA_LENGTH = SECTION_BLOCK_LENGTH * SECTION_BLOCK_LENGTH;
|
||||
|
||||
private final ClonedChunkSectionCache backingCache;
|
||||
private final World world;
|
||||
@@ -53,7 +56,13 @@ public class ClonedChunkSection {
|
||||
throw new RuntimeException("Couldn't retrieve chunk at " + pos.toChunkPos());
|
||||
}
|
||||
|
||||
ExtendedBlockStorage section = getChunkSection(chunk, pos);
|
||||
ExtendedBlockStorage section;
|
||||
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
section = CubicChunksAPI.getCubeStorage(world, pos.x, pos.y, pos.z);
|
||||
} else {
|
||||
section = getChunkSection(chunk, pos);
|
||||
}
|
||||
|
||||
if (section == null) {
|
||||
section = EMPTY_SECTION;
|
||||
@@ -62,7 +71,7 @@ public class ClonedChunkSection {
|
||||
this.pos = pos;
|
||||
this.data = new ExtendedBlockStorageExt(chunk, section);
|
||||
|
||||
this.biomeData = new BiomeGenBase[16 * 16];
|
||||
this.biomeData = new BiomeGenBase[BIOME_DATA_LENGTH];
|
||||
|
||||
copyBlockEntities(chunk, pos);
|
||||
|
||||
@@ -72,15 +81,22 @@ public class ClonedChunkSection {
|
||||
}
|
||||
|
||||
private void fillBiomeData(Chunk chunk) {
|
||||
final WorldChunkManager wcm = world.getWorldChunkManager();
|
||||
for (int lz = 0; lz < 16; lz++) {
|
||||
for (int lx = 0; lx < 16; lx++) {
|
||||
this.biomeData[lx | (lz << 4)] = chunk.getBiomeGenForWorldCoords(lx, lz, wcm);
|
||||
if (LOTRCompat.isLotrProvider(world.provider)) {
|
||||
for (int z = pos.getMinZ(); z <= pos.getMaxZ(); z++) {
|
||||
for (int x = pos.getMinX(); x <= pos.getMaxX(); x++) {
|
||||
this.biomeData[((z & 15) << 4) | (x & 15)] = world.getBiomeGenForCoords(x, z);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final WorldChunkManager wcm = world.getWorldChunkManager();
|
||||
for (int lz = 0; lz < SECTION_BLOCK_LENGTH; lz++) {
|
||||
for (int lx = 0; lx < SECTION_BLOCK_LENGTH; lx++) {
|
||||
this.biomeData[(lz << 4) | lx] = chunk.getBiomeGenForWorldCoords(lx, lz, wcm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void copyBlockEntities(Chunk chunk, ChunkSectionPos pos) {
|
||||
this.tileEntities.clear();
|
||||
|
||||
@@ -88,22 +104,13 @@ public class ClonedChunkSection {
|
||||
|
||||
map.readLock();
|
||||
try {
|
||||
final Object2ObjectOpenHashMap<ChunkPosition, TileEntity> delegate = map.getDelegate();
|
||||
|
||||
if (!delegate.isEmpty()) {
|
||||
final int minY = pos.getMinY();
|
||||
final int maxY = pos.getMaxY();
|
||||
|
||||
for (Object2ObjectMap.Entry<ChunkPosition, TileEntity> entry : Object2ObjectMaps.fastIterable(delegate)) {
|
||||
final ChunkPosition tePos = entry.getKey();
|
||||
if (tePos.chunkPosY < minY || tePos.chunkPosY > maxY) continue;
|
||||
|
||||
final TileEntity te = entry.getValue();
|
||||
if (te != null && !te.isInvalid()) {
|
||||
this.tileEntities.put(ChunkSectionPos.packLocal(tePos.chunkPosX & 15, tePos.chunkPosY & 15, tePos.chunkPosZ & 15), te);
|
||||
}
|
||||
}
|
||||
}
|
||||
map.forEachInYRange(pos.getMinY(), pos.getMaxY(), entry -> {
|
||||
final ChunkPosition tePos = entry.getKey();
|
||||
this.tileEntities.put(
|
||||
ChunkSectionPos.packLocal(tePos.chunkPosX & 15, tePos.chunkPosY & 15, tePos.chunkPosZ & 15),
|
||||
entry.getValue()
|
||||
);
|
||||
});
|
||||
} finally {
|
||||
map.readUnlock();
|
||||
}
|
||||
@@ -169,12 +176,15 @@ public class ClonedChunkSection {
|
||||
this.lastUsedTimestamp = timestamp;
|
||||
}
|
||||
|
||||
public static boolean isOutOfBuildLimitVertically(int y) {
|
||||
public static boolean isOutOfBuildLimitVertically(World world, int y) {
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
return y < CubicChunksAPI.getMinHeight(world) || y >= CubicChunksAPI.getMaxHeight(world);
|
||||
}
|
||||
return y < 0 || y >= 256;
|
||||
}
|
||||
|
||||
private static ExtendedBlockStorage getChunkSection(Chunk chunk, ChunkSectionPos pos) {
|
||||
if (!isOutOfBuildLimitVertically(ChunkSectionPos.getBlockCoord(pos.y))) {
|
||||
private ExtendedBlockStorage getChunkSection(Chunk chunk, ChunkSectionPos pos) {
|
||||
if (!isOutOfBuildLimitVertically(this.world, ChunkSectionPos.getBlockCoord(pos.y))) {
|
||||
return chunk.getBlockStorageArray()[pos.y];
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.RenderThreadContext;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.ChunkPosition;
|
||||
@@ -10,15 +11,30 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Thread-safe wrapper around Object2ObjectOpenHashMap for chunkTileEntityMap.
|
||||
* Thread-safe wrapper around a tile entity map for chunkTileEntityMap.
|
||||
* May wrap either a plain Object2ObjectOpenHashMap (vanilla) or a ColumnTileEntityMap (cubic chunks).
|
||||
*/
|
||||
public class ConcurrentTileEntityMap implements Map<ChunkPosition, TileEntity> {
|
||||
private final Object2ObjectOpenHashMap<ChunkPosition, TileEntity> delegate = new Object2ObjectOpenHashMap<>();
|
||||
private final Object2ObjectOpenHashMap<ChunkPosition, TileEntity> flatDelegate;
|
||||
private final Map<ChunkPosition, TileEntity> delegate;
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final ConcurrentLinkedQueue<ChunkPosition> invalidationQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
/** Vanilla path: backs the map with a fresh flat map. */
|
||||
public ConcurrentTileEntityMap() {
|
||||
this.flatDelegate = new Object2ObjectOpenHashMap<>();
|
||||
this.delegate = this.flatDelegate;
|
||||
}
|
||||
|
||||
/** cubic chunks path: wraps an existing map (e.g. ColumnTileEntityMap). */
|
||||
public ConcurrentTileEntityMap(Map<ChunkPosition, TileEntity> toWrap) {
|
||||
this.flatDelegate = null;
|
||||
this.delegate = toWrap;
|
||||
}
|
||||
|
||||
public void queueInvalidation(ChunkPosition pos) {
|
||||
invalidationQueue.add(pos);
|
||||
}
|
||||
@@ -59,14 +75,27 @@ public class ConcurrentTileEntityMap implements Map<ChunkPosition, TileEntity> {
|
||||
}
|
||||
}
|
||||
|
||||
public Object2ObjectOpenHashMap<ChunkPosition, TileEntity> getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public TileEntity putDirect(ChunkPosition key, TileEntity value) {
|
||||
return delegate.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates all non-invalid tile entities whose Y coordinate falls within [minY, maxY].
|
||||
* Caller must hold the read lock.
|
||||
*/
|
||||
public void forEachInYRange(int minY, int maxY, Consumer<Map.Entry<ChunkPosition, TileEntity>> action) {
|
||||
final Iterable<? extends Map.Entry<ChunkPosition, TileEntity>> entries =
|
||||
flatDelegate != null ? Object2ObjectMaps.fastIterable(flatDelegate) : delegate.entrySet();
|
||||
for (Map.Entry<ChunkPosition, TileEntity> entry : entries) {
|
||||
final ChunkPosition tePos = entry.getKey();
|
||||
if (tePos.chunkPosY < minY || tePos.chunkPosY > maxY) continue;
|
||||
final TileEntity te = entry.getValue();
|
||||
if (te != null && !te.isInvalid()) {
|
||||
action.accept(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isRenderWorkerThread() {
|
||||
return RenderThreadContext.hasWorldSlice();
|
||||
}
|
||||
@@ -135,7 +164,10 @@ public class ConcurrentTileEntityMap implements Map<ChunkPosition, TileEntity> {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
invalidationQueue.clear();
|
||||
delegate.clear();
|
||||
if (flatDelegate != null) {
|
||||
flatDelegate.clear();
|
||||
}
|
||||
// wrapped delegate manages its own TE lifetime; skip clear()
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import com.prupe.mcpatcher.mal.block.BlockStateMatcher;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
final class BlockOrientation extends RenderBlockState {
|
||||
|
||||
@@ -39,12 +40,13 @@ final class BlockOrientation extends RenderBlockState {
|
||||
private int altMetadata;
|
||||
private int metadataBits;
|
||||
private int renderType;
|
||||
private boolean iCtmBlock;
|
||||
|
||||
private int blockFace;
|
||||
private int textureFace;
|
||||
private int textureFaceOrig;
|
||||
private int rotateUV;
|
||||
private boolean flipBottom;
|
||||
private boolean flipped;
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
@@ -57,7 +59,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
offsetsComputed = false;
|
||||
haveOffsets = false;
|
||||
dx = dy = dz = 0;
|
||||
flipBottom = !fixedBottomFaceUV;
|
||||
flipped = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,6 +77,11 @@ final class BlockOrientation extends RenderBlockState {
|
||||
return z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockFace() {
|
||||
return blockFace;
|
||||
@@ -102,7 +109,10 @@ final class BlockOrientation extends RenderBlockState {
|
||||
|
||||
@Override
|
||||
public int[] getOffset(int blockFace, int relativeDirection) {
|
||||
return NEIGHBOR_OFFSET[flipBottom && blockFace == 0 ? 1 : blockFace][rotateUV(relativeDirection)];
|
||||
if(flipped){
|
||||
blockFace = ForgeDirection.OPPOSITES[blockFace];
|
||||
}
|
||||
return NEIGHBOR_OFFSET[blockFace][rotateUV(relativeDirection)];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,14 +156,14 @@ final class BlockOrientation extends RenderBlockState {
|
||||
}
|
||||
|
||||
|
||||
public void setFlipBottom(){
|
||||
flipBottom = true;
|
||||
public void flipFace() {
|
||||
flipped = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConnectByBlock(Block neighbor, int neighborX, int neighborY, int neighborZ) {
|
||||
return block == neighbor
|
||||
&& (metadataBits & (1 << blockAccess.getBlockMetadata(neighborX, neighborY, neighborZ))) != 0;
|
||||
return iCtmBlock ? ((ICTMBlock) block).shouldConnectByBlock(this, neighbor, neighborX, neighborY, neighborZ)
|
||||
: block == neighbor && (metadataBits & (1 << blockAccess.getBlockMetadata(neighborX, neighborY, neighborZ))) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -171,11 +181,12 @@ final class BlockOrientation extends RenderBlockState {
|
||||
copy.altMetadata = altMetadata;
|
||||
copy.metadataBits = metadataBits;
|
||||
copy.renderType = renderType;
|
||||
copy.iCtmBlock = iCtmBlock;
|
||||
copy.blockFace = blockFace;
|
||||
copy.textureFace = textureFace;
|
||||
copy.textureFaceOrig = textureFaceOrig;
|
||||
copy.rotateUV = rotateUV;
|
||||
copy.flipBottom = flipBottom;
|
||||
copy.flipped = flipped;
|
||||
|
||||
copy.blockAccess = blockAccess;
|
||||
copy.block = block;
|
||||
@@ -199,6 +210,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
this.z = z;
|
||||
renderType = block.getRenderType();
|
||||
metadata = altMetadata = blockAccess.getBlockMetadata(x, y, z);
|
||||
iCtmBlock = block instanceof ICTMBlock;
|
||||
offsetsComputed = false;
|
||||
}
|
||||
|
||||
@@ -208,7 +220,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
rotateUV = 0;
|
||||
textureFace = blockFaceToTextureFace(blockFace);
|
||||
metadataBits = (1 << metadata) | (1 << altMetadata);
|
||||
flipBottom = !fixedBottomFaceUV;
|
||||
flipped = !fixedBottomFaceUV && blockFace == ForgeDirection.DOWN.ordinal();
|
||||
}
|
||||
|
||||
void setBlockMetadata(Block block, int metadata, int face) {
|
||||
@@ -219,6 +231,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
renderType = block.getRenderType();
|
||||
blockFace = textureFace = textureFaceOrig = face;
|
||||
this.metadata = metadata;
|
||||
iCtmBlock = block instanceof ICTMBlock;
|
||||
metadataBits = 1 << metadata;
|
||||
dx = dy = dz = 0;
|
||||
rotateUV = 0;
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.github.bsideup.jabel.Desugar;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -34,10 +35,22 @@ public class CTMUtils {
|
||||
|
||||
public static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
private static class Overrides {
|
||||
public static class Overrides {
|
||||
private final List<TileOverride> all = new ArrayList<>();
|
||||
private final Map<Block, List<BlockStateMatcher>> block = new IdentityHashMap<>();
|
||||
private final Map<String, List<TileOverride>> tile = new HashMap<>();
|
||||
|
||||
public List<TileOverride> getAll() {
|
||||
return all;
|
||||
}
|
||||
|
||||
public Map<Block, List<BlockStateMatcher>> getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public Map<String, List<TileOverride>> getTile() {
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
|
||||
private static Overrides overrides = new Overrides();
|
||||
@@ -82,6 +95,8 @@ public class CTMUtils {
|
||||
|
||||
private static final AtomicInteger texturePackChangeCounter = new AtomicInteger(0);
|
||||
|
||||
private static final ArrayList<Consumer<Overrides>> ctmRegistrationCallbacks = new ArrayList<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName(MCPatcherUtils.RENDER_PASS_CLASS)
|
||||
@@ -117,6 +132,9 @@ public class CTMUtils {
|
||||
.listResources(TexturePackAPI.MCPATCHER_SUBDIR + "ctm", ".properties", true)) {
|
||||
registerOverrideWithoutLock(newOverrides, TileOverride.create(resource, tileLoader));
|
||||
}
|
||||
for (Consumer<Overrides> callback : ctmRegistrationCallbacks) {
|
||||
callback.accept(newOverrides);
|
||||
}
|
||||
}
|
||||
for (ResourceLocation resource : BlendMethod.getAllBlankResources()) {
|
||||
tileLoader.preloadTile(resource, false);
|
||||
@@ -257,7 +275,7 @@ public class CTMUtils {
|
||||
return RenderPassAPI.instance.skipDefaultRendering(block);
|
||||
}
|
||||
|
||||
private static void registerOverrideWithoutLock(Overrides overrides, TileOverride override) {
|
||||
public static void registerOverrideWithoutLock(Overrides overrides, TileOverride override) {
|
||||
if (override != null && !override.isDisabled()) {
|
||||
boolean registered = false;
|
||||
List<BlockStateMatcher> matchingBlocks = override.getMatchingBlocks();
|
||||
@@ -308,4 +326,8 @@ public class CTMUtils {
|
||||
private static TileOverrideIterator.Metadata newMetadataIterator() {
|
||||
return new TileOverrideIterator.Metadata(overrides.block, overrides.tile);
|
||||
}
|
||||
|
||||
public static void addCtmRegistrationCallback(Consumer<Overrides> consumer) {
|
||||
ctmRegistrationCallbacks.add(consumer);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
public final class CompactCtmQuadProcessor {
|
||||
|
||||
@@ -52,29 +53,33 @@ public final class CompactCtmQuadProcessor {
|
||||
this.override = compact;
|
||||
}
|
||||
|
||||
public boolean processFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
if(renderBlockState instanceof BlockOrientation blockOrientation){
|
||||
blockOrientation.setFlipBottom();
|
||||
public boolean processFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon origIcon, int face) {
|
||||
boolean shouldFlip = face == ForgeDirection.DOWN.ordinal();
|
||||
if(ForgeDirection.OPPOSITES[face] == renderBlockState.getBlockFace()){
|
||||
shouldFlip = !shouldFlip;
|
||||
}
|
||||
if(shouldFlip && renderBlockState instanceof BlockOrientation blockOrientation){
|
||||
blockOrientation.flipFace();
|
||||
}
|
||||
int neighborBits = override.getNeighborBits(renderBlockState, origIcon);
|
||||
|
||||
int ctmIndex = TileOverride.neighborMap[neighborBits & 0xFF];
|
||||
IIcon replacement = getReplacementIcon(ctmIndex);
|
||||
if (replacement != null) {
|
||||
renderWholeFace(rb, renderBlockState, replacement);
|
||||
renderWholeFace(rb, renderBlockState, replacement, face);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (neighborBits == 0 || neighborBits == 0xFF) {
|
||||
IIcon sprite = sprites[getCompactSpriteIndex(neighborBits)];
|
||||
if (sprite != null) {
|
||||
renderWholeFace(rb, renderBlockState, sprite);
|
||||
renderWholeFace(rb, renderBlockState, sprite, face);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
renderSplitFace(rb, renderBlockState, neighborBits);
|
||||
renderSplitFace(rb, renderBlockState, neighborBits, face);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -117,8 +122,7 @@ public final class CompactCtmQuadProcessor {
|
||||
return SPRITE_DEFAULT;
|
||||
}
|
||||
|
||||
private void renderSplitFace(RenderBlocks rb, RenderBlockState renderBlockState, int connections) {
|
||||
final int face = renderBlockState.getBlockFace();
|
||||
private void renderSplitFace(RenderBlocks rb, RenderBlockState renderBlockState, int connections, int face) {
|
||||
|
||||
final double minX = rb.renderMinX, minY = rb.renderMinY, minZ = rb.renderMinZ;
|
||||
final double maxX = rb.renderMaxX, maxY = rb.renderMaxY, maxZ = rb.renderMaxZ;
|
||||
@@ -155,7 +159,7 @@ public final class CompactCtmQuadProcessor {
|
||||
}
|
||||
setQuadrantBounds(rb, face, q, minX, minY, minZ, maxX, maxY, maxZ);
|
||||
rb.overrideBlockTexture = icon;
|
||||
renderFace(rb, renderBlockState, icon);
|
||||
renderFace(rb, renderBlockState, icon, face);
|
||||
}
|
||||
|
||||
if (ao) {
|
||||
@@ -219,18 +223,17 @@ public final class CompactCtmQuadProcessor {
|
||||
return (sky << 16) | (block & 0xFFFF);
|
||||
}
|
||||
|
||||
private void renderWholeFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon) {
|
||||
private void renderWholeFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon, int face) {
|
||||
IIcon saved = rb.overrideBlockTexture;
|
||||
rb.overrideBlockTexture = icon;
|
||||
try {
|
||||
renderFace(rb, renderBlockState, icon);
|
||||
renderFace(rb, renderBlockState, icon, face);
|
||||
} finally {
|
||||
rb.overrideBlockTexture = saved;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon) {
|
||||
int face = renderBlockState.getBlockFace();
|
||||
private void renderFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon icon, int face) {
|
||||
Block block = renderBlockState.getBlock();
|
||||
int x = renderBlockState.getX();
|
||||
int y = renderBlockState.getY();
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.prupe.mcpatcher.ctm;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
/**
|
||||
* Implement this interface on blocks that support connected textures to allow them to control if they connect to neighbors or not
|
||||
*/
|
||||
public interface ICTMBlock {
|
||||
boolean shouldConnectByBlock(RenderBlockState renderBlockState, Block neighbor, int neighborX, int neighborY, int neighborZ);
|
||||
}
|
||||
@@ -109,6 +109,8 @@ abstract public class RenderBlockState {
|
||||
|
||||
abstract public int getZ();
|
||||
|
||||
abstract public int getMetadata();
|
||||
|
||||
abstract public int getBlockFace();
|
||||
|
||||
abstract public int getTextureFace();
|
||||
|
||||
@@ -33,7 +33,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
|
||||
private static final MCLogger logger = MCLogger.getLogger(MCLogger.Category.CONNECTED_TEXTURES, "CTM");
|
||||
|
||||
protected final PropertiesFile properties;
|
||||
public final PropertiesFile properties;
|
||||
private final String baseFilename;
|
||||
protected final TileLoader tileLoader;
|
||||
protected final int renderPass;
|
||||
@@ -79,7 +79,10 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
if (properties == null) {
|
||||
return null;
|
||||
}
|
||||
return create(properties, tileLoader);
|
||||
}
|
||||
|
||||
public static TileOverride create(PropertiesFile properties, TileLoader tileLoader) {
|
||||
String method = properties.getString("method", "default")
|
||||
.toLowerCase();
|
||||
TileOverride override = null;
|
||||
@@ -211,7 +214,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
for (int i = 0;; i++) {
|
||||
ResourceLocation resource = TileLoader
|
||||
.parseTileAddress(properties.getResource(), String.valueOf(i), blankResource);
|
||||
if (!TexturePackAPI.hasResource(resource)) {
|
||||
if (!TexturePackAPI.hasResource(resource) && !tileLoader.ignoreMissingTextures()) {
|
||||
break;
|
||||
}
|
||||
if (!addIcon(resource)) {
|
||||
@@ -237,7 +240,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
.parseTileAddress(properties.getResource(), token, blankResource);
|
||||
if (resource == null) {
|
||||
tileNames.add(null);
|
||||
} else if (TexturePackAPI.hasResource(resource)) {
|
||||
} else if (TexturePackAPI.hasResource(resource) || tileLoader.ignoreMissingTextures()) {
|
||||
addIcon(resource);
|
||||
} else {
|
||||
properties.warning("could not find image %s", resource);
|
||||
@@ -252,7 +255,7 @@ public abstract class TileOverride implements Comparable<TileOverride> {
|
||||
for (int i = from; i <= to; i++) {
|
||||
ResourceLocation resource = TileLoader
|
||||
.parseTileAddress(properties.getResource(), String.valueOf(i), blankResource);
|
||||
if (TexturePackAPI.hasResource(resource)) {
|
||||
if (TexturePackAPI.hasResource(resource) || tileLoader.ignoreMissingTextures()) {
|
||||
addIcon(resource);
|
||||
} else {
|
||||
properties.warning("could not find image %s", resource);
|
||||
|
||||
@@ -9,12 +9,12 @@ import net.minecraft.util.ResourceLocation;
|
||||
import com.prupe.mcpatcher.MCLogger;
|
||||
import com.prupe.mcpatcher.MCPatcherUtils;
|
||||
|
||||
final public class PropertiesFile {
|
||||
public class PropertiesFile {
|
||||
|
||||
private final MCLogger logger;
|
||||
private final ResourceLocation resource;
|
||||
private final String prefix;
|
||||
private final Properties properties;
|
||||
protected final MCLogger logger;
|
||||
protected final ResourceLocation resource;
|
||||
protected final String prefix;
|
||||
protected final Properties properties;
|
||||
private int errorCount;
|
||||
|
||||
public static PropertiesFile get(MCLogger logger, ResourceLocation resource) {
|
||||
@@ -147,4 +147,16 @@ final public class PropertiesFile {
|
||||
return resource.equals(((PropertiesFile) that).resource);
|
||||
}
|
||||
}
|
||||
|
||||
public MCLogger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class TileLoader {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map) {
|
||||
protected boolean registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map) {
|
||||
ResourceLocation resource = tilesToRegister.iterator()
|
||||
.next();
|
||||
String name = resource.toString();
|
||||
@@ -304,6 +304,16 @@ public class TileLoader {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TextureAtlasSprite icon = registerIconToMap(textureMap, resource, name);
|
||||
map.put(name, icon);
|
||||
iconMap.put(name, icon);
|
||||
String extra = (width == height ? "" : ", " + (height / width) + " frames");
|
||||
subLogger.finer("%s -> %s icon %dx%d%s", name, mapName, width, width, extra);
|
||||
tilesToRegister.remove(resource);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected TextureAtlasSprite registerIconToMap(TextureMap textureMap, ResourceLocation resource, String name) {
|
||||
TextureAtlasSprite icon;
|
||||
if(generatedTiles.contains(resource)){
|
||||
icon = new GeneratedCTMAtlasSprite(this, textureMap.anisotropicFiltering > 1, name);
|
||||
@@ -311,12 +321,7 @@ public class TileLoader {
|
||||
}else{
|
||||
icon = (TextureAtlasSprite)textureMap.registerIcon(name);
|
||||
}
|
||||
map.put(name, icon);
|
||||
iconMap.put(name, icon);
|
||||
String extra = (width == height ? "" : ", " + (height / width) + " frames");
|
||||
subLogger.finer("%s -> %s icon %dx%d%s", name, mapName, width, width, extra);
|
||||
tilesToRegister.remove(resource);
|
||||
return true;
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
@@ -339,4 +344,8 @@ public class TileLoader {
|
||||
public IIcon getIcon(ResourceLocation resource) {
|
||||
return resource == null ? null : getIcon(resource.toString());
|
||||
}
|
||||
|
||||
public boolean ignoreMissingTextures() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@ public enum Settings {
|
||||
},
|
||||
VOID_FOG(new NotFineOptionTickBox(false, OptionImpact.LOW)),
|
||||
ZOOM_SMOOTH(new NotFineOptionTickBox(true, OptionImpact.LOW)),
|
||||
ZOOM_SMOOTH_SPEED(new NotFineOptionSlider(14, 1, 30, 1, OptionImpact.LOW));
|
||||
ZOOM_SMOOTH_SPEED(new NotFineOptionSlider(20, 1, 30, 1, OptionImpact.LOW));
|
||||
|
||||
public final NotFineOption<?> option;
|
||||
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
package me.jellysquid.mods.sodium.client.gui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import com.cardinalstar.cubicchunks.api.compat.CubicChunksVideoSettings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import cpw.mods.fml.common.Optional.Method;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import jss.notfine.core.Settings;
|
||||
import jss.notfine.core.SettingsManager;
|
||||
@@ -22,19 +36,12 @@ import me.jellysquid.mods.sodium.client.gui.options.named.LightingQuality;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.named.ParticleMode;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.AngelicaOptionsStorage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.CubicChunksOptionStorage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
|
||||
import net.coderbot.iris.Iris;
|
||||
import net.coderbot.iris.gui.option.IrisVideoSettings;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.occlusion.AsyncOcclusionMode;
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SodiumGameOptionPages {
|
||||
private static final SodiumOptionsStorage sodiumOpts = new SodiumOptionsStorage();
|
||||
@@ -54,7 +61,13 @@ public class SodiumGameOptionPages {
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
.build());
|
||||
|
||||
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
firstGroupBuilder.add(getCCVerticalViewDistance());
|
||||
}
|
||||
|
||||
if(Iris.enabled) {
|
||||
|
||||
final OptionImpl<GameSettings, Integer> maxShadowDistanceSlider = OptionImpl.createBuilder(int.class, vanillaOpts)
|
||||
.setName(I18n.format("options.iris.shadowDistance"))
|
||||
.setTooltip(I18n.format("options.iris.shadowDistance.sodium_tooltip"))
|
||||
@@ -499,6 +512,27 @@ public class SodiumGameOptionPages {
|
||||
return new OptionPage(I18n.format("sodium.options.pages.performance"), ImmutableList.copyOf(groups));
|
||||
}
|
||||
|
||||
@Method(modid = "cubicchunks")
|
||||
private static OptionImpl<?, ?> getCCVerticalViewDistance() {
|
||||
CubicChunksOptionStorage storage = new CubicChunksOptionStorage();
|
||||
|
||||
return OptionImpl.createBuilder(int.class, storage)
|
||||
.setName(I18n.format("sodium.options.cc.vertical_view_distance.name"))
|
||||
.setTooltip(I18n.format("sodium.options.cc.vertical_view_distance.tooltip"))
|
||||
.setControl(option -> new SliderControl(
|
||||
option,
|
||||
CubicChunksVideoSettings.getMinVerticalViewDistance(),
|
||||
CubicChunksVideoSettings.getMaxVerticalViewDistance(),
|
||||
1,
|
||||
ControlValueFormatter.quantity("options.chunks")))
|
||||
.setBinding(
|
||||
(options, value) -> options.verticalViewDistance = value,
|
||||
options -> options.verticalViewDistance)
|
||||
.setImpact(OptionImpact.HIGH)
|
||||
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static OptionPage appearance() {
|
||||
final List<OptionGroup> groups = new ArrayList<>();
|
||||
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package me.jellysquid.mods.sodium.client.gui.options.storage;
|
||||
|
||||
import com.cardinalstar.cubicchunks.api.compat.CubicChunksVideoSettings;
|
||||
|
||||
public class CubicChunksOptionStorage implements OptionStorage<CubicChunksOptionStorage.CubicChunksOptions> {
|
||||
|
||||
public static class CubicChunksOptions {
|
||||
public int verticalViewDistance = CubicChunksVideoSettings.getVerticalViewDistance();
|
||||
}
|
||||
|
||||
private final CubicChunksOptions options = new CubicChunksOptions();
|
||||
|
||||
@Override
|
||||
public CubicChunksOptions getData() {
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
CubicChunksVideoSettings.setVerticalViewDistance(options.verticalViewDistance);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import java.util.function.BooleanSupplier;
|
||||
public enum FeatureFlags {
|
||||
SEPARATE_HARDWARE_SAMPLERS(() -> true, () -> true),
|
||||
PER_BUFFER_BLENDING(() -> true, RenderSystem::supportsBufferBlending),
|
||||
COMPUTE_SHADERS(() -> false, RenderSystem::supportsCompute),
|
||||
COMPUTE_SHADERS(() -> true, RenderSystem::supportsCompute),
|
||||
ENTITY_TRANSLUCENT(() -> true, () -> true),
|
||||
BLOCK_EMISSION_ATTRIBUTE(() -> true, () -> true),
|
||||
CUSTOM_IMAGES(() -> true, RenderSystem::supportsImageLoadStore),
|
||||
|
||||
@@ -12,7 +12,12 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class GlImage extends GlObject {
|
||||
public static final Map<String, GlImage> BY_NAME = new ConcurrentHashMap<>();
|
||||
|
||||
protected final String name;
|
||||
protected final String samplerName;
|
||||
protected final TextureType target;
|
||||
@@ -42,6 +47,8 @@ public class GlImage extends GlObject {
|
||||
setup(texture, width, height, depth);
|
||||
|
||||
GLStateManager.glBindTexture(target.getGlType(), 0);
|
||||
|
||||
BY_NAME.put(name, this);
|
||||
}
|
||||
|
||||
protected void setup(int texture, int width, int height, int depth) {
|
||||
@@ -106,6 +113,7 @@ public class GlImage extends GlObject {
|
||||
@Override
|
||||
protected void destroyInternal() {
|
||||
GLStateManager.glDeleteTextures(handle());
|
||||
BY_NAME.remove(name, this);
|
||||
}
|
||||
|
||||
public InternalTextureFormat getInternalFormat() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.compat.toremove.MatrixStack;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.rendering.PlayerReflectionCapture;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderingState;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
import net.coderbot.iris.Iris;
|
||||
@@ -468,11 +469,13 @@ public class ShadowRenderer {
|
||||
profiler.endStartSection("build geometry");
|
||||
|
||||
setupEntityShadowState(modelView, cameraX, cameraY, cameraZ);
|
||||
PlayerReflectionCapture.begin(player);
|
||||
try {
|
||||
for (Entity entity : renderedEntitiesList) {
|
||||
RenderManager.instance.renderEntitySimple(entity, tickDelta);
|
||||
}
|
||||
} finally {
|
||||
PlayerReflectionCapture.end();
|
||||
teardownEntityShadowState();
|
||||
}
|
||||
|
||||
@@ -516,7 +519,7 @@ public class ShadowRenderer {
|
||||
private void renderPlayerEntity(EntityRenderer levelRenderer, Frustrum frustum, Object bufferSource, MatrixStack modelView, double cameraX, double cameraY, double cameraZ, float tickDelta) {
|
||||
profiler.startSection("cull");
|
||||
|
||||
Entity player = Minecraft.getMinecraft().thePlayer;
|
||||
EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
|
||||
// Skip if spectating or outside frustum
|
||||
if (GameModeUtil.isSpectator()) {
|
||||
@@ -547,7 +550,12 @@ public class ShadowRenderer {
|
||||
shadowEntities++;
|
||||
}
|
||||
|
||||
RenderManager.instance.renderEntitySimple(player, tickDelta);
|
||||
PlayerReflectionCapture.begin(player);
|
||||
try {
|
||||
RenderManager.instance.renderEntitySimple(player, tickDelta);
|
||||
} finally {
|
||||
PlayerReflectionCapture.end();
|
||||
}
|
||||
shadowEntities++;
|
||||
} finally {
|
||||
teardownEntityShadowState();
|
||||
@@ -684,7 +692,7 @@ public class ShadowRenderer {
|
||||
// Mark the shadow graph as needing update before terrain setup
|
||||
// Modern Celeritas does this to ensure the shadow render lists get populated
|
||||
com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer.getInstance()
|
||||
.getRenderSectionManager().markGraphDirty();
|
||||
.getRenderSectionManager().markShadowGraphDirty();
|
||||
|
||||
// Execute the vanilla terrain setup / culling routines using our shadow frustum.
|
||||
mc.renderGlobal.clipRenderersByFrustum(terrainFrustumHolder.getFrustum(), playerCamera.getPartialTicks());
|
||||
|
||||
@@ -17,6 +17,8 @@ sodium.options.pages.appearance=Appearance
|
||||
sodium.options.pages.text=Text Effects
|
||||
sodium.options.view_distance.tooltip=The render distance controls how far away terrain will be rendered. Shorter distances mean that less terrain will be rendered, improving frame rates.
|
||||
sodium.options.simulation_distance.tooltip=The simulation distance controls how far away terrain and entities will be loaded and ticked. Shorter distances can reduce the internal server's load and may improve frame rates.
|
||||
sodium.options.cc.vertical_view_distance.name=Vertical View Distance
|
||||
sodium.options.cc.vertical_view_distance.tooltip=The vertical view distance controls the vertical range within which cubes will be loaded, centered on the player. Shorter distances mean that less terrain will be rendered, improving frame rates.
|
||||
sodium.options.brightness.tooltip=Controls the brightness (gamma) of the game.
|
||||
sodium.options.clouds.name=Clouds
|
||||
sodium.options.clouds.tooltip=Controls whether or not clouds will be visible.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#version 330 core
|
||||
|
||||
in vec2 v_UV;
|
||||
in float v_FogCoord;
|
||||
in vec3 v_EyePos;
|
||||
in float v_Factor;
|
||||
|
||||
uniform sampler2D u_Tex;
|
||||
uniform vec4 u_ColorMult;
|
||||
uniform vec4 u_FogParams; // x: -1/(end-start), y: end/(end-start)
|
||||
uniform vec4 u_FogParams; // linear: x=-1/(end-start), y=end/(end-start); exp/exp2: z=density; w=mode (0=linear, 1=exp, 2=exp2)
|
||||
uniform vec4 u_FogColor;
|
||||
uniform bool u_FogEnabled;
|
||||
|
||||
@@ -19,8 +19,17 @@ void main() {
|
||||
color.rgb *= v_Factor;
|
||||
|
||||
if (u_FogEnabled) {
|
||||
float f = clamp(v_FogCoord * u_FogParams.x + u_FogParams.y, 0.0, 1.0);
|
||||
color.rgb = mix(u_FogColor.rgb, color.rgb, f);
|
||||
float fogCoord = length(v_EyePos);
|
||||
float f;
|
||||
if (u_FogParams.w == 0.0) {
|
||||
f = fogCoord * u_FogParams.x + u_FogParams.y;
|
||||
} else if (u_FogParams.w == 1.0) {
|
||||
f = exp(-u_FogParams.z * fogCoord);
|
||||
} else {
|
||||
float d = u_FogParams.z * fogCoord;
|
||||
f = exp(-d * d);
|
||||
}
|
||||
color.rgb = mix(u_FogColor.rgb, color.rgb, clamp(f, 0.0, 1.0));
|
||||
}
|
||||
|
||||
fragColor = color;
|
||||
|
||||
@@ -5,14 +5,15 @@ layout(location = 2) in vec2 a_UV;
|
||||
layout(location = 5) in float a_Factor;
|
||||
|
||||
uniform mat4 u_MVPMatrix;
|
||||
uniform mat4 u_MVMatrix;
|
||||
|
||||
out vec2 v_UV;
|
||||
out float v_FogCoord;
|
||||
out vec3 v_EyePos;
|
||||
out float v_Factor;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_MVPMatrix * vec4(a_Position, 1.0);
|
||||
v_UV = a_UV;
|
||||
v_FogCoord = length(a_Position.xz);
|
||||
v_EyePos = (u_MVMatrix * vec4(a_Position, 1.0)).xyz;
|
||||
v_Factor = a_Factor;
|
||||
}
|
||||
+56
-12
@@ -13,6 +13,7 @@ import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -22,7 +23,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Single-pass damage-overlay tint, modeled on modern Minecraft's OverlayTexture.
|
||||
* The tint is applied only while the main body model renders.
|
||||
*/
|
||||
@Mixin(RendererLivingEntity.class)
|
||||
public class MixinRendererLivingEntity_OverlayTint {
|
||||
@@ -62,13 +62,20 @@ public class MixinRendererLivingEntity_OverlayTint {
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderModel(Lnet/minecraft/entity/EntityLivingBase;FFFFFF)V",
|
||||
shift = At.Shift.AFTER),
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderEquippedItems(Lnet/minecraft/entity/EntityLivingBase;F)V"),
|
||||
require = 1, expect = 1
|
||||
)
|
||||
private void angelica$clearBodyTint(EntityLivingBase entity, double x, double y, double z,
|
||||
float yaw, float partialTick, CallbackInfo ci) {
|
||||
angelica$clearOverlayColor();
|
||||
private void angelica$teardownOverlay(EntityLivingBase entity, double x, double y, double z,
|
||||
float yaw, float partialTick, CallbackInfo ci) {
|
||||
if (angelica$ffpOverlayActive) {
|
||||
GLStateManager.setOverlayColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$ffpOverlayActive = false;
|
||||
}
|
||||
if (angelica$shaderEntityColorActive) {
|
||||
// Don't tint held items
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$shaderEntityColorActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,17 +98,45 @@ public class MixinRendererLivingEntity_OverlayTint {
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the overlay on additive passes (emissive eyes, enchantment glint).
|
||||
*/
|
||||
@WrapOperation(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/model/ModelBase;render(Lnet/minecraft/entity/Entity;FFFFFF)V"),
|
||||
slice = @Slice(
|
||||
from = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;shouldRenderPass(Lnet/minecraft/entity/EntityLivingBase;IF)I"),
|
||||
to = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/entity/RendererLivingEntity;renderEquippedItems(Lnet/minecraft/entity/EntityLivingBase;F)V")
|
||||
),
|
||||
require = 1, expect = 1
|
||||
)
|
||||
private void angelica$untintAdditivePass(ModelBase model, Entity entity,
|
||||
float p1, float p2, float p3, float p4, float p5, float p6, Operation<Void> original) {
|
||||
if (!angelica$skipReRender || !angelica$isAdditivePass()) {
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
return;
|
||||
}
|
||||
if (angelica$ffpOverlayActive) {
|
||||
GLStateManager.setOverlayColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
GLStateManager.setOverlayColor(1.0F, 0.0F, 0.0F, ANGELICA$RED_MIX);
|
||||
} else if (angelica$shaderEntityColorActive) {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(1.0F, 0.0F, 0.0F, ANGELICA$RED_MIX);
|
||||
} else {
|
||||
original.call(model, entity, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "doRender(Lnet/minecraft/entity/EntityLivingBase;DDDFF)V",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void angelica$reset(CallbackInfo ci) {
|
||||
angelica$clearOverlayColor();
|
||||
angelica$skipReRender = false;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$clearOverlayColor() {
|
||||
if (angelica$ffpOverlayActive) {
|
||||
GLStateManager.setOverlayColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$ffpOverlayActive = false;
|
||||
@@ -110,6 +145,15 @@ public class MixinRendererLivingEntity_OverlayTint {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
angelica$shaderEntityColorActive = false;
|
||||
}
|
||||
angelica$skipReRender = false;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean angelica$isAdditivePass() {
|
||||
if (!GLStateManager.glIsEnabled(GL11.GL_BLEND)) return false;
|
||||
if (GLStateManager.glGetInteger(GL14.GL_BLEND_DST_RGB) != GL11.GL_ONE) return false;
|
||||
final int src = GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_RGB);
|
||||
return src == GL11.GL_ONE || src == GL11.GL_SRC_COLOR;
|
||||
}
|
||||
|
||||
@Unique
|
||||
|
||||
+9
-1
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.mixins.interfaces.ClippingHelperExt;
|
||||
import net.minecraft.client.renderer.culling.ClippingHelper;
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -18,17 +19,24 @@ public class MixinClippingHelper implements ClippingHelperExt {
|
||||
@Unique private final FrustumIntersection celeritas$frustumIntersection = new FrustumIntersection();
|
||||
@Unique private final Matrix4f celeritas$projMatrix = new Matrix4f();
|
||||
@Unique private final Matrix4f celeritas$mvMatrix = new Matrix4f();
|
||||
@Unique private final Matrix4f celeritas$combined = new Matrix4f();
|
||||
|
||||
@Override
|
||||
public FrustumIntersection celeritas$getFrustumIntersection() {
|
||||
return this.celeritas$frustumIntersection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matrix4fc celeritas$getCombinedMatrix() {
|
||||
return this.celeritas$combined;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void celeritas$updateFrustumIntersection() {
|
||||
this.celeritas$projMatrix.set(this.projectionMatrix);
|
||||
this.celeritas$mvMatrix.set(this.modelviewMatrix);
|
||||
this.celeritas$frustumIntersection.set(this.celeritas$projMatrix.mul(this.celeritas$mvMatrix));
|
||||
this.celeritas$combined.set(this.celeritas$projMatrix).mul(this.celeritas$mvMatrix);
|
||||
this.celeritas$frustumIntersection.set(this.celeritas$combined, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-2
@@ -5,23 +5,48 @@ import net.minecraft.client.renderer.culling.ClippingHelper;
|
||||
import net.minecraft.client.renderer.culling.Frustrum;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.ViewportProvider;
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Vector3d;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Frustrum.class)
|
||||
public abstract class MixinFrustrum implements ViewportProvider {
|
||||
@Shadow @Final private ClippingHelper clippingHelper;
|
||||
@Shadow private double xPosition, yPosition, zPosition;
|
||||
|
||||
@Unique private final FrustumIntersection celeritas$snapshot = new FrustumIntersection();
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void celeritas$snapshotFrustum(CallbackInfo ci) {
|
||||
this.celeritas$snapshot.set(((ClippingHelperExt) this.clippingHelper).celeritas$getCombinedMatrix(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Viewport sodium$createViewport() {
|
||||
// Shadow frustum subclasses override this via their own ViewportProvider impl.
|
||||
// This default handles vanilla Frustrum only.
|
||||
// This default handles vanilla Frustrum only, using the per-instance snapshot
|
||||
return new Viewport(
|
||||
((ClippingHelperExt) this.clippingHelper).celeritas$getFrustumIntersection()::testAab,
|
||||
this.celeritas$snapshot::testAab,
|
||||
new Vector3d(this.xPosition, this.yPosition, this.zPosition)
|
||||
);
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public boolean isBoxInFrustum(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||
if (Double.isInfinite(minX) || Double.isInfinite(minY) || Double.isInfinite(minZ)
|
||||
|| Double.isInfinite(maxX) || Double.isInfinite(maxY) || Double.isInfinite(maxZ)) {
|
||||
return true;
|
||||
}
|
||||
return this.celeritas$snapshot.testAab(
|
||||
(float) (minX - this.xPosition), (float) (minY - this.yPosition), (float) (minZ - this.zPosition),
|
||||
(float) (maxX - this.xPosition), (float) (maxY - this.yPosition), (float) (maxZ - this.zPosition)
|
||||
);
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTracker;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ChunkTracker.class)
|
||||
public interface ChunkTrackerAccessor extends com.gtnewhorizons.angelica.mixins.interfaces.ChunkTrackerAccessor {
|
||||
@Accessor("chunkStatus")
|
||||
Long2IntOpenHashMap angelica$getChunkStatus();
|
||||
|
||||
@Accessor("chunkReady")
|
||||
LongOpenHashSet angelica$getChunkReady();
|
||||
|
||||
@Accessor("unloadQueue")
|
||||
LongSet angelica$getUnloadQueue();
|
||||
|
||||
@Accessor("loadQueue")
|
||||
LongSet angelica$getLoadQueue();
|
||||
}
|
||||
+18
-6
@@ -2,11 +2,6 @@ package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.hash.Fnv1a64;
|
||||
import com.gtnewhorizons.angelica.event.ChunkBiomeDataChangedEvent;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IChunkTileEntityMapHolder;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderThreadContext;
|
||||
import com.gtnewhorizons.angelica.utils.ConcurrentTileEntityMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.ChunkPosition;
|
||||
@@ -25,10 +20,17 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.hash.Fnv1a64;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.event.ChunkBiomeDataChangedEvent;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IChunkTileEntityMapHolder;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderThreadContext;
|
||||
import com.gtnewhorizons.angelica.utils.ConcurrentTileEntityMap;
|
||||
|
||||
@Mixin(Chunk.class)
|
||||
public abstract class MixinChunk implements IChunkTileEntityMapHolder {
|
||||
|
||||
@Shadow public Map<ChunkPosition, TileEntity> chunkTileEntityMap = new ConcurrentTileEntityMap();
|
||||
@Shadow public Map<ChunkPosition, TileEntity> chunkTileEntityMap;
|
||||
@Shadow public World worldObj;
|
||||
@Shadow private ExtendedBlockStorage[] storageArrays;
|
||||
@Final @Shadow public int xPosition;
|
||||
@@ -41,6 +43,16 @@ public abstract class MixinChunk implements IChunkTileEntityMapHolder {
|
||||
@Unique private long angelica$biomeHash;
|
||||
@Unique private boolean angelica$biomeHashValid;
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void angelica$wrapTEMapIfNeeded(CallbackInfo ci) {
|
||||
// Wrap the cubic chunks map, since it uses a custom map
|
||||
if (ModStatus.isCubicChunksLoaded) {
|
||||
this.chunkTileEntityMap = new ConcurrentTileEntityMap(this.chunkTileEntityMap);
|
||||
} else {
|
||||
this.chunkTileEntityMap = new ConcurrentTileEntityMap();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcurrentTileEntityMap angelica$getConcurrentTEMap() {
|
||||
return (ConcurrentTileEntityMap) this.chunkTileEntityMap;
|
||||
|
||||
+7
-4
@@ -1,19 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.AngelicaChunkTracker;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.SmoothBiomeColorCache;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.WorldClientExtension;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTracker;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTrackerHolder;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.AngelicaChunkTracker;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CubeStatusTracker;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.SmoothBiomeColorCache;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.WorldClientExtension;
|
||||
|
||||
@Mixin(WorldClient.class)
|
||||
public class MixinWorldClient implements ChunkTrackerHolder, WorldClientExtension {
|
||||
@Unique private final ChunkTracker celeritas$tracker = new AngelicaChunkTracker();
|
||||
@Unique private final ChunkTracker celeritas$tracker = ModStatus.isCubicChunksLoaded ? new CubeStatusTracker() : new AngelicaChunkTracker();
|
||||
@Unique private final SmoothBiomeColorCache celeritas$smoothBiomeColorCache = new SmoothBiomeColorCache((IBlockAccess) this);
|
||||
|
||||
@Override
|
||||
|
||||
+7
-1
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.notfine.clouds;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.render.CloudRenderer;
|
||||
import jss.notfine.core.SettingsManager;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -12,7 +14,11 @@ import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
public abstract class MixinEntityRenderer {
|
||||
@ModifyArg(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false), index = 3)
|
||||
private float notFine$modifyFarPlane(float original) {
|
||||
return Math.max(original, SettingsManager.minimumFarPlaneDistance);
|
||||
float required = SettingsManager.minimumFarPlaneDistance;
|
||||
if (AngelicaConfig.enableVBOClouds) {
|
||||
required = Math.max(required, CloudRenderer.getCloudRenderer().getRequiredFarPlaneDistance());
|
||||
}
|
||||
return Math.max(original, required);
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.rendering;
|
||||
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(BlockLiquid.class)
|
||||
public interface BlockLiquidFlowInvoker {
|
||||
|
||||
@Invoker("getFlowVector")
|
||||
Vec3 angelica$getFlowVector(IBlockAccess world, int x, int y, int z);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.rendering;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(BlockLiquid.class)
|
||||
public class MixinBlockLiquid {
|
||||
|
||||
@Redirect(
|
||||
method = "getFlowDirection",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockLiquid;getFlowVector(Lnet/minecraft/world/IBlockAccess;III)Lnet/minecraft/util/Vec3;"))
|
||||
private static Vec3 angelica$flowVectorOfActualBlock(BlockLiquid singleton, IBlockAccess world, int x, int y, int z) {
|
||||
final Block block = world.getBlock(x, y, z);
|
||||
final BlockLiquid liquid = block instanceof BlockLiquid ? (BlockLiquid) block : singleton;
|
||||
return ((BlockLiquidFlowInvoker) liquid).angelica$getFlowVector(world, x, y, z);
|
||||
}
|
||||
}
|
||||
+9
-8
@@ -21,6 +21,7 @@ import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -185,7 +186,7 @@ public abstract class MixinRenderBlocks {
|
||||
public IBlockAccess blockAccess;
|
||||
|
||||
@Unique
|
||||
private void angelica$handleCompactCtmFace(IIcon icon, CallbackInfo ci) {
|
||||
private void angelica$handleCompactCtmFace(IIcon icon, ForgeDirection direction, CallbackInfo ci) {
|
||||
CTMUtils.CTMCompactContext ctx = CTMUtils.getCurrentCompact();
|
||||
if (ctx == null) {
|
||||
return;
|
||||
@@ -197,39 +198,39 @@ public abstract class MixinRenderBlocks {
|
||||
return;
|
||||
}
|
||||
|
||||
if (processor.processFace((RenderBlocks)(Object)this, renderBlockState, icon)) {
|
||||
if (processor.processFace((RenderBlocks)(Object)this, renderBlockState, icon, direction.ordinal())) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ForgeDirection.DOWN, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceYPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceYPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ForgeDirection.UP, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ForgeDirection.NORTH, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceZPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceZPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ForgeDirection.SOUTH, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXNeg", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXNeg(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ForgeDirection.WEST, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderFaceXPos", at = @At("HEAD"), cancellable = true)
|
||||
private void compactCtm_onRenderFaceXPos(Block block, double x, double y, double z, IIcon icon, CallbackInfo ci) {
|
||||
angelica$handleCompactCtmFace(icon, ci);
|
||||
angelica$handleCompactCtmFace(icon, ForgeDirection.EAST, ci);
|
||||
}
|
||||
|
||||
@Inject(method = "renderStandardBlock(Lnet/minecraft/block/Block;III)Z", at = @At("RETURN"))
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import net.minecraft.client.model.ModelBox;
|
||||
import net.minecraft.client.model.TexturedQuad;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ModelBox.class)
|
||||
public interface AccessorModelBox {
|
||||
|
||||
@Accessor("quadList")
|
||||
TexturedQuad[] angelica$getQuadList();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.PlayerReflectionCapture;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.model.ModelBox;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.client.model.PositionTextureVertex;
|
||||
import net.minecraft.client.model.TexturedQuad;
|
||||
import net.minecraft.entity.Entity;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Mixin(ModelBiped.class)
|
||||
public abstract class MixinModelBiped {
|
||||
|
||||
@Unique private static final Matrix4f angelica$mat = new Matrix4f();
|
||||
@Unique private static final Vector3f angelica$vec = new Vector3f();
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/entity/Entity;FFFFFF)V", at = @At("RETURN"))
|
||||
private void angelica$capturePlayerReflection(Entity entity, float limbSwing, float limbSwingAmount,
|
||||
float ageInTicks, float netHeadYaw, float headPitch, float scale, CallbackInfo ci) {
|
||||
|
||||
if (!PlayerReflectionCapture.shouldCapture()) return;
|
||||
if ((Object) this != PlayerReflectionCapture.getTarget()) return;
|
||||
if (entity != PlayerReflectionCapture.getTargetEntity()) return;
|
||||
|
||||
final ModelBiped model = (ModelBiped) (Object) this;
|
||||
final float[] data = PlayerReflectionCapture.vertexScratch();
|
||||
|
||||
// Six 48-vertex blocks = body parts, each base box + overlay box
|
||||
int o = 0;
|
||||
o = angelica$emitBox(model.bipedHead, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedHeadwear, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedRightArm, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedRightArm, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedLeftLeg, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedLeftLeg, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedLeftArm, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedLeftArm, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedRightLeg, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedRightLeg, data, o, scale);
|
||||
o = angelica$emitBox(model.bipedBody, data, o, scale);
|
||||
angelica$emitBox(model.bipedBody, data, o, scale);
|
||||
|
||||
PlayerReflectionCapture.drawPlayerCapture(data);
|
||||
}
|
||||
|
||||
private static int angelica$emitBox(ModelRenderer part, float[] out, int o, float scale) {
|
||||
if (part == null || part.cubeList == null || part.cubeList.isEmpty()) {
|
||||
final int end = o + 24 * PlayerReflectionCapture.FLOATS_PER_VERTEX;
|
||||
Arrays.fill(out, o, end, 0.0f);
|
||||
return end;
|
||||
}
|
||||
|
||||
final Matrix4f m = angelica$mat.identity();
|
||||
m.translate(part.offsetX, part.offsetY, part.offsetZ);
|
||||
m.translate(part.rotationPointX * scale, part.rotationPointY * scale, part.rotationPointZ * scale);
|
||||
if (part.rotateAngleZ != 0.0f) m.rotateZ(part.rotateAngleZ);
|
||||
if (part.rotateAngleY != 0.0f) m.rotateY(part.rotateAngleY);
|
||||
if (part.rotateAngleX != 0.0f) m.rotateX(part.rotateAngleX);
|
||||
|
||||
final ModelBox box = part.cubeList.getFirst();
|
||||
final TexturedQuad[] quads = ((AccessorModelBox) (Object) box).angelica$getQuadList();
|
||||
final Vector3f v = angelica$vec;
|
||||
|
||||
for (int q = 0; q < 6; q++) {
|
||||
final PositionTextureVertex[] corners = quads[q].vertexPositions;
|
||||
for (int c = 0; c < 4; c++) {
|
||||
final PositionTextureVertex corner = corners[c];
|
||||
v.set((float) corner.vector3D.xCoord * scale,
|
||||
(float) corner.vector3D.yCoord * scale,
|
||||
(float) corner.vector3D.zCoord * scale);
|
||||
m.transformPosition(v);
|
||||
out[o++] = v.x;
|
||||
out[o++] = v.y;
|
||||
out[o++] = v.z;
|
||||
out[o++] = corner.texturePositionX;
|
||||
out[o++] = corner.texturePositionY * 0.5f;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.gtnewhorizons.angelica.mixins.late.client.farseek;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Pseudo
|
||||
@Mixin(targets = "farseek.world.package$IBlockAccessValue$", remap = false)
|
||||
public class MixinFarseekIBlockAccessValue {
|
||||
|
||||
@Inject(method = "worldProvider$extension", at = @At("HEAD"), cancellable = true, remap = false)
|
||||
private void angelica$worldSliceProvider(IBlockAccess bac, CallbackInfoReturnable<WorldProvider> cir) {
|
||||
if (bac instanceof WorldSlice slice) {
|
||||
cir.setReturnValue(slice.getWorld().provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,8 @@ dependencies {
|
||||
}
|
||||
shadowImplementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.5-GTNH) { transitive = false }
|
||||
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
runtimeOnlyNonPublishable("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
|
||||
runtimeOnlyNonPublishable("org.embeddedt.celeritas:celeritas-common:2.5.5-GTNH") { transitive = false }
|
||||
runtimeOnlyNonPublishable("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
|
||||
}
|
||||
Reference in new issue
Block a user