Remove internal phase comments and format code
Strip all Phase references, TODO/FUTURE roadmap notes, and internal planning comments from the codebase. Run Prettier for consistent formatting across all Java files.
This commit is contained in:
@@ -5,11 +5,11 @@ import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
||||
import dev.kosmx.playerAnim.core.util.Ease;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
@@ -52,10 +52,16 @@ public final class GltfPoseConverter {
|
||||
* @param animationName the name of the animation to convert (e.g. "Struggle", "Idle")
|
||||
* @return a static looping KeyframeAnimation suitable for PlayerAnimator
|
||||
*/
|
||||
public static KeyframeAnimation convert(GltfData data, String animationName) {
|
||||
public static KeyframeAnimation convert(
|
||||
GltfData data,
|
||||
String animationName
|
||||
) {
|
||||
GltfData.AnimationClip rawClip = data.getRawAnimation(animationName);
|
||||
if (rawClip == null) {
|
||||
LOGGER.warn("[GltfPipeline] Animation '{}' not found, falling back to default", animationName);
|
||||
LOGGER.warn(
|
||||
"[GltfPipeline] Animation '{}' not found, falling back to default",
|
||||
animationName
|
||||
);
|
||||
return convert(data);
|
||||
}
|
||||
return convertClip(data, rawClip, "gltf_" + animationName);
|
||||
@@ -76,8 +82,12 @@ public final class GltfPoseConverter {
|
||||
* are only enabled if the GLB has keyframes for them
|
||||
* @return KeyframeAnimation with selective parts active
|
||||
*/
|
||||
public static KeyframeAnimation convertSelective(GltfData data, @Nullable String animationName,
|
||||
Set<String> ownedParts, Set<String> enabledParts) {
|
||||
public static KeyframeAnimation convertSelective(
|
||||
GltfData data,
|
||||
@Nullable String animationName,
|
||||
Set<String> ownedParts,
|
||||
Set<String> enabledParts
|
||||
) {
|
||||
GltfData.AnimationClip rawClip;
|
||||
String animName;
|
||||
if (animationName != null) {
|
||||
@@ -90,7 +100,13 @@ public final class GltfPoseConverter {
|
||||
if (rawClip == null) {
|
||||
rawClip = data.rawGltfAnimation();
|
||||
}
|
||||
return convertClipSelective(data, rawClip, animName, ownedParts, enabledParts);
|
||||
return convertClipSelective(
|
||||
data,
|
||||
rawClip,
|
||||
animName,
|
||||
ownedParts,
|
||||
enabledParts
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,10 +121,17 @@ public final class GltfPoseConverter {
|
||||
* @param ownedParts parts the item explicitly owns (always enabled)
|
||||
* @param enabledParts parts the item may animate (owned + free)
|
||||
*/
|
||||
private static KeyframeAnimation convertClipSelective(GltfData data, GltfData.AnimationClip rawClip,
|
||||
String animName, Set<String> ownedParts, Set<String> enabledParts) {
|
||||
private static KeyframeAnimation convertClipSelective(
|
||||
GltfData data,
|
||||
GltfData.AnimationClip rawClip,
|
||||
String animName,
|
||||
Set<String> ownedParts,
|
||||
Set<String> enabledParts
|
||||
) {
|
||||
KeyframeAnimation.AnimationBuilder builder =
|
||||
new KeyframeAnimation.AnimationBuilder(AnimationFormat.JSON_EMOTECRAFT);
|
||||
new KeyframeAnimation.AnimationBuilder(
|
||||
AnimationFormat.JSON_EMOTECRAFT
|
||||
);
|
||||
|
||||
builder.beginTick = 0;
|
||||
builder.endTick = 1;
|
||||
@@ -129,21 +152,27 @@ public final class GltfPoseConverter {
|
||||
|
||||
// Check if this joint has explicit animation data (not just rest pose fallback).
|
||||
// A bone counts as explicitly animated if it has rotation OR translation keyframes.
|
||||
boolean hasExplicitAnim = rawClip != null && (
|
||||
(j < rawClip.rotations().length && rawClip.rotations()[j] != null)
|
||||
|| (rawClip.translations() != null
|
||||
&& j < rawClip.translations().length
|
||||
&& rawClip.translations()[j] != null)
|
||||
);
|
||||
boolean hasExplicitAnim =
|
||||
rawClip != null &&
|
||||
((j < rawClip.rotations().length &&
|
||||
rawClip.rotations()[j] != null) ||
|
||||
(rawClip.translations() != null &&
|
||||
j < rawClip.translations().length &&
|
||||
rawClip.translations()[j] != null));
|
||||
|
||||
Quaternionf animQ = getRawAnimQuaternion(rawClip, rawRestRotations, j);
|
||||
Quaternionf animQ = getRawAnimQuaternion(
|
||||
rawClip,
|
||||
rawRestRotations,
|
||||
j
|
||||
);
|
||||
Quaternionf restQ = rawRestRotations[j];
|
||||
|
||||
// delta_local = inverse(rest_q) * anim_q (in bone-local frame)
|
||||
Quaternionf deltaLocal = new Quaternionf(restQ).invert().mul(animQ);
|
||||
|
||||
// Convert to PARENT frame: delta_parent = rest * delta_local * inv(rest)
|
||||
Quaternionf deltaParent = new Quaternionf(restQ).mul(deltaLocal)
|
||||
Quaternionf deltaParent = new Quaternionf(restQ)
|
||||
.mul(deltaLocal)
|
||||
.mul(new Quaternionf(restQ).invert());
|
||||
|
||||
// Convert from glTF parent frame to MC model-def frame.
|
||||
@@ -168,7 +197,9 @@ public final class GltfPoseConverter {
|
||||
if (GltfBoneMapper.isLowerBone(boneName)) {
|
||||
String upperBone = GltfBoneMapper.getUpperBoneFor(boneName);
|
||||
if (upperBone != null) {
|
||||
String upperPart = GltfBoneMapper.getAnimPartName(upperBone);
|
||||
String upperPart = GltfBoneMapper.getAnimPartName(
|
||||
upperBone
|
||||
);
|
||||
if (upperPart != null) {
|
||||
partsWithKeyframes.add(upperPart);
|
||||
}
|
||||
@@ -178,11 +209,21 @@ public final class GltfPoseConverter {
|
||||
}
|
||||
|
||||
// Selective: enable owned parts always, free parts only if they have keyframes
|
||||
enableSelectiveParts(builder, ownedParts, enabledParts, partsWithKeyframes);
|
||||
enableSelectiveParts(
|
||||
builder,
|
||||
ownedParts,
|
||||
enabledParts,
|
||||
partsWithKeyframes
|
||||
);
|
||||
|
||||
KeyframeAnimation anim = builder.build();
|
||||
LOGGER.debug("[GltfPipeline] Converted selective animation '{}' (owned: {}, enabled: {}, withKeyframes: {})",
|
||||
animName, ownedParts, enabledParts, partsWithKeyframes);
|
||||
LOGGER.debug(
|
||||
"[GltfPipeline] Converted selective animation '{}' (owned: {}, enabled: {}, withKeyframes: {})",
|
||||
animName,
|
||||
ownedParts,
|
||||
enabledParts,
|
||||
partsWithKeyframes
|
||||
);
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -200,10 +241,11 @@ public final class GltfPoseConverter {
|
||||
* @return set of part names that received actual keyframe data from the GLB
|
||||
*/
|
||||
public static Set<String> addBonesToBuilder(
|
||||
KeyframeAnimation.AnimationBuilder builder,
|
||||
GltfData data, @Nullable GltfData.AnimationClip rawClip,
|
||||
Set<String> ownedParts) {
|
||||
|
||||
KeyframeAnimation.AnimationBuilder builder,
|
||||
GltfData data,
|
||||
@Nullable GltfData.AnimationClip rawClip,
|
||||
Set<String> ownedParts
|
||||
) {
|
||||
String[] jointNames = data.jointNames();
|
||||
Quaternionf[] rawRestRotations = data.rawGltfRestRotations();
|
||||
Set<String> partsWithKeyframes = new HashSet<>();
|
||||
@@ -221,23 +263,33 @@ public final class GltfPoseConverter {
|
||||
if (GltfBoneMapper.isLowerBone(boneName)) {
|
||||
String upperBone = GltfBoneMapper.getUpperBoneFor(boneName);
|
||||
if (upperBone != null) {
|
||||
String upperPart = GltfBoneMapper.getAnimPartName(upperBone);
|
||||
if (upperPart == null || !ownedParts.contains(upperPart)) continue;
|
||||
String upperPart = GltfBoneMapper.getAnimPartName(
|
||||
upperBone
|
||||
);
|
||||
if (
|
||||
upperPart == null || !ownedParts.contains(upperPart)
|
||||
) continue;
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasExplicitAnim = rawClip != null && (
|
||||
(j < rawClip.rotations().length && rawClip.rotations()[j] != null)
|
||||
|| (rawClip.translations() != null
|
||||
&& j < rawClip.translations().length
|
||||
&& rawClip.translations()[j] != null)
|
||||
);
|
||||
boolean hasExplicitAnim =
|
||||
rawClip != null &&
|
||||
((j < rawClip.rotations().length &&
|
||||
rawClip.rotations()[j] != null) ||
|
||||
(rawClip.translations() != null &&
|
||||
j < rawClip.translations().length &&
|
||||
rawClip.translations()[j] != null));
|
||||
|
||||
Quaternionf animQ = getRawAnimQuaternion(rawClip, rawRestRotations, j);
|
||||
Quaternionf animQ = getRawAnimQuaternion(
|
||||
rawClip,
|
||||
rawRestRotations,
|
||||
j
|
||||
);
|
||||
Quaternionf restQ = rawRestRotations[j];
|
||||
|
||||
Quaternionf deltaLocal = new Quaternionf(restQ).invert().mul(animQ);
|
||||
Quaternionf deltaParent = new Quaternionf(restQ).mul(deltaLocal)
|
||||
Quaternionf deltaParent = new Quaternionf(restQ)
|
||||
.mul(deltaLocal)
|
||||
.mul(new Quaternionf(restQ).invert());
|
||||
|
||||
Quaternionf deltaQ = new Quaternionf(deltaParent);
|
||||
@@ -255,8 +307,12 @@ public final class GltfPoseConverter {
|
||||
if (GltfBoneMapper.isLowerBone(boneName)) {
|
||||
String upperBone = GltfBoneMapper.getUpperBoneFor(boneName);
|
||||
if (upperBone != null) {
|
||||
String upperPart = GltfBoneMapper.getAnimPartName(upperBone);
|
||||
if (upperPart != null) partsWithKeyframes.add(upperPart);
|
||||
String upperPart = GltfBoneMapper.getAnimPartName(
|
||||
upperBone
|
||||
);
|
||||
if (upperPart != null) partsWithKeyframes.add(
|
||||
upperPart
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,16 +337,25 @@ public final class GltfPoseConverter {
|
||||
* @return a static looping KeyframeAnimation with all parts enabled
|
||||
*/
|
||||
public static KeyframeAnimation convertWithSkeleton(
|
||||
GltfData skeleton, GltfData.AnimationClip clip, String animName) {
|
||||
GltfData skeleton,
|
||||
GltfData.AnimationClip clip,
|
||||
String animName
|
||||
) {
|
||||
return convertClip(skeleton, clip, animName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal: convert a specific raw animation clip to a KeyframeAnimation.
|
||||
*/
|
||||
private static KeyframeAnimation convertClip(GltfData data, GltfData.AnimationClip rawClip, String animName) {
|
||||
private static KeyframeAnimation convertClip(
|
||||
GltfData data,
|
||||
GltfData.AnimationClip rawClip,
|
||||
String animName
|
||||
) {
|
||||
KeyframeAnimation.AnimationBuilder builder =
|
||||
new KeyframeAnimation.AnimationBuilder(AnimationFormat.JSON_EMOTECRAFT);
|
||||
new KeyframeAnimation.AnimationBuilder(
|
||||
AnimationFormat.JSON_EMOTECRAFT
|
||||
);
|
||||
|
||||
builder.beginTick = 0;
|
||||
builder.endTick = 1;
|
||||
@@ -307,7 +372,11 @@ public final class GltfPoseConverter {
|
||||
|
||||
if (!GltfBoneMapper.isKnownBone(boneName)) continue;
|
||||
|
||||
Quaternionf animQ = getRawAnimQuaternion(rawClip, rawRestRotations, j);
|
||||
Quaternionf animQ = getRawAnimQuaternion(
|
||||
rawClip,
|
||||
rawRestRotations,
|
||||
j
|
||||
);
|
||||
Quaternionf restQ = rawRestRotations[j];
|
||||
|
||||
// delta_local = inverse(rest_q) * anim_q (in bone-local frame)
|
||||
@@ -315,7 +384,8 @@ public final class GltfPoseConverter {
|
||||
|
||||
// Convert to PARENT frame: delta_parent = rest * delta_local * inv(rest)
|
||||
// Simplifies algebraically to: animQ * inv(restQ)
|
||||
Quaternionf deltaParent = new Quaternionf(restQ).mul(deltaLocal)
|
||||
Quaternionf deltaParent = new Quaternionf(restQ)
|
||||
.mul(deltaLocal)
|
||||
.mul(new Quaternionf(restQ).invert());
|
||||
|
||||
// Convert from glTF parent frame to MC model-def frame.
|
||||
@@ -324,12 +394,24 @@ public final class GltfPoseConverter {
|
||||
deltaQ.x = -deltaQ.x;
|
||||
deltaQ.y = -deltaQ.y;
|
||||
|
||||
LOGGER.debug(String.format(
|
||||
"[GltfPipeline] Bone '%s': restQ=(%.3f,%.3f,%.3f,%.3f) animQ=(%.3f,%.3f,%.3f,%.3f) deltaQ=(%.3f,%.3f,%.3f,%.3f)",
|
||||
boneName,
|
||||
restQ.x, restQ.y, restQ.z, restQ.w,
|
||||
animQ.x, animQ.y, animQ.z, animQ.w,
|
||||
deltaQ.x, deltaQ.y, deltaQ.z, deltaQ.w));
|
||||
LOGGER.debug(
|
||||
String.format(
|
||||
"[GltfPipeline] Bone '%s': restQ=(%.3f,%.3f,%.3f,%.3f) animQ=(%.3f,%.3f,%.3f,%.3f) deltaQ=(%.3f,%.3f,%.3f,%.3f)",
|
||||
boneName,
|
||||
restQ.x,
|
||||
restQ.y,
|
||||
restQ.z,
|
||||
restQ.w,
|
||||
animQ.x,
|
||||
animQ.y,
|
||||
animQ.z,
|
||||
animQ.w,
|
||||
deltaQ.x,
|
||||
deltaQ.y,
|
||||
deltaQ.z,
|
||||
deltaQ.w
|
||||
)
|
||||
);
|
||||
|
||||
if (GltfBoneMapper.isLowerBone(boneName)) {
|
||||
convertLowerBone(builder, boneName, deltaQ);
|
||||
@@ -341,7 +423,10 @@ public final class GltfPoseConverter {
|
||||
builder.fullyEnableParts();
|
||||
|
||||
KeyframeAnimation anim = builder.build();
|
||||
LOGGER.debug("[GltfPipeline] Converted glTF animation '{}' to KeyframeAnimation", animName);
|
||||
LOGGER.debug(
|
||||
"[GltfPipeline] Converted glTF animation '{}' to KeyframeAnimation",
|
||||
animName
|
||||
);
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -350,10 +435,15 @@ public final class GltfPoseConverter {
|
||||
* Falls back to rest rotation if the clip is null or has no data for this joint.
|
||||
*/
|
||||
private static Quaternionf getRawAnimQuaternion(
|
||||
GltfData.AnimationClip rawClip, Quaternionf[] rawRestRotations, int jointIndex
|
||||
GltfData.AnimationClip rawClip,
|
||||
Quaternionf[] rawRestRotations,
|
||||
int jointIndex
|
||||
) {
|
||||
if (rawClip != null && jointIndex < rawClip.rotations().length
|
||||
&& rawClip.rotations()[jointIndex] != null) {
|
||||
if (
|
||||
rawClip != null &&
|
||||
jointIndex < rawClip.rotations().length &&
|
||||
rawClip.rotations()[jointIndex] != null
|
||||
) {
|
||||
return rawClip.rotations()[jointIndex][0]; // first frame
|
||||
}
|
||||
return rawRestRotations[jointIndex]; // fallback to rest
|
||||
@@ -361,29 +451,36 @@ public final class GltfPoseConverter {
|
||||
|
||||
private static void convertUpperBone(
|
||||
KeyframeAnimation.AnimationBuilder builder,
|
||||
String boneName, Quaternionf deltaQ
|
||||
String boneName,
|
||||
Quaternionf deltaQ
|
||||
) {
|
||||
// Decompose delta quaternion to Euler ZYX
|
||||
// JOML's getEulerAnglesZYX stores: euler.x = X rotation, euler.y = Y rotation, euler.z = Z rotation
|
||||
// (the "ZYX" refers to rotation ORDER, not storage order)
|
||||
Vector3f euler = new Vector3f();
|
||||
deltaQ.getEulerAnglesZYX(euler);
|
||||
float pitch = euler.x; // X rotation (pitch)
|
||||
float yaw = euler.y; // Y rotation (yaw)
|
||||
float roll = euler.z; // Z rotation (roll)
|
||||
float pitch = euler.x; // X rotation (pitch)
|
||||
float yaw = euler.y; // Y rotation (yaw)
|
||||
float roll = euler.z; // Z rotation (roll)
|
||||
|
||||
LOGGER.debug(String.format(
|
||||
"[GltfPipeline] Upper bone '%s': pitch=%.1f° yaw=%.1f° roll=%.1f°",
|
||||
boneName,
|
||||
Math.toDegrees(pitch),
|
||||
Math.toDegrees(yaw),
|
||||
Math.toDegrees(roll)));
|
||||
LOGGER.debug(
|
||||
String.format(
|
||||
"[GltfPipeline] Upper bone '%s': pitch=%.1f° yaw=%.1f° roll=%.1f°",
|
||||
boneName,
|
||||
Math.toDegrees(pitch),
|
||||
Math.toDegrees(yaw),
|
||||
Math.toDegrees(roll)
|
||||
)
|
||||
);
|
||||
|
||||
// Get the StateCollection for this body part
|
||||
String animPart = GltfBoneMapper.getAnimPartName(boneName);
|
||||
if (animPart == null) return;
|
||||
|
||||
KeyframeAnimation.StateCollection part = getPartByName(builder, animPart);
|
||||
KeyframeAnimation.StateCollection part = getPartByName(
|
||||
builder,
|
||||
animPart
|
||||
);
|
||||
if (part == null) return;
|
||||
|
||||
part.pitch.addKeyFrame(0, pitch, Ease.CONSTANT);
|
||||
@@ -393,12 +490,12 @@ public final class GltfPoseConverter {
|
||||
|
||||
private static void convertLowerBone(
|
||||
KeyframeAnimation.AnimationBuilder builder,
|
||||
String boneName, Quaternionf deltaQ
|
||||
String boneName,
|
||||
Quaternionf deltaQ
|
||||
) {
|
||||
// Extract bend angle and axis from the delta quaternion
|
||||
float angle = 2.0f * (float) Math.acos(
|
||||
Math.min(1.0, Math.abs(deltaQ.w))
|
||||
);
|
||||
float angle =
|
||||
2.0f * (float) Math.acos(Math.min(1.0, Math.abs(deltaQ.w)));
|
||||
|
||||
// Determine bend direction from axis
|
||||
float bendDirection = 0.0f;
|
||||
@@ -411,11 +508,14 @@ public final class GltfPoseConverter {
|
||||
angle = -angle;
|
||||
}
|
||||
|
||||
LOGGER.debug(String.format(
|
||||
"[GltfPipeline] Lower bone '%s': bendAngle=%.1f° bendDir=%.1f°",
|
||||
boneName,
|
||||
Math.toDegrees(angle),
|
||||
Math.toDegrees(bendDirection)));
|
||||
LOGGER.debug(
|
||||
String.format(
|
||||
"[GltfPipeline] Lower bone '%s': bendAngle=%.1f° bendDir=%.1f°",
|
||||
boneName,
|
||||
Math.toDegrees(angle),
|
||||
Math.toDegrees(bendDirection)
|
||||
)
|
||||
);
|
||||
|
||||
// Apply bend to the upper bone's StateCollection
|
||||
String upperBone = GltfBoneMapper.getUpperBoneFor(boneName);
|
||||
@@ -424,7 +524,10 @@ public final class GltfPoseConverter {
|
||||
String animPart = GltfBoneMapper.getAnimPartName(upperBone);
|
||||
if (animPart == null) return;
|
||||
|
||||
KeyframeAnimation.StateCollection part = getPartByName(builder, animPart);
|
||||
KeyframeAnimation.StateCollection part = getPartByName(
|
||||
builder,
|
||||
animPart
|
||||
);
|
||||
if (part == null || !part.isBendable) return;
|
||||
|
||||
part.bend.addKeyFrame(0, angle, Ease.CONSTANT);
|
||||
@@ -432,7 +535,8 @@ public final class GltfPoseConverter {
|
||||
}
|
||||
|
||||
private static KeyframeAnimation.StateCollection getPartByName(
|
||||
KeyframeAnimation.AnimationBuilder builder, String name
|
||||
KeyframeAnimation.AnimationBuilder builder,
|
||||
String name
|
||||
) {
|
||||
return switch (name) {
|
||||
case "head" -> builder.head;
|
||||
@@ -461,17 +565,32 @@ public final class GltfPoseConverter {
|
||||
* @param partsWithKeyframes parts that received actual animation data from the GLB
|
||||
*/
|
||||
private static void enableSelectiveParts(
|
||||
KeyframeAnimation.AnimationBuilder builder,
|
||||
Set<String> ownedParts, Set<String> enabledParts,
|
||||
Set<String> partsWithKeyframes) {
|
||||
String[] allParts = {"head", "body", "rightArm", "leftArm", "rightLeg", "leftLeg"};
|
||||
KeyframeAnimation.AnimationBuilder builder,
|
||||
Set<String> ownedParts,
|
||||
Set<String> enabledParts,
|
||||
Set<String> partsWithKeyframes
|
||||
) {
|
||||
String[] allParts = {
|
||||
"head",
|
||||
"body",
|
||||
"rightArm",
|
||||
"leftArm",
|
||||
"rightLeg",
|
||||
"leftLeg",
|
||||
};
|
||||
for (String partName : allParts) {
|
||||
KeyframeAnimation.StateCollection part = getPartByName(builder, partName);
|
||||
KeyframeAnimation.StateCollection part = getPartByName(
|
||||
builder,
|
||||
partName
|
||||
);
|
||||
if (part != null) {
|
||||
if (ownedParts.contains(partName)) {
|
||||
// Always enable owned parts — the item controls these bones
|
||||
part.fullyEnablePart(false);
|
||||
} else if (enabledParts.contains(partName) && partsWithKeyframes.contains(partName)) {
|
||||
} else if (
|
||||
enabledParts.contains(partName) &&
|
||||
partsWithKeyframes.contains(partName)
|
||||
) {
|
||||
// Free part WITH keyframes: enable so the GLB animation drives it
|
||||
part.fullyEnablePart(false);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user