Retour
Version Originale

./aip/1.8aipmod/source/Irrlicht/CAnimatedMeshHalfLife.cpp :


// Copyright (C) 2002-2011 Nikolaus Gebhardt / Fabio Concas / Thomas Alten

// This file is part of the "Irrlicht Engine".

// For conditions of distribution and use, see copyright notice in irrlicht.h


#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_HALFLIFE_LOADER_

#include "CAnimatedMeshHalfLife.h"
#include "os.h"
#include "CColorConverter.h"
#include "CImage.h"
#include "coreutil.h"
#include "SMeshBuffer.h"
#include "IVideoDriver.h"
#include "IFileSystem.h"

namespace irr
{
namespace scene
{

	using namespace video;

	void AngleQuaternion( const vec3_hl angles, vec4_hl quaternion )
	{
		f32		angle;
		f32		sr, sp, sy, cr, cp, cy;

		// FIXME: rescale the inputs to 1/2 angle

		angle = angles[2] * 0.5f;
		sy = sin(angle);
		cy = cos(angle);
		angle = angles[1] * 0.5f;
		sp = sin(angle);
		cp = cos(angle);
		angle = angles[0] * 0.5f;
		sr = sin(angle);
		cr = cos(angle);

		quaternion[0] = sr*cp*cy-cr*sp*sy; // X

		quaternion[1] = cr*sp*cy+sr*cp*sy; // Y

		quaternion[2] = cr*cp*sy-sr*sp*cy; // Z

		quaternion[3] = cr*cp*cy+sr*sp*sy; // W

	}

	void QuaternionMatrix( const vec4_hl quaternion, f32 (*matrix)[4] )
	{
		matrix[0][0] = 1.f - 2.f * quaternion[1] * quaternion[1] - 2.f * quaternion[2] * quaternion[2];
		matrix[1][0] = 2.f * quaternion[0] * quaternion[1] + 2.f * quaternion[3] * quaternion[2];
		matrix[2][0] = 2.f * quaternion[0] * quaternion[2] - 2.f * quaternion[3] * quaternion[1];

		matrix[0][1] = 2.f * quaternion[0] * quaternion[1] - 2.f * quaternion[3] * quaternion[2];
		matrix[1][1] = 1.f - 2.f * quaternion[0] * quaternion[0] - 2.f * quaternion[2] * quaternion[2];
		matrix[2][1] = 2.f * quaternion[1] * quaternion[2] + 2.f * quaternion[3] * quaternion[0];

		matrix[0][2] = 2.f * quaternion[0] * quaternion[2] + 2.f * quaternion[3] * quaternion[1];
		matrix[1][2] = 2.f * quaternion[1] * quaternion[2] - 2.f * quaternion[3] * quaternion[0];
		matrix[2][2] = 1.f - 2.f * quaternion[0] * quaternion[0] - 2.f * quaternion[1] * quaternion[1];
	}

	void QuaternionSlerp( const vec4_hl p, vec4_hl q, f32 t, vec4_hl qt )
	{
		s32 i;
		f32 omega, cosom, sinom, sclp, sclq;

		// decide if one of the quaternions is backwards

		f32 a = 0;
		f32 b = 0;
		for (i = 0; i < 4; i++) {
			a += (p[i]-q[i])*(p[i]-q[i]);
			b += (p[i]+q[i])*(p[i]+q[i]);
		}
		if (a > b) {
			for (i = 0; i < 4; i++) {
				q[i] = -q[i];
			}
		}

		cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3];

		if ((1.f + cosom) > 0.00000001) {
			if ((1.f - cosom) > 0.00000001) {
				omega = acos( cosom );
				sinom = sin( omega );
				sclp = sin( (1.f - t)*omega) / sinom;
				sclq = sin( t*omega ) / sinom;
			}
			else {
				sclp = 1.f - t;
				sclq = t;
			}
			for (i = 0; i < 4; i++) {
				qt[i] = sclp * p[i] + sclq * q[i];
			}
		}
		else {
			qt[0] = -p[1];
			qt[1] = p[0];
			qt[2] = -p[3];
			qt[3] = p[2];
			sclp = sin( (1.f - t) * 0.5f * core::PI);
			sclq = sin( t * 0.5f * core::PI);
			for (i = 0; i < 3; i++) {
				qt[i] = sclp * p[i] + sclq * qt[i];
			}
		}
	}

	void R_ConcatTransforms (const f32 in1[3][4], const f32 in2[3][4], f32 out[3][4])
	{
		out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
		out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
		out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
		out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3];
		out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
		out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
		out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
		out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3];
		out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
		out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
		out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
		out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3];
	}

	#define	EQUAL_EPSILON	0.001
	s32 VectorCompare (vec3_hl v1, vec3_hl v2)
	{
		s32		i;

		for (i=0 ; i<3 ; i++)
			if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
				return false;

		return true;
	}


	#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])


	inline void VectorTransform (const vec3_hl in1, const f32 in2[3][4], vec3_hl out)
	{
		out[0] = DotProduct(in1, in2[0]) + in2[0][3];
		out[1] = DotProduct(in1, in2[1]) + in2[1][3];
		out[2] = DotProduct(in1, in2[2]) + in2[2][3];
	}

	inline void VectorTransform2 (core::vector3df &out, const vec3_hl in1, const f32 in2[3][4])
	{
		out.X = DotProduct(in1, in2[0]) + in2[0][3];
		out.Z = DotProduct(in1, in2[1]) + in2[1][3];
		out.Y = DotProduct(in1, in2[2]) + in2[2][3];
	}


	static f32 BoneTransform[MAXSTUDIOBONES][3][4];	// bone transformation matrix



	void getBoneVector ( core::vector3df &out, u32 index )
	{
		out.X = BoneTransform[index][0][3];
		out.Z = BoneTransform[index][1][3];
		out.Y = BoneTransform[index][2][3];
	}

	void getBoneBox ( core::aabbox3df &box, u32 index, f32 size = 0.5f )
	{
		box.MinEdge.X = BoneTransform[index][0][3] - size;
		box.MinEdge.Z = BoneTransform[index][1][3] - size;
		box.MinEdge.Y = BoneTransform[index][2][3] - size;

		size *= 2.f;
		box.MaxEdge.X = box.MinEdge.X + size;
		box.MaxEdge.Y = box.MinEdge.Y + size;
		box.MaxEdge.Z = box.MinEdge.Z + size;
	}

	void getTransformedBoneVector ( core::vector3df &out, u32 index, const vec3_hl in)
	{
		out.X = DotProduct(in, BoneTransform[index][0]) + BoneTransform[index][0][3];
		out.Z = DotProduct(in, BoneTransform[index][1]) + BoneTransform[index][1][3];
		out.Y = DotProduct(in, BoneTransform[index][2]) + BoneTransform[index][2][3];

	}


//! Constructor

CHalflifeMDLMeshFileLoader::CHalflifeMDLMeshFileLoader( scene::ISceneManager* smgr )
{
#ifdef _DEBUG
	setDebugName("CHalflifeMDLMeshFileLoader");
#endif
	SceneManager = smgr;
}


//! returns true if the file maybe is able to be loaded by this class

//! based on the file extension (e.g. ".bsp")

bool CHalflifeMDLMeshFileLoader::isALoadableFileExtension(const io::path& filename) const
{
	return core::hasFileExtension ( filename, "mdl" );
}


//! 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* CHalflifeMDLMeshFileLoader::createMesh(io::IReadFile* file)
{
	CAnimatedMeshHalfLife* msh = new CAnimatedMeshHalfLife();
	if (msh)
	{
		if ( msh->loadModelFile ( file, SceneManager ) )
			return msh;

		msh->drop();
	}

	return 0;
}

//! Constructor

CAnimatedMeshHalfLife::CAnimatedMeshHalfLife()
{
#ifdef _DEBUG
	setDebugName("CAnimatedMeshHalfLife");
#endif

	initData ();
}

/*!
	loads a complete model
*/
bool CAnimatedMeshHalfLife::loadModelFile( io::IReadFile* file,ISceneManager * smgr )
{
	if (!file)
		return false;

	bool r = false;

	SceneManager = smgr;

	if ( loadModel ( file, file->getFileName() ) )
	{
		if ( postLoadModel ( file->getFileName() ) )
		{
			initModel ();
			//dumpModelInfo ( 1 );

			r = true;
		}
	}
	return r;
}



//! Destructor

CAnimatedMeshHalfLife::~CAnimatedMeshHalfLife()
{
	freeModel ();
}


//! Returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.

u32 CAnimatedMeshHalfLife::getFrameCount() const
{
	return FrameCount;
}


//! set the hardware mapping hint, for driver

void CAnimatedMeshHalfLife::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,E_BUFFER_TYPE buffer)
{
}


//! flags the meshbuffer as changed, reloads hardware buffers

void CAnimatedMeshHalfLife::setDirty(E_BUFFER_TYPE buffer)
{
}




static vec3_hl TransformedVerts[MAXSTUDIOVERTS];	// transformed vertices

static vec3_hl TransformedNormals[MAXSTUDIOVERTS];	// light surface normals



/*!
*/
void CAnimatedMeshHalfLife::initModel ()
{
	// init Sequences to Animation

	KeyFrameInterpolation ipol;
	ipol.Name.reserve ( 64 );
	u32 i;

	AnimList.clear();
	FrameCount = 0;

	SHalflifeSequence *seq = (SHalflifeSequence*) ((u8*) Header + Header->seqindex);
	for ( i = 0; i < Header->numseq; i++)
	{
		ipol.Name = seq[i].label;
		ipol.StartFrame = FrameCount;
		ipol.Frames = core::max_ ( 1, seq[i].numframes - 1 );
		ipol.EndFrame = ipol.StartFrame + ipol.Frames - 1;
		ipol.FramesPerSecond = seq[i].fps;
		ipol.AnimationType = seq[i].flags & STUDIO_LOOPING ? EAMT_LOOPING : EAMT_WAYPOINT;
		AnimList.push_back ( ipol );

		FrameCount += ipol.Frames;
	}

	// initBoneControllers

/*
	SHalflifeBoneController *bonecontroller = (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex);
	for ( i = 0; i < Header->numbonecontrollers; i++)
	{
		printf ( "BoneController%d index:%d%s range:%f - %f\n",
			i,
			bonecontroller[i].index, bonecontroller[i].index == MOUTH_CONTROLLER ? " (Mouth)": "",
			bonecontroller[i].start,bonecontroller[i].end
			);
	}

	// initSkins

	for (i = 0; i < TextureHeader->numskinfamilies; i++)
	{
		printf ( "Skin%d\n", i + 1);
	}
*/

	// initBodyparts

	u32 meshBuffer = 0;
	BodyList.clear();
	SHalflifeBody *body = (SHalflifeBody *) ((u8*) Header + Header->bodypartindex);
	for ( i = 0; i < Header->numbodyparts; ++i)
	{
		BodyPart part;
		part.name = body[i].name;
		part.defaultModel = core::max_ ( 0, (s32) body[i].base - 1 );

		SHalflifeModel * model = (SHalflifeModel *)((u8*) Header + body[i].modelindex);
		for ( u32 g = 0; g < body[i].nummodels; ++g)
		{
			SubModel sub;
			sub.name = model[g].name;
			sub.startBuffer = meshBuffer;
			sub.endBuffer = sub.startBuffer + model[g].nummesh;
			sub.state = g == part.defaultModel;
			part.model.push_back ( sub );
			meshBuffer += model[g].nummesh;
		}
		BodyList.push_back ( part );
	}

	SequenceIndex = 0;
	CurrentFrame = 0.f;

	SetController (0, 0.f);
	SetController (1, 0.f);
	SetController (2, 0.f);
	SetController (3, 0.f);
	SetController (MOUTH_CONTROLLER, 0.f);

	SetSkin (0);

	// init Meshbuffers

	io::path store;

	const SHalflifeTexture *tex = (SHalflifeTexture *) ((u8*) TextureHeader + TextureHeader->textureindex);
	const u16 *skinref = (u16 *)((u8*)TextureHeader + TextureHeader->skinindex);
	if (SkinGroupSelection != 0 && SkinGroupSelection < TextureHeader->numskinfamilies)
		skinref += (SkinGroupSelection * TextureHeader->numskinref);

	io::path fname;
	io::path ext;


	core::vector2df tex_scale;
	core::vector2di tex_trans ( 0, 0 );

#ifdef HL_TEXTURE_ATLAS
	TextureAtlas.getScale ( tex_scale );
#endif

	for ( u32 bodypart=0 ; bodypart < Header->numbodyparts ; ++bodypart)
	{
		const SHalflifeBody *body = (SHalflifeBody *)((u8*) Header + Header->bodypartindex) + bodypart;

		for ( u32 modelnr = 0; modelnr < body->nummodels; ++modelnr )
		{
			const SHalflifeModel *model = (SHalflifeModel *)((u8*) Header + body->modelindex) + modelnr;
#if 0
			const vec3_hl *studioverts = (vec3_hl *)((u8*)Header + model->vertindex);
			const vec3_hl *studionorms = (vec3_hl *)((u8*)Header + model->normindex);
#endif
			for (i = 0; i < model->nummesh; ++i)
			{
				const SHalflifeMesh *mesh = (SHalflifeMesh *)((u8*)Header + model->meshindex) + i;
				const SHalflifeTexture *currentex = &tex[skinref[mesh->skinref]];

#ifdef HL_TEXTURE_ATLAS
				TextureAtlas.getTranslation ( currentex->name, tex_trans );
#else
				tex_scale.X = 1.f/(f32)currentex->width;
				tex_scale.Y = 1.f/(f32)currentex->height;
#endif

				SMeshBuffer * buffer = new SMeshBuffer();

				// count index vertex size	indexcount = mesh->numtris * 3

				u32 indexCount = 0;
				u32 vertexCount = 0;

				const s16 *tricmd = (s16*)((u8*)Header + mesh->triindex);
				s32 c;
				while ( (c = *(tricmd++)) )
				{
					if (c < 0)
						c = -c;

					indexCount += ( c - 2 ) * 3;
					vertexCount += c;
					tricmd += ( 4 * c );
				}

				// indices

				buffer->Indices.set_used ( indexCount );
				buffer->Vertices.set_used ( vertexCount );

				// fill in static indices and vertex

				u16 *index = buffer->Indices.pointer();
				video::S3DVertex * v = buffer->Vertices.pointer();

				// blow up gl_triangle_fan/gl_triangle_strip to indexed triangle list

				E_PRIMITIVE_TYPE type;
				vertexCount = 0;
				indexCount = 0;
				tricmd = (s16*)((u8*)Header + mesh->triindex);
				while ( (c = *(tricmd++)) )
				{
					if (c < 0)
					{
						// triangle fan

						c = -c;
						type = EPT_TRIANGLE_FAN;
					}
					else
					{
						type = EPT_TRIANGLE_STRIP;
					}

					for ( s32 g = 0; g < c; ++g, v += 1, tricmd += 4 )
					{
						// fill vertex

	#if 0
						const f32 *av = studioverts[tricmd[0]];
						v->Pos.X = av[0];
						v->Pos.Z = av[1];
						v->Pos.Y = av[2];

						av = studionorms[tricmd[1]];
						v->Normal.X = av[0];
						v->Normal.Z = av[1];
						v->Normal.Y = av[2];

	#endif
						v->Normal.X = 0.f;
						v->Normal.Z = 0.f;
						v->Normal.Y = 1.f;

						v->TCoords.X = (tex_trans.X + tricmd[2])*tex_scale.X;
						v->TCoords.Y = (tex_trans.Y + tricmd[3])*tex_scale.Y;

						v->Color.color = 0xFFFFFFFF;

						// fill index

						if ( g < c - 2 )
						{
							if ( type == EPT_TRIANGLE_FAN )
							{
								index[indexCount+0] = vertexCount;
								index[indexCount+1] = vertexCount+g+1;
								index[indexCount+2] = vertexCount+g+2;
							}
							else
							{
								if ( g & 1 )
								{
									index[indexCount+0] = vertexCount+g+1;
									index[indexCount+1] = vertexCount+g+0;
									index[indexCount+2] = vertexCount+g+2;
								}
								else
								{
									index[indexCount+0] = vertexCount+g+0;
									index[indexCount+1] = vertexCount+g+1;
									index[indexCount+2] = vertexCount+g+2;
								}
							}

							indexCount += 3;
						}
					}

					vertexCount += c;
				}

				// material

				video::SMaterial &m = buffer->getMaterial();

				m.MaterialType = video::EMT_SOLID;
				m.BackfaceCulling = true;

				if ( currentex->flags & STUDIO_NF_CHROME )
				{
					// don't know what to do with chrome here

				}

#ifdef HL_TEXTURE_ATLAS
				store = TextureBaseName + "atlas";
#else
				core::splitFilename ( currentex->name, 0, &fname, &ext );
				store = TextureBaseName + fname;
#endif
				m.TextureLayer[0].Texture = SceneManager->getVideoDriver()->getTexture ( store );
				m.Lighting = false;

				MeshIPol.addMeshBuffer ( buffer );
				buffer->drop ();
			} // mesh

		} // model

	} // body part


}


/*!
*/
void CAnimatedMeshHalfLife::buildVertices ()
{
/*
	const u16 *skinref = (u16 *)((u8*)TextureHeader + TextureHeader->skinindex);
	if (SkinGroupSelection != 0 && SkinGroupSelection < TextureHeader->numskinfamilies)
		skinref += (SkinGroupSelection * TextureHeader->numskinref);
*/
	u32 i;
	s32 c,g;
	const s16 *tricmd;

	u32 meshBufferNr = 0;
	for ( u32 bodypart = 0 ; bodypart < Header->numbodyparts; ++bodypart)
	{
		const SHalflifeBody *body = (SHalflifeBody *)((u8*) Header + Header->bodypartindex) + bodypart;

		for ( u32 modelnr = 0; modelnr < body->nummodels; ++modelnr )
		{
			const SHalflifeModel *model = (SHalflifeModel *)((u8*) Header + body->modelindex) + modelnr;

			const u8 *vertbone = ((u8*)Header + model->vertinfoindex);
			const u8 *normbone = ((u8*)Header + model->norminfoindex);

			const vec3_hl *studioverts = (vec3_hl *)((u8*)Header + model->vertindex);
			const vec3_hl *studionorms = (vec3_hl *)((u8*)Header + model->normindex);

			for ( i = 0; i < model->numverts; i++)
			{
				VectorTransform ( studioverts[i],  BoneTransform[vertbone[i]], TransformedVerts[i]  );
			}
	/*
			for ( i = 0; i < model->numnorms; i++)
			{
				VectorTransform ( studionorms[i],  BoneTransform[normbone[i]], TransformedNormals[i]  );
			}
	*/
			for (i = 0; i < model->nummesh; i++)
			{
				const SHalflifeMesh *mesh = (SHalflifeMesh *)((u8*)Header + model->meshindex) + i;

				IMeshBuffer * buffer = MeshIPol.getMeshBuffer ( meshBufferNr++ );
				video::S3DVertex* v = (video::S3DVertex* ) buffer->getVertices();

				tricmd = (s16*)((u8*)Header + mesh->triindex);
				while ( (c = *(tricmd++)) )
				{
					if (c < 0)
						c = -c;

					for ( g = 0; g < c; ++g, v += 1, tricmd += 4 )
					{
						// fill vertex

						const f32 *av = TransformedVerts[tricmd[0]];
						v->Pos.X = av[0];
						v->Pos.Z = av[1];
						v->Pos.Y = av[2];
	/*
						av = TransformedNormals[tricmd[1]];
						v->Normal.X = av[0];
						v->Normal.Z = av[1];
						v->Normal.Y = av[2];
						//v->Normal.normalize();

	*/
					}
				} // tricmd

			} // nummesh

		} // model

	} // bodypart

}



/*!
	render Bones
*/
void CAnimatedMeshHalfLife::renderModel (  u32 param, IVideoDriver * driver, const core::matrix4 &absoluteTransformation)
{
	SHalflifeBone *bone = (SHalflifeBone *) ((u8 *) Header + Header->boneindex);

	video::SColor blue ( 0xFF000080 );
	video::SColor red ( 0xFF800000 );
	video::SColor yellow ( 0xFF808000 );
	video::SColor cyan ( 0xFF008080 );

	core::aabbox3df box;

	u32 i;
	for ( i = 0; i < Header->numbones; i++)
	{
		if (bone[i].parent >= 0)
		{
			getBoneVector ( box.MinEdge, bone[i].parent );
			getBoneVector ( box.MaxEdge, i );
			driver->draw3DLine ( box.MinEdge, box.MaxEdge, blue );

			// draw parent bone node

			if (bone[bone[i].parent].parent >=0 )
			{
				getBoneBox ( box, bone[i].parent );
				driver->draw3DBox ( box, blue );
			}
			getBoneBox ( box, i );
			driver->draw3DBox ( box, blue );
		}
		else
		{
			// draw parent bone node

			getBoneBox ( box, i, 1.f );
			driver->draw3DBox ( box , red );
		}
	}

	// attachements

	SHalfelifeAttachment *attach = (SHalfelifeAttachment *) ((u8*) Header + Header->attachmentindex);
	core::vector3df v[8];
	for ( i = 0; i < Header->numattachments; i++)
	{
		getTransformedBoneVector ( v[0],attach[i].bone,attach[i].org );
		getTransformedBoneVector ( v[1],attach[i].bone,attach[i].vectors[0] );
		getTransformedBoneVector ( v[2],attach[i].bone,attach[i].vectors[1] );
		getTransformedBoneVector ( v[3],attach[i].bone,attach[i].vectors[2] );
		driver->draw3DLine ( v[0], v[1], cyan );
		driver->draw3DLine ( v[0], v[2], cyan );
		driver->draw3DLine ( v[0], v[3], cyan );
	}

	// hit boxes

	SHalflifeBBox *hitbox = (SHalflifeBBox *) ((u8*) Header + Header->hitboxindex);
	f32 *bbmin,*bbmax;
	vec3_hl v2[8];
	for (i = 0; i < Header->numhitboxes; i++)
	{
		bbmin = hitbox[i].bbmin;
		bbmax = hitbox[i].bbmax;

		v2[0][0] = bbmin[0];
		v2[0][1] = bbmax[1];
		v2[0][2] = bbmin[2];

		v2[1][0] = bbmin[0];
		v2[1][1] = bbmin[1];
		v2[1][2] = bbmin[2];

		v2[2][0] = bbmax[0];
		v2[2][1] = bbmax[1];
		v2[2][2] = bbmin[2];

		v2[3][0] = bbmax[0];
		v2[3][1] = bbmin[1];
		v2[3][2] = bbmin[2];

		v2[4][0] = bbmax[0];
		v2[4][1] = bbmax[1];
		v2[4][2] = bbmax[2];

		v2[5][0] = bbmax[0];
		v2[5][1] = bbmin[1];
		v2[5][2] = bbmax[2];

		v2[6][0] = bbmin[0];
		v2[6][1] = bbmax[1];
		v2[6][2] = bbmax[2];

		v2[7][0] = bbmin[0];
		v2[7][1] = bbmin[1];
		v2[7][2] = bbmax[2];


		for ( u32 g = 0; g < 8; ++g )
			getTransformedBoneVector ( v[g],hitbox[i].bone,v2[g] );

		driver->draw3DLine(v[0], v[1], yellow);
		driver->draw3DLine(v[1], v[3], yellow);
		driver->draw3DLine(v[3], v[2], yellow);
		driver->draw3DLine(v[2], v[0], yellow);

		driver->draw3DLine(v[4], v[5], yellow);
		driver->draw3DLine(v[5], v[7], yellow);
		driver->draw3DLine(v[7], v[6], yellow);
		driver->draw3DLine(v[6], v[4], yellow);

		driver->draw3DLine(v[0], v[6], yellow);
		driver->draw3DLine(v[1], v[7], yellow);
		driver->draw3DLine(v[3], v[5], yellow);
		driver->draw3DLine(v[2], v[4], yellow);
	}
}

//! Returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail.

IMesh* CAnimatedMeshHalfLife::getMesh(s32 frameInt, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
	f32 frame = frameInt + (detailLevel * 0.001f);
	u32 frameA = core::floor32 ( frame );
	f32 blend = core::fract ( frame );

	u32 i;

	SHalflifeSequence *seq = (SHalflifeSequence*) ((u8*) Header + Header->seqindex);

	// find SequenceIndex from summed list

	u32 frameCount = 0;
	for ( i = 0; i < Header->numseq; i++)
	{
		u32 val = core::max_ ( 1, seq[i].numframes - 1 );
		if ( frameCount + val > frameA  )
		{
			SequenceIndex = i;
			CurrentFrame = frame - frameCount;
			break;
		}
		frameCount += val;
	}

	seq += SequenceIndex;

	//SetBodyPart ( 1, 1 );

	setUpBones ();
	buildVertices();

	MeshIPol.BoundingBox.MinEdge.X = seq->bbmin[0];
	MeshIPol.BoundingBox.MinEdge.Z = seq->bbmin[1];
	MeshIPol.BoundingBox.MinEdge.Y = seq->bbmin[2];

	MeshIPol.BoundingBox.MaxEdge.X = seq->bbmax[0];
	MeshIPol.BoundingBox.MaxEdge.Z = seq->bbmax[1];
	MeshIPol.BoundingBox.MaxEdge.Y = seq->bbmax[2];

	return &MeshIPol;
}

/*!
*/
void CAnimatedMeshHalfLife::initData ()
{
	u32 i;

	Header = 0;
	TextureHeader = 0;
	OwnTexModel = false;

	for ( i = 0; i < 32; ++i )
		AnimationHeader[i] = 0;

	SequenceIndex = 0;
	CurrentFrame = 0.f;

	for ( i = 0; i < 5; ++i )
		BoneController[i] = 0;

	for ( i = 0; i < 2; ++i )
		Blending[i] = 0;

	SkinGroupSelection = 0;

	AnimList.clear();
	FrameCount = 0;

	MeshIPol.clear();

#ifdef HL_TEXTURE_ATLAS
	TextureAtlas.release();
#endif
}

/*!
*/
void CAnimatedMeshHalfLife::freeModel ()
{
	delete [] (u8*) Header;
	if (OwnTexModel )
		delete [] (u8*) TextureHeader;

	for ( u32 i = 0; i < 32; ++i )
		delete [] (u8*) AnimationHeader[i];
}



/*!
*/
void STextureAtlas::release ()
{
	for (u32 i = 0; i < atlas.size(); i++)
	{
		if ( atlas[i].image )
		{
			atlas[i].image->drop ();
			atlas[i].image = 0;
		}
	}
	Master = 0;
}

/*!
*/
void STextureAtlas::addSource ( const c8 * name, video::IImage * image )
{
	TextureAtlasEntry entry;
	entry.name = name;
	entry.image = image;
	entry.width = image->getDimension().Width;
	entry.height = image->getDimension().Height;
	entry.pos.X = 0;
	entry.pos.Y = 0;
	atlas.push_back ( entry );
}

/*!
*/
void STextureAtlas::getScale(core::vector2df& scale)
{
	for (s32 i = static_cast<s32>(atlas.size()) - 1; i >= 0; --i)
	{
		if ( atlas[i].name == "_merged_" )
		{
			scale.X = 1.f / atlas[i].width;
			scale.Y = 1.f / atlas[i].height;
			return;
		}
	}
	scale.X = 1.f;
	scale.Y = 1.f;
}

/*!
*/
void STextureAtlas::getTranslation ( const c8 * name, core::vector2di &pos )
{
	u32 i = 0;

	for ( u32 i = 0; i < atlas.size(); ++i)
	{
		if ( atlas[i].name == name )
		{
			pos = atlas[i].pos;
			return;
		}
	}
}

/*!
*/
void STextureAtlas::create ( u32 border, E_TEXTURE_CLAMP texmode)
{
	u32 i = 0;
	u32 w = 0;
	u32 w2;
	u32 h2;
	u32 h;
	u32 wsum;
	u32 hsum = 0;
	ECOLOR_FORMAT format = ECF_R8G8B8;

	const s32 frame = core::s32_max ( 0, (border - 1 ) / 2 );

	// sort for biggest coming first

	atlas.sort();

	// split size

	wsum = frame;
	for (i = 0; i < atlas.size(); i++)
	{
		// make space

		w2 = atlas[i].width + border;

		// align

		w2 = (w2 + 1) & ~1;
		wsum += w2;
	}
	u32 splitsize = 256;
	if ( wsum > 512 )
		splitsize = 512;

	wsum = frame;
	hsum = frame;
	w = frame;
	h = 0;
	for (i = 0; i < atlas.size(); i++)
	{
		if ( atlas[i].image->getColorFormat() == ECF_A8R8G8B8 )
		{
			format = ECF_A8R8G8B8;
		}

		// make space

		w2 = atlas[i].width + border;
		h2 = atlas[i].height + border;

		// align

		w2 = (w2 + 1) & ~1;
		h2 = (h2 + 1) & ~1;

		h = core::s32_max ( h, h2 );

		if ( w + w2 >= splitsize )
		{
			hsum += h;
			wsum = core::s32_max ( wsum, w );
			h = h2;
			w = frame;
		}

		atlas[i].pos.X = w;
		atlas[i].pos.Y = hsum;

		w += w2;

	}
	hsum += h;
	wsum = core::s32_max ( wsum, w );

	// build image

	core::dimension2d<u32> dim = core::dimension2d<u32>( wsum, hsum ).getOptimalSize();
	IImage* master = new CImage( format, dim );
	master->fill ( 0 );


	video::SColor col[2];

	static const u8 wrap[][4] =
	{
		{1, 0 },	// ETC_REPEAT

		{0, 1 },	// ETC_CLAMP

		{0, 1 },	// ETC_CLAMP_TO_EDGE

		{0, 1 }	// ETC_MIRROR

	};

	s32 a,b;
	for (i = 0; i < atlas.size(); i++)
	{
		atlas[i].image->copyTo ( master, atlas[i].pos );

		// clamp/wrap ( copy edges, filtering needs it )


		for ( b = 0; b < frame; ++b )
		{
			for ( a = 0 - b; a <= (s32) atlas[i].width + b; ++a )
			{
				col[0] = atlas[i].image->getPixel ( core::s32_clamp ( a, 0, atlas[i].width - 1 ), 0 );
				col[1] = atlas[i].image->getPixel ( core::s32_clamp ( a, 0, atlas[i].width - 1 ), atlas[i].height - 1 );

				master->setPixel ( atlas[i].pos.X + a, atlas[i].pos.Y + ( b + 1 ) * -1, col[wrap[texmode][0]] );
				master->setPixel ( atlas[i].pos.X + a, atlas[i].pos.Y + atlas[i].height - 1 + ( b + 1 ) * 1, col[wrap[texmode][1]] );
			}

			for ( a = -1 - b; a <= (s32) atlas[i].height + b; ++a )
			{
				col[0] = atlas[i].image->getPixel ( 0, core::s32_clamp ( a, 0, atlas[i].height - 1 ) );
				col[1] = atlas[i].image->getPixel ( atlas[i].width - 1, core::s32_clamp ( a, 0, atlas[i].height - 1 ) );

				master->setPixel ( atlas[i].pos.X + ( b + 1 ) * -1, atlas[i].pos.Y + a, col[wrap[texmode][0]] );
				master->setPixel ( atlas[i].pos.X + atlas[i].width + b, atlas[i].pos.Y + a, col[wrap[texmode][1]] );
			}
		}
	}

	addSource ( "_merged_", master );
	Master = master;
}


/*!
*/
SHalflifeHeader * CAnimatedMeshHalfLife::loadModel( io::IReadFile* file, const io::path &filename )
{
	bool closefile = false;

	if ( 0 == file )
	{
		file = SceneManager->getFileSystem()->createAndOpenFile ( filename );
		closefile = true;
	}

	if ( 0 == file )
		return 0;

	u8 * pin = new u8 [ file->getSize() ];
	file->read ( pin, file->getSize() );

	SHalflifeHeader * header = (SHalflifeHeader*) pin;

	const bool idst = 0 == strncmp ( header->id, "IDST", 4);
	const bool idsq = 0 == strncmp ( header->id, "IDSQ", 4);

	if ( (!idst && !idsq) || (idsq && !Header) )
	{
		os::Printer::log("MDL Halflife Loader: Wrong file header", file->getFileName(), ELL_WARNING);
		if ( closefile )
		{
			file->drop();
			file = 0;
		}
		delete [] pin;
		return false;
	}

	// don't know the real header.. idsg might be different

	if (header->textureindex && idst )
	{
		io::path fname;
		io::path ext;
		io::path path;
		io::path store;

		core::splitFilename ( file->getFileName(), &path, &fname, &ext );
		TextureBaseName = path + fname + "_";

		SHalflifeTexture *tex = (SHalflifeTexture *)(pin + header->textureindex);
		u32 i;

		u32 *palette = new u32[256];
		for (i = 0; i < header->numtextures; i++)
		{
			const u8 *src = pin + tex[i].index;

			// convert rgb to argb palette

			{
				const u8 *pal = src + tex[i].width * tex[i].height;
				for( u32 g=0; g<256; ++g )
				{
					palette[g] = 0xFF000000 | pal[0] << 16 | pal[1] << 8 | pal[2];
					pal += 3;
				}
			}

			IImage* image = new CImage( ECF_R8G8B8, core::dimension2d<u32> ( tex[i].width, tex[i].height ) );

			CColorConverter::convert8BitTo24Bit(src, (u8*)image->lock(), tex[i].width, tex[i].height, (u8*) palette, 0, false);
			image->unlock();


#if 0
			core::splitFilename ( tex[i].name, 0, &fname, 0 );
			io::path store = io::path ( "c:/h2/convert/" ) + fname + ".bmp";
			SceneManager->getVideoDriver()->writeImageToFile ( image, store );
#endif

#ifdef HL_TEXTURE_ATLAS
			TextureAtlas.addSource ( tex[i].name, image );
#else
			core::splitFilename ( tex[i].name, 0, &fname, &ext );
			store = TextureBaseName + fname;

			SceneManager->getVideoDriver()->addTexture ( store, image );
			image->drop();
#endif
		}
		delete [] palette;

#ifdef HL_TEXTURE_ATLAS
		TextureAtlas.create ( 2 * 2 + 1, ETC_CLAMP );

		store = TextureBaseName + "atlas";
		SceneManager->getVideoDriver()->addTexture ( store, TextureAtlas.Master );

	#if 0
		core::splitFilename ( store, 0, &fname, 0 );
		store = io::path ( "c:/h2/convert/" ) + fname + ".bmp";
		SceneManager->getVideoDriver()->writeImageToFile ( TextureAtlas.Master, store );
	#endif
		TextureAtlas.release ();
#endif
	}

	if (!Header)
		Header = header;

	if ( closefile )
	{
		file->drop();
		file = 0;
	}

	return header;
}


/*!
*/
f32 CAnimatedMeshHalfLife::SetController( s32 controllerIndex, f32 value )
{
	if (!Header)
		return 0.f;

	SHalflifeBoneController *bonecontroller = (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex);

	// find first controller that matches the index

	u32 i;
	for (i = 0; i < Header->numbonecontrollers; i++, bonecontroller++)
	{
		if (bonecontroller->index == controllerIndex)
			break;
	}
	if (i >= Header->numbonecontrollers)
		return value;

	// wrap 0..360 if it's a rotational controller

	if (bonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
	{
		// ugly hack, invert value if end < start

		if (bonecontroller->end < bonecontroller->start)
			value = -value;

		// does the controller not wrap?

		if (bonecontroller->start + 359.f >= bonecontroller->end)
		{
			if (value > ((bonecontroller->start + bonecontroller->end) / 2.f) + 180.f)
				value = value - 360.f;
			if (value < ((bonecontroller->start + bonecontroller->end) / 2.f) - 180.f)
				value = value + 360.f;
		}
		else
		{
			if (value > 360.f)
				value = value - (s32)(value / 360.f) * 360.f;
			else if (value < 0.f)
				value = value + (s32)((value / -360.f) + 1) * 360.f;
		}
	}

	s32 range = controllerIndex == MOUTH_CONTROLLER ? 64 : 255;

	s32 setting = (s32) ( (f32) range * (value - bonecontroller->start) / (bonecontroller->end - bonecontroller->start));

	if (setting < 0) setting = 0;
	if (setting > range) setting = range;

	BoneController[controllerIndex] = setting;

	return setting * (1.f / (f32) range ) * (bonecontroller->end - bonecontroller->start) + bonecontroller->start;
}



/*!
*/
u32 CAnimatedMeshHalfLife::SetSkin( u32 value )
{
	if (value < Header->numskinfamilies)
		SkinGroupSelection = value;

	return SkinGroupSelection;
}

/*!
*/
bool CAnimatedMeshHalfLife::postLoadModel( const io::path &filename )
{
	io::path path;
	io::path texname;
	io::path submodel;

	core::splitFilename ( filename ,&path, &texname, 0 );

	// preload textures

	if (Header->numtextures == 0)
	{
		submodel = path + texname + "T.mdl";
		TextureHeader = loadModel( 0, submodel );
		if (!TextureHeader)
			return false;
		OwnTexModel = true;
	}
	else
	{
		TextureHeader = Header;
		OwnTexModel = false;
	}

	u32 i;

	// preload animations

	if (Header->numseqgroups > 1)
	{
		c8 seq[8];
		for ( i = 1; i < Header->numseqgroups; i++)
		{
			snprintf( seq, 8, "%02d.mdl", i );
			submodel = path + texname + seq;

			AnimationHeader[i] = loadModel( 0, submodel );
			if (!AnimationHeader[i])
				return false;
		}
	}

	return true;
}



/*!
*/
void CAnimatedMeshHalfLife::dumpModelInfo ( u32 level )
{
	u8 *phdr = (u8*) Header;
	SHalflifeHeader * hdr = Header;
	u32 i;

	if ( level == 0 )
	{
		printf (
			"Bones: %d\n"
			"Bone Controllers: %d\n"
			"Hit Boxes: %d\n"
			"Sequences: %d\n"
			"Sequence Groups: %d\n",
			hdr->numbones,
			hdr->numbonecontrollers,
			hdr->numhitboxes,
			hdr->numseq,
			hdr->numseqgroups
			);
		printf (
			"Textures: %d\n"
			"Skin Families: %d\n"
			"Bodyparts: %d\n"
			"Attachments: %d\n"
			"Transitions: %d\n",
			hdr->numtextures,
			hdr->numskinfamilies,
			hdr->numbodyparts,
			hdr->numattachments,
			hdr->numtransitions);
		return;
	}


	printf("id: %c%c%c%c\n", phdr[0], phdr[1], phdr[2], phdr[3]);
	printf("version: %d\n", hdr->version);
	printf("name: \"%s\"\n", hdr->name);
	printf("length: %d\n\n", hdr->length);

	printf("eyeposition: %f %f %f\n", hdr->eyeposition[0], hdr->eyeposition[1], hdr->eyeposition[2]);
	printf("min: %f %f %f\n", hdr->min[0], hdr->min[1], hdr->min[2]);
	printf("max: %f %f %f\n", hdr->max[0], hdr->max[1], hdr->max[2]);
	printf("bbmin: %f %f %f\n", hdr->bbmin[0], hdr->bbmin[1], hdr->bbmin[2]);
	printf("bbmax: %f %f %f\n", hdr->bbmax[0], hdr->bbmax[1], hdr->bbmax[2]);

	printf("flags: %d\n\n", hdr->flags);

	printf("numbones: %d\n", hdr->numbones);
	for (i = 0; i < hdr->numbones; i++)
	{
		SHalflifeBone *bone = (SHalflifeBone *) (phdr + hdr->boneindex);
		printf("bone %d.name: \"%s\"\n", i + 1, bone[i].name);
		printf("bone %d.parent: %d\n", i + 1, bone[i].parent);
		printf("bone %d.flags: %d\n", i + 1, bone[i].flags);
		printf("bone %d.bonecontroller: %d %d %d %d %d %d\n", i + 1, bone[i].bonecontroller[0], bone[i].bonecontroller[1], bone[i].bonecontroller[2], bone[i].bonecontroller[3], bone[i].bonecontroller[4], bone[i].bonecontroller[5]);
		printf("bone %d.value: %f %f %f %f %f %f\n", i + 1, bone[i].value[0], bone[i].value[1], bone[i].value[2], bone[i].value[3], bone[i].value[4], bone[i].value[5]);
		printf("bone %d.scale: %f %f %f %f %f %f\n", i + 1, bone[i].scale[0], bone[i].scale[1], bone[i].scale[2], bone[i].scale[3], bone[i].scale[4], bone[i].scale[5]);
	}

	printf("\nnumbonecontrollers: %d\n", hdr->numbonecontrollers);
	SHalflifeBoneController *bonecontrollers = (SHalflifeBoneController *) (phdr + hdr->bonecontrollerindex);
	for (i = 0; i < hdr->numbonecontrollers; i++)
	{
		printf("bonecontroller %d.bone: %d\n", i + 1, bonecontrollers[i].bone);
		printf("bonecontroller %d.type: %d\n", i + 1, bonecontrollers[i].type);
		printf("bonecontroller %d.start: %f\n", i + 1, bonecontrollers[i].start);
		printf("bonecontroller %d.end: %f\n", i + 1, bonecontrollers[i].end);
		printf("bonecontroller %d.rest: %d\n", i + 1, bonecontrollers[i].rest);
		printf("bonecontroller %d.index: %d\n", i + 1, bonecontrollers[i].index);
	}

	printf("\nnumhitboxes: %d\n", hdr->numhitboxes);
	SHalflifeBBox *box = (SHalflifeBBox *) (phdr + hdr->hitboxindex);
	for (i = 0; i < hdr->numhitboxes; i++)
	{
		printf("hitbox %d.bone: %d\n", i + 1, box[i].bone);
		printf("hitbox %d.group: %d\n", i + 1, box[i].group);
		printf("hitbox %d.bbmin: %f %f %f\n", i + 1, box[i].bbmin[0], box[i].bbmin[1], box[i].bbmin[2]);
		printf("hitbox %d.bbmax: %f %f %f\n", i + 1, box[i].bbmax[0], box[i].bbmax[1], box[i].bbmax[2]);
	}

	printf("\nnumseq: %d\n", hdr->numseq);
	SHalflifeSequence *seq = (SHalflifeSequence *) (phdr + hdr->seqindex);
	for (i = 0; i < hdr->numseq; i++)
	{
		printf("seqdesc %d.label: \"%s\"\n", i + 1, seq[i].label);
		printf("seqdesc %d.fps: %f\n", i + 1, seq[i].fps);
		printf("seqdesc %d.flags: %d\n", i + 1, seq[i].flags);
		printf("<...>\n");
	}

	printf("\nnumseqgroups: %d\n", hdr->numseqgroups);
	for (i = 0; i < hdr->numseqgroups; i++)
	{
		SHalflifeSequenceGroup *group = (SHalflifeSequenceGroup *) (phdr + hdr->seqgroupindex);
		printf("\nseqgroup %d.label: \"%s\"\n", i + 1, group[i].label);
		printf("\nseqgroup %d.namel: \"%s\"\n", i + 1, group[i].name);
		printf("\nseqgroup %d.data: %d\n", i + 1, group[i].data);
	}

	printf("\nnumskinref: %d\n", hdr->numskinref);
	printf("numskinfamilies: %d\n", hdr->numskinfamilies);

	printf("\nnumbodyparts: %d\n", hdr->numbodyparts);
	SHalflifeBody *pbodyparts = (SHalflifeBody*) ((u8*) hdr + hdr->bodypartindex);
	for (i = 0; i < hdr->numbodyparts; i++)
	{
		printf("bodypart %d.name: \"%s\"\n", i + 1, pbodyparts[i].name);
		printf("bodypart %d.nummodels: %d\n", i + 1, pbodyparts[i].nummodels);
		printf("bodypart %d.base: %d\n", i + 1, pbodyparts[i].base);
		printf("bodypart %d.modelindex: %d\n", i + 1, pbodyparts[i].modelindex);
	}

	printf("\nnumattachments: %d\n", hdr->numattachments);
	for (i = 0; i < hdr->numattachments; i++)
	{
		SHalfelifeAttachment *attach = (SHalfelifeAttachment *) ((u8*) hdr + hdr->attachmentindex);
		printf("attachment %d.name: \"%s\"\n", i + 1, attach[i].name);
	}

	hdr = TextureHeader;
	printf("\nnumtextures: %d\n", hdr->numtextures);
	printf("textureindex: %d\n", hdr->textureindex);
	printf("texturedataindex: %d\n", hdr->texturedataindex);
	SHalflifeTexture *ptextures = (SHalflifeTexture *) ((u8*) hdr + hdr->textureindex);
	for (i = 0; i < hdr->numtextures; i++)
	{
		printf("texture %d.name: \"%s\"\n", i + 1, ptextures[i].name);
		printf("texture %d.flags: %d\n", i + 1, ptextures[i].flags);
		printf("texture %d.width: %d\n", i + 1, ptextures[i].width);
		printf("texture %d.height: %d\n", i + 1, ptextures[i].height);
		printf("texture %d.index: %d\n", i + 1, ptextures[i].index);
	}
}

/*!
*/
void CAnimatedMeshHalfLife::ExtractBbox( s32 sequence, core::aabbox3df &box )
{
	SHalflifeSequence *seq = (SHalflifeSequence *)((u8*)Header + Header->seqindex) + sequence;

	box.MinEdge.X = seq[0].bbmin[0];
	box.MinEdge.Y = seq[0].bbmin[1];
	box.MinEdge.Z = seq[0].bbmin[2];

	box.MaxEdge.X = seq[0].bbmax[0];
	box.MaxEdge.Y = seq[0].bbmax[1];
	box.MaxEdge.Z = seq[0].bbmax[2];
}



/*!
*/
void CAnimatedMeshHalfLife::calcBoneAdj()
{
	u32 j;
	s32 i;
	f32 value;
	SHalflifeBoneController *bonecontroller;

	bonecontroller = (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex);

	for (j = 0; j < Header->numbonecontrollers; j++)
	{
		i = bonecontroller[j].index;
		f32 range = i <= 3 ? 255.f : 64.f;
		// check for 360% wrapping

		if (bonecontroller[j].type & STUDIO_RLOOP)
		{
			value = BoneController[i] * (360.f/256.f) + bonecontroller[j].start;
		}
		else
		{
			value = BoneController[i] / range;
			if (value < 0.f) value = 0.f;
			if (value > 1.f) value = 1.f;
			value = (1.f - value) * bonecontroller[j].start + value * bonecontroller[j].end;
		}

		switch(bonecontroller[j].type & STUDIO_TYPES)
		{
		case STUDIO_XR:
		case STUDIO_YR:
		case STUDIO_ZR:
			BoneAdj[j] = value * (core::PI / 180.f);
			break;
		case STUDIO_X:
		case STUDIO_Y:
		case STUDIO_Z:
			BoneAdj[j] = value;
			break;
		}
	}
}

/*!
*/
void CAnimatedMeshHalfLife::calcBoneQuaternion( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *q ) const
{
	s32					j, k;
	vec4_hl				q1, q2;
	vec3_hl				angle1, angle2;
	SHalfelifeAnimationFrame	*animvalue;

	for (j = 0; j < 3; j++)
	{
		if (anim->offset[j+3] == 0)
		{
			angle2[j] = angle1[j] = bone->value[j+3]; // default;

		}
		else
		{
			animvalue = (SHalfelifeAnimationFrame *)((u8*)anim + anim->offset[j+3]);
			k = frame;
			while (animvalue->num.total <= k)
			{
				k -= animvalue->num.total;
				animvalue += animvalue->num.valid + 1;
			}
			// Bah, missing blend!

			if (animvalue->num.valid > k)
			{
				angle1[j] = animvalue[k+1].value;

				if (animvalue->num.valid > k + 1)
				{
					angle2[j] = animvalue[k+2].value;
				}
				else
				{
					if (animvalue->num.total > k + 1)
						angle2[j] = angle1[j];
					else
						angle2[j] = animvalue[animvalue->num.valid+2].value;
				}
			}
			else
			{
				angle1[j] = animvalue[animvalue->num.valid].value;
				if (animvalue->num.total > k + 1)
				{
					angle2[j] = angle1[j];
				}
				else
				{
					angle2[j] = animvalue[animvalue->num.valid + 2].value;
				}
			}
			angle1[j] = bone->value[j+3] + angle1[j] * bone->scale[j+3];
			angle2[j] = bone->value[j+3] + angle2[j] * bone->scale[j+3];
		}

		if (bone->bonecontroller[j+3] != -1)
		{
			angle1[j] += BoneAdj[bone->bonecontroller[j+3]];
			angle2[j] += BoneAdj[bone->bonecontroller[j+3]];
		}
	}

	if (!VectorCompare( angle1, angle2 ))
	{
		AngleQuaternion( angle1, q1 );
		AngleQuaternion( angle2, q2 );
		QuaternionSlerp( q1, q2, s, q );
	}
	else
	{
		AngleQuaternion( angle1, q );
	}
}


/*!
*/
void CAnimatedMeshHalfLife::calcBonePosition( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *pos ) const
{
	s32					j, k;
	SHalfelifeAnimationFrame	*animvalue;

	for (j = 0; j < 3; j++)
	{
		pos[j] = bone->value[j]; // default;

		if (anim->offset[j] != 0)
		{
			animvalue = (SHalfelifeAnimationFrame *)((u8*)anim + anim->offset[j]);

			k = frame;
			// find span of values that includes the frame we want

			while (animvalue->num.total <= k)
			{
				k -= animvalue->num.total;
				animvalue += animvalue->num.valid + 1;
			}
			// if we're inside the span

			if (animvalue->num.valid > k)
			{
				// and there's more data in the span

				if (animvalue->num.valid > k + 1)
				{
					pos[j] += (animvalue[k+1].value * (1.f - s) + s * animvalue[k+2].value) * bone->scale[j];
				}
				else
				{
					pos[j] += animvalue[k+1].value * bone->scale[j];
				}
			}
			else
			{
				// are we at the end of the repeating values section and there's another section with data?

				if (animvalue->num.total <= k + 1)
				{
					pos[j] += (animvalue[animvalue->num.valid].value * (1.f - s) + s * animvalue[animvalue->num.valid + 2].value) * bone->scale[j];
				}
				else
				{
					pos[j] += animvalue[animvalue->num.valid].value * bone->scale[j];
				}
			}
		}
		if (bone->bonecontroller[j] != -1)
		{
			pos[j] += BoneAdj[bone->bonecontroller[j]];
		}
	}
}

/*!
*/
void CAnimatedMeshHalfLife::calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f )
{
	s32 frame;
	SHalflifeBone *bone;
	f32 s;

	frame = (s32)f;
	s = (f - frame);

	// add in programatic controllers

	calcBoneAdj( );

	bone = (SHalflifeBone *)((u8 *)Header + Header->boneindex);
	for ( u32 i = 0; i < Header->numbones; i++, bone++, anim++)
	{
		calcBoneQuaternion( frame, s, bone, anim, q[i] );
		calcBonePosition( frame, s, bone, anim, pos[i] );
	}

	if (seq->motiontype & STUDIO_X)
		pos[seq->motionbone][0] = 0.f;
	if (seq->motiontype & STUDIO_Y)
		pos[seq->motionbone][1] = 0.f;
	if (seq->motiontype & STUDIO_Z)
		pos[seq->motionbone][2] = 0.f;
}

/*!
*/
SHalflifeAnimOffset * CAnimatedMeshHalfLife::getAnim( SHalflifeSequence *seq )
{
	SHalflifeSequenceGroup	*seqgroup;
	seqgroup = (SHalflifeSequenceGroup *)((u8*)Header + Header->seqgroupindex) + seq->seqgroup;

	if (seq->seqgroup == 0)
	{
		return (SHalflifeAnimOffset *)((u8*)Header + seqgroup->data + seq->animindex);
	}

	return (SHalflifeAnimOffset *)((u8*)AnimationHeader[seq->seqgroup] + seq->animindex);
}


/*!
*/
void CAnimatedMeshHalfLife::slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s )
{
	vec4_hl q3;
	f32 s1;

	if (s < 0) s = 0;
	else if (s > 1.f) s = 1.f;

	s1 = 1.f - s;

	for ( u32 i = 0; i < Header->numbones; i++)
	{
		QuaternionSlerp( q1[i], q2[i], s, q3 );
		q1[i][0] = q3[0];
		q1[i][1] = q3[1];
		q1[i][2] = q3[2];
		q1[i][3] = q3[3];
		pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s;
		pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s;
		pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s;
	}
}

/*!
*/
void CAnimatedMeshHalfLife::setUpBones ()
{
	SHalflifeBone		*bone;
	SHalflifeSequence	*seq;
	SHalflifeAnimOffset	*anim;

	static vec3_hl pos[MAXSTUDIOBONES];
	f32 bonematrix[3][4];
	static vec4_hl q[MAXSTUDIOBONES];

	static vec3_hl pos2[MAXSTUDIOBONES];
	static vec4_hl q2[MAXSTUDIOBONES];
	static vec3_hl pos3[MAXSTUDIOBONES];
	static vec4_hl q3[MAXSTUDIOBONES];
	static vec3_hl pos4[MAXSTUDIOBONES];
	static vec4_hl q4[MAXSTUDIOBONES];

	if (SequenceIndex >= Header->numseq)
		SequenceIndex = 0;

	seq = (SHalflifeSequence *)((u8*) Header + Header->seqindex) + SequenceIndex;

	anim = getAnim( seq );
	calcRotations( pos, q, seq, anim, CurrentFrame );

	if (seq->numblends > 1)
	{
		f32 s;

		anim += Header->numbones;
		calcRotations( pos2, q2, seq, anim, CurrentFrame );
		s = Blending[0] / 255.f;

		slerpBones( q, pos, q2, pos2, s );

		if (seq->numblends == 4)
		{
			anim += Header->numbones;
			calcRotations( pos3, q3, seq, anim, CurrentFrame );

			anim += Header->numbones;
			calcRotations( pos4, q4, seq, anim, CurrentFrame );

			s = Blending[0] / 255.f;
			slerpBones( q3, pos3, q4, pos4, s );

			s = Blending[1] / 255.f;
			slerpBones( q, pos, q3, pos3, s );
		}
	}

	bone = (SHalflifeBone *)((u8*) Header + Header->boneindex);

	for (u32 i = 0; i < Header->numbones; i++)
	{
		QuaternionMatrix( q[i], bonematrix );

		bonematrix[0][3] = pos[i][0];
		bonematrix[1][3] = pos[i][1];
		bonematrix[2][3] = pos[i][2];

		if (bone[i].parent == -1) {
			memcpy(BoneTransform[i], bonematrix, sizeof(f32) * 12);
		}
		else {
			R_ConcatTransforms (BoneTransform[bone[i].parent], bonematrix, BoneTransform[i]);
		}
	}
}



//! Returns an axis aligned bounding box

const core::aabbox3d<f32>& CAnimatedMeshHalfLife::getBoundingBox() const
{
	return MeshIPol.BoundingBox;
}


//! Returns the type of the animated mesh.

E_ANIMATED_MESH_TYPE CAnimatedMeshHalfLife::getMeshType() const
{
	return EAMT_MDL_HALFLIFE;
}


//! returns amount of mesh buffers.

u32 CAnimatedMeshHalfLife::getMeshBufferCount() const
{
	return MeshIPol.getMeshBufferCount();
}

//! returns pointer to a mesh buffer

IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer(u32 nr) const
{
	return MeshIPol.getMeshBuffer ( nr );
}


//! Returns pointer to a mesh buffer which fits a material

/** \param material: material to search for
\return Returns the pointer to the mesh buffer or
NULL if there is no such mesh buffer. */
IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer( const video::SMaterial &material) const
{
	return MeshIPol.getMeshBuffer ( material );
}

void CAnimatedMeshHalfLife::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
{
	MeshIPol.setMaterialFlag ( flag, newvalue );
}

//! set user axis aligned bounding box

void CAnimatedMeshHalfLife::setBoundingBox(const core::aabbox3df& box)
{
	return;
}


} // end namespace scene

} // end namespace irr


#endif // _IRR_COMPILE_WITH_MD3_LOADER_


Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
881 membres
1427 sujets
11117 messages
Dernier membre inscrit: Bidule
35 invités en ligne
Aucun membre connecté
RSS Feed