mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-18 12:43:44 -04:00
Added undo for selection and object transform
This commit is contained in:
parent
c3b70b120d
commit
fd7bf04577
@ -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 = []
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
@ -72,7 +72,7 @@ class ObjectPaletteUI(wx.Panel):
|
||||
|
||||
def onSelected(self, event):
|
||||
pass
|
||||
|
||||
|
||||
def onBeginDrag(self, event):
|
||||
item = event.GetItem()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user