/* =========================================================================== Copyright (C) 2000 - 2013, Raven Software, Inc. Copyright (C) 2001 - 2013, Activision, Inc. Copyright (C) 2013 - 2015, OpenJK contributors This file is part of the OpenJK source code. OpenJK is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . =========================================================================== */ #pragma once #if !defined(GHOUL2_SHARED_H_INC) #define GHOUL2_SHARED_H_INC #include #include #define G2T_SV_TIME (0) #define G2T_CG_TIME (1) #define NUM_G2T_TIME (2) void G2API_SetTime(int currentTime,int clock); int G2API_GetTime(int argTime); // this may or may not return arg depending on ghoul2_time cvar //=================================================================== // // G H O U L I I D E F I N E S // // we save the whole surfaceInfo_t struct struct surfaceInfo_t { int offFlags; // what the flags are for this model int surface; // index into array held inside the model definition of pointers to the actual surface data loaded in - used by both client and game float genBarycentricJ; // point 0 barycentric coors float genBarycentricI; // point 1 barycentric coors - point 2 is 1 - point0 - point1 int genPolySurfaceIndex; // used to point back to the original surface and poly if this is a generated surface int genLod; // used to determine original lod of original surface and poly hit location surfaceInfo_t(): offFlags(0), surface(0), genBarycentricJ(0), genBarycentricI(0), genPolySurfaceIndex(0), genLod(0) {} void sg_export( ojk::SavedGameHelper& saved_game) const { saved_game.write(offFlags); saved_game.write(surface); saved_game.write(genBarycentricJ); saved_game.write(genBarycentricI); saved_game.write(genPolySurfaceIndex); saved_game.write(genLod); } void sg_import( ojk::SavedGameHelper& saved_game) { saved_game.read(offFlags); saved_game.read(surface); saved_game.read(genBarycentricJ); saved_game.read(genBarycentricI); saved_game.read(genPolySurfaceIndex); saved_game.read(genLod); } }; #define BONE_ANGLES_PREMULT 0x0001 #define BONE_ANGLES_POSTMULT 0x0002 #define BONE_ANGLES_REPLACE 0x0004 //rww - RAGDOLL_BEGIN #define BONE_ANGLES_RAGDOLL 0x2000 // the rag flags give more details #define BONE_ANGLES_IK 0x4000 // the rag flags give more details //rww - RAGDOLL_END #define BONE_ANGLES_TOTAL ( BONE_ANGLES_PREMULT | BONE_ANGLES_POSTMULT | BONE_ANGLES_REPLACE ) #define BONE_ANIM_OVERRIDE 0x0008 #define BONE_ANIM_OVERRIDE_LOOP 0x0010 // Causes Last Frame To Lerp to First Frame And Start Over #define BONE_ANIM_OVERRIDE_FREEZE (0x0040 + BONE_ANIM_OVERRIDE) // Causes Last Frame To Freeze And Not Loop To Beginning #define BONE_ANIM_BLEND 0x0080 // Blends to and from previously played frame on same bone for given time #define BONE_ANIM_NO_LERP 0x1000 #define BONE_ANIM_TOTAL (BONE_ANIM_NO_LERP| BONE_ANIM_OVERRIDE | BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND ) #define BONE_INDEX_INVALID -1 /*#define MDXABONEDEF // used in the mdxformat.h file to stop redefinitions of the bone struct. typedef struct { float matrix[3][4]; } mdxaBone_t; */ #include "../rd-common/mdx_format.h" // we save the whole structure here. struct boneInfo_t { int boneNumber; // what bone are we overriding? mdxaBone_t matrix; // details of bone angle overrides - some are pre-done on the server, some in ghoul2 int flags; // flags for override int startFrame; // start frame for animation int endFrame; // end frame for animation NOTE anim actually ends on endFrame+1 int startTime; // time we started this animation int pauseTime; // time we paused this animation - 0 if not paused float animSpeed; // speed at which this anim runs. 1.0f means full speed of animation incoming - ie if anim is 20hrtz, we run at 20hrts. If 5hrts, we run at 5 hrts float blendFrame; // frame PLUS LERP value to blend from int blendLerpFrame; // frame to lerp the blend frame with. int blendTime; // Duration time for blending - used to calc amount each frame of new anim is blended with last frame of the last anim int blendStart; // Time when blending starts - not necessarily the same as startTime since we might start half way through an anim int boneBlendTime; // time for duration of bone angle blend with normal animation int boneBlendStart; // time bone angle blend with normal animation began mdxaBone_t newMatrix; // This is the lerped matrix that Ghoul2 uses on the client side - does not go across the network //rww - RAGDOLL_BEGIN int lastTimeUpdated; // if non-zero this is all intialized int lastContents; vec3_t lastPosition; vec3_t velocityEffector; vec3_t lastAngles; vec3_t minAngles; vec3_t maxAngles; vec3_t currentAngles; vec3_t anglesOffset; vec3_t positionOffset; float radius; float weight; // current radius cubed int ragIndex; vec3_t velocityRoot; // I am really tired of recomiling the whole game to add a param here int ragStartTime; int firstTime; int firstCollisionTime; int restTime; int RagFlags; int DependentRagIndexMask; mdxaBone_t originalTrueBoneMatrix; mdxaBone_t parentTrueBoneMatrix; // figure I will need this sooner or later mdxaBone_t parentOriginalTrueBoneMatrix; // figure I will need this sooner or later vec3_t originalOrigin; vec3_t originalAngles; vec3_t lastShotDir; mdxaBone_t *basepose; mdxaBone_t *baseposeInv; mdxaBone_t *baseposeParent; mdxaBone_t *baseposeInvParent; int parentRawBoneIndex; mdxaBone_t ragOverrideMatrix; // figure I will need this sooner or later mdxaBone_t extraMatrix; // figure I will need this sooner or later vec3_t extraVec1; // I am really tired of recomiling the whole game to add a param here float extraFloat1; int extraInt1; vec3_t ikPosition; float ikSpeed; //new ragdoll stuff -rww vec3_t epVelocity; //velocity factor, can be set, and is also maintained by physics based on gravity, mass, etc. float epGravFactor; //gravity factor maintained by bone physics int solidCount; //incremented every time we try to move and are in solid - if we get out of solid, it is reset to 0 bool physicsSettled; //true when the bone is on ground and finished bouncing, etc. but may still be pushed into solid by other bones bool snapped; //the bone is broken out of standard constraints int parentBoneIndex; float offsetRotation; //user api overrides float overGradSpeed; vec3_t overGoalSpot; bool hasOverGoal; mdxaBone_t animFrameMatrix; //matrix for the bone in the desired settling pose -rww int hasAnimFrameMatrix; int airTime; //base is in air, be more quick and sensitive about collisions //rww - RAGDOLL_END boneInfo_t(): boneNumber(-1), flags(0), startFrame(0), endFrame(0), startTime(0), pauseTime(0), animSpeed(0), blendFrame(0), blendLerpFrame(0), blendTime(0), blendStart(0), boneBlendTime(0), boneBlendStart(0) { matrix.matrix[0][0] = matrix.matrix[0][1] = matrix.matrix[0][2] = matrix.matrix[0][3] = matrix.matrix[1][0] = matrix.matrix[1][1] = matrix.matrix[1][2] = matrix.matrix[1][3] = matrix.matrix[2][0] = matrix.matrix[2][1] = matrix.matrix[2][2] = matrix.matrix[2][3] = 0.0f; } void sg_export( ojk::SavedGameHelper& saved_game) const { saved_game.write(boneNumber); saved_game.write<>(matrix); saved_game.write(flags); saved_game.write(startFrame); saved_game.write(endFrame); saved_game.write(startTime); saved_game.write(pauseTime); saved_game.write(animSpeed); saved_game.write(blendFrame); saved_game.write(blendLerpFrame); saved_game.write(blendTime); saved_game.write(blendStart); saved_game.write(boneBlendTime); saved_game.write(boneBlendStart); saved_game.write(newMatrix); #ifndef JK2_MODE saved_game.write(lastTimeUpdated); saved_game.write(lastContents); saved_game.write(lastPosition); saved_game.write(velocityEffector); saved_game.write(lastAngles); saved_game.write(minAngles); saved_game.write(maxAngles); saved_game.write(currentAngles); saved_game.write(anglesOffset); saved_game.write(positionOffset); saved_game.write(radius); saved_game.write(weight); saved_game.write(ragIndex); saved_game.write(velocityRoot); saved_game.write(ragStartTime); saved_game.write(firstTime); saved_game.write(firstCollisionTime); saved_game.write(restTime); saved_game.write(RagFlags); saved_game.write(DependentRagIndexMask); saved_game.write<>(originalTrueBoneMatrix); saved_game.write<>(parentTrueBoneMatrix); saved_game.write<>(parentOriginalTrueBoneMatrix); saved_game.write(originalOrigin); saved_game.write(originalAngles); saved_game.write(lastShotDir); saved_game.write(basepose); saved_game.write(baseposeInv); saved_game.write(baseposeParent); saved_game.write(baseposeInvParent); saved_game.write(parentRawBoneIndex); saved_game.write<>(ragOverrideMatrix); saved_game.write<>(extraMatrix); saved_game.write(extraVec1); saved_game.write(extraFloat1); saved_game.write(extraInt1); saved_game.write(ikPosition); saved_game.write(ikSpeed); saved_game.write(epVelocity); saved_game.write(epGravFactor); saved_game.write(solidCount); saved_game.write(physicsSettled); saved_game.write(snapped); saved_game.skip(2); saved_game.write(parentBoneIndex); saved_game.write(offsetRotation); saved_game.write(overGradSpeed); saved_game.write(overGoalSpot); saved_game.write(hasOverGoal); saved_game.skip(3); saved_game.write<>(animFrameMatrix); saved_game.write(hasAnimFrameMatrix); saved_game.write(airTime); #endif // !JK2_MODE } void sg_import( ojk::SavedGameHelper& saved_game) { saved_game.read(boneNumber); saved_game.read<>(matrix); saved_game.read(flags); saved_game.read(startFrame); saved_game.read(endFrame); saved_game.read(startTime); saved_game.read(pauseTime); saved_game.read(animSpeed); saved_game.read(blendFrame); saved_game.read(blendLerpFrame); saved_game.read(blendTime); saved_game.read(blendStart); saved_game.read(boneBlendTime); saved_game.read(boneBlendStart); saved_game.read(newMatrix); #ifndef JK2_MODE saved_game.read(lastTimeUpdated); saved_game.read(lastContents); saved_game.read(lastPosition); saved_game.read(velocityEffector); saved_game.read(lastAngles); saved_game.read(minAngles); saved_game.read(maxAngles); saved_game.read(currentAngles); saved_game.read(anglesOffset); saved_game.read(positionOffset); saved_game.read(radius); saved_game.read(weight); saved_game.read(ragIndex); saved_game.read(velocityRoot); saved_game.read(ragStartTime); saved_game.read(firstTime); saved_game.read(firstCollisionTime); saved_game.read(restTime); saved_game.read(RagFlags); saved_game.read(DependentRagIndexMask); saved_game.read<>(originalTrueBoneMatrix); saved_game.read<>(parentTrueBoneMatrix); saved_game.read<>(parentOriginalTrueBoneMatrix); saved_game.read(originalOrigin); saved_game.read(originalAngles); saved_game.read(lastShotDir); saved_game.read(basepose); saved_game.read(baseposeInv); saved_game.read(baseposeParent); saved_game.read(baseposeInvParent); saved_game.read(parentRawBoneIndex); saved_game.read<>(ragOverrideMatrix); saved_game.read<>(extraMatrix); saved_game.read(extraVec1); saved_game.read(extraFloat1); saved_game.read(extraInt1); saved_game.read(ikPosition); saved_game.read(ikSpeed); saved_game.read(epVelocity); saved_game.read(epGravFactor); saved_game.read(solidCount); saved_game.read(physicsSettled); saved_game.read(snapped); saved_game.skip(2); saved_game.read(parentBoneIndex); saved_game.read(offsetRotation); saved_game.read(overGradSpeed); saved_game.read(overGoalSpot); saved_game.read(hasOverGoal); saved_game.skip(3); saved_game.read<>(animFrameMatrix); saved_game.read(hasAnimFrameMatrix); saved_game.read(airTime); #endif // JK2_MODE } }; //we save from top to boltUsed here. Don't bother saving the position, it gets rebuilt every frame anyway struct boltInfo_t{ int boneNumber; // bone number bolt attaches to int surfaceNumber; // surface number bolt attaches to int surfaceType; // if we attach to a surface, this tells us if it is an original surface or a generated one - doesn't go across the network int boltUsed; // nor does this boltInfo_t(): boneNumber(-1), surfaceNumber(-1), surfaceType(0), boltUsed(0) {} void sg_export( ojk::SavedGameHelper& saved_game) const { saved_game.write(boneNumber); saved_game.write(surfaceNumber); saved_game.write(surfaceType); saved_game.write(boltUsed); } void sg_import( ojk::SavedGameHelper& saved_game) { saved_game.read(boneNumber); saved_game.read(surfaceNumber); saved_game.read(surfaceType); saved_game.read(boltUsed); } }; #define MAX_GHOUL_COUNT_BITS 8 // bits required to send across the MAX_G2_MODELS inside of the networking - this is the only restriction on ghoul models possible per entity typedef std::vector surfaceInfo_v; typedef std::vector boneInfo_v; typedef std::vector boltInfo_v; typedef std::vector mdxaBone_v; // defines for stuff to go into the mflags #define GHOUL2_NOCOLLIDE 0x001 #define GHOUL2_NORENDER 0x002 #define GHOUL2_NOMODEL 0x004 #define GHOUL2_NEWORIGIN 0x008 // NOTE order in here matters. We save out from mModelindex to mFlags, but not the STL vectors that are at the top or the bottom. class CBoneCache; struct model_s; //struct mdxaHeader_t; #ifdef VV_GHOUL_HACKS class CRenderableSurface { public: int ident; // ident of this surface - required so the materials renderer knows what sort of surface this refers to CBoneCache *boneCache; // pointer to transformed bone list for this surf mdxmSurface_t *surfaceData; // pointer to surface data loaded into file - only used by client renderer DO NOT USE IN GAME SIDE - if there is a vid restart this will be out of wack on the game CRenderableSurface(): ident(8), //SF_MDX boneCache(0), surfaceData(0) {} CRenderableSurface(const CRenderableSurface& rs): ident(rs.ident), boneCache(rs.boneCache), surfaceData(rs.surfaceData) {} }; #endif class CGhoul2Info { public: surfaceInfo_v mSlist; boltInfo_v mBltlist; boneInfo_v mBlist; // save from here (do not put any ptrs etc within this save block unless you adds special handlers to G2_SaveGhoul2Models / G2_LoadGhoul2Models!!!!!!!!!!!! #define BSAVE_START_FIELD mModelindex // this is the start point for loadsave, keep it up to date it you change anything int mModelindex; int animModelIndexOffset; qhandle_t mCustomShader; qhandle_t mCustomSkin; int mModelBoltLink; int mSurfaceRoot; int mLodBias; int mNewOrigin; // this contains the bolt index of the new origin for this model #ifdef _G2_GORE int mGoreSetTag; #endif qhandle_t mModel; // this and the next entries do NOT go across the network. They are for gameside access ONLY char mFileName[MAX_QPATH]; int mAnimFrameDefault; int mSkelFrameNum; int mMeshFrameNum; int mFlags; // used for determining whether to do full collision detection against this object // to here #define BSAVE_END_FIELD mTransformedVertsArray // this is the end point for loadsave, keep it up to date it you change anything intptr_t *mTransformedVertsArray; // used to create an array of pointers to transformed verts per surface for collision detection CBoneCache *mBoneCache; int mSkin; // these occasionally are not valid (like after a vid_restart) // call the questionably efficient G2_SetupModelPointers(this) to insure validity bool mValid; // all the below are proper and valid const model_s *currentModel; int currentModelSize; const model_s *animModel; int currentAnimModelSize; const mdxaHeader_t *aHeader; CGhoul2Info(): mModelindex(-1), animModelIndexOffset(0), mCustomShader(0), mCustomSkin(0), mModelBoltLink(0), mSurfaceRoot(0), mLodBias(0), mNewOrigin(-1), #ifdef _G2_GORE mGoreSetTag(0), #endif mModel(0), mAnimFrameDefault(0), mSkelFrameNum(-1), mMeshFrameNum(-1), mFlags(0), mTransformedVertsArray(0), mBoneCache(0), mSkin(0), mValid(false), currentModel(0), currentModelSize(0), animModel(0), currentAnimModelSize(0), aHeader(0) { mFileName[0] = 0; } void sg_export( ojk::SavedGameHelper& saved_game) const { saved_game.write(mModelindex); #ifndef JK2_MODE saved_game.write(animModelIndexOffset); #endif // !JK2_MODE saved_game.write(mCustomShader); saved_game.write(mCustomSkin); saved_game.write(mModelBoltLink); saved_game.write(mSurfaceRoot); saved_game.write(mLodBias); saved_game.write(mNewOrigin); #ifdef _G2_GORE saved_game.write(mGoreSetTag); #endif // _G2_GORE saved_game.write(mModel); saved_game.write(mFileName); saved_game.write(mAnimFrameDefault); saved_game.write(mSkelFrameNum); saved_game.write(mMeshFrameNum); saved_game.write(mFlags); } void sg_import( ojk::SavedGameHelper& saved_game) { saved_game.read(mModelindex); #ifndef JK2_MODE saved_game.read(animModelIndexOffset); #endif // !JK2_MODE saved_game.read(mCustomShader); saved_game.read(mCustomSkin); saved_game.read(mModelBoltLink); saved_game.read(mSurfaceRoot); saved_game.read(mLodBias); saved_game.read(mNewOrigin); #ifdef _G2_GORE saved_game.read(mGoreSetTag); #endif // _G2_GORE saved_game.read(mModel); saved_game.read(mFileName); saved_game.read(mAnimFrameDefault); saved_game.read(mSkelFrameNum); saved_game.read(mMeshFrameNum); saved_game.read(mFlags); } }; class CGhoul2Info_v; class IGhoul2InfoArray { public: virtual int New()=0; virtual void Delete(int handle)=0; virtual bool IsValid(int handle) const=0; virtual std::vector &Get(int handle)=0; virtual const std::vector &Get(int handle) const=0; }; #ifdef RENDERER IGhoul2InfoArray &TheGhoul2InfoArray(); #elif _JK2EXE IGhoul2InfoArray &_TheGhoul2InfoArray(); #else IGhoul2InfoArray &TheGameGhoul2InfoArray(); #endif class CGhoul2Info_v { int mItem; IGhoul2InfoArray &InfoArray() const { #ifdef RENDERER return TheGhoul2InfoArray(); #elif _JK2EXE return _TheGhoul2InfoArray(); #else return TheGameGhoul2InfoArray(); #endif } void Alloc() { assert(!mItem); //already alloced mItem=InfoArray().New(); assert(!Array().size()); } void Free() { if (mItem) { assert(InfoArray().IsValid(mItem)); InfoArray().Delete(mItem); mItem=0; } } std::vector &Array() { assert(InfoArray().IsValid(mItem)); return InfoArray().Get(mItem); } const std::vector &Array() const { assert(InfoArray().IsValid(mItem)); return InfoArray().Get(mItem); } public: CGhoul2Info_v() { mItem=0; } ~CGhoul2Info_v() { Free(); //this had better be taken care of via the clean ghoul2 models call } void operator=(const CGhoul2Info_v &other) { mItem=other.mItem; } void DeepCopy(const CGhoul2Info_v &other) { Free(); if (other.mItem) { Alloc(); Array()=other.Array(); int i; for (i=0;i=0&&idx=0&&idx=0); if (num) { if (!mItem) { Alloc(); } } if (mItem||num) { Array().resize(num); } } void clear() { Free(); } void push_back(const CGhoul2Info &model) { if (!mItem) { Alloc(); } Array().push_back(model); } int size() const { if (!IsValid()) { return 0; } return static_cast(Array().size()); } bool IsValid() const { return InfoArray().IsValid(mItem); } void kill() { // this scary method zeros the infovector handle without actually freeing it // it is used for some places where a copy is made, but we don't want to go through the trouble // of making a deep copy mItem=0; } void sg_export( ojk::SavedGameHelper& saved_game) const { saved_game.write(mItem); } void sg_import( ojk::SavedGameHelper& saved_game) { saved_game.read(mItem); } }; // collision detection stuff #define G2_FRONTFACE 1 #define G2_BACKFACE 0 class CCollisionRecord { public: float mDistance; int mEntityNum; int mModelIndex; int mPolyIndex; int mSurfaceIndex; vec3_t mCollisionPosition; vec3_t mCollisionNormal; int mFlags; int mMaterial; int mLocation; float mBarycentricI; // two barycentic coodinates for the hit point float mBarycentricJ; // K = 1-I-J CCollisionRecord(): mDistance(100000), mEntityNum(-1) {} void sg_export( ojk::SavedGameHelper& saved_game) const { saved_game.write(mDistance); saved_game.write(mEntityNum); saved_game.write(mModelIndex); saved_game.write(mPolyIndex); saved_game.write(mSurfaceIndex); saved_game.write(mCollisionPosition); saved_game.write(mCollisionNormal); saved_game.write(mFlags); saved_game.write(mMaterial); saved_game.write(mLocation); saved_game.write(mBarycentricI); saved_game.write(mBarycentricJ); } void sg_import( ojk::SavedGameHelper& saved_game) { saved_game.read(mDistance); saved_game.read(mEntityNum); saved_game.read(mModelIndex); saved_game.read(mPolyIndex); saved_game.read(mSurfaceIndex); saved_game.read(mCollisionPosition); saved_game.read(mCollisionNormal); saved_game.read(mFlags); saved_game.read(mMaterial); saved_game.read(mLocation); saved_game.read(mBarycentricI); saved_game.read(mBarycentricJ); } }; // calling defines for the trace function enum EG2_Collision { G2_NOCOLLIDE, G2_COLLIDE, G2_RETURNONHIT }; //==================================================================== #endif // GHOUL2_SHARED_H_INC