irrlicht/source/Irrlicht/CAnimatedMeshHalfLife.cpp

1695 lines
45 KiB
C++

// Copyright (C) 2002-2012 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 core::vector3df& angles, vec4_hl quaternion)
{
double angle;
double sr, sp, sy, cr, cp, cy;
// FIXME: rescale the inputs to 1/2 angle
angle = angles.Z * 0.5;
sy = sin(angle);
cy = cos(angle);
angle = angles.Y * 0.5;
sp = sin(angle);
cp = cos(angle);
angle = angles.X * 0.5;
sr = sin(angle);
cr = cos(angle);
quaternion[0] = (irr::f32)(sr*cp*cy-cr*sp*sy); // X
quaternion[1] = (irr::f32)(cr*sp*cy+sr*cp*sy); // Y
quaternion[2] = (irr::f32)(cr*cp*sy-sr*sp*cy); // Z
quaternion[3] = (irr::f32)(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 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], core::vector3df& out)
{
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) : SceneManager(smgr)
{
#ifdef _DEBUG
setDebugName("CHalflifeMDLMeshFileLoader");
#endif
}
//! 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()
: FrameCount(0), MeshIPol(0), SceneManager(0), Header(0), TextureHeader(0),
OwnTexModel(false), SequenceIndex(0), CurrentFrame(0), FramesPerSecond(25.f),
SkinGroupSelection(0)
#ifdef HL_TEXTURE_ATLAS
// , TextureMaster(0)
#endif
{
#ifdef _DEBUG
setDebugName("CAnimatedMeshHalfLife");
#endif
initData();
}
/*!
loads a complete model
*/
bool CAnimatedMeshHalfLife::loadModelFile(io::IReadFile* file,
ISceneManager* smgr)
{
if (!file)
return false;
SceneManager = smgr;
if ( loadModel(file, file->getFileName()) )
{
if ( postLoadModel ( file->getFileName() ) )
{
initModel ();
//dumpModelInfo ( 1 );
return true;
}
}
return false;
}
//! Destructor
CAnimatedMeshHalfLife::~CAnimatedMeshHalfLife()
{
delete [] (u8*) Header;
if (OwnTexModel)
delete [] (u8*) TextureHeader;
for (u32 i = 0; i < 32; ++i)
delete [] (u8*) AnimationHeader[i];
if (MeshIPol)
MeshIPol->drop();
}
//! 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 core::vector3df TransformedVerts[MAXSTUDIOVERTS]; // transformed vertices
//static core::vector3df 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
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);
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
io::path store = TextureBaseName + "atlas";
#else
io::path fname;
io::path ext;
core::splitFilename ( currentex->name, 0, &fname, &ext );
io::path store = TextureBaseName + fname;
#endif
m.TextureLayer[0].Texture = SceneManager->getVideoDriver()->getTexture ( store );
m.Lighting = false;
MeshIPol->addMeshBuffer(buffer);
buffer->recalculateBoundingBox();
buffer->drop();
} // mesh
MeshIPol->recalculateBoundingBox();
} // 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 vec3_hl *studioverts = (vec3_hl *)((u8*)Header + model->vertindex);
for ( i = 0; i < model->numverts; i++)
{
VectorTransform ( studioverts[i], BoneTransform[vertbone[i]], TransformedVerts[i] );
}
/*
const u8 *normbone = ((u8*)Header + model->norminfoindex);
const vec3_hl *studionorms = (vec3_hl *)((u8*)Header + model->normindex);
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 core::vector3df& av = TransformedVerts[tricmd[0]];
v->Pos = av;
/*
const core::vector3df& an = TransformedNormals[tricmd[1]];
v->Normal = an;
//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
SHalflifeAttachment *attach = (SHalflifeAttachment *) ((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 );
SHalflifeSequence *seq = (SHalflifeSequence*) ((u8*) Header + Header->seqindex);
// find SequenceIndex from summed list
u32 frameCount = 0;
for (u32 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;
if (!MeshIPol)
MeshIPol = new SMesh();
MeshIPol->clear();
#ifdef HL_TEXTURE_ATLAS
TextureAtlas.release();
#endif
}
/*!
*/
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)
{
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 secondary files are needed, open here and mark for closing
if ( 0 == file )
{
file = SceneManager->getFileSystem()->createAndOpenFile(filename);
closefile = true;
}
if ( 0 == file )
return 0;
// read into memory
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 0;
}
// don't know the real header.. idsg might be different
if (header->textureindex && idst )
{
io::path path;
io::path fname;
io::path ext;
core::splitFilename(file->getFileName(), &path, &fname, &ext);
TextureBaseName = path + fname + "_";
SHalflifeTexture *tex = (SHalflifeTexture *)(pin + header->textureindex);
u32 *palette = new u32[256];
for (u32 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 = SceneManager->getVideoDriver()->createImage(ECF_R8G8B8, core::dimension2d<u32>(tex[i].width, tex[i].height));
CColorConverter::convert8BitTo24Bit(src, (u8*)image->getData(), tex[i].width, tex[i].height, (u8*) palette, 0, false);
#ifdef HL_TEXTURE_ATLAS
TextureAtlas.addSource ( tex[i].name, image );
#else
core::splitFilename ( tex[i].name, 0, &fname, &ext );
SceneManager->getVideoDriver()->addTexture ( TextureBaseName + fname, image );
image->drop();
#endif
}
delete [] palette;
#ifdef HL_TEXTURE_ATLAS
TextureAtlas.create ( 2 * 2 + 1, ETC_CLAMP );
SceneManager->getVideoDriver()->addTexture ( TextureBaseName + "atlas", TextureAtlas.Master );
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 no textures are stored in main file, use texfile
if (Header->numtextures == 0)
{
submodel = path + texname + "T.mdl";
TextureHeader = loadModel(0, submodel);
if (!TextureHeader)
return false;
OwnTexModel = true;
}
else
{
TextureHeader = Header;
OwnTexModel = false;
}
// preload animations
if (Header->numseqgroups > 1)
{
c8 seq[8];
for (u32 i = 1; i < Header->numseqgroups; i++)
{
snprintf_irr( seq, 8, "%02u.mdl", i );
submodel = path + texname + seq;
AnimationHeader[i] = loadModel(0, submodel);
if (!AnimationHeader[i])
return false;
}
}
return true;
}
/*!
*/
void CAnimatedMeshHalfLife::dumpModelInfo(u32 level) const
{
const u8 *phdr = (const u8*) Header;
const SHalflifeHeader * hdr = Header;
u32 i;
if (level == 0)
{
printf (
"Bones: %u\n"
"Bone Controllers: %u\n"
"Hit Boxes: %u\n"
"Sequences: %u\n"
"Sequence Groups: %u\n",
hdr->numbones,
hdr->numbonecontrollers,
hdr->numhitboxes,
hdr->numseq,
hdr->numseqgroups
);
printf (
"Textures: %u\n"
"Skin Families: %u\n"
"Bodyparts: %u\n"
"Attachments: %u\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: %u\n", hdr->numbones);
for (i = 0; i < hdr->numbones; i++)
{
const SHalflifeBone *bone = (const SHalflifeBone *) (phdr + hdr->boneindex);
printf("bone %u.name: \"%s\"\n", i + 1, bone[i].name);
printf("bone %u.parent: %d\n", i + 1, bone[i].parent);
printf("bone %u.flags: %d\n", i + 1, bone[i].flags);
printf("bone %u.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 %u.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 %u.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: %u\n", hdr->numbonecontrollers);
const SHalflifeBoneController *bonecontrollers = (const SHalflifeBoneController *) (phdr + hdr->bonecontrollerindex);
for (i = 0; i < hdr->numbonecontrollers; i++)
{
printf("bonecontroller %u.bone: %d\n", i + 1, bonecontrollers[i].bone);
printf("bonecontroller %u.type: %d\n", i + 1, bonecontrollers[i].type);
printf("bonecontroller %u.start: %f\n", i + 1, bonecontrollers[i].start);
printf("bonecontroller %u.end: %f\n", i + 1, bonecontrollers[i].end);
printf("bonecontroller %u.rest: %d\n", i + 1, bonecontrollers[i].rest);
printf("bonecontroller %u.index: %d\n", i + 1, bonecontrollers[i].index);
}
printf("\nnumhitboxes: %u\n", hdr->numhitboxes);
const SHalflifeBBox *box = (const SHalflifeBBox *) (phdr + hdr->hitboxindex);
for (i = 0; i < hdr->numhitboxes; i++)
{
printf("hitbox %u.bone: %d\n", i + 1, box[i].bone);
printf("hitbox %u.group: %d\n", i + 1, box[i].group);
printf("hitbox %u.bbmin: %f %f %f\n", i + 1, box[i].bbmin[0], box[i].bbmin[1], box[i].bbmin[2]);
printf("hitbox %u.bbmax: %f %f %f\n", i + 1, box[i].bbmax[0], box[i].bbmax[1], box[i].bbmax[2]);
}
printf("\nnumseq: %u\n", hdr->numseq);
const SHalflifeSequence *seq = (const SHalflifeSequence *) (phdr + hdr->seqindex);
for (i = 0; i < hdr->numseq; i++)
{
printf("seqdesc %u.label: \"%s\"\n", i + 1, seq[i].label);
printf("seqdesc %u.fps: %f\n", i + 1, seq[i].fps);
printf("seqdesc %u.flags: %d\n", i + 1, seq[i].flags);
printf("<...>\n");
}
printf("\nnumseqgroups: %u\n", hdr->numseqgroups);
for (i = 0; i < hdr->numseqgroups; i++)
{
const SHalflifeSequenceGroup *group = (const SHalflifeSequenceGroup *) (phdr + hdr->seqgroupindex);
printf("\nseqgroup %u.label: \"%s\"\n", i + 1, group[i].label);
printf("\nseqgroup %u.namel: \"%s\"\n", i + 1, group[i].name);
printf("\nseqgroup %u.data: %d\n", i + 1, group[i].data);
}
printf("\nnumskinref: %u\n", hdr->numskinref);
printf("numskinfamilies: %u\n", hdr->numskinfamilies);
printf("\nnumbodyparts: %u\n", hdr->numbodyparts);
const SHalflifeBody *pbodyparts = (const SHalflifeBody*) ((const u8*) hdr + hdr->bodypartindex);
for (i = 0; i < hdr->numbodyparts; i++)
{
printf("bodypart %u.name: \"%s\"\n", i + 1, pbodyparts[i].name);
printf("bodypart %u.nummodels: %u\n", i + 1, pbodyparts[i].nummodels);
printf("bodypart %u.base: %u\n", i + 1, pbodyparts[i].base);
printf("bodypart %d.modelindex: %u\n", i + 1, pbodyparts[i].modelindex);
}
printf("\nnumattachments: %u\n", hdr->numattachments);
for (i = 0; i < hdr->numattachments; i++)
{
const SHalflifeAttachment *attach = (const SHalflifeAttachment *) ((const u8*) hdr + hdr->attachmentindex);
printf("attachment %u.name: \"%s\"\n", i + 1, attach[i].name);
}
hdr = TextureHeader;
printf("\nnumtextures: %u\n", hdr->numtextures);
printf("textureindex: %u\n", hdr->textureindex);
printf("texturedataindex: %u\n", hdr->texturedataindex);
const SHalflifeTexture *ptextures = (const SHalflifeTexture *) ((const u8*) hdr + hdr->textureindex);
for (i = 0; i < hdr->numtextures; i++)
{
printf("texture %u.name: \"%s\"\n", i + 1, ptextures[i].name);
printf("texture %u.flags: %d\n", i + 1, ptextures[i].flags);
printf("texture %u.width: %d\n", i + 1, ptextures[i].width);
printf("texture %u.height: %d\n", i + 1, ptextures[i].height);
printf("texture %u.index: %d\n", i + 1, ptextures[i].index);
}
}
/*!
*/
void CAnimatedMeshHalfLife::ExtractBbox(s32 sequence, core::aabbox3df &box) const
{
const SHalflifeSequence *seq = (const SHalflifeSequence *)((const 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()
{
const SHalflifeBoneController *bonecontroller =
(const SHalflifeBoneController *)((const u8*) Header + Header->bonecontrollerindex);
for (u32 j = 0; j < Header->numbonecontrollers; j++)
{
const s32 i = bonecontroller[j].index;
// check for 360% wrapping
f32 value;
if (bonecontroller[j].type & STUDIO_RLOOP)
{
value = BoneController[i] * (360.f/256.f) + bonecontroller[j].start;
}
else
{
const f32 range = i <= 3 ? 255.f : 64.f;
value = core::clamp(BoneController[i] / range,0.f,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::DEGTORAD;
break;
case STUDIO_X:
case STUDIO_Y:
case STUDIO_Z:
BoneAdj[j] = value;
break;
}
}
}
/*!
*/
void CAnimatedMeshHalfLife::calcBoneQuaternion(const s32 frame, const SHalflifeBone * const bone,
SHalflifeAnimOffset *anim, const u32 j, f32& angle1, f32& angle2) const
{
// three vector components
if (anim->offset[j+3] == 0)
{
angle2 = angle1 = bone->value[j+3]; // default
}
else
{
SHalflifeAnimationFrame *animvalue = (SHalflifeAnimationFrame *)((u8*)anim + anim->offset[j+3]);
s32 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 = animvalue[k+1].value;
if (animvalue->num.valid > k + 1)
{
angle2 = animvalue[k+2].value;
}
else
{
if (animvalue->num.total > k + 1)
angle2 = angle1;
else
angle2 = animvalue[animvalue->num.valid+2].value;
}
}
else
{
angle1 = animvalue[animvalue->num.valid].value;
if (animvalue->num.total > k + 1)
{
angle2 = angle1;
}
else
{
angle2 = animvalue[animvalue->num.valid + 2].value;
}
}
angle1 = bone->value[j+3] + angle1 * bone->scale[j+3];
angle2 = bone->value[j+3] + angle2 * bone->scale[j+3];
}
if (bone->bonecontroller[j+3] != -1)
{
angle1 += BoneAdj[bone->bonecontroller[j+3]];
angle2 += BoneAdj[bone->bonecontroller[j+3]];
}
}
/*!
*/
void CAnimatedMeshHalfLife::calcBonePosition(const s32 frame, f32 s,
const SHalflifeBone * const bone, SHalflifeAnimOffset *anim, f32 *pos) const
{
for (s32 j = 0; j < 3; ++j)
{
pos[j] = bone->value[j]; // default;
if (anim->offset[j] != 0)
{
SHalflifeAnimationFrame *animvalue = (SHalflifeAnimationFrame *)((u8*)anim + anim->offset[j]);
s32 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 = (s32)f;
f32 s = (f - frame);
// add in programatic controllers
calcBoneAdj();
SHalflifeBone *bone = (SHalflifeBone *)((u8 *)Header + Header->boneindex);
for ( u32 i = 0; i < Header->numbones; i++, bone++, anim++)
{
core::vector3df angle1, angle2;
calcBoneQuaternion(frame, bone, anim, 0, angle1.X, angle2.X);
calcBoneQuaternion(frame, bone, anim, 1, angle1.Y, angle2.Y);
calcBoneQuaternion(frame, bone, anim, 2, angle1.Z, angle2.Z);
if (!angle1.equals(angle2))
{
vec4_hl q1, q2;
AngleQuaternion( angle1, q1 );
AngleQuaternion( angle2, q2 );
QuaternionSlerp( q1, q2, s, q[i] );
}
else
{
AngleQuaternion( angle1, 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 = (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)
{
if (s < 0)
s = 0;
else if (s > 1.f)
s = 1.f;
f32 s1 = 1.f - s;
for ( u32 i = 0; i < Header->numbones; i++)
{
vec4_hl q3;
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()
{
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;
SHalflifeSequence *seq = (SHalflifeSequence *)((u8*) Header + Header->seqindex) + SequenceIndex;
SHalflifeAnimOffset *anim = getAnim(seq);
calcRotations(pos, q, seq, anim, CurrentFrame);
if (seq->numblends > 1)
{
anim += Header->numbones;
calcRotations( pos2, q2, seq, anim, CurrentFrame );
f32 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 );
}
}
SHalflifeBone *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)
{
MeshIPol->setBoundingBox(box);
}
} // end namespace scene
} // end namespace irr
#endif // _IRR_COMPILE_WITH_MD3_LOADER_