From be9b18c4f5524c01835f4fdbf58c9952d4ae4cd7 Mon Sep 17 00:00:00 2001 From: craisinlord Date: Sat, 3 Oct 2026 14:33:44 -0500 Subject: [PATCH] portal guns second pass make more apis public for boogaloo --- .../client/ContractionAfterimages.java | 41 ++- .../renderer/PortalGunPortalRendererPool.java | 12 +- .../PortalGunPortalViewAreaManager.java | 25 +- .../renderer/PortalGunPortalViewRenderer.java | 250 ++++++++++++------ .../PortalGunPortalVisibilityQueries.java | 93 +++++++ .../renderer/PortalGunProxySceneRenderer.java | 102 ++++--- .../renderer/PortalSceneRenderTrace.java | 8 + .../client/renderer/PortalStencilTarget.java | 136 ++++++++-- .../content/client/renderer/SodiumCompat.java | 33 ++- .../TemporalContractionAfterimageLayer.java | 3 +- .../renderer/TemporalContractionGeoLayer.java | 3 +- .../portalgun/PortalGunPortalEntity.java | 51 +++- .../content/time/TimeDilationApi.java | 75 +++++- .../time/TimeDilationEntityAccess.java | 8 + .../content/time/TimeDilationManager.java | 13 +- .../content/time/TimeDilationMath.java | 28 +- .../LevelRendererPortalLifecycleMixin.java | 4 +- .../time/EntityMoveTimeDilationMixin.java | 3 + .../time/EntityTimeDilationStateMixin.java | 23 +- .../profession/computer_scientist.png | Bin 0 -> 861 bytes .../profession/computer_scientist.png.mcmeta | 5 + .../profession/pest_control.png | Bin 0 -> 613 bytes .../profession/pest_control.png.mcmeta | 5 + .../client/AntarchyFabricClientBootstrap.java | 4 +- .../PortalGunPortalViewRenderHandler.java | 2 +- 25 files changed, 730 insertions(+), 197 deletions(-) create mode 100644 common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalVisibilityQueries.java create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/computer_scientist.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/computer_scientist.png.mcmeta create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/pest_control.png create mode 100644 common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/pest_control.png.mcmeta diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ContractionAfterimages.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ContractionAfterimages.java index b842cbf3..a76fcb9a 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ContractionAfterimages.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/ContractionAfterimages.java @@ -8,6 +8,10 @@ import java.util.Deque; import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Function; +import java.util.function.Predicate; import net.minecraft.core.Holder; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.world.effect.MobEffect; @@ -17,6 +21,9 @@ import net.minecraft.world.entity.LivingEntity; public final class ContractionAfterimages { public static final int MAX_SAMPLES = 10; public static final int SAMPLE_LIFETIME_TICKS = 13; + public static final int DEFAULT_COLOR = 0xFFD35A; + private static final List> ACTIVATORS = new CopyOnWriteArrayList<>(); + private static final List> COLOR_PROVIDERS = new CopyOnWriteArrayList<>(); private static final Map> HISTORY = new WeakHashMap<>(); @@ -51,13 +58,45 @@ public final class ContractionAfterimages { return HISTORY.get(entity); } + public static void addActivator(Predicate activator) { + ACTIVATORS.add(activator); + } + + public static void addColorProvider(Function provider) { + COLOR_PROVIDERS.add(provider); + } + + public static int colorFor(Entity entity) { + if (!(entity instanceof LivingEntity living)) { + return DEFAULT_COLOR; + } + for (Function provider : COLOR_PROVIDERS) { + Integer color = provider.apply(living); + if (color != null) { + return color & 0xFFFFFF; + } + } + return DEFAULT_COLOR; + } + public static boolean isActive(LivingEntity living) { Holder holder = RoyalEffectHooks.contractedHolder(); boolean contracted = holder != null && living.hasEffect(holder); boolean tuned = living instanceof net.minecraft.world.entity.player.Player player && TemporalTunerItem.isAvailable(player) && TimeDilationApi.getRate(living) > 1.0D; - return !living.isInvisible() && (contracted || tuned); + if (living.isInvisible()) { + return false; + } + if (contracted || tuned) { + return true; + } + for (Predicate activator : ACTIVATORS) { + if (activator.test(living)) { + return true; + } + } + return false; } public static float fade(Sample sample, int now) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRendererPool.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRendererPool.java index ea0edbc9..27d8e801 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRendererPool.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalRendererPool.java @@ -10,12 +10,14 @@ 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 RenderBuffers PORTAL_RENDER_BUFFERS = new RenderBuffers(2); private static final LinkedHashMap RENDERERS = new LinkedHashMap<>(8, 0.75F, true); private static final Set PROXY_RENDERERS = java.util.Collections.newSetFromMap(new IdentityHashMap<>()); private static final ThreadLocal SECTION_BUDGET_DEADLINE = ThreadLocal.withInitial(() -> 0L); @@ -37,7 +39,7 @@ public final class PortalGunPortalRendererPool { minecraft, minecraft.getEntityRenderDispatcher(), minecraft.getBlockEntityRenderDispatcher(), - minecraft.renderBuffers() + PORTAL_RENDER_BUFFERS ); PROXY_RENDERERS.add(renderer); renderer.setLevel(minecraft.level); @@ -56,13 +58,11 @@ public final class PortalGunPortalRendererPool { return PROXY_RENDERERS.contains(renderer); } - 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); + public static net.minecraft.client.renderer.MultiBufferSource.BufferSource bufferSource() { + return PORTAL_RENDER_BUFFERS.bufferSource(); } - public static void beginSceneBudget() { + 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); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewAreaManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewAreaManager.java index 4c3d6a06..ce72e0a2 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewAreaManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewAreaManager.java @@ -35,6 +35,9 @@ public final class 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) { @@ -42,7 +45,6 @@ public final class PortalGunPortalViewAreaManager { ownerLevel = minecraft.level; ownerViewDistance = viewDistance; } - Scope sodiumScope = SodiumCompat.isLoaded() ? new SodiumScope(SodiumCompat.beginPortalView(portalCamera)) : null; ViewKey viewKey = new ViewKey(renderer, destinationId); ViewState state = VIEW_AREAS.get(viewKey); if (state == null) { @@ -80,7 +82,7 @@ public final class PortalGunPortalViewAreaManager { accessor.antarchy$setLastCameraSectionX(sectionX); accessor.antarchy$setLastCameraSectionY(sectionY); accessor.antarchy$setLastCameraSectionZ(sectionZ); - return sodiumScope == null ? scope : new CombinedScope(scope, sodiumScope); + return scope; } public static boolean routeSectionCompiled(SectionRenderDispatcher.RenderSection section) { @@ -231,25 +233,6 @@ public final class PortalGunPortalViewAreaManager { void close(); } - private static final class CombinedScope implements Scope { - private final Scope viewAreaScope; - private final Scope sodiumScope; - - private CombinedScope(Scope viewAreaScope, Scope sodiumScope) { - this.viewAreaScope = viewAreaScope; - this.sodiumScope = sodiumScope; - } - - @Override - public void close() { - try { - viewAreaScope.close(); - } finally { - sodiumScope.close(); - } - } - } - private static final class SodiumScope implements Scope { private final SodiumCompat.TerrainScope terrainScope; diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java index ddc62d33..3c5fd47b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalViewRenderer.java @@ -22,6 +22,7 @@ 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.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -43,7 +44,9 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; +import org.joml.Matrix4fStack; import org.joml.Vector4f; +import org.lwjgl.opengl.GL11; import org.joml.Quaternionf; public final class PortalGunPortalViewRenderer { @@ -58,7 +61,6 @@ public final class PortalGunPortalViewRenderer { private static final ResourceLocation BLUE_OVERLAY = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png"); private static final ResourceLocation ORANGE_OVERLAY = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png"); private static final TextureTarget[] PORTAL_TARGETS = new TextureTarget[MAX_RECURSION_DEPTH + 1]; - private static TextureTarget depthBackup; private static ShaderInstance portalViewShader; private static boolean renderingPortalView; private static boolean loggedRendererActivation; @@ -87,6 +89,7 @@ public final class PortalGunPortalViewRenderer { public static void releaseTargets() { PortalSceneRenderTrace.reset(); + PortalGunPortalVisibilityQueries.clear(); for (int i = 0; i < PORTAL_TARGETS.length; i++) { TextureTarget target = PORTAL_TARGETS[i]; if (target != null) { @@ -96,10 +99,6 @@ public final class PortalGunPortalViewRenderer { } } java.util.Arrays.fill(TARGET_ALLOCATION_FAILURES, null); - if (depthBackup != null) { - depthBackup.destroyBuffers(); - depthBackup = null; - } } public static void render(Camera camera, Matrix4f poseMatrix, Matrix4f projectionMatrix, DeltaTracker tickCounter) { @@ -125,7 +124,7 @@ public final class PortalGunPortalViewRenderer { Vec3 cameraPos = camera.getPosition(); Vec3 rootLook = new Vec3(camera.getLookVector()).normalize(); Vec3 rootUp = new Vec3(camera.getUpVector()).normalize(); - Matrix4f rootViewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(rootLook, rootUp).conjugate(new Quaternionf())); + Matrix4f rootViewMatrix = new Matrix4f(poseMatrix); List portals = collectVisiblePortals(minecraft, cameraPos); if (portals.isEmpty()) { return; @@ -135,10 +134,6 @@ public final class PortalGunPortalViewRenderer { loggedMissingPortalViewShader = true; Antarchy.LOGGER.error("Portal gun destination view shader is unavailable; skipping destination render"); } - minecraft.getMainRenderTarget().bindWrite(true); - for (PortalGunPortalEntity portal : portals) { - drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); - } return; } Vec3 effectiveCameraPos = cameraPos; @@ -168,16 +163,8 @@ public final class PortalGunPortalViewRenderer { return linked != null && linked.isAlive(); }) .sorted((left, right) -> Integer.compare(rootScreenClips.get(right).area(), rootScreenClips.get(left).area())) + .limit(MAX_PORTAL_VIEWS_PER_FRAME) .toList(); - Set selectedPortalSet = Collections.newSetFromMap(new IdentityHashMap<>()); - selectedPortalSet.addAll(selectedPortals); - List renderOrder = new ArrayList<>(portals.size()); - renderOrder.addAll(selectedPortals); - for (PortalGunPortalEntity portal : portals) { - if (!selectedPortalSet.contains(portal)) { - renderOrder.add(portal); - } - } long linkedPortalCount = renderablePortals.stream() .filter(portal -> portal.getLinkedPortal() != null && portal.getLinkedPortal().isAlive()) .count(); @@ -210,47 +197,43 @@ public final class PortalGunPortalViewRenderer { ); } if (selectedPortals.isEmpty()) { - minecraft.getMainRenderTarget().bindWrite(true); - for (PortalGunPortalEntity portal : portals) { - drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); - } return; } RenderContext rootContext = new RenderContext(effectiveCameraPos, rootLook, rootUp, rootViewMatrix); - PortalViewBudget viewBudget = new PortalViewBudget(MAX_PORTAL_VIEWS_PER_FRAME); + boolean directStencil = PortalStencilTarget.supportsDirect(minecraft.getMainRenderTarget()); + PortalViewBudget fallbackBudget = directStencil ? null : new PortalViewBudget(MAX_PORTAL_VIEWS_PER_FRAME); renderingPortalView = true; boolean projectionBackedUp = false; try { RenderSystem.backupProjectionMatrix(); projectionBackedUp = true; - for (PortalGunPortalEntity portal : renderOrder) { - if (!selectedPortalSet.contains(portal)) { - minecraft.getMainRenderTarget().bindWrite(true); - drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); - continue; - } + for (PortalGunPortalEntity portal : selectedPortals) { PortalGunPortalEntity linkedPortal = portal.getLinkedPortal(); if (linkedPortal == null || !linkedPortal.isAlive()) { - minecraft.getMainRenderTarget().bindWrite(true); - drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); continue; } boolean rendered; try { - rendered = renderPortalSurfaceDirect(minecraft, cameraEntity, tickCounter, rootContext, rootProjectionMatrix, portal, linkedPortal, rootScreenClips.get(portal)); + if (directStencil) { + rendered = renderPortalSurfaceDirect(minecraft, cameraEntity, tickCounter, rootContext, rootProjectionMatrix, portal, linkedPortal, rootScreenClips.get(portal)); + } else { + int textureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, fallbackBudget, rootContext, + rootProjectionMatrix, portal, linkedPortal, 0, rootScreenClips.get(portal)); + minecraft.gameRenderer.resetProjectionMatrix(rootProjectionMatrix); + 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) { logRenderFailure(minecraft, exception); RenderSystem.restoreProjectionMatrix(); projectionBackedUp = false; RenderSystem.backupProjectionMatrix(); projectionBackedUp = true; - minecraft.getMainRenderTarget().bindWrite(true); - drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); continue; } if (!rendered) { - minecraft.getMainRenderTarget().bindWrite(true); - drawPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); continue; } RenderSystem.restoreProjectionMatrix(); @@ -258,14 +241,8 @@ public final class PortalGunPortalViewRenderer { RenderSystem.backupProjectionMatrix(); projectionBackedUp = true; minecraft.getMainRenderTarget().bindWrite(true); - ScreenClip clip = rootScreenClips.get(portal); String portalPairKey = portal.getUUID() + "->" + linkedPortal.getUUID(); - boolean composited; - try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "direct-framebuffer", "target=" + targetWidth + "x" + targetHeight)) { - composited = true; - drawAnimatedPortalOverlay(minecraft, cameraPos, rootViewMatrix, portal); - } - if (composited && 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); } } @@ -386,14 +363,10 @@ public final class PortalGunPortalViewRenderer { } int width = target.width; int height = target.height; - if (depthBackup == null) { - depthBackup = new TextureTarget(width, height, true, Minecraft.ON_OSX); - } else if (depthBackup.width != width || depthBackup.height != height) { - depthBackup.resize(width, height, Minecraft.ON_OSX); - } RenderContext transformedContext = transformContext(currentContext, sourcePortal, destinationPortal); float partialTick = tickCounter.getGameTimeDeltaPartialTick(true); Matrix4f clippedProjection = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal); + Matrix4f apertureCullProjection = cropProjectionToScreenClip(clippedProjection, clip, width, height); PortalCamera portalCamera = new PortalCamera(); portalCamera.setup(minecraft.level, cameraEntity, partialTick, transformedContext.cameraPos(), transformedContext.look(), transformedContext.up()); String portalPairKey = sourcePortal.getUUID() + "->" + destinationPortal.getUUID(); @@ -407,6 +380,8 @@ public final class PortalGunPortalViewRenderer { 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) { entityEffectTarget.antarchy$setScreenTarget(target); @@ -417,22 +392,33 @@ public final class PortalGunPortalViewRenderer { 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, depthBackup, - () -> drawPortalQuad(currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, width, height, sourcePortal, partialTick, maskTexture, clip)); + 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); + } finally { + PortalGunPortalVisibilityQueries.end(sourcePortal.getUUID(), destinationPortal.getUUID(), queryStarted); + } + }, + () -> clearDepthInPortalStencil(minecraft), + () -> restorePortalApertureDepth(minecraft, currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, sourcePortal, partialTick)); + } + if (!renderDestination) { + return false; } RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height()); PortalGunPortalRenderState.pushPortalView(new PortalGunPortalRenderState.PortalRenderContext( sourcePortal.getWorldPortalShape(), destinationPortal.getWorldPortalShape(), transformedContext.cameraPos(), transformedContext.look(), transformedContext.up(), transformedContext.viewMatrix(), clippedProjection, - 0, true, destinationPortal.getUUID())); + 0, false, destinationPortal.getUUID())); contextPushed = true; - proxyRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), clippedProjection); + proxyRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), apertureCullProjection); PortalGunPortalViewAreaManager.Scope areaScope = PortalGunPortalViewAreaManager.enter( minecraft, proxyRenderer, destinationPortal.getUUID(), transformedContext.cameraPos(), portalCamera); try { try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "direct-manual-scene", "target=" + width + "x" + height)) { PortalGunProxySceneRenderer.render(minecraft, proxyRenderer, portalCamera, partialTick, - transformedContext.viewMatrix(), clippedProjection, portalPairKey); + transformedContext.viewMatrix(), clippedProjection, apertureCullProjection, portalPairKey); } } finally { areaScope.close(); @@ -448,7 +434,7 @@ public final class PortalGunPortalViewRenderer { stencilScope.close(); } if (contextPushed) { - PortalGunPortalRenderState.popPortalView(true); + PortalGunPortalRenderState.popPortalView(false); } } finally { if (transparencyTarget != null) { @@ -495,6 +481,7 @@ public final class PortalGunPortalViewRenderer { return -1; } Matrix4f clippedProjectionMatrix = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal); + Matrix4f apertureCullProjection = cropProjectionToScreenClip(clippedProjectionMatrix, clip, target.width, target.height); boolean renderAll = true; PortalCamera portalCamera = new PortalCamera(); portalCamera.setup(minecraft.level, cameraEntity, tickCounter.getGameTimeDeltaPartialTick(true), transformedContext.cameraPos(), transformedContext.look(), transformedContext.up()); @@ -550,14 +537,14 @@ public final class PortalGunPortalViewRenderer { PortalGunPortalRenderState.pushPortalView(portalRenderContext); portalRenderStatePushed = true; try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "prepare-portal-view", "camera=" + transformedContext.cameraPos())) { - proxyRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), clippedProjectionMatrix); + proxyRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), apertureCullProjection); } try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(portalPairKey, "activate-view-area", "destination=" + destinationPortal.getUUID())) { PortalGunPortalViewAreaManager.Scope scope = PortalGunPortalViewAreaManager.enter(minecraft, proxyRenderer, destinationPortal.getUUID(), transformedContext.cameraPos(), portalCamera); ignored.close(); try { try (PortalSceneRenderTrace.Phase sceneTrace = PortalSceneRenderTrace.begin(portalPairKey, "manual-scene-subpass", "target=" + target.width + "x" + target.height)) { - PortalGunProxySceneRenderer.render(minecraft, proxyRenderer, portalCamera, partialTick, transformedContext.viewMatrix(), clippedProjectionMatrix, portalPairKey); + PortalGunProxySceneRenderer.render(minecraft, proxyRenderer, portalCamera, partialTick, transformedContext.viewMatrix(), clippedProjectionMatrix, apertureCullProjection, portalPairKey); } } finally { try (PortalSceneRenderTrace.Phase releaseTrace = PortalSceneRenderTrace.begin(portalPairKey, "release-view-area", "destination=" + destinationPortal.getUUID())) { @@ -749,6 +736,20 @@ public final class PortalGunPortalViewRenderer { return new ScreenClip(scissorX, scissorY, scissorWidth, scissorHeight); } + private static Matrix4f cropProjectionToScreenClip(Matrix4f projectionMatrix, ScreenClip clip, int width, int height) { + float left = (float) (clip.x() * 2.0D / width - 1.0D); + float right = (float) ((clip.x() + clip.width()) * 2.0D / width - 1.0D); + float bottom = (float) (clip.y() * 2.0D / height - 1.0D); + float top = (float) ((clip.y() + clip.height()) * 2.0D / height - 1.0D); + float widthNdc = Math.max(1.0E-5F, right - left); + float heightNdc = Math.max(1.0E-5F, top - bottom); + float scaleX = 2.0F / widthNdc; + float scaleY = 2.0F / heightNdc; + float offsetX = -(right + left) / widthNdc; + float offsetY = -(top + bottom) / heightNdc; + return new Matrix4f().translate(offsetX, offsetY, 0.0F).scale(scaleX, scaleY, 1.0F).mul(projectionMatrix); + } + private static List clipPolygon(List vertices, boolean nearPlane) { if (vertices.isEmpty()) { return vertices; @@ -793,18 +794,9 @@ public final class PortalGunPortalViewRenderer { if (clip == null || width <= 0 || height <= 0) { return false; } - PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick)); + PortalGunWorldPortalShape shape = portal.getWorldPortalShape().scaleAperture(portal.getPortalVisualScale(partialTick) * PORTAL_APERTURE_SCALE); Matrix4f combined = new Matrix4f(projectionMatrix).mul(viewMatrix); - double apertureInset = (1.0D - PORTAL_APERTURE_SCALE) * 0.5D; - double apertureStart = apertureInset; - double apertureEnd = 1.0D - apertureInset; - List polygon = new ArrayList<>(4); - polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureStart, apertureStart)); - polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureEnd, apertureStart)); - polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureEnd, apertureEnd)); - polygon.add(projectPortalVertex(combined, cameraPos, shape, apertureStart, apertureEnd)); - polygon = clipPolygon(polygon, false); - polygon = clipPolygon(polygon, true); + List polygon = projectPortalPolygon(combined, cameraPos, shape); if (polygon.size() < 3) { return false; } @@ -818,29 +810,133 @@ public final class PortalGunPortalViewRenderer { if (mesh == null) { return false; } + Matrix4fStack modelViewStack = RenderSystem.getModelViewStack(); + Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix()); + VertexSorting previousSorting = RenderSystem.getVertexSorting(); + modelViewStack.pushMatrix(); try { RenderSystem.disableBlend(); RenderSystem.depthMask(false); RenderSystem.enableDepthTest(); RenderSystem.disableCull(); + modelViewStack.identity(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting); portalViewShader.setSampler("Sampler0", textureId); RenderSystem.setShader(() -> portalViewShader); RenderSystem.setShaderTexture(0, textureId); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); BufferUploader.drawWithShader(mesh); } finally { + RenderSystem.setProjectionMatrix(previousProjection, previousSorting); + modelViewStack.popMatrix(); + RenderSystem.applyModelViewMatrix(); restorePortalDrawState(); } return true; } - private static Vector4f projectPortalVertex(Matrix4f combined, Vec3 cameraPos, PortalGunWorldPortalShape shape, double u, double v) { - Vec3 worldPosition = shape.center() - .add(shape.right().scale((u * 2.0D - 1.0D) * shape.halfWidth())) - .add(shape.up().scale((v * 2.0D - 1.0D) * shape.halfHeight())) - .add(shape.normal().scale(SURFACE_OFFSET)); - Vec3 relative = worldPosition.subtract(cameraPos); - return new Vector4f((float) relative.x, (float) relative.y, (float) relative.z, 1.0F).mul(combined); + private static void clearDepthInPortalStencil(Minecraft minecraft) { + int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC); + RenderSystem.enableDepthTest(); + RenderSystem.depthMask(true); + RenderSystem.colorMask(false, false, false, false); + GL11.glDepthFunc(GL11.GL_ALWAYS); + BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION); + buffer.addVertex(-1.0F, -1.0F, 1.0F); + buffer.addVertex(3.0F, -1.0F, 1.0F); + buffer.addVertex(-1.0F, 3.0F, 1.0F); + MeshData mesh = buffer.build(); + try { + drawClipSpaceMesh(minecraft, mesh); + } finally { + GL11.glDepthFunc(previousDepthFunction); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.depthMask(true); + RenderSystem.enableDepthTest(); + } + } + + private static void restorePortalApertureDepth(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 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)); + } + MeshData mesh = buffer.build(); + int previousDepthFunction = GL11.glGetInteger(GL11.GL_DEPTH_FUNC); + RenderSystem.enableDepthTest(); + RenderSystem.depthMask(true); + RenderSystem.colorMask(false, false, false, false); + GL11.glDepthFunc(GL11.GL_ALWAYS); + try { + drawClipSpaceMesh(minecraft, mesh); + } finally { + GL11.glDepthFunc(previousDepthFunction); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.depthMask(true); + RenderSystem.enableDepthTest(); + } + } + + private static void addDepthVertex(BufferBuilder buffer, Vector4f clipPosition) { + float inverseW = 1.0F / clipPosition.w(); + buffer.addVertex(clipPosition.x() * inverseW, clipPosition.y() * inverseW, clipPosition.z() * inverseW); + } + + private static void drawClipSpaceMesh(Minecraft minecraft, MeshData mesh) { + if (mesh == null) { + return; + } + Matrix4fStack modelViewStack = RenderSystem.getModelViewStack(); + boolean cullEnabled = GL11.glIsEnabled(GL11.GL_CULL_FACE); + boolean blendEnabled = GL11.glIsEnabled(GL11.GL_BLEND); + Matrix4f previousProjection = new Matrix4f(RenderSystem.getProjectionMatrix()); + VertexSorting previousSorting = RenderSystem.getVertexSorting(); + modelViewStack.pushMatrix(); + try { + RenderSystem.disableCull(); + RenderSystem.disableBlend(); + modelViewStack.identity(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.setProjectionMatrix(new Matrix4f(), previousSorting); + RenderSystem.setShader(GameRenderer::getPositionShader); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + BufferUploader.drawWithShader(mesh); + } finally { + RenderSystem.setProjectionMatrix(previousProjection, previousSorting); + modelViewStack.popMatrix(); + RenderSystem.applyModelViewMatrix(); + if (cullEnabled) { + RenderSystem.enableCull(); + } else { + RenderSystem.disableCull(); + } + if (blendEnabled) { + RenderSystem.enableBlend(); + } else { + RenderSystem.disableBlend(); + } + } + } + + private static List projectPortalPolygon(Matrix4f combined, Vec3 cameraPos, PortalGunWorldPortalShape shape) { + Vec3[] corners = shape.getCorners(SURFACE_OFFSET); + List polygon = new ArrayList<>(corners.length); + for (Vec3 corner : corners) { + Vec3 relative = corner.subtract(cameraPos); + polygon.add(new Vector4f((float) relative.x, (float) relative.y, (float) relative.z, 1.0F).mul(combined)); + } + polygon = clipPolygon(polygon, false); + return clipPolygon(polygon, true); } private static void addPortalViewVertex(BufferBuilder buffer, Vector4f clipPosition) { @@ -852,10 +948,10 @@ public final class PortalGunPortalViewRenderer { } private static void renderPortalTransitionEntities(Minecraft minecraft, Vec3 cameraPos, float partialTick, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) { - if (minecraft.level == null || minecraft.getEntityRenderDispatcher() == null || minecraft.renderBuffers() == null) { + if (minecraft.level == null || minecraft.getEntityRenderDispatcher() == null) { return; } - MultiBufferSource.BufferSource bufferSource = minecraft.renderBuffers().bufferSource(); + MultiBufferSource.BufferSource bufferSource = PortalGunPortalRendererPool.bufferSource(); PoseStack poseStack = new PoseStack(); AABB transitionBounds = sourcePortal.getWorldPortalShape().getBoundsForCulling(); List renderEntities = minecraft.level.getEntities( diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalVisibilityQueries.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalVisibilityQueries.java new file mode 100644 index 00000000..8c2be980 --- /dev/null +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunPortalVisibilityQueries.java @@ -0,0 +1,93 @@ +package com.craisinlord.antarchy.content.client.renderer; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; +import org.lwjgl.opengl.GL15; + +public final class PortalGunPortalVisibilityQueries { + private static final int MAX_ENTRIES = 128; + private static final LinkedHashMap QUERIES = new LinkedHashMap<>(16, 0.75F, true); + + private PortalGunPortalVisibilityQueries() { + } + + public static boolean shouldRender(UUID source, UUID destination) { + QueryState state = QUERIES.get(new Pair(source, destination)); + if (state == null) { + return true; + } + if (state.inFlight && GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT_AVAILABLE) != 0) { + state.visible = GL15.glGetQueryObjecti(state.query, GL15.GL_QUERY_RESULT) != 0; + state.hasResult = true; + state.inFlight = false; + PortalSceneRenderTrace.event(source + "->" + destination, "visibility-query-result", "visible=" + state.visible); + } + return !state.hasResult || state.visible; + } + + public static boolean begin(UUID source, UUID destination) { + Pair pair = new Pair(source, destination); + QueryState state = QUERIES.computeIfAbsent(pair, key -> new QueryState(GL15.glGenQueries())); + if (state.inFlight || state.active) { + trim(); + return false; + } + state.active = true; + try { + GL15.glBeginQuery(GL15.GL_SAMPLES_PASSED, state.query); + } catch (RuntimeException | Error exception) { + state.active = false; + throw exception; + } + trim(); + return true; + } + + public static void end(UUID source, UUID destination, boolean started) { + if (!started) { + return; + } + QueryState state = QUERIES.get(new Pair(source, destination)); + if (state == null || !state.active) { + return; + } + GL15.glEndQuery(GL15.GL_SAMPLES_PASSED); + state.active = false; + state.inFlight = true; + } + + public static void clear() { + for (QueryState state : QUERIES.values()) { + GL15.glDeleteQueries(state.query); + } + QUERIES.clear(); + } + + private static void trim() { + Iterator> iterator = QUERIES.entrySet().iterator(); + while (QUERIES.size() > MAX_ENTRIES && iterator.hasNext()) { + QueryState state = iterator.next().getValue(); + if (!state.active) { + GL15.glDeleteQueries(state.query); + iterator.remove(); + } + } + } + + private record Pair(UUID source, UUID destination) { + } + + private static final class QueryState { + private final int query; + private boolean active; + private boolean inFlight; + private boolean hasResult; + private boolean visible = true; + + private QueryState(int query) { + this.query = query; + } + } +} diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunProxySceneRenderer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunProxySceneRenderer.java index 71a18308..eaf1795b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunProxySceneRenderer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalGunProxySceneRenderer.java @@ -4,6 +4,7 @@ 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; @@ -13,7 +14,6 @@ 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.particle.ParticleRenderType; import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; @@ -30,12 +30,21 @@ public final class PortalGunProxySceneRenderer { private PortalGunProxySceneRenderer() { } - public static void render(Minecraft minecraft, LevelRenderer renderer, Camera camera, float partialTick, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, String tracePair) { + 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(); + try { Vec3 cameraPosition = camera.getPosition(); double cameraX = cameraPosition.x; double cameraY = cameraPosition.y; double cameraZ = cameraPosition.z; - Frustum frustum = new Frustum(modelViewMatrix, projectionMatrix); + Frustum frustum = new Frustum(modelViewMatrix, cullProjectionMatrix); frustum.prepare(cameraX, cameraY, cameraZ); LevelRendererPortalSceneInvoker scene = (LevelRendererPortalSceneInvoker) renderer; float renderDistance = minecraft.gameRenderer.getRenderDistance(); @@ -55,29 +64,33 @@ public final class PortalGunProxySceneRenderer { () -> FogRenderer.setupFog(camera, FogRenderer.FogMode.FOG_SKY, renderDistance, foggy, partialTick)); } - try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "frustum-and-visibility", "renderDistance=" + rendererAccessDistance(renderer))) { + 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 = (LevelRendererPortalViewAreaAccessor) renderer; - ObjectArrayList fullVisibleSections = viewAreaAccessor.antarchy$getVisibleSections(); - ObjectArrayList budgetedVisibleSections = new ObjectArrayList<>(); - PortalGunPortalRendererPool.beginSceneBudget(); - 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); + LevelRendererPortalViewAreaAccessor viewAreaAccessor = sodium ? null : (LevelRendererPortalViewAreaAccessor) renderer; + int visibleSectionCount = 0; + if (viewAreaAccessor != null) { + ObjectArrayList fullVisibleSections = viewAreaAccessor.antarchy$getVisibleSections(); + ObjectArrayList 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", "count=" + viewAreaAccessor.antarchy$getVisibleSections().size() + " scheduledRebuilds=" + PortalGunPortalRendererPool.scheduledSectionRebuilds()); + PortalSceneRenderTrace.event(tracePair, "visible-sections-ready", "renderer=" + (sodium ? "sodium" : "vanilla") + " count=" + visibleSectionCount + " scheduledRebuilds=" + PortalGunPortalRendererPool.scheduledSectionRebuilds()); - try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "opaque-terrain-layers", "sections=" + viewAreaAccessor.antarchy$getVisibleSections().size())) { + 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); @@ -98,7 +111,7 @@ public final class PortalGunProxySceneRenderer { RenderSystem.applyModelViewMatrix(); } PoseStack poseStack = new PoseStack(); - MultiBufferSource.BufferSource buffers = minecraft.renderBuffers().bufferSource(); + MultiBufferSource.BufferSource buffers = PortalGunPortalRendererPool.bufferSource(); try { int entityCount = 0; try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "entities", "dispatch-start")) { @@ -122,32 +135,40 @@ public final class PortalGunProxySceneRenderer { PortalSceneRenderTrace.event(tracePair, "entities-dispatched", "count=" + entityCount); int blockEntityCount = 0; - try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "block-entities", "visibleSections=" + viewAreaAccessor.antarchy$getVisibleSections().size())) { - for (var section : viewAreaAccessor.antarchy$getVisibleSections()) { - for (BlockEntity blockEntity : section.getCompiled().getRenderableBlockEntities()) { + 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); - blockEntityCount++; - } - } - synchronized (viewAreaAccessor.antarchy$getGlobalBlockEntities()) { - for (BlockEntity blockEntity : viewAreaAccessor.antarchy$getGlobalBlockEntities()) { - if (frustum.isVisible(new AABB(blockEntity.getBlockPos()).inflate(1.0D))) { + 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")) { - minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick, frustum, type -> !type.isTranslucent()); 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, frustum, ParticleRenderType::isTranslucent); + minecraft.particleEngine.render(minecraft.gameRenderer.lightTexture(), camera, partialTick); } try (PortalSceneRenderTrace.Phase ignored = PortalSceneRenderTrace.begin(tracePair, "clouds", minecraft.options.getCloudsType().toString())) { @@ -173,6 +194,14 @@ public final class PortalGunProxySceneRenderer { RenderSystem.disableBlend(); } } + } finally { + 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) { @@ -186,8 +215,11 @@ public final class PortalGunProxySceneRenderer { } BlockPos pos = blockEntity.getBlockPos(); poseStack.pushPose(); - poseStack.translate(pos.getX() - cameraX, pos.getY() - cameraY, pos.getZ() - cameraZ); - minecraft.getBlockEntityRenderDispatcher().render(blockEntity, partialTick, poseStack, buffers); - poseStack.popPose(); + try { + poseStack.translate(pos.getX() - cameraX, pos.getY() - cameraY, pos.getZ() - cameraZ); + minecraft.getBlockEntityRenderDispatcher().render(blockEntity, partialTick, poseStack, buffers); + } finally { + poseStack.popPose(); + } } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalSceneRenderTrace.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalSceneRenderTrace.java index 5be56e6b..c5fa632c 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalSceneRenderTrace.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalSceneRenderTrace.java @@ -5,7 +5,9 @@ import java.util.HashSet; import java.util.Set; public final class PortalSceneRenderTrace { + private static final String DEBUG_PROPERTY = "antarchy.portalGun.debugRender"; private static final Set LOGGED_PHASES = new HashSet<>(); + private static final Phase NOOP_PHASE = new Phase(0L, "", "", false, 0L); private static long generation; private static String armedKey = ""; @@ -27,6 +29,9 @@ public final class PortalSceneRenderTrace { } public static Phase begin(String pair, String phase, String detail) { + if (!Boolean.getBoolean(DEBUG_PROPERTY)) { + return NOOP_PHASE; + } long currentGeneration; boolean shouldLog; synchronized (PortalSceneRenderTrace.class) { @@ -40,6 +45,9 @@ public final class PortalSceneRenderTrace { } public static void event(String pair, String event, String detail) { + if (!Boolean.getBoolean(DEBUG_PROPERTY)) { + return; + } long currentGeneration; boolean shouldLog; synchronized (PortalSceneRenderTrace.class) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalStencilTarget.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalStencilTarget.java index fe6562d7..6153f269 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalStencilTarget.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/PortalStencilTarget.java @@ -1,5 +1,6 @@ package com.craisinlord.antarchy.content.client.renderer; +import com.craisinlord.antarchy.Antarchy; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.systems.RenderSystem; import java.util.Collections; @@ -9,16 +10,25 @@ import java.util.Objects; import java.util.Set; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; -import com.mojang.blaze3d.pipeline.TextureTarget; public final class PortalStencilTarget { private static final Map ATTACHMENTS = Collections.synchronizedMap(new IdentityHashMap<>()); + private static final Map MAIN_STENCIL_ATTACHMENTS = Collections.synchronizedMap(new IdentityHashMap<>()); + private static final Map UNSUPPORTED_MAIN_TARGETS = Collections.synchronizedMap(new IdentityHashMap<>()); private static final Set DIRECT_TARGETS = Collections.newSetFromMap(new IdentityHashMap<>()); private PortalStencilTarget() { } public static void prepareMainTarget(RenderTarget target) { + target.bindWrite(true); + if (stencilBits() > 0) { + UNSUPPORTED_MAIN_TARGETS.remove(target); + return; + } + if (Objects.equals(UNSUPPORTED_MAIN_TARGETS.get(target), target.frameBufferId)) { + return; + } try { java.lang.reflect.Method enabled = target.getClass().getMethod("isStencilEnabled"); if (!Boolean.TRUE.equals(enabled.invoke(target))) { @@ -27,14 +37,26 @@ public final class PortalStencilTarget { } catch (ReflectiveOperationException ignored) { } target.bindWrite(true); + if (stencilBits() <= 0) { + try { + ensureMainStencilAttachment(target); + } catch (IllegalStateException exception) { + UNSUPPORTED_MAIN_TARGETS.put(target, target.frameBufferId); + Antarchy.LOGGER.warn("Portal gun direct stencil is unavailable; using offscreen portal rendering", exception); + } + } } - public static Scope beginDirect(RenderTarget target, TextureTarget depthBackup, Runnable drawApertureMask) { + public static boolean supportsDirect(RenderTarget target) { target.bindWrite(true); - if (GL11.glGetInteger(GL11.GL_STENCIL_BITS) <= 0) { + 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"); } - depthBackup.copyDepthFrom(target); DIRECT_TARGETS.add(target); target.bindWrite(true); RenderSystem.disableScissor(); @@ -45,14 +67,32 @@ public final class PortalStencilTarget { RenderSystem.stencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE); RenderSystem.colorMask(false, false, false, false); RenderSystem.depthMask(false); - RenderSystem.disableDepthTest(); + 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) { - resetState(); - target.copyDepthFrom(depthBackup); - DIRECT_TARGETS.remove(target); + 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); @@ -60,9 +100,8 @@ public final class PortalStencilTarget { 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, depthBackup); + return new Scope(target, restoreApertureDepth); } public static Scope begin(RenderTarget target, Runnable drawApertureMask) { @@ -107,10 +146,15 @@ public final class PortalStencilTarget { 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()); + } } public static void clear() { @@ -118,9 +162,47 @@ public final class PortalStencilTarget { 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) { + Attachment attachment = MAIN_STENCIL_ATTACHMENTS.get(target); + if (attachment != null + && attachment.framebuffer() == target.frameBufferId + && attachment.width() == target.width + && attachment.height() == target.height) { + throw new IllegalStateException("Main render target stencil attachment has no stencil bits"); + } + if (attachment != null) { + GL30.glDeleteRenderbuffers(attachment.renderbuffer()); + MAIN_STENCIL_ATTACHMENTS.remove(target); + } + int previousRenderbuffer = GL11.glGetInteger(GL30.GL_RENDERBUFFER_BINDING); + int renderbuffer = GL30.glGenRenderbuffers(); + try { + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, renderbuffer); + GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_STENCIL_INDEX8, target.width, target.height); + GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, renderbuffer); + int status = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER); + if (status != GL30.GL_FRAMEBUFFER_COMPLETE) { + GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, 0); + throw new IllegalStateException("Main render target is incomplete with stencil attachment: 0x" + Integer.toHexString(status)); + } + MAIN_STENCIL_ATTACHMENTS.put(target, new Attachment(target.frameBufferId, renderbuffer, target.width, target.height)); + PortalSceneRenderTrace.event("main-target", "stencil-attached", "framebuffer=" + target.frameBufferId + " size=" + target.width + "x" + target.height); + } catch (RuntimeException | Error exception) { + GL30.glDeleteRenderbuffers(renderbuffer); + throw exception; + } finally { + GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, previousRenderbuffer); + } + } + private static void ensureAttachment(RenderTarget target) { Attachment attachment = ATTACHMENTS.get(target); if (attachment != null @@ -162,21 +244,28 @@ public final class PortalStencilTarget { RenderSystem.enableDepthTest(); } + private static int stencilBits() { + int type = GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, + GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + return type == GL11.GL_NONE ? 0 : GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, + GL30.GL_STENCIL_ATTACHMENT, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE); + } + 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 TextureTarget depthBackup; + private final Runnable restoreApertureDepth; private Scope(RenderTarget target) { this(target, null); } - private Scope(RenderTarget target, TextureTarget depthBackup) { + private Scope(RenderTarget target, Runnable restoreApertureDepth) { this.target = Objects.requireNonNull(target); - this.depthBackup = depthBackup; + this.restoreApertureDepth = restoreApertureDepth; } @Override @@ -185,12 +274,23 @@ public final class PortalStencilTarget { return; } closed = true; - if (this.depthBackup != null) { - this.target.copyDepthFrom(this.depthBackup); - this.target.bindWrite(true); - DIRECT_TARGETS.remove(this.target); + 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(); } - resetState(); } } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/SodiumCompat.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/SodiumCompat.java index cd6cc797..1c767c44 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/SodiumCompat.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/SodiumCompat.java @@ -5,7 +5,9 @@ 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"; @@ -29,13 +31,10 @@ public final class SodiumCompat { RENDER_STATE.get().activeSetup = new TerrainSetup(camera, viewport, spectator, updateChunks); } - public static TerrainScope beginPortalView(Camera portalCamera) { + public static TerrainScope beginPortalView() { RenderState state = RENDER_STATE.get(); TerrainSetup previousSetup = state.activeSetup; state.previousSetups.push(new PreviousSetup(previousSetup)); - if (previousSetup != null) { - restoreTerrainSetup(previousSetup.withCamera(portalCamera), state); - } return () -> { PreviousSetup previous = state.previousSetups.pop(); if (previous.setup() == null) { @@ -50,6 +49,20 @@ public final class SodiumCompat { RENDER_STATE.remove(); } + public static void forEachVisibleBlockEntity(Consumer 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(); @@ -77,7 +90,14 @@ public final class SodiumCompat { state.activeSetup = setup; return; } - terrainSetup.invoke(renderer, setup.camera(), setup.viewport(), setup.spectator(), setup.updateChunks()); + 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; @@ -110,8 +130,5 @@ public final class SodiumCompat { } private record TerrainSetup(Camera camera, Object viewport, boolean spectator, boolean updateChunks) { - private TerrainSetup withCamera(Camera camera) { - return new TerrainSetup(camera, this.viewport, this.spectator, this.updateChunks); - } } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/TemporalContractionAfterimageLayer.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/TemporalContractionAfterimageLayer.java index ad099e6a..4b72bbec 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/TemporalContractionAfterimageLayer.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/client/renderer/TemporalContractionAfterimageLayer.java @@ -17,7 +17,6 @@ import org.joml.Matrix4f; import org.joml.Vector3f; public final class TemporalContractionAfterimageLayer> extends RenderLayer { - private static final int RGB = 0xFFD35A; private static final float BASE_ALPHA = 0.42F; private static final int SAMPLE_STRIDE = 2; @@ -64,7 +63,7 @@ public final class TemporalContractionAfterimageLayer extends GeoRenderLayer { - private static final int RGB = 0xFFD35A; private static final float BASE_ALPHA = 0.42F; private static final int SAMPLE_STRIDE = 2; @@ -66,7 +65,7 @@ public final class TemporalContractionGeoLayer extends Vector3f local = inverse.transformDirection(new Vector3f((float) dx, (float) dy, (float) dz)); int alpha = (int) (BASE_ALPHA * fade * 255.0F) & 0xFF; - int color = alpha << 24 | RGB; + int color = alpha << 24 | ContractionAfterimages.colorFor(entity); poseStack.pushPose(); poseStack.translate(local.x, local.y, local.z); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java index 61986260..71db86b8 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/portalgun/PortalGunPortalEntity.java @@ -79,6 +79,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle"); private static final int OPEN_TICKS = 5; public static final int TELEPORT_COOLDOWN_TICKS = 3; + private static final String DEBUG_TRAVERSAL_PROPERTY = "antarchy.portalGun.debugTraversal"; private static final double PORTAL_INSIDE_SHIFT = 0.05D; private static final Set LOGGED_CROSSING_PATHS = new HashSet<>(); private static final double HALF_DEPTH = 0.35D; @@ -358,22 +359,26 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { Vec3 currentProbe = this.teleportProbePosition(entity, currentBox); Vec3 normal = this.getNormalVec().normalize(); if (entity instanceof Player && Math.abs(normal.y) < 0.5D) { - double offset = Math.min(0.05D, Math.abs(this.position().subtract(entity.position()).dot(normal))); - Vec3 shift = normal.scale(offset); - if (this.getScanRange().move(shift).contains(currentProbe) - || !this.getPortalInsides().move(shift).contains(currentProbe)) { + 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; } - PortalGunWorldPortalShape shape = this.getWorldPortalShape(); - AABB flatPlane = shape.getFlatPlane(); - boolean aligned; - if (Math.abs(normal.y) > 0.5D) { - aligned = currentBox.minX >= flatPlane.minX && currentBox.maxX <= flatPlane.maxX - && currentBox.minZ >= flatPlane.minZ && currentBox.maxZ <= flatPlane.maxZ; - } else { - aligned = currentBox.minY >= flatPlane.minY && currentBox.maxY <= flatPlane.maxY; + Vec3 center = currentBox.getCenter(); + PortalGunWorldPortalShape.PortalLocalCoords localCenter = shape.localCoords(center); + 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 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 aligned ? new PortalCrossing(currentProbe, offset) : null; + return new PortalCrossing(currentProbe, Math.max(0.05D, currentDepth + 0.05D)); } return this.crossesPortal(previousProbe, currentProbe) ? new PortalCrossing(currentProbe, 0.0D) : null; } @@ -416,6 +421,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } PortalCrossing crossing = portal.resolveCrossing(entity, previousBox, currentBox); if (crossing != null) { + if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) { + Antarchy.LOGGER.info("Portal gun traversal accepted path=movement portal={} player={} probe={} offset={} linked={}", + portal.getUUID(), entity.getUUID(), crossing.probe(), crossing.normalOffset(), linked.getUUID()); + } logCrossingPath(entity, "movement"); portal.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset()); return; @@ -506,6 +515,10 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { } PortalGunPortalEntity linked = this.findLinkedPortal(serverLevel); if (linked == null || !linked.isAlive()) { + if (Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY) && this.ageTicks % 40 == 0) { + Antarchy.LOGGER.info("Portal gun traversal unavailable portal={} side={} linked={} loaded={}", + this.getUUID(), this.getPortalSide(), this.linkedPortalId, linked != null); + } if (this.ageTicks % 100 == 0) { Antarchy.LOGGER.debug("Portal gun portal has no loaded linked portal portal={} owner={} side={} linked={}", this.getUUID(), this.ownerId, this.getPortalSide(), this.linkedPortalId); } @@ -560,8 +573,20 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity { AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ()); PortalCrossing crossing = this.resolveCrossing(entity, previousBox, currentBox); if (crossing == null) { + if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY) && this.ageTicks % 20 == 0) { + Vec3 probe = this.teleportProbePosition(entity, currentBox); + double depth = this.getWorldPortalShape().localCoords(probe).depth(); + if (depth < 1.5D) { + Antarchy.LOGGER.info("Portal gun traversal waiting portal={} player={} depth={} position={} linked={}", + this.getUUID(), entity.getUUID(), depth, entity.position(), linked.getUUID()); + } + } continue; } + if (entity instanceof Player && Boolean.getBoolean(DEBUG_TRAVERSAL_PROPERTY)) { + Antarchy.LOGGER.info("Portal gun traversal accepted portal={} player={} probe={} offset={} linked={}", + this.getUUID(), entity.getUUID(), crossing.probe(), crossing.normalOffset(), linked.getUUID()); + } logCrossingPath(entity, "portal_tick"); Vec3 resolvedMovement = entity.position().subtract(entity.xo, entity.yo, entity.zo); this.teleportEntity(entity, linked, crossing.probe(), resolvedMovement, crossing.normalOffset()); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java index 325c2f28..2d835329 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationApi.java @@ -5,6 +5,8 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -17,6 +19,7 @@ public final class TimeDilationApi { }; private static final Map SYNCED_CLIENT_RATES = new ConcurrentHashMap<>(); private static final ThreadLocal ROTATION_BYPASS_DEPTH = ThreadLocal.withInitial(() -> 0); + private static final List> HORIZONTAL_ONLY_MOVEMENT = new CopyOnWriteArrayList<>(); private TimeDilationApi() { } @@ -98,7 +101,7 @@ public final class TimeDilationApi { } public static void applySyncedRate(UUID entityUuid, double rate) { - double clampedRate = TimeDilationMath.clampRate(rate); + double clampedRate = TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE); SYNCED_CLIENT_RATES.put(entityUuid, clampedRate); } @@ -122,7 +125,75 @@ public final class TimeDilationApi { } public static void syncEntityRate(Entity entity, double rate) { - syncDispatcher.accept(entity, TimeDilationMath.clampRate(rate)); + syncDispatcher.accept(entity, TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE)); + } + + public static void addHorizontalOnlyMovement(Predicate predicate) { + HORIZONTAL_ONLY_MOVEMENT.add(predicate); + } + + public static boolean isHorizontalOnlyMovement(Entity entity) { + for (Predicate predicate : HORIZONTAL_ONLY_MOVEMENT) { + if (predicate.test(entity)) { + return true; + } + } + return false; + } + + public static double getRateCeiling(Entity entity) { + if (entity instanceof TimeDilationEntityAccess access) { + return access.antarchy$getTimeDilationCeiling(); + } + return TimeDilationMath.MAX_RATE; + } + + public static void setRateCeiling(Entity entity, double ceiling) { + if (entity instanceof TimeDilationEntityAccess access) { + access.antarchy$setTimeDilationCeiling(ceiling); + TimeDilationManager.trackPotentiallyAffected(entity); + } + } + + public static void clearRateCeiling(Entity entity) { + setRateCeiling(entity, TimeDilationMath.MAX_RATE); + } + + public static void setPersonalRate(Entity entity, String sourceId, double rate) { + if (entity instanceof TimeDilationEntityAccess access) { + double clampedRate = TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE); + if (Math.abs(clampedRate - TimeDilationMath.NORMAL_RATE) < 0.001D) { + access.antarchy$getPersonalTimeDilationRates().remove(sourceId); + } else { + access.antarchy$getPersonalTimeDilationRates().put(sourceId, clampedRate); + } + TimeDilationManager.trackPotentiallyAffected(entity); + } + } + + public static void clearPersonalRate(Entity entity, String sourceId) { + if (entity instanceof TimeDilationEntityAccess access + && access.antarchy$getPersonalTimeDilationRates().remove(sourceId) != null) { + TimeDilationManager.trackPotentiallyAffected(entity); + } + } + + public static double getPersonalRate(Entity entity, String sourceId) { + if (entity instanceof TimeDilationEntityAccess access) { + return access.antarchy$getPersonalTimeDilationRates().getOrDefault(sourceId, TimeDilationMath.NORMAL_RATE); + } + return TimeDilationMath.NORMAL_RATE; + } + + public static boolean hasPersonalRates(Entity entity) { + return entity instanceof TimeDilationEntityAccess access && !access.antarchy$getPersonalTimeDilationRates().isEmpty(); + } + + public static TimeDilationFieldEntity createAttachedField(ServerLevel level, Entity owner, double radius, double rate, int durationTicks) { + TimeDilationFieldEntity field = TimeDilationFieldEntity.create(level, owner.position(), radius, rate, durationTicks, owner.getUUID(), true); + field.attachTo(owner); + level.addFreshEntity(field); + return field; } public static void syncFields(ServerPlayer player, List fields) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationEntityAccess.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationEntityAccess.java index 5bb9002f..c571ead1 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationEntityAccess.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationEntityAccess.java @@ -1,6 +1,14 @@ package com.craisinlord.antarchy.content.time; +import java.util.Map; + public interface TimeDilationEntityAccess { + double antarchy$getTimeDilationCeiling(); + + void antarchy$setTimeDilationCeiling(double ceiling); + + Map antarchy$getPersonalTimeDilationRates(); + double antarchy$getTimeDilationRate(); void antarchy$setTimeDilationRate(double rate); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java index 8f4eed34..6233b24b 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationManager.java @@ -77,6 +77,7 @@ public final class TimeDilationManager { && Math.abs(access.antarchy$getTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D && Math.abs(access.antarchy$getInheritedTimeDilationRate() - TimeDilationMath.NORMAL_RATE) < 0.001D && Math.abs(TimeDilationApi.getVehicleRate(entity) - TimeDilationMath.NORMAL_RATE) < 0.001D + && !TimeDilationApi.hasPersonalRates(entity) && !hasTuner && !hasEffect) { return; } @@ -215,8 +216,16 @@ public final class TimeDilationManager { } else { fastRate = Math.max(fastRate, inheritedRate); } - double targetRate = slowRate < TimeDilationMath.NORMAL_RATE ? slowRate : fastRate; - double rate = TimeDilationMath.transitionRate(previousRate, targetRate); + for (double personalRate : access.antarchy$getPersonalTimeDilationRates().values()) { + if (personalRate < TimeDilationMath.NORMAL_RATE) { + slowRate = Math.min(slowRate, personalRate); + } else { + fastRate = Math.max(fastRate, personalRate); + } + } + double ceiling = access.antarchy$getTimeDilationCeiling(); + double targetRate = TimeDilationMath.clampRate(slowRate < TimeDilationMath.NORMAL_RATE ? slowRate : fastRate, ceiling); + double rate = TimeDilationMath.transitionRate(previousRate, targetRate, ceiling); if (Math.abs(targetRate - TimeDilationMath.NORMAL_RATE) < 0.001D && Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) { TimeDilationApi.clearRate(entity); diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java index 9f18e850..19f5f554 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/content/time/TimeDilationMath.java @@ -4,6 +4,7 @@ public final class TimeDilationMath { public static final double MIN_RATE = 0.05D; public static final double NORMAL_RATE = 1.0D; public static final double MAX_RATE = 3.0D; + public static final double ABSOLUTE_MAX_RATE = 20.0D; public static final int FIELD_TRANSITION_TICKS = 20; public static final int EFFECT_TRANSITION_TICKS = 20; private static final double GAUSSIAN_EDGE_EXPONENT = -4.5D; @@ -18,12 +19,31 @@ public final class TimeDilationMath { return Math.max(MIN_RATE, Math.min(MAX_RATE, rate)); } + public static double clampCeiling(double ceiling) { + if (Double.isNaN(ceiling) || Double.isInfinite(ceiling)) { + return MAX_RATE; + } + return Math.max(NORMAL_RATE, Math.min(ABSOLUTE_MAX_RATE, ceiling)); + } + + public static double clampRate(double rate, double ceiling) { + if (Double.isNaN(rate) || Double.isInfinite(rate)) { + return NORMAL_RATE; + } + return Math.max(MIN_RATE, Math.min(clampCeiling(ceiling), rate)); + } + public static double transitionRate(double currentRate, double targetRate) { - double current = clampRate(currentRate); - double target = clampRate(targetRate); - double maximumStep = (MAX_RATE - MIN_RATE) / EFFECT_TRANSITION_TICKS; + return transitionRate(currentRate, targetRate, MAX_RATE); + } + + public static double transitionRate(double currentRate, double targetRate, double ceiling) { + double maximum = clampCeiling(ceiling); + double current = clampRate(currentRate, maximum); + double target = clampRate(targetRate, maximum); + double maximumStep = (maximum - MIN_RATE) / EFFECT_TRANSITION_TICKS; double delta = Math.max(-maximumStep, Math.min(maximumStep, target - current)); - return clampRate(current + delta); + return clampRate(current + delta, maximum); } public static double gaussianFalloff(double distance, double radius) { diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalLifecycleMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalLifecycleMixin.java index 19921874..0ccc40d6 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalLifecycleMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/client/LevelRendererPortalLifecycleMixin.java @@ -4,6 +4,7 @@ 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; @@ -17,7 +18,7 @@ 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 (!PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) { + if (PortalGunPortalViewRenderer.isEnabled() && !PortalGunPortalRendererPool.isProxy((LevelRenderer) (Object) this)) { PortalStencilTarget.prepareMainTarget(Minecraft.getInstance().getMainRenderTarget()); } } @@ -29,6 +30,7 @@ public abstract class LevelRendererPortalLifecycleMixin { } PortalGunPortalRendererPool.clear(); PortalGunPortalViewAreaManager.clear(); + PortalGunPortalVisibilityQueries.clear(); } @Inject(method = "close", at = @At("HEAD")) diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java index af8ac9f0..aca4cb53 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityMoveTimeDilationMixin.java @@ -39,6 +39,9 @@ public abstract class EntityMoveTimeDilationMixin { if (Math.abs(rate - TimeDilationMath.NORMAL_RATE) < 0.001D) { return movement; } + if (rate > TimeDilationMath.NORMAL_RATE && TimeDilationApi.isHorizontalOnlyMovement(entity)) { + return movement.multiply(rate, 1.0D, rate); + } return movement.scale(rate); } } diff --git a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityTimeDilationStateMixin.java b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityTimeDilationStateMixin.java index 7e64a0e8..a15419ce 100644 --- a/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityTimeDilationStateMixin.java +++ b/common-1.21.1/src/main/java/com/craisinlord/antarchy/mixins/time/EntityTimeDilationStateMixin.java @@ -20,6 +20,25 @@ public abstract class EntityTimeDilationStateMixin implements TimeDilationEntity private boolean antarchy$inTimeDilationMove; @Unique private boolean antarchy$applyingExternalImpulse; + @Unique + private double antarchy$timeDilationCeiling = TimeDilationMath.MAX_RATE; + @Unique + private final Map antarchy$personalTimeDilationRates = new HashMap<>(); + + @Override + public double antarchy$getTimeDilationCeiling() { + return this.antarchy$timeDilationCeiling; + } + + @Override + public void antarchy$setTimeDilationCeiling(double ceiling) { + this.antarchy$timeDilationCeiling = TimeDilationMath.clampCeiling(ceiling); + } + + @Override + public Map antarchy$getPersonalTimeDilationRates() { + return this.antarchy$personalTimeDilationRates; + } @Override public double antarchy$getTimeDilationRate() { @@ -29,7 +48,7 @@ public abstract class EntityTimeDilationStateMixin implements TimeDilationEntity @Override public void antarchy$setTimeDilationRate(double rate) { double previousRate = this.antarchy$timeDilationRate; - this.antarchy$timeDilationRate = TimeDilationMath.clampRate(rate); + this.antarchy$timeDilationRate = TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE); if (previousRate != TimeDilationMath.NORMAL_RATE && this.antarchy$timeDilationRate == TimeDilationMath.NORMAL_RATE) { this.antarchy$timeDilationTimerProgress.clear(); @@ -53,7 +72,7 @@ public abstract class EntityTimeDilationStateMixin implements TimeDilationEntity @Override public int antarchy$consumeTimeDilationTicks(String timerKey, double rate) { - double clampedRate = TimeDilationMath.clampRate(rate); + double clampedRate = TimeDilationMath.clampRate(rate, TimeDilationMath.ABSOLUTE_MAX_RATE); if (Math.abs(clampedRate - TimeDilationMath.NORMAL_RATE) < 0.001D) { return 1; } diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/computer_scientist.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/computer_scientist.png new file mode 100644 index 0000000000000000000000000000000000000000..420cfcfe7f186a7efde46fe8316fa7862ef66e8a GIT binary patch literal 861 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0#2l#}zs;Q|N8ynl$*j#w^^1+|qD=yqTeR%hwT^o0-TeNz?iK_Iddc+8X=7ML>4nJ@ErtU#vK#0-vR|CN?apKg7ec#$`gxH z83GbB^GfvmTtgI0^$hjQyRUt>WME)&@^o#2p_?5mZJJ{`A=T05&SCya5&m7D{%dl~&1TwEBAo~+|q@J-9%qY}e~$+4^q z-!HLWHuei-4=`qZ;iB4J9Lf+HF~@;(#=1auh9A!g9u=!Im^;1ocn~Anz*(}VvFON7 zt%js3HF|h3W=^R~TE)-S%nFy%9R$j_=vpDA6`a z?`gTur4(|t)oz;|lU6fPnYnV6G6&!C#qXJJZ95vZi}OOKqe7=#YOVJ7Vu`n1?SDCU zEEf?E>huaty!<#cn;}o`-pkFa4&0I1bo(s(irwy&-y=f=mYw*}&;L$S(zc$_!2I`} z_bz<%+g~5uK4s2!wT0({51Vejn`h3`ez;)ok7+?W=30t11n<9dI_~@P_xF`mpOu#E zVz`j-S~lfEqMSmNO|=Ol!mIM1RHzf6n8#gs`$ zW6Dm3@YG+=gNlwgEMBfKeSIZE`PX}<(+hPXb&r>6|KwJU*Wb4^2 fH(c+|f2nVHqIOa0`*m}GDUQL@)z4*}Q$iB}sttk$ literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/computer_scientist.png.mcmeta b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/computer_scientist.png.mcmeta new file mode 100644 index 00000000..4525a63b --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/computer_scientist.png.mcmeta @@ -0,0 +1,5 @@ +{ + "villager": { + "hat": "full" + } +} diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/pest_control.png b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/pest_control.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb6ca141cf1fbc0c3a744d31c958758c8bac79e GIT binary patch literal 613 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1U2Ka=yT1c|^D6=`qvl>hBDGPG=YfF|nYsH%>Hg(hnhWc7rSsgySX5rK} z=i-?*8J$J07E3eybbL~q0~`(Ftf$1d#_1{mP4daz{TfJ#mjw9*|Hld#o;f^=11jMx z@Q5sCVBk9p!i>lBSEK+1B}!Z)N`mv#O3D+9QW*jgGxJLH{9Hp6O!W-)%)76Bw*-15 z$=2V)Fa~SiJI`tZtmr zyTO_u{%2#unoUz@1}r+lygHp}N0Y&D$6 zWqyh>o)7Q0-Trg>oc!bqm4>??2sarlEKX>XI<%})>wh>dJFTRq_v+}qofV(*vVs5a=>vMYzNv7@Kf{#oUuesfNi zlIh;mdKI;Vst0Ec`3AOHXW literal 0 HcmV?d00001 diff --git a/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/pest_control.png.mcmeta b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/pest_control.png.mcmeta new file mode 100644 index 00000000..5571aa23 --- /dev/null +++ b/common-1.21.1/src/main/resources/assets/antarchy/textures/entity/zombie_villager/profession/pest_control.png.mcmeta @@ -0,0 +1,5 @@ +{ + "villager": { + "hat": "none" + } +} diff --git a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java index 59ddeac1..1683bbd7 100644 --- a/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java +++ b/fabric-1.21.1/src/main/java/com/craisinlord/antarchy/fabric/client/AntarchyFabricClientBootstrap.java @@ -633,8 +633,8 @@ public final class AntarchyFabricClientBootstrap { BloodCrystalKatanaTrailHandler.render(context); ScorpionWhipTetherRenderHandler.render(context); WormHookTetherRenderHandler.render(context); - if (context.matrixStack() != null && com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.isEnabled()) { - com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.matrixStack().last().pose(), context.projectionMatrix(), context.tickCounter()); + if (com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.isEnabled()) { + com.craisinlord.antarchy.content.client.renderer.PortalGunPortalViewRenderer.render(context.camera(), context.positionMatrix(), context.projectionMatrix(), context.tickCounter()); } }); } diff --git a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java index cc38226c..79a90c1f 100644 --- a/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java +++ b/neoforge-1.21.1/src/main/java/com/craisinlord/antarchy/neoforge/client/PortalGunPortalViewRenderHandler.java @@ -17,7 +17,7 @@ public final class PortalGunPortalViewRenderHandler { if (!PortalGunPortalViewRenderer.isEnabled()) { return; } - if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_BLOCK_ENTITIES) { + if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_LEVEL) { return; } PortalGunPortalViewRenderer.render(event.getCamera(), event.getModelViewMatrix(), event.getProjectionMatrix(), event.getPartialTick());