diff --git a/docs/api/en/loaders/ObjectLoader.html b/docs/api/en/loaders/ObjectLoader.html
index 75c8acc58e713f..12abe23b38531e 100644
--- a/docs/api/en/loaders/ObjectLoader.html
+++ b/docs/api/en/loaders/ObjectLoader.html
@@ -88,6 +88,14 @@
[property:String resourcePath]
Methods
+ [method:ObjectLoader bindSkeletons]( [param:Object3D object], [param:Array skeletons] )
+
+ [page:Object3D object] — required. The parsed object hierarchy.
+ [page:Array skeletons] — required. An array of parsed skeletons.
+
+ Used to bind skeletons to objects of type [page:SkinnedMesh].
+
+
[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )
[page:String url] — the path or URL to the file. This can also be a
@@ -100,7 +108,6 @@
[method:null load]( [param:String url], [param:Function onLoad], [param:Func
Begin loading from url and call onLoad with the parsed response content.
-
[method:Object3D parse]( [param:Object json], [param:Function onLoad] )
[page:Object json] — required. The JSON source to parse.
@@ -111,46 +118,41 @@
[method:Object3D parse]( [param:Object json], [param:Function onLoad] )
- [method:Object3D parseGeometries]( [param:Object json] )
+ [method:Array parseAnimations]( [param:Object json] )
[page:Object json] — required. The JSON source to parse.
- This is used [page:.parse] to parse any [page:Geometry geometries] or [page:BufferGeometry buffer geometries] in the JSON structure.
+ This is used by [page:.parse]() to parse any animations in the JSON structure, using [page:AnimationClip.parse]().
- [method:Object3D parseMaterials]( [param:Object json] )
+ [method:Object parseGeometries]( [param:Object json] )
[page:Object json] — required. The JSON source to parse.
- This is used [page:.parse] to parse any materials in the JSON structure using [page:MaterialLoader].
+ This is used by [page:.parse]() to parse any geometries in the JSON structure.
- [method:Object3D parseAnimations]( [param:Object json] )
-
- [page:Object json] — required. The JSON source to parse.
-
- This is used [page:.parse] to parse any animations in the JSON structure, using [page:AnimationClip.parse].
-
-
- [method:Object3D parseImages]( [param:Object json] )
+ [method:Object parseImages]( [param:Object json] )
[page:Object json] — required. The JSON source to parse.
This is used [page:.parse] to parse any images in the JSON structure, using [page:ImageLoader].
- [method:Object3D parseTextures]( [param:Object json] )
+ [method:Object parseMaterials]( [param:Object json] )
[page:Object json] — required. The JSON source to parse.
- This is used [page:.parse] to parse any textures in the JSON structure.
+ This is used by [page:.parse]() to parse any materials in the JSON structure using [page:MaterialLoader].
- [method:Object3D parseObject]( [param:Object json] )
+ [method:Object3D parseObject]( [param:Object data], [param:Object geometries], [param:Object materials] )
- [page:Object json] — required. The JSON source to parse.
+ [page:Object data] — required. The JSON source to parse.
+ [page:Object geometries] — The parsed geometries.
+ [page:Object materials] — The parsed materials.
- This is used [page:.parse] to parse any objects in the JSON structure.
+ This is used by [page:.parse]() to parse any objects in the JSON structure.
Objects can be of the following types:
@@ -178,9 +180,15 @@ [method:Object3D parseObject]( [param:Object json] )
-
[page:HemisphereLight]
+ -
+ [page:SkinnedMesh]
+
-
[page:Mesh]
+ -
+ [page:Bone]
+
-
[page:LOD]
@@ -206,6 +214,28 @@ [method:Object3D parseObject]( [param:Object json] )
+ [method:Object parseShapes]( [param:Object json] )
+
+ [page:Object json] — required. The JSON source to parse.
+
+ This is used by [page:.parse]() to parse any shapes in the JSON structure.
+
+
+ [method:Object parseSkeletons]( [param:Object json], [param:Object3D object] )
+
+ [page:Object json] — required. The JSON source to parse.
+ [page:Object3D object] — required. A hierarchy of 3D objects which is used to query the bones.
+
+ This is used by [page:.parse]() to parse any skeletons in the JSON structure.
+
+
+ [method:Object parseTextures]( [param:Object json] )
+
+ [page:Object json] — required. The JSON source to parse.
+
+ This is used by [page:.parse]() to parse any textures in the JSON structure.
+
+
[method:ObjectLoader setCrossOrigin]( [param:String value] )
[page:String value] — The crossOrigin string to implement CORS for loading the url from a different domain that allows CORS.
diff --git a/docs/api/en/objects/Skeleton.html b/docs/api/en/objects/Skeleton.html
index b5c94d851b5898..194352ccfa30ca 100644
--- a/docs/api/en/objects/Skeleton.html
+++ b/docs/api/en/objects/Skeleton.html
@@ -79,6 +79,11 @@
[property:DataTexture boneTexture]
The [page:DataTexture] holding the bone data when using a vertex texture.
+ [property:Number boneTextureSize]
+
+ The size of the bone texture.
+
+
Methods
@@ -92,10 +97,22 @@ [method:null calculateInverses]()
Generates the [page:.boneInverses boneInverses] array if not provided in the constructor.
+ [method:Skeleton fromJSON]( [param:Object json], [param:Object3D object])
+ Creates the skeleton by the given JSON data and object hierarchy.
+
+
+ [method:Skeleton init]()
+ Initializes [page:.boneMatrices] and [page:.boneInverses] if necessary.
+
+
[method:null pose]()
Returns the skeleton to the base pose.
+ [method:Object toJSON]()
+ Serializes this skeleton object.
+
+
[method:null update]()
Updates the [page:Float32Array boneMatrices] and [page:DataTexture boneTexture] after changing the bones.
diff --git a/docs/api/zh/loaders/ObjectLoader.html b/docs/api/zh/loaders/ObjectLoader.html
index 0d75f52c8473bf..f51d2c5bc63a41 100644
--- a/docs/api/zh/loaders/ObjectLoader.html
+++ b/docs/api/zh/loaders/ObjectLoader.html
@@ -71,8 +71,8 @@
属性
[property:String crossOrigin]
- 如果设置了,在开始加载前, 将为图片分配 [link:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes crossOrigin]
- 属性,其值为 *crossOrigin*, 默认为"anonymous"。
+ 如果设置了,在开始加载前, 将为图片分配 [link:https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes crossOrigin]
+ 属性,其值为 *crossOrigin*, 默认为"anonymous"。
[property:LoadingManager manager]
@@ -88,6 +88,14 @@ [property:String texturePath]
方法
+ [method:ObjectLoader bindSkeletons]( [param:Object3D object], [param:Array skeletons] )
+
+ [page:Object3D object] — TODO.
+ [page:Array skeletons] — TODO.
+
+ TODO
+
+
[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )
[page:String url] — 文件的URL或者路径,也可以为
@@ -97,58 +105,74 @@
[method:null load]( [param:String url], [param:Function onLoad], [param:Func
[page:Function onError] — 在加载错误时被调用。
- 从URL中进行加载,并将被解析的响应内容传递给onLoad。
+ 从URL中进行加载,并将被解析的响应内容传递给onLoad。
- [method:Object3D parse]( [param:Object json], [param:Function onLoad] )
+ [method:Object3D parse]( [param:Object json], [param:Function onLoad] )
[page:Object json] — 必选参数,需要被解析的JSON源。
[page:Function onLoad] — 当解析完成时被调用,其中参数被解析为[page:Object3D object].
- 解析一个JSON结构,并返回一个threejs对象.
+ 解析一个JSON结构,并返回一个threejs对象.
内部使用[page:.load]进行加载, 但也可以直接用于解析先前加载的JSON结构。
- [method:Object3D parseGeometries]( [param:Object json] )
+ [method:Object parseGeometries]( [param:Object json] )
[page:Object json] — 必选参数,需要被解析的JSON源。
- 此函数以JSON结构,用[page:.parse]去解析[page:Geometry geometries]或[page:BufferGeometry buffer geometries]。
+ 此函数以JSON结构,用[page:.parse]去解析[page:Geometry geometries]或[page:BufferGeometry buffer geometries]。
- [method:Object3D parseMaterials]( [param:Object json] )
+ [method:Object parseMaterials]( [param:Object json] )
- [page:Object json] — 必选参数,需要被解析的JSON源。
+ [page:Object json] — 必选参数,需要被解析的JSON源。
此函数通过[page:.parse]来使用[page:MaterialLoader],以解析JSON结构中任意材质。
- [method:Object3D parseAnimations]( [param:Object json] )
+ [method:Array parseAnimations]( [param:Object json] )
- [page:Object json] — 必选参数,需要被解析的JSON源。
+ [page:Object json] — 必选参数,需要被解析的JSON源。
此函数通过[page:.parse]来使用[page:AnimationClip.parse], 以解析JSON结构中任意动画。
-
+
+
+ [method:Object parseImages]( [param:Object json] )
+
+ [page:Object json] — 必选参数,需要被解析的JSON源。
+ 此函数通过[page:.parse]来使用[page:ImageLoader], 以解析JSON结构中任意图片。
+
- [method:Object3D parseImages]( [param:Object json] )
+ [method:Object parseShapes]( [param:Object json] )
- [page:Object json] — 必选参数,需要被解析的JSON源。
+ [page:Object json] — TODO.
- 此函数通过[page:.parse]来使用[page:ImageLoader], 以解析JSON结构中任意图片。
+ TODO.
- [method:Object3D parseTextures]( [param:Object json] )
+ [method:Object parseSkeletons]( [param:Object json], [param:Object3D object] )
- [page:Object json] — 必选参数,需要被解析的JSON源。
- 此函数通过[page:.parse]来解析JSON结构中任意纹理。
+ [page:Object json] — TODO.
+ [page:Object3D object] — TODO.
+
+ TODO.
+
+
+ [method:Object parseTextures]( [param:Object json] )
+
+ [page:Object json] — 必选参数,需要被解析的JSON源。
+ 此函数通过[page:.parse]来解析JSON结构中任意纹理。
- [method:Object3D parseObject]( [param:Object json] )
+ [method:Object3D parseObject]( [param:Object data], [param:Object geometries], [param:Object materials] )
- [page:Object json] — 必选参数,需要被解析的JSON源。
+ [page:Object data] — 必选参数,需要被解析的JSON源。
+ [page:Object geometries] — TODO.
+ [page:Object materials] — TODO.
- 此函数通过[page:.parse]来解析JSON结构中任意对象。
+ 此函数通过[page:.parse]来解析JSON结构中任意对象。
对象可以为如下类型:
@@ -176,9 +200,15 @@ [method:Object3D parseObject]( [param:Object json] )
-
[page:HemisphereLight]
+ -
+ [page:SkinnedMesh]
+
-
[page:Mesh]
+ -
+ [page:Bone]
+
-
[page:LOD]
diff --git a/docs/api/zh/objects/Skeleton.html b/docs/api/zh/objects/Skeleton.html
index 8512ae3586bf49..3d6321ac1a6ecc 100644
--- a/docs/api/zh/objects/Skeleton.html
+++ b/docs/api/zh/objects/Skeleton.html
@@ -76,6 +76,11 @@ [property:DataTexture boneTexture]
当使用顶点纹理时,[page:DataTexture]保存着骨骼数据。
+ [property:Number boneTextureSize]
+
+ TODO
+
+
方法
@@ -90,10 +95,22 @@ [method:null calculateInverses]()
+ [method:Skeleton fromJSON]( [param:Object json], [param:Object3D object])
+ TODO
+
+
+ [method:Skeleton init]()
+ TODO
+
+
[method:null pose]()
返回骨架的基础姿势。
+ [method:Object toJSON]()
+ TODO
+
+
[method:null update]()
在改变骨骼后,更新[page:Float32Array boneMatrices] 和 [page:DataTexture boneTexture]的值。
@@ -102,7 +119,7 @@
[method:null update]()
[method:Bone getBoneByName]( [param:String name] )
-
+
name —— 匹配Bone对象中.name属性的字符串。
在骨架中的骨骼数组中遍览,并返回第一个能够和name匹配上的骨骼对象。
diff --git a/src/core/BufferGeometry.js b/src/core/BufferGeometry.js
index a200245ed306ea..6f324cec9970e5 100644
--- a/src/core/BufferGeometry.js
+++ b/src/core/BufferGeometry.js
@@ -952,7 +952,30 @@ BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototy
var attribute = attributes[ key ];
- var array = Array.prototype.slice.call( attribute.array );
+ var array;
+
+ if ( attribute.isInterleavedBufferAttribute ) {
+
+ var count = attribute.count;
+ var itemSize = attribute.itemSize;
+ array = attribute.array.slice( 0, count * itemSize );
+
+ var s = 0;
+
+ for ( var i = 0; i < count; ++ i ) {
+
+ array[ s ++ ] = attribute.getX( i );
+ if ( itemSize >= 2 ) array[ s ++ ] = attribute.getY( i );
+ if ( itemSize >= 3 ) array[ s ++ ] = attribute.getZ( i );
+ if ( itemSize >= 4 ) array[ s ++ ] = attribute.getW( i );
+
+ }
+
+ } else {
+
+ array = Array.prototype.slice.call( attribute.array );
+
+ }
data.data.attributes[ key ] = {
itemSize: attribute.itemSize,
diff --git a/src/core/Object3D.js b/src/core/Object3D.js
index f7a47a29ed2846..67bf184a6242fe 100644
--- a/src/core/Object3D.js
+++ b/src/core/Object3D.js
@@ -687,7 +687,8 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
materials: {},
textures: {},
images: {},
- shapes: {}
+ shapes: {},
+ skeletons: {}
};
output.metadata = {
@@ -762,6 +763,21 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
}
+ if ( this.isSkinnedMesh ) {
+
+ if ( this.bindMode !== undefined ) object.bindMode = this.bindMode;
+ if ( this.bindMatrix !== undefined ) 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 ) ) {
@@ -805,12 +821,14 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
var textures = extractFromCache( meta.textures );
var images = extractFromCache( meta.images );
var shapes = extractFromCache( meta.shapes );
+ var 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;
}
diff --git a/src/loaders/ObjectLoader.js b/src/loaders/ObjectLoader.js
index 1b1cfbd86bbdd6..584a4a7a2719ea 100644
--- a/src/loaders/ObjectLoader.js
+++ b/src/loaders/ObjectLoader.js
@@ -30,6 +30,8 @@ import { LineSegments } from '../objects/LineSegments.js';
import { LOD } from '../objects/LOD.js';
import { Mesh } from '../objects/Mesh.js';
import { SkinnedMesh } from '../objects/SkinnedMesh.js';
+import { Bone } from '../objects/Bone.js';
+import { Skeleton } from '../objects/Skeleton.js';
import { Shape } from '../extras/core/Shape.js';
import { Fog } from '../scenes/Fog.js';
import { FogExp2 } from '../scenes/FogExp2.js';
@@ -134,7 +136,7 @@ Object.assign( ObjectLoader.prototype, {
parse: function ( json, onLoad ) {
- var shapes = this.parseShape( json.shapes );
+ var shapes = this.parseShapes( json.shapes );
var geometries = this.parseGeometries( json.geometries, shapes );
var images = this.parseImages( json.images, function () {
@@ -148,6 +150,10 @@ Object.assign( ObjectLoader.prototype, {
var object = this.parseObject( json.object, geometries, materials );
+ var skeletons = this.parseSkeletons( json.skeletons, object );
+
+ this.bindSkeletons( object, skeletons );
+
if ( json.animations ) {
object.animations = this.parseAnimations( json.animations );
@@ -164,7 +170,7 @@ Object.assign( ObjectLoader.prototype, {
},
- parseShape: function ( json ) {
+ parseShapes: function ( json ) {
var shapes = {};
@@ -184,6 +190,26 @@ Object.assign( ObjectLoader.prototype, {
},
+ parseSkeletons: function ( json, object ) {
+
+ var skeletons = {};
+
+ if ( json !== undefined ) {
+
+ for ( var i = 0, l = json.length; i < l; i ++ ) {
+
+ var skeleton = new Skeleton().fromJSON( json[ i ], object );
+
+ skeletons[ skeleton.uuid ] = skeleton;
+
+ }
+
+ }
+
+ return skeletons;
+
+ },
+
parseGeometries: function ( json, shapes ) {
var geometries = {};
@@ -828,22 +854,32 @@ Object.assign( ObjectLoader.prototype, {
case 'SkinnedMesh':
- console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' );
+ var geometry = getGeometry( data.geometry );
+ var 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 );
+
+ // "skeletonUUID" is used to indentify the correct skeleton in .bindSkeletons()
+
+ if ( data.skeleton !== undefined ) object.skeletonUUID = data.skeleton;
+
+ break;
case 'Mesh':
var geometry = getGeometry( data.geometry );
var material = getMaterial( data.material );
- if ( geometry.bones && geometry.bones.length > 0 ) {
+ object = new Mesh( geometry, material );
- object = new SkinnedMesh( geometry, material );
-
- } else {
+ break;
- object = new Mesh( geometry, material );
+ case 'Bone':
- }
+ object = new Bone();
break;
@@ -967,6 +1003,36 @@ Object.assign( ObjectLoader.prototype, {
return object;
+ },
+
+ bindSkeletons: function ( object, skeletons ) {
+
+ if ( Object.keys( skeletons ).length === 0 ) return;
+
+ object.traverse( function ( child ) {
+
+ if ( child.isSkinnedMesh === true && child.skeletonUUID !== undefined ) {
+
+ var skeleton = skeletons[ child.skeletonUUID ];
+
+ if ( skeleton === undefined ) {
+
+ console.warn( 'THREE.ObjectLoader: No skeleton found with UUID:', child.skeletonUUID );
+
+ } else {
+
+ child.bind( skeleton, child.bindMatrix );
+
+ }
+
+ delete child.skeletonUUID;
+
+ }
+
+ } );
+
+ return this;
+
}
} );
diff --git a/src/objects/Skeleton.js b/src/objects/Skeleton.js
index 8764e93cecd9b8..10d48da369172a 100644
--- a/src/objects/Skeleton.js
+++ b/src/objects/Skeleton.js
@@ -1,4 +1,6 @@
import { Matrix4 } from '../math/Matrix4.js';
+import { _Math } from '../math/Math.js';
+import { Bone } from './Bone.js';
/**
* @author mikael emtinger / http://gomo.se/
@@ -9,40 +11,18 @@ import { Matrix4 } from '../math/Matrix4.js';
function Skeleton( bones, boneInverses ) {
- // copy the bone array
+ this.uuid = _Math.generateUUID();
- bones = bones || [];
+ this.type = 'Skeleton';
- this.bones = bones.slice( 0 );
- this.boneMatrices = new Float32Array( this.bones.length * 16 );
+ this.bones = bones || [];
+ this.boneInverses = boneInverses;
- // use the supplied bone inverses or calculate the inverses
+ this.boneMatrices = undefined;
+ this.boneTexture = undefined;
+ this.boneTextureSize = undefined;
- if ( boneInverses === undefined ) {
-
- this.calculateInverses();
-
- } else {
-
- if ( this.bones.length === boneInverses.length ) {
-
- this.boneInverses = boneInverses.slice( 0 );
-
- } else {
-
- console.warn( 'THREE.Skeleton boneInverses is the wrong length.' );
-
- this.boneInverses = [];
-
- for ( var i = 0, il = this.bones.length; i < il; i ++ ) {
-
- this.boneInverses.push( new Matrix4() );
-
- }
-
- }
-
- }
+ this.init();
}
@@ -170,6 +150,109 @@ Object.assign( Skeleton.prototype, {
return undefined;
+ },
+
+ init: function () {
+
+ var bones = this.bones;
+ var boneInverses = this.boneInverses;
+
+ this.boneMatrices = new Float32Array( bones.length * 16 );
+
+ // calculate inverse bone matrices if necessary
+
+ if ( boneInverses === undefined ) {
+
+ this.calculateInverses();
+
+ } else {
+
+ // handle special case
+
+ if ( bones.length !== boneInverses.length ) {
+
+ console.warn( 'THREE.Skeleton: Amount of inverse bone matrices does not match amount of bones.' );
+
+ this.boneInverses = [];
+
+ for ( var i = 0, il = this.bones.length; i < il; i ++ ) {
+
+ this.boneInverses.push( new Matrix4() );
+
+ }
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ fromJSON: function ( json, object ) {
+
+ this.uuid = json.uuid;
+
+ this.boneInverses = [];
+
+ var bones = this.bones;
+ var boneInverses = this.boneInverses;
+
+ for ( var i = 0, l = json.bones.length; i < l; i ++ ) {
+
+ var boneUUID = json.bones[ i ];
+
+ // consider to use a lookup table for bones in order to improve performance
+
+ var bone = object.getObjectByProperty( 'uuid', boneUUID );
+
+ if ( bone === undefined ) {
+
+ console.warn( 'THREE.Skeleton: No bone found with UUID:', boneUUID );
+ bone = new Bone();
+
+ }
+
+ bones.push( bone );
+ boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) );
+
+ }
+
+ this.init();
+
+ return this;
+
+ },
+
+ toJSON: function () {
+
+ var data = {
+ metadata: {
+ version: 4.5,
+ type: 'Skeleton',
+ generator: 'Skeleton.toJSON'
+ },
+ bones: [],
+ boneInverses: []
+ };
+
+ data.uuid = this.uuid;
+
+ var bones = this.bones;
+ var boneInverses = this.boneInverses;
+
+ for ( var i = 0, l = bones.length; i < l; i ++ ) {
+
+ var bone = bones[ i ];
+ data.bones.push( bone.uuid );
+
+ var boneInverse = boneInverses[ i ];
+ data.boneInverses.push( boneInverse.toArray() );
+
+ }
+
+ return data;
+
}
} );