Skip to content

Commit ae85118

Browse files
author
Avaer Kazmer
committed
Add Rig.js automated bones binding
1 parent 62807ae commit ae85118

File tree

1 file changed

+228
-33
lines changed

1 file changed

+228
-33
lines changed

vrarmik/Rig.js

Lines changed: 228 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,7 @@ const poses = {
1111
};
1212

1313
class Rig {
14-
constructor(model, {boneMappings}) {
15-
const modelBones = {
16-
Hips: null,
17-
Spine: null,
18-
Chest: null,
19-
Neck: null,
20-
Head: null,
21-
Eye_L: null,
22-
Eye_R: null,
23-
24-
Left_shoulder: null,
25-
Left_arm: null,
26-
Left_elbow: null,
27-
Left_wrist: null,
28-
Left_leg: null,
29-
Left_knee: null,
30-
Left_ankle: null,
31-
32-
Right_shoulder: null,
33-
Right_arm: null,
34-
Right_elbow: null,
35-
Right_wrist: null,
36-
Right_leg: null,
37-
Right_knee: null,
38-
Right_ankle: null,
39-
};
40-
this.modelBones = modelBones;
41-
14+
constructor(model) {
4215
model.updateMatrixWorld(true);
4316
let skeleton;
4417
model.traverse(o => {
@@ -50,7 +23,229 @@ class Rig {
5023
skeleton = o.skeleton;
5124
}
5225
o.bind(skeleton);
26+
}
27+
});
5328

29+
const tailBones = skeleton.bones.filter(bone => bone.children.length === 0);
30+
const _findClosestParentBone = (bone, pred) => {
31+
for (; bone; bone = bone.parent) {
32+
if (pred(bone)) {
33+
return bone;
34+
}
35+
}
36+
return null;
37+
};
38+
const _findFurthestParentBone = (bone, pred) => {
39+
let result = null;
40+
for (; bone; bone = bone.parent) {
41+
if (pred(bone)) {
42+
result = bone;
43+
}
44+
}
45+
return result;
46+
};
47+
const _distanceToParentBone = (bone, parentBone) => {
48+
for (let i = 0; bone; bone = bone.parent, i++) {
49+
if (bone === parentBone) {
50+
return i;
51+
}
52+
}
53+
return Infinity;
54+
};
55+
const _traverseChild = (bone, distance) => {
56+
if (distance <= 0) {
57+
return bone;
58+
} else {
59+
for (let i = 0; i < bone.children.length; i++) {
60+
const child = bone.children[i];
61+
const subchild = _traverseChild(child, distance - 1);
62+
if (subchild !== null) {
63+
return subchild;
64+
}
65+
}
66+
return null;
67+
}
68+
};
69+
const _countCharacters = (name, regex) => {
70+
let result = 0;
71+
for (let i = 0; i < name.length; i++) {
72+
if (regex.test(name[i])) {
73+
result++;
74+
}
75+
}
76+
return result;
77+
};
78+
const _findEye = left => {
79+
const regexp = left ? /l/i : /r/i;
80+
const eyeBones = tailBones.map(tailBone => {
81+
const eyeBone = _findFurthestParentBone(tailBone, bone => /eye/i.test(bone.name) && regexp.test(bone.name.replace(/eye/gi, '')));
82+
if (eyeBone) {
83+
return eyeBone;
84+
} else {
85+
return null;
86+
}
87+
}).filter(spec => spec).sort((a, b) => {
88+
const aName = a.name.replace(/shoulder/gi, '');
89+
const aLeftBalance = _countCharacters(aName, /l/i) - _countCharacters(aName, /r/i);
90+
const bName = b.name.replace(/shoulder/gi, '');
91+
const bLeftBalance = _countCharacters(bName, /l/i) - _countCharacters(bName, /r/i);
92+
if (!left) {
93+
return aLeftBalance - bLeftBalance;
94+
} else {
95+
return bLeftBalance - aLeftBalance;
96+
}
97+
});
98+
const eyeBone = eyeBones.length > 0 ? eyeBones[0] : null;
99+
if (eyeBone) {
100+
return eyeBone;
101+
} else {
102+
return null;
103+
}
104+
};
105+
const _findHips = () => {
106+
return skeleton.bones.find(bone => /hip/i.test(bone.name));
107+
};
108+
const _findSpine = (chest, hips) => {
109+
for (let bone = chest; bone; bone = bone.parent) {
110+
if (bone.parent === hips) {
111+
return bone;
112+
}
113+
}
114+
return null;
115+
};
116+
const _findShoulder = left => {
117+
const regexp = left ? /l/i : /r/i;
118+
const shoulderBones = tailBones.map(tailBone => {
119+
const shoulderBone = _findClosestParentBone(tailBone, bone => /shoulder/i.test(bone.name) && regexp.test(bone.name.replace(/shoulder/gi, '')));
120+
if (shoulderBone) {
121+
const distance = _distanceToParentBone(tailBone, shoulderBone);
122+
if (distance >= 3) {
123+
return {
124+
bone: shoulderBone,
125+
distance,
126+
};
127+
} else {
128+
return null;
129+
}
130+
} else {
131+
return null;
132+
}
133+
}).filter(spec => spec).sort((a, b) => {
134+
const diff = b.distance - a.distance;
135+
if (diff !== 0) {
136+
return diff;
137+
} else {
138+
const aName = a.bone.name.replace(/shoulder/gi, '');
139+
const aLeftBalance = _countCharacters(aName, /l/i) - _countCharacters(aName, /r/i);
140+
const bName = b.bone.name.replace(/shoulder/gi, '');
141+
const bLeftBalance = _countCharacters(bName, /l/i) - _countCharacters(bName, /r/i);
142+
if (!left) {
143+
return aLeftBalance - bLeftBalance;
144+
} else {
145+
return bLeftBalance - aLeftBalance;
146+
}
147+
}
148+
});
149+
const shoulderBone = shoulderBones.length > 0 ? shoulderBones[0].bone : null;
150+
if (shoulderBone) {
151+
return shoulderBone;
152+
} else {
153+
return null;
154+
}
155+
};
156+
const _findHand = shoulderBone => _traverseChild(shoulderBone, 3);
157+
const _findFoot = left => {
158+
const regexp = left ? /l/i : /r/i;
159+
const legBones = tailBones.map(tailBone => {
160+
const legBone = _findFurthestParentBone(tailBone, bone => /leg/i.test(bone.name) && regexp.test(bone.name.replace(/leg/gi, '')));
161+
if (legBone) {
162+
const distance = _distanceToParentBone(tailBone, legBone);
163+
if (distance >= 2) {
164+
return {
165+
bone: legBone,
166+
distance,
167+
};
168+
} else {
169+
return null;
170+
}
171+
} else {
172+
return null;
173+
}
174+
}).filter(spec => spec).sort((a, b) => {
175+
const diff = b.distance - a.distance;
176+
if (diff !== 0) {
177+
return diff;
178+
} else {
179+
const aName = a.bone.name.replace(/leg/gi, '');
180+
const aLeftBalance = _countCharacters(aName, /l/i) - _countCharacters(aName, /r/i);
181+
const bName = b.bone.name.replace(/leg/gi, '');
182+
const bLeftBalance = _countCharacters(bName, /l/i) - _countCharacters(bName, /r/i);
183+
if (!left) {
184+
return aLeftBalance - bLeftBalance;
185+
} else {
186+
return bLeftBalance - aLeftBalance;
187+
}
188+
}
189+
});
190+
const legBone = legBones.length > 0 ? legBones[0].bone : null;
191+
if (legBone) {
192+
const footBone = _traverseChild(legBone, 2);
193+
return footBone;
194+
} else {
195+
return null;
196+
}
197+
};
198+
const Eye_L = _findEye(true);
199+
const Eye_R = _findEye(false);
200+
const Head = Eye_L.parent;
201+
const Neck = Head.parent;
202+
const Chest = Neck.parent;
203+
const Hips = _findHips();
204+
const Spine = _findSpine(Chest, Hips);
205+
const Left_shoulder = _findShoulder(true);
206+
const Left_wrist = _findHand(Left_shoulder);
207+
const Left_elbow = Left_wrist.parent;
208+
const Left_arm = Left_elbow.parent;
209+
const Right_shoulder = _findShoulder(false);
210+
const Right_wrist = _findHand(Right_shoulder);
211+
const Right_elbow = Right_wrist.parent;
212+
const Right_arm = Right_elbow.parent;
213+
const Left_ankle = _findFoot(true);
214+
const Left_knee = Left_ankle.parent;
215+
const Left_leg = Left_knee.parent;
216+
const Right_ankle = _findFoot(false);
217+
const Right_knee = Right_ankle.parent;
218+
const Right_leg = Right_knee.parent;
219+
// console.log('got left hand', {leftEye, rightEye, head, neck, chest, hips, leftHand, leftLowerArm, leftUpperArm, rightHand, rightLowerArm, rightUpperArm, leftFoot, leftKnee, leftLeg, rightFoot, rightKnee, rightLeg});
220+
const modelBones = {
221+
Hips,
222+
Spine,
223+
Chest,
224+
Neck,
225+
Head,
226+
Eye_L,
227+
Eye_R,
228+
229+
Left_shoulder,
230+
Left_arm,
231+
Left_elbow,
232+
Left_wrist,
233+
Left_leg,
234+
Left_knee,
235+
Left_ankle,
236+
237+
Right_shoulder,
238+
Right_arm,
239+
Right_elbow,
240+
Right_wrist,
241+
Right_leg,
242+
Right_knee,
243+
Right_ankle,
244+
};
245+
this.modelBones = modelBones;
246+
247+
/* model.traverse(o => {
248+
if (o.isSkinnedMesh) {
54249
for (const k in modelBones) {
55250
if (!modelBones[k]) {
56251
const userlandBoneName = boneMappings[k];
@@ -59,7 +254,7 @@ class Rig {
59254
}
60255
}
61256
}
62-
});
257+
}); */
63258

64259
const eyeDirection = modelBones.Eye_L.getWorldPosition(new Vector3()).sub(modelBones.Head.getWorldPosition(new Vector3()));
65260
const flipZ = eyeDirection.z < 0;
@@ -82,16 +277,16 @@ class Rig {
82277
});
83278
if (!flipZ) {
84279
['Left_arm', 'Right_arm'].forEach((name, i) => {
85-
const userlandBoneName = boneMappings[name];
86-
const bone = skeleton.bones.find(bone => bone.name === userlandBoneName);
280+
// const userlandBoneName = boneMappings[name];
281+
const bone = modelBones[name];// skeleton.bones.find(bone => bone.name === userlandBoneName);
87282
if (bone) {
88283
bone.quaternion.premultiply(new Quaternion().setFromAxisAngle(new Vector3(0, 0, 1), (i === 0 ? 1 : -1) * Math.PI*0.25));
89284
}
90285
});
91286
} else {
92287
['Hips'].forEach(name => {
93-
const userlandBoneName = boneMappings[name];
94-
const bone = skeleton.bones.find(bone => bone.name === userlandBoneName);
288+
// const userlandBoneName = boneMappings[name];
289+
const bone = modelBones[name];// skeleton.bones.find(bone => bone.name === userlandBoneName);
95290
if (bone) {
96291
bone.quaternion.premultiply(new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), Math.PI));
97292
}

0 commit comments

Comments
 (0)