-
Notifications
You must be signed in to change notification settings - Fork 0
Java Library Concepts
This is mostly for internal housekeeping and documentation, and much of it only really matters if you're writing a model by hand. The Blockbench exporter should abstract most of the technical details mentioned here.
MarionettePart |
one segment: a Forge PartEntity with a length, a direction, and a hitbox |
Limb |
an ordered chain of parts + the animator that drives it |
FabrikAnimator |
solver which decides where each part ends up |
Marionette |
the interface your entity implements to own a list of limbs |
A MarionettePart is a entity in the world with a real hitbox, so a
tentacle can be hit, collided with and hurt along its length.
Its length is fixed at construction and its position is the center of the segment, not either end. The two ends are derived:
getRootPos() // position - direction * length/2, the parent-side end
getEndPos() // position + direction * length/2, the tip end
setRootPos(v) // move so the parent-side end lands on v
setEndPos(v) // move so the tip end lands on vdirection is a unit vector; setPartDirection normalizes for you. Everything
is in blocks.
Positions are double-buffered: setPartPos stores a pending position that
position() reports immediately, and tick() commits it and syncs the
previous-position fields so rendering is interpolated. The solver
calls tick() on every part at the end of a solve, so you normally never should.
Limb<MarionettePart<OctopusEntity>> tentacle = Limb.builder(this)
.segments(4, 16f / 16, 16f / 16, 16f / 16) // count, sizeXZ, sizeY, length
.segments(4, 12f / 16, 12f / 16, 12f / 16) // taper by chaining calls
.segments(7, 10f / 16, 10f / 16, 10f / 16)
.build();Index 0 is the root end, attached to the entity; the last index is the tip that reaches for the target. Part order has to match the model's segment-name array.
Call setFabrikTarget(...) in your entity's tick(), then tickMarionette().
Each solve runs a backward pass (walk from the root, pinning each segment's root
end to the previous segment's tip) and a forward pass (walk from the tip,
pinning each segment's tip to the next segment's root), repeating until the tip
is within 0.01 blocks of the target or 100 iterations are up.
followRootOnly(true) drops the forward pass: segments
only follow the root, dragging along behind it. Use this for worms, tails, anything that doesn't have a set root position.
As with everything in this page, it only really matters when you're writing a model by hand. the Blockbench exporter already applies all of them.
- A segment's model part is positioned relative to the entity, in model
units (16 per block), with Y flipped: the rest position of every part is
PartPose.offset(0, 24, 0). -
setupAnimoverwrites every part's position and rotation each frame, so the pose baked into yourPartDefinitions is never seen. Author geometry around the segment's centre and leave the pose at the default. - Segment geometry points down +Z. The renderer rotates the whole model 180° about Y so that matches Blockbench's forward.
Java library
Blockbench plugin