Fix rendering far from world origin

- Make BatchingEngine use a shifting render origin
- Make frustum checks relative to the render origin
- Improve AbstractEntityVisual#isVisible to be closer to vanilla
- Move render origin implementation code to AbstractEngine
This commit is contained in:
PepperCode1 2023-04-10 14:30:08 -07:00
parent 05458d01d2
commit 2847ad28ed
13 changed files with 124 additions and 130 deletions

View file

@ -1,9 +1,5 @@
package com.jozufozu.flywheel.api.event;
import org.jetbrains.annotations.NotNull;
import org.joml.FrustumIntersection;
import com.jozufozu.flywheel.lib.math.MatrixUtil;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
@ -12,20 +8,12 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderBuffers;
public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection,
Matrix4f projection, RenderBuffers buffers, Camera camera, FrustumIntersection culler) {
@NotNull
public static Matrix4f createViewProjection(PoseStack view, Matrix4f projection) {
var viewProjection = projection.copy();
viewProjection.multiply(view.last().pose());
return viewProjection;
}
public record RenderContext(LevelRenderer renderer, ClientLevel level, RenderBuffers buffers, PoseStack stack,
Matrix4f projection, Matrix4f viewProjection, Camera camera) {
public static RenderContext create(LevelRenderer renderer, ClientLevel level, RenderBuffers buffers, PoseStack stack, Matrix4f projection, Camera camera) {
Matrix4f viewProjection = projection.copy();
viewProjection.multiply(stack.last().pose());
public static FrustumIntersection createCuller(Matrix4f viewProjection, float camX, float camY, float camZ) {
org.joml.Matrix4f proj = MatrixUtil.toJoml(viewProjection);
proj.translate(camX, camY, camZ);
return new FrustumIntersection(proj);
return new RenderContext(renderer, level, buffers, stack, projection, viewProjection, camera);
}
}

View file

@ -19,7 +19,7 @@ public class Backends {
*/
public static final Backend BATCHING = SimpleBackend.builder()
.engineMessage(new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN))
.engineFactory(level -> new BatchingEngine())
.engineFactory(level -> new BatchingEngine(256))
.supported(() -> !ShadersModHandler.isShaderPackInUse())
.register(Flywheel.rl("batching"));
@ -28,7 +28,7 @@ public class Backends {
*/
public static final Backend INSTANCING = SimpleBackend.builder()
.engineMessage(new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN))
.engineFactory(level -> new InstancingEngine(Contexts.WORLD, 100))
.engineFactory(level -> new InstancingEngine(256, Contexts.WORLD))
.fallback(() -> Backends.BATCHING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.instancedArraysSupported())
@ -40,7 +40,7 @@ public class Backends {
*/
public static final Backend INDIRECT = SimpleBackend.builder()
.engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
.engineFactory(level -> new IndirectEngine(100))
.engineFactory(level -> new IndirectEngine(256))
.fallback(() -> Backends.INSTANCING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.supportsIndirect())

View file

@ -112,7 +112,7 @@ public class FlwCompiler {
var details = errors.stream()
.map(FailedCompilation::getMessage)
.collect(Collectors.joining("\n"));
throw new ShaderLoadingException("Compilation failed.\n" + details);
// throw new ShaderLoadingException("Compilation failed.\n" + details);
}
}

View file

@ -0,0 +1,42 @@
package com.jozufozu.flywheel.backend.engine;
import com.jozufozu.flywheel.api.backend.Engine;
import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
public abstract class AbstractEngine implements Engine {
protected final int sqrMaxOriginDistance;
protected BlockPos renderOrigin = BlockPos.ZERO;
public AbstractEngine(int maxOriginDistance) {
sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
}
@Override
public boolean updateRenderOrigin(Camera camera) {
Vec3 cameraPos = camera.getPosition();
double dx = renderOrigin.getX() - cameraPos.x;
double dy = renderOrigin.getY() - cameraPos.y;
double dz = renderOrigin.getZ() - cameraPos.z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
if (distanceSqr <= sqrMaxOriginDistance) {
return false;
}
renderOrigin = new BlockPos(cameraPos);
onRenderOriginChanged();
return true;
}
@Override
public Vec3i renderOrigin() {
return renderOrigin;
}
protected void onRenderOriginChanged() {
}
}

View file

@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.engine.batching;
import java.util.List;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.Instance;
@ -10,19 +9,21 @@ import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.instance.Instancer;
import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
import com.jozufozu.flywheel.util.FlwUtil;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
public class BatchingEngine implements Engine {
public class BatchingEngine extends AbstractEngine {
private final BatchingTransformManager transformManager = new BatchingTransformManager();
private final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
public BatchingEngine(int maxOriginDistance) {
super(maxOriginDistance);
}
@Override
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
return transformManager.getInstancer(type, model, stage);
@ -32,9 +33,9 @@ public class BatchingEngine implements Engine {
public void beginFrame(TaskExecutor executor, RenderContext context) {
transformManager.flush();
Vec3 cameraPos = context.camera().getPosition();
var stack = FlwUtil.copyPoseStack(context.stack());
stack.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
Vec3 cameraPos = context.camera().getPosition();
stack.translate(renderOrigin.getX() - cameraPos.x, renderOrigin.getY() - cameraPos.y, renderOrigin.getZ() - cameraPos.z);
// TODO: async task executor barriers
executor.syncPoint();
@ -78,13 +79,8 @@ public class BatchingEngine implements Engine {
}
@Override
public boolean updateRenderOrigin(Camera camera) {
return false;
}
@Override
public Vec3i renderOrigin() {
return BlockPos.ZERO;
protected void onRenderOriginChanged() {
transformManager.clearInstancers();
}
@Override
@ -95,5 +91,6 @@ public class BatchingEngine implements Engine {
@Override
public void addDebugInfo(List<String> info) {
info.add("Batching");
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
}
}

View file

@ -4,7 +4,6 @@ import java.util.List;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.Instance;
@ -12,25 +11,18 @@ import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.instance.Instancer;
import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.GlTextureUnit;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
public class IndirectEngine implements Engine {
private final int sqrMaxOriginDistance;
public class IndirectEngine extends AbstractEngine {
private final IndirectDrawManager drawManager = new IndirectDrawManager();
private BlockPos renderOrigin = BlockPos.ZERO;
public IndirectEngine(int maxOriginDistance) {
this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
super(maxOriginDistance);
}
@Override
@ -68,25 +60,8 @@ public class IndirectEngine implements Engine {
}
@Override
public boolean updateRenderOrigin(Camera camera) {
Vec3 cameraPos = camera.getPosition();
double dx = renderOrigin.getX() - cameraPos.x;
double dy = renderOrigin.getY() - cameraPos.y;
double dz = renderOrigin.getZ() - cameraPos.z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
if (distanceSqr <= sqrMaxOriginDistance) {
return false;
}
renderOrigin = new BlockPos(cameraPos);
protected void onRenderOriginChanged() {
drawManager.clearInstancers();
return true;
}
@Override
public Vec3i renderOrigin() {
return renderOrigin;
}
@Override

View file

@ -4,7 +4,6 @@ import java.util.List;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.context.Context;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
@ -15,29 +14,22 @@ import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.Pipelines;
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.GlTextureUnit;
import com.jozufozu.flywheel.lib.material.MaterialIndices;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
public class InstancingEngine implements Engine {
public class InstancingEngine extends AbstractEngine {
private final Context context;
private final int sqrMaxOriginDistance;
private final InstancingDrawManager drawManager = new InstancingDrawManager();
private BlockPos renderOrigin = BlockPos.ZERO;
public InstancingEngine(Context context, int maxOriginDistance) {
public InstancingEngine(int maxOriginDistance, Context context) {
super(maxOriginDistance);
this.context = context;
this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
}
@Override
@ -116,25 +108,8 @@ public class InstancingEngine implements Engine {
}
@Override
public boolean updateRenderOrigin(Camera camera) {
Vec3 cameraPos = camera.getPosition();
double dx = renderOrigin.getX() - cameraPos.x;
double dy = renderOrigin.getY() - cameraPos.y;
double dz = renderOrigin.getZ() - cameraPos.z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
if (distanceSqr <= sqrMaxOriginDistance) {
return false;
}
renderOrigin = new BlockPos(cameraPos);
protected void onRenderOriginChanged() {
drawManager.clearInstancers();
return true;
}
@Override
public Vec3i renderOrigin() {
return renderOrigin;
}
@Override

View file

@ -19,7 +19,9 @@ import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager;
import com.jozufozu.flywheel.impl.visualization.manager.VisualManager;
import com.jozufozu.flywheel.lib.math.MatrixUtil;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -93,16 +95,20 @@ public class VisualWorld implements AutoCloseable {
taskExecutor.syncPoint();
if (!originChanged) {
Vec3i renderOrigin = engine.renderOrigin();
var cameraPos = context.camera()
.getPosition();
double cameraX = cameraPos.x;
double cameraY = cameraPos.y;
double cameraZ = cameraPos.z;
FrustumIntersection culler = context.culler();
blockEntities.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, culler);
entities.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, culler);
effects.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, culler);
org.joml.Matrix4f proj = MatrixUtil.toJoml(context.viewProjection());
proj.translate((float) (renderOrigin.getX() - cameraX), (float) (renderOrigin.getY() - cameraY), (float) (renderOrigin.getZ() - cameraZ));
FrustumIntersection frustum = new FrustumIntersection(proj);
blockEntities.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, frustum);
entities.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, frustum);
effects.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, frustum);
}
engine.beginFrame(taskExecutor, context);

View file

@ -69,10 +69,11 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
* represents should be rendered at to appear in the correct location.
*/
public BlockPos getVisualPosition() {
return pos.subtract(renderOrigin);
return visualPos;
}
public boolean isVisible(FrustumIntersection frustum) {
return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
return frustum.testAab(visualPos.getX(), visualPos.getY(), visualPos.getZ(),
visualPos.getX() + 1, visualPos.getY() + 1, visualPos.getZ() + 1);
}
}

View file

@ -76,7 +76,9 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
*/
public Vector3f getVisualPosition() {
Vec3 pos = entity.position();
return new Vector3f((float) (pos.x - renderOrigin.getX()), (float) (pos.y - renderOrigin.getY()), (float) (pos.z - renderOrigin.getZ()));
return new Vector3f((float) (pos.x - renderOrigin.getX()),
(float) (pos.y - renderOrigin.getY()),
(float) (pos.z - renderOrigin.getZ()));
}
/**
@ -88,11 +90,22 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
*/
public Vector3f getVisualPosition(float partialTicks) {
Vec3 pos = entity.position();
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()), (float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()), (float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()),
(float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()),
(float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
}
public boolean isVisible(FrustumIntersection frustum) {
AABB aabb = entity.getBoundingBox();
return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
if (entity.noCulling) {
return true;
}
AABB aabb = entity.getBoundingBoxForCulling();
return frustum.testAab((float) (aabb.minX - renderOrigin.getX()) - 0.5f,
(float) (aabb.minY - renderOrigin.getY()) - 0.5f,
(float) (aabb.minZ - renderOrigin.getZ()) - 0.5f,
(float) (aabb.maxX - renderOrigin.getX()) + 0.5f,
(float) (aabb.maxY - renderOrigin.getY()) + 0.5f,
(float) (aabb.maxZ - renderOrigin.getZ()) + 0.5f);
}
}

View file

@ -40,10 +40,7 @@ public class LevelRendererMixin {
@Inject(at = @At("HEAD"), method = "renderLevel")
private void flywheel$beginRender(PoseStack pPoseStack, float pPartialTick, long pFinishNanoTime, boolean pRenderBlockOutline, Camera pCamera, GameRenderer pGameRenderer, LightTexture pLightTexture, Matrix4f pProjectionMatrix, CallbackInfo ci) {
var viewProjection = RenderContext.createViewProjection(pPoseStack, pProjectionMatrix);
var cameraPos = pCamera.getPosition();
var culler = RenderContext.createCuller(viewProjection, (float) -cameraPos.x, (float) -cameraPos.y, (float) -cameraPos.z);
flywheel$renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, viewProjection, pProjectionMatrix, renderBuffers, pCamera, culler);
flywheel$renderContext = RenderContext.create((LevelRenderer) (Object) this, level, renderBuffers, pPoseStack, pProjectionMatrix, pCamera);
MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(flywheel$renderContext));
}

View file

@ -24,8 +24,8 @@ import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVisual<T> implements DynamicVisual, TickableVisual {
private static final SimpleLazyModel BODY_MODEL = new SimpleLazyModel(MinecartVisual::getBodyMesh, Materials.MINECART);
public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVisual<T> implements TickableVisual, DynamicVisual {
private static final SimpleLazyModel BODY_MODEL = new SimpleLazyModel(MinecartVisual::createBodyMesh, Materials.MINECART);
private final PoseStack stack = new PoseStack();
@ -47,11 +47,6 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
super.init();
}
@Override
public boolean decreaseFramerateWithDistance() {
return false;
}
@Override
public void tick() {
BlockState displayBlockState = entity.getDisplayBlockState();
@ -139,6 +134,11 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
body.setTransform(stack);
}
@Override
public boolean decreaseFramerateWithDistance() {
return false;
}
@Override
public void updateLight() {
if (contents == null) {
@ -156,6 +156,11 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
}
}
private TransformedInstance createBodyInstance() {
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, BODY_MODEL, RenderStage.AFTER_ENTITIES)
.createInstance();
}
private TransformedInstance createContentsInstance() {
RenderShape shape = blockState.getRenderShape();
@ -174,13 +179,8 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
.createInstance();
}
private TransformedInstance createBodyInstance() {
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, BODY_MODEL, RenderStage.AFTER_ENTITIES)
.createInstance();
}
@NotNull
private static ModelPart getBodyMesh() {
private static ModelPart createBodyMesh() {
int y = -3;
return ModelPart.builder("minecart", 64, 32)
.cuboid().invertYZ().start(-10, -8, -y).size(20, 16, 2).textureOffset(0, 10).rotateZ((float) Math.PI).rotateX(((float)Math.PI / 2F)).endCuboid()

View file

@ -72,6 +72,14 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
super.init();
}
private Direction getDirection() {
if (blockState.getBlock() instanceof ShulkerBoxBlock) {
return blockState.getValue(ShulkerBoxBlock.FACING);
}
return Direction.UP;
}
@Override
public void beginFrame() {
float progress = blockEntity.getProgress(AnimationTickHolder.getPartialTicks());
@ -139,12 +147,4 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
.endCuboid()
.build();
}
private Direction getDirection() {
if (blockState.getBlock() instanceof ShulkerBoxBlock) {
return blockState.getValue(ShulkerBoxBlock.FACING);
}
return Direction.UP;
}
}