// Copyright (C) 2002-2011 Nikolaus Gebhardt / Fabio Concas / Thomas Alten // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "IrrCompileConfig.h" #ifdef _IRR_COMPILE_WITH_HALFLIFE_LOADER_ #include "CAnimatedMeshHalfLife.h" #include "os.h" #include "CColorConverter.h" #include "CImage.h" #include "coreutil.h" #include "SMeshBuffer.h" #include "IVideoDriver.h" #include "IFileSystem.h" namespace irr { namespace scene { using namespace video; void AngleQuaternion( const vec3_hl angles, vec4_hl quaternion ) { f32 angle; f32 sr, sp, sy, cr, cp, cy; // FIXME: rescale the inputs to 1/2 angle angle = angles[2] * 0.5f; sy = sin(angle); cy = cos(angle); angle = angles[1] * 0.5f; sp = sin(angle); cp = cos(angle); angle = angles[0] * 0.5f; sr = sin(angle); cr = cos(angle); quaternion[0] = sr*cp*cy-cr*sp*sy; // X quaternion[1] = cr*sp*cy+sr*cp*sy; // Y quaternion[2] = cr*cp*sy-sr*sp*cy; // Z quaternion[3] = cr*cp*cy+sr*sp*sy; // W } void QuaternionMatrix( const vec4_hl quaternion, f32 (*matrix)[4] ) { matrix[0][0] = 1.f - 2.f * quaternion[1] * quaternion[1] - 2.f * quaternion[2] * quaternion[2]; matrix[1][0] = 2.f * quaternion[0] * quaternion[1] + 2.f * quaternion[3] * quaternion[2]; matrix[2][0] = 2.f * quaternion[0] * quaternion[2] - 2.f * quaternion[3] * quaternion[1]; matrix[0][1] = 2.f * quaternion[0] * quaternion[1] - 2.f * quaternion[3] * quaternion[2]; matrix[1][1] = 1.f - 2.f * quaternion[0] * quaternion[0] - 2.f * quaternion[2] * quaternion[2]; matrix[2][1] = 2.f * quaternion[1] * quaternion[2] + 2.f * quaternion[3] * quaternion[0]; matrix[0][2] = 2.f * quaternion[0] * quaternion[2] + 2.f * quaternion[3] * quaternion[1]; matrix[1][2] = 2.f * quaternion[1] * quaternion[2] - 2.f * quaternion[3] * quaternion[0]; matrix[2][2] = 1.f - 2.f * quaternion[0] * quaternion[0] - 2.f * quaternion[1] * quaternion[1]; } void QuaternionSlerp( const vec4_hl p, vec4_hl q, f32 t, vec4_hl qt ) { s32 i; f32 omega, cosom, sinom, sclp, sclq; // decide if one of the quaternions is backwards f32 a = 0; f32 b = 0; for (i = 0; i < 4; i++) { a += (p[i]-q[i])*(p[i]-q[i]); b += (p[i]+q[i])*(p[i]+q[i]); } if (a > b) { for (i = 0; i < 4; i++) { q[i] = -q[i]; } } cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3]; if ((1.f + cosom) > 0.00000001) { if ((1.f - cosom) > 0.00000001) { omega = acos( cosom ); sinom = sin( omega ); sclp = sin( (1.f - t)*omega) / sinom; sclq = sin( t*omega ) / sinom; } else { sclp = 1.f - t; sclq = t; } for (i = 0; i < 4; i++) { qt[i] = sclp * p[i] + sclq * q[i]; } } else { qt[0] = -p[1]; qt[1] = p[0]; qt[2] = -p[3]; qt[3] = p[2]; sclp = sin( (1.f - t) * 0.5f * core::PI); sclq = sin( t * 0.5f * core::PI); for (i = 0; i < 3; i++) { qt[i] = sclp * p[i] + sclq * qt[i]; } } } void R_ConcatTransforms (const f32 in1[3][4], const f32 in2[3][4], f32 out[3][4]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; } #define EQUAL_EPSILON 0.001 s32 VectorCompare (vec3_hl v1, vec3_hl v2) { s32 i; for (i=0 ; i<3 ; i++) if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON) return false; return true; } #define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) inline void VectorTransform (const vec3_hl in1, const f32 in2[3][4], vec3_hl out) { out[0] = DotProduct(in1, in2[0]) + in2[0][3]; out[1] = DotProduct(in1, in2[1]) + in2[1][3]; out[2] = DotProduct(in1, in2[2]) + in2[2][3]; } inline void VectorTransform2 (core::vector3df &out, const vec3_hl in1, const f32 in2[3][4]) { out.X = DotProduct(in1, in2[0]) + in2[0][3]; out.Z = DotProduct(in1, in2[1]) + in2[1][3]; out.Y = DotProduct(in1, in2[2]) + in2[2][3]; } static f32 BoneTransform[MAXSTUDIOBONES][3][4]; // bone transformation matrix void getBoneVector ( core::vector3df &out, u32 index ) { out.X = BoneTransform[index][0][3]; out.Z = BoneTransform[index][1][3]; out.Y = BoneTransform[index][2][3]; } void getBoneBox ( core::aabbox3df &box, u32 index, f32 size = 0.5f ) { box.MinEdge.X = BoneTransform[index][0][3] - size; box.MinEdge.Z = BoneTransform[index][1][3] - size; box.MinEdge.Y = BoneTransform[index][2][3] - size; size *= 2.f; box.MaxEdge.X = box.MinEdge.X + size; box.MaxEdge.Y = box.MinEdge.Y + size; box.MaxEdge.Z = box.MinEdge.Z + size; } void getTransformedBoneVector ( core::vector3df &out, u32 index, const vec3_hl in) { out.X = DotProduct(in, BoneTransform[index][0]) + BoneTransform[index][0][3]; out.Z = DotProduct(in, BoneTransform[index][1]) + BoneTransform[index][1][3]; out.Y = DotProduct(in, BoneTransform[index][2]) + BoneTransform[index][2][3]; } //! Constructor CHalflifeMDLMeshFileLoader::CHalflifeMDLMeshFileLoader( scene::ISceneManager* smgr ) { #ifdef _DEBUG setDebugName("CHalflifeMDLMeshFileLoader"); #endif SceneManager = smgr; } //! returns true if the file maybe is able to be loaded by this class //! based on the file extension (e.g. ".bsp") bool CHalflifeMDLMeshFileLoader::isALoadableFileExtension(const io::path& filename) const { return core::hasFileExtension ( filename, "mdl" ); } //! creates/loads an animated mesh from the file. //! \return Pointer to the created mesh. Returns 0 if loading failed. //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). //! See IReferenceCounted::drop() for more information. IAnimatedMesh* CHalflifeMDLMeshFileLoader::createMesh(io::IReadFile* file) { CAnimatedMeshHalfLife* msh = new CAnimatedMeshHalfLife(); if (msh) { if ( msh->loadModelFile ( file, SceneManager ) ) return msh; msh->drop(); } return 0; } //! Constructor CAnimatedMeshHalfLife::CAnimatedMeshHalfLife() { #ifdef _DEBUG setDebugName("CAnimatedMeshHalfLife"); #endif initData (); } /*! loads a complete model */ bool CAnimatedMeshHalfLife::loadModelFile( io::IReadFile* file,ISceneManager * smgr ) { if (!file) return false; bool r = false; SceneManager = smgr; if ( loadModel ( file, file->getFileName() ) ) { if ( postLoadModel ( file->getFileName() ) ) { initModel (); //dumpModelInfo ( 1 ); r = true; } } return r; } //! Destructor CAnimatedMeshHalfLife::~CAnimatedMeshHalfLife() { freeModel (); } //! Returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh. u32 CAnimatedMeshHalfLife::getFrameCount() const { return FrameCount; } //! set the hardware mapping hint, for driver void CAnimatedMeshHalfLife::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint,E_BUFFER_TYPE buffer) { } //! flags the meshbuffer as changed, reloads hardware buffers void CAnimatedMeshHalfLife::setDirty(E_BUFFER_TYPE buffer) { } static vec3_hl TransformedVerts[MAXSTUDIOVERTS]; // transformed vertices static vec3_hl TransformedNormals[MAXSTUDIOVERTS]; // light surface normals /*! */ void CAnimatedMeshHalfLife::initModel () { // init Sequences to Animation KeyFrameInterpolation ipol; ipol.Name.reserve ( 64 ); u32 i; AnimList.clear(); FrameCount = 0; SHalflifeSequence *seq = (SHalflifeSequence*) ((u8*) Header + Header->seqindex); for ( i = 0; i < Header->numseq; i++) { ipol.Name = seq[i].label; ipol.StartFrame = FrameCount; ipol.Frames = core::max_ ( 1, seq[i].numframes - 1 ); ipol.EndFrame = ipol.StartFrame + ipol.Frames - 1; ipol.FramesPerSecond = seq[i].fps; ipol.AnimationType = seq[i].flags & STUDIO_LOOPING ? EAMT_LOOPING : EAMT_WAYPOINT; AnimList.push_back ( ipol ); FrameCount += ipol.Frames; } // initBoneControllers /* SHalflifeBoneController *bonecontroller = (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex); for ( i = 0; i < Header->numbonecontrollers; i++) { printf ( "BoneController%d index:%d%s range:%f - %f\n", i, bonecontroller[i].index, bonecontroller[i].index == MOUTH_CONTROLLER ? " (Mouth)": "", bonecontroller[i].start,bonecontroller[i].end ); } // initSkins for (i = 0; i < TextureHeader->numskinfamilies; i++) { printf ( "Skin%d\n", i + 1); } */ // initBodyparts u32 meshBuffer = 0; BodyList.clear(); SHalflifeBody *body = (SHalflifeBody *) ((u8*) Header + Header->bodypartindex); for ( i = 0; i < Header->numbodyparts; ++i) { BodyPart part; part.name = body[i].name; part.defaultModel = core::max_ ( 0, (s32) body[i].base - 1 ); SHalflifeModel * model = (SHalflifeModel *)((u8*) Header + body[i].modelindex); for ( u32 g = 0; g < body[i].nummodels; ++g) { SubModel sub; sub.name = model[g].name; sub.startBuffer = meshBuffer; sub.endBuffer = sub.startBuffer + model[g].nummesh; sub.state = g == part.defaultModel; part.model.push_back ( sub ); meshBuffer += model[g].nummesh; } BodyList.push_back ( part ); } SequenceIndex = 0; CurrentFrame = 0.f; SetController (0, 0.f); SetController (1, 0.f); SetController (2, 0.f); SetController (3, 0.f); SetController (MOUTH_CONTROLLER, 0.f); SetSkin (0); // init Meshbuffers io::path store; const SHalflifeTexture *tex = (SHalflifeTexture *) ((u8*) TextureHeader + TextureHeader->textureindex); const u16 *skinref = (u16 *)((u8*)TextureHeader + TextureHeader->skinindex); if (SkinGroupSelection != 0 && SkinGroupSelection < TextureHeader->numskinfamilies) skinref += (SkinGroupSelection * TextureHeader->numskinref); io::path fname; io::path ext; core::vector2df tex_scale; core::vector2di tex_trans ( 0, 0 ); #ifdef HL_TEXTURE_ATLAS TextureAtlas.getScale ( tex_scale ); #endif for ( u32 bodypart=0 ; bodypart < Header->numbodyparts ; ++bodypart) { const SHalflifeBody *body = (SHalflifeBody *)((u8*) Header + Header->bodypartindex) + bodypart; for ( u32 modelnr = 0; modelnr < body->nummodels; ++modelnr ) { const SHalflifeModel *model = (SHalflifeModel *)((u8*) Header + body->modelindex) + modelnr; #if 0 const vec3_hl *studioverts = (vec3_hl *)((u8*)Header + model->vertindex); const vec3_hl *studionorms = (vec3_hl *)((u8*)Header + model->normindex); #endif for (i = 0; i < model->nummesh; ++i) { const SHalflifeMesh *mesh = (SHalflifeMesh *)((u8*)Header + model->meshindex) + i; const SHalflifeTexture *currentex = &tex[skinref[mesh->skinref]]; #ifdef HL_TEXTURE_ATLAS TextureAtlas.getTranslation ( currentex->name, tex_trans ); #else tex_scale.X = 1.f/(f32)currentex->width; tex_scale.Y = 1.f/(f32)currentex->height; #endif SMeshBuffer * buffer = new SMeshBuffer(); // count index vertex size indexcount = mesh->numtris * 3 u32 indexCount = 0; u32 vertexCount = 0; const s16 *tricmd = (s16*)((u8*)Header + mesh->triindex); s32 c; while ( (c = *(tricmd++)) ) { if (c < 0) c = -c; indexCount += ( c - 2 ) * 3; vertexCount += c; tricmd += ( 4 * c ); } // indices buffer->Indices.set_used ( indexCount ); buffer->Vertices.set_used ( vertexCount ); // fill in static indices and vertex u16 *index = buffer->Indices.pointer(); video::S3DVertex * v = buffer->Vertices.pointer(); // blow up gl_triangle_fan/gl_triangle_strip to indexed triangle list E_PRIMITIVE_TYPE type; vertexCount = 0; indexCount = 0; tricmd = (s16*)((u8*)Header + mesh->triindex); while ( (c = *(tricmd++)) ) { if (c < 0) { // triangle fan c = -c; type = EPT_TRIANGLE_FAN; } else { type = EPT_TRIANGLE_STRIP; } for ( s32 g = 0; g < c; ++g, v += 1, tricmd += 4 ) { // fill vertex #if 0 const f32 *av = studioverts[tricmd[0]]; v->Pos.X = av[0]; v->Pos.Z = av[1]; v->Pos.Y = av[2]; av = studionorms[tricmd[1]]; v->Normal.X = av[0]; v->Normal.Z = av[1]; v->Normal.Y = av[2]; #endif v->Normal.X = 0.f; v->Normal.Z = 0.f; v->Normal.Y = 1.f; v->TCoords.X = (tex_trans.X + tricmd[2])*tex_scale.X; v->TCoords.Y = (tex_trans.Y + tricmd[3])*tex_scale.Y; v->Color.color = 0xFFFFFFFF; // fill index if ( g < c - 2 ) { if ( type == EPT_TRIANGLE_FAN ) { index[indexCount+0] = vertexCount; index[indexCount+1] = vertexCount+g+1; index[indexCount+2] = vertexCount+g+2; } else { if ( g & 1 ) { index[indexCount+0] = vertexCount+g+1; index[indexCount+1] = vertexCount+g+0; index[indexCount+2] = vertexCount+g+2; } else { index[indexCount+0] = vertexCount+g+0; index[indexCount+1] = vertexCount+g+1; index[indexCount+2] = vertexCount+g+2; } } indexCount += 3; } } vertexCount += c; } // material video::SMaterial &m = buffer->getMaterial(); m.MaterialType = video::EMT_SOLID; m.BackfaceCulling = true; if ( currentex->flags & STUDIO_NF_CHROME ) { // don't know what to do with chrome here } #ifdef HL_TEXTURE_ATLAS store = TextureBaseName + "atlas"; #else core::splitFilename ( currentex->name, 0, &fname, &ext ); store = TextureBaseName + fname; #endif m.TextureLayer[0].Texture = SceneManager->getVideoDriver()->getTexture ( store ); m.Lighting = false; MeshIPol.addMeshBuffer ( buffer ); buffer->drop (); } // mesh } // model } // body part } /*! */ void CAnimatedMeshHalfLife::buildVertices () { /* const u16 *skinref = (u16 *)((u8*)TextureHeader + TextureHeader->skinindex); if (SkinGroupSelection != 0 && SkinGroupSelection < TextureHeader->numskinfamilies) skinref += (SkinGroupSelection * TextureHeader->numskinref); */ u32 i; s32 c,g; const s16 *tricmd; u32 meshBufferNr = 0; for ( u32 bodypart = 0 ; bodypart < Header->numbodyparts; ++bodypart) { const SHalflifeBody *body = (SHalflifeBody *)((u8*) Header + Header->bodypartindex) + bodypart; for ( u32 modelnr = 0; modelnr < body->nummodels; ++modelnr ) { const SHalflifeModel *model = (SHalflifeModel *)((u8*) Header + body->modelindex) + modelnr; const u8 *vertbone = ((u8*)Header + model->vertinfoindex); const u8 *normbone = ((u8*)Header + model->norminfoindex); const vec3_hl *studioverts = (vec3_hl *)((u8*)Header + model->vertindex); const vec3_hl *studionorms = (vec3_hl *)((u8*)Header + model->normindex); for ( i = 0; i < model->numverts; i++) { VectorTransform ( studioverts[i], BoneTransform[vertbone[i]], TransformedVerts[i] ); } /* for ( i = 0; i < model->numnorms; i++) { VectorTransform ( studionorms[i], BoneTransform[normbone[i]], TransformedNormals[i] ); } */ for (i = 0; i < model->nummesh; i++) { const SHalflifeMesh *mesh = (SHalflifeMesh *)((u8*)Header + model->meshindex) + i; IMeshBuffer * buffer = MeshIPol.getMeshBuffer ( meshBufferNr++ ); video::S3DVertex* v = (video::S3DVertex* ) buffer->getVertices(); tricmd = (s16*)((u8*)Header + mesh->triindex); while ( (c = *(tricmd++)) ) { if (c < 0) c = -c; for ( g = 0; g < c; ++g, v += 1, tricmd += 4 ) { // fill vertex const f32 *av = TransformedVerts[tricmd[0]]; v->Pos.X = av[0]; v->Pos.Z = av[1]; v->Pos.Y = av[2]; /* av = TransformedNormals[tricmd[1]]; v->Normal.X = av[0]; v->Normal.Z = av[1]; v->Normal.Y = av[2]; //v->Normal.normalize(); */ } } // tricmd } // nummesh } // model } // bodypart } /*! render Bones */ void CAnimatedMeshHalfLife::renderModel ( u32 param, IVideoDriver * driver, const core::matrix4 &absoluteTransformation) { SHalflifeBone *bone = (SHalflifeBone *) ((u8 *) Header + Header->boneindex); video::SColor blue ( 0xFF000080 ); video::SColor red ( 0xFF800000 ); video::SColor yellow ( 0xFF808000 ); video::SColor cyan ( 0xFF008080 ); core::aabbox3df box; u32 i; for ( i = 0; i < Header->numbones; i++) { if (bone[i].parent >= 0) { getBoneVector ( box.MinEdge, bone[i].parent ); getBoneVector ( box.MaxEdge, i ); driver->draw3DLine ( box.MinEdge, box.MaxEdge, blue ); // draw parent bone node if (bone[bone[i].parent].parent >=0 ) { getBoneBox ( box, bone[i].parent ); driver->draw3DBox ( box, blue ); } getBoneBox ( box, i ); driver->draw3DBox ( box, blue ); } else { // draw parent bone node getBoneBox ( box, i, 1.f ); driver->draw3DBox ( box , red ); } } // attachements SHalfelifeAttachment *attach = (SHalfelifeAttachment *) ((u8*) Header + Header->attachmentindex); core::vector3df v[8]; for ( i = 0; i < Header->numattachments; i++) { getTransformedBoneVector ( v[0],attach[i].bone,attach[i].org ); getTransformedBoneVector ( v[1],attach[i].bone,attach[i].vectors[0] ); getTransformedBoneVector ( v[2],attach[i].bone,attach[i].vectors[1] ); getTransformedBoneVector ( v[3],attach[i].bone,attach[i].vectors[2] ); driver->draw3DLine ( v[0], v[1], cyan ); driver->draw3DLine ( v[0], v[2], cyan ); driver->draw3DLine ( v[0], v[3], cyan ); } // hit boxes SHalflifeBBox *hitbox = (SHalflifeBBox *) ((u8*) Header + Header->hitboxindex); f32 *bbmin,*bbmax; vec3_hl v2[8]; for (i = 0; i < Header->numhitboxes; i++) { bbmin = hitbox[i].bbmin; bbmax = hitbox[i].bbmax; v2[0][0] = bbmin[0]; v2[0][1] = bbmax[1]; v2[0][2] = bbmin[2]; v2[1][0] = bbmin[0]; v2[1][1] = bbmin[1]; v2[1][2] = bbmin[2]; v2[2][0] = bbmax[0]; v2[2][1] = bbmax[1]; v2[2][2] = bbmin[2]; v2[3][0] = bbmax[0]; v2[3][1] = bbmin[1]; v2[3][2] = bbmin[2]; v2[4][0] = bbmax[0]; v2[4][1] = bbmax[1]; v2[4][2] = bbmax[2]; v2[5][0] = bbmax[0]; v2[5][1] = bbmin[1]; v2[5][2] = bbmax[2]; v2[6][0] = bbmin[0]; v2[6][1] = bbmax[1]; v2[6][2] = bbmax[2]; v2[7][0] = bbmin[0]; v2[7][1] = bbmin[1]; v2[7][2] = bbmax[2]; for ( u32 g = 0; g < 8; ++g ) getTransformedBoneVector ( v[g],hitbox[i].bone,v2[g] ); driver->draw3DLine(v[0], v[1], yellow); driver->draw3DLine(v[1], v[3], yellow); driver->draw3DLine(v[3], v[2], yellow); driver->draw3DLine(v[2], v[0], yellow); driver->draw3DLine(v[4], v[5], yellow); driver->draw3DLine(v[5], v[7], yellow); driver->draw3DLine(v[7], v[6], yellow); driver->draw3DLine(v[6], v[4], yellow); driver->draw3DLine(v[0], v[6], yellow); driver->draw3DLine(v[1], v[7], yellow); driver->draw3DLine(v[3], v[5], yellow); driver->draw3DLine(v[2], v[4], yellow); } } //! Returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. IMesh* CAnimatedMeshHalfLife::getMesh(s32 frameInt, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) { f32 frame = frameInt + (detailLevel * 0.001f); u32 frameA = core::floor32 ( frame ); f32 blend = core::fract ( frame ); u32 i; SHalflifeSequence *seq = (SHalflifeSequence*) ((u8*) Header + Header->seqindex); // find SequenceIndex from summed list u32 frameCount = 0; for ( i = 0; i < Header->numseq; i++) { u32 val = core::max_ ( 1, seq[i].numframes - 1 ); if ( frameCount + val > frameA ) { SequenceIndex = i; CurrentFrame = frame - frameCount; break; } frameCount += val; } seq += SequenceIndex; //SetBodyPart ( 1, 1 ); setUpBones (); buildVertices(); MeshIPol.BoundingBox.MinEdge.X = seq->bbmin[0]; MeshIPol.BoundingBox.MinEdge.Z = seq->bbmin[1]; MeshIPol.BoundingBox.MinEdge.Y = seq->bbmin[2]; MeshIPol.BoundingBox.MaxEdge.X = seq->bbmax[0]; MeshIPol.BoundingBox.MaxEdge.Z = seq->bbmax[1]; MeshIPol.BoundingBox.MaxEdge.Y = seq->bbmax[2]; return &MeshIPol; } /*! */ void CAnimatedMeshHalfLife::initData () { u32 i; Header = 0; TextureHeader = 0; OwnTexModel = false; for ( i = 0; i < 32; ++i ) AnimationHeader[i] = 0; SequenceIndex = 0; CurrentFrame = 0.f; for ( i = 0; i < 5; ++i ) BoneController[i] = 0; for ( i = 0; i < 2; ++i ) Blending[i] = 0; SkinGroupSelection = 0; AnimList.clear(); FrameCount = 0; MeshIPol.clear(); #ifdef HL_TEXTURE_ATLAS TextureAtlas.release(); #endif } /*! */ void CAnimatedMeshHalfLife::freeModel () { delete [] (u8*) Header; if (OwnTexModel ) delete [] (u8*) TextureHeader; for ( u32 i = 0; i < 32; ++i ) delete [] (u8*) AnimationHeader[i]; } /*! */ void STextureAtlas::release () { for (u32 i = 0; i < atlas.size(); i++) { if ( atlas[i].image ) { atlas[i].image->drop (); atlas[i].image = 0; } } Master = 0; } /*! */ void STextureAtlas::addSource ( const c8 * name, video::IImage * image ) { TextureAtlasEntry entry; entry.name = name; entry.image = image; entry.width = image->getDimension().Width; entry.height = image->getDimension().Height; entry.pos.X = 0; entry.pos.Y = 0; atlas.push_back ( entry ); } /*! */ void STextureAtlas::getScale(core::vector2df& scale) { for (s32 i = static_cast<s32>(atlas.size()) - 1; i >= 0; --i) { if ( atlas[i].name == "_merged_" ) { scale.X = 1.f / atlas[i].width; scale.Y = 1.f / atlas[i].height; return; } } scale.X = 1.f; scale.Y = 1.f; } /*! */ void STextureAtlas::getTranslation ( const c8 * name, core::vector2di &pos ) { u32 i = 0; for ( u32 i = 0; i < atlas.size(); ++i) { if ( atlas[i].name == name ) { pos = atlas[i].pos; return; } } } /*! */ void STextureAtlas::create ( u32 border, E_TEXTURE_CLAMP texmode) { u32 i = 0; u32 w = 0; u32 w2; u32 h2; u32 h; u32 wsum; u32 hsum = 0; ECOLOR_FORMAT format = ECF_R8G8B8; const s32 frame = core::s32_max ( 0, (border - 1 ) / 2 ); // sort for biggest coming first atlas.sort(); // split size wsum = frame; for (i = 0; i < atlas.size(); i++) { // make space w2 = atlas[i].width + border; // align w2 = (w2 + 1) & ~1; wsum += w2; } u32 splitsize = 256; if ( wsum > 512 ) splitsize = 512; wsum = frame; hsum = frame; w = frame; h = 0; for (i = 0; i < atlas.size(); i++) { if ( atlas[i].image->getColorFormat() == ECF_A8R8G8B8 ) { format = ECF_A8R8G8B8; } // make space w2 = atlas[i].width + border; h2 = atlas[i].height + border; // align w2 = (w2 + 1) & ~1; h2 = (h2 + 1) & ~1; h = core::s32_max ( h, h2 ); if ( w + w2 >= splitsize ) { hsum += h; wsum = core::s32_max ( wsum, w ); h = h2; w = frame; } atlas[i].pos.X = w; atlas[i].pos.Y = hsum; w += w2; } hsum += h; wsum = core::s32_max ( wsum, w ); // build image core::dimension2d<u32> dim = core::dimension2d<u32>( wsum, hsum ).getOptimalSize(); IImage* master = new CImage( format, dim ); master->fill ( 0 ); video::SColor col[2]; static const u8 wrap[][4] = { {1, 0 }, // ETC_REPEAT {0, 1 }, // ETC_CLAMP {0, 1 }, // ETC_CLAMP_TO_EDGE {0, 1 } // ETC_MIRROR }; s32 a,b; for (i = 0; i < atlas.size(); i++) { atlas[i].image->copyTo ( master, atlas[i].pos ); // clamp/wrap ( copy edges, filtering needs it ) for ( b = 0; b < frame; ++b ) { for ( a = 0 - b; a <= (s32) atlas[i].width + b; ++a ) { col[0] = atlas[i].image->getPixel ( core::s32_clamp ( a, 0, atlas[i].width - 1 ), 0 ); col[1] = atlas[i].image->getPixel ( core::s32_clamp ( a, 0, atlas[i].width - 1 ), atlas[i].height - 1 ); master->setPixel ( atlas[i].pos.X + a, atlas[i].pos.Y + ( b + 1 ) * -1, col[wrap[texmode][0]] ); master->setPixel ( atlas[i].pos.X + a, atlas[i].pos.Y + atlas[i].height - 1 + ( b + 1 ) * 1, col[wrap[texmode][1]] ); } for ( a = -1 - b; a <= (s32) atlas[i].height + b; ++a ) { col[0] = atlas[i].image->getPixel ( 0, core::s32_clamp ( a, 0, atlas[i].height - 1 ) ); col[1] = atlas[i].image->getPixel ( atlas[i].width - 1, core::s32_clamp ( a, 0, atlas[i].height - 1 ) ); master->setPixel ( atlas[i].pos.X + ( b + 1 ) * -1, atlas[i].pos.Y + a, col[wrap[texmode][0]] ); master->setPixel ( atlas[i].pos.X + atlas[i].width + b, atlas[i].pos.Y + a, col[wrap[texmode][1]] ); } } } addSource ( "_merged_", master ); Master = master; } /*! */ SHalflifeHeader * CAnimatedMeshHalfLife::loadModel( io::IReadFile* file, const io::path &filename ) { bool closefile = false; if ( 0 == file ) { file = SceneManager->getFileSystem()->createAndOpenFile ( filename ); closefile = true; } if ( 0 == file ) return 0; u8 * pin = new u8 [ file->getSize() ]; file->read ( pin, file->getSize() ); SHalflifeHeader * header = (SHalflifeHeader*) pin; const bool idst = 0 == strncmp ( header->id, "IDST", 4); const bool idsq = 0 == strncmp ( header->id, "IDSQ", 4); if ( (!idst && !idsq) || (idsq && !Header) ) { os::Printer::log("MDL Halflife Loader: Wrong file header", file->getFileName(), ELL_WARNING); if ( closefile ) { file->drop(); file = 0; } delete [] pin; return false; } // don't know the real header.. idsg might be different if (header->textureindex && idst ) { io::path fname; io::path ext; io::path path; io::path store; core::splitFilename ( file->getFileName(), &path, &fname, &ext ); TextureBaseName = path + fname + "_"; SHalflifeTexture *tex = (SHalflifeTexture *)(pin + header->textureindex); u32 i; u32 *palette = new u32[256]; for (i = 0; i < header->numtextures; i++) { const u8 *src = pin + tex[i].index; // convert rgb to argb palette { const u8 *pal = src + tex[i].width * tex[i].height; for( u32 g=0; g<256; ++g ) { palette[g] = 0xFF000000 | pal[0] << 16 | pal[1] << 8 | pal[2]; pal += 3; } } IImage* image = new CImage( ECF_R8G8B8, core::dimension2d<u32> ( tex[i].width, tex[i].height ) ); CColorConverter::convert8BitTo24Bit(src, (u8*)image->lock(), tex[i].width, tex[i].height, (u8*) palette, 0, false); image->unlock(); #if 0 core::splitFilename ( tex[i].name, 0, &fname, 0 ); io::path store = io::path ( "c:/h2/convert/" ) + fname + ".bmp"; SceneManager->getVideoDriver()->writeImageToFile ( image, store ); #endif #ifdef HL_TEXTURE_ATLAS TextureAtlas.addSource ( tex[i].name, image ); #else core::splitFilename ( tex[i].name, 0, &fname, &ext ); store = TextureBaseName + fname; SceneManager->getVideoDriver()->addTexture ( store, image ); image->drop(); #endif } delete [] palette; #ifdef HL_TEXTURE_ATLAS TextureAtlas.create ( 2 * 2 + 1, ETC_CLAMP ); store = TextureBaseName + "atlas"; SceneManager->getVideoDriver()->addTexture ( store, TextureAtlas.Master ); #if 0 core::splitFilename ( store, 0, &fname, 0 ); store = io::path ( "c:/h2/convert/" ) + fname + ".bmp"; SceneManager->getVideoDriver()->writeImageToFile ( TextureAtlas.Master, store ); #endif TextureAtlas.release (); #endif } if (!Header) Header = header; if ( closefile ) { file->drop(); file = 0; } return header; } /*! */ f32 CAnimatedMeshHalfLife::SetController( s32 controllerIndex, f32 value ) { if (!Header) return 0.f; SHalflifeBoneController *bonecontroller = (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex); // find first controller that matches the index u32 i; for (i = 0; i < Header->numbonecontrollers; i++, bonecontroller++) { if (bonecontroller->index == controllerIndex) break; } if (i >= Header->numbonecontrollers) return value; // wrap 0..360 if it's a rotational controller if (bonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) { // ugly hack, invert value if end < start if (bonecontroller->end < bonecontroller->start) value = -value; // does the controller not wrap? if (bonecontroller->start + 359.f >= bonecontroller->end) { if (value > ((bonecontroller->start + bonecontroller->end) / 2.f) + 180.f) value = value - 360.f; if (value < ((bonecontroller->start + bonecontroller->end) / 2.f) - 180.f) value = value + 360.f; } else { if (value > 360.f) value = value - (s32)(value / 360.f) * 360.f; else if (value < 0.f) value = value + (s32)((value / -360.f) + 1) * 360.f; } } s32 range = controllerIndex == MOUTH_CONTROLLER ? 64 : 255; s32 setting = (s32) ( (f32) range * (value - bonecontroller->start) / (bonecontroller->end - bonecontroller->start)); if (setting < 0) setting = 0; if (setting > range) setting = range; BoneController[controllerIndex] = setting; return setting * (1.f / (f32) range ) * (bonecontroller->end - bonecontroller->start) + bonecontroller->start; } /*! */ u32 CAnimatedMeshHalfLife::SetSkin( u32 value ) { if (value < Header->numskinfamilies) SkinGroupSelection = value; return SkinGroupSelection; } /*! */ bool CAnimatedMeshHalfLife::postLoadModel( const io::path &filename ) { io::path path; io::path texname; io::path submodel; core::splitFilename ( filename ,&path, &texname, 0 ); // preload textures if (Header->numtextures == 0) { submodel = path + texname + "T.mdl"; TextureHeader = loadModel( 0, submodel ); if (!TextureHeader) return false; OwnTexModel = true; } else { TextureHeader = Header; OwnTexModel = false; } u32 i; // preload animations if (Header->numseqgroups > 1) { c8 seq[8]; for ( i = 1; i < Header->numseqgroups; i++) { snprintf( seq, 8, "%02d.mdl", i ); submodel = path + texname + seq; AnimationHeader[i] = loadModel( 0, submodel ); if (!AnimationHeader[i]) return false; } } return true; } /*! */ void CAnimatedMeshHalfLife::dumpModelInfo ( u32 level ) { u8 *phdr = (u8*) Header; SHalflifeHeader * hdr = Header; u32 i; if ( level == 0 ) { printf ( "Bones: %d\n" "Bone Controllers: %d\n" "Hit Boxes: %d\n" "Sequences: %d\n" "Sequence Groups: %d\n", hdr->numbones, hdr->numbonecontrollers, hdr->numhitboxes, hdr->numseq, hdr->numseqgroups ); printf ( "Textures: %d\n" "Skin Families: %d\n" "Bodyparts: %d\n" "Attachments: %d\n" "Transitions: %d\n", hdr->numtextures, hdr->numskinfamilies, hdr->numbodyparts, hdr->numattachments, hdr->numtransitions); return; } printf("id: %c%c%c%c\n", phdr[0], phdr[1], phdr[2], phdr[3]); printf("version: %d\n", hdr->version); printf("name: \"%s\"\n", hdr->name); printf("length: %d\n\n", hdr->length); printf("eyeposition: %f %f %f\n", hdr->eyeposition[0], hdr->eyeposition[1], hdr->eyeposition[2]); printf("min: %f %f %f\n", hdr->min[0], hdr->min[1], hdr->min[2]); printf("max: %f %f %f\n", hdr->max[0], hdr->max[1], hdr->max[2]); printf("bbmin: %f %f %f\n", hdr->bbmin[0], hdr->bbmin[1], hdr->bbmin[2]); printf("bbmax: %f %f %f\n", hdr->bbmax[0], hdr->bbmax[1], hdr->bbmax[2]); printf("flags: %d\n\n", hdr->flags); printf("numbones: %d\n", hdr->numbones); for (i = 0; i < hdr->numbones; i++) { SHalflifeBone *bone = (SHalflifeBone *) (phdr + hdr->boneindex); printf("bone %d.name: \"%s\"\n", i + 1, bone[i].name); printf("bone %d.parent: %d\n", i + 1, bone[i].parent); printf("bone %d.flags: %d\n", i + 1, bone[i].flags); printf("bone %d.bonecontroller: %d %d %d %d %d %d\n", i + 1, bone[i].bonecontroller[0], bone[i].bonecontroller[1], bone[i].bonecontroller[2], bone[i].bonecontroller[3], bone[i].bonecontroller[4], bone[i].bonecontroller[5]); printf("bone %d.value: %f %f %f %f %f %f\n", i + 1, bone[i].value[0], bone[i].value[1], bone[i].value[2], bone[i].value[3], bone[i].value[4], bone[i].value[5]); printf("bone %d.scale: %f %f %f %f %f %f\n", i + 1, bone[i].scale[0], bone[i].scale[1], bone[i].scale[2], bone[i].scale[3], bone[i].scale[4], bone[i].scale[5]); } printf("\nnumbonecontrollers: %d\n", hdr->numbonecontrollers); SHalflifeBoneController *bonecontrollers = (SHalflifeBoneController *) (phdr + hdr->bonecontrollerindex); for (i = 0; i < hdr->numbonecontrollers; i++) { printf("bonecontroller %d.bone: %d\n", i + 1, bonecontrollers[i].bone); printf("bonecontroller %d.type: %d\n", i + 1, bonecontrollers[i].type); printf("bonecontroller %d.start: %f\n", i + 1, bonecontrollers[i].start); printf("bonecontroller %d.end: %f\n", i + 1, bonecontrollers[i].end); printf("bonecontroller %d.rest: %d\n", i + 1, bonecontrollers[i].rest); printf("bonecontroller %d.index: %d\n", i + 1, bonecontrollers[i].index); } printf("\nnumhitboxes: %d\n", hdr->numhitboxes); SHalflifeBBox *box = (SHalflifeBBox *) (phdr + hdr->hitboxindex); for (i = 0; i < hdr->numhitboxes; i++) { printf("hitbox %d.bone: %d\n", i + 1, box[i].bone); printf("hitbox %d.group: %d\n", i + 1, box[i].group); printf("hitbox %d.bbmin: %f %f %f\n", i + 1, box[i].bbmin[0], box[i].bbmin[1], box[i].bbmin[2]); printf("hitbox %d.bbmax: %f %f %f\n", i + 1, box[i].bbmax[0], box[i].bbmax[1], box[i].bbmax[2]); } printf("\nnumseq: %d\n", hdr->numseq); SHalflifeSequence *seq = (SHalflifeSequence *) (phdr + hdr->seqindex); for (i = 0; i < hdr->numseq; i++) { printf("seqdesc %d.label: \"%s\"\n", i + 1, seq[i].label); printf("seqdesc %d.fps: %f\n", i + 1, seq[i].fps); printf("seqdesc %d.flags: %d\n", i + 1, seq[i].flags); printf("<...>\n"); } printf("\nnumseqgroups: %d\n", hdr->numseqgroups); for (i = 0; i < hdr->numseqgroups; i++) { SHalflifeSequenceGroup *group = (SHalflifeSequenceGroup *) (phdr + hdr->seqgroupindex); printf("\nseqgroup %d.label: \"%s\"\n", i + 1, group[i].label); printf("\nseqgroup %d.namel: \"%s\"\n", i + 1, group[i].name); printf("\nseqgroup %d.data: %d\n", i + 1, group[i].data); } printf("\nnumskinref: %d\n", hdr->numskinref); printf("numskinfamilies: %d\n", hdr->numskinfamilies); printf("\nnumbodyparts: %d\n", hdr->numbodyparts); SHalflifeBody *pbodyparts = (SHalflifeBody*) ((u8*) hdr + hdr->bodypartindex); for (i = 0; i < hdr->numbodyparts; i++) { printf("bodypart %d.name: \"%s\"\n", i + 1, pbodyparts[i].name); printf("bodypart %d.nummodels: %d\n", i + 1, pbodyparts[i].nummodels); printf("bodypart %d.base: %d\n", i + 1, pbodyparts[i].base); printf("bodypart %d.modelindex: %d\n", i + 1, pbodyparts[i].modelindex); } printf("\nnumattachments: %d\n", hdr->numattachments); for (i = 0; i < hdr->numattachments; i++) { SHalfelifeAttachment *attach = (SHalfelifeAttachment *) ((u8*) hdr + hdr->attachmentindex); printf("attachment %d.name: \"%s\"\n", i + 1, attach[i].name); } hdr = TextureHeader; printf("\nnumtextures: %d\n", hdr->numtextures); printf("textureindex: %d\n", hdr->textureindex); printf("texturedataindex: %d\n", hdr->texturedataindex); SHalflifeTexture *ptextures = (SHalflifeTexture *) ((u8*) hdr + hdr->textureindex); for (i = 0; i < hdr->numtextures; i++) { printf("texture %d.name: \"%s\"\n", i + 1, ptextures[i].name); printf("texture %d.flags: %d\n", i + 1, ptextures[i].flags); printf("texture %d.width: %d\n", i + 1, ptextures[i].width); printf("texture %d.height: %d\n", i + 1, ptextures[i].height); printf("texture %d.index: %d\n", i + 1, ptextures[i].index); } } /*! */ void CAnimatedMeshHalfLife::ExtractBbox( s32 sequence, core::aabbox3df &box ) { SHalflifeSequence *seq = (SHalflifeSequence *)((u8*)Header + Header->seqindex) + sequence; box.MinEdge.X = seq[0].bbmin[0]; box.MinEdge.Y = seq[0].bbmin[1]; box.MinEdge.Z = seq[0].bbmin[2]; box.MaxEdge.X = seq[0].bbmax[0]; box.MaxEdge.Y = seq[0].bbmax[1]; box.MaxEdge.Z = seq[0].bbmax[2]; } /*! */ void CAnimatedMeshHalfLife::calcBoneAdj() { u32 j; s32 i; f32 value; SHalflifeBoneController *bonecontroller; bonecontroller = (SHalflifeBoneController *)((u8*) Header + Header->bonecontrollerindex); for (j = 0; j < Header->numbonecontrollers; j++) { i = bonecontroller[j].index; f32 range = i <= 3 ? 255.f : 64.f; // check for 360% wrapping if (bonecontroller[j].type & STUDIO_RLOOP) { value = BoneController[i] * (360.f/256.f) + bonecontroller[j].start; } else { value = BoneController[i] / range; if (value < 0.f) value = 0.f; if (value > 1.f) value = 1.f; value = (1.f - value) * bonecontroller[j].start + value * bonecontroller[j].end; } switch(bonecontroller[j].type & STUDIO_TYPES) { case STUDIO_XR: case STUDIO_YR: case STUDIO_ZR: BoneAdj[j] = value * (core::PI / 180.f); break; case STUDIO_X: case STUDIO_Y: case STUDIO_Z: BoneAdj[j] = value; break; } } } /*! */ void CAnimatedMeshHalfLife::calcBoneQuaternion( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *q ) const { s32 j, k; vec4_hl q1, q2; vec3_hl angle1, angle2; SHalfelifeAnimationFrame *animvalue; for (j = 0; j < 3; j++) { if (anim->offset[j+3] == 0) { angle2[j] = angle1[j] = bone->value[j+3]; // default; } else { animvalue = (SHalfelifeAnimationFrame *)((u8*)anim + anim->offset[j+3]); k = frame; while (animvalue->num.total <= k) { k -= animvalue->num.total; animvalue += animvalue->num.valid + 1; } // Bah, missing blend! if (animvalue->num.valid > k) { angle1[j] = animvalue[k+1].value; if (animvalue->num.valid > k + 1) { angle2[j] = animvalue[k+2].value; } else { if (animvalue->num.total > k + 1) angle2[j] = angle1[j]; else angle2[j] = animvalue[animvalue->num.valid+2].value; } } else { angle1[j] = animvalue[animvalue->num.valid].value; if (animvalue->num.total > k + 1) { angle2[j] = angle1[j]; } else { angle2[j] = animvalue[animvalue->num.valid + 2].value; } } angle1[j] = bone->value[j+3] + angle1[j] * bone->scale[j+3]; angle2[j] = bone->value[j+3] + angle2[j] * bone->scale[j+3]; } if (bone->bonecontroller[j+3] != -1) { angle1[j] += BoneAdj[bone->bonecontroller[j+3]]; angle2[j] += BoneAdj[bone->bonecontroller[j+3]]; } } if (!VectorCompare( angle1, angle2 )) { AngleQuaternion( angle1, q1 ); AngleQuaternion( angle2, q2 ); QuaternionSlerp( q1, q2, s, q ); } else { AngleQuaternion( angle1, q ); } } /*! */ void CAnimatedMeshHalfLife::calcBonePosition( s32 frame, f32 s, SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *pos ) const { s32 j, k; SHalfelifeAnimationFrame *animvalue; for (j = 0; j < 3; j++) { pos[j] = bone->value[j]; // default; if (anim->offset[j] != 0) { animvalue = (SHalfelifeAnimationFrame *)((u8*)anim + anim->offset[j]); k = frame; // find span of values that includes the frame we want while (animvalue->num.total <= k) { k -= animvalue->num.total; animvalue += animvalue->num.valid + 1; } // if we're inside the span if (animvalue->num.valid > k) { // and there's more data in the span if (animvalue->num.valid > k + 1) { pos[j] += (animvalue[k+1].value * (1.f - s) + s * animvalue[k+2].value) * bone->scale[j]; } else { pos[j] += animvalue[k+1].value * bone->scale[j]; } } else { // are we at the end of the repeating values section and there's another section with data? if (animvalue->num.total <= k + 1) { pos[j] += (animvalue[animvalue->num.valid].value * (1.f - s) + s * animvalue[animvalue->num.valid + 2].value) * bone->scale[j]; } else { pos[j] += animvalue[animvalue->num.valid].value * bone->scale[j]; } } } if (bone->bonecontroller[j] != -1) { pos[j] += BoneAdj[bone->bonecontroller[j]]; } } } /*! */ void CAnimatedMeshHalfLife::calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f ) { s32 frame; SHalflifeBone *bone; f32 s; frame = (s32)f; s = (f - frame); // add in programatic controllers calcBoneAdj( ); bone = (SHalflifeBone *)((u8 *)Header + Header->boneindex); for ( u32 i = 0; i < Header->numbones; i++, bone++, anim++) { calcBoneQuaternion( frame, s, bone, anim, q[i] ); calcBonePosition( frame, s, bone, anim, pos[i] ); } if (seq->motiontype & STUDIO_X) pos[seq->motionbone][0] = 0.f; if (seq->motiontype & STUDIO_Y) pos[seq->motionbone][1] = 0.f; if (seq->motiontype & STUDIO_Z) pos[seq->motionbone][2] = 0.f; } /*! */ SHalflifeAnimOffset * CAnimatedMeshHalfLife::getAnim( SHalflifeSequence *seq ) { SHalflifeSequenceGroup *seqgroup; seqgroup = (SHalflifeSequenceGroup *)((u8*)Header + Header->seqgroupindex) + seq->seqgroup; if (seq->seqgroup == 0) { return (SHalflifeAnimOffset *)((u8*)Header + seqgroup->data + seq->animindex); } return (SHalflifeAnimOffset *)((u8*)AnimationHeader[seq->seqgroup] + seq->animindex); } /*! */ void CAnimatedMeshHalfLife::slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s ) { vec4_hl q3; f32 s1; if (s < 0) s = 0; else if (s > 1.f) s = 1.f; s1 = 1.f - s; for ( u32 i = 0; i < Header->numbones; i++) { QuaternionSlerp( q1[i], q2[i], s, q3 ); q1[i][0] = q3[0]; q1[i][1] = q3[1]; q1[i][2] = q3[2]; q1[i][3] = q3[3]; pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s; pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s; pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s; } } /*! */ void CAnimatedMeshHalfLife::setUpBones () { SHalflifeBone *bone; SHalflifeSequence *seq; SHalflifeAnimOffset *anim; static vec3_hl pos[MAXSTUDIOBONES]; f32 bonematrix[3][4]; static vec4_hl q[MAXSTUDIOBONES]; static vec3_hl pos2[MAXSTUDIOBONES]; static vec4_hl q2[MAXSTUDIOBONES]; static vec3_hl pos3[MAXSTUDIOBONES]; static vec4_hl q3[MAXSTUDIOBONES]; static vec3_hl pos4[MAXSTUDIOBONES]; static vec4_hl q4[MAXSTUDIOBONES]; if (SequenceIndex >= Header->numseq) SequenceIndex = 0; seq = (SHalflifeSequence *)((u8*) Header + Header->seqindex) + SequenceIndex; anim = getAnim( seq ); calcRotations( pos, q, seq, anim, CurrentFrame ); if (seq->numblends > 1) { f32 s; anim += Header->numbones; calcRotations( pos2, q2, seq, anim, CurrentFrame ); s = Blending[0] / 255.f; slerpBones( q, pos, q2, pos2, s ); if (seq->numblends == 4) { anim += Header->numbones; calcRotations( pos3, q3, seq, anim, CurrentFrame ); anim += Header->numbones; calcRotations( pos4, q4, seq, anim, CurrentFrame ); s = Blending[0] / 255.f; slerpBones( q3, pos3, q4, pos4, s ); s = Blending[1] / 255.f; slerpBones( q, pos, q3, pos3, s ); } } bone = (SHalflifeBone *)((u8*) Header + Header->boneindex); for (u32 i = 0; i < Header->numbones; i++) { QuaternionMatrix( q[i], bonematrix ); bonematrix[0][3] = pos[i][0]; bonematrix[1][3] = pos[i][1]; bonematrix[2][3] = pos[i][2]; if (bone[i].parent == -1) { memcpy(BoneTransform[i], bonematrix, sizeof(f32) * 12); } else { R_ConcatTransforms (BoneTransform[bone[i].parent], bonematrix, BoneTransform[i]); } } } //! Returns an axis aligned bounding box const core::aabbox3d<f32>& CAnimatedMeshHalfLife::getBoundingBox() const { return MeshIPol.BoundingBox; } //! Returns the type of the animated mesh. E_ANIMATED_MESH_TYPE CAnimatedMeshHalfLife::getMeshType() const { return EAMT_MDL_HALFLIFE; } //! returns amount of mesh buffers. u32 CAnimatedMeshHalfLife::getMeshBufferCount() const { return MeshIPol.getMeshBufferCount(); } //! returns pointer to a mesh buffer IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer(u32 nr) const { return MeshIPol.getMeshBuffer ( nr ); } //! Returns pointer to a mesh buffer which fits a material /** \param material: material to search for \return Returns the pointer to the mesh buffer or NULL if there is no such mesh buffer. */ IMeshBuffer* CAnimatedMeshHalfLife::getMeshBuffer( const video::SMaterial &material) const { return MeshIPol.getMeshBuffer ( material ); } void CAnimatedMeshHalfLife::setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue) { MeshIPol.setMaterialFlag ( flag, newvalue ); } //! set user axis aligned bounding box void CAnimatedMeshHalfLife::setBoundingBox(const core::aabbox3df& box) { return; } } // end namespace scene } // end namespace irr #endif // _IRR_COMPILE_WITH_MD3_LOADER_
Options | Liens officiels | Caractéristiques | Statistiques | Communauté |
---|---|---|---|---|
Corrections |
|
xhtml 1.0 css 2.1 Propulsé par FluxBB Traduit par FluxBB.fr |
882 membres 1429 sujets 11119 messages |
Dernier membre inscrit: LiseBuisson96 83 invités en ligne Aucun membre connecté RSS Feed |