diff --git a/direct/src/leveleditor/LevelEditor.py b/direct/src/leveleditor/LevelEditor.py index 46c39669ee..c6cb5746b4 100644 --- a/direct/src/leveleditor/LevelEditor.py +++ b/direct/src/leveleditor/LevelEditor.py @@ -6,6 +6,7 @@ to be game specific. """ from LevelEditorBase import * +from ObjectMgr import * from ObjectHandler import * from ObjectPalette import * from LevelEditorUI import * @@ -21,6 +22,7 @@ class LevelEditor(LevelEditorBase): # If you have your own ObjectPalette and ObjectHandler # connect them in your own LevelEditor class + self.objectMgr = ObjectMgr(self) self.objectPalette = ObjectPalette() self.objectHandler = ObjectHandler(self) self.protoPalette = ProtoPalette() diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py index cdd97aca2d..e39df34340 100755 --- a/direct/src/leveleditor/LevelEditorBase.py +++ b/direct/src/leveleditor/LevelEditorBase.py @@ -14,7 +14,6 @@ from direct.gui.DirectGui import * base = ShowBase(False) -from ObjectMgr import * from FileMgr import * from ActionMgr import * from MayaConverter import * @@ -26,7 +25,7 @@ class LevelEditorBase(DirectObject): self.currentFile = None self.fNeedToSave = False self.actionEvents = [] - self.objectMgr = ObjectMgr(self) + #self.objectMgr = ObjectMgr(self) self.fileMgr = FileMgr(self) self.actionMgr = ActionMgr() diff --git a/direct/src/leveleditor/ObjectGlobals.py b/direct/src/leveleditor/ObjectGlobals.py index 95821328fd..f3b81672ac 100755 --- a/direct/src/leveleditor/ObjectGlobals.py +++ b/direct/src/leveleditor/ObjectGlobals.py @@ -18,6 +18,7 @@ PROP_UI_RADIO = '_PropUIRadio' PROP_UI_CHECK = '_PropUICheckBox' PROP_UI_SLIDE = '_PropUISlider' PROP_UI_SPIN = '_PropUISpinner' +PROP_UI_BLIND = '_PropUIBlind' # index for property definition PROP_TYPE = 0 @@ -40,6 +41,7 @@ PROP_INT = 0 # int type value PROP_BOOL = 1 # bool type value PROP_FLOAT = 2 # float type value PROP_STR = 3 # string type value +PROP_BLIND = 4 # blind type value TYPE_CONV = {PROP_INT: int, PROP_BOOL: bool, PROP_FLOAT: float, PROP_STR: str} diff --git a/direct/src/leveleditor/ObjectMgr.py b/direct/src/leveleditor/ObjectMgr.py index 5e838d0648..3f870489c2 100755 --- a/direct/src/leveleditor/ObjectMgr.py +++ b/direct/src/leveleditor/ObjectMgr.py @@ -1,670 +1,10 @@ """ Defines ObjectMgr """ +from ObjectMgrBase import * -import os, time, wx, types - -from direct.task import Task -from direct.actor.Actor import Actor -from pandac.PandaModules import * -from ActionMgr import * -import ObjectGlobals as OG -from ObjectPaletteBase import ObjectGen -class ObjectMgr: +class ObjectMgr(ObjectMgrBase): """ ObjectMgr will create, manage, update objects in the scene """ def __init__(self, editor): - self.editor = editor - - # main obj repository of objects in the scene - self.objects = {} - self.npIndex = {} - self.saveData = [] - self.objectsLastXform = {} - - self.lastUid = '' - self.lastUidMode = 0 - self.currNodePath = None - - self.currLiveNP = None - - def reset(self): - base.direct.deselectAllCB() - - for id in self.objects.keys(): - try: - self.objects[id][OG.OBJ_NP].removeNode() - except: - pass - del self.objects[id] - - for np in self.npIndex.keys(): - del self.npIndex[np] - - self.objects = {} - self.npIndex = {} - self.saveData = [] - - def genUniqueId(self): - # [gjeon] to solve the problem of unproper $USERNAME - userId = os.path.basename(os.path.expandvars('$USERNAME')) - if userId == '': - userId = base.config.GetString("le-user-id") - if userId == '': - userId = 'unknown' - newUid = str(time.time()) + userId - # prevent duplicates from being generated in the same frame (this can - # happen when creating several new objects at once) - if (self.lastUid == newUid): - # append a value to the end to uniquify the id - newUid = newUid + str(self.lastUidMod) - self.lastUidMod = self.lastUidMod + 1 - else: - self.lastUid = newUid - self.lastUidMod = 0 - return newUid - - def addNewObject(self, typeName, uid = None, model = None, parent=None, anim = None, fSelectObject=True, nodePath=None): - """ function to add new obj to the scene """ - if parent is None: - parent = self.editor.NPParent - - if self.editor: - objDef = self.editor.objectPalette.findItem(typeName) - if objDef is None: - objDef = self.editor.protoPalette.findItem(typeName) - else: # when loaded outside of LE - objDef = base.objectPalette.findItem(typeName) - if objDef is None: - objDef = base.protoPalette.findItem(typeName) - newobj = None - if objDef and type(objDef) != dict: - if nodePath is None: - if objDef.createFunction: - funcName = objDef.createFunction[OG.FUNC_NAME] - funcArgs = objDef.createFunction[OG.FUNC_ARGS] - if type(funcName) == types.StringType: - if funcName.startswith('.'): - # when it's using default objectHandler - if self.editor: - func = Functor(eval("self.editor.objectHandler%s"%funcName)) - else: # when loaded outside of LE - func = Functor(eval("base.objectHandler%s"%funcName)) - else: - # when it's not using default objectHandler, whole name of the handling obj - # should be included in function name - func = Functor(eval(funcName)) - else: - func = funcName - # 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 - try: - newobj = loader.loadModel(model) - except: - newobj = loader.loadModel(Filename.fromOsSpecific(model).getFullpath()) - else: - newobj = hidden.attachNewNode(objDef.name) - else: - newobj = nodePath - i = 0 - for i in range(len(objDef.anims)): - animFile = objDef.anims[i] - # load new anim - animName = os.path.basename(animFile) - if i < len(objDef.animNames): - animName = objDef.animNames[i] - newAnim = newobj.loadAnims({animName:animFile}) - - if anim: - if anim == animFile: - newobj.loop(animName) - else: - if i == 0: - anim = animFile - newobj.loop(animName) - - if newobj is None: - return None - - newobj.reparentTo(parent) - newobj.setTag('OBJRoot','1') - - if uid is None: - uid = self.genUniqueId() - - # populate obj data using default values - properties = {} - for key in objDef.properties.keys(): - properties[key] = objDef.properties[key][OG.PROP_DEFAULT] - - # insert obj data to main repository - self.objects[uid] = [uid, newobj, objDef, model, anim, 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) - self.editor.fNeedToSave = True - return newobj - - def removeObjectById(self, uid): - obj = self.findObjectById(uid) - nodePath = obj[OG.OBJ_NP] - del self.objects[uid] - del self.npIndex[nodePath] - - # remove children also - for child in nodePath.getChildren(): - if child.hasTag('OBJRoot'): - self.removeObjectByNodePath(child) - nodePath.remove() - - self.editor.fNeedToSave = True - - def removeObjectByNodePath(self, nodePath): - uid = self.npIndex.get(nodePath) - if uid: - del self.objects[uid] - del self.npIndex[nodePath] - - # remove children also - for child in nodePath.getChildren(): - if child.hasTag('OBJRoot'): - self.removeObjectByNodePath(child) - self.editor.fNeedToSave = True - - def findObjectById(self, uid): - return self.objects.get(uid) - - def findObjectByNodePath(self, nodePath): - uid = self.npIndex.get(NodePath(nodePath)) - if uid is None: - return None - else: - return self.objects[uid] - - def deselectAll(self): - self.currNodePath = None - taskMgr.remove('_le_updateObjectUITask') - self.editor.ui.objectPropertyUI.clearPropUI() - self.editor.ui.sceneGraphUI.tree.UnselectAll() - - def selectObject(self, nodePath, fLEPane=0): - obj = self.findObjectByNodePath(nodePath) - if obj is None: - return - - self.currNodePath = obj[OG.OBJ_NP] - self.objectsLastXform[obj[OG.OBJ_UID]] = Mat4(self.currNodePath.getMat()) - # [gjeon] to connect transform UI with nodepath's transform - self.spawnUpdateObjectUITask() - self.updateObjectPropertyUI(obj) - #import pdb;pdb.set_trace() - if fLEPane == 0: - self.editor.ui.sceneGraphUI.select(obj[OG.OBJ_UID]) - - if not obj[OG.OBJ_DEF].movable: - if base.direct.widget.fActive: - base.direct.widget.toggleWidget() - - - - def updateObjectPropertyUI(self, obj): - objDef = obj[OG.OBJ_DEF] - objProp = obj[OG.OBJ_PROP] - self.editor.ui.objectPropertyUI.updateProps(obj, objDef.movable) - self.editor.fNeedToSave = True - - def onEnterObjectPropUI(self, event): - taskMgr.remove('_le_updateObjectUITask') - self.editor.ui.bindKeyEvents(False) - - def onLeaveObjectPropUI(self, event): - self.spawnUpdateObjectUITask() - self.editor.ui.bindKeyEvents(True) - - def spawnUpdateObjectUITask(self): - if self.currNodePath is None: - return - - taskMgr.remove('_le_updateObjectUITask') - t = Task.Task(self.updateObjectUITask) - t.np = self.currNodePath - taskMgr.add(t, '_le_updateObjectUITask') - - def updateObjectUITask(self, state): - self.editor.ui.objectPropertyUI.propX.setValue(state.np.getX()) - self.editor.ui.objectPropertyUI.propY.setValue(state.np.getY()) - self.editor.ui.objectPropertyUI.propZ.setValue(state.np.getZ()) - - h = state.np.getH() - while h < 0: - h = h + 360.0 - - while h > 360: - h = h - 360.0 - - p = state.np.getP() - while p < 0: - p = p + 360.0 - - while p > 360: - p = p - 360.0 - - r = state.np.getR() - while r < 0: - r = r + 360.0 - - while r > 360: - r = r - 360.0 - - self.editor.ui.objectPropertyUI.propH.setValue(h) - self.editor.ui.objectPropertyUI.propP.setValue(p) - self.editor.ui.objectPropertyUI.propR.setValue(r) - - self.editor.ui.objectPropertyUI.propSX.setValue(state.np.getSx()) - self.editor.ui.objectPropertyUI.propSY.setValue(state.np.getSy()) - self.editor.ui.objectPropertyUI.propSZ.setValue(state.np.getSz()) - - return Task.cont - - def updateObjectTransform(self, event): - if self.currNodePath is None: - return - - np = hidden.attachNewNode('temp') - np.setX(float(self.editor.ui.objectPropertyUI.propX.getValue())) - np.setY(float(self.editor.ui.objectPropertyUI.propY.getValue())) - np.setZ(float(self.editor.ui.objectPropertyUI.propZ.getValue())) - - h = float(self.editor.ui.objectPropertyUI.propH.getValue()) - while h < 0: - h = h + 360.0 - - while h > 360: - h = h - 360.0 - - p = float(self.editor.ui.objectPropertyUI.propP.getValue()) - while p < 0: - p = p + 360.0 - - while p > 360: - p = p - 360.0 - - r = float(self.editor.ui.objectPropertyUI.propR.getValue()) - while r < 0: - r = r + 360.0 - - while r > 360: - r = r - 360.0 - - np.setH(h) - np.setP(p) - np.setR(r) - - np.setSx(float(self.editor.ui.objectPropertyUI.propSX.getValue())) - np.setSy(float(self.editor.ui.objectPropertyUI.propSY.getValue())) - np.setSz(float(self.editor.ui.objectPropertyUI.propSZ.getValue())) - - obj = self.findObjectByNodePath(self.currNodePath) - action = ActionTransformObj(self.editor, obj[OG.OBJ_UID], Mat4(np.getMat())) - self.editor.actionMgr.push(action) - np.remove() - action() - self.editor.fNeedToSave = True - - def setObjectTransform(self, uid, xformMat): - obj = self.findObjectById(uid) - if obj: - obj[OG.OBJ_NP].setMat(xformMat) - self.editor.fNeedToSave = True - - def updateObjectColor(self, r, g, b, a, np=None): - if np is None: - np = self.currNodePath - - obj = self.findObjectByNodePath(np) - if not obj: - return - obj[OG.OBJ_RGBA] = (r,g,b,a) - for child in np.getChildren(): - if not child.hasTag('OBJRoot') and\ - child.getName() != 'bboxLines': - child.setTransparency(1) - child.setColorScale(r, g, b, a) - self.editor.fNeedToSave = True - - def updateObjectModel(self, model, obj, fSelectObject=True): - """ replace object's model """ - if obj[OG.OBJ_MODEL] != model: - base.direct.deselectAllCB() - - objNP = obj[OG.OBJ_NP] - objRGBA = obj[OG.OBJ_RGBA] - - # load new model - newobj = loader.loadModel(model) - newobj.setTag('OBJRoot','1') - - # reparent children - objNP.findAllMatches("=OBJRoot").reparentTo(newobj) - - # reparent to parent - newobj.reparentTo(objNP.getParent()) - - # copy transform - newobj.setPos(objNP.getPos()) - newobj.setHpr(objNP.getHpr()) - newobj.setScale(objNP.getScale()) - - # copy RGBA data - self.updateObjectColor(objRGBA[0], objRGBA[1], objRGBA[2], objRGBA[3], newobj) - - # delete old geom - del self.npIndex[NodePath(objNP)] - objNP.removeNode() - - # register new geom - obj[OG.OBJ_NP] = newobj - obj[OG.OBJ_MODEL] = model - self.npIndex[NodePath(newobj)] = obj[OG.OBJ_UID] - - if fSelectObject: - base.direct.select(newobj, fUndo=0) - - self.editor.fNeedToSave = True - - def updateObjectAnim(self, anim, obj, fSelectObject=True): - """ replace object's anim """ - if obj[OG.OBJ_ANIM] != anim: - base.direct.deselectAllCB() - 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, fUndo=0) - - self.editor.fNeedToSave = True - - 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 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, - this will update it's value in data structure. - And call update function if defined. - """ - - objDef = obj[OG.OBJ_DEF] - objProp = obj[OG.OBJ_PROP] - - propDef = objDef.properties[propName] - if propDef is None: - return - - propType = propDef[OG.PROP_TYPE] - propDataType = propDef[OG.PROP_DATATYPE] - - if propType == OG.PROP_UI_SLIDE: - if len(propDef) <= OG.PROP_RANGE: - return - - strVal = event.GetString() - if strVal == '': - min = float(propDef[OG.PROP_RANGE][OG.RANGE_MIN]) - max = float(propDef[OG.PROP_RANGE][OG.RANGE_MAX]) - intVal = event.GetInt() - if intVal is None: - return - val = intVal / 100.0 * (max - min) + min - else: - val = strVal - - elif propType == OG.PROP_UI_ENTRY: - val = event.GetString() - - elif propType == OG.PROP_UI_SPIN: - val = event.GetInt() - - elif propType == OG.PROP_UI_CHECK: - if event.GetInt(): - val = True - else: - val = False - - elif propType == OG.PROP_UI_RADIO: - val = event.GetString() - - elif propType == OG.PROP_UI_COMBO: - val = event.GetString() - - else: - # unsupported property type - return - - # now update object prop value and call update function - self.updateObjectPropValue(obj, propName, val, \ - fSelectObject=(propType != OG.PROP_UI_SLIDE) - ) - - def updateObjectPropValue(self, obj, propName, val, fSelectObject=False): - """ - Update object property value and - call update function if defined. - """ - - objDef = obj[OG.OBJ_DEF] - objProp = obj[OG.OBJ_PROP] - - propDef = objDef.properties[propName] - propDataType = propDef[OG.PROP_DATATYPE] - - val = OG.TYPE_CONV[propDataType](val) - oldVal = objProp[propName] - #objProp[propName] = val - - if propDef[OG.PROP_FUNC] is None: - func = None - undoFunc = None - else: - funcName = propDef[OG.PROP_FUNC][OG.FUNC_NAME] - funcArgs = propDef[OG.PROP_FUNC][OG.FUNC_ARGS] - - # populate keyword arguments - kwargs = {} - undoKwargs = {} - for key in funcArgs.keys(): - if funcArgs[key] == OG.ARG_VAL: - kwargs[key] = val - undoKwargs[key] = oldVal - elif funcArgs[key] == OG.ARG_OBJ: - undoKwargs[key] = obj - objProp[propName] = val - kwargs[key] = obj - else: - kwargs[key] = funcArgs[key] - undoKwargs[key] = funcArgs[key] - - if type(funcName) == types.StringType: - if funcName.startswith('.'): - if self.editor: - func = Functor(eval("self.editor.objectHandler%s"%funcName), **kwargs) - undoFunc = Functor(eval("self.editor.objectHandler%s"%funcName), **undoKwargs) - else: # when loaded outside of LE - func = Functor(eval("base.objectHandler%s"%funcName), **kwargs) - undoFunc = Functor(eval("base.objectHandler%s"%funcName), **undoKwargs) - else: - func = Functor(eval(funcName), **kwargs) - undoFunc = Functor(eval(funcName), **undoKwargs) - else: - func = Functor(funcName, **kwargs) - undoFunc = Functor(funcName, **undoKwargs) - - # finally call update function - #func(**kwargs) - action = ActionUpdateObjectProp(self.editor, fSelectObject, obj, propName, val, oldVal, func, undoFunc) - self.editor.actionMgr.push(action) - action() - - if self.editor: - self.editor.fNeedToSave = True - if fSelectObject: - base.direct.select(obj[OG.OBJ_NP], fUndo=0) - - 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]) - - def traverse(self, parent, parentId = None): - """ - Trasverse scene graph to gather data for saving - """ - - for child in parent.getChildren(): - if child.hasTag('OBJRoot'): - obj = self.findObjectByNodePath(child) - - if obj: - uid = obj[OG.OBJ_UID] - np = obj[OG.OBJ_NP] - objDef = obj[OG.OBJ_DEF] - objModel = obj[OG.OBJ_MODEL] - objAnim = obj[OG.OBJ_ANIM] - objProp = obj[OG.OBJ_PROP] - objRGBA = obj[OG.OBJ_RGBA] - - if parentId: - parentStr = "objects['%s']"%parentId - else: - parentStr = "None" - - if objModel: - modelStr = "'%s'"%objModel - else: - modelStr = "None" - - if objAnim: - animStr = "'%s'"%objAnim - else: - animStr = "None" - - self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s, fSelectObject=False)"%(uid, objDef.name, uid, modelStr, parentStr, animStr)) - 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)) - - self.traverse(child, uid) - - def getSaveData(self): - 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] - objModel = obj[OG.OBJ_MODEL] - objAnim = obj[OG.OBJ_ANIM] - objRGBA = obj[OG.OBJ_RGBA] - - if parent is None: - parentNP = nodePath.getParent() - parentObj = self.findObjectByNodePath(parentNP) - if parentObj is None: - parent = parentNP - else: - parent = parentObj[OG.OBJ_NP] - - 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 anim info - self.updateObjectAnim(obj[OG.OBJ_ANIM], newObj, fSelectObject=False) - - # copy other properties - for key in obj[OG.OBJ_PROP]: - self.updateObjectPropValue(newObj, key, obj[OG.OBJ_PROP][key]) - 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.deselectAllCB() - for newNodePath in duplicatedNPs: - base.direct.select(newNodePath, fMultiSelect = 1, fUndo=0) - - self.editor.fNeedToSave = True - - def makeSelectedLive(self): - obj = self.findObjectByNodePath(base.direct.selected.last) - if obj: - if self.currLiveNP: - self.currLiveNP.clearColorScale() - if self.currLiveNP == obj[OG.OBJ_NP]: - self.currLiveNP = None - return - - self.currLiveNP = obj[OG.OBJ_NP] - self.currLiveNP.setColorScale(0, 1, 0, 1) + ObjectMgrBase.__init__(self, editor) diff --git a/direct/src/leveleditor/ObjectMgrBase.py b/direct/src/leveleditor/ObjectMgrBase.py new file mode 100755 index 0000000000..e1002db5c0 --- /dev/null +++ b/direct/src/leveleditor/ObjectMgrBase.py @@ -0,0 +1,674 @@ +""" +Defines ObjectMgrBase +""" + +import os, time, wx, types + +from direct.task import Task +from direct.actor.Actor import Actor +from pandac.PandaModules import * +from ActionMgr import * +import ObjectGlobals as OG +from ObjectPaletteBase import ObjectGen + +class ObjectMgrBase: + """ ObjectMgr will create, manage, update objects in the scene """ + + def __init__(self, editor): + self.editor = editor + + # main obj repository of objects in the scene + self.objects = {} + self.npIndex = {} + self.saveData = [] + self.objectsLastXform = {} + + self.lastUid = '' + self.lastUidMode = 0 + self.currNodePath = None + + self.currLiveNP = None + + def reset(self): + base.direct.deselectAllCB() + + for id in self.objects.keys(): + try: + self.objects[id][OG.OBJ_NP].removeNode() + except: + pass + del self.objects[id] + + for np in self.npIndex.keys(): + del self.npIndex[np] + + self.objects = {} + self.npIndex = {} + self.saveData = [] + + def genUniqueId(self): + # [gjeon] to solve the problem of unproper $USERNAME + userId = os.path.basename(os.path.expandvars('$USERNAME')) + if userId == '': + userId = base.config.GetString("le-user-id") + if userId == '': + userId = 'unknown' + newUid = str(time.time()) + userId + # prevent duplicates from being generated in the same frame (this can + # happen when creating several new objects at once) + if (self.lastUid == newUid): + # append a value to the end to uniquify the id + newUid = newUid + str(self.lastUidMod) + self.lastUidMod = self.lastUidMod + 1 + else: + self.lastUid = newUid + self.lastUidMod = 0 + return newUid + + def addNewObject(self, typeName, uid = None, model = None, parent=None, anim = None, fSelectObject=True, nodePath=None): + """ function to add new obj to the scene """ + if parent is None: + parent = self.editor.NPParent + + if self.editor: + objDef = self.editor.objectPalette.findItem(typeName) + if objDef is None: + objDef = self.editor.protoPalette.findItem(typeName) + else: # when loaded outside of LE + objDef = base.objectPalette.findItem(typeName) + if objDef is None: + objDef = base.protoPalette.findItem(typeName) + newobj = None + if objDef and type(objDef) != dict: + if nodePath is None: + if objDef.createFunction: + funcName = objDef.createFunction[OG.FUNC_NAME] + funcArgs = objDef.createFunction[OG.FUNC_ARGS] + if type(funcName) == types.StringType: + if funcName.startswith('.'): + # when it's using default objectHandler + if self.editor: + func = Functor(eval("self.editor.objectHandler%s"%funcName)) + else: # when loaded outside of LE + func = Functor(eval("base.objectHandler%s"%funcName)) + else: + # when it's not using default objectHandler, whole name of the handling obj + # should be included in function name + func = Functor(eval(funcName)) + else: + func = funcName + # 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 + try: + newobj = loader.loadModel(model) + except: + newobj = loader.loadModel(Filename.fromOsSpecific(model).getFullpath()) + else: + newobj = hidden.attachNewNode(objDef.name) + else: + newobj = nodePath + i = 0 + for i in range(len(objDef.anims)): + animFile = objDef.anims[i] + # load new anim + animName = os.path.basename(animFile) + if i < len(objDef.animNames): + animName = objDef.animNames[i] + newAnim = newobj.loadAnims({animName:animFile}) + + if anim: + if anim == animFile: + newobj.loop(animName) + else: + if i == 0: + anim = animFile + newobj.loop(animName) + + if newobj is None: + return None + + newobj.reparentTo(parent) + newobj.setTag('OBJRoot','1') + + if uid is None: + uid = self.genUniqueId() + + # populate obj data using default values + properties = {} + for key in objDef.properties.keys(): + properties[key] = objDef.properties[key][OG.PROP_DEFAULT] + + # insert obj data to main repository + self.objects[uid] = [uid, newobj, objDef, model, anim, 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) + self.editor.fNeedToSave = True + return newobj + + def removeObjectById(self, uid): + obj = self.findObjectById(uid) + nodePath = obj[OG.OBJ_NP] + del self.objects[uid] + del self.npIndex[nodePath] + + # remove children also + for child in nodePath.getChildren(): + if child.hasTag('OBJRoot'): + self.removeObjectByNodePath(child) + nodePath.remove() + + self.editor.fNeedToSave = True + + def removeObjectByNodePath(self, nodePath): + uid = self.npIndex.get(nodePath) + if uid: + del self.objects[uid] + del self.npIndex[nodePath] + + # remove children also + for child in nodePath.getChildren(): + if child.hasTag('OBJRoot'): + self.removeObjectByNodePath(child) + self.editor.fNeedToSave = True + + def findObjectById(self, uid): + return self.objects.get(uid) + + def findObjectByNodePath(self, nodePath): + uid = self.npIndex.get(NodePath(nodePath)) + if uid is None: + return None + else: + return self.objects[uid] + + def deselectAll(self): + self.currNodePath = None + taskMgr.remove('_le_updateObjectUITask') + self.editor.ui.objectPropertyUI.clearPropUI() + self.editor.ui.sceneGraphUI.tree.UnselectAll() + + def selectObject(self, nodePath, fLEPane=0): + obj = self.findObjectByNodePath(nodePath) + if obj is None: + return + + self.currNodePath = obj[OG.OBJ_NP] + self.objectsLastXform[obj[OG.OBJ_UID]] = Mat4(self.currNodePath.getMat()) + # [gjeon] to connect transform UI with nodepath's transform + self.spawnUpdateObjectUITask() + self.updateObjectPropertyUI(obj) + #import pdb;pdb.set_trace() + if fLEPane == 0: + self.editor.ui.sceneGraphUI.select(obj[OG.OBJ_UID]) + + if not obj[OG.OBJ_DEF].movable: + if base.direct.widget.fActive: + base.direct.widget.toggleWidget() + + + + def updateObjectPropertyUI(self, obj): + objDef = obj[OG.OBJ_DEF] + objProp = obj[OG.OBJ_PROP] + self.editor.ui.objectPropertyUI.updateProps(obj, objDef.movable) + self.editor.fNeedToSave = True + + def onEnterObjectPropUI(self, event): + taskMgr.remove('_le_updateObjectUITask') + self.editor.ui.bindKeyEvents(False) + + def onLeaveObjectPropUI(self, event): + self.spawnUpdateObjectUITask() + self.editor.ui.bindKeyEvents(True) + + def spawnUpdateObjectUITask(self): + if self.currNodePath is None: + return + + taskMgr.remove('_le_updateObjectUITask') + t = Task.Task(self.updateObjectUITask) + t.np = self.currNodePath + taskMgr.add(t, '_le_updateObjectUITask') + + def updateObjectUITask(self, state): + self.editor.ui.objectPropertyUI.propX.setValue(state.np.getX()) + self.editor.ui.objectPropertyUI.propY.setValue(state.np.getY()) + self.editor.ui.objectPropertyUI.propZ.setValue(state.np.getZ()) + + h = state.np.getH() + while h < 0: + h = h + 360.0 + + while h > 360: + h = h - 360.0 + + p = state.np.getP() + while p < 0: + p = p + 360.0 + + while p > 360: + p = p - 360.0 + + r = state.np.getR() + while r < 0: + r = r + 360.0 + + while r > 360: + r = r - 360.0 + + self.editor.ui.objectPropertyUI.propH.setValue(h) + self.editor.ui.objectPropertyUI.propP.setValue(p) + self.editor.ui.objectPropertyUI.propR.setValue(r) + + self.editor.ui.objectPropertyUI.propSX.setValue(state.np.getSx()) + self.editor.ui.objectPropertyUI.propSY.setValue(state.np.getSy()) + self.editor.ui.objectPropertyUI.propSZ.setValue(state.np.getSz()) + + return Task.cont + + def updateObjectTransform(self, event): + if self.currNodePath is None: + return + + np = hidden.attachNewNode('temp') + np.setX(float(self.editor.ui.objectPropertyUI.propX.getValue())) + np.setY(float(self.editor.ui.objectPropertyUI.propY.getValue())) + np.setZ(float(self.editor.ui.objectPropertyUI.propZ.getValue())) + + h = float(self.editor.ui.objectPropertyUI.propH.getValue()) + while h < 0: + h = h + 360.0 + + while h > 360: + h = h - 360.0 + + p = float(self.editor.ui.objectPropertyUI.propP.getValue()) + while p < 0: + p = p + 360.0 + + while p > 360: + p = p - 360.0 + + r = float(self.editor.ui.objectPropertyUI.propR.getValue()) + while r < 0: + r = r + 360.0 + + while r > 360: + r = r - 360.0 + + np.setH(h) + np.setP(p) + np.setR(r) + + np.setSx(float(self.editor.ui.objectPropertyUI.propSX.getValue())) + np.setSy(float(self.editor.ui.objectPropertyUI.propSY.getValue())) + np.setSz(float(self.editor.ui.objectPropertyUI.propSZ.getValue())) + + obj = self.findObjectByNodePath(self.currNodePath) + action = ActionTransformObj(self.editor, obj[OG.OBJ_UID], Mat4(np.getMat())) + self.editor.actionMgr.push(action) + np.remove() + action() + self.editor.fNeedToSave = True + + def setObjectTransform(self, uid, xformMat): + obj = self.findObjectById(uid) + if obj: + obj[OG.OBJ_NP].setMat(xformMat) + self.editor.fNeedToSave = True + + def updateObjectColor(self, r, g, b, a, np=None): + if np is None: + np = self.currNodePath + + obj = self.findObjectByNodePath(np) + if not obj: + return + obj[OG.OBJ_RGBA] = (r,g,b,a) + for child in np.getChildren(): + if not child.hasTag('OBJRoot') and\ + child.getName() != 'bboxLines': + child.setTransparency(1) + child.setColorScale(r, g, b, a) + self.editor.fNeedToSave = True + + def updateObjectModel(self, model, obj, fSelectObject=True): + """ replace object's model """ + if obj[OG.OBJ_MODEL] != model: + base.direct.deselectAllCB() + + objNP = obj[OG.OBJ_NP] + objRGBA = obj[OG.OBJ_RGBA] + + # load new model + newobj = loader.loadModel(model) + newobj.setTag('OBJRoot','1') + + # reparent children + objNP.findAllMatches("=OBJRoot").reparentTo(newobj) + + # reparent to parent + newobj.reparentTo(objNP.getParent()) + + # copy transform + newobj.setPos(objNP.getPos()) + newobj.setHpr(objNP.getHpr()) + newobj.setScale(objNP.getScale()) + + # copy RGBA data + self.updateObjectColor(objRGBA[0], objRGBA[1], objRGBA[2], objRGBA[3], newobj) + + # delete old geom + del self.npIndex[NodePath(objNP)] + objNP.removeNode() + + # register new geom + obj[OG.OBJ_NP] = newobj + obj[OG.OBJ_MODEL] = model + self.npIndex[NodePath(newobj)] = obj[OG.OBJ_UID] + + if fSelectObject: + base.direct.select(newobj, fUndo=0) + + self.editor.fNeedToSave = True + + def updateObjectAnim(self, anim, obj, fSelectObject=True): + """ replace object's anim """ + if obj[OG.OBJ_ANIM] != anim: + base.direct.deselectAllCB() + 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, fUndo=0) + + self.editor.fNeedToSave = True + + 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 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, + this will update it's value in data structure. + And call update function if defined. + """ + + objDef = obj[OG.OBJ_DEF] + objProp = obj[OG.OBJ_PROP] + + propDef = objDef.properties[propName] + if propDef is None: + return + + propType = propDef[OG.PROP_TYPE] + propDataType = propDef[OG.PROP_DATATYPE] + + if propType == OG.PROP_UI_SLIDE: + if len(propDef) <= OG.PROP_RANGE: + return + + strVal = event.GetString() + if strVal == '': + min = float(propDef[OG.PROP_RANGE][OG.RANGE_MIN]) + max = float(propDef[OG.PROP_RANGE][OG.RANGE_MAX]) + intVal = event.GetInt() + if intVal is None: + return + val = intVal / 100.0 * (max - min) + min + else: + val = strVal + + elif propType == OG.PROP_UI_ENTRY: + val = event.GetString() + + elif propType == OG.PROP_UI_SPIN: + val = event.GetInt() + + elif propType == OG.PROP_UI_CHECK: + if event.GetInt(): + val = True + else: + val = False + + elif propType == OG.PROP_UI_RADIO: + val = event.GetString() + + elif propType == OG.PROP_UI_COMBO: + val = event.GetString() + + else: + # unsupported property type + return + + # now update object prop value and call update function + self.updateObjectPropValue(obj, propName, val, \ + fSelectObject=(propType != OG.PROP_UI_SLIDE) + ) + + def updateObjectPropValue(self, obj, propName, val, fSelectObject=False): + """ + Update object property value and + call update function if defined. + """ + objDef = obj[OG.OBJ_DEF] + objProp = obj[OG.OBJ_PROP] + + propDef = objDef.properties[propName] + propDataType = propDef[OG.PROP_DATATYPE] + + if propDataType != OG.PROP_BLIND: + val = OG.TYPE_CONV[propDataType](val) + oldVal = objProp[propName] + + if propDef[OG.PROP_FUNC] is None: + func = None + undoFunc = None + else: + funcName = propDef[OG.PROP_FUNC][OG.FUNC_NAME] + funcArgs = propDef[OG.PROP_FUNC][OG.FUNC_ARGS] + + # populate keyword arguments + kwargs = {} + undoKwargs = {} + for key in funcArgs.keys(): + if funcArgs[key] == OG.ARG_VAL: + kwargs[key] = val + undoKwargs[key] = oldVal + elif funcArgs[key] == OG.ARG_OBJ: + undoKwargs[key] = obj + objProp[propName] = val + kwargs[key] = obj + else: + kwargs[key] = funcArgs[key] + undoKwargs[key] = funcArgs[key] + + if type(funcName) == types.StringType: + if funcName.startswith('.'): + if self.editor: + func = Functor(eval("self.editor.objectHandler%s"%funcName), **kwargs) + undoFunc = Functor(eval("self.editor.objectHandler%s"%funcName), **undoKwargs) + else: # when loaded outside of LE + func = Functor(eval("base.objectHandler%s"%funcName), **kwargs) + undoFunc = Functor(eval("base.objectHandler%s"%funcName), **undoKwargs) + else: + func = Functor(eval(funcName), **kwargs) + undoFunc = Functor(eval(funcName), **undoKwargs) + else: + func = Functor(funcName, **kwargs) + undoFunc = Functor(funcName, **undoKwargs) + + # finally call update function + #func(**kwargs) + else: + oldVal = objProp[propName] + func = None + undoFunc = None + action = ActionUpdateObjectProp(self.editor, fSelectObject, obj, propName, val, oldVal, func, undoFunc) + self.editor.actionMgr.push(action) + action() + + if self.editor: + self.editor.fNeedToSave = True + if fSelectObject: + base.direct.select(obj[OG.OBJ_NP], fUndo=0) + + 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]) + + def traverse(self, parent, parentId = None): + """ + Trasverse scene graph to gather data for saving + """ + + for child in parent.getChildren(): + if child.hasTag('OBJRoot'): + obj = self.findObjectByNodePath(child) + + if obj: + uid = obj[OG.OBJ_UID] + np = obj[OG.OBJ_NP] + objDef = obj[OG.OBJ_DEF] + objModel = obj[OG.OBJ_MODEL] + objAnim = obj[OG.OBJ_ANIM] + objProp = obj[OG.OBJ_PROP] + objRGBA = obj[OG.OBJ_RGBA] + + if parentId: + parentStr = "objects['%s']"%parentId + else: + parentStr = "None" + + if objModel: + modelStr = "'%s'"%objModel + else: + modelStr = "None" + + if objAnim: + animStr = "'%s'"%objAnim + else: + animStr = "None" + + self.saveData.append("\nobjects['%s'] = objectMgr.addNewObject('%s', '%s', %s, %s, %s, fSelectObject=False)"%(uid, objDef.name, uid, modelStr, parentStr, animStr)) + 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)) + + self.traverse(child, uid) + + def getSaveData(self): + 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] + objModel = obj[OG.OBJ_MODEL] + objAnim = obj[OG.OBJ_ANIM] + objRGBA = obj[OG.OBJ_RGBA] + + if parent is None: + parentNP = nodePath.getParent() + parentObj = self.findObjectByNodePath(parentNP) + if parentObj is None: + parent = parentNP + else: + parent = parentObj[OG.OBJ_NP] + + 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 anim info + self.updateObjectAnim(obj[OG.OBJ_ANIM], newObj, fSelectObject=False) + + # copy other properties + for key in obj[OG.OBJ_PROP]: + self.updateObjectPropValue(newObj, key, obj[OG.OBJ_PROP][key]) + 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.deselectAllCB() + for newNodePath in duplicatedNPs: + base.direct.select(newNodePath, fMultiSelect = 1, fUndo=0) + + self.editor.fNeedToSave = True + + def makeSelectedLive(self): + obj = self.findObjectByNodePath(base.direct.selected.last) + if obj: + if self.currLiveNP: + self.currLiveNP.clearColorScale() + if self.currLiveNP == obj[OG.OBJ_NP]: + self.currLiveNP = None + return + + self.currLiveNP = obj[OG.OBJ_NP] + self.currLiveNP.setColorScale(0, 1, 0, 1)