//===== Copyright 1996-2006, Valve Corporation, All rights reserved. ======// // // Purpose: particle system code // //===========================================================================// #include #include "tier0/platform.h" #include "tier0/vprof.h" #include "particles/particles.h" #include "particles_internal.h" #include "bitmap/psheet.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define SORTBUFSIZE0 (( MAX_PARTICLES_IN_A_SYSTEM + 4 ) * sizeof( ParticleRenderData_t ) ) #define SORTBUFSIZE1 ( ( 1 + MAX_PARTICLES_IN_A_SYSTEM / 4 )* sizeof( ParticleFullRenderData_Scalar_View ) ) #define SORTBUFSIZE2 ( ( 1 + MAX_PARTICLES_IN_A_SYSTEM / 4 )* sizeof( ParticleRenderDataWithOutlineInformation_Scalar_View ) ) static ALIGN16 uint8 s_SortBuffer[ COMPILETIME_MAX( COMPILETIME_MAX( SORTBUFSIZE0, SORTBUFSIZE1 ), SORTBUFSIZE2 )] ALIGN16_POST; static ALIGN16 ParticleFullRenderData_Scalar_View *s_pParticlePtrs[MAX_PARTICLES_IN_A_SYSTEM] ALIGN16_POST; enum EParticleSortKeyType { SORT_KEY_NONE, SORT_KEY_DISTANCE, SORT_KEY_CREATION_TIME, }; void C4VInterpolatedAttributeIterator::Init( int nAttribute, CParticleCollection *pParticles ) { // initializing this is somewhat complicated by the behavior of prev_xyz and xyz m_pData = pParticles->Get4VAttributePtr( nAttribute, &m_nStride ); Assert( pParticles->GetPrevAttributeMemory() ); if ( m_nStride ) { m_nOldDataOffset = pParticles->m_PreviousFrameAttributes.ByteAddress( nAttribute ) - pParticles->m_ParticleAttributes.ByteAddress( nAttribute ); } else { // it's constant data m_nOldDataOffset = 0; } } template void s_GenerateData( void *pOutData, Vector CameraPos, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles ) { fltx4 *pOutUnSorted = reinterpret_cast( pOutData ); C4VAttributeIterator pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles ); CM128AttributeIterator pCreationTimeStamp( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles ); CM128AttributeIterator pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles ); CM128AttributeIterator pAlpha2( PARTICLE_ATTRIBUTE_ALPHA2, pParticles ); CM128AttributeIterator pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles ); int nParticles = pParticles->m_nActiveParticles; FourVectors EyePos; EyePos.DuplicateVector( CameraPos ); FourVectors v4Fwd; if ( bCull ) v4Fwd.DuplicateVector( *pCameraFwd ); fltx4 fl4AlphaVis = ReplicateX4( pVisibilityData->m_flAlphaVisibility ); fltx4 fl4RadVis = ReplicateX4( pVisibilityData->m_flRadiusVisibility ); // indexing. We will generate the index as float and use magicf2i to convert to integer fltx4 fl4OutIdx = g_SIMD_0123; // 0 1 2 3 fl4OutIdx = AddSIMD( fl4OutIdx, Four_2ToThe23s); // fix as int bool bUseVis = pVisibilityData->m_bUseVisibility; fltx4 fl4AlphaScale = ReplicateX4( 255.0 ); fltx4 fl4SortKey = Four_Zeros; do { fltx4 fl4FinalAlpha = MulSIMD( *pAlpha, *pAlpha2 ); fltx4 fl4FinalRadius = *pRadius; if ( bUseVis ) { fl4FinalAlpha = MaxSIMD ( Four_Zeros, MinSIMD( Four_Ones, MulSIMD( fl4FinalAlpha, fl4AlphaVis) ) ); fl4FinalRadius = MulSIMD( fl4FinalRadius, fl4RadVis ); } // convert float 0..1 to int 0..255 fl4FinalAlpha = AddSIMD( MulSIMD( fl4FinalAlpha, fl4AlphaScale ), Four_2ToThe23s ); if ( eSortKeyMode == SORT_KEY_CREATION_TIME ) { fl4SortKey = *pCreationTimeStamp; } if ( bCull || ( eSortKeyMode == SORT_KEY_DISTANCE ) ) { fltx4 fl4X = pXYZ->x; fltx4 fl4Y = pXYZ->y; fltx4 fl4Z = pXYZ->z; fltx4 Xdiff = SubSIMD( fl4X, EyePos.x ); fltx4 Ydiff = SubSIMD( fl4Y, EyePos.y ); fltx4 Zdiff = SubSIMD( fl4Z, EyePos.z ); if ( bCull ) { fltx4 dot = AddSIMD( MulSIMD( Xdiff, v4Fwd.x ), AddSIMD( MulSIMD( Ydiff, v4Fwd.y ), MulSIMD( Zdiff, v4Fwd.z ) ) ); fl4FinalAlpha = AndSIMD( fl4FinalAlpha, CmpGeSIMD( dot, Four_Zeros ) ); } if ( eSortKeyMode == SORT_KEY_DISTANCE ) { fl4SortKey = AddSIMD( MulSIMD( Xdiff, Xdiff ), AddSIMD( MulSIMD( Ydiff, Ydiff ), MulSIMD( Zdiff, Zdiff ) ) ); } } // now, we will use simd transpose to write the output fltx4 i4Indices = AndSIMD( fl4OutIdx, LoadAlignedSIMD( (float *) g_SIMD_Low16BitsMask ) ); TransposeSIMD( fl4SortKey, i4Indices, fl4FinalRadius, fl4FinalAlpha ); pOutUnSorted[0] = fl4SortKey; pOutUnSorted[1] = i4Indices; pOutUnSorted[2] = fl4FinalRadius; pOutUnSorted[3] = fl4FinalAlpha; pOutUnSorted += 4; fl4OutIdx = AddSIMD( fl4OutIdx, Four_Fours ); nParticles -= 4; ++pXYZ; ++pAlpha; ++pAlpha2; ++pRadius; } while( nParticles > 0 ); // we're not called with 0 } #define TREATASINT(x) ( *( ( (int32 const *)( &(x) ) ) ) ) static bool SortLessFunc( const ParticleRenderData_t &left, const ParticleRenderData_t &right ) { return TREATASINT( left.m_flSortKey ) < TREATASINT( right.m_flSortKey ); } int CParticleCollection::GenerateSortedIndexList( ParticleRenderData_t *pOut, Vector vecCamera, CParticleVisibilityData *pVisibilityData, bool bSorted ) { VPROF_BUDGET( "CParticleCollection::GenerateSortedIndexList", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); int nParticles = m_nActiveParticles; if ( bSorted ) { s_GenerateData( pOut, vecCamera, NULL, pVisibilityData, this ); } else s_GenerateData( pOut, vecCamera, NULL, pVisibilityData, this ); if ( bSorted ) { // sort the output in place std::make_heap( pOut, pOut + nParticles, SortLessFunc ); std::sort_heap( pOut, pOut + nParticles, SortLessFunc ); } return nParticles; } int CParticleCollection::GenerateCulledSortedIndexList( ParticleRenderData_t *pOut, Vector vecCamera, Vector vecFwd, CParticleVisibilityData *pVisibilityData, bool bSorted ) { VPROF_BUDGET( "CParticleCollection::GenerateSortedIndexList", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); int nParticles = m_nActiveParticles; if ( bSorted ) { s_GenerateData( pOut, vecCamera, &vecFwd, pVisibilityData, this ); } else s_GenerateData( pOut, vecCamera, &vecFwd, pVisibilityData, this ); #ifndef SWDS if ( bSorted ) { // sort the output in place std::make_heap( pOut, pOut + nParticles, SortLessFunc ); std::sort_heap( pOut, pOut + nParticles, SortLessFunc ); } #endif return nParticles; } const ParticleRenderData_t *CParticleCollection::GetRenderList( IMatRenderContext *pRenderContext, bool bSorted, int *pNparticles, CParticleVisibilityData *pVisibilityData) { if ( bSorted ) bSorted = m_pDef->m_bShouldSort; Vector vecCamera; pRenderContext->GetWorldSpaceCameraPosition( &vecCamera ); ParticleRenderData_t *pOut = ( ParticleRenderData_t * ) s_SortBuffer; // check if the camera is inside the bounding box to see whether culling is worth it int nParticles; if ( vecCamera.WithinAABox( m_MinBounds, m_MaxBounds ) ) { Vector vecFwd, vecRight, vecUp; pRenderContext->GetWorldSpaceCameraVectors( &vecFwd, &vecRight, &vecUp ); nParticles = GenerateCulledSortedIndexList( pOut, vecCamera, vecFwd, pVisibilityData, bSorted ); } else { // outside the bounds. don't bother agressive culling nParticles = GenerateSortedIndexList( pOut, vecCamera, pVisibilityData, bSorted ); } *pNparticles = nParticles; return pOut + nParticles; } template void GenerateExtendedData( void *pOutbuf, ParticleFullRenderData_Scalar_View **pIndexBuffer, Vector CameraPos, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles, float flInterpT ) { OutType_t * RESTRICT pOutUnSorted = reinterpret_cast( pOutbuf ); // interpolated values VECTORITERATOR pXYZ( PARTICLE_ATTRIBUTE_XYZ, pParticles ); VECTORITERATOR pRGB( PARTICLE_ATTRIBUTE_TINT_RGB, pParticles ); VECTORITERATOR pRGB2; SCALARITERATOR pAlpha( PARTICLE_ATTRIBUTE_ALPHA, pParticles ); SCALARITERATOR pAlpha2( PARTICLE_ATTRIBUTE_ALPHA2, pParticles ); SCALARITERATOR pRadius( PARTICLE_ATTRIBUTE_RADIUS, pParticles ); SCALARITERATOR pRot( PARTICLE_ATTRIBUTE_ROTATION, pParticles ); SCALARITERATOR pYaw( PARTICLE_ATTRIBUTE_YAW, pParticles ); SCALARITERATOR pGlowAlpha; // non-interpolated values CM128AttributeIterator pSeq( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, pParticles ); CM128AttributeIterator pSeq1( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1, pParticles ); CM128AttributeIterator pCreationTimeStamp( PARTICLE_ATTRIBUTE_CREATION_TIME, pParticles ); if ( bDoColor2 ) { pGlowAlpha.Init( PARTICLE_ATTRIBUTE_GLOW_ALPHA, pParticles ); pRGB2.Init( PARTICLE_ATTRIBUTE_GLOW_RGB, pParticles ); } C4VAttributeIterator pNormal; if ( bDoNormalVector ) { pNormal.Init( PARTICLE_ATTRIBUTE_NORMAL, pParticles ); } int nParticles = pParticles->m_nActiveParticles; fltx4 fl4CurTime; fltx4 fl4MaximumCreationTimeToDraw = Four_2ToThe23s; if ( bLerpCoords ) { fl4CurTime = ReplicateX4( pParticles->m_flTargetDrawTime ); fl4MaximumCreationTimeToDraw = ReplicateX4( pParticles->m_flPrevSimTime ); } else { fl4CurTime = pParticles->m_fl4CurTime; } fltx4 Four_256s = ReplicateX4( 256.0 ); fltx4 fl4T = ReplicateX4( flInterpT ); FourVectors EyePos; EyePos.DuplicateVector( CameraPos ); FourVectors v4Fwd; if ( bCull ) v4Fwd.DuplicateVector( *pCameraFwd ); fltx4 fl4AlphaVis = ReplicateX4( pVisibilityData->m_flAlphaVisibility ); fltx4 fl4RadVis = ReplicateX4( pVisibilityData->m_flRadiusVisibility ); bool bUseVis = pVisibilityData->m_bUseVisibility; fltx4 fl4AlphaScale = ReplicateX4( 255.0 ); uint8 **pOutPtrs = reinterpret_cast( pIndexBuffer ); do { // Note: UNDER NO CIRCUMSTANCES should you write through these // pointers inside the scope of this function! pOutUnSorted is // marked as __restrict, meaning that we have promised the compiler // that it is unaliased. Therefore any writes through an alias // will cause DOOM. pOutPtrs[0] = reinterpret_cast( pOutUnSorted ); pOutPtrs[1] = reinterpret_cast( pOutUnSorted ) + sizeof( float ); pOutPtrs[2] = reinterpret_cast( pOutUnSorted ) + 2 * sizeof( float ); pOutPtrs[3] = reinterpret_cast( pOutUnSorted ) + 3 * sizeof( float ); fltx4 fl4FinalAlpha = MulSIMD( pAlpha( fl4T ), pAlpha2( fl4T ) ); fltx4 fl4FinalRadius = pRadius( fl4T ); if ( bUseVis ) { fl4FinalAlpha = MaxSIMD ( Four_Zeros, MinSIMD( Four_Ones, MulSIMD( fl4FinalAlpha, fl4AlphaVis) ) ); fl4FinalRadius = MulSIMD( fl4FinalRadius, fl4RadVis ); } // convert float 0..1 to int 0..255 fl4FinalAlpha = AddSIMD( MulSIMD( fl4FinalAlpha, fl4AlphaScale ), Four_2ToThe23s ); fltx4 fl4X = pXYZ.X( fl4T ); fltx4 fl4Y = pXYZ.Y( fl4T ); fltx4 fl4Z = pXYZ.Z( fl4T ); if ( eSortKeyMode == SORT_KEY_CREATION_TIME ) { pOutUnSorted->m_fl4SortKey = *pCreationTimeStamp; } if ( bCull || ( eSortKeyMode == SORT_KEY_DISTANCE ) ) { fltx4 Xdiff = SubSIMD( fl4X, EyePos.x ); fltx4 Ydiff = SubSIMD( fl4Y, EyePos.y ); fltx4 Zdiff = SubSIMD( fl4Z, EyePos.z ); if ( bCull ) { fltx4 dot = AddSIMD( MulSIMD( Xdiff, v4Fwd.x ), AddSIMD( MulSIMD( Ydiff, v4Fwd.y ), MulSIMD( Zdiff, v4Fwd.z ) ) ); fl4FinalAlpha = AndSIMD( fl4FinalAlpha, CmpGeSIMD( dot, Four_Zeros ) ); } if ( eSortKeyMode == SORT_KEY_DISTANCE ) { pOutUnSorted->m_fl4SortKey = AddSIMD( MulSIMD( Xdiff, Xdiff ), AddSIMD( MulSIMD( Ydiff, Ydiff ), MulSIMD( Zdiff, Zdiff ) ) ); } } fltx4 fl4Age = SubSIMD( fl4CurTime, *pCreationTimeStamp ); // if we are lerping, we need to supress particles which didn't exist on the last sim if ( bLerpCoords ) { fl4FinalAlpha = AndSIMD( fl4FinalAlpha, CmpLtSIMD( *pCreationTimeStamp, fl4MaximumCreationTimeToDraw ) ); } pOutUnSorted->m_fl4XYZ.x = fl4X; pOutUnSorted->m_fl4XYZ.y = fl4Y; pOutUnSorted->m_fl4XYZ.z = fl4Z; pOutUnSorted->m_fl4Alpha = fl4FinalAlpha; pOutUnSorted->m_fl4Red = AddSIMD( MulSIMD( pRGB.X( fl4T ), fl4AlphaScale ), Four_2ToThe23s ); pOutUnSorted->m_fl4Green = AddSIMD( MulSIMD( pRGB.Y( fl4T ), fl4AlphaScale ), Four_2ToThe23s ); pOutUnSorted->m_fl4Blue = AddSIMD( MulSIMD( pRGB.Z( fl4T ), fl4AlphaScale ), Four_2ToThe23s ); pOutUnSorted->m_fl4Radius = fl4FinalRadius; pOutUnSorted->m_fl4AnimationTimeValue = fl4Age; pOutUnSorted->m_fl4Rotation = pRot( fl4T ); pOutUnSorted->m_fl4Yaw = pYaw( fl4T ); if ( pSeq1.Stride() ) { pOutUnSorted->m_fl4SequenceID = AddSIMD( AddSIMD( *pSeq, MulSIMD( Four_256s, *pSeq1 ) ), Four_2ToThe23s ); } else { pOutUnSorted->m_fl4SequenceID = AddSIMD( *pSeq, Four_2ToThe23s ); } if ( bDoColor2 ) { pOutUnSorted->SetARGB2( pRGB2.X( fl4T ), pRGB2.Y( fl4T ), pRGB2.Z( fl4T ), *pGlowAlpha ); ++pRGB2; ++pGlowAlpha; } if ( bDoNormalVector ) { pOutUnSorted->SetNormal( pNormal.X( fl4T ), pNormal.Y( fl4T ), pNormal.Z( fl4T ) ); ++pNormal; } pOutUnSorted++; nParticles -= 4; pOutPtrs += 4; ++pXYZ; ++pAlpha; ++pAlpha2; ++pRadius; ++pRGB; ++pYaw; ++pRot; ++pSeq; ++pSeq1; ++pCreationTimeStamp; } while( nParticles > 0 ); // we're not called with 0 } template void GenerateExtendedData( void *pOutbuf, ParticleFullRenderData_Scalar_View **pIndexBuffer, Vector CameraPos, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles, float flInterpT ) { if ( bLerpCoords ) { GenerateExtendedData( pOutbuf, pIndexBuffer, CameraPos, pCameraFwd, pVisibilityData, pParticles, flInterpT ); } else { GenerateExtendedData( pOutbuf, pIndexBuffer, CameraPos, pCameraFwd, pVisibilityData, pParticles, flInterpT ); } } template void s_GenerateExtendedData( void *pOutbuf, ParticleFullRenderData_Scalar_View **pIndexBuffer, Vector CameraPos, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles, float flInterpT, bool bSort ) { if ( bSort ) { GenerateExtendedData( pOutbuf, pIndexBuffer, CameraPos, pCameraFwd, pVisibilityData, pParticles, flInterpT ); } else { GenerateExtendedData( pOutbuf, pIndexBuffer, CameraPos, pCameraFwd, pVisibilityData, pParticles, flInterpT ); } } static bool SortLessFuncExtended( ParticleFullRenderData_Scalar_View * const &left, const ParticleFullRenderData_Scalar_View * const &right ) { return left->m_nSortKey < right->m_nSortKey; } int GenerateExtendedSortedIndexList( Vector vecCamera, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles, bool bSorted, void *pOutBuf, ParticleFullRenderData_Scalar_View **pParticlePtrs ) { // check interpolation if ( pParticles->IsUsingInterpolatedRendering() ) { float t = ( pParticles->m_flTargetDrawTime - pParticles->m_flPrevSimTime ) / ( pParticles->m_flCurTime - pParticles->m_flPrevSimTime ); Assert( ( t >= 0.0 ) && ( t <= 1.0 ) ); s_GenerateExtendedData( pOutBuf, pParticlePtrs, vecCamera, NULL, pVisibilityData, pParticles, t, bSorted ); } else { s_GenerateExtendedData( pOutBuf, pParticlePtrs, vecCamera, NULL, pVisibilityData, pParticles, 0., bSorted ); } int nParticles = pParticles->m_nActiveParticles; if ( bSorted ) { // sort the output in place std::make_heap( pParticlePtrs, pParticlePtrs + nParticles, SortLessFuncExtended ); std::sort_heap( pParticlePtrs, pParticlePtrs + nParticles, SortLessFuncExtended ); } return nParticles; } int GenerateExtendedSortedIndexListWithPerParticleGlow( Vector vecCamera, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles, bool bSorted, void *pOutBuf, ParticleRenderDataWithOutlineInformation_Scalar_View **pParticlePtrs ) { // check interpolation if ( pParticles->IsUsingInterpolatedRendering() ) { float t = ( pParticles->m_flTargetDrawTime - pParticles->m_flPrevSimTime ) / ( pParticles->m_flCurTime - pParticles->m_flPrevSimTime ); Assert( ( t >= 0.0 ) && ( t <= 1.0 ) ); s_GenerateExtendedData( pOutBuf, ( ParticleFullRenderData_Scalar_View **) pParticlePtrs, vecCamera, NULL, pVisibilityData, pParticles, t, bSorted ); } else { s_GenerateExtendedData( pOutBuf, ( ParticleFullRenderData_Scalar_View **) pParticlePtrs, vecCamera, NULL, pVisibilityData, pParticles, 0., bSorted ); } int nParticles = pParticles->m_nActiveParticles; if ( bSorted ) { // sort the output in place std::make_heap( pParticlePtrs, pParticlePtrs + nParticles, SortLessFuncExtended ); std::sort_heap( pParticlePtrs, pParticlePtrs + nParticles, SortLessFuncExtended ); } return nParticles; } int GenerateExtendedSortedIndexListWithNormals( Vector vecCamera, Vector *pCameraFwd, CParticleVisibilityData *pVisibilityData, CParticleCollection *pParticles, bool bSorted, void *pOutBuf, ParticleRenderDataWithNormal_Scalar_View **pParticlePtrs ) { // check interpolation if ( pParticles->IsUsingInterpolatedRendering() ) { float t = ( pParticles->m_flTargetDrawTime - pParticles->m_flPrevSimTime ) / ( pParticles->m_flCurTime - pParticles->m_flPrevSimTime ); Assert( ( t >= 0.0 ) && ( t <= 1.0 ) ); s_GenerateExtendedData( pOutBuf, ( ParticleFullRenderData_Scalar_View **) pParticlePtrs, vecCamera, NULL, pVisibilityData, pParticles, t, bSorted ); } else { s_GenerateExtendedData( pOutBuf, ( ParticleFullRenderData_Scalar_View **) pParticlePtrs, vecCamera, NULL, pVisibilityData, pParticles, 0., bSorted ); } int nParticles = pParticles->m_nActiveParticles; if ( bSorted ) { // sort the output in place std::make_heap( pParticlePtrs, pParticlePtrs + nParticles, SortLessFuncExtended ); std::sort_heap( pParticlePtrs, pParticlePtrs + nParticles, SortLessFuncExtended ); } return nParticles; } ParticleFullRenderData_Scalar_View **GetExtendedRenderList( CParticleCollection *pParticles, IMatRenderContext *pRenderContext, bool bSorted, int *pNparticles, CParticleVisibilityData *pVisibilityData) { Assert( sizeof( ParticleFullRenderData_Scalar_View ) == sizeof( ParticleFullRenderData_SIMD_View ) ); if ( bSorted ) bSorted = pParticles->m_pDef->m_bShouldSort; Vector vecCamera; pRenderContext->GetWorldSpaceCameraPosition( &vecCamera ); int nParticles = GenerateExtendedSortedIndexList( vecCamera, NULL, pVisibilityData, pParticles, bSorted, s_SortBuffer, s_pParticlePtrs ); *pNparticles = nParticles; return s_pParticlePtrs + nParticles; } ParticleRenderDataWithOutlineInformation_Scalar_View **GetExtendedRenderListWithPerParticleGlow( CParticleCollection *pParticles, IMatRenderContext *pRenderContext, bool bSorted, int *pNparticles, CParticleVisibilityData *pVisibilityData) { Assert( sizeof( ParticleRenderDataWithOutlineInformation_Scalar_View ) == sizeof( ParticleRenderDataWithOutlineInformation_SIMD_View ) ); if ( bSorted ) bSorted = pParticles->m_pDef->m_bShouldSort; Vector vecCamera; pRenderContext->GetWorldSpaceCameraPosition( &vecCamera ); int nParticles = GenerateExtendedSortedIndexListWithPerParticleGlow( vecCamera, NULL, pVisibilityData, pParticles, bSorted, s_SortBuffer, ( ParticleRenderDataWithOutlineInformation_Scalar_View ** ) s_pParticlePtrs ); *pNparticles = nParticles; return ( ParticleRenderDataWithOutlineInformation_Scalar_View ** ) ( s_pParticlePtrs + nParticles ); } ParticleRenderDataWithNormal_Scalar_View **GetExtendedRenderListWithNormals( CParticleCollection *pParticles, IMatRenderContext *pRenderContext, bool bSorted, int *pNparticles, CParticleVisibilityData *pVisibilityData) { Assert( sizeof( ParticleRenderDataWithNormal_SIMD_View ) == sizeof( ParticleRenderDataWithNormal_Scalar_View ) ); if ( bSorted ) bSorted = pParticles->m_pDef->m_bShouldSort; Vector vecCamera; pRenderContext->GetWorldSpaceCameraPosition( &vecCamera ); int nParticles = GenerateExtendedSortedIndexListWithNormals( vecCamera, NULL, pVisibilityData, pParticles, bSorted, s_SortBuffer, ( ParticleRenderDataWithNormal_Scalar_View ** ) s_pParticlePtrs ); *pNparticles = nParticles; return ( ParticleRenderDataWithNormal_Scalar_View ** ) ( s_pParticlePtrs + nParticles ); }