portal guns third pass

This commit is contained in:
craisinlord committed 2026-10-04 19:45:11 -05:00
1 parent be9b18c4f5
commit 39688c70ad
13 files changed
+344 -115

No files matched your search

@@ -1,6 +1,7 @@
package com.craisinlord.antarchy.content.client; package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape; import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import com.craisinlord.antarchy.content.portalgun.PortalGunApertureCuller;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.UUID; import java.util.UUID;
@@ -12,6 +13,7 @@ public final class PortalGunPortalRenderState {
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_DESTINATION_SHAPES = ThreadLocal.withInitial(ArrayDeque::new); private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_DESTINATION_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_SOURCE_SHAPES = ThreadLocal.withInitial(ArrayDeque::new); private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_SOURCE_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalRenderContext>> ACTIVE_CONTEXTS = ThreadLocal.withInitial(ArrayDeque::new); private static final ThreadLocal<Deque<PortalRenderContext>> ACTIVE_CONTEXTS = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalGunApertureCuller>> ACTIVE_CULLERS = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Integer> RENDER_ALL_DEPTH = ThreadLocal.withInitial(() -> 0); private static final ThreadLocal<Integer> RENDER_ALL_DEPTH = ThreadLocal.withInitial(() -> 0);
private PortalGunPortalRenderState() { private PortalGunPortalRenderState() {
@@ -27,6 +29,7 @@ public final class PortalGunPortalRenderState {
public static void pushPortalView(PortalRenderContext context) { public static void pushPortalView(PortalRenderContext context) {
ACTIVE_CONTEXTS.get().push(context); ACTIVE_CONTEXTS.get().push(context);
ACTIVE_CULLERS.get().push(PortalGunApertureCuller.create(context.destinationShape(), context.cameraPos()));
pushPortalView(context.sourceShape(), context.destinationShape(), context.renderAll()); pushPortalView(context.sourceShape(), context.destinationShape(), context.renderAll());
} }
@@ -60,6 +63,13 @@ public final class PortalGunPortalRenderState {
if (contexts.isEmpty()) { if (contexts.isEmpty()) {
ACTIVE_CONTEXTS.remove(); ACTIVE_CONTEXTS.remove();
} }
Deque<PortalGunApertureCuller> cullers = ACTIVE_CULLERS.get();
if (!cullers.isEmpty()) {
cullers.pop();
}
if (cullers.isEmpty()) {
ACTIVE_CULLERS.remove();
}
} }
public static void pushDestinationShape(PortalGunWorldPortalShape shape) { public static void pushDestinationShape(PortalGunWorldPortalShape shape) {
@@ -109,7 +119,11 @@ public final class PortalGunPortalRenderState {
PortalGunWorldPortalShape sourceShape = getSourceShape(); PortalGunWorldPortalShape sourceShape = getSourceShape();
return destinationShape == null || shouldRenderBounds(bounds, sourceShape, destinationShape, false); return destinationShape == null || shouldRenderBounds(bounds, sourceShape, destinationShape, false);
} }
return shouldRenderBounds(bounds, context.sourceShape(), context.destinationShape(), context.renderAll()); if (!context.destinationShape().intersectsFront(bounds, 0.125D)) {
return false;
}
Deque<PortalGunApertureCuller> cullers = ACTIVE_CULLERS.get();
return cullers.isEmpty() || cullers.peek().intersects(bounds);
} }
private static boolean shouldRenderBounds(AABB bounds, PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) { private static boolean shouldRenderBounds(AABB bounds, PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
@@ -119,10 +133,7 @@ public final class PortalGunPortalRenderState {
if (!destinationShape.intersectsFront(bounds, 0.125D)) { if (!destinationShape.intersectsFront(bounds, 0.125D)) {
return false; return false;
} }
if (!destinationShape.intersectsPortalColumn(bounds, 1.5D, 1.5D, destinationShape.halfDepth() + 96.0D)) { return true;
return false;
}
return sourceShape == null || !sourceShape.intersectsFront(bounds, -0.02D);
} }
public record PortalRenderContext( public record PortalRenderContext(
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.client.renderer; package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.mixins.client.SectionRenderDispatcherPortalUploadsAccessor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Iterator; import java.util.Iterator;
@@ -17,12 +18,15 @@ import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
public final class PortalGunPortalRendererPool { public final class PortalGunPortalRendererPool {
private static final int MAX_RENDERERS = 8; private static final int MAX_RENDERERS = 8;
private static final long PORTAL_SECTION_BUDGET_NANOS = 4_166_666L; 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 RenderBuffers PORTAL_RENDER_BUFFERS = new RenderBuffers(2);
private static final LinkedHashMap<UUID, PortalRenderer> RENDERERS = new LinkedHashMap<>(8, 0.75F, true); 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 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<Long> SECTION_BUDGET_DEADLINE = ThreadLocal.withInitial(() -> 0L);
private static final ThreadLocal<Integer> SCHEDULED_SECTION_REBUILDS = ThreadLocal.withInitial(() -> 0); 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<Boolean> SKIP_SET_NOT_DIRTY = ThreadLocal.withInitial(() -> false);
private static final ThreadLocal<Integer> UPLOADED_THIS_FRAME = ThreadLocal.withInitial(() -> 0);
private static ClientLevel ownerLevel; private static ClientLevel ownerLevel;
private PortalGunPortalRendererPool() { private PortalGunPortalRendererPool() {
@@ -66,12 +70,39 @@ public final class PortalGunPortalRendererPool {
SECTION_BUDGET_DEADLINE.set(System.nanoTime() + PORTAL_SECTION_BUDGET_NANOS); SECTION_BUDGET_DEADLINE.set(System.nanoTime() + PORTAL_SECTION_BUDGET_NANOS);
SCHEDULED_SECTION_REBUILDS.set(0); SCHEDULED_SECTION_REBUILDS.set(0);
SKIP_SET_NOT_DIRTY.set(false); 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() { public static int scheduledSectionRebuilds() {
return SCHEDULED_SECTION_REBUILDS.get(); return SCHEDULED_SECTION_REBUILDS.get();
} }
public static int uploadedThisFrame() {
return UPLOADED_THIS_FRAME.get();
}
public static long remainingSectionBudgetNanos() { public static long remainingSectionBudgetNanos() {
return Math.max(0L, SECTION_BUDGET_DEADLINE.get() - System.nanoTime()); return Math.max(0L, SECTION_BUDGET_DEADLINE.get() - System.nanoTime());
} }
@@ -126,6 +157,10 @@ public final class PortalGunPortalRendererPool {
} }
RENDERERS.clear(); RENDERERS.clear();
ownerLevel = null; ownerLevel = null;
SECTION_BUDGET_DEADLINE.remove();
SCHEDULED_SECTION_REBUILDS.remove();
SKIP_SET_NOT_DIRTY.remove();
UPLOADED_THIS_FRAME.remove();
} }
private static void trim() { private static void trim() {
@@ -8,6 +8,7 @@ import com.craisinlord.antarchy.mixins.client.MinecraftMainRenderTargetAccessor;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalTargetsAccessor; import com.craisinlord.antarchy.mixins.client.LevelRendererPortalTargetsAccessor;
import com.craisinlord.antarchy.mixins.client.PostChainTargetAccessor; import com.craisinlord.antarchy.mixins.client.PostChainTargetAccessor;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil; import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectionUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity; import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape; import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import com.craisinlord.antarchy.mixins.client.CameraBasisAccessor; import com.craisinlord.antarchy.mixins.client.CameraBasisAccessor;
@@ -129,13 +130,6 @@ public final class PortalGunPortalViewRenderer {
if (portals.isEmpty()) { if (portals.isEmpty()) {
return; return;
} }
if (portalViewShader == null) {
if (!loggedMissingPortalViewShader) {
loggedMissingPortalViewShader = true;
Antarchy.LOGGER.error("Portal gun destination view shader is unavailable; skipping destination render");
}
return;
}
Vec3 effectiveCameraPos = cameraPos; Vec3 effectiveCameraPos = cameraPos;
Matrix4f rootProjectionMatrix = new Matrix4f(projectionMatrix); Matrix4f rootProjectionMatrix = new Matrix4f(projectionMatrix);
float partialTick = tickCounter.getGameTimeDeltaPartialTick(true); float partialTick = tickCounter.getGameTimeDeltaPartialTick(true);
@@ -201,6 +195,13 @@ public final class PortalGunPortalViewRenderer {
} }
RenderContext rootContext = new RenderContext(effectiveCameraPos, rootLook, rootUp, rootViewMatrix); RenderContext rootContext = new RenderContext(effectiveCameraPos, rootLook, rootUp, rootViewMatrix);
boolean directStencil = PortalStencilTarget.supportsDirect(minecraft.getMainRenderTarget()); boolean directStencil = PortalStencilTarget.supportsDirect(minecraft.getMainRenderTarget());
if (!directStencil && portalViewShader == null) {
if (!loggedMissingPortalViewShader) {
loggedMissingPortalViewShader = true;
Antarchy.LOGGER.error("Portal gun destination view shader is unavailable; skipping offscreen portal rendering");
}
return;
}
PortalViewBudget fallbackBudget = directStencil ? null : new PortalViewBudget(MAX_PORTAL_VIEWS_PER_FRAME); PortalViewBudget fallbackBudget = directStencil ? null : new PortalViewBudget(MAX_PORTAL_VIEWS_PER_FRAME);
renderingPortalView = true; renderingPortalView = true;
boolean projectionBackedUp = false; boolean projectionBackedUp = false;
@@ -212,18 +213,23 @@ public final class PortalGunPortalViewRenderer {
if (linkedPortal == null || !linkedPortal.isAlive()) { if (linkedPortal == null || !linkedPortal.isAlive()) {
continue; continue;
} }
String portalPairKey = portal.getUUID() + "->" + linkedPortal.getUUID();
boolean rendered; boolean rendered;
try { try {
if (directStencil) { try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey,
rendered = renderPortalSurfaceDirect(minecraft, cameraEntity, tickCounter, rootContext, rootProjectionMatrix, portal, linkedPortal, rootScreenClips.get(portal)); "root-view-dispatch", "backend=" + (directStencil ? "direct-stencil" : "offscreen")
} else { + " aperture=" + rootScreenClips.get(portal).width() + "x" + rootScreenClips.get(portal).height())) {
int textureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, fallbackBudget, rootContext, if (directStencil) {
rootProjectionMatrix, portal, linkedPortal, 0, rootScreenClips.get(portal)); rendered = renderPortalSurfaceDirect(minecraft, cameraEntity, tickCounter, rootContext, rootProjectionMatrix, portal, linkedPortal, rootScreenClips.get(portal));
minecraft.gameRenderer.resetProjectionMatrix(rootProjectionMatrix); } else {
minecraft.getMainRenderTarget().bindWrite(true); int textureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, fallbackBudget, rootContext,
rendered = textureId >= 0 && drawPortalQuad(cameraPos, rootViewMatrix, rootProjectionMatrix, rootProjectionMatrix, portal, linkedPortal, 0, rootScreenClips.get(portal));
minecraft.getMainRenderTarget().width, minecraft.getMainRenderTarget().height, minecraft.gameRenderer.resetProjectionMatrix(rootProjectionMatrix);
portal, partialTick, textureId, rootScreenClips.get(portal)); minecraft.getMainRenderTarget().bindWrite(true);
rendered = textureId >= 0 && drawPortalQuad(cameraPos, rootViewMatrix, rootProjectionMatrix,
minecraft.getMainRenderTarget().width, minecraft.getMainRenderTarget().height,
portal, partialTick, textureId, rootScreenClips.get(portal));
}
} }
} catch (RuntimeException exception) { } catch (RuntimeException exception) {
logRenderFailure(minecraft, exception); logRenderFailure(minecraft, exception);
@@ -241,7 +247,6 @@ public final class PortalGunPortalViewRenderer {
RenderSystem.backupProjectionMatrix(); RenderSystem.backupProjectionMatrix();
projectionBackedUp = true; projectionBackedUp = true;
minecraft.getMainRenderTarget().bindWrite(true); minecraft.getMainRenderTarget().bindWrite(true);
String portalPairKey = portal.getUUID() + "->" + linkedPortal.getUUID();
if (loggedPortalViewComposites.add(portalPairKey)) { if (loggedPortalViewComposites.add(portalPairKey)) {
Antarchy.LOGGER.info("Portal gun destination view rendered directly source={} destination={} target={}x{}", portal.getId(), linkedPortal.getId(), targetWidth, targetHeight); Antarchy.LOGGER.info("Portal gun destination view rendered directly source={} destination={} target={}x{}", portal.getId(), linkedPortal.getId(), targetWidth, targetHeight);
} }
@@ -358,7 +363,7 @@ public final class PortalGunPortalViewRenderer {
ScreenClip clip ScreenClip clip
) { ) {
RenderTarget target = minecraft.getMainRenderTarget(); RenderTarget target = minecraft.getMainRenderTarget();
if (clip == null || portalViewShader == null) { if (clip == null) {
return false; return false;
} }
int width = target.width; int width = target.width;
@@ -370,17 +375,18 @@ public final class PortalGunPortalViewRenderer {
PortalCamera portalCamera = new PortalCamera(); PortalCamera portalCamera = new PortalCamera();
portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformedContext.cameraPos(), transformedContext.look(), transformedContext.up()); portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformedContext.cameraPos(), transformedContext.look(), transformedContext.up());
String portalPairKey = sourcePortal.getUUID() + "->" + destinationPortal.getUUID(); String portalPairKey = sourcePortal.getUUID() + "->" + destinationPortal.getUUID();
LevelRenderer proxyRenderer = PortalGunPortalRendererPool.acquire(minecraft, destinationPortal.getUUID(), width, height); boolean renderDestination = PortalGunPortalVisibilityQueries.shouldRender(sourcePortal.getUUID(), destinationPortal.getUUID());
LevelRendererPortalTargetsAccessor levelRendererTargets = (LevelRendererPortalTargetsAccessor) proxyRenderer; LevelRenderer proxyRenderer = renderDestination
PostChain entityEffect = levelRendererTargets.antarchy$getEntityEffect(); ? PortalGunPortalRendererPool.acquire(minecraft, destinationPortal.getUUID(), width, height) : null;
PostChain transparencyChain = levelRendererTargets.antarchy$getTransparencyChain(); LevelRendererPortalTargetsAccessor levelRendererTargets = proxyRenderer == null ? null : (LevelRendererPortalTargetsAccessor) proxyRenderer;
PostChain entityEffect = levelRendererTargets == null ? null : levelRendererTargets.antarchy$getEntityEffect();
PostChain transparencyChain = levelRendererTargets == null ? null : levelRendererTargets.antarchy$getTransparencyChain();
PostChainTargetAccessor entityEffectTarget = entityEffect == null ? null : (PostChainTargetAccessor) entityEffect; PostChainTargetAccessor entityEffectTarget = entityEffect == null ? null : (PostChainTargetAccessor) entityEffect;
PostChainTargetAccessor transparencyTarget = transparencyChain == null ? null : (PostChainTargetAccessor) transparencyChain; PostChainTargetAccessor transparencyTarget = transparencyChain == null ? null : (PostChainTargetAccessor) transparencyChain;
RenderTarget previousEntityEffectTarget = entityEffectTarget == null ? null : entityEffectTarget.antarchy$getScreenTarget(); RenderTarget previousEntityEffectTarget = entityEffectTarget == null ? null : entityEffectTarget.antarchy$getScreenTarget();
RenderTarget previousTransparencyTarget = transparencyTarget == null ? null : transparencyTarget.antarchy$getScreenTarget(); RenderTarget previousTransparencyTarget = transparencyTarget == null ? null : transparencyTarget.antarchy$getScreenTarget();
PortalStencilTarget.Scope stencilScope = null; PortalStencilTarget.Scope stencilScope = null;
boolean contextPushed = false; boolean contextPushed = false;
boolean renderDestination = PortalGunPortalVisibilityQueries.shouldRender(sourcePortal.getUUID(), destinationPortal.getUUID());
PortalSceneRenderTrace.event(portalPairKey, "destination-render-decision", "render=" + renderDestination + " clip=" + clip.width() + "x" + clip.height()); PortalSceneRenderTrace.event(portalPairKey, "destination-render-decision", "render=" + renderDestination + " clip=" + clip.width() + "x" + clip.height());
try { try {
if (entityEffectTarget != null) { if (entityEffectTarget != null) {
@@ -390,12 +396,12 @@ public final class PortalGunPortalViewRenderer {
transparencyTarget.antarchy$setScreenTarget(target); transparencyTarget.antarchy$setScreenTarget(target);
} }
minecraft.gameRenderer.resetProjectionMatrix(clippedProjection); minecraft.gameRenderer.resetProjectionMatrix(clippedProjection);
int maskTexture = minecraft.getTextureManager().getTexture(BLUE_OVERLAY).getId();
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "direct-stencil-mask", "clip=" + clip.width() + "x" + clip.height())) { try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "direct-stencil-mask", "clip=" + clip.width() + "x" + clip.height())) {
stencilScope = PortalStencilTarget.beginDirect(target, () -> { stencilScope = PortalStencilTarget.beginDirect(target, () -> {
boolean queryStarted = PortalGunPortalVisibilityQueries.begin(sourcePortal.getUUID(), destinationPortal.getUUID()); boolean queryStarted = PortalGunPortalVisibilityQueries.begin(sourcePortal.getUUID(), destinationPortal.getUUID());
try { try {
drawPortalQuad(currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, width, height, sourcePortal, partialTick, maskTexture, clip); drawPortalMask(minecraft, currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix,
sourcePortal, partialTick);
} finally { } finally {
PortalGunPortalVisibilityQueries.end(sourcePortal.getUUID(), destinationPortal.getUUID(), queryStarted); PortalGunPortalVisibilityQueries.end(sourcePortal.getUUID(), destinationPortal.getUUID(), queryStarted);
} }
@@ -430,22 +436,37 @@ public final class PortalGunPortalViewRenderer {
return true; return true;
} finally { } finally {
try { try {
if (stencilScope != null) { try {
stencilScope.close(); if (stencilScope != null) {
} stencilScope.close();
if (contextPushed) { }
PortalGunPortalRenderState.popPortalView(false); } finally {
if (contextPushed) {
PortalGunPortalRenderState.popPortalView(false);
}
} }
} finally { } finally {
if (transparencyTarget != null) { try {
transparencyTarget.antarchy$setScreenTarget(previousTransparencyTarget); if (transparencyTarget != null) {
transparencyTarget.antarchy$setScreenTarget(previousTransparencyTarget);
}
} finally {
try {
if (entityEffectTarget != null) {
entityEffectTarget.antarchy$setScreenTarget(previousEntityEffectTarget);
}
} finally {
try {
restorePortalDrawState();
} finally {
try {
target.bindWrite(true);
} finally {
minecraft.gameRenderer.resetProjectionMatrix(baseProjectionMatrix);
}
}
}
} }
if (entityEffectTarget != null) {
entityEffectTarget.antarchy$setScreenTarget(previousEntityEffectTarget);
}
restorePortalDrawState();
target.bindWrite(true);
minecraft.gameRenderer.resetProjectionMatrix(baseProjectionMatrix);
} }
} }
} }
@@ -482,7 +503,9 @@ public final class PortalGunPortalViewRenderer {
} }
Matrix4f clippedProjectionMatrix = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal); Matrix4f clippedProjectionMatrix = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal);
Matrix4f apertureCullProjection = cropProjectionToScreenClip(clippedProjectionMatrix, clip, target.width, target.height); Matrix4f apertureCullProjection = cropProjectionToScreenClip(clippedProjectionMatrix, clip, target.width, target.height);
boolean renderAll = true; // The offscreen target is still composited only through this portal's aperture,
// so use the same conservative destination bounds filter as the direct path.
boolean renderAll = false;
PortalCamera portalCamera = new PortalCamera(); PortalCamera portalCamera = new PortalCamera();
portalCamera.setup(minecraft.level, cameraEntity, tickCounter.getGameTimeDeltaPartialTick(true), transformedContext.cameraPos(), transformedContext.look(), transformedContext.up()); portalCamera.setup(minecraft.level, cameraEntity, tickCounter.getGameTimeDeltaPartialTick(true), transformedContext.cameraPos(), transformedContext.look(), transformedContext.up());
RenderTarget previousMainTarget = minecraft.getMainRenderTarget(); RenderTarget previousMainTarget = minecraft.getMainRenderTarget();
@@ -603,11 +626,14 @@ public final class PortalGunPortalViewRenderer {
} finally { } finally {
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "restore-render-state", "depth=" + depth)) { try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "restore-render-state", "depth=" + depth)) {
try { try {
if (stencilScope != null) { try {
stencilScope.close(); if (stencilScope != null) {
} stencilScope.close();
if (portalRenderStatePushed) { }
PortalGunPortalRenderState.popPortalView(renderAll); } finally {
if (portalRenderStatePushed) {
PortalGunPortalRenderState.popPortalView(renderAll);
}
} }
} finally { } finally {
try { try {
@@ -635,11 +661,13 @@ public final class PortalGunPortalViewRenderer {
} }
private static RenderContext transformContext(RenderContext currentContext, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) { private static RenderContext transformContext(RenderContext currentContext, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) {
Vec3 relativeEye = currentContext.cameraPos().subtract(sourcePortal.position()); var sourceShape = sourcePortal.getWorldPortalShape();
Vec3 transformedEyeOffset = PortalGunTransformUtil.transformRelativePosition(sourcePortal, destinationPortal, relativeEye); var destinationShape = destinationPortal.getWorldPortalShape();
Vec3 destinationEye = destinationPortal.position().add(transformedEyeOffset); Vec3 relativeEye = currentContext.cameraPos().subtract(sourceShape.center());
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, currentContext.look()).normalize(); Vec3 transformedEyeOffset = PortalGunTransformUtil.transformPosition(sourceShape, destinationShape, relativeEye);
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, currentContext.up()).normalize(); Vec3 destinationEye = destinationShape.center().add(transformedEyeOffset);
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourceShape, destinationShape, currentContext.look()).normalize();
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourceShape, destinationShape, currentContext.up()).normalize();
Matrix4f viewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(transformedLook, transformedUp).conjugate(new Quaternionf())); Matrix4f viewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(transformedLook, transformedUp).conjugate(new Quaternionf()));
return new RenderContext(destinationEye, transformedLook, transformedUp, viewMatrix); return new RenderContext(destinationEye, transformedLook, transformedUp, viewMatrix);
} }
@@ -647,51 +675,8 @@ public final class PortalGunPortalViewRenderer {
private static Matrix4f applyPortalClipPlane(Matrix4f projectionMatrix, RenderContext renderContext, PortalGunPortalEntity destinationPortal) { private static Matrix4f applyPortalClipPlane(Matrix4f projectionMatrix, RenderContext renderContext, PortalGunPortalEntity destinationPortal) {
Vec3 planePointWorld = destinationPortal.position().add(destinationPortal.getNormalVec().normalize().scale(0.01D)); Vec3 planePointWorld = destinationPortal.position().add(destinationPortal.getNormalVec().normalize().scale(0.01D));
Vec3 planeNormalWorld = destinationPortal.getNormalVec().normalize(); Vec3 planeNormalWorld = destinationPortal.getNormalVec().normalize();
Vector4f clipPlane = portalPlaneToCameraSpace(renderContext, planePointWorld, planeNormalWorld); return PortalGunProjectionUtil.clipAtExit(projectionMatrix, renderContext.cameraPos(), renderContext.look(),
if (clipPlane.lengthSquared() <= 1.0E-6F) { renderContext.up(), planePointWorld, planeNormalWorld);
return projectionMatrix;
}
Matrix4f clippedProjection = new Matrix4f(projectionMatrix);
Vector4f q = new Vector4f(signNonZero(clipPlane.x()), signNonZero(clipPlane.y()), 1.0F, 1.0F)
.mul(new Matrix4f(projectionMatrix).invert());
float planeCornerDot = clipPlane.dot(q);
if (!Float.isFinite(planeCornerDot) || Math.abs(planeCornerDot) <= 1.0E-6F) {
return projectionMatrix;
}
float scale = 2.0F / planeCornerDot;
Vector4f c = clipPlane.mul(scale, new Vector4f());
clippedProjection.m02(c.x());
clippedProjection.m12(c.y());
clippedProjection.m22(c.z() + 1.0F);
clippedProjection.m32(c.w());
return clippedProjection;
}
private static Vector4f portalPlaneToCameraSpace(RenderContext renderContext, Vec3 planePointWorld, Vec3 planeNormalWorld) {
Vec3 look = renderContext.look().normalize();
Vec3 up = renderContext.up().normalize();
Vec3 right = look.cross(up).normalize();
Vec3 relativePoint = planePointWorld.subtract(renderContext.cameraPos());
Vector4f point = new Vector4f(
(float) relativePoint.dot(right),
(float) relativePoint.dot(up),
(float) -relativePoint.dot(look),
1.0F
);
Vector4f normal = new Vector4f(
(float) planeNormalWorld.dot(right),
(float) planeNormalWorld.dot(up),
(float) -planeNormalWorld.dot(look),
0.0F
);
if (normal.z() > 0.0F) {
normal.mul(-1.0F);
}
return new Vector4f(normal.x(), normal.y(), normal.z(), -(normal.x() * point.x() + normal.y() * point.y() + normal.z() * point.z()));
}
private static float signNonZero(float value) {
return value >= 0.0F ? 1.0F : -1.0F;
} }
private static ScreenClip computeScreenClip(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, PortalGunPortalEntity portal, float partialTick, int width, int height) { private static ScreenClip computeScreenClip(Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, PortalGunPortalEntity portal, float partialTick, int width, int height) {
@@ -836,6 +821,24 @@ public final class PortalGunPortalViewRenderer {
return true; return true;
} }
private static void drawPortalMask(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix,
PortalGunPortalEntity portal, float partialTick) {
PortalGunWorldPortalShape shape = portal.getWorldPortalShape()
.scaleAperture(portal.getPortalVisualScale(partialTick) * PORTAL_APERTURE_SCALE);
Matrix4f combined = new Matrix4f(projectionMatrix).mul(viewMatrix);
List<Vector4f> polygon = projectPortalPolygon(combined, cameraPos, shape);
if (polygon.size() < 3) {
return;
}
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION);
for (int index = 1; index + 1 < polygon.size(); index++) {
addDepthVertex(buffer, polygon.get(0));
addDepthVertex(buffer, polygon.get(index));
addDepthVertex(buffer, polygon.get(index + 1));
}
drawClipSpaceMesh(minecraft, buffer.build());
}
private static void clearDepthInPortalStencil(Minecraft minecraft) { private static void clearDepthInPortalStencil(Minecraft minecraft) {
int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC); int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC);
RenderSystem.enableDepthTest(); RenderSystem.enableDepthTest();
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.client.renderer; 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.LevelRendererPortalSceneInvoker;
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor; import com.craisinlord.antarchy.mixins.client.LevelRendererPortalViewAreaAccessor;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
@@ -39,6 +40,7 @@ public final class PortalGunProxySceneRenderer {
outerModelViewStack.pushMatrix(); outerModelViewStack.pushMatrix();
outerModelViewStack.identity(); outerModelViewStack.identity();
RenderSystem.applyModelViewMatrix(); RenderSystem.applyModelViewMatrix();
ObjectArrayList<SectionRenderDispatcher.RenderSection> originalVisibleSections = null;
try { try {
Vec3 cameraPosition = camera.getPosition(); Vec3 cameraPosition = camera.getPosition();
double cameraX = cameraPosition.x; double cameraX = cameraPosition.x;
@@ -71,8 +73,20 @@ public final class PortalGunProxySceneRenderer {
} }
LevelRendererPortalViewAreaAccessor viewAreaAccessor = sodium ? null : (LevelRendererPortalViewAreaAccessor) renderer; LevelRendererPortalViewAreaAccessor viewAreaAccessor = sodium ? null : (LevelRendererPortalViewAreaAccessor) renderer;
int visibleSectionCount = 0; int visibleSectionCount = 0;
int candidateSectionCount = 0;
if (viewAreaAccessor != null) { if (viewAreaAccessor != null) {
ObjectArrayList<SectionRenderDispatcher.RenderSection> fullVisibleSections = viewAreaAccessor.antarchy$getVisibleSections(); 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<>(); ObjectArrayList<SectionRenderDispatcher.RenderSection> budgetedVisibleSections = new ObjectArrayList<>();
int scannedVisibleSections = 0; int scannedVisibleSections = 0;
while (scannedVisibleSections < fullVisibleSections.size() && PortalGunPortalRendererPool.remainingSectionBudgetNanos() > 0L) { while (scannedVisibleSections < fullVisibleSections.size() && PortalGunPortalRendererPool.remainingSectionBudgetNanos() > 0L) {
@@ -88,7 +102,11 @@ public final class PortalGunProxySceneRenderer {
} }
visibleSectionCount = fullVisibleSections.size(); visibleSectionCount = fullVisibleSections.size();
} }
PortalSceneRenderTrace.event(tracePair, "visible-sections-ready", "renderer=" + (sodium ? "sodium" : "vanilla") + " count=" + visibleSectionCount + " scheduledRebuilds=" + PortalGunPortalRendererPool.scheduledSectionRebuilds()); 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)) { try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain-layers", "sections=" + visibleSectionCount)) {
RenderSystem.enableDepthTest(); RenderSystem.enableDepthTest();
@@ -195,6 +213,9 @@ public final class PortalGunProxySceneRenderer {
} }
} }
} finally { } finally {
if (originalVisibleSections != null) {
((LevelRendererPortalViewAreaAccessor) renderer).antarchy$setVisibleSections(originalVisibleSections);
}
outerModelViewStack.popMatrix(); outerModelViewStack.popMatrix();
RenderSystem.applyModelViewMatrix(); RenderSystem.applyModelViewMatrix();
RenderSystem.setShaderFogStart(previousFogStart); RenderSystem.setShaderFogStart(previousFogStart);
@@ -6,8 +6,8 @@ import java.util.Set;
public final class PortalSceneRenderTrace { public final class PortalSceneRenderTrace {
private static final String DEBUG_PROPERTY = "antarchy.portalGun.debugRender"; private static final String DEBUG_PROPERTY = "antarchy.portalGun.debugRender";
private static final long SLOW_PHASE_NANOS = 100_000_000L;
private static final Set<String> LOGGED_PHASES = new HashSet<>(); private static final Set<String> LOGGED_PHASES = new HashSet<>();
private static final Phase NOOP_PHASE = new Phase(0L, "", "", false, 0L);
private static long generation; private static long generation;
private static String armedKey = ""; private static String armedKey = "";
@@ -29,19 +29,17 @@ public final class PortalSceneRenderTrace {
} }
public static Phase begin(String pair, String phase, String detail) { public static Phase begin(String pair, String phase, String detail) {
if (!Boolean.getBoolean(DEBUG_PROPERTY)) { boolean verbose = Boolean.getBoolean(DEBUG_PROPERTY);
return NOOP_PHASE;
}
long currentGeneration; long currentGeneration;
boolean shouldLog; boolean logStart;
synchronized (PortalSceneRenderTrace.class) { synchronized (PortalSceneRenderTrace.class) {
currentGeneration = generation; currentGeneration = generation;
shouldLog = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":" + phase); logStart = LOGGED_PHASES.add(currentGeneration + ":" + pair + ":" + phase);
} }
if (shouldLog) { if (logStart) {
Antarchy.LOGGER.info("Portal scene trace begin generation={} pair={} phase={} detail={}", currentGeneration, pair, phase, detail); Antarchy.LOGGER.info("Portal scene trace begin generation={} pair={} phase={} detail={}", currentGeneration, pair, phase, detail);
} }
return new Phase(currentGeneration, pair, phase, shouldLog, System.nanoTime()); return new Phase(currentGeneration, pair, phase, logStart, verbose, System.nanoTime());
} }
public static void event(String pair, String event, String detail) { public static void event(String pair, String event, String detail) {
@@ -63,15 +61,17 @@ public final class PortalSceneRenderTrace {
private final long generation; private final long generation;
private final String pair; private final String pair;
private final String phase; private final String phase;
private final boolean logCompletion; private final boolean logStart;
private final boolean verbose;
private final long startNanos; private final long startNanos;
private boolean closed; private boolean closed;
private Phase(long generation, String pair, String phase, boolean logCompletion, long startNanos) { private Phase(long generation, String pair, String phase, boolean logStart, boolean verbose, long startNanos) {
this.generation = generation; this.generation = generation;
this.pair = pair; this.pair = pair;
this.phase = phase; this.phase = phase;
this.logCompletion = logCompletion; this.logStart = logStart;
this.verbose = verbose;
this.startNanos = startNanos; this.startNanos = startNanos;
} }
@@ -81,9 +81,10 @@ public final class PortalSceneRenderTrace {
return; return;
} }
this.closed = true; this.closed = true;
if (this.logCompletion) { long durationNanos = System.nanoTime() - this.startNanos;
if (this.logStart && (this.verbose || durationNanos >= SLOW_PHASE_NANOS)) {
Antarchy.LOGGER.info("Portal scene trace complete generation={} pair={} phase={} durationMs={}", Antarchy.LOGGER.info("Portal scene trace complete generation={} pair={} phase={} durationMs={}",
this.generation, this.pair, this.phase, (System.nanoTime() - this.startNanos) / 1_000_000.0D); this.generation, this.pair, this.phase, durationNanos / 1_000_000.0D);
} }
} }
} }
@@ -0,0 +1,63 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
/**
* Conservative side planes through the destination opening and virtual eye.
* The exit plane itself is handled separately so bounds crossing it remain visible.
*/
public final class PortalGunApertureCuller {
private static final double MIN_EYE_DISTANCE = 0.05D;
private static final double PLANE_PADDING = 0.5D;
private final Vec3 eye;
private final Vec3[] inwardNormals;
private PortalGunApertureCuller(Vec3 eye, Vec3[] inwardNormals) {
this.eye = eye;
this.inwardNormals = inwardNormals;
}
public static PortalGunApertureCuller create(PortalGunWorldPortalShape aperture, Vec3 eye) {
double eyeDepth = aperture.localCoords(eye).depth();
if (eyeDepth >= -MIN_EYE_DISTANCE) {
return new PortalGunApertureCuller(eye, null);
}
Vec3[] corners = aperture.getCorners(0.0D);
Vec3 centerRay = aperture.center().subtract(eye);
Vec3[] normals = new Vec3[corners.length];
for (int index = 0; index < corners.length; index++) {
Vec3 first = corners[index].subtract(eye);
Vec3 second = corners[(index + 1) % corners.length].subtract(eye);
Vec3 normal = first.cross(second);
if (normal.lengthSqr() < 1.0E-8D) {
return new PortalGunApertureCuller(eye, null);
}
normal = normal.normalize();
if (normal.dot(centerRay) < 0.0D) {
normal = normal.scale(-1.0D);
}
normals[index] = normal;
}
return new PortalGunApertureCuller(eye, normals);
}
public boolean intersects(AABB bounds) {
if (inwardNormals == null) {
return true;
}
for (Vec3 normal : inwardNormals) {
double supportX = normal.x >= 0.0D ? bounds.maxX : bounds.minX;
double supportY = normal.y >= 0.0D ? bounds.maxY : bounds.minY;
double supportZ = normal.z >= 0.0D ? bounds.maxZ : bounds.minZ;
double maxDistance = (supportX - eye.x) * normal.x
+ (supportY - eye.y) * normal.y
+ (supportZ - eye.z) * normal.z;
if (maxDistance < -PLANE_PADDING) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,60 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Vector4f;
/** Applies the destination portal plane as the near plane of a camera projection. */
public final class PortalGunProjectionUtil {
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) {
return projection;
}
Vector4f q = new Vector4f(signNonZero(clipPlane.x()), signNonZero(clipPlane.y()), 1.0F, 1.0F)
.mul(new Matrix4f(projection).invert());
float planeCornerDot = clipPlane.dot(q);
if (!Float.isFinite(planeCornerDot) || Math.abs(planeCornerDot) <= 1.0E-6F) {
return projection;
}
float scale = 2.0F / planeCornerDot;
Vector4f c = clipPlane.mul(scale, new Vector4f());
Matrix4f clipped = new Matrix4f(projection);
clipped.m02(c.x());
clipped.m12(c.y());
clipped.m22(c.z() + 1.0F);
clipped.m32(c.w());
return clipped;
}
private static Vector4f planeInCameraSpace(Vec3 eye, Vec3 look, Vec3 up, Vec3 planePoint, Vec3 planeNormal) {
Vec3 forward = look.normalize();
Vec3 correctedUp = up.subtract(forward.scale(up.dot(forward))).normalize();
Vec3 right = forward.cross(correctedUp).normalize();
Vec3 relativePoint = planePoint.subtract(eye);
Vector4f point = new Vector4f(
(float) relativePoint.dot(right),
(float) relativePoint.dot(correctedUp),
(float) -relativePoint.dot(forward),
1.0F
);
Vector4f normal = new Vector4f(
(float) planeNormal.dot(right),
(float) planeNormal.dot(correctedUp),
(float) -planeNormal.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));
}
private static float signNonZero(float value) {
return value >= 0.0F ? 1.0F : -1.0F;
}
}
@@ -65,6 +65,16 @@ public final class PortalGunTransformUtil {
.add(destinationOut.scale(vector.dot(sourceInto))); .add(destinationOut.scale(vector.dot(sourceInto)));
} }
public static Vec3 transformPosition(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 relativePosition) {
return transformVector(source, destination, relativePosition);
}
public static Vec3 transformVector(PortalGunWorldPortalShape source, PortalGunWorldPortalShape destination, Vec3 vector) {
return destination.right().scale(-vector.dot(source.right()))
.add(destination.up().scale(vector.dot(source.up())))
.add(destination.normal().scale(-vector.dot(source.normal())));
}
public static float yawFromLook(Vec3 look) { public static float yawFromLook(Vec3 look) {
return (float) Math.toDegrees(Math.atan2(-look.x, look.z)); return (float) Math.toDegrees(Math.atan2(-look.x, look.z));
} }
@@ -33,6 +33,7 @@ public final class AntarchyMixinPlugin implements IMixinConfigPlugin {
&& (mixinClassName.endsWith(".LevelRendererPortalViewAreaAccessor") && (mixinClassName.endsWith(".LevelRendererPortalViewAreaAccessor")
|| mixinClassName.endsWith(".LevelRendererPortalViewAreaMixin") || mixinClassName.endsWith(".LevelRendererPortalViewAreaMixin")
|| mixinClassName.endsWith(".LevelRendererPortalChunkBudgetMixin") || mixinClassName.endsWith(".LevelRendererPortalChunkBudgetMixin")
|| mixinClassName.endsWith(".SectionRenderDispatcherPortalUploadsAccessor")
|| mixinClassName.endsWith(".PortalViewChunkCacheMixin") || mixinClassName.endsWith(".PortalViewChunkCacheMixin")
|| mixinClassName.endsWith(".PortalViewSectionSortingMixin"))) { || mixinClassName.endsWith(".PortalViewSectionSortingMixin"))) {
return false; return false;
@@ -12,6 +12,14 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
public abstract class LevelRendererPortalChunkBudgetMixin { 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( @Redirect(
method = "renderLevel", method = "renderLevel",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;pollLightUpdates()V") at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;pollLightUpdates()V")
@@ -16,6 +16,9 @@ import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
public interface LevelRendererPortalViewAreaAccessor { public interface LevelRendererPortalViewAreaAccessor {
@Accessor("sectionRenderDispatcher")
SectionRenderDispatcher antarchy$getSectionRenderDispatcher();
@Accessor("viewArea") @Accessor("viewArea")
ViewArea antarchy$getViewArea(); ViewArea antarchy$getViewArea();
@@ -0,0 +1,12 @@
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();
}
@@ -143,6 +143,7 @@
"client.LevelRendererPortalViewAreaAccessor", "client.LevelRendererPortalViewAreaAccessor",
"client.LevelRendererPortalViewAreaMixin", "client.LevelRendererPortalViewAreaMixin",
"client.LevelRendererPortalChunkBudgetMixin", "client.LevelRendererPortalChunkBudgetMixin",
"client.SectionRenderDispatcherPortalUploadsAccessor",
"client.LevelRendererPortalLifecycleMixin", "client.LevelRendererPortalLifecycleMixin",
"client.PortalViewChunkCacheMixin", "client.PortalViewChunkCacheMixin",
"client.PortalViewSectionSortingMixin", "client.PortalViewSectionSortingMixin",