1include "ogre/lib/ogreSkeletonLib_usefulfns.ms"
2
3-----------------------------------------------------------------------------
4-- gets  keyframes from the controllers animation
5-----------------------------------------------------------------------------
6function getTimeList obj firstframe lastframe samplerate IKsamplerate =
7(
8	local list,rotContr,posContr,e  ;
9	list = #(firstframe) ;
10	list2 = #() ; -- this is the list which will be returned.
11
12	-- Biped Bones and the root: Bip01 for example don't have the same controller
13
14	-- Root : Bip01
15	if (isPelvis obj) then (
16		-- vertical controller
17		for e in obj.controller.vertical.controller.keys do (
18			t =e.time ;
19			if (t>firstFrame and t<=lastFrame) then (
20				append list t ;
21				--print t ;
22			)
23		)
24		-- horizontal controller
25		for e in obj.controller.horizontal.controller.keys do (
26			t =e.time ;
27			if (t>firstFrame and t<=lastFrame) then
28				append list t ;
29		)
30		-- turn controller
31		for e in obj.controller.turning.controller.keys do (
32			t =e.time ;
33			if (t>firstFrame and t<=lastFrame) then
34				append list t ;
35		)
36		sort list ;
37	)
38	-- Biped Bones
39	else if (isKindOf obj Biped_Object) then (
40
41		for e in obj.controller.keys do (
42			t =e.time ;
43			if (t>firstFrame and t<=lastFrame) then
44				append list t ;
45		)
46	)
47	-- Standard Bones
48	else
49	(
50		--print obj.name;
51		--print (classof obj.controller) ;
52		if (classof obj.controller == prs) then		-- standard controller
53		(
54			rotContr = obj.rotation.controller ;
55			posContr = obj.pos.controller ;
56
57			for e in rotContr.keys do
58			(
59				t = e.time ;
60				if (t>firstFrame and t<=lastFrame) then
61					append list t ;
62			)
63			for e in posContr.keys do
64			(
65				t = e.time ;
66				if (t>firstFrame and t<=lastFrame) then
67					append list t ;
68			)
69		)
70		else if ((classof obj.controller == IK_ControllerMatrix3Controller) or (classof obj.controller == IKControl)) then		-- IK controller
71		(
72			local IKSR = IKsamplerate;
73
74			if (IKSR == 0.0) then
75				IKSR = 1.0;
76
77			i=firstFrame as Float;
78			while (i<=lastFrame) do
79			(
80				append list (i as Float);
81				i = i + IKSR;
82			)
83		)
84	)
85
86	append list (firstFrame as Float);		-- add a keyframe on the first frame
87	append list (lastFrame as Float);		-- add a keyframe on then last frame
88
89	if (samplerate > 0) then				-- sample the animation by adding keyframes on regular intervals
90	(
91		i=firstFrame as Float;
92		while (i<=lastFrame) do
93		(
94			append list (i as Float);
95			i = i + samplerate;
96		)
97	)
98	sort list ;
99
100	-- if several keyframes have the same value, we keep just one
101	keepLoneValues list list2 ;
102	list2 ;
103)
104
105
106-----------------------------------------------------------------------------
107-- write <track />
108-- Selected keys belongs to [firstframe,lastFrame]
109-- time = (key.time - firstFrame)*length/(lastFrame-firstFrame)
110--   (e.g. first key has time 0.)
111-----------------------------------------------------------------------------
112function writeTrack bone_name boneId firstframe lastframe samplerate IKsamplerate length scale flipYZ outFile=
113(
114	local angle,timef,i,bname,d,mref,mparent ;
115
116	-- displays information in the maxscript listener
117	if (not g_MAX) then
118		format "retrieving key information for % ...\n" (bone_name) ;
119
120	-- gets bone acording to the parameter boneId
121	bname = bone_name ;
122	replaceSpaces bname ;
123	d = getNodeByName bname ;
124
125	-- gets keyframe list
126	timelist = getTimeList d firstframe lastframe samplerate IKsamplerate;
127
128	-- track header
129	format("\t\t\t\t<track bone = \"%\">\n") bname to:outFile ;
130	format("\t\t\t\t\t<keyframes>\n") to:outFile ;
131
132	-- gets initial transform at frame 0f
133	at time 0f (
134		initTform = d.transform ;
135		if (not isRootUniversal2 d) then (
136			mparent = d.parent.transform ;
137			initTform = initTform*inverse(mparent) ;
138		)
139		else if (flipYZ) then (
140			if (not g_MAX) then
141				format " - flipping root track..." ;
142			-- we add the bip Transform
143			--initTform = initTform * d.controller.rootNode.transform ;
144			initTform = flipYZTransform initTform ;
145		)
146	)
147
148	-- for each frame in the list
149	for i in timelist do
150	(
151		-- moves slider time and compute OGRE time
152		at time i (
153			timef = ((float) (i-firstFrame)*length)/(lastframe - firstframe ) ;
154
155		-- First, rotation which depends on initial transformation
156			Tform = d.transform ;
157			-- if this is the pelvis
158			if (isRootUniversal2 d) then (
159				mparent = matrix3 1 ;
160				-- if flipYZ == true
161				if (flipYZ) then
162					Tform = flipYZTransform Tform ;
163			)
164			else
165				mparent = d.parent.transform ;
166
167			-- computes rotation
168			mref = initTform*mparent ;
169			Tform = Tform*inverse(mref) ;
170
171			-- rotation part is saved.
172			--rot = Tform.rotation as angleaxis ;
173			--angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :((((((
174			rot = toAngleAxis Tform.rotation ;
175			axis = rot.axis;
176			angle = - rot.angle;
177
178			-- Then, position which depends on parent
179			Tform=d.transform ;
180			Tform=Tform*inverse(mparent) ;
181
182			-- if this is the root bone and flipYZ == true
183			if (isRootUniversal2 d and flipYZ) then (
184				Tform = flipYZTransform Tform ;
185			)
186
187			-- substracts position of the initial transform
188			Tform.pos -= initTform.pos ;
189			Tform.pos = Tform.pos * scale ;
190
191			pos = Tform.pos ;
192
193			-- writes them !
194			if (abs(pos.x)<1e-5) then pos.x = 0 ;
195			if (abs(pos.y)<1e-5) then pos.y = 0 ;
196			if (abs(pos.z)<1e-5) then pos.z = 0 ;
197
198			format("\t\t\t\t\t\t<keyframe time=\"%\">\n") timef to: outFile ;
199			format("\t\t\t\t\t\t\t<translate x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to: outFile ;
200			format("\t\t\t\t\t\t\t<rotate angle=\"%\">\n") angle to:outFile ;
201			format("\t\t\t\t\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") (axis.x) (axis.y) (axis.z) to:outFile ;
202			format("\t\t\t\t\t\t\t</rotate>\n") to:outFile ;
203			format("\t\t\t\t\t\t</keyframe>\n") to:outFile ;
204		)
205	)
206
207	-- track end
208	format("\t\t\t\t\t</keyframes>\n") to:outFile ;
209	format("\t\t\t\t</track>\n") to: outFile ;
210)
211
212-------------------------------------------------------------------------------------------------
213------------------------------------------- WRITE SKELETON --------------------------------------
214-------------------------------------------------------------------------------------------------
215
216-------------------------------------
217-- List of bones in the hierarchy
218-------------------------------------
219global BonesList=#()
220global RootsList=#()
221
222-------------------------------------
223-- helper functions to build skeleton
224-------------------------------------
225
226-----------------------------------------------------------------
227-- recursive function to build the list of bones for the skeleton
228-----------------------------------------------------------------
229function computeBList b sk phy exportHelpers =
230(
231	bname = b ;
232	bone = getNodeByName bname ;
233	if (findItem BonesList bname == 0) then
234		if (isKindOf bone BoneGeometry or iskindOf bone Biped_Object or (exportHelpers and (isPartOfModifier bone sk phy)) ) then
235			append BonesList bname ;
236	childrenArray = bone.children ;
237	for i=1 to childrenArray.count do
238	(
239        if (isKindOf bone BoneGeometry or iskindOf bone Biped_Object or (exportHelpers and (isPartOfModifier bone sk phy))) then
240			computeBList (replaceSpaces childrenArray[i].name) sk phy exportHelpers;
241	)
242)
243
244function addHelpersToHierarchy phy sk =
245(
246	if (sk != undefined) then
247	(
248		for i=1 to (skinOps.GetNumberBones sk) do
249		(
250			bname = skinOps.GetBoneName sk i 1 ;
251			replaceSpaces bname ;
252			d = getNodeByName bname ;
253
254			if (iskindof d helper) then
255				append BonesList bname ;
256		)
257	)
258	else if (phy != undefined) then
259	(
260		for i=1 to (physiqueOps.GetBoneCount $) do
261		(
262			bname = (physiqueOps.GetBones $)[i].name;
263			replaceSpaces bname ;
264			d = getNodeByName bname ;
265
266			if (iskindof d helper) then
267				append BonesList bname ;
268		)
269	)
270)
271
272-----------------------------------------------------------------
273-- find the root(s) of the rhierarchy
274-----------------------------------------------------------------
275function getHierarchyRoots phy sk exportHelpers =
276(
277	local rootstab=#();
278
279	if (sk != undefined) then
280	(
281		for i=1 to (skinOps.GetNumberBones sk) do
282		(
283			bname= skinOps.GetBoneName sk i 1 ;
284			replaceSpaces bname ;
285			d = getNodeByName bname ;
286
287			while (d.parent!=undefined and (iskindof d.parent BoneGeometry or iskindOf d.parent Biped_Object or (exportHelpers and (isPartOfModifier d.parent sk phy)) )) do
288			(
289				d = d.parent
290			)
291			trouve = 0;
292			--format("new potential root bone \"%\"\n") (replaceSpaces d.name) ;
293			for j=1 to rootstab.count do
294			(
295				if (rootstab[j]!=undefined and (rootstab[j]==(replaceSpaces d.name)))then
296				(
297					trouve = 1;
298					exit;
299				)
300			)
301			if trouve==0 then
302			(
303				if (iskindof d BoneGeometry or iskindOf d Biped_Object or (exportHelpers and (isPartOfModifier d sk phy)) ) then
304				(
305					if (not g_MAX) then
306						format("new root bone \"%\"\n") (replaceSpaces d.name) ;
307					rootstab[rootstab.count+1] = (replaceSpaces d.name) ;
308				)
309			)
310		)
311	)
312	else if (phy != undefined) then		-- physique modifier
313	(
314		for i=1 to (physiqueOps.GetBoneCount $) do
315		(
316			bname = (physiqueOps.GetBones $)[i].name;
317			replaceSpaces bname ;
318			d = getNodeByName bname ;
319
320			while (d.parent!=undefined and (iskindof d.parent BoneGeometry or iskindOf d.parent Biped_Object  or (exportHelpers and (isPartOfModifier d.parent sk phy)) )) do
321			(
322				d = d.parent
323			)
324			trouve = 0;
325			--format("new potential root bone \"%\"\n") (replaceSpaces d.name) ;
326			for j=1 to rootstab.count do
327			(
328				if (rootstab[j]!=undefined and (rootstab[j]==(replaceSpaces d.name)))then
329				(
330					trouve = 1;
331					exit;
332				)
333			)
334			if trouve==0 then
335			(
336				if (iskindof d BoneGeometry or iskindOf d Biped_Object or (exportHelpers and (isPartOfModifier d sk phy))) then
337				(
338					if (not g_MAX) then
339						format("new root bone \"%\"\n") (replaceSpaces d.name) ;
340					rootstab[rootstab.count+1] = (replaceSpaces d.name) ;
341				)
342			)
343		)
344	)
345
346	rootstab;
347)
348
349-----------------------------------------------------------------
350-- function to build the list of bones for the skeleton
351-----------------------------------------------------------------
352function computeBonesList phy sk exportHelpers =
353(
354	RootsList = getHierarchyRoots phy sk exportHelpers;	-- find the roots of the current hierarchy
355	print RootsList;
356	for b in RootsList do
357	(
358		computeBList b sk phy exportHelpers;
359	)
360
361	-- add the nodes that are parts of the skin (or physique) modifier but are neither biped_object nor standard bones
362	-- only helpers (Point, Dummy) at the moment...
363	-- addHelpersToHierarchy phy sk ;
364)
365
366------------------
367-- write <bones />
368------------------
369function writeB bone_name id scale flipYZ outFile =
370(
371	-- gets bone acording to the parameter boneId
372	bname = bone_name ;
373	replaceSpaces bname;
374	d = getNodeByName bname ;
375
376	-- gets initial transform at frame 0f
377	format("\t\t<bone id=\"%\" name=\"%\">\n") (id-1) bname to:outFile ;
378
379	slidertime = 0f ;
380	Tform = d.transform ;
381	if (not isRootUniversal2 d) then (
382		mparent = d.parent.transform ;
383		Tform = Tform*inverse(mparent) ;
384	)
385
386	Tform.pos = Tform.pos * scale ;
387
388	if ((isRootUniversal2 d) and flipYZ) then (
389		if (not g_MAX) then
390			format "- Flipping root... \n" ;
391		Tform = flipYZTransform Tform ;
392	)
393
394	pos = Tform.pos ;
395	--rot = Tform.rotation as angleaxis ;
396	--angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :((((((
397	rot = toAngleAxis Tform.rotation ;
398	angle = - rot.angle ;
399
400	--		if (abs(pos.x)<1e-5) then pos.x = 0 ;
401	--		if (abs(pos.y)<1e-5) then pos.y = 0 ;
402	--		if (abs(pos.z)<1e-5) then pos.z = 0 ;
403
404	-- Only object.transform was taken into account, but when mirror is applied
405    -- object.scale is modified and become [-1,-1,-1] that's why we do what follows:
406	if ((d.parent != undefined) and (hasproperty d.parent "scale")) then (
407        pos = pos * d.parent.scale ;
408    )
409
410	format("\t\t\t<position x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to:outFile ;
411    format("\t\t\t<rotation angle=\"%\">\n") angle to:outFile ;
412    format("\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") rot.axis.x rot.axis.y rot.axis.z to:outFile ;
413   	format("\t\t\t</rotation>\n") to:outFile ;
414	format("\t\t</bone>\n") to:outFile ;
415)
416
417-----------------------------
418-- write Bones (using writeB)
419-----------------------------
420function writeBones phy sk scale flipYZ exportHelpers outFile =
421(
422	local i ;
423
424	OgreExportObject.exportProgress.value = 0;
425
426	if (BonesList.count == 0) then
427		computeBonesList phy sk exportHelpers;
428
429	format("\t<bones>\n") to:outFile;
430
431	i = 0 ;
432
433	for i=1 to BonesList.count do
434	(
435		OgreExportObject.exportProgress.value = (100.0*i/BonesList.count);
436
437		writeB BonesList[i] i scale flipYZ outFile ;
438	)
439
440	format("\t</bones>\n") to:outFile;
441
442	OgreExportObject.exportProgress.value = 100;
443)
444
445--------------------------
446-- write <bonehierarchy />
447--------------------------
448function writeH b outFile =
449(
450	if (not isRootUniversal2 b) then
451	(
452		p = b.parent ;
453		format("\t\t<boneparent bone=\"%\" parent=\"%\" />\n") (replaceSpaces b.name) (replaceSpaces p.name) to:outFile ;
454	)
455)
456
457function writeHierarchy outFile =
458(
459	OgreExportObject.exportProgress.value = 0;
460
461	local bname,pelvis
462	format("\t<bonehierarchy>\n") to:outFile ;
463
464	for i=1 to BonesList.count do
465	(
466		OgreExportObject.exportProgress.value = (100.0*i/BonesList.count);
467
468		b = getNodeByName BonesList[i] ;
469		writeH b outFile ;
470	)
471
472	format("\t</bonehierarchy>\n") to:outFile ;
473
474	OgreExportObject.exportProgress.value = 100;
475)
476
477-----------------------
478-- write <animations />
479-----------------------
480function writeAnim Anims samplerate IKsamplerate scale flipYZ outFile =
481(
482	local i,n ;
483
484	OgreExportObject.exportProgress.value = 0;
485
486	format("\t<animations>\n") to: outFile ;
487
488	for anm=1 to Anims.names.count do
489	(
490	    format("\t\t<animation name=\"%\" length=\"%\">\n") Anims.names[anm] Anims.lengths[anm] to:outFile ;
491		format("\t\t\t<tracks>\n") to:outFile
492
493		n = BonesList.count ;
494		for i = 1 to n do
495		(
496			OgreExportObject.exportProgress.value = (100.0*i/BonesList.count)/Anims.names.count;
497
498			writeTrack BonesList[i] i Anims.startframes[anm] Anims.endframes[anm] samplerate IKsamplerate Anims.lengths[anm] scale flipYZ outFile ;
499		)
500
501		format("\t\t\t</tracks>\n") to:outFile
502		format("\t\t</animation>\n") to: outFile ;
503	)
504
505	format("\t</animations>\n") to: outFile ;
506
507	OgreExportObject.exportProgress.value = 100;
508)
509
510-------------------------------------------------------------
511-- write <skeleton /> main function
512-- write the animation in the file out_name + ".skeleton.xml"
513-- between the frame firstFrame and lastFrame
514-- and scale time according to length
515-------------------------------------------------------------
516
517function writeSkeleton pmesh exportOptions Anims out_name =
518(
519	local sk,n,keys,initialKeys,messages,phy ;
520
521	sk = getSkin pmesh ;
522	phy = getPhysique pmesh ;
523
524	if (sk == undefined and phy == undefined) then
525	(
526		MessageBox "There is no skin or physique modifier for this object" ;
527		return false;
528	)
529	else
530	(
531		-- in order to perform, skin should be opened
532		max modify mode ;
533		if (sk != undefined) then
534			modPanel.setCurrentObject pmesh.modifiers[#Skin] ;
535		else -- physique
536			modPanel.setCurrentObject pmesh.modifiers[#Physique] ;
537
538		if (not g_MAX) then
539		(
540			format "------------------------------------------\n"
541			format "------ OGRE skeleton Exporter Log   ------\n"
542			format "------------------------------------------\n"
543
544			format "Exporter options :\n"
545			for i=1 to Anims.names.count do
546				format "Anim % - firstFrame: % - lastFrame: %\n" Anims.names[i] Anims.startframes[i] Anims.endframes[i] ;
547			-- creates the output file
548			outFile = createfile (out_name + ".skeleton.xml") ;
549		)
550		else
551		(
552			if (g_MAX_use_listener) then
553				format("<ogrestartfile>%</ogrestartfile><ogrestartdata>\n") (outName + ".skeleton.xml");
554			outFile = listener;
555		)
556
557		-- writes header
558		format("<skeleton>\n") to:outFile ;
559
560		if (not g_MAX) then
561			format "Writing bones :\n" ;
562		writeBones phy sk exportOptions.scale exportOptions.flipYZ exportOptions.exportHelpers outFile ;
563
564		if (not g_MAX) then
565			format "Writing bone hierarchy.\n" ;
566		writeHierarchy outFile ;
567
568		if (not g_MAX) then
569			format "Writing bone tracks.\n" ;
570		writeAnim Anims exportOptions.sampleRate exportOptions.ikSampleRate exportOptions.scale exportOptions.flipYZ outFile ;
571
572		-- ecriture, fin des balises
573		format("</skeleton>\n") to: outFile ;
574
575		if (not g_MAX) then
576		(
577			format "------------------------------------------\n"
578			format "----------          END          ---------\n"
579			format "------------------------------------------\n"
580
581			close outFile ;
582		)
583		else
584		(
585			if (g_MAX_use_listener) then
586				format("</ogrestartdata>\n") to: outFile;
587		)
588
589		messageBox "Exporting skeleton successful !"
590		return true;
591	)
592)
593
594
595
596