From 821656be003c3c956566265d626859d533cbb092 Mon Sep 17 00:00:00 2001 From: Gyedo Jeon Date: Fri, 22 Jan 2010 00:11:18 +0000 Subject: [PATCH] Added [WIP] ProtoPalette --- direct/src/leveleditor/LevelEditorBase.py | 2 + direct/src/leveleditor/LevelEditorUI.py | 11 +++- direct/src/leveleditor/ObjectGlobals.py | 3 +- direct/src/leveleditor/ObjectHandler.py | 4 +- direct/src/leveleditor/ObjectMgr.py | 47 ++++++++++++-- direct/src/leveleditor/ObjectPalette.py | 1 + direct/src/leveleditor/ObjectPaletteBase.py | 11 ++-- direct/src/leveleditor/ObjectPropertyUI.py | 41 +++++++++++- direct/src/leveleditor/ProtoPalette.py | 58 +++++++++++++++++ direct/src/leveleditor/ProtoPaletteUI.py | 71 +++++++++++++++++++++ 10 files changed, 234 insertions(+), 15 deletions(-) create mode 100755 direct/src/leveleditor/ProtoPalette.py create mode 100755 direct/src/leveleditor/ProtoPaletteUI.py diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py index 2f3c4f82fc..1035edaec5 100755 --- a/direct/src/leveleditor/LevelEditorBase.py +++ b/direct/src/leveleditor/LevelEditorBase.py @@ -14,6 +14,7 @@ base = ShowBase(False) from ObjectMgr import * from FileMgr import * +from ProtoPalette import * class LevelEditorBase(DirectObject): """ Base Class for Panda3D LevelEditor """ @@ -23,6 +24,7 @@ class LevelEditorBase(DirectObject): self.actionEvents = [] self.objectMgr = ObjectMgr(self) self.fileMgr = FileMgr(self) + self.protoPalette = ProtoPalette() # define your own config file in inherited class self.settingsFile = None diff --git a/direct/src/leveleditor/LevelEditorUI.py b/direct/src/leveleditor/LevelEditorUI.py index 91fcdadfd4..2b863fde76 100755 --- a/direct/src/leveleditor/LevelEditorUI.py +++ b/direct/src/leveleditor/LevelEditorUI.py @@ -11,6 +11,7 @@ from ObjectPropertyUI import * from SceneGraphUI import * from LayerEditorUI import * from HotKeyUI import * +from ProtoPaletteUI import * class PandaTextDropTarget(wx.TextDropTarget): def __init__(self, editor): @@ -95,12 +96,15 @@ class LevelEditorUI(WxAppShell): self.perspView = Viewport.makePerspective(self.viewFrame) self.viewFrame.AppendWindow(self.perspView) - self.leftBarUpPane = wx.Panel(self.leftFrame) + self.leftBarUpFrame = wx.SplitterWindow(self.leftFrame, wx.SP_3D | wx.SP_BORDER) + self.leftBarUpPane = wx.Panel(self.leftBarUpFrame) + self.leftBarMidPane = wx.Panel(self.leftBarUpFrame) self.leftBarDownPane = wx.Panel(self.leftFrame) self.rightBarUpPane = wx.Panel(self.rightFrame) self.rightBarDownPane = wx.Panel(self.rightFrame) - self.leftFrame.SplitHorizontally(self.leftBarUpPane, self.leftBarDownPane) + self.leftFrame.SplitHorizontally(self.leftBarUpFrame, self.leftBarDownPane) + self.leftBarUpFrame.SplitHorizontally(self.leftBarUpPane, self.leftBarMidPane) self.rightFrame.SplitHorizontally(self.rightBarUpPane, self.rightBarDownPane) self.mainFrame.SplitVertically(self.leftFrame, self.baseFrame, 200) self.baseFrame.SplitVertically(self.viewFrame, self.rightFrame, 600) @@ -108,6 +112,7 @@ class LevelEditorUI(WxAppShell): self.viewFrame.SetDropTarget(PandaTextDropTarget(self.editor)) self.leftFrame.SetSashGravity(0.5) + self.leftBarUpFrame.SetSashGravity(0.5) self.rightFrame.SetSashGravity(0.5) self.baseFrame.SetSashGravity(1.0) @@ -116,6 +121,7 @@ class LevelEditorUI(WxAppShell): self.SetSizer(sizer); self.Layout() self.objectPaletteUI = ObjectPaletteUI(self.leftBarUpPane, self.editor) + self.protoPaletteUI = ProtoPaletteUI(self.leftBarMidPane, self.editor) self.objectPropertyUI = ObjectPropertyUI(self.rightBarUpPane, self.editor) self.sceneGraphUI = SceneGraphUI(self.leftBarDownPane, self.editor) self.layerEditorUI = LayerEditorUI(self.rightBarDownPane, self.editor) @@ -190,6 +196,7 @@ class LevelEditorUI(WxAppShell): self.sceneGraphUI.showPandaObjectChildren() def onDestroy(self, evt): + self.editor.protoPalette.saveToFile() self.editor.saveSettings() def updateGrids(self, newSize, newSpacing): diff --git a/direct/src/leveleditor/ObjectGlobals.py b/direct/src/leveleditor/ObjectGlobals.py index 4d73293575..ff82e245c2 100755 --- a/direct/src/leveleditor/ObjectGlobals.py +++ b/direct/src/leveleditor/ObjectGlobals.py @@ -7,7 +7,8 @@ OBJ_UID = 0 OBJ_NP = 1 OBJ_DEF = 2 OBJ_MODEL = 3 -OBJ_PROP = 4 +OBJ_ANIM = 4 +OBJ_PROP = 5 # supported UI types PROP_UI_ENTRY = '_PropUIEntry' diff --git a/direct/src/leveleditor/ObjectHandler.py b/direct/src/leveleditor/ObjectHandler.py index b9b7f0be48..25c569b4c8 100755 --- a/direct/src/leveleditor/ObjectHandler.py +++ b/direct/src/leveleditor/ObjectHandler.py @@ -68,8 +68,8 @@ class ObjectHandler: class PandaActor(Actor.Actor): def __init__(self): - Actor.Actor.__init__(self, "models/panda-model.egg",{"walk":"models/panda-walk4.egg"}) + Actor.Actor.__init__(self, "models/panda-model.egg") self.setScale(0.005) - self.loop("walk") + diff --git a/direct/src/leveleditor/ObjectMgr.py b/direct/src/leveleditor/ObjectMgr.py index 81decc03e1..45f256ac15 100755 --- a/direct/src/leveleditor/ObjectMgr.py +++ b/direct/src/leveleditor/ObjectMgr.py @@ -5,6 +5,7 @@ Defines ObjectMgr import os, time, wx from direct.task import Task +from direct.actor.Actor import Actor from pandac.PandaModules import * import ObjectGlobals as OG @@ -63,6 +64,8 @@ class ObjectMgr: parent = render objDef = self.editor.objectPalette.findItem(typeName) + if objDef is None: + objDef = self.editor.protoPalette.findItem(typeName) newobj = None if objDef and type(objDef) != dict: if objDef.createFunction: @@ -78,12 +81,29 @@ class ObjectMgr: # create new obj using function and keyword arguments defined in ObjectPalette newobj = func(**funcArgs) - + elif objDef.actor: + if model is None: + model = objDef.model + try: + newobj = Actor(model) + except: + newobj = Actor(Filename.fromOsSpecific(model).getFullpath()) elif objDef.model is not None: # since this obj is simple model let's load the model if model is None: model = objDef.model - newobj = loader.loadModel(model) + try: + newobj = loader.loadModel(model) + except: + newobj = loader.loadModel(Filename.fromOsSpecific(model).getFullpath()) + + anim = '' + if len(objDef.anims) > 0: + anim = objDef.anims[0] + # load new anim + animName = os.path.basename(anim) + newAnim = newobj.loadAnims({animName:anim}) + newobj.loop(animName) if newobj is None: return None @@ -102,7 +122,7 @@ class ObjectMgr: properties[key] = objDef.properties[key][OG.PROP_DEFAULT] # insert obj data to main repository - self.objects[uid] = [uid, newobj, objDef, model, properties] + self.objects[uid] = [uid, newobj, objDef, model, anim, properties] self.npIndex[NodePath(newobj)] = uid if fSelectObject: @@ -269,7 +289,20 @@ class ObjectMgr: if fSelectObject: base.direct.select(newobj) - + + def updateObjectAnim(self, anim, obj, fSelectObject=True): + """ replace object's anim """ + if obj[OG.OBJ_ANIM] != anim: + base.direct.deselectAll() + objNP = obj[OG.OBJ_NP] + + # load new anim + animName = os.path.basename(anim) + newAnim = objNP.loadAnims({animName:anim}) + objNP.loop(animName) + obj[OG.OBJ_ANIM] = anim + if fSelectObject: + base.direct.select(objNP) def updateObjectModelFromUI(self, event, obj): """ replace object's model with one selected from UI """ @@ -277,6 +310,12 @@ class ObjectMgr: if model is not None: self.updateObjectModel(model, obj) + def updateObjectAnimFromUI(self, event, obj): + """ replace object's anim with one selected from UI """ + anim = event.GetString() + if anim is not None: + self.updateObjectAnim(anim, obj) + def updateObjectProperty(self, event, obj, propName): """ When an obj's property is updated in UI, diff --git a/direct/src/leveleditor/ObjectPalette.py b/direct/src/leveleditor/ObjectPalette.py index 4b61cec981..d7b1a0e7d6 100755 --- a/direct/src/leveleditor/ObjectPalette.py +++ b/direct/src/leveleditor/ObjectPalette.py @@ -101,6 +101,7 @@ class ObjectPalette(ObjectPaletteBase): self.add('Animal') self.add(ObjectBase(name='Panda', createFunction = ('.createPanda', {}), + anims = ['models/panda-walk4.egg',], properties = {}), 'Animal') diff --git a/direct/src/leveleditor/ObjectPaletteBase.py b/direct/src/leveleditor/ObjectPaletteBase.py index 81c05e4a59..7dd5f91387 100755 --- a/direct/src/leveleditor/ObjectPaletteBase.py +++ b/direct/src/leveleditor/ObjectPaletteBase.py @@ -1,16 +1,19 @@ +import copy import ObjectGlobals as OG class ObjectBase: """ Base class for obj definitions """ - def __init__(self, name='', createFunction = None, model = None, models= [], properties={}, - movable = True): + def __init__(self, name='', createFunction = None, model = None, models= [], anims = [], properties={}, + movable = True, actor = False): self.name = name self.createFunction = createFunction self.model = model - self.models = models - self.properties = properties + self.models = models[:] + self.anims = anims[:] + self.properties = copy.deepcopy(properties) self.movable = movable + self.actor = actor class ObjectPaletteBase: """ diff --git a/direct/src/leveleditor/ObjectPropertyUI.py b/direct/src/leveleditor/ObjectPropertyUI.py index 5221e8c0ba..e9a89f80e6 100755 --- a/direct/src/leveleditor/ObjectPropertyUI.py +++ b/direct/src/leveleditor/ObjectPropertyUI.py @@ -2,13 +2,40 @@ UI for object property control """ import wx +import os + from wx.lib.scrolledpanel import ScrolledPanel - from direct.wxwidgets.WxSlider import * - +from pandac.PandaModules import * import ObjectGlobals as OG +class AnimFileDrop(wx.FileDropTarget): + def __init__(self, editor): + wx.FileDropTarget.__init__(self) + self.editor = editor + def OnDropFiles(self, x, y, filenames): + obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last) + if obj is None: + return + + objDef = obj[OG.OBJ_DEF] + if not objDef.actor: + return + + objNP = obj[OG.OBJ_NP] + + for filename in filenames: + name = os.path.basename(filename) + animName = Filename.fromOsSpecific(filename).getFullpath() + if animName not in objDef.anims: + objDef.anims.append(animName) + + objNP.loadAnims({name:animName}) + objNP.loop(name) + obj[OG.OBJ_ANIM] = animName + self.editor.ui.objectPropertyUI.updateProps(obj) + class ObjectPropUI(wx.Panel): """ Base class for ObjectPropUIs, @@ -124,6 +151,8 @@ class ObjectPropertyUI(ScrolledPanel): parentSizer.Add(self, 1, wx.EXPAND, 0) parent.SetSizer(parentSizer); parent.Layout() + self.SetDropTarget(AnimFileDrop(self.editor)) + def clearPropUI(self): sizer = self.GetSizer() if sizer is not None: @@ -175,6 +204,14 @@ class ObjectPropertyUI(ScrolledPanel): self.editor.objectMgr.onLeaveObjectPropUI, lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModelFromUI(p0, p1)) + if len(objDef.anims) > 0: + propUI = ObjectPropUICombo(self.propPane, 'anim', obj[OG.OBJ_ANIM], objDef.anims) + sizer.Add(propUI) + + propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI, + self.editor.objectMgr.onLeaveObjectPropUI, + lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectAnimFromUI(p0, p1)) + for key in objDef.properties.keys(): propDef = objDef.properties[key] propType = propDef[OG.PROP_TYPE] diff --git a/direct/src/leveleditor/ProtoPalette.py b/direct/src/leveleditor/ProtoPalette.py new file mode 100755 index 0000000000..ad49a991fb --- /dev/null +++ b/direct/src/leveleditor/ProtoPalette.py @@ -0,0 +1,58 @@ +""" +Palette for Prototyping +""" +import os +import imp +import types + +from ObjectPaletteBase import * + +class ProtoPalette(ObjectPaletteBase): + def __init__(self): + ObjectPaletteBase.__init__(self) + + def addItems(self, protoData, parent=None): + if type(protoData) == types.DictType: + for key in protoData.keys(): + if type(protoData[key]) == types.DictType: + self.add(key, parent) + self.addItems(protoData[key], key) + else: + self.add(protoData[key], parent) + + def populate(self): + dirname = os.path.dirname(__file__) + moduleName = 'protoPaletteData' + try: + file, pathname, description = imp.find_module(moduleName, [dirname]) + module = imp.load_module(moduleName, file, pathname, description) + except: + print "protoPaletteData doesn't exist" + return + + self.addItems(module.protoData) + + def saveProtoData(self, f, protoData, depth): + tab = ' '*4*depth + if not f: + return + + for key in protoData.keys(): + f.write("%s'%s' : "%(tab, key)) + if type(protoData[key]) == types.DictType: + f.write("{\n") + self.saveProtoData(f, protoData[key], depth + 1) + f.write("%s},\n"%tab) + else: + f.write("ObjectBase(name='%s', model='%s', anims=%s, actor=True),\n"%(protoData[key].name, protoData[key].model, protoData[key].anims)) + + def saveToFile(self): + try: + f = open(os.path.dirname(__file__) + '/protoPaletteData.py', 'w') + f.write("from direct.leveleditor.ObjectPaletteBase import *\n\n") + f.write("protoData = {\n") + self.saveProtoData(f, self.data, 1) + f.write("}\n") + f.close() + except: + pass diff --git a/direct/src/leveleditor/ProtoPaletteUI.py b/direct/src/leveleditor/ProtoPaletteUI.py new file mode 100755 index 0000000000..4212eb5dc3 --- /dev/null +++ b/direct/src/leveleditor/ProtoPaletteUI.py @@ -0,0 +1,71 @@ +""" +Defines ProtoPalette tree UI +""" +import wx +import os +import cPickle as pickl +from pandac.PandaModules import * +from ObjectPaletteBase import * + +class FileDrop(wx.FileDropTarget): + def __init__(self, editor): + wx.FileDropTarget.__init__(self) + self.editor = editor + + def OnDropFiles(self, x, y, filenames): + for filename in filenames: + name = os.path.basename(filename) + modelname = Filename.fromOsSpecific(filename).getFullpath() + itemData = ObjectBase(name=name, model=modelname, actor=True) + base.le.protoPalette.add(itemData) + newItem = self.editor.ui.protoPaletteUI.tree.AppendItem(self.editor.ui.protoPaletteUI.root, name) + self.editor.ui.protoPaletteUI.tree.SetItemPyData(newItem, itemData) + self.editor.ui.protoPaletteUI.tree.ScrollTo(newItem) + +class ProtoPaletteUI(wx.Panel): + def __init__(self, parent, editor): + wx.Panel.__init__(self, parent) + + self.editor = editor + self.palette = self.editor.protoPalette + self.tree = wx.TreeCtrl(self) + self.root = self.tree.AddRoot('Proto Objects') + self.addTreeNodes(self.root, self.palette.data) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self.tree, 1, wx.EXPAND, 0) + self.SetSizer(sizer); self.Layout() + + parentSizer = wx.BoxSizer(wx.VERTICAL) + parentSizer.Add(self, 1, wx.EXPAND, 0) + parent.SetSizer(parentSizer); parent.Layout() + + self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.onSelected) + self.tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self.onBeginDrag) + + self.SetDropTarget(FileDrop(self.editor)) + + def addTreeNodes(self, parentItem, items): + for key in items.keys(): + newItem = self.tree.AppendItem(parentItem, key) + if type(items[key]) == dict: + self.addTreeNodes(newItem, items[key]) + else: + self.tree.SetItemPyData(newItem, items[key]) + + def onSelected(self, event): + data = self.tree.GetItemPyData(event.GetItem()) + if data: + print data.properties + + def onBeginDrag(self, event): + item = event.GetItem() + + if item != self.tree.GetRootItem(): # prevent dragging root item + text = self.tree.GetItemText(item) + print "Starting drag'n'drop with %s..." % repr(text) + + tdo = wx.TextDataObject(text) + tds = wx.DropSource(self.tree) + tds.SetData(tdo) + tds.DoDragDrop(True)