PageRenderTime 147ms CodeModel.GetById 40ms app.highlight 38ms RepoModel.GetById 13ms app.codeStats 1ms

/src/away3d/animators/data/SkeletonAnimationState.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 356 lines | 266 code | 39 blank | 51 comment | 21 complexity | 1214eb867e438a05e85cd1c913a9be5c MD5 | raw file
  1package away3d.animators.data
  2{
  3	import away3d.animators.skeleton.JointPose;
  4	import away3d.animators.skeleton.Skeleton;
  5	import away3d.animators.skeleton.SkeletonJoint;
  6	import away3d.animators.skeleton.SkeletonPose;
  7	import away3d.animators.skeleton.SkeletonTreeNode;
  8	import away3d.arcane;
  9	import away3d.core.base.IRenderable;
 10	import away3d.core.base.SkinnedSubGeometry;
 11	import away3d.core.base.SubMesh;
 12	import away3d.core.managers.Stage3DProxy;
 13	import away3d.core.math.Quaternion;
 14	import away3d.materials.passes.MaterialPassBase;
 15
 16	import flash.display3D.Context3D;
 17	import flash.display3D.Context3DProgramType;
 18	import flash.geom.Vector3D;
 19	import flash.utils.Dictionary;
 20
 21	use namespace arcane;
 22
 23	/**
 24	 * SkeletonAnimationState defines the state for a given Mesh and SkeletonAnimation. The state consists of a skeleton pose.
 25	 *
 26	 * @see away3d.core.animation.skinned.SkinnedAnimation
 27	 *
 28	 */
 29	public class SkeletonAnimationState extends AnimationStateBase
 30	{
 31		private var _globalMatrices : Vector.<Number>;
 32		private var _numJoints : uint;
 33		private var _skinnedAnimation : SkeletonAnimation;
 34		private var _jointsPerVertex : uint;
 35		private var _bufferFormat : String;
 36        private var _skeleton : Skeleton;
 37        private var _blendTree : SkeletonTreeNode;
 38        private var _globalPose : SkeletonPose;
 39        private var _globalInput : Boolean;
 40		private var _buffersValid : Dictionary = new Dictionary(true);
 41		private var _globalMatricesInvalid : Boolean;
 42
 43		/**
 44		 * Creates a SkeletonAnimationState object.
 45		 * @param animation The animation object the state refers to.
 46		 * @param jointsPerVertex The amount of joints per vertex define
 47		 */
 48		public function SkeletonAnimationState(animation : SkeletonAnimation)
 49		{
 50			super(animation);
 51
 52			_skinnedAnimation = animation;
 53            if (animation.numJoints > 0) {
 54				init();
 55			}
 56		}
 57
 58		private function init() : void
 59		{
 60			_jointsPerVertex = _skinnedAnimation.jointsPerVertex;
 61            _skeleton = _skinnedAnimation.skeleton;
 62            _numJoints = _skinnedAnimation.numJoints;
 63			_globalMatrices = new Vector.<Number>(_numJoints*12, true);
 64			_bufferFormat = "float"+_jointsPerVertex;
 65            _globalPose = new SkeletonPose();
 66
 67			var j : int;
 68			for (var i : uint = 0; i < _numJoints; ++i) {
 69				_globalMatrices[j++] = 1; _globalMatrices[j++] = 0; _globalMatrices[j++] = 0; _globalMatrices[j++] = 0;
 70				_globalMatrices[j++] = 0; _globalMatrices[j++] = 1; _globalMatrices[j++] = 0; _globalMatrices[j++] = 0;
 71				_globalMatrices[j++] = 0; _globalMatrices[j++] = 0; _globalMatrices[j++] = 1; _globalMatrices[j++] = 0;
 72			}
 73		}
 74
 75		/**
 76		 * The amount of joints in the target skeleton.
 77		 */
 78		public function get numJoints() : uint
 79		{
 80			return _numJoints;
 81		}
 82
 83		/**
 84		 * The chained raw data of the global pose matrices in row-major order.
 85		 */
 86		public function get globalMatrices() : Vector.<Number>
 87		{
 88			return _globalMatrices;
 89		}
 90
 91		/**
 92		 * The global skeleton pose used to transform the mesh's vertices.
 93		 */
 94		public function get globalPose() : SkeletonPose
 95		{
 96			if (_stateInvalid) updateGlobalPose();
 97			return _globalPose;
 98		}
 99
100		public function set globalPose(value : SkeletonPose) : void
101		{
102			if (!_globalInput) throw new Error("Cannot set global pose if globalInput is false");
103			_globalPose = value;
104			_globalMatricesInvalid = true;
105		}
106
107        arcane function validateGlobalMatrices() : void
108        {
109            _stateInvalid = false;
110            _globalMatricesInvalid = false;
111        }
112
113		/**
114		 * The local skeleton blend tree that will be used to generate the global pose.
115		 */
116		public function get blendTree() : SkeletonTreeNode
117		{
118			return _blendTree;
119		}
120
121		public function set blendTree(value : SkeletonTreeNode) : void
122		{
123			_blendTree = value;
124		}
125
126		/**
127		 * @inheritDoc
128		 */
129		override public function invalidateState() : void
130		{
131			super.invalidateState();
132
133			for (var key : Object in _buffersValid) {
134			    _buffersValid[key] = false;
135			}
136		}
137
138		/**
139		 * @inheritDoc
140		 */
141        override public function setRenderState(stage3DProxy : Stage3DProxy, pass : MaterialPassBase, renderable : IRenderable) : void
142		{
143			if (_numJoints == 0) {
144				// delayed skeleton instantiation
145				if (_skinnedAnimation.numJoints > 0)
146					init();
147				else
148					return;
149			}
150
151			// do on request of globalPose
152			if (_stateInvalid) updateGlobalPose();
153			if (_globalMatricesInvalid) convertToMatrices();
154
155			if (_skinnedAnimation.usesCPU) {
156				var subGeom : SkinnedSubGeometry = SkinnedSubGeometry(SubMesh(renderable).subGeometry);
157				if (!_buffersValid[subGeom]) {
158					morphGeometry(subGeom);
159					_buffersValid[subGeom] = true;
160				}
161				return;
162			}
163
164			var skinnedGeom : SkinnedSubGeometry = SkinnedSubGeometry(SubMesh(renderable).subGeometry);
165			var context : Context3D = stage3DProxy._context3D;
166
167			context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, pass.numUsedVertexConstants, _globalMatrices, _numJoints*3);
168
169			var streamOffset : uint = pass.numUsedStreams;
170			stage3DProxy.setSimpleVertexBuffer(streamOffset, skinnedGeom.getJointIndexBuffer(stage3DProxy), _bufferFormat);
171			stage3DProxy.setSimpleVertexBuffer(streamOffset+1, skinnedGeom.getJointWeightsBuffer(stage3DProxy), _bufferFormat);
172		}
173
174		private function updateGlobalPose() : void
175		{
176			if (!_globalInput) {
177				_blendTree.updatePose(_skeleton);
178				_blendTree.skeletonPose.toGlobalPose(_globalPose, _skeleton);
179			}
180			_globalMatricesInvalid = true;
181			_stateInvalid = false;
182		}
183
184		/**
185		 * @inheritDoc
186		 */
187		override public function clone() : AnimationStateBase
188		{
189			return new SkeletonAnimationState(_skinnedAnimation);
190		}
191
192		/**
193		 * Defines whether or not to bypass the blend tree and allow setting the global skeleton pose directly.
194		 * todo: remove, use post-processing effects for global position based... stuff.
195		 */
196		arcane function get globalInput() : Boolean
197        {
198            return _globalInput;
199        }
200
201        arcane function set globalInput(value : Boolean) : void
202        {
203            _globalInput = value;
204        }
205
206		/**
207		 * Converts the current final pose to matrices for the actual transformations
208		 */
209		private function convertToMatrices() : void
210		{
211			// convert pose to matrix
212		    var mtxOffset : uint;
213			var globalPoses : Vector.<JointPose> = _globalPose.jointPoses;
214			var raw : Vector.<Number>;
215			var ox : Number, oy : Number, oz : Number, ow : Number;
216			var xy2 : Number, xz2 : Number, xw2 : Number;
217			var yz2 : Number, yw2 : Number, zw2 : Number;
218			var xx : Number, yy : Number, zz : Number, ww : Number;
219			var n11 : Number, n12 : Number, n13 : Number, n14 : Number;
220			var n21 : Number, n22 : Number, n23 : Number, n24 : Number;
221			var n31 : Number, n32 : Number, n33 : Number, n34 : Number;
222			var m11 : Number, m12 : Number, m13 : Number, m14 : Number;
223			var m21 : Number, m22 : Number, m23 : Number, m24 : Number;
224			var m31 : Number, m32 : Number, m33 : Number, m34 : Number;
225			var joints : Vector.<SkeletonJoint> = _skeleton.joints;
226			var pose : JointPose;
227			var quat : Quaternion;
228			var vec : Vector3D;
229
230			for (var i : uint = 0; i < _numJoints; ++i) {
231				pose = globalPoses[i];
232				quat = pose.orientation;
233				vec = pose.translation;
234				ox = quat.x;	oy = quat.y;	oz = quat.z;	ow = quat.w;
235				xy2 = 2.0 * ox * oy; 	xz2 = 2.0 * ox * oz; 	xw2 = 2.0 * ox * ow;
236				yz2 = 2.0 * oy * oz; 	yw2 = 2.0 * oy * ow; 	zw2 = 2.0 * oz * ow;
237				xx = ox * ox;			yy = oy * oy;			zz = oz * oz; 			ww = ow * ow;
238
239				n11 = xx - yy - zz + ww;	n12 = xy2 - zw2;			n13 = xz2 + yw2;			n14 = vec.x;
240				n21 = xy2 + zw2;			n22 = -xx + yy - zz + ww;	n23 = yz2 - xw2;			n24 = vec.y;
241				n31 = xz2 - yw2;			n32 = yz2 + xw2;			n33 = -xx - yy + zz + ww;	n34 = vec.z;
242
243				// prepend inverse bind pose
244				raw = joints[i].inverseBindPose;
245				m11 = raw[0];	m12 = raw[4];	m13 = raw[8];	m14 = raw[12];
246				m21 = raw[1];	m22 = raw[5];   m23 = raw[9];	m24 = raw[13];
247				m31 = raw[2];   m32 = raw[6];   m33 = raw[10];  m34 = raw[14];
248
249				_globalMatrices[mtxOffset++] = n11 * m11 + n12 * m21 + n13 * m31;
250				_globalMatrices[mtxOffset++] = n11 * m12 + n12 * m22 + n13 * m32;
251				_globalMatrices[mtxOffset++] = n11 * m13 + n12 * m23 + n13 * m33;
252				_globalMatrices[mtxOffset++] = n11 * m14 + n12 * m24 + n13 * m34 + n14;
253				_globalMatrices[mtxOffset++] = n21 * m11 + n22 * m21 + n23 * m31;
254				_globalMatrices[mtxOffset++] = n21 * m12 + n22 * m22 + n23 * m32;
255				_globalMatrices[mtxOffset++] = n21 * m13 + n22 * m23 + n23 * m33;
256				_globalMatrices[mtxOffset++] = n21 * m14 + n22 * m24 + n23 * m34 + n24;
257				_globalMatrices[mtxOffset++] = n31 * m11 + n32 * m21 + n33 * m31;
258				_globalMatrices[mtxOffset++] = n31 * m12 + n32 * m22 + n33 * m32;
259				_globalMatrices[mtxOffset++] = n31 * m13 + n32 * m23 + n33 * m33;
260				_globalMatrices[mtxOffset++] = n31 * m14 + n32 * m24 + n33 * m34 + n34;
261			}
262
263			_globalMatricesInvalid = false;
264		}
265
266		/**
267		 * If the animation can't be performed on cpu, transform vertices manually
268		 * @param subGeom The subgeometry containing the weights and joint index data per vertex.
269		 * @param pass The material pass for which we need to transform the vertices
270		 *
271		 * todo: we may be able to transform tangents more easily, similar to how it happens on gpu
272		 */
273		private function morphGeometry(subGeom : SkinnedSubGeometry) : void
274		{
275			var verts : Vector.<Number> = subGeom.vertexData;
276			var normals : Vector.<Number> = subGeom.vertexNormalData;
277			var tangents : Vector.<Number> = subGeom.vertexTangentData;
278			var targetVerts : Vector.<Number> = subGeom.animatedVertexData;
279			var targetNormals : Vector.<Number> = subGeom.animatedNormalData;
280			var targetTangents : Vector.<Number> = subGeom.animatedTangentData;
281			var jointIndices : Vector.<Number> = subGeom.jointIndexData;
282			var jointWeights : Vector.<Number> = subGeom.jointWeightsData;
283			var i1 : uint, i2 : uint = 1, i3 : uint = 2;
284			var j : uint, k : uint;
285			var vx : Number, vy : Number, vz : Number;
286			var nx : Number, ny : Number, nz : Number;
287			var tx : Number, ty : Number, tz : Number;
288			var len : int = verts.length;
289			var weight : Number;
290			var mtxOffset : uint;
291			var vertX : Number, vertY : Number, vertZ : Number;
292			var normX : Number, normY : Number, normZ : Number;
293			var tangX : Number, tangY : Number, tangZ : Number;
294			var m11 : Number, m12 : Number, m13 : Number;
295			var m21 : Number, m22 : Number, m23 : Number;
296			var m31 : Number, m32 : Number, m33 : Number;
297
298			while (i1 < len) {
299				vertX = verts[i1]; vertY = verts[i2]; vertZ = verts[i3];
300				vx = 0; vy = 0; vz = 0;
301				normX = normals[i1]; normY = normals[i2]; normZ = normals[i3];
302				nx = 0; ny = 0; nz = 0;
303				tangX = tangents[i1]; tangY = tangents[i2]; tangZ = tangents[i3];
304				tx = 0; ty = 0; tz = 0;
305
306				k = 0;
307				while (k < _jointsPerVertex) {
308					weight = jointWeights[j];
309					if (weight == 0) {
310						j += _jointsPerVertex - k;
311						k = _jointsPerVertex;
312					}
313					else {
314						// implicit /3*12 (/3 because indices are multiplied by 3 for gpu matrix access, *12 because it's the matrix size)
315						mtxOffset = jointIndices[uint(j++)]*4;
316						m11 = _globalMatrices[mtxOffset]; m12 = _globalMatrices[mtxOffset+1]; m13 = _globalMatrices[mtxOffset+2];
317						m21 = _globalMatrices[mtxOffset+4]; m22 = _globalMatrices[mtxOffset+5]; m23 = _globalMatrices[mtxOffset+6];
318						m31 = _globalMatrices[mtxOffset+8]; m32 = _globalMatrices[mtxOffset+9]; m33 = _globalMatrices[mtxOffset+10];
319						vx += weight*(m11*vertX + m12*vertY + m13*vertZ + _globalMatrices[mtxOffset+3]);
320						vy += weight*(m21*vertX + m22*vertY + m23*vertZ + _globalMatrices[mtxOffset+7]);
321						vz += weight*(m31*vertX + m32*vertY + m33*vertZ + _globalMatrices[mtxOffset+11]);
322
323						nx += weight*(m11*normX + m12*normY + m13*normZ);
324						ny += weight*(m21*normX + m22*normY + m23*normZ);
325						nz += weight*(m31*normX + m32*normY + m33*normZ);
326						tx += weight*(m11*tangX + m12*tangY + m13*tangZ);
327						ty += weight*(m21*tangX + m22*tangY + m23*tangZ);
328						tz += weight*(m31*tangX + m32*tangY + m33*tangZ);
329						k++;
330					}
331				}
332
333				targetVerts[i1] = vx; targetVerts[i2] = vy; targetVerts[i3] = vz;
334				targetNormals[i1] = nx; targetNormals[i2] = ny; targetNormals[i3] = nz;
335				targetTangents[i1] = tx; targetTangents[i2] = ty; targetTangents[i3] = tz;
336
337				i1 += 3; i2 += 3; i3 += 3;
338			}
339			subGeom.animatedVertexData = targetVerts;
340			subGeom.animatedNormalData = targetNormals;
341			subGeom.animatedTangentData = targetTangents;
342		}
343
344		public function applyRootDelta() : void
345		{
346			var delta : Vector3D = blendTree.rootDelta;
347			var dist : Number = delta.length;
348			var len : uint;
349			if (dist > 0) {
350				len = _owners.length;
351				for (var i : uint = 0; i < len; ++i)
352					_owners[i].translateLocal(delta, dist);
353			}
354		}
355	}
356}