openmohaa/code/renderer/tr_marks_permanent.c

1997 lines
59 KiB
C
Raw Normal View History

2023-05-09 19:18:16 +02:00
/*
===========================================================================
Copyright (C) 2024 the OpenMoHAA team
2023-05-09 19:18:16 +02:00
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
===========================================================================
*/
// tr_marks_pernanent.c: permanent marks
#include "tr_local.h"
2023-05-09 20:49:04 +02:00
#define DCL_FILE_SIGNATURE *(int *)"DCL "
#define DCL_FILE_OLD_VERSION 1
#define DCL_FILE_VERSION 2
2023-05-19 02:09:28 +02:00
typedef struct lmEditPoly_s {
srfMarkFragment_t surf;
shader_t *shader;
int viewCount;
polyVert_t verts[8];
struct lmEditPoly_s *pNextPoly;
mnode_t *pLeafs[8];
int iNumLeafs;
2023-05-19 02:09:28 +02:00
} lmEditPoly_t;
typedef struct lmEditMarkDef_s {
struct lmEditMarkDef_s *pNextMark;
struct lmEditMarkDef_s *pPrevMark;
shader_t *markShader;
vec3_t vPos;
vec3_t vProjection;
float fRadius;
float fHeightScale;
float fWidthScale;
float fRotation;
vec4_t color;
qboolean bDoLighting;
lmEditPoly_t *pMarkEditPolys;
int iNumEditPolys;
vec3_t vPathCorners[4];
2023-05-19 02:09:28 +02:00
} lmEditMarkDef_t;
typedef struct lmPoly_s {
srfMarkFragment_t surf;
shader_t *shader;
int viewCount;
2023-05-19 02:09:28 +02:00
} lmPoly_t;
typedef struct {
qboolean bLevelMarksLoaded;
char szDCLFilename[MAX_QPATH];
lmPoly_t *pMarkFragments;
qboolean bAutoApplySettings;
lmEditPoly_t *pFreeEditPolys;
lmEditMarkDef_t activeMarkDefs;
lmEditMarkDef_t *pFreeMarkDefs;
lmEditMarkDef_t *pCurrentMark;
qboolean bPathLayingMode;
treadMark_t treadMark;
lmEditMarkDef_t *pTreadMarkStartDecal;
lmEditMarkDef_t *pTreadMarkEndDecal;
2023-05-19 02:09:28 +02:00
} lmGlobals_t;
typedef struct {
int ident;
int version;
int checksum;
int iNumDecals;
int iNumFragments;
2023-05-19 02:09:28 +02:00
} dclHeader_t;
typedef struct {
char shader[MAX_RES_NAME];
vec3_t vPos;
vec3_t vProjection;
float fRadius;
float fHeightScale;
float fWidthScale;
float fRotation;
vec4_t color;
qboolean bDoLighting;
2023-05-19 02:09:28 +02:00
} dclSavedMarkDef_t;
typedef struct {
char shader[MAX_RES_NAME];
int fogIndex;
int iIndex;
int iNumVerts;
2023-05-19 02:09:28 +02:00
} dclSavedMarkPoly_t;
cvar_t *dcl_editmode;
cvar_t *dcl_showcurrent;
cvar_t *dcl_autogetinfo;
cvar_t *dcl_shiftstep;
cvar_t *dcl_shader;
cvar_t *dcl_radius;
cvar_t *dcl_heightscale;
cvar_t *dcl_widthscale;
cvar_t *dcl_rotation;
cvar_t *dcl_r;
cvar_t *dcl_g;
cvar_t *dcl_b;
cvar_t *dcl_alpha;
cvar_t *dcl_dolighting;
cvar_t *dcl_doworld;
cvar_t *dcl_doterrain;
cvar_t *dcl_dobmodels;
cvar_t *dcl_dostring;
cvar_t *dcl_pathmode;
cvar_t *dcl_maxsegment;
cvar_t *dcl_minsegment;
cvar_t *dcl_maxoffset;
cvar_t *dcl_texturescale;
static lmGlobals_t lm;
2023-05-19 02:09:28 +02:00
vec3_t vec_upwards;
lmEditMarkDef_t *R_AllocateMarkDef(void);
lmEditMarkDef_t *R_ApplyLevelDecal(
const vec3_t vPos, const vec3_t vDir, qboolean bUseCurrent, qboolean bUseCurrentSettings, int iPathIndex
);
void DCLC_Delete(void);
void DCLC_GetInfo(void);
/*
=============
DCLC_Save
Save level marks into a DCL file
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_Save(void)
{
fileHandle_t hFile;
dclHeader_t header;
int i, j;
int iNumDecals, iNumFragments;
lmEditPoly_t *pPoly;
lmEditMarkDef_t *pMark;
bmodel_t *pBmodel;
mnode_t *pLeaf;
dclSavedMarkDef_t saveMark;
dclSavedMarkPoly_t savePoly;
char map_time[32];
int littleValue;
hFile = FS_FOpenFileWrite(lm.szDCLFilename);
if (!hFile) {
ri.Printf(PRINT_ALL, "R_SaveDCLFile: couldn't write to %s\n", lm.szDCLFilename);
}
pMark = lm.activeMarkDefs.pNextMark;
iNumDecals = 0;
iNumFragments = 0;
for (pMark = lm.activeMarkDefs.pNextMark; pMark != &lm.activeMarkDefs; pMark = pMark->pNextMark, iNumDecals++) {
for (pPoly = pMark->pMarkEditPolys; pPoly; iNumFragments++) {
pPoly->viewCount = iNumFragments;
pPoly = pPoly->pNextPoly;
}
}
header.ident = DCL_FILE_SIGNATURE;
header.version = LittleLong(DCL_FILE_VERSION);
header.checksum = 0;
header.iNumDecals = LittleLong(iNumDecals);
header.iNumFragments = LittleLong(iNumFragments);
FS_Write(&header, sizeof(header), hFile);
//
// Write the map time
//
memset(map_time, 0, sizeof(map_time));
Q_strncpyz(map_time, ri.CM_MapTime(), sizeof(map_time));
FS_Write(map_time, sizeof(map_time), hFile);
if (!iNumDecals) {
//
// Nothing to write
//
FS_FCloseFile(hFile);
return;
}
for (pMark = lm.activeMarkDefs.pNextMark; pMark != &lm.activeMarkDefs; pMark = pMark->pNextMark) {
Q_strncpyz(saveMark.shader, pMark->markShader->name, sizeof(saveMark.shader));
saveMark.vPos[0] = LittleFloat(pMark->vPos[0]);
saveMark.vPos[1] = LittleFloat(pMark->vPos[1]);
saveMark.vPos[2] = LittleFloat(pMark->vPos[2]);
saveMark.vProjection[0] = LittleFloat(pMark->vProjection[0]);
saveMark.vProjection[1] = LittleFloat(pMark->vProjection[1]);
saveMark.vProjection[2] = LittleFloat(pMark->vProjection[2]);
saveMark.fRadius = LittleFloat(pMark->fRadius);
saveMark.fHeightScale = LittleFloat(pMark->fHeightScale);
saveMark.fWidthScale = LittleFloat(pMark->fWidthScale);
saveMark.fRotation = LittleFloat(pMark->fRotation);
saveMark.color[0] = LittleFloat(pMark->color[0]);
saveMark.color[1] = LittleFloat(pMark->color[1]);
saveMark.color[2] = LittleFloat(pMark->color[2]);
saveMark.color[3] = LittleFloat(pMark->color[3]);
saveMark.bDoLighting = LittleLong(pMark->bDoLighting);
FS_Write(&saveMark, sizeof(saveMark), hFile);
}
for (pMark = lm.activeMarkDefs.pNextMark; pMark != &lm.activeMarkDefs; pMark = pMark->pNextMark) {
for (pPoly = pMark->pMarkEditPolys; pPoly; pPoly = pPoly->pNextPoly) {
Q_strncpyz(savePoly.shader, pMark->markShader->name, sizeof(savePoly.shader));
savePoly.fogIndex = 0;
savePoly.iIndex = LittleLong(pPoly->surf.iIndex);
savePoly.iNumVerts = LittleLong(pPoly->surf.numVerts);
FS_Write(&savePoly, sizeof(savePoly), hFile);
FS_Write(pPoly->verts, sizeof(pPoly->verts[0]) * savePoly.iNumVerts, hFile);
}
}
for (i = 0; i < tr.world->numBmodels; i++) {
pBmodel = &tr.world->bmodels[i];
if (!pBmodel->iNumMarkFragment) {
//
// No fragment to save
//
littleValue = 0;
FS_Write(&littleValue, sizeof(littleValue), hFile);
continue;
}
littleValue = LittleLong(pBmodel->iNumMarkFragment);
FS_Write(&littleValue, sizeof(int), hFile);
for (j = 0; j < pBmodel->iNumMarkFragment; i++) {
littleValue = ((lmPoly_t *)pBmodel->pFirstMarkFragment[j])->viewCount;
FS_Write(&littleValue, sizeof(int), hFile);
}
}
for (i = tr.world->numDecisionNodes; i < tr.world->numnodes; i++) {
pLeaf = &tr.world->nodes[i];
if (!pLeaf->iNumMarkFragment) {
//
// No fragment to save
//
littleValue = 0;
FS_Write(&littleValue, sizeof(littleValue), hFile);
continue;
}
littleValue = LittleLong(pLeaf->iNumMarkFragment);
FS_Write(&littleValue, sizeof(int), hFile);
for (j = 0; j < pLeaf->iNumMarkFragment; j++) {
littleValue = ((lmPoly_t *)pLeaf->pFirstMarkFragment[j])->viewCount;
FS_Write(&littleValue, sizeof(int), hFile);
}
}
FS_FCloseFile(hFile);
2023-05-19 02:09:28 +02:00
}
/*
=============
R_LevelMarksLoad
Load level marks from a DCL file
=============
*/
void R_LevelMarksLoad(const char *szBSPName)
2023-05-09 20:49:04 +02:00
{
int i;
int iLength;
fileHandle_t hFile;
dclHeader_t dclHeader;
const char *real_map_time;
char map_time[33];
if (lm.bLevelMarksLoaded) {
//
// Destroy and reallocate level marks
//
R_LevelMarksFree();
R_LevelMarksInit();
}
COM_StripExtension(szBSPName, lm.szDCLFilename, sizeof(lm.szDCLFilename));
Q_strcat(lm.szDCLFilename, sizeof(lm.szDCLFilename), ".dcl");
if (ri.FS_OpenFile(lm.szDCLFilename, &hFile, qtrue, qtrue) <= 0) {
ri.Printf(PRINT_ALL, "R_LevelMarksLoad: %s not found\n", lm.szDCLFilename);
return;
}
lm.bLevelMarksLoaded = qtrue;
//
// Read the DCL file header
//
iLength = ri.FS_Read(&dclHeader, sizeof(dclHeader), hFile);
if (iLength < sizeof(dclHeader)) {
ri.FS_CloseFile(hFile);
ri.Printf(PRINT_ALL, "------ Finished loading DCL file %s -----\n", lm.szDCLFilename);
return;
}
//
// Signature and version checks
//
if (dclHeader.ident != DCL_FILE_SIGNATURE) {
ri.Printf(PRINT_ALL, "R_LevelMarksLoad: %s Seems to be an invalid DCL file\n", lm.szDCLFilename);
ri.FS_CloseFile(hFile);
return;
}
for (i = 0; i < sizeof(dclHeader) / sizeof(int); i++) {
((int *)&dclHeader)[i] = LittleLong(((int *)&dclHeader)[i]);
}
if (dclHeader.version != DCL_FILE_OLD_VERSION && dclHeader.version != DCL_FILE_VERSION) {
ri.Printf(
PRINT_ALL,
"R_LevelMarksLoad: %s is wrong version (%i should be %i)\n",
lm.szDCLFilename,
dclHeader.version,
DCL_FILE_VERSION
);
ri.FS_CloseFile(hFile);
return;
}
if (dclHeader.version == DCL_FILE_OLD_VERSION) {
//
// The old version has no map time
//
map_time[0] = 0;
} else {
map_time[32] = 0;
ri.FS_Read(map_time, sizeof(map_time) - 1, hFile);
}
ri.Printf(PRINT_ALL, "----------- Loading DCL file %s ---------\n", lm.szDCLFilename);
if (dcl_editmode->integer) {
dclSavedMarkDef_t saveMark;
lmEditMarkDef_t *pMark;
qhandle_t hShader;
//
// Load all editor-mode decals
//
for (i = 0; i < dclHeader.iNumDecals; i++) {
ri.FS_Read(&saveMark, sizeof(saveMark), hFile);
saveMark.vPos[0] = LittleFloat(saveMark.vPos[0]);
saveMark.vPos[1] = LittleFloat(saveMark.vPos[1]);
saveMark.vPos[2] = LittleFloat(saveMark.vPos[2]);
saveMark.vProjection[0] = LittleFloat(saveMark.vProjection[0]);
saveMark.vProjection[1] = LittleFloat(saveMark.vProjection[1]);
saveMark.vProjection[2] = LittleFloat(saveMark.vProjection[2]);
saveMark.fRadius = LittleFloat(saveMark.fRadius);
saveMark.fHeightScale = LittleFloat(saveMark.fHeightScale);
saveMark.fWidthScale = LittleFloat(saveMark.fWidthScale);
saveMark.fRotation = LittleFloat(saveMark.fRotation);
saveMark.color[0] = LittleFloat(saveMark.color[0]);
saveMark.color[1] = LittleFloat(saveMark.color[1]);
saveMark.color[2] = LittleFloat(saveMark.color[2]);
saveMark.color[3] = LittleFloat(saveMark.color[3]);
saveMark.bDoLighting = LittleLong(saveMark.bDoLighting);
pMark = R_AllocateMarkDef();
if (!pMark) {
break;
}
hShader = RE_RegisterShader(saveMark.shader);
if (hShader) {
pMark->markShader = R_GetShaderByHandle(hShader);
} else {
ri.Printf(PRINT_ALL, "Bad decal shader %s\n", saveMark.shader);
pMark->markShader = tr.defaultShader;
}
//
// Copy settings from the saved mark
// to the newly allocated mark def
//
VectorCopy(saveMark.vPos, pMark->vPos);
VectorCopy(saveMark.vProjection, pMark->vProjection);
pMark->fRadius = saveMark.fRadius;
pMark->fWidthScale = saveMark.fWidthScale;
pMark->fHeightScale = saveMark.fHeightScale;
pMark->fRotation = saveMark.fRotation;
Vector4Copy(saveMark.color, pMark->color);
pMark->bDoLighting = saveMark.bDoLighting;
lm.pCurrentMark = pMark;
//
// Now add the loaded mark
//
if (!R_ApplyLevelDecal(pMark->vPos, pMark->vProjection, qtrue, qtrue, 0)) {
DCLC_Delete();
}
}
} else {
int j;
int iIndex;
dclSavedMarkPoly_t savePoly;
lmPoly_t *pPoly;
shader_t *shader;
qhandle_t hShader;
char szLastShaderName[MAX_RES_NAME];
bmodel_t *pBmodel;
mnode_t *pLeaf;
szLastShaderName[0] = 0;
real_map_time = ri.CM_MapTime();
if (!*real_map_time || strcmp(real_map_time, map_time)) {
ri.Printf(PRINT_ALL, "!!! BSP file is newer than DCL file.\n");
ri.Printf(PRINT_ALL, "!!! The DCL file needs to be updated.\n");
ri.FS_CloseFile(hFile);
ri.Printf(PRINT_ALL, "------ Finished loading DCL file %s -----\n", lm.szDCLFilename);
return;
}
if (!dclHeader.iNumDecals) {
//
// Nothing to load
//
ri.FS_CloseFile(hFile);
ri.Printf(PRINT_ALL, "------ Finished loading DCL file %s -----\n", lm.szDCLFilename);
return;
}
//
// Skip editor stuff
//
ri.FS_Seek(hFile, sizeof(dclSavedMarkDef_t) * dclHeader.iNumDecals, FS_SEEK_CUR);
//
// Load all saved polys
//
lm.pMarkFragments = (lmPoly_t *)ri.Hunk_Alloc(sizeof(lmPoly_t) * dclHeader.iNumFragments, h_low);
for (i = 0; i < dclHeader.iNumFragments; i++) {
polyVert_t *pv;
ri.FS_Read(&savePoly, sizeof(savePoly), hFile);
savePoly.fogIndex = LittleLong(savePoly.fogIndex);
savePoly.iIndex = LittleLong(savePoly.iIndex);
savePoly.iNumVerts = LittleLong(savePoly.iNumVerts);
pPoly = &lm.pMarkFragments[i];
//
// Make sure to register the shader once
//
if (Q_stricmp(savePoly.shader, szLastShaderName)) {
hShader = RE_RegisterShader(savePoly.shader);
if (!hShader) {
pPoly->shader = tr.defaultShader;
pPoly->surf.iIndex = 0;
pPoly->surf.surfaceType = SF_SKIP;
pPoly->surf.numVerts = 0;
continue;
}
shader = R_GetShaderByHandle(hShader);
Q_strncpyz(szLastShaderName, savePoly.shader, sizeof(szLastShaderName));
}
pPoly->shader = shader;
pPoly->surf.iIndex = savePoly.iIndex;
pPoly->surf.surfaceType = SF_MARK_FRAG;
//
// Load vertices
//
pPoly->surf.numVerts = savePoly.iNumVerts;
pPoly->surf.verts = (polyVert_t *)ri.Hunk_Alloc(sizeof(polyVert_t) * savePoly.iNumVerts, h_low);
ri.FS_Read(pPoly->surf.verts, sizeof(polyVert_t) * savePoly.iNumVerts, hFile);
for (j = 0; j < savePoly.iNumVerts; j++) {
pv = &pPoly->surf.verts[j];
pv->xyz[0] = LittleFloat(pv->xyz[0]);
pv->xyz[1] = LittleFloat(pv->xyz[1]);
pv->xyz[2] = LittleFloat(pv->xyz[2]);
pv->st[0] = LittleFloat(pv->st[0]);
pv->st[1] = LittleFloat(pv->st[1]);
}
}
//
// Load all brush models
//
for (i = 0; i < tr.world->numBmodels; i++) {
pBmodel = &tr.world->bmodels[i];
ri.FS_Read(&pBmodel->iNumMarkFragment, sizeof(int), hFile);
pBmodel->iNumMarkFragment = LittleLong(pBmodel->iNumMarkFragment);
//
// Load brush model fragments
//
if (pBmodel->iNumMarkFragment) {
pBmodel->pFirstMarkFragment = (void **)ri.Hunk_Alloc(sizeof(void *) * pBmodel->iNumMarkFragment, h_low);
for (j = 0; j < pBmodel->iNumMarkFragment; j++) {
ri.FS_Read(&iIndex, sizeof(iIndex), hFile);
iIndex = LittleLong(iIndex);
pBmodel->pFirstMarkFragment[j] = &lm.pMarkFragments[iIndex];
}
}
}
//
// Load all leafs
//
for (i = tr.world->numDecisionNodes; i < tr.world->numnodes; i++) {
pLeaf = &tr.world->nodes[i];
ri.FS_Read(&pLeaf->iNumMarkFragment, sizeof(int), h_low);
pLeaf->iNumMarkFragment = LittleLong(pLeaf->iNumMarkFragment);
if (pLeaf->iNumMarkFragment) {
pLeaf->pFirstMarkFragment = (void **)ri.Hunk_Alloc(sizeof(void *) * pLeaf->iNumMarkFragment, h_low);
//
// Load leaf fragments
//
for (j = 0; j < pLeaf->iNumMarkFragment; j++) {
ri.FS_Read(&iIndex, sizeof(iIndex), hFile);
iIndex = LittleLong(iIndex);
pLeaf->pFirstMarkFragment[j] = &lm.pMarkFragments[iIndex];
}
}
}
}
ri.FS_CloseFile(hFile);
ri.Printf(0, "------ Finished loading DCL file %s -----\n", lm.szDCLFilename);
2023-05-09 20:49:04 +02:00
}
2023-05-09 21:58:24 +02:00
/*
=============
R_RemoveEditPolyFromPointerList
=============
*/
void R_RemoveEditPolyFromPointerList(lmEditPoly_t *pPoly, void **pFragmentList, int *piNumFragments)
2023-05-19 02:09:28 +02:00
{
int i;
for (i = 0; i < *piNumFragments; i++, pFragmentList++) {
if (*pFragmentList == pPoly) {
*pFragmentList = NULL;
}
if (!*pFragmentList && i < *piNumFragments - 1) {
*pFragmentList = pFragmentList[1];
pFragmentList[1] = NULL;
}
}
(*piNumFragments)--;
2023-05-19 02:09:28 +02:00
}
/*
=============
R_FreeMarkFragments
Free up all allocated marks
=============
*/
void R_FreeMarkFragments(lmEditMarkDef_t *pMark)
2023-05-19 02:09:28 +02:00
{
lmEditPoly_t *pPoly;
int i;
while ((pPoly = pMark->pMarkEditPolys)) {
pMark->pMarkEditPolys = pPoly->pNextPoly;
if (pPoly->surf.iIndex < 0) {
//
// It's a brush model
//
bmodel_t *pBmodel;
pBmodel = &tr.world->bmodels[-pPoly->surf.iIndex];
R_RemoveEditPolyFromPointerList(pPoly, pBmodel->pFirstMarkFragment, &pBmodel->iNumMarkFragment);
continue;
} else {
//
// It's a leaf
//
mnode_t *pLeaf;
for (i = 0; i < pPoly->iNumLeafs; i++) {
pLeaf = pPoly->pLeafs[i];
R_RemoveEditPolyFromPointerList(pPoly, pLeaf->pFirstMarkFragment, &pLeaf->iNumMarkFragment);
}
}
pPoly->pNextPoly = lm.pFreeEditPolys;
lm.pFreeEditPolys = pPoly;
}
pMark->iNumEditPolys = 0;
2023-05-19 02:09:28 +02:00
}
/*
=============
R_InitMarkDef
Applies default values to the specified mark definition
=============
*/
void R_InitMarkDef(lmEditMarkDef_t *pMark)
2023-05-19 02:09:28 +02:00
{
VectorClear(pMark->vPos);
VectorSet(pMark->vProjection, 0, 0, -1);
pMark->fRadius = 8.0;
pMark->fHeightScale = 1.0;
pMark->fWidthScale = 1.0;
pMark->fRotation = 0.0;
pMark->markShader = tr.defaultShader;
pMark->color[0] = NAN;
pMark->bDoLighting = 1;
pMark->pMarkEditPolys = 0;
pMark->iNumEditPolys = 0;
2023-05-19 02:09:28 +02:00
}
/*
=============
R_AllocateMarkDef
Allocates a new mark definition for editing
=============
*/
lmEditMarkDef_t *R_AllocateMarkDef(void)
2023-05-19 02:09:28 +02:00
{
lmEditMarkDef_t *pNewMark;
int i;
if (!lm.pFreeMarkDefs) {
lm.pFreeMarkDefs = (lmEditMarkDef_t *)ri.Hunk_Alloc(sizeof(lmEditMarkDef_t) * MAX_MARK_FRAGMENTS, h_low);
if (!lm.pFreeMarkDefs) {
return NULL;
}
for (i = 0; i < MAX_MARK_FRAGMENTS - 1; i++) {
lm.pFreeMarkDefs[i].pNextMark = &lm.pFreeMarkDefs[i + 1];
}
lm.pFreeMarkDefs[MAX_MARK_FRAGMENTS - 1].pNextMark = NULL;
}
pNewMark = lm.pFreeMarkDefs;
lm.pFreeMarkDefs = lm.pFreeMarkDefs->pNextMark;
pNewMark->pNextMark = lm.activeMarkDefs.pNextMark;
pNewMark->pPrevMark = &lm.activeMarkDefs;
lm.activeMarkDefs.pNextMark->pPrevMark = pNewMark;
lm.activeMarkDefs.pNextMark = pNewMark;
R_InitMarkDef(pNewMark);
return pNewMark;
2023-05-19 02:09:28 +02:00
}
/*
=============
R_AllocateEditPoly
Allocates a new mark poly for editing
=============
*/
lmEditPoly_t *R_AllocateEditPoly(void)
2023-05-19 02:09:28 +02:00
{
lmEditPoly_t *pNewPoly;
int i;
if (!lm.pFreeEditPolys) {
lm.pFreeEditPolys = (lmEditPoly_t *)ri.Hunk_Alloc(sizeof(lmEditPoly_t) * MAX_MARK_POLYVERTS, h_low);
if (!lm.pFreeEditPolys) {
return NULL;
}
for (i = 0; i < MAX_MARK_POLYVERTS - 1; i++) {
lm.pFreeEditPolys[i].pNextPoly = &lm.pFreeEditPolys[i + 1];
}
lm.pFreeEditPolys[MAX_MARK_POLYVERTS - 1].pNextPoly = NULL;
}
pNewPoly = lm.pFreeEditPolys;
lm.pFreeEditPolys = lm.pFreeEditPolys->pNextPoly;
pNewPoly->surf.surfaceType = SF_MARK_FRAG;
pNewPoly->surf.verts = pNewPoly->verts;
return pNewPoly;
2023-05-19 02:09:28 +02:00
}
/*
=============
R_AddEditPolyToPointerList
=============
*/
qboolean R_AddEditPolyToPointerList(lmEditPoly_t *pPoly, void ***pFragmentList, int *piNumFragments)
2023-05-19 02:09:28 +02:00
{
int i;
if (!*pFragmentList || (*pFragmentList)[*piNumFragments] == (void *)-1) {
void **pNewList = (void **)ri.Malloc((*piNumFragments + MAX_MARK_FRAGMENTS) * sizeof(void *));
if (!pNewList) {
return 0;
}
for (i = 0; i < *piNumFragments + MAX_MARK_FRAGMENTS; i++) {
pNewList[i] = NULL;
}
pNewList[*piNumFragments + MAX_MARK_FRAGMENTS - 1] = (void *)-1;
if (*pFragmentList) {
Com_Memcpy(pNewList, *pFragmentList, *piNumFragments * sizeof(void *));
ri.Free(*pFragmentList);
}
*pFragmentList = pNewList;
}
(*pFragmentList)[*piNumFragments] = pPoly;
(*piNumFragments)++;
return qtrue;
2023-05-19 02:09:28 +02:00
}
/*
=============
R_ApplyLevelDecal
=============
*/
lmEditMarkDef_t *R_ApplyLevelDecal(
const vec3_t vPos, const vec3_t vDir, qboolean bUseCurrent, qboolean bUseCurrentSettings, int iPathIndex
)
2023-05-19 02:09:28 +02:00
{
qboolean bDoLighting;
qboolean bDoMerge;
int i, j, k, l;
int iNumFragments;
float fRadius;
float fSScale, fTScale;
float fR, fG, fB, fA;
float fRoll;
shader_t *shader;
qhandle_t hShader;
lmEditMarkDef_t *pMark;
lmEditPoly_t *pPoly;
vec3_t vOrigin;
vec3_t vProjection;
vec3_t vTmp, vTmp2;
2024-09-04 20:05:48 +02:00
vec3_t vNorm, vNorm2;
vec3_t bounds[2];
markFragment_t markFragments[MAX_MARK_FRAGMENTS];
markFragment_t *mf, *mf2;
polyVert_t verts[MAX_MARK_POLYVERTS];
bmodel_t *pBModel;
mnode_t *pNode;
int iNumNewVerts, iNumMergeVerts;
int iFirstEdgeVert, iFirstEdgeVert2;
polyVert_t *pNewVerts[8];
polyVert_t *pFragVerts;
polyVert_t *pMergeVerts[16];
float fDist, fDist2;
2024-09-04 20:05:48 +02:00
iFirstEdgeVert = iFirstEdgeVert2 = 0;
if (bUseCurrent && !lm.pCurrentMark) {
return NULL;
}
if (iPathIndex) {
shader = R_GetShaderByHandle(lm.treadMark.hTreadShader);
fRadius = lm.treadMark.fWidth;
fTScale = 1.0;
fSScale = 1.0;
fG = 1.0;
fR = 1.0;
fA = 1.0;
fB = 1.0;
bDoLighting = lm.pCurrentMark->bDoLighting;
fRoll = -1.0;
} else if (bUseCurrent && bUseCurrentSettings) {
shader = lm.pCurrentMark->markShader;
fRadius = lm.pCurrentMark->fRadius;
fSScale = lm.pCurrentMark->fWidthScale;
fTScale = lm.pCurrentMark->fHeightScale;
fR = lm.pCurrentMark->color[0];
fG = lm.pCurrentMark->color[1];
fB = lm.pCurrentMark->color[2];
fA = lm.pCurrentMark->color[3];
bDoLighting = lm.pCurrentMark->bDoLighting;
fRoll = lm.pCurrentMark->fRotation;
} else {
hShader = RE_RegisterShader(dcl_shader->string);
if (hShader) {
shader = R_GetShaderByHandle(hShader);
} else {
ri.Printf(PRINT_ALL, "Bad decal shader %s\n", dcl_shader->string);
shader = tr.defaultShader;
}
fRadius = dcl_radius->value;
fSScale = dcl_widthscale->value;
fTScale = dcl_heightscale->value;
fR = Q_clamp_float(dcl_r->value, 0.0, 1.0);
fG = Q_clamp_float(dcl_g->value, 0.0, 1.0);
fB = Q_clamp_float(dcl_b->value, 0.0, 1.0);
fA = Q_clamp_float(dcl_alpha->value, 0.0, 1.0);
bDoLighting = dcl_dolighting->integer != 0;
fRoll = anglemod(dcl_rotation->value);
}
if (fRadius < 0.0) {
fRadius = 0.001;
}
if (fSScale < 0.0) {
fSScale = 0.001;
}
if (fTScale < 0.0) {
fTScale = 0.001;
}
VectorCopy(vPos, vOrigin);
VectorNormalize2(vDir, vProjection);
if (iPathIndex) {
iNumFragments =
ri.CG_PermanentTreadMarkDecal(&lm.treadMark, iPathIndex == 1, bDoLighting, markFragments, verts);
} else {
iNumFragments = ri.CG_PermanentMark(
vOrigin,
vProjection,
anglemod(fRoll + 90),
fSScale * fRadius,
fTScale * fRadius,
fR,
fG,
fB,
fA,
bDoLighting,
0.5,
0.5,
markFragments,
verts
);
}
if (!iNumFragments) {
return NULL;
}
if (bUseCurrent) {
pMark = lm.pCurrentMark;
R_FreeMarkFragments(lm.pCurrentMark);
} else {
pMark = R_AllocateMarkDef();
if (!pMark) {
return NULL;
}
}
VectorCopy(vOrigin, pMark->vPos);
VectorCopy(vProjection, pMark->vProjection);
pMark->markShader = shader;
pMark->fRadius = fRadius;
pMark->fWidthScale = fSScale;
pMark->fHeightScale = fTScale;
VectorSet4(pMark->color, fR, fG, fB, fA);
pMark->bDoLighting = bDoLighting;
pMark->fRotation = fRoll;
if (iPathIndex) {
if (iPathIndex == 1) {
VectorCopy(lm.treadMark.vStartVerts[1], pMark->vPathCorners[0]);
VectorCopy(lm.treadMark.vStartVerts[0], pMark->vPathCorners[1]);
VectorCopy(lm.treadMark.vMidVerts[0], pMark->vPathCorners[2]);
VectorCopy(lm.treadMark.vMidVerts[1], pMark->vPathCorners[3]);
} else {
VectorCopy(lm.treadMark.vMidVerts[1], pMark->vPathCorners[0]);
VectorCopy(lm.treadMark.vMidVerts[0], pMark->vPathCorners[1]);
VectorCopy(lm.treadMark.vEndVerts[0], pMark->vPathCorners[2]);
VectorCopy(lm.treadMark.vEndVerts[1], pMark->vPathCorners[3]);
}
VectorClear(vOrigin);
for (i = 0; i < 4; i++) {
VectorAdd(vOrigin, pMark->vPathCorners[i], vOrigin);
}
vOrigin[0] /= 4.0;
vOrigin[1] /= 4.0;
vOrigin[2] = vOrigin[2] / 4.0 + 16.0;
VectorCopy(vOrigin, pMark->vPos);
}
for (i = 0; i < iNumFragments; i++) {
mf = &markFragments[i];
if (!mf->numPoints) {
continue;
}
assert(mf->numPoints >= 3);
iNumNewVerts = mf->numPoints;
for (j = 0; j < mf->numPoints; ++j) {
pNewVerts[j] = &verts[mf->firstPoint + j];
}
VectorSubtract(pNewVerts[0]->xyz, pNewVerts[1]->xyz, vTmp);
VectorSubtract(pNewVerts[2]->xyz, pNewVerts[1]->xyz, vTmp2);
CrossProduct(vTmp, vTmp2, vNorm);
VectorNormalize(vNorm);
fDist = DotProduct(vNorm, pNewVerts[0]->xyz);
if (mf->iIndex <= 0) {
for (j = i + 1; j < iNumFragments; j++) {
mf2 = &markFragments[j];
if (!mf2->numPoints) {
continue;
}
if (mf2->iIndex > 0 || mf->iIndex == mf2->iIndex) {
continue;
}
pFragVerts = &verts[mf2->firstPoint];
if (fabs(DotProduct(vNorm, pFragVerts->xyz) - fDist) > 0.01) {
continue;
}
VectorSubtract(pFragVerts[0].xyz, pFragVerts[1].xyz, vTmp);
VectorSubtract(pFragVerts[2].xyz, pFragVerts[1].xyz, vTmp2);
CrossProduct(vTmp, vTmp2, vNorm2);
VectorNormalize(vNorm2);
if (!VectorCompare(vNorm, vNorm2)) {
continue;
}
bDoMerge = qfalse;
for (k = 0; k < iNumNewVerts; k++) {
for (l = 0; l < mf2->numPoints; l++) {
if (VectorCompare(pNewVerts[k]->xyz, pFragVerts[(l + 1) % mf2->numPoints].xyz)
&& VectorCompare(pNewVerts[(k + 1) % iNumNewVerts]->xyz, pFragVerts[l].xyz)) {
bDoMerge = 1;
iFirstEdgeVert = k;
iFirstEdgeVert2 = l;
break;
}
}
if (bDoMerge) {
break;
}
}
if (bDoMerge) {
iNumMergeVerts = 0;
for (k = 1; k < iNumNewVerts; ++k) {
pMergeVerts[iNumMergeVerts++] = pNewVerts[(k + iFirstEdgeVert) % iNumNewVerts];
}
for (k = 1; k < mf2->numPoints; k++) {
pMergeVerts[iNumMergeVerts++] = &pFragVerts[(k + iFirstEdgeVert2) % mf2->numPoints];
}
bDoMerge = qtrue;
for (k = 0; k < iNumMergeVerts; k++) {
VectorSubtract(pMergeVerts[(k + 1) % iNumMergeVerts]->xyz, pMergeVerts[k]->xyz, vTmp);
CrossProduct(vNorm, vTmp, vNorm2);
VectorNormalize(vNorm2);
for (l = 0; l < iNumMergeVerts; l++) {
if (l == k) {
break;
}
if (DotProduct(pMergeVerts[l]->xyz, vNorm)
> DotProduct(pMergeVerts[k]->xyz, vNorm2) + 0.01) {
bDoMerge = qfalse;
break;
}
}
if (!bDoMerge) {
break;
}
}
if (bDoMerge) {
for (k = 0; k < iNumMergeVerts; k++) {
VectorSubtract(pMergeVerts[(k + 1) % iNumMergeVerts]->xyz, pMergeVerts[k]->xyz, vTmp);
VectorNormalize(vTmp);
VectorSubtract(
pMergeVerts[k]->xyz, pMergeVerts[(iNumMergeVerts + k - 1) % iNumMergeVerts]->xyz, vTmp
);
VectorNormalize(vTmp2);
if (DotProduct(vTmp, vTmp2) > 0.999) {
for (l = k + 1; l < iNumMergeVerts; l++) {
pMergeVerts[l - 1] = pMergeVerts[l];
}
}
}
if (iNumMergeVerts <= 8) {
iNumNewVerts = iNumMergeVerts;
for (k = 0; k < iNumMergeVerts; k++) {
pNewVerts[k] = pMergeVerts[k];
}
mf2->numPoints = 0;
}
}
}
}
}
pPoly = R_AllocateEditPoly();
if (!pPoly) {
return pMark;
}
pPoly->shader = shader;
pPoly->surf.numVerts = iNumNewVerts;
for (j = 0; j < iNumNewVerts; j++) {
memcpy(&pPoly->verts[j], pNewVerts[j], sizeof(polyVert_t));
}
pPoly->surf.iIndex = mf->iIndex;
pPoly->iNumLeafs = 0;
if (mf->iIndex <= 0 || dcl_doworld->integer && dcl_doterrain->integer) {
if (mf->iIndex >= 0) {
if (dcl_doworld->integer) {
ClearBounds(bounds[0], bounds[1]);
VectorClear(vOrigin);
for (j = 0; j < pPoly->surf.numVerts; j++) {
VectorAdd(pPoly->verts[j].xyz, vNorm, vTmp);
pNode = R_PointInLeaf(vTmp);
if (pNode && pNode->contents != -1 && !(pNode->contents & CONTENTS_SOLID)) {
for (k = 0; k < pPoly->iNumLeafs; k++) {
if (pPoly->pLeafs[k] == pNode) {
break;
}
}
if (k == pPoly->iNumLeafs) {
R_AddEditPolyToPointerList(pPoly, &pNode->pFirstMarkFragment, &pNode->iNumMarkFragment);
pPoly->pLeafs[pPoly->iNumLeafs] = pNode;
pPoly->iNumLeafs++;
}
}
AddPointToBounds(vTmp, bounds[0], bounds[1]);
VectorAdd(vOrigin, vTmp, vOrigin);
}
VectorScale(vOrigin, 1.0 / pPoly->surf.numVerts, vOrigin);
pNode = R_PointInLeaf(vOrigin);
for (k = 0; k < pPoly->iNumLeafs; k++) {
if (pPoly->pLeafs[k] == pNode) {
break;
}
}
if (k == pPoly->iNumLeafs) {
R_AddEditPolyToPointerList(pPoly, &pNode->pFirstMarkFragment, &pNode->iNumMarkFragment);
pPoly->pLeafs[pPoly->iNumLeafs] = pNode;
pPoly->iNumLeafs++;
}
pPoly->pNextPoly = pMark->pMarkEditPolys;
pMark->pMarkEditPolys = pPoly;
pMark->iNumEditPolys++;
}
} else {
if (dcl_dobmodels->integer) {
R_AddEditPolyToPointerList(
pPoly,
&tr.world->bmodels[-mf->iIndex].pFirstMarkFragment,
&tr.world->bmodels[-mf->iIndex].iNumMarkFragment
);
pPoly->pNextPoly = pMark->pMarkEditPolys;
pMark->pMarkEditPolys = pPoly;
pMark->iNumEditPolys++;
}
}
}
}
return pMark;
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_Make
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_Make(void)
{
lmEditMarkDef_t *pNewMark;
vec3_t vProjection;
VectorNegate(tr.refdef.viewaxis[0], vProjection);
pNewMark = R_ApplyLevelDecal(tr.refdef.vieworg, vProjection, qfalse, qfalse, 0);
if (pNewMark) {
lm.pCurrentMark = pNewMark;
lm.bAutoApplySettings = qtrue;
}
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_Reapply
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_Reapply(void)
{
lmEditMarkDef_t *pNewMark;
if (!lm.pCurrentMark) {
return;
}
if (lm.pCurrentMark->fRotation == -1) {
return;
}
pNewMark = R_ApplyLevelDecal(lm.pCurrentMark->vPos, lm.pCurrentMark->vProjection, qtrue, qfalse, 0);
if (pNewMark) {
lm.pCurrentMark = pNewMark;
lm.bAutoApplySettings = qtrue;
}
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_Delete
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_Delete(void)
{
lmEditMarkDef_t *pMark;
if (!lm.pCurrentMark) {
return;
}
pMark = lm.pCurrentMark;
R_FreeMarkFragments(lm.pCurrentMark);
lm.pCurrentMark = pMark->pNextMark;
if (lm.pCurrentMark == &lm.activeMarkDefs) {
lm.pCurrentMark = pMark->pPrevMark;
if (lm.pCurrentMark == &lm.activeMarkDefs) {
lm.pCurrentMark = NULL;
}
}
pMark->pPrevMark->pNextMark = pMark->pNextMark;
pMark->pNextMark->pPrevMark = pMark->pPrevMark;
pMark->pNextMark = lm.pFreeMarkDefs;
pMark->pPrevMark = 0;
lm.pFreeMarkDefs = pMark;
if (dcl_autogetinfo->integer) {
lm.bAutoApplySettings = qtrue;
DCLC_GetInfo();
} else {
lm.bAutoApplySettings = qfalse;
}
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_SelectNext
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_SelectNext(void)
{
lmEditMarkDef_t *pMark;
lm.bAutoApplySettings = dcl_autogetinfo->integer != 0;
if (!lm.pCurrentMark) {
if (lm.activeMarkDefs.pPrevMark != &lm.activeMarkDefs) {
lm.pCurrentMark = lm.activeMarkDefs.pPrevMark;
}
if (dcl_autogetinfo->integer) {
DCLC_GetInfo();
}
return;
}
pMark = lm.pCurrentMark;
lm.pCurrentMark = lm.pCurrentMark->pPrevMark;
if (lm.pCurrentMark == &lm.activeMarkDefs) {
lm.pCurrentMark = lm.activeMarkDefs.pPrevMark;
if (lm.activeMarkDefs.pPrevMark == &lm.activeMarkDefs) {
lm.pCurrentMark = pMark;
}
}
if (dcl_autogetinfo->integer) {
DCLC_GetInfo();
}
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_SelectPrev
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_SelectPrev(void)
{
lmEditMarkDef_t *pMark;
lm.bAutoApplySettings = dcl_autogetinfo->integer != 0;
if (!lm.pCurrentMark) {
if (lm.activeMarkDefs.pNextMark != &lm.activeMarkDefs) {
lm.pCurrentMark = lm.activeMarkDefs.pNextMark;
}
if (dcl_autogetinfo->integer) {
DCLC_GetInfo();
}
return;
}
pMark = lm.pCurrentMark;
lm.pCurrentMark = lm.pCurrentMark->pNextMark;
if (lm.pCurrentMark == &lm.activeMarkDefs) {
lm.pCurrentMark = lm.activeMarkDefs.pNextMark;
if (lm.activeMarkDefs.pNextMark == &lm.activeMarkDefs) {
lm.pCurrentMark = pMark;
}
}
if (dcl_autogetinfo->integer) {
DCLC_GetInfo();
}
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_SelectCh
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_SelectCh(void)
{
// Nothing to do
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_SelectChNxt
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_SelectChNxt(void)
{
// Nothing to do
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_GetInfo
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_GetInfo(void)
{
if (!lm.pCurrentMark) {
return;
}
if (lm.pCurrentMark->fRotation == -1) {
Cvar_Set("dcl_shader", lm.pCurrentMark->markShader->name);
Cvar_SetValue("dcl_radius", lm.pCurrentMark->fRadius);
Cvar_SetValue("dcl_widthscale", lm.pCurrentMark->fWidthScale);
} else {
Cvar_Set("dcl_shader", lm.pCurrentMark->markShader->name);
Cvar_SetValue("dcl_radius", lm.pCurrentMark->fRadius);
Cvar_SetValue("dcl_heightscale", lm.pCurrentMark->fHeightScale);
Cvar_SetValue("dcl_heightscale", lm.pCurrentMark->fHeightScale);
Cvar_SetValue("dcl_widthscale", lm.pCurrentMark->fWidthScale);
Cvar_SetValue("dcl_rotation", lm.pCurrentMark->fRotation);
Cvar_SetValue("dcl_r", lm.pCurrentMark->color[0]);
Cvar_SetValue("dcl_g", lm.pCurrentMark->color[1]);
Cvar_SetValue("dcl_b", lm.pCurrentMark->color[2]);
}
Cvar_SetValue("dcl_alpha", lm.pCurrentMark->color[3]);
Cvar_SetValue("dcl_dolighting", lm.pCurrentMark->bDoLighting);
lm.bAutoApplySettings = qtrue;
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_ShiftUp
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_ShiftUp(void)
{
vec3_t vAngles;
vec3_t vUp;
if (!lm.pCurrentMark) {
return;
}
if (lm.pCurrentMark->fRotation == -1) {
return;
}
vectoangles(lm.pCurrentMark->vProjection, vAngles);
AngleVectors(vAngles, NULL, NULL, vUp);
VectorMA(lm.pCurrentMark->vPos, dcl_shiftstep->value, vUp, lm.pCurrentMark->vPos);
DCLC_Reapply();
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_ShiftDown
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_ShiftDown(void)
{
vec3_t vAngles;
vec3_t vUp;
if (!lm.pCurrentMark) {
return;
}
if (lm.pCurrentMark->fRotation == -1) {
return;
}
vectoangles(lm.pCurrentMark->vProjection, vAngles);
AngleVectors(vAngles, NULL, NULL, vUp);
VectorMA(lm.pCurrentMark->vPos, -dcl_shiftstep->value, vUp, lm.pCurrentMark->vPos);
DCLC_Reapply();
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_ShiftLeft
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_ShiftLeft(void)
{
vec3_t vAngles;
vec3_t vRight;
if (!lm.pCurrentMark) {
return;
}
if (lm.pCurrentMark->fRotation == -1) {
return;
}
vectoangles(lm.pCurrentMark->vProjection, vAngles);
AngleVectors(vAngles, NULL, vRight, NULL);
VectorMA(lm.pCurrentMark->vPos, -dcl_shiftstep->value, vRight, lm.pCurrentMark->vPos);
DCLC_Reapply();
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_ShiftRight
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_ShiftRight(void)
{
vec3_t vAngles;
vec3_t vRight;
if (!lm.pCurrentMark) {
return;
}
if (lm.pCurrentMark->fRotation == -1) {
return;
}
vectoangles(lm.pCurrentMark->vProjection, vAngles);
AngleVectors(vAngles, NULL, vRight, NULL);
VectorMA(lm.pCurrentMark->vPos, dcl_shiftstep->value, vRight, lm.pCurrentMark->vPos);
DCLC_Reapply();
2023-05-19 02:09:28 +02:00
}
/*
=============
DCLC_RandomRoll
=============
*/
2023-05-19 02:09:28 +02:00
void DCLC_RandomRoll(void)
{
Cvar_SetValue("dcl_rotation", random() * 360.0);
2023-05-19 02:09:28 +02:00
}
/*
=============
R_LevelMarksInit
Initialize the level mark system with variables and commands
=============
*/
2023-05-09 21:58:24 +02:00
void R_LevelMarksInit()
{
memset(&lm, 0, sizeof(lm));
dcl_editmode = ri.Cvar_Get("dcl_editmode", "0", CVAR_LATCH);
dcl_showcurrent = ri.Cvar_Get("dcl_showcurrent", "1", 0);
dcl_autogetinfo = ri.Cvar_Get("dcl_autogetinfo", "1", 0);
dcl_shiftstep = ri.Cvar_Get("dcl_shiftstep", "4", 0);
dcl_shader = ri.Cvar_Get("dcl_shader", "blastmark", 0);
dcl_radius = ri.Cvar_Get("dcl_radius", "16", 0);
dcl_heightscale = ri.Cvar_Get("dcl_heightscale", "1", 0);
dcl_widthscale = ri.Cvar_Get("dcl_widthscale", "1", 0);
dcl_rotation = ri.Cvar_Get("dcl_rotation", "0", 0);
dcl_r = ri.Cvar_Get("dcl_r", "1", 0);
dcl_g = ri.Cvar_Get("dcl_g", "1", 0);
dcl_b = ri.Cvar_Get("dcl_b", "1", 0);
dcl_alpha = ri.Cvar_Get("dcl_alpha", "1", 0);
dcl_dolighting = ri.Cvar_Get("dcl_dolighting", "1", 0);
dcl_doworld = ri.Cvar_Get("dcl_doworld", "1", 0);
dcl_doterrain = ri.Cvar_Get("dcl_doterrain", "1", 0);
dcl_dobmodels = ri.Cvar_Get("dcl_dobmodels", "1", 0);
dcl_dostring = ri.Cvar_Get("dcl_dostring", "apply to all", 0);
dcl_pathmode = ri.Cvar_Get("dcl_pathmode", "0", 0);
dcl_maxsegment = ri.Cvar_Get("dcl_maxsegment", "512", 0);
dcl_minsegment = ri.Cvar_Get("dcl_minsegment", "24", 0);
dcl_maxoffset = ri.Cvar_Get("dcl_maxoffset", "10", 0);
dcl_texturescale = ri.Cvar_Get("dcl_texturescale", "32", 0);
if (dcl_editmode->integer) {
ri.Cmd_AddCommand("dclc_make", DCLC_Make);
ri.Cmd_AddCommand("dclc_reapply", DCLC_Reapply);
ri.Cmd_AddCommand("dclc_delete", DCLC_Delete);
ri.Cmd_AddCommand("dclc_selectnext", DCLC_SelectNext);
ri.Cmd_AddCommand("dclc_selectprev", DCLC_SelectPrev);
ri.Cmd_AddCommand("dclc_selectch", DCLC_SelectCh);
ri.Cmd_AddCommand("dclc_selectchnxt", DCLC_SelectChNxt);
ri.Cmd_AddCommand("dclc_getinfo", DCLC_GetInfo);
ri.Cmd_AddCommand("dclc_shiftup", DCLC_ShiftUp);
ri.Cmd_AddCommand("dclc_shiftdown", DCLC_ShiftDown);
ri.Cmd_AddCommand("dclc_shiftleft", DCLC_ShiftLeft);
ri.Cmd_AddCommand("dclc_shiftright", DCLC_ShiftRight);
ri.Cmd_AddCommand("dclc_randomroll", DCLC_RandomRoll);
ri.Cmd_AddCommand("dclc_save", DCLC_Save);
lm.activeMarkDefs.pNextMark = &lm.activeMarkDefs;
lm.activeMarkDefs.pPrevMark = &lm.activeMarkDefs;
}
2023-05-09 21:58:24 +02:00
}
/*
=============
R_LevelMarksFree
Remove level marks commands and deallocate all memory
=============
*/
2023-05-09 21:58:24 +02:00
void R_LevelMarksFree()
{
int i;
if (!dcl_editmode->integer) {
return;
}
ri.Cmd_RemoveCommand("dclc_make");
ri.Cmd_RemoveCommand("dclc_reapply");
ri.Cmd_RemoveCommand("dclc_delete");
ri.Cmd_RemoveCommand("dclc_selectnext");
ri.Cmd_RemoveCommand("dclc_selectprev");
ri.Cmd_RemoveCommand("dclc_selectch");
ri.Cmd_RemoveCommand("dclc_selectchnxt");
ri.Cmd_RemoveCommand("dclc_getinfo");
ri.Cmd_RemoveCommand("dclc_shiftup");
ri.Cmd_RemoveCommand("dclc_shiftdown");
ri.Cmd_RemoveCommand("dclc_shiftleft");
ri.Cmd_RemoveCommand("dclc_shiftright");
ri.Cmd_RemoveCommand("dclc_randomroll");
ri.Cmd_RemoveCommand("dclc_save");
if (tr.world) {
for (i = 0; i < tr.world->numBmodels; i++) {
bmodel_t *pBmodel = &tr.world->bmodels[i];
if (pBmodel->iNumMarkFragment && pBmodel->pFirstMarkFragment) {
ri.Free(pBmodel->pFirstMarkFragment);
pBmodel->pFirstMarkFragment = NULL;
pBmodel->iNumMarkFragment = 0;
}
}
for (i = tr.world->numDecisionNodes; i < tr.world->numnodes; i++) {
mnode_t *pLeaf = &tr.world->nodes[i];
2024-09-08 00:56:03 +02:00
if (pLeaf->iNumMarkFragment && pLeaf->pFirstMarkFragment) {
ri.Free(pLeaf->pFirstMarkFragment);
pLeaf->pFirstMarkFragment = NULL;
pLeaf->iNumMarkFragment = 0;
}
}
}
}
/*
=============
R_InitTreadMark
=============
*/
static void R_InitTreadMark(treadMark_t *treadMark)
{
treadMark->iReferenceNumber = 0;
treadMark->iLastTime = 0;
treadMark->hTreadShader = 0;
treadMark->iState = 0;
treadMark->fWidth = 0;
VectorClear(treadMark->vStartDir);
VectorClear(treadMark->vStartVerts[0]);
VectorClear(treadMark->vStartVerts[1]);
treadMark->fStartTexCoord = 0;
treadMark->fStartAlpha = 0;
VectorClear(treadMark->vMidPos);
VectorClear(treadMark->vMidVerts[0]);
VectorClear(treadMark->vMidVerts[1]);
treadMark->fMidTexCoord = 0;
treadMark->fMidAlpha = 0;
VectorClear(treadMark->vEndPos);
VectorClear(treadMark->vEndVerts[0]);
VectorClear(treadMark->vEndVerts[1]);
treadMark->fEndTexCoord = 0;
treadMark->fEndAlpha = 0;
2023-05-09 21:58:24 +02:00
}
2023-05-15 14:21:16 +02:00
/*
=============
R_UpdateLevelMarksSystem
Draw lines for editing
=============
*/
2023-05-15 14:21:16 +02:00
void R_UpdateLevelMarksSystem()
{
int iReturn;
float fAlpha;
float fTexScale;
if (!dcl_editmode->integer) {
return;
}
if (dcl_pathmode->integer) {
if (!lm.bPathLayingMode) {
R_InitTreadMark(&lm.treadMark);
lm.treadMark.iLastTime = -1000;
lm.pTreadMarkStartDecal = NULL;
lm.pTreadMarkEndDecal = NULL;
lm.bPathLayingMode = qtrue;
}
lm.treadMark.hTreadShader = RE_RegisterShader(dcl_shader->string);
lm.treadMark.fWidth = Q_clamp_float(dcl_radius->value * dcl_widthscale->value, 2, 1024);
fAlpha = Q_clamp_float(dcl_alpha->value, 0, 1);
fTexScale = 1.0 / dcl_texturescale->value;
iReturn = ri.CG_PermanentUpdateTreadMark(
&lm.treadMark, fAlpha, dcl_minsegment->value, dcl_maxsegment->value, dcl_maxoffset->value, fTexScale
);
if (iReturn == 1) {
lm.pTreadMarkStartDecal = lm.pTreadMarkEndDecal;
lm.pTreadMarkEndDecal = NULL;
ri.CG_PermanentUpdateTreadMark(
&lm.treadMark, fAlpha, dcl_minsegment->value, dcl_maxsegment->value, dcl_maxoffset->value, fTexScale
);
}
if (iReturn != -1) {
if (!lm.pTreadMarkEndDecal) {
lm.pTreadMarkEndDecal = R_AllocateMarkDef();
}
if (lm.pTreadMarkEndDecal) {
lm.pCurrentMark = lm.pTreadMarkEndDecal;
lm.pCurrentMark = R_ApplyLevelDecal(vec_upwards, vec_upwards, qtrue, qtrue, 2);
if (!lm.pCurrentMark) {
R_FreeMarkFragments(lm.pTreadMarkEndDecal);
}
}
if (lm.treadMark.iState == 3) {
if (!lm.pTreadMarkStartDecal) {
lm.pTreadMarkStartDecal = R_AllocateMarkDef();
}
if (lm.pTreadMarkStartDecal) {
lm.pCurrentMark = lm.pTreadMarkStartDecal;
lm.pCurrentMark = R_ApplyLevelDecal(vec_upwards, vec_upwards, qtrue, qtrue, 1);
if (!lm.pCurrentMark) {
R_FreeMarkFragments(lm.pTreadMarkStartDecal);
}
}
}
}
if (lm.pTreadMarkEndDecal) {
lm.pCurrentMark = lm.pTreadMarkEndDecal;
} else if (lm.pTreadMarkStartDecal) {
lm.pCurrentMark = lm.pTreadMarkStartDecal;
}
} else if (lm.bPathLayingMode) {
lm.bPathLayingMode = qfalse;
}
if (lm.bAutoApplySettings && lm.pCurrentMark && lm.pCurrentMark->fRotation != -1) {
qboolean bDoUpdate = qfalse;
float f;
if (dcl_shader->modified) {
shader_t *shader;
qhandle_t hShader;
hShader = RE_RegisterShader(dcl_shader->string);
if (hShader) {
shader = R_GetShaderByHandle(hShader);
if (shader != tr.defaultShader && lm.pCurrentMark->markShader != shader) {
lm.pCurrentMark->markShader = shader;
// Set the decal radius
Cvar_SetValue("dcl_radius", 16.0);
dcl_radius->modified = qfalse;
// Set the decal scale
Cvar_SetValue(
"dcl_heightscale", shader->unfoggedStages[0]->bundle[0].image[0]->height / 16.0 * 0.5
);
Cvar_SetValue("dcl_widthscale", shader->unfoggedStages[0]->bundle[0].image[0]->width / 16.0 * 0.5);
bDoUpdate = qtrue;
}
}
dcl_shader->modified = 0;
}
if (dcl_radius->modified) {
f = dcl_radius->value;
if (lm.pCurrentMark->fRadius != f) {
lm.pCurrentMark->fRadius = f;
bDoUpdate = qtrue;
}
dcl_radius->modified = qfalse;
}
if (dcl_heightscale->modified) {
f = dcl_heightscale->value;
if (lm.pCurrentMark->fHeightScale != f) {
lm.pCurrentMark->fHeightScale = f;
bDoUpdate = qtrue;
}
dcl_heightscale->modified = qfalse;
}
if (dcl_widthscale->modified) {
f = dcl_widthscale->value;
if (lm.pCurrentMark->fWidthScale != f) {
lm.pCurrentMark->fWidthScale = f;
bDoUpdate = qtrue;
}
dcl_widthscale->modified = qfalse;
}
if (dcl_rotation->modified) {
f = anglemod(dcl_rotation->value);
if (lm.pCurrentMark->fRotation != f) {
lm.pCurrentMark->fRotation = f;
bDoUpdate = qtrue;
}
dcl_rotation->modified = qfalse;
}
if (dcl_r->modified) {
f = Q_clamp_float(dcl_r->value, 0, 1);
if (lm.pCurrentMark->color[0] != f) {
lm.pCurrentMark->color[0] = f;
bDoUpdate = qtrue;
}
dcl_r->modified = qfalse;
}
if (dcl_g->modified) {
f = Q_clamp_float(dcl_g->value, 0, 1);
if (lm.pCurrentMark->color[1] != f) {
lm.pCurrentMark->color[1] = f;
bDoUpdate = qtrue;
}
dcl_g->modified = qfalse;
}
if (dcl_b->modified) {
f = Q_clamp_float(dcl_b->value, 0, 1);
if (lm.pCurrentMark->color[2] != f) {
lm.pCurrentMark->color[2] = f;
bDoUpdate = qtrue;
}
dcl_b->modified = qfalse;
}
if (dcl_alpha->modified) {
f = Q_clamp_float(dcl_alpha->value, 0, 1);
if (lm.pCurrentMark->color[3] != f) {
lm.pCurrentMark->color[3] = f;
bDoUpdate = qtrue;
}
dcl_alpha->modified = qfalse;
}
if (dcl_dolighting->modified) {
qboolean bDoLighting = dcl_dolighting->integer != 0;
if (lm.pCurrentMark->bDoLighting != bDoLighting) {
lm.pCurrentMark->bDoLighting = bDoLighting;
bDoUpdate = 1;
}
dcl_dolighting->modified = 0;
}
if (bDoUpdate) {
lm.pCurrentMark = R_ApplyLevelDecal(lm.pCurrentMark->vPos, lm.pCurrentMark->vProjection, qtrue, qtrue, 0);
}
}
if (dcl_showcurrent->integer && lm.pCurrentMark && lm.pCurrentMark->iNumEditPolys) {
float fColor;
float fLength;
int j;
int iEntNum;
lmEditPoly_t *pPoly;
polyVert_t *pVert, *pVert2;
polyVert_t verts[8];
trRefEntity_t *ent;
bmodel_t *bmodel, *lastbmodel;
model_t *pModel;
vec3_t vOrigin;
vec3_t axis[3];
vec3_t vPos, vPos2;
vec3_t polyaxis[3];
iEntNum = 0;
ent = NULL;
lastbmodel = NULL;
vectoangles(lm.pCurrentMark->vProjection, vPos);
vPos[2] = anglemod(vPos[2] + lm.pCurrentMark->fRotation);
AngleVectorsLeft(vPos, polyaxis[0], polyaxis[1], polyaxis[2]);
VectorNormalize2(lm.pCurrentMark->vProjection, polyaxis[0]);
PerpendicularVector(polyaxis[1], polyaxis[0]);
RotatePointAroundVector(polyaxis[2], polyaxis[0], polyaxis[1], anglemod(lm.pCurrentMark->fRotation + 90.0));
CrossProduct(polyaxis[0], polyaxis[2], polyaxis[1]);
fColor = sin(tr.refdef.time / 500.0) / 4.0 + 0.6;
for (pPoly = lm.pCurrentMark->pMarkEditPolys; pPoly; pPoly = pPoly->pNextPoly) {
if (pPoly->surf.iIndex > 0) {
for (j = 0; j < pPoly->surf.numVerts; j++) {
VectorMA(pPoly->verts[j % pPoly->surf.numVerts].xyz, 0.1, polyaxis[0], vPos);
VectorMA(pPoly->verts[(j + 1) % pPoly->surf.numVerts].xyz, 0.1, polyaxis[0], vPos2);
R_DebugLine(vPos, vPos2, fColor, fColor, fColor, 1.0);
VectorMA(vPos, 8.0, polyaxis[0], vPos2);
R_DebugLine(vPos, vPos2, fColor, 1.0 - fColor, 0.1, 1.0);
}
} else if (pPoly->surf.iIndex < 0) {
bmodel = &tr.world->bmodels[-pPoly->surf.iIndex];
if (bmodel != lastbmodel) {
//
// Find the matching entity
//
ent = &tr.refdef.entities[0];
for (iEntNum = 0; iEntNum < tr.refdef.num_entities; iEntNum++) {
ent = &tr.refdef.entities[iEntNum];
pModel = R_GetModelByHandle(ent->e.hModel);
if (pModel->type == MOD_BRUSH && pModel->d.bmodel == bmodel) {
break;
}
}
VectorCopy(ent->e.origin, vOrigin);
AxisCopy(ent->e.axis, axis);
}
if (iEntNum != tr.refdef.num_entities) {
for (j = 0; j < pPoly->surf.numVerts; j++) {
MatrixTransformVector(pPoly->verts[j].xyz, axis, pPoly->verts[j].xyz);
VectorAdd(pPoly->verts[j].xyz, vOrigin, pPoly->verts[j].xyz);
}
for (j = 0; j < pPoly->surf.numVerts; j++) {
VectorMA(pPoly->verts[j % pPoly->surf.numVerts].xyz, 0.1, polyaxis[0], vPos);
VectorMA(pPoly->verts[(j + 1) % pPoly->surf.numVerts].xyz, 0.1, polyaxis[0], vPos2);
R_DebugLine(vPos, vPos2, fColor, fColor, fColor, 1.0);
VectorMA(vPos, 8.0, polyaxis[0], vPos2);
R_DebugLine(vPos, vPos2, 0.1, 1.0 - fColor, fColor, 1.0);
}
}
} else {
for (j = 0; j < pPoly->surf.numVerts; j++) {
VectorMA(pPoly->verts[j % pPoly->surf.numVerts].xyz, 0.1, polyaxis[0], vPos);
VectorMA(pPoly->verts[(j + 1) % pPoly->surf.numVerts].xyz, 0.1, polyaxis[0], vPos2);
R_DebugLine(vPos, vPos2, fColor, fColor, fColor, 1.0);
VectorMA(vPos, 8.0, polyaxis[0], vPos2);
R_DebugLine(vPos, vPos2, 0.1, 1.0 - fColor, fColor, 1.0);
}
}
}
//
// Draw the front
//
fLength = (lm.pCurrentMark->fWidthScale + lm.pCurrentMark->fHeightScale) * lm.pCurrentMark->fRadius * 0.75;
if (fLength < 4) {
fLength = 4;
}
VectorMA(lm.pCurrentMark->vPos, fLength, polyaxis[0], vPos);
R_DebugLine(lm.pCurrentMark->vPos, vPos, 0.1, fColor, 1.0 - fColor, 1.0);
//
// Draw the right
//
fLength = lm.pCurrentMark->fRadius * lm.pCurrentMark->fWidthScale;
if (fLength < 1) {
fLength = 1;
}
VectorMA(lm.pCurrentMark->vPos, fLength, polyaxis[1], vOrigin);
VectorMA(lm.pCurrentMark->vPos, -fLength, polyaxis[1], vPos);
R_DebugLine(vOrigin, vPos, 0.1, fColor, 1.0 - fColor, 1.0);
//
// Draw the up
//
fLength = lm.pCurrentMark->fRadius * lm.pCurrentMark->fHeightScale;
if (fLength < 1) {
fLength = 1;
}
VectorMA(lm.pCurrentMark->vPos, fLength, polyaxis[2], vOrigin);
VectorMA(lm.pCurrentMark->vPos, -fLength, polyaxis[2], vPos);
R_DebugLine(vOrigin, vPos, 0.1, fColor, 1.0 - fColor, 1.0);
}
2023-05-15 14:21:16 +02:00
}
2023-05-19 03:13:23 +02:00
/*
=============
R_AddPermanentMarkFragmentSurfaces
=============
*/
void R_AddPermanentMarkFragmentSurfaces(void **pFirstMarkFragment, int iNumMarkFragment)
{
int i;
lmPoly_t *pPoly;
for (i = 0; i < iNumMarkFragment; i++) {
pPoly = (lmPoly_t *)pFirstMarkFragment[i];
if (pPoly->viewCount == tr.viewCount) {
continue;
}
pPoly->viewCount = tr.viewCount;
R_AddDrawSurf(&pPoly->surf.surfaceType, pPoly->shader, 0);
}
2023-05-19 03:13:23 +02:00
}