55
66#include " render/3d.h"
77
8+ #include < anl.h>
9+
810namespace particle {
911
1012ParticleEffect::ParticleEffect (SCP_string name)
@@ -31,11 +33,15 @@ ParticleEffect::ParticleEffect(SCP_string name)
3133 m_lifetime(::util::UniformFloatRange(0 .0f )),
3234 m_length(::util::UniformFloatRange(0 .0f )),
3335 m_vel_inherit(::util::UniformFloatRange(0 .0f )),
34- m_velocity_scaling(::util::UniformFloatRange(0 .0f )),
36+ m_velocity_scaling(::util::UniformFloatRange(1 .0f )),
37+ m_velocity_noise_scaling(::util::UniformFloatRange(1 .0f )),
38+ m_position_noise_scaling(::util::UniformFloatRange(1 .0f )),
3539 m_vel_inherit_from_orientation(std::nullopt ),
3640 m_vel_inherit_from_position(std::nullopt ),
3741 m_velocityVolume(nullptr ),
3842 m_spawnVolume(nullptr ),
43+ m_velocityNoise(nullptr ),
44+ m_spawnNoise(nullptr ),
3945 m_manual_offset (std::nullopt ),
4046 m_particleTrail(ParticleEffectHandle::invalid()),
4147 m_size_lifetime_curve(-1 ),
@@ -87,10 +93,14 @@ ParticleEffect::ParticleEffect(SCP_string name,
8793 m_length(::util::UniformFloatRange(0 .0f )),
8894 m_vel_inherit(vel_inherit),
8995 m_velocity_scaling(velocity_scaling),
96+ m_velocity_noise_scaling(::util::UniformFloatRange(1 .0f )),
97+ m_position_noise_scaling(::util::UniformFloatRange(1 .0f )),
9098 m_vel_inherit_from_orientation(vel_inherit_from_orientation),
9199 m_vel_inherit_from_position(vel_inherit_from_position),
92100 m_velocityVolume(std::move(velocityVolume)),
93101 m_spawnVolume(std::move(spawnVolume)),
102+ m_velocityNoise(nullptr ),
103+ m_spawnNoise(nullptr ),
94104 m_manual_offset (std::nullopt ),
95105 m_particleTrail(particleTrail),
96106 m_size_lifetime_curve(-1 ),
@@ -134,26 +144,41 @@ matrix ParticleEffect::getNewDirection(const matrix& hostOrientation, const std:
134144 }
135145}
136146
137- void ParticleEffect::processSource (float interp, const ParticleSource& source, size_t effectNumber, const vec3d& vel, int parent, int parent_sig, float parentLifetime, float parentRadius, float particle_percent) const {
138- const auto & [pos, hostOrientation] = source.m_host ->getPositionAndOrientation (m_parent_local, interp, m_manual_offset);
147+ void ParticleEffect::sampleNoise (vec3d& noiseTarget, const matrix* orientation, std::pair<anl::CKernel, anl::CInstructionIndex>& noise, const std::tuple<const ParticleSource&, const size_t &>& source, ParticleCurvesOutput noiseMult, ParticleCurvesOutput noiseTimeMult, ParticleCurvesOutput noiseSeed) const {
148+ auto & [kernel, instruction] = noise;
149+ anl::CNoiseExecutor executor (kernel);
150+ const auto & color = executor.evaluateColor (
151+ ParticleSource::getEffectRunningTime (source)
152+ * m_modular_curves.get_output (noiseTimeMult, source)
153+ , m_modular_curves.get_output (noiseSeed, source), instruction);
154+
155+ vec3d noiseSampleLocal{{{ color.r , color.g , color.b }}};
156+ noiseSampleLocal *= m_modular_curves.get_output (noiseMult, source);
157+
158+ vm_vec_unrotate (&noiseTarget, &noiseSampleLocal, orientation);
159+ }
139160
161+ void ParticleEffect::processSource (float interp, const ParticleSource& source, size_t effectNumber, const vec3d& velParent, int parent, int parent_sig, float parentLifetime, float parentRadius, float particle_percent) const {
140162 if (m_affectedByDetail){
141163 if (Detail.num_particles > 0 )
142164 particle_percent *= (0 .5f + (0 .25f * static_cast <float >(Detail.num_particles - 1 )));
143165 else
144166 return ; // Will not emit on current detail settings, but may in the future.
145167 }
146168
169+ auto modularCurvesInput = std::forward_as_tuple (source, effectNumber);
170+
171+ const auto & [pos, hostOrientation] = source.m_host ->getPositionAndOrientation (m_parent_local, interp, m_manual_offset);
172+
173+ const auto & orientation = getNewDirection (hostOrientation, source.m_normal );
174+
147175 if (m_distanceCulled > 0 .f ) {
148176 float min_dist = 125 .0f ;
149177 float dist = vm_vec_dist_quick (&pos, &Eye_position) / m_distanceCulled;
150178 if (dist > min_dist)
151179 particle_percent *= min_dist / dist;
152180 }
153181
154- const auto & orientation = getNewDirection (hostOrientation, source.m_normal );
155-
156- auto modularCurvesInput = std::forward_as_tuple (source, effectNumber);
157182 particle_percent *= m_particleChance * m_modular_curves.get_output (ParticleCurvesOutput::PARTICLE_NUM_MULT , modularCurvesInput);
158183 float radiusMultiplier = m_modular_curves.get_output (ParticleCurvesOutput::RADIUS_MULT , modularCurvesInput);
159184 float lengthMultiplier = m_modular_curves.get_output (ParticleCurvesOutput::LENGTH_MULT , modularCurvesInput);
@@ -163,6 +188,18 @@ void ParticleEffect::processSource(float interp, const ParticleSource& source, s
163188 float positionInheritVelocityMultiplier = m_modular_curves.get_output (ParticleCurvesOutput::POSITION_INHERIT_VELOCITY_MULT , modularCurvesInput);
164189 float orientationInheritVelocityMultiplier = m_modular_curves.get_output (ParticleCurvesOutput::ORIENTATION_INHERIT_VELOCITY_MULT , modularCurvesInput);
165190
191+ vec3d velNoise = ZERO_VECTOR ;
192+ if (m_velocityNoise != nullptr ) {
193+ sampleNoise (velNoise, &orientation, *m_velocityNoise, modularCurvesInput, ParticleCurvesOutput::VELOCITY_NOISE_MULT , ParticleCurvesOutput::VELOCITY_NOISE_TIME_MULT , ParticleCurvesOutput::VELOCITY_NOISE_SEED );
194+ velNoise *= m_velocity_noise_scaling.next ();
195+ }
196+
197+ vec3d posNoise = ZERO_VECTOR ;
198+ if (m_spawnNoise != nullptr ) {
199+ sampleNoise (posNoise, &orientation, *m_spawnNoise, modularCurvesInput, ParticleCurvesOutput::SPAWN_POSITION_NOISE_MULT , ParticleCurvesOutput::SPAWN_POSITION_NOISE_TIME_MULT , ParticleCurvesOutput::SPAWN_POSITION_NOISE_SEED );
200+ posNoise *= m_position_noise_scaling.next ();
201+ }
202+
166203 float num = m_particleNum.next () * particle_percent;
167204 unsigned int num_spawn;
168205
@@ -177,7 +214,7 @@ void ParticleEffect::processSource(float interp, const ParticleSource& source, s
177214 particle_info info;
178215
179216 info.pos = pos;
180- info.vel = vel ;
217+ info.vel = velParent ;
181218
182219 if (m_parent_local) {
183220 info.attached_objnum = parent;
@@ -189,45 +226,45 @@ void ParticleEffect::processSource(float interp, const ParticleSource& source, s
189226 }
190227
191228 if (m_vel_inherit_absolute)
192- vm_vec_normalize_quick (&info.vel );
229+ vm_vec_normalize_safe (&info.vel , true );
193230
194231 info.vel *= m_vel_inherit.next () * inheritVelocityMultiplier;
195232
196- vec3d velocity = ZERO_VECTOR ;
197- vec3d localPos = ZERO_VECTOR ;
233+ vec3d localVelocity = velNoise ;
234+ vec3d localPos = posNoise ;
198235
199236 if (m_spawnVolume != nullptr ) {
200237 localPos += m_spawnVolume->sampleRandomPoint (orientation, modularCurvesInput);
201238 }
202239
203240 if (m_velocityVolume != nullptr ) {
204- velocity += m_velocityVolume->sampleRandomPoint (orientation, modularCurvesInput) * (m_velocity_scaling.next () * velocityVolumeMultiplier);
241+ localVelocity += m_velocityVolume->sampleRandomPoint (orientation, modularCurvesInput) * (m_velocity_scaling.next () * velocityVolumeMultiplier);
205242 }
206243
207244 if (m_vel_inherit_from_orientation.has_value ()) {
208- velocity += orientation.vec .fvec * (m_vel_inherit_from_orientation->next () * orientationInheritVelocityMultiplier);
245+ localVelocity += orientation.vec .fvec * (m_vel_inherit_from_orientation->next () * orientationInheritVelocityMultiplier);
209246 }
210247
211248 if (m_vel_inherit_from_position.has_value ()) {
212249 vec3d velFromPos = localPos;
213250 if (m_vel_inherit_from_position_absolute)
214251 vm_vec_normalize_safe (&velFromPos);
215- velocity += velFromPos * (m_vel_inherit_from_position->next () * positionInheritVelocityMultiplier);
252+ localVelocity += velFromPos * (m_vel_inherit_from_position->next () * positionInheritVelocityMultiplier);
216253 }
217254
218255 if (m_velocity_directional_scaling != VelocityScaling::NONE ) {
219- // Scale the vector with a random velocity sample and also multiply that with cos(angle between
220- // info.vel and sourceDir) That should produce good looking directions where the maximum velocity is
256+ // Scale the vector with a random localVelocity sample and also multiply that with cos(angle between
257+ // info.vel and sourceDir) That should produce good looking directions where the maximum localVelocity is
221258 // only achieved when the particle travels directly on the normal/reflect vector
222259 vec3d normalizedVelocity;
223- vm_vec_copy_normalize_safe (&normalizedVelocity, &velocity );
260+ vm_vec_copy_normalize_safe (&normalizedVelocity, &localVelocity );
224261 float dot = vm_vec_dot (&normalizedVelocity, &orientation.vec .fvec );
225- vm_vec_scale (&velocity ,
262+ vm_vec_scale (&localVelocity ,
226263 m_velocity_directional_scaling == VelocityScaling::DOT ? dot : 1 .f / std::max (0 .001f , dot));
227264 }
228265
229266 info.pos += localPos;
230- info.vel += velocity ;
267+ info.vel += localVelocity ;
231268
232269 info.bitmap = m_bitmap_list[m_bitmap_range.next ()];
233270
0 commit comments