// Copyright (C) 2002-2012 Thomas Alten // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #ifndef __C_ANIMATED_MESH_HALFLIFE_H_INCLUDED__ #define __C_ANIMATED_MESH_HALFLIFE_H_INCLUDED__ #include "IAnimatedMesh.h" #include "ISceneManager.h" #include "irrArray.h" #include "irrString.h" #include "IMeshLoader.h" #include "SMesh.h" #include "IReadFile.h" namespace irr { namespace scene { // STUDIO MODELS, Copyright (c) 1998, Valve LLC. All rights reserved. #define MAXSTUDIOTRIANGLES 20000 // TODO: tune this #define MAXSTUDIOVERTS 2048 // TODO: tune this #define MAXSTUDIOSEQUENCES 256 // total animation sequences #define MAXSTUDIOSKINS 100 // total textures #define MAXSTUDIOSRCBONES 512 // bones allowed at source movement #define MAXSTUDIOBONES 128 // total bones actually used #define MAXSTUDIOMODELS 32 // sub-models per model #define MAXSTUDIOBODYPARTS 32 #define MAXSTUDIOGROUPS 4 #define MAXSTUDIOANIMATIONS 512 // per sequence #define MAXSTUDIOMESHES 256 #define MAXSTUDIOEVENTS 1024 #define MAXSTUDIOPIVOTS 256 #define MAXSTUDIOCONTROLLERS 8 typedef f32 vec3_hl[3]; // x,y,z typedef f32 vec4_hl[4]; // x,y,z,w // byte-align structures #include "irrpack.h" struct SHalflifeHeader { c8 id[4]; s32 version; c8 name[64]; s32 length; vec3_hl eyeposition; // ideal eye position vec3_hl min; // ideal movement hull size vec3_hl max; vec3_hl bbmin; // clipping bounding box vec3_hl bbmax; s32 flags; u32 numbones; // bones u32 boneindex; u32 numbonecontrollers; // bone controllers u32 bonecontrollerindex; u32 numhitboxes; // complex bounding boxes u32 hitboxindex; u32 numseq; // animation sequences u32 seqindex; u32 numseqgroups; // demand loaded sequences u32 seqgroupindex; u32 numtextures; // raw textures u32 textureindex; u32 texturedataindex; u32 numskinref; // replaceable textures u32 numskinfamilies; u32 skinindex; u32 numbodyparts; u32 bodypartindex; u32 numattachments; // queryable attachable points u32 attachmentindex; s32 soundtable; s32 soundindex; s32 soundgroups; s32 soundgroupindex; s32 numtransitions; // animation node to animation node transition graph s32 transitionindex; } PACK_STRUCT; // header for demand loaded sequence group data struct studioseqhdr_t { s32 id; s32 version; c8 name[64]; s32 length; } PACK_STRUCT; // bones struct SHalflifeBone { c8 name[32]; // bone name for symbolic links s32 parent; // parent bone s32 flags; // ?? s32 bonecontroller[6]; // bone controller index, -1 == none f32 value[6]; // default DoF values f32 scale[6]; // scale for delta DoF values } PACK_STRUCT; // bone controllers struct SHalflifeBoneController { s32 bone; // -1 == 0 s32 type; // X, Y, Z, XR, YR, ZR, M f32 start; f32 end; s32 rest; // byte index value at rest s32 index; // 0-3 user set controller, 4 mouth } PACK_STRUCT; // intersection boxes struct SHalflifeBBox { s32 bone; s32 group; // intersection group vec3_hl bbmin; // bounding box vec3_hl bbmax; } PACK_STRUCT; #ifndef ZONE_H // NOTE: this was a void*, but that crashes on 64bit. // I have found no mdl format desc, so not sure what it's meant to be, but s32 at least works. typedef s32 cache_user_t; #endif // demand loaded sequence groups struct SHalflifeSequenceGroup { c8 label[32]; // textual name c8 name[64]; // file name cache_user_t cache; // cache index pointer s32 data; // hack for group 0 } PACK_STRUCT; // sequence descriptions struct SHalflifeSequence { c8 label[32]; // sequence label f32 fps; // frames per second s32 flags; // looping/non-looping flags s32 activity; s32 actweight; s32 numevents; s32 eventindex; s32 numframes; // number of frames per sequence u32 numpivots; // number of foot pivots u32 pivotindex; s32 motiontype; s32 motionbone; vec3_hl linearmovement; s32 automoveposindex; s32 automoveangleindex; vec3_hl bbmin; // per sequence bounding box vec3_hl bbmax; s32 numblends; s32 animindex; // SHalflifeAnimOffset pointer relative to start of sequence group data // [blend][bone][X, Y, Z, XR, YR, ZR] s32 blendtype[2]; // X, Y, Z, XR, YR, ZR f32 blendstart[2]; // starting value f32 blendend[2]; // ending value s32 blendparent; s32 seqgroup; // sequence group for demand loading s32 entrynode; // transition node at entry s32 exitnode; // transition node at exit s32 nodeflags; // transition rules s32 nextseq; // auto advancing sequences } PACK_STRUCT; // events struct mstudioevent_t { s32 frame; s32 event; s32 type; c8 options[64]; } PACK_STRUCT; // pivots struct mstudiopivot_t { vec3_hl org; // pivot point s32 start; s32 end; } PACK_STRUCT; // attachment struct SHalflifeAttachment { c8 name[32]; s32 type; s32 bone; vec3_hl org; // attachment point vec3_hl vectors[3]; } PACK_STRUCT; struct SHalflifeAnimOffset { u16 offset[6]; } PACK_STRUCT; // animation frames union SHalflifeAnimationFrame { struct { u8 valid; u8 total; } PACK_STRUCT num; s16 value; } PACK_STRUCT; // body part index struct SHalflifeBody { c8 name[64]; u32 nummodels; u32 base; u32 modelindex; // index into models array } PACK_STRUCT; // skin info struct SHalflifeTexture { c8 name[64]; s32 flags; s32 width; s32 height; s32 index; } PACK_STRUCT; // skin families // short index[skinfamilies][skinref] // studio models struct SHalflifeModel { c8 name[64]; s32 type; f32 boundingradius; u32 nummesh; u32 meshindex; u32 numverts; // number of unique vertices u32 vertinfoindex; // vertex bone info u32 vertindex; // vertex vec3_hl u32 numnorms; // number of unique surface normals u32 norminfoindex; // normal bone info u32 normindex; // normal vec3_hl u32 numgroups; // deformation groups u32 groupindex; } PACK_STRUCT; // meshes struct SHalflifeMesh { u32 numtris; u32 triindex; u32 skinref; u32 numnorms; // per mesh normals u32 normindex; // normal vec3_hl } PACK_STRUCT; // Default alignment #include "irrunpack.h" // lighting options #define STUDIO_NF_FLATSHADE 0x0001 #define STUDIO_NF_CHROME 0x0002 #define STUDIO_NF_FULLBRIGHT 0x0004 // motion flags #define STUDIO_X 0x0001 #define STUDIO_Y 0x0002 #define STUDIO_Z 0x0004 #define STUDIO_XR 0x0008 #define STUDIO_YR 0x0010 #define STUDIO_ZR 0x0020 #define STUDIO_LX 0x0040 #define STUDIO_LY 0x0080 #define STUDIO_LZ 0x0100 #define STUDIO_AX 0x0200 #define STUDIO_AY 0x0400 #define STUDIO_AZ 0x0800 #define STUDIO_AXR 0x1000 #define STUDIO_AYR 0x2000 #define STUDIO_AZR 0x4000 #define STUDIO_TYPES 0x7FFF #define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance // sequence flags #define STUDIO_LOOPING 0x0001 // bone flags #define STUDIO_HAS_NORMALS 0x0001 #define STUDIO_HAS_VERTICES 0x0002 #define STUDIO_HAS_BBOX 0x0004 #define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them #define RAD_TO_STUDIO (32768.0/M_PI) #define STUDIO_TO_RAD (M_PI/32768.0) /*! Textureatlas Combine Source Images with arbitrary size and bithdepth to an Image with 2^n size borders from the source images are copied around for allowing filtering ( bilinear, mipmap ) */ struct STextureAtlas { STextureAtlas () { release(); } virtual ~STextureAtlas () { release (); } void release (); void addSource ( const c8 * name, video::IImage * image ); void create ( u32 pixelborder, video::E_TEXTURE_CLAMP texmode ); void getScale ( core::vector2df &scale ); void getTranslation ( const c8 * name, core::vector2di &pos ); struct TextureAtlasEntry { io::path name; u32 width; u32 height; core::vector2di pos; video::IImage * image; bool operator < ( const TextureAtlasEntry & other ) { return height > other.height; } }; core::array < TextureAtlasEntry > atlas; video::IImage * Master; }; //! Possible types of Animation Type enum E_ANIMATION_TYPE { //! No Animation EAMT_STILL, //! From Start to End, then Stop ( Limited Line ) EAMT_WAYPOINT, //! Linear Cycling Animation ( Sawtooth ) EAMT_LOOPING, //! Linear bobbing ( Triangle ) EAMT_PINGPONG }; //! Names for Animation Type const c8* const MeshAnimationTypeNames[] = { "still", "waypoint", "looping", "pingpong", 0 }; //! Data for holding named Animation Info struct KeyFrameInterpolation { core::stringc Name; // Name of the current Animation/Bone E_ANIMATION_TYPE AnimationType; // Type of Animation ( looping, usw..) f32 CurrentFrame; // Current Frame s32 NextFrame; // Frame which will be used next. For blending s32 StartFrame; // Absolute Frame where the current animation start s32 Frames; // Relative Frames how much Frames this animation have s32 LoopingFrames; // How much of Frames sould be looped s32 EndFrame; // Absolute Frame where the current animation ends End = start + frames - 1 f32 FramesPerSecond; // Speed in Frames/Seconds the animation is played f32 RelativeSpeed; // Factor Original fps is modified u32 BeginTime; // Animation started at this thime u32 EndTime; // Animation end at this time u32 LastTime; // Last Keyframe was done at this time KeyFrameInterpolation ( const c8 * name = "", s32 start = 0, s32 frames = 0, s32 loopingframes = 0, f32 fps = 0.f, f32 relativefps = 1.f ) : Name ( name ), AnimationType ( loopingframes ? EAMT_LOOPING : EAMT_WAYPOINT), CurrentFrame ( (f32) start ), NextFrame ( start ), StartFrame ( start ), Frames ( frames ), LoopingFrames ( loopingframes ), EndFrame ( start + frames - 1 ), FramesPerSecond ( fps ), RelativeSpeed ( relativefps ), BeginTime ( 0 ), EndTime ( 0 ), LastTime ( 0 ) { } // linear search bool operator == ( const KeyFrameInterpolation & other ) const { return Name.equals_ignore_case ( other.Name ); } }; //! a List holding named Animations typedef core::array < KeyFrameInterpolation > IAnimationList; //! a List holding named Skins typedef core::array < core::stringc > ISkinList; // Current Model per Body struct SubModel { core::stringc name; u32 startBuffer; u32 endBuffer; u32 state; }; struct BodyPart { core::stringc name; u32 defaultModel; core::array < SubModel > model; }; //! a List holding named Models and SubModels typedef core::array < BodyPart > IBodyList; class CAnimatedMeshHalfLife : public IAnimatedMesh { public: //! constructor CAnimatedMeshHalfLife(); //! destructor virtual ~CAnimatedMeshHalfLife(); //! loads a Halflife mdl file bool loadModelFile( io::IReadFile* file, ISceneManager * smgr ); //IAnimatedMesh virtual u32 getFrameCount() const _IRR_OVERRIDE_; virtual IMesh* getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) _IRR_OVERRIDE_; virtual const core::aabbox3d& getBoundingBox() const _IRR_OVERRIDE_; virtual E_ANIMATED_MESH_TYPE getMeshType() const _IRR_OVERRIDE_; void renderModel ( u32 param, video::IVideoDriver * driver, const core::matrix4 &absoluteTransformation); //! returns amount of mesh buffers. virtual u32 getMeshBufferCount() const _IRR_OVERRIDE_; //! returns pointer to a mesh buffer virtual IMeshBuffer* getMeshBuffer(u32 nr) const _IRR_OVERRIDE_; //! Returns pointer to a mesh buffer which fits a material virtual IMeshBuffer* getMeshBuffer( const video::SMaterial &material) const _IRR_OVERRIDE_; virtual void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue) _IRR_OVERRIDE_; //! set the hardware mapping hint, for driver virtual void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer=EBT_VERTEX_AND_INDEX) _IRR_OVERRIDE_; //! flags the meshbuffer as changed, reloads hardware buffers virtual void setDirty(E_BUFFER_TYPE buffer=EBT_VERTEX_AND_INDEX) _IRR_OVERRIDE_; //! set user axis aligned bounding box virtual void setBoundingBox(const core::aabbox3df& box) _IRR_OVERRIDE_; //! Gets the default animation speed of the animated mesh. /** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */ virtual f32 getAnimationSpeed() const _IRR_OVERRIDE_ { return FramesPerSecond; } //! Gets the frame count of the animated mesh. /** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated. The actual speed is set in the scene node the mesh is instantiated in.*/ virtual void setAnimationSpeed(f32 fps) _IRR_OVERRIDE_ { FramesPerSecond=fps; } //! Get the Animation List IAnimationList* getAnimList () { return &AnimList; } //! Return the named Body List of this Animated Mesh IBodyList *getBodyList() { return &BodyList; } private: // KeyFrame Animation List IAnimationList AnimList; // Sum of all sequences u32 FrameCount; // Named meshes of the Body IBodyList BodyList; //! return a Mesh per frame SMesh* MeshIPol; ISceneManager *SceneManager; SHalflifeHeader *Header; SHalflifeHeader *TextureHeader; bool OwnTexModel; // do we have a modelT.mdl ? SHalflifeHeader *AnimationHeader[32]; // sequences named model01.mdl, model02.mdl void initData (); SHalflifeHeader * loadModel( io::IReadFile* file, const io::path &filename ); bool postLoadModel( const io::path &filename ); u32 SequenceIndex; // sequence index f32 CurrentFrame; // Current Frame f32 FramesPerSecond; #define MOUTH_CONTROLLER 4 u8 BoneController[4 + 1 ]; // bone controllers + mouth position u8 Blending[2]; // animation blending vec4_hl BoneAdj; f32 SetController( s32 controllerIndex, f32 value ); u32 SkinGroupSelection; // skin group selection u32 SetSkin( u32 value ); void initModel(); void dumpModelInfo(u32 level) const; void ExtractBbox(s32 sequence, core::aabbox3df &box) const; void setUpBones (); SHalflifeAnimOffset * getAnim( SHalflifeSequence *seq ); void slerpBones( vec4_hl q1[], vec3_hl pos1[], vec4_hl q2[], vec3_hl pos2[], f32 s ); void calcRotations ( vec3_hl *pos, vec4_hl *q, SHalflifeSequence *seq, SHalflifeAnimOffset *anim, f32 f ); void calcBoneAdj(); void calcBoneQuaternion(const s32 frame, const SHalflifeBone *bone, SHalflifeAnimOffset *anim, const u32 j, f32& angle1, f32& angle2) const; void calcBonePosition(const s32 frame, f32 s, const SHalflifeBone *bone, SHalflifeAnimOffset *anim, f32 *pos ) const; void buildVertices (); io::path TextureBaseName; #define HL_TEXTURE_ATLAS #ifdef HL_TEXTURE_ATLAS STextureAtlas TextureAtlas; // video::ITexture *TextureMaster; #endif }; //! Meshloader capable of loading HalfLife Model files class CHalflifeMDLMeshFileLoader : public IMeshLoader { public: //! Constructor CHalflifeMDLMeshFileLoader( scene::ISceneManager* smgr ); //! returns true if the file maybe is able to be loaded by this class /** based on the file extension (e.g. ".bsp") */ virtual bool isALoadableFileExtension(const io::path& filename) const _IRR_OVERRIDE_; //! 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. */ virtual IAnimatedMesh* createMesh(io::IReadFile* file) _IRR_OVERRIDE_; private: scene::ISceneManager* SceneManager; }; } // end namespace scene } // end namespace irr #endif