/****************************************************************************** libx42make - skinned vertex animation library (exporter utilities) Copyright (C) 2007 HermitWorks Entertainment Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA. ******************************************************************************/ #include "local.h" namespace x42 { namespace make { inline void NormalizeVertexWeights( float wt[X42_WEIGHTS_PER_VERT] ) { float sum = 0; for( uint i = 0; i < X42_WEIGHTS_PER_VERT; i++ ) sum += wt[i]; if( sum < 1e-5F ) { memset( wt, 0, sizeof( float[X42_WEIGHTS_PER_VERT] ) ); return; } float div = 1.0F / sum; for( uint i = 0; i < X42_WEIGHTS_PER_VERT; i++ ) wt[i] *= div; } /***** Group */ void Group::CleanWeakWeights( float minWeight ) { FixupWeights(); std::vector< Influence > &influences = GetModel()->influences; for( uint i = 0; i < verts.size(); i++ ) { Vertex &v = verts[i]; bool normalize = false; for( uint i = 1; i < X42_WEIGHTS_PER_VERT; i++ ) { if( v.wt[i] == 0 || v.wt[i] >= minWeight ) continue; influences[infMap[v.idx[i]]].useCount--; v.wt[i] = 0; v.idx[i] = 0; normalize = true; } if( normalize ) NormalizeVertexWeights( v.wt ); } UnreferenceUnusedInfluences(); CountWeights(); } void Group::CleanExtraWeights( uint maxWeights ) { demand( maxWeights >= 1, "must keep at least one weight" ); FixupWeights(); std::vector< Influence > &influences = GetModel()->influences; if( maxWeights >= X42_WEIGHTS_PER_VERT ) return; if( maxInfsPerVert <= maxWeights ) return; for( uint i = 0; i < verts.size(); i++ ) { Vertex &v = verts[i]; bool normalize = false; for( uint i = maxWeights; i < X42_WEIGHTS_PER_VERT; i++ ) { if( v.wt[i] == 0 ) continue; influences[infMap[v.idx[i]]].useCount--; v.wt[i] = 0; v.idx[i] = 0; normalize = true; } if( normalize ) NormalizeVertexWeights( v.wt ); } UnreferenceUnusedInfluences(); CountWeights(); } void Group::UnreferenceUnusedInfluences( void ) { std::vector< uint > counts( infMap.size(), 0 ); for( uint i = 0; i < verts.size(); i++ ) { const Vertex &v = verts[i]; for( uint i = 0; i < X42_WEIGHTS_PER_VERT; i++ ) { if( v.wt[i] ) counts[v.idx[i]]++; } } std::vector< uint > newInfs; std::vector< uint > remap( infMap.size() ); for( uint i = 0; i < infMap.size(); i++ ) { if( counts[i] ) { remap[i] = (uint)newInfs.size(); newInfs.push_back( infMap[i] ); } else { remap[i] = ModelData::InvalidInfluence; } } infMap = newInfs; ApplyLocalInfluenceRemap( remap ); } void Group::FixupWeights( void ) { maxInfsPerVert = 1; std::vector< Influence > &influences = GetModel()->influences; for( uint i = 0; i < verts.size(); i++ ) { Vertex &v = verts[i]; //merge duplicate weights for( uint i = 0; i < X42_WEIGHTS_PER_VERT - 1; i++ ) { if( v.wt[i] == 0 ) continue; for( uint j = i + 1; j < X42_WEIGHTS_PER_VERT; j++ ) { if( v.wt[j] == 0 ) continue; if( v.idx[i] == v.idx[j] ) { v.wt[i] += v.wt[j]; v.wt[j] = 0; v.idx[j] = 0; //vert counted as two references before the collapse influences[infMap[v.idx[i]]].useCount--; } } } //sort the weights for( uint i = 0; i < X42_WEIGHTS_PER_VERT - 1; i++ ) { for( uint j = i + 1; j < X42_WEIGHTS_PER_VERT; j++ ) { if( v.wt[i] < v.wt[j] ) { uint tmpIdx = v.idx[i]; float tmpWeight = v.wt[i]; v.idx[i] = v.idx[j]; v.wt[i] = v.wt[j]; v.idx[j] = tmpIdx; v.wt[j] = tmpWeight; } } } NormalizeVertexWeights( v.wt ); } CountWeights(); } void Group::CountWeights( void ) { uint max = 1; for( uint i = 0; i < verts.size(); i++ ) { const Vertex &v = verts[i]; uint count = 0; for( uint i = 0; i < X42_WEIGHTS_PER_VERT; i++ ) { if( v.wt[i] ) count++; } if( count > max ) max = count; if( max == X42_WEIGHTS_PER_VERT ) //can't get any higher break; } maxInfsPerVert = max; } /***** ModelData */ void ModelData::CleanWeakWeights( float minWeight ) { for( uint i = 0; i < lods.size(); i++ ) { Lod &lod = *lods[i]; for( uint i = 0; i < lod.groups.size(); i++ ) lod.groups[i]->CleanWeakWeights( minWeight ); } } void ModelData::CleanExtraWeights( uint maxWeights ) { for( uint i = 0; i < lods.size(); i++ ) { Lod &lod = *lods[i]; for( uint i = 0; i < lod.groups.size(); i++ ) lod.groups[i]->CleanExtraWeights( maxWeights ); } } void ModelData::FixupWeights( void ) { for( uint i = 0; i < lods.size(); i++ ) { Lod &lod = *lods[i]; for( uint i = 0; i < lod.groups.size(); i++ ) lod.groups[i]->FixupWeights(); } } }; };