瀏覽代碼

SkinnedMesh: Added serialization/deserialization.

Mugen87 4 年之前
父節點
當前提交
1c8be06133
共有 4 個文件被更改,包括 230 次插入26 次删除
  1. 42 1
      editor/js/Editor.js
  2. 19 1
      src/core/Object3D.js
  3. 81 3
      src/loaders/ObjectLoader.js
  4. 88 21
      src/objects/Skeleton.js

+ 42 - 1
editor/js/Editor.js

@@ -664,10 +664,50 @@ Editor.prototype = {
 
 		} );
 
+		// animations
+
+		var animationsJSON = json.animations;
+
+		for ( var i = 0; i < animationsJSON.length; i ++ ) {
+
+			var objectJSON = animationsJSON[ i ];
+			var animations = [];
+
+			for ( var j = 0; j < objectJSON.animations.length; j ++ ) {
+
+				animations.push( THREE.AnimationClip.parse( objectJSON.animations[ j ] ) );
+
+			}
+
+			this.animations[ objectJSON.uuid ] = animations;
+
+		}
+
 	},
 
 	toJSON: function () {
 
+		// animations
+
+		var animations = this.animations;
+		var animationsJSON = [];
+
+		for ( var entry in animations ) {
+
+			var objectAnimations = animations[ entry ];
+			var objectJSON = { uuid: entry, animations: [] };
+
+			for ( var i = 0; i < objectAnimations.length; i ++ ) {
+
+				var objectAnimation = objectAnimations[ i ];
+				objectJSON.animations.push( THREE.AnimationClip.toJSON( objectAnimation ) );
+
+			}
+
+			animationsJSON.push( objectJSON );
+
+		}
+
 		// scripts clean up
 
 		var scene = this.scene;
@@ -701,7 +741,8 @@ Editor.prototype = {
 			camera: this.camera.toJSON(),
 			scene: this.scene.toJSON(),
 			scripts: this.scripts,
-			history: this.history.toJSON()
+			history: this.history.toJSON(),
+			animations: animationsJSON
 
 		};
 

+ 19 - 1
src/core/Object3D.js

@@ -658,7 +658,8 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 				materials: {},
 				textures: {},
 				images: {},
-				shapes: {}
+				shapes: {},
+				skeletons: {}
 			};
 
 			output.metadata = {
@@ -743,6 +744,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 ) ) {
@@ -786,12 +802,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;
 
 		}
 

+ 81 - 3
src/loaders/ObjectLoader.js

@@ -30,6 +30,9 @@ import { LineLoop } from '../objects/LineLoop.js';
 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';
@@ -110,7 +113,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 () {
@@ -123,6 +126,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 ) {
 
@@ -140,7 +146,7 @@ class ObjectLoader extends Loader {
 
 	}
 
-	parseShape( json ) {
+	parseShapes( json ) {
 
 		const shapes = {};
 
@@ -160,6 +166,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 = {};
@@ -826,7 +863,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':
 
@@ -892,6 +938,12 @@ class ObjectLoader extends Loader {
 
 				break;
 
+			case 'Bone':
+
+				object = new Bone();
+
+				break;
+
 			default:
 
 				object = new Object3D();
@@ -974,6 +1026,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 ) {

+ 88 - 21
src/objects/Skeleton.js

@@ -1,54 +1,63 @@
+import { Bone } from './Bone.js';
 import { Matrix4 } from '../math/Matrix4.js';
+import { MathUtils } from '../math/MathUtils.js';
 
 const _offsetMatrix = new Matrix4();
 const _identityMatrix = new Matrix4();
 
-function Skeleton( bones, boneInverses ) {
+function Skeleton( bones = [], boneInverses = [] ) {
 
-	// copy the bone array
-
-	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();
+
+}
+
+Object.assign( Skeleton.prototype, {
+
+	init: function () {
 
-	if ( boneInverses === undefined ) {
+		const bones = this.bones;
+		const boneInverses = this.boneInverses;
 
-		this.calculateInverses();
+		this.boneMatrices = new Float32Array( bones.length * 16 );
 
-	} else {
+		// calculate inverse bone matrices if necessary
 
-		if ( this.bones.length === boneInverses.length ) {
+		if ( boneInverses.length === 0 ) {
 
-			this.boneInverses = boneInverses.slice( 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 ++ ) {
 
@@ -171,6 +180,64 @@ Object.assign( Skeleton.prototype, {
 
 		}
 
+	},
+
+	fromJSON: function ( json, bones ) {
+
+		this.uuid = json.uuid;
+
+		for ( let i = 0, l = json.bones.length; i < l; i ++ ) {
+
+			const uuid = json.bones[ i ];
+			let bone = bones[ uuid ];
+
+			if ( bone === undefined ) {
+
+				console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid );
+				bone = new Bone();
+
+			}
+
+			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;
+
 	}
 
 } );