diff --git a/include/IECoreMaya/LiveScene.h b/include/IECoreMaya/LiveScene.h index 2565de1602..87db282946 100644 --- a/include/IECoreMaya/LiveScene.h +++ b/include/IECoreMaya/LiveScene.h @@ -78,9 +78,6 @@ class IECOREMAYA_API LiveScene : public IECoreScene::SceneInterface /// Name of maya attribute overriding `IECoreScene::SceneInterface::visibilityName` static IECoreScene::SceneInterface::Name visibilityOverrideName; - /// Returns the MDagPath object to the scene node - MDagPath dagPath() const; - /* * Bounding box */ @@ -188,6 +185,19 @@ class IECOREMAYA_API LiveScene : public IECoreScene::SceneInterface /// Currently raises an exception void hash( HashType hashType, double time, IECore::MurmurHash &h ) const override; + /* + * Utility functions + */ + + /// Returns the MDagPath object to the scene node + MDagPath dagPath() const; + + /// Returns the scene path corresponding to the maya dag path + static void dagPathToPath( MDagPath dagPath, IECoreScene::SceneInterface::Path &path ); + + /// Returns the maya dag path corresponding to the scene path + static void pathToDagPath( const IECoreScene::SceneInterface::Path &path, MDagPath &dagPath ); + /// Translates cortex attribute name to maya attribute name /// Returns an empty string if there are no valid mappings static Name toMayaAttributeName( const Name &name ); diff --git a/include/IECoreMaya/SceneShapeInterface.h b/include/IECoreMaya/SceneShapeInterface.h index 790e1d0a15..e61be5c296 100644 --- a/include/IECoreMaya/SceneShapeInterface.h +++ b/include/IECoreMaya/SceneShapeInterface.h @@ -138,6 +138,8 @@ class IECOREMAYA_API SceneShapeInterface: public MPxComponentShape const std::vector< IECore::InternedString > & componentNames() const; /// Return the value of the time plug for the SceneShape. double time() const; + /// Determines scene visibility while accounting for ancestral visibility overrides + static bool isVisible( const MDagPath &dagPath ); protected : diff --git a/src/IECoreMaya/LiveScene.cpp b/src/IECoreMaya/LiveScene.cpp index 435270743d..fd28025791 100644 --- a/src/IECoreMaya/LiveScene.cpp +++ b/src/IECoreMaya/LiveScene.cpp @@ -206,20 +206,7 @@ void LiveScene::path( Path &p ) const throw Exception( "IECoreMaya::LiveScene::path: Dag path no longer exists!" ); } - std::string pathStr( m_dagPath.fullPathName().asChar() ); - boost::tokenizer > t( pathStr, boost::char_separator( "|" ) ); - - p.clear(); - - for ( - boost::tokenizer >::iterator it = t.begin(); - it != t.end(); - ++it - ) - { - p.push_back( Name( *it ) ); - } - + dagPathToPath( m_dagPath, p ); } Imath::Box3d LiveScene::readBound( double time ) const @@ -1122,6 +1109,66 @@ void LiveScene::hash( HashType hashType, double time, MurmurHash &h ) const throw Exception( "Hashes currently not supported in IECoreMaya::LiveScene objects." ); } +void LiveScene::dagPathToPath( MDagPath dagPath, IECoreScene::SceneInterface::Path &path ) +{ + tbb::recursive_mutex::scoped_lock l( g_mutex ); + path.clear(); + + if( dagPath.isValid() ) + { + // Only transforms can be part of the path + if( !dagPath.hasFn( MFn::kTransform ) ) + { + dagPath.pop(); + } + + const std::string pathStr( dagPath.fullPathName().asChar() ); + const boost::tokenizer< boost::char_separator > tokens( pathStr, boost::char_separator( "|" ) ); + for ( const auto &token : tokens ) + { + path.push_back( token ); + } + + return; + } + + throw Exception( "IECoreMaya::LiveScene::dagPathToPath invalid dag path." ); +} + +void LiveScene::pathToDagPath( const IECoreScene::SceneInterface::Path &path, MDagPath &dagPath ) +{ + tbb::recursive_mutex::scoped_lock l( g_mutex ); + + if( path.empty() ) + { + MItDag itDag; + itDag.getPath( dagPath ); + return; + } + + std::string dagPathStr; + for( const auto &name : path ) + { + dagPathStr += "|"; + dagPathStr += name; + } + + MSelectionList sel; + if( sel.add( dagPathStr.c_str() ) && sel.getDagPath( 0, dagPath ) ) + { + return; + } + + // Invalid dag path + std::string pathStr; + IECoreScene::SceneInterface::pathToString( path, pathStr ); + throw Exception( + boost::str( + boost::format( "IECoreMaya::LiveScene::pathToDagPath invalid conversion to dag path from \"%1%\"." ) % pathStr + ) + ); +} + void LiveScene::registerCustomObject( HasFn hasFn, ReadFn readFn ) { CustomReader r; diff --git a/src/IECoreMaya/SceneShapeInterface.cpp b/src/IECoreMaya/SceneShapeInterface.cpp index e17932161c..93c60347a4 100644 --- a/src/IECoreMaya/SceneShapeInterface.cpp +++ b/src/IECoreMaya/SceneShapeInterface.cpp @@ -57,6 +57,7 @@ #include "IECoreMaya/ToMayaCurveConverter.h" #include "IECoreMaya/MayaTypeIds.h" #include "IECoreMaya/PostLoadCallback.h" +#include "IECoreMaya/LiveScene.h" #include "IECorePython/ScopedGILLock.h" #include "IECorePython/ScopedGILRelease.h" @@ -97,6 +98,7 @@ #include "maya/MFnGeometryData.h" #include "maya/MPlugArray.h" #include "maya/MFileIO.h" +#include "maya/MAnimControl.h" #if MAYA_API_VERSION >= 201600 @@ -539,6 +541,38 @@ double SceneShapeInterface::time() const return time.as( MTime::kSeconds ); } +bool SceneShapeInterface::isVisible( const MDagPath &dagPath ) +{ + // Let maya check for basic dag visibility + // Maya handles hidden shapes, display layers, render layers, and intermediate objects + if( !dagPath.isVisible() ) + { + return false; + } + + // Check for ancestral visibility via LiveScene which accounts for visibility overrides which are not exposed to maya + IECoreMaya::ConstLiveScenePtr liveScene = new IECoreMaya::LiveScene(); + const double currentTime = MAnimControl::currentTime().as( MTime::kSeconds ); + + SceneInterface::Path scenePath; + IECoreMaya::LiveScene::dagPathToPath( dagPath, scenePath ); + const size_t pathLength = scenePath.size(); + for( size_t i = 0; i < pathLength; ++i ) + { + ConstSceneInterfacePtr scene = liveScene->scene( scenePath ); + ConstBoolDataPtr liveSceneVisibility = runTimeCast( scene->readAttribute( SceneInterface::visibilityName, currentTime ) ); + if( liveSceneVisibility && !liveSceneVisibility->readable() ) + { + // Not visible due to a visibility override + return false; + } + + scenePath.pop_back(); + } + + return true; +} + MBoundingBox SceneShapeInterface::boundingBox() const { MBoundingBox bound( MPoint( -1, -1, -1 ), MPoint( 1, 1, 1 ) ); diff --git a/src/IECoreMaya/SceneShapeSubSceneOverride.cpp b/src/IECoreMaya/SceneShapeSubSceneOverride.cpp index d3465c56a6..7670433c9c 100644 --- a/src/IECoreMaya/SceneShapeSubSceneOverride.cpp +++ b/src/IECoreMaya/SceneShapeSubSceneOverride.cpp @@ -1121,17 +1121,22 @@ bool SceneShapeSubSceneOverride::requiresUpdate(const MSubSceneContainer& contai { allInvisibleByFilter = true; - MObject component; - MSelectionList viewSelectedSet; - view.filteredObjectList( viewSelectedSet ); + // Create a selection list of the dagPaths which are visible after the view is filtered + MSelectionList visibleList; + view.filteredObjectList( visibleList ); - for( size_t i = 0; i < dagPaths.length(); ++i ) + // Create a selection list with all of our dag paths + MSelectionList dagPathList; + for( const auto &dagPath : dagPaths ) { - if( viewSelectedSet.hasItemPartly( dagPaths[i], component ) ) - { - allInvisibleByFilter = false; - break; - } + dagPathList.add( dagPath ); + } + + // Intersect the two lists to determine if any of our dag paths remain unfiltered + visibleList.intersect( dagPathList, true ); + if( !visibleList.isEmpty() ) + { + allInvisibleByFilter = false; } } @@ -1360,22 +1365,31 @@ void SceneShapeSubSceneOverride::update( MSubSceneContainer& container, const MF M3dView view; MString panelName; MSelectionList viewSelectedSet; - MObject component; frameContext.renderingDestination( panelName ); M3dView::getM3dViewFromModelPanel( panelName, view ); view.filteredObjectList( viewSelectedSet ); bool allInvisibleByFilter = false; - if ( view.viewIsFiltered() ) + if( view.viewIsFiltered() ) { allInvisibleByFilter = true; - for( auto &instance : m_instances ) + + // Create a selection list of the dagPaths which are visible after the view is filtered + MSelectionList visibleList; + view.filteredObjectList( visibleList ); + + // Create a selection list with all of our dag paths + MSelectionList dagPathList; + for( const auto &instance : m_instances ) { - if ( viewSelectedSet.hasItemPartly( instance.path, component ) ) - { - allInvisibleByFilter = false; - break; - } + dagPathList.add( instance.path ); + } + + // Intersect the two lists to determine if any of our dag paths remain unfiltered + visibleList.intersect( dagPathList, true ); + if( !visibleList.isEmpty() ) + { + allInvisibleByFilter = false; } } @@ -1404,6 +1418,16 @@ void SceneShapeSubSceneOverride::visitSceneLocations( const SceneInterface *scen return; } + // respect visibility attribute + if( sceneInterface->hasAttribute( SceneInterface::visibilityName ) ) + { + ConstBoolDataPtr vis = runTimeCast( sceneInterface->readAttribute( SceneInterface::visibilityName, m_time ) ); + if( vis && !vis->readable() ) + { + return; + } + } + MMatrix accumulatedMatrix; if( !isRoot ) { @@ -1430,7 +1454,6 @@ void SceneShapeSubSceneOverride::visitSceneLocations( const SceneInterface *scen } // Now handle current location. - if( isRoot ) { // override relative location for root as it would otherwise be empty @@ -1469,16 +1492,6 @@ void SceneShapeSubSceneOverride::visitSceneLocations( const SceneInterface *scen return; } - // respect visibility attribute - if( sceneInterface->hasAttribute( "scene:visible" ) ) - { - ConstBoolDataPtr vis = runTimeCast( sceneInterface->readAttribute( "scene:visible", m_time ) ); - if( vis && !vis->readable() ) - { - return; - } - } - IECore::ConstObjectPtr object = sceneInterface->readObject( m_time ); if( !objectCanBeRendered( object ) ) { @@ -1924,16 +1937,15 @@ void SceneShapeSubSceneOverride::collectInstances( Instances &instances ) const MDagPathArray dagPaths; dagNode.getAllPaths(dagPaths); size_t numInstances = dagPaths.length(); - instances.reserve( numInstances ); + for( size_t pathIndex = 0; pathIndex < numInstances; ++pathIndex ) { MDagPath& path = dagPaths[pathIndex]; MMatrix matrix = path.inclusiveMatrix(); bool pathSelected = isPathSelected( selectionList, path ); bool componentMode = componentsSelectable( path ); - MFnDagNode nodeFn( path ); - bool visible = path.isVisible(); + bool visible = IECoreMaya::SceneShapeInterface::isVisible( path ); instances.emplace_back( matrix, pathSelected, componentMode, path, visible ); } diff --git a/src/IECoreMaya/SceneShapeUI.cpp b/src/IECoreMaya/SceneShapeUI.cpp index f6951aaf61..25fa55810c 100644 --- a/src/IECoreMaya/SceneShapeUI.cpp +++ b/src/IECoreMaya/SceneShapeUI.cpp @@ -102,6 +102,13 @@ void SceneShapeUI::getDrawRequests( const MDrawInfo &info, bool objectAndActiveO { return; } + + // make sure we don't have an ancestral visibility override which makes us invisible + if( !SceneShapeInterface::isVisible( info.multiPath() ) ) + { + return; + } + // the node we're meant to be drawing SceneShape *sceneShape = (SceneShape *)surfaceShape(); diff --git a/src/IECoreMaya/bindings/LiveSceneBinding.cpp b/src/IECoreMaya/bindings/LiveSceneBinding.cpp index dfea085ee6..172cf3d199 100644 --- a/src/IECoreMaya/bindings/LiveSceneBinding.cpp +++ b/src/IECoreMaya/bindings/LiveSceneBinding.cpp @@ -198,6 +198,45 @@ void registerCustomAttributes( object namesFn, object readFn, object mightHaveFn } } +inline std::string dagPath( object liveSceneObject ) +{ + const LiveScene &liveScene = extract( liveSceneObject ); + return liveScene.dagPath().fullPathName().asChar(); +} + +inline list dagPathToPath( object dagObj ) +{ + list pathList; + extract dagPath( dagObj ); + + // Return the root path (an empty list) if we are given an empty string + // Maya doesn't really have a "root" dag path, so this is as close as we can get while simultaneously + // ensuring dagPathToPath( pathToDagPath( [] ) ) == [] + if( dagPath.check() && len( dagObj ) == 0 ) + { + return pathList; + } + + // Get the path, or throw if the dag path object is invalid + IECoreScene::SceneInterface::Path path; + LiveScene::dagPathToPath( dagPath(), path ); + for(const auto &name : path ) + { + pathList.append( name.string() ); + } + return pathList; +} + +inline std::string pathToDagPath( list pathList ) +{ + IECoreScene::SceneInterface::Path path; + container_utils::extend_container( path, pathList ); + + MDagPath dagPath; + LiveScene::pathToDagPath( path, dagPath ); + return dagPath.fullPathName().asChar(); +} + } // namespace void IECoreMaya::bindLiveScene() @@ -206,6 +245,9 @@ void IECoreMaya::bindLiveScene() .def( init<>() ) .def( "registerCustomTags", registerCustomTags, ( arg_( "hasFn" ), arg_( "readFn" ) ) ).staticmethod( "registerCustomTags" ) .def( "registerCustomAttributes", registerCustomAttributes, ( arg_( "namesFn" ), arg_( "readFn" ), arg_( "mightHaveFn" ) = object() ) ).staticmethod( "registerCustomAttributes" ) + .def( "dagPath", ::dagPath ) + .def( "dagPathToPath", ::dagPathToPath ).staticmethod( "dagPathToPath" ) + .def( "pathToDagPath", ::pathToDagPath ).staticmethod( "pathToDagPath" ) .def( "toMayaAttributeName", LiveScene::toMayaAttributeName ).staticmethod( "toMayaAttributeName" ) .def( "fromMayaAttributeName", LiveScene::fromMayaAttributeName ).staticmethod( "fromMayaAttributeName" ) .def_readonly("visibilityOverrideName", LiveScene::visibilityOverrideName ) diff --git a/test/IECoreMaya/LiveSceneTest.py b/test/IECoreMaya/LiveSceneTest.py index dea0f3852a..15a323aa4b 100644 --- a/test/IECoreMaya/LiveSceneTest.py +++ b/test/IECoreMaya/LiveSceneTest.py @@ -1449,6 +1449,56 @@ def testToFromMayaAttributeName( self ): def testVisibilityOverrideName( self ) : self.assertEqual( IECoreMaya.LiveScene.visibilityOverrideName, 'ieVisibility' ) + def testDagPath( self ): + group = str( maya.cmds.group( empty=True ) ) + cube = str( maya.cmds.polyCube( constructionHistory=False )[0] ) + maya.cmds.parent( cube, group ) + + liveScene = IECoreMaya.LiveScene() + cubeScene = liveScene.scene( [group, cube] ) + self.assertEqual( cubeScene.dagPath(), '|{}|{}'.format( group, cube ) ) + + def testDagPathToPath( self ): + group = str( maya.cmds.group( empty=True ) ) + cubeTransform = maya.cmds.polyCube( constructionHistory=False )[0] + maya.cmds.parent( cubeTransform, group ) + cubeTransform = str( maya.cmds.ls( cubeTransform, long=True )[0] ) + cubeShape = str( maya.cmds.listRelatives( cubeTransform, fullPath=True )[0] ) + + dagTransform = OpenMaya.MDagPath() + dagShape = OpenMaya.MDagPath() + sel = OpenMaya.MSelectionList() + sel.add( cubeTransform ) + sel.add( cubeShape ) + sel.getDagPath( 0, dagTransform ) + sel.getDagPath( 0, dagShape ) + + pathTransform = IECoreMaya.LiveScene.dagPathToPath( dagTransform.fullPathName() ) + self.assertEqual( pathTransform, cubeTransform[1:].split('|') ) + + pathShape = IECoreMaya.LiveScene.dagPathToPath( dagShape.fullPathName() ) + self.assertEqual( pathShape, cubeTransform[1:].split('|') ) + + pathRoot = IECoreMaya.LiveScene.dagPathToPath( '' ) + self.assertEqual( pathRoot, [] ) + + self.assertRaises( Exception, IECoreMaya.LiveScene.dagPathToPath, '|invalid' ) + + def testPathToDagPath( self ): + group = str( maya.cmds.group( empty=True ) ) + cube = str( maya.cmds.polyCube( constructionHistory=False )[0] ) + maya.cmds.parent( cube, group ) + + path = [group, cube] + dagTransform = IECoreMaya.LiveScene.pathToDagPath( path ) + self.assertEqual( dagTransform, '|{}|{}'.format( group, cube ) ) + + dagRoot = IECoreMaya.LiveScene.pathToDagPath( [] ) + self.assertEqual( dagRoot, '' ) + + self.assertRaises( Exception, IECoreMaya.LiveScene.pathToDagPath, ['invalid'] ) + + if __name__ == "__main__": IECoreMaya.TestProgram( plugins = [ "ieCore" ] )