Example #1
0
    def __init__(self, editorInstance):
        """EditorApp constructor."""
        # Instance of the editor
        self.editorInstance = editorInstance

        # Create the Wx app
        self.wxApp = wx.App(redirect=False)
        self.wxApp.SetAppName("Panda Editor")
        self.wxApp.SetClassName("PEditor")

        self.modified = True
        self.filename = Filename()

        # Initialize the app shell and add some controls
        AppShell.__init__(self, title="Panda Editor", pos=origin)
        self.splitter1 = wx.SplitterWindow(self, style=wx.SP_3D | wx.SP_BORDER)
        self.splitter1.SetMinimumPaneSize(1)
        self.splitter2 = wx.SplitterWindow(self.splitter1,
                                           style=wx.SP_3D | wx.SP_BORDER)
        self.splitter2.SetMinimumPaneSize(1)
        self.leftBarSplitter = wx.SplitterWindow(self.splitter2,
                                                 style=wx.SP_3D | wx.SP_BORDER)
        self.leftBarSplitter.SetMinimumPaneSize(1)
        #self.rightBarSplitter = wx.SplitterWindow(self.splitter1, style = wx.SP_3D | wx.SP_BORDER)
        #self.rightBarSplitter.SetMinimumPaneSize(1)
        self.sceneGraphTree = SceneGraphTree(self.leftBarSplitter)
        self.propertyGrid = PropertyGrid(self.leftBarSplitter)
        self.textureManager = TextureManager(self.splitter1,
                                             style=wx.SP_3D | wx.SUNKEN_BORDER)
        self.view = Viewport.makePerspective(self.splitter2)
        sizer = wx.BoxSizer(wx.VERTICAL)
        assert self.leftBarSplitter.SplitHorizontally(self.sceneGraphTree,
                                                      self.propertyGrid)
        assert self.splitter2.SplitVertically(self.leftBarSplitter, self.view,
                                              200)
        #assert self.rightBarSplitter.SplitHorizontally(self.textureManager, None)
        assert self.splitter1.SplitVertically(self.splitter2,
                                              self.textureManager, -200)
        sizer.Add(self.splitter1, 1, wx.EXPAND, 0)
        self.splitter1.Unsplit()  # Yes, I know this looks odd.
        self.SetSizer(sizer)
        self.Layout()
        self.initialize()
        self.splitter2.SetSashPosition(200)

        # Setup some events
        base.accept("c", self.onCenterTrackball)

        base.accept(EVENT_MODELCONTROLLER_SELECTED_OBJECT_CHANGE,
                    self.onModelSelect)
        # If a model-translate-rotate-scale tool is selected the automatic mouse
        # movement has to be disable to prevent camera & object movement.
        # Hmm doesnt really work as well... (camera is still moved)
        base.accept(EVENT_MODELCONTROLLER_EDITTOOL_SELECTED,
                    lambda x=None: base.disableMouse())
        base.accept(EVENT_MODELCONTROLLER_EDITTOOL_DESELECTED,
                    lambda x=None: base.enableMouse())
        base.accept(EVENT_MODELCONTROLLER_FULL_REFRESH, self.__setattr__,
                    ["modified", True])
 def __init__(self, editorInstance):
   """EditorApp constructor."""
   # Instance of the editor
   self.editorInstance = editorInstance
   
   # Create the Wx app
   self.wxApp = wx.App(redirect = False)
   self.wxApp.SetAppName("Panda Editor")
   self.wxApp.SetClassName("PEditor")
   
   self.modified = True
   self.filename = Filename()
   
   # Initialize the app shell and add some controls
   AppShell.__init__(self, title = "Panda Editor", pos = origin)
   self.splitter1 = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_BORDER)
   self.splitter1.SetMinimumPaneSize(1)
   self.splitter2 = wx.SplitterWindow(self.splitter1, style = wx.SP_3D | wx.SP_BORDER)
   self.splitter2.SetMinimumPaneSize(1)
   self.leftBarSplitter = wx.SplitterWindow(self.splitter2, style = wx.SP_3D | wx.SP_BORDER)
   self.leftBarSplitter.SetMinimumPaneSize(1)
   #self.rightBarSplitter = wx.SplitterWindow(self.splitter1, style = wx.SP_3D | wx.SP_BORDER)
   #self.rightBarSplitter.SetMinimumPaneSize(1)
   self.sceneGraphTree = SceneGraphTree(self.leftBarSplitter)
   self.propertyGrid = PropertyGrid(self.leftBarSplitter)
   self.textureManager = TextureManager(self.splitter1, style = wx.SP_3D | wx.SUNKEN_BORDER)
   self.view = Viewport.makePerspective(self.splitter2)
   sizer = wx.BoxSizer(wx.VERTICAL)
   assert self.leftBarSplitter.SplitHorizontally(self.sceneGraphTree, self.propertyGrid)
   assert self.splitter2.SplitVertically(self.leftBarSplitter, self.view, 200)
   #assert self.rightBarSplitter.SplitHorizontally(self.textureManager, None)
   assert self.splitter1.SplitVertically(self.splitter2, self.textureManager, -200)
   sizer.Add(self.splitter1, 1, wx.EXPAND, 0)
   self.splitter1.Unsplit() # Yes, I know this looks odd.
   self.SetSizer(sizer); self.Layout()
   self.initialize()
   self.splitter2.SetSashPosition(200)
   
   # Setup some events
   base.accept("c", self.onCenterTrackball)
   
   base.accept(EVENT_MODELCONTROLLER_SELECTED_OBJECT_CHANGE, self.onModelSelect)
   # If a model-translate-rotate-scale tool is selected the automatic mouse
   # movement has to be disable to prevent camera & object movement.
   # Hmm doesnt really work as well... (camera is still moved)
   base.accept(EVENT_MODELCONTROLLER_EDITTOOL_SELECTED, lambda x=None:base.disableMouse())
   base.accept(EVENT_MODELCONTROLLER_EDITTOOL_DESELECTED, lambda x=None:base.enableMouse())
   base.accept(EVENT_MODELCONTROLLER_FULL_REFRESH, self.__setattr__, ["modified", True])
class EditorApp(AppShell):
  appversion    = "cvs"
  appname       = "Panda Editor"
  copyright     = "Copyright (c) Carnegie Mellon University.\nAll rights reserved."
  contactname   = "pro-rsoft"
  contactemail  = "*****@*****.**"
  frameWidth    = defWP.getXSize()
  frameHeight   = defWP.getYSize()
  
  def __init__(self, editorInstance):
    """EditorApp constructor."""
    # Instance of the editor
    self.editorInstance = editorInstance
    
    # Create the Wx app
    self.wxApp = wx.App(redirect = False)
    self.wxApp.SetAppName("Panda Editor")
    self.wxApp.SetClassName("PEditor")
    
    self.modified = True
    self.filename = Filename()
    
    # Initialize the app shell and add some controls
    AppShell.__init__(self, title = "Panda Editor", pos = origin)
    self.splitter1 = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_BORDER)
    self.splitter1.SetMinimumPaneSize(1)
    self.splitter2 = wx.SplitterWindow(self.splitter1, style = wx.SP_3D | wx.SP_BORDER)
    self.splitter2.SetMinimumPaneSize(1)
    self.leftBarSplitter = wx.SplitterWindow(self.splitter2, style = wx.SP_3D | wx.SP_BORDER)
    self.leftBarSplitter.SetMinimumPaneSize(1)
    #self.rightBarSplitter = wx.SplitterWindow(self.splitter1, style = wx.SP_3D | wx.SP_BORDER)
    #self.rightBarSplitter.SetMinimumPaneSize(1)
    self.sceneGraphTree = SceneGraphTree(self.leftBarSplitter)
    self.propertyGrid = PropertyGrid(self.leftBarSplitter)
    self.textureManager = TextureManager(self.splitter1, style = wx.SP_3D | wx.SUNKEN_BORDER)
    self.view = Viewport.makePerspective(self.splitter2)
    sizer = wx.BoxSizer(wx.VERTICAL)
    assert self.leftBarSplitter.SplitHorizontally(self.sceneGraphTree, self.propertyGrid)
    assert self.splitter2.SplitVertically(self.leftBarSplitter, self.view, 200)
    #assert self.rightBarSplitter.SplitHorizontally(self.textureManager, None)
    assert self.splitter1.SplitVertically(self.splitter2, self.textureManager, -200)
    sizer.Add(self.splitter1, 1, wx.EXPAND, 0)
    self.splitter1.Unsplit() # Yes, I know this looks odd.
    self.SetSizer(sizer); self.Layout()
    self.initialize()
    self.splitter2.SetSashPosition(200)
    
    # Setup some events
    base.accept("c", self.onCenterTrackball)
    
    base.accept(EVENT_MODELCONTROLLER_SELECTED_OBJECT_CHANGE, self.onModelSelect)
    # If a model-translate-rotate-scale tool is selected the automatic mouse
    # movement has to be disable to prevent camera & object movement.
    # Hmm doesnt really work as well... (camera is still moved)
    base.accept(EVENT_MODELCONTROLLER_EDITTOOL_SELECTED, lambda x=None:base.disableMouse())
    base.accept(EVENT_MODELCONTROLLER_EDITTOOL_DESELECTED, lambda x=None:base.enableMouse())
    base.accept(EVENT_MODELCONTROLLER_FULL_REFRESH, self.__setattr__, ["modified", True])
    # The object has been modified in the scene, this event happens every frame
    #base.accept(EVENT_MODELCONTROLLER_FAST_REFRESH, )
    # The editor has been disabled, collisions etc are deleted
    #base.accept(EDITOR_TOGGLE_OFF_EVENT, )
    # The editor has been enabled, collisions etc are created.
    # This event happens shortly after the wxgui has been created
    #base.accept(EDITOR_TOGGLE_ON_EVENT, )
  
  def appInit(self):
    print "I: EditorApp.appInit"
    """Overridden from WxAppShell.py."""
    # Create a new event loop (to overide default wxEventLoop)
    self.evtLoop = wx.EventLoop()
    self.oldLoop = wx.EventLoop.GetActive()
    wx.EventLoop.SetActive(self.evtLoop)
    taskMgr.add(self.wxStep, "evtLoopTask")
  
  def createMenuBar(self):
    """Overridden from WxAppShell.py."""
    # File menu
    self.menuFile = wx.Menu()
    self.menuBar.Append(self.menuFile, "&File")
    self.Bind(wx.EVT_MENU, self.onNew, self.menuFile.Append(wx.ID_NEW, "&New"))
    self.Bind(wx.EVT_MENU, self.onOpen, self.menuFile.Append(wx.ID_OPEN, "&Open"))
    self.Bind(wx.EVT_MENU, self.onSave, self.menuFile.Append(wx.ID_SAVE, "&Save"))
    self.Bind(wx.EVT_MENU, self.onSaveAs, self.menuFile.Append(wx.ID_SAVEAS, "Save &As..."))
    self.menuFile.AppendSeparator()
    self.Bind(wx.EVT_MENU, self.quit, self.menuFile.Append(wx.ID_EXIT, "&Quit"))
    
    # Edit menu
    #self.menuEdit = wx.Menu()
    #self.menuBar.Append(self.menuEdit, "&Edit")
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_UNDO, "&Undo"))
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_REDO, "&Redo"))
    #self.menuEdit.AppendSeparator()
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_CUT, "Cu&t"))
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_COPY, "&Copy"))
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_PASTE, "&Paste"))
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_DELETE, "&Delete"))
    #self.menuEdit.AppendSeparator()
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_SELECTALL, "Select &All"))
    #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_ANY, "Select &None"))
    
    # View menu
    self.menuView = wx.Menu()
    self.menuBar.Append(self.menuView, "&View")
    self.Bind(wx.EVT_MENU, self.onToggleGrid, self.menuView.AppendCheckItem(ID_ENABLE_GRID, "E&nable Grid"))
    self.Bind(wx.EVT_MENU, self.onCenterTrackball, self.menuView.Append(wx.ID_ANY, "&Center Model"))
    self.menuBar.Check(ID_ENABLE_GRID, True)
    
    # Create menu
    self.menuCreate = wx.Menu()
    self.menuBar.Append(self.menuCreate, "&Create")
    self.Bind(wx.EVT_MENU, self.onCreateObject, self.menuCreate.Append(ID_MODEL, "&Model..."))
    self.Bind(wx.EVT_MENU, self.onCreateObject, self.menuCreate.Append(ID_TERRAIN, "&Terrain..."))
    
    self.menuCreateLight = wx.Menu()
    self.Bind(wx.EVT_MENU, self.onCreateObject, self.menuCreateLight.Append(ID_AMBIENT, "&Ambient"))
    self.Bind(wx.EVT_MENU, self.onCreateObject, self.menuCreateLight.Append(ID_DIRECTIONAL, "&Directional"))
    self.Bind(wx.EVT_MENU, self.onCreateObject, self.menuCreateLight.Append(ID_POINT, "&Point"))
    self.Bind(wx.EVT_MENU, self.onCreateObject, self.menuCreateLight.Append(ID_SPOT, "&Spotlight"))
    self.menuCreate.AppendSubMenu(self.menuCreateLight, "&Light")
    
    # Viewports menu
    self.menuViewports = wx.Menu()
    self.menuBar.Append(self.menuViewports, "View&ports")
    self.Bind(wx.EVT_MENU, self.onChangeViewports, self.menuViewports.AppendRadioItem(ID_SINGLE_VIEWPORT, "&Single Viewport"))
    self.Bind(wx.EVT_MENU, self.onChangeViewports, self.menuViewports.AppendRadioItem(ID_4x4_GRID, "4x4 &Grid"))
    self.Bind(wx.EVT_MENU, self.onChangeViewports, self.menuViewports.AppendRadioItem(ID_2_HORIZONTAL, "2 &Horizontal"))
    self.Bind(wx.EVT_MENU, self.onChangeViewports, self.menuViewports.AppendRadioItem(ID_2_VERTICAL, "2 &Vertical"))
    self.menuBar.Check(ID_SINGLE_VIEWPORT, True)
    self.menuViewports.AppendSeparator()
    self.viewportMenus = []
  
  def createInterface(self):
    """Overridden from WxAppShell.py."""
    self.CreateStatusBar()
    self.SetStatusText("Welcome to the Panda3D Editor")
    self.Update()
  
  def initialize(self):
    """Initializes the viewports and editor."""
    print "I: EditorApp.initialize"
    self.Update()
    ViewportManager.updateAll()
    self.wxStep()
    ViewportManager.initializeAll()
    self.reloadViewportMenus()
    #self.editorInstance.toggle("WorldEditMode")
    # Position the camera
    if base.trackball != None:
      base.trackball.node().setPos(0, 30, 0)
      base.trackball.node().setHpr(0, 15, 0)
    
    # Load the direct things
    self.grid = DirectGrid(parent = render)
    self.sceneGraphTree.reload()
    if not isinstance(self.view, Viewport):
      self.view.center()
    
    # Initialize some stuff
    self.editorInstance.toggleEditmode(True)
  
  def reloadViewportMenus(self):
    """Reloads the viewport menus."""
    # Add the individual viewport menus
    for m in self.viewportMenus:
      m.Destroy()
    self.viewportMenus = []
    collect()
    #for v in range(len(ViewportManager.viewports)):
    #  self.viewportMenus.append(self.menuViewports.AppendSubMenu(ViewportMenu(ViewportManager.viewports[v]), "Viewport %d" % (v + 1)))
  
  def wxStep(self, task = None):
    """A step in the WX event loop. You can either call this yourself or use as task."""
    while self.evtLoop.Pending():
      self.evtLoop.Dispatch()
    self.wxApp.ProcessIdle()
    if task != None: return task.cont
  
  def onModelSelect(self, model):
    """Invoked when a model is selected. Shows/hides the texture panel."""
    if model == None and self.splitter1.IsSplit():
      self.splitter1.Unsplit()
    elif model != None and not self.splitter1.IsSplit():
      assert self.splitter1.SplitVertically(self.splitter2, self.textureManager, -200)
  
  def onDestroy(self, evt):
    """Invoked when the window is destroyed."""
    wx.EventLoop.SetActive(self.oldLoop)
  
  def onNew(self, evt = None):
    self.filename = Filename()
    self.modified = True
    self.SetTitle("Panda Editor")
    self.editorInstance.destroyAllModels()
  
  def onOpen(self, evt = None):
    filter = "Panda3D Egg Scene Format (*.egs)|*.[eE][gG][sS];*.egs"
    #filter += "|Panda3D Compressed Egg Format (*.egg.pz)|*.[eE][gG][gG].[pP][zZ];*.egg.pz"
    #filter += "|Panda3D Binary Format (*.bam)|*.[bB][aA][mM];*.bam"
    #filter += "|Panda3D Compressed Binary Format (*.bam)|*.[bB][aA][mM].[pP][zZ];*.bam.pz"
    ''' # disabled by hypnos, making the loading work
    filter += "|MultiGen (*.flt)|*.[fF][lL][tT]"
    filter += "|Lightwave (*.lwo)|*.[lL][wW][oO]"
    filter += "|AutoCAD (*.dxf)|*.[dD][xX][fF]"
    filter += "|VRML (*.wrl)|*.[wW][rR][lL]"
    filter += "|DirectX (*.x)|*.[xX]"
    filter += "|COLLADA (*.dae)|*.[dD][aA][eE]" '''
    dlg = wx.FileDialog(self, "Load file", "", "", filter, wx.OPEN)
    try:
      if dlg.ShowModal() == wx.ID_OK:
        #self.filename = Filename.fromOsSpecific(dlg.GetPath())
        p3dFilename = Filename.fromOsSpecific(dlg.GetPath())
        self.filename = str(dlg.GetPath())
        self.SetTitle(p3dFilename.getBasename() + " - Panda Editor")
        self.modified = False
        self.editorInstance.loadEggModelsFile( self.filename )
        # Reset the camera
        if base.trackball != None:
          base.trackball.node().setPos(0, 30, 0)
          base.trackball.node().setHpr(0, 15, 0)
        self.onCenterTrackball()
        if p3dFilename.getExtension().lower() != "bam":
          self.filename = Filename()
          self.modified = True
        self.sceneGraphTree.reload()
    finally:
      dlg.Destroy()
  
  def onSave(self, evt = None):
    if not self.modified: return
    if self.filename == None or self.filename.empty():
      self.onSaveAs(evt)
    else:
      dlg = wx.FileDialog(self, "Save file", "", "", "Panda3D Binary Format (*.bam)|.[bB][aA][mM];*.bam", wx.SAVE | wx.FD_OVERWRITE_PROMPT)
      try:
        if dlg.ShowModal() == wx.ID_OK:
          self.filename = Filename.fromOsSpecific(dlg.GetPath())
          self.SetTitle(self.filename.getBasename() + " - Panda Editor")
          self.modified = False
          self.editorInstance.saveEggModelsFile(self.filename.getFullpath())
      finally:
        dlg.Destroy()
  
  def onSaveAs(self, evt = None):
    dlg = wx.FileDialog(self, "Save file as", "", "", "Panda3D Binary Format (*.bam)|.[bB][aA][mM];*.bam", wx.SAVE | wx.FD_OVERWRITE_PROMPT)
    try:
      if dlg.ShowModal() == wx.ID_OK:
        self.onNew()
        self.filename = Filename.fromOsSpecific(dlg.GetPath())
        self.SetTitle(self.filename.getBasename() + " - Panda Editor")
        self.modified = False
        self.editorInstance.saveEggModelsFile(self.filename.getFullpath())
    finally:
      dlg.Destroy()
  
  def onCreateObject(self, e):
    """Invoked when the user hits one of the buttons in the "Create" menu."""
    
    modelParent = modelController.getSelectedObject() 
    if modelParent == None: modelParent = render
    objectInstance = None
    if e.Id == ID_NODEPATH:
      objectInstance = NodePathWrapper.onCreateInstance(modelParent)
    elif e.Id == ID_MODEL:
      filter = "Panda3D Egg Format (*.egg)|*.[eE][gG][gG];*.egg"
      filter += "|Panda3D Binary Format (*.bam)|*.[bB][aA][mM];*.bam"
      filter += "|MultiGen (*.flt)|*.[fF][lL][tT];*.flt"
      filter += "|Lightwave (*.lwo)|*.[lL][wW][oO];*.lwo"
      filter += "|AutoCAD (*.dxf)|*.[dD][xX][fF];*.dxf"
      filter += "|VRML (*.wrl)|*.[wW][rR][lL];*.wrl"
      filter += "|DirectX (*.x)|*.[xX];*.x"
      filter += "|COLLADA (*.dae)|*.[dD][aA][eE];*.dae"
      dlg = wx.FileDialog(self, "Select model", "", "", filter, wx.OPEN)
      try:
        if dlg.ShowModal() == wx.ID_OK:
          objectInstance = NodePathWrapper.onCreateInstance(modelParent, Filename.fromOsSpecific(dlg.GetPath()).getFullpath())
      finally:
        dlg.Destroy()
    elif e.Id == ID_TERRAIN:
      filter = "Portable Network Graphics (*.png)|*.[pP][nN][gG];*.png"
      dlg = wx.FileDialog(self, "Select heightfield", "", "", filter, wx.OPEN)
      try:
        if dlg.ShowModal() == wx.ID_OK:
          objectInstance = GeoMipTerrainNodeWrapper.onCreateInstance(modelParent, Filename.fromOsSpecific(dlg.GetPath()).getFullpath())
      finally:
        dlg.Destroy()
    elif e.Id == ID_AMBIENT:
      objectInstance = AmbientLightNodeWrapper.onCreateInstance(modelParent)
    elif e.Id == ID_DIRECTIONAL:
      objectInstance = DirectionalLightNodeWrapper.onCreateInstance(modelParent)
    elif e.Id == ID_POINT:
      objectInstance = PointLightNodeWrapper.onCreateInstance(modelParent)
    elif e.Id == ID_SPOT:
      objectInstance = SpotLightNodeWrapper.onCreateInstance(modelParent)
    
    if objectInstance != None:
      objectInstance.reparentTo(modelParent)
      objectInstance.enableEditmode() 
      modelController.selectObject(objectInstance)
      messenger.send(EVENT_SCENEGRAPH_REFRESH)
  
  def onChangeViewports(self, e):
    """Invoked when the user changes viewport layout."""
    self.Update()
    sashpos = self.splitter2.GetSashPosition()
    if e.Id == ID_SINGLE_VIEWPORT:
      if isinstance(self.view, Viewport): return
      self.view.close()
      self.view = Viewport.makePerspective(self.splitter2)
    elif e.Id == ID_4x4_GRID:
      if isinstance(self.view, ViewportGrid): return
      self.view.close()
      self.view = ViewportGrid(self.splitter2, [[Viewport.VPTOP,  Viewport.VPFRONT],
                                               [Viewport.VPLEFT, Viewport.VPPERSPECTIVE]])
      self.view.center()
    else:
      if e.Id == ID_2_HORIZONTAL: orientation = wx.SPLIT_HORIZONTAL
      elif e.Id == ID_2_VERTICAL: orientation = wx.SPLIT_VERTICAL
      else: return
      if isinstance(self.view, ViewportSplitter) and not isinstance(self.view, ViewportGrid):
        if self.view.GetSplitMode() == orientation: return
        self.view.close()
        self.view.split(Viewport.VPTOP, Viewport.VPPERSPECTIVE, orientation)
      else:
        self.view.close()
        self.view = ViewportSplitter(self.splitter2, Viewport.VPTOP, Viewport.VPPERSPECTIVE, orientation)
    self.splitter2.Unsplit()
    assert self.splitter2.SplitVertically(self.leftBarSplitter, self.view, sashpos)
    # Reload the menus
    collect()
    self.reloadViewportMenus()
    # Make sure the viewports are initialized correctly
    self.Update()
    ViewportManager.updateAll()
    self.wxStep()
    ViewportManager.initializeAll()
    messenger.send("window-event")
  
  def onToggleGrid(self, evt = None):
    """Toggles the grid on/off."""
    if evt.GetEventObject().IsChecked(ID_ENABLE_GRID):
      self.grid.enable()
    else:
      self.grid.disable()
  
  def onCenterTrackball(self, evt = None):
    """Center the trackball, like 'c' does in pview."""
    gbv = render.getBounds();
    # Determine the bounding sphere around the object.
    if gbv.isInfinite(): return
    if gbv.isEmpty(): return
    
    # The BoundingVolume might be a sphere (it's likely), but since it
    # might not, we'll take no chances and make our own sphere.
    sphere = BoundingSphere(gbv.getApproxCenter(), 0.0)
    if (not sphere.extendBy(gbv)): return
    
    radius = 50.0
    
    # Loop through the windows/viewports
    for w in WindowManager.windows:
      # Choose a suitable distance to view the whole volume in our frame.
      # This is based on the camera lens in use.
      fov = w.camLens.getFov();
      distance = radius / tan(deg2Rad(min(fov[0], fov[1]) / 2.0));
      
      # Ensure the far plane is far enough back to see the entire object.
      idealFarPlane = distance + radius * 1.5;
      w.camLens.setFar(max(w.camLens.getDefaultFar(), idealFarPlane));
      
      # And that the near plane is far enough forward.
      w.camLens.setNear(min(w.camLens.getDefaultNear(), radius - sphere.getRadius()))
      
      w.trackball.node().setOrigin(sphere.getCenter())
      w.trackball.node().setPos(Vec3.forward() * distance)
      
      # Also set the movement scale on the trackball to be consistent
      # with the size of the model and the lens field-of-view.
      w.trackball.node().setForwardScale(distance * 0.006)
Example #4
0
class EditorApp(AppShell):
    appversion = "cvs"
    appname = "Panda Editor"
    copyright = "Copyright (c) Carnegie Mellon University.\nAll rights reserved."
    contactname = "pro-rsoft"
    contactemail = "*****@*****.**"
    frameWidth = defWP.getXSize()
    frameHeight = defWP.getYSize()

    def __init__(self, editorInstance):
        """EditorApp constructor."""
        # Instance of the editor
        self.editorInstance = editorInstance

        # Create the Wx app
        self.wxApp = wx.App(redirect=False)
        self.wxApp.SetAppName("Panda Editor")
        self.wxApp.SetClassName("PEditor")

        self.modified = True
        self.filename = Filename()

        # Initialize the app shell and add some controls
        AppShell.__init__(self, title="Panda Editor", pos=origin)
        self.splitter1 = wx.SplitterWindow(self, style=wx.SP_3D | wx.SP_BORDER)
        self.splitter1.SetMinimumPaneSize(1)
        self.splitter2 = wx.SplitterWindow(self.splitter1,
                                           style=wx.SP_3D | wx.SP_BORDER)
        self.splitter2.SetMinimumPaneSize(1)
        self.leftBarSplitter = wx.SplitterWindow(self.splitter2,
                                                 style=wx.SP_3D | wx.SP_BORDER)
        self.leftBarSplitter.SetMinimumPaneSize(1)
        #self.rightBarSplitter = wx.SplitterWindow(self.splitter1, style = wx.SP_3D | wx.SP_BORDER)
        #self.rightBarSplitter.SetMinimumPaneSize(1)
        self.sceneGraphTree = SceneGraphTree(self.leftBarSplitter)
        self.propertyGrid = PropertyGrid(self.leftBarSplitter)
        self.textureManager = TextureManager(self.splitter1,
                                             style=wx.SP_3D | wx.SUNKEN_BORDER)
        self.view = Viewport.makePerspective(self.splitter2)
        sizer = wx.BoxSizer(wx.VERTICAL)
        assert self.leftBarSplitter.SplitHorizontally(self.sceneGraphTree,
                                                      self.propertyGrid)
        assert self.splitter2.SplitVertically(self.leftBarSplitter, self.view,
                                              200)
        #assert self.rightBarSplitter.SplitHorizontally(self.textureManager, None)
        assert self.splitter1.SplitVertically(self.splitter2,
                                              self.textureManager, -200)
        sizer.Add(self.splitter1, 1, wx.EXPAND, 0)
        self.splitter1.Unsplit()  # Yes, I know this looks odd.
        self.SetSizer(sizer)
        self.Layout()
        self.initialize()
        self.splitter2.SetSashPosition(200)

        # Setup some events
        base.accept("c", self.onCenterTrackball)

        base.accept(EVENT_MODELCONTROLLER_SELECTED_OBJECT_CHANGE,
                    self.onModelSelect)
        # If a model-translate-rotate-scale tool is selected the automatic mouse
        # movement has to be disable to prevent camera & object movement.
        # Hmm doesnt really work as well... (camera is still moved)
        base.accept(EVENT_MODELCONTROLLER_EDITTOOL_SELECTED,
                    lambda x=None: base.disableMouse())
        base.accept(EVENT_MODELCONTROLLER_EDITTOOL_DESELECTED,
                    lambda x=None: base.enableMouse())
        base.accept(EVENT_MODELCONTROLLER_FULL_REFRESH, self.__setattr__,
                    ["modified", True])
        # The object has been modified in the scene, this event happens every frame
        #base.accept(EVENT_MODELCONTROLLER_FAST_REFRESH, )
        # The editor has been disabled, collisions etc are deleted
        #base.accept(EDITOR_TOGGLE_OFF_EVENT, )
        # The editor has been enabled, collisions etc are created.
        # This event happens shortly after the wxgui has been created
        #base.accept(EDITOR_TOGGLE_ON_EVENT, )

    def appInit(self):
        print "I: EditorApp.appInit"
        """Overridden from WxAppShell.py."""
        # Create a new event loop (to overide default wxEventLoop)
        self.evtLoop = wx.EventLoop()
        self.oldLoop = wx.EventLoop.GetActive()
        wx.EventLoop.SetActive(self.evtLoop)
        taskMgr.add(self.wxStep, "evtLoopTask")

    def createMenuBar(self):
        """Overridden from WxAppShell.py."""
        # File menu
        self.menuFile = wx.Menu()
        self.menuBar.Append(self.menuFile, "&File")
        self.Bind(wx.EVT_MENU, self.onNew,
                  self.menuFile.Append(wx.ID_NEW, "&New"))
        self.Bind(wx.EVT_MENU, self.onOpen,
                  self.menuFile.Append(wx.ID_OPEN, "&Open"))
        self.Bind(wx.EVT_MENU, self.onSave,
                  self.menuFile.Append(wx.ID_SAVE, "&Save"))
        self.Bind(wx.EVT_MENU, self.onSaveAs,
                  self.menuFile.Append(wx.ID_SAVEAS, "Save &As..."))
        self.menuFile.AppendSeparator()
        self.Bind(wx.EVT_MENU, self.quit,
                  self.menuFile.Append(wx.ID_EXIT, "&Quit"))

        # Edit menu
        #self.menuEdit = wx.Menu()
        #self.menuBar.Append(self.menuEdit, "&Edit")
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_UNDO, "&Undo"))
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_REDO, "&Redo"))
        #self.menuEdit.AppendSeparator()
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_CUT, "Cu&t"))
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_COPY, "&Copy"))
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_PASTE, "&Paste"))
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_DELETE, "&Delete"))
        #self.menuEdit.AppendSeparator()
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_SELECTALL, "Select &All"))
        #self.Bind(wx.EVT_MENU, self.quit, self.menuEdit.Append(wx.ID_ANY, "Select &None"))

        # View menu
        self.menuView = wx.Menu()
        self.menuBar.Append(self.menuView, "&View")
        self.Bind(
            wx.EVT_MENU, self.onToggleGrid,
            self.menuView.AppendCheckItem(ID_ENABLE_GRID, "E&nable Grid"))
        self.Bind(wx.EVT_MENU, self.onCenterTrackball,
                  self.menuView.Append(wx.ID_ANY, "&Center Model"))
        self.menuBar.Check(ID_ENABLE_GRID, True)

        # Create menu
        self.menuCreate = wx.Menu()
        self.menuBar.Append(self.menuCreate, "&Create")
        self.Bind(wx.EVT_MENU, self.onCreateObject,
                  self.menuCreate.Append(ID_MODEL, "&Model..."))
        self.Bind(wx.EVT_MENU, self.onCreateObject,
                  self.menuCreate.Append(ID_TERRAIN, "&Terrain..."))

        self.menuCreateLight = wx.Menu()
        self.Bind(wx.EVT_MENU, self.onCreateObject,
                  self.menuCreateLight.Append(ID_AMBIENT, "&Ambient"))
        self.Bind(wx.EVT_MENU, self.onCreateObject,
                  self.menuCreateLight.Append(ID_DIRECTIONAL, "&Directional"))
        self.Bind(wx.EVT_MENU, self.onCreateObject,
                  self.menuCreateLight.Append(ID_POINT, "&Point"))
        self.Bind(wx.EVT_MENU, self.onCreateObject,
                  self.menuCreateLight.Append(ID_SPOT, "&Spotlight"))
        self.menuCreate.AppendSubMenu(self.menuCreateLight, "&Light")

        # Viewports menu
        self.menuViewports = wx.Menu()
        self.menuBar.Append(self.menuViewports, "View&ports")
        self.Bind(
            wx.EVT_MENU, self.onChangeViewports,
            self.menuViewports.AppendRadioItem(ID_SINGLE_VIEWPORT,
                                               "&Single Viewport"))
        self.Bind(wx.EVT_MENU, self.onChangeViewports,
                  self.menuViewports.AppendRadioItem(ID_4x4_GRID, "4x4 &Grid"))
        self.Bind(
            wx.EVT_MENU, self.onChangeViewports,
            self.menuViewports.AppendRadioItem(ID_2_HORIZONTAL,
                                               "2 &Horizontal"))
        self.Bind(
            wx.EVT_MENU, self.onChangeViewports,
            self.menuViewports.AppendRadioItem(ID_2_VERTICAL, "2 &Vertical"))
        self.menuBar.Check(ID_SINGLE_VIEWPORT, True)
        self.menuViewports.AppendSeparator()
        self.viewportMenus = []

    def createInterface(self):
        """Overridden from WxAppShell.py."""
        self.CreateStatusBar()
        self.SetStatusText("Welcome to the Panda3D Editor")
        self.Update()

    def initialize(self):
        """Initializes the viewports and editor."""
        print "I: EditorApp.initialize"
        self.Update()
        ViewportManager.updateAll()
        self.wxStep()
        ViewportManager.initializeAll()
        self.reloadViewportMenus()
        #self.editorInstance.toggle("WorldEditMode")
        # Position the camera
        if base.trackball != None:
            base.trackball.node().setPos(0, 30, 0)
            base.trackball.node().setHpr(0, 15, 0)

        # Load the direct things
        self.grid = DirectGrid(parent=render)
        self.sceneGraphTree.reload()
        if not isinstance(self.view, Viewport):
            self.view.center()

        # Initialize some stuff
        self.editorInstance.toggleEditmode(True)

    def reloadViewportMenus(self):
        """Reloads the viewport menus."""
        # Add the individual viewport menus
        for m in self.viewportMenus:
            m.Destroy()
        self.viewportMenus = []
        collect()
        #for v in range(len(ViewportManager.viewports)):
        #  self.viewportMenus.append(self.menuViewports.AppendSubMenu(ViewportMenu(ViewportManager.viewports[v]), "Viewport %d" % (v + 1)))

    def wxStep(self, task=None):
        """A step in the WX event loop. You can either call this yourself or use as task."""
        while self.evtLoop.Pending():
            self.evtLoop.Dispatch()
        self.wxApp.ProcessIdle()
        if task != None: return task.cont

    def onModelSelect(self, model):
        """Invoked when a model is selected. Shows/hides the texture panel."""
        if model == None and self.splitter1.IsSplit():
            self.splitter1.Unsplit()
        elif model != None and not self.splitter1.IsSplit():
            assert self.splitter1.SplitVertically(self.splitter2,
                                                  self.textureManager, -200)

    def onDestroy(self, evt):
        """Invoked when the window is destroyed."""
        wx.EventLoop.SetActive(self.oldLoop)

    def onNew(self, evt=None):
        self.filename = Filename()
        self.modified = True
        self.SetTitle("Panda Editor")
        self.editorInstance.destroyAllModels()

    def onOpen(self, evt=None):
        filter = "Panda3D Egg Scene Format (*.egs)|*.[eE][gG][sS];*.egs"
        #filter += "|Panda3D Compressed Egg Format (*.egg.pz)|*.[eE][gG][gG].[pP][zZ];*.egg.pz"
        #filter += "|Panda3D Binary Format (*.bam)|*.[bB][aA][mM];*.bam"
        #filter += "|Panda3D Compressed Binary Format (*.bam)|*.[bB][aA][mM].[pP][zZ];*.bam.pz"
        ''' # disabled by hypnos, making the loading work
    filter += "|MultiGen (*.flt)|*.[fF][lL][tT]"
    filter += "|Lightwave (*.lwo)|*.[lL][wW][oO]"
    filter += "|AutoCAD (*.dxf)|*.[dD][xX][fF]"
    filter += "|VRML (*.wrl)|*.[wW][rR][lL]"
    filter += "|DirectX (*.x)|*.[xX]"
    filter += "|COLLADA (*.dae)|*.[dD][aA][eE]" '''
        dlg = wx.FileDialog(self, "Load file", "", "", filter, wx.OPEN)
        try:
            if dlg.ShowModal() == wx.ID_OK:
                #self.filename = Filename.fromOsSpecific(dlg.GetPath())
                p3dFilename = Filename.fromOsSpecific(dlg.GetPath())
                self.filename = str(dlg.GetPath())
                self.SetTitle(p3dFilename.getBasename() + " - Panda Editor")
                self.modified = False
                self.editorInstance.loadEggModelsFile(self.filename)
                # Reset the camera
                if base.trackball != None:
                    base.trackball.node().setPos(0, 30, 0)
                    base.trackball.node().setHpr(0, 15, 0)
                self.onCenterTrackball()
                if p3dFilename.getExtension().lower() != "bam":
                    self.filename = Filename()
                    self.modified = True
                self.sceneGraphTree.reload()
        finally:
            dlg.Destroy()

    def onSave(self, evt=None):
        if not self.modified: return
        if self.filename == None or self.filename.empty():
            self.onSaveAs(evt)
        else:
            dlg = wx.FileDialog(
                self, "Save file", "", "",
                "Panda3D Binary Format (*.bam)|.[bB][aA][mM];*.bam",
                wx.SAVE | wx.FD_OVERWRITE_PROMPT)
            try:
                if dlg.ShowModal() == wx.ID_OK:
                    self.filename = Filename.fromOsSpecific(dlg.GetPath())
                    self.SetTitle(self.filename.getBasename() +
                                  " - Panda Editor")
                    self.modified = False
                    self.editorInstance.saveEggModelsFile(
                        self.filename.getFullpath())
            finally:
                dlg.Destroy()

    def onSaveAs(self, evt=None):
        dlg = wx.FileDialog(
            self, "Save file as", "", "",
            "Panda3D Binary Format (*.bam)|.[bB][aA][mM];*.bam",
            wx.SAVE | wx.FD_OVERWRITE_PROMPT)
        try:
            if dlg.ShowModal() == wx.ID_OK:
                self.onNew()
                self.filename = Filename.fromOsSpecific(dlg.GetPath())
                self.SetTitle(self.filename.getBasename() + " - Panda Editor")
                self.modified = False
                self.editorInstance.saveEggModelsFile(
                    self.filename.getFullpath())
        finally:
            dlg.Destroy()

    def onCreateObject(self, e):
        """Invoked when the user hits one of the buttons in the "Create" menu."""

        modelParent = modelController.getSelectedObject()
        if modelParent == None: modelParent = render
        objectInstance = None
        if e.Id == ID_NODEPATH:
            objectInstance = NodePathWrapper.onCreateInstance(modelParent)
        elif e.Id == ID_MODEL:
            filter = "Panda3D Egg Format (*.egg)|*.[eE][gG][gG];*.egg"
            filter += "|Panda3D Binary Format (*.bam)|*.[bB][aA][mM];*.bam"
            filter += "|MultiGen (*.flt)|*.[fF][lL][tT];*.flt"
            filter += "|Lightwave (*.lwo)|*.[lL][wW][oO];*.lwo"
            filter += "|AutoCAD (*.dxf)|*.[dD][xX][fF];*.dxf"
            filter += "|VRML (*.wrl)|*.[wW][rR][lL];*.wrl"
            filter += "|DirectX (*.x)|*.[xX];*.x"
            filter += "|COLLADA (*.dae)|*.[dD][aA][eE];*.dae"
            dlg = wx.FileDialog(self, "Select model", "", "", filter, wx.OPEN)
            try:
                if dlg.ShowModal() == wx.ID_OK:
                    objectInstance = NodePathWrapper.onCreateInstance(
                        modelParent,
                        Filename.fromOsSpecific(dlg.GetPath()).getFullpath())
            finally:
                dlg.Destroy()
        elif e.Id == ID_TERRAIN:
            filter = "Portable Network Graphics (*.png)|*.[pP][nN][gG];*.png"
            dlg = wx.FileDialog(self, "Select heightfield", "", "", filter,
                                wx.OPEN)
            try:
                if dlg.ShowModal() == wx.ID_OK:
                    objectInstance = GeoMipTerrainNodeWrapper.onCreateInstance(
                        modelParent,
                        Filename.fromOsSpecific(dlg.GetPath()).getFullpath())
            finally:
                dlg.Destroy()
        elif e.Id == ID_AMBIENT:
            objectInstance = AmbientLightNodeWrapper.onCreateInstance(
                modelParent)
        elif e.Id == ID_DIRECTIONAL:
            objectInstance = DirectionalLightNodeWrapper.onCreateInstance(
                modelParent)
        elif e.Id == ID_POINT:
            objectInstance = PointLightNodeWrapper.onCreateInstance(
                modelParent)
        elif e.Id == ID_SPOT:
            objectInstance = SpotLightNodeWrapper.onCreateInstance(modelParent)

        if objectInstance != None:
            objectInstance.reparentTo(modelParent)
            objectInstance.enableEditmode()
            modelController.selectObject(objectInstance)
            messenger.send(EVENT_SCENEGRAPH_REFRESH)

    def onChangeViewports(self, e):
        """Invoked when the user changes viewport layout."""
        self.Update()
        sashpos = self.splitter2.GetSashPosition()
        if e.Id == ID_SINGLE_VIEWPORT:
            if isinstance(self.view, Viewport): return
            self.view.close()
            self.view = Viewport.makePerspective(self.splitter2)
        elif e.Id == ID_4x4_GRID:
            if isinstance(self.view, ViewportGrid): return
            self.view.close()
            self.view = ViewportGrid(
                self.splitter2, [[Viewport.VPTOP, Viewport.VPFRONT],
                                 [Viewport.VPLEFT, Viewport.VPPERSPECTIVE]])
            self.view.center()
        else:
            if e.Id == ID_2_HORIZONTAL: orientation = wx.SPLIT_HORIZONTAL
            elif e.Id == ID_2_VERTICAL: orientation = wx.SPLIT_VERTICAL
            else: return
            if isinstance(self.view, ViewportSplitter) and not isinstance(
                    self.view, ViewportGrid):
                if self.view.GetSplitMode() == orientation: return
                self.view.close()
                self.view.split(Viewport.VPTOP, Viewport.VPPERSPECTIVE,
                                orientation)
            else:
                self.view.close()
                self.view = ViewportSplitter(self.splitter2, Viewport.VPTOP,
                                             Viewport.VPPERSPECTIVE,
                                             orientation)
        self.splitter2.Unsplit()
        assert self.splitter2.SplitVertically(self.leftBarSplitter, self.view,
                                              sashpos)
        # Reload the menus
        collect()
        self.reloadViewportMenus()
        # Make sure the viewports are initialized correctly
        self.Update()
        ViewportManager.updateAll()
        self.wxStep()
        ViewportManager.initializeAll()
        messenger.send("window-event")

    def onToggleGrid(self, evt=None):
        """Toggles the grid on/off."""
        if evt.GetEventObject().IsChecked(ID_ENABLE_GRID):
            self.grid.enable()
        else:
            self.grid.disable()

    def onCenterTrackball(self, evt=None):
        """Center the trackball, like 'c' does in pview."""
        gbv = render.getBounds()
        # Determine the bounding sphere around the object.
        if gbv.isInfinite(): return
        if gbv.isEmpty(): return

        # The BoundingVolume might be a sphere (it's likely), but since it
        # might not, we'll take no chances and make our own sphere.
        sphere = BoundingSphere(gbv.getApproxCenter(), 0.0)
        if (not sphere.extendBy(gbv)): return

        radius = 50.0

        # Loop through the windows/viewports
        for w in WindowManager.windows:
            # Choose a suitable distance to view the whole volume in our frame.
            # This is based on the camera lens in use.
            fov = w.camLens.getFov()
            distance = radius / tan(deg2Rad(min(fov[0], fov[1]) / 2.0))

            # Ensure the far plane is far enough back to see the entire object.
            idealFarPlane = distance + radius * 1.5
            w.camLens.setFar(max(w.camLens.getDefaultFar(), idealFarPlane))

            # And that the near plane is far enough forward.
            w.camLens.setNear(
                min(w.camLens.getDefaultNear(), radius - sphere.getRadius()))

            w.trackball.node().setOrigin(sphere.getCenter())
            w.trackball.node().setPos(Vec3.forward() * distance)

            # Also set the movement scale on the trackball to be consistent
            # with the size of the model and the lens field-of-view.
            w.trackball.node().setForwardScale(distance * 0.006)