/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program 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 3 of the License, or (at your option) any later version. 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 . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "hsTypes.h" #include "plParticleFiller.h" // Core background #include "hsTemplates.h" #include "hsFastMath.h" #include "plPipeline.h" #include "plViewTransform.h" // Getting at the destination data #include "pnSceneObject/plDrawInterface.h" #include "plDrawable/plDrawableSpans.h" #include "plPipeline/plGBufferGroup.h" // For shading #include "plGLight/plLightInfo.h" // Getting at the source data #include "plParticleSystem/plParticleEmitter.h" #include "plParticleSystem/plParticle.h" static hsScalar sInvDelSecs; //// Local Static Stuff /////////////////////////////////////////////////////// /// Macros for getting/setting data in a D3D vertex buffer #define STUFF_POINT( ptr, point ) { float *fPtr = (float *)ptr; \ fPtr[ 0 ] = point.fX; fPtr[ 1 ] = point.fY; fPtr[ 2 ] = point.fZ; \ ptr += sizeof( float ) * 3; } #define STUFF_UINT32( ptr, uint ) { UInt32 *dPtr = (UInt32 *)ptr; \ dPtr[ 0 ] = uint; ptr += sizeof( UInt32 ); } #define EXTRACT_POINT( ptr, pt ) { float *fPtr = (float *)ptr; \ pt.fX = fPtr[ 0 ]; pt.fY = fPtr[ 1 ]; pt.fZ = fPtr[ 2 ]; \ ptr += sizeof( float ) * 3; } #define EXTRACT_FLOAT( ptr, f ) { float *fPtr = (float *)ptr; \ f = fPtr[ 0 ]; \ ptr += sizeof( float ); } #define EXTRACT_UINT32( ptr, uint ) { UInt32 *dPtr = (UInt32 *)ptr; \ uint = dPtr[ 0 ]; ptr += sizeof( UInt32 ); } static hsScalar sCurrMinWidth = 0; /////////////////////////////////////////////////////////////////////////////// //// Particles //////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //// Particle Processing Inlines ////////////////////////////////////////////// // Thanks to the beauty of C++, the internal loop of // IFillParticlePolys would be horrendous without these inlines (with them, // it's just slightly annoying). The goal is to make the code easier to // maintain without loosing speed (hence the inlines, which will *hopefully* // remove the function call overhead....) void inline IInlSetParticlePathFollow( const plParticleCore &particle, const hsMatrix44& viewToWorld, hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) { /// Follow path specified by interpreting orientation as a velocity vector. hsVector3 viewDir(&particle.fPos, &viewToWorld.GetTranslate()); hsFastMath::NormalizeAppr(viewDir); zVec = viewDir; const hsVector3& orientation = (const hsVector3)(particle.fOrientation); yVec = orientation - orientation.InnerProduct(viewDir) * viewDir; hsFastMath::NormalizeAppr(yVec); xVec = yVec % viewDir; hsFastMath::NormalizeAppr(xVec); xVec *= particle.fHSize; yVec *= particle.fVSize; } void inline IInlSetParticlePathStretch( const plParticleCore &particle, const hsMatrix44& viewToWorld, hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) { /// Follow path specified by interpreting orientation as a velocity vector. // Well, that's one way to do it, but it has a bit of a flaw. If the length of the // particle doesn't match the distance it's moved each frame, you get a nasty strobing // effect, particularly as particles move faster. One of those things they don't teach // you in the math class you slept through. // So what we want is for the tail of the particle this frame to be where the head of // the particle was last frame. // First thing changed was the orientation passed in is now the change in position for // this frame (was the last frame's velocity). // zVec will still be the normalized vector from the eye to the particle (in world space). // Does zVec ever get used? Hmm, only gets used for the normal facing the camera. // This thing gets a lot faster and cleaner when we just use the view axes to compute. // So zVec is just -viewDir. // yVec = orientation - orientation.InnerProduct(zVec) * zVec // xVec = yVec % zVec // To be really correct, you can normalize xVec, because the particle doesn't get fatter // as it goes faster, just longer. Costs a little more, but... // And it also raises the question of what to do with the user supplied sizes. Again, // for correctness, we want to add the size to the length of the displacement. // We could afford that (maybe) by doing a fast normalize on yVec, then multiplying // by orientation.InnerProduct(yVec) + fVSize. // So the new stuff we need here are: // The particle (to get the size out of). // The viewToWorld transform (we can pull the eyePt and viewDir out of that. // Note that we could probably slim away a normalize or two, but the actual number // of normalizes we're doing hasn't gone up, I've just moved them up from IInlSetParticlePoints(). hsVector3 viewDir(&particle.fPos, &viewToWorld.GetTranslate()); hsScalar invD = hsFastMath::InvSqrtAppr(viewDir.MagnitudeSquared()); viewDir *= invD; zVec = viewDir; const hsVector3& orientation = (const hsVector3)(particle.fOrientation); // We don't want the projection of orientation orthogonal to viewDir here, // it's okay (and looks better) if yVec isn't in the image plane, we just // want to make sure that xVec is in the image plane. Might want to make // the same change to IInlSetParticlePathFollow(), but I haven't checked // that yet. Artifact to look for is particles starting to look goofy // as their orientation gets close in direction to viewDir. mf yVec = orientation; hsFastMath::NormalizeAppr(yVec); xVec = yVec % viewDir; // cross product of two orthonormal vectors, no need to normalize. hsScalar xLen = particle.fHSize; if( xLen * invD < sCurrMinWidth ) xLen = sCurrMinWidth / invD; xVec *= xLen; hsScalar len = yVec.InnerProduct(orientation); // Might want to give it a little boost to overlap itself (and compensate for the massive // transparent border the artists love). But they can do that themselves with the VSize. // len *= 1.5f; len += particle.fVSize; yVec *= len * -1.f; } void inline IInlSetParticlePathFlow( const plParticleCore &particle, const hsMatrix44& viewToWorld, hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) { // Okay, all the notes for SetParticlePathStretch apply here too. The only // difference is that we're going to keep the area of the particle constant, // so the longer it stretches, the narrower it gets orthogonal to the velocity. hsVector3 viewDir(&particle.fPos, &viewToWorld.GetTranslate()); hsScalar invD = hsFastMath::InvSqrtAppr(viewDir.MagnitudeSquared()); viewDir *= invD; zVec = viewDir; const hsVector3& orientation = (const hsVector3)(particle.fOrientation); // We don't want the projection of orientation orthogonal to viewDir here, // it's okay (and looks better) if yVec isn't in the image plane, we just // want to make sure that xVec is in the image plane. Might want to make // the same change to IInlSetParticlePathFollow(), but I haven't checked // that yet. Artifact to look for is particles starting to look goofy // as their orientation gets close in direction to viewDir. mf yVec = orientation; hsFastMath::NormalizeAppr(yVec); xVec = yVec % viewDir; // cross product of two orthonormal vectors, no need to normalize. hsScalar len = yVec.InnerProduct(orientation); hsScalar xLen = particle.fHSize * hsFastMath::InvSqrtAppr(1.f + len * sInvDelSecs); if( xLen * invD < sCurrMinWidth ) xLen = sCurrMinWidth / invD; xVec *= xLen; // Might want to give it a little boost to overlap itself (and compensate for the massive // transparent border the artists love). But they can do that themselves with the VSize. len += particle.fVSize; yVec *= len * -2.f; } void inline IInlSetParticleExplicit( const hsMatrix44 &viewToWorld, const plParticleCore &particle, hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) { const hsVector3& orientation = (const hsVector3)(particle.fOrientation); #if 0 // See notes below - mf zVec.Set( 0, 0, -1 ); yVec.Set( &orientation ); zVec = viewToWorld * zVec; yVec = viewToWorld * yVec; xVec = yVec % zVec; #else // See notes below - mf // The above has a bit of a problem with wide field of view. All of the // particles are facing the same direction with respect to lighting, // even though some are to the left and some are to the right of the camera // (when facing the center of the system). We'll also start seeing them side on // as they get to the edge of the screen. Fortunately, it's actually faster // to calculate the vector from camera to particle and normalize it than // to transform the vector (0,0,-1) (though not as fast as pulling the // camera direction directly from the viewToWorld). hsVector3 del(&particle.fPos, &viewToWorld.GetTranslate()); hsFastMath::NormalizeAppr(del); zVec = del; yVec.Set(&(viewToWorld * orientation)); xVec = yVec % zVec; #endif // See notes below - mf xVec = hsFastMath::NormalizeAppr( xVec ) * particle.fHSize; yVec = hsFastMath::NormalizeAppr( yVec ) * particle.fVSize; } void inline IInlSetParticlePoints( const hsVector3 &xVec, const hsVector3 &yVec, const plParticleCore &particle, hsPoint3 *partPts, UInt32 &partColor ) { /// Do the 4 verts for this particle partPts[ 0 ] = partPts[ 1 ] = partPts[ 2 ] = partPts[ 3 ] = particle.fPos; partPts[ 0 ] += xVec - yVec; partPts[ 1 ] += -xVec - yVec; partPts[ 2 ] += -xVec + yVec; partPts[ 3 ] += xVec + yVec; partColor = particle.fColor; } void inline IInlSetParticlePointsStretch( const hsVector3 &xVec, const hsVector3 &yVec, const plParticleCore &particle, hsPoint3 *partPts, UInt32 &partColor ) { /// Do the 4 verts for this particle partPts[ 0 ] = partPts[ 1 ] = partPts[ 2 ] = partPts[ 3 ] = particle.fPos; partPts[ 0 ] += xVec + yVec; partPts[ 1 ] += -xVec + yVec; partPts[ 2 ] += -xVec; partPts[ 3 ] += xVec; partColor = particle.fColor; } void inline IInlStuffParticle1UV( UInt8 *&destPtr, const hsPoint3 *partPts, const hsVector3 &partNorm, const UInt32 &partColor, const plParticleCore &particle ) { UInt8 j; for( j = 0; j < 4; j++ ) { STUFF_POINT( destPtr, partPts[ j ] ); STUFF_POINT( destPtr, partNorm ); STUFF_UINT32( destPtr, partColor ); STUFF_UINT32( destPtr, 0 ); STUFF_POINT( destPtr, particle.fUVCoords[ j ] ); } } void inline IInlStuffParticleNoUVs( UInt8 *&destPtr, const hsPoint3 *partPts, const hsVector3 &partNorm, const UInt32 &partColor ) { UInt8 j; for( j = 0; j < 4; j++ ) { STUFF_POINT( destPtr, partPts[ j ] ); STUFF_POINT( destPtr, partNorm ); STUFF_UINT32( destPtr, partColor ); STUFF_UINT32( destPtr, 0 ); } } void inline IInlSetNormalViewFace( hsVector3 &partNorm, const hsVector3 &zVec ) { partNorm = -zVec; } void inline IInlSetNormalStrongestLight( hsVector3 &partNorm, const plParticleCore &particle, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight, const hsVector3 &zVec ) { if( omniLight != nil ) { partNorm.Set( &particle.fPos, &omniLight->GetWorldPosition() ); partNorm = -partNorm; } else if( directionLight != nil ) { partNorm = -directionLight->GetWorldDirection(); } else partNorm = -zVec; partNorm = hsFastMath::NormalizeAppr( partNorm ); } void inline IInlSetNormalExplicit( hsVector3 &partNorm, const plParticleCore &particle ) { partNorm = particle.fNormal; } //// IIPL Functions (Internal-Inline-Particle-Loop) /////////////////////////// // Function names go as follows: // IIPL_u_o_n() // where u is 1UV or 0UV (for 1 UV channel or no UV channels), // o is OVel (for orientation-follows-velocity) or OExp (for orientation-is-explicit) // and n is NViewFace (for normals facing view), NLite (for facing strongest light) // or NExp (for explicit) #define IIPL_PROLOG \ UInt32 i, partColor; \ hsVector3 xVec, yVec, zVec, partNorm; \ hsPoint3 partPts[ 4 ]; \ for( i = 0; i < numParticles; i++ ) void inline IIPL_1UV_OVel_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OVel_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OVel_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OStr_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OStr_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OStr_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OFlo_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OFlo_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OFlo_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OExp_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OExp_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_1UV_OExp_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); } } void inline IIPL_0UV_OVel_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OVel_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OVel_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OStr_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OStr_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OStr_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OFlo_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OFlo_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OFlo_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OExp_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalViewFace( partNorm, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OExp_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) { IIPL_PROLOG { IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } void inline IIPL_0UV_OExp_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) { IIPL_PROLOG { IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); IInlSetNormalExplicit( partNorm, particles[ i ] ); IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); } } //// IFillParticlePolys /////////////////////////////////////////////////////// // Takes a list of particles and makes the polys for them. #include "hsTimer.h" #include "plProfile.h" plProfile_CreateTimer("Fill Polys", "Particles", ParticleFillPoly); void plParticleFiller::FillParticles(plPipeline* pipe, plDrawableSpans* drawable, plParticleSpan* span) { if (!span->fSource || span->fNumParticles <= 0) return; plProfile_BeginTiming(ParticleFillPoly); sInvDelSecs = hsTimer::GetDelSysSeconds(); if( sInvDelSecs > 0 ) sInvDelSecs = 1.f / sInvDelSecs; const plParticleCore* particles = span->fSource->GetParticleArray(); const UInt32 numParticles = span->fNumParticles; plGBufferGroup* group = drawable->GetBufferGroup(span->fGroupIdx); UInt8* destPtr = group->GetVertBufferData(span->fVBufferIdx); destPtr += span->fVStartIdx * group->GetVertexSize(); /// Get the z vector (pointing away from the camera) in worldspace hsMatrix44 viewToWorld = pipe->GetCameraToWorld(); plOmniLightInfo* omniLight = nil; plDirectionalLightInfo* directionLight = nil; /// Get strongest light, if there is one, for normal generation if( span->GetNumLights( false ) > 0 ) { omniLight = plOmniLightInfo::ConvertNoRef( span->GetLight( 0, false ) ); directionLight = plDirectionalLightInfo::ConvertNoRef( span->GetLight( 0, false ) ); } /// Fill with 1 UV channel if( group->GetNumUVs() == 1 ) { /// Switch on orientation if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityBased ) { /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_1UV_OVel_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_1UV_OVel_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_1UV_OVel_NExp( numParticles, particles, viewToWorld, destPtr ); } else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityStretch ) { sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_1UV_OStr_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_1UV_OStr_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_1UV_OStr_NExp( numParticles, particles, viewToWorld, destPtr ); } else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityFlow ) { sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_1UV_OFlo_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_1UV_OFlo_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_1UV_OFlo_NExp( numParticles, particles, viewToWorld, destPtr ); } else // Orientation explicit { /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_1UV_OExp_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_1UV_OExp_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_1UV_OExp_NExp( numParticles, particles, viewToWorld, destPtr ); } } else /// Fill with no UV channels { /// Switch on orientation if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityBased ) { /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_0UV_OVel_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_0UV_OVel_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_0UV_OVel_NExp( numParticles, particles, viewToWorld, destPtr ); } else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityStretch ) { sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_0UV_OStr_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_0UV_OStr_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_0UV_OStr_NExp( numParticles, particles, viewToWorld, destPtr ); } else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityFlow ) { sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_0UV_OFlo_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_0UV_OFlo_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_0UV_OFlo_NExp( numParticles, particles, viewToWorld, destPtr ); } else // Orientation explicit { /// Switch on normal generation if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) IIPL_0UV_OExp_NViewFace( numParticles, particles, viewToWorld, destPtr ); else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) IIPL_0UV_OExp_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); else IIPL_0UV_OExp_NExp( numParticles, particles, viewToWorld, destPtr ); } } /// All done! plProfile_EndTiming(ParticleFillPoly); } void plParticleFiller::FillParticlePolys(plPipeline* pipe, plDrawInterface* di) { if( !di ) return; // should probably be an asserted error. plDrawableSpans* drawable = plDrawableSpans::ConvertNoRef(di->GetDrawable(0)); if( !drawable ) return; // Currently, the di always points to exactly 1 drawable with 1 span index. If // that changes, this would just become a loop. UInt32 diIndex = di->GetDrawableMeshIndex(0); hsAssert(diIndex >= 0, "Bogus input to fill particles"); const plDISpanIndex& diSpans = drawable->GetDISpans(diIndex); int i; for( i = 0; i < diSpans.GetCount(); i++ ) { UInt32 spanIdx = diSpans[i]; hsAssert(drawable->GetSpan(spanIdx), "Bogus input to fill particles"); hsAssert(drawable->GetSpan(spanIdx)->fTypeMask & plSpan::kParticleSpan, "Bogus input to fill particles"); // Safe cast, since we just checked the type mask. plParticleSpan* span = (plParticleSpan*)drawable->GetSpan(spanIdx); if( !span->fSource ) return; // Nothing to do, it's idle. FillParticles(pipe, drawable, span); } }