Hi Deltakosh. I don’t have a code prepared for sandbox. I can post a code how I’m testing the workflow. Nothing worked for me at the moment. I will try to prepare it for sandbox.
This is RAW code that I use for testing:
private importGLBAndAnimateBones(assetName: string): void {
// Make sure double_test.glb is in src/assets/
SceneLoader.ImportMesh(
null,
'/assets/',
assetName,
this.scene,
(meshes, particleSystems, skeletons) => {
if (skeletons.length > 0 && meshes.length > 0) {
const originalSkeleton = skeletons[0];
// Find a Mesh (not AbstractMesh) with the skeleton
const originalMesh = meshes.find(m => m instanceof Mesh && m.skeleton === originalSkeleton) as Mesh || meshes.find(m => m instanceof Mesh) as Mesh;
if (!originalMesh) return;
// Create a new skeleton with the same structure as the original
const newSkeleton = new Skeleton('NewSkeleton', 'newSkeletonId', this.scene);
const newSkeleton2 = new Skeleton('NewSkeleton', 'newSkeletonId', this.scene);
const newSkeleton3 = new Skeleton('NewSkeleton', 'newSkeletonId', this.scene);
const boneMap: Bone[] = [];
const boneMap2: Bone[] = [];
const boneMap3: Bone[] = [];
// Clone bone hierarchy
for (const bone of originalSkeleton.bones) {
const parent = bone.getParent();
const parentIndex = parent ? originalSkeleton.bones.indexOf(parent) : -1;
const newParent = parentIndex >= 0 ? boneMap[parentIndex] : null;
const newParent2 = parentIndex >= 0 ? boneMap[parentIndex] : null;
const newParent3 = parentIndex >= 0 ? boneMap[parentIndex] : null;
const newBone = new Bone(`${bone.name}_new`, newSkeleton, newParent, bone.getLocalMatrix().clone(), bone.getBindMatrix().clone(), bone.getRestMatrix().clone());
const newBone2 = new Bone(`${bone.name}_new2`, newSkeleton2, newParent2, bone.getLocalMatrix().clone(), bone.getBindMatrix().clone(), bone.getRestMatrix().clone());
const newBone3 = new Bone(`${bone.name}_new3`, newSkeleton3, newParent3, bone.getLocalMatrix().clone(), bone.getBindMatrix().clone(), bone.getRestMatrix().clone());
boneMap.push(newBone);
boneMap2.push(newBone2);
boneMap3.push(newBone3);
}
// Assign the new skeleton to the mesh (skin data is already in the mesh)
originalMesh.skeleton = newSkeleton;
// Show the bones visually for the new skeleton
const skeletonViewer = new SkeletonViewer(newSkeleton, originalMesh, this.scene);
skeletonViewer.isEnabled = true;
// save new vertices positions to list
const vertexData = VertexData.ExtractFromMesh(originalMesh);
const originalPositions = vertexData.positions ? Array.from(vertexData.positions) : [];
// Animate the new bones to test skinning
this.scene.onBeforeRenderObservable.add(() => {
if (newSkeleton.bones.length > 0) {
newSkeleton.bones[1].setPosition(new Vector3(0.1, 0, 0), Space.BONE);
newSkeleton.bones[2].setPosition(new Vector3(0.1, 0, 0), Space.BONE);
newSkeleton.bones[3].setPosition(new Vector3(0.1, 0, 0), Space.BONE);
newSkeleton.bones[4].setPosition(new Vector3(0.0, 0, 0), Space.BONE);
}
});
// Animate the new bones to test skinning
this.scene.onBeforeRenderObservable.add(() => {
if (newSkeleton2.bones.length > 0) {
newSkeleton2.bones[1].setPosition(new Vector3(0.1, 0, 0), Space.BONE);
newSkeleton2.bones[2].setPosition(new Vector3(0.15, 0, 0), Space.BONE);
newSkeleton2.bones[3].setPosition(new Vector3(0.01, 0, 0), Space.BONE);
newSkeleton2.bones[4].setPosition(new Vector3(0.05, 0, 0), Space.BONE);
}
});
// Animate the new bones to test skinning
this.scene.onBeforeRenderObservable.add(() => {
if (newSkeleton3.bones.length > 0) {
newSkeleton3.bones[1].setPosition(new Vector3(1, 0, 0), Space.BONE);
newSkeleton3.bones[2].setPosition(new Vector3(0.25, 0, 0), Space.BONE);
newSkeleton3.bones[3].setPosition(new Vector3(0.1, 0, 0), Space.BONE);
newSkeleton3.bones[4].setPosition(new Vector3(0.1, 0, 0), Space.BONE);
}
});
// Log original positions for comparison
console.log("Original Positions:", originalPositions);
const uniquePositions = new Set<number>();
const movedPositions = originalPositions.map((pos, index) => {
if (uniquePositions.has(pos)) {
return null; // Skip duplicate positions
}
uniquePositions.add(pos);
return {
x: pos,
y: 0, // Use normals for Y position, default to 0 if undefined
z: 0 // Use normals for Z position, default to 0 if undefined
};
}).filter(Boolean);
// Log moved positions for comparison
console.log("Moved Positions:", movedPositions);
// Compare original and moved positions
const arePositionsSame = JSON.stringify(originalPositions) === JSON.stringify(movedPositions);
console.log("Are positions the same?", arePositionsSame ? "Yes" : "No");
//clone the mesh to create a new instance
const clonedMesh = originalMesh.clone('clonedMesh');
clonedMesh.skeleton = newSkeleton; // Assign the new skeleton to the cloned mesh
clonedMesh.position = new Vector3(2, 0, 0); // Move cloned mesh to the right
this.scene.addMesh(clonedMesh);
//bake vertex animation
// After moving bones and before baking
// clonedMesh.skeleton.prepare();
// clonedMesh.computeWorldMatrix(true);
// Get deformed positions
// const deformedPositions = this.getCurrentDeformedPositions(clonedMesh);
// Bake: overwrite mesh vertex data with deformed positions
// if (deformedPositions) {
// clonedMesh.setVerticesData(VertexBuffer.PositionKind, deformedPositions);
// clonedMesh.refreshBoundingInfo();
// }
//get moved positions after bone movement
//clone the mesh to create a new instance
const clonedMesh2 = originalMesh.clone('clonedMesh');
// Set a different position for the second cloned mesh
clonedMesh2.name = 'clonedMesh2';
clonedMesh2.skeleton = newSkeleton2; // Assign the new skeleton to the cloned mesh
clonedMesh2.position = new Vector3(2, 4, 0); // Move cloned mesh to the right
this.scene.addMesh(clonedMesh2);
const clonedMesh3 = originalMesh.clone('clonedMesh3');
//clonedMesh3.skeleton = newSkeleton3; // Assign the new skeleton to the cloned mesh
clonedMesh3.position = new Vector3(-4, 2, 0); // Move cloned mesh to the right
this.scene.addMesh(clonedMesh3);
/* // Usage:
const result = this.mergeSkeletonsComplete([clonedMesh, clonedMesh2], this.scene);
if (!result){
const finalMesh = result!.mergedMesh;
const finalSkeleton = result!.skeleton;
const boneMapping = result!.boneMapping;
// Log the final merged mesh and skeleton
console.log("Final Merged Mesh:", finalMesh);
console.log("Final Skeleton:", finalSkeleton);
console.log("Bone Mapping:", boneMapping);
// Add merged mesh to the scene
if (finalMesh) {
finalMesh.material = new StandardMaterial('finalMat', this.scene); // Assign a visible material
finalMesh.name = 'finalMergedMesh';
finalMesh.position = new Vector3(0, 0, -2); // Center the merged mesh
finalMesh.scaling = new Vector3(10.5, 10.5, 10.5); // Scale down for visibility
this.camera.setTarget(finalMesh.position); // Focus camera
console.log("Mesh exists, vertices:", finalMesh.getTotalVertices());
this.scene.addMesh(finalMesh);
}
}*/
}
}
);
}