2016-03-27 11:49:47 +02:00
|
|
|
/*
|
|
|
|
===========================================================================
|
2023-04-30 00:02:16 +02:00
|
|
|
Copyright (C) 2023 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
|
|
|
|
|
|
|
|
===================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
markPoly_t cg_activeMarkPolys; // double linked list
|
|
|
|
markPoly_t *cg_freeMarkPolys; // single linked list
|
|
|
|
markPoly_t cg_markPolys[MAX_MARK_POLYS];
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
vec3_t vec_upwards;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
memset(cg_markPolys, 0, sizeof(cg_markPolys));
|
|
|
|
|
|
|
|
cg_activeMarkPolys.nextMark = &cg_activeMarkPolys;
|
|
|
|
cg_activeMarkPolys.prevMark = &cg_activeMarkPolys;
|
|
|
|
cg_freeMarkPolys = cg_markPolys;
|
|
|
|
for (i = 0; i < MAX_MARK_POLYS - 1; i++) {
|
|
|
|
cg_markPolys[i].nextMark = &cg_markPolys[i + 1];
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
CG_FreeMarkPoly
|
|
|
|
==================
|
|
|
|
*/
|
2023-05-01 00:08:56 +02:00
|
|
|
void CG_FreeMarkPoly(markPoly_t* le)
|
|
|
|
{
|
|
|
|
if (!le->prevMark) {
|
|
|
|
cgi.Error(ERR_DROP, "CG_FreeLocalEntity: not active");
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove from the doubly linked active list
|
|
|
|
le->prevMark->nextMark = le->nextMark;
|
|
|
|
le->nextMark->prevMark = le->prevMark;
|
|
|
|
|
|
|
|
// the free list is only singly linked
|
|
|
|
le->nextMark = cg_freeMarkPolys;
|
|
|
|
cg_freeMarkPolys = le;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===================
|
|
|
|
CG_AllocMark
|
|
|
|
|
|
|
|
Will allways succeed, even if it requires freeing an old active mark
|
|
|
|
===================
|
|
|
|
*/
|
2023-05-01 00:08:56 +02:00
|
|
|
markPoly_t* CG_AllocMark(void)
|
|
|
|
{
|
|
|
|
markPoly_t* le;
|
|
|
|
|
|
|
|
if (!cg_freeMarkPolys) {
|
|
|
|
int time;
|
|
|
|
// no free entities, so free the one at the end of the chain
|
|
|
|
// remove the oldest active entity
|
|
|
|
time = cg_activeMarkPolys.prevMark->time;
|
|
|
|
while (cg_activeMarkPolys.prevMark &&
|
|
|
|
time == cg_activeMarkPolys.prevMark->time) {
|
|
|
|
CG_FreeMarkPoly(cg_activeMarkPolys.prevMark);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
le = cg_freeMarkPolys;
|
|
|
|
cg_freeMarkPolys = cg_freeMarkPolys->nextMark;
|
|
|
|
|
|
|
|
memset(le, 0, sizeof(*le));
|
|
|
|
|
|
|
|
// link into the active list
|
|
|
|
le->nextMark = cg_activeMarkPolys.nextMark;
|
|
|
|
le->prevMark = &cg_activeMarkPolys;
|
|
|
|
cg_activeMarkPolys.nextMark->prevMark = le;
|
|
|
|
cg_activeMarkPolys.nextMark = le;
|
|
|
|
return le;
|
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.
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
#define MAX_MARK_FRAGMENTS 128
|
|
|
|
#define MAX_MARK_POINTS 384
|
|
|
|
|
2023-04-30 00:02:16 +02:00
|
|
|
void CG_ImpactMark
|
|
|
|
(
|
|
|
|
qhandle_t markShader,
|
|
|
|
const vec3_t origin,
|
|
|
|
const vec3_t dir,
|
|
|
|
float orientation,
|
|
|
|
float red,
|
|
|
|
float green,
|
|
|
|
float blue,
|
|
|
|
float alpha,
|
|
|
|
qboolean alphaFade,
|
|
|
|
float radius,
|
|
|
|
qboolean temporary,
|
|
|
|
int lightstyle,
|
2023-04-30 01:42:57 +02:00
|
|
|
qboolean fadein,
|
|
|
|
float fSCenter,
|
|
|
|
float fTCenter
|
2023-04-30 00:02:16 +02:00
|
|
|
)
|
2023-05-01 00:08:56 +02:00
|
|
|
{
|
|
|
|
vec3_t axis[3];
|
|
|
|
float texCoordScale;
|
|
|
|
vec3_t originalPoints[4];
|
|
|
|
byte colors[4];
|
|
|
|
int i, j;
|
|
|
|
int numFragments;
|
|
|
|
markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf;
|
|
|
|
vec3_t markPoints[MAX_MARK_POINTS];
|
|
|
|
vec3_t projection;
|
|
|
|
|
|
|
|
if (!cg_addMarks->integer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (radius <= 0) {
|
|
|
|
cgi.Error(ERR_DROP, "CG_ImpactMark called with <= 0 radius");
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the texture axis
|
|
|
|
|
|
|
|
if (orientation) {
|
|
|
|
VectorNormalize2(dir, axis[0]);
|
|
|
|
PerpendicularVector(axis[1], axis[0]);
|
|
|
|
RotatePointAroundVector(axis[2], axis[0], axis[1], orientation);
|
|
|
|
CrossProduct(axis[0], axis[2], axis[1]);
|
|
|
|
} else {
|
|
|
|
vec3_t angles;
|
|
|
|
vec3_t tmp;
|
|
|
|
|
|
|
|
VectorNormalize2(dir, axis[0]);
|
|
|
|
VectorCopy(dir, tmp);
|
|
|
|
tmp[0] = -tmp[0];
|
|
|
|
tmp[1] = -tmp[1];
|
|
|
|
vectoangles(tmp, angles);
|
|
|
|
AnglesToMat(angles, axis);
|
|
|
|
VectorScale(axis[2], -1, axis[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
texCoordScale = 0.5 * 1.0 / radius;
|
|
|
|
|
|
|
|
// create the full polygon
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
originalPoints[0][i] =
|
|
|
|
origin[i] - radius * axis[1][i] - radius * axis[2][i];
|
|
|
|
originalPoints[1][i] =
|
|
|
|
origin[i] + radius * axis[1][i] - radius * axis[2][i];
|
|
|
|
originalPoints[2][i] =
|
|
|
|
origin[i] + radius * axis[1][i] + radius * axis[2][i];
|
|
|
|
originalPoints[3][i] =
|
|
|
|
origin[i] - radius * axis[1][i] + radius * axis[2][i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the fragments
|
|
|
|
VectorScale(dir, -32, projection);
|
|
|
|
|
|
|
|
// FIXME: fRadiusSquared
|
|
|
|
numFragments = cgi.R_MarkFragments(4, (void*)originalPoints, projection,
|
|
|
|
MAX_MARK_POINTS, markPoints[0],
|
|
|
|
MAX_MARK_FRAGMENTS, markFragments, 0.f);
|
|
|
|
|
|
|
|
if (fadein) {
|
|
|
|
colors[0] = 0;
|
|
|
|
colors[1] = 0;
|
|
|
|
colors[2] = 0;
|
|
|
|
colors[3] = 0;
|
|
|
|
} else {
|
|
|
|
colors[0] = red * 255;
|
|
|
|
colors[1] = green * 255;
|
|
|
|
colors[2] = blue * 255;
|
|
|
|
colors[3] = alpha * 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, mf = markFragments; i < numFragments; i++, mf++) {
|
|
|
|
polyVert_t* v;
|
|
|
|
polyVert_t verts[MAX_VERTS_ON_POLY];
|
|
|
|
markPoly_t* mark;
|
|
|
|
|
|
|
|
// we have an upper limit on the complexity of polygons
|
|
|
|
// that we store persistantly
|
|
|
|
if (mf->numPoints > MAX_VERTS_ON_POLY) {
|
|
|
|
mf->numPoints = MAX_VERTS_ON_POLY;
|
|
|
|
}
|
|
|
|
for (j = 0, v = verts; j < mf->numPoints; j++, v++) {
|
|
|
|
vec3_t delta;
|
|
|
|
|
|
|
|
VectorCopy(markPoints[mf->firstPoint + j], v->xyz);
|
|
|
|
|
|
|
|
VectorSubtract(v->xyz, origin, delta);
|
|
|
|
v->st[0] = 0.5 + DotProduct(delta, axis[1]) * texCoordScale;
|
|
|
|
v->st[1] = 0.5 + DotProduct(delta, axis[2]) * texCoordScale;
|
|
|
|
*(int*)v->modulate = *(int*)colors;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it is a temporary (shadow) mark, add it immediately and forget
|
|
|
|
// about it
|
|
|
|
if (temporary) {
|
|
|
|
cgi.R_AddPolyToScene(markShader, mf->numPoints, verts, 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise save it persistantly
|
|
|
|
mark = CG_AllocMark();
|
|
|
|
mark->time = cg.time;
|
|
|
|
mark->alphaFade = alphaFade;
|
|
|
|
mark->markShader = markShader;
|
|
|
|
mark->poly.numVerts = mf->numPoints;
|
|
|
|
mark->color[0] = red;
|
|
|
|
mark->color[1] = green;
|
|
|
|
mark->color[2] = blue;
|
|
|
|
mark->color[3] = alpha;
|
|
|
|
mark->fadein = fadein;
|
|
|
|
mark->lightstyle = lightstyle;
|
|
|
|
memcpy(mark->verts, verts, mf->numPoints * sizeof(verts[0]));
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
void CG_ImpactMarkSimple(
|
|
|
|
qhandle_t markShader,
|
|
|
|
vec_t* origin,
|
|
|
|
vec_t* dir,
|
|
|
|
float orientation,
|
|
|
|
float fRadius,
|
|
|
|
float red,
|
|
|
|
float green,
|
|
|
|
float blue,
|
|
|
|
float alpha,
|
|
|
|
qboolean alphaFade,
|
|
|
|
qboolean temporary,
|
|
|
|
qboolean dolighting,
|
|
|
|
qboolean fadein
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
}
|
2016-03-27 11:49:47 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
CG_AddMarks
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
#define MARK_TOTAL_TIME 10000
|
2023-04-30 00:02:16 +02:00
|
|
|
#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)
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
markPoly_t *mp, *next;
|
|
|
|
int t;
|
|
|
|
int fade;
|
|
|
|
|
|
|
|
if (!cg_addMarks->integer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp = cg_activeMarkPolys.nextMark;
|
|
|
|
for (; mp != &cg_activeMarkPolys; mp = next) {
|
|
|
|
// grab next now, so if the local entity is freed we
|
|
|
|
// still have it
|
|
|
|
next = mp->nextMark;
|
|
|
|
|
|
|
|
// see if it is time to completely remove it
|
|
|
|
if (cg.time > mp->time + MARK_TOTAL_TIME) {
|
|
|
|
CG_FreeMarkPoly(mp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fade all marks out with time
|
|
|
|
t = mp->time + MARK_TOTAL_TIME - cg.time;
|
|
|
|
|
|
|
|
if (mp->lightstyle > 0) {
|
|
|
|
CG_LightStyleColor(mp->lightstyle, t, mp->color, qtrue);
|
|
|
|
|
|
|
|
if (mp->color[3] <= 0) {
|
|
|
|
CG_FreeMarkPoly(mp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < mp->poly.numVerts; j++) {
|
|
|
|
mp->verts[j].modulate[0] = mp->color[0] * 255;
|
|
|
|
mp->verts[j].modulate[1] = mp->color[1] * 255;
|
|
|
|
mp->verts[j].modulate[2] = mp->color[2] * 255;
|
|
|
|
mp->verts[j].modulate[3] = mp->color[3] * 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fade in marks
|
|
|
|
if (mp->fadein) {
|
|
|
|
fade = 255 * (cg.time - mp->time) / MARK_FADE_TIME;
|
|
|
|
|
|
|
|
if (fade > 255) {
|
|
|
|
fade = 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp->alphaFade) {
|
|
|
|
for (j = 0; j < mp->poly.numVerts; j++) {
|
|
|
|
mp->verts[j].modulate[3] = fade;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < mp->poly.numVerts; j++) {
|
|
|
|
mp->verts[j].modulate[0] = mp->color[0] * fade;
|
|
|
|
mp->verts[j].modulate[1] = mp->color[1] * fade;
|
|
|
|
mp->verts[j].modulate[2] = mp->color[2] * fade;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fade out marks
|
|
|
|
if (t < MARK_FADE_TIME) {
|
|
|
|
fade = 255 * t / MARK_FADE_TIME;
|
|
|
|
if (mp->alphaFade) {
|
|
|
|
for (j = 0; j < mp->poly.numVerts; j++) {
|
|
|
|
mp->verts[j].modulate[3] = fade;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < mp->poly.numVerts; j++) {
|
|
|
|
mp->verts[j].modulate[0] = mp->color[0] * fade;
|
|
|
|
mp->verts[j].modulate[1] = mp->color[1] * fade;
|
|
|
|
mp->verts[j].modulate[2] = mp->color[2] * fade;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cgi.R_AddPolyToScene(mp->markShader, mp->poly.numVerts, mp->verts, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qboolean CG_CheckMakeMarkOnEntity(int iEntIndex)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CG_InitTestTreadMark()
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
int CG_StartTreadMark(
|
|
|
|
int iReference,
|
|
|
|
qhandle_t treadShader,
|
|
|
|
vec_t* vStartPos,
|
|
|
|
float fWidth,
|
|
|
|
float fAlpha)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
qboolean CG_MakeTreadMarkDecal_PerPolyCallback(
|
|
|
|
vec3_t* markPoints,
|
|
|
|
markFragment_t* mf,
|
|
|
|
polyVert_t* verts,
|
|
|
|
void* pCustom
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CG_MakeTreadMarkDecal_GetLeafCallback(
|
|
|
|
markFragment_t* mf,
|
|
|
|
void* pCustom
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CG_MakeTreadMarkDecal(
|
|
|
|
treadMark_t* pTread,
|
|
|
|
qboolean bStartSegment,
|
|
|
|
qboolean bTemporary
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
int CG_UpdateTreadMark(
|
|
|
|
int iReference,
|
|
|
|
vec_t* vNewPos,
|
|
|
|
float fAlpha
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return 0;
|
2016-03-27 11:49:47 +02:00
|
|
|
}
|
|
|
|
|
2023-05-01 00:08:56 +02:00
|
|
|
void CG_AddTreadMarks()
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
int CG_PermanentMark(
|
2023-05-19 21:34:36 +02:00
|
|
|
const vec3_t origin,
|
|
|
|
const vec3_t dir,
|
2023-05-01 00:08:56 +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
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CG_PermanentTreadMarkDecal(
|
|
|
|
treadMark_t* pTread,
|
|
|
|
qboolean bStartSegment,
|
|
|
|
qboolean dolighting,
|
|
|
|
markFragment_t* pMarkFragments,
|
|
|
|
void* pVoidPolyVerts
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CG_PermanentUpdateTreadMark(
|
|
|
|
treadMark_t* pTread,
|
|
|
|
float fAlpha,
|
|
|
|
float fMinSegment,
|
|
|
|
float fMaxSegment,
|
|
|
|
float fMaxOffset,
|
|
|
|
float fTexScale
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// FIXME: unimplemented
|
|
|
|
return 0;
|
|
|
|
}
|