Beispiel #1
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 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 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();
}