void DecompressSmoothSkinningDataOp::modify( Object * object, const CompoundObject * operands )
{
	SmoothSkinningData *skinningData = static_cast<SmoothSkinningData *>( object );
	assert( skinningData );

	const std::vector<std::string> &influenceNames = skinningData->influenceNames()->readable();
	const std::vector<int> &pointIndexOffsets = skinningData->pointIndexOffsets()->readable();
	const std::vector<int> &pointInfluenceCounts = skinningData->pointInfluenceCounts()->readable();
	const std::vector<int> &pointInfluenceIndices = skinningData->pointInfluenceIndices()->readable();
	const std::vector<float> &pointInfluenceWeights = skinningData->pointInfluenceWeights()->readable();
	
	std::vector<int> newOffsets;
	std::vector<int> newCounts;
	std::vector<int> newIndices;
	std::vector<float> newWeights;
	
	int offset = 0;
	int numInfluences = influenceNames.size();
	
	for ( unsigned i=0; i < pointIndexOffsets.size(); i++ )
	{
		const std::vector<int>::const_iterator first = pointInfluenceIndices.begin() + pointIndexOffsets[i];
		const std::vector<int>::const_iterator last = first + pointInfluenceCounts[i];
		
		for ( int j=0; j < numInfluences; j++ )
		{			
			newIndices.push_back( j );
			
			const std::vector<int>::const_iterator location = find( first, last, j );
			if ( location != last )
			{
				newWeights.push_back( pointInfluenceWeights[ location - pointInfluenceIndices.begin() ] );
			}
			else
			{
				newWeights.push_back( 0.0f );
			}
		}

		newOffsets.push_back( offset );
		newCounts.push_back( numInfluences );
		offset += numInfluences;
	}
	
	// replace the vectors on the SmoothSkinningData
	if ( newWeights.size() != pointInfluenceWeights.size() )
	{
		skinningData->pointIndexOffsets()->writable().swap( newOffsets );
		skinningData->pointInfluenceCounts()->writable().swap( newCounts );
		skinningData->pointInfluenceIndices()->writable().swap( newIndices );
		skinningData->pointInfluenceWeights()->writable().swap( newWeights );
	}
}
void MixSmoothSkinningWeightsOp::modify( Object * object, const CompoundObject * operands )
{
	SmoothSkinningData *skinningData = static_cast<SmoothSkinningData *>( object );
	assert( skinningData );
	
	SmoothSkinningData *origMixingData = runTimeCast<SmoothSkinningData>( m_skinningDataParameter->getValidatedValue() );
	if ( !origMixingData )
	{
		throw IECore::Exception( "MixSmoothSkinningWeightsOp: skinningDataToMix is not valid" );
	}
	
	assert( origMixingData );
	
	// make sure the number of influences matches
	if ( origMixingData->influenceNames()->readable().size() != skinningData->influenceNames()->readable().size() )
	{
		throw IECore::Exception( "MixSmoothSkinningWeightsOp: skinningDataToMix and input have different numbers of influences" );
	}
	
	std::vector<float> &mixingWeights = m_mixingWeightsParameter->getTypedValue();
	
	// make sure there is one mixing weight per influence
	if ( mixingWeights.size() != skinningData->influenceNames()->readable().size() )
	{
		throw IECore::Exception( "MixSmoothSkinningWeightsOp: There must be exactly one mixing weight per influence" );
	}
	
	// decompress both sets of skinning data
	DecompressSmoothSkinningDataOp decompressionOp;
	decompressionOp.inputParameter()->setValidatedValue( skinningData );
	decompressionOp.copyParameter()->setTypedValue( false );
	decompressionOp.operate();
	decompressionOp.inputParameter()->setValidatedValue( origMixingData );
	decompressionOp.copyParameter()->setTypedValue( true );
	decompressionOp.operate();
	const SmoothSkinningData *mixingData = runTimeCast<const SmoothSkinningData>( decompressionOp.resultParameter()->getValidatedValue() );
	if ( !mixingData )
	{
		throw IECore::Exception( "MixSmoothSkinningWeightsOp: skinningDataToMix did not decompress correctly" );
	}
	
	// make sure everything matches except the weights
	if ( *(mixingData->pointIndexOffsets()) != *(skinningData->pointIndexOffsets()) )
	{
		throw IECore::Exception( "MixSmoothSkinningWeightsOp: skinningDataToMix and input have different pointIndexOffsets when decompressed" );
	}
	if ( *(mixingData->pointInfluenceCounts()) != *(skinningData->pointInfluenceCounts()) )
	{
		throw IECore::Exception( "MixSmoothSkinningWeightsOp: skinningDataToMix and input have different pointInfluenceCounts when decompressed" );
	}
	if ( *(mixingData->pointInfluenceIndices()) != *(skinningData->pointInfluenceIndices()) )
	{
		throw IECore::Exception( "MixSmoothSkinningWeightsOp: skinningDataToMix and input have different pointInfluenceIndices when decompressed" );
	}
	
	const std::vector<int> &inputIndexOffsets = skinningData->pointIndexOffsets()->readable();
	const std::vector<int> &inputInfluenceCounts = skinningData->pointInfluenceCounts()->readable();
	const std::vector<int> &inputInfluenceIndices = skinningData->pointInfluenceIndices()->readable();
	
	std::vector<float> &inputInfluenceWeights = skinningData->pointInfluenceWeights()->writable();
	const std::vector<float> &mixingInfluenceWeights = mixingData->pointInfluenceWeights()->readable();
	
	LinearInterpolator<float> lerp;
	
	// mix the weights
	for ( unsigned i=0; i < inputIndexOffsets.size(); i++ )
	{
		for ( int j=0; j < inputInfluenceCounts[i]; j++ )
		{
			int current = inputIndexOffsets[i] + j;
			
			lerp( mixingInfluenceWeights[current], inputInfluenceWeights[current], mixingWeights[ inputInfluenceIndices[current] ], inputInfluenceWeights[current] );
		}
	}
	
	// re-compress the input data
	CompressSmoothSkinningDataOp compressionOp;
	compressionOp.inputParameter()->setValidatedValue( skinningData );
	compressionOp.copyParameter()->setTypedValue( false );
	compressionOp.operate();
}
void TransferSmoothSkinningWeightsOp::modify( Object * object, const CompoundObject * operands )
{
	SmoothSkinningData *skinningData = static_cast<SmoothSkinningData *>( object );
	assert( skinningData );
	
	const std::string target = m_targetInfluenceNameParameter->getTypedValue();
	const std::vector<std::string> &sources = m_sourceInfluenceNamesParameter->getTypedValue();
	if ( !sources.size() )
	{
		throw IECore::Exception( "TransferSmoothSkinningWeightsOp: you need to specify source influences" );
	}
	
	const std::vector<std::string> &influenceNames = skinningData->influenceNames()->readable();
	
	const std::vector<std::string>::const_iterator foundSame = find( sources.begin(), sources.end(), target );
	if ( foundSame != sources.end() )
	{
		throw IECore::Exception( ( boost::format( "TransferSmoothSkinningWeightsOp: \"%s\" cannot be both source and target" ) % target ).str() );
	}
	
	const std::vector<std::string>::const_iterator location = find( influenceNames.begin(), influenceNames.end(), target );
	if ( location == influenceNames.end() )
	{
		throw IECore::Exception( ( boost::format( "TransferSmoothSkinningWeightsOp: \"%s\" is not a valid influence name" ) % target ).str() );
	}
	int targetIndex = location - influenceNames.begin();
	std::vector<int> sourceIndices;
	
	for ( unsigned i=0; i < sources.size(); i++ )
	{
		std::string name = sources[i];
		
		const std::vector<std::string>::const_iterator found = find( influenceNames.begin(), influenceNames.end(), name );
		if ( found == influenceNames.end() )
		{
			throw IECore::Exception( ( boost::format( "TransferSmoothSkinningWeightsOp: \"%s\" is not a valid influenceName" ) % name ).str() );
		}
		sourceIndices.push_back( found - influenceNames.begin() );
	}

	// decompress skinning data
	DecompressSmoothSkinningDataOpPtr decompressionOp = new DecompressSmoothSkinningDataOp;
	decompressionOp->inputParameter()->setValidatedValue( skinningData );
	decompressionOp->copyParameter()->setTypedValue( false );
	decompressionOp->operate();
	
	const std::vector<int> &pointIndexOffsets = skinningData->pointIndexOffsets()->readable();
	const std::vector<int> &pointInfluenceCounts = skinningData->pointInfluenceCounts()->readable();
	const std::vector<int> &pointInfluenceIndices = skinningData->pointInfluenceIndices()->readable();
	std::vector<float> &pointInfluenceWeights = skinningData->pointInfluenceWeights()->writable();
	
	for ( unsigned i=0; i < pointIndexOffsets.size(); i++ )
	{
		float targetWeight = 0.0;
		int targetCurrentIndex = 0;
		
		for ( int j=0; j < pointInfluenceCounts[i]; j++ )
		{
			int current = pointIndexOffsets[i] + j;
			int index = pointInfluenceIndices[current];
			float weight = pointInfluenceWeights[current];
			
			if( index == targetIndex )
			{
				targetWeight += weight;
				targetCurrentIndex = current;
			}
			else
			{
				const std::vector<int>::const_iterator found = find( sourceIndices.begin(), sourceIndices.end(), index );
				if ( found != sourceIndices.end() )
				{
					targetWeight += weight;
					pointInfluenceWeights[ current ] = 0.0;
				}
			}
		}
		pointInfluenceWeights[ targetCurrentIndex ] = targetWeight;
	}
	
	// re-compress
	CompressSmoothSkinningDataOpPtr compressionOp = new CompressSmoothSkinningDataOp;
	compressionOp->inputParameter()->setValidatedValue( skinningData );
	compressionOp->copyParameter()->setTypedValue( false );
	compressionOp->operate();
}
Exemplo n.º 4
0
void LimitSmoothSkinningInfluencesOp::modify( Object * object, const CompoundObject * operands )
{
	SmoothSkinningData *skinningData = static_cast<SmoothSkinningData *>( object );
	assert( skinningData );

	const std::vector<int> &pointIndexOffsets = skinningData->pointIndexOffsets()->readable();
	const std::vector<int> &pointInfluenceCounts = skinningData->pointInfluenceCounts()->readable();
	const std::vector<int> &pointInfluenceIndices = skinningData->pointInfluenceIndices()->readable();
	
	std::vector<float> &pointInfluenceWeights = skinningData->pointInfluenceWeights()->writable();
	
	bool useLocks = m_useLocksParameter->getTypedValue();
	std::vector<bool> &locks = m_influenceLocksParameter->getTypedValue();
	
	int mode = m_modeParameter->getNumericValue();
	
	// make sure there is one lock per influence
	if ( useLocks && ( locks.size() != skinningData->influenceNames()->readable().size() ) && ( mode != LimitSmoothSkinningInfluencesOp::Indexed ) )
	{
		throw IECore::Exception( "LimitSmoothSkinningInfluencesOp: There must be exactly one lock per influence" );
	}
	
	if ( !useLocks )
	{
		locks.clear();
		locks.resize( skinningData->influenceNames()->readable().size(), false );
	}
	
	// Limit influences based on minumum allowable weight
	if ( mode == LimitSmoothSkinningInfluencesOp::WeightLimit )
	{
		float minWeight = m_minWeightParameter->getNumericValue();
		
		for ( unsigned i=0; i < pointIndexOffsets.size(); i++ )
		{
			for ( int j=0; j < pointInfluenceCounts[i]; j++ )
			{
				int current = pointIndexOffsets[i] + j;
				
				if ( !locks[ pointInfluenceIndices[current] ] && (pointInfluenceWeights[current] < minWeight) )
				{
					pointInfluenceWeights[current] = 0.0f;
				}
			}
		}
	}
	// Limit the number of influences per point
	else if ( mode == LimitSmoothSkinningInfluencesOp::MaxInfluences )
	{
		int maxInfluences = m_maxInfluencesParameter->getNumericValue();
		std::vector<int> influencesToLimit;
		
		for ( unsigned i=0; i < pointIndexOffsets.size(); i++ )
		{
			int numToLimit = pointInfluenceCounts[i] - maxInfluences;
			if ( numToLimit <= 0 )
			{
				continue;
			}
			
			influencesToLimit.clear();
			
			for ( int j=0; j < numToLimit; j++ )
			{				
				int indexOfMin = -1;
				float minWeight = Imath::limits<float>::max();
				
				for ( int k=0; k < pointInfluenceCounts[i]; k++ )
				{
					int current = pointIndexOffsets[i] + k;
					
					if ( locks[ pointInfluenceIndices[current] ] || find( influencesToLimit.begin(), influencesToLimit.end(), current ) != influencesToLimit.end() )
					{
						continue;
					}
					
					float weight = pointInfluenceWeights[current];
					
					if ( weight < minWeight )
					{
						minWeight = weight;
						indexOfMin = current;
					}
				}
				
				if ( indexOfMin == -1 )
				{
					break;
				}
				
				influencesToLimit.push_back( indexOfMin );
			}
			
			for ( unsigned j=0; j < influencesToLimit.size(); j++ )
			{
				pointInfluenceWeights[ influencesToLimit[j] ] = 0.0f;
			}
		}
	}
	// Zero specific influences for all points
	else if ( mode == LimitSmoothSkinningInfluencesOp::Indexed )
	{
#if defined(_WIN32)		
		std::vector<int> indicesToLimit;
#else
		std::vector<int64_t> indicesToLimit;
#endif
		m_influenceIndicesParameter->getFrameListValue()->asList( indicesToLimit );
		std::vector<bool> limitIndex( skinningData->influenceNames()->readable().size(), false );
		for ( unsigned i=0; i < indicesToLimit.size(); i++ )
		{
			limitIndex[ indicesToLimit[i] ] = true;
		}
		
		for ( unsigned i=0; i < pointIndexOffsets.size(); i++ )
		{
			for ( int j=0; j < pointInfluenceCounts[i]; j++ )
			{
				int current = pointIndexOffsets[i] + j;
				
				if ( limitIndex[ pointInfluenceIndices[current] ] )
				{
					pointInfluenceWeights[current] = 0.0f;
				}
			}
		}
	}
	else
	{
		throw IECore::Exception( ( boost::format( "LimitSmoothSkinningInfluencesOp: \"%d\" is not a recognized mode" ) % mode ).str() );
	}
	
	if ( m_compressionParameter->getTypedValue() )
	{
		CompressSmoothSkinningDataOp compressionOp;
		compressionOp.inputParameter()->setValidatedValue( skinningData );
		compressionOp.copyParameter()->setTypedValue( false );
		compressionOp.operate();
	}
}
	static IntVectorDataPtr pointInfluenceCounts(  SmoothSkinningData &p )
	{
		return p.pointInfluenceCounts();
	}
Exemplo n.º 6
0
void ContrastSmoothSkinningWeightsOp::modify( Object * object, const CompoundObject * operands )
{
	SmoothSkinningData *skinningData = static_cast<SmoothSkinningData *>( object );
	assert( skinningData );
	
	// \todo: remove decompression/compression.
	// decompress
	DecompressSmoothSkinningDataOp decompressionOp;
	decompressionOp.inputParameter()->setValidatedValue( skinningData );
	decompressionOp.copyParameter()->setTypedValue( false );
	decompressionOp.operate();
	
	const std::vector<int> &pointIndexOffsets = skinningData->pointIndexOffsets()->readable();
	const std::vector<int> &pointInfluenceCounts = skinningData->pointInfluenceCounts()->readable();
	const std::vector<int> &pointInfluenceIndices = skinningData->pointInfluenceIndices()->readable();
	int numSsdVerts = pointIndexOffsets.size();
	
	std::vector<float> &pointInfluenceWeights = skinningData->pointInfluenceWeights()->writable();
	
	const MeshPrimitive *mesh = runTimeCast<const MeshPrimitive>( m_meshParameter->getValidatedValue() );
	if ( !mesh )
	{
		throw IECore::Exception( "ContrastSmoothSkinningWeightsOp: The given mesh is not valid" );
	}
	int numMeshVerts = mesh->variableSize( PrimitiveVariable::Vertex );
	
	// make sure the mesh matches the skinning data
	if ( numMeshVerts != numSsdVerts )
	{
		throw IECore::Exception( "ContrastSmoothSkinningWeightsOp: The input SmoothSkinningData and mesh have a different number of vertices" );
	}
	
	bool useLocks = m_useLocksParameter->getTypedValue();
	std::vector<bool> &locks = m_influenceLocksParameter->getTypedValue();
		
	// make sure there is one lock per influence
	if ( useLocks && ( locks.size() != skinningData->influenceNames()->readable().size() ) )
	{
		throw IECore::Exception( "ContrastSmoothSkinningWeightsOp: There must be exactly one lock per influence" );
	}
	
	if ( !useLocks )
	{
		locks.clear();
		locks.resize( skinningData->influenceNames()->readable().size(), false );
	}
	
#if defined(_WIN32)
	std::vector<int> vertexIds;
#else
	std::vector<int64_t> vertexIds;
#endif
	m_vertexIdsParameter->getFrameListValue()->asList( vertexIds );
	
	// make sure all vertex ids are valid
	for ( unsigned i=0; i < vertexIds.size(); i++ )
	{
		if ( vertexIds[i] > numSsdVerts )
		{
			throw IECore::Exception( ( boost::format( "ContrastSmoothSkinningWeightsOp: VertexId \"%d\" is outside the range of the SmoothSkinningData and mesh" ) % vertexIds[i] ).str() );
		}
	}
	
	float contrastRatio = m_contrastRatioParameter->getNumericValue();
	float contrastCenter = m_contrastCenterParameter->getNumericValue();
	int numIterations = m_iterationsParameter->getNumericValue();

	ContrastSmoothStep contrastFunction( numIterations, contrastRatio, contrastCenter );
	
	// an empty vertexId list means we operate over all vertices
	if ( vertexIds.size() == 0 )
	{
		for ( unsigned int i=0; i < pointInfluenceWeights.size(); i++ )
		{
			unsigned int current = i;
			if ( !locks[ pointInfluenceIndices[current] ] )
			{
				pointInfluenceWeights[current] = contrastFunction( pointInfluenceWeights[ current ] );
			}
		}
	}
	else
	{
		// apply contrast with the per-influence locks
		for ( unsigned i=0; i < vertexIds.size(); i++ )
		{
			int currentVertId = vertexIds[i];
			for ( int j=0; j < pointInfluenceCounts[currentVertId]; j++ )
			{
				int current = pointIndexOffsets[currentVertId] + j;
				if ( !locks[ pointInfluenceIndices[current] ] )
				{
					pointInfluenceWeights[current] = contrastFunction( pointInfluenceWeights[ current ] );
				}
			}
		}
	}
	
	// re-compress
	CompressSmoothSkinningDataOp compressionOp;
	compressionOp.inputParameter()->setValidatedValue( skinningData );
	compressionOp.copyParameter()->setTypedValue( false );
	compressionOp.operate();
}
void SmoothSmoothSkinningWeightsOp::modify( Object * object, const CompoundObject * operands )
{
	SmoothSkinningData *skinningData = static_cast<SmoothSkinningData *>( object );
	assert( skinningData );
	
	// decompress
	DecompressSmoothSkinningDataOp decompressionOp;
	decompressionOp.inputParameter()->setValidatedValue( skinningData );
	decompressionOp.copyParameter()->setTypedValue( false );
	decompressionOp.operate();
	
	const std::vector<int> &pointIndexOffsets = skinningData->pointIndexOffsets()->readable();
	const std::vector<int> &pointInfluenceCounts = skinningData->pointInfluenceCounts()->readable();
	const std::vector<int> &pointInfluenceIndices = skinningData->pointInfluenceIndices()->readable();
	int numSsdVerts = pointIndexOffsets.size();
	
	std::vector<float> &pointInfluenceWeights = skinningData->pointInfluenceWeights()->writable();
	
	const MeshPrimitive *mesh = runTimeCast<const MeshPrimitive>( m_meshParameter->getValidatedValue() );
	if ( !mesh )
	{
		throw IECore::Exception( "SmoothSmoothSkinningWeightsOp: The given mesh is not valid" );
	}
	int numMeshVerts = mesh->variableSize( PrimitiveVariable::Vertex );
	const std::vector<int> &meshVertexIds = mesh->vertexIds()->readable();
	
	// make sure the mesh matches the skinning data
	if ( numMeshVerts != numSsdVerts )
	{
		throw IECore::Exception( "SmoothSmoothSkinningWeightsOp: The input SmoothSkinningData and mesh have a different number of vertices" );
	}
	
	bool useLocks = m_useLocksParameter->getTypedValue();
	std::vector<bool> &locks = m_influenceLocksParameter->getTypedValue();
		
	// make sure there is one lock per influence
	if ( useLocks && ( locks.size() != skinningData->influenceNames()->readable().size() ) )
	{
		throw IECore::Exception( "SmoothSmoothSkinningWeightsOp: There must be exactly one lock per influence" );
	}
	
	if ( !useLocks )
	{
		locks.clear();
		locks.resize( skinningData->influenceNames()->readable().size(), false );
	}
	
	std::vector<int64_t> vertexIds;
	m_vertexIdsParameter->getFrameListValue()->asList( vertexIds );
	
	// make sure all vertex ids are valid
	for ( unsigned i=0; i < vertexIds.size(); i++ )
	{
		if ( vertexIds[i] > numSsdVerts )
		{
			throw IECore::Exception( ( boost::format( "SmoothSmoothSkinningWeightsOp: VertexId \"%d\" is outside the range of the SmoothSkinningData and mesh" ) % vertexIds[i] ).str() );
		}
	}
	
	// an empty vertexId list means we smooth all vertices
	if ( vertexIds.size() == 0 )
	{
		for ( int i=0; i < numSsdVerts; i++ )
		{
			vertexIds.push_back( i );
		}
	}
	
	// add the mesh vertices to the neighbourhood graph
	/// \todo: consider moving this mesh connectivity graphing to the MeshPrimitive
	Graph meshGraph;
	for ( int i=0; i < numMeshVerts; i++ )
	{
		boost::add_vertex( meshGraph );
	}
	
	// add the mesh edges to the neighbourhood graph
	int v = 0, v1, v2;
	const std::vector<int> &verticesPerFace = mesh->verticesPerFace()->readable();
	unsigned numFaces = verticesPerFace.size();
	for ( unsigned f=0; f < numFaces; f++ )
	{
		for ( int fv=0; fv < verticesPerFace[f] - 1; fv++ )
		{
			v1 = meshVertexIds[v+fv];
			v2 = meshVertexIds[v+fv+1];
			
			Vertex source = boost::vertex( v1, meshGraph );
			Vertex target = boost::vertex( v2, meshGraph );
			if ( !boost::edge( source, target, meshGraph ).second )
			{
				boost::add_edge( source, target, meshGraph );
			}
		}
		
		v1 = meshVertexIds[v + verticesPerFace[f] - 1];
		v2 = meshVertexIds[v];
		Vertex source = boost::vertex( v1, meshGraph );
		Vertex target = boost::vertex( v2, meshGraph );
		if ( !boost::edge( source, target, meshGraph ).second )
		{
			boost::add_edge( source, target, meshGraph );
		}
		
		v += verticesPerFace[f];
	}
	
	// get the property map that relates each Graph Vertex to a vertexId
	VertexIdMap vertexIdMap = boost::get( boost::vertex_index, meshGraph );
	
	std::vector<float> smoothInfluenceWeights( skinningData->pointInfluenceWeights()->readable().size(), 0.0f );
	LinearInterpolator<float> lerp;
	float smoothingRatio = m_smoothingRatioParameter->getNumericValue();
	int numIterations = m_iterationsParameter->getNumericValue();
	
	NormalizeSmoothSkinningWeightsOp normalizeOp;
	normalizeOp.copyParameter()->setTypedValue( false );
	normalizeOp.parameters()->setParameterValue( "applyLocks", m_useLocksParameter->getValue() );
	normalizeOp.parameters()->setParameterValue( "influenceLocks", m_influenceLocksParameter->getValue() );
	
	// iterate
	for ( int iteration=0; iteration < numIterations; iteration++ )
	{
		// smooth the weights
		for ( unsigned i=0; i < vertexIds.size(); i++ )
		{
			int currentVertId = vertexIds[i];
			Vertex currentVert = boost::vertex( currentVertId, meshGraph );
			NeighbourIteratorRange neighbourhood = boost::adjacent_vertices( currentVert, meshGraph );
			float numNeighbours = boost::out_degree( currentVert, meshGraph );
			
			for ( int j=0; j < pointInfluenceCounts[currentVertId]; j++ )
			{
				int current = pointIndexOffsets[currentVertId] + j;
				
				// calculate the average neighbour weight
				float totalNeighbourWeight = 0.0f;
				for( NeighbourIterator nIt = neighbourhood.first; nIt != neighbourhood.second; nIt++ )
				{
					int neighbourId = vertexIdMap[*nIt];					
					float currentNeighbourWeight = pointInfluenceWeights[ pointIndexOffsets[neighbourId] + j ];
					totalNeighbourWeight += currentNeighbourWeight;
				}
				float averageNeighbourWeight = totalNeighbourWeight / numNeighbours;
				
				lerp( pointInfluenceWeights[current], averageNeighbourWeight, smoothingRatio, smoothInfluenceWeights[current] );
			}
		}
		
		// apply the per-influence locks
		for ( unsigned i=0; i < vertexIds.size(); i++ )
		{
			int currentVertId = vertexIds[i];

			for ( int j=0; j < pointInfluenceCounts[currentVertId]; j++ )
			{
				int current = pointIndexOffsets[currentVertId] + j;

				if ( !locks[ pointInfluenceIndices[current] ] )
				{
					pointInfluenceWeights[current] = smoothInfluenceWeights[current];
				}
			}
		}
		
		// normalize
		normalizeOp.inputParameter()->setValidatedValue( skinningData );
		normalizeOp.operate();
	}
	
	// re-compress
	CompressSmoothSkinningDataOp compressionOp;
	compressionOp.inputParameter()->setValidatedValue( skinningData );
	compressionOp.copyParameter()->setTypedValue( false );
	compressionOp.operate();
}