[libgdx] updateCache sorting improvements.

This commit is contained in:
Nathan Sweet 2025-05-02 18:34:23 -04:00
parent 020cd51b96
commit 589200250b
8 changed files with 85 additions and 100 deletions

View File

@ -65,12 +65,4 @@ public class Bone extends PosedActive<BoneData, BoneLocal, BonePose> {
public Array<Bone> getChildren () {
return children;
}
void resetUpdate (Skeleton skeleton) {
if (applied.world != skeleton.update) return;
applied.world = 0;
Bone[] children = this.children.items;
for (int i = 0, n = this.children.size; i < n; i++)
children[i].resetUpdate(skeleton);
}
}

View File

@ -139,8 +139,8 @@ public class BonePose extends BoneLocal implements Update {
* Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The local transform after
* calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */
public void updateLocalTransform (Skeleton skeleton) {
world = skeleton.update;
local = 0;
world = skeleton.update;
if (bone.parent == null) {
x = worldX - skeleton.x;
@ -219,10 +219,33 @@ public class BonePose extends BoneLocal implements Update {
}
}
/** When true, the world transform has been modified and the local transform no longer matches. Call
* {@link #updateLocalTransform(Skeleton)} before using the local transform. */
public boolean isLocalDirty (Skeleton skeleton) {
return local == skeleton.update;
/** If the world transform has been modified and the local transform no longer matches, {@link #updateLocalTransform(Skeleton)}
* is called. */
public void validateLocalTransform (Skeleton skeleton) {
if (local == skeleton.update) updateLocalTransform(skeleton);
}
void modifyLocal (Skeleton skeleton) {
if (local == skeleton.update) updateLocalTransform(skeleton);
world = 0;
resetWorld(skeleton.update);
}
void modifyWorld (int update) {
local = update;
world = update;
resetWorld(update);
}
void resetWorld (int update) {
Bone[] children = bone.children.items;
for (int i = 0, n = bone.children.size; i < n; i++) {
BonePose child = children[i].applied;
if (child.world == update) {
child.world = 0;
child.resetWorld(update);
}
}
}
/** Part of the world transform matrix for the X axis. If changed, {@link #updateLocalTransform(Skeleton)} should be called. */

View File

@ -76,19 +76,12 @@ public class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
void sort (Skeleton skeleton) {
skeleton.sortBone(target);
Bone parent = bones.items[0].bone;
skeleton.resetCache(parent);
skeleton.sortBone(parent);
if (bones.size == 1) {
skeleton.updateCache.add(this);
skeleton.sortReset(parent.children);
} else {
Bone child = bones.items[1].bone;
skeleton.resetCache(child);
skeleton.sortBone(child);
skeleton.updateCache.add(this);
skeleton.sortReset(parent.children);
child.sorted = true;
}
skeleton.updateCache.add(this);
parent.sorted = false;
skeleton.sortReset(parent.children);
skeleton.constrained(parent);
if (bones.size > 1) skeleton.constrained(bones.items[1].bone);
}
boolean isSourceActive () {
@ -112,9 +105,9 @@ public class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
/** Applies 1 bone IK. The target is specified in the world coordinate system. */
static public void apply (Skeleton skeleton, BonePose bone, float targetX, float targetY, boolean compress, boolean stretch,
boolean uniform, float alpha) {
boolean uniform, float mix) {
if (bone == null) throw new IllegalArgumentException("bone cannot be null.");
if (bone.local == skeleton.update) bone.updateLocalTransform(skeleton);
bone.modifyLocal(skeleton);
BonePose p = bone.bone.parent.applied;
float pa = p.a, pb = p.b, pc = p.c, pd = p.d;
float rotationIK = -bone.shearX - bone.rotation, tx, ty;
@ -148,7 +141,7 @@ public class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
rotationIK -= 360;
else if (rotationIK < -180) //
rotationIK += 360;
bone.rotation += rotationIK * alpha;
bone.rotation += rotationIK * mix;
if (compress || stretch) {
switch (bone.inherit) {
case noScale, noScaleOrReflection -> {
@ -160,25 +153,23 @@ public class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
if (b > 0.0001f) {
float dd = tx * tx + ty * ty;
if ((compress && dd < b * b) || (stretch && dd > b * b)) {
float s = ((float)Math.sqrt(dd) / b - 1) * alpha + 1;
float s = ((float)Math.sqrt(dd) / b - 1) * mix + 1;
bone.scaleX *= s;
if (uniform) bone.scaleY *= s;
}
}
}
bone.updateWorldTransform(skeleton);
bone.bone.resetUpdate(skeleton);
}
/** Applies 2 bone IK. The target is specified in the world coordinate system.
* @param child A direct descendant of the parent bone. */
static public void apply (Skeleton skeleton, BonePose parent, BonePose child, float targetX, float targetY, int bendDir,
boolean stretch, boolean uniform, float softness, float alpha) {
boolean stretch, boolean uniform, float softness, float mix) {
if (parent == null) throw new IllegalArgumentException("parent cannot be null.");
if (child == null) throw new IllegalArgumentException("child cannot be null.");
if (parent.inherit != Inherit.normal || child.inherit != Inherit.normal) return;
if (parent.local == skeleton.update) parent.updateLocalTransform(skeleton);
if (child.local == skeleton.update) child.updateLocalTransform(skeleton);
parent.modifyLocal(skeleton);
child.modifyLocal(skeleton);
float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
int os1, os2, s2;
if (psx < 0) {
@ -218,10 +209,8 @@ public class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.bone.data.length * csx, a1, a2;
if (l1 < 0.0001f) {
apply(skeleton, parent, targetX, targetY, false, stretch, false, alpha);
apply(skeleton, parent, targetX, targetY, false, stretch, false, mix);
child.rotation = 0;
child.updateWorldTransform(skeleton);
child.bone.resetUpdate(skeleton);
return;
}
x = targetX - pp.worldX;
@ -250,7 +239,7 @@ public class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
cos = 1;
a2 = 0;
if (stretch) {
a = ((float)Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
a = ((float)Math.sqrt(dd) / (l1 + l2) - 1) * mix + 1;
parent.scaleX *= a;
if (uniform) parent.scaleY *= a;
}
@ -315,15 +304,12 @@ public class IkConstraint extends Constraint<IkConstraint, IkConstraintData, IkC
a1 -= 360;
else if (a1 < -180) //
a1 += 360;
parent.rotation += a1 * alpha;
parent.updateWorldTransform(skeleton);
parent.rotation += a1 * mix;
a2 = ((a2 + os) * radDeg - child.shearX) * s2 + os2 - child.rotation;
if (a2 > 180)
a2 -= 360;
else if (a2 < -180) //
a2 += 360;
child.rotation += a2 * alpha;
child.updateWorldTransform(skeleton);
parent.bone.resetUpdate(skeleton);
child.rotation += a2 * mix;
}
}

View File

@ -152,7 +152,7 @@ public class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
BonePose p = slot.bone.applied;
offsetRotation *= p.a * p.d - p.b * p.c > 0 ? degRad : -degRad;
}
for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
for (int i = 0, p = 3, u = skeleton.update; i < boneCount; i++, p += 3) {
BonePose bone = bones[i];
bone.worldX += (boneX - bone.worldX) * mixX;
bone.worldY += (boneY - bone.worldY) * mixY;
@ -196,8 +196,7 @@ public class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
bone.c = sin * a + cos * c;
bone.d = sin * b + cos * d;
}
bone.local = skeleton.update;
bone.bone.resetUpdate(skeleton);
bone.modifyWorld(u);
}
}
@ -462,16 +461,16 @@ public class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
void sort (Skeleton skeleton) {
int slotIndex = slot.getData().index;
Bone slotBone = slot.bone;
if (skeleton.skin != null) sortPathConstraintAttachment(skeleton, skeleton.skin, slotIndex, slotBone);
if (skeleton.skin != null) sortPathSlot(skeleton, skeleton.skin, slotIndex, slotBone);
if (skeleton.data.defaultSkin != null && skeleton.data.defaultSkin != skeleton.skin)
sortPathConstraintAttachment(skeleton, skeleton.data.defaultSkin, slotIndex, slotBone);
sortPathConstraintAttachment(skeleton, slot.pose.attachment, slotBone);
sortPathSlot(skeleton, skeleton.data.defaultSkin, slotIndex, slotBone);
sortPath(skeleton, slot.pose.attachment, slotBone);
BonePose[] bones = this.bones.items;
int boneCount = this.bones.size;
for (int i = 0; i < boneCount; i++) {
Bone bone = bones[i].bone;
skeleton.resetCache(bone);
skeleton.sortBone(bone);
skeleton.constrained(bone);
}
skeleton.updateCache.add(this);
for (int i = 0; i < boneCount; i++)
@ -480,15 +479,15 @@ public class PathConstraint extends Constraint<PathConstraint, PathConstraintDat
bones[i].bone.sorted = true;
}
private void sortPathConstraintAttachment (Skeleton skeleton, Skin skin, int slotIndex, Bone slotBone) {
private void sortPathSlot (Skeleton skeleton, Skin skin, int slotIndex, Bone slotBone) {
Object[] entries = skin.attachments.orderedItems().items;
for (int i = 0, n = skin.attachments.size; i < n; i++) {
var entry = (SkinEntry)entries[i];
if (entry.slotIndex == slotIndex) sortPathConstraintAttachment(skeleton, entry.attachment, slotBone);
if (entry.slotIndex == slotIndex) sortPath(skeleton, entry.attachment, slotBone);
}
}
private void sortPathConstraintAttachment (Skeleton skeleton, Attachment attachment, Bone slotBone) {
private void sortPath (Skeleton skeleton, Attachment attachment, Bone slotBone) {
if (!(attachment instanceof PathAttachment pathAttachment)) return;
int[] pathBones = pathAttachment.getBones();
if (pathBones == null)

View File

@ -263,16 +263,15 @@ public class PhysicsConstraint extends Constraint<PhysicsConstraint, PhysicsCons
tx = l * bone.a;
ty = l * bone.c;
}
bone.local = skeleton.update;
bone.bone.resetUpdate(skeleton);
bone.modifyWorld(skeleton.update);
}
void sort (Skeleton skeleton) {
Bone bone = this.bone.bone;
skeleton.sortBone(bone);
skeleton.resetCache(bone);
skeleton.updateCache.add(this);
skeleton.sortReset(bone.children);
skeleton.constrained(bone);
}
boolean isSourceActive () {

View File

@ -202,7 +202,7 @@ public class Skeleton {
if (updateCache[i] instanceof Bone bone) updateCache[i] = bone.applied;
}
void resetCache (Posed object) {
void constrained (Posed object) {
if (object.pose == object.applied) {
object.applied = object.constrained;
resetCache.add(object);

View File

@ -70,22 +70,10 @@ public class Slider extends Constraint<Slider, SliderData, SliderPose> {
pose.time = Math.max(0, pose.time);
}
SliderData data = this.data;
Timeline[] timelines = animation.timelines.items;
int timelineCount = animation.timelines.size;
Bone[] bones = skeleton.bones.items;
if (pose.mix == 1) {
for (int i = 0; i < timelineCount; i++)
if (timelines[i] instanceof BoneTimeline timeline) bones[timeline.getBoneIndex()].resetUpdate(skeleton);
} else {
for (int i = 0; i < timelineCount; i++) {
if (timelines[i] instanceof BoneTimeline timeline) {
Bone bone = bones[timeline.getBoneIndex()];
bone.resetUpdate(skeleton);
if (bone.applied.local == skeleton.update) bone.applied.updateLocalTransform(skeleton);
}
}
}
Timeline[] timelines = animation.timelines.items;
for (int i = 0, n = animation.timelines.size; i < n; i++)
if (timelines[i] instanceof BoneTimeline timeline) bones[timeline.getBoneIndex()].applied.modifyLocal(skeleton);
SliderPose pose = applied;
animation.apply(skeleton, pose.time, pose.time, data.loop, null, pose.mix, data.additive ? MixBlend.add : MixBlend.replace,
@ -93,35 +81,32 @@ public class Slider extends Constraint<Slider, SliderData, SliderPose> {
}
void sort (Skeleton skeleton) {
Timeline[] timelines = data.animation.timelines.items;
int timelineCount = data.animation.timelines.size;
Bone[] bones = skeleton.bones.items;
for (int i = 0; i < timelineCount; i++)
if (timelines[i] instanceof BoneTimeline timeline) skeleton.sortBone(bones[timeline.getBoneIndex()]);
if (bone != null && !data.local) skeleton.sortBone(bone);
skeleton.updateCache.add(this);
Timeline[] timelines = data.animation.timelines.items;
Bone[] bones = skeleton.bones.items;
Slot[] slots = skeleton.slots.items;
Constraint[] constraints = skeleton.constraints.items;
PhysicsConstraint[] physics = skeleton.physics.items;
int physicsCount = skeleton.physics.size;
for (int i = 0; i < timelineCount; i++) {
for (int i = 0, n = data.animation.timelines.size; i < n; i++) {
Timeline t = timelines[i];
if (t instanceof BoneTimeline timeline) {
Bone bone = bones[timeline.getBoneIndex()];
skeleton.resetCache(bone);
skeleton.sortReset(bone.children);
bone.sorted = false;
skeleton.sortReset(bone.children);
skeleton.constrained(bone);
} else if (t instanceof SlotTimeline timeline)
skeleton.resetCache(slots[timeline.getSlotIndex()]);
skeleton.constrained(slots[timeline.getSlotIndex()]);
else if (t instanceof PhysicsConstraintTimeline timeline) {
if (timeline.constraintIndex == -1) {
for (int ii = 0; ii < physicsCount; ii++)
skeleton.resetCache(physics[ii]);
skeleton.constrained(physics[ii]);
} else
skeleton.resetCache(constraints[timeline.constraintIndex]);
skeleton.constrained(constraints[timeline.constraintIndex]);
} else if (t instanceof ConstraintTimeline timeline) //
skeleton.resetCache(constraints[timeline.getConstraintIndex()]);
skeleton.constrained(constraints[timeline.getConstraintIndex()]);
}
}

View File

@ -72,13 +72,16 @@ public class TransformConstraint extends Constraint<TransformConstraint, Transfo
float[] offsets = data.offsets;
BonePose source = this.source.applied;
int update = skeleton.update;
if (localSource && source.local == skeleton.update) source.updateLocalTransform(skeleton);
if (localSource) source.modifyLocal(skeleton);
FromProperty[] fromItems = data.properties.items;
int fn = data.properties.size;
BonePose[] bones = this.bones.items;
for (int i = 0, n = this.bones.size; i < n; i++) {
BonePose bone = bones[i];
if (localTarget && bone.local == update) bone.updateLocalTransform(skeleton);
if (localTarget)
bone.modifyLocal(skeleton);
else
bone.modifyWorld(update);
for (int f = 0; f < fn; f++) {
FromProperty from = fromItems[f];
float value = from.value(source, localSource, offsets) - from.offset;
@ -97,28 +100,26 @@ public class TransformConstraint extends Constraint<TransformConstraint, Transfo
}
}
}
if (localTarget)
bone.updateWorldTransform(skeleton);
else
bone.local = update;
bone.bone.resetUpdate(skeleton);
}
}
void sort (Skeleton skeleton) {
skeleton.sortBone(source);
if (!data.localSource) skeleton.sortBone(source);
BonePose[] bones = this.bones.items;
int boneCount = this.bones.size;
for (int i = 0; i < boneCount; i++) {
Bone bone = bones[i].bone;
skeleton.resetCache(bone);
skeleton.sortBone(bone);
boolean worldTarget = !data.localTarget;
if (worldTarget) {
for (int i = 0; i < boneCount; i++)
skeleton.sortBone(bones[i].bone);
}
skeleton.updateCache.add(this);
for (int i = 0; i < boneCount; i++) {
Bone bone = bones[i].bone;
skeleton.sortReset(bone.children);
skeleton.constrained(bone);
}
for (int i = 0; i < boneCount; i++)
skeleton.sortReset(bones[i].bone.children);
for (int i = 0; i < boneCount; i++)
bones[i].bone.sorted = true;
bones[i].bone.sorted = worldTarget;
}
boolean isSourceActive () {