Commits

Murat Sari committed c8e93e6 Merge

Merge with v1-8

  • Participants
  • Parent commits a58d997, d3beb59
  • Branches v1-9

Comments (0)

Files changed (12)

File RenderSystems/GLES2/src/EAGL/OgreEAGL2Window.mm

         {
             switchFullScreen(false);
         }
-        
-        if(!mUsingExternalView)
-            [mView release];
-        
+
         if(!mUsingExternalViewController)
             [mViewController release];
     }
         
         CAEAGLLayer *eaglLayer = (CAEAGLLayer *)mView.layer;
         OgreAssert(eaglLayer != nil, "EAGL2Window: Failed to retrieve a pointer to the view's Core Animation layer");
-        
+
+        BOOL retainedBacking = NO;
+        NameValuePairList::const_iterator option;
+        if ((option = miscParams->find("retainedBacking")) != miscParams->end())
+        {
+            retainedBacking = StringConverter::parseBool(option->second);
+        }
+
         eaglLayer.opaque = YES;
         eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
-                                        [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
+                                        [NSNumber numberWithBool:retainedBacking], kEAGLDrawablePropertyRetainedBacking,
                                         kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
         // Set up the view controller
         if(!mUsingExternalViewController)
         if(eaglLayer)
         {
             EAGLSharegroup *group = nil;
-            NameValuePairList::const_iterator option;
             
             if ((option = miscParams->find("externalSharegroup")) != miscParams->end())
             {
 
     void EAGL2Window::copyContentsToMemory(const PixelBox &dst, FrameBuffer buffer)
     {
-        if(dst.format != PF_A8R8G8B8)
-            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Only PF_A8R8G8B8 is a supported format for OpenGL ES", __FUNCTION__);
-
         if ((dst.right > mWidth) ||
 			(dst.bottom > mHeight) ||
 			(dst.front != 0) || (dst.back != 1))
         // Read pixel data from the framebuffer
         glPixelStorei(GL_PACK_ALIGNMENT, 4);
         GL_CHECK_ERROR
+
 		glReadPixels((GLint)dst.left, (GLint)dst.top,
                      (GLsizei)dst.getWidth(), (GLsizei)dst.getHeight(),
                      GL_RGBA, GL_UNSIGNED_BYTE, data);
         CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
         CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
         CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace,
-                                        kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,
+                                        kCGBitmapByteOrder32Big | PixelUtil::hasAlpha(dst.format) ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast,
                                         ref, NULL, true, kCGRenderingIntentDefault);
 
         // OpenGL ES measures data in PIXELS

File Tools/Blender2.6Export/ogre_mesh_exporter/__init__.py

 	import imp
 	imp.reload(global_properties)
 	imp.reload(material_properties)
+	imp.reload(skeleton_properties)
 	imp.reload(mesh_properties)
 	imp.reload(main_exporter_panel)
 	imp.reload(log_manager)
+	#~ imp.reload(material_panel)
+	imp.reload(skeleton_panel)
+	imp.reload(skeleton_impl)
 	imp.reload(mesh_panel)
 	imp.reload(mesh_exporter)
 	imp.reload(mesh_impl)
 else:
 	from . import global_properties
 	from . import material_properties
+	from . import skeleton_properties
 	from . import mesh_properties
 	from . import main_exporter_panel
 	from . import log_manager
+	#~ from . import material_panel
+	from . import skeleton_panel
+	from . import skeleton_impl
 	from . import mesh_panel
 	from . import mesh_exporter
 	from . import mesh_impl
 from bpy.app.handlers import persistent
 from ogre_mesh_exporter.global_properties import GlobalProperties, loadStaticConfig
 from ogre_mesh_exporter.material_properties import MaterialProperties
+from ogre_mesh_exporter.skeleton_properties import SkeletonProperties
 from ogre_mesh_exporter.mesh_properties import MeshProperties
 from ogre_mesh_exporter.main_exporter_panel import MainExporterPanel
 
 		description = "Ogre Mesh Exporter properties"
 	)
 
+	bpy.types.Armature.ogre_mesh_exporter = PointerProperty(
+		name = "Ogre Mesh Exporter properties",
+		type = SkeletonProperties,
+		description = "Ogre Mesh Exporter properties"
+	)
+
 	bpy.types.Mesh.ogre_mesh_exporter = PointerProperty(
 		name = "Ogre Mesh Exporter properties",
 		type = MeshProperties,

File Tools/Blender2.6Export/ogre_mesh_exporter/global_properties.py

 STATIC_CONFIG_FILENAME = "ogre_mesh_exporter.cfg"
 
 class SelectedObject(bpy.types.PropertyGroup):
-	name = StringProperty(name = "Name", default = "Unknown")
-	objectName = StringProperty(name = "Object", default = "Unknown")
+	name = StringProperty(name = "Name", default = "Unknown", options = set())
+	objectName = StringProperty(name = "Object", default = "Unknown", options = set())
 
 class SelectedObjectList(bpy.types.PropertyGroup):
 	def onSelectionChanged(self, context):
 		# Set the selected object as active.
 		bpy.context.scene.objects.active = bpy.data.objects[self.collection[self.collectionIndex].objectName]
 
-	collection = CollectionProperty(type = SelectedObject)
-	collectionIndex = IntProperty(min = -1, default = -1, update=onSelectionChanged)
+	collection = CollectionProperty(type = SelectedObject, options = set())
+	collectionIndex = IntProperty(min = -1, default = -1, options = set(), update=onSelectionChanged)
 
 class GlobalProperties(bpy.types.PropertyGroup):
 	# ##############################################
 	exportMaterials = BoolProperty(
 		name = "Export Materials",
 		description = "Enable/Disable material file exporting.",
-		default = True
+		default = True,
+		options = set()
 	)
 	materialFile = StringProperty(
 		name = "Material File",
 		description = "File name of material.",
-		default = "Scene.material"
+		default = "Scene.material",
+		options = set()
 	)
 	copyTextures = BoolProperty(
 		name = "Copy Textures",
 		description = "Copy textures to export path.",
-		default = False
+		default = False,
+		options = set()
 	)
 	materialExportMode = EnumProperty(
-		name= "Material Export Mode",
-		description= "Diffrent Material Export Modes.",
-		items=(("rend", "Rendering Materials", "Export using rendering materials."),
+		name = "Material Export Mode",
+		description = "Diffrent Material Export Modes.",
+		items = (("rend", "Rendering Materials", "Export using rendering materials."),
 				("game", "Game Engine Materials", "Export using game engine materials."),
 				("custom",  "Custom Materials", "Export using custom template based materials."),
 				),
-		default= "rend"
+		default = "rend",
+		options = set()
 	)
 	templatePath = StringProperty(
 		name = "Template Path",
 		description = "Path to material templates for generating custom materials.",
-		subtype = "DIR_PATH"
+		subtype = "DIR_PATH",
+		options = set()
 	)
 
 	# ##############################################
 	exportMeshes = BoolProperty(
 		name = "Export Meshes",
 		description = "Enable/Disable mesh & skeleton file exporting.",
-		default = True
+		default = True,
+		options = set()
 	)
 	exportPath = StringProperty(
 		name = "Export Path",
 		description = "Path to export files.",
-		subtype = "DIR_PATH"
+		subtype = "DIR_PATH",
+		options = set()
 	)
 	fixUpAxisToY = BoolProperty(
 		name = "Fix Up Axis to Y",
 		description = "Fix up axis as Y instead of Z.",
-		default = True
+		default = True,
+		options = set()
 	)
 	requireMaterials = BoolProperty(
 		name = "Require Materials",
 		description = "Generate Error message when part of a mesh is not assigned with a material.",
-		default = True
+		default = True,
+		options = set()
 	)
 	applyModifiers = BoolProperty(
 		name = "Apply Modifiers",
 		description = "Apply mesh modifiers before export. (Slow and may break vertex order for morph targets!)",
-		default = False
+		default = False,
+		options = set()
 	)
 	skeletonNameFollowMesh = BoolProperty(
 		name = "Skeleton Name Follow Mesh",
 		description = "Use mesh name for exported skeleton name instead of the armature name.",
-		default = True
+		default = True,
+		options = set()
 	)
 	runOgreXMLConverter = BoolProperty(
 		name = "OgreXMLConverter",
 		description = "Run OgreXMLConverter on exported XML files.",
-		default = True
+		default = True,
+		options = set()
 	)
 
 	# ##############################################
 	ogreXMLConverterPath = StringProperty(
 		name = "Ogre XML Converter Path",
 		description = "Path to OgreXMLConverter.",
-		subtype = "FILE_PATH"
+		subtype = "FILE_PATH",
+		options = {'SKIP_SAVE'}
 	)
 	# Saved to the shared config file as above.
 	ogreXMLConverterAdditionalArg = StringProperty(
 		name = "Additional Arguments",
-		description = "Additional Arguments outside of the provided options below. Note that this is shared across all blend files."
+		description = "Additional Arguments outside of the provided options below. Note that this is shared across all blend files.",
+		options = {'SKIP_SAVE'}
 	)
 
 	useXMLConverterOptions = BoolProperty(
 		name = "Use XML Converter Options",
 		description = "Use the settings set by this XML converter option. These options are saved in blend file. If you want a globally shared option, please uncheck this and use the 'Additional Arguments' option.",
-		default = True
+		default = True,
+		options = {'SKIP_SAVE'}
 	)
 	extremityPoints = IntProperty(
 		name = "Extremity Points",
 		description = "Generate no more than num eXtremes for every submesh. (For submesh render sorting when using alpha materials on submesh)",
 		soft_min = 0,
-		soft_max = 65536
+		soft_max = 65536,
+		options = {'SKIP_SAVE'}
 	)
 	edgeLists = BoolProperty(
 		name = "Edge Lists",
 		description = "Generate edge lists. (Useful for outlining or doing stencil shadows)",
-		default = False
+		default = False,
+		options = {'SKIP_SAVE'}
 	)
 	tangent = BoolProperty(
 		name = "Tangent",
 		description = "Generate tangent.",
-		default = False
+		default = False,
+		options = {'SKIP_SAVE'}
 	)
 	tangentSemantic = EnumProperty(
 		name = "Tangent Semantic",
 		items=(("uvw", "uvw", "Use UV semantic."),
 				("tangent", "tangent", "Use tangent semantic."),
 				),
-		default= "tangent"
+		default= "tangent",
+		options = {'SKIP_SAVE'}
 	)
 	tangentSize = EnumProperty(
 		name = "Tangent Size",
 		items=(("4", "4 component (parity)", "Use 4 component tangent where 4th component is parity."),
 				("3", "3 component", "Use 3 component tangent."),
 				),
-		default= "3"
+		default= "3",
+		options = {'SKIP_SAVE'}
 	)
 	splitMirrored = BoolProperty(
 		name = "Split Mirrored",
 		description = "Split tangent vertices at UV mirror points.",
-		default = False
+		default = False,
+		options = {'SKIP_SAVE'}
 	)
 	splitRotated = BoolProperty(
 		name = "Split Rotated",
 		description = "Split tangent vertices where basis is rotated > 90 degrees.",
-		default = False
+		default = False,
+		options = {'SKIP_SAVE'}
 	)
 	reorganiseVertBuff = BoolProperty(
 		name = "Reorganise Vertex Buffers",
 		description = "Reorganise vertex buffer to make it GPU vertex cache friendly.",
-		default = True
+		default = True,
+		options = {'SKIP_SAVE'}
 	)
 	optimiseAnimation = BoolProperty(
 		name = "Optimise Animation",
 		description = "Optimise out redundant tracks & keyframes.",
-		default = True
+		default = True,
+		options = {'SKIP_SAVE'}
 	)
 
 	# ##############################################
 	logPageSize = IntProperty(
 		name = "Log Page Size",
 		description = "Size of a visible log page",
-		default = 10
+		default = 10,
+		options = {'SKIP_SAVE'}
 	)
 	logPercentage = IntProperty(
 		name = "Log Percentage",
 		description = "Log progress",
 		default = 100, min = 0, max = 100,
-		subtype = 'PERCENTAGE'
+		subtype = 'PERCENTAGE',
+		options = {'SKIP_SAVE'}
 	)
 
 	# ##############################################
 	# temporary collection for listing selected meshes.
 	selectedObjectList = PointerProperty(type = SelectedObjectList)
 
+	def onDummyTrueChanged(self, context):
+		# Never let Dummy change.
+		self.dummyTrue = True
+	def onDummyFalseChanged(self, context):
+		# Never let Dummy change.
+		self.dummyFalse = False
+
+	# Dummy property for tab use. (NEVER SET)
+	dummyTrue = BoolProperty(
+		default = True,
+		update = onDummyTrueChanged,
+		options = {'SKIP_SAVE'})
+
+	# Dummy property for label box use. (NEVER SET)
+	dummyFalse = BoolProperty(
+		default = False,
+		update = onDummyFalseChanged,
+		options = {'SKIP_SAVE'})
+
 # Load static data from config file.
 def loadStaticConfig():
 	global OGRE_XML_CONVERTERPATH

File Tools/Blender2.6Export/ogre_mesh_exporter/main_exporter_panel.py

 # THE SOFTWARE.
 # ##### END MIT LICENSE BLOCK #####
 
-from ogre_mesh_exporter.global_properties import saveStaticConfig
+from ogre_mesh_exporter.global_properties import loadStaticConfig, saveStaticConfig
 from ogre_mesh_exporter.mesh_exporter import exportMesh
 from ogre_mesh_exporter.log_manager import LogManager, ObjectLog, Message
 
 		subrow.scale_y = 1.5
 		exportRow = subrow.row(True)
 		exportRow.scale_y = 1.5
-		if (not exportPathValid or len(selectedObjectList.collection) == 0 or \
+		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')
+		exportRow.operator("ogre3d.export", icon = 'EXPORT')
 		subrow.operator("ogre3d.preferences", icon = 'SETTINGS')
 		subrow.operator("ogre3d.help", icon = 'HELP')
 		row = row.row()
 
 	def modal(self, context, event):
 		if (event.type == 'ESC'):  # Cancel
-			for objectLog, process in self.pendingProcesses.items():
-				process.kill() 
+			for objectLog, processList in self.pendingProcesses.items():
+				for process in processList: process.kill() 
 				objectLog.mStatus = "(Canceling...)"
 			self.canceling = True
 			self.refresh(context)
 
 		# 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
+		for objectLog, processList in self.pendingProcesses.items():
+			result = 0
+			for process in processList:
+				pResult = process.poll()
+				if (pResult == None): continue
+				result += pResult
 			if (result == 0):
 				objectLog.mStatus = ""
 				objectLog.logMessage("OgreXMLConverter Success!")
 		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))
+		result = exportMesh(object, 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]
+		if (False in result): objectLog.mState = ObjectLog.ST_FAILED
+		elif (len(result)):
+			self.pendingProcesses[objectLog] = result
 			objectLog.mStatus = "(Converting...)"
 			objectLog.mState = ObjectLog.ST_CONVERTING
 		else:

File Tools/Blender2.6Export/ogre_mesh_exporter/material_properties.py

 	materialExportMode_override = BoolProperty(
 		name = "Material Export Mode Override",
 		description = "Override global setting.",
-		default = True
+		default = True,
+		options = set()
 	)
 	materialExportMode = EnumProperty(
 		name= "Material Export Mode",
 				("game", "Game Engine Materials", "Export using game engine materials."),
 				("custom",  "Custom Materials", "Export using custom template based materials."),
 				),
-		default= "rend"
+		default= "rend",
+		options = set()
 	)
 	template_name = StringProperty(
 		name = "Template Name",
-		description = "Name of material template."
+		description = "Name of material template.",
+		options = set()
 	)

File Tools/Blender2.6Export/ogre_mesh_exporter/mesh_exporter.py

 
 from ogre_mesh_exporter.mesh_impl import Mesh
 from ogre_mesh_exporter.mesh_impl import MeshExportSettings
+from ogre_mesh_exporter.skeleton_impl import Skeleton
 from ogre_mesh_exporter.log_manager import LogManager, Message
 
 # Mesh XML Converter settings class to define how we are going to convert the mesh.
 			reorganiseVertBuff = meshSettings.reorganiseVertBuff if (meshSettings.reorganiseVertBuff_override) else globalSettings.reorganiseVertBuff,
 			optimiseAnimation = meshSettings.optimiseAnimation if (meshSettings.optimiseAnimation_override) else globalSettings.optimiseAnimation)
 
+# NOTE: filepath must NOT contain extension part. Mesh will append mesh.xml and Skeleton will append skeleton.xml.
 def exportMesh(meshObject, filepath):
-	result = (False, None)
+	result = list()
 	try:
-		LogManager.logMessage("Output: %s" % filepath, Message.LVL_INFO)
+		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)
 			cleanUpMesh = False
 
 		# prepare mesh.
-		ogreMesh = Mesh(mesh, meshExportSettings)
+		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()):
 			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")
+		meshFilePath = filepath + ".mesh.xml"
+		file = open(meshFilePath, "w", encoding="utf8", newline="\n")
 		ogreMesh.serialize(file)
 		file.close()
 
 		if (cleanUpMesh): bpy.data.meshes.remove(mesh)
 		ogreMesh = None
 
-		LogManager.logMessage("Done exporting XML.")
+		LogManager.logMessage("Done exporting mesh XML.")
 
-		# check if we need to convert to ogre mesh.
+		# Run XML Converter if needed.
 		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))
+			result.append(executeOgreXMLConverter(globalSettings.ogreXMLConverterPath, meshFilePath, meshXMLConverterSettings))
 		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()
+		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
 
 gDevNull = open(os.devnull, "w")
 
-def convertToOgreMesh(converterpath, filepath, settings = MeshXMLConverterSettings()):
+def executeOgreXMLConverter(converterpath, filepath, settings = MeshXMLConverterSettings()):
 	global gDevNull
 	try:
 		if os.path.exists(converterpath):
 
 			LogManager.logMessage("Executing OgrXMLConverter: " + " ".join(command))
 			p = subprocess.Popen(command, stdout = gDevNull, stderr = gDevNull)
-			return (True, p)
+			return p
 		else:
 			LogManager.logMessage("No converter found at %s" % converterpath, Message.LVL_ERROR)
 	except:
 		traceback.print_exc()
 
-	return (False, None)
+	return False

File Tools/Blender2.6Export/ogre_mesh_exporter/mesh_impl.py

 import bpy, mathutils
 
 from ogre_mesh_exporter.log_manager import LogManager, Message
+from operator import attrgetter
 
 # Mesh export settings class to define how we are going to export the mesh.
 class MeshExportSettings():
 			skeletonNameFollowMesh = meshSettings.skeletonNameFollowMesh if (meshSettings.skeletonNameFollowMesh_override) else globalSettings.skeletonNameFollowMesh,
 			runOgreXMLConverter = globalSettings.runOgreXMLConverter)
 
+class BoneWeight():
+	def __init__(self, boneIndex, boneWeight):
+		self.mBoneIndex = boneIndex
+		self.mBoneWeight = boneWeight
+
 class Vertex():
-	def __init__(self, pos, norm, uvs = list(), colors = list()):
+	def __init__(self, pos, norm, uvs = list(), colors = list(), boneWeights = list()):
 		self.mPosition = pos
 		self.mNormal = norm
 		self.mUVs = uvs
 		self.mColors = colors
+		self.mBoneWeights = boneWeights
 
 	def match(self, norm, uvs, colors):
 		# Test normal.
 		return True
 
 class VertexBuffer():
-	def __init__(self, uvLayers = 0, colorLayers = 0):
+	def __init__(self, uvLayers = 0, colorLayers = 0, hasBoneWeights = False):
 		# Vertex data.
 		self.mVertexData = list()
 		self.mUVLayers = uvLayers
 		self.mColorLayers = colorLayers
+		self.mHasBoneWeights = hasBoneWeights
 
 		# Blender mesh -> vertex index link.
 		# Only useful when exporting.
 		self.mMeshVertexIndexLink = dict()
 
-	def reset(self, uvLayers, colorLayers):
+	def reset(self, uvLayers, colorLayers, hasBoneWeights = False):
 		self.mVertexData = list()
 		self.mUVLayers = uvLayers
 		self.mColorLayers = colorLayers
+		self.mHasBoneWeights = hasBoneWeights
 
 	def vertexCount(self):
 		return len(self.mVertexData)
 	# This method adds a vertex from the given blend mesh index into the buffer.
 	# If the uv information does not match the recorded vertex, it will automatically
 	# clone a new vertex for use.
-	def addVertex(self, index, pos, norm, uvs, colors, fixUpAxisToY):
+	def addVertex(self, index, pos, norm, uvs, colors, boneWeights = list(), fixUpAxisToY = True):
 		# Fix Up axis to Y (swap Y and Z and negate Z)
 		if (fixUpAxisToY):
 			pos = [pos[0], pos[2], -pos[1]]
 		localIndex = len(self.mVertexData)
 		if (index not in self.mMeshVertexIndexLink): self.mMeshVertexIndexLink[index] = list()
 		self.mMeshVertexIndexLink[index].append(localIndex)
-		self.mVertexData.append(Vertex(pos, norm, uvs, colors))
+		self.mVertexData.append(Vertex(pos, norm, uvs, colors, boneWeights))
 		return localIndex
 
 	def serialize(self, file, indent = ''):
 
 		file.write('%s</vertexbuffer>\n' % indent)
 
+	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)
+
 class SubMesh():
 	def __init__(self, vertexBuffer = None, meshVertexIndexLink = None, name = None):
 		# True if submesh is sharing vertex buffer.
 		if ((vertexBuffer is not None) and (meshVertexIndexLink is not None)):
 			self.mShareVertexBuffer = True
 
-	def insertPolygon(self, blendMesh, polygon, fixUpAxisToY):
+	def insertPolygon(self, blendMesh, polygon, blendVertexGroups = None, ogreSkeleton = None, fixUpAxisToY = True):
 		polygonVertices = polygon.vertices
 		polygonVertexCount = polygon.loop_total
 
 		for index, uvs, colors in zip(polygonVertices, polygonVertUVs, polygonVertColors):
 			vertex = blendMesh.vertices[index]
 			norm = vertex.normal if (useSmooth) else polygon.normal
-			localIndices.append(self.mVertexBuffer.addVertex(index, vertex.co, norm, uvs, colors, fixUpAxisToY))
+
+			# 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):
 			(materialAttribute, 'true' if self.mShareVertexBuffer else 'false',
 			'true' if (vertexCount > 65536) else 'false'))
 
+		# write face data.
+		file.write('\t\t\t<faces count="%d">\n' % len(self.mFaceData))
+		for face in self.mFaceData:
+			file.write('\t\t\t\t<face v1="%d" v2="%d" v3="%d" />\n' % tuple(face))
+		file.write('\t\t\t</faces>\n')
+
 		# write submesh vertex buffer if not shared.
 		if (not self.mShareVertexBuffer):
 			file.write('\t\t\t<geometry vertexcount="%d">\n' % vertexCount)
 			self.mVertexBuffer.serialize(file, '\t\t\t\t')
 			file.write('\t\t\t</geometry>\n')
 
-		# write face data.
-		file.write('\t\t\t<faces count="%d">\n' % len(self.mFaceData))
-		for face in self.mFaceData:
-			file.write('\t\t\t\t<face v1="%d" v2="%d" v3="%d" />\n' % tuple(face))
-		file.write('\t\t\t</faces>\n')
+			# write bone assignments
+			if (self.mShareVertexBuffer.mHasBoneWeights):
+				self.mSharedVertexBuffer.serializeBoneAssignments(file, '\t\t\t')
 
 		file.write('\t\t</submesh>\n')
 
 class Mesh():
-	def __init__(self, blendMesh = None, exportSettings = MeshExportSettings()):
+	def __init__(self, blendMesh = None, blendVertexGroups = None, ogreSkeleton = None, exportSettings = MeshExportSettings()):
 		# shared vertex buffer.
 		self.mSharedVertexBuffer = VertexBuffer()
 		# Blender mesh -> shared vertex index link.
 
 		# 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)
 		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)
+		self.mSharedVertexBuffer.reset(uvLayerCount, colorLayerCount, hasBoneWeights)
 
 		# split up the mesh into submeshes by materials.
 		# we first get sub mesh shared vertices option.
 				if (subMeshProperty.useSharedVertices):
 					subMesh = SubMesh(self.mSharedVertexBuffer, self.mSharedMeshVertexIndexLink, subMeshProperty.name)
 				else:
-					subMesh = SubMesh(VertexBuffer(uvLayerCount, colorLayerCount), name = subMeshProperty.name)
+					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):
 				self.mSubMeshDict[polygon.material_index] = subMesh
 
 			# insert polygon.
-			subMesh.insertPolygon(blendMesh, polygon, exportSettings.fixUpAxisToY)
+			subMesh.insertPolygon(blendMesh, polygon, blendVertexGroups, ogreSkeleton, exportSettings.fixUpAxisToY)
 
 	def serialize(self, file):
 		file.write('<mesh>\n')
 			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\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')

File Tools/Blender2.6Export/ogre_mesh_exporter/mesh_panel.py

 
 import bpy
 
+# Helper class to get linked skeleton settings from mesh object
+def getSkeletonSettings(context):
+	# get linked armature
+	meshObject = context.object
+	parentObject = meshObject.parent
+	armatureObject = None
+	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
+
+	return armatureObject.data.ogre_mesh_exporter \
+		if (armatureObject is not None) else None
+
 class MeshExporterPanel(bpy.types.Panel):
 	bl_idname = "ogre3d_mesh_panel"
 	bl_label = "Ogre Mesh Exporter"
 
 	def draw(self, context):
 		layout = self.layout
+		globalSettings = bpy.context.scene.ogre_mesh_exporter
 		meshSettings = context.mesh.ogre_mesh_exporter
+		skeletonSettings = getSkeletonSettings(context)
 
-		layout.prop(meshSettings, "exportEnabled", icon = 'MESH_MONKEY', toggle = True)
+		row = layout.row(True)
+		row.prop(meshSettings, "exportEnabled", icon = 'EXPORT', toggle = True)
+
+		row = row.row()
+		row.alignment = 'RIGHT'
+		row.scale_x = 0.3
+		row.enabled = meshSettings.exportEnabled
+		row.prop_enum(meshSettings, "exportTab", 'mesh', text = "", icon = 'MESH_MONKEY')
+		row.prop_enum(meshSettings, "exportTab", 'animation', text = "", icon = 'ANIM')
+		row.prop_enum(meshSettings, "exportTab", 'settings', text = "", icon = 'SETTINGS')
+
 		if (not meshSettings.exportEnabled): return
 
 		# prepare material slot shared vertex info.
 		while (len(subMeshProperties) < materialCount): subMeshProperties.add() # add more items if needed.
 		while (len(subMeshProperties) > materialCount): subMeshProperties.remove(0) # remove items if needed.
 
-		layout.label("Submesh Properties:")
-		submeshNames = list()
-		box = layout.box()
-		for index, subMeshProperty in enumerate(subMeshProperties):
-			row = box.row(True)
+		if (meshSettings.exportTab == 'mesh'):
+			layout.label("Submesh Properties:")
+			submeshNames = list()
+			box = layout.box()
+			if (len(subMeshProperties) == 0): box.label("No Materials Defined", icon = 'INFO')
+			for index, subMeshProperty in enumerate(subMeshProperties):
+				row = box.row(True)
 
-			# Material index & name.
-			material = materialList[index]
-			row.label("[%d]%s" % (index, "NONE" if (material == None) else materialList[index].name), icon = 'ERROR' if (material == None) else 'MATERIAL')
+				# Material index & name.
+				material = materialList[index]
+				row.label("[%d]%s" % (index, "NONE" if (material == None) else materialList[index].name), icon = 'ERROR' if (material == None) else 'MATERIAL')
 
-			# Submesh name.
-			subrow = row.row()
-			if (subMeshProperty.name in submeshNames): subrow.alert = True
-			else: submeshNames.append(subMeshProperty.name)
-			subrow.prop(subMeshProperty, "name", "")
+				# Submesh name.
+				subrow = row.row()
+				if (subMeshProperty.name in submeshNames): subrow.alert = True
+				else: submeshNames.append(subMeshProperty.name)
+				subrow.prop(subMeshProperty, "name", "")
 
-			# Use shared vertices.
-			row.prop(subMeshProperty, "useSharedVertices", "", icon = 'GROUP_VERTEX')
+				# Use shared vertices.
+				row.prop(subMeshProperty, "useSharedVertices", "", icon = 'GROUP_VERTEX')
 
-			# Select vertices under this submesh.
-			if (context.mode == 'EDIT_MESH'):
-				prop = row.operator("ogre3d.select_submesh_vertices", "", icon='MESH_DATA')
-				prop.index = index
+				# Select vertices under this submesh.
+				if (context.mode == 'EDIT_MESH'):
+					prop = row.operator("ogre3d.select_submesh_vertices", "", icon='MESH_DATA')
+					prop.index = index
 
-		# Mesh override settings:
-		layout.label("Mesh Override Settings:")
-		col = layout.column(True)
+		elif (meshSettings.exportTab == 'animation'):
+			# Animations.
+			layout.separator()
+			row = layout.row(True)
+			row.label("Animations:")
+			row.prop_enum(meshSettings, "animationTab", 'skel', icon = 'POSE_DATA')
+			row.prop_enum(meshSettings, "animationTab", 'pose', icon = 'OUTLINER_DATA_MESH')
+			row.prop_enum(meshSettings, "animationTab", 'morph', icon = 'OUTLINER_DATA_MESH')
 
-		row = col.row(True)
-		overrideSetting = meshSettings.requireMaterials_override
-		row.prop(meshSettings, "requireMaterials_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "requireMaterials", toggle = True)
+			box = layout.box()
+			table = box.column(True)
+			row = table.row()
+			col = row.column()
+			delCol = row.column()
 
-		row = col.row(True)
-		overrideSetting = meshSettings.skeletonNameFollowMesh_override
-		row.prop(meshSettings, "skeletonNameFollowMesh_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "skeletonNameFollowMesh", toggle = True)
+			# draw grid header.
+			row = col.row()
+			row.prop(globalSettings, "dummyTrue", toggle = True, text = "Action")
+			row.prop(globalSettings, "dummyTrue", toggle = True, text = "Name")
+			frameRow = row.row()
+			frameRow.scale_x = 0.5
+			frameRow.prop(globalSettings, "dummyTrue", toggle = True, text = "Start")
+			frameRow.prop(globalSettings, "dummyTrue", toggle = True, text = "End")
+			delCol.prop(globalSettings, "dummyTrue", text = "", icon = 'SCRIPTWIN')
 
-		row = col.row(True)
-		overrideSetting = meshSettings.applyModifiers_override
-		row.prop(meshSettings, "applyModifiers_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "applyModifiers", toggle = True)
+			# Populate skeletal animation action list.
+			if (meshSettings.animationTab == 'skel'):
+				if (skeletonSettings is not None):
+					if (len(skeletonSettings.exportSkeletonActions) == 0):
+						table.prop(globalSettings, "dummyFalse", toggle = True, text = "No Animations")
+					for index, item in enumerate(skeletonSettings.exportSkeletonActions):
+						row = col.row()
+						row.prop(item, "action", text = "", icon = 'ACTION')
+						row.prop(item, "name", text = "")
+						frameRow = row.row()
+						frameRow.scale_x = 0.5
+						frameRow.prop(item, "startFrame", text = "")
+						frameRow.prop(item, "endFrame", text = "")
+						prop = delCol.operator("ogre3d.skeleton_delete_animation", text = "", icon = 'ZOOMOUT')
+						prop.index = index
+					box.operator("ogre3d.skeleton_add_animation", icon = 'ZOOMIN')
+				else:
+					table.prop(globalSettings, "dummyFalse", toggle = True, text = "No Armature Link")
+			elif (meshSettings.animationTab == 'pose'):
+				table.prop(globalSettings, "dummyFalse", toggle = True, text = "Not Implemented Yet")
+			else:
+				table.prop(globalSettings, "dummyFalse", toggle = True, text = "Not Implemented Yet")
 
-		# XML Converter override settings:
-		layout.label("XML Converter Override Settings:")
-		col = layout.column(True)
+		else:
+			# Mesh override settings:
+			layout.label("Mesh Override Settings:")
+			col = layout.column(True)
+			row = col.row()
+			overrideSetting = meshSettings.requireMaterials_override
+			row.prop(meshSettings, "requireMaterials_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "requireMaterials", toggle = True)
 
-		row = col.row(True)
-		overrideSetting = meshSettings.extremityPoints_override
-		row.prop(meshSettings, "extremityPoints_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "extremityPoints")
+			row = col.row()
+			overrideSetting = meshSettings.skeletonNameFollowMesh_override
+			row.prop(meshSettings, "skeletonNameFollowMesh_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "skeletonNameFollowMesh", toggle = True)
 
-		row = col.row(True)
-		overrideSetting = meshSettings.edgeLists_override
-		row.prop(meshSettings, "edgeLists_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "edgeLists", toggle = True)
+			row = col.row()
+			overrideSetting = meshSettings.applyModifiers_override
+			row.prop(meshSettings, "applyModifiers_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "applyModifiers", toggle = True)
 
-		row = col.row(True)
-		overrideSetting = meshSettings.tangent_override
-		row.prop(meshSettings, "tangent_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "tangent", toggle = True)
+			# XML Converter override settings:
+			layout.label("XML Converter Override Settings:")
+			col = layout.column(True)
+			row = col.row()
+			overrideSetting = meshSettings.extremityPoints_override
+			row.prop(meshSettings, "extremityPoints_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "extremityPoints")
 
-		row = col.row(True)
-		overrideSetting = meshSettings.tangentSemantic_override
-		row.prop(meshSettings, "tangentSemantic_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "tangentSemantic", "")
+			row = col.row()
+			overrideSetting = meshSettings.edgeLists_override
+			row.prop(meshSettings, "edgeLists_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "edgeLists", toggle = True)
 
-		row = col.row(True)
-		overrideSetting = meshSettings.tangentSize_override
-		row.prop(meshSettings, "tangentSize_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "tangentSize", "")
+			row = col.row()
+			overrideSetting = meshSettings.tangent_override
+			row.prop(meshSettings, "tangent_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "tangent", toggle = True)
 
-		row = col.row(True)
-		overrideSetting = meshSettings.splitMirrored_override
-		row.prop(meshSettings, "splitMirrored_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "splitMirrored", toggle = True)
+			row = col.row()
+			overrideSetting = meshSettings.tangentSemantic_override
+			row.prop(meshSettings, "tangentSemantic_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "tangentSemantic", "")
 
-		row = col.row(True)
-		overrideSetting = meshSettings.splitRotated_override
-		row.prop(meshSettings, "splitRotated_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "splitRotated", toggle = True)
+			row = col.row()
+			overrideSetting = meshSettings.tangentSize_override
+			row.prop(meshSettings, "tangentSize_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "tangentSize", "")
 
-		row = col.row(True)
-		overrideSetting = meshSettings.reorganiseVertBuff_override
-		row.prop(meshSettings, "reorganiseVertBuff_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "reorganiseVertBuff", toggle = True)
+			row = col.row()
+			overrideSetting = meshSettings.splitMirrored_override
+			row.prop(meshSettings, "splitMirrored_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "splitMirrored", toggle = True)
 
-		row = col.row(True)
-		overrideSetting = meshSettings.optimiseAnimation_override
-		row.prop(meshSettings, "optimiseAnimation_override", "",
-			icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
-		if (not overrideSetting):
-			row = row.row(True)
-			row.enabled = False
-		row.prop(meshSettings, "optimiseAnimation", toggle = True)
+			row = col.row()
+			overrideSetting = meshSettings.splitRotated_override
+			row.prop(meshSettings, "splitRotated_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "splitRotated", toggle = True)
+
+			row = col.row()
+			overrideSetting = meshSettings.reorganiseVertBuff_override
+			row.prop(meshSettings, "reorganiseVertBuff_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "reorganiseVertBuff", toggle = True)
+
+			row = col.row()
+			overrideSetting = meshSettings.optimiseAnimation_override
+			row.prop(meshSettings, "optimiseAnimation_override", "",
+				icon = 'PINNED' if overrideSetting else 'UNPINNED', toggle = True)
+			if (not overrideSetting):
+				row = row.row()
+				row.enabled = False
+			row.prop(meshSettings, "optimiseAnimation", toggle = True)
 
 class OperatorSelectSubmeshVertices(bpy.types.Operator):
 	bl_idname = "ogre3d.select_submesh_vertices"
 		bpy.ops.mesh.select_all(action = 'DESELECT')
 		bpy.ops.object.material_slot_select()
 		return {'FINISHED'}
+
+class OperatorSkeletonAddAnimation(bpy.types.Operator):
+	bl_idname = "ogre3d.skeleton_add_animation"
+	bl_label = "Add"
+	bl_description = "Add new skeleton animation."
+
+	def invoke(self, context, event):
+		skeletonSettings = getSkeletonSettings(context)
+		item = skeletonSettings.exportSkeletonActions.add()
+		item.onActionChanged(context)
+		return {'FINISHED'}
+
+class OperatorSkeletonDeleteAnimation(bpy.types.Operator):
+	bl_idname = "ogre3d.skeleton_delete_animation"
+	bl_label = "Delete"
+	bl_description = "Delete skeleton animation."
+
+	index = bpy.props.IntProperty()
+
+	def invoke(self, context, event):
+		skeletonSettings = getSkeletonSettings(context)
+		skeletonSettings.exportSkeletonActions.remove(self.index)
+		return {'FINISHED'}

File Tools/Blender2.6Export/ogre_mesh_exporter/mesh_properties.py

 	useSharedVertices = BoolProperty(
 		name = "Use Shared Vertices",
 		description = "Use shared vertices with other submeshes.",
-		default = True
+		default = True,
+		options = set()
 	)
 
 	# Custom name of submesh.
 	name = StringProperty(
 		name = "Custom name",
 		description = "Custom name of submesh.",
-		default = ""
+		default = "",
+		options = set()
 	)
 
 # ##############################################
 	exportEnabled = BoolProperty(
 		name = "Export",
 		description = "Export this mesh.",
-		default = True
+		default = True,
+		options = set()
+	)
+
+	exportTab = EnumProperty(
+		items = (
+			("mesh", "Mesh", "Mesh tab"),
+			("animation", "Animation", "Animation tab"),
+			("settings", "Override Settings", "Override global settings tab")),
+		default = "mesh",
+		options = {'SKIP_SAVE'}
 	)
 
 	subMeshProperties = CollectionProperty(type = SubMeshProperties)
 
+	animationTab = EnumProperty(
+		items = (
+			("skel", "Skeleton", "Skeleton animation tab"),
+			("pose", "Pose", "Vertex Pose animation tab"),
+			("morph", "Morph", "Vertex Morph animation tab")),
+		default = "skel",
+		options = {'SKIP_SAVE'}
+	)
+
+
 	# ##############################################
 	# Export override specific Properties
-
 	requireMaterials_override = BoolProperty(
 		name = "Require Materials Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	requireMaterials = BoolProperty(
 		name = "Require Materials",
 		description = "Generate Error message when part of this mesh is not assigned with a material.",
-		default = True
+		default = True,
+		options = set()
 	)
 
 	applyModifiers_override = BoolProperty(
 		name = "Apply Modifiers Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	applyModifiers = BoolProperty(
 		name = "Apply Modifiers",
 		description = "Apply mesh modifiers before export. (Slow and may break vertex order for morph targets!)",
-		default = False
+		default = False,
+		options = set()
 	)
 
 	skeletonNameFollowMesh_override = BoolProperty(
 		name = "Skeleton Name Follow Mesh Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	skeletonNameFollowMesh = BoolProperty(
 		name = "Skeleton Name Follow Mesh",
 		description = "Use mesh name for exported skeleton name instead of the armature name.",
-		default = True
+		default = True,
+		options = set()
 	)
 
 	# ##############################################
 	extremityPoints_override = BoolProperty(
 		name = "Extremity Points Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	extremityPoints = IntProperty(
 		name = "Extremity Points",
 		description = "Generate no more than num eXtremes for every submesh. (For submesh render sorting when using alpha materials on submesh)",
 		soft_min = 0,
-		soft_max = 65536
+		soft_max = 65536,
+		options = set()
 	)
 
 	edgeLists_override = BoolProperty(
 		name = "Edge Lists Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	edgeLists = BoolProperty(
 		name = "Edge Lists",
 		description = "Generate edge lists. (Useful for outlining or doing stencil shadows)",
-		default = False
+		default = False,
+		options = set()
 	)
 
 	tangent_override = BoolProperty(
 		name = "Tangent Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	tangent = BoolProperty(
 		name = "Tangent",
 		description = "Generate tangent.",
-		default = False
+		default = False,
+		options = set()
 	)
 
 	tangentSemantic_override = BoolProperty(
 		name = "Tangent Semantic Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	tangentSemantic = EnumProperty(
 		name = "Tangent Semantic",
 		items=(("uvw", "uvw", "Use UV semantic."),
 				("tangent", "tangent", "Use tangent semantic."),
 				),
-		default= "tangent"
+		default= "tangent",
+		options = set()
 	)
 
 	tangentSize_override = BoolProperty(
 		name = "Tangent Size Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	tangentSize = EnumProperty(
 		name = "Tangent Size",
 		items=(("4", "4 component (parity)", "Use 4 component tangent where 4th component is parity."),
 				("3", "3 component", "Use 3 component tangent."),
 				),
-		default= "3"
+		default= "3",
+		options = set()
 	)
 
 	splitMirrored_override = BoolProperty(
 		name = "Split Mirrored Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	splitMirrored = BoolProperty(
 		name = "Split Mirrored",
 		description = "Split tangent vertices at UV mirror points.",
-		default = False
+		default = False,
+		options = set()
 	)
 
 	splitRotated_override = BoolProperty(
 		name = "Split Rotated Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	splitRotated = BoolProperty(
 		name = "Split Rotated",
 		description = "Split tangent vertices where basis is rotated > 90 degrees.",
-		default = False
+		default = False,
+		options = set()
 	)
 
 	reorganiseVertBuff_override = BoolProperty(
 		name = "Reorganise Vertex Buffers Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	reorganiseVertBuff = BoolProperty(
 		name = "Reorganise Vertex Buffers",
 		description = "Reorganise vertex buffer to make it GPU vertex cache friendly.",
-		default = True
+		default = True,
+		options = set()
 	)
 
 	optimiseAnimation_override = BoolProperty(
 		name = "Optimise Animation Override",
 		description = "Override global setting.",
-		default = False
+		default = False,
+		options = set()
 	)
 	optimiseAnimation = BoolProperty(
 		name = "Optimise Animation",
 		description = "Optimise out redundant tracks & keyframes.",
-		default = True
+		default = True,
+		options = set()
 	)

File Tools/Blender2.6Export/ogre_mesh_exporter/skeleton_impl.py

+# ##### BEGIN MIT LICENSE BLOCK #####
+# Copyright (C) 2012 by Lih-Hern Pang
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+# ##### END MIT LICENSE BLOCK #####
+
+import bpy, mathutils, pprint
+
+from ogre_mesh_exporter.log_manager import LogManager, Message
+
+# ##############################################
+# Bone export filter helpers
+
+class BoneNoFilter():
+	def shouldSkipBone(self, bPoseBone):
+		return False
+
+class BoneLayerFilter():
+	def __init__(self, exportLayerMask):
+		self.exportLayerMask = exportLayerMask
+
+	def shouldSkipBone(self, bPoseBone):
+		for boneLayerFlag, exportLayerFlag in zip(bPoseBone.bone.layers, self.exportLayerMask):
+			if (boneLayerFlag and exportLayerFlag): return False
+		return True
+
+class BoneGroupFilter():
+	def __init__(self, exportGroupMask):
+		self.exportGroupMask = exportGroupMask
+
+	def shouldSkipBone(self, bPoseBone):
+		return not (bPoseBone.bone_group and bPoseBone.bone_group.name in self.exportGroupMask)
+
+class Bone():
+	def __init__(self, name, pos, rot, parentIndex = -1, invMeshSpaceMatrix = None):
+		self.mName = name
+		self.mPosition = pos
+		self.mRotation = rot
+		self.mParentIndex = parentIndex
+		self.mInvMeshSpaceMatrix = invMeshSpaceMatrix
+
+# ##############################################
+# Skeleton export classes
+
+class Skeleton():
+	def __init__(self, name, armatureObject = None, targetMeshInverseMatrix = mathutils.Matrix.Identity(4), fixUpAxisToY = True):
+		# List of bones.
+		self.mBones = list()
+		self.mName = name
+
+		# Skip blender armature conversion if not set.
+		if (armatureObject is None): return
+
+		# Make sure we set the armature as active object.
+		# Otherwise the context will be wrong and RNA data will not get proper result for export.
+		lastActiveObject = bpy.context.scene.objects.active
+		bpy.context.scene.objects.active = armatureObject
+
+		# build matrix transform to convert from armature space to mesh space.
+		# NOTE: Blender matrix multiplication order is World = Parent * Child
+		if (fixUpAxisToY): targetMeshInverseMatrix = mathutils.Matrix(((1,0,0,0),(0,0,1,0),(0,-1,0,0),(0,0,0,1))) * targetMeshInverseMatrix
+		self.mArmatureToMeshMatrix = targetMeshInverseMatrix * armatureObject.matrix_world
+
+		# iterate through each bone and initialize our own version.
+		# all bones are converted into mesh relative space.
+		# The reason for this is to observe situations where Armature is not
+		# parent of mesh or mesh position is not at origin relative to Armature.
+		# This method ensures that skeletal animation will always work regardless
+		# of good/bad mesh<->skeletal setup.
+		pose = armatureObject.pose
+
+		# get skeleton export settings.
+		skeletonSettings = armatureObject.data.ogre_mesh_exporter
+		if (skeletonSettings.exportFilter == 'layers'): exportBoneFilter = BoneLayerFilter(skeletonSettings.exportBoneLayerMask)
+		elif (skeletonSettings.exportFilter == 'groups'): exportBoneFilter = BoneGroupFilter(skeletonSettings.exportBoneGroupMask)
+		else: exportBoneFilter = BoneNoFilter()
+
+		# first we convert all bones to mesh space.
+		boneMatrixMeshSpaceList = list()
+		boneMatrixInvMeshSpaceList = list()
+		bBoneList = list()
+		for bPoseBone in pose.bones:
+			# check that the bone is what we want to export.
+			if (exportBoneFilter.shouldSkipBone(bPoseBone)): continue
+
+			bBone = bPoseBone.bone
+			matrix = self.mArmatureToMeshMatrix * bBone.matrix_local
+			boneMatrixMeshSpaceList.append(matrix)
+			boneMatrixInvMeshSpaceList.append(matrix.inverted())
+			bBoneList.append(bBone)
+
+		# then we convert bone to new parent space(base on mesh space) and do actual bone creation
+		for index, bBone in enumerate(bBoneList):
+			# attempt to find the next parent bone that actually gets exported.
+			parentBone = bBone.parent
+			while (True):
+				try:
+					parentIndex = bBoneList.index(parentBone)
+					matrix = boneMatrixInvMeshSpaceList[parentIndex] * boneMatrixMeshSpaceList[index]
+					break
+				except ValueError:
+					if (parentBone is None):
+						parentIndex = -1
+						matrix = boneMatrixMeshSpaceList[index]
+						break
+					parentBone = parentBone.parent
+
+			bone = Bone(bBone.name, matrix.translation, matrix.to_quaternion().normalized(),
+				parentIndex, boneMatrixInvMeshSpaceList[index])
+			self.mBones.append(bone)
+
+		# done importing essential armature data. reset active object.
+		bpy.context.scene.objects.active = lastActiveObject
+
+	def getBoneIndex(self, name):
+		index = 0
+		count = len(self.mBones)
+		while (index < count):
+			if (self.mBones[index].mName == name):
+				return index
+			index += 1
+		return -1
+
+	def serialize(self, file):
+		file.write('<skeleton>\n')
+
+		# write bones.
+		file.write('\t<bones>\n')
+		for index, bone in enumerate(self.mBones):
+			position = tuple(bone.mPosition)
+			angle = bone.mRotation.angle
+			axis = tuple(bone.mRotation.axis)
+
+			file.write('\t\t<bone id="%d" name="%s">\n' % (index, bone.mName))
+			file.write('\t\t\t<position x="%.6f" y="%.6f" z="%.6f" />\n' % position)
+			file.write('\t\t\t<rotation angle="%.6f">\n' % angle)
+			file.write('\t\t\t\t<axis x="%.6f" y="%.6f" z="%.6f" />\n' % axis)
+			file.write('\t\t\t</rotation>\n')
+			file.write('\t\t</bone>\n')
+		file.write('\t</bones>\n')
+
+		# write bone hierarchies.
+		file.write('\t<bonehierarchy>\n')
+		for bone in self.mBones:
+			if (bone.mParentIndex == -1): continue
+			file.write('\t\t<boneparent bone="%s" parent="%s" />\n' %
+				(bone.mName, self.mBones[bone.mParentIndex].mName))
+		file.write('\t</bonehierarchy>\n')
+
+		file.write('</skeleton>\n')
+

File Tools/Blender2.6Export/ogre_mesh_exporter/skeleton_panel.py

+# ##### BEGIN MIT LICENSE BLOCK #####
+# Copyright (C) 2012 by Lih-Hern Pang
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+# ##### END MIT LICENSE BLOCK #####
+
+import bpy
+
+class SkeletonExporterPanel(bpy.types.Panel):
+	bl_idname = "ogre3d_skeleton_panel"
+	bl_label = "Ogre Mesh Exporter"
+	bl_space_type = "PROPERTIES"
+	bl_region_type = "WINDOW"
+	bl_context = "data"
+
+	@classmethod
+	def poll(cls, context):
+		return context.armature
+
+	def draw(self, context):
+		layout = self.layout
+		globalSettings = bpy.context.scene.ogre_mesh_exporter
+		skeletonSettings = context.armature.ogre_mesh_exporter
+
+		# bone filter type.
+		layout.label("Export bone filter:", icon = 'FILTER')
+		row = layout.row(True)
+		row.prop_enum(skeletonSettings, "exportFilter", 'all', icon = 'ARMATURE_DATA')
+		row.prop_enum(skeletonSettings, "exportFilter", 'layers', icon = 'MESH_GRID')
+		row.prop_enum(skeletonSettings, "exportFilter", 'groups', icon = 'GROUP_BONE')
+
+		# bone filter by layer UI.
+		box = layout.box()
+		table = box.column(True)
+		if (skeletonSettings.exportFilter == 'layers'):
+			table.label("Export Bones In Selected Layers:")
+			table.prop(skeletonSettings, "exportBoneLayerMask", text = "")
+		elif (skeletonSettings.exportFilter == 'groups'):
+			table.label("Export Bones In Selected Bone Groups:")
+			row = table.row()
+			checkCol = row.column()
+			checkCol.alignment = 'RIGHT'
+			checkCol.scale_x = 0.185
+			col = row.column()
+
+			boneGroups = context.object.pose.bone_groups
+			for group in boneGroups:
+				exportingGroup = (group.name in skeletonSettings.exportBoneGroupMask)
+				checkCol.prop_enum(skeletonSettings, "exportBoneGroupMask", group.name, text = "",
+					icon = 'CHECKBOX_HLT' if (exportingGroup) else 'CHECKBOX_DEHLT')
+				#~ col.prop_enum(skeletonSettings, "exportBoneGroupMask", group.name, icon = 'GROUP_BONE')
+				col.prop(globalSettings, "dummyTrue", toggle = True, text = group.name, icon = 'GROUP_BONE')
+		else: table.label("Export All Bones", icon = 'INFO')

File Tools/Blender2.6Export/ogre_mesh_exporter/skeleton_properties.py

+# ##### BEGIN MIT LICENSE BLOCK #####
+# Copyright (C) 2012 by Lih-Hern Pang
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+# ##### END MIT LICENSE BLOCK #####
+
+import bpy
+from bpy.types import PoseBone
+from bpy.props import *
+
+def boneGroupList(self, context):
+	boneGroups = context.object.pose.bone_groups
+	return {(group.name, group.name, '') for group in boneGroups}
+
+# helper function to get all bone names related to given action.
+# Copied from export_fbx.py
+def getActionBoneNames(obj, action):
+	names = set()
+	path_resolve = obj.path_resolve
+
+	for fcu in action.fcurves:
+		try:
+			prop = path_resolve(fcu.data_path, False)
+		except:
+			prop = None
+
+		if prop is not None:
+			data = prop.data
+			if isinstance(data, PoseBone):
+				names.add(data.name)
+	return names
+
+# Helper class to get linked skeleton object from mesh object if context is on mesh object
+def getSkeletonObject(context):
+	object = context.object
+	if (object.type == 'ARMATURE'): return object
+
+	# get linked armature
+	parentObject = object.parent
+	if (parentObject and object.parent_type == 'ARMATURE'):
+		armatureObject = parentObject
+	else:
+		# check modifier stack, use first valid armature modifier.
+		for modifier in object.modifiers:
+			if (modifier.type == 'ARMATURE' and
+				(modifier.use_vertex_groups or
+				modifier.use_bone_envelopes)):
+				armatureObject = modifier.object
+	return armatureObject
+
+# Callback to list available actions for armature.
+def actionByArmatureList(self, context):
+	actions = bpy.data.actions
+	object = getSkeletonObject(context)
+	armature = object.data
+	actionNames = set()
+
+	armatureBoneNames = set([bBone.name for bBone in armature.bones])
+	for action in actions:
+		actionBoneNames = getActionBoneNames(object, action)
+		if (armatureBoneNames.intersection(actionBoneNames)):  # at least one channel matches.
+			actionNames.add((action.name, action.name, ''))
+	return actionNames
+
+# Single skeleton animation action sequence.
+class SkeletonActionSequence(bpy.types.PropertyGroup):
+	def onActionChanged(self, context):
+		actionIndex = bpy.data.actions.find(self.action)
+		if (actionIndex == -1): return
+		action = bpy.data.actions[actionIndex]
+		self.name = action.name
+		self.startFrame = action.frame_range[0]
+		self.endFrame = action.frame_range[1]
+
+	action = EnumProperty(
+		name = "Action",
+		description = "Action to export from",
+		items = actionByArmatureList,
+		update = onActionChanged,
+		options = set())
+
+	name = StringProperty(name = "Name", default = "Unknown", options = set())
+
+	startFrame = IntProperty(
+		name = "Start",
+		soft_min = 0,
+		soft_max = 300000,