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