Thursday, June 9, 2016

THREE.js - add a THREE.SkinnedMesh to a custom skeleton structure

Leave a Comment

I'm trying to define a model of the human body in THREE.js using the classes THREE.Bone, THREE.Skeleton and THREE.SkinnedMesh.

I defined a custom skeleton structure made of 12 body parts, each of which is a THREE.Bone instance, and used the .add() method to define parent / child relationships among them. Finally, I created a standard THREE.Object3D as the body root that is parent of the full skeleton.

Posting only part of the structure for conciseness:

// create person object var body_root = new THREE.Object3D()  // create torso var torso  = new THREE.Bone(); torso.id   = 1; torso.name = "torso"; x_t = 0; y_t = 0; z_t = 0; torso.position.set(x_t,y_t,z_t); x_alpha = 0 * Math.PI; y_alpha = 0 * Math.PI; z_alpha = 0 * Math.PI; torso.rotation.set(x_alpha,y_alpha,z_alpha);  // create right arm var right_arm = new THREE.Bone(); right_arm.id   = 2; right_arm.name = "right_arm"; x_t = -TORSO_WIDTH / 2; y_t = TORSO_HEIGHT; z_t = 0; right_arm.position.set(x_t,y_t,z_t); x_alpha = 0 * Math.PI; y_alpha = 0 * Math.PI; z_alpha = 0 * Math.PI; right_arm.rotation.set(x_alpha,y_alpha,z_alpha);  // add right_arm as child of torso torso.add( right_arm ); 

This works just fine, and after loading the page I can access the model and traverse it correctly through the console.

However, when I try to render the skeleton in the scene things get tricky.

1. How can I add a THREE.SkinnedMesh for a custom skeleton structure?

In the documentation (check source code) a CylinderGeometry and a SkinnedMesh are created for all the bones jointly

var geometry = new THREE.CylinderGeometry( 5, 5, 5, 5, 15, 5, 30 ); var mesh = THREE.SkinnedMesh( geometry, material ); 

and then the bone structure is binded:

// See example from THREE.Skeleton for the armSkeleton var rootBone = armSkeleton.bones[ 0 ]; mesh.add( rootBone ); // Bind the skeleton to the mesh mesh.bind( armSkeleton ); 

This works perfectly for the simple example in the documentation (5 bones each one parent of the next one), but how can I adapt this example to a more complex structure in which some bones have multiple children? And how can I implement bones with different geometry? For instance I would like to implement joints like shoulder and elbow with a sphere for which I can only change rotation and body parts like arm and forearm with cylinders having different base radius.

Is it possible to define a SkinnedMesh for each joint independently and for a more complex structure than the one in the example? If so how do you link them all together?

2. Can I add a THREE.SkeletonHelper to the scene without defining a skinned mesh but using only the bones?

Since I don't know the answer to question 1 I decided to simply try to render the skeleton structure. This is done in other examples (such as this one) by creating a THREE.SkeletonHelper instance and adding that to the scene.

I tried passing the body_root variable (instead of the SkinnedMesh) to the constructor and the helper is created, but not rendered.

helper = new THREE.SkeletonHelper( body_root ); helper.material.linewidth = 3; scene.add( helper ); 

In order to visualize the helper it needs to be binded to a mesh, even if the mesh is not added directly to the scene, i.e. adding the line mesh.bind(armSkeleton) visualizes the skeleton helper.

Is this possible at all to visualize the skeleton helper without defining a mesh? If so how?

NOTE on question 2:

I believe this should be possible, since the THREE.SkeletonHelper class defines internally its own geometry and material, so it should be possible to render it without needing the mesh of the skeleton:

THREE.SkeletonHelper = function ( object ) {      this.bones = this.getBoneList( object );      var geometry = new THREE.Geometry();      for ( var i = 0; i < this.bones.length; i ++ ) {          var bone = this.bones[ i ];          if ( bone.parent instanceof THREE.Bone ) {              geometry.vertices.push( new THREE.Vector3() );             geometry.vertices.push( new THREE.Vector3() );             geometry.colors.push( new THREE.Color( 0, 0, 1 ) );             geometry.colors.push( new THREE.Color( 0, 1, 0 ) );          }      }      geometry.dynamic = true;      var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );      THREE.LineSegments.call( this, geometry, material );      this.root = object;      this.matrix = object.matrixWorld;     this.matrixAutoUpdate = false;      this.update();  }; 

0 Answers

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment