three.js 骨骼动画的clone

     three.js中普通Mesh的clone没有任何问题,但是牵扯到SkinnedMesh的clone,不是很好处理,而且3ds max和Maya当前无法直接导出多个动画(这意味着动画不是在同一时间线上)到一个文件中。

    这篇文章将主要讨论如何clone SkinnedMesh 和如何 将3ds max中导出的骨骼动画进行剪辑提取不同的动画切片。

 

1、  首先看看如何clone SkinnedMesh, 3ds max中导出的ShinnedMesh的结构一般是 一个Group节点下面包含一个Bone和一个SkinnedMesh,可以先按照这个结构来推导其他结构。

  • Group中包含的信息是Bone 和 SkinnedMesh
  • Group中的Bone是一个骨骼的树结构的根节点,每一根骨骼都有名称,骨骼动画会根据这个名称找到骨骼,然后变换骨骼矩阵。
  • ShinnedMesh中存储的信息是 Geometry,Material, Skelen。
  • Geometry内部会包含 骨骼索引 和 骨骼权重信息,Geometry可以在各个ShinnedMesh中共享。
  • Material信息中与骨骼相关的信息是skinning,这个没有什么可解释的。
  • Skelen信息是骨骼动画的关键,Skelen中会存储bones信息,这个bones是所有骨骼的数组,注意这个数组是有顺序的,这个顺序
  • 是与Geometry中的骨骼索引信息对应的,不能错乱。

具体的clone代码如下:


//将多有的骨骼节点存储到一个数组中
function getBoneArray(bone, bonearr){
    for(let i=0; i{
			newArr.push(bonearr[item.name]);
		});

        //绑定骨骼,和offsetMatrix(世界坐标系初始姿态)
		skinmesh.bind(new THREE.Skeleton(newArr, object.children[1].boneInverses));
        //将skinnedMesh添加到新对象当中
		obj.add(skinmesh);
        //修改位置和缩放
		obj.position.set(i*10,0,0);
		obj.scale.set(10,10,10);
        //添加到场景中
		scene.add( obj );
	}

} );

2、3ds max和Maya当前无法直接导出多个动画,所有的动画都在一个时间线上,如何分离动画切片,这个要明白动画切片即AnimationClip的结构。

  • AnimationClip包含name(动画名称)、duration(动画总时长) 、tracks(关键帧轨道)
  • name 的用处是AnimationMixer.clipAction(clip, optionalRoot),第一个参数可以使AnimationClip 的name。
  • duration 动画总时长
  • tracks是一个数组,存储的是所有骨骼节点在运动过程中对应的位移、旋转、颜色对应的关键帧,一个骨骼可以使移动、旋转等,一个位移或者旋转会在关键中持续存在。轨迹包括BooleanKeyframeTrack、ColorKeyframeTrack、ColorKeyframeTrack、NumberKeyframeTrack、QuaternionKeyframeTrack、StringKeyframeTrack、VectorKeyframeTrack,每一个轨迹都有一个名称,这个名称和骨骼是对应的,例如leg.position,其中leg对应的是骨骼的名称,position代表的是骨骼的位置变化。

具体的代码如下:

let timeNode = [0, 0.3, 0.52, 1.0];               //假设将骨骼动画分成3个动画切片
let duration = object.animations[ 0 ].duration;   //骨骼动画的总时长
let animations = [];                              //保存动画切片

for(let n=0; n{

	let values = [];
	let times = [];
	let step = 0;
	
	if(item.ValueTypeName =='vector')                        //关键帧轨迹是那种类型
		step = 3;                                            //这种类型的分量有几个
	else if(item.ValueTypeName =='quaternion')               //关键帧轨迹是那种类型
		step = 4;                                            //这种类型的分量有几个

	let length = item.times.length;                          //关键帧的时间点
	for(let j=0; j

最后动画的播放代码:

let mixer = new THREE.AnimationMixer();                  //动画混合器
var action = mixer.clipAction( animations[ 0 ],  obj);   //创建一个活动动画(动画可以在多个对象之间共享)    
action.play();                                           //设置播放

其中Bone的克隆代码如下:

function Bone() {
		Object3D.call( this );
		this.type = 'Bone';
	}
	Bone.prototype = Object.assign( Object.create( Object3D.prototype ), {
		constructor: Bone,
		isBone: true,
		clone: function () {
			let bone = new Bone( );
			bone.copy( this );
			return bone;
		},
	} );

完整代码

function getBoneArray(bone, bonearr){
    for(let i=0; i{
	
			let values = [];
			let times = [];
			let step = 0;
			
			if(item.ValueTypeName =='vector')
				step = 3;
			else if(item.ValueTypeName =='quaternion')
				step = 4;

			let length = item.times.length;
			for(let j=0; j{
			newArr.push(bonearr[item.name]);
		});

		skinmesh.bind(new THREE.Skeleton(newArr, object.children[1].boneInverses));
		obj.add(skinmesh);
		

		var quat = new THREE.Quaternion();
		quat.setFromUnitVectors(new THREE.Vector3(0, 1, 0), new THREE.Vector3(1, 1, -1));
		obj.rotation.setFromQuaternion(quat);
		obj.position.set(i*10,0,0);
		obj.scale.set(10,10,10);
		scene.add( obj );
	
		action = mixer.clipAction( animations[ 1 ],  obj);
		action.play();
	}
	g_mixer.push(mixer);
	
} );

 

你可能感兴趣的:(three.js)