/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program 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 this program; if not, see .
===========================================================================
*/
////////////////////////////////////////////////////////////////////////////////////////
// RAVEN SOFTWARE - STAR WARS: JK II
// (c) 2002 Activision
//
// World Effects
//
//
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////////////
#include "tr_local.h"
#include "tr_WorldEffects.h"
#include "Ravl/CVec.h"
#include "Ratl/vector_vs.h"
#include "Ratl/bits_vs.h"
#include "glext.h"
////////////////////////////////////////////////////////////////////////////////////////
// Defines
////////////////////////////////////////////////////////////////////////////////////////
#define GLS_ALPHA (GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA)
#define MAX_WIND_ZONES 10
#define MAX_WEATHER_ZONES 10
#define MAX_PUFF_SYSTEMS 2
#define MAX_PARTICLE_CLOUDS 5
#define POINTCACHE_CELL_SIZE 96.0f
////////////////////////////////////////////////////////////////////////////////////////
// Globals
////////////////////////////////////////////////////////////////////////////////////////
float mMillisecondsElapsed = 0;
float mSecondsElapsed = 0;
bool mFrozen = false;
CVec3 mGlobalWindVelocity;
CVec3 mGlobalWindDirection;
float mGlobalWindSpeed;
int mParticlesRendered;
////////////////////////////////////////////////////////////////////////////////////////
// Handy Functions
////////////////////////////////////////////////////////////////////////////////////////
// Returns a float min <= x < max (exclusive; will get max - 0.00001; but never max)
inline float WE_flrand(float min, float max) {
return ((rand() * (max - min)) / (RAND_MAX)) + min;
}
////////////////////////////////////////////////////////////////////////////////////////
// Externs & Fwd Decl.
////////////////////////////////////////////////////////////////////////////////////////
extern void SetViewportAndScissor( void );
inline void VectorFloor(vec3_t in)
{
in[0] = floorf(in[0]);
in[1] = floorf(in[1]);
in[2] = floorf(in[2]);
}
inline void VectorCeil(vec3_t in)
{
in[0] = ceilf(in[0]);
in[1] = ceilf(in[1]);
in[2] = ceilf(in[2]);
}
inline float FloatRand(void)
{
return ((float)rand() / (float)RAND_MAX);
}
inline void SnapFloatToGrid(float& f, int GridSize)
{
f = (int)(f);
bool fNeg = (f<0);
if (fNeg)
{
f *= -1; // Temporarly make it positive
}
int Offset = ((int)(f) % (int)(GridSize));
int OffsetAbs = abs(Offset);
if (OffsetAbs>(GridSize/2))
{
Offset = (GridSize - OffsetAbs) * -1;
}
f -= Offset;
if (fNeg)
{
f *= -1; // Put It Back To Negative
}
f = (int)(f);
assert(((int)(f)%(int)(GridSize)) == 0);
}
inline void SnapVectorToGrid(CVec3& Vec, int GridSize)
{
SnapFloatToGrid(Vec[0], GridSize);
SnapFloatToGrid(Vec[1], GridSize);
SnapFloatToGrid(Vec[2], GridSize);
}
////////////////////////////////////////////////////////////////////////////////////////
// Range Structures
////////////////////////////////////////////////////////////////////////////////////////
struct SVecRange
{
CVec3 mMins;
CVec3 mMaxs;
inline void Clear()
{
mMins.Clear();
mMaxs.Clear();
}
inline void Pick(CVec3& V)
{
V[0] = WE_flrand(mMins[0], mMaxs[0]);
V[1] = WE_flrand(mMins[1], mMaxs[1]);
V[2] = WE_flrand(mMins[2], mMaxs[2]);
}
inline void Wrap(CVec3& V, SVecRange &spawnRange)
{
if (V[0]mMaxs[0])
{
const float d = V[0]-mMaxs[0];
V[0] = mMins[0]+fmod(d, mMaxs[0]-mMins[0]);
}
if (V[1]mMaxs[1])
{
const float d = V[1]-mMaxs[1];
V[1] = mMins[1]+fmod(d, mMaxs[1]-mMins[1]);
}
if (V[2]mMaxs[2])
{
const float d = V[2]-mMaxs[2];
V[2] = mMins[2]+fmod(d, mMaxs[2]-mMins[2]);
}
}
inline bool In(const CVec3& V)
{
return (V>mMins && VmMin && VmMin && V TFlags;
float mAlpha;
TFlags mFlags;
CVec3 mPosition;
CVec3 mVelocity;
float mMass; // A higher number will more greatly resist force and result in greater gravity
};
////////////////////////////////////////////////////////////////////////////////////////
// The Wind
////////////////////////////////////////////////////////////////////////////////////////
class CWindZone
{
public:
bool mGlobal;
SVecRange mRBounds;
SVecRange mRVelocity;
SIntRange mRDuration;
SIntRange mRDeadTime;
float mMaxDeltaVelocityPerUpdate;
float mChanceOfDeadTime;
CVec3 mCurrentVelocity;
CVec3 mTargetVelocity;
int mTargetVelocityTimeRemaining;
public:
////////////////////////////////////////////////////////////////////////////////////
// Initialize - Will setup default values for all data
////////////////////////////////////////////////////////////////////////////////////
void Initialize()
{
mRBounds.Clear();
mGlobal = true;
mRVelocity.mMins = -1500.0f;
mRVelocity.mMins[2] = -10.0f;
mRVelocity.mMaxs = 1500.0f;
mRVelocity.mMaxs[2] = 10.0f;
mMaxDeltaVelocityPerUpdate = 10.0f;
mRDuration.mMin = 1000;
mRDuration.mMax = 2000;
mChanceOfDeadTime = 0.3f;
mRDeadTime.mMin = 1000;
mRDeadTime.mMax = 3000;
mCurrentVelocity.Clear();
mTargetVelocity.Clear();
mTargetVelocityTimeRemaining = 0;
}
////////////////////////////////////////////////////////////////////////////////////
// Update - Changes wind when current target velocity expires
////////////////////////////////////////////////////////////////////////////////////
void Update()
{
if (mTargetVelocityTimeRemaining==0)
{
if (FloatRand() mMaxDeltaVelocityPerUpdate)
{
DeltaVelocityLen = mMaxDeltaVelocityPerUpdate;
}
DeltaVelocity *= (DeltaVelocityLen);
mCurrentVelocity += DeltaVelocity;
}
}
};
ratl::vector_vs mWindZones;
bool R_GetWindVector(vec3_t windVector)
{
VectorCopy(mGlobalWindDirection.v, windVector);
return true;
}
bool R_GetWindSpeed(float &windSpeed)
{
windSpeed = mGlobalWindSpeed;
return true;
}
bool R_GetWindGusting()
{
return (mGlobalWindSpeed>1000.0f);
}
////////////////////////////////////////////////////////////////////////////////////////
// Outside Point Cache
////////////////////////////////////////////////////////////////////////////////////////
class COutside
{
public:
////////////////////////////////////////////////////////////////////////////////////
//Global Public Outside Variables
////////////////////////////////////////////////////////////////////////////////////
bool mOutsideShake;
float mOutsidePain;
private:
////////////////////////////////////////////////////////////////////////////////////
// The Outside Cache
////////////////////////////////////////////////////////////////////////////////////
bool mCacheInit; // Has It Been Cached?
struct SWeatherZone
{
static bool mMarkedOutside;
uint32_t* mPointCache;
SVecRange mExtents;
SVecRange mSize;
int mWidth;
int mHeight;
int mDepth;
////////////////////////////////////////////////////////////////////////////////////
// Convert To Cell
////////////////////////////////////////////////////////////////////////////////////
inline void ConvertToCell(const CVec3& pos, int& x, int& y, int& z, int& bit)
{
x = (int)((pos[0] / POINTCACHE_CELL_SIZE) - mSize.mMins[0]);
y = (int)((pos[1] / POINTCACHE_CELL_SIZE) - mSize.mMins[1]);
z = (int)((pos[2] / POINTCACHE_CELL_SIZE) - mSize.mMins[2]);
bit = (z & 31);
z >>= 5;
}
////////////////////////////////////////////////////////////////////////////////////
// CellOutside - Test to see if a given cell is outside
////////////////////////////////////////////////////////////////////////////////////
inline bool CellOutside(int x, int y, int z, int bit)
{
if ((x < 0 || x >= mWidth) || (y < 0 || y >= mHeight) || (z < 0 || z >= mDepth) || (bit < 0 || bit >= 32))
{
return !(mMarkedOutside);
}
return (mMarkedOutside==(!!(mPointCache[((z * mWidth * mHeight) + (y * mWidth) + x)]&(1 << bit))));
}
};
ratl::vector_vs mWeatherZones;
private:
////////////////////////////////////////////////////////////////////////////////////
// Iteration Variables
////////////////////////////////////////////////////////////////////////////////////
int mWCells;
int mHCells;
int mXCell;
int mYCell;
int mZBit;
int mXMax;
int mYMax;
int mZMax;
private:
////////////////////////////////////////////////////////////////////////////////////
// Contents Outside
////////////////////////////////////////////////////////////////////////////////////
inline bool ContentsOutside(int contents)
{
if (contents&CONTENTS_WATER || contents&CONTENTS_SOLID)
{
return false;
}
if (mCacheInit)
{
if (SWeatherZone::mMarkedOutside)
{
return (!!(contents&CONTENTS_OUTSIDE));
}
return (!(contents&CONTENTS_INSIDE));
}
return !!(contents&CONTENTS_OUTSIDE);
}
public:
////////////////////////////////////////////////////////////////////////////////////
// Constructor - Will setup default values for all data
////////////////////////////////////////////////////////////////////////////////////
void Reset()
{
mOutsideShake = false;
mOutsidePain = 0.0;
mCacheInit = false;
SWeatherZone::mMarkedOutside = false;
for (int wz=0; wz> 5;
int arraySize = (Wz.mWidth * Wz.mHeight * Wz.mDepth);
Wz.mPointCache = (uint32_t *)Z_Malloc(arraySize*sizeof(uint32_t), TAG_POINTCACHE, qtrue);
}
}
////////////////////////////////////////////////////////////////////////////////////
// Cache - Will Scan the World, Creating The Cache
////////////////////////////////////////////////////////////////////////////////////
void Cache()
{
if (!tr.world || mCacheInit)
{
return;
}
CVec3 CurPos;
CVec3 Size;
CVec3 Mins;
int x, y, z, q, zbase;
bool curPosOutside;
uint32_t contents;
uint32_t bit;
// Record The Extents Of The World Incase No Other Weather Zones Exist
//---------------------------------------------------------------------
if (!mWeatherZones.size())
{
ri.Printf( PRINT_ALL, "WARNING: No Weather Zones Encountered\n");
AddWeatherZone(tr.world->bmodels[0].bounds[0], tr.world->bmodels[0].bounds[1]);
}
// Iterate Over All Weather Zones
//--------------------------------
for (int zone=0; zoneSRC
int mFilterMode; // 0 = LINEAR, 1 = NEAREST
float mFade; // How much to fade in and out 1.0 = instant, 0.01 = very slow
SFloatRange mRotation;
float mRotationDelta;
float mRotationDeltaTarget;
float mRotationCurrent;
SIntRange mRotationChangeTimer;
int mRotationChangeNext;
SFloatRange mMass; // Determines how slowness to accelerate, higher number = slower
float mFrictionInverse; // How much air friction does this particle have 1.0=none, 0.0=nomove
int mParticleCount;
bool mWaterParticles;
public:
////////////////////////////////////////////////////////////////////////////////////
// Initialize - Create Image, Particles, And Setup All Values
////////////////////////////////////////////////////////////////////////////////////
void Initialize(int count, const char* texturePath, int VertexCount=4)
{
Reset();
assert(mParticleCount==0 && mParticles==0);
assert(mImage==0);
// Create The Image
//------------------
mImage = R_FindImageFile(texturePath, qfalse, qfalse, qfalse, GL_CLAMP);
if (!mImage)
{
Com_Error(ERR_DROP, "CWeatherParticleCloud: Could not texture %s", texturePath);
}
GL_Bind(mImage);
// Create The Particles
//----------------------
mParticleCount = count;
mParticles = new CWeatherParticle[mParticleCount];
CWeatherParticle* part=0;
for (int particleNum=0; particleNummPosition.Clear();
part->mVelocity.Clear();
part->mAlpha = 0.0f;
mMass.Pick(part->mMass);
}
mVertexCount = VertexCount;
mGLModeEnum = (mVertexCount==3)?(GL_TRIANGLES):(GL_QUADS);
}
////////////////////////////////////////////////////////////////////////////////////
// Reset - Initializes all data to default values
////////////////////////////////////////////////////////////////////////////////////
void Reset()
{
if (mImage)
{
// TODO: Free Image?
}
mImage = 0;
if (mParticleCount)
{
delete [] mParticles;
}
mParticleCount = 0;
mParticles = 0;
mPopulated = 0;
// These Are The Default Startup Values For Constant Data
//========================================================
mOrientWithVelocity = false;
mWaterParticles = false;
mSpawnPlaneDistance = 500;
mSpawnPlaneSize = 500;
mSpawnRange.mMins = -(mSpawnPlaneDistance*1.25f);
mSpawnRange.mMaxs = (mSpawnPlaneDistance*1.25f);
mGravity = 300.0f; // Units Per Second
mColor = 1.0f;
mVertexCount = 4;
mWidth = 1.0f;
mHeight = 1.0f;
mBlendMode = 0;
mFilterMode = 0;
mFade = 10.0f;
mRotation.Clear();
mRotationDelta = 0.0f;
mRotationDeltaTarget= 0.0f;
mRotationCurrent = 0.0f;
mRotationChangeNext = -1;
mRotation.mMin = -0.7f;
mRotation.mMax = 0.7f;
mRotationChangeTimer.mMin = 500;
mRotationChangeTimer.mMax = 2000;
mMass.mMin = 5.0f;
mMass.mMax = 10.0f;
mFrictionInverse = 0.7f; // No Friction?
}
////////////////////////////////////////////////////////////////////////////////////
// Constructor - Will setup default values for all data
////////////////////////////////////////////////////////////////////////////////////
CWeatherParticleCloud()
{
mImage = 0;
mParticleCount = 0;
Reset();
}
////////////////////////////////////////////////////////////////////////////////////
// Initialize - Will setup default values for all data
////////////////////////////////////////////////////////////////////////////////////
~CWeatherParticleCloud()
{
Reset();
}
////////////////////////////////////////////////////////////////////////////////////
// UseSpawnPlane - Check To See If We Should Spawn On A Plane, Or Just Wrap The Box
////////////////////////////////////////////////////////////////////////////////////
inline bool UseSpawnPlane()
{
return (mGravity!=0.0f);
}
////////////////////////////////////////////////////////////////////////////////////
// Update - Applies All Physics Forces To All Contained Particles
////////////////////////////////////////////////////////////////////////////////////
void Update()
{
CWeatherParticle* part=0;
CVec3 partForce;
CVec3 partMoved;
CVec3 partToCamera;
bool partRendering;
bool partOutside;
bool partInRange;
bool partInView;
int particleNum;
float particleFade = (mFade * mSecondsElapsed);
/* TODO: Non Global Wind Zones
CWindZone* wind=0;
int windNum;
int windCount = mWindZones.size();
*/
// Compute Camera
//----------------
{
mCameraPosition = backEnd.viewParms.ori.origin;
mCameraForward = backEnd.viewParms.ori.axis[0];
mCameraLeft = backEnd.viewParms.ori.axis[1];
mCameraDown = backEnd.viewParms.ori.axis[2];
if (mRotationChangeNext!=-1)
{
if (mRotationChangeNext==0)
{
mRotation.Pick(mRotationDeltaTarget);
mRotationChangeTimer.Pick(mRotationChangeNext);
if (mRotationChangeNext<=0)
{
mRotationChangeNext = 1;
}
}
mRotationChangeNext--;
float RotationDeltaDifference = (mRotationDeltaTarget - mRotationDelta);
if (fabsf(RotationDeltaDifference)>0.01)
{
mRotationDelta += RotationDeltaDifference; // Blend To New Delta
}
mRotationCurrent += (mRotationDelta * mSecondsElapsed);
float s = sinf(mRotationCurrent);
float c = cosf(mRotationCurrent);
CVec3 TempCamLeft(mCameraLeft);
mCameraLeft *= (c * mWidth);
mCameraLeft.ScaleAdd(mCameraDown, (s * mWidth * -1.0f));
mCameraDown *= (c * mHeight);
mCameraDown.ScaleAdd(TempCamLeft, (s * mHeight));
}
else
{
mCameraLeft *= mWidth;
mCameraDown *= mHeight;
}
}
// Compute Global Force
//----------------------
CVec3 force;
{
force.Clear();
// Apply Gravity
//---------------
force[2] = -1.0f * mGravity;
// Apply Wind Velocity
//---------------------
force += mGlobalWindVelocity;
}
// Update Range
//--------------
{
mRange.mMins = mCameraPosition + mSpawnRange.mMins;
mRange.mMaxs = mCameraPosition + mSpawnRange.mMaxs;
// If Using A Spawn Plane, Increase The Range Box A Bit To Account For Rotation On The Spawn Plane
//-------------------------------------------------------------------------------------------------
if (UseSpawnPlane())
{
for (int dim=0; dim<3; dim++)
{
if (force[dim]>0.01)
{
mRange.mMins[dim] -= (mSpawnPlaneDistance/2.0f);
}
else if (force[dim]<-0.01)
{
mRange.mMaxs[dim] += (mSpawnPlaneDistance/2.0f);
}
}
mSpawnPlaneNorm = force;
mSpawnSpeed = VectorNormalize(mSpawnPlaneNorm.v);
MakeNormalVectors(mSpawnPlaneNorm.v, mSpawnPlaneRight.v, mSpawnPlaneUp.v);
if (mOrientWithVelocity)
{
mCameraDown = mSpawnPlaneNorm;
mCameraDown *= (mHeight * -1);
}
}
// Optimization For Quad Position Calculation
//--------------------------------------------
if (mVertexCount==4)
{
mCameraLeftPlusUp = (mCameraLeft - mCameraDown);
mCameraLeftMinusUp = (mCameraLeft + mCameraDown);
}
else
{
mCameraLeftPlusUp = (mCameraDown + mCameraLeft); // should really be called mCamera Left + Down
}
}
// Stop All Additional Processing
//--------------------------------
if (mFrozen)
{
return;
}
// Now Update All Particles
//--------------------------
mParticleCountRender = 0;
for (particleNum=0; particleNummPosition); // First Time Spawn Location
}
// Grab The Force And Apply Non Global Wind
//------------------------------------------
partForce = force;
partForce /= part->mMass;
// Apply The Force
//-----------------
part->mVelocity += partForce;
part->mVelocity *= mFrictionInverse;
part->mPosition.ScaleAdd(part->mVelocity, mSecondsElapsed);
partToCamera = (part->mPosition - mCameraPosition);
partRendering = part->mFlags.get_bit(CWeatherParticle::FLAG_RENDER);
partOutside = mOutside.PointOutside(part->mPosition, mWidth, mHeight);
partInRange = mRange.In(part->mPosition);
partInView = (partOutside && partInRange && (partToCamera.Dot(mCameraForward)>0.0f));
// Process Respawn
//-----------------
if (!partInRange && !partRendering)
{
part->mVelocity.Clear();
// Reselect A Position On The Spawn Plane
//----------------------------------------
if (UseSpawnPlane())
{
part->mPosition = mCameraPosition;
part->mPosition -= (mSpawnPlaneNorm* mSpawnPlaneDistance);
part->mPosition += (mSpawnPlaneRight*WE_flrand(-mSpawnPlaneSize, mSpawnPlaneSize));
part->mPosition += (mSpawnPlaneUp* WE_flrand(-mSpawnPlaneSize, mSpawnPlaneSize));
}
// Otherwise, Just Wrap Around To The Other End Of The Range
//-----------------------------------------------------------
else
{
mRange.Wrap(part->mPosition, mSpawnRange);
}
partInRange = true;
}
// Process Fade
//--------------
{
// Start A Fade Out
//------------------
if (partRendering && !partInView)
{
part->mFlags.clear_bit(CWeatherParticle::FLAG_FADEIN);
part->mFlags.set_bit(CWeatherParticle::FLAG_FADEOUT);
}
// Switch From Fade Out To Fade In
//---------------------------------
else if (partRendering && partInView && part->mFlags.get_bit(CWeatherParticle::FLAG_FADEOUT))
{
part->mFlags.set_bit(CWeatherParticle::FLAG_FADEIN);
part->mFlags.clear_bit(CWeatherParticle::FLAG_FADEOUT);
}
// Start A Fade In
//-----------------
else if (!partRendering && partInView)
{
partRendering = true;
part->mAlpha = 0.0f;
part->mFlags.set_bit(CWeatherParticle::FLAG_RENDER);
part->mFlags.set_bit(CWeatherParticle::FLAG_FADEIN);
part->mFlags.clear_bit(CWeatherParticle::FLAG_FADEOUT);
}
// Update Fade
//-------------
if (partRendering)
{
// Update Fade Out
//-----------------
if (part->mFlags.get_bit(CWeatherParticle::FLAG_FADEOUT))
{
part->mAlpha -= particleFade;
if (part->mAlpha<=0.0f)
{
part->mAlpha = 0.0f;
part->mFlags.clear_bit(CWeatherParticle::FLAG_FADEOUT);
part->mFlags.clear_bit(CWeatherParticle::FLAG_FADEIN);
part->mFlags.clear_bit(CWeatherParticle::FLAG_RENDER);
partRendering = false;
}
}
// Update Fade In
//----------------
else if (part->mFlags.get_bit(CWeatherParticle::FLAG_FADEIN))
{
part->mAlpha += particleFade;
if (part->mAlpha>=mColor[3])
{
part->mFlags.clear_bit(CWeatherParticle::FLAG_FADEIN);
part->mAlpha = mColor[3];
}
}
}
}
// Keep Track Of The Number Of Particles To Render
//-------------------------------------------------
if (part->mFlags.get_bit(CWeatherParticle::FLAG_RENDER))
{
mParticleCountRender ++;
}
}
mPopulated = true;
}
////////////////////////////////////////////////////////////////////////////////////
// Render -
////////////////////////////////////////////////////////////////////////////////////
void Render()
{
CWeatherParticle* part=0;
int particleNum;
// Set The GL State And Image Binding
//------------------------------------
GL_State((mBlendMode==0)?(GLS_ALPHA):(GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE));
GL_Bind(mImage);
// Enable And Disable Things
//---------------------------
qglEnable(GL_TEXTURE_2D);
//qglDisable(GL_CULL_FACE);
//naughty, you are making the assumption that culling is on when you get here. -rww
GL_Cull(CT_TWO_SIDED);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mFilterMode==0)?(GL_LINEAR):(GL_NEAREST));
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (mFilterMode==0)?(GL_LINEAR):(GL_NEAREST));
// Setup Matrix Mode And Translation
//-----------------------------------
qglMatrixMode(GL_MODELVIEW);
qglPushMatrix();
// Begin
//-------
qglBegin(mGLModeEnum);
for (particleNum=0; particleNummFlags.get_bit(CWeatherParticle::FLAG_RENDER))
{
continue;
}
// Blend Mode Zero -> Apply Alpha Just To Alpha Channel
//------------------------------------------------------
if (mBlendMode==0)
{
qglColor4f(mColor[0], mColor[1], mColor[2], part->mAlpha);
}
// Otherwise Apply Alpha To All Channels
//---------------------------------------
else
{
qglColor4f(mColor[0]*part->mAlpha, mColor[1]*part->mAlpha, mColor[2]*part->mAlpha, mColor[3]*part->mAlpha);
}
// Render A Triangle
//-------------------
if (mVertexCount==3)
{
qglTexCoord2f(1.0, 0.0);
qglVertex3f(part->mPosition[0],
part->mPosition[1],
part->mPosition[2]);
qglTexCoord2f(0.0, 1.0);
qglVertex3f(part->mPosition[0] + mCameraLeft[0],
part->mPosition[1] + mCameraLeft[1],
part->mPosition[2] + mCameraLeft[2]);
qglTexCoord2f(0.0, 0.0);
qglVertex3f(part->mPosition[0] + mCameraLeftPlusUp[0],
part->mPosition[1] + mCameraLeftPlusUp[1],
part->mPosition[2] + mCameraLeftPlusUp[2]);
}
// Render A Quad
//---------------
else
{
// Left bottom.
qglTexCoord2f( 0.0, 0.0 );
qglVertex3f(part->mPosition[0] - mCameraLeftMinusUp[0],
part->mPosition[1] - mCameraLeftMinusUp[1],
part->mPosition[2] - mCameraLeftMinusUp[2] );
// Right bottom.
qglTexCoord2f( 1.0, 0.0 );
qglVertex3f(part->mPosition[0] - mCameraLeftPlusUp[0],
part->mPosition[1] - mCameraLeftPlusUp[1],
part->mPosition[2] - mCameraLeftPlusUp[2] );
// Right top.
qglTexCoord2f( 1.0, 1.0 );
qglVertex3f(part->mPosition[0] + mCameraLeftMinusUp[0],
part->mPosition[1] + mCameraLeftMinusUp[1],
part->mPosition[2] + mCameraLeftMinusUp[2] );
// Left top.
qglTexCoord2f( 0.0, 1.0 );
qglVertex3f(part->mPosition[0] + mCameraLeftPlusUp[0],
part->mPosition[1] + mCameraLeftPlusUp[1],
part->mPosition[2] + mCameraLeftPlusUp[2] );
}
}
qglEnd();
//qglEnable(GL_CULL_FACE);
//you don't need to do this when you are properly setting cull state.
qglPopMatrix();
mParticlesRendered += mParticleCountRender;
}
};
ratl::vector_vs mParticleClouds;
////////////////////////////////////////////////////////////////////////////////////////
// Init World Effects - Will Iterate Over All Particle Clouds, Clear Them Out, And Erase
////////////////////////////////////////////////////////////////////////////////////////
void R_InitWorldEffects(void)
{
srand(ri.Milliseconds());
for (int i=0; i1000.0f)
{
mMillisecondsElapsed = 1000.0f;
}
mSecondsElapsed = (mMillisecondsElapsed / 1000.0f);
// Make Sure We Are Always Outside Cached
//----------------------------------------
if (!mOutside.Initialized())
{
mOutside.Cache();
}
else
{
// Update All Wind Zones
//-----------------------
if (!mFrozen)
{
mGlobalWindVelocity.Clear();
for (int wz=0; wz