Added curve editing feature

This commit is contained in:
Shuying Feng 2010-06-17 00:22:35 +00:00
parent 9d3724a40b
commit be8dcd75cb
8 changed files with 419 additions and 17 deletions

View File

@ -199,7 +199,7 @@ class DirectSession(DirectObject):
keyList.extend(map(chr, range(48, 58)))
keyList.extend(["`", "-", "=", "[", "]", ";", "'", ",", ".", "/", "\\"])
self.specialKeys = ['escape', 'delete', 'page_up', 'page_down']
self.specialKeys = ['escape', 'delete', 'page_up', 'page_down', 'enter']
def addCtrl(a):
return "control-%s"%a
@ -286,6 +286,10 @@ class DirectSession(DirectObject):
'control-q': ('Quit', 'LE-Quit'),
}
self.speicalKeyMap = {
'enter': 'DIRECT-enter',
}
self.passThroughKeys = ['v','b','l','p', 'r', 'shift-r', 's', 't','shift-a', 'w']
if base.wantTk:
@ -555,6 +559,8 @@ class DirectSession(DirectObject):
if input in self.hotKeyMap.keys():
keyDesc = self.hotKeyMap[input]
messenger.send(keyDesc[1])
elif input in self.speicalKeyMap.keys():
messenger.send(self.speicalKeyMap[input])
elif input in self.directOnlyKeyMap.keys():
if self.fIgnoreDirectOnlyKeyMap:
return

View File

@ -0,0 +1,145 @@
"""
This is the module for curve edit
"""
from pandac.PandaModules import *
from direct.showbase.DirectObject import *
from direct.directtools.DirectSelection import SelectionRay
from direct.showutil.Rope import Rope
from ActionMgr import *
from direct.task import Task
import ObjectGlobals as OG
class CurveEditor(DirectObject):
""" CurveEditor will create and edit the curve """
def __init__(self, editor):
self.editor = editor
self.i = 0
self.ropeNum = 0
self.curve = []
self.curveControl = []
self.currentRope = None
self.degree = 3
def createCurve(self):
if self.editor.mode == self.editor.CREATE_CURVE_MODE:
self.view = self.editor.ui.currentView
#Get the mouse position
x = base.direct.dr.mouseX
y = base.direct.dr.mouseY
if self.editor.fMoveCamera == False and self.view != None:
self.createControler(x,y)
if self.currentRope != None:
self.currentRope.detachNode()
self.ropeUpdate(self.curve)
self.accept("DIRECT-enter", self.onBaseMode)
self.accept("DIRECT-enter", self.onBaseMode)
def editCurve(self, task):
if self.editor.mode == self.editor.EDIT_CURVE_MODE:
if self.editor.fMoveCamera == False:
self.selected = None
self.selected = base.direct.selected.last
if self.selected != None:
for item in self.curveControl:
if item[1] == self.selected:
self.point = item #temporarily save the controler information for further use
self.currentCurve = self.currentRope.ropeNode.getCurve()
self.currentCurve.setVertex(item[0], self.selected.getPos())
self.accept("DIRECT-delete", self.onControlerDelete)
return task.cont
def onControlerDelete(self):
if self.editor.mode == self.editor.EDIT_CURVE_MODE:
self.curve.remove(self.curve[self.point[0]])
#reset the controller list
for item in self.curveControl:
if item[0] > self.point[0]:
newname = 'controler%d' % (item[0]-1)
item[1].setName(newname)
self.curveControl[item[0]] = (item[0]-1, item[1])
self.curveControl.remove(self.point)
self.currentRope.setup(self.degree,self.curve)
def ropeUpdate(self, curve):
self.currentRope = Rope()
self.currentRope.setup(self.degree, curve)
self.currentRope.reparentTo(render)
def onBaseMode(self):
self.editor.preMode = self.editor.mode
self.editor.mode = self.editor.BASE_MODE
self.editor.ui.editCurveMenuItem.Check(False)
self.editor.ui.createCurveMenuItem.Check(False)
self.i = 0
for item in self.curveControl:
item[1].hide()
if self.editor.preMode == self.editor.BASE_MODE :
pass
if self.editor.preMode == self.editor.CREATE_CURVE_MODE :
self.updateScene()
if self.editor.preMode == self.editor.EDIT_CURVE_MODE :
self.doneEdit()
self.curveControl = []
self.curve = []
self.currentRope = None
base.direct.manipulationControl.enableManipulation()
self.editor.ui.createCurveMenuItem.Check(False)
self.editor.ui.editCurveMenuItem.Check(False)
def updateScene(self):
curveObjNP = self.editor.objectMgr.addNewCurve(self.curveControl, self.degree, nodePath=self.currentRope)
curveObj = self.editor.objectMgr.findObjectByNodePath(curveObjNP)
for item in self.curveControl:
item[1].reparentTo(curveObjNP)
self.editor.objectMgr.updateObjectPropValue(curveObj, 'Degree', self.degree, fSelectObject=False, fUndo=False)
def doneEdit(self):
base.direct.selected.last = None
def createControler(self, x, y):
if self.view != None:
self.controler = render.attachNewNode("controler")
self.controler = loader.loadModel('models/misc/smiley')
controlerPathname = 'controler%d' % self.i
self.controler.setName(controlerPathname)
self.controler.setColor(0, 0, 0, 1)
self.controler.setScale(0.2)
self.controler.reparentTo(render)
self.controler.setTag('OBJRoot','1')
self.controler.setTag('Controller','1') #controller Tag
self.i += 1
iRay = SelectionRay(self.view.camera)
iRay.collider.setFromLens(self.view.camNode, x, y)
iRay.collideWithBitMask(BitMask32.bit(21))
iRay.ct.traverse(self.view.collPlane)
if iRay.getNumEntries() > 0:
entry = iRay.getEntry(0)
hitPt = entry.getSurfacePoint(entry.getFromNodePath())
if hitPt:
# create a temp nodePath to get the position
np = NodePath('temp')
np.setPos(self.view.camera, hitPt)
if base.direct.manipulationControl.fGridSnap:
snappedPos = self.view.grid.computeSnapPoint(np.getPos())
np.setPos(snappedPos)
# update temp nodePath's HPR and scale with newobj's
np.setHpr(self.controler.getHpr())
np.setScale(self.controler.getScale())
# transform newobj to cursor position
self.controler.setMat(Mat4(np.getMat()))
np.remove()
iRay.collisionNodePath.removeNode()
del iRay
self.curve.append((None, self.controler.getPos()))
self.curveControl.append((self.i-1, self.controler))

View File

@ -5,11 +5,11 @@ LevelEditor, ObjectHandler, ObjectPalette should be rewritten
to be game specific.
"""
from LevelEditorUI import *
from LevelEditorBase import *
from ObjectMgr import *
from ObjectHandler import *
from ObjectPalette import *
from LevelEditorUI import *
from ProtoPalette import *
class LevelEditor(LevelEditorBase):

View File

@ -9,6 +9,7 @@ from direct.showbase.DirectObject import *
from direct.directtools.DirectUtil import *
from direct.gui.DirectGui import *
from CurveEditor import *
from FileMgr import *
from ActionMgr import *
from MayaConverter import *
@ -21,6 +22,7 @@ class LevelEditorBase(DirectObject):
self.fNeedToSave = False
self.actionEvents = []
#self.objectMgr = ObjectMgr(self)
self.curveEditor = CurveEditor(self)
self.fileMgr = FileMgr(self)
self.actionMgr = ActionMgr()
@ -32,7 +34,12 @@ class LevelEditorBase(DirectObject):
self.settingsFile = None
# you can show/hide specific properties by using propertiesMask and this mode
self.mode = BitMask32()
self.BASE_MODE = BitMask32.bit(0)
self.CREATE_CURVE_MODE = BitMask32.bit(2)
self.EDIT_CURVE_MODE = BitMask32.bit(3)
self.mode = self.BASE_MODE
self.preMode = None
def initialize(self):
""" You should call this in your __init__ method of inherited LevelEditor class """
@ -122,10 +129,14 @@ class LevelEditorBase(DirectObject):
if base.direct.fAlt or modifiers == 4:
self.fMoveCamera = True
return
if self.mode == self.CREATE_CURVE_MODE :
self.curveEditor.createCurve()
def handleMouse1Up(self):
def handleMouse1Up(self):
self.fMoveCamera = False
def handleMouse2(self, modifiers):
if base.direct.fAlt or modifiers == 4:
self.fMoveCamera = True
@ -138,7 +149,7 @@ class LevelEditorBase(DirectObject):
if base.direct.fAlt or modifiers == 4:
self.fMoveCamera = True
return
self.ui.onRightDown()
def handleMouse3Up(self):
@ -209,7 +220,11 @@ class LevelEditorBase(DirectObject):
self.ui.sceneGraphUI.deSelect(obj[OG.OBJ_UID])
self.objectMgr.selectObject(nodePath, fLEPane)
self.ui.buildContextMenu(nodePath)
if self.mode == self.EDIT_CURVE_MODE:
taskMgr.add(self.curveEditor.editCurve, "modify")
self.curveEditor.accept("DIRECT-enter", self.curveEditor.onBaseMode)
def deselectAll(self, np=None):
if len(base.direct.selected.getSelectedAsList()) ==0:
return

View File

@ -29,6 +29,7 @@ class PandaTextDropTarget(wx.TextDropTarget):
action = ActionAddNewObj(self.editor, text, parent=parentNPRef[0])
self.editor.actionMgr.push(action)
newobj = action()
print newobj
if newobj is None:
return
@ -114,6 +115,9 @@ ID_SHOW_PANDA_OBJECT = 304
ID_HOT_KEYS = 305
ID_PARENT_TO_SELECTED = 306
ID_CREATE_CURVE = 601
ID_EDIT_CURVE = 602
class LevelEditorUIBase(WxPandaShell):
""" Class for Panda3D LevelEditor """
def __init__(self, editor):
@ -132,7 +136,9 @@ class LevelEditorUIBase(WxPandaShell):
ID_GRID_SNAP : ("Grid S&nap", None),
ID_SHOW_PANDA_OBJECT : ("Show &Panda Objects", None),
ID_HOT_KEYS : ("&Hot Keys", None),
ID_PARENT_TO_SELECTED : ("&Parent To Selected", None)
ID_PARENT_TO_SELECTED : ("&Parent To Selected", None),
ID_CREATE_CURVE : ("&Create Curve", None),
ID_EDIT_CURVE : ("&Edit Curve", None)
})
self.editor = editor
@ -197,9 +203,81 @@ class LevelEditorUIBase(WxPandaShell):
self.hotKeysMenuItem = self.menuOptions.Append(ID_HOT_KEYS, self.MENU_TEXTS[ID_HOT_KEYS][0])
self.Bind(wx.EVT_MENU, self.onHotKeys, self.hotKeysMenuItem)
self.menuCurve = wx.Menu()
self.menuBar.Insert(3, self.menuCurve, "&CurveMode")
self.createCurveMenuItem = self.menuCurve.Append(ID_CREATE_CURVE, self.MENU_TEXTS[ID_CREATE_CURVE][0], kind = wx.ITEM_CHECK)
self.Bind(wx.EVT_MENU, self.onCreateCurve, self.createCurveMenuItem)
self.editCurveMenuItem = self.menuCurve.Append(ID_EDIT_CURVE, self.MENU_TEXTS[ID_EDIT_CURVE][0], kind = wx.ITEM_CHECK)
self.Bind(wx.EVT_MENU, self.onEditCurve, self.editCurveMenuItem)
WxPandaShell.createMenu(self)
def onCreateCurve(self,e):
"""Function to invoke curve creating, need to check previous mode"""
if self.editor.mode == self.editor.CREATE_CURVE_MODE:
self.createCurveMenuItem.Check(False)
self.editor.curveEditor.onBaseMode()
else:
if self.editor.mode == self.editor.EDIT_CURVE_MODE:
self.editor.curveEditor.onBaseMode()
self.editCurveMenuItem.Check(False)
self.createCurveMenuItem.Check(True)
self.onCreateCurve(None)
else:
self.currentView = self.getCurrentView()
if self.currentView == None:
dlg = wx.MessageDialog(None, 'Please select a viewport first.Do not support curve creation under four viewports.', 'NOTICE', wx.OK )
dlg.ShowModal()
dlg.Destroy()
self.createCurveMenuItem.Check(False)
else:
self.editor.mode = self.editor.CREATE_CURVE_MODE
self.editor.updateStatusReadout('Please press ENTER to end the curve creation.')
degreeUI = CurveDegreeUI(self, -1, 'Curve Degree')
degreeUI.ShowModal()
degreeUI.Destroy()
base.direct.manipulationControl.disableManipulation()
self.editCurveMenuItem.Check(False)
def onEditCurve(self,e):
"""Function to invoke curve editing and transfer global information to local information. Need to check previous mode"""
if self.editor.mode == self.editor.EDIT_CURVE_MODE:
self.editCurveMenuItem.Check(False)
self.editor.curveEditor.onBaseMode()
else:
if self.editor.mode == self.editor.CREATE_CURVE_MODE:
self.editor.curveEditor.onBaseMode()
self.editCurveMenuItem.Check(True)
self.createCurveMenuItem.Check(False)
self.onEditCurve(None)
else:
if base.direct.selected.last == None:
dlg = wx.MessageDialog(None, 'Please select a curve first.', 'NOTICE', wx.OK )
dlg.ShowModal()
dlg.Destroy()
self.editCurveMenuItem.Check(False)
if base.direct.selected.last != None :
base.direct.manipulationControl.enableManipulation()
self.createCurveMenuItem.Check(False)
self.curveObj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last)
if self.curveObj[OG.OBJ_DEF].name == '__Curve__':
self.editor.mode = self.editor.EDIT_CURVE_MODE
self.editor.updateStatusReadout('Please press ENTER to end the curve editing.')
self.editor.curveEditor.currentRope = self.curveObj[OG.OBJ_NP]
self.editor.curveEditor.curveControl = self.curveObj[OG.OBJ_PROP]['curveInfo']
self.editor.curveEditor.degree = self.curveObj[OG.OBJ_PROP]['Degree']
for item in self.editor.curveEditor.curveControl:
item[1].show()
self.editor.curveEditor.curve.append((None, item[1].getPos()))
else:
dlg = wx.MessageDialog(None, 'Please select a curve first.', 'NOTICE', wx.OK )
dlg.ShowModal()
dlg.Destroy()
self.editCurveMenuItem.Check(False)
def updateMenu(self):
hotKeyDict = {}
for hotKey in base.direct.hotKeyMap.keys():
@ -521,3 +599,32 @@ class ViewportMenu(wx.Menu):
parent.AppendMenu(id, name, subMenu)
return subMenu
class CurveDegreeUI(wx.Dialog):
def __init__(self, parent, id, title):
wx.Dialog.__init__(self, parent, id, title, size=(150, 120))
self.parent = parent
panel = wx.Panel(self, -1)
degreeBox = wx.BoxSizer(wx.VERTICAL)
degreeList = ['2','3','4']
self.degree = wx.RadioBox(panel, -1, 'Curve Degree', (5, 5), wx.DefaultSize, degreeList, 3, wx.RA_SPECIFY_COLS)
self.degree.SetToolTipString("Select the degree of the curve.")
self.degree.SetSelection(1)
okButton = wx.Button(self, -1, 'Apply', size=(70, 20))
okButton.Bind(wx.EVT_BUTTON, self.onApply)
degreeBox.Add(panel, 1, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 5)
degreeBox.Add(okButton, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 5)
self.SetSizer(degreeBox)
def onApply(self, evt):
if(str(self.degree.GetSelection())=='0'):
self.parent.editor.curveEditor.degree = 2
if(str(self.degree.GetSelection())=='1'):
self.parent.editor.curveEditor.degree = 3
if(str(self.degree.GetSelection())=='2'):
self.parent.editor.curveEditor.degree = 4
self.Destroy()

View File

@ -70,6 +70,84 @@ class ObjectMgrBase:
self.lastUidMod = 0
return newUid
def addNewCurveFromFile(self, curveInfo, degree, uid=None, parent=None, fSelectObject=True, nodePath=None):
""" function to add new curve to the scene from file"""
curve = []
curveControl = []
#transfer the curve information from simple positions into control nodes
for item in curveInfo:
controler = render.attachNewNode("controler")
controler = loader.loadModel('models/misc/smiley')
controlerPathname = 'controler%d' % item[0]
controler.setName(controlerPathname)
controler.setPos(item[1])
controler.setColor(0, 0, 0, 1)
controler.setScale(0.2)
controler.reparentTo(render)
controler.setTag('OBJRoot','1')
controler.setTag('Controller','1')
curve.append((None, item[1]))
curveControl.append((item[0], controler))
self.editor.curveEditor.degree = degree
self.editor.curveEditor.ropeUpdate (curve)
#add new curve to the scene
curveObjNP = self.addNewCurve(curveControl, degree, uid, parent, fSelectObject, nodePath = self.editor.curveEditor.currentRope)
curveObj = self.findObjectByNodePath(curveObjNP)
self.editor.objectMgr.updateObjectPropValue(curveObj, 'Degree', degree, fSelectObject=False, fUndo=False)
for item in curveControl:
item[1].reparentTo(curveObjNP)
item[1].hide()
curveControl = []
curve = []
self.editor.curveEditor.currentRope = None
return curveObjNP
def addNewCurve(self, curveInfo, degree, uid=None, parent=None, fSelectObject=True, nodePath=None):
""" function to add new curve to the scene"""
if parent is None:
parent = self.editor.NPParent
if uid is None:
uid = self.genUniqueId()
if self.editor:
objDef = self.editor.objectPalette.findItem('__Curve__')
if nodePath is None:
# we need to create curve
# and then create newobj with newly created curve
pass
else:
newobj = nodePath
newobj.reparentTo(parent)
newobj.setTag('OBJRoot','1')
# populate obj data using default values
properties = {}
for key in objDef.properties.keys():
properties[key] = objDef.properties[key][OG.PROP_DEFAULT]
properties['Degree'] = degree
properties['curveInfo'] = curveInfo
# insert obj data to main repository
self.objects[uid] = [uid, newobj, objDef, None, None, properties, (1,1,1,1)]
self.npIndex[NodePath(newobj)] = uid
if self.editor:
if fSelectObject:
self.editor.select(newobj, fUndo=0)
self.editor.ui.sceneGraphUI.add(newobj, parent)
self.editor.fNeedToSave = True
return newobj
def addNewObject(self, typeName, uid = None, model = None, parent=None, anim = None, fSelectObject=True, nodePath=None, nameStr=None):
""" function to add new obj to the scene """
if parent is None:
@ -529,9 +607,6 @@ class ObjectMgrBase:
elif propType == OG.PROP_UI_COMBO_DYNAMIC:
val = event.GetString()
elif propType == OG.PROP_UI_TIME:
val = event.ClientObject.GetParent().GetParent().getValue()
else:
# unsupported property type
return
@ -612,14 +687,23 @@ class ObjectMgrBase:
if fSelectObject:
base.direct.select(obj[OG.OBJ_NP], fUndo=0)
def updateCurve(self, val, obj):
curve = obj[OG.OBJ_NP]
degree = int(val)
curveNode = obj[OG.OBJ_PROP]['curveInfo']
curveInfor = []
for item in curveNode:
curveInfor.append((None, item[1].getPos()))
curve.setup(degree, curveInfor)
def updateObjectProperties(self, nodePath, propValues):
"""
When a saved level is loaded,
update an object's properties
And call update function if defined.
"""
obj = self.findObjectByNodePath(nodePath)
if obj:
for propName in propValues:
self.updateObjectPropValue(obj, propName, propValues[propName])
@ -629,7 +713,7 @@ class ObjectMgrBase:
Trasverse scene graph to gather data for saving
"""
for child in parent.getChildren():
if child.hasTag('OBJRoot'):
if child.hasTag('OBJRoot') and not child.hasTag('Controller'):
obj = self.findObjectByNodePath(child)
if obj:
@ -640,7 +724,7 @@ class ObjectMgrBase:
objAnim = obj[OG.OBJ_ANIM]
objProp = obj[OG.OBJ_PROP]
objRGBA = obj[OG.OBJ_RGBA]
if parentId:
parentStr = "objects['%s']"%parentId
else:
@ -661,13 +745,28 @@ class ObjectMgrBase:
else:
nameStr = "None"
self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s, False, None, %s)"%(uid, objDef.name, uid, modelStr, parentStr, animStr, nameStr))
if objDef.name == '__Curve__':
#transfer the curve information from control nodes into simple positions for file save
objCurveInfo = obj[OG.OBJ_PROP]['curveInfo']
self.objDegree = obj[OG.OBJ_PROP]['Degree']
newobjCurveInfo = []
for item in objCurveInfo:
newobjCurveInfo.append((item[0], item[1].getPos()))
self.saveData.append("\nobjects['%s'] = objectMgr.addNewCurveFromFile(%s, %s, '%s', %s, False, None)"%(uid, newobjCurveInfo, self.objDegree, uid, parentStr))
else:
self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s, False, None, %s)"%(uid, objDef.name, uid, modelStr, parentStr, animStr, nameStr))
self.saveData.append("if objects['%s']:"%uid)
self.saveData.append(" objects['%s'].setPos(%s)"%(uid, np.getPos()))
self.saveData.append(" objects['%s'].setHpr(%s)"%(uid, np.getHpr()))
self.saveData.append(" objects['%s'].setScale(%s)"%(uid, np.getScale()))
self.saveData.append(" objectMgr.updateObjectColor(%f, %f, %f, %f, objects['%s'])"%(objRGBA[0], objRGBA[1], objRGBA[2], objRGBA[3], uid))
self.saveData.append(" objectMgr.updateObjectProperties(objects['%s'], %s)"%(uid,objProp))
if objDef.name == '__Curve__':
pass
else:
self.saveData.append(" objectMgr.updateObjectProperties(objects['%s'], %s)"%(uid,objProp))
self.traverse(child, uid)

View File

@ -27,6 +27,15 @@ class ObjectBase(ObjectGen):
# to show/hide properties per editor mode
self.propertiesMask = copy.deepcopy(propertiesMask)
class ObjectCurve(ObjectBase):
def __init__(self, *args, **kw):
ObjectBase.__init__(self, *args, **kw)
self.properties['Degree'] =[OG.PROP_UI_COMBO, # UI type
OG.PROP_INT, # data type
('base.le.objectMgr.updateCurve', {'val':OG.ARG_VAL, 'obj':OG.ARG_OBJ}), # update function
3, # default value
[2, 3, 4]] # value range
class ObjectPaletteBase:
"""
Base class for objectPalette
@ -40,6 +49,7 @@ class ObjectPaletteBase:
self.data = {}
self.dataStruct = {}
self.dataKeys = []
self.populateSystemObjs()
#self.populate()
def insertItem(self, item, parentName):
@ -128,6 +138,9 @@ class ObjectPaletteBase:
return False
return True
def populateSystemObjs(self):
self.addHidden(ObjectCurve(name='__Curve__'))
def populate(self):
# You should implement this in subclass
raise NotImplementedError('populate() must be implemented in ObjectPalette.py')

View File

@ -64,7 +64,7 @@ class WxPandaShell(WxAppShell):
menuItem = self.menuView.AppendRadioItem(ID_LEFT_VIEW, self.MENU_TEXTS[ID_LEFT_VIEW][0])
self.Bind(wx.EVT_MENU, lambda p0=None, p1=2:self.onViewChange(p0, p1), menuItem)
self.perspViewMenuItem = self.menuView.AppendRadioItem(ID_PERSP_VIEW, self.MENU_TEXTS[ID_PERSP_VIEW][0])
self.Bind(wx.EVT_MENU, lambda p0=None, p1=3:self.onViewChange(p0, p1), self.perspViewMenuItem)
@ -223,3 +223,20 @@ class WxPandaShell(WxAppShell):
base.winList[i].setActive(1)
self.viewFrame.SetExpanded(viewIdx)
def getCurrentView(self):
"""Function for get the current Viewport"""
if self.viewFrame._expanded == -1: #four view
self.currentView = None
if self.viewFrame._expanded == 0: #top view
self.currentView = self.topView
if self.viewFrame._expanded == 1: #front view
self.currentView = self.frontView
if self.viewFrame._expanded == 2: #left view
self.currentView = self.leftView
if self.viewFrame._expanded == 3: #perspect view
self.currentView = self.perspView
return self.currentView