Search - User list
Full Version: Undo for viewport navigation ?
Root » SI Users » Undo for viewport navigation ?
julca
Hello,

Is there a way to undo only current viewport navigation like we do with “Alt+Z” in Softimage ?

Thanks
anon_user_89151269
Exactly this behavior no, but there's something one can do to emulate that:
navigate your viewport through a locked camera (3rd lock icon on the tool bar on the right side of the viewport).
Of course it's rather limited for an “in the now” usage so to speak, since the undo will fill with other actions as you work and you won't be able to undo your camera changes.
File a RFE for “separate camera undo/redo stack” if you miss this feature so much.
julca
I see, then when I do Ctrl+Z it will undo current camera position ?
Ok, as you say that's not the same because here we also undo action but thank you for the tip !
julca
Yes, I'll file a RFE for this.
Thank you.
Grendizer
Very nice, I was about to start a thread about that. Would be cool if it's implemented in v17
Rebus B
Grendizer
…Would be cool if it's implemented in v17

Was it?
Just rotated view with the pivot in the wrong place and I want to go back… let me go back… can't I go back?

Alternately, is there a “pivot view on center” or “pivot on selected” versus “pivot at camera point” button?
Rebus B
Found “g” or space+“g” to pivot around selected, and can change pivot behavior under Camera Tool popup menu to “Keep Pivot on Tumble/Rotate”
Grendizer
Good! Can someone with V17 tell us if undo camera is here?
julca
Grendizer
Good! Can someone with V17 tell us if undo camera is here?
No, there is not still present in H17.
cval
We logged an RFE for this feature a few years ago. It's ID is #73866 if you want to reference it in your RFEs
julca
cval
We logged an RFE for this feature a few years ago. It's ID is #73866 if you want to reference it in your RFEs
Updated my exact same request (#63120) with the reference to your RFE number !
OneBigTree
Can we upvote RFEs?
julca
OneBigTree
Can we upvote RFEs?
As I know we can't upvote RFE, you may send a new one with same request.
Grendizer
How about v17.5 ?
cval
I don't see it in v17.5
anon_user_89151269
As I said in another thread, it's best to make a habit of keying your camera. It's a solution for both the present and it would still be a good idea to do it even if you were to have this feature implemented, if the goal is to make sure you don't lose your camera transformation. And make sure “auto-commit changes” is off, or you'll update the keys every time you modify the cam view.
motionpunk
I so miss that coming from c4d 3 years later digging up this post again
animatrix_
I implemented this recently. You can give it a try. It supports multiple viewports. It has to be installed as a package or can be run without a package by installing directly into Houdini user directory. There seems to be some focus bug about scene viewer only hotkeys so I had to use a global hotkey for these. I will see if it's a bug in Houdini.



pythonrc.py:

import hou
from hdefereval import executeDeferred
from PySide2 import QtCore, QtWidgets, QtGui
import shiboken2



debug = False



def areIdenticalCameras(cam1, cam2):
    if cam1.isPerspective() and cam2.isOrthographic():
        return False
    if cam2.isPerspective() and cam1.isOrthographic():
        return False
    if cam1.aperture() != cam2.aperture():
        return False
    if cam1.focalLength() != cam2.focalLength():
        return False
    if cam1.aspectRatio() != cam2.aspectRatio():
        return False
    if cam1.orthoWidth() != cam2.orthoWidth():
        return False
    if cam1.rotation() != cam2.rotation():
        return False
    if cam1.translation() != cam2.translation():
        return False
    if cam1.windowOffset() != cam2.windowOffset():
        return False
    if cam1.windowSize() != cam2.windowSize():
        return False
    return True
        


class SceneViewerEventFilter(QtCore.QObject):

    SceneViewers = []
    Refs = []
    
    def __init__(self, sceneViewer, sceneViewerQt):
        QtCore.QObject.__init__(self)

        SceneViewerEventFilter.SceneViewers.append(sceneViewer)
        SceneViewerEventFilter.Refs.append(self)

        self.sceneViewer = sceneViewer;
        self.sceneViewerQt = sceneViewerQt;
        viewport = self.sceneViewer.curViewport()
        cam = viewport.defaultCamera().stash()

        self.views = [cam]
        self.viewtypes = [viewport.type()]
        self.viewindex = -1



    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.MouseButtonRelease:
            pane = hou.ui.paneTabUnderCursor()
            if pane and pane.type() == hou.paneTabType.SceneViewer:
                if "view" in pane.currentState():
                    executeDeferred(self.viewChangedEvent)

        elif event.type() == QtCore.QEvent.MouseMove:
            if len(self.views) == 0:
                viewport = self.sceneViewer.curViewport()
                cam = viewport.defaultCamera().stash()
                self.views.append(cam)
                self.viewtypes.append(viewport.type())
                if debug:
                    print ("mouse move")
        """
        elif event.type() == QtCore.QEvent.KeyPress:
            if event.modifiers() & QtCore.Qt.ShiftModifier:
                if event.key() == QtCore.Qt.Key_Z:
                    self.undoView()
                elif event.key() == QtCore.Qt.Key_A:
                    self.redoView()
        """
        return False


    
    def viewChangedEvent(self, force=False):
        viewport = self.sceneViewer.curViewport()
        cam = viewport.defaultCamera().stash()
        if force or len(self.views) == 0 or not areIdenticalCameras(cam, self.views[-1]):
            if self.viewindex != -1:
                self.views = self.views[: self.viewindex + 1]
                self.viewtypes = self.viewtypes[: self.viewindex + 1]
                self.viewindex = -1

            self.views.append(cam)
            self.viewtypes.append(viewport.type())
            if debug:
                print ("VIEW CHANGED: ", len(self.views), viewport.type())
                print ("------------------------------------")



    def undoView(self):
        if self.viewindex == -1:
            self.viewindex = len(self.views) - 1

        if self.viewindex > 0:
            self.viewindex -= 1
            if debug:
                print ("undo", self.viewindex)

            viewport = self.sceneViewer.curViewport()
            viewport.changeType(self.viewtypes[self.viewindex])
            try:
                viewport.setDefaultCamera(self.views[self.viewindex])
            except:
                pass



    def redoView(self):
        if self.viewindex == -1:
            self.viewindex = len(self.views) - 1

        if self.viewindex < len(self.viewtypes) - 1:
            self.viewindex += 1
            if debug:
                print ("redo", self.viewindex)

            viewport = self.sceneViewer.curViewport()
            viewport.changeType(self.viewtypes[self.viewindex])
            try:
                viewport.setDefaultCamera(self.views[self.viewindex])
            except:
                pass



    @staticmethod
    def reset():
        for ref in SceneViewerEventFilter.Refs:
            ref.views = []
            ref.viewtypes = []
            ref.viewindex = -1


    
# Install event filter if it's not installed yet for each viewport
def installEventFilterForCurrentViewport():
    pane = hou.ui.paneTabUnderCursor()
    if pane and pane.type() == hou.paneTabType.SceneViewer:
        if pane not in SceneViewerEventFilter.SceneViewers:

            if not hasattr(hou.session, "ViewerEventFilters"):
                hou.session.ViewerEventFilters = []
            
            app = QtWidgets.QApplication.instance()
            sceneViewerQt = app.widgetAt(QtGui.QCursor.pos())
            if len(sceneViewerQt.children()) == 0:
                viewerEF = SceneViewerEventFilter(pane, sceneViewerQt)
                hou.session.ViewerEventFilters.append(viewerEF)

                sceneViewerQt.installEventFilter(viewerEF)
                if debug:
                    print ("INSTALL EVENT FILTER: ", pane.name())



def viewportViewTypeChangedCallback():
    if not hasattr(hou.session, "AllSceneViewers"):
        hou.session.AllSceneViewers = []
        hou.session.AllSceneViewerTypes = []

    desktop = hou.ui.curDesktop()
    panetabs = desktop.paneTabs()
    for pane in panetabs:
            if pane.type() == hou.paneTabType.SceneViewer:
                if pane not in hou.session.AllSceneViewers:
                    hou.session.AllSceneViewers.append(pane)
                    hou.session.AllSceneViewerTypes.append(pane.curViewport().type())
                    if debug:
                        print ("SCENE VIEWER ADDED")
                else:
                    index = hou.session.AllSceneViewers.index(pane)
                    viewtype = pane.curViewport().type()
                    if viewtype != hou.session.AllSceneViewerTypes[index]:
                        svindex = SceneViewerEventFilter.SceneViewers.index(pane)
                        sv = SceneViewerEventFilter.Refs[index]
                        if sv.viewindex == -1:
                            if debug:
                                print ("VIEW TYPE CHANGED ", hou.session.AllSceneViewerTypes[index], viewtype)
                            hou.session.AllSceneViewerTypes[index] = viewtype
                            sv.viewChangedEvent(force=True)




hou.ui.addEventLoopCallback(installEventFilterForCurrentViewport)
hou.ui.addEventLoopCallback(viewportViewTypeChangedCallback)

hou.session.SceneViewerEventFilter = SceneViewerEventFilter


456.py:

hou.session.AllSceneViewers = []
hou.session.AllSceneViewerTypes = []
hou.session.SceneViewerEventFilter.reset()

Special thanks to Alexey of DM and kosukek of SESI
Grendizer
Wow thanks!
I'd like to install this.
In my "houdini19.0" prefs folder, I added a folder called "packages".
Then if I understand correctly I must put your code into a text file and rename it something as "camundo.json"?
Then I must edit 456.py as you shown.
Then if I want to test it, what's the keyboard shortcut? In Maya it's ALT+Z.
Thanks
animatrix_
Hi,

I made a separate zip, it's easier than copy pasting everything here. I am using them as part of my full customization so I separated these for there manually, it should work.

As for hotkeys, normally they were within the event filter code but there seems to be a focus problem in the latest Houdini I tried. I have to investigate this with SESI because it happens with scene viewer restricted hotkeys too.

So for now you have to hold Ctrl+Shift+Alt and click each shelf button to assign global hotkeys for undo and redo until that bug is fixed.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB