/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*************************************************************************** *** 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 *** *************************************************************************** * * * Project Name : G * * * * $Archive:: /VSS_Sync/ww3d2/part_emt.h $* * * * $Author:: Vss_sync $* * * * $Modtime:: 10/26/01 2:56p $* * * * $Revision:: 11 $* * * *-------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #if defined(_MSC_VER) #pragma once #endif #ifndef PART_EMT_H #define PART_EMT_H #include "rendobj.h" #include "random.h" #include "part_buf.h" #include "quat.h" #include "w3d_file.h" #include "w3derr.h" #include "v3_rnd.h" // Forward declarations class ParticleEmitterDefClass; class ChunkSaveClass; struct NewParticleStruct; /* ** Properties / Keyframes: These are for properties of the particles which ** are a straight function of their age, such as color and size. These are ** typically defined by a series of "keyframes", which are property / age ** pairs. Linear interpolation is performed between the keyframes. */ template struct ParticlePropertyStruct { T Start; T Rand; unsigned int NumKeyFrames; float * KeyTimes; T * Values; }; /* ** Utility function for copying the contents of one keyframe struct into another. */ template __inline void Copy_Emitter_Property_Struct ( ParticlePropertyStruct &dest, const ParticlePropertyStruct &src ) { dest.Start = src.Start; dest.Rand = src.Rand; dest.NumKeyFrames = src.NumKeyFrames; dest.KeyTimes = NULL; dest.Values = NULL; if (dest.NumKeyFrames > 0) { dest.KeyTimes = new float[dest.NumKeyFrames]; dest.Values = new T[dest.NumKeyFrames]; ::memcpy (dest.KeyTimes, src.KeyTimes, sizeof (float) * dest.NumKeyFrames); ::memcpy (dest.Values, src.Values, sizeof (T) * dest.NumKeyFrames); } return ; } /** ** ParticleEmitterClass: This is a renderobject which emits particles ** regularly. The particles emitted go into a different renderobject, a ** ParticleBufferClass (there is one particle buffer per particle emitter). ** This separation is so that the bounding volumes of the particle group and ** the object containing the emitter (emitters will typically be inserted ** into a hierarchy object or some such) will remain separate. */ class ParticleEmitterClass : public RenderObjClass { public: // Note: all time/velocity/acceleration quantities use seconds (converted to ms internally) ParticleEmitterClass(float emit_rate, unsigned int burst_size, Vector3Randomizer *pos_rnd, Vector3 base_vel, Vector3Randomizer *vel_rnd, float out_vel, float vel_inherit_factor, ParticlePropertyStruct &color, ParticlePropertyStruct &opacity, ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frames, ParticlePropertyStruct &blur_times, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader = ShaderClass::_PresetAdditiveSpriteShader, int max_particles = 0, int max_buffer_size = -1, bool pingpong = false, int render_mode = W3D_EMITTER_RENDER_MODE_TRI_PARTICLES, int frame_mode = W3D_EMITTER_FRAME_MODE_1x1, const W3dEmitterLinePropertiesStruct * line_props = NULL); ParticleEmitterClass(const ParticleEmitterClass & src); ParticleEmitterClass & operator = (const ParticleEmitterClass &); virtual ~ParticleEmitterClass(void); // Creation/serialization methods virtual RenderObjClass * Clone(void) const; static ParticleEmitterClass * Create_From_Definition (const ParticleEmitterDefClass &definition); ParticleEmitterDefClass * Build_Definition (void) const; WW3DErrorType Save (ChunkSaveClass &chunk_save) const; // Identification methods virtual int Class_ID (void) const { return CLASSID_PARTICLEEMITTER; } virtual const char * Get_Name (void) const { return NameString; } virtual void Set_Name (const char *pname); virtual void Notify_Added(SceneClass * scene); virtual void Notify_Removed(SceneClass * scene); // Update particle state and draw the particles. virtual void Render(RenderInfoClass & rinfo) { } virtual void Restart(void); // Scales the size of all particles and effects positions/velocities of // particles emitted after the Scale() call (but not before) virtual void Scale(float scale); // Put particle buffer in scene if this is the first time (clunky code // - hopefully can be rewritten more cleanly in future)... virtual void On_Frame_Update(void); virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const { sphere.Center.Set(0,0,0); sphere.Radius = 0; } virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const { box.Center.Set(0,0,0); box.Extent.Set(0,0,0); } virtual void Set_Hidden(int onoff) { RenderObjClass::Set_Hidden (onoff); Update_On_Visibilty (); } virtual void Set_Visible(int onoff) { RenderObjClass::Set_Visible (onoff); Update_On_Visibilty (); } virtual void Set_Animation_Hidden(int onoff) { RenderObjClass::Set_Animation_Hidden (onoff); Update_On_Visibilty (); } virtual void Set_Force_Visible(int onoff) { RenderObjClass::Set_Force_Visible (onoff); Update_On_Visibilty (); } virtual void Set_LOD_Bias(float bias) { if (Buffer) Buffer->Set_LOD_Bias(bias); } // These are not part of the renderobject interface: // You can use Reset() to re-cycle a particle emitter. Make sure its transform is // set up before you call Reset(). void Reset(void); void Start(void); void Stop(void); bool Is_Stopped(void); // Change starting position/velocity/acceleration parameters: void Set_Position_Randomizer(Vector3Randomizer *rand); void Set_Velocity_Randomizer(Vector3Randomizer *rand); void Set_Base_Velocity(const Vector3& base_vel); void Set_Outwards_Velocity(float out_vel); void Set_Velocity_Inheritance_Factor(float inh_factor); void Set_Acceleration (const Vector3 &acceleration) { if (Buffer != NULL) Buffer->Set_Acceleration (acceleration/1000000.0f); } // Change visual properties of emitter / buffer: void Reset_Colors(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Colors(new_props); } void Reset_Opacity(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Opacity(new_props); } void Reset_Size(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Size(new_props); } void Reset_Rotations(ParticlePropertyStruct &new_props, float orient_rnd) { if (Buffer) Buffer->Reset_Rotations(new_props, orient_rnd); } void Reset_Frames(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Frames(new_props); } void Reset_Blur_Times(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Blur_Times(new_props); } // Change emission/burst rate, or tell the emitter to emit a one-time burst. // NOTE: default buffer size fits the emission/burst rate that the emitter was created with. // Setting the rates too high will cause particles to be prematurely killed (to make space // for new ones) - setting it too low will cause wasted space in the particle buffer. // These problems can be avoided by specifying the desired buffer size in the emitter CTor. void Set_Emission_Rate (float rate) { EmitRate = rate > 0.0f ? (unsigned int)(1000.0f / rate) : 1000U; } void Set_Burst_Size (int size) { BurstSize = size != 0 ? size : 1; } void Set_One_Time_Burst(int size) { OneTimeBurstSize = size != 0 ? size : 1; OneTimeBurst = true; } // Emit particles (put in particle buffer). This is called by the // particle buffer On_Frame_Update() function to avoid order dependence. virtual void Emit(void); // Buffer control ParticleBufferClass *Peek_Buffer( void ) { return Buffer; } void Buffer_Scene_Not_Needed( void ) { BufferSceneNeeded = false; } void Remove_Buffer_From_Scene (void) { Buffer->Remove (); FirstTime = true; BufferSceneNeeded = true; } // from RenderObj... virtual bool Is_Complete(void) { return IsComplete; } // Auto deletion behavior controls bool Is_Remove_On_Complete_Enabled(void) { return RemoveOnComplete; } void Enable_Remove_On_Complete(bool onoff) { RemoveOnComplete = onoff; } static bool Default_Remove_On_Complete (void) { return DefaultRemoveOnComplete; } static void Set_Default_Remove_On_Complete (bool onoff) { DefaultRemoveOnComplete = onoff; } // // Virtual accessors (used for type specific information) // virtual int Get_User_Type (void) const { return EMITTER_TYPEID_DEFAULT; } virtual const char * Get_User_String (void) const { return NULL; } // // Inline accessors. // These methods are provided as a means to get the emitter's settings. // int Get_Render_Mode (void) const { return Buffer->Get_Render_Mode(); } int Get_Frame_Mode (void) const { return Buffer->Get_Frame_Mode(); } float Get_Particle_Size (void) const { return Buffer->Get_Particle_Size(); } Vector3 Get_Acceleration (void) const { return Buffer->Get_Acceleration (); } float Get_Lifetime (void) const { return Buffer->Get_Lifetime (); } Vector3 Get_End_Color (void) const { return Buffer->Get_End_Color (); } float Get_End_Opacity (void) const { return Buffer->Get_End_Opacity (); } TextureClass * Get_Texture (void) const { return Buffer->Get_Texture (); } void Set_Texture (TextureClass *tex) { Buffer->Set_Texture(tex); } float Get_Fade_Time (void) const { return Buffer->Get_Fade_Time (); } Vector3 Get_Start_Color (void) const { return Buffer->Get_Start_Color(); } float Get_Start_Opacity (void) const { return Buffer->Get_Start_Opacity(); } float Get_Position_Random (void) const { return PosRand ? PosRand->Get_Maximum_Extent() : 0.0f; } //float Get_Velocity_Random (void) const { return VelRand ? (VelRand->Get_Maximum_Extent() * 1000.0f) : 0.0f; } float Get_Emission_Rate (void) const { return 1000.0f / float(EmitRate); } int Get_Burst_Size (void) const { return BurstSize; } int Get_Max_Particles (void) const { return MaxParticles; } Vector3 Get_Start_Velocity (void) const { return BaseVel * 1000.0F; } Vector3Randomizer * Get_Creation_Volume (void) const; Vector3Randomizer * Get_Velocity_Random (void) const; float Get_Outwards_Vel (void) const { return OutwardVel * 1000.0F; } float Get_Velocity_Inherit (void) const{ return VelInheritFactor; } ShaderClass Get_Shader (void) const { return Buffer->Get_Shader (); } // Note: Caller IS RESPONSIBLE for freeing any memory allocated by these calls void Get_Color_Key_Frames (ParticlePropertyStruct &colors) const { Buffer->Get_Color_Key_Frames (colors); } void Get_Opacity_Key_Frames (ParticlePropertyStruct &opacities) const { Buffer->Get_Opacity_Key_Frames (opacities); } void Get_Size_Key_Frames (ParticlePropertyStruct &sizes) const { Buffer->Get_Size_Key_Frames (sizes); } void Get_Rotation_Key_Frames (ParticlePropertyStruct &rotations) const { Buffer->Get_Rotation_Key_Frames (rotations); } void Get_Frame_Key_Frames (ParticlePropertyStruct &frames) const { Buffer->Get_Frame_Key_Frames (frames); } void Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const { Buffer->Get_Blur_Time_Key_Frames (blurtimes); } float Get_Initial_Orientation_Random (void) const { return Buffer->Get_Initial_Orientation_Random(); } // Line rendering accessors int Get_Line_Texture_Mapping_Mode(void) const { return Buffer->Get_Line_Texture_Mapping_Mode(); } int Is_Merge_Intersections(void) const { return Buffer->Is_Merge_Intersections(); } int Is_Freeze_Random(void) const { return Buffer->Is_Freeze_Random(); } int Is_Sorting_Disabled(void) const { return Buffer->Is_Sorting_Disabled(); } int Are_End_Caps_Enabled(void) const { return Buffer->Are_End_Caps_Enabled(); } int Get_Subdivision_Level(void) const { return Buffer->Get_Subdivision_Level(); } float Get_Noise_Amplitude(void) const { return Buffer->Get_Noise_Amplitude(); } float Get_Merge_Abort_Factor(void) const { return Buffer->Get_Merge_Abort_Factor(); } float Get_Texture_Tile_Factor(void) const { return Buffer->Get_Texture_Tile_Factor(); } Vector2 Get_UV_Offset_Rate(void) const { return Buffer->Get_UV_Offset_Rate(); } // Global debugging option for disabling all particle emission #ifdef WWDEBUG static void Disable_All_Emitters(bool onoff) { DebugDisable = onoff; } static bool Are_Emitters_Disabled(void) { return DebugDisable; } #endif protected: // Used to build a list of filenames this emitter is dependent on virtual void Add_Dependencies_To_List (DynamicVectorClass &file_list, bool textures_only = false); // This method is called each time the visiblity state of the emitter changes. virtual void Update_On_Visibilty (void); private: // Collision sphere is a point - emitter emits also when not visible, // so this is only important to avoid affecting the collision spheres // of heirarchy objects into which the emitter is inserted. virtual void Update_Cached_Bounding_Volumes(void) const; // Create new particles and pass them to the particle buffer. Receives // the end-of-interval quaternion and origin and interpolates between // them and PrevQ/PrevOrig to get the transform to apply to each // particle's position and velocity as it is created. New particles are // placed into a circular queue for processing by the particle buffer. void Create_New_Particles(const Quaternion & curr_quat, const Vector3 & curr_orig); // Initialize one new particle at the given NewParticleStruct // address, with the given age and emitter transform (expressed as a // quaternion and origin vector). (must check if address is NULL). void Initialize_Particle(NewParticleStruct * newpart, unsigned int age, const Quaternion & quat, const Vector3 & orig); unsigned int EmitRate; // Emission rate (1/milliseconds). unsigned int BurstSize; // Burst size (how many particles in each emission). unsigned int OneTimeBurstSize; // Burst size for a one-time burst. bool OneTimeBurst; // Do we need to do a one-time burst? Vector3Randomizer * PosRand; // Position randomizer pointer (may be NULL). Vector3 BaseVel; // Base initial emission velocity. Vector3Randomizer * VelRand; // Velocity randomizer pointer (may be NULL). float OutwardVel; // Size of outwards velocity. float VelInheritFactor; // Affects emitter vel. inherited by particles. unsigned int EmitRemain; // Millisecond emitter remainder. Quaternion PrevQ; // Previous quaternion (for interpolation). Vector3 PrevOrig; // Previous origin (for interpolation). bool Active; // Is the emitter currently Active? bool FirstTime; // Has it been rendered before? bool BufferSceneNeeded;// Does the buffer need a scene? int ParticlesLeft; // Particles left to emit int MaxParticles; // Total particles to emit bool IsComplete; // Completed Emissions char * NameString; char * UserString; bool RemoveOnComplete; // Should this emitter destroy itself when it completes? bool IsInScene; // This pointer is used only for sending new particles to the particle // buffer and for informing the buffer when the emitter is destroyed. ParticleBufferClass * Buffer; // This is used to set the global behavior of emitters... // Should they be removed from the scene when they complete their // emissions, or should they stay in the scene. (For editing purposes) static bool DefaultRemoveOnComplete; // This setting is used in debug and profile builds to disable // all particle emitters. static bool DebugDisable; }; #endif // PART_EMT_H