Undo for viewport navigation ?

   18907   25   5
User Avatar
Member
219 posts
Joined: Oct. 2015
Online
Hello,

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

Thanks
User Avatar
Member
1755 posts
Joined: March 2014
Offline
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.
Edited by anon_user_89151269 - June 27, 2018 06:14:47
User Avatar
Member
219 posts
Joined: Oct. 2015
Online
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 !
User Avatar
Member
219 posts
Joined: Oct. 2015
Online
Yes, I'll file a RFE for this.
Thank you.
User Avatar
Member
375 posts
Joined: May 2014
Offline
Very nice, I was about to start a thread about that. Would be cool if it's implemented in v17
Houdini gamboler
User Avatar
Member
80 posts
Joined: June 2013
Offline
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?
User Avatar
Member
80 posts
Joined: June 2013
Offline
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”
User Avatar
Member
375 posts
Joined: May 2014
Offline
Good! Can someone with V17 tell us if undo camera is here?
Edited by Grendizer - Nov. 28, 2018 02:19:42
Houdini gamboler
User Avatar
Member
219 posts
Joined: Oct. 2015
Online
Grendizer
Good! Can someone with V17 tell us if undo camera is here?
No, there is not still present in H17.
User Avatar
Member
161 posts
Joined: May 2015
Offline
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
User Avatar
Member
219 posts
Joined: Oct. 2015
Online
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 !
User Avatar
Member
382 posts
Joined: Nov. 2010
Offline
Can we upvote RFEs?
User Avatar
Member
219 posts
Joined: Oct. 2015
Online
OneBigTree
Can we upvote RFEs?
As I know we can't upvote RFE, you may send a new one with same request.
User Avatar
Member
375 posts
Joined: May 2014
Offline
How about v17.5 ?
Houdini gamboler
User Avatar
Member
161 posts
Joined: May 2015
Offline
I don't see it in v17.5
User Avatar
Member
1755 posts
Joined: March 2014
Offline
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.
Edited by anon_user_89151269 - April 11, 2019 12:58:34
User Avatar
Member
14 posts
Joined: Dec. 2016
Offline
I so miss that coming from c4d 3 years later digging up this post again
www.motion-punk.com
User Avatar
Member
4702 posts
Joined: Feb. 2012
Offline
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
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
User Avatar
Member
375 posts
Joined: May 2014
Offline
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
Houdini gamboler
User Avatar
Member
4702 posts
Joined: Feb. 2012
Offline
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.

Attachments:
viewhistory.zip (3.7 KB)

Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
  • Quick Links