更新于 

Cloning And Animating Models Loaded From glTF Files. 克隆动画GLTF模型

要实现克隆动画模型的功能,需要注意以下几点:

  • 克隆GLTF模型需要使用SkeletonUtils库,进行特殊模型复制
  • 对于每一个模型都要创建单独的mixer控制动画

在读取模型时需要将其作为模具全局存储:

1
2
3
4
5
6
7
const gltfLoader = new GLTFLoader()
const shibaInuURL = new URL('../asserts/ShibaInu.gltf', import.meta.url)
let shibaInuModel
gltfLoader.load(shibaInuURL.href, gltf => {
gltf.scene.scale.set(0.3, 0.3, 0.3)
shibaInuModel = gltf
},undefined,err=>console.error(err))

鼠标点击平面时,使用SkeletonUtils克隆,
并创建相应mixer动画控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const mousePosition = new THREE.Vector2()
const raycaster = new THREE.Raycaster()
const objects = []
const mixers = []
const isExist = () => objects.find(obj => obj.position.x === highlight.position.x && obj.position.z === highlight.position.z)

function getInterSections(e) {
mousePosition.x = e.offsetX / window.innerWidth * 2 - 1
mousePosition.y = 1 - e.offsetY / window.innerHeight * 2
raycaster.setFromCamera(mousePosition, camera)
return raycaster.intersectObject(plane)
}

window.onmousemove = e => {
let interSections = getInterSections(e)
if (interSections[0]) {
let position = interSections[0].point.floor().addScalar(0.5)
highlight.position.set(position.x, 0, position.z)
highlight.material.color.set(isExist() ? 0xff0000 : 0xffffff)
}
}

window.onmousedown = e => {
if (isExist() || !getInterSections(e)[0]) return
let obj = SkeletonUtils.clone(shibaInuModel.scene)
scene.add(obj)
obj.position.set(highlight.position.x, 0, highlight.position.z)
objects.push(obj)

const mixer = new THREE.AnimationMixer(obj)
const action = mixer.clipAction(
THREE.AnimationClip.findByName(shibaInuModel.animations, 'Idle_2')
)
action.play()
mixers.push(mixer)

highlight.material.color.set(0xff0000)
}

在动画中对每一个mixer进行更新:

1
2
3
4
5
6
7
8
9
10
let clock = new THREE.Clock()
function animate(time) {
const delta = clock.getDelta()
mixers.forEach(mixer => {
mixer.update(delta)
})
highlight.material.opacity = Math.abs(Math.sin(time/120))
renderer.render(scene, camera);
}