/* * Copyright 2007 (c) Tim Knip, ascollada.org. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.ascollada.core { import org.ascollada.ASCollada; import org.ascollada.core.DaeEntity; import org.ascollada.core.DaeSource; import org.ascollada.core.DaeVertexWeights; import org.ascollada.utils.Logger; /** * */ public class student_ar_org_ascollada_core_DaeSkin extends DaeEntity { /** bind shape matrix */ public var bind_shape_matrix:Array; /** */ public var source:String; /** */ public var joints:Array; /** */ public var bind_matrices:Array; /** */ public var vertex_weights:Array; /** */ public var blendWeightsByJointID:Object; /** */ public var jointsType:String; /** * * @param node * @return */ public function student_ar_org_ascollada_core_DaeSkin( node:XML = null ):void { super( node ); } /** * * @param node * @return */ public function findJointBindMatrix( node:DaeNode ):Array { var id:String = this.jointsType == "IDREF" ? node.id : node.sid; for( var i:int = 0; i < this.joints.length; i++ ) { if( id == this.joints[i] ) return this.bind_matrices[i]; } return null; } /** * * @param id * @return */ public function findJointBindMatrix2( id:String ):Array { for( var i:int = 0; i < this.joints.length; i++ ) { if( id == this.joints[i] ) return this.bind_matrices[i]; } return null; } /** * * @param node * * @return */ public function findJointVertexWeights( node:DaeNode ):Array { var id:String = this.jointsType == "IDREF" ? node.id : node.sid; var weights:Array = new Array(); for( var i:int = 0; i < this.vertex_weights.length; i++ ) { var arr:Array = this.vertex_weights[i]; for( var j:int = 0; j < arr.length; j++ ) if( arr[j].joint == id ) weights.push( arr[j] ); } return weights; } /** * * @param node * * @return */ public function findJointVertexWeightsByIDOrSID( id:String ):Array { var weights:Array = new Array(); for( var i:int = 0; i < this.vertex_weights.length; i++ ) { var arr:Array = this.vertex_weights[i]; for( var j:int = 0; j < arr.length; j++ ) if( arr[j].joint == id ) weights.push( arr[j] ); } return weights; } /** * * @param node * @return */ override public function read( node:XML ):void { this.joints = new Array(); this.vertex_weights = new Array(); this.bind_matrices = new Array(); if( node.localName() != ASCollada.DAE_CONTROLLER_SKIN_ELEMENT ) return; super.read( node ); // required - ref to skin's geometry this.source = getAttribute(node, ASCollada.DAE_SOURCE_ATTRIBUTE); Logger.log( "reading skin, source: " + this.source ); // optional - bind_shape_matrix, defaults to identity matrix var bindList:XMLList = getNodeList(node, ASCollada.DAE_BINDSHAPEMX_SKIN_PARAMETER); if( bindList.length() ) this.bind_shape_matrix = getFloats( bindList[0] ); else this.bind_shape_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; // 3 or more elements var sourceList:XMLList = getNodeList(node, ASCollada.DAE_SOURCE_ELEMENT); if( sourceList.length() < 3 ) throw new Error( " requires a minimum of 3 elements!" ); // exactly 1 element var jointsNode:XML = getNode(node, ASCollada.DAE_JOINTS_ELEMENT); if( !jointsNode ) throw new Error( "need exactly one element!" ); // exactly 1 element var weightsNode:XML = getNode(node, ASCollada.DAE_WEIGHTS_ELEMENT); if( !weightsNode ) throw new Error( "need exactly one element!" ); var jointsList:XMLList = getNodeList(jointsNode, ASCollada.DAE_INPUT_ELEMENT); var weights:DaeVertexWeights = new DaeVertexWeights( weightsNode ); var srcNode:XML; var input:DaeInput; // fetch sources for var src:DaeSource var sources:Object = new Object(); for each( var inputNode:XML in jointsList ) { input = new DaeInput( inputNode ); srcNode = getNodeById(node, ASCollada.DAE_SOURCE_ELEMENT, input.source); if( !srcNode ) throw new Error( "source not found! (id='" + input.source + "')" ); src = new DaeSource( srcNode ); switch( input.semantic ) { case ASCollada.DAE_JOINT_SKIN_INPUT: this.joints = src.values; this.jointsType = src.accessor.params[ASCollada.DAE_JOINT_SKIN_INPUT]; //Logger.log( " => => joints: " + this.joints ); break; case ASCollada.DAE_BINDMATRIX_SKIN_INPUT: this.bind_matrices = src.values; //Logger.log( " => => bind_matrices: " + this.bind_matrices ); break; default: break; } } var maxOffset:int = 0; var jointOffset:uint = 0; var weightOffset:uint = 1; var tmpWeights:Array; // fetch sources for for each( input in weights.inputs ) { srcNode = getNodeById(node, ASCollada.DAE_SOURCE_ELEMENT, input.source); if( !srcNode ) throw new Error( "source not found! (id='" + input.source + "')" ); src = new DaeSource( srcNode ); switch( input.semantic ) { case ASCollada.DAE_JOINT_SKIN_INPUT: jointOffset = input.offset; maxOffset++; break; case ASCollada.DAE_WEIGHT_SKIN_INPUT: tmpWeights = src.values; weightOffset = input.offset; maxOffset++; //Logger.trace( " => => => vertex_weights: " + tmpWeights ); break; default: break; } } //Logger.trace( " => source: " + this.source ); //Logger.trace( " => bind_shape_matrix: " + this.bind_shape_matrix ); //Logger.trace( " => max offset: " + maxOffset + " " + jointOffset + " " + weightOffset ); var cur:int = 0; for( var i:int = 0; i < weights.vcounts.length; i++ ) { var vcount:int = weights.vcounts[i]; var tmp:Array = new Array(); for( var j:int = 0; j < vcount; j++ ) { var jidx:int = weights.v[cur + jointOffset ]; var widx:int = weights.v[cur + weightOffset]; var w:Number = tmpWeights[ widx ]; var jnt:String = jidx < 0 ? null : this.joints[ jidx ]; var blendWeight:DaeBlendWeight = new DaeBlendWeight( i, jnt, w ); tmp.push( blendWeight ); cur += maxOffset; } this.vertex_weights[i] = tmp; } Logger.log( " => => #vertex_weights " + vertex_weights.length ); } /** * normalize blendweights. * * @param blendWeights the weights to normalize. */ private function normalizeBlendWeights( blendWeights:Array ):void { var i:int, j:int; for( i = 0; i < blendWeights.length; i++ ) { var arr:Array = blendWeights[i] var weightSum:Number = 0; for( j = 0; j < arr.length; j++ ) { weightSum += arr[j].weight; arr[j].originalWeight = arr[j].weight; } if( weightSum == 0.0 || weightSum == 1.0 ) continue; var invWeightSum:Number = 1.0 / weightSum; for( j = 0; j < arr.length; j++ ) arr[j].weight *= invWeightSum; } } } }