diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py index 0841a4521f..b3b864e5dc 100755 --- a/direct/src/leveleditor/LevelEditorBase.py +++ b/direct/src/leveleditor/LevelEditorBase.py @@ -19,6 +19,7 @@ class LevelEditorBase(DirectObject): """ Base Class for Panda3D LevelEditor """ def __init__(self): #loadPrcFileData('startup', 'window-type none') + self.currentFile = None self.actionEvents = [] self.objectMgr = ObjectMgr(self) self.fileMgr = FileMgr(self) @@ -130,9 +131,14 @@ class LevelEditorBase(DirectObject): base.direct.deselectAll() self.objectMgr.reset() - def save(self, fileName): + def save(self): + if self.currentFile: + self.fileMgr.saveToFile(self.currentFile) + + def saveAs(self, fileName): self.fileMgr.saveToFile(fileName) def load(self, fileName): self.reset() self.fileMgr.loadFromFile(fileName) + self.currentFile = fileName diff --git a/direct/src/leveleditor/LevelEditorUI.py b/direct/src/leveleditor/LevelEditorUI.py index 2083d3551b..4076bae435 100755 --- a/direct/src/leveleditor/LevelEditorUI.py +++ b/direct/src/leveleditor/LevelEditorUI.py @@ -48,6 +48,18 @@ class LevelEditorUI(WxAppShell): menuItem = self.menuFile.Insert(2, -1 , "&Save") self.Bind(wx.EVT_MENU, self.onSave, menuItem) + + menuItem = self.menuFile.Insert(3, -1 , "Save &As") + self.Bind(wx.EVT_MENU, self.onSaveAs, menuItem) + + self.menuEdit = wx.Menu() + self.menuBar.Insert(1, self.menuEdit, "&Edit") + + menuItem = self.menuEdit.Append(-1, "&Duplicate") + self.Bind(wx.EVT_MENU, self.onDuplicate, menuItem) + + self.gridSnapMenuItem = self.menuEdit.Append(-1, "&Grid Snap", kind = wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.toggleGridSnap, self.gridSnapMenuItem) def createInterface(self): self.createMenu() @@ -132,7 +144,22 @@ class LevelEditorUI(WxAppShell): dialog.Destroy() def onSave(self, evt): + if self.editor.currentFile is None: + self.onSaveAs(evt) + else: + self.editor.save() + + def onSaveAs(self, evt): dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.SAVE) if dialog.ShowModal() == wx.ID_OK: - self.editor.save(dialog.GetPath()) + self.editor.saveAs(dialog.GetPath()) dialog.Destroy() + + def onDuplicate(self, evt): + self.editor.objectMgr.duplicateSelected() + + def toggleGridSnap(self, evt): + if self.gridSnapMenuItem.IsChecked(): + base.direct.manipulationControl.fGridSnap = 1 + else: + base.direct.manipulationControl.fGridSnap = 0 diff --git a/direct/src/leveleditor/ObjectHandler.py b/direct/src/leveleditor/ObjectHandler.py index 0f68866086..b9b7f0be48 100755 --- a/direct/src/leveleditor/ObjectHandler.py +++ b/direct/src/leveleditor/ObjectHandler.py @@ -56,8 +56,6 @@ class ObjectHandler: b.reparentTo(objNP) a.removeNode() - base.direct.select(objNP) - def createPanda(self): pandaActor = PandaActor() return pandaActor diff --git a/direct/src/leveleditor/ObjectMgr.py b/direct/src/leveleditor/ObjectMgr.py index 57daa1fa8d..6a19ac971e 100755 --- a/direct/src/leveleditor/ObjectMgr.py +++ b/direct/src/leveleditor/ObjectMgr.py @@ -57,7 +57,7 @@ class ObjectMgr: self.lastUidMod = 0 return newUid - def addNewObject(self, typeName, uid = None, model = None, parent=None): + def addNewObject(self, typeName, uid = None, model = None, parent=None, fSelectObject=True): """ function to add new obj to the scene """ if parent is None: parent = render @@ -93,9 +93,8 @@ class ObjectMgr: if uid is None: uid = self.genUniqueId() - isLoadingArea = False else: - isLoadingArea = True + fSelectObject = False # populate obj data using default values properties = {} @@ -106,7 +105,7 @@ class ObjectMgr: self.objects[uid] = [uid, newobj, objDef, model, properties] self.npIndex[NodePath(newobj)] = uid - if not isLoadingArea: + if fSelectObject: base.direct.select(newobj) return newobj @@ -235,12 +234,8 @@ class ObjectMgr: np.setSy(float(self.editor.ui.objectPropertyUI.propSY.getValue())) np.setSz(float(self.editor.ui.objectPropertyUI.propSZ.getValue())) - def updateObjectModel(self, event, obj): + def updateObjectModel(self, model, obj, fSelectObject=True): """ replace object's model """ - model = event.GetString() - if model is None: - return - if obj[OG.OBJ_MODEL] != model: base.direct.deselectAll() @@ -269,8 +264,16 @@ class ObjectMgr: obj[OG.OBJ_NP] = newobj obj[OG.OBJ_MODEL] = model self.npIndex[NodePath(newobj)] = obj[OG.OBJ_UID] - - base.direct.select(newobj) + + if fSelectObject: + base.direct.select(newobj) + + + def updateObjectModelFromUI(self, event, obj): + """ replace object's model with one selected from UI """ + model = event.GetString() + if model is not None: + self.updateObjectModel(model, obj) def updateObjectProperty(self, event, obj, propName): """ @@ -329,7 +332,7 @@ class ObjectMgr: # now update object prop value and call update function self.updateObjectPropValue(obj, propName, val) - def updateObjectPropValue(self, obj, propName, val): + def updateObjectPropValue(self, obj, propName, val, fSelectObject=True): """ Update object property value and call update function if defined. @@ -368,6 +371,9 @@ class ObjectMgr: # finally call update function func(**kwargs) + if fSelectObject: + base.direct.select(obj[OG.OBJ_NP]) + def updateObjectProperties(self, nodePath, propValues): """ When a saved level is loaded, @@ -417,5 +423,51 @@ class ObjectMgr: self.saveData = [] self.traverse(render) return self.saveData + + def duplicateObject(self, nodePath, parent=None): + obj = self.findObjectByNodePath(nodePath) + if obj is None: + return None + objDef = obj[OG.OBJ_DEF] + if parent is None: + parent = nodePath.getParent() + + newObjNP = self.addNewObject(objDef.name, parent=parent, fSelectObject = False) + + # copy transform data + newObjNP.setPos(obj[OG.OBJ_NP].getPos()) + newObjNP.setHpr(obj[OG.OBJ_NP].getHpr()) + newObjNP.setScale(obj[OG.OBJ_NP].getScale()) + + newObj = self.findObjectByNodePath(NodePath(newObjNP)) + if newObj is None: + return None + # copy model info + self.updateObjectModel(obj[OG.OBJ_MODEL], newObj, fSelectObject=False) + + # copy other properties + for key in obj[OG.OBJ_PROP]: + self.updateObjectPropValue(newObj, key, obj[OG.OBJ_PROP][key], fSelectObject=False) + + return newObjNP + + def duplicateChild(self, nodePath, parent): + children = nodePath.findAllMatches('=OBJRoot') + for childNP in children: + newChildObjNP = self.duplicateObject(childNP, parent) + if newChildObjNP is not None: + self.duplicateChild(childNP, newChildObjNP) - + def duplicateSelected(self): + selectedNPs = base.direct.selected.getSelectedAsList() + duplicatedNPs = [] + for nodePath in selectedNPs: + newObjNP = self.duplicateObject(nodePath) + if newObjNP is not None: + self.duplicateChild(nodePath, newObjNP) + duplicatedNPs.append(newObjNP) + + base.direct.deselectAll() + print duplicatedNPs + for newNodePath in duplicatedNPs: + base.direct.select(newNodePath, fMultiSelect = 1) diff --git a/direct/src/leveleditor/ObjectPropertyUI.py b/direct/src/leveleditor/ObjectPropertyUI.py index 6d25f61201..5221e8c0ba 100755 --- a/direct/src/leveleditor/ObjectPropertyUI.py +++ b/direct/src/leveleditor/ObjectPropertyUI.py @@ -173,7 +173,7 @@ class ObjectPropertyUI(ScrolledPanel): propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI, self.editor.objectMgr.onLeaveObjectPropUI, - lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModel(p0, p1)) + lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModelFromUI(p0, p1)) for key in objDef.properties.keys(): propDef = objDef.properties[key]