WeatherMgr.cpp 108 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378
  1. /*
  2. ** Command & Conquer Renegade(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. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/WeatherMgr.cpp $*
  25. * *
  26. * Author:: Ian Leslie *
  27. * *
  28. * $Modtime:: 1/02/02 1:26p $*
  29. * *
  30. * $Revision:: 27 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. // Includes.
  36. #include "weathermgr.h"
  37. #include "apppackettypes.h"
  38. #include "assetmgr.h"
  39. #include "audiblesound.h"
  40. #include "camera.h"
  41. #include "chunkio.h"
  42. #include "combat.h"
  43. #include "gameobjmanager.h"
  44. #include "gametype.h"
  45. #include "light.h"
  46. #include "dx8indexbuffer.h"
  47. #include "dx8wrapper.h"
  48. #include "phys.h"
  49. #include "physcoltest.h"
  50. #include "pscene.h"
  51. #include "rinfo.h"
  52. #include "scene.h"
  53. #include "sortingrenderer.h"
  54. #include "soundenvironment.h"
  55. #include "wwaudio.h"
  56. #include "wwmemlog.h"
  57. // Singletons.
  58. WeatherMgrClass _TheWeatherMgr;
  59. // Static data.
  60. DEFINE_AUTO_POOL(WeatherSystemClass::RayStruct, WeatherSystemClass::GROWTH_STEP);
  61. DEFINE_AUTO_POOL(WeatherSystemClass::ParticleStruct, WeatherSystemClass::GROWTH_STEP);
  62. Random2Class WeatherSystemClass::_RandomNumber (0x60486223);
  63. unsigned WeatherSystemClass::_GlobalParticleCount = 0;
  64. SoundEnvironmentClass *WeatherMgrClass::_SoundEnvironment;
  65. WeatherParameterClass WeatherMgrClass::_Parameters [PARAMETER_COUNT];
  66. bool WeatherMgrClass::_Prime;
  67. bool WeatherMgrClass::_Imported;
  68. unsigned WeatherMgrClass::_WindOverrideCount;
  69. unsigned WeatherMgrClass::_PrecipitationOverrideCount;
  70. WindClass *WeatherMgrClass::_Wind;
  71. WeatherSystemClass *WeatherMgrClass::_Precipitation [PRECIPITATION_COUNT];
  72. bool WeatherMgrClass::_FogEnabled;
  73. bool WeatherMgrClass::_Dirty;
  74. /***********************************************************************************************
  75. * WindClass::WindClass -- *
  76. * *
  77. * INPUT: *
  78. * *
  79. * OUTPUT: *
  80. * *
  81. * WARNINGS: *
  82. * *
  83. * HISTORY: *
  84. * 04/09/01 IML : Created. *
  85. *=============================================================================================*/
  86. WindClass::WindClass (float heading, float speed, float variability, SoundEnvironmentClass *soundenvironment)
  87. : Velocity (0.0f, 0.0f),
  88. SoundEnvironment (soundenvironment)
  89. {
  90. const char *windsamplename = "Wind01";
  91. Set (heading, speed, variability);
  92. for (unsigned octave = 0; octave < OCTAVE_COUNT; octave++) {
  93. Theta [octave] = 0.0l;
  94. }
  95. // Create the wind sound effect.
  96. Sound = WWAudioClass::Get_Instance()->Create_Sound (windsamplename, NULL, 0, CLASSID_2D);
  97. if (Sound != NULL) {
  98. SoundEnvironment->Add_User();
  99. Sound->Set_Volume (0.0f);
  100. Sound->Play();
  101. }
  102. }
  103. /***********************************************************************************************
  104. * WindClass::~WindClass -- *
  105. * *
  106. * INPUT: *
  107. * *
  108. * OUTPUT: *
  109. * *
  110. * WARNINGS: *
  111. * *
  112. * HISTORY: *
  113. * 04/09/01 IML : Created. *
  114. *=============================================================================================*/
  115. WindClass::~WindClass()
  116. {
  117. // Remove wind sound effect.
  118. if (Sound != NULL) {
  119. Sound->Stop();
  120. REF_PTR_RELEASE (Sound);
  121. SoundEnvironment->Remove_User();
  122. }
  123. }
  124. /***********************************************************************************************
  125. * WindClass::Set -- *
  126. * *
  127. * INPUT: *
  128. * *
  129. * OUTPUT: *
  130. * *
  131. * WARNINGS: *
  132. * *
  133. * HISTORY: *
  134. * 04/09/01 IML : Created. *
  135. *=============================================================================================*/
  136. void WindClass::Set (float heading, float speed, float variability)
  137. {
  138. WWASSERT (speed >= 0.0f);
  139. Heading = DEG_TO_RADF (heading);
  140. Speed = speed;
  141. Variability = variability;
  142. }
  143. /***********************************************************************************************
  144. * WindClass::Update -- *
  145. * *
  146. * INPUT: *
  147. * *
  148. * OUTPUT: *
  149. * *
  150. * WARNINGS: *
  151. * *
  152. * HISTORY: *
  153. * 04/09/01 IML : Created. *
  154. *=============================================================================================*/
  155. bool WindClass::Update()
  156. {
  157. const double frequency [OCTAVE_COUNT] = {0.5l, 0.2l};
  158. const double twopi = WWMATH_PI * 2.0l;
  159. float h, speed;
  160. if (Variability > 0.0f) {
  161. float f = 0.0f;
  162. int d;
  163. for (unsigned octave = 0; octave < OCTAVE_COUNT; octave++) {
  164. Theta [octave] += WW3D::Get_Frame_Time() * 0.001l * frequency [octave];
  165. d = floorf (Theta [octave] / twopi);
  166. if (d >= 1) Theta [octave] -= d * twopi;
  167. f += sinf (Theta [octave]);
  168. }
  169. speed = Speed - (Speed * ((f + 1.0f) * 0.5f) * Variability);
  170. } else {
  171. speed = Speed;
  172. }
  173. h = Heading + (0.5f * WWMATH_PI);
  174. Velocity.Set (cosf (h) * speed, sinf (h) * speed);
  175. // Update wind sound effect.
  176. if (Sound != NULL) {
  177. const float maxvolumespeed = 10.0f;
  178. float attenuation;
  179. // Precalculate volume attenuation based on speed.
  180. attenuation = (0.5f * (MIN (Speed, maxvolumespeed) + MIN (speed, maxvolumespeed))) / maxvolumespeed;
  181. Sound->Set_Volume (SoundEnvironment->Get_Amplitude() * attenuation);
  182. }
  183. // Is the wind essentially idle (ie. it is not contributing to the scene visually or audibly)?
  184. return (Speed > 0.0f);
  185. }
  186. /***********************************************************************************************
  187. * WeatherSystemClass::WeatherSystemClass -- *
  188. * *
  189. * INPUT: *
  190. * *
  191. * OUTPUT: *
  192. * *
  193. * WARNINGS: *
  194. * *
  195. * HISTORY: *
  196. * 03/06/01 IML : Created. *
  197. *=============================================================================================*/
  198. WeatherSystemClass::WeatherSystemClass (PhysicsSceneClass *scene,
  199. float emittersize,
  200. float emitterheight,
  201. float particledensity,
  202. float particlesperunitlength,
  203. float particlewidth,
  204. float particleheight,
  205. float particlespeed,
  206. const Vector2 &pageoffset,
  207. const Vector2 &pagesize,
  208. unsigned pagecount,
  209. bool staticpageexists,
  210. float minstatictime,
  211. float maxstatictime,
  212. RenderModeEnum rendermode,
  213. bool decayaftercollision,
  214. bool prime)
  215. : Scene (scene),
  216. EmitterSize (emittersize),
  217. EmitterHeight (emitterheight),
  218. EmitterPosition (Vector3 (0.0f, 0.0f, 0.0f)),
  219. ParticlesPerUnitLength (particlesperunitlength),
  220. ParticleSpeed (particlespeed),
  221. HalfParticleWidth (particlewidth * 0.5f),
  222. HalfParticleHeight (particleheight * 0.5f),
  223. RayHead (NULL),
  224. RayCount (0),
  225. RaySpawnPtr (NULL),
  226. RayUpdatePtr (NULL),
  227. ParticleHead (NULL),
  228. ParticleCount (0),
  229. MinRayEndZ (FLT_MAX),
  230. SpawnCountFraction (0.0f),
  231. PageCount (pagecount),
  232. StaticPageExists (staticpageexists),
  233. MinStaticTime (minstatictime),
  234. MaxStaticTime (maxstatictime),
  235. RenderMode (rendermode),
  236. DecayAfterCollision (decayaftercollision),
  237. CameraPositionValid (false)
  238. {
  239. const char *texturename = "WeatherParticles.tga";
  240. const TextureClass::MipCountType mipcount = TextureClass::MIP_LEVELS_5;
  241. const float oopagecount = 1.0f / pagecount;
  242. WWASSERT (particlespeed >= 0.0f);
  243. // How old is the weather system?
  244. Age = prime ? MAX_AGE : 0.0f;
  245. // Set density of system.
  246. Set_Density (particledensity);
  247. // Get the scene's bounding box.
  248. scene->Get_Level_Extents (SceneMin, SceneMax);
  249. // Expand the bounding box by a small amount so that we can distinguish between collisions with geometry and
  250. // collisions with the bounding box.
  251. SceneMin.Z -= 1.0f;
  252. SceneMax.Z += 1.0f;
  253. // Initialize an index buffer.
  254. #if WEATHER_PARTICLE_SORT
  255. IndexBuffer = NEW_REF (SortingIndexBufferClass, (MAX_IB_PARTICLE_COUNT * VERTICES_PER_TRIANGLE));
  256. #else
  257. IndexBuffer = NEW_REF (DX8IndexBufferClass, (MAX_IB_PARTICLE_COUNT * VERTICES_PER_TRIANGLE));
  258. #endif
  259. {
  260. SortingIndexBufferClass::WriteLockClass lock (IndexBuffer);
  261. unsigned short *indices = lock.Get_Index_Array();
  262. for (unsigned i = 0; i < MAX_IB_PARTICLE_COUNT * VERTICES_PER_TRIANGLE; i++) {
  263. *indices++ = i;
  264. }
  265. }
  266. // Configure material.
  267. Material = VertexMaterialClass::Get_Preset (VertexMaterialClass::PRELIT_NODIFFUSE);
  268. // Configure shader.
  269. Shader = ShaderClass::_PresetAlphaShader;
  270. Shader.Set_Primary_Gradient (ShaderClass::GRADIENT_MODULATE);
  271. Shader.Set_Cull_Mode (ShaderClass::CULL_MODE_DISABLE);
  272. Shader.Enable_Fog ("WeatherSystemClass");
  273. // Configure texture.
  274. Texture = WW3DAssetManager::Get_Instance()->Get_Texture (texturename, mipcount);
  275. Texture->Set_U_Addr_Mode (TextureClass::TEXTURE_ADDRESS_CLAMP);
  276. Texture->Set_V_Addr_Mode (TextureClass::TEXTURE_ADDRESS_CLAMP);
  277. Set_Translucent (true);
  278. // Configure texture coordinates according to given page count.
  279. // NOTE: Split the texture into vertical pages.
  280. WWASSERT (pagecount > 0);
  281. TextureArray = new Vector2 [pagecount * VERTICES_PER_TRIANGLE];
  282. WWASSERT (TextureArray != NULL);
  283. for (unsigned page = 0; page < pagecount; page++) {
  284. TextureArray [page * VERTICES_PER_TRIANGLE + 0].Set (pageoffset.U + (((page + 0.5f) * oopagecount) * pagesize.U), pageoffset.V + 0.0f);
  285. TextureArray [page * VERTICES_PER_TRIANGLE + 1].Set (pageoffset.U + (((page + 1.0f) * oopagecount) * pagesize.U), pageoffset.V + pagesize.V);
  286. TextureArray [page * VERTICES_PER_TRIANGLE + 2].Set (pageoffset.U + (((page + 0.0f) * oopagecount) * pagesize.U), pageoffset.V + pagesize.V);
  287. }
  288. }
  289. /***********************************************************************************************
  290. * WeatherSystemClass::WeatherSystemClass -- *
  291. * *
  292. * INPUT: *
  293. * *
  294. * OUTPUT: *
  295. * *
  296. * WARNINGS: *
  297. * *
  298. * HISTORY: *
  299. * 03/06/01 IML : Created. *
  300. *=============================================================================================*/
  301. WeatherSystemClass::~WeatherSystemClass()
  302. {
  303. RayStruct *rayptr;
  304. ParticleStruct *particleptr;
  305. // Clean-up rays.
  306. rayptr = RayHead;
  307. while (rayptr != NULL) {
  308. RayStruct *nextrayptr;
  309. nextrayptr = rayptr->Next;
  310. delete rayptr;
  311. rayptr = nextrayptr;
  312. RayCount--;
  313. }
  314. WWASSERT (RayCount == 0);
  315. // Clean-up particles.
  316. particleptr = ParticleHead;
  317. while (particleptr != NULL) {
  318. ParticleStruct *nextparticleptr = particleptr->Next;
  319. Kill (particleptr);
  320. particleptr = nextparticleptr;
  321. }
  322. WWASSERT (ParticleCount == 0);
  323. REF_PTR_RELEASE (Material);
  324. delete [] TextureArray;
  325. REF_PTR_RELEASE (Texture);
  326. REF_PTR_RELEASE (IndexBuffer);
  327. }
  328. /***********************************************************************************************
  329. * WeatherSystemClass::Set_Density -- *
  330. * *
  331. * INPUT: *
  332. * *
  333. * OUTPUT: *
  334. * *
  335. * WARNINGS: *
  336. * *
  337. * HISTORY: *
  338. * 03/06/01 IML : Created. *
  339. *=============================================================================================*/
  340. void WeatherSystemClass::Set_Density (float density)
  341. {
  342. unsigned raycount;
  343. int signedcount, r;
  344. RayStruct *rayptr;
  345. // Calculate no. of rays required.
  346. ParticleDensity = density;
  347. raycount = Spawn_Count (1.0f) / (ParticlesPerUnitLength * ParticleSpeed);
  348. // Is the ray count increasing or decreasing in size?
  349. signedcount = ((int) RayCount) - ((int) raycount);
  350. if (signedcount < 0) {
  351. // Add new, uninitialized rays to head of list.
  352. for (r = signedcount; r < 0; r++) {
  353. rayptr = new RayStruct;
  354. WWASSERT (rayptr != NULL);
  355. rayptr->Next = RayHead;
  356. rayptr->Initialized = false;
  357. RayHead = rayptr;
  358. }
  359. // If necessary, initialize the spawn and update pointers.
  360. if (RaySpawnPtr == NULL) RaySpawnPtr = RayHead;
  361. if (RayUpdatePtr == NULL) RayUpdatePtr = RayHead;
  362. } else {
  363. // Remove rays from head of list.
  364. rayptr = RayHead;
  365. for (r = 0; r < signedcount; r++) {
  366. RayStruct *nextrayptr;
  367. if (rayptr == NULL) break;
  368. nextrayptr = rayptr->Next;
  369. // If this ray is referenced by the spawn or update pointers then change them.
  370. if (RaySpawnPtr == rayptr) RaySpawnPtr = nextrayptr;
  371. if (RayUpdatePtr == rayptr) RayUpdatePtr = nextrayptr;
  372. delete rayptr;
  373. rayptr = nextrayptr;
  374. }
  375. RayHead = rayptr;
  376. }
  377. RayCount = raycount;
  378. }
  379. /***********************************************************************************************
  380. * WeatherSystemClass::Update -- *
  381. * *
  382. * INPUT: *
  383. * *
  384. * OUTPUT: *
  385. * *
  386. * WARNINGS: *
  387. * *
  388. * HISTORY: *
  389. * 03/06/01 IML : Created. *
  390. *=============================================================================================*/
  391. bool WeatherSystemClass::Update (WindClass *wind, const Vector3 &cameraposition)
  392. {
  393. struct BoundingBoxStruct {
  394. Vector2 Min, Max;
  395. };
  396. const unsigned randomness = 10000;
  397. const float oorandomness = 1.0f / randomness;
  398. const float overlapdelta = EmitterSize * 2.0f;
  399. const unsigned rayupdatecount = MAX (RayCount * 0.018f, 1);
  400. Vector3 oldemitterposition;
  401. float ooz;
  402. Vector3 emitterdirection, emitteroffset;
  403. Vector3 projectedemitterposition;
  404. BoundingBoxStruct emitterbounds;
  405. float deltax, deltay;
  406. BoundingBoxStruct nonoverlapregions [2];
  407. float regionthreshold;
  408. Vector3 sceneminoffset, scenemaxoffset;
  409. Vector2 raystartoffset;
  410. RayStruct *rayptr;
  411. float spawncountfraction;
  412. float alpha, beta;
  413. Vector2 range;
  414. Vector3 minrayendposition;
  415. float l, boxoffset;
  416. ParticleStruct *particleptr;
  417. float s;
  418. unsigned spawncount;
  419. float time;
  420. oldemitterposition = EmitterPosition;
  421. // Calculate particle velocity based on wind.
  422. if (wind != NULL) {
  423. ParticleVelocity.Set (wind->Get_Velocity().X, wind->Get_Velocity().Y, -ParticleSpeed);
  424. } else {
  425. ParticleVelocity.Set (0.0f, 0.0f, -ParticleSpeed);
  426. }
  427. // Calculate new emitter position.
  428. ooz = 1.0f / ParticleVelocity.Z;
  429. emitterdirection.Set (ParticleVelocity.X * ooz, ParticleVelocity.Y * ooz, 1.0f);
  430. emitteroffset = EmitterHeight * emitterdirection;
  431. EmitterPosition = cameraposition + emitteroffset;
  432. // Define bounding box for new emitter area.
  433. emitterbounds.Min.Set (EmitterPosition.X - EmitterSize, EmitterPosition.Y - EmitterSize);
  434. emitterbounds.Max.Set (EmitterPosition.X + EmitterSize, EmitterPosition.Y + EmitterSize);
  435. // Calculate new regions that define the areas of non-overlap between the old and new emitter positions.
  436. // Project the old emitter onto the plane of the new emitter.
  437. projectedemitterposition = oldemitterposition + ((EmitterPosition.Z - oldemitterposition.Z) * emitterdirection);
  438. // If the old and new emitters partially overlap...
  439. deltax = WWMath::Fabs (EmitterPosition.X - projectedemitterposition.X);
  440. deltay = WWMath::Fabs (EmitterPosition.Y - projectedemitterposition.Y);
  441. if ((deltax < overlapdelta) && (deltay < overlapdelta) && (EmitterPosition != projectedemitterposition)) {
  442. float area0, area1;
  443. // Compare X-extents.
  444. if (EmitterPosition.X < projectedemitterposition.X) {
  445. nonoverlapregions [0].Min.X = emitterbounds.Min.X;
  446. nonoverlapregions [0].Max.X = projectedemitterposition.X - EmitterSize;
  447. nonoverlapregions [1].Min.X = nonoverlapregions [0].Max.X;
  448. nonoverlapregions [1].Max.X = emitterbounds.Max.X;
  449. } else {
  450. nonoverlapregions [0].Min.X = projectedemitterposition.X + EmitterSize;
  451. nonoverlapregions [0].Max.X = emitterbounds.Max.X;
  452. nonoverlapregions [1].Min.X = emitterbounds.Min.X;
  453. nonoverlapregions [1].Max.X = nonoverlapregions [0].Min.X;
  454. }
  455. nonoverlapregions [0].Min.Y = emitterbounds.Min.Y;
  456. nonoverlapregions [0].Max.Y = emitterbounds.Max.Y;
  457. // Compare Y-extents.
  458. if (EmitterPosition.Y < projectedemitterposition.Y) {
  459. nonoverlapregions [1].Min.Y = emitterbounds.Min.Y;
  460. nonoverlapregions [1].Max.Y = projectedemitterposition.Y - EmitterSize;
  461. } else {
  462. nonoverlapregions [1].Min.Y = projectedemitterposition.Y + EmitterSize;
  463. nonoverlapregions [1].Max.Y = emitterbounds.Max.Y;
  464. }
  465. area0 = (nonoverlapregions [0].Max.X - nonoverlapregions [0].Min.X) * (nonoverlapregions [0].Max.Y - nonoverlapregions [0].Min.Y);
  466. area1 = (nonoverlapregions [1].Max.X - nonoverlapregions [1].Min.X) * (nonoverlapregions [1].Max.Y - nonoverlapregions [1].Min.Y);
  467. if (area0 == 0.0f) {
  468. // Always select region 1.
  469. regionthreshold = -FLT_MAX;
  470. } else {
  471. if (area1 == 0.0f) {
  472. // Always select region 0.
  473. regionthreshold = +FLT_MAX;
  474. } else {
  475. // Select either region 0 or region 1 weighted by area.
  476. regionthreshold = area0 / (area0 + area1);
  477. }
  478. }
  479. } else {
  480. // No overlap or complete overlap: region 0 is emitter area, region 1 is not used.
  481. nonoverlapregions [0] = emitterbounds;
  482. regionthreshold = +FLT_MAX;
  483. }
  484. // Calculate offset of projection of ray start from old to new emitter.
  485. raystartoffset = (EmitterPosition.Z - oldemitterposition.Z) * Vector2 (emitterdirection.X, emitterdirection.Y);
  486. // Precalculate offsets to intersection with upper and lower planes of scene bounds from emitter position.
  487. sceneminoffset = (SceneMin.Z - EmitterPosition.Z) * emitterdirection;
  488. scenemaxoffset = (SceneMax.Z - EmitterPosition.Z) * emitterdirection;
  489. // Iterate over all rays...
  490. spawncountfraction = 0.0f;
  491. rayptr = RayHead;
  492. while (rayptr != NULL) {
  493. Vector3 raystartposition;
  494. // Does this ray need to be initialized?
  495. if (!rayptr->Initialized) {
  496. // Randomly allocate a new ray start position from the entire emitter region.
  497. alpha = _RandomNumber (0, randomness) * oorandomness;
  498. beta = _RandomNumber (0, randomness) * oorandomness;
  499. range = emitterbounds.Max - emitterbounds.Min;
  500. range.Scale (alpha, beta);
  501. rayptr->StartPosition = emitterbounds.Min + range;
  502. rayptr->Initialized = true;
  503. rayptr->RayCast = true;
  504. } else {
  505. // Project the ray's emitter position onto the plane of the new emitter.
  506. rayptr->StartPosition += raystartoffset;
  507. // Does the ray fall outside the emitter (and therefore needs to be raycast)?
  508. rayptr->RayCast = (rayptr->StartPosition.X < emitterbounds.Min.X) ||
  509. (rayptr->StartPosition.X > emitterbounds.Max.X) ||
  510. (rayptr->StartPosition.Y < emitterbounds.Min.Y) ||
  511. (rayptr->StartPosition.Y > emitterbounds.Max.Y);
  512. if (rayptr->RayCast) {
  513. unsigned regionindex;
  514. // Randomly allocate a new ray start position from one of the non-overlap regions.
  515. if ((_RandomNumber (0, randomness) * oorandomness) < regionthreshold) {
  516. regionindex = 0;
  517. } else {
  518. regionindex = 1;
  519. }
  520. alpha = _RandomNumber (0, randomness) * oorandomness;
  521. beta = _RandomNumber (0, randomness) * oorandomness;
  522. range = nonoverlapregions [regionindex].Max - nonoverlapregions [regionindex].Min;
  523. range.Scale (alpha, beta);
  524. rayptr->StartPosition = nonoverlapregions [regionindex].Min + range;
  525. } else {
  526. // Next ray.
  527. rayptr = rayptr->Next;
  528. continue;
  529. }
  530. }
  531. raystartposition.Set (rayptr->StartPosition.X, rayptr->StartPosition.Y, EmitterPosition.Z);
  532. // Raycast to find the ray's collision point with the environment.
  533. {
  534. Vector3 raycaststartpoint (raystartposition + scenemaxoffset);
  535. Vector3 raycastendpoint (raystartposition + sceneminoffset);
  536. LineSegClass raycast (raycaststartpoint, raycastendpoint);
  537. CastResultStruct rayresult;
  538. PhysRayCollisionTestClass raytest (raycast, &rayresult, TERRAIN_ONLY_COLLISION_GROUP, COLLISION_TYPE_PROJECTILE);
  539. Scene->Cast_Ray (raytest);
  540. raycast.Compute_Point (raytest.Result->Fraction, &(rayptr->EndPosition));
  541. if (raytest.Result->Fraction < 1.0f) {
  542. rayptr->ValidSurfaceNormal = true;
  543. rayptr->SurfaceNormal = raytest.Result->Normal;
  544. } else {
  545. rayptr->ValidSurfaceNormal = false;
  546. }
  547. }
  548. rayptr->ParticleVelocity = ParticleVelocity;
  549. if ((Age > 0.0f) && (Can_Spawn (rayptr))) {
  550. float s;
  551. unsigned spawncount;
  552. // Spawn some particles along the ray.
  553. // NOTE: For accuracy, accumulate fractional spawncounts so that they can be used on a later ray.
  554. s = ParticlesPerUnitLength * (rayptr->EndPosition - raystartposition).Quick_Length();
  555. spawncount = floor (s);
  556. spawncountfraction += s - spawncount;
  557. if (spawncountfraction >= 1.0f) {
  558. spawncountfraction -= 1.0f;
  559. spawncount++;
  560. }
  561. for (unsigned p = 0; p < spawncount; p++) {
  562. Spawn (rayptr);
  563. }
  564. }
  565. // Update minimum ray end Z.
  566. if (rayptr->EndPosition.Z < MinRayEndZ) {
  567. MinRayEndZ = rayptr->EndPosition.Z;
  568. }
  569. // Next ray.
  570. rayptr = rayptr->Next;
  571. }
  572. // Now iterate over rayupdatecount rays and randomize them so that the ray 'pattern' is
  573. // not discernable to the user (because it is constantly changing) and also to take
  574. // account of the new particle velocity.
  575. if (RayUpdatePtr != NULL) {
  576. for (unsigned r = 0; r < rayupdatecount; r++) {
  577. // NOTE: Only need to randomize those rays that have not just been relocated inside the emitter.
  578. if (!RayUpdatePtr->RayCast) {
  579. // Randomly allocate a new ray start position from the entire emitter region.
  580. alpha = _RandomNumber (0, randomness) * oorandomness;
  581. beta = _RandomNumber (0, randomness) * oorandomness;
  582. range = emitterbounds.Max - emitterbounds.Min;
  583. range.Scale (alpha, beta);
  584. RayUpdatePtr->StartPosition = emitterbounds.Min + range;
  585. // Raycast to find the ray's collision point with the environment.
  586. {
  587. Vector3 raystartposition (RayUpdatePtr->StartPosition.X, RayUpdatePtr->StartPosition.Y, EmitterPosition.Z);
  588. Vector3 raycaststartpoint (raystartposition + scenemaxoffset);
  589. Vector3 raycastendpoint (raystartposition + sceneminoffset);
  590. LineSegClass raycast (raycaststartpoint, raycastendpoint);
  591. CastResultStruct rayresult;
  592. PhysRayCollisionTestClass raytest (raycast, &rayresult, TERRAIN_ONLY_COLLISION_GROUP, COLLISION_TYPE_PROJECTILE);
  593. Scene->Cast_Ray (raytest);
  594. raycast.Compute_Point (raytest.Result->Fraction, &(RayUpdatePtr->EndPosition));
  595. if (raytest.Result->Fraction < 1.0f) {
  596. RayUpdatePtr->ValidSurfaceNormal = true;
  597. RayUpdatePtr->SurfaceNormal = raytest.Result->Normal;
  598. } else {
  599. RayUpdatePtr->ValidSurfaceNormal = false;
  600. }
  601. }
  602. RayUpdatePtr->ParticleVelocity = ParticleVelocity;
  603. // Update minimum ray end Z.
  604. if (RayUpdatePtr->EndPosition.Z < MinRayEndZ) {
  605. MinRayEndZ = RayUpdatePtr->EndPosition.Z;
  606. }
  607. }
  608. // Next ray. If necessary, wrap around to head of list.
  609. RayUpdatePtr = RayUpdatePtr->Next;
  610. if (RayUpdatePtr == NULL) RayUpdatePtr = RayHead;
  611. }
  612. }
  613. // Calculate a bounding box for the render object that encompasses the rays.
  614. // NOTE 0: For efficiency, calculate the parallelepiped that is generated by projecting
  615. // the emitter area onto the plane that contains the lowest ray end point and put
  616. // a bounding box around this.
  617. // NOTE 1: Make the bounding box a little bigger to account for the particle point size.
  618. minrayendposition = EmitterPosition + ((MinRayEndZ - EmitterPosition.Z) * emitterdirection);
  619. l = MAX (HalfParticleWidth, HalfParticleHeight);
  620. boxoffset = EmitterSize + l;
  621. ObjectMax.Set (MAX (EmitterPosition.X, minrayendposition.X) + boxoffset, MAX (EmitterPosition.Y, minrayendposition.Y) + boxoffset, MAX (EmitterPosition.Z, minrayendposition.Z) + l);
  622. ObjectMin.Set (MIN (EmitterPosition.X, minrayendposition.X) - boxoffset, MIN (EmitterPosition.Y, minrayendposition.Y) - boxoffset, MIN (EmitterPosition.Z, minrayendposition.Z) - l);
  623. // Flag that the object bounding box has been modified.
  624. Invalidate_Cached_Bounding_Volumes();
  625. // Iterate over all particles...
  626. time = WW3D::Get_Frame_Time() * 0.001f;
  627. particleptr = ParticleHead;
  628. while (particleptr != NULL) {
  629. Vector2 emitterposition;
  630. bool outside;
  631. ParticleStruct *nextparticleptr = particleptr->Next;
  632. // Advance time.
  633. particleptr->ElapsedTime += time;
  634. // Has it expired?
  635. if (particleptr->ElapsedTime >= particleptr->LifeTime) {
  636. Kill (particleptr);
  637. particleptr = nextparticleptr;
  638. continue;
  639. }
  640. // Has it just collided?
  641. if (particleptr->ElapsedTime >= particleptr->CollisionTime) {
  642. if (particleptr->Velocity.Z != 0.0f) {
  643. // Place the particle at the collision point.
  644. particleptr->Velocity.Set (0.0f, 0.0f, 0.0f);
  645. particleptr->CurrentPosition = particleptr->CollisionPosition;
  646. if (StaticPageExists) particleptr->Page = PageCount - 1;
  647. particleptr->RenderMode = RENDER_MODE_SURFACE_ALIGNED;
  648. }
  649. } else {
  650. // Advance position.
  651. particleptr->CurrentPosition += particleptr->Velocity * time;
  652. }
  653. // Project the particle's position onto the plane of the new emitter.
  654. emitterposition = Vector2 (particleptr->CurrentPosition.X, particleptr->CurrentPosition.Y) + ((EmitterPosition.Z - particleptr->CurrentPosition.Z) * particleptr->UnitZVelocity);
  655. // Does the particle fall outside the emitter?
  656. outside = (emitterposition.X < emitterbounds.Min.X) ||
  657. (emitterposition.X > emitterbounds.Max.X) ||
  658. (emitterposition.Y < emitterbounds.Min.Y) ||
  659. (emitterposition.Y > emitterbounds.Max.Y);
  660. if (outside) {
  661. Kill (particleptr);
  662. }
  663. particleptr = nextparticleptr;
  664. }
  665. // Spawn any new particles that need to be spawned on this update.
  666. // NOTE: For accuracy, accumulate fractional spawncounts so that they can be used on a later iteration.
  667. s = Spawn_Count (time);
  668. spawncount = floor (s);
  669. SpawnCountFraction += s - spawncount;
  670. if (SpawnCountFraction >= 1.0f) {
  671. SpawnCountFraction -= 1.0f;
  672. spawncount++;
  673. }
  674. for (unsigned p = 0; p < spawncount; p++) {
  675. Spawn();
  676. }
  677. // Advance weather system age.
  678. // NOTE: To prevent floating point overflow, don't advance the age past some maximum.
  679. if (Age < MAX_AGE) Age += time;
  680. // Is the weather still active (ie. it is contributing to the scene visually or audibly)?
  681. return ((ParticleDensity > 0.0f) || (ParticleCount > 0));
  682. }
  683. /***********************************************************************************************
  684. * WeatherSystemClass::Spawn -- *
  685. * *
  686. * INPUT: *
  687. * *
  688. * OUTPUT: *
  689. * *
  690. * WARNINGS: *
  691. * *
  692. * HISTORY: *
  693. * 03/06/01 IML : Created. *
  694. *=============================================================================================*/
  695. bool WeatherSystemClass::Spawn (RayStruct *suppliedrayptr)
  696. {
  697. const unsigned maxparticlecount = USHRT_MAX / (2 * VERTICES_PER_TRIANGLE);
  698. // Due to a limitation in the underlying rendering API, there cannot be more than USHRT_MAX
  699. // total vertices in the system. Conservatively restrict this to half of this to compensate
  700. // for other primitives in the system.
  701. if (_GlobalParticleCount < maxparticlecount) {
  702. RayStruct *rayptr;
  703. if (suppliedrayptr == NULL) {
  704. // Assign a ray to this particle.
  705. // If there are no rays to assign then return failure to spawn.
  706. if (RaySpawnPtr != NULL) {
  707. rayptr = RaySpawnPtr;
  708. RaySpawnPtr = RaySpawnPtr->Next;
  709. if (RaySpawnPtr == NULL) RaySpawnPtr = RayHead;
  710. } else {
  711. return (false);
  712. }
  713. } else {
  714. rayptr = suppliedrayptr;
  715. }
  716. // Ensure that this ray's endpoint lies below the emitter (otherwise there is an obstruction
  717. // between the ray's source and the emitter).
  718. if (Can_Spawn (rayptr)) {
  719. const unsigned randomness = 1000;
  720. const float oorandomness = 1.0f / randomness;
  721. const float ooz = 1.0f / rayptr->ParticleVelocity.Z;
  722. const float collisiontime = (rayptr->EndPosition.Z - EmitterPosition.Z) * ooz;
  723. ParticleStruct *particleptr;
  724. particleptr = new ParticleStruct;
  725. WWASSERT (particleptr != NULL);
  726. // Add this particle to the head of the list.
  727. if (ParticleHead != NULL) {
  728. ParticleHead->Prev = particleptr;
  729. }
  730. particleptr->Prev = NULL;
  731. particleptr->Next = ParticleHead;
  732. ParticleHead = particleptr;
  733. // Increment the no. of particles.
  734. ParticleCount++;
  735. // Increment total no. of global weather particles.
  736. _GlobalParticleCount++;
  737. particleptr->UnitZVelocity.Set (rayptr->ParticleVelocity.X * ooz, rayptr->ParticleVelocity.Y * ooz);
  738. particleptr->CollisionTime = collisiontime;
  739. particleptr->CollisionPosition = rayptr->EndPosition;
  740. // If the ray does not have a valid surface normal then the ray's end position intersects the level's bounding box.
  741. // In this case it is not necessary to sustain the particle past the collision time.
  742. if (rayptr->ValidSurfaceNormal) {
  743. particleptr->LifeTime = collisiontime + WWMath::Lerp (MinStaticTime, MaxStaticTime, _RandomNumber (0, randomness) * oorandomness);
  744. particleptr->SurfaceNormal = rayptr->SurfaceNormal;
  745. } else {
  746. particleptr->LifeTime = collisiontime;
  747. }
  748. if (suppliedrayptr == NULL) {
  749. // Start particle at emitter.
  750. particleptr->ElapsedTime = 0.0f;
  751. particleptr->Velocity = rayptr->ParticleVelocity;
  752. particleptr->CurrentPosition = Vector3 (rayptr->StartPosition.X, rayptr->StartPosition.Y, EmitterPosition.Z);
  753. particleptr->Page = _RandomNumber (0, PageCount - (StaticPageExists) ? 2 : 1);
  754. particleptr->RenderMode = RenderMode;
  755. } else {
  756. float t;
  757. // Advance the particle some random amount in time.
  758. // NOTE: The particle cannot have existed longer than the weather system itself.
  759. t = _RandomNumber (0, randomness) * oorandomness * particleptr->LifeTime;
  760. particleptr->ElapsedTime = MIN (t, Age);
  761. if (particleptr->ElapsedTime >= particleptr->CollisionTime) {
  762. particleptr->Velocity.Set (0.0f, 0.0f, 0.0f);
  763. particleptr->CurrentPosition = rayptr->EndPosition;
  764. if (StaticPageExists) {
  765. particleptr->Page = PageCount - 1;
  766. } else {
  767. particleptr->Page = _RandomNumber (0, PageCount - 1);
  768. }
  769. particleptr->RenderMode = RENDER_MODE_SURFACE_ALIGNED;
  770. } else {
  771. particleptr->Velocity = rayptr->ParticleVelocity;
  772. particleptr->CurrentPosition = Vector3 (rayptr->StartPosition.X, rayptr->StartPosition.Y, EmitterPosition.Z) + (particleptr->Velocity * particleptr->ElapsedTime);
  773. particleptr->Page = _RandomNumber (0, PageCount - (StaticPageExists) ? 2 : 1);
  774. particleptr->RenderMode = RenderMode;
  775. }
  776. }
  777. return (true);
  778. }
  779. }
  780. return (false);
  781. }
  782. /***********************************************************************************************
  783. * WeatherSystemClass::Kill -- *
  784. * *
  785. * INPUT: *
  786. * *
  787. * OUTPUT: *
  788. * *
  789. * WARNINGS: *
  790. * *
  791. * HISTORY: *
  792. * 03/06/01 IML : Created. *
  793. *=============================================================================================*/
  794. void WeatherSystemClass::Kill (ParticleStruct *particleptr)
  795. {
  796. WWASSERT (ParticleCount > 0);
  797. // Remove this particle from the list.
  798. if (particleptr->Prev != NULL) {
  799. particleptr->Prev->Next = particleptr->Next;
  800. } else {
  801. ParticleHead = particleptr->Next;
  802. }
  803. if (particleptr->Next != NULL) {
  804. particleptr->Next->Prev = particleptr->Prev;
  805. }
  806. delete particleptr;
  807. // Decrement the no. of particles.
  808. ParticleCount--;
  809. // Decrement the no. of global weather particles.
  810. _GlobalParticleCount--;
  811. }
  812. /***********************************************************************************************
  813. * WeatherSystemClass::Render -- *
  814. * *
  815. * INPUT: *
  816. * *
  817. * OUTPUT: *
  818. * *
  819. * WARNINGS: *
  820. * *
  821. * HISTORY: *
  822. * 03/06/01 IML : Created. *
  823. *=============================================================================================*/
  824. void WeatherSystemClass::Render (RenderInfoClass &rinfo)
  825. {
  826. if (WW3D::Are_Static_Sort_Lists_Enabled()) {
  827. const unsigned sortlevel = 31;
  828. WW3D::Add_To_Static_Sort_List (this, sortlevel);
  829. } else {
  830. const Vector3 zero (0.0f, 0.0f, 0.0f);
  831. const Vector3 white (1.0f, 1.0f, 1.0f);
  832. Vector3 cameravelocity;
  833. Vector3 color;
  834. unsigned dxcolor;
  835. Matrix4 viewmatrix (true), identitymatrix (true);
  836. float maxalphaheight, oomaxalphaheight, deltaheight;
  837. Vector3 x, y;
  838. float w, h;
  839. Vector3 offset [VERTICES_PER_TRIANGLE];
  840. Vector3 camerafocus;
  841. ParticleStruct *particleptr;
  842. unsigned processedparticlecount, bufferparticlecount;
  843. if (CameraPositionValid) {
  844. cameravelocity = ((rinfo.Camera.Get_Position() - CameraPosition) / (WW3D::Get_Frame_Time() * 0.001f));
  845. } else {
  846. cameravelocity.Set (0.0f, 0.0f, 0.0f);
  847. CameraPositionValid = true;
  848. }
  849. CameraPosition = rinfo.Camera.Get_Position();
  850. dxcolor = DX8Wrapper::Convert_Color (Vector3 (1.0f, 1.0f, 1.0f), 0.0f);
  851. // Precalculate alpha values.
  852. maxalphaheight = EmitterHeight * 0.2f;
  853. oomaxalphaheight = 1.0f / maxalphaheight;
  854. deltaheight = rinfo.Camera.Get_Position().Z + (EmitterHeight - maxalphaheight);
  855. // NOTE: All particle positions are already in world space.
  856. DX8Wrapper::Set_Transform (D3DTS_WORLD, identitymatrix);
  857. DX8Wrapper::Set_Material (Material);
  858. DX8Wrapper::Set_Shader (Shader);
  859. DX8Wrapper::Set_Texture (0, Texture);
  860. DX8Wrapper::Set_Index_Buffer (IndexBuffer, 0);
  861. #if WEATHER_PARTICLE_SORT
  862. #else
  863. DX8Wrapper::Set_DX8_Render_State (D3DRS_ZBIAS, 12);
  864. #endif
  865. camerafocus = rinfo.Camera.Get_Transform().Get_Z_Vector();
  866. particleptr = ParticleHead;
  867. processedparticlecount = 0;
  868. bufferparticlecount = MIN (MAX_IB_PARTICLE_COUNT, ParticleCount);
  869. while (processedparticlecount < ParticleCount) {
  870. unsigned particlecount, submittedparticlecount;
  871. #if WEATHER_PARTICLE_SORT
  872. DynamicVBAccessClass dynamicvb (BUFFER_TYPE_DYNAMIC_SORTING, dynamic_fvf_type, bufferparticlecount * VERTICES_PER_TRIANGLE);
  873. #else
  874. DynamicVBAccessClass dynamicvb (BUFFER_TYPE_DYNAMIC_DX8, dynamic_fvf_type, bufferparticlecount * VERTICES_PER_TRIANGLE);
  875. #endif
  876. // Copy the data into the sorting vertex buffer.
  877. particlecount = MIN (ParticleCount - processedparticlecount, MAX_IB_PARTICLE_COUNT);
  878. submittedparticlecount = 0;
  879. {
  880. DynamicVBAccessClass::WriteLockClass lock (&dynamicvb);
  881. VertexFormatXYZNDUV2 *vertex = lock.Get_Formatted_Vertex_Array();
  882. for (unsigned p = 0; p < particlecount; p++) {
  883. Vector3 position;
  884. WWASSERT (particleptr != NULL);
  885. position = particleptr->CurrentPosition;
  886. // Optimization: only submit this particle for rendering if it is in the view frustum.
  887. if (CollisionMath::Overlap_Test (rinfo.Camera.Get_Frustum(), position) != CollisionMath::OUTSIDE) {
  888. unsigned base;
  889. float alphaheight, alpha;
  890. switch (particleptr->RenderMode) {
  891. case RENDER_MODE_AXIS_ALIGNED:
  892. y = particleptr->Velocity - cameravelocity;
  893. y /= y.Quick_Length();
  894. x = Vector3::Cross_Product (camerafocus, y);
  895. x /= x.Quick_Length();
  896. w = HalfParticleWidth;
  897. h = HalfParticleHeight;
  898. break;
  899. case RENDER_MODE_CAMERA_ALIGNED:
  900. x = rinfo.Camera.Get_Transform().Get_X_Vector();
  901. y = rinfo.Camera.Get_Transform().Get_Y_Vector();
  902. w = HalfParticleWidth;
  903. h = HalfParticleHeight;
  904. break;
  905. case RENDER_MODE_SURFACE_ALIGNED:
  906. // Particle has an orientation so back-face cull it.
  907. if (Vector3::Dot_Product (camerafocus, particleptr->SurfaceNormal) > 0.0f) {
  908. x = Vector3::Cross_Product (camerafocus, particleptr->SurfaceNormal);
  909. x /= x.Quick_Length();
  910. y = Vector3::Cross_Product (x, particleptr->SurfaceNormal);
  911. if (DecayAfterCollision) {
  912. float decaytime, totaldecaytime, s;
  913. decaytime = particleptr->ElapsedTime - particleptr->CollisionTime;
  914. totaldecaytime = particleptr->LifeTime - particleptr->CollisionTime;
  915. if ((decaytime >= 0.0f) && (totaldecaytime > 0.0f)) {
  916. s = 1.0f - (decaytime / totaldecaytime);
  917. w = HalfParticleWidth * s;
  918. h = HalfParticleHeight * s;
  919. } else {
  920. w = h = 0.0f;
  921. }
  922. } else {
  923. w = HalfParticleWidth;
  924. h = HalfParticleHeight;
  925. }
  926. break;
  927. } else {
  928. // Advance to next particle.
  929. particleptr = particleptr->Next;
  930. continue;
  931. }
  932. default:
  933. WWASSERT (false);
  934. w = h = 0.0f;
  935. break;
  936. }
  937. offset [0] = -h * y;
  938. offset [1] = h * y + w * x;
  939. offset [2] = h * y - w * x;
  940. base = particleptr->Page * VERTICES_PER_TRIANGLE;
  941. alphaheight = position.Z - deltaheight;
  942. if (alphaheight > 0.0f) {
  943. alpha = 1.0f - (alphaheight * oomaxalphaheight);
  944. } else {
  945. alpha = 1.0f;
  946. }
  947. DX8Wrapper::Set_Alpha (alpha, dxcolor);
  948. // Vertex 0 of triangle.
  949. vertex->x = position.X + offset [0].X;
  950. vertex->y = position.Y + offset [0].Y;
  951. vertex->z = position.Z + offset [0].Z;
  952. vertex->diffuse = dxcolor;
  953. vertex->u1 = TextureArray [base].U;
  954. vertex->v1 = TextureArray [base].V;
  955. vertex++;
  956. // Vertex 1 of triangle.
  957. vertex->x = position.X + offset [1].X;
  958. vertex->y = position.Y + offset [1].Y;
  959. vertex->z = position.Z + offset [1].Z;
  960. vertex->diffuse = dxcolor;
  961. vertex->u1 = TextureArray [base + 1].U;
  962. vertex->v1 = TextureArray [base + 1].V;
  963. vertex++;
  964. // Vertex 2 of triangle.
  965. vertex->x = position.X + offset [2].X;
  966. vertex->y = position.Y + offset [2].Y;
  967. vertex->z = position.Z + offset [2].Z;
  968. vertex->diffuse = dxcolor;
  969. vertex->u1 = TextureArray [base + 2].U;
  970. vertex->v1 = TextureArray [base + 2].V;
  971. vertex++;
  972. submittedparticlecount++;
  973. }
  974. // Advance to next particle.
  975. particleptr = particleptr->Next;
  976. }
  977. }
  978. if (submittedparticlecount > 0) {
  979. DX8Wrapper::Set_Vertex_Buffer (dynamicvb);
  980. #if WEATHER_PARTICLE_SORT
  981. SortingRendererClass::Insert_Triangles (0, submittedparticlecount, 0, submittedparticlecount * VERTICES_PER_TRIANGLE);
  982. #else
  983. DX8Wrapper::Draw_Triangles (0, submittedparticlecount, 0, submittedparticlecount * VERTICES_PER_TRIANGLE);
  984. #endif
  985. }
  986. processedparticlecount += particlecount;
  987. }
  988. WWASSERT (particleptr == NULL);
  989. #if WEATHER_PARTICLE_SORT
  990. #else
  991. DX8Wrapper::Set_DX8_Render_State (D3DRS_ZBIAS, 0);
  992. #endif
  993. }
  994. }
  995. /***********************************************************************************************
  996. * WeatherSystemClass::Get_Obj_Space_Bounding_Sphere -- *
  997. * *
  998. * INPUT: *
  999. * *
  1000. * OUTPUT: *
  1001. * *
  1002. * WARNINGS: *
  1003. * *
  1004. * HISTORY: *
  1005. * 03/06/01 IML : Created. *
  1006. *=============================================================================================*/
  1007. void WeatherSystemClass::Get_Obj_Space_Bounding_Sphere (SphereClass &sphere) const
  1008. {
  1009. sphere.Init ((ObjectMin + ObjectMax) * 0.5f, ((ObjectMax - ObjectMin) * 0.5f).Length());
  1010. }
  1011. /***********************************************************************************************
  1012. * WeatherSystemClass::Get_Obj_Space_Bounding_Box -- *
  1013. * *
  1014. * INPUT: *
  1015. * *
  1016. * OUTPUT: *
  1017. * *
  1018. * WARNINGS: *
  1019. * *
  1020. * HISTORY: *
  1021. * 03/06/01 IML : Created. *
  1022. *=============================================================================================*/
  1023. void WeatherSystemClass::Get_Obj_Space_Bounding_Box (AABoxClass &box) const
  1024. {
  1025. box.Init ((ObjectMin + ObjectMax) * 0.5f, (ObjectMax - ObjectMin) * 0.5f);
  1026. }
  1027. /***********************************************************************************************
  1028. * RainSystemClass::RainSystemClass -- *
  1029. * *
  1030. * INPUT: *
  1031. * *
  1032. * OUTPUT: *
  1033. * *
  1034. * WARNINGS: *
  1035. * *
  1036. * HISTORY: *
  1037. * 03/06/01 IML : Created. *
  1038. *=============================================================================================*/
  1039. RainSystemClass::RainSystemClass (PhysicsSceneClass *scene, float particledensity, WindClass *wind, SoundEnvironmentClass *soundenvironment, bool prime)
  1040. : WeatherSystemClass (scene, 20.0f, 20.0f, particledensity, 0.2f, 0.15f, 0.45f, 15.0f, Vector2 (0.0f, 0.0f), Vector2 (1.0f, 0.5f), PAGE_COUNT, true, 0.1f, 0.2f, WeatherSystemClass::RENDER_MODE_AXIS_ALIGNED, false, prime),
  1041. SoundEnvironment (soundenvironment)
  1042. {
  1043. const char *rainsamplename = "Rainfall01";
  1044. // Create the rain sound effect.
  1045. // Optimization: Only add the sound effect if wind speed is non-zero.
  1046. Sound = WWAudioClass::Get_Instance()->Create_Sound (rainsamplename, NULL, 0, CLASSID_2D);
  1047. if (Sound != NULL) {
  1048. SoundEnvironment->Add_User();
  1049. Sound->Set_Volume (0.0f);
  1050. Sound->Play();
  1051. }
  1052. }
  1053. /***********************************************************************************************
  1054. * RainSystemClass::~RainSystemClass -- *
  1055. * *
  1056. * INPUT: *
  1057. * *
  1058. * OUTPUT: *
  1059. * *
  1060. * WARNINGS: *
  1061. * *
  1062. * HISTORY: *
  1063. * 03/06/01 IML : Created. *
  1064. *=============================================================================================*/
  1065. RainSystemClass::~RainSystemClass()
  1066. {
  1067. // Remove rain sound effect.
  1068. if (Sound != NULL) {
  1069. Sound->Stop();
  1070. REF_PTR_RELEASE (Sound);
  1071. SoundEnvironment->Remove_User();
  1072. }
  1073. }
  1074. /***********************************************************************************************
  1075. * RainSystemClass::Update -- *
  1076. * *
  1077. * INPUT: *
  1078. * *
  1079. * OUTPUT: *
  1080. * *
  1081. * WARNINGS: *
  1082. * *
  1083. * HISTORY: *
  1084. * 03/06/01 IML : Created. *
  1085. *=============================================================================================*/
  1086. bool RainSystemClass::Update (WindClass *wind, const Vector3 &cameraposition)
  1087. {
  1088. if (Sound != NULL) {
  1089. const float maxvolume = 4.0f;
  1090. const float volumeperparticle = 0.0025f;
  1091. float attenuation;
  1092. // Calculate sound attenuation based on no. of particles in system.
  1093. attenuation = MIN (ParticleCount * volumeperparticle, maxvolume) / maxvolume;
  1094. Sound->Set_Volume (SoundEnvironment->Get_Amplitude() * attenuation);
  1095. }
  1096. // Base class update.
  1097. return (WeatherSystemClass::Update (wind, cameraposition));
  1098. }
  1099. /***********************************************************************************************
  1100. * SnowSystemClass::SnowSystemClass -- *
  1101. * *
  1102. * INPUT: *
  1103. * *
  1104. * OUTPUT: *
  1105. * *
  1106. * WARNINGS: *
  1107. * *
  1108. * HISTORY: *
  1109. * 03/06/01 IML : Created. *
  1110. *=============================================================================================*/
  1111. SnowSystemClass::SnowSystemClass (PhysicsSceneClass *scene, float particledensity, WindClass *wind, bool prime)
  1112. : WeatherSystemClass (scene, 40.0f, 20.0f, particledensity, 0.1f, 0.32f, 0.32f, 3.5f, Vector2 (0.0f, 0.5f), Vector2 (1.0f, 0.25f), PAGE_COUNT, false, 1.0f, 2.0f, WeatherSystemClass::RENDER_MODE_CAMERA_ALIGNED, true, prime)
  1113. {
  1114. }
  1115. /***********************************************************************************************
  1116. * SnowSystemClass::Update -- *
  1117. * *
  1118. * INPUT: *
  1119. * *
  1120. * OUTPUT: *
  1121. * *
  1122. * WARNINGS: *
  1123. * *
  1124. * HISTORY: *
  1125. * 03/06/01 IML : Created. *
  1126. *=============================================================================================*/
  1127. bool SnowSystemClass::Update (WindClass *wind, const Vector3 &cameraposition)
  1128. {
  1129. // Base class update.
  1130. return (WeatherSystemClass::Update (wind, cameraposition));
  1131. }
  1132. /***********************************************************************************************
  1133. * AshSystemClass::AshSystemClass -- *
  1134. * *
  1135. * INPUT: *
  1136. * *
  1137. * OUTPUT: *
  1138. * *
  1139. * WARNINGS: *
  1140. * *
  1141. * HISTORY: *
  1142. * 03/06/01 IML : Created. *
  1143. *=============================================================================================*/
  1144. AshSystemClass::AshSystemClass (PhysicsSceneClass *scene, float particledensity, WindClass *wind, bool prime)
  1145. : WeatherSystemClass (scene, 40.0f, 20.0f, particledensity, 0.1f, 0.32f, 0.32f, 3.0f, Vector2 (0.0f, 0.75f), Vector2 (1.0f, 0.25f), PAGE_COUNT, false, 1.0f, 2.0f, WeatherSystemClass::RENDER_MODE_CAMERA_ALIGNED, true, prime)
  1146. {
  1147. }
  1148. /***********************************************************************************************
  1149. * AshSystemClass::Update -- *
  1150. * *
  1151. * INPUT: *
  1152. * *
  1153. * OUTPUT: *
  1154. * *
  1155. * WARNINGS: *
  1156. * *
  1157. * HISTORY: *
  1158. * 03/06/01 IML : Created. *
  1159. *=============================================================================================*/
  1160. bool AshSystemClass::Update (WindClass *wind, const Vector3 &cameraposition)
  1161. {
  1162. // Base class update.
  1163. return (WeatherSystemClass::Update (wind, cameraposition));
  1164. }
  1165. /***********************************************************************************************
  1166. * WeatherParameterClass::Initialize -- *
  1167. * *
  1168. * INPUT: *
  1169. * *
  1170. * OUTPUT: *
  1171. * *
  1172. * WARNINGS: *
  1173. * *
  1174. * HISTORY: *
  1175. * 04/24/01 IML : Created. *
  1176. *=============================================================================================*/
  1177. void WeatherParameterClass::Initialize()
  1178. {
  1179. CurrentValue = 0.0f;
  1180. NormalTarget = 0.0f;
  1181. NormalDuration = 0.0f;
  1182. OverrideTarget = 0.0f;
  1183. OverrideDuration = 0.0f;
  1184. }
  1185. /***********************************************************************************************
  1186. * WeatherParameterClass::Set -- *
  1187. * *
  1188. * INPUT: *
  1189. * *
  1190. * OUTPUT: *
  1191. * *
  1192. * WARNINGS: *
  1193. * *
  1194. * HISTORY: *
  1195. * 04/24/01 IML : Created. *
  1196. *=============================================================================================*/
  1197. void WeatherParameterClass::Set (float target, float ramptime, bool override)
  1198. {
  1199. if (!override) {
  1200. NormalTarget = target;
  1201. NormalDuration = ramptime;
  1202. } else {
  1203. OverrideTarget = target;
  1204. OverrideDuration = ramptime;
  1205. }
  1206. }
  1207. /***********************************************************************************************
  1208. * WeatherParameterClass::Update -- *
  1209. * *
  1210. * INPUT: *
  1211. * *
  1212. * OUTPUT: *
  1213. * *
  1214. * WARNINGS: *
  1215. * *
  1216. * HISTORY: *
  1217. * 04/24/01 IML : Created. *
  1218. *=============================================================================================*/
  1219. bool WeatherParameterClass::Update (float time, bool override)
  1220. {
  1221. const float previouscurrentvalue = CurrentValue;
  1222. // Update the normal parameters.
  1223. Update (NormalValue, NormalTarget, NormalDuration, time);
  1224. // Update the override parameters?
  1225. if (override) {
  1226. Update (CurrentValue, OverrideTarget, OverrideDuration, time);
  1227. } else {
  1228. if (OverrideDuration > 0.0f) {
  1229. Update (CurrentValue, NormalValue, OverrideDuration, time);
  1230. } else {
  1231. CurrentValue = NormalValue;
  1232. }
  1233. }
  1234. return (CurrentValue != previouscurrentvalue);
  1235. }
  1236. /***********************************************************************************************
  1237. * WeatherParameterClass::Update -- *
  1238. * *
  1239. * INPUT: *
  1240. * *
  1241. * OUTPUT: *
  1242. * *
  1243. * WARNINGS: *
  1244. * *
  1245. * HISTORY: *
  1246. * 04/24/01 IML : Created. *
  1247. *=============================================================================================*/
  1248. void WeatherParameterClass::Update (float &value, float &target, float &duration, float time)
  1249. {
  1250. if (value == target) {
  1251. duration = 0.0f;
  1252. } else {
  1253. duration -= time;
  1254. if (duration > 0.0f) {
  1255. bool sign0, sign1;
  1256. sign0 = value < target;
  1257. value += ((target - value) * (time / duration));
  1258. if (value == target) {
  1259. duration = 0.0f;
  1260. } else {
  1261. sign1 = value < target;
  1262. // If the value has 'blown past' the target value...
  1263. if (sign0 ^ sign1) {
  1264. duration = 0.0f;
  1265. value = target;
  1266. }
  1267. }
  1268. } else {
  1269. duration = 0.0f;
  1270. value = target;
  1271. }
  1272. }
  1273. }
  1274. /***********************************************************************************************
  1275. * WeatherMgrClass::WeatherMgrClass -- *
  1276. * *
  1277. * INPUT: *
  1278. * *
  1279. * OUTPUT: *
  1280. * *
  1281. * WARNINGS: *
  1282. * *
  1283. * HISTORY: *
  1284. * 03/06/01 IML : Created. *
  1285. *=============================================================================================*/
  1286. WeatherMgrClass::WeatherMgrClass()
  1287. {
  1288. Set_Network_ID (NETID_SERVER_WEATHER);
  1289. Set_App_Packet_Type (APPPACKETTYPE_NETWEATHER);
  1290. }
  1291. /***********************************************************************************************
  1292. * WeatherMgrClass::Init -- *
  1293. * *
  1294. * INPUT: *
  1295. * *
  1296. * OUTPUT: *
  1297. * *
  1298. * WARNINGS: *
  1299. * *
  1300. * HISTORY: *
  1301. * 03/06/01 IML : Created. *
  1302. *=============================================================================================*/
  1303. void WeatherMgrClass::Init (SoundEnvironmentClass *soundenvironment)
  1304. {
  1305. WWASSERT (soundenvironment != NULL);
  1306. REF_PTR_SET (_SoundEnvironment, soundenvironment);
  1307. _Wind = NULL;
  1308. for (int p = PRECIPITATION_FIRST; p < PRECIPITATION_COUNT; p++) {
  1309. _Precipitation [p] = NULL;
  1310. }
  1311. Reset();
  1312. }
  1313. /***********************************************************************************************
  1314. * WeatherMgrClass::Reset -- *
  1315. * *
  1316. * INPUT: *
  1317. * *
  1318. * OUTPUT: *
  1319. * *
  1320. * WARNINGS: *
  1321. * *
  1322. * HISTORY: *
  1323. * 03/06/01 IML : Created. *
  1324. *=============================================================================================*/
  1325. void WeatherMgrClass::Reset()
  1326. {
  1327. int p;
  1328. // Iterate and initialize the parameters.
  1329. for (p = 0; p < PARAMETER_COUNT; p++) {
  1330. _Parameters [p].Initialize();
  1331. }
  1332. if (_Wind != NULL) {
  1333. delete _Wind;
  1334. _Wind = NULL;
  1335. }
  1336. // Restore the settings to default.
  1337. Set_Wind (0.0f, 0.0f, 0.0f, 0.0f, false);
  1338. for (p = PRECIPITATION_FIRST; p < PRECIPITATION_COUNT; p++) {
  1339. if (_Precipitation [p] != NULL) {
  1340. _Precipitation [p]->Remove();
  1341. REF_PTR_RELEASE (_Precipitation [p]);
  1342. Set_Precipitation ((PrecipitationEnum) p, 0.0f, 0.0f, false);
  1343. }
  1344. }
  1345. Set_Fog_Enable (false);
  1346. Set_Fog_Range (200.0f, 300.0f);
  1347. _Prime = true;
  1348. _Imported = false;
  1349. _WindOverrideCount = 0;
  1350. _PrecipitationOverrideCount = 0;
  1351. Set_Dirty();
  1352. }
  1353. /***********************************************************************************************
  1354. * WeatherMgrClass::Shutdown -- *
  1355. * *
  1356. * INPUT: *
  1357. * *
  1358. * OUTPUT: *
  1359. * *
  1360. * WARNINGS: *
  1361. * *
  1362. * HISTORY: *
  1363. * 03/06/01 IML : Created. *
  1364. *=============================================================================================*/
  1365. void WeatherMgrClass::Shutdown()
  1366. {
  1367. Reset();
  1368. REF_PTR_RELEASE (_SoundEnvironment);
  1369. }
  1370. /***********************************************************************************************
  1371. * WeatherMgrClass::Set_Wind -- *
  1372. * *
  1373. * INPUT: *
  1374. * *
  1375. * OUTPUT: *
  1376. * *
  1377. * WARNINGS: *
  1378. * *
  1379. * HISTORY: *
  1380. * 03/06/01 IML : Created. *
  1381. *=============================================================================================*/
  1382. bool WeatherMgrClass::Set_Wind (float heading, float speed, float variability, float ramptime)
  1383. {
  1384. if (CombatManager::I_Am_Server()) {
  1385. if (Set_Wind (heading, speed, variability, ramptime, false)) {
  1386. _TheWeatherMgr.Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  1387. return (true);
  1388. }
  1389. }
  1390. return (false);
  1391. }
  1392. /***********************************************************************************************
  1393. * WeatherMgrClass::Override_Wind -- *
  1394. * *
  1395. * INPUT: *
  1396. * *
  1397. * OUTPUT: *
  1398. * *
  1399. * WARNINGS: *
  1400. * *
  1401. * HISTORY: *
  1402. * 03/06/01 IML : Created. *
  1403. *=============================================================================================*/
  1404. bool WeatherMgrClass::Override_Wind (float heading, float speed, float variability, float ramptime)
  1405. {
  1406. if (CombatManager::I_Am_Server()) {
  1407. _WindOverrideCount++;
  1408. if (Set_Wind (heading, speed, variability, ramptime, true)) {
  1409. _TheWeatherMgr.Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  1410. return (true);
  1411. }
  1412. }
  1413. return (false);
  1414. }
  1415. /***********************************************************************************************
  1416. * WeatherMgrClass::Set_Wind -- *
  1417. * *
  1418. * INPUT: *
  1419. * *
  1420. * OUTPUT: *
  1421. * *
  1422. * WARNINGS: *
  1423. * *
  1424. * HISTORY: *
  1425. * 03/06/01 IML : Created. *
  1426. *=============================================================================================*/
  1427. bool WeatherMgrClass::Set_Wind (float heading, float speed, float variability, float ramptime, bool override)
  1428. {
  1429. if ((heading >= 0.0f) && (heading <= 360.0f) && (speed >= 0.0f) && (variability >= 0.0f) &&
  1430. (variability <= 1.0f) && (ramptime >= 0.0f)) {
  1431. bool o;
  1432. o = (_WindOverrideCount > 0) && override;
  1433. _Parameters [PARAMETER_WIND_HEADING].Set (heading, ramptime, o);
  1434. _Parameters [PARAMETER_WIND_SPEED].Set (speed, ramptime, o);
  1435. _Parameters [PARAMETER_WIND_VARIABILITY].Set (variability, ramptime, o);
  1436. return (true);
  1437. }
  1438. return (false);
  1439. }
  1440. /***********************************************************************************************
  1441. * WeatherMgrClass::Get_Wind -- *
  1442. * *
  1443. * INPUT: *
  1444. * *
  1445. * OUTPUT: *
  1446. * *
  1447. * WARNINGS: *
  1448. * *
  1449. * HISTORY: *
  1450. * 03/06/01 IML : Created. *
  1451. *=============================================================================================*/
  1452. void WeatherMgrClass::Get_Wind (float &heading, float &speed, float &variability)
  1453. {
  1454. heading = _Parameters [PARAMETER_WIND_HEADING].Value();
  1455. speed = _Parameters [PARAMETER_WIND_SPEED].Value();
  1456. variability = _Parameters [PARAMETER_WIND_VARIABILITY].Value();
  1457. }
  1458. /***********************************************************************************************
  1459. * WeatherMgrClass::Restore_Wind -- *
  1460. * *
  1461. * INPUT: *
  1462. * *
  1463. * OUTPUT: *
  1464. * *
  1465. * WARNINGS: *
  1466. * *
  1467. * HISTORY: *
  1468. * 03/06/01 IML : Created. *
  1469. *=============================================================================================*/
  1470. void WeatherMgrClass::Restore_Wind (float ramptime)
  1471. {
  1472. if (CombatManager::I_Am_Server()) {
  1473. _Parameters [PARAMETER_WIND_HEADING].Set (ramptime);
  1474. _Parameters [PARAMETER_WIND_SPEED].Set (ramptime);
  1475. _Parameters [PARAMETER_WIND_VARIABILITY].Set (ramptime);
  1476. _WindOverrideCount--;
  1477. _TheWeatherMgr.Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  1478. }
  1479. }
  1480. /***********************************************************************************************
  1481. * WeatherMgrClass::Set_Precipitation -- *
  1482. * *
  1483. * INPUT: *
  1484. * *
  1485. * OUTPUT: *
  1486. * *
  1487. * WARNINGS: *
  1488. * *
  1489. * HISTORY: *
  1490. * 03/06/01 IML : Created. *
  1491. *=============================================================================================*/
  1492. bool WeatherMgrClass::Set_Precipitation (PrecipitationEnum precipitation, float density, float ramptime)
  1493. {
  1494. if (CombatManager::I_Am_Server()) {
  1495. if (Set_Precipitation (precipitation, density, ramptime, false)) {
  1496. _TheWeatherMgr.Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  1497. return (true);
  1498. }
  1499. }
  1500. return (false);
  1501. }
  1502. /***********************************************************************************************
  1503. * WeatherMgrClass::Override_Precipitation -- *
  1504. * *
  1505. * INPUT: *
  1506. * *
  1507. * OUTPUT: *
  1508. * *
  1509. * WARNINGS: *
  1510. * *
  1511. * HISTORY: *
  1512. * 03/06/01 IML : Created. *
  1513. *=============================================================================================*/
  1514. bool WeatherMgrClass::Override_Precipitation (PrecipitationEnum precipitation, float density, float ramptime)
  1515. {
  1516. if (CombatManager::I_Am_Server()) {
  1517. bool success;
  1518. _PrecipitationOverrideCount++;
  1519. // Override requested precipitation but also override and ramp down any other types of precipitation that may exist.
  1520. success = true;
  1521. for (int p = PRECIPITATION_FIRST; p < PRECIPITATION_COUNT; p++) {
  1522. if (p != precipitation) {
  1523. success &= Set_Precipitation ((PrecipitationEnum) p, 0.0f, ramptime, true);
  1524. } else {
  1525. success &= Set_Precipitation ((PrecipitationEnum) p, density, ramptime, true);
  1526. }
  1527. }
  1528. _TheWeatherMgr.Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  1529. return (success);
  1530. }
  1531. return (false);
  1532. }
  1533. /***********************************************************************************************
  1534. * WeatherMgrClass::Set_Precipitation -- *
  1535. * *
  1536. * INPUT: *
  1537. * *
  1538. * OUTPUT: *
  1539. * *
  1540. * WARNINGS: *
  1541. * *
  1542. * HISTORY: *
  1543. * 03/06/01 IML : Created. *
  1544. *=============================================================================================*/
  1545. bool WeatherMgrClass::Set_Precipitation (PrecipitationEnum precipitation, float density, float ramptime, bool override)
  1546. {
  1547. if ((density >= 0.0f) && (ramptime >= 0.0f)) {
  1548. const bool o = (_PrecipitationOverrideCount > 0) && override;
  1549. switch (precipitation) {
  1550. case PRECIPITATION_RAIN:
  1551. _Parameters [PARAMETER_RAIN_DENSITY].Set (density, ramptime, o);
  1552. break;
  1553. case PRECIPITATION_SNOW:
  1554. _Parameters [PARAMETER_SNOW_DENSITY].Set (density, ramptime, o);
  1555. break;
  1556. case PRECIPITATION_ASH:
  1557. _Parameters [PARAMETER_ASH_DENSITY].Set (density, ramptime, o);
  1558. break;
  1559. default:
  1560. WWASSERT (false);
  1561. break;
  1562. }
  1563. return (true);
  1564. }
  1565. return (false);
  1566. }
  1567. /***********************************************************************************************
  1568. * WeatherMgrClass::Get_Precipitation -- *
  1569. * *
  1570. * INPUT: *
  1571. * *
  1572. * OUTPUT: *
  1573. * *
  1574. * WARNINGS: *
  1575. * *
  1576. * HISTORY: *
  1577. * 03/06/01 IML : Created. *
  1578. *=============================================================================================*/
  1579. void WeatherMgrClass::Get_Precipitation (PrecipitationEnum precipitation, float &density)
  1580. {
  1581. switch (precipitation) {
  1582. case PRECIPITATION_RAIN:
  1583. density = _Parameters [PARAMETER_RAIN_DENSITY].Value();
  1584. break;
  1585. case PRECIPITATION_SNOW:
  1586. density = _Parameters [PARAMETER_SNOW_DENSITY].Value();
  1587. break;
  1588. case PRECIPITATION_ASH:
  1589. density = _Parameters [PARAMETER_ASH_DENSITY].Value();
  1590. break;
  1591. default:
  1592. WWASSERT (false);
  1593. break;
  1594. }
  1595. }
  1596. /***********************************************************************************************
  1597. * WeatherMgrClass::Restore_Precipitation -- *
  1598. * *
  1599. * INPUT: *
  1600. * *
  1601. * OUTPUT: *
  1602. * *
  1603. * WARNINGS: *
  1604. * *
  1605. * HISTORY: *
  1606. * 03/06/01 IML : Created. *
  1607. *=============================================================================================*/
  1608. void WeatherMgrClass::Restore_Precipitation (float ramptime)
  1609. {
  1610. if (CombatManager::I_Am_Server()) {
  1611. _Parameters [PARAMETER_RAIN_DENSITY].Set (ramptime);
  1612. _Parameters [PARAMETER_SNOW_DENSITY].Set (ramptime);
  1613. _Parameters [PARAMETER_ASH_DENSITY].Set (ramptime);
  1614. _PrecipitationOverrideCount--;
  1615. _TheWeatherMgr.Set_Object_Dirty_Bit (NetworkObjectClass::BIT_RARE, true);
  1616. }
  1617. }
  1618. /***********************************************************************************************
  1619. * WeatherMgrClass::Set_Fog_Range -- *
  1620. * *
  1621. * INPUT: *
  1622. * *
  1623. * OUTPUT: *
  1624. * *
  1625. * WARNINGS: *
  1626. * *
  1627. * HISTORY: *
  1628. * 07/12/01 IML : Created. *
  1629. *=============================================================================================*/
  1630. bool WeatherMgrClass::Set_Fog_Range (float startdistance, float enddistance, float ramptime)
  1631. {
  1632. if ((startdistance >= 0.0f) && (enddistance >= startdistance)) {
  1633. _Parameters [PARAMETER_FOG_START_DISTANCE].Set (startdistance, ramptime, false);
  1634. _Parameters [PARAMETER_FOG_END_DISTANCE].Set (enddistance, ramptime, false);
  1635. return (true);
  1636. }
  1637. return (false);
  1638. }
  1639. /***********************************************************************************************
  1640. * WeatherMgrClass::Get_Fog_Range -- *
  1641. * *
  1642. * INPUT: *
  1643. * *
  1644. * OUTPUT: *
  1645. * *
  1646. * WARNINGS: *
  1647. * *
  1648. * HISTORY: *
  1649. * 07/12/01 IML : Created. *
  1650. *=============================================================================================*/
  1651. void WeatherMgrClass::Get_Fog_Range (float &startdistance, float &enddistance)
  1652. {
  1653. startdistance = _Parameters [PARAMETER_FOG_START_DISTANCE].Value();
  1654. enddistance = _Parameters [PARAMETER_FOG_END_DISTANCE].Value();
  1655. }
  1656. /***********************************************************************************************
  1657. * WeatherMgrClass::Update -- *
  1658. * *
  1659. * INPUT: *
  1660. * *
  1661. * OUTPUT: *
  1662. * *
  1663. * WARNINGS: *
  1664. * *
  1665. * HISTORY: *
  1666. * 03/06/01 IML : Created. *
  1667. *=============================================================================================*/
  1668. void WeatherMgrClass::Update (PhysicsSceneClass *scene, CameraClass *camera)
  1669. {
  1670. float time;
  1671. bool windmodified, fogmodified;
  1672. time = WW3D::Get_Frame_Time() * 0.001f;
  1673. // Update wind.
  1674. windmodified = _Parameters [PARAMETER_WIND_HEADING].Update (time, _WindOverrideCount > 0);
  1675. windmodified |= _Parameters [PARAMETER_WIND_SPEED].Update (time, _WindOverrideCount > 0);
  1676. windmodified |= _Parameters [PARAMETER_WIND_VARIABILITY].Update (time, _WindOverrideCount > 0);
  1677. if (_Wind != NULL) {
  1678. if (windmodified) {
  1679. _Wind->Set (_Parameters [PARAMETER_WIND_HEADING].Value(), _Parameters [PARAMETER_WIND_SPEED].Value(), _Parameters [PARAMETER_WIND_VARIABILITY].Value());
  1680. }
  1681. // Optimization: if there is nothing to update, can safely remove the wind.
  1682. if (!_Wind->Update()) {
  1683. delete _Wind;
  1684. _Wind = NULL;
  1685. }
  1686. } else {
  1687. // Optimization: only create wind if speed is non-zero.
  1688. if (_Parameters [PARAMETER_WIND_SPEED].Value() > 0.0f) {
  1689. _Wind = new WindClass (_Parameters [PARAMETER_WIND_HEADING].Value(), _Parameters [PARAMETER_WIND_SPEED].Value(), _Parameters [PARAMETER_WIND_VARIABILITY].Value(), _SoundEnvironment);
  1690. }
  1691. }
  1692. for (int p = PRECIPITATION_FIRST; p < PRECIPITATION_COUNT; p++) {
  1693. WeatherParameterClass *parameterptr = NULL;
  1694. bool modified;
  1695. switch (p) {
  1696. case PRECIPITATION_RAIN:
  1697. parameterptr = &_Parameters [PARAMETER_RAIN_DENSITY];
  1698. break;
  1699. case PRECIPITATION_SNOW:
  1700. parameterptr = &_Parameters [PARAMETER_SNOW_DENSITY];
  1701. break;
  1702. case PRECIPITATION_ASH:
  1703. parameterptr = &_Parameters [PARAMETER_ASH_DENSITY];
  1704. break;
  1705. default:
  1706. WWASSERT (false);
  1707. break;
  1708. }
  1709. modified = parameterptr->Update (time, _PrecipitationOverrideCount > 0);
  1710. if (_Precipitation [p] != NULL) {
  1711. if (modified) {
  1712. _Precipitation [p]->Set_Density (parameterptr->Value());
  1713. }
  1714. // Optimization: if there is nothing to update, can safely remove the weather system.
  1715. if (!_Precipitation [p]->Update (_Wind, camera->Get_Position())) {
  1716. _Precipitation [p]->Remove();
  1717. REF_PTR_RELEASE (_Precipitation [p]);
  1718. }
  1719. } else {
  1720. // Optimization: only create weather if density is non-zero.
  1721. if (parameterptr->Value() > 0.0f) {
  1722. // Don't bother on dedicated servers.
  1723. if (!CombatManager::I_Am_Only_Server()) {
  1724. switch (p) {
  1725. case PRECIPITATION_RAIN:
  1726. _Precipitation [p] = NEW_REF (RainSystemClass, (scene, parameterptr->Value(), _Wind, _SoundEnvironment, _Prime));
  1727. break;
  1728. case PRECIPITATION_SNOW:
  1729. _Precipitation [p] = NEW_REF (SnowSystemClass, (scene, parameterptr->Value(), _Wind, _Prime));
  1730. break;
  1731. case PRECIPITATION_ASH:
  1732. _Precipitation [p] = NEW_REF (AshSystemClass, (scene, parameterptr->Value(), _Wind, _Prime));
  1733. break;
  1734. default:
  1735. WWASSERT (false);
  1736. break;
  1737. }
  1738. scene->Add_Render_Object (_Precipitation [p]);
  1739. }
  1740. }
  1741. }
  1742. }
  1743. fogmodified = _Parameters [PARAMETER_FOG_START_DISTANCE].Update (time, false);
  1744. fogmodified |= _Parameters [PARAMETER_FOG_END_DISTANCE].Update (time, false);
  1745. if (Is_Dirty() || fogmodified) {
  1746. scene->Set_Fog_Enable (_FogEnabled);
  1747. scene->Set_Fog_Range (_Parameters [PARAMETER_FOG_START_DISTANCE].Value(), _Parameters [PARAMETER_FOG_END_DISTANCE].Value());
  1748. }
  1749. if (CombatManager::I_Am_Server()) {
  1750. // Server precipitation can only be primed on the first update iteration of a level.
  1751. _Prime = false;
  1752. } else {
  1753. // Clients can be primed right up until the first import from the server.
  1754. if (_Imported) _Prime = false;
  1755. }
  1756. // Everything necessary has been updated. Clear the dirty flag.
  1757. Set_Dirty (false);
  1758. }
  1759. /***********************************************************************************************
  1760. * WeatherMgrClass::Save -- *
  1761. * *
  1762. * INPUT: *
  1763. * *
  1764. * OUTPUT: *
  1765. * *
  1766. * WARNINGS: *
  1767. * *
  1768. * HISTORY: *
  1769. * 03/02/01 IML : Created. *
  1770. *=============================================================================================*/
  1771. #define WRITE_PARAMETER(varname) \
  1772. WRITE_MICRO_CHUNK (csave, VARID_ ## varname ## _CURRENT_VALUE, _Parameters [PARAMETER_ ## varname ##].CurrentValue); \
  1773. WRITE_MICRO_CHUNK (csave, VARID_ ## varname ## _NORMAL_VALUE, _Parameters [PARAMETER_ ## varname ##].NormalValue); \
  1774. WRITE_MICRO_CHUNK (csave, VARID_ ## varname ## _NORMAL_TARGET, _Parameters [PARAMETER_ ## varname ##].NormalTarget); \
  1775. WRITE_MICRO_CHUNK (csave, VARID_ ## varname ## _NORMAL_DURATION, _Parameters [PARAMETER_ ## varname ##].NormalDuration); \
  1776. WRITE_MICRO_CHUNK (csave, VARID_ ## varname ## _OVERRIDE_TARGET, _Parameters [PARAMETER_ ## varname ##].OverrideTarget); \
  1777. WRITE_MICRO_CHUNK (csave, VARID_ ## varname ## _OVERRIDE_DURATION, _Parameters [PARAMETER_ ## varname ##].OverrideDuration)
  1778. bool WeatherMgrClass::Save (ChunkSaveClass &csave)
  1779. {
  1780. csave.Begin_Chunk (CHUNKID_MICRO_CHUNKS);
  1781. csave.End_Chunk ();
  1782. Save_Dynamic (csave);
  1783. return (true);
  1784. }
  1785. /***********************************************************************************************
  1786. * WeatherMgrClass::Save_Dynamic -- *
  1787. * *
  1788. * INPUT: *
  1789. * *
  1790. * OUTPUT: *
  1791. * *
  1792. * WARNINGS: *
  1793. * *
  1794. * HISTORY: *
  1795. * 03/02/01 IML : Created. *
  1796. *=============================================================================================*/
  1797. bool WeatherMgrClass::Save_Dynamic (ChunkSaveClass &csave)
  1798. {
  1799. csave.Begin_Chunk (CHUNKID_DYNAMIC_MICRO_CHUNKS);
  1800. WRITE_PARAMETER (WIND_HEADING);
  1801. WRITE_PARAMETER (WIND_SPEED);
  1802. WRITE_PARAMETER (WIND_VARIABILITY);
  1803. WRITE_PARAMETER (RAIN_DENSITY);
  1804. WRITE_PARAMETER (SNOW_DENSITY);
  1805. WRITE_PARAMETER (ASH_DENSITY);
  1806. WRITE_MICRO_CHUNK (csave, VARID_WIND_OVERRIDE_COUNT, _WindOverrideCount);
  1807. WRITE_MICRO_CHUNK (csave, VARID_PRECIPITATION_OVERRIDE_COUNT, _PrecipitationOverrideCount);
  1808. WRITE_MICRO_CHUNK (csave, VARID_FOG_ENABLED, _FogEnabled);
  1809. WRITE_PARAMETER (FOG_START_DISTANCE);
  1810. WRITE_PARAMETER (FOG_END_DISTANCE);
  1811. csave.End_Chunk ();
  1812. return (true);
  1813. }
  1814. /***********************************************************************************************
  1815. * WeatherMgrClass::Load -- *
  1816. * *
  1817. * INPUT: *
  1818. * *
  1819. * OUTPUT: *
  1820. * *
  1821. * WARNINGS: *
  1822. * *
  1823. * HISTORY: *
  1824. * 03/02/01 IML : Created. *
  1825. *=============================================================================================*/
  1826. bool WeatherMgrClass::Load (ChunkLoadClass &cload)
  1827. {
  1828. WWMEMLOG (MEM_GAMEDATA);
  1829. bool retval = true;
  1830. while (cload.Open_Chunk ()) {
  1831. switch (cload.Cur_Chunk_ID ()) {
  1832. case CHUNKID_MICRO_CHUNKS:
  1833. retval &= Load_Micro_Chunks (cload);
  1834. break;
  1835. case CHUNKID_DYNAMIC_MICRO_CHUNKS:
  1836. retval &= Load_Dynamic_Micro_Chunks (cload);
  1837. break;
  1838. }
  1839. cload.Close_Chunk ();
  1840. }
  1841. return (retval);
  1842. }
  1843. /***********************************************************************************************
  1844. * WeatherMgrClass::Load_Micro_Chunks -- *
  1845. * *
  1846. * INPUT: *
  1847. * *
  1848. * OUTPUT: *
  1849. * *
  1850. * WARNINGS: *
  1851. * *
  1852. * HISTORY: *
  1853. * 03/02/01 IML : Created. *
  1854. *=============================================================================================*/
  1855. #define READ_PARAMETER(varname) \
  1856. READ_MICRO_CHUNK (cload, VARID_ ## varname ## _CURRENT_VALUE, _Parameters [PARAMETER_ ## varname ##].CurrentValue); \
  1857. READ_MICRO_CHUNK (cload, VARID_ ## varname ## _NORMAL_VALUE, _Parameters [PARAMETER_ ## varname ##].NormalValue); \
  1858. READ_MICRO_CHUNK (cload, VARID_ ## varname ## _NORMAL_TARGET, _Parameters [PARAMETER_ ## varname ##].NormalTarget); \
  1859. READ_MICRO_CHUNK (cload, VARID_ ## varname ## _NORMAL_DURATION, _Parameters [PARAMETER_ ## varname ##].NormalDuration); \
  1860. READ_MICRO_CHUNK (cload, VARID_ ## varname ## _OVERRIDE_TARGET, _Parameters [PARAMETER_ ## varname ##].OverrideTarget); \
  1861. READ_MICRO_CHUNK (cload, VARID_ ## varname ## _OVERRIDE_DURATION, _Parameters [PARAMETER_ ## varname ##].OverrideDuration)
  1862. bool WeatherMgrClass::Load_Micro_Chunks (ChunkLoadClass &cload)
  1863. {
  1864. // Read weather micro chunk.
  1865. while (cload.Open_Micro_Chunk ()) {
  1866. cload.Close_Micro_Chunk ();
  1867. }
  1868. return (true);
  1869. }
  1870. /***********************************************************************************************
  1871. * WeatherMgrClass::Load_Dynamic -- *
  1872. * *
  1873. * INPUT: *
  1874. * *
  1875. * OUTPUT: *
  1876. * *
  1877. * WARNINGS: *
  1878. * *
  1879. * HISTORY: *
  1880. * 03/02/01 IML : Created. *
  1881. *=============================================================================================*/
  1882. bool WeatherMgrClass::Load_Dynamic (ChunkLoadClass &cload)
  1883. {
  1884. WWMEMLOG (MEM_GAMEDATA);
  1885. bool retval = true;
  1886. while (cload.Open_Chunk ()) {
  1887. switch (cload.Cur_Chunk_ID ()) {
  1888. case CHUNKID_DYNAMIC_MICRO_CHUNKS:
  1889. retval &= Load_Dynamic_Micro_Chunks (cload);
  1890. break;
  1891. }
  1892. cload.Close_Chunk ();
  1893. }
  1894. return (retval);
  1895. }
  1896. /***********************************************************************************************
  1897. * WeatherMgrClass::Load_Dynamic_Micro_Chunks -- *
  1898. * *
  1899. * INPUT: *
  1900. * *
  1901. * OUTPUT: *
  1902. * *
  1903. * WARNINGS: *
  1904. * *
  1905. * HISTORY: *
  1906. * 03/02/01 IML : Created. *
  1907. *=============================================================================================*/
  1908. bool WeatherMgrClass::Load_Dynamic_Micro_Chunks (ChunkLoadClass &cload)
  1909. {
  1910. // Read weather micro chunk.
  1911. while (cload.Open_Micro_Chunk ()) {
  1912. switch (cload.Cur_Micro_Chunk_ID ()) {
  1913. READ_PARAMETER (WIND_HEADING);
  1914. READ_PARAMETER (WIND_SPEED);
  1915. READ_PARAMETER (WIND_VARIABILITY);
  1916. READ_PARAMETER (RAIN_DENSITY);
  1917. READ_PARAMETER (SNOW_DENSITY);
  1918. READ_PARAMETER (ASH_DENSITY);
  1919. READ_MICRO_CHUNK (cload, VARID_WIND_OVERRIDE_COUNT, _WindOverrideCount);
  1920. READ_MICRO_CHUNK (cload, VARID_PRECIPITATION_OVERRIDE_COUNT, _PrecipitationOverrideCount);
  1921. READ_MICRO_CHUNK (cload, VARID_FOG_ENABLED, _FogEnabled);
  1922. READ_PARAMETER (FOG_START_DISTANCE);
  1923. READ_PARAMETER (FOG_END_DISTANCE);
  1924. }
  1925. cload.Close_Micro_Chunk ();
  1926. }
  1927. return (true);
  1928. }
  1929. /***********************************************************************************************
  1930. * WeatherMgrClass::Export_Rare -- *
  1931. * *
  1932. * INPUT: *
  1933. * *
  1934. * OUTPUT: *
  1935. * *
  1936. * WARNINGS: *
  1937. * *
  1938. * HISTORY: *
  1939. * 03/02/01 IML : Created. *
  1940. *=============================================================================================*/
  1941. #define EXPORT_PARAMETER(object, varname) \
  1942. object.Add (_Parameters [PARAMETER_ ## varname ##].NormalTarget); \
  1943. object.Add (_Parameters [PARAMETER_ ## varname ##].NormalDuration); \
  1944. object.Add (_Parameters [PARAMETER_ ## varname ##].OverrideTarget); \
  1945. object.Add (_Parameters [PARAMETER_ ## varname ##].OverrideDuration)
  1946. void WeatherMgrClass::Export_Rare (BitStreamClass &packet)
  1947. {
  1948. WWASSERT (CombatManager::I_Am_Server());
  1949. EXPORT_PARAMETER (packet, WIND_HEADING);
  1950. EXPORT_PARAMETER (packet, WIND_SPEED);
  1951. EXPORT_PARAMETER (packet, WIND_VARIABILITY);
  1952. EXPORT_PARAMETER (packet, RAIN_DENSITY);
  1953. EXPORT_PARAMETER (packet, SNOW_DENSITY);
  1954. EXPORT_PARAMETER (packet, ASH_DENSITY);
  1955. packet.Add (_WindOverrideCount);
  1956. packet.Add (_PrecipitationOverrideCount);
  1957. }
  1958. /***********************************************************************************************
  1959. * WeatherMgrClass::Import_Rare -- *
  1960. * *
  1961. * INPUT: *
  1962. * *
  1963. * OUTPUT: *
  1964. * *
  1965. * WARNINGS: *
  1966. * *
  1967. * HISTORY: *
  1968. * 03/02/01 IML : Created. *
  1969. *=============================================================================================*/
  1970. #define IMPORT_PARAMETER(object, varname) \
  1971. object.Get (_Parameters [PARAMETER_ ## varname ##].NormalTarget); \
  1972. object.Get (_Parameters [PARAMETER_ ## varname ##].NormalDuration); \
  1973. object.Get (_Parameters [PARAMETER_ ## varname ##].OverrideTarget); \
  1974. object.Get (_Parameters [PARAMETER_ ## varname ##].OverrideDuration)
  1975. void WeatherMgrClass::Import_Rare (BitStreamClass &packet)
  1976. {
  1977. WWASSERT (CombatManager::I_Am_Client());
  1978. IMPORT_PARAMETER (packet, WIND_HEADING);
  1979. IMPORT_PARAMETER (packet, WIND_SPEED);
  1980. IMPORT_PARAMETER (packet, WIND_VARIABILITY);
  1981. IMPORT_PARAMETER (packet, RAIN_DENSITY);
  1982. IMPORT_PARAMETER (packet, SNOW_DENSITY);
  1983. IMPORT_PARAMETER (packet, ASH_DENSITY);
  1984. packet.Get (_WindOverrideCount);
  1985. packet.Get (_PrecipitationOverrideCount);
  1986. // Flag that weather data has been imported.
  1987. _Imported = true;
  1988. }
  1989. #undef EXPORT_PARAMETER
  1990. #undef IMPORT_PARAMETER
  1991. #undef READ_PARAMETER
  1992. #undef WRITE_PARAMETER