From 400bd74e6af3212a91f67859dfe5324037b7bc5f Mon Sep 17 00:00:00 2001 From: hotpad100c <46704388+hotpad100c@users.noreply.github.com> Date: Sun, 9 Nov 2025 12:43:34 +0900 Subject: [PATCH 1/6] New transform system --- src/main/java/mypals/ml/Helpers.java | 25 ++ .../ml/builderManager/BuilderManager.java | 5 +- .../builders/shapeBuilders/BaseBuilder.java | 9 +- .../shapeBuilders/ObjModelBuilder.java | 3 +- .../shapeBuilders/ObjModelOutlineBuilder.java | 4 +- .../builders/shapeBuilders/SphereBuilder.java | 7 +- .../shapeBuilders/StripLineBuilder.java | 3 +- src/main/java/mypals/ml/shape/Shape.java | 234 +++++++------- src/main/java/mypals/ml/shape/Shape.md | 45 +++ src/main/java/mypals/ml/shape/ShapeForAI.md | 33 ++ .../mypals/ml/shape/basics/BoxLikeShape.java | 40 ++- .../ml/shape/basics/CircleLikeShape.java | 4 +- .../ml/shape/basics/core/LineLikeShape.java | 37 ++- .../shape/basics/core/TwoPointsLineShape.java | 63 ++-- .../mypals/ml/shape/box/BoxFaceShape.java | 98 +++--- .../java/mypals/ml/shape/box/BoxShape.java | 176 +++++------ .../ml/shape/box/BoxWireframeShape.java | 134 ++++---- .../ml/shape/box/WireframedBoxShape.java | 22 +- .../mypals/ml/shape/cylinder/ConeShape.java | 97 +++--- .../ml/shape/cylinder/ConeWireframeShape.java | 93 +++--- .../ml/shape/cylinder/CylinderShape.java | 259 +++++++--------- .../cylinder/CylinderWireframeShape.java | 152 ++++----- .../java/mypals/ml/shape/line/LineShape.java | 106 ++++--- .../mypals/ml/shape/line/StripLineShape.java | 155 ++++----- .../mypals/ml/shape/model/ObjModelShape.java | 140 +++++---- .../ml/shape/model/ObjModelShapeOutline.java | 86 +++-- .../ml/shape/round/FaceCircleShape.java | 200 +++++------- .../ml/shape/round/LineCircleShape.java | 252 +++++++++------ .../mypals/ml/shape/round/SphereShape.java | 293 +++--------------- .../java/mypals/ml/shape/text/TextShape.java | 6 +- .../mypals/ml/shapeManagers/ShapeManager.java | 4 +- .../ml/shapeManagers/ShapeManagers.java | 6 +- .../ml/shapeManagers/VertexBuilderGetter.java | 6 +- src/main/java/mypals/ml/test/Tester.java | 131 ++++++-- .../ml/transform/FloatValueTransformer.java | 30 -- .../ml/transform/IntValueTransformer.java | 38 --- .../ml/transform/QuaternionTransformer.java | 32 -- .../mypals/ml/transform/Vec3dTransformer.java | 34 -- .../shapeTransformers/DefaultTransformer.java | 168 ++++++++++ .../shapeTransformers/ModelInfoLayer.java | 10 + .../shapeTransformers/TransformLayer.java | 38 +++ .../BoxModelInfo.java | 30 ++ .../CircleModelInfo.java | 42 +++ .../LineModelInfo.java | 67 ++++ .../valueTransformers/FloatTransformer.java | 30 ++ .../valueTransformers/IntTransformer.java | 32 ++ .../QuaternionTransformer.java | 28 ++ .../valueTransformers/ValueTransformer.java | 23 ++ .../valueTransformers/Vec3Transformer.java | 32 ++ 49 files changed, 1952 insertions(+), 1610 deletions(-) create mode 100644 src/main/java/mypals/ml/shape/Shape.md create mode 100644 src/main/java/mypals/ml/shape/ShapeForAI.md delete mode 100644 src/main/java/mypals/ml/transform/FloatValueTransformer.java delete mode 100644 src/main/java/mypals/ml/transform/IntValueTransformer.java delete mode 100644 src/main/java/mypals/ml/transform/QuaternionTransformer.java delete mode 100644 src/main/java/mypals/ml/transform/Vec3dTransformer.java create mode 100644 src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java create mode 100644 src/main/java/mypals/ml/transform/shapeTransformers/ModelInfoLayer.java create mode 100644 src/main/java/mypals/ml/transform/shapeTransformers/TransformLayer.java create mode 100644 src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/BoxModelInfo.java create mode 100644 src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/CircleModelInfo.java create mode 100644 src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java create mode 100644 src/main/java/mypals/ml/transform/valueTransformers/FloatTransformer.java create mode 100644 src/main/java/mypals/ml/transform/valueTransformers/IntTransformer.java create mode 100644 src/main/java/mypals/ml/transform/valueTransformers/QuaternionTransformer.java create mode 100644 src/main/java/mypals/ml/transform/valueTransformers/ValueTransformer.java create mode 100644 src/main/java/mypals/ml/transform/valueTransformers/Vec3Transformer.java diff --git a/src/main/java/mypals/ml/Helpers.java b/src/main/java/mypals/ml/Helpers.java index 2cf88ed..377f5f3 100644 --- a/src/main/java/mypals/ml/Helpers.java +++ b/src/main/java/mypals/ml/Helpers.java @@ -2,6 +2,7 @@ import java.util.concurrent.ThreadLocalRandom; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.Vec3; import static mypals.ml.RyansRenderingKit.MOD_ID; @@ -11,4 +12,28 @@ public static ResourceLocation generateUniqueId(String prefix) { int randomNum = ThreadLocalRandom.current().nextInt(10000); return ResourceLocation.fromNamespaceAndPath(MOD_ID,prefix.toLowerCase() +"_"+ timestamp + "_" + randomNum); } + public static Vec3 max(Vec3 a, Vec3 b) { + return new Vec3( + Math.max(a.x, b.x), + Math.max(a.y, b.y), + Math.max(a.z, b.z) + ); + } + + public static Vec3 min(Vec3 a, Vec3 b) { + return new Vec3( + Math.min(a.x, b.x), + Math.min(a.y, b.y), + Math.min(a.z, b.z) + ); + } + + + public static double maxComponent(Vec3 v) { + return Math.max(Math.max(v.x, v.y), v.z); + } + + public static double minComponent(Vec3 v) { + return Math.min(Math.min(v.x, v.y), v.z); + } } diff --git a/src/main/java/mypals/ml/builderManager/BuilderManager.java b/src/main/java/mypals/ml/builderManager/BuilderManager.java index 2148be9..f85708d 100644 --- a/src/main/java/mypals/ml/builderManager/BuilderManager.java +++ b/src/main/java/mypals/ml/builderManager/BuilderManager.java @@ -1,5 +1,6 @@ package mypals.ml.builderManager; +import com.mojang.blaze3d.vertex.PoseStack; import mypals.ml.builders.vertexBuilders.BatchVertexBuilder; import mypals.ml.builders.vertexBuilders.BufferedVertexBuilder; import mypals.ml.builders.vertexBuilders.ImmediateVertexBuilder; @@ -73,7 +74,7 @@ public void rebuildVBO(Collection shapeList, boolean seeThrough){ sortedShapes.sort(SHAPE_ORDER_COMPARATOR); for(Shape shape: sortedShapes){ - shape.draw(builder); + shape.draw(builder,new PoseStack(),1); } }); else normalBuilderGroup.bufferedVertexBuilder.rebuild(renderMethod, builder->{ @@ -82,7 +83,7 @@ public void rebuildVBO(Collection shapeList, boolean seeThrough){ sortedShapes.sort(SHAPE_ORDER_COMPARATOR); for(Shape shape: sortedShapes){ - shape.draw(builder); + shape.draw(builder,new PoseStack(),1); } }); } diff --git a/src/main/java/mypals/ml/builders/shapeBuilders/BaseBuilder.java b/src/main/java/mypals/ml/builders/shapeBuilders/BaseBuilder.java index 3dc9f56..5de562f 100644 --- a/src/main/java/mypals/ml/builders/shapeBuilders/BaseBuilder.java +++ b/src/main/java/mypals/ml/builders/shapeBuilders/BaseBuilder.java @@ -5,6 +5,7 @@ import java.awt.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public abstract class BaseBuilder, R> implements ShapeBuilder { @@ -12,7 +13,7 @@ public abstract class BaseBuilder, R> implements Sha protected Color color = Color.WHITE; protected boolean seeThrough = false; - private TransformerSupplier transformerSupplier = () -> (t, s) -> {}; + private TransformerSupplier transformerSupplier = () ->(t)-> {}; @SuppressWarnings("unchecked") protected T self() { @@ -24,7 +25,7 @@ protected T self() { public T color(int color) {return this.color(new Color(color)); } @Override public T seeThrough(boolean seeThrough) { this.seeThrough = seeThrough; return self(); } - public T transform(BiConsumer transformer) { + public T transform(Consumer transformer) { this.transformerSupplier = () -> transformer; return self(); } @@ -34,7 +35,7 @@ public T transform(TransformerSupplier supplier) { return self(); } - protected BiConsumer getTransformer() { + protected Consumer getTransformer() { return transformerSupplier.get(); } @@ -42,6 +43,6 @@ protected BiConsumer getTransformer() { @FunctionalInterface public interface TransformerSupplier { - BiConsumer get(); + Consumer get(); } } diff --git a/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelBuilder.java b/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelBuilder.java index edb5783..22a2d21 100644 --- a/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelBuilder.java +++ b/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelBuilder.java @@ -2,11 +2,12 @@ import mypals.ml.shape.Shape; import mypals.ml.shape.model.ObjModelShape; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.minecraft.resources.ResourceLocation; import java.util.function.BiConsumer; -public class ObjModelBuilder extends BaseBuilder { +public class ObjModelBuilder extends BaseBuilder { private ResourceLocation resourceLocation; public ObjModelBuilder model(ResourceLocation resourceLocation) { diff --git a/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelOutlineBuilder.java b/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelOutlineBuilder.java index c6f7600..3572552 100644 --- a/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelOutlineBuilder.java +++ b/src/main/java/mypals/ml/builders/shapeBuilders/ObjModelOutlineBuilder.java @@ -5,9 +5,7 @@ import mypals.ml.shape.model.ObjModelShapeOutline; import net.minecraft.resources.ResourceLocation; -import java.util.function.BiConsumer; - -public class ObjModelOutlineBuilder extends BaseBuilder { +public class ObjModelOutlineBuilder extends BaseBuilder { private ResourceLocation resourceLocation; private float lineWidth = 1.0f; diff --git a/src/main/java/mypals/ml/builders/shapeBuilders/SphereBuilder.java b/src/main/java/mypals/ml/builders/shapeBuilders/SphereBuilder.java index d78edde..6956fb0 100644 --- a/src/main/java/mypals/ml/builders/shapeBuilders/SphereBuilder.java +++ b/src/main/java/mypals/ml/builders/shapeBuilders/SphereBuilder.java @@ -1,16 +1,15 @@ package mypals.ml.builders.shapeBuilders; import mypals.ml.shape.Shape; +import mypals.ml.shape.round.FaceCircleShape; import mypals.ml.shape.round.SphereShape; import java.util.function.BiConsumer; -public class SphereBuilder extends BaseBuilder { - private SphereShape.SphereMode mode = SphereShape.SphereMode.UV; +public class SphereBuilder extends BaseBuilder { private int segments = 32; private float radius = 1.0f; - public SphereBuilder mode(SphereShape.SphereMode mode) { this.mode = mode; return this; } public SphereBuilder segments(int segments) { this.segments = segments; return this; } public SphereBuilder radius(float radius) { this.radius = radius; return this; } @@ -18,6 +17,6 @@ public class SphereBuilder extends BaseBuilder { +public class StripLineBuilder extends BaseBuilder { private final List vertexes = new ArrayList<>(); private final List colors = new ArrayList<>(); private float lineWidth = 1.0f; diff --git a/src/main/java/mypals/ml/shape/Shape.java b/src/main/java/mypals/ml/shape/Shape.java index 7baf691..0e24099 100644 --- a/src/main/java/mypals/ml/shape/Shape.java +++ b/src/main/java/mypals/ml/shape/Shape.java @@ -1,146 +1,154 @@ package mypals.ml.shape; import mypals.ml.builders.vertexBuilders.VertexBuilder; -import mypals.ml.shape.basics.tags.ExtractableShape; import mypals.ml.shapeManagers.ShapeManagers; -import mypals.ml.transform.Vec3dTransformer; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.phys.Vec3; -import mypals.ml.transform.QuaternionTransformer; +import org.joml.Matrix4f; import org.joml.Quaternionf; -import org.joml.Vector3f; import com.mojang.blaze3d.vertex.PoseStack; -import java.util.function.BiConsumer; +import org.joml.Vector3f; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; public abstract class Shape { - public enum RenderingType { - IMMEDIATE, // Immediately rendered - BATCH, // Stored in bufferBuilder, batch uploads every frame - BUFFERED // Stored in VBO - } + public enum RenderingType { IMMEDIATE, BATCH, BUFFERED } + public ResourceLocation id; public final RenderingType type; - public BiConsumer transformFunction; public DefaultTransformer transformer; - public boolean seeThrough; - public Vec3 centerPoint = Vec3.ZERO; + public Consumer transformFunction; - protected Shape(RenderingType type,BiConsumer transform) { - this(type); - this.transformFunction = (transformer, shape) -> transform.accept(this.transformer,shape); - } - protected Shape(RenderingType type) { - this(type,false); + public Shape parent; + public List children = new ArrayList<>(); + + public boolean visible = true; + public Color baseColor = Color.WHITE; + public boolean seeThrough = false; + + public List model_vertexes = new ArrayList<>();//This is the original model of our model. + public int[] indexBuffer = new int[0]; + + + protected Shape(RenderingType type, Consumer transform,Color color, Vec3 center, boolean seeThrough) { + this(type,color,seeThrough); + this.transformer = new DefaultTransformer(this,center); + this.transformFunction = transform; } - protected Shape(RenderingType type,boolean seeThrough) { + protected Shape(RenderingType type,Color color, boolean seeThrough) { this.type = type; this.seeThrough = seeThrough; + this.baseColor = color; } - protected Shape(RenderingType type,BiConsumer transform,boolean seeThrough) { - this(type,seeThrough); - this.transformFunction = (transformer, shape) -> transform.accept(this.transformer,shape); - } - public DefaultTransformer getTransformer() { - return transformer; - } - public void setId(ResourceLocation identifier){ - this.id = identifier; - } - public void draw(VertexBuilder builder, PoseStack matrixStack, float deltaTime) { - matrixStack.pushPose(); - beforeDraw(matrixStack,deltaTime); - builder.setPositionMatrix(matrixStack.last().pose()); - draw(builder); - matrixStack.popPose(); - } - public void discard(){ - ShapeManagers.removeShape(this.id); - } - public void draw(VertexBuilder builder) { - if(this instanceof ExtractableShape) throw new UnsupportedOperationException("This shape cant be rendered directly, use addGroup to extract it."); - } - public void beforeDraw(PoseStack matrixStack,float deltaTime){ - transformer.updateTickDelta(deltaTime); - transformFunction.accept(transformer,this); - transformer.applyTransformations(matrixStack); - } - public void setMatrixScale(Vector3f scale, PoseStack matrixStack){ - matrixStack.scale(scale.x,scale.y,scale.z); - } - public void setMatrixRotation(Quaternionf rotation, PoseStack matrixStack){ - matrixStack.mulPose(rotation); - } - public void setMatrixCenterPos(Vec3 pos, PoseStack matrixStack){ - matrixStack.translate(pos.x, pos.y, pos.z); - } - public Vec3 calculateShapeCenterPos(){ - return this.centerPoint; + public List getChildren(){ + return this.children; } - public Vec3 getShapeCenterPos(){ - return this.centerPoint; + public void addChild(Shape shape){ + shape.setParent(this); + children.add(shape); } - public void setShapeCenterPos(Vec3 pos){ - this.centerPoint = pos; - } - public void syncLastToTarget(){ - this.transformer.syncLastToTarget(); - } - public static class DefaultTransformer{ - public float delta = 0; - public Shape managedShape; - public Vec3dTransformer shapeCenterTransformer = new Vec3dTransformer(); - public Vec3dTransformer matrixCenterTransformer = new Vec3dTransformer(); - public QuaternionTransformer matrixRotationTransformer = new QuaternionTransformer(); - public Vec3dTransformer matrixScaleTransformer = new Vec3dTransformer(Vec3.ZERO.add(1)); - public DefaultTransformer(Shape managerShape){ - this.managedShape = managerShape; - } - public float getTickDelta(){ - return this.delta; + public void setParent(Shape parent){ + if(this.parent != null){ + this.parent.children.remove(this); } - public void updateTickDelta(float delta){ - this.delta = delta; - } - public void applyTransformations(PoseStack matrixStack) { - float deltaTime = getTickDelta(); - shapeCenterTransformer.updateVector(pos -> managedShape.setShapeCenterPos(pos), deltaTime); - matrixCenterTransformer.updateVector(pos -> managedShape.setMatrixCenterPos(pos, matrixStack), deltaTime); + this.parent = parent; + } + public void setLocalPosition(Vec3 pos) { this.transformer.setShapeLocalPivot(pos); } + public void setLocalRotation(Vector3f rot) { this.transformer.setShapeLocalRotationDegrees(rot.x,rot.y,rot.z); } + public void setLocalScale(Vec3 scale) { this.transformer.setShapeLocalScale(scale); } + + public void setWorldPosition(Vec3 pos) {this.transformer.setShapeWorldPivot(pos);} + + public void setWorldRotation(Vector3f rot) {this.transformer.setShapeWorldRotationDegrees(rot.x, rot.y, rot.z);} + + public void setWorldScale(Vec3 scale) {this.transformer.setShapeWorldScale(scale);} + + public void setRenderPivot(Vec3 pos) {this.transformer.matrix.position.setTargetVector(pos);} + + public void setRenderRotation(Vector3f rot) {this.transformer.matrix.setRotationDegrees(rot.x, rot.y, rot.z);} - Vec3 matrixCenter = matrixCenterTransformer.getCurrentVector().add(shapeCenterTransformer.getCurrentVector()); + public void setRenderScale(Vec3 scale) {this.transformer.matrix.scale.setTargetVector(scale);} - matrixStack.translate(matrixCenter.x, matrixCenter.y, matrixCenter.z); - matrixRotationTransformer.updateRotation(rot -> managedShape.setMatrixRotation(rot, matrixStack), deltaTime); - matrixScaleTransformer.updateVector(scl -> managedShape.setMatrixScale(scl.toVector3f(), matrixStack), deltaTime); - matrixStack.translate(-matrixCenter.x, -matrixCenter.y, -matrixCenter.z); + protected abstract void generateRawGeometry(boolean lerp); + + + public List getRealModel() { + if (this.transformer.asyncModelInfo()) { + model_vertexes.clear(); + generateRawGeometry(false); } - public void setShapeCenterPos(Vec3 pos){ - shapeCenterTransformer.setTargetVector(pos); + + PoseStack poseStack = new PoseStack(); + + + List hierarchy = new ArrayList<>(); + Shape current = this; + while (current != null) { + hierarchy.add(current); + current = current.parent; } - public void setMatrixCenterPos(Vec3 pos){ - matrixCenterTransformer.setTargetVector(pos); + for (int i = hierarchy.size() - 1; i >= 0; i--) { + Shape n = hierarchy.get(i); + n.transformer.applyModelTransformations(poseStack, true); } - public void setMatrixCenterPos(Vector3f pos){ - matrixCenterTransformer.setTargetVector(new Vec3(pos.x,pos.y,pos.z)); + + Matrix4f matrix = poseStack.last().pose(); + + List transformed = new ArrayList<>(model_vertexes.size()); + for (Vec3 local : model_vertexes) { + Vector3f vec = new Vector3f((float) local.x, (float) local.y, (float) local.z); + vec.mulPosition(matrix); + transformed.add(new Vec3(vec.x(), vec.y(), vec.z())); } - public void setMatrixRotation(Quaternionf rotation){ - matrixRotationTransformer.setTargetRotation(rotation); + + return transformed; + } + + public void beforeDraw(PoseStack matrixStack, float deltaTime) { + transformer.updateTickDelta(deltaTime); + transformFunction.accept(transformer); + if (this.transformer.asyncModelInfo()) { + model_vertexes.clear(); + generateRawGeometry(true); } - public void setMatrixRotation(Vec3 rotation){ - matrixRotationTransformer.setTargetRotation(new Quaternionf().rotationXYZ( - (float)Math.toRadians(rotation.x), - (float)Math.toRadians(rotation.y), - (float)Math.toRadians(rotation.z) - )); + + List hierarchy = new ArrayList<>(); + Shape current = this; + while (current != null) { + hierarchy.add(current); + current = current.parent; } - public void setMatrixScale(Vec3 scale) { - matrixScaleTransformer.setTargetVector(scale); + for (int i = hierarchy.size() - 1; i >= 0; i--) { + Shape n = hierarchy.get(i); + n.transformer.applyTransformations(matrixStack, true); } - public void syncLastToTarget(){ - this.shapeCenterTransformer.syncLastToTarget(); - this.matrixCenterTransformer.syncLastToTarget(); - this.matrixRotationTransformer.syncLastToTarget(); - this.matrixScaleTransformer.syncLastToTarget(); + //We'll apply all transformations to the matrix stack before drawing(With lerp!). + } + + public void draw(VertexBuilder builder, PoseStack matrixStack, float deltaTime) { + if(!visible) return; + matrixStack.pushPose(); + beforeDraw(matrixStack, deltaTime); + builder.setPositionMatrix(matrixStack.last().pose()); + drawInternal(builder); + matrixStack.popPose(); + } + + protected void drawInternal(VertexBuilder builder) { + builder.putColor(baseColor); + for (int i : indexBuffer) { + builder.putVertex(model_vertexes.get(i)); } } -} + + public void setId(ResourceLocation id) { this.id = id; } + public void discard() { + children.forEach(Shape::discard); + ShapeManagers.removeShape(this.id); } + public void syncLastToTarget() { transformer.syncLastToTarget(); } +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/Shape.md b/src/main/java/mypals/ml/shape/Shape.md new file mode 100644 index 0000000..66cdf8e --- /dev/null +++ b/src/main/java/mypals/ml/shape/Shape.md @@ -0,0 +1,45 @@ +### Shape Rendering System + +#### How It Works +Think of your shape in **3 layers**: + +| Layer | What It Is | When It Updates | Magic Applied | +|-------|------------|-----------------|---------------| +| **Raw** | Your "blueprint" (e.g., 8 corners of a cube) | Only when you change the design | Nothing yet! | +| **Shape** | "Baked" version in game space | When position/scale changes | **Local + World** transforms (locked in forever) | +| **Rendering** | Final "draw me!" list (e.g., 36 triangles or wireframe lines) | Every frame (or when style changes) | **Pivot + Spin + Zoom** (animated live!) | + +#### Superpowers +- **Animate anything**: Spin a cube? `transformer.setRotation(...)` +- **Switch styles**: Solid → Wireframe? Just flip a flag! +- **No stutter**: Only rebuild what's dirty. +- **Type-safe**: Code knows *exactly* what shape it is (no casting hacks). +- **Minecraft-ready**: Works with `PoseStack`, colors, transparency, visibility. + +#### Example: Spinning Red Cube +```java +class RedCube extends Shape { + RedCube() { + super(BATCH); // Fast batch rendering + setBaseColor(Color.RED); + setLocalScale(new Vec3(2,2,2)); // Big! + markGeometryDirty(); // Bake it! + } + + // 8 raw corners + void generateRawGeometry() { /* add 8 Vec3 points */ } + + // 36 triangles for solid look + void generateRenderingTopology() { /* unfold faces */ } +} + +// Use it: +RedCube cube = new RedCube(); +cube.transformer().setPivot(new Vec3(0,1,0)); // Spin around top +cube.transformer().setRotation(new Quaternionf().rotateY(1)); // Spin! +``` + +**Result**: A red cube spins smoothly in-game. Add to your mod → instant 3D UI, tools, or effects! + +**Pro Tip**: Copy the **prompt table** (from before) into AI chats for instant code gen. +**Ready to build?** Load OBJ models next? 🚀 Let me know! \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/ShapeForAI.md b/src/main/java/mypals/ml/shape/ShapeForAI.md new file mode 100644 index 0000000..7e0ed73 --- /dev/null +++ b/src/main/java/mypals/ml/shape/ShapeForAI.md @@ -0,0 +1,33 @@ +### Shape System Prompt (Copy-Paste Ready) + +> **You are a 3D rendering expert in Minecraft/Forge. Implement/extend shapes using this exact system:** +> +> | Layer | Purpose | Vertices | Update Trigger | Transform Applied | +> |-------|---------|----------|----------------|-------------------| +> | **Raw** | Design-time geometry | `raw_vertexes` (unique points) | `generateRawGeometry()` | None | +> | **Shape** | Baked local + world space | `shape_vertexes` | `markGeometryDirty()` → `bakeLocalAndWorldTransform()` | `localPos/Rot/Scale` + `worldPos/Rot/Scale` (via `translationRotateScale`) | +> | **Rendering** | Final draw sequence | `rendering_vertexes` (unfolded tris/edges) | `markTopologyDirty()` → `generateRenderingTopology()` → `applyMatrixTransform()` | **Per-frame**: `pivot` + `rotation` + `scale` (matrix only) | +> +> **Key Rules:** +> - **Transforms**: Local/World → **baked once** into `shape_vertexes`. Matrix (`DefaultTransformer`) → **every frame** on `rendering_vertexes`. +> - **Dirty Flags**: `geometryDirty` rebuilds shape verts; `topologyDirty` rebuilds rendering verts (e.g., wireframe vs solid). +> - **Transformer**: `DefaultTransformer` → `getShape()` returns **exact subclass** (type-safe). +> - **Draw Flow**: `beforeDraw()` → update delta → bake if dirty → topology if dirty → matrix transform → `drawInternal()` loops `rendering_vertexes`. +> - **RenderingType**: IMMEDIATE/BATCH/BUFFERED. +> - **API**: `setLocal*/setWorld*` → mark dirty. `transformer.setPivot/Rotation/Scale` → animated. +> - **Extends**: Subclass → override `generateRawGeometry()` + `generateRenderingTopology()`. +> +> **Example Cube**: +> ```java:disable-run +> class Cube extends Shape { +> Cube() { super(BATCH); setLocalScale(Vec3(2,2,2)); markGeometryDirty(); } +> @Override void generateRawGeometry() { /* 8 corners */ } +> @Override void generateRenderingTopology() { /* 36 tris or 24 lines */ } +> } +> ``` +> +> **Use this for ALL shape code. No deviations.** + +**Paste this prompt to any AI → instant understanding!** 🚀 +(132 words — ultra-concise) +``` \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java b/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java index aa13eb4..b996fc2 100644 --- a/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java +++ b/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java @@ -2,7 +2,11 @@ import com.mojang.blaze3d.vertex.PoseStack; import mypals.ml.shape.Shape; -import mypals.ml.transform.Vec3dTransformer; +import mypals.ml.shape.box.BoxShape; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import mypals.ml.transform.shapeTransformers.ModelInfoLayer; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.BoxModelInfo; +import mypals.ml.transform.valueTransformers.Vec3Transformer; import net.minecraft.world.phys.Vec3; public interface BoxLikeShape { @@ -17,26 +21,32 @@ default Vec3 getShapeCenterPos() { double centerZ = (getMin().z + getMax().z) / 2.0; return new Vec3(centerX, centerY, centerZ); } - class BoxTransformer extends Shape.DefaultTransformer { - public BoxTransformer(Shape managedShape) { - super(managedShape); + class BoxTransformer extends DefaultTransformer { + + public BoxModelInfo modelInfoLayer; + public BoxTransformer(BoxShape managedShape,Vec3 dim,Vec3 center) { + super(managedShape,center); + modelInfoLayer = new BoxModelInfo(dim); } - public Vec3dTransformer dimensionTransformer = new Vec3dTransformer(); public void setDimension(Vec3 dimension) { - this.dimensionTransformer.setTargetVector(dimension); - } - @Override - public void applyTransformations(PoseStack matrixStack){ - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if(this.managedShape instanceof BoxLikeShape boxLikeShape) { - dimensionTransformer.updateVector(boxLikeShape::setDimensions, deltaTime); - } + this.modelInfoLayer.setDimension(dimension); } + public void syncLastToTarget(){ - this.dimensionTransformer.syncLastToTarget(); + this.modelInfoLayer.syncLastToTarget(); super.syncLastToTarget(); } + public Vec3 getDimension(boolean lerp){ + return modelInfoLayer.getDimension(lerp); + } + public boolean asyncModelInfo(){ + return modelInfoLayer.async(); + } + @Override + public void updateTickDelta(float delta){ + this.modelInfoLayer.update(delta); + super.updateTickDelta(delta); + } } default void normalizeBounds() { diff --git a/src/main/java/mypals/ml/shape/basics/CircleLikeShape.java b/src/main/java/mypals/ml/shape/basics/CircleLikeShape.java index 627e37b..1f73d9f 100644 --- a/src/main/java/mypals/ml/shape/basics/CircleLikeShape.java +++ b/src/main/java/mypals/ml/shape/basics/CircleLikeShape.java @@ -3,8 +3,8 @@ public interface CircleLikeShape { void setRadius(float radius); void setSegments(int segments); - float getRadius(); - int getSegments(); + float getRadius(boolean lerp); + int getSegments(boolean lerp); enum CircleAxis{ X, Y, diff --git a/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java b/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java index f0429a5..3043748 100644 --- a/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java +++ b/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java @@ -1,14 +1,15 @@ package mypals.ml.shape.basics.core; -import com.mojang.blaze3d.vertex.PoseStack; import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.tags.DrawableLine; -import mypals.ml.transform.FloatValueTransformer; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; import net.minecraft.world.phys.Vec3; public interface LineLikeShape extends DrawableLine { void setLineWidth(float width); + float getLineWidth(boolean lerp); default void addLineSegment(VertexBuilder vertexBuilder, Vec3 start, Vec3 end) { double dx = end.x() - start.x(); double dy = end.y() - start.y(); @@ -20,27 +21,29 @@ default void addLineSegment(VertexBuilder vertexBuilder, Vec3 start, Vec3 end) { vertexBuilder.putVertex(start, normal); vertexBuilder.putVertex(end, normal); } - - class DefaultLineTransformer extends Shape.DefaultTransformer{ - public FloatValueTransformer widthTransformer = new FloatValueTransformer(); - public DefaultLineTransformer(Shape managerShape) { - super(managerShape); + class SimpleLineTransformer extends DefaultTransformer { + public LineModelInfo lineModelInfo; + public SimpleLineTransformer(Shape s, float width, Vec3 center) { + super(s, center); + lineModelInfo = new LineModelInfo(width); + } + public void setWidth(float width){ + lineModelInfo.setWidth(width); + } + public float getWidth(boolean lerp){ + return lineModelInfo.getWidth(lerp); } public void syncLastToTarget(){ - this.widthTransformer.syncLastToTarget(); + lineModelInfo.syncLastToTarget(); super.syncLastToTarget(); } - public void setWidth(float width){ - this.widthTransformer.setTargetValue(width); + public boolean asyncModelInfo(){ + return lineModelInfo.async(); } @Override - public void applyTransformations(PoseStack matrixStack){ - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if(this.managedShape instanceof LineLikeShape lineLikeShape) { - this.widthTransformer.updateValue(lineLikeShape::setLineWidth,deltaTime); - } + public void updateTickDelta(float delta){ + this.lineModelInfo.update(delta); + super.updateTickDelta(delta); } } - } diff --git a/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java b/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java index 0840381..8c87323 100644 --- a/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java +++ b/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java @@ -2,44 +2,59 @@ import com.mojang.blaze3d.vertex.PoseStack; import mypals.ml.shape.Shape; -import mypals.ml.transform.Vec3dTransformer; +import mypals.ml.shape.line.LineShape; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; +import mypals.ml.transform.valueTransformers.Vec3Transformer; import net.minecraft.world.phys.Vec3; public interface TwoPointsLineShape extends LineLikeShape{ void setStart(Vec3 start); void setEnd(Vec3 end); - Vec3 getStart(); - Vec3 getEnd(); - class TwoPointsLineTransformer extends DefaultLineTransformer { - public TwoPointsLineTransformer(Shape managedShape) { - super(managedShape); - } - public Vec3dTransformer startPosTransformer = new Vec3dTransformer(); - public Vec3dTransformer endPosTransformer = new Vec3dTransformer(); + Vec3 getStart(boolean lerp); + Vec3 getEnd(boolean lerp); + + class TwoPointsLineTransformer extends DefaultTransformer { + public LineModelInfo.TwoPointLineModelInfo lineModelInfo; + public TwoPointsLineTransformer(LineShape managedShape,Vec3 start, Vec3 end,float width,Vec3 center) { + super(managedShape,center); + lineModelInfo = new LineModelInfo.TwoPointLineModelInfo(start,end,width); + } public void setStartEnd(Vec3 start, Vec3 end) { - this.endPosTransformer.setTargetVector(end); - this.startPosTransformer.setTargetVector(start); + this.lineModelInfo.setStart(start); + this.lineModelInfo.setEnd(end); + } + public Vec3 getStart(boolean lerp){ + return lineModelInfo.getStart(lerp); + } + public Vec3 getEnd(boolean lerp){ + return lineModelInfo.getEnd(lerp); + } + public void setWidth(float width){ + lineModelInfo.setWidth(width); + } + public float getWidth(boolean lerp){ + return lineModelInfo.getWidth(lerp); } public void setStart(Vec3 start) { - this.startPosTransformer.setTargetVector(start); + lineModelInfo.setStart(start); } public void setEnd(Vec3 end) { - this.endPosTransformer.setTargetVector(end); - } - @Override - public void applyTransformations(PoseStack matrixStack){ - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if(this.managedShape instanceof TwoPointsLineShape twoPointLineShape) { - startPosTransformer.updateVector(twoPointLineShape::setStart, deltaTime); - endPosTransformer.updateVector(twoPointLineShape::setEnd, deltaTime); - } + lineModelInfo.setEnd(end); } + public void syncLastToTarget(){ - this.startPosTransformer.syncLastToTarget(); - this.endPosTransformer.syncLastToTarget(); + lineModelInfo.syncLastToTarget(); super.syncLastToTarget(); } + public boolean asyncModelInfo(){ + return lineModelInfo.async(); + } + @Override + public void updateTickDelta(float delta){ + this.lineModelInfo.update(delta); + super.updateTickDelta(delta); + } } } diff --git a/src/main/java/mypals/ml/shape/box/BoxFaceShape.java b/src/main/java/mypals/ml/shape/box/BoxFaceShape.java index 8484854..d7b924c 100644 --- a/src/main/java/mypals/ml/shape/box/BoxFaceShape.java +++ b/src/main/java/mypals/ml/shape/box/BoxFaceShape.java @@ -6,75 +6,55 @@ import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class BoxFaceShape extends BoxShape implements DrawableQuad{ - public Color faceputColor; + public BoxFaceShape(RenderingType type, - BiConsumer transform, + Consumer transform, Vec3 min, Vec3 max, Color faceputColor, boolean seeThrough, BoxConstructionType constructionType) { - super(type, transform,min,max, seeThrough,constructionType); - this.faceputColor = faceputColor; - } - @Override - public void draw(VertexBuilder builder) { - this.renderFaces(builder); + super(type, transform,min,max,faceputColor, seeThrough,constructionType); + generateRawGeometry(false); } - private void renderFaces(VertexBuilder vertexBuilder) { - vertexBuilder.putColor(faceputColor); - - Vec3 dimensions = this.getDimensions(); - Vec3 center = this.getCenter(); - - Vec3 halfDimensions = dimensions.scale(0.5); - double halfWidth = halfDimensions.x; - double halfHeight = halfDimensions.y; - double halfLength = halfDimensions.z; - - Vec3 v1 = new Vec3(center.x - halfWidth, center.y - halfHeight, center.z - halfLength); - Vec3 v2 = new Vec3(center.x + halfWidth, center.y - halfHeight, center.z - halfLength); - Vec3 v3 = new Vec3(center.x + halfWidth, center.y - halfHeight, center.z + halfLength); - Vec3 v4 = new Vec3(center.x - halfWidth, center.y - halfHeight, center.z + halfLength); - - Vec3 v5 = new Vec3(center.x - halfWidth, center.y + halfHeight, center.z - halfLength); - Vec3 v6 = new Vec3(center.x + halfWidth, center.y + halfHeight, center.z - halfLength); - Vec3 v7 = new Vec3(center.x + halfWidth, center.y + halfHeight, center.z + halfLength); - Vec3 v8 = new Vec3(center.x - halfWidth, center.y + halfHeight, center.z + halfLength); - - - vertexBuilder.putVertex((float) v1.x, (float) v1.y, (float) v1.z); // v1 - vertexBuilder.putVertex((float) v5.x, (float) v5.y, (float) v5.z); // v5 - vertexBuilder.putVertex((float) v6.x, (float) v6.y, (float) v6.z); // v6 - vertexBuilder.putVertex((float) v2.x, (float) v2.y, (float) v2.z); // v2 - - vertexBuilder.putVertex((float) v4.x, (float) v4.y, (float) v4.z); // v4 - vertexBuilder.putVertex((float) v3.x, (float) v3.y, (float) v3.z); // v3 - vertexBuilder.putVertex((float) v7.x, (float) v7.y, (float) v7.z); // v7 - vertexBuilder.putVertex((float) v8.x, (float) v8.y, (float) v8.z); // v8 - - vertexBuilder.putVertex((float) v1.x, (float) v1.y, (float) v1.z); // v1 - vertexBuilder.putVertex((float) v4.x, (float) v4.y, (float) v4.z); // v4 - vertexBuilder.putVertex((float) v8.x, (float) v8.y, (float) v8.z); // v8 - vertexBuilder.putVertex((float) v5.x, (float) v5.y, (float) v5.z); // v5 - - vertexBuilder.putVertex((float) v2.x, (float) v2.y, (float) v2.z); // v2 - vertexBuilder.putVertex((float) v6.x, (float) v6.y, (float) v6.z); // v6 - vertexBuilder.putVertex((float) v7.x, (float) v7.y, (float) v7.z); // v7 - vertexBuilder.putVertex((float) v3.x, (float) v3.y, (float) v3.z); // v3 - - vertexBuilder.putVertex((float) v1.x, (float) v1.y, (float) v1.z); // v1 - vertexBuilder.putVertex((float) v2.x, (float) v2.y, (float) v2.z); // v2 - vertexBuilder.putVertex((float) v3.x, (float) v3.y, (float) v3.z); // v3 - vertexBuilder.putVertex((float) v4.x, (float) v4.y, (float) v4.z); // v4 - - vertexBuilder.putVertex((float) v5.x, (float) v5.y, (float) v5.z); // v5 - vertexBuilder.putVertex((float) v8.x, (float) v8.y, (float) v8.z); // v8 - vertexBuilder.putVertex((float) v7.x, (float) v7.y, (float) v7.z); // v7 - vertexBuilder.putVertex((float) v6.x, (float) v6.y, (float) v6.z); // v6 + @Override + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + + BoxTransformer bt = (BoxTransformer) transformer; + Vec3 c = bt.getLocalPivot(); + Vec3 d = bt.getDimension(lerp); + double hx = d.x * 0.5; + double hy = d.y * 0.5; + double hz = d.z * 0.5; + + model_vertexes.add(new Vec3(c.x - hx, c.y - hy, c.z - hz)); // 0 + model_vertexes.add(new Vec3(c.x + hx, c.y - hy, c.z - hz)); // 1 + model_vertexes.add(new Vec3(c.x + hx, c.y + hy, c.z - hz)); // 2 + model_vertexes.add(new Vec3(c.x - hx, c.y + hy, c.z - hz)); // 3 + model_vertexes.add(new Vec3(c.x - hx, c.y - hy, c.z + hz)); // 4 + model_vertexes.add(new Vec3(c.x + hx, c.y - hy, c.z + hz)); // 5 + model_vertexes.add(new Vec3(c.x + hx, c.y + hy, c.z + hz)); // 6 + model_vertexes.add(new Vec3(c.x - hx, c.y + hy, c.z + hz)); // 7 + + indexBuffer = new int[] { + // -Z + 0, 1, 2, 2, 3, 0, + // +Z + 4, 6, 5, 6, 4, 7, + // -Y + 0, 4, 1, 1, 4, 5, + // +Y + 3, 2, 6, 6, 7, 3, + // -X + 0, 3, 4, 4, 3, 7, + // +X + 1, 5, 2, 2, 5, 6 + }; } } diff --git a/src/main/java/mypals/ml/shape/box/BoxShape.java b/src/main/java/mypals/ml/shape/box/BoxShape.java index 9e78ab1..d5cfaa9 100644 --- a/src/main/java/mypals/ml/shape/box/BoxShape.java +++ b/src/main/java/mypals/ml/shape/box/BoxShape.java @@ -1,151 +1,115 @@ package mypals.ml.shape.box; +import mypals.ml.Helpers; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.BoxLikeShape; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.minecraft.world.phys.Vec3; + +import java.awt.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class BoxShape extends Shape implements BoxLikeShape { - private Vec3 center; - private Vec3 dimensions; - private Vec3 min; - private Vec3 max; - - public enum BoxConstructionType { - CENTER_AND_DIMENSIONS, - CORNERS - } - public BoxShape(RenderingType type, BiConsumer transform, - Vec3 vec1, Vec3 vec2, boolean seeThrough, BoxConstructionType constructionType) { - super(type, seeThrough); - this.transformer = new BoxTransformer(this); - this.transformFunction = (defaultTransformer, shape) -> transform.accept((BoxTransformer) this.transformer, shape); + public enum BoxConstructionType { CENTER_AND_DIMENSIONS, CORNERS } + + public BoxShape(RenderingType type, + Consumer transform, + Vec3 vec1, + Vec3 vec2, + Color color, + boolean seeThrough, + BoxConstructionType constructionType) { + super(type, color, seeThrough); + + this.transformFunction = (t)->{transform.accept((BoxTransformer)this.transformer);}; if (constructionType == BoxConstructionType.CENTER_AND_DIMENSIONS) { - this.center = vec1; - this.dimensions = new Vec3(Math.abs(vec2.x), Math.abs(vec2.y), Math.abs(vec2.z)); + this.transformer = new BoxTransformer(this,new Vec3(Math.abs(vec2.x), Math.abs(vec2.y), Math.abs(vec2.z)),vec1); } else { - - this.dimensions = new Vec3( - Math.abs(vec2.x - vec1.x), - Math.abs(vec2.y - vec1.y), - Math.abs(vec2.z - vec1.z) - ); - this.center = new Vec3( - (vec1.x + vec2.x) / 2.0, - (vec1.y + vec2.y) / 2.0, - (vec1.z + vec2.z) / 2.0 - ); + Vec3 min = Helpers.min(vec1, vec2); + Vec3 max = Helpers.max(vec1, vec2); + Vec3 dims = max.subtract(min); + Vec3 center = min.add(dims.scale(0.5)); + this.transformer = new BoxTransformer(this,dims,center); } - this.transformer.setShapeCenterPos(this.center); - ((BoxTransformer)this.transformer).setDimension(this.dimensions); - updateCornersFromCenterAndDimensions(); - normalizeBounds(); + + syncLastToTarget(); } - public BoxShape(RenderingType type, BiConsumer transform, - Vec3 vec1, Vec3 vec2, BoxConstructionType constructionType) { - this(type, transform, vec1, vec2, false, constructionType); + + + @Override + protected void generateRawGeometry(boolean lerp) { + } + // ====================== BoxLikeShape 接口实现 ====================== + @Override public Vec3 getMin() { - return this.min; + BoxTransformer bt = (BoxTransformer) transformer; + Vec3 half = bt.getDimension(false).scale(0.5); + return bt.getWorldPivot().subtract(half); } @Override public Vec3 getMax() { - return this.max; + BoxTransformer bt = (BoxTransformer) transformer; + Vec3 half = bt.getDimension(false).scale(0.5); + return bt.getWorldPivot().add(half); } @Override public void setMin(Vec3 min) { - this.min = min; - this.center = new Vec3( - (min.x + max.x) / 2.0, - (min.y + max.y) / 2.0, - (min.z + max.z) / 2.0 - ); - this.dimensions = new Vec3( - max.x - min.x, - max.y - min.y, - max.z - min.z - ); + Vec3 max = getMax(); + Vec3 center = min.add(max).scale(0.5); + Vec3 dims = max.subtract(min); + BoxTransformer bt = (BoxTransformer) transformer; + bt.setShapeWorldPivot(center); + bt.setDimension(dims); + } @Override public void setMax(Vec3 max) { - this.max = max; - this.center = new Vec3( - (min.x + max.x) / 2.0, - (min.y + max.y) / 2.0, - (min.z + max.z) / 2.0 - ); - this.dimensions = new Vec3( - max.x - min.x, - max.y - min.y, - max.z - min.z - ); + Vec3 min = getMin(); + Vec3 center = min.add(max).scale(0.5); + Vec3 dims = max.subtract(min); + BoxTransformer bt = (BoxTransformer) transformer; + bt.setShapeWorldPivot(center); + bt.setDimension(dims); + } - public Vec3 getCenter() { - return this.center; - } - - public Vec3 getDimensions() { - return this.dimensions; - } - - @Override - public void setShapeCenterPos(Vec3 center) { - this.center = center; - this.centerPoint = center; - updateCornersFromCenterAndDimensions(); - } public void setDimensions(Vec3 dimensions) { - this.dimensions = new Vec3(Math.abs(dimensions.x), Math.abs(dimensions.y), Math.abs(dimensions.z)); - updateCornersFromCenterAndDimensions(); - } - - private void updateCornersFromCenterAndDimensions() { - Vec3 half = dimensions.scale(0.5); - this.min = center.subtract(half); - this.max = center.add(half); + ((BoxTransformer) transformer).setDimension( + new Vec3(Math.abs(dimensions.x), Math.abs(dimensions.y), Math.abs(dimensions.z)) + ); + } - public void setCorners(Vec3 min, Vec3 max) { - Vec3 dims = new Vec3( - Math.abs(max.x - min.x), - Math.abs(max.y - min.y), - Math.abs(max.z - min.z) - ); - Vec3 c = new Vec3( - (min.x + max.x) / 2.0, - (min.y + max.y) / 2.0, - (min.z + max.z) / 2.0 - ); - setShapeCenterPos(c); - setDimensions(dims); + public void setCorners(Vec3 corner1, Vec3 corner2) { + Vec3 min = Helpers.min(corner1, corner2); + Vec3 max = Helpers.max(corner1, corner2); + Vec3 center = min.add(max).scale(0.5); + Vec3 dims = max.subtract(min); + BoxTransformer bt = (BoxTransformer) transformer; + bt.setShapeWorldPivot(center); + bt.setDimension(dims); + } @Override public void normalizeBounds() { - // 保证 min/max 永远小于等于 max - double minX = Math.min(min.x, max.x); - double minY = Math.min(min.y, max.y); - double minZ = Math.min(min.z, max.z); - double maxX = Math.max(min.x, max.x); - double maxY = Math.max(min.y, max.y); - double maxZ = Math.max(min.z, max.z); - this.min = new Vec3(minX, minY, minZ); - this.max = new Vec3(maxX, maxY, maxZ); - } - @Override - public Vec3 calculateShapeCenterPos() { - return this.center; + Vec3 min = Helpers.min(getMin(), getMax()); + Vec3 max = Helpers.max(getMin(), getMax()); + setCorners(min, max); } + + } diff --git a/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java b/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java index 205288a..e632a78 100644 --- a/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java +++ b/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java @@ -5,72 +5,96 @@ import mypals.ml.shape.Shape; import mypals.ml.shape.basics.tags.DrawableLine; import net.minecraft.world.phys.Vec3; +import org.joml.Vector3f; + import java.awt.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class BoxWireframeShape extends BoxShape implements DrawableLine { - public final Color edgeputColor; - public float edgeWidth; + + public final float edgeWidth; + public BoxWireframeShape(RenderingType type, - BiConsumer transform, Vec3 min, Vec3 max, - Color edgeputColor, boolean seeThrough, float edgeWidth,BoxConstructionType constructionType) { - super(type, transform,min,max,seeThrough,constructionType); - this.edgeputColor = edgeputColor; - this.edgeWidth = edgeWidth; - this.seeThrough = seeThrough; - } - @Override - public void draw(VertexBuilder builder) { - RenderSystem.lineWidth(edgeWidth); - renderEdges(builder); + Consumer transform, + Vec3 vec1, + Vec3 vec2, + Color edgeColor, + boolean seeThrough, + float edgeWidth, + BoxConstructionType constructionType) { + + super(type, transform, vec1, vec2,edgeColor, seeThrough, constructionType); + this.edgeWidth = Math.max(0.1f, edgeWidth); + generateRawGeometry(false); } - private void renderEdges(VertexBuilder vertexBuilder) { - vertexBuilder.putColor(edgeputColor); - Vec3 dimensions = this.getDimensions(); - Vec3 center = this.getCenter(); - - Vec3 halfDimensions = dimensions.scale(0.5); - double halfWidth = halfDimensions.x; - double halfHeight = halfDimensions.y; - double halfLength = halfDimensions.z; - - Vec3 v1 = new Vec3(center.x - halfWidth, center.y - halfHeight, center.z - halfLength); - Vec3 v2 = new Vec3(center.x + halfWidth, center.y - halfHeight, center.z - halfLength); - Vec3 v3 = new Vec3(center.x + halfWidth, center.y - halfHeight, center.z + halfLength); - Vec3 v4 = new Vec3(center.x - halfWidth, center.y - halfHeight, center.z + halfLength); - - Vec3 v5 = new Vec3(center.x - halfWidth, center.y + halfHeight, center.z - halfLength); - Vec3 v6 = new Vec3(center.x + halfWidth, center.y + halfHeight, center.z - halfLength); - Vec3 v7 = new Vec3(center.x + halfWidth, center.y + halfHeight, center.z + halfLength); - Vec3 v8 = new Vec3(center.x - halfWidth, center.y + halfHeight, center.z + halfLength); - - addLineSegment(vertexBuilder, v1, v2); - addLineSegment(vertexBuilder, v2, v3); - addLineSegment(vertexBuilder, v3, v4); - addLineSegment(vertexBuilder, v4, v1); - - addLineSegment(vertexBuilder, v5, v6); - addLineSegment(vertexBuilder, v6, v7); - addLineSegment(vertexBuilder, v7, v8); - addLineSegment(vertexBuilder, v8, v5); - - addLineSegment(vertexBuilder, v1, v5); - addLineSegment(vertexBuilder, v2, v6); - addLineSegment(vertexBuilder, v3, v7); - addLineSegment(vertexBuilder, v4, v8); + public BoxWireframeShape(RenderingType type, + Consumer transform, + Vec3 vec1, + Vec3 vec2, + BoxConstructionType constructionType) { + this(type, transform, vec1, vec2, Color.WHITE, false, 1.0f, constructionType); } - private void addLineSegment(VertexBuilder vertexBuilder, Vec3 start, Vec3 end) { - double dx = end.x() - start.x(); - double dy = end.y() - start.y(); - double dz = end.z() - start.z(); + @Override + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + + BoxTransformer bt = (BoxTransformer) transformer; + Vec3 c = bt.getLocalPivot(); + Vec3 d = bt.getDimension(lerp); - double distanceInv = 1.0 / Math.sqrt(dx * dx + dy * dy + dz * dz); - Vec3 normal = new Vec3(dx * distanceInv, dy * distanceInv, dz * distanceInv); + double hx = d.x * 0.5; + double hy = d.y * 0.5; + double hz = d.z * 0.5; - vertexBuilder.putVertex(start, normal); - vertexBuilder.putVertex(end, normal); + Vec3 v0 = new Vec3(c.x - hx, c.y - hy, c.z - hz); + Vec3 v1 = new Vec3(c.x + hx, c.y - hy, c.z - hz); + Vec3 v2 = new Vec3(c.x + hx, c.y - hy, c.z + hz); + Vec3 v3 = new Vec3(c.x - hx, c.y - hy, c.z + hz); + Vec3 v4 = new Vec3(c.x - hx, c.y + hy, c.z - hz); + Vec3 v5 = new Vec3(c.x + hx, c.y + hy, c.z - hz); + Vec3 v6 = new Vec3(c.x + hx, c.y + hy, c.z + hz); + Vec3 v7 = new Vec3(c.x - hx, c.y + hy, c.z + hz); + + model_vertexes.add(v0); // 0 + model_vertexes.add(v1); // 1 + model_vertexes.add(v2); // 2 + model_vertexes.add(v3); // 3 + model_vertexes.add(v4); // 4 + model_vertexes.add(v5); // 5 + model_vertexes.add(v6); // 6 + model_vertexes.add(v7); // 7 + + indexBuffer = new int[] { + // Bottom + 0, 1, 1, 2, 2, 3, 3, 0, + // Top + 4, 5, 5, 6, 6, 7, 7, 4, + // Vert + 0, 4, 1, 5, 2, 6, 3, 7 + }; } + @Override + protected void drawInternal(VertexBuilder builder) { + RenderSystem.lineWidth(edgeWidth); + builder.putColor(baseColor); + + for (int i = 0; i < indexBuffer.length; i += 2) { + Vec3 start = model_vertexes.get(indexBuffer[i]); + Vec3 end = model_vertexes.get(indexBuffer[i + 1]); + addLineSegment(builder, start, end); + } + } + private void addLineSegment(VertexBuilder builder, Vec3 start, Vec3 end) { + Vec3 dir = end.subtract(start); + dir.normalize(); + + Vec3 normal = new Vec3(dir.x(), dir.y(), dir.z()); + + builder.putVertex(start, normal); + builder.putVertex(end, normal); + } } diff --git a/src/main/java/mypals/ml/shape/box/WireframedBoxShape.java b/src/main/java/mypals/ml/shape/box/WireframedBoxShape.java index 814e203..bec45f9 100644 --- a/src/main/java/mypals/ml/shape/box/WireframedBoxShape.java +++ b/src/main/java/mypals/ml/shape/box/WireframedBoxShape.java @@ -7,6 +7,7 @@ import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class WireframedBoxShape extends BoxShape implements ExtractableShape { public Color faceputColor; @@ -15,9 +16,9 @@ public class WireframedBoxShape extends BoxShape implements ExtractableShape { public boolean lineSeeThrough; public BoxConstructionType constructionType; - public BiConsumer transformFunction; + public Consumer recordedTransformFunction; public WireframedBoxShape(RenderingType type, - BiConsumer transform, + Consumer transform, Vec3 min, Vec3 max, Color faceputColor, @@ -26,9 +27,9 @@ public WireframedBoxShape(RenderingType type, boolean seeThrough, boolean lineSeeThrough,BoxConstructionType constructionType) { - super(type, transform,min,max, seeThrough,constructionType); - this.transformer = new BoxTransformer(this); - this.transformFunction = transform; + super(type, transform,min,max,faceputColor, seeThrough,constructionType); + + this.recordedTransformFunction = transform; this.faceputColor = faceputColor; this.edgeputColor = edgeputColor; this.edgeWidth = edgeWidth; @@ -41,7 +42,7 @@ public void addGroup(ResourceLocation identifier) { identifier.withPath(identifier.getPath()+"/wireframe"), new BoxWireframeShape( this.type, - this.transformFunction, + recordedTransformFunction, this.getMin(), this.getMax(), this.edgeputColor, @@ -50,11 +51,11 @@ public void addGroup(ResourceLocation identifier) { constructionType ) ); - ShapeManagers.QUADS_SHAPE_MANAGER.addShape( + ShapeManagers.TRIANGLES_SHAPE_MANAGER.addShape( identifier.withPath(identifier.getPath()+"/face"), new BoxFaceShape( this.type, - this.transformFunction, + recordedTransformFunction, this.getMin(), this.getMax(), this.faceputColor, @@ -63,4 +64,9 @@ public void addGroup(ResourceLocation identifier) { ) ); } + + @Override + protected void generateRawGeometry(boolean lerp) { + + } } diff --git a/src/main/java/mypals/ml/shape/cylinder/ConeShape.java b/src/main/java/mypals/ml/shape/cylinder/ConeShape.java index 9440117..cfb68e4 100644 --- a/src/main/java/mypals/ml/shape/cylinder/ConeShape.java +++ b/src/main/java/mypals/ml/shape/cylinder/ConeShape.java @@ -1,62 +1,34 @@ package mypals.ml.shape.cylinder; +import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class ConeShape extends CylinderShape { - public ConeShape(RenderingType type, BiConsumer transform, CircleAxis circleAxis, Vec3 center, int segments, float radius, float height, Color color, boolean seeThrough) { - super(type, seeThrough); - this.transformer = new CylinderTransformer(this,segments,radius,height); - this.transformFunction = (defaultTransformer,shape)->transform.accept((CylinderTransformer) this.transformer, shape); - this.segments = segments; - this.radius = radius; - this.height = height; - this.color = color; - this.centerPoint = center; - this.transformer.setShapeCenterPos(center); - this.setAxis(circleAxis); - syncLastToTarget(); + public ConeShape(RenderingType type, Consumer transform, CircleAxis circleAxis, Vec3 center, int segments, float radius, float height, Color color, boolean seeThrough) { + super(type, transform, circleAxis, center, segments, radius, height, color, seeThrough); } - @Override - public void generateCylinder() { - ArrayList vs = new ArrayList<>(); - double halfH = height / 2.0; - - ArrayList baseRing = new ArrayList<>(); + private List generateConeVertices(double halfH, int segments, float radius, CircleAxis axis) { + List vertices = new ArrayList<>(); for (int i = 0; i < segments; i++) { double theta = 2 * Math.PI * i / segments; double c = Math.cos(theta); double s = Math.sin(theta); - double x = 0, y = 0, z = 0; - - switch (axis) { - case X -> { - y = radius * c; - z = radius * s; - x = -halfH; - baseRing.add(new Vec3(x, y, z)); - } - case Y -> { - x = radius * c; - z = radius * s; - y = -halfH; - baseRing.add(new Vec3(x, y, z)); - } - case Z -> { - x = radius * c; - y = radius * s; - z = -halfH; - baseRing.add(new Vec3(x, y, z)); - } - default -> throw new IllegalStateException("Unexpected axis value: " + axis); - } + Vec3 point = switch (axis) { + case X -> new Vec3(-halfH, radius * c, radius * s); + case Y -> new Vec3(radius * c, -halfH, radius * s); + case Z -> new Vec3(radius * c, radius * s, -halfH); + }; + vertices.add(point); } Vec3 apex = switch (axis) { @@ -64,34 +36,49 @@ public void generateCylinder() { case Y -> new Vec3(0, halfH, 0); case Z -> new Vec3(0, 0, halfH); }; + vertices.add(apex); Vec3 baseCenter = switch (axis) { case X -> new Vec3(-halfH, 0, 0); case Y -> new Vec3(0, -halfH, 0); case Z -> new Vec3(0, 0, -halfH); }; + vertices.add(baseCenter); + + return vertices; + } + + @Override + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + List indices = new ArrayList<>(); + + float height = ((CylinderTransformer)this.transformer).getHeight(lerp); + int segments = ((CylinderTransformer)this.transformer).getSegments(lerp); + float radius = ((CylinderTransformer)this.transformer).getRadius(lerp); + double halfH = height / 2.0; + + List verts = generateConeVertices(halfH, segments, radius, axis); + model_vertexes.addAll(verts); + + int apexIndex = segments; + int baseCenterIndex = segments + 1; for (int i = 0; i < segments; i++) { int next = (i + 1) % segments; - Vec3 b0 = baseRing.get(i); - Vec3 b1 = baseRing.get(next); - - vs.add(b0); - vs.add(apex); - vs.add(b1); + indices.add(i); + indices.add(apexIndex); + indices.add(next); } for (int i = 0; i < segments; i++) { int next = (i + 1) % segments; - Vec3 b0 = baseRing.get(i); - Vec3 b1 = baseRing.get(next); - - vs.add(baseCenter); - vs.add(b1); - vs.add(b0); + indices.add(baseCenterIndex); + indices.add(next); + indices.add(i); } - vertexes = vs; - + this.indexBuffer = indices.stream().mapToInt(Integer::intValue).toArray(); } + } diff --git a/src/main/java/mypals/ml/shape/cylinder/ConeWireframeShape.java b/src/main/java/mypals/ml/shape/cylinder/ConeWireframeShape.java index 22fbf20..c7e0b32 100644 --- a/src/main/java/mypals/ml/shape/cylinder/ConeWireframeShape.java +++ b/src/main/java/mypals/ml/shape/cylinder/ConeWireframeShape.java @@ -2,49 +2,26 @@ import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; -import mypals.ml.shape.basics.CircleLikeShape; -import mypals.ml.shape.basics.tags.DrawableLine; import net.minecraft.world.phys.Vec3; -import org.jetbrains.annotations.NotNull; import java.awt.*; import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class ConeWireframeShape extends CylinderWireframeShape { - public ConeWireframeShape(RenderingType type, BiConsumer transform, CircleAxis circleAxis, Vec3 center, int segments, float radius, float height, float lineWidth, Color color, boolean seeThrough) { - super(type, transform,circleAxis,center,segments, radius, height,lineWidth,color, seeThrough); - } - @Override - public void generateCylinder() { - ArrayList vs = new ArrayList<>(); - double halfH = height / 2.0; - - ArrayList baseRing = getBase(halfH); - - Vec3 apex = switch (axis) { - case X -> new Vec3( halfH, 0, 0); - case Y -> new Vec3(0, halfH, 0); - case Z -> new Vec3(0, 0, halfH); - default -> throw new IllegalStateException("Unexpected axis value: " + axis); - }; - - for (int i = 0; i < segments; i++) { - int next = (i + 1) % segments; - vs.add(baseRing.get(i)); - vs.add(baseRing.get(next)); - } - - for (int i = 0; i < segments; i++) { - vs.add(baseRing.get(i)); - vs.add(apex); - } - vertexes = vs; + public ConeWireframeShape(RenderingType type, + Consumer transform, + CircleAxis circleAxis, Vec3 center, + int segments, float radius, float height, + float lineWidth, Color color, boolean seeThrough) { + super(type, transform, circleAxis, center, segments, radius, height, lineWidth, color, seeThrough); } + private List generateConeVertices(double halfH, int segments, float radius, CircleAxis axis) { + List vertices = new ArrayList<>(); - private @NotNull ArrayList getBase(double halfH) { - ArrayList baseRing = new ArrayList<>(); for (int i = 0; i < segments; i++) { double theta = 2 * Math.PI * i / segments; double c = Math.cos(theta); @@ -54,10 +31,54 @@ public void generateCylinder() { case X -> new Vec3(-halfH, radius * c, radius * s); case Y -> new Vec3(radius * c, -halfH, radius * s); case Z -> new Vec3(radius * c, radius * s, -halfH); - default -> throw new IllegalStateException("Unexpected axis value: " + axis); }; - baseRing.add(point); + vertices.add(point); + } + + Vec3 apex = switch (axis) { + case X -> new Vec3(halfH, 0, 0); + case Y -> new Vec3(0, halfH, 0); + case Z -> new Vec3(0, 0, halfH); + }; + vertices.add(apex); + + Vec3 baseCenter = switch (axis) { + case X -> new Vec3(-halfH, 0, 0); + case Y -> new Vec3(0, -halfH, 0); + case Z -> new Vec3(0, 0, -halfH); + }; + vertices.add(baseCenter); + + return vertices; + } + @Override + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + List indices = new ArrayList<>(); + + float height = ((CylinderWireframeTransformer)this.transformer).getHeight(lerp); + int segments = ((CylinderWireframeTransformer)this.transformer).getSegments(lerp); + float radius = ((CylinderWireframeTransformer)this.transformer).getRadius(lerp); + double halfH = height / 2.0; + + List verts = generateConeVertices(halfH, segments, radius, axis); + model_vertexes.addAll(verts); + + int apexIndex = segments; + + for (int i = 0; i < segments; i++) { + int next = (i + 1) % segments; + indices.add(i); + indices.add(next); } - return baseRing; + + for (int i = 0; i < segments; i++) { + indices.add(i); + indices.add(apexIndex); + } + + this.indexBuffer = indices.stream().mapToInt(Integer::intValue).toArray(); } + } + diff --git a/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java b/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java index 9e08966..d2f963e 100644 --- a/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java +++ b/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java @@ -4,220 +4,189 @@ import mypals.ml.shape.Shape; import mypals.ml.shape.basics.CircleLikeShape; import mypals.ml.shape.basics.tags.DrawableTriangle; -import mypals.ml.transform.FloatValueTransformer; -import mypals.ml.transform.IntValueTransformer; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.CircleModelInfo; +import mypals.ml.transform.valueTransformers.FloatTransformer; import net.minecraft.world.phys.Vec3; -import com.mojang.blaze3d.vertex.PoseStack; + import java.awt.*; import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class CylinderShape extends Shape implements CircleLikeShape, DrawableTriangle { - public int segments = 180; - public float radius = 1; - public float height = 1; public CircleAxis axis = CircleAxis.X; - public ArrayList vertexes = new ArrayList<>(); - public Color color = Color.white; - public CylinderShape(RenderingType type, BiConsumer transform, CircleAxis circleAxis, Vec3 center, int segments, float radius,float height, Color color, boolean seeThrough) { - super(type, seeThrough); - this.transformer = new CylinderTransformer(this,segments,radius,height); - this.transformFunction = (defaultTransformer,shape)->transform.accept((CylinderTransformer) this.transformer, shape); - this.segments = segments; - this.radius = radius; - this.height = height; - this.color = color; - this.centerPoint = center; - this.transformer.setShapeCenterPos(center); - this.setAxis(circleAxis); + public Color color = Color.WHITE; + + public CylinderShape(RenderingType type, Consumer transform, + CircleAxis circleAxis, Vec3 center, int segments, + float radius, float height, Color color, boolean seeThrough) { + super(type, shape -> {}, color, center, seeThrough); + + this.transformer = new CylinderTransformer(this, segments, radius, height,center); + this.transformFunction = t -> transform.accept((CylinderTransformer) this.transformer); + this.axis = circleAxis; + generateRawGeometry(false); syncLastToTarget(); } public CylinderShape(RenderingType type, boolean seeThrough) { - super(type,seeThrough); + super(type, Color.WHITE, seeThrough); } + void generateCylinderVertices(boolean lerp) { + model_vertexes.clear(); - public void generateCylinder() { - ArrayList vs = new ArrayList<>(); + float height = getHeight(lerp); + float radius = getRadius(lerp); + int segments = getSegments(lerp); double halfH = height / 2.0; - ArrayList bottomRing = new ArrayList<>(); - ArrayList topRing = new ArrayList<>(); - for (int i = 0; i < segments; i++) { - double theta = (2 * Math.PI * i) / segments; + double theta = 2 * Math.PI * i / segments; double c = Math.cos(theta); double s = Math.sin(theta); - double x, y, z; - - switch (axis) { - case X -> { - y = radius * c; - z = radius * s; - x = 0; - bottomRing.add(new Vec3(x - halfH, y, z)); - topRing.add(new Vec3(x + halfH, y, z)); - } - case Y -> { - x = radius * c; - z = radius * s; - y = 0; - bottomRing.add(new Vec3(x, y - halfH, z)); - topRing.add(new Vec3(x, y + halfH, z)); - } - case Z -> { - x = radius * c; - y = radius * s; - z = 0; - bottomRing.add(new Vec3(x, y, z - halfH)); - topRing.add(new Vec3(x, y, z + halfH)); - } - } + Vec3 bottom = switch (axis) { + case X -> new Vec3(-halfH, radius * c, radius * s); + case Y -> new Vec3(radius * c, -halfH, radius * s); + case Z -> new Vec3(radius * c, radius * s, -halfH); + }; + model_vertexes.add(bottom); } for (int i = 0; i < segments; i++) { - int next = (i + 1) % segments; - - Vec3 b0 = bottomRing.get(i); - Vec3 b1 = bottomRing.get(next); - Vec3 t0 = topRing.get(i); - Vec3 t1 = topRing.get(next); - - vs.add(b0); - vs.add(t0); - vs.add(t1); + double theta = 2 * Math.PI * i / segments; + double c = Math.cos(theta); + double s = Math.sin(theta); - vs.add(b0); - vs.add(t1); - vs.add(b1); + Vec3 top = switch (axis) { + case X -> new Vec3( halfH, radius * c, radius * s); + case Y -> new Vec3(radius * c, halfH, radius * s); + case Z -> new Vec3(radius * c, radius * s, halfH); + }; + model_vertexes.add(top); } - Vec3 baseCenter = switch (axis) { + model_vertexes.add(switch (axis) { case X -> new Vec3(-halfH, 0, 0); case Y -> new Vec3(0, -halfH, 0); case Z -> new Vec3(0, 0, -halfH); - }; + }); + + model_vertexes.add(switch (axis) { + case X -> new Vec3( halfH, 0, 0); + case Y -> new Vec3(0, halfH, 0); + case Z -> new Vec3(0, 0, halfH); + }); + } + + @Override + protected void generateRawGeometry(boolean lerp) { + generateCylinderVertices(lerp); + + List indices = new ArrayList<>(); + int segments = getSegments(lerp); + + int bottomStart = 0; + int topStart = segments; + int bottomCenter = 2 * segments; + int topCenter = 2 * segments + 1; for (int i = 0; i < segments; i++) { int next = (i + 1) % segments; - Vec3 b0 = bottomRing.get(i); - Vec3 b1 = bottomRing.get(next); + int b0 = bottomStart + i; + int b1 = bottomStart + next; + int t0 = topStart + i; + int t1 = topStart + next; - vs.add(baseCenter); - vs.add(b1); - vs.add(b0); + indices.add(b0); indices.add(t0); indices.add(t1); + indices.add(b0); indices.add(t1); indices.add(b1); } - Vec3 topCenter = switch (axis) { - case X -> new Vec3(halfH, 0, 0); - case Y -> new Vec3(0, halfH, 0); - case Z -> new Vec3(0, 0, halfH); - }; - for (int i = 0; i < segments; i++) { int next = (i + 1) % segments; - Vec3 t0 = topRing.get(i); - Vec3 t1 = topRing.get(next); + indices.add(bottomCenter); + indices.add(bottomStart + next); + indices.add(bottomStart + i); + } - vs.add(topCenter); - vs.add(t0); - vs.add(t1); + for (int i = 0; i < segments; i++) { + int next = (i + 1) % segments; + indices.add(topCenter); + indices.add(topStart + i); + indices.add(topStart + next); } - vertexes = vs; + this.indexBuffer = indices.stream().mapToInt(Integer::intValue).toArray(); } - public void setAxis(CircleAxis circleAxis){ + public void setAxis(CircleAxis circleAxis) { this.axis = circleAxis; - generateCylinder(); + generateRawGeometry(true); } - @Override - public void draw(VertexBuilder builder) { - builder.putColor(this.color); - for(Vec3 v : vertexes){ - builder.putVertex(v.add(getShapeCenterPos())); - } - - } public static class CylinderTransformer extends DefaultTransformer { - public IntValueTransformer segmentTransformer = new IntValueTransformer(); - public FloatValueTransformer radiusTransformer = new FloatValueTransformer(); - public FloatValueTransformer heightTransformer = new FloatValueTransformer(); - public CylinderTransformer(Shape managerShape,int seg,float rad,float height) { - super(managerShape); - setSegment(seg); - setRadius(rad); - setHeight(height); - } - public void setSegment(int segment){ - this.segmentTransformer.setTargetValue(segment); - } - public void setRadius(float radius){ - this.radiusTransformer.setTargetValue(radius); + + public CircleModelInfo circleModelInfo; + public FloatTransformer heightTransformer; + + public CylinderTransformer(Shape managedShape, int seg, float rad, float height,Vec3 vec3) { + super(managedShape,vec3); + circleModelInfo = new CircleModelInfo(seg,rad); + heightTransformer = new FloatTransformer(height); } - public void setHeight(float height){ - this.heightTransformer.setTargetValue(height); + + public void setSegment(int segment) { this.circleModelInfo.setSegment(segment); } + public void setRadius(float radius) { this.circleModelInfo.setRadius(radius); } + public void setHeight(float height) { this.heightTransformer.setTargetValue(height); } + + public float getRadius(boolean lerp) { + return circleModelInfo.getRadius(lerp); } - @Override - public void applyTransformations(PoseStack matrixStack){ - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if(this.managedShape instanceof CylinderShape cylinderShape) { - this.segmentTransformer.updateValue(cylinderShape::setSegments,deltaTime); - this.radiusTransformer.updateValue(cylinderShape::setRadius,deltaTime); - this.heightTransformer.updateValue(cylinderShape::setHeight,deltaTime); - } + + public int getSegments(boolean lerp) { + return circleModelInfo.getSegment(lerp); } + public float getHeight(boolean lerp) { return heightTransformer.getValue(lerp); } @Override - public void syncLastToTarget(){ - this.segmentTransformer.syncLastToTarget(); - this.radiusTransformer.syncLastToTarget(); + public void syncLastToTarget() { + this.circleModelInfo.syncLastToTarget(); this.heightTransformer.syncLastToTarget(); super.syncLastToTarget(); } + @Override + public boolean asyncModelInfo(){ + return circleModelInfo.async() || heightTransformer.async(); + } } + @Override public void setRadius(float radius) { - boolean rebuild = this.radius != radius; - this.radius = radius; - if(rebuild){ - generateCylinder(); - } - + ((CylinderTransformer)this.transformer).setRadius(radius); } public void setHeight(float height) { - boolean rebuild = this.height != height; - this.height = height; - if(rebuild){ - generateCylinder(); - } - + ((CylinderTransformer)this.transformer).setHeight(height); } @Override public void setSegments(int segments) { - boolean rebuild = this.segments != segments; - this.segments = segments; - if(rebuild){ - generateCylinder(); - } + ((CylinderTransformer)this.transformer).setSegment(segments); } @Override - public float getRadius() { - return radius; + public float getRadius(boolean lerp) { + return ((CylinderTransformer)this.transformer).getRadius(lerp); } @Override - public int getSegments() { - return segments; - } - public float getHeight(){ - return height; + public int getSegments(boolean lerp) { + return ((CylinderTransformer)this.transformer).getSegments(lerp); } + + public float getHeight(boolean lerp) { return ((CylinderTransformer)this.transformer).getHeight(lerp); } } + diff --git a/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java b/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java index 6797ebf..5bb80e9 100644 --- a/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java +++ b/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java @@ -6,144 +6,104 @@ import mypals.ml.shape.Shape; import mypals.ml.shape.basics.core.LineLikeShape; import mypals.ml.shape.basics.tags.DrawableLine; -import mypals.ml.transform.FloatValueTransformer; -import mypals.ml.transform.IntValueTransformer; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import java.awt.*; import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; -public class CylinderWireframeShape extends CylinderShape implements DrawableLine,LineLikeShape { - public float lineWidth; +public class CylinderWireframeShape extends CylinderShape implements DrawableLine, LineLikeShape { - public CylinderWireframeShape(RenderingType type, BiConsumer transform, CircleAxis circleAxis, Vec3 center, int segments, float radius, float height, float lineWidth, Color color, boolean seeThrough) { + public CylinderWireframeShape(RenderingType type, Consumer transform, + CircleAxis circleAxis, Vec3 center, int segments, + float radius, float height, float lineWidth, Color color, boolean seeThrough) { super(type, seeThrough); - this.transformer = new CylinderWireframeTransformer(this,segments,radius,height,lineWidth); - this.transformFunction = (defaultTransformer,shape)->transform.accept((CylinderWireframeTransformer) this.transformer, shape); - this.segments = segments; - this.radius = radius; - this.height = height; - this.lineWidth = lineWidth; - this.color = color; - this.centerPoint = center; - this.transformer.setShapeCenterPos(center); + this.baseColor = color; + this.transformer = new CylinderWireframeTransformer(this, segments, radius, height, lineWidth,center); + this.transformFunction = t -> transform.accept((CylinderWireframeTransformer) this.transformer); + this.setAxis(circleAxis); + this.setLocalPosition(center); + syncLastToTarget(); } + @Override - public void draw(VertexBuilder builder) { - RenderSystem.lineWidth(lineWidth); - builder.putColor(this.color); - for (int i = 0; i < vertexes.size(); i += 2) { - Vec3 a = vertexes.get(i); - Vec3 b = vertexes.get(i + 1); - addLineSegment(builder, a.add(getShapeCenterPos()), b.add(getShapeCenterPos())); - } - } - @Override - public void generateCylinder() { - ArrayList vs = new ArrayList<>(); - double halfH = height / 2.0; + protected void generateRawGeometry(boolean lerp) { + generateCylinderVertices(lerp); - ArrayList baseRing = getBase(halfH); + List indices = new ArrayList<>(); + int segments = getSegments(lerp); - ArrayList topRing = getTop(baseRing, halfH); + int bottomStart = 0; + int topStart = segments; for (int i = 0; i < segments; i++) { int next = (i + 1) % segments; - vs.add(baseRing.get(i)); - vs.add(baseRing.get(next)); + indices.add(bottomStart + i); + indices.add(bottomStart + next); } for (int i = 0; i < segments; i++) { int next = (i + 1) % segments; - vs.add(topRing.get(i)); - vs.add(topRing.get(next)); + indices.add(topStart + i); + indices.add(topStart + next); } for (int i = 0; i < segments; i++) { - vs.add(baseRing.get(i)); - vs.add(topRing.get(i)); + indices.add(bottomStart + i); + indices.add(topStart + i); } - vertexes = vs; + this.indexBuffer = indices.stream().mapToInt(Integer::intValue).toArray(); } - private @NotNull ArrayList getTop(ArrayList baseRing, double halfH) { - ArrayList topRing = new ArrayList<>(); - for (int i = 0; i < segments; i++) { - Vec3 bottom = baseRing.get(i); - Vec3 top = switch (axis) { - case X -> new Vec3(halfH, bottom.y, bottom.z); - case Y -> new Vec3(bottom.x, halfH, bottom.z); - case Z -> new Vec3(bottom.x, bottom.y, halfH); - }; - topRing.add(top); + @Override + protected void drawInternal(VertexBuilder builder) { + RenderSystem.lineWidth(getLineWidth(true)); + builder.putColor(this.color); + + for (int i = 0; i < indexBuffer.length; i += 2) { + Vec3 a = model_vertexes.get(indexBuffer[i]); + Vec3 b = model_vertexes.get(indexBuffer[i + 1]); + addLineSegment(builder, a, b); } - return topRing; } - private @NotNull ArrayList getBase(double halfH) { - ArrayList baseRing = new ArrayList<>(); + public void setLineWidth(float width) { - for (int i = 0; i < segments; i++) { - double theta = 2 * Math.PI * i / segments; - double c = Math.cos(theta); - double s = Math.sin(theta); - - Vec3 point = switch (axis) { - case X -> new Vec3(-halfH, radius * c, radius * s); - case Y -> new Vec3(radius * c, -halfH, radius * s); - case Z -> new Vec3(radius * c, radius * s, -halfH); - }; - baseRing.add(point); - } - return baseRing; + ((CylinderWireframeTransformer)this.transformer).setWidth(width); } @Override - public void setLineWidth(float width) { - lineWidth = width; + public float getLineWidth(boolean lerp) { + return ((CylinderWireframeTransformer)this.transformer).getWidth(lerp); } - public static class CylinderWireframeTransformer extends LineLikeShape.DefaultLineTransformer { - public IntValueTransformer segmentTransformer = new IntValueTransformer(); - public FloatValueTransformer radiusTransformer = new FloatValueTransformer(); - public FloatValueTransformer heightTransformer = new FloatValueTransformer(); - public CylinderWireframeTransformer(Shape managerShape,int seg,float rad,float height,float lineWidth) { - super(managerShape); - setSegment(seg); - setRadius(rad); - setHeight(height); - setWidth(lineWidth); - } - public void setSegment(int segment){ - this.segmentTransformer.setTargetValue(segment); - } - public void setRadius(float radius){ - this.radiusTransformer.setTargetValue(radius); - } - public void setHeight(float height){ - this.heightTransformer.setTargetValue(height); + public static class CylinderWireframeTransformer extends CylinderTransformer { + public LineModelInfo lineModelInfo; + + public CylinderWireframeTransformer(Shape managedShape, int seg, float rad, float height, float lineWidth,Vec3 vec3) { + super(managedShape,seg,rad,height,vec3); + lineModelInfo = new LineModelInfo(lineWidth); } + + public void setWidth(float width) { lineModelInfo.setWidth(width); } + public float getWidth(boolean lerp) { return lineModelInfo.getWidth(lerp); } + @Override - public void applyTransformations(PoseStack matrixStack){ - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if(this.managedShape instanceof CylinderShape cylinderShape) { - this.segmentTransformer.updateValue(cylinderShape::setSegments,deltaTime); - this.radiusTransformer.updateValue(cylinderShape::setRadius,deltaTime); - this.heightTransformer.updateValue(cylinderShape::setHeight,deltaTime); - } + public void syncLastToTarget() { + lineModelInfo.syncLastToTarget(); + super.syncLastToTarget(); } @Override - public void syncLastToTarget(){ - this.segmentTransformer.syncLastToTarget(); - this.radiusTransformer.syncLastToTarget(); - this.heightTransformer.syncLastToTarget(); - super.syncLastToTarget(); + public boolean asyncModelInfo(){ + return super.asyncModelInfo() || lineModelInfo.async(); } } } + diff --git a/src/main/java/mypals/ml/shape/line/LineShape.java b/src/main/java/mypals/ml/shape/line/LineShape.java index 1d8ec62..0e09285 100644 --- a/src/main/java/mypals/ml/shape/line/LineShape.java +++ b/src/main/java/mypals/ml/shape/line/LineShape.java @@ -7,82 +7,86 @@ import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class LineShape extends Shape implements TwoPointsLineShape { - public Vec3 start; - public Vec3 end; - public Color color; - public float lineWidth; + public LineShape(RenderingType type, - BiConsumer transform, - Vec3 start, - Vec3 end, - Color color, - float lineWidth, - boolean seeThrough) - { - super(type, seeThrough); - this.start = start; - this.end = end; - this.color = color; - setShapeCenterPos(calculateShapeCenterPos()); - this.lineWidth = lineWidth; - this.transformer = new TwoPointsLineTransformer(this); - this.transformer.setShapeCenterPos(this.getShapeCenterPos()); - this.transformFunction = (defaultTransformer,shape)->transform.accept((TwoPointsLineTransformer) this.transformer, shape); - ((TwoPointsLineTransformer)this.transformer).setStart(this.getStart()); - ((TwoPointsLineTransformer)this.transformer).setEnd(this.getEnd()); - ((TwoPointsLineTransformer)this.transformer).setWidth(this.lineWidth); + Consumer transform, + Vec3 start, Vec3 end, + Color color, float lineWidth, + boolean seeThrough) { + + super(type, color, seeThrough); + + this.transformer = new TwoPointsLineTransformer(this,start,end,lineWidth,Vec3.ZERO); + this.transformer.setShapeWorldPivot(calculateShapeCenterPos()); + this.transformFunction = (defaultTransformer) -> + transform.accept((TwoPointsLineTransformer) this.transformer); + syncLastToTarget(); + generateRawGeometry(false); } - @Override + public Vec3 calculateShapeCenterPos() { - double centerX = (getStart().x + getEnd().x) / 2.0; - double centerY = (getStart().y + getEnd().y) / 2.0; - double centerZ = (getStart().z + getEnd().z) / 2.0; + double centerX = (getStart(false).x + getEnd(false).x) / 2.0; + double centerY = (getStart(false).y + getEnd(false).y) / 2.0; + double centerZ = (getStart(false).z + getEnd(false).z) / 2.0; return new Vec3(centerX, centerY, centerZ); } - @Override - public void setShapeCenterPos(Vec3 newCenter) { - super.setShapeCenterPos(newCenter); - Vec3 currentCenter = calculateShapeCenterPos(); - Vec3 offset = newCenter.subtract(currentCenter); - - setStart(getStart().add(offset)); - setEnd(getEnd().add(offset)); - } - - @Override public void setStart(Vec3 start) { - this.start = start; + ((TwoPointsLineTransformer)this.transformer).setStart(start); } @Override public void setEnd(Vec3 end) { - this.end = end; + ((TwoPointsLineTransformer)this.transformer).setEnd(end); } @Override public void setLineWidth(float width) { - this.lineWidth = width; + ((TwoPointsLineTransformer)this.transformer).setWidth(width); } @Override - public Vec3 getStart() { - return start; + public float getLineWidth(boolean lerp) { + return ((TwoPointsLineTransformer)this.transformer).getWidth(lerp); } + @Override - public Vec3 getEnd() { - return end; - } + public Vec3 getStart(boolean lerp) { return ((TwoPointsLineTransformer)this.transformer).getStart(lerp);} @Override - public void draw(VertexBuilder builder) { - RenderSystem.lineWidth(lineWidth); - builder.putColor(color); - addLineSegment(builder,getStart(),getEnd()); - } + public Vec3 getEnd(boolean lerp) { return ((TwoPointsLineTransformer)this.transformer).getEnd(lerp);} + + public float getWidth(boolean lerp) { return ((TwoPointsLineTransformer)this.transformer).getWidth(lerp);} + + @Override + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + + Vec3 start = getStart(lerp); + Vec3 end = getEnd(lerp); + Vec3 center = calculateShapeCenterPos(); + + this.transformer.setShapeWorldPivot(center); + this.transformer.world.position.syncLastToTarget(); + + Vec3 localA = start.subtract(center); + Vec3 localB = end.subtract(center); + + model_vertexes.add(localA); + model_vertexes.add(localB); + + this.indexBuffer = new int[] { 0, 1 }; + } + protected void drawInternal(VertexBuilder builder) { + RenderSystem.lineWidth(getWidth(true)); + builder.putColor(baseColor); + addLineSegment(builder,model_vertexes.getFirst(),model_vertexes.getLast()); + } } + diff --git a/src/main/java/mypals/ml/shape/line/StripLineShape.java b/src/main/java/mypals/ml/shape/line/StripLineShape.java index f504179..07c697e 100644 --- a/src/main/java/mypals/ml/shape/line/StripLineShape.java +++ b/src/main/java/mypals/ml/shape/line/StripLineShape.java @@ -3,127 +3,138 @@ import com.mojang.blaze3d.systems.RenderSystem; import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; -import mypals.ml.shape.basics.core.LineLikeShape; import mypals.ml.shape.basics.core.StripLineLikeShape; import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.ArrayList; import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class StripLineShape extends Shape implements StripLineLikeShape { - public List vertexes = new ArrayList<>(); - public float lineWidth; - public Color color; - public List vertexColors = new ArrayList<>(); - public StripLineShape(RenderingType type, Color color,float lineWidth,boolean seeThrough) { - super(type,seeThrough); - this.color = color; - this.lineWidth = lineWidth; + private List vertexes = new ArrayList<>(); + private List vertexColors = new ArrayList<>(); + + public StripLineShape(RenderingType type, + Consumer transform, + List vertexes, + float lineWidth, + Color color, + boolean seeThrough) { + super(type, color, seeThrough); + this.vertexes = new ArrayList<>(vertexes); + this.transformer = new SimpleLineTransformer(this,lineWidth,this.calculateShapeCenterPos()); + this.transformFunction = (defaultTransformer) -> + transform.accept((SimpleLineTransformer) this.transformer); - } - public StripLineShape(RenderingType type, BiConsumer transform, List vertexes, float lineWidth, Color color, boolean seeThrough) { - super(type, seeThrough); - this.setVertexes(vertexes); - this.color = color; - this.lineWidth = lineWidth; - this.transformer = new LineLikeShape.DefaultLineTransformer(this); - this.transformer.setShapeCenterPos(this.calculateShapeCenterPos()); - this.transformFunction = (defaultTransformer,shape)->transform.accept((LineLikeShape.DefaultLineTransformer) this.transformer, shape); - ((LineLikeShape.DefaultLineTransformer)this.transformer).setWidth(this.lineWidth); syncLastToTarget(); + generateRawGeometry(false); } - @Override + + public Vec3 calculateShapeCenterPos() { - if (vertexes.isEmpty()) { - return Vec3.ZERO; - } + if (vertexes.isEmpty()) return Vec3.ZERO; double sumX = 0, sumY = 0, sumZ = 0; - for (Vec3 vertex : vertexes) { - sumX += vertex.x; - sumY += vertex.y; - sumZ += vertex.z; + for (Vec3 v : vertexes) { + sumX += v.x; + sumY += v.y; + sumZ += v.z; } - int count = vertexes.size(); - return new Vec3(sumX / count, sumY / count, sumZ / count); + double n = vertexes.size(); + return new Vec3(sumX / n, sumY / n, sumZ / n); } - public void setVertexColors(List colors){ - vertexColors = colors; - } @Override - public void setShapeCenterPos(Vec3 newCenter) { - super.setShapeCenterPos(newCenter); + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + if (vertexes.size() < 2) return; - if (vertexes.isEmpty()) { - return; - } + // (1) 仅计算模型内部中心点(局部坐标) + Vec3 localCenter = calculateShapeCenterPos(); + transformer.setShapeLocalPivot(localCenter); - Vec3 currentCenter = calculateShapeCenterPos(); + // (2) 世界 pivot 由外部或实体位置决定,不要在这里改 + // transformer.setWorldPivot(...); - Vec3 offset = newCenter.subtract(currentCenter); + // (3) 将模型顶点移到以 localPivot 为中心 + for (Vec3 v : vertexes) { + model_vertexes.add(v.subtract(localCenter)); + } - vertexes.replaceAll(vec3 -> vec3.add(offset)); - //syncLastToTarget(); + int n = model_vertexes.size(); + indexBuffer = new int[n]; + for (int i = 0; i < n; i++) indexBuffer[i] = i; } - @Override - public void setVertexes(List vertexes) { - this.vertexes = vertexes; - } - @Override - public List getVertexes() { - return this.vertexes; - } - @Override - public void draw(VertexBuilder builder) { - RenderSystem.lineWidth(lineWidth); - int n = vertexes.size(); - if (n < 2) return; + @Override + protected void drawInternal(VertexBuilder builder) { + RenderSystem.lineWidth(getLineWidth(true)); - builder.putColor(0x00000000F); - Vec3 first = vertexes.getFirst(); - builder.putVertex((float) first.x, (float) first.y, (float) first.z, 0, 0, 0); + int n = model_vertexes.size(); + if (n < 2) return;; + Vec3 first = model_vertexes.getFirst(); + builder.putColor(new Color(0, 0, 0, 0)); + builder.putVertex((float) first.x, (float) first.y, (float) first.z, 0f, 0f, 0f); for (int i = 0; i < n; i++) { - Color vColor = color; - if(i 0 ? nextDir : prevDir; + normal = fallback.normalize(); + } } - Vec3 pos = vertexes.get(i); + Vec3 pos = model_vertexes.get(i); builder.putVertex((float) pos.x, (float) pos.y, (float) pos.z, (float) normal.x, (float) normal.y, (float) normal.z); } - builder.putColor(0x00000000F); - Vec3 last = vertexes.getLast(); - builder.putVertex((float) last.x, (float) last.y, (float) last.z, 0, 0, 0); + Vec3 last = model_vertexes.get(n - 1); + builder.putColor(new Color(0, 0, 0, 0)); + builder.putVertex((float) last.x, (float) last.y, (float) last.z, 0f, 0f, 0f); + } + + @Override + public void setVertexes(List vertexes) { + this.vertexes = new ArrayList<>(vertexes); + generateRawGeometry(false); + } + + @Override + public List getVertexes() { + return vertexes; + } + public void setVertexColors(List colors) { + this.vertexColors = new ArrayList<>(colors); } @Override public void setLineWidth(float width) { - this.lineWidth = width; + ((SimpleLineTransformer)this.transformer).setWidth(width); + } + + @Override + public float getLineWidth(boolean lerp) { + return ((SimpleLineTransformer)this.transformer).getWidth(lerp); } } + diff --git a/src/main/java/mypals/ml/shape/model/ObjModelShape.java b/src/main/java/mypals/ml/shape/model/ObjModelShape.java index 4dc004a..17e32e6 100644 --- a/src/main/java/mypals/ml/shape/model/ObjModelShape.java +++ b/src/main/java/mypals/ml/shape/model/ObjModelShape.java @@ -2,6 +2,7 @@ import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.Resource; @@ -13,52 +14,62 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; -import java.util.function.BiConsumer; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; public class ObjModelShape extends Shape { - public Color color = Color.white; public Vec3 modelCenter = Vec3.ZERO; public SimpleOBJModel model; - public ObjModelShape(RenderingType type, BiConsumer transform, ResourceLocation resourceLocation, Vec3 center, Color color) { - this(type, transform,resourceLocation,center,color,false); + public ObjModelShape(RenderingType type, + Consumer transform, + ResourceLocation resourceLocation, + Vec3 center, + Color color) { + this(type, transform, resourceLocation, center, color, false); } - protected ObjModelShape(RenderingType type) { - super(type); - } - protected ObjModelShape(RenderingType type, boolean seeThrough) { - super(type, seeThrough); + protected ObjModelShape(RenderingType type,Color color, boolean seeThrough) { + super(type,color, seeThrough); } - public ObjModelShape(RenderingType type, BiConsumer transform, ResourceLocation resourceLocation, Vec3 center, Color color, boolean seeThrough) { - super(type, transform, seeThrough); - this.transformer = new DefaultTransformer(this); + public ObjModelShape(RenderingType type, + Consumer transform, + ResourceLocation resourceLocation, + Vec3 center, + Color color, + boolean seeThrough) { + super(type,transform, color,center, seeThrough); try { loadOBJ(resourceLocation); - }catch (IOException e){ + } catch (IOException e) { e.printStackTrace(); } - this.color = color; - this.centerPoint = center; - this.centerPoint = calculateShapeCenterPos(); - this.transformer.setShapeCenterPos(center); + + + this.transformer.setShapeWorldPivot(center); + + syncLastToTarget(); + generateRawGeometry(false); } public static class SimpleOBJModel { - public ArrayList vertices = new ArrayList<>(); - public ArrayList faces = new ArrayList<>(); + public final ArrayList vertices = new ArrayList<>(); + public final ArrayList faces = new ArrayList<>(); } - public void loadOBJ(ResourceLocation location) throws IOException { ResourceManager manager = Minecraft.getInstance().getResourceManager(); + if (manager.getResource(location).isEmpty()) return; + + Optional optional = manager.getResource(location); + if (optional.isEmpty()) return; - if(manager.getResource(location).isPresent()) { - Resource resource = manager.getResource(location).get(); - BufferedReader reader = new BufferedReader(new InputStreamReader(resource.open())); + Resource resource = optional.get(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.open()))) { SimpleOBJModel m = new SimpleOBJModel(); String line; while ((line = reader.readLine()) != null) { @@ -67,64 +78,85 @@ public void loadOBJ(ResourceLocation location) throws IOException { String[] parts = line.split("\\s+"); switch (parts[0]) { - case "v": + case "v" -> { double x = Double.parseDouble(parts[1]); double y = Double.parseDouble(parts[2]); double z = Double.parseDouble(parts[3]); m.vertices.add(new Vec3(x, y, z)); - break; - case "f": + } + case "f" -> { int[] faceIndices = new int[parts.length - 1]; for (int i = 1; i < parts.length; i++) { - String vert = parts[i]; - String[] vParts = vert.split("/"); + String[] vParts = parts[i].split("/"); faceIndices[i - 1] = Integer.parseInt(vParts[0]) - 1; } m.faces.add(faceIndices); - break; + } } } this.model = m; } + + } + + + public Vec3 calculateShapeCenterPos() { + if (model == null || model.vertices.isEmpty()) return Vec3.ZERO; + + Vec3 min = new Vec3(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + Vec3 max = new Vec3(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE); + + for (Vec3 v : model.vertices) { + min = new Vec3(Math.min(min.x, v.x), Math.min(min.y, v.y), Math.min(min.z, v.z)); + max = new Vec3(Math.max(max.x, v.x), Math.max(max.y, v.y), Math.max(max.z, v.z)); + } + + this.modelCenter = new Vec3( + (min.x + max.x) / 2, + (min.y + max.y) / 2, + (min.z + max.z) / 2 + ); + + return modelCenter; } @Override - public void draw(VertexBuilder builder) { - builder.putColor(this.color); + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); if (model == null) return; + + model_vertexes.addAll(model.vertices); + + List indices = new ArrayList<>(); for (int[] face : model.faces) { if (face.length < 3) continue; - for (int i = 1; i < face.length - 1; i++) { - Vec3 v0 = model.vertices.get(face[0]).subtract(modelCenter).add(centerPoint); - Vec3 v1 = model.vertices.get(face[i]).subtract(modelCenter).add(centerPoint); - Vec3 v2 = model.vertices.get(face[i + 1]).subtract(modelCenter).add(centerPoint); - - builder.putVertex(v0); - builder.putVertex(v1); - builder.putVertex(v2); + indices.add(face[0]); + indices.add(face[i]); + indices.add(face[i + 1]); } } + + this.indexBuffer = indices.stream().mapToInt(i -> i).toArray(); } + @Override - public Vec3 calculateShapeCenterPos(){ - if (model == null || model.vertices.isEmpty()) return centerPoint; + protected void drawInternal(VertexBuilder builder) { + if (model_vertexes.isEmpty() || indexBuffer == null || indexBuffer.length < 3) + return; - Vec3 min = new Vec3(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); - Vec3 max = new Vec3(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE); + builder.putColor(baseColor); - for (Vec3 v : model.vertices) { - min = new Vec3(Math.min(min.x,v.x), Math.min(min.y,v.y), Math.min(min.z,v.z)); - max = new Vec3(Math.max(max.x,v.x), Math.max(max.y,v.y), Math.max(max.z,v.z)); - } + for (int i = 0; i < indexBuffer.length; i += 3) { + Vec3 v0 = model_vertexes.get(indexBuffer[i]); + Vec3 v1 = model_vertexes.get(indexBuffer[i + 1]); + Vec3 v2 = model_vertexes.get(indexBuffer[i + 2]); - Vec3 modelCenter = new Vec3( - (min.x + max.x)/2, - (min.y + max.y)/2, - (min.z + max.z)/2 - ); - this.modelCenter = modelCenter; - return this.centerPoint; + builder.putVertex(v0); + builder.putVertex(v1); + builder.putVertex(v2); + } } } + diff --git a/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java b/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java index 27922ce..f84af04 100644 --- a/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java +++ b/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java @@ -10,66 +10,86 @@ import java.awt.*; import java.io.IOException; import java.util.function.BiConsumer; +import java.util.function.Consumer; public class ObjModelShapeOutline extends ObjModelShape implements LineLikeShape { public float lineWidth; - public Color color = Color.white; - public ObjModelShapeOutline(RenderingType type, BiConsumer transform, ResourceLocation resourceLocation, Vec3 center,float lineWidth, Color color) { - this(type, transform,resourceLocation,center,lineWidth,color,false); + public Color color = Color.WHITE; + + public ObjModelShapeOutline(RenderingType type, + Consumer transform, + ResourceLocation resourceLocation, + Vec3 center, + float lineWidth, + Color color) { + this(type, transform, resourceLocation, center, lineWidth, color, false); } protected ObjModelShapeOutline(RenderingType type) { - super(type); + super(type, Color.WHITE, false); } protected ObjModelShapeOutline(RenderingType type, boolean seeThrough) { - super(type, seeThrough); + super(type, Color.WHITE, seeThrough); } - public ObjModelShapeOutline(RenderingType type, BiConsumer transform, ResourceLocation resourceLocation, Vec3 center,float lineWidth, Color color, boolean seeThrough) { - super(type, seeThrough); - this.transformer = new DefaultLineTransformer(this); - this.transformFunction = (tr,s)->{transform.accept((DefaultLineTransformer)this.transformer,this);}; - try { - loadOBJ(resourceLocation); - }catch (IOException e){ - e.printStackTrace(); - } + public ObjModelShapeOutline(RenderingType type, + Consumer transform, + ResourceLocation resourceLocation, + Vec3 center, + float lineWidth, + Color color, + boolean seeThrough) { + super(type, (d) -> {}, resourceLocation, center, color, seeThrough); + + this.transformer = new SimpleLineTransformer(this,lineWidth,center); + this.transformFunction = (t) -> transform.accept((SimpleLineTransformer) this.transformer); + this.lineWidth = lineWidth; - ((DefaultLineTransformer)this.transformer).setWidth(this.lineWidth); - syncLastToTarget(); this.color = color; - this.centerPoint = center; - this.transformer.setShapeCenterPos(center); + + ((SimpleLineTransformer) this.transformer).setWidth(this.lineWidth); + this.transformer.setShapeWorldPivot(center); + + syncLastToTarget(); } @Override public void setLineWidth(float width) { this.lineWidth = width; + if (this.transformer instanceof SimpleLineTransformer slt) { + slt.setWidth(width); + } } @Override - public void draw(VertexBuilder builder) { - RenderSystem.lineWidth(this.lineWidth); - builder.putColor(this.color); + public float getLineWidth(boolean lerp) { + return ((SimpleLineTransformer)this.transformer).getWidth(lerp); + } - if (model == null) return; + @Override + protected void generateRawGeometry(boolean lerp) { + super.generateRawGeometry(lerp); + } - for (int[] face : model.faces) { - int n = face.length; - if (n < 2) continue; + @Override + protected void drawInternal(VertexBuilder builder) { + if (model_vertexes.isEmpty() || indexBuffer == null || indexBuffer.length < 3) + return; - for (int i = 0; i < n; i++) { - int next = (i + 1) % n; + RenderSystem.lineWidth(this.lineWidth); + builder.putColor(this.color); - Vec3 v0 = model.vertices.get(face[i]).subtract(modelCenter).add(centerPoint); - Vec3 v1 = model.vertices.get(face[next]).subtract(modelCenter).add(centerPoint); + for (int i = 0; i < indexBuffer.length; i += 3) { + Vec3 v0 = model_vertexes.get(indexBuffer[i]); + Vec3 v1 = model_vertexes.get(indexBuffer[i + 1]); + Vec3 v2 = model_vertexes.get(indexBuffer[i + 2]); - addLineSegment(builder,v0,v1); - } + addLineSegment(builder, v0, v1); + addLineSegment(builder, v1, v2); + addLineSegment(builder, v2, v0); } } - - } + diff --git a/src/main/java/mypals/ml/shape/round/FaceCircleShape.java b/src/main/java/mypals/ml/shape/round/FaceCircleShape.java index 3afde93..4703a9a 100644 --- a/src/main/java/mypals/ml/shape/round/FaceCircleShape.java +++ b/src/main/java/mypals/ml/shape/round/FaceCircleShape.java @@ -1,159 +1,121 @@ package mypals.ml.shape.round; - -import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.CircleLikeShape; -import mypals.ml.shape.basics.tags.DrawableTriangle; -import mypals.ml.transform.FloatValueTransformer; -import mypals.ml.transform.IntValueTransformer; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.CircleModelInfo; import net.minecraft.world.phys.Vec3; -import com.mojang.blaze3d.vertex.PoseStack; import java.awt.*; -import java.util.ArrayList; -import java.util.function.BiConsumer; +import java.util.function.Consumer; -public class FaceCircleShape extends Shape implements CircleLikeShape, DrawableTriangle { +public class FaceCircleShape extends Shape implements CircleLikeShape { - public int segments; - public float radius; public CircleAxis axis = CircleAxis.X; - public ArrayList vertexes = new ArrayList<>(); - public Color color; - - public FaceCircleShape(RenderingType type, BiConsumer transform, CircleAxis circleAxis, Vec3 center, int segments, float radius,Color color, boolean seeThrough) { - super(type, seeThrough); - this.transformer = new FaceCircleTransformer(this,segments,radius); - this.transformFunction = (defaultTransformer,shape)->transform.accept((FaceCircleTransformer) this.transformer, shape); - this.segments = segments; - this.radius = radius; - this.color = color; - ((FaceCircleTransformer)this.transformer).setRadius(this.radius); - ((FaceCircleTransformer)this.transformer).setSegment(this.segments); - this.setAxis(circleAxis); - this.centerPoint = center; - this.transformer.setShapeCenterPos(center); + + public FaceCircleShape(RenderingType type, + Consumer transform, + CircleAxis axis, + Vec3 center, + int segments, + float radius, + Color color, + boolean seeThrough) { + super(type, color, seeThrough); + this.axis = axis; + + this.transformer = new FaceCircleTransformer(this, segments, radius,center); + this.transformFunction = (t) -> transform.accept((FaceCircleTransformer) this.transformer); + + generateRawGeometry(true); syncLastToTarget(); } - /*@Override - public Vec3 calculateShapeCenterPos() { - if (vertexes.isEmpty()) { - return Vec3.ZERO; - } - double sumX = 0, sumY = 0, sumZ = 0; - for (Vec3 vertex : vertexes) { - sumX += vertex.x; - sumY += vertex.y; - sumZ += vertex.z; - } + @Override + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + if (this.getSegments(lerp) < 3) return; + + int segments = getSegments(lerp); + float radius = getRadius(lerp); + + model_vertexes.add(Vec3.ZERO); - int count = vertexes.size(); - return new Vec3(sumX / count, sumY / count, sumZ / count); - }*/ - public void generateCircle(){ - ArrayList vs = new ArrayList<>(); - Vec3 center = Vec3.ZERO; for (int i = 0; i < segments; i++) { - double theta = (2 * Math.PI * i) / segments; - double x, y, z; + double theta = 2 * Math.PI * i / segments; + double x = 0, y = 0, z = 0; switch (axis) { case X -> { - y = center.y + radius * Math.cos(theta); - z = center.z + radius * Math.sin(theta); - x = center.x; + y = radius * Math.cos(theta); + z = radius * Math.sin(theta); + x = 0; } case Y -> { - x = center.x + radius * Math.cos(theta); - z = center.z + radius * Math.sin(theta); - y = center.y; + x = radius * Math.cos(theta); + z = radius * Math.sin(theta); + y = 0; } case Z -> { - x = center.x + radius * Math.cos(theta); - y = center.y + radius * Math.sin(theta); - z = center.z; + x = radius * Math.cos(theta); + y = radius * Math.sin(theta); + z = 0; } - default -> throw new IllegalStateException("Unexpected axis value: " + axis); } - - vs.add(new Vec3(x, y, z)); - } - if (!vs.isEmpty()) { - vs.add(vs.getFirst()); - } - vertexes = vs; - } - public void setAxis(CircleAxis circleAxis){ - this.axis = circleAxis; - generateCircle(); - } - @Override - public void draw(VertexBuilder builder) { - - builder.putColor(this.color); - - builder.putVertex(this.centerPoint); - for(Vec3 v : vertexes){ - builder.putVertex(v.add(getShapeCenterPos())); + model_vertexes.add(new Vec3(x, y, z)); } - } - public static class FaceCircleTransformer extends DefaultTransformer { - public IntValueTransformer segmentTransformer = new IntValueTransformer(); - public FloatValueTransformer radiusTransformer = new FloatValueTransformer(); - public FaceCircleTransformer(Shape managerShape,int seg,float rad) { - super(managerShape); - setSegment(seg); - setRadius(rad); - } - public void setSegment(int segment){ - this.segmentTransformer.setTargetValue(segment); - } - public void setRadius(float radius){ - this.radiusTransformer.setTargetValue(radius); - } - @Override - public void applyTransformations(PoseStack matrixStack){ - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if(this.managedShape instanceof FaceCircleShape circleShape) { - this.segmentTransformer.updateValue(circleShape::setSegments,deltaTime); - this.radiusTransformer.updateValue(circleShape::setRadius,deltaTime); - } - } - @Override - public void syncLastToTarget(){ - this.segmentTransformer.syncLastToTarget(); - this.radiusTransformer.syncLastToTarget(); - super.syncLastToTarget(); + indexBuffer = new int[segments * 3]; + for (int i = 0; i < segments; i++) { + int next = (i + 1) % segments; + indexBuffer[i * 3] = 0; // center + indexBuffer[i * 3 + 1] = i + 1; + indexBuffer[i * 3 + 2] = next + 1; } } + @Override public void setRadius(float radius) { - boolean rebuild = this.radius != radius; - this.radius = radius; - if(rebuild){ - generateCircle(); - } - + ((FaceCircleTransformer)this.transformer).setRadius(radius); } @Override public void setSegments(int segments) { - boolean rebuild = this.segments != segments; - this.segments = segments; - if(rebuild){ - generateCircle(); - } + ((FaceCircleTransformer)this.transformer).setSegment(segments); } @Override - public float getRadius() { - return radius; + public float getRadius(boolean lerp) { + return ((FaceCircleTransformer)this.transformer).getRadius(lerp); } @Override - public int getSegments() { - return segments; + public int getSegments(boolean lerp) { + return ((FaceCircleTransformer)this.transformer).getSegment(lerp); + } + + public static class FaceCircleTransformer extends DefaultTransformer { + public CircleModelInfo circleModelInfo; + public FaceCircleTransformer(Shape managedShape, int seg, float rad,Vec3 center) { + super(managedShape,center); + circleModelInfo = new CircleModelInfo(seg,rad); + } + + public void setSegment(int segment) { this.circleModelInfo.setSegment(segment); } + public void setRadius(float radius) { this.circleModelInfo.setRadius(radius); } + public int getSegment(boolean lerp) {return this.circleModelInfo.getSegment(lerp); } + public float getRadius(boolean lerp) {return this.circleModelInfo.getRadius(lerp); } + + @Override + public void updateTickDelta(float delta) { + this.circleModelInfo.update(delta); + super.updateTickDelta(delta); + } + @Override + public void syncLastToTarget() { + this.circleModelInfo.syncLastToTarget(); + super.syncLastToTarget(); + } + public boolean asyncModelInfo(){ + return circleModelInfo.async(); + } } } diff --git a/src/main/java/mypals/ml/shape/round/LineCircleShape.java b/src/main/java/mypals/ml/shape/round/LineCircleShape.java index f5312a4..28b4a63 100644 --- a/src/main/java/mypals/ml/shape/round/LineCircleShape.java +++ b/src/main/java/mypals/ml/shape/round/LineCircleShape.java @@ -1,149 +1,197 @@ package mypals.ml.shape.round; +import com.mojang.blaze3d.systems.RenderSystem; +import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.CircleLikeShape; import mypals.ml.shape.basics.core.LineLikeShape; -import mypals.ml.shape.line.StripLineShape; -import mypals.ml.transform.FloatValueTransformer; -import mypals.ml.transform.IntValueTransformer; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.CircleModelInfo; +import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; import net.minecraft.world.phys.Vec3; -import com.mojang.blaze3d.vertex.PoseStack; import java.awt.*; -import java.util.ArrayList; -import java.util.function.BiConsumer; +import java.util.function.Consumer; -public class LineCircleShape extends StripLineShape implements CircleLikeShape { +public class LineCircleShape extends Shape implements CircleLikeShape, LineLikeShape { - public int segments ; - public float radius; public CircleAxis axis = CircleAxis.X; - public LineCircleShape(RenderingType type, BiConsumer transform, CircleAxis circleAxis, Vec3 center, int segments, float radius, float lineWidth, Color color, boolean seeThrough) { - super(type,color,lineWidth, seeThrough); - this.transformer = new LineCircleTransformer(this,segments,radius); - this.transformFunction = (defaultTransformer,shape)->transform.accept((LineCircleTransformer) this.transformer, shape); - this.segments = segments; - this.radius = radius; - ((LineCircleTransformer)this.transformer).setWidth(this.lineWidth); - ((LineCircleTransformer)this.transformer).setRadius(this.radius); - ((LineCircleTransformer)this.transformer).setSegment(this.segments); - this.setAxis(circleAxis); - this.centerPoint = center; - this.transformer.setShapeCenterPos(center); + public LineCircleShape(RenderingType type, + Consumer transform, + CircleAxis axis, + Vec3 center, + int segments, + float radius, + float width, + Color color, + boolean seeThrough) { + super(type, color, seeThrough); + this.axis = axis; + + this.transformer = new LineCircleTransformer(this, segments, radius,width, center); + this.transformFunction = t -> transform.accept((LineCircleTransformer) this.transformer); + + generateRawGeometry(true); syncLastToTarget(); } - @Override - public Vec3 calculateShapeCenterPos() { - if (vertexes.isEmpty()) { - return Vec3.ZERO; - } - double sumX = 0, sumY = 0, sumZ = 0; - for (Vec3 vertex : vertexes) { - sumX += vertex.x; - sumY += vertex.y; - sumZ += vertex.z; - } + @Override + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); + int segments = getSegments(lerp); + float radius = getRadius(lerp); + if (segments < 3) return; - int count = vertexes.size(); - return new Vec3(sumX / count, sumY / count, sumZ / count); - } - public void generateCircle(){ - ArrayList vs = new ArrayList<>(); - Vec3 center = getShapeCenterPos(); for (int i = 0; i < segments; i++) { - double theta = (2 * Math.PI * i) / segments; - double x, y, z; + double theta = 2 * Math.PI * i / segments; + double x = 0, y = 0, z = 0; switch (axis) { case X -> { - y = center.y + radius * Math.cos(theta); - z = center.z + radius * Math.sin(theta); - x = center.x; + y = radius * Math.cos(theta); + z = radius * Math.sin(theta); + x = 0; } case Y -> { - x = center.x + radius * Math.cos(theta); - z = center.z + radius * Math.sin(theta); - y = center.y; + x = radius * Math.cos(theta); + z = radius * Math.sin(theta); + y = 0; } case Z -> { - x = center.x + radius * Math.cos(theta); - y = center.y + radius * Math.sin(theta); - z = center.z; + x = radius * Math.cos(theta); + y = radius * Math.sin(theta); + z = 0; } - default -> throw new IllegalStateException("Unexpected axis value: " + axis); } - - vs.add(new Vec3(x, y, z).add(getShapeCenterPos())); + model_vertexes.add(new Vec3(x, y, z)); } - if (!vs.isEmpty()) { - vs.add(vs.getFirst().add(getShapeCenterPos())); + + indexBuffer = new int[segments * 2]; + for (int i = 0; i < segments; i++) { + indexBuffer[i * 2] = i; + indexBuffer[i * 2 + 1] = (i + 1) % segments; // 循环闭合 } - this.setVertexes(vs); } - public void setAxis(CircleAxis circleAxis){ - this.axis = circleAxis; - generateCircle(); + + @Override + public void setRadius(float radius) { + ((LineCircleTransformer) this.transformer).setRadius(radius); + } + + @Override + public void setSegments(int segments) { + ((LineCircleTransformer) this.transformer).setSegment(segments); + } + + @Override + public float getRadius(boolean lerp) { + return ((LineCircleTransformer) this.transformer).getRadius(lerp); + } + + @Override + public int getSegments(boolean lerp) { + return ((LineCircleTransformer) this.transformer).getSegment(lerp); } - public static class LineCircleTransformer extends LineLikeShape.DefaultLineTransformer { - public IntValueTransformer segmentTransformer = new IntValueTransformer(); - public FloatValueTransformer radiusTransformer = new FloatValueTransformer(); - public LineCircleTransformer(Shape managerShape) { - super(managerShape); + + @Override + public void setLineWidth(float width) { + ((LineCircleTransformer) this.transformer).setWidth(width); + } + + @Override + public float getLineWidth(boolean lerp) { + return ((LineCircleTransformer) this.transformer).getWidth(lerp); + } + + public static class LineCircleTransformer extends DefaultTransformer { + private final CircleModelInfo circleModelInfo; + private final LineModelInfo lineModelInfo; + public LineCircleTransformer(Shape managedShape, int segments, float radius,float width, Vec3 center) { + super(managedShape, center); + circleModelInfo = new CircleModelInfo(segments, radius); + lineModelInfo = new LineModelInfo(width); + } + + public void setSegment(int segments) { + circleModelInfo.setSegment(segments); + } + + public void setRadius(float radius) { + circleModelInfo.setRadius(radius); + } + public void setWidth(float radius) { + lineModelInfo.setWidth(radius); } - public LineCircleTransformer(Shape managerShape,int seg,float rad) { - super(managerShape); - setSegment(seg); - setRadius(rad); + + public float getWidth(boolean lerp) { + return lineModelInfo.getWidth(lerp); } - public void setSegment(int segment){ - this.segmentTransformer.setTargetValue(segment); + public int getSegment(boolean lerp) { + return circleModelInfo.getSegment(lerp); } - public void setRadius(float radius){ - this.radiusTransformer.setTargetValue(radius); + + public float getRadius(boolean lerp) { + return circleModelInfo.getRadius(lerp); } + @Override - public void applyTransformations(PoseStack matrixStack){ - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if(this.managedShape instanceof LineCircleShape circleShape) { - this.segmentTransformer.updateValue(circleShape::setSegments,deltaTime); - this.radiusTransformer.updateValue(circleShape::setRadius,deltaTime); - } + public void updateTickDelta(float delta) { + circleModelInfo.update(delta); + lineModelInfo.update(delta); + super.updateTickDelta(delta); } + @Override - public void syncLastToTarget(){ - this.segmentTransformer.syncLastToTarget(); - this.radiusTransformer.syncLastToTarget(); + public void syncLastToTarget() { + circleModelInfo.syncLastToTarget(); + lineModelInfo.syncLastToTarget(); super.syncLastToTarget(); } + public boolean asyncModelInfo(){ + return circleModelInfo.async() || lineModelInfo.async(); + } } @Override - public void setRadius(float radius) { - boolean rebuild = this.radius != radius; - this.radius = radius; - if(rebuild){ - generateCircle(); - } + protected void drawInternal(VertexBuilder builder) { + RenderSystem.lineWidth(getLineWidth(true)); - } + int n = model_vertexes.size(); + if (n < 2) return; - @Override - public void setSegments(int segments) { - boolean rebuild = this.segments != segments; - this.segments = segments; - if(rebuild){ - generateCircle(); + Vec3 first = model_vertexes.getFirst(); + builder.putColor(new Color(0, 0, 0, 0)); + builder.putVertex((float) first.x, (float) first.y, (float) first.z, 0f, 0f, 0f); + builder.putColor(baseColor); + for (int i = 0; i < n; i++) { + + Vec3 normal; + if (i == 0) { + Vec3 dir = model_vertexes.get(1).subtract(model_vertexes.get(0)); + normal = dir.normalize(); + } else if (i == n - 1) { + Vec3 dir = model_vertexes.get(n - 1).subtract(model_vertexes.get(n - 2)); + normal = dir.normalize(); + } else { + Vec3 prevDir = model_vertexes.get(i).subtract(model_vertexes.get(i - 1)); + Vec3 nextDir = model_vertexes.get(i + 1).subtract(model_vertexes.get(i)); + normal = prevDir.add(nextDir).normalize(); + if (Double.isNaN(normal.x) || Double.isNaN(normal.y) || Double.isNaN(normal.z)) { + Vec3 fallback = nextDir.lengthSqr() > 0 ? nextDir : prevDir; + normal = fallback.normalize(); + } + } + + Vec3 pos = model_vertexes.get(i); + builder.putVertex((float) pos.x, (float) pos.y, (float) pos.z, + (float) normal.x, (float) normal.y, (float) normal.z); } - } + Vec3 finish = model_vertexes.getFirst(); - @Override - public float getRadius() { - return radius; - } + builder.putVertex((float) finish.x, (float) finish.y, (float) finish.z, 0f, 0f, 0f); - @Override - public int getSegments() { - return segments; + Vec3 last = model_vertexes.get(n - 1); + builder.putColor(new Color(0, 0, 0, 0)); + builder.putVertex((float) last.x, (float) last.y, (float) last.z, 0f, 0f, 0f); } } + diff --git a/src/main/java/mypals/ml/shape/round/SphereShape.java b/src/main/java/mypals/ml/shape/round/SphereShape.java index af2f29c..bcce438 100644 --- a/src/main/java/mypals/ml/shape/round/SphereShape.java +++ b/src/main/java/mypals/ml/shape/round/SphereShape.java @@ -4,8 +4,6 @@ import mypals.ml.shape.Shape; import mypals.ml.shape.basics.CircleLikeShape; import mypals.ml.shape.basics.tags.DrawableTriangle; -import mypals.ml.transform.FloatValueTransformer; -import mypals.ml.transform.IntValueTransformer; import net.minecraft.world.phys.Vec3; import com.mojang.blaze3d.vertex.PoseStack; import java.awt.*; @@ -14,94 +12,60 @@ import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; +import java.util.function.Consumer; -import static mypals.ml.shape.round.SphereShape.SphereMode.UV; public class SphereShape extends Shape implements CircleLikeShape, DrawableTriangle { - public enum SphereMode { - UV, - Ico + @Override + protected void generateRawGeometry(boolean lerp) { + generateSphereShape(lerp); } - public int segments = 180; - public float radius = 1; - public SphereMode mode = UV; - public ArrayList vertexes = new ArrayList<>(); - public Color color = Color.white; - public SphereShape(RenderingType type, BiConsumer transform, SphereMode mode, Vec3 center, int segments, float radius, Color color, boolean seeThrough) { - super(type, seeThrough); - this.transformer = new SphereTransformer(this, segments, radius); - this.transformFunction = (defaultTransformer, shape) -> transform.accept((SphereTransformer) this.transformer, shape); - this.segments = segments; - this.radius = radius; - this.color = color; - ((SphereTransformer) this.transformer).setRadius(this.radius); - ((SphereTransformer) this.transformer).setSegment(this.segments); - this.setMode(mode); - this.centerPoint = center; - this.transformer.setShapeCenterPos(this.centerPoint); + + public SphereShape(RenderingType type, Consumer transform, Vec3 center, int segments, float radius, Color color, boolean seeThrough) { + super(type,color, seeThrough); + this.transformer = new FaceCircleShape.FaceCircleTransformer(this, segments, radius, center); + this.transformFunction = (defaultTransformer) -> transform.accept((FaceCircleShape.FaceCircleTransformer) this.transformer); syncLastToTarget(); + generateSphereShape(true); } - /*@Override - public Vec3 calculateShapeCenterPos() { - if (vertexes.isEmpty()) { - return Vec3.ZERO; - } - - double sumX = 0, sumY = 0, sumZ = 0; - for (Vec3 vertex : vertexes) { - sumX += vertex.x; - sumY += vertex.y; - sumZ += vertex.z; - } - int count = vertexes.size(); - return new Vec3(sumX / count, sumY / count, sumZ / count); - }*/ - public void generateSphereShape() { + public void generateSphereShape(boolean lerp) { ArrayList vs = new ArrayList<>(); Vec3 center = Vec3.ZERO; - double radius = getRadius(); + double radius = getRadius(lerp); + int segments = getSegments(lerp); - switch (mode) { - case UV -> { - int latSegments = segments; - int lonSegments = segments * 2; + int lonSegments = segments * 2; - for (int i = 0; i < latSegments; i++) { - double theta1 = i * Math.PI / latSegments; - double theta2 = (i + 1) * Math.PI / latSegments; + Vec3[][] vertexGrid = new Vec3[segments + 1][lonSegments + 1]; - for (int j = 0; j < lonSegments; j++) { - double phi1 = j * 2 * Math.PI / lonSegments; - double phi2 = (j + 1) * 2 * Math.PI / lonSegments; - - Vec3 v0 = sphericalToCartesian(center, radius, theta1, phi1); - Vec3 v1 = sphericalToCartesian(center, radius, theta1, phi2); - Vec3 v2 = sphericalToCartesian(center, radius, theta2, phi1); - Vec3 v3 = sphericalToCartesian(center, radius, theta2, phi2); - - vs.add(v0); - vs.add(v2); - vs.add(v1); - - vs.add(v1); - vs.add(v2); - vs.add(v3); - } - } + for (int i = 0; i <= segments; i++) { + double theta = i * Math.PI / segments; + for (int j = 0; j <= lonSegments; j++) { + double phi = j * 2 * Math.PI / lonSegments; + vertexGrid[i][j] = sphericalToCartesian(center, radius, theta, phi); + vs.add(vertexGrid[i][j]); } + } - case Ico -> { - IcoSphereGenerator generator = new IcoSphereGenerator(center, radius, segments); - vs.addAll(generator.getVertexList()); - } + ArrayList indices = new ArrayList<>(); + for (int i = 0; i < segments; i++) { + for (int j = 0; j < lonSegments; j++) { + int v0 = i * (lonSegments + 1) + j; + int v1 = (i + 1) * (lonSegments + 1) + j; + int v2 = i * (lonSegments + 1) + (j + 1); + int v3 = (i + 1) * (lonSegments + 1) + (j + 1); - default -> throw new IllegalStateException("Unexpected mode value: " + mode); + indices.add(v0); indices.add(v1); indices.add(v2); + indices.add(v2); indices.add(v1); indices.add(v3); + } } - vertexes = vs; + indexBuffer = indices.stream().mapToInt(Integer::intValue).toArray(); + + model_vertexes = vs; } private Vec3 sphericalToCartesian(Vec3 center, double r, double theta, double phi) { @@ -111,198 +75,23 @@ private Vec3 sphericalToCartesian(Vec3 center, double r, double theta, double ph return new Vec3(x, y, z); } - public void setMode(SphereMode mode) { - this.mode = mode; - generateSphereShape(); - } - - @Override - public void draw(VertexBuilder builder) { - - builder.putColor(this.color); - - for (Vec3 v : vertexes) { - builder.putVertex(v.add(getShapeCenterPos())); - } - - } - - public static class SphereTransformer extends DefaultTransformer { - public IntValueTransformer segmentTransformer = new IntValueTransformer(); - public FloatValueTransformer radiusTransformer = new FloatValueTransformer(); - - public SphereTransformer(Shape managerShape, int seg, float rad) { - super(managerShape); - setSegment(seg); - setRadius(rad); - } - - public void setSegment(int segment) { - this.segmentTransformer.setTargetValue(segment); - } - - public void setRadius(float radius) { - this.radiusTransformer.setTargetValue(radius); - } - - @Override - public void applyTransformations(PoseStack matrixStack) { - super.applyTransformations(matrixStack); - float deltaTime = getTickDelta(); - if (this.managedShape instanceof SphereShape shape) { - this.segmentTransformer.updateValue(shape::setSegments, deltaTime); - this.radiusTransformer.updateValue(shape::setRadius, deltaTime); - } - } - - @Override - public void syncLastToTarget() { - this.segmentTransformer.syncLastToTarget(); - this.radiusTransformer.syncLastToTarget(); - super.syncLastToTarget(); - } - } - @Override public void setRadius(float radius) { - boolean rebuild = this.radius != radius; - this.radius = radius; - if (rebuild) { - generateSphereShape(); - } - + ((FaceCircleShape.FaceCircleTransformer)this.transformer).setRadius(radius); } @Override public void setSegments(int segments) { - boolean rebuild = this.segments != segments; - this.segments = segments; - if (rebuild) { - generateSphereShape(); - } + ((FaceCircleShape.FaceCircleTransformer)this.transformer).setSegment(segments); } @Override - public float getRadius() { - return radius; + public float getRadius(boolean lerp) { + return ((FaceCircleShape.FaceCircleTransformer)this.transformer).getRadius(lerp); } @Override - public int getSegments() { - return segments; - } - - public static class IcoSphereGenerator { - - private final Map middlePointCache = new HashMap<>(); - private final ArrayList vertices = new ArrayList<>(); - private final ArrayList faces = new ArrayList<>(); - private final Vec3 center; - private final double radius; - private final int segments; - - public IcoSphereGenerator(Vec3 center, double radius, int segments) { - this.center = center; - this.radius = radius; - this.segments = segments; - generateIcosahedron(); - refine(); - } - - private void generateIcosahedron() { - vertices.clear(); - faces.clear(); - - double t = (1.0 + Math.sqrt(5.0)) / 2.0; // 黄金分割 - double s = Math.sqrt(1 + t * t); - - vertices.add(normalize(new Vec3(-1, t, 0), radius)); - vertices.add(normalize(new Vec3(1, t, 0), radius)); - vertices.add(normalize(new Vec3(-1, -t, 0), radius)); - vertices.add(normalize(new Vec3(1, -t, 0), radius)); - - vertices.add(normalize(new Vec3(0, -1, t), radius)); - vertices.add(normalize(new Vec3(0, 1, t), radius)); - vertices.add(normalize(new Vec3(0, -1, -t), radius)); - vertices.add(normalize(new Vec3(0, 1, -t), radius)); - - vertices.add(normalize(new Vec3(t, 0, -1), radius)); - vertices.add(normalize(new Vec3(t, 0, 1), radius)); - vertices.add(normalize(new Vec3(-t, 0, -1), radius)); - vertices.add(normalize(new Vec3(-t, 0, 1), radius)); - - faces.addAll(Arrays.asList( - new int[]{0, 11, 5}, new int[]{0, 5, 1}, new int[]{0, 1, 7}, new int[]{0, 7, 10}, new int[]{0, 10, 11}, - new int[]{1, 5, 9}, new int[]{5, 11, 4}, new int[]{11, 10, 2}, new int[]{10, 7, 6}, new int[]{7, 1, 8}, - new int[]{3, 9, 4}, new int[]{3, 4, 2}, new int[]{3, 2, 6}, new int[]{3, 6, 8}, new int[]{3, 8, 9}, - new int[]{4, 9, 5}, new int[]{2, 4, 11}, new int[]{6, 2, 10}, new int[]{8, 6, 7}, new int[]{9, 8, 1} - )); - } - - private void refine() { - for (int i = 0; i < segments; i++) { - ArrayList newFaces = new ArrayList<>(); - for (int[] tri : faces) { - int a = tri[0]; - int b = tri[1]; - int c = tri[2]; - - int ab = getMiddlePoint(a, b); - int bc = getMiddlePoint(b, c); - int ca = getMiddlePoint(c, a); - - newFaces.add(new int[]{a, ab, ca}); - newFaces.add(new int[]{b, bc, ab}); - newFaces.add(new int[]{c, ca, bc}); - newFaces.add(new int[]{ab, bc, ca}); - } - faces.clear(); - faces.addAll(newFaces); - } - } - - private int getMiddlePoint(int p1, int p2) { - long smallerIndex = Math.min(p1, p2); - long greaterIndex = Math.max(p1, p2); - long key = (smallerIndex << 32) + greaterIndex; - - if (middlePointCache.containsKey(key)) { - return middlePointCache.get(key); - } - - Vec3 point1 = vertices.get(p1); - Vec3 point2 = vertices.get(p2); - Vec3 middle = new Vec3( - (point1.x + point2.x) / 2, - (point1.y + point2.y) / 2, - (point1.z + point2.z) / 2 - ); - - middle = normalize(middle, radius); - - vertices.add(middle); - int index = vertices.size() - 1; - middlePointCache.put(key, index); - return index; - } - - private Vec3 normalize(Vec3 v, double r) { - double length = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); - return new Vec3( - center.x + v.x / length * r, - center.y + v.y / length * r, - center.z + v.z / length * r - ); - } - - public ArrayList getVertexList() { - ArrayList result = new ArrayList<>(); - for (int[] tri : faces) { - result.add(vertices.get(tri[0])); - result.add(vertices.get(tri[1])); - result.add(vertices.get(tri[2])); - } - return result; - } + public int getSegments(boolean lerp) { + return ((FaceCircleShape.FaceCircleTransformer)this.transformer).getSegment(lerp); } } \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/text/TextShape.java b/src/main/java/mypals/ml/shape/text/TextShape.java index 93e022c..f3dcefb 100644 --- a/src/main/java/mypals/ml/shape/text/TextShape.java +++ b/src/main/java/mypals/ml/shape/text/TextShape.java @@ -12,8 +12,8 @@ import java.util.ArrayList; import java.util.function.BiConsumer; -public class TextShape extends Shape { - public ArrayList contents = new ArrayList<>(); +public class TextShape { + /*public ArrayList contents = new ArrayList<>(); public ArrayList color = new ArrayList<>(); public BillBoardMode bill = BillBoardMode.ALL; public enum BillBoardMode { @@ -106,6 +106,6 @@ public void applyBillboard(PoseStack matrices, BillBoardMode mode) { matrices.mulPose(Minecraft.getInstance().gameRenderer.getMainCamera().rotation()); break; } - } + }*/ } \ No newline at end of file diff --git a/src/main/java/mypals/ml/shapeManagers/ShapeManager.java b/src/main/java/mypals/ml/shapeManagers/ShapeManager.java index ece65eb..3c2bd8b 100644 --- a/src/main/java/mypals/ml/shapeManagers/ShapeManager.java +++ b/src/main/java/mypals/ml/shapeManagers/ShapeManager.java @@ -20,8 +20,8 @@ public class ShapeManager { public ShapeGroup bufferedShapeGroup; public BuilderManager builderManager; public static Comparator SHAPE_ORDER_COMPARATOR = (s1, s2) -> { - Vec3 shape1Pos = s1.centerPoint; - Vec3 shape2Pos = s2.centerPoint; + Vec3 shape1Pos = s1.transformer.getWorldPivot(); + Vec3 shape2Pos = s2.transformer.getWorldPivot(); double distance1 = shape1Pos.lengthSqr(); // Square of distance for efficiency double distance2 = shape2Pos.lengthSqr(); return Double.compare(distance2, distance1); diff --git a/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java b/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java index 9213889..5c2879a 100644 --- a/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java +++ b/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java @@ -13,7 +13,6 @@ public class ShapeManagers { public static final String TEMP_HEADER = "temp_shape"; - public static ShapeManager QUADS_SHAPE_MANAGER; public static ShapeManager LINES_SHAPE_MANAGER; public static ShapeManager LINE_STRIP_SHAPE_MANAGER; public static ShapeManager TEXT; @@ -22,12 +21,11 @@ public class ShapeManagers { public static ShapeManager TRIANGLES_FAN_SHAPE_MANAGER; public static List managers = new ArrayList<>(); public static void init(){ - QUADS_SHAPE_MANAGER = register(BuilderManagers.QUADS_BUILDER_MANAGER,"quads_shape_manager"); LINE_STRIP_SHAPE_MANAGER = register(BuilderManagers.LINE_STRIP_BUILDER_MANAGER,"line_strip_shape_manager"); LINES_SHAPE_MANAGER = register(BuilderManagers.LINES_BUILDER_MANAGER,"lines_shape_manager"); TRIANGLES_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_BUILDER_MANAGER,"triangles_shape_manager"); - TRIANGLES_STRIP_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_STRIP_BUILDER_MANAGER,"triangles_strip_shape_manager"); - TRIANGLES_FAN_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_FAN_BUILDER_MANAGER,"triangles_fan_shape_manager"); + //TRIANGLES_STRIP_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_STRIP_BUILDER_MANAGER,"triangles_strip_shape_manager"); + //TRIANGLES_FAN_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_FAN_BUILDER_MANAGER,"triangles_fan_shape_manager"); TEXT = register(BuilderManagers.TEXT,"text_manager"); } public static void renderAll(PoseStack matrixStack, float tickDelta){ diff --git a/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java b/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java index 94e00cd..3354b8d 100644 --- a/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java +++ b/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java @@ -26,12 +26,12 @@ public static void registerShapeBuilder(Class shapeClass, Shape } public static void init(){ registerShapeBuilder(BoxWireframeShape.class, ShapeManagers.LINES_SHAPE_MANAGER); - registerShapeBuilder(BoxFaceShape.class, ShapeManagers.QUADS_SHAPE_MANAGER); + registerShapeBuilder(BoxFaceShape.class, ShapeManagers.TRIANGLES_SHAPE_MANAGER); registerShapeBuilder(LineShape.class,ShapeManagers.LINES_SHAPE_MANAGER); registerShapeBuilder(StripLineShape.class,ShapeManagers.LINE_STRIP_SHAPE_MANAGER); registerShapeBuilder(LineCircleShape.class,ShapeManagers.LINE_STRIP_SHAPE_MANAGER); - registerShapeBuilder(TextShape.class,ShapeManagers.TEXT); - registerShapeBuilder(FaceCircleShape.class,ShapeManagers.TRIANGLES_FAN_SHAPE_MANAGER); + //registerShapeBuilder(TextShape.class,ShapeManagers.TEXT); + registerShapeBuilder(FaceCircleShape.class,ShapeManagers.TRIANGLES_SHAPE_MANAGER); registerShapeBuilder(SphereShape.class,ShapeManagers.TRIANGLES_SHAPE_MANAGER); registerShapeBuilder(ObjModelShape.class,ShapeManagers.TRIANGLES_SHAPE_MANAGER); registerShapeBuilder(ObjModelShapeOutline.class,ShapeManagers.LINES_SHAPE_MANAGER); diff --git a/src/main/java/mypals/ml/test/Tester.java b/src/main/java/mypals/ml/test/Tester.java index cbe77bc..99e6450 100644 --- a/src/main/java/mypals/ml/test/Tester.java +++ b/src/main/java/mypals/ml/test/Tester.java @@ -89,9 +89,9 @@ public static void init(){ .axis(CircleLikeShape.CircleAxis.Y) .color(randomColor()) .seeThrough(true) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(0, time * 3, 0)); + t.setShapeMatrixRotationDegrees(0, time * 3, 0); }) .build(Shape.RenderingType.BATCH) ); @@ -107,9 +107,12 @@ public static void init(){ .lineWidth(3.0f) .color(randomColor()) .seeThrough(false) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(0, time * 4, 0)); + //t.setShapeMatrixRotationDegrees(0, time * 4, 0); + + t.setShapeLocalRotationDegrees(0,time*4,0); + t.setShapeWorldRotationDegrees(time*4, 0, 0); }) .build(Shape.RenderingType.BATCH) ); @@ -121,16 +124,74 @@ public static void init(){ .pos(new Vec3(xPos(), 0,0)) .radius(2.0f) .segments(32) - .mode(SphereShape.SphereMode.UV) .color(randomColor()) .seeThrough(true) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(time * 2, time * 3, 0)); + t.setShapeMatrixRotationDegrees(time * 2, time * 3, 0); }) .build(Shape.RenderingType.BATCH) ); + Vec3 spos = new Vec3(xPos(), 0,20); + Shape s1 = ShapeGenerator.generateSphere() + .pos(spos) + .radius(2.0f) + .segments(6) + .color(randomColor()) + .seeThrough(false) + .transform((t) -> { + float time = client.getGameTime(); + t.setShapeWorldRotationDegrees(0, time * 3, 0); + + float yOffset = (float)Math.sin(time * 0.1) * 2f; + Vec3 worldPivot = t.getShapeWorldPivot(false); + t.setShapeWorldPivot(new Vec3(worldPivot.x, 0 + yOffset, worldPivot.z)); + }) + .build(Shape.RenderingType.BATCH); + + Shape s2 = ShapeGenerator.generateSphere() + .pos(new Vec3(15,0,0)) + .radius(1.0f) + .segments(3) + .color(randomColor()) + .seeThrough(false) + .transform((t) -> { + float time = client.getGameTime(); + t.setShapeMatrixRotationDegrees(0, time*6, 0); + }) + .build(Shape.RenderingType.BATCH); + + Shape s3 = ShapeGenerator.generateSphere() + .pos(new Vec3(7,0,0)) + .radius(0.3f) + .segments(5) + .color(randomColor()) + .seeThrough(false) + .transform((t) -> { + float time = client.getGameTime(); + //t.setShapeMatrixRotationDegrees(time * 2, time * 3, 0); + }) + .build(Shape.RenderingType.BATCH); + + s2.addChild(s3); + s1.addChild(s2); + + + // 3...3. Sphere + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_sphere_2"), + s1 + ); + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_sphere_2_child"), + s2 + ); + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_sphere_3_child"), + s3 + ); + // 4. ObjModel ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_obj_model"), @@ -139,9 +200,9 @@ public static void init(){ .model(ResourceLocation.fromNamespaceAndPath(MOD_ID, "models/monkey.obj")) .color(randomColor()) .seeThrough(false) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(0, time * 5, 0)); + t.setShapeMatrixRotationDegrees(0, time * 5, 0); }) .build(Shape.RenderingType.BATCH) ); @@ -155,9 +216,9 @@ public static void init(){ .lineWidth(4.0f) .color(randomColor()) .seeThrough(false) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(0, time * 5, 0)); + t.setShapeMatrixRotationDegrees(0, time * 5, 0); }) .build(Shape.RenderingType.BATCH) ); @@ -173,9 +234,9 @@ public static void init(){ .axis(CircleLikeShape.CircleAxis.Y) .color(randomColor()) .seeThrough(true) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(time * 1, 0, 0)); + t.setShapeMatrixRotationDegrees(time, 0, 0); double f = (Math.sin(time * 0.1) + 1) / 2; // 0 → 1 → 0 int seg = (int)(3 + f * 20-3); @@ -196,9 +257,9 @@ public static void init(){ .axis(CircleLikeShape.CircleAxis.Z) .color(randomColor()) .seeThrough(false) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(time * 3, time * 4, 0)); + t.setShapeLocalPivot(new Vec3(0,0,0)); }) .build(Shape.RenderingType.BATCH) ); @@ -214,9 +275,9 @@ public static void init(){ .color(randomColor()) .seeThrough(true) .width(3) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(time * 1, 0, 0)); + t.setShapeMatrixRotationDegrees(time, 0, 0); double f = (Math.sin(time * 0.1) + 1) / 2; // 0 → 1 → 0 int seg = (int)(3 + f * 20-3); @@ -236,9 +297,10 @@ public static void init(){ .axis(CircleLikeShape.CircleAxis.Z) .color(randomColor()) .seeThrough(false) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(time * 3, time * 4, 0)); + t.setShapeMatrixRotationDegrees(time*2, time * 5, 0); + //t.setShapeLocalRotationDegrees(0,0,time * 5); }) .build(Shape.RenderingType.BATCH) ); @@ -253,7 +315,7 @@ public static void init(){ .lineWidth(3.0f) .color(randomColor()) .seeThrough(false) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); t.setStart(new Vec3(linex - 2, 0,0)); t.setEnd(new Vec3(linex + 2, 4 + (float)Math.sin(time * 0.1) * 2,0)); @@ -269,10 +331,10 @@ public static void init(){ .lineWidth(2.0f) .color(randomColor()) .seeThrough(false) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); // 动态更新顶点(旋转螺旋) - ((StripLineShape)s).setVertexes(generateSpiral(xPos(), 100, 2.0f, 5.0f, time * 0.05f)); + ((StripLineShape)t.getShape()).setVertexes(generateSpiral(xPos(), 100, 2.0f, 5.0f, time * 0.05f)); }) .build(Shape.RenderingType.BATCH) ); @@ -286,9 +348,9 @@ public static void init(){ .color(randomColor()) .seeThrough(true) .construction(BoxShape.BoxConstructionType.CENTER_AND_DIMENSIONS ) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(time * 2, time * 3, time * 1.5f)); + t.setShapeMatrixRotationDegrees(time * 2, time * 3, time * 1.5f); }) .build(Shape.RenderingType.BATCH) ); @@ -303,9 +365,9 @@ public static void init(){ .color(randomColor()) .seeThrough(false) .construction(BoxShape.BoxConstructionType.CORNERS) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(0, time * 4, 0)); + t.setShapeMatrixRotationDegrees(0, time * 4, 0); }) .build(Shape.RenderingType.BATCH) ); @@ -322,9 +384,9 @@ public static void init(){ .seeThrough(true) .lineSeeThrough(false) .construction(BoxShape.BoxConstructionType.CORNERS) - .transform((t, s) -> { + .transform((t) -> { float time = client.getGameTime(); - t.setMatrixRotation(new Vec3(time * 1.5f, time * 2.5f, 0)); + t.setShapeMatrixRotationDegrees(time * 1.5f, time * 2.5f, time*0.5f); }) .build(Shape.RenderingType.BATCH) ); @@ -355,7 +417,7 @@ public static void rotate(BoxLikeShape.BoxTransformer boxTransformer, Shape shap float gameTime = Minecraft.getInstance().level.getGameTime(); float rotationAngle = (gameTime % 3600) * 2f; - boxTransformer.setMatrixRotation(new Vec3(rotationAngle, rotationAngle, rotationAngle)); + boxTransformer.setShapeMatrixRotationDegrees(rotationAngle, rotationAngle, rotationAngle); } public static void addEntity(Entity entity) { Player player = Minecraft.getInstance().player; @@ -367,13 +429,13 @@ public static void addEntity(Entity entity) { ResourceLocation.fromNamespaceAndPath(MOD_ID, "entity_tracker_" + entityId + "/bounding_box"), new BoxFaceShape( Shape.RenderingType.BATCH, - (transformer, shape) -> { + (transformer) -> { Vec3 entityCenter = entity.position().add(0, dimensions.height() / 2, 0); if (entity.isRemoved()) { - shape.discard(); + transformer.shape.discard(); return; } - transformer.setShapeCenterPos(entityCenter); + transformer.setShapeWorldPivot(entityCenter); }, entity.position().add(0, dimensions.height() / 2, 0), new Vec3(dimensions.width(), dimensions.height(), dimensions.width()), @@ -387,13 +449,14 @@ public static void addEntity(Entity entity) { ResourceLocation.fromNamespaceAndPath(MOD_ID, "entity_tracker_" + entityId + "/line"), new LineShape( Shape.RenderingType.BATCH, - (transformer, shape) -> { + (transformer) -> { if (entity.isRemoved()) { - shape.discard(); + transformer.getShape().discard(); return; } if (Minecraft.getInstance().level != null && Minecraft.getInstance().player != null) { transformer.setStart(player.getEyePosition().add(player.getLookAngle().scale(2))); + //transformer.lineModelInfo.syncLastToTarget(); transformer.setEnd(entity.position()); } }, diff --git a/src/main/java/mypals/ml/transform/FloatValueTransformer.java b/src/main/java/mypals/ml/transform/FloatValueTransformer.java deleted file mode 100644 index 18c1102..0000000 --- a/src/main/java/mypals/ml/transform/FloatValueTransformer.java +++ /dev/null @@ -1,30 +0,0 @@ -package mypals.ml.transform; - -import java.util.function.Consumer; -import net.minecraft.util.Mth; - -public class FloatValueTransformer { - private float last = 0; - private float target = 0; - private float current = 0; - public FloatValueTransformer(float init){ - this.target = init; - syncLastToTarget(); - } - public FloatValueTransformer(){ - - } - public void setTargetValue(float target) { - this.target = target; - } - public void updateValue(Consumer setter, float delta) { - current = (float) Mth.lerp((double)delta, last, target); - setter.accept(current); - } - public void syncLastToTarget() { - this.last = this.target; - } - public float getCurrentValue(){ - return current; - } -} diff --git a/src/main/java/mypals/ml/transform/IntValueTransformer.java b/src/main/java/mypals/ml/transform/IntValueTransformer.java deleted file mode 100644 index 55e4c92..0000000 --- a/src/main/java/mypals/ml/transform/IntValueTransformer.java +++ /dev/null @@ -1,38 +0,0 @@ -package mypals.ml.transform; - -import java.util.function.Consumer; -import net.minecraft.util.Mth; - -public class IntValueTransformer { - private int last = 0; - private int target = 0; - private int current = 0; - - public IntValueTransformer(int init) { - this.target = init; - syncLastToTarget(); - } - - public IntValueTransformer() { - } - - public void setTargetValue(int target) { - this.target = target; - } - - public void updateValue(Consumer setter, float delta) { - - float lerped = Mth.lerp(delta, (float) last, (float) target); - current = Math.round(lerped); - setter.accept(current); - } - - public void syncLastToTarget() { - this.last = this.target; - } - - public int getCurrentValue() { - return current; - } -} - diff --git a/src/main/java/mypals/ml/transform/QuaternionTransformer.java b/src/main/java/mypals/ml/transform/QuaternionTransformer.java deleted file mode 100644 index 3f1b797..0000000 --- a/src/main/java/mypals/ml/transform/QuaternionTransformer.java +++ /dev/null @@ -1,32 +0,0 @@ -package mypals.ml.transform; - -import java.util.function.Consumer; -import org.joml.Quaternionf; -import org.joml.Quaternionfc; - -public class QuaternionTransformer { - private Quaternionf lastRotation = new Quaternionf(); - private Quaternionf targetRotation = new Quaternionf(); - private Quaternionf currentRotation = new Quaternionf(); - public QuaternionTransformer(Quaternionf init){ - this.targetRotation = init; - syncLastToTarget(); - } - public QuaternionTransformer(){ - - } - public void setTargetRotation(Quaternionfc targetRotation) { - this.targetRotation.set(targetRotation); - } - - public void updateRotation(Consumer setter, float delta) { - currentRotation = new Quaternionf(); - lastRotation.slerp(targetRotation, delta, currentRotation); - - setter.accept(currentRotation); - - } - public void syncLastToTarget() { - this.lastRotation.set(targetRotation); - } -} diff --git a/src/main/java/mypals/ml/transform/Vec3dTransformer.java b/src/main/java/mypals/ml/transform/Vec3dTransformer.java deleted file mode 100644 index 1af21c5..0000000 --- a/src/main/java/mypals/ml/transform/Vec3dTransformer.java +++ /dev/null @@ -1,34 +0,0 @@ -package mypals.ml.transform; - -import java.util.function.Consumer; -import net.minecraft.util.Mth; -import net.minecraft.world.phys.Vec3; - -public class Vec3dTransformer { - private Vec3 lastVector = Vec3.ZERO; - private Vec3 targetVector = Vec3.ZERO; - private Vec3 currentVector = Vec3.ZERO; - public Vec3dTransformer(Vec3 init){ - this.targetVector = init; - syncLastToTarget(); - } - public Vec3dTransformer(){ - - } - public void setTargetVector(Vec3 targetPosition) { - this.targetVector = targetPosition; - } - public void updateVector(Consumer setter, float delta) { - double d = Mth.lerp((double)delta, lastVector.x, targetVector.x); - double e = Mth.lerp((double)delta, lastVector.y, targetVector.y); - double f = Mth.lerp((double)delta, lastVector.z, targetVector.z); - currentVector = new Vec3(d,e,f); - setter.accept(currentVector); - } - public void syncLastToTarget() { - this.lastVector = this.targetVector; - } - public Vec3 getCurrentVector(){ - return currentVector; - } -} diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java b/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java new file mode 100644 index 0000000..a89add8 --- /dev/null +++ b/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java @@ -0,0 +1,168 @@ +package mypals.ml.transform.shapeTransformers; + +import com.mojang.blaze3d.vertex.PoseStack; +import mypals.ml.shape.Shape; +import mypals.ml.transform.valueTransformers.QuaternionTransformer; +import mypals.ml.transform.valueTransformers.Vec3Transformer; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; +import org.joml.Quaternionf; + +public class DefaultTransformer { + private float delta = 0; + public final Shape shape; + + public final TransformLayer local = new TransformLayer(); // 模型本地 + public final TransformLayer world = new TransformLayer(); // 世界空间 + public final TransformLayer matrix = new TransformLayer(); // 实时动画 + + public DefaultTransformer(Shape s,Vec3 center) { + this.shape = s; + this.world.setPosition(center); + this.world.syncLastToTarget(); + } + + public Shape getShape() { + return shape; + } + + public void updateTickDelta(float d) { + this.delta = d; + updateAll(d); + } + public float getTickDelta() { return delta; } + public void updateAll(float t) { + local.update(t); + world.update(t); + matrix.update(t); + } + + public void syncLastToTarget() { + local.syncLastToTarget(); + world.syncLastToTarget(); + matrix.syncLastToTarget(); + } + + public void applyTransformations(PoseStack stack,boolean lerp) { + applyLayer(stack,local,false, lerp); + applyLayer(stack,world,false,lerp); + applyLayer(stack, matrix,false, lerp); + } + public void applyModelTransformations(PoseStack stack,boolean lerp) { + applyLayer(stack,local,true,lerp); + applyLayer(stack,world,false,lerp); + } + + private void applyLayer(PoseStack stack, TransformLayer layer,boolean isLocal, boolean lerp) { + Vec3 p = layer.position.getValue(lerp); + Quaternionf r = layer.rotation.getValue(lerp); + Vec3 s = layer.scale.getValue(lerp); + + if(isLocal){ + stack.translate(p.x, p.y, p.z); + stack.mulPose(r); + stack.scale((float) s.x, (float) s.y, (float) s.z); + stack.translate(-p.x, -p.y, -p.z); + }else{ + stack.translate(p.x, p.y, p.z); + stack.mulPose(r); + stack.scale((float) s.x, (float) s.y, (float) s.z); + } + } + + + public TransformLayer local() { return local; } + public TransformLayer world() { return world; } + public TransformLayer matrix() { return matrix; } + + // ====================== WORLD GETTERS ====================== + public Vec3 getShapeWorldPivot(boolean lerp) { + return world.getPosition(lerp); + } + + public Quaternionf getShapeWorldRotation(boolean lerp) { + return world.getRotation(lerp); + } + + public Vec3 getShapeWorldScale(boolean lerp) { + return world.getScale(lerp); + } + + // ====================== LOCAL GETTERS ====================== + public Vec3 getShapeLocalPivot(boolean lerp) { + return local.getPosition(lerp); + } + + public Quaternionf getShapeLocalRotation(boolean lerp) { + return local.getRotation(lerp); + } + + public Vec3 getShapeLocalScale(boolean lerp) { + return local.getScale(lerp); + } + + // ====================== MATRIX (Render) GETTERS ====================== + public Vec3 getShapeMatrixPivot(boolean lerp) { + return matrix.getPosition(lerp); + } + + public Quaternionf getShapeMatrixRotation(boolean lerp) { + return matrix.getRotation(lerp); + } + + public Vec3 getShapeMatrixScale(boolean lerp) { + return matrix.getScale(lerp); + } + + public Vec3 getWorldPivot() { return getShapeWorldPivot(true); } + public Quaternionf getWorldRotation() { return getShapeWorldRotation(true); } + public Vec3 getWorldScale() { return getShapeWorldScale(true); } + + public Vec3 getLocalPivot() { return getShapeLocalPivot(true); } + public Quaternionf getLocalRotation() { return getShapeLocalRotation(true); } + public Vec3 getLocalScale() { return getShapeLocalScale(true); } + + public Vec3 getMatrixPivot() { return getShapeMatrixPivot(true); } + public Quaternionf getMatrixRotation() { return getShapeMatrixRotation(true); } + public Vec3 getMatrixScale() { return getShapeMatrixScale(true); } + + + // World + public void setShapeWorldPivot(Vec3 v) { world.setPosition(v); } + public void setShapeWorldRotation(Quaternionf q) { world.setRotation(q); } + public void setShapeWorldRotationDegrees(float x, float y, float z) { + world.setRotation(new Quaternionf().rotateXYZ( + (float) Math.toRadians(x), + (float) Math.toRadians(y), + (float) Math.toRadians(z) + )); + } + public void setShapeWorldScale(Vec3 s) { world.setScale(s); } + + // Local + public void setShapeLocalPivot(Vec3 v) { local.setPosition(v); } + public void setShapeLocalRotation(Quaternionf q) { local.setRotation(q); } + public void setShapeLocalRotationDegrees(float x, float y, float z) { + local.setRotation(new Quaternionf().rotateXYZ( + (float) Math.toRadians(x), + (float) Math.toRadians(y), + (float) Math.toRadians(z) + )); + } + public void setShapeLocalScale(Vec3 s) { local.setScale(s); } + + // Matrix (Render layer) + public void setShapeMatrixPivot(Vec3 v) { matrix.setPosition(v); } + public void setShapeMatrixRotation(Quaternionf q) { matrix.setRotation(q); } + public void setShapeMatrixRotationDegrees(float x, float y, float z) { + matrix.setRotation(new Quaternionf().rotateXYZ( + (float) Math.toRadians(x), + (float) Math.toRadians(y), + (float) Math.toRadians(z) + )); + } + public void setShapeMatrixScale(Vec3 s) { matrix.setScale(s); } + public boolean asyncModelInfo(){ + return false; + } +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/ModelInfoLayer.java b/src/main/java/mypals/ml/transform/shapeTransformers/ModelInfoLayer.java new file mode 100644 index 0000000..e88fbb5 --- /dev/null +++ b/src/main/java/mypals/ml/transform/shapeTransformers/ModelInfoLayer.java @@ -0,0 +1,10 @@ +package mypals.ml.transform.shapeTransformers; + +public abstract class ModelInfoLayer { + public void update(float delta){} + public boolean async(){ + return false; + } + + public void syncLastToTarget() {} +} diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/TransformLayer.java b/src/main/java/mypals/ml/transform/shapeTransformers/TransformLayer.java new file mode 100644 index 0000000..52cf064 --- /dev/null +++ b/src/main/java/mypals/ml/transform/shapeTransformers/TransformLayer.java @@ -0,0 +1,38 @@ +package mypals.ml.transform.shapeTransformers; + +import mypals.ml.transform.valueTransformers.QuaternionTransformer; +import mypals.ml.transform.valueTransformers.Vec3Transformer; +import net.minecraft.world.phys.Vec3; +import org.joml.Quaternionf; + +public class TransformLayer { + public final Vec3Transformer position = new Vec3Transformer(Vec3.ZERO); + public final QuaternionTransformer rotation = new QuaternionTransformer(); + public final Vec3Transformer scale = new Vec3Transformer(Vec3.ZERO.add(1)); + + public void update(float delta) { + position.update(delta); + rotation.update(delta); + scale.update(delta); + } + + public void syncLastToTarget() { + position.syncLastToTarget(); + rotation.syncLastToTarget(); + scale.syncLastToTarget(); + } + + public void setPosition(Vec3 v) { position.setTargetVector(v); } + public void setRotation(Quaternionf q) { rotation.setTargetRotation(q); } + public void setRotationDegrees(float x, float y, float z) { + rotation.setTargetRotation(new Quaternionf().rotateXYZ( + (float)Math.toRadians(x), + (float)Math.toRadians(y), + (float)Math.toRadians(z) + )); + } + public void setScale(Vec3 s) { scale.setTargetVector(s); } + public Vec3 getPosition(boolean useLerp) { return position.getValue(useLerp); } + public Vec3 getScale(boolean useLerp) { return scale.getValue(useLerp); } + public Quaternionf getRotation(boolean useLerp) { return rotation.getValue(useLerp); } +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/BoxModelInfo.java b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/BoxModelInfo.java new file mode 100644 index 0000000..a5647bc --- /dev/null +++ b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/BoxModelInfo.java @@ -0,0 +1,30 @@ +package mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer; + +import mypals.ml.transform.shapeTransformers.ModelInfoLayer; +import mypals.ml.transform.valueTransformers.Vec3Transformer; +import net.minecraft.world.phys.Vec3; + +public class BoxModelInfo extends ModelInfoLayer { + public Vec3Transformer boxDimensionTransformer; + public BoxModelInfo(Vec3 dim){ + boxDimensionTransformer = new Vec3Transformer(dim); + } + public boolean async(){ + return boxDimensionTransformer.async(); + } + public void update(float delta){ + boxDimensionTransformer.update(delta); + } + public Vec3 getDimension(boolean lerp){ + return boxDimensionTransformer.getValue(lerp); + } + public void setDimension(Vec3 target){ + boxDimensionTransformer.setTargetVector(target); + } + + @Override + public void syncLastToTarget() { + this.boxDimensionTransformer.syncLastToTarget(); + super.syncLastToTarget(); + } +} diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/CircleModelInfo.java b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/CircleModelInfo.java new file mode 100644 index 0000000..dec7842 --- /dev/null +++ b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/CircleModelInfo.java @@ -0,0 +1,42 @@ +package mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer; + +import mypals.ml.transform.shapeTransformers.ModelInfoLayer; +import mypals.ml.transform.valueTransformers.FloatTransformer; +import mypals.ml.transform.valueTransformers.IntTransformer; +import mypals.ml.transform.valueTransformers.Vec3Transformer; +import net.minecraft.world.phys.Vec3; + +public class CircleModelInfo extends ModelInfoLayer { + public IntTransformer segmentTransformer; + public FloatTransformer radiusTransformer; + + public CircleModelInfo(int seg,float rad){ + segmentTransformer = new IntTransformer(seg); + radiusTransformer = new FloatTransformer(rad); + } + public boolean async(){ + return radiusTransformer.async() || segmentTransformer.async(); + } + public void update(float delta){ + radiusTransformer.update(delta); + segmentTransformer.update(delta); + } + public float getRadius(boolean lerp){ + return radiusTransformer.getValue(lerp); + } + public void setRadius(float target){ + radiusTransformer.setTargetValue(target); + } + public int getSegment(boolean lerp){ + return segmentTransformer.getValue(lerp); + } + public void setSegment(int target){ + segmentTransformer.setTargetValue(target); + } + @Override + public void syncLastToTarget() { + this.segmentTransformer.syncLastToTarget(); + this.radiusTransformer.syncLastToTarget(); + super.syncLastToTarget(); + } +} diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java new file mode 100644 index 0000000..994b456 --- /dev/null +++ b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java @@ -0,0 +1,67 @@ +package mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer; + +import mypals.ml.transform.shapeTransformers.ModelInfoLayer; +import mypals.ml.transform.valueTransformers.FloatTransformer; +import mypals.ml.transform.valueTransformers.Vec3Transformer; +import net.minecraft.world.phys.Vec3; + +public class LineModelInfo extends ModelInfoLayer { + public FloatTransformer widthTransformer; + public LineModelInfo(float width){ + this.widthTransformer = new FloatTransformer(width); + } + public boolean async(){ + return widthTransformer.async(); + } + public void update(float delta){ + widthTransformer.update(delta); + } + public float getWidth(boolean lerp){ + return widthTransformer.getValue(lerp); + } + public void setWidth(float target){ + widthTransformer.setTargetValue(target); + } + + @Override + public void syncLastToTarget() { + this.widthTransformer.syncLastToTarget(); + super.syncLastToTarget(); + } + public static class TwoPointLineModelInfo extends LineModelInfo { + public Vec3Transformer endPointTransformer; + public Vec3Transformer startPointTransformer; + public TwoPointLineModelInfo(Vec3 start, Vec3 end,float width){ + super(width); + endPointTransformer = new Vec3Transformer(end); + startPointTransformer = new Vec3Transformer(start); + } + public boolean async(){ + return super.async() || endPointTransformer.async() || startPointTransformer.async(); + } + public void update(float delta){ + endPointTransformer.update(delta); + startPointTransformer.update(delta); + super.update(delta); + } + public Vec3 getStart(boolean lerp){ + return startPointTransformer.getValue(lerp); + } + public Vec3 getEnd(boolean lerp){ + return endPointTransformer.getValue(lerp); + } + public void setStart(Vec3 target){ + startPointTransformer.setTargetVector(target); + } + public void setEnd(Vec3 target){ + endPointTransformer.setTargetVector(target); + } + + @Override + public void syncLastToTarget() { + this.startPointTransformer.syncLastToTarget(); + this.endPointTransformer.syncLastToTarget(); + super.syncLastToTarget(); + } + } +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/transform/valueTransformers/FloatTransformer.java b/src/main/java/mypals/ml/transform/valueTransformers/FloatTransformer.java new file mode 100644 index 0000000..771af35 --- /dev/null +++ b/src/main/java/mypals/ml/transform/valueTransformers/FloatTransformer.java @@ -0,0 +1,30 @@ +package mypals.ml.transform.valueTransformers; + +import net.minecraft.util.Mth; + +public final class FloatTransformer extends ValueTransformer { + + public FloatTransformer(float initial) { + this.target = initial; + syncLastToTarget(); + } + + public FloatTransformer() { this(0f); } + + @Override public void update(float delta) { + current = (float) Mth.lerp(delta, last, target); + } + + @Override public void syncLastToTarget() { + this.last = this.target; + this.current = this.target; + } + + @Override protected void setTarget(Float value) { + this.target = value; + } + + public void setTargetValue(float v) { setTarget(v); } + + public float getCurrentValue() { return current; } +} diff --git a/src/main/java/mypals/ml/transform/valueTransformers/IntTransformer.java b/src/main/java/mypals/ml/transform/valueTransformers/IntTransformer.java new file mode 100644 index 0000000..b2c965e --- /dev/null +++ b/src/main/java/mypals/ml/transform/valueTransformers/IntTransformer.java @@ -0,0 +1,32 @@ +package mypals.ml.transform.valueTransformers; + +import net.minecraft.util.Mth; + +public final class IntTransformer extends ValueTransformer { + + public IntTransformer(int initial) { + this.target = initial; + syncLastToTarget(); + } + + public IntTransformer() { this(0); } + + @Override public void update(float delta) { + float lerped = Mth.lerp(delta, last.floatValue(), target.floatValue()); + current = Math.round(lerped); + } + + @Override public void syncLastToTarget() { + this.last = this.target; + this.current = this.target; + } + + @Override protected void setTarget(Integer value) { + this.target = value; + } + + public void setTargetValue(int v) { setTarget(v); } + + public int getCurrentValue() { return current; } +} + diff --git a/src/main/java/mypals/ml/transform/valueTransformers/QuaternionTransformer.java b/src/main/java/mypals/ml/transform/valueTransformers/QuaternionTransformer.java new file mode 100644 index 0000000..c57aa39 --- /dev/null +++ b/src/main/java/mypals/ml/transform/valueTransformers/QuaternionTransformer.java @@ -0,0 +1,28 @@ +package mypals.ml.transform.valueTransformers; +import org.joml.Quaternionf; +import org.joml.Quaternionfc; +public final class QuaternionTransformer extends ValueTransformer { + + public QuaternionTransformer(Quaternionf initial) { + this.target = new Quaternionf(initial); + syncLastToTarget(); + } + + public QuaternionTransformer() { this(new Quaternionf()); } + + @Override public void update(float delta) { + current = new Quaternionf(); + last.slerp(target, delta, current); + } + + @Override public void syncLastToTarget() { + this.last = new Quaternionf(target); + this.current = new Quaternionf(target); + } + + @Override protected void setTarget(Quaternionf value) { + this.target.set(value); + } + + public void setTargetRotation(Quaternionfc rot) { setTarget(new Quaternionf(rot)); } +} diff --git a/src/main/java/mypals/ml/transform/valueTransformers/ValueTransformer.java b/src/main/java/mypals/ml/transform/valueTransformers/ValueTransformer.java new file mode 100644 index 0000000..0daaef1 --- /dev/null +++ b/src/main/java/mypals/ml/transform/valueTransformers/ValueTransformer.java @@ -0,0 +1,23 @@ +package mypals.ml.transform.valueTransformers; + +public abstract class ValueTransformer { + + protected T last; // value at the start of the current tick + protected T target; // value we are moving toward + protected T current; // interpolated value (updated each frame) + + /** Update tick-delta */ + public abstract void update(float delta); + + /** Set last → target (instant change) */ + public abstract void syncLastToTarget(); + + /** Current interpolated value */ + public T getValue(boolean lerp) { return lerp?current:target; } + + public boolean async() { + return !current.equals(target); + } + /** Set a new target*/ + protected abstract void setTarget(T value); +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/transform/valueTransformers/Vec3Transformer.java b/src/main/java/mypals/ml/transform/valueTransformers/Vec3Transformer.java new file mode 100644 index 0000000..b75671b --- /dev/null +++ b/src/main/java/mypals/ml/transform/valueTransformers/Vec3Transformer.java @@ -0,0 +1,32 @@ +package mypals.ml.transform.valueTransformers; + +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; + +public final class Vec3Transformer extends ValueTransformer { + + public Vec3Transformer(Vec3 initial) { + this.target = initial; + syncLastToTarget(); + } + + public Vec3Transformer() { this(Vec3.ZERO); } + + @Override public void update(float delta) { + double x = Mth.lerp(delta, last.x, target.x); + double y = Mth.lerp(delta, last.y, target.y); + double z = Mth.lerp(delta, last.z, target.z); + current = new Vec3(x, y, z); + } + + @Override public void syncLastToTarget() { + this.last = this.target; + this.current = this.target; + } + + @Override protected void setTarget(Vec3 value) { + this.target = value; + } + + public void setTargetVector(Vec3 v) { setTarget(v); } +} \ No newline at end of file From 0c88c4f33d8ee4b4762b7973357b2427b4ba29dc Mon Sep 17 00:00:00 2001 From: hotpad100c <46704388+hotpad100c@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:16:58 +0900 Subject: [PATCH 2/6] New transform system --- .../builders/vertexBuilders/BatchVertexBuilder.java | 11 ++++++----- .../vertexBuilders/ImmediateVertexBuilder.java | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java index 901e42f..34d0b74 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java @@ -40,12 +40,13 @@ public void drawBatch() { return; } - setUpRendererSystem(null); - - MeshData builtBuffer = this.getBufferBuilder().buildOrThrow(); - BufferUploader.drawWithShader(builtBuffer); + MeshData builtBuffer = this.getBufferBuilder().build(); + if(builtBuffer!=null){ + setUpRendererSystem(null); + BufferUploader.drawWithShader(builtBuffer); + restoreRendererSystem(); + } - restoreRendererSystem(); isBuilding = false; } diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java index 261a8e8..de6fdc0 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java @@ -20,11 +20,12 @@ public void draw(Shape shape, Consumer builder, RenderMethod rend RenderSystem.setShader(renderMethod.shader()); builder.accept(this); - setUpRendererSystem(shape); - MeshData builtBuffer = this.getBufferBuilder().buildOrThrow(); - BufferUploader.drawWithShader(builtBuffer); - - restoreRendererSystem(); + MeshData builtBuffer = this.getBufferBuilder().build(); + if(builtBuffer!=null){ + setUpRendererSystem(shape); + BufferUploader.drawWithShader(builtBuffer); + restoreRendererSystem(); + } } } \ No newline at end of file From 78c9a39f4c354cee168b79617473f4e0c3e495f6 Mon Sep 17 00:00:00 2001 From: hotpad100c <46704388+hotpad100c@users.noreply.github.com> Date: Sat, 15 Nov 2025 20:26:12 +0900 Subject: [PATCH 3/6] Shape raycast --- src/main/java/mypals/ml/Helpers.java | 9 -- .../ml/builderManager/BuilderManagers.java | 6 - .../vertexBuilders/BatchVertexBuilder.java | 2 +- .../vertexBuilders/BufferedVertexBuilder.java | 1 + .../ImmediateVertexBuilder.java | 2 +- .../ml/collision/RayModelIntersection.java | 104 ++++++++++++++++++ src/main/java/mypals/ml/shape/Shape.java | 42 ++++++- src/main/java/mypals/ml/shape/Shape.md | 45 -------- src/main/java/mypals/ml/shape/ShapeForAI.md | 33 ------ .../java/mypals/ml/shape/box/BoxShape.java | 3 - .../cylinder/CylinderWireframeShape.java | 2 +- .../java/mypals/ml/shape/line/LineShape.java | 8 ++ .../mypals/ml/shape/line/StripLineShape.java | 20 ++-- .../ml/shape/model/ObjModelShapeOutline.java | 39 ++----- .../ml/shape/round/LineCircleShape.java | 11 +- src/main/java/mypals/ml/test/Tester.java | 74 ++++++++----- .../shapeTransformers/DefaultTransformer.java | 8 +- 17 files changed, 234 insertions(+), 175 deletions(-) create mode 100644 src/main/java/mypals/ml/collision/RayModelIntersection.java delete mode 100644 src/main/java/mypals/ml/shape/Shape.md delete mode 100644 src/main/java/mypals/ml/shape/ShapeForAI.md diff --git a/src/main/java/mypals/ml/Helpers.java b/src/main/java/mypals/ml/Helpers.java index 377f5f3..9f09e28 100644 --- a/src/main/java/mypals/ml/Helpers.java +++ b/src/main/java/mypals/ml/Helpers.java @@ -27,13 +27,4 @@ public static Vec3 min(Vec3 a, Vec3 b) { Math.min(a.z, b.z) ); } - - - public static double maxComponent(Vec3 v) { - return Math.max(Math.max(v.x, v.y), v.z); - } - - public static double minComponent(Vec3 v) { - return Math.min(Math.min(v.x, v.y), v.z); - } } diff --git a/src/main/java/mypals/ml/builderManager/BuilderManagers.java b/src/main/java/mypals/ml/builderManager/BuilderManagers.java index 6f16485..bb8042f 100644 --- a/src/main/java/mypals/ml/builderManager/BuilderManagers.java +++ b/src/main/java/mypals/ml/builderManager/BuilderManagers.java @@ -7,22 +7,16 @@ import java.util.List; public class BuilderManagers { - public static BuilderManager QUADS_BUILDER_MANAGER = null; public static BuilderManager LINES_BUILDER_MANAGER = null; public static BuilderManager LINE_STRIP_BUILDER_MANAGER = null; public static BuilderManager TRIANGLES_BUILDER_MANAGER = null; - public static BuilderManager TRIANGLES_STRIP_BUILDER_MANAGER = null; - public static BuilderManager TRIANGLES_FAN_BUILDER_MANAGER = null; public static BuilderManager TEXT = null; public static List builders = new ArrayList<>(); public static void init() { Matrix4f matrix4f = new Matrix4f(); - QUADS_BUILDER_MANAGER = register(matrix4f,RenderMethod.QUADS,"quads_builder_manager"); LINES_BUILDER_MANAGER = register(matrix4f, RenderMethod.LINES,"lines_builder_manager"); LINE_STRIP_BUILDER_MANAGER = register(matrix4f,RenderMethod.LINE_STRIP,"line_strip_builder_manager"); TRIANGLES_BUILDER_MANAGER = register(matrix4f,RenderMethod.TRIANGLES,"triangles_builder_manager"); - TRIANGLES_STRIP_BUILDER_MANAGER = register(matrix4f,RenderMethod.TRIANGLES_STRIP,"triangles_strip_builder_manager"); - TRIANGLES_FAN_BUILDER_MANAGER = register(matrix4f,RenderMethod.TRIANGLES_FAN,"triangles_fan_builder_manager"); TEXT = register(matrix4f,RenderMethod.TEXT,"text_manager"); } public static void updateMatrix(Matrix4f matrix4f){ diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java index 34d0b74..0034412 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java @@ -39,7 +39,7 @@ public void drawBatch() { if (!isBuilding) { return; } - + //flushTransparent(); MeshData builtBuffer = this.getBufferBuilder().build(); if(builtBuffer!=null){ setUpRendererSystem(null); diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java index 6ffab61..34bd262 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java @@ -24,6 +24,7 @@ public BufferedVertexBuilder(Matrix4f modelViewMatrix, boolean seeThrough, Rende public void rebuild(RenderMethod renderMethod, Consumer builder) { start(renderMethod); push(builder); + //flushTransparent(); end(); } diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java index de6fdc0..f8b898b 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java @@ -19,7 +19,7 @@ public void draw(Shape shape, Consumer builder, RenderMethod rend begin(renderMethod); RenderSystem.setShader(renderMethod.shader()); builder.accept(this); - + //flushTransparent(); MeshData builtBuffer = this.getBufferBuilder().build(); if(builtBuffer!=null){ diff --git a/src/main/java/mypals/ml/collision/RayModelIntersection.java b/src/main/java/mypals/ml/collision/RayModelIntersection.java new file mode 100644 index 0000000..bb85e5e --- /dev/null +++ b/src/main/java/mypals/ml/collision/RayModelIntersection.java @@ -0,0 +1,104 @@ +package mypals.ml.collision; + +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class RayModelIntersection { + private static final double EPSILON = 1e-6; + + + public static class Ray { + public Vec3 origin; + public Vec3 direction; + + public Ray(Vec3 origin, Vec3 direction) { + this.origin = origin; + this.direction = direction.normalize(); + } + } + public static class HitResult { + public boolean hit; + @Nullable + public Vec3 pos; + public double distance; + + public HitResult(boolean hit, @Nullable Vec3 pos, double distance) { + this.hit = hit; + this.pos = pos; + this.distance = distance; + } + } + + public static boolean intersectTriangle(Ray ray, Vec3 v0, Vec3 v1, Vec3 v2, double[] outT) { + + + Vec3 e1 = v1.subtract(v0); + Vec3 e2 = v2.subtract(v0); + Vec3 P = ray.direction.cross(e2); + double det = e1.dot(P); + + if (Math.abs(det) < EPSILON) return false; + + double invDet = 1.0 / det; + Vec3 T = ray.origin.subtract(v0); + + double u = T.dot(P) * invDet; + if (u < 0.0 || u > 1.0) return false; + + Vec3 Q = T.cross(e1); + double v = ray.direction.dot(Q) * invDet; + if (v < 0.0 || u + v > 1.0) return false; + + double t = e2.dot(Q) * invDet; + if (t < EPSILON) return false; + + if (outT != null) outT[0] = t; + return true; + } + + public static HitResult rayIntersectsModel( + Ray ray, + List modelVertexes, + int[] indexBuffer) { + + double closestT = Double.POSITIVE_INFINITY; + Vec3 closestHitVec3d = null; + boolean hit = false; + + for (int i = 0; i < indexBuffer.length; i += 3) { + int i0 = indexBuffer[i]; + int i1 = indexBuffer[i + 1]; + int i2 = indexBuffer[i + 2]; + if (i0 >= modelVertexes.size() || i1 >= modelVertexes.size() || i2 >= modelVertexes.size()) { + continue; + } + + Vec3 v0 = modelVertexes.get(i0); + Vec3 v1 = modelVertexes.get(i1); + Vec3 v2 = modelVertexes.get(i2); + + double[] tOut = new double[1]; + if (intersectTriangle(ray, v0, v1, v2, tOut)) { + double t = tOut[0]; + if (t < closestT) { + closestT = t; + closestHitVec3d = ray.origin.add(ray.direction.scale(t)); + hit = true; + } + } + } + + if (!hit) { + return new HitResult(hit,null, -1); + } + + Vec3 hitPoint = new Vec3( + (float) closestHitVec3d.x, + (float) closestHitVec3d.y, + (float) closestHitVec3d.z + ); + return new HitResult(hit,hitPoint, closestT); + } +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/Shape.java b/src/main/java/mypals/ml/shape/Shape.java index 0e24099..4aef250 100644 --- a/src/main/java/mypals/ml/shape/Shape.java +++ b/src/main/java/mypals/ml/shape/Shape.java @@ -1,9 +1,13 @@ package mypals.ml.shape; import mypals.ml.builders.vertexBuilders.VertexBuilder; +import mypals.ml.collision.RayModelIntersection; import mypals.ml.shapeManagers.ShapeManagers; import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Quaternionf; @@ -12,7 +16,9 @@ import java.awt.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Consumer; public abstract class Shape { @@ -32,7 +38,7 @@ public enum RenderingType { IMMEDIATE, BATCH, BUFFERED } public List model_vertexes = new ArrayList<>();//This is the original model of our model. public int[] indexBuffer = new int[0]; - + public Map customData = new HashMap<>(); protected Shape(RenderingType type, Consumer transform,Color color, Vec3 center, boolean seeThrough) { this(type,color,seeThrough); @@ -129,6 +135,18 @@ public void beforeDraw(PoseStack matrixStack, float deltaTime) { //We'll apply all transformations to the matrix stack before drawing(With lerp!). } + public RayModelIntersection.HitResult isPlayerLookingAt(){ + Minecraft minecraft = Minecraft.getInstance(); + Player p = minecraft.player; + Camera camera = minecraft.gameRenderer.getMainCamera(); + RayModelIntersection.Ray r = new RayModelIntersection.Ray(camera.getPosition(), p.getForward()); + + return RayModelIntersection.rayIntersectsModel( + r, + this.getRealModel(), + this.indexBuffer + ); + } public void draw(VertexBuilder builder, PoseStack matrixStack, float deltaTime) { if(!visible) return; matrixStack.pushPose(); @@ -145,10 +163,30 @@ protected void drawInternal(VertexBuilder builder) { } } - + public void setBaseColor(Color color){ + this.baseColor = color; + } public void setId(ResourceLocation id) { this.id = id; } public void discard() { children.forEach(Shape::discard); ShapeManagers.removeShape(this.id); } public void syncLastToTarget() { transformer.syncLastToTarget(); } + + private Map data() { + if (customData == null) customData = new HashMap<>(1); + return customData; + } + + public void putCustomData(String key, T value) { + data().put(key , value); + } + + @SuppressWarnings("unchecked") + public T getCustomData(String key, T def) { + return (T) customData.getOrDefault(key, null); + } + @SuppressWarnings("unchecked") + public void removeCustomData(String key) { + customData.remove(key); + } } \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/Shape.md b/src/main/java/mypals/ml/shape/Shape.md deleted file mode 100644 index 66cdf8e..0000000 --- a/src/main/java/mypals/ml/shape/Shape.md +++ /dev/null @@ -1,45 +0,0 @@ -### Shape Rendering System - -#### How It Works -Think of your shape in **3 layers**: - -| Layer | What It Is | When It Updates | Magic Applied | -|-------|------------|-----------------|---------------| -| **Raw** | Your "blueprint" (e.g., 8 corners of a cube) | Only when you change the design | Nothing yet! | -| **Shape** | "Baked" version in game space | When position/scale changes | **Local + World** transforms (locked in forever) | -| **Rendering** | Final "draw me!" list (e.g., 36 triangles or wireframe lines) | Every frame (or when style changes) | **Pivot + Spin + Zoom** (animated live!) | - -#### Superpowers -- **Animate anything**: Spin a cube? `transformer.setRotation(...)` -- **Switch styles**: Solid → Wireframe? Just flip a flag! -- **No stutter**: Only rebuild what's dirty. -- **Type-safe**: Code knows *exactly* what shape it is (no casting hacks). -- **Minecraft-ready**: Works with `PoseStack`, colors, transparency, visibility. - -#### Example: Spinning Red Cube -```java -class RedCube extends Shape { - RedCube() { - super(BATCH); // Fast batch rendering - setBaseColor(Color.RED); - setLocalScale(new Vec3(2,2,2)); // Big! - markGeometryDirty(); // Bake it! - } - - // 8 raw corners - void generateRawGeometry() { /* add 8 Vec3 points */ } - - // 36 triangles for solid look - void generateRenderingTopology() { /* unfold faces */ } -} - -// Use it: -RedCube cube = new RedCube(); -cube.transformer().setPivot(new Vec3(0,1,0)); // Spin around top -cube.transformer().setRotation(new Quaternionf().rotateY(1)); // Spin! -``` - -**Result**: A red cube spins smoothly in-game. Add to your mod → instant 3D UI, tools, or effects! - -**Pro Tip**: Copy the **prompt table** (from before) into AI chats for instant code gen. -**Ready to build?** Load OBJ models next? 🚀 Let me know! \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/ShapeForAI.md b/src/main/java/mypals/ml/shape/ShapeForAI.md deleted file mode 100644 index 7e0ed73..0000000 --- a/src/main/java/mypals/ml/shape/ShapeForAI.md +++ /dev/null @@ -1,33 +0,0 @@ -### Shape System Prompt (Copy-Paste Ready) - -> **You are a 3D rendering expert in Minecraft/Forge. Implement/extend shapes using this exact system:** -> -> | Layer | Purpose | Vertices | Update Trigger | Transform Applied | -> |-------|---------|----------|----------------|-------------------| -> | **Raw** | Design-time geometry | `raw_vertexes` (unique points) | `generateRawGeometry()` | None | -> | **Shape** | Baked local + world space | `shape_vertexes` | `markGeometryDirty()` → `bakeLocalAndWorldTransform()` | `localPos/Rot/Scale` + `worldPos/Rot/Scale` (via `translationRotateScale`) | -> | **Rendering** | Final draw sequence | `rendering_vertexes` (unfolded tris/edges) | `markTopologyDirty()` → `generateRenderingTopology()` → `applyMatrixTransform()` | **Per-frame**: `pivot` + `rotation` + `scale` (matrix only) | -> -> **Key Rules:** -> - **Transforms**: Local/World → **baked once** into `shape_vertexes`. Matrix (`DefaultTransformer`) → **every frame** on `rendering_vertexes`. -> - **Dirty Flags**: `geometryDirty` rebuilds shape verts; `topologyDirty` rebuilds rendering verts (e.g., wireframe vs solid). -> - **Transformer**: `DefaultTransformer` → `getShape()` returns **exact subclass** (type-safe). -> - **Draw Flow**: `beforeDraw()` → update delta → bake if dirty → topology if dirty → matrix transform → `drawInternal()` loops `rendering_vertexes`. -> - **RenderingType**: IMMEDIATE/BATCH/BUFFERED. -> - **API**: `setLocal*/setWorld*` → mark dirty. `transformer.setPivot/Rotation/Scale` → animated. -> - **Extends**: Subclass → override `generateRawGeometry()` + `generateRenderingTopology()`. -> -> **Example Cube**: -> ```java:disable-run -> class Cube extends Shape { -> Cube() { super(BATCH); setLocalScale(Vec3(2,2,2)); markGeometryDirty(); } -> @Override void generateRawGeometry() { /* 8 corners */ } -> @Override void generateRenderingTopology() { /* 36 tris or 24 lines */ } -> } -> ``` -> -> **Use this for ALL shape code. No deviations.** - -**Paste this prompt to any AI → instant understanding!** 🚀 -(132 words — ultra-concise) -``` \ No newline at end of file diff --git a/src/main/java/mypals/ml/shape/box/BoxShape.java b/src/main/java/mypals/ml/shape/box/BoxShape.java index d5cfaa9..809e86b 100644 --- a/src/main/java/mypals/ml/shape/box/BoxShape.java +++ b/src/main/java/mypals/ml/shape/box/BoxShape.java @@ -45,9 +45,6 @@ public BoxShape(RenderingType type, protected void generateRawGeometry(boolean lerp) { } - - // ====================== BoxLikeShape 接口实现 ====================== - @Override public Vec3 getMin() { BoxTransformer bt = (BoxTransformer) transformer; diff --git a/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java b/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java index 5bb80e9..c4f1579 100644 --- a/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java +++ b/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java @@ -27,7 +27,7 @@ public CylinderWireframeShape(RenderingType type, Consumer transform.accept((CylinderWireframeTransformer) this.transformer); this.setAxis(circleAxis); - this.setLocalPosition(center); + this.setWorldPosition(center); syncLastToTarget(); } diff --git a/src/main/java/mypals/ml/shape/line/LineShape.java b/src/main/java/mypals/ml/shape/line/LineShape.java index 0e09285..a039ed0 100644 --- a/src/main/java/mypals/ml/shape/line/LineShape.java +++ b/src/main/java/mypals/ml/shape/line/LineShape.java @@ -2,8 +2,12 @@ import com.mojang.blaze3d.systems.RenderSystem; import mypals.ml.builders.vertexBuilders.VertexBuilder; +import mypals.ml.collision.RayModelIntersection; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.core.TwoPointsLineShape; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.function.BiConsumer; @@ -83,6 +87,10 @@ protected void generateRawGeometry(boolean lerp) { this.indexBuffer = new int[] { 0, 1 }; } + @Override + public RayModelIntersection.HitResult isPlayerLookingAt(){ + return new RayModelIntersection.HitResult(false,null,-1); + } protected void drawInternal(VertexBuilder builder) { RenderSystem.lineWidth(getWidth(true)); builder.putColor(baseColor); diff --git a/src/main/java/mypals/ml/shape/line/StripLineShape.java b/src/main/java/mypals/ml/shape/line/StripLineShape.java index 07c697e..0ce832c 100644 --- a/src/main/java/mypals/ml/shape/line/StripLineShape.java +++ b/src/main/java/mypals/ml/shape/line/StripLineShape.java @@ -2,6 +2,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import mypals.ml.builders.vertexBuilders.VertexBuilder; +import mypals.ml.collision.RayModelIntersection; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.core.StripLineLikeShape; import net.minecraft.world.phys.Vec3; @@ -46,20 +47,16 @@ public Vec3 calculateShapeCenterPos() { double n = vertexes.size(); return new Vec3(sumX / n, sumY / n, sumZ / n); } - + @Override + public RayModelIntersection.HitResult isPlayerLookingAt(){ + return new RayModelIntersection.HitResult(false,null,-1); + } @Override protected void generateRawGeometry(boolean lerp) { model_vertexes.clear(); if (vertexes.size() < 2) return; - - // (1) 仅计算模型内部中心点(局部坐标) Vec3 localCenter = calculateShapeCenterPos(); transformer.setShapeLocalPivot(localCenter); - - // (2) 世界 pivot 由外部或实体位置决定,不要在这里改 - // transformer.setWorldPivot(...); - - // (3) 将模型顶点移到以 localPivot 为中心 for (Vec3 v : vertexes) { model_vertexes.add(v.subtract(localCenter)); } @@ -80,7 +77,7 @@ protected void drawInternal(VertexBuilder builder) { Vec3 first = model_vertexes.getFirst(); builder.putColor(new Color(0, 0, 0, 0)); - builder.putVertex((float) first.x, (float) first.y, (float) first.z, 0f, 0f, 0f); + builder.putVertex(first, Vec3.ZERO); for (int i = 0; i < n; i++) { Color vColor = baseColor; if (i < vertexColors.size()) vColor = vertexColors.get(i); @@ -103,13 +100,12 @@ protected void drawInternal(VertexBuilder builder) { } Vec3 pos = model_vertexes.get(i); - builder.putVertex((float) pos.x, (float) pos.y, (float) pos.z, - (float) normal.x, (float) normal.y, (float) normal.z); + builder.putVertex(pos, normal); } Vec3 last = model_vertexes.get(n - 1); builder.putColor(new Color(0, 0, 0, 0)); - builder.putVertex((float) last.x, (float) last.y, (float) last.z, 0f, 0f, 0f); + builder.putVertex(last, Vec3.ZERO); } @Override diff --git a/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java b/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java index f84af04..53eb515 100644 --- a/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java +++ b/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java @@ -15,24 +15,6 @@ public class ObjModelShapeOutline extends ObjModelShape implements LineLikeShape { public float lineWidth; - public Color color = Color.WHITE; - - public ObjModelShapeOutline(RenderingType type, - Consumer transform, - ResourceLocation resourceLocation, - Vec3 center, - float lineWidth, - Color color) { - this(type, transform, resourceLocation, center, lineWidth, color, false); - } - - protected ObjModelShapeOutline(RenderingType type) { - super(type, Color.WHITE, false); - } - - protected ObjModelShapeOutline(RenderingType type, boolean seeThrough) { - super(type, Color.WHITE, seeThrough); - } public ObjModelShapeOutline(RenderingType type, Consumer transform, @@ -47,7 +29,7 @@ public ObjModelShapeOutline(RenderingType type, this.transformFunction = (t) -> transform.accept((SimpleLineTransformer) this.transformer); this.lineWidth = lineWidth; - this.color = color; + this.baseColor = color; ((SimpleLineTransformer) this.transformer).setWidth(this.lineWidth); this.transformer.setShapeWorldPivot(center); @@ -79,17 +61,20 @@ protected void drawInternal(VertexBuilder builder) { return; RenderSystem.lineWidth(this.lineWidth); - builder.putColor(this.color); + builder.putColor(this.baseColor); + + for (int[] face : model.faces) { + int n = face.length; + if (n < 2) continue; - for (int i = 0; i < indexBuffer.length; i += 3) { - Vec3 v0 = model_vertexes.get(indexBuffer[i]); - Vec3 v1 = model_vertexes.get(indexBuffer[i + 1]); - Vec3 v2 = model_vertexes.get(indexBuffer[i + 2]); + for (int i = 0; i < n; i++) { + Vec3 v0 = model_vertexes.get(face[i]); + Vec3 v1 = model_vertexes.get(face[(i + 1) % n]); - addLineSegment(builder, v0, v1); - addLineSegment(builder, v1, v2); - addLineSegment(builder, v2, v0); + addLineSegment(builder, v0, v1); + } } + } } diff --git a/src/main/java/mypals/ml/shape/round/LineCircleShape.java b/src/main/java/mypals/ml/shape/round/LineCircleShape.java index 28b4a63..9452539 100644 --- a/src/main/java/mypals/ml/shape/round/LineCircleShape.java +++ b/src/main/java/mypals/ml/shape/round/LineCircleShape.java @@ -69,7 +69,7 @@ protected void generateRawGeometry(boolean lerp) { indexBuffer = new int[segments * 2]; for (int i = 0; i < segments; i++) { indexBuffer[i * 2] = i; - indexBuffer[i * 2 + 1] = (i + 1) % segments; // 循环闭合 + indexBuffer[i * 2 + 1] = (i + 1) % segments; } } @@ -160,7 +160,7 @@ protected void drawInternal(VertexBuilder builder) { Vec3 first = model_vertexes.getFirst(); builder.putColor(new Color(0, 0, 0, 0)); - builder.putVertex((float) first.x, (float) first.y, (float) first.z, 0f, 0f, 0f); + builder.putVertex(first, Vec3.ZERO); builder.putColor(baseColor); for (int i = 0; i < n; i++) { @@ -182,16 +182,15 @@ protected void drawInternal(VertexBuilder builder) { } Vec3 pos = model_vertexes.get(i); - builder.putVertex((float) pos.x, (float) pos.y, (float) pos.z, - (float) normal.x, (float) normal.y, (float) normal.z); + builder.putVertex(pos,normal); } Vec3 finish = model_vertexes.getFirst(); - builder.putVertex((float) finish.x, (float) finish.y, (float) finish.z, 0f, 0f, 0f); + builder.putVertex(finish,Vec3.ZERO); Vec3 last = model_vertexes.get(n - 1); builder.putColor(new Color(0, 0, 0, 0)); - builder.putVertex((float) last.x, (float) last.y, (float) last.z, 0f, 0f, 0f); + builder.putVertex(last,Vec3.ZERO); } } diff --git a/src/main/java/mypals/ml/test/Tester.java b/src/main/java/mypals/ml/test/Tester.java index 99e6450..9f5b3a1 100644 --- a/src/main/java/mypals/ml/test/Tester.java +++ b/src/main/java/mypals/ml/test/Tester.java @@ -1,6 +1,7 @@ package mypals.ml.test; import mypals.ml.builders.shapeBuilders.ShapeGenerator; +import mypals.ml.collision.RayModelIntersection; import mypals.ml.shape.basics.BoxLikeShape; import mypals.ml.shape.basics.CircleLikeShape; import mypals.ml.shape.box.BoxFaceShape; @@ -13,6 +14,7 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; @@ -31,7 +33,7 @@ public class Tester { public static boolean added =false; public static Random random = new Random(); - public static float spacing = 8.0f; // 每个图形间隔 8 格 + public static float spacing = 8.0f; public static int index = 0; static Color randomColor() { @@ -39,7 +41,7 @@ static Color randomColor() { random.nextInt(256), random.nextInt(256), random.nextInt(256), - 120 + random.nextInt(136) // 透明度 120~255 + 120 + random.nextInt(136) ); } @@ -88,10 +90,10 @@ public static void init(){ .segments(64) .axis(CircleLikeShape.CircleAxis.Y) .color(randomColor()) - .seeThrough(true) + .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(0, time * 3, 0); + t.setShapeWorldRotationDegrees(0, time * 3, 0); }) .build(Shape.RenderingType.BATCH) ); @@ -109,12 +111,12 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - //t.setShapeMatrixRotationDegrees(0, time * 4, 0); + //t.setShapeWorldRotationDegrees(0, time * 4, 0); t.setShapeLocalRotationDegrees(0,time*4,0); t.setShapeWorldRotationDegrees(time*4, 0, 0); }) - .build(Shape.RenderingType.BATCH) + .build(Shape.RenderingType.BUFFERED) ); // 3. Sphere @@ -125,12 +127,12 @@ public static void init(){ .radius(2.0f) .segments(32) .color(randomColor()) - .seeThrough(true) + .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(time * 2, time * 3, 0); + t.setShapeWorldRotationDegrees(time * 2, time * 3, 0); }) - .build(Shape.RenderingType.BATCH) + .build(Shape.RenderingType.BUFFERED) ); Vec3 spos = new Vec3(xPos(), 0,20); @@ -158,7 +160,7 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(0, time*6, 0); + t.setShapeWorldRotationDegrees(0, time*6, 0); }) .build(Shape.RenderingType.BATCH); @@ -170,7 +172,12 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - //t.setShapeMatrixRotationDegrees(time * 2, time * 3, 0); + //t.setShapeWorldRotationDegrees(time * 2, time * 3, 0); + if(t.shape.isPlayerLookingAt().hit){ + t.shape.baseColor = new Color(255, 234, 0,100); + }else{ + t.shape.baseColor = new Color(255, 0, 251,100); + } }) .build(Shape.RenderingType.BATCH); @@ -202,7 +209,15 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(0, time * 5, 0); + + t.setShapeWorldRotationDegrees(0, time * 5, 0); + + if(t.shape.isPlayerLookingAt().hit){ + t.shape.baseColor = new Color(255,0,72,100); + }else{ + t.shape.baseColor = new Color(0, 255, 72,100); + } + }) .build(Shape.RenderingType.BATCH) ); @@ -218,7 +233,12 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(0, time * 5, 0); + t.setShapeWorldRotationDegrees(0, time * 5, 0); + if(t.shape.isPlayerLookingAt().hit){ + t.shape.baseColor = new Color(255,0,72,100); + }else{ + t.shape.baseColor = new Color(0, 255, 72,100); + } }) .build(Shape.RenderingType.BATCH) ); @@ -233,14 +253,18 @@ public static void init(){ .segments(32) .axis(CircleLikeShape.CircleAxis.Y) .color(randomColor()) - .seeThrough(true) + .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(time, 0, 0); + t.setShapeWorldRotationDegrees(time, 0, 0); double f = (Math.sin(time * 0.1) + 1) / 2; // 0 → 1 → 0 int seg = (int)(3 + f * 20-3); - + if(t.shape.isPlayerLookingAt().hit){ + t.shape.baseColor = new Color(0, 255, 149,100); + }else{ + t.shape.baseColor = new Color(136, 136, 136,100); + } t.setSegment(Math.max(3,seg)); }) .build(Shape.RenderingType.BATCH) @@ -273,11 +297,11 @@ public static void init(){ .segments(32) .axis(CircleLikeShape.CircleAxis.Y) .color(randomColor()) - .seeThrough(true) + .seeThrough(false) .width(3) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(time, 0, 0); + t.setShapeWorldRotationDegrees(time, 0, 0); double f = (Math.sin(time * 0.1) + 1) / 2; // 0 → 1 → 0 int seg = (int)(3 + f * 20-3); @@ -299,7 +323,7 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(time*2, time * 5, 0); + t.setShapeWorldRotationDegrees(time*2, time * 5, 0); //t.setShapeLocalRotationDegrees(0,0,time * 5); }) .build(Shape.RenderingType.BATCH) @@ -346,11 +370,11 @@ public static void init(){ .pos(new Vec3(xPos(), 0, 0)) .size(new Vec3(2,2,2)) .color(randomColor()) - .seeThrough(true) + .seeThrough(false) .construction(BoxShape.BoxConstructionType.CENTER_AND_DIMENSIONS ) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(time * 2, time * 3, time * 1.5f); + t.setShapeWorldRotationDegrees(time * 2, time * 3, time * 1.5f); }) .build(Shape.RenderingType.BATCH) ); @@ -367,7 +391,7 @@ public static void init(){ .construction(BoxShape.BoxConstructionType.CORNERS) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(0, time * 4, 0); + t.setShapeWorldRotationDegrees(0, time * 4, 0); }) .build(Shape.RenderingType.BATCH) ); @@ -381,12 +405,12 @@ public static void init(){ .color(randomColor()) // 面颜色 .edgeColor(new Color(255, 255, 255, 200)) .edgeWidth(2.0f) - .seeThrough(true) + .seeThrough(false) .lineSeeThrough(false) .construction(BoxShape.BoxConstructionType.CORNERS) .transform((t) -> { float time = client.getGameTime(); - t.setShapeMatrixRotationDegrees(time * 1.5f, time * 2.5f, time*0.5f); + t.setShapeWorldRotationDegrees(time * 1.5f, time * 2.5f, time*0.5f); }) .build(Shape.RenderingType.BATCH) ); @@ -417,7 +441,7 @@ public static void rotate(BoxLikeShape.BoxTransformer boxTransformer, Shape shap float gameTime = Minecraft.getInstance().level.getGameTime(); float rotationAngle = (gameTime % 3600) * 2f; - boxTransformer.setShapeMatrixRotationDegrees(rotationAngle, rotationAngle, rotationAngle); + boxTransformer.setShapeWorldRotationDegrees(rotationAngle, rotationAngle, rotationAngle); } public static void addEntity(Entity entity) { Player player = Minecraft.getInstance().player; diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java b/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java index a89add8..7f89c83 100644 --- a/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java +++ b/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java @@ -12,9 +12,9 @@ public class DefaultTransformer { private float delta = 0; public final Shape shape; - public final TransformLayer local = new TransformLayer(); // 模型本地 - public final TransformLayer world = new TransformLayer(); // 世界空间 - public final TransformLayer matrix = new TransformLayer(); // 实时动画 + public final TransformLayer local = new TransformLayer(); + public final TransformLayer world = new TransformLayer(); + public final TransformLayer matrix = new TransformLayer(); public DefaultTransformer(Shape s,Vec3 center) { this.shape = s; @@ -49,7 +49,7 @@ public void applyTransformations(PoseStack stack,boolean lerp) { applyLayer(stack, matrix,false, lerp); } public void applyModelTransformations(PoseStack stack,boolean lerp) { - applyLayer(stack,local,true,lerp); + applyLayer(stack,local,false,lerp); applyLayer(stack,world,false,lerp); } From 789119f67e82357259a1132029000b4b0d874ad6 Mon Sep 17 00:00:00 2001 From: hotpad100c <46704388+hotpad100c@users.noreply.github.com> Date: Sat, 15 Nov 2025 23:26:53 +0900 Subject: [PATCH 4/6] Vertex sorting --- src/main/java/mypals/ml/Helpers.java | 6 ++ .../ml/builderManager/BuilderManager.java | 2 +- .../vertexBuilders/BatchVertexBuilder.java | 13 ++- .../vertexBuilders/BufferedVertexBuilder.java | 17 +++- .../ImmediateVertexBuilder.java | 12 ++- .../mypals/ml/interfaces/MeshDataExt.java | 12 +++ .../java/mypals/ml/mixin/MeshDataMixin.java | 95 +++++++++++++++++++ .../mypals/ml/render/InformationRender.java | 4 +- src/main/java/mypals/ml/shape/Shape.java | 1 - .../resources/ryansrenderingkit.mixins.json | 6 +- 10 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 src/main/java/mypals/ml/interfaces/MeshDataExt.java create mode 100644 src/main/java/mypals/ml/mixin/MeshDataMixin.java diff --git a/src/main/java/mypals/ml/Helpers.java b/src/main/java/mypals/ml/Helpers.java index 9f09e28..bca2b50 100644 --- a/src/main/java/mypals/ml/Helpers.java +++ b/src/main/java/mypals/ml/Helpers.java @@ -1,6 +1,11 @@ package mypals.ml; import java.util.concurrent.ThreadLocalRandom; + +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexSorting; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.phys.Vec3; @@ -27,4 +32,5 @@ public static Vec3 min(Vec3 a, Vec3 b) { Math.min(a.z, b.z) ); } + } diff --git a/src/main/java/mypals/ml/builderManager/BuilderManager.java b/src/main/java/mypals/ml/builderManager/BuilderManager.java index f85708d..0ec3ef0 100644 --- a/src/main/java/mypals/ml/builderManager/BuilderManager.java +++ b/src/main/java/mypals/ml/builderManager/BuilderManager.java @@ -39,7 +39,7 @@ public BuilderGroup(Matrix4f matrix,boolean seeThrough,RenderMethod renderMethod public void drawBatch(Consumer builder, RenderMethod renderMethod){ batchVertexBuilder.beginBatch(renderMethod); builder.accept(batchVertexBuilder); - batchVertexBuilder.drawBatch(); + batchVertexBuilder.drawBatch(renderMethod); } public void drawImmediate(Shape shape, Consumer builder, RenderMethod renderMethod){ immediateShapeBuilder.draw(shape,builder,renderMethod); diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java index 0034412..4150f8a 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java @@ -2,7 +2,10 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferUploader; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.VertexFormat; +import mypals.ml.interfaces.MeshDataExt; import mypals.ml.render.RenderMethod; import mypals.ml.shape.Shape; import org.joml.Matrix4f; @@ -32,18 +35,24 @@ public void push(Consumer builder) { public void draw(Shape shape, Consumer builder, RenderMethod renderMethod) { beginBatch(renderMethod); push(builder); - drawBatch(); + drawBatch(renderMethod); } - public void drawBatch() { + public void drawBatch(RenderMethod renderMethod) { if (!isBuilding) { return; } //flushTransparent(); MeshData builtBuffer = this.getBufferBuilder().build(); if(builtBuffer!=null){ + ByteBufferBuilder builder = null; + if(renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ + builder = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); + } setUpRendererSystem(null); BufferUploader.drawWithShader(builtBuffer); + if(builder != null) builder.close(); + builtBuffer.close(); restoreRendererSystem(); } diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java index 34bd262..8282170 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java @@ -2,8 +2,11 @@ import com.mojang.blaze3d.buffers.BufferUsage; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.MeshData; import com.mojang.blaze3d.vertex.VertexBuffer; +import com.mojang.blaze3d.vertex.VertexFormat; +import mypals.ml.interfaces.MeshDataExt; import mypals.ml.render.RenderMethod; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; @@ -25,7 +28,7 @@ public void rebuild(RenderMethod renderMethod, Consumer b start(renderMethod); push(builder); //flushTransparent(); - end(); + end(renderMethod); } public void start(RenderMethod renderMethod) { @@ -44,7 +47,7 @@ public void push(Consumer builder) { } } - public void end() { + public void end(RenderMethod renderMethod) { if (!isBuilding) { return; } @@ -56,8 +59,16 @@ public void end() { isBuilding = false; return; } - + ByteBufferBuilder builder = null; + if(renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ + builder = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); + } + if(renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ + builder = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); + } this.vertexBuffer.upload(builtBuffer); + if(builder != null) builder.close(); + builtBuffer.close(); VertexBuffer.unbind(); isBuilding = false; } diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java index f8b898b..ec88010 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java @@ -2,7 +2,10 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferUploader; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.VertexFormat; +import mypals.ml.interfaces.MeshDataExt; import mypals.ml.render.RenderMethod; import mypals.ml.shape.Shape; import org.joml.Matrix4f; @@ -19,12 +22,19 @@ public void draw(Shape shape, Consumer builder, RenderMethod rend begin(renderMethod); RenderSystem.setShader(renderMethod.shader()); builder.accept(this); - //flushTransparent(); MeshData builtBuffer = this.getBufferBuilder().build(); + if(builtBuffer!=null){ + ByteBufferBuilder builder1 = null; + + if(shape.baseColor.getAlpha() < 255 && renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ + builder1 = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); + } setUpRendererSystem(shape); BufferUploader.drawWithShader(builtBuffer); + if(builder1 != null)builder1.close(); + builtBuffer.close(); restoreRendererSystem(); } } diff --git a/src/main/java/mypals/ml/interfaces/MeshDataExt.java b/src/main/java/mypals/ml/interfaces/MeshDataExt.java new file mode 100644 index 0000000..f8c6d64 --- /dev/null +++ b/src/main/java/mypals/ml/interfaces/MeshDataExt.java @@ -0,0 +1,12 @@ +package mypals.ml.interfaces; + +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.VertexSorting; + +public interface MeshDataExt { + ByteBufferBuilder ryansrenderingkit$sortTriangles(VertexSorting vertexSorting); + public interface MeshDataSortableExt { + ByteBufferBuilder.Result ryansrenderingkit$buildSortedIndexBufferTriangles(ByteBufferBuilder byteBufferBuilder, VertexSorting vertexSorting); + } +} diff --git a/src/main/java/mypals/ml/mixin/MeshDataMixin.java b/src/main/java/mypals/ml/mixin/MeshDataMixin.java new file mode 100644 index 0000000..3bf9b97 --- /dev/null +++ b/src/main/java/mypals/ml/mixin/MeshDataMixin.java @@ -0,0 +1,95 @@ +package mypals.ml.mixin; + +import com.mojang.blaze3d.vertex.*; +import mypals.ml.interfaces.MeshDataExt; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.function.IntConsumer; + +@Mixin(MeshData.class) +public abstract class MeshDataMixin implements MeshDataExt { + @Shadow @Nullable private ByteBufferBuilder.@Nullable Result indexBuffer; + + @Shadow @Final private ByteBufferBuilder.Result vertexBuffer; + + @Shadow @Final private MeshData.DrawState drawState; + + @Unique + private static Vector3f[] unpackTriangleCentroids(ByteBuffer byteBuffer, int vertexCount, VertexFormat vertexFormat) { + int posOffset = vertexFormat.getOffset(VertexFormatElement.POSITION); + if (posOffset == -1) { + throw new IllegalArgumentException("Cannot identify triangle centers with no position element"); + } + + FloatBuffer fb = byteBuffer.asFloatBuffer(); + int floatsPerVertex = vertexFormat.getVertexSize() / 4; + int triangles = vertexCount / 3; + Vector3f[] centroids = new Vector3f[triangles]; + + for (int t = 0; t < triangles; t++) { + int base = t * floatsPerVertex * 3 + posOffset; + float x0 = fb.get(base + 0); + float y0 = fb.get(base + 1); + float z0 = fb.get(base + 2); + + int b1 = base + floatsPerVertex; + float x1 = fb.get(b1 + 0); + float y1 = fb.get(b1 + 1); + float z1 = fb.get(b1 + 2); + + int b2 = base + floatsPerVertex * 2; + float x2 = fb.get(b2 + 0); + float y2 = fb.get(b2 + 1); + float z2 = fb.get(b2 + 2); + + centroids[t] = new Vector3f((x0 + x1 + x2) / 3f, (y0 + y1 + y2) / 3f, (z0 + z1 + z2) / 3f); + } + + return centroids; + } + + + @Unique + public ByteBufferBuilder ryansrenderingkit$sortTriangles(VertexSorting vertexSorting) { + int vertexCount = this.drawState.vertexCount(); + int bufferSize = vertexCount * Integer.BYTES; + ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(bufferSize); + Vector3f[] compactVectorArray = unpackTriangleCentroids(this.vertexBuffer.byteBuffer(), this.drawState.vertexCount(), this.drawState.format()); + MeshData.SortState sortState = new MeshData.SortState(compactVectorArray, this.drawState.indexType()); + this.indexBuffer = ((MeshDataSortableExt)(Object)sortState).ryansrenderingkit$buildSortedIndexBufferTriangles(byteBufferBuilder, vertexSorting); + return byteBufferBuilder; + } + @Mixin(MeshData.SortState.class) + public static abstract class SortStateMixin implements MeshDataSortableExt{ + @Shadow protected abstract it.unimi.dsi.fastutil.ints.IntConsumer indexWriter(long l, VertexFormat.IndexType indexType); + + @Shadow @Final private VertexFormat.IndexType indexType; + + @Shadow @Final private Vector3f[] centroids; + + @Unique + public ByteBufferBuilder.Result ryansrenderingkit$buildSortedIndexBufferTriangles(ByteBufferBuilder byteBufferBuilder, VertexSorting vertexSorting) { + int[] sortedTriangleIndices = vertexSorting.sort(this.centroids); + + long offset = byteBufferBuilder.reserve(sortedTriangleIndices.length * 3 * this.indexType.bytes); + IntConsumer intConsumer = this.indexWriter(offset, this.indexType); + + for (int i : sortedTriangleIndices) { + int baseVertex = i * 3; + intConsumer.accept(baseVertex + 0); + intConsumer.accept(baseVertex + 1); + intConsumer.accept(baseVertex + 2); + } + + return byteBufferBuilder.build(); + } + + } +} diff --git a/src/main/java/mypals/ml/render/InformationRender.java b/src/main/java/mypals/ml/render/InformationRender.java index 8fe65a8..23d27a9 100644 --- a/src/main/java/mypals/ml/render/InformationRender.java +++ b/src/main/java/mypals/ml/render/InformationRender.java @@ -16,7 +16,7 @@ public static void render(PoseStack matrixStack, Camera camera, float tickDelta) matrixStack.pushPose(); matrixStack.translate(-camera.getPosition().x, -camera.getPosition().y, -camera.getPosition().z); - RenderSystem.depthMask(false); + //RenderSystem.depthMask(false); RenderSystem.enableBlend(); Matrix4f pose = matrixStack.last().pose(); @@ -26,7 +26,7 @@ public static void render(PoseStack matrixStack, Camera camera, float tickDelta) ShapeManagers.renderAll(matrixStack, tickDelta); - RenderSystem.depthMask(true); + //RenderSystem.depthMask(true); RenderSystem.disableBlend(); matrixStack.popPose(); diff --git a/src/main/java/mypals/ml/shape/Shape.java b/src/main/java/mypals/ml/shape/Shape.java index 4aef250..c9d40be 100644 --- a/src/main/java/mypals/ml/shape/Shape.java +++ b/src/main/java/mypals/ml/shape/Shape.java @@ -162,7 +162,6 @@ protected void drawInternal(VertexBuilder builder) { builder.putVertex(model_vertexes.get(i)); } } - public void setBaseColor(Color color){ this.baseColor = color; } diff --git a/src/main/resources/ryansrenderingkit.mixins.json b/src/main/resources/ryansrenderingkit.mixins.json index 05d1725..72054a9 100644 --- a/src/main/resources/ryansrenderingkit.mixins.json +++ b/src/main/resources/ryansrenderingkit.mixins.json @@ -11,6 +11,8 @@ "requireAnnotations": true }, "client": [ - "ClientWorldMixin" - ] + "ClientWorldMixin", + "MeshDataMixin", + "MeshDataMixin$SortStateMixin" + ] } \ No newline at end of file From a8d1f7746c1379789cc9209e01cc3517b500b610 Mon Sep 17 00:00:00 2001 From: hotpad100c <46704388+hotpad100c@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:45:34 +0900 Subject: [PATCH 5/6] frustum culling --- build.gradle | 3 + src/main/java/mypals/ml/Helpers.java | 43 +++++++++++++ .../ml/builderManager/BuilderManager.java | 4 +- .../vertexBuilders/BatchVertexBuilder.java | 14 +++-- .../vertexBuilders/BufferedVertexBuilder.java | 16 ++--- .../ImmediateVertexBuilder.java | 27 +++++--- .../mypals/ml/interfaces/MeshDataExt.java | 2 +- .../java/mypals/ml/mixin/MeshDataMixin.java | 7 +-- src/main/java/mypals/ml/shape/Shape.java | 62 +++++++++++++++++-- .../mypals/ml/shape/line/StripLineShape.java | 2 +- .../mypals/ml/shapeManagers/ShapeManager.java | 8 +-- src/main/java/mypals/ml/test/Tester.java | 51 +++++++++++---- src/main/resources/fabric.mod.json | 1 + .../resources/ryansRenderingKit.accesswidener | 2 + 14 files changed, 190 insertions(+), 52 deletions(-) create mode 100644 src/main/resources/ryansRenderingKit.accesswidener diff --git a/build.gradle b/build.gradle index 84a7fd4..feb4b39 100644 --- a/build.gradle +++ b/build.gradle @@ -78,4 +78,7 @@ publishing { // The repositories here will be used for publishing your artifact, not for // retrieving dependencies. } +} +loom { + accessWidenerPath = file("src/main/resources/ryansRenderingKit.accesswidener") } \ No newline at end of file diff --git a/src/main/java/mypals/ml/Helpers.java b/src/main/java/mypals/ml/Helpers.java index bca2b50..cef9c2a 100644 --- a/src/main/java/mypals/ml/Helpers.java +++ b/src/main/java/mypals/ml/Helpers.java @@ -1,13 +1,19 @@ package mypals.ml; +import java.util.List; import java.util.concurrent.ThreadLocalRandom; import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.MeshData; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexSorting; +import net.minecraft.client.Camera; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import org.joml.Vector4f; import static mypals.ml.RyansRenderingKit.MOD_ID; @@ -32,5 +38,42 @@ public static Vec3 min(Vec3 a, Vec3 b) { Math.min(a.z, b.z) ); } + public static Vec3 calculateCentroid(List vertices) { + double sumX = 0, sumY = 0, sumZ = 0; + for (Vec3 v : vertices) { + sumX += v.x; + sumY += v.y; + sumZ += v.z; + } + double n = vertices.size(); + return new Vec3(sumX / n, sumY / n, sumZ / n); + } + public static Matrix4f createViewMatrix(Camera camera) { + Matrix4f view = new Matrix4f(); + + Quaternionf camRot = camera.rotation(); + Quaternionf camRotInv = new Quaternionf(camRot).conjugate(); + view.rotation(camRotInv); + + Vec3 camPos = camera.getPosition(); + view.translate(new Vector3f( + (float) -camPos.x, + (float) -camPos.y, + (float) -camPos.z + )); + return view; + } + public static boolean isVertexInFrustum(Vec3 v, Matrix4f mvp) { + Vector4f clip = new Vector4f((float)v.x, (float)v.y, (float)v.z, 1f); + clip.mul(mvp); + if (clip.w <= 0) return false; + float ndcX = clip.x / clip.w; + float ndcY = clip.y / clip.w; + float ndcZ = clip.z / clip.w; + + return ndcX >= -1 && ndcX <= 1 && + ndcY >= -1 && ndcY <= 1 && + ndcZ >= -1 && ndcZ <= 1; + } } diff --git a/src/main/java/mypals/ml/builderManager/BuilderManager.java b/src/main/java/mypals/ml/builderManager/BuilderManager.java index 0ec3ef0..82ae8af 100644 --- a/src/main/java/mypals/ml/builderManager/BuilderManager.java +++ b/src/main/java/mypals/ml/builderManager/BuilderManager.java @@ -74,7 +74,7 @@ public void rebuildVBO(Collection shapeList, boolean seeThrough){ sortedShapes.sort(SHAPE_ORDER_COMPARATOR); for(Shape shape: sortedShapes){ - shape.draw(builder,new PoseStack(),1); + shape.draw(false, builder,new PoseStack(),1); } }); else normalBuilderGroup.bufferedVertexBuilder.rebuild(renderMethod, builder->{ @@ -83,7 +83,7 @@ public void rebuildVBO(Collection shapeList, boolean seeThrough){ sortedShapes.sort(SHAPE_ORDER_COMPARATOR); for(Shape shape: sortedShapes){ - shape.draw(builder,new PoseStack(),1); + shape.draw(false, builder,new PoseStack(),1); } }); } diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java index 4150f8a..0af00b9 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/BatchVertexBuilder.java @@ -39,19 +39,25 @@ public void draw(Shape shape, Consumer builder, RenderMethod rend } public void drawBatch(RenderMethod renderMethod) { - if (!isBuilding) { + if (!isBuilding || this.getBufferBuilder().vertices == 0) { return; } //flushTransparent(); + MeshData builtBuffer = this.getBufferBuilder().build(); if(builtBuffer!=null){ - ByteBufferBuilder builder = null; + ByteBufferBuilder byteBufferBuilder = null; if(renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ - builder = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); + int vertexCount = builtBuffer.drawState().vertexCount(); + int bufferSize = vertexCount * Integer.BYTES; + byteBufferBuilder = new ByteBufferBuilder(bufferSize); + ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(byteBufferBuilder, RenderSystem.getProjectionType().vertexSorting()); } setUpRendererSystem(null); BufferUploader.drawWithShader(builtBuffer); - if(builder != null) builder.close(); + if(byteBufferBuilder != null){ + byteBufferBuilder.close(); + } builtBuffer.close(); restoreRendererSystem(); } diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java index 8282170..71444fc 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/BufferedVertexBuilder.java @@ -48,7 +48,7 @@ public void push(Consumer builder) { } public void end(RenderMethod renderMethod) { - if (!isBuilding) { + if (!isBuilding || this.getBufferBuilder().vertices == 0) { return; } @@ -59,15 +59,17 @@ public void end(RenderMethod renderMethod) { isBuilding = false; return; } - ByteBufferBuilder builder = null; + ByteBufferBuilder byteBufferBuilder = null; if(renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ - builder = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); - } - if(renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ - builder = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); + int vertexCount = builtBuffer.drawState().vertexCount(); + int bufferSize = vertexCount * Integer.BYTES; + byteBufferBuilder = new ByteBufferBuilder(bufferSize); + ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(byteBufferBuilder,RenderSystem.getProjectionType().vertexSorting()); } this.vertexBuffer.upload(builtBuffer); - if(builder != null) builder.close(); + if(byteBufferBuilder != null){ + byteBufferBuilder.close(); + } builtBuffer.close(); VertexBuffer.unbind(); isBuilding = false; diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java index ec88010..421881a 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/ImmediateVertexBuilder.java @@ -18,23 +18,32 @@ public ImmediateVertexBuilder(Matrix4f modelViewMatrix, boolean seeThrough) { super(modelViewMatrix, seeThrough); } - public void draw(Shape shape, Consumer builder, RenderMethod renderMethod) { + public void draw(Shape shape, Consumer vertexBuilderConsumer, RenderMethod renderMethod) { begin(renderMethod); RenderSystem.setShader(renderMethod.shader()); - builder.accept(this); + vertexBuilderConsumer.accept(this); - MeshData builtBuffer = this.getBufferBuilder().build(); + if(this.getBufferBuilder().vertices == 0){ + return; + } + MeshData meshData = this.getBufferBuilder().build(); - if(builtBuffer!=null){ - ByteBufferBuilder builder1 = null; + if(meshData!=null){ + ByteBufferBuilder byteBufferBuilder = null; if(shape.baseColor.getAlpha() < 255 && renderMethod.mode() == VertexFormat.Mode.TRIANGLES){ - builder1 = ((MeshDataExt)builtBuffer).ryansrenderingkit$sortTriangles(RenderSystem.getProjectionType().vertexSorting()); + int vertexCount = meshData.drawState().vertexCount(); + int bufferSize = vertexCount * Integer.BYTES; + byteBufferBuilder = new ByteBufferBuilder(bufferSize); + + ((MeshDataExt)meshData).ryansrenderingkit$sortTriangles(byteBufferBuilder,RenderSystem.getProjectionType().vertexSorting()); } setUpRendererSystem(shape); - BufferUploader.drawWithShader(builtBuffer); - if(builder1 != null)builder1.close(); - builtBuffer.close(); + BufferUploader.drawWithShader(meshData); + if(byteBufferBuilder != null){ + byteBufferBuilder.close(); + } + meshData.close(); restoreRendererSystem(); } } diff --git a/src/main/java/mypals/ml/interfaces/MeshDataExt.java b/src/main/java/mypals/ml/interfaces/MeshDataExt.java index f8c6d64..3138c46 100644 --- a/src/main/java/mypals/ml/interfaces/MeshDataExt.java +++ b/src/main/java/mypals/ml/interfaces/MeshDataExt.java @@ -5,7 +5,7 @@ import com.mojang.blaze3d.vertex.VertexSorting; public interface MeshDataExt { - ByteBufferBuilder ryansrenderingkit$sortTriangles(VertexSorting vertexSorting); + void ryansrenderingkit$sortTriangles(ByteBufferBuilder byteBufferBuilder, VertexSorting vertexSorting); public interface MeshDataSortableExt { ByteBufferBuilder.Result ryansrenderingkit$buildSortedIndexBufferTriangles(ByteBufferBuilder byteBufferBuilder, VertexSorting vertexSorting); } diff --git a/src/main/java/mypals/ml/mixin/MeshDataMixin.java b/src/main/java/mypals/ml/mixin/MeshDataMixin.java index 3bf9b97..a9c7b81 100644 --- a/src/main/java/mypals/ml/mixin/MeshDataMixin.java +++ b/src/main/java/mypals/ml/mixin/MeshDataMixin.java @@ -57,14 +57,11 @@ private static Vector3f[] unpackTriangleCentroids(ByteBuffer byteBuffer, int ver @Unique - public ByteBufferBuilder ryansrenderingkit$sortTriangles(VertexSorting vertexSorting) { - int vertexCount = this.drawState.vertexCount(); - int bufferSize = vertexCount * Integer.BYTES; - ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(bufferSize); + public void ryansrenderingkit$sortTriangles(ByteBufferBuilder byteBufferBuilder,VertexSorting vertexSorting) { + Vector3f[] compactVectorArray = unpackTriangleCentroids(this.vertexBuffer.byteBuffer(), this.drawState.vertexCount(), this.drawState.format()); MeshData.SortState sortState = new MeshData.SortState(compactVectorArray, this.drawState.indexType()); this.indexBuffer = ((MeshDataSortableExt)(Object)sortState).ryansrenderingkit$buildSortedIndexBufferTriangles(byteBufferBuilder, vertexSorting); - return byteBufferBuilder; } @Mixin(MeshData.SortState.class) public static abstract class SortStateMixin implements MeshDataSortableExt{ diff --git a/src/main/java/mypals/ml/shape/Shape.java b/src/main/java/mypals/ml/shape/Shape.java index c9d40be..c5955e6 100644 --- a/src/main/java/mypals/ml/shape/Shape.java +++ b/src/main/java/mypals/ml/shape/Shape.java @@ -1,11 +1,14 @@ package mypals.ml.shape; +import com.mojang.blaze3d.systems.RenderSystem; import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.collision.RayModelIntersection; import mypals.ml.shapeManagers.ShapeManagers; import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; @@ -13,6 +16,7 @@ import org.joml.Quaternionf; import com.mojang.blaze3d.vertex.PoseStack; import org.joml.Vector3f; +import org.joml.Vector4f; import java.awt.*; import java.util.ArrayList; @@ -21,6 +25,8 @@ import java.util.Map; import java.util.function.Consumer; +import static mypals.ml.Helpers.*; + public abstract class Shape { public enum RenderingType { IMMEDIATE, BATCH, BUFFERED } @@ -82,7 +88,7 @@ public void setParent(Shape parent){ protected abstract void generateRawGeometry(boolean lerp); - public List getRealModel() { + public List getModel(boolean applyMatrixTransformer) { if (this.transformer.asyncModelInfo()) { model_vertexes.clear(); generateRawGeometry(false); @@ -99,7 +105,11 @@ public List getRealModel() { } for (int i = hierarchy.size() - 1; i >= 0; i--) { Shape n = hierarchy.get(i); - n.transformer.applyModelTransformations(poseStack, true); + if(applyMatrixTransformer){ + n.transformer.applyTransformations(poseStack, true); + }else{ + n.transformer.applyModelTransformations(poseStack, true); + } } Matrix4f matrix = poseStack.last().pose(); @@ -143,19 +153,59 @@ public RayModelIntersection.HitResult isPlayerLookingAt(){ return RayModelIntersection.rayIntersectsModel( r, - this.getRealModel(), + this.getModel(false), this.indexBuffer ); } - public void draw(VertexBuilder builder, PoseStack matrixStack, float deltaTime) { + public void draw(boolean frustumCull, VertexBuilder builder, PoseStack matrixStack, float deltaTime) { if(!visible) return; matrixStack.pushPose(); beforeDraw(matrixStack, deltaTime); builder.setPositionMatrix(matrixStack.last().pose()); - drawInternal(builder); + if(!frustumCull || shouldDraw()){ + drawInternal(builder); + } matrixStack.popPose(); } + public boolean shouldDraw() { + List vertices = this.getModel(true); + if (vertices.isEmpty()) return false; + + Minecraft client = Minecraft.getInstance(); + Camera camera = client.gameRenderer.getMainCamera(); + GameRenderer gameRenderer = client.gameRenderer; + + Vec3 center = this.transformer.getWorldPivot().add(this.transformer.getLocalPivot()); + + Matrix4f viewMatrix = createViewMatrix(camera); + + float fov = client.options.fov().get().floatValue(); + Matrix4f projectionMatrix = gameRenderer.getProjectionMatrix(fov); + + Matrix4f mvp = new Matrix4f(projectionMatrix); + mvp.mul(viewMatrix); + + if (isVertexInFrustum(center, mvp)) return true; + for (Vec3 v : vertices) { + if (isVertexInFrustum(v, mvp)) return true; + } + return false; + } + + public static boolean isVertexInFrustum(Vec3 v, Matrix4f mvp) { + Vector4f clip = new Vector4f((float)v.x, (float)v.y, (float)v.z, 1f); + clip.mul(mvp); + if (clip.w <= 0) return false; // 在 near plane 之前 + + float ndcX = clip.x / clip.w; + float ndcY = clip.y / clip.w; + float ndcZ = clip.z / clip.w; + + return ndcX >= -1 && ndcX <= 1 + && ndcY >= -1 && ndcY <= 1 + && ndcZ >= -1 && ndcZ <= 1; // 关键修复:Z 是 [-1,1] + } protected void drawInternal(VertexBuilder builder) { builder.putColor(baseColor); for (int i : indexBuffer) { @@ -182,7 +232,7 @@ public void putCustomData(String key, T value) { @SuppressWarnings("unchecked") public T getCustomData(String key, T def) { - return (T) customData.getOrDefault(key, null); + return (T) customData.getOrDefault(key, def); } @SuppressWarnings("unchecked") public void removeCustomData(String key) { diff --git a/src/main/java/mypals/ml/shape/line/StripLineShape.java b/src/main/java/mypals/ml/shape/line/StripLineShape.java index 0ce832c..1bd5437 100644 --- a/src/main/java/mypals/ml/shape/line/StripLineShape.java +++ b/src/main/java/mypals/ml/shape/line/StripLineShape.java @@ -56,7 +56,7 @@ protected void generateRawGeometry(boolean lerp) { model_vertexes.clear(); if (vertexes.size() < 2) return; Vec3 localCenter = calculateShapeCenterPos(); - transformer.setShapeLocalPivot(localCenter); + transformer.setShapeWorldPivot(localCenter); for (Vec3 v : vertexes) { model_vertexes.add(v.subtract(localCenter)); } diff --git a/src/main/java/mypals/ml/shapeManagers/ShapeManager.java b/src/main/java/mypals/ml/shapeManagers/ShapeManager.java index 3c2bd8b..345da74 100644 --- a/src/main/java/mypals/ml/shapeManagers/ShapeManager.java +++ b/src/main/java/mypals/ml/shapeManagers/ShapeManager.java @@ -115,7 +115,7 @@ public void drawImmediate(BuilderManager builderManager, PoseStack matrixStack,f for (Shape shape : sortedShapes) { builderManager.drawImmediate(shape, builder -> { - shape.draw(builder, matrixStack,tickDelta); + shape.draw(true, builder, matrixStack,tickDelta); }); } } @@ -125,7 +125,7 @@ public void drawImmediate(BuilderManager builderManager, PoseStack matrixStack,f for (Shape shape : sortedShapes) { builderManager.drawImmediate(shape, builder -> { - shape.draw(builder, matrixStack,tickDelta); + shape.draw(true, builder, matrixStack,tickDelta); }); } } @@ -137,7 +137,7 @@ public void drawBatched(BuilderManager builderManager, PoseStack matrixStack,flo sortedShapes.sort(SHAPE_ORDER_COMPARATOR); for (Shape shape : sortedShapes) { - shape.draw(builder, matrixStack,tickDelta); + shape.draw(true,builder, matrixStack,tickDelta); } }, false); } @@ -147,7 +147,7 @@ public void drawBatched(BuilderManager builderManager, PoseStack matrixStack,flo sortedShapes.sort(SHAPE_ORDER_COMPARATOR); for (Shape shape : sortedShapes) { - shape.draw(builder, matrixStack,tickDelta); + shape.draw(true,builder, matrixStack,tickDelta); } }, true); } diff --git a/src/main/java/mypals/ml/test/Tester.java b/src/main/java/mypals/ml/test/Tester.java index 9f5b3a1..e356a50 100644 --- a/src/main/java/mypals/ml/test/Tester.java +++ b/src/main/java/mypals/ml/test/Tester.java @@ -16,6 +16,7 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; +import net.minecraft.client.MouseHandler; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityDimensions; @@ -203,21 +204,44 @@ public static void init(){ ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_obj_model"), ShapeGenerator.generateObjModel() - .pos(new Vec3(xPos(), 0,0)) + .pos(new Vec3(xPos(), 0, 0)) .model(ResourceLocation.fromNamespaceAndPath(MOD_ID, "models/monkey.obj")) .color(randomColor()) .seeThrough(false) - .transform((t) -> { + .transform((transformer) -> { float time = client.getGameTime(); - - t.setShapeWorldRotationDegrees(0, time * 5, 0); - - if(t.shape.isPlayerLookingAt().hit){ - t.shape.baseColor = new Color(255,0,72,100); - }else{ - t.shape.baseColor = new Color(0, 255, 72,100); - } - + transformer.setShapeWorldRotationDegrees(0, time * 5, 0); + Shape shape = transformer.shape; + if (shape.isPlayerLookingAt().hit/* 如果玩家正在看着这个 shape*/) { + if (Minecraft.getInstance().mouseHandler.isLeftPressed()) { + Player player = Minecraft.getInstance().player; + // 第一次抓取时,记录形状与玩家视线的距离 + if (!shape.getCustomData("isHolding", false)) { + shape.putCustomData("isHolding", true); + double distance = transformer + .getShapeWorldPivot(true) + .distanceTo(player.getEyePosition()); + shape.putCustomData("distance", distance); + } + // 将模型移动到玩家正前方 + transformer.setShapeWorldPivot( + player.getEyePosition(transformer.getTickDelta()) + .add(player.getLookAngle().scale(shape.getCustomData("distance", 5.0))) + .add(0, -1, 0) + ); + transformer.world.position.syncLastToTarget(); + } else { + // 松开鼠标 → 结束抓取 + if (shape.getCustomData("isHolding", false)) + shape.putCustomData("isHolding", false); + } + + shape.baseColor = new Color(255, 0, 72, 100); + } else { + if (shape.getCustomData("isHolding", false)) + shape.putCustomData("isHolding", false); + shape.baseColor = new Color(0, 255, 72, 100); + } }) .build(Shape.RenderingType.BATCH) ); @@ -348,6 +372,7 @@ public static void init(){ ); // 9. StripLine(螺旋线) + float cx = xPos(); ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_strip_line"), ShapeGenerator.generateStripLine() @@ -358,7 +383,7 @@ public static void init(){ .transform((t) -> { float time = client.getGameTime(); // 动态更新顶点(旋转螺旋) - ((StripLineShape)t.getShape()).setVertexes(generateSpiral(xPos(), 100, 2.0f, 5.0f, time * 0.05f)); + ((StripLineShape)t.getShape()).setVertexes(generateSpiral(cx, 100, 2.0f, 5.0f, time * 0.05f)); }) .build(Shape.RenderingType.BATCH) ); @@ -402,7 +427,7 @@ public static void init(){ ShapeGenerator.generateWireframedBox() .aabb(new Vec3(xPos() - 2, 0, -2), new Vec3(xPos() + 2, 4, 2)) - .color(randomColor()) // 面颜色 + .color(randomColor()) .edgeColor(new Color(255, 255, 255, 200)) .edgeWidth(2.0f) .seeThrough(false) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 43030c3..f9359b5 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -4,6 +4,7 @@ "version": "${version}", "name": "RyansRenderingKit", "description": "This is an example description! Tell everyone what your mod is about!", + "access_widener": "ryansRenderingKit.accesswidener", "authors": [ "Me!" ], diff --git a/src/main/resources/ryansRenderingKit.accesswidener b/src/main/resources/ryansRenderingKit.accesswidener new file mode 100644 index 0000000..68105db --- /dev/null +++ b/src/main/resources/ryansRenderingKit.accesswidener @@ -0,0 +1,2 @@ +accessWidener v2 named +accessible field com/mojang/blaze3d/vertex/BufferBuilder vertices I \ No newline at end of file From 77af0558f992aded302966526dd7562274f986ef Mon Sep 17 00:00:00 2001 From: hotpad100c <46704388+hotpad100c@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:22:15 +0900 Subject: [PATCH 6/6] Clean up, bug fix and maven setup? --- build.gradle | 9 +- pom.xml | 93 +++++++ src/main/java/mypals/ml/Helpers.java | 69 ++++- .../java/mypals/ml/RyansRenderingKit.java | 22 +- .../ml/builderManager/BuilderManagers.java | 15 +- .../builderManager/EmptyBuilderManager.java | 33 +++ .../shapeBuilders/BoxFaceBuilder.java | 2 +- .../shapeBuilders/BoxWireframeBuilder.java | 2 +- .../builders/shapeBuilders/ConeBuilder.java | 2 +- .../shapeBuilders/ConeWireframeBuilder.java | 2 +- .../shapeBuilders/CylinderBuilder.java | 2 +- .../CylinderWireframeBuilder.java | 2 +- .../shapeBuilders/FaceCircleBuilder.java | 2 +- .../builders/shapeBuilders/LineBuilder.java | 2 +- .../shapeBuilders/LineCircleBuilder.java | 2 +- .../shapeBuilders/ObjModelBuilder.java | 2 +- .../shapeBuilders/ObjModelOutlineBuilder.java | 2 +- .../shapeBuilders/ShapeGenerator.java | 3 + .../builders/shapeBuilders/SphereBuilder.java | 2 +- .../shapeBuilders/StripLineBuilder.java | 2 +- .../builders/shapeBuilders/TextBuilder.java | 100 +++++++ .../shapeBuilders/WireframedBoxBuilder.java | 2 +- .../vertexBuilders/EmptyVertexBuilder.java | 13 + .../vertexBuilders/VertexBuilder.java | 5 - .../mypals/ml/interfaces/MeshDataExt.java | 2 +- .../java/mypals/ml/mixin/KeyBoardMixin.java | 4 + .../java/mypals/ml/mixin/MeshDataMixin.java | 10 +- .../mypals/ml/render/InformationRender.java | 5 +- .../java/mypals/ml/render/RenderMethod.java | 25 -- src/main/java/mypals/ml/shape/Shape.java | 96 ++++++- .../mypals/ml/shape/basics/BoxLikeShape.java | 20 +- .../ml/shape/basics/core/LineLikeShape.java | 41 +++ .../shape/basics/core/TwoPointsLineShape.java | 6 +- .../ml/shape/basics/tags/EmptyMesh.java | 4 + .../java/mypals/ml/shape/box/BoxShape.java | 25 +- .../ml/shape/box/BoxWireframeShape.java | 56 +++- .../ml/shape/cylinder/CylinderShape.java | 17 ++ .../cylinder/CylinderWireframeShape.java | 46 +++- .../java/mypals/ml/shape/line/LineShape.java | 62 ++++- .../mypals/ml/shape/line/StripLineShape.java | 47 +++- .../ml/shape/model/ObjModelShapeOutline.java | 39 +++ .../ml/shape/round/FaceCircleShape.java | 12 +- .../ml/shape/round/LineCircleShape.java | 58 ++++- .../java/mypals/ml/shape/text/TextShape.java | 239 +++++++++++------ .../ml/shapeManagers/EmptyShapeManager.java | 86 ++++++ .../ml/shapeManagers/ShapeManagers.java | 43 ++- .../ml/shapeManagers/VertexBuilderGetter.java | 12 +- src/main/java/mypals/ml/test/Tester.java | 244 +++++++++++++++--- .../shapeTransformers/DefaultTransformer.java | 31 +-- .../LineModelInfo.java | 2 +- 50 files changed, 1345 insertions(+), 277 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/mypals/ml/builderManager/EmptyBuilderManager.java create mode 100644 src/main/java/mypals/ml/builders/shapeBuilders/TextBuilder.java create mode 100644 src/main/java/mypals/ml/builders/vertexBuilders/EmptyVertexBuilder.java create mode 100644 src/main/java/mypals/ml/mixin/KeyBoardMixin.java create mode 100644 src/main/java/mypals/ml/shape/basics/tags/EmptyMesh.java create mode 100644 src/main/java/mypals/ml/shapeManagers/EmptyShapeManager.java diff --git a/build.gradle b/build.gradle index feb4b39..025aaff 100644 --- a/build.gradle +++ b/build.gradle @@ -17,13 +17,20 @@ repositories { // See https://docs.gradle.org/current/userguide/declaring_repositories.html // for more information about repositories. mavenCentral() + maven { + name = 'ParchmentMC' + url = 'https://maven.parchmentmc.org' + } //----for test--- } dependencies { // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings loom.officialMojangMappings() + mappings loom.layered() { + officialMojangMappings() + parchment("org.parchmentmc.data:parchment-1.20.2:2023.10.08@zip") + } modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" // Fabric API. This is technically optional, but you probably want it anyway. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5e50ffa --- /dev/null +++ b/pom.xml @@ -0,0 +1,93 @@ + + 4.0.0 + + + io.github.hotpad100c + ryansrenderingkit + 1.0.0 + Ryan's Rendering Kit + A Fabric-based rendering toolkit for Minecraft + https://github.com/hotpad100c/ryansrenderingkit + + + + 21 + 21 + UTF-8 + + + true + + + + + + github + GitHub Packages + https://maven.pkg.github.com/hotpad100c/ryansrenderingkit + + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + + jar + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.0 + + + attach-javadocs + + jar + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + + + + + + + diff --git a/src/main/java/mypals/ml/Helpers.java b/src/main/java/mypals/ml/Helpers.java index cef9c2a..e2c799f 100644 --- a/src/main/java/mypals/ml/Helpers.java +++ b/src/main/java/mypals/ml/Helpers.java @@ -1,14 +1,17 @@ package mypals.ml; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ThreadLocalRandom; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import com.mojang.blaze3d.vertex.VertexSorting; +import com.mojang.blaze3d.vertex.*; +import com.mojang.math.Axis; import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ShapeRenderer; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.entity.SignText; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Quaternionf; @@ -76,4 +79,62 @@ public static boolean isVertexInFrustum(Vec3 v, Matrix4f mvp) { ndcY >= -1 && ndcY <= 1 && ndcZ >= -1 && ndcZ <= 1; } + public static int multiplyRGB(int color, float shade) { + int alpha = color >>> 24 & 255; + int red = (int)((float)(color >>> 16 & 255) * shade); + int green = (int)((float)(color >>> 8 & 255) * shade); + int blue = (int)((float)(color & 255) * shade); + return alpha << 24 | red << 16 | green << 8 | blue; + } + public static void renderLineBox(PoseStack poseStack, VertexConsumer consumer, + Vec3 center, float size, + float red, float green, float blue, float alpha) { + + double half = size / 2.0; + + ShapeRenderer.renderLineBox( + poseStack, consumer, + center.x - half, center.y - half, center.z - half, + center.x + half, center.y + half, center.z + half, + red, green, blue, + alpha, red, green, + blue + ); + } + public static void renderBillboardFrame( + PoseStack poseStack, + VertexConsumer vc, + Vec3 vec3, + float size, + float r, float g, float b, float a + ) { + poseStack.pushPose(); + poseStack.translate(vec3); + + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + + poseStack.mulPose(Axis.YP.rotationDegrees(-camera.getYRot())); + poseStack.mulPose(Axis.XP.rotationDegrees(camera.getXRot())); + + PoseStack.Pose pose = poseStack.last(); + + float s = size / 2f; + + Vec3 v1 = new Vec3(-s, -s, 0); + Vec3 v2 = new Vec3(s, -s, 0); + Vec3 v3 = new Vec3(s, s, 0); + Vec3 v4 = new Vec3(-s, s, 0); + + Vec3 n = new Vec3(0, 0, -1); + + addLine(pose, vc, v1, v2, r, g, b, a, n); + addLine(pose, vc, v2, v3, r, g, b, a, n); + addLine(pose, vc, v3, v4, r, g, b, a, n); + addLine(pose, vc, v4, v1, r, g, b, a, n); + poseStack.popPose(); + } + private static void addLine(PoseStack.Pose pose, VertexConsumer vc, Vec3 a, Vec3 b, float r, float g, float b2, float a2, Vec3 normal) { + vc.addVertex(pose, (float)a.x, (float)a.y, (float)a.z).setColor(r, g, b2, a2).setNormal(pose, (float)normal.x, (float)normal.y, (float)normal.z); + vc.addVertex(pose, (float)b.x, (float)b.y, (float)b.z).setColor(r, g, b2, a2).setNormal(pose, (float)normal.x, (float)normal.y, (float)normal.z); + } } diff --git a/src/main/java/mypals/ml/RyansRenderingKit.java b/src/main/java/mypals/ml/RyansRenderingKit.java index a84c7bd..4eadfa3 100644 --- a/src/main/java/mypals/ml/RyansRenderingKit.java +++ b/src/main/java/mypals/ml/RyansRenderingKit.java @@ -7,43 +7,33 @@ import mypals.ml.test.Tester; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static mypals.ml.test.Tester.registerDebugCommands; + public class RyansRenderingKit implements ModInitializer { public static final String MOD_ID = "ryansrenderingkit"; - // This logger is used to write text to the console and the log file. - // It is considered best practice to use your mod id as the logger's name. - // That way, it's clear which mod wrote info, warnings, and errors. public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); @Override public void onInitialize() { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. BuilderManagers.init(); ShapeManagers.init(); VertexBuilderGetter.init(); - WorldRenderEvents.AFTER_TRANSLUCENT.register(this::handleRenderFabulous); WorldRenderEvents.LAST.register(this::handleRenderLast); ClientTickEvents.END_WORLD_TICK.register(c-> ShapeManagers.syncShapeTransform()); - + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + registerDebugCommands(dispatcher); + }); Tester.init(); - - LOGGER.info("Hello Fabric world!"); } - private void handleRenderFabulous(WorldRenderContext ctx) { - //if (!ctx.advancedTranslucency()) return; - //InformationRender.render(ctx.matrixStack(), ctx.camera(),ctx.tickCounter().getGameTimeDeltaPartialTick(true)); - } - private void handleRenderLast(WorldRenderContext ctx) { - //if (ctx.advancedTranslucency()) return; InformationRender.render(ctx.matrixStack(), ctx.camera(),ctx.tickCounter().getGameTimeDeltaPartialTick(true)); } } \ No newline at end of file diff --git a/src/main/java/mypals/ml/builderManager/BuilderManagers.java b/src/main/java/mypals/ml/builderManager/BuilderManagers.java index bb8042f..6f2fdfb 100644 --- a/src/main/java/mypals/ml/builderManager/BuilderManagers.java +++ b/src/main/java/mypals/ml/builderManager/BuilderManagers.java @@ -1,5 +1,4 @@ package mypals.ml.builderManager; - import mypals.ml.render.RenderMethod; import org.joml.Matrix4f; @@ -10,23 +9,33 @@ public class BuilderManagers { public static BuilderManager LINES_BUILDER_MANAGER = null; public static BuilderManager LINE_STRIP_BUILDER_MANAGER = null; public static BuilderManager TRIANGLES_BUILDER_MANAGER = null; - public static BuilderManager TEXT = null; + public static EmptyBuilderManager NON_SHAPE_OBJECTS = null; public static List builders = new ArrayList<>(); + public static List emptyBuilders = new ArrayList<>(); public static void init() { Matrix4f matrix4f = new Matrix4f(); LINES_BUILDER_MANAGER = register(matrix4f, RenderMethod.LINES,"lines_builder_manager"); LINE_STRIP_BUILDER_MANAGER = register(matrix4f,RenderMethod.LINE_STRIP,"line_strip_builder_manager"); TRIANGLES_BUILDER_MANAGER = register(matrix4f,RenderMethod.TRIANGLES,"triangles_builder_manager"); - TEXT = register(matrix4f,RenderMethod.TEXT,"text_manager"); + NON_SHAPE_OBJECTS = registerEmpty(matrix4f,"empty"); } public static void updateMatrix(Matrix4f matrix4f){ for(BuilderManager builderManager : builders){ builderManager.updateMatrix(matrix4f); } + for(EmptyBuilderManager builderManager : emptyBuilders){ + builderManager.updateMatrix(matrix4f); + } } + public static BuilderManager register(Matrix4f matrix4f, RenderMethod renderMethod,String id){ BuilderManager builderManager = new BuilderManager(matrix4f,renderMethod,id); builders.add(builderManager); return builderManager; } + public static EmptyBuilderManager registerEmpty(Matrix4f matrix4f, String id){ + EmptyBuilderManager builderManager = new EmptyBuilderManager(matrix4f,id); + emptyBuilders.add(builderManager); + return builderManager; + } } diff --git a/src/main/java/mypals/ml/builderManager/EmptyBuilderManager.java b/src/main/java/mypals/ml/builderManager/EmptyBuilderManager.java new file mode 100644 index 0000000..94852e3 --- /dev/null +++ b/src/main/java/mypals/ml/builderManager/EmptyBuilderManager.java @@ -0,0 +1,33 @@ +package mypals.ml.builderManager; + +import com.mojang.blaze3d.vertex.PoseStack; +import mypals.ml.builders.vertexBuilders.*; +import mypals.ml.render.RenderMethod; +import mypals.ml.shape.Shape; +import net.minecraft.client.Camera; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + +import static mypals.ml.shapeManagers.ShapeManager.SHAPE_ORDER_COMPARATOR; + +public class EmptyBuilderManager{ + public String id; + public EmptyVertexBuilder emptyVertexBuilder;; + public EmptyBuilderManager(Matrix4f matrix4f, String id){ + this.id = id; + emptyVertexBuilder = new EmptyVertexBuilder(matrix4f); + } + + public void draw(Consumer builder){ + emptyVertexBuilder.draw(builder); + } + public void updateMatrix(Matrix4f modelViewMatrix){ + emptyVertexBuilder.setPositionMatrix(modelViewMatrix); + } +} + diff --git a/src/main/java/mypals/ml/builders/shapeBuilders/BoxFaceBuilder.java b/src/main/java/mypals/ml/builders/shapeBuilders/BoxFaceBuilder.java index 800198e..09baf69 100644 --- a/src/main/java/mypals/ml/builders/shapeBuilders/BoxFaceBuilder.java +++ b/src/main/java/mypals/ml/builders/shapeBuilders/BoxFaceBuilder.java @@ -21,7 +21,7 @@ public class BoxFaceBuilder extends BaseBuilder { + + private Shape.RenderingType type = Shape.RenderingType.IMMEDIATE; + private List texts = new ArrayList<>(); + private List textColors = new ArrayList<>(); + private TextShape.BillBoardMode billBoardMode = TextShape.BillBoardMode.FIXED; + private boolean shadow = true; + private boolean outline = false; + + + public TextBuilder type(Shape.RenderingType type) { + this.type = type; + return this; + } + + public TextBuilder texts(List texts) { + this.texts = new ArrayList<>(texts); + return this; + } + + public TextBuilder texts(String... texts) { + this.texts = Arrays.asList(texts); + return this; + } + + public TextBuilder addText(String text) { + this.texts.add(text); + return this; + } + + public TextBuilder addText(String text, Color color) { + this.texts.add(text); + this.textColors.add(color); + return this; + } + + public TextBuilder textColors(List colors) { + this.textColors = new ArrayList<>(colors); + return this; + } + + public TextBuilder textColors(Color... colors) { + this.textColors = Arrays.asList(colors); + return this; + } + + public TextBuilder billBoardMode(TextShape.BillBoardMode mode) { + this.billBoardMode = mode != null ? mode : TextShape.BillBoardMode.FIXED; + return this; + } + + public TextBuilder shadow(boolean shadow) { + this.shadow = shadow; + return this; + } + + public TextBuilder outline(boolean outline) { + this.outline = outline; + return this; + } + + @Override + public TextShape build(Shape.RenderingType type) { + this.type = type; + if (textColors.isEmpty() || textColors.size() < texts.size()) { + List colors = new ArrayList<>(texts.size()); + colors.addAll(textColors); + while (colors.size() < texts.size()) { + colors.add(Color.WHITE); + } + textColors = colors; + } + + return new TextShape( + this.type, + getTransformer(), + center, + Collections.unmodifiableList(texts), + Collections.unmodifiableList(textColors), + billBoardMode, + seeThrough, + shadow, + outline + ); + } + +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/builders/shapeBuilders/WireframedBoxBuilder.java b/src/main/java/mypals/ml/builders/shapeBuilders/WireframedBoxBuilder.java index 0ff73fc..3934a4b 100644 --- a/src/main/java/mypals/ml/builders/shapeBuilders/WireframedBoxBuilder.java +++ b/src/main/java/mypals/ml/builders/shapeBuilders/WireframedBoxBuilder.java @@ -31,7 +31,7 @@ public class WireframedBoxBuilder extends BaseBuilder vertexBuilderConsumer) { + vertexBuilderConsumer.accept(this); + } +} \ No newline at end of file diff --git a/src/main/java/mypals/ml/builders/vertexBuilders/VertexBuilder.java b/src/main/java/mypals/ml/builders/vertexBuilders/VertexBuilder.java index 6240d45..dbea40e 100644 --- a/src/main/java/mypals/ml/builders/vertexBuilders/VertexBuilder.java +++ b/src/main/java/mypals/ml/builders/vertexBuilders/VertexBuilder.java @@ -64,11 +64,6 @@ public float[] toARGB(Color color) { colors[3] = (argb & 0xFF) / 255f; return colors; } - - protected boolean shouldApplyNormal(RenderMethod renderMethod) { - return renderMethod == RenderMethod.LINES || renderMethod == RenderMethod.LINE_STRIP; - } - public void putVertex(Vector3f v, float r, float g, float b, float a) { this.bufferBuilder.addVertex(positionMatrix, v.x, v.y, v.z).setColor(r, g, b, a); } diff --git a/src/main/java/mypals/ml/interfaces/MeshDataExt.java b/src/main/java/mypals/ml/interfaces/MeshDataExt.java index 3138c46..98cb0f6 100644 --- a/src/main/java/mypals/ml/interfaces/MeshDataExt.java +++ b/src/main/java/mypals/ml/interfaces/MeshDataExt.java @@ -6,7 +6,7 @@ public interface MeshDataExt { void ryansrenderingkit$sortTriangles(ByteBufferBuilder byteBufferBuilder, VertexSorting vertexSorting); - public interface MeshDataSortableExt { + interface MeshDataSortableExt { ByteBufferBuilder.Result ryansrenderingkit$buildSortedIndexBufferTriangles(ByteBufferBuilder byteBufferBuilder, VertexSorting vertexSorting); } } diff --git a/src/main/java/mypals/ml/mixin/KeyBoardMixin.java b/src/main/java/mypals/ml/mixin/KeyBoardMixin.java new file mode 100644 index 0000000..142a0c1 --- /dev/null +++ b/src/main/java/mypals/ml/mixin/KeyBoardMixin.java @@ -0,0 +1,4 @@ +package mypals.ml.mixin; + +public class KeyBoardMixin { +} diff --git a/src/main/java/mypals/ml/mixin/MeshDataMixin.java b/src/main/java/mypals/ml/mixin/MeshDataMixin.java index a9c7b81..530200f 100644 --- a/src/main/java/mypals/ml/mixin/MeshDataMixin.java +++ b/src/main/java/mypals/ml/mixin/MeshDataMixin.java @@ -15,7 +15,7 @@ @Mixin(MeshData.class) public abstract class MeshDataMixin implements MeshDataExt { - @Shadow @Nullable private ByteBufferBuilder.@Nullable Result indexBuffer; + @Shadow @Nullable private ByteBufferBuilder.Result indexBuffer; @Shadow @Final private ByteBufferBuilder.Result vertexBuffer; @@ -35,17 +35,17 @@ private static Vector3f[] unpackTriangleCentroids(ByteBuffer byteBuffer, int ver for (int t = 0; t < triangles; t++) { int base = t * floatsPerVertex * 3 + posOffset; - float x0 = fb.get(base + 0); + float x0 = fb.get(base); float y0 = fb.get(base + 1); float z0 = fb.get(base + 2); int b1 = base + floatsPerVertex; - float x1 = fb.get(b1 + 0); + float x1 = fb.get(b1); float y1 = fb.get(b1 + 1); float z1 = fb.get(b1 + 2); int b2 = base + floatsPerVertex * 2; - float x2 = fb.get(b2 + 0); + float x2 = fb.get(b2); float y2 = fb.get(b2 + 1); float z2 = fb.get(b2 + 2); @@ -80,7 +80,7 @@ public static abstract class SortStateMixin implements MeshDataSortableExt{ for (int i : sortedTriangleIndices) { int baseVertex = i * 3; - intConsumer.accept(baseVertex + 0); + intConsumer.accept(baseVertex); intConsumer.accept(baseVertex + 1); intConsumer.accept(baseVertex + 2); } diff --git a/src/main/java/mypals/ml/render/InformationRender.java b/src/main/java/mypals/ml/render/InformationRender.java index 23d27a9..73135a1 100644 --- a/src/main/java/mypals/ml/render/InformationRender.java +++ b/src/main/java/mypals/ml/render/InformationRender.java @@ -16,17 +16,16 @@ public static void render(PoseStack matrixStack, Camera camera, float tickDelta) matrixStack.pushPose(); matrixStack.translate(-camera.getPosition().x, -camera.getPosition().y, -camera.getPosition().z); - //RenderSystem.depthMask(false); RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); Matrix4f pose = matrixStack.last().pose(); BuilderManagers.updateMatrix(pose); - Tester.renderTick(matrixStack);//------ + Tester.renderTick(matrixStack); ShapeManagers.renderAll(matrixStack, tickDelta); - //RenderSystem.depthMask(true); RenderSystem.disableBlend(); matrixStack.popPose(); diff --git a/src/main/java/mypals/ml/render/RenderMethod.java b/src/main/java/mypals/ml/render/RenderMethod.java index 9b80a9d..e846755 100644 --- a/src/main/java/mypals/ml/render/RenderMethod.java +++ b/src/main/java/mypals/ml/render/RenderMethod.java @@ -13,13 +13,6 @@ public record RenderMethod( @NotNull VertexFormat format, boolean cullFace ) { - public static final RenderMethod QUADS = new RenderMethod( - CoreShaders.POSITION_COLOR, - VertexFormat.Mode.QUADS, - DefaultVertexFormat.POSITION_COLOR, - true - ); - public static final RenderMethod LINES = new RenderMethod( CoreShaders.RENDERTYPE_LINES, VertexFormat.Mode.LINES, @@ -40,22 +33,4 @@ public record RenderMethod( DefaultVertexFormat.POSITION_COLOR, true ); - public static final RenderMethod TRIANGLES_STRIP = new RenderMethod( - CoreShaders.POSITION_COLOR, - VertexFormat.Mode.TRIANGLE_STRIP, - DefaultVertexFormat.POSITION_COLOR, - true - ); - public static final RenderMethod TRIANGLES_FAN = new RenderMethod( - CoreShaders.POSITION_COLOR, - VertexFormat.Mode.TRIANGLE_FAN, - DefaultVertexFormat.POSITION_COLOR, - true - ); - public static final RenderMethod TEXT = new RenderMethod( - CoreShaders.RENDERTYPE_TEXT, - VertexFormat.Mode.QUADS, - DefaultVertexFormat.POSITION_TEX_LIGHTMAP_COLOR, - true - ); } diff --git a/src/main/java/mypals/ml/shape/Shape.java b/src/main/java/mypals/ml/shape/Shape.java index c5955e6..9d362fc 100644 --- a/src/main/java/mypals/ml/shape/Shape.java +++ b/src/main/java/mypals/ml/shape/Shape.java @@ -1,19 +1,19 @@ package mypals.ml.shape; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexConsumer; import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.collision.RayModelIntersection; import mypals.ml.shapeManagers.ShapeManagers; import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; -import net.minecraft.client.Options; import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; -import org.joml.Quaternionf; import com.mojang.blaze3d.vertex.PoseStack; import org.joml.Vector3f; import org.joml.Vector4f; @@ -26,6 +26,7 @@ import java.util.function.Consumer; import static mypals.ml.Helpers.*; +import static mypals.ml.test.Tester.ENABLE_DEBUG; public abstract class Shape { public enum RenderingType { IMMEDIATE, BATCH, BUFFERED } @@ -39,8 +40,8 @@ public enum RenderingType { IMMEDIATE, BATCH, BUFFERED } public List children = new ArrayList<>(); public boolean visible = true; - public Color baseColor = Color.WHITE; - public boolean seeThrough = false; + public Color baseColor; + public boolean seeThrough; public List model_vertexes = new ArrayList<>();//This is the original model of our model. public int[] indexBuffer = new int[0]; @@ -79,11 +80,57 @@ public void setParent(Shape parent){ public void setWorldScale(Vec3 scale) {this.transformer.setShapeWorldScale(scale);} - public void setRenderPivot(Vec3 pos) {this.transformer.matrix.position.setTargetVector(pos);} + public void setRenderPivot(Vec3 pos) {this.transformer.setShapeMatrixPivot(pos);} - public void setRenderRotation(Vector3f rot) {this.transformer.matrix.setRotationDegrees(rot.x, rot.y, rot.z);} + public void setRenderRotation(Vector3f rot) {this.transformer.setShapeMatrixRotationDegrees(rot.x, rot.y, rot.z);} + + public void setRenderScale(Vec3 scale) {this.transformer.setShapeMatrixScale(scale);} + + public void forceSetLocalPosition(Vec3 pos) { + setLocalPosition(pos); + this.transformer.local.position.syncLastToTarget(); + } + + public void forceSetLocalRotation(Vector3f rot) { + setLocalRotation(rot); + this.transformer.local.rotation.syncLastToTarget(); + } + + public void forceSetLocalScale(Vec3 scale) { + setLocalScale(scale); + this.transformer.local.scale.syncLastToTarget(); + } + + public void forceSetWorldPosition(Vec3 pos) { + setWorldPosition(pos); + this.transformer.world.position.syncLastToTarget(); + } + + public void forceSetWorldRotation(Vector3f rot) { + setWorldRotation(rot); + this.transformer.world.rotation.syncLastToTarget(); + } + + public void forceSetWorldScale(Vec3 scale) { + setWorldScale(scale); + this.transformer.world.scale.syncLastToTarget(); + } + + public void forceSetRenderPivot(Vec3 pos) { + setRenderPivot(pos); + this.transformer.matrix.position.syncLastToTarget(); + } + + public void forceSetRenderRotation(Vector3f rot) { + setRenderRotation(rot); + this.transformer.matrix.rotation.syncLastToTarget(); + } + + public void forceSetRenderScale(Vec3 scale) { + setRenderScale(scale); + this.transformer.matrix.scale.syncLastToTarget(); + } - public void setRenderScale(Vec3 scale) {this.transformer.matrix.scale.setTargetVector(scale);} protected abstract void generateRawGeometry(boolean lerp); @@ -125,6 +172,7 @@ public List getModel(boolean applyMatrixTransformer) { } public void beforeDraw(PoseStack matrixStack, float deltaTime) { + transformer.updateTickDelta(deltaTime); transformFunction.accept(transformer); if (this.transformer.asyncModelInfo()) { @@ -144,10 +192,33 @@ public void beforeDraw(PoseStack matrixStack, float deltaTime) { } //We'll apply all transformations to the matrix stack before drawing(With lerp!). } + public void drawShapeDebugInfo(PoseStack matrixStack, float deltaTime){ + VertexConsumer vertexConsumer = Minecraft.getInstance() + .renderBuffers().bufferSource().getBuffer(RenderType.LINES); + + Vec3 localCenter = this.transformer.getShapeWorldPivot(true).add(this.transformer.getShapeLocalPivot(true)); + Vec3 worldCenter = this.transformer.getShapeWorldPivot(true); + Vec3 visualCenter = this.transformer.getShapeMatrixPivot(true).add(worldCenter); + renderLineBox(matrixStack,vertexConsumer,localCenter,0.15f,1,0,0,1); + + renderLineBox(matrixStack,vertexConsumer,worldCenter,0.1f,0,1,0,1); + + renderLineBox(matrixStack,vertexConsumer,visualCenter,0.05f,0,0,1,1); + + RenderSystem.depthMask(false); + for(Vec3 v : getModel(false)){ + double distanceTo = v.distanceToSqr(Minecraft.getInstance().cameraEntity.position()); + if(distanceTo < 50) + renderBillboardFrame(matrixStack,vertexConsumer,v, (float) (distanceTo * 0.01),1,0,1,1); + } + RenderSystem.depthMask(true); + } + public RayModelIntersection.HitResult isPlayerLookingAt(){ Minecraft minecraft = Minecraft.getInstance(); Player p = minecraft.player; + if(p == null) return new RayModelIntersection.HitResult(false,null, -1);; Camera camera = minecraft.gameRenderer.getMainCamera(); RayModelIntersection.Ray r = new RayModelIntersection.Ray(camera.getPosition(), p.getForward()); @@ -158,11 +229,16 @@ public RayModelIntersection.HitResult isPlayerLookingAt(){ ); } public void draw(boolean frustumCull, VertexBuilder builder, PoseStack matrixStack, float deltaTime) { + boolean shouldDraw = shouldDraw(); + Minecraft mc = Minecraft.getInstance(); + if(mc.getEntityRenderDispatcher().shouldRenderHitBoxes() && ENABLE_DEBUG){ + drawShapeDebugInfo(matrixStack,deltaTime); + } if(!visible) return; matrixStack.pushPose(); beforeDraw(matrixStack, deltaTime); builder.setPositionMatrix(matrixStack.last().pose()); - if(!frustumCull || shouldDraw()){ + if(!frustumCull || shouldDraw){ drawInternal(builder); } matrixStack.popPose(); @@ -196,7 +272,7 @@ public static boolean isVertexInFrustum(Vec3 v, Matrix4f mvp) { Vector4f clip = new Vector4f((float)v.x, (float)v.y, (float)v.z, 1f); clip.mul(mvp); - if (clip.w <= 0) return false; // 在 near plane 之前 + if (clip.w <= 0) return false; float ndcX = clip.x / clip.w; float ndcY = clip.y / clip.w; @@ -204,7 +280,7 @@ public static boolean isVertexInFrustum(Vec3 v, Matrix4f mvp) { return ndcX >= -1 && ndcX <= 1 && ndcY >= -1 && ndcY <= 1 - && ndcZ >= -1 && ndcZ <= 1; // 关键修复:Z 是 [-1,1] + && ndcZ >= -1 && ndcZ <= 1; } protected void drawInternal(VertexBuilder builder) { builder.putColor(baseColor); diff --git a/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java b/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java index b996fc2..698ef6e 100644 --- a/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java +++ b/src/main/java/mypals/ml/shape/basics/BoxLikeShape.java @@ -1,12 +1,8 @@ package mypals.ml.shape.basics; -import com.mojang.blaze3d.vertex.PoseStack; -import mypals.ml.shape.Shape; import mypals.ml.shape.box.BoxShape; import mypals.ml.transform.shapeTransformers.DefaultTransformer; -import mypals.ml.transform.shapeTransformers.ModelInfoLayer; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.BoxModelInfo; -import mypals.ml.transform.valueTransformers.Vec3Transformer; import net.minecraft.world.phys.Vec3; public interface BoxLikeShape { @@ -14,7 +10,7 @@ public interface BoxLikeShape { Vec3 getMax(); void setMin(Vec3 min); void setMax(Vec3 max); - void setDimensions(Vec3 dimensions); + void setDimension(Vec3 dimensions); default Vec3 getShapeCenterPos() { double centerX = (getMin().x + getMax().x) / 2.0; double centerY = (getMin().y + getMax().y) / 2.0; @@ -23,28 +19,28 @@ default Vec3 getShapeCenterPos() { } class BoxTransformer extends DefaultTransformer { - public BoxModelInfo modelInfoLayer; + public BoxModelInfo boxModelInfo; public BoxTransformer(BoxShape managedShape,Vec3 dim,Vec3 center) { super(managedShape,center); - modelInfoLayer = new BoxModelInfo(dim); + boxModelInfo = new BoxModelInfo(dim); } public void setDimension(Vec3 dimension) { - this.modelInfoLayer.setDimension(dimension); + this.boxModelInfo.setDimension(dimension); } public void syncLastToTarget(){ - this.modelInfoLayer.syncLastToTarget(); + this.boxModelInfo.syncLastToTarget(); super.syncLastToTarget(); } public Vec3 getDimension(boolean lerp){ - return modelInfoLayer.getDimension(lerp); + return boxModelInfo.getDimension(lerp); } public boolean asyncModelInfo(){ - return modelInfoLayer.async(); + return boxModelInfo.async(); } @Override public void updateTickDelta(float delta){ - this.modelInfoLayer.update(delta); + this.boxModelInfo.update(delta); super.updateTickDelta(delta); } } diff --git a/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java b/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java index 3043748..586d9c2 100644 --- a/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java +++ b/src/main/java/mypals/ml/shape/basics/core/LineLikeShape.java @@ -5,7 +5,16 @@ import mypals.ml.shape.basics.tags.DrawableLine; import mypals.ml.transform.shapeTransformers.DefaultTransformer; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; +import org.joml.Vector4f; + +import java.util.List; + +import static mypals.ml.Helpers.createViewMatrix; public interface LineLikeShape extends DrawableLine { void setLineWidth(float width); @@ -46,4 +55,36 @@ public void updateTickDelta(float delta){ super.updateTickDelta(delta); } } + + + public static boolean isSegmentInFrustum(Vec3 a, Vec3 b, Matrix4f mvp) { + Vector4f ca = new Vector4f((float)a.x, (float)a.y, (float)a.z, 1f); + Vector4f cb = new Vector4f((float)b.x, (float)b.y, (float)b.z, 1f); + + ca.mul(mvp); + cb.mul(mvp); + + if (ca.w <= 0 && cb.w <= 0) return false; + + int codeA = computeOutCode(ca); + int codeB = computeOutCode(cb); + + if ((codeA & codeB) != 0) return false; + + return true; + } + + private static int computeOutCode(Vector4f c) { + int code = 0; + float x = c.x, y = c.y, z = c.z, w = c.w; + + if (x < -w) code |= 1; + if (x > w) code |= 2; + if (y < -w) code |= 4; + if (y > w) code |= 8; + if (z < -w) code |= 16; + if (z > w) code |= 32; + + return code; + } } diff --git a/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java b/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java index 8c87323..94c7240 100644 --- a/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java +++ b/src/main/java/mypals/ml/shape/basics/core/TwoPointsLineShape.java @@ -1,11 +1,7 @@ package mypals.ml.shape.basics.core; - -import com.mojang.blaze3d.vertex.PoseStack; -import mypals.ml.shape.Shape; import mypals.ml.shape.line.LineShape; import mypals.ml.transform.shapeTransformers.DefaultTransformer; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; -import mypals.ml.transform.valueTransformers.Vec3Transformer; import net.minecraft.world.phys.Vec3; public interface TwoPointsLineShape extends LineLikeShape{ @@ -52,8 +48,8 @@ public boolean asyncModelInfo(){ } @Override public void updateTickDelta(float delta){ - this.lineModelInfo.update(delta); super.updateTickDelta(delta); + this.lineModelInfo.update(delta); } } diff --git a/src/main/java/mypals/ml/shape/basics/tags/EmptyMesh.java b/src/main/java/mypals/ml/shape/basics/tags/EmptyMesh.java new file mode 100644 index 0000000..ed9b228 --- /dev/null +++ b/src/main/java/mypals/ml/shape/basics/tags/EmptyMesh.java @@ -0,0 +1,4 @@ +package mypals.ml.shape.basics.tags; + +public interface EmptyMesh { +} diff --git a/src/main/java/mypals/ml/shape/box/BoxShape.java b/src/main/java/mypals/ml/shape/box/BoxShape.java index 809e86b..5dd0bb8 100644 --- a/src/main/java/mypals/ml/shape/box/BoxShape.java +++ b/src/main/java/mypals/ml/shape/box/BoxShape.java @@ -3,11 +3,9 @@ import mypals.ml.Helpers; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.BoxLikeShape; -import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.minecraft.world.phys.Vec3; import java.awt.*; -import java.util.function.BiConsumer; import java.util.function.Consumer; public class BoxShape extends Shape implements BoxLikeShape { @@ -82,7 +80,7 @@ public void setMax(Vec3 max) { } - public void setDimensions(Vec3 dimensions) { + public void setDimension(Vec3 dimensions) { ((BoxTransformer) transformer).setDimension( new Vec3(Math.abs(dimensions.x), Math.abs(dimensions.y), Math.abs(dimensions.z)) ); @@ -100,6 +98,27 @@ public void setCorners(Vec3 corner1, Vec3 corner2) { } + public void forceSetMax(Vec3 max) { + setMax(max); + ((BoxTransformer)this.transformer).boxModelInfo.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetMin(Vec3 min) { + setMax(min); + ((BoxTransformer)this.transformer).boxModelInfo.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetDimensions(Vec3 dim) { + setDimension(dim); + ((BoxTransformer)this.transformer).boxModelInfo.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetCorners(Vec3 c1,Vec3 c2) { + setCorners(c1,c2); + ((BoxTransformer)this.transformer).boxModelInfo.syncLastToTarget(); + generateRawGeometry(false); + } + @Override public void normalizeBounds() { diff --git a/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java b/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java index e632a78..6d0c2ff 100644 --- a/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java +++ b/src/main/java/mypals/ml/shape/box/BoxWireframeShape.java @@ -3,14 +3,23 @@ import com.mojang.blaze3d.systems.RenderSystem; import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; +import mypals.ml.shape.basics.core.LineLikeShape; import mypals.ml.shape.basics.tags.DrawableLine; +import mypals.ml.shape.cylinder.CylinderWireframeShape; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; import org.joml.Vector3f; import java.awt.*; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; +import static mypals.ml.Helpers.createViewMatrix; + public class BoxWireframeShape extends BoxShape implements DrawableLine { public final float edgeWidth; @@ -29,14 +38,47 @@ public BoxWireframeShape(RenderingType type, generateRawGeometry(false); } - public BoxWireframeShape(RenderingType type, - Consumer transform, - Vec3 vec1, - Vec3 vec2, - BoxConstructionType constructionType) { - this(type, transform, vec1, vec2, Color.WHITE, false, 1.0f, constructionType); - } + @Override + public boolean shouldDraw() { + List vertices = this.getModel(true); + if (vertices.isEmpty()) return false; + + Minecraft client = Minecraft.getInstance(); + Camera camera = client.gameRenderer.getMainCamera(); + GameRenderer gameRenderer = client.gameRenderer; + + Vec3 center = this.transformer.getWorldPivot().add(this.transformer.getLocalPivot()); + + Matrix4f viewMatrix = createViewMatrix(camera); + float fov = client.options.fov().get().floatValue(); + Matrix4f projectionMatrix = gameRenderer.getProjectionMatrix(fov); + + Matrix4f mvp = new Matrix4f(projectionMatrix); + mvp.mul(viewMatrix); + + if (isVertexInFrustum(center, mvp)) return true; + + for (Vec3 v : vertices) { + if (isVertexInFrustum(v, mvp)) return true; + } + + for (int i = 0; i < vertices.size() - 1; i++) { + Vec3 a = vertices.get(i); + Vec3 b = vertices.get(i + 1); + if (LineLikeShape.isSegmentInFrustum(a, b, mvp)) return true; + } + + return false; + } + public void forceSetLineWidth(float width) { + setLineWidth(width); + ((LineLikeShape.SimpleLineTransformer)this.transformer).lineModelInfo.widthTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void setLineWidth(float width) { + ((LineLikeShape.SimpleLineTransformer)this.transformer).setWidth(width); + } @Override protected void generateRawGeometry(boolean lerp) { model_vertexes.clear(); diff --git a/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java b/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java index d2f963e..cd641a3 100644 --- a/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java +++ b/src/main/java/mypals/ml/shape/cylinder/CylinderShape.java @@ -3,6 +3,7 @@ import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.CircleLikeShape; +import mypals.ml.shape.basics.core.LineLikeShape; import mypals.ml.shape.basics.tags.DrawableTriangle; import mypals.ml.transform.shapeTransformers.DefaultTransformer; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.CircleModelInfo; @@ -188,5 +189,21 @@ public int getSegments(boolean lerp) { } public float getHeight(boolean lerp) { return ((CylinderTransformer)this.transformer).getHeight(lerp); } + + public void forceSetRadius(float radius) { + setRadius(radius); + ((CylinderTransformer)this.transformer).circleModelInfo.radiusTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetSegments(float segments) { + setRadius(segments); + ((CylinderTransformer)this.transformer).circleModelInfo.segmentTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetHeight(float height) { + setHeight(height); + ((CylinderTransformer)this.transformer).heightTransformer.syncLastToTarget(); + generateRawGeometry(false); + } } diff --git a/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java b/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java index c4f1579..7f10a0d 100644 --- a/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java +++ b/src/main/java/mypals/ml/shape/cylinder/CylinderWireframeShape.java @@ -7,8 +7,12 @@ import mypals.ml.shape.basics.core.LineLikeShape; import mypals.ml.shape.basics.tags.DrawableLine; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; +import org.joml.Matrix4f; import java.awt.*; import java.util.ArrayList; @@ -16,6 +20,8 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; +import static mypals.ml.Helpers.createViewMatrix; + public class CylinderWireframeShape extends CylinderShape implements DrawableLine, LineLikeShape { public CylinderWireframeShape(RenderingType type, Consumer transform, @@ -61,7 +67,39 @@ protected void generateRawGeometry(boolean lerp) { this.indexBuffer = indices.stream().mapToInt(Integer::intValue).toArray(); } + @Override + public boolean shouldDraw() { + List vertices = this.getModel(true); + if (vertices.isEmpty()) return false; + + Minecraft client = Minecraft.getInstance(); + Camera camera = client.gameRenderer.getMainCamera(); + GameRenderer gameRenderer = client.gameRenderer; + + Vec3 center = this.transformer.getWorldPivot().add(this.transformer.getLocalPivot()); + + Matrix4f viewMatrix = createViewMatrix(camera); + float fov = client.options.fov().get().floatValue(); + Matrix4f projectionMatrix = gameRenderer.getProjectionMatrix(fov); + + Matrix4f mvp = new Matrix4f(projectionMatrix); + mvp.mul(viewMatrix); + + if (isVertexInFrustum(center, mvp)) return true; + + for (Vec3 v : vertices) { + if (isVertexInFrustum(v, mvp)) return true; + } + + for (int i = 0; i < vertices.size() - 1; i++) { + Vec3 a = vertices.get(i); + Vec3 b = vertices.get(i + 1); + if (LineLikeShape.isSegmentInFrustum(a, b, mvp)) return true; + } + + return false; + } @Override protected void drawInternal(VertexBuilder builder) { RenderSystem.lineWidth(getLineWidth(true)); @@ -74,11 +112,15 @@ protected void drawInternal(VertexBuilder builder) { } } + public void forceSetLineWidth(float width) { + setLineWidth(width); + ((CylinderWireframeTransformer)this.transformer).lineModelInfo.widthTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + @Override public void setLineWidth(float width) { - ((CylinderWireframeTransformer)this.transformer).setWidth(width); } - @Override public float getLineWidth(boolean lerp) { return ((CylinderWireframeTransformer)this.transformer).getWidth(lerp); diff --git a/src/main/java/mypals/ml/shape/line/LineShape.java b/src/main/java/mypals/ml/shape/line/LineShape.java index a039ed0..3d56aa4 100644 --- a/src/main/java/mypals/ml/shape/line/LineShape.java +++ b/src/main/java/mypals/ml/shape/line/LineShape.java @@ -4,15 +4,24 @@ import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.collision.RayModelIntersection; import mypals.ml.shape.Shape; +import mypals.ml.shape.basics.core.LineLikeShape; import mypals.ml.shape.basics.core.TwoPointsLineShape; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; +import org.joml.Vector4f; + import java.awt.*; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; +import static mypals.ml.Helpers.createViewMatrix; + public class LineShape extends Shape implements TwoPointsLineShape { public LineShape(RenderingType type, @@ -24,12 +33,12 @@ public LineShape(RenderingType type, super(type, color, seeThrough); this.transformer = new TwoPointsLineTransformer(this,start,end,lineWidth,Vec3.ZERO); - this.transformer.setShapeWorldPivot(calculateShapeCenterPos()); this.transformFunction = (defaultTransformer) -> transform.accept((TwoPointsLineTransformer) this.transformer); syncLastToTarget(); generateRawGeometry(false); + this.transformer.setShapeWorldPivot(calculateShapeCenterPos()); } public Vec3 calculateShapeCenterPos() { @@ -38,7 +47,22 @@ public Vec3 calculateShapeCenterPos() { double centerZ = (getStart(false).z + getEnd(false).z) / 2.0; return new Vec3(centerX, centerY, centerZ); } + public void forceSetStart(Vec3 start) { + setStart(start); + ((TwoPointsLineTransformer)this.transformer).lineModelInfo.startPointTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetEnd(Vec3 end) { + setEnd(end); + ((TwoPointsLineTransformer)this.transformer).lineModelInfo.endPointTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetLineWidth(float width) { + setLineWidth(width); + ((TwoPointsLineTransformer)this.transformer).lineModelInfo.widthTransformer.syncLastToTarget(); + generateRawGeometry(false); + } @Override public void setStart(Vec3 start) { ((TwoPointsLineTransformer)this.transformer).setStart(start); @@ -75,7 +99,6 @@ protected void generateRawGeometry(boolean lerp) { Vec3 end = getEnd(lerp); Vec3 center = calculateShapeCenterPos(); - this.transformer.setShapeWorldPivot(center); this.transformer.world.position.syncLastToTarget(); @@ -87,6 +110,41 @@ protected void generateRawGeometry(boolean lerp) { this.indexBuffer = new int[] { 0, 1 }; } + @Override + public boolean shouldDraw() { + List vertices = this.getModel(true); + if (vertices.isEmpty()) return false; + + Minecraft client = Minecraft.getInstance(); + Camera camera = client.gameRenderer.getMainCamera(); + GameRenderer gameRenderer = client.gameRenderer; + + Vec3 center = this.transformer.getWorldPivot().add(this.transformer.getLocalPivot()); + + Matrix4f viewMatrix = createViewMatrix(camera); + + float fov = client.options.fov().get().floatValue(); + Matrix4f projectionMatrix = gameRenderer.getProjectionMatrix(fov); + + Matrix4f mvp = new Matrix4f(projectionMatrix); + mvp.mul(viewMatrix); + + if (isVertexInFrustum(center, mvp)) return true; + + for (Vec3 v : vertices) { + if (isVertexInFrustum(v, mvp)) return true; + } + + for (int i = 0; i < vertices.size() - 1; i++) { + Vec3 a = vertices.get(i); + Vec3 b = vertices.get(i + 1); + if (LineLikeShape.isSegmentInFrustum(a, b, mvp)) return true; + } + + return false; + } + + @Override public RayModelIntersection.HitResult isPlayerLookingAt(){ return new RayModelIntersection.HitResult(false,null,-1); diff --git a/src/main/java/mypals/ml/shape/line/StripLineShape.java b/src/main/java/mypals/ml/shape/line/StripLineShape.java index 1bd5437..da1800f 100644 --- a/src/main/java/mypals/ml/shape/line/StripLineShape.java +++ b/src/main/java/mypals/ml/shape/line/StripLineShape.java @@ -4,14 +4,23 @@ import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.collision.RayModelIntersection; import mypals.ml.shape.Shape; +import mypals.ml.shape.basics.core.LineLikeShape; import mypals.ml.shape.basics.core.StripLineLikeShape; +import mypals.ml.shape.basics.core.TwoPointsLineShape; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; + import java.awt.*; import java.util.ArrayList; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; +import static mypals.ml.Helpers.createViewMatrix; + public class StripLineShape extends Shape implements StripLineLikeShape { private List vertexes = new ArrayList<>(); @@ -65,7 +74,39 @@ protected void generateRawGeometry(boolean lerp) { indexBuffer = new int[n]; for (int i = 0; i < n; i++) indexBuffer[i] = i; } + @Override + public boolean shouldDraw() { + List vertices = this.getModel(true); + if (vertices.isEmpty()) return false; + + Minecraft client = Minecraft.getInstance(); + Camera camera = client.gameRenderer.getMainCamera(); + GameRenderer gameRenderer = client.gameRenderer; + + Vec3 center = this.transformer.getWorldPivot().add(this.transformer.getLocalPivot()); + Matrix4f viewMatrix = createViewMatrix(camera); + + float fov = client.options.fov().get().floatValue(); + Matrix4f projectionMatrix = gameRenderer.getProjectionMatrix(fov); + + Matrix4f mvp = new Matrix4f(projectionMatrix); + mvp.mul(viewMatrix); + + if (isVertexInFrustum(center, mvp)) return true; + + for (Vec3 v : vertices) { + if (isVertexInFrustum(v, mvp)) return true; + } + + for (int i = 0; i < vertices.size() - 1; i++) { + Vec3 a = vertices.get(i); + Vec3 b = vertices.get(i + 1); + if (LineLikeShape.isSegmentInFrustum(a, b, mvp)) return true; + } + + return false; + } @Override @@ -122,7 +163,11 @@ public List getVertexes() { public void setVertexColors(List colors) { this.vertexColors = new ArrayList<>(colors); } - + public void forceSetLineWidth(float width) { + setLineWidth(width); + ((SimpleLineTransformer)this.transformer).lineModelInfo.widthTransformer.syncLastToTarget(); + generateRawGeometry(false); + } @Override public void setLineWidth(float width) { ((SimpleLineTransformer)this.transformer).setWidth(width); diff --git a/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java b/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java index 53eb515..83132e3 100644 --- a/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java +++ b/src/main/java/mypals/ml/shape/model/ObjModelShapeOutline.java @@ -4,14 +4,21 @@ import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.core.LineLikeShape; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; import java.awt.*; import java.io.IOException; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; +import static mypals.ml.Helpers.createViewMatrix; + public class ObjModelShapeOutline extends ObjModelShape implements LineLikeShape { public float lineWidth; @@ -54,7 +61,39 @@ public float getLineWidth(boolean lerp) { protected void generateRawGeometry(boolean lerp) { super.generateRawGeometry(lerp); } + @Override + public boolean shouldDraw() { + List vertices = this.getModel(true); + if (vertices.isEmpty()) return false; + + Minecraft client = Minecraft.getInstance(); + Camera camera = client.gameRenderer.getMainCamera(); + GameRenderer gameRenderer = client.gameRenderer; + + Vec3 center = this.transformer.getWorldPivot().add(this.transformer.getLocalPivot()); + + Matrix4f viewMatrix = createViewMatrix(camera); + + float fov = client.options.fov().get().floatValue(); + Matrix4f projectionMatrix = gameRenderer.getProjectionMatrix(fov); + + Matrix4f mvp = new Matrix4f(projectionMatrix); + mvp.mul(viewMatrix); + if (isVertexInFrustum(center, mvp)) return true; + + for (Vec3 v : vertices) { + if (isVertexInFrustum(v, mvp)) return true; + } + + for (int i = 0; i < vertices.size() - 1; i++) { + Vec3 a = vertices.get(i); + Vec3 b = vertices.get(i + 1); + if (LineLikeShape.isSegmentInFrustum(a, b, mvp)) return true; + } + + return false; + } @Override protected void drawInternal(VertexBuilder builder) { if (model_vertexes.isEmpty() || indexBuffer == null || indexBuffer.length < 3) diff --git a/src/main/java/mypals/ml/shape/round/FaceCircleShape.java b/src/main/java/mypals/ml/shape/round/FaceCircleShape.java index 4703a9a..dc5d0fd 100644 --- a/src/main/java/mypals/ml/shape/round/FaceCircleShape.java +++ b/src/main/java/mypals/ml/shape/round/FaceCircleShape.java @@ -1,6 +1,7 @@ package mypals.ml.shape.round; import mypals.ml.shape.Shape; import mypals.ml.shape.basics.CircleLikeShape; +import mypals.ml.shape.cylinder.CylinderShape; import mypals.ml.transform.shapeTransformers.DefaultTransformer; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.CircleModelInfo; import net.minecraft.world.phys.Vec3; @@ -81,7 +82,16 @@ public void setRadius(float radius) { public void setSegments(int segments) { ((FaceCircleTransformer)this.transformer).setSegment(segments); } - + public void forceSetRadius(float radius) { + setRadius(radius); + ((FaceCircleTransformer)this.transformer).circleModelInfo.radiusTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetSegments(float segments) { + setRadius(segments); + ((FaceCircleTransformer)this.transformer).circleModelInfo.segmentTransformer.syncLastToTarget(); + generateRawGeometry(false); + } @Override public float getRadius(boolean lerp) { return ((FaceCircleTransformer)this.transformer).getRadius(lerp); diff --git a/src/main/java/mypals/ml/shape/round/LineCircleShape.java b/src/main/java/mypals/ml/shape/round/LineCircleShape.java index 9452539..5c6794d 100644 --- a/src/main/java/mypals/ml/shape/round/LineCircleShape.java +++ b/src/main/java/mypals/ml/shape/round/LineCircleShape.java @@ -5,13 +5,22 @@ import mypals.ml.shape.Shape; import mypals.ml.shape.basics.CircleLikeShape; import mypals.ml.shape.basics.core.LineLikeShape; +import mypals.ml.shape.basics.core.TwoPointsLineShape; import mypals.ml.transform.shapeTransformers.DefaultTransformer; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.CircleModelInfo; import mypals.ml.transform.shapeTransformers.shapeModelInfoTransformer.LineModelInfo; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; + import java.awt.*; +import java.util.List; import java.util.function.Consumer; +import static mypals.ml.Helpers.createViewMatrix; + public class LineCircleShape extends Shape implements CircleLikeShape, LineLikeShape { public CircleAxis axis = CircleAxis.X; @@ -77,7 +86,21 @@ protected void generateRawGeometry(boolean lerp) { public void setRadius(float radius) { ((LineCircleTransformer) this.transformer).setRadius(radius); } - + public void forceSetRadius(float radius) { + setRadius(radius); + ((LineCircleTransformer)this.transformer).circleModelInfo.radiusTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetSegments(float segments) { + setRadius(segments); + ((LineCircleTransformer)this.transformer).circleModelInfo.segmentTransformer.syncLastToTarget(); + generateRawGeometry(false); + } + public void forceSetLineWidth(float width) { + setLineWidth(width); + ((LineCircleTransformer)this.transformer).lineModelInfo.widthTransformer.syncLastToTarget(); + generateRawGeometry(false); + } @Override public void setSegments(int segments) { ((LineCircleTransformer) this.transformer).setSegment(segments); @@ -152,6 +175,39 @@ public boolean asyncModelInfo(){ } } @Override + public boolean shouldDraw() { + List vertices = this.getModel(true); + if (vertices.isEmpty()) return false; + + Minecraft client = Minecraft.getInstance(); + Camera camera = client.gameRenderer.getMainCamera(); + GameRenderer gameRenderer = client.gameRenderer; + + Vec3 center = this.transformer.getWorldPivot().add(this.transformer.getLocalPivot()); + + Matrix4f viewMatrix = createViewMatrix(camera); + + float fov = client.options.fov().get().floatValue(); + Matrix4f projectionMatrix = gameRenderer.getProjectionMatrix(fov); + + Matrix4f mvp = new Matrix4f(projectionMatrix); + mvp.mul(viewMatrix); + + if (isVertexInFrustum(center, mvp)) return true; + + for (Vec3 v : vertices) { + if (isVertexInFrustum(v, mvp)) return true; + } + + for (int i = 0; i < vertices.size() - 1; i++) { + Vec3 a = vertices.get(i); + Vec3 b = vertices.get(i + 1); + if (LineLikeShape.isSegmentInFrustum(a, b, mvp)) return true; + } + + return false; + } + @Override protected void drawInternal(VertexBuilder builder) { RenderSystem.lineWidth(getLineWidth(true)); diff --git a/src/main/java/mypals/ml/shape/text/TextShape.java b/src/main/java/mypals/ml/shape/text/TextShape.java index f3dcefb..5b9740a 100644 --- a/src/main/java/mypals/ml/shape/text/TextShape.java +++ b/src/main/java/mypals/ml/shape/text/TextShape.java @@ -1,111 +1,194 @@ package mypals.ml.shape.text; +import com.mojang.blaze3d.systems.RenderSystem; import mypals.ml.builders.vertexBuilders.VertexBuilder; import mypals.ml.shape.Shape; +import mypals.ml.shape.basics.tags.EmptyMesh; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; +import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper; +import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.screens.inventory.AbstractSignEditScreen; +import net.minecraft.client.gui.screens.inventory.SignEditScreen; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.SignRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.world.level.block.entity.SignBlockEntity; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; -import org.joml.Vector3f; import com.mojang.blaze3d.vertex.PoseStack; + import java.awt.*; import java.util.ArrayList; -import java.util.function.BiConsumer; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +import static mypals.ml.Helpers.multiplyRGB; + +public class TextShape extends Shape implements EmptyMesh { + + public ArrayList contents = new ArrayList<>(); + public ArrayList colors = new ArrayList<>(); + public boolean shadow; + public boolean outline; + + public Color backgroundColor = new Color(0,0,0,0); + public BillBoardMode billBoardMode = BillBoardMode.ALL; + @Nullable + private FormattedCharSequence[] renderMessages; + -public class TextShape { - /*public ArrayList contents = new ArrayList<>(); - public ArrayList color = new ArrayList<>(); - public BillBoardMode bill = BillBoardMode.ALL; public enum BillBoardMode { - VERTICAL, - HORIZONTAL, - ALL + FIXED, VERTICAL, HORIZONTAL, ALL } - protected TextShape(RenderingType type, BiConsumer transform, ArrayList texts,ArrayList colors) { - super(type, transform); - this.color = colors; - this.contents = texts; - } + public TextShape(RenderingType type, + Consumer transform, + Vec3 center, List texts, List textColors, + Color backgroundColor, + BillBoardMode mode, boolean seeThrough, + boolean shadow,boolean outline) { + super(type, transform, Color.white, center, seeThrough); + this.seeThrough = seeThrough; + this.contents.addAll(texts); + this.colors.addAll(textColors); + this.billBoardMode = mode; + this.shadow = shadow; + this.outline = outline; + this.backgroundColor = backgroundColor; - protected TextShape(RenderingType type) { - super(type); - } + this.transformer.setShapeWorldPivot(center); - protected TextShape(RenderingType type, boolean seeThrough) { - super(type, seeThrough); + syncLastToTarget(); } + public TextShape(RenderingType type, + Consumer transform, + Vec3 center, List texts, List textColors, + BillBoardMode mode, boolean seeThrough,boolean shadow,boolean outline) { - public TextShape(RenderingType type, BiConsumer transform, boolean seeThrough) { - super(type, transform, seeThrough); - } - public TextShape(RenderingType type, BiConsumer transform, BillBoardMode bill, Vec3 center,ArrayList texts,ArrayList colors, boolean seeThrough) { - super(type,seeThrough); - this.transformer = new DefaultTransformer(this); - this.bill = bill; - this.color = colors; - this.contents = texts; - this.transformFunction = (defaultTransformer,shape)->transform.accept(this.transformer, shape); - this.centerPoint = center; - this.transformer.setShapeCenterPos(this.calculateShapeCenterPos()); - syncLastToTarget(); + this(type,transform,center,texts,textColors,new Color(0,0,0,0),mode,seeThrough,shadow,outline); } @Override - public void beforeDraw(PoseStack matrixStack, float deltaTime) { - applyBillboard(matrixStack,this.bill); - super.beforeDraw(matrixStack,deltaTime); + protected void generateRawGeometry(boolean lerp) { + model_vertexes.clear(); } - public Vec3 calculateShapeCenterPos(){ - return centerPoint; + public FormattedCharSequence[] getRenderMessages() { + if (this.renderMessages == null) { + Minecraft mc = Minecraft.getInstance(); + Font font = mc.font; + this.renderMessages = new FormattedCharSequence[contents.size()]; + MutableComponent[] components = this.getMessages(); + for (int i = 0; i < components.length; i++) { + this.renderMessages[i] = font.split(components[i],Integer.MAX_VALUE).getFirst(); + } + } + + return this.renderMessages; } - @Override - public void setMatrixScale(Vector3f scale, PoseStack matrixStack){ - matrixStack.scale(scale.x,-scale.y,1); + public MutableComponent[] getMessages() { + MutableComponent[] components = new MutableComponent[contents.size()]; + for (String string : contents){ + components[contents.indexOf(string)] = Component.literal(string); + } + return components; } @Override - public void draw(VertexBuilder builder) { - Minecraft client = Minecraft.getInstance(); - Font textRenderer = client.font; - - float[] lineHeights = new float[this.contents.size()]; - float totalHeight = 0.0f; - for (int i = 0; i < this.contents.size(); i++) { - lineHeights[i] = textRenderer.wordWrapHeight(this.contents.get(i), Integer.MAX_VALUE) * 1.25f; + protected void drawInternal(VertexBuilder builder) { + + MultiBufferSource.BufferSource bufferSource = Minecraft.getInstance() + .renderBuffers().bufferSource(); + + Minecraft mc = Minecraft.getInstance(); + Font font = mc.font; + float totalHeight = 0f; + float[] lineHeights = new float[contents.size()]; + + for (int i = 0; i < contents.size(); i++) { + String line = contents.get(i); + int wrappedHeight = font.wordWrapHeight(line, Integer.MAX_VALUE); + lineHeights[i] = wrappedHeight * 1.25f; totalHeight += lineHeights[i]; } - float renderYBase = -totalHeight / 2.0f; - for (int i = 0; i < this.contents.size(); i++) { - String text = this.contents.get(i); - float renderX = -textRenderer.width(text) * 0.5f; - float renderY = renderYBase + (i > 0 ? lineHeights[i - 1] : 0); - int colorValue = (this.color.size() > i && this.color.get(i) != null) ? this.color.get(i).getRGB() : Color.WHITE.getRGB(); - - textRenderer.drawInBatch( - text, renderX, renderY, colorValue, true, - builder.getPositionMatrix(), new NonDrawVertexConsumerProvider(builder.getBufferBuilder()), - seeThrough ? Font.DisplayMode.SEE_THROUGH : Font.DisplayMode.NORMAL, - 0, 0xF000F0 - ); - - if (i > 0) renderYBase += lineHeights[i - 1]; + + float yOffset = -totalHeight / 2f; + + FormattedCharSequence[] renderMessages = getRenderMessages(); + for (int i = 0; i < contents.size(); i++) { + String text = contents.get(i); + Color color = i < colors.size() ? colors.get(i) : baseColor; + + float x = -font.width(text) / 2f; + float y = yOffset; + if(outline) { + font.drawInBatch8xOutline(renderMessages[i],x, y, + color.getRGB(), + multiplyRGB(color.getRGB(),0.8f), + builder.getPositionMatrix(), + bufferSource, LightTexture.FULL_BRIGHT); + }else { + font.drawInBatch( + text, + x, y, + outline ? multiplyRGB(color.getRGB(), 0.9f) : color.getRGB(), + shadow, + builder.getPositionMatrix(), + bufferSource, + seeThrough ? Font.DisplayMode.SEE_THROUGH : Font.DisplayMode.POLYGON_OFFSET, + backgroundColor.getRGB(), + LightTexture.FULL_BRIGHT + ); + } + + yOffset += lineHeights[i]; + } + RenderSystem.setShaderColor(1,1,1,1); + } + @Override + public void beforeDraw(PoseStack poseStack, float deltaTime) { + super.beforeDraw(poseStack, deltaTime); + + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Quaternionf camRot = camera.rotation(); + + switch (billBoardMode) { + case ALL -> { + poseStack.mulPose(camRot); + } + case VERTICAL -> { + float yaw = (float) Math.toRadians(camera.getYRot() + 180); + poseStack.mulPose(new Quaternionf().rotateY(yaw)); + } + case HORIZONTAL -> { + float pitch = (float) Math.toRadians(camera.getXRot()); + poseStack.mulPose(new Quaternionf().rotateX(pitch)); + } } + poseStack.scale(0.015625F, -0.015625F, 0.015625F); } - public void applyBillboard(PoseStack matrices, BillBoardMode mode) { - float yaw = Minecraft.getInstance().gameRenderer.getMainCamera().getYRot(); - float pitch = Minecraft.getInstance().gameRenderer.getMainCamera().getXRot(); - - switch (mode) { - case VERTICAL: - matrices.mulPose(new Quaternionf().rotateY((float)Math.toRadians(yaw))); - break; - case HORIZONTAL: - matrices.mulPose(new Quaternionf().rotateX((float)Math.toRadians(pitch))); - break; - case ALL: - matrices.mulPose(Minecraft.getInstance().gameRenderer.getMainCamera().rotation()); - break; + public void setText(int line, String text) { + line--; + if (line >= 0 && line < contents.size()) { + contents.set(line, text); } - }*/ + } + + public void setColor(int line, Color color) { + while (colors.size() <= line) colors.add(Color.WHITE); + colors.set(line, color); + } + public void setBillboardMode(BillBoardMode mode) { + this.billBoardMode = mode; + } + @Override + public boolean shouldDraw() { + return true; + } } \ No newline at end of file diff --git a/src/main/java/mypals/ml/shapeManagers/EmptyShapeManager.java b/src/main/java/mypals/ml/shapeManagers/EmptyShapeManager.java new file mode 100644 index 0000000..b328860 --- /dev/null +++ b/src/main/java/mypals/ml/shapeManagers/EmptyShapeManager.java @@ -0,0 +1,86 @@ +package mypals.ml.shapeManagers; + +import com.mojang.blaze3d.vertex.PoseStack; +import mypals.ml.builderManager.BuilderManager; +import mypals.ml.builderManager.EmptyBuilderManager; +import mypals.ml.shape.Shape; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import static mypals.ml.shapeManagers.ShapeManagers.TEMP_HEADER; + +public class EmptyShapeManager { + public String id; + public EmptyShapeGroup shapeGroup; + public EmptyBuilderManager builderManager; + public static Comparator SHAPE_ORDER_COMPARATOR = (s1, s2) -> { + Vec3 shape1Pos = s1.transformer.getWorldPivot(); + Vec3 shape2Pos = s2.transformer.getWorldPivot(); + double distance1 = shape1Pos.lengthSqr(); // Square of distance for efficiency + double distance2 = shape2Pos.lengthSqr(); + return Double.compare(distance2, distance1); + }; + public EmptyShapeManager(EmptyBuilderManager builderManager, String id){ + this.id = id; + shapeGroup = new EmptyShapeGroup(); + this.builderManager = builderManager; + } + public void syncShapeTransform(){ + shapeGroup.syncShapeTransform(); + } + public void addShape(ResourceLocation identifier,Shape shape){ + shapeGroup.addShape(identifier, shape); + } + public void removeShape(ResourceLocation identifier){ + shapeGroup.removeShape(identifier); + } + public void removeShapes(ResourceLocation root){ + shapeGroup.removeShape(root); + } + public void draw(PoseStack matrixStack,float tickDelta){ + shapeGroup.drawAll(builderManager,matrixStack,tickDelta); + } + public void clearTempAfterRender(){ + shapeGroup.clearTemp(); + } + public static class EmptyShapeGroup{ + public ConcurrentHashMap shapeMap = new ConcurrentHashMap<>(); + public void addShape(ResourceLocation id,Shape shape){ + shapeMap.put(id,shape); + } + public void removeShape(@NotNull ResourceLocation identifier){ + shapeMap.remove(identifier); + } + public void removeShapes(@NotNull ResourceLocation identifier) { + shapeMap.entrySet().removeIf(entry -> entry.getKey().getPath().startsWith(identifier.getPath())); + } + public void clear(){ + shapeMap.clear(); + } + public void clearTemp(){ + shapeMap.entrySet().removeIf(entry -> entry.getKey().getPath().startsWith(TEMP_HEADER)); + } + public void syncShapeTransform(){ + for (Shape shape : shapeMap.values()) { + shape.syncLastToTarget(); + } + } + public void drawAll(EmptyBuilderManager builderManager, PoseStack matrixStack, float tickDelta){ + if(!shapeMap.isEmpty()) { + List sortedShapes = new ArrayList<>(shapeMap.values()); + sortedShapes.sort(SHAPE_ORDER_COMPARATOR); + for (Shape shape : sortedShapes) { + builderManager.draw(builder -> { + shape.draw(true, builder, matrixStack,tickDelta); + }); + } + } + } + } +} diff --git a/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java b/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java index 5c2879a..716a2dd 100644 --- a/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java +++ b/src/main/java/mypals/ml/shapeManagers/ShapeManagers.java @@ -1,4 +1,6 @@ package mypals.ml.shapeManagers; +import mypals.ml.builderManager.EmptyBuilderManager; +import mypals.ml.shape.basics.tags.EmptyMesh; import mypals.ml.shape.basics.tags.ExtractableShape; import net.minecraft.resources.ResourceLocation; import mypals.ml.builderManager.BuilderManager; @@ -15,18 +17,15 @@ public class ShapeManagers { public static final String TEMP_HEADER = "temp_shape"; public static ShapeManager LINES_SHAPE_MANAGER; public static ShapeManager LINE_STRIP_SHAPE_MANAGER; - public static ShapeManager TEXT; public static ShapeManager TRIANGLES_SHAPE_MANAGER; - public static ShapeManager TRIANGLES_STRIP_SHAPE_MANAGER; - public static ShapeManager TRIANGLES_FAN_SHAPE_MANAGER; + public static EmptyShapeManager NON_SHAPE_OBJECTS; public static List managers = new ArrayList<>(); + public static List emptyManagers = new ArrayList<>(); public static void init(){ LINE_STRIP_SHAPE_MANAGER = register(BuilderManagers.LINE_STRIP_BUILDER_MANAGER,"line_strip_shape_manager"); LINES_SHAPE_MANAGER = register(BuilderManagers.LINES_BUILDER_MANAGER,"lines_shape_manager"); TRIANGLES_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_BUILDER_MANAGER,"triangles_shape_manager"); - //TRIANGLES_STRIP_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_STRIP_BUILDER_MANAGER,"triangles_strip_shape_manager"); - //TRIANGLES_FAN_SHAPE_MANAGER = register(BuilderManagers.TRIANGLES_FAN_BUILDER_MANAGER,"triangles_fan_shape_manager"); - TEXT = register(BuilderManagers.TEXT,"text_manager"); + NON_SHAPE_OBJECTS = registerEmpty(BuilderManagers.NON_SHAPE_OBJECTS,"empty"); } public static void renderAll(PoseStack matrixStack, float tickDelta){ for(ShapeManager manager : managers @@ -34,24 +33,36 @@ public static void renderAll(PoseStack matrixStack, float tickDelta){ manager.draw(matrixStack, tickDelta); manager.clearTempAfterRender(); } + for(EmptyShapeManager manager : emptyManagers + ){ + manager.draw(matrixStack, tickDelta); + manager.clearTempAfterRender(); + } } public static ShapeManager register(BuilderManager builderManager,String id){ ShapeManager shapeManager = new ShapeManager(builderManager,id); managers.add(shapeManager); return shapeManager; } + public static EmptyShapeManager registerEmpty(EmptyBuilderManager builderManager, String id){ + EmptyShapeManager shapeManager = new EmptyShapeManager(builderManager,id); + emptyManagers.add(shapeManager); + return shapeManager; + } public static void removeShape(ResourceLocation identifier){ managers.forEach( - (shapeManager) -> { - shapeManager.removeShape(identifier); - } + (shapeManager) -> shapeManager.removeShape(identifier) + ); + emptyManagers.forEach( + (shapeManager) -> shapeManager.removeShape(identifier) ); } public static void removeShapes(ResourceLocation root){ managers.forEach( - (shapeManager) ->{ - shapeManager.removeShapes(root); - } + (shapeManager) -> shapeManager.removeShapes(root) + ); + managers.forEach( + (shapeManager) -> shapeManager.removeShape(root) ); } @@ -61,12 +72,18 @@ public static void addShape(ResourceLocation identifier, Shape shape){ exts.addGroup(identifier); return; } - VertexBuilderGetter.getBuilderManager(shape).addShape(identifier,shape); + if( shape instanceof EmptyMesh ) + VertexBuilderGetter.getEmptyBuilderManager(shape).addShape(identifier,shape); + else + VertexBuilderGetter.getBuilderManager(shape).addShape(identifier,shape); } public static void syncShapeTransform(){ for(ShapeManager manager : managers){ manager.syncShapeTransform(); } + for(EmptyShapeManager manager : emptyManagers){ + manager.syncShapeTransform(); + } } public static void addShape(Shape shape){ addShape(generateUniqueId(TEMP_HEADER),shape); diff --git a/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java b/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java index 3354b8d..7a1ed11 100644 --- a/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java +++ b/src/main/java/mypals/ml/shapeManagers/VertexBuilderGetter.java @@ -1,5 +1,6 @@ package mypals.ml.shapeManagers; +import mypals.ml.shape.basics.tags.EmptyMesh; import mypals.ml.shape.box.BoxFaceShape; import mypals.ml.shape.box.BoxWireframeShape; import mypals.ml.shape.Shape; @@ -21,9 +22,14 @@ public class VertexBuilderGetter { public static Map, ShapeManager> shapeManagerMap = new HashMap<>(); + public static Map, EmptyShapeManager> emptyShapeManagerMap = new HashMap<>(); + public static void registerShapeBuilder(Class shapeClass, ShapeManager manager){ shapeManagerMap.put(shapeClass,manager); } + public static void registerEmptyShapeBuilder(Class shapeClass, EmptyShapeManager manager){ + emptyShapeManagerMap.put(shapeClass,manager); + } public static void init(){ registerShapeBuilder(BoxWireframeShape.class, ShapeManagers.LINES_SHAPE_MANAGER); registerShapeBuilder(BoxFaceShape.class, ShapeManagers.TRIANGLES_SHAPE_MANAGER); @@ -39,12 +45,14 @@ public static void init(){ registerShapeBuilder(ConeWireframeShape.class,ShapeManagers.LINES_SHAPE_MANAGER); registerShapeBuilder(CylinderShape.class,ShapeManagers.TRIANGLES_SHAPE_MANAGER); registerShapeBuilder(ConeShape.class,ShapeManagers.TRIANGLES_SHAPE_MANAGER); - + registerEmptyShapeBuilder(TextShape.class,ShapeManagers.NON_SHAPE_OBJECTS); } public static ShapeManager getBuilderManager(Shape shape){ return shapeManagerMap.getOrDefault(shape.getClass(),null); } - + public static EmptyShapeManager getEmptyBuilderManager(Shape shape){ + return emptyShapeManagerMap.getOrDefault(shape.getClass(),null); + } public void removeShapes(ResourceLocation root){ } diff --git a/src/main/java/mypals/ml/test/Tester.java b/src/main/java/mypals/ml/test/Tester.java index e356a50..469db98 100644 --- a/src/main/java/mypals/ml/test/Tester.java +++ b/src/main/java/mypals/ml/test/Tester.java @@ -1,32 +1,42 @@ package mypals.ml.test; +import com.mojang.brigadier.CommandDispatcher; import mypals.ml.builders.shapeBuilders.ShapeGenerator; import mypals.ml.collision.RayModelIntersection; import mypals.ml.shape.basics.BoxLikeShape; import mypals.ml.shape.basics.CircleLikeShape; +import mypals.ml.shape.basics.core.TwoPointsLineShape; import mypals.ml.shape.box.BoxFaceShape; import mypals.ml.shape.box.BoxShape; import mypals.ml.shape.Shape; import mypals.ml.shape.line.LineShape; import mypals.ml.shape.line.StripLineShape; import mypals.ml.shape.round.SphereShape; +import mypals.ml.shape.text.TextShape; +import mypals.ml.shapeManagers.ShapeManager; import mypals.ml.shapeManagers.ShapeManagers; +import mypals.ml.transform.shapeTransformers.DefaultTransformer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.MouseHandler; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.Vec3; import java.awt.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Random; +import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; import static mypals.ml.RyansRenderingKit.MOD_ID; import com.mojang.blaze3d.vertex.PoseStack; @@ -36,6 +46,7 @@ public class Tester { public static Random random = new Random(); public static float spacing = 8.0f; public static int index = 0; + public static boolean ENABLE_DEBUG = false; static Color randomColor() { return new Color( @@ -49,19 +60,56 @@ static Color randomColor() { static float xPos() { return (index++ * spacing); } - - public static void init(){ - ClientCommandRegistrationCallback.EVENT.register((commandDispatcher, commandRegistryAccess) -> { - commandDispatcher.register( - ClientCommandManager.literal("reload") - .executes(context -> { + public static void registerDebugCommands(CommandDispatcher dispatcher) { + + dispatcher.register( + ClientCommandManager.literal("ryansRenderingKit_DEBUG") + .then(ClientCommandManager.literal("toggle") + .executes(ctx -> { + boolean newValue = toggleDebugMode(); + ctx.getSource().sendFeedback( + Component.nullToEmpty("Ryan's Rendering Kit Debug Mode: " + (newValue ? + "§aENABLED" + : "§cDISABLED"))); + return 1; + }) + ).then(ClientCommandManager.literal("reload") + .executes(ctx -> { added = false; + ctx.getSource().sendFeedback( + Component.nullToEmpty("Ryan's Rendering Kit Debug Shapes Reloaded.")); return 0; }) - ); - }); + ) + ); + } + private static boolean toggleDebugMode(){ + ENABLE_DEBUG = !ENABLE_DEBUG; + if(!ENABLE_DEBUG){ + List.of( + "demo_face_circle", + "demo_line_circle", + "demo_sphere", + "demo_obj_model", + "demo_obj_outline", + "demo_cone", + "demo_cylinder", + "demo_cone_wire", + "demo_cylinder_wire", + "demo_line", + "demo_strip_line", + "demo_box_face", + "demo_box_wire", + "demo_wireframed_box" + ).forEach(name -> ShapeManagers.removeShapes( + ResourceLocation.fromNamespaceAndPath(MOD_ID, name) + )); + } + return ENABLE_DEBUG; + } + public static void init(){ ClientTickEvents.START_WORLD_TICK.register(client -> { - if (added) return; + if (added || !ENABLE_DEBUG) return; index = 0; List.of( "demo_face_circle", @@ -82,7 +130,6 @@ public static void init(){ ResourceLocation.fromNamespaceAndPath(MOD_ID, name) )); - // 1. FaceCircle ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_face_circle"), ShapeGenerator.generateFaceCircle() @@ -99,7 +146,6 @@ public static void init(){ .build(Shape.RenderingType.BATCH) ); - // 2. LineCircle ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_line_circle"), ShapeGenerator.generateLineCircle() @@ -112,15 +158,12 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - //t.setShapeWorldRotationDegrees(0, time * 4, 0); - t.setShapeLocalRotationDegrees(0,time*4,0); t.setShapeWorldRotationDegrees(time*4, 0, 0); }) .build(Shape.RenderingType.BUFFERED) ); - // 3. Sphere ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_sphere"), ShapeGenerator.generateSphere() @@ -173,7 +216,6 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - //t.setShapeWorldRotationDegrees(time * 2, time * 3, 0); if(t.shape.isPlayerLookingAt().hit){ t.shape.baseColor = new Color(255, 234, 0,100); }else{ @@ -185,8 +227,6 @@ public static void init(){ s2.addChild(s3); s1.addChild(s2); - - // 3...3. Sphere ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_sphere_2"), s1 @@ -200,7 +240,6 @@ public static void init(){ s3 ); - // 4. ObjModel ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_obj_model"), ShapeGenerator.generateObjModel() @@ -212,10 +251,9 @@ public static void init(){ float time = client.getGameTime(); transformer.setShapeWorldRotationDegrees(0, time * 5, 0); Shape shape = transformer.shape; - if (shape.isPlayerLookingAt().hit/* 如果玩家正在看着这个 shape*/) { + if (shape.isPlayerLookingAt().hit) { if (Minecraft.getInstance().mouseHandler.isLeftPressed()) { Player player = Minecraft.getInstance().player; - // 第一次抓取时,记录形状与玩家视线的距离 if (!shape.getCustomData("isHolding", false)) { shape.putCustomData("isHolding", true); double distance = transformer @@ -223,15 +261,13 @@ public static void init(){ .distanceTo(player.getEyePosition()); shape.putCustomData("distance", distance); } - // 将模型移动到玩家正前方 - transformer.setShapeWorldPivot( + transformer.setShapeLocalPivot( player.getEyePosition(transformer.getTickDelta()) .add(player.getLookAngle().scale(shape.getCustomData("distance", 5.0))) .add(0, -1, 0) ); transformer.world.position.syncLastToTarget(); } else { - // 松开鼠标 → 结束抓取 if (shape.getCustomData("isHolding", false)) shape.putCustomData("isHolding", false); } @@ -246,7 +282,6 @@ public static void init(){ .build(Shape.RenderingType.BATCH) ); - // 5. ObjModelOutline ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_obj_outline"), ShapeGenerator.generateObjModelOutline() @@ -267,7 +302,6 @@ public static void init(){ .build(Shape.RenderingType.BATCH) ); - // 6. Cone ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_cone"), ShapeGenerator.generateCone() @@ -282,7 +316,7 @@ public static void init(){ float time = client.getGameTime(); t.setShapeWorldRotationDegrees(time, 0, 0); - double f = (Math.sin(time * 0.1) + 1) / 2; // 0 → 1 → 0 + double f = (Math.sin(time * 0.1) + 1) / 2; int seg = (int)(3 + f * 20-3); if(t.shape.isPlayerLookingAt().hit){ t.shape.baseColor = new Color(0, 255, 149,100); @@ -294,7 +328,6 @@ public static void init(){ .build(Shape.RenderingType.BATCH) ); - // 7. Cylinder ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_cylinder"), ShapeGenerator.generateCylinder() @@ -327,7 +360,7 @@ public static void init(){ float time = client.getGameTime(); t.setShapeWorldRotationDegrees(time, 0, 0); - double f = (Math.sin(time * 0.1) + 1) / 2; // 0 → 1 → 0 + double f = (Math.sin(time * 0.1) + 1) / 2; int seg = (int)(3 + f * 20-3); t.setSegment(Math.max(3,seg)); @@ -348,13 +381,11 @@ public static void init(){ .transform((t) -> { float time = client.getGameTime(); t.setShapeWorldRotationDegrees(time*2, time * 5, 0); - //t.setShapeLocalRotationDegrees(0,0,time * 5); }) .build(Shape.RenderingType.BATCH) ); float linex = xPos(); - // 8. Line ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_line"), ShapeGenerator.generateLine() @@ -371,7 +402,6 @@ public static void init(){ .build(Shape.RenderingType.BATCH) ); - // 9. StripLine(螺旋线) float cx = xPos(); ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_strip_line"), @@ -382,13 +412,11 @@ public static void init(){ .seeThrough(false) .transform((t) -> { float time = client.getGameTime(); - // 动态更新顶点(旋转螺旋) ((StripLineShape)t.getShape()).setVertexes(generateSpiral(cx, 100, 2.0f, 5.0f, time * 0.05f)); }) .build(Shape.RenderingType.BATCH) ); - // 10. BoxFace ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_box_face"), ShapeGenerator.generateBoxFace() @@ -404,7 +432,6 @@ public static void init(){ .build(Shape.RenderingType.BATCH) ); - // 11. BoxWireframe ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_box_wire"), ShapeGenerator.generateBoxWireframe() @@ -421,7 +448,6 @@ public static void init(){ .build(Shape.RenderingType.BATCH) ); - // 12. WireframedBox ShapeManagers.addShape( ResourceLocation.fromNamespaceAndPath(MOD_ID, "demo_wireframed_box"), ShapeGenerator.generateWireframedBox() @@ -439,7 +465,24 @@ public static void init(){ }) .build(Shape.RenderingType.BATCH) ); + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "test_text1"), + ShapeGenerator.generateText() + .pos(new Vec3(xPos(), 0, 0)) + .type(Shape.RenderingType.BATCH) + .billBoardMode(TextShape.BillBoardMode.ALL) + .seeThrough(false) + .shadow(true) + .outline(true) + .texts("§b§l!TEST!", "§e你好", "§aAWA", "§dBillBoardMode.ALL") + .textColors(Color.CYAN, Color.YELLOW, Color.GREEN, Color.MAGENTA) + + .transform(t -> t.setShapeMatrixPivot( + new Vec3(0, Math.sin(Minecraft.getInstance().player.tickCount * 0.05) * 0.3, 0) + )) + .build(Shape.RenderingType.BATCH) + ); added = true; }); @@ -462,6 +505,128 @@ private static List generateSpiral(float centerX, int segments, float radi } public static void renderTick(PoseStack matrixStack){ } + public static void generateLineTracker(){ + Minecraft mc = Minecraft.getInstance(); + Shape anchor1 = ShapeGenerator.generateSphere() + .pos(new Vec3(xPos(), 0,0)) + .radius(0.1f) + .segments(16) + .color(new Color(0,0,1,0.5f)) + .seeThrough(false) + .transform((transformer) -> { + Shape shape = transformer.getShape(); + handleMouseGrab(mc.player,shape,transformer); + + }) + .build(Shape.RenderingType.BATCH); + Shape anchor2 = ShapeGenerator.generateSphere() + .pos(new Vec3(xPos(), 0,0)) + .radius(0.1f) + .segments(16) + .color(new Color(1,0,0,0.5f)) + .seeThrough(false) + .transform((transformer) -> { + Shape shape = transformer.getShape(); + handleMouseGrab(mc.player,shape,transformer); + }) + .build(Shape.RenderingType.BATCH); + + Shape line = ShapeGenerator.generateLine() + .pos(new Vec3(0, 0,0)) + .lineWidth(2.1f) + .color(Color.WHITE) + .seeThrough(false) + .transform((transformer) -> { + transformer.setStart(anchor1.transformer.getShapeWorldPivot(true)); + transformer.setEnd(anchor2.transformer.getShapeWorldPivot(true)); + }) + .build(Shape.RenderingType.BATCH); + + Shape text = ShapeGenerator.generateText() + .pos(new Vec3(xPos(), 0, 0)) + .type(Shape.RenderingType.BATCH) + .billBoardMode(TextShape.BillBoardMode.ALL) + .seeThrough(false) + .shadow(true) + .outline(false) + .texts("-") + .textColors(Color.CYAN, Color.YELLOW, Color.GREEN, Color.MAGENTA) + .transform(t -> { + Vec3 start = ((TwoPointsLineShape.TwoPointsLineTransformer)line.transformer).getStart(true); + Vec3 end = ((TwoPointsLineShape.TwoPointsLineTransformer)line.transformer).getEnd(true); + Entity player = Minecraft.getInstance().player; + if (player == null) return; + Vec3 eyePos = player.getPosition(t.getTickDelta()).add(0, player.getEyeHeight(), 0); + Vec3 lineDir = end.subtract(start); + double lineLength = lineDir.length(); + if (lineLength < 0.001) { + t.setShapeMatrixPivot(start.add(0, 0.1, 0)); + return; + } + Vec3 toEye = eyePos.subtract(start); + double proj = toEye.dot(lineDir) / (lineLength * lineLength); + proj = Mth.clamp(proj, 0.0, 1.0); + Vec3 closestPoint = start.add(lineDir.scale(proj)); + Vec3 labelOffset = new Vec3(0, 0.3, 0); + Vec3 towardsPlayer = eyePos.subtract(closestPoint).normalize().scale(0.15); + Vec3 finalPos = closestPoint.add(labelOffset).add(towardsPlayer.yRot((float)Math.toRadians(15))); + t.setShapeWorldPivot(finalPos); + t.world.syncLastToTarget(); + double d = anchor1.transformer.getWorldPivot().distanceTo(anchor2.transformer.getWorldPivot()); + ((TextShape)t.shape).setText(1,"Distance : " + String.format("%.2f", d)); + }) + .build(Shape.RenderingType.BATCH); + + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "a1"), + anchor1 + ); + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "a2"), + anchor2 + ); + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "l1"), + line + ); + ShapeManagers.addShape( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "tex"), + text + ); + } + public static void handleMouseGrab(Player player, Shape shape, DefaultTransformer transformer) { + Minecraft mc = Minecraft.getInstance(); + boolean isLeftPressed = mc.mouseHandler.isLeftPressed(); + boolean wasHolding = shape.getCustomData("isHolding", false); + boolean isLookingAt = shape.isPlayerLookingAt().hit; + if(!wasHolding) shape.putCustomData("color",shape.baseColor); + boolean shouldHold = wasHolding || (isLeftPressed && isLookingAt); + if (shouldHold && !wasHolding) { + double distance = transformer.getShapeWorldPivot(true) + .distanceTo(player.getEyePosition()); + shape.putCustomData("grabDistance", distance); + shape.putCustomData("isHolding", true); + } + if (isLeftPressed && shape.getCustomData("isHolding", false)) { + double savedDistance = shape.getCustomData("grabDistance", 4.0); + Vec3 eyePos = player.getEyePosition(transformer.getTickDelta()); + Vec3 look = player.getLookAngle(); + + Vec3 targetPos = eyePos.add(look.scale(savedDistance)); + + transformer.setShapeWorldPivot(targetPos); + transformer.world.position.syncLastToTarget(); + shape.baseColor = new Color(255, 255, 255, 200); + } + else if (!isLeftPressed && wasHolding) { + shape.putCustomData("isHolding", false); + shape.putCustomData("grabDistance", null); + shape.baseColor = shape.getCustomData("color",Color.WHITE); + } + else if (!isLeftPressed) { + shape.baseColor = shape.getCustomData("color",Color.WHITE); + } + } public static void rotate(BoxLikeShape.BoxTransformer boxTransformer, Shape shape){ float gameTime = Minecraft.getInstance().level.getGameTime(); @@ -470,7 +635,7 @@ public static void rotate(BoxLikeShape.BoxTransformer boxTransformer, Shape shap } public static void addEntity(Entity entity) { Player player = Minecraft.getInstance().player; - if (player == null || entity == null || entity == player) return; + if (player == null || entity == null || entity == player || !ENABLE_DEBUG) return; int entityId = entity.getId(); EntityDimensions dimensions = entity.getDimensions(entity.getPose()); @@ -480,7 +645,7 @@ public static void addEntity(Entity entity) { Shape.RenderingType.BATCH, (transformer) -> { Vec3 entityCenter = entity.position().add(0, dimensions.height() / 2, 0); - if (entity.isRemoved()) { + if (entity.isRemoved() || !ENABLE_DEBUG) { transformer.shape.discard(); return; } @@ -499,13 +664,12 @@ public static void addEntity(Entity entity) { new LineShape( Shape.RenderingType.BATCH, (transformer) -> { - if (entity.isRemoved()) { + if (entity.isRemoved() || !ENABLE_DEBUG) { transformer.getShape().discard(); return; } if (Minecraft.getInstance().level != null && Minecraft.getInstance().player != null) { - transformer.setStart(player.getEyePosition().add(player.getLookAngle().scale(2))); - //transformer.lineModelInfo.syncLastToTarget(); + ((LineShape)transformer.getShape()).forceSetStart(player.getEyePosition(transformer.getTickDelta()).add(player.getLookAngle().scale(2))); transformer.setEnd(entity.position()); } }, diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java b/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java index 7f89c83..9ca6a4a 100644 --- a/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java +++ b/src/main/java/mypals/ml/transform/shapeTransformers/DefaultTransformer.java @@ -44,30 +44,23 @@ public void syncLastToTarget() { } public void applyTransformations(PoseStack stack,boolean lerp) { - applyLayer(stack,local,false, lerp); - applyLayer(stack,world,false,lerp); - applyLayer(stack, matrix,false, lerp); + applyLayer(stack,world,lerp); + applyLayer(stack,local, lerp); + applyLayer(stack, matrix, lerp); } public void applyModelTransformations(PoseStack stack,boolean lerp) { - applyLayer(stack,local,false,lerp); - applyLayer(stack,world,false,lerp); + applyLayer(stack,world,lerp); + applyLayer(stack,local,lerp); } - private void applyLayer(PoseStack stack, TransformLayer layer,boolean isLocal, boolean lerp) { + private void applyLayer(PoseStack stack, TransformLayer layer, boolean lerp) { Vec3 p = layer.position.getValue(lerp); Quaternionf r = layer.rotation.getValue(lerp); Vec3 s = layer.scale.getValue(lerp); - if(isLocal){ - stack.translate(p.x, p.y, p.z); - stack.mulPose(r); - stack.scale((float) s.x, (float) s.y, (float) s.z); - stack.translate(-p.x, -p.y, -p.z); - }else{ - stack.translate(p.x, p.y, p.z); - stack.mulPose(r); - stack.scale((float) s.x, (float) s.y, (float) s.z); - } + stack.translate(p.x, p.y, p.z); + stack.mulPose(r); + stack.scale((float) s.x, (float) s.y, (float) s.z); } @@ -75,7 +68,6 @@ private void applyLayer(PoseStack stack, TransformLayer layer,boolean isLocal, b public TransformLayer world() { return world; } public TransformLayer matrix() { return matrix; } - // ====================== WORLD GETTERS ====================== public Vec3 getShapeWorldPivot(boolean lerp) { return world.getPosition(lerp); } @@ -88,7 +80,6 @@ public Vec3 getShapeWorldScale(boolean lerp) { return world.getScale(lerp); } - // ====================== LOCAL GETTERS ====================== public Vec3 getShapeLocalPivot(boolean lerp) { return local.getPosition(lerp); } @@ -101,7 +92,6 @@ public Vec3 getShapeLocalScale(boolean lerp) { return local.getScale(lerp); } - // ====================== MATRIX (Render) GETTERS ====================== public Vec3 getShapeMatrixPivot(boolean lerp) { return matrix.getPosition(lerp); } @@ -127,7 +117,6 @@ public Vec3 getShapeMatrixScale(boolean lerp) { public Vec3 getMatrixScale() { return getShapeMatrixScale(true); } - // World public void setShapeWorldPivot(Vec3 v) { world.setPosition(v); } public void setShapeWorldRotation(Quaternionf q) { world.setRotation(q); } public void setShapeWorldRotationDegrees(float x, float y, float z) { @@ -139,7 +128,6 @@ public void setShapeWorldRotationDegrees(float x, float y, float z) { } public void setShapeWorldScale(Vec3 s) { world.setScale(s); } - // Local public void setShapeLocalPivot(Vec3 v) { local.setPosition(v); } public void setShapeLocalRotation(Quaternionf q) { local.setRotation(q); } public void setShapeLocalRotationDegrees(float x, float y, float z) { @@ -151,7 +139,6 @@ public void setShapeLocalRotationDegrees(float x, float y, float z) { } public void setShapeLocalScale(Vec3 s) { local.setScale(s); } - // Matrix (Render layer) public void setShapeMatrixPivot(Vec3 v) { matrix.setPosition(v); } public void setShapeMatrixRotation(Quaternionf q) { matrix.setRotation(q); } public void setShapeMatrixRotationDegrees(float x, float y, float z) { diff --git a/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java index 994b456..dfe4ed9 100644 --- a/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java +++ b/src/main/java/mypals/ml/transform/shapeTransformers/shapeModelInfoTransformer/LineModelInfo.java @@ -59,9 +59,9 @@ public void setEnd(Vec3 target){ @Override public void syncLastToTarget() { + super.syncLastToTarget(); this.startPointTransformer.syncLastToTarget(); this.endPointTransformer.syncLastToTarget(); - super.syncLastToTarget(); } } } \ No newline at end of file