W3DParticleSys.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // W3DParticleSys.cpp
  24. // W3D Particle System implementation
  25. // Author: Michael S. Booth, November 2001
  26. #include "GameClient/Color.h"
  27. #include "W3DDevice/GameClient/W3DParticleSys.h"
  28. #include "W3DDevice/GameClient/W3DAssetManager.h"
  29. #include "W3DDevice/GameClient/W3DDisplay.h"
  30. #include "W3DDevice/GameClient/heightmap.h"
  31. #include "WW3D2/Camera.h"
  32. //------------------------------------------------------------------------------ Performance Timers
  33. //#include "Common/PerfMetrics.h"
  34. //#include "Common/PerfTimer.h"
  35. //-------------------------------------------------------------------------------------------------
  36. #include "Common/QuickTrig.h"
  37. W3DParticleSystemManager::W3DParticleSystemManager()
  38. {
  39. m_pointGroup = NULL;
  40. m_streakLine = NULL;
  41. m_posBuffer = NULL;
  42. m_RGBABuffer = NULL;
  43. m_sizeBuffer = NULL;
  44. m_angleBuffer = NULL;
  45. m_readyToRender = false;
  46. m_onScreenParticleCount = 0;
  47. m_pointGroup = NEW PointGroupClass();
  48. //m_streakLine = NULL;
  49. m_streakLine = NEW StreakLineClass();
  50. m_posBuffer = NEW_REF( ShareBufferClass<Vector3>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_posBuffer") );
  51. m_RGBABuffer = NEW_REF( ShareBufferClass<Vector4>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_RGBABuffer") );
  52. m_sizeBuffer = NEW_REF( ShareBufferClass<float>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_sizeBuffer") );
  53. m_angleBuffer = NEW_REF( ShareBufferClass<uint8>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_angleBuffer") );
  54. }
  55. W3DParticleSystemManager::~W3DParticleSystemManager()
  56. {
  57. delete m_pointGroup;
  58. // W3DDisplay::m_3DScene->Remove_Render_Object( m_streakLine );
  59. if (m_streakLine)
  60. {
  61. REF_PTR_RELEASE(m_streakLine);
  62. }
  63. REF_PTR_RELEASE(m_posBuffer);
  64. REF_PTR_RELEASE(m_RGBABuffer);
  65. REF_PTR_RELEASE(m_sizeBuffer);
  66. REF_PTR_RELEASE(m_angleBuffer);
  67. }
  68. /**
  69. * Hack because DoParticles is called from Flush(), which is called
  70. * multiple times per frame. We only want to render once.
  71. * @todo Clean up the flag/Flush hack.
  72. */
  73. void W3DParticleSystemManager::queueParticleRender()
  74. {
  75. m_readyToRender = true;
  76. }
  77. /**
  78. * Nasty hack to render particles last. Called directly by WW3D::Flush()
  79. */
  80. void DoParticles( RenderInfoClass &rinfo )
  81. {
  82. if (TheParticleSystemManager)
  83. TheParticleSystemManager->doParticles(rinfo);
  84. }
  85. void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)
  86. {
  87. if (m_readyToRender == false)
  88. return;
  89. // external mechanism must tell us when it's OK to render again...
  90. m_readyToRender = false;
  91. //reset each frame
  92. /// @todo lorenzen sez: this should be debug only:
  93. m_onScreenParticleCount = 0;
  94. const FrustumClass & frustum = rinfo.Camera.Get_Frustum();
  95. AABoxClass bbox;
  96. //Get a bounding box around our visible universe. Bounded by terrain and the sky
  97. //so much tighter fitting volume than what's actually visible. This will cull
  98. //particles falling under the ground.
  99. TheTerrainRenderObject->getMaximumVisibleBox(frustum, &bbox, TRUE);
  100. //@todo lorenzen sez: put these in registers for sure
  101. Real bcX = bbox.Center.X;
  102. Real bcY = bbox.Center.Y;
  103. Real bcZ = bbox.Center.Z;
  104. Real beX = bbox.Extent.X;
  105. Real beY = bbox.Extent.Y;
  106. Real beZ = bbox.Extent.Z;
  107. unsigned int personalities[MAX_POINTS_PER_GROUP];
  108. m_fieldParticleCount = 0;
  109. ParticleSystemManager::ParticleSystemList &particleSysList = TheParticleSystemManager->getAllParticleSystems();
  110. for( ParticleSystemManager::ParticleSystemListIt it = particleSysList.begin(); it != particleSysList.end(); ++it)
  111. {
  112. ParticleSystem *sys = (*it);
  113. if (!sys) {
  114. continue;
  115. }
  116. // only look at particle/point style systems
  117. if (sys->isUsingDrawables())
  118. continue;
  119. /// @todo lorenzen sez: declare these outside the sys loop, and put some in registers
  120. // initialize them here still, of course
  121. // build W3D particle buffer
  122. Int count = 0;
  123. Vector3 *posArray = m_posBuffer->Get_Array();
  124. Real *sizeArray = m_sizeBuffer->Get_Array();
  125. Vector4 *RGBAArray = m_RGBABuffer->Get_Array();
  126. uint8 *angleArray = m_angleBuffer->Get_Array();
  127. const Coord3D *pos;
  128. const RGBColor *color;
  129. Real psize;
  130. //set-up all the per-particle
  131. for (Particle *p = sys->getFirstParticle(); p; p = p->m_systemNext)
  132. {
  133. // do not attempt to render totally invisible particles
  134. if (p->isInvisible())
  135. continue;
  136. pos = p->getPosition();
  137. psize = p->getSize();
  138. //Cull particle to edges of screen and terrain.
  139. if (WWMath::Fabs(pos->x - bcX) > (beX + psize))
  140. continue;
  141. if (WWMath::Fabs(pos->y - bcY) > (beY + psize))
  142. continue;
  143. if (WWMath::Fabs(pos->z - bcZ) > (beZ + psize))
  144. continue;
  145. m_fieldParticleCount += ( sys->getPriority() == AREA_EFFECT && sys->m_isGroundAligned != FALSE );
  146. //@todo lorenzen sez: use pointer arithmetic for these arrays
  147. personalities[count] = p->getPersonality();
  148. posArray[count].X = pos->x;
  149. posArray[count].Y = pos->y;
  150. posArray[count].Z = pos->z;
  151. sizeArray[count] = psize;
  152. color = p->getColor();
  153. RGBAArray[count].X = color->red;
  154. RGBAArray[count].Y = color->green;
  155. RGBAArray[count].Z = color->blue;
  156. RGBAArray[count].W = p->getAlpha();
  157. angleArray[count] = (uint8)(p->getAngle() * 255.0f / (2.0f * PI));
  158. if (++count == MAX_POINTS_PER_GROUP)
  159. break;
  160. }
  161. if ( count == 0 )
  162. continue; //this system has no particles to render
  163. TextureClass *texture = W3DDisplay::m_assetManager->Get_Texture( sys->getParticleTypeName().str() );
  164. if ( m_streakLine && sys->isUsingStreak() && (count >= 2) )
  165. {
  166. m_streakLine->Reset_Line();
  167. m_streakLine->Set_Texture( texture );
  168. texture->Release_Ref();//release reference since it's held by streakline
  169. switch( sys->getShaderType() )
  170. {
  171. case ParticleSystemInfo::ADDITIVE:
  172. m_streakLine->Set_Shader( ShaderClass::_PresetAdditiveSpriteShader );
  173. break;
  174. case ParticleSystemInfo::ALPHA:
  175. m_streakLine->Set_Shader( ShaderClass::_PresetAlphaSpriteShader );
  176. break;
  177. case ParticleSystemInfo::ALPHA_TEST:
  178. m_streakLine->Set_Shader( ShaderClass::_PresetATestSpriteShader );
  179. break;
  180. case ParticleSystemInfo::MULTIPLY:
  181. m_streakLine->Set_Shader( ShaderClass::_PresetMultiplicativeSpriteShader );
  182. break;
  183. }
  184. //UPDATE THE STREAK'S ARRAYS
  185. m_streakLine->Set_LocsWidthsColors(
  186. count,
  187. m_posBuffer->Get_Array(),
  188. m_sizeBuffer->Get_Array(),
  189. m_RGBABuffer->Get_Array(),
  190. &personalities[0]
  191. );
  192. //WWASSERT( m_streakLine->Get_Num_Points() == count );
  193. // This is the happy place for this!
  194. RGBAArray[0].X = 0;//eliminates the scissor edge on the trailing edge of the streak
  195. RGBAArray[0].Y = 0;
  196. RGBAArray[0].Z = 0;
  197. RGBAArray[0].W = 0;
  198. //RENDER STREAK!
  199. m_streakLine->Render( rinfo );
  200. }
  201. else
  202. {
  203. WWASSERT( m_pointGroup );
  204. if ( m_pointGroup ) // this catches the particle and volumeparticle cases
  205. {
  206. // render all the systems' particles
  207. m_pointGroup->Set_Texture( texture );
  208. texture->Release_Ref();//release reference since it's held by pointGroup
  209. m_pointGroup->Set_Flag( PointGroupClass::TRANSFORM, true ); // transform to screen space
  210. switch( sys->getShaderType() )
  211. {
  212. case ParticleSystemInfo::ADDITIVE:
  213. m_pointGroup->Set_Shader( ShaderClass::_PresetAdditiveSpriteShader );
  214. break;
  215. case ParticleSystemInfo::ALPHA:
  216. m_pointGroup->Set_Shader( ShaderClass::_PresetAlphaSpriteShader );
  217. break;
  218. case ParticleSystemInfo::ALPHA_TEST:
  219. m_pointGroup->Set_Shader( ShaderClass::_PresetATestSpriteShader );
  220. break;
  221. case ParticleSystemInfo::MULTIPLY:
  222. m_pointGroup->Set_Shader( ShaderClass::_PresetMultiplicativeSpriteShader );
  223. break;
  224. }
  225. /// @todo Use both QUADS and TRIS for particles
  226. m_pointGroup->Set_Point_Mode( PointGroupClass::QUADS );
  227. m_pointGroup->Set_Arrays( m_posBuffer, m_RGBABuffer, NULL, m_sizeBuffer, m_angleBuffer, NULL, count );
  228. m_pointGroup->Set_Billboard(sys->shouldBillboard());
  229. /// @todo Support animated texture particles
  230. /// @todo lorenzen sez: unimplemented code wastes cpu cycles
  231. m_pointGroup->Set_Point_Frame( 0 );
  232. //RENDER IT!
  233. if( sys->getVolumeParticleDepth() > 1 )
  234. {
  235. m_pointGroup->RenderVolumeParticle( rinfo, sys->getVolumeParticleDepth() );
  236. }
  237. else
  238. m_pointGroup->Render( rinfo );
  239. }
  240. }
  241. /// @todo lorenzen sez: this should be debug only:
  242. //add particle count to total
  243. m_onScreenParticleCount += count;
  244. /*
  245. // draw the wind vector for this particle system on the screen
  246. UnsignedInt width = TheDisplay->getWidth();
  247. UnsignedInt height = TheDisplay->getHeight();
  248. Coord3D worldStart, worldEnd;
  249. ICoord2D pixelStart, pixelEnd;
  250. sys->getPosition( &worldStart );
  251. worldEnd.x = Cos( sys->getWindAngle() ) * 50.0f + worldStart.x;
  252. worldEnd.y = Sin( sys->getWindAngle() ) * 50.0f + worldStart.y;
  253. worldEnd.z = worldStart.z;
  254. TheTacticalView->worldToScreen( &worldStart, &pixelStart );
  255. TheTacticalView->worldToScreen( &worldEnd, &pixelEnd );
  256. Color colorStart = GameMakeColor( 255, 255, 255, 255 );
  257. Color colorEnd = GameMakeColor( 255, 128, 128, 255 );
  258. TheDisplay->drawLine( pixelStart.x, pixelStart.y, pixelEnd.x, pixelEnd.y, 1.0f, colorStart, colorEnd );
  259. */
  260. }// next system
  261. /// @todo lorenzen sez: this should be debug only:
  262. TheParticleSystemManager->setOnScreenParticleCount(m_onScreenParticleCount);
  263. }