|
@@ -6921,7 +6921,8 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
|
|
|
materials: {},
|
|
|
textures: {},
|
|
|
images: {},
|
|
|
- shapes: {}
|
|
|
+ shapes: {},
|
|
|
+ skeletons: {}
|
|
|
};
|
|
|
|
|
|
output.metadata = {
|
|
@@ -7006,6 +7007,21 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
|
|
|
|
|
|
}
|
|
|
|
|
|
+ if ( this.isSkinnedMesh ) {
|
|
|
+
|
|
|
+ object.bindMode = this.bindMode;
|
|
|
+ object.bindMatrix = this.bindMatrix.toArray();
|
|
|
+
|
|
|
+ if ( this.skeleton !== undefined ) {
|
|
|
+
|
|
|
+ serialize( meta.skeletons, this.skeleton );
|
|
|
+
|
|
|
+ object.skeleton = this.skeleton.uuid;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
if ( this.material !== undefined ) {
|
|
|
|
|
|
if ( Array.isArray( this.material ) ) {
|
|
@@ -7049,12 +7065,14 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
|
|
|
const textures = extractFromCache( meta.textures );
|
|
|
const images = extractFromCache( meta.images );
|
|
|
const shapes = extractFromCache( meta.shapes );
|
|
|
+ const skeletons = extractFromCache( meta.skeletons );
|
|
|
|
|
|
if ( geometries.length > 0 ) output.geometries = geometries;
|
|
|
if ( materials.length > 0 ) output.materials = materials;
|
|
|
if ( textures.length > 0 ) output.textures = textures;
|
|
|
if ( images.length > 0 ) output.images = images;
|
|
|
if ( shapes.length > 0 ) output.shapes = shapes;
|
|
|
+ if ( skeletons.length > 0 ) output.skeletons = skeletons;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -26202,55 +26220,78 @@ SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
|
|
|
|
|
|
} );
|
|
|
|
|
|
+function Bone() {
|
|
|
+
|
|
|
+ Object3D.call( this );
|
|
|
+
|
|
|
+ this.type = 'Bone';
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+Bone.prototype = Object.assign( Object.create( Object3D.prototype ), {
|
|
|
+
|
|
|
+ constructor: Bone,
|
|
|
+
|
|
|
+ isBone: true
|
|
|
+
|
|
|
+} );
|
|
|
+
|
|
|
const _offsetMatrix = new Matrix4();
|
|
|
const _identityMatrix = new Matrix4();
|
|
|
|
|
|
-function Skeleton( bones, boneInverses ) {
|
|
|
-
|
|
|
- // copy the bone array
|
|
|
+function Skeleton( bones = [], boneInverses = [] ) {
|
|
|
|
|
|
- bones = bones || [];
|
|
|
+ this.uuid = MathUtils.generateUUID();
|
|
|
|
|
|
this.bones = bones.slice( 0 );
|
|
|
- this.boneMatrices = new Float32Array( this.bones.length * 16 );
|
|
|
+ this.boneInverses = boneInverses;
|
|
|
|
|
|
this.frame = - 1;
|
|
|
|
|
|
- // use the supplied bone inverses or calculate the inverses
|
|
|
+ this.init();
|
|
|
|
|
|
- if ( boneInverses === undefined ) {
|
|
|
+}
|
|
|
|
|
|
- this.calculateInverses();
|
|
|
+Object.assign( Skeleton.prototype, {
|
|
|
|
|
|
- } else {
|
|
|
+ init: function () {
|
|
|
+
|
|
|
+ const bones = this.bones;
|
|
|
+ const boneInverses = this.boneInverses;
|
|
|
+
|
|
|
+ this.boneMatrices = new Float32Array( bones.length * 16 );
|
|
|
|
|
|
- if ( this.bones.length === boneInverses.length ) {
|
|
|
+ // calculate inverse bone matrices if necessary
|
|
|
|
|
|
- this.boneInverses = boneInverses.slice( 0 );
|
|
|
+ if ( boneInverses.length === 0 ) {
|
|
|
+
|
|
|
+ this.calculateInverses();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- console.warn( 'THREE.Skeleton boneInverses is the wrong length.' );
|
|
|
+ // handle special case
|
|
|
|
|
|
- this.boneInverses = [];
|
|
|
+ if ( bones.length !== boneInverses.length ) {
|
|
|
|
|
|
- for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
|
|
+ console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' );
|
|
|
|
|
|
- this.boneInverses.push( new Matrix4() );
|
|
|
+ this.boneInverses = [];
|
|
|
|
|
|
- }
|
|
|
+ for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ this.boneInverses.push( new Matrix4() );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
-}
|
|
|
+ }
|
|
|
|
|
|
-Object.assign( Skeleton.prototype, {
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
|
|
|
calculateInverses: function () {
|
|
|
|
|
|
- this.boneInverses = [];
|
|
|
+ this.boneInverses.length = 0;
|
|
|
|
|
|
for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
|
|
|
|
|
@@ -26373,23 +26414,65 @@ Object.assign( Skeleton.prototype, {
|
|
|
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
+ },
|
|
|
|
|
|
-} );
|
|
|
+ fromJSON: function ( json, bones ) {
|
|
|
|
|
|
-function Bone() {
|
|
|
+ this.uuid = json.uuid;
|
|
|
|
|
|
- Object3D.call( this );
|
|
|
+ for ( let i = 0, l = json.bones.length; i < l; i ++ ) {
|
|
|
|
|
|
- this.type = 'Bone';
|
|
|
+ const uuid = json.bones[ i ];
|
|
|
+ let bone = bones[ uuid ];
|
|
|
|
|
|
-}
|
|
|
+ if ( bone === undefined ) {
|
|
|
|
|
|
-Bone.prototype = Object.assign( Object.create( Object3D.prototype ), {
|
|
|
+ console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid );
|
|
|
+ bone = new Bone();
|
|
|
|
|
|
- constructor: Bone,
|
|
|
+ }
|
|
|
|
|
|
- isBone: true
|
|
|
+ this.bones.push( bone );
|
|
|
+ this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.init();
|
|
|
+
|
|
|
+ return this;
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ toJSON: function () {
|
|
|
+
|
|
|
+ const data = {
|
|
|
+ metadata: {
|
|
|
+ version: 4.5,
|
|
|
+ type: 'Skeleton',
|
|
|
+ generator: 'Skeleton.toJSON'
|
|
|
+ },
|
|
|
+ bones: [],
|
|
|
+ boneInverses: []
|
|
|
+ };
|
|
|
+
|
|
|
+ data.uuid = this.uuid;
|
|
|
+
|
|
|
+ const bones = this.bones;
|
|
|
+ const boneInverses = this.boneInverses;
|
|
|
+
|
|
|
+ for ( let i = 0, l = bones.length; i < l; i ++ ) {
|
|
|
+
|
|
|
+ const bone = bones[ i ];
|
|
|
+ data.bones.push( bone.uuid );
|
|
|
+
|
|
|
+ const boneInverse = boneInverses[ i ];
|
|
|
+ data.boneInverses.push( boneInverse.toArray() );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return data;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
} );
|
|
|
|
|
@@ -40764,7 +40847,7 @@ class ObjectLoader extends Loader {
|
|
|
|
|
|
parse( json, onLoad ) {
|
|
|
|
|
|
- const shapes = this.parseShape( json.shapes );
|
|
|
+ const shapes = this.parseShapes( json.shapes );
|
|
|
const geometries = this.parseGeometries( json.geometries, shapes );
|
|
|
|
|
|
const images = this.parseImages( json.images, function () {
|
|
@@ -40777,6 +40860,9 @@ class ObjectLoader extends Loader {
|
|
|
const materials = this.parseMaterials( json.materials, textures );
|
|
|
|
|
|
const object = this.parseObject( json.object, geometries, materials );
|
|
|
+ const skeletons = this.parseSkeletons( json.skeletons, object );
|
|
|
+
|
|
|
+ this.bindSkeletons( object, skeletons );
|
|
|
|
|
|
if ( json.animations ) {
|
|
|
|
|
@@ -40794,7 +40880,7 @@ class ObjectLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
- parseShape( json ) {
|
|
|
+ parseShapes( json ) {
|
|
|
|
|
|
const shapes = {};
|
|
|
|
|
@@ -40814,6 +40900,37 @@ class ObjectLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ parseSkeletons( json, object ) {
|
|
|
+
|
|
|
+ const skeletons = {};
|
|
|
+ const bones = {};
|
|
|
+
|
|
|
+ // generate bone lookup table
|
|
|
+
|
|
|
+ object.traverse( function ( child ) {
|
|
|
+
|
|
|
+ if ( child.isBone ) bones[ child.uuid ] = child;
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ // create skeletons
|
|
|
+
|
|
|
+ if ( json !== undefined ) {
|
|
|
+
|
|
|
+ for ( let i = 0, l = json.length; i < l; i ++ ) {
|
|
|
+
|
|
|
+ const skeleton = new Skeleton().fromJSON( json[ i ], bones );
|
|
|
+
|
|
|
+ skeletons[ skeleton.uuid ] = skeleton;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return skeletons;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
parseGeometries( json, shapes ) {
|
|
|
|
|
|
const geometries = {};
|
|
@@ -41480,7 +41597,16 @@ class ObjectLoader extends Loader {
|
|
|
|
|
|
case 'SkinnedMesh':
|
|
|
|
|
|
- console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' );
|
|
|
+ geometry = getGeometry( data.geometry );
|
|
|
+ material = getMaterial( data.material );
|
|
|
+
|
|
|
+ object = new SkinnedMesh( geometry, material );
|
|
|
+
|
|
|
+ if ( data.bindMode !== undefined ) object.bindMode = data.bindMode;
|
|
|
+ if ( data.bindMatrix !== undefined ) object.bindMatrix.fromArray( data.bindMatrix );
|
|
|
+ if ( data.skeleton !== undefined ) object.skeleton = data.skeleton;
|
|
|
+
|
|
|
+ break;
|
|
|
|
|
|
case 'Mesh':
|
|
|
|
|
@@ -41546,6 +41672,12 @@ class ObjectLoader extends Loader {
|
|
|
|
|
|
break;
|
|
|
|
|
|
+ case 'Bone':
|
|
|
+
|
|
|
+ object = new Bone();
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
|
|
|
object = new Object3D();
|
|
@@ -41628,6 +41760,32 @@ class ObjectLoader extends Loader {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ bindSkeletons( object, skeletons ) {
|
|
|
+
|
|
|
+ if ( Object.keys( skeletons ).length === 0 ) return;
|
|
|
+
|
|
|
+ object.traverse( function ( child ) {
|
|
|
+
|
|
|
+ if ( child.isSkinnedMesh === true && child.skeleton !== undefined ) {
|
|
|
+
|
|
|
+ const skeleton = skeletons[ child.skeleton ];
|
|
|
+
|
|
|
+ if ( skeleton === undefined ) {
|
|
|
+
|
|
|
+ console.warn( 'THREE.ObjectLoader: No skeleton found with UUID:', child.skeleton );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ child.bind( skeleton, child.bindMatrix );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
/* DEPRECATED */
|
|
|
|
|
|
setTexturePath( value ) {
|