22/*
33 Particles and Emitters are updated on a variable-timestep basis.
44 Becuase of this, we don't really separate the update and render routines.
5+
6+ TODO: definitely need to set proper sizing modes based on a single axis,
7+ rather than having absolute sizing where each axis is scaled independently
8+
59*/
610
711
@@ -34,50 +38,52 @@ Particle :: struct {
3438 // maybe later add animation states so we can transition to another state on death or contact with certain elements in level
3539}
3640
37- do_particle :: (emitter : Particle_Emitter , using particle : * Particle , delta_time : float ) -> bool {
38- particle .velocity += particle .acceleration * delta_time ;
39- particle .position += particle .velocity * delta_time ;
41+ do_particle :: (using particle : * Particle , emitter : Particle_Emitter , delta_time : float ) -> bool {
42+ velocity += acceleration * delta_time ;
43+ position += velocity * delta_time ;
44+
45+ rotation_velocity += rotation_acceleration * delta_time ;
46+ rotation += rotation_velocity * delta_time ;
4047
41- particle . rotation_velocity += particle . rotation_acceleration * delta_time ;
42- particle . rotation += particle . rotation_velocity * delta_time ;
48+ alpha_velocity += alpha_acceleration * delta_time ;
49+ alpha += alpha_velocity * delta_time ;
4350
44- particle .alpha_velocity += particle .alpha_acceleration * delta_time ;
45- particle .alpha += particle .alpha_velocity * delta_time ;
51+ step_animator (* animator , emitter .animation , Game .render_delta_time );
4652
47- step_animator ( * animator , animation , Game . render_delta_time );
53+ offset := Vec2f .{ 0 , 0 }; // TODO: make parameter
4854
49- clip := rect_to_frect (animation .frames [animator .current ].clip );
50- render_size := size * render_unit ;
51- render_position := ((position + offset ) * tile_render_unit ) - (render_size / 2 );
55+ current_frame := get_current_frame (animator , emitter .animation );
56+ clip := rect_to_frect (current_frame .clip );
57+ render_size := size * Game .tile_render_unit ;
58+ render_position := ((position + offset ) * Game .tile_render_unit ) - (render_size / 2 );
5259
53- lifetime_lerp := clamp ((Game .current_time - spawn_time ) / lifetime , 0 , 1 );
60+ lifetime_lerp := clamp ((Game .time_since_start - spawn_time ) / lifetime , 0 , 1 );
5461
5562 // fade-in and fade-out particle
56- render_alpha = alpha ;
63+ render_alpha : = alpha ;
5764 if lifetime_lerp < fade_in_time {
5865 render_alpha *= lifetime_lerp / fade_in_time ;
5966 } else if lifetime_lerp > 1 - fade_out_time {
6067 render_alpha *= ((1 - lifetime_lerp ) / fade_out_time );
6168 }
6269
70+ texture := * Game .textures [emitter .texture_id ];
71+
6372 render_draw_quad (
6473 texture = texture ,
6574 position = render_position ,
6675 size = render_size ,
6776 clip = * clip ,
68- flip = animation . frames [ animator . current ] .flip ,
69- palette = palette ,
77+ flip = current_frame .flip ,
78+ // palette = palette,
7079 color = .{ 1 , 1 , 1 , render_alpha },
7180 rotate = rotation
7281 );
7382
74- return spawn_time + lifetime > Game .current_time ;
83+ return spawn_time + lifetime > Game .time_since_start ;
7584}
7685
7786Particle_Emitter :: struct {
78- my_update_particle : type_of (update_particle );
79- my_render_particle : type_of (render_particle );
80-
8187 // user can set desired_particle_count in LSD,
8288 // then we use that to init the particles array
8389 desired_particle_count : int ;
@@ -97,7 +103,7 @@ Particle_Emitter :: struct {
97103 rotation_velocity : Range (float );
98104 rotation_acceleration : Range (float );
99105
100- alpha : Range (float );
106+ alpha : Range (float ) = .{ 1 , 1 } ;
101107 alpha_velocity : Range (float );
102108 alpha_acceleration : Range (float );
103109
@@ -117,19 +123,32 @@ Particle_Emitter :: struct {
117123 }
118124}
119125
120- init_particle_emitter :: (emitter : Particle_Emitter ) {
121- emitter .particles = NewArray (particle_count , desired_particle_count );
126+ free_particle_emitter :: (using emitter : * Particle_Emitter ) {
127+ array_free (emitter .particles );
128+ }
129+
130+ prep_particle_emitter :: (using emitter : * Particle_Emitter ) {
131+ particles = NewArray (desired_particle_count , Particle );
122132
123- if emit_frequency [0 ] <= 0 {
124- log (" Warning: emit_frquency values cannot be zero! Defaulting to 1." );
125- emit_frequency [0 ] = 1 ;
133+ check_emit_frequency :: inline (value : * float , name : string ) {
134+ if value .* <= 0 {
135+ log (" Warning: % must be greater than zero! Defaulting to 1." , name );
136+ value .* = 1 ;
137+ }
126138 }
127- if emit_frequency [1 ] <= 0 {
128- log (" Warning: emit_frquency values cannot be zero! Defaulting to 1." );
129- emit_frequency [1 ] = 1 ;
139+ check_emit_frequency (* emit_frequency .min , " emit_frequency.min" );
140+ check_emit_frequency (* emit_frequency .max , " emit_frequency.max" );
141+
142+ check_fade_time :: inline (value : * float , name : string ) {
143+ if value .* < 0 {
144+ log (" Warning: % must be between 0 and 1! Clamping value." , name );
145+ value .* = clamp (value .* , 0 , 1 );
146+ }
130147 }
131-
132- // check fade times
148+ check_fade_time (* fade_in_time .min , " fade_in_time.min" );
149+ check_fade_time (* fade_in_time .max , " fade_in_time.max" );
150+ check_fade_time (* fade_out_time .min , " fade_out_time.min" );
151+ check_fade_time (* fade_out_time .max , " fade_out_time.max" );
133152}
134153
135154get_first_empty_particle_slot :: (emitter : * Particle_Emitter ) -> * Particle {
@@ -138,10 +157,10 @@ get_first_empty_particle_slot :: (emitter: *Particle_Emitter) -> *Particle {
138157}
139158
140159do_particle_emitter :: (using emitter : * Particle_Emitter , offset : Vector2 ) {
141- while Game .current_time > next_emit_time {
160+ while Game .time_since_start > next_emit_time {
142161 p := get_first_empty_particle_slot (emitter );
143162 if ! p {
144- next_emit_time = Game .current_time + random_get_within_range (emit_frequency );
163+ next_emit_time = Game .time_since_start + random_get_within_range (emit_frequency );
145164 break ;
146165 }
147166
@@ -160,6 +179,9 @@ do_particle_emitter :: (using emitter: *Particle_Emitter, offset: Vector2) {
160179 alpha_acceleration = random_get_within_range (alpha_acceleration ),
161180 lifetime = random_get_within_range (lifetime ),
162181
182+ fade_in\_time = random_get_within_range (fade_in\_time ),
183+ fade_out_time = random_get_within_range (fade_out_time ),
184+
163185 spawn_time = next_emit_time ,
164186 active = true ,
165187 };
@@ -168,13 +190,44 @@ do_particle_emitter :: (using emitter: *Particle_Emitter, offset: Vector2) {
168190 }
169191
170192 for * emitter .particles {
171- if ! do_particle (emitter , it ) {
193+ if ! it .active continue ;
194+ if ! do_particle (it , emitter , Game .render_delta_time ) {
172195 it .active = false ;
173196 }
174197 }
175198}
176199
177200
201+ // manual particle emitting procedures
202+
203+ // emit particles using the particle emitter's properties to initialize
204+ // TODO: create a version that allows user to manually override particular initializer properties
205+ // this will require either making some separate Particle_Initializer struct or having a lot of optional parameters
206+ emit_particles :: (using emitter : * Particle_Emitter , count : int , offset : Vector2 ) {
207+ for 0 ..count - 1 {
208+ p := get_first_empty_particle_slot (emitter );
209+ if ! p return ;
210+
211+ p .* = Particle .{
212+ position = offset + emit_box .position + random_get_within_range (Vector2 .{}, emit_box .size ),
213+ velocity = random_get_within_range (velocity ),
214+ acceleration = random_get_within_range (acceleration ),
215+ size = random_get_within_range (size ),
216+ size_velocity = random_get_within_range (size_velocity ),
217+ size_acceleration = random_get_within_range (size_acceleration ),
218+ rotation = random_get_within_range (rotation ),
219+ rotation_velocity = random_get_within_range (rotation_velocity ),
220+ rotation_acceleration = random_get_within_range (rotation_acceleration ),
221+ alpha = random_get_within_range (alpha ),
222+ alpha_velocity = random_get_within_range (alpha_velocity ),
223+ alpha_acceleration = random_get_within_range (alpha_acceleration ),
224+ lifetime = random_get_within_range (lifetime ),
225+
226+ spawn_time = Game .time_since_start ,
227+ active = true ,
228+ };
229+ }
230+ }
178231
179232
180233
@@ -183,7 +236,7 @@ do_particle_emitter :: (using emitter: *Particle_Emitter, offset: Vector2) {
183236// TODO: move to another file?
184237Range :: struct (T : Type ) { min , max : T ; }
185238
186- random_get_within_range :: (using range : Range (T )) -> T {
239+ random_get_within_range :: (using range : Range ($ T )) -> T {
187240 return random_get_within_range (range .min , range .max );
188241}
189242
0 commit comments