/* =========================================================================== 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