# -------------------------------------------------------------------------- # Illusoft Collada 1.4 plugin for Blender # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # # Copyright (C) 2006: Illusoft - colladablender@illusoft.com # - 2008.08: multiple bugfixes by migius (AKA Remigiusz Fiedler) # - 2009.05: bugfixes by jan (AKA Jan Diederich) # - 2009.08: bugfixed by nico (AKA Nicolai Wojke, Labor Bilderkennen Uni-Koblenz) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, # or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- # History # 2009.08.22 by nico: # - Fixed a bug where visual scene nodes containing instances of nodes in the nodes library, # which themselves instantiate geometry in the geometry library, where not imported # correctly (only the node instantiation was created, not the geometry) # - Fixed a bug where nodes in the nodes library that have children instantiating other # nodes in the nodes library where not resolved properly. Added a post-library-creation # phase where DaeInstance object references are updated after the entire library is created # - Changed nodes library syntax from 'library_NODES' to 'library_nodes' # 2009.05.17 by jan: # - More information for the user if an error happened (wrong/missing parenting). # - Added a progress bar for export (bar for import already exists). # 2009.05.11 by jan: # - Perfected the id renaming to fulfill at 100.0% the COLLADA standard for allowed UTF-8 # chars. The new renaming method is also much faster then the old one (benchmarked with # Python 2.5). # - Fixed the skeleton/joint controller! # (Corrected the referencing of the # "," # attribute. It pointed to its node, and not the array in .) # 2009.04.16 by jan: # - Added the possibility to export models with modifiers (mirrors, transformers, etc.). # - Added helpful messages for users in case something goes wrong, so they know how and # where to fix their model. # - The patch from 2008.08.31, implementing the patch from Dmitri was incomplete. # The joints were renamed (replace('.','_')), but not the vertex group names! # 2008.09.20 by migius: # - bugfix meshes with more than 16 materials: material index bigger than 15 replaced with 15. # 2008.08.31 by migius: # - added support for import IPOs interpolation type: LINEAR,BEZIER # - include patch jointVertexWeight from Dmitri: http://projects.blender.org/tracker/index.php?func=detail&aid=17427 # - non-armature-animation export/import correted # - still buggy: armatures-position and armature-animation export&import # 2008.08.04 by migius: # - bugfix/refactor localTransformMatrix usage in hierarchies: # it preserves local orientation for each child, so imported IPOs are correct working # 2008.05.08 by migius: modif. for debug mode from cutils import * import collada import Blender from Blender.Mathutils import * import math import datetime from helperObjects import * import BPyMesh import BPyObject debprn = 0 #--- print debug "print 'deb: ..." dmitri = 0 #switch for testing patch from Dmitri class Translator(object): isImporter = False def __init__(self, isImporter, version, debugM, fileName, _useTriangles, \ _usePolygons, _bakeMatrices, _exportSelection, _createNewScene, \ _clearScene, _lookAt, _exportPhysics, _exportCurrentScene, \ _exportRelativePaths, _useUV, _sampleAnimation, _onlyMainScene, \ _applyModifiers): global __version__, debugMode, usePhysics, useTriangles, usePolygons, \ bakeMatrices, exportSelection, createNewScene, clearScene, lookAt, \ replaceNames, exportPhysics, exportCurrentScene, useRelativePaths, \ useUV, sampleAnimation, onlyMainScene, applyModifiers #if debprn: print 'deb:class_Translator isImporter=', isImporter #deb--------- __version__ = version debugMode = debugM usePhysics = None useTriangles = _useTriangles usePolygons = _usePolygons bakeMatrices = _bakeMatrices exportSelection = _exportSelection createNewScene = _createNewScene clearScene = _clearScene lookAt = _lookAt exportPhysics = _exportPhysics usePhysics = _exportPhysics exportCurrentScene = _exportCurrentScene useRelativePaths = _exportRelativePaths useUV = _useUV sampleAnimation = _sampleAnimation onlyMainScene= _onlyMainScene applyModifiers= _applyModifiers replaceNames = clearScene self.isImporter = isImporter self.fileName = '' #if debprn: print 'deb:class_Translator fileName=', fileName #deb--------- if self.isImporter: self.__Import(fileName) else: self.__Export(fileName) def __Import(self,fileName=''): #if debprn: print 'deb:translator__Import fileName=', fileName #deb--------- documentTranslator = DocumentTranslator(fileName) documentTranslator.Import(fileName) def __Export(self,filename=''): documentTranslator = DocumentTranslator(filename) documentTranslator.Export(filename) class DocumentTranslator(object): isImport = None ids = [] sceneGraph = None # Keep track of the layers on import layers = None cameraLibrary = None ## geometryLibrary = None controllersLibrary = None animationsLibrary = None ## materialLibrary = None texturesLibrary = None lampsLibrary = None colladaDocument = None scenesLibrary = None fps = 25 def __init__(self, fileName): global waitingControllers, armatures # Keep track of the controller that are waiting to be applied waitingControllers = dict() # Keep track of the armatures created armatures = dict() self.isImport = False self.scenesLibrary = ScenesLibrary(self) self.sceneGraph = SceneGraph(self) self.lampsLibrary = LampsLibrary(self) self.colladaDocument = None self.texturesLibrary = TexturesLibrary(self) self.camerasLibrary = CamerasLibrary(self) self.materialsLibrary = MaterialsLibrary(self) self.meshLibrary = MeshLibrary(self) self.animationsLibrary = AnimationsLibrary(self) self.controllersLibrary = ControllersLibrary(self) self.filename = None self.filePath = '' self.currentBScene = Blender.Scene.GetCurrent() self.progressCount = 0.4 self.progressField = (1.0 - self.progressCount) self.progressStep = 0.0 self.progressPartCount = 0.0 self.tMatOLD = Matrix() self._zUpMatrix = Matrix() self._yUpMatrix = Matrix( [0,0,1,0], [1,0,0,0], [0,1,0,0], [0,0,0,1]) self.axisTransformMatrix = Matrix() self.inverseAxisTransformMatrix = Matrix() self.orgAxiss = ["X","Y","Z"] def CreateID(self, name, typeName=None): if len(name) > 0 and not name[0].isalpha(): name = "i"+name if not (name in self.ids): self.ids.append(name) return name else: tempName = name if not(typeName is None) and name.rfind(typeName) >= 0: # Check for existing number at the end? return self.IncrementString(tempName, True) else: # First check if no Blender Object exists with the name 'tempName + typeName' if (tempName + typeName) in self.allBlenderNames: return self.IncrementString(tempName + typeName, True) else: return self.CreateID(tempName+typeName, typeName) def IncrementString(self, name, checkName): tempName = name if name.rfind('.') >= 0: while tempName[-1:].isdigit(): tempName = tempName[:-1] digitStr = name[-(len(name)-len(tempName)):] digit = 1 if len(digitStr) > 0 and len(digitStr) != len(name): digit = int(digitStr)+1 newName = tempName+str(digit).zfill(3) else: newName = name+'.001' if not (newName in self.ids) and (not checkName or not (newName in self.allBlenderNames)): self.ids.append(newName) return newName else: return self.IncrementString(newName, checkName) def CreateNameForObject(self, name, replace, myType): if not replace: return name if myType == 'object': try: ob = Blender.Object.Get(name) ob.name = self.CreateNameForObject(self.IncrementString(ob.name, False), True, 'object') except ValueError: pass elif myType == 'mesh': try: mesh = Blender.Mesh.Get(name) if not mesh is None: mesh.name = self.CreateNameForObject(self.IncrementString(mesh.name, False), True, 'mesh') except ValueError: pass elif myType == 'armature': try: armature = Blender.Armature.Get(str(name)) if not armature is None: armature.name = self.CreateNameForObject(self.IncrementString(armature.name, False), True, 'armature') except ValueError: pass elif myType == 'camera': try: camera = Blender.Camera.Get(str(name)) if not camera is None: camera.name = self.CreateNameForObject(self.IncrementString(camera.name, False), True, 'camera') except NameError: pass elif myType == 'lamp': try: lamp = Blender.Lamp.Get(str(name)) if not lamp is None: lamp.name = self.CreateNameForObject(self.IncrementString(lamp.name, False), True, 'lamp') except NameError: pass return name def Import(self, fileName): global debugMode, createNewScene #if debprn: print 'deb:DocumentTranslator_Import fileName', fileName #deb--------- self.filename = fileName self.filePath = Blender.sys.dirname(self.filename) + Blender.sys.sep self.isImport = True Blender.Window.EditMode(0) Blender.Window.DrawProgressBar(0.0, 'Starting Import') # Keep track of the 20 layers self.layers = [None for x in range(20)] if createNewScene: self.currentBScene = Blender.Scene.New('Scene') self.currentBScene.makeCurrent() else: self.currentBScene = Blender.Scene.GetCurrent() # Create a new Collada document Blender.Window.DrawProgressBar(0.1, 'Get Collada Document') self.colladaDocument = collada.DaeDocument(debugMode) # Setup the libraries self.camerasLibrary.SetDaeLibrary(self.colladaDocument.camerasLibrary) self.lampsLibrary.SetDaeLibrary(self.colladaDocument.lightsLibrary) self.texturesLibrary.SetDaeLibrary(self.colladaDocument.imagesLibrary) self.materialsLibrary.SetDaeLibrary(self.colladaDocument.materialsLibrary) self.meshLibrary.SetDaeLibrary(self.colladaDocument.geometriesLibrary) self.animationsLibrary.SetDaeLibrary(self.colladaDocument.animationsLibrary) self.controllersLibrary.SetDaeLibrary(self.colladaDocument.controllersLibrary) # Parse the COLLADA file self.colladaDocument.LoadDocumentFromFile(fileName) self.axiss = ["X", "Y", "Z"] if self.colladaDocument.asset.upAxis == collada.DaeSyntax.Y_UP: self.tMatOLD[0][0] = 0 self.tMatOLD[1][1] = 0 self.tMatOLD[2][2] = 0 self.tMatOLD[0][1] = 1 self.tMatOLD[1][2] = 1 self.tMatOLD[2][0] = 1 self.axiss = ["Y", "Z", "X"] if self.colladaDocument.asset.upAxis == collada.DaeSyntax.Y_UP: self.axisTransformMatrix = Matrix(self._yUpMatrix) self.axiss = ["Y", "Z", "X"] self.inverseAxisTransformMatrix = Matrix(self.axisTransformMatrix).invert() self.progressStep = self.progressField/(self.colladaDocument.GetItemCount()+1) # Get the animation info #TODO: for what is this good? (migius) if 0: animations = AnimationInfo.CreateAnimations(self.animationsLibrary, self.fps, self.axiss) # Read the COLLADA structure and build the scene in Blender. Blender.Window.DrawProgressBar(0.4, 'Translate Collada 2 Blender') self.sceneGraph.LoadFromCollada(self.colladaDocument.visualScenesLibrary.items, self.colladaDocument.scene) self.Progress() def CalcVector(self, vector): if self.colladaDocument.asset.upAxis == collada.DaeSyntax.Y_UP: return Vector(vector[2], vector[0], vector[1]) else: return vector # Calculate the correct transform matrix dependent of the current UP axis. # When Up axis is Yup: # orgTrans * vecYup = newVecYup # newTrans * vecZup = newVecZup # vecZup = tMat * vecYUp # vecYup = invtMat * vecZup # newVecYup = orgTrans * vecYup = orgTrans * (invtMat * vecZup) # newVecZup = tMat * newVecYup = tMat * (orgTrans * (invtMat * vecZup)) # newTrans = tMat * orgTrans * invtMat def CalcMatrix(self, matrix): if self.colladaDocument.asset.upAxis == collada.DaeSyntax.Y_UP: return self.axisTransformMatrix * matrix * self.inverseAxisTransformMatrix else: return matrix def CalcMatrixReverse(self, matrix): if self.colladaDocument.asset.upAxis == collada.DaeSyntax.Y_UP: return self.inverseAxisTransformMatrix * matrix * self.axisTransformMatrix else: return matrix def Export(self, fileName): global __version__, filename, usePhysics filename = fileName self.ids = [] self.isImport = False Blender.Window.EditMode(0) Blender.Window.DrawProgressBar(0.0, 'Starting Export') # Create a new Collada document self.colladaDocument = collada.DaeDocument(debugMode) daeAsset = collada.DaeAsset() daeAsset.upAxis = 'Z_UP' daeAsset.created = datetime.datetime.today() daeAsset.modified = datetime.datetime.today() daeAsset.unit = collada.DaeUnit() daeAsset.unit.name = 'centimeter' daeAsset.unit.meter = '0.01' daeContributor = collada.DaeContributor() daeContributor.author = 'Illusoft Collada 1.4.0 plugin for Blender - http://colladablender.illusoft.com' daeContributor.authoringTool = 'Blender v:%s - Illusoft Collada Exporter v:%s' % (Blender.Get('version'), __version__) daeContributor.sourceData = GetValidFilename(Blender.Get('filename')) daeAsset.contributors.append(daeContributor) self.colladaDocument.asset = daeAsset bObjects = Blender.Object.Get() self.allBlenderNames = [x.getData(True) for x in bObjects] + [x.name for x in bObjects] daeScene = collada.DaeScene() #---------- Copied from export OBJ ----------------------------- # Get Container Mesh # Remember: is this doesn't work, due to general problems, not especially # COLLADA related, OBJ export fails also too! Keep this in sync, but remember # that OBJ then still may use "NMesh", while this already uses "Mesh". temp_mesh_name = '~tmp-mesh' # Get the container mesh. - used for applying modifiers and non mesh objects. self.containerMesh = temp_mesh = None for temp_mesh in Blender.Mesh.Get(): if temp_mesh.name.startswith(temp_mesh_name): if not temp_mesh.users: self.containerMesh = temp_mesh if not self.containerMesh: self.containerMesh = Blender.Mesh.New(temp_mesh_name) #------------ [end] Copied from export OBJ ------------------------ # Loop through all scenes sceneCount = len(Blender.Scene.Get()) self.progressStep = self.progressField / sceneCount Blender.Window.DrawProgressBar(0.1, \ 'Exporting ' + str(sceneCount) + ' scene(s)') for bScene in Blender.Scene.Get(): if not exportCurrentScene or self.currentBScene == bScene: self.fps = bScene.getRenderingContext().framesPerSec() daeInstanceVisualScene = collada.DaeVisualSceneInstance() if usePhysics: daeInstancePhysicsScene = collada.DaePhysicsSceneInstance() daeVisualScene = self.colladaDocument.visualScenesLibrary.FindObject(bScene.name) if daeVisualScene is None: sceneGraph = SceneGraph(self) scenesList = sceneGraph.SaveToDae(bScene) daeVisualScene = scenesList[0] if usePhysics: daePhysicsScene = scenesList[1] daeInstanceVisualScene.object = daeVisualScene #self.colladaDocument.visualScenesLibrary.AddItem(daeIntanceVisualScene) if self.currentBScene == bScene: daeScene.iVisualScenes.append(daeInstanceVisualScene) if usePhysics: if not (daePhysicsScene is None): daeInstancePhysicsScene.object = daePhysicsScene daeScene.iPhysicsScenes.append(daeInstancePhysicsScene) #self.colladaDocument.visualScenesLibrary.AddItem(sceneGraph.ObjectToDae(bScene)) #daeScene = collada.DaeScene() #daeScene.AddInstance() self.ProgressExport() self.colladaDocument.scene = daeScene Blender.Window.DrawProgressBar(0.98, 'Saving file to disk...') self.colladaDocument.SaveDocumentToFile(fileName) Blender.Window.DrawProgressBar(1.0, 'Export finished.') def Progress(self): self.progressPartCount = 0.0 self.progressCount += self.progressStep Blender.Window.DrawProgressBar(self.progressCount, 'Creating Blender Nodes...') def ProgressPart(self, val, text): self.progressPartCount += val Blender.Window.DrawProgressBar(self.progressCount+self.progressPartCount*self.progressStep,text) def ProgressExport(self): self.progressPartCount = 0.0 self.progressCount += self.progressStep Blender.Window.DrawProgressBar(self.progressCount, 'Exporting Blender Scenes...') def ProgressPartExport(self, val, text): self.progressPartCount += val Blender.Window.DrawProgressBar(self.progressCount+self.progressPartCount, text) class SceneGraph(object): def __init__(self, document): self.document = document self.name = '' self.childNodes = dict() self.rootNodes = [] self.objectNames = dict() # Get the current blender scene #self.currentBScene = Blender.Scene.getCurrent() def LoadFromCollada(self, visualScenes, colladaScene): global debugMode, newScene, clearScene, onlyMainScene currentBlenderSceneNames = [x.name for x in Blender.Scene.Get()] if visualScenes is None or len(visualScenes) == 0: return False # When in debugMode, delete everything from the scene before proceding if debugMode or clearScene: print "Delete everything from the scene.." bNodes = Blender.Object.Get() count = 0 newName = "TEMPSCENE" newTempName = newName colladaSceneNames = [x.name for x in visualScenes] while newTempName in currentBlenderSceneNames+colladaSceneNames: count = count + 1 newTempName = newName + str(count).zfill(3) tempScene = Blender.Scene.New(newTempName) tempScene.makeCurrent() if onlyMainScene: # delete only current scene Blender.Scene.Unlink(self.document.currentBScene) else: # delete all scenes except for the one just created for bs in Blender.Scene.Get(): if not bs.name == newTempName: Blender.Scene.Unlink(bs) self.document.currentBScene = tempScene #if colladaScene == None: # return False ##//visualScenes = colladaScene.GetVisualScenes() ##if visualScenes is None: ## return setDefaultScene = True for visualScene in visualScenes: if not onlyMainScene or (onlyMainScene and visualScene == colladaScene.GetVisualScene()): # Create a new Scene newName = visualScene.name count = 0 nameCol = [] if not clearScene: nameCol = nameCol+currentBlenderSceneNames while newName in nameCol: count = count + 1 newName = visualScene.name + str(count).zfill(3) newScene = Blender.Scene.New(newName) self.document.currentBScene = newScene if onlyMainScene or (not onlyMainScene and clearScene and not colladaScene is None): newScene.makeCurrent() setDefaultScene = False Blender.Window.DrawProgressBar(0.5,'Build the items on the scene') # loop trough all nodes for daeNode in visualScene.nodes: sceneNode = SceneNode(self.document, None) ob = sceneNode.ObjectFromDae(daeNode) if ob != None: self.objectNames[daeNode.id] = ob.name ##sceneNode.ObjectFromDae(daeNode) if visualScene == colladaScene.GetVisualScene(): # Now get the physics Scene physicsScenes = colladaScene.GetPhysicsScenes() if not (physicsScenes is None) and len(physicsScenes) > 0: # For now, only pick the fist available physics Scene physicsScene = physicsScenes[0] for iPhysicsModel in physicsScene.iPhysicsModels: physicsNode = PhysicsNode(self.document) physicsNode.LoadFromDae(iPhysicsModel, self.objectNames) if setDefaultScene: Blender.Scene.Get()[0].makeCurrent() if clearScene: Blender.Scene.Unlink(tempScene) tempScene = None # Update the current Scene. self.document.currentBScene.update(1) def SaveToDae(self, bScene): global exportSelection, usePhysics daeVisualScene = collada.DaeVisualScene() daeVisualScene.id = daeVisualScene.name = self.document.CreateID(bScene.name,'-Scene') daePhysicsScene = collada.DaePhysicsScene() daePhysicsScene.id = daePhysicsScene.name = self.document.CreateID(bScene.name+'-Physics', '-Scene') if exportSelection: self.rootNodes = Blender.Object.GetSelected() self.childNodes = [] else: # Now loop trough all nodes in this scene and create a list with # root nodes and children. sceneObjects = bScene.objects if len(sceneObjects) > 0: progressStep = self.document.progressStep / len(sceneObjects) for node in sceneObjects: pNode = node.parent if pNode is None: self.rootNodes.append(node) else: try: self.childNodes[pNode.name].append(node) except: self.childNodes[pNode.name] = [node] self.document.ProgressPartExport(progressStep, "Saving node(s)...") # Create a new Physics Model. daePhysicsModel = collada.DaePhysicsModel() daePhysicsModel.id = daePhysicsModel.name = self.document.CreateID(bScene.name,'-PhysicsModel') # Create a new Physics Model Instance. daePhysicsModelInstance = collada.DaePhysicsModelInstance() # Set the Physics Model of this instance. daePhysicsModelInstance.object = daePhysicsModel # add the physics model to the library. if usePhysics: self.document.colladaDocument.physicsModelsLibrary.items.append(daePhysicsModel) if not daePhysicsModelInstance is None: daePhysicsScene.iPhysicsModels.append(daePhysicsModelInstance) # Begin with the rootnodes for rootNode in self.rootNodes: sceneNode = SceneNode(self.document,self) nodeResult = sceneNode.SaveSceneToDae(rootNode,self.childNodes, \ daePhysicsModel,daePhysicsModelInstance,bScene) daeNode = nodeResult[0] daeVisualScene.nodes.append(daeNode) self.document.colladaDocument.visualScenesLibrary.AddItem(daeVisualScene) if usePhysics and len(daePhysicsScene.iPhysicsModels) > 0: self.document.colladaDocument.physicsScenesLibrary.AddItem(daePhysicsScene) else: daePhysicsScene = None daePhysicsModel = None return (daeVisualScene, daePhysicsScene) class PhysicsNode(object): dynamic = 0 child = 1 actor = 2 inertiaLockX = 3 inertiaLockY = 4 inertiaLockZ = 5 doFH = 6 rotFH = 7 anisotropicFriction = 8 ghost = 9 rigidBody = 10 bounds = 11 collisionResponse = 12 def __init__(self, document): self.document = document def LoadFromDae(self, daeInstancePhysicsModel, objectNames): global usePhysics if not(usePhysics is None) and not usePhysics: return for iRigidBody in daeInstancePhysicsModel.iRigidBodies: # Get the real blender name instead of the collada name. realObjectName = objectNames[iRigidBody.targetString] # Get the Blender object with the specified name. bObject = Blender.Object.Get(realObjectName) # Check if physics is supported. if usePhysics is None: usePhysics = hasattr(bObject, "rbShapeBoundType") if not usePhysics: return # Get the rigid body. rigidBody = daeInstancePhysicsModel.object.FindRigidBody(iRigidBody.bodyString) ##rigidBody = iRigidBody.body # Get the common technique of the rigid body. rigidBodyT = rigidBody.techniqueCommon # Get the physics material. physicsMaterial = rigidBodyT.GetPhysicsMaterial() # Get the shapes of the bounding volumes in this rigid body. shapes = rigidBodyT.shapes # The Rigid Body Flags rbFlags = 0 + rigidBodyT.dynamic + (1 << self.actor) + (1 << self.rigidBody) + (1 << self.bounds) rbShapeBoundType = 0 # For now only get the first shape shape = shapes[0] # Check the type of the shape if isinstance(shape, collada.DaeGeometryShape): ##print shape, shape.iGeometry.object.data if isinstance(shape.iGeometry.object.data, collada.DaeMesh): rbShapeBoundType = 4 else: rbShapeBoundType = 5 elif isinstance(shape, collada.DaeBoxShape): rbShapeBoundType = 0 elif isinstance(shape, collada.DaeSphereShape): rbShapeBoundType = 1 elif isinstance(shape, collada.DaeCylinderShape): rbShapeBoundType = 2 elif isinstance(shape, collada.DaeTaperedCylinderShape): rbShapeBoundType = 3 bObject.rbFlags = rbFlags if not (rigidBodyT.mass is None): bObject.rbMass = rigidBodyT.mass bObject.rbShapeBoundType = rbShapeBoundType class Controller(object): # TODO: class Controller: armature Pose Mode need refactoring def __init__(self, document): if debprn: print 'deb:class Controller__init__ ---RUN---' #---------- self.document = document self.bMesh = None self.daeController = None self.armatureName = None self.modifier = None self.bObject = None # Recursive method for setting the locations of the bones from their Bind matrices. def PositionBone(self, boneName, armature, bindMatrices): if debprn: print 'deb:class Controller_PositionBone() ---RUN---' #---------- # Get the boneInfo for this bone. boneInfo = armature.boneInfos[boneName] # Get the jointName for this bone. jointName = boneInfo.GetJointName() ## headJointName = boneInfo.name ## tailJointName = ## print "deb: bone:" , boneName, "- headJoint:", jointName, "- tailJoint:", boneInfo.GetTailName(), "- isEnd:", boneInfo.IsEnd() # Get the BindMatrix for the head of this bone. headMatrix = bindMatrices[jointName] # Get the name of the tail joint. tailJointName = boneInfo.GetTailName() ##armature.boneInfos[boneName].tailJointName # If there is a tail joint, get the BindMatrix for that joint. tailMatrix = bindMatrices[tailJointName] ## PrintTransforms(headMatrix, boneName) ## if not boneInfo.IsEnd(): ## tailMatrix = bindMatrices[tailJointName] # Get the location of the armature Object. armatureLocation = armature.GetLocation() # Create a vector at [0,0,0,1] nullVec = Vector().resize4D() # Calculate the position of the head headVec = self.document.CalcVector(headMatrix * nullVec).resize4D() ##- armatureLocation headVec = Matrix(armature.GetTransformation()).transpose().invert() * headVec # Set the default value for the tail. tailVec = headVec + Vector(0,0,1,1) # If this bone has a Tail joint, calculate the position of the tail. if not boneInfo.IsEnd(): tailVec = self.document.CalcVector(tailMatrix * nullVec).resize4D() ##- armatureLocation tailVec = Matrix(armature.GetTransformation()).transpose().invert() * tailVec else: parentBone = boneInfo.parent.GetBone() tailVec = 2 * headVec - Vector(parentBone.head).resize4D() ## pass#PrintTransforms(headMatrix, jointName) ##tailVec = (headMatrix * headVec)-armatureLocation ##print jointName, headVec, headMatrix ##print headVec, boneInfo.GetBone().tail - boneInfo.GetBone().head ##tailVec = headVec + (boneInfo.GetBone().tail - boneInfo.GetBone().head).resize4D() ## parentBoneInfo = boneInfo.parent ## print "parentBoneInfo:",parentBoneInfo.name ## if not parentBoneInfo is None: ## parentBone = parentBoneInfo.GetBone() ## tailVec = 2 * parentBone.tail - parentBone.head ## else: ## tailVec = Vector(0,0,1,1) ## if boneInfo.IsRoot(): ## headVec -= Vector(0,0,-0.1,1) # Set the head and tail location. ## if not boneInfo.IsEnd(): ##PrintTransforms(self.document.CalcMatrix(tailMatrix) * Matrix(armature.GetTransformation()).invert(), tailJointName) ## PrintTransforms(self.document.CalcMatrix(tailMatrix), tailJointName) ##boneInfo.GetBone().matrix = self.document.CalcMatrix(tailMatrix).transpose() * Matrix(armature.GetTransformation()).invert() ##if boneName == "root" or boneName == "pelvis" or boneName == "spine": ## print boneName ## print boneInfo.GetHead() ## print boneInfo.GetTail() ## print headVec ## print tailVec ## print boneInfo.SetHead(headVec) boneInfo.SetTail(tailVec) ##PrintTransforms(TranslationMatrix(armatureLocation).transpose(),"armature") ##PrintTransforms(self.document.CalcMatrix(headMatrix)* ## boneInfo.GetBone().matrix = self.document.CalcMatrix(headMatrix).transpose() ##* TranslationMatrix(armatureLocation).invert() # Do the same for each child. for childBoneName in boneInfo.childs: self.PositionBone(childBoneName, armature, bindMatrices) if debprn: print 'deb:class Controller_PositionBone() ---END---' #---------- def PoseBone(self, boneName, armature, bindMatrices, bPose, parentBindI = Matrix()): if debprn: print 'deb:class Controller_PoseBone() ---RUN---' #---------- #if debprn: print 'deb:class Controller_PoseBone() bindMatrices=', bindMatrices #---------- boneInfo = armature.boneInfos[boneName] #if debprn: print 'deb:class Controller_PoseBone() boneInfo =', boneInfo #---------- #if debprn: print 'deb:class Controller_PoseBone() dir(boneInfo)=', dir(boneInfo) #---------- bBone = boneInfo.GetBone() #if debprn: print 'deb:class Controller_PoseBone() bBone=', bBone #---------- jointName = boneInfo.GetJointName() #jointName = u'joint1' #if debprn: print 'deb:class Controller_PoseBone() jointName=', jointName #---------- bindMatrixCollada = bindMatrices[jointName] bindMatrixBlender = self.document.CalcMatrix(bindMatrixCollada) ## PrintTransforms(bindMatrixBlender, "bind "+boneName) bindMatrixBlenderI = Matrix(bindMatrixBlender).invert() # calculate the local transform for the current position F = boneInfo.localTransformMatrix ## PrintTransforms(F, "local transform") # calculate the transform in bindmode relative to its parent bind pose. E = parentBindI * bindMatrixBlender deltaBlender = Matrix() ## if jointName != boneName: # calculate the difference between the two transforms deltaBlender = Matrix(E).invert() * F ## PrintTransforms(deltaBlender, "delta") deltaBlenderT = Matrix(deltaBlender).transpose() # Set the transform bPose.bones[boneName].localMatrix = deltaBlenderT # Do the same for each child. for childBoneName in boneInfo.childs: self.PoseBone(childBoneName, armature, bindMatrices, bPose, bindMatrixBlenderI) if debprn: print 'deb:class Controller_PoseBone() ---end---' #---------- def AnimateBone(self, boneName, armature, bPose, action): if debprn: print 'deb:class Controller_AnimateBone() ---RUN---' #---------- boneInfo = armature.boneInfos[boneName] animationInfo = AnimationInfo.GetAnimationInfo(boneName) if not animationInfo is None: def pose_rot(anim_data): bone_rotation_matrix= Euler(anim_data[0], anim_data[1], anim_data[2]).toMatrix() bone_rotation_matrix.resize4x4() return tuple(bone_rotation_matrix.toQuat()) # qw,qx,qy,qz poseBone = bPose.bones[boneName] types = animationInfo.GetTypes(boneInfo.daeNode) for time in animationInfo.times.keys(): poseBone.insertKey(armature.GetBlenderObject(), int(round(time)), types) target = animationInfo.times[time] actionIpos = action.getAllChannelIpos() ipo = actionIpos[boneName] for time in animationInfo.times: target = animationInfo.times[time] for value in target: type = animationInfo.GetType(boneInfo.daeNode, value) if type[0] == collada.DaeSyntax.TRANSLATE: pass elif type[0] == collada.DaeSyntax.ROTATE: if type[1][1] == "ANGLE": pass ## axis = self.document.axiss[self.document.orgAxiss.index(type[1][0][-1])] ## curve_wquat = ipo[Blender.Ipo.PO_QUATW] ## curve_xquat = ipo[Blender.Ipo.PO_QUATX] ## curve_yquat = ipo[Blender.Ipo.PO_QUATY] ## pose_rotations = pose_rot([50+time,0,0]) ## print pose_rotations ## curve_wquat.append((time, pose_rotations[0])) ## curve_xquat.append((time, pose_rotations[1])) ## curve_yquat.append((time, pose_rotations[2])) ##for time in animationinfo.times.keys(): for childBoneName in boneInfo.childs: self.AnimateBone(childBoneName,armature, bPose, action) if debprn: print 'deb:class Controller_AnimateBone() ---end---' #---------- def BindMesh(self): if debprn: print 'deb:class Controller_BindMesh() ---RUN---' #---------- global waitingControllers, armatures armature = Armature.GetArmature(self.armatureName) realArmatureName = armature.realName armatureObject = armature.GetBlenderObject() self.modifier[Blender.Modifier.Settings.OBJECT] = armatureObject bArmature = armature.GetBlenderArmature() daeSkin = self.daeController.skin # For each bone create a vertex group. for boneName in armatureObject.data.bones.keys(): self.bMesh.addVertGroup(boneName) vertexWeights = daeSkin.vertexWeights daeJoints = daeSkin.joints if not vertexWeights.vcount is None and not vertexWeights.v is None: # Get the Joint Source jointList = daeSkin.FindSource(vertexWeights.FindInput('JOINT')).source.data #if debprn: print 'deb: jointList=', jointList #--------------- #if debprn: print 'deb: jointList[0]=', jointList[0], type(jointList[0] #--------------- # Get the weights weightList = daeSkin.FindSource(vertexWeights.FindInput('WEIGHT')).source.data # Get the BindMatrix values bindFloats = daeSkin.FindSource(daeJoints.FindInput("INV_BIND_MATRIX")).source.data # Create the BindMatrices bindMatrices = dict() for jointNameIndex in range(len(jointList)): jointName = jointList[jointNameIndex] bindMatrix = ToMatrix4(bindFloats[jointNameIndex*16:(jointNameIndex+1)*16]).invert() bindMatrices[jointName] = bindMatrix bPose = armatureObject.getPose() # Loop trough each Root bone. Those bones will position their childs. for rootBoneName in armature.rootBoneInfos: self.PoseBone(rootBoneName, armature, bindMatrices, bPose) # Set the bind positions of the bones (in Edit mode) armature.MakeEditable(True) # Loop trough each Root bone. Those bones will position their childs. for rootBoneName in armature.rootBoneInfos: self.PositionBone(rootBoneName, armature, bindMatrices) armature.MakeEditable(False) vIndex = 0 vertInfos = [] maxInputOffset = vertexWeights.GetStride() for vCountIndex in range(len(vertexWeights.vcount)): # loop trough each element in vcount. vcount = vertexWeights.vcount[vCountIndex] vertInfo = [vCountIndex,[]] for jointIndex in range(vcount): # Get all the info for this vertice. vertInfo[1].append([0,0]) vertJointInfo = vertInfo[1][jointIndex] for input in vertexWeights.inputs: inputVal = vertexWeights.v[vIndex+input.offset] if input.semantic == "JOINT": vertJointInfo[0] = jointList[inputVal] elif input.semantic == "WEIGHT": vertJointInfo[1] = weightList[inputVal] vIndex += maxInputOffset vertInfos.append(vertInfo) bonesList = bArmature.bones.keys() jointBoneInfoList = armature.GetJointList() for vertInfo in vertInfos: for vertJointInfo in vertInfo[1]: groupName = vertJointInfo[0] ## if groupName in jointBoneInfoList: ##boneName = jointBoneInfoList[groupName].name boneName = groupName vertIndex = vertInfo[0] vertWeight = vertJointInfo[1] self.bMesh.assignVertsToGroup(boneName,[vertIndex],vertWeight,'add') action = Blender.Armature.NLA.NewAction("Action") action.setActive(armature.GetBlenderObject()) # Apply the animations for the bones (if available) for rootBoneName in armature.rootBoneInfos: pass#self.AnimateBone(rootBoneName, armature, bPose, action) if debprn: print 'deb:class Controller_BindMesh() ---end---' #---------- def LoadFromDae(self, daeController, daeControllerInstance, bObject): if debprn: print 'deb:class Controller_LoadFromDae() ---RUN---' #---------- global waitingControllers # Check if this controller is a SKIN controller if not (daeController.skin is None): # Create a new GeometryInstance for getting the mesh for this controller. daeGeoInstance = collada.DaeGeometryInstance() daeGeoInstance.url = daeController.skin.source daeGeoInstance.bindMaterials = daeControllerInstance.bindMaterials # Get the Blender Mesh self.bMesh = self.document.meshLibrary.FindObject(daeGeoInstance, True) self.daeController = daeController # Link the mesh to the blenderObject. bObject.link(self.bMesh) # Set the bObject to the right place. bindShapeMatrix = daeController.skin.bindShapeMatrix bindShapeMatrix = self.document.CalcMatrix(bindShapeMatrix) bObject.setMatrix(bindShapeMatrix) # Create an armature modifier for the Blender Object. self.modifier = bObject.modifiers.append(Blender.Modifier.Type.ARMATURE) # Set the object skinned to this armature self.bObject = bObject # Find the armature for this controller armature = None for jointName in daeControllerInstance.skeletons: armature = Armature.FindArmatureWithJoint(jointName[1:]) if not armature is None: self.armatureName = armature.name break # Check if the armature is already parsed. If not, wait until then. if not armature is None: # make the armature the parent of the blender object armature.GetBlenderObject().makeParent([bObject], 0 , 1) self.BindMesh() else: # Add this controller to a to-do list ;) and wait until the armature is parsed. ##waitingControllers[self.armatureName] = self waitingControllers[jointName[1:]] = self #return the mesh. return self.bMesh else: # It's a morph controller. do nothing. print "WARNING: Morph is not supported" return None if debprn: print 'deb:class Controller_LoadFromDae() ---end---' #---------- def SaveToDae(self, bModifier, bMeshObject, meshName): bMesh = bMeshObject.getData() daeController = collada.DaeController() daeController.id = self.document.CreateID(bMesh.name,"-skin") # Create a skin daeController.skin = daeSkin = collada.DaeSkin() daeSkin.source = meshName # Set the bindshapematrix daeSkin.bindShapeMatrix = Matrix(bMeshObject.matrix).transpose()##bMeshObject.getMatrix('localspace').transpose() bArmatureObject = bModifier[Blender.Modifier.Settings.OBJECT] if (bArmatureObject is None): HandleError(ERROR_MESH_ARMATURE_PARENT, meshName) bArmature = bArmatureObject.data # Create the joints elements daeSkin.joints = collada.DaeJoints() # Create the vertexWeights element daeSkin.vertexWeights = collada.DaeVertexWeights() # Create the source for the joints jointSource = collada.DaeSource() jointSource.id = self.document.CreateID(daeController.id, "-joints") jointSource.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() jointSource.source = jointSourceArray = collada.DaeIDREFArray() jointSourceArray.id = self.document.CreateID(jointSource.id, "-array") jointSource.techniqueCommon.accessor = jointAccessor = collada.DaeAccessor() jointAccessor.AddParam("JOINT",collada.DaeSyntax.IDREF) jointAccessor.source = jointSourceArray.id daeSkin.sources.append(jointSource) # And the input for the joints jointInput = collada.DaeInput() jointInput.semantic = "JOINT" jointInput.source = jointSource.id jointInput.offset = 0 jointInput2 = collada.DaeInput() jointInput2.semantic = "JOINT" jointInput2.source = jointSource.id # Create the source for the weights weightSource = collada.DaeSource() weightSource.id = self.document.CreateID(daeController.id, "-weights") weightSource.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() weightSource.source = weightSourceArray = collada.DaeFloatArray() weightSourceArray.id = self.document.CreateID(weightSource.id, "-array") weightSource.techniqueCommon.accessor = weightAccessor = collada.DaeAccessor() weightAccessor.AddParam("WEIGHT","float") weightAccessor.source = weightSourceArray.id daeSkin.sources.append(weightSource) # And the input for the weights weightInput = collada.DaeInput() weightInput.semantic = "WEIGHT" weightInput.source = weightSource.id weightInput.offset = 1 daeSkin.joints.inputs.append(jointInput2) daeSkin.vertexWeights.inputs.append(jointInput) daeSkin.vertexWeights.inputs.append(weightInput) poseSource = collada.DaeSource() poseSource.id = self.document.CreateID(daeController.id,"-poses") poseSource.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() poseSource.source = poseSourceArray = collada.DaeFloatArray() poseSourceArray.id = self.document.CreateID(poseSource.id,"-array") poseSource.techniqueCommon.accessor = poseAccessor = collada.DaeAccessor() poseAccessor.AddParam("","float4x4") poseAccessor.stride = 16 poseAccessor.source = poseSourceArray.id daeSkin.sources.append(poseSource) # Add the input for the poses poseInput = collada.DaeInput() poseInput.semantic = "INV_BIND_MATRIX" poseInput.source = poseSource.id # Add this input to the joints daeSkin.joints.inputs.append(poseInput) # Get all vertextGroups vGroups = dict() for vertexGroupName in bMesh.getVertGroupNames(): vwsdict = vGroups[vertexGroupName] = dict() try: vws = bMesh.getVertsFromGroup(vertexGroupName,True) for vw in vws: vwsdict[vw[0]] = vw[1] except: print("Vertex group '%s' couldn't be handled.\n" \ "Maybe the group is empty.\n" % vertexGroupName) # Loop through each vertex group. for vertexGroupName in bMesh.getVertGroupNames(): # Check if this vertexgroup has the same name as a bone in the armature. if vertexGroupName in bArmature.bones.keys(): jointAccessor.count += 1 adjustedName = "" + vertexGroupName # Jan: If we rename the bones/joints/armatures, we must rename the # (corresponding) vertex groups also. adjustedName = AdjustName(adjustedName) jointSourceArray.data.append(adjustedName) # Get the vertices in this vertexGroup verts = bMesh.getVertsFromGroup(vertexGroupName) ## print ## PrintTransforms(Matrix(bArmature.bones[vertexGroupName].matrix['ARMATURESPACE']).transpose().invert(), vertexGroupName) if 0: bindMatrix = Matrix(bArmature.bones[vertexGroupName].matrix['ARMATURESPACE']).transpose() bindMatrix = Matrix(bMeshObject.matrix).transpose() * bindMatrix elif dmitri: #by dmitri: Use ARAMATURE matrix for a global position/orientation bindMatrix = Matrix(bArmature.bones[vertexGroupName].matrix["ARMATURESPACE"]).resize4x4().transpose() bindMatrix = Matrix(bArmatureObject.getMatrix('localspace')).transpose() * bindMatrix else: headPos = bArmature.bones[vertexGroupName].head["ARMATURESPACE"] bindMatrix = Matrix([1,0,0,headPos.x], [0,1,0,headPos.y], [0,0,1,headPos.z],[0,0,0,1]) bindMatrix = Matrix(bArmatureObject.getMatrix('localspace')).transpose() * bindMatrix invBindMatrix = Matrix(bindMatrix).invert() poseSourceArray.data.extend(MatrixToList(invBindMatrix)) poseAccessor.count += 1 for vert in verts: weightAccessor.count += 1 vertJointCount = dict() weightIndex = 0 for vert in bMesh.verts: jointCount = 0 vertTotalWeight = 0.0 jointVertexWeight = dict() # Jan: Keep this bone/joints renaming always in snyc with the vertex group # renaming! #count up the number of joints to get an equal weight for vGroup in vGroups: if vert.index in vGroups[vGroup]: adjustedName = "" + vGroup adjustedName = AdjustName(adjustedName) found = False weight = 0.0 try : jointSourceArray.data.index(adjustedName) vi = vGroups[vGroup] weight = vi[vert.index] found = True except: found = False if found : jointCount += 1 vertJointCount[vert.index] = jointCount jointVertexWeight[adjustedName] = weight vertTotalWeight+= weight #now we know how many, so make an even weight: # !!! Update cast3d !!! - even weight distribution only used if vertex total weight == 0 # otherwise used each joint weight normalised by total weight for vGroup in vGroups: if vert.index in vGroups[vGroup]: adjustedName = "" + vGroup adjustedName = AdjustName(adjustedName) found = False try : jointSourceArray.data.index(adjustedName) found = True except: found = False if found : daeSkin.vertexWeights.v.append(jointSourceArray.data.index(adjustedName)) daeSkin.vertexWeights.v.append(weightIndex) if vertTotalWeight != 0.0: jw = jointVertexWeight[adjustedName] vw = jw / vertTotalWeight weightSourceArray.data.append(vw) #print "Joint ", adjustedName, " weight=", jw, "Total weight=",vertTotalWeight, " Final weight=", vw else: weightSourceArray.data.append( 1.0 / vertJointCount[vert.index]) weightIndex += 1 #update the counts for this vertex if jointCount > 0: daeSkin.vertexWeights.count += 1 daeSkin.vertexWeights.vcount.append(jointCount) self.document.colladaDocument.controllersLibrary.AddItem(daeController) return daeController class Animation(object): def __init__(self, document): self.document = document def LoadFromDae(self, daeAnimation, daeNode, bObject): if debprn: print 'deb:class Animation_LoadFromDae RUN-------:' #------ # Loop trough all channels for channel in daeAnimation.channels: if debprn: print 'deb: channel.target=',channel.target #------ if debprn: print 'deb: daeNode.id=',daeNode.id #------ ca = channel.target.split("/",1) #if it targets to this daeNode if ca[0] == daeNode.id: for s in daeAnimation.samplers: if debprn: print 'deb: s.id=',s.id #------ #if debprn: print 'deb: channel.source=',channel.source #------ #org if s.id == channel.source[1:]: if s.id == channel.source: if debprn: print 'deb: BINGO ---> channel.source=s.id' #------ sampler = s if s!=None: #TODO: Animation_bObject sometimes is None if bObject.ipo is None: ipo = Blender.Ipo.New("Object",daeAnimation.id) bObject.setIpo(ipo) else: ipo = bObject.ipo type = self.FindType(ca[1], daeNode) input = sampler.GetInput("INPUT") inputSource = daeAnimation.GetSource(input.source) # Check if the input has a TIME parameter and is the only one. if not (type is None) and inputSource.techniqueCommon.accessor.HasParam("TIME") and len(inputSource.techniqueCommon.accessor.params) == 1: output = sampler.GetInput("OUTPUT") outputSource = daeAnimation.GetSource(output.source) accessorCount = outputSource.techniqueCommon.accessor.count accessorStride = outputSource.techniqueCommon.accessor.stride interpolations = sampler.GetInput("INTERPOLATION") interpolationsSource = daeAnimation.GetSource(interpolations.source) times = [x * self.document.fps for x in inputSource.source.data] if type[0] == "translate" or type[0] == "scale" or (type[0] == "rotate" and type[1][1] == "ANGLE"): axiss = [] if len(type[1]) == 1: axiss = ["X", "Y", "Z"] elif type[0] == "rotate": axiss = [type[1][0][-1]] elif len(type[1]) == 2 and type[1][1] in ["X", "Y", "Z"]: axiss = [type[1][1]] for axis in axiss: if type[0] == "translate": cname = "Loc" elif type[0] == "scale": cname = "Scale" elif type[0] == "rotate": cname = "Rot" cname += self.document.axiss[self.document.orgAxiss.index(axis)] curve = ipo.addCurve(cname) curve.interpolation = 1 #LINEAR for time in times: value = outputSource.source.data[times.index(time) * accessorStride + axiss.index(axis)] if type[0] == "rotate": value /= 10 #old curve.addBezier((time,value)) inter = interpolationsSource.source.data[times.index(time) * accessorStride + axiss.index(axis)] if inter=='BEZIER': point=Blender.BezTriple.New() point.pt=(time, value) point.handleTypes=[1,1] else: #if inter=='LINEAR': inter=1 point=(time, value) curve.append(point) if 1: #TODO: need individual support interpolation type for each point #if debprn: print 'deb: dir(curve)=', dir(curve) #-------- if inter=='LINEAR': inter = 1 elif inter=='BEZIER': inter = 2 else: inter = 1 curve.interpolation = inter #if debprn: print 'deb: inter=', inter #-------- #if debprn: print 'deb: curve.interpolation=', curve.interpolation #-------- def FindType(self, target, daeNode): ta = target.split(".",1) for t in daeNode.transforms: if t[2] == ta[0]: return [t[0],ta] return None def GetEulerAnimations(self, ipo, targetDaeNode, joint=None, bPose=None, bParentMatrix=None, bArmatureObject=None): curves = ipo.getCurves() if not curves is None: quatXList = dict() quatYList = dict() quatZList = dict() quatWList = dict() #collect quats quatKey = dict() for cur in curves: curName = cur.getName() if curName.startswith("Quat"): quatKey[curName] = [] curNameIndex = curName[-1] if curNameIndex == 'X': for point in cur.bezierPoints: quatXList[point.pt[0]] = point.pt[1] elif curNameIndex == 'Y': for point in cur.bezierPoints: quatYList[point.pt[0]] = point.pt[1] elif curNameIndex == 'Z': for point in cur.bezierPoints: quatZList[point.pt[0]] = point.pt[1] elif curNameIndex == 'W': for point in cur.bezierPoints: quatWList[point.pt[0]] = point.pt[1] quats = dict() eulers = dict() xKeyList = quatXList.keys() yKeyList = quatYList.keys() zKeyList = quatZList.keys() wKeyList = quatWList.keys() #Assumption: All the keys are the same!! for xKey in xKeyList: if not quats.has_key(xKey): quats[xKey] = Quaternion() #assign value for key in xKeyList: quats[key].x = quatXList[key] for key in yKeyList: quats[key].y = quatYList[key] for key in zKeyList: quats[key].z = quatZList[key] for key in wKeyList: quats[key].w = quatWList[key] for key in quats: euler = quats[key].toEuler() if joint is not None: if dmitri: bindMatrix = Matrix(joint.matrix["ARMATURESPACE"]).resize4x4().transpose() else: headPos = joint.head["ARMATURESPACE"] bindMatrix = Matrix([1,0,0,headPos.x], [0,1,0,headPos.y], [0,0,1,headPos.z],[0,0,0,1]) armMatrix = Matrix(bindMatrix) if not joint.hasParent(): armMatrix = Matrix(bArmatureObject.getMatrix('localspace')).transpose().invert() armMatrix *= bindMatrix if 1: #migius swap = euler.y euler.y = - euler.z euler.z = swap else: poseMatrix = Matrix(bParentMatrix).invert() * armMatrix poseMatrix.transpose() poseEuler = poseMatrix.toEuler() euler.x += poseEuler.x euler.y += poseEuler.y euler.z += poseEuler.z #if debprn: print 'deb: getEuler: ', joint.name , poseEuler, euler eulers[key] = euler # this nodes list of euler angles: return eulers return None def SaveToDae(self, ipo, targetDaeNode, joint=None, bPose=None, bParentMatrix=None, bArmatureObject=None): global sampleAnimation animations = None curves = ipo.getCurves() if not curves is None: animations = dict() for curve in curves: cName = curve.getName() interpolation = curve.getInterpolation() #interpolation = curve.interpolation if debprn: print 'deb: interpolation=', interpolation #-------- if cName.startswith("Loc") or cName.startswith("Rot") or cName.startswith("Scale"): if cName.startswith("Loc"): n = collada.DaeSyntax.TRANSLATE elif cName.startswith("Scale"): n = collada.DaeSyntax.SCALE else: n = collada.DaeSyntax.ROTATE+cName[-1] ani = animations.setdefault(n,{}) # Get all the framenumbers for the current curve. frames = [bp.pt[0] for bp in curve.bezierPoints] if sampleAnimation: ## if the users wants to sample the animation each frame.. # .. generate a sequence of frames, starting at the first(smallest) framenumber of the current curve # and ending at the last(largest)framenumber. frames = range(min(frames), max(frames)+1) ##print cName, frames # Now get the values for each frame for frameNumber in frames: anit = ani.setdefault(float(frameNumber),{'X':None, 'Y':None, 'Z':None, 'interpolation':interpolation}) # calculate the value at the current frame. timeVal = float(curve[float(frameNumber)]) ##print cName, frameNumber, timeVal anit[cName[-1]] = timeVal if not joint is None: if dmitri: bindMatrix = Matrix(joint.matrix["ARMATURESPACE"]).resize4x4().transpose() else: headPos = joint.head["ARMATURESPACE"] bindMatrix = Matrix([1,0,0,headPos.x], [0,1,0,headPos.y], [0,0,1,headPos.z],[0,0,0,1]) armMatrix = bindMatrix if ( not joint.hasParent() ): armMatrix = Matrix(bArmatureObject.getMatrix('localspace')).transpose() armMatrix *= bindMatrix poseMatrix = Matrix(bParentMatrix).invert() * armMatrix if cName.startswith("Loc"): poseMatrix.transpose() jointPosition = poseMatrix.translationPart() if cName[-1] == 'X': anit['X'] += jointPosition.x if cName[-1] == 'Y': anit['Y'] += jointPosition.y if cName[-1] == 'Z': anit['Z'] += jointPosition.z if cName.startswith("Scale"): poseMatrix.transpose() jointPosition = poseMatrix.scalePart() if cName[-1] == 'X': anit['X'] *= jointPosition.x if cName[-1] == 'Y': anit['Y'] *= jointPosition.y if cName[-1] == 'Z': anit['Z'] *= jointPosition.z if cName.startswith("Rot"): anit[cName[-1]] = anit[cName[-1]]*10 # Multiply the angle times 10 (Blender uses angle/10) else: pass eulers = self.GetEulerAnimations(ipo, targetDaeNode, joint, bPose, bParentMatrix, bArmatureObject) eulerKeys = eulers.keys() for key in eulerKeys: euler = eulers[key] aniX = animations.setdefault(str(collada.DaeSyntax.ROTATE) + 'X',{}) aniY = animations.setdefault(str(collada.DaeSyntax.ROTATE) + 'Y',{}) aniZ = animations.setdefault(str(collada.DaeSyntax.ROTATE) + 'Z',{}) anitx = aniX.setdefault(key,{'X':0, 'Y':0, 'Z':0, 'interpolation':interpolation}) anitx['X'] = euler.x anitx['Y'] = euler.y anitx['Z'] = euler.z anity = aniY.setdefault(key,{'X':0, 'Y':0, 'Z':0, 'interpolation':interpolation}) anity['X'] = euler.x anity['Y'] = euler.y anity['Z'] = euler.z anitz = aniZ.setdefault(key,{'X':0, 'Y':0, 'Z':0, 'interpolation':interpolation}) anitz['X'] = euler.x anitz['Y'] = euler.y anitz['Z'] = euler.z # add animations to collada for name, animation in animations.iteritems(): daeAnimation = collada.DaeAnimation() daeAnimation.id = daeAnimation.name = self.document.CreateID(targetDaeNode.id+'-'+name,'-Animation') daeSourceInput = collada.DaeSource() daeSourceInput.id = self.document.CreateID(daeAnimation.id,'-input') daeFloatArrayInput = collada.DaeFloatArray() daeFloatArrayInput.id = self.document.CreateID(daeSourceInput.id,'-array') daeSourceInput.source = daeFloatArrayInput daeSourceInput.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() accessorInput = collada.DaeAccessor() daeSourceInput.techniqueCommon.accessor = accessorInput accessorInput.source = daeFloatArrayInput.id accessorInput.count = len(animation) accessorInput.AddParam('TIME','float') if name == collada.DaeSyntax.TRANSLATE or name == collada.DaeSyntax.SCALE: vals = self.CreateSourceOutput(daeAnimation, len(animation), "xyz") elif name.startswith(collada.DaeSyntax.ROTATE): vals = self.CreateSourceOutput(daeAnimation, len(animation), "angle") daeSourceOutput = vals[0] outputArray = vals[1] daeSourceInterpolation = vals[2] interpolationArray = vals[3] daeAnimation.sources.append(daeSourceInput) daeAnimation.sources.append(daeSourceOutput) daeAnimation.sources.append(daeSourceInterpolation) animationKeys = animation.keys() animationKeys.sort() prevX = prevY = prevZ = 0 for key in animationKeys: value = animation[key] daeFloatArrayInput.data.append(key/self.document.fps) interpolation = value['interpolation'] if interpolation == 'Constant': cInterpolation = 'STEP' elif interpolation == 'Linear': cInterpolation = 'LINEAR' else: cInterpolation = 'BEZIER' if name == collada.DaeSyntax.TRANSLATE or name == collada.DaeSyntax.SCALE: if value['X'] is None: value['X'] = prevX else: prevX = value['X'] if value['Y'] is None: value['Y'] = prevY else: prevY = value['Y'] if value['Z'] is None: value['Z'] = prevZ else: prevZ = value['Z'] outputArray.data.append(value['X']) outputArray.data.append(value['Y']) outputArray.data.append(value['Z']) interpolationArray.data.append(cInterpolation) interpolationArray.data.append(cInterpolation) interpolationArray.data.append(cInterpolation) elif name.startswith(collada.DaeSyntax.ROTATE): outputArray.data.append(value[name[-1]]) interpolationArray.data.append(cInterpolation) #if not name.startswith(collada.DaeSyntax.ROTATE) or sum(outputArray.data) != 0: #dimitr: rotation could be full circle, can not use sum(outputArray.data) != 0: if not name.startswith(collada.DaeSyntax.ROTATE) or len(animation) > 0: daeSampler = collada.DaeSampler() daeSampler.id = self.document.CreateID(daeAnimation.id,"-sampler") daeAnimation.samplers.append(daeSampler) daeInputInput = collada.DaeInput() daeInputInput.semantic = 'INPUT' daeInputInput.source = daeSourceInput.id daeSampler.inputs.append(daeInputInput) daeInputOutput = collada.DaeInput() daeInputOutput.semantic = 'OUTPUT' daeInputOutput.source = daeSourceOutput.id daeSampler.inputs.append(daeInputOutput) daeInputInterpolation = collada.DaeInput() daeInputInterpolation.semantic = 'INTERPOLATION' daeInputInterpolation.source = daeSourceInterpolation.id daeSampler.inputs.append(daeInputInterpolation) daeChannel = collada.DaeChannel() daeChannel.source = daeSampler daeChannel.target = collada.StripString(targetDaeNode.id) +'/'+name if name.startswith(collada.DaeSyntax.ROTATE): daeChannel.target = daeChannel.target + ".ANGLE" daeAnimation.channels.append(daeChannel) self.document.colladaDocument.animationsLibrary.AddItem(daeAnimation) def CreateSourceOutput(self, daeAnimation, count, type): daeSourceOutput = collada.DaeSource() daeSourceOutput.id = self.document.CreateID(daeAnimation.id,'-output') if type == "xyz" or "angle": outputArray = collada.DaeFloatArray() else: pass ##daeFloatArrayOutput = collada.DaeFloatArray() outputArray.id = self.document.CreateID(daeSourceOutput.id,'-array') daeSourceOutput.source = outputArray daeSourceOutput.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() accessorOutput = collada.DaeAccessor() daeSourceOutput.techniqueCommon.accessor = accessorOutput accessorOutput.source = outputArray.id accessorOutput.count = count daeSourceInterpolation = collada.DaeSource() daeSourceInterpolation.id = self.document.CreateID(daeAnimation.id,'-interpolation') interpolationArray = collada.DaeNameArray() interpolationArray.id = self.document.CreateID(daeSourceInterpolation.id,'-array') daeSourceInterpolation.source = interpolationArray daeSourceInterpolation.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() accessorInterpolation = collada.DaeAccessor() daeSourceInterpolation.techniqueCommon.accessor = accessorInterpolation accessorInterpolation.source = interpolationArray.id accessorInterpolation.count = count if type == "xyz": accessorOutput.AddParam('X','float') accessorOutput.AddParam('Y','float') accessorOutput.AddParam('Z','float') accessorInterpolation.AddParam('X','Name') accessorInterpolation.AddParam('Y','Name') accessorInterpolation.AddParam('Z','Name') elif type == "angle": accessorOutput.AddParam('ANGLE','float') accessorInterpolation.AddParam('ANGLE','Name') return [daeSourceOutput, outputArray, daeSourceInterpolation, interpolationArray] class SceneNode(object): def __init__(self, document, sceneNode): self.id = '' self.type = '' self.document = document self.transformMatrix = None self.localTransformMatrix = None self.parentNode = sceneNode self.armature = None self.isJoint = False self.createRootBone = False self.createLastBone = False def ObjectFromDae(self,daeNode): if debprn: print 'deb:class SceneNode_ObjectFromDae ---RUN---' #------------ global replaceNames, objectList, waitingControllers, armatures self.document.Progress() self.id = daeNode.id self.name = daeNode.name self.type = daeNode.type if debprn: print 'deb: daeNode.id =', daeNode.id #------------ if debprn: print 'deb: daeNode.name=', daeNode.name #------------ if debprn: print 'deb: daeNode.type=', daeNode.type #------------ editBone = None newObject = None dataObject = None armature = None parentBone = None daeInstance = None boneName = None noninverse = 0 #Get the transformation # TODO: replace all the self.document.tMatOLD and calculate the transformmatrices the correct way using CalcMatrix() mat = Matrix().resize4x4() for i in range(len(daeNode.transforms)): transform = daeNode.transforms[len(daeNode.transforms)-(i+1)] type = transform[0] data = transform[1] if type == collada.DaeSyntax.TRANSLATE: mat = mat*TranslationMatrix(Vector(data)* self.document.tMatOLD) elif type == collada.DaeSyntax.ROTATE: mat = mat*RotationMatrix(data[3] % 360,4,'r',Vector([data[0],data[1],data[2]])* self.document.tMatOLD) elif type == collada.DaeSyntax.SCALE: skewVec = Vector(data[0],data[1], data[2])*self.document.tMatOLD mat = mat * Matrix([skewVec.x,0,0,0],[0,skewVec.y,0,0],[0,0,skewVec.z,0],[0,0,0,1]) elif type == collada.DaeSyntax.SKEW: s = math.tan(data[0]*angleToRadian) rotVec = Vector(data[1],data[2],data[3])*self.document.tMatOLD transVec = Vector(data[4],data[5],data[6])*self.document.tMatOLD fac1 = s*transVec.x fac2 = s*transVec.y fac3 = s*transVec.z mat = mat * Matrix([1+fac1*rotVec.x,fac1*rotVec.y,fac1*rotVec.z,0],[fac2*rotVec.x,1+fac2*rotVec.y,fac2*rotVec.z,0],[fac3*rotVec.x,fac3*rotVec.y,1+fac3*rotVec.z,0],[0,0,0,1]) elif type == collada.DaeSyntax.LOOKAT: # TODO: LOOKAT Transform: use the correct up-axis position = Vector([data[0],data[1], data[2]]) target = Vector([data[3],data[4], data[5]]) up = Vector([data[6],data[7], data[8]]).normalize() front = (position-target).normalize() side = -1*CrossVecs(front, up).normalize() m = Matrix().resize4x4() m[0][0] = side.x m[0][1] = side.y m[0][2] = side.z m[1][0] = up.x m[1][1] = up.y m[1][2] = up.z m[2][0] = front.x m[2][1] = front.y m[2][2] = front.z m[3][0] = position.x m[3][1] = position.y m[3][2] = position.z mat = mat*m elif type == collada.DaeSyntax.MATRIX: mat = mat * self.document.CalcMatrix(data)## * self.document.tMatOLD self.localTransformMatrix = Matrix(mat) #if debprn: print 'deb: localTransformMatrix:\n' , self.localTransformMatrix #--------- self.transformMatrix = mat if daeNode.IsJoint():#---daeNode is Joint if isinstance(self.parentNode, SceneNode): self.transformMatrix = self.transformMatrix * self.parentNode.transformMatrix #if debprn: print 'deb: transformMatrix:\n' , self.transformMatrix #-------- currentBoneExists = False self.isJoint = True # it's a Joint, so check if we have to create a new armature or a bone inside an existing armature. if daeNode.parentNode == None or not daeNode.parentNode.IsJoint(): # Create a unique name for the armature object #old objectName = self.document.CreateNameForObject(self.id, replaceNames, 'object') objectName = self.document.CreateNameForObject(self.name, replaceNames, 'object') # Create a unique name for the armature data armatureName = self.document.CreateNameForObject(objectName, replaceNames, 'armature') # Create a new armature #old self.armature = Armature.CreateArmature(objectName, self.id, armatureName, daeNode) self.armature = Armature.CreateArmature(objectName, self.name, armatureName, daeNode) ##print "create armature", armatureName # Get the new created Blender Object newObject = self.armature.GetBlenderObject() # Link the new object to the current scene. self.document.currentBScene.link(newObject) # Set the position of the armature. newObject.setMatrix(self.transformMatrix) else: self.armature = self.parentNode.armature # Make the armature editable, so we can create a new bone. self.armature.MakeEditable(True) # Get the name of the parent bone of this bone. parentBoneName = None if isinstance(self.parentNode.parentNode, SceneNode): #old parentBoneName = str(self.parentNode.parentNode.id) parentBoneName = str(self.parentNode.parentNode.name) # Create the name for the new bone #old boneName = str(self.parentNode.id) boneName = str(self.parentNode.name) if not self.armature.HasBone(boneName): # Add a new bone to the armature. boneInfo = self.armature.AddNewBone(boneName, parentBoneName, daeNode) # Get the location of the armature. armatureLoc = self.armature.GetLocation() # Set the correct head and tail positions of this bone. headLoc = Vector(0,0,0,1) if not boneInfo.parent is None: # The head of this bone starts at the end of it's parent. ## headLoc = Matrix(self.parentNode.transformMatrix).transpose() * Vector(0,0,0,1) - armatureLoc headLoc = Matrix(self.parentNode.transformMatrix).transpose() * Vector(0,0,0,1) # Check if the head of this bone is at the same position as the tail of it's parent. # Get the location of this node. nodeLoc = Matrix(self.transformMatrix).transpose() * Vector(0,0,0,1) ## tailLoc = (nodeLoc - armatureLoc).resize3D() if headLoc == nodeLoc: print "zero bone:", boneName ##print headLoc, nodeLoc ## if (headLoc - nodeLoc).length < 0.001: nodeLoc += Vector(0,0,0.001,1) tailLoc = Matrix(self.armature.GetTransformation()).transpose().invert() * nodeLoc.resize4D() ## print boneName ## print "headLoc PRE" ,headLoc # Undo the armature transformation if not boneInfo.parent is None: headLoc = Matrix(self.armature.GetTransformation()).transpose().invert() * headLoc.resize4D() if (Vector(headLoc).resize3D() - boneInfo.parent.GetTail()).length < 0.001: boneInfo.SetConnected() ## print "headLoc POST", headLoc ## PrintTransforms(Matrix(self.armature.GetTransformation()).transpose().invert(),"armature") ## print "invert origin", Matrix(self.armature.GetTransformation()).transpose().invert() * Vector(0,0,0,1) ## print "nodeLoc PRE:",nodeLoc ## print "tailLoc POST:", tailLoc ## print # Set the location of the tail to the difference between the NodeLoc and armatureLoc boneInfo.SetHead(headLoc) boneInfo.SetTail(tailLoc) # Store the localTransformMatrix of this joint boneInfo.localTransformMatrix = Matrix(self.parentNode.localTransformMatrix).transpose() boneInfo.worldTransformMatrix = Matrix(self.parentNode.transformMatrix).transpose() else: currentBoneExists = True self.armature.MakeEditable(False) # Check if this is the last Joint hasJointChilds = False for daeChild in daeNode.nodes: if daeChild.IsJoint(): hasJointChilds = True break #old if not hasJointChilds: if not hasJointChilds and not self.parentNode==None: # This is the last joint of the armature. # Create one last bone for the last joint. # (otherwise pivoting the end point of the last created bone is not possible) self.armature.MakeEditable(True) # Get the name of the parent bone of this bone. #old parentBoneName = str(self.parentNode.id) parentBoneName = str(self.parentNode.name) if debprn: print 'deb: i have parentBoneName=', parentBoneName #----------- # Create the name for the new bone #old boneName = str(self.id) boneName = str(self.name) if debprn: print 'deb: add me boneName=', boneName #---------- # Add a new bone to the armature. boneInfo = self.armature.AddNewBone(boneName, parentBoneName, daeNode) # Get the location of the armature. armatureLoc = self.armature.GetLocation().resize3D() if debprn: print 'deb: armatureLoc=', armatureLoc #---------- # Set the correct head and tail positons of this bone. headLoc = Vector(0,0,0) tailLoc = Vector(0,0,1) if not parentBoneName is None: # The head of this bone starts at the end of it's parent. parentBone = boneInfo.parent.GetBone() if currentBoneExists: headLoc = self.transformMatrix.translationPart() - armatureLoc else: headLoc = parentBone.tail boneInfo.SetConnected() tailLoc = 2 * headLoc - parentBone.head boneInfo.localTransformMatrix = Matrix(self.localTransformMatrix).transpose() boneInfo.SetHead(headLoc) boneInfo.SetTail(tailLoc) self.armature.MakeEditable(False) else : #---daeNode is not Joint if isinstance(self.parentNode, SceneNode): self.transformMatrix *= self.parentNode.localTransformMatrix #if debprn: print 'deb: transformMatrix:\n' , self.transformMatrix #-------- daeInstances = daeNode.GetInstances() if debprn: print 'deb: daeInstances:\n' , daeInstances #-------- isController = False newObjects = [] if len(daeInstances): for daeInstance in daeInstances: # Check which type the instance is if isinstance(daeInstance,collada.DaeAnimationInstance): # Animation newObject = Blender.Object.New('Empty',self.document.CreateNameForObject(self.id,replaceNames, 'empty')) newObjects.append(newObject) elif isinstance(daeInstance,collada.DaeCameraInstance): # Camera newObject = Blender.Object.New('Camera',self.document.CreateNameForObject(self.id,replaceNames, 'camera')) dataObject = self.document.camerasLibrary.FindObject(daeInstance,True) newObject.link(dataObject) newObjects.append(newObject) elif isinstance(daeInstance,collada.DaeControllerInstance): # Controller newObject = Blender.Object.New('Mesh',self.document.CreateNameForObject(self.id,replaceNames, 'object')) dataObject = self.document.controllersLibrary.FindObject(daeInstance, True, newObject) newObject.link(dataObject) isController = True newObjects.append(newObject) if debprn: print 'deb: M E S H isController -------O O O O O:' #-------- elif isinstance(daeInstance,collada.DaeGeometryInstance): # Geometry newObject = Blender.Object.New('Mesh',self.document.CreateNameForObject(self.id,replaceNames, 'object')) dataObject = self.document.meshLibrary.FindObject(daeInstance,True) newObject.link(dataObject) newObjects.append(newObject) if debprn: print 'deb: M E S H daeInstance -------ooooooooo:' #-------- elif isinstance(daeInstance,collada.DaeLightInstance): # Light newObject = Blender.Object.New('Lamp',self.document.CreateNameForObject(self.id,replaceNames, 'lamp')) dataObject = self.document.lampsLibrary.FindObject(daeInstance,True) newObject.link(dataObject) newObjects.append(newObject) elif isinstance(daeInstance,collada.DaeNodeInstance): # Node newObject = Blender.Object.New('Empty',self.document.CreateNameForObject(self.id,replaceNames, 'empty')) newObjects.append(newObject) elif isinstance(daeInstance,collada.DaeVisualSceneInstance): # Visual Scene newObject = Blender.Object.New('Empty',self.document.CreateNameForObject(self.id,replaceNames, 'empty')) newObjects.append(newObject) else: print "??? Loading Unknown Instance:", daeInstance #old return # If there were multiple instance, create an empty. We add the instances to it later. #if not isController or len (daeInstances) > 1: #if debprn: print 'deb: PROBLEM >>>>>>> make newObject as Empty cointainer: ' #-------- #newObject = Blender.Object.New('Empty',self.id) if newObjects: if len(newObjects)==1: newObject = newObjects[0] if debprn: print 'deb: >>>>>>>bObject: ' , newObject #-------- self.bObject = newObject self.document.currentBScene.link(newObject) else: newObject = Blender.Object.New('Empty',self.id) self.document.currentBScene.link(newObject) # If there were more instances in this node, # also add all those instances to the Blender Scene # and make them childs of the Empty. if debprn: print 'deb: >>>>>>>newObjects: ' , newObjects #-------- for newObjectChild in newObjects: self.document.currentBScene.link(newObjectChild) newObject.makeParent(newObjects, noninverse , 1) else: if debprn: print 'deb: no daeInstances ? ? ? ?:' #-------- newObject = Blender.Object.New('Empty',self.id) #newObjects.append(newObject) #reload properties: if len(daeNode.extras) > 0: for extra in daeNode.extras: for tech in extra.techniques: for prop in tech.params: self.bObject.addProperty(prop.name, prop.value, prop.type) if prop.value is not None: if prop.type == 'STRING': realValue = prop.value.encode('utf-8') self.bObject.getProperty(prop.name).setData(realValue) elif prop.type == 'INT': realValue = int(prop.value.encode('utf-8')) self.bObject.getProperty(prop.name).setData(realValue) elif prop.type == 'FLOAT': realValue = float(prop.value.encode('utf-8')) self.bObject.getProperty(prop.name).setData(realValue) elif prop.type == 'BOOL': realValue = int(prop.value.encode('utf-8')) self.bObject.getProperty(prop.name).setData(realValue) elif prop.type == 'TIME': realValue = float(prop.value.encode('utf-8')) self.bObject.getProperty(prop.name).setData(realValue) # TODO: Vertex Colors: MAYBE CHANGE THIS LATER update the mesh.. if newObject.getType() == 'Mesh': newObject.getData().update(1,1,1) # Set the vertex colors. try: for f in newObject.getData(mesh=1).faces: for c in f.col: c.r = 255 c.g = 255 c.b = 255 c.a = 255 except ValueError: pass #old if not self.isJoint and self.armature is None: #old2 if self.armature is None: if 1: #---Check if the daeNode has some Layer information. if len(daeNode.layer): layers = self.document.layers # Keep track of the blender layers to which this Node belongs. myLayers = [] # Loop through all layers of this node. for layer in daeNode.layer: # Check if this layer is used before. if layer in layers: # If so, the layer to add is the index of that layer + 1. layerNo = layers.index(layer)+1 else: # else, create a new layer. addLayer = True # When the layer is a digit, try to use the same digit in Blender. if layer.isdigit(): # If So, check if this digit is between 1 and 20 AND if this layer is not used before digit = int(layer) if digit >= 1 and digit <= len(layers) and layers[digit-1] is None: # Add the new layer to the list. layers[digit-1] = layer layerNo = digit # Set this flag to false, so further checking is skipped. addLayer = False # If the layer was not a digit, Create a new one. if addLayer: layerNo = 1 # Get the first free spot. if None in layers: index = layers.index(None) layers[index] = layer layerNo = index+1 # When this layerNo is not in myLayers yet, add it to the list. if not (layerNo in myLayers): myLayers.append(layerNo) else: # Use the current selected layers. myLayers = self.document.currentBScene.layers childlist = [] for daeChild in daeNode.nodes: try: childSceneNode = SceneNode(self.document,self) object = childSceneNode.ObjectFromDae(daeChild) if object: childlist.append(object) except NameError: if debprn: print "a child of node " + daeNode.id + " has no id ? ?" for iDaeChild in daeNode.iNodes: try: childSceneNode = SceneNode(self.document,self) object = childSceneNode.ObjectFromDae(iDaeChild.object) if object: newObject.makeParent([object], noninverse , 1) childlist.append(object) except NoneType: if debprn: print "a child instance of node " + daeNode.id + " has no id ? ?" if newObject: if not self.isJoint and not self.armature: newObject.setMatrix(Matrix().resize4x4()) newObject.makeParent(childlist, noninverse,1) # Set the layers of the new Object. newObject.layers = myLayers newObject.setMatrix(self.localTransformMatrix) elif self.isJoint and self.armature: for jointName in waitingControllers: armature = Armature.FindArmatureWithJoint(jointName) if armature and armature.name == self.armature.name: controller = waitingControllers[jointName] controller.armatureName = armature.name armature.GetBlenderObject().makeParent([controller.bObject], noninverse , 1) controller.BindMesh() break # Check if this node has an animation. daeAnimations = self.document.animationsLibrary.GetDaeAnimations(self.id) if debprn: print 'deb: O ------- O animation check' #------------ for daeAnimation in daeAnimations: a = Animation(self.document) a.LoadFromDae(daeAnimation, daeNode, newObject) if debprn: print 'deb: X -------- X an animation is loaded= \n', a #------------ if debprn: print 'deb:class SceneNode_ObjectFromDae ---end---' #------------ return newObject def SaveSceneToDae(self, bNode, childNodes, daeGlobalPhysicsModel, \ daeGlobalPhysicsModelInstance, bScene): global bakeMatrices, exportSelection, applyModifiers, debprn daeNode = collada.DaeNode() daeNode.id = daeNode.name = self.document.CreateID(bNode.name,'-Node')# +'-node' if len(bNode.getAllProperties()) > 0 : daeExtra = collada.DaeExtra() daeNode.extras.append(daeExtra) tech = collada.DaeTechnique() daeExtra.techniques.append(tech) for property in bNode.getAllProperties(): tech.AddParam( property.getName(), property.getType(), property.getData()) # Get the transformations mat = bNode.getMatrix('localspace') if bakeMatrices : mat = Matrix(mat).transpose() daeNode.transforms.append([collada.DaeSyntax.MATRIX, mat]) else: loc = mat.translationPart() daeNode.transforms.append([collada.DaeSyntax.TRANSLATE, loc]) euler = mat.toEuler() rotxVec = [1,0,0,euler.x] rotyVec = [0,1,0,euler.y] rotzVec = [0,0,1,euler.z] daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotzVec]) daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotyVec]) daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotxVec]) scale = mat.scalePart() daeNode.transforms.append([collada.DaeSyntax.SCALE, scale]) # Get the instance type = bNode.getType() instance = None meshID = None if type == 'Mesh': instance = collada.DaeGeometryInstance() daeGeometry = self.document.colladaDocument.geometriesLibrary.FindObject(bNode.getData(True)) meshNode = MeshNode(self.document) if daeGeometry is None: derivedObjsMatrices = BPyObject.getDerivedObjects(bNode) for derivedObject, matrix in derivedObjsMatrices: virtualMesh = BPyMesh.getMeshFromObject(derivedObject, \ self.document.containerMesh, applyModifiers, False, bScene) if debprn: print("Virtual mesh: " + str(virtualMesh) ) # + "; type: " + str(type(virtualMesh))) if not virtualMesh: # Fallback! # Should never happen! # (The "mesh=1" param on the getData() method ensures that # it gets a "Mesh" instead of a "NMesh".) print("Error while trying to save derived object / apply modifiers. Try saving " \ + "more direct, all modifiers will be ignored.") daeGeometry = meshNode.SaveToDae(bNode.getData(mesh=1)) else: # Apply original name from untransformed object # to transformed object (name is later copied 1:1 to id). virtualMesh.name = derivedObject.name # _Don't_ apply transformation matrix "matrix"! # The transformation will get instead written in the file itself # seperately! daeGeometry = meshNode.SaveToDae(virtualMesh) meshID = daeGeometry.id bindMaterials = meshNode.GetBindMaterials(bNode.getData(),\ daeGeometry.uvTextures, daeGeometry.uvIndex) instance.object = daeGeometry instance.bindMaterials = bindMaterials instanceController = None # Check if this mesh is skinned to an amarture. isSkinned = False for bModifier in bNode.modifiers: if bModifier.type == Blender.Modifier.Type.ARMATURE: isSkinned = True # An Armature modifier exists, so create a Controller. controller = Controller(self.document) daeController = controller.SaveToDae(bModifier, bNode, meshID) #Get the root bone bArmatureObject = bModifier[Blender.Modifier.Settings.OBJECT] if (bArmatureObject is None): HandleError(ERROR_MESH_ARMATURE_PARENT, meshID) bArmature = bArmatureObject.getData() rootBones = [] for boneName in bArmature.bones.keys(): bone = bArmature.bones[boneName] if not bone.hasParent(): rootBones.append(bone) instanceController = collada.DaeControllerInstance() instanceController.object = daeController daeNode.transforms = [] loc = [0,0,0] daeNode.transforms.append([collada.DaeSyntax.TRANSLATE, loc]) rotxVec = [1,0,0,0] rotyVec = [0,1,0,0] rotzVec = [0,0,1,0] daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotzVec]) daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotyVec]) daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotxVec]) scale = [1,1,1] daeNode.transforms.append([collada.DaeSyntax.SCALE, scale]) #use the rootBone's name for consistancy with vert groups instanceController.skeletons.append(rootBones[0].name) instanceController.bindMaterials = bindMaterials if not isSkinned: # only add an instance geometry if it is not skinned daeNode.iGeometries.append(instance) else: daeNode.iControllers.append(instanceController) elif type == 'Camera': instance = collada.DaeCameraInstance() daeCamera = self.document.colladaDocument.camerasLibrary.FindObject(bNode.getData(True)) if daeCamera is None: cameraNode = CameraNode(self.document) daeCamera = cameraNode.SaveToDae(bNode.getData()) instance.object = daeCamera daeNode.iCameras.append(instance) elif type == 'Lamp': instance = collada.DaeLightInstance() daeLight = self.document.colladaDocument.lightsLibrary.FindObject(bNode.getData(True)) if daeLight is None: lampNode = LampNode(self.document) daeLight = lampNode.SaveToDae(bNode.getData()) instance.object = daeLight daeNode.iLights.append(instance) elif type == 'Armature': daeNode.type = collada.DaeSyntax.TYPE_JOINT armatureNode = ArmatureNode(self.document) daeArmature = armatureNode.SaveToDae(bNode.getData(),bNode, bNode.getPose()) daeNode = daeArmature[0] # Check if the object has an IPO curve. if so, export animation. if not (bNode.ipo is None): ipo = bNode.ipo animation = Animation(self.document) animation.SaveToDae(ipo, daeNode) # Export layer information. daeNode.layer = ['L'+str(layer) for layer in bNode.layers] if not exportSelection: myChildNodes = [] if childNodes.has_key(bNode.name): myChildNodes = childNodes[bNode.name] for bNode in myChildNodes: sceneNode = SceneNode(self.document, self) daeNode.nodes.append(sceneNode.SaveSceneToDae(bNode, childNodes, daeGlobalPhysicsModel, \ daeGlobalPhysicsModelInstance, bScene)[0]) daePhysicsInstance = self.SavePhysicsToDae(bNode, meshID, daeNode,daeGlobalPhysicsModel,daeGlobalPhysicsModelInstance) return (daeNode, daePhysicsInstance) def SavePhysicsToDae(self, bNode, meshID, daeNode,daeGlobalPhysicsModel,daeGlobalPhysicsModelInstance): global usePhysics if meshID is None or (not(usePhysics is None) and not usePhysics): return None # Check if physics is supported. if usePhysics is None: usePhysics = hasattr(bNode, "rbShapeBoundType") if not usePhysics: return None rbFlags = [0]*32 rbF = bNode.rbFlags lastIndex = 0 # Get the bit flags. while rbF > 0: val = rbF >> 1 if val << 1 == rbF: ##rbFlags.append(0) rbFlags[lastIndex] = 0 else: ##rbFlags.append(1) rbFlags[lastIndex] = 1 lastIndex += 1 rbF = val daeRigidBody = collada.DaeRigidBody() #print "daeNode.id",daeNode.id daeRigidBody.id = daeRigidBody.name = daeRigidBody.sid = self.document.CreateID(daeNode.id,'-RigidBody') #print "daeRigidBody.sid",daeRigidBody.sid daeRigidBodyTechniqueCommon = collada.DaeRigidBody.DaeTechniqueCommon() daeRigidBodyTechniqueCommon.dynamic = bool(rbFlags[0]) if daeRigidBodyTechniqueCommon.dynamic: daeRigidBodyTechniqueCommon.mass = bNode.rbMass else: daeRigidBodyTechniqueCommon.mass = 0 #not dynamic means mass == 0! # Check the shape of the rigid body. if bNode.rbShapeBoundType == 0 and rbFlags[PhysicsNode.bounds]: # Box shape = collada.DaeBoxShape() shape.halfExtents = list(bNode.rbHalfExtents) elif bNode.rbShapeBoundType == 1 and rbFlags[PhysicsNode.bounds]: # Sphere shape = collada.DaeSphereShape() shape.radius = bNode.rbRadius elif bNode.rbShapeBoundType == 2 and rbFlags[PhysicsNode.bounds]: # Cylinder shape = collada.DaeCylinderShape() shape.radius = [[bNode.rbRadius],[bNode.rbRadius]] shape.height = bNode.rbHalfExtents[2] elif bNode.rbShapeBoundType == 3 and rbFlags[PhysicsNode.bounds]: # Cone shape = collada.DaeTaperedCylinderShape() shape.radius1 = [[bNode.rbRadius],[bNode.rbRadius]] shape.radius2 = [0 , 0] shape.height = bNode.rbHalfExtents[2] else: # Convex hull or # Static Triangle Mesh for static, and # Sphere if dynamic if daeRigidBodyTechniqueCommon.dynamic and not rbFlags[PhysicsNode.bounds]: shape = collada.DaeSphereShape() shape.radius = bNode.rbRadius else: shape = collada.DaeGeometryShape() iGeometry = collada.DaeGeometryInstance() if bNode.rbShapeBoundType == 5: object = self.document.colladaDocument.geometriesLibrary.FindObject(daeNode.id+'-Convex') else: object = self.document.colladaDocument.geometriesLibrary.FindObject(meshID) if not object.HasOnlyTriangles(): # The geometry contains no triangles pass if object is None: object = collada.DaeGeometry() object.id = object.name = self.document.CreateID(daeNode.id,'-ConvexGeom') convexMesh = collada.DaeConvexMesh() convexMesh.convexHullOf = meshID object.data = convexMesh self.document.colladaDocument.geometriesLibrary.AddItem(object) iGeometry.object = object shape.iGeometry = iGeometry # Create a physics material. daePhysicsMaterial = self.document.colladaDocument.physicsMaterialsLibrary.FindObject(daeNode.id+'-PxMaterial') if daePhysicsMaterial is None: daePhysicsMaterial = collada.DaePhysicsMaterial() physicsMaterials = bNode.getData().getMaterials() if (not (physicsMaterials is None)) and len(physicsMaterials) > 0: physicsMaterial = physicsMaterials[0] if not (physicsMaterial is None): daePhysicsMaterial.techniqueCommon.restitution = physicsMaterial.rbRestitution # Usually dynamic friction is less than static friction. However, as of Blender 2.42, # only a single friction coefficient can be specified. This is used for both # static and dynamic friction coefficients. daePhysicsMaterial.techniqueCommon.staticFriction = physicsMaterial.rbFriction daePhysicsMaterial.techniqueCommon.dynamicFriction = physicsMaterial.rbFriction daePhysicsMaterial.id = daePhysicsMaterial.name = self.document.CreateID(daeNode.id, '-PhysicsMaterial') self.document.colladaDocument.physicsMaterialsLibrary.AddItem(daePhysicsMaterial) # Create a physics material instance. daePhysicsMaterialInstance = collada.DaePhysicsMaterialInstance() # Set the physics material of this instance daePhysicsMaterialInstance.object = daePhysicsMaterial # add the shape to the commom technique. daeRigidBodyTechniqueCommon.shapes.append(shape) # Set the material of the common technique. daeRigidBodyTechniqueCommon.iPhysicsMaterial = daePhysicsMaterialInstance # Set the common technique of this rigid body. daeRigidBody.techniqueCommon = daeRigidBodyTechniqueCommon # Add the rigid body to the physics model. daeGlobalPhysicsModel.rigidBodies.append(daeRigidBody) # Create a new RigidBody instance daeRigidBodyInstance = collada.DaeRigidBodyInstance() # Set the rigid body of this instance daeRigidBodyInstance.body = daeRigidBody # Set the node of this instance daeRigidBodyInstance.target = daeNode # add the rigidbody instance to this physics model instance. daeGlobalPhysicsModelInstance.iRigidBodies.append(daeRigidBodyInstance) #handle constraints for rbconstraint in bNode.constraints: if rbconstraint.type == Blender.Constraint.Type.RIGIDBODYJOINT: daeRigidConstraint = collada.DaeRigidConstraint() daeRigidConstraint.id = daeRigidConstraint.name = daeRigidConstraint.sid = rbconstraint.name daeRigidConstraint.attachment = collada.DaeRigidConstraint.DaeAttachment() daeRigidConstraint.attachment.rigid_body = daeRigidBody daeRigidConstraint.attachment.blenderobject = bNode daeRigidConstraint.attachment.pivX = rbconstraint[Blender.Constraint.Settings.CONSTR_RB_PIVX] daeRigidConstraint.attachment.pivY = rbconstraint[Blender.Constraint.Settings.CONSTR_RB_PIVY] daeRigidConstraint.attachment.pivZ = rbconstraint[Blender.Constraint.Settings.CONSTR_RB_PIVZ] daeRigidConstraint.attachment.axX = rbconstraint[Blender.Constraint.Settings.CONSTR_RB_AXX] daeRigidConstraint.attachment.axY = rbconstraint[Blender.Constraint.Settings.CONSTR_RB_AXY] daeRigidConstraint.attachment.axZ = rbconstraint[Blender.Constraint.Settings.CONSTR_RB_AXZ] if not (rbconstraint[Blender.Constraint.Settings.TARGET] is None): #find rigidbody that goes with TARGET... blendertargetob = rbconstraint[Blender.Constraint.Settings.TARGET] targetRbId = blendertargetob.name+'-RigidBody' targetrigidbody = daeGlobalPhysicsModel.FindRigidBody(targetRbId) if not (targetrigidbody is None): daeRigidConstraint.ref_attachment = collada.DaeRigidConstraint.DaeRefAttachment() daeRigidConstraint.ref_attachment.rigid_body = targetrigidbody daeRigidConstraint.ref_attachment.blenderobject = blendertargetob #calculate pivX/pivY/pivZ and axX,axY,axZ for this reference frame refObjectWorldMatrix = blendertargetob.matrix attachObjectWorldMatrix = bNode.matrix euler = Euler(daeRigidConstraint.attachment.axX,daeRigidConstraint.attachment.axY,daeRigidConstraint.attachment.axZ) matRot= euler.toMatrix() matT1 = TranslationMatrix(Vector(daeRigidConstraint.attachment.pivX,daeRigidConstraint.attachment.pivY,daeRigidConstraint.attachment.pivZ)) matT = TranslationMatrix(Vector(0,0,0)) matT[0][0]=matRot[0][0] matT[0][1]=matRot[0][1] matT[0][2]=matRot[0][2] matT[1][0]=matRot[1][0] matT[1][1]=matRot[1][1] matT[1][2]=matRot[1][2] matT[2][0]=matRot[2][0] matT[2][1]=matRot[2][1] matT[2][2]=matRot[2][2] copyMat = Matrix(refObjectWorldMatrix) attachLocalMatrix = matT * matT1 invertedMat = copyMat.invert() globalFrameA = attachLocalMatrix * attachObjectWorldMatrix refAttachLocalMatrix = globalFrameA * invertedMat refPivot = refAttachLocalMatrix.translationPart() eulerAngles = refAttachLocalMatrix.toEuler() daeRigidConstraint.ref_attachment.pivX = refPivot[0] daeRigidConstraint.ref_attachment.pivY = refPivot[1] daeRigidConstraint.ref_attachment.pivZ = refPivot[2] daeRigidConstraint.ref_attachment.axX = eulerAngles[0] daeRigidConstraint.ref_attachment.axY = eulerAngles[1] daeRigidConstraint.ref_attachment.axZ = eulerAngles[2] #print "constraint type = ",rbconstraint[Blender.Constraint.Settings.CONSTR_RB_TYPE] daeRigidConstraintTechniqueCommon = collada.DaeRigidConstraint.DaeTechniqueCommon() daeRigidConstraintTechniqueCommon.limits.linearLimits.min = [0,0,0] daeRigidConstraintTechniqueCommon.limits.linearLimits.max = [0,0,0] daeRigidConstraintTechniqueCommon.limits.angularLimits.min = [0,0,0] daeRigidConstraintTechniqueCommon.limits.angularLimits.max = [0,0,0] #generic 6 degree-of-freedom joint if rbconstraint[Blender.Constraint.Settings.CONSTR_RB_TYPE] == 12: daeRigidConstraintTechniqueCommon.limits.linearLimits.min = [rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MINLIMIT0],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MINLIMIT1],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MINLIMIT2]] daeRigidConstraintTechniqueCommon.limits.linearLimits.max = [rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MAXLIMIT0],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MAXLIMIT1],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MAXLIMIT2]] daeRigidConstraintTechniqueCommon.limits.angularLimits.min = [rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MINLIMIT3],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MINLIMIT4],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MINLIMIT5]] daeRigidConstraintTechniqueCommon.limits.angularLimits.max = [rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MAXLIMIT3],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MAXLIMIT4],rbconstraint[Blender.Constraint.Settings.CONSTR_RB_MAXLIMIT5]] #ball-socket joint if rbconstraint[Blender.Constraint.Settings.CONSTR_RB_TYPE] == 1: daeRigidConstraintTechniqueCommon.limits.linearLimits.min = [0,0,0] daeRigidConstraintTechniqueCommon.limits.linearLimits.max = [0,0,0] daeRigidConstraintTechniqueCommon.limits.angularLimits.min = ['-INF','-INF','-INF'] daeRigidConstraintTechniqueCommon.limits.angularLimits.max = ['INF','INF','INF'] #hinge if rbconstraint[Blender.Constraint.Settings.CONSTR_RB_TYPE] == 2: daeRigidConstraintTechniqueCommon.limits.linearLimits.min = [0,0,0] daeRigidConstraintTechniqueCommon.limits.linearLimits.max = [0,0,0] daeRigidConstraintTechniqueCommon.limits.angularLimits.min = ['-INF',0,0] daeRigidConstraintTechniqueCommon.limits.angularLimits.max = ['INF',0,0] #daeRigidConstraintTechniqueCommon.enabled = True daeRigidConstraint.techniqueCommon = daeRigidConstraintTechniqueCommon daeGlobalPhysicsModel.constraints.append(daeRigidConstraint) # Create a new RigidConstraint instance daeRigidConstraintInstance = collada.DaeRigidConstraintInstance() # Set the rigid body of this instance daeRigidConstraintInstance.constraint = daeRigidConstraint #add constraint instance daeGlobalPhysicsModelInstance.iConstraints.append(daeRigidConstraintInstance) return daeRigidBodyInstance class ArmatureNode(object): def __init__(self, document): self.document = document def SaveToDae(self, bArmature, bArmatureObject, bPose): daeNodes = [] # Get the root bones rootBones = [] for boneName in bArmature.bones.keys(): bone = bArmature.bones[boneName] if not bone.hasParent(): rootBones.append(bone) # Only take the first root and ignore it. if len(rootBones) > 0 and not rootBones[0].children is None: daeNodes.append(self.BoneToDae(rootBones[0], bPose,Matrix(), bArmatureObject)) ##for childBone in rootBones[0].children: ## print PrintTransforms(Matrix(bPose.bones[rootBones[0].name].poseMatrix).transpose(),rootBones[0].name) ## poseMatrix = Matrix(bPose.bones[rootBones[0].name].poseMatrix).transpose() ## poseMatrixRotInv = poseMatrix.rotationPart().invert().resize4x4() ##poseMatrixNoRot = poseMatrix * poseMatrixRotInv ## print rootBones[0] ## print Matrix(bArmatureObject.matrix).transpose().invert() ##daeNodes.append(self.BoneToDae(rootBones[0], bPose, Matrix(bArmatureObject.matrix).transpose().invert(),poseMatrixRotInv, bArmatureObject)) if len(rootBones) > 1: print("Please use only a single root for proper export.\n" \ "Nr. of root bones: %i.\n" \ "List of root bones:" % len(rootBones)) for rootBone in rootBones: print("\t%s" % rootBone.__str__()) return daeNodes def BoneToDae(self, bBone, bPose, parentMatrix, bArmatureObject, bParentBind=None): daeNode = collada.DaeNode() daeNode.id = daeNode.name = daeNode.sid = self.document.CreateID(bBone.name, '-Joint') daeNode.type = collada.DaeSyntax.TYPE_JOINT # Get the transformations if dmitri: bindMatrix = Matrix(bBone.matrix["ARMATURESPACE"]).resize4x4().transpose() else: headPos = bBone.head["ARMATURESPACE"] bindMatrix = Matrix([1,0,0,headPos.x], [0,1,0,headPos.y], [0,0,1,headPos.z],[0,0,0,1]) armMatrix = bindMatrix if ( not bBone.hasParent() ): armMatrix = Matrix(bArmatureObject.getMatrix('localspace')).transpose() armMatrix *= bindMatrix try : armAction = bArmatureObject.getAction() ipList = armAction.getAllChannelIpos() ip_bone_channel = ipList[bBone.name] ip_bone_name = ip_bone_channel.getName() ipo = Blender.Ipo.Get(ip_bone_name) if not (ipo is None): animation = Animation(self.document) if bParentBind is None: animation.SaveToDae(ipo, daeNode, bBone, bPose, parentMatrix, bArmatureObject) else: animation.SaveToDae(ipo, daeNode, bBone, bPose, bParentBind, bArmatureObject) except: pass mat = Matrix(parentMatrix).invert() * armMatrix boneMatrix = Matrix(bindMatrix) mat.transpose() #PrintTransforms(mat, "ARMATURESPACE: " + bBone.name) if bakeMatrices : mat = Matrix(mat).transpose() daeNode.transforms.append([collada.DaeSyntax.MATRIX, mat]) else: translation = mat.translationPart() daeNode.transforms.append([collada.DaeSyntax.TRANSLATE, translation]) euler = mat.toEuler() ## print euler rotxVec = [1,0,0,euler.x] rotyVec = [0,1,0,euler.y] rotzVec = [0,0,1,euler.z] ## print rotxVec[3], rotyVec[3], rotzVec[3] daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotzVec]) daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotyVec]) daeNode.transforms.append([collada.DaeSyntax.ROTATE, rotxVec]) scale = mat.scalePart() daeNode.transforms.append([collada.DaeSyntax.SCALE, scale]) if not bBone.children is None: for childBone in bBone.children: daeNode.nodes.append(self.BoneToDae(childBone, bPose, boneMatrix, bArmatureObject, boneMatrix)) return daeNode class MeshNode(object): def __init__(self,document): self.document = document self.materials = [] self.verts = [] def FindMaterial(self, bMaterials,name): for i in range(len(bMaterials)): if bMaterials[i].name == name: return i return -1 def LoadFromDae(self, daeGeometry): global replaceNames meshID = daeGeometry.id meshName = daeGeometry.name if isinstance(daeGeometry.data,collada.DaeMesh): # check if it's a mesh # Create a new meshObject bMesh2 = Blender.NMesh.New(self.document.CreateNameForObject(meshID,replaceNames,'mesh')) materials = [] for matName, material in self.materials.iteritems(): materials.append(material) bMesh2.materials = materials if len(materials)>16: print 'Warning: Mesh-Object:"%s" has more than 16 materials:' %meshName faces = [] daeMesh = daeGeometry.data daeVertices = daeMesh.vertices # Get all the sources sources = dict() for source in daeMesh.sources: sources[source.id] = source.vectors # Get the Position Input vPosInput = daeVertices.FindInput('POSITION') if not (daeMesh.FindSource(vPosInput).techniqueCommon is None): # Get the Normal Input vNorInput = daeVertices.FindInput('NORMAL') # Keep track of the Blender Vertices pVertices = [] self.document.ProgressPart(0.0,'Create Vertices') vertIndex = 0 self.verts = range(len(sources[vPosInput.source])) # Create all the vertices. for i in sources[vPosInput.source]: vPosVector = Vector(i[0], i[1], i[2]) * self.document.tMatOLD bVert = Blender.NMesh.Vert(vPosVector.x, vPosVector.y, vPosVector.z) self.verts[vertIndex] = bVert pVertices.append(bVert) vertIndex += 1 bMesh2.verts = pVertices faceVerts = [] # The list of vertices for each face to add edgeVerts = [] # The list of vertices for each edge to add # Now create the primitives self.document.ProgressPart(0.5,'Create Primitives') daePrimitives = daeMesh.primitives for primitive in daePrimitives: # Get the UV Input vUvInput = primitive.FindInput('TEXCOORD') uvs = [] if not (vUvInput is None): for i in sources[vUvInput.source]: vUvVector = Vector(i[0],i[1]) uvs.append(vUvVector) inputs = primitive.inputs maxOffset = primitive.GetStride() # The number of values for one vertice vertCount = 0 # The number of vertices for one primitive realVertCount = 0 # The number of vertices for one primitive plist = [] # The list with all primitives if isinstance(primitive, collada.DaePolygons): # Polygons plist = primitive.polygons vertCount = 4 elif isinstance(primitive, collada.DaeTriangles): # Triangles plist.append(primitive.triangles) vertCount = 3 elif isinstance(primitive, collada.DaeLines): # Lines plist.append(primitive.lines) vertCount = 2 elif isinstance(primitive, collada.DaePolylist): # Polylist plist.append(primitive.polygons) vertCount = 5 elif isinstance(primitive, collada.DaeTriFans): #TriFan plist = primitive.trifans vertCount = 3 elif isinstance(primitive, collada.DaeTriStrips): #TriStrip plist = primitive.tristrips vertCount = 3 realVertCount = vertCount # Loop through each P (primitive) for p in plist: # for PolyList: Keep track of the index of the polygon polyListIndex = -1 if isinstance(primitive, collada.DaeTriFans) or isinstance(primitive, collada.DaeTriStrips): pIndex = maxOffset * 2 vertexInput = primitive.FindInput("VERTEX") uvInput = primitive.FindInput("TEXCOORD") norInput = primitive.FindInput("NORMAL") if not vertexInput is None: firstVertIndex = p[vertexInput.offset] prevVertIndex = p[maxOffset + vertexInput.offset] if not uvInput is None: firstUVIndex = p[uvInput.offset] prevUVIndex = p[maxOffset + uvInput.offset] realVertCount = 1 else: pIndex = 0 if isinstance(primitive, collada.DaePolygons): #patch from tera_api vertCount = len(p)/maxOffset realVertCount = vertCount # A list with edges in this face faceEdges = [] # a list to store all the created faces in to add them to the mesh afterwards. allFaceVerts = [] # loop through all the values in this 'p' while pIndex < len(p): # update the realVertCount if vertCount == 5: polyListIndex += 1 realVertCount = primitive.vcount[polyListIndex] # Keep track of the verts in this face curFaceVerts2 = [] uvList = [] # for every vertice for this primitive for i in range(realVertCount): # Check all the inputs and do the right thing for input in inputs: inputVal = p[pIndex+(i*maxOffset)+input.offset] if input.semantic == "VERTEX": vert2 = pVertices[inputVal] if isinstance(primitive, collada.DaeTriFans): firstVert = pVertices[firstVertIndex] prevVert = pVertices[prevVertIndex] curFaceVerts2.append(firstVert) curFaceVerts2.append(prevVert) curFaceVerts2.append(vert2) prevVertIndex = inputVal elif isinstance(primitive, collada.DaeTriStrips): firstVert = pVertices[firstVertIndex] prevVert = pVertices[prevVertIndex] curFaceVerts2.append(firstVert) curFaceVerts2.append(prevVert) curFaceVerts2.append(vert2) firstVertIndex = prevVertIndex prevVertIndex = inputVal else: curFaceVerts2.append(vert2) elif input.semantic == "TEXCOORD": uv2 = uvs[inputVal] if isinstance(primitive, collada.DaeTriFans): firstUV = uvs[firstUVIndex] prevUV = uvs[prevUVIndex] uvList.append((firstUV[0],firstUV[1])) uvList.append((prevUV[0],prevUV[1])) uvList.append((uv2[0],uv2[1])) prevUVIndex = inputVal elif isinstance(primitive, collada.DaeTriStrips): firstUV = uvs[firstUVIndex] prevUV = uvs[prevUVIndex] uvList.append((firstUV[0],firstUV[1])) uvList.append((prevUV[0],prevUV[1])) uvList.append((uv2[0],uv2[1])) firstUVIndex = prevUVIndex prevUVIndex = inputVal else: uvList.append((uvs[inputVal][0],uvs[inputVal][1])) elif input.semantic == "NORMAL": pass #TODO: support for normals if vertCount > 2: if isinstance(primitive, collada.DaeTriFans): faceCount = 1 else: faceCount = 1 + (realVertCount-4) / 2 + (realVertCount-4) % 2 #print 'deb: VertCount/realVertCount/faceCount:',VertCount,' / ',realVertCount,' / ',faceCount # --------- firstIndex = 2 lastIndex = 1 for a in range(faceCount): if isinstance(primitive, collada.DaeTriFans): newFirstIndex = (firstIndex + 1) % vertCount newLastIndex = (lastIndex -1) % vertCount else: newFirstIndex = (firstIndex + 1) % realVertCount newLastIndex = (lastIndex -1) % realVertCount fuv = [] if newFirstIndex != newLastIndex: fv = [curFaceVerts2[firstIndex]] + [curFaceVerts2[newFirstIndex]] + [curFaceVerts2[newLastIndex]] + [curFaceVerts2[lastIndex]] if len(uvList) == realVertCount: fuv = [uvList[firstIndex]] + [uvList[newFirstIndex]] + [uvList[newLastIndex]] + [uvList[lastIndex]] else: fv = [curFaceVerts2[firstIndex]] + [curFaceVerts2[newFirstIndex]] + [curFaceVerts2[lastIndex]] if len(uvList) == realVertCount: fuv = [uvList[firstIndex]] + [uvList[newFirstIndex]] + [uvList[lastIndex]] firstIndex = newFirstIndex lastIndex = newLastIndex # Create a new Face. newFace = Blender.NMesh.Face(fv) # Set the UV Coordinates newFace.uv = fuv # Add the new face to the list #bMesh2.addFace(newFace) faces.append(newFace) # Add the material to this face if primitive.material != '': if self.materials.has_key(primitive.material): #print 'deb: primitive.material:', primitive.material # --------- # Find the material index. matIndex = self.FindMaterial(bMesh2.materials, self.materials[primitive.material].name) # Set the material index for the new face. if 15 > matIndex > -1: newFace.materialIndex = matIndex else: newFace.materialIndex = 15 #TODO: fake for all material index bigger than 15 print 'material Index:%s, outside specification! set to 15 ----' %matIndex # --------- textures = self.materials[primitive.material].getTextures() #if len(textures) > 0 and textures[0]!=None: for texture in textures: #searching for any texture-image if texture is not None: image = texture.tex.getImage() if image is not None: newFace.image = image break else: print "Warning: Cannot find material:", primitive.material elif vertCount == 2: bMesh2.addEdge(curFaceVerts2[0], curFaceVerts2[1]) else: pass # update the index if isinstance(primitive, collada.DaeTriFans) or isinstance(primitive, collada.DaeTriStrips): pIndex += maxOffset else: pIndex += realVertCount * maxOffset bMesh2.faces = faces return bMesh2 return def SaveToDae(self, mesh): global useTriangles, usePolygons, useUV uvTextures = dict() uvIndex = dict() daeGeometry = collada.DaeGeometry() daeGeometry.id = daeGeometry.name = self.document.CreateID(mesh.name,'-Geometry') daeMesh = collada.DaeMesh() # Keep track of the edges in faces faceEdges = [] daeSource = collada.DaeSource() daeSource.id = self.document.CreateID(daeGeometry.id,'-Position') daeFloatArray = collada.DaeFloatArray() daeFloatArray.id = self.document.CreateID(daeSource.id,'-array') daeSource.source = daeFloatArray daeSource.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() accessor = collada.DaeAccessor() daeSource.techniqueCommon.accessor = accessor accessor.source = daeFloatArray.id accessor.count = len(mesh.verts) accessor.AddParam('X','float') accessor.AddParam('Y','float') accessor.AddParam('Z','float') daeSourceNormals = collada.DaeSource() daeSourceNormals.id = self.document.CreateID(daeGeometry.id,'-Normals') daeFloatArrayNormals = collada.DaeFloatArray() daeFloatArrayNormals.id = self.document.CreateID(daeSourceNormals.id,'-array') daeSourceNormals.source = daeFloatArrayNormals daeSourceNormals.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() accessorNormals = collada.DaeAccessor() daeSourceNormals.techniqueCommon.accessor = accessorNormals accessorNormals.source = daeFloatArrayNormals.id accessorNormals.count = 0 accessorNormals.AddParam('X','float') accessorNormals.AddParam('Y','float') accessorNormals.AddParam('Z','float') daeSourceTextures = collada.DaeSource() daeSourceTextures.id = self.document.CreateID(daeGeometry.id , '-UV') daeFloatArrayTextures = collada.DaeFloatArray() daeFloatArrayTextures.id = self.document.CreateID(daeSourceTextures.id,'-array') daeSourceTextures.source = daeFloatArrayTextures daeSourceTextures.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() accessorTextures = collada.DaeAccessor() daeSourceTextures.techniqueCommon.accessor = accessorTextures accessorTextures.source = daeFloatArrayTextures.id accessorTextures.AddParam('S','float') accessorTextures.AddParam('T','float') daeVertices = collada.DaeVertices() daeVertices.id = self.document.CreateID(daeGeometry.id,'-Vertex') daeInput = collada.DaeInput() daeInput.semantic = 'POSITION' daeInput.source = daeSource.id daeVertices.inputs.append(daeInput) daeInputNormals = collada.DaeInput() daeInputNormals.semantic = 'NORMAL' daeInputNormals.source = daeSourceNormals.id daeInputNormals.offset = 1 ## daeVertices.inputs.append(daeInputNormals) daeMesh.vertices = daeVertices hasColor = False #Vertex colors: if ( mesh.vertexColors ) : hasColor = True daeSourceColors = collada.DaeSource() daeSourceColors.id = self.document.CreateID(daeGeometry.id , '-color') daeFloatArrayColors = collada.DaeFloatArray() daeFloatArrayColors.id = self.document.CreateID(daeSourceColors.id,'-array') daeSourceColors.source = daeFloatArrayColors daeSourceColors.techniqueCommon = collada.DaeSource.DaeTechniqueCommon() accessorColors = collada.DaeAccessor() daeSourceColors.techniqueCommon.accessor = accessorColors accessorColors.source = daeFloatArrayColors.id accessorColors.AddParam('R','float') accessorColors.AddParam('G','float') accessorColors.AddParam('B','float') accessorColors.AddParam('A','float') for vert in mesh.verts: #print vert for co in vert.co: daeFloatArray.data.append(co) ## for no in vert.no: ## daeFloatArrayNormals.data.append(no) daePolygonsDict = dict() daeTrianglesDict = dict() daeLines = None # Loop trough all the faces for face in mesh.faces: matIndex = -1 if not useUV and mesh.materials and len(mesh.materials) > 0: matIndex = face.mat elif mesh.faceUV and (useUV or mesh.materials is None or len(mesh.materials) == 0): if not face.image is None: matIndex = face.image.name vertCount = len(face.verts) # Create a new function which adds vertices to a list. def AddVerts(verts,prlist,smooth,secondTriangle=False): secondTriangleIndex = [2,3,0] prevVert = None lastVert = verts[-1] for v_index in range(len(verts)): # Get the vertice vert = verts[v_index] vert_index = v_index # Add the vertice to the end of the list. prlist.append(vert.index) # Add the normals of these verts if smooth: for no in vert.no: daeFloatArrayNormals.data.append(no) accessorNormals.count = accessorNormals.count + 1 #prlist.append(len(daeFloatArrayNormals.data)/3-1) prlist.append(accessorNormals.count - 1) if hasColor: if secondTriangle: vert_index = secondTriangleIndex[vert_index] daeFloatArrayColors.data.append(face.col[vert_index].r / 255.0) daeFloatArrayColors.data.append(face.col[vert_index].g / 255.0) daeFloatArrayColors.data.append(face.col[vert_index].b / 255.0) daeFloatArrayColors.data.append(face.col[vert_index].a / 255.0) prlist.append(len(daeFloatArrayColors.data)/4-1) accessorColors.count = accessorColors.count + 1 vert_index = v_index # If the mesh has UV-coords, add these to the textures array. if mesh.faceUV: if secondTriangle: vert_index = secondTriangleIndex[vert_index] daeFloatArrayTextures.data.append(face.uv[vert_index][0]) daeFloatArrayTextures.data.append(face.uv[vert_index][1]) prlist.append(len(daeFloatArrayTextures.data)/2-1) accessorTextures.count = accessorTextures.count + 1 # Add the edge to the faceEdges list. if prevVert != None: faceEdges.append(mesh.findEdges(prevVert, vert)) else: faceEdges.append(mesh.findEdges([(vert, lastVert)])) # Update the prevVert vertice. prevVert = vert # Now use that AddVerts(...) function exactly in this "if"-block if (vertCount == 3 and not usePolygons) or (useTriangles and vertCount == 4): # triangle # Iff a Triangle Item for the current material not exists, create one. daeTrianglesDict.setdefault(matIndex, collada.DaeTriangles()) if not face.smooth: # Add the face normal to the normals source for no in face.no: daeFloatArrayNormals.data.append(no) accessorNormals.count = accessorNormals.count + 1 if vertCount == 3: # Add all the vertices to the triangle list. AddVerts(face.verts,daeTrianglesDict[matIndex].triangles, face.smooth ) # Update the vertice count for the trianglelist. daeTrianglesDict[matIndex].count += 1 else: # Convert polygon to triangles verts1 = face.verts[:3] verts2 = face.verts[2:] + tuple([face.verts[0]]) # Add all the vertices to the triangle list. AddVerts(verts1,daeTrianglesDict[matIndex].triangles, face.smooth) AddVerts(verts2, daeTrianglesDict[matIndex].triangles,face.smooth,True) # Update the vertice count for the trianglelist. daeTrianglesDict[matIndex].count += 2 else: # polygon # Iff a Polygon Item for the current material not exists, create one. daePolygonsDict.setdefault(matIndex, collada.DaePolygons()) # for each polygon, create a new P element pverts = [] if not face.smooth: # Add the face normal to the normals source for no in face.no: daeFloatArrayNormals.data.append(no) accessorNormals.count = accessorNormals.count + 1 # Add all the vertices to the pverts list. AddVerts(face.verts,pverts, face.smooth) # Add the pverts list to the polygons list. daePolygonsDict[matIndex].polygons.append(pverts) # Update the vertice count for the polygonlist. daePolygonsDict[matIndex].count += 1 if mesh.faceUV: if not face.image is None: if not face.image.name in uvTextures: uvIndex[face.image.name] = face.mat uvTextures[face.image.name] = self.document.CreateID(face.image.name, "-Material") # Loop through all the edges for edge in mesh.edges: if not edge.index in faceEdges: if daeLines == None: daeLines = collada.DaeLines() daeLines.count += 1 daeLines.lines.append(edge.v1.index) daeLines.lines.append(edge.v2.index) daeInput = collada.DaeInput() daeInput.semantic = 'VERTEX' daeInput.offset = 0 daeInput.source = daeVertices.id daeInputUV = collada.DaeInput() daeInputUV.semantic = 'TEXCOORD' daeInputUV.offset = 2 daeInputUV.source = daeSourceTextures.id if hasColor: daeInputColor = collada.DaeInput() daeInputColor.semantic = 'COLOR' daeInputColor.offset = 3 daeInputColor.source = daeSourceColors.id materialName = '' for k, daeTriangles in daeTrianglesDict.iteritems(): ##print k if k != -1: if not useUV and not mesh.materials is None and len(mesh.materials) > 0 and k >= 0: daeTriangles.material = mesh.materials[k].name elif mesh.faceUV and (useUV or mesh.materials is None or len(mesh.materials) == 0): daeTriangles.material = uvTextures[k] offsetCount = 0 daeInput.offset = offsetCount daeTriangles.inputs.append(daeInput) offsetCount += 1 daeInputNormals.offset = offsetCount daeTriangles.inputs.append(daeInputNormals) offsetCount += 1 if mesh.faceUV: daeInputUV.offset = offsetCount daeTriangles.inputs.append(daeInputUV) offsetCount += 1 if hasColor: daeInputColor.offset = offsetCount daeTriangles.inputs.append(daeInputColor) offsetCount += 1 daeMesh.primitives.append(daeTriangles) for k, daePolygons in daePolygonsDict.iteritems(): if k != -1: if not useUV and not mesh.materials is None and len(mesh.materials) > 0 and k >= 0: daePolygons.material = getattr(mesh.materials[k], 'name', "") elif mesh.faceUV and (useUV or mesh.materials is None or len(mesh.materials) == 0): daePolygons.material = uvTextures[k] offsetCount = 0 daeInput.offset = offsetCount daePolygons.inputs.append(daeInput) offsetCount += 1 daeInputNormals.offset = offsetCount daePolygons.inputs.append(daeInputNormals) offsetCount += 1 if mesh.faceUV: daeInputUV.offset = offsetCount daePolygons.inputs.append(daeInputUV) offsetCount += 1 if hasColor: daeInputColor.offset = offsetCount daePolygons.inputs.append(daeInputColor) offsetCount += 1 daeMesh.primitives.append(daePolygons) if daeLines != None: daeLines.inputs.append(daeInput) daeMesh.primitives.append(daeLines) daeMesh.sources.append(daeSource) daeMesh.sources.append(daeSourceNormals) if mesh.faceUV: daeMesh.sources.append(daeSourceTextures) if hasColor: daeMesh.sources.append(daeSourceColors) daeGeometry.data = daeMesh self.document.colladaDocument.geometriesLibrary.AddItem(daeGeometry) daeGeometry.uvTextures = uvTextures daeGeometry.uvIndex = uvIndex return daeGeometry def GetBindMaterials(self, bMesh, uvTextures, uvIndex): global useUV bindMaterials = [] mesh = Blender.Mesh.Get(bMesh.name) # now check the materials if (not useUV) and bMesh.materials and len(bMesh.materials) > 0: daeBindMaterial = collada.DaeFxBindMaterial() for bMaterial in bMesh.materials: instance = collada.DaeFxMaterialInstance() daeMaterial = self.document.colladaDocument.materialsLibrary.FindObject(bMaterial.name) if daeMaterial is None: materialNode = MaterialNode(self.document) daeMaterial = materialNode.SaveToDae(bMaterial) instance.object = daeMaterial daeBindMaterial.techniqueCommon.iMaterials.append(instance) # now we have to add this bindmaterial to the intance of this geometry bindMaterials.append(daeBindMaterial) elif mesh.faceUV and (useUV or bMesh.materials is None or len(bMesh.materials) == 0): daeBindMaterial = collada.DaeFxBindMaterial() for imageName, imageNameUnique in uvTextures.iteritems(): image = Blender.Image.Get(imageName) instance = collada.DaeFxMaterialInstance() daeMaterial = self.document.colladaDocument.materialsLibrary.FindObject(imageNameUnique) if daeMaterial is None: textureNode = TextureNode(self.document) daeMaterial = textureNode.SaveToDae(image) daeMaterial.id = daeMaterial.name = imageNameUnique if len(bMesh.materials) > 0 : materialIndex = uvIndex[imageName] bMaterial = bMesh.materials[materialIndex] materialNode = MaterialNode(self.document) materialShader = materialNode.GenerateShader(bMaterial, False) textureDiffuse = daeMaterial.iEffects[0].object.profileCommon.technique.shader.diffuse materialShader.diffuse = textureDiffuse daeMaterial.iEffects[0].object.AddShader(materialShader) instance.object = daeMaterial daeBindMaterial.techniqueCommon.iMaterials.append(instance) # now we have to add this bindmaterial to the intance of this geometry bindMaterials.append(daeBindMaterial) return bindMaterials class TextureNode(object): def __init__(self, document): self.document = document def LoadFromDae(self, daeImage): imageID = daeImage.id imageName = daeImage.name bTexture = Blender.Texture.New(imageID) filename = '' if isinstance(daeImage.initFrom, str): if Blender.sys.exists(daeImage.initFrom): filename = daeImage.initFrom elif Blender.sys.exists(self.document.filePath + daeImage.initFrom): filename = self.document.filePath + daeImage.initFrom if filename <> '': bTexture.setType('Image') img = Blender.Image.Load(filename) bTexture.setImage(img) else: print 'image not found: %s'%(daeImage.initFrom) return bTexture def AddImageTexture(self, daeEffect, bImage): #creating surface daeSurfaceParam = collada.DaeFxNewParam() surfaceId = self.document.CreateID(bImage.name,'-surface') daeSurfaceParam.sid = surfaceId daeSurface = collada.DaeFxSurface() daeSurfaceParam.surface = daeSurface daeSurface.initfrom = bImage.name + "-img" daeEffect.profileCommon.newParams.append( daeSurfaceParam ) #creating Sampler daeSamplerParam = collada.DaeFxNewParam() samplerId = self.document.CreateID(bImage.name,'-sampler') daeSamplerParam.sid = samplerId daeSampler = collada.DaeFxSampler2D() daeSamplerParam.sampler = daeSampler daeSampler.source.value = surfaceId daeEffect.profileCommon.newParams.append( daeSamplerParam ) shader = collada.DaeFxShadeLambert() daeImage = self.document.colladaDocument.imagesLibrary.FindObject(bImage.name) if daeImage is None: # Create the image daeImage = collada.DaeImage() daeImage.id = daeImage.name = bImage.name + "-img" daeImage.initFrom = Blender.sys.expandpath(bImage.filename) if useRelativePaths: daeImage.initFrom = CreateRelativePath(filename, daeImage.initFrom) self.document.colladaDocument.imagesLibrary.AddItem(daeImage) daeTexture = collada.DaeFxTexture() daeTexture.texture = samplerId shader.AddValue(collada.DaeFxSyntax.DIFFUSE, daeTexture) daeEffect.AddShader(shader) return def SaveToDae(self, bImage): daeMaterial = collada.DaeFxMaterial() ##daeMaterial.id = daeMaterial.name = self.document.CreateID(bImage.name, '-Material') self.document.colladaDocument.materialsLibrary.AddItem(daeMaterial) instance = collada.DaeFxEffectInstance() daeEffect = self.document.colladaDocument.effectsLibrary.FindObject(bImage.name+'-fx') if daeEffect is None: daeEffect = collada.DaeFxEffect() daeEffect.id = daeEffect.name = self.document.CreateID(bImage.name , '-fx') #Add the texture stuff!! self.AddImageTexture(daeEffect, bImage) self.document.colladaDocument.effectsLibrary.AddItem(daeEffect) instance.object = daeEffect daeMaterial.iEffects.append(instance) return daeMaterial class MaterialNode(object): def __init__(self, document): self.document = document def LoadFromDae(self, daeMaterial): materialID = daeMaterial.id materialName = daeMaterial.name bMat = Blender.Material.New(materialID) for i in daeMaterial.iEffects: daeEffect = self.document.colladaDocument.effectsLibrary.FindObject(i.url) if not (daeEffect.profileCommon is None): shader = daeEffect.profileCommon.technique.shader if shader.transparent: if shader.transparent.color!=None: tcol = shader.transparent.color.rgba tkey = 1 if shader.transparency: tkey = shader.transparency.float alpha = 1 - tkey * (tcol[0]*0.21 + tcol[1]*0.71 + tcol[2]*0.08) bMat.setAlpha(alpha) if shader.transparent.texture!=None: # Texture textureSampler = shader.transparent.texture.texture if debprn: print "shader:" #--------- if debprn: print shader.transparent.texture #--------- if debprn: print "shader end" #--------- if not(textureSampler is None): #support 1.4.0: texture = textureSampler #support 1.4.1 for newParam in daeEffect.profileCommon.newParams: if newParam.sid == textureSampler: surfaceID = newParam.sampler.source for newSurface in daeEffect.profileCommon.newParams: if newSurface.sid == surfaceID: texture = newSurface.surface.initfrom texture = self.document.colladaDocument.imagesLibrary.FindObject(texture) bTexture = self.document.texturesLibrary.FindObject(texture, True) if not bTexture is None: bMat.setTexture(0, bTexture, Blender.Texture.TexCo.UV, Blender.Texture.MapTo.ALPHA) elif shader.transparency: alpha = 1 - shader.transparency.float if shader.reflective: if shader.reflective.color: # TODO: bug: shader.reflective.color.rgba raise error NoneType with file F40.DAE if debprn: print 'deb: shader.reflective.color=',shader.reflective.color #------- if debprn: print 'deb: shader.reflective.color.rgba=',shader.reflective.color.rgba #------- color = shader.reflective.color.rgba bMat.setMirCol(color[0], color[1], color[2]) if shader.reflectivity: bMat.setRayMirr(shader.reflectivity.float) if isinstance(shader,collada.DaeFxShadeLambert) or isinstance(shader, collada.DaeFxShadeBlinn) or isinstance(shader, collada.DaeFxShadePhong): bMat.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_LAMBERT) if shader.diffuse: ##print shader.diffuse.color.rgba, shader.diffuse.color if not (shader.diffuse.color is None): color = shader.diffuse.color.rgba bMat.setRGBCol(color[0],color[1], color[2]) if not (shader.diffuse.texture is None): # Texture textureSampler = shader.diffuse.texture.texture if not (textureSampler is None): #support 1.4.0: texture = textureSampler #support 1.4.1 for newParam in daeEffect.profileCommon.newParams: if newParam.sid == textureSampler: surfaceID = newParam.sampler.source for newSurface in daeEffect.profileCommon.newParams: if newSurface.sid == surfaceID: texture = newSurface.surface.initfrom texture = self.document.colladaDocument.imagesLibrary.FindObject(texture) bTexture = self.document.texturesLibrary.FindObject(texture, True) if not bTexture is None: bMat.setTexture(0, bTexture, Blender.Texture.TexCo.UV) bMat.setSpec(0) if isinstance(shader, collada.DaeFxShadeBlinn) or isinstance(shader, collada.DaeFxShadePhong): if not isinstance(shader, collada.DaeFxShadePhong): bMat.setSpecShader(Blender.Material.Shaders.SPEC_BLINN) if shader.indexOfRefraction: shader.indexOfRefraction.float bMat.setRefracIndex(shader.indexOfRefraction.float) else: bMat.setSpecShader(Blender.Material.Shaders.SPEC_PHONG) if shader.specular: if not (shader.specular.color is None): #it's a color and not a texture specColor = shader.specular.color.rgba bMat.setSpecCol(specColor[0], specColor[1], specColor[2]) bMat.setSpec(1) if shader.shininess: bMat.setHardness(int(shader.shininess.float) * 4) return bMat def GenerateShader(self, bMaterial, updateShader): shader = updateShader previousDiffuse = None if shader != None: try: previousDiffuse = shader.diffuse except: pass if bMaterial.getSpec() > 0.0: if bMaterial.getSpecShader() == Blender.Material.Shaders.SPEC_BLINN: shader = collada.DaeFxShadeBlinn() shader.AddValue(collada.DaeFxSyntax.INDEXOFREFRACTION, bMaterial.getRefracIndex()) else: shader = collada.DaeFxShadePhong() shader.AddValue(collada.DaeFxSyntax.SPECULAR, [col * bMaterial.getSpec() for col in bMaterial.getSpecCol()]+[1]) shader.AddValue(collada.DaeFxSyntax.SHININESS, bMaterial.getHardness() * 0.25) else: shader = collada.DaeFxShadeLambert() shader.AddValue(collada.DaeFxSyntax.DIFFUSE,bMaterial.getRGBCol()+[1]) shader.AddValue(collada.DaeFxSyntax.TRANSPARENCY, 1 - bMaterial.alpha) shader.AddValue(collada.DaeFxSyntax.TRANSPARENT, [1,1,1,1]) mainColor = bMaterial.getRGBCol() white = [1.0,1.0,1.0] shader.AddValue(collada.DaeFxSyntax.EMISSION, [col * bMaterial.getEmit() for col in white] + [1]) shader.AddValue(collada.DaeFxSyntax.AMBIENT, [col * bMaterial.getAmb() for col in mainColor] + [1]) shader.AddValue(collada.DaeFxSyntax.REFLECTIVE, bMaterial.getMirCol() + [1]) shader.AddValue(collada.DaeFxSyntax.REFLECTIVITY, bMaterial.getRayMirr()) if previousDiffuse != None: shader.diffuse = previousDiffuse return shader def SaveToDae(self, bMaterial): global useRelativePaths, filename daeMaterial = collada.DaeFxMaterial() daeMaterial.id = daeMaterial.name = self.document.CreateID(bMaterial.name, '-Material') instance = collada.DaeFxEffectInstance() daeEffect = self.document.colladaDocument.effectsLibrary.FindObject(bMaterial.name+'-fx') meshNode = MeshNode(self.document) if daeEffect is None: daeEffect = collada.DaeFxEffect() daeEffect.id = daeEffect.name = self.document.CreateID(bMaterial.name , '-fx') # Check if a texture is used for color textures = bMaterial.getTextures() for mTex in textures: # Check if this texture is mapped to Color if not mTex is None and mTex.mapto == Blender.Texture.MapTo.COL and mTex.tex.image != None: texture = mTex.tex textureNode = TextureNode(self.document) textureNode.AddImageTexture(daeEffect, texture.image) shader = self.GenerateShader(bMaterial, daeEffect.profileCommon.technique.shader) daeEffect.AddShader(shader) self.document.colladaDocument.effectsLibrary.AddItem(daeEffect) instance.object = daeEffect daeMaterial.iEffects.append(instance) self.document.colladaDocument.materialsLibrary.AddItem(daeMaterial) return daeMaterial class CameraNode(object): def __init__(self,document): self.document = document def LoadFromDae(self, daeCamera): camID = daeCamera.id camName = daeCamera.name camType = 'persp' clipStart = daeCamera.optics.techniqueCommon.znear clipEnd = daeCamera.optics.techniqueCommon.zfar if daeCamera.optics.techniqueCommon.GetType() == collada.DaeSyntax.ORTHOGRAPHIC: camType = 'ortho' camera = Blender.Camera.New(camType,camID) camera.clipStart = clipStart camera.clipEnd = clipEnd return camera def SaveToDae(self, bCamera): daeCamera = collada.DaeCamera() daeCamera.id = daeCamera.name = self.document.CreateID(bCamera.name,'-Camera') daeOptics = collada.DaeOptics() daeTechniqueCommon = None if bCamera.type == 1: # orthographic daeTechniqueCommon = collada.DaeOptics.DaeOrthoGraphic() else: # perspective daeTechniqueCommon = collada.DaeOptics.DaePerspective() lens = bCamera.getLens( ) daeTechniqueCommon.yfov = 2 * ( math.atan( 16.0 / lens ) ) * radianToAngle daeTechniqueCommon.znear = bCamera.clipStart daeTechniqueCommon.zfar = bCamera.clipEnd daeOptics.techniqueCommon = daeTechniqueCommon daeCamera.optics = daeOptics self.document.colladaDocument.camerasLibrary.AddItem(daeCamera) return daeCamera class LampNode(object): def __init__(self,document): self.document = document def LoadFromDae(self, daeLight): lampID = daeLight.id lampName = daeLight.name # Create a new lampObject lamp = Blender.Lamp.New('Sun',lampID) if daeLight.techniqueCommon.GetType() == collada.DaeSyntax.DIRECTIONAL: lamp.type = Blender.Lamp.Types.Sun elif daeLight.techniqueCommon.GetType() == collada.DaeSyntax.POINT: lamp.type = Blender.Lamp.Types.Lamp # Get the attenuation constAtt = daeLight.techniqueCommon.constantAttenuation linAtt = daeLight.techniqueCommon.linearAttenuation # set the attenuation lamp.energy = 1-constAtt if linAtt > 0.0: lamp.dist = (lamp.energy/2)/ linAtt else: lamp.dist = 5000.0 elif daeLight.techniqueCommon.GetType() == collada.DaeSyntax.SPOT: lamp.type = Blender.Lamp.Types.Spot # Get the attenuation constAtt = daeLight.techniqueCommon.constantAttenuation linAtt = daeLight.techniqueCommon.linearAttenuation # set the attenuation lamp.energy = 1-constAtt if linAtt > 0.0: lamp.dist = (0.5 - constAtt)/ linAtt else: lamp.dist = 5000.0 lamp.spotSize = daeLight.techniqueCommon.falloffAngle lamp.spotBlend = daeLight.techniqueCommon.falloffExponent elif daeLight.techniqueCommon.GetType() == collada.DaeSyntax.AMBIENT: lamp.type = Blender.Lamp.Types.Hemi # Set the color lamp.col = daeLight.techniqueCommon.color return lamp def SaveToDae(self, bLamp): daeLight = collada.DaeLight() daeLight.id = daeLight.name = self.document.CreateID(bLamp.name,'-Light') daeTechniqueCommon = None if bLamp.type == Blender.Lamp.Types.Hemi: # Ambient daeTechniqueCommon = collada.DaeLight.DaeAmbient() elif bLamp.type == Blender.Lamp.Types.Lamp: # Point light daeTechniqueCommon = collada.DaeLight.DaePoint() elif bLamp.type == Blender.Lamp.Types.Spot: # Spot daeTechniqueCommon = collada.DaeLight.DaeSpot() daeTechniqueCommon.constantAttenuation = 1-bLamp.energy daeTechniqueCommon.linearAttenuation = (0.5 - daeTechniqueCommon.constantAttenuation)/bLamp.dist # Export the falloff Angle. daeTechniqueCommon.falloffAngle = bLamp.getSpotSize() elif bLamp.type == Blender.Lamp.Types.Sun: # Directional daeTechniqueCommon = collada.DaeLight.DaeDirectional() else: # area daeTechniqueCommon = collada.DaeOptics.DaeTechniqueCommon() daeTechniqueCommon.color = bLamp.col daeLight.techniqueCommon = daeTechniqueCommon self.document.colladaDocument.lightsLibrary.AddItem(daeLight) return daeLight class Library(object): def __init__(self, document): self.objects = dict() self.document = document self.daeLibrary = None def SetDaeLibrary(self, daeLibrary): self.daeLibrary = daeLibrary def FindObject(self, daeInstance, fromDae, bObject = None): for k in self.objects: if 'url' in dir(daeInstance) and k == daeInstance.url: return self.objects[k][0] elif 'target' in dir(daeInstance) and k == daeInstance.target: return self.objects[k][0] elif isinstance(daeInstance, str): return self.objects[k][0] if fromDae: # dataObject not in library, so add it return self.LoadFromDae(daeInstance, bObject) else: return self.SaveToDae(daeIntance) def FindObjectTotal(self, daeInstance): for k in self.objects: if 'url' in dir(daeInstance) and k == daeInstance.url: return self.objects[k] elif 'target' in dir(daeInstance) and k == daeInstance.target: return self.objects[k] elif isinstance(daeInstance, str): return self.objects[k] return None def FindObjectEx(self, bObject): for k in self.objects: if k == id: return self.objects[k][0] return self.SaveToDae(bObject) def LoadFromDae(self, daeInstance): Debug.Debug('Library: Please override this method','WARNING') def SaveToDae(self, bScene): Debug.Debug('Library: Please override this method','WARNING') class ScenesLibrary(Library): def LoadFromDae(self, daeInstance, bObject): daeVisualScene = self.daeLibrary.FindObject(daeInstance.url) # TODO: Scene: implement multiple scenes return None def SaveToDae(self, id): pass #print bScene class CamerasLibrary(Library): def LoadFromDae(self, daeInstance, bObject): daeCamera = self.daeLibrary.FindObject(daeInstance.url) if daeCamera is None: Debug.Debug('CamerasLibrary: Object with this ID does not exist','ERROR') return camID = daeCamera.id camName = daeCamera.name cameraNode = CameraNode(self.document) camera = cameraNode.LoadFromDae(daeCamera) self.objects[camID] = [camera,camera.name] return camera def SaveToDae(self, id): pass class LampsLibrary(Library): def LoadFromDae(self, daeInstance, bObject): daeLight = self.daeLibrary.FindObject(daeInstance.url) if daeLight is None: Debug.Debug('LightsLibrary: Object with this ID does not exist','ERROR') return lampID = daeLight.id lampName = daeLight.name lampNode = LampNode(self.document) lamp = lampNode.LoadFromDae(daeLight) self.objects[lampID] = [lamp,lamp.name] return lamp def SaveToDae(self, id): pass class MeshLibrary(Library): def LoadFromDae(self, daeInstance, bObject): daeGeometry = None if isinstance(daeInstance, collada.DaeInstance): daeGeometry = self.daeLibrary.FindObject(daeInstance.url) else: ##print daeInstance daeGeometry = self.daeLibrary.FindObject(daeInstance) if daeGeometry is None: Debug.Debug('MeshLibrary: Object with this ID does not exist: %s'%(daeInstance.url),'ERROR') return meshID = daeGeometry.id meshName = daeGeometry.name meshNode = MeshNode(self.document) # Get the materials ( only get the first one right now) bMaterials = daeInstance.bindMaterials meshNode.materials = dict() if bMaterials: for bMaterial in bMaterials: for iMaterial in bMaterial.techniqueCommon.iMaterials: Material = self.document.materialsLibrary.FindObject(iMaterial,True) meshNode.materials[iMaterial.symbol] = Material bMesh = meshNode.LoadFromDae(daeGeometry) # Add this mesh in this library, under it's real name self.objects[meshID] = [bMesh,bMesh.name, meshNode] return bMesh def SaveToDae(self, id): pass class MaterialsLibrary(Library): def LoadFromDae(self, daeInstance, bObject): daeMaterial = self.daeLibrary.FindObject(daeInstance.target) if daeMaterial is None: Debug.Debug('MaterialLibrary: Object with this TARGET:%s does not exist'%(daeInstance.target),'ERROR') return materialID = daeMaterial.id materialName = daeMaterial.name materialNode = MaterialNode(self.document) bMaterial = materialNode.LoadFromDae(daeMaterial) # Add this mesh in this library, under it's real name self.objects[materialID] = [bMaterial,bMaterial.name] return bMaterial def SaveToDae(self, id): pass class TexturesLibrary(Library): def LoadFromDae(self, daeImage, bObject): if daeImage is None: Debug.Debug('TexturesLibrary: Object with this TARGET:%s does not exist'%(daeImage),'ERROR') return imageID = daeImage.id imageName = daeImage.name textureNode = TextureNode(self.document) bTexture = textureNode.LoadFromDae(daeImage) bTexture.setType('Image') # Add this texture in this library, under it's real name self.objects[imageID] = [bTexture,bTexture.name] return bTexture def SaveToDae(self, id): pass class AnimationsLibrary(Library): def LoadFromDae(self, animationName, bObject): if debprn: print 'deb:class AnimationsLibrary << < daeNodeId=',daeNodeId #--------- daeAnimations = [] for daeAnimation in self.daeLibrary.items: #if debprn: print 'deb: daeAnimation=', daeAnimation #----------- for channel in daeAnimation.channels: ta = channel.target.split("/", 1) #if debprn: print 'deb:------> ta=', ta #--------- if ta[0] == daeNodeId: daeAnimations.append(daeAnimation) #if debprn: print 'deb:--> daeAnimations=', daeAnimations #------------ return daeAnimations class ControllersLibrary(Library): def LoadFromDae(self, daeInstance, bObject): daeController = self.daeLibrary.FindObject(daeInstance.url) if daeController is None: Debug.Debug('ControllersLibrary: Object with this TARGET:%s does not exist'%(daeInstance.target),'ERROR') return controllerID = daeController.id controllerName = daeController.name controller = Controller(self.document) bMesh = controller.LoadFromDae(daeController, daeInstance, bObject) # Add this mesh in this library, under it's real name self.objects[controllerID] = [bMesh, bMesh.name] return bMesh