mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 13:47:58 +03:00
1370 lines
43 KiB
C++
1370 lines
43 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2023 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
|
|
===========================================================================
|
|
*/
|
|
|
|
// DESCRIPTION:
|
|
// Volumetric smoke A.K.A VSS sources
|
|
|
|
#include "cg_local.h"
|
|
#include "cg_commands.h"
|
|
#include "memarchiver.h"
|
|
|
|
const char *cg_vsstypes[] = {
|
|
"default",
|
|
"gun",
|
|
"bulletimpact",
|
|
"bulletdirtimpact",
|
|
"heavy",
|
|
"steam",
|
|
"mist",
|
|
"smokegrenade",
|
|
"grenade",
|
|
"fire",
|
|
"greasefire",
|
|
"debris"
|
|
};
|
|
|
|
#define MAX_VSS_SORTS 16384
|
|
|
|
cvssource_t *vss_sorttable[MAX_VSS_SORTS];
|
|
|
|
static int lastVSSFrameTime;
|
|
static constexpr float MAX_VSS_COORDS = 8096.0;
|
|
static constexpr float MAX_VSS_WIND_DIST = 512;
|
|
static constexpr float MAX_VSS_WIND_DIST_SQUARED = MAX_VSS_WIND_DIST * MAX_VSS_WIND_DIST;
|
|
|
|
extern cvar_t *cg_detail;
|
|
extern cvar_t *cg_effectdetail;
|
|
cvar_t *vss_draw;
|
|
cvar_t *vss_physics_fps;
|
|
cvar_t *vss_repulsion_fps;
|
|
cvar_t *vss_maxcount;
|
|
cvar_t *vss_color;
|
|
cvar_t *vss_showsources;
|
|
cvar_t *vss_wind_x;
|
|
cvar_t *vss_wind_y;
|
|
cvar_t *vss_wind_z;
|
|
cvar_t *vss_wind_strength;
|
|
cvar_t *vss_movement_dampen;
|
|
cvar_t *vss_maxvisible;
|
|
cvar_t *vss_gridsize;
|
|
cvar_t *vss_default_r;
|
|
cvar_t *vss_default_g;
|
|
cvar_t *vss_default_b;
|
|
cvar_t *vss_lighting_fps;
|
|
|
|
void VSS_ClampAlphaLife(cvssource_t *pSource, int maxlife);
|
|
|
|
void VSS_AddRepulsion(cvssource_t *pA, cvssource_t *pB)
|
|
{
|
|
vec3_t vPush;
|
|
float fDist, fForce, f;
|
|
|
|
VectorSubtract(pA->newOrigin, pB->newOrigin, vPush);
|
|
|
|
if (!vPush[0] && !vPush[1] && !vPush[2]) {
|
|
VectorSet(vPush, crandom(), crandom(), crandom());
|
|
VectorAdd(pA->repulsion, vPush, pA->repulsion);
|
|
VectorSubtract(pB->repulsion, vPush, pB->repulsion);
|
|
return;
|
|
}
|
|
|
|
fDist = VectorNormalize(vPush);
|
|
f = fDist - pB->newRadius;
|
|
|
|
if (f > 0.0f) {
|
|
f *= pA->ooRadius;
|
|
if (f > 1.49f) {
|
|
f = 0.0f;
|
|
} else {
|
|
f = f * (f * 0.0161f + -0.3104f) + 1.2887f;
|
|
}
|
|
|
|
if (f < 0.0) {
|
|
f *= 1.1f;
|
|
}
|
|
|
|
fForce = f;
|
|
} else {
|
|
fForce = 1.0;
|
|
}
|
|
|
|
f = fDist - pA->newRadius;
|
|
if (f > 0.0) {
|
|
f *= pB->ooRadius;
|
|
if (f > 1.49f) {
|
|
f = 0.0f;
|
|
} else {
|
|
f = f * (f * 0.0161f + -0.3104f) + 1.2887f;
|
|
}
|
|
|
|
if (f < 0.0) {
|
|
f *= 1.1f;
|
|
}
|
|
|
|
fForce += f;
|
|
} else {
|
|
fForce += 1.0f;
|
|
}
|
|
|
|
if (fForce <= -0.05f || fForce >= 0.05f) {
|
|
fForce = (pA->newRadius + pB->newRadius) * 0.03f * fForce;
|
|
VectorScale(vPush, fForce, vPush);
|
|
|
|
VectorAdd(pA->repulsion, vPush, pA->repulsion);
|
|
VectorSubtract(pB->repulsion, vPush, pB->repulsion);
|
|
}
|
|
}
|
|
|
|
cvssource_t *ClientGameCommandManager::AllocateVSSSource()
|
|
{
|
|
cvssource_t *pNew;
|
|
|
|
if (!m_free_vsssources) {
|
|
FreeVSSSource(m_active_vsssources.prev);
|
|
}
|
|
|
|
pNew = m_free_vsssources;
|
|
m_free_vsssources = m_free_vsssources->next;
|
|
memset(pNew, 0, sizeof(cvssource_t));
|
|
|
|
pNew->next = m_active_vsssources.next;
|
|
pNew->prev = &m_active_vsssources;
|
|
|
|
m_active_vsssources.next->prev = pNew;
|
|
m_active_vsssources.next = pNew;
|
|
|
|
return pNew;
|
|
}
|
|
|
|
void ClientGameCommandManager::FreeVSSSource(cvssource_t *p)
|
|
{
|
|
if (!p->prev) {
|
|
cgi.Error(ERR_DROP, "CCM::FreeVSSSource: not active");
|
|
}
|
|
|
|
p->prev->next = p->next;
|
|
p->next->prev = p->prev;
|
|
p->next = m_free_vsssources;
|
|
m_free_vsssources = p;
|
|
}
|
|
|
|
void ClientGameCommandManager::ResetVSSSources()
|
|
{
|
|
int i;
|
|
cvssource_t *p;
|
|
cvssource_t *next;
|
|
|
|
vss_maxvisible = cgi.Cvar_Get("vss_maxvisible", "1024", CVAR_ARCHIVE | CVAR_LATCH);
|
|
if (m_iAllocatedvsssources && m_iAllocatedvsssources == vss_maxvisible->integer) {
|
|
// free existing vss sources
|
|
for (p = m_active_vsssources.prev; p != &m_active_vsssources; p = next) {
|
|
next = p->prev;
|
|
FreeVSSSource(p);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (m_iAllocatedvsssources) {
|
|
cgi.Free(m_vsssources);
|
|
}
|
|
|
|
if (vss_maxvisible->integer >= 128) {
|
|
m_iAllocatedvsssources = vss_maxvisible->integer;
|
|
} else {
|
|
m_iAllocatedvsssources = 128;
|
|
}
|
|
|
|
m_vsssources = (cvssource_t *)cgi.Malloc(sizeof(cvssource_t) * m_iAllocatedvsssources);
|
|
memset(m_vsssources, 0, sizeof(cvssource_t) * m_iAllocatedvsssources);
|
|
|
|
m_active_vsssources.next = &m_active_vsssources;
|
|
m_active_vsssources.prev = &m_active_vsssources;
|
|
m_free_vsssources = m_vsssources;
|
|
|
|
for (i = 0; i < m_iAllocatedvsssources - 1; ++i) {
|
|
m_vsssources[i].next = &m_vsssources[i + 1];
|
|
}
|
|
|
|
m_vsssources[m_iAllocatedvsssources - 1].next = NULL;
|
|
}
|
|
|
|
void ClientGameCommandManager::ResetVSSSources(Event *ev)
|
|
{
|
|
ResetVSSSources();
|
|
}
|
|
|
|
void CG_ResetVSSSources()
|
|
{
|
|
commandManager.ResetVSSSources();
|
|
lastVSSFrameTime = cg.time;
|
|
}
|
|
|
|
void CG_ArchiveVSSGlobals(MemArchiver& archiver)
|
|
{
|
|
archiver.ArchiveTime(&lastVSSFrameTime);
|
|
}
|
|
|
|
void ClientGameCommandManager::InitializeVSSSources()
|
|
{
|
|
int i;
|
|
|
|
vss_maxvisible = cgi.Cvar_Get("vss_maxvisible", "1024", CVAR_ARCHIVE | CVAR_LATCH);
|
|
if (m_iAllocatedvsssources && m_iAllocatedvsssources == vss_maxvisible->integer) {
|
|
// already allocated
|
|
return;
|
|
}
|
|
|
|
if (m_iAllocatedvsssources) {
|
|
cgi.Free(m_vsssources);
|
|
}
|
|
|
|
if (vss_maxvisible->integer >= 128) {
|
|
m_iAllocatedvsssources = vss_maxvisible->integer;
|
|
} else {
|
|
m_iAllocatedvsssources = 128;
|
|
}
|
|
|
|
m_vsssources = (cvssource_t *)cgi.Malloc(sizeof(cvssource_t) * m_iAllocatedvsssources);
|
|
memset(m_vsssources, 0, sizeof(sizeof(cvssource_t) * m_iAllocatedvsssources));
|
|
|
|
m_active_vsssources.next = &m_active_vsssources;
|
|
m_active_vsssources.prev = &m_active_vsssources;
|
|
m_free_vsssources = m_vsssources;
|
|
|
|
for (i = 0; i < m_iAllocatedvsssources - 1; ++i) {
|
|
m_vsssources[i].next = &m_vsssources[i + 1];
|
|
}
|
|
|
|
m_vsssources[m_iAllocatedvsssources - 1].next = NULL;
|
|
}
|
|
|
|
void ClientGameCommandManager::InitializeVSSCvars()
|
|
{
|
|
vss_draw = cgi.Cvar_Get("vss_draw", "0", CVAR_ARCHIVE);
|
|
vss_physics_fps = cgi.Cvar_Get("vss_physics_fps", "8", 0);
|
|
vss_repulsion_fps = cgi.Cvar_Get("vss_repulsion_fps", "4", 0);
|
|
vss_maxcount = cgi.Cvar_Get("vss_maxcount", "22", CVAR_ARCHIVE);
|
|
vss_color = cgi.Cvar_Get("vss_color", "1", 0);
|
|
vss_showsources = cgi.Cvar_Get("vss_showsources", "1", 0);
|
|
vss_wind_x = cgi.Cvar_Get("vss_wind_x", "8", 0);
|
|
vss_wind_y = cgi.Cvar_Get("vss_wind_y", "4", 0);
|
|
vss_wind_z = cgi.Cvar_Get("vss_wind_z", "2", 0);
|
|
vss_wind_strength = cgi.Cvar_Get("vss_wind_strength", "8", 0);
|
|
vss_movement_dampen = cgi.Cvar_Get("vss_movement_dampen", "4", 0);
|
|
vss_maxvisible = cgi.Cvar_Get("vss_maxvisible", "1024", 33);
|
|
vss_gridsize = cgi.Cvar_Get("vss_gridsize", "12", 0);
|
|
vss_default_r = cgi.Cvar_Get("vss_default_r", "0.5", 0);
|
|
vss_default_g = cgi.Cvar_Get("vss_default_g", "0.45", 0);
|
|
vss_default_b = cgi.Cvar_Get("vss_default_b", "0.4", 0);
|
|
vss_lighting_fps = cgi.Cvar_Get("vss_lighting_fps", "15", 0);
|
|
}
|
|
|
|
qboolean VSS_SourcePhysics(cvssource_t *pSource, float ftime)
|
|
{
|
|
int i;
|
|
int iSmokeType;
|
|
float fWind;
|
|
vec3_t vVel, vDelta;
|
|
trace_t trace;
|
|
entityState_t *pEntState;
|
|
|
|
fWind = 0.0;
|
|
|
|
if ((pSource->flags2 & (T2_ACCEL | T2_MOVE)) != 0) {
|
|
VectorMA(pSource->velocity, ftime, pSource->repulsion, pSource->velocity);
|
|
}
|
|
|
|
pSource->lastOrigin = pSource->newOrigin;
|
|
|
|
if (pSource->flags & T_COLLISION) {
|
|
trace.allsolid = qfalse;
|
|
CG_ClipMoveToEntities(
|
|
pSource->newOrigin, vec3_origin, vec3_origin, pSource->newOrigin, -1, MASK_VOLUMETRIC_SMOKE, &trace, qfalse
|
|
);
|
|
|
|
if (trace.allsolid) {
|
|
vec3_t vMins, vMaxs;
|
|
pEntState = &cg_entities[trace.entityNum].currentState;
|
|
|
|
IntegerToBoundingBox(pEntState->solid, vMins, vMaxs);
|
|
for (i = 0; i < 3; i++) {
|
|
vDelta[i] = pSource->newOrigin[i] - ((vMins[i] + vMaxs[i]) * 0.5 + pEntState->origin[i]);
|
|
}
|
|
VectorNormalizeFast(vDelta);
|
|
|
|
pSource->velocity = Vector(vDelta) * 16.0;
|
|
}
|
|
}
|
|
|
|
if (pSource->flags2 & (T2_ACCEL | T2_MOVE)) {
|
|
VectorMA(pSource->newOrigin, ftime, pSource->velocity, pSource->newOrigin);
|
|
}
|
|
|
|
if (pSource->flags & T_COLLISION) {
|
|
CG_Trace(
|
|
&trace,
|
|
pSource->lastOrigin,
|
|
vec3_origin,
|
|
vec3_origin,
|
|
pSource->newOrigin,
|
|
-1,
|
|
MASK_VOLUMETRIC_SMOKE,
|
|
qfalse,
|
|
qfalse,
|
|
"Collision"
|
|
);
|
|
|
|
if (trace.fraction != 1.0) {
|
|
float fDot;
|
|
vec3_t vNorm;
|
|
|
|
VectorCopy(trace.plane.normal, vNorm);
|
|
|
|
VectorAdd(trace.endpos, trace.plane.normal, pSource->newOrigin);
|
|
fDot = DotProduct(vNorm, pSource->velocity);
|
|
VectorMA(pSource->velocity, fDot, vNorm, pSource->velocity);
|
|
|
|
if (vNorm[2] > 0.7) {
|
|
VectorMA(pSource->velocity, ftime * -0.2, pSource->velocity, pSource->velocity);
|
|
}
|
|
|
|
iSmokeType = abs(pSource->smokeType);
|
|
if (iSmokeType >= 3 && iSmokeType <= 4) {
|
|
if (vNorm[2] > 0.7) {
|
|
pSource->newDensity -= ftime * 0.08;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pSource->newOrigin[0] < -MAX_VSS_COORDS || pSource->newOrigin[0] > MAX_VSS_COORDS
|
|
|| pSource->newOrigin[1] < -MAX_VSS_COORDS || pSource->newOrigin[1] > MAX_VSS_COORDS
|
|
|| pSource->newOrigin[2] < -MAX_VSS_COORDS || pSource->newOrigin[2] > MAX_VSS_COORDS) {
|
|
return qfalse;
|
|
}
|
|
|
|
iSmokeType = abs(pSource->smokeType);
|
|
if (pSource->flags2 & (T2_ACCEL | T2_MOVE)) {
|
|
VectorCopy(pSource->velocity, vVel);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
switch (i) {
|
|
case 0:
|
|
fWind = vss_wind_x->value;
|
|
break;
|
|
case 1:
|
|
fWind = vss_wind_y->value;
|
|
break;
|
|
case 2:
|
|
fWind = vss_wind_z->value;
|
|
break;
|
|
}
|
|
|
|
if (fWind < 0.0) {
|
|
if (vVel[i] > fWind) {
|
|
vVel[i] -= ftime * vss_wind_strength->value;
|
|
if (vVel[i] > fWind) {
|
|
vVel[i] = fWind;
|
|
}
|
|
} else {
|
|
vVel[i] += ftime * vss_movement_dampen->value;
|
|
if (vVel[i] < fWind) {
|
|
vVel[i] = fWind;
|
|
}
|
|
}
|
|
} else if (vVel[i] > fWind) {
|
|
vVel[i] -= ftime * vss_movement_dampen->value;
|
|
if (vVel[i] < fWind) {
|
|
vVel[i] = fWind;
|
|
}
|
|
} else {
|
|
vVel[i] += ftime * vss_movement_dampen->value;
|
|
if (vVel[i] > fWind) {
|
|
vVel[i] = fWind;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (iSmokeType) {
|
|
case 3:
|
|
if (vVel[2] > -8.0) {
|
|
vVel[2] -= ftime * 8.0;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (vVel[2] > -5.0) {
|
|
vVel[2] -= ftime * 3.0;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (vVel[2] < 256.0) {
|
|
vVel[2] += ftime * 40.0;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (vVel[2] > -25.0) {
|
|
vVel[2] -= ftime * 10.0;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (vVel[2] > -10.0) {
|
|
vVel[2] -= ftime * 4.0;
|
|
}
|
|
break;
|
|
case 9:
|
|
case 10:
|
|
if (pSource->typeInfo > 8.0) {
|
|
if (vVel[2] < pSource->typeInfo) {
|
|
vVel[2] += ftime * pSource->typeInfo;
|
|
}
|
|
|
|
pSource->typeInfo -= ftime * pSource->typeInfo * 0.04;
|
|
if (pSource->typeInfo < 10.0) {
|
|
pSource->typeInfo = 10.0;
|
|
}
|
|
}
|
|
break;
|
|
case 11:
|
|
if (vVel[2] > -800.0) {
|
|
vVel[2] -= ftime * 300.0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
fWind = VectorLengthSquared(vVel);
|
|
if (fWind > MAX_VSS_WIND_DIST_SQUARED) {
|
|
VectorNormalizeFast(vVel);
|
|
VectorScale(vVel, MAX_VSS_WIND_DIST, vVel);
|
|
}
|
|
|
|
pSource->velocity = vVel;
|
|
}
|
|
|
|
pSource->lastRadius = pSource->newRadius;
|
|
switch (iSmokeType) {
|
|
case 1:
|
|
pSource->newRadius += ftime * 1.2 * pSource->scaleMult;
|
|
break;
|
|
case 2:
|
|
pSource->newRadius += ftime * 0.7 * pSource->scaleMult;
|
|
break;
|
|
case 3:
|
|
if ((double)pSource->lifeTime >= 1.0) {
|
|
pSource->newRadius += ftime * 0.7 * pSource->scaleMult;
|
|
} else {
|
|
pSource->newRadius += ftime * 1.2 * pSource->scaleMult;
|
|
}
|
|
break;
|
|
case 4:
|
|
pSource->newRadius += ftime * 1.2 * pSource->scaleMult;
|
|
break;
|
|
case 5:
|
|
pSource->newRadius += ftime * 5.0 * pSource->scaleMult;
|
|
break;
|
|
case 6:
|
|
pSource->newRadius += ftime * 0.8 * pSource->scaleMult;
|
|
break;
|
|
case 7:
|
|
if (pSource->newRadius >= 24.0) {
|
|
pSource->newRadius += ftime * 0.4 * pSource->scaleMult;
|
|
} else {
|
|
pSource->newRadius += ftime * 1.6 * pSource->scaleMult;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (pSource->newRadius >= 24.0) {
|
|
pSource->newRadius += ftime * 0.4 * pSource->scaleMult;
|
|
} else {
|
|
pSource->newRadius += ftime * 1.6 * pSource->scaleMult;
|
|
}
|
|
break;
|
|
case 9:
|
|
case 10:
|
|
if (pSource->newRadius >= 16.0) {
|
|
pSource->newRadius += ftime * 0.4 * pSource->scaleMult;
|
|
} else {
|
|
pSource->newRadius += ftime * 0.8 * pSource->scaleMult;
|
|
}
|
|
break;
|
|
case 11:
|
|
pSource->newRadius += ftime * 0.8 * pSource->scaleMult;
|
|
break;
|
|
default:
|
|
pSource->newRadius += ftime * 1.2 * pSource->scaleMult;
|
|
break;
|
|
}
|
|
|
|
if (pSource->newRadius < 1.0) {
|
|
pSource->newRadius = 1.0;
|
|
} else if (pSource->newRadius > 32.0) {
|
|
pSource->newRadius = 32.0;
|
|
}
|
|
|
|
pSource->ooRadius = 1.0 / pSource->newRadius;
|
|
pSource->lastDensity = pSource->newDensity;
|
|
if (pSource->smokeType >= 0) {
|
|
switch (iSmokeType) {
|
|
case 1:
|
|
pSource->newDensity -= ftime * 0.07 * pSource->fadeMult;
|
|
break;
|
|
case 2:
|
|
pSource->newDensity -= ftime * 0.075 * pSource->fadeMult;
|
|
break;
|
|
case 3:
|
|
if (pSource->newDensity > 0.6) {
|
|
pSource->newDensity -= ftime * 0.05 * pSource->fadeMult;
|
|
} else {
|
|
pSource->newDensity -= ftime * 0.4 * pSource->fadeMult;
|
|
}
|
|
break;
|
|
case 4:
|
|
pSource->newDensity -= ftime * 0.0080000004 * pSource->fadeMult;
|
|
break;
|
|
case 5:
|
|
pSource->newDensity -= ftime * 0.75 * pSource->fadeMult;
|
|
break;
|
|
case 6:
|
|
pSource->newDensity -= ftime * 0.016000001 * pSource->fadeMult;
|
|
break;
|
|
case 7:
|
|
pSource->newDensity -= ftime * 0.0049999999 * pSource->fadeMult;
|
|
break;
|
|
case 8:
|
|
if (pSource->newDensity > 0.7) {
|
|
pSource->newDensity -= ftime * 0.025 * pSource->fadeMult;
|
|
} else {
|
|
pSource->newDensity -= ftime * 0.38 * pSource->fadeMult;
|
|
}
|
|
break;
|
|
case 11:
|
|
pSource->newDensity = pSource->newDensity - ftime * 0.125 * pSource->fadeMult;
|
|
break;
|
|
default:
|
|
if (pSource->newDensity > 0.4) {
|
|
pSource->newDensity -= ftime * 0.01 * pSource->fadeMult;
|
|
} else {
|
|
pSource->newDensity -= ftime * 0.0075 * pSource->fadeMult;
|
|
}
|
|
break;
|
|
}
|
|
if (pSource->newDensity <= 0.06) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
switch (iSmokeType) {
|
|
case 3:
|
|
VSS_ClampAlphaLife(pSource, 150);
|
|
break;
|
|
case 4:
|
|
VSS_ClampAlphaLife(pSource, 200);
|
|
break;
|
|
case 5:
|
|
case 11:
|
|
VSS_ClampAlphaLife(pSource, 50);
|
|
break;
|
|
case 6:
|
|
case 8:
|
|
VSS_ClampAlphaLife(pSource, 600);
|
|
break;
|
|
case 7:
|
|
VSS_ClampAlphaLife(pSource, 800);
|
|
break;
|
|
case 9:
|
|
case 10:
|
|
VSS_ClampAlphaLife(pSource, 1500);
|
|
break;
|
|
default:
|
|
VSS_ClampAlphaLife(pSource, 100);
|
|
break;
|
|
}
|
|
}
|
|
|
|
VectorCopy(pSource->newColor, pSource->lastColor);
|
|
if (iSmokeType == 1) {
|
|
for (i = 0; i < 3; ++i) {
|
|
pSource->newColor[i] -= ftime * 0.05f * pSource->fadeMult;
|
|
if (pSource->newColor[i] < 0.0f) {
|
|
pSource->newColor[i] = 0.0f;
|
|
}
|
|
}
|
|
} else if (iSmokeType == 9) {
|
|
for (i = 0; i < 3; ++i) {
|
|
if (pSource->newColor[i] < 0.9f) {
|
|
pSource->newColor[i] += ftime * 0.02f * pSource->fadeMult;
|
|
if (pSource->newColor[i] > 0.9f) {
|
|
pSource->newColor[i] = 0.9f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean VSS_LerpSource(cvssource_t *pCurrent, cvssourcestate_t *pState, float fLerpFrac, float fLightingFrac)
|
|
{
|
|
int i;
|
|
|
|
if (pCurrent->flags & (T_HARDLINK | T_PARENTLINK)) {
|
|
Vector parentOrigin;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
pState->origin[i] =
|
|
(pCurrent->newOrigin[i] - pCurrent->lastOrigin[i]) * fLerpFrac + pCurrent->lastOrigin[i];
|
|
}
|
|
|
|
if (!cg_entities[pCurrent->parent].currentValid) {
|
|
return qfalse;
|
|
}
|
|
|
|
refEntity_t *e = cgi.R_GetRenderEntity(pCurrent->parent);
|
|
if (!e) {
|
|
return qfalse;
|
|
}
|
|
|
|
parentOrigin = e->origin;
|
|
VectorAdd(pState->origin, parentOrigin, pState->origin);
|
|
} else if (pCurrent->flags2 & (T2_ACCEL | T2_MOVE)) {
|
|
for (i = 0; i < 3; i++) {
|
|
pState->origin[i] =
|
|
(pCurrent->newOrigin[i] - pCurrent->lastOrigin[i]) * fLerpFrac + pCurrent->lastOrigin[i];
|
|
}
|
|
}
|
|
|
|
if (vss_color->integer) {
|
|
for (i = 0; i < 3; ++i) {
|
|
pState->color[i] = (pCurrent->newColor[i] - pCurrent->lastColor[i]) * fLerpFrac + pCurrent->lastColor[i];
|
|
}
|
|
}
|
|
|
|
if (vss_lighting_fps->integer) {
|
|
for (i = 0; i < 3; ++i) {
|
|
pState->color[i] =
|
|
((pCurrent->newLighting[i] - pCurrent->lastLighting[i]) * fLightingFrac + pCurrent->lastLighting[i])
|
|
* pState->color[i];
|
|
}
|
|
}
|
|
|
|
pState->density = (pCurrent->newDensity - pCurrent->lastDensity) * fLerpFrac + pCurrent->lastDensity;
|
|
pState->radius = (pCurrent->newRadius - pCurrent->lastRadius) * fLerpFrac + pCurrent->lastRadius;
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
void ClientGameCommandManager::SpawnVSSSource(int count, int timealive)
|
|
{
|
|
int i;
|
|
int iSmokeLeft, iSmokeType;
|
|
float fSmokeTypeDataValue = 0.0;
|
|
float fFadeMult = 0.0, fScaleMult = 0.0;
|
|
float fCountScale;
|
|
float fDensity, fRadius;
|
|
float fAngle = 0.0, fAngleStep = 0.0;
|
|
Vector vNewForward;
|
|
str sSmokeName;
|
|
cvssource_t *pSource;
|
|
|
|
if (m_spawnthing->cgd.alpha <= 0.0) {
|
|
return;
|
|
}
|
|
fDensity = this->m_spawnthing->cgd.alpha;
|
|
fRadius = this->m_spawnthing->cgd.scale * vss_maxcount->value * 0.1;
|
|
if (fRadius > 32.0) {
|
|
fRadius = 32.0;
|
|
}
|
|
|
|
if (m_spawnthing->cgd.flags & T_CIRCLE) {
|
|
fAngle = 0.0;
|
|
fAngleStep = 360.0 / (count / vss_maxcount->value);
|
|
}
|
|
|
|
iSmokeType = 0;
|
|
sSmokeName = m_spawnthing->GetModel();
|
|
|
|
for (i = 0; i < 12; ++i) {
|
|
if (sSmokeName == cg_vsstypes[i]) {
|
|
iSmokeType = i;
|
|
if (i < 9 || i > 10) {
|
|
fSmokeTypeDataValue = m_spawnthing->cgd.accel[0];
|
|
} else {
|
|
fSmokeTypeDataValue = m_spawnthing->cgd.accel[0];
|
|
if (fSmokeTypeDataValue == 0.0) {
|
|
fSmokeTypeDataValue = 24.0;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
iSmokeType = -iSmokeType;
|
|
fFadeMult = m_spawnthing->cgd.accel[1];
|
|
if (fFadeMult < 0.0001) {
|
|
fFadeMult = 1.0;
|
|
}
|
|
|
|
fScaleMult = m_spawnthing->cgd.accel[2];
|
|
if (!fScaleMult) {
|
|
fScaleMult = 1.0;
|
|
}
|
|
|
|
fCountScale = m_spawnthing->cgd.life / 1000;
|
|
if (count * fCountScale < vss_maxcount->value) {
|
|
iSmokeLeft = count * fCountScale;
|
|
} else {
|
|
iSmokeLeft = (int)(fCountScale * count * cg_effectdetail->value);
|
|
if (iSmokeLeft < vss_maxcount->value) {
|
|
iSmokeLeft = (int)vss_maxcount->value;
|
|
}
|
|
}
|
|
|
|
while (iSmokeLeft > 0) {
|
|
pSource = AllocateVSSSource();
|
|
if (!pSource) {
|
|
cgi.DPrintf("Out of VSS Sources\n");
|
|
return;
|
|
}
|
|
|
|
pSource->startAlpha = (random() * 0.15 + 0.85) * fDensity;
|
|
pSource->newDensity = 0.0;
|
|
if (m_spawnthing->cgd.flags & T_RANDSCALE) {
|
|
pSource->newRadius = RandomizeRange(m_spawnthing->cgd.scalemin, m_spawnthing->cgd.scalemax);
|
|
if (pSource->newRadius > 32.0) {
|
|
pSource->newRadius = 32.0;
|
|
}
|
|
} else {
|
|
pSource->newRadius = fRadius;
|
|
}
|
|
|
|
if (iSmokeLeft < vss_maxcount->value) {
|
|
pSource->newRadius = iSmokeLeft / vss_maxcount->value * pSource->newRadius;
|
|
}
|
|
|
|
if (vss_color->value) {
|
|
float fRandom = random() * 0.3 + 0.7;
|
|
pSource->newColor[0] = fRandom * m_spawnthing->cgd.color[0];
|
|
pSource->newColor[1] = fRandom * m_spawnthing->cgd.color[1];
|
|
pSource->newColor[2] = fRandom * m_spawnthing->cgd.color[2];
|
|
}
|
|
|
|
pSource->parent = m_spawnthing->cgd.parent;
|
|
pSource->flags = m_spawnthing->cgd.flags;
|
|
pSource->flags2 = m_spawnthing->cgd.flags2;
|
|
pSource->smokeType = iSmokeType;
|
|
pSource->typeInfo = fSmokeTypeDataValue;
|
|
pSource->fadeMult = fFadeMult;
|
|
pSource->scaleMult = fScaleMult;
|
|
pSource->roll = anglemod(fAngle);
|
|
|
|
if (random() < 0.5) {
|
|
pSource->flags |= T_RANDOMROLL;
|
|
}
|
|
|
|
VectorCopy(m_spawnthing->axis[0], vNewForward);
|
|
if (m_spawnthing->cgd.flags & T_SPHERE) {
|
|
VectorCopy(m_spawnthing->cgd.origin, pSource->newOrigin);
|
|
do {
|
|
vNewForward = Vector(crandom(), crandom(), crandom());
|
|
} while (Vector::Dot(vNewForward, vNewForward) < 1.0);
|
|
} else if (m_spawnthing->cgd.flags & T_CIRCLE) {
|
|
if (m_spawnthing->sphereRadius != 0.0) {
|
|
Vector dst, end;
|
|
|
|
end = m_spawnthing->axis[0];
|
|
RotatePointAroundVector(dst, m_spawnthing->axis[2], end, fAngle);
|
|
|
|
VectorAdd(m_spawnthing->cgd.origin, dst, pSource->newOrigin);
|
|
VectorSubtract(pSource->newOrigin, m_spawnthing->cgd.origin, vNewForward);
|
|
VectorNormalizeFast(vNewForward);
|
|
|
|
fAngle += fAngleStep;
|
|
}
|
|
} else if (m_spawnthing->cgd.flags & T_INWARDSPHERE) {
|
|
Vector dir, end;
|
|
do {
|
|
dir = Vector(crandom(), crandom(), crandom());
|
|
} while (Vector::Dot(dir, dir) < 1.0);
|
|
|
|
end = m_spawnthing->cgd.origin + dir * m_spawnthing->sphereRadius;
|
|
VectorCopy(end, pSource->newOrigin);
|
|
vNewForward = dir * -1.0;
|
|
} else if (m_spawnthing->cgd.flags2 & T2_CONE) {
|
|
float fHeight, fRadius;
|
|
float fAngle;
|
|
float sina, cosa;
|
|
|
|
fHeight = random();
|
|
fRadius = random();
|
|
if (fHeight < fRadius) {
|
|
float fTemp = fHeight;
|
|
fHeight = fRadius;
|
|
fRadius = fTemp;
|
|
}
|
|
fHeight *= m_spawnthing->coneHeight;
|
|
fRadius *= m_spawnthing->sphereRadius;
|
|
fAngle = random() * 6.2831855;
|
|
cosa = cos(fAngle);
|
|
sina = sin(fAngle);
|
|
|
|
VectorMA(m_spawnthing->cgd.origin, fHeight, m_spawnthing->axis[0], pSource->newOrigin);
|
|
VectorMA(m_spawnthing->cgd.origin, fRadius * cosa, m_spawnthing->axis[1], pSource->newOrigin);
|
|
VectorMA(m_spawnthing->cgd.origin, fRadius * sina, m_spawnthing->axis[2], pSource->newOrigin);
|
|
} else if (m_spawnthing->sphereRadius) {
|
|
Vector dir, end;
|
|
do {
|
|
dir = Vector(crandom(), crandom(), crandom());
|
|
} while (Vector::Dot(dir, dir) < 1.0);
|
|
|
|
dir.normalize();
|
|
end = m_spawnthing->cgd.origin + dir * m_spawnthing->sphereRadius;
|
|
VectorCopy(end, pSource->newOrigin);
|
|
vNewForward = dir;
|
|
} else {
|
|
VectorCopy(m_spawnthing->cgd.origin, pSource->newOrigin);
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
pSource->newOrigin[i] +=
|
|
random() * m_spawnthing->origin_offset_base[i] + m_spawnthing->origin_offset_amplitude[i];
|
|
}
|
|
|
|
VectorCopy(pSource->newOrigin, pSource->lastOrigin);
|
|
if (timealive > 0) {
|
|
pSource->lifeTime = timealive;
|
|
} else {
|
|
pSource->lifeTime = 0;
|
|
}
|
|
|
|
if (m_spawnthing->forwardVelocity) {
|
|
for (i = 0; i < 3; ++i) {
|
|
pSource->velocity[i] = vNewForward[i] * m_spawnthing->forwardVelocity;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
float fVel = m_spawnthing->randvel_base[i] + random() * m_spawnthing->randvel_amplitude[i];
|
|
|
|
if (m_spawnthing->cgd.flags & T_RANDVELAXIS) {
|
|
pSource->velocity += Vector(m_spawnthing->tag_axis[i]) * fVel;
|
|
} else {
|
|
pSource->velocity[i] += fVel;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
float fDist = m_spawnthing->axis_offset_base[i] + random() * m_spawnthing->axis_offset_amplitude[i];
|
|
|
|
if (pSource->flags2 & T2_PARALLEL) {
|
|
pSource->newOrigin += Vector(m_spawnthing->axis[i]) * fDist;
|
|
} else {
|
|
pSource->newOrigin += Vector(m_spawnthing->tag_axis[i]) * fDist;
|
|
}
|
|
}
|
|
|
|
pSource->newOrigin += pSource->velocity * (pSource->lifeTime / 1000.0);
|
|
if (vss_lighting_fps->integer) {
|
|
cgi.R_GetLightingForSmoke(pSource->newLighting, pSource->newOrigin);
|
|
}
|
|
|
|
iSmokeLeft -= vss_maxcount->value;
|
|
}
|
|
}
|
|
|
|
void VSS_CalcRepulsionForces(cvssource_t *pActiveSources)
|
|
{
|
|
cvssource_t *pCurrent;
|
|
cvssource_t *pComp;
|
|
qboolean bXUp, bXDown;
|
|
qboolean bYUp, bYDown;
|
|
qboolean bZDown;
|
|
int i;
|
|
int iIndex;
|
|
int iX, iY, iZ;
|
|
int iMinX, iMinY, iMinZ;
|
|
int iMaxX, iMaxY, iMaxZ;
|
|
float fOfs;
|
|
cvssource_t *pSTLatch;
|
|
|
|
pCurrent = pActiveSources->prev;
|
|
if (pCurrent == pActiveSources) {
|
|
return;
|
|
}
|
|
|
|
memset(vss_sorttable, 0, sizeof(vss_sorttable));
|
|
|
|
for (pCurrent = pActiveSources->prev; pCurrent != pActiveSources; pCurrent = pCurrent->prev) {
|
|
VectorClear(pCurrent->repulsion);
|
|
|
|
iIndex = ((int)floor(pCurrent->newOrigin[0] + 8192.0 + 0.5) / 96) % 32;
|
|
iIndex |= (((int)floor(pCurrent->newOrigin[1] + 8192.0 + 0.5) / 96) % 32) << 5;
|
|
iIndex |= (((int)floor(pCurrent->newOrigin[2] + 8192.0 + 0.5) / 96) % 16) << 10;
|
|
|
|
pCurrent->stnext = vss_sorttable[iIndex];
|
|
vss_sorttable[iIndex] = pCurrent;
|
|
pCurrent->stindex = iIndex;
|
|
}
|
|
|
|
for (pCurrent = pActiveSources->prev; pCurrent != pActiveSources; pCurrent = pCurrent->prev) {
|
|
if (vss_sorttable[pCurrent->stindex] == pCurrent) {
|
|
pSTLatch = (cvssource_t *)-1;
|
|
pComp = pCurrent->stnext;
|
|
} else {
|
|
pSTLatch = 0;
|
|
pComp = vss_sorttable[pCurrent->stindex];
|
|
}
|
|
|
|
for(; pComp; pComp = pComp->stnext) {
|
|
VSS_AddRepulsion(pCurrent, pComp);
|
|
if (!pSTLatch && pComp->stnext == pCurrent) {
|
|
pSTLatch = pComp;
|
|
// skip current
|
|
pComp = pComp->stnext;
|
|
}
|
|
}
|
|
|
|
iX = ((int)floor(pCurrent->newOrigin[0] + 8192.0 + 0.5) / 96) % 32;
|
|
iY = (((int)floor(pCurrent->newOrigin[1] + 8192.0 + 0.5) / 96) % 32) << 5;
|
|
iZ = (((int)floor(pCurrent->newOrigin[2] + 8192.0 + 0.5) / 96) % 16) << 10;
|
|
|
|
fOfs = pCurrent->newRadius + 1.49 + 48.0;
|
|
iMaxX = ((int)floor(pCurrent->newOrigin[0] + fOfs + 8192.0 + 0.5) / 96) % 32;
|
|
iMaxY = (((int)floor(pCurrent->newOrigin[1] + fOfs + 8192.0 + 0.5) / 96) % 32) << 5;
|
|
iMaxZ = (((int)floor(pCurrent->newOrigin[2] + fOfs + 8192.0 + 0.5) / 96) % 16) << 10;
|
|
|
|
iMinX = ((int)floor(pCurrent->newOrigin[0] - fOfs + 8192.0 + 0.5) / 96) % 32;
|
|
iMinY = (((int)floor(pCurrent->newOrigin[1] - fOfs + 8192.0 + 0.5) / 96) % 32) << 5;
|
|
iMinZ = (((int)floor(pCurrent->newOrigin[2] - fOfs + 8192.0 + 0.5) / 96) % 16) << 10;
|
|
|
|
bXUp = (iMaxX | (pCurrent->stindex & 0xFFFFFFE0)) != pCurrent->stindex;
|
|
bXDown = (iMinX | (pCurrent->stindex & 0xFFFFFFE0)) != pCurrent->stindex;
|
|
bYUp = (iMaxY | (pCurrent->stindex & 0xFFFFFC1F)) != pCurrent->stindex;
|
|
bYDown = (iMinY | (pCurrent->stindex & 0xFFFFFC1F)) != pCurrent->stindex;
|
|
|
|
iIndex = iMinZ | (pCurrent->stindex & 0xFFFFC3FF);
|
|
bZDown = iIndex != pCurrent->stindex;
|
|
|
|
if (iIndex == pCurrent->stindex) {
|
|
iIndex = iMaxY | (pCurrent->stindex & 0xFFFFFC1F);
|
|
|
|
i = 9;
|
|
} else {
|
|
i = 0;
|
|
}
|
|
|
|
for (; i < (bZDown ? 26 : 17); i++) {
|
|
switch (i) {
|
|
case 0:
|
|
iIndex = iMaxZ | (pCurrent->stindex & 0xFFFFC3FF);
|
|
break;
|
|
case 1:
|
|
iIndex = iMaxX | (iIndex & 0xFFFFFFE0);
|
|
if (bXUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 2:
|
|
iIndex = iMaxY | (iIndex & 0xFFFFFC1F);
|
|
if (bXUp && bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 3:
|
|
iIndex = iMinY | (iIndex & 0xFFFFFC1F);
|
|
if (bXUp && bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 4:
|
|
iIndex = iMinY | (iIndex & 0xFFFFFFE0);
|
|
if (bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 5:
|
|
iIndex = iMinX | (iIndex & 0xFFFFFFE0);
|
|
if (bXDown && bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 6:
|
|
iIndex = iY | (iIndex & 0xFFFFFC1F);
|
|
if (bXDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 7:
|
|
iIndex = iMaxY | (iIndex & 0xFFFFFC1F);
|
|
if (bXDown && bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 8:
|
|
iIndex = iX | (iIndex & 0xFFFFFFE0);
|
|
if (bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 9:
|
|
iIndex = iZ | (iIndex & 0xFFFFFFC3);
|
|
if (bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 10:
|
|
iIndex = iMaxX | (iIndex & 0xFFFFFFE0);
|
|
if (bXUp && bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 11:
|
|
iIndex = iMinX | (iIndex & 0xFFFFFFE0);
|
|
if (bXDown && bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 12:
|
|
iIndex = iY | (iIndex & 0xFFFFFC1F);
|
|
if (bXDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 13:
|
|
iIndex = iMinY | (iIndex & 0xFFFFFC1F);
|
|
if (bXDown && bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 14:
|
|
iIndex = iX | (iIndex & 0xFFFFFFE0);
|
|
if (bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 15:
|
|
iIndex = iMaxX | (iIndex & 0xFFFFFFE0);
|
|
if (bXUp && bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 16:
|
|
iIndex = iY | (iIndex & 0xFFFFFC1F);
|
|
if (bXUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 17:
|
|
iIndex = iMinZ | (iIndex & 0xFFFFFCC3);
|
|
if (bXUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 18:
|
|
iIndex = iMaxY | (iIndex & 0xFFFFFC1F);
|
|
if (bXUp && bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 19:
|
|
iIndex = iMinY | (iIndex & 0xFFFFFC1F);
|
|
if (bXUp && bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 20:
|
|
iIndex = iX | (iIndex & 0xFFFFFFE0);
|
|
if (bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 21:
|
|
iIndex = iMinX | (iIndex & 0xFFFFFFE0);
|
|
if (bXDown && bYDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 22:
|
|
iIndex = iY | (iIndex & 0xFFFFFC1F);
|
|
if (bXDown) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 23:
|
|
iIndex = iMaxY | (iIndex & 0xFFFFFC1F);
|
|
if (bXDown && bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 24:
|
|
iIndex = iX | (iIndex & 0xFFFFFFE0);
|
|
if (bYUp) {
|
|
break;
|
|
}
|
|
continue;
|
|
case 25:
|
|
iIndex = iY | (iIndex & 0xFFFFFC1F);
|
|
break;
|
|
default:
|
|
assert(0); // This can't happen
|
|
break;
|
|
}
|
|
|
|
for (pComp = vss_sorttable[iIndex]; pComp; pComp = pComp->stnext) {
|
|
VSS_AddRepulsion(pCurrent, pComp);
|
|
}
|
|
}
|
|
|
|
if (pSTLatch == (cvssource_t *)-1) {
|
|
vss_sorttable[pCurrent->stindex] = pCurrent->stnext;
|
|
} else {
|
|
pSTLatch->stnext = pCurrent->stnext;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CG_AddVSSSources()
|
|
{
|
|
commandManager.AddVSSSources();
|
|
}
|
|
|
|
void ClientGameCommandManager::AddVSSSources()
|
|
{
|
|
int i, j;
|
|
int frameTime;
|
|
int physics_rate, lighting_rate;
|
|
int mstime;
|
|
float fLerpFrac, fLightingFrac;
|
|
vec3_t vAng;
|
|
cvssource_t *pCurrent;
|
|
cvssource_t *pComp;
|
|
cvssourcestate_t state;
|
|
int hModel, hModel2;
|
|
refEntity_t newEnt;
|
|
|
|
hModel = 0;
|
|
hModel2 = 0;
|
|
|
|
if (vss_showsources->integer) {
|
|
// load sources
|
|
hModel = cgi.R_RegisterModel("VSSSource.spr");
|
|
hModel2 = cgi.R_RegisterModel("VSSSource2.spr");
|
|
|
|
memset(&newEnt, 0, sizeof(newEnt));
|
|
memset(vAng, 0, sizeof(vAng));
|
|
|
|
AnglesToAxis(vAng, newEnt.axis);
|
|
|
|
newEnt.renderfx = 0;
|
|
newEnt.reType = RT_SPRITE;
|
|
newEnt.shaderTime = 0.0;
|
|
newEnt.frameInfo[0].index = 0;
|
|
newEnt.frameInfo[0].weight = 1.0;
|
|
newEnt.frameInfo[0].time = 0.0;
|
|
newEnt.actionWeight = 1.0;
|
|
}
|
|
|
|
if (lastVSSFrameTime) {
|
|
if (cg.time < lastVSSFrameTime || cg.time - lastVSSFrameTime > 500) {
|
|
for (pCurrent = m_active_vsssources.prev; pCurrent != &m_active_vsssources; pCurrent = pCurrent->prev) {
|
|
pCurrent->lastPhysicsTime = cg.time;
|
|
pCurrent->lastLightingTime = cg.time;
|
|
}
|
|
|
|
m_iLastVSSRepulsionTime = cg.time;
|
|
lastVSSFrameTime = cg.time;
|
|
|
|
return;
|
|
}
|
|
|
|
frameTime = cg.time - lastVSSFrameTime;
|
|
} else {
|
|
frameTime = 0;
|
|
}
|
|
if (paused->integer) {
|
|
lastVSSFrameTime = 0;
|
|
} else {
|
|
lastVSSFrameTime = cg.time;
|
|
}
|
|
|
|
if (lastVSSFrameTime) {
|
|
if (cg.time >= m_iLastVSSRepulsionTime && cg.time - m_iLastVSSRepulsionTime <= 500) {
|
|
if (cg.time - m_iLastVSSRepulsionTime >= 1000 / vss_repulsion_fps->integer) {
|
|
VSS_CalcRepulsionForces(&m_active_vsssources);
|
|
m_iLastVSSRepulsionTime = cg.time;
|
|
}
|
|
} else {
|
|
m_iLastVSSRepulsionTime = cg.time;
|
|
}
|
|
} else {
|
|
m_iLastVSSRepulsionTime = 0;
|
|
}
|
|
|
|
physics_rate = (int)(1000.0 / (float)vss_physics_fps->integer);
|
|
lighting_rate = (int)(1000.0 / (float)vss_lighting_fps->integer);
|
|
for (pCurrent = this->m_active_vsssources.prev; pCurrent != &this->m_active_vsssources; pCurrent = pComp) {
|
|
pComp = pCurrent->prev;
|
|
|
|
newEnt.renderfx = 0;
|
|
if ((pCurrent->flags & T_DETAIL) && !cg_detail->integer) {
|
|
FreeVSSSource(pCurrent);
|
|
continue;
|
|
}
|
|
|
|
if ((pCurrent->flags2 & T2_ALWAYSDRAW) != 0) {
|
|
newEnt.renderfx = RF_ALWAYSDRAW;
|
|
}
|
|
|
|
if (pCurrent->lastPhysicsTime) {
|
|
mstime = cg.time - pCurrent->lastPhysicsTime;
|
|
if (mstime > 2 * physics_rate) {
|
|
mstime = physics_rate;
|
|
}
|
|
|
|
if (mstime >= physics_rate || (pCurrent->flags2 & T2_PHYSICS_EVERYFRAME) != 0) {
|
|
if (!VSS_SourcePhysics(pCurrent, (float)mstime / 1000.0)) {
|
|
FreeVSSSource(pCurrent);
|
|
continue;
|
|
}
|
|
|
|
pCurrent->lastPhysicsTime = cg.time;
|
|
}
|
|
}
|
|
|
|
if (pCurrent->lastLightingTime) {
|
|
mstime = cg.time - pCurrent->lastLightingTime;
|
|
if (mstime > 2 * lighting_rate) {
|
|
mstime = lighting_rate;
|
|
}
|
|
|
|
if (mstime >= lighting_rate) {
|
|
pCurrent->lastLighting[0] = pCurrent->newLighting[0];
|
|
pCurrent->lastLighting[1] = pCurrent->newLighting[1];
|
|
pCurrent->lastLighting[2] = pCurrent->newLighting[2];
|
|
cgi.R_GetLightingForSmoke(pCurrent->newLighting, pCurrent->newOrigin);
|
|
pCurrent->lastLightingTime = cg.time;
|
|
}
|
|
}
|
|
|
|
fLerpFrac = (float)(cg.time - pCurrent->lastPhysicsTime) / (float)physics_rate;
|
|
fLerpFrac = Q_clamp_float(fLerpFrac, 0, 1);
|
|
|
|
fLightingFrac = (float)(cg.time - pCurrent->lastLightingTime) / (float)lighting_rate;
|
|
fLightingFrac = Q_clamp_float(fLightingFrac, 0, 1);
|
|
|
|
if (lastVSSFrameTime) {
|
|
pCurrent->lifeTime += frameTime;
|
|
}
|
|
|
|
if (!pCurrent->lastValid) {
|
|
if (!VSS_SourcePhysics(pCurrent, (float)physics_rate / 1000)) {
|
|
ClientGameCommandManager::FreeVSSSource(pCurrent);
|
|
continue;
|
|
}
|
|
|
|
pCurrent->lastLighting[0] = pCurrent->newLighting[0];
|
|
pCurrent->lastLighting[1] = pCurrent->newLighting[1];
|
|
pCurrent->lastLighting[2] = pCurrent->newLighting[2];
|
|
cgi.R_GetLightingForSmoke(pCurrent->newLighting, pCurrent->newOrigin);
|
|
|
|
fLerpFrac = 0.0;
|
|
fLightingFrac = 0.0;
|
|
|
|
pCurrent->lastPhysicsTime = cg.time;
|
|
pCurrent->lastLightingTime = cg.time;
|
|
pCurrent->lastValid = 1;
|
|
}
|
|
|
|
if (VSS_LerpSource(pCurrent, &state, fLerpFrac, fLightingFrac)) {
|
|
if (vss_showsources->integer) {
|
|
VectorCopy(state.origin, newEnt.origin);
|
|
newEnt.scale = state.radius / 5.0;
|
|
|
|
if (vss_color->integer) {
|
|
newEnt.shaderRGBA[0] = (int)(state.color[0] * 255.0);
|
|
newEnt.shaderRGBA[1] = (int)(state.color[1] * 255.0);
|
|
newEnt.shaderRGBA[2] = (int)(state.color[2] * 255.0);
|
|
} else {
|
|
newEnt.shaderRGBA[0] = (int)(vss_default_r->value * 255.0);
|
|
newEnt.shaderRGBA[1] = (int)(vss_default_g->value * 255.0);
|
|
newEnt.shaderRGBA[2] = (int)(vss_default_b->value * 255.0);
|
|
}
|
|
|
|
newEnt.shaderRGBA[3] = (int)(state.density * 255.0);
|
|
|
|
if (lastVSSFrameTime) {
|
|
pCurrent->roll += frameTime;
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
j = (int)(frameTime * pCurrent->velocity[i] * 0.03);
|
|
if (pCurrent->velocity[i] < 0.0) {
|
|
j = -j;
|
|
}
|
|
|
|
if (j > frameTime) {
|
|
j = (int)((float)frameTime + (float)(j - frameTime) * 0.75);
|
|
if (j > 2 * frameTime) {
|
|
j = 2 * frameTime;
|
|
}
|
|
}
|
|
|
|
pCurrent->roll -= j;
|
|
}
|
|
|
|
if ((pCurrent->flags & T_RANDOMROLL) != 0) {
|
|
newEnt.hModel = hModel;
|
|
} else {
|
|
newEnt.hModel = hModel2;
|
|
}
|
|
} else if ((pCurrent->flags & T_RANDOMROLL) != 0) {
|
|
newEnt.hModel = hModel;
|
|
} else {
|
|
newEnt.hModel = hModel2;
|
|
}
|
|
|
|
newEnt.shaderTime = (float)(pCurrent->roll + cg.time - pCurrent->lifeTime) * 0.001;
|
|
cgi.R_AddRefSpriteToScene(&newEnt);
|
|
}
|
|
} else {
|
|
FreeVSSSource(pCurrent);
|
|
}
|
|
}
|
|
|
|
if (vss_showsources->integer == 2) {
|
|
i = 0;
|
|
|
|
for (pCurrent = this->m_active_vsssources.prev; pCurrent != &this->m_active_vsssources;
|
|
pCurrent = pCurrent->prev) {
|
|
++i;
|
|
}
|
|
|
|
cgi.DPrintf("VSS Sources In Use: %i\n", i);
|
|
}
|
|
}
|
|
|
|
void VSS_ClampAlphaLife(cvssource_t *pSource, int maxlife)
|
|
{
|
|
if (pSource->lifeTime >= maxlife) {
|
|
pSource->smokeType = -pSource->smokeType;
|
|
pSource->newDensity = pSource->startAlpha;
|
|
} else {
|
|
pSource->newDensity = (float)pSource->lifeTime / (float)maxlife * pSource->startAlpha;
|
|
}
|
|
}
|