jkxr/Projects/Android/jni/OpenJK/code/rd-gles/G2_misc.cpp
Simon a3fdb460c4 Prevent crash when throwing Saber
seems it is indexing the g2 model surface that doesn't exist, might be something to do with the new hilt and the old hilt being in the save. Not sure, but this stops it crashing.
2023-04-04 23:03:31 +01:00

1984 lines
50 KiB
C++

/*
===========================================================================
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 <http://www.gnu.org/licenses/>.
===========================================================================
*/
#include "../server/exe_headers.h"
#ifndef __Q_SHARED_H
#include "../qcommon/q_shared.h"
#endif
#include "tr_common.h"
#if !defined(TR_LOCAL_H)
#include "tr_local.h"
#endif
#include "qcommon/matcomp.h"
#if !defined(G2_H_INC)
#include "../ghoul2/G2.h"
#endif
#if !defined (MINIHEAP_H_INC)
#include "../qcommon/MiniHeap.h"
#endif
#define G2_MODEL_OK(g) ((g)&&(g)->mValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel)
#include "../server/server.h"
#include <float.h>
#include "qcommon/ojk_saved_game_helper.h"
#ifdef _G2_GORE
#include "../ghoul2/ghoul2_gore.h"
#define GORE_TAG_UPPER (256)
#define GORE_TAG_MASK (~255)
static int CurrentTag=GORE_TAG_UPPER+1;
static int CurrentTagUpper=GORE_TAG_UPPER;
static std::map<int,GoreTextureCoordinates> GoreRecords;
static std::map<std::pair<int,int>,int> GoreTagsTemp; // this is a surface index to gore tag map used only
// temporarily during the generation phase so we reuse gore tags per LOD
int goreModelIndex;
static cvar_t *cg_g2MarksAllModels=NULL;
GoreTextureCoordinates *FindGoreRecord(int tag);
static inline void DestroyGoreTexCoordinates(int tag)
{
GoreTextureCoordinates *gTC = FindGoreRecord(tag);
if (!gTC)
{
return;
}
(*gTC).~GoreTextureCoordinates();
//I don't know what's going on here, it should call the destructor for
//this when it erases the record but sometimes it doesn't. -rww
}
//TODO: This needs to be set via a scalability cvar with some reasonable minimum value if pgore is used at all
#define MAX_GORE_RECORDS (500)
int AllocGoreRecord()
{
while (GoreRecords.size()>MAX_GORE_RECORDS)
{
int tagHigh=(*GoreRecords.begin()).first&GORE_TAG_MASK;
std::map<int,GoreTextureCoordinates>::iterator it;
GoreTextureCoordinates *gTC;
it = GoreRecords.begin();
gTC = &(*it).second;
if (gTC)
{
gTC->~GoreTextureCoordinates();
}
GoreRecords.erase(GoreRecords.begin());
while (GoreRecords.size())
{
if (((*GoreRecords.begin()).first&GORE_TAG_MASK)!=tagHigh)
{
break;
}
it = GoreRecords.begin();
gTC = &(*it).second;
if (gTC)
{
gTC->~GoreTextureCoordinates();
}
GoreRecords.erase(GoreRecords.begin());
}
}
int ret=CurrentTag;
GoreRecords[CurrentTag]=GoreTextureCoordinates();
CurrentTag++;
return ret;
}
void ResetGoreTag()
{
GoreTagsTemp.clear();
CurrentTag=CurrentTagUpper;
CurrentTagUpper+=GORE_TAG_UPPER;
}
GoreTextureCoordinates *FindGoreRecord(int tag)
{
std::map<int,GoreTextureCoordinates>::iterator i=GoreRecords.find(tag);
if (i!=GoreRecords.end())
{
return &(*i).second;
}
return 0;
}
void *G2_GetGoreRecord(int tag)
{
return FindGoreRecord(tag);
}
void DeleteGoreRecord(int tag)
{
DestroyGoreTexCoordinates(tag);
GoreRecords.erase(tag);
}
static int CurrentGoreSet=1; // this is a UUID for gore sets
static std::map<int,CGoreSet *> GoreSets; // map from uuid to goreset
CGoreSet *FindGoreSet(int goreSetTag)
{
std::map<int,CGoreSet *>::iterator f=GoreSets.find(goreSetTag);
if (f!=GoreSets.end())
{
return (*f).second;
}
return 0;
}
CGoreSet *NewGoreSet()
{
CGoreSet *ret=new CGoreSet(CurrentGoreSet++);
GoreSets[ret->mMyGoreSetTag]=ret;
ret->mRefCount = 1;
return ret;
}
void DeleteGoreSet(int goreSetTag)
{
std::map<int,CGoreSet *>::iterator f=GoreSets.find(goreSetTag);
if (f!=GoreSets.end())
{
if ( (*f).second->mRefCount == 0 || (*f).second->mRefCount - 1 == 0 )
{
delete (*f).second;
GoreSets.erase(f);
}
else
{
(*f).second->mRefCount--;
}
}
}
CGoreSet::~CGoreSet()
{
std::multimap<int,SGoreSurface>::iterator i;
for (i=mGoreRecords.begin();i!=mGoreRecords.end();++i)
{
DeleteGoreRecord((*i).second.mGoreTag);
}
};
#endif
extern mdxaBone_t worldMatrix;
extern mdxaBone_t worldMatrixInv;
const mdxaBone_t &EvalBoneCache(int index,CBoneCache *boneCache);
class CTraceSurface
{
public:
int surfaceNum;
surfaceInfo_v &rootSList;
const model_t *currentModel;
const int lod;
vec3_t rayStart;
vec3_t rayEnd;
CCollisionRecord *collRecMap;
const int entNum;
const int modelIndex;
const skin_t *skin;
const jk_shader_t *cust_shader;
intptr_t *TransformedVertsArray;
const EG2_Collision eG2TraceType;
bool hitOne;
float m_fRadius;
#ifdef _G2_GORE
//gore application thing
float ssize;
float tsize;
float theta;
int goreShader;
CGhoul2Info *ghoul2info;
// Procedural-gore application things
SSkinGoreData *gore;
#endif
CTraceSurface(
int initsurfaceNum,
surfaceInfo_v &initrootSList,
const model_t *initcurrentModel,
int initlod,
vec3_t initrayStart,
vec3_t initrayEnd,
CCollisionRecord *initcollRecMap,
int initentNum,
int initmodelIndex,
const skin_t *initskin,
const jk_shader_t *initcust_shader,
intptr_t *initTransformedVertsArray,
const EG2_Collision einitG2TraceType,
#ifdef _G2_GORE
float fRadius,
float initssize,
float inittsize,
float inittheta,
int initgoreShader,
CGhoul2Info *initghoul2info,
SSkinGoreData *initgore
#else
float fRadius
#endif
):
surfaceNum(initsurfaceNum),
rootSList(initrootSList),
currentModel(initcurrentModel),
lod(initlod),
collRecMap(initcollRecMap),
entNum(initentNum),
modelIndex(initmodelIndex),
skin(initskin),
cust_shader(initcust_shader),
TransformedVertsArray(initTransformedVertsArray),
eG2TraceType(einitG2TraceType),
hitOne(false),
#ifdef _G2_GORE
m_fRadius(fRadius),
ssize(initssize),
tsize(inittsize),
theta(inittheta),
goreShader(initgoreShader),
ghoul2info(initghoul2info),
gore(initgore)
#else
m_fRadius(fRadius)
#endif
{
VectorCopy(initrayStart, rayStart);
VectorCopy(initrayEnd, rayEnd);
}
};
// assorted Ghoul 2 functions.
// list all surfaces associated with a model
void G2_List_Model_Surfaces(const char *fileName)
{
int i, x;
model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
mdxmSurfHierarchy_t *surf;
surf = (mdxmSurfHierarchy_t *) ( (byte *)mod_m->mdxm + mod_m->mdxm->ofsSurfHierarchy );
mdxmSurface_t *surface = (mdxmSurface_t *)((byte *)mod_m->mdxm + mod_m->mdxm->ofsLODs + sizeof(mdxmLOD_t));
for ( x = 0 ; x < mod_m->mdxm->numSurfaces ; x++)
{
Com_Printf("Surface %i Name %s\n", x, surf->name);
if (r_verbose->value)
{
Com_Printf("Num Descendants %i\n", surf->numChildren);
for (i=0; i<surf->numChildren; i++)
{
Com_Printf("Descendant %i\n", surf->childIndexes[i]);
}
}
// find the next surface
surf = (mdxmSurfHierarchy_t *)( (byte *)surf + (intptr_t)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surf->numChildren ] ));
surface =(mdxmSurface_t *)( (byte *)surface + surface->ofsEnd );
}
}
// list all bones associated with a model
void G2_List_Model_Bones(const char *fileName, int frame)
{
int x, i;
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex);
// mdxaFrame_t *aframe=0;
// int frameSize;
mdxaHeader_t *header = mod_a->mdxa;
// figure out where the offset list is
offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t));
// frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] );
// aframe = (mdxaFrame_t *)((byte *)header + header->ofsFrames + (frame * frameSize));
// walk each bone and list it's name
for (x=0; x< mod_a->mdxa->numBones; x++)
{
skel = (mdxaSkel_t *)((byte *)header + sizeof(mdxaHeader_t) + offsets->offsets[x]);
Com_Printf("Bone %i Name %s\n", x, skel->name);
Com_Printf("X pos %f, Y pos %f, Z pos %f\n", skel->BasePoseMat.matrix[0][3], skel->BasePoseMat.matrix[1][3], skel->BasePoseMat.matrix[2][3]);
// if we are in verbose mode give us more details
if (r_verbose->value)
{
Com_Printf("Num Descendants %i\n", skel->numChildren);
for (i=0; i<skel->numChildren; i++)
{
Com_Printf("Num Descendants %i\n", skel->numChildren);
}
}
}
}
/************************************************************************************************
* G2_GetAnimFileName
* obtain the .gla filename for a model
*
* Input
* filename of model
*
* Output
* true if we successfully obtained a filename, false otherwise
*
************************************************************************************************/
qboolean G2_GetAnimFileName(const char *fileName, char **filename)
{
// find the model we want
model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName));
if (mod && mod->mdxm && (mod->mdxm->animName[0] != 0))
{
*filename = mod->mdxm->animName;
return qtrue;
}
return qfalse;
}
/////////////////////////////////////////////////////////////////////
//
// Code for collision detection for models gameside
//
/////////////////////////////////////////////////////////////////////
int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod)
{
int returnLod = useLod;
// if we are overriding the LOD at top level, then we can afford to only check this level of model
if (ghoul2.mLodBias > returnLod)
{
returnLod = ghoul2.mLodBias;
}
assert(G2_MODEL_OK(&ghoul2));
assert(ghoul2.currentModel);
assert(ghoul2.currentModel->mdxm);
//what about r_lodBias?
// now ensure that we haven't selected a lod that doesn't exist for this model
if ( returnLod >= ghoul2.currentModel->mdxm->numLODs )
{
returnLod = ghoul2.currentModel->mdxm->numLODs - 1;
}
return returnLod;
}
void R_TransformEachSurface( const mdxmSurface_t *surface, vec3_t scale, CMiniHeap *G2VertSpace, intptr_t *TransformedVertsArray,CBoneCache *boneCache)
{
int j, k;
mdxmVertex_t *v;
float *TransformedVerts;
//
// deform the vertexes by the lerped bones
//
int *piBoneReferences = (int*) ((byte*)surface + surface->ofsBoneReferences);
// alloc some space for the transformed verts to get put in
TransformedVerts = (float *)G2VertSpace->MiniHeapAlloc(surface->numVerts * 5 * 4);
TransformedVertsArray[surface->thisSurfaceIndex] = (intptr_t)TransformedVerts;
if (!TransformedVerts)
{
assert(TransformedVerts);
Com_Error(ERR_DROP, "Ran out of transform space for Ghoul2 Models. Adjust G2_MINIHEAP_SIZE in sv_init.cpp.\n");
}
// whip through and actually transform each vertex
const int numVerts = surface->numVerts;
v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts);
mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts];
// optimisation issue
if ((scale[0] != 1.0) || (scale[1] != 1.0) || (scale[2] != 1.0))
{
for ( j = 0; j < numVerts; j++ )
{
vec3_t tempVert, tempNormal;
// mdxmWeight_t *w;
VectorClear( tempVert );
VectorClear( tempNormal );
// w = v->weights;
const int iNumWeights = G2_GetVertWeights( v );
float fTotalWeight = 0.0f;
for ( k = 0 ; k < iNumWeights ; k++ )
{
int iBoneIndex = G2_GetVertBoneIndex( v, k );
float fBoneWeight = G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights );
const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex],boneCache);
tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] );
tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] );
tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] );
tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal );
tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal );
tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal );
}
int pos = j * 5;
// copy tranformed verts into temp space
TransformedVerts[pos++] = tempVert[0] * scale[0];
TransformedVerts[pos++] = tempVert[1] * scale[1];
TransformedVerts[pos++] = tempVert[2] * scale[2];
// we will need the S & T coors too for hitlocation and hitmaterial stuff
TransformedVerts[pos++] = pTexCoords[j].texCoords[0];
TransformedVerts[pos] = pTexCoords[j].texCoords[1];
v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights];
}
}
else
{
int pos = 0;
for ( j = 0; j < numVerts; j++ )
{
vec3_t tempVert, tempNormal;
// const mdxmWeight_t *w;
VectorClear( tempVert );
VectorClear( tempNormal );
// w = v->weights;
const int iNumWeights = G2_GetVertWeights( v );
float fTotalWeight = 0.0f;
for ( k = 0 ; k < iNumWeights ; k++ )
{
int iBoneIndex = G2_GetVertBoneIndex( v, k );
float fBoneWeight = G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights );
const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex],boneCache);
tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] );
tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] );
tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] );
tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal );
tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal );
tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal );
}
// copy tranformed verts into temp space
TransformedVerts[pos++] = tempVert[0];
TransformedVerts[pos++] = tempVert[1];
TransformedVerts[pos++] = tempVert[2];
// we will need the S & T coors too for hitlocation and hitmaterial stuff
TransformedVerts[pos++] = pTexCoords[j].texCoords[0];
TransformedVerts[pos++] = pTexCoords[j].texCoords[1];
v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights];
}
}
}
void G2_TransformSurfaces(int surfaceNum, surfaceInfo_v &rootSList,
CBoneCache *boneCache, const model_t *currentModel, int lod, vec3_t scale, CMiniHeap *G2VertSpace, intptr_t *TransformedVertArray, bool secondTimeAround)
{
int i;
assert(currentModel);
assert(currentModel->mdxm);
// back track and get the surfinfo struct for this surface
const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(currentModel, surfaceNum, lod);
const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)currentModel->mdxm + sizeof(mdxmHeader_t));
const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]);
// see if we have an override surface in the surface list
const surfaceInfo_t *surfOverride = G2_FindOverrideSurface(surfaceNum, rootSList);
// really, we should use the default flags for this surface unless it's been overriden
int offFlags = surfInfo->flags;
if (surfOverride)
{
offFlags = surfOverride->offFlags;
}
// if this surface is not off, add it to the shader render list
if (!offFlags)
{
R_TransformEachSurface(surface, scale, G2VertSpace, TransformedVertArray, boneCache);
}
// if we are turning off all descendants, then stop this recursion now
if (offFlags & G2SURFACEFLAG_NODESCENDANTS)
{
return;
}
// now recursively call for the children
for (i=0; i< surfInfo->numChildren; i++)
{
G2_TransformSurfaces(surfInfo->childIndexes[i], rootSList, boneCache, currentModel, lod, scale, G2VertSpace, TransformedVertArray, secondTimeAround);
}
}
// main calling point for the model transform for collision detection. At this point all of the skeleton has been transformed.
#ifdef _G2_GORE
void G2_TransformModel(CGhoul2Info_v &ghoul2, const int frameNum, vec3_t scale, CMiniHeap *G2VertSpace, int useLod, bool ApplyGore, SSkinGoreData *gore)
#else
void G2_TransformModel(CGhoul2Info_v &ghoul2, const int frameNum, vec3_t scale, CMiniHeap *G2VertSpace, int useLod)
#endif
{
int i, lod;
vec3_t correctScale;
#if !defined(JK2_MODE) || defined(_G2_GORE)
qboolean firstModelOnly = qfalse;
#endif // !JK2_MODE || _G2_GORE
#ifndef JK2_MODE
if ( cg_g2MarksAllModels == NULL )
{
cg_g2MarksAllModels = ri.Cvar_Get( "cg_g2MarksAllModels", "0", 0 );
}
if (cg_g2MarksAllModels == NULL
|| !cg_g2MarksAllModels->integer )
{
firstModelOnly = qtrue;
}
#endif // !JK2_MODE
#ifdef _G2_GORE
if ( gore
&& gore->firstModel > 0 )
{
firstModelOnly = qfalse;
}
#endif
VectorCopy(scale, correctScale);
// check for scales of 0 - that's the default I believe
if (!scale[0])
{
correctScale[0] = 1.0;
}
if (!scale[1])
{
correctScale[1] = 1.0;
}
if (!scale[2])
{
correctScale[2] = 1.0;
}
// walk each possible model for this entity and try rendering it out
for (i=0; i<ghoul2.size(); i++)
{
CGhoul2Info &g=ghoul2[i];
// don't bother with models that we don't care about.
if (!g.mValid)
{
continue;
}
assert(g.mBoneCache);
assert(G2_MODEL_OK(&g));
// stop us building this model more than once per frame
g.mMeshFrameNum = frameNum;
// decide the LOD
#ifdef _G2_GORE
if (ApplyGore)
{
lod=useLod;
assert(g.currentModel);
if (lod>=g.currentModel->numLods)
{
g.mTransformedVertsArray = 0;
if ( firstModelOnly )
{
// we don't really need to do multiple models for gore.
return;
}
//do the rest
continue;
}
}
else
#endif
{
lod = G2_DecideTraceLod(g, useLod);
}
// give us space for the transformed vertex array to be put in
g.mTransformedVertsArray = (intptr_t *)G2VertSpace->MiniHeapAlloc(g.currentModel->mdxm->numSurfaces * sizeof (intptr_t));
if (!g.mTransformedVertsArray)
{
Com_Error(ERR_DROP, "Ran out of transform space for Ghoul2 Models. Adjust G2_MINIHEAP_SIZE in sv_init.cpp.\n");
}
memset(g.mTransformedVertsArray, 0,(g.currentModel->mdxm->numSurfaces * sizeof (intptr_t)));
G2_FindOverrideSurface(-1,g.mSlist); //reset the quick surface override lookup;
// recursively call the model surface transform
G2_TransformSurfaces(g.mSurfaceRoot, g.mSlist, g.mBoneCache, g.currentModel, lod, correctScale, G2VertSpace, g.mTransformedVertsArray, false);
#ifdef _G2_GORE
if (ApplyGore && firstModelOnly )
{
// we don't really need to do multiple models for gore.
break;
}
#endif
}
}
// work out how much space a triangle takes
static float G2_AreaOfTri(const vec3_t A, const vec3_t B, const vec3_t C)
{
vec3_t cross, ab, cb;
VectorSubtract(A, B, ab);
VectorSubtract(C, B, cb);
CrossProduct(ab, cb, cross);
return VectorLength(cross);
}
// actually determine the S and T of the coordinate we hit in a given poly
static void G2_BuildHitPointST( const vec3_t A, const float SA, const float TA,
const vec3_t B, const float SB, const float TB,
const vec3_t C, const float SC, const float TC,
const vec3_t P, float *s, float *t,float &bary_i,float &bary_j)
{
float areaABC = G2_AreaOfTri(A, B, C);
float i = G2_AreaOfTri(P, B, C) / areaABC;
bary_i=i;
float j = G2_AreaOfTri(A, P, C) / areaABC;
bary_j=j;
float k = G2_AreaOfTri(A, B, P) / areaABC;
*s = SA * i + SB * j + SC * k;
*t = TA * i + TB * j + TC * k;
*s=fmod(*s, 1);
if (*s< 0)
{
*s+= 1.0;
}
*t=fmod(*t, 1);
if (*t< 0)
{
*t+= 1.0;
}
}
// routine that works out given a ray whether or not it hits a poly
static inline qboolean G2_SegmentTriangleTest( const vec3_t start, const vec3_t end,
const vec3_t A, const vec3_t B, const vec3_t C,
qboolean backFaces,qboolean frontFaces,vec3_t returnedPoint,vec3_t returnedNormal, float *denom)
{
static const float tiny=1E-10f;
vec3_t returnedNormalT;
vec3_t edgeAC;
VectorSubtract(C, A, edgeAC);
VectorSubtract(B, A, returnedNormalT);
CrossProduct(returnedNormalT, edgeAC, returnedNormal);
vec3_t ray;
VectorSubtract(end, start, ray);
*denom=DotProduct(ray, returnedNormal);
if (Q_fabs(*denom)<tiny|| // triangle parallel to ray
(!backFaces && *denom>0)|| // not accepting back faces
(!frontFaces && *denom<0)) //not accepting front faces
{
return qfalse;
}
vec3_t toPlane;
VectorSubtract(A, start, toPlane);
float t=DotProduct(toPlane, returnedNormal)/ *denom;
if (t<0.0f||t>1.0f)
{
return qfalse; // off segment
}
VectorScale(ray, t, ray);
VectorAdd(ray, start, returnedPoint);
vec3_t edgePA;
VectorSubtract(A, returnedPoint, edgePA);
vec3_t edgePB;
VectorSubtract(B, returnedPoint, edgePB);
vec3_t edgePC;
VectorSubtract(C, returnedPoint, edgePC);
vec3_t temp;
CrossProduct(edgePA, edgePB, temp);
if (DotProduct(temp, returnedNormal)<0.0f)
{
return qfalse; // off triangle
}
CrossProduct(edgePC, edgePA, temp);
if (DotProduct(temp,returnedNormal)<0.0f)
{
return qfalse; // off triangle
}
CrossProduct(edgePB, edgePC, temp);
if (DotProduct(temp, returnedNormal)<0.0f)
{
return qfalse; // off triangle
}
return qtrue;
}
#ifdef _G2_GORE
struct SVertexTemp
{
int flags;
int touch;
int newindex;
float tex[2];
SVertexTemp()
{
touch=0;
}
};
#define MAX_GORE_VERTS (3000)
static SVertexTemp GoreVerts[MAX_GORE_VERTS];
static int GoreIndexCopy[MAX_GORE_VERTS];
static int GoreTouch=1;
#define MAX_GORE_INDECIES (6000)
static int GoreIndecies[MAX_GORE_INDECIES];
#define GORE_MARGIN (0.0f)
int G2API_GetTime(int argTime);
// now we at poly level, check each model space transformed poly against the model world transfomed ray
static void G2_GorePolys( const mdxmSurface_t *surface, CTraceSurface &TS, const mdxmSurfHierarchy_t *surfInfo)
{
int j;
vec3_t basis1;
vec3_t basis2;
vec3_t taxis;
vec3_t saxis;
if (!TS.gore)
{
return;
}
if (!TS.gore->useTheta)
{
VectorCopy(TS.gore->uaxis,basis2);
CrossProduct(TS.rayEnd,basis2,basis1);
if (DotProduct(basis1,basis1)<0.005f)
{ //shot dir and slash dir are too close
return;
}
}
if (TS.gore->useTheta)
{
basis2[0]=0.0f;
basis2[1]=0.0f;
basis2[2]=1.0f;
CrossProduct(TS.rayEnd,basis2,basis1);
if (DotProduct(basis1,basis1)<.1f)
{
basis2[0]=0.0f;
basis2[1]=1.0f;
basis2[2]=0.0f;
CrossProduct(TS.rayEnd,basis2,basis1);
}
CrossProduct(TS.rayEnd,basis1,basis2);
}
// Give me a shot direction not a bunch of zeros :) -Gil
assert(DotProduct(basis1,basis1)>.0001f);
assert(DotProduct(basis2,basis2)>.0001f);
VectorNormalize(basis1);
VectorNormalize(basis2);
float c=cos(TS.theta);
float s=sin(TS.theta);
VectorScale(basis1,.5f*c/TS.tsize,taxis);
VectorMA(taxis,.5f*s/TS.tsize,basis2,taxis);
VectorScale(basis1,-.5f*s/TS.ssize,saxis);
VectorMA(saxis,.5f*c/TS.ssize,basis2,saxis);
//fixme, everything above here should be pre-calculated in G2API_AddSkinGore
float *verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
int numVerts = surface->numVerts;
int flags=63;
assert(numVerts<MAX_GORE_VERTS);
for ( j = 0; j < numVerts; j++ )
{
int pos=j*5;
vec3_t delta;
delta[0]=verts[pos+0]-TS.rayStart[0];
delta[1]=verts[pos+1]-TS.rayStart[1];
delta[2]=verts[pos+2]-TS.rayStart[2];
float s=DotProduct(delta,saxis)+0.5f;
float t=DotProduct(delta,taxis)+0.5f;
float depth = DotProduct(delta,TS.rayEnd);
int vflags=0;
if (s>GORE_MARGIN)
{
vflags|=1;
}
if (s<1.0f-GORE_MARGIN)
{
vflags|=2;
}
if (t>GORE_MARGIN)
{
vflags|=4;
}
if (t<1.0f-GORE_MARGIN)
{
vflags|=8;
}
if (depth > TS.gore->depthStart)
{
vflags|=16;
}
if (depth < TS.gore->depthEnd)
{
vflags|=32;
}
vflags=(~vflags);
flags&=vflags;
GoreVerts[j].flags=vflags;
GoreVerts[j].tex[0]=s;
GoreVerts[j].tex[1]=t;
}
if (flags)
{
return; // completely off the gore splotch.
}
int numTris,newNumTris,newNumVerts;
numTris = surface->numTriangles;
mdxmTriangle_t *tris;
tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles);
verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
newNumTris=0;
newNumVerts=0;
GoreTouch++;
for ( j = 0; j < numTris; j++ )
{
assert(tris[j].indexes[0]>=0&&tris[j].indexes[0]<numVerts);
assert(tris[j].indexes[1]>=0&&tris[j].indexes[1]<numVerts);
assert(tris[j].indexes[2]>=0&&tris[j].indexes[2]<numVerts);
flags=63&
GoreVerts[tris[j].indexes[0]].flags&
GoreVerts[tris[j].indexes[1]].flags&
GoreVerts[tris[j].indexes[2]].flags;
if (flags)
{
continue;
}
if (!TS.gore->frontFaces || !TS.gore->backFaces)
{
// we need to back/front face cull
vec3_t e1,e2,n;
VectorSubtract(&verts[tris[j].indexes[1]*5],&verts[tris[j].indexes[0]*5],e1);
VectorSubtract(&verts[tris[j].indexes[2]*5],&verts[tris[j].indexes[0]*5],e2);
CrossProduct(e1,e2,n);
if (DotProduct(TS.rayEnd,n)>0.0f)
{
if (!TS.gore->frontFaces)
{
continue;
}
}
else
{
if (!TS.gore->backFaces)
{
continue;
}
}
}
int k;
assert(newNumTris*3+3<MAX_GORE_INDECIES);
for (k=0;k<3;k++)
{
if (GoreVerts[tris[j].indexes[k]].touch==GoreTouch)
{
GoreIndecies[newNumTris*3+k]=GoreVerts[tris[j].indexes[k]].newindex;
}
else
{
GoreVerts[tris[j].indexes[k]].touch=GoreTouch;
GoreVerts[tris[j].indexes[k]].newindex=newNumVerts;
GoreIndecies[newNumTris*3+k]=newNumVerts;
GoreIndexCopy[newNumVerts]=tris[j].indexes[k];
newNumVerts++;
}
}
newNumTris++;
}
if (!newNumVerts)
{
return;
}
int newTag;
std::map<std::pair<int,int>,int>::iterator f=GoreTagsTemp.find(std::make_pair(goreModelIndex,TS.surfaceNum));
if (f==GoreTagsTemp.end()) // need to generate a record
{
newTag=AllocGoreRecord();
CGoreSet *goreSet=0;
if (TS.ghoul2info->mGoreSetTag)
{
goreSet=FindGoreSet(TS.ghoul2info->mGoreSetTag);
}
if (!goreSet)
{
goreSet=NewGoreSet();
TS.ghoul2info->mGoreSetTag=goreSet->mMyGoreSetTag;
}
assert(goreSet);
SGoreSurface add;
add.shader=TS.goreShader;
add.mDeleteTime=0;
if (TS.gore->lifeTime)
{
add.mDeleteTime=G2API_GetTime(0) + TS.gore->lifeTime;
}
add.mFadeTime = TS.gore->fadeOutTime;
add.mFadeRGB = TS.gore->fadeRGB;
add.mGoreTag = newTag;
add.mGoreGrowStartTime=G2API_GetTime(0);
if( TS.gore->growDuration == -1)
{
add.mGoreGrowEndTime = -1; // set this to -1 to disable growing
}
else
{
add.mGoreGrowEndTime = G2API_GetTime(0) + TS.gore->growDuration;
}
assert(TS.gore->growDuration != 0);
add.mGoreGrowFactor = ( 1.0f - TS.gore->goreScaleStartFraction) / (float)(TS.gore->growDuration); //curscale = (curtime-mGoreGrowStartTime)*mGoreGrowFactor;
add.mGoreGrowOffset = TS.gore->goreScaleStartFraction;
goreSet->mGoreRecords.insert(std::make_pair(TS.surfaceNum,add));
GoreTagsTemp[std::make_pair(goreModelIndex,TS.surfaceNum)]=newTag;
}
else
{
newTag=(*f).second;
}
GoreTextureCoordinates *gore=FindGoreRecord(newTag);
if (gore)
{
assert(sizeof(float)==sizeof(int));
// data block format:
unsigned int size=
sizeof(int)+ // num verts
sizeof(int)+ // num tris
sizeof(int)*newNumVerts+ // which verts to copy from original surface
sizeof(float)*4*newNumVerts+ // storgage for deformed verts
sizeof(float)*4*newNumVerts+ // storgage for deformed normal
sizeof(float)*2*newNumVerts+ // texture coordinates
sizeof(int)*newNumTris*3; // new indecies
int *data=(int *)R_Malloc ( sizeof(int)*size, TAG_GHOUL2, qtrue );
if ( gore->tex[TS.lod] )
R_Free(gore->tex[TS.lod]);
gore->tex[TS.lod]=(float *)data;
*data++=newNumVerts;
*data++=newNumTris;
memcpy(data,GoreIndexCopy,sizeof(int)*newNumVerts);
data+=newNumVerts*9; // skip verts and normals
float *fdata=(float *)data;
for (j=0;j<newNumVerts;j++)
{
*fdata++=GoreVerts[GoreIndexCopy[j]].tex[0];
*fdata++=GoreVerts[GoreIndexCopy[j]].tex[1];
}
data=(int *)fdata;
memcpy(data,GoreIndecies,sizeof(int)*newNumTris*3);
data+=newNumTris*3;
assert((data-(int *)gore->tex[TS.lod])*sizeof(int)==size);
fdata = (float *)data;
// build the entity to gore matrix
VectorCopy(saxis,fdata+0);
VectorCopy(taxis,fdata+4);
VectorCopy(TS.rayEnd,fdata+8);
VectorNormalize(fdata+0);
VectorNormalize(fdata+4);
VectorNormalize(fdata+8);
fdata[3]=-0.5f; // subtract texture center
fdata[7]=-0.5f;
fdata[11]=0.0f;
vec3_t shotOriginInCurrentSpace; // unknown space
TransformPoint(TS.rayStart,shotOriginInCurrentSpace,(mdxaBone_t *)fdata); // dest middle arg
// this will insure the shot origin in our unknown space is now the shot origin, making it a known space
fdata[3]-=shotOriginInCurrentSpace[0];
fdata[7]-=shotOriginInCurrentSpace[1];
fdata[11]-=shotOriginInCurrentSpace[2];
Inverse_Matrix((mdxaBone_t *)fdata,(mdxaBone_t *)(fdata+12)); // dest 2nd arg
data+=24;
// assert((data - (int *)gore->tex[TS.lod]) * sizeof(int) == size);
}
}
#else
struct SVertexTemp
{
int flags;
// int touch;
// int newindex;
// float tex[2];
SVertexTemp()
{
// touch=0;
}
};
#define MAX_GORE_VERTS (3000)
static SVertexTemp GoreVerts[MAX_GORE_VERTS];
#endif
// now we're at poly level, check each model space transformed poly against the model world transfomed ray
static bool G2_TracePolys(const mdxmSurface_t *surface, const mdxmSurfHierarchy_t *surfInfo, CTraceSurface &TS)
{
int j, numTris;
// whip through and actually transform each vertex
const mdxmTriangle_t *tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles);
const float *verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
numTris = surface->numTriangles;
for ( j = 0; j < numTris; j++ )
{
float face;
vec3_t hitPoint, normal;
// determine actual coords for this triangle
const float *point1 = &verts[(tris[j].indexes[0] * 5)];
const float *point2 = &verts[(tris[j].indexes[1] * 5)];
const float *point3 = &verts[(tris[j].indexes[2] * 5)];
// did we hit it?
if (G2_SegmentTriangleTest(TS.rayStart, TS.rayEnd, point1, point2, point3, qtrue, qtrue, hitPoint, normal, &face))
{ // find space in the collision records for this record
int i=0;
for (; i<MAX_G2_COLLISIONS;i++)
{
if (TS.collRecMap[i].mEntityNum == -1)
{
CCollisionRecord &newCol = TS.collRecMap[i];
vec3_t distVect;
float x_pos = 0, y_pos = 0;
newCol.mPolyIndex = j;
newCol.mEntityNum = TS.entNum;
newCol.mSurfaceIndex = surface->thisSurfaceIndex;
newCol.mModelIndex = TS.modelIndex;
if (face>0)
{
newCol.mFlags = G2_FRONTFACE;
}
else
{
newCol.mFlags = G2_BACKFACE;
}
VectorSubtract(hitPoint, TS.rayStart, distVect);
newCol.mDistance = VectorLength(distVect);
assert( !Q_isnan(newCol.mDistance) );
// put the hit point back into world space
TransformAndTranslatePoint(hitPoint, newCol.mCollisionPosition, &worldMatrix);
// transform normal (but don't translate) into world angles
TransformPoint(normal, newCol.mCollisionNormal, &worldMatrix);
VectorNormalize(newCol.mCollisionNormal);
newCol.mMaterial = newCol.mLocation = 0;
// Determine our location within the texture, and barycentric coordinates
G2_BuildHitPointST(point1, point1[3], point1[4],
point2, point2[3], point2[4],
point3, point3[3], point3[4],
hitPoint, &x_pos, &y_pos,newCol.mBarycentricI,newCol.mBarycentricJ);
/*
const jk_shader_t *shader = 0;
// now, we know what surface this hit belongs to, we need to go get the shader handle so we can get the correct hit location and hit material info
if ( cust_shader )
{
shader = cust_shader;
}
else if ( skin )
{
int j;
// match the surface name to something in the skin file
shader = tr.defaultShader;
for ( j = 0 ; j < skin->numSurfaces ; j++ )
{
// the names have both been lowercased
if ( !strcmp( skin->surfaces[j]->name, surfInfo->name ) )
{
shader = skin->surfaces[j]->shader;
break;
}
}
}
else
{
shader = R_GetShaderByHandle( surfInfo->shaderIndex );
}
// do we even care to decide what the hit or location area's are? If we don't have them in the shader there is little point
if ((shader->hitLocation) || (shader->hitMaterial))
{
// ok, we have a floating point position. - determine location in data we need to look at
if (shader->hitLocation)
{
newCol.mLocation = *(hitMatReg[shader->hitLocation].loc +
((int)(y_pos * hitMatReg[shader->hitLocation].height) * hitMatReg[shader->hitLocation].width) +
((int)(x_pos * hitMatReg[shader->hitLocation].width)));
Com_Printf("G2_TracePolys hit location: %d\n", newCol.mLocation);
}
if (shader->hitMaterial)
{
newCol.mMaterial = *(hitMatReg[shader->hitMaterial].loc +
((int)(y_pos * hitMatReg[shader->hitMaterial].height) * hitMatReg[shader->hitMaterial].width) +
((int)(x_pos * hitMatReg[shader->hitMaterial].width)));
}
}
*/
// exit now if we should
if (TS.eG2TraceType == G2_RETURNONHIT)
{
TS.hitOne = true;
return true;
}
break;
}
}
if (i == MAX_G2_COLLISIONS)
{
assert(i!=MAX_G2_COLLISIONS); // run out of collision record space - will probalbly never happen
TS.hitOne = true; //force stop recursion
return true; // return true to avoid wasting further time, but no hit will result without a record
}
}
}
return false;
}
// now we're at poly level, check each model space transformed poly against the model world transfomed ray
static bool G2_RadiusTracePolys(
const mdxmSurface_t *surface,
CTraceSurface &TS
)
{
int j;
vec3_t basis1;
vec3_t basis2;
vec3_t taxis;
vec3_t saxis;
basis2[0]=0.0f;
basis2[1]=0.0f;
basis2[2]=1.0f;
vec3_t v3RayDir;
VectorSubtract(TS.rayEnd, TS.rayStart, v3RayDir);
CrossProduct(v3RayDir,basis2,basis1);
if (DotProduct(basis1,basis1)<.1f)
{
basis2[0]=0.0f;
basis2[1]=1.0f;
basis2[2]=0.0f;
CrossProduct(v3RayDir,basis2,basis1);
}
CrossProduct(v3RayDir,basis1,basis2);
// Give me a shot direction not a bunch of zeros :) -Gil
// assert(DotProduct(basis1,basis1)>.0001f);
// assert(DotProduct(basis2,basis2)>.0001f);
VectorNormalize(basis1);
VectorNormalize(basis2);
const float c=cos(0.0f);//theta
const float s=sin(0.0f);//theta
VectorScale(basis1, 0.5f * c / TS.m_fRadius,taxis);
VectorMA(taxis, 0.5f * s / TS.m_fRadius,basis2,taxis);
VectorScale(basis1,-0.5f * s /TS.m_fRadius,saxis);
VectorMA( saxis, 0.5f * c /TS.m_fRadius,basis2,saxis);
const float * const verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
const int numVerts = surface->numVerts;
int flags=63;
//rayDir/=lengthSquared(raydir);
const float f = VectorLengthSquared(v3RayDir);
v3RayDir[0]/=f;
v3RayDir[1]/=f;
v3RayDir[2]/=f;
for ( j = 0; j < numVerts; j++ )
{
const int pos=j*5;
vec3_t delta;
delta[0]=verts[pos+0]-TS.rayStart[0];
delta[1]=verts[pos+1]-TS.rayStart[1];
delta[2]=verts[pos+2]-TS.rayStart[2];
const float s=DotProduct(delta,saxis)+0.5f;
const float t=DotProduct(delta,taxis)+0.5f;
const float u=DotProduct(delta,v3RayDir);
int vflags=0;
if (s>0)
{
vflags|=1;
}
if (s<1)
{
vflags|=2;
}
if (t>0)
{
vflags|=4;
}
if (t<1)
{
vflags|=8;
}
if (u>0)
{
vflags|=16;
}
if (u<1)
{
vflags|=32;
}
vflags=(~vflags);
flags&=vflags;
GoreVerts[j].flags=vflags;
}
if (flags)
{
return false; // completely off the gore splotch (so presumably hit nothing? -Ste)
}
const int numTris = surface->numTriangles;
const mdxmTriangle_t * const tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles);
for ( j = 0; j < numTris; j++ )
{
assert(tris[j].indexes[0]>=0&&tris[j].indexes[0]<numVerts);
assert(tris[j].indexes[1]>=0&&tris[j].indexes[1]<numVerts);
assert(tris[j].indexes[2]>=0&&tris[j].indexes[2]<numVerts);
flags=63&
GoreVerts[tris[j].indexes[0]].flags&
GoreVerts[tris[j].indexes[1]].flags&
GoreVerts[tris[j].indexes[2]].flags;
if (flags)
{
continue;
}
else
{
// we hit a triangle, so init a collision record...
//
int i = 0;
for (; i<MAX_G2_COLLISIONS;i++)
{
if (TS.collRecMap[i].mEntityNum == -1)
{
CCollisionRecord &newCol = TS.collRecMap[i];
newCol.mPolyIndex = j;
newCol.mEntityNum = TS.entNum;
newCol.mSurfaceIndex = surface->thisSurfaceIndex;
newCol.mModelIndex = TS.modelIndex;
// if (face>0)
// {
newCol.mFlags = G2_FRONTFACE;
// }
// else
// {
// newCol.mFlags = G2_BACKFACE;
// }
//get normal from triangle
const float *A = &verts[(tris[j].indexes[0] * 5)];
const float *B = &verts[(tris[j].indexes[1] * 5)];
const float *C = &verts[(tris[j].indexes[2] * 5)];
vec3_t normal;
vec3_t edgeAC, edgeBA;
VectorSubtract(C, A, edgeAC);
VectorSubtract(B, A, edgeBA);
CrossProduct(edgeBA, edgeAC, normal);
// transform normal (but don't translate) into world angles
TransformPoint(normal, newCol.mCollisionNormal, &worldMatrix);
VectorNormalize(newCol.mCollisionNormal);
newCol.mMaterial = newCol.mLocation = 0;
// exit now if we should
if (TS.eG2TraceType == G2_RETURNONHIT)
{
TS.hitOne = true;
return true;
}
vec3_t distVect;
#if 0
//i don't know the hitPoint, but let's just assume it's the first vert for now...
float *hitPoint = (float *)A;
#else
//yeah, I want the collision point. Let's work out the impact point on the triangle. -rww
vec3_t hitPoint;
float side, side2;
float dist;
float third = -(A[0]*(B[1]*C[2] - C[1]*B[2]) + B[0]*(C[1]*A[2] - A[1]*C[2]) + C[0]*(A[1]*B[2] - B[1]*A[2]) );
VectorSubtract(TS.rayEnd, TS.rayStart, distVect);
side = normal[0]*TS.rayStart[0] + normal[1]*TS.rayStart[1] + normal[2]*TS.rayStart[2] + third;
side2 = normal[0]*distVect[0] + normal[1]*distVect[1] + normal[2]*distVect[2];
if (fabsf(side2)<1E-8f)
{
//i don't know the hitPoint, but let's just assume it's the first vert for now...
VectorSubtract(A, TS.rayStart, distVect);
dist = VectorLength(distVect);
VectorSubtract(TS.rayEnd, TS.rayStart, distVect);
VectorMA(TS.rayStart, dist/VectorLength(distVect), distVect, hitPoint);
}
else
{
dist = side/side2;
VectorMA(TS.rayStart, -dist, distVect, hitPoint);
}
#endif
VectorSubtract(hitPoint, TS.rayStart, distVect);
newCol.mDistance = VectorLength(distVect);
assert( !Q_isnan(newCol.mDistance) );
// put the hit point back into world space
TransformAndTranslatePoint(hitPoint, newCol.mCollisionPosition, &worldMatrix);
newCol.mBarycentricI = newCol.mBarycentricJ = 0.0f;
break;
}
}
if (i==MAX_G2_COLLISIONS)
{
//assert(i!=MAX_G2_COLLISIONS); // run out of collision record space - happens OFTEN
TS.hitOne = true; //force stop recursion
return true; // return true to avoid wasting further time, but no hit will result without a record
}
}
}
return false;
}
// look at a surface and then do the trace on each poly
static void G2_TraceSurfaces(CTraceSurface &TS)
{
int i;
// back track and get the surfinfo struct for this surface
assert(TS.currentModel);
assert(TS.currentModel->mdxm);
const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(TS.currentModel, TS.surfaceNum, TS.lod);
const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)TS.currentModel->mdxm + sizeof(mdxmHeader_t));
const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]);
// see if we have an override surface in the surface list
const surfaceInfo_t *surfOverride = G2_FindOverrideSurface(TS.surfaceNum, TS.rootSList);
// don't allow recursion if we've already hit a polygon
if (TS.hitOne)
{
return;
}
// really, we should use the default flags for this surface unless it's been overriden
int offFlags = surfInfo->flags;
// set the off flags if we have some
if (surfOverride)
{
offFlags = surfOverride->offFlags;
}
// if this surface is not off, try to hit it
if (!offFlags)
{
#ifdef _G2_GORE
if (TS.collRecMap)
{
#endif
if (!(Q_fabs(TS.m_fRadius) < 0.1)) // if not a point-trace
{
// .. then use radius check
//
if (G2_RadiusTracePolys(surface, // const mdxmSurface_t *surface,
TS
)
&& (TS.eG2TraceType == G2_RETURNONHIT)
)
{
TS.hitOne = true;
return;
}
}
else
{
// go away and trace the polys in this surface
if (G2_TracePolys(surface, surfInfo, TS)
&& (TS.eG2TraceType == G2_RETURNONHIT)
)
{
// ok, we hit one, *and* we want to return instantly because the returnOnHit is set
// so indicate we've hit one, so other surfaces don't get hit and return
TS.hitOne = true;
return;
}
}
#ifdef _G2_GORE
}
else
{
G2_GorePolys(surface, TS, surfInfo);
}
#endif
}
// if we are turning off all descendants, then stop this recursion now
if (offFlags & G2SURFACEFLAG_NODESCENDANTS)
{
return;
}
// now recursively call for the children
for (i=0; i< surfInfo->numChildren && !TS.hitOne; i++)
{
TS.surfaceNum = surfInfo->childIndexes[i];
G2_TraceSurfaces(TS);
}
}
#ifdef _G2_GORE
void G2_TraceModels(CGhoul2Info_v &ghoul2, vec3_t rayStart, vec3_t rayEnd, CCollisionRecord *collRecMap, int entNum, EG2_Collision eG2TraceType, int useLod, float fRadius, float ssize,float tsize,float theta,int shader, SSkinGoreData *gore, qboolean skipIfLODNotMatch)
#else
void G2_TraceModels(CGhoul2Info_v &ghoul2, vec3_t rayStart, vec3_t rayEnd, CCollisionRecord *collRecMap, int entNum, EG2_Collision eG2TraceType, int useLod, float fRadius)
#endif
{
int i, lod;
skin_t *skin;
jk_shader_t *cust_shader;
#if !defined(JK2_MODE) || defined(_G2_GORE)
qboolean firstModelOnly = qfalse;
#endif // !JK2_MODE || _G2_GORE
int firstModel = 0;
#ifndef JK2_MODE
if ( cg_g2MarksAllModels == NULL )
{
cg_g2MarksAllModels = ri.Cvar_Get( "cg_g2MarksAllModels", "0", 0 );
}
if (cg_g2MarksAllModels == NULL
|| !cg_g2MarksAllModels->integer )
{
firstModelOnly = qtrue;
}
#endif // !JK2_MODE
#ifdef _G2_GORE
if ( gore
&& gore->firstModel > 0 )
{
firstModel = gore->firstModel;
firstModelOnly = qfalse;
}
#endif
// walk each possible model for this entity and try tracing against it
for (i=firstModel; i<ghoul2.size(); i++)
{
CGhoul2Info &g=ghoul2[i];
#ifdef _G2_GORE
goreModelIndex=i;
// don't bother with models that we don't care about.
if (g.mModelindex == -1)
{
continue;
}
#endif
// don't bother with models that we don't care about.
if (!g.mValid)
{
continue;
}
assert(G2_MODEL_OK(&ghoul2[i]));
// do we really want to collide with this object?
if (g.mFlags & GHOUL2_NOCOLLIDE)
{
continue;
}
if (g.mCustomShader)
{
cust_shader = R_GetShaderByHandle(g.mCustomShader );
}
else
{
cust_shader = NULL;
}
// figure out the custom skin thing
if ( g.mSkin > 0 && g.mSkin < tr.numSkins )
{
skin = R_GetSkinByHandle( g.mSkin );
}
else
{
skin = NULL;
}
lod = G2_DecideTraceLod(g,useLod);
#ifndef JK2_MODE
if ( skipIfLODNotMatch )
{//we only want to hit this SPECIFIC LOD...
if ( lod != useLod )
{//doesn't match, skip this model
continue;
}
}
#endif // !JK2_MODE
//reset the quick surface override lookup
G2_FindOverrideSurface(-1, g.mSlist);
#ifdef _G2_GORE
CTraceSurface TS(g.mSurfaceRoot, g.mSlist, g.currentModel, lod, rayStart, rayEnd, collRecMap, entNum, i, skin, cust_shader, g.mTransformedVertsArray, eG2TraceType, fRadius, ssize, tsize, theta, shader, &g, gore);
#else
CTraceSurface TS(g.mSurfaceRoot, g.mSlist, g.currentModel, lod, rayStart, rayEnd, collRecMap, entNum, i, skin, cust_shader, g.mTransformedVertsArray, eG2TraceType, fRadius);
#endif
// start the surface recursion loop
G2_TraceSurfaces(TS);
// if we've hit one surface on one model, don't bother doing the rest
if (TS.hitOne)
{
break;
}
#ifdef _G2_GORE
if ( !collRecMap && firstModelOnly )
{
// we don't really need to do multiple models for gore.
break;
}
#endif
}
}
void TransformPoint (const vec3_t in, vec3_t out, mdxaBone_t *mat) {
for (int i=0;i<3;i++)
{
out[i]= in[0]*mat->matrix[i][0] + in[1]*mat->matrix[i][1] + in[2]*mat->matrix[i][2];
}
}
void TransformAndTranslatePoint (const vec3_t in, vec3_t out, mdxaBone_t *mat) {
for (int i=0;i<3;i++)
{
out[i]= in[0]*mat->matrix[i][0] + in[1]*mat->matrix[i][1] + in[2]*mat->matrix[i][2] + mat->matrix[i][3];
}
}
// create a matrix using a set of angles
void Create_Matrix(const float *angle, mdxaBone_t *matrix)
{
vec3_t axis[3];
// convert angles to axis
AnglesToAxis( angle, axis );
matrix->matrix[0][0] = axis[0][0];
matrix->matrix[1][0] = axis[0][1];
matrix->matrix[2][0] = axis[0][2];
matrix->matrix[0][1] = axis[1][0];
matrix->matrix[1][1] = axis[1][1];
matrix->matrix[2][1] = axis[1][2];
matrix->matrix[0][2] = axis[2][0];
matrix->matrix[1][2] = axis[2][1];
matrix->matrix[2][2] = axis[2][2];
matrix->matrix[0][3] = 0;
matrix->matrix[1][3] = 0;
matrix->matrix[2][3] = 0;
}
// given a matrix, generate the inverse of that matrix
void Inverse_Matrix(mdxaBone_t *src, mdxaBone_t *dest)
{
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
dest->matrix[i][j]=src->matrix[j][i];
}
}
for (i = 0; i < 3; i++)
{
dest->matrix[i][3]=0;
for (j = 0; j < 3; j++)
{
dest->matrix[i][3]-=dest->matrix[i][j]*src->matrix[j][3];
}
}
}
// generate the world matrix for a given set of angles and origin - called from lots of places
void G2_GenerateWorldMatrix(const vec3_t angles, const vec3_t origin)
{
Create_Matrix(angles, &worldMatrix);
worldMatrix.matrix[0][3] = origin[0];
worldMatrix.matrix[1][3] = origin[1];
worldMatrix.matrix[2][3] = origin[2];
Inverse_Matrix(&worldMatrix, &worldMatrixInv);
}
// go away and determine what the pointer for a specific surface definition within the model definition is
void *G2_FindSurface(const model_s *mod, int index, int lod)
{
assert(mod);
assert(mod->mdxm);
// point at first lod list
byte *current = (byte*)((intptr_t)mod->mdxm + (intptr_t)mod->mdxm->ofsLODs);
int i;
//walk the lods
assert(lod>=0&&lod<mod->mdxm->numLODs);
for (i=0; i<lod; i++)
{
mdxmLOD_t *lodData = (mdxmLOD_t *)current;
current += lodData->ofsEnd;
}
// avoid the lod pointer data structure
current += sizeof(mdxmLOD_t);
mdxmLODSurfOffset_t *indexes = (mdxmLODSurfOffset_t *)current;
// we are now looking at the offset array
if (index >= mod->mdxm->numSurfaces)
{
index = mod->mdxm->numSurfaces-1;
}
current += indexes->offsets[index];
return (void *)current;
}
#define SURFACE_SAVE_BLOCK_SIZE sizeof(surfaceInfo_t)
#define BOLT_SAVE_BLOCK_SIZE sizeof(boltInfo_t)
#define BONE_SAVE_BLOCK_SIZE sizeof(boneInfo_t)
void G2_SaveGhoul2Models(
CGhoul2Info_v& ghoul2)
{
ojk::SavedGameHelper saved_game(
::ri.saved_game);
saved_game.reset_buffer();
// is there anything to save?
if (!ghoul2.IsValid() || ghoul2.size() == 0)
{
const int zero_size = 0;
#ifdef JK2_MODE
saved_game.write<int32_t>(
zero_size);
saved_game.write_chunk_and_size<int32_t>(
INT_ID('G', 'L', '2', 'S'),
INT_ID('G', 'H', 'L', '2'));
#else
saved_game.write_chunk<int32_t>(
INT_ID('G', 'H', 'L', '2'),
zero_size); //write out a zero buffer
#endif // JK2_MODE
return;
}
// save out how many ghoul2 models we have
const int model_count = static_cast<int>(ghoul2.size());
saved_game.write<int32_t>(
model_count);
for (int i = 0; i < model_count; ++i)
{
// first save out the ghoul2 details themselves
ghoul2[i].sg_export(
saved_game);
// save out how many surfaces we have
const int surface_count = static_cast<int>(ghoul2[i].mSlist.size());
saved_game.write<int32_t>(
surface_count);
// now save the all the surface list info
for (int x = 0; x < surface_count; ++x)
{
ghoul2[i].mSlist[x].sg_export(
saved_game);
}
// save out how many bones we have
const int bone_count = static_cast<int>(ghoul2[i].mBlist.size());
saved_game.write<int32_t>(
bone_count);
// now save the all the bone list info
for (int x = 0; x < bone_count; ++x)
{
ghoul2[i].mBlist[x].sg_export(
saved_game);
}
// save out how many bolts we have
const int bolt_count = static_cast<int>(ghoul2[i].mBltlist.size());
saved_game.write<int32_t>(
bolt_count);
// lastly save the all the bolt list info
for (int x = 0; x < bolt_count; ++x)
{
ghoul2[i].mBltlist[x].sg_export(
saved_game);
}
}
#ifdef JK2_MODE
saved_game.write_chunk_and_size<int32_t>(
INT_ID('G', 'L', '2', 'S'),
INT_ID('G', 'H', 'L', '2'));
#else
saved_game.write_chunk(
INT_ID('G', 'H', 'L', '2'));
#endif // JK2_MODE
}
// FIXME Remove 'buffer' parameter
void G2_LoadGhoul2Model(
CGhoul2Info_v& ghoul2,
char* buffer)
{
static_cast<void>(buffer);
ojk::SavedGameHelper saved_game(
::ri.saved_game);
// first thing, lets see how many ghoul2 models we have, and resize our buffers accordingly
int model_count = 0;
#ifdef JK2_MODE
if (saved_game.get_buffer_size() > 0)
{
#endif // JK2_MODE
saved_game.read<int32_t>(
model_count);
#ifdef JK2_MODE
}
#endif // JK2_MODE
ghoul2.resize(
model_count);
// did we actually resize to a value?
if (model_count == 0)
{
// no, ok, well, done then.
return;
}
// now we have enough instances, lets go through each one and load up the relevant details
for (decltype(model_count) i = 0; i < model_count; ++i)
{
ghoul2[i].mSkelFrameNum = 0;
ghoul2[i].mModelindex = -1;
ghoul2[i].mFileName[0] = 0;
ghoul2[i].mValid = false;
// load the ghoul2 info from the buffer
ghoul2[i].sg_import(
saved_game);
if (ghoul2[i].mModelindex != -1 && ghoul2[i].mFileName[0])
{
ghoul2[i].mModelindex = i;
::G2_SetupModelPointers(
&ghoul2[i]);
}
// give us enough surfaces to load up the data
int surface_count = 0;
saved_game.read<int32_t>(
surface_count);
ghoul2[i].mSlist.resize(surface_count);
// now load all the surfaces
for (decltype(surface_count) x = 0; x < surface_count; ++x)
{
ghoul2[i].mSlist[x].sg_import(
saved_game);
}
// give us enough bones to load up the data
int bone_count = 0;
saved_game.read<int32_t>(
bone_count);
ghoul2[i].mBlist.resize(
bone_count);
// now load all the bones
for (decltype(bone_count) x = 0; x < bone_count; ++x)
{
ghoul2[i].mBlist[x].sg_import(
saved_game);
}
// give us enough bolts to load up the data
int bolt_count = 0;
saved_game.read<int32_t>(
bolt_count);
ghoul2[i].mBltlist.resize(
bolt_count);
// now load all the bolts
for (decltype(bolt_count) x = 0; x < bolt_count; ++x)
{
ghoul2[i].mBltlist[x].sg_import(
saved_game);
}
}
saved_game.ensure_all_data_read();
}