diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py index e5fdbb606a..75b633ee85 100755 --- a/direct/src/leveleditor/LevelEditorBase.py +++ b/direct/src/leveleditor/LevelEditorBase.py @@ -1,349 +1,359 @@ -""" -Base class for Level Editor - -You should write your own LevelEditor class inheriting this. -Refer LevelEditor.py for example. -""" - -from direct.showbase.DirectObject import * -from direct.directtools.DirectUtil import * -from direct.gui.DirectGui import * - -from FileMgr import * -from ActionMgr import * -from MayaConverter import * - -class LevelEditorBase(DirectObject): - """ Base Class for Panda3D LevelEditor """ - def __init__(self): - #loadPrcFileData('startup', 'window-type none') - self.currentFile = None - self.fNeedToSave = False - self.actionEvents = [] - #self.objectMgr = ObjectMgr(self) - self.fileMgr = FileMgr(self) - self.actionMgr = ActionMgr() - - self.NPParent = render - - # define your own config file in inherited class - self.settingsFile = None - - # you can show/hide specific properties by using propertiesMask and this mode - self.mode = BitMask32() - - def initialize(self): - """ You should call this in your __init__ method of inherited LevelEditor class """ - # specifiy what obj can be 'selected' as objects - base.direct.selected.addTag('OBJRoot') - - 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_deselectAll', self.deselectAll), - ('LE-Undo', self.actionMgr.undo), - ('LE-Redo', self.actionMgr.redo), - ('LE-Duplicate', self.objectMgr.duplicateSelected), - ('DIRECT_manipulateObjectCleanup', self.cleanUpManipulating), - ('LE-MakeLive', self.objectMgr.makeSelectedLive), - ('LE-NewScene', self.ui.onNew), - ('LE-SaveScene', self.ui.onSave), - ('LE-OpenScene', self.ui.onOpen), - ('LE-Quit', self.ui.quit), - ('DIRECT-mouse3', self.handleMouse3), - ('DIRECT-toggleWidgetVis', self.toggleWidget), - ]) - - # Add all the action events - for event in self.actionEvents: - if len(event) == 3: - self.accept(event[0], event[1], event[2]) - else: - self.accept(event[0], event[1]) - - # editor state text display such as edit mode - self.statusReadout = OnscreenText( - pos = (-1.2, 0.9), bg=Vec4(1,1,1,1), - scale = 0.05, align = TextNode.ALeft, - mayChange = 1, font = TextNode.getDefaultFont()) - self.statusReadout.setText("") - # Make sure readout is never lit or drawn in wireframe - useDirectRenderStyle(self.statusReadout) - self.statusReadout.reparentTo(hidden) - self.statusLines = [] - taskMgr.doMethodLater(5, self.updateStatusReadoutTimeouts, 'updateStatus') - - self.loadSettings() - self.reset() - - def setTitleWithFilename(self, filename=""): - title = self.ui.appname - if filename != "": - filenameshort = os.path.basename(filename) - title = title + " (%s)"%filenameshort - self.ui.SetLabel(title) - - def removeNodePathHook(self, nodePath): - if nodePath is None: - return - base.direct.deselect(nodePath) - self.objectMgr.removeObjectByNodePath(nodePath) - - if (base.direct.selected.last != None and nodePath.compareTo(base.direct.selected.last)==0): - # if base.direct.selected.last is refering to this - # removed obj, clear the reference - if (hasattr(__builtins__,'last')): - __builtins__.last = None - else: - __builtins__['last'] = None - base.direct.selected.last = None - - def toggleWidget(self): - if self.objectMgr.currNodePath: - obj = self.objectMgr.findObjectByNodePath(self.objectMgr.currNodePath) - if obj and not obj[OG.OBJ_DEF].movable: - return - base.direct.toggleWidgetVis() - - def handleMouse3(self, modifiers): - if base.direct.fAlt or modifiers == 4: - return - - self.ui.onRightDown() - - def handleDelete(self): - oldSelectedNPs = base.direct.selected.getSelectedAsList() - oldUIDs = [] - for oldNP in oldSelectedNPs: - obj = self.objectMgr.findObjectByNodePath(oldNP) - if obj: - oldUIDs.append(obj[OG.OBJ_UID]) - - action = ActionDeleteObj(self) - self.actionMgr.push(action) - action() - - for uid in oldUIDs: - self.ui.sceneGraphUI.delete(uid) - -## reply = wx.MessageBox("Do you want to delete selected?", "Delete?", -## wx.YES_NO | wx.ICON_QUESTION) -## if reply == wx.YES: -## base.direct.removeAllSelected() -## else: -## # need to reset COA -## dnp = base.direct.selected.last -## # Update camera controls coa to this point -## # Coa2Camera = Coa2Dnp * Dnp2Camera -## mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(base.direct.camera) -## row = mCoa2Camera.getRow(3) -## coa = Vec3(row[0], row[1], row[2]) -## base.direct.cameraControl.updateCoa(coa) - - def cleanUpManipulating(self, selectedNPs): - for np in selectedNPs: - obj = self.objectMgr.findObjectByNodePath(np) - if obj: - action = ActionTransformObj(self, obj[OG.OBJ_UID], Mat4(np.getMat())) - self.actionMgr.push(action) - action() - - 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: - base.direct.deselect(nodePath) - return - - if fMultiSelect == 0 and fLEPane == 0: - oldSelectedNPs = base.direct.selected.getSelectedAsList() - for oldNP in oldSelectedNPs: - obj = self.objectMgr.findObjectByNodePath(oldNP) - if obj: - self.ui.sceneGraphUI.deSelect(obj[OG.OBJ_UID]) - self.objectMgr.selectObject(nodePath, fLEPane) - self.ui.buildContextMenu(nodePath) - - 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): - if self.fNeedToSave: - reply = wx.MessageBox("Do you want to save current scene?", "Save?", - wx.YES_NO | wx.ICON_QUESTION) - if reply == wx.YES: - result = self.ui.onSave() - if result == False: - return - - base.direct.deselectAll() - self.ui.reset() - self.objectMgr.reset() - self.actionMgr.reset() - self.ui.perspView.camera.setPos(-19, -19, 19) - self.ui.perspView.camera.lookAt(Point3(0, 0, 0)) - self.ui.leftView.camera.setPos(600, 0, 0) - self.ui.frontView.camera.setPos(0, -600, 0) - self.ui.topView.camera.setPos(0, 0, 600) - self.resetOrthoCam(self.ui.topView) - self.resetOrthoCam(self.ui.frontView) - self.resetOrthoCam(self.ui.leftView) - self.fNeedToSave = False - self.setTitleWithFilename() - - def resetOrthoCam(self, view): - base.direct.drList[base.camList.index(NodePath(view.camNode))].orthoFactor = 0.1 - x = view.ClientSize.GetWidth() * 0.1 - y = view.ClientSize.GetHeight() * 0.1 - view.camLens.setFilmSize(x, y) - - def save(self): - if self.currentFile: - self.fileMgr.saveToFile(self.currentFile) - - def saveAs(self, fileName): - self.fileMgr.saveToFile(fileName) - self.currentFile = fileName - - def load(self, fileName): - self.reset() - self.fileMgr.loadFromFile(fileName) - self.currentFile = fileName - - def saveSettings(self): - if self.settingsFile is None: - return - - try: - f = open(self.settingsFile, 'w') - f.write('gridSize\n%f\n'%self.ui.perspView.grid.gridSize) - f.write('gridSpacing\n%f\n'%self.ui.perspView.grid.gridSpacing) - f.write('hotKey\n%s\n'%base.direct.hotKeyMap) - f.close() - except: - pass - - def loadSettings(self): - if self.settingsFile is None: - return - - try: - f = open(self.settingsFile, 'r') - configLines = f.readlines() - f.close() - - gridSize = 100.0 - gridSpacing = 5.0 - for i in range(0, len(configLines)): - line = configLines[i] - i = i + 1 - if line.startswith('gridSize'): - gridSize = float(configLines[i]) - elif line.startswith('gridSpacing'): - gridSpacing = float(configLines[i]) - elif line.startswith('hotKey'): - customHotKeyMap = eval(configLines[i]) - customHotKeyDict = {} - for hotKey in customHotKeyMap.keys(): - desc = customHotKeyMap[hotKey] - customHotKeyDict[desc[1]] = hotKey - - overriddenKeys = [] - for key in base.direct.hotKeyMap.keys(): - desc = base.direct.hotKeyMap[key] - if desc[1] in customHotKeyDict.keys(): - overriddenKeys.append(key) - - for key in overriddenKeys: - del base.direct.hotKeyMap[key] - - base.direct.hotKeyMap.update(customHotKeyMap) - - self.ui.updateGrids(gridSize, gridSpacing) - self.ui.updateMenu() - except: - pass - - def convertMaya(self, modelname, callBack, obj=None, isAnim=False): - if obj and isAnim: - mayaConverter = MayaConverter(self.ui, self, modelname, callBack, obj, isAnim) - else: - reply = wx.MessageBox("Is it an animation file?", "Animation?", - wx.YES_NO | wx.ICON_QUESTION) - if reply == wx.YES: - mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, True) - else: - mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False) - mayaConverter.Show() - - def convertFromMaya(self, modelname, callBack): - mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False) - mayaConverter.Show() - - def updateStatusReadout(self, status, color=None): - if status: - # add new status line, first check to see if it already exists - alreadyExists = False - for currLine in self.statusLines: - if (status == currLine[1]): - alreadyExists = True - break - if (alreadyExists == False): - time = globalClock.getRealTime() + 15 - self.statusLines.append([time,status,color]) - - # update display of new status lines - self.statusReadout.reparentTo(aspect2d) - statusText = "" - lastColor = None - for currLine in self.statusLines: - statusText += currLine[1] + '\n' - lastColor = currLine[2] - self.statusReadout.setText(statusText) - if (lastColor): - self.statusReadout.textNode.setCardColor( - lastColor[0], lastColor[1], lastColor[2], lastColor[3]) - self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1) - else: - self.statusReadout.textNode.setCardColor(1,1,1,1) - self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1) - - def updateStatusReadoutTimeouts(self,task=None): - removalList = [] - for currLine in self.statusLines: - if (globalClock.getRealTime() >= currLine[0]): - removalList.append(currLine) - for currRemoval in removalList: - self.statusLines.remove(currRemoval) - self.updateStatusReadout(None) - # perform doMethodLater again after delay - # This crashes when CTRL-C'ing, so this is a cheap hack. - #return 2 - from direct.task import Task - return Task.again +""" +Base class for Level Editor + +You should write your own LevelEditor class inheriting this. +Refer LevelEditor.py for example. +""" + +from direct.showbase.DirectObject import * +from direct.directtools.DirectUtil import * +from direct.gui.DirectGui import * + +from FileMgr import * +from ActionMgr import * +from MayaConverter import * + +class LevelEditorBase(DirectObject): + """ Base Class for Panda3D LevelEditor """ + def __init__(self): + #loadPrcFileData('startup', 'window-type none') + self.currentFile = None + self.fNeedToSave = False + self.actionEvents = [] + #self.objectMgr = ObjectMgr(self) + self.fileMgr = FileMgr(self) + self.actionMgr = ActionMgr() + + self.NPParent = render + + # define your own config file in inherited class + self.settingsFile = None + + # you can show/hide specific properties by using propertiesMask and this mode + self.mode = BitMask32() + + def initialize(self): + """ You should call this in your __init__ method of inherited LevelEditor class """ + # specifiy what obj can be 'selected' as objects + base.direct.selected.addTag('OBJRoot') + + 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_deselectAll', self.deselectAll), + ('LE-Undo', self.actionMgr.undo), + ('LE-Redo', self.actionMgr.redo), + ('LE-Duplicate', self.objectMgr.duplicateSelected), + ('DIRECT_manipulateObjectCleanup', self.cleanUpManipulating), + ('LE-MakeLive', self.objectMgr.makeSelectedLive), + ('LE-NewScene', self.ui.onNew), + ('LE-SaveScene', self.ui.onSave), + ('LE-OpenScene', self.ui.onOpen), + ('LE-Quit', self.ui.quit), + ('DIRECT-mouse3', self.handleMouse3), + ('DIRECT-toggleWidgetVis', self.toggleWidget), + ]) + + # Add all the action events + for event in self.actionEvents: + if len(event) == 3: + self.accept(event[0], event[1], event[2]) + else: + self.accept(event[0], event[1]) + + # editor state text display such as edit mode + self.statusReadout = OnscreenText( + pos = (-1.2, 0.9), bg=Vec4(1,1,1,1), + scale = 0.05, align = TextNode.ALeft, + mayChange = 1, font = TextNode.getDefaultFont()) + self.statusReadout.setText("") + # Make sure readout is never lit or drawn in wireframe + useDirectRenderStyle(self.statusReadout) + self.statusReadout.reparentTo(hidden) + self.statusLines = [] + taskMgr.doMethodLater(5, self.updateStatusReadoutTimeouts, 'updateStatus') + + self.loadSettings() + self.reset() + + def setTitleWithFilename(self, filename=""): + title = self.ui.appname + if filename != "": + filenameshort = os.path.basename(filename) + title = title + " (%s)"%filenameshort + self.ui.SetLabel(title) + + def removeNodePathHook(self, nodePath): + if nodePath is None: + return + base.direct.deselect(nodePath) + self.objectMgr.removeObjectByNodePath(nodePath) + + if (base.direct.selected.last != None and nodePath.compareTo(base.direct.selected.last)==0): + # if base.direct.selected.last is refering to this + # removed obj, clear the reference + if (hasattr(__builtins__,'last')): + __builtins__.last = None + else: + __builtins__['last'] = None + base.direct.selected.last = None + + def toggleWidget(self): + if self.objectMgr.currNodePath: + obj = self.objectMgr.findObjectByNodePath(self.objectMgr.currNodePath) + if obj and not obj[OG.OBJ_DEF].movable: + return + base.direct.toggleWidgetVis() + + def handleMouse3(self, modifiers): + if base.direct.fAlt or modifiers == 4: + return + + self.ui.onRightDown() + + def handleDelete(self): + oldSelectedNPs = base.direct.selected.getSelectedAsList() + oldUIDs = [] + for oldNP in oldSelectedNPs: + obj = self.objectMgr.findObjectByNodePath(oldNP) + if obj: + oldUIDs.append(obj[OG.OBJ_UID]) + + action = ActionDeleteObj(self) + self.actionMgr.push(action) + action() + + for uid in oldUIDs: + self.ui.sceneGraphUI.delete(uid) + +## reply = wx.MessageBox("Do you want to delete selected?", "Delete?", +## wx.YES_NO | wx.ICON_QUESTION) +## if reply == wx.YES: +## base.direct.removeAllSelected() +## else: +## # need to reset COA +## dnp = base.direct.selected.last +## # Update camera controls coa to this point +## # Coa2Camera = Coa2Dnp * Dnp2Camera +## mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(base.direct.camera) +## row = mCoa2Camera.getRow(3) +## coa = Vec3(row[0], row[1], row[2]) +## base.direct.cameraControl.updateCoa(coa) + + def cleanUpManipulating(self, selectedNPs): + for np in selectedNPs: + obj = self.objectMgr.findObjectByNodePath(np) + if obj: + action = ActionTransformObj(self, obj[OG.OBJ_UID], Mat4(np.getMat())) + self.actionMgr.push(action) + action() + + 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: + base.direct.deselect(nodePath) + return + + if fMultiSelect == 0 and fLEPane == 0: + oldSelectedNPs = base.direct.selected.getSelectedAsList() + for oldNP in oldSelectedNPs: + obj = self.objectMgr.findObjectByNodePath(oldNP) + if obj: + self.ui.sceneGraphUI.deSelect(obj[OG.OBJ_UID]) + self.objectMgr.selectObject(nodePath, fLEPane) + self.ui.buildContextMenu(nodePath) + + 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): + if self.fNeedToSave: + reply = wx.MessageBox("Do you want to save current scene?", "Save?", + wx.YES_NO | wx.ICON_QUESTION) + if reply == wx.YES: + result = self.ui.onSave() + if result == False: + return + + base.direct.deselectAll() + self.ui.reset() + self.objectMgr.reset() + self.actionMgr.reset() + self.ui.perspView.camera.setPos(-19, -19, 19) + self.ui.perspView.camera.lookAt(Point3(0, 0, 0)) + self.ui.leftView.camera.setPos(600, 0, 0) + self.ui.frontView.camera.setPos(0, -600, 0) + self.ui.topView.camera.setPos(0, 0, 600) + self.resetOrthoCam(self.ui.topView) + self.resetOrthoCam(self.ui.frontView) + self.resetOrthoCam(self.ui.leftView) + self.fNeedToSave = False + self.setTitleWithFilename() + + def resetOrthoCam(self, view): + base.direct.drList[base.camList.index(NodePath(view.camNode))].orthoFactor = 0.1 + x = view.ClientSize.GetWidth() * 0.1 + y = view.ClientSize.GetHeight() * 0.1 + view.camLens.setFilmSize(x, y) + + def save(self): + if self.currentFile: + self.fileMgr.saveToFile(self.currentFile) + + def saveAs(self, fileName): + self.fileMgr.saveToFile(fileName) + self.currentFile = fileName + + def load(self, fileName): + self.reset() + self.fileMgr.loadFromFile(fileName) + self.currentFile = fileName + + def saveSettings(self): + if self.settingsFile is None: + return + + try: + f = open(self.settingsFile, 'w') + f.write('gridSize\n%f\n'%self.ui.perspView.grid.gridSize) + f.write('gridSpacing\n%f\n'%self.ui.perspView.grid.gridSpacing) + f.write('hotKey\n%s\n'%base.direct.hotKeyMap) + f.close() + except: + pass + + def loadSettings(self): + if self.settingsFile is None: + return + + try: + f = open(self.settingsFile, 'r') + configLines = f.readlines() + f.close() + + gridSize = 100.0 + gridSpacing = 5.0 + for i in range(0, len(configLines)): + line = configLines[i] + i = i + 1 + if line.startswith('gridSize'): + gridSize = float(configLines[i]) + elif line.startswith('gridSpacing'): + gridSpacing = float(configLines[i]) + elif line.startswith('hotKey'): + customHotKeyMap = eval(configLines[i]) + customHotKeyDict = {} + for hotKey in customHotKeyMap.keys(): + desc = customHotKeyMap[hotKey] + customHotKeyDict[desc[1]] = hotKey + + overriddenKeys = [] + for key in base.direct.hotKeyMap.keys(): + desc = base.direct.hotKeyMap[key] + if desc[1] in customHotKeyDict.keys(): + overriddenKeys.append(key) + + for key in overriddenKeys: + del base.direct.hotKeyMap[key] + + base.direct.hotKeyMap.update(customHotKeyMap) + + self.ui.updateGrids(gridSize, gridSpacing) + self.ui.updateMenu() + except: + pass + + def convertMaya(self, modelname, callBack, obj=None, isAnim=False): + if obj and isAnim: + mayaConverter = MayaConverter(self.ui, self, modelname, callBack, obj, isAnim) + else: + reply = wx.MessageBox("Is it an animation file?", "Animation?", + wx.YES_NO | wx.ICON_QUESTION) + if reply == wx.YES: + mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, True) + else: + mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False) + mayaConverter.Show() + + def convertFromMaya(self, modelname, callBack): + mayaConverter = MayaConverter(self.ui, self, modelname, callBack, None, False) + mayaConverter.Show() + + def updateStatusReadout(self, status, color=None): + if status: + # add new status line, first check to see if it already exists + alreadyExists = False + for currLine in self.statusLines: + if (status == currLine[1]): + alreadyExists = True + break + if (alreadyExists == False): + time = globalClock.getRealTime() + 15 + self.statusLines.append([time,status,color]) + + # update display of new status lines + self.statusReadout.reparentTo(aspect2d) + statusText = "" + lastColor = None + for currLine in self.statusLines: + statusText += currLine[1] + '\n' + lastColor = currLine[2] + self.statusReadout.setText(statusText) + if (lastColor): + self.statusReadout.textNode.setCardColor( + lastColor[0], lastColor[1], lastColor[2], lastColor[3]) + self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + else: + self.statusReadout.textNode.setCardColor(1,1,1,1) + self.statusReadout.textNode.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + + def updateStatusReadoutTimeouts(self,task=None): + removalList = [] + for currLine in self.statusLines: + if (globalClock.getRealTime() >= currLine[0]): + removalList.append(currLine) + for currRemoval in removalList: + self.statusLines.remove(currRemoval) + self.updateStatusReadout(None) + # perform doMethodLater again after delay + # This crashes when CTRL-C'ing, so this is a cheap hack. + #return 2 + from direct.task import Task + return Task.again + + def propMeetsReq(self, typeName, parentNP): + if self.ui.parentToSelectedMenuItem.IsChecked(): + if base.direct.selected.last: + parent = base.le.objectMgr.findObjectByNodePath(base.direct.selected.last) + if parent: + parentNP[0] = parent[OG.OBJ_NP] + else: + parentNP[0] = None + return True diff --git a/direct/src/leveleditor/LevelEditorUIBase.py b/direct/src/leveleditor/LevelEditorUIBase.py index 014272612f..ea80da63b7 100755 --- a/direct/src/leveleditor/LevelEditorUIBase.py +++ b/direct/src/leveleditor/LevelEditorUIBase.py @@ -23,7 +23,10 @@ class PandaTextDropTarget(wx.TextDropTarget): def OnDropText(self, x, y, text): # create new object - action = ActionAddNewObj(self.editor, text) + parentNPRef = [None] + if not self.editor.propMeetsReq(text, parentNPRef): + return + action = ActionAddNewObj(self.editor, text, parent=parentNPRef[0]) self.editor.actionMgr.push(action) newobj = action() if newobj is None: @@ -112,6 +115,7 @@ ID_GRID_SIZE = 302 ID_GRID_SNAP = 303 ID_SHOW_PANDA_OBJECT = 304 ID_HOT_KEYS = 305 +ID_PARENT_TO_SELECTED = 306 class LevelEditorUIBase(WxPandaShell): """ Class for Panda3D LevelEditor """ @@ -131,6 +135,7 @@ class LevelEditorUIBase(WxPandaShell): ID_GRID_SNAP : ("Grid S&nap", None), ID_SHOW_PANDA_OBJECT : ("Show &Panda Objects", None), ID_HOT_KEYS : ("&Hot Keys", None), + ID_PARENT_TO_SELECTED : ("&Parent To Selected", None) }) self.editor = editor @@ -191,6 +196,8 @@ class LevelEditorUIBase(WxPandaShell): self.showPandaObjectsMenuItem = self.menuOptions.Append(ID_SHOW_PANDA_OBJECT, self.MENU_TEXTS[ID_SHOW_PANDA_OBJECT][0], kind = wx.ITEM_CHECK) self.Bind(wx.EVT_MENU, self.onShowPandaObjects, self.showPandaObjectsMenuItem) + self.parentToSelectedMenuItem = self.menuOptions.Append(ID_PARENT_TO_SELECTED, self.MENU_TEXTS[ID_PARENT_TO_SELECTED][0], kind = wx.ITEM_CHECK) + self.hotKeysMenuItem = self.menuOptions.Append(ID_HOT_KEYS, self.MENU_TEXTS[ID_HOT_KEYS][0]) self.Bind(wx.EVT_MENU, self.onHotKeys, self.hotKeysMenuItem) diff --git a/direct/src/leveleditor/ObjectMgrBase.py b/direct/src/leveleditor/ObjectMgrBase.py index 6f178c44be..2316be4262 100755 --- a/direct/src/leveleditor/ObjectMgrBase.py +++ b/direct/src/leveleditor/ObjectMgrBase.py @@ -180,7 +180,7 @@ class ObjectMgrBase: if self.editor: if fSelectObject: self.editor.select(newobj, fUndo=0) - self.editor.ui.sceneGraphUI.add(newobj) + self.editor.ui.sceneGraphUI.add(newobj, parent) self.editor.fNeedToSave = True return newobj @@ -771,3 +771,5 @@ class ObjectMgrBase: def flatten(self, newobjModel, objDef, uid): # override this to flatten models pass + + diff --git a/direct/src/leveleditor/SceneGraphUIBase.py b/direct/src/leveleditor/SceneGraphUIBase.py index 6a7c676d44..65bb775d5f 100755 --- a/direct/src/leveleditor/SceneGraphUIBase.py +++ b/direct/src/leveleditor/SceneGraphUIBase.py @@ -112,7 +112,7 @@ class SceneGraphUIBase(wx.Panel): item, cookie = self.tree.GetNextChild(parent, cookie) self.removePandaObjectChildren(itemToRemove) - def add(self, item): + def add(self, item, parentNP = None): #import pdb;pdb.set_trace() if item is None: return @@ -120,10 +120,10 @@ class SceneGraphUIBase(wx.Panel): if obj is None: return - parentNodePath = obj[OG.OBJ_NP].getParent() - parentObj = self.editor.objectMgr.findObjectByNodePath(parentNodePath) + if parentNP is None : + parentNP = obj[OG.OBJ_NP].getParent() + parentObj = self.editor.objectMgr.findObjectByNodePath(parentNP) - #import pdb;pdb.set_trace() if parentObj is None: parent = self.root else: