From d1a7492e84bd845eec5d70c16c5c57b18317f173 Mon Sep 17 00:00:00 2001 From: Brian R Hanke <59420805+BrianHanke@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:59:12 -0500 Subject: [PATCH] Add support for camera guides Added preliminary support for camera guides (action safe, title safe and rule of thirds). --- Changes.md | 1 + python/GafferSceneUI/SceneViewUI.py | 86 ++++++++++++++++ src/GafferSceneUI/SceneView.cpp | 154 +++++++++++++++++++++++++++- 3 files changed, 238 insertions(+), 3 deletions(-) diff --git a/Changes.md b/Changes.md index 894cc9ab84..501f1eba0c 100644 --- a/Changes.md +++ b/Changes.md @@ -4,6 +4,7 @@ Improvements ------------ +- Added support for camera guides (action safe, title safe and rule of thirds). - Instancer : - Added `inactiveIds` plug for selecting primitive variables to disable some instances. - Added support for 64 bit integer ids (matching what is loaded from USD). diff --git a/python/GafferSceneUI/SceneViewUI.py b/python/GafferSceneUI/SceneViewUI.py index 93d365ce54..dcf155d1cf 100644 --- a/python/GafferSceneUI/SceneViewUI.py +++ b/python/GafferSceneUI/SceneViewUI.py @@ -801,6 +801,64 @@ def __menuDefinition( self ) : } ) + # BHGC START + + m.append( "/GuidesDivider", { "divider" : True } ) + + titleSafeEnabled = self.getPlug()["titleSafeEnabled"].getValue() + actionSafeEnabled = self.getPlug()["actionSafeEnabled"].getValue() + customGridEnabled = self.getPlug()["customGridEnabled"].getValue() + + # possible way to iterate over all guides instead of doing them one by one: + + # cameraGuides = self.getPlug()["guidesEnabled"].getValue() + + # allGuides = [ "Title Safe", "Action Safe", "Rule of Thirds" ] + # for guide in allGuides : + # newGuides = IECore.StringVectorData( [ + # g for g in allGuides + # if + # ( g == guide ) + # ] ) + # m.append( + # "/Guides/{}".format( guide ), + # { + # "checkBox" : cameraGuidesEnabled, + # #"active" : cameraGuidesEnabled, + # #"command" : functools.partial( self.getPlug()["cameraGuides"]["value"].setValue, newGuides ), + # "command" : functools.partial( Gaffer.WeakMethod( self.__enableGuides ), "" ) + # } + # ) + + # doing them one by one for now (not sure if the "active" thing is needed): + + m.append( + "/Guides/Action Safe", + { + "checkBox" : actionSafeEnabled, + # "active" : actionSafeEnabled, + "command" : functools.partial( Gaffer.WeakMethod( self.__enableActionSafe ), "" ) + } + ) + m.append( + "/Guides/Title Safe", + { + "checkBox" : titleSafeEnabled, + # "active" : titleSafeEnabled, + "command" : functools.partial( Gaffer.WeakMethod( self.__enableTitleSafe ), "" ) + } + ) + m.append( + "/Guides/Rule of Thirds", + { + "checkBox" : customGridEnabled, + # "active" : customGridEnabled, + "command" : functools.partial( Gaffer.WeakMethod( self.__enableCustomGrid ), "" ) + } + ) + + # BHGC END + m.append( "/BrowseDivider", { "divider" : True } ) m.append( @@ -836,6 +894,34 @@ def __lookThrough( self, path, *unused ) : self.getPlug()["lookThroughEnabled"].setValue( True ) self.getPlug()["lookThroughCamera"].setValue( path ) + # BHGC START + + # separate functions for each guide, should probably combine these + # is there a better way to toggle than checking if off or on first? + + def __enableTitleSafe( self, *unused ) : + + if( self.getPlug()["titleSafeEnabled"].getValue() ) : + self.getPlug()["titleSafeEnabled"].setValue( False ) + else : + self.getPlug()["titleSafeEnabled"].setValue( True ) + + def __enableActionSafe( self, *unused ) : + + if( self.getPlug()["actionSafeEnabled"].getValue() ) : + self.getPlug()["actionSafeEnabled"].setValue( False ) + else : + self.getPlug()["actionSafeEnabled"].setValue( True ) + + def __enableCustomGrid( self, *unused ) : + + if( self.getPlug()["customGridEnabled"].getValue() ) : + self.getPlug()["customGridEnabled"].setValue( False ) + else : + self.getPlug()["customGridEnabled"].setValue( True ) + + # BHGC END + def __browse( self ) : w = GafferSceneUI.ScenePathPlugValueWidget( diff --git a/src/GafferSceneUI/SceneView.cpp b/src/GafferSceneUI/SceneView.cpp index 6d9a57e112..fc2a015d92 100644 --- a/src/GafferSceneUI/SceneView.cpp +++ b/src/GafferSceneUI/SceneView.cpp @@ -1081,6 +1081,43 @@ class CameraOverlay : public GafferUI::Gadget dirty( DirtyType::Render ); } + // BHGC START + + // separate functions/variables for each guide, should probably combine all this + // i just copied the resolution/aperture gate syntax, not sure if this is ideal + + void actionSafeEnabled( const bool &actionSafeEnabled ) + { + if( actionSafeEnabled == m_actionSafeEnabled ) + { + return; + } + m_actionSafeEnabled = actionSafeEnabled; + dirty( DirtyType::Render ); + } + + void titleSafeEnabled( const bool &titleSafeEnabled ) + { + if( titleSafeEnabled == m_titleSafeEnabled ) + { + return; + } + m_titleSafeEnabled = titleSafeEnabled; + dirty( DirtyType::Render ); + } + + void customGridEnabled( const bool &customGridEnabled ) + { + if( customGridEnabled == m_customGridEnabled ) + { + return; + } + m_customGridEnabled = customGridEnabled; + dirty( DirtyType::Render ); + } + + // BHGC END + const std::string &getIcon() const { return m_icon; @@ -1140,8 +1177,66 @@ class CameraOverlay : public GafferUI::Gadget { glEnable( GL_LINE_SMOOTH ); glLineWidth( 1.5f ); + glColor4f( 0, 0.25, 0, 1.0f ); + + // BHGC START + + // stipple looks good, but it doesn't work becuase the customGrid edges overlap + + // glEnable( GL_LINE_STIPPLE ); + // glLineStipple(1, 0x00FF); + + V2f gateDiff = V2f( m_resolutionGate.max - m_resolutionGate.min ); + + V2f titlePercent = gateDiff * V2f( std::sqrt( 0.8 ) ); + V2f actionPercent = gateDiff * V2f( std::sqrt( 0.9 ) ); + + titlePercent = ( gateDiff - titlePercent ) / 2; + actionPercent = ( gateDiff - actionPercent ) / 2; + + if( m_titleSafeEnabled ) + { + Box2f titleSafe = Box2f( ( m_resolutionGate.min + titlePercent ), ( m_resolutionGate.max - titlePercent ) ); + + style->renderRectangle( titleSafe ); + } + + if( m_actionSafeEnabled ) + { + Box2f actionSafe = Box2f( ( m_resolutionGate.min + actionPercent ), ( m_resolutionGate.max - actionPercent ) ); + + style->renderRectangle( actionSafe ); + } + + // custom grid divisions, 3x3 is rule of thirds + + int div_h = 3; + int div_v = 3; + + if( m_customGridEnabled ) + { + V2f fraction = V2f( gateDiff / V2f( div_h, div_v ) ); + vector customGrid; + customGrid.reserve( div_h * div_v ); // I read that this is galaxy brain C++, maybe not needed, haha + + for( int v = 0; v < div_v; v++ ) + { + for( int h = 0; h < div_h; h++ ) + { + customGrid.push_back( Box2f( m_resolutionGate.min + ( fraction * V2f( h, v ) ), m_resolutionGate.min + ( fraction * V2f( h, v ) ) + fraction ) ); + } + } + + for( const auto &i : customGrid ) + { + style->renderRectangle( i ); + } + } + + // glDisable( GL_LINE_STIPPLE ); + + // BHGC END - glColor4f( 0.5, 0.5, 0.5, 0.5 ); style->renderRectangle( Box2f( V2f( lerp( m_resolutionGate.min.x, m_resolutionGate.max.x, m_cropWindow.min.x ), @@ -1153,7 +1248,7 @@ class CameraOverlay : public GafferUI::Gadget ) ) ); - glColor4f( 0, 0.25, 0, 1.0f ); + //glColor4f( 0, 0.25, 0, 1.0f ); style->renderRectangle( m_resolutionGate ); if( m_overscan[0] != 0.0f || m_overscan[1] != 0.0f || m_overscan[2] != 0.0f || m_overscan[3] != 0.0f ) @@ -1228,6 +1323,14 @@ class CameraOverlay : public GafferUI::Gadget std::string m_caption; std::string m_icon; + // BHGC START + + bool m_actionSafeEnabled; + bool m_titleSafeEnabled; + bool m_customGridEnabled; + + // BHGC END + }; IE_CORE_DECLAREPTR( CameraOverlay ) @@ -1382,6 +1485,16 @@ class SceneView::Camera : public Signals::Trackable ) ); + // BHGC START + + // create plugs for each guide, combine these three into one? + + plug->addChild( new BoolPlug( "actionSafeEnabled", Plug::In, false, Plug::Default & ~Plug::AcceptsInputs ) ); + plug->addChild( new BoolPlug( "titleSafeEnabled", Plug::In, false, Plug::Default & ~Plug::AcceptsInputs ) ); + plug->addChild( new BoolPlug( "customGridEnabled", Plug::In, false, Plug::Default & ~Plug::AcceptsInputs ) ); + + // BHGC END + view->addChild( plug ); // Set up our nodes. @@ -1526,6 +1639,25 @@ class SceneView::Camera : public Signals::Trackable return plug()->getChild( 6 ); } + // BHGC START + + const Gaffer::BoolPlug *titleSafeEnabledPlug() const + { + return plug()->getChild( "titleSafeEnabled" ); + } + + const Gaffer::BoolPlug *actionSafeEnabledPlug() const + { + return plug()->getChild( "actionSafeEnabled" ); + } + + const Gaffer::BoolPlug *customGridEnabledPlug() const + { + return plug()->getChild( "customGridEnabled" ); + } + + // BHGC END + SceneGadget *sceneGadget() { return static_cast( m_view->viewportGadget()->getPrimaryChild() ); @@ -1569,6 +1701,8 @@ class SceneView::Camera : public Signals::Trackable return; } + // BHGC - added the guides here, i assume that's needed in order to redraw things when they're turned on and off? + if( plug == scenePlug()->childNamesPlug() || plug == scenePlug()->globalsPlug() || @@ -1576,7 +1710,10 @@ class SceneView::Camera : public Signals::Trackable plug == scenePlug()->transformPlug() || plug == lookThroughEnabledPlug() || plug == lookThroughCameraPlug() || - plug == freeCameraPlug() + plug == freeCameraPlug() || + plug == actionSafeEnabledPlug() || // BHGC + plug == titleSafeEnabledPlug() || // BHGC + plug == customGridEnabledPlug() // BHGC ) { m_lookThroughCameraDirty = m_viewportCameraDirty = true; @@ -1867,6 +2004,17 @@ class SceneView::Camera : public Signals::Trackable ( apertureGate.min - viewportScreenWindow.min ) / viewportScreenWindow.size() * viewport, ( apertureGate.max - viewportScreenWindow.min ) / viewportScreenWindow.size() * viewport ) ); + + // BHGC START + + // draw the guides, combine these three into one? + + m_overlay->actionSafeEnabled( m_view->cameraPlug()->getChild( "actionSafeEnabled" )->getValue() ); + m_overlay->titleSafeEnabled( m_view->cameraPlug()->getChild( "titleSafeEnabled" )->getValue() ); + m_overlay->customGridEnabled( m_view->cameraPlug()->getChild( "customGridEnabled" )->getValue() ); + + // BHGC END + m_overlay->setVisible( true ); // Now set up a camera that can see all of the aperture and resolution gates.