mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 13:47:58 +03:00
2001 lines
59 KiB
C
2001 lines
59 KiB
C
![]() |
/*
|
||
|
===========================================================================
|
||
|
Copyright (C) 2024 the OpenMoHAA team
|
||
|
|
||
|
This file is part of OpenMoHAA source code.
|
||
|
|
||
|
OpenMoHAA source code is free software; you can redistribute it
|
||
|
and/or modify it under the terms of the GNU General Public License as
|
||
|
published by the Free Software Foundation; either version 2 of the License,
|
||
|
or (at your option) any later version.
|
||
|
|
||
|
OpenMoHAA source code is distributed in the hope that it will be
|
||
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with OpenMoHAA source code; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
===========================================================================
|
||
|
*/
|
||
|
|
||
|
// tr_marks_pernanent.c: permanent marks
|
||
|
|
||
|
#include "tr_local.h"
|
||
|
|
||
|
#define DCL_FILE_SIGNATURE *(int *)"DCL "
|
||
|
#define DCL_FILE_OLD_VERSION 1
|
||
|
#define DCL_FILE_VERSION 2
|
||
|
|
||
|
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;
|
||
|
} 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];
|
||
|
} lmEditMarkDef_t;
|
||
|
|
||
|
typedef struct lmPoly_s {
|
||
|
srfMarkFragment_t surf;
|
||
|
shader_t *shader;
|
||
|
int viewCount;
|
||
|
} 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;
|
||
|
} lmGlobals_t;
|
||
|
|
||
|
typedef struct {
|
||
|
int ident;
|
||
|
int version;
|
||
|
int checksum;
|
||
|
int iNumDecals;
|
||
|
int iNumFragments;
|
||
|
} 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;
|
||
|
} dclSavedMarkDef_t;
|
||
|
|
||
|
typedef struct {
|
||
|
char shader[MAX_RES_NAME];
|
||
|
int fogIndex;
|
||
|
int iIndex;
|
||
|
int iNumVerts;
|
||
|
} 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;
|
||
|
|
||
|
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
|
||
|
=============
|
||
|
*/
|
||
|
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 = ri.FS_OpenFileWrite(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);
|
||
|
|
||
|
ri.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));
|
||
|
ri.FS_Write(map_time, sizeof(map_time), hFile);
|
||
|
|
||
|
if (!iNumDecals) {
|
||
|
//
|
||
|
// Nothing to write
|
||
|
//
|
||
|
ri.FS_CloseFile(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);
|
||
|
|
||
|
ri.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);
|
||
|
|
||
|
ri.FS_Write(&savePoly, sizeof(savePoly), hFile);
|
||
|
ri.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;
|
||
|
ri.FS_Write(&littleValue, sizeof(littleValue), hFile);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
littleValue = LittleLong(pBmodel->iNumMarkFragment);
|
||
|
ri.FS_Write(&littleValue, sizeof(int), hFile);
|
||
|
|
||
|
for (j = 0; j < pBmodel->iNumMarkFragment; i++) {
|
||
|
littleValue = ((lmPoly_t *)pBmodel->pFirstMarkFragment[j])->viewCount;
|
||
|
ri.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;
|
||
|
ri.FS_Write(&littleValue, sizeof(littleValue), hFile);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
littleValue = LittleLong(pLeaf->iNumMarkFragment);
|
||
|
ri.FS_Write(&littleValue, sizeof(int), hFile);
|
||
|
|
||
|
for (j = 0; j < pLeaf->iNumMarkFragment; j++) {
|
||
|
littleValue = ((lmPoly_t *)pLeaf->pFirstMarkFragment[j])->viewCount;
|
||
|
ri.FS_Write(&littleValue, sizeof(int), hFile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ri.FS_CloseFile(hFile);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_LevelMarksLoad
|
||
|
|
||
|
Load level marks from a DCL file
|
||
|
=============
|
||
|
*/
|
||
|
void R_LevelMarksLoad(const char *szBSPName)
|
||
|
{
|
||
|
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) {
|
||
|
// Added in 2.30
|
||
|
// Don't mess with decal loading, so drop immediately
|
||
|
Com_Error(ERR_DROP, "Decal file lists non-existant shader: %s", savePoly.shader);
|
||
|
|
||
|
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), hFile);
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_RemoveEditPolyFromPointerList
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
void R_RemoveEditPolyFromPointerList(lmEditPoly_t *pPoly, void **pFragmentList, int *piNumFragments)
|
||
|
{
|
||
|
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)--;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_FreeMarkFragments
|
||
|
|
||
|
Free up all allocated marks
|
||
|
=============
|
||
|
*/
|
||
|
void R_FreeMarkFragments(lmEditMarkDef_t *pMark)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_InitMarkDef
|
||
|
|
||
|
Applies default values to the specified mark definition
|
||
|
=============
|
||
|
*/
|
||
|
void R_InitMarkDef(lmEditMarkDef_t *pMark)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_AllocateMarkDef
|
||
|
|
||
|
Allocates a new mark definition for editing
|
||
|
=============
|
||
|
*/
|
||
|
lmEditMarkDef_t *R_AllocateMarkDef(void)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_AllocateEditPoly
|
||
|
|
||
|
Allocates a new mark poly for editing
|
||
|
=============
|
||
|
*/
|
||
|
lmEditPoly_t *R_AllocateEditPoly(void)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_AddEditPolyToPointerList
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
qboolean R_AddEditPolyToPointerList(lmEditPoly_t *pPoly, void ***pFragmentList, int *piNumFragments)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_ApplyLevelDecal
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
lmEditMarkDef_t *R_ApplyLevelDecal(
|
||
|
const vec3_t vPos, const vec3_t vDir, qboolean bUseCurrent, qboolean bUseCurrentSettings, int iPathIndex
|
||
|
)
|
||
|
{
|
||
|
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;
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_Make
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_Reapply
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_Delete
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_SelectNext
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_SelectPrev
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_SelectCh
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
void DCLC_SelectCh(void)
|
||
|
{
|
||
|
// Nothing to do
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_SelectChNxt
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
void DCLC_SelectChNxt(void)
|
||
|
{
|
||
|
// Nothing to do
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_GetInfo
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
void DCLC_GetInfo(void)
|
||
|
{
|
||
|
if (!lm.pCurrentMark) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (lm.pCurrentMark->fRotation == -1) {
|
||
|
ri.Cvar_Set("dcl_shader", lm.pCurrentMark->markShader->name);
|
||
|
ri.Cvar_SetValue("dcl_radius", lm.pCurrentMark->fRadius);
|
||
|
ri.Cvar_SetValue("dcl_widthscale", lm.pCurrentMark->fWidthScale);
|
||
|
} else {
|
||
|
ri.Cvar_Set("dcl_shader", lm.pCurrentMark->markShader->name);
|
||
|
ri.Cvar_SetValue("dcl_radius", lm.pCurrentMark->fRadius);
|
||
|
ri.Cvar_SetValue("dcl_heightscale", lm.pCurrentMark->fHeightScale);
|
||
|
ri.Cvar_SetValue("dcl_heightscale", lm.pCurrentMark->fHeightScale);
|
||
|
ri.Cvar_SetValue("dcl_widthscale", lm.pCurrentMark->fWidthScale);
|
||
|
ri.Cvar_SetValue("dcl_rotation", lm.pCurrentMark->fRotation);
|
||
|
ri.Cvar_SetValue("dcl_r", lm.pCurrentMark->color[0]);
|
||
|
ri.Cvar_SetValue("dcl_g", lm.pCurrentMark->color[1]);
|
||
|
ri.Cvar_SetValue("dcl_b", lm.pCurrentMark->color[2]);
|
||
|
}
|
||
|
|
||
|
ri.Cvar_SetValue("dcl_alpha", lm.pCurrentMark->color[3]);
|
||
|
ri.Cvar_SetValue("dcl_dolighting", lm.pCurrentMark->bDoLighting);
|
||
|
|
||
|
lm.bAutoApplySettings = qtrue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_ShiftUp
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_ShiftDown
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_ShiftLeft
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_ShiftRight
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
DCLC_RandomRoll
|
||
|
|
||
|
=============
|
||
|
*/
|
||
|
void DCLC_RandomRoll(void)
|
||
|
{
|
||
|
ri.Cvar_SetValue("dcl_rotation", random() * 360.0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_LevelMarksInit
|
||
|
|
||
|
Initialize the level mark system with variables and commands
|
||
|
=============
|
||
|
*/
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_LevelMarksFree
|
||
|
|
||
|
Remove level marks commands and deallocate all memory
|
||
|
=============
|
||
|
*/
|
||
|
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];
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
R_UpdateLevelMarksSystem
|
||
|
|
||
|
Draw lines for editing
|
||
|
=============
|
||
|
*/
|
||
|
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
|
||
|
ri.Cvar_SetValue("dcl_radius", 16.0);
|
||
|
dcl_radius->modified = qfalse;
|
||
|
|
||
|
// Set the decal scale
|
||
|
ri.Cvar_SetValue(
|
||
|
"dcl_heightscale", shader->stages[0]->bundle[0].image[0]->height / 16.0 * 0.5
|
||
|
);
|
||
|
ri.Cvar_SetValue("dcl_widthscale", shader->stages[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->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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============
|
||
|
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, 0, 0, 0);
|
||
|
}
|
||
|
}
|