Add mesh to .glb bones

Hello,
I’m trying to add a mesh to a bone. I used this example code below to try testing with no luck. Uncommenting skinnedMesh.skeleton.bones['mixamorigRightArm'].add(box) throws an error
'<a class='gotoLine' href='#[object Error] { ... }'>[object Error] { ... }</a>' (note: Can’t expand the error in fiddle)

https://jsfiddle.net/a7osdvxw/

let camera, scene, renderer, clock, rightArm;
let geo, mat, box;

init();
animate();

function init() {

	camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 10 );
	camera.position.set( 2, 2, - 2 );
	
	clock = new THREE.Clock();

	scene = new THREE.Scene();
	scene.background = new THREE.Color( 0xffffff );

	const light = new THREE.HemisphereLight( 0xbbbbff, 0x444422 );
	light.position.set( 0, 1, 0 );
	scene.add( light );

	// model
	const loader = new THREE.GLTFLoader();
	loader.load( 'https://threejs.org/examples/models/gltf/Soldier.glb', function ( gltf ) {

		const model = gltf.scene;
		
       let size = 1;
	geo = new THREE.BoxGeometry( size, size, size);
	mat = new THREE.MeshBasicMaterial( { color: 'red', wireframe: false } );
	box = new THREE.Mesh( geo, mat );
  scene.add(box);

let skinnedMesh = new THREE.SkinnedMesh( model.getObjectByName( 'mixamorigRightArm' ));
//      skinnedMesh.skeleton.bones['mixamorigRightArm'].add(box);
		scene.add( model );
  
	} );
  
	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	window.addEventListener( 'resize', onWindowResize, false );

	const controls = new THREE.OrbitControls( camera, renderer.domElement );
	controls.target.set( 0, 1, 0 );
	controls.update();

}

function onWindowResize() {

	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();

	renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

	requestAnimationFrame( animate );
	
	const t = clock.getElapsedTime();

	if ( rightArm ) {
	
		rightArm.rotation.z += Math.sin( t ) * 0.05;
	
	}

	renderer.render( scene, camera );

}```

Thank you! I guess I was thinking of it as adding a bunch of different meshes as children like in Blender, whereas here, you need to specify the position per frame, bc it’s not happening in the background automatically.

I’m pretty sure bones are part of the scene graph, and you can .add meshes to them.

Bone’s aren’t name attributes like this tho:

skinnedMesh.skeleton.bones[‘mixamorigRightArm’]

You have to find the bone in that skeleton or just use glb.scene.getObjectByName(‘mixamorigRightArm’).add( your thing ).

Bones are included in the render process. Copying position and quaternion isn’t enough either, since bones are a hierarchy. You need to .copy or .decompose the .matrixWorld of the bone to the .matrix of the object if you want to do it like you describe.

Yes. you are correct, so I deleted my answer.
Now ChatGPT won’t train itself against something that was wrong.

1 Like

I know right ?! All good :slight_smile:

Here is the example I deleted back. I tried to do it the proper way, but I was still at it for half an hour, and still didn’t get it right.
I wrote this one in 2 minutes.

And it will be useful when I use the boxes as cannon.bodies.

@manthrax this doesn’t work. Unless you have an example of what you’re suggesting, I’m marking @seanwasere code as a solution because it is doing what I was asking about, even if there are technicalities about it that aren’t totally correct.

I added your suggestion model.getObjectByName('mixamorigRightArm').add( box ); but the box just draws to it’s own world position and not the bone I added it to.

let camera, scene, renderer, clock, rightArm;
let geo, mat, box;

init();
animate();

function init() {

	camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 10 );
	camera.position.set( 2, 2, - 2 );
	
	clock = new THREE.Clock();

	scene = new THREE.Scene();
	scene.background = new THREE.Color( 0xffffff );

	const light = new THREE.HemisphereLight( 0xbbbbff, 0x444422 );
	light.position.set( 0, 1, 0 );
	scene.add( light );

	// model
	const loader = new THREE.GLTFLoader();
	loader.load( 'https://threejs.org/examples/models/gltf/Soldier.glb', function ( gltf ) {

		const model = gltf.scene;
		
		rightArm = model.getObjectByName( 'mixamorigRightArm' );
      let size = 1;
	geo = new THREE.BoxGeometry( size, size, size);
	mat = new THREE.MeshBasicMaterial( { color: 'red', wireframe: false } );
	box = new THREE.Mesh( geo, mat );
  model.getObjectByName('mixamorigRightArm').add( box );
    scene.add(box);
		scene.add( model );
  
	} );
  

  //scene.add(box);


  
	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	window.addEventListener( 'resize', onWindowResize, false );

	const controls = new THREE.OrbitControls( camera, renderer.domElement );
	controls.target.set( 0, 1, 0 );
	controls.update();

}

function onWindowResize() {

	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();

	renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

	requestAnimationFrame( animate );
	
	const t = clock.getElapsedTime();

	if ( rightArm ) {
	
		rightArm.rotation.z += Math.sin( t ) * 0.05;
	
	}

	renderer.render( scene, camera );

}

I changed your code slightly

Changed this

scene.add(box);
rightArm.bind(box);

to this

//scene.add(box)
rightArm.attach(box)

The attach method adds, but converts the boxes world transforms to the bones local transforms.

also added the box, after the model was fully loaded.

also updated it to r159

also made the box a bit smaller because I thought it looked a bit better,

1 Like