summaryrefslogtreecommitdiff
path: root/particles/builtin_particle_render_ops.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'particles/builtin_particle_render_ops.cpp')
-rw-r--r--particles/builtin_particle_render_ops.cpp2416
1 files changed, 2416 insertions, 0 deletions
diff --git a/particles/builtin_particle_render_ops.cpp b/particles/builtin_particle_render_ops.cpp
new file mode 100644
index 0000000..966b155
--- /dev/null
+++ b/particles/builtin_particle_render_ops.cpp
@@ -0,0 +1,2416 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: particle system code
+//
+//===========================================================================//
+
+#include "tier0/platform.h"
+#include "particles/particles.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier2/fileutils.h"
+#include "tier2/renderutils.h"
+#include "tier2/beamsegdraw.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/strtools.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "psheet.h"
+#include "tier0/vprof.h"
+
+#ifdef USE_BLOBULATOR
+// TODO: These should be in public by the time the SDK ships
+ #include "../common/blobulator/Implicit/ImpDefines.h"
+ #include "../common/blobulator/Implicit/ImpRenderer.h"
+ #include "../common/blobulator/Implicit/ImpTiler.h"
+ #include "../common/blobulator/Implicit/UserFunctions.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Vertex instancing (1 vert submitted per particle, duplicated to 4 (a quad) on the GPU) is supported only on 360
+const bool bUseInstancing = IsX360();
+
+
+//-----------------------------------------------------------------------------
+// Utility method to compute the max # of particles per batch
+//-----------------------------------------------------------------------------
+static inline int GetMaxParticlesPerBatch( IMatRenderContext *pRenderContext, IMaterial *pMaterial, bool bWithInstancing )
+{
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+
+ if ( bWithInstancing )
+ return nMaxVertices;
+ else
+ return min( (nMaxVertices / 4), (nMaxIndices / 6) );
+}
+
+void SetupParticleVisibility( CParticleCollection *pParticles, CParticleVisibilityData *pVisibilityData, const CParticleVisibilityInputs *pVisibilityInputs, int *nQueryHandle )
+{
+ float flScale = pVisibilityInputs->m_flProxyRadius;
+ Vector vecOrigin;
+ /*
+ if ( pVisibilityInputs->m_bUseBBox )
+ {
+ Vector vecMinBounds;
+ Vector vecMaxBounds;
+ Vector mins;
+ Vector maxs;
+
+ pParticles->GetBounds( &vecMinBounds, &vecMaxBounds );
+
+ vecOrigin = ( ( vecMinBounds + vecMaxBounds ) / 2 );
+
+ Vector vecBounds = ( vecMaxBounds - vecMinBounds );
+
+ flScale = ( max(vecBounds.x, max (vecBounds.y, vecBounds.z) ) * pVisibilityInputs->m_flBBoxScale );
+ }
+ if ( pVisibilityInputs->m_nCPin >= 0 )
+ {
+ vecOrigin = pParticles->GetControlPointAtCurrentTime( pVisibilityInputs->m_nCPin );
+ }
+ */
+ vecOrigin = pParticles->GetControlPointAtCurrentTime( pVisibilityInputs->m_nCPin );
+ float flVisibility = g_pParticleSystemMgr->Query()->GetPixelVisibility( nQueryHandle, vecOrigin, flScale );
+
+ pVisibilityData->m_flAlphaVisibility = RemapValClamped( flVisibility, pVisibilityInputs->m_flInputMin,
+ pVisibilityInputs->m_flInputMax, pVisibilityInputs->m_flAlphaScaleMin, pVisibilityInputs->m_flAlphaScaleMax );
+ pVisibilityData->m_flRadiusVisibility = RemapValClamped( flVisibility, pVisibilityInputs->m_flInputMin,
+ pVisibilityInputs->m_flInputMax, pVisibilityInputs->m_flRadiusScaleMin, pVisibilityInputs->m_flRadiusScaleMax );
+
+ pVisibilityData->m_flCameraBias = pVisibilityInputs->m_flCameraBias;
+}
+
+static SheetSequenceSample_t s_DefaultSheetSequence =
+{
+ {
+ { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }, // SequenceSampleTextureCoords_t image 0
+ { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f } // SequenceSampleTextureCoords_t image 1
+ },
+ 1.0f // m_fBlendFactor
+};
+
+
+class C_OP_RenderPoints : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderPoints );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+
+ struct C_OP_RenderPointsContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderPointsContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderPointsContext_t *pCtx = reinterpret_cast<C_OP_RenderPointsContext_t *>( pContext );
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderPoints, "render_points", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderPoints )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderPoints )
+
+void C_OP_RenderPoints::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ C_OP_RenderPointsContext_t *pCtx = reinterpret_cast<C_OP_RenderPointsContext_t *>( pContext );
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ int nParticles;
+ const ParticleRenderData_t *pRenderList =
+ pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ pRenderContext->Bind( pMaterial );
+
+ CMeshBuilder meshBuilder;
+
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ while ( nParticles )
+ {
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+
+ int nParticlesInBatch = min( nMaxVertices, nParticles );
+ nParticles -= nParticlesInBatch;
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch );
+
+ meshBuilder.Begin( pMesh, MATERIAL_POINTS, nParticlesInBatch );
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pRenderList)->m_nIndex;
+ int nIndex = ( hParticle / 4 ) * xyz_stride;
+ int nOffset = hParticle & 0x3;
+ meshBuilder.Position3f( SubFloat( xyz[nIndex], nOffset ), SubFloat( xyz[nIndex+1], nOffset ), SubFloat( xyz[nIndex+2], nOffset ) );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Sprite Rendering
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Utility struct to help with sprite rendering
+//-----------------------------------------------------------------------------
+struct SpriteRenderInfo_t
+{
+ size_t m_nXYZStride;
+ const fltx4 *m_pXYZ;
+ size_t m_nRotStride;
+ const fltx4 *m_pRot;
+ size_t m_nYawStride;
+ const fltx4 *m_pYaw;
+ size_t m_nRGBStride;
+ const fltx4 *m_pRGB;
+ size_t m_nCreationTimeStride;
+ const fltx4 *m_pCreationTimeStamp;
+ size_t m_nSequenceStride;
+ const fltx4 *m_pSequenceNumber;
+ size_t m_nSequence1Stride;
+ const fltx4 *m_pSequence1Number;
+ float m_flAgeScale;
+ float m_flAgeScale2;
+
+ CSheet *m_pSheet;
+ int m_nVertexOffset;
+ CParticleCollection *m_pParticles;
+
+ void Init( CParticleCollection *pParticles, int nVertexOffset, float flAgeScale, float flAgeScale2, CSheet *pSheet )
+ {
+ m_pParticles = pParticles;
+ m_pXYZ = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &m_nXYZStride );
+ m_pRot = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ROTATION, &m_nRotStride );
+ m_pYaw = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_YAW, &m_nYawStride );
+ m_pRGB = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TINT_RGB, &m_nRGBStride );
+ m_pCreationTimeStamp = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &m_nCreationTimeStride );
+ m_pSequenceNumber = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, &m_nSequenceStride );
+ m_pSequence1Number = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1, &m_nSequence1Stride );
+ m_flAgeScale = flAgeScale;
+ m_flAgeScale2 = flAgeScale2;
+ m_pSheet = pSheet;
+ m_nVertexOffset = nVertexOffset;
+ }
+};
+
+class C_OP_RenderSprites : public C_OP_RenderPoints
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderSprites );
+
+ struct C_OP_RenderSpritesContext_t
+ {
+ unsigned int m_nOrientationVarToken;
+ unsigned int m_nOrientationMatrixVarToken;
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderSpritesContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+ pCtx->m_nOrientationVarToken = 0;
+ pCtx->m_nOrientationMatrixVarToken = 0;
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+
+ virtual uint64 GetReadControlPointMask() const
+ {
+ if ( m_nOrientationControlPoint >= 0 )
+ return 1ULL << m_nOrientationControlPoint;
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_ROTATION_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK |
+ PARTICLE_ATTRIBUTE_TINT_RGB_MASK | PARTICLE_ATTRIBUTE_ALPHA_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK |
+ PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1_MASK |
+ PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER_MASK | PARTICLE_ATTRIBUTE_LIFE_DURATION_MASK;
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement );
+ virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const;
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+ virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+ void RenderSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const;
+ void RenderTwoSequenceSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const;
+
+ void RenderNonSpriteCardCameraFacing( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const;
+
+ void RenderNonSpriteCardZRotating( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList ) const;
+ void RenderNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const;
+ void RenderUnsortedNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ void RenderNonSpriteCardOriented( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList, bool bUseYaw ) const;
+ void RenderNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial, bool bUseYaw ) const;
+ void RenderUnsortedNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ // cycles per second
+ float m_flAnimationRate;
+ float m_flAnimationRate2;
+ bool m_bFitCycleToLifetime;
+ bool m_bAnimateInFPS;
+ int m_nOrientationType;
+
+
+ int m_nOrientationControlPoint;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderSprites, "render_animated_sprites", OPERATOR_GENERIC );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderSprites )
+ DMXELEMENT_UNPACK_FIELD( "animation rate", ".1", float, m_flAnimationRate )
+ DMXELEMENT_UNPACK_FIELD( "animation_fit_lifetime", "0", bool, m_bFitCycleToLifetime )
+ DMXELEMENT_UNPACK_FIELD( "orientation_type", "0", int, m_nOrientationType )
+ DMXELEMENT_UNPACK_FIELD( "orientation control point", "-1", int, m_nOrientationControlPoint )
+ DMXELEMENT_UNPACK_FIELD( "second sequence animation rate", "0", float, m_flAnimationRate2 )
+ DMXELEMENT_UNPACK_FIELD( "use animation rate as FPS", "0", bool, m_bAnimateInFPS )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderSprites )
+
+void C_OP_RenderSprites::InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+{
+}
+
+const SheetSequenceSample_t *GetSampleForSequence( CSheet *pSheet, float flCreationTime, float flCurTime, float flAgeScale, int nSequence )
+{
+ if ( pSheet == NULL )
+ return NULL;
+
+ if ( pSheet->m_nNumFrames[nSequence] == 1 )
+ return (const SheetSequenceSample_t *) &pSheet->m_pSamples[nSequence][0];
+
+ float flAge = flCurTime - flCreationTime;
+
+ flAge *= flAgeScale;
+ unsigned int nFrame = flAge;
+ if ( pSheet->m_bClamp[nSequence] )
+ {
+ nFrame = min( nFrame, (unsigned int)SEQUENCE_SAMPLE_COUNT-1 );
+ }
+ else
+ {
+ nFrame &= SEQUENCE_SAMPLE_COUNT-1;
+ }
+
+ return (const SheetSequenceSample_t *) &pSheet->m_pSamples[nSequence][nFrame];
+}
+
+int C_OP_RenderSprites::GetParticlesToRender( CParticleCollection *pParticles,
+ void *pContext, int nFirstParticle,
+ int nRemainingVertices, int nRemainingIndices,
+ int *pVertsUsed, int *pIndicesUsed ) const
+{
+ int nMaxParticles = ( (nRemainingVertices / 4) > (nRemainingIndices / 6) ) ? nRemainingIndices / 6 : nRemainingVertices / 4;
+ int nParticleCount = pParticles->m_nActiveParticles - nFirstParticle;
+ if ( nParticleCount > nMaxParticles )
+ {
+ nParticleCount = nMaxParticles;
+ }
+ *pVertsUsed = nParticleCount * 4;
+ *pIndicesUsed = nParticleCount * 6;
+ return nParticleCount;
+}
+
+void C_OP_RenderSprites::RenderNonSpriteCardCameraFacing( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const
+{
+
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ // generate the sort list before this code starts messing with the matrices
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = (&pCtx->m_VisibilityData )->m_flCameraBias;
+
+ Vector vecCamera;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ VMatrix tempView;
+
+ // Store matrices off so we can restore them in RenderEnd().
+ pRenderContext->GetMatrix(MATERIAL_VIEW, &tempView);
+
+ // Force the user clip planes to use the old view matrix
+ pRenderContext->EnableUserClipTransformOverride( true );
+ pRenderContext->UserClipTransform( tempView );
+
+ // The particle renderers want to do things in camera space
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ size_t rot_stride;
+ const fltx4 *pRot = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ROTATION, &rot_stride );
+
+ size_t rgb_stride;
+ const fltx4 *pRGB = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TINT_RGB, &rgb_stride );
+
+ size_t ct_stride;
+ const fltx4 *pCreationTimeStamp = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &ct_stride );
+
+ size_t seq_stride;
+ const fltx4 *pSequenceNumber = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, &seq_stride );
+
+ size_t ld_stride;
+ const fltx4 *pLifeDuration = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_LIFE_DURATION, &ld_stride );
+
+
+ float flAgeScale;
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+
+ CSheet *pSheet = pParticles->m_Sheet();
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nParticlesInBatch );
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ continue;
+
+ int nColorIndex = nGroup * rgb_stride;
+ float r = SubFloat( pRGB[nColorIndex], nOffset );
+ float g = SubFloat( pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ float rad = pSortList->m_flRadius;
+
+ int nXYZIndex = nGroup * xyz_stride;
+ Vector vecWorldPos( SubFloat( xyz[ nXYZIndex ], nOffset ), SubFloat( xyz[ nXYZIndex+1 ], nOffset ), SubFloat( xyz[ nXYZIndex+2 ], nOffset ) );
+
+ // Move the Particle if their is a camerabias
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = vecCamera - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ Vector vecViewPos;
+ Vector3DMultiplyPosition( tempView, vecWorldPos, vecViewPos );
+
+ if ( !IsFinite( vecViewPos.x ) )
+ continue;
+
+ float rot = SubFloat( pRot[ nGroup * rot_stride ], nOffset );
+ float ca = (float)cos(rot);
+ float sa = (float)sin(rot);
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( pSheet )
+ {
+ if ( m_bFitCycleToLifetime )
+ {
+ float flLifetime = SubFloat( pLifeDuration[ nGroup * ld_stride ], nOffset );
+ flAgeScale = ( flLifetime > 0.0f ) ? ( 1.0f / flLifetime ) * SEQUENCE_SAMPLE_COUNT : 0.0f;
+ }
+ else
+ {
+ flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ if ( m_bAnimateInFPS )
+ {
+ int nSequence = SubFloat( pSequenceNumber[ nGroup * seq_stride ], nOffset );
+ flAgeScale = flAgeScale / pSheet->m_flFrameSpan[nSequence];
+ }
+ }
+ pSample = GetSampleForSequence( pSheet,
+ SubFloat( pCreationTimeStamp[ nGroup * ct_stride ], nOffset ),
+ pParticles->m_flCurTime,
+ flAgeScale,
+ SubFloat( pSequenceNumber[ nGroup * seq_stride ], nOffset ) );
+ }
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+
+ meshBuilder.Position3f( vecViewPos.x + (-ca + sa) * rad, vecViewPos.y + (-sa - ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecViewPos.x + (-ca - sa) * rad, vecViewPos.y + (-sa + ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecViewPos.x + (ca - sa) * rad, vecViewPos.y + (sa + ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecViewPos.x + (ca + sa) * rad, vecViewPos.y + (sa - ca) * rad, vecViewPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+
+ pRenderContext->EnableUserClipTransformOverride( false );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::RenderNonSpriteCardZRotating( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ return;
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ float rad = pSortList->m_flRadius;
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+
+ float ca = (float)cos(-rot);
+ float sa = (float)sin(-rot);
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+
+ // Move the Particle if their is a camerabias
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = vecCameraPos - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ Vector vecViewToPos;
+ VectorSubtract( vecWorldPos, vecCameraPos, vecViewToPos );
+ float flLength = vecViewToPos.Length();
+ if ( flLength < rad / 2 )
+ return;
+
+ Vector vecUp( 0, 0, 1 );
+ Vector vecRight;
+ CrossProduct( vecUp, vecCameraPos, vecRight );
+ VectorNormalize( vecRight );
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ vecRight *= rad;
+
+ float x, y;
+ Vector vecCorner;
+
+ x = - ca - sa; y = - ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = - ca + sa; y = + ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = + ca + sa; y = + ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = + ca - sa; y = - ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z + y * rad );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::RenderNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 * 3, nParticlesInBatch * 6 * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nParticlesInBatch * 4, nParticlesInBatch * 6 );
+ info.m_nVertexOffset = 0;
+
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderNonSpriteCardZRotating( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList );
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void C_OP_RenderSprites::RenderUnsortedNonSpriteCardZRotating( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, nVertexOffset, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ int hParticle = nFirstParticle;
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderNonSpriteCardZRotating( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::RenderNonSpriteCardOriented(
+ CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, const Vector& vecCameraPos, ParticleRenderData_t const *pSortList, bool bUseYaw ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ return;
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ float rad = pSortList->m_flRadius;
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+
+ float ca = (float)cos(-rot);
+ float sa = (float)sin(-rot);
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+
+ // Move the Particle if their is a camerabias
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = vecCameraPos - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ Vector vecViewToPos;
+ VectorSubtract( vecWorldPos, vecCameraPos, vecViewToPos );
+ float flLength = vecViewToPos.Length();
+ if ( flLength < rad / 2 )
+ return;
+
+ Vector vecNormal, vecRight, vecUp;
+ if ( m_nOrientationControlPoint < 0 )
+ {
+ vecNormal.Init( 0, 0, 1 );
+ vecRight.Init( 1, 0, 0 );
+ vecUp.Init( 0, -1, 0 );
+ }
+ else
+ {
+ info.m_pParticles->GetControlPointOrientationAtCurrentTime(
+ m_nOrientationControlPoint, &vecRight, &vecUp, &vecNormal );
+ }
+
+ if ( bUseYaw )
+ {
+ float yaw = SubFloat( info.m_pYaw[nGroup * info.m_nYawStride], nOffset );
+
+ if ( yaw != 0.0f )
+ {
+ Vector particleRight = Vector( 1, 0, 0 );
+ yaw = RAD2DEG( yaw ); // I hate you source (VectorYawRotate will undo this)
+ matrix3x4_t matRot;
+ MatrixBuildRotationAboutAxis( vecUp, yaw, matRot );
+ VectorRotate( vecRight, matRot, particleRight );
+ vecRight = particleRight;
+ }
+ }
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ vecRight *= rad;
+ vecUp *= rad;
+
+ float x, y;
+ Vector vecCorner;
+
+ x = + ca - sa; y = - ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = + ca + sa; y = + ca - sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = - ca + sa; y = + ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ x = - ca - sa; y = - ca + sa;
+ VectorMA( vecWorldPos, x, vecRight, vecCorner );
+ VectorMA( vecCorner, y, vecUp, vecCorner );
+ meshBuilder.Position3f( vecCorner.x, vecCorner.y, vecCorner.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+}
+
+void C_OP_RenderSprites::RenderNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, IMaterial *pMaterial, bool bUseYaw ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 * 3, nParticlesInBatch * 6 * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nParticlesInBatch * 4, nParticlesInBatch * 6 );
+ info.m_nVertexOffset = 0;
+
+ for( int i = 0; i < nParticlesInBatch; i++)
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderNonSpriteCardOriented( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList, bUseYaw );
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void C_OP_RenderSprites::RenderUnsortedNonSpriteCardOriented( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, nVertexOffset, flAgeScale, 0, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ int hParticle = nFirstParticle;
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderNonSpriteCardOriented( meshBuilder, pCtx, info, hParticle, vecCameraPos, pSortList, false );
+ }
+}
+
+void C_OP_RenderSprites::RenderSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= -1e-6f) && (g >= -1e-6f) && (b >= -1e-6f) );
+ if ( !HushAsserts() )
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+ unsigned char ac = pSortList->m_nAlpha;
+
+ float rad = pSortList->m_flRadius;
+ if ( !IsFinite( rad ) )
+ {
+ return;
+ }
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+ float yaw = SubFloat( info.m_pYaw[ nGroup * info.m_nYawStride ], nOffset );
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos;
+ vecWorldPos.x = SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset );
+ vecWorldPos.y = SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset );
+ vecWorldPos.z = SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset );
+
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = *pCamera - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ float flAgeScale = info.m_flAgeScale;
+// if ( m_bFitCycleToLifetime )
+// {
+// float flLifetime = SubFloat( pLifeDuration[ nGroup * ld_stride ], nOffset );
+// flAgeScale = ( flLifetime > 0.0f ) ? ( 1.0f / flLifetime ) * SEQUENCE_SAMPLE_COUNT : 0.0f;
+// }
+ if ( m_bAnimateInFPS )
+ {
+ int nSequence = SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset );
+ flAgeScale = flAgeScale / info.m_pParticles->m_Sheet()->m_flFrameSpan[nSequence];
+ }
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ const SequenceSampleTextureCoords_t *pSecondTexture0 = &(pSample->m_TextureCoordData[1]);
+
+ // Submit 1 (instanced) or 4 (non-instanced) verts (if we're instancing, we don't produce indices either)
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ // FIXME: change the vertex decl (remove texcoord3/cornerid) if instancing - need to adjust elements beyond texcoord3 down, though
+ if ( !bUseInstancing )
+ meshBuilder.TexCoord2f( 3, 0, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ if ( !bUseInstancing )
+ {
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 0, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+ }
+}
+
+void C_OP_RenderSprites::RenderTwoSequenceSpriteCard( CMeshBuilder &meshBuilder, C_OP_RenderSpritesContext_t *pCtx, SpriteRenderInfo_t& info, int hParticle, ParticleRenderData_t const *pSortList, Vector *pCamera ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= 0.0f) && (g >= 0.0f) && (b >= 0.0f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+ unsigned char ac = pSortList->m_nAlpha;
+
+ bool bCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias != 0.0f;
+ float flCameraBias = ( &pCtx->m_VisibilityData )->m_flCameraBias;
+
+ float rad = pSortList->m_flRadius;
+ float rot = SubFloat( info.m_pRot[ nGroup * info.m_nRotStride ], nOffset );
+ float yaw = SubFloat( info.m_pYaw[ nGroup * info.m_nYawStride ], nOffset );
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos;
+ vecWorldPos.x = SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset );
+ vecWorldPos.y = SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset );
+ vecWorldPos.z = SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset );
+
+ if ( bCameraBias )
+ {
+ Vector vEyeDir = *pCamera - vecWorldPos;
+ VectorNormalizeFast( vEyeDir );
+ vEyeDir *= flCameraBias;
+ vecWorldPos += vEyeDir;
+ }
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ const SheetSequenceSample_t *pSample1 = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ pSample1 = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale2,
+ SubFloat( info.m_pSequence1Number[ nGroup * info.m_nSequence1Stride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+ const SequenceSampleTextureCoords_t *pSecondTexture0 = &(pSample->m_TextureCoordData[1]);
+ const SequenceSampleTextureCoords_t *pSample1Frame = &(pSample1->m_TextureCoordData[0]);
+
+ // Submit 1 (instanced) or 4 (non-instanced) verts (if we're instancing, we don't produce indices either)
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ // FIXME: change the vertex decl (remove texcoord3/cornerid) if instancing - need to adjust elements beyond texcoord3 down, though
+ if ( ! bUseInstancing )
+ meshBuilder.TexCoord2f( 3, 0, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ if ( !bUseInstancing )
+ {
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 0 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 1, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vecWorldPos.x, vecWorldPos.y, vecWorldPos.z );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord4f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 1, pSample0->m_fLeft_U1, pSample0->m_fTop_V1, pSample0->m_fRight_U1, pSample0->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 2, pSample->m_fBlendFactor, rot, rad, yaw );
+ meshBuilder.TexCoord2f( 3, 0, 1 );
+ meshBuilder.TexCoord4f( 4, pSecondTexture0->m_fLeft_U0, pSecondTexture0->m_fTop_V0, pSecondTexture0->m_fRight_U0, pSecondTexture0->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 5, pSample1Frame->m_fLeft_U0, pSample1Frame->m_fTop_V0, pSample1Frame->m_fRight_U0, pSample1Frame->m_fBottom_V0 );
+ meshBuilder.TexCoord4f( 6, pSample1Frame->m_fLeft_U1, pSample1Frame->m_fTop_V1, pSample1Frame->m_fRight_U1, pSample1Frame->m_fBottom_V1 );
+ meshBuilder.TexCoord4f( 7, pSample1->m_fBlendFactor, 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderSprites::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+
+ IMaterialVar* pVar = pMaterial->FindVarFast( "$orientation", &pCtx->m_nOrientationVarToken );
+ if ( pVar )
+ {
+ pVar->SetIntValue( m_nOrientationType );
+ }
+
+ pRenderContext->Bind( pMaterial );
+
+ if ( !pMaterial->IsSpriteCard() )
+ {
+ switch( m_nOrientationType )
+ {
+ case 0:
+ RenderNonSpriteCardCameraFacing( pParticles, pContext, pRenderContext, pMaterial );
+ break;
+
+ case 1:
+ RenderNonSpriteCardZRotating( pParticles, pContext, pRenderContext, pMaterial );
+ break;
+
+ case 2:
+ RenderNonSpriteCardOriented( pParticles, pContext, pRenderContext, pMaterial, false );
+ break;
+
+ case 3:
+ RenderNonSpriteCardOriented( pParticles, pContext, pRenderContext, pMaterial, true );
+ break;
+ }
+
+ return;
+ }
+
+ if ( m_nOrientationType == 2 )
+ {
+ pVar = pMaterial->FindVarFast( "$orientationMatrix", &pCtx->m_nOrientationMatrixVarToken );
+ if ( pVar )
+ {
+ VMatrix mat;
+ if ( m_nOrientationControlPoint < 0 )
+ {
+ MatrixSetIdentity( mat );
+ }
+ else
+ {
+ pParticles->GetControlPointTransformAtCurrentTime( m_nOrientationControlPoint, &mat );
+ }
+ pVar->SetMatrixValue( mat );
+ }
+ }
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ float flAgeScale2 = m_flAnimationRate2 * SEQUENCE_SAMPLE_COUNT;
+
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, flAgeScale2, pParticles->m_Sheet() );
+
+ MaterialPrimitiveType_t primType = bUseInstancing ? MATERIAL_INSTANCED_QUADS : MATERIAL_TRIANGLES;
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, bUseInstancing );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ Vector vecCamera;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
+
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ int vertexCount = bUseInstancing ? nParticlesInBatch : nParticlesInBatch * 4;
+ int indexCount = bUseInstancing ? 0 : nParticlesInBatch * 6;
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+
+ if ( bUseInstancing )
+ {
+ g_pParticleSystemMgr->TallyParticlesRendered( vertexCount * ( primType == MATERIAL_TRIANGLES ? 3 : 4 ) );
+ meshBuilder.Begin( pMesh, primType, vertexCount );
+ }
+ else
+ {
+ g_pParticleSystemMgr->TallyParticlesRendered( vertexCount * ( primType == MATERIAL_TRIANGLES ? 3 : 4 ), indexCount * ( primType == MATERIAL_TRIANGLES ? 3 : 4 ) );
+ meshBuilder.Begin( pMesh, primType, vertexCount, indexCount );
+ }
+ info.m_nVertexOffset = 0;
+ if ( meshBuilder.TextureCoordinateSize( 5 ) ) // second sequence?
+ {
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderTwoSequenceSpriteCard( meshBuilder, pCtx, info, hParticle, pSortList, &vecCamera );
+ }
+ }
+ else
+ {
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderSpriteCard( meshBuilder, pCtx, info, hParticle, pSortList, &vecCamera );
+ }
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+
+void C_OP_RenderSprites::RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ if ( !pParticles->m_pDef->GetMaterial()->IsSpriteCard() )
+ {
+ switch( m_nOrientationType )
+ {
+ case 0:
+ // FIXME: Implement! Requires removing MATERIAL_VIEW modification from sorted version
+ Warning( "C_OP_RenderSprites::RenderUnsorted: Attempting to use an unimplemented sprite renderer for system \"%s\"!\n",
+ pParticles->m_pDef->GetName() );
+// RenderUnsortedNonSpriteCardCameraFacing( pParticles, pContext, pRenderContext, meshBuilder, nVertexOffset, nFirstParticle, nParticleCount );
+ break;
+
+ case 1:
+ RenderUnsortedNonSpriteCardZRotating( pParticles, pContext, pRenderContext, meshBuilder, nVertexOffset, nFirstParticle, nParticleCount );
+ break;
+
+ case 2:
+ RenderUnsortedNonSpriteCardOriented( pParticles, pContext, pRenderContext, meshBuilder, nVertexOffset, nFirstParticle, nParticleCount );
+ break;
+ }
+ return;
+ }
+
+ C_OP_RenderSpritesContext_t *pCtx = reinterpret_cast<C_OP_RenderSpritesContext_t *>( pContext );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ float flAgeScale2 = m_flAnimationRate2 * SEQUENCE_SAMPLE_COUNT;
+
+ SpriteRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, flAgeScale2, pParticles->m_Sheet() );
+
+ int hParticle = nFirstParticle;
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ Vector vecCamera;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
+
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderSpriteCard( meshBuilder, pCtx, info, hParticle, pSortList, &vecCamera );
+ }
+}
+
+//
+//
+//
+//
+
+struct SpriteTrailRenderInfo_t : public SpriteRenderInfo_t
+{
+ size_t m_nPrevXYZStride;
+ const fltx4 *m_pPrevXYZ;
+ size_t length_stride;
+ const fltx4 *m_pLength;
+
+ const fltx4 *m_pCreationTime;
+ size_t m_nCreationTimeStride;
+
+
+ void Init( CParticleCollection *pParticles, int nVertexOffset, float flAgeScale, CSheet *pSheet )
+ {
+ SpriteRenderInfo_t::Init( pParticles, nVertexOffset, flAgeScale, 0, pSheet );
+ m_pParticles = pParticles;
+ m_pPrevXYZ = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, &m_nPrevXYZStride );
+ m_pLength = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TRAIL_LENGTH, &length_stride );
+ m_pCreationTime = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_CREATION_TIME, &m_nCreationTimeStride );
+ }
+};
+
+class C_OP_RenderSpritesTrail : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderSpritesTrail );
+
+ struct C_OP_RenderSpriteTrailContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderSpriteTrailContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderSpriteTrailContext_t *pCtx = reinterpret_cast<C_OP_RenderSpriteTrailContext_t *>( pContext );
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK |
+ PARTICLE_ATTRIBUTE_TINT_RGB_MASK | PARTICLE_ATTRIBUTE_ALPHA_MASK | PARTICLE_ATTRIBUTE_CREATION_TIME_MASK |
+ PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER_MASK | PARTICLE_ATTRIBUTE_TRAIL_LENGTH_MASK;
+ }
+
+ virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const ;
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+ virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ void RenderSpriteTrail( CMeshBuilder &meshBuilder, SpriteTrailRenderInfo_t& info, int hParticle, const Vector &vecCameraPos, float flOODt, ParticleRenderData_t const *pSortlist ) const;
+
+ float m_flAnimationRate;
+ float m_flLengthFadeInTime;
+ float m_flMaxLength;
+ float m_flMinLength;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderSpritesTrail, "render_sprite_trail", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderSpritesTrail )
+ DMXELEMENT_UNPACK_FIELD( "animation rate", ".1", float, m_flAnimationRate )
+ DMXELEMENT_UNPACK_FIELD( "length fade in time", "0", float, m_flLengthFadeInTime )
+ DMXELEMENT_UNPACK_FIELD( "max length", "2000", float, m_flMaxLength )
+ DMXELEMENT_UNPACK_FIELD( "min length", "0", float, m_flMinLength )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderSpritesTrail )
+
+int C_OP_RenderSpritesTrail::GetParticlesToRender( CParticleCollection *pParticles,
+ void *pContext, int nFirstParticle, int nRemainingVertices,
+ int nRemainingIndices,
+ int *pVertsUsed, int *pIndicesUsed ) const
+{
+ int nMaxParticles = ( (nRemainingVertices / 4) > (nRemainingIndices / 6) ) ? nRemainingIndices / 6 : nRemainingVertices / 4;
+ int nParticleCount = pParticles->m_nActiveParticles - nFirstParticle;
+ if ( nParticleCount > nMaxParticles )
+ {
+ nParticleCount = nMaxParticles;
+ }
+ *pVertsUsed = nParticleCount * 4;
+ *pIndicesUsed = nParticleCount * 6;
+ return nParticleCount;
+}
+
+void C_OP_RenderSpritesTrail::RenderSpriteTrail( CMeshBuilder &meshBuilder,
+ SpriteTrailRenderInfo_t& info, int hParticle,
+ const Vector &vecCameraPos, float flOODt, ParticleRenderData_t const *pSortList ) const
+{
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ // Setup our alpha
+ unsigned char ac = pSortList->m_nAlpha;
+ if ( ac == 0 )
+ return;
+
+ // Setup our colors
+ int nColorIndex = nGroup * info.m_nRGBStride;
+ float r = SubFloat( info.m_pRGB[nColorIndex], nOffset );
+ float g = SubFloat( info.m_pRGB[nColorIndex+1], nOffset );
+ float b = SubFloat( info.m_pRGB[nColorIndex+2], nOffset );
+
+ Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) );
+ Assert( (r >= -1e-6f) && (g >= -1e-6f) && (b >= -1e-6f) );
+ Assert( (r <= 1.0f) && (g <= 1.0f) && (b <= 1.0f) );
+
+ unsigned char rc = FastFToC( r );
+ unsigned char gc = FastFToC( g );
+ unsigned char bc = FastFToC( b );
+
+ // Setup the scale and rotation
+ float rad = pSortList->m_flRadius;
+
+ // Find the sample for this frame
+ const SheetSequenceSample_t *pSample = &s_DefaultSheetSequence;
+ if ( info.m_pSheet )
+ {
+ pSample = GetSampleForSequence( info.m_pSheet,
+ SubFloat( info.m_pCreationTimeStamp[ nGroup * info.m_nCreationTimeStride ], nOffset ),
+ info.m_pParticles->m_flCurTime,
+ info.m_flAgeScale,
+ SubFloat( info.m_pSequenceNumber[ nGroup * info.m_nSequenceStride ], nOffset ) );
+ }
+
+ const SequenceSampleTextureCoords_t *pSample0 = &(pSample->m_TextureCoordData[0]);
+
+ int nCreationTimeIndex = nGroup * info.m_nCreationTimeStride;
+ float flAge = info.m_pParticles->m_flCurTime - SubFloat( info.m_pCreationTimeStamp[ nCreationTimeIndex ], nOffset );
+
+ float flLengthScale = ( flAge >= m_flLengthFadeInTime ) ? 1.0 : ( flAge / m_flLengthFadeInTime );
+
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ Vector vecWorldPos( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ Vector vecViewPos = vecWorldPos;
+
+ // Get our screenspace last position
+ int nPrevXYZIndex = nGroup * info.m_nPrevXYZStride;
+ Vector vecPrevWorldPos( SubFloat( info.m_pPrevXYZ[ nPrevXYZIndex ], nOffset ), SubFloat( info.m_pPrevXYZ[ nPrevXYZIndex+1 ], nOffset ), SubFloat( info.m_pPrevXYZ[ nPrevXYZIndex+2 ], nOffset ) );
+ Vector vecPrevViewPos = vecPrevWorldPos;
+
+ // Get the delta direction and find the magnitude, then scale the length by the desired length amount
+ Vector vecDelta;
+ VectorSubtract( vecPrevViewPos, vecViewPos, vecDelta );
+ float flMag = VectorNormalize( vecDelta );
+ float flLength = flLengthScale * flMag * flOODt * SubFloat( info.m_pLength[ nGroup * info.length_stride ], nOffset );
+ if ( flLength <= 0.0f )
+ return;
+
+ flLength = max( m_flMinLength, min( m_flMaxLength, flLength ) );
+
+ vecDelta *= flLength;
+
+ // Fade the width as the length fades to keep it at a square aspect ratio
+ if ( flLength < rad )
+ {
+ rad = flLength;
+ }
+
+ // Find our tangent direction which "fattens" the line
+ Vector vDirToBeam, vTangentY;
+ VectorSubtract( vecWorldPos, vecCameraPos, vDirToBeam );
+ CrossProduct( vDirToBeam, vecDelta, vTangentY );
+ VectorNormalizeFast( vTangentY );
+
+ // Calculate the verts we'll use as our points
+ Vector verts[4];
+ VectorMA( vecWorldPos, rad*0.5f, vTangentY, verts[0] );
+ VectorMA( vecWorldPos, -rad*0.5f, vTangentY, verts[1] );
+ VectorAdd( verts[0], vecDelta, verts[3] );
+ VectorAdd( verts[1], vecDelta, verts[2] );
+ Assert( verts[0].IsValid() && verts[1].IsValid() && verts[2].IsValid() && verts[3].IsValid() );
+
+ meshBuilder.Position3fv( verts[0].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( verts[1].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fBottom_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( verts[2].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fRight_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( verts[3].Base() );
+ meshBuilder.Color4ub( rc, gc, bc, ac );
+ meshBuilder.TexCoord2f( 0, pSample0->m_fLeft_U0, pSample0->m_fTop_V0 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 1 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 2 );
+ meshBuilder.FastIndex( info.m_nVertexOffset + 3 );
+ info.m_nVertexOffset += 4;
+}
+
+
+void C_OP_RenderSpritesTrail::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ C_OP_RenderSpriteTrailContext_t *pCtx = reinterpret_cast<C_OP_RenderSpriteTrailContext_t *>( pContext );
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+ // Right now we only have a meshbuilder version!
+ if ( !HushAsserts() )
+ Assert( pMaterial->IsSpriteCard() == false );
+ if ( pMaterial->IsSpriteCard() )
+ return;
+
+ // Store matrices off so we can restore them in RenderEnd().
+ pRenderContext->Bind( pMaterial );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+
+ // Get the camera's worldspace position
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ SpriteTrailRenderInfo_t info;
+ info.Init( pParticles, 0, flAgeScale, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, true, &nParticles, &pCtx->m_VisibilityData );
+
+ int nMaxParticlesInBatch = GetMaxParticlesPerBatch( pRenderContext, pMaterial, false );
+ float flOODt = ( pParticles->m_flDt != 0.0f ) ? ( 1.0f / pParticles->m_flDt ) : 1.0f;
+ while ( nParticles )
+ {
+ int nParticlesInBatch = min( nMaxParticlesInBatch, nParticles );
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nParticlesInBatch * 4 * 3, nParticlesInBatch * 6 * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nParticlesInBatch * 4, nParticlesInBatch * 6 );
+ info.m_nVertexOffset = 0;
+ for( int i = 0; i < nParticlesInBatch; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ RenderSpriteTrail( meshBuilder, info, hParticle, vecCameraPos, flOODt, pSortList );
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void C_OP_RenderSpritesTrail::RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ C_OP_RenderSpriteTrailContext_t *pCtx = reinterpret_cast<C_OP_RenderSpriteTrailContext_t *>( pContext );
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ Vector vecCameraPos;
+ pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ float flAgeScale = m_flAnimationRate * SEQUENCE_SAMPLE_COUNT;
+ SpriteTrailRenderInfo_t info;
+ info.Init( pParticles, nVertexOffset, flAgeScale, pParticles->m_Sheet() );
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ float flOODt = ( pParticles->m_flDt != 0.0f ) ? ( 1.0f / pParticles->m_flDt ) : 1.0f;
+ int hParticle = nFirstParticle;
+ for( int i = 0; i < nParticleCount; i++, hParticle++ )
+ {
+ RenderSpriteTrail( meshBuilder, info, hParticle, vecCameraPos, flOODt, pSortList );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Rope renderer
+//
+//-----------------------------------------------------------------------------
+struct RopeRenderInfo_t
+{
+ size_t m_nXYZStride;
+ const fltx4 *m_pXYZ;
+ size_t m_nRadStride;
+ const fltx4 *m_pRadius;
+ size_t m_nRGBStride;
+ const fltx4 *m_pRGB;
+ size_t m_nAlphaStride;
+ const fltx4 *m_pAlpha;
+ CParticleCollection *m_pParticles;
+
+ void Init( CParticleCollection *pParticles )
+ {
+ m_pParticles = pParticles;
+ m_pXYZ = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &m_nXYZStride );
+ m_pRadius = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_RADIUS, &m_nRadStride );
+ m_pRGB = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_TINT_RGB, &m_nRGBStride );
+ m_pAlpha = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ALPHA, &m_nAlphaStride );
+ }
+
+ void GenerateSeg( int hParticle, BeamSeg_t& seg )
+ {
+ Assert( hParticle != -1 );
+ int nGroup = hParticle / 4;
+ int nOffset = hParticle & 0x3;
+
+ int nXYZIndex = nGroup * m_nXYZStride;
+ int nColorIndex = nGroup * m_nRGBStride;
+ seg.m_vPos.Init( SubFloat( m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ seg.m_vColor.Init( SubFloat( m_pRGB[ nColorIndex ], nOffset ), SubFloat( m_pRGB[ nColorIndex+1 ], nOffset ), SubFloat( m_pRGB[nColorIndex+2], nOffset ) );
+ seg.m_flAlpha = SubFloat( m_pAlpha[ nGroup * m_nAlphaStride ], nOffset );
+ seg.m_flWidth = SubFloat( m_pRadius[ nGroup * m_nRadStride ], nOffset );
+ }
+};
+
+
+struct RenderRopeContext_t
+{
+ float m_flRenderedRopeLength;
+};
+
+class C_OP_RenderRope : public CParticleOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderRope );
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_RADIUS_MASK |
+ PARTICLE_ATTRIBUTE_TINT_RGB_MASK | PARTICLE_ATTRIBUTE_ALPHA_MASK;
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ RenderRopeContext_t *pCtx = reinterpret_cast<RenderRopeContext_t *>( pContext );
+ pCtx->m_flRenderedRopeLength = false;
+ float *pSubdivList = (float*)( pCtx + 1 );
+ for ( int iSubdiv = 0; iSubdiv < m_nSubdivCount; iSubdiv++ )
+ {
+ pSubdivList[iSubdiv] = (float)iSubdiv / (float)m_nSubdivCount;
+ }
+
+ // NOTE: Has to happen here, and not in InitParams, since the material isn't set up yet
+ const_cast<C_OP_RenderRope*>( this )->m_flTextureScale = 1.0f / ( pParticles->m_pDef->GetMaterial()->GetMappingHeight() * m_flTexelSizeInUnits );
+ }
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( RenderRopeContext_t ) + m_nSubdivCount * sizeof(float);
+ }
+
+ virtual void InitParams( CParticleSystemDefinition *pDef, CDmxElement *pElement )
+ {
+ if ( m_nSubdivCount <= 0 )
+ {
+ m_nSubdivCount = 1;
+ }
+ if ( m_flTexelSizeInUnits <= 0 )
+ {
+ m_flTexelSizeInUnits = 1.0f;
+ }
+ m_flTStep = 1.0 / m_nSubdivCount;
+ }
+
+ virtual int GetParticlesToRender( CParticleCollection *pParticles, void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices, int *pVertsUsed, int *pIndicesUsed ) const;
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+ virtual void RenderSpriteCard( CParticleCollection *pParticles, void *pContext, IMaterial *pMaterial ) const;
+ virtual void RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const;
+
+ // We connect neighboring particle instances to each other, so if the order isn't maintained we will have a particle that jumps
+ // back to the wrong place and look terrible.
+ virtual bool RequiresOrderInvariance( void ) const OVERRIDE
+ {
+ return true;
+ }
+
+ int m_nSubdivCount;
+
+ float m_flTexelSizeInUnits;
+ float m_flTextureScale;
+ float m_flTextureScrollRate;
+ float m_flTStep;
+
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderRope, "render_rope", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_RenderRope )
+ DMXELEMENT_UNPACK_FIELD( "subdivision_count", "3", int, m_nSubdivCount )
+ DMXELEMENT_UNPACK_FIELD( "texel_size", "4.0f", float, m_flTexelSizeInUnits )
+ DMXELEMENT_UNPACK_FIELD( "texture_scroll_rate", "0.0f", float, m_flTextureScrollRate )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderRope )
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of particles to render
+//-----------------------------------------------------------------------------
+int C_OP_RenderRope::GetParticlesToRender( CParticleCollection *pParticles,
+ void *pContext, int nFirstParticle, int nRemainingVertices, int nRemainingIndices,
+ int *pVertsUsed, int *pIndicesUsed ) const
+{
+ if ( ( nFirstParticle >= pParticles->m_nActiveParticles - 1 ) || ( pParticles->m_nActiveParticles <= 1 ) )
+ {
+ *pVertsUsed = 0;
+ *pIndicesUsed = 0;
+ return 0;
+ }
+
+ // NOTE: This is only true for particles *after* the first particle.
+ // First particle takes 2 verts, no indices.
+ int nVertsPerParticle = 2 * m_nSubdivCount;
+ int nIndicesPerParticle = 6 * m_nSubdivCount;
+
+ // Subtract 2 is because the first particle uses an extra pair of vertices
+ int nMaxParticleCount = 1 + ( nRemainingVertices - 2 ) / nVertsPerParticle;
+ int nMaxParticleCount2 = nRemainingIndices / nIndicesPerParticle;
+ if ( nMaxParticleCount > nMaxParticleCount2 )
+ {
+ nMaxParticleCount = nMaxParticleCount2;
+ }
+
+ int nParticleCount = pParticles->m_nActiveParticles - nFirstParticle;
+
+ // We can't choose a max particle count so that we only have 1 particle to render next time
+ if ( nMaxParticleCount == nParticleCount - 1 )
+ {
+ --nMaxParticleCount;
+ Assert( nMaxParticleCount > 0 );
+ }
+
+ if ( nParticleCount > nMaxParticleCount )
+ {
+ nParticleCount = nMaxParticleCount;
+ }
+
+ *pVertsUsed = ( nParticleCount - 1 ) * m_nSubdivCount * 2 + 2;
+ *pIndicesUsed = nParticleCount * m_nSubdivCount * 6;
+ return nParticleCount;
+}
+
+
+#define OUTPUT_2SPLINE_VERTS( t ) \
+ meshBuilder.Color4ub( FastFToC( vecColor.x ), FastFToC( vecColor.y), FastFToC( vecColor.z), FastFToC( vecColor.w ) ); \
+ meshBuilder.Position3f( (t), flU, 0 ); \
+ meshBuilder.TexCoord4fv( 0, vecP0.Base() ); \
+ meshBuilder.TexCoord4fv( 1, vecP1.Base() ); \
+ meshBuilder.TexCoord4fv( 2, vecP2.Base() ); \
+ meshBuilder.TexCoord4fv( 3, vecP3.Base() ); \
+ meshBuilder.AdvanceVertex(); \
+ meshBuilder.Color4ub( FastFToC( vecColor.x ), FastFToC( vecColor.y), FastFToC( vecColor.z), FastFToC( vecColor.w ) ); \
+ meshBuilder.Position3f( (t), flU, 1 ); \
+ meshBuilder.TexCoord4fv( 0, vecP0.Base() ); \
+ meshBuilder.TexCoord4fv( 1, vecP1.Base() ); \
+ meshBuilder.TexCoord4fv( 2, vecP2.Base() ); \
+ meshBuilder.TexCoord4fv( 3, vecP3.Base() ); \
+ meshBuilder.AdvanceVertex();
+
+void C_OP_RenderRope::RenderSpriteCard( CParticleCollection *pParticles, void *pContext, IMaterial *pMaterial ) const
+{
+ int nParticles = pParticles->m_nActiveParticles;
+
+ int nSegmentsToRender = nParticles - 1;
+ if ( ! nSegmentsToRender )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( pMaterial );
+
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+
+ int nNumIndicesPerSegment = 6 * m_nSubdivCount;
+ int nNumVerticesPerSegment = 2 * m_nSubdivCount;
+
+ int nNumSegmentsPerBatch = min( ( nMaxVertices - 2 )/nNumVerticesPerSegment,
+ ( nMaxIndices ) / nNumIndicesPerSegment );
+
+ const float *pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, 0 );
+ const float *pColor = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_TINT_RGB, 0 );
+
+ const float *pRadius = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_RADIUS, 0 );
+ const float *pAlpha = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_ALPHA, 0 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+
+ int nNumSegmentsIWillRenderPerBatch = min( nNumSegmentsPerBatch, nSegmentsToRender );
+
+ bool bFirstPoint = true;
+
+ float flTexOffset = m_flTextureScrollRate * pParticles->m_flCurTime;
+ float flU = flTexOffset;
+
+ // initialize first spline segment
+ Vector4D vecP1( pXYZ[0], pXYZ[4], pXYZ[8], pRadius[0] );
+ Vector4D vecP2( pXYZ[1], pXYZ[5], pXYZ[9], pRadius[1] );
+ Vector4D vecP0 = vecP1;
+ Vector4D vecColor( pColor[0], pColor[4], pColor[8], pAlpha[0] );
+ Vector4D vecDelta = vecP2;
+ vecDelta -= vecP1;
+ vecP0 -= vecDelta;
+
+ Vector4D vecP3;
+
+ if ( nParticles < 3 )
+ {
+ vecP3 = vecP2;
+ vecP3 += vecDelta;
+ }
+ else
+ {
+ vecP3.Init( pXYZ[2], pXYZ[6], pXYZ[10], pRadius[2] );
+ }
+ int nPnt = 3;
+ int nCurIDX = 0;
+
+ int nSegmentsAvailableInBuffer = nNumSegmentsIWillRenderPerBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment * 3, nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch * 3 );
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES,
+ 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment,
+ nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch );
+
+
+
+ float flDUScale = ( m_flTStep * m_flTexelSizeInUnits );
+ float flT = 0;
+
+ do
+ {
+ if ( ! nSegmentsAvailableInBuffer )
+ {
+ meshBuilder.End();
+ pMesh->Draw();
+
+ g_pParticleSystemMgr->TallyParticlesRendered( 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment * 3, nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch * 3 );
+
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 2 + nNumSegmentsIWillRenderPerBatch * nNumVerticesPerSegment,
+ nNumIndicesPerSegment * nNumSegmentsIWillRenderPerBatch );
+
+ // copy the last emitted points
+ OUTPUT_2SPLINE_VERTS( flT );
+ nSegmentsAvailableInBuffer = nNumSegmentsIWillRenderPerBatch;
+ nCurIDX = 0;
+ }
+ nSegmentsAvailableInBuffer--;
+ flT = 0.;
+ float flDu = flDUScale * ( vecP2.AsVector3D() - vecP1.AsVector3D() ).Length();
+ for( int nSlice = 0 ; nSlice < m_nSubdivCount; nSlice++ )
+ {
+ OUTPUT_2SPLINE_VERTS( flT );
+ flT += m_flTStep;
+ flU += flDu;
+ if ( ! bFirstPoint )
+ {
+ meshBuilder.FastIndex( nCurIDX );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+3 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+ nCurIDX += 2;
+ }
+ bFirstPoint = false;
+ }
+ // next segment
+ if ( nSegmentsToRender > 1 )
+ {
+ vecP0 = vecP1;
+ vecP1 = vecP2;
+ vecP2 = vecP3;
+ pRadius = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_RADIUS, nPnt );
+ pAlpha = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_ALPHA, nPnt -2 );
+ vecColor.Init( pColor[0], pColor[4], pColor[8], pAlpha[0] );
+
+ if ( nPnt < nParticles )
+ {
+ pXYZ = pParticles->GetFloatAttributePtr(
+ PARTICLE_ATTRIBUTE_XYZ, nPnt );
+ vecP3.Init( pXYZ[0], pXYZ[4], pXYZ[8], pRadius[0] );
+ nPnt++;
+ }
+ else
+ {
+ // fake last point by extrapolating
+ vecP3 += vecP2;
+ vecP3 -= vecP1;
+ }
+ }
+ } while( --nSegmentsToRender );
+
+ // output last piece
+ OUTPUT_2SPLINE_VERTS( 1.0 );
+ meshBuilder.FastIndex( nCurIDX );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+ meshBuilder.FastIndex( nCurIDX+1 );
+ meshBuilder.FastIndex( nCurIDX+3 );
+ meshBuilder.FastIndex( nCurIDX+2 );
+
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Renders particles, sorts them (?)
+//-----------------------------------------------------------------------------
+void C_OP_RenderRope::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ // FIXME: What does this even mean? Ropes can't really be sorted.
+
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+ if ( pMaterial->IsSpriteCard() )
+ {
+ RenderSpriteCard( pParticles, pContext, pMaterial );
+ return;
+ }
+
+ pRenderContext->Bind( pMaterial );
+
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+ int nParticles = pParticles->m_nActiveParticles;
+
+ int nFirstParticle = 0;
+ while ( nParticles )
+ {
+ int nVertCount, nIndexCount;
+ int nParticlesInBatch = GetParticlesToRender( pParticles, pContext, nFirstParticle, nMaxVertices, nMaxIndices, &nVertCount, &nIndexCount );
+ if ( nParticlesInBatch == 0 )
+ break;
+
+ nParticles -= nParticlesInBatch;
+
+ g_pParticleSystemMgr->TallyParticlesRendered( nVertCount * 3, nIndexCount * 3 );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );
+
+ RenderUnsorted( pParticles, pContext, pRenderContext, meshBuilder, 0, nFirstParticle, nParticlesInBatch );
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ nFirstParticle += nParticlesInBatch;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_OP_RenderRope::RenderUnsorted( CParticleCollection *pParticles, void *pContext, IMatRenderContext *pRenderContext, CMeshBuilder &meshBuilder, int nVertexOffset, int nFirstParticle, int nParticleCount ) const
+{
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ // Right now we only have a meshbuilder version!
+ Assert( pMaterial->IsSpriteCard() == false );
+ if ( pMaterial->IsSpriteCard() )
+ return;
+
+ RenderRopeContext_t *pCtx = reinterpret_cast<RenderRopeContext_t *>( pContext );
+ float *pSubdivList = (float*)( pCtx + 1 );
+ if ( nFirstParticle == 0 )
+ {
+ pCtx->m_flRenderedRopeLength = 0.0f;
+ }
+
+ float flTexOffset = m_flTextureScrollRate * pParticles->m_flCurTime;
+
+ RopeRenderInfo_t info;
+ info.Init( pParticles );
+
+ CBeamSegDraw beamSegment;
+ beamSegment.Start( pRenderContext, ( nParticleCount - 1 ) * m_nSubdivCount + 1, pMaterial, &meshBuilder, nVertexOffset );
+
+ Vector vecCatmullRom[4];
+ BeamSeg_t seg[2];
+ info.GenerateSeg( nFirstParticle, seg[0] );
+ seg[0].m_flTexCoord = ( pCtx->m_flRenderedRopeLength + flTexOffset ) * m_flTextureScale;
+
+ beamSegment.NextSeg( &seg[0] );
+ vecCatmullRom[1] = seg[0].m_vPos;
+ if ( nFirstParticle == 0 )
+ {
+ vecCatmullRom[0] = vecCatmullRom[1];
+ }
+ else
+ {
+ int nGroup = ( nFirstParticle-1 ) / 4;
+ int nOffset = ( nFirstParticle-1 ) & 0x3;
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ vecCatmullRom[0].Init( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ }
+
+ float flOOSubDivCount = 1.0f / m_nSubdivCount;
+ int hParticle = nFirstParticle + 1;
+ for ( int i = 1; i < nParticleCount; ++i, ++hParticle )
+ {
+ int nCurr = i & 1;
+ int nPrev = 1 - nCurr;
+ info.GenerateSeg( hParticle, seg[nCurr] );
+ pCtx->m_flRenderedRopeLength += seg[nCurr].m_vPos.DistTo( seg[nPrev].m_vPos );
+ seg[nCurr].m_flTexCoord = ( pCtx->m_flRenderedRopeLength + flTexOffset ) * m_flTextureScale;
+
+ if ( m_nSubdivCount > 1 )
+ {
+ vecCatmullRom[ (i+1) & 0x3 ] = seg[nCurr].m_vPos;
+ if ( hParticle != info.m_pParticles->m_nActiveParticles - 1 )
+ {
+ int nGroup = ( hParticle+1 ) / 4;
+ int nOffset = ( hParticle+1 ) & 0x3;
+ int nXYZIndex = nGroup * info.m_nXYZStride;
+ vecCatmullRom[ (i+2) & 0x3 ].Init( SubFloat( info.m_pXYZ[ nXYZIndex ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+1 ], nOffset ), SubFloat( info.m_pXYZ[ nXYZIndex+2 ], nOffset ) );
+ }
+ else
+ {
+ vecCatmullRom[ (i+2) & 0x3 ] = vecCatmullRom[ (i+1) & 0x3 ];
+ }
+
+ BeamSeg_t &subDivSeg = seg[nPrev];
+ Vector vecColorInc = ( seg[nCurr].m_vColor - seg[nPrev].m_vColor ) * flOOSubDivCount;
+ float flAlphaInc = ( seg[nCurr].m_flAlpha - seg[nPrev].m_flAlpha ) * flOOSubDivCount;
+ float flTexcoordInc = ( seg[nCurr].m_flTexCoord - seg[nPrev].m_flTexCoord ) * flOOSubDivCount;
+ float flWidthInc = ( seg[nCurr].m_flWidth - seg[nPrev].m_flWidth ) * flOOSubDivCount;
+ for( int iSubdiv = 1; iSubdiv < m_nSubdivCount; ++iSubdiv )
+ {
+ subDivSeg.m_vColor += vecColorInc;
+ subDivSeg.m_vColor.x = clamp( subDivSeg.m_vColor.x, 0.0f, 1.0f );
+ subDivSeg.m_vColor.y = clamp( subDivSeg.m_vColor.y, 0.0f, 1.0f );
+ subDivSeg.m_vColor.z = clamp( subDivSeg.m_vColor.z, 0.0f, 1.0f );
+ subDivSeg.m_flAlpha += flAlphaInc;
+ subDivSeg.m_flAlpha = clamp( subDivSeg.m_flAlpha, 0.0f, 1.0f );
+ subDivSeg.m_flTexCoord += flTexcoordInc;
+ subDivSeg.m_flWidth += flWidthInc;
+
+ Catmull_Rom_Spline( vecCatmullRom[ (i+3) & 0x3 ], vecCatmullRom[ i & 0x3 ],
+ vecCatmullRom[ (i+1) & 0x3 ], vecCatmullRom[ (i+2) & 0x3 ],
+ pSubdivList[iSubdiv], subDivSeg.m_vPos );
+
+ beamSegment.NextSeg( &subDivSeg );
+ }
+ }
+
+ beamSegment.NextSeg( &seg[nCurr] );
+ }
+
+ beamSegment.End();
+}
+
+#ifdef USE_BLOBULATOR // Enable blobulator for EP3
+
+//-----------------------------------------------------------------------------
+// Installs renderers
+//-----------------------------------------------------------------------------
+class C_OP_RenderBlobs : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderBlobs );
+
+ float m_cubeWidth;
+ float m_cutoffRadius;
+ float m_renderRadius;
+
+
+ struct C_OP_RenderBlobsContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderBlobsContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderBlobsContext_t *pCtx = reinterpret_cast<C_OP_RenderBlobsContext_t *>( pContext );
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return 0;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK;
+ }
+
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+
+ virtual bool IsBatchable() const
+ {
+ return false;
+ }
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderBlobs, "render_blobs", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderBlobs )
+ DMXELEMENT_UNPACK_FIELD( "cube_width", "1.0f", float, m_cubeWidth )
+ DMXELEMENT_UNPACK_FIELD( "cutoff_radius", "3.3f", float, m_cutoffRadius )
+ DMXELEMENT_UNPACK_FIELD( "render_radius", "1.3f", float, m_renderRadius )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderBlobs )
+
+
+void C_OP_RenderBlobs::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ ImpTiler* tiler = ImpTilerFactory::factory->getTiler();
+ //RENDERER_CLASS* sweepRenderer = tiler->getRenderer();
+
+ C_OP_RenderBlobsContext_t *pCtx = reinterpret_cast<C_OP_RenderBlobsContext_t *>( pContext );
+
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+
+
+ #if 0
+ // Note: it is not good to have these static variables here.
+ static RENDERER_CLASS* sweepRenderer = NULL;
+ static ImpTiler* tiler = NULL;
+ if(!sweepRenderer)
+ {
+ sweepRenderer = new RENDERER_CLASS();
+ tiler = new ImpTiler(sweepRenderer);
+ }
+ #endif
+
+ // TODO: I should get rid of this static array and static calls
+ // to setCubeWidth, etc...
+ static SmartArray<ImpParticle> imp_particles_sa; // This doesn't specify alignment, might have problems with SSE
+
+ RENDERER_CLASS::setCubeWidth(m_cubeWidth);
+ RENDERER_CLASS::setRenderR(m_renderRadius);
+ RENDERER_CLASS::setCutoffR(m_cutoffRadius);
+
+ RENDERER_CLASS::setCalcSignFunc(calcSign);
+ RENDERER_CLASS::setCalcSign2Func(calcSign2);
+ #if 0
+ RENDERER_CLASS::setCalcCornerFunc(CALC_CORNER_NORMAL_COLOR_CI_SIZE, calcCornerNormalColor);
+ RENDERER_CLASS::setCalcVertexFunc(calcVertexNormalNColor);
+ #endif
+ #if 1
+ RENDERER_CLASS::setCalcCornerFunc(CALC_CORNER_NORMAL_CI_SIZE, calcCornerNormal);
+ RENDERER_CLASS::setCalcVertexFunc(calcVertexNormalDebugColor);
+ #endif
+ #if 0
+ RENDERER_CLASS::setCalcCornerFunc(CALC_CORNER_NORMAL_COLOR_CI_SIZE, calcCornerNormalHiFreqColor);
+ RENDERER_CLASS::setCalcVertexFunc(calcVertexNormalNColor);
+ #endif
+
+
+ IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
+
+ // TODO: I don't need to load this as a sorted list. See Lennard Jones forces for better way!
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ Vector bbMin;
+ Vector bbMax;
+ pParticles->GetBounds( &bbMin, &bbMax );
+ Vector bbCenter = 0.5f * ( bbMin + bbMax );
+
+ // FIXME: Make this configurable. Not all shaders perform lighting. Although it's pretty likely for isosurface shaders.
+ g_pParticleSystemMgr->Query()->SetUpLightingEnvironment( bbCenter );
+
+ // FIXME: Ugly hack to get particle system location to a special blob shader lighting proxy
+ pRenderContext->Bind( pMaterial, &bbCenter );
+
+ //CMeshBuilder meshBuilder;
+ //int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
+
+ tiler->beginFrame(Point3D(0.0f, 0.0f, 0.0f), (void*)&pRenderContext);
+
+ while(imp_particles_sa.size < nParticles)
+ {
+ imp_particles_sa.pushAutoSize(ImpParticle());
+ }
+
+ for( int i = 0; i < nParticles; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ int nIndex = ( hParticle / 4 ) * xyz_stride;
+ int nOffset = hParticle & 0x3;
+ float x = SubFloat( xyz[nIndex], nOffset );
+ float y = SubFloat( xyz[nIndex+1], nOffset );
+ float z = SubFloat( xyz[nIndex+2], nOffset );
+
+ ImpParticle* imp_particle = &imp_particles_sa[i];
+ imp_particle->center[0]=x;
+ imp_particle->center[1]=y;
+ imp_particle->center[2]=z;
+ imp_particle->setFieldScale(1.0f);
+ //imp_particle->interpolants1.set(1.0f, 1.0f, 1.0f);
+ //imp_particle->interpolants1[3] = 0.0f; //m_flSurfaceV[i];
+ tiler->insertParticle(imp_particle);
+ }
+
+
+ tiler->drawSurface(); // NOTE: need to call drawSurfaceSorted for transparency
+ tiler->endFrame();
+
+ ImpTilerFactory::factory->returnTiler(tiler);
+
+}
+
+#endif //blobs
+
+
+
+//-----------------------------------------------------------------------------
+// Installs renderers
+//-----------------------------------------------------------------------------
+class C_OP_RenderScreenVelocityRotate : public CParticleRenderOperatorInstance
+{
+ DECLARE_PARTICLE_OPERATOR( C_OP_RenderScreenVelocityRotate );
+
+ float m_flRotateRateDegrees;
+ float m_flForwardDegrees;
+
+ struct C_OP_RenderScreenVelocityRotateContext_t
+ {
+ CParticleVisibilityData m_VisibilityData;
+ int m_nQueryHandle;
+ };
+
+ size_t GetRequiredContextBytes( void ) const
+ {
+ return sizeof( C_OP_RenderScreenVelocityRotateContext_t );
+ }
+
+ virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
+ {
+ C_OP_RenderScreenVelocityRotateContext_t *pCtx = reinterpret_cast<C_OP_RenderScreenVelocityRotateContext_t *>( pContext );
+ if ( VisibilityInputs.m_nCPin >= 0 )
+ pCtx->m_VisibilityData.m_bUseVisibility = true;
+ else
+ pCtx->m_VisibilityData.m_bUseVisibility = false;
+ pCtx->m_VisibilityData.m_flCameraBias = VisibilityInputs.m_flCameraBias;
+ }
+ uint32 GetWrittenAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_ROTATION_MASK;
+ }
+
+ uint32 GetReadAttributes( void ) const
+ {
+ return PARTICLE_ATTRIBUTE_XYZ_MASK | PARTICLE_ATTRIBUTE_PREV_XYZ_MASK | PARTICLE_ATTRIBUTE_ROTATION_MASK ;
+ }
+
+ virtual void Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const;
+};
+
+DEFINE_PARTICLE_OPERATOR( C_OP_RenderScreenVelocityRotate, "render_screen_velocity_rotate", OPERATOR_SINGLETON );
+
+BEGIN_PARTICLE_RENDER_OPERATOR_UNPACK( C_OP_RenderScreenVelocityRotate )
+ DMXELEMENT_UNPACK_FIELD( "rotate_rate(dps)", "0.0f", float, m_flRotateRateDegrees )
+ DMXELEMENT_UNPACK_FIELD( "forward_angle", "-90.0f", float, m_flForwardDegrees )
+END_PARTICLE_OPERATOR_UNPACK( C_OP_RenderScreenVelocityRotate )
+
+
+void C_OP_RenderScreenVelocityRotate::Render( IMatRenderContext *pRenderContext, CParticleCollection *pParticles, void *pContext ) const
+{
+ C_OP_RenderScreenVelocityRotateContext_t *pCtx = reinterpret_cast<C_OP_RenderScreenVelocityRotateContext_t *>( pContext );
+ if ( pCtx->m_VisibilityData.m_bUseVisibility )
+ {
+ SetupParticleVisibility( pParticles, &pCtx->m_VisibilityData, &VisibilityInputs, &pCtx->m_nQueryHandle );
+ }
+
+
+
+ // NOTE: This is interesting to support because at first we won't have all the various
+ // pixel-shader versions of SpriteCard, like modulate, twotexture, etc. etc.
+ VMatrix tempView;
+
+ // Store matrices off so we can restore them in RenderEnd().
+ pRenderContext->GetMatrix(MATERIAL_VIEW, &tempView);
+
+ int nParticles;
+ const ParticleRenderData_t *pSortList = pParticles->GetRenderList( pRenderContext, false, &nParticles, &pCtx->m_VisibilityData );
+
+ size_t xyz_stride;
+ const fltx4 *xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
+
+ size_t prev_xyz_stride;
+ const fltx4 *prev_xyz = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, &prev_xyz_stride );
+
+ size_t rot_stride;
+ // const fltx4 *pRot = pParticles->GetM128AttributePtr( PARTICLE_ATTRIBUTE_ROTATION, &rot_stride );
+ fltx4 *pRot = pParticles->GetM128AttributePtrForWrite( PARTICLE_ATTRIBUTE_ROTATION, &rot_stride );
+
+ float flForwardRadians = m_flForwardDegrees * ( M_PI / 180.0f );
+ //float flRotateRateRadians = m_flRotateRateDegrees * ( M_PI / 180.0f );
+
+ for( int i = 0; i < nParticles; i++ )
+ {
+ int hParticle = (--pSortList)->m_nIndex;
+ int nGroup = ( hParticle / 4 );
+ int nOffset = hParticle & 0x3;
+
+ int nXYZIndex = nGroup * xyz_stride;
+ Vector vecWorldPos( SubFloat( xyz[ nXYZIndex ], nOffset ), SubFloat( xyz[ nXYZIndex+1 ], nOffset ), SubFloat( xyz[ nXYZIndex+2 ], nOffset ) );
+ Vector vecViewPos;
+ Vector3DMultiplyPosition( tempView, vecWorldPos, vecViewPos );
+
+ if (!IsFinite(vecViewPos.x))
+ continue;
+
+ int nPrevXYZIndex = nGroup * prev_xyz_stride;
+ Vector vecPrevWorldPos( SubFloat( prev_xyz[ nPrevXYZIndex ], nOffset ), SubFloat( prev_xyz[ nPrevXYZIndex+1 ], nOffset ), SubFloat( prev_xyz[ nPrevXYZIndex+2 ], nOffset ) );
+ Vector vecPrevViewPos;
+ Vector3DMultiplyPosition( tempView, vecPrevWorldPos, vecPrevViewPos );
+
+ float rot = atan2( vecViewPos.y - vecPrevViewPos.y, vecViewPos.x - vecPrevViewPos.x ) + flForwardRadians;
+ SubFloat( pRot[ nGroup * rot_stride ], nOffset ) = rot;
+ }
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Installs renderers
+//-----------------------------------------------------------------------------
+void AddBuiltInParticleRenderers( void )
+{
+#ifdef _DEBUG
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderPoints );
+#endif
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderSprites );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderSpritesTrail );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderRope );
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderScreenVelocityRotate );
+#ifdef USE_BLOBULATOR
+ REGISTER_PARTICLE_OPERATOR( FUNCTION_RENDERER, C_OP_RenderBlobs );
+#endif // blobs
+}
+
+
+
+