Files

594 lines
16 KiB
C++

/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "cm_local.h"
#include "../server/server.h"
#include "../client/client.h"
#include "../corepp/tiki.h"
#define LOCATION_FAIL -2
#define MAX_HITLOCATIONS 19
const char *szLocArray[] = {
"Bip01 Head",
"Bip01 Head",
"Bip01 Neck",
"Bip01 Spine2",
"Bip01 Spine1",
"Bip01 Spine",
"Bip01 Pelvis",
"Bip01 R UpperArm",
"Bip01 L UpperArm",
"Bip01 R Thigh",
"Bip01 L Thigh",
"Bip01 R Forearm",
"Bip01 L Forearm",
"Bip01 R Calf",
"Bip01 L Calf",
"Bip01 R Hand",
"Bip01 L Hand",
"Bip01 R Foot",
"Bip01 L Foot"
};
float fLocRadius[] = {
5.0, 5.5, 4.0, 9.0, 8.0, 9.0, 9.0, 7.0, 7.0, 8.0, 8.0, 5.5, 5.5, 7.5, 7.5, 6.0, 6.0, 6.0, 6.0,
};
vec3_t vLocOffset[] = {
{2.5, 3.5, 0.0},
{8.5, 1.0, 0.0},
{3.5, -1.0, 0.0},
{5.0, 1.0, 3.0},
{2.0, 1.0, 3.0},
{1.0, 1.0, 3.0},
{-3.0, 1.0, 4.0},
{4.0, 0.0, 0.0},
{4.0, 0.0, 0.0},
{12.0, 0.0, 0.0},
{12.0, 0.0, 0.0},
{5.0, 1.0, 0.0},
{5.0, 1.0, 0.0},
{8.0, 0.0, 0.0},
{8.0, 0.0, 0.0},
{5.0, 1.0, 0.0},
{5.0, 1.0, 0.0},
{3.0, 2.0, 0.0},
{3.0, 2.0, 0.0},
};
float fLocRadius_Secondary[] = {
0.0, 5.5, 4.0, 9.0, 8.0, 9.0, 9.0, 6.0, 6.0, 8.0, 8.0, 5.0, 5.0, 6.5, 6.5, 0.0, 0.0, 5.0, 5.0,
};
vec3_t vLocOffset_Secondary[] = {
{0.0, 0.0, 0.0 },
{7.0, -2.5, 0.0 },
{2.0, 2.0, 0.0 },
{5.0, 1.0, -3.0},
{2.0, 1.0, -3.0},
{1.0, 1.0, -3.0},
{-3.0, 1.0, -4.0},
{11.5, 0.0, 0.0 },
{11.5, 0.0, 0.0 },
{22.0, 0.0, 0.0 },
{22.0, 0.0, 0.0 },
{11.5, 0.0, 0.0 },
{11.5, 0.0, 0.0 },
{19.0, 0.0, 0.0 },
{19.0, 0.0, 0.0 },
{0.0, 0.0, 0.0 },
{0.0, 0.0, 0.0 },
{4.0, 9.0, 0.0 },
{4.0, 9.0, 0.0 },
};
/*
==================
LineSegmentToSphereIntersect
==================
*/
static qboolean
LineSegmentToSphereIntersect(trace_t *pTrace, float fRadius, const vec3_t vCntr, const vec3_t vStart, const vec3_t vEnd)
{
vec3_t vDiff, vDir;
float fDist;
float fLSqrd;
float fRSqrd;
float fDSqrd;
float fLength;
float fOfs;
float fAngSin;
vec3_t vProject, vOfs;
fRSqrd = fRadius * fRadius;
VectorSubtract(vEnd, vStart, vDir);
fLength = VectorNormalize(vDir);
VectorSubtract(vCntr, vStart, vDiff);
fDSqrd = DotProduct(vDiff, vDir);
fDist = VectorLengthSquared(vDiff);
if (fDSqrd < 0 && fDist > fRSqrd) {
return qfalse;
}
fLSqrd = fDist - fDSqrd * fDSqrd;
if (fLSqrd > fRSqrd || fDSqrd > fLength && fDSqrd - fLength > fRadius) {
return qfalse;
}
vOfs[0] = vStart[0] + vDir[0] * fDSqrd;
vOfs[1] = vStart[1] + vDir[1] * fDSqrd;
vOfs[2] = vStart[2] + vDir[2] * fDSqrd;
VectorSubtract(vCntr, vOfs, vProject);
fOfs = VectorLength(vProject) / fRadius;
if (fOfs >= -0.5) {
if (fOfs <= 0.5) {
fAngSin = fOfs * (-0.531387674508458 * fOfs - 2.11e-14) + 1.00086138065435;
} else {
fAngSin = fOfs
* (((((-1507.55394345521 * fOfs + 6580.58002318442) * fOfs - 11860.0735285953) * fOfs
+ 11290.7510782536)
* fOfs
- 5986.89654545347)
* fOfs
+ 1675.66417006387)
- 192.426950291139;
}
} else {
fAngSin = fOfs
* (((((-337.31875783205 * fOfs - 1237.54375255107) * fOfs - 1802.11467325687) * fOfs - 1303.19904613494)
* fOfs
- 471.347871690988)
* fOfs
- 70.0883838161826);
}
pTrace->fraction = (fDSqrd - fRadius * fAngSin) / fLength;
return qtrue;
}
/*
==================
CM_GetHitLocationInfo
==================
*/
const char *CM_GetHitLocationInfo(int i_iLocation, float *o_fRadius, vec3_t o_vOffset)
{
if (i_iLocation < MAX_HITLOCATIONS) {
*o_fRadius = fLocRadius[i_iLocation];
VectorCopy(vLocOffset[i_iLocation], o_vOffset);
return szLocArray[i_iLocation];
} else {
*o_fRadius = 8.0f;
VectorClear(o_vOffset);
return NULL;
}
}
/*
==================
CM_GetHitLocationInfoSecondary
==================
*/
const char *CM_GetHitLocationInfoSecondary(int i_iLocation, float *o_fRadius, vec3_t o_vOffset)
{
if (i_iLocation < MAX_HITLOCATIONS) {
*o_fRadius = fLocRadius_Secondary[i_iLocation];
VectorCopy(vLocOffset_Secondary[i_iLocation], o_vOffset);
return szLocArray[i_iLocation];
} else {
*o_fRadius = 8.0f;
VectorClear(o_vOffset);
return NULL;
}
}
/*
==================
CM_TraceDeepSimple
==================
*/
static qboolean CM_TraceDeepSimple(
trace_t *results,
const vec3_t vStart,
const vec3_t vEnd,
int iBrushMask,
int iEntContents,
const vec3_t vEntMins,
const vec3_t vEntMaxs,
const vec3_t vEntAngles,
vec3_t vTransStart,
vec3_t vTransEnd
)
{
vec3_t vCntr;
vec3_t vTemp;
vec3_t vNewMins, vNewMaxs;
vec3_t vForward, vLeft, vUp;
clipHandle_t clipHandle;
vNewMins[0] = vEntMins[0] - 40.0;
vNewMins[1] = vEntMins[1] - 40.0;
vNewMins[2] = vEntMins[2] - 40.0;
vNewMaxs[0] = vEntMaxs[0] + 40.0;
vNewMaxs[1] = vEntMaxs[1] + 40.0;
vNewMaxs[2] = vEntMaxs[2] + 40.0;
clipHandle = CM_TempBoxModel(vNewMins, vNewMaxs, iEntContents);
AngleVectorsLeft(vEntAngles, vForward, vLeft, vUp);
VectorCopy(vTransStart, vTemp);
vTransStart[0] = DotProduct(vTemp, vForward);
vTransStart[1] = DotProduct(vTemp, vLeft);
vTransStart[2] = DotProduct(vTemp, vUp);
VectorCopy(vTransEnd, vTemp);
vTransEnd[0] = DotProduct(vTemp, vForward);
vTransEnd[1] = DotProduct(vTemp, vLeft);
vTransEnd[2] = DotProduct(vTemp, vUp);
// don't use mins and maxs
VectorClear(vCntr);
CM_BoxTrace(results, vTransStart, vTransEnd, vCntr, vCntr, clipHandle, iBrushMask, qfalse);
if (results->fraction == 1.0 && !results->startsolid && !results->allsolid) {
VectorCopy(vEnd, results->endpos);
return qfalse;
}
return qtrue;
}
/*
==================
CM_TraceDeepSimple2
==================
*/
static qboolean CM_TraceDeepSimple2(
trace_t *results,
const orientation_t *orPosition,
vec3_t vOffset,
float fRad,
const char *pszTagName,
int iLocation,
const vec3_t vTransStart,
const vec3_t vTransEnd
)
{
vec3_t vCntr;
VectorCopy(orPosition->origin, vCntr);
if (vOffset[0] != 0.0) {
VectorMA(vCntr, vOffset[0], orPosition->axis[0], vCntr);
}
if (vOffset[1] != 0.0) {
VectorMA(vCntr, vOffset[1], orPosition->axis[1], vCntr);
}
if (vOffset[2] != 0.0) {
VectorMA(vCntr, vOffset[2], orPosition->axis[2], vCntr);
}
if (LineSegmentToSphereIntersect(results, fRad, vCntr, vTransStart, vTransEnd)) {
return qtrue;
}
// get the secondary location if any
if (!CM_GetHitLocationInfoSecondary(iLocation, &fRad, vOffset)) {
return qfalse;
}
VectorCopy(orPosition->origin, vCntr);
if (vOffset[0] != 0.0) {
VectorMA(vCntr, vOffset[0], orPosition->axis[0], vCntr);
}
if (vOffset[1] != 0.0) {
VectorMA(vCntr, vOffset[1], orPosition->axis[1], vCntr);
}
if (vOffset[2] != 0.0) {
VectorMA(vCntr, vOffset[2], orPosition->axis[2], vCntr);
}
if (LineSegmentToSphereIntersect(results, fRad, vCntr, vTransStart, vTransEnd)) {
return qtrue;
}
return qfalse;
}
/*
==================
CM_TraceDeepFail
==================
*/
static void CM_TraceDeepFail(trace_t *results, const vec3_t vEnd)
{
results->fraction = 1.0;
results->allsolid = qfalse;
results->startsolid = qfalse;
VectorCopy(vEnd, results->endpos);
results->entityNum = ENTITYNUM_NONE;
results->location = LOCATION_FAIL;
results->ent = NULL;
}
/*
==================
CM_TraceDeepSuccess
==================
*/
static void CM_TraceDeepSuccess(
trace_t *results, const vec3_t vStart, const vec3_t vEnd, int iEntNum, int iEntContents, int iLocation
)
{
results->endpos[0] = vStart[0] + results->fraction * (vEnd[0] - vStart[0]);
results->endpos[1] = vStart[1] + results->fraction * (vEnd[1] - vStart[1]);
results->endpos[2] = vStart[2] + results->fraction * (vEnd[2] - vStart[2]);
results->allsolid = qfalse;
results->startsolid = qfalse;
results->contents = iEntContents;
results->entityNum = iEntNum;
results->location = iLocation;
results->ent = NULL;
}
/*
==================
SV_TraceDeep_DebugDraw
==================
*/
void SV_TraceDeep_DebugDraw(gentity_t *touch, orientation_t& orTag, int iLocation, qboolean bHit)
{
int i;
float fRad;
vec3_t vOffset;
vec3_t vCntr;
orientation_t orEnt;
orientation_t orPosition;
const char *pszLocationName;
if (!sv_deeptracedebug->integer) {
return;
}
VectorCopy(touch->s.origin, orEnt.origin);
AngleVectors(touch->s.angles, orEnt.axis[0], orEnt.axis[1], orEnt.axis[2]);
VectorInverse(orEnt.axis[1]);
pszLocationName = CM_GetHitLocationInfo(iLocation, &fRad, vOffset);
if (bHit) {
Com_Printf("Hit %i '%s'\n", iLocation, pszLocationName);
}
// get the orientation
VectorCopy(orEnt.origin, orPosition.origin);
for (i = 0; i < 3; i++) {
VectorMA(orPosition.origin, orEnt.origin[i], orEnt.axis[i], orPosition.origin);
}
MatrixMultiply(orTag.axis, orEnt.axis, orPosition.axis);
VectorCopy(orPosition.origin, vCntr);
if (vOffset[0] != 0.0) {
VectorMA(vCntr, vOffset[0], orPosition.axis[0], vCntr);
}
if (vOffset[1] != 0.0) {
VectorMA(vCntr, vOffset[1], orPosition.axis[1], vCntr);
}
if (vOffset[2] != 0.0) {
VectorMA(vCntr, vOffset[2], orPosition.axis[2], vCntr);
}
if (bHit) {
ge->DebugCircle(vCntr, fRad, 1.0, 0.5, 0.0, 1.0, qfalse);
} else {
ge->DebugCircle(vCntr, fRad, 0.0, 0.5, 1.0, 1.0, qfalse);
}
// get the secondary location if any
if (!CM_GetHitLocationInfoSecondary(iLocation, &fRad, vOffset)) {
return;
}
VectorCopy(orPosition.origin, vCntr);
if (vOffset[0] != 0.0) {
VectorMA(vCntr, vOffset[0], orPosition.axis[0], vCntr);
}
if (vOffset[1] != 0.0) {
VectorMA(vCntr, vOffset[1], orPosition.axis[1], vCntr);
}
if (vOffset[2] != 0.0) {
VectorMA(vCntr, vOffset[2], orPosition.axis[2], vCntr);
}
if (bHit) {
ge->DebugCircle(vCntr, fRad, 1.0, 0.0, 0.5, 1.0, qfalse);
} else {
ge->DebugCircle(vCntr, fRad, 0.5, 0.0, 1.0, 1.0, qfalse);
}
}
/*
==================
SV_TraceDeep
==================
*/
void SV_TraceDeep(trace_t *results, const vec3_t vStart, const vec3_t vEnd, int iBrushMask, gentity_t *touch)
{
int iLocation, iBoneNum;
float fRad;
vec3_t vOffset;
orientation_t orPosition;
const char *pszTagName;
dtiki_t *tiki;
vec3_t vTransStart, vTransEnd;
// subtract the origin
VectorSubtract(vStart, touch->s.origin, vTransStart);
VectorSubtract(vEnd, touch->s.origin, vTransEnd);
// check if something was not hit first
if (!CM_TraceDeepSimple(
results,
vStart,
vEnd,
iBrushMask,
touch->r.contents,
touch->r.mins,
touch->r.maxs,
touch->s.angles,
vTransStart,
vTransEnd
)) {
return;
}
tiki = touch->tiki;
// trace through each locations
for (iLocation = 0; iLocation < MAX_HITLOCATIONS; iLocation++) {
pszTagName = CM_GetHitLocationInfo(iLocation, &fRad, vOffset);
iBoneNum = tiki->GetBoneNumFromName(pszTagName);
if (iBoneNum == -1) {
continue;
}
// retrieve the orientation from the game dll
orPosition = ge->TIKI_Orientation(touch, iBoneNum);
// check if the tag was hit
if (CM_TraceDeepSimple2(results, &orPosition, vOffset, fRad, pszTagName, iLocation, vTransStart, vTransEnd)) {
SV_TraceDeep_DebugDraw(touch, orPosition, iLocation, qtrue);
// set the location
CM_TraceDeepSuccess(results, vStart, vEnd, touch->s.number, touch->r.contents, iLocation);
return;
}
SV_TraceDeep_DebugDraw(touch, orPosition, iLocation, qfalse);
}
// fail
CM_TraceDeepFail(results, vEnd);
}
/*
==================
CL_CheckWeights
==================
*/
qboolean CL_CheckWeights(refEntity_t *ent)
{
int i;
if (!ent->tiki) {
return qtrue;
}
for (i = 0; i < MAX_FRAMEINFOS; i++) {
if (ent->frameInfo[i].weight > 0.0f) {
return qtrue;
}
}
return qfalse;
}
#ifdef CLIENT
/*
==================
CL_TraceDeep
==================
*/
qboolean CL_TraceDeep(
trace_t *results,
const vec3_t vStart,
const vec3_t vEnd,
int iBrushMask,
int iEntNum,
int iEntContents,
const vec3_t vEntMins,
const vec3_t vEntMaxs,
const vec3_t vEntOrigin,
const vec3_t vEntAngles,
refEntity_t *model
)
{
int iLocation, iBoneNum;
float fRad;
vec3_t vOffset;
orientation_t orPosition;
const char *pszTagName;
dtiki_t *tiki;
vec3_t vTransStart, vTransEnd;
// subtract the origin
VectorSubtract(vStart, vEntOrigin, vTransStart);
VectorSubtract(vEnd, vEntOrigin, vTransEnd);
// check if something was not hit first
if (!CM_TraceDeepSimple(
results, vStart, vEnd, iBrushMask, iEntContents, vEntMins, vEntMaxs, vEntAngles, vTransStart, vTransEnd
)) {
return qfalse;
}
tiki = model->tiki;
// trace through each locations
for (iLocation = 0; iLocation < MAX_HITLOCATIONS; iLocation++) {
pszTagName = CM_GetHitLocationInfo(iLocation, &fRad, vOffset);
iBoneNum = tiki->GetBoneNumFromName(pszTagName);
if (iBoneNum == -1) {
continue;
}
// retrieve the orientation from the game dll
orPosition = re.TIKI_Orientation(model, iBoneNum);
// check if the tag was hit
if (CM_TraceDeepSimple2(results, &orPosition, vOffset, fRad, pszTagName, iLocation, vTransStart, vTransEnd)) {
// set the location
CM_TraceDeepSuccess(results, vStart, vEnd, iEntNum, iEntContents, iLocation);
return qtrue;
}
}
// fail
CM_TraceDeepFail(results, vEnd);
return qfalse;
}
#endif