ghefts proto-format hyper extensible mesh entaglement device

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
gheft
Posts: 34
Joined: Mon Jul 30, 2007 4:11 am

ghefts proto-format hyper extensible mesh entaglement device

Post by gheft »

This is just something I came up with after getting pissed off at irrlichts mesh loading abilities trying to work on my project, and a way to learn directly(and try to make clearer) how irrlicht loads mesh data. by no means a finished product and maybe not worth using for most people, but here it is anyway.

After writing a simple maxscript for exporting physics data into physx, and working for hours with the .x format trying to get animations and meshes together (at the same time!) I decided the format itself was just too complicated and not even worth debugging so I came up with this

Code: Select all

<mesh numverts="3" numtris="1" material="material.material">
x y z
x y z
x y z
0 1 2
</mesh>

<bone name="parent" position="x,y,z" rotation="w,x,y,z">
	<bone name="child" position="x,y,z" rotation="w,x,y,z">
	</bone>
</bone>

<skinweights bone="bone name" numweights="1">
vertexnumber weight
</skinweights>

<animation name="animation">
	<joint name="joint name" numkeys="1">
		time w x y z//quaternion
	</joint>
</animation>
this way I could easily separate animations or even skeletons into different files(walk.animation for example)
you might notice the vertices dont even have uvs, normals or anything, but that can easily be added, with a flag for vertex type(its just not my top priority right now)
currently the material loading is not working either(see my thread in advanced help)
if anyone is interested in using it I could post the irrlicht meshloader
but its still a work in progress(every thing but animations works)

heres the maxscript code that generates the text(into the maxscript listener) which I paste into files (.mesh,.skeleton,.animation no .material yet) I could easily write a function to save all to file but anyways

Code: Select all

utility util "animation"
(
	button saveskeleton "save skeleton"
	button saveskinnedmesh "save skinned mesh"
	button saveskinweights "save skin weights"
	button saveanimation "save animation" 

	function addBone b tab =
	(
		if (classOf b == BoneGeometry or classOf b == Biped_Object or classOf b == dummy) then
		(
			pos = (getNodeTM b).translationpart
			rot = (getNodeTM b).rotationpart
			
			bname = substituteString b.name " " "_"
			format "%<bone name=\"%\" position=\"%,%,%\" rotation=\"%,%,%,%\">\n" tab bname pos.x pos.z pos.y rot.w rot.x rot.z rot.y
			for i in b.children do
			(
				addBone i (tab+"	")
			)
			format "%</bone>\n" tab
		)
	)
	on saveskeleton pressed do
	(
		for i in geometry do
		(
			if(i.parent==undefined and ((classOf i)==BoneGeometry or (classOf i)==Biped_Object)) then
			(
				addBone i ""
			)
		)
	)
	on saveskinnedmesh pressed do
	(
		for i in geometry do
		(
			if ((classOf i)==Editable_mesh) then
			(
				for m in i.modifiers do
				(
					if((classOf m)==Skin) then
					(
						format "<mesh numverts=\"%\" numtris=\"%\">\n" i.numverts i.numfaces
						for v=1 to i.numverts  do
						(
							vert = getVert i v
							format "% % %\n" vert.x vert.z vert.y
						)
						for f=1 to i.numfaces do
						(
							face = getFace i f
							format "% % %\n" (face.x as Integer-1) (face.z as Integer-1) (face.y as Integer-1)
						)
						format "</mesh>\n"
					)
				)
			)
		)
	)
	on saveskinweights pressed do
	(
		for i in geometry do
		(
			if ((classOf i)==Editable_mesh) then
			(
				for m in i.modifiers do
				(
					if((classOf m)==Skin) then
					(
						max modify mode
						modPanel.setCurrentObject m
						
						BoneWeights = #()
						BoneIndex = #()
						for n=1 to skinOps.getNumberBones m do
						(
							BoneWeights[n] = #()
							BoneIndex[n] = #()
						)
						for v=1 to skinOps.GetNumberVertices m do
						(
							index = 1
							for n=1 to skinOps.GetVertexWeightCount m v do
							(
								append BoneWeights[skinOps.GetVertexWeightBoneID m v n] v
								append BoneIndex[skinOps.GetVertexWeightBoneID m v n] index
								index+=1
							)
						)
						for n=1 to BoneWeights.count do
						(
							bname = substituteString (skinOps.getBoneName m n 1) " " "_"
							format "<skinweights bone=\"%\" numweights=\"%\">\n" bname BoneWeights[n].count
							for v=1 to BoneWeights[n].count do
							(
								format "% %\n" (BoneWeights[n][v]-1) (skinOps.GetVertexWeight m BoneWeights[n][v] BoneIndex[n][v])
							)
							format "</skinweights>\n"
						)
					)
				)
			)
		)
	)
	on saveanimation pressed do
	(
		format "<animation name=\"%\">\n" "..."
		for i in geometry do
		(
			if((classOf i)==BoneGeometry) then
			(
				if(i.rotation.isAnimated) then
				(
					bname = substituteString i.name " " "_"
					format "	<bone name=\"%\" numkeys=\"%\">\n" bname i.rotation.controller.keys.count
					for k in i.rotation.controller.keys do
					(
						rot = k.value as quat
						format "		% % % % %\n" (k.time as float/4800) rot.w rot.x rot.z rot.y
					)
					format "	</bone>\n"
				)
			)
		)
		format "</animation>\n"
	)
)
its very simple so I imagine it could be easily translated to another app like blender
any suggestions? :idea:
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Thats pretty cool, there aren't many ways to get animation from MAX into irrlicht currently (.X is sometimes buggy, b3d exporter is old, and they are both rarely updated or maintained.). So this could really be useful to alot of people if the animation export/import work correctly, maybe someone could make a blender python script too? :wink: (Hell I might if I feel up to it.)

Are you only saving the rotation of the joints? What about scale/position?

Also a binary version would be nice if its possible, XML-style formats can take up alot of space.
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
gheft
Posts: 34
Joined: Mon Jul 30, 2007 4:11 am

Post by gheft »

at the moment only rotation keys are saved but of course this could be easily changed, maybe something like this

Code: Select all

<animation name="animation">
   <joint name="joint name">
      <rotation numkeys="1">
         time w x y z
      </rotation>
      <translation numkeys="1">
         time x y z
      </translation>
      <scale numkeys="1">
         time x y z
      </scale>
   </joint>
</animation>
I wouldn't mind doing a binary format eventually too, maybe just something that can be automatically saved by the loader and reloaded after that. but realistically its not something I'm worried about for now, the filesize (a 3000+ poly skinned mesh with 1 animation is only 150kb) or speed, I assume binary loads faster?

I tested a simple walking animation and found the characters legs shrink to his hips... it seems the joints local positions are being set to zero, moved to their parents.

when I tried to set a rotation key the skinnedmesh checks for any kind of key and then reads from animated position which is zero because there's no keys. but looking through the svn I think its been fixed already 8)
I have yet to compile the latest svn but this means it won't work in 1.4.

I guess the reason other files work is because they have position keys too? for most skeletal animation I think only rotation is necessary
gheft
Posts: 34
Joined: Mon Jul 30, 2007 4:11 am

Post by gheft »

Haven't had much time to work on it this weekend and spent a while debugging some totally irrelevant code.

well it looks like the svn update didn't actually change anything, still having the same problems with joint positions. I'm looking through CSkinnedMesh to try to fix this but not really getting anywhere. With .x files the keys are matrices not quaternions.

I guess I'd have to talk to Luke about this (?)but it seems there needs to be a change to CSkinnedMesh in order to have rotation keys without position keys.
so Luke, if you see this plz help :wink:
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

That is a pretty simple format, and pretty good to. It could easily be ported over to Blender since it is so easy to write Python scripts for Blender. But, just curious, has an actual loader been written for this to load it into an Irrlicht application yet?
TheQuestion = 2B || !2B
gheft
Posts: 34
Joined: Mon Jul 30, 2007 4:11 am

Post by gheft »

yes there is a loader, which works for meshes, bones and skinweights. I just want to get animations working before I release it.
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

Ah, okay, I see. Cool.
TheQuestion = 2B || !2B
Zeuss
Posts: 114
Joined: Mon Nov 08, 2004 9:02 pm
Location: Canberra - Australia
Contact:

Post by Zeuss »

the best .x exporter for max is kwxport.

http://kwxport.sourceforge.net/

Its fantastic! Beats panda exporter by a long shot and I havn't had too many issues with it so far.
Help make Irrlicht even Better! Create and submit your own Irrlicht Extension
Want a Games Education? Try The Academy of Interactive Entertainment
gheft
Posts: 34
Joined: Mon Jul 30, 2007 4:11 am

Post by gheft »

unfortunately I have had issues with kwxport, was a while ago with older version, but I'd like to stay away from .x anyway.

I fixed the animation problems with a change to CSkinnedMesh

Code: Select all

void CSkinnedMesh::buildAll_LocalAnimatedMatrices()
{
	for (u32 i=0; i<AllJoints.size(); ++i)
	{
		SJoint *joint = AllJoints[i];

		//Could be faster:

		if (joint->UseAnimationFrom &&
			(joint->UseAnimationFrom->PositionKeys.size() ||
			 joint->UseAnimationFrom->ScaleKeys.size() ||
			 joint->UseAnimationFrom->RotationKeys.size() ))
		{
			joint->LocalAnimatedMatrix=joint->Animatedrotation.getMatrix();

			// --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() ---
			f32 *m1 = joint->LocalAnimatedMatrix.pointer();
			core::vector3df &Pos = joint->Animatedposition;
			m1[0] += Pos.X*m1[3];
			m1[1] += Pos.Y*m1[3];
			m1[2] += Pos.Z*m1[3];
			m1[4] += Pos.X*m1[7];
			m1[5] += Pos.Y*m1[7];
			m1[6] += Pos.Z*m1[7];
			m1[8] += Pos.X*m1[11];
			m1[9] += Pos.Y*m1[11];
			m1[10] += Pos.Z*m1[11];
			m1[12] += Pos.X*m1[15];
			m1[13] += Pos.Y*m1[15];
			m1[14] += Pos.Z*m1[15];
			// -----------------------------------

			joint->GlobalSkinningSpace=false;

			if (joint->ScaleKeys.size())
			{
				/*
				core::matrix4 scaleMatrix;
				scaleMatrix.setScale(joint->Animatedscale);
				joint->LocalAnimatedMatrix *= scaleMatrix;
				*/

				// -------- joint->LocalAnimatedMatrix *= scaleMatrix -----------------
				f32 *m1 = joint->LocalAnimatedMatrix.pointer();
				m1[0] *= joint->Animatedscale.X;
				m1[1] *= joint->Animatedscale.X;
				m1[2] *= joint->Animatedscale.X;
				m1[3] *= joint->Animatedscale.X;
				m1[4] *= joint->Animatedscale.Y;
				m1[5] *= joint->Animatedscale.Y;
				m1[6] *= joint->Animatedscale.Y;
				m1[7] *= joint->Animatedscale.Y;
				m1[8] *= joint->Animatedscale.Z;
				m1[9] *= joint->Animatedscale.Z;
				m1[10] *= joint->Animatedscale.Z;
				m1[11] *= joint->Animatedscale.Z;
				m1[12] *= joint->Animatedscale.X;
				// -----------------------------------

			}
		}
		else
		{
			joint->LocalAnimatedMatrix=joint->LocalMatrix;
		}
	}
}
I changed to this

Code: Select all

void CSkinnedMesh::buildAll_LocalAnimatedMatrices()
{
	for (u32 i=0; i<AllJoints.size(); ++i)
	{
		SJoint *joint = AllJoints[i];

		//Could be faster:

		if (joint->UseAnimationFrom &&
			(joint->UseAnimationFrom->PositionKeys.size() ||
			 joint->UseAnimationFrom->ScaleKeys.size() ||
			 joint->UseAnimationFrom->RotationKeys.size() ))
		{
			if(joint->PositionKeys.size())
			{
				joint->LocalAnimatedMatrix = joint->Animatedrotation.getMatrix();
				f32 *m1 = joint->LocalAnimatedMatrix.pointer();
				core::vector3df &Pos = joint->Animatedposition;
				m1[0] += Pos.X*m1[3];
				m1[1] += Pos.Y*m1[3];
				m1[2] += Pos.Z*m1[3];
				m1[4] += Pos.X*m1[7];
				m1[5] += Pos.Y*m1[7];
				m1[6] += Pos.Z*m1[7];
				m1[8] += Pos.X*m1[11];
				m1[9] += Pos.Y*m1[11];
				m1[10] += Pos.Z*m1[11];
				m1[12] += Pos.X*m1[15];
				m1[13] += Pos.Y*m1[15];
				m1[14] += Pos.Z*m1[15];
			}
			else if(joint->RotationKeys.size())
			{
				vector3df t = joint->LocalAnimatedMatrix.getTranslation();
				joint->LocalAnimatedMatrix = joint->Animatedrotation.getMatrix();
				joint->LocalAnimatedMatrix.setTranslation(t);
			}

			joint->GlobalSkinningSpace=false;

			if (joint->ScaleKeys.size())
			{
				
				//core::matrix4 scaleMatrix;
				//scaleMatrix.setScale(joint->Animatedscale);
				//joint->LocalAnimatedMatrix *= scaleMatrix;
				

				// -------- joint->LocalAnimatedMatrix *= scaleMatrix -----------------
				f32 *m1 = joint->LocalAnimatedMatrix.pointer();
				m1[0] *= joint->Animatedscale.X;
				m1[1] *= joint->Animatedscale.X;
				m1[2] *= joint->Animatedscale.X;
				m1[3] *= joint->Animatedscale.X;
				m1[4] *= joint->Animatedscale.Y;
				m1[5] *= joint->Animatedscale.Y;
				m1[6] *= joint->Animatedscale.Y;
				m1[7] *= joint->Animatedscale.Y;
				m1[8] *= joint->Animatedscale.Z;
				m1[9] *= joint->Animatedscale.Z;
				m1[10] *= joint->Animatedscale.Z;
				m1[11] *= joint->Animatedscale.Z;
				m1[12] *= joint->Animatedscale.X;
				// -----------------------------------

			}
		}
		else
		{
			joint->LocalAnimatedMatrix=joint->LocalMatrix;
		}
	}
}
I dunno if I should post this to bug reports maybe make a patch, it should probably be tested with meshes that worked before.

well I just have to update the script and polish a few things up and I'll post the loader...
Zeuss
Posts: 114
Joined: Mon Nov 08, 2004 9:02 pm
Location: Canberra - Australia
Contact:

Post by Zeuss »

I know that .x is not the best format, but I personally havn't a good skinned exporter for max that is any good.

kwxport has been reasonably problem free. You just need to make sure that your mesh is 1 mesh not made up from seperate meshes rigged onto the same bone system. It has a fit about that.
Help make Irrlicht even Better! Create and submit your own Irrlicht Extension
Want a Games Education? Try The Academy of Interactive Entertainment
gheft
Posts: 34
Joined: Mon Jul 30, 2007 4:11 am

Post by gheft »

Zeuss: never tried exporting seperate meshes, but your right I probably could have gotten .x to work by now. I just wanted to have a format that was more flexible and simple

ok I finally got the right values out of max after reading the docs for ever.

so here is the new exporter script which now saves to file and outputs animation right

Code: Select all

utility util "gmesh export"
(
	button savetogmesh  "save to gmesh"
	button savemesh "save mesh"
	button saveskeleton "save skeleton"
	button saveskinweights "save skin weights"
	button saveanimation "save animation" 
	edittext animname "animation name" text:"anim" labelOnTop:true 

	button prepanimation "prepare animation"
	
	function addBone b tab =
	(
		if (classOf b == BoneGeometry or classOf b == Biped_Object or classOf b == dummy) then
		(
			pos = (getNodeTM b).translationpart
			rot = (getNodeTM b).rotationpart
			
			bname = substituteString b.name " " "_"
			format "%<bone name=\"%\" position=\"%,%,%\" rotation=\"%,%,%,%\">\n" tab bname pos.x pos.z pos.y rot.w rot.x rot.z rot.y
			for i in b.children do
			(
				addBone i (tab+"	")
			)
			format "%</bone>\n" tab
		)
	)
	function printMesh s =
	(
		for m in s do
		(
			if ((classOf m)==Editable_mesh and m.isHidden==false) then
			(
				format "<mesh name=\"%\" numverts=\"%\" numtris=\"%\" material=\"%\">\n" m.name m.numverts m.numfaces ""
				for v=1 to m.numverts  do
				(
					vert = getVert m v
					format "% % %\n" vert.x vert.z vert.y
				)
				for f=1 to m.numfaces do
				(
					face = getFace m f
					format "% % %\n" (face.x as Integer-1) (face.z as Integer-1) (face.y as Integer-1)
				)
				format "</mesh>\n"
			)
		)
	)
	function printBones s =
	(
		for b in s do
		(
			if(b.parent==undefined and ((classOf b)==BoneGeometry or (classOf b)==Biped_Object)) then
			(
				addBone b ""
			)
		)
	)
	function printWeights s =
	(
		for i in s do
		(
			if ((classOf i)==Editable_mesh and i.isHidden==false) then
			(
				for m in i.modifiers do
				(
					if((classOf m)==Skin) then
					(
						max modify mode
						modPanel.setCurrentObject m
						
						BoneWeights = #()
						BoneIndex = #()
						for n=1 to skinOps.getNumberBones m do
						(
							BoneWeights[n] = #()
							BoneIndex[n] = #()
						)
						for v=1 to skinOps.GetNumberVertices m do
						(
							index = 1
							for n=1 to skinOps.GetVertexWeightCount m v do
							(
								append BoneWeights[skinOps.GetVertexWeightBoneID m v n] v
								append BoneIndex[skinOps.GetVertexWeightBoneID m v n] index
								index+=1
							)
						)
						for n=1 to BoneWeights.count do
						(
							bname = substituteString (skinOps.getBoneName m n 1) " " "_"
							format "<skinweights bone=\"%\" numweights=\"%\">\n" bname BoneWeights[n].count
							for v=1 to BoneWeights[n].count do
							(
								format "% %\n" (BoneWeights[n][v]-1) (skinOps.GetVertexWeight m BoneWeights[n][v] BoneIndex[n][v])
							)
							format "</skinweights>\n"
						)
					)
				)
			)
		)
	)
	function printAnimation s =
	(
		if animname.text != "" then
		(
			format "<animation name=\"%\">\n" animname.text
			for i in s do
			(
				if((classOf i)==BoneGeometry) then
				(
					if(i.isAnimated) then
					(
						bname = substituteString i.name " " "_"
						
						pkeys = i.position.controller.keys.count 
						skeys = i.scale.controller.keys.count 
						rkeys = i.rotation.controller.keys.count
						if(not i.position.isAnimated) then pkeys=0
						if(not i.scale.isAnimated) then skeys=0
						if(not i.rotation.isAnimated) then rkeys=0
						
						--rkeys = 1
						format "	<joint name=\"%\" poskeys=\"%\" scalekeys=\"%\" rotkeys=\"%\">\n" bname pkeys skeys rkeys
							
						/*b = inverse (getNodeTM i).rotationpart
						at time 0
						(
							rot = b*(getNodeTM i).rotationpart
							format "		% % % % %\n" 0 rot.w rot.x rot.z rot.y
						)*/
						if(i.position.isAnimated) then
						(
							for k in i.position.controller.keys do
							(
								b = (getNodeTM i).translationpart
								at time k.time
								(
									pos = b-(getNodeTM i).translationpart
									format "		% % % %\n" (k.time as float/4800) pos.x pos.z pos.y
								)
							)
						)
						if(i.scale.isAnimated) then
						(
							for k in i.scale.controller.keys do
							(
								sca = k.value as point3
								format "		% % % %\n" (k.time as float/4800) sca.x sca.z sca.y
							)
						)
						
						if(i.rotation.isAnimated) then
						(
							for k in i.rotation.controller.keys do
							(
								b = inverse(getNodeTM i).rotationpart
								p = inverse(getNodeTM i.parent).rotationpart
								at time k.time
								(
									rot = (inverse(p*(getNodeTM i.parent).rotationpart))*(b*(getNodeTM i).rotationpart)
									format "		% % % % %\n" (k.time as float/4800) rot.w rot.x rot.z rot.y
								)
							)
						)
						format "	</joint>\n"
					)
				)
			)
			format "</animation>\n"
		)
	)
	on savetogmesh pressed do
	(
		clearListener()
		output_name = getSaveFileName caption:"gmesh File" types:"gmesh (*.gmesh)|*.gmesh|All Files (*.*)|*.*|"
		openLog output_name
		printMesh geometry
		printBones geometry
		printWeights geometry
		printAnimation geometry
		closeLog()
	)
	on savemesh pressed do
	(
		printMesh selection
	)
	on saveskeleton pressed do
	(
		printBones selection
	)
	on saveskinweights pressed do
	(
		printWeights selection
	)
	on saveanimation pressed do
	(
		printAnimation selection
	)
	on prepanimation pressed do
	(
		for i in geometry do
		(
			if((classOf i)==BoneGeometry) then
			(
				deleteKeys i.position.controller.keys
				deleteKeys i.scale.controller.keys
				i.rotation.controller = tcb_rotation()
			)
		)
	)
)
and here is the loader

gmeshloader.h

Code: Select all

#pragma once

#include "irrlicht.h"
#include "IMeshLoader.h"
#include "CSkinnedMesh.h"
using namespace irr;
using namespace io;
using namespace core;
using namespace video;
using namespace scene;

class GMeshFileLoader : public IMeshLoader
{
public:

	GMeshFileLoader(ISceneManager* smgr, IFileSystem* fs);

	virtual bool isALoadableFileExtension(const c8* fileName) const;
	
	virtual IAnimatedMesh* createMesh(IReadFile* file);
	
	void readBone(CSkinnedMesh::SJoint *Parent);

	CSkinnedMesh* AnimatedMesh;
	IXMLReaderUTF8* xml;

	ISceneManager* SceneManager;
	IFileSystem* FileSystem;
};
quaternion readQuat(char* string);
vector3df readVect(char* string);
void skipWhite(char** string);
void skipToWhite(char** string);
f32 readFloat(char** string);
gmeshloader.cpp

Code: Select all

#include "gmeshloader.h"

GMeshFileLoader::GMeshFileLoader(scene::ISceneManager* smgr, io::IFileSystem* fs) : SceneManager(smgr), FileSystem(fs)
{
}

//! Returns true if the file maybe is able to be loaded by this class.
/** This decision should be based only on the file extension (e.g. ".cob") */
bool GMeshFileLoader::isALoadableFileExtension(const c8* fileName) const
{
	return strstr(fileName, ".gmesh");
}

//! creates/loads an animated mesh from the file.
//! \return Pointer to the created mesh. Returns 0 if loading failed.
//! If you no longer need the mesh, you should call IAnimatedMesh::drop().
//! See IReferenceCounted::drop() for more information.
IAnimatedMesh* GMeshFileLoader::createMesh(io::IReadFile* file)
{
	xml = FileSystem->createXMLReaderUTF8(file);
	if (!xml)
		return 0;

	AnimatedMesh = new CSkinnedMesh();
	
	while(xml->read())
	{
		if (xml->getNodeType() == io::EXN_ELEMENT)
		{
			if (stringw("mesh") == xml->getNodeName())
			{
				int numverts = xml->getAttributeValueAsInt("numverts");
				int numindices = xml->getAttributeValueAsInt("numtris")*3;
				//c8* matname = (c8*)xml->getAttributeValue("material");
				//buffer->Material = loadMaterial(matname);
				SSkinMeshBuffer* buffer = AnimatedMesh->createBuffer();
				buffer->Vertices_Standard.reallocate(numverts);
				
				while(xml->read()&&xml->getNodeType()!=EXN_ELEMENT_END)
				{
					if (xml->getNodeType() == io::EXN_TEXT)
					{
						core::stringc string = xml->getNodeData();
						c8* p = &string[0];
						for(int i=0;i<numverts;i++)
						{
							S3DVertex vert;
							skipWhite(&p);
							vert.Pos.X = readFloat(&p);
							skipWhite(&p);
							vert.Pos.Y = readFloat(&p);
							skipWhite(&p);
							vert.Pos.Z = readFloat(&p);
							buffer->Vertices_Standard.push_back(vert);
						}
						for(int i=0;i<numindices;i++)
						{
							skipWhite(&p);
							buffer->Indices.push_back((u16)readFloat(&p));
						}
					}
				}
			}
			if (stringw("bone") == xml->getNodeName())
			{
				readBone(0);
			}
			if (stringw("skinweights") == xml->getNodeName())
			{
				stringc boneName = xml->getAttributeValue("bone");
				int numweights = xml->getAttributeValueAsInt("numweights");
				CSkinnedMesh::SJoint *joint=0;
				for (u32 n=0; n < AnimatedMesh->getAllJoints().size(); ++n)
				{
					if (AnimatedMesh->getAllJoints()[n]->Name==boneName)
					{
						joint=AnimatedMesh->getAllJoints()[n];
						break;
					}
				}
				xml->read();
				if (xml->getNodeType() == io::EXN_TEXT)
				{
					core::stringc string = xml->getNodeData();
					c8* p = &string[0];
									
					for(int i=0;i<numweights;i++)
					{
						CSkinnedMesh::SWeight *weight=AnimatedMesh->createWeight(joint);
						weight->buffer_id=0;
						skipWhite(&p);
						weight->vertex_id=(u32)readFloat(&p);
						skipWhite(&p);
						weight->strength = readFloat(&p);
					}
				}
			}	
			if (stringw("animation") == xml->getNodeName())
			{
				stringc animName = xml->getAttributeValue("name");
				while(xml->read()&&xml->getNodeType()!=EXN_ELEMENT_END)
				{
					if (stringw("joint") == xml->getNodeName())
					{
						stringc jointName = xml->getAttributeValue("name");
						int rotkeys = xml->getAttributeValueAsInt("rotkeys");
						int poskeys = xml->getAttributeValueAsInt("poskeys");
						int scalekeys = xml->getAttributeValueAsInt("scalekeys");

						CSkinnedMesh::SJoint *joint=0;
						for(int i=0;i<AnimatedMesh->getAllJoints().size();i++)
						{
							if(AnimatedMesh->getAllJoints()[i]->Name==jointName)
							{
								joint=AnimatedMesh->getAllJoints()[i];
								break;
							}
						}
						xml->read();//skip to text whitespace
						if (xml->getNodeType() == io::EXN_TEXT)
						{
							core::stringc string = xml->getNodeData();
							c8* p = &string[0];
							
							joint->PositionKeys.reallocate(poskeys);
							joint->ScaleKeys.reallocate(scalekeys);
							joint->RotationKeys.reallocate(rotkeys);
							for(int i=0;i<poskeys;i++)
							{
								
								ISkinnedMesh::SPositionKey *key=AnimatedMesh->createPositionKey(joint);
								skipWhite(&p);
								key->frame = readFloat(&p);
								skipWhite(&p);
								key->position.X = readFloat(&p);
								skipWhite(&p);
								key->position.Y = readFloat(&p);
								skipWhite(&p);
								key->position.Z = readFloat(&p);
								
								key->position += joint->LocalMatrix.getTranslation();//dont work
								//cout<<"added pos key at frame "<<key->frame<<endl;
								joint->PositionKeys.push_back(*key);
							}
							for(int i=0;i<scalekeys;i++)
							{
								
								ISkinnedMesh::SScaleKey *key=AnimatedMesh->createScaleKey(joint);
								skipWhite(&p);
								key->frame = readFloat(&p);
								skipWhite(&p);
								key->scale.X = readFloat(&p);
								skipWhite(&p);
								key->scale.Y = readFloat(&p);
								skipWhite(&p);
								key->scale.Z = readFloat(&p);
								
								//cout<<"added scale key at frame "<<key->frame<<endl;
								joint->ScaleKeys.push_back(*key);
							}
							for(int i=0;i<rotkeys;i++)
							{
								ISkinnedMesh::SRotationKey *key=AnimatedMesh->createRotationKey(joint);
								skipWhite(&p);
								key->frame = readFloat(&p);
								skipWhite(&p);
								key->rotation.W = readFloat(&p);
								skipWhite(&p);
								key->rotation.X = readFloat(&p);
								skipWhite(&p);
								key->rotation.Y = readFloat(&p);
								skipWhite(&p);
								key->rotation.Z = readFloat(&p);
								
								key->rotation = quaternion(joint->LocalMatrix)*key->rotation.makeInverse();

								//cout<<"added rot key at frame "<<key->frame<<endl;
								joint->RotationKeys.push_back(*key);
							}
						}
						xml->read();//skip </bone>
					}
				}
			}
		}
	}
	xml->drop();

	SceneManager->getMeshManipulator()->recalculateNormals(AnimatedMesh->getMesh(0),1);
	AnimatedMesh->finalize();
	return AnimatedMesh;
}
void GMeshFileLoader::readBone(CSkinnedMesh::SJoint *Parent)
{
	CSkinnedMesh::SJoint *joint = AnimatedMesh->createJoint(Parent);
	joint->Name = (char*)xml->getAttributeValue("name");
	
	matrix4 parentMat = matrix4();
	if(Parent) Parent->GlobalMatrix.getInverse(parentMat);
	
	quaternion rot;
	rot.fromAngleAxis(-PI/2,vector3df(0,0,1));

	quaternion quat = readQuat((char*)xml->getAttributeValue("rotation"));
	if(Parent) quat *= rot;//convert binormal to tangent (?)

	vector3df pos = readVect((char*)xml->getAttributeValue("position"));
	
	matrix4 localMat = quat.getMatrix();
	localMat.setTranslation(pos);
	joint->LocalMatrix = parentMat*localMat;
	joint->GlobalMatrix = localMat;

	joint->LocalMatrix(0,2) = clamp(joint->LocalMatrix(0,2),-1.0f,1.0f);

	while(xml->read()&&xml->getNodeType()!=EXN_ELEMENT_END)
	{
		if (xml->getNodeType() == EXN_ELEMENT)
		{
			if (stringw("bone") == xml->getNodeName())
			{
				readBone(joint);
			}
		}
	}
}
quaternion readQuat(char* string)
{
	f32 w = atof(strtok(string,","));
	f32 x = atof(strtok(NULL,","));
	f32 y = atof(strtok(NULL,","));
	f32 z = atof(strtok(NULL,","));
	return quaternion(x,y,z,w);
}
vector3df readVect(char* string)
{
	f32 x = atof(strtok(string,","));
	f32 y = atof(strtok(NULL,","));
	f32 z = atof(strtok(NULL,","));
	return vector3df(x,y,z);
}
//! ganked from CIrrMeshFileLoader
void skipWhite(char** string)
{
	c8* p = *string;
	while(*p && (*p==' ' || *p=='\n' || *p=='\r' || *p=='\t'))
		++p;
	*string = p;
}
void skipToWhite(char** string)
{
	c8* p = *string;
	while(*p && !(*p==' ' || *p=='\n' || *p=='\r' || *p=='\t'))
		++p;
	*string = p;
}
f32 readFloat(char** string)
{
	f32 ftmp;
	*string = (char*)fast_atof_move(*string, ftmp);
	return ftmp;
}
note that it is open to support position and scale keys... but the position keys aren't working right and I don't think it's even possible to use scale keys? I tested scale keys and it seems they aren't supported.
FuzzYspo0N
Posts: 914
Joined: Fri Aug 03, 2007 12:43 pm
Location: South Africa
Contact:

Post by FuzzYspo0N »

This is a really great addition, and im sure it will get further to helping integrate max into irrlicht with ease. there are tricks to doing it but something like this can only get better!

It should be added to the core as the "gheft" format :D or something.

There is space for growth here, like lights, cameras all being loaded from one scene. i know a huge amount of people that would really help, including me. setting up a scene in max and exporting to a format to load in irrlicht ? hell yea :)

the one i was working on was messy, it saved each piece of the map into its own folder inside the mesh texture lightmaps materials folders and then made one xml telling irrlicht which to load where. that kinda worked but i think this is come a nice directin.

Good work
Zeuss
Posts: 114
Joined: Mon Nov 08, 2004 9:02 pm
Location: Canberra - Australia
Contact:

Post by Zeuss »

Thats all cool gheft, I'm only trying to help.

I was told (by my gf) that skinning info was unavailble in max script. Has this recently been added in Max 2008?

If so yay!

Also out of interest, it would be fanstastic to get the normals, binormals and tangents. As unwrapped mesh's texture seems cause irrlicht serious issues in generating the binormals and tangents.
Help make Irrlicht even Better! Create and submit your own Irrlicht Extension
Want a Games Education? Try The Academy of Interactive Entertainment
gheft
Posts: 34
Joined: Mon Jul 30, 2007 4:11 am

Post by gheft »

fuzzyspoon: I would absolutely like to see it added to the irrlicht core as an irrlicht standard format, something like ogres format maybe, of course if it was made official it would probably need a lot more development.

I think irrlicht should have a universal format and I was actually thinking about adding skinning to irrmesh when I started but really the only reason I didn`t is because I wanted to be able to separate animations and materials. my ideal format would be something more like ogres, with separated files for materials, scenes, skeletons, animations etc... I just had the idea of making a loader for ogre files instead :lol: might not be a bad idea.

I really wish irrlicht was more like ogre in the way it separates meshes and materials and has a unified animation system (I sound like someone who chose the wrong engine :lol:).

anyway I wouldn't mind seeing what you came up for exporting scenes in max, having 1 file for a skinned mesh is one thing, but for scenes I think its better to have separate folders.

Zeuss: reading the documentation, I think the skinOps interface is available in older versions of max as well, but it's a little bit obtuse since you can't actually get the info from the skin modifier, and you have to actually have it open in the modifier stack to work (modPanel.setCurrentObject m ). it doesn't work with the physique modifier though, you're lucky to have a gf that knows maxscript :P

I guess I don't know enough about binormals and tangents, but I didn't know they hadn't anything to do with a meshes uvs, but yeah I definitely want to add support for more vertex types, I just don't have any unwrapped meshes at the moment to test it on :wink:
Post Reply