Skip to content

Commit

Permalink
[usdview] Revert "Pick prims"/"Pick instances" to their old behavior …
Browse files Browse the repository at this point in the history
…of selecting the root boundable of the picked object. Add "Pick prototypes", with the current behavior of selecting an instance of the picked gprim.

This necessitates passing HdInstancerContext into python.  The instancer munging code in the selection model and the rollover handling has also been updated for the new API.

Fixes #1196

(Internal change: 2078845)
  • Loading branch information
tcauchois authored and pixar-oss committed Jun 30, 2020
1 parent 9669c83 commit e0c4dc3
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from pxr.Usdviewq.qt import QtCore

INSTANCER_PATH = "/Foo/Cube/Instancer"
PROTO_PATH = "/Foo/Cube/Instancer/Protos/Proto1/cube"
FOO_PATH = "/Foo"

# Remove any unwanted visuals from the view and set complexity.
Expand Down Expand Up @@ -66,7 +67,7 @@ def _checkInstanceSelection(appController, path, instance):
def _testPickPrims(appController):
_setPickModeAction(appController, appController._ui.actionPick_Prims)
pt = (0, 0, 0)
appController.onPrimSelected(INSTANCER_PATH, 0, pt, QtCore.Qt.LeftButton, 0)
appController.onPrimSelected(PROTO_PATH, 0, INSTANCER_PATH, 0, pt, QtCore.Qt.LeftButton, 0)

_checkPrimSelection(appController, INSTANCER_PATH)
_checkNoInstancesSelected(appController, INSTANCER_PATH)
Expand All @@ -75,7 +76,7 @@ def _testPickPrims(appController):
def _testPickModels(appController):
_setPickModeAction(appController, appController._ui.actionPick_Models)
pt = (0, 0, 0)
appController.onPrimSelected(INSTANCER_PATH, 0, pt, QtCore.Qt.LeftButton, 0)
appController.onPrimSelected(PROTO_PATH, 0, INSTANCER_PATH, 0, pt, QtCore.Qt.LeftButton, 0)

_checkPrimSelection(appController, FOO_PATH)
_checkNoInstancesSelected(appController, INSTANCER_PATH)
Expand All @@ -84,14 +85,24 @@ def _testPickModels(appController):
def _testPickInstances(appController):
_setPickModeAction(appController, appController._ui.actionPick_Instances)
pt = (0, 0, 0)
appController.onPrimSelected(INSTANCER_PATH, 0, pt, QtCore.Qt.LeftButton, 0)
appController.onPrimSelected(PROTO_PATH, 0, INSTANCER_PATH, 0, pt, QtCore.Qt.LeftButton, 0)

_checkPrimSelection(appController, INSTANCER_PATH)
_checkInstanceSelection(appController, INSTANCER_PATH, 0)

# Test picking a prototype.
def _testPickPrototypes(appController):
_setPickModeAction(appController, appController._ui.actionPick_Prototypes)
pt = (0, 0, 0)
appController.onPrimSelected(PROTO_PATH, 0, INSTANCER_PATH, 0, pt, QtCore.Qt.LeftButton, 0)

_checkPrimSelection(appController, PROTO_PATH)
_checkInstanceSelection(appController, PROTO_PATH, 0)

# Test that selection highlighting works properly in usdview
def testUsdviewInputFunction(appController):
_modifySettings(appController)
_testPickPrims(appController)
_testPickModels(appController)
_testPickInstances(appController)
_testPickPrototypes(appController)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 12 additions & 2 deletions pxr/usdImaging/usdImagingGL/wrapEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ _TestIntersection(
SdfPath hitPrimPath;
SdfPath hitInstancerPath;
int hitInstanceIndex;
HdInstancerContext hitInstancerContext;

self.TestIntersection(
viewMatrix,
Expand All @@ -65,9 +66,18 @@ _TestIntersection(
&hitPoint,
&hitPrimPath,
&hitInstancerPath,
&hitInstanceIndex);
&hitInstanceIndex,
&hitInstancerContext);

return boost::python::make_tuple(hitPoint, hitPrimPath, hitInstancerPath, hitInstanceIndex);
SdfPath topLevelPath = SdfPath::EmptyPath();
int topLevelInstanceIndex = -1;
if (hitInstancerContext.size() > 0) {
topLevelPath = hitInstancerContext[0].first;
topLevelInstanceIndex = hitInstancerContext[0].second;
}

return boost::python::make_tuple(hitPoint, hitPrimPath,
hitInstanceIndex, topLevelPath, topLevelInstanceIndex);
}

static void
Expand Down
66 changes: 42 additions & 24 deletions pxr/usdImaging/usdviewq/appController.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
from .common import (UIBaseColors, UIPropertyValueSourceColors, UIFonts,
GetPropertyColor, GetPropertyTextFont,
Timer, Drange, BusyContext, DumpMallocTags,
GetValueAndDisplayString, GetInstanceIdForIndex,
ResetSessionVisibility, InvisRootPrims, GetAssetCreationTime,
GetValueAndDisplayString, ResetSessionVisibility,
InvisRootPrims, GetAssetCreationTime,
PropertyViewIndex, PropertyViewIcons, PropertyViewDataRoles,
RenderModes, ColorCorrectionModes, ShadedRenderModes,
PickModes, SelectionHighlightModes, CameraMaskModes,
Expand Down Expand Up @@ -659,7 +659,8 @@ def __init__(self, parserData, resolverContextFn):
self._pickModeActions = (
self._ui.actionPick_Prims,
self._ui.actionPick_Models,
self._ui.actionPick_Instances)
self._ui.actionPick_Instances,
self._ui.actionPick_Prototypes)
for action in self._pickModeActions:
self._ui.pickModeActionGroup.addAction(action)

Expand Down Expand Up @@ -4535,7 +4536,7 @@ def unloadSelectedPrims(self):
def onStageViewMouseDrag(self):
return

def onPrimSelected(self, path, instanceIndex, point, button, modifiers):
def onPrimSelected(self, path, instanceIndex, topLevelPath, topLevelInstanceIndex, point, button, modifiers):

# Ignoring middle button until we have something
# meaningfully different for it to do
Expand All @@ -4562,37 +4563,57 @@ def onPrimSelected(self, path, instanceIndex, point, button, modifiers):
if path != Sdf.Path.emptyPath:
prim = self._dataModel.stage.GetPrimAtPath(path)

# Model picking ignores instancing, but selects the enclosing
# model of the picked prim.
if self._dataModel.viewSettings.pickMode == PickModes.MODELS:
if prim.IsModel():
model = prim
else:
model = GetEnclosingModelPrim(prim)
if model:
prim = model
instanceIndex = ALL_INSTANCES

if self._dataModel.viewSettings.pickMode != PickModes.INSTANCES:
# Prim picking selects the top level boundable: either a
# gprim, or the top-level point instancer of a point
# instanced gprim. It discards the instance index.
elif self._dataModel.viewSettings.pickMode == PickModes.PRIMS:
topLevelPrim = self._dataModel.stage.GetPrimAtPath(topLevelPath)
if topLevelPrim:
prim = topLevelPrim
instanceIndex = ALL_INSTANCES

instance = instanceIndex
if instanceIndex != ALL_INSTANCES:
instanceId = GetInstanceIdForIndex(prim, instanceIndex,
self._dataModel.currentFrame)
if instanceId is not None:
instance = instanceId
# Instance picking is like prim picking, but selects a
# particular instance of the top-level point instancer
# (if applicable).
elif self._dataModel.viewSettings.pickMode == PickModes.INSTANCES:
topLevelPrim = self._dataModel.stage.GetPrimAtPath(topLevelPath)
if topLevelPrim:
prim = topLevelPrim
instanceIndex = topLevelInstanceIndex

# Prototype picking selects a specific instance of the
# actual picked gprim, if the gprim is point-instanced.
# This differs from instance picking by selecting the gprim,
# rather than the prototype subtree; and selecting only one
# drawn instance, rather than all sub-instances of a top-level
# instance (for nested point instancers).
# elif self._dataModel.viewSettings.pickMode == PickModes.PROTOTYPES:
# Just pass the selection info through!

if shiftPressed:
# Clicking prim while holding shift adds it to the
# selection.
self._dataModel.selection.addPrim(prim, instance)
self._dataModel.selection.addPrim(prim, instanceIndex)
elif ctrlPressed:
# Clicking prim while holding ctrl toggles it in the
# selection.
self._dataModel.selection.togglePrim(prim, instance)
self._dataModel.selection.togglePrim(prim, instanceIndex)
else:
# Clicking prim with no modifiers sets it as the
# selection.
self._dataModel.selection.switchToPrimPath(
prim.GetPath(), instance)
prim.GetPath(), instanceIndex)

elif not shiftPressed and not ctrlPressed:
# Clicking the background with no modifiers clears the
Expand All @@ -4613,7 +4634,7 @@ def onPrimSelected(self, path, instanceIndex, point, button, modifiers):
QtCore.Qt.KeyboardModifiers())
QtWidgets.QApplication.sendEvent(self._stageView, mrEvent)

def onRollover(self, path, instanceIndex, modifiers):
def onRollover(self, path, instanceIndex, topLevelPath, topLevelInstanceIndex, modifiers):
prim = self._dataModel.stage.GetPrimAtPath(path)
if prim:
headerStr = ""
Expand Down Expand Up @@ -4710,15 +4731,16 @@ def _HTMLEscape(s):
if mesh:
propertyStr += "<br> -- <em>subdivisionScheme</em> = %s" %\
mesh.GetSubdivisionSchemeAttr().Get()
pi = UsdGeom.PointInstancer(prim)
topLevelPrim = self._dataModel.stage.GetPrimAtPath(topLevelPath)
pi = UsdGeom.PointInstancer(topLevelPrim)
if pi:
indices = pi.GetProtoIndicesAttr().Get(
self._dataModel.currentFrame)
propertyStr += "<br> -- <em>%d instances</em>" % len(indices)
protos = pi.GetPrototypesRel().GetForwardedTargets()
propertyStr += "<br> -- <em>%d unique prototypes</em>" % len(protos)
if instanceIndex >= 0 and instanceIndex < len(indices):
protoIndex = indices[instanceIndex]
if topLevelInstanceIndex >= 0 and topLevelInstanceIndex < len(indices):
protoIndex = indices[topLevelInstanceIndex]
if protoIndex < len(protos):
currProtoPath = protos[protoIndex]
# If, as is common, proto is beneath the PI,
Expand Down Expand Up @@ -4773,12 +4795,8 @@ def _HTMLEscape(s):
instanceStr = "<hr><b>Instancing:</b><br>"
instanceStr += "<nobr><small><em>Instance of master:</em></small> %s</nobr>" % \
str(prim.GetMaster().GetPath())
elif instanceIndex != -1:
instanceStr = "<hr><b>Instance Index:</b> %d" % instanceIndex
instanceId = GetInstanceIdForIndex(prim, instanceIndex,
self._dataModel.currentFrame)
if instanceId is not None:
instanceStr += "<br><b>Instance Id:</b> %d" % instanceId
elif topLevelInstanceIndex != -1:
instanceStr = "<hr><b>Instance Id:</b> %d" % topLevelInstanceIndex

# Then put it all together
tip = headerStr + propertyStr + materialStr + instanceStr + aiStr + vsStr
Expand Down
1 change: 1 addition & 0 deletions pxr/usdImaging/usdviewq/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class PickModes(ConstantGroup):
PRIMS = "Prims"
MODELS = "Models"
INSTANCES = "Instances"
PROTOTYPES = "Prototypes"

class SelectionHighlightModes(ConstantGroup):
# Selection highlight modes
Expand Down
9 changes: 9 additions & 0 deletions pxr/usdImaging/usdviewq/mainWindowUI.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,7 @@
<addaction name="actionPick_Prims"/>
<addaction name="actionPick_Models"/>
<addaction name="actionPick_Instances"/>
<addaction name="actionPick_Prototypes"/>
</widget>
<widget class="QMenu" name="menuSelection_Highlighting">
<property name="title">
Expand Down Expand Up @@ -2596,6 +2597,14 @@
<string>Instances</string>
</property>
</action>
<action name="actionPick_Prototypes">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Prototypes</string>
</property>
</action>
<action name="actionToggle_Viewer_Mode">
<property name="text">
<string>Toggle Viewer Mode</string>
Expand Down
48 changes: 21 additions & 27 deletions pxr/usdImaging/usdviewq/stageView.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@
from pxr import CameraUtil

from .common import (RenderModes, ColorCorrectionModes, ShadedRenderModes, Timer,
ReportMetricSize, GetInstanceIndicesForIds,
SelectionHighlightModes, DEBUG_CLIPPING)
ReportMetricSize, SelectionHighlightModes, DEBUG_CLIPPING)
from .rootDataModel import RootDataModel
from .selectionDataModel import ALL_INSTANCES, SelectionDataModel
from .viewSettingsDataModel import ViewSettingsDataModel
Expand Down Expand Up @@ -662,14 +661,18 @@ def viewSettings(self):
# First arg is primPath, (which could be empty Path)
# Second arg is instanceIndex (or UsdImagingGL.ALL_INSTANCES for all
# instances)
# Third arg is selectedPoint
# Fourth and Fifth args represent state at time of the pick
signalPrimSelected = QtCore.Signal(Sdf.Path, int, Gf.Vec3f, QtCore.Qt.MouseButton,
# Third and fourth arg are primPath, instanceIndex, of root level
# boundable (if applicable).
# Fifth arg is selectedPoint
# Sixth and seventh args represent state at time of the pick
signalPrimSelected = QtCore.Signal(Sdf.Path, int, Sdf.Path, int, Gf.Vec3f,
QtCore.Qt.MouseButton,
QtCore.Qt.KeyboardModifiers)

# Only raised when StageView has been told to do so, setting
# rolloverPicking to True
signalPrimRollover = QtCore.Signal(Sdf.Path, int, Gf.Vec3f, QtCore.Qt.KeyboardModifiers)
signalPrimRollover = QtCore.Signal(Sdf.Path, int, Sdf.Path, int,
Gf.Vec3f, QtCore.Qt.KeyboardModifiers)
signalMouseDrag = QtCore.Signal()
signalErrorMessage = QtCore.Signal(str)

Expand Down Expand Up @@ -1325,16 +1328,6 @@ def updateSelection(self):
continue
primInstances = allInstances[prim]
if primInstances != ALL_INSTANCES:

# If the prim is a point instancer and has authored instance
# ids, the selection contains instance ids rather than
# instance indices. We need to convert these back to indices
# before feeding them to the renderer.
instanceIds = GetInstanceIndicesForIds(prim, primInstances,
self._dataModel.currentFrame)
if instanceIds is not None:
primInstances = instanceIds

for instanceIndex in primInstances:
renderer.AddSelected(prim.GetPath(), instanceIndex)
else:
Expand Down Expand Up @@ -2094,9 +2087,9 @@ def computeAndSetClosestDistance(self):
def pick(self, pickFrustum):
'''
Find closest point in scene rendered through 'pickFrustum'.
Returns a quartuple:
Returns a quintuple:
selectedPoint, selectedPrimPath, selectedInstancerPath,
selectedInstanceIndex
selectedInstanceIndex, selectedInstancerContext
'''
renderer = self._getRenderer()
if not self._dataModel.stage or not renderer:
Expand Down Expand Up @@ -2179,14 +2172,15 @@ def pickObject(self, x, y, button, modifiers):
(inImageBounds, pickFrustum) = self.computePickFrustum(x,y)

if inImageBounds:
selectedPoint, selectedPrimPath, selectedInstancerPath, \
selectedInstanceIndex = self.pick(pickFrustum)
selectedPoint, selectedPrimPath, \
selectedInstanceIndex, selectedTLPath, selectedTLIndex = \
self.pick(pickFrustum)
else:
# If we're picking outside the image viewport (maybe because
# camera guides are on), treat that as a de-select.
selectedPoint, selectedPrimPath, selectedInstancerPath, \
selectedInstanceIndex = \
None, Sdf.Path.emptyPath, None, None
selectedPoint, selectedPrimPath, \
selectedInstanceIndex, selectedTLPath, selectedTLIndex = \
None, Sdf.Path.emptyPath, -1, Sdf.Path.emptyPath, -1

# Correct for high DPI displays
coord = self._scaleMouseCoords( \
Expand All @@ -2196,12 +2190,12 @@ def pickObject(self, x, y, button, modifiers):

if button:
self.signalPrimSelected.emit(
selectedPrimPath, selectedInstanceIndex, selectedPoint,
button, modifiers)
selectedPrimPath, selectedInstanceIndex, selectedTLPath,
selectedTLIndex, selectedPoint, button, modifiers)
else:
self.signalPrimRollover.emit(
selectedPrimPath, selectedInstanceIndex, selectedPoint,
modifiers)
selectedPrimPath, selectedInstanceIndex, selectedTLPath,
selectedTLIndex, selectedPoint, modifiers)
except Tf.ErrorException as e:
# If we encounter an error, we want to continue running. Just log
# the error and continue.
Expand Down

0 comments on commit e0c4dc3

Please sign in to comment.