Skip to content

Commit c702462

Browse files
committed
Apply spring bone joint stiffness and gravity in world space
1 parent 82b7c5f commit c702462

File tree

1 file changed

+34
-76
lines changed

1 file changed

+34
-76
lines changed

packages/three-vrm-springbone/src/VRMSpringBoneJoint.ts

+34-76
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as THREE from 'three';
2-
import { mat4InvertCompat } from './utils/mat4InvertCompat';
32
import { Matrix4InverseCache } from './utils/Matrix4InverseCache';
43
import type { VRMSpringBoneColliderGroup } from './VRMSpringBoneColliderGroup';
54
import type { VRMSpringBoneJointSettings } from './VRMSpringBoneJointSettings';
@@ -13,26 +12,18 @@ const IDENTITY_MATRIX4 = new THREE.Matrix4();
1312
// 計算中の一時保存用変数(一度インスタンスを作ったらあとは使い回す)
1413
const _v3A = new THREE.Vector3();
1514
const _v3B = new THREE.Vector3();
16-
const _v3C = new THREE.Vector3();
1715

1816
/**
1917
* A temporary variable which is used in `update`
2018
*/
2119
const _worldSpacePosition = new THREE.Vector3();
2220

23-
/**
24-
* A temporary variable which is used in `update`
25-
*/
26-
const _centerSpacePosition = new THREE.Vector3();
27-
2821
/**
2922
* A temporary variable which is used in `update`
3023
*/
3124
const _nextTail = new THREE.Vector3();
3225

33-
const _quatA = new THREE.Quaternion();
3426
const _matA = new THREE.Matrix4();
35-
const _matB = new THREE.Matrix4();
3627

3728
/**
3829
* A class represents a single joint of a spring bone.
@@ -204,7 +195,7 @@ export class VRMSpringBoneJoint {
204195
}
205196

206197
// copy the child position to tails
207-
const matrixWorldToCenter = this._getMatrixWorldToCenter(_matA);
198+
const matrixWorldToCenter = this._getMatrixWorldToCenter();
208199
this.bone.localToWorld(this._currentTail.copy(this._initialLocalChildPosition)).applyMatrix4(matrixWorldToCenter);
209200
this._prevTail.copy(this._currentTail);
210201

@@ -224,7 +215,7 @@ export class VRMSpringBoneJoint {
224215
this.bone.matrixWorld.multiplyMatrices(this._parentMatrixWorld, this.bone.matrix);
225216

226217
// Apply updated position to tail states
227-
const matrixWorldToCenter = this._getMatrixWorldToCenter(_matA);
218+
const matrixWorldToCenter = this._getMatrixWorldToCenter();
228219
this.bone.localToWorld(this._currentTail.copy(this._initialLocalChildPosition)).applyMatrix4(matrixWorldToCenter);
229220
this._prevTail.copy(this._currentTail);
230221
}
@@ -241,64 +232,42 @@ export class VRMSpringBoneJoint {
241232
// Update the _worldSpaceBoneLength
242233
this._calcWorldSpaceBoneLength();
243234

244-
// Get bone position in center space
245-
_worldSpacePosition.setFromMatrixPosition(this.bone.matrixWorld);
246-
let matrixWorldToCenter = this._getMatrixWorldToCenter(_matA);
247-
_centerSpacePosition.copy(_worldSpacePosition).applyMatrix4(matrixWorldToCenter);
248-
const quatWorldToCenter = _quatA.setFromRotationMatrix(matrixWorldToCenter);
249-
250-
// Get parent matrix in center space
251-
const centerSpaceParentMatrix = _matB.copy(matrixWorldToCenter).multiply(this._parentMatrixWorld);
252-
253-
// Get boneAxis in center space
254-
const centerSpaceBoneAxis = _v3B
235+
// Get boneAxis in world space
236+
const worldSpaceBoneAxis = _v3B
255237
.copy(this._boneAxis)
256-
.applyMatrix4(this._initialLocalMatrix)
257-
.applyMatrix4(centerSpaceParentMatrix)
258-
.sub(_centerSpacePosition)
259-
.normalize();
260-
261-
// gravity in center space
262-
const centerSpaceGravity = _v3C.copy(this.settings.gravityDir).applyQuaternion(quatWorldToCenter).normalize();
263-
264-
const matrixCenterToWorld = this._getMatrixCenterToWorld(_matA);
238+
.transformDirection(this._initialLocalMatrix)
239+
.transformDirection(this._parentMatrixWorld);
265240

266241
// verlet積分で次の位置を計算
267242
_nextTail
243+
// Determine inertia in center space
268244
.copy(this._currentTail)
269-
.add(
270-
_v3A
271-
.copy(this._currentTail)
272-
.sub(this._prevTail)
273-
.multiplyScalar(1 - this.settings.dragForce),
274-
) // 前フレームの移動を継続する(減衰もあるよ)
275-
.add(_v3A.copy(centerSpaceBoneAxis).multiplyScalar(this.settings.stiffness * delta)) // 親の回転による子ボーンの移動目標
276-
.add(_v3A.copy(centerSpaceGravity).multiplyScalar(this.settings.gravityPower * delta)) // 外力による移動量
277-
.applyMatrix4(matrixCenterToWorld); // tailをworld spaceに戻す
245+
.add(_v3A.subVectors(this._currentTail, this._prevTail).multiplyScalar(1 - this.settings.dragForce)) // 前フレームの移動を継続する(減衰もあるよ)
246+
// Convert center space to world space
247+
.applyMatrix4(this._getMatrixCenterToWorld()) // tailをworld spaceに戻す
248+
// Apply stiffness and gravity in world space
249+
.addScaledVector(worldSpaceBoneAxis, this.settings.stiffness * delta) // 親の回転による子ボーンの移動目標
250+
.addScaledVector(this.settings.gravityDir, this.settings.gravityPower * delta); // 外力による移動量
278251

279252
// normalize bone length
253+
_worldSpacePosition.setFromMatrixPosition(this.bone.matrixWorld);
280254
_nextTail.sub(_worldSpacePosition).normalize().multiplyScalar(this._worldSpaceBoneLength).add(_worldSpacePosition);
281255

282256
// Collisionで移動
283257
this._collision(_nextTail);
284258

285259
// update prevTail and currentTail
286-
matrixWorldToCenter = this._getMatrixWorldToCenter(_matA);
287-
288260
this._prevTail.copy(this._currentTail);
289-
this._currentTail.copy(_v3A.copy(_nextTail).applyMatrix4(matrixWorldToCenter));
261+
this._currentTail.copy(_nextTail).applyMatrix4(this._getMatrixWorldToCenter());
290262

291263
// Apply rotation, convert vector3 thing into actual quaternion
292264
// Original UniVRM is doing center unit calculus at here but we're gonna do this on local unit
293-
const worldSpaceInitialMatrixInv = mat4InvertCompat(
294-
_matA.copy(this._parentMatrixWorld).multiply(this._initialLocalMatrix),
295-
);
296-
const applyRotation = _quatA.setFromUnitVectors(
297-
this._boneAxis,
298-
_v3A.copy(_nextTail).applyMatrix4(worldSpaceInitialMatrixInv).normalize(),
299-
);
300-
301-
this.bone.quaternion.copy(this._initialLocalRotation).multiply(applyRotation);
265+
const worldSpaceInitialMatrixInv = _matA
266+
.multiplyMatrices(this._parentMatrixWorld, this._initialLocalMatrix)
267+
.invert();
268+
this.bone.quaternion
269+
.setFromUnitVectors(this._boneAxis, _v3A.copy(_nextTail).applyMatrix4(worldSpaceInitialMatrixInv).normalize())
270+
.premultiply(this._initialLocalRotation);
302271

303272
// We need to update its matrixWorld manually, since we tweaked the bone by our hand
304273
this.bone.updateMatrix();
@@ -311,19 +280,22 @@ export class VRMSpringBoneJoint {
311280
* @param tail The tail you want to process
312281
*/
313282
private _collision(tail: THREE.Vector3): void {
314-
this.colliderGroups.forEach((colliderGroup) => {
315-
colliderGroup.colliders.forEach((collider) => {
283+
for (let cg = 0; cg < this.colliderGroups.length; cg++) {
284+
for (let c = 0; c < this.colliderGroups[cg].colliders.length; c++) {
285+
const collider = this.colliderGroups[cg].colliders[c];
316286
const dist = collider.shape.calculateCollision(collider.matrixWorld, tail, this.settings.hitRadius, _v3A);
317287

318288
if (dist < 0.0) {
319289
// hit
320-
tail.add(_v3A.multiplyScalar(-dist));
290+
tail.addScaledVector(_v3A, -dist);
321291

322292
// normalize bone length
323-
tail.sub(_worldSpacePosition).normalize().multiplyScalar(this._worldSpaceBoneLength).add(_worldSpacePosition);
293+
tail.sub(_worldSpacePosition);
294+
const length = tail.length();
295+
tail.multiplyScalar(this._worldSpaceBoneLength / length).add(_worldSpacePosition);
324296
}
325-
});
326-
});
297+
}
298+
}
327299
}
328300

329301
/**
@@ -345,29 +317,15 @@ export class VRMSpringBoneJoint {
345317

346318
/**
347319
* Create a matrix that converts center space into world space.
348-
* @param target Target matrix
349320
*/
350-
private _getMatrixCenterToWorld(target: THREE.Matrix4): THREE.Matrix4 {
351-
if (this._center) {
352-
target.copy(this._center.matrixWorld);
353-
} else {
354-
target.identity();
355-
}
356-
357-
return target;
321+
private _getMatrixCenterToWorld(): THREE.Matrix4 {
322+
return this._center ? this._center.matrixWorld : IDENTITY_MATRIX4;
358323
}
359324

360325
/**
361326
* Create a matrix that converts world space into center space.
362-
* @param target Target matrix
363327
*/
364-
private _getMatrixWorldToCenter(target: THREE.Matrix4): THREE.Matrix4 {
365-
if (this._center) {
366-
target.copy((this._center.userData.inverseCacheProxy as Matrix4InverseCache).inverse);
367-
} else {
368-
target.identity();
369-
}
370-
371-
return target;
328+
private _getMatrixWorldToCenter(): THREE.Matrix4 {
329+
return this._center ? (this._center.userData.inverseCacheProxy as Matrix4InverseCache).inverse : IDENTITY_MATRIX4;
372330
}
373331
}

0 commit comments

Comments
 (0)