LevelEditorBase.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. """
  2. Base class for Level Editor
  3. You should write your own LevelEditor class inheriting this.
  4. Refer LevelEditor.py for example.
  5. """
  6. from pandac.PandaModules import *
  7. from direct.showbase.ShowBase import *
  8. from direct.showbase.DirectObject import *
  9. from direct.directtools.DirectGlobals import *
  10. base = ShowBase(False)
  11. from ObjectMgr import *
  12. from FileMgr import *
  13. from ActionMgr import *
  14. from MayaConverter import *
  15. class LevelEditorBase(DirectObject):
  16. """ Base Class for Panda3D LevelEditor """
  17. def __init__(self):
  18. #loadPrcFileData('startup', 'window-type none')
  19. self.currentFile = None
  20. self.actionEvents = []
  21. self.objectMgr = ObjectMgr(self)
  22. self.fileMgr = FileMgr(self)
  23. self.actionMgr = ActionMgr()
  24. # define your own config file in inherited class
  25. self.settingsFile = None
  26. def initialize(self):
  27. """ You should call this in your __init__ method of inherited LevelEditor class """
  28. fTk = 0
  29. fWx = 0
  30. base.startDirect(fWantTk = fTk, fWantWx = fWx)
  31. base.closeWindow(base.win)
  32. base.win = base.winList[3]
  33. base.direct.disableMouseEvents()
  34. newMouseEvents = map(lambda x: "_le_per_%s"%x, base.direct.mouseEvents) +\
  35. map(lambda x: "_le_fro_%s"%x, base.direct.mouseEvents) +\
  36. map(lambda x: "_le_lef_%s"%x, base.direct.mouseEvents) +\
  37. map(lambda x: "_le_top_%s"%x, base.direct.mouseEvents)
  38. base.direct.mouseEvents = newMouseEvents
  39. base.direct.enableMouseEvents()
  40. base.direct.disableKeyEvents()
  41. keyEvents = map(lambda x: "_le_per_%s"%x, base.direct.keyEvents) +\
  42. map(lambda x: "_le_fro_%s"%x, base.direct.keyEvents) +\
  43. map(lambda x: "_le_lef_%s"%x, base.direct.keyEvents) +\
  44. map(lambda x: "_le_top_%s"%x, base.direct.keyEvents)
  45. base.direct.keyEvents = keyEvents
  46. base.direct.enableKeyEvents()
  47. base.direct.disableModifierEvents()
  48. modifierEvents = map(lambda x: "_le_per_%s"%x, base.direct.modifierEvents) +\
  49. map(lambda x: "_le_fro_%s"%x, base.direct.modifierEvents) +\
  50. map(lambda x: "_le_lef_%s"%x, base.direct.modifierEvents) +\
  51. map(lambda x: "_le_top_%s"%x, base.direct.modifierEvents)
  52. base.direct.modifierEvents = modifierEvents
  53. base.direct.enableModifierEvents()
  54. base.direct.cameraControl.lockRoll = True
  55. base.direct.setFScaleWidgetByCam(1)
  56. unpickables = [
  57. "z-guide",
  58. "y-guide",
  59. "x-guide",
  60. "x-disc-geom",
  61. "x-ring-line",
  62. "x-post-line",
  63. "y-disc-geom",
  64. "y-ring-line",
  65. "y-post-line",
  66. "z-disc-geom",
  67. "z-ring-line",
  68. "z-post-line",
  69. "centerLines",
  70. "majorLines",
  71. "minorLines",
  72. "Sphere",]
  73. for unpickable in unpickables:
  74. base.direct.addUnpickable(unpickable)
  75. base.direct.manipulationControl.optionalSkipFlags |= SKIP_UNPICKABLE
  76. base.direct.manipulationControl.fAllowMarquee = 1
  77. base.direct.manipulationControl.supportMultiView()
  78. base.direct.cameraControl.useMayaCamControls = 1
  79. for widget in base.direct.manipulationControl.widgetList:
  80. widget.setBin('gui-popup', 0)
  81. widget.setDepthTest(0)
  82. # [gjeon] to intercept messages here
  83. base.direct.ignore('DIRECT-delete')
  84. base.direct.ignore('DIRECT-select')
  85. base.direct.ignore('DIRECT-preDeselectAll')
  86. base.direct.fIgnoreDirectOnlyKeyMap = 1
  87. # [gjeon] do not use the old way of finding current DR
  88. base.direct.drList.tryToGetCurrentDr = False
  89. # specifiy what obj can be 'selected' as objects
  90. base.direct.selected.addTag('OBJRoot')
  91. self.actionEvents.extend([
  92. # Node path events
  93. ('DIRECT-select', self.select),
  94. ('DIRECT-delete', self.handleDelete),
  95. ('DIRECT-preDeselectAll', self.deselectAll),
  96. ('DIRECT_deselectAll', self.deselectAllCB),
  97. ('preRemoveNodePath', self.removeNodePathHook),
  98. ('DIRECT_deselectedNodePath', self.deselectAllCB),
  99. ('DIRECT_selectedNodePath_fMulti_fTag_fLEPane', self.selectedNodePathHook),
  100. ('DIRECT_deselectAll', self.deselectAll),
  101. ('LE-Undo', self.actionMgr.undo),
  102. ('LE-Redo', self.actionMgr.redo),
  103. ('LE-Duplicate', self.objectMgr.duplicateSelected),
  104. ('DIRECT_manipulateObjectCleanup', self.cleanUpManipulating),
  105. ])
  106. # Add all the action events
  107. for event in self.actionEvents:
  108. if len(event) == 3:
  109. self.accept(event[0], event[1], event[2])
  110. else:
  111. self.accept(event[0], event[1])
  112. self.loadSettings()
  113. def removeNodePathHook(self, nodePath):
  114. if nodePath is None:
  115. return
  116. base.direct.deselect(nodePath)
  117. self.objectMgr.removeObjectByNodePath(nodePath)
  118. if (base.direct.selected.last != None and nodePath.compareTo(base.direct.selected.last)==0):
  119. # if base.direct.selected.last is refering to this
  120. # removed obj, clear the reference
  121. if (hasattr(__builtins__,'last')):
  122. __builtins__.last = None
  123. else:
  124. __builtins__['last'] = None
  125. base.direct.selected.last = None
  126. def handleDelete(self):
  127. oldSelectedNPs = base.direct.selected.getSelectedAsList()
  128. oldUIDs = []
  129. for oldNP in oldSelectedNPs:
  130. obj = self.objectMgr.findObjectByNodePath(oldNP)
  131. if obj:
  132. oldUIDs.append(obj[OG.OBJ_UID])
  133. action = ActionDeleteObj(self)
  134. self.actionMgr.push(action)
  135. action()
  136. for uid in oldUIDs:
  137. self.ui.sceneGraphUI.delete(uid)
  138. ## reply = wx.MessageBox("Do you want to delete selected?", "Delete?",
  139. ## wx.YES_NO | wx.ICON_QUESTION)
  140. ## if reply == wx.YES:
  141. ## base.direct.removeAllSelected()
  142. ## else:
  143. ## # need to reset COA
  144. ## dnp = base.direct.selected.last
  145. ## # Update camera controls coa to this point
  146. ## # Coa2Camera = Coa2Dnp * Dnp2Camera
  147. ## mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(base.direct.camera)
  148. ## row = mCoa2Camera.getRow(3)
  149. ## coa = Vec3(row[0], row[1], row[2])
  150. ## base.direct.cameraControl.updateCoa(coa)
  151. def cleanUpManipulating(self, selectedNPs):
  152. for np in selectedNPs:
  153. obj = self.objectMgr.findObjectByNodePath(np)
  154. if obj:
  155. action = ActionTransformObj(self, obj[OG.OBJ_UID], Mat4(np.getMat()))
  156. self.actionMgr.push(action)
  157. action()
  158. def select(self, nodePath, fMultiSelect=0, fSelectTag=1, fResetAncestry=1, fLEPane=0, fUndo=1):
  159. if fUndo:
  160. # Select tagged object if present
  161. if fSelectTag:
  162. for tag in base.direct.selected.tagList:
  163. if nodePath.hasNetTag(tag):
  164. nodePath = nodePath.findNetTag(tag)
  165. break
  166. action = ActionSelectObj(self, nodePath, fMultiSelect)
  167. self.actionMgr.push(action)
  168. action()
  169. else:
  170. base.direct.selectCB(nodePath, fMultiSelect, fSelectTag, fResetAncestry, fLEPane, fUndo)
  171. def selectedNodePathHook(self, nodePath, fMultiSelect = 0, fSelectTag = 1, fLEPane = 0):
  172. # handle unpickable nodepath
  173. if nodePath.getName() in base.direct.iRay.unpickable:
  174. base.direct.deselect(nodePath)
  175. return
  176. if fMultiSelect == 0 and fLEPane == 0:
  177. oldSelectedNPs = base.direct.selected.getSelectedAsList()
  178. for oldNP in oldSelectedNPs:
  179. obj = self.objectMgr.findObjectByNodePath(oldNP)
  180. if obj:
  181. self.ui.sceneGraphUI.deSelect(obj[OG.OBJ_UID])
  182. self.objectMgr.selectObject(nodePath, fLEPane)
  183. def deselectAll(self, np=None):
  184. if len(base.direct.selected.getSelectedAsList()) ==0:
  185. return
  186. action = ActionDeselectAll(self)
  187. self.actionMgr.push(action)
  188. action()
  189. def deselectAllCB(self, dnp=None):
  190. self.objectMgr.deselectAll()
  191. def reset(self):
  192. base.direct.deselectAll()
  193. self.objectMgr.reset()
  194. self.actionMgr.reset()
  195. self.ui.perspView.camera.setPos(-19, -19, 19)
  196. self.ui.leftView.camera.setPos(600, 0, 0)
  197. self.ui.frontView.camera.setPos(0, -600, 0)
  198. self.ui.topView.camera.setPos(0, 0, 600)
  199. self.resetOrthoCam(self.ui.topView)
  200. self.resetOrthoCam(self.ui.frontView)
  201. self.resetOrthoCam(self.ui.leftView)
  202. def resetOrthoCam(self, view):
  203. base.direct.drList[base.camList.index(NodePath(view.camNode))].orthoFactor = 0.1
  204. x = view.ClientSize.GetWidth() * 0.1
  205. y = view.ClientSize.GetHeight() * 0.1
  206. view.camLens.setFilmSize(x, y)
  207. def save(self):
  208. if self.currentFile:
  209. self.fileMgr.saveToFile(self.currentFile)
  210. def saveAs(self, fileName):
  211. self.fileMgr.saveToFile(fileName)
  212. def load(self, fileName):
  213. self.reset()
  214. self.fileMgr.loadFromFile(fileName)
  215. self.currentFile = fileName
  216. def saveSettings(self):
  217. if self.settingsFile is None:
  218. return
  219. try:
  220. f = open(self.settingsFile, 'w')
  221. f.write('gridSize\n%f\n'%self.ui.perspView.grid.gridSize)
  222. f.write('gridSpacing\n%f\n'%self.ui.perspView.grid.gridSpacing)
  223. f.write('hotKey\n%s\n'%base.direct.hotKeyMap)
  224. f.close()
  225. except:
  226. pass
  227. def loadSettings(self):
  228. if self.settingsFile is None:
  229. return
  230. try:
  231. f = open(self.settingsFile, 'r')
  232. configLines = f.readlines()
  233. f.close()
  234. gridSize = 100.0
  235. gridSpacing = 5.0
  236. for i in range(0, len(configLines)):
  237. line = configLines[i]
  238. i = i + 1
  239. if line.startswith('gridSize'):
  240. gridSize = float(configLines[i])
  241. elif line.startswith('gridSpacing'):
  242. gridSpacing = float(configLines[i])
  243. elif line.startswith('hotKey'):
  244. base.direct.hotKeyMap.update(eval(configLines[i]))
  245. self.ui.updateGrids(gridSize, gridSpacing)
  246. except:
  247. pass
  248. def convertMaya(self, modelname, obj=None, isAnim=False):
  249. if obj and isAnim:
  250. mayaConverter = MayaConverter(self.ui, self, modelname, obj, isAnim)
  251. else:
  252. reply = wx.MessageBox("Is it an animation file?", "Animation?",
  253. wx.YES_NO | wx.ICON_QUESTION)
  254. if reply == wx.YES:
  255. mayaConverter = MayaConverter(self.ui, self, modelname, None, True)
  256. else:
  257. mayaConverter = MayaConverter(self.ui, self, modelname, None, False)
  258. mayaConverter.Show()