Favivon - Correção

This commit is contained in:
2026-05-30 19:59:39 -03:00
parent 76ddaa815d
commit d7dfd221f0
32859 changed files with 5459654 additions and 404 deletions
+57
View File
@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const AnimationClipCreator = {
CreateRotationAnimation(period, axis = "x") {
const times = [0, period], values = [0, 360];
const trackName = ".rotation[" + axis + "]";
const track = new THREE.NumberKeyframeTrack(trackName, times, values);
return new THREE.AnimationClip(null, period, [track]);
},
CreateScaleAxisAnimation(period, axis = "x") {
const times = [0, period], values = [0, 1];
const trackName = ".scale[" + axis + "]";
const track = new THREE.NumberKeyframeTrack(trackName, times, values);
return new THREE.AnimationClip(null, period, [track]);
},
CreateShakeAnimation(duration, shakeScale) {
const times = [], values = [], tmp = new THREE.Vector3();
for (let i = 0; i < duration * 10; i++) {
times.push(i / 10);
tmp.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1).multiply(shakeScale).toArray(values, values.length);
}
const trackName = ".position";
const track = new THREE.VectorKeyframeTrack(trackName, times, values);
return new THREE.AnimationClip(null, duration, [track]);
},
CreatePulsationAnimation(duration, pulseScale) {
const times = [], values = [], tmp = new THREE.Vector3();
for (let i = 0; i < duration * 10; i++) {
times.push(i / 10);
const scaleFactor = Math.random() * pulseScale;
tmp.set(scaleFactor, scaleFactor, scaleFactor).toArray(values, values.length);
}
const trackName = ".scale";
const track = new THREE.VectorKeyframeTrack(trackName, times, values);
return new THREE.AnimationClip(null, duration, [track]);
},
CreateVisibilityAnimation(duration) {
const times = [0, duration / 2, duration], values = [true, false, true];
const trackName = ".visible";
const track = new THREE.BooleanKeyframeTrack(trackName, times, values);
return new THREE.AnimationClip(null, duration, [track]);
},
CreateMaterialColorAnimation(duration, colors) {
const times = [], values = [], timeStep = duration / colors.length;
for (let i = 0; i < colors.length; i++) {
times.push(i * timeStep);
const color = colors[i];
values.push(color.r, color.g, color.b);
}
const trackName = ".material.color";
const track = new THREE.ColorKeyframeTrack(trackName, times, values);
return new THREE.AnimationClip(null, duration, [track]);
}
};
exports.AnimationClipCreator = AnimationClipCreator;
//# sourceMappingURL=AnimationClipCreator.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"AnimationClipCreator.cjs","sources":["../../src/animation/AnimationClipCreator.js"],"sourcesContent":["import {\n AnimationClip,\n BooleanKeyframeTrack,\n ColorKeyframeTrack,\n NumberKeyframeTrack,\n Vector3,\n VectorKeyframeTrack,\n} from 'three'\n\nconst AnimationClipCreator = {\n CreateRotationAnimation(period, axis = 'x') {\n const times = [0, period],\n values = [0, 360]\n\n const trackName = '.rotation[' + axis + ']'\n\n const track = new NumberKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, period, [track])\n },\n\n CreateScaleAxisAnimation(period, axis = 'x') {\n const times = [0, period],\n values = [0, 1]\n\n const trackName = '.scale[' + axis + ']'\n\n const track = new NumberKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, period, [track])\n },\n\n CreateShakeAnimation(duration, shakeScale) {\n const times = [],\n values = [],\n tmp = new Vector3()\n\n for (let i = 0; i < duration * 10; i++) {\n times.push(i / 10)\n\n tmp\n .set(Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0)\n .multiply(shakeScale)\n .toArray(values, values.length)\n }\n\n const trackName = '.position'\n\n const track = new VectorKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n\n CreatePulsationAnimation(duration, pulseScale) {\n const times = [],\n values = [],\n tmp = new Vector3()\n\n for (let i = 0; i < duration * 10; i++) {\n times.push(i / 10)\n\n const scaleFactor = Math.random() * pulseScale\n tmp.set(scaleFactor, scaleFactor, scaleFactor).toArray(values, values.length)\n }\n\n const trackName = '.scale'\n\n const track = new VectorKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n\n CreateVisibilityAnimation(duration) {\n const times = [0, duration / 2, duration],\n values = [true, false, true]\n\n const trackName = '.visible'\n\n const track = new BooleanKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n\n CreateMaterialColorAnimation(duration, colors) {\n const times = [],\n values = [],\n timeStep = duration / colors.length\n\n for (let i = 0; i < colors.length; i++) {\n times.push(i * timeStep)\n\n const color = colors[i]\n values.push(color.r, color.g, color.b)\n }\n\n const trackName = '.material.color'\n\n const track = new ColorKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n}\n\nexport { AnimationClipCreator }\n"],"names":["NumberKeyframeTrack","AnimationClip","Vector3","VectorKeyframeTrack","BooleanKeyframeTrack","ColorKeyframeTrack"],"mappings":";;;AASK,MAAC,uBAAuB;AAAA,EAC3B,wBAAwB,QAAQ,OAAO,KAAK;AAC1C,UAAM,QAAQ,CAAC,GAAG,MAAM,GACtB,SAAS,CAAC,GAAG,GAAG;AAElB,UAAM,YAAY,eAAe,OAAO;AAExC,UAAM,QAAQ,IAAIA,MAAAA,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAIC,MAAAA,cAAc,MAAM,QAAQ,CAAC,KAAK,CAAC;AAAA,EAC/C;AAAA,EAED,yBAAyB,QAAQ,OAAO,KAAK;AAC3C,UAAM,QAAQ,CAAC,GAAG,MAAM,GACtB,SAAS,CAAC,GAAG,CAAC;AAEhB,UAAM,YAAY,YAAY,OAAO;AAErC,UAAM,QAAQ,IAAID,MAAAA,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAIC,MAAAA,cAAc,MAAM,QAAQ,CAAC,KAAK,CAAC;AAAA,EAC/C;AAAA,EAED,qBAAqB,UAAU,YAAY;AACzC,UAAM,QAAQ,CAAE,GACd,SAAS,CAAE,GACX,MAAM,IAAIC,MAAAA,QAAS;AAErB,aAAS,IAAI,GAAG,IAAI,WAAW,IAAI,KAAK;AACtC,YAAM,KAAK,IAAI,EAAE;AAEjB,UACG,IAAI,KAAK,OAAM,IAAK,IAAM,GAAK,KAAK,OAAQ,IAAG,IAAM,GAAK,KAAK,OAAM,IAAK,IAAM,CAAG,EACnF,SAAS,UAAU,EACnB,QAAQ,QAAQ,OAAO,MAAM;AAAA,IACjC;AAED,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAIC,MAAAA,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAIF,MAAAA,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AAAA,EAED,yBAAyB,UAAU,YAAY;AAC7C,UAAM,QAAQ,CAAE,GACd,SAAS,CAAE,GACX,MAAM,IAAIC,MAAAA,QAAS;AAErB,aAAS,IAAI,GAAG,IAAI,WAAW,IAAI,KAAK;AACtC,YAAM,KAAK,IAAI,EAAE;AAEjB,YAAM,cAAc,KAAK,OAAM,IAAK;AACpC,UAAI,IAAI,aAAa,aAAa,WAAW,EAAE,QAAQ,QAAQ,OAAO,MAAM;AAAA,IAC7E;AAED,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAIC,MAAAA,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAIF,MAAAA,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AAAA,EAED,0BAA0B,UAAU;AAClC,UAAM,QAAQ,CAAC,GAAG,WAAW,GAAG,QAAQ,GACtC,SAAS,CAAC,MAAM,OAAO,IAAI;AAE7B,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAIG,MAAAA,qBAAqB,WAAW,OAAO,MAAM;AAE/D,WAAO,IAAIH,MAAAA,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AAAA,EAED,6BAA6B,UAAU,QAAQ;AAC7C,UAAM,QAAQ,CAAE,GACd,SAAS,CAAE,GACX,WAAW,WAAW,OAAO;AAE/B,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,KAAK,IAAI,QAAQ;AAEvB,YAAM,QAAQ,OAAO,CAAC;AACtB,aAAO,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAAA,IACtC;AAED,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAII,MAAAA,mBAAmB,WAAW,OAAO,MAAM;AAE7D,WAAO,IAAIJ,MAAAA,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AACH;;"}
+12
View File
@@ -0,0 +1,12 @@
import { AnimationClip, Vector3 } from 'three'
export interface AnimationClipCreator {
CreateRotationAnimation(period: number, axis: string): AnimationClip
CreateScaleAxisAnimation(period: number, axis: string): AnimationClip
CreateShakeAnimation(duration: number, shakeScale: Vector3): AnimationClip
CreatePulsationAnimation(duration: number, pulseScale: number): AnimationClip
CreateVisibilityAnimation(duration: number): AnimationClip
CreateMaterialColorAnimation(duration: number, colors: number[]): AnimationClip
}
export const AnimationClipCreator: AnimationClipCreator
+57
View File
@@ -0,0 +1,57 @@
import { NumberKeyframeTrack, AnimationClip, Vector3, VectorKeyframeTrack, BooleanKeyframeTrack, ColorKeyframeTrack } from "three";
const AnimationClipCreator = {
CreateRotationAnimation(period, axis = "x") {
const times = [0, period], values = [0, 360];
const trackName = ".rotation[" + axis + "]";
const track = new NumberKeyframeTrack(trackName, times, values);
return new AnimationClip(null, period, [track]);
},
CreateScaleAxisAnimation(period, axis = "x") {
const times = [0, period], values = [0, 1];
const trackName = ".scale[" + axis + "]";
const track = new NumberKeyframeTrack(trackName, times, values);
return new AnimationClip(null, period, [track]);
},
CreateShakeAnimation(duration, shakeScale) {
const times = [], values = [], tmp = new Vector3();
for (let i = 0; i < duration * 10; i++) {
times.push(i / 10);
tmp.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1).multiply(shakeScale).toArray(values, values.length);
}
const trackName = ".position";
const track = new VectorKeyframeTrack(trackName, times, values);
return new AnimationClip(null, duration, [track]);
},
CreatePulsationAnimation(duration, pulseScale) {
const times = [], values = [], tmp = new Vector3();
for (let i = 0; i < duration * 10; i++) {
times.push(i / 10);
const scaleFactor = Math.random() * pulseScale;
tmp.set(scaleFactor, scaleFactor, scaleFactor).toArray(values, values.length);
}
const trackName = ".scale";
const track = new VectorKeyframeTrack(trackName, times, values);
return new AnimationClip(null, duration, [track]);
},
CreateVisibilityAnimation(duration) {
const times = [0, duration / 2, duration], values = [true, false, true];
const trackName = ".visible";
const track = new BooleanKeyframeTrack(trackName, times, values);
return new AnimationClip(null, duration, [track]);
},
CreateMaterialColorAnimation(duration, colors) {
const times = [], values = [], timeStep = duration / colors.length;
for (let i = 0; i < colors.length; i++) {
times.push(i * timeStep);
const color = colors[i];
values.push(color.r, color.g, color.b);
}
const trackName = ".material.color";
const track = new ColorKeyframeTrack(trackName, times, values);
return new AnimationClip(null, duration, [track]);
}
};
export {
AnimationClipCreator
};
//# sourceMappingURL=AnimationClipCreator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"AnimationClipCreator.js","sources":["../../src/animation/AnimationClipCreator.js"],"sourcesContent":["import {\n AnimationClip,\n BooleanKeyframeTrack,\n ColorKeyframeTrack,\n NumberKeyframeTrack,\n Vector3,\n VectorKeyframeTrack,\n} from 'three'\n\nconst AnimationClipCreator = {\n CreateRotationAnimation(period, axis = 'x') {\n const times = [0, period],\n values = [0, 360]\n\n const trackName = '.rotation[' + axis + ']'\n\n const track = new NumberKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, period, [track])\n },\n\n CreateScaleAxisAnimation(period, axis = 'x') {\n const times = [0, period],\n values = [0, 1]\n\n const trackName = '.scale[' + axis + ']'\n\n const track = new NumberKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, period, [track])\n },\n\n CreateShakeAnimation(duration, shakeScale) {\n const times = [],\n values = [],\n tmp = new Vector3()\n\n for (let i = 0; i < duration * 10; i++) {\n times.push(i / 10)\n\n tmp\n .set(Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0)\n .multiply(shakeScale)\n .toArray(values, values.length)\n }\n\n const trackName = '.position'\n\n const track = new VectorKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n\n CreatePulsationAnimation(duration, pulseScale) {\n const times = [],\n values = [],\n tmp = new Vector3()\n\n for (let i = 0; i < duration * 10; i++) {\n times.push(i / 10)\n\n const scaleFactor = Math.random() * pulseScale\n tmp.set(scaleFactor, scaleFactor, scaleFactor).toArray(values, values.length)\n }\n\n const trackName = '.scale'\n\n const track = new VectorKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n\n CreateVisibilityAnimation(duration) {\n const times = [0, duration / 2, duration],\n values = [true, false, true]\n\n const trackName = '.visible'\n\n const track = new BooleanKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n\n CreateMaterialColorAnimation(duration, colors) {\n const times = [],\n values = [],\n timeStep = duration / colors.length\n\n for (let i = 0; i < colors.length; i++) {\n times.push(i * timeStep)\n\n const color = colors[i]\n values.push(color.r, color.g, color.b)\n }\n\n const trackName = '.material.color'\n\n const track = new ColorKeyframeTrack(trackName, times, values)\n\n return new AnimationClip(null, duration, [track])\n },\n}\n\nexport { AnimationClipCreator }\n"],"names":[],"mappings":";AASK,MAAC,uBAAuB;AAAA,EAC3B,wBAAwB,QAAQ,OAAO,KAAK;AAC1C,UAAM,QAAQ,CAAC,GAAG,MAAM,GACtB,SAAS,CAAC,GAAG,GAAG;AAElB,UAAM,YAAY,eAAe,OAAO;AAExC,UAAM,QAAQ,IAAI,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAI,cAAc,MAAM,QAAQ,CAAC,KAAK,CAAC;AAAA,EAC/C;AAAA,EAED,yBAAyB,QAAQ,OAAO,KAAK;AAC3C,UAAM,QAAQ,CAAC,GAAG,MAAM,GACtB,SAAS,CAAC,GAAG,CAAC;AAEhB,UAAM,YAAY,YAAY,OAAO;AAErC,UAAM,QAAQ,IAAI,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAI,cAAc,MAAM,QAAQ,CAAC,KAAK,CAAC;AAAA,EAC/C;AAAA,EAED,qBAAqB,UAAU,YAAY;AACzC,UAAM,QAAQ,CAAE,GACd,SAAS,CAAE,GACX,MAAM,IAAI,QAAS;AAErB,aAAS,IAAI,GAAG,IAAI,WAAW,IAAI,KAAK;AACtC,YAAM,KAAK,IAAI,EAAE;AAEjB,UACG,IAAI,KAAK,OAAM,IAAK,IAAM,GAAK,KAAK,OAAQ,IAAG,IAAM,GAAK,KAAK,OAAM,IAAK,IAAM,CAAG,EACnF,SAAS,UAAU,EACnB,QAAQ,QAAQ,OAAO,MAAM;AAAA,IACjC;AAED,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAI,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAI,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AAAA,EAED,yBAAyB,UAAU,YAAY;AAC7C,UAAM,QAAQ,CAAE,GACd,SAAS,CAAE,GACX,MAAM,IAAI,QAAS;AAErB,aAAS,IAAI,GAAG,IAAI,WAAW,IAAI,KAAK;AACtC,YAAM,KAAK,IAAI,EAAE;AAEjB,YAAM,cAAc,KAAK,OAAM,IAAK;AACpC,UAAI,IAAI,aAAa,aAAa,WAAW,EAAE,QAAQ,QAAQ,OAAO,MAAM;AAAA,IAC7E;AAED,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAI,oBAAoB,WAAW,OAAO,MAAM;AAE9D,WAAO,IAAI,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AAAA,EAED,0BAA0B,UAAU;AAClC,UAAM,QAAQ,CAAC,GAAG,WAAW,GAAG,QAAQ,GACtC,SAAS,CAAC,MAAM,OAAO,IAAI;AAE7B,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAI,qBAAqB,WAAW,OAAO,MAAM;AAE/D,WAAO,IAAI,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AAAA,EAED,6BAA6B,UAAU,QAAQ;AAC7C,UAAM,QAAQ,CAAE,GACd,SAAS,CAAE,GACX,WAAW,WAAW,OAAO;AAE/B,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,KAAK,IAAI,QAAQ;AAEvB,YAAM,QAAQ,OAAO,CAAC;AACtB,aAAO,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAAA,IACtC;AAED,UAAM,YAAY;AAElB,UAAM,QAAQ,IAAI,mBAAmB,WAAW,OAAO,MAAM;AAE7D,WAAO,IAAI,cAAc,MAAM,UAAU,CAAC,KAAK,CAAC;AAAA,EACjD;AACH;"}
+270
View File
@@ -0,0 +1,270 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const _q = /* @__PURE__ */ new THREE.Quaternion();
const _targetPos = /* @__PURE__ */ new THREE.Vector3();
const _targetVec = /* @__PURE__ */ new THREE.Vector3();
const _effectorPos = /* @__PURE__ */ new THREE.Vector3();
const _effectorVec = /* @__PURE__ */ new THREE.Vector3();
const _linkPos = /* @__PURE__ */ new THREE.Vector3();
const _invLinkQ = /* @__PURE__ */ new THREE.Quaternion();
const _linkScale = /* @__PURE__ */ new THREE.Vector3();
const _axis = /* @__PURE__ */ new THREE.Vector3();
const _vector = /* @__PURE__ */ new THREE.Vector3();
const _matrix = /* @__PURE__ */ new THREE.Matrix4();
class CCDIKSolver {
/**
* @param {THREE.SkinnedMesh} mesh
* @param {Array<Object>} iks
*/
constructor(mesh, iks = []) {
this.mesh = mesh;
this.iks = iks;
this._valid();
}
/**
* Update all IK bones.
*
* @return {CCDIKSolver}
*/
update() {
const iks = this.iks;
for (let i = 0, il = iks.length; i < il; i++) {
this.updateOne(iks[i]);
}
return this;
}
/**
* Update one IK bone
*
* @param {Object} ik parameter
* @return {CCDIKSolver}
*/
updateOne(ik) {
const bones = this.mesh.skeleton.bones;
const math = Math;
const effector = bones[ik.effector];
const target = bones[ik.target];
_targetPos.setFromMatrixPosition(target.matrixWorld);
const links = ik.links;
const iteration = ik.iteration !== void 0 ? ik.iteration : 1;
for (let i = 0; i < iteration; i++) {
let rotated = false;
for (let j = 0, jl = links.length; j < jl; j++) {
const link = bones[links[j].index];
if (links[j].enabled === false)
break;
const limitation = links[j].limitation;
const rotationMin = links[j].rotationMin;
const rotationMax = links[j].rotationMax;
link.matrixWorld.decompose(_linkPos, _invLinkQ, _linkScale);
_invLinkQ.invert();
_effectorPos.setFromMatrixPosition(effector.matrixWorld);
_effectorVec.subVectors(_effectorPos, _linkPos);
_effectorVec.applyQuaternion(_invLinkQ);
_effectorVec.normalize();
_targetVec.subVectors(_targetPos, _linkPos);
_targetVec.applyQuaternion(_invLinkQ);
_targetVec.normalize();
let angle = _targetVec.dot(_effectorVec);
if (angle > 1) {
angle = 1;
} else if (angle < -1) {
angle = -1;
}
angle = math.acos(angle);
if (angle < 1e-5)
continue;
if (ik.minAngle !== void 0 && angle < ik.minAngle) {
angle = ik.minAngle;
}
if (ik.maxAngle !== void 0 && angle > ik.maxAngle) {
angle = ik.maxAngle;
}
_axis.crossVectors(_effectorVec, _targetVec);
_axis.normalize();
_q.setFromAxisAngle(_axis, angle);
link.quaternion.multiply(_q);
if (limitation !== void 0) {
let c = link.quaternion.w;
if (c > 1)
c = 1;
const c2 = math.sqrt(1 - c * c);
link.quaternion.set(limitation.x * c2, limitation.y * c2, limitation.z * c2, c);
}
if (rotationMin !== void 0) {
link.rotation.setFromVector3(_vector.setFromEuler(link.rotation).max(rotationMin));
}
if (rotationMax !== void 0) {
link.rotation.setFromVector3(_vector.setFromEuler(link.rotation).min(rotationMax));
}
link.updateMatrixWorld(true);
rotated = true;
}
if (!rotated)
break;
}
return this;
}
/**
* Creates Helper
*
* @return {CCDIKHelper}
*/
createHelper() {
return new CCDIKHelper(this.mesh, this.iks);
}
// private methods
_valid() {
const iks = this.iks;
const bones = this.mesh.skeleton.bones;
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const effector = bones[ik.effector];
const links = ik.links;
let link0, link1;
link0 = effector;
for (let j = 0, jl = links.length; j < jl; j++) {
link1 = bones[links[j].index];
if (link0.parent !== link1) {
console.warn("THREE.CCDIKSolver: bone " + link0.name + " is not the child of bone " + link1.name);
}
link0 = link1;
}
}
}
}
function getPosition(bone, matrixWorldInv) {
return _vector.setFromMatrixPosition(bone.matrixWorld).applyMatrix4(matrixWorldInv);
}
function setPositionOfBoneToAttributeArray(array, index, bone, matrixWorldInv) {
const v = getPosition(bone, matrixWorldInv);
array[index * 3 + 0] = v.x;
array[index * 3 + 1] = v.y;
array[index * 3 + 2] = v.z;
}
class CCDIKHelper extends THREE.Object3D {
constructor(mesh, iks = [], sphereSize = 0.25) {
super();
this.root = mesh;
this.iks = iks;
this.matrix.copy(mesh.matrixWorld);
this.matrixAutoUpdate = false;
this.sphereGeometry = new THREE.SphereGeometry(sphereSize, 16, 8);
this.targetSphereMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(16746632),
depthTest: false,
depthWrite: false,
transparent: true
});
this.effectorSphereMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(8978312),
depthTest: false,
depthWrite: false,
transparent: true
});
this.linkSphereMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color(8947967),
depthTest: false,
depthWrite: false,
transparent: true
});
this.lineMaterial = new THREE.LineBasicMaterial({
color: new THREE.Color(16711680),
depthTest: false,
depthWrite: false,
transparent: true
});
this._init();
}
/**
* Updates IK bones visualization.
*/
updateMatrixWorld(force) {
const mesh = this.root;
if (this.visible) {
let offset = 0;
const iks = this.iks;
const bones = mesh.skeleton.bones;
_matrix.copy(mesh.matrixWorld).invert();
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const targetBone = bones[ik.target];
const effectorBone = bones[ik.effector];
const targetMesh = this.children[offset++];
const effectorMesh = this.children[offset++];
targetMesh.position.copy(getPosition(targetBone, _matrix));
effectorMesh.position.copy(getPosition(effectorBone, _matrix));
for (let j = 0, jl = ik.links.length; j < jl; j++) {
const link = ik.links[j];
const linkBone = bones[link.index];
const linkMesh = this.children[offset++];
linkMesh.position.copy(getPosition(linkBone, _matrix));
}
const line = this.children[offset++];
const array = line.geometry.attributes.position.array;
setPositionOfBoneToAttributeArray(array, 0, targetBone, _matrix);
setPositionOfBoneToAttributeArray(array, 1, effectorBone, _matrix);
for (let j = 0, jl = ik.links.length; j < jl; j++) {
const link = ik.links[j];
const linkBone = bones[link.index];
setPositionOfBoneToAttributeArray(array, j + 2, linkBone, _matrix);
}
line.geometry.attributes.position.needsUpdate = true;
}
}
this.matrix.copy(mesh.matrixWorld);
super.updateMatrixWorld(force);
}
/**
* Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
*/
dispose() {
this.sphereGeometry.dispose();
this.targetSphereMaterial.dispose();
this.effectorSphereMaterial.dispose();
this.linkSphereMaterial.dispose();
this.lineMaterial.dispose();
const children = this.children;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.isLine)
child.geometry.dispose();
}
}
// private method
_init() {
const scope = this;
const iks = this.iks;
function createLineGeometry(ik) {
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array((2 + ik.links.length) * 3);
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
return geometry;
}
function createTargetMesh() {
return new THREE.Mesh(scope.sphereGeometry, scope.targetSphereMaterial);
}
function createEffectorMesh() {
return new THREE.Mesh(scope.sphereGeometry, scope.effectorSphereMaterial);
}
function createLinkMesh() {
return new THREE.Mesh(scope.sphereGeometry, scope.linkSphereMaterial);
}
function createLine(ik) {
return new THREE.Line(createLineGeometry(ik), scope.lineMaterial);
}
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
this.add(createTargetMesh());
this.add(createEffectorMesh());
for (let j = 0, jl = ik.links.length; j < jl; j++) {
this.add(createLinkMesh());
}
this.add(createLine(ik));
}
}
}
exports.CCDIKHelper = CCDIKHelper;
exports.CCDIKSolver = CCDIKSolver;
//# sourceMappingURL=CCDIKSolver.cjs.map
File diff suppressed because one or more lines are too long
+30
View File
@@ -0,0 +1,30 @@
import { Object3D, SkinnedMesh, Vector3 } from 'three'
// tslint:disable-next-line:interface-name
export interface IKS {
effector: number
iteration: number
links: Array<{
enabled: boolean
index: number
limitation?: Vector3
rotationMin?: Vector3
rotationMax?: Vector3
}>
minAngle: number
maxAngle: number
target: number
}
export class CCDIKSolver {
constructor(mesh: SkinnedMesh, iks: IKS[])
update(): this
updateOne(iks: IKS): this
createHelper(): CCDIKHelper
}
export class CCDIKHelper extends Object3D {
constructor(mesh: SkinnedMesh, iks?: IKS[], sphereSize?: number)
dispose(): void
}
+270
View File
@@ -0,0 +1,270 @@
import { Object3D, SphereGeometry, MeshBasicMaterial, Color, LineBasicMaterial, Quaternion, Vector3, Matrix4, Mesh, Line, BufferGeometry, BufferAttribute } from "three";
const _q = /* @__PURE__ */ new Quaternion();
const _targetPos = /* @__PURE__ */ new Vector3();
const _targetVec = /* @__PURE__ */ new Vector3();
const _effectorPos = /* @__PURE__ */ new Vector3();
const _effectorVec = /* @__PURE__ */ new Vector3();
const _linkPos = /* @__PURE__ */ new Vector3();
const _invLinkQ = /* @__PURE__ */ new Quaternion();
const _linkScale = /* @__PURE__ */ new Vector3();
const _axis = /* @__PURE__ */ new Vector3();
const _vector = /* @__PURE__ */ new Vector3();
const _matrix = /* @__PURE__ */ new Matrix4();
class CCDIKSolver {
/**
* @param {THREE.SkinnedMesh} mesh
* @param {Array<Object>} iks
*/
constructor(mesh, iks = []) {
this.mesh = mesh;
this.iks = iks;
this._valid();
}
/**
* Update all IK bones.
*
* @return {CCDIKSolver}
*/
update() {
const iks = this.iks;
for (let i = 0, il = iks.length; i < il; i++) {
this.updateOne(iks[i]);
}
return this;
}
/**
* Update one IK bone
*
* @param {Object} ik parameter
* @return {CCDIKSolver}
*/
updateOne(ik) {
const bones = this.mesh.skeleton.bones;
const math = Math;
const effector = bones[ik.effector];
const target = bones[ik.target];
_targetPos.setFromMatrixPosition(target.matrixWorld);
const links = ik.links;
const iteration = ik.iteration !== void 0 ? ik.iteration : 1;
for (let i = 0; i < iteration; i++) {
let rotated = false;
for (let j = 0, jl = links.length; j < jl; j++) {
const link = bones[links[j].index];
if (links[j].enabled === false)
break;
const limitation = links[j].limitation;
const rotationMin = links[j].rotationMin;
const rotationMax = links[j].rotationMax;
link.matrixWorld.decompose(_linkPos, _invLinkQ, _linkScale);
_invLinkQ.invert();
_effectorPos.setFromMatrixPosition(effector.matrixWorld);
_effectorVec.subVectors(_effectorPos, _linkPos);
_effectorVec.applyQuaternion(_invLinkQ);
_effectorVec.normalize();
_targetVec.subVectors(_targetPos, _linkPos);
_targetVec.applyQuaternion(_invLinkQ);
_targetVec.normalize();
let angle = _targetVec.dot(_effectorVec);
if (angle > 1) {
angle = 1;
} else if (angle < -1) {
angle = -1;
}
angle = math.acos(angle);
if (angle < 1e-5)
continue;
if (ik.minAngle !== void 0 && angle < ik.minAngle) {
angle = ik.minAngle;
}
if (ik.maxAngle !== void 0 && angle > ik.maxAngle) {
angle = ik.maxAngle;
}
_axis.crossVectors(_effectorVec, _targetVec);
_axis.normalize();
_q.setFromAxisAngle(_axis, angle);
link.quaternion.multiply(_q);
if (limitation !== void 0) {
let c = link.quaternion.w;
if (c > 1)
c = 1;
const c2 = math.sqrt(1 - c * c);
link.quaternion.set(limitation.x * c2, limitation.y * c2, limitation.z * c2, c);
}
if (rotationMin !== void 0) {
link.rotation.setFromVector3(_vector.setFromEuler(link.rotation).max(rotationMin));
}
if (rotationMax !== void 0) {
link.rotation.setFromVector3(_vector.setFromEuler(link.rotation).min(rotationMax));
}
link.updateMatrixWorld(true);
rotated = true;
}
if (!rotated)
break;
}
return this;
}
/**
* Creates Helper
*
* @return {CCDIKHelper}
*/
createHelper() {
return new CCDIKHelper(this.mesh, this.iks);
}
// private methods
_valid() {
const iks = this.iks;
const bones = this.mesh.skeleton.bones;
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const effector = bones[ik.effector];
const links = ik.links;
let link0, link1;
link0 = effector;
for (let j = 0, jl = links.length; j < jl; j++) {
link1 = bones[links[j].index];
if (link0.parent !== link1) {
console.warn("THREE.CCDIKSolver: bone " + link0.name + " is not the child of bone " + link1.name);
}
link0 = link1;
}
}
}
}
function getPosition(bone, matrixWorldInv) {
return _vector.setFromMatrixPosition(bone.matrixWorld).applyMatrix4(matrixWorldInv);
}
function setPositionOfBoneToAttributeArray(array, index, bone, matrixWorldInv) {
const v = getPosition(bone, matrixWorldInv);
array[index * 3 + 0] = v.x;
array[index * 3 + 1] = v.y;
array[index * 3 + 2] = v.z;
}
class CCDIKHelper extends Object3D {
constructor(mesh, iks = [], sphereSize = 0.25) {
super();
this.root = mesh;
this.iks = iks;
this.matrix.copy(mesh.matrixWorld);
this.matrixAutoUpdate = false;
this.sphereGeometry = new SphereGeometry(sphereSize, 16, 8);
this.targetSphereMaterial = new MeshBasicMaterial({
color: new Color(16746632),
depthTest: false,
depthWrite: false,
transparent: true
});
this.effectorSphereMaterial = new MeshBasicMaterial({
color: new Color(8978312),
depthTest: false,
depthWrite: false,
transparent: true
});
this.linkSphereMaterial = new MeshBasicMaterial({
color: new Color(8947967),
depthTest: false,
depthWrite: false,
transparent: true
});
this.lineMaterial = new LineBasicMaterial({
color: new Color(16711680),
depthTest: false,
depthWrite: false,
transparent: true
});
this._init();
}
/**
* Updates IK bones visualization.
*/
updateMatrixWorld(force) {
const mesh = this.root;
if (this.visible) {
let offset = 0;
const iks = this.iks;
const bones = mesh.skeleton.bones;
_matrix.copy(mesh.matrixWorld).invert();
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const targetBone = bones[ik.target];
const effectorBone = bones[ik.effector];
const targetMesh = this.children[offset++];
const effectorMesh = this.children[offset++];
targetMesh.position.copy(getPosition(targetBone, _matrix));
effectorMesh.position.copy(getPosition(effectorBone, _matrix));
for (let j = 0, jl = ik.links.length; j < jl; j++) {
const link = ik.links[j];
const linkBone = bones[link.index];
const linkMesh = this.children[offset++];
linkMesh.position.copy(getPosition(linkBone, _matrix));
}
const line = this.children[offset++];
const array = line.geometry.attributes.position.array;
setPositionOfBoneToAttributeArray(array, 0, targetBone, _matrix);
setPositionOfBoneToAttributeArray(array, 1, effectorBone, _matrix);
for (let j = 0, jl = ik.links.length; j < jl; j++) {
const link = ik.links[j];
const linkBone = bones[link.index];
setPositionOfBoneToAttributeArray(array, j + 2, linkBone, _matrix);
}
line.geometry.attributes.position.needsUpdate = true;
}
}
this.matrix.copy(mesh.matrixWorld);
super.updateMatrixWorld(force);
}
/**
* Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
*/
dispose() {
this.sphereGeometry.dispose();
this.targetSphereMaterial.dispose();
this.effectorSphereMaterial.dispose();
this.linkSphereMaterial.dispose();
this.lineMaterial.dispose();
const children = this.children;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.isLine)
child.geometry.dispose();
}
}
// private method
_init() {
const scope = this;
const iks = this.iks;
function createLineGeometry(ik) {
const geometry = new BufferGeometry();
const vertices = new Float32Array((2 + ik.links.length) * 3);
geometry.setAttribute("position", new BufferAttribute(vertices, 3));
return geometry;
}
function createTargetMesh() {
return new Mesh(scope.sphereGeometry, scope.targetSphereMaterial);
}
function createEffectorMesh() {
return new Mesh(scope.sphereGeometry, scope.effectorSphereMaterial);
}
function createLinkMesh() {
return new Mesh(scope.sphereGeometry, scope.linkSphereMaterial);
}
function createLine(ik) {
return new Line(createLineGeometry(ik), scope.lineMaterial);
}
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
this.add(createTargetMesh());
this.add(createEffectorMesh());
for (let j = 0, jl = ik.links.length; j < jl; j++) {
this.add(createLinkMesh());
}
this.add(createLine(ik));
}
}
}
export {
CCDIKHelper,
CCDIKSolver
};
//# sourceMappingURL=CCDIKSolver.js.map
File diff suppressed because one or more lines are too long
+689
View File
@@ -0,0 +1,689 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const CCDIKSolver = require("./CCDIKSolver.cjs");
const MMDPhysics = require("./MMDPhysics.cjs");
class MMDAnimationHelper {
/**
* @param {Object} params - (optional)
* @param {boolean} params.sync - Whether animation durations of added objects are synched. Default is true.
* @param {Number} params.afterglow - Default is 0.0.
* @param {boolean} params.resetPhysicsOnLoop - Default is true.
*/
constructor(params = {}) {
this.meshes = [];
this.camera = null;
this.cameraTarget = new THREE.Object3D();
this.cameraTarget.name = "target";
this.audio = null;
this.audioManager = null;
this.objects = /* @__PURE__ */ new WeakMap();
this.configuration = {
sync: params.sync !== void 0 ? params.sync : true,
afterglow: params.afterglow !== void 0 ? params.afterglow : 0,
resetPhysicsOnLoop: params.resetPhysicsOnLoop !== void 0 ? params.resetPhysicsOnLoop : true,
pmxAnimation: params.pmxAnimation !== void 0 ? params.pmxAnimation : false
};
this.enabled = {
animation: true,
ik: true,
grant: true,
physics: true,
cameraAnimation: true
};
this.onBeforePhysics = function() {
};
this.sharedPhysics = false;
this.masterPhysics = null;
}
/**
* Adds an Three.js Object to helper and setups animation.
* The anmation durations of added objects are synched
* if this.configuration.sync is true.
*
* @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object
* @param {Object} params - (optional)
* @param {THREE.AnimationClip|Array<THREE.AnimationClip>} params.animation - Only for THREE.SkinnedMesh and THREE.Camera. Default is undefined.
* @param {boolean} params.physics - Only for THREE.SkinnedMesh. Default is true.
* @param {Integer} params.warmup - Only for THREE.SkinnedMesh and physics is true. Default is 60.
* @param {Number} params.unitStep - Only for THREE.SkinnedMesh and physics is true. Default is 1 / 65.
* @param {Integer} params.maxStepNum - Only for THREE.SkinnedMesh and physics is true. Default is 3.
* @param {Vector3} params.gravity - Only for THREE.SkinnedMesh and physics is true. Default ( 0, - 9.8 * 10, 0 ).
* @param {Number} params.delayTime - Only for THREE.Audio. Default is 0.0.
* @return {MMDAnimationHelper}
*/
add(object, params = {}) {
if (object.isSkinnedMesh) {
this._addMesh(object, params);
} else if (object.isCamera) {
this._setupCamera(object, params);
} else if (object.type === "Audio") {
this._setupAudio(object, params);
} else {
throw new Error(
"THREE.MMDAnimationHelper.add: accepts only THREE.SkinnedMesh or THREE.Camera or THREE.Audio instance."
);
}
if (this.configuration.sync)
this._syncDuration();
return this;
}
/**
* Removes an Three.js Object from helper.
*
* @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object
* @return {MMDAnimationHelper}
*/
remove(object) {
if (object.isSkinnedMesh) {
this._removeMesh(object);
} else if (object.isCamera) {
this._clearCamera(object);
} else if (object.type === "Audio") {
this._clearAudio(object);
} else {
throw new Error(
"THREE.MMDAnimationHelper.remove: accepts only THREE.SkinnedMesh or THREE.Camera or THREE.Audio instance."
);
}
if (this.configuration.sync)
this._syncDuration();
return this;
}
/**
* Updates the animation.
*
* @param {Number} delta
* @return {MMDAnimationHelper}
*/
update(delta) {
if (this.audioManager !== null)
this.audioManager.control(delta);
for (let i = 0; i < this.meshes.length; i++) {
this._animateMesh(this.meshes[i], delta);
}
if (this.sharedPhysics)
this._updateSharedPhysics(delta);
if (this.camera !== null)
this._animateCamera(this.camera, delta);
return this;
}
/**
* Changes the pose of SkinnedMesh as VPD specifies.
*
* @param {THREE.SkinnedMesh} mesh
* @param {Object} vpd - VPD content parsed MMDParser
* @param {Object} params - (optional)
* @param {boolean} params.resetPose - Default is true.
* @param {boolean} params.ik - Default is true.
* @param {boolean} params.grant - Default is true.
* @return {MMDAnimationHelper}
*/
pose(mesh, vpd, params = {}) {
if (params.resetPose !== false)
mesh.pose();
const bones = mesh.skeleton.bones;
const boneParams = vpd.bones;
const boneNameDictionary = {};
for (let i = 0, il = bones.length; i < il; i++) {
boneNameDictionary[bones[i].name] = i;
}
const vector = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
for (let i = 0, il = boneParams.length; i < il; i++) {
const boneParam = boneParams[i];
const boneIndex = boneNameDictionary[boneParam.name];
if (boneIndex === void 0)
continue;
const bone = bones[boneIndex];
bone.position.add(vector.fromArray(boneParam.translation));
bone.quaternion.multiply(quaternion.fromArray(boneParam.quaternion));
}
mesh.updateMatrixWorld(true);
if (this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === "pmx") {
const sortedBonesData = this._sortBoneDataArray(mesh.geometry.userData.MMD.bones.slice());
const ikSolver = params.ik !== false ? this._createCCDIKSolver(mesh) : null;
const grantSolver = params.grant !== false ? this.createGrantSolver(mesh) : null;
this._animatePMXMesh(mesh, sortedBonesData, ikSolver, grantSolver);
} else {
if (params.ik !== false) {
this._createCCDIKSolver(mesh).update();
}
if (params.grant !== false) {
this.createGrantSolver(mesh).update();
}
}
return this;
}
/**
* Enabes/Disables an animation feature.
*
* @param {string} key
* @param {boolean} enabled
* @return {MMDAnimationHelper}
*/
enable(key, enabled) {
if (this.enabled[key] === void 0) {
throw new Error("THREE.MMDAnimationHelper.enable: unknown key " + key);
}
this.enabled[key] = enabled;
if (key === "physics") {
for (let i = 0, il = this.meshes.length; i < il; i++) {
this._optimizeIK(this.meshes[i], enabled);
}
}
return this;
}
/**
* Creates an GrantSolver instance.
*
* @param {THREE.SkinnedMesh} mesh
* @return {GrantSolver}
*/
createGrantSolver(mesh) {
return new GrantSolver(mesh, mesh.geometry.userData.MMD.grants);
}
// private methods
_addMesh(mesh, params) {
if (this.meshes.indexOf(mesh) >= 0) {
throw new Error("THREE.MMDAnimationHelper._addMesh: SkinnedMesh '" + mesh.name + "' has already been added.");
}
this.meshes.push(mesh);
this.objects.set(mesh, { looped: false });
this._setupMeshAnimation(mesh, params.animation);
if (params.physics !== false) {
this._setupMeshPhysics(mesh, params);
}
return this;
}
_setupCamera(camera, params) {
if (this.camera === camera) {
throw new Error("THREE.MMDAnimationHelper._setupCamera: Camera '" + camera.name + "' has already been set.");
}
if (this.camera)
this.clearCamera(this.camera);
this.camera = camera;
camera.add(this.cameraTarget);
this.objects.set(camera, {});
if (params.animation !== void 0) {
this._setupCameraAnimation(camera, params.animation);
}
return this;
}
_setupAudio(audio, params) {
if (this.audio === audio) {
throw new Error("THREE.MMDAnimationHelper._setupAudio: Audio '" + audio.name + "' has already been set.");
}
if (this.audio)
this.clearAudio(this.audio);
this.audio = audio;
this.audioManager = new AudioManager(audio, params);
this.objects.set(this.audioManager, {
duration: this.audioManager.duration
});
return this;
}
_removeMesh(mesh) {
let found = false;
let writeIndex = 0;
for (let i = 0, il = this.meshes.length; i < il; i++) {
if (this.meshes[i] === mesh) {
this.objects.delete(mesh);
found = true;
continue;
}
this.meshes[writeIndex++] = this.meshes[i];
}
if (!found) {
throw new Error(
"THREE.MMDAnimationHelper._removeMesh: SkinnedMesh '" + mesh.name + "' has not been added yet."
);
}
this.meshes.length = writeIndex;
return this;
}
_clearCamera(camera) {
if (camera !== this.camera) {
throw new Error("THREE.MMDAnimationHelper._clearCamera: Camera '" + camera.name + "' has not been set yet.");
}
this.camera.remove(this.cameraTarget);
this.objects.delete(this.camera);
this.camera = null;
return this;
}
_clearAudio(audio) {
if (audio !== this.audio) {
throw new Error("THREE.MMDAnimationHelper._clearAudio: Audio '" + audio.name + "' has not been set yet.");
}
this.objects.delete(this.audioManager);
this.audio = null;
this.audioManager = null;
return this;
}
_setupMeshAnimation(mesh, animation) {
const objects = this.objects.get(mesh);
if (animation !== void 0) {
const animations = Array.isArray(animation) ? animation : [animation];
objects.mixer = new THREE.AnimationMixer(mesh);
for (let i = 0, il = animations.length; i < il; i++) {
objects.mixer.clipAction(animations[i]).play();
}
objects.mixer.addEventListener("loop", function(event) {
const tracks = event.action._clip.tracks;
if (tracks.length > 0 && tracks[0].name.slice(0, 6) !== ".bones")
return;
objects.looped = true;
});
}
objects.ikSolver = this._createCCDIKSolver(mesh);
objects.grantSolver = this.createGrantSolver(mesh);
return this;
}
_setupCameraAnimation(camera, animation) {
const animations = Array.isArray(animation) ? animation : [animation];
const objects = this.objects.get(camera);
objects.mixer = new THREE.AnimationMixer(camera);
for (let i = 0, il = animations.length; i < il; i++) {
objects.mixer.clipAction(animations[i]).play();
}
}
_setupMeshPhysics(mesh, params) {
const objects = this.objects.get(mesh);
if (params.world === void 0 && this.sharedPhysics) {
const masterPhysics = this._getMasterPhysics();
if (masterPhysics !== null)
world = masterPhysics.world;
}
objects.physics = this._createMMDPhysics(mesh, params);
if (objects.mixer && params.animationWarmup !== false) {
this._animateMesh(mesh, 0);
objects.physics.reset();
}
objects.physics.warmup(params.warmup !== void 0 ? params.warmup : 60);
this._optimizeIK(mesh, true);
}
_animateMesh(mesh, delta) {
const objects = this.objects.get(mesh);
const mixer = objects.mixer;
const ikSolver = objects.ikSolver;
const grantSolver = objects.grantSolver;
const physics = objects.physics;
const looped = objects.looped;
if (mixer && this.enabled.animation) {
this._restoreBones(mesh);
mixer.update(delta);
this._saveBones(mesh);
if (this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === "pmx") {
if (!objects.sortedBonesData)
objects.sortedBonesData = this._sortBoneDataArray(mesh.geometry.userData.MMD.bones.slice());
this._animatePMXMesh(
mesh,
objects.sortedBonesData,
ikSolver && this.enabled.ik ? ikSolver : null,
grantSolver && this.enabled.grant ? grantSolver : null
);
} else {
if (ikSolver && this.enabled.ik) {
mesh.updateMatrixWorld(true);
ikSolver.update();
}
if (grantSolver && this.enabled.grant) {
grantSolver.update();
}
}
}
if (looped === true && this.enabled.physics) {
if (physics && this.configuration.resetPhysicsOnLoop)
physics.reset();
objects.looped = false;
}
if (physics && this.enabled.physics && !this.sharedPhysics) {
this.onBeforePhysics(mesh);
physics.update(delta);
}
}
// Sort bones in order by 1. transformationClass and 2. bone index.
// In PMX animation system, bone transformations should be processed
// in this order.
_sortBoneDataArray(boneDataArray) {
return boneDataArray.sort(function(a, b) {
if (a.transformationClass !== b.transformationClass) {
return a.transformationClass - b.transformationClass;
} else {
return a.index - b.index;
}
});
}
// PMX Animation system is a bit too complex and doesn't great match to
// Three.js Animation system. This method attempts to simulate it as much as
// possible but doesn't perfectly simulate.
// This method is more costly than the regular one so
// you are recommended to set constructor parameter "pmxAnimation: true"
// only if your PMX model animation doesn't work well.
// If you need better method you would be required to write your own.
_animatePMXMesh(mesh, sortedBonesData, ikSolver, grantSolver) {
_quaternionIndex = 0;
_grantResultMap.clear();
for (let i = 0, il = sortedBonesData.length; i < il; i++) {
updateOne(mesh, sortedBonesData[i].index, ikSolver, grantSolver);
}
mesh.updateMatrixWorld(true);
return this;
}
_animateCamera(camera, delta) {
const mixer = this.objects.get(camera).mixer;
if (mixer && this.enabled.cameraAnimation) {
mixer.update(delta);
camera.updateProjectionMatrix();
camera.up.set(0, 1, 0);
camera.up.applyQuaternion(camera.quaternion);
camera.lookAt(this.cameraTarget.position);
}
}
_optimizeIK(mesh, physicsEnabled) {
const iks = mesh.geometry.userData.MMD.iks;
const bones = mesh.geometry.userData.MMD.bones;
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const links = ik.links;
for (let j = 0, jl = links.length; j < jl; j++) {
const link = links[j];
if (physicsEnabled === true) {
link.enabled = bones[link.index].rigidBodyType > 0 ? false : true;
} else {
link.enabled = true;
}
}
}
}
_createCCDIKSolver(mesh) {
if (CCDIKSolver.CCDIKSolver === void 0) {
throw new Error("THREE.MMDAnimationHelper: Import CCDIKSolver.");
}
return new CCDIKSolver.CCDIKSolver(mesh, mesh.geometry.userData.MMD.iks);
}
_createMMDPhysics(mesh, params) {
if (MMDPhysics.MMDPhysics === void 0) {
throw new Error("THREE.MMDPhysics: Import MMDPhysics.");
}
return new MMDPhysics.MMDPhysics(mesh, mesh.geometry.userData.MMD.rigidBodies, mesh.geometry.userData.MMD.constraints, params);
}
/*
* Detects the longest duration and then sets it to them to sync.
* TODO: Not to access private properties ( ._actions and ._clip )
*/
_syncDuration() {
let max = 0;
const objects = this.objects;
const meshes = this.meshes;
const camera = this.camera;
const audioManager = this.audioManager;
for (let i = 0, il = meshes.length; i < il; i++) {
const mixer = this.objects.get(meshes[i]).mixer;
if (mixer === void 0)
continue;
for (let j = 0; j < mixer._actions.length; j++) {
const clip = mixer._actions[j]._clip;
if (!objects.has(clip)) {
objects.set(clip, {
duration: clip.duration
});
}
max = Math.max(max, objects.get(clip).duration);
}
}
if (camera !== null) {
const mixer = this.objects.get(camera).mixer;
if (mixer !== void 0) {
for (let i = 0, il = mixer._actions.length; i < il; i++) {
const clip = mixer._actions[i]._clip;
if (!objects.has(clip)) {
objects.set(clip, {
duration: clip.duration
});
}
max = Math.max(max, objects.get(clip).duration);
}
}
}
if (audioManager !== null) {
max = Math.max(max, objects.get(audioManager).duration);
}
max += this.configuration.afterglow;
for (let i = 0, il = this.meshes.length; i < il; i++) {
const mixer = this.objects.get(this.meshes[i]).mixer;
if (mixer === void 0)
continue;
for (let j = 0, jl = mixer._actions.length; j < jl; j++) {
mixer._actions[j]._clip.duration = max;
}
}
if (camera !== null) {
const mixer = this.objects.get(camera).mixer;
if (mixer !== void 0) {
for (let i = 0, il = mixer._actions.length; i < il; i++) {
mixer._actions[i]._clip.duration = max;
}
}
}
if (audioManager !== null) {
audioManager.duration = max;
}
}
// workaround
_updatePropertyMixersBuffer(mesh) {
const mixer = this.objects.get(mesh).mixer;
const propertyMixers = mixer._bindings;
const accuIndex = mixer._accuIndex;
for (let i = 0, il = propertyMixers.length; i < il; i++) {
const propertyMixer = propertyMixers[i];
const buffer = propertyMixer.buffer;
const stride = propertyMixer.valueSize;
const offset = (accuIndex + 1) * stride;
propertyMixer.binding.getValue(buffer, offset);
}
}
/*
* Avoiding these two issues by restore/save bones before/after mixer animation.
*
* 1. PropertyMixer used by AnimationMixer holds cache value in .buffer.
* Calculating IK, Grant, and Physics after mixer animation can break
* the cache coherency.
*
* 2. Applying Grant two or more times without reset the posing breaks model.
*/
_saveBones(mesh) {
const objects = this.objects.get(mesh);
const bones = mesh.skeleton.bones;
let backupBones = objects.backupBones;
if (backupBones === void 0) {
backupBones = new Float32Array(bones.length * 7);
objects.backupBones = backupBones;
}
for (let i = 0, il = bones.length; i < il; i++) {
const bone = bones[i];
bone.position.toArray(backupBones, i * 7);
bone.quaternion.toArray(backupBones, i * 7 + 3);
}
}
_restoreBones(mesh) {
const objects = this.objects.get(mesh);
const backupBones = objects.backupBones;
if (backupBones === void 0)
return;
const bones = mesh.skeleton.bones;
for (let i = 0, il = bones.length; i < il; i++) {
const bone = bones[i];
bone.position.fromArray(backupBones, i * 7);
bone.quaternion.fromArray(backupBones, i * 7 + 3);
}
}
// experimental
_getMasterPhysics() {
if (this.masterPhysics !== null)
return this.masterPhysics;
for (let i = 0, il = this.meshes.length; i < il; i++) {
const physics = this.meshes[i].physics;
if (physics !== void 0 && physics !== null) {
this.masterPhysics = physics;
return this.masterPhysics;
}
}
return null;
}
_updateSharedPhysics(delta) {
if (this.meshes.length === 0 || !this.enabled.physics || !this.sharedPhysics)
return;
const physics = this._getMasterPhysics();
if (physics === null)
return;
for (let i = 0, il = this.meshes.length; i < il; i++) {
const p = this.meshes[i].physics;
if (p !== null && p !== void 0) {
p.updateRigidBodies();
}
}
physics.stepSimulation(delta);
for (let i = 0, il = this.meshes.length; i < il; i++) {
const p = this.meshes[i].physics;
if (p !== null && p !== void 0) {
p.updateBones();
}
}
}
}
const _quaternions = [];
let _quaternionIndex = 0;
function getQuaternion() {
if (_quaternionIndex >= _quaternions.length) {
_quaternions.push(new THREE.Quaternion());
}
return _quaternions[_quaternionIndex++];
}
const _grantResultMap = /* @__PURE__ */ new Map();
function updateOne(mesh, boneIndex, ikSolver, grantSolver) {
const bones = mesh.skeleton.bones;
const bonesData = mesh.geometry.userData.MMD.bones;
const boneData = bonesData[boneIndex];
const bone = bones[boneIndex];
if (_grantResultMap.has(boneIndex))
return;
const quaternion = getQuaternion();
_grantResultMap.set(boneIndex, quaternion.copy(bone.quaternion));
if (grantSolver && boneData.grant && !boneData.grant.isLocal && boneData.grant.affectRotation) {
const parentIndex = boneData.grant.parentIndex;
const ratio = boneData.grant.ratio;
if (!_grantResultMap.has(parentIndex)) {
updateOne(mesh, parentIndex, ikSolver, grantSolver);
}
grantSolver.addGrantRotation(bone, _grantResultMap.get(parentIndex), ratio);
}
if (ikSolver && boneData.ik) {
mesh.updateMatrixWorld(true);
ikSolver.updateOne(boneData.ik);
const links = boneData.ik.links;
for (let i = 0, il = links.length; i < il; i++) {
const link = links[i];
if (link.enabled === false)
continue;
const linkIndex = link.index;
if (_grantResultMap.has(linkIndex)) {
_grantResultMap.set(linkIndex, _grantResultMap.get(linkIndex).copy(bones[linkIndex].quaternion));
}
}
}
quaternion.copy(bone.quaternion);
}
class AudioManager {
/**
* @param {THREE.Audio} audio
* @param {Object} params - (optional)
* @param {Nuumber} params.delayTime
*/
constructor(audio, params = {}) {
this.audio = audio;
this.elapsedTime = 0;
this.currentTime = 0;
this.delayTime = params.delayTime !== void 0 ? params.delayTime : 0;
this.audioDuration = this.audio.buffer.duration;
this.duration = this.audioDuration + this.delayTime;
}
/**
* @param {Number} delta
* @return {AudioManager}
*/
control(delta) {
this.elapsed += delta;
this.currentTime += delta;
if (this._shouldStopAudio())
this.audio.stop();
if (this._shouldStartAudio())
this.audio.play();
return this;
}
// private methods
_shouldStartAudio() {
if (this.audio.isPlaying)
return false;
while (this.currentTime >= this.duration) {
this.currentTime -= this.duration;
}
if (this.currentTime < this.delayTime)
return false;
if (this.currentTime - this.delayTime > this.audioDuration)
return false;
return true;
}
_shouldStopAudio() {
return this.audio.isPlaying && this.currentTime >= this.duration;
}
}
const _q = /* @__PURE__ */ new THREE.Quaternion();
class GrantSolver {
constructor(mesh, grants = []) {
this.mesh = mesh;
this.grants = grants;
}
/**
* Solve all the grant bones
* @return {GrantSolver}
*/
update() {
const grants = this.grants;
for (let i = 0, il = grants.length; i < il; i++) {
this.updateOne(grants[i]);
}
return this;
}
/**
* Solve a grant bone
* @param {Object} grant - grant parameter
* @return {GrantSolver}
*/
updateOne(grant) {
const bones = this.mesh.skeleton.bones;
const bone = bones[grant.index];
const parentBone = bones[grant.parentIndex];
if (grant.isLocal) {
if (grant.affectPosition)
;
if (grant.affectRotation)
;
} else {
if (grant.affectPosition)
;
if (grant.affectRotation) {
this.addGrantRotation(bone, parentBone.quaternion, grant.ratio);
}
}
return this;
}
addGrantRotation(bone, q, ratio) {
_q.set(0, 0, 0, 1);
_q.slerp(q, ratio);
bone.quaternion.multiply(_q);
return this;
}
}
exports.MMDAnimationHelper = MMDAnimationHelper;
//# sourceMappingURL=MMDAnimationHelper.cjs.map
File diff suppressed because one or more lines are too long
+93
View File
@@ -0,0 +1,93 @@
import { AnimationClip, Audio, Camera, Object3D, Quaternion, SkinnedMesh, Bone, AnimationMixer } from 'three'
import { CCDIKSolver } from './CCDIKSolver'
import { MMDPhysics } from './MMDPhysics'
export interface MMDAnimationHelperParameter {
sync?: boolean | undefined
afterglow?: number | undefined
resetPhysicsOnLoop?: boolean | undefined
}
export interface MMDAnimationHelperAddParameter {
animation?: AnimationClip | AnimationClip[] | undefined
physics?: boolean | undefined
warmup?: number | undefined
unitStep?: number | undefined
maxStepNum?: number | undefined
gravity?: number | undefined
delayTime?: number | undefined
}
export interface MMDAnimationHelperPoseParameter {
resetPose?: boolean | undefined
ik?: boolean | undefined
grant?: boolean | undefined
}
export interface MMDAnimationHelperMixer {
looped: boolean
mixer?: AnimationMixer | undefined
ikSolver: CCDIKSolver
grantSolver: GrantSolver
physics?: MMDPhysics | undefined
duration?: number | undefined
}
export class MMDAnimationHelper {
constructor(params?: MMDAnimationHelperParameter)
meshes: SkinnedMesh[]
camera: Camera | null
cameraTarget: Object3D
audio: Audio
audioManager: AudioManager
configuration: {
sync: boolean
afterglow: number
resetPhysicsOnLoop: boolean
}
enabled: {
animation: boolean
ik: boolean
grant: boolean
physics: boolean
cameraAnimation: boolean
}
objects: WeakMap<SkinnedMesh | Camera | AudioManager, MMDAnimationHelperMixer>
onBeforePhysics: (mesh: SkinnedMesh) => void
sharedPhysics: boolean
masterPhysics: null
add(object: SkinnedMesh | Camera | Audio, params?: MMDAnimationHelperAddParameter): this
remove(object: SkinnedMesh | Camera | Audio): this
update(delta: number): this
pose(mesh: SkinnedMesh, vpd: object, params?: MMDAnimationHelperPoseParameter): this
enable(key: string, enabled: boolean): this
createGrantSolver(mesh: SkinnedMesh): GrantSolver
}
export interface AudioManagerParameter {
delayTime?: number | undefined
}
export class AudioManager {
constructor(audio: Audio, params?: AudioManagerParameter)
audio: Audio
elapsedTime: number
currentTime: number
delayTime: number
audioDuration: number
duration: number
control(delta: number): this
}
export class GrantSolver {
constructor(mesh: SkinnedMesh, grants: object[])
mesh: SkinnedMesh
grants: object[]
update(): this
updateOne(gran: object[]): this
addGrantRotation(bone: Bone, q: Quaternion, ratio: number): this
}
+689
View File
@@ -0,0 +1,689 @@
import { Object3D, Vector3, Quaternion, AnimationMixer } from "three";
import { CCDIKSolver } from "./CCDIKSolver.js";
import { MMDPhysics } from "./MMDPhysics.js";
class MMDAnimationHelper {
/**
* @param {Object} params - (optional)
* @param {boolean} params.sync - Whether animation durations of added objects are synched. Default is true.
* @param {Number} params.afterglow - Default is 0.0.
* @param {boolean} params.resetPhysicsOnLoop - Default is true.
*/
constructor(params = {}) {
this.meshes = [];
this.camera = null;
this.cameraTarget = new Object3D();
this.cameraTarget.name = "target";
this.audio = null;
this.audioManager = null;
this.objects = /* @__PURE__ */ new WeakMap();
this.configuration = {
sync: params.sync !== void 0 ? params.sync : true,
afterglow: params.afterglow !== void 0 ? params.afterglow : 0,
resetPhysicsOnLoop: params.resetPhysicsOnLoop !== void 0 ? params.resetPhysicsOnLoop : true,
pmxAnimation: params.pmxAnimation !== void 0 ? params.pmxAnimation : false
};
this.enabled = {
animation: true,
ik: true,
grant: true,
physics: true,
cameraAnimation: true
};
this.onBeforePhysics = function() {
};
this.sharedPhysics = false;
this.masterPhysics = null;
}
/**
* Adds an Three.js Object to helper and setups animation.
* The anmation durations of added objects are synched
* if this.configuration.sync is true.
*
* @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object
* @param {Object} params - (optional)
* @param {THREE.AnimationClip|Array<THREE.AnimationClip>} params.animation - Only for THREE.SkinnedMesh and THREE.Camera. Default is undefined.
* @param {boolean} params.physics - Only for THREE.SkinnedMesh. Default is true.
* @param {Integer} params.warmup - Only for THREE.SkinnedMesh and physics is true. Default is 60.
* @param {Number} params.unitStep - Only for THREE.SkinnedMesh and physics is true. Default is 1 / 65.
* @param {Integer} params.maxStepNum - Only for THREE.SkinnedMesh and physics is true. Default is 3.
* @param {Vector3} params.gravity - Only for THREE.SkinnedMesh and physics is true. Default ( 0, - 9.8 * 10, 0 ).
* @param {Number} params.delayTime - Only for THREE.Audio. Default is 0.0.
* @return {MMDAnimationHelper}
*/
add(object, params = {}) {
if (object.isSkinnedMesh) {
this._addMesh(object, params);
} else if (object.isCamera) {
this._setupCamera(object, params);
} else if (object.type === "Audio") {
this._setupAudio(object, params);
} else {
throw new Error(
"THREE.MMDAnimationHelper.add: accepts only THREE.SkinnedMesh or THREE.Camera or THREE.Audio instance."
);
}
if (this.configuration.sync)
this._syncDuration();
return this;
}
/**
* Removes an Three.js Object from helper.
*
* @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object
* @return {MMDAnimationHelper}
*/
remove(object) {
if (object.isSkinnedMesh) {
this._removeMesh(object);
} else if (object.isCamera) {
this._clearCamera(object);
} else if (object.type === "Audio") {
this._clearAudio(object);
} else {
throw new Error(
"THREE.MMDAnimationHelper.remove: accepts only THREE.SkinnedMesh or THREE.Camera or THREE.Audio instance."
);
}
if (this.configuration.sync)
this._syncDuration();
return this;
}
/**
* Updates the animation.
*
* @param {Number} delta
* @return {MMDAnimationHelper}
*/
update(delta) {
if (this.audioManager !== null)
this.audioManager.control(delta);
for (let i = 0; i < this.meshes.length; i++) {
this._animateMesh(this.meshes[i], delta);
}
if (this.sharedPhysics)
this._updateSharedPhysics(delta);
if (this.camera !== null)
this._animateCamera(this.camera, delta);
return this;
}
/**
* Changes the pose of SkinnedMesh as VPD specifies.
*
* @param {THREE.SkinnedMesh} mesh
* @param {Object} vpd - VPD content parsed MMDParser
* @param {Object} params - (optional)
* @param {boolean} params.resetPose - Default is true.
* @param {boolean} params.ik - Default is true.
* @param {boolean} params.grant - Default is true.
* @return {MMDAnimationHelper}
*/
pose(mesh, vpd, params = {}) {
if (params.resetPose !== false)
mesh.pose();
const bones = mesh.skeleton.bones;
const boneParams = vpd.bones;
const boneNameDictionary = {};
for (let i = 0, il = bones.length; i < il; i++) {
boneNameDictionary[bones[i].name] = i;
}
const vector = new Vector3();
const quaternion = new Quaternion();
for (let i = 0, il = boneParams.length; i < il; i++) {
const boneParam = boneParams[i];
const boneIndex = boneNameDictionary[boneParam.name];
if (boneIndex === void 0)
continue;
const bone = bones[boneIndex];
bone.position.add(vector.fromArray(boneParam.translation));
bone.quaternion.multiply(quaternion.fromArray(boneParam.quaternion));
}
mesh.updateMatrixWorld(true);
if (this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === "pmx") {
const sortedBonesData = this._sortBoneDataArray(mesh.geometry.userData.MMD.bones.slice());
const ikSolver = params.ik !== false ? this._createCCDIKSolver(mesh) : null;
const grantSolver = params.grant !== false ? this.createGrantSolver(mesh) : null;
this._animatePMXMesh(mesh, sortedBonesData, ikSolver, grantSolver);
} else {
if (params.ik !== false) {
this._createCCDIKSolver(mesh).update();
}
if (params.grant !== false) {
this.createGrantSolver(mesh).update();
}
}
return this;
}
/**
* Enabes/Disables an animation feature.
*
* @param {string} key
* @param {boolean} enabled
* @return {MMDAnimationHelper}
*/
enable(key, enabled) {
if (this.enabled[key] === void 0) {
throw new Error("THREE.MMDAnimationHelper.enable: unknown key " + key);
}
this.enabled[key] = enabled;
if (key === "physics") {
for (let i = 0, il = this.meshes.length; i < il; i++) {
this._optimizeIK(this.meshes[i], enabled);
}
}
return this;
}
/**
* Creates an GrantSolver instance.
*
* @param {THREE.SkinnedMesh} mesh
* @return {GrantSolver}
*/
createGrantSolver(mesh) {
return new GrantSolver(mesh, mesh.geometry.userData.MMD.grants);
}
// private methods
_addMesh(mesh, params) {
if (this.meshes.indexOf(mesh) >= 0) {
throw new Error("THREE.MMDAnimationHelper._addMesh: SkinnedMesh '" + mesh.name + "' has already been added.");
}
this.meshes.push(mesh);
this.objects.set(mesh, { looped: false });
this._setupMeshAnimation(mesh, params.animation);
if (params.physics !== false) {
this._setupMeshPhysics(mesh, params);
}
return this;
}
_setupCamera(camera, params) {
if (this.camera === camera) {
throw new Error("THREE.MMDAnimationHelper._setupCamera: Camera '" + camera.name + "' has already been set.");
}
if (this.camera)
this.clearCamera(this.camera);
this.camera = camera;
camera.add(this.cameraTarget);
this.objects.set(camera, {});
if (params.animation !== void 0) {
this._setupCameraAnimation(camera, params.animation);
}
return this;
}
_setupAudio(audio, params) {
if (this.audio === audio) {
throw new Error("THREE.MMDAnimationHelper._setupAudio: Audio '" + audio.name + "' has already been set.");
}
if (this.audio)
this.clearAudio(this.audio);
this.audio = audio;
this.audioManager = new AudioManager(audio, params);
this.objects.set(this.audioManager, {
duration: this.audioManager.duration
});
return this;
}
_removeMesh(mesh) {
let found = false;
let writeIndex = 0;
for (let i = 0, il = this.meshes.length; i < il; i++) {
if (this.meshes[i] === mesh) {
this.objects.delete(mesh);
found = true;
continue;
}
this.meshes[writeIndex++] = this.meshes[i];
}
if (!found) {
throw new Error(
"THREE.MMDAnimationHelper._removeMesh: SkinnedMesh '" + mesh.name + "' has not been added yet."
);
}
this.meshes.length = writeIndex;
return this;
}
_clearCamera(camera) {
if (camera !== this.camera) {
throw new Error("THREE.MMDAnimationHelper._clearCamera: Camera '" + camera.name + "' has not been set yet.");
}
this.camera.remove(this.cameraTarget);
this.objects.delete(this.camera);
this.camera = null;
return this;
}
_clearAudio(audio) {
if (audio !== this.audio) {
throw new Error("THREE.MMDAnimationHelper._clearAudio: Audio '" + audio.name + "' has not been set yet.");
}
this.objects.delete(this.audioManager);
this.audio = null;
this.audioManager = null;
return this;
}
_setupMeshAnimation(mesh, animation) {
const objects = this.objects.get(mesh);
if (animation !== void 0) {
const animations = Array.isArray(animation) ? animation : [animation];
objects.mixer = new AnimationMixer(mesh);
for (let i = 0, il = animations.length; i < il; i++) {
objects.mixer.clipAction(animations[i]).play();
}
objects.mixer.addEventListener("loop", function(event) {
const tracks = event.action._clip.tracks;
if (tracks.length > 0 && tracks[0].name.slice(0, 6) !== ".bones")
return;
objects.looped = true;
});
}
objects.ikSolver = this._createCCDIKSolver(mesh);
objects.grantSolver = this.createGrantSolver(mesh);
return this;
}
_setupCameraAnimation(camera, animation) {
const animations = Array.isArray(animation) ? animation : [animation];
const objects = this.objects.get(camera);
objects.mixer = new AnimationMixer(camera);
for (let i = 0, il = animations.length; i < il; i++) {
objects.mixer.clipAction(animations[i]).play();
}
}
_setupMeshPhysics(mesh, params) {
const objects = this.objects.get(mesh);
if (params.world === void 0 && this.sharedPhysics) {
const masterPhysics = this._getMasterPhysics();
if (masterPhysics !== null)
world = masterPhysics.world;
}
objects.physics = this._createMMDPhysics(mesh, params);
if (objects.mixer && params.animationWarmup !== false) {
this._animateMesh(mesh, 0);
objects.physics.reset();
}
objects.physics.warmup(params.warmup !== void 0 ? params.warmup : 60);
this._optimizeIK(mesh, true);
}
_animateMesh(mesh, delta) {
const objects = this.objects.get(mesh);
const mixer = objects.mixer;
const ikSolver = objects.ikSolver;
const grantSolver = objects.grantSolver;
const physics = objects.physics;
const looped = objects.looped;
if (mixer && this.enabled.animation) {
this._restoreBones(mesh);
mixer.update(delta);
this._saveBones(mesh);
if (this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === "pmx") {
if (!objects.sortedBonesData)
objects.sortedBonesData = this._sortBoneDataArray(mesh.geometry.userData.MMD.bones.slice());
this._animatePMXMesh(
mesh,
objects.sortedBonesData,
ikSolver && this.enabled.ik ? ikSolver : null,
grantSolver && this.enabled.grant ? grantSolver : null
);
} else {
if (ikSolver && this.enabled.ik) {
mesh.updateMatrixWorld(true);
ikSolver.update();
}
if (grantSolver && this.enabled.grant) {
grantSolver.update();
}
}
}
if (looped === true && this.enabled.physics) {
if (physics && this.configuration.resetPhysicsOnLoop)
physics.reset();
objects.looped = false;
}
if (physics && this.enabled.physics && !this.sharedPhysics) {
this.onBeforePhysics(mesh);
physics.update(delta);
}
}
// Sort bones in order by 1. transformationClass and 2. bone index.
// In PMX animation system, bone transformations should be processed
// in this order.
_sortBoneDataArray(boneDataArray) {
return boneDataArray.sort(function(a, b) {
if (a.transformationClass !== b.transformationClass) {
return a.transformationClass - b.transformationClass;
} else {
return a.index - b.index;
}
});
}
// PMX Animation system is a bit too complex and doesn't great match to
// Three.js Animation system. This method attempts to simulate it as much as
// possible but doesn't perfectly simulate.
// This method is more costly than the regular one so
// you are recommended to set constructor parameter "pmxAnimation: true"
// only if your PMX model animation doesn't work well.
// If you need better method you would be required to write your own.
_animatePMXMesh(mesh, sortedBonesData, ikSolver, grantSolver) {
_quaternionIndex = 0;
_grantResultMap.clear();
for (let i = 0, il = sortedBonesData.length; i < il; i++) {
updateOne(mesh, sortedBonesData[i].index, ikSolver, grantSolver);
}
mesh.updateMatrixWorld(true);
return this;
}
_animateCamera(camera, delta) {
const mixer = this.objects.get(camera).mixer;
if (mixer && this.enabled.cameraAnimation) {
mixer.update(delta);
camera.updateProjectionMatrix();
camera.up.set(0, 1, 0);
camera.up.applyQuaternion(camera.quaternion);
camera.lookAt(this.cameraTarget.position);
}
}
_optimizeIK(mesh, physicsEnabled) {
const iks = mesh.geometry.userData.MMD.iks;
const bones = mesh.geometry.userData.MMD.bones;
for (let i = 0, il = iks.length; i < il; i++) {
const ik = iks[i];
const links = ik.links;
for (let j = 0, jl = links.length; j < jl; j++) {
const link = links[j];
if (physicsEnabled === true) {
link.enabled = bones[link.index].rigidBodyType > 0 ? false : true;
} else {
link.enabled = true;
}
}
}
}
_createCCDIKSolver(mesh) {
if (CCDIKSolver === void 0) {
throw new Error("THREE.MMDAnimationHelper: Import CCDIKSolver.");
}
return new CCDIKSolver(mesh, mesh.geometry.userData.MMD.iks);
}
_createMMDPhysics(mesh, params) {
if (MMDPhysics === void 0) {
throw new Error("THREE.MMDPhysics: Import MMDPhysics.");
}
return new MMDPhysics(mesh, mesh.geometry.userData.MMD.rigidBodies, mesh.geometry.userData.MMD.constraints, params);
}
/*
* Detects the longest duration and then sets it to them to sync.
* TODO: Not to access private properties ( ._actions and ._clip )
*/
_syncDuration() {
let max = 0;
const objects = this.objects;
const meshes = this.meshes;
const camera = this.camera;
const audioManager = this.audioManager;
for (let i = 0, il = meshes.length; i < il; i++) {
const mixer = this.objects.get(meshes[i]).mixer;
if (mixer === void 0)
continue;
for (let j = 0; j < mixer._actions.length; j++) {
const clip = mixer._actions[j]._clip;
if (!objects.has(clip)) {
objects.set(clip, {
duration: clip.duration
});
}
max = Math.max(max, objects.get(clip).duration);
}
}
if (camera !== null) {
const mixer = this.objects.get(camera).mixer;
if (mixer !== void 0) {
for (let i = 0, il = mixer._actions.length; i < il; i++) {
const clip = mixer._actions[i]._clip;
if (!objects.has(clip)) {
objects.set(clip, {
duration: clip.duration
});
}
max = Math.max(max, objects.get(clip).duration);
}
}
}
if (audioManager !== null) {
max = Math.max(max, objects.get(audioManager).duration);
}
max += this.configuration.afterglow;
for (let i = 0, il = this.meshes.length; i < il; i++) {
const mixer = this.objects.get(this.meshes[i]).mixer;
if (mixer === void 0)
continue;
for (let j = 0, jl = mixer._actions.length; j < jl; j++) {
mixer._actions[j]._clip.duration = max;
}
}
if (camera !== null) {
const mixer = this.objects.get(camera).mixer;
if (mixer !== void 0) {
for (let i = 0, il = mixer._actions.length; i < il; i++) {
mixer._actions[i]._clip.duration = max;
}
}
}
if (audioManager !== null) {
audioManager.duration = max;
}
}
// workaround
_updatePropertyMixersBuffer(mesh) {
const mixer = this.objects.get(mesh).mixer;
const propertyMixers = mixer._bindings;
const accuIndex = mixer._accuIndex;
for (let i = 0, il = propertyMixers.length; i < il; i++) {
const propertyMixer = propertyMixers[i];
const buffer = propertyMixer.buffer;
const stride = propertyMixer.valueSize;
const offset = (accuIndex + 1) * stride;
propertyMixer.binding.getValue(buffer, offset);
}
}
/*
* Avoiding these two issues by restore/save bones before/after mixer animation.
*
* 1. PropertyMixer used by AnimationMixer holds cache value in .buffer.
* Calculating IK, Grant, and Physics after mixer animation can break
* the cache coherency.
*
* 2. Applying Grant two or more times without reset the posing breaks model.
*/
_saveBones(mesh) {
const objects = this.objects.get(mesh);
const bones = mesh.skeleton.bones;
let backupBones = objects.backupBones;
if (backupBones === void 0) {
backupBones = new Float32Array(bones.length * 7);
objects.backupBones = backupBones;
}
for (let i = 0, il = bones.length; i < il; i++) {
const bone = bones[i];
bone.position.toArray(backupBones, i * 7);
bone.quaternion.toArray(backupBones, i * 7 + 3);
}
}
_restoreBones(mesh) {
const objects = this.objects.get(mesh);
const backupBones = objects.backupBones;
if (backupBones === void 0)
return;
const bones = mesh.skeleton.bones;
for (let i = 0, il = bones.length; i < il; i++) {
const bone = bones[i];
bone.position.fromArray(backupBones, i * 7);
bone.quaternion.fromArray(backupBones, i * 7 + 3);
}
}
// experimental
_getMasterPhysics() {
if (this.masterPhysics !== null)
return this.masterPhysics;
for (let i = 0, il = this.meshes.length; i < il; i++) {
const physics = this.meshes[i].physics;
if (physics !== void 0 && physics !== null) {
this.masterPhysics = physics;
return this.masterPhysics;
}
}
return null;
}
_updateSharedPhysics(delta) {
if (this.meshes.length === 0 || !this.enabled.physics || !this.sharedPhysics)
return;
const physics = this._getMasterPhysics();
if (physics === null)
return;
for (let i = 0, il = this.meshes.length; i < il; i++) {
const p = this.meshes[i].physics;
if (p !== null && p !== void 0) {
p.updateRigidBodies();
}
}
physics.stepSimulation(delta);
for (let i = 0, il = this.meshes.length; i < il; i++) {
const p = this.meshes[i].physics;
if (p !== null && p !== void 0) {
p.updateBones();
}
}
}
}
const _quaternions = [];
let _quaternionIndex = 0;
function getQuaternion() {
if (_quaternionIndex >= _quaternions.length) {
_quaternions.push(new Quaternion());
}
return _quaternions[_quaternionIndex++];
}
const _grantResultMap = /* @__PURE__ */ new Map();
function updateOne(mesh, boneIndex, ikSolver, grantSolver) {
const bones = mesh.skeleton.bones;
const bonesData = mesh.geometry.userData.MMD.bones;
const boneData = bonesData[boneIndex];
const bone = bones[boneIndex];
if (_grantResultMap.has(boneIndex))
return;
const quaternion = getQuaternion();
_grantResultMap.set(boneIndex, quaternion.copy(bone.quaternion));
if (grantSolver && boneData.grant && !boneData.grant.isLocal && boneData.grant.affectRotation) {
const parentIndex = boneData.grant.parentIndex;
const ratio = boneData.grant.ratio;
if (!_grantResultMap.has(parentIndex)) {
updateOne(mesh, parentIndex, ikSolver, grantSolver);
}
grantSolver.addGrantRotation(bone, _grantResultMap.get(parentIndex), ratio);
}
if (ikSolver && boneData.ik) {
mesh.updateMatrixWorld(true);
ikSolver.updateOne(boneData.ik);
const links = boneData.ik.links;
for (let i = 0, il = links.length; i < il; i++) {
const link = links[i];
if (link.enabled === false)
continue;
const linkIndex = link.index;
if (_grantResultMap.has(linkIndex)) {
_grantResultMap.set(linkIndex, _grantResultMap.get(linkIndex).copy(bones[linkIndex].quaternion));
}
}
}
quaternion.copy(bone.quaternion);
}
class AudioManager {
/**
* @param {THREE.Audio} audio
* @param {Object} params - (optional)
* @param {Nuumber} params.delayTime
*/
constructor(audio, params = {}) {
this.audio = audio;
this.elapsedTime = 0;
this.currentTime = 0;
this.delayTime = params.delayTime !== void 0 ? params.delayTime : 0;
this.audioDuration = this.audio.buffer.duration;
this.duration = this.audioDuration + this.delayTime;
}
/**
* @param {Number} delta
* @return {AudioManager}
*/
control(delta) {
this.elapsed += delta;
this.currentTime += delta;
if (this._shouldStopAudio())
this.audio.stop();
if (this._shouldStartAudio())
this.audio.play();
return this;
}
// private methods
_shouldStartAudio() {
if (this.audio.isPlaying)
return false;
while (this.currentTime >= this.duration) {
this.currentTime -= this.duration;
}
if (this.currentTime < this.delayTime)
return false;
if (this.currentTime - this.delayTime > this.audioDuration)
return false;
return true;
}
_shouldStopAudio() {
return this.audio.isPlaying && this.currentTime >= this.duration;
}
}
const _q = /* @__PURE__ */ new Quaternion();
class GrantSolver {
constructor(mesh, grants = []) {
this.mesh = mesh;
this.grants = grants;
}
/**
* Solve all the grant bones
* @return {GrantSolver}
*/
update() {
const grants = this.grants;
for (let i = 0, il = grants.length; i < il; i++) {
this.updateOne(grants[i]);
}
return this;
}
/**
* Solve a grant bone
* @param {Object} grant - grant parameter
* @return {GrantSolver}
*/
updateOne(grant) {
const bones = this.mesh.skeleton.bones;
const bone = bones[grant.index];
const parentBone = bones[grant.parentIndex];
if (grant.isLocal) {
if (grant.affectPosition)
;
if (grant.affectRotation)
;
} else {
if (grant.affectPosition)
;
if (grant.affectRotation) {
this.addGrantRotation(bone, parentBone.quaternion, grant.ratio);
}
}
return this;
}
addGrantRotation(bone, q, ratio) {
_q.set(0, 0, 0, 1);
_q.slerp(q, ratio);
bone.quaternion.multiply(_q);
return this;
}
}
export {
MMDAnimationHelper
};
//# sourceMappingURL=MMDAnimationHelper.js.map
File diff suppressed because one or more lines are too long
+831
View File
@@ -0,0 +1,831 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const THREE = require("three");
const CapsuleGeometry = require("../_polyfill/CapsuleGeometry.cjs");
class MMDPhysics {
/**
* @param {THREE.SkinnedMesh} mesh
* @param {Array<Object>} rigidBodyParams
* @param {Array<Object>} (optional) constraintParams
* @param {Object} params - (optional)
* @param {Number} params.unitStep - Default is 1 / 65.
* @param {Integer} params.maxStepNum - Default is 3.
* @param {Vector3} params.gravity - Default is ( 0, - 9.8 * 10, 0 )
*/
constructor(mesh, rigidBodyParams, constraintParams = [], params = {}) {
if (typeof Ammo === "undefined") {
throw new Error("THREE.MMDPhysics: Import ammo.js https://github.com/kripken/ammo.js");
}
this.manager = new ResourceManager();
this.mesh = mesh;
this.unitStep = params.unitStep !== void 0 ? params.unitStep : 1 / 65;
this.maxStepNum = params.maxStepNum !== void 0 ? params.maxStepNum : 3;
this.gravity = new THREE.Vector3(0, -9.8 * 10, 0);
if (params.gravity !== void 0)
this.gravity.copy(params.gravity);
this.world = params.world !== void 0 ? params.world : null;
this.bodies = [];
this.constraints = [];
this._init(mesh, rigidBodyParams, constraintParams);
}
/**
* Advances Physics calculation and updates bones.
*
* @param {Number} delta - time in second
* @return {MMDPhysics}
*/
update(delta) {
const manager = this.manager;
const mesh = this.mesh;
let isNonDefaultScale = false;
const position = manager.allocThreeVector3();
const quaternion = manager.allocThreeQuaternion();
const scale = manager.allocThreeVector3();
mesh.matrixWorld.decompose(position, quaternion, scale);
if (scale.x !== 1 || scale.y !== 1 || scale.z !== 1) {
isNonDefaultScale = true;
}
let parent;
if (isNonDefaultScale) {
parent = mesh.parent;
if (parent !== null)
mesh.parent = null;
scale.copy(this.mesh.scale);
mesh.scale.set(1, 1, 1);
mesh.updateMatrixWorld(true);
}
this._updateRigidBodies();
this._stepSimulation(delta);
this._updateBones();
if (isNonDefaultScale) {
if (parent !== null)
mesh.parent = parent;
mesh.scale.copy(scale);
}
manager.freeThreeVector3(scale);
manager.freeThreeQuaternion(quaternion);
manager.freeThreeVector3(position);
return this;
}
/**
* Resets rigid bodies transorm to current bone's.
*
* @return {MMDPhysics}
*/
reset() {
for (let i = 0, il = this.bodies.length; i < il; i++) {
this.bodies[i].reset();
}
return this;
}
/**
* Warm ups Rigid bodies. Calculates cycles steps.
*
* @param {Integer} cycles
* @return {MMDPhysics}
*/
warmup(cycles) {
for (let i = 0; i < cycles; i++) {
this.update(1 / 60);
}
return this;
}
/**
* Sets gravity.
*
* @param {Vector3} gravity
* @return {MMDPhysicsHelper}
*/
setGravity(gravity) {
this.world.setGravity(new Ammo.btVector3(gravity.x, gravity.y, gravity.z));
this.gravity.copy(gravity);
return this;
}
/**
* Creates MMDPhysicsHelper
*
* @return {MMDPhysicsHelper}
*/
createHelper() {
return new MMDPhysicsHelper(this.mesh, this);
}
// private methods
_init(mesh, rigidBodyParams, constraintParams) {
const manager = this.manager;
const parent = mesh.parent;
if (parent !== null)
mesh.parent = null;
const currentPosition = manager.allocThreeVector3();
const currentQuaternion = manager.allocThreeQuaternion();
const currentScale = manager.allocThreeVector3();
currentPosition.copy(mesh.position);
currentQuaternion.copy(mesh.quaternion);
currentScale.copy(mesh.scale);
mesh.position.set(0, 0, 0);
mesh.quaternion.set(0, 0, 0, 1);
mesh.scale.set(1, 1, 1);
mesh.updateMatrixWorld(true);
if (this.world === null) {
this.world = this._createWorld();
this.setGravity(this.gravity);
}
this._initRigidBodies(rigidBodyParams);
this._initConstraints(constraintParams);
if (parent !== null)
mesh.parent = parent;
mesh.position.copy(currentPosition);
mesh.quaternion.copy(currentQuaternion);
mesh.scale.copy(currentScale);
mesh.updateMatrixWorld(true);
this.reset();
manager.freeThreeVector3(currentPosition);
manager.freeThreeQuaternion(currentQuaternion);
manager.freeThreeVector3(currentScale);
}
_createWorld() {
const config = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(config);
const cache = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const world = new Ammo.btDiscreteDynamicsWorld(dispatcher, cache, solver, config);
return world;
}
_initRigidBodies(rigidBodies) {
for (let i = 0, il = rigidBodies.length; i < il; i++) {
this.bodies.push(new RigidBody(this.mesh, this.world, rigidBodies[i], this.manager));
}
}
_initConstraints(constraints) {
for (let i = 0, il = constraints.length; i < il; i++) {
const params = constraints[i];
const bodyA = this.bodies[params.rigidBodyIndex1];
const bodyB = this.bodies[params.rigidBodyIndex2];
this.constraints.push(new Constraint(this.mesh, this.world, bodyA, bodyB, params, this.manager));
}
}
_stepSimulation(delta) {
const unitStep = this.unitStep;
let stepTime = delta;
let maxStepNum = (delta / unitStep | 0) + 1;
if (stepTime < unitStep) {
stepTime = unitStep;
maxStepNum = 1;
}
if (maxStepNum > this.maxStepNum) {
maxStepNum = this.maxStepNum;
}
this.world.stepSimulation(stepTime, maxStepNum, unitStep);
}
_updateRigidBodies() {
for (let i = 0, il = this.bodies.length; i < il; i++) {
this.bodies[i].updateFromBone();
}
}
_updateBones() {
for (let i = 0, il = this.bodies.length; i < il; i++) {
this.bodies[i].updateBone();
}
}
}
class ResourceManager {
constructor() {
this.threeVector3s = [];
this.threeMatrix4s = [];
this.threeQuaternions = [];
this.threeEulers = [];
this.transforms = [];
this.quaternions = [];
this.vector3s = [];
}
allocThreeVector3() {
return this.threeVector3s.length > 0 ? this.threeVector3s.pop() : new THREE.Vector3();
}
freeThreeVector3(v) {
this.threeVector3s.push(v);
}
allocThreeMatrix4() {
return this.threeMatrix4s.length > 0 ? this.threeMatrix4s.pop() : new THREE.Matrix4();
}
freeThreeMatrix4(m) {
this.threeMatrix4s.push(m);
}
allocThreeQuaternion() {
return this.threeQuaternions.length > 0 ? this.threeQuaternions.pop() : new THREE.Quaternion();
}
freeThreeQuaternion(q) {
this.threeQuaternions.push(q);
}
allocThreeEuler() {
return this.threeEulers.length > 0 ? this.threeEulers.pop() : new THREE.Euler();
}
freeThreeEuler(e) {
this.threeEulers.push(e);
}
allocTransform() {
return this.transforms.length > 0 ? this.transforms.pop() : new Ammo.btTransform();
}
freeTransform(t) {
this.transforms.push(t);
}
allocQuaternion() {
return this.quaternions.length > 0 ? this.quaternions.pop() : new Ammo.btQuaternion();
}
freeQuaternion(q) {
this.quaternions.push(q);
}
allocVector3() {
return this.vector3s.length > 0 ? this.vector3s.pop() : new Ammo.btVector3();
}
freeVector3(v) {
this.vector3s.push(v);
}
setIdentity(t) {
t.setIdentity();
}
getBasis(t) {
var q = this.allocQuaternion();
t.getBasis().getRotation(q);
return q;
}
getBasisAsMatrix3(t) {
var q = this.getBasis(t);
var m = this.quaternionToMatrix3(q);
this.freeQuaternion(q);
return m;
}
getOrigin(t) {
return t.getOrigin();
}
setOrigin(t, v) {
t.getOrigin().setValue(v.x(), v.y(), v.z());
}
copyOrigin(t1, t2) {
var o = t2.getOrigin();
this.setOrigin(t1, o);
}
setBasis(t, q) {
t.setRotation(q);
}
setBasisFromMatrix3(t, m) {
var q = this.matrix3ToQuaternion(m);
this.setBasis(t, q);
this.freeQuaternion(q);
}
setOriginFromArray3(t, a) {
t.getOrigin().setValue(a[0], a[1], a[2]);
}
setOriginFromThreeVector3(t, v) {
t.getOrigin().setValue(v.x, v.y, v.z);
}
setBasisFromArray3(t, a) {
var thQ = this.allocThreeQuaternion();
var thE = this.allocThreeEuler();
thE.set(a[0], a[1], a[2]);
this.setBasisFromThreeQuaternion(t, thQ.setFromEuler(thE));
this.freeThreeEuler(thE);
this.freeThreeQuaternion(thQ);
}
setBasisFromThreeQuaternion(t, a) {
var q = this.allocQuaternion();
q.setX(a.x);
q.setY(a.y);
q.setZ(a.z);
q.setW(a.w);
this.setBasis(t, q);
this.freeQuaternion(q);
}
multiplyTransforms(t1, t2) {
var t = this.allocTransform();
this.setIdentity(t);
var m1 = this.getBasisAsMatrix3(t1);
var m2 = this.getBasisAsMatrix3(t2);
var o1 = this.getOrigin(t1);
var o2 = this.getOrigin(t2);
var v1 = this.multiplyMatrix3ByVector3(m1, o2);
var v2 = this.addVector3(v1, o1);
this.setOrigin(t, v2);
var m3 = this.multiplyMatrices3(m1, m2);
this.setBasisFromMatrix3(t, m3);
this.freeVector3(v1);
this.freeVector3(v2);
return t;
}
inverseTransform(t) {
var t2 = this.allocTransform();
var m1 = this.getBasisAsMatrix3(t);
var o = this.getOrigin(t);
var m2 = this.transposeMatrix3(m1);
var v1 = this.negativeVector3(o);
var v2 = this.multiplyMatrix3ByVector3(m2, v1);
this.setOrigin(t2, v2);
this.setBasisFromMatrix3(t2, m2);
this.freeVector3(v1);
this.freeVector3(v2);
return t2;
}
multiplyMatrices3(m1, m2) {
var m3 = [];
var v10 = this.rowOfMatrix3(m1, 0);
var v11 = this.rowOfMatrix3(m1, 1);
var v12 = this.rowOfMatrix3(m1, 2);
var v20 = this.columnOfMatrix3(m2, 0);
var v21 = this.columnOfMatrix3(m2, 1);
var v22 = this.columnOfMatrix3(m2, 2);
m3[0] = this.dotVectors3(v10, v20);
m3[1] = this.dotVectors3(v10, v21);
m3[2] = this.dotVectors3(v10, v22);
m3[3] = this.dotVectors3(v11, v20);
m3[4] = this.dotVectors3(v11, v21);
m3[5] = this.dotVectors3(v11, v22);
m3[6] = this.dotVectors3(v12, v20);
m3[7] = this.dotVectors3(v12, v21);
m3[8] = this.dotVectors3(v12, v22);
this.freeVector3(v10);
this.freeVector3(v11);
this.freeVector3(v12);
this.freeVector3(v20);
this.freeVector3(v21);
this.freeVector3(v22);
return m3;
}
addVector3(v1, v2) {
var v = this.allocVector3();
v.setValue(v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z());
return v;
}
dotVectors3(v1, v2) {
return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
}
rowOfMatrix3(m, i) {
var v = this.allocVector3();
v.setValue(m[i * 3 + 0], m[i * 3 + 1], m[i * 3 + 2]);
return v;
}
columnOfMatrix3(m, i) {
var v = this.allocVector3();
v.setValue(m[i + 0], m[i + 3], m[i + 6]);
return v;
}
negativeVector3(v) {
var v2 = this.allocVector3();
v2.setValue(-v.x(), -v.y(), -v.z());
return v2;
}
multiplyMatrix3ByVector3(m, v) {
var v4 = this.allocVector3();
var v0 = this.rowOfMatrix3(m, 0);
var v1 = this.rowOfMatrix3(m, 1);
var v2 = this.rowOfMatrix3(m, 2);
var x = this.dotVectors3(v0, v);
var y = this.dotVectors3(v1, v);
var z = this.dotVectors3(v2, v);
v4.setValue(x, y, z);
this.freeVector3(v0);
this.freeVector3(v1);
this.freeVector3(v2);
return v4;
}
transposeMatrix3(m) {
var m2 = [];
m2[0] = m[0];
m2[1] = m[3];
m2[2] = m[6];
m2[3] = m[1];
m2[4] = m[4];
m2[5] = m[7];
m2[6] = m[2];
m2[7] = m[5];
m2[8] = m[8];
return m2;
}
quaternionToMatrix3(q) {
var m = [];
var x = q.x();
var y = q.y();
var z = q.z();
var w = q.w();
var xx = x * x;
var yy = y * y;
var zz = z * z;
var xy = x * y;
var yz = y * z;
var zx = z * x;
var xw = x * w;
var yw = y * w;
var zw = z * w;
m[0] = 1 - 2 * (yy + zz);
m[1] = 2 * (xy - zw);
m[2] = 2 * (zx + yw);
m[3] = 2 * (xy + zw);
m[4] = 1 - 2 * (zz + xx);
m[5] = 2 * (yz - xw);
m[6] = 2 * (zx - yw);
m[7] = 2 * (yz + xw);
m[8] = 1 - 2 * (xx + yy);
return m;
}
matrix3ToQuaternion(m) {
var t = m[0] + m[4] + m[8];
var s, x, y, z, w;
if (t > 0) {
s = Math.sqrt(t + 1) * 2;
w = 0.25 * s;
x = (m[7] - m[5]) / s;
y = (m[2] - m[6]) / s;
z = (m[3] - m[1]) / s;
} else if (m[0] > m[4] && m[0] > m[8]) {
s = Math.sqrt(1 + m[0] - m[4] - m[8]) * 2;
w = (m[7] - m[5]) / s;
x = 0.25 * s;
y = (m[1] + m[3]) / s;
z = (m[2] + m[6]) / s;
} else if (m[4] > m[8]) {
s = Math.sqrt(1 + m[4] - m[0] - m[8]) * 2;
w = (m[2] - m[6]) / s;
x = (m[1] + m[3]) / s;
y = 0.25 * s;
z = (m[5] + m[7]) / s;
} else {
s = Math.sqrt(1 + m[8] - m[0] - m[4]) * 2;
w = (m[3] - m[1]) / s;
x = (m[2] + m[6]) / s;
y = (m[5] + m[7]) / s;
z = 0.25 * s;
}
var q = this.allocQuaternion();
q.setX(x);
q.setY(y);
q.setZ(z);
q.setW(w);
return q;
}
}
class RigidBody {
constructor(mesh, world, params, manager) {
this.mesh = mesh;
this.world = world;
this.params = params;
this.manager = manager;
this.body = null;
this.bone = null;
this.boneOffsetForm = null;
this.boneOffsetFormInverse = null;
this._init();
}
/**
* Resets rigid body transform to the current bone's.
*
* @return {RigidBody}
*/
reset() {
this._setTransformFromBone();
return this;
}
/**
* Updates rigid body's transform from the current bone.
*
* @return {RidigBody}
*/
updateFromBone() {
if (this.params.boneIndex !== -1 && this.params.type === 0) {
this._setTransformFromBone();
}
return this;
}
/**
* Updates bone from the current ridid body's transform.
*
* @return {RidigBody}
*/
updateBone() {
if (this.params.type === 0 || this.params.boneIndex === -1) {
return this;
}
this._updateBoneRotation();
if (this.params.type === 1) {
this._updateBonePosition();
}
this.bone.updateMatrixWorld(true);
if (this.params.type === 2) {
this._setPositionFromBone();
}
return this;
}
// private methods
_init() {
function generateShape(p) {
switch (p.shapeType) {
case 0:
return new Ammo.btSphereShape(p.width);
case 1:
return new Ammo.btBoxShape(new Ammo.btVector3(p.width, p.height, p.depth));
case 2:
return new Ammo.btCapsuleShape(p.width, p.height);
default:
throw new Error("unknown shape type " + p.shapeType);
}
}
const manager = this.manager;
const params = this.params;
const bones = this.mesh.skeleton.bones;
const bone = params.boneIndex === -1 ? new THREE.Bone() : bones[params.boneIndex];
const shape = generateShape(params);
const weight = params.type === 0 ? 0 : params.weight;
const localInertia = manager.allocVector3();
localInertia.setValue(0, 0, 0);
if (weight !== 0) {
shape.calculateLocalInertia(weight, localInertia);
}
const boneOffsetForm = manager.allocTransform();
manager.setIdentity(boneOffsetForm);
manager.setOriginFromArray3(boneOffsetForm, params.position);
manager.setBasisFromArray3(boneOffsetForm, params.rotation);
const vector = manager.allocThreeVector3();
const boneForm = manager.allocTransform();
manager.setIdentity(boneForm);
manager.setOriginFromThreeVector3(boneForm, bone.getWorldPosition(vector));
const form = manager.multiplyTransforms(boneForm, boneOffsetForm);
const state = new Ammo.btDefaultMotionState(form);
const info = new Ammo.btRigidBodyConstructionInfo(weight, state, shape, localInertia);
info.set_m_friction(params.friction);
info.set_m_restitution(params.restitution);
const body = new Ammo.btRigidBody(info);
if (params.type === 0) {
body.setCollisionFlags(body.getCollisionFlags() | 2);
body.setActivationState(4);
}
body.setDamping(params.positionDamping, params.rotationDamping);
body.setSleepingThresholds(0, 0);
this.world.addRigidBody(body, 1 << params.groupIndex, params.groupTarget);
this.body = body;
this.bone = bone;
this.boneOffsetForm = boneOffsetForm;
this.boneOffsetFormInverse = manager.inverseTransform(boneOffsetForm);
manager.freeVector3(localInertia);
manager.freeTransform(form);
manager.freeTransform(boneForm);
manager.freeThreeVector3(vector);
}
_getBoneTransform() {
const manager = this.manager;
const p = manager.allocThreeVector3();
const q = manager.allocThreeQuaternion();
const s = manager.allocThreeVector3();
this.bone.matrixWorld.decompose(p, q, s);
const tr = manager.allocTransform();
manager.setOriginFromThreeVector3(tr, p);
manager.setBasisFromThreeQuaternion(tr, q);
const form = manager.multiplyTransforms(tr, this.boneOffsetForm);
manager.freeTransform(tr);
manager.freeThreeVector3(s);
manager.freeThreeQuaternion(q);
manager.freeThreeVector3(p);
return form;
}
_getWorldTransformForBone() {
const manager = this.manager;
const tr = this.body.getCenterOfMassTransform();
return manager.multiplyTransforms(tr, this.boneOffsetFormInverse);
}
_setTransformFromBone() {
const manager = this.manager;
const form = this._getBoneTransform();
this.body.setCenterOfMassTransform(form);
this.body.getMotionState().setWorldTransform(form);
manager.freeTransform(form);
}
_setPositionFromBone() {
const manager = this.manager;
const form = this._getBoneTransform();
const tr = manager.allocTransform();
this.body.getMotionState().getWorldTransform(tr);
manager.copyOrigin(tr, form);
this.body.setCenterOfMassTransform(tr);
this.body.getMotionState().setWorldTransform(tr);
manager.freeTransform(tr);
manager.freeTransform(form);
}
_updateBoneRotation() {
const manager = this.manager;
const tr = this._getWorldTransformForBone();
const q = manager.getBasis(tr);
const thQ = manager.allocThreeQuaternion();
const thQ2 = manager.allocThreeQuaternion();
const thQ3 = manager.allocThreeQuaternion();
thQ.set(q.x(), q.y(), q.z(), q.w());
thQ2.setFromRotationMatrix(this.bone.matrixWorld);
thQ2.conjugate();
thQ2.multiply(thQ);
thQ3.setFromRotationMatrix(this.bone.matrix);
this.bone.quaternion.copy(thQ2.multiply(thQ3).normalize());
manager.freeThreeQuaternion(thQ);
manager.freeThreeQuaternion(thQ2);
manager.freeThreeQuaternion(thQ3);
manager.freeQuaternion(q);
manager.freeTransform(tr);
}
_updateBonePosition() {
const manager = this.manager;
const tr = this._getWorldTransformForBone();
const thV = manager.allocThreeVector3();
const o = manager.getOrigin(tr);
thV.set(o.x(), o.y(), o.z());
if (this.bone.parent) {
this.bone.parent.worldToLocal(thV);
}
this.bone.position.copy(thV);
manager.freeThreeVector3(thV);
manager.freeTransform(tr);
}
}
class Constraint {
/**
* @param {THREE.SkinnedMesh} mesh
* @param {Ammo.btDiscreteDynamicsWorld} world
* @param {RigidBody} bodyA
* @param {RigidBody} bodyB
* @param {Object} params
* @param {ResourceManager} manager
*/
constructor(mesh, world, bodyA, bodyB, params, manager) {
this.mesh = mesh;
this.world = world;
this.bodyA = bodyA;
this.bodyB = bodyB;
this.params = params;
this.manager = manager;
this.constraint = null;
this._init();
}
// private method
_init() {
const manager = this.manager;
const params = this.params;
const bodyA = this.bodyA;
const bodyB = this.bodyB;
const form = manager.allocTransform();
manager.setIdentity(form);
manager.setOriginFromArray3(form, params.position);
manager.setBasisFromArray3(form, params.rotation);
const formA = manager.allocTransform();
const formB = manager.allocTransform();
bodyA.body.getMotionState().getWorldTransform(formA);
bodyB.body.getMotionState().getWorldTransform(formB);
const formInverseA = manager.inverseTransform(formA);
const formInverseB = manager.inverseTransform(formB);
const formA2 = manager.multiplyTransforms(formInverseA, form);
const formB2 = manager.multiplyTransforms(formInverseB, form);
const constraint = new Ammo.btGeneric6DofSpringConstraint(bodyA.body, bodyB.body, formA2, formB2, true);
const lll = manager.allocVector3();
const lul = manager.allocVector3();
const all = manager.allocVector3();
const aul = manager.allocVector3();
lll.setValue(params.translationLimitation1[0], params.translationLimitation1[1], params.translationLimitation1[2]);
lul.setValue(params.translationLimitation2[0], params.translationLimitation2[1], params.translationLimitation2[2]);
all.setValue(params.rotationLimitation1[0], params.rotationLimitation1[1], params.rotationLimitation1[2]);
aul.setValue(params.rotationLimitation2[0], params.rotationLimitation2[1], params.rotationLimitation2[2]);
constraint.setLinearLowerLimit(lll);
constraint.setLinearUpperLimit(lul);
constraint.setAngularLowerLimit(all);
constraint.setAngularUpperLimit(aul);
for (let i = 0; i < 3; i++) {
if (params.springPosition[i] !== 0) {
constraint.enableSpring(i, true);
constraint.setStiffness(i, params.springPosition[i]);
}
}
for (let i = 0; i < 3; i++) {
if (params.springRotation[i] !== 0) {
constraint.enableSpring(i + 3, true);
constraint.setStiffness(i + 3, params.springRotation[i]);
}
}
if (constraint.setParam !== void 0) {
for (let i = 0; i < 6; i++) {
constraint.setParam(2, 0.475, i);
}
}
this.world.addConstraint(constraint, true);
this.constraint = constraint;
manager.freeTransform(form);
manager.freeTransform(formA);
manager.freeTransform(formB);
manager.freeTransform(formInverseA);
manager.freeTransform(formInverseB);
manager.freeTransform(formA2);
manager.freeTransform(formB2);
manager.freeVector3(lll);
manager.freeVector3(lul);
manager.freeVector3(all);
manager.freeVector3(aul);
}
}
const _position = /* @__PURE__ */ new THREE.Vector3();
const _quaternion = /* @__PURE__ */ new THREE.Quaternion();
const _scale = /* @__PURE__ */ new THREE.Vector3();
const _matrixWorldInv = /* @__PURE__ */ new THREE.Matrix4();
class MMDPhysicsHelper extends THREE.Object3D {
/**
* Visualize Rigid bodies
*
* @param {THREE.SkinnedMesh} mesh
* @param {Physics} physics
*/
constructor(mesh, physics) {
super();
this.root = mesh;
this.physics = physics;
this.matrix.copy(mesh.matrixWorld);
this.matrixAutoUpdate = false;
this.materials = [];
this.materials.push(
new THREE.MeshBasicMaterial({
color: new THREE.Color(16746632),
wireframe: true,
depthTest: false,
depthWrite: false,
opacity: 0.25,
transparent: true
})
);
this.materials.push(
new THREE.MeshBasicMaterial({
color: new THREE.Color(8978312),
wireframe: true,
depthTest: false,
depthWrite: false,
opacity: 0.25,
transparent: true
})
);
this.materials.push(
new THREE.MeshBasicMaterial({
color: new THREE.Color(8947967),
wireframe: true,
depthTest: false,
depthWrite: false,
opacity: 0.25,
transparent: true
})
);
this._init();
}
/**
* Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
*/
dispose() {
const materials = this.materials;
const children = this.children;
for (let i = 0; i < materials.length; i++) {
materials[i].dispose();
}
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.isMesh)
child.geometry.dispose();
}
}
/**
* Updates Rigid Bodies visualization.
*/
updateMatrixWorld(force) {
var mesh = this.root;
if (this.visible) {
var bodies = this.physics.bodies;
_matrixWorldInv.copy(mesh.matrixWorld).decompose(_position, _quaternion, _scale).compose(_position, _quaternion, _scale.set(1, 1, 1)).invert();
for (var i = 0, il = bodies.length; i < il; i++) {
var body = bodies[i].body;
var child = this.children[i];
var tr = body.getCenterOfMassTransform();
var origin = tr.getOrigin();
var rotation = tr.getRotation();
child.position.set(origin.x(), origin.y(), origin.z()).applyMatrix4(_matrixWorldInv);
child.quaternion.setFromRotationMatrix(_matrixWorldInv).multiply(_quaternion.set(rotation.x(), rotation.y(), rotation.z(), rotation.w()));
}
}
this.matrix.copy(mesh.matrixWorld).decompose(_position, _quaternion, _scale).compose(_position, _quaternion, _scale.set(1, 1, 1));
super.updateMatrixWorld(force);
}
// private method
_init() {
var bodies = this.physics.bodies;
function createGeometry(param2) {
switch (param2.shapeType) {
case 0:
return new THREE.SphereGeometry(param2.width, 16, 8);
case 1:
return new THREE.BoxGeometry(param2.width * 2, param2.height * 2, param2.depth * 2, 8, 8, 8);
case 2:
return new CapsuleGeometry.CapsuleGeometry(param2.width, param2.height, 8, 16);
default:
return null;
}
}
for (var i = 0, il = bodies.length; i < il; i++) {
var param = bodies[i].params;
this.add(new THREE.Mesh(createGeometry(param), this.materials[param.type]));
}
}
}
exports.MMDPhysics = MMDPhysics;
//# sourceMappingURL=MMDPhysics.cjs.map
File diff suppressed because one or more lines are too long
+119
View File
@@ -0,0 +1,119 @@
import { Bone, Euler, Matrix4, MeshBasicMaterial, Object3D, Quaternion, SkinnedMesh, Vector3 } from 'three'
export interface MMDPhysicsParameter {
unitStep?: number | undefined
maxStepNum?: number | undefined
gravity?: Vector3 | undefined
}
export class MMDPhysics {
constructor(mesh: SkinnedMesh, rigidBodyParams: object[], constraintParams?: object[], params?: MMDPhysicsParameter)
manager: ResourceManager
mesh: SkinnedMesh
unitStep: number
maxStepNum: number
gravity: Vector3
world: null
bodies: RigidBody[]
constraints: Constraint[]
update(delta: number): this
reset(): this
warmup(cycles: number): this
setGravity(gravity: Vector3): this
createHelper(): MMDPhysicsHelper
}
export class ResourceManager {
constructor()
threeVector3s: Vector3[]
threeMatrix4s: Matrix4[]
threeQuaternions: Quaternion[]
threeEulers: Euler[]
transforms: object[]
quaternions: object[]
vector3s: object[]
allocThreeVector3(): void
freeThreeVector3(v: Vector3): void
allocThreeMatrix4(): void
freeThreeMatrix4(m: Matrix4): void
allocThreeQuaternion(): void
freeThreeQuaternion(q: Quaternion): void
allocThreeEuler(): void
freeThreeEuler(e: Euler): void
allocTransform(): void
freeTransform(t: object): void
allocQuaternion(): void
freeQuaternion(q: object): void
allocVector3(): void
freeVector3(v: object): void
setIdentity(): void
getBasis(t: object): object
getBasisAsMatrix3(t: object): object
getOrigin(t: object): object
setOrigin(t: object, v: object): void
copyOrigin(t1: object, t2: object): void
setBasis(t: object, q: object): void
setBasisFromMatrix3(t: object, m: object): void
setOriginFromArray3(t: object, a: number[]): void
setOriginFromThreeVector3(t: object, v: Vector3): void
setBasisFromArray3(t: object, a: number[]): void
setBasisFromThreeQuaternion(t: object, a: Quaternion): void
multiplyTransforms(t1: object, t2: object): object
inverseTransform(t: object): object
multiplyMatrices3(m1: object, m2: object): object
addVector3(v1: object, v2: object): object
dotVectors3(v1: object, v2: object): number
rowOfMatrix3(m: object, i: number): object
columnOfMatrix3(m: object, i: number): object
negativeVector3(v: object): object
multiplyMatrix3ByVector3(m: object, v: object): object
transposeMatrix3(m: object): object
quaternionToMatrix3(q: object): object
matrix3ToQuaternion(m: object): object
}
export class RigidBody {
constructor(mesh: SkinnedMesh, world: object, params: object, manager: ResourceManager)
mesh: SkinnedMesh
world: object
params: object
manager: ResourceManager
body: object
bone: Bone
boneOffsetForm: object
boneOffsetFormInverse: object
reset(): this
updateFromBone(): this
updateBone(): this
}
export class Constraint {
constructor(
mesh: SkinnedMesh,
world: object,
bodyA: RigidBody,
bodyB: RigidBody,
params: object,
manager: ResourceManager,
)
mesh: SkinnedMesh
world: object
bodyA: RigidBody
bodyB: RigidBody
params: object
manager: ResourceManager
}
export class MMDPhysicsHelper extends Object3D {
mesh: SkinnedMesh
physics: MMDPhysics
materials: [MeshBasicMaterial, MeshBasicMaterial, MeshBasicMaterial]
constructor(mesh: SkinnedMesh, physics: MMDPhysics)
dispose(): void
}
+831
View File
@@ -0,0 +1,831 @@
import { Vector3, Matrix4, Quaternion, Euler, Bone, Object3D, MeshBasicMaterial, Color, Mesh, BoxGeometry, SphereGeometry } from "three";
import { CapsuleGeometry } from "../_polyfill/CapsuleGeometry.js";
class MMDPhysics {
/**
* @param {THREE.SkinnedMesh} mesh
* @param {Array<Object>} rigidBodyParams
* @param {Array<Object>} (optional) constraintParams
* @param {Object} params - (optional)
* @param {Number} params.unitStep - Default is 1 / 65.
* @param {Integer} params.maxStepNum - Default is 3.
* @param {Vector3} params.gravity - Default is ( 0, - 9.8 * 10, 0 )
*/
constructor(mesh, rigidBodyParams, constraintParams = [], params = {}) {
if (typeof Ammo === "undefined") {
throw new Error("THREE.MMDPhysics: Import ammo.js https://github.com/kripken/ammo.js");
}
this.manager = new ResourceManager();
this.mesh = mesh;
this.unitStep = params.unitStep !== void 0 ? params.unitStep : 1 / 65;
this.maxStepNum = params.maxStepNum !== void 0 ? params.maxStepNum : 3;
this.gravity = new Vector3(0, -9.8 * 10, 0);
if (params.gravity !== void 0)
this.gravity.copy(params.gravity);
this.world = params.world !== void 0 ? params.world : null;
this.bodies = [];
this.constraints = [];
this._init(mesh, rigidBodyParams, constraintParams);
}
/**
* Advances Physics calculation and updates bones.
*
* @param {Number} delta - time in second
* @return {MMDPhysics}
*/
update(delta) {
const manager = this.manager;
const mesh = this.mesh;
let isNonDefaultScale = false;
const position = manager.allocThreeVector3();
const quaternion = manager.allocThreeQuaternion();
const scale = manager.allocThreeVector3();
mesh.matrixWorld.decompose(position, quaternion, scale);
if (scale.x !== 1 || scale.y !== 1 || scale.z !== 1) {
isNonDefaultScale = true;
}
let parent;
if (isNonDefaultScale) {
parent = mesh.parent;
if (parent !== null)
mesh.parent = null;
scale.copy(this.mesh.scale);
mesh.scale.set(1, 1, 1);
mesh.updateMatrixWorld(true);
}
this._updateRigidBodies();
this._stepSimulation(delta);
this._updateBones();
if (isNonDefaultScale) {
if (parent !== null)
mesh.parent = parent;
mesh.scale.copy(scale);
}
manager.freeThreeVector3(scale);
manager.freeThreeQuaternion(quaternion);
manager.freeThreeVector3(position);
return this;
}
/**
* Resets rigid bodies transorm to current bone's.
*
* @return {MMDPhysics}
*/
reset() {
for (let i = 0, il = this.bodies.length; i < il; i++) {
this.bodies[i].reset();
}
return this;
}
/**
* Warm ups Rigid bodies. Calculates cycles steps.
*
* @param {Integer} cycles
* @return {MMDPhysics}
*/
warmup(cycles) {
for (let i = 0; i < cycles; i++) {
this.update(1 / 60);
}
return this;
}
/**
* Sets gravity.
*
* @param {Vector3} gravity
* @return {MMDPhysicsHelper}
*/
setGravity(gravity) {
this.world.setGravity(new Ammo.btVector3(gravity.x, gravity.y, gravity.z));
this.gravity.copy(gravity);
return this;
}
/**
* Creates MMDPhysicsHelper
*
* @return {MMDPhysicsHelper}
*/
createHelper() {
return new MMDPhysicsHelper(this.mesh, this);
}
// private methods
_init(mesh, rigidBodyParams, constraintParams) {
const manager = this.manager;
const parent = mesh.parent;
if (parent !== null)
mesh.parent = null;
const currentPosition = manager.allocThreeVector3();
const currentQuaternion = manager.allocThreeQuaternion();
const currentScale = manager.allocThreeVector3();
currentPosition.copy(mesh.position);
currentQuaternion.copy(mesh.quaternion);
currentScale.copy(mesh.scale);
mesh.position.set(0, 0, 0);
mesh.quaternion.set(0, 0, 0, 1);
mesh.scale.set(1, 1, 1);
mesh.updateMatrixWorld(true);
if (this.world === null) {
this.world = this._createWorld();
this.setGravity(this.gravity);
}
this._initRigidBodies(rigidBodyParams);
this._initConstraints(constraintParams);
if (parent !== null)
mesh.parent = parent;
mesh.position.copy(currentPosition);
mesh.quaternion.copy(currentQuaternion);
mesh.scale.copy(currentScale);
mesh.updateMatrixWorld(true);
this.reset();
manager.freeThreeVector3(currentPosition);
manager.freeThreeQuaternion(currentQuaternion);
manager.freeThreeVector3(currentScale);
}
_createWorld() {
const config = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(config);
const cache = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const world = new Ammo.btDiscreteDynamicsWorld(dispatcher, cache, solver, config);
return world;
}
_initRigidBodies(rigidBodies) {
for (let i = 0, il = rigidBodies.length; i < il; i++) {
this.bodies.push(new RigidBody(this.mesh, this.world, rigidBodies[i], this.manager));
}
}
_initConstraints(constraints) {
for (let i = 0, il = constraints.length; i < il; i++) {
const params = constraints[i];
const bodyA = this.bodies[params.rigidBodyIndex1];
const bodyB = this.bodies[params.rigidBodyIndex2];
this.constraints.push(new Constraint(this.mesh, this.world, bodyA, bodyB, params, this.manager));
}
}
_stepSimulation(delta) {
const unitStep = this.unitStep;
let stepTime = delta;
let maxStepNum = (delta / unitStep | 0) + 1;
if (stepTime < unitStep) {
stepTime = unitStep;
maxStepNum = 1;
}
if (maxStepNum > this.maxStepNum) {
maxStepNum = this.maxStepNum;
}
this.world.stepSimulation(stepTime, maxStepNum, unitStep);
}
_updateRigidBodies() {
for (let i = 0, il = this.bodies.length; i < il; i++) {
this.bodies[i].updateFromBone();
}
}
_updateBones() {
for (let i = 0, il = this.bodies.length; i < il; i++) {
this.bodies[i].updateBone();
}
}
}
class ResourceManager {
constructor() {
this.threeVector3s = [];
this.threeMatrix4s = [];
this.threeQuaternions = [];
this.threeEulers = [];
this.transforms = [];
this.quaternions = [];
this.vector3s = [];
}
allocThreeVector3() {
return this.threeVector3s.length > 0 ? this.threeVector3s.pop() : new Vector3();
}
freeThreeVector3(v) {
this.threeVector3s.push(v);
}
allocThreeMatrix4() {
return this.threeMatrix4s.length > 0 ? this.threeMatrix4s.pop() : new Matrix4();
}
freeThreeMatrix4(m) {
this.threeMatrix4s.push(m);
}
allocThreeQuaternion() {
return this.threeQuaternions.length > 0 ? this.threeQuaternions.pop() : new Quaternion();
}
freeThreeQuaternion(q) {
this.threeQuaternions.push(q);
}
allocThreeEuler() {
return this.threeEulers.length > 0 ? this.threeEulers.pop() : new Euler();
}
freeThreeEuler(e) {
this.threeEulers.push(e);
}
allocTransform() {
return this.transforms.length > 0 ? this.transforms.pop() : new Ammo.btTransform();
}
freeTransform(t) {
this.transforms.push(t);
}
allocQuaternion() {
return this.quaternions.length > 0 ? this.quaternions.pop() : new Ammo.btQuaternion();
}
freeQuaternion(q) {
this.quaternions.push(q);
}
allocVector3() {
return this.vector3s.length > 0 ? this.vector3s.pop() : new Ammo.btVector3();
}
freeVector3(v) {
this.vector3s.push(v);
}
setIdentity(t) {
t.setIdentity();
}
getBasis(t) {
var q = this.allocQuaternion();
t.getBasis().getRotation(q);
return q;
}
getBasisAsMatrix3(t) {
var q = this.getBasis(t);
var m = this.quaternionToMatrix3(q);
this.freeQuaternion(q);
return m;
}
getOrigin(t) {
return t.getOrigin();
}
setOrigin(t, v) {
t.getOrigin().setValue(v.x(), v.y(), v.z());
}
copyOrigin(t1, t2) {
var o = t2.getOrigin();
this.setOrigin(t1, o);
}
setBasis(t, q) {
t.setRotation(q);
}
setBasisFromMatrix3(t, m) {
var q = this.matrix3ToQuaternion(m);
this.setBasis(t, q);
this.freeQuaternion(q);
}
setOriginFromArray3(t, a) {
t.getOrigin().setValue(a[0], a[1], a[2]);
}
setOriginFromThreeVector3(t, v) {
t.getOrigin().setValue(v.x, v.y, v.z);
}
setBasisFromArray3(t, a) {
var thQ = this.allocThreeQuaternion();
var thE = this.allocThreeEuler();
thE.set(a[0], a[1], a[2]);
this.setBasisFromThreeQuaternion(t, thQ.setFromEuler(thE));
this.freeThreeEuler(thE);
this.freeThreeQuaternion(thQ);
}
setBasisFromThreeQuaternion(t, a) {
var q = this.allocQuaternion();
q.setX(a.x);
q.setY(a.y);
q.setZ(a.z);
q.setW(a.w);
this.setBasis(t, q);
this.freeQuaternion(q);
}
multiplyTransforms(t1, t2) {
var t = this.allocTransform();
this.setIdentity(t);
var m1 = this.getBasisAsMatrix3(t1);
var m2 = this.getBasisAsMatrix3(t2);
var o1 = this.getOrigin(t1);
var o2 = this.getOrigin(t2);
var v1 = this.multiplyMatrix3ByVector3(m1, o2);
var v2 = this.addVector3(v1, o1);
this.setOrigin(t, v2);
var m3 = this.multiplyMatrices3(m1, m2);
this.setBasisFromMatrix3(t, m3);
this.freeVector3(v1);
this.freeVector3(v2);
return t;
}
inverseTransform(t) {
var t2 = this.allocTransform();
var m1 = this.getBasisAsMatrix3(t);
var o = this.getOrigin(t);
var m2 = this.transposeMatrix3(m1);
var v1 = this.negativeVector3(o);
var v2 = this.multiplyMatrix3ByVector3(m2, v1);
this.setOrigin(t2, v2);
this.setBasisFromMatrix3(t2, m2);
this.freeVector3(v1);
this.freeVector3(v2);
return t2;
}
multiplyMatrices3(m1, m2) {
var m3 = [];
var v10 = this.rowOfMatrix3(m1, 0);
var v11 = this.rowOfMatrix3(m1, 1);
var v12 = this.rowOfMatrix3(m1, 2);
var v20 = this.columnOfMatrix3(m2, 0);
var v21 = this.columnOfMatrix3(m2, 1);
var v22 = this.columnOfMatrix3(m2, 2);
m3[0] = this.dotVectors3(v10, v20);
m3[1] = this.dotVectors3(v10, v21);
m3[2] = this.dotVectors3(v10, v22);
m3[3] = this.dotVectors3(v11, v20);
m3[4] = this.dotVectors3(v11, v21);
m3[5] = this.dotVectors3(v11, v22);
m3[6] = this.dotVectors3(v12, v20);
m3[7] = this.dotVectors3(v12, v21);
m3[8] = this.dotVectors3(v12, v22);
this.freeVector3(v10);
this.freeVector3(v11);
this.freeVector3(v12);
this.freeVector3(v20);
this.freeVector3(v21);
this.freeVector3(v22);
return m3;
}
addVector3(v1, v2) {
var v = this.allocVector3();
v.setValue(v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z());
return v;
}
dotVectors3(v1, v2) {
return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
}
rowOfMatrix3(m, i) {
var v = this.allocVector3();
v.setValue(m[i * 3 + 0], m[i * 3 + 1], m[i * 3 + 2]);
return v;
}
columnOfMatrix3(m, i) {
var v = this.allocVector3();
v.setValue(m[i + 0], m[i + 3], m[i + 6]);
return v;
}
negativeVector3(v) {
var v2 = this.allocVector3();
v2.setValue(-v.x(), -v.y(), -v.z());
return v2;
}
multiplyMatrix3ByVector3(m, v) {
var v4 = this.allocVector3();
var v0 = this.rowOfMatrix3(m, 0);
var v1 = this.rowOfMatrix3(m, 1);
var v2 = this.rowOfMatrix3(m, 2);
var x = this.dotVectors3(v0, v);
var y = this.dotVectors3(v1, v);
var z = this.dotVectors3(v2, v);
v4.setValue(x, y, z);
this.freeVector3(v0);
this.freeVector3(v1);
this.freeVector3(v2);
return v4;
}
transposeMatrix3(m) {
var m2 = [];
m2[0] = m[0];
m2[1] = m[3];
m2[2] = m[6];
m2[3] = m[1];
m2[4] = m[4];
m2[5] = m[7];
m2[6] = m[2];
m2[7] = m[5];
m2[8] = m[8];
return m2;
}
quaternionToMatrix3(q) {
var m = [];
var x = q.x();
var y = q.y();
var z = q.z();
var w = q.w();
var xx = x * x;
var yy = y * y;
var zz = z * z;
var xy = x * y;
var yz = y * z;
var zx = z * x;
var xw = x * w;
var yw = y * w;
var zw = z * w;
m[0] = 1 - 2 * (yy + zz);
m[1] = 2 * (xy - zw);
m[2] = 2 * (zx + yw);
m[3] = 2 * (xy + zw);
m[4] = 1 - 2 * (zz + xx);
m[5] = 2 * (yz - xw);
m[6] = 2 * (zx - yw);
m[7] = 2 * (yz + xw);
m[8] = 1 - 2 * (xx + yy);
return m;
}
matrix3ToQuaternion(m) {
var t = m[0] + m[4] + m[8];
var s, x, y, z, w;
if (t > 0) {
s = Math.sqrt(t + 1) * 2;
w = 0.25 * s;
x = (m[7] - m[5]) / s;
y = (m[2] - m[6]) / s;
z = (m[3] - m[1]) / s;
} else if (m[0] > m[4] && m[0] > m[8]) {
s = Math.sqrt(1 + m[0] - m[4] - m[8]) * 2;
w = (m[7] - m[5]) / s;
x = 0.25 * s;
y = (m[1] + m[3]) / s;
z = (m[2] + m[6]) / s;
} else if (m[4] > m[8]) {
s = Math.sqrt(1 + m[4] - m[0] - m[8]) * 2;
w = (m[2] - m[6]) / s;
x = (m[1] + m[3]) / s;
y = 0.25 * s;
z = (m[5] + m[7]) / s;
} else {
s = Math.sqrt(1 + m[8] - m[0] - m[4]) * 2;
w = (m[3] - m[1]) / s;
x = (m[2] + m[6]) / s;
y = (m[5] + m[7]) / s;
z = 0.25 * s;
}
var q = this.allocQuaternion();
q.setX(x);
q.setY(y);
q.setZ(z);
q.setW(w);
return q;
}
}
class RigidBody {
constructor(mesh, world, params, manager) {
this.mesh = mesh;
this.world = world;
this.params = params;
this.manager = manager;
this.body = null;
this.bone = null;
this.boneOffsetForm = null;
this.boneOffsetFormInverse = null;
this._init();
}
/**
* Resets rigid body transform to the current bone's.
*
* @return {RigidBody}
*/
reset() {
this._setTransformFromBone();
return this;
}
/**
* Updates rigid body's transform from the current bone.
*
* @return {RidigBody}
*/
updateFromBone() {
if (this.params.boneIndex !== -1 && this.params.type === 0) {
this._setTransformFromBone();
}
return this;
}
/**
* Updates bone from the current ridid body's transform.
*
* @return {RidigBody}
*/
updateBone() {
if (this.params.type === 0 || this.params.boneIndex === -1) {
return this;
}
this._updateBoneRotation();
if (this.params.type === 1) {
this._updateBonePosition();
}
this.bone.updateMatrixWorld(true);
if (this.params.type === 2) {
this._setPositionFromBone();
}
return this;
}
// private methods
_init() {
function generateShape(p) {
switch (p.shapeType) {
case 0:
return new Ammo.btSphereShape(p.width);
case 1:
return new Ammo.btBoxShape(new Ammo.btVector3(p.width, p.height, p.depth));
case 2:
return new Ammo.btCapsuleShape(p.width, p.height);
default:
throw new Error("unknown shape type " + p.shapeType);
}
}
const manager = this.manager;
const params = this.params;
const bones = this.mesh.skeleton.bones;
const bone = params.boneIndex === -1 ? new Bone() : bones[params.boneIndex];
const shape = generateShape(params);
const weight = params.type === 0 ? 0 : params.weight;
const localInertia = manager.allocVector3();
localInertia.setValue(0, 0, 0);
if (weight !== 0) {
shape.calculateLocalInertia(weight, localInertia);
}
const boneOffsetForm = manager.allocTransform();
manager.setIdentity(boneOffsetForm);
manager.setOriginFromArray3(boneOffsetForm, params.position);
manager.setBasisFromArray3(boneOffsetForm, params.rotation);
const vector = manager.allocThreeVector3();
const boneForm = manager.allocTransform();
manager.setIdentity(boneForm);
manager.setOriginFromThreeVector3(boneForm, bone.getWorldPosition(vector));
const form = manager.multiplyTransforms(boneForm, boneOffsetForm);
const state = new Ammo.btDefaultMotionState(form);
const info = new Ammo.btRigidBodyConstructionInfo(weight, state, shape, localInertia);
info.set_m_friction(params.friction);
info.set_m_restitution(params.restitution);
const body = new Ammo.btRigidBody(info);
if (params.type === 0) {
body.setCollisionFlags(body.getCollisionFlags() | 2);
body.setActivationState(4);
}
body.setDamping(params.positionDamping, params.rotationDamping);
body.setSleepingThresholds(0, 0);
this.world.addRigidBody(body, 1 << params.groupIndex, params.groupTarget);
this.body = body;
this.bone = bone;
this.boneOffsetForm = boneOffsetForm;
this.boneOffsetFormInverse = manager.inverseTransform(boneOffsetForm);
manager.freeVector3(localInertia);
manager.freeTransform(form);
manager.freeTransform(boneForm);
manager.freeThreeVector3(vector);
}
_getBoneTransform() {
const manager = this.manager;
const p = manager.allocThreeVector3();
const q = manager.allocThreeQuaternion();
const s = manager.allocThreeVector3();
this.bone.matrixWorld.decompose(p, q, s);
const tr = manager.allocTransform();
manager.setOriginFromThreeVector3(tr, p);
manager.setBasisFromThreeQuaternion(tr, q);
const form = manager.multiplyTransforms(tr, this.boneOffsetForm);
manager.freeTransform(tr);
manager.freeThreeVector3(s);
manager.freeThreeQuaternion(q);
manager.freeThreeVector3(p);
return form;
}
_getWorldTransformForBone() {
const manager = this.manager;
const tr = this.body.getCenterOfMassTransform();
return manager.multiplyTransforms(tr, this.boneOffsetFormInverse);
}
_setTransformFromBone() {
const manager = this.manager;
const form = this._getBoneTransform();
this.body.setCenterOfMassTransform(form);
this.body.getMotionState().setWorldTransform(form);
manager.freeTransform(form);
}
_setPositionFromBone() {
const manager = this.manager;
const form = this._getBoneTransform();
const tr = manager.allocTransform();
this.body.getMotionState().getWorldTransform(tr);
manager.copyOrigin(tr, form);
this.body.setCenterOfMassTransform(tr);
this.body.getMotionState().setWorldTransform(tr);
manager.freeTransform(tr);
manager.freeTransform(form);
}
_updateBoneRotation() {
const manager = this.manager;
const tr = this._getWorldTransformForBone();
const q = manager.getBasis(tr);
const thQ = manager.allocThreeQuaternion();
const thQ2 = manager.allocThreeQuaternion();
const thQ3 = manager.allocThreeQuaternion();
thQ.set(q.x(), q.y(), q.z(), q.w());
thQ2.setFromRotationMatrix(this.bone.matrixWorld);
thQ2.conjugate();
thQ2.multiply(thQ);
thQ3.setFromRotationMatrix(this.bone.matrix);
this.bone.quaternion.copy(thQ2.multiply(thQ3).normalize());
manager.freeThreeQuaternion(thQ);
manager.freeThreeQuaternion(thQ2);
manager.freeThreeQuaternion(thQ3);
manager.freeQuaternion(q);
manager.freeTransform(tr);
}
_updateBonePosition() {
const manager = this.manager;
const tr = this._getWorldTransformForBone();
const thV = manager.allocThreeVector3();
const o = manager.getOrigin(tr);
thV.set(o.x(), o.y(), o.z());
if (this.bone.parent) {
this.bone.parent.worldToLocal(thV);
}
this.bone.position.copy(thV);
manager.freeThreeVector3(thV);
manager.freeTransform(tr);
}
}
class Constraint {
/**
* @param {THREE.SkinnedMesh} mesh
* @param {Ammo.btDiscreteDynamicsWorld} world
* @param {RigidBody} bodyA
* @param {RigidBody} bodyB
* @param {Object} params
* @param {ResourceManager} manager
*/
constructor(mesh, world, bodyA, bodyB, params, manager) {
this.mesh = mesh;
this.world = world;
this.bodyA = bodyA;
this.bodyB = bodyB;
this.params = params;
this.manager = manager;
this.constraint = null;
this._init();
}
// private method
_init() {
const manager = this.manager;
const params = this.params;
const bodyA = this.bodyA;
const bodyB = this.bodyB;
const form = manager.allocTransform();
manager.setIdentity(form);
manager.setOriginFromArray3(form, params.position);
manager.setBasisFromArray3(form, params.rotation);
const formA = manager.allocTransform();
const formB = manager.allocTransform();
bodyA.body.getMotionState().getWorldTransform(formA);
bodyB.body.getMotionState().getWorldTransform(formB);
const formInverseA = manager.inverseTransform(formA);
const formInverseB = manager.inverseTransform(formB);
const formA2 = manager.multiplyTransforms(formInverseA, form);
const formB2 = manager.multiplyTransforms(formInverseB, form);
const constraint = new Ammo.btGeneric6DofSpringConstraint(bodyA.body, bodyB.body, formA2, formB2, true);
const lll = manager.allocVector3();
const lul = manager.allocVector3();
const all = manager.allocVector3();
const aul = manager.allocVector3();
lll.setValue(params.translationLimitation1[0], params.translationLimitation1[1], params.translationLimitation1[2]);
lul.setValue(params.translationLimitation2[0], params.translationLimitation2[1], params.translationLimitation2[2]);
all.setValue(params.rotationLimitation1[0], params.rotationLimitation1[1], params.rotationLimitation1[2]);
aul.setValue(params.rotationLimitation2[0], params.rotationLimitation2[1], params.rotationLimitation2[2]);
constraint.setLinearLowerLimit(lll);
constraint.setLinearUpperLimit(lul);
constraint.setAngularLowerLimit(all);
constraint.setAngularUpperLimit(aul);
for (let i = 0; i < 3; i++) {
if (params.springPosition[i] !== 0) {
constraint.enableSpring(i, true);
constraint.setStiffness(i, params.springPosition[i]);
}
}
for (let i = 0; i < 3; i++) {
if (params.springRotation[i] !== 0) {
constraint.enableSpring(i + 3, true);
constraint.setStiffness(i + 3, params.springRotation[i]);
}
}
if (constraint.setParam !== void 0) {
for (let i = 0; i < 6; i++) {
constraint.setParam(2, 0.475, i);
}
}
this.world.addConstraint(constraint, true);
this.constraint = constraint;
manager.freeTransform(form);
manager.freeTransform(formA);
manager.freeTransform(formB);
manager.freeTransform(formInverseA);
manager.freeTransform(formInverseB);
manager.freeTransform(formA2);
manager.freeTransform(formB2);
manager.freeVector3(lll);
manager.freeVector3(lul);
manager.freeVector3(all);
manager.freeVector3(aul);
}
}
const _position = /* @__PURE__ */ new Vector3();
const _quaternion = /* @__PURE__ */ new Quaternion();
const _scale = /* @__PURE__ */ new Vector3();
const _matrixWorldInv = /* @__PURE__ */ new Matrix4();
class MMDPhysicsHelper extends Object3D {
/**
* Visualize Rigid bodies
*
* @param {THREE.SkinnedMesh} mesh
* @param {Physics} physics
*/
constructor(mesh, physics) {
super();
this.root = mesh;
this.physics = physics;
this.matrix.copy(mesh.matrixWorld);
this.matrixAutoUpdate = false;
this.materials = [];
this.materials.push(
new MeshBasicMaterial({
color: new Color(16746632),
wireframe: true,
depthTest: false,
depthWrite: false,
opacity: 0.25,
transparent: true
})
);
this.materials.push(
new MeshBasicMaterial({
color: new Color(8978312),
wireframe: true,
depthTest: false,
depthWrite: false,
opacity: 0.25,
transparent: true
})
);
this.materials.push(
new MeshBasicMaterial({
color: new Color(8947967),
wireframe: true,
depthTest: false,
depthWrite: false,
opacity: 0.25,
transparent: true
})
);
this._init();
}
/**
* Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
*/
dispose() {
const materials = this.materials;
const children = this.children;
for (let i = 0; i < materials.length; i++) {
materials[i].dispose();
}
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.isMesh)
child.geometry.dispose();
}
}
/**
* Updates Rigid Bodies visualization.
*/
updateMatrixWorld(force) {
var mesh = this.root;
if (this.visible) {
var bodies = this.physics.bodies;
_matrixWorldInv.copy(mesh.matrixWorld).decompose(_position, _quaternion, _scale).compose(_position, _quaternion, _scale.set(1, 1, 1)).invert();
for (var i = 0, il = bodies.length; i < il; i++) {
var body = bodies[i].body;
var child = this.children[i];
var tr = body.getCenterOfMassTransform();
var origin = tr.getOrigin();
var rotation = tr.getRotation();
child.position.set(origin.x(), origin.y(), origin.z()).applyMatrix4(_matrixWorldInv);
child.quaternion.setFromRotationMatrix(_matrixWorldInv).multiply(_quaternion.set(rotation.x(), rotation.y(), rotation.z(), rotation.w()));
}
}
this.matrix.copy(mesh.matrixWorld).decompose(_position, _quaternion, _scale).compose(_position, _quaternion, _scale.set(1, 1, 1));
super.updateMatrixWorld(force);
}
// private method
_init() {
var bodies = this.physics.bodies;
function createGeometry(param2) {
switch (param2.shapeType) {
case 0:
return new SphereGeometry(param2.width, 16, 8);
case 1:
return new BoxGeometry(param2.width * 2, param2.height * 2, param2.depth * 2, 8, 8, 8);
case 2:
return new CapsuleGeometry(param2.width, param2.height, 8, 16);
default:
return null;
}
}
for (var i = 0, il = bodies.length; i < il; i++) {
var param = bodies[i].params;
this.add(new Mesh(createGeometry(param), this.materials[param.type]));
}
}
}
export {
MMDPhysics
};
//# sourceMappingURL=MMDPhysics.js.map
File diff suppressed because one or more lines are too long