diff --git a/src/main/java/com/jozufozu/flywheel/lib/material/StandardMaterialShaders.java b/src/main/java/com/jozufozu/flywheel/lib/material/StandardMaterialShaders.java index 7937f7a87..a8a8aa3b0 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/material/StandardMaterialShaders.java +++ b/src/main/java/com/jozufozu/flywheel/lib/material/StandardMaterialShaders.java @@ -12,6 +12,8 @@ public final class StandardMaterialShaders { public static final MaterialShaders WIREFRAME = MaterialShaders.REGISTRY.registerAndGet(new SimpleMaterialShaders(Flywheel.rl("material/wireframe.vert"), Flywheel.rl("material/wireframe.frag"))); + public static final MaterialShaders CENTERLINE = MaterialShaders.REGISTRY.registerAndGet(new SimpleMaterialShaders(Flywheel.rl("material/centerline.vert"), Flywheel.rl("material/centerline.frag"))); + private StandardMaterialShaders() { } diff --git a/src/main/java/com/jozufozu/flywheel/lib/visual/SmartRecycler.java b/src/main/java/com/jozufozu/flywheel/lib/visual/SmartRecycler.java new file mode 100644 index 000000000..0a76c91f3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/lib/visual/SmartRecycler.java @@ -0,0 +1,37 @@ +package com.jozufozu.flywheel.lib.visual; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import com.jozufozu.flywheel.api.instance.Instance; + +public class SmartRecycler { + private final Function factory; + private final Map> recyclers = new HashMap<>(); + + public SmartRecycler(Function factory) { + this.factory = factory; + } + + public void resetCount() { + recyclers.values() + .forEach(InstanceRecycler::resetCount); + } + + public I get(K key) { + return recyclers.computeIfAbsent(key, k -> new InstanceRecycler<>(() -> factory.apply(k))) + .get(); + } + + public void discardExtra() { + recyclers.values() + .forEach(InstanceRecycler::discardExtra); + } + + public void delete() { + recyclers.values() + .forEach(InstanceRecycler::delete); + recyclers.clear(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/lib/visual/components/BoundingBoxComponent.java b/src/main/java/com/jozufozu/flywheel/lib/visual/components/BoundingBoxComponent.java index 72a61f0a6..789deb3b2 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/visual/components/BoundingBoxComponent.java +++ b/src/main/java/com/jozufozu/flywheel/lib/visual/components/BoundingBoxComponent.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.lib.visual.components; +import org.joml.Quaternionf; import org.joml.Vector4f; import org.joml.Vector4fc; @@ -16,7 +17,7 @@ import com.jozufozu.flywheel.lib.math.MoreMath; import com.jozufozu.flywheel.lib.model.QuadMesh; import com.jozufozu.flywheel.lib.model.SingleMeshModel; import com.jozufozu.flywheel.lib.visual.EntityComponent; -import com.jozufozu.flywheel.lib.visual.InstanceRecycler; +import com.jozufozu.flywheel.lib.visual.SmartRecycler; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LightTexture; @@ -25,30 +26,38 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; public class BoundingBoxComponent implements EntityComponent { - private static final Material MATERIAL = SimpleMaterial.builder() + private static final Material WIREFRAME = SimpleMaterial.builder() .shaders(StandardMaterialShaders.WIREFRAME) .backfaceCulling(false) .build(); - private static final Model MODEL = new SingleMeshModel(BoundingBoxMesh.INSTANCE, MATERIAL); + + private static final Material CENTERLINE = SimpleMaterial.builder() + .shaders(StandardMaterialShaders.CENTERLINE) + .backfaceCulling(false) + .build(); + private static final Model BOX = new SingleMeshModel(BoundingBoxMesh.INSTANCE, WIREFRAME); + + // Should we try a single quad oriented to face the camera instead? + private static final Model LINE = new SingleMeshModel(BoundingBoxMesh.INSTANCE, CENTERLINE); private final VisualizationContext context; private final Entity entity; private boolean showEyeBox; - private final InstanceRecycler recycler; + private final SmartRecycler recycler; public BoundingBoxComponent(VisualizationContext context, Entity entity) { this.context = context; this.entity = entity; this.showEyeBox = entity instanceof LivingEntity; - this.recycler = new InstanceRecycler<>(this::createInstance); + this.recycler = new SmartRecycler<>(this::createInstance); } - private TransformedInstance createInstance() { + private TransformedInstance createInstance(Model model) { TransformedInstance instance = context.instancerProvider() - .instancer(InstanceTypes.TRANSFORMED, MODEL) + .instancer(InstanceTypes.TRANSFORMED, model) .createInstance(); instance.setBlockLight(LightTexture.block(LightTexture.FULL_BLOCK)); instance.setChanged(); @@ -76,22 +85,33 @@ public class BoundingBoxComponent implements EntityComponent { var bbWidth = entity.getBbWidth(); var bbHeight = entity.getBbHeight(); var bbWidthHalf = bbWidth * 0.5; - recycler.get() + recycler.get(BOX) .loadIdentity() .translate(entityX - bbWidthHalf, entityY, entityZ - bbWidthHalf) .scale(bbWidth, bbHeight, bbWidth) .setChanged(); - // TODO: multipart entities and view vectors + // TODO: multipart entities, but forge seems to have an + // injection for them so we'll need platform specific code. if (showEyeBox) { - recycler.get() + recycler.get(BOX) .loadIdentity() .translate(entityX - bbWidthHalf, entityY + entity.getEyeHeight() - 0.01, entityZ - bbWidthHalf) .scale(bbWidth, 0.02f, bbWidth) .setColor(255, 0, 0) .setChanged(); } + + var viewVector = entity.getViewVector(context.partialTick()); + + recycler.get(LINE) + .loadIdentity() + .translate(entityX, entityY + entity.getEyeHeight(), entityZ) + .rotate(new Quaternionf().rotateTo(0, 1, 0, (float) viewVector.x, (float) viewVector.y, (float) viewVector.z)) + .scale(0.02f, 2f, 0.02f) + .setColor(0, 0, 255) + .setChanged(); } recycler.discardExtra(); diff --git a/src/main/java/com/jozufozu/flywheel/lib/visual/components/FireComponent.java b/src/main/java/com/jozufozu/flywheel/lib/visual/components/FireComponent.java index 3b4a04bca..7ea47f044 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/visual/components/FireComponent.java +++ b/src/main/java/com/jozufozu/flywheel/lib/visual/components/FireComponent.java @@ -4,6 +4,7 @@ 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; @@ -15,7 +16,7 @@ import com.jozufozu.flywheel.lib.model.ModelCache; import com.jozufozu.flywheel.lib.model.QuadMesh; import com.jozufozu.flywheel.lib.model.SingleMeshModel; import com.jozufozu.flywheel.lib.visual.EntityComponent; -import com.jozufozu.flywheel.lib.visual.InstanceRecycler; +import com.jozufozu.flywheel.lib.visual.SmartRecycler; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; @@ -42,20 +43,18 @@ public class FireComponent implements EntityComponent { private final Entity entity; private final PoseStack stack = new PoseStack(); - private final InstanceRecycler fire0; - private final InstanceRecycler fire1; + private final SmartRecycler recycler; public FireComponent(VisualizationContext context, Entity entity) { this.context = context; this.entity = entity; - fire0 = new InstanceRecycler<>(() -> createInstance(ModelBakery.FIRE_0)); - fire1 = new InstanceRecycler<>(() -> createInstance(ModelBakery.FIRE_1)); + recycler = new SmartRecycler<>(this::createInstance); } - private TransformedInstance createInstance(net.minecraft.client.resources.model.Material texture) { + private TransformedInstance createInstance(Model model) { TransformedInstance instance = context.instancerProvider() - .instancer(InstanceTypes.TRANSFORMED, FIRE_MODELS.get(texture)) + .instancer(InstanceTypes.TRANSFORMED, model) .createInstance(); instance.setBlockLight(LightTexture.block(LightTexture.FULL_BLOCK)); instance.setChanged(); @@ -70,15 +69,13 @@ public class FireComponent implements EntityComponent { */ @Override public void beginFrame(VisualFrameContext context) { - fire0.resetCount(); - fire1.resetCount(); + recycler.resetCount(); if (entity.displayFireAnimation()) { setupInstances(context); } - fire0.discardExtra(); - fire1.discardExtra(); + recycler.discardExtra(); } private void setupInstances(VisualFrameContext context) { @@ -101,7 +98,7 @@ public class FireComponent implements EntityComponent { stack.translate(0.0F, 0.0F, -0.3F + (float) ((int) maxHeight) * 0.02F); for (int i = 0; y < maxHeight; ++i) { - var instance = (i % 2 == 0 ? this.fire0 : this.fire1).get() + var instance = recycler.get(FIRE_MODELS.get(i % 2 == 0 ? ModelBakery.FIRE_0 : ModelBakery.FIRE_1)) .setTransform(stack) .scaleX(width) .translate(0, y, z); @@ -123,8 +120,7 @@ public class FireComponent implements EntityComponent { @Override public void delete() { - fire0.delete(); - fire1.delete(); + recycler.delete(); } private record FireMesh(TextureAtlasSprite sprite) implements QuadMesh { diff --git a/src/main/resources/assets/flywheel/flywheel/material/centerline.frag b/src/main/resources/assets/flywheel/flywheel/material/centerline.frag new file mode 100644 index 000000000..e40b80e60 --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/material/centerline.frag @@ -0,0 +1,10 @@ +void flw_materialFragment() { + float toCenter = abs(flw_vertexTexCoord.s - 0.5); + + // multiply by fwidth to get the width of the edge in screen space + if (flw_defaultLineWidth * fwidth(toCenter) < toCenter) { + discard; + } + + flw_fragColor = flw_vertexColor; +} diff --git a/src/main/resources/assets/flywheel/flywheel/material/centerline.vert b/src/main/resources/assets/flywheel/flywheel/material/centerline.vert new file mode 100644 index 000000000..5d92e7ffb --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/material/centerline.vert @@ -0,0 +1,2 @@ +void flw_materialVertex() { +}