diff --git a/direct/src/leveleditor/ActionMgr.py b/direct/src/leveleditor/ActionMgr.py index d1723588a0..a890b94965 100755 --- a/direct/src/leveleditor/ActionMgr.py +++ b/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 = [] diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py index 6150efcff5..359598ea57 100755 --- a/direct/src/leveleditor/LevelEditorBase.py +++ b/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): diff --git a/direct/src/leveleditor/ObjectHandler.py b/direct/src/leveleditor/ObjectHandler.py index 967cae37da..1bd6bd76f2 100755 --- a/direct/src/leveleditor/ObjectHandler.py +++ b/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() diff --git a/direct/src/leveleditor/ObjectMgr.py b/direct/src/leveleditor/ObjectMgr.py index 90d98ce3e7..08919438b8 100755 --- a/direct/src/leveleditor/ObjectMgr.py +++ b/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) diff --git a/direct/src/leveleditor/ObjectPaletteUI.py b/direct/src/leveleditor/ObjectPaletteUI.py index a7ea06f6d2..c61f715c9f 100755 --- a/direct/src/leveleditor/ObjectPaletteUI.py +++ b/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()