From 9dee1d6f71f6c0243df0dd530693027d37fd0cdc Mon Sep 17 00:00:00 2001 From: Eric Mehl Date: Tue, 19 Dec 2023 15:25:00 -0500 Subject: [PATCH] LightPositionTool : Persist handle across scene transforms Previously we were using the final scene transform in world space to store and calculate the pivot and target points. This meant when switching from a node upstream of a `Group` node with a transformation, we would lose the handle because the world space position would not align with the handle. Resetting in that case is not necessary if we instead store the world space transformation from the scene that will receive the edits, what the `TransformTool` calls the transform space. Also, by using the upstream path as the key into the pivot and target maps, we can persist the handle across hierarchy changes if the transform allows. --- include/GafferSceneUI/LightPositionTool.h | 10 +++- src/GafferSceneUI/LightPositionTool.cpp | 61 ++++++++++++++++------- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/include/GafferSceneUI/LightPositionTool.h b/include/GafferSceneUI/LightPositionTool.h index 9e378e4eb77..d3c3712c0ac 100644 --- a/include/GafferSceneUI/LightPositionTool.h +++ b/include/GafferSceneUI/LightPositionTool.h @@ -60,6 +60,8 @@ class GAFFERSCENEUI_API LightPositionTool : public GafferSceneUI::TransformTool GAFFER_NODE_DECLARE_TYPE( GafferSceneUI::LightPositionTool, LightPositionToolTypeId, TransformTool ); + // Positions the current selection to cast a shadow from `shadowPivot` to `shadowTarget`, + // with the light `targetDistance` from the pivot. All coordinates are in world space. void position( const Imath::V3f &shadowPivot, const Imath::V3f &shadowTarget, const float targetDistance ); protected : @@ -140,8 +142,12 @@ class GAFFERSCENEUI_API LightPositionTool : public GafferSceneUI::TransformTool Gaffer::Signals::ScopedConnection m_contextChangedConnection; - std::unordered_map> m_shadowPivotMap; // world-space - std::unordered_map> m_shadowTargetMap; // world-space + // Pivots and targets are stored in transform space - the world space transform + // of the scene in which the transform will be applied. + // See `TransformTool::transformSpace()` for details. + std::unordered_map> m_shadowPivotMap; + std::unordered_map> m_shadowTargetMap; + std::unordered_map> m_shadowPivotDistanceMap; bool m_draggingTarget; diff --git a/src/GafferSceneUI/LightPositionTool.cpp b/src/GafferSceneUI/LightPositionTool.cpp index 9ee5c35f4ea..228df6ecedb 100644 --- a/src/GafferSceneUI/LightPositionTool.cpp +++ b/src/GafferSceneUI/LightPositionTool.cpp @@ -374,6 +374,11 @@ class ShadowHandle : public Handle return V3f( 0, 0, m_drag.updatedPosition( event ) - m_drag.startPosition() ); } + void setTransformToSceneSpace( const M44f &t ) + { + m_transformToSceneSpace = t; + } + protected : void renderHandle( const Style *style, Style::State state ) const override @@ -446,7 +451,7 @@ class ShadowHandle : public Handle ); pivotGroup->addChild( circle() ); - const V3f localPivot = m_shadowPivot.value() * fullTransformInverse; + const V3f localPivot = m_shadowPivot.value() * m_transformToSceneSpace * fullTransformInverse; pivotGroup->setTransform( M44f().scale( V3f( circleSize ) * ::rasterScaleFactor( this, localPivot ) ) * @@ -464,7 +469,7 @@ class ShadowHandle : public Handle IECoreGL::GroupPtr coneGroup = new IECoreGL::Group; coneGroup->addChild( unitCone() ); - localTarget = m_shadowTarget.value() * fullTransformInverse; + localTarget = m_shadowTarget.value() * m_transformToSceneSpace * fullTransformInverse; const V3f coneScale = V3f( coneSize ) * ::rasterScaleFactor( this, localTarget ); coneHeightOffset = V3f( 0, 0, g_unitConeHeight * coneScale.z ); @@ -504,10 +509,16 @@ class ShadowHandle : public Handle private : + // As with `LightPositionTool::m_shadowPivotMap` and `LightPositionTool::m_shadowTargetMap`, + // we store the pivot and target position in transform space. std::optional m_shadowPivot; std::optional m_shadowTarget; + std::optional m_pivotDistance; + // Used to transform the rendered elements from transform to world space. + M44f m_transformToSceneSpace; + LinearDrag m_drag; float m_startDistance; @@ -641,8 +652,11 @@ void LightPositionTool::updateHandles( float rasterScale ) std::optional shadowPivot = getShadowPivot(); std::optional shadowTarget = getShadowTarget(); + const M44f sceneToTransform = s.sceneToTransformSpace(); + const M44f sceneToTransformInverse = sceneToTransform.inverse(); shadowHandle->setShadowPivot( shadowPivot ); shadowHandle->setShadowTarget( shadowTarget ); + shadowHandle->setTransformToSceneSpace( sceneToTransformInverse ); if( !shadowPivot || !shadowTarget ) { @@ -655,12 +669,12 @@ void LightPositionTool::updateHandles( float rasterScale ) Context::Scope scopedContext( s.context() ); - const M44f worldTransform = s.scene()->fullTransform( s.path() ); - const V3f p = worldTransform.translation(); + const M44f transform = s.scene()->fullTransform( s.path() ) * sceneToTransform; + const V3f p = transform.translation(); const Line3f handleLine( shadowTarget.value(), shadowPivot.value() ); V3f direction; - worldTransform.multDirMatrix( V3f( 0, 0, -1.f ), direction ); + transform.multDirMatrix( V3f( 0, 0, -1.f ), direction ); const V3f handleDir = ( shadowTarget.value() - shadowPivot.value() ).normalized(); @@ -879,10 +893,10 @@ bool LightPositionTool::buttonRelease( const ButtonEvent &event ) bool LightPositionTool::placeTarget( const LineSegment3f &eventLine ) { ScenePlug::ScenePath scenePath; - V3f targetPos; + V3f gadgetTargetPos; const SceneGadget *sceneGadget = runTimeCast( view()->viewportGadget()->getPrimaryChild() ); - if( !sceneGadget->objectAt( eventLine, scenePath, targetPos ) ) + if( !sceneGadget->objectAt( eventLine, scenePath, gadgetTargetPos ) ) { return false; } @@ -891,20 +905,25 @@ bool LightPositionTool::placeTarget( const LineSegment3f &eventLine ) Selection s = selection().back(); ScriptNodePtr scriptNode = s.editTarget()->ancestor(); + const M44f sceneToTransformSpace = s.sceneToTransformSpace(); + const M44f sceneToTransformSpaceInverse = sceneToTransformSpace.inverse(); + + shadowHandle->setTransformToSceneSpace( sceneToTransformSpaceInverse ); + if( getTargetMode() == TargetMode::ShadowPivot ) { - const V3f newPivot = targetPos * sceneGadget->fullTransform(); + const V3f newPivot = gadgetTargetPos * sceneGadget->fullTransform() * sceneToTransformSpace; if( !shadowHandle->getShadowPivot() ) { setShadowPivotDistance( - ( newPivot - ( V3f( 0 ) * s.orientedTransform( Orientation::World ) ) ).length() + ( newPivot - ( V3f( 0 ) * ( s.orientedTransform( Orientation::World ) * sceneToTransformSpace ) ) ).length() ); } setShadowPivot( newPivot, scriptNode ); } else if( getTargetMode() == TargetMode::ShadowTarget ) { - setShadowTarget( targetPos * sceneGadget->fullTransform(), scriptNode ); + setShadowTarget( gadgetTargetPos * sceneGadget->fullTransform() * sceneToTransformSpace, scriptNode ); } if( !shadowHandle->getShadowPivot() || !shadowHandle->getShadowTarget() ) @@ -912,7 +931,11 @@ bool LightPositionTool::placeTarget( const LineSegment3f &eventLine ) return false; } - position( shadowHandle->getShadowPivot().value(), shadowHandle->getShadowTarget().value(), shadowHandle->getPivotDistance().value() ); + position( + shadowHandle->getShadowPivot().value() * sceneToTransformSpaceInverse, + shadowHandle->getShadowTarget().value() * sceneToTransformSpaceInverse, + shadowHandle->getPivotDistance().value() + ); return true; } @@ -938,7 +961,7 @@ void LightPositionTool::setShadowPivot( const V3f &p, ScriptNodePtr scriptNode ) { std::optional currentValue = getShadowPivot(); auto shadowHandle = static_cast( m_shadowHandle.get() ); - const auto pathString = ScenePlug::pathToString( selection().back().path() ); + const auto pathString = ScenePlug::pathToString( selection().back().upstreamPath() ); Action::enact( scriptNode, [t = LightPositionToolPtr( this ), k = pathString, p, shadowHandle]() { @@ -947,7 +970,7 @@ void LightPositionTool::setShadowPivot( const V3f &p, ScriptNodePtr scriptNode ) }, [t = LightPositionToolPtr( this ), k = pathString, currentValue, shadowHandle]() { t->m_shadowPivotMap[k] = currentValue; - if( ScenePlug::pathToString( t->selection().back().path() ) == k ) + if( ScenePlug::pathToString( t->selection().back().upstreamPath() ) == k ) { shadowHandle->setShadowPivot( currentValue ); } @@ -957,7 +980,7 @@ void LightPositionTool::setShadowPivot( const V3f &p, ScriptNodePtr scriptNode ) std::optional LightPositionTool::getShadowPivot() const { - auto it = m_shadowPivotMap.find( ScenePlug::pathToString( selection().back().path() ) ); + auto it = m_shadowPivotMap.find( ScenePlug::pathToString( selection().back().upstreamPath() ) ); if( it == m_shadowPivotMap.end() ) { return std::nullopt; @@ -969,7 +992,7 @@ void LightPositionTool::setShadowTarget( const V3f &p, ScriptNodePtr scriptNode { std::optional currentValue = getShadowTarget(); auto shadowHandle = static_cast( m_shadowHandle.get() ); - const auto pathString = ScenePlug::pathToString( selection().back().path() ); + const auto pathString = ScenePlug::pathToString( selection().back().upstreamPath() ); Action::enact( scriptNode, [t = LightPositionToolPtr( this ), k = pathString, p, shadowHandle]() { @@ -978,7 +1001,7 @@ void LightPositionTool::setShadowTarget( const V3f &p, ScriptNodePtr scriptNode }, [t = LightPositionToolPtr( this ), k = pathString, currentValue, shadowHandle]() { t->m_shadowTargetMap[k] = currentValue; - if( ScenePlug::pathToString( t->selection().back().path() ) == k ) + if( ScenePlug::pathToString( t->selection().back().upstreamPath() ) == k ) { shadowHandle->setShadowTarget( currentValue ); } @@ -988,7 +1011,7 @@ void LightPositionTool::setShadowTarget( const V3f &p, ScriptNodePtr scriptNode std::optional LightPositionTool::getShadowTarget() const { - auto it = m_shadowTargetMap.find( ScenePlug::pathToString( selection().back().path() ) ); + auto it = m_shadowTargetMap.find( ScenePlug::pathToString( selection().back().upstreamPath() ) ); if( it == m_shadowTargetMap.end() ) { return std::nullopt; @@ -998,13 +1021,13 @@ std::optional LightPositionTool::getShadowTarget() const void LightPositionTool::setShadowPivotDistance( const float d ) { - m_shadowPivotDistanceMap[ScenePlug::pathToString( selection().back().path() )] = d; + m_shadowPivotDistanceMap[ScenePlug::pathToString( selection().back().upstreamPath() )] = d; static_cast( m_shadowHandle.get() )->setPivotDistance( d ); } std::optional LightPositionTool::getShadowPivotDistance() const { - auto it = m_shadowPivotDistanceMap.find( ScenePlug::pathToString( selection().back().path() ) ); + auto it = m_shadowPivotDistanceMap.find( ScenePlug::pathToString( selection().back().upstreamPath() ) ); if( it == m_shadowPivotDistanceMap.end() ) { return std::nullopt;