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