Skip to content

Commit

Permalink
LightPositionTool : Persist handle across scene transforms
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ericmehl committed Dec 19, 2023
1 parent 9eed5ea commit 9dee1d6
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 21 deletions.
10 changes: 8 additions & 2 deletions include/GafferSceneUI/LightPositionTool.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 :
Expand Down Expand Up @@ -140,8 +142,12 @@ class GAFFERSCENEUI_API LightPositionTool : public GafferSceneUI::TransformTool

Gaffer::Signals::ScopedConnection m_contextChangedConnection;

std::unordered_map<std::string, std::optional<Imath::V3f>> m_shadowPivotMap; // world-space
std::unordered_map<std::string, std::optional<Imath::V3f>> 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<std::string, std::optional<Imath::V3f>> m_shadowPivotMap;
std::unordered_map<std::string, std::optional<Imath::V3f>> m_shadowTargetMap;

std::unordered_map<std::string, std::optional<float>> m_shadowPivotDistanceMap;

bool m_draggingTarget;
Expand Down
61 changes: 42 additions & 19 deletions src/GafferSceneUI/LightPositionTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ) ) *
Expand All @@ -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 );

Expand Down Expand Up @@ -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<V3f> m_shadowPivot;
std::optional<V3f> m_shadowTarget;

std::optional<float> m_pivotDistance;

// Used to transform the rendered elements from transform to world space.
M44f m_transformToSceneSpace;

LinearDrag m_drag;
float m_startDistance;

Expand Down Expand Up @@ -641,8 +652,11 @@ void LightPositionTool::updateHandles( float rasterScale )
std::optional<V3f> shadowPivot = getShadowPivot();
std::optional<V3f> shadowTarget = getShadowTarget();

const M44f sceneToTransform = s.sceneToTransformSpace();
const M44f sceneToTransformInverse = sceneToTransform.inverse();
shadowHandle->setShadowPivot( shadowPivot );
shadowHandle->setShadowTarget( shadowTarget );
shadowHandle->setTransformToSceneSpace( sceneToTransformInverse );

if( !shadowPivot || !shadowTarget )
{
Expand All @@ -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();

Expand Down Expand Up @@ -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<SceneGadget>( view()->viewportGadget()->getPrimaryChild() );
if( !sceneGadget->objectAt( eventLine, scenePath, targetPos ) )
if( !sceneGadget->objectAt( eventLine, scenePath, gadgetTargetPos ) )
{
return false;
}
Expand All @@ -891,28 +905,37 @@ bool LightPositionTool::placeTarget( const LineSegment3f &eventLine )
Selection s = selection().back();
ScriptNodePtr scriptNode = s.editTarget()->ancestor<ScriptNode>();

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() )
{
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;
}
Expand All @@ -938,7 +961,7 @@ void LightPositionTool::setShadowPivot( const V3f &p, ScriptNodePtr scriptNode )
{
std::optional<V3f> currentValue = getShadowPivot();
auto shadowHandle = static_cast<ShadowHandle *>( 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]() {
Expand All @@ -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 );
}
Expand All @@ -957,7 +980,7 @@ void LightPositionTool::setShadowPivot( const V3f &p, ScriptNodePtr scriptNode )

std::optional<V3f> 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;
Expand All @@ -969,7 +992,7 @@ void LightPositionTool::setShadowTarget( const V3f &p, ScriptNodePtr scriptNode
{
std::optional<V3f> currentValue = getShadowTarget();
auto shadowHandle = static_cast<ShadowHandle *>( 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]() {
Expand All @@ -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 );
}
Expand All @@ -988,7 +1011,7 @@ void LightPositionTool::setShadowTarget( const V3f &p, ScriptNodePtr scriptNode

std::optional<V3f> 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;
Expand All @@ -998,13 +1021,13 @@ std::optional<V3f> 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<ShadowHandle *>( m_shadowHandle.get() )->setPivotDistance( d );
}

std::optional<float> 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;
Expand Down

0 comments on commit 9dee1d6

Please sign in to comment.