//////////////////////////////////////////////////////////////////////////////////////// // RAVEN SOFTWARE - STAR WARS: JK II // (c) 2002 Activision // // World Effects // // //////////////////////////////////////////////////////////////////////////////////////// #include "../qcommon/exe_headers.h" #pragma warning( disable : 4512 ) // 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+1)) + min; } //////////////////////////////////////////////////////////////////////////////////////// // Externs & Fwd Decl. //////////////////////////////////////////////////////////////////////////////////////// extern qboolean ParseVector( const char **text, int count, float *v ); extern void SetViewportAndScissor( void ); //////////////////////////////////////////////////////////////////////////////////////// // Includes //////////////////////////////////////////////////////////////////////////////////////// #include "tr_local.h" #include "tr_WorldEffects.h" #include "../Ravl/CVec.h" #include "../Ratl/vector_vs.h" #include "../Ratl/bits_vs.h" #ifdef _XBOX #include "../win32/glw_win_dx8.h" #else #include "glext.h" #endif //////////////////////////////////////////////////////////////////////////////////////// // 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 #ifdef _XBOX #define POINTCACHE_CELL_SIZE 96.0f // Note to Vv: // you guys may want to look into lowering that number. I've optimized the storage // space by breaking it up into small boxes (weather zones) around the areas we care about // in order to speed up load time and reduce memory. A very high number here will mean // that weather related effects like rain, fog, snow, etc will bleed through to where // they shouldn't... #else #define POINTCACHE_CELL_SIZE 96.0f #endif //////////////////////////////////////////////////////////////////////////////////////// // Globals //////////////////////////////////////////////////////////////////////////////////////// float mMillisecondsElapsed = 0; float mSecondsElapsed = 0; bool mFrozen = false; CVec3 mGlobalWindVelocity; CVec3 mGlobalWindDirection; float mGlobalWindSpeed; int mParticlesRendered; //////////////////////////////////////////////////////////////////////////////////////// // Handy Functions //////////////////////////////////////////////////////////////////////////////////////// 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; ulong* 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 = (ulong *)Z_Malloc(arraySize*sizeof(ulong), 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; ulong contents; ulong bit; // Record The Extents Of The World Incase No Other Weather Zones Exist //--------------------------------------------------------------------- if (!mWeatherZones.size()) { Com_Printf("WARNING: No Weather Zones Encountered"); AddWeatherZone(tr.world->bmodels[0].bounds[0], tr.world->bmodels[0].bounds[1]); } // Iterate Over All Weather Zones //-------------------------------- for (int zone=0; zoneinDrawBlock); // start the draw block glw_state->inDrawBlock = true; glw_state->primitiveMode = D3DPT_POINTLIST; // update DX with any pending state changes glw_state->drawStride = 4; DWORD mask = D3DFVF_XYZ | D3DFVF_DIFFUSE; glw_state->device->SetVertexShader(mask); glw_state->shaderMask = mask; if(glw_state->matricesDirty[glwstate_t::MatrixMode_Model]) { glw_state->device->SetTransform(D3DTS_VIEW, glw_state->matrixStack[glwstate_t::MatrixMode_Model]->GetTop()); glw_state->matricesDirty[glwstate_t::MatrixMode_Model] = false; } // Update the texture and states // NOTE: Point sprites ALWAYS go on texture stage 3 glwstate_t::texturexlat_t::iterator it = glw_state->textureXlat.find(glw_state->currentTexture[0]); glw_state->device->SetTexture( 3, it->second.mipmap ); glw_state->device->SetTextureStageState(3, D3DTSS_COLOROP, glw_state->textureEnv[0]); glw_state->device->SetTextureStageState(3, D3DTSS_COLORARG1, D3DTA_TEXTURE); glw_state->device->SetTextureStageState(3, D3DTSS_COLORARG2, D3DTA_CURRENT); glw_state->device->SetTextureStageState(3, D3DTSS_ALPHAOP, glw_state->textureEnv[0]); glw_state->device->SetTextureStageState(3, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); glw_state->device->SetTextureStageState(3, D3DTSS_ALPHAARG2, D3DTA_CURRENT); glw_state->device->SetTextureStageState(3, D3DTSS_MAXANISOTROPY, it->second.anisotropy); glw_state->device->SetTextureStageState(3, D3DTSS_MINFILTER, it->second.minFilter); glw_state->device->SetTextureStageState(3, D3DTSS_MIPFILTER, it->second.mipFilter); glw_state->device->SetTextureStageState(3, D3DTSS_MAGFILTER, it->second.magFilter); glw_state->device->SetTextureStageState(3, D3DTSS_ADDRESSU, it->second.wrapU); glw_state->device->SetTextureStageState(3, D3DTSS_ADDRESSV, it->second.wrapV); glw_state->device->SetTexture( 0, NULL ); glw_state->device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE ); float attena = 1.0f, attenb = 0.0f, attenc = 0.0004f; glw_state->device->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE ); glw_state->device->SetRenderState( D3DRS_POINTSCALEENABLE, TRUE ); glw_state->device->SetRenderState( D3DRS_POINTSIZE, *((DWORD*)&size) ); glw_state->device->SetRenderState( D3DRS_POINTSIZE_MIN, *((DWORD*)&attenb)); glw_state->device->SetRenderState( D3DRS_POINTSCALE_A, *((DWORD*)&attena) ); glw_state->device->SetRenderState( D3DRS_POINTSCALE_B, *((DWORD*)&attenb) ); glw_state->device->SetRenderState( D3DRS_POINTSCALE_C, *((DWORD*)&attenc) ); // set vertex counters glw_state->numVertices = 0; glw_state->totalVertices = verts; int max = glw_state->totalVertices; if (max > 2040 / glw_state->drawStride) { max = 2040 / glw_state->drawStride; } glw_state->maxVertices = max; // open a draw packet int num_packets; if(verts == 0) { num_packets = 1; } else { num_packets = (verts / glw_state->maxVertices) + (!!(verts % glw_state->maxVertices)); } int cmd_size = num_packets * 3; int vert_size = glw_state->drawStride * verts; glw_state->device->BeginPush(vert_size + cmd_size + 2, &glw_state->drawArray); glw_state->drawArray[0] = D3DPUSH_ENCODE(D3DPUSH_SET_BEGIN_END, 1); glw_state->drawArray[1] = glw_state->primitiveMode; glw_state->drawArray[2] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG|D3DPUSH_INLINE_ARRAY, glw_state->drawStride * glw_state->maxVertices); glw_state->drawArray += 3; } static void pointEnd() { glw_state->device->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE ); glw_state->device->SetRenderState( D3DRS_POINTSCALEENABLE, FALSE ); glw_state->device->SetTexture( 3, NULL ); glw_state->device->SetTextureStageState( 3, D3DTSS_COLOROP, D3DTOP_DISABLE ); } #endif // _XBOX //////////////////////////////////////////////////////////////////////////////////////// // Particle Cloud //////////////////////////////////////////////////////////////////////////////////////// class CWeatherParticleCloud { private: //////////////////////////////////////////////////////////////////////////////////// // DYNAMIC MEMORY //////////////////////////////////////////////////////////////////////////////////// image_t* mImage; CWeatherParticle* mParticles; private: //////////////////////////////////////////////////////////////////////////////////// // RUN TIME VARIANTS //////////////////////////////////////////////////////////////////////////////////// float mSpawnSpeed; CVec3 mSpawnPlaneNorm; CVec3 mSpawnPlaneRight; CVec3 mSpawnPlaneUp; SVecRange mRange; CVec3 mCameraPosition; CVec3 mCameraForward; CVec3 mCameraLeft; CVec3 mCameraDown; CVec3 mCameraLeftPlusUp; CVec3 mCameraLeftMinusUp; int mParticleCountRender; int mGLModeEnum; bool mPopulated; public: //////////////////////////////////////////////////////////////////////////////////// // CONSTANTS //////////////////////////////////////////////////////////////////////////////////// bool mOrientWithVelocity; float mSpawnPlaneSize; float mSpawnPlaneDistance; SVecRange mSpawnRange; float mGravity; // How much gravity affects the velocity of a particle CVec4 mColor; // RGBA color int mVertexCount; // 3 for triangle, 4 for quad, other numbers not supported float mWidth; float mHeight; int mBlendMode; // 0 = ALPHA, 1 = SRC->SRC 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; #ifdef _XBOX if(mVertexCount == 1) mGLModeEnum = GL_POINTS; else #endif 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.mMin = 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 //--------------------------- /* if (mGLModeEnum==GL_POINTS && qglPointParameteriNV) { qglEnable(GL_POINT_SPRITE_NV); qglPointSize(mWidth); qglPointParameterfEXT( GL_POINT_SIZE_MIN_EXT, 4.0f ); qglPointParameterfEXT( GL_POINT_SIZE_MAX_EXT, 2047.0f ); qglTexEnvi(GL_POINT_SPRITE_NV, GL_COORD_REPLACE_NV, GL_TRUE); } else */ //FIXME use this extension? const float attenuation[3] = { 1, 0.0, 0.0004 }; #ifdef _XBOX if (mGLModeEnum==GL_POINTS) { pointBegin(mParticleCountRender, mWidth); } #else if (mGLModeEnum == GL_POINTS && qglPointParameterfEXT) { //fixme use custom parameters but gotta make sure it expects them on same scale first qglPointSize(10.0); qglPointParameterfEXT(GL_POINT_SIZE_MIN_EXT, 1.0); qglPointParameterfEXT(GL_POINT_SIZE_MAX_EXT, 4.0); qglPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, (float *)attenuation); } #endif else { 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(); #ifdef _XBOX qglBeginEXT(mGLModeEnum, mParticleCountRender*mVertexCount, mParticleCountRender, 0, mParticleCountRender*mVertexCount, 0); #endif } // Begin //------- #ifndef _XBOX qglBegin(mGLModeEnum); #endif 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 Point //---------------- if (mGLModeEnum==GL_POINTS) { qglVertex3fv(part->mPosition.v); } // Render A Triangle //------------------- else 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(); if (mGLModeEnum==GL_POINTS) { #ifdef _XBOX pointEnd(); #else //qglDisable(GL_POINT_SPRITE_NV); //qglTexEnvi(GL_POINT_SPRITE_NV, GL_COORD_REPLACE_NV, GL_FALSE); #endif } else { //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(Com_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