openmohaa/code/renderergl2/tr_ghost.cpp

1663 lines
34 KiB
C++
Raw Normal View History

/*
===========================================================================
Copyright (C) 2023-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_ghost.cpp
#include "tr_local.h"
#include "tr_ghost.h"
GhostManager ghostManager;
int frameTime;
int lastTime;
/*
====================
RandomizeRange
====================
*/
float RandomizeRange(float min, float max)
{
return min + (max - min) * (rand() / (float)RAND_MAX);
}
/*
====================
Particle::Particle
Initializes the particle with the specified parameters
====================
*/
Particle::Particle(
Vector pos,
Vector vel,
Vector acc,
int srcColor,
int dstColor,
float colorRate,
qboolean wavy,
float wavyDist,
float currentTime,
float dieTime,
float maxspeed,
Vector parentOrg,
qboolean swarm,
int freq,
int delta
)
{
m_position = pos;
m_realposition = pos;
m_velocity = vel;
m_acceleration = acc;
m_srcColor = srcColor;
m_dstColor = dstColor;
m_color = srcColor;
m_colorRate = colorRate;
m_wavy = wavy;
m_wavyDist = wavyDist;
m_wavyOffset = RandomizeRange(-wavyDist, wavyDist);
m_swarm = swarm;
m_swarmfrequency = freq;
m_swarmdelta = delta;
m_startTime = currentTime;
m_dieTime = dieTime;
m_life = dieTime - currentTime;
m_maxspeed = maxspeed;
m_parentOrigin = parentOrg;
Vector tempvec = m_velocity;
VectorNormalize(tempvec);
m_perp.x = tempvec.x * cos(M_PI / 2) - tempvec.y * sin(M_PI / 2);
m_perp.y = tempvec.x * sin(M_PI / 2) + tempvec.y * cos(M_PI / 2);
m_srcR = (m_srcColor & 0xff);
m_srcG = (m_srcColor & 0xff00) >> 8;
m_srcB = (m_srcColor & 0xff0000) >> 16;
m_dstR = (m_dstColor & 0xff);
m_dstG = (m_dstColor & 0xff00) >> 8;
m_dstB = (m_dstColor & 0xff0000) >> 16;
m_deltaR = m_dstR - m_srcR;
m_deltaG = m_dstG - m_srcG;
m_deltaB = m_dstB - m_srcB;
}
/*
====================
Particle::Update
Updates the particle based on the current time
====================
*/
void Particle::Update(float currentTime)
{
int r, g, b;
float ftime;
float time2;
float factor;
Vector offset;
ftime = frameTime / 1000.0;
time2 = Square(ftime) * 0.5;
if (m_swarm) {
if (!(rand() % m_swarmfrequency)) {
m_velocity[0] = crandom() * m_maxspeed;
m_velocity[1] = crandom() * m_maxspeed;
m_velocity[2] = crandom() * m_maxspeed;
}
if (m_parentOrigin[0] > m_position[0]) {
m_velocity[0] += m_swarmdelta;
} else {
m_velocity[0] -= m_swarmdelta;
}
if (m_parentOrigin[1] > m_position[1]) {
m_velocity[1] += m_swarmdelta;
} else {
m_velocity[1] -= m_swarmdelta;
}
if (m_parentOrigin[2] > m_position[2]) {
m_velocity[2] += m_swarmdelta;
} else {
m_velocity[2] -= m_swarmdelta;
}
}
m_position = m_realposition;
m_position += m_velocity * ftime + m_acceleration * time2;
m_velocity += m_acceleration * ftime;
m_realposition = m_position;
if (m_wavy) {
offset = m_perp * sin(currentTime + m_wavyOffset) * (m_wavyOffset + m_wavyDist);
m_position += offset;
}
factor = 1.0 - (m_dieTime - currentTime) / m_life;
r = m_deltaR * factor;
g = m_deltaG * factor;
b = m_deltaB * factor;
m_color = (r) | (g << 8) | (b << 8);
}
/*
====================
Particle::GetDieTime
Returns the time at which the particle stops
====================
*/
float Particle::GetDieTime()
{
return m_dieTime;
}
/*
====================
ParticleEmitter::ParticleEmitter
Initializes the particle emitter with specified parameters
====================
*/
ParticleEmitter::ParticleEmitter(
Vector position,
float minSpeed,
float maxSpeed,
float angle,
float angleVar,
float minAccSpeed,
float maxAccSpeed,
float accAngle,
float accAngleVar,
float minRate,
float maxRate,
int srcColor,
int dstColor,
float minColorRate,
float maxColorRate,
qboolean wavy,
float minWavyDist,
float maxWavyDist,
float minLife,
float maxLife,
qboolean particles,
qboolean gravityWell,
float gravityWellStrength,
qboolean ballLightning,
int minBallLightningRadius,
int maxBallLightningRadius,
float lightningVar,
int lightningSubdivisions,
qboolean swarm,
int swarmfreq,
int swarmdelta
)
{
m_position = position;
m_minSpeed = minSpeed;
m_maxSpeed = maxSpeed;
m_angle = angle;
m_angleVar = angleVar;
m_minAccSpeed = minAccSpeed;
m_maxAccSpeed = maxAccSpeed;
m_accAngle = accAngle;
m_accAngleVar = accAngleVar;
m_maxRate = 1.0 / maxRate;
m_minRate = 1.0 / minRate;
m_srcColor = srcColor;
m_dstColor = dstColor;
m_minColorRate = minColorRate;
m_maxColorRate = maxColorRate;
m_minWavyDist = minWavyDist;
m_maxWavyDist = maxWavyDist;
m_minLife = minLife;
m_maxLife = maxLife;
m_wavy = wavy;
m_particles = particles;
m_gravityWellStrength = gravityWellStrength;
m_gravityWell = gravityWell;
m_ballLightning = ballLightning;
m_minBallLightningRadius = minBallLightningRadius;
m_lightningVar = lightningVar;
m_maxBallLightningRadius = maxBallLightningRadius;
m_lightningSubdivisions = lightningSubdivisions;
m_swarm = swarm;
m_swarmfrequency = swarmfreq;
m_swarmdelta = swarmdelta;
m_time = tr.refdef.time;
m_lasttime = m_time;
m_gravityEffectVector = vec_zero;
}
/*
====================
ParticleEmitter::ParticleEmitter
====================
*/
ParticleEmitter::ParticleEmitter()
{
m_lasttime = tr.refdef.time / 1000.0;
}
/*
====================
ParticleEmitter::~ParticleEmitter
====================
*/
ParticleEmitter::~ParticleEmitter() {}
/*
====================
ParticleEmitter::Emit
Creates new particles based on the current time
====================
*/
void ParticleEmitter::Emit(float currentTime)
{
Particle *p;
float rate;
float delta;
rate = GetRate();
delta = currentTime = m_lasttime;
if (!rate) {
m_lasttime = currentTime;
return;
}
for (; delta > rate; delta -= rate) {
p = new Particle(
m_position,
GetVelocity(),
GetAcceleration(),
GetSrcColor(),
GetDstColor(),
GetColorRate(),
IsWavy(),
GetWavyDistance(),
currentTime,
GetDieTime(currentTime),
m_maxSpeed,
GetPosition(),
IsSwarm(),
m_swarmfrequency,
m_swarmdelta
);
particleList.AddObject(p);
m_lasttime = currentTime;
}
}
/*
====================
ParticleEmitter::UpdateValues
Updates all particles with the last renderer time
====================
*/
void ParticleEmitter::UpdateValues()
{
int i;
int numParticles;
numParticles = particleList.NumObjects();
for (i = 1; i <= numParticles; i++) {
Particle *p = particleList.ObjectAt(i);
p->Update(m_lasttime);
}
}
/*
====================
ParticleEmitter::GetPosition
Returns the current particle position
====================
*/
Vector ParticleEmitter::GetPosition()
{
return m_position;
}
/*
====================
ParticleEmitter::GetVelocity
Returns the random velocity based on random angle, and random speed
====================
*/
Vector ParticleEmitter::GetVelocity()
{
float var;
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_angle, m_angleVar);
v = CalculateDirection(angle);
//
// Calculate the velocity from random speed
//
var = RandomizeRange(m_minSpeed, m_maxSpeed);
v *= var;
return v;
}
/*
====================
ParticleEmitter::GetVelocityDirection
Returns the direction of the random velocity
====================
*/
Vector ParticleEmitter::GetVelocityDirection()
{
float var;
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_angle, m_angleVar);
v = CalculateDirection(angle);
//
// Calculate the velocity from random speed
//
var = RandomizeRange(m_minSpeed, m_maxSpeed);
v *= var;
v.normalize();
return v;
}
/*
====================
ParticleEmitter::GetVelocityDirectionMin
Returns the random velocity direction based on random angle, and minimum speed
====================
*/
Vector ParticleEmitter::GetVelocityDirectionMin()
{
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_angle, m_angleVar);
v = CalculateDirection(angle);
// Use the minimum speed
v *= m_minSpeed;
v.normalize();
return v;
}
/*
====================
ParticleEmitter::GetVelocityDirectionMax
Returns the random velocity direction based on random angle, and maximum speed
====================
*/
Vector ParticleEmitter::GetVelocityDirectionMax()
{
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_angle, m_angleVar);
v = CalculateDirection(angle);
// Use the minimum speed
v *= m_maxSpeed;
v.normalize();
return v;
}
/*
====================
ParticleEmitter::GetAcceleration
Returns the random acceleration based on random angle, and random speed
====================
*/
Vector ParticleEmitter::GetAcceleration()
{
float var;
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_accAngle, m_accAngleVar);
v = CalculateDirection(angle);
//
// Calculate the velocity from random speed
//
var = RandomizeRange(m_minSpeed, m_maxSpeed);
v += m_gravityEffectVector * var;
return v;
}
/*
====================
ParticleEmitter::GetAccelerationDirection
Returns the direction of the random acceleration
====================
*/
Vector ParticleEmitter::GetAccelerationDirection()
{
float var;
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_accAngle, m_accAngleVar);
v = CalculateDirection(angle);
//
// Calculate the velocity from random speed
//
var = RandomizeRange(m_minSpeed, m_maxSpeed);
v += m_gravityEffectVector * var;
v.normalize();
return v;
}
/*
====================
ParticleEmitter::GetAccelerationDirectionMin
Returns the random acceleration direction based on random angle, and minimum speed
====================
*/
Vector ParticleEmitter::GetAccelerationDirectionMin()
{
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_accAngle, m_accAngleVar);
v = CalculateDirection(angle);
// Use the minimum speed
v += m_gravityEffectVector * m_minSpeed;
v.normalize();
return v;
}
/*
====================
ParticleEmitter::GetAccelerationDirectionMax
Returns the random acceleration direction based on random angle, and maximum speed
====================
*/
Vector ParticleEmitter::GetAccelerationDirectionMax()
{
float angle;
Vector v;
//
// Calculate the direction from random angle
//
angle = RandomizeAngle(m_accAngle, m_accAngleVar);
v = CalculateDirection(angle);
// Use the maximum speed
v += m_gravityEffectVector * m_minSpeed;
v.normalize();
return v;
}
/*
====================
ParticleEmitter::GetSrcColor
Returns the source color
====================
*/
int ParticleEmitter::GetSrcColor()
{
return m_srcColor;
}
/*
====================
ParticleEmitter::GetDstColor
Returns the destination color
====================
*/
int ParticleEmitter::GetDstColor()
{
return m_dstColor;
}
/*
====================
ParticleEmitter::GetGravityWellStrength
Returns the strength of the gravity
====================
*/
float ParticleEmitter::GetGravityWellStrength()
{
return m_gravityWellStrength;
}
/*
====================
ParticleEmitter::GetRate
Returns a random rate between minimum and maximum
====================
*/
float ParticleEmitter::GetRate()
{
return RandomizeRange(m_minRate, m_maxRate);
}
/*
====================
ParticleEmitter::GetColorRate
Returns a random color between minimum and maximum
====================
*/
float ParticleEmitter::GetColorRate()
{
return RandomizeRange(m_minColorRate, m_maxColorRate);
}
/*
====================
ParticleEmitter::GetWavyDistance
Returns a random wavy distance between minimum and maximum
====================
*/
float ParticleEmitter::GetWavyDistance()
{
return RandomizeRange(m_minWavyDist, m_maxWavyDist);
}
/*
====================
ParticleEmitter::GetDieTime
Returns a random time at which the particle will stop, between minimum and maximum
====================
*/
float ParticleEmitter::GetDieTime(float currentTime)
{
return currentTime + RandomizeRange(m_minLife, m_maxLife);
}
/*
====================
ParticleEmitter::GetBallLightningRadius
Returns a random lightning radius between minimum and maximum
====================
*/
int ParticleEmitter::GetBallLightningRadius()
{
return RandomizeRange(m_minBallLightningRadius, m_maxBallLightningRadius);
}
/*
====================
ParticleEmitter::GetLightningVar
Returns the maximum lightning value
====================
*/
float ParticleEmitter::GetLightningVar()
{
return m_lightningVar;
}
/*
====================
ParticleEmitter::GetLightningSubdivisions
Returns the number of lightning subdivisions
====================
*/
int ParticleEmitter::GetLightningSubdivisions()
{
return m_lightningSubdivisions;
}
/*
====================
ParticleEmitter::IsWavy
Returns whether or not this is a wavy particle
====================
*/
qboolean ParticleEmitter::IsWavy()
{
return m_wavy;
}
/*
====================
ParticleEmitter::IsGravityWell
Returns whether or not this particle has gravity
====================
*/
qboolean ParticleEmitter::IsGravityWell()
{
return m_gravityWell;
}
/*
====================
ParticleEmitter::IsParticles
Returns whether or not this contains particles
====================
*/
qboolean ParticleEmitter::IsParticles()
{
return m_particles;
}
/*
====================
ParticleEmitter::IsBallLightning
Returns whether or not ball lightning can be applied
====================
*/
qboolean ParticleEmitter::IsBallLightning()
{
return m_ballLightning;
}
/*
====================
ParticleEmitter::IsSwarm
Returns whether or not this is a swarm of particles
====================
*/
qboolean ParticleEmitter::IsSwarm()
{
return m_swarm;
}
/*
====================
ParticleEmitter::SetMinSpeed
Sets the minimum speed
====================
*/
void ParticleEmitter::SetMinSpeed(float value)
{
m_minSpeed = value;
}
/*
====================
ParticleEmitter::SetMaxSpeed
Sets the maximum speed
====================
*/
void ParticleEmitter::SetMaxSpeed(float value)
{
m_maxSpeed = value;
}
/*
====================
ParticleEmitter::SetAngle
Sets the angle/minimum angle
====================
*/
void ParticleEmitter::SetAngle(float value)
{
m_angle = value;
}
/*
====================
ParticleEmitter::SetAngleVar
Sets the maximum speed
====================
*/
void ParticleEmitter::SetAngleVar(float value)
{
m_angleVar = value;
}
/*
====================
ParticleEmitter::SetMinAccSpeed
Sets the minimum acceleration speed
====================
*/
void ParticleEmitter::SetMinAccSpeed(float value)
{
m_minAccSpeed = value;
}
/*
====================
ParticleEmitter::SetMaxAccSpeed
Sets the maximum acceleration speed
====================
*/
void ParticleEmitter::SetMaxAccSpeed(float value)
{
m_maxAccSpeed = value;
}
/*
====================
ParticleEmitter::SetAccAngle
Sets the acceleration angle / minimum acceleration angle
====================
*/
void ParticleEmitter::SetAccAngle(float value)
{
m_accAngle = value;
}
/*
====================
ParticleEmitter::SetAccAngleVar
Sets the maximum acceleration speed
====================
*/
void ParticleEmitter::SetAccAngleVar(float value)
{
m_accAngleVar = value;
}
/*
====================
ParticleEmitter::SetMinRate
Sets the minimum rate
====================
*/
void ParticleEmitter::SetMinRate(float value)
{
m_minRate = value;
}
/*
====================
ParticleEmitter::SetMaxRate
Sets the maximum rate
====================
*/
void ParticleEmitter::SetMaxRate(float value)
{
m_maxRate = value;
}
/*
====================
ParticleEmitter::SetSrcColor
Sets the source color
====================
*/
void ParticleEmitter::SetSrcColor(int value)
{
m_srcColor = value;
}
/*
====================
ParticleEmitter::SetDstColor
Sets the destination color
====================
*/
void ParticleEmitter::SetDstColor(int value)
{
m_dstColor = value;
}
/*
====================
ParticleEmitter::SetMinColorRate
Sets the minimum color rate
====================
*/
void ParticleEmitter::SetMinColorRate(float value)
{
m_minColorRate = value;
}
/*
====================
ParticleEmitter::SetMaxColorRate
Sets the maximum color rate
====================
*/
void ParticleEmitter::SetMaxColorRate(float value)
{
m_maxColorRate = value;
}
/*
====================
ParticleEmitter::SetWavy
Sets whether or not this is a wavy particle
====================
*/
void ParticleEmitter::SetWavy(int value)
{
m_wavy = value;
}
/*
====================
ParticleEmitter::SetParticles
Sets whether or not this contains particles
====================
*/
void ParticleEmitter::SetParticles(int value)
{
m_particles = value;
}
/*
====================
ParticleEmitter::SetGravityWell
Sets whether or not this has gravity
====================
*/
void ParticleEmitter::SetGravityWell(int value)
{
m_gravityWell = value;
}
/*
====================
ParticleEmitter::SetMinWavyDist
Sets the minimum wavy distance
====================
*/
void ParticleEmitter::SetMinWavyDist(float value)
{
m_minWavyDist = value;
}
/*
====================
ParticleEmitter::SetMaxWavyDist
Sets the maximum wavy distance
====================
*/
void ParticleEmitter::SetMaxWavyDist(float value)
{
m_maxWavyDist = value;
}
/*
====================
ParticleEmitter::SetMinLife
Sets the minimum life
====================
*/
void ParticleEmitter::SetMinLife(float value)
{
m_minLife = value;
}
/*
====================
ParticleEmitter::SetMaxLife
Sets the maximum life
====================
*/
void ParticleEmitter::SetMaxLife(float value)
{
m_maxLife = value;
}
/*
====================
ParticleEmitter::SetGravityWellStrength
Sets the strength of the gravity
====================
*/
void ParticleEmitter::SetGravityWellStrength(float value)
{
m_gravityWellStrength = value;
}
/*
====================
ParticleEmitter::SetGravityEffectVector
Sets the direction at which gravity is applied
====================
*/
void ParticleEmitter::SetGravityEffectVector(Vector value)
{
m_gravityEffectVector = value;
}
/*
====================
ParticleEmitter::SetBallLightning
Sets whether or not it has ball lightning
====================
*/
void ParticleEmitter::SetBallLightning(int value)
{
m_ballLightning = value;
}
/*
====================
ParticleEmitter::SetMinBallLightningRadius
Sets the minimum ball lightning radius
====================
*/
void ParticleEmitter::SetMinBallLightningRadius(int value)
{
m_minBallLightningRadius = value;
}
/*
====================
ParticleEmitter::SetMaxBallLightningRadius
Sets the maximum ball lightning radius
====================
*/
void ParticleEmitter::SetMaxBallLightningRadius(int value)
{
m_maxBallLightningRadius = value;
}
/*
====================
ParticleEmitter::SetLightningVar
Sets the maximum lightning value
====================
*/
void ParticleEmitter::SetLightningVar(float value)
{
m_lightningVar = value;
}
/*
====================
ParticleEmitter::SetLightningSubdivisions
Sets the number of subdivisions for the lightning
====================
*/
void ParticleEmitter::SetLightningSubdivisions(int value)
{
m_lightningSubdivisions = value;
}
/*
====================
ParticleEmitter::RandomizeRange
Returns a random value between the range [min, max]
====================
*/
float ParticleEmitter::RandomizeRange(float min, float max)
{
return min + (max - min) * (rand() / (float)RAND_MAX);
}
/*
====================
ParticleEmitter::RandomizeAngle
Returns a random angle between the range [min, max]
====================
*/
float ParticleEmitter::RandomizeAngle(float min, float max)
{
return min + ((rand() - (RAND_MAX / 2)) / (float)RAND_MAX) * max;
}
/*
====================
ParticleEmitter::CalculateDirection
Returns a random Vector direction based on the angle value
====================
*/
Vector ParticleEmitter::CalculateDirection(float value)
{
float deg = DEG2RAD(value);
return Vector(cos(deg), -sin(deg), 0);
}
/*
====================
ParticleEmitter::Load
Loads the specified particle emitter from the string buffer
====================
*/
void ParticleEmitter::Load(char **buf_p)
{
m_position[0] = atoi(COM_ParseExt(buf_p, qtrue));
m_position[1] = atoi(COM_ParseExt(buf_p, qtrue));
m_position[2] = atoi(COM_ParseExt(buf_p, qtrue));
m_srcColor = atoi(COM_ParseExt(buf_p, qtrue));
m_dstColor = atoi(COM_ParseExt(buf_p, qtrue));
m_minColorRate = atoi(COM_ParseExt(buf_p, qtrue));
m_maxColorRate = atoi(COM_ParseExt(buf_p, qtrue));
m_minRate = atof(COM_ParseExt(buf_p, qtrue));
m_maxRate = atof(COM_ParseExt(buf_p, qtrue));
m_minLife = atoi(COM_ParseExt(buf_p, qtrue));
m_maxLife = atoi(COM_ParseExt(buf_p, qtrue));
m_angle = atoi(COM_ParseExt(buf_p, qtrue));
m_angleVar = atoi(COM_ParseExt(buf_p, qtrue));
m_accAngle = atoi(COM_ParseExt(buf_p, qtrue));
m_accAngleVar = atoi(COM_ParseExt(buf_p, qtrue));
m_minSpeed = atof(COM_ParseExt(buf_p, qtrue));
m_maxSpeed = atof(COM_ParseExt(buf_p, qtrue));
m_minAccSpeed = atof(COM_ParseExt(buf_p, qtrue));
m_maxAccSpeed = atof(COM_ParseExt(buf_p, qtrue));
m_minWavyDist = atof(COM_ParseExt(buf_p, qtrue));
m_maxWavyDist = atof(COM_ParseExt(buf_p, qtrue));
m_wavy = atoi(COM_ParseExt(buf_p, qtrue));
m_particles = atoi(COM_ParseExt(buf_p, qtrue));
m_gravityWell = atoi(COM_ParseExt(buf_p, qtrue));
m_gravityWellStrength = atof(COM_ParseExt(buf_p, qtrue));
m_ballLightning = atoi(COM_ParseExt(buf_p, qtrue));
m_minBallLightningRadius = atof(COM_ParseExt(buf_p, qtrue));
m_maxBallLightningRadius = atof(COM_ParseExt(buf_p, qtrue));
m_lightningVar = atof(COM_ParseExt(buf_p, qtrue));
m_lightningSubdivisions = atof(COM_ParseExt(buf_p, qtrue));
m_swarm = atoi(COM_ParseExt(buf_p, qtrue));
m_swarmfrequency = atoi(COM_ParseExt(buf_p, qtrue));
m_swarmdelta = atoi(COM_ParseExt(buf_p, qtrue));
}
/*
====================
GhostTexture::GhostTexture
====================
*/
GhostTexture::GhostTexture()
{
m_image = NULL;
m_texture = NULL;
}
/*
====================
GhostTexture::SetTexel
Sets color at the specified xy texel location
====================
*/
void GhostTexture::SetTexel(int x, int y, unsigned int color)
{
m_texture[x + m_width * y] = color;
}
/*
====================
GhostTexture::Burn
Applies fade/burn effect to the ghost texture
====================
*/
void GhostTexture::Burn()
{
int x, y;
int r, g, b;
GLint pixel;
GLuint *line;
if (m_isfade) {
for (y = 1; y < m_height - 1; y++) {
line = &m_texture[m_width * y];
for (x = 1; x < m_width - 1; x++) {
r = (line[x] & 0xff) - m_faderate;
g = ((line[x] & 0xff00) >> 8) - m_faderate;
b = ((line[x] & 0xff0000) >> 16) - m_faderate;
if (r < 0) {
r = 0;
}
if (g < 0) {
g = 0;
}
if (b < 0) {
b = 0;
}
line[x] = (r) | (g << 8) | (b << 16);
}
}
} else if (m_isburn) {
for (y = 1; y < m_height - 1; y++) {
line = &m_texture[m_width * y];
for (x = 1; x < m_width - 1; x++) {
r = (((line[m_width + x] & 0xff) + (line[x + 1] & 0xff) + (line[x - 1] & 0xff) + (line[x] & 0xff)) >> 2)
- m_burnrate;
g = ((((line[m_width + x] & 0xFF00) >> 8) + ((line[x + 1] & 0xFF00) >> 8)
+ ((line[x - 1] & 0xFF00) >> 8) + ((line[x] & 0xFF00) >> 8))
>> 2)
- m_burnrate;
b = ((((line[m_width + x] & 0xFF0000) >> 16) + ((line[x + 1] & 0xFF0000) >> 16)
+ ((line[x - 1] & 0xFF0000) >> 16) + ((line[x] & 0xFF0000) >> 16))
>> 2)
- m_burnrate;
if (r < 0) {
r = 0;
}
if (g < 0) {
g = 0;
}
if (b < 0) {
b = 0;
}
pixel = (r) | (g << 8) | (b << 16);
if (pixel > 0) {
line[x] = pixel;
}
}
}
}
line = m_texture;
for (x = 0; x < m_width; x++) {
line[x] = 0;
}
for (y = 0; y < m_height; ++y) {
line[m_width * y] = 0;
line[m_width - 1 + m_width * y] = 0;
}
line = &m_texture[m_width * (m_height - 1)];
for (x = 0; x < m_width; x++) {
line[x] = 0;
}
}
/*
====================
GhostTexture::ComputeOutCode
Returns a flag value indicating if x/y variable reached xmin/xmax
====================
*/
outcode GhostTexture::ComputeOutCode(int x, int y, int xmin, int xmax, int ymin, int ymax)
{
outcode code = 0;
if (y > ymax) {
code = 1;
} else if (y < ymin) {
code = 2;
}
if (x > xmax) {
code |= 4;
} else if (x < xmin) {
code |= 8;
}
return code;
}
/*
====================
GhostTexture::ClipAndDrawLine
Draws a line from p0 to p1 with clipping
====================
*/
void GhostTexture::ClipAndDrawLine(Vector p0, Vector p1, int color)
{
int x0, x1;
int y0, y1;
int d;
int x, y;
int ax, ay;
int sx, sy;
int dx, dy;
int xmax, ymax;
bool accept;
outcode outcode0;
outcode outcode1;
x0 = p0.x;
y0 = p0.y;
x1 = p1.x;
y1 = p1.y;
xmax = m_width - 1;
ymax = m_height - 1;
accept = false;
outcode0 = ComputeOutCode(x0, y0, 0, xmax, 0, ymax);
outcode1 = ComputeOutCode(x1, y1, 0, xmax, 0, ymax);
while (outcode1 | outcode0) {
outcode outCodeOut;
if (outcode0 & outcode1) {
break;
}
outCodeOut = outcode0 ? outcode0 : outcode1;
if (outCodeOut & 1) {
x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
y = ymax;
} else if (outCodeOut & 2) {
x = x0 - y0 * (x1 - x0) / (y1 - y0);
y = 0;
} else if (outCodeOut & 4) {
x = xmax;
y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
} else if (outCodeOut * 8) {
x = 0;
y = y0 - x0 * (y1 - y0) / (x1 - x0);
} else {
x = x0;
y = y0;
}
if (outCodeOut == outcode0) {
y0 = y;
x0 = x;
outcode0 = ComputeOutCode(x, y, 0, xmax, 0, ymax);
} else {
x1 = x;
y1 = y;
outcode1 = ComputeOutCode(x, y, 0, xmax, 0, ymax);
}
}
accept = true;
if (!accept) {
return;
}
dx = x1 - x0;
if (dx < 0) {
dx = x0 - x1;
}
ax = dx * 2;
sx = dx >= 0 ? 1 : -1;
dy = y1 - y0;
if (dy < 0) {
dy = y0 - y1;
}
ay = dy * 2;
sy = dy >= 0 ? 1 : -1;
x = x0;
y = y0;
if (ax > ay) {
for (d = ay - (ax >> 1);; d += ay, x += sx) {
SetTexel(x, y, color);
if (x == x1) {
break;
}
if (d >= 0) {
y += sy;
d -= ax;
}
}
} else {
for (d = ax - (ay >> 1);; d += ax, y += sy) {
SetTexel(x, y, color);
if (y == y1) {
break;
}
if (d >= 0) {
x += sx;
d -= ay;
}
}
}
}
/*
====================
GhostTexture::RotateVector
Rotates the vector from angle
====================
*/
Vector GhostTexture::RotateVector(Vector v, float angle)
{
Vector vec;
float rad;
rad = DEG2RAD(angle);
vec.x = cos(rad) * v.x - sin(rad) * v.y;
vec.y = cos(rad) * v.y + sin(rad) * v.x;
vec.z = 0;
return vec;
}
/*
====================
GhostTexture::GenerateLightning
Generates a lightning effect
====================
*/
void GhostTexture::GenerateLightning(
Vector p1, Vector p2, int color, float angleVar, int numSubdivisions, int maxSubdivisions
)
{
Vector mid;
Vector delta;
float length;
if (numSubdivisions == maxSubdivisions) {
p1[0] += 0.5;
p1[1] += 0.5;
p2[0] += 0.5;
p2[1] += 0.5;
ClipAndDrawLine(p1, p2, color);
return;
}
delta = p2 - p1;
length = delta.length() * 0.5;
delta.normalize();
mid = RotateVector(delta, RandomizeRange(-angleVar, angleVar));
mid = p1 + mid * length;
GenerateLightning(p1, mid, color, angleVar, numSubdivisions + 1, maxSubdivisions);
GenerateLightning(mid, p2, color, angleVar, numSubdivisions + 1, maxSubdivisions);
}
/*
====================
GhostTexture::Update
Updates the ghost texture
====================
*/
void GhostTexture::Update()
{
Vector pos;
int i, j;
int numEmitters;
float currentTime;
currentTime = tr.refdef.time / 1000.0;
numEmitters = m_emitterList.NumObjects();
for (i = 1; i <= numEmitters; i++) {
ParticleEmitter *pe = m_emitterList.ObjectAt(i);
if (pe->IsGravityWell()) {
break;
}
if (pe->IsBallLightning()) {
Vector p1, p2;
if (pe->GetBallLightningRadius()) {
p1 = pe->GetPosition();
p2 = pe->GetVelocityDirection();
p2 = p1 + p2 * pe->GetBallLightningRadius();
GenerateLightning(p1, p2, pe->GetSrcColor(), pe->GetLightningVar(), 0, pe->GetLightningSubdivisions());
}
} else if (pe->IsParticles()) {
pe->Emit(currentTime);
} else {
Vector p = pe->GetPosition();
SetTexel(p.x, p.y, pe->GetSrcColor());
}
for (j = pe->particleList.NumObjects(); j > 0; j--) {
Particle *p = pe->particleList.ObjectAt(j);
p->Update(currentTime);
if (p->m_position.x < m_width && p->m_position.x >= 0 && p->m_position.y < m_height && p->m_position.y >= 0
&& currentTime <= p->GetDieTime()) {
SetTexel(p->m_position.x, p->m_position.y, p->m_color);
} else {
delete p;
pe->particleList.RemoveObjectAt(j);
}
}
}
}
/*
====================
R_UpdateGhostTextures
Updates all ghost textures
====================
*/
void R_UpdateGhostTextures()
{
int i;
int numTextures;
frameTime = tr.refdef.time - lastTime;
lastTime = tr.refdef.time;
numTextures = ghostManager.m_textureList.NumObjects();
for (i = 1; i <= numTextures; i++) {
GhostTexture *gt = ghostManager.m_textureList.ObjectAt(i);
gt->Update();
gt->Burn();
GL_BindToTMU(gt->m_image, 0);
qglTexImage2D(GL_TEXTURE_2D, 0, 3, gt->m_width, gt->m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, gt->m_texture);
}
}
/*
====================
R_SetGhostImage
Applies an image to the specified named ghost texture
====================
*/
void R_SetGhostImage(const char *name, image_t *image)
{
int i;
int numTextures;
numTextures = ghostManager.m_textureList.NumObjects();
for (i = 1; i <= numTextures; i++) {
GhostTexture *gt = ghostManager.m_textureList.ObjectAt(i);
if (gt->m_name == name) {
gt->m_image = image;
}
}
}
/*
====================
LoadGHOST
Loads a GHOST image file
====================
*/
void LoadGHOST(const char *name, byte **pic, int *width, int *height)
{
char *buf_p;
char *buffer;
int numPixels;
int numParticles;
int i;
GhostTexture *gt;
if (ri.FS_ReadFile(name, (void **)&buffer) == -1) {
return;
}
buf_p = buffer;
COM_ParseExt(&buf_p, qtrue);
*width = atoi(COM_ParseExt(&buf_p, qtrue));
*height = atoi(COM_ParseExt(&buf_p, qtrue));
numParticles = atoi(COM_ParseExt(&buf_p, qtrue));
numPixels = *height * *width;
*pic = new byte[numPixels * 4];
gt = new GhostTexture();
gt->m_width = *width;
gt->m_height = *height;
gt->m_texture = new unsigned int[numPixels];
gt->m_isburn = atoi(COM_ParseExt(&buf_p, qtrue));
gt->m_burnrate = atof(COM_ParseExt(&buf_p, qtrue));
gt->m_isfade = atoi(COM_ParseExt(&buf_p, qtrue));
gt->m_faderate = atof(COM_ParseExt(&buf_p, qtrue));
ghostManager.m_textureList.AddObject(gt);
for (i = 0; i < numParticles; i++) {
ParticleEmitter *pe = new ParticleEmitter();
gt->m_emitterList.AddObject(pe);
pe->Load(&buf_p);
}
}