Lines are hard

- Add BoundingBoxComponent for drawing entity bounding boxes.
- Render lines with a wireframe material.
- Add viewport size and default line width uniforms.
This commit is contained in:
Jozufozu 2024-02-01 16:56:01 -08:00
parent c5687ccf43
commit cdfe144131
8 changed files with 203 additions and 3 deletions

View file

@ -7,11 +7,12 @@ import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.lib.math.MatrixMath;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
public class FrameUniforms implements UniformProvider {
public static final int SIZE = 220;
public static final int SIZE = 232;
private RenderContext context;
@ -51,10 +52,17 @@ public class FrameUniforms implements UniformProvider {
writeVec3(ptr + 176, lookVector.x, lookVector.y, lookVector.z);
writeVec2(ptr + 192, camera.getXRot(), camera.getYRot());
var window = Minecraft.getInstance()
.getWindow();
MemoryUtil.memPutInt(ptr + 200, getConstantAmbientLightFlag(context));
writeVec2(ptr + 200, window.getWidth(), window.getHeight());
writeTime(ptr + 204);
// default line width: net.minecraft.client.renderer.RenderStateShard.LineStateShard
MemoryUtil.memPutFloat(ptr + 208, Math.max(2.5F, (float) window.getWidth() / 1920.0F * 2.5F));
MemoryUtil.memPutInt(ptr + 212, getConstantAmbientLightFlag(context));
writeTime(ptr + 216);
}
private void writeTime(long ptr) {

View file

@ -10,6 +10,8 @@ public final class StandardMaterialShaders {
Flywheel.rl("material/default.vert"),
Flywheel.rl("material/default.frag")));
public static final MaterialShaders WIREFRAME = MaterialShaders.REGISTRY.registerAndGet(new SimpleMaterialShaders(Flywheel.rl("material/wireframe.vert"), Flywheel.rl("material/wireframe.frag")));
private StandardMaterialShaders() {
}

View file

@ -35,6 +35,7 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
protected final EntityVisibilityTester visibilityTester;
protected final ShadowComponent shadow;
protected final FireComponent fire;
protected final BoundingBoxComponent boundingBox;
public AbstractEntityVisual(VisualizationContext ctx, T entity) {
super(ctx, entity.level());
@ -42,6 +43,7 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
visibilityTester = new EntityVisibilityTester(entity, ctx.renderOrigin(), 1.5f);
shadow = new ShadowComponent(ctx, entity);
fire = new FireComponent(ctx, entity);
boundingBox = new BoundingBoxComponent(ctx, entity);
}
@Override
@ -96,5 +98,6 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
protected void _delete() {
shadow.delete();
fire.delete();
boundingBox.delete();
}
}

View file

@ -0,0 +1,170 @@
package com.jozufozu.flywheel.lib.visual;
import org.joml.Vector4f;
import org.joml.Vector4fc;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
import com.jozufozu.flywheel.lib.instance.TransformedInstance;
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
import com.jozufozu.flywheel.lib.material.StandardMaterialShaders;
import com.jozufozu.flywheel.lib.math.MoreMath;
import com.jozufozu.flywheel.lib.model.QuadMesh;
import com.jozufozu.flywheel.lib.model.SingleMeshModel;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
public class BoundingBoxComponent {
private static final Material MATERIAL = SimpleMaterial.builder()
.shaders(StandardMaterialShaders.WIREFRAME)
.backfaceCulling(false)
.build();
private static final Model MODEL = new SingleMeshModel(BoundingBoxMesh.INSTANCE, MATERIAL);
private final VisualizationContext context;
private final Entity entity;
private boolean showEyeBox;
private final InstanceRecycler<TransformedInstance> recycler;
public BoundingBoxComponent(VisualizationContext context, Entity entity) {
this.context = context;
this.entity = entity;
this.showEyeBox = entity instanceof LivingEntity;
this.recycler = new InstanceRecycler<>(this::createInstance);
}
private TransformedInstance createInstance() {
TransformedInstance instance = context.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, MODEL)
.createInstance();
instance.setBlockLight(LightTexture.block(LightTexture.FULL_BLOCK));
instance.setChanged();
return instance;
}
public void showEyeBox(boolean renderEyeBox) {
this.showEyeBox = renderEyeBox;
}
public void beginFrame(VisualFrameContext context) {
recycler.resetCount();
var shouldRenderHitBoxes = Minecraft.getInstance()
.getEntityRenderDispatcher()
.shouldRenderHitBoxes();
if (shouldRenderHitBoxes && !entity.isInvisible() && !Minecraft.getInstance()
.showOnlyReducedInfo()) {
double entityX = Mth.lerp(context.partialTick(), entity.xOld, entity.getX());
double entityY = Mth.lerp(context.partialTick(), entity.yOld, entity.getY());
double entityZ = Mth.lerp(context.partialTick(), entity.zOld, entity.getZ());
var bbWidth = entity.getBbWidth();
var bbHeight = entity.getBbHeight();
var bbWidthHalf = bbWidth * 0.5;
recycler.get()
.loadIdentity()
.translate(entityX - bbWidthHalf, entityY, entityZ - bbWidthHalf)
.scale(bbWidth, bbHeight, bbWidth)
.setChanged();
// TODO: multipart entities and view vectors
if (showEyeBox) {
recycler.get()
.loadIdentity()
.translate(entityX - bbWidthHalf, entityY + entity.getEyeHeight() - 0.01, entityZ - bbWidthHalf)
.scale(bbWidth, 0.02f, bbWidth)
.setColor(255, 0, 0)
.setChanged();
}
}
recycler.discardExtra();
}
public void delete() {
recycler.delete();
}
private static class BoundingBoxMesh implements QuadMesh {
private static final BoundingBoxMesh INSTANCE = new BoundingBoxMesh();
private static final Vector4fc BOUNDING_SPHERE = new Vector4f(0.5f, 0.5f, 0.5f, MoreMath.SQRT_3_OVER_2);
private BoundingBoxMesh() {
}
@Override
public int vertexCount() {
return 24;
}
@Override
public void write(MutableVertexList vertexList) {
// very cursed, maybe we should use vanilla ModelParts instead?
writeVertex(vertexList, 0, 0, 0, 0, 0, 0);
writeVertex(vertexList, 1, 1, 0, 0, 1, 0);
writeVertex(vertexList, 2, 1, 1, 0, 1, 1);
writeVertex(vertexList, 3, 0, 1, 0, 0, 1);
writeVertex(vertexList, 4, 0, 0, 1, 0, 0);
writeVertex(vertexList, 5, 0, 0, 0, 1, 0);
writeVertex(vertexList, 6, 0, 1, 0, 1, 1);
writeVertex(vertexList, 7, 0, 1, 1, 0, 1);
writeVertex(vertexList, 8, 0, 1, 0, 0, 0);
writeVertex(vertexList, 9, 1, 1, 0, 1, 0);
writeVertex(vertexList, 10, 1, 1, 1, 1, 1);
writeVertex(vertexList, 11, 0, 1, 1, 0, 1);
writeVertex(vertexList, 12, 1, 0, 1, 0, 0);
writeVertex(vertexList, 13, 0, 0, 1, 1, 0);
writeVertex(vertexList, 14, 0, 1, 1, 1, 1);
writeVertex(vertexList, 15, 1, 1, 1, 0, 1);
writeVertex(vertexList, 16, 1, 0, 0, 0, 0);
writeVertex(vertexList, 17, 1, 0, 1, 1, 0);
writeVertex(vertexList, 18, 1, 1, 1, 1, 1);
writeVertex(vertexList, 19, 1, 1, 0, 0, 1);
writeVertex(vertexList, 20, 0, 0, 0, 0, 0);
writeVertex(vertexList, 21, 1, 0, 0, 1, 0);
writeVertex(vertexList, 22, 1, 0, 1, 1, 1);
writeVertex(vertexList, 23, 0, 0, 1, 0, 1);
}
@Override
public Vector4fc boundingSphere() {
return BOUNDING_SPHERE;
}
@Override
public void delete() {
}
private static void writeVertex(MutableVertexList vertexList, int i, float x, float y, float z, float u, float v) {
vertexList.x(i, x);
vertexList.y(i, y);
vertexList.z(i, z);
vertexList.r(i, 1);
vertexList.g(i, 1);
vertexList.b(i, 1);
vertexList.u(i, u);
vertexList.v(i, v);
vertexList.light(i, LightTexture.FULL_BRIGHT);
vertexList.normalX(i, 0);
vertexList.normalY(i, 1);
vertexList.normalZ(i, 0);
}
}
}

View file

@ -111,6 +111,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
public void beginFrame(VisualFrameContext context) {
shadow.beginFrame(context);
fire.beginFrame(context);
boundingBox.beginFrame(context);
if (!isVisible(context.frustum())) {
return;

View file

@ -15,6 +15,10 @@ layout(std140) uniform _FlwFrameUniforms {
vec4 flw_cameraPos;
vec4 flw_cameraLook;
vec2 flw_cameraRot;
vec2 flw_viewportSize;
float flw_defaultLineWidth;
uint flw_constantAmbientLight;
uint flw_ticks;

View file

@ -0,0 +1,10 @@
void flw_materialFragment() {
float closestEdge = min(flw_vertexTexCoord.s, min(flw_vertexTexCoord.t, min(1. - flw_vertexTexCoord.s, 1. - flw_vertexTexCoord.t)));
// multiply by fwidth to get the width of the edge in screen space
if (flw_defaultLineWidth * fwidth(closestEdge) < closestEdge) {
discard;
}
flw_fragColor = flw_vertexColor;
}

View file

@ -0,0 +1,2 @@
void flw_materialVertex() {
}