def serialize(self, file): file.write('<mesh>\n') # write shared vertex buffer if available. sharedVertexCount = self.mSharedVertexBuffer.vertexCount() if (sharedVertexCount > 0): file.write('\t<sharedgeometry vertexcount="%d">\n' % sharedVertexCount) self.mSharedVertexBuffer.serialize(file, '\t\t') file.write('\t</sharedgeometry>\n') subMeshNames = list() # write submeshes. file.write('\t<submeshes>\n') for subMesh in self.mSubMeshDict.values(): name = subMesh.mName if (name): if (not name in subMeshNames): subMeshNames.append(name) else: LogManager.logMessage("Mulitple submesh with same name defined: %s" % name, Message.LVL_WARNING) subMesh.serialize(file) file.write('\t</submeshes>\n') # write submesh names if (len(subMeshNames)): file.write('\t<submeshnames>\n') for index, name in enumerate(subMeshNames): file.write('\t\t<submeshname name="%s" index="%d" />\n' % (name, index)) file.write('\t</submeshnames>\n') file.write('</mesh>\n')
def insertPolygon(self, blendMesh, polygon, blendVertexGroups = None, ogreSkeleton = None, fixUpAxisToY = True): polygonVertices = polygon.vertices polygonVertexCount = polygon.loop_total # extract uv information. # Here we convert blender uv data into our own # uv information that lists uvs by vertices. blendUVLoopLayers = blendMesh.uv_layers # construct empty polygon vertex uv list. polygonVertUVs = list() for i in range(polygonVertexCount): polygonVertUVs.append(list()) for uvLoopLayer in blendUVLoopLayers: for i, loopIndex in enumerate(polygon.loop_indices): polygonVertUVs[i].append(uvLoopLayer.data[loopIndex].uv) # extract color information. # Here we convert blender color data into our own # color information that lists colors by vertices. blendColorLoopLayers = blendMesh.vertex_colors # construct empty polygon vertex color list. polygonVertColors = list() for i in range(polygonVertexCount): polygonVertColors.append(list()) for colorLoopLayer in blendColorLoopLayers: for i, loopIndex in enumerate(polygon.loop_indices): polygonVertColors[i].append(colorLoopLayer.data[loopIndex].color) # loop through the vertices and add to this submesh. localIndices = list() useSmooth = polygon.use_smooth for index, uvs, colors in zip(polygonVertices, polygonVertUVs, polygonVertColors): vertex = blendMesh.vertices[index] norm = vertex.normal if (useSmooth) else polygon.normal # grab bone weights. boneWeights = list() if (ogreSkeleton is not None): for groupElement in vertex.groups: groupName = blendVertexGroups[groupElement.group].name boneIndex = ogreSkeleton.getBoneIndex(groupName) if (boneIndex == -1 or abs(groupElement.weight) < 0.000001): continue boneWeight = groupElement.weight boneWeights.append(BoneWeight(boneIndex, boneWeight)) # trim bone weight count if too many defined. if (len(boneWeights) > 4): LogManager.logMessage("More than 4 bone weights are defined for a vertex! Best 4 will be used.", Message.LVL_WARNING) boneWeights.sort(key=attrgetter('mBoneWeight'), reverse=True) while (len(boneWeights) > 4): del boneWeights[-1] localIndices.append(self.mVertexBuffer.addVertex(index, vertex.co, norm, uvs, colors, boneWeights, fixUpAxisToY)) # construct triangle index data. if (polygonVertexCount is 3): self.mFaceData.append(localIndices) else: # split quad into triangles. self.mFaceData.append(localIndices[:3]) self.mFaceData.append([localIndices[0], localIndices[2], localIndices[3]])
def executeOgreXMLConverter(converterpath, filepath, settings=MeshXMLConverterSettings()): global gDevNull try: if os.path.exists(converterpath): filepath = os.path.normpath(filepath) converterpath = os.path.normpath(converterpath) # converter path command = [converterpath] # options if settings.extremityPoints > 0: command.extend(['-x', str(settings.extremityPoints)]) if not settings.edgeLists: command.append('-e') if settings.tangent: command.append('-t') if settings.tangentSemantic == 'uvw': command.extend(['-td', 'uvw']) if settings.tangentSize == '4': command.extend(['-ts', '4']) if settings.splitMirrored: command.append('-tm') if settings.splitRotated: command.append('-tr') if settings.reorganiseVertBuff: command.append('-r') if not settings.optimiseAnimation: command.append('-o') # additional arguments globalSettings = bpy.context.scene.ogre_mesh_exporter additional = shlex.split( globalSettings.ogreXMLConverterAdditionalArg) if len(additional): command.extend(additional) command.extend(['-log', '%s.log' % filepath]) # file path command.append(filepath) LogManager.logMessage("Executing OgrXMLConverter: " + " ".join(command)) p = subprocess.Popen(command, stdout=gDevNull, stderr=gDevNull) return p else: LogManager.logMessage("No converter found at %s" % converterpath, Message.LVL_ERROR) except: traceback.print_exc() return False
def __init__(self, blendMesh = None, blendVertexGroups = None, ogreSkeleton = None, exportSettings = MeshExportSettings()): # shared vertex buffer. self.mSharedVertexBuffer = VertexBuffer() # Blender mesh -> shared vertex index link. self.mSharedMeshVertexIndexLink = dict() # collection of submeshes. self.mSubMeshDict = dict() # skip blend mesh conversion if no blend mesh passed in. if (blendMesh is None): return self.mOgreSkeleton = ogreSkeleton hasBoneWeights = ogreSkeleton is not None # Lets do some pre checking to show warnings if needed. uvLayerCount = len(blendMesh.uv_layers) colorLayerCount = len(blendMesh.vertex_colors) if (uvLayerCount > 8): LogManager.logMessage("More than 8 UV layers in this mesh. Only 8 will be exported.", Message.LVL_WARNING) if (colorLayerCount > 2): LogManager.logMessage("More than 2 color layers in this mesh. Only 2 will be exported.", Message.LVL_WARNING) # setup shared vertex buffer. self.mSharedVertexBuffer.reset(uvLayerCount, colorLayerCount, hasBoneWeights) # split up the mesh into submeshes by materials. # we first get sub mesh shared vertices option. materialList = blendMesh.materials materialCount = len(materialList) subMeshProperties = blendMesh.ogre_mesh_exporter.subMeshProperties while (len(subMeshProperties) < materialCount): subMeshProperties.add() # add more items if needed. while (len(subMeshProperties) > materialCount): subMeshProperties.remove(0) # remove items if needed. LogManager.logMessage("Material Count: %d" % len(materialList), Message.LVL_INFO) for polygon in blendMesh.polygons: # get or create submesh. if (polygon.material_index in self.mSubMeshDict): subMesh = self.mSubMeshDict[polygon.material_index] else: # instantiate submesh base on wether sharing vertices or not. subMeshProperty = subMeshProperties[polygon.material_index] if (subMeshProperty.useSharedVertices): subMesh = SubMesh(self.mSharedVertexBuffer, self.mSharedMeshVertexIndexLink, subMeshProperty.name) else: subMesh = SubMesh(VertexBuffer(uvLayerCount, colorLayerCount, hasBoneWeights), name = subMeshProperty.name) subMesh.mMaterial = None if (len(materialList) == 0) else materialList[polygon.material_index] if (exportSettings.requireMaterials and subMesh.mMaterial == None): LogManager.logMessage("Some faces are not assigned with a material!", Message.LVL_WARNING) LogManager.logMessage("To hide this warning, please uncheck the 'Require Materials' option.", Message.LVL_WARNING) self.mSubMeshDict[polygon.material_index] = subMesh # insert polygon. subMesh.insertPolygon(blendMesh, polygon, blendVertexGroups, ogreSkeleton, exportSettings.fixUpAxisToY)
def serializeBoneAssignments(self, file, indent = ''): file.write('%s\t<boneassignments>\n' % indent) vertexWithNoBoneAssignements = 0; for i, vertex in enumerate(self.mVertexData): if (len(vertex.mBoneWeights) == 0): vertexWithNoBoneAssignements += 1 for boneWeight in vertex.mBoneWeights: file.write('%s\t\t<vertexboneassignment vertexindex="%d" boneindex="%d" weight="%.6f" />\n' % (indent, i, boneWeight.mBoneIndex, boneWeight.mBoneWeight)) if (vertexWithNoBoneAssignements > 0): LogManager.logMessage("There are %d vertices with no bone assignements!" % vertexWithNoBoneAssignements, Message.LVL_WARNING) file.write('%s\t</boneassignments>\n' % indent)
def executeOgreXMLConverter(converterpath, filepath, settings = MeshXMLConverterSettings()): global gDevNull try: if os.path.exists(converterpath): filepath = os.path.normpath(filepath) converterpath = os.path.normpath(converterpath) # converter path command = [converterpath] # options if settings.extremityPoints > 0: command.extend(['-x', str(settings.extremityPoints)]) if not settings.edgeLists: command.append('-e') if settings.tangent: command.append('-t') if settings.tangentSemantic == 'uvw': command.extend(['-td', 'uvw']) if settings.tangentSize == '4': command.extend(['-ts', '4']) if settings.splitMirrored: command.append('-tm') if settings.splitRotated: command.append('-tr') if settings.reorganiseVertBuff: command.append('-r') if not settings.optimiseAnimation: command.append('-o') # additional arguments globalSettings = bpy.context.scene.ogre_mesh_exporter additional = shlex.split(globalSettings.ogreXMLConverterAdditionalArg) if len(additional): command.extend(additional) command.extend(['-log', '%s.log' % filepath]) # file path command.append(filepath) LogManager.logMessage("Executing OgrXMLConverter: " + " ".join(command)) p = subprocess.Popen(command, stdout = gDevNull, stderr = gDevNull) return p else: LogManager.logMessage("No converter found at %s" % converterpath, Message.LVL_ERROR) except: traceback.print_exc() return False
def serialize(self, file): file.write('<mesh>\n') # write shared vertex buffer if available. sharedVertexCount = self.mSharedVertexBuffer.vertexCount() if (sharedVertexCount > 0): file.write('\t<sharedgeometry vertexcount="%d">\n' % sharedVertexCount) self.mSharedVertexBuffer.serialize(file, '\t\t') file.write('\t</sharedgeometry>\n') # write bone assignments if (self.mSharedVertexBuffer.mHasBoneWeights): self.mSharedVertexBuffer.serializeBoneAssignments(file, '\t\t') subMeshNames = list() # write submeshes. file.write('\t<submeshes>\n') for subMesh in self.mSubMeshDict.values(): name = subMesh.mName if (name): if (not name in subMeshNames): subMeshNames.append(name) else: LogManager.logMessage( "Mulitple submesh with same name defined: %s" % name, Message.LVL_WARNING) subMesh.serialize(file) file.write('\t</submeshes>\n') # write submesh names if (len(subMeshNames)): file.write('\t<submeshnames>\n') for index, name in enumerate(subMeshNames): file.write('\t\t<submeshname name="%s" index="%d" />\n' % (name, index)) file.write('\t</submeshnames>\n') # write skeleton link if (self.mOgreSkeleton is not None): file.write('\t<skeletonlink name="%s.skeleton" />\n' % self.mOgreSkeleton.mName) file.write('</mesh>\n')
def invoke(self, context, event): # change our view to log panel. MainExporterPanel.sViewState = MainExporterPanel.VS_LOG self.globalSettings = bpy.context.scene.ogre_mesh_exporter self.collection = self.globalSettings.selectedObjectList.collection self.collectionCount = len(self.collection) self.pendingProcesses = dict() self.completedCount = 0 self.canceling = False self.itemIndex = 0 # clear log. LogManager.reset() # run modal operator mode. context.window_manager.modal_handler_add(self) self.timer = context.window_manager.event_timer_add(0.1, context.window) return {'RUNNING_MODAL'}
def serializeBoneAssignments(self, file, indent=''): file.write('%s\t<boneassignments>\n' % indent) vertexWithNoBoneAssignements = 0 for i, vertex in enumerate(self.mVertexData): if (len(vertex.mBoneWeights) == 0): vertexWithNoBoneAssignements += 1 for boneWeight in vertex.mBoneWeights: file.write( '%s\t\t<vertexboneassignment vertexindex="%d" boneindex="%d" weight="%.6f" />\n' % (indent, i, boneWeight.mBoneIndex, boneWeight.mBoneWeight)) if (vertexWithNoBoneAssignements > 0): LogManager.logMessage( "There are %d vertices with no bone assignements!" % vertexWithNoBoneAssignements, Message.LVL_WARNING) file.write('%s\t</boneassignments>\n' % indent)
def draw(self, context): layout = self.layout globalSettings = bpy.context.scene.ogre_mesh_exporter # #################################################### # display preference view. # #################################################### if (MainExporterPanel.sViewState == MainExporterPanel.VS_PREFERENCE): col = layout.column(True) col.label("Global Static Config (shared across blend files):") staticConfigBox = col.box() col = staticConfigBox.column() col.prop(globalSettings, "ogreXMLConverterPath") col.prop(globalSettings, "ogreXMLConverterAdditionalArg") row = col.row() row.split() row.operator("ogre3d.preferences_apply_static_config", icon='LIBRARY_DATA_DIRECT') xmlConverterOptionsBox = layout.column(True) xmlConverterOptionsBox.label("Global XML Converter Options:") disableConverterOptions = not globalSettings.useXMLConverterOptions row = xmlConverterOptionsBox.row(True) row.prop(globalSettings, "useXMLConverterOptions", "", 'MODIFIER', toggle=True) row = row.row(True) if (disableConverterOptions): row.enabled = False row.prop(globalSettings, "extremityPoints") row.prop(globalSettings, "edgeLists", toggle=True) row.prop(globalSettings, "tangent", toggle=True) row = xmlConverterOptionsBox.row(True) if (disableConverterOptions): row.enabled = False row.prop(globalSettings, "tangentSemantic", "") row.prop(globalSettings, "tangentSize", "") row.prop(globalSettings, "splitMirrored", toggle=True) row.prop(globalSettings, "splitRotated", toggle=True) row = xmlConverterOptionsBox.row(True) if (disableConverterOptions): row.enabled = False row.prop(globalSettings, "reorganiseVertBuff", toggle=True) row.prop(globalSettings, "optimiseAnimation", toggle=True) row = layout.row() row.scale_y = 1.5 row.operator("ogre3d.preferences_back", icon='BACK') return # #################################################### # display log view. # #################################################### elif (MainExporterPanel.sViewState == MainExporterPanel.VS_LOG): LogManager.drawLog(layout) return # #################################################### # display main view. # #################################################### # display selection list. col = layout.column() col.label("Selected:") selectedObjectList = globalSettings.selectedObjectList col.template_list(selectedObjectList, "collection", selectedObjectList, "collectionIndex") # #################################################### # Material settings disableMaterialSettings = not globalSettings.exportMaterials materialExportBox = layout.column(True) materialExportBox.label("Material Settings:") row = materialExportBox.row(True) row.prop(globalSettings, "exportMaterials", "", 'MATERIAL', toggle=True) row = row.row(True) if (disableMaterialSettings): row.enabled = False row.prop(globalSettings, "materialFile", "") row.prop(globalSettings, "copyTextures", "", 'TEXTURE', toggle=True) row = materialExportBox.row(True) if (disableMaterialSettings): row.enabled = False row.prop_enum(globalSettings, "materialExportMode", 'rend') row.prop_enum(globalSettings, "materialExportMode", 'game') row.prop_enum(globalSettings, "materialExportMode", 'custom') row = materialExportBox.row(True) if (disableMaterialSettings or globalSettings.materialExportMode != 'custom'): row.enabled = False row.prop(globalSettings, "templatePath", "") # #################################################### # Mesh settings disableMeshSettings = not globalSettings.exportMeshes meshExportBox = layout.column(True) meshExportBox.label("Mesh Settings:") row = meshExportBox.row(True) row.prop(globalSettings, "exportMeshes", "", 'MESH_MONKEY', toggle=True) row = row.row(True) if (disableMeshSettings): row.enabled = False row.prop(globalSettings, "fixUpAxisToY", icon='NONE', toggle=True) row.prop(globalSettings, "requireMaterials", icon='NONE', toggle=True) row.prop(globalSettings, "applyModifiers", icon='NONE', toggle=True) row = meshExportBox.row(True) if (disableMeshSettings): row.enabled = False row.prop(globalSettings, "skeletonNameFollowMesh", icon='NONE', toggle=True) row.prop(globalSettings, "runOgreXMLConverter", icon='NONE', toggle=True) exportPathValid = os.path.isdir(globalSettings.exportPath) exportPathBox = layout.column(True) exportPathBox.label("Export Path:") exportPathBox.prop(globalSettings, "exportPath", "", icon=('NONE' if (exportPathValid) else 'ERROR')) row = layout.row(True) subrow = row.row() subrow.scale_y = 1.5 exportRow = subrow.row(True) exportRow.scale_y = 1.5 if (not exportPathValid or len(selectedObjectList.collection) == 0 or \ (not globalSettings.exportMeshes and not globalSettings.exportMaterials)): exportRow.enabled = False exportRow.operator("ogre3d.export", icon='SCRIPTWIN') subrow.operator("ogre3d.preferences", icon='SETTINGS') subrow.operator("ogre3d.help", icon='HELP') row = row.row() row.scale_y = 1.5 row.alignment = 'RIGHT' row.operator("ogre3d.log", "", icon='CONSOLE')
def __init__(self, blendMesh=None, blendVertexGroups=None, ogreSkeleton=None, exportSettings=MeshExportSettings()): # shared vertex buffer. self.mSharedVertexBuffer = VertexBuffer() # Blender mesh -> shared vertex index link. self.mSharedMeshVertexIndexLink = dict() # collection of submeshes. self.mSubMeshDict = dict() # skip blend mesh conversion if no blend mesh passed in. if (blendMesh is None): return self.mOgreSkeleton = ogreSkeleton hasBoneWeights = ogreSkeleton is not None # Lets do some pre checking to show warnings if needed. uvLayerCount = len(blendMesh.uv_layers) colorLayerCount = len(blendMesh.vertex_colors) if (uvLayerCount > 8): LogManager.logMessage( "More than 8 UV layers in this mesh. Only 8 will be exported.", Message.LVL_WARNING) if (colorLayerCount > 2): LogManager.logMessage( "More than 2 color layers in this mesh. Only 2 will be exported.", Message.LVL_WARNING) # setup shared vertex buffer. self.mSharedVertexBuffer.reset(uvLayerCount, colorLayerCount, hasBoneWeights) # split up the mesh into submeshes by materials. # we first get sub mesh shared vertices option. materialList = blendMesh.materials materialCount = len(materialList) subMeshProperties = blendMesh.ogre_mesh_exporter.subMeshProperties while (len(subMeshProperties) < materialCount): subMeshProperties.add() # add more items if needed. while (len(subMeshProperties) > materialCount): subMeshProperties.remove(0) # remove items if needed. LogManager.logMessage("Material Count: %d" % len(materialList), Message.LVL_INFO) for polygon in blendMesh.polygons: # get or create submesh. if (polygon.material_index in self.mSubMeshDict): subMesh = self.mSubMeshDict[polygon.material_index] else: # instantiate submesh base on wether sharing vertices or not. subMeshProperty = subMeshProperties[polygon.material_index] if (subMeshProperty.useSharedVertices): subMesh = SubMesh(self.mSharedVertexBuffer, self.mSharedMeshVertexIndexLink, subMeshProperty.name) else: subMesh = SubMesh(VertexBuffer(uvLayerCount, colorLayerCount, hasBoneWeights), name=subMeshProperty.name) subMesh.mMaterial = None if ( len(materialList) == 0) else materialList[polygon.material_index] if (exportSettings.requireMaterials and subMesh.mMaterial == None): LogManager.logMessage( "Some faces are not assigned with a material!", Message.LVL_WARNING) LogManager.logMessage( "To hide this warning, please uncheck the 'Require Materials' option.", Message.LVL_WARNING) self.mSubMeshDict[polygon.material_index] = subMesh # insert polygon. subMesh.insertPolygon(blendMesh, polygon, blendVertexGroups, ogreSkeleton, exportSettings.fixUpAxisToY)
def insertPolygon(self, blendMesh, polygon, blendVertexGroups=None, ogreSkeleton=None, fixUpAxisToY=True): polygonVertices = polygon.vertices polygonVertexCount = polygon.loop_total # extract uv information. # Here we convert blender uv data into our own # uv information that lists uvs by vertices. blendUVLoopLayers = blendMesh.uv_layers # construct empty polygon vertex uv list. polygonVertUVs = list() for i in range(polygonVertexCount): polygonVertUVs.append(list()) for uvLoopLayer in blendUVLoopLayers: for i, loopIndex in enumerate(polygon.loop_indices): polygonVertUVs[i].append(uvLoopLayer.data[loopIndex].uv) # extract color information. # Here we convert blender color data into our own # color information that lists colors by vertices. blendColorLoopLayers = blendMesh.vertex_colors # construct empty polygon vertex color list. polygonVertColors = list() for i in range(polygonVertexCount): polygonVertColors.append(list()) for colorLoopLayer in blendColorLoopLayers: for i, loopIndex in enumerate(polygon.loop_indices): polygonVertColors[i].append( colorLoopLayer.data[loopIndex].color) # loop through the vertices and add to this submesh. localIndices = list() useSmooth = polygon.use_smooth for index, uvs, colors in zip(polygonVertices, polygonVertUVs, polygonVertColors): vertex = blendMesh.vertices[index] norm = vertex.normal if (useSmooth) else polygon.normal # grab bone weights. boneWeights = list() if (ogreSkeleton is not None): for groupElement in vertex.groups: groupName = blendVertexGroups[groupElement.group].name boneIndex = ogreSkeleton.getBoneIndex(groupName) if (boneIndex == -1 or abs(groupElement.weight) < 0.000001): continue boneWeight = groupElement.weight boneWeights.append(BoneWeight(boneIndex, boneWeight)) # trim bone weight count if too many defined. if (len(boneWeights) > 4): LogManager.logMessage( "More than 4 bone weights are defined for a vertex! Best 4 will be used.", Message.LVL_WARNING) boneWeights.sort(key=attrgetter('mBoneWeight'), reverse=True) while (len(boneWeights) > 4): del boneWeights[-1] localIndices.append( self.mVertexBuffer.addVertex(index, vertex.co, norm, uvs, colors, boneWeights, fixUpAxisToY)) # construct triangle index data. if (polygonVertexCount is 3): self.mFaceData.append(localIndices) else: # split quad into triangles. self.mFaceData.append(localIndices[:3]) self.mFaceData.append( [localIndices[0], localIndices[2], localIndices[3]])
def exportMesh(meshObject, filepath): result = list() try: LogManager.logMessage("Output: %s.mesh.xml" % filepath, Message.LVL_INFO) # get combined mesh override & global settings. meshExportSettings = MeshExportSettings.fromRNA(meshObject) if (meshExportSettings.runOgreXMLConverter): meshXMLConverterSettings = MeshXMLConverterSettings.fromRNA(meshObject) # get linked armature parentObject = meshObject.parent if (parentObject and meshObject.parent_type == 'ARMATURE'): armatureObject = parentObject else: # check modifier stack, use first valid armature modifier. for modifier in meshObject.modifiers: if (modifier.type == 'ARMATURE' and (modifier.use_vertex_groups or modifier.use_bone_envelopes)): armatureObject = modifier.object # Do skeleton export first if armature exist. if (armatureObject): # get skeleton file path and name. if (meshExportSettings.skeletonNameFollowMesh): skeletonFilePath = filepath + '.skeleton.xml'; skeletonName = os.path.basename(filepath) else: dirname = os.path.dirname(filepath) skeletonFilePath = dirname + armatureObject.data.name + '.skeleton.xml'; skeletonName = armatureObject.data.name LogManager.logMessage("Skeleton: " + skeletonName, Message.LVL_INFO); # prepare skeleton. meshInverseMatrix = meshObject.matrix_world.inverted() ogreSkeleton = Skeleton(skeletonName, armatureObject, meshInverseMatrix, meshExportSettings.fixUpAxisToY) LogManager.logMessage("Bones: %d" % len(ogreSkeleton.mBones), Message.LVL_INFO); # write skeleton. file = open(skeletonFilePath, "w", encoding="utf8", newline="\n") ogreSkeleton.serialize(file) file.close() LogManager.logMessage("Done exporting skeleton XML.") # Run XML Converter if needed. if (meshExportSettings.runOgreXMLConverter): globalSettings = bpy.context.scene.ogre_mesh_exporter LogManager.logMessage("Converting skeleton to Ogre binary format...") result.append(executeOgreXMLConverter(globalSettings.ogreXMLConverterPath, skeletonFilePath, meshXMLConverterSettings)) # If modifiers need to be applied, we will need to create a new mesh with flattened modifiers. LogManager.logMessage("Apply Modifier: %s" % meshExportSettings.applyModifiers, Message.LVL_INFO) if (meshExportSettings.applyModifiers): mesh = meshObject.to_mesh(bpy.context.scene, True, 'PREVIEW') cleanUpMesh = True else: mesh = meshObject.data cleanUpMesh = False # prepare mesh. ogreMesh = Mesh(mesh, meshObject.vertex_groups, ogreSkeleton, meshExportSettings) LogManager.logMessage("Shared Vertices: %d" % len(ogreMesh.mSharedVertexBuffer.mVertexData), Message.LVL_INFO); LogManager.logMessage("Submeshes: %d" % len(ogreMesh.mSubMeshDict), Message.LVL_INFO); for index, submesh in enumerate(ogreMesh.mSubMeshDict.values()): if (submesh.mShareVertexBuffer): continue LogManager.logMessage(" [%d]%s: vertices: %d" % (index, submesh.mName if (submesh.mName) else '' , len(submesh.mVertexBuffer.mVertexData)), Message.LVL_INFO); # write mesh. meshFilePath = filepath + ".mesh.xml" file = open(meshFilePath, "w", encoding="utf8", newline="\n") ogreMesh.serialize(file) file.close() # remove mesh if we created a new one that has modifiers applied. if (cleanUpMesh): bpy.data.meshes.remove(mesh) ogreMesh = None LogManager.logMessage("Done exporting mesh XML.") # Run XML Converter if needed. if (meshExportSettings.runOgreXMLConverter): globalSettings = bpy.context.scene.ogre_mesh_exporter LogManager.logMessage("Converting mesh to Ogre binary format...") result.append(executeOgreXMLConverter(globalSettings.ogreXMLConverterPath, meshFilePath, meshXMLConverterSettings)) else: LogManager.logMessage("Success!") except IOError as err: LogManager.logMessage("I/O error(%d): %s" % (err.errno, err.strerror), Message.LVL_ERROR) result.append(False) #~ except Exception as err: #~ LogManager.logMessage(str(err), Message.LVL_ERROR) #~ result.append(False) #~ except: #~ traceback.print_exc() #~ result.append(False) return result
def draw(self, context): layout = self.layout globalSettings = bpy.context.scene.ogre_mesh_exporter # #################################################### # display preference view. # #################################################### if (MainExporterPanel.sViewState == MainExporterPanel.VS_PREFERENCE): col = layout.column(True) col.label("Global Static Config (shared across blend files):") staticConfigBox = col.box() col = staticConfigBox.column() col.prop(globalSettings, "ogreXMLConverterPath") col.prop(globalSettings, "ogreXMLConverterAdditionalArg") row = col.row() row.split() row.operator("ogre3d.preferences_apply_static_config", icon = 'LIBRARY_DATA_DIRECT') xmlConverterOptionsBox = layout.column(True) xmlConverterOptionsBox.label("Global XML Converter Options:") disableConverterOptions = not globalSettings.useXMLConverterOptions row = xmlConverterOptionsBox.row(True) row.prop(globalSettings, "useXMLConverterOptions", "", 'MODIFIER', toggle = True) row = row.row(True) if (disableConverterOptions): row.enabled = False row.prop(globalSettings, "extremityPoints") row.prop(globalSettings, "edgeLists", toggle = True) row.prop(globalSettings, "tangent", toggle = True) row = xmlConverterOptionsBox.row(True) if (disableConverterOptions): row.enabled = False row.prop(globalSettings, "tangentSemantic", "") row.prop(globalSettings, "tangentSize", "") row.prop(globalSettings, "splitMirrored", toggle = True) row.prop(globalSettings, "splitRotated", toggle = True) row = xmlConverterOptionsBox.row(True) if (disableConverterOptions): row.enabled = False row.prop(globalSettings, "reorganiseVertBuff", toggle = True) row.prop(globalSettings, "optimiseAnimation", toggle = True) row = layout.row() row.scale_y = 1.5 row.operator("ogre3d.preferences_back", icon = 'BACK') return # #################################################### # display log view. # #################################################### elif (MainExporterPanel.sViewState == MainExporterPanel.VS_LOG): LogManager.drawLog(layout) return # #################################################### # display main view. # #################################################### # display selection list. col = layout.column() col.label("Selected:") selectedObjectList = globalSettings.selectedObjectList col.template_list(selectedObjectList, "collection", selectedObjectList, "collectionIndex") # #################################################### # Material settings disableMaterialSettings = not globalSettings.exportMaterials materialExportBox = layout.column(True) materialExportBox.label("Material Settings:") row = materialExportBox.row(True) row.prop(globalSettings, "exportMaterials", "", 'MATERIAL', toggle = True) row = row.row(True) if (disableMaterialSettings): row.enabled = False row.prop(globalSettings, "materialFile", "") row.prop(globalSettings, "copyTextures", "", 'TEXTURE', toggle = True) row = materialExportBox.row(True) if (disableMaterialSettings): row.enabled = False row.prop_enum(globalSettings, "materialExportMode", 'rend') row.prop_enum(globalSettings, "materialExportMode", 'game') row.prop_enum(globalSettings, "materialExportMode", 'custom') row = materialExportBox.row(True) if (disableMaterialSettings or globalSettings.materialExportMode != 'custom'): row.enabled = False row.prop(globalSettings, "templatePath", "") # #################################################### # Mesh settings disableMeshSettings = not globalSettings.exportMeshes meshExportBox = layout.column(True) meshExportBox.label("Mesh Settings:") row = meshExportBox.row(True) row.prop(globalSettings, "exportMeshes", "", 'MESH_MONKEY', toggle = True) row = row.row(True) if (disableMeshSettings): row.enabled = False row.prop(globalSettings, "fixUpAxisToY", icon = 'NONE', toggle = True) row.prop(globalSettings, "requireMaterials", icon = 'NONE', toggle = True) row.prop(globalSettings, "applyModifiers", icon = 'NONE', toggle = True) row = meshExportBox.row(True) if (disableMeshSettings): row.enabled = False row.prop(globalSettings, "skeletonNameFollowMesh", icon = 'NONE', toggle = True) row.prop(globalSettings, "runOgreXMLConverter", icon = 'NONE', toggle = True) exportPathValid = os.path.isdir(globalSettings.exportPath) exportPathBox = layout.column(True) exportPathBox.label("Export Path:") exportPathBox.prop(globalSettings, "exportPath", "", icon = ('NONE' if (exportPathValid) else 'ERROR')) row = layout.row(True) subrow = row.row() subrow.scale_y = 1.5 exportRow = subrow.row(True) exportRow.scale_y = 1.5 if (not exportPathValid or len(selectedObjectList.collection) == 0 or \ (not globalSettings.exportMeshes and not globalSettings.exportMaterials)): exportRow.enabled = False exportRow.operator("ogre3d.export", icon = 'SCRIPTWIN') subrow.operator("ogre3d.preferences", icon = 'SETTINGS') subrow.operator("ogre3d.help", icon = 'HELP') row = row.row() row.scale_y = 1.5 row.alignment = 'RIGHT' row.operator("ogre3d.log", "", icon = 'CONSOLE')
def exportMesh(meshObject, filepath): result = (False, None) try: LogManager.logMessage("Output: %s" % filepath, Message.LVL_INFO) # get combined mesh override & global settings. meshExportSettings = MeshExportSettings.fromRNA(meshObject) # If modifiers need to be applied, we will need to create a new mesh with flattened modifiers. LogManager.logMessage("Apply Modifier: %s" % meshExportSettings.applyModifiers, Message.LVL_INFO) if (meshExportSettings.applyModifiers): mesh = meshObject.to_mesh(bpy.context.scene, True, 'PREVIEW') cleanUpMesh = True else: mesh = meshObject.data cleanUpMesh = False # prepare mesh. ogreMesh = Mesh(mesh, meshExportSettings) LogManager.logMessage("Shared Vertices: %d" % len(ogreMesh.mSharedVertexBuffer.mVertexData), Message.LVL_INFO); LogManager.logMessage("Submeshes: %d" % len(ogreMesh.mSubMeshDict), Message.LVL_INFO); for index, submesh in enumerate(ogreMesh.mSubMeshDict.values()): if (submesh.mShareVertexBuffer): continue LogManager.logMessage(" [%d]%s: vertices: %d" % (index, submesh.mName if (submesh.mName) else '' , len(submesh.mVertexBuffer.mVertexData)), Message.LVL_INFO); # write mesh. file = open(filepath, "w", encoding="utf8", newline="\n") ogreMesh.serialize(file) file.close() # remove mesh if we created a new one that has modifiers applied. if (cleanUpMesh): bpy.data.meshes.remove(mesh) ogreMesh = None LogManager.logMessage("Done exporting XML.") # check if we need to convert to ogre mesh. if (meshExportSettings.runOgreXMLConverter): globalSettings = bpy.context.scene.ogre_mesh_exporter LogManager.logMessage("Converting mesh to Ogre binary format...") result = convertToOgreMesh(globalSettings.ogreXMLConverterPath, filepath, MeshXMLConverterSettings.fromRNA(meshObject)) else: LogManager.logMessage("Success!") result[0] = True except IOError as err: LogManager.logMessage("I/O error(%d): %s" % (err.errno, err.strerror), Message.LVL_ERROR) except Exception as err: LogManager.logMessage(str(err), Message.LVL_ERROR) except: traceback.print_exc() return result
def modal(self, context, event): if (event.type == 'ESC'): # Cancel for objectLog, process in self.pendingProcesses.items(): process.kill() objectLog.mStatus = "(Canceling...)" self.canceling = True self.refresh(context) return {'RUNNING_MODAL'} if (LogManager.getLogCount() == 0): # add first item to log so we can see the progress. # TODO: Fix this for when no mesh data are exported but materials are. LogManager.addObjectLog(self.collection[0].name, ObjectLog.TYPE_MESH) LogManager.setProgress(0) self.refresh(context) return {'RUNNING_MODAL'} # poll subprocesses to make sure they are done and log accordingly. pendingDelete = list() for objectLog, process in self.pendingProcesses.items(): result = process.poll() if (result == None): continue if (result == 0): objectLog.mStatus = "" objectLog.logMessage("OgreXMLConverter Success!") objectLog.mState = ObjectLog.ST_SUCCEED self.completedCount += 1 elif (self.canceling): objectLog.mStatus = "(Canceled)" objectLog.logMessage("OgreXMLConverter Canceled.", Message.LVL_INFO) objectLog.mState = ObjectLog.ST_CANCELED else: objectLog.mStatus = "" objectLog.logMessage( "OgreXMLConverter Failed! Check log file for more detail.", Message.LVL_ERROR) objectLog.mState = ObjectLog.ST_FAILED pendingDelete.append(objectLog) # cache it for delete. # delete from dictionary what needs to be deleted. # (Wish python has smarter way to do this -_-" Why can't they have erase iterator system?) for objectLog in pendingDelete: del self.pendingProcesses[objectLog] # check that we do not have too many process running. If so, skip until done. if (len(self.pendingProcesses) == OperatorExport.MAX_PENDING_PROCESSES ): self.refresh(context) return {'RUNNING_MODAL'} # Check exit strategy. if (self.itemIndex == self.collectionCount or self.canceling): if (len(self.pendingProcesses)): return {'RUNNING_MODAL'} context.window_manager.event_timer_remove(self.timer) LogManager.setProgress(100) self.refresh(context) return {'FINISHED'} # TODO: Handle exportMeshes == FALSE state. item = self.collection[self.itemIndex] object = bpy.data.objects[item.objectName] #~ time.sleep(0.1) result = exportMesh( object, "%s%s.mesh.xml" % (self.globalSettings.exportPath, item.name)) objectLog = LogManager.getObjectLog(-1) if (not result[0]): objectLog.mState = ObjectLog.ST_FAILED elif (result[1]): self.pendingProcesses[objectLog] = result[1] objectLog.mStatus = "(Converting...)" objectLog.mState = ObjectLog.ST_CONVERTING else: objectLog.mState = ObjectLog.ST_SUCCEED self.completedCount += 1 # update progress bar. LogManager.setProgress( (100 * self.completedCount) / self.collectionCount) self.itemIndex += 1 if (self.itemIndex < self.collectionCount): LogManager.addObjectLog(self.collection[self.itemIndex].name, ObjectLog.TYPE_MESH) # tell blender to refresh. self.refresh(context) return {'RUNNING_MODAL'}
def exportMesh(meshObject, filepath): result = list() try: LogManager.logMessage("Output: %s.mesh.xml" % filepath, Message.LVL_INFO) # get combined mesh override & global settings. meshExportSettings = MeshExportSettings.fromRNA(meshObject) if (meshExportSettings.runOgreXMLConverter): meshXMLConverterSettings = MeshXMLConverterSettings.fromRNA(meshObject) # get linked armature armatureObject = None ogreSkeleton = None parentObject = meshObject.parent if (parentObject and meshObject.parent_type == 'ARMATURE'): armatureObject = parentObject else: # check modifier stack, use first valid armature modifier. for modifier in meshObject.modifiers: if (modifier.type == 'ARMATURE' and (modifier.use_vertex_groups or modifier.use_bone_envelopes)): armatureObject = modifier.object # Do skeleton export first if armature exist. if (armatureObject): # get skeleton file path and name. if (meshExportSettings.skeletonNameFollowMesh): skeletonFilePath = filepath + '.skeleton.xml'; skeletonName = os.path.basename(filepath) else: dirname = os.path.dirname(filepath) skeletonFilePath = dirname + armatureObject.data.name + '.skeleton.xml'; skeletonName = armatureObject.data.name LogManager.logMessage("Skeleton: " + skeletonName, Message.LVL_INFO); # prepare skeleton. meshInverseMatrix = meshObject.matrix_world.inverted() ogreSkeleton = Skeleton(skeletonName, armatureObject, meshInverseMatrix, meshExportSettings.fixUpAxisToY) LogManager.logMessage("Bones: %d" % len(ogreSkeleton.mBones), Message.LVL_INFO); # write skeleton. file = open(skeletonFilePath, "w", encoding="utf8", newline="\n") ogreSkeleton.serialize(file) file.close() LogManager.logMessage("Done exporting skeleton XML.") # Run XML Converter if needed. if (meshExportSettings.runOgreXMLConverter): globalSettings = bpy.context.scene.ogre_mesh_exporter LogManager.logMessage("Converting skeleton to Ogre binary format...") result.append(executeOgreXMLConverter(globalSettings.ogreXMLConverterPath, skeletonFilePath, meshXMLConverterSettings)) # If modifiers need to be applied, we will need to create a new mesh with flattened modifiers. LogManager.logMessage("Apply Modifier: %s" % meshExportSettings.applyModifiers, Message.LVL_INFO) if (meshExportSettings.applyModifiers): mesh = meshObject.to_mesh(bpy.context.scene, True, 'PREVIEW') cleanUpMesh = True else: mesh = meshObject.data cleanUpMesh = False # prepare mesh. ogreMesh = Mesh(mesh, meshObject.vertex_groups, ogreSkeleton, meshExportSettings) LogManager.logMessage("Shared Vertices: %d" % len(ogreMesh.mSharedVertexBuffer.mVertexData), Message.LVL_INFO); LogManager.logMessage("Submeshes: %d" % len(ogreMesh.mSubMeshDict), Message.LVL_INFO); for index, submesh in enumerate(ogreMesh.mSubMeshDict.values()): if (submesh.mShareVertexBuffer): continue LogManager.logMessage(" [%d]%s: vertices: %d" % (index, submesh.mName if (submesh.mName) else '' , len(submesh.mVertexBuffer.mVertexData)), Message.LVL_INFO); # write mesh. meshFilePath = filepath + ".mesh.xml" file = open(meshFilePath, "w", encoding="utf8", newline="\n") ogreMesh.serialize(file) file.close() # remove mesh if we created a new one that has modifiers applied. if (cleanUpMesh): bpy.data.meshes.remove(mesh) ogreMesh = None LogManager.logMessage("Done exporting mesh XML.") # Run XML Converter if needed. if (meshExportSettings.runOgreXMLConverter): globalSettings = bpy.context.scene.ogre_mesh_exporter LogManager.logMessage("Converting mesh to Ogre binary format...") result.append(executeOgreXMLConverter(globalSettings.ogreXMLConverterPath, meshFilePath, meshXMLConverterSettings)) else: LogManager.logMessage("Success!") except IOError as err: LogManager.logMessage("I/O error(%d): %s" % (err.errno, err.strerror), Message.LVL_ERROR) result.append(False) #~ except Exception as err: #~ LogManager.logMessage(str(err), Message.LVL_ERROR) #~ result.append(False) #~ except: #~ traceback.print_exc() #~ result.append(False) return result
def modal(self, context, event): if (event.type == 'ESC'): # Cancel for objectLog, process in self.pendingProcesses.items(): process.kill() objectLog.mStatus = "(Canceling...)" self.canceling = True self.refresh(context) return {'RUNNING_MODAL'} if (LogManager.getLogCount() == 0): # add first item to log so we can see the progress. # TODO: Fix this for when no mesh data are exported but materials are. LogManager.addObjectLog(self.collection[0].name, ObjectLog.TYPE_MESH) LogManager.setProgress(0) self.refresh(context) return {'RUNNING_MODAL'} # poll subprocesses to make sure they are done and log accordingly. pendingDelete = list() for objectLog, process in self.pendingProcesses.items(): result = process.poll() if (result == None): continue if (result == 0): objectLog.mStatus = "" objectLog.logMessage("OgreXMLConverter Success!") objectLog.mState = ObjectLog.ST_SUCCEED self.completedCount += 1 elif (self.canceling): objectLog.mStatus = "(Canceled)" objectLog.logMessage("OgreXMLConverter Canceled.", Message.LVL_INFO) objectLog.mState = ObjectLog.ST_CANCELED else: objectLog.mStatus = "" objectLog.logMessage("OgreXMLConverter Failed! Check log file for more detail.", Message.LVL_ERROR) objectLog.mState = ObjectLog.ST_FAILED pendingDelete.append(objectLog) # cache it for delete. # delete from dictionary what needs to be deleted. # (Wish python has smarter way to do this -_-" Why can't they have erase iterator system?) for objectLog in pendingDelete: del self.pendingProcesses[objectLog] # check that we do not have too many process running. If so, skip until done. if (len(self.pendingProcesses) == OperatorExport.MAX_PENDING_PROCESSES): self.refresh(context) return {'RUNNING_MODAL'} # Check exit strategy. if (self.itemIndex == self.collectionCount or self.canceling): if (len(self.pendingProcesses)): return {'RUNNING_MODAL'} context.window_manager.event_timer_remove(self.timer) LogManager.setProgress(100) self.refresh(context) return {'FINISHED'} # TODO: Handle exportMeshes == FALSE state. item = self.collection[self.itemIndex] object = bpy.data.objects[item.objectName] #~ time.sleep(0.1) result = exportMesh(object, "%s%s.mesh.xml" % (self.globalSettings.exportPath, item.name)) objectLog = LogManager.getObjectLog(-1) if (not result[0]): objectLog.mState = ObjectLog.ST_FAILED elif (result[1]): self.pendingProcesses[objectLog] = result[1] objectLog.mStatus = "(Converting...)" objectLog.mState = ObjectLog.ST_CONVERTING else: objectLog.mState = ObjectLog.ST_SUCCEED self.completedCount += 1 # update progress bar. LogManager.setProgress((100 * self.completedCount) / self.collectionCount) self.itemIndex += 1 if (self.itemIndex < self.collectionCount): LogManager.addObjectLog(self.collection[self.itemIndex].name, ObjectLog.TYPE_MESH) # tell blender to refresh. self.refresh(context) return {'RUNNING_MODAL'}