Retour
Version Originale

./aip/1.8aipmod/source/Irrlicht/CAnimatedMeshMD3.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_MD3_LOADER_

#include "CAnimatedMeshMD3.h"
#include "os.h"

namespace irr
{
namespace scene
{


#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__) 
#	pragma pack( push, packing )
#	pragma pack( 1 )
#	define PACK_STRUCT
#elif defined( __GNUC__ )
#	define PACK_STRUCT	__attribute__((packed))
#else
#	error compiler not supported
#endif


//! General properties of a single animation frame.

struct SMD3Frame
{
	f32  mins[3];		// bounding box per frame

	f32  maxs[3];
	f32  position[3];	// position of bounding box

	f32  radius;		// radius of bounding sphere

	c8   creator[16];	// name of frame

};


//! An attachment point for another MD3 model.

struct SMD3Tag
{
	c8 Name[64];		//name of 'tag' as it's usually called in the md3 files try to see it as a sub-mesh/seperate mesh-part.

	f32 position[3];	//relative position of tag

	f32 rotationMatrix[9];	//3x3 rotation direction of tag

};

//!Shader

struct SMD3Shader
{
	c8 name[64];		// name of shader

	s32 shaderIndex;
};



// Default alignment

#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__) 
#	pragma pack( pop, packing )
#endif

#undef PACK_STRUCT


//! Constructor

CAnimatedMeshMD3::CAnimatedMeshMD3()
:Mesh(0), IPolShift(0), LoopMode(0), Scaling(1.f)
{
#ifdef _DEBUG
	setDebugName("CAnimatedMeshMD3");
#endif

	Mesh = new SMD3Mesh();

	setInterpolationShift ( 0, 0 );
}


//! Destructor

CAnimatedMeshMD3::~CAnimatedMeshMD3()
{
	if (Mesh)
		Mesh->drop();
}


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

u32 CAnimatedMeshMD3::getFrameCount() const
{
	return Mesh->MD3Header.numFrames << IPolShift;
}


//! Rendering Hint

void CAnimatedMeshMD3::setInterpolationShift ( u32 shift, u32 loopMode )
{
	IPolShift = shift;
	LoopMode = loopMode;
}


//! set the hardware mapping hint, for driver

void CAnimatedMeshMD3::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,
		E_BUFFER_TYPE buffer)
{
	MeshIPol.setHardwareMappingHint(newMappingHint, buffer);
}


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

void CAnimatedMeshMD3::setDirty(E_BUFFER_TYPE buffer)
{
	MeshIPol.setDirty(buffer);
}


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

SMD3QuaternionTagList *CAnimatedMeshMD3::getTagList(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
	if ( 0 == Mesh )
		return 0;

	getMesh ( frame, detailLevel, startFrameLoop, endFrameLoop );
	return &TagListIPol;
}


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

IMesh* CAnimatedMeshMD3::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
	if ( 0 == Mesh )
		return 0;

	u32 i;

	//! check if we have the mesh in our private cache

	SCacheInfo candidate ( frame, startFrameLoop, endFrameLoop );
	if ( candidate == Current )
		return &MeshIPol;

	startFrameLoop = core::s32_max ( 0, startFrameLoop >> IPolShift );
	endFrameLoop = core::if_c_a_else_b ( endFrameLoop < 0, Mesh->MD3Header.numFrames - 1, endFrameLoop >> IPolShift );

	const u32 mask = 1 << IPolShift;

	s32 frameA;
	s32 frameB;
	f32 iPol;

	if ( LoopMode )
	{
		// correct frame to "pixel center"

		frame -= mask >> 1;

		// interpolation

		iPol = f32(frame & ( mask - 1 )) * core::reciprocal ( f32(mask) );

		// wrap anim

		frame >>= IPolShift;
		frameA = core::if_c_a_else_b ( frame < startFrameLoop, endFrameLoop, frame );
		frameB = core::if_c_a_else_b ( frameA + 1 > endFrameLoop, startFrameLoop, frameA + 1 );
	}
	else
	{
		// correct frame to "pixel center"

		frame -= mask >> 1;

		iPol = f32(frame & ( mask - 1 )) * core::reciprocal ( f32(mask) );

		// clamp anim

		frame >>= IPolShift;
		frameA = core::s32_clamp ( frame, startFrameLoop, endFrameLoop );
		frameB = core::s32_min ( frameA + 1, endFrameLoop );
	}

	// build current vertex

	for ( i = 0; i!= Mesh->Buffer.size (); ++i )
	{
		buildVertexArray(frameA, frameB, iPol,
					Mesh->Buffer[i],
					(SMeshBufferLightMap*) MeshIPol.getMeshBuffer(i)
				);
	}
	MeshIPol.recalculateBoundingBox();

	// build current tags

	buildTagArray( frameA, frameB, iPol );

	Current = candidate;
	return &MeshIPol;
}


//! create a Irrlicht MeshBuffer for a MD3 MeshBuffer

IMeshBuffer * CAnimatedMeshMD3::createMeshBuffer(const SMD3MeshBuffer* source,
												 io::IFileSystem* fs, video::IVideoDriver * driver)
{
	SMeshBufferLightMap * dest = new SMeshBufferLightMap();
	dest->Vertices.set_used( source->MeshHeader.numVertices );
	dest->Indices.set_used( source->Indices.size () );

	u32 i;

	// fill in static face info

	for ( i = 0; i < source->Indices.size(); i += 3 )
	{
		dest->Indices[i + 0 ] = (u16) source->Indices[i + 0];
		dest->Indices[i + 1 ] = (u16) source->Indices[i + 1];
		dest->Indices[i + 2 ] = (u16) source->Indices[i + 2];
	}

	// fill in static vertex info

	for ( i = 0; i!= (u32)source->MeshHeader.numVertices; ++i )
	{
		video::S3DVertex2TCoords &v = dest->Vertices[i];
		v.Color = 0xFFFFFFFF;
		v.TCoords.X = source->Tex[i].u;
		v.TCoords.Y = source->Tex[i].v;
		v.TCoords2.X = 0.f;
		v.TCoords2.Y = 0.f;
	}

	// load static texture

	u32 pos = 0;
	quake3::tTexArray textureArray;
	quake3::getTextures( textureArray, source->Shader, pos, fs, driver );
	dest->Material.MaterialType = video::EMT_SOLID;
	dest->Material.setTexture ( 0, textureArray[0] );
	dest->Material.Lighting = false;

	return dest;
}


//! build final mesh's vertices from frames frameA and frameB with linear interpolation.

void CAnimatedMeshMD3::buildVertexArray ( u32 frameA, u32 frameB, f32 interpolate,
						const SMD3MeshBuffer * source,
						SMeshBufferLightMap * dest
					)
{
	const u32 frameOffsetA = frameA * source->MeshHeader.numVertices;
	const u32 frameOffsetB = frameB * source->MeshHeader.numVertices;
	const f32 scale = ( 1.f/ 64.f );

	for (s32 i = 0; i != source->MeshHeader.numVertices; ++i)
	{
		video::S3DVertex2TCoords &v = dest->Vertices [ i ];

		const SMD3Vertex &vA = source->Vertices [ frameOffsetA + i ];
		const SMD3Vertex &vB = source->Vertices [ frameOffsetB + i ];

		// position

		v.Pos.X = scale * ( vA.position[0] + interpolate * ( vB.position[0] - vA.position[0] ) );
		v.Pos.Y = scale * ( vA.position[2] + interpolate * ( vB.position[2] - vA.position[2] ) );
		v.Pos.Z = scale * ( vA.position[1] + interpolate * ( vB.position[1] - vA.position[1] ) );

		// normal

		const core::vector3df nA( quake3::getMD3Normal ( vA.normal[0], vA.normal[1] ));
		const core::vector3df nB( quake3::getMD3Normal ( vB.normal[0], vB.normal[1] ));

		v.Normal.X = nA.X + interpolate * ( nB.X - nA.X );
		v.Normal.Y = nA.Z + interpolate * ( nB.Z - nA.Z );
		v.Normal.Z = nA.Y + interpolate * ( nB.Y - nA.Y );
	}

	dest->recalculateBoundingBox ();
}


//! build final mesh's tag from frames frameA and frameB with linear interpolation.

void CAnimatedMeshMD3::buildTagArray ( u32 frameA, u32 frameB, f32 interpolate )
{
	const u32 frameOffsetA = frameA * Mesh->MD3Header.numTags;
	const u32 frameOffsetB = frameB * Mesh->MD3Header.numTags;

	for ( s32 i = 0; i != Mesh->MD3Header.numTags; ++i )
	{
		SMD3QuaternionTag &d = TagListIPol [ i ];

		const SMD3QuaternionTag &qA = Mesh->TagList[ frameOffsetA + i];
		const SMD3QuaternionTag &qB = Mesh->TagList[ frameOffsetB + i];

		// rotation

		d.rotation.slerp( qA.rotation, qB.rotation, interpolate );

		// position

		d.position.X = qA.position.X + interpolate * ( qB.position.X - qA.position.X );
		d.position.Y = qA.position.Y + interpolate * ( qB.position.Y - qA.position.Y );
		d.position.Z = qA.position.Z + interpolate * ( qB.position.Z - qA.position.Z );
	}
}


/*!
	loads a model
*/
bool CAnimatedMeshMD3::loadModelFile( u32 modelIndex, io::IReadFile* file,
									 io::IFileSystem* fs, video::IVideoDriver * driver)
{
	if (!file)
		return false;

	//! Check MD3Header

	{
		file->read( &Mesh->MD3Header, sizeof(SMD3Header) );

		if ( strncmp("IDP3", Mesh->MD3Header.headerID, 4) )
		{
			os::Printer::log("MD3 Loader: invalid header");
			return false;
		}
	}

	//! store model name

	Mesh->Name = file->getFileName();

	u32 i;

	//! Frame Data ( ignore )

#if 0
	SMD3Frame frameImport;
	file->seek ( Mesh->MD3Header.frameStart );
	for (i = 0; i != Mesh->MD3Header.numFrames; ++i )
	{
		file->read(&frameImport, sizeof(frameImport) );
	}
#endif

	//! Tag Data

	const u32 totalTags = Mesh->MD3Header.numTags * Mesh->MD3Header.numFrames;

	SMD3Tag import;

	file->seek( Mesh->MD3Header.tagStart );
	Mesh->TagList.set_used ( totalTags );
	for (i = 0; i != totalTags; ++i )
	{
		file->read(&import, sizeof(import) );

		SMD3QuaternionTag &exp = Mesh->TagList[i];

		//! tag name

		exp.Name = import.Name;

		//! position

		exp.position.X = import.position[0];
		exp.position.Y = import.position[2];
		exp.position.Z = import.position[1];

		//! construct quaternion from a RH 3x3 Matrix

		exp.rotation.set (import.rotationMatrix[7],
					0.f,
					-import.rotationMatrix[6],
					1 + import.rotationMatrix[8]);
		exp.rotation.normalize ();
	}

	//! Meshes

	u32 offset = Mesh->MD3Header.tagEnd;

	for (i = 0; i != (u32)Mesh->MD3Header.numMeshes; ++i )
	{
		//! construct a new mesh buffer

		SMD3MeshBuffer * buf = new SMD3MeshBuffer ();

		// !read mesh header info

		SMD3MeshHeader &meshHeader = buf->MeshHeader;

		//! read mesh info

		file->seek( offset );
		file->read( &meshHeader, sizeof(SMD3MeshHeader) );

		//! prepare memory

		buf->Vertices.set_used ( meshHeader.numVertices * Mesh->MD3Header.numFrames );
		buf->Indices.set_used ( meshHeader.numTriangles * 3 );
		buf->Tex.set_used ( meshHeader.numVertices );

		//! read skins (shaders). should be 1 per meshbuffer

		SMD3Shader skin;
		file->seek( offset + buf->MeshHeader.offset_shaders );
		for ( s32 g = 0; g != buf->MeshHeader.numShader; ++g )
		{
			file->read( &skin, sizeof(skin) );

			io::path name;
			cutFilenameExtension ( name, skin.name );
			name.replace ( '\\', '/' );
			buf->Shader = name;
		}

		//! read texture coordinates

		file->seek( offset + buf->MeshHeader.offset_st);
		file->read( buf->Tex.pointer(), buf->MeshHeader.numVertices * sizeof(SMD3TexCoord) );

		//! read vertices

		file->seek(offset + meshHeader.vertexStart);
		file->read( buf->Vertices.pointer(), Mesh->MD3Header.numFrames * meshHeader.numVertices * sizeof(SMD3Vertex) );

		//! read indices

		file->seek( offset + meshHeader.offset_triangles );
		file->read( buf->Indices.pointer(), meshHeader.numTriangles * sizeof(SMD3Face) );

		//! store meshBuffer

		Mesh->Buffer.push_back ( buf );

		offset += meshHeader.offset_end;
	}

	// Init Mesh Interpolation

	for ( i = 0; i != Mesh->Buffer.size (); ++i )
	{
		IMeshBuffer * buffer = createMeshBuffer ( Mesh->Buffer[i], fs, driver );
		MeshIPol.addMeshBuffer ( buffer );
		buffer->drop ();
	}
	MeshIPol.recalculateBoundingBox ();

	// Init Tag Interpolation

	for (i = 0; i != (u32)Mesh->MD3Header.numTags; ++i )
	{
		TagListIPol.push_back ( Mesh->TagList[i] );
	}

	return true;
}


SMD3Mesh * CAnimatedMeshMD3::getOriginalMesh ()
{
	return Mesh;
}


//! Returns an axis aligned bounding box

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


//! Returns the type of the animated mesh.

E_ANIMATED_MESH_TYPE CAnimatedMeshMD3::getMeshType() const
{
	return EAMT_MD3;
}


} // 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
41 invités en ligne
Aucun membre connecté
RSS Feed