W3DParticleSys.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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 "common/GlobalData.h"
  27. #include "GameClient/Color.h"
  28. #include "W3DDevice/GameClient/W3DParticleSys.h"
  29. #include "W3DDevice/GameClient/W3DAssetManager.h"
  30. #include "W3DDevice/GameClient/W3DDisplay.h"
  31. #include "W3DDevice/GameClient/heightmap.h"
  32. #include "W3DDevice/GameClient/W3DSmudge.h"
  33. #include "W3DDevice/GameClient/W3DSnow.h"
  34. #include "WW3D2/Camera.h"
  35. #ifdef _INTERNAL
  36. // for occasional debugging...
  37. //#pragma optimize("", off)
  38. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  39. #endif
  40. //------------------------------------------------------------------------------ Performance Timers
  41. //#include "Common/PerfMetrics.h"
  42. //#include "Common/PerfTimer.h"
  43. //-------------------------------------------------------------------------------------------------
  44. #include "Common/QuickTrig.h"
  45. W3DParticleSystemManager::W3DParticleSystemManager()
  46. {
  47. m_pointGroup = NULL;
  48. m_streakLine = NULL;
  49. m_posBuffer = NULL;
  50. m_RGBABuffer = NULL;
  51. m_sizeBuffer = NULL;
  52. m_angleBuffer = NULL;
  53. m_readyToRender = false;
  54. m_onScreenParticleCount = 0;
  55. m_pointGroup = NEW PointGroupClass();
  56. //m_streakLine = NULL;
  57. m_streakLine = NEW StreakLineClass();
  58. m_posBuffer = NEW_REF( ShareBufferClass<Vector3>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_posBuffer") );
  59. m_RGBABuffer = NEW_REF( ShareBufferClass<Vector4>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_RGBABuffer") );
  60. m_sizeBuffer = NEW_REF( ShareBufferClass<float>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_sizeBuffer") );
  61. m_angleBuffer = NEW_REF( ShareBufferClass<uint8>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_angleBuffer") );
  62. }
  63. W3DParticleSystemManager::~W3DParticleSystemManager()
  64. {
  65. delete m_pointGroup;
  66. // W3DDisplay::m_3DScene->Remove_Render_Object( m_streakLine );
  67. if (m_streakLine)
  68. {
  69. REF_PTR_RELEASE(m_streakLine);
  70. }
  71. REF_PTR_RELEASE(m_posBuffer);
  72. REF_PTR_RELEASE(m_RGBABuffer);
  73. REF_PTR_RELEASE(m_sizeBuffer);
  74. REF_PTR_RELEASE(m_angleBuffer);
  75. }
  76. /**
  77. * Hack because DoParticles is called from Flush(), which is called
  78. * multiple times per frame. We only want to render once.
  79. * @todo Clean up the flag/Flush hack.
  80. */
  81. void W3DParticleSystemManager::queueParticleRender()
  82. {
  83. m_readyToRender = true;
  84. }
  85. /**
  86. * Nasty hack to render particles last. Called directly by WW3D::Flush()
  87. */
  88. void DoParticles( RenderInfoClass &rinfo )
  89. {
  90. if (TheParticleSystemManager)
  91. TheParticleSystemManager->doParticles(rinfo);
  92. }
  93. void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)
  94. {
  95. if (m_readyToRender == false)
  96. return;
  97. // external mechanism must tell us when it's OK to render again...
  98. m_readyToRender = false;
  99. //reset each frame
  100. /// @todo lorenzen sez: this should be debug only:
  101. m_onScreenParticleCount = 0;
  102. Int visibleSmudgeCount = 0;
  103. if (TheSmudgeManager)
  104. TheSmudgeManager->setSmudgeCountLastFrame(0); //keep track of visible smudges
  105. const FrustumClass & frustum = rinfo.Camera.Get_Frustum();
  106. AABoxClass bbox;
  107. //Get a bounding box around our visible universe. Bounded by terrain and the sky
  108. //so much tighter fitting volume than what's actually visible. This will cull
  109. //particles falling under the ground.
  110. TheTerrainRenderObject->getMaximumVisibleBox(frustum, &bbox, TRUE);
  111. //@todo lorenzen sez: put these in registers for sure
  112. Real bcX = bbox.Center.X;
  113. Real bcY = bbox.Center.Y;
  114. Real bcZ = bbox.Center.Z;
  115. Real beX = bbox.Extent.X;
  116. Real beY = bbox.Extent.Y;
  117. Real beZ = bbox.Extent.Z;
  118. unsigned int personalities[MAX_POINTS_PER_GROUP];
  119. m_fieldParticleCount = 0;
  120. SmudgeSet *set=NULL;
  121. if (TheSmudgeManager)
  122. set=TheSmudgeManager->addSmudgeSet(); //global smudge set through which all smudges are rendered.
  123. ParticleSystemManager::ParticleSystemList &particleSysList = TheParticleSystemManager->getAllParticleSystems();
  124. for( ParticleSystemManager::ParticleSystemListIt it = particleSysList.begin(); it != particleSysList.end(); ++it)
  125. {
  126. ParticleSystem *sys = (*it);
  127. if (!sys) {
  128. continue;
  129. }
  130. // only look at particle/point style systems
  131. if (sys->isUsingDrawables())
  132. continue;
  133. //temporary hack that checks if texture name starts with "SMUD" - if so, we can assume it's a smudge type
  134. if (/*sys->isUsingSmudge()*/ *((DWORD *)sys->getParticleTypeName().str()) == 0x44554D53)
  135. {
  136. if (TheSmudgeManager && ((W3DSmudgeManager*)TheSmudgeManager)->getHardwareSupport() && TheGlobalData->m_useHeatEffects)
  137. {
  138. //set-up all the per-particle
  139. for (Particle *p = sys->getFirstParticle(); p; p = p->m_systemNext)
  140. {
  141. const Coord3D *pos = p->getPosition();
  142. Real psize = p->getSize();
  143. //Cull particle to edges of screen and terrain.
  144. if (WWMath::Fabs( pos->x - bcX ) > ( beX + psize ) )
  145. continue;
  146. if (WWMath::Fabs( pos->y - bcY ) > ( beY + psize ) )
  147. continue;
  148. if (WWMath::Fabs( pos->z - bcZ ) > ( beZ + psize ) )
  149. continue;
  150. Smudge *smudge = set->addSmudgeToSet();
  151. smudge->m_pos.Set( pos->x, pos->y, pos->z );
  152. smudge->m_offset.Set( GameClientRandomValueReal(-0.06f,0.06f), GameClientRandomValueReal(-0.03f,0.03f) );
  153. smudge->m_size = psize;
  154. smudge->m_opacity = p->getAlpha();
  155. visibleSmudgeCount++;
  156. }
  157. }
  158. continue;
  159. }
  160. /// @todo lorenzen sez: declare these outside the sys loop, and put some in registers
  161. // initialize them here still, of course
  162. // build W3D particle buffer
  163. Int count = 0;
  164. Vector3 *posArray = m_posBuffer->Get_Array();
  165. Real *sizeArray = m_sizeBuffer->Get_Array();
  166. Vector4 *RGBAArray = m_RGBABuffer->Get_Array();
  167. uint8 *angleArray = m_angleBuffer->Get_Array();
  168. const Coord3D *pos;
  169. const RGBColor *color;
  170. Real psize;
  171. //set-up all the per-particle
  172. for (Particle *p = sys->getFirstParticle(); p; p = p->m_systemNext)
  173. {
  174. pos = p->getPosition();
  175. psize = p->getSize();
  176. //Cull particle to edges of screen and terrain.
  177. if (WWMath::Fabs(pos->x - bcX) > (beX + psize))
  178. continue;
  179. if (WWMath::Fabs(pos->y - bcY) > (beY + psize))
  180. continue;
  181. if (WWMath::Fabs(pos->z - bcZ) > (beZ + psize))
  182. continue;
  183. m_fieldParticleCount += ( sys->getPriority() == AREA_EFFECT && sys->m_isGroundAligned != FALSE );
  184. //@todo lorenzen sez: use pointer arithmetic for these arrays
  185. personalities[count] = p->getPersonality();
  186. posArray[count].X = pos->x;
  187. posArray[count].Y = pos->y;
  188. posArray[count].Z = pos->z;
  189. sizeArray[count] = psize;
  190. color = p->getColor();
  191. RGBAArray[count].X = color->red;
  192. RGBAArray[count].Y = color->green;
  193. RGBAArray[count].Z = color->blue;
  194. RGBAArray[count].W = p->getAlpha();
  195. angleArray[count] = (uint8)(p->getAngle() * 255.0f / (2.0f * PI));
  196. if (++count == MAX_POINTS_PER_GROUP)
  197. break;
  198. }
  199. if ( count == 0 )
  200. continue; //this system has no particles to render
  201. TextureClass *texture = W3DDisplay::m_assetManager->Get_Texture( sys->getParticleTypeName().str() );
  202. if ( m_streakLine && sys->isUsingStreak() && (count >= 2) )
  203. {
  204. m_streakLine->Reset_Line();
  205. m_streakLine->Set_Texture( texture );
  206. texture->Release_Ref();//release reference since it's held by streakline
  207. switch( sys->getShaderType() )
  208. {
  209. case ParticleSystemInfo::ADDITIVE:
  210. m_streakLine->Set_Shader( ShaderClass::_PresetAdditiveSpriteShader );
  211. break;
  212. case ParticleSystemInfo::ALPHA:
  213. m_streakLine->Set_Shader( ShaderClass::_PresetAlphaSpriteShader );
  214. break;
  215. case ParticleSystemInfo::ALPHA_TEST:
  216. m_streakLine->Set_Shader( ShaderClass::_PresetATestSpriteShader );
  217. break;
  218. case ParticleSystemInfo::MULTIPLY:
  219. m_streakLine->Set_Shader( ShaderClass::_PresetMultiplicativeSpriteShader );
  220. break;
  221. }
  222. //UPDATE THE STREAK'S ARRAYS
  223. m_streakLine->Set_LocsWidthsColors(
  224. count,
  225. m_posBuffer->Get_Array(),
  226. m_sizeBuffer->Get_Array(),
  227. m_RGBABuffer->Get_Array(),
  228. &personalities[0]
  229. );
  230. //WWASSERT( m_streakLine->Get_Num_Points() == count );
  231. // This is the happy place for this!
  232. RGBAArray[0].X = 0;//eliminates the scissor edge on the trailing edge of the streak
  233. RGBAArray[0].Y = 0;
  234. RGBAArray[0].Z = 0;
  235. RGBAArray[0].W = 0;
  236. //RENDER STREAK!
  237. m_streakLine->Render( rinfo );
  238. }
  239. else
  240. {
  241. WWASSERT( m_pointGroup );
  242. if ( m_pointGroup ) // this catches the particle and volumeparticle cases
  243. {
  244. // render all the systems' particles
  245. m_pointGroup->Set_Texture( texture );
  246. texture->Release_Ref();//release reference since it's held by pointGroup
  247. m_pointGroup->Set_Flag( PointGroupClass::TRANSFORM, true ); // transform to screen space
  248. switch( sys->getShaderType() )
  249. {
  250. case ParticleSystemInfo::ADDITIVE:
  251. m_pointGroup->Set_Shader( ShaderClass::_PresetAdditiveSpriteShader );
  252. break;
  253. case ParticleSystemInfo::ALPHA:
  254. m_pointGroup->Set_Shader( ShaderClass::_PresetAlphaSpriteShader );
  255. break;
  256. case ParticleSystemInfo::ALPHA_TEST:
  257. m_pointGroup->Set_Shader( ShaderClass::_PresetATestSpriteShader );
  258. break;
  259. case ParticleSystemInfo::MULTIPLY:
  260. m_pointGroup->Set_Shader( ShaderClass::_PresetMultiplicativeSpriteShader );
  261. break;
  262. }
  263. /// @todo Use both QUADS and TRIS for particles
  264. m_pointGroup->Set_Point_Mode( PointGroupClass::QUADS );
  265. m_pointGroup->Set_Arrays( m_posBuffer, m_RGBABuffer, NULL, m_sizeBuffer, m_angleBuffer, NULL, count );
  266. m_pointGroup->Set_Billboard(sys->shouldBillboard());
  267. /// @todo Support animated texture particles
  268. /// @todo lorenzen sez: unimplemented code wastes cpu cycles
  269. m_pointGroup->Set_Point_Frame( 0 );
  270. //RENDER IT!
  271. if( sys->getVolumeParticleDepth() > 1 )
  272. {
  273. m_pointGroup->RenderVolumeParticle( rinfo, sys->getVolumeParticleDepth() );
  274. }
  275. else
  276. m_pointGroup->Render( rinfo );
  277. }
  278. }
  279. /// @todo lorenzen sez: this should be debug only:
  280. //add particle count to total
  281. m_onScreenParticleCount += count;
  282. /*
  283. // draw the wind vector for this particle system on the screen
  284. UnsignedInt width = TheDisplay->getWidth();
  285. UnsignedInt height = TheDisplay->getHeight();
  286. Coord3D worldStart, worldEnd;
  287. ICoord2D pixelStart, pixelEnd;
  288. sys->getPosition( &worldStart );
  289. worldEnd.x = Cos( sys->getWindAngle() ) * 50.0f + worldStart.x;
  290. worldEnd.y = Sin( sys->getWindAngle() ) * 50.0f + worldStart.y;
  291. worldEnd.z = worldStart.z;
  292. TheTacticalView->worldToScreen( &worldStart, &pixelStart );
  293. TheTacticalView->worldToScreen( &worldEnd, &pixelEnd );
  294. Color colorStart = GameMakeColor( 255, 255, 255, 255 );
  295. Color colorEnd = GameMakeColor( 255, 128, 128, 255 );
  296. TheDisplay->drawLine( pixelStart.x, pixelStart.y, pixelEnd.x, pixelEnd.y, 1.0f, colorStart, colorEnd );
  297. */
  298. }// next system
  299. /// @todo lorenzen sez: this should be debug only:
  300. TheParticleSystemManager->setOnScreenParticleCount(m_onScreenParticleCount);
  301. //Draw any particles belonging to weather effects
  302. if (TheSnowManager)
  303. ((W3DSnowManager *)TheSnowManager)->render(rinfo);
  304. //Now process screen smudges which are particles that distort the background behind them.
  305. if(TheSmudgeManager)
  306. {
  307. ((W3DSmudgeManager *)TheSmudgeManager)->render(rinfo);
  308. TheSmudgeManager->reset(); //clear all the smudges after rendering since we fill again each frame.
  309. TheSmudgeManager->setSmudgeCountLastFrame(visibleSmudgeCount);
  310. }
  311. }