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(); }
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 pointInfluenceIndices( SmoothSkinningData &p ) { return p.pointInfluenceIndices(); }
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(); }