Browse Source

Added undo for selection and object transform

Gyedo Jeon 16 years ago
parent
commit
fd7bf04577

+ 135 - 24
direct/src/leveleditor/ActionMgr.py

@@ -1,24 +1,6 @@
+from pandac.PandaModules import *
 import ObjectGlobals as OG
 
-class ActionBase(Functor):
-    """ Base class for user actions """
-
-    def __init__(self, function, *args, **kargs):
-        Functor.__init__(self, function, *args, **kargs)
-        self.result = None
-
-    def _do__call__(self, *args, **kargs):
-        self.saveStatus()
-        self.result = Functor._do__call__(self, *args, **kargs)
-        return self.result
-
-    def saveStatus(self):
-        # save object status for undo here
-        pass
-        
-    def undo(self):
-        print "undo method is not defined for this action"
-
 class ActionMgr:
     def __init__(self):
         self.undoList = []
@@ -35,6 +17,7 @@ class ActionMgr:
 
     def push(self, action):
         self.undoList.append(action)
+        print 'current undoList', self.undoList
 
     def undo(self):
         if len(self.undoList) < 1:
@@ -43,6 +26,7 @@ class ActionMgr:
             action = self.undoList.pop()
             self.redoList.append(action)
             action.undo()
+            print 'current redoList', self.redoList
 
     def redo(self):
         if len(self.redoList) < 1:
@@ -50,7 +34,31 @@ class ActionMgr:
         else:
             action = self.redoList.pop()
             self.undoList.append(action)
-            action()
+            action.redo()
+            print 'current undoList', self.undoList
+
+class ActionBase(Functor):
+    """ Base class for user actions """
+
+    def __init__(self, function, *args, **kargs):
+        Functor.__init__(self, function, *args, **kargs)
+        self.result = None
+
+    def _do__call__(self, *args, **kargs):
+        self.saveStatus()
+        self.result = Functor._do__call__(self, *args, **kargs)
+        return self.result
+
+    def redo(self):
+        self.result = self._do__call__()
+        return self.result
+
+    def saveStatus(self):
+        # save object status for undo here
+        pass
+        
+    def undo(self):
+        print "undo method is not defined for this action"
 
 class ActionAddNewObj(ActionBase):
     """ Action class for adding new object """
@@ -59,11 +67,23 @@ class ActionAddNewObj(ActionBase):
         self.editor = editor
         function = self.editor.objectMgr.addNewObject
         ActionBase.__init__(self, function, *args, **kargs)
+        self.uid = None
 
+    def redo(self):
+        if self.uid is None:
+            print "Can't redo this add"        
+        else:
+            self.result = self._do__call__(uid=self.uid)
+            return self.result
+            
     def undo(self):
         if self.result is None:
-            print "Can't undo this"
+            print "Can't undo this add"
         else:
+            print "Undo: addNewObject"
+            obj = self.editor.objectMgr.findObjectByNodePath(self.result)
+            self.uid = obj[OG.OBJ_UID]
+            self.editor.ui.sceneGraphUI.delete(self.uid)
             base.direct.deselect(self.result)
             base.direct.removeNodePath(self.result)
             self.result = None
@@ -78,6 +98,7 @@ class ActionDeleteObj(ActionBase):
         self.selectedUIDs = []
         self.hierarchy = {}
         self.objInfos = {}
+        self.objTransforms = {}
 
     def saveStatus(self):
         selectedNPs = base.direct.selected.getSelectedAsList()
@@ -89,6 +110,7 @@ class ActionDeleteObj(ActionBase):
                     self.selectedUIDs.append(uid)
                 objNP = obj[OG.OBJ_NP]
                 self.objInfos[uid] = obj
+                self.objTransforms[uid] = objNP.getMat()
                 parentNP = objNP.getParent()
                 if parentNP == render:
                     self.hierarchy[uid] = None
@@ -107,10 +129,12 @@ class ActionDeleteObj(ActionBase):
     def undo(self):
         if len(self.hierarchy.keys()) == 0 or\
            len(self.objInfos.keys()) == 0:
-            print "Can't undo this"
+            print "Can't undo this deletion"
         else:
+            print "Undo: deleteObject"
             def restoreObject(uid, parentNP):
                 obj = self.objInfos[uid]
+                objNP = obj[OG.OBJ_NP]
                 objDef = obj[OG.OBJ_DEF]
                 objModel = obj[OG.OBJ_MODEL]
                 objProp = obj[OG.OBJ_PROP]
@@ -121,6 +145,7 @@ class ActionDeleteObj(ActionBase):
                                                    parentNP)
                 self.editor.objectMgr.updateObjectColor(objRGBA[0], objRGBA[1], objRGBA[2], objRGBA[3], uid)
                 self.editor.objectMgr.updateObjectProperties(uid, objProp)
+                objNP.setMat(self.objTransforms[uid])
 
             while (len(self.hierarchy.keys()) > 0):
                 for uid in self.hierarchy.keys():
@@ -135,12 +160,98 @@ class ActionDeleteObj(ActionBase):
                             restoreObject(uid, parentNP)
                             del self.hierarchy[uid]
 
-            base.direct.deselectAll()
+            base.direct.deselectAllCB()
             for uid in self.selectedUIDs:
                 obj = self.editor.objectMgr.findObjectById(uid)
                 if obj:
-                    base.direct.select(obj[OG.OBJ_NP], fMultiSelect=1)
+                    self.editor.select(obj[OG.OBJ_NP], fMultiSelect=1, fUndo=0)
 
             self.selecteUIDs = []
             self.hierarchy = {}
             self.objInfos = {}            
+
+class ActionSelectObj(ActionBase):
+    """ Action class for adding new object """
+    
+    def __init__(self, editor, *args, **kargs):
+        self.editor = editor
+        function = base.direct.selectCB
+        ActionBase.__init__(self, function, *args, **kargs)
+        self.selectedUIDs = []
+
+    def saveStatus(self):
+        selectedNPs = base.direct.selected.getSelectedAsList()
+        for np in selectedNPs:
+            obj = self.editor.objectMgr.findObjectByNodePath(np)
+            if obj:
+                uid = obj[OG.OBJ_UID]
+                self.selectedUIDs.append(uid)
+
+    def undo(self):
+        print "Undo : selectObject"
+        base.direct.deselectAllCB()
+        for uid in self.selectedUIDs:
+            obj = self.editor.objectMgr.findObjectById(uid)
+            if obj:
+                self.editor.select(obj[OG.OBJ_NP], fMultiSelect=1, fUndo=0)
+        self.selectedUIDs = []
+
+class ActionTransformObj(ActionBase):
+    """ Action class for object transformation """
+
+    def __init__(self, editor, *args, **kargs):
+        self.editor = editor
+        function = self.editor.objectMgr.setObjectTransform
+        ActionBase.__init__(self, function, *args, **kargs)
+        self.uid = args[0]
+        #self.xformMat = Mat4(args[1])
+        self.origMat = None
+
+    def saveStatus(self):
+        obj = self.editor.objectMgr.findObjectById(self.uid)
+        if obj:
+            self.origMat = Mat4(obj[OG.OBJ_NP].getMat())
+
+    def redo(self):
+        if self.uid is None:
+            print "Can't redo this add"        
+        else:
+            self.result = self._do__call__()#uid=self.uid, xformMat=self.xformMat)
+            return self.result
+
+    def undo(self):
+        if self.origMat is None:
+            print "Can't undo this transform"
+        else:
+            print "Undo: transformObject"
+            obj = self.editor.objectMgr.findObjectById(self.uid)
+            if obj:
+                obj[OG.OBJ_NP].setMat(self.origMat)
+            del self.origMat
+            self.origMat = None
+
+class ActionDeselectAll(ActionBase):
+    """ Action class for adding new object """
+    
+    def __init__(self, editor, *args, **kargs):
+        self.editor = editor
+        function = base.direct.deselectAllCB
+        ActionBase.__init__(self, function, *args, **kargs)
+        self.selectedUIDs = []
+
+    def saveStatus(self):
+        selectedNPs = base.direct.selected.getSelectedAsList()
+        for np in selectedNPs:
+            obj = self.editor.objectMgr.findObjectByNodePath(np)
+            if obj:
+                uid = obj[OG.OBJ_UID]
+                self.selectedUIDs.append(uid)
+
+    def undo(self):
+        print "Undo : deselectAll"
+        base.direct.deselectAllCB()
+        for uid in self.selectedUIDs:
+            obj = self.editor.objectMgr.findObjectById(uid)
+            if obj:
+                self.editor.select(obj[OG.OBJ_NP], fMultiSelect=1, fUndo=0)
+        self.selectedUIDs = []

+ 29 - 3
direct/src/leveleditor/LevelEditorBase.py

@@ -34,7 +34,7 @@ class LevelEditorBase(DirectObject):
         
     def initialize(self):
         """ You should call this in your __init__ method of inherited LevelEditor class """
-        fTk = base.config.GetBool('want-tk', 0)
+        fTk = 0
         fWx = 0
         base.startDirect(fWantTk = fTk, fWantWx = fWx)
         base.closeWindow(base.win)
@@ -97,8 +97,10 @@ class LevelEditorBase(DirectObject):
             widget.setBin('gui-popup', 0)
             widget.setDepthTest(0)
 
-        # [gjeon] to handle delete here first
+        # [gjeon] to intercept messages here
         base.direct.ignore('DIRECT-delete')
+        base.direct.ignore('DIRECT-select')
+        base.direct.ignore('DIRECT-preDeselectAll')
         
         # [gjeon] do not use the old way of finding current DR
         base.direct.drList.tryToGetCurrentDr = False
@@ -108,10 +110,13 @@ class LevelEditorBase(DirectObject):
 
         self.actionEvents.extend([
             # Node path events
+            ('DIRECT-select', self.select),
             ('DIRECT-delete', self.handleDelete),
+            ('DIRECT-preDeselectAll', self.deselectAll),
+            ('DIRECT_deselectAll', self.deselectAllCB),
             ('preRemoveNodePath', self.removeNodePathHook),
+            ('DIRECT_deselectedNodePath', self.deselectAllCB),
             ('DIRECT_selectedNodePath_fMulti_fTag_fLEPane', self.selectedNodePathHook),
-            ('DIRECT_deselectedNodePath', self.deselectAll),
             ('DIRECT_deselectAll', self.deselectAll),
             ('LE-Undo', self.actionMgr.undo),
             ('LE-Redo', self.actionMgr.redo),
@@ -165,6 +170,20 @@ class LevelEditorBase(DirectObject):
 ##             coa = Vec3(row[0], row[1], row[2])
 ##             base.direct.cameraControl.updateCoa(coa)
 
+    def select(self, nodePath, fMultiSelect=0, fSelectTag=1, fResetAncestry=1, fLEPane=0, fUndo=1):
+        if fUndo:
+            # Select tagged object if present
+            if fSelectTag:
+                for tag in base.direct.selected.tagList:
+                    if nodePath.hasNetTag(tag):
+                        nodePath = nodePath.findNetTag(tag)
+                        break
+            action = ActionSelectObj(self, nodePath, fMultiSelect)
+            self.actionMgr.push(action)
+            action()
+        else:
+            base.direct.selectCB(nodePath, fMultiSelect, fSelectTag, fResetAncestry, fLEPane, fUndo)
+
     def selectedNodePathHook(self, nodePath, fMultiSelect = 0, fSelectTag = 1, fLEPane = 0):
         # handle unpickable nodepath
         if nodePath.getName() in base.direct.iRay.unpickable:
@@ -180,6 +199,13 @@ class LevelEditorBase(DirectObject):
         self.objectMgr.selectObject(nodePath, fLEPane)
 
     def deselectAll(self, np=None):
+        if len(base.direct.selected.getSelectedAsList()) ==0:
+            return
+        action = ActionDeselectAll(self)
+        self.actionMgr.push(action)
+        action()
+
+    def deselectAllCB(self, dnp=None):
         self.objectMgr.deselectAll()
 
     def reset(self):

+ 1 - 1
direct/src/leveleditor/ObjectHandler.py

@@ -46,7 +46,7 @@ class ObjectHandler:
     def updateSmiley(self, val, obj):
         objNP = obj[OG.OBJ_NP]
         if base.direct:
-            base.direct.deselectAll()
+            base.direct.deselectAllCB()
         for child in objNP.findAllMatches("+GeomNode"):
             child.removeNode()
 

+ 24 - 14
direct/src/leveleditor/ObjectMgr.py

@@ -7,7 +7,7 @@ import os, time, wx
 from direct.task import Task
 from direct.actor.Actor import Actor
 from pandac.PandaModules import *
-
+from ActionMgr import *
 import ObjectGlobals as OG
 
 class ObjectMgr:
@@ -26,7 +26,7 @@ class ObjectMgr:
         self.currNodePath = None   
 
     def reset(self):
-        self.deselectAll()
+        self.deselectAllCB()
 
         for id in self.objects.keys():
             self.objects[id][OG.OBJ_NP].removeNode()
@@ -130,8 +130,6 @@ class ObjectMgr:
 
             if uid is None:
                 uid = self.genUniqueId()
-            else:
-                fSelectObject = False
 
             # populate obj data using default values
             properties = {}
@@ -144,7 +142,7 @@ class ObjectMgr:
 
             if self.editor:
                 if fSelectObject:
-                    base.direct.select(newobj)
+                    self.editor.select(newobj, fUndo=0)
                 self.editor.ui.sceneGraphUI.add(newobj)
 
         return newobj
@@ -249,7 +247,7 @@ class ObjectMgr:
         if self.currNodePath is None:
             return
 
-        np = self.currNodePath
+        np = hidden.attachNewNode('temp')
         np.setX(float(self.editor.ui.objectPropertyUI.propX.getValue()))
         np.setY(float(self.editor.ui.objectPropertyUI.propY.getValue()))
         np.setZ(float(self.editor.ui.objectPropertyUI.propZ.getValue()))
@@ -282,6 +280,18 @@ class ObjectMgr:
         np.setSx(float(self.editor.ui.objectPropertyUI.propSX.getValue()))
         np.setSy(float(self.editor.ui.objectPropertyUI.propSY.getValue()))
         np.setSz(float(self.editor.ui.objectPropertyUI.propSZ.getValue()))        
+
+        obj = self.findObjectByNodePath(self.currNodePath)
+        action = ActionTransformObj(self.editor, obj[OG.OBJ_UID], Mat4(np.getMat()))
+        self.editor.actionMgr.push(action)
+        np.remove()
+        action()
+
+    def setObjectTransform(self, uid, xformMat):
+        obj = self.findObjectById(uid)
+        if obj:
+            print obj[OG.OBJ_NP], xformMat
+            obj[OG.OBJ_NP].setMat(xformMat)
         
     def updateObjectColor(self, r, g, b, a, np=None):
         if np is None:
@@ -300,7 +310,7 @@ class ObjectMgr:
     def updateObjectModel(self, model, obj, fSelectObject=True):
         """ replace object's model """
         if obj[OG.OBJ_MODEL] != model:
-            base.direct.deselectAll()
+            base.direct.deselectAllCB()
 
             objNP = obj[OG.OBJ_NP]
             objRGBA = obj[OG.OBJ_RGBA]
@@ -333,12 +343,12 @@ class ObjectMgr:
             self.npIndex[NodePath(newobj)] = obj[OG.OBJ_UID]
 
             if fSelectObject:
-                base.direct.select(newobj)        
+                base.direct.select(newobj, fUndo=0)        
 
     def updateObjectAnim(self, anim, obj, fSelectObject=True):
         """ replace object's anim """
         if obj[OG.OBJ_ANIM] != anim:
-            base.direct.deselectAll()
+            base.direct.deselectAllCB()
             objNP = obj[OG.OBJ_NP]
 
             # load new anim
@@ -347,7 +357,7 @@ class ObjectMgr:
             objNP.loop(animName)
             obj[OG.OBJ_ANIM] = anim
             if fSelectObject:
-                base.direct.select(objNP)
+                base.direct.select(objNP, fUndo=0)
 
     def updateObjectModelFromUI(self, event, obj):
         """ replace object's model with one selected from UI """
@@ -463,7 +473,7 @@ class ObjectMgr:
         func(**kwargs)
 
         if self.editor and fSelectObject:
-            base.direct.select(obj[OG.OBJ_NP])
+            base.direct.select(obj[OG.OBJ_NP], fUndo=0)
 
     def updateObjectProperties(self, nodePath, propValues):
         """
@@ -510,7 +520,7 @@ class ObjectMgr:
                     else:
                         animStr = "None"
 
-                    self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s)"%(uid, objDef.name, uid, modelStr, parentStr, animStr))
+                    self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s, fSelectObject=False)"%(uid, objDef.name, uid, modelStr, parentStr, animStr))
                     self.saveData.append("if objects['%s']:"%uid)
                     self.saveData.append("    objects['%s'].setPos(%s)"%(uid, np.getPos()))
                     self.saveData.append("    objects['%s'].setHpr(%s)"%(uid, np.getHpr()))
@@ -575,7 +585,7 @@ class ObjectMgr:
                 self.duplicateChild(nodePath, newObjNP)
                 duplicatedNPs.append(newObjNP)
 
-        base.direct.deselectAll()
+        base.direct.deselectAllCB()
         print duplicatedNPs
         for newNodePath in duplicatedNPs:
-            base.direct.select(newNodePath, fMultiSelect = 1)
+            base.direct.select(newNodePath, fMultiSelect = 1, fUndo=0)

+ 1 - 1
direct/src/leveleditor/ObjectPaletteUI.py

@@ -72,7 +72,7 @@ class ObjectPaletteUI(wx.Panel):
 
     def onSelected(self, event):
         pass
-        
+
     def onBeginDrag(self, event):
         item = event.GetItem()