portal guns third pass
This commit is contained in:
1 parent
be9b18c4f5
commit
39688c70ad
13 files changed
+316
-87
No files matched your search
+16
-5
@@ -1,6 +1,7 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
|
||||
import com.craisinlord.antarchy.content.portalgun.PortalGunApertureCuller;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
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_SOURCE_SHAPES = 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 PortalGunPortalRenderState() {
|
||||
@@ -27,6 +29,7 @@ public final class PortalGunPortalRenderState {
|
||||
|
||||
public static void pushPortalView(PortalRenderContext context) {
|
||||
ACTIVE_CONTEXTS.get().push(context);
|
||||
ACTIVE_CULLERS.get().push(PortalGunApertureCuller.create(context.destinationShape(), context.cameraPos()));
|
||||
pushPortalView(context.sourceShape(), context.destinationShape(), context.renderAll());
|
||||
}
|
||||
|
||||
@@ -60,6 +63,13 @@ public final class PortalGunPortalRenderState {
|
||||
if (contexts.isEmpty()) {
|
||||
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) {
|
||||
@@ -109,7 +119,11 @@ public final class PortalGunPortalRenderState {
|
||||
PortalGunWorldPortalShape sourceShape = getSourceShape();
|
||||
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) {
|
||||
@@ -119,10 +133,7 @@ public final class PortalGunPortalRenderState {
|
||||
if (!destinationShape.intersectsFront(bounds, 0.125D)) {
|
||||
return false;
|
||||
}
|
||||
if (!destinationShape.intersectsPortalColumn(bounds, 1.5D, 1.5D, destinationShape.halfDepth() + 96.0D)) {
|
||||
return false;
|
||||
}
|
||||
return sourceShape == null || !sourceShape.intersectsFront(bounds, -0.02D);
|
||||
return true;
|
||||
}
|
||||
|
||||
public record PortalRenderContext(
|
||||
|
||||
+35
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -17,12 +18,15 @@ 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() {
|
||||
@@ -66,12 +70,39 @@ public final class PortalGunPortalRendererPool {
|
||||
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());
|
||||
}
|
||||
@@ -126,6 +157,10 @@ public final class PortalGunPortalRendererPool {
|
||||
}
|
||||
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() {
|
||||
|
||||
+70
-67
@@ -8,6 +8,7 @@ import com.craisinlord.antarchy.mixins.client.MinecraftMainRenderTargetAccessor;
|
||||
import com.craisinlord.antarchy.mixins.client.LevelRendererPortalTargetsAccessor;
|
||||
import com.craisinlord.antarchy.mixins.client.PostChainTargetAccessor;
|
||||
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.PortalGunWorldPortalShape;
|
||||
import com.craisinlord.antarchy.mixins.client.CameraBasisAccessor;
|
||||
@@ -129,13 +130,6 @@ public final class PortalGunPortalViewRenderer {
|
||||
if (portals.isEmpty()) {
|
||||
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;
|
||||
Matrix4f rootProjectionMatrix = new Matrix4f(projectionMatrix);
|
||||
float partialTick = tickCounter.getGameTimeDeltaPartialTick(true);
|
||||
@@ -201,6 +195,13 @@ public final class PortalGunPortalViewRenderer {
|
||||
}
|
||||
RenderContext rootContext = new RenderContext(effectiveCameraPos, rootLook, rootUp, rootViewMatrix);
|
||||
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);
|
||||
renderingPortalView = true;
|
||||
boolean projectionBackedUp = false;
|
||||
@@ -212,8 +213,12 @@ public final class PortalGunPortalViewRenderer {
|
||||
if (linkedPortal == null || !linkedPortal.isAlive()) {
|
||||
continue;
|
||||
}
|
||||
String portalPairKey = portal.getUUID() + "->" + linkedPortal.getUUID();
|
||||
boolean rendered;
|
||||
try {
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey,
|
||||
"root-view-dispatch", "backend=" + (directStencil ? "direct-stencil" : "offscreen")
|
||||
+ " aperture=" + rootScreenClips.get(portal).width() + "x" + rootScreenClips.get(portal).height())) {
|
||||
if (directStencil) {
|
||||
rendered = renderPortalSurfaceDirect(minecraft, cameraEntity, tickCounter, rootContext, rootProjectionMatrix, portal, linkedPortal, rootScreenClips.get(portal));
|
||||
} else {
|
||||
@@ -225,6 +230,7 @@ public final class PortalGunPortalViewRenderer {
|
||||
minecraft.getMainRenderTarget().width, minecraft.getMainRenderTarget().height,
|
||||
portal, partialTick, textureId, rootScreenClips.get(portal));
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
logRenderFailure(minecraft, exception);
|
||||
RenderSystem.restoreProjectionMatrix();
|
||||
@@ -241,7 +247,6 @@ public final class PortalGunPortalViewRenderer {
|
||||
RenderSystem.backupProjectionMatrix();
|
||||
projectionBackedUp = true;
|
||||
minecraft.getMainRenderTarget().bindWrite(true);
|
||||
String portalPairKey = portal.getUUID() + "->" + linkedPortal.getUUID();
|
||||
if (loggedPortalViewComposites.add(portalPairKey)) {
|
||||
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
|
||||
) {
|
||||
RenderTarget target = minecraft.getMainRenderTarget();
|
||||
if (clip == null || portalViewShader == null) {
|
||||
if (clip == null) {
|
||||
return false;
|
||||
}
|
||||
int width = target.width;
|
||||
@@ -370,17 +375,18 @@ public final class PortalGunPortalViewRenderer {
|
||||
PortalCamera portalCamera = new PortalCamera();
|
||||
portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformedContext.cameraPos(), transformedContext.look(), transformedContext.up());
|
||||
String portalPairKey = sourcePortal.getUUID() + "->" + destinationPortal.getUUID();
|
||||
LevelRenderer proxyRenderer = PortalGunPortalRendererPool.acquire(minecraft, destinationPortal.getUUID(), width, height);
|
||||
LevelRendererPortalTargetsAccessor levelRendererTargets = (LevelRendererPortalTargetsAccessor) proxyRenderer;
|
||||
PostChain entityEffect = levelRendererTargets.antarchy$getEntityEffect();
|
||||
PostChain transparencyChain = levelRendererTargets.antarchy$getTransparencyChain();
|
||||
boolean renderDestination = PortalGunPortalVisibilityQueries.shouldRender(sourcePortal.getUUID(), destinationPortal.getUUID());
|
||||
LevelRenderer proxyRenderer = renderDestination
|
||||
? PortalGunPortalRendererPool.acquire(minecraft, destinationPortal.getUUID(), width, height) : null;
|
||||
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 transparencyTarget = transparencyChain == null ? null : (PostChainTargetAccessor) transparencyChain;
|
||||
RenderTarget previousEntityEffectTarget = entityEffectTarget == null ? null : entityEffectTarget.antarchy$getScreenTarget();
|
||||
RenderTarget previousTransparencyTarget = transparencyTarget == null ? null : transparencyTarget.antarchy$getScreenTarget();
|
||||
PortalStencilTarget.Scope stencilScope = null;
|
||||
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());
|
||||
try {
|
||||
if (entityEffectTarget != null) {
|
||||
@@ -390,12 +396,12 @@ public final class PortalGunPortalViewRenderer {
|
||||
transparencyTarget.antarchy$setScreenTarget(target);
|
||||
}
|
||||
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())) {
|
||||
stencilScope = PortalStencilTarget.beginDirect(target, () -> {
|
||||
boolean queryStarted = PortalGunPortalVisibilityQueries.begin(sourcePortal.getUUID(), destinationPortal.getUUID());
|
||||
try {
|
||||
drawPortalQuad(currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, width, height, sourcePortal, partialTick, maskTexture, clip);
|
||||
drawPortalMask(minecraft, currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix,
|
||||
sourcePortal, partialTick);
|
||||
} finally {
|
||||
PortalGunPortalVisibilityQueries.end(sourcePortal.getUUID(), destinationPortal.getUUID(), queryStarted);
|
||||
}
|
||||
@@ -429,26 +435,41 @@ public final class PortalGunPortalViewRenderer {
|
||||
renderPortalTransitionEntities(minecraft, transformedContext.cameraPos(), partialTick, sourcePortal, destinationPortal);
|
||||
return true;
|
||||
} finally {
|
||||
try {
|
||||
try {
|
||||
if (stencilScope != null) {
|
||||
stencilScope.close();
|
||||
}
|
||||
} finally {
|
||||
if (contextPushed) {
|
||||
PortalGunPortalRenderState.popPortalView(false);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int renderPortalSurface(
|
||||
Minecraft minecraft,
|
||||
@@ -482,7 +503,9 @@ public final class PortalGunPortalViewRenderer {
|
||||
}
|
||||
Matrix4f clippedProjectionMatrix = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal);
|
||||
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.setup(minecraft.level, cameraEntity, tickCounter.getGameTimeDeltaPartialTick(true), transformedContext.cameraPos(), transformedContext.look(), transformedContext.up());
|
||||
RenderTarget previousMainTarget = minecraft.getMainRenderTarget();
|
||||
@@ -602,13 +625,16 @@ public final class PortalGunPortalViewRenderer {
|
||||
}
|
||||
} finally {
|
||||
try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "restore-render-state", "depth=" + depth)) {
|
||||
try {
|
||||
try {
|
||||
if (stencilScope != null) {
|
||||
stencilScope.close();
|
||||
}
|
||||
} finally {
|
||||
if (portalRenderStatePushed) {
|
||||
PortalGunPortalRenderState.popPortalView(renderAll);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
restorePortalDrawState();
|
||||
@@ -635,11 +661,13 @@ public final class PortalGunPortalViewRenderer {
|
||||
}
|
||||
|
||||
private static RenderContext transformContext(RenderContext currentContext, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) {
|
||||
Vec3 relativeEye = currentContext.cameraPos().subtract(sourcePortal.position());
|
||||
Vec3 transformedEyeOffset = PortalGunTransformUtil.transformRelativePosition(sourcePortal, destinationPortal, relativeEye);
|
||||
Vec3 destinationEye = destinationPortal.position().add(transformedEyeOffset);
|
||||
Vec3 transformedLook = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, currentContext.look()).normalize();
|
||||
Vec3 transformedUp = PortalGunTransformUtil.transformVector(sourcePortal, destinationPortal, currentContext.up()).normalize();
|
||||
var sourceShape = sourcePortal.getWorldPortalShape();
|
||||
var destinationShape = destinationPortal.getWorldPortalShape();
|
||||
Vec3 relativeEye = currentContext.cameraPos().subtract(sourceShape.center());
|
||||
Vec3 transformedEyeOffset = PortalGunTransformUtil.transformPosition(sourceShape, destinationShape, relativeEye);
|
||||
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()));
|
||||
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) {
|
||||
Vec3 planePointWorld = destinationPortal.position().add(destinationPortal.getNormalVec().normalize().scale(0.01D));
|
||||
Vec3 planeNormalWorld = destinationPortal.getNormalVec().normalize();
|
||||
Vector4f clipPlane = portalPlaneToCameraSpace(renderContext, planePointWorld, planeNormalWorld);
|
||||
if (clipPlane.lengthSquared() <= 1.0E-6F) {
|
||||
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;
|
||||
return PortalGunProjectionUtil.clipAtExit(projectionMatrix, renderContext.cameraPos(), renderContext.look(),
|
||||
renderContext.up(), planePointWorld, planeNormalWorld);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC);
|
||||
RenderSystem.enableDepthTest();
|
||||
|
||||
+23
-2
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -39,6 +40,7 @@ public final class PortalGunProxySceneRenderer {
|
||||
outerModelViewStack.pushMatrix();
|
||||
outerModelViewStack.identity();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
ObjectArrayList<SectionRenderDispatcher.RenderSection> originalVisibleSections = null;
|
||||
try {
|
||||
Vec3 cameraPosition = camera.getPosition();
|
||||
double cameraX = cameraPosition.x;
|
||||
@@ -71,8 +73,20 @@ public final class PortalGunProxySceneRenderer {
|
||||
}
|
||||
LevelRendererPortalViewAreaAccessor viewAreaAccessor = sodium ? null : (LevelRendererPortalViewAreaAccessor) renderer;
|
||||
int visibleSectionCount = 0;
|
||||
int candidateSectionCount = 0;
|
||||
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<>();
|
||||
int scannedVisibleSections = 0;
|
||||
while (scannedVisibleSections < fullVisibleSections.size() && PortalGunPortalRendererPool.remainingSectionBudgetNanos() > 0L) {
|
||||
@@ -88,7 +102,11 @@ public final class PortalGunProxySceneRenderer {
|
||||
}
|
||||
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)) {
|
||||
RenderSystem.enableDepthTest();
|
||||
@@ -195,6 +213,9 @@ public final class PortalGunProxySceneRenderer {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (originalVisibleSections != null) {
|
||||
((LevelRendererPortalViewAreaAccessor) renderer).antarchy$setVisibleSections(originalVisibleSections);
|
||||
}
|
||||
outerModelViewStack.popMatrix();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
RenderSystem.setShaderFogStart(previousFogStart);
|
||||
|
||||
+14
-13
@@ -6,8 +6,8 @@ import java.util.Set;
|
||||
|
||||
public final class PortalSceneRenderTrace {
|
||||
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 Phase NOOP_PHASE = new Phase(0L, "", "", false, 0L);
|
||||
private static long generation;
|
||||
private static String armedKey = "";
|
||||
|
||||
@@ -29,19 +29,17 @@ public final class PortalSceneRenderTrace {
|
||||
}
|
||||
|
||||
public static Phase begin(String pair, String phase, String detail) {
|
||||
if (!Boolean.getBoolean(DEBUG_PROPERTY)) {
|
||||
return NOOP_PHASE;
|
||||
}
|
||||
boolean verbose = Boolean.getBoolean(DEBUG_PROPERTY);
|
||||
long currentGeneration;
|
||||
boolean shouldLog;
|
||||
boolean logStart;
|
||||
synchronized (PortalSceneRenderTrace.class) {
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
@@ -63,15 +61,17 @@ public final class PortalSceneRenderTrace {
|
||||
private final long generation;
|
||||
private final String pair;
|
||||
private final String phase;
|
||||
private final boolean logCompletion;
|
||||
private final boolean logStart;
|
||||
private final boolean verbose;
|
||||
private final long startNanos;
|
||||
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.pair = pair;
|
||||
this.phase = phase;
|
||||
this.logCompletion = logCompletion;
|
||||
this.logStart = logStart;
|
||||
this.verbose = verbose;
|
||||
this.startNanos = startNanos;
|
||||
}
|
||||
|
||||
@@ -81,9 +81,10 @@ public final class PortalSceneRenderTrace {
|
||||
return;
|
||||
}
|
||||
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={}",
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -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;
|
||||
}
|
||||
}
|
||||
+60
@@ -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;
|
||||
}
|
||||
}
|
||||
+10
@@ -65,6 +65,16 @@ public final class PortalGunTransformUtil {
|
||||
.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) {
|
||||
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(".LevelRendererPortalViewAreaMixin")
|
||||
|| mixinClassName.endsWith(".LevelRendererPortalChunkBudgetMixin")
|
||||
|| mixinClassName.endsWith(".SectionRenderDispatcherPortalUploadsAccessor")
|
||||
|| mixinClassName.endsWith(".PortalViewChunkCacheMixin")
|
||||
|| mixinClassName.endsWith(".PortalViewSectionSortingMixin"))) {
|
||||
return false;
|
||||
|
||||
+8
@@ -12,6 +12,14 @@ 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")
|
||||
|
||||
+3
@@ -16,6 +16,9 @@ import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public interface LevelRendererPortalViewAreaAccessor {
|
||||
@Accessor("sectionRenderDispatcher")
|
||||
SectionRenderDispatcher antarchy$getSectionRenderDispatcher();
|
||||
|
||||
@Accessor("viewArea")
|
||||
ViewArea antarchy$getViewArea();
|
||||
|
||||
|
||||
+12
@@ -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.LevelRendererPortalViewAreaMixin",
|
||||
"client.LevelRendererPortalChunkBudgetMixin",
|
||||
"client.SectionRenderDispatcherPortalUploadsAccessor",
|
||||
"client.LevelRendererPortalLifecycleMixin",
|
||||
"client.PortalViewChunkCacheMixin",
|
||||
"client.PortalViewSectionSortingMixin",
|
||||
|
||||
Reference in new issue
Block a user