package org.papervision3d.core.controller { import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.core.math.Matrix3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.special.Skin3D; public class SkinController implements IObjectController { /** */ public var poseMatrix:Matrix3D; /** */ public var bindShapeMatrix:Matrix3D; /** */ public var target:Skin3D; /** */ public var joints:Array; /** */ public var invBindMatrices:Array; /** */ public var vertexWeights:Array; /** * Constructor. * * @param target */ public function SkinController(target:Skin3D) { this.target = target; this.joints = new Array(); this.invBindMatrices = new Array(); this.vertexWeights = new Array(); } /** * Update. */ public function update():void { if(!joints.length || !bindShapeMatrix) return; if(!_cached) cacheVertices(); if(invBindMatrices.length != this.joints.length) return; var vertices:Array = target.geometry.vertices; var i:int; // reset mesh's vertices to 0 for(i = 0; i < vertices.length; i++) vertices[i].x = vertices[i].y = vertices[i].z = 0; // skin the mesh! for(i = 0; i < joints.length; i++) skinMesh(joints[i], this.vertexWeights[i], invBindMatrices[i], _cached, vertices); } /** * Cache original vertices. */ private function cacheVertices():void { this.target.transformVertices(this.bindShapeMatrix); this.target.geometry.ready = true; var vertices:Array = this.target.geometry.vertices; _cached = new Array(vertices.length); for(var i:int = 0; i < vertices.length; i++) _cached[i] = new Number3D(vertices[i].x, vertices[i].y, vertices[i].z); } /** * Skins a mesh. * * @param joint * @param meshVerts * @param skinnedVerts */ private function skinMesh(joint:DisplayObject3D, weights:Array, inverseBindMatrix:Matrix3D, meshVerts:Array, skinnedVerts:Array):void { var i:int; var pos:Number3D = new Number3D(); var original:Number3D; var skinned:Vertex3D; var matrix:Matrix3D = Matrix3D.multiply(joint.world, inverseBindMatrix); for( i = 0; i < weights.length; i++ ) { var weight:Number = weights[i].weight; var vertexIndex:int = weights[i].vertexIndex; if( weight <= 0.0001 || weight >= 1.0001) continue; original = meshVerts[ vertexIndex ]; skinned = skinnedVerts[ vertexIndex ]; pos.x = original.x; pos.y = original.y; pos.z = original.z; // joint transform Matrix3D.multiplyVector(matrix, pos); //update the vertex skinned.x += (pos.x * weight); skinned.y += (pos.y * weight); skinned.z += (pos.z * weight); } } private var _cached:Array; } }