forked from webaverse/app
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cloneObject3D.js
126 lines (93 loc) · 3.36 KB
/
cloneObject3D.js
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { PropertyBinding, AnimationClip } from 'three';
// Modified version of Don McCurdy's AnimationUtils.clone
// https://github.com/mrdoob/three.js/pull/14494
function parallelTraverse(a, b, callback) {
callback(a, b);
for (let i = 0; i < a.children.length; i++) {
parallelTraverse(a.children[i], b.children[i], callback);
}
}
// Supports the following PropertyBinding path formats:
// uuid.propertyName
// uuid.propertyName[propertyIndex]
// uuid.objectName[objectIndex].propertyName[propertyIndex]
// Does not support property bindings that use object3D names or parent nodes
function cloneKeyframeTrack(sourceKeyframeTrack, cloneUUIDLookup) {
const { nodeName: uuid, objectName, objectIndex, propertyName, propertyIndex } = PropertyBinding.parseTrackName(
sourceKeyframeTrack.name
);
let path = "";
if (uuid !== undefined) {
const clonedUUID = cloneUUIDLookup.get(uuid);
if (clonedUUID === undefined) {
throw new Error(`Error cloning model. Could not find KeyframeTrack target with uuid: "${uuid}"`);
}
path += clonedUUID;
}
if (objectName !== undefined) {
path += "." + objectName;
}
if (objectIndex !== undefined) {
path += "[" + objectIndex + "]";
}
if (propertyName !== undefined) {
path += "." + propertyName;
}
if (propertyIndex !== undefined) {
path += "[" + propertyIndex + "]";
}
const clonedKeyframeTrack = sourceKeyframeTrack.clone();
clonedKeyframeTrack.name = path;
return clonedKeyframeTrack;
}
function cloneAnimationClip(sourceAnimationClip, cloneUUIDLookup) {
const clonedTracks = sourceAnimationClip.tracks.map(keyframeTrack =>
cloneKeyframeTrack(keyframeTrack, cloneUUIDLookup)
);
return new AnimationClip(sourceAnimationClip.name, sourceAnimationClip.duration, clonedTracks);
}
export default function cloneObject3D(source, preserveUUIDs) {
const cloneLookup = new Map();
const cloneUUIDLookup = new Map();
const clone = source.clone();
parallelTraverse(source, clone, (sourceNode, clonedNode) => {
cloneLookup.set(sourceNode, clonedNode);
});
source.traverse(sourceNode => {
const clonedNode = cloneLookup.get(sourceNode);
if (!clonedNode) {
throw new Error(
`Couldn't find the cloned node for ${sourceNode.nodeName || sourceNode.type} "${sourceNode.name}"`
);
}
if (preserveUUIDs) {
clonedNode.uuid = sourceNode.uuid;
}
cloneUUIDLookup.set(sourceNode.uuid, clonedNode.uuid);
});
source.traverse(sourceNode => {
const clonedNode = cloneLookup.get(sourceNode);
if (!clonedNode) {
return;
}
if (sourceNode.animations) {
clonedNode.animations = sourceNode.animations.map(animationClip =>
cloneAnimationClip(animationClip, cloneUUIDLookup)
);
}
if (sourceNode.isMesh && sourceNode.geometry.boundsTree) {
clonedNode.geometry.boundsTree = sourceNode.geometry.boundsTree;
}
if (!sourceNode.isSkinnedMesh) return;
const sourceBones = sourceNode.skeleton.bones;
clonedNode.skeleton = sourceNode.skeleton.clone();
clonedNode.skeleton.bones = sourceBones.map(sourceBone => {
if (!cloneLookup.has(sourceBone)) {
throw new Error("Required bones are not descendants of the given object.");
}
return cloneLookup.get(sourceBone);
});
clonedNode.bind(clonedNode.skeleton, sourceNode.bindMatrix);
});
return clone;
}