/* ** 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_buf.h $* * * * $Author:: Vss_sync $* * * * $Modtime:: 10/26/01 2:56p $* * * * $Revision:: 10 $* * * *-------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #if defined(_MSC_VER) #pragma once #endif #ifndef PART_BUF_H #define PART_BUF_H #include "rendobj.h" #include "pointgr.h" #include "seglinerenderer.h" #include "linegrp.h" class ParticleEmitterClass; template struct ParticlePropertyStruct; /** ** NewParticleStruct: structure for passing new particles from the particle ** emitter to the particle buffer. Since the emitter always continues ** emitting (unless stopped) but the buffer may not update for long periods, ** the emitter may emit more particles than the buffer can contain. However, ** in this case the older particles can be ignored. Therefore ** ParticleBufferClass contains a circular buffer of NewParticleStructs, and ** new ones overwrite the oldest in the case of overflows. */ struct NewParticleStruct { Vector3 Position; // Particle position in worldspace. Vector3 Velocity; // Particle velocity in worldspace. unsigned int TimeStamp; // Millisecond time at creation. // These are needed by DynamicVectorClass (will probably never be used). bool operator != (const NewParticleStruct & p) { return (p.TimeStamp != TimeStamp) || (p.Position != Position); } bool operator == (const NewParticleStruct & p) { return (p.TimeStamp == TimeStamp) && (p.Position == Position); } }; /** ** ParticleBufferClass: This is a renderobject which contains the particles ** emitted by a given renderer. The particle emitter is a different ** renderobject, a ParticleEmitterClass (there is one particle emitter per ** particle buffer). 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 ParticleBufferClass : public RenderObjClass { public: ParticleBufferClass(ParticleEmitterClass *emitter, unsigned int buffer_size, ParticlePropertyStruct &color, ParticlePropertyStruct &opacity, ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frame, ParticlePropertyStruct &blurtime, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader, bool pingpong, int render_mode, int frame_mode, const W3dEmitterLinePropertiesStruct * line_props); ParticleBufferClass(const ParticleBufferClass & src); ParticleBufferClass & operator = (const ParticleBufferClass &); virtual ~ParticleBufferClass(void); /* ** RenderObjClass Interface: */ virtual RenderObjClass * Clone(void) const; virtual int Class_ID(void) const { return CLASSID_PARTICLEBUFFER; } virtual int Get_Num_Polys(void) const; int Get_Particle_Count(void) const; // Update particle state and draw the particles. virtual void Render(RenderInfoClass & rinfo); // Scales the size of the individual particles but doesn't affect their // position (and therefore the size of the particle system as a whole) virtual void Scale(float scale); // The particle buffer never receives a Set_Transform/Position call, // evem though its bounding volume changes. Since bounding volume // invalidations ordinarily occur when these functions are called, // the cached bounding volumes will not be invalidated unless we do // it elsewhere (such as here). We also need to call the particle // emitter's Emit() function (done here to avoid order dependence). virtual void On_Frame_Update(void); virtual void Notify_Added(SceneClass * scene); virtual void Notify_Removed(SceneClass * scene); virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const; virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const; ///////////////////////////////////////////////////////////////////////////// // Render Object Interface - Predictive LOD ///////////////////////////////////////////////////////////////////////////// virtual void Prepare_LOD(CameraClass &camera); virtual void Increment_LOD(void); virtual void Decrement_LOD(void); virtual float Get_Cost(void) const; virtual float Get_Value(void) const; virtual float Get_Post_Increment_Value(void) const; virtual void Set_LOD_Level(int lod); virtual int Get_LOD_Level(void) const; virtual int Get_LOD_Count(void) const; virtual void Set_LOD_Bias(float bias) { LodBias = MAX(bias, 0.0f); } virtual int Calculate_Cost_Value_Arrays(float screen_area, float *values, float *costs) const; /* ** These members are not part of the RenderObjClass Interface: */ void Reset_Colors(ParticlePropertyStruct &new_props); void Reset_Opacity(ParticlePropertyStruct &new_props); void Reset_Size(ParticlePropertyStruct &new_props); void Reset_Rotations(ParticlePropertyStruct &new_rotations, float orient_rnd); void Reset_Frames(ParticlePropertyStruct &new_frames); void Reset_Blur_Times(ParticlePropertyStruct &new_blur_times); // This informs the buffer that the emitter is dead, so it can release // its pointer to it and be removed itself after all its particles dies // out. void Emitter_Is_Dead(void); // This set's the buffer's current emitter - this should usually be // called only by the emitter's copy constructor after it clones a // buffer. void Set_Emitter(ParticleEmitterClass *emitter); // from RenderObj... virtual bool Is_Complete(void) { return IsEmitterDead && !NonNewNum && !NewNum; } // This adds an uninitialized NewParticleStuct to the new particle // buffer and returns its address so the particle emitter can // initialize it. This is how the emitter sends new particles to the // buffer - it is done this way to avoid needless copying. NewParticleStruct * Add_Uninitialized_New_Particle(void); // Change the acceleration of the particles on the fly void Set_Acceleration (const Vector3 &acceleration) { Accel = acceleration; HasAccel = ((Accel.X != 0) || (Accel.Y != 0) || (Accel.Z != 0)); } // // Inline accessors. // These methods are provided as a means to get the emitter's settings. // int Get_Render_Mode (void) const { return RenderMode; } int Get_Frame_Mode (void) const { return FrameMode; } float Get_Particle_Size (void) const { return SizeKeyFrameValues[0]; } Vector3 Get_Acceleration (void) const { return Accel * 1000000.0F; } float Get_Lifetime (void) const { return (float(MaxAge)) / 1000.0F; } Vector3 Get_Start_Color (void) const { return ColorKeyFrameValues[0]; } float Get_Start_Opacity (void) const { return AlphaKeyFrameValues[0]; } Vector3 Get_End_Color (void) const { return (NumColorKeyFrames > 1) ? ColorKeyFrameValues[NumColorKeyFrames - 1] : ColorKeyFrameValues[0]; } float Get_End_Opacity (void) const { return (NumAlphaKeyFrames > 1) ? AlphaKeyFrameValues[NumAlphaKeyFrames - 1] : AlphaKeyFrameValues[0]; } TextureClass * Get_Texture (void) const; void Set_Texture (TextureClass *tex); float Get_Fade_Time (void) const { return (NumColorKeyFrames > 1) ? (((float)ColorKeyFrameTimes[1]) / 1000.0f) : 0.0f; } ShaderClass Get_Shader (void) const; // // Line rendering properties. These functions will always return // a default value if line rendering is not enabled. // int Get_Line_Texture_Mapping_Mode(void) const; int Is_Merge_Intersections(void) const; int Is_Freeze_Random(void) const; int Is_Sorting_Disabled(void) const; int Are_End_Caps_Enabled(void) const; int Get_Subdivision_Level(void) const; float Get_Noise_Amplitude(void) const; float Get_Merge_Abort_Factor(void) const; float Get_Texture_Tile_Factor(void) const; Vector2 Get_UV_Offset_Rate(void) const; // This is a utility function only meant to be called by the particle emitter. unsigned int Get_Buffer_Size(void) const { return MaxNum; } // Note: Caller IS RESPONSIBLE for freeing any memory allocated by these calls void Get_Color_Key_Frames (ParticlePropertyStruct &colors) const; void Get_Opacity_Key_Frames (ParticlePropertyStruct &opacities) const; void Get_Size_Key_Frames (ParticlePropertyStruct &sizes) const; void Get_Rotation_Key_Frames (ParticlePropertyStruct &rotations) const; void Get_Frame_Key_Frames (ParticlePropertyStruct &frames) const; void Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const; float Get_Initial_Orientation_Random (void) const { return InitialOrientationRandom; } // Total Active Particle Buffer Count static unsigned int Get_Total_Active_Count( void ) { return TotalActiveCount; } // Global control of particle LOD. static void Set_LOD_Max_Screen_Size(int lod_level,float max_screen_size); static float Get_LOD_Max_Screen_Size(int lod_level); protected: virtual void Update_Cached_Bounding_Volumes(void) const; // render the particle system as a collection of particles void Render_Particles(RenderInfoClass & rinfo); // render the particle system as a line void Render_Line(RenderInfoClass & rinfo); // render the particle system as a line group void Render_Line_Group(RenderInfoClass & rinfo); // Update the kinematic particle state. This includes getting new // particles from the new particle queue, updating velocity/position // for any existing particles, killing old ones, and updating // LastUpdateTime. void Update_Kinematic_Particle_State(void); // Update the visual particle state. This includes updating color/size // for all existing particles. Only needs to happen at rendering time. void Update_Visual_Particle_State(void); // Update the bounding box. (Updates the particle state if it needs to). void Update_Bounding_Box(void); // Helper function for Render_Particles and Render_LineGroup void Generate_APT(ShareBufferClass **apt,unsigned int &active_point_count); void Combine_Color_And_Alpha(); // Get new particles from the emitter and write them into the circular // particle buffer, possibly overwriting older particles. Perform // partial-interval upddate on them as well. void Get_New_Particles(void); // Kill all remaining particles which will be above their maxage at the // end of this time interval. void Kill_Old_Particles(void); // Update all living non-new particles according to time elapsed since // last update. void Update_Non_New_Particles(unsigned int elapsed); // Seperate circular buffer used by the emitter to pass new particles. // It is implemented as an array, start and end indices and a count (to // differentiate between completely full and completely empty). NewParticleStruct * NewParticleQueue; unsigned int NewParticleQueueStart; unsigned int NewParticleQueueEnd; int NewParticleQueueCount; // State global to the entire particle buffer. int RenderMode; // rendering mode being used (settings found in w3d_file.h) int FrameMode; // frame mode (settings found in w3d_file.h - 1x1..16x16) Vector3 Accel; // Worldspace acceleration per ms^2. bool HasAccel; // Is the acceleration non-zero? unsigned int MaxAge; // Maximum age in milliseconds. unsigned int LastUpdateTime;// Time at last update. bool IsEmitterDead; float MaxSize; // Used for BBox calculations // Circular buffer implementation. This is actually 2 sequential // circular buffers: one for non-new particles and one for new // particles (the distinction is needed because the two types of // particles are updated differently). // Besides the head/tail indices, a count is used for each buffer to // distinguish between full and empty. unsigned int MaxNum; // Maximum number of particles. unsigned int Start; // Start of existing (non-new) particles. unsigned int End; // End of existing (non-new) particles. unsigned int NewEnd; // End of new particles. int NonNewNum; // Non-new entry count (to know when empty). int NewNum; // New entry count (to know when empty). // Worldspace-aligned bounding box: AABoxClass BoundingBox; bool BoundingBoxDirty; // At least one keyframe must exist for each property (time 0). // If a randomizer is zero and there are no additional keyframes for // that property (or the keyframes are all equal), all the arrays for // that property are NULL (since they will never be used), except for // the Values array which will have one entry (the constant value). // Note that the rotation and orientation properties are different - // only orientation is used in rendering. The rotation data is only // used to compute the orientations. So the condition is different - // if rotation and orientation randomizers, and all rotation keyframes // are all zero, then all of the arrays will be NULL (including the // Values array). unsigned int NumColorKeyFrames; unsigned int * ColorKeyFrameTimes; // 0th entry is always 0 Vector3 * ColorKeyFrameValues; Vector3 * ColorKeyFrameDeltas; unsigned int NumAlphaKeyFrames; unsigned int * AlphaKeyFrameTimes; // 0th entry is always 0 float * AlphaKeyFrameValues; float * AlphaKeyFrameDeltas; unsigned int NumSizeKeyFrames; unsigned int * SizeKeyFrameTimes; // 0th entry is always 0 float * SizeKeyFrameValues; float * SizeKeyFrameDeltas; unsigned int NumRotationKeyFrames; unsigned int * RotationKeyFrameTimes; // 0th entry is always 0 float * RotationKeyFrameValues; // In rotations per millisecond float * HalfRotationKeyFrameDeltas; // (* 0.5f) float * OrientationKeyFrameValues; // Rotation preintegrated to keyframe times unsigned int NumFrameKeyFrames; unsigned int * FrameKeyFrameTimes; // 0th entry is always 0 float * FrameKeyFrameValues; float * FrameKeyFrameDeltas; unsigned int NumBlurTimeKeyFrames; unsigned int * BlurTimeKeyFrameTimes; // 0th entry is always 0 float * BlurTimeKeyFrameValues; float * BlurTimeKeyFrameDeltas; Vector4 DefaultTailDiffuse; // For line group mode, when all the tails are the same color // These tables are indexed by the array position in the particle buffer. // The table size is either the smallest power of two equal or larger // than the buffer size, or MAX_RANDOM_ENTRIES (defined in the .cpp // file - MUST be a power of two), whichever is smaller. Note that if a // randomizer is zero, the table will have one entry (containing zero), // which is why each property has its own NumXXXRandomEntries variable. // If a randomizer is zero and the property has no keyframes, the table // will be NULL since it will never be used (property is constant)). unsigned int NumRandomColorEntriesMinus1; // 2^n - 1 so can be used as a mask also Vector3 * RandomColorEntries; unsigned int NumRandomAlphaEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomAlphaEntries; unsigned int NumRandomSizeEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomSizeEntries; unsigned int NumRandomRotationEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomRotationEntries; unsigned int NumRandomOrientationEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomOrientationEntries; unsigned int NumRandomFrameEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomFrameEntries; unsigned int NumRandomBlurTimeEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomBlurTimeEntries; Vector3 ColorRandom; float OpacityRandom; float SizeRandom; float RotationRandom; float FrameRandom; float BlurTimeRandom; float InitialOrientationRandom; // This object implements particle rendering PointGroupClass * PointGroup; // This object implements line rendering SegLineRendererClass * LineRenderer; // This object implements line group rendering LineGroupClass * LineGroup; // These are shared with the point group. The position, color and alpha // arrays serve double duty: they are used to store and update particle // state and also to pass point information to the point group. The // active point table is used to communicate to the point group which // points are active (it is only used if all are not active).. ShareBufferClass * Position[2]; // Only [0] used unless pingpong enabled ShareBufferClass * Diffuse; // passed into point group ShareBufferClass * Color; ShareBufferClass * Alpha; ShareBufferClass * Size; ShareBufferClass * Frame; ShareBufferClass * UCoord; // Only used for line groups, uses Frame keyframes ShareBufferClass * TailPosition; // Only used for line groups ShareBufferClass * TailDiffuse; // Only used for line groups ShareBufferClass * Orientation; ShareBufferClass * APT; // Do we keep two ping-pong position buffers (for collision and possibly other effects // which need the previous frames position as well as this frames) bool PingPongPosition; // Additional per-particle state: Vector3 * Velocity; // World units per millisecond. unsigned int * TimeStamp; // Millisecond time at creation. // This pointer is used for synchronization - the emitter is called to // add new particles at the start of the buffers render function - to // prevent behavior which is dependent on the relative time order of // the emitter and buffer in the rendering list. ParticleEmitterClass * Emitter; // These are used for decimating particles for LOD purposes. The // threshold is compared vs. an array filled with a random permutation // of the numbers 0 to 15, and any particle whose entry (modulo 16) is // less than the threshold is not rendered. So if DecimationThreshold // is 0 (the minimum value), all particles are rendered - if it is 16 // (the maximum value) none are rendered. unsigned int DecimationThreshold; static const unsigned int PermutationArray[16]; // LOD values unsigned int LodCount; float Cost[17]; // Cost array needs one entry for each LOD level float Value[18]; // Value array needs one more entry than # of LODs float LodBias; // Projected area, used for LOD purposes float ProjectedArea; // Total Active Particle Buffer Count static unsigned int TotalActiveCount; // Static array of screen-size clamps for the 17 possible LOD levels a // particle buffer can have. We can change these from being global to // being per-buffer later if we wish. Default is NO_MAX_SCREEN_SIZE. static float LODMaxScreenSizes[17]; enum TailDiffuseTypeEnum { BLACK, WHITE, SAME_AS_HEAD, SAME_AS_HEAD_ALPHA_ZERO }; // Determine based on shader and texture // what the tail color should be TailDiffuseTypeEnum Determine_Tail_Diffuse(); }; #endif // PART_BUF_H