feat(gltf): accept custom bones — remove 11-bone filter, simplify joint remap

This commit is contained in:
NotEvil
2026-04-17 01:40:30 +02:00
parent d7f8bf6c72
commit 8af58b5dd5

View File

@@ -103,10 +103,8 @@ public final class GlbParser {
JsonObject skin = skins.get(0).getAsJsonObject(); JsonObject skin = skins.get(0).getAsJsonObject();
JsonArray skinJoints = skin.getAsJsonArray("joints"); JsonArray skinJoints = skin.getAsJsonArray("joints");
// Filter skin joints to only include known deforming bones // Accept all skin joints (no filtering — custom bones are supported)
List<Integer> filteredJointNodes = new ArrayList<>(); List<Integer> allJointNodes = new ArrayList<>();
int[] skinJointRemap = new int[skinJoints.size()]; // old skin index -> new filtered index
java.util.Arrays.fill(skinJointRemap, -1);
for (int j = 0; j < skinJoints.size(); j++) { for (int j = 0; j < skinJoints.size(); j++) {
int nodeIdx = skinJoints.get(j).getAsInt(); int nodeIdx = skinJoints.get(j).getAsInt();
JsonObject node = nodes.get(nodeIdx).getAsJsonObject(); JsonObject node = nodes.get(nodeIdx).getAsJsonObject();
@@ -117,19 +115,25 @@ public final class GlbParser {
if (name.contains("|")) { if (name.contains("|")) {
name = name.substring(name.lastIndexOf('|') + 1); name = name.substring(name.lastIndexOf('|') + 1);
} }
if (GltfBoneMapper.isKnownBone(name)) { // Log info for non-MC bones
skinJointRemap[j] = filteredJointNodes.size(); if (!GltfBoneMapper.isKnownBone(name)) {
filteredJointNodes.add(nodeIdx); String suggestion = GltfBoneMapper.suggestBoneName(name);
} else { if (suggestion != null) {
LOGGER.debug( LOGGER.warn(
"[GltfPipeline] Skipping non-deforming bone: '{}' (node {})", "[GltfPipeline] Unknown bone '{}' in {} — did you mean '{}'? (treated as custom bone)",
name, name, debugName, suggestion
nodeIdx );
); } else {
LOGGER.info(
"[GltfPipeline] Custom bone '{}' in {} (will follow parent hierarchy in rest pose)",
name, debugName
);
}
} }
allJointNodes.add(nodeIdx);
} }
int jointCount = filteredJointNodes.size(); int jointCount = allJointNodes.size();
String[] jointNames = new String[jointCount]; String[] jointNames = new String[jointCount];
int[] parentJointIndices = new int[jointCount]; int[] parentJointIndices = new int[jointCount];
Quaternionf[] restRotations = new Quaternionf[jointCount]; Quaternionf[] restRotations = new Quaternionf[jointCount];
@@ -139,14 +143,14 @@ public final class GlbParser {
int[] nodeToJoint = new int[nodes.size()]; int[] nodeToJoint = new int[nodes.size()];
java.util.Arrays.fill(nodeToJoint, -1); java.util.Arrays.fill(nodeToJoint, -1);
for (int j = 0; j < jointCount; j++) { for (int j = 0; j < jointCount; j++) {
int nodeIdx = filteredJointNodes.get(j); int nodeIdx = allJointNodes.get(j);
nodeToJoint[nodeIdx] = j; nodeToJoint[nodeIdx] = j;
} }
// Read joint names, rest pose, and build parent mapping // Read joint names, rest pose, and build parent mapping
java.util.Arrays.fill(parentJointIndices, -1); java.util.Arrays.fill(parentJointIndices, -1);
for (int j = 0; j < jointCount; j++) { for (int j = 0; j < jointCount; j++) {
int nodeIdx = filteredJointNodes.get(j); int nodeIdx = allJointNodes.get(j);
JsonObject node = nodes.get(nodeIdx).getAsJsonObject(); JsonObject node = nodes.get(nodeIdx).getAsJsonObject();
String rawName = node.has("name") String rawName = node.has("name")
@@ -200,7 +204,6 @@ public final class GlbParser {
} }
// -- Inverse Bind Matrices -- // -- Inverse Bind Matrices --
// IBM accessor is indexed by original skin joint order, so we pick the filtered entries
Matrix4f[] inverseBindMatrices = new Matrix4f[jointCount]; Matrix4f[] inverseBindMatrices = new Matrix4f[jointCount];
if (skin.has("inverseBindMatrices")) { if (skin.has("inverseBindMatrices")) {
int ibmAccessor = skin.get("inverseBindMatrices").getAsInt(); int ibmAccessor = skin.get("inverseBindMatrices").getAsInt();
@@ -210,12 +213,9 @@ public final class GlbParser {
binData, binData,
ibmAccessor ibmAccessor
); );
for (int origJ = 0; origJ < skinJoints.size(); origJ++) { for (int j = 0; j < jointCount; j++) {
int newJ = skinJointRemap[origJ]; inverseBindMatrices[j] = new Matrix4f();
if (newJ >= 0) { inverseBindMatrices[j].set(ibmData, j * 16);
inverseBindMatrices[newJ] = new Matrix4f();
inverseBindMatrices[newJ].set(ibmData, origJ * 16);
}
} }
} else { } else {
for (int j = 0; j < jointCount; j++) { for (int j = 0; j < jointCount; j++) {
@@ -344,15 +344,10 @@ public final class GlbParser {
binData, binData,
attributes.get("JOINTS_0").getAsInt() attributes.get("JOINTS_0").getAsInt()
); );
// Remap vertex joint indices from original skin order to filtered order // No remap needed — all joints are kept, indices match directly.
// Guard against out-of-range joint indices.
for (int i = 0; i < primJoints.length; i++) { for (int i = 0; i < primJoints.length; i++) {
int origIdx = primJoints[i]; if (primJoints[i] < 0 || primJoints[i] >= jointCount) {
if (origIdx >= 0 && origIdx < skinJointRemap.length) {
primJoints[i] =
skinJointRemap[origIdx] >= 0
? skinJointRemap[origIdx]
: 0;
} else {
primJoints[i] = 0; primJoints[i] = 0;
} }
} }