mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 02:15:43 -04:00
Added object duplication and grid snapping
This commit is contained in:
parent
d1d8685fe5
commit
47b9e90b4c
@ -19,6 +19,7 @@ class LevelEditorBase(DirectObject):
|
||||
""" Base Class for Panda3D LevelEditor """
|
||||
def __init__(self):
|
||||
#loadPrcFileData('startup', 'window-type none')
|
||||
self.currentFile = None
|
||||
self.actionEvents = []
|
||||
self.objectMgr = ObjectMgr(self)
|
||||
self.fileMgr = FileMgr(self)
|
||||
@ -130,9 +131,14 @@ class LevelEditorBase(DirectObject):
|
||||
base.direct.deselectAll()
|
||||
self.objectMgr.reset()
|
||||
|
||||
def save(self, fileName):
|
||||
def save(self):
|
||||
if self.currentFile:
|
||||
self.fileMgr.saveToFile(self.currentFile)
|
||||
|
||||
def saveAs(self, fileName):
|
||||
self.fileMgr.saveToFile(fileName)
|
||||
|
||||
def load(self, fileName):
|
||||
self.reset()
|
||||
self.fileMgr.loadFromFile(fileName)
|
||||
self.currentFile = fileName
|
||||
|
@ -48,6 +48,18 @@ class LevelEditorUI(WxAppShell):
|
||||
|
||||
menuItem = self.menuFile.Insert(2, -1 , "&Save")
|
||||
self.Bind(wx.EVT_MENU, self.onSave, menuItem)
|
||||
|
||||
menuItem = self.menuFile.Insert(3, -1 , "Save &As")
|
||||
self.Bind(wx.EVT_MENU, self.onSaveAs, menuItem)
|
||||
|
||||
self.menuEdit = wx.Menu()
|
||||
self.menuBar.Insert(1, self.menuEdit, "&Edit")
|
||||
|
||||
menuItem = self.menuEdit.Append(-1, "&Duplicate")
|
||||
self.Bind(wx.EVT_MENU, self.onDuplicate, menuItem)
|
||||
|
||||
self.gridSnapMenuItem = self.menuEdit.Append(-1, "&Grid Snap", kind = wx.ITEM_CHECK)
|
||||
self.Bind(wx.EVT_MENU, self.toggleGridSnap, self.gridSnapMenuItem)
|
||||
|
||||
def createInterface(self):
|
||||
self.createMenu()
|
||||
@ -132,7 +144,22 @@ class LevelEditorUI(WxAppShell):
|
||||
dialog.Destroy()
|
||||
|
||||
def onSave(self, evt):
|
||||
if self.editor.currentFile is None:
|
||||
self.onSaveAs(evt)
|
||||
else:
|
||||
self.editor.save()
|
||||
|
||||
def onSaveAs(self, evt):
|
||||
dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.SAVE)
|
||||
if dialog.ShowModal() == wx.ID_OK:
|
||||
self.editor.save(dialog.GetPath())
|
||||
self.editor.saveAs(dialog.GetPath())
|
||||
dialog.Destroy()
|
||||
|
||||
def onDuplicate(self, evt):
|
||||
self.editor.objectMgr.duplicateSelected()
|
||||
|
||||
def toggleGridSnap(self, evt):
|
||||
if self.gridSnapMenuItem.IsChecked():
|
||||
base.direct.manipulationControl.fGridSnap = 1
|
||||
else:
|
||||
base.direct.manipulationControl.fGridSnap = 0
|
||||
|
@ -56,8 +56,6 @@ class ObjectHandler:
|
||||
b.reparentTo(objNP)
|
||||
a.removeNode()
|
||||
|
||||
base.direct.select(objNP)
|
||||
|
||||
def createPanda(self):
|
||||
pandaActor = PandaActor()
|
||||
return pandaActor
|
||||
|
@ -57,7 +57,7 @@ class ObjectMgr:
|
||||
self.lastUidMod = 0
|
||||
return newUid
|
||||
|
||||
def addNewObject(self, typeName, uid = None, model = None, parent=None):
|
||||
def addNewObject(self, typeName, uid = None, model = None, parent=None, fSelectObject=True):
|
||||
""" function to add new obj to the scene """
|
||||
if parent is None:
|
||||
parent = render
|
||||
@ -93,9 +93,8 @@ class ObjectMgr:
|
||||
|
||||
if uid is None:
|
||||
uid = self.genUniqueId()
|
||||
isLoadingArea = False
|
||||
else:
|
||||
isLoadingArea = True
|
||||
fSelectObject = False
|
||||
|
||||
# populate obj data using default values
|
||||
properties = {}
|
||||
@ -106,7 +105,7 @@ class ObjectMgr:
|
||||
self.objects[uid] = [uid, newobj, objDef, model, properties]
|
||||
self.npIndex[NodePath(newobj)] = uid
|
||||
|
||||
if not isLoadingArea:
|
||||
if fSelectObject:
|
||||
base.direct.select(newobj)
|
||||
|
||||
return newobj
|
||||
@ -235,12 +234,8 @@ class ObjectMgr:
|
||||
np.setSy(float(self.editor.ui.objectPropertyUI.propSY.getValue()))
|
||||
np.setSz(float(self.editor.ui.objectPropertyUI.propSZ.getValue()))
|
||||
|
||||
def updateObjectModel(self, event, obj):
|
||||
def updateObjectModel(self, model, obj, fSelectObject=True):
|
||||
""" replace object's model """
|
||||
model = event.GetString()
|
||||
if model is None:
|
||||
return
|
||||
|
||||
if obj[OG.OBJ_MODEL] != model:
|
||||
base.direct.deselectAll()
|
||||
|
||||
@ -269,8 +264,16 @@ class ObjectMgr:
|
||||
obj[OG.OBJ_NP] = newobj
|
||||
obj[OG.OBJ_MODEL] = model
|
||||
self.npIndex[NodePath(newobj)] = obj[OG.OBJ_UID]
|
||||
|
||||
base.direct.select(newobj)
|
||||
|
||||
if fSelectObject:
|
||||
base.direct.select(newobj)
|
||||
|
||||
|
||||
def updateObjectModelFromUI(self, event, obj):
|
||||
""" replace object's model with one selected from UI """
|
||||
model = event.GetString()
|
||||
if model is not None:
|
||||
self.updateObjectModel(model, obj)
|
||||
|
||||
def updateObjectProperty(self, event, obj, propName):
|
||||
"""
|
||||
@ -329,7 +332,7 @@ class ObjectMgr:
|
||||
# now update object prop value and call update function
|
||||
self.updateObjectPropValue(obj, propName, val)
|
||||
|
||||
def updateObjectPropValue(self, obj, propName, val):
|
||||
def updateObjectPropValue(self, obj, propName, val, fSelectObject=True):
|
||||
"""
|
||||
Update object property value and
|
||||
call update function if defined.
|
||||
@ -368,6 +371,9 @@ class ObjectMgr:
|
||||
# finally call update function
|
||||
func(**kwargs)
|
||||
|
||||
if fSelectObject:
|
||||
base.direct.select(obj[OG.OBJ_NP])
|
||||
|
||||
def updateObjectProperties(self, nodePath, propValues):
|
||||
"""
|
||||
When a saved level is loaded,
|
||||
@ -417,5 +423,51 @@ class ObjectMgr:
|
||||
self.saveData = []
|
||||
self.traverse(render)
|
||||
return self.saveData
|
||||
|
||||
def duplicateObject(self, nodePath, parent=None):
|
||||
obj = self.findObjectByNodePath(nodePath)
|
||||
if obj is None:
|
||||
return None
|
||||
objDef = obj[OG.OBJ_DEF]
|
||||
if parent is None:
|
||||
parent = nodePath.getParent()
|
||||
|
||||
newObjNP = self.addNewObject(objDef.name, parent=parent, fSelectObject = False)
|
||||
|
||||
# copy transform data
|
||||
newObjNP.setPos(obj[OG.OBJ_NP].getPos())
|
||||
newObjNP.setHpr(obj[OG.OBJ_NP].getHpr())
|
||||
newObjNP.setScale(obj[OG.OBJ_NP].getScale())
|
||||
|
||||
newObj = self.findObjectByNodePath(NodePath(newObjNP))
|
||||
if newObj is None:
|
||||
return None
|
||||
# copy model info
|
||||
self.updateObjectModel(obj[OG.OBJ_MODEL], newObj, fSelectObject=False)
|
||||
|
||||
# copy other properties
|
||||
for key in obj[OG.OBJ_PROP]:
|
||||
self.updateObjectPropValue(newObj, key, obj[OG.OBJ_PROP][key], fSelectObject=False)
|
||||
|
||||
return newObjNP
|
||||
|
||||
def duplicateChild(self, nodePath, parent):
|
||||
children = nodePath.findAllMatches('=OBJRoot')
|
||||
for childNP in children:
|
||||
newChildObjNP = self.duplicateObject(childNP, parent)
|
||||
if newChildObjNP is not None:
|
||||
self.duplicateChild(childNP, newChildObjNP)
|
||||
|
||||
|
||||
def duplicateSelected(self):
|
||||
selectedNPs = base.direct.selected.getSelectedAsList()
|
||||
duplicatedNPs = []
|
||||
for nodePath in selectedNPs:
|
||||
newObjNP = self.duplicateObject(nodePath)
|
||||
if newObjNP is not None:
|
||||
self.duplicateChild(nodePath, newObjNP)
|
||||
duplicatedNPs.append(newObjNP)
|
||||
|
||||
base.direct.deselectAll()
|
||||
print duplicatedNPs
|
||||
for newNodePath in duplicatedNPs:
|
||||
base.direct.select(newNodePath, fMultiSelect = 1)
|
||||
|
@ -173,7 +173,7 @@ class ObjectPropertyUI(ScrolledPanel):
|
||||
|
||||
propUI.bindFunc(self.editor.objectMgr.onEnterObjectPropUI,
|
||||
self.editor.objectMgr.onLeaveObjectPropUI,
|
||||
lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModel(p0, p1))
|
||||
lambda p0=None, p1=obj: self.editor.objectMgr.updateObjectModelFromUI(p0, p1))
|
||||
|
||||
for key in objDef.properties.keys():
|
||||
propDef = objDef.properties[key]
|
||||
|
Loading…
x
Reference in New Issue
Block a user