iMSTK
Interactive Medical Simulation Toolkit
imstkRenderParticleEmitter.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkRenderParticleEmitter.h"
8 #include "imstkLogger.h"
9 #include "imstkRenderParticles.h"
10 #include "imstkTimer.h"
11 
12 namespace imstk
13 {
14 RenderParticleEmitter::RenderParticleEmitter(std::shared_ptr<Geometry> geometry,
15  const float time /*= 3000*/,
16  RenderParticleEmitter::Mode mode /*= Mode::CONTINUOUS*/)
17  : AnimationModel(geometry),
18  m_mode(mode), m_time(time), m_emitTime(m_time), m_stopWatch(std::make_unique<StopWatch>())
19 {
20  this->setGeometry(geometry);
21 
22  RenderParticleKeyFrame startFrame;
23  startFrame.m_color = Color::White;
24  startFrame.m_time = 0.0f;
25  startFrame.m_acceleration = Vec3f(0, 0, 0);
26  startFrame.m_rotationalAcceleration = 0.0f;
27 
28  RenderParticleKeyFrame endFrame;
29  endFrame.m_color = startFrame.m_color;
30  endFrame.m_time = m_time;
31  endFrame.m_acceleration = Vec3f(0, 0, 0);
32  endFrame.m_rotationalAcceleration = 0.0f;
33 
34  m_keyFrames.push_back(startFrame);
35  m_keyFrames.push_back(endFrame);
36 
37  this->initializeParticles();
38 }
39 
40 void
42  std::shared_ptr<Geometry> geometry)
43 {
44  CHECK(geometry->getTypeName() == RenderParticles::getStaticTypeName()) << "Error: Geometry must be RenderParticles";
45 
46  m_animationGeometry = geometry;
47  m_particles = &std::static_pointer_cast<RenderParticles>(m_animationGeometry)->getParticles();
48 }
49 
52 {
53  return m_mode;
54 }
55 
56 void
58 {
59  m_emitterSize = size;
60 }
61 
62 void
64  const Vec3f maxDirection,
65  const float minSpeed,
66  const float maxSpeed,
67  const float minRotationSpeed,
68  const float maxRotationSpeed)
69 {
70  m_minDirection = minDirection;
71  m_maxDirection = maxDirection;
72  m_minDirection.normalize();
73  m_maxDirection.normalize();
74  m_minSpeed = minSpeed;
75  m_maxSpeed = maxSpeed;
76  m_minRotationSpeed = minRotationSpeed;
77  m_maxRotationSpeed = maxRotationSpeed;
78 }
79 
80 bool
82 {
83  if (static_cast<int>(m_keyFrames.size()) >= c_maxNumKeyFrames)
84  {
85  return false;
86  }
87 
88  m_keyFrames.push_back(keyFrame);
89  return true;
90 }
91 
94 {
95  size_t index = 0;
96 
97  for (size_t i = 0; i < m_keyFrames.size(); i++)
98  {
99  if (m_keyFrames[i].m_time < m_keyFrames[index].m_time)
100  {
101  index = i;
102  }
103  }
104 
105  return &m_keyFrames[index];
106 }
107 
109 RenderParticleEmitter::getEndKeyFrame()
110 {
111  size_t index = 0;
112 
113  for (size_t i = 0; i < m_keyFrames.size(); i++)
114  {
115  if (m_keyFrames[i].m_time > m_keyFrames[index].m_time)
116  {
117  index = i;
118  }
119  }
120 
121  return &m_keyFrames[index];
122 }
123 
124 std::vector<RenderParticleKeyFrame>&
126 {
127  return m_keyFrames;
128 }
129 
130 void
132 {
133  if (m_mode != Mode::Burst)
134  {
135  return;
136  }
137 
138  auto renderParticles = std::static_pointer_cast<RenderParticles>(m_geometry);
139  renderParticles->reset();
140 
141  this->initializeParticles();
142 }
143 
144 void
146 {
147  auto renderParticles = std::static_pointer_cast<RenderParticles>(m_geometry);
148 
149  if (!m_started)
150  {
151  m_stopWatch->start();
152  m_started = true;
153  }
154 
155  auto time = m_stopWatch->getTimeElapsed();
156  float dt = (float)(time - m_lastUpdateTime);
157  m_lastUpdateTime = time;
158 
159  for (auto&& particle : (*m_particles))
160  {
161  auto startKeyFrameTemp = *this->getStartKeyFrame();
162  auto startKeyFrame = &startKeyFrameTemp;
163  auto endKeyFrameTemp = *this->getEndKeyFrame();
164  auto endKeyFrame = &endKeyFrameTemp;
165 
166  particle->m_age += dt;
167 
168  if (!(particle->m_created) && particle->m_age >= 0)
169  {
170  particle->m_created = true;
171  this->emitParticle(particle);
172  renderParticles->incrementNumOfParticles();
173  }
174  else if (particle->m_age < 0)
175  {
176  continue;
177  }
178 
180  && particle->m_age > m_time)
181  {
182  particle->m_age = particle->m_age - ((int)(particle->m_age / m_time) * m_time);
183  this->emitParticle(particle);
184  }
185 
186  // Search for nearest keyframe
187  for (size_t i = 0; i < m_keyFrames.size(); i++)
188  {
189  auto keyFrame = &m_keyFrames[i];
190  if (particle->m_age >= keyFrame->m_time
191  && keyFrame->m_time > startKeyFrame->m_time)
192  {
193  startKeyFrame = keyFrame;
194  }
195  if (particle->m_age < keyFrame->m_time
196  && keyFrame->m_time < endKeyFrame->m_time)
197  {
198  endKeyFrame = keyFrame;
199  }
200  }
201 
202  // Update velocity and position
203  particle->m_rotationalAcceleration = startKeyFrame->m_rotationalAcceleration;
204  particle->m_rotationalVelocity += particle->m_rotationalAcceleration * (dt / 1000.0f);
205  particle->m_rotation += particle->m_rotationalVelocity * (dt / 1000.0f);
206 
207  particle->m_acceleration = startKeyFrame->m_acceleration;
208  particle->m_velocity += particle->m_acceleration * (dt / 1000.0);
209  particle->m_position += particle->m_velocity * (dt / 1000.0);
210 
211  float timeDifference = endKeyFrame->m_time - startKeyFrame->m_time;
212  float alpha = (particle->m_age - startKeyFrame->m_time) / timeDifference;
213 
214  particle->m_scale = (alpha * endKeyFrame->m_scale)
215  + ((1.0f - alpha) * startKeyFrame->m_scale);
216 
217  auto particleColor = imstk::Color(particle->m_color[0], particle->m_color[1], particle->m_color[2], particle->m_color[3]);
218  this->interpolateColor(particleColor,
219  endKeyFrame->m_color,
220  startKeyFrame->m_color,
221  alpha);
222  }
223 }
224 
225 void
227 {
228  m_particles->clear();
229 
230  auto particles = std::static_pointer_cast<RenderParticles>(m_animationGeometry);
231 
232  for (unsigned int i = 0; i < particles->getMaxNumParticles(); i++)
233  {
234  m_particles->push_back(std::unique_ptr<RenderParticle>(new RenderParticle()));
235  (*m_particles)[i]->m_age = -(i / (float)(particles->getMaxNumParticles())) * m_emitTime;
236  (*m_particles)[i]->m_created = false;
237  }
238 }
239 
240 void
241 RenderParticleEmitter::emitParticle(std::unique_ptr<RenderParticle>& particle)
242 {
243  auto position = m_animationGeometry->getTranslation();
244 
245  if (m_shape == Shape::Cube)
246  {
247  float x = (getRandomNormalizedFloat() - 0.5f) * m_emitterSize;
248  float y = (getRandomNormalizedFloat() - 0.5f) * m_emitterSize;
249  float z = (getRandomNormalizedFloat() - 0.5f) * m_emitterSize;
250 
251  particle->m_position[0] = (float)position[0] + x;
252  particle->m_position[1] = (float)position[1] + y;
253  particle->m_position[2] = (float)position[2] + z;
254  }
255 
256  float randomRotation = getRandomNormalizedFloat() * (float)PI * 2.0f;
257  float randomRotationalVelocity = getRandomNormalizedFloat();
258  particle->m_rotation = randomRotation;
259  particle->m_rotationalVelocity = (randomRotationalVelocity * m_minRotationSpeed) +
260  ((1.0f - randomRotationalVelocity) * m_maxRotationSpeed);
261 
262  float randomDirectionX = getRandomNormalizedFloat();
263  float randomDirectionY = getRandomNormalizedFloat();
264  float randomDirectionZ = getRandomNormalizedFloat();
265  float randomSpeed = getRandomNormalizedFloat();
266 
267  float speed = (randomSpeed * m_minSpeed) + ((1.0f - randomSpeed) * m_maxSpeed);
268  float directionX = (randomDirectionX * m_minDirection[0]) + ((1.0f - randomDirectionX) * m_maxDirection[0]);
269  float directionY = (randomDirectionY * m_minDirection[1]) + ((1.0f - randomDirectionY) * m_maxDirection[1]);
270  float directionZ = (randomDirectionZ * m_minDirection[2]) + ((1.0f - randomDirectionZ) * m_maxDirection[2]);
271  Vec3f direction(directionX, directionY, directionZ);
272  direction.normalize();
273  particle->m_velocity[0] = directionX * speed;
274  particle->m_velocity[1] = directionY * speed;
275  particle->m_velocity[2] = directionZ * speed;
276 }
277 
278 void
280  const Color& sourceA,
281  const Color& sourceB,
282  const float alpha)
283 {
284  destination.r = (sourceA.r * alpha) + (sourceB.r * (1.0f - alpha));
285  destination.g = (sourceA.g * alpha) + (sourceB.g * (1.0f - alpha));
286  destination.b = (sourceA.b * alpha) + (sourceB.b * (1.0f - alpha));
287  destination.a = (sourceA.a * alpha) + (sourceB.a * (1.0f - alpha));
288 }
289 
290 float
292 {
293  return (float)std::rand() / RAND_MAX;
294 }
295 } // namespace imstk
void reset()
Reset number of particles.
float getRandomNormalizedFloat()
Get uniformly-distributed float.
Keyframe for particle animation.
Compound Geometry.
RenderParticleEmitter(std::shared_ptr< Geometry > geometry, const float time=3000.0f, Mode mode=Mode::Continuous)
Constructor.
Particles for rendering.
std::vector< RenderParticleKeyFrame > & getKeyFrames()
Get key frames.
RenderParticleKeyFrame * getStartKeyFrame()
Get start and end frames.
std::vector< RenderParticleKeyFrame > m_keyFrames
Particle keyframes.
bool addKeyFrame(RenderParticleKeyFrame keyFrame)
Add keyframe to particle emitter.
void initializeParticles()
Initialize particles.
const int c_maxNumKeyFrames
Maximum key frames.
Emitter continuously releases/recycles particles.
float m_time
total time for particle system
void emitParticle(std::unique_ptr< RenderParticle > &particle)
Emit particle.
Contains geometric and animation render information.
void interpolateColor(Color &destination, const Color &sourceA, const Color &sourceB, const float alpha)
Interpolate color.
virtual void setGeometry(std::shared_ptr< Geometry > renderParticles)
Set animation geometry.
Emitter releases particles once until manually reset.
Color in RGB space.
Definition: imstkColor.h:24
virtual void reset()
Reset the emitter Only works for burst particles.
Stop Watch utility class.
Definition: imstkTimer.h:19
static Color White
Various commonly used colors.
Definition: imstkColor.h:112
void setEmitterSize(const float size)
Set size of emitter.
void setInitialVelocityRange(const Vec3f minDirection, const Vec3f maxDirection, const float minSpeed, const float maxSpeed, const float minRotationSpeed, const float maxRotationSpeed)
Set velocity range This functions sets minimum and maximum rotation values for determining the initia...
RenderParticleEmitter::Mode getEmitterMode() const
Get mode of emitter.