136 lines
4.0 KiB
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;
|
|
}
|
|
}
|