2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2024-09-04 21:41:16 +02:00
|
|
|
Copyright (C) 2024 the OpenMoHAA team
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-04-30 00:02:16 +02:00
|
|
|
This file is part of OpenMoHAA source code.
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-04-30 00:02:16 +02:00
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
2016-03-27 11:49:47 +02:00
|
|
|
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.
|
|
|
|
|
2023-04-30 00:02:16 +02:00
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
2016-03-27 11:49:47 +02:00
|
|
|
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
|
2023-04-30 00:02:16 +02:00
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
2016-03-27 11:49:47 +02:00
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
2023-04-30 00:02:16 +02:00
|
|
|
|
|
|
|
// DESCRIPTION:
|
|
|
|
// wall marks
|
2016-03-27 11:49:47 +02:00
|
|
|
|
|
|
|
#include "cg_local.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
===================================================================
|
|
|
|
|
|
|
|
MARK POLYS
|
|
|
|
|
|
|
|
===================================================================
|
|
|
|
*/
|
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
#define MAX_MARK_FRAGMENTS 128
|
|
|
|
#define MAX_MARK_POINTS 384
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
static vec3_t cg_vEntAngles;
|
|
|
|
static vec3_t cg_vEntOrigin;
|
|
|
|
static vec3_t cg_fEntAxis[3];
|
2023-07-12 23:53:30 +02:00
|
|
|
static qboolean cg_bEntAnglesSet;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
static int cg_iLastEntIndex;
|
|
|
|
static int cg_iLastEntTime;
|
2023-07-15 20:41:52 +02:00
|
|
|
static qboolean cg_bLastEntValid;
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
markPoly_t *cg_freeMarkPolys; // single linked list
|
|
|
|
markObj_t cg_activeMarkObjs;
|
|
|
|
markObj_t *cg_freeMarkObjs;
|
2023-07-10 23:28:17 +02:00
|
|
|
treadMark_t cg_treadMarks[MAX_TREAD_MARKS];
|
2023-07-12 23:53:30 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
markPoly_t *cg_markPolys;
|
|
|
|
int cg_iNumMarkPolys;
|
|
|
|
markObj_t *cg_markObjs;
|
|
|
|
int cg_iNumFreeMarkObjs;
|
|
|
|
int cg_iMinFreeMarkObjs;
|
|
|
|
qboolean cg_bMarksInitialized;
|
|
|
|
cvar_t *cg_treadmark_test;
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2024-09-05 00:54:39 +02:00
|
|
|
vec3_t vec_upwards = {0, 0, 1};
|
2023-05-01 00:08:56 +02:00
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
typedef struct cg_impactmarkinfo_s {
|
|
|
|
vec3_t axis[3];
|
|
|
|
vec3_t origin;
|
2024-09-04 21:41:16 +02:00
|
|
|
float fSCenter;
|
|
|
|
float fTCenter;
|
|
|
|
float texCoordScaleS;
|
|
|
|
float texCoordScaleT;
|
|
|
|
byte colors[4];
|
|
|
|
int leafnum;
|
2023-07-15 20:41:52 +02:00
|
|
|
} cg_impactmarkinfo_t;
|
|
|
|
|
|
|
|
typedef struct cg_treadmarkinfo_s {
|
2024-09-04 21:41:16 +02:00
|
|
|
treadMark_t *pTread;
|
|
|
|
vec3_t vDirection;
|
|
|
|
vec3_t vRight;
|
|
|
|
float fStartDist;
|
|
|
|
float fStartTex;
|
|
|
|
float fStartAlpha;
|
|
|
|
float fLeftStartDist;
|
|
|
|
float fRightStartDist;
|
|
|
|
float fRightCenterDist;
|
|
|
|
float fLeftTexScale;
|
|
|
|
float fRightTexScale;
|
|
|
|
float fCenterTexScale;
|
|
|
|
float fLeftAlphaScale;
|
|
|
|
float fRightAlphaScale;
|
|
|
|
float fCenterAlphaScale;
|
|
|
|
float fOOWidth;
|
|
|
|
float fOODoubleWidth;
|
|
|
|
byte colors[4];
|
|
|
|
int leafnum;
|
2023-07-15 20:41:52 +02:00
|
|
|
} cg_treadmarkinfo_t;
|
|
|
|
|
|
|
|
int CG_GetMarkFragments(
|
2024-09-04 21:41:16 +02:00
|
|
|
int numVerts,
|
|
|
|
const vec3_t *pVerts,
|
|
|
|
const vec3_t vProjection,
|
|
|
|
const vec3_t *pPointBuffer,
|
|
|
|
markFragment_t *pFragmentBuffer,
|
|
|
|
float fRadiusSquared
|
|
|
|
)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
int iNumEnts;
|
|
|
|
int iCurrPoints, iCurrFragments;
|
|
|
|
int iCurrMaxPoints, iCurrMaxFragments;
|
|
|
|
int iNewPoints, iNewFragments;
|
|
|
|
clipHandle_t cmodel;
|
|
|
|
vec3_t vProjectionDir;
|
|
|
|
vec3_t vMins, vMaxs;
|
|
|
|
vec3_t vTemp;
|
|
|
|
vec3_t vAngles, vOrigin;
|
|
|
|
const vec3_t *pCurrPoint;
|
|
|
|
centity_t *pEntList[64];
|
|
|
|
entityState_t *pEnt;
|
|
|
|
markFragment_t *pCurrFragment;
|
|
|
|
markFragment_t *pFragment;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
iNewFragments = cgi.R_MarkFragments(
|
|
|
|
numVerts,
|
|
|
|
pVerts,
|
|
|
|
vProjection,
|
|
|
|
MAX_MARK_POINTS,
|
2024-09-04 21:41:16 +02:00
|
|
|
(float *)pPointBuffer,
|
2023-07-15 20:41:52 +02:00
|
|
|
MAX_MARK_FRAGMENTS,
|
|
|
|
pFragmentBuffer,
|
|
|
|
fRadiusSquared
|
|
|
|
);
|
|
|
|
|
|
|
|
if (iNewFragments > MAX_MARK_FRAGMENTS) {
|
|
|
|
return iNewFragments;
|
|
|
|
}
|
|
|
|
|
|
|
|
iNewPoints = 0;
|
|
|
|
for (i = 0, pFragment = pFragmentBuffer; i < iNewFragments; i++, pFragment++) {
|
|
|
|
iNewPoints += pFragment->numPoints;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iNewPoints >= MAX_MARK_POINTS) {
|
|
|
|
return iNewFragments;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
iCurrPoints = iNewPoints;
|
|
|
|
iCurrFragments = iNewFragments;
|
|
|
|
iCurrMaxPoints = MAX_MARK_POINTS - iNewPoints;
|
2023-07-15 20:41:52 +02:00
|
|
|
iCurrMaxFragments = MAX_MARK_FRAGMENTS - iNewFragments;
|
2024-09-04 21:41:16 +02:00
|
|
|
pCurrPoint = &pPointBuffer[iNewPoints];
|
|
|
|
pCurrFragment = &pFragmentBuffer[iNewFragments];
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
VectorNormalize2(vProjection, vProjectionDir);
|
|
|
|
ClearBounds(vMins, vMaxs);
|
|
|
|
|
|
|
|
for (i = 0; i < numVerts; i++) {
|
|
|
|
AddPointToBounds(pVerts[i], vMins, vMaxs);
|
|
|
|
VectorAdd(pVerts[i], vProjection, vTemp);
|
|
|
|
AddPointToBounds(vTemp, vMins, vMaxs);
|
|
|
|
VectorMA(pVerts[i], -20.0f, vProjectionDir, vTemp);
|
|
|
|
AddPointToBounds(vTemp, vMins, vMaxs);
|
|
|
|
}
|
|
|
|
|
|
|
|
iNumEnts = CG_GetBrushEntitiesInBounds(ARRAY_LEN(pEntList), pEntList, vMins, vMaxs);
|
|
|
|
if (!iNumEnts) {
|
|
|
|
return iNewFragments;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < iNumEnts; i++) {
|
|
|
|
pEnt = &pEntList[i]->currentState;
|
|
|
|
VectorCopy(pEntList[i]->lerpAngles, vAngles);
|
|
|
|
VectorCopy(pEntList[i]->lerpOrigin, vOrigin);
|
|
|
|
|
|
|
|
cmodel = cgi.CM_InlineModel(pEnt->modelindex);
|
2024-09-04 21:41:16 +02:00
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
iNewFragments = cgi.R_MarkFragmentsForInlineModel(
|
|
|
|
cmodel,
|
|
|
|
vAngles,
|
|
|
|
vOrigin,
|
|
|
|
numVerts,
|
|
|
|
pVerts,
|
|
|
|
vProjection,
|
|
|
|
iCurrMaxPoints,
|
2024-09-04 21:41:16 +02:00
|
|
|
(float *)pCurrPoint,
|
2023-07-15 20:41:52 +02:00
|
|
|
iCurrMaxFragments,
|
|
|
|
pCurrFragment,
|
|
|
|
fRadiusSquared
|
|
|
|
);
|
|
|
|
|
|
|
|
iCurrFragments += iNewFragments;
|
|
|
|
if (iCurrFragments >= MAX_MARK_FRAGMENTS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
iNewPoints = 0;
|
2024-01-27 21:14:13 +01:00
|
|
|
for (j = 0, pFragment = pCurrFragment; j < iNewFragments; j++, pFragment++) {
|
2023-07-15 20:41:52 +02:00
|
|
|
pFragment->firstPoint += iCurrPoints;
|
|
|
|
pFragment->iIndex = -pEnt->number;
|
|
|
|
iNewPoints += pFragment->numPoints;
|
|
|
|
}
|
|
|
|
|
|
|
|
iCurrPoints += iNewPoints;
|
|
|
|
if (iCurrPoints >= MAX_MARK_POINTS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pCurrPoint += iNewPoints;
|
|
|
|
iCurrMaxPoints -= iNewPoints;
|
|
|
|
iCurrMaxFragments -= iNewFragments;
|
|
|
|
pCurrFragment += iNewFragments;
|
|
|
|
}
|
|
|
|
|
|
|
|
return iCurrFragments;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
static qboolean CG_GetMarkInlineModelOrientation(int iIndex)
|
|
|
|
{
|
|
|
|
centity_t *pCEnt;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
if (iIndex == cg_iLastEntIndex && cg_iLastEntTime == cg.time) {
|
|
|
|
return cg_bLastEntValid;
|
|
|
|
}
|
|
|
|
|
|
|
|
pCEnt = &cg_entities[-iIndex];
|
2024-09-04 21:41:16 +02:00
|
|
|
if (pCEnt->currentValid && pCEnt->currentState.modelindex < cgs.numInlineModels) {
|
2023-07-15 20:41:52 +02:00
|
|
|
VectorCopy(pCEnt->lerpAngles, cg_vEntAngles);
|
|
|
|
VectorCopy(pCEnt->lerpOrigin, cg_vEntOrigin);
|
2024-01-27 21:14:13 +01:00
|
|
|
cg_bLastEntValid = qtrue;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
if (cg_vEntAngles[0] || cg_vEntAngles[1] || cg_vEntAngles[2]) {
|
|
|
|
AngleVectorsLeft(cg_vEntAngles, cg_fEntAxis[0], cg_fEntAxis[1], cg_fEntAxis[2]);
|
|
|
|
cg_bEntAnglesSet = qtrue;
|
|
|
|
} else if (cg_bEntAnglesSet) {
|
|
|
|
AxisClear(cg_fEntAxis);
|
|
|
|
cg_bEntAnglesSet = qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
cg_iLastEntIndex = iIndex;
|
2024-09-04 21:41:16 +02:00
|
|
|
cg_iLastEntTime = cg.time;
|
2023-07-15 20:41:52 +02:00
|
|
|
return cg_bLastEntValid;
|
|
|
|
}
|
|
|
|
|
|
|
|
cg_bLastEntValid = qfalse;
|
|
|
|
VectorClear(cg_vEntAngles);
|
|
|
|
VectorClear(cg_vEntOrigin);
|
|
|
|
|
|
|
|
if (cg_bEntAnglesSet) {
|
|
|
|
AxisClear(cg_fEntAxis);
|
|
|
|
cg_bEntAnglesSet = qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
static void CG_FragmentPosToWorldPos(const vec3_t vFrom, vec3_t vTo)
|
|
|
|
{
|
|
|
|
if (cg_bEntAnglesSet) {
|
2023-07-15 20:41:52 +02:00
|
|
|
VectorMA(cg_vEntOrigin, vFrom[0], cg_fEntAxis[0], vTo);
|
|
|
|
VectorMA(vTo, vFrom[1], cg_fEntAxis[1], vTo);
|
|
|
|
VectorMA(vTo, vFrom[2], cg_fEntAxis[2], vTo);
|
2024-09-04 21:41:16 +02:00
|
|
|
} else {
|
2023-07-15 20:41:52 +02:00
|
|
|
vTo[0] = cg_vEntOrigin[0] + *vFrom;
|
|
|
|
vTo[1] = cg_vEntOrigin[1] + vFrom[1];
|
|
|
|
vTo[2] = cg_vEntOrigin[2] + vFrom[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
qboolean CG_UpdateMarkPosition(markObj_t *pMark)
|
|
|
|
{
|
2023-07-15 20:41:52 +02:00
|
|
|
vec3_t v;
|
|
|
|
vec3_t pt;
|
2024-09-04 21:41:16 +02:00
|
|
|
int iIndex;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
iIndex = pMark->markPolys->iIndex;
|
|
|
|
if (!CG_GetMarkInlineModelOrientation(iIndex)) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy(pMark->markPolys->verts[0].xyz, v);
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (cg_bEntAnglesSet) {
|
2023-07-15 20:41:52 +02:00
|
|
|
VectorScale(cg_fEntAxis[0], v[0], pt);
|
|
|
|
VectorMA(pt, v[1], cg_fEntAxis[1], pt);
|
|
|
|
VectorMA(pt, v[2], cg_fEntAxis[2], pt);
|
|
|
|
VectorAdd(pt, cg_vEntOrigin, pt);
|
|
|
|
} else {
|
|
|
|
VectorAdd(v, cg_vEntOrigin, pt);
|
|
|
|
}
|
|
|
|
|
|
|
|
pMark->leafnum = cgi.CM_PointLeafnum(pt);
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
void CG_AddFragmentToScene(int iIndex, qhandle_t hShader, int iNumVerts, polyVert_t *pVerts)
|
|
|
|
{
|
2023-07-15 20:41:52 +02:00
|
|
|
if (!iIndex) {
|
|
|
|
cgi.R_AddPolyToScene(hShader, iNumVerts, pVerts, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iIndex > 0) {
|
|
|
|
cgi.R_AddTerrainMarkToScene(iIndex, hShader, iNumVerts, pVerts, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cg_bEntAnglesSet) {
|
2024-09-04 21:41:16 +02:00
|
|
|
int i;
|
|
|
|
vec3_t vTmp;
|
|
|
|
polyVert_t *pCurrVert;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
for (i = 0; i < iNumVerts; i++) {
|
|
|
|
pCurrVert = &pVerts[i];
|
|
|
|
|
|
|
|
vTmp[0] = cg_fEntAxis[0][0] * pCurrVert->xyz[0];
|
|
|
|
vTmp[1] = cg_fEntAxis[0][1] * pCurrVert->xyz[0];
|
|
|
|
vTmp[2] = cg_fEntAxis[0][2] * pCurrVert->xyz[0];
|
|
|
|
VectorMA(vTmp, pCurrVert->xyz[1], cg_fEntAxis[1], vTmp);
|
|
|
|
VectorMA(vTmp, pCurrVert->xyz[2], cg_fEntAxis[2], vTmp);
|
|
|
|
VectorAdd(vTmp, cg_vEntOrigin, pCurrVert->xyz);
|
|
|
|
}
|
|
|
|
} else {
|
2024-09-04 21:41:16 +02:00
|
|
|
int i;
|
|
|
|
polyVert_t *pCurrVert;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
for (i = 0; i < iNumVerts; i++) {
|
|
|
|
pCurrVert = &pVerts[i];
|
|
|
|
VectorAdd(pCurrVert->xyz, cg_vEntOrigin, pCurrVert->xyz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cgi.R_AddPolyToScene(hShader, iNumVerts, pVerts, 0);
|
|
|
|
}
|
|
|
|
|
2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===================
|
|
|
|
CG_InitMarkPolys
|
|
|
|
|
|
|
|
This is called at startup and for tournement restarts
|
|
|
|
===================
|
|
|
|
*/
|
2023-05-01 00:08:56 +02:00
|
|
|
void CG_InitMarks(void)
|
|
|
|
{
|
|
|
|
int i;
|
2023-07-12 23:53:30 +02:00
|
|
|
int iMaxMarks;
|
|
|
|
|
|
|
|
iMaxMarks = cg_maxMarks->integer;
|
|
|
|
if (iMaxMarks < 32) {
|
|
|
|
iMaxMarks = 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
cg_iMinFreeMarkObjs = iMaxMarks / 32;
|
|
|
|
if (cg_iMinFreeMarkObjs < 8) {
|
|
|
|
cg_iMinFreeMarkObjs = 8;
|
|
|
|
}
|
|
|
|
if (cg_iMinFreeMarkObjs > 32) {
|
|
|
|
cg_iMinFreeMarkObjs = 32;
|
|
|
|
}
|
2023-05-01 00:08:56 +02:00
|
|
|
|
2023-07-12 23:53:30 +02:00
|
|
|
cg_iNumMarkPolys = 7 * iMaxMarks / 4;
|
2023-05-01 00:08:56 +02:00
|
|
|
|
2023-07-12 23:53:30 +02:00
|
|
|
if (cg_markPolys) {
|
|
|
|
cgi.Free(cg_markPolys);
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
2023-07-10 21:09:56 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
cg_markPolys = (markPoly_t *)cgi.Malloc(sizeof(markPoly_t) * cg_iNumMarkPolys);
|
2023-07-12 23:53:30 +02:00
|
|
|
|
|
|
|
if (!cg_markPolys) {
|
|
|
|
cgi.Error(ERR_DROP, "CG_InitMarks: Could not allocate array for mark polys");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cg_markObjs) {
|
|
|
|
cgi.Free(cg_markObjs);
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
cg_markObjs = (markObj_t *)cgi.Malloc(sizeof(markObj_t) * iMaxMarks);
|
2023-07-12 23:53:30 +02:00
|
|
|
|
|
|
|
if (!cg_markObjs) {
|
|
|
|
cgi.Error(ERR_DROP, "CG_InitMarks: Could not allocate array for mark objects");
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(cg_markPolys, 0, sizeof(markPoly_t) * cg_iNumMarkPolys);
|
|
|
|
|
|
|
|
cg_freeMarkPolys = cg_markPolys;
|
|
|
|
for (i = 0; i < cg_iNumMarkPolys - 1; i++) {
|
|
|
|
cg_markPolys[i].nextPoly = &cg_markPolys[i + 1];
|
|
|
|
}
|
|
|
|
cg_markPolys[i].nextPoly = NULL;
|
|
|
|
|
|
|
|
cg_activeMarkObjs.nextMark = &cg_activeMarkObjs;
|
|
|
|
cg_activeMarkObjs.prevMark = &cg_activeMarkObjs;
|
|
|
|
memset(cg_markObjs, 0, sizeof(markObj_t) * iMaxMarks);
|
|
|
|
cg_freeMarkObjs = cg_markObjs;
|
|
|
|
|
|
|
|
for (i = 0; i < iMaxMarks - 1; i++) {
|
|
|
|
cg_markObjs[i].nextMark = &cg_markObjs[i + 1];
|
|
|
|
}
|
|
|
|
cg_markObjs[i].nextMark = NULL;
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
cg_iNumFreeMarkObjs = iMaxMarks;
|
2023-07-10 21:09:56 +02:00
|
|
|
cg_bMarksInitialized = qtrue;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
CG_FreeMarkPoly
|
|
|
|
==================
|
|
|
|
*/
|
2023-07-05 21:24:23 +02:00
|
|
|
void CG_FreeMarkPoly(markPoly_t *le)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2023-07-12 23:53:30 +02:00
|
|
|
if (!cg_bMarksInitialized) {
|
|
|
|
return;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
le->nextPoly = cg_freeMarkPolys;
|
2023-05-01 00:08:56 +02:00
|
|
|
cg_freeMarkPolys = le;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-07-12 23:53:30 +02:00
|
|
|
/*
|
|
|
|
==================
|
|
|
|
CG_FreeMarkObj
|
|
|
|
==================
|
|
|
|
*/
|
2024-09-04 21:41:16 +02:00
|
|
|
void CG_FreeMarkObj(markObj_t *pMark)
|
|
|
|
{
|
|
|
|
markPoly_t *pPoly;
|
|
|
|
markPoly_t *pNextPoly;
|
2023-07-12 23:53:30 +02:00
|
|
|
|
2024-11-11 15:08:08 +01:00
|
|
|
assert(pMark != &cg_activeMarkObjs);
|
|
|
|
|
2023-07-12 23:53:30 +02:00
|
|
|
for (pPoly = pMark->markPolys; pPoly; pPoly = pNextPoly) {
|
|
|
|
pNextPoly = pPoly->nextPoly;
|
|
|
|
CG_FreeMarkPoly(pPoly);
|
|
|
|
}
|
|
|
|
|
|
|
|
pMark->prevMark->nextMark = pMark->nextMark;
|
|
|
|
pMark->nextMark->prevMark = pMark->prevMark;
|
2024-09-04 21:41:16 +02:00
|
|
|
pMark->nextMark = cg_freeMarkObjs;
|
|
|
|
cg_freeMarkObjs = pMark;
|
2023-07-12 23:53:30 +02:00
|
|
|
cg_iNumFreeMarkObjs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
CG_FreeBestMarkObj
|
|
|
|
==================
|
|
|
|
*/
|
2024-09-04 21:41:16 +02:00
|
|
|
void CG_FreeBestMarkObj(qboolean bAllowFade)
|
|
|
|
{
|
|
|
|
markObj_t *pMark;
|
2023-07-12 23:53:30 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
for (pMark = cg_activeMarkObjs.prevMark; pMark != &cg_activeMarkObjs; pMark = pMark->prevMark) {
|
|
|
|
if (pMark->lastVisTime < cg.time - 250) {
|
2023-07-12 23:53:30 +02:00
|
|
|
CG_FreeMarkObj(pMark);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cg_iNumFreeMarkObjs || !bAllowFade) {
|
|
|
|
CG_FreeMarkObj(cg_activeMarkObjs.prevMark);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
for (pMark = cg_activeMarkObjs.prevMark; pMark != &cg_activeMarkObjs; pMark = pMark->prevMark) {
|
2023-07-12 23:53:30 +02:00
|
|
|
if (!pMark->alphaFade || pMark->time > cg.time - 9000) {
|
2024-10-31 19:37:42 +01:00
|
|
|
break;
|
2023-07-12 23:53:30 +02:00
|
|
|
}
|
|
|
|
}
|
2024-10-31 19:37:42 +01:00
|
|
|
|
|
|
|
pMark->time = cg.time - 9000;
|
|
|
|
pMark->alphaFade = qtrue;
|
2023-07-12 23:53:30 +02:00
|
|
|
}
|
|
|
|
|
2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===================
|
|
|
|
CG_AllocMark
|
|
|
|
|
|
|
|
Will allways succeed, even if it requires freeing an old active mark
|
|
|
|
===================
|
|
|
|
*/
|
2024-09-04 21:41:16 +02:00
|
|
|
markObj_t *CG_AllocMark(int iNumPolys)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
int iPolyCount;
|
|
|
|
markPoly_t *pPoly;
|
|
|
|
markObj_t *pMark;
|
2023-07-12 23:53:30 +02:00
|
|
|
|
|
|
|
if (!cg_bMarksInitialized) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iNumPolys < 1) {
|
|
|
|
return NULL;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2024-11-11 15:08:08 +01:00
|
|
|
if (iNumPolys > cg_iNumMarkPolys) {
|
|
|
|
// Added in OPM
|
|
|
|
// Make sure to not over allocate polys
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-07-12 23:53:30 +02:00
|
|
|
if (cg_iNumFreeMarkObjs <= cg_iMinFreeMarkObjs) {
|
|
|
|
CG_FreeBestMarkObj(1);
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
pMark = cg_freeMarkObjs;
|
2023-07-12 23:53:30 +02:00
|
|
|
cg_freeMarkObjs = cg_freeMarkObjs->nextMark;
|
|
|
|
|
|
|
|
memset(pMark, 0, sizeof(markObj_t));
|
|
|
|
pMark->lastVisTime = cg.time;
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
for (iPolyCount = 0; iPolyCount < iNumPolys; iPolyCount++) {
|
2023-07-12 23:53:30 +02:00
|
|
|
while (!cg_freeMarkPolys) {
|
|
|
|
CG_FreeBestMarkObj(qfalse);
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
pPoly = cg_freeMarkPolys;
|
2023-07-12 23:53:30 +02:00
|
|
|
cg_freeMarkPolys = pPoly->nextPoly;
|
|
|
|
|
|
|
|
memset(pPoly, 0, sizeof(*pPoly));
|
2024-09-04 21:41:16 +02:00
|
|
|
pPoly->nextPoly = pMark->markPolys;
|
2023-07-12 23:53:30 +02:00
|
|
|
pMark->markPolys = pPoly;
|
|
|
|
}
|
2023-05-01 00:08:56 +02:00
|
|
|
|
|
|
|
// link into the active list
|
2024-09-04 21:41:16 +02:00
|
|
|
pMark->nextMark = cg_activeMarkObjs.nextMark;
|
|
|
|
pMark->prevMark = &cg_activeMarkObjs;
|
2023-07-12 23:53:30 +02:00
|
|
|
cg_activeMarkObjs.nextMark->prevMark = pMark;
|
2024-09-04 21:41:16 +02:00
|
|
|
cg_activeMarkObjs.nextMark = pMark;
|
2024-10-31 19:37:42 +01:00
|
|
|
|
2023-07-12 23:53:30 +02:00
|
|
|
cg_iNumFreeMarkObjs--;
|
2024-10-31 19:37:42 +01:00
|
|
|
assert(cg_iNumFreeMarkObjs >= 0);
|
|
|
|
|
2023-07-12 23:53:30 +02:00
|
|
|
return pMark;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
2023-07-10 23:28:17 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
int CG_ImpactMark_GetLeafCallback(markFragment_t *mf, void *pCustom)
|
|
|
|
{
|
|
|
|
return ((cg_impactmarkinfo_t *)pCustom)->leafnum;
|
2023-07-15 20:41:52 +02:00
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
qboolean CG_ImpactMark_PerPolyCallback(const vec3_t *markPoints, markFragment_t *mf, polyVert_t *verts, void *pCustom)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
polyVert_t *v;
|
|
|
|
cg_impactmarkinfo_t *pInfo;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
pInfo = (cg_impactmarkinfo_t *)pCustom;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (mf->iIndex >= 0) {
|
2023-07-15 20:41:52 +02:00
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
|
|
|
vec3_t delta;
|
|
|
|
|
|
|
|
v = &verts[j];
|
|
|
|
VectorCopy(markPoints[j + mf->firstPoint], v->xyz);
|
|
|
|
VectorSubtract(v->xyz, pInfo->origin, delta);
|
2024-09-04 21:41:16 +02:00
|
|
|
v->st[0] = pInfo->fSCenter + DotProduct(delta, pInfo->axis[1]) * pInfo->texCoordScaleS;
|
|
|
|
v->st[1] = pInfo->fTCenter + DotProduct(delta, pInfo->axis[2]) * pInfo->texCoordScaleT;
|
2023-07-15 20:41:52 +02:00
|
|
|
v->modulate[0] = pInfo->colors[0];
|
|
|
|
v->modulate[1] = pInfo->colors[1];
|
|
|
|
v->modulate[2] = pInfo->colors[2];
|
|
|
|
v->modulate[3] = pInfo->colors[3];
|
|
|
|
}
|
2024-09-04 21:41:16 +02:00
|
|
|
} else {
|
2023-07-15 20:41:52 +02:00
|
|
|
if (!CG_GetMarkInlineModelOrientation(mf->iIndex)) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
|
|
|
vec3_t vWorldPos;
|
|
|
|
vec3_t delta;
|
|
|
|
|
|
|
|
v = &verts[j];
|
|
|
|
VectorCopy(markPoints[j + mf->firstPoint], v->xyz);
|
|
|
|
|
|
|
|
CG_FragmentPosToWorldPos(v->xyz, vWorldPos);
|
|
|
|
VectorSubtract(vWorldPos, pInfo->origin, delta);
|
2024-09-04 21:41:16 +02:00
|
|
|
v->st[0] = pInfo->fSCenter + DotProduct(delta, pInfo->axis[1]) * pInfo->texCoordScaleS;
|
|
|
|
v->st[1] = pInfo->fTCenter + DotProduct(delta, pInfo->axis[2]) * pInfo->texCoordScaleT;
|
2023-07-15 20:41:52 +02:00
|
|
|
v->modulate[0] = pInfo->colors[0];
|
|
|
|
v->modulate[1] = pInfo->colors[1];
|
|
|
|
v->modulate[2] = pInfo->colors[2];
|
|
|
|
v->modulate[3] = pInfo->colors[3];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return qtrue;
|
2023-07-10 23:28:17 +02:00
|
|
|
}
|
|
|
|
|
2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
CG_ImpactMark
|
|
|
|
|
|
|
|
origin should be a point within a unit of the plane
|
|
|
|
dir should be the plane normal
|
|
|
|
|
|
|
|
temporary marks will not be stored or randomly oriented, but immediately
|
|
|
|
passed to the renderer.
|
|
|
|
=================
|
|
|
|
*/
|
2023-07-05 21:24:23 +02:00
|
|
|
|
|
|
|
void CG_ImpactMark(
|
|
|
|
qhandle_t markShader,
|
|
|
|
const vec3_t origin,
|
|
|
|
const vec3_t dir,
|
|
|
|
float orientation,
|
2023-07-10 21:09:56 +02:00
|
|
|
float fSScale,
|
|
|
|
float fTScale,
|
2023-07-05 21:24:23 +02:00
|
|
|
float red,
|
|
|
|
float green,
|
|
|
|
float blue,
|
|
|
|
float alpha,
|
|
|
|
qboolean alphaFade,
|
|
|
|
qboolean temporary,
|
2023-07-10 21:09:56 +02:00
|
|
|
qboolean dolighting,
|
2023-07-05 21:24:23 +02:00
|
|
|
qboolean fadein,
|
|
|
|
float fSCenter,
|
|
|
|
float fTCenter
|
|
|
|
)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
int i;
|
|
|
|
int numFragments;
|
|
|
|
float fSScale2, fTScale2;
|
|
|
|
float fSScale3, fTScale3;
|
|
|
|
vec3_t originalPoints[4];
|
|
|
|
vec3_t markPoints[MAX_MARK_POINTS];
|
|
|
|
vec3_t projection;
|
|
|
|
markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf;
|
|
|
|
vec3_t v;
|
|
|
|
float fRadiusSquared;
|
2023-07-15 20:41:52 +02:00
|
|
|
cg_impactmarkinfo_t info;
|
2023-05-01 00:08:56 +02:00
|
|
|
|
2023-07-10 21:09:56 +02:00
|
|
|
if (!cg_bMarksInitialized) {
|
2023-05-01 00:08:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (!cg_addMarks->integer && markShader != cgs.media.shadowMarkShader
|
2023-07-10 21:09:56 +02:00
|
|
|
&& markShader != cgs.media.footShadowMarkShader) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fSScale == 0.0) {
|
|
|
|
fSScale = 1.0;
|
|
|
|
}
|
|
|
|
if (fTScale == 0.0) {
|
|
|
|
fTScale = 1.0;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
fRadiusSquared = fSScale * fSScale + fTScale * fTScale;
|
2023-07-15 20:41:52 +02:00
|
|
|
info.texCoordScaleS = 0.5 / fSScale;
|
|
|
|
info.texCoordScaleT = 0.5 / fTScale;
|
|
|
|
|
|
|
|
if (fSCenter < 0.0f || fSCenter > 1.0f) {
|
|
|
|
fSCenter = 0.5f;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
if (fTCenter < 0.0f || fTCenter > 1.0f) {
|
|
|
|
fTCenter = 0.5f;
|
|
|
|
}
|
|
|
|
|
|
|
|
fSScale2 = (fSCenter + fSCenter) * fSScale;
|
|
|
|
fTScale2 = (fTCenter + fTCenter) * fTScale;
|
|
|
|
fSScale3 = (1.0 - fSCenter) * fSScale + (1.0 - fSCenter) * fSScale;
|
|
|
|
fTScale3 = (1.0 - fTCenter) * fTScale + (1.0 - fTCenter) * fTScale;
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
// create the texture axis
|
|
|
|
|
|
|
|
if (orientation) {
|
2023-07-15 20:41:52 +02:00
|
|
|
VectorNormalize2(dir, info.axis[0]);
|
|
|
|
PerpendicularVector(info.axis[1], info.axis[0]);
|
|
|
|
RotatePointAroundVector(info.axis[2], info.axis[0], info.axis[1], orientation);
|
|
|
|
CrossProduct(info.axis[0], info.axis[2], info.axis[1]);
|
2023-05-01 00:08:56 +02:00
|
|
|
} else {
|
|
|
|
vec3_t angles;
|
|
|
|
vec3_t tmp;
|
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
VectorNormalize2(dir, info.axis[0]);
|
2023-05-01 00:08:56 +02:00
|
|
|
VectorCopy(dir, tmp);
|
|
|
|
vectoangles(tmp, angles);
|
2023-07-15 20:41:52 +02:00
|
|
|
AnglesToAxis(angles, info.axis);
|
|
|
|
VectorScale(info.axis[2], -1, info.axis[2]);
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// create the full polygon
|
|
|
|
for (i = 0; i < 3; i++) {
|
2023-07-15 20:41:52 +02:00
|
|
|
originalPoints[0][i] = origin[i] - fSScale2 * info.axis[1][i] - fTScale2 * info.axis[2][i];
|
2023-08-27 17:48:10 +02:00
|
|
|
originalPoints[1][i] = origin[i] + fSScale3 * info.axis[1][i] - fTScale2 * info.axis[2][i];
|
2023-07-15 20:41:52 +02:00
|
|
|
originalPoints[2][i] = origin[i] + fSScale3 * info.axis[1][i] + fTScale3 * info.axis[2][i];
|
2023-08-27 17:48:10 +02:00
|
|
|
originalPoints[3][i] = origin[i] - fSScale2 * info.axis[1][i] + fTScale3 * info.axis[2][i];
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// get the fragments
|
|
|
|
VectorScale(dir, -32, projection);
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
numFragments = CG_GetMarkFragments(4, originalPoints, projection, markPoints, markFragments, fRadiusSquared);
|
2023-07-15 20:41:52 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (dolighting) {
|
2023-07-15 20:41:52 +02:00
|
|
|
vec3_t vLight;
|
|
|
|
cgi.R_GetLightingForDecal(vLight, dir, origin);
|
|
|
|
|
|
|
|
info.colors[0] = (int)(red * vLight[0]);
|
|
|
|
info.colors[1] = (int)(green * vLight[1]);
|
|
|
|
info.colors[2] = (int)(blue * vLight[2]);
|
2024-09-04 21:41:16 +02:00
|
|
|
} else {
|
2023-07-15 20:41:52 +02:00
|
|
|
info.colors[0] = (int)(red * 255.0f);
|
|
|
|
info.colors[1] = (int)(green * 255.0f);
|
|
|
|
info.colors[2] = (int)(blue * 255.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fadein) {
|
|
|
|
info.colors[3] = 0;
|
|
|
|
} else {
|
|
|
|
info.colors[3] = (int)(alpha * 255.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
info.fSCenter = fSCenter;
|
|
|
|
info.fTCenter = fTCenter;
|
|
|
|
VectorCopy(origin, info.origin);
|
|
|
|
VectorAdd(origin, dir, v);
|
|
|
|
info.leafnum = cgi.CM_PointLeafnum(v);
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (temporary) {
|
2023-07-15 20:41:52 +02:00
|
|
|
polyVert_t verts[8];
|
|
|
|
|
|
|
|
for (i = 0, mf = markFragments; i < numFragments; i++, mf++) {
|
|
|
|
if (mf->numPoints > 8) {
|
|
|
|
mf->numPoints = 8;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (CG_ImpactMark_PerPolyCallback(markPoints, mf, verts, (void *)&info)) {
|
2023-07-15 20:41:52 +02:00
|
|
|
CG_AddFragmentToScene(mf->iIndex, markShader, mf->numPoints, verts);
|
|
|
|
}
|
|
|
|
}
|
2024-09-04 21:41:16 +02:00
|
|
|
} else {
|
2023-07-15 20:41:52 +02:00
|
|
|
CG_AssembleFinalMarks(
|
|
|
|
markPoints,
|
|
|
|
markFragments,
|
|
|
|
numFragments,
|
|
|
|
&CG_ImpactMark_PerPolyCallback,
|
|
|
|
&CG_ImpactMark_GetLeafCallback,
|
2024-09-04 21:41:16 +02:00
|
|
|
(void *)&info,
|
2023-07-15 20:41:52 +02:00
|
|
|
info.origin,
|
|
|
|
sqrt(fRadiusSquared),
|
|
|
|
markShader,
|
|
|
|
fadein,
|
|
|
|
alphaFade
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CG_AssembleFinalMarks(
|
2024-09-04 21:41:16 +02:00
|
|
|
vec3_t *markPoints,
|
|
|
|
markFragment_t *markFragments,
|
|
|
|
int numFragments,
|
|
|
|
qboolean (*PerPolyCallback)(const vec3_t *markPoints, markFragment_t *mf, polyVert_t *verts, void *pCustom),
|
|
|
|
int (*GetLeafCallback)(markFragment_t *mf, void *pCustom),
|
|
|
|
void *pCustom,
|
|
|
|
vec3_t pos,
|
|
|
|
float radius,
|
|
|
|
qhandle_t markShader,
|
|
|
|
qboolean fadein,
|
|
|
|
qboolean alphaFade
|
2023-07-15 20:41:52 +02:00
|
|
|
)
|
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
markObj_t *pMark;
|
|
|
|
markPoly_t *pPoly;
|
|
|
|
int iGroup, iFirstNewGroup;
|
|
|
|
int numFragsForMark;
|
|
|
|
int i;
|
|
|
|
markFragment_t *mf;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
iFirstNewGroup = 0;
|
2024-09-04 21:41:16 +02:00
|
|
|
do {
|
|
|
|
i = iFirstNewGroup;
|
|
|
|
mf = &markFragments[iFirstNewGroup];
|
|
|
|
iGroup = mf->iIndex;
|
2023-07-15 20:41:52 +02:00
|
|
|
numFragsForMark = 0;
|
|
|
|
|
|
|
|
for (; i < numFragments; i++, mf++) {
|
|
|
|
if (iGroup < 0 && mf->iIndex == iGroup) {
|
|
|
|
numFragsForMark++;
|
|
|
|
} else if (iGroup >= 0 && mf->iIndex >= 0) {
|
|
|
|
numFragsForMark++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pMark = CG_AllocMark(numFragsForMark);
|
|
|
|
if (!pMark) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
pMark->time = cg.time;
|
|
|
|
pMark->alphaFade = alphaFade;
|
2023-07-15 20:41:52 +02:00
|
|
|
pMark->markShader = markShader;
|
2024-09-04 21:41:16 +02:00
|
|
|
pMark->fadein = fadein;
|
2023-07-15 20:41:52 +02:00
|
|
|
VectorCopy(pos, pMark->pos);
|
|
|
|
pMark->radius = radius;
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
i = iFirstNewGroup;
|
|
|
|
mf = &markFragments[iFirstNewGroup];
|
2023-07-15 20:41:52 +02:00
|
|
|
iFirstNewGroup = 0;
|
|
|
|
|
|
|
|
if (mf->iIndex < 0) {
|
2024-10-28 20:17:07 +01:00
|
|
|
centity_t *cent;
|
|
|
|
vec3_t pos;
|
|
|
|
|
|
|
|
cent = &cg_entities[-mf->iIndex];
|
|
|
|
|
|
|
|
VectorSubtract(pMark->pos, cent->lerpOrigin, pos);
|
|
|
|
|
|
|
|
if (cent->lerpAngles[0] || cent->lerpAngles[1] || cent->lerpAngles[2]) {
|
|
|
|
vec3_t axis[3];
|
|
|
|
|
|
|
|
// Fixed in OPM
|
|
|
|
// Make the position completely local to the entity
|
2024-10-30 00:23:42 +01:00
|
|
|
AngleVectorsLeft(cent->lerpAngles, axis[0], axis[1], axis[2]);
|
2024-10-28 20:17:07 +01:00
|
|
|
MatrixTransformVectorRight(axis, pos, pMark->pos);
|
|
|
|
} else {
|
|
|
|
VectorCopy(pos, pMark->pos);
|
|
|
|
}
|
2023-07-15 20:41:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pPoly = pMark->markPolys;
|
|
|
|
for (; i < numFragments; i++, mf++) {
|
|
|
|
if (iGroup < 0 && mf->iIndex != iGroup) {
|
|
|
|
if (!iFirstNewGroup) {
|
|
|
|
iFirstNewGroup = i;
|
|
|
|
}
|
|
|
|
} else if (iGroup >= 0 && mf->iIndex < 0) {
|
|
|
|
if (!iFirstNewGroup) {
|
|
|
|
iFirstNewGroup = i;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mf->numPoints > 8) {
|
|
|
|
mf->numPoints = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PerPolyCallback(markPoints, mf, pPoly->verts, pCustom)) {
|
|
|
|
pPoly->numVerts = mf->numPoints;
|
2024-09-04 21:41:16 +02:00
|
|
|
pPoly->iIndex = mf->iIndex;
|
|
|
|
pPoly = pPoly->nextPoly;
|
2023-07-15 20:41:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pMark->leafnum = GetLeafCallback(mf, pCustom);
|
|
|
|
} while (iFirstNewGroup);
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
void CG_ImpactMarkSimple(
|
2023-07-10 21:09:56 +02:00
|
|
|
qhandle_t markShader,
|
|
|
|
const vec3_t origin,
|
|
|
|
const vec3_t dir,
|
|
|
|
float orientation,
|
|
|
|
float fRadius,
|
|
|
|
float red,
|
|
|
|
float green,
|
|
|
|
float blue,
|
|
|
|
float alpha,
|
|
|
|
qboolean alphaFade,
|
|
|
|
qboolean temporary,
|
|
|
|
qboolean dolighting,
|
|
|
|
qboolean fadein
|
2023-05-01 00:08:56 +02:00
|
|
|
)
|
|
|
|
{
|
2023-07-10 21:09:56 +02:00
|
|
|
if (cg_bMarksInitialized) {
|
|
|
|
CG_ImpactMark(
|
|
|
|
markShader,
|
|
|
|
origin,
|
|
|
|
dir,
|
|
|
|
orientation,
|
|
|
|
fRadius,
|
|
|
|
fRadius,
|
|
|
|
red,
|
|
|
|
green,
|
|
|
|
blue,
|
|
|
|
alpha,
|
|
|
|
alphaFade,
|
|
|
|
temporary,
|
|
|
|
dolighting,
|
|
|
|
fadein,
|
|
|
|
0.5f,
|
|
|
|
0.5f
|
|
|
|
);
|
|
|
|
}
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
CG_AddMarks
|
|
|
|
===============
|
|
|
|
*/
|
2023-07-05 21:24:23 +02:00
|
|
|
#define MARK_TOTAL_TIME 10000
|
|
|
|
#define MARK_FADE_TIME 1000
|
2016-03-27 11:49:47 +02:00
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
void CG_AddMarks(void)
|
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
int j;
|
|
|
|
int t;
|
|
|
|
int fade;
|
|
|
|
markObj_t *pMark;
|
|
|
|
markObj_t *pNext;
|
|
|
|
markPoly_t *pPoly;
|
|
|
|
polyVert_t *pVert;
|
|
|
|
polyVert_t tmpVerts[8];
|
|
|
|
int viewleafnum;
|
2023-07-12 23:53:30 +02:00
|
|
|
|
|
|
|
if (!cg_bMarksInitialized) {
|
|
|
|
return;
|
|
|
|
}
|
2023-05-01 00:08:56 +02:00
|
|
|
|
|
|
|
if (!cg_addMarks->integer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
viewleafnum = cgi.CM_PointLeafnum(cg.refdef.vieworg);
|
2023-07-12 23:53:30 +02:00
|
|
|
for (pMark = cg_activeMarkObjs.nextMark; pMark != &cg_activeMarkObjs; pMark = pNext) {
|
2023-05-01 00:08:56 +02:00
|
|
|
// grab next now, so if the local entity is freed we
|
|
|
|
// still have it
|
2023-07-12 23:53:30 +02:00
|
|
|
pNext = pMark->nextMark;
|
2023-05-01 00:08:56 +02:00
|
|
|
|
|
|
|
// see if it is time to completely remove it
|
2024-09-04 21:41:16 +02:00
|
|
|
if (pMark->alphaFade && cg.time > pMark->time + MARK_TOTAL_TIME) {
|
2023-07-12 23:53:30 +02:00
|
|
|
CG_FreeMarkObj(pMark);
|
2023-05-01 00:08:56 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
if (pMark->markPolys->iIndex < 0 && !CG_UpdateMarkPosition(pMark)) {
|
|
|
|
if (pMark->lastVisTime < cg.time - 3000) {
|
|
|
|
CG_FreeMarkObj(pMark);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cgi.CM_LeafInPVS(viewleafnum, pMark->leafnum)) {
|
2023-07-12 23:53:30 +02:00
|
|
|
continue;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
if (pMark->markPolys->iIndex < 0) {
|
|
|
|
vec3_t vWorldPos;
|
|
|
|
CG_FragmentPosToWorldPos(pMark->pos, vWorldPos);
|
|
|
|
if (CG_FrustumCullSphere(vWorldPos, pMark->radius)) {
|
|
|
|
continue;
|
|
|
|
}
|
2024-01-27 21:14:13 +01:00
|
|
|
} else {
|
|
|
|
if (CG_FrustumCullSphere(pMark->pos, pMark->radius)) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-07-15 20:41:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pMark->lastVisTime = cg.time;
|
|
|
|
if (pMark->fadein) {
|
|
|
|
fade = 255 * (cg.time - pMark->time) / MARK_FADE_TIME;
|
|
|
|
if (fade > 255) {
|
2024-09-04 21:41:16 +02:00
|
|
|
fade = 255;
|
2023-07-15 20:41:52 +02:00
|
|
|
pMark->fadein = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (pPoly = pMark->markPolys; pPoly; pPoly = pPoly->nextPoly) {
|
|
|
|
for (j = 0; j < pPoly->numVerts; j++) {
|
|
|
|
pPoly->verts[j].modulate[3] = fade;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pMark->alphaFade) {
|
|
|
|
t = pMark->time + MARK_TOTAL_TIME - cg.time;
|
|
|
|
if (t < MARK_FADE_TIME) {
|
|
|
|
fade = 255 * t / MARK_FADE_TIME;
|
|
|
|
for (pPoly = pMark->markPolys; pPoly; pPoly = pPoly->nextPoly) {
|
|
|
|
for (j = 0; j < pPoly->numVerts; j++) {
|
|
|
|
pPoly->verts[j].modulate[3] = fade;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
for (pPoly = pMark->markPolys; pPoly; pPoly = pPoly->nextPoly) {
|
|
|
|
if (pPoly->iIndex < 0) {
|
2023-07-15 20:41:52 +02:00
|
|
|
memcpy(tmpVerts, pPoly->verts, 24 * pPoly->numVerts);
|
|
|
|
pVert = tmpVerts;
|
|
|
|
} else {
|
|
|
|
pVert = pPoly->verts;
|
|
|
|
}
|
|
|
|
|
|
|
|
CG_AddFragmentToScene(pPoly->iIndex, pMark->markShader, pPoly->numVerts, pVert);
|
|
|
|
}
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
2023-07-12 23:53:30 +02:00
|
|
|
|
|
|
|
CG_AddTreadMarks();
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qboolean CG_CheckMakeMarkOnEntity(int iEntIndex)
|
|
|
|
{
|
2023-07-10 23:28:17 +02:00
|
|
|
if (iEntIndex == ENTITYNUM_WORLD) {
|
|
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iEntIndex == ENTITYNUM_NONE) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cg_entities[iEntIndex].currentState.solid != SOLID_BMODEL) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (cg_entities[iEntIndex].currentState.modelindex < 0
|
|
|
|
|| cg_entities[iEntIndex].currentState.modelindex > cgi.CM_NumInlineModels()) {
|
2023-07-10 23:28:17 +02:00
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qtrue;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CG_InitTestTreadMark()
|
|
|
|
{
|
2023-07-10 23:28:17 +02:00
|
|
|
cg_treadmark_test = cgi.Cvar_Get("cg_treadmark_test", "0", 0);
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2023-07-15 20:41:52 +02:00
|
|
|
int CG_StartTreadMark(int iReference, qhandle_t treadShader, const vec3_t vStartPos, float fWidth, float fAlpha)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
int i;
|
|
|
|
int iTreadNum;
|
|
|
|
treadMark_t *pTread;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
if (!cg_bMarksInitialized) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cg_addMarks->integer) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
iTreadNum = -1;
|
|
|
|
for (i = 0; i < MAX_TREAD_MARKS; i++) {
|
|
|
|
if (!cg_treadMarks[i].iState) {
|
|
|
|
iTreadNum = i;
|
2024-09-04 21:41:16 +02:00
|
|
|
pTread = &cg_treadMarks[i];
|
2023-07-15 20:41:52 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iTreadNum == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(pTread, 0, sizeof(*pTread));
|
2024-09-04 21:41:16 +02:00
|
|
|
pTread->iState = 1;
|
2023-07-15 20:41:52 +02:00
|
|
|
pTread->iReferenceNumber = iReference;
|
2024-09-04 21:41:16 +02:00
|
|
|
pTread->iLastTime = cg.time;
|
|
|
|
pTread->hTreadShader = treadShader;
|
|
|
|
pTread->fWidth = fWidth * 0.5;
|
2023-07-15 20:41:52 +02:00
|
|
|
VectorCopy(vStartPos, pTread->vMidPos);
|
|
|
|
VectorCopy(vStartPos, pTread->vEndPos);
|
|
|
|
|
|
|
|
if (fAlpha < 0.0) {
|
|
|
|
pTread->fMidAlpha = 255.0;
|
|
|
|
} else {
|
|
|
|
pTread->fMidAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTread->fEndAlpha = pTread->fMidAlpha;
|
|
|
|
return iTreadNum;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
qboolean
|
|
|
|
CG_MakeTreadMarkDecal_PerPolyCallback(const vec3_t *markPoints, markFragment_t *mf, polyVert_t *verts, void *pCustom)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
int j;
|
|
|
|
float fDist;
|
|
|
|
float fSideDist;
|
|
|
|
float fSideAlpha;
|
|
|
|
float fCenterAlpha;
|
|
|
|
float fFrac;
|
2024-09-06 21:57:53 +02:00
|
|
|
float fTex, fAlpha;
|
2024-09-04 21:41:16 +02:00
|
|
|
polyVert_t *v;
|
|
|
|
cg_treadmarkinfo_t *pInfo;
|
2024-09-04 20:29:29 +02:00
|
|
|
|
2024-09-05 23:54:01 +02:00
|
|
|
pInfo = (cg_treadmarkinfo_t *)pCustom;
|
|
|
|
|
|
|
|
if (mf->iIndex < 0) {
|
|
|
|
vec3_t vWorldPos;
|
|
|
|
|
|
|
|
if (!CG_GetMarkInlineModelOrientation(mf->iIndex)) {
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
|
|
|
v = &verts[j];
|
|
|
|
VectorCopy(markPoints[mf->firstPoint + j], v->xyz);
|
|
|
|
v->modulate[0] = pInfo->colors[0];
|
|
|
|
v->modulate[1] = pInfo->colors[1];
|
|
|
|
v->modulate[2] = pInfo->colors[2];
|
|
|
|
v->modulate[3] = pInfo->colors[3];
|
|
|
|
|
|
|
|
CG_FragmentPosToWorldPos(v->xyz, vWorldPos);
|
2024-09-06 21:57:53 +02:00
|
|
|
fSideDist = DotProduct(pInfo->vRight, vWorldPos) - pInfo->fRightCenterDist;
|
|
|
|
v->st[0] = (fSideDist + pInfo->pTread->fWidth) * pInfo->fOODoubleWidth;
|
2024-09-05 23:54:01 +02:00
|
|
|
|
|
|
|
fSideAlpha = fSideDist * pInfo->fOOWidth;
|
|
|
|
fDist = DotProduct(vWorldPos, pInfo->vDirection);
|
|
|
|
if (fSideAlpha < 0) {
|
|
|
|
fSideAlpha = -fSideAlpha;
|
|
|
|
v->st[1] = pInfo->fStartTex + ((fDist - pInfo->fLeftStartDist) * pInfo->fLeftTexScale * fSideAlpha)
|
|
|
|
+ ((fDist - pInfo->fStartDist) * pInfo->fCenterTexScale * (1.0 - fSideAlpha));
|
|
|
|
v->modulate[3] = pInfo->fStartAlpha
|
|
|
|
+ (1.0 - fSideAlpha)
|
|
|
|
* ((fDist - pInfo->fStartDist) * pInfo->fCenterTexScale * pInfo->fCenterAlphaScale)
|
|
|
|
+ (fDist - pInfo->fLeftStartDist) * pInfo->fLeftAlphaScale * fSideAlpha;
|
|
|
|
} else {
|
|
|
|
v->st[1] = pInfo->fStartTex + ((fDist - pInfo->fRightStartDist) * pInfo->fRightTexScale * fSideAlpha)
|
|
|
|
+ ((fDist - pInfo->fStartDist) * pInfo->fCenterTexScale * (1.0 - fSideAlpha));
|
|
|
|
v->modulate[3] = pInfo->fStartAlpha
|
|
|
|
+ (1.0 - fSideAlpha)
|
|
|
|
* ((fDist - pInfo->fStartDist) * pInfo->fCenterTexScale * pInfo->fCenterAlphaScale)
|
|
|
|
+ (fDist - pInfo->fRightStartDist) * pInfo->fRightAlphaScale * fSideAlpha;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
|
|
|
v = &verts[j];
|
|
|
|
VectorCopy(markPoints[mf->firstPoint + j], v->xyz);
|
|
|
|
v->modulate[0] = pInfo->colors[0];
|
|
|
|
v->modulate[1] = pInfo->colors[1];
|
|
|
|
v->modulate[2] = pInfo->colors[2];
|
|
|
|
v->modulate[3] = pInfo->colors[3];
|
|
|
|
|
2024-09-06 21:57:53 +02:00
|
|
|
fSideDist = DotProduct(pInfo->vRight, v->xyz) - pInfo->fRightCenterDist;
|
|
|
|
v->st[0] = (fSideDist + pInfo->pTread->fWidth) * pInfo->fOODoubleWidth;
|
2024-09-05 23:54:01 +02:00
|
|
|
|
|
|
|
fSideAlpha = fSideDist * pInfo->fOOWidth;
|
|
|
|
fDist = DotProduct(v->xyz, pInfo->vDirection);
|
|
|
|
if (fSideAlpha < 0) {
|
|
|
|
fSideAlpha = -fSideAlpha;
|
2024-09-06 21:57:53 +02:00
|
|
|
fTex = (fDist - pInfo->fLeftStartDist) * pInfo->fLeftTexScale;
|
|
|
|
fAlpha = (fDist - pInfo->fLeftStartDist) * pInfo->fLeftAlphaScale;
|
2024-09-05 23:54:01 +02:00
|
|
|
} else {
|
2024-09-06 21:57:53 +02:00
|
|
|
fTex = (fDist - pInfo->fRightStartDist) * pInfo->fRightTexScale;
|
|
|
|
fAlpha = (fDist - pInfo->fRightStartDist) * pInfo->fRightAlphaScale;
|
2024-09-05 23:54:01 +02:00
|
|
|
}
|
2024-09-06 21:57:53 +02:00
|
|
|
|
|
|
|
v->st[1] = pInfo->fStartTex + (fTex * fSideAlpha)
|
|
|
|
+ (fDist - pInfo->fStartDist) * pInfo->fCenterTexScale * (1.0 - fSideAlpha);
|
|
|
|
v->modulate[3] = pInfo->fStartAlpha
|
|
|
|
+ ((1.0 - fSideAlpha) * ((fDist - pInfo->fStartDist) * pInfo->fCenterAlphaScale))
|
|
|
|
+ (fAlpha * fSideAlpha);
|
2024-09-05 23:54:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return qtrue;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:23 +02:00
|
|
|
int CG_MakeTreadMarkDecal_GetLeafCallback(markFragment_t *mf, void *pCustom)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
return ((cg_treadmarkinfo_t *)pCustom)->leafnum;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2023-07-05 21:24:23 +02:00
|
|
|
void CG_MakeTreadMarkDecal(treadMark_t *pTread, qboolean bStartSegment, qboolean bTemporary)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
int i;
|
|
|
|
int numFragments;
|
|
|
|
vec3_t originalPoints[4];
|
|
|
|
vec3_t markPoints[MAX_MARK_POLYVERTS];
|
|
|
|
vec3_t projection;
|
|
|
|
markFragment_t markFragments[MAX_MARK_FRAGMENTS];
|
|
|
|
markFragment_t *mf;
|
2024-09-04 20:29:29 +02:00
|
|
|
cg_treadmarkinfo_t info;
|
2024-09-04 21:41:16 +02:00
|
|
|
float fEndAlpha, fEndTex;
|
|
|
|
float fDist;
|
|
|
|
vec3_t vStartCenter;
|
|
|
|
vec3_t vEndCenter;
|
|
|
|
vec3_t vDelta;
|
|
|
|
vec3_t origin;
|
|
|
|
float fRadiusSquared;
|
2024-09-04 20:29:29 +02:00
|
|
|
|
2024-09-05 23:54:01 +02:00
|
|
|
if (bStartSegment) {
|
|
|
|
VectorCopy(pTread->vStartVerts[1], originalPoints[0]);
|
|
|
|
VectorCopy(pTread->vStartVerts[0], originalPoints[1]);
|
|
|
|
VectorCopy(pTread->vMidVerts[0], originalPoints[2]);
|
|
|
|
VectorCopy(pTread->vMidVerts[1], originalPoints[3]);
|
|
|
|
|
|
|
|
info.fStartAlpha = pTread->fStartAlpha;
|
|
|
|
fEndAlpha = pTread->fMidAlpha;
|
|
|
|
info.fStartTex = pTread->fStartTexCoord;
|
|
|
|
fEndTex = pTread->fMidTexCoord;
|
|
|
|
|
|
|
|
// Calculate the start center
|
2024-09-06 21:57:53 +02:00
|
|
|
VectorAdd(originalPoints[1], originalPoints[0], vStartCenter);
|
|
|
|
VectorScale(vStartCenter, 0.5, vStartCenter);
|
2024-09-05 23:54:01 +02:00
|
|
|
VectorCopy(pTread->vMidPos, vEndCenter);
|
|
|
|
VectorCopy(pTread->vStartDir, info.vDirection);
|
|
|
|
} else {
|
|
|
|
VectorCopy(pTread->vMidVerts[1], originalPoints[0]);
|
|
|
|
VectorCopy(pTread->vMidVerts[0], originalPoints[1]);
|
|
|
|
VectorCopy(pTread->vEndVerts[0], originalPoints[2]);
|
|
|
|
VectorCopy(pTread->vEndVerts[1], originalPoints[3]);
|
|
|
|
|
|
|
|
info.fStartAlpha = pTread->fMidAlpha;
|
|
|
|
fEndAlpha = pTread->fEndAlpha;
|
|
|
|
info.fStartTex = pTread->fMidTexCoord;
|
|
|
|
fEndTex = pTread->fEndTexCoord;
|
|
|
|
|
|
|
|
VectorCopy(pTread->vMidPos, vStartCenter);
|
|
|
|
VectorCopy(pTread->vEndPos, vEndCenter);
|
|
|
|
|
|
|
|
// Calculate the direction
|
|
|
|
VectorSubtract(vEndCenter, vStartCenter, info.vDirection);
|
|
|
|
VectorNormalizeFast(info.vDirection);
|
|
|
|
}
|
|
|
|
|
|
|
|
CrossProduct(vec_upwards, info.vDirection, info.vRight);
|
|
|
|
info.fRightCenterDist = DotProduct(vEndCenter, info.vRight);
|
|
|
|
info.fOODoubleWidth = 0.5 / pTread->fWidth;
|
|
|
|
info.fOOWidth = info.fOODoubleWidth * 2;
|
|
|
|
info.fStartDist = DotProduct(info.vDirection, vStartCenter);
|
|
|
|
info.fRightStartDist = DotProduct(info.vDirection, originalPoints[1]);
|
|
|
|
info.fLeftStartDist = DotProduct(info.vDirection, originalPoints[0]);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Center
|
|
|
|
//
|
|
|
|
|
|
|
|
VectorSubtract(vEndCenter, vStartCenter, vDelta);
|
|
|
|
fDist = VectorLength(vDelta);
|
|
|
|
|
2024-09-06 21:57:53 +02:00
|
|
|
fRadiusSquared = (Square(fDist) + Square(pTread->fWidth)) * 0.25;
|
2024-09-05 23:54:01 +02:00
|
|
|
|
|
|
|
info.fCenterTexScale = (fEndTex - info.fStartTex) / fDist;
|
|
|
|
info.fCenterAlphaScale = (fEndAlpha - info.fStartAlpha) / fDist;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Right
|
|
|
|
//
|
|
|
|
|
|
|
|
VectorSubtract(originalPoints[2], originalPoints[1], vDelta);
|
|
|
|
fDist = VectorLength(vDelta);
|
|
|
|
|
|
|
|
info.fRightTexScale = (fEndTex - info.fStartTex) / fDist;
|
|
|
|
info.fRightAlphaScale = (fEndAlpha - info.fStartAlpha) / fDist;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Left
|
|
|
|
//
|
|
|
|
|
|
|
|
VectorSubtract(originalPoints[3], originalPoints[1], vDelta);
|
|
|
|
fDist = VectorLength(vDelta);
|
|
|
|
|
|
|
|
info.fLeftTexScale = (fEndTex - info.fStartTex) / fDist;
|
|
|
|
info.fLeftAlphaScale = (fEndAlpha - info.fStartAlpha) / fDist;
|
|
|
|
|
2024-09-06 21:57:53 +02:00
|
|
|
VectorSet(projection, 0, 0, -32);
|
2024-09-05 23:54:01 +02:00
|
|
|
info.colors[0] = info.colors[1] = info.colors[2] = info.colors[3] = -1;
|
|
|
|
|
|
|
|
numFragments = CG_GetMarkFragments(4, originalPoints, projection, markPoints, markFragments, fRadiusSquared);
|
2024-09-06 21:57:53 +02:00
|
|
|
VectorAdd(vStartCenter, vEndCenter, origin);
|
|
|
|
VectorScale(origin, 0.5, origin);
|
2024-09-05 23:54:01 +02:00
|
|
|
info.leafnum = cgi.CM_PointLeafnum(origin);
|
|
|
|
info.pTread = pTread;
|
|
|
|
|
|
|
|
if (bTemporary) {
|
|
|
|
for (i = 0; i < numFragments; i++) {
|
|
|
|
polyVert_t verts[8];
|
|
|
|
|
|
|
|
mf = &markFragments[i];
|
|
|
|
if (mf->numPoints > 8) {
|
|
|
|
mf->numPoints = 8;
|
|
|
|
}
|
|
|
|
if (CG_MakeTreadMarkDecal_PerPolyCallback(markPoints, mf, verts, &info)) {
|
2024-09-08 17:53:35 +02:00
|
|
|
CG_AddFragmentToScene(mf->iIndex, pTread->hTreadShader, mf->numPoints, verts);
|
2024-09-05 23:54:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CG_AssembleFinalMarks(
|
|
|
|
markPoints,
|
|
|
|
markFragments,
|
|
|
|
numFragments,
|
|
|
|
&CG_MakeTreadMarkDecal_PerPolyCallback,
|
|
|
|
&CG_MakeTreadMarkDecal_GetLeafCallback,
|
|
|
|
&info,
|
|
|
|
origin,
|
|
|
|
sqrt(fRadiusSquared),
|
|
|
|
pTread->hTreadShader,
|
|
|
|
qfalse,
|
|
|
|
qfalse
|
|
|
|
);
|
|
|
|
}
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
2024-09-05 00:54:39 +02:00
|
|
|
int CG_UpdateTreadMark(int iReference, const vec3_t vNewPos, float fAlpha)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
int i;
|
|
|
|
int iTreadNum;
|
|
|
|
float fDist;
|
|
|
|
float fSplitLength;
|
|
|
|
float fNewLength;
|
|
|
|
float fTmp;
|
|
|
|
qboolean bDoSegmentation;
|
2024-09-06 21:57:53 +02:00
|
|
|
vec3_t vDelta, vDeltaNorm;
|
2024-09-04 21:41:16 +02:00
|
|
|
vec3_t vDir, vMidDir;
|
|
|
|
vec3_t vRight;
|
|
|
|
treadMark_t *pTread;
|
2024-09-04 20:29:29 +02:00
|
|
|
|
2024-09-06 21:57:53 +02:00
|
|
|
iTreadNum = -1;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_TREAD_MARKS; i++) {
|
|
|
|
if (cg_treadMarks[i].iReferenceNumber == iReference) {
|
|
|
|
iTreadNum = i;
|
|
|
|
pTread = &cg_treadMarks[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iTreadNum == -1 || !pTread->iState) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTread->iLastTime = cg.time;
|
|
|
|
|
|
|
|
if (VectorCompare(pTread->vEndPos, vNewPos)) {
|
|
|
|
if (fAlpha >= 0) {
|
|
|
|
pTread->fEndAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorSubtract(vNewPos, pTread->vMidPos, vDelta);
|
|
|
|
fDist = VectorNormalize2(vDelta, vDeltaNorm);
|
|
|
|
if (pTread->iState == 1) {
|
|
|
|
fSplitLength = 0;
|
|
|
|
bDoSegmentation = qfalse;
|
|
|
|
} else {
|
|
|
|
VectorAdd(vDeltaNorm, pTread->vStartDir, vMidDir);
|
|
|
|
VectorScale(vMidDir, 0.5, vMidDir);
|
|
|
|
VectorNormalizeFast(vMidDir);
|
|
|
|
|
|
|
|
fTmp = DotProduct(vMidDir, vDeltaNorm);
|
|
|
|
fSplitLength = pTread->fWidth / fTmp;
|
|
|
|
|
|
|
|
if (fTmp < -0.5) {
|
|
|
|
fTmp = fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp * (fTmp * (fTmp * -337.31875783205 + -1237.54375255107) + -1802.11467325687)
|
|
|
|
+ -1303.19904613494)
|
|
|
|
+ -471.347871690988)
|
|
|
|
+ -70.0883838161826);
|
|
|
|
} else if (fTmp > 0.5) {
|
|
|
|
fTmp = fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp * (fTmp * (fTmp * -1507.55394345521 + 6580.58002318442) + -11860.0735285953)
|
|
|
|
+ 11290.7510782536)
|
|
|
|
+ -5986.89654545347)
|
|
|
|
+ 1675.66417006387)
|
|
|
|
+ -192.426950291139;
|
|
|
|
} else {
|
|
|
|
fTmp = fTmp * (fTmp * -0.531387674508458 + -2.11e-14) + 1.00086138065435;
|
|
|
|
}
|
|
|
|
|
|
|
|
fNewLength = fSplitLength * fTmp;
|
|
|
|
if (fNewLength + 24 > fDist) {
|
|
|
|
bDoSegmentation = qfalse;
|
|
|
|
} else if (fSplitLength < -1) {
|
|
|
|
fSplitLength = -fSplitLength;
|
|
|
|
bDoSegmentation = qtrue;
|
|
|
|
} else if (fDist > 256) {
|
|
|
|
bDoSegmentation = qtrue;
|
|
|
|
} else {
|
|
|
|
CrossProduct(vec_upwards, pTread->vStartDir, vRight);
|
|
|
|
VectorNormalizeFast(vRight);
|
|
|
|
bDoSegmentation = fabs(DotProduct(vRight, vDelta)) > 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pTread->iState == 2 && bDoSegmentation) {
|
|
|
|
VectorCopy(pTread->vMidVerts[0], pTread->vStartVerts[0]);
|
|
|
|
VectorCopy(pTread->vMidVerts[1], pTread->vStartVerts[1]);
|
|
|
|
pTread->fMidTexCoord = pTread->fEndTexCoord;
|
|
|
|
pTread->fMidAlpha = pTread->fEndAlpha;
|
|
|
|
VectorCopy(vNewPos, pTread->vEndPos);
|
|
|
|
|
|
|
|
if (fAlpha >= 0) {
|
|
|
|
pTread->fEndAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTread->iState = 3;
|
|
|
|
} else if (pTread->iState == 0 || pTread->iState == 1) {
|
|
|
|
VectorCopy(vNewPos, pTread->vEndPos);
|
|
|
|
|
|
|
|
if (fAlpha >= 0) {
|
|
|
|
pTread->fEndAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CrossProduct(vec_upwards, vDeltaNorm, vDir);
|
|
|
|
VectorMA(pTread->vMidPos, pTread->fWidth, vDir, pTread->vMidVerts[0]);
|
|
|
|
VectorMA(pTread->vMidPos, 0.0 - pTread->fWidth, vDir, pTread->vMidVerts[1]);
|
|
|
|
VectorMA(pTread->vEndPos, pTread->fWidth, vDir, pTread->vEndVerts[0]);
|
|
|
|
VectorMA(pTread->vEndPos, 0.0 - pTread->fWidth, vDir, pTread->vEndVerts[1]);
|
|
|
|
|
|
|
|
pTread->fEndTexCoord = fDist / 32.0;
|
|
|
|
|
|
|
|
if (pTread->iState == 1 && fDist > 8) {
|
|
|
|
VectorCopy(vDelta, pTread->vStartDir);
|
|
|
|
pTread->iState = 2;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
if (bDoSegmentation) {
|
|
|
|
CG_MakeTreadMarkDecal(pTread, qtrue, qfalse);
|
|
|
|
|
|
|
|
VectorCopy(pTread->vMidVerts[0], pTread->vStartVerts[0]);
|
|
|
|
VectorCopy(pTread->vMidVerts[1], pTread->vStartVerts[1]);
|
|
|
|
VectorSubtract(pTread->vEndPos, pTread->vMidPos, pTread->vStartDir);
|
|
|
|
VectorNormalizeFast(pTread->vStartDir);
|
|
|
|
VectorCopy(pTread->vEndPos, pTread->vMidPos);
|
|
|
|
pTread->fMidTexCoord = pTread->fEndTexCoord;
|
|
|
|
pTread->fMidAlpha = pTread->fEndAlpha;
|
|
|
|
|
|
|
|
if (pTread->fStartTexCoord >= 1.0) {
|
|
|
|
pTread->fMidTexCoord -= (int)pTread->fStartTexCoord;
|
|
|
|
pTread->fStartTexCoord -= (int)pTread->fStartTexCoord;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy(vNewPos, pTread->vEndPos);
|
|
|
|
pTread->fEndTexCoord = pTread->fMidTexCoord + fDist / 32.0;
|
|
|
|
|
|
|
|
if (fAlpha >= 0) {
|
|
|
|
pTread->fEndAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CrossProduct(vec_upwards, vMidDir, vRight);
|
|
|
|
VectorNormalizeFast(vRight);
|
|
|
|
VectorMA(pTread->vMidPos, fSplitLength, vRight, pTread->vMidVerts[0]);
|
|
|
|
VectorMA(pTread->vMidPos, 0.0 - fSplitLength, vRight, pTread->vMidVerts[1]);
|
|
|
|
CrossProduct(vec_upwards, vDeltaNorm, vRight);
|
|
|
|
VectorNormalizeFast(vRight);
|
|
|
|
VectorMA(pTread->vEndPos, pTread->fWidth, vRight, pTread->vEndVerts[0]);
|
|
|
|
VectorMA(pTread->vEndPos, 0.0 - pTread->fWidth, vRight, pTread->vEndVerts[1]);
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
return 0;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
void CG_AddTreadMarks()
|
|
|
|
{
|
2024-09-06 21:57:53 +02:00
|
|
|
treadMark_t *pTread;
|
|
|
|
trace_t trace;
|
|
|
|
vec3_t vPos, vEnd;
|
|
|
|
int i;
|
2023-07-15 20:41:52 +02:00
|
|
|
|
|
|
|
if (cg_treadmark_test->integer) {
|
|
|
|
VectorCopy(cg.predicted_player_state.origin, vPos);
|
|
|
|
VectorCopy(vPos, vEnd);
|
|
|
|
vPos[2] += 32.0f;
|
|
|
|
vEnd[2] -= 128.0f;
|
|
|
|
|
|
|
|
CG_Trace(
|
|
|
|
&trace,
|
|
|
|
vPos,
|
|
|
|
vec3_origin,
|
|
|
|
vec3_origin,
|
|
|
|
vEnd,
|
|
|
|
cg.snap->ps.clientNum,
|
|
|
|
MASK_SHOT,
|
|
|
|
qfalse,
|
|
|
|
qtrue,
|
|
|
|
"CG_AddTreadMarks test"
|
|
|
|
);
|
|
|
|
|
2024-09-06 21:57:53 +02:00
|
|
|
if (trace.fraction < 1.0 && CG_UpdateTreadMark(1, trace.endpos, 1.0) == -1) {
|
2023-07-15 20:41:52 +02:00
|
|
|
qhandle_t shader = cgi.R_RegisterShader("testtread");
|
|
|
|
|
2024-09-06 21:57:53 +02:00
|
|
|
CG_StartTreadMark(1, shader, trace.endpos, cg_treadmark_test->integer, 1.0);
|
2023-07-15 20:41:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_TREAD_MARKS; i++) {
|
2024-09-06 21:57:53 +02:00
|
|
|
pTread = &cg_treadMarks[i];
|
|
|
|
|
|
|
|
if (!pTread->iState) {
|
2023-07-15 20:41:52 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-09-06 21:57:53 +02:00
|
|
|
if (cg.time - pTread->iLastTime > 500) {
|
|
|
|
if (pTread->iState == 3) {
|
|
|
|
CG_MakeTreadMarkDecal(pTread, qtrue, qfalse);
|
|
|
|
}
|
|
|
|
CG_MakeTreadMarkDecal(pTread, qfalse, qfalse);
|
|
|
|
pTread->iState = 0;
|
2024-09-04 21:41:16 +02:00
|
|
|
} else {
|
2024-09-06 21:57:53 +02:00
|
|
|
if (pTread->iState == 3) {
|
|
|
|
CG_MakeTreadMarkDecal(pTread, qtrue, qtrue);
|
|
|
|
}
|
|
|
|
CG_MakeTreadMarkDecal(pTread, qfalse, qtrue);
|
2023-07-15 20:41:52 +02:00
|
|
|
}
|
|
|
|
}
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CG_PermanentMark(
|
2024-09-04 21:41:16 +02:00
|
|
|
vec3_t origin,
|
|
|
|
vec3_t dir,
|
2023-07-05 21:24:23 +02:00
|
|
|
float orientation,
|
|
|
|
float fSScale,
|
|
|
|
float fTScale,
|
|
|
|
float red,
|
|
|
|
float green,
|
|
|
|
float blue,
|
|
|
|
float alpha,
|
|
|
|
qboolean dolighting,
|
|
|
|
float fSCenter,
|
|
|
|
float fTCenter,
|
|
|
|
markFragment_t *pMarkFragments,
|
|
|
|
void *pVoidPolyVerts
|
2023-05-01 00:08:56 +02:00
|
|
|
)
|
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
byte colors[4];
|
|
|
|
int i, j;
|
|
|
|
int numFragments;
|
|
|
|
float fSScale2, fTScale2;
|
|
|
|
float fSScale3, fTScale3;
|
|
|
|
float texCoordScaleS, texCoordScaleT;
|
|
|
|
vec3_t originalPoints[4];
|
|
|
|
vec3_t markPoints[MAX_MARK_POLYVERTS];
|
|
|
|
vec3_t projection;
|
|
|
|
vec3_t vLight;
|
|
|
|
vec3_t vTmp;
|
|
|
|
vec3_t axis[3];
|
|
|
|
markFragment_t *mf;
|
|
|
|
polyVert_t *pPolyVerts;
|
|
|
|
polyVert_t *v;
|
|
|
|
clipHandle_t cmodel;
|
|
|
|
trace_t trace;
|
|
|
|
float fRadiusSquared;
|
|
|
|
|
|
|
|
pPolyVerts = (polyVert_t *)pVoidPolyVerts;
|
|
|
|
|
|
|
|
VectorMA(origin, -2048, dir, vTmp);
|
|
|
|
VectorAdd(origin, dir, origin);
|
|
|
|
CG_Trace(
|
|
|
|
&trace, origin, vec3_origin, vec3_origin, vTmp, ENTITYNUM_NONE, MASK_MARK, qfalse, qtrue, "CG_PermanentMark"
|
|
|
|
);
|
2024-09-04 20:29:29 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
if (trace.fraction == 1) {
|
|
|
|
return 0;
|
|
|
|
}
|
2024-09-04 20:29:29 +02:00
|
|
|
|
2024-09-04 21:41:16 +02:00
|
|
|
VectorCopy(trace.endpos, origin);
|
|
|
|
VectorCopy(trace.plane.normal, dir);
|
|
|
|
|
|
|
|
if (!fSScale) {
|
|
|
|
fSScale = 1.0;
|
|
|
|
}
|
|
|
|
if (!fTScale) {
|
|
|
|
fTScale = 1.0;
|
|
|
|
}
|
|
|
|
texCoordScaleT = 0.5 / fTScale;
|
|
|
|
texCoordScaleS = 0.5 / fSScale;
|
|
|
|
if (fSCenter < 0.0 || fSCenter > 1.0) {
|
|
|
|
fSCenter = 0.5;
|
|
|
|
}
|
|
|
|
if (fTCenter < 0.0 || fTCenter > 1.0) {
|
|
|
|
fTCenter = 0.5;
|
|
|
|
}
|
|
|
|
fRadiusSquared = fSScale * fSScale;
|
|
|
|
fRadiusSquared = fTScale * fTScale + fRadiusSquared;
|
|
|
|
fSScale2 = (fSScale * (1.0 - fSCenter)) * 2;
|
|
|
|
fTScale2 = (fTScale * (1.0 - fTCenter)) * 2;
|
|
|
|
fSScale3 = fSCenter * 2 * fSScale;
|
|
|
|
fTScale3 = fTCenter * 2 * fTScale;
|
|
|
|
|
|
|
|
VectorNormalize2(dir, axis[0]);
|
|
|
|
PerpendicularVector(axis[1], axis[0]);
|
|
|
|
RotatePointAroundVector(axis[2], axis[0], axis[1], orientation);
|
|
|
|
CrossProduct(axis[0], axis[2], axis[1]);
|
|
|
|
|
|
|
|
// create the full polygon
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
originalPoints[0][i] = origin[i] - fSScale3 * axis[1][i] - fTScale3 * axis[2][i];
|
|
|
|
originalPoints[1][i] = origin[i] + fSScale2 * axis[1][i] - fTScale3 * axis[2][i];
|
|
|
|
originalPoints[2][i] = origin[i] + fSScale2 * axis[1][i] + fTScale2 * axis[2][i];
|
|
|
|
originalPoints[3][i] = origin[i] - fSScale3 * axis[1][i] + fTScale2 * axis[2][i];
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorScale(dir, -32, projection);
|
|
|
|
numFragments = CG_GetMarkFragments(
|
|
|
|
ARRAY_LEN(originalPoints), originalPoints, projection, markPoints, pMarkFragments, fRadiusSquared
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!dolighting) {
|
|
|
|
colors[0] = (int)(red * 255.0f);
|
|
|
|
colors[1] = (int)(green * 255.0f);
|
|
|
|
colors[2] = (int)(blue * 255.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
colors[3] = (int)(alpha * 255.0f);
|
|
|
|
|
|
|
|
for (i = 0, mf = pMarkFragments; i < numFragments; i++, mf++) {
|
|
|
|
vec3_t vWorldPos;
|
|
|
|
vec3_t delta;
|
|
|
|
|
|
|
|
if (mf->numPoints > 8) {
|
|
|
|
mf->numPoints = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mf->iIndex >= 0) {
|
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
2024-09-04 21:51:42 +02:00
|
|
|
v = &pPolyVerts[mf->firstPoint + j];
|
2024-09-04 21:41:16 +02:00
|
|
|
VectorCopy(markPoints[mf->firstPoint + j], v->xyz);
|
|
|
|
|
|
|
|
if (dolighting) {
|
2024-09-04 21:51:42 +02:00
|
|
|
cgi.R_GetLightingForDecal(vLight, dir, v->xyz);
|
2024-09-04 21:41:16 +02:00
|
|
|
|
|
|
|
colors[0] = (int)(red * vLight[0]);
|
|
|
|
colors[1] = (int)(green * vLight[1]);
|
|
|
|
colors[2] = (int)(blue * vLight[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
v->modulate[0] = colors[0];
|
|
|
|
v->modulate[1] = colors[1];
|
|
|
|
v->modulate[2] = colors[2];
|
|
|
|
v->modulate[3] = colors[3];
|
|
|
|
|
|
|
|
VectorSubtract(v->xyz, origin, delta);
|
|
|
|
v->st[0] = fSCenter + DotProduct(delta, axis[1]) * texCoordScaleS;
|
|
|
|
v->st[1] = fTCenter + DotProduct(delta, axis[2]) * texCoordScaleT;
|
|
|
|
}
|
|
|
|
} else if (CG_GetMarkInlineModelOrientation(mf->iIndex)) {
|
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
2024-09-04 21:51:42 +02:00
|
|
|
v = &pPolyVerts[mf->firstPoint + j];
|
2024-09-04 21:41:16 +02:00
|
|
|
VectorCopy(markPoints[mf->firstPoint + j], v->xyz);
|
|
|
|
|
|
|
|
if (dolighting) {
|
|
|
|
CG_FragmentPosToWorldPos(v->xyz, vWorldPos);
|
2024-09-04 21:51:42 +02:00
|
|
|
cgi.R_GetLightingForDecal(vLight, dir, v->xyz);
|
2024-09-04 21:41:16 +02:00
|
|
|
|
|
|
|
colors[0] = (int)(red * vLight[0]);
|
|
|
|
colors[1] = (int)(green * vLight[1]);
|
|
|
|
colors[2] = (int)(blue * vLight[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
v->modulate[0] = colors[0];
|
|
|
|
v->modulate[1] = colors[1];
|
|
|
|
v->modulate[2] = colors[2];
|
|
|
|
v->modulate[3] = colors[3];
|
|
|
|
|
|
|
|
VectorSubtract(v->xyz, origin, delta);
|
|
|
|
v->st[0] = fSCenter + DotProduct(delta, axis[1]) * texCoordScaleS;
|
|
|
|
v->st[1] = fTCenter + DotProduct(delta, axis[2]) * texCoordScaleT;
|
|
|
|
}
|
|
|
|
|
2024-09-05 00:54:39 +02:00
|
|
|
mf->iIndex = -cgi.CM_InlineModel(cg_entities[-mf->iIndex].currentState.modelindex);
|
2024-09-04 21:41:16 +02:00
|
|
|
} else {
|
|
|
|
mf->numPoints = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return numFragments;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CG_PermanentTreadMarkDecal(
|
2023-07-05 21:24:23 +02:00
|
|
|
treadMark_t *pTread,
|
|
|
|
qboolean bStartSegment,
|
|
|
|
qboolean dolighting,
|
|
|
|
markFragment_t *pMarkFragments,
|
|
|
|
void *pVoidPolyVerts
|
2023-05-01 00:08:56 +02:00
|
|
|
)
|
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
byte colors[4];
|
|
|
|
int i, j;
|
|
|
|
int numFragments;
|
|
|
|
vec3_t originalPoints[4];
|
|
|
|
vec3_t markPoints[MAX_MARK_POLYVERTS];
|
|
|
|
vec3_t projection;
|
|
|
|
vec3_t vLight;
|
|
|
|
markFragment_t *mf;
|
|
|
|
polyVert_t *pPolyVerts;
|
|
|
|
polyVert_t *v;
|
|
|
|
clipHandle_t cmodel;
|
|
|
|
float fStartAlpha, fEndAlpha;
|
|
|
|
float fStartTex, fEndTex;
|
|
|
|
float fRightCenterDist;
|
|
|
|
float fOOWidth, fOODoubleWidth;
|
|
|
|
float fDist, fSideDist;
|
|
|
|
float fFrac;
|
2024-09-05 00:54:39 +02:00
|
|
|
float fStartDist, fRightStartDist, fLeftStartDist;
|
2024-09-04 21:41:16 +02:00
|
|
|
float fCenterTexScale, fRightTexScale, fLeftTexScale;
|
2024-09-05 00:54:39 +02:00
|
|
|
float fCenterAlphaScale, fRightAlphaScale, fLeftAlphaScale;
|
2024-09-04 21:41:16 +02:00
|
|
|
float fSideAlpha, fCenterAlpha;
|
|
|
|
vec3_t vStartCenter, vEndCenter;
|
|
|
|
vec3_t vDirection;
|
|
|
|
vec3_t vRight;
|
|
|
|
vec3_t vDelta;
|
|
|
|
float fRadiusSquared;
|
2024-09-04 20:29:29 +02:00
|
|
|
|
2024-09-05 00:54:39 +02:00
|
|
|
pPolyVerts = (polyVert_t *)pVoidPolyVerts;
|
|
|
|
|
|
|
|
if (bStartSegment) {
|
|
|
|
VectorCopy(pTread->vStartVerts[1], originalPoints[0]);
|
|
|
|
VectorCopy(pTread->vStartVerts[0], originalPoints[1]);
|
|
|
|
VectorCopy(pTread->vMidVerts[0], originalPoints[2]);
|
|
|
|
VectorCopy(pTread->vMidVerts[1], originalPoints[3]);
|
|
|
|
|
|
|
|
fStartAlpha = pTread->fStartAlpha;
|
|
|
|
fEndAlpha = pTread->fMidAlpha;
|
|
|
|
fStartTex = pTread->fStartTexCoord;
|
|
|
|
fEndTex = pTread->fMidTexCoord;
|
|
|
|
|
|
|
|
// Calculate the start center
|
|
|
|
vStartCenter[0] = (originalPoints[1][0] + originalPoints[0][0]) * 0.5;
|
|
|
|
vStartCenter[1] = (originalPoints[1][1] + originalPoints[0][1]) * 0.5;
|
|
|
|
vStartCenter[2] = (originalPoints[1][2] + originalPoints[0][2]) * 0.5;
|
|
|
|
VectorCopy(pTread->vMidPos, vEndCenter);
|
|
|
|
VectorCopy(pTread->vStartDir, vDirection);
|
|
|
|
} else {
|
|
|
|
VectorCopy(pTread->vMidVerts[1], originalPoints[0]);
|
|
|
|
VectorCopy(pTread->vMidVerts[0], originalPoints[1]);
|
|
|
|
VectorCopy(pTread->vEndVerts[0], originalPoints[2]);
|
|
|
|
VectorCopy(pTread->vEndVerts[1], originalPoints[3]);
|
|
|
|
|
|
|
|
fStartAlpha = pTread->fMidAlpha;
|
|
|
|
fEndAlpha = pTread->fEndAlpha;
|
|
|
|
fStartTex = pTread->fMidTexCoord;
|
|
|
|
fEndTex = pTread->fEndTexCoord;
|
|
|
|
|
|
|
|
VectorCopy(pTread->vMidPos, vStartCenter);
|
|
|
|
VectorCopy(pTread->vEndPos, vEndCenter);
|
|
|
|
|
|
|
|
// Calculate the direction
|
|
|
|
VectorSubtract(vEndCenter, vStartCenter, vDirection);
|
|
|
|
VectorNormalizeFast(vDirection);
|
|
|
|
}
|
|
|
|
|
|
|
|
CrossProduct(vec_upwards, vDirection, vRight);
|
|
|
|
fRightCenterDist = DotProduct(vEndCenter, vRight);
|
|
|
|
fOOWidth = 1.0 / pTread->fWidth;
|
|
|
|
fOODoubleWidth = 1.0 / (pTread->fWidth * 2);
|
|
|
|
fStartDist = DotProduct(vDirection, vStartCenter);
|
|
|
|
fRightStartDist = DotProduct(vDirection, originalPoints[1]);
|
|
|
|
fLeftStartDist = DotProduct(vDirection, originalPoints[1]);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate center
|
|
|
|
//
|
|
|
|
VectorSubtract(vEndCenter, vStartCenter, vDelta);
|
|
|
|
fDist = VectorLength(vDelta);
|
|
|
|
fCenterTexScale = (fEndTex - fStartTex) / fDist;
|
|
|
|
fCenterAlphaScale = (fEndAlpha - fStartAlpha) / fDist;
|
|
|
|
fSideDist = fDist;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate right
|
|
|
|
//
|
|
|
|
VectorSubtract(originalPoints[2], originalPoints[1], vDelta);
|
|
|
|
fDist = VectorLength(vDelta);
|
|
|
|
fRightTexScale = (fEndTex - fStartTex) / fDist;
|
|
|
|
fRightAlphaScale = (fEndAlpha - fStartAlpha) / fDist;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate left
|
|
|
|
//
|
|
|
|
VectorSubtract(originalPoints[3], originalPoints[0], vDelta);
|
|
|
|
fDist = VectorLength(vDelta);
|
|
|
|
fLeftTexScale = (fEndTex - fStartTex) / fDist;
|
|
|
|
fLeftAlphaScale = (fEndAlpha - fStartAlpha) / fDist;
|
|
|
|
|
|
|
|
VectorSet(projection, 0, 0, -32);
|
|
|
|
colors[0] = colors[1] = colors[2] = colors[3] = 0xff;
|
|
|
|
|
|
|
|
numFragments = CG_GetMarkFragments(
|
|
|
|
4, originalPoints, projection, markPoints, pMarkFragments, (Square(pTread->fWidth) + Square(fSideDist)) * 0.25
|
|
|
|
);
|
|
|
|
|
|
|
|
for (i = 0; i < numFragments; i++) {
|
|
|
|
vec3_t vWorldPos;
|
|
|
|
|
|
|
|
mf = &pMarkFragments[i];
|
|
|
|
|
|
|
|
if (mf->numPoints > 8) {
|
|
|
|
mf->numPoints = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mf->iIndex >= 0) {
|
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
|
|
|
v = &pPolyVerts[mf->firstPoint + j];
|
|
|
|
VectorCopy(markPoints[mf->firstPoint + j], v->xyz);
|
|
|
|
|
|
|
|
if (dolighting) {
|
|
|
|
cgi.R_GetLightingForDecal(vLight, projection, v->xyz);
|
|
|
|
|
|
|
|
colors[0] = vLight[0];
|
|
|
|
colors[1] = vLight[1];
|
|
|
|
colors[2] = vLight[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
fSideDist = DotProduct(v->xyz, vRight) - fRightCenterDist;
|
|
|
|
fSideAlpha = fSideDist * fOOWidth;
|
|
|
|
v->st[0] = (fSideDist + pTread->fWidth) * fOODoubleWidth;
|
|
|
|
|
|
|
|
if (fSideAlpha < 0) {
|
|
|
|
fSideAlpha = -fSideAlpha;
|
|
|
|
v->st[1] =
|
|
|
|
fStartTex + ((DotProduct(v->xyz, vDirection) - fLeftStartDist) * fLeftTexScale * fSideAlpha);
|
|
|
|
} else {
|
|
|
|
v->st[1] =
|
|
|
|
fStartTex + ((DotProduct(v->xyz, vDirection) - fRightStartDist) * fRightTexScale * fSideAlpha);
|
|
|
|
}
|
|
|
|
|
|
|
|
v->st[1] += (DotProduct(v->xyz, vDirection) - fStartDist) * fCenterTexScale * (1.0 - fSideAlpha);
|
|
|
|
}
|
|
|
|
} else if (CG_GetMarkInlineModelOrientation(mf->iIndex)) {
|
|
|
|
for (j = 0; j < mf->numPoints; j++) {
|
|
|
|
v = &pPolyVerts[mf->firstPoint + j];
|
|
|
|
VectorCopy(markPoints[mf->firstPoint + j], v->xyz);
|
|
|
|
|
|
|
|
if (dolighting) {
|
|
|
|
cgi.R_GetLightingForDecal(vLight, projection, v->xyz);
|
|
|
|
|
|
|
|
colors[0] = vLight[0];
|
|
|
|
colors[1] = vLight[1];
|
|
|
|
colors[2] = vLight[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
v->modulate[0] = colors[0];
|
|
|
|
v->modulate[1] = colors[1];
|
|
|
|
v->modulate[2] = colors[2];
|
|
|
|
v->modulate[3] = colors[3];
|
|
|
|
|
|
|
|
CG_FragmentPosToWorldPos(v->xyz, vWorldPos);
|
|
|
|
|
|
|
|
fSideDist = DotProduct(vWorldPos, vRight) - fRightCenterDist;
|
|
|
|
fSideAlpha = fSideDist * fOOWidth;
|
|
|
|
v->st[0] = (fSideDist + pTread->fWidth) * fOODoubleWidth;
|
|
|
|
|
|
|
|
if (fSideAlpha < 0) {
|
|
|
|
fSideAlpha = -fSideAlpha;
|
|
|
|
v->st[1] =
|
|
|
|
fStartTex + ((DotProduct(vWorldPos, vDirection) - fLeftStartDist) * fLeftTexScale * fSideAlpha);
|
|
|
|
} else {
|
|
|
|
v->st[1] = fStartTex
|
|
|
|
+ ((DotProduct(vWorldPos, vDirection) - fRightStartDist) * fRightTexScale * fSideAlpha);
|
|
|
|
}
|
|
|
|
|
|
|
|
v->st[1] += (DotProduct(vWorldPos, vDirection) - fStartDist) * fCenterTexScale * (1.0 - fSideAlpha);
|
|
|
|
}
|
|
|
|
|
|
|
|
mf->iIndex = -cgi.CM_InlineModel(cg_entities[-mf->iIndex].currentState.modelindex);
|
|
|
|
} else {
|
|
|
|
mf->numPoints = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return numFragments;
|
2023-05-01 00:08:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CG_PermanentUpdateTreadMark(
|
2023-07-05 21:24:23 +02:00
|
|
|
treadMark_t *pTread, float fAlpha, float fMinSegment, float fMaxSegment, float fMaxOffset, float fTexScale
|
2023-05-01 00:08:56 +02:00
|
|
|
)
|
|
|
|
{
|
2024-09-04 21:41:16 +02:00
|
|
|
trace_t trace;
|
|
|
|
float fDist;
|
|
|
|
float fSplitLength;
|
|
|
|
float fNewLength;
|
|
|
|
float fTmp;
|
2024-09-04 20:29:29 +02:00
|
|
|
qboolean bDoSegmentation;
|
2024-09-04 21:41:16 +02:00
|
|
|
vec3_t vPos;
|
|
|
|
vec3_t vEnd;
|
|
|
|
vec3_t vNewPos;
|
|
|
|
vec3_t vDelta, vDeltaNorm;
|
|
|
|
vec3_t vDir, vMidDir;
|
|
|
|
vec3_t vRight;
|
2024-09-04 20:29:29 +02:00
|
|
|
|
2024-09-05 00:54:39 +02:00
|
|
|
VectorCopy(cg.predicted_player_state.origin, vPos);
|
|
|
|
VectorCopy(cg.predicted_player_state.origin, vEnd);
|
|
|
|
|
|
|
|
vPos[2] += 32;
|
|
|
|
vEnd[2] -= 256;
|
|
|
|
|
|
|
|
CG_Trace(
|
|
|
|
&trace,
|
|
|
|
vPos,
|
|
|
|
vec3_origin,
|
|
|
|
vec3_origin,
|
|
|
|
vEnd,
|
|
|
|
cg.snap->ps.clientNum,
|
|
|
|
MASK_TREADMARK,
|
|
|
|
qfalse,
|
|
|
|
qtrue,
|
|
|
|
"CG_PermanentUpdateTreadMark"
|
|
|
|
);
|
|
|
|
|
|
|
|
VectorCopy(trace.endpos, vNewPos);
|
|
|
|
|
|
|
|
if (cg.time - pTread->iLastTime > 500) {
|
|
|
|
VectorClear(pTread->vStartDir);
|
|
|
|
VectorClear(pTread->vStartVerts[0]);
|
|
|
|
VectorClear(pTread->vStartVerts[1]);
|
|
|
|
pTread->fStartTexCoord = 0;
|
|
|
|
pTread->fStartAlpha = 0;
|
|
|
|
|
|
|
|
VectorClear(pTread->vMidPos);
|
|
|
|
VectorClear(pTread->vMidVerts[0]);
|
|
|
|
VectorClear(pTread->vMidVerts[1]);
|
|
|
|
pTread->fMidTexCoord = 0;
|
|
|
|
pTread->fMidAlpha = 0;
|
|
|
|
|
|
|
|
VectorClear(pTread->vEndPos);
|
|
|
|
VectorClear(pTread->vEndVerts[0]);
|
|
|
|
VectorClear(pTread->vEndVerts[1]);
|
|
|
|
pTread->fEndTexCoord = 0;
|
|
|
|
pTread->fEndAlpha = 0;
|
|
|
|
|
|
|
|
pTread->iState = 1;
|
|
|
|
pTread->iReferenceNumber = 0;
|
|
|
|
pTread->iLastTime = cg.time;
|
|
|
|
VectorCopy(vNewPos, pTread->vMidPos);
|
|
|
|
VectorCopy(vNewPos, pTread->vEndPos);
|
|
|
|
|
|
|
|
if (fAlpha < 0) {
|
|
|
|
pTread->fMidAlpha = 255.0;
|
|
|
|
} else {
|
|
|
|
pTread->fMidAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
pTread->fEndAlpha = pTread->fMidAlpha;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VectorCompare(pTread->vEndPos, vNewPos)) {
|
|
|
|
if (fAlpha < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fTmp = pTread->fEndAlpha;
|
|
|
|
pTread->fEndAlpha = fAlpha * 255;
|
|
|
|
|
|
|
|
//if (fabs(fTmp - pTread->fEndAlpha > 0.05)) {
|
|
|
|
// Fixed in OPM
|
|
|
|
// Looks like this was accidental
|
|
|
|
if (fabs(fTmp - pTread->fEndAlpha) > 0.05) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorSubtract(vNewPos, pTread->vMidPos, vDelta);
|
|
|
|
fDist = VectorNormalize2(vDelta, vDeltaNorm);
|
|
|
|
|
|
|
|
if (pTread->iState == 1) {
|
|
|
|
fSplitLength = 0.0;
|
|
|
|
bDoSegmentation = qfalse;
|
|
|
|
} else {
|
|
|
|
VectorAdd(vDeltaNorm, pTread->vStartDir, vMidDir);
|
|
|
|
VectorScale(vMidDir, 0.5, vMidDir);
|
|
|
|
VectorNormalizeFast(vMidDir);
|
|
|
|
|
|
|
|
fTmp = DotProduct(vMidDir, vDeltaNorm);
|
|
|
|
fSplitLength = pTread->fWidth / fTmp;
|
|
|
|
|
|
|
|
if (fTmp < -0.5) {
|
|
|
|
fNewLength =
|
|
|
|
fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp * (fTmp * (fTmp * -337.31875783205 + -1237.54375255107) + -1802.11467325687)
|
|
|
|
+ -1303.19904613494)
|
|
|
|
+ -471.347871690988)
|
|
|
|
+ -70.0883838161826);
|
|
|
|
} else if (fTmp > 0.5) {
|
|
|
|
fNewLength =
|
|
|
|
fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp
|
|
|
|
* (fTmp * (fTmp * (fTmp * -1507.55394345521 + 6580.58002318442) + -11860.0735285953)
|
|
|
|
+ 11290.7510782536)
|
|
|
|
+ -5986.89654545347)
|
|
|
|
+ 1675.66417006387)
|
|
|
|
+ -192.426950291139;
|
|
|
|
} else {
|
|
|
|
fNewLength = fTmp * (fTmp * -0.531387674508458 + -2.11e-14) + 1.00086138065435;
|
|
|
|
}
|
|
|
|
|
|
|
|
fNewLength *= fSplitLength;
|
|
|
|
if (fNewLength + fMinSegment > fDist) {
|
|
|
|
bDoSegmentation = qfalse;
|
|
|
|
} else if (fSplitLength < -1) {
|
|
|
|
fSplitLength = -fSplitLength;
|
|
|
|
bDoSegmentation = qtrue;
|
|
|
|
} else if (fDist > fMaxSegment) {
|
|
|
|
bDoSegmentation = qtrue;
|
|
|
|
} else {
|
|
|
|
CrossProduct(vec_upwards, pTread->vStartDir, vRight);
|
|
|
|
VectorNormalizeFast(vRight);
|
|
|
|
|
|
|
|
if (fabs(DotProduct(vRight, vDelta)) > fMaxOffset) {
|
|
|
|
bDoSegmentation = qtrue;
|
|
|
|
} else {
|
|
|
|
bDoSegmentation = qfalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pTread->iState == 2 && bDoSegmentation) {
|
|
|
|
VectorCopy(pTread->vStartVerts[0], pTread->vMidVerts[0]);
|
|
|
|
VectorCopy(pTread->vStartVerts[1], pTread->vMidVerts[1]);
|
|
|
|
pTread->fMidTexCoord = pTread->fEndTexCoord;
|
|
|
|
pTread->fMidAlpha = pTread->fEndAlpha;
|
|
|
|
VectorCopy(vNewPos, pTread->vEndPos);
|
|
|
|
|
|
|
|
if (fAlpha >= 0) {
|
|
|
|
pTread->fEndAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTread->iState = 3;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} else if (pTread->iState == 1 || pTread->iState == 2) {
|
|
|
|
VectorCopy(vNewPos, pTread->vEndPos);
|
|
|
|
|
|
|
|
if (fAlpha >= 0.0) {
|
|
|
|
pTread->fEndAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CrossProduct(vec_upwards, vDeltaNorm, vDir);
|
|
|
|
VectorMA(pTread->vMidPos, pTread->fWidth, vDir, pTread->vMidVerts[0]);
|
|
|
|
VectorMA(pTread->vMidPos, -pTread->fWidth, vDir, pTread->vMidVerts[1]);
|
|
|
|
VectorMA(pTread->vEndPos, pTread->fWidth, vDir, pTread->vEndVerts[0]);
|
|
|
|
VectorMA(pTread->vEndPos, -pTread->fWidth, vDir, pTread->vEndVerts[1]);
|
|
|
|
|
|
|
|
pTread->fEndTexCoord = fDist * fTexScale;
|
|
|
|
|
|
|
|
if (pTread->iState == 1 && fDist > 8.0) {
|
|
|
|
VectorCopy(vDelta, pTread->vStartDir);
|
|
|
|
pTread->iState = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
} else if (bDoSegmentation) {
|
|
|
|
if (!pTread->iReferenceNumber) {
|
|
|
|
pTread->iReferenceNumber = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTread->iReferenceNumber = 0;
|
|
|
|
VectorCopy(pTread->vStartVerts[0], pTread->vMidVerts[0]);
|
|
|
|
VectorCopy(pTread->vStartVerts[1], pTread->vMidVerts[1]);
|
|
|
|
VectorSubtract(pTread->vEndPos, pTread->vMidPos, pTread->vStartDir);
|
|
|
|
VectorNormalizeFast(pTread->vStartDir);
|
|
|
|
|
|
|
|
VectorCopy(pTread->vEndPos, pTread->vMidPos);
|
|
|
|
pTread->fMidTexCoord = pTread->fEndTexCoord;
|
|
|
|
pTread->fMidAlpha = pTread->fEndAlpha;
|
|
|
|
|
|
|
|
if (pTread->fStartTexCoord >= 1.0) {
|
|
|
|
pTread->fStartTexCoord = pTread->fStartTexCoord - floor(pTread->fStartTexCoord);
|
|
|
|
pTread->fMidTexCoord = pTread->fMidTexCoord - floor(pTread->fStartTexCoord);
|
|
|
|
}
|
|
|
|
|
|
|
|
VectorCopy(vNewPos, pTread->vEndPos);
|
|
|
|
pTread->fEndTexCoord = fDist * fTexScale + pTread->fMidTexCoord;
|
|
|
|
if (fAlpha >= 0) {
|
|
|
|
pTread->fEndAlpha = fAlpha * 255.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CrossProduct(vec_upwards, vMidDir, vRight);
|
|
|
|
VectorNormalizeFast(vRight);
|
|
|
|
VectorMA(pTread->vMidPos, fSplitLength, vRight, pTread->vMidVerts[0]);
|
|
|
|
VectorMA(pTread->vMidPos, -fSplitLength, vRight, pTread->vMidVerts[1]);
|
|
|
|
CrossProduct(vec_upwards, vDeltaNorm, vRight);
|
|
|
|
VectorNormalizeFast(vRight);
|
|
|
|
VectorMA(pTread->vEndPos, pTread->fWidth, vRight, pTread->vEndVerts[0]);
|
|
|
|
VectorMA(pTread->vEndPos, -pTread->fWidth, vRight, pTread->vEndVerts[1]);
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|