From 7028cbe09c688437910a25623098762bf0fa592d Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Mon, 28 Mar 2016 22:28:34 +1000 Subject: Move Irrlicht to src/others. --- .../source/Irrlicht/CAnimatedMeshHalfLife.cpp | 1695 ++++++++++++++++++++ 1 file changed, 1695 insertions(+) create mode 100644 src/others/irrlicht-1.8.1/source/Irrlicht/CAnimatedMeshHalfLife.cpp (limited to 'src/others/irrlicht-1.8.1/source/Irrlicht/CAnimatedMeshHalfLife.cpp') diff --git a/src/others/irrlicht-1.8.1/source/Irrlicht/CAnimatedMeshHalfLife.cpp b/src/others/irrlicht-1.8.1/source/Irrlicht/CAnimatedMeshHalfLife.cpp new file mode 100644 index 0000000..6531e1a --- /dev/null +++ b/src/others/irrlicht-1.8.1/source/Irrlicht/CAnimatedMeshHalfLife.cpp @@ -0,0 +1,1695 @@ +// 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) + { + f32 angle; + f32 sr, sp, sy, cr, cp, cy; + + // FIXME: rescale the inputs to 1/2 angle + angle = angles.Z * 0.5f; + sy = sin(angle); + cy = cos(angle); + angle = angles.Y * 0.5f; + sp = sin(angle); + cp = cos(angle); + angle = angles.X * 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 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(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 dim = core::dimension2d( 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(tex[i].width, tex[i].height)); + + CColorConverter::convert8BitTo24Bit(src, (u8*)image->lock(), tex[i].width, tex[i].height, (u8*) palette, 0, false); + image->unlock(); + +#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( 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) const +{ + const u8 *phdr = (const u8*) Header; + const 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++) + { + const SHalflifeBone *bone = (const 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); + const SHalflifeBoneController *bonecontrollers = (const 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); + const SHalflifeBBox *box = (const 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); + const SHalflifeSequence *seq = (const 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++) + { + const SHalflifeSequenceGroup *group = (const 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); + const SHalflifeBody *pbodyparts = (const SHalflifeBody*) ((const 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++) + { + const SHalflifeAttachment *attach = (const SHalflifeAttachment *) ((const 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); + const SHalflifeTexture *ptextures = (const SHalflifeTexture *) ((const 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) 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& 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_ + -- cgit v1.1