portal guns WORK NOW BABYYYYYYY

This commit is contained in:
craisinlord committed 2026-10-07 22:14:16 -05:00
1 parent 39688c70ad
commit dcf5eb3cb1
44 files changed
+1566 -2441

No files matched your search

@@ -1,116 +0,0 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.client.PortalGunIndicatorClientState;
import com.craisinlord.antarchy.content.network.PortalGunIndicatorPayload;
import com.craisinlord.antarchy.config.AntarchySettings;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
public final class PortalGunCrosshairRenderer {
private static final ResourceLocation CROSSHAIR = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/gui/portal_gun_crosshair.png");
private static final int WIDTH = 34;
private static final int HEIGHT = 33;
private static ClientLevel cachedLevel;
private static long cachedGameTime = Long.MIN_VALUE;
private static UUID cachedOwnerId;
private static Set<PortalKey> cachedPortals = Set.of();
private PortalGunCrosshairRenderer() {
}
public static void render(GuiGraphics guiGraphics) {
Minecraft minecraft = Minecraft.getInstance();
if (PortalGunGrabClientState.isActive()) {
return;
}
if (minecraft.player == null || minecraft.screen != null || minecraft.options.hideGui || !minecraft.options.getCameraType().isFirstPerson()) {
return;
}
ItemStack held = minecraft.player.getMainHandItem();
if (!(held.getItem() instanceof PortalGunItem)) {
held = minecraft.player.getOffhandItem();
if (!(held.getItem() instanceof PortalGunItem)) {
return;
}
}
UUID gunId = PortalGunItem.getGunId(held);
UUID ownerId = PortalGunItem.getPortalOwnerId(held, minecraft.player.getUUID());
String channelName = PortalGunItem.getChannelName(held);
PortalGunIndicatorClientState.requestIfNeeded(minecraft, gunId);
Set<PortalKey> portals = portalsFor(minecraft, ownerId, channelName);
PortalGunIndicatorPayload serverState = PortalGunIndicatorClientState.get(gunId);
boolean blueAvailable = serverState == null
? portals.contains(new PortalKey(channelName, PortalGunPortalEntity.PortalSide.BLUE))
: serverState.blueAvailable();
boolean orangeAvailable = serverState == null
? portals.contains(new PortalKey(channelName, PortalGunPortalEntity.PortalSide.ORANGE))
: serverState.orangeAvailable();
int left = (minecraft.getWindow().getGuiScaledWidth() - WIDTH) / 2;
int top = (minecraft.getWindow().getGuiScaledHeight() - HEIGHT) / 2;
guiGraphics.blit(CROSSHAIR, left + 6, top, 0.0F, 0.0F, 17, HEIGHT, WIDTH, HEIGHT);
guiGraphics.blit(CROSSHAIR, left + 11, top, 17.0F, 0.0F, 17, HEIGHT, WIDTH, HEIGHT);
if (!blueAvailable) {
guiGraphics.fill(left + 6, top, left + 23, top + HEIGHT, 0x99000000);
}
if (!orangeAvailable) {
guiGraphics.fill(left + 11, top, left + 28, top + HEIGHT, 0x99000000);
}
int indicatorSize = AntarchySettings.portalGunIndicatorSize();
if (indicatorSize > 0) {
int dot = Math.max(1, Math.round(HEIGHT * indicatorSize / 100.0F));
int gap = Math.max(1, dot / 3);
int indicatorLeft = left + (WIDTH - dot * 2 - gap) / 2;
int indicatorTop = top + HEIGHT + 2;
guiGraphics.fill(indicatorLeft, indicatorTop, indicatorLeft + dot, indicatorTop + dot,
blueAvailable ? 0xFF21A8FF : 0xFF33414A);
int orangeLeft = indicatorLeft + dot + gap;
guiGraphics.fill(orangeLeft, indicatorTop, orangeLeft + dot, indicatorTop + dot,
orangeAvailable ? 0xFFFF8A24 : 0xFF4A4035);
}
}
private static Set<PortalKey> portalsFor(Minecraft minecraft, UUID ownerId, String channelName) {
ClientLevel level = minecraft.level;
long gameTime = level.getGameTime();
if (cachedLevel != level || cachedGameTime != gameTime || !ownerId.equals(cachedOwnerId)) {
Set<PortalKey> portals = new HashSet<>();
if (minecraft.player != null) {
AABB bounds = minecraft.player.getBoundingBox().inflate(128.0D);
for (PortalGunPortalEntity portal : level.getEntitiesOfClass(PortalGunPortalEntity.class, bounds, Entity::isAlive)) {
UUID portalOwnerId = portal.getOwnerId();
String portalChannelName = portal.getChannelName();
if (ownerId.equals(portalOwnerId) && portalChannelName != null && !portalChannelName.isEmpty()) {
portals.add(new PortalKey(portalChannelName, portal.getPortalSide()));
if (portal.getLinkedPortalId() != null) {
portals.add(new PortalKey(portalChannelName, PortalGunPortalEntity.PortalSide.BLUE));
portals.add(new PortalKey(portalChannelName, PortalGunPortalEntity.PortalSide.ORANGE));
}
}
}
}
cachedLevel = level;
cachedGameTime = gameTime;
cachedOwnerId = ownerId;
cachedPortals = Set.copyOf(portals);
}
if (channelName == null || channelName.isEmpty()) {
return Set.of();
}
return cachedPortals;
}
private record PortalKey(String channelName, PortalGunPortalEntity.PortalSide side) {
}
}
@@ -1,183 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.mixins.client.SectionRenderDispatcherPortalUploadsAccessor;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.chunk.RenderRegionCache;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
public final class PortalGunPortalRendererPool {
private static final int MAX_RENDERERS = 8;
private static final long PORTAL_SECTION_BUDGET_NANOS = 4_166_666L;
private static final long PORTAL_UPLOAD_BUDGET_NANOS = 1_000_000L;
private static final int MAX_PORTAL_UPLOADS_PER_FRAME = 16;
private static final RenderBuffers PORTAL_RENDER_BUFFERS = new RenderBuffers(2);
private static final LinkedHashMap<UUID, PortalRenderer> RENDERERS = new LinkedHashMap<>(8, 0.75F, true);
private static final Set<LevelRenderer> PROXY_RENDERERS = java.util.Collections.newSetFromMap(new IdentityHashMap<>());
private static final ThreadLocal<Long> SECTION_BUDGET_DEADLINE = ThreadLocal.withInitial(() -> 0L);
private static final ThreadLocal<Integer> SCHEDULED_SECTION_REBUILDS = ThreadLocal.withInitial(() -> 0);
private static final ThreadLocal<Boolean> SKIP_SET_NOT_DIRTY = ThreadLocal.withInitial(() -> false);
private static final ThreadLocal<Integer> UPLOADED_THIS_FRAME = ThreadLocal.withInitial(() -> 0);
private static ClientLevel ownerLevel;
private PortalGunPortalRendererPool() {
}
public static LevelRenderer acquire(Minecraft minecraft, UUID destinationId, int width, int height) {
if (ownerLevel != minecraft.level) {
clear();
ownerLevel = minecraft.level;
}
PortalRenderer state = RENDERERS.get(destinationId);
if (state == null) {
LevelRenderer renderer = new LevelRenderer(
minecraft,
minecraft.getEntityRenderDispatcher(),
minecraft.getBlockEntityRenderDispatcher(),
PORTAL_RENDER_BUFFERS
);
PROXY_RENDERERS.add(renderer);
renderer.setLevel(minecraft.level);
renderer.resize(width, height);
state = new PortalRenderer(renderer, width, height);
RENDERERS.put(destinationId, state);
trim();
} else if (state.width() != width || state.height() != height) {
state.renderer().resize(width, height);
RENDERERS.put(destinationId, new PortalRenderer(state.renderer(), width, height));
}
return state.renderer();
}
public static boolean isProxy(LevelRenderer renderer) {
return PROXY_RENDERERS.contains(renderer);
}
public static net.minecraft.client.renderer.MultiBufferSource.BufferSource bufferSource() {
return PORTAL_RENDER_BUFFERS.bufferSource();
}
public static void beginFrame() {
SECTION_BUDGET_DEADLINE.set(System.nanoTime() + PORTAL_SECTION_BUDGET_NANOS);
SCHEDULED_SECTION_REBUILDS.set(0);
SKIP_SET_NOT_DIRTY.set(false);
UPLOADED_THIS_FRAME.set(0);
}
public static void uploadPending(LevelRenderer renderer, SectionRenderDispatcher dispatcher) {
if (!isProxy(renderer)) {
dispatcher.uploadAllPendingUploads();
return;
}
java.util.Queue<Runnable> uploads = ((SectionRenderDispatcherPortalUploadsAccessor) dispatcher).antarchy$getToUpload();
long deadline = System.nanoTime() + PORTAL_UPLOAD_BUDGET_NANOS;
int uploaded = UPLOADED_THIS_FRAME.get();
while (uploaded < MAX_PORTAL_UPLOADS_PER_FRAME) {
if (uploaded > 0 && System.nanoTime() >= deadline) {
break;
}
Runnable upload = uploads.poll();
if (upload == null) {
break;
}
upload.run();
uploaded++;
}
UPLOADED_THIS_FRAME.set(uploaded);
}
public static int scheduledSectionRebuilds() {
return SCHEDULED_SECTION_REBUILDS.get();
}
public static int uploadedThisFrame() {
return UPLOADED_THIS_FRAME.get();
}
public static long remainingSectionBudgetNanos() {
return Math.max(0L, SECTION_BUDGET_DEADLINE.get() - System.nanoTime());
}
public static void rebuildSection(LevelRenderer renderer, SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) {
if (!isProxy(renderer)) {
dispatcher.rebuildSectionSync(section, cache);
SKIP_SET_NOT_DIRTY.set(false);
return;
}
if (remainingSectionBudgetNanos() <= 0L) {
SKIP_SET_NOT_DIRTY.set(true);
return;
}
section.rebuildSectionAsync(dispatcher, cache);
SCHEDULED_SECTION_REBUILDS.set(SCHEDULED_SECTION_REBUILDS.get() + 1);
SKIP_SET_NOT_DIRTY.set(false);
}
public static void rebuildSectionAsync(LevelRenderer renderer, SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) {
if (!isProxy(renderer)) {
section.rebuildSectionAsync(dispatcher, cache);
SKIP_SET_NOT_DIRTY.set(false);
return;
}
if (remainingSectionBudgetNanos() <= 0L) {
SKIP_SET_NOT_DIRTY.set(true);
return;
}
section.rebuildSectionAsync(dispatcher, cache);
SCHEDULED_SECTION_REBUILDS.set(SCHEDULED_SECTION_REBUILDS.get() + 1);
SKIP_SET_NOT_DIRTY.set(false);
}
public static void setNotDirty(SectionRenderDispatcher.RenderSection section) {
boolean leaveDirty = SKIP_SET_NOT_DIRTY.get();
SKIP_SET_NOT_DIRTY.set(false);
if (!leaveDirty) {
section.setNotDirty();
}
}
public static void clear() {
for (PortalRenderer state : new ArrayList<>(RENDERERS.values())) {
LevelRenderer renderer = state.renderer();
PortalGunPortalViewAreaManager.releaseRenderer(renderer);
try {
renderer.close();
} finally {
PROXY_RENDERERS.remove(renderer);
}
}
RENDERERS.clear();
ownerLevel = null;
SECTION_BUDGET_DEADLINE.remove();
SCHEDULED_SECTION_REBUILDS.remove();
SKIP_SET_NOT_DIRTY.remove();
UPLOADED_THIS_FRAME.remove();
}
private static void trim() {
Iterator<Map.Entry<UUID, PortalRenderer>> iterator = RENDERERS.entrySet().iterator();
while (RENDERERS.size() > MAX_RENDERERS && iterator.hasNext()) {
PortalRenderer state = iterator.next().getValue();
iterator.remove();
LevelRenderer renderer = state.renderer();
PortalGunPortalViewAreaManager.releaseRenderer(renderer);
try {
renderer.close();
} finally {
PROXY_RENDERERS.remove(renderer);
}
}
}
private record PortalRenderer(LevelRenderer renderer, int width, int height) {
}
}
@@ -0,0 +1,233 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalSceneInvoker;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.shaders.FogShape;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexSorting;
import java.util.function.Consumer;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
public final class PortalGunPortalSceneRenderer {
private static RenderBuffers portalBuffers;
private static boolean renderingScene;
private PortalGunPortalSceneRenderer() {
}
public static MultiBufferSource.BufferSource bufferSource() {
if (portalBuffers == null) {
portalBuffers = new RenderBuffers(1);
}
return portalBuffers.bufferSource();
}
public static boolean isRenderingScene() {
return renderingScene;
}
public static void render(Minecraft minecraft, Camera camera, float partialTick, Matrix4f viewMatrix,
Matrix4f projection, Matrix4f skyProjection, Frustum frustum,
PortalTerrain terrain, Consumer<PoseStack> crossingEntities, String tracePair) {
LevelRenderer renderer = minecraft.levelRenderer;
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) renderer;
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
Vec3 cameraPosition = camera.getPosition();
double cameraX = cameraPosition.x;
double cameraY = cameraPosition.y;
double cameraZ = cameraPosition.z;
int previousRainSoundTime = state.antarchy$getRainSoundTime();
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
float previousFogStart = RenderSystem.getShaderFogStart();
float previousFogEnd = RenderSystem.getShaderFogEnd();
float[] previousFogColor = RenderSystem.getShaderFogColor().clone();
FogShape previousFogShape = RenderSystem.getShaderFogShape();
Camera previousBlockEntityCamera = minecraft.getBlockEntityRenderDispatcher().camera;
modelViewStack.pushMatrix();
modelViewStack.identity();
RenderSystem.applyModelViewMatrix();
renderingScene = true;
terrain.begin(minecraft);
try {
float renderDistance = minecraft.gameRenderer.getRenderDistance();
BlockPos cameraBlockPos = BlockPos.containing(cameraX, cameraY, cameraZ);
boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY())
|| minecraft.gui.getBossOverlay().shouldCreateWorldFog();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "background-and-sky", "sections=" + terrain.sectionCount())) {
RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick);
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
FogRenderer.setupColor(camera, partialTick, minecraft.level, minecraft.options.getEffectiveRenderDistance(),
minecraft.gameRenderer.getDarkenWorldAmount(partialTick));
FogRenderer.levelFogColor();
fillBackground(RenderSystem.getShaderFogColor());
RenderSystem.setProjectionMatrix(skyProjection, VertexSorting.DISTANCE_TO_ORIGIN);
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick);
RenderSystem.setShader(GameRenderer::getPositionShader);
renderer.renderSky(viewMatrix, skyProjection, partialTick, camera, foggy,
() -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick));
}
RenderSystem.setProjectionMatrix(projection, VertexSorting.DISTANCE_TO_ORIGIN);
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick);
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain", "sections=" + terrain.sectionCount())) {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
terrain.drawLayer(minecraft, RenderType.solid(), cameraPosition, viewMatrix, projection);
terrain.drawLayer(minecraft, RenderType.cutoutMipped(), cameraPosition, viewMatrix, projection);
terrain.drawLayer(minecraft, RenderType.cutout(), cameraPosition, viewMatrix, projection);
}
if (minecraft.level.effects().constantAmbientLight()) {
Lighting.setupNetherLevel();
} else {
Lighting.setupLevel();
}
modelViewStack.pushMatrix();
modelViewStack.mul(viewMatrix);
RenderSystem.applyModelViewMatrix();
PoseStack poseStack = new PoseStack();
MultiBufferSource.BufferSource buffers = bufferSource();
try {
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "rendering")) {
for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ)
&& !entity.hasIndirectPassenger(minecraft.player)) {
continue;
}
if (entity instanceof PortalGunPortalEntity portal && !portal.shouldRenderFront(cameraPosition)) {
continue;
}
if (entity == camera.getEntity()
&& entity.getBoundingBox().inflate(0.1D).contains(cameraPosition)
&& (!(entity instanceof LivingEntity living) || !living.isSleeping())) {
continue;
}
scene.antarchy$renderPortalEntity(entity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
}
crossingEntities.accept(poseStack);
}
buffers.endBatch();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "rendering")) {
terrain.forEachBlockEntity(minecraft, frustum, blockEntity ->
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers));
}
buffers.endBatch();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "translucent-and-particles", "rendering")) {
terrain.drawLayer(minecraft, RenderType.translucent(), cameraPosition, viewMatrix, projection);
buffers.endBatch();
terrain.drawLayer(minecraft, RenderType.tripwire(), cameraPosition, viewMatrix, projection);
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick);
}
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "weather-and-border", "rendering")) {
RenderSystem.depthMask(false);
scene.antarchy$renderPortalWeather(minecraft.gameRenderer.lightTexture(), partialTick, cameraX, cameraY, cameraZ);
scene.antarchy$renderPortalWorldBorder(camera);
}
} finally {
try {
buffers.endBatch();
} finally {
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
}
}
} finally {
renderingScene = false;
terrain.end(minecraft);
state.antarchy$setRainSoundTime(previousRainSoundTime);
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
RenderSystem.setShaderFogStart(previousFogStart);
RenderSystem.setShaderFogEnd(previousFogEnd);
RenderSystem.setShaderFogColor(previousFogColor[0], previousFogColor[1], previousFogColor[2], previousFogColor[3]);
RenderSystem.setShaderFogShape(previousFogShape);
if (previousBlockEntityCamera != null) {
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, previousBlockEntityCamera, minecraft.hitResult);
}
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
RenderSystem.enableCull();
RenderSystem.disableBlend();
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
}
}
private static void fillBackground(float[] fogColor) {
int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC);
Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix());
VertexSorting previousSorting = RenderSystem.getVertexSorting();
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION);
buffer.addVertex(-1.0F, -1.0F, 0.99999F);
buffer.addVertex(3.0F, -1.0F, 0.99999F);
buffer.addVertex(-1.0F, 3.0F, 0.99999F);
MeshData mesh = buffer.build();
try {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.disableBlend();
RenderSystem.disableCull();
GL11.glDepthFunc(GL11.GL_ALWAYS);
RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting);
RenderSystem.setShader(GameRenderer::getPositionShader);
RenderSystem.setShaderColor(fogColor[0], fogColor[1], fogColor[2], 1.0F);
if (mesh != null) {
BufferUploader.drawWithShader(mesh);
}
} finally {
GL11.glDepthFunc(previousDepthFunction);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.setProjectionMatrix(previousProjection, previousSorting);
RenderSystem.enableCull();
}
}
private static void renderBlockEntity(Minecraft minecraft, BlockEntity blockEntity, double cameraX, double cameraY, double cameraZ,
float partialTick, PoseStack poseStack, MultiBufferSource.BufferSource buffers) {
if (!blockEntity.hasLevel() || !blockEntity.getType().isValid(blockEntity.getBlockState())) {
return;
}
BlockPos pos = blockEntity.getBlockPos();
poseStack.pushPose();
try {
poseStack.translate(pos.getX() - cameraX, pos.getY() - cameraY, pos.getZ() - cameraZ);
minecraft.getBlockEntityRenderDispatcher().render(blockEntity, partialTick, poseStack, buffers);
} finally {
poseStack.popPose();
}
}
}
@@ -0,0 +1,165 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.SectionOcclusionGraph;
import net.minecraft.client.renderer.ViewArea;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public final class PortalGunPortalSectionViews {
private static final int MAX_VIEWS = 8;
private static final double SEED_OFFSET = 0.5D;
private static final LinkedHashMap<UUID, View> VIEWS = new LinkedHashMap<>(8, 0.75F, true);
private static volatile SectionOcclusionGraph[] graphs = new SectionOcclusionGraph[0];
private static ViewArea ownerArea;
private static int mainSectionX = Integer.MIN_VALUE;
private static int mainSectionY = Integer.MIN_VALUE;
private static int mainSectionZ = Integer.MIN_VALUE;
private PortalGunPortalSectionViews() {
}
public static boolean canView(Minecraft minecraft, Vec3 exitCenter) {
ViewArea area = ((LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer).antarchy$getViewArea();
if (area == null) {
return false;
}
LevelRendererPortalViewAreaAccessor accessor = (LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer;
int limit = area.getViewDistance() - 1;
return Math.abs(SectionPos.posToSectionCoord(exitCenter.x) - accessor.antarchy$getLastCameraSectionX()) <= limit
&& Math.abs(SectionPos.posToSectionCoord(exitCenter.z) - accessor.antarchy$getLastCameraSectionZ()) <= limit;
}
public static View prepare(Minecraft minecraft, UUID exitId, Vec3 exitCenter, Vec3 exitNormal, Frustum frustum) {
LevelRendererPortalViewAreaAccessor accessor = (LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer;
ViewArea area = accessor.antarchy$getViewArea();
if (area != ownerArea) {
clear();
ownerArea = area;
}
int sectionX = accessor.antarchy$getLastCameraSectionX();
int sectionY = accessor.antarchy$getLastCameraSectionY();
int sectionZ = accessor.antarchy$getLastCameraSectionZ();
if (sectionX != mainSectionX || sectionY != mainSectionY || sectionZ != mainSectionZ) {
mainSectionX = sectionX;
mainSectionY = sectionY;
mainSectionZ = sectionZ;
for (View view : VIEWS.values()) {
view.graph.invalidate();
}
}
View view = VIEWS.get(exitId);
if (view == null) {
view = new View(area);
VIEWS.put(exitId, view);
trim();
publishGraphs();
}
Vec3 seed = exitCenter.add(exitNormal.normalize().scale(SEED_OFFSET));
if (!seed.equals(view.seed)) {
view.seed = seed;
view.seedCamera = new SeedCamera(seed);
view.updateFrustum = centredFrustum(seed);
view.graph.invalidate();
}
view.partialUpdates.clear();
view.graph.update(minecraft.smartCull, view.seedCamera, view.updateFrustum(), view.partialUpdates);
view.visibleSections.clear();
view.graph.addSectionsInFrustum(frustum, view.visibleSections);
return view;
}
public static void onSectionCompiled(SectionRenderDispatcher.RenderSection section) {
for (SectionOcclusionGraph graph : graphs) {
graph.onSectionCompiled(section);
}
}
public static void onChunkLoaded(ChunkPos pos) {
for (SectionOcclusionGraph graph : graphs) {
graph.onChunkLoaded(pos);
}
}
public static void onChunkDropped(ChunkPos pos) {
for (SectionOcclusionGraph graph : graphs) {
graph.invalidate();
}
}
public static void clear() {
for (View view : VIEWS.values()) {
view.release();
}
VIEWS.clear();
publishGraphs();
ownerArea = null;
mainSectionX = Integer.MIN_VALUE;
mainSectionY = Integer.MIN_VALUE;
mainSectionZ = Integer.MIN_VALUE;
}
private static void publishGraphs() {
graphs = VIEWS.values().stream().map(view -> view.graph).toArray(SectionOcclusionGraph[]::new);
}
private static void trim() {
Iterator<Map.Entry<UUID, View>> iterator = VIEWS.entrySet().iterator();
while (VIEWS.size() > MAX_VIEWS && iterator.hasNext()) {
View view = iterator.next().getValue();
iterator.remove();
view.release();
}
}
public static final class View {
private final SectionOcclusionGraph graph = new SectionOcclusionGraph();
private final List<SectionRenderDispatcher.RenderSection> partialUpdates = new ArrayList<>();
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections = new ObjectArrayList<>();
private Vec3 seed;
private Camera seedCamera;
private Frustum updateFrustum;
private View(ViewArea area) {
((PortalSectionGraphMarker) this.graph).antarchy$markPortalGraph();
this.graph.waitAndReset(area);
}
public ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections() {
return this.visibleSections;
}
private Frustum updateFrustum() {
return this.updateFrustum;
}
private void release() {
this.graph.waitAndReset(null);
}
}
private static Frustum centredFrustum(Vec3 position) {
Frustum frustum = new Frustum(new Matrix4f(), new Matrix4f().setPerspective((float) Math.toRadians(90.0D), 1.0F, 0.05F, 1024.0F));
frustum.prepare(position.x, position.y, position.z);
return frustum;
}
private static final class SeedCamera extends Camera {
private SeedCamera(Vec3 position) {
this.setPosition(position);
}
}
}
@@ -1,386 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import com.mojang.blaze3d.vertex.VertexBuffer;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import net.minecraft.client.CloudStatus;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.SectionOcclusionGraph;
import net.minecraft.client.renderer.ViewArea;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public final class PortalGunPortalViewAreaManager {
private static final int MAX_VIEW_AREAS = 8;
private static final LinkedHashMap<ViewKey, ViewState> VIEW_AREAS = new LinkedHashMap<>(8, 0.75F, true);
private static final Map<SectionRenderDispatcher.RenderSection, ViewState> SECTION_OWNERS = Collections.synchronizedMap(new IdentityHashMap<>());
private static ClientLevel ownerLevel;
private static int ownerViewDistance = -1;
private PortalGunPortalViewAreaManager() {
}
public static Scope enter(Minecraft minecraft, LevelRenderer renderer, UUID destinationId, Vec3 cameraPos, net.minecraft.client.Camera portalCamera) {
if (SodiumCompat.isLoaded()) {
return new SodiumScope(SodiumCompat.beginPortalView());
}
LevelRendererPortalViewAreaAccessor accessor = (LevelRendererPortalViewAreaAccessor) renderer;
int viewDistance = minecraft.options.getEffectiveRenderDistance();
if (ownerLevel != minecraft.level || ownerViewDistance != viewDistance) {
clear();
ownerLevel = minecraft.level;
ownerViewDistance = viewDistance;
}
ViewKey viewKey = new ViewKey(renderer, destinationId);
ViewState state = VIEW_AREAS.get(viewKey);
if (state == null) {
ViewArea area = accessor.antarchy$getViewArea();
SectionOcclusionGraph graph = accessor.antarchy$getSectionOcclusionGraph();
ObjectArrayList<SectionRenderDispatcher.RenderSection> sections = accessor.antarchy$getVisibleSections();
state = new ViewState(area, graph, sections, accessor.antarchy$getGlobalBlockEntities());
state.cloudBuffer = accessor.antarchy$getCloudBuffer();
state.generateClouds = accessor.antarchy$getGenerateClouds();
state.prevCloudX = accessor.antarchy$getPrevCloudX();
state.prevCloudY = accessor.antarchy$getPrevCloudY();
state.prevCloudZ = accessor.antarchy$getPrevCloudZ();
state.prevCloudColor = accessor.antarchy$getPrevCloudColor();
state.prevCloudsType = accessor.antarchy$getPrevCloudsType();
for (SectionRenderDispatcher.RenderSection section : area.sections) {
((PortalViewSectionMarker) section).antarchy$markPortalViewSection();
SECTION_OWNERS.put(section, state);
}
VIEW_AREAS.put(viewKey, state);
trimOldViews();
}
state.area.repositionCamera(cameraPos.x, cameraPos.z);
int sectionX = SectionPos.posToSectionCoord(cameraPos.x);
int sectionY = SectionPos.posToSectionCoord(cameraPos.y);
int sectionZ = SectionPos.posToSectionCoord(cameraPos.z);
state.cameraPos = cameraPos;
state.lastSectionX = sectionX;
state.lastSectionY = sectionY;
state.lastSectionZ = sectionZ;
Scope scope = new VanillaScope(renderer, accessor, state, minecraft.getBlockEntityRenderDispatcher());
accessor.antarchy$setViewArea(state.area);
accessor.antarchy$setSectionOcclusionGraph(state.graph);
accessor.antarchy$setVisibleSections(state.visibleSections);
accessor.antarchy$setGlobalBlockEntities(state.globalBlockEntities);
accessor.antarchy$setLastCameraSectionX(sectionX);
accessor.antarchy$setLastCameraSectionY(sectionY);
accessor.antarchy$setLastCameraSectionZ(sectionZ);
return scope;
}
public static boolean routeSectionCompiled(SectionRenderDispatcher.RenderSection section) {
ViewState state = SECTION_OWNERS.get(section);
if (state == null) {
return ((PortalViewSectionMarker) section).antarchy$isPortalViewSection();
}
state.graph.onSectionCompiled(section);
return true;
}
public static Vec3 cameraForSection(SectionRenderDispatcher.RenderSection section, Vec3 fallback) {
ViewState state = SECTION_OWNERS.get(section);
return state == null || state.cameraPos == null ? fallback : state.cameraPos;
}
public static boolean updateGlobalBlockEntities(SectionRenderDispatcher.RenderSection section, Iterable<BlockEntity> removed, Iterable<BlockEntity> added) {
ViewState state = SECTION_OWNERS.get(section);
if (state == null) {
return ((PortalViewSectionMarker) section).antarchy$isPortalViewSection();
}
synchronized (state.globalBlockEntities) {
for (BlockEntity blockEntity : removed) {
state.globalBlockEntities.remove(blockEntity);
}
for (BlockEntity blockEntity : added) {
state.globalBlockEntities.add(blockEntity);
}
}
return true;
}
public static void onSectionDirty(int sectionX, int sectionY, int sectionZ, boolean playerChanged) {
for (ViewState state : VIEW_AREAS.values()) {
int sectionRange = state.area.getViewDistance() + 1;
if (Math.abs((long) sectionX - state.lastSectionX) > sectionRange
|| Math.abs((long) sectionZ - state.lastSectionZ) > sectionRange
|| sectionY < ownerLevel.getMinSection()
|| sectionY > ownerLevel.getMaxSection()) {
continue;
}
state.area.setDirty(sectionX, sectionY, sectionZ, playerChanged);
}
}
public static void onChunkLoaded(net.minecraft.world.level.ChunkPos pos) {
for (ViewState state : VIEW_AREAS.values()) {
state.graph.onChunkLoaded(pos);
markChunkDirty(state, pos);
}
}
public static void onChunkDropped(net.minecraft.world.level.ChunkPos pos) {
for (ViewState state : VIEW_AREAS.values()) {
markChunkDirty(state, pos);
state.graph.invalidate();
}
}
private static void markChunkDirty(ViewState state, net.minecraft.world.level.ChunkPos pos) {
int sectionRange = state.area.getViewDistance() + 1;
if (Math.abs((long) pos.x - state.lastSectionX) <= sectionRange
&& Math.abs((long) pos.z - state.lastSectionZ) <= sectionRange) {
for (int sectionY = ownerLevel.getMinSection(); sectionY <= ownerLevel.getMaxSection(); sectionY++) {
state.area.setDirty(pos.x, sectionY, pos.z, true);
}
}
}
public static void clear() {
SodiumCompat.clear();
for (ViewState state : VIEW_AREAS.values()) {
state.graph.waitAndReset(state.area);
}
VIEW_AREAS.clear();
SECTION_OWNERS.clear();
ownerLevel = null;
ownerViewDistance = -1;
}
public static void releaseRenderer(LevelRenderer renderer) {
Iterator<Map.Entry<ViewKey, ViewState>> iterator = VIEW_AREAS.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<ViewKey, ViewState> entry = iterator.next();
if (entry.getKey().renderer() == renderer) {
ViewState state = entry.getValue();
state.graph.waitAndReset(state.area);
removeOwners(state);
iterator.remove();
}
}
}
private static void trimOldViews() {
Iterator<Map.Entry<ViewKey, ViewState>> iterator = VIEW_AREAS.entrySet().iterator();
while (VIEW_AREAS.size() > MAX_VIEW_AREAS && iterator.hasNext()) {
ViewState state = iterator.next().getValue();
iterator.remove();
state.graph.waitAndReset(state.area);
removeOwners(state);
}
}
private static void removeOwners(ViewState state) {
for (SectionRenderDispatcher.RenderSection section : state.area.sections) {
SECTION_OWNERS.remove(section);
}
}
private static final class ViewState {
private final ViewArea area;
private final SectionOcclusionGraph graph;
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections;
private final Set<BlockEntity> globalBlockEntities;
private VertexBuffer cloudBuffer;
private boolean generateClouds = true;
private int prevCloudX = Integer.MIN_VALUE;
private int prevCloudY = Integer.MIN_VALUE;
private int prevCloudZ = Integer.MIN_VALUE;
private Vec3 prevCloudColor = Vec3.ZERO;
private CloudStatus prevCloudsType;
private int lastSectionX;
private int lastSectionY;
private int lastSectionZ;
private volatile Vec3 cameraPos;
private double transparentSortX = Double.NaN;
private double transparentSortY = Double.NaN;
private double transparentSortZ = Double.NaN;
private double prevCamX = Double.NaN;
private double prevCamY = Double.NaN;
private double prevCamZ = Double.NaN;
private double prevCamRotX = Double.NaN;
private double prevCamRotY = Double.NaN;
private ViewState(ViewArea area, SectionOcclusionGraph graph, ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections, Set<BlockEntity> globalBlockEntities) {
this.area = area;
this.graph = graph;
this.visibleSections = visibleSections;
this.globalBlockEntities = globalBlockEntities;
}
}
private record ViewKey(LevelRenderer renderer, UUID destinationId) {
}
public interface Scope extends AutoCloseable {
@Override
void close();
}
private static final class SodiumScope implements Scope {
private final SodiumCompat.TerrainScope terrainScope;
private SodiumScope(SodiumCompat.TerrainScope terrainScope) {
this.terrainScope = terrainScope;
}
@Override
public void close() {
terrainScope.close();
}
}
private static final class VanillaScope implements Scope {
private final LevelRendererPortalViewAreaAccessor accessor;
private final ViewArea previousArea;
private final SectionOcclusionGraph previousGraph;
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> previousVisibleSections;
private final Set<BlockEntity> previousGlobalBlockEntities;
private final VertexBuffer previousCloudBuffer;
private final boolean previousGenerateClouds;
private final int previousCloudX;
private final int previousCloudY;
private final int previousCloudZ;
private final Vec3 previousCloudColor;
private final CloudStatus previousCloudsType;
private final int previousRainSoundTime;
private final int previousSectionX;
private final int previousSectionY;
private final int previousSectionZ;
private final double previousPrevCamX;
private final double previousPrevCamY;
private final double previousPrevCamZ;
private final double previousPrevCamRotX;
private final double previousPrevCamRotY;
private final double previousTransparentSortX;
private final double previousTransparentSortY;
private final double previousTransparentSortZ;
private final Vec3 previousDispatcherCamera;
private final SectionRenderDispatcher dispatcher;
private final BlockEntityRenderDispatcher blockEntityDispatcher;
private final Level previousBlockEntityLevel;
private final net.minecraft.client.Camera previousBlockEntityCamera;
private final HitResult previousBlockEntityHitResult;
private final ViewState state;
private boolean closed;
private VanillaScope(LevelRenderer renderer, LevelRendererPortalViewAreaAccessor accessor, ViewState state, BlockEntityRenderDispatcher blockEntityDispatcher) {
this.accessor = accessor;
this.state = state;
this.blockEntityDispatcher = blockEntityDispatcher;
this.previousBlockEntityLevel = blockEntityDispatcher.level;
this.previousBlockEntityCamera = blockEntityDispatcher.camera;
this.previousBlockEntityHitResult = blockEntityDispatcher.cameraHitResult;
this.previousArea = accessor.antarchy$getViewArea();
this.previousGraph = accessor.antarchy$getSectionOcclusionGraph();
this.previousVisibleSections = accessor.antarchy$getVisibleSections();
this.previousGlobalBlockEntities = accessor.antarchy$getGlobalBlockEntities();
this.previousCloudBuffer = accessor.antarchy$getCloudBuffer();
this.previousGenerateClouds = accessor.antarchy$getGenerateClouds();
this.previousCloudX = accessor.antarchy$getPrevCloudX();
this.previousCloudY = accessor.antarchy$getPrevCloudY();
this.previousCloudZ = accessor.antarchy$getPrevCloudZ();
this.previousCloudColor = accessor.antarchy$getPrevCloudColor();
this.previousCloudsType = accessor.antarchy$getPrevCloudsType();
this.previousRainSoundTime = accessor.antarchy$getRainSoundTime();
this.previousSectionX = accessor.antarchy$getLastCameraSectionX();
this.previousSectionY = accessor.antarchy$getLastCameraSectionY();
this.previousSectionZ = accessor.antarchy$getLastCameraSectionZ();
this.previousPrevCamX = accessor.antarchy$getPrevCamX();
this.previousPrevCamY = accessor.antarchy$getPrevCamY();
this.previousPrevCamZ = accessor.antarchy$getPrevCamZ();
this.previousPrevCamRotX = accessor.antarchy$getPrevCamRotX();
this.previousPrevCamRotY = accessor.antarchy$getPrevCamRotY();
this.previousTransparentSortX = accessor.antarchy$getTransparentSortX();
this.previousTransparentSortY = accessor.antarchy$getTransparentSortY();
this.previousTransparentSortZ = accessor.antarchy$getTransparentSortZ();
this.dispatcher = renderer.getSectionRenderDispatcher();
this.previousDispatcherCamera = dispatcher.getCameraPosition();
accessor.antarchy$setLastCameraSectionX(state.lastSectionX);
accessor.antarchy$setLastCameraSectionY(state.lastSectionY);
accessor.antarchy$setLastCameraSectionZ(state.lastSectionZ);
accessor.antarchy$setPrevCamX(state.prevCamX);
accessor.antarchy$setPrevCamY(state.prevCamY);
accessor.antarchy$setPrevCamZ(state.prevCamZ);
accessor.antarchy$setPrevCamRotX(state.prevCamRotX);
accessor.antarchy$setPrevCamRotY(state.prevCamRotY);
accessor.antarchy$setTransparentSortX(state.transparentSortX);
accessor.antarchy$setTransparentSortY(state.transparentSortY);
accessor.antarchy$setTransparentSortZ(state.transparentSortZ);
accessor.antarchy$setCloudBuffer(state.cloudBuffer);
accessor.antarchy$setGenerateClouds(state.generateClouds);
accessor.antarchy$setPrevCloudX(state.prevCloudX);
accessor.antarchy$setPrevCloudY(state.prevCloudY);
accessor.antarchy$setPrevCloudZ(state.prevCloudZ);
accessor.antarchy$setPrevCloudColor(state.prevCloudColor);
accessor.antarchy$setPrevCloudsType(state.prevCloudsType);
}
@Override
public void close() {
if (closed) {
return;
}
closed = true;
state.lastSectionX = accessor.antarchy$getLastCameraSectionX();
state.lastSectionY = accessor.antarchy$getLastCameraSectionY();
state.lastSectionZ = accessor.antarchy$getLastCameraSectionZ();
state.prevCamX = accessor.antarchy$getPrevCamX();
state.prevCamY = accessor.antarchy$getPrevCamY();
state.prevCamZ = accessor.antarchy$getPrevCamZ();
state.prevCamRotX = accessor.antarchy$getPrevCamRotX();
state.prevCamRotY = accessor.antarchy$getPrevCamRotY();
state.transparentSortX = accessor.antarchy$getTransparentSortX();
state.transparentSortY = accessor.antarchy$getTransparentSortY();
state.transparentSortZ = accessor.antarchy$getTransparentSortZ();
state.cloudBuffer = accessor.antarchy$getCloudBuffer();
state.generateClouds = accessor.antarchy$getGenerateClouds();
state.prevCloudX = accessor.antarchy$getPrevCloudX();
state.prevCloudY = accessor.antarchy$getPrevCloudY();
state.prevCloudZ = accessor.antarchy$getPrevCloudZ();
state.prevCloudColor = accessor.antarchy$getPrevCloudColor();
state.prevCloudsType = accessor.antarchy$getPrevCloudsType();
accessor.antarchy$setViewArea(previousArea);
accessor.antarchy$setSectionOcclusionGraph(previousGraph);
accessor.antarchy$setVisibleSections(previousVisibleSections);
accessor.antarchy$setGlobalBlockEntities(previousGlobalBlockEntities);
accessor.antarchy$setCloudBuffer(previousCloudBuffer);
accessor.antarchy$setGenerateClouds(previousGenerateClouds);
accessor.antarchy$setPrevCloudX(previousCloudX);
accessor.antarchy$setPrevCloudY(previousCloudY);
accessor.antarchy$setPrevCloudZ(previousCloudZ);
accessor.antarchy$setPrevCloudColor(previousCloudColor);
accessor.antarchy$setPrevCloudsType(previousCloudsType);
accessor.antarchy$setRainSoundTime(previousRainSoundTime);
accessor.antarchy$setLastCameraSectionX(previousSectionX);
accessor.antarchy$setLastCameraSectionY(previousSectionY);
accessor.antarchy$setLastCameraSectionZ(previousSectionZ);
accessor.antarchy$setPrevCamX(previousPrevCamX);
accessor.antarchy$setPrevCamY(previousPrevCamY);
accessor.antarchy$setPrevCamZ(previousPrevCamZ);
accessor.antarchy$setPrevCamRotX(previousPrevCamRotX);
accessor.antarchy$setPrevCamRotY(previousPrevCamRotY);
accessor.antarchy$setTransparentSortX(previousTransparentSortX);
accessor.antarchy$setTransparentSortY(previousTransparentSortY);
accessor.antarchy$setTransparentSortZ(previousTransparentSortZ);
dispatcher.setCamera(previousDispatcherCamera);
blockEntityDispatcher.prepare(previousBlockEntityLevel, previousBlockEntityCamera, previousBlockEntityHitResult);
}
}
}
@@ -1,246 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalSceneInvoker;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.shaders.FogShape;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Camera;
import net.minecraft.client.CloudStatus;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.AABB;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
public final class PortalGunProxySceneRenderer {
private PortalGunProxySceneRenderer() {
}
public static void render(Minecraft minecraft, LevelRenderer renderer, Camera camera, float partialTick, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, Matrix4f cullProjectionMatrix, String tracePair) {
Matrix4fStack outerModelViewStack = RenderSystem.getModelViewStack();
float previousFogStart = RenderSystem.getShaderFogStart();
float previousFogEnd = RenderSystem.getShaderFogEnd();
float[] previousFogColor = RenderSystem.getShaderFogColor().clone();
FogShape previousFogShape = RenderSystem.getShaderFogShape();
outerModelViewStack.pushMatrix();
outerModelViewStack.identity();
RenderSystem.applyModelViewMatrix();
ObjectArrayList<SectionRenderDispatcher.RenderSection> originalVisibleSections = null;
try {
Vec3 cameraPosition = camera.getPosition();
double cameraX = cameraPosition.x;
double cameraY = cameraPosition.y;
double cameraZ = cameraPosition.z;
Frustum frustum = new Frustum(modelViewMatrix, cullProjectionMatrix);
frustum.prepare(cameraX, cameraY, cameraZ);
LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer;
float renderDistance = minecraft.gameRenderer.getRenderDistance();
BlockPos cameraBlockPos = BlockPos.containing(cameraX, cameraY, cameraZ);
boolean foggy = minecraft.level.effects().isFoggyAt(cameraBlockPos.getX(), cameraBlockPos.getY())
|| minecraft.gui.getBossOverlay().shouldCreateWorldFog();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "sky", "camera=" + cameraPosition)) {
RenderSystem.setShaderGameTime(minecraft.level.getGameTime(), partialTick);
minecraft.getBlockEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.hitResult);
minecraft.getEntityRenderDispatcher().prepare(minecraft.level, camera, minecraft.crosshairPickEntity);
FogRenderer.setupColor(camera, partialTick, minecraft.level, minecraft.options.getEffectiveRenderDistance(), minecraft.gameRenderer.getDarkenWorldAmount(partialTick));
FogRenderer.levelFogColor();
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick);
RenderSystem.setShader(net.minecraft.client.renderer.GameRenderer::getPositionShader);
renderer.renderSky(modelViewMatrix, projectionMatrix, partialTick, camera, foggy,
() -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick));
}
boolean sodium = SodiumCompat.isLoaded();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "frustum-and-visibility", "renderDistance=" + (sodium ? minecraft.options.getEffectiveRenderDistance() : rendererAccessDistance(renderer)))) {
FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_TERRAIN, Math.max(renderDistance, 32.0F), foggy, partialTick);
scene.antarchy$setupPortalScene(camera, frustum, false, minecraft.player != null && minecraft.player.isSpectator());
}
LevelRendererPortalViewAreaAccessor viewAreaAccessor = sodium ? null : (LevelRendererPortalViewAreaAccessor) renderer;
int visibleSectionCount = 0;
int candidateSectionCount = 0;
if (viewAreaAccessor != null) {
originalVisibleSections = viewAreaAccessor.antarchy$getVisibleSections();
candidateSectionCount = originalVisibleSections.size();
ObjectArrayList<SectionRenderDispatcher.RenderSection> apertureSections = new ObjectArrayList<>();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "aperture-section-cull", "candidateSections=" + candidateSectionCount)) {
for (SectionRenderDispatcher.RenderSection section : originalVisibleSections) {
if (PortalGunPortalRenderState.shouldRenderBounds(section.getBoundingBox())) {
apertureSections.add(section);
}
}
}
viewAreaAccessor.antarchy$setVisibleSections(apertureSections);
ObjectArrayList<SectionRenderDispatcher.RenderSection> fullVisibleSections = apertureSections;
ObjectArrayList<SectionRenderDispatcher.RenderSection> budgetedVisibleSections = new ObjectArrayList<>();
int scannedVisibleSections = 0;
while (scannedVisibleSections < fullVisibleSections.size() && PortalGunPortalRendererPool.remainingSectionBudgetNanos() > 0L) {
budgetedVisibleSections.add(fullVisibleSections.get(scannedVisibleSections++));
}
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "compile-visible-sections", "visibleTotal=" + fullVisibleSections.size() + " visibleScanned=" + scannedVisibleSections + " rebuildBudgetNanos=" + PortalGunPortalRendererPool.remainingSectionBudgetNanos())) {
viewAreaAccessor.antarchy$setVisibleSections(budgetedVisibleSections);
try {
scene.antarchy$compilePortalSections(camera);
} finally {
viewAreaAccessor.antarchy$setVisibleSections(fullVisibleSections);
}
}
visibleSectionCount = fullVisibleSections.size();
}
PortalSceneRenderTrace.event(tracePair, "visible-sections-ready", "renderer=" + (sodium ? "sodium" : "vanilla")
+ " count=" + visibleSectionCount + " candidates=" + candidateSectionCount
+ " scheduledRebuilds=" + PortalGunPortalRendererPool.scheduledSectionRebuilds()
+ " uploaded=" + PortalGunPortalRendererPool.uploadedThisFrame()
+ " pendingUploads=" + (sodium ? "sodium-managed" : viewAreaAccessor.antarchy$getSectionRenderDispatcher().getToUpload()));
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain-layers", "sections=" + visibleSectionCount)) {
RenderSystem.enableDepthTest();
RenderSystem.depthMask(true);
scene.antarchy$renderPortalSectionLayer(RenderType.solid(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
scene.antarchy$renderPortalSectionLayer(RenderType.cutoutMipped(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
scene.antarchy$renderPortalSectionLayer(RenderType.cutout(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
}
if (minecraft.level.effects().constantAmbientLight()) {
Lighting.setupNetherLevel();
} else {
Lighting.setupLevel();
}
Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entity-scene-state", "enter-model-view")) {
modelViewStack.pushMatrix();
modelViewStack.mul(modelViewMatrix);
RenderSystem.applyModelViewMatrix();
}
PoseStack poseStack = new PoseStack();
MultiBufferSource.BufferSource buffers = PortalGunPortalRendererPool.bufferSource();
try {
int entityCount = 0;
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "dispatch-start")) {
for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!minecraft.getEntityRenderDispatcher().shouldRender(entity, frustum, cameraX, cameraY, cameraZ)
&& !entity.hasIndirectPassenger(minecraft.player)) {
continue;
}
if (entity == camera.getEntity() && !camera.isDetached()
&& (!(entity instanceof LivingEntity living) || !living.isSleeping())) {
continue;
}
if (entity instanceof Player player && player == minecraft.player && !player.isSpectator() && entity != camera.getEntity()) {
continue;
}
scene.antarchy$renderPortalEntity(entity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
entityCount++;
}
}
buffers.endBatch();
PortalSceneRenderTrace.event(tracePair, "entities-dispatched", "count=" + entityCount);
int blockEntityCount = 0;
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "visibleSections=" + visibleSectionCount)) {
if (sodium) {
int[] count = {0};
SodiumCompat.forEachVisibleBlockEntity(blockEntity -> {
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
count[0]++;
});
blockEntityCount = count[0];
} else {
for (var section : viewAreaAccessor.antarchy$getVisibleSections()) {
for (BlockEntity blockEntity : section.getCompiled().getRenderableBlockEntities()) {
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
blockEntityCount++;
}
}
synchronized (viewAreaAccessor.antarchy$getGlobalBlockEntities()) {
for (BlockEntity blockEntity : viewAreaAccessor.antarchy$getGlobalBlockEntities()) {
if (frustum.isVisible(new AABB(blockEntity.getBlockPos()).inflate(1.0D))) {
renderBlockEntity(minecraft, blockEntity, cameraX, cameraY, cameraZ, partialTick, poseStack, buffers);
blockEntityCount++;
}
}
}
}
}
buffers.endBatch();
PortalSceneRenderTrace.event(tracePair, "block-entities-dispatched", "count=" + blockEntityCount);
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "translucent-and-particles", "rendering")) {
scene.antarchy$renderPortalSectionLayer(RenderType.translucent(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
buffers.endBatch(RenderType.lines());
buffers.endBatch();
scene.antarchy$renderPortalSectionLayer(RenderType.tripwire(), cameraX, cameraY, cameraZ, modelViewMatrix, projectionMatrix);
minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick);
}
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "clouds", minecraft.options.getCloudsType().toString())) {
if (minecraft.options.getCloudsType() != CloudStatus.OFF) {
renderer.renderClouds(poseStack, modelViewMatrix, projectionMatrix, partialTick, cameraX, cameraY, cameraZ);
}
}
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "weather-and-border", "rendering")) {
RenderSystem.depthMask(false);
scene.antarchy$renderPortalWeather(minecraft.gameRenderer.lightTexture(), partialTick, cameraX, cameraY, cameraZ);
scene.antarchy$renderPortalWorldBorder(camera);
}
} finally {
try {
buffers.endBatch();
} finally {
modelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
RenderSystem.enableCull();
RenderSystem.disableBlend();
}
}
} finally {
if (originalVisibleSections != null) {
((LevelRendererPortalViewAreaAccessor) renderer).antarchy$setVisibleSections(originalVisibleSections);
}
outerModelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix();
RenderSystem.setShaderFogStart(previousFogStart);
RenderSystem.setShaderFogEnd(previousFogEnd);
RenderSystem.setShaderFogColor(previousFogColor[0], previousFogColor[1], previousFogColor[2], previousFogColor[3]);
RenderSystem.setShaderFogShape(previousFogShape);
}
}
private static int rendererAccessDistance(LevelRenderer renderer) {
return ((LevelRendererPortalViewAreaAccessor) renderer).antarchy$getViewArea().getViewDistance();
}
private static void renderBlockEntity(Minecraft minecraft, BlockEntity blockEntity, double cameraX, double cameraY, double cameraZ,
float partialTick, PoseStack poseStack, MultiBufferSource.BufferSource buffers) {
if (!blockEntity.hasLevel() || !blockEntity.getType().isValid(blockEntity.getBlockState())) {
return;
}
BlockPos pos = blockEntity.getBlockPos();
poseStack.pushPose();
try {
poseStack.translate(pos.getX() - cameraX, pos.getY() - cameraY, pos.getZ() - cameraZ);
minecraft.getBlockEntityRenderDispatcher().render(blockEntity, partialTick, poseStack, buffers);
} finally {
poseStack.popPose();
}
}
}
@@ -0,0 +1,56 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
final class PortalRenderWatchdog {
private static final long STALL_NANOS = 5_000_000_000L;
private static final long RECENT_PORTAL_NANOS = 10_000_000_000L;
private static volatile Thread renderThread;
private static volatile long lastFrameNanos;
private static volatile long lastPortalViewNanos = Long.MIN_VALUE;
private static volatile boolean reportedStall;
private static Thread watcher;
private PortalRenderWatchdog() {
}
static void frame() {
lastFrameNanos = System.nanoTime();
reportedStall = false;
if (watcher == null) {
renderThread = Thread.currentThread();
watcher = new Thread(PortalRenderWatchdog::watch, "Antarchy portal render watchdog");
watcher.setDaemon(true);
watcher.start();
}
}
static void portalViewDrawn() {
lastPortalViewNanos = System.nanoTime();
}
private static void watch() {
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException exception) {
return;
}
long now = System.nanoTime();
long lastFrame = lastFrameNanos;
long lastPortal = lastPortalViewNanos;
Thread thread = renderThread;
if (reportedStall || thread == null || lastPortal == Long.MIN_VALUE
|| now - lastFrame < STALL_NANOS || lastFrame - lastPortal > RECENT_PORTAL_NANOS) {
continue;
}
reportedStall = true;
StringBuilder stack = new StringBuilder();
for (StackTraceElement element : thread.getStackTrace()) {
stack.append("\n\tat ").append(element);
}
Antarchy.LOGGER.warn("Render thread has not finished a frame for {} ms after portal views were drawn (state {}). Stack:{}",
(now - lastFrame) / 1_000_000L, thread.getState(), stack);
}
}
}
@@ -0,0 +1,5 @@
package com.craisinlord.antarchy.content.client.renderer;
public interface PortalSectionGraphMarker {
void antarchy$markPortalGraph();
}
@@ -7,15 +7,14 @@ import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
public final class PortalStencilTarget {
private static final Map<RenderTarget, Attachment> ATTACHMENTS = Collections.synchronizedMap(new IdentityHashMap<>());
private static final int GL_INCR = 7682;
private static final int GL_DECR = 7683;
private static final Map<RenderTarget, Attachment> MAIN_STENCIL_ATTACHMENTS = Collections.synchronizedMap(new IdentityHashMap<>());
private static final Map<RenderTarget, Integer> UNSUPPORTED_MAIN_TARGETS = Collections.synchronizedMap(new IdentityHashMap<>());
private static final Set<RenderTarget> DIRECT_TARGETS = Collections.newSetFromMap(new IdentityHashMap<>());
private PortalStencilTarget() {
}
@@ -52,105 +51,67 @@ public final class PortalStencilTarget {
return stencilBits() > 0;
}
public static Scope beginDirect(RenderTarget target, Runnable drawApertureMask, Runnable clearApertureDepth, Runnable restoreApertureDepth) {
target.bindWrite(true);
if (stencilBits() <= 0) {
throw new IllegalStateException("Main render target has no stencil attachment");
}
DIRECT_TARGETS.add(target);
public static void beginFrame(RenderTarget target) {
target.bindWrite(true);
RenderSystem.disableScissor();
RenderSystem.stencilMask(0xFF);
RenderSystem.clearStencil(0);
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(false);
RenderSystem.enableDepthTest();
GL11.glEnable(GL11.GL_STENCIL_TEST);
try {
Objects.requireNonNull(drawApertureMask).run();
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(true);
RenderSystem.stencilMask(0x00);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
Objects.requireNonNull(clearApertureDepth).run();
} catch (RuntimeException | Error exception) {
try {
target.bindWrite(true);
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0x00);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(true);
restoreApertureDepth.run();
} catch (RuntimeException | Error restoreFailure) {
exception.addSuppressed(restoreFailure);
} finally {
DIRECT_TARGETS.remove(target);
resetState();
}
throw exception;
}
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.stencilMask(0x00);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.enableDepthTest();
return new Scope(target, restoreApertureDepth);
drawInside(0);
}
public static Scope begin(RenderTarget target, Runnable drawApertureMask) {
ensureAttachment(target);
target.bindWrite(true);
RenderSystem.disableScissor();
public static void endFrame() {
GL11.glDisable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0xFF);
RenderSystem.clearStencil(0);
GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(false);
RenderSystem.disableDepthTest();
GL11.glEnable(GL11.GL_STENCIL_TEST);
try {
Objects.requireNonNull(drawApertureMask).run();
} catch (RuntimeException | Error exception) {
resetState();
throw exception;
}
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 0, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.stencilMask(0x00);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
RenderSystem.enableDepthTest();
return new Scope(target);
}
public static void activate(RenderTarget target) {
if (!ATTACHMENTS.containsKey(target) && !DIRECT_TARGETS.contains(target)) {
return;
}
target.bindWrite(true);
public static void pushAperture(int parentLevel, Runnable drawAperture) {
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_EQUAL, parentLevel, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL_INCR);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(false);
RenderSystem.enableDepthTest();
try {
drawAperture.run();
} finally {
drawInside(parentLevel + 1);
}
}
public static void popAperture(int parentLevel, Runnable drawApertureDepth) {
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_EQUAL, parentLevel + 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL_DECR);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
try {
drawApertureDepth.run();
} finally {
drawInside(parentLevel);
}
}
public static void drawInside(int level) {
RenderSystem.stencilMask(0x00);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
RenderSystem.stencilFunc(GL11.GL_EQUAL, level, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
}
public static void release(RenderTarget target) {
DIRECT_TARGETS.remove(target);
UNSUPPORTED_MAIN_TARGETS.remove(target);
Attachment attachment = ATTACHMENTS.remove(target);
if (attachment != null) {
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
}
Attachment mainAttachment = MAIN_STENCIL_ATTACHMENTS.remove(target);
if (mainAttachment != null) {
GL30.glDeleteRenderbuffers(mainAttachment.renderbuffer());
@@ -158,16 +119,11 @@ public final class PortalStencilTarget {
}
public static void clear() {
for (Attachment attachment : ATTACHMENTS.values()) {
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
}
ATTACHMENTS.clear();
for (Attachment attachment : MAIN_STENCIL_ATTACHMENTS.values()) {
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
}
MAIN_STENCIL_ATTACHMENTS.clear();
UNSUPPORTED_MAIN_TARGETS.clear();
DIRECT_TARGETS.clear();
}
private static void ensureMainStencilAttachment(RenderTarget target) {
@@ -203,47 +159,6 @@ public final class PortalStencilTarget {
}
}
private static void ensureAttachment(RenderTarget target) {
Attachment attachment = ATTACHMENTS.get(target);
if (attachment != null
&& attachment.framebuffer() == target.frameBufferId
&& attachment.width() == target.width
&& attachment.height() == target.height) {
return;
}
if (attachment != null) {
GL30.glDeleteRenderbuffers(attachment.renderbuffer());
}
int previousFramebuffer = GL11.glGetInteger(GL30.GL_FRAMEBUFFER_BINDING);
int previousRenderbuffer = GL11.glGetInteger(GL30.GL_RENDERBUFFER_BINDING);
int renderbuffer = GL30.glGenRenderbuffers();
try {
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target.frameBufferId);
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, renderbuffer);
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, target.width, target.height);
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, renderbuffer);
int status = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER);
if (status != GL30.GL_FRAMEBUFFER_COMPLETE) {
GL30.glDeleteRenderbuffers(renderbuffer);
throw new IllegalStateException("Portal view framebuffer is incomplete with packed depth/stencil attachment: 0x" + Integer.toHexString(status));
}
ATTACHMENTS.put(target, new Attachment(target.frameBufferId, renderbuffer, target.width, target.height));
} finally {
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, previousRenderbuffer);
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, previousFramebuffer);
}
}
private static void resetState() {
GL11.glDisable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0xFF);
RenderSystem.stencilFunc(GL11.GL_ALWAYS, 0, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.depthMask(true);
RenderSystem.enableDepthTest();
}
private static int stencilBits() {
int type = GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT,
GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
@@ -253,44 +168,4 @@ public final class PortalStencilTarget {
private record Attachment(int framebuffer, int renderbuffer, int width, int height) {
}
public static final class Scope implements AutoCloseable {
private boolean closed;
private final RenderTarget target;
private final Runnable restoreApertureDepth;
private Scope(RenderTarget target) {
this(target, null);
}
private Scope(RenderTarget target, Runnable restoreApertureDepth) {
this.target = Objects.requireNonNull(target);
this.restoreApertureDepth = restoreApertureDepth;
}
@Override
public void close() {
if (closed) {
return;
}
closed = true;
try {
if (this.restoreApertureDepth != null) {
this.target.bindWrite(true);
GL11.glEnable(GL11.GL_STENCIL_TEST);
RenderSystem.stencilMask(0x00);
RenderSystem.stencilFunc(GL11.GL_EQUAL, 1, 0xFF);
RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
RenderSystem.colorMask(false, false, false, false);
RenderSystem.depthMask(true);
this.restoreApertureDepth.run();
}
} finally {
if (this.restoreApertureDepth != null) {
DIRECT_TARGETS.remove(this.target);
}
resetState();
}
}
}
}
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.content.client.renderer;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public interface PortalTerrain {
static boolean canView(Minecraft minecraft, Vec3 exitCenter) {
return SodiumCompat.isLoaded()
? com.craisinlord.antarchy.content.client.renderer.sodium.SodiumPortalTerrain.canView(minecraft, exitCenter)
: PortalGunPortalSectionViews.canView(minecraft, exitCenter);
}
static PortalTerrain prepare(Minecraft minecraft, java.util.UUID exitId, Vec3 exitCenter, Vec3 exitNormal, Frustum frustum) {
return SodiumCompat.isLoaded()
? com.craisinlord.antarchy.content.client.renderer.sodium.SodiumPortalTerrain.prepare(minecraft, frustum)
: VanillaPortalTerrain.prepare(minecraft, exitId, exitCenter, exitNormal, frustum);
}
int sectionCount();
void begin(Minecraft minecraft);
void end(Minecraft minecraft);
void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection);
void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer);
}
@@ -1,7 +0,0 @@
package com.craisinlord.antarchy.content.client.renderer;
public interface PortalViewSectionMarker {
void antarchy$markPortalViewSection();
boolean antarchy$isPortalViewSection();
}
@@ -1,23 +1,20 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.function.Consumer;
import net.minecraft.client.Camera;
import net.minecraft.world.level.block.entity.BlockEntity;
public final class SodiumCompat {
private static final String SODIUM_RENDERER_CLASS = "net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer";
private static final ThreadLocal<RenderState> RENDER_STATE = ThreadLocal.withInitial(RenderState::new);
private static boolean restoreWarningLogged;
private static Boolean loaded;
private SodiumCompat() {
}
public static boolean isLoaded() {
if (loaded == null) {
loaded = detect();
}
return loaded;
}
private static boolean detect() {
String resource = SODIUM_RENDERER_CLASS.replace('.', '/') + ".class";
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
if (contextLoader != null && contextLoader.getResource(resource) != null) {
@@ -26,109 +23,4 @@ public final class SodiumCompat {
ClassLoader ownLoader = SodiumCompat.class.getClassLoader();
return ownLoader != null && ownLoader.getResource(resource) != null;
}
public static void captureTerrainSetup(Camera camera, Object viewport, boolean spectator, boolean updateChunks) {
RENDER_STATE.get().activeSetup = new TerrainSetup(camera, viewport, spectator, updateChunks);
}
public static TerrainScope beginPortalView() {
RenderState state = RENDER_STATE.get();
TerrainSetup previousSetup = state.activeSetup;
state.previousSetups.push(new PreviousSetup(previousSetup));
return () -> {
PreviousSetup previous = state.previousSetups.pop();
if (previous.setup() == null) {
state.activeSetup = null;
return;
}
restoreTerrainSetup(previous.setup(), state);
};
}
public static void clear() {
RENDER_STATE.remove();
}
public static void forEachVisibleBlockEntity(Consumer<BlockEntity> consumer) {
try {
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
Class<?> rendererClass = Class.forName(SODIUM_RENDERER_CLASS, false,
contextLoader == null ? SodiumCompat.class.getClassLoader() : contextLoader);
Object renderer = rendererClass.getMethod("instanceNullable").invoke(null);
if (renderer != null) {
rendererClass.getMethod("iterateVisibleBlockEntities", Consumer.class).invoke(renderer, consumer);
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | LinkageError exception) {
logRestoreFailure("Could not iterate Sodium block entities for a portal view", exception);
}
}
private static void restoreTerrainSetup(TerrainSetup setup, RenderState state) {
try {
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
Class<?> rendererClass = Class.forName(SODIUM_RENDERER_CLASS, false,
contextLoader == null ? SodiumCompat.class.getClassLoader() : contextLoader);
Object renderer = rendererClass.getMethod("instanceNullable").invoke(null);
if (renderer == null) {
return;
}
Method terrainSetup = null;
for (Method method : rendererClass.getMethods()) {
Class<?>[] parameters = method.getParameterTypes();
if (method.getName().equals("setupTerrain")
&& parameters.length == 4
&& parameters[0].isInstance(setup.camera())
&& parameters[1].isInstance(setup.viewport())
&& parameters[2] == boolean.class
&& parameters[3] == boolean.class) {
terrainSetup = method;
break;
}
}
if (terrainSetup == null) {
logRestoreFailure("Sodium terrain setup method was not found", null);
state.activeSetup = setup;
return;
}
Class<?> deviceClass = Class.forName("net.caffeinemc.mods.sodium.client.gl.device.RenderDevice", false,
contextLoader == null ? SodiumCompat.class.getClassLoader() : contextLoader);
deviceClass.getMethod("enterManagedCode").invoke(null);
try {
terrainSetup.invoke(renderer, setup.camera(), setup.viewport(), setup.spectator(), setup.updateChunks());
} finally {
deviceClass.getMethod("exitManagedCode").invoke(null);
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | LinkageError exception) {
logRestoreFailure("Could not restore Sodium terrain after a portal view", exception);
state.activeSetup = setup;
}
}
private static void logRestoreFailure(String message, Throwable exception) {
if (!restoreWarningLogged) {
restoreWarningLogged = true;
if (exception == null) {
Antarchy.LOGGER.warn(message);
} else {
Antarchy.LOGGER.warn(message, exception);
}
}
}
@FunctionalInterface
public interface TerrainScope extends AutoCloseable {
@Override
void close();
}
private static final class RenderState {
private final Deque<PreviousSetup> previousSetups = new ArrayDeque<>();
private TerrainSetup activeSetup;
}
private record PreviousSetup(TerrainSetup setup) {
}
private record TerrainSetup(Camera camera, Object viewport, boolean spectator, boolean updateChunks) {
}
}
@@ -0,0 +1,134 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import com.mojang.blaze3d.shaders.Uniform;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import java.util.UUID;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.client.renderer.chunk.RenderRegionCache;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
final class VanillaPortalTerrain implements PortalTerrain {
private static final int MAX_REBUILDS_PER_SCENE = 32;
private final ObjectArrayList<SectionRenderDispatcher.RenderSection> sections;
private VanillaPortalTerrain(ObjectArrayList<SectionRenderDispatcher.RenderSection> sections) {
this.sections = sections;
}
static VanillaPortalTerrain prepare(Minecraft minecraft, UUID exitId, Vec3 exitCenter, Vec3 exitNormal, Frustum frustum) {
PortalGunPortalSectionViews.View view = PortalGunPortalSectionViews.prepare(minecraft, exitId, exitCenter, exitNormal, frustum);
view.visibleSections().removeIf(section -> !PortalGunPortalRenderState.shouldRenderBounds(section.getBoundingBox()));
queueDirtySections(minecraft, view.visibleSections());
return new VanillaPortalTerrain(view.visibleSections());
}
@Override
public int sectionCount() {
return this.sections.size();
}
@Override
public void begin(Minecraft minecraft) {
}
@Override
public void end(Minecraft minecraft) {
}
@Override
public void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection) {
renderType.setupRenderState();
ShaderInstance shader = RenderSystem.getShader();
if (shader == null) {
renderType.clearRenderState();
return;
}
shader.setDefaultUniforms(VertexFormat.Mode.QUADS, viewMatrix, projection, minecraft.getWindow());
shader.apply();
Uniform chunkOffset = shader.CHUNK_OFFSET;
boolean frontToBack = renderType != RenderType.translucent();
ObjectListIterator<SectionRenderDispatcher.RenderSection> iterator =
this.sections.listIterator(frontToBack ? 0 : this.sections.size());
try {
while (frontToBack ? iterator.hasNext() : iterator.hasPrevious()) {
SectionRenderDispatcher.RenderSection section = frontToBack ? iterator.next() : iterator.previous();
if (section.getCompiled().isEmpty(renderType)) {
continue;
}
VertexBuffer buffer = section.getBuffer(renderType);
BlockPos origin = section.getOrigin();
if (chunkOffset != null) {
chunkOffset.set((float) (origin.getX() - cameraPosition.x), (float) (origin.getY() - cameraPosition.y),
(float) (origin.getZ() - cameraPosition.z));
chunkOffset.upload();
}
buffer.bind();
buffer.draw();
}
} finally {
if (chunkOffset != null) {
chunkOffset.set(0.0F, 0.0F, 0.0F);
}
shader.clear();
VertexBuffer.unbind();
renderType.clearRenderState();
}
}
@Override
public void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer) {
for (SectionRenderDispatcher.RenderSection section : this.sections) {
for (BlockEntity blockEntity : section.getCompiled().getRenderableBlockEntities()) {
consumer.accept(blockEntity);
}
}
LevelRendererPortalViewAreaAccessor state = (LevelRendererPortalViewAreaAccessor) minecraft.levelRenderer;
synchronized (state.antarchy$getGlobalBlockEntities()) {
for (BlockEntity blockEntity : state.antarchy$getGlobalBlockEntities()) {
if (frustum.isVisible(new AABB(blockEntity.getBlockPos()).inflate(1.0D))) {
consumer.accept(blockEntity);
}
}
}
}
private static void queueDirtySections(Minecraft minecraft, ObjectArrayList<SectionRenderDispatcher.RenderSection> sections) {
SectionRenderDispatcher dispatcher = minecraft.levelRenderer.getSectionRenderDispatcher();
if (dispatcher == null) {
return;
}
RenderRegionCache cache = null;
int queued = 0;
for (SectionRenderDispatcher.RenderSection section : sections) {
if (queued >= MAX_REBUILDS_PER_SCENE) {
break;
}
if (!section.isDirty() || !minecraft.level.getLightEngine().lightOnInSection(SectionPos.of(section.getOrigin()))) {
continue;
}
if (cache == null) {
cache = new RenderRegionCache();
}
section.rebuildSectionAsync(dispatcher, cache);
section.setNotDirty();
queued++;
}
}
}
@@ -0,0 +1,208 @@
package com.craisinlord.antarchy.content.client.renderer.sodium;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.content.client.renderer.PortalTerrain;
import com.craisinlord.antarchy.mixins.compat.sodium.RenderSectionManagerAccessor;
import com.craisinlord.antarchy.mixins.compat.sodium.SodiumWorldRendererAccessor;
import java.util.ArrayDeque;
import java.util.EnumMap;
import java.util.Map;
import java.util.function.Consumer;
import net.caffeinemc.mods.sodium.client.SodiumClientMod;
import net.caffeinemc.mods.sodium.client.gl.device.RenderDevice;
import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer;
import net.caffeinemc.mods.sodium.client.render.chunk.ChunkRenderMatrices;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
import net.caffeinemc.mods.sodium.client.render.chunk.TaskQueueType;
import net.caffeinemc.mods.sodium.client.render.chunk.lists.CoordinateSectionVisitor;
import net.caffeinemc.mods.sodium.client.render.chunk.lists.SortedRenderLists;
import net.caffeinemc.mods.sodium.client.render.chunk.lists.TreeSectionCollector;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
import net.caffeinemc.mods.sodium.client.render.viewport.Viewport;
import net.caffeinemc.mods.sodium.client.render.viewport.ViewportProvider;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
public final class SodiumPortalTerrain implements PortalTerrain {
private static final int FIRST_PORTAL_FRAME = -1_000_000;
private static final int MAX_PENDING_TASKS_PER_QUEUE = 256;
private static final Map<TaskQueueType, ArrayDeque<RenderSection>> PENDING_TASKS = new EnumMap<>(TaskQueueType.class);
private static int nextFrame = FIRST_PORTAL_FRAME;
private final SodiumWorldRenderer worldRenderer;
private final RenderSectionManager sectionManager;
private final SortedRenderLists renderLists;
private final int sectionCount;
private SortedRenderLists previousRenderLists;
private boolean previousEntityCulling;
private SodiumPortalTerrain(SodiumWorldRenderer worldRenderer, RenderSectionManager sectionManager, SortedRenderLists renderLists, int sectionCount) {
this.worldRenderer = worldRenderer;
this.sectionManager = sectionManager;
this.renderLists = renderLists;
this.sectionCount = sectionCount;
}
public static boolean canView(Minecraft minecraft, Vec3 exitCenter) {
SodiumWorldRenderer worldRenderer = SodiumWorldRenderer.instanceNullable();
if (worldRenderer == null || minecraft.player == null
|| ((SodiumWorldRendererAccessor) worldRenderer).antarchy$getRenderSectionManager() == null) {
return false;
}
int limit = minecraft.options.getEffectiveRenderDistance();
return Math.abs(SectionPos.posToSectionCoord(exitCenter.x) - minecraft.player.chunkPosition().x) <= limit
&& Math.abs(SectionPos.posToSectionCoord(exitCenter.z) - minecraft.player.chunkPosition().z) <= limit;
}
public static PortalTerrain prepare(Minecraft minecraft, Frustum frustum) {
SodiumWorldRenderer worldRenderer = SodiumWorldRenderer.instanceNullable();
RenderSectionManager sectionManager = worldRenderer == null ? null
: ((SodiumWorldRendererAccessor) worldRenderer).antarchy$getRenderSectionManager();
if (sectionManager == null) {
return EmptyTerrain.INSTANCE;
}
RenderSectionManagerAccessor manager = (RenderSectionManagerAccessor) sectionManager;
int frame = nextFrame--;
if (nextFrame > FIRST_PORTAL_FRAME) {
nextFrame = FIRST_PORTAL_FRAME;
}
Viewport viewport = ((ViewportProvider) frustum).sodium$createViewport();
TreeSectionCollector collector = new TreeSectionCollector(frame,
SodiumClientMod.options().performance.chunkBuildDeferMode.getImportantRebuildQueueType(),
manager.antarchy$getSortBehavior().getDeferMode().getImportantRebuildQueueType(),
manager.antarchy$getSectionByPosition());
int[] visited = {0};
CoordinateSectionVisitor apertureFilter = (x, y, z) -> {
int minX = SectionPos.sectionToBlockCoord(x);
int minY = SectionPos.sectionToBlockCoord(y);
int minZ = SectionPos.sectionToBlockCoord(z);
if (PortalGunPortalRenderState.shouldRenderBounds(new AABB(minX, minY, minZ, minX + 16, minY + 16, minZ + 16))) {
collector.visit(x, y, z);
visited[0]++;
}
};
manager.antarchy$getRenderableSectionTree().prepareForTraversal();
manager.antarchy$getRenderableSectionTree().traverse(apertureFilter, viewport, manager.antarchy$getSearchDistance());
SortedRenderLists renderLists = collector.createRenderLists(viewport);
stashPendingTasks(collector.getTaskLists());
sectionManager.markGraphDirty();
return new SodiumPortalTerrain(worldRenderer, sectionManager, renderLists, visited[0]);
}
public static void drainPendingTasks(Map<TaskQueueType, ArrayDeque<RenderSection>> taskLists) {
if (PENDING_TASKS.isEmpty() || taskLists == null) {
return;
}
for (Map.Entry<TaskQueueType, ArrayDeque<RenderSection>> entry : PENDING_TASKS.entrySet()) {
ArrayDeque<RenderSection> queue = taskLists.get(entry.getKey());
if (queue == null) {
continue;
}
int limit = entry.getKey().queueSizeLimit();
for (RenderSection section : entry.getValue()) {
if (queue.size() >= limit) {
break;
}
if (!section.isDisposed() && section.getPendingUpdate() != 0 && section.getRunningJob() == null) {
queue.add(section);
}
}
}
PENDING_TASKS.clear();
}
private static void stashPendingTasks(Map<TaskQueueType, ArrayDeque<RenderSection>> taskLists) {
for (Map.Entry<TaskQueueType, ArrayDeque<RenderSection>> entry : taskLists.entrySet()) {
if (entry.getValue().isEmpty()) {
continue;
}
ArrayDeque<RenderSection> pending = PENDING_TASKS.computeIfAbsent(entry.getKey(), type -> new ArrayDeque<>());
for (RenderSection section : entry.getValue()) {
if (pending.size() >= MAX_PENDING_TASKS_PER_QUEUE) {
break;
}
pending.add(section);
}
}
}
@Override
public int sectionCount() {
return this.sectionCount;
}
@Override
public void begin(Minecraft minecraft) {
RenderSectionManagerAccessor manager = (RenderSectionManagerAccessor) this.sectionManager;
SodiumWorldRendererAccessor renderer = (SodiumWorldRendererAccessor) this.worldRenderer;
this.previousRenderLists = manager.antarchy$getRenderLists();
this.previousEntityCulling = renderer.antarchy$getUseEntityCulling();
manager.antarchy$setRenderLists(this.renderLists);
renderer.antarchy$setUseEntityCulling(false);
}
@Override
public void end(Minecraft minecraft) {
((RenderSectionManagerAccessor) this.sectionManager).antarchy$setRenderLists(this.previousRenderLists);
((SodiumWorldRendererAccessor) this.worldRenderer).antarchy$setUseEntityCulling(this.previousEntityCulling);
}
@Override
public void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer) {
this.worldRenderer.iterateVisibleBlockEntities(consumer);
}
@Override
public void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection) {
TerrainRenderPass[] passes;
if (renderType == RenderType.solid()) {
passes = new TerrainRenderPass[] {DefaultTerrainRenderPasses.SOLID, DefaultTerrainRenderPasses.CUTOUT};
} else if (renderType == RenderType.translucent()) {
passes = new TerrainRenderPass[] {DefaultTerrainRenderPasses.TRANSLUCENT};
} else {
return;
}
ChunkRenderMatrices matrices = new ChunkRenderMatrices(projection, viewMatrix);
RenderDevice.enterManagedCode();
try {
for (TerrainRenderPass pass : passes) {
this.sectionManager.renderLayer(matrices, pass, cameraPosition.x, cameraPosition.y, cameraPosition.z);
}
} finally {
RenderDevice.exitManagedCode();
}
}
private enum EmptyTerrain implements PortalTerrain {
INSTANCE;
@Override
public int sectionCount() {
return 0;
}
@Override
public void begin(Minecraft minecraft) {
}
@Override
public void end(Minecraft minecraft) {
}
@Override
public void drawLayer(Minecraft minecraft, RenderType renderType, Vec3 cameraPosition, Matrix4f viewMatrix, Matrix4f projection) {
}
@Override
public void forEachBlockEntity(Minecraft minecraft, Frustum frustum, Consumer<BlockEntity> consumer) {
}
}
}
@@ -102,7 +102,7 @@ public class DrTrayaurusEntity extends AbstractVillager {
protected void updateTrades() {
MerchantOffers offers = this.getOffers();
offers.clear();
offers.addAll(DrTrayaurusTradeManager.createOffers());
offers.addAll(DrTrayaurusTradeManager.createOffers(this.registryAccess()));
this.overrideXp(1);
this.tradesInitialized = true;
}
@@ -6,7 +6,13 @@ import com.google.gson.JsonParseException;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.mojang.logging.LogUtils;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponentPredicate;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
@@ -20,8 +26,10 @@ import net.minecraft.world.item.trading.MerchantOffers;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
public final class DrTrayaurusTradeManager extends SimpleJsonResourceReloadListener {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Gson GSON = new Gson();
private static final String DIRECTORY = "trayaurus_trades";
private static final Codec<TradeFile> TRADE_FILE_CODEC = RecordCodecBuilder.create(instance -> instance.group(
@@ -47,10 +55,10 @@ public final class DrTrayaurusTradeManager extends SimpleJsonResourceReloadListe
loadedTrades = parsedTrades.isEmpty() ? defaultTrades() : List.copyOf(parsedTrades);
}
public static MerchantOffers createOffers() {
public static MerchantOffers createOffers(HolderLookup.Provider registries) {
MerchantOffers offers = new MerchantOffers();
for (TradeEntry trade : loadedTrades) {
trade.toOffer().ifPresent(offers::add);
trade.toOffer(registries).ifPresent(offers::add);
}
return offers;
}
@@ -78,18 +86,18 @@ public final class DrTrayaurusTradeManager extends SimpleJsonResourceReloadListe
Codec.FLOAT.optionalFieldOf("price_multiplier", 0.05F).forGetter(TradeEntry::priceMultiplier)
).apply(instance, TradeEntry::new));
private Optional<MerchantOffer> toOffer() {
private Optional<MerchantOffer> toOffer(HolderLookup.Provider registries) {
if (this.maxUses < 1 || this.villagerXp < 1 || this.villagerXp > 99) {
return Optional.empty();
}
Optional<ItemCost> buyCost = this.buy.toCost();
Optional<ItemCost> buyCost = this.buy.toCost(registries);
if (buyCost.isEmpty()) {
return Optional.empty();
}
Optional<ItemCost> secondBuyCost = this.secondBuy.flatMap(StackEntry::toCost);
ItemStack sellStack = this.sell.toStack();
Optional<ItemCost> secondBuyCost = this.secondBuy.flatMap(entry -> entry.toCost(registries));
ItemStack sellStack = this.sell.toStack(registries);
if (sellStack.isEmpty()) {
return Optional.empty();
}
@@ -105,28 +113,60 @@ public final class DrTrayaurusTradeManager extends SimpleJsonResourceReloadListe
}
}
private record StackEntry(ResourceLocation itemId, int count) {
/**
* One side of a trade. {@code components} is optional: on {@code sell} it is a data component
* patch applied to the result (enchantments, custom name, ...); on {@code buy}/{@code second_buy}
* it is a component predicate the offered stack must match exactly for each listed component.
* It is kept as raw JSON and parsed per offer because components such as enchantments need
* registry access, which the reload listener does not have.
*/
private record StackEntry(ResourceLocation itemId, int count, Optional<JsonElement> components) {
private static final Codec<StackEntry> CODEC = RecordCodecBuilder.create(instance -> instance.group(
ResourceLocation.CODEC.fieldOf("item").forGetter(StackEntry::itemId),
Codec.INT.optionalFieldOf("count", 1).forGetter(StackEntry::count)
Codec.INT.optionalFieldOf("count", 1).forGetter(StackEntry::count),
ExtraCodecs.JSON.optionalFieldOf("components").forGetter(StackEntry::components)
).apply(instance, StackEntry::new));
private StackEntry(ResourceLocation itemId, int count) {
this(itemId, count, Optional.empty());
}
private Optional<Item> item() {
return BuiltInRegistries.ITEM.getOptional(this.itemId)
.filter(item -> item != net.minecraft.world.item.Items.AIR);
}
private ItemStack toStack() {
return this.item()
.filter(item -> this.count > 0)
.map(item -> new ItemStack(item, this.count))
.orElse(ItemStack.EMPTY);
private ItemStack toStack(HolderLookup.Provider registries) {
Optional<Item> item = this.item().filter(found -> this.count > 0);
if (item.isEmpty()) {
return ItemStack.EMPTY;
}
ItemStack stack = new ItemStack(item.get(), this.count);
if (this.components.isPresent()) {
Optional<DataComponentPatch> patch = DataComponentPatch.CODEC
.parse(RegistryOps.create(JsonOps.INSTANCE, registries), this.components.get())
.resultOrPartial(message -> LOGGER.error("Dr. Trayaurus trade {}: bad components: {}", this.itemId, message));
if (patch.isEmpty()) {
return ItemStack.EMPTY;
}
stack.applyComponents(patch.get());
}
return stack;
}
private Optional<ItemCost> toCost() {
return this.item()
.filter(item -> this.count > 0)
.map(item -> new ItemCost(item, this.count));
private Optional<ItemCost> toCost(HolderLookup.Provider registries) {
Optional<Item> item = this.item().filter(found -> this.count > 0);
if (item.isEmpty()) {
return Optional.empty();
}
if (this.components.isEmpty()) {
return Optional.of(new ItemCost(item.get(), this.count));
}
// A predicate that fails to parse drops the trade rather than accepting any stack.
return DataComponentPredicate.CODEC
.parse(RegistryOps.create(JsonOps.INSTANCE, registries), this.components.get())
.resultOrPartial(message -> LOGGER.error("Dr. Trayaurus trade {}: bad components: {}", this.itemId, message))
.map(predicate -> new ItemCost(item.get().builtInRegistryHolder(), this.count, predicate));
}
}
}
@@ -357,28 +357,27 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
public PortalCrossing resolveCrossing(Entity entity, AABB previousBox, AABB currentBox) {
Vec3 previousProbe = this.teleportProbePosition(entity, previousBox);
Vec3 currentProbe = this.teleportProbePosition(entity, currentBox);
Vec3 normal = this.getNormalVec().normalize();
if (entity instanceof Player && Math.abs(normal.y) < 0.5D) {
if (entity instanceof Player) {
PortalGunWorldPortalShape shape = this.getWorldPortalShape();
double previousDepth = shape.localCoords(previousProbe).depth();
double currentDepth = shape.localCoords(currentProbe).depth();
double halfBodyDepth = entity.getBbWidth() * 0.5D;
if (previousDepth <= 0.0D || currentDepth >= previousDepth - 1.0E-6D
|| currentDepth - halfBodyDepth > 0.02D || currentDepth < -shape.halfDepth()) {
return null;
}
Vec3 center = currentBox.getCenter();
PortalGunWorldPortalShape.PortalLocalCoords localCenter = shape.localCoords(center);
double previousDepth = shape.localCoords(previousBox.getCenter()).depth();
double currentDepth = localCenter.depth();
double halfX = (currentBox.maxX - currentBox.minX) * 0.5D;
double halfY = (currentBox.maxY - currentBox.minY) * 0.5D;
double halfZ = (currentBox.maxZ - currentBox.minZ) * 0.5D;
double depthExtent = halfX * Math.abs(shape.normal().x) + halfY * Math.abs(shape.normal().y) + halfZ * Math.abs(shape.normal().z);
if (previousDepth <= 0.0D || currentDepth >= previousDepth - 1.0E-6D
|| currentDepth - depthExtent > 0.02D || currentDepth < -shape.halfDepth()) {
return null;
}
double widthExtent = halfX * Math.abs(shape.right().x) + halfY * Math.abs(shape.right().y) + halfZ * Math.abs(shape.right().z);
double heightExtent = halfX * Math.abs(shape.up().x) + halfY * Math.abs(shape.up().y) + halfZ * Math.abs(shape.up().z);
if (Math.abs(localCenter.horizontal()) + widthExtent > shape.halfWidth() + 1.0E-3D
|| Math.abs(localCenter.vertical()) + heightExtent > shape.halfHeight() + 1.0E-3D) {
return null;
}
return new PortalCrossing(currentProbe, Math.max(0.05D, currentDepth + 0.05D));
return new PortalCrossing(currentProbe, Math.max(0.05D, shape.localCoords(currentProbe).depth() + 0.05D));
}
return this.crossesPortal(previousProbe, currentProbe) ? new PortalCrossing(currentProbe, 0.0D) : null;
}
@@ -403,10 +402,20 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
}
public static void teleportEntityAfterMovement(Entity entity, AABB previousBox, Vec3 resolvedMovement) {
if (entity instanceof ServerPlayer) {
return;
}
teleportAfterMovement(entity, previousBox, entity.getBoundingBox(), resolvedMovement);
}
public static void teleportPlayerAfterMovementPacket(ServerPlayer player, AABB previousBox, Vec3 claimedMovement) {
teleportAfterMovement(player, previousBox, previousBox.move(claimedMovement), claimedMovement);
}
private static void teleportAfterMovement(Entity entity, AABB previousBox, AABB currentBox, Vec3 resolvedMovement) {
if (entity.level().isClientSide || !entity.isAlive() || entity.isPassenger() || entity instanceof PortalGunPortalEntity) {
return;
}
AABB currentBox = entity.getBoundingBox();
if (previousBox.getCenter().distanceToSqr(currentBox.getCenter()) <= 1.0E-10D) {
return;
}
@@ -6,13 +6,15 @@ import org.joml.Vector4f;
/** Applies the destination portal plane as the near plane of a camera projection. */
public final class PortalGunProjectionUtil {
public static final double MIN_EYE_PLANE_DISTANCE = 0.01D;
private PortalGunProjectionUtil() {
}
public static Matrix4f clipAtExit(Matrix4f projection, Vec3 eye, Vec3 look, Vec3 up,
Vec3 planePoint, Vec3 planeNormal) {
Vector4f clipPlane = planeInCameraSpace(eye, look, up, planePoint, planeNormal);
if (clipPlane.lengthSquared() <= 1.0E-6F) {
if (clipPlane.w() > -MIN_EYE_PLANE_DISTANCE) {
return projection;
}
Vector4f q = new Vector4f(signNonZero(clipPlane.x()), signNonZero(clipPlane.y()), 1.0F, 1.0F)
@@ -42,15 +44,13 @@ public final class PortalGunProjectionUtil {
(float) -relativePoint.dot(forward),
1.0F
);
Vec3 unitNormal = planeNormal.normalize();
Vector4f normal = new Vector4f(
(float) planeNormal.dot(right),
(float) planeNormal.dot(correctedUp),
(float) -planeNormal.dot(forward),
(float) unitNormal.dot(right),
(float) unitNormal.dot(correctedUp),
(float) -unitNormal.dot(forward),
0.0F
);
if (normal.z() > 0.0F) {
normal.mul(-1.0F);
}
return new Vector4f(normal.x(), normal.y(), normal.z(), -normal.dot(point));
}
@@ -1,6 +1,5 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.client.renderer.SodiumCompat;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.tree.ClassNode;
@@ -29,18 +28,6 @@ public final class AntarchyMixinPlugin implements IMixinConfigPlugin {
if (!classPresent(targetClassName)) {
return false;
}
if (SodiumCompat.isLoaded()
&& (mixinClassName.endsWith(".LevelRendererPortalViewAreaAccessor")
|| mixinClassName.endsWith(".LevelRendererPortalViewAreaMixin")
|| mixinClassName.endsWith(".LevelRendererPortalChunkBudgetMixin")
|| mixinClassName.endsWith(".SectionRenderDispatcherPortalUploadsAccessor")
|| mixinClassName.endsWith(".PortalViewChunkCacheMixin")
|| mixinClassName.endsWith(".PortalViewSectionSortingMixin"))) {
return false;
}
if (mixinClassName.endsWith(".SodiumWorldRendererMixin")) {
return SodiumCompat.isLoaded();
}
if (mixinClassName.startsWith("com.craisinlord.antarchy.mixins.compat.scguns.")) {
return classPresent("top.ribs.scguns.entity.projectile.ProjectileEntity");
}
@@ -0,0 +1,56 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerGamePacketListenerImpl.class)
public abstract class PortalGunPlayerMovePacketMixin {
@Unique
private static final double MAX_CLAIMED_MOVEMENT_SQR = 100.0D;
@Unique
private AABB antarchy$portalGunMoveStartBox;
@Unique
private Vec3 antarchy$portalGunMoveStartPosition;
@Inject(method = "handleMovePlayer", at = @At("HEAD"))
private void antarchy$capturePortalGunMoveStart(ServerboundMovePlayerPacket packet, CallbackInfo ci) {
ServerPlayer player = ((ServerGamePacketListenerImpl) (Object) this).player;
if (player.getServer() == null || !player.getServer().isSameThread()) {
return;
}
this.antarchy$portalGunMoveStartBox = null;
this.antarchy$portalGunMoveStartPosition = null;
if (!packet.hasPosition() || ((ServerGamePacketListenerTeleportAccessor) this).antarchy$getAwaitingPositionFromClient() != null) {
return;
}
this.antarchy$portalGunMoveStartBox = player.getBoundingBox();
this.antarchy$portalGunMoveStartPosition = player.position();
}
@Inject(method = "handleMovePlayer", at = @At("RETURN"))
private void antarchy$teleportPlayerOnPortalCrossing(ServerboundMovePlayerPacket packet, CallbackInfo ci) {
AABB startBox = this.antarchy$portalGunMoveStartBox;
Vec3 startPosition = this.antarchy$portalGunMoveStartPosition;
this.antarchy$portalGunMoveStartBox = null;
this.antarchy$portalGunMoveStartPosition = null;
if (startBox == null || startPosition == null) {
return;
}
ServerPlayer player = ((ServerGamePacketListenerImpl) (Object) this).player;
Vec3 claimedMovement = new Vec3(packet.getX(startPosition.x), packet.getY(startPosition.y), packet.getZ(startPosition.z))
.subtract(startPosition);
double lengthSqr = claimedMovement.lengthSqr();
if (lengthSqr > 1.0E-10D && lengthSqr < MAX_CLAIMED_MOVEMENT_SQR) {
PortalGunPortalEntity.teleportPlayerAfterMovementPacket(player, startBox, claimedMovement);
}
}
}
@@ -0,0 +1,12 @@
package com.craisinlord.antarchy.mixins;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ServerGamePacketListenerImpl.class)
public interface ServerGamePacketListenerTeleportAccessor {
@Accessor("awaitingPositionFromClient")
Vec3 antarchy$getAwaitingPositionFromClient();
}
@@ -1,73 +0,0 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalRendererPool;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.chunk.RenderRegionCache;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.world.level.lighting.LevelLightEngine;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(LevelRenderer.class)
public abstract class LevelRendererPortalChunkBudgetMixin {
@Redirect(
method = "compileSections",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;uploadAllPendingUploads()V")
)
private void antarchy$budgetPortalUploads(SectionRenderDispatcher dispatcher) {
PortalGunPortalRendererPool.uploadPending((LevelRenderer) (Object) this, dispatcher);
}
@Redirect(
method = "renderLevel",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;pollLightUpdates()V")
)
private void antarchy$skipProxyLightQueuePoll(ClientLevel level) {
if (!PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
level.pollLightUpdates();
}
}
@Redirect(
method = "renderLevel",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I")
)
private int antarchy$skipProxyLightQueueDrain(LevelLightEngine lightEngine) {
return PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this) ? 0 : lightEngine.runLightUpdates();
}
@Redirect(
method = "compileSections",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;rebuildSectionSync(Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;Lnet/minecraft/client/renderer/chunk/RenderRegionCache;)V"
)
)
private void antarchy$queuePortalSyncRebuilds(SectionRenderDispatcher dispatcher, SectionRenderDispatcher.RenderSection section, RenderRegionCache cache) {
PortalGunPortalRendererPool.rebuildSection((LevelRenderer) (Object) this, dispatcher, section, cache);
}
@Redirect(
method = "compileSections",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;rebuildSectionAsync(Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;Lnet/minecraft/client/renderer/chunk/RenderRegionCache;)V"
)
)
private void antarchy$budgetPortalAsyncRebuilds(SectionRenderDispatcher.RenderSection section, SectionRenderDispatcher dispatcher, RenderRegionCache cache) {
PortalGunPortalRendererPool.rebuildSectionAsync((LevelRenderer) (Object) this, dispatcher, section, cache);
}
@Redirect(
method = "compileSections",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher$RenderSection;setNotDirty()V"
)
)
private void antarchy$keepUnscheduledPortalSectionsDirty(SectionRenderDispatcher.RenderSection section) {
PortalGunPortalRendererPool.setNotDirty(section);
}
}
@@ -1,10 +1,6 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalRendererPool;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalVisibilityQueries;
import com.craisinlord.antarchy.content.client.renderer.PortalStencilTarget;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
@@ -18,40 +14,24 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class LevelRendererPortalLifecycleMixin {
@Inject(method = "renderLevel", at = @At("HEAD"))
private void antarchy$preparePortalStencil(CallbackInfo ci) {
if (PortalGunPortalViewRenderer.isEnabled() && !PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
if (PortalGunPortalViewRenderer.isEnabled()) {
PortalStencilTarget.prepareMainTarget(Minecraft.getInstance().getMainRenderTarget());
}
}
@Inject(method = "setLevel", at = @At("HEAD"))
private void antarchy$clearPortalViewsOnLevelChange(ClientLevel level, CallbackInfo ci) {
if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
return;
}
PortalGunPortalRendererPool.clear();
PortalGunPortalViewAreaManager.clear();
PortalGunPortalVisibilityQueries.clear();
PortalGunPortalViewRenderer.releaseTargets();
}
@Inject(method = "close", at = @At("HEAD"))
private void antarchy$clearPortalViewsOnClose(CallbackInfo ci) {
if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
return;
}
PortalGunPortalRendererPool.clear();
PortalGunPortalViewAreaManager.clear();
PortalGunPortalViewRenderer.releaseTargets();
PortalStencilTarget.clear();
}
@Inject(method = "allChanged", at = @At("TAIL"))
private void antarchy$clearPortalViewsOnRendererRebuild(CallbackInfo ci) {
if (PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) {
return;
}
if (PortalGunPortalRenderState.getContext() == null) {
PortalGunPortalRendererPool.clear();
PortalGunPortalViewAreaManager.clear();
PortalGunPortalViewRenderer.releaseTargets();
}
PortalGunPortalViewRenderer.releaseTargets();
}
}
@@ -2,27 +2,15 @@ package com.craisinlord.antarchy.mixins.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(LevelRenderer.class)
public interface LevelRendererPortalSceneInvoker {
@Invoker("setupRender")
void antarchy$setupPortalScene(Camera camera, Frustum frustum, boolean capturedFrustum, boolean spectator);
@Invoker("compileSections")
void antarchy$compilePortalSections(Camera camera);
@Invoker("renderSectionLayer")
void antarchy$renderPortalSectionLayer(RenderType renderType, double cameraX, double cameraY, double cameraZ, Matrix4f modelViewMatrix, Matrix4f projectionMatrix);
@Invoker("renderEntity")
void antarchy$renderPortalEntity(Entity entity, double cameraX, double cameraY, double cameraZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource);
@@ -1,15 +0,0 @@
package com.craisinlord.antarchy.mixins.client;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.PostChain;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(LevelRenderer.class)
public interface LevelRendererPortalTargetsAccessor {
@Accessor("entityEffect")
PostChain antarchy$getEntityEffect();
@Accessor("transparencyChain")
PostChain antarchy$getTransparencyChain();
}
@@ -1,8 +1,7 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalSectionViews;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
@@ -25,39 +24,13 @@ public abstract class LevelRendererPortalViewAreaMixin {
}
}
@ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getX()D"), require = 0)
private double antarchy$usePortalCameraX(double original) {
PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext();
return context == null ? original : context.cameraPos().x;
}
@ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getY()D"), require = 0)
private double antarchy$usePortalCameraY(double original) {
PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext();
return context == null ? original : context.cameraPos().y;
}
@ModifyExpressionValue(method = "setupRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getZ()D"), require = 0)
private double antarchy$usePortalCameraZ(double original) {
PortalGunPortalRenderState.PortalRenderContext context = PortalGunPortalRenderState.getContext();
return context == null ? original : context.cameraPos().z;
}
@Inject(method = "addRecentlyCompiledSection", at = @At("HEAD"), cancellable = true)
private void antarchy$routePortalSectionCompile(SectionRenderDispatcher.RenderSection section, CallbackInfo ci) {
if (PortalGunPortalViewAreaManager.routeSectionCompiled(section)) {
ci.cancel();
}
}
@Inject(method = "setSectionDirty(IIIZ)V", at = @At("TAIL"))
private void antarchy$dirtyPortalSection(int sectionX, int sectionY, int sectionZ, boolean playerChanged, CallbackInfo ci) {
PortalGunPortalViewAreaManager.onSectionDirty(sectionX, sectionY, sectionZ, playerChanged);
@Inject(method = "addRecentlyCompiledSection", at = @At("TAIL"))
private void antarchy$notifyPortalGraphs(SectionRenderDispatcher.RenderSection section, CallbackInfo ci) {
PortalGunPortalSectionViews.onSectionCompiled(section);
}
@Inject(method = "onChunkLoaded", at = @At("TAIL"))
private void antarchy$updatePortalChunkGraph(ChunkPos pos, CallbackInfo ci) {
PortalGunPortalViewAreaManager.onChunkLoaded(pos);
PortalGunPortalSectionViews.onChunkLoaded(pos);
}
}
@@ -0,0 +1,18 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalSceneRenderer;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Minecraft.class)
public abstract class MinecraftPortalTransparencyMixin {
@Inject(method = "useShaderTransparency", at = @At("HEAD"), cancellable = true)
private static void antarchy$disableShaderTransparencyInPortalScenes(CallbackInfoReturnable<Boolean> cir) {
if (PortalGunPortalSceneRenderer.isRenderingScene()) {
cir.setReturnValue(false);
}
}
}
@@ -1,6 +1,6 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalSectionViews;
import net.minecraft.client.multiplayer.ClientChunkCache;
import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Mixin;
@@ -12,6 +12,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class PortalViewChunkCacheMixin {
@Inject(method = "drop", at = @At("TAIL"))
private void antarchy$invalidatePortalChunk(ChunkPos pos, CallbackInfo ci) {
PortalGunPortalViewAreaManager.onChunkDropped(pos);
PortalGunPortalSectionViews.onChunkDropped(pos);
}
}
@@ -1,42 +0,0 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewAreaManager;
import com.craisinlord.antarchy.content.client.renderer.PortalViewSectionMarker;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import org.spongepowered.asm.mixin.Unique;
import java.util.Collection;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(SectionRenderDispatcher.RenderSection.class)
public abstract class PortalViewSectionSortingMixin implements PortalViewSectionMarker {
@Unique
private boolean antarchy$portalViewSection;
@Override
public void antarchy$markPortalViewSection() {
this.antarchy$portalViewSection = true;
}
@Override
public boolean antarchy$isPortalViewSection() {
return this.antarchy$portalViewSection;
}
@ModifyExpressionValue(method = "createVertexSorting", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/SectionRenderDispatcher;getCameraPosition()Lnet/minecraft/world/phys/Vec3;"))
private Vec3 antarchy$sortPortalGeometryFromVirtualCamera(Vec3 original) {
return PortalGunPortalViewAreaManager.cameraForSection((SectionRenderDispatcher.RenderSection) (Object) this, original);
}
@Redirect(method = "updateGlobalBlockEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;updateGlobalBlockEntities(Ljava/util/Collection;Ljava/util/Collection;)V"))
private void antarchy$routePortalGlobalBlockEntities(LevelRenderer renderer, Collection<BlockEntity> removed, Collection<BlockEntity> added) {
if (!PortalGunPortalViewAreaManager.updateGlobalBlockEntities((SectionRenderDispatcher.RenderSection) (Object) this, removed, added)) {
renderer.updateGlobalBlockEntities(removed, added);
}
}
}
@@ -1,17 +0,0 @@
package com.craisinlord.antarchy.mixins.client;
import com.mojang.blaze3d.pipeline.RenderTarget;
import net.minecraft.client.renderer.PostChain;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(PostChain.class)
public interface PostChainTargetAccessor {
@Accessor("screenTarget")
RenderTarget antarchy$getScreenTarget();
@Mutable
@Accessor("screenTarget")
void antarchy$setScreenTarget(RenderTarget renderTarget);
}
@@ -0,0 +1,35 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.PortalSectionGraphMarker;
import net.minecraft.client.renderer.SectionOcclusionGraph;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SectionOcclusionGraph.class)
public abstract class SectionOcclusionGraphPortalMixin implements PortalSectionGraphMarker {
@Unique
private volatile boolean antarchy$portalGraph;
@Override
public void antarchy$markPortalGraph() {
this.antarchy$portalGraph = true;
}
@Inject(method = "getRelativeFrom", at = @At("RETURN"), cancellable = true)
private void antarchy$rejectWrappedPortalSections(BlockPos cameraSectionPos, SectionRenderDispatcher.RenderSection section,
Direction direction, CallbackInfoReturnable<SectionRenderDispatcher.RenderSection> cir) {
if (!this.antarchy$portalGraph) {
return;
}
SectionRenderDispatcher.RenderSection relative = cir.getReturnValue();
if (relative != null && !relative.getOrigin().equals(section.getRelativeOrigin(direction))) {
cir.setReturnValue(null);
}
}
}
@@ -1,12 +0,0 @@
package com.craisinlord.antarchy.mixins.client;
import java.util.Queue;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(SectionRenderDispatcher.class)
public interface SectionRenderDispatcherPortalUploadsAccessor {
@Accessor("toUpload")
Queue<Runnable> antarchy$getToUpload();
}
@@ -1,19 +0,0 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.renderer.SodiumCompat;
import net.minecraft.client.Camera;
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.Coerce;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Pseudo
@Mixin(targets = "net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer", remap = false)
public abstract class SodiumWorldRendererMixin {
@Inject(method = "setupTerrain", at = @At("HEAD"), remap = false)
private void antarchy$captureTerrainSetup(Camera camera, @Coerce Object viewport, boolean spectator, boolean updateChunks, CallbackInfo ci) {
SodiumCompat.captureTerrainSetup(camera, viewport, spectator, updateChunks);
}
}
@@ -0,0 +1,32 @@
package com.craisinlord.antarchy.mixins.compat.sodium;
import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
import net.caffeinemc.mods.sodium.client.render.chunk.lists.SortedRenderLists;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior;
import net.caffeinemc.mods.sodium.client.render.chunk.tree.RemovableMultiForest;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(value = RenderSectionManager.class, remap = false)
public interface RenderSectionManagerAccessor {
@Accessor(value = "renderLists", remap = false)
SortedRenderLists antarchy$getRenderLists();
@Accessor(value = "renderLists", remap = false)
void antarchy$setRenderLists(SortedRenderLists renderLists);
@Accessor(value = "renderableSectionTree", remap = false)
RemovableMultiForest antarchy$getRenderableSectionTree();
@Accessor(value = "sectionByPosition", remap = false)
Long2ReferenceMap<RenderSection> antarchy$getSectionByPosition();
@Accessor(value = "sortBehavior", remap = false)
SortBehavior antarchy$getSortBehavior();
@Invoker(value = "getSearchDistance", remap = false)
float antarchy$getSearchDistance();
}
@@ -0,0 +1,24 @@
package com.craisinlord.antarchy.mixins.compat.sodium;
import com.craisinlord.antarchy.content.client.renderer.sodium.SodiumPortalTerrain;
import java.util.ArrayDeque;
import java.util.Map;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
import net.caffeinemc.mods.sodium.client.render.chunk.TaskQueueType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = RenderSectionManager.class, remap = false)
public abstract class RenderSectionManagerPortalTasksMixin {
@Shadow(remap = false)
private Map<TaskQueueType, ArrayDeque<RenderSection>> taskLists;
@Inject(method = "update", at = @At("TAIL"), remap = false)
private void antarchy$addPortalViewTasks(CallbackInfo ci) {
SodiumPortalTerrain.drainPendingTasks(this.taskLists);
}
}
@@ -0,0 +1,18 @@
package com.craisinlord.antarchy.mixins.compat.sodium;
import net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(value = SodiumWorldRenderer.class, remap = false)
public interface SodiumWorldRendererAccessor {
@Accessor(value = "renderSectionManager", remap = false)
RenderSectionManager antarchy$getRenderSectionManager();
@Accessor(value = "useEntityCulling", remap = false)
boolean antarchy$getUseEntityCulling();
@Accessor(value = "useEntityCulling", remap = false)
void antarchy$setUseEntityCulling(boolean useEntityCulling);
}
@@ -7,6 +7,8 @@
"AbstractMinecartMixin",
"AbstractArrowAccessor",
"PortalGunEntityCollisionMixin",
"PortalGunPlayerMovePacketMixin",
"ServerGamePacketListenerTeleportAccessor",
"PortalGunZombieMixin",
"FallingBlockEntityAccessor",
"AccessorFireBlock",
@@ -142,15 +144,14 @@
"client.CameraBasisAccessor",
"client.LevelRendererPortalViewAreaAccessor",
"client.LevelRendererPortalViewAreaMixin",
"client.LevelRendererPortalChunkBudgetMixin",
"client.SectionRenderDispatcherPortalUploadsAccessor",
"client.LevelRendererPortalLifecycleMixin",
"client.PortalViewChunkCacheMixin",
"client.PortalViewSectionSortingMixin",
"client.LevelRendererPortalTargetsAccessor",
"client.LevelRendererPortalSceneInvoker",
"client.SectionOcclusionGraphPortalMixin",
"client.MinecraftPortalTransparencyMixin",
"client.EntityRenderDispatcherPortalMixin",
"client.BlockEntityRenderDispatcherPortalMixin",
"client.MinecraftMainRenderTargetAccessor",
"client.PostChainTargetAccessor",
"client.AbstractMinecartRendererMixin",
"client.EntityLightProbeMixin",
"client.CustomBossBarMixin",
@@ -4,7 +4,9 @@
"plugin": "com.craisinlord.antarchy.mixins.AntarchyMixinPlugin",
"compatibilityLevel": "JAVA_21",
"client": [
"client.SodiumWorldRendererMixin"
"compat.sodium.SodiumWorldRendererAccessor",
"compat.sodium.RenderSectionManagerAccessor",
"compat.sodium.RenderSectionManagerPortalTasksMixin"
],
"injectors": {
"defaultRequire": 1
Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B