|
|
@@ -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
|