Просмотр исходного кода

Added modified Ogre Maxscript Exporter contributed by Vladimir Pobedinskij, which outputs materials in Urho3D .xml format.

Lasse Öörni 13 лет назад
Родитель
Сommit
c513645a1a

+ 1 - 0
Docs/Urho3D.dox

@@ -52,6 +52,7 @@ Urho3D development, contributions and bugfixes by:
 - Carlo Carollo
 - Alex Fuller
 - Jason Kinzer
+- Vladimir Pobedinskij
 - Miika Santala
 - Wei Tjong Yao
 - Magic.Lixin

+ 11 - 0
Extras/OgreMaxscriptExport/Startup/initOgreTools.ms

@@ -0,0 +1,11 @@
+
+-- initOgreTools.ms
+--
+-- Copyright © 2002 John Martin
+--
+-- $Id: initOgreTools.ms,v 1.1 2006-09-19 21:58:40 sinbad Exp $
+--
+-- Initializes the ogre 3dsmax tools. Just place in scripts/startup
+-- folder and ogre tools will be loaded with max.
+
+fileIn "ogre/macros/ogreToolbar.mcr"

+ 595 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreBipedLib.ms

@@ -0,0 +1,595 @@
+include "ogre/lib/ogreSkeletonLib_usefulfns.ms"
+
+-----------------------------------------------------------------------------
+-- gets  keyframes from the controllers animation
+-----------------------------------------------------------------------------
+function getTimeList obj firstframe lastframe samplerate IKsamplerate =
+(
+	local list,rotContr,posContr,e  ;
+	list = #(firstframe) ;
+	list2 = #() ; -- this is the list which will be returned.
+	
+	-- Biped Bones and the root: Bip01 for example don't have the same controller
+	
+	-- Root : Bip01
+	if (isPelvis obj) then (
+		-- vertical controller
+		for e in obj.controller.vertical.controller.keys do (
+			t =e.time ;
+			if (t>firstFrame and t<=lastFrame) then (
+				append list t ;
+				--print t ;	
+			)
+		)
+		-- horizontal controller
+		for e in obj.controller.horizontal.controller.keys do (
+			t =e.time ;
+			if (t>firstFrame and t<=lastFrame) then
+				append list t ;
+		)
+		-- turn controller
+		for e in obj.controller.turning.controller.keys do (
+			t =e.time ;
+			if (t>firstFrame and t<=lastFrame) then
+				append list t ;
+		)
+		sort list ;
+	)
+	-- Biped Bones
+	else if (isKindOf obj Biped_Object) then (
+		
+		for e in obj.controller.keys do (
+			t =e.time ;
+			if (t>firstFrame and t<=lastFrame) then
+				append list t ;
+		)
+	)
+	-- Standard Bones
+	else 
+	(
+		--print obj.name;
+		--print (classof obj.controller) ;
+		if (classof obj.controller == prs) then		-- standard controller
+		(
+			rotContr = obj.rotation.controller ;
+			posContr = obj.pos.controller ;
+		
+			for e in rotContr.keys do 
+			(
+				t = e.time ;
+				if (t>firstFrame and t<=lastFrame) then
+					append list t ;
+			)
+			for e in posContr.keys do 
+			(
+				t = e.time ;
+				if (t>firstFrame and t<=lastFrame) then
+					append list t ;
+			)	
+		)
+		else if ((classof obj.controller == IK_ControllerMatrix3Controller) or (classof obj.controller == IKControl)) then		-- IK controller
+		(
+			local IKSR = IKsamplerate;
+			
+			if (IKSR == 0.0) then
+				IKSR = 1.0;
+			
+			i=firstFrame as Float;
+			while (i<=lastFrame) do
+			(
+				append list (i as Float);			
+				i = i + IKSR;
+			)
+		)
+	)
+
+	append list (firstFrame as Float);		-- add a keyframe on the first frame
+	append list (lastFrame as Float);		-- add a keyframe on then last frame
+
+	if (samplerate > 0) then				-- sample the animation by adding keyframes on regular intervals
+	(
+		i=firstFrame as Float;
+		while (i<=lastFrame) do
+		(
+			append list (i as Float);			
+			i = i + samplerate;
+		)
+	)
+	sort list ;
+	
+	-- if several keyframes have the same value, we keep just one
+	keepLoneValues list list2 ;
+	list2 ;
+)
+
+
+-----------------------------------------------------------------------------
+-- write <track />
+-- Selected keys belongs to [firstframe,lastFrame]
+-- time = (key.time - firstFrame)*length/(lastFrame-firstFrame)
+--   (e.g. first key has time 0.)
+-----------------------------------------------------------------------------
+function writeTrack bone_name boneId firstframe lastframe samplerate IKsamplerate length scale flipYZ outFile=
+(
+	local angle,timef,i,bname,d,mref,mparent ;
+	
+	-- displays information in the maxscript listener
+	if (not g_MAX) then
+		format "retrieving key information for % ...\n" (bone_name) ;
+
+	-- gets bone acording to the parameter boneId
+	bname = bone_name ;
+	replaceSpaces bname ;
+	d = getNodeByName bname ;
+	
+	-- gets keyframe list
+	timelist = getTimeList d firstframe lastframe samplerate IKsamplerate;
+	
+	-- track header
+	format("\t\t\t\t<track bone = \"%\">\n") bname to:outFile ;
+	format("\t\t\t\t\t<keyframes>\n") to:outFile ;
+	
+	-- gets initial transform at frame 0f
+	at time 0f (
+		initTform = d.transform ;
+		if (not isRootUniversal2 d) then (
+			mparent = d.parent.transform ;
+			initTform = initTform*inverse(mparent) ;
+		)
+		else if (flipYZ) then (
+			if (not g_MAX) then
+				format " - flipping root track..." ;
+			-- we add the bip Transform
+			--initTform = initTform * d.controller.rootNode.transform ;
+			initTform = flipYZTransform initTform ;
+		)
+	)
+		
+	-- for each frame in the list
+	for i in timelist do
+	(
+		-- moves slider time and compute OGRE time
+		at time i (
+			timef = ((float) (i-firstFrame)*length)/(lastframe - firstframe ) ;
+			
+		-- First, rotation which depends on initial transformation
+			Tform = d.transform ;
+			-- if this is the pelvis
+			if (isRootUniversal2 d) then (
+				mparent = matrix3 1 ;
+				-- if flipYZ == true
+				if (flipYZ) then
+					Tform = flipYZTransform Tform ;
+			)			
+			else
+				mparent = d.parent.transform ; 
+			
+			-- computes rotation
+			mref = initTform*mparent ;	
+			Tform = Tform*inverse(mref) ;
+			
+			-- rotation part is saved.
+			--rot = Tform.rotation as angleaxis ;
+			--angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :((((((
+			rot = toAngleAxis Tform.rotation ;
+			axis = rot.axis;
+			angle = - rot.angle;
+			
+			-- Then, position which depends on parent			
+			Tform=d.transform ;
+			Tform=Tform*inverse(mparent) ;
+			
+			-- if this is the root bone and flipYZ == true
+			if (isRootUniversal2 d and flipYZ) then (
+				Tform = flipYZTransform Tform ;
+			)
+			
+			-- substracts position of the initial transform
+			Tform.pos -= initTform.pos ;
+			Tform.pos = Tform.pos * scale ;
+			
+			pos = Tform.pos ;
+
+			-- writes them !
+			if (abs(pos.x)<1e-5) then pos.x = 0 ;
+			if (abs(pos.y)<1e-5) then pos.y = 0 ;
+			if (abs(pos.z)<1e-5) then pos.z = 0 ;
+			
+			format("\t\t\t\t\t\t<keyframe time=\"%\">\n") timef to: outFile ;
+			format("\t\t\t\t\t\t\t<translate x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to: outFile ;
+			format("\t\t\t\t\t\t\t<rotate angle=\"%\">\n") angle to:outFile ; 
+			format("\t\t\t\t\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") (axis.x) (axis.y) (axis.z) to:outFile ;
+			format("\t\t\t\t\t\t\t</rotate>\n") to:outFile ;
+			format("\t\t\t\t\t\t</keyframe>\n") to:outFile ;
+		)
+	)
+	
+	-- track end
+	format("\t\t\t\t\t</keyframes>\n") to:outFile ;
+	format("\t\t\t\t</track>\n") to: outFile ;
+)
+
+-------------------------------------------------------------------------------------------------
+------------------------------------------- WRITE SKELETON --------------------------------------
+-------------------------------------------------------------------------------------------------
+
+-------------------------------------
+-- List of bones in the hierarchy
+-------------------------------------
+global BonesList=#()
+global RootsList=#()
+
+-------------------------------------
+-- helper functions to build skeleton
+-------------------------------------
+
+-----------------------------------------------------------------
+-- recursive function to build the list of bones for the skeleton
+-----------------------------------------------------------------
+function computeBList b sk phy exportHelpers =
+(
+	bname = b ;
+	bone = getNodeByName bname ;
+	if (findItem BonesList bname == 0) then
+		if (isKindOf bone BoneGeometry or iskindOf bone Biped_Object or (exportHelpers and (isPartOfModifier bone sk phy)) ) then
+			append BonesList bname ;
+	childrenArray = bone.children ;
+	for i=1 to childrenArray.count do
+	(
+        if (isKindOf bone BoneGeometry or iskindOf bone Biped_Object or (exportHelpers and (isPartOfModifier bone sk phy))) then
+			computeBList (replaceSpaces childrenArray[i].name) sk phy exportHelpers;
+	)
+)
+
+function addHelpersToHierarchy phy sk =
+(
+	if (sk != undefined) then
+	(
+		for i=1 to (skinOps.GetNumberBones sk) do
+		(
+			bname = skinOps.GetBoneName sk i 1 ;
+			replaceSpaces bname ;
+			d = getNodeByName bname ;
+
+			if (iskindof d helper) then
+				append BonesList bname ;
+		)	
+	)
+	else if (phy != undefined) then
+	(
+		for i=1 to (physiqueOps.GetBoneCount $) do
+		(
+			bname = (physiqueOps.GetBones $)[i].name;
+			replaceSpaces bname ;
+			d = getNodeByName bname ;
+		
+			if (iskindof d helper) then
+				append BonesList bname ;
+		)	
+	)
+)
+
+-----------------------------------------------------------------
+-- find the root(s) of the rhierarchy
+-----------------------------------------------------------------
+function getHierarchyRoots phy sk exportHelpers =
+(
+	local rootstab=#();
+	
+	if (sk != undefined) then
+	(
+		for i=1 to (skinOps.GetNumberBones sk) do
+		(
+			bname= skinOps.GetBoneName sk i 1 ;
+			replaceSpaces bname ;
+			d = getNodeByName bname ;
+		
+			while (d.parent!=undefined and (iskindof d.parent BoneGeometry or iskindOf d.parent Biped_Object or (exportHelpers and (isPartOfModifier d.parent sk phy)) )) do
+			(
+				d = d.parent
+			)
+			trouve = 0;
+			--format("new potential root bone \"%\"\n") (replaceSpaces d.name) ;
+			for j=1 to rootstab.count do
+			(
+				if (rootstab[j]!=undefined and (rootstab[j]==(replaceSpaces d.name)))then
+				(
+					trouve = 1;
+					exit;
+				)
+			)
+			if trouve==0 then
+			(
+				if (iskindof d BoneGeometry or iskindOf d Biped_Object or (exportHelpers and (isPartOfModifier d sk phy)) ) then
+				(
+					if (not g_MAX) then
+						format("new root bone \"%\"\n") (replaceSpaces d.name) ;
+					rootstab[rootstab.count+1] = (replaceSpaces d.name) ;
+				)
+			)
+		)
+	)
+	else if (phy != undefined) then		-- physique modifier
+	(
+		for i=1 to (physiqueOps.GetBoneCount $) do
+		(
+			bname = (physiqueOps.GetBones $)[i].name;
+			replaceSpaces bname ;
+			d = getNodeByName bname ;
+		
+			while (d.parent!=undefined and (iskindof d.parent BoneGeometry or iskindOf d.parent Biped_Object  or (exportHelpers and (isPartOfModifier d.parent sk phy)) )) do
+			(
+				d = d.parent
+			)
+			trouve = 0;
+			--format("new potential root bone \"%\"\n") (replaceSpaces d.name) ;
+			for j=1 to rootstab.count do
+			(
+				if (rootstab[j]!=undefined and (rootstab[j]==(replaceSpaces d.name)))then
+				(
+					trouve = 1;
+					exit;
+				)
+			)
+			if trouve==0 then
+			(
+				if (iskindof d BoneGeometry or iskindOf d Biped_Object or (exportHelpers and (isPartOfModifier d sk phy))) then
+				(
+					if (not g_MAX) then
+						format("new root bone \"%\"\n") (replaceSpaces d.name) ;
+					rootstab[rootstab.count+1] = (replaceSpaces d.name) ;
+				)
+			)
+		)
+	)
+	
+	rootstab;
+)
+
+-----------------------------------------------------------------
+-- function to build the list of bones for the skeleton
+-----------------------------------------------------------------
+function computeBonesList phy sk exportHelpers =
+(
+	RootsList = getHierarchyRoots phy sk exportHelpers;	-- find the roots of the current hierarchy
+	print RootsList;
+	for b in RootsList do
+	(
+		computeBList b sk phy exportHelpers;
+	)
+	
+	-- add the nodes that are parts of the skin (or physique) modifier but are neither biped_object nor standard bones
+	-- only helpers (Point, Dummy) at the moment...
+	-- addHelpersToHierarchy phy sk ;
+)
+
+------------------
+-- write <bones />
+------------------
+function writeB bone_name id scale flipYZ outFile =
+(
+	-- gets bone acording to the parameter boneId
+	bname = bone_name ;
+	replaceSpaces bname;
+	d = getNodeByName bname ;
+	
+	-- gets initial transform at frame 0f
+	format("\t\t<bone id=\"%\" name=\"%\">\n") (id-1) bname to:outFile ;
+
+	slidertime = 0f ;
+	Tform = d.transform ;
+	if (not isRootUniversal2 d) then (
+		mparent = d.parent.transform ;
+		Tform = Tform*inverse(mparent) ;
+	)
+	
+	Tform.pos = Tform.pos * scale ;
+		
+	if ((isRootUniversal2 d) and flipYZ) then (
+		if (not g_MAX) then
+			format "- Flipping root... \n" ;
+		Tform = flipYZTransform Tform ;
+	)
+	
+	pos = Tform.pos ;
+	--rot = Tform.rotation as angleaxis ;
+	--angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :((((((
+	rot = toAngleAxis Tform.rotation ;
+	angle = - rot.angle ;
+		
+	--		if (abs(pos.x)<1e-5) then pos.x = 0 ;
+	--		if (abs(pos.y)<1e-5) then pos.y = 0 ;
+	--		if (abs(pos.z)<1e-5) then pos.z = 0 ;
+	
+	-- Only object.transform was taken into account, but when mirror is applied
+    -- object.scale is modified and become [-1,-1,-1] that's why we do what follows:
+	if ((d.parent != undefined) and (hasproperty d.parent "scale")) then (
+        pos = pos * d.parent.scale ;
+    ) 
+				
+	format("\t\t\t<position x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to:outFile ;
+    format("\t\t\t<rotation angle=\"%\">\n") angle to:outFile ;
+    format("\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") rot.axis.x rot.axis.y rot.axis.z to:outFile ;
+   	format("\t\t\t</rotation>\n") to:outFile ;
+	format("\t\t</bone>\n") to:outFile ;
+)
+
+-----------------------------
+-- write Bones (using writeB)
+-----------------------------
+function writeBones phy sk scale flipYZ exportHelpers outFile =
+(
+	local i ;
+	
+	OgreExportObject.exportProgress.value = 0;
+
+	if (BonesList.count == 0) then
+		computeBonesList phy sk exportHelpers;
+	
+	format("\t<bones>\n") to:outFile;
+	
+	i = 0 ;
+	
+	for i=1 to BonesList.count do
+	(
+		OgreExportObject.exportProgress.value = (100.0*i/BonesList.count);
+
+		writeB BonesList[i] i scale flipYZ outFile ;
+	)	
+	
+	format("\t</bones>\n") to:outFile;
+	
+	OgreExportObject.exportProgress.value = 100;
+)
+
+--------------------------
+-- write <bonehierarchy />
+--------------------------
+function writeH b outFile =
+(
+	if (not isRootUniversal2 b) then
+	(
+		p = b.parent ;
+		format("\t\t<boneparent bone=\"%\" parent=\"%\" />\n") (replaceSpaces b.name) (replaceSpaces p.name) to:outFile ;
+	)
+)
+
+function writeHierarchy outFile =
+(
+	OgreExportObject.exportProgress.value = 0;
+
+	local bname,pelvis
+	format("\t<bonehierarchy>\n") to:outFile ;
+
+	for i=1 to BonesList.count do
+	(
+		OgreExportObject.exportProgress.value = (100.0*i/BonesList.count);
+
+		b = getNodeByName BonesList[i] ;
+		writeH b outFile ;	
+	)
+
+	format("\t</bonehierarchy>\n") to:outFile ;
+	
+	OgreExportObject.exportProgress.value = 100;
+)
+
+-----------------------
+-- write <animations />
+-----------------------
+function writeAnim Anims samplerate IKsamplerate scale flipYZ outFile =
+(
+	local i,n ;
+	
+	OgreExportObject.exportProgress.value = 0;
+	
+	format("\t<animations>\n") to: outFile ;
+	
+	for anm=1 to Anims.names.count do
+	(
+	    format("\t\t<animation name=\"%\" length=\"%\">\n") Anims.names[anm] Anims.lengths[anm] to:outFile ;
+		format("\t\t\t<tracks>\n") to:outFile
+		
+		n = BonesList.count ;
+		for i = 1 to n do
+		(
+			OgreExportObject.exportProgress.value = (100.0*i/BonesList.count)/Anims.names.count;
+
+			writeTrack BonesList[i] i Anims.startframes[anm] Anims.endframes[anm] samplerate IKsamplerate Anims.lengths[anm] scale flipYZ outFile ;
+		)
+	
+		format("\t\t\t</tracks>\n") to:outFile					
+		format("\t\t</animation>\n") to: outFile ;
+	)
+	
+	format("\t</animations>\n") to: outFile ;
+
+	OgreExportObject.exportProgress.value = 100;
+)
+
+-------------------------------------------------------------
+-- write <skeleton /> main function
+-- write the animation in the file out_name + ".skeleton.xml"
+-- between the frame firstFrame and lastFrame 
+-- and scale time according to length
+-------------------------------------------------------------
+
+function writeSkeleton pmesh exportOptions Anims out_name = 
+(
+	local sk,n,keys,initialKeys,messages,phy ;
+	
+	sk = getSkin pmesh ;
+	phy = getPhysique pmesh ;
+	
+	if (sk == undefined and phy == undefined) then
+	(
+		MessageBox "There is no skin or physique modifier for this object" ;
+		return false;
+	)
+	else
+	(	
+		-- in order to perform, skin should be opened
+		max modify mode ;
+		if (sk != undefined) then
+			modPanel.setCurrentObject pmesh.modifiers[#Skin] ;
+		else -- physique
+			modPanel.setCurrentObject pmesh.modifiers[#Physique] ;
+
+		if (not g_MAX) then
+		(
+			format "------------------------------------------\n"
+			format "------ OGRE skeleton Exporter Log   ------\n"
+			format "------------------------------------------\n"
+			
+			format "Exporter options :\n"
+			for i=1 to Anims.names.count do
+				format "Anim % - firstFrame: % - lastFrame: %\n" Anims.names[i] Anims.startframes[i] Anims.endframes[i] ;
+			-- creates the output file
+			outFile = createfile (out_name + ".skeleton.xml") ;
+		)
+		else
+		(
+			if (g_MAX_use_listener) then
+				format("<ogrestartfile>%</ogrestartfile><ogrestartdata>\n") (outName + ".skeleton.xml");
+			outFile = listener;
+		)
+	
+		-- writes header
+		format("<skeleton>\n") to:outFile ;	
+	
+		if (not g_MAX) then
+			format "Writing bones :\n" ;
+		writeBones phy sk exportOptions.scale exportOptions.flipYZ exportOptions.exportHelpers outFile ;
+
+		if (not g_MAX) then
+			format "Writing bone hierarchy.\n" ;
+		writeHierarchy outFile ;
+
+		if (not g_MAX) then
+			format "Writing bone tracks.\n" ;
+		writeAnim Anims exportOptions.sampleRate exportOptions.ikSampleRate exportOptions.scale exportOptions.flipYZ outFile ;
+
+		-- ecriture, fin des balises
+		format("</skeleton>\n") to: outFile ;
+				
+		if (not g_MAX) then
+		(
+			format "------------------------------------------\n"
+			format "----------          END          ---------\n"
+			format "------------------------------------------\n"
+		
+			close outFile ;
+		)
+		else
+		(
+			if (g_MAX_use_listener) then
+				format("</ogrestartdata>\n") to: outFile;
+		)
+		
+		messageBox "Exporting skeleton successful !"
+		return true;
+	)
+)
+
+
+

+ 46 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreMaterialPlugin.ms

@@ -0,0 +1,46 @@
+-- this is a level 3 plug-in, the beginnings of a custom glass material. 
+-- It extends Standard material and replaces its UI with a single 
+-- rollout with 2 spinners and a color picker 
+
+plugin material OgreMaterial 
+name:"OGRE Material" 
+classID:#(695425,446581) 
+extends:Standard replaceUI:true version:1 
+( 
+	parameters main rollout:paramsRollout 
+	( 
+		ambient type:#color default:blue ui:ambientColor  
+		diffuse type:#color default:blue ui:diffuseColor
+		specular type:#color default:black ui:specularColor
+		selfIllumColor type:#color default:black ui:selfIllumColor
+		specularLevel type:#Integer ui:specularLevel
+	    diffuseMap type:#texturemap ui:diffuseMap
+		
+		on ambient set val do delegate.ambient_color = val 
+		on diffuse set val do delegate.diffuse_color = val
+		on specular set val do delegate.specular_color = val
+		on selfIllumColor set val do delegate.self_Illum_color = val
+		on specularLevel set val do delegate.specular_level = val
+		on diffuseMap set val do delegate.diffuseMap = val
+	) 
+	
+	rollout paramsRollout "OGRE Material Parameters" 
+	( 
+		colorpicker ambientColor "ambient color: " align:#right 
+		colorpicker diffuseColor "diffuse color: " align:#right
+		colorpicker specularColor "specular color: " align:#right
+		colorpicker selfIllumColor "emissive color: " align:#right
+		spinner specularLevel "specular level in %:" range:[0,1000,0] type:#Integer ;
+		label l1 "Choose the diffuse map" align:#left;
+		mapbutton diffuseMap "" align:#right ;
+	) 
+	
+	on create do 
+	( 
+		-- setup initial material 
+		-- delegate.opacityFalloff = 75 
+		delegate.useSelfIllumColor=true ;
+		-- delegate.diffuseMapEnable = false ;
+	)
+
+) 

+ 4 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreSkeletonLib.ms

@@ -0,0 +1,4 @@
+include "ogre/lib/ogreSkeletonLib_usefulfns.ms"
+include "ogre/lib/ogreSkeletonLib_meshfns.ms"
+include "ogre/lib/ogreBipedLib.ms"
+include "ogre/lib/ogreMaterialLib.ms"

+ 75 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreSkeletonLib_addmissingkeys.ms

@@ -0,0 +1,75 @@
+-- add key to Position tracks.
+function addPosK obj time =
+(
+	local posContr,subPosContr,i ;
+	posContr = obj.pos.controller ;
+	subPosContr = getXYZControllers posContr ;
+	for i = 1 to 3 do
+	(
+		addNewKey subPosContr[i] time ;
+	)
+)
+
+-- add key to Rotation tracks.
+function addRotK obj time =
+(
+	local rotContr,subRotContr,i ;
+	rotContr = obj.rotation.controller ;
+	subRotContr = getXYZControllers rotContr ;
+	for i = 1 to 3 do
+	(
+		addNewKey subRotContr[i] time ;
+	)
+)
+
+
+-------------------------------------------------------------------------------
+-- add keys for example in position track when there are some in rotation track
+-------------------------------------------------------------------------------
+
+function addMissingKeys obj =
+(
+	local rotContr,posContr,nRotKeys,nPosKeys
+	
+	rotContr = obj.rotation.controller ;
+	posContr = obj.pos.controller ;
+	subRotContr = getXYZControllers rotContr ;
+	subPosContr = getXYZControllers posContr ;
+	nRotKeys = #() ;
+	nPosKeys = #() ;
+	
+	if (subRotContr[1] != undefined and subPosContr[1] != undefined) then
+	(
+		nRotKeys[1] = numKeys subRotContr[1] ;
+		nRotKeys[2] = numKeys subRotContr[2] ;
+		nRotKeys[3] = numKeys subRotContr[3] ;
+	
+		nPosKeys[1] = numKeys subPosContr[1] ;
+		nPosKeys[2] = numKeys subPosContr[2] ;
+		nPosKeys[3] = numKeys subPosContr[3] ;
+	
+	
+		for i=1 to 3 do -- i = controller index
+		(
+			for n = 1 to numKeys subRotContr[i] do -- n = number of keys
+			(
+				for j=1 to n do  -- j = key index
+				(
+					t = (getKey subRotContr[1] j).time ;
+					addRotK obj t ;
+					addPosK obj t ;
+				)
+			)
+		
+			for n = 1 to numKeys subPosContr[i] do -- n = number of keys
+			(
+				for j=1 to n do  -- j = key index
+				(
+					t = (getKey subPosContr[1] j).time ;
+					addRotK obj t ;
+					addPosK obj t ;
+				)
+			)	
+		)
+	)
+)

+ 714 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreSkeletonLib_meshfns.ms

@@ -0,0 +1,714 @@
+include "ogre/lib/ogreSkeletonLib_usefulfns.ms"
+include "ogre/lib/ogrebipedlib.ms"
+
+----------------------------------------------------------------------------------------
+-- ----------------------------------- WRITE MESH ----------------------------------- --
+----------------------------------------------------------------------------------------
+
+-----------------------------------------------------------------
+-- Global 'variables'
+-- There is an array with vertices datas for each submesh
+-- There is also an array made up of faces.
+-----------------------------------------------------------------
+
+verticesArrays=#() ;
+facesArrays=#() ;
+boneAssignments=#() ;
+submeshesId=#() ;
+materialName = "" ;
+
+-- verticesArrays[i] will describe the subMesh number i and its elements will be ogreVertices where
+--	ogreVert = #(x,y,z,nx,ny,nz,r,g,b,a,u1,v1,u2,v2....) 
+--   		     ----- -------- ------- ---------------
+--          	 Pos     Nmal    RGBA       UVs sets
+-- RGBA being the  vertex color when relevant 	(Optional)
+-- A vertex can contain multiple UVs sets		(Optional)
+
+-- facesArrays[i] is an array of Point3 describing the subMesh i. Each Point3 contains vertices indices for a face that is gonna be exported
+
+-- boneAssignments[i] describes the bones assignements for subMesh i. It is an array of triplets like #(vertexIndex,boneIndex,weight)
+
+-- submeshesId is the list of material Ids 
+
+-- materialName is the name that Ogre will use
+
+
+----------------------------------------------------------------
+-- exploreMesh returns a string which is "OK" or an error message
+-- if there are warnings, it displays message boxes.
+-----------------------------------------------------------------
+function exploreMesh pmesh exportUV exportColours=
+(
+	local material, answer, m ;
+	
+	answer = "" ;
+	m = snapShotAsMesh pmesh ;
+	
+	-- first, is a material defined	?
+	material = pmesh.material ;
+	if (material == undefined) then
+		answer = "This mesh doesn't have any material, please apply one\n" ;
+	else
+		-- then, are UVW coords set up ?
+		if (exportUV and getNumTVerts m == 0) then
+			answer = "This mesh must have UVW coords in order to be exported" ;
+		
+	if (answer == "") then 
+		answer = "OK" ;
+	
+	delete m ;
+	answer ;
+)
+
+
+----------------------------------------------------------------
+-- returns the correct normal for the vertex
+-- according to smoothing groups
+-----------------------------------------------------------------
+function getVertexNormalAccordingToSmoothingGroups selectedMesh faceIndex vertexIndex =
+(
+	local normal;
+       	local faceSmoothingGroup = getFaceSmoothGroup selectedMesh faceIndex
+
+        if faceSmoothingGroup == 0 then
+        (
+                -- if not smooth use face normal
+                normal = getFaceNormal selectedMesh faceIndex
+        )       
+        else
+        (
+				local vertexIndexes = getFace selectedMesh faceIndex;
+                local vI = vertexIndexes[vertexIndex] 
+                local n = [0, 0, 0] -- point3
+                for adjoiningFaceIndex in (meshop.getFacesUsingVert selectedMesh vI) do
+                (
+					sg = getFaceSmoothGroup selectedMesh adjoiningFaceIndex;
+					and_sg = bit.and sg faceSmoothingGroup ;
+					if (and_sg != 0) then
+						n += getFaceNormal selectedMesh adjoiningFaceIndex ;
+                )
+                normal = normalize n;
+        ) 	
+
+	normal;
+)
+
+function tabEquals tab1 tab2 =
+(
+	if (tab1.count != tab2.count) then
+		return false;
+
+	for i=1 to tab1.count do
+		if (tab1[i] != tab2[i]) then
+			return false;
+		
+	return true;
+)
+
+function doesVertexExistAlready searchTable vertexIndex vertexNormal vertexColorIndex vertexAlphaIndex UVs exportUV exportColor =
+(
+	local start_uvs;
+	vertex = #();
+	--format "vertex a tester % % \n" vertexIndex vertexNormal;
+
+	if (searchTable[vertexIndex]==undefined) then
+		return 0;
+	
+	for vertex in searchTable[vertexIndex] do
+	(
+		if (vertex[2]!=vertexNormal) then
+			continue;
+			
+		if (exportColor) then
+		(
+			if (vertex[3]!=vertexColorIndex) then
+				continue;
+				
+			if (vertex[4]!=vertexAlphaIndex) then
+				continue;
+				
+			start_uvs = 4;
+		)
+		else
+		(
+			start_uvs = 2;
+		)
+		
+		if (exportUV) then
+		(
+			local equal = true;
+			for i=1 to UVs.count do
+			(
+				if (vertex[start_uvs+i]!=UVs[i]) then
+				(
+					equal = false;
+					exit;
+				)
+			)
+			if (not equal) then 
+				continue;
+		)
+		
+		return vertex[1] ;
+	)
+	return 0;
+)
+
+----------------------------------------------------------------------------
+-- Retrieves al the datas which wil be used
+-- tmesh is the mesh object
+-- flipyz = true if you want coords to fit in OGRE.
+-- flipNormal = true if you want to flip normals
+-- scale : the scale factor
+-- exportUV = true if you want to export texture
+-- numUVsets : number of texture coordinates to export (if exportUV is true, the minimum is 1. if exportUV is false, this parameter is irrelevant)
+-- exportColor = true if you want to export vertices colors
+-- sk : the skin modifier (may be undefined)
+-- phy : the physique modifier (may be undefined)
+-- stores datas in verticesArrays and facesArrays.
+----------------------------------------------------------------------------
+function getDatas tmesh flipyz scale flipNormal exportUV numUVsets exportColours exportHelpers sk phy =
+(
+	local face;						--index of the current face
+	local localVertexIndex; 		--index of the current vertex (relative to the current face) ie : 1, 2 or 3
+	local vertexNormal;				--normal of the current vertex (Point3)
+	local vertexPosition;			--position of the current vertex (Point3)
+	local faceVerticesIndices;		--indices of the vertex for the current face (Point3)
+	local vertexIndex;				--index of the current vertex (in the mesh)
+	local matId;					--material Id of the current face
+	local numFaces;					--number of faces in the mesh
+	
+	local faceVertexColorIndices;	--indices of the vertices for the current face in the vertex color table of the mesh (Point3)
+	local vertexColorIndex;			--index of the current vertex in the vertex color table of the mesh
+	local vertexColor;				--vertex color for the current vertex (color)
+	local faceVertexAlphaIndices;	--indices of the vertices for the current face in the vertex alpha table of the mesh (Point3)
+	local vertexAlpha;				--vertex alpha of the current alpha
+	local vertexAlphaIndex;			--index of the current vertex in the vertex alpha map channel (-2)
+	
+	local ogreFace;					--indices of the vertices for the current face (references the list of vertices that is gonna be exported, not the vertices of the original mesh)
+	
+	local vertexWeightCount;		--number of bones influencing the current vertex
+	local vertexWeight;				--current weight
+	
+	local searchTable ;
+	local k ;
+	local rootId ;
+	
+	local UVcoords;					-- will contain the texture coordinates for the current vertex. its size will be numUVsets*2 if exportUV is true.
+	
+	-- searchTables will contain a searchTable for every subMesh.
+	-- searchTable will be the current table for the current subMesh.
+	-- Every vertex will be stored here to check for duplicates.
+	searchTable=#() ;
+	searchTables=#() ;
+	
+	-- initialisation of the current ogre face as a vector
+	ogreFace=Point3 12 12 12 ;
+	
+	-- Data arrays init.	
+	verticesArrays=#() ;
+	facesArrays=#() ;
+	boneAssignments = #() ;
+	submeshesId = #() ;
+	UVcoords=#() ;
+
+	-- compute bones list for the model.
+	BonesList=#()
+	computeBonesList phy sk exportHelpers ;
+
+	numFaces = (getNumFaces tmesh);
+
+	-- For each face
+	for face = 1 to numFaces do 		-- LOOP on FACES --
+	(		
+		OgreExportObject.exportProgress.value = (100.0*face/numFaces);
+	
+		faceVerticesIndices = getFace tmesh face ;
+		matId = getFaceMatId tmesh face ;		
+		
+		-- Vertex Color Face
+		if (exportColours) then
+		(
+			faceVertexColorIndices = meshOp.getMapFace tmesh 0 face ;	-- 0 is the vertex color channel
+			faceVertexAlphaIndices = meshOp.getMapFace tmesh -2 face ;	-- -2 is the vertex alpha channel
+		)
+		else
+		(
+			faceVertexColorIndices = Point3 1 1 1 ;
+			faceVertexAlphaIndices = Point3 1 1 1 ;
+		)
+		
+		-- For each vertex whose face is made up of.
+		for localVertexIndex=1 to 3 do 				-- LOOP on VERTICES --
+		(			
+		    vertexIndex = (int) (faceVerticesIndices[localVertexIndex]) ;
+			vertexColorIndex = (int) (faceVertexColorIndices[localVertexIndex]) ;
+			vertexAlphaIndex = (int) (faceVertexAlphaIndices[localVertexIndex]);
+			vertexNormal = getVertexNormalAccordingToSmoothingGroups tmesh face localVertexIndex;
+			-- flip normal capability
+			if (flipNormal) then
+			(
+				vertexNormal = vertexNormal * -1
+			)
+			
+			-- we retrieve datas. it depends on options.
+			-- UV face
+			if (exportUV) then
+			(
+				UVcoords=#();
+				for ch=1 to numUVsets do
+				(
+					local vertInds = meshOp.getMapFace tmesh ch face ;
+					local UVW = meshOp.getMapVert tmesh ch vertInds[localVertexIndex] ;	-- retrieve the UV for the corresponding channel.
+					append UVcoords UVW[1];
+					append UVcoords UVW[2];							-- don't care about the W coordinate
+				)
+			)
+			else
+			(
+				UVcoords=#();
+			)
+		
+			-- we try to find the corresponding searchtable.											
+			if (searchTables[matId] == undefined ) then
+				searchTables[matId]=#() ;			-- we found a new subMesh, create the searchTable for it
+			
+			searchTable = searchTables[matId] ;		-- set the searchTable to the current subMesh
+			
+			newVertex = 1 ;
+			ogreVertNum = 1;
+			-- Maybe this vertex has already been found.
+			-- So we use searchTable.
+			res = doesVertexExistAlready searchTable vertexIndex vertexNormal vertexColorIndex vertexAlphaIndex UVcoords exportUV exportColours;
+			if (res==0) then
+			(
+				newVertex = 1 ;
+				--format "nouveau vertex % % %\n" vertexIndex vertexNormal UVcoords;
+			)
+			else
+			(
+				newVertex = 0;
+				ogreVertNum = res;
+			)
+
+			if (newVertex == 1) then -- That is to say it has not been found. 
+			(
+				-- Maybe the material found defines a new submesh...
+				if (verticesArrays[matId] == undefined) then
+				(
+					format "- Submesh detected: material ID = %\n" matId
+					verticesArrays[matId] = #() ;
+					boneAssignments[matId] = #() ;
+					append submeshesId matId ;
+				)
+
+				-- the vertex number for the current submesh must be updated
+				-- vertex number is increased
+				ogreVertNum = verticesArrays[matId].count + 1; 
+				
+				-- it is added to the searchTable
+				if (searchTable[vertexIndex]==undefined) then
+				(
+					searchTable[vertexIndex] = #() ;
+				)
+				local data = #();
+				append data ogreVertNum ;
+				n = copy vertexNormal ;
+				append data n ;
+				if (exportColours) then
+				(
+					append data VertexColorIndex ;
+					append data VertexAlphaIndex ;
+				)
+				if (exportUV) then
+				(
+					if (UVcoords.count > 0) then
+					(
+						for uv=1 to UVcoords.count do
+							append data UVcoords[uv];
+					)
+				)
+				append searchTable[vertexIndex] data ;
+					
+				-- we retrieve data
+				vertexPosition = getVert tmesh faceVerticesIndices[localVertexIndex] ;
+				
+				vertexColor = Point3 0 0 0;
+				vertexAlpha = 255;
+				if (exportColours) then
+				(
+					vertexColor = (meshOp.getMapVert tmesh 0 vertexColorIndex);
+					vertexAlpha = (meshOp.getMapVert tmesh -2 vertexAlphaIndex)[1];
+				)
+				
+				-- change scale
+				vertexPosition = vertexPosition * scale ;
+		
+				-- flip axes		
+				if (flipyz) then
+				(
+					vertTmp = copy vertexPosition ;
+					vertexPosition[2] = vertTmp[3] ;
+					vertexPosition[3] = -vertTmp[2] ;
+					
+					nmalTmp = copy vertexNormal ;
+					vertexNormal[2] = nmalTmp[3] ;
+					vertexNormal[3] = -nmalTmp[2] ;
+				)
+				
+				-- store the vertex in verticesArrays
+				vertexData = #(vertexPosition[1],vertexPosition[2],vertexPosition[3],vertexNormal[1],vertexNormal[2],vertexNormal[3],vertexColor[1],vertexColor[2],vertexColor[3],vertexAlpha) ;
+				if (exportUV) then
+				(
+					for ch=1 to numUVsets do
+					(
+						append vertexData UVcoords[1+(ch-1)*2];
+						append vertexData UVcoords[2+(ch-1)*2];
+					)
+				)
+				append verticesArrays[matId] vertexData ;   -- Vertex is added to datas
+				
+				-- And now, bone assignments. (if a skin modifier is present)
+				-- It seems that there are issues when just few vertices have bone assignments.
+				-- So there is at least the root assignment.
+				if (sk != undefined) then 
+				(
+					vertexWeightCount = skinOps.getVertexWeightCount sk vertexIndex ; 
+
+					if (vertexWeightCount > 4) then
+					(
+						if (not g_MAX) then
+						(
+							format "*** vertex % has more than 4 bones assigned...\n" vertexIndex;	
+						)
+					)
+					
+					for k=1 to vertexWeightCount do
+					(
+						bid = skinOps.getVertexWeightBoneID sk vertexIndex k ;
+						bname = skinOps.getBoneName sk bid 1 ;
+						bname = replaceSpaces bname;
+						vertexWeight = skinOps.getVertexWeight sk vertexIndex k ;
+						
+						id_bone = findItem BonesList bname;
+						if (id_bone != 0) then
+							append boneAssignments[matId] #(ogreVertNum-1,id_bone-1,vertexWeight) ;
+					)
+					-- assignment to the root bone.
+					if (vertexWeightCount==0) then 
+					(
+						-- gets the root Id:
+						rootId=getRootsId sk
+						rootname = skinOps.getBoneName sk rootId[1] 1 ;
+						id_bone = findItem BonesList rootname ;
+						if (id_bone != 0) then
+							append boneAssignments[matId] #(ogreVertNum-1,id_bone-1,1) ;
+					)
+				)
+				-- same thing with physique modifier if defined
+				if (phy != undefined) then 
+				(
+					vertexWeightCount = physiqueOps.getVertexBoneCount $ vertexIndex ;
+					
+					if (vertexWeightCount > 4) then
+					(
+						if (not g_MAX) then
+						(
+							format "*** vertex % has more than 4 bones assigned...\n" vertexIndex;	
+						)
+					)
+					
+					for k=1 to vertexWeightCount do
+					(
+						bone = physiqueOps.getVertexBone $ vertexIndex k
+						vertexWeight = physiqueOps.getVertexWeight $ vertexIndex k
+						
+						-- search the bone number
+						bname = replaceSpaces bone.name;
+						id_bone = findItem BonesList bname ;
+						if (id_bone!=0)	then				
+							append boneAssignments[matId] #(ogreVertNum-1,id_bone-1,vertexWeight) ;
+					)
+					-- assignment to the first bone if the vertex was not assigned.
+					if (vertexWeightCount==0) then 
+					(
+						-- gets the root Id:
+						append boneAssignments[matId] #(ogreVertNum-1,0,1) ;
+					)
+				)				
+
+			)
+		
+			ogreFace[localVertexIndex] = ogreVertNum - 1;		
+		)
+		
+		-- flip normal capability
+		if (flipNormal) then
+		(
+			faceTmp = copy ogreFace ;
+			ogreFace[2] = faceTmp[3] ;
+			ogreFace[3] = faceTmp[2] ;
+		)
+
+		if (facesArrays[matId] == undefined) then
+			facesArrays[matId] = #() ;		
+		append facesArrays[matId] #(ogreFace[1],ogreFace[2],ogreFace[3]) ;   -- Face is added to datas		
+	)
+)
+
+-------------------------------------------------
+-- writes in the mesh.xml file
+-- outName : filename
+-- exportUV = true if you want to export texture
+-- numUVsets : number of UVs sets per vertex to be exported (only relevant if exportUV = true)
+-- exportColor = true if you want to export vertices colors
+-- material : material used by the mesh
+-- Uses the arrays verticesArrays and facesArrays
+-------------------------------------------------
+function writeM exportUV numUVsets exportColours material outName =
+(
+	local a,v,f,submatname,hasSkeleton,outFile,hasColours,matId ;
+	
+	hasSkeleton = false ;
+	texCoordString = "" ;
+	if (exportUV) then 
+	(
+		texCoordString = "texture_coords=\"" + (numUVsets as string) + "\" " ;
+		for num=1 to numUVsets do
+		(
+			texCoordString =  texCoordString + "texture_coords_dimensions_" + ((num-1) as string) + "=\"2\" "; -- I really don't care about the W coordinates
+		)
+	)
+	hasColours = "false" ;	
+	if (exportColours) then
+		hasColours = "true" ;		
+	
+
+	-- the final file is created
+	if (g_MAX) then
+	(
+		ClearListener();
+		if (g_MAX_use_listener) then
+			format("<ogrestartfile>%</ogrestartfile><ogrestartdata>\n") (outName + ".mesh.xml");
+		outFile = listener;
+	)
+	else
+	(
+		outFile = createfile (outName + ".mesh.xml") ;
+	)
+
+	-- writes the header
+	format("<mesh>\n") to:outFile ;
+
+	-- submeshes start
+	format("\t<submeshes>\n") to:outFile ;
+	
+	-- for each submesh
+	for matId in submeshesId do 
+	(
+		-- Do we need 32-bit indexes?
+		use32bitindexes = "false";
+		if arrayLength verticesArrays[matId] > 65535 then
+			use32bitindexes = "true";
+		
+		-- get the name of the sub material if needed.
+		submatname = replacespaces material.name ;
+		
+		if (classof material == MultiMaterial) then
+		(
+			if (material.materialList[matId]==undefined) then
+			(
+				msg = "";
+				format "You are using in your mesh a material Id (%) that does not exist in your MultiMaterial (%)\nMaterial information won't be properly exported." matId submatname to:msg ;
+				messageBox msg;	
+			)
+			else
+			(
+		    	submatname  += "/" + replacespaces material.materiallist[matId].name ;
+			)
+		)
+			
+									-- HELLO ! --
+			---------------------------------------------------------------------------------
+			-- IF YOU COME HERE BECAUSE YOUR OBJECT FAILED EXPORTING, MAYBE THAT'S BECAUSE --
+			-- YOU USE MATERIAL ID IN YOUR MESH THAT DOESN'T EXIST  IN YOUR MULTIMATERIAL  --
+			---------------------------------------------------------------------------------
+		
+		format("\t\t<submesh material = \"%\" usesharedvertices=\"false\" use32bitindexes=\"%\">\n") submatname use32bitindexes to:outFile ;
+	
+		if (not g_MAX) then
+			format "- writing faces...\n"		
+		format("\t\t\t<faces count=\"%\">\n") (arraylength facesArrays[matId]) to:outFile;
+		
+		-- for each face
+		for f in facesArrays[matId] do 
+		(
+			format("\t\t\t\t<face ") to:outFile ;
+			format("v1=\"%\" v2=\"%\" v3=\"%\" />\n") ((int)f[1]) ((int)f[2]) ((int)f[3]) to:outFile ;
+		)
+		format("\t\t\t</faces>\n") to:outFile ;
+
+		if (not g_MAX) then
+			format "- writing vertices...\n"	
+		format("\t\t\t<geometry vertexcount=\"%\">\n") (arrayLength verticesArrays[matId] ) to:outFile;
+		format("\t\t\t\t<vertexbuffer positions=\"true\" normals=\"true\" colours_diffuse=\"%\" %>\n") hasColours TexCoordString to:outFile ;		
+		
+		-- for each vertex
+		for v in verticesArrays[matId] do
+		(	
+			format("\t\t\t\t\t<vertex>\n") to:outFile ;
+			format("\t\t\t\t\t\t<position x=\"%\" y=\"%\" z=\"%\" />\n") v[1] v[2] v[3] to:outFile ;
+			format("\t\t\t\t\t\t<normal x=\"%\" y=\"%\" z=\"%\" />\n") v[4] v[5] v[6] to:outFile ;
+			if (exportUV) then
+			(
+				for ch=1 to numUVsets do
+					format("\t\t\t\t\t\t<texcoord u=\"%\" v=\"%\" />\n") v[11+((ch-1)*2)] (1 - v[12+((ch-1)*2)]) to:outFile ;		
+			)
+			if (exportColours) then 
+			(
+				color_string = (v[7] as string) + " " + (v[8] as string) + " " + (v[9] as string) + " " +(v[10] as string);
+				format("\t\t\t\t\t\t<colour_diffuse value=\"%\" />\n") color_string to:outFile ;
+			)
+			format("\t\t\t\t\t</vertex>\n") to:outFile ;
+		)	
+		format("\t\t\t\t</vertexbuffer>\n") to:outFile ;
+		format("\t\t\t</geometry>\n") to:outFile ;
+					
+	
+		-- and now bone assignments (and skeleton), if there is at least one element in boneAssignments array.		
+		if ((arrayLength boneAssignments[matId]) != 0) then 
+		(
+			hasSkeleton = true ;
+			if (not g_MAX) then
+				format "- writing bone assignments...\n"	
+			format("\t\t\t<boneassignments>\n") to:outFile ;
+				
+			for a in boneAssignments[matId] do 
+			(
+				format("\t\t\t\t<vertexboneassignment vertexindex=\"%\" boneindex=\"%\" weight=\"%\" />\n") a[1] a[2] a[3] to:outFile ;
+			)
+			format("\t\t\t</boneassignments>\n") to:outFile ;
+		)
+	
+		-- submesh end
+		format("\t\t</submesh>\n") to:outFile ;
+	)
+	
+	-- submeshes end
+	format("\t</submeshes>\n") to:outFile ;
+
+	-- Skeleton link if there is at least one bone assignement.	
+	if (hasSkeleton) then 
+	(
+		t = filterstring outName "\\" ;
+		format ("\t<skeletonlink name=\"%\"/>\n") (t[arrayLength t] + ".skeleton") to:outFile ;				
+	)
+				
+	format("</mesh>\n") to: outFile ;
+	
+	if (not g_MAX) then
+	(
+		close outFile ;
+	)
+	else
+	(
+		if (g_MAX_use_listener) then
+			format("</ogrestartdata>\n") to: outFile;
+	)
+)
+
+
+---------------------------------
+-- writes the mesh: main function
+---------------------------------
+
+function writeMesh pmesh exportOptions out_name =
+(
+	local m,sk,outFile,message,phy ;
+
+	m = snapshotAsMesh pmesh ;
+		
+	-- tries to find errors
+	message = exploreMesh pmesh exportOptions.exportUV exportOptions.exportColours;
+	if (message != "OK") then
+	(
+		MessageBox ("\n There is a problem with your mesh:\n" + message + "\n\nOperation aborted") ;
+		delete m ;
+		return false ;
+	)
+	else
+	(			
+		format "\n\n"
+		format "------------------------------------------\n"
+		format "-------   OGRE Mesh Exporter Log   -------\n"
+		format "-----                                -----\n"
+		
+		-- get the skin modifier ( may be undefined )
+		-- and physique modifier
+		phy = getPhysique pmesh ;
+		sk = getSkin pmesh;
+		
+		if (sk != undefined) then
+			format "Skin modifier detected.\n"
+			
+		if (phy != undefined) then
+			format "Physique modifier detected.\n"
+					
+		-- if not undefined, skin modifier is selected in modifier tab. Or there will be an error.
+		if (sk != undefined) then
+		(
+			-- in order to perform, skin should be opened
+			max modify mode ;
+			modPanel.setCurrentObject pmesh.modifiers[#Skin] ;		
+		)
+		
+		-- physique
+		if (phy != undefined) then
+		(
+			-- in order to perform, skin should be opened
+			max modify mode ;
+			modPanel.setCurrentObject pmesh.modifiers[#Physique] ;
+			--physiqueOps.setInitialPose pmesh true ;
+		)
+
+		OgreExportObject.exportProgress.value = 0;
+	
+		-- retieving material
+		if (pmesh.material != undefined) then (
+			materialName = pmesh.material.name ;
+			replaceSpaces materialName ;
+		)
+		else materialName = pmesh.name + "Material" ;
+		
+		format "Material name exported : \n- %/*** \n" materialName ;
+		
+		-- retrieving all datas
+		format "Retrieving vertices and faces data : \n" ;
+	
+		getDatas m exportOptions.flipyz exportOptions.scale exportOptions.flipNormal exportOptions.exportUV exportOptions.UVchannels exportOptions.exportColours exportOptions.exportHelpers sk phy;
+		
+		-- writing in the file
+		format "Writing in file %.mesh.xml : \n" out_name ;
+		WriteM exportOptions.exportUV exportOptions.UVchannels exportOptions.exportColours pmesh.material out_name ;
+		
+		if (not g_MAX) then
+			format "Export successful.\n"		
+		
+		delete m ;		
+	
+		if (not g_MAX) then	
+		(
+			format "-----                                -----\n"
+			format "----------          END          ---------\n"
+			format "------------------------------------------\n"
+		)
+		
+		--MessageBox ("Exporting mesh successful !") ;
+		
+		OgreExportObject.exportProgress.value = 100;
+	)
+	return true ;
+)
+
+
+

+ 25 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreSkeletonLib_mtlfns.ms

@@ -0,0 +1,25 @@
+function writeMaterial material outFile =
+(
+	local t, matname, mat;
+
+	mat = material ;
+	
+	if (mat != undefined) then 
+	(
+		matname = material.name ;	
+		format "3ds/%\n" matname to:outFile ;
+		format "{\n" ;
+    	format("   ambient % % %\n") (mat.ambient.r/255.0) (mat.ambient.g/255.0) (mat.ambient.b/255.0) to:outFile ;
+	    format("   diffuse % % %\n") (mat.diffuse.r/255.0) (mat.diffuse.g/255.0) (mat.diffuse.b/255.0) to:outFile ;
+    	format("   specular % % % %\n") (mat.specular.r/255.0) (mat.specular.g/255.0) (mat.specularLevel*100.0) to:outFile ;
+		format("   emissive % % % %\n") (mat.selfIllumColor.r/255.0) (mat.selfIllumColor.g/255.0) (mat.selfIllumColor.b/255.0) (mat.selfIllumAmount) to:outFile ;
+		
+	    -- if mesh has a material: reading the diffuse map:
+		if ((mat.diffusemap != undefined) and (iskindof mat.diffusemap Bitmaptexture)) then
+		(	
+			t = filterstring mat.diffusemap.filename "\\" ;
+			format "{\n  texture % \n}" t[arrayLength t] to:outFile ;
+		)
+		format "}\n" ;
+	)
+)

+ 337 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreSkeletonLib_skelfns.ms

@@ -0,0 +1,337 @@
+-----------------------------------------------------------------------------
+-- gets  keyframes from the controllers animation
+-----------------------------------------------------------------------------
+function getTimeList obj firstframe lastframe =
+(
+	local list,rotContr,posContr,  ;
+	list = #(firstframe) ;
+	list2 = #() ; -- this is the list which will be returned.
+	
+	-- Biped_Objects and Bones don't have the same properties
+	
+	-- Biped_Objets
+	if (isKindOf obj Biped_Object) then (
+		for e in obj.controller.keys do (
+			t =e.time ;
+			if (t>firstFrame and t<=lastFrame) then
+				append list t ;
+		)
+	)
+	-- Bones	
+	else (
+		rotContr = obj.rotation.controller ;
+		posContr = obj.pos.controller ;
+		
+		for e in rotContr.keys do (
+			t = e.time ;
+			if (t>firstFrame and t<=lastFrame) then
+				append list t ;
+		)
+		for e in posContr.keys do (
+			t = e.time ;
+			if (t>firstFrame and t<=lastFrame) then
+				append list t ;
+		)	
+	)
+	-- if several keyframes have the same value, we keep just one
+	keepLoneValues list list2 ;
+	list2 ;
+)
+
+
+-----------------------------------------------------------------------------
+-- write <track />
+-- Selected keys belongs to [firstframe,lastFrame]
+-- time = (key.time - firstFrame)*length/(lastFrame-firstFrame)
+--   (e.g. first key has time 0.)
+-----------------------------------------------------------------------------
+
+function writeTrack sk boneId firstframe lastframe length scale flipYZ outFile=
+(
+	local angle,timef,i,bname,d,mref,mparent ;
+	
+	-- displays information in the maxscript listener
+	format "retrieving key information for % ...\n" (skinOps.GetBoneName sk boneId 1) ;
+	
+	-- gets bone acording to the parameter boneId
+	bname= skinOps.GetBoneName sk boneId 1 ;
+	replaceSpaces bname ;
+	d = getNodeByName bname ;
+	
+	-- gets keyframe list
+	timelist = getTimeList d firstframe lastframe ;
+	
+	-- track header
+	format("\t\t\t\t<track bone = \"%\">\n") (replaceSpaces d.name) to:outFile ;
+	format("\t\t\t\t\t<keyframes>\n") to:outFile ;
+	
+	-- gets initial transform at frame 0f
+	at time 0f (
+		initTform = d.transform ;
+		if (not isRoot d) then (
+			mparent = d.parent.transform ;
+			initTform = initTform*inverse(mparent) ;
+		)
+		else if (flipYZ) then (
+			format " - flipping root bone track..." ;
+			initTform = flipYZTransform initTform ;
+		)
+	)
+		
+	-- for each frame in the list
+	for i in timelist do
+	(
+		-- moves slider time and compute OGRE time
+		at time i (
+			timef = ((float) (i-firstFrame)*length)/(lastframe - firstframe ) ;
+			
+		-- First, rotation which depends on initial transformation
+			Tform = d.transform ;
+			-- if this is the root bone
+			if (isRoot d) then (
+				mparent = matrix3 1 ;
+				-- if flipYZ == true
+				if (flipYZ) then
+					Tform = flipYZTransform Tform ;
+			)			
+			else
+				mparent = d.parent.transform ; 
+			
+			-- computes rotation
+			mref = initTform*mparent ;	
+			Tform = Tform*inverse(mref) ;
+			
+			-- rotation part is saved.
+			rot = Tform.rotation as angleaxis ;
+			angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :((((((
+			
+		-- Then, position which depends on parent			
+			Tform=d.transform ;
+			Tform=Tform*inverse(mparent) ;
+			
+			-- if this is the root bone and flipYZ == true
+			if (isRoot d and flipYZ) then (
+				Tform = flipYZTransform Tform ;
+			)
+			
+			-- substracts position of the initial transform
+			Tform.pos -= initTform.pos ;
+			Tform.pos = Tform.pos * scale ;
+			
+			pos = Tform.pos ;
+
+				
+			-- writes them !
+			format("\t\t\t\t\t\t<keyframe time=\"%\">\n") timef to: outFile ;
+			format("\t\t\t\t\t\t\t<translate x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to: outFile ;
+			format("\t\t\t\t\t\t\t<rotate angle=\"%\">\n") angle to:outFile ; 
+			format("\t\t\t\t\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") (rot.axis.x) (rot.axis.y) (rot.axis.z) to:outFile ;
+			format("\t\t\t\t\t\t\t</rotate>\n") to:outFile ;
+			format("\t\t\t\t\t\t</keyframe>\n") to:outFile ;
+		)
+	)
+	
+	-- track end
+	format("\t\t\t\t\t</keyframes>\n") to:outFile ;
+	format("\t\t\t\t</track>\n") d.name to: outFile ;
+)
+
+
+-------------------------------------------------------------------------------------------------
+------------------------------------------- WRITE SKELETON --------------------------------------
+-------------------------------------------------------------------------------------------------
+
+-- Nota :
+-- A vertex 0 and a bone 0 are added because 3dsmax starts with vertex 1 and bone 1.
+
+
+------------------
+-- write <bones />
+------------------
+
+function writeB sk id scale flipYZ outFile =
+(
+	-- gets bone acording to the parameter boneId
+	bname= skinOps.GetBoneName sk id 1 ;
+	replaceSpaces bname ;
+	d = getNodeByName bname ;
+	
+	-- gets initial transform at frame 0f
+	format("\t\t<bone id=\"%\" name=\"%\">\n") id (replaceSpaces d.name) to:outFile ;
+	
+	slidertime = 0f ;
+	Tform = d.transform ;
+	if (not isRoot d) then (
+		mparent = d.parent.transform ;
+		Tform = Tform*inverse(mparent) ;
+	)
+	
+	Tform.pos = Tform.pos * scale ;
+		
+	if ((isRoot d) and flipYZ) then (
+		format "- Flipping root bone... \n" ;
+		Tform = flipYZTransform Tform ;
+	)
+	
+	pos = Tform.pos ;
+	rot = Tform.rotation as angleaxis ;
+	angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :((((((
+	
+	format("\t\t\t<position x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to:outFile ;
+    format("\t\t\t<rotation angle=\"%\">\n") angle to:outFile ;
+    format("\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") rot.axis.x rot.axis.y rot.axis.z to:outFile ;
+   	format("\t\t\t</rotation>\n") to:outFile ;
+	format("\t\t</bone>\n") to:outFile ;
+)
+-----------------------------
+-- write Bones (using writeB)
+-----------------------------
+
+function writeBones sk scale flipYZ outFile =
+(
+	local i ;
+	
+	format("\t<bones>\n") to:outFile;
+	
+	-- we create a virtual bone number 0.
+	format("\t\t<bone id=\"0\" name=\"zzwxy\">\n") to:outFile ;
+	format("\t\t\t<position x=\"0\" y=\"0\" z=\"0\" />\n") to:outFile ;
+    format("\t\t\t<rotation angle=\"0\">\n") to:outFile ;
+    format("\t\t\t\t<axis x=\"1\" y=\"0\" z=\"0\" />\n") to:outFile ;
+    format("\t\t\t</rotation>\n") to:outFile ;
+	format("\t\t</bone>\n") to:outFile ;
+	i = 0 ;
+	for i=1 to (skinOps.GetNumberBones sk) do
+	(
+		writeB sk i scale flipYZ outFile ;
+	)
+	format("\t</bones>\n") to:outFile;
+)
+
+-----------------------------------
+-- recursive fun to write hierarchy
+-----------------------------------
+
+function writeH b outFile =
+(
+	if ( b.parent != undefined and (isKindOf b.parent BoneGeometry or iskindOf b.parent Biped_Object)) then
+	(
+		p = b.parent ;
+		format("\t\t<boneparent bone=\"%\" parent=\"%\" />\n") (replaceSpaces b.name) (replaceSpaces p.name) to:outFile ;
+	)
+	childrenArray = b.children ;
+	for i=1 to childrenArray.count do
+	(
+        if (isKindOf b BoneGeometry or iskindOf b Biped_Object) then
+			writeH childrenArray[i] outFile ;
+	)
+	
+)
+
+--------------------------
+-- write <bonehierarchy />
+--------------------------
+
+function writeHierarchy sk outFile =
+(
+	format("\t<bonehierarchy>\n") to:outFile ;
+	roots = getRoots sk ;
+	
+	-- ecriture de la track pour le bone 0.
+	format("\t\t<boneparent bone=\"zzwxy\" parent=\"%\" />\n") roots[1].name to:outFile ;
+	for b in roots do
+	(
+		writeH b outFile;
+	)
+	format("\t</bonehierarchy>\n") to:outFile ;
+)
+
+-----------------------
+-- write <animations />
+-----------------------
+function writeAnim sk firstFrame lastFrame length scale flipYZ name outFile =
+(
+	local i,n ;
+	
+	format("\t<animations>\n") to: outFile ;
+    format("\t\t<animation name=\"%\" length=\"%\">\n") name length to:outFile ;
+	format("\t\t\t<tracks>\n") to:outFile
+	
+	-- ecriture track 0
+	format("\t\t\t\t<track bone = \"zzwxy\">\n") to: outFile ;
+	format("\t\t\t\t\t<keyframes>\n") to:outFile ;
+	format("\t\t\t\t\t\t<keyframe  time=\"0\">\n") to:outFile ;
+	format("\t\t\t\t\t\t\t<translate x=\"0\" y=\"0\" z=\"0\" />\n") to: outFile ;
+	format("\t\t\t\t\t\t\t<rotate angle=\"0\">\n")  to:outFile ; 
+	format("\t\t\t\t\t\t\t\t<axis x=\"1\" y=\"0\" z=\"0\" />\n")  to:outFile ;
+	format("\t\t\t\t\t\t\t</rotate>\n") to:outFile ;
+	format("\t\t\t\t\t\t</keyframe>\n") to:outFile ;
+	format("\t\t\t\t\t</keyframes>\n") to:outFile ;
+	format("\t\t\t\t</track>\n") to: outFile ;
+	
+	n = skinOps.GetNumberBones sk ;
+	liste = #(0,20,30,40) ;
+	for i = 1 to n do
+		writeTrack sk i firstframe lastframe length scale flipYZ outFile ;
+
+	format("\t\t\t</tracks>\n") to:outFile					
+	format("\t\t</animation>\n") to: outFile ;
+	format("\t</animations>\n") to: outFile ;
+)
+
+-------------------------------------------------------------
+-- write <skeleton /> main function
+-- write the animation in the file out_name + ".skeleton.xml"
+-- between the frame firstFrame and lastFrame 
+-- and scale time according to length
+-------------------------------------------------------------
+
+function writeSkeleton pmesh exportOptions out_name = 
+(
+	local sk,n,keys,initialKeys,messages ;
+	sk = getSkin pmesh ;
+	
+	if (sk == undefined) then
+	(
+		MessageBox "There is no skin modifier for this object" ;
+	)
+	else
+	(	
+		-- in order to perform, skin should be opened
+		max modify mode ;
+		modPanel.setCurrentObject pmesh.modifiers[#Skin] ;
+		
+			format "------------------------------------------\n"
+			format "------ OGRE skeleton Exporter Log   ------\n"
+			format "------------------------------------------\n"
+		
+			format "Exporter options :\n"
+			format "- firstFrame: % \n- lastFrame: %\n" exportOptions.firstFrame exportOptions.lastFrame ;
+			-- creates the output file
+			outFile = createfile (out_name + ".skeleton.xml") ;
+
+				-- writes header
+				format("<skeleton>\n") to:outFile ;	
+	
+				format "Writing bones :\n" ;
+				writeBones sk exportOptions.scale exportOptions.flipYZ outFile ;
+				format "Writing bone hierarchy.\n" ;
+				writeHierarchy sk outFile ;
+				format "Writing bone tracks.\n" ;
+				writeAnim sk exportOptions.firstFrame exportOptions.lastFrame exportOptions.length exportOptions.scale exportOptions.flipYZ exportOptions.animName outFile ;
+
+				-- ecriture, fin des balises
+				format("</skeleton>\n") to: outFile ;
+				
+			format "------------------------------------------\n"
+			format "----------          END          ---------\n"
+			format "------------------------------------------\n"
+		
+				close outFile ;
+				messageBox "Exporting skeleton successful !"
+	)
+	
+)
+
+
+

+ 286 - 0
Extras/OgreMaxscriptExport/ogre/lib/OgreSkeletonLib_usefulfns.ms

@@ -0,0 +1,286 @@
+----------------------------------------------------------------------------------------
+-- ------------------------------ SOME USEFUL FUNCTIONS ----------------------------- --
+----------------------------------------------------------------------------------------
+
+-- global variables
+global g_MAX = false;
+global g_MAX_use_listener = false;
+
+-- global structures
+struct exportOptions (sampleRate, ikSampleRate, scale, flipyz, flipNormal, exportColours, exportUV, UVchannels, exportHelpers);
+struct exportAnims (names, startframes, endframes, lengths) ;
+
+global Anims;
+global Options;
+
+global OgreExportFloater;
+global OgreExportOptions, OgreExportObject, OgreExportMesh, OgreExportAnimation, OgreExportMaterial, OgreExportAbout;
+
+--------------------------------------------------------------------
+-- compute the transform, if you want to flip Y and Z axis,
+-- because the axis which 'defines height' in OGRE is the Y axis,
+-- whereas 3dsmax uses the Z one.
+--------------------------------------------------------------------
+function flipYZTransform Tform = (
+	local axis1,axis2,axis3,t,m
+	
+	-- computes the matrix
+	axis1 = point3 1 0 0 ;
+	axis2 = point3 0 0 1 ;
+	axis3 = point3 0 -1 0 ;
+	t = point3 0 0 0 ;
+	m=matrix3 axis1 axis2 axis3 t ;
+	
+	-- multiplies by the inverse
+	Tform = Tform*inverse(m) ;
+
+	return Tform ;
+)
+
+-----------------------------------------------------------------------------
+-- check if the bone is the root object of the biped
+-----------------------------------------------------------------------------
+function isPelvis bipObj =
+(
+	if (bipObj == undefined) then return false ;
+	if (classof bipObj != Biped_Object) then return false;
+	return ((biped.getNode bipObj 13) == bipObj) ;
+)
+
+
+-----------------------------------------------------------------------------
+-- check if the bone is the footstep object of the biped
+-----------------------------------------------------------------------------
+function isFootStep bipObj =
+(
+	if (bipObj == undefined) then return false ;
+	if (classof bipObj != Biped_Object) then return false;
+	return ((biped.getNode bipObj 16) == bipObj) ;
+)
+
+
+--------------------------------------------------------------------
+-- returns if the bone is the root or not,
+-- ia if its parent is undefined or is not a bone.
+--------------------------------------------------------------------
+function isRoot b = (
+	if (b.parent==undefined or not (iskindof b.parent BoneGeometry or iskindOf b.parent Biped_Object) ) then
+		return true ;
+	else
+		return false ;
+	
+)
+
+--------------------------------------------------------------------
+-- returns if the bone is the root or not,
+-- handles standard bones as well as biped
+--------------------------------------------------------------------
+function isRootUniversal b = (
+	if (isRoot b) then
+		return true; 
+	else if (isPelvis b) then
+		return true;
+	else
+		return false;
+)
+
+--------------------------------------------------------------------
+-- returns if the object is a root or not,
+-- handles any kind oj object
+--------------------------------------------------------------------
+function isRootUniversal2 b = (
+	bname = replaceSpaces b.name;
+	ind = (findItem RootsList bname);
+	if (ind == 0) then
+		return false;
+	else
+		return true;
+)
+
+--------------------------------------------------------------------
+-- returns if the object is part of the skin (or physique modifier)
+--------------------------------------------------------------------
+function isPartOfModifier b sk phy = (
+	name = replaceSpaces b.name ;
+	if (sk!=undefined) then
+	(
+		for i=1 to (skinOps.GetNumberBones sk) do
+		(
+			bname = skinOps.GetBoneName sk i 1 ;
+			replaceSpaces bname ;
+			if (name == bname) then
+				return true;
+		)
+	)
+	else if (phy!=undefined) then
+	(
+		for i=1 to (physiqueOps.GetBoneCount $) do
+		(
+			bname = (physiqueOps.GetBones $)[i].name;
+			replaceSpaces bname ;
+			if (name == bname) then
+				return true;
+		)
+	)
+	return false;
+)
+
+--------------------------------------------------------------------
+-- creates a new array (which must be set up as an array before
+-- calling this function) in which there isn't the same element.
+-- Moreover, the array is sorted.
+--------------------------------------------------------------------
+
+function keepLoneValues a b= (
+	local e, last_e ;
+	sort a ;
+	last_e = undefined ;
+	for e in a do (
+		if (e!=last_e) then 
+			append b e ;
+		last_e = e ;
+	)
+)
+
+---------------------------------------------------------------------
+-- replaces " " by "_" in a string.
+-- when a name is for example Left Biceps max knows it at Left_Biceps
+-- and execute function will not work if you don't use this function
+---------------------------------------------------------------------
+function replaceSpaces s =
+(
+	for i=1 to s.count do
+	(
+		if (s[i] == " ") then
+			s[i] = "_" ;
+	)
+	s ;
+)
+
+--------------------------------
+-- return the length of an array
+--------------------------------
+function arrayLength a = 
+(
+	local i ;
+	i = 1 ;
+	while (a[i] != undefined) do
+		i = i + 1 ;	
+	i-1 ;
+)
+
+-----------------------------------------------------------------------------
+-- return the skin modifier or undefined if object don't have a skin modifier
+-----------------------------------------------------------------------------
+function getSkin obj =
+(
+	local s,i ;
+	s = undefined ;
+	if obj != undefined then
+		for i in obj.modifiers do
+		(
+			if iskindof i Skin do
+				s = i ;
+		)
+	s ;
+)
+
+
+-----------------------------------------------------------------------------
+-- return the physique modifier or undefined if object don't have it
+-----------------------------------------------------------------------------
+function getPhysique obj =
+(
+	local s,i ;
+	s = undefined ;
+	if obj != undefined then
+		for i in obj.modifiers do
+		(
+			if iskindof i Physique do
+				s = i ;
+		)
+	s ;
+)
+
+-----------------------------------------------------------------------------
+-- return the OctopusExport modifier or undefined if object don't have it
+-----------------------------------------------------------------------------
+function getOctopusExport obj =
+(
+	local s,i ;
+	s = undefined ;
+	if obj != undefined then
+		for i in obj.modifiers do
+		(
+			if iskindof i OctopusMeshModifier do
+				s = i ;
+		)
+	s ;
+)
+
+--------------------------------------------------
+-- return an Array with the root bones of the skin
+--------------------------------------------------
+
+function getRoots skin =
+(
+	local rootstab,n,i,c,d ;
+	rootstab = #() ;
+	n = skinOps.GetNumberBones skin ;
+	for i = 1 to n do
+	(
+		c= skinOps.GetBoneName skin i 1 ;
+		replaceSpaces c ;
+		d = getNodeByName c ;
+		if (isRoot d) then
+			append rootstab d ;
+	)
+	rootstab ;
+)
+
+--------------------------------------------------------
+-- return an Array with the ID of root bones of the skin
+--------------------------------------------------------
+
+function getRootsId skin =
+(
+	local rootstab,n,i,c,d ;
+	rootstab = #() ;
+	n = skinOps.GetNumberBones skin ;
+	for i = 1 to n do
+	(
+		c= skinOps.GetBoneName skin i 1 ;
+		replaceSpaces c ;
+		d = getNodeByName c ;
+		if (isRoot d) then
+			append rootstab i ;
+	)
+	rootstab ;
+)
+
+-------------------------------------------------------
+-- return a angleAxis given a Quaternion
+-------------------------------------------------------
+
+function toAngleAxis q =
+(
+	local angle, axis;
+	local result;
+	
+	fSqrLength = q.x*q.x+q.y*q.y+q.z*q.z ;
+	if (fSqrLength > 0.0) then
+	(
+		angle = ((acos q.w) * pi / 90);
+		fInvLength = 1.0 / (sqrt fSqrLength);
+		axis = [q.x*fInvLength, q.y*fInvLength, q.z*fInvLength];
+		result = angleAxis angle axis;
+	)
+	else
+	(
+		angle = 0;
+		axis = [1,0,0];
+		result = angleAxis angle axis;
+	)
+	
+	return result;
+)

+ 154 - 0
Extras/OgreMaxscriptExport/ogre/lib/ogreMaterialLib.ms

@@ -0,0 +1,154 @@
+-- write into "outfile" the selected OgreMaterial
+-- precondition: iskindof material OgreMAterial == true.
+-- Use export OgreMaterial.
+function writeOgreMaterial material outname =
+(
+	local t, matname, mat, ext;
+
+	mat = material ;
+	local outFile ;
+	
+	if (mat != undefined) then 
+	(
+		matname = material.name ;
+		ext = ".mat.xml" ;
+		fname = outname +"_"+ matname + ext ;
+		outFile = createfile fname ;
+
+		format "<material>\n" to:outFile ;
+		format "    <technique name=\"Techniques/Diff.xml\" />\n" to:outFile ;
+		format "    <parameter name=\"MatDiffColor\" value=\"% % % 1\" />\n" (material.diffuse.r/255.0) (material.diffuse.g/255.0) (material.diffuse.b/255.0) to:outFile ;
+		format "    <parameter name=\"MatSpecColor\" value=\"% % % %\" />\n" (material.specular.r/255.0) (material.specular.g/255.0) (material.specular.b/255.0) (material.specularLevel*100.0) to:outFile ;
+		if ((material.diffusemap != undefined) and (iskindof material.diffusemap Bitmaptexture)) then
+		(	
+			t = filterstring material.diffusemap.filename "\\" ;
+			format "    <texture unit=\"diffuse\" name=\"textures%\" />\n" t[arrayLength t] to:outFile ;
+		)
+		format "</material>\n" to:outFile ;
+		close outFile ;
+	)
+)
+
+function exportOgreMaterial material outName =
+(
+    local outFile ;
+	if (classof material == OgreMaterial or classof material == ogreMaterial) then
+	(
+	    --outFile = createfile outName ;
+		writeOgreMaterial material outname;
+		--close outFile ;
+	)
+	else
+	(
+		messageBox "You have to choose an OgreMaterial." ;
+	)
+)
+
+---------------------------------------------------------------
+-- Writes a standard material in a material script
+-- prefix will be before material name. (prefix+name)
+-- used for multi material
+---------------------------------------------------------------
+function writeStandardMaterial material outname =
+(
+	local outFile ;
+	local t, matname, mat, ext, fname;
+	
+	matname = replaceSpaces material.name ;
+	ext = ".mat.xml" ;
+	fname = outname + "_" + matname + ext ;
+	outFile = createfile fname;
+	format "<material>\n" to:outFile ;
+	format "    <technique name=\"Techniques/Diff.xml\" />\n" to:outFile ;
+	format "    <parameter name=\"MatDiffColor\" value=\"% % % 1\" />\n" (material.diffuse.r/255.0) (material.diffuse.g/255.0) (material.diffuse.b/255.0) to:outFile ;
+	format "    <parameter name=\"MatSpecColor\" value=\"% % % %\" />\n" (material.specular.r/255.0) (material.specular.g/255.0) (material.specular.b/255.0) (material.specularLevel*100.0) to:outFile ;
+	if ((material.diffusemap != undefined) and (iskindof material.diffusemap Bitmaptexture)) then
+	(	
+		t = filterstring material.diffusemap.filename "\\" ;
+		format "    <texture unit=\"diffuse\" name=\"textures/%\" />\n" t[arrayLength t] to:outFile ;
+	)
+	format "</material>\n" to:outFile ;
+	close outFile ;
+)
+
+---------------------------------------------------------------
+-- Exports a standard material
+-- PRE: material is a standard material
+-- Use exportMaterial function.
+---------------------------------------------------------------
+function exportStandardMaterial material outname =
+(
+	local sub,i,matname ;
+	matname = replaceSpaces material.name ;
+	writeStandardMaterial material outname ;
+)
+
+
+---------------------------------------------------------------
+-- Exports a multi material
+-- PRE: material is a multi material
+-- Use exportMaterial function.
+---------------------------------------------------------------
+function exportMultiMaterial material outname=
+(
+	local sub,i,matname ;
+
+	matname = replaceSpaces material.name ;
+	
+	i=0 ;
+	for sub in material.materiallist do 
+	(
+		i+=1 ;
+		writeStandardMaterial sub outname ;
+		--format "\n" to:outFile ;
+	)
+)
+
+
+
+---------------------------------------------------------------
+-- Exports a material
+-- call the fonction linked to the material type
+---------------------------------------------------------------
+function exportMaterial material outname =
+(
+	local outFile ;
+		
+	if (material!=undefined and classof material == MultiMaterial) then
+	(
+		exportMultiMaterial material outname;
+	)
+	else 
+	(
+		if (material!=undefined and classof material == StandardMaterial) then
+		(
+			exportStandardMaterial material outname;
+		)
+		else
+		(
+			messageBox "You have to choose a Standard Material or a MultiMaterial." ;
+			return 0;
+		)
+	)
+	--messageBox "Exporting material successful !" ;
+)
+
+
+---------------------------------------------------------------
+-- Exports all the materials used in the scene
+---------------------------------------------------------------
+function exportAllSceneMaterials outname =
+(
+	for material in sceneMaterials do
+	(			
+		if (material!=undefined and classof material == MultiMaterial) then
+			exportMultiMaterial material outname;
+		else (
+			if (material!=undefined and classof material == StandardMaterial) then
+				exportStandardMaterial material outname;
+		)
+		format "\n" to:outFile ;
+	)		
+	-- messageBox "Exporting all materials in the scene successful !" ;
+)
+

+ 648 - 0
Extras/OgreMaxscriptExport/ogre/macros/ogreToolbar.mcr

@@ -0,0 +1,648 @@
+-- ogreToolbar.mcr
+--
+-- Banania - 2004
+--
+-- Macroscript for the Ogre Toolbar 2.0
+--
+-- Thanks to John Martin and Etienne Mallard for the work they did in the previous versions
+--
+
+-- library functions
+include "ogre/lib/ogreSkeletonLib.ms"
+include "ogre/lib/ogreMaterialLib.ms"
+include "ogre/lib/ogreMaterialPlugin.ms"
+
+macroScript showOgreExportTools
+	category:"Ogre Tools"
+	internalCategory:"Ogre Tools"
+	buttonText:"Ogre Exporter"
+	tooltip:"Ogre Exporter"
+	Icon:#("Maintoolbar",49)
+(
+	-- create a floater
+	OgreExportFloater = newRolloutFloater "Ogre Exporter - 1.19" 280 800 ;
+	
+	rollout OgreExportOptions "Options" width:270 height:140 rolledUp:true
+	(
+		button openScript "open OgreScript.ini" pos:[7,8] width:116 height:26 toolTip:"open the script to edit your settings"
+		checkbox CBconvertXML "convert XML file after export" pos:[8,42] width:255 height:22 checked:true
+		label lbl01 "XML Converter Program" pos:[8,64] width:256 height:18
+		editText editXMLconverter "" pos:[7,80] width:253 height:23
+		button browseXMLconverter "Browse" pos:[188,106] width:72 height:25 toolTip:"choose your XML converter"
+		groupBox grp1 "Conversion settings" pos:[8,132] width:254 height:125
+		checkbox CBgenerateedges "Generate Edges List (for stencil shadows)" pos:[12,145] width:245 height:20 checked:true
+		checkbox CBgeneratetangents "Generate Tangent Vectors (for normal mapping)" pos:[12,163] width:245 height:20 checked:false
+		checkbox CBgenerateLOD "Generate LOD" pos:[12,181] width:245 height:20 checked:false
+		label lbl02 "LOD levels" pos:[30,200] width:156 height:18
+		label lbl03 "LOD distance" pos:[30,218] width:156 height:18
+		label lbl04 "LOD reduction (%)" pos:[30,236] width:156 height:18
+		spinner SPlodlevels "" pos:[120,200] width:114 height:16 range:[1,20,1] type:#integer scale:1 enabled:false
+		spinner SPloddistance "" pos:[120,218] width:114 height:16 range:[0.0,100000.0,10.0] type:#float scale:1.0 enabled:false
+		spinner SPlodreduction "" pos:[120,236] width:114 height:16 range:[0.0,100.0,20.0] type:#float scale:5.0 enabled:false
+
+		on CBgenerateLOD changed state do
+		(
+			SPlodlevels.enabled = state;
+			SPloddistance.enabled = state;
+			SPlodreduction.enabled = state;
+		)
+		on openScript pressed  do
+		(
+			shellLaunch ((getDir #scripts) + "\\ogre\\ogreScript.ini") "" ;
+		)
+		on CBconvertXML changed state do
+		(
+			if (state and (editXMLconverter.text!="")) then
+			(
+				if (not (doesFileExist editXMLconverter.text)) then
+				(
+					editXMLconverter.text = "The file/directory specified in the .ini for the XML converter does not exist !";
+					CBconvertXML.checked = false;
+				)
+			)
+		)
+		on browseXMLconverter pressed  do
+		(
+			filename = getOpenFileName types:"Executables(*.exe)|*.exe|" ;			
+			if (filename != undefined) then
+			(
+				editXMLconverter.text = filename ;
+				CBconvertXML.checked = true;
+				if (not (doesFileExist editXMLconverter.text)) then
+				(
+					editXMLconverter.text = "The file/directory specified in the .ini for the XML converter does not exist !";
+					CBconvertXML.checked = false;
+				)
+				else
+				(
+					xmlConvPath = getFilenamePath editXMLconverter.text;
+					xmlexe = getFilenameFile editXMLconverter.text;
+					setINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Directories" "XMLConverterPath" xmlConvPath;
+					setINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Exe" "XMLConverterExe" xmlexe;
+				)
+			)
+		)
+		on OgreExportOptions open  do
+		(
+			runXMLConverter = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Tools" "runXMLConverter"
+			if (runXMLConverter=="yes") then
+			(
+				CBconvertXML.enabled = true;
+				CBconvertXML.checked = true;
+			)
+			else
+			(
+				CBconvertXML.enabled = false;
+			)
+			
+			xmlConvPath = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Directories" "XMLConverterPath"
+			xmlexe = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Exe" "XMLConverterExe"
+			
+			ext = substring xmlexe (xmlexe.count-4) 4;
+			if ( (ext[2]!="e" and ext[2]!="E") or (ext[3]!="x" and ext[3]!="X") or (ext[4]!="e" and ext[4]!="E") ) then
+				editXMLconverter.text = xmlConvPath + "\\" + xmlexe + ".exe";
+			else
+				editXMLconverter.text = xmlConvPath + "\\" + xmlexe;
+
+			print editXMLconverter.text;
+			if (not (doesFileExist editXMLconverter.text)) then
+			(
+				editXMLconverter.text = "The file/directory specified in the .ini for the XML converter does not exist !";
+				CBconvertXML.checked = false;
+			)
+			else
+			(
+				CBconvertXML.checked = true;
+			)
+		)
+		on OgreExportOptions close  do
+		(
+		
+		)		
+	)
+
+	rollout OgreExportObject "Object" width:270 height:259
+	(
+		label lbl01 "Selected Object :" pos:[6,35] width:260 height:17
+		editText editObject "" pos:[0,51] width:263 height:20
+		button selectMesh "Select a mesh..." pos:[115,2] width:152 height:28 toolTip:"select the mesh you want to export"
+		spinner SPscale "Scale" pos:[149,75] width:114 height:16 range:[0.0,1000.0,1.0] type:#float scale:0.1
+		checkbox CBFlipYZ "Flip Y/Z" pos:[203,96] width:60 height:19 checked:true
+		groupBox grp1 "Output" pos:[8,112] width:254 height:75
+		label lbl03 "File name" pos:[17,125] width:238 height:17
+		editText editFilename "" pos:[11,140] width:242 height:22
+		button chooseFilename "Browse" pos:[170,164] width:82 height:20 toolTip:"chooose the name of your output files"
+		button export "Export !" pos:[29,191] width:214 height:40 toolTip:"export your mesh, skeleton ......"
+		progressBar exportProgress pos:[9,235] width:250 height:10
+		
+		on OgreExportObject open  do
+		(
+			if (maxOps.productAppID == #max) then	
+				g_MAX = false;
+			else
+				g_MAX = true;
+			
+			lastFile = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastFile"
+			lastAnimName = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastAnimName"
+			lastfirstFrame = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastFrameStart"
+			lastlastFrame = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastFrameEnd"
+			lastAnimLength = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastAnimLength"
+			lastScale = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastScale"
+			
+			editFilename.text = lastFile ;
+			OgreExportMesh.CBexportUV.checked = true ;
+			CBFlipYZ.checked = true ;
+			SPscale.value = lastScale as Float;
+			if (SPscale.value == 0.0) then
+				SPscale.value = 1.0;
+
+			select = getCurrentSelection() ;
+			if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+			(
+				editObject.text = select[1].name ;
+				OgreExportMesh.CBexportMesh.enabled = true;
+				OgreExportMesh.CBexportMesh.checked = true;
+				OgreExportMesh.CBflipnormals.enabled = true ;
+				OgreExportMesh.CBexportColor.enabled = true ;
+				OgreExportMesh.CBexportUV.enabled = true ;
+				OgreExportMesh.CBexportUV.checked = true ;
+				OgreExportMesh.SPchannels.enabled = true ;
+				OgreExportAnimation.CBexportSkeleton.enabled = (getSkin(select[1]) != undefined) or (getPhysique(select[1]) != undefined) ;
+			)
+		)
+		on OgreExportObject close  do
+		(
+		
+		)
+		on selectMesh pressed do
+		(
+			max tool hlist ;
+			select = getCurrentSelection() ;
+			if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+			(
+				editObject.text = select[1].name ;
+			
+				-- options accessible ou non
+				OgreExportMesh.CBexportMesh.enabled = true;
+				OgreExportMesh.CBexportMesh.checked = true;
+				OgreExportMesh.CBflipnormals.enabled = true ;
+				OgreExportMesh.CBexportColor.enabled = true ;
+				OgreExportMesh.CBexportUV.enabled = true ;
+				OgreExportMesh.CBexportUV.checked = true ;
+				OgreExportMesh.SPchannels.enabled = true ;
+				OgreExportAnimation.CBexportSkeleton.enabled = (getSkin(select[1]) != undefined) or (getPhysique(select[1]) != undefined) ;
+			)
+		)
+		on chooseFilename pressed  do
+		(
+			filename = getSaveFileName types:"All Files(*.*)|*.*|" ;			
+			if (filename != undefined) then
+			(
+				editFilename.text = filename ;
+			)
+		)
+		
+		-------------------------------------
+		-- EXPORT ---------------------------
+		-------------------------------------
+		on export pressed  do
+		(
+			sliderTime = 0 ;
+			m = getNodeByName editObject.text ;
+			if (editFilename.text == "" or m == undefined ) then
+			(
+				messageBox "You have to choose a filename and a valid object." ;
+			)
+			else
+			(
+				clearlistener() ;
+				
+				if (SPscale.value == 0.0) then
+					SPscale.value = 1.0;
+				
+				Options = exportOptions scale:SPscale.value flipYZ:false flipNormal:false exportColours:false exportUV:false ;
+				exportingMeshDone = false ;
+				exportingSkelDone = false ;
+				-- sets options
+				---------------
+				if (CBFlipYZ.checked) then
+					Options.flipYZ= true ;					
+				if (OgreExportMesh.CBflipnormals.checked and OgreExportMesh.CBflipnormals.enabled) then
+					Options.flipNormal = true ;
+				if (OgreExportMesh.CBexportColor.checked and OgreExportMesh.CBexportColor.enabled) then
+					Options.exportColours = true ;
+				if (OgreExportMesh.CBexportUV.checked and OgreExportMesh.CBexportUV.enabled) then
+				(
+					Options.exportUV = true ;	
+					Options.UVchannels = OgreExportMesh.SPchannels.value;	
+				)
+				Options.exportHelpers = (OgreExportAnimation.CBexporthelpers.enabled and OgreExportAnimation.CBexporthelpers.checked);
+				
+				if (not g_MAX) then
+					setINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastScale" (SPscale.value as string)
+
+				-- exports mesh
+				---------------
+				if (OgreExportMesh.CBexportMesh.enabled and OgreExportMesh.CBexportMesh.checked) then
+				(
+					if (not g_MAX) then
+						setINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Settings" "lastFile" editFilename.text	
+					--progressStart "Mesh Export" ;
+					exportingMeshDone = writeMesh m Options (editFilename.text) ;
+					--progressStop ;
+				)
+			
+				-- exports skeleton
+				-------------------
+				if (OgreExportAnimation.CBexportSkeleton.enabled and OgreExportAnimation.CBexportSkeleton.checked) then
+				(
+					Options.sampleRate = OgreExportAnimation.SPsamplerate.value ;
+					Options.ikSampleRate = OgreExportAnimation.SPiksamplerate.value ;
+				
+					exportingSkelDone =	writeSkeleton m Options Anims editFilename.text ;
+				)
+				
+				-- exports material
+				if (OgreExportMaterial.CBexportmaterial.enabled and OgreExportMaterial.CBexportmaterial.checked) then
+				(
+					select = getCurrentSelection() ;
+					if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+					(
+						mat = select[1].material
+						if (exportSomeMaterial!=undefined) then		-- export with Octopus functions
+						(
+							local outFile ;
+							if (not g_MAX) then
+							(
+								outFile = createfile (editFilename.text + ".material");
+							)
+							else
+							(
+								ClearListener();
+								if (g_MAX_use_listener) then
+									format("<ogrestartfile>%</ogrestartfile><ogrestartdata>\n") (editFilename.text + ".material");
+								outFile = listener;
+							)
+
+							exportSomeMaterial mat "" outFile;
+
+							if (not g_MAX) then
+							(
+							    close outFile ;
+							)
+							else
+							(
+								if (g_MAX_use_listener) then
+									format("</ogrestartdata>\n") to: outFile;
+							)
+						)
+						else
+						(
+							exportMaterial mat (editFilename.text);
+						)
+					)
+				)
+				
+				-- post traitement
+				------------------
+				if (exportingMeshDone or exportingSkelDone) then 
+				(					
+					if (OgreExportOptions.CBconvertXML.checked) then
+					(
+						if (not g_MAX) then
+						(
+							OptionConv = "";
+							mediaPath = getINISetting ((getDir #scripts) + "\\ogre\\ogreScript.ini") "Directories" "MediaPath"
+					
+							if (exportingMeshDone) then 
+							(
+								if (OgreExportOptions.CBgenerateedges.enabled==false or OgreExportOptions.CBgenerateedges.checked==false) then
+									OptionConv = OptionConv + " -e";
+								if (OgreExportOptions.CBgeneratetangents.enabled and OgreExportOptions.CBgeneratetangents.checked) then
+								(
+									if (OgreExportMesh.CBexportUV.enabled and OgreExportMesh.CBexportUV.checked and (OgreExportMesh.SPchannels.value>0)) then
+										OptionConv = OptionConv + " -t";
+									else
+										messageBox "Tangent vectors cannot be generated without UV sets. Generation will be deactivated." ;
+								)
+								if (OgreExportOptions.CBgeneratelod.enabled and OgreExportOptions.CBgeneratelod.checked) then
+									OptionConv = OptionConv + " -l " + (OgreExportOptions.SPlodlevels.value as string) + " -d " + (OgreExportOptions.SPloddistance.value as string) + " -p " + (OgreExportOptions.SPlodreduction.value as string);
+								
+								DOSCommand (OgreExportOptions.editXMLconverter.text + OptionConv + " \"" + editFilename.text + ".mesh.xml\" \"" + editFilename.text + ".mesh\"") ;
+								DOSCommand ("copy \"" + editFilename.text + ".mesh\" \"" + mediaPath + "\"") ;
+							)
+							if (exportingSkelDone) then 
+							(
+								DOSCommand (OgreExportOptions.editXMLconverter.text + " \"" + editFilename.text + ".skeleton.xml\" \"" + editFilename.text + ".skeleton\"") ;
+								DOSCommand ("copy \"" + editFilename.text + ".skeleton\" \"" + mediaPath + "\"") ;						
+							)
+							messageBox "OgreXMLConverter has been run and files copied to the media directory."
+							print (OgreExportOptions.editXMLconverter.text + OptionConv + " \"" + editFilename.text + ".mesh.xml\" \"" + editFilename.text + ".mesh\"") ;
+						)
+						else
+						(
+							messageBox "gmax cannot launch XMLConverter due to its limitations..."
+						)
+					)
+				)
+			)
+		)
+	)
+	
+	rollout OgreExportMesh "Mesh" width:270 height:120
+	(
+		checkbox CBexportMesh "Export Mesh" pos:[5,5] width:150 height:17
+		groupBox grp2 "Export settings" pos:[4,25] width:262 height:102
+		checkbox CBflipnormals "Flip normals" pos:[14,42] width:140 height:19
+		checkbox CBexportUV "Export UV sets" pos:[14,64] width:140 height:20
+		spinner SPchannels "# UV channels" pos:[36,85] width:173 height:16 range:[1,8,1] type:#integer scale:1
+		checkbox CBexportColor "Export Vertex Color" pos:[13,105] width:140 height:19
+		on OgreExportMesh open  do
+		(
+			OgreExportMesh.CBexportMesh.enabled = false;
+			OgreExportMesh.CBexportMesh.checked = true;
+			OgreExportMesh.CBflipnormals.enabled = false ;
+			OgreExportMesh.CBexportColor.enabled = false ;
+			OgreExportMesh.CBexportUV.enabled = false ;
+			OgreExportMesh.CBexportUV.checked = true ;
+			OgreExportMesh.SPchannels.enabled = false ;
+
+			select = getCurrentSelection() ;
+			if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+			(
+				OgreExportMesh.CBexportMesh.enabled = true;
+				OgreExportMesh.CBexportMesh.checked = true;
+				OgreExportMesh.CBflipnormals.enabled = true ;
+				OgreExportMesh.CBexportColor.enabled = true ;
+				OgreExportMesh.CBexportUV.enabled = true ;
+				if (classof select[1] == Editable_Mesh) then
+				(
+					if (getNumTVerts select[1] == 0) then
+						OgreExportMesh.CBexportUV.checked = false ;
+					else
+						OgreExportMesh.CBexportUV.checked = true ;
+				)
+				else
+				(
+					OgreExportMesh.CBexportUV.checked = false ;
+				)
+				OgreExportMesh.SPchannels.enabled = true ;
+			)
+		)
+		on OgreExportMesh close  do
+		(
+		
+		)
+		on CBexportMesh changed state do
+		(
+			OgreExportMesh.CBflipnormals.enabled = state ;
+			OgreExportMesh.CBexportColor.enabled = state ;
+			OgreExportMesh.CBexportUV.enabled = state ;
+			OgreExportMesh.SPchannels.enabled = state ;
+		)
+		on CBexportUV changed state do
+		(
+			SPchannels.enabled = state ;
+		)
+		on SPchannels changed state do
+		(
+			name = OgreExportObject.editObject.text;
+			replaceSpaces name;
+			d = getNodeByName name ;
+			num_channels = (meshOp.getNumMaps d) - 1;
+			if (state > num_channels) then
+				SPchannels.value = num_channels;
+		)
+	)
+	
+	rollout OgreExportAnimation "Animation" width:270 height:347 rolledUp:true
+	(
+		checkbox CBexportSkeleton "Export Skeleton" pos:[5,4] width:150 height:19 enabled:false
+		groupBox grp3 "Export settings" pos:[4,26] width:262 height:76
+		--checkbox CBbiped "Biped Export" pos:[13,44] width:246 height:19 enabled:false
+		spinner SPsamplerate "Sample Rate" pos:[11,45] width:186 height:16 enabled:false range:[0.0,10000,0.0] type:#float scale:0.5 
+		spinner SPiksamplerate "IK Sample Rate" pos:[17,63] width:180 height:16 enabled:false range:[0.0,10000,5.0] type:#float scale:0.5 
+		checkbox CBexporthelpers "Export non-bones objects (must be in the modifier)" pos:[11,81] width:254 height:16 enabled:false checked:false
+		groupBox grp4 "Animation settings" pos:[4,106] width:261 height:252
+		button getFromModifier "<-" pos:[14,124] width:61 height:21 enabled:false toolTip:"retrieve information from OctopusExport Modifier"
+		label lbl4 "Get settings from Octopus modifier" pos:[86,128] width:167 height:14 enabled:true
+		groupBox grp5 "Animations" pos:[8,149] width:253 height:203
+		button addAnimation "Add" pos:[16,164] width:80 height:22 enabled:false toolTip:"add an animation to the list"
+		button deleteAnimation "Delete" pos:[172,165] width:80 height:22 enabled:false toolTip:"remove an animation from the list"
+		comboBox ListAnimations "" pos:[17,191] width:235 height:6 enabled:false
+		label lbl5 "Frames" pos:[17,290] width:236 height:16
+		spinner SPframestart "from" pos:[25,307] width:106 height:16 enabled:false range:[0,10000,0] type:#integer scale:1
+		spinner SPframeend "to" pos:[154,307] width:96 height:16 enabled:false range:[0,10000,100] type:#integer scale:1
+		label lbl6 "Length (in seconds)" pos:[18,330] width:115 height:16
+		spinner SPanimlength "" pos:[120,329] width:130 height:16 range:[0,10000,10] type:#float scale:0.1
+		on OgreExportAnimation open  do
+		(
+			OgreExportAnimation.CBexportSkeleton.enabled = false;
+			--OgreExportAnimation.CBbiped.enabled = false;
+			--OgreExportAnimation.CBbiped.checked = false;
+			OgreExportAnimation.getFromModifier.enabled = false;
+			OgreExportAnimation.SPsamplerate.enabled = false;
+			OgreExportAnimation.SPiksamplerate.enabled = false;
+			OgreExportAnimation.CBexporthelpers.enabled = false;
+			OgreExportAnimation.addAnimation.enabled = false;
+			OgreExportAnimation.deleteAnimation.enabled = false;
+			OgreExportAnimation.ListAnimations.enabled = false;
+			OgreExportAnimation.SPframestart.enabled = false;
+			OgreExportAnimation.SPframeend.enabled = false;
+			OgreExportAnimation.SPanimlength.enabled = false;
+
+			select = getCurrentSelection() ;
+			if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+			(
+				OgreExportAnimation.CBexportSkeleton.enabled = (getSkin(select[1]) != undefined) or (getPhysique(select[1]) != undefined) ;
+				if (OgreExportAnimation.CBexportSkeleton.enabled and OgreExportAnimation.CBexportSkeleton.checked) then
+				(
+					--OgreExportAnimation.CBbiped.enabled = true;
+					--OgreExportAnimation.CBbiped.checked = (getPhysique(select[1]) != undefined);
+					OgreExportAnimation.getFromModifier.enabled = true;
+					OgreExportAnimation.SPsamplerate.enabled = true;
+					OgreExportAnimation.SPiksamplerate.enabled = true;
+					OgreExportAnimation.CBexporthelpers.enabled = true;
+					OgreExportAnimation.addAnimation.enabled = true;
+					OgreExportAnimation.deleteAnimation.enabled = true;
+					OgreExportAnimation.ListAnimations.enabled = true;
+					OgreExportAnimation.SPframestart.enabled = true;
+					OgreExportAnimation.SPframeend.enabled = true;
+					OgreExportAnimation.SPanimlength.enabled = true;
+				)
+			)
+			
+			Anims = exportAnims names:#() startframes:#() endframes:#() lengths:#();
+		)
+		--on CBbiped changed state do
+		--(
+		--)
+		on CBexportSkeleton changed state do
+		(
+			select = getCurrentSelection() ;
+			if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+			(
+				--OgreExportAnimation.CBbiped.enabled = state;
+				--OgreExportAnimation.CBbiped.checked = (getPhysique(select[1]) != undefined);
+				OgreExportAnimation.getFromModifier.enabled = state;
+				OgreExportAnimation.SPsamplerate.enabled = state;
+				OgreExportAnimation.SPiksamplerate.enabled = state;
+				OgreExportAnimation.CBexporthelpers.enabled = state;
+				OgreExportAnimation.addAnimation.enabled = state;
+				OgreExportAnimation.deleteAnimation.enabled = state;
+				OgreExportAnimation.ListAnimations.enabled = state;
+				OgreExportAnimation.SPframestart.enabled = state;
+				OgreExportAnimation.SPframeend.enabled = state;
+				OgreExportAnimation.SPanimlength.enabled = state;
+			)
+		)
+		on OgreExportAnimation close  do
+		(
+		
+		)
+		on getFromModifier pressed  do
+		(
+			select = getCurrentSelection()
+			if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+			(
+				max modify mode ;
+				--if (select[1].modifiers["OctopusExport"]!=undefined) then
+				--	modPanel.setCurrentObject select[1].modifiers["OctopusExport"] ;
+				octexp = getOctopusExport select[1];
+				
+				if (octexp!=undefined) then
+				(
+					local array=#();
+					for n in octexp.anames do
+						append array n;
+					
+					ListAnimations.items = array;
+					ListAnimations.selection = 1;
+
+					Anims.names = array;
+					
+					array=#();
+					for n in octexp.afframes do
+						append Anims.startframes n;
+
+					array=#();
+					for n in octexp.alframes do
+						append Anims.endframes n;
+					
+					array=#();
+					for n in octexp.artimes do
+						append Anims.lengths n;
+
+					if (ListAnimations.items.count!=0) then
+					(
+						SPframestart.value = Anims.startframes[1]
+						SPframeend.value = Anims.endframes[1]
+						SPanimlength.value = Anims.lengths[1]
+					)
+				)
+			)
+		)
+		on addAnimation pressed  do
+		(
+			if (ListAnimations.text != "") then
+			(
+				local array=#();
+				for n in ListAnimations.items do
+					append array n;
+				append array ListAnimations.text;
+				ListAnimations.items = array;
+				append Anims.names ListAnimations.items[ListAnimations.items.count];
+				append Anims.startframes SPframestart.value;
+				append Anims.endframes SPframeend.value;
+				append Anims.lengths SPanimlength.value;
+				ListAnimations.selection = 0;
+			)		
+		)
+		on deleteAnimation pressed  do
+		(
+			if ((ListAnimations.items.count > 0) and (ListAnimations.selection > 0)) then
+			(
+				local array=#();
+				ind = ListAnimations.selection
+				deleteItem Anims.names ind
+				deleteItem Anims.startframes ind
+				deleteItem Anims.endframes ind
+				deleteItem Anims.lengths ind
+				for n in Anims.names do
+					append array n;
+				ListAnimations.items = array;
+				ListAnimations.selection = 1;
+			)
+		)
+		on ListAnimations selected num  do
+		(
+			if (ListAnimations.items.count >= num) then
+			(
+				SPframestart.value = Anims.startframes[num]
+				SPframeend.value = Anims.endframes[num]
+				SPanimlength.value = Anims.lengths[num]
+			)		
+		)
+		on SPframestart changed val do
+		(
+			if ((ListAnimations.items.count > 0) and (ListAnimations.selection > 0)) then
+			(
+				if (ListAnimations.text == Anims.names[ListAnimations.selection]) then
+					Anims.startframes[ListAnimations.selection] = SPframestart.value;
+			)		
+		)
+		on SPframeend changed val do
+		(
+			if ((ListAnimations.items.count > 0) and (ListAnimations.selection > 0)) then
+			(
+				if (ListAnimations.text == Anims.names[ListAnimations.selection]) then
+					Anims.endframes[ListAnimations.selection] = SPframeend.value;
+			)		
+		)
+		on SPanimlength changed val do
+		(
+			if ((ListAnimations.items.count > 0) and (ListAnimations.selection > 0)) then
+			(
+				if (ListAnimations.text == Anims.names[ListAnimations.selection]) then
+					Anims.lengths[ListAnimations.selection] = SPanimlength.value;
+			)		
+		)
+	)
+	
+	rollout OgreExportMaterial "Material" width:270 height:33
+	(
+		checkbox CBexportmaterial "Export Material" pos:[5,7] width:260 height:17 enabled:false checked:true
+		on OgreExportMaterial open  do
+		(
+			CBexportmaterial.enabled = false;
+
+			select = getCurrentSelection() ;
+			if ((select[1]!=undefined) and (iskindof select[1] GeometryClass)) then
+			(
+				CBexportmaterial.enabled = true;
+			)		
+		)
+		on OgreExportMaterial close  do
+		(
+		
+		)
+	)
+	
+	rollout OgreExportAbout "About" rolledUp:true
+	(
+		label label11 "For use with the Ogre graphics engine." align:#center;
+		label label12 "See website for details: http://ogre.sourceforge.net" align:#center;
+		label label13 "This software is distributed under the terms of the LGPL." align:#center ;
+		label label16 "Based on the exporter realised by" align:#center ;
+		label label14 "EarthquakeProof - [email protected]  (summer 2003)" align:#center ;
+		label label15 "by Banania - November 2004" align:#center ;
+	)
+	
+	-- add the rollout, which contains the dialog	
+	addRollout OgreExportOptions OgreExportFloater ;
+	addRollout OgreExportObject OgreExportFloater ;
+	addRollout OgreExportMesh OgreExportFloater ;
+	addRollout OgreExportAnimation OgreExportFloater ;
+	addRollout OgreExportMaterial OgreExportFloater ;
+	addRollout OgreExportAbout OgreExportFloater ;
+)

BIN
Extras/OgreMaxscriptExport/ogre/ogreScript.ini


+ 7 - 0
Extras/Readme.txt

@@ -3,3 +3,10 @@ OgreBatchConverter
 - Contributed by Carlo Carollo. Converts multiple Ogre .mesh.xml files (also from 
   subdirectories) by invoking the OgreImporter tool.
 - Enable in build by uncommenting at the bottom of the Urho3D root CMakeLists.txt
+
+OgreMaxscriptExport
+
+- Contributed by Vladimir Pobedinskij. A modified version of the Maxscript
+  Exporter from the Ogre SDK that will import Ogre .mesh.xml files (for feeding
+  into OgreImporter) and materials in Urho3D .xml format.
+

+ 1 - 0
Readme.txt

@@ -15,6 +15,7 @@ Urho3D development, contributions and bugfixes by:
 - Carlo Carollo
 - Alex Fuller
 - Jason Kinzer
+- Vladimir Pobedinskij
 - Miika Santala
 - Wei Tjong Yao
 - Magic.Lixin