Files
TiedUp-/src/main/java/com/tiedup/remake/client/gltf/GltfBoneMapper.java

136 lines
4.0 KiB
Java

package com.tiedup.remake.client.gltf;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;
/**
* Maps glTF bone names to Minecraft HumanoidModel parts.
* Handles upper bones (full rotation) and lower bones (bend only).
*/
@OnlyIn(Dist.CLIENT)
public final class GltfBoneMapper {
/** Maps glTF bone name -> MC model part field name */
private static final Map<String, String> BONE_TO_PART = new HashMap<>();
/** Lower bones that represent bend (elbow/knee) */
private static final Set<String> LOWER_BONES = Set.of(
"leftLowerArm",
"rightLowerArm",
"leftLowerLeg",
"rightLowerLeg"
);
/** Maps lower bone name -> corresponding upper bone name */
private static final Map<String, String> LOWER_TO_UPPER = Map.of(
"leftLowerArm",
"leftUpperArm",
"rightLowerArm",
"rightUpperArm",
"leftLowerLeg",
"leftUpperLeg",
"rightLowerLeg",
"rightUpperLeg"
);
static {
BONE_TO_PART.put("body", "body");
BONE_TO_PART.put("torso", "body");
BONE_TO_PART.put("head", "head");
BONE_TO_PART.put("leftUpperArm", "leftArm");
BONE_TO_PART.put("leftLowerArm", "leftArm");
BONE_TO_PART.put("rightUpperArm", "rightArm");
BONE_TO_PART.put("rightLowerArm", "rightArm");
BONE_TO_PART.put("leftUpperLeg", "leftLeg");
BONE_TO_PART.put("leftLowerLeg", "leftLeg");
BONE_TO_PART.put("rightUpperLeg", "rightLeg");
BONE_TO_PART.put("rightLowerLeg", "rightLeg");
}
private GltfBoneMapper() {}
/**
* Get the ModelPart corresponding to a glTF bone name.
*
* @param model the HumanoidModel
* @param boneName glTF bone name
* @return the ModelPart, or null if not mapped
*/
public static ModelPart getModelPart(
HumanoidModel<?> model,
String boneName
) {
String partName = BONE_TO_PART.get(boneName);
if (partName == null) return null;
return switch (partName) {
case "body" -> model.body;
case "head" -> model.head;
case "leftArm" -> model.leftArm;
case "rightArm" -> model.rightArm;
case "leftLeg" -> model.leftLeg;
case "rightLeg" -> model.rightLeg;
default -> null;
};
}
/**
* Check if this bone represents a lower segment (bend: elbow/knee).
*/
public static boolean isLowerBone(String boneName) {
return LOWER_BONES.contains(boneName);
}
/**
* Get the upper bone name for a given lower bone.
* Returns null if not a lower bone.
*/
public static String getUpperBoneFor(String lowerBoneName) {
return LOWER_TO_UPPER.get(lowerBoneName);
}
/**
* Get the PlayerAnimator part name for a glTF bone.
* Both glTF and PlayerAnimator use "body" for the torso part.
*/
public static String getAnimPartName(String boneName) {
String partName = BONE_TO_PART.get(boneName);
if (partName == null) return null;
return partName;
}
/**
* Check if a bone name is known/mapped.
*/
public static boolean isKnownBone(String boneName) {
return BONE_TO_PART.containsKey(boneName);
}
/**
* Get all known bone names for validation/suggestion purposes.
*/
public static Set<String> knownBoneNames() {
return BONE_TO_PART.keySet();
}
/**
* Suggest a known bone name for a case-insensitive match.
* Returns null if no case-insensitive match is found.
*/
@Nullable
public static String suggestBoneName(String unknownBone) {
for (String known : BONE_TO_PART.keySet()) {
if (known.equalsIgnoreCase(unknownBone)) {
return known;
}
}
return null;
}
}