Commit fe4fdef0 authored by Sreekanth Arikatla's avatar Sreekanth Arikatla
Browse files

Merge branch 'particleSystem' into 'master'

ENH: Render Particle System

See merge request iMSTK/iMSTK!279
parents 226892e9 d79f3c70
......@@ -19,6 +19,8 @@ function(CopyAndCompileShaders)
compileShaders(Mesh/mesh_frag.frag Mesh/mesh_frag.spv)
compileShaders(Mesh/decal_vert.vert Mesh/decal_vert.spv)
compileShaders(Mesh/decal_frag.frag Mesh/decal_frag.spv)
compileShaders(Mesh/particle_vert.vert Mesh/particle_vert.spv)
compileShaders(Mesh/particle_frag.frag Mesh/particle_frag.spv)
compileShaders(Mesh/shadow_vert.vert Mesh/shadow_vert.spv)
compileShaders(Mesh/shadow_frag.frag Mesh/shadow_frag.spv)
compileShaders(Mesh/depth_frag.frag Mesh/depth_frag.spv)
......
......@@ -343,6 +343,7 @@ add_subdirectory(Source/SimulationManager)
add_subdirectory(Source/Constraint)
add_subdirectory(Source/Materials)
add_subdirectory(Source/GUIOverlay)
add_subdirectory(Source/Animation)
#--------------------------------------------------------------------------
# Export Targets
......
02b526d12222a723a9dba86fbeeabf7aa66e06f5730b043f48c1ca9b96ec509b50c89aab4c458fbd198c96d5f2cef210b207ac071f180f95cc6ae2566f5af018
eb6f17318fba433631f53429a6b0b4281d577026a2ea44365fc78b10ff5487b969022b77e02ea35e1c85d9dec8cf4aadbcf555322aa924115137f10e71e4b15c
b8219a7352a2ab446b41389484153aed669ef12a0a82e2231d9ea51f42acb5d6ec1c880a5708a8be1b4386da69fc69f511e1c347a454dd430f8dfde226c5d6fe
###########################################################################
#
# Copyright (c) Kitware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0.txt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###########################################################################
if(iMSTK_USE_Vulkan)
project(Example-VulkanParticles)
#-----------------------------------------------------------------------------
# Create executable
#-----------------------------------------------------------------------------
add_executable(${PROJECT_NAME} VulkanParticlesExample.cpp)
#-----------------------------------------------------------------------------
# Link libraries to executable
#-----------------------------------------------------------------------------
target_link_libraries(${PROJECT_NAME} SimulationManager)
#-----------------------------------------------------------------------------
# Add shaders
#-----------------------------------------------------------------------------
include(imstkCopyAndCompileShaders)
CopyAndCompileShaders()
#-----------------------------------------------------------------------------
# Associate external data
#-----------------------------------------------------------------------------
list(APPEND FILE_LIST
particles/,REGEX:.*)
imstk_add_data(${PROJECT_NAME} ${FILE_LIST})
endif()
\ No newline at end of file
/*=========================================================================
Library: iMSTK
Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
& Imaging in Medicine, Rensselaer Polytechnic Institute.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.txt
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=========================================================================*/
#include "imstkSimulationManager.h"
#include "imstkAnimationObject.h"
#include "imstkRenderParticles.h"
#include "imstkRenderParticleEmitter.h"
#include "imstkAPIUtilities.h"
using namespace imstk;
///
/// \brief This example demonstrates particle rendering feature.
/// NOTE: Requires enabling Vulkan rendering backend
///
int main()
{
// SDK and Scene
auto sdk = std::make_shared<SimulationManager>();
auto scene = sdk->createNewScene("RenderParticles");
// Position camera
auto cam = scene->getCamera();
cam->setPosition(0, 3, 6);
cam->setFocalPoint(0, 0, 0);
// Smoke
{
// Create sparks material
auto particleMaterial = std::make_shared<RenderMaterial>();
auto particleTexture = std::make_shared<Texture>
(iMSTK_DATA_ROOT "/particles/smoke_01.png", Texture::Type::DIFFUSE);
particleMaterial->addTexture(particleTexture);
particleMaterial->setBlendMode(RenderMaterial::BlendMode::ALPHA);
// Create particle geometry (for visual and animation)
auto particles = std::make_shared<RenderParticles>(128);
particles->setParticleSize(0.4f);
// Create particle animation model
auto particleEmitter = std::make_shared<RenderParticleEmitter>(particles, 2000.0f);
particleEmitter->setInitialVelocityRange(Vec3f(-1, 5, -1), Vec3f(1, 5, 1),
0.5, 1.0,
-1.0, 1.0);
particleEmitter->setEmitterSize(0.3f);
// Modify the first keyframe
auto startKeyFrame = particleEmitter->getStartKeyFrame();
startKeyFrame->m_color = Color(1.0, 0.7, 0.0, 1.0);
// Add another keyframe
RenderParticleKeyFrame midFrame0;
midFrame0.m_time = 700.0f;
midFrame0.m_color = Color::Red;
midFrame0.m_scale = 1.5f;
particleEmitter->addKeyFrame(midFrame0);
// Add another keyframe
RenderParticleKeyFrame midFrame1;
midFrame1.m_time = 1300.0f;
midFrame1.m_color = Color::DarkGray;
midFrame1.m_color.a = 0.7f;
midFrame1.m_scale = 2.0f;
particleEmitter->addKeyFrame(midFrame1);
// Modify the last keyframe
auto endKeyFrame = particleEmitter->getEndKeyFrame();
endKeyFrame->m_color = Color::Black;
endKeyFrame->m_color.a = 0.0;
endKeyFrame->m_scale = 4.0;
// Create and add animation scene object
auto particleObject = std::make_shared<AnimationObject>("Smoke");
auto particleModel = std::make_shared<VisualModel>(particles);
particleModel->setRenderMaterial(particleMaterial);
particleObject->addVisualModel(particleModel);
particleObject->setAnimationModel(particleEmitter);
scene->addSceneObject(particleObject);
}
// Sparks
{
// Create sparks material
auto particleMaterial = std::make_shared<RenderMaterial>();
auto particleTexture = std::make_shared<Texture>
(iMSTK_DATA_ROOT "/particles/flare_01.png", Texture::Type::DIFFUSE);
particleMaterial->addTexture(particleTexture);
particleMaterial->setBlendMode(RenderMaterial::BlendMode::ALPHA);
// Create particle geometry (for visual and animation)
auto particles = std::make_shared<RenderParticles>(128);
particles->setTranslation(2, 0.1, 0);
particles->setParticleSize(0.3f);
// Create animation model
auto particleEmitter = std::make_shared<RenderParticleEmitter>(particles,
850.0f, RenderParticleEmitter::Mode::BURST);
particleEmitter->setInitialVelocityRange(Vec3f(-1, 5, -1), Vec3f(1, 5, 1),
4.0, 5.0,
-1.0, 1.0);
particleEmitter->setEmitterSize(0.1f);
// Modifying the first keyframe
auto startKeyFrame = particleEmitter->getStartKeyFrame();
startKeyFrame->m_acceleration = Vec3f(0, -9.8, 0);
startKeyFrame->m_color = Color::Yellow;
// Modifying the last keyframe
auto endKeyFrame = particleEmitter->getEndKeyFrame();
endKeyFrame->m_color = Color::Orange;
// Create and add animation object
auto particleObject = std::make_shared<AnimationObject>("Sparks");
auto particleModel = std::make_shared<VisualModel>(particles);
particleModel->setRenderMaterial(particleMaterial);
particleObject->addVisualModel(particleModel);
particleObject->setAnimationModel(particleEmitter);
scene->addSceneObject(particleObject);
}
// Plane
auto plane = apiutils::createVisualAnalyticalSceneObject(Geometry::Type::Plane, scene, "plane", 10);
plane->getVisualModel(0)->getRenderMaterial()->setColor(Color::Black);
// Light
auto light = std::make_shared<DirectionalLight>("Light");
light->setIntensity(7);
light->setColor(Color(1.0, 0.95, 0.8));
light->setFocalPoint(Vec3d(-1, -1, 0));
scene->addLight(light);
auto viewer = sdk->getViewer();
// Create a call back on key press of 'b' to trigger the sparks emitter
viewer->setOnCharFunction('b', [&](InteractorStyle* c) -> bool
{
auto sparks = std::static_pointer_cast<AnimationObject>(
scene->getSceneObject("Sparks"));
sparks->getAnimationModel()->reset();
return false;
});
// Run
sdk->setActiveScene(scene);
sdk->startSimulation(SimulationStatus::PAUSED);
return 0;
}
#-----------------------------------------------------------------------------
# Create target
#-----------------------------------------------------------------------------
include(imstkAddLibrary)
imstk_add_library( Animation
DEPENDS
Core
Geometry
SceneElements
)
#-----------------------------------------------------------------------------
# Testing
#-----------------------------------------------------------------------------
if( iMSTK_BUILD_TESTING )
add_subdirectory( Testing )
endif()
/*=========================================================================
Library: iMSTK
Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
& Imaging in Medicine, Rensselaer Polytechnic Institute.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.txt
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=========================================================================*/
#include "imstkRenderParticleEmitter.h"
namespace imstk
{
RenderParticleEmitter::RenderParticleEmitter(std::shared_ptr<Geometry> geometry,
const float time /*= 3000*/,
RenderParticleEmitter::Mode mode /*= Mode::CONTINUOUS*/)
: AnimationModel(geometry)
{
this->setGeometry(geometry);
m_time = time;
m_emitTime = m_time;
m_mode = mode;
RenderParticleKeyFrame startFrame;
startFrame.m_color = Color::White;
startFrame.m_time = 0.0f;
startFrame.m_acceleration = Vec3f(0, 0, 0);
startFrame.m_rotationalAcceleration = 0.0f;
RenderParticleKeyFrame endFrame;
endFrame.m_color = startFrame.m_color;
endFrame.m_time = m_time;
endFrame.m_acceleration = Vec3f(0, 0, 0);
endFrame.m_rotationalAcceleration = 0.0f;
m_keyFrames.push_back(startFrame);
m_keyFrames.push_back(endFrame);
this->initializeParticles();
}
void
RenderParticleEmitter::setGeometry(
std::shared_ptr<Geometry> geometry)
{
if (geometry->getType() != Geometry::Type::RenderParticles)
{
LOG(FATAL) << "Geometry must be RenderParticles";
return;
}
m_animationGeometry = geometry;
m_particles = &std::static_pointer_cast<RenderParticles>(m_animationGeometry)->getParticles();
}
RenderParticleEmitter::Mode
RenderParticleEmitter::getEmitterMode() const
{
return m_mode;
}
void
RenderParticleEmitter::setEmitterSize(const float size)
{
m_emitterSize = size;
}
void
RenderParticleEmitter::setInitialVelocityRange(const Vec3f minDirection,
const Vec3f maxDirection,
const float minSpeed,
const float maxSpeed,
const float minRotationSpeed,
const float maxRotationSpeed)
{
m_minDirection = minDirection;
m_maxDirection = maxDirection;
m_minDirection.normalize();
m_maxDirection.normalize();
m_minSpeed = minSpeed;
m_maxSpeed = maxSpeed;
m_minRotationSpeed = minRotationSpeed;
m_maxRotationSpeed = maxRotationSpeed;
}
bool
RenderParticleEmitter::addKeyFrame(RenderParticleKeyFrame keyFrame)
{
if (m_keyFrames.size() >= c_maxNumKeyFrames)
{
return false;
}
m_keyFrames.push_back(keyFrame);
return true;
}
RenderParticleKeyFrame *
RenderParticleEmitter::getStartKeyFrame()
{
unsigned int index = 0;
for (unsigned int i = 0; i < m_keyFrames.size(); i++)
{
if (m_keyFrames[i].m_time < m_keyFrames[index].m_time)
{
index = i;
}
}
return &m_keyFrames[index];
}
RenderParticleKeyFrame *
RenderParticleEmitter::getEndKeyFrame()
{
unsigned int index = 0;
for (unsigned int i = 0; i < m_keyFrames.size(); i++)
{
if (m_keyFrames[i].m_time > m_keyFrames[index].m_time)
{
index = i;
}
}
return &m_keyFrames[index];
}
std::vector<RenderParticleKeyFrame>&
RenderParticleEmitter::getKeyFrames()
{
return m_keyFrames;
}
void
RenderParticleEmitter::reset()
{
if (m_mode != Mode::BURST)
{
return;
}
auto renderParticles = std::static_pointer_cast<RenderParticles>(m_geometry);
renderParticles->reset();
this->initializeParticles();
}
void
RenderParticleEmitter::update()
{
auto renderParticles = std::static_pointer_cast<RenderParticles>(m_geometry);
if (!m_started)
{
m_stopWatch.start();
m_started = true;
}
auto time = m_stopWatch.getTimeElapsed();
float dt = (float)(time - m_lastUpdateTime);
m_lastUpdateTime = time;
for (auto&& particle : (*m_particles))
{
auto startKeyFrameTemp = *this->getStartKeyFrame();
auto startKeyFrame = &startKeyFrameTemp;
auto endKeyFrameTemp = *this->getEndKeyFrame();
auto endKeyFrame = &endKeyFrameTemp;
particle->m_age += dt;
if (!(particle->m_created) && particle->m_age >= 0)
{
particle->m_created = true;
this->emitParticle(particle);
renderParticles->incrementNumOfParticles();
}
else if (particle->m_age < 0)
{
continue;
}
if (m_mode == RenderParticleEmitter::Mode::CONTINUOUS
&& particle->m_age > m_time)
{
particle->m_age = particle->m_age - ((int)(particle->m_age / m_time) * m_time);
this->emitParticle(particle);
}
// Search for nearest keyframe
for (unsigned int i = 0; i < m_keyFrames.size(); i++)
{
auto keyFrame = &m_keyFrames[i];
if (particle->m_age >= keyFrame->m_time
&& keyFrame->m_time > startKeyFrame->m_time)
{
startKeyFrame = keyFrame;
}
if (particle->m_age < keyFrame->m_time
&& keyFrame->m_time < endKeyFrame->m_time)
{
endKeyFrame = keyFrame;
}
}
// Update velocity and position
particle->m_rotationalAcceleration = startKeyFrame->m_rotationalAcceleration;
particle->m_rotationalVelocity += particle->m_rotationalAcceleration * (dt / 1000.0f);
particle->m_rotation += particle->m_rotationalVelocity * (dt / 1000.0f);
particle->m_acceleration = startKeyFrame->m_acceleration;
particle->m_velocity += particle->m_acceleration * (dt / 1000.0);
particle->m_position += particle->m_velocity * (dt / 1000.0);
float timeDifference = endKeyFrame->m_time - startKeyFrame->m_time;
float alpha = (particle->m_age - startKeyFrame->m_time) / timeDifference;
particle->m_scale = (alpha * endKeyFrame->m_scale)
+ ((1.0f - alpha) * startKeyFrame->m_scale);
this->interpolateColor(particle->m_color,
endKeyFrame->m_color,
startKeyFrame->m_color,
alpha);
}
}
void
RenderParticleEmitter::initializeParticles()
{
m_particles->clear();
auto particles = std::static_pointer_cast<RenderParticles>(m_animationGeometry);
for (unsigned int i = 0; i < particles->getMaxNumParticles(); i++)
{
m_particles->push_back(std::unique_ptr<RenderParticle>(new RenderParticle()));
(*m_particles)[i]->m_age = -(i / (float)(particles->getMaxNumParticles())) * m_emitTime;
(*m_particles)[i]->m_created = false;
}
}
void
RenderParticleEmitter::emitParticle(std::unique_ptr<RenderParticle>& particle)
{
auto position = m_animationGeometry->getTranslation();
if (m_shape == Shape::CUBE)
{
float x = (getRandomNormalizedFloat() - 0.5f) * m_emitterSize;
float y = (getRandomNormalizedFloat() - 0.5f) * m_emitterSize;
float z = (getRandomNormalizedFloat() - 0.5f) * m_emitterSize;
particle->m_position[0] = (float)position[0] + x;
particle->m_position[1] = (float)position[1] + y;
particle->m_position[2] = (float)position[2] + z;
}
float randomRotation = getRandomNormalizedFloat() * (float)imstk::PI * 2.0f;
float randomRotationalVelocity = getRandomNormalizedFloat();
particle->m_rotation = randomRotation;
particle->m_rotationalVelocity = (randomRotationalVelocity * m_minRotationSpeed) +
((1.0f - randomRotationalVelocity) * m_maxRotationSpeed);
float randomDirectionX = getRandomNormalizedFloat();
float randomDirectionY = getRandomNormalizedFloat();
float randomDirectionZ = getRandomNormalizedFloat();
float randomSpeed = getRandomNormalizedFloat();
float speed = (randomSpeed * m_minSpeed) + ((1.0f - randomSpeed) * m_maxSpeed);
float directionX = (randomDirectionX * m_minDirection[0]) + ((1.0f - randomDirectionX) * m_maxDirection[0]);
float directionY = (randomDirectionY * m_minDirection[1]) + ((1.0f - randomDirectionY) * m_maxDirection[1]);
float directionZ = (randomDirectionZ * m_minDirection[2]) + ((1.0f - randomDirectionZ) * m_maxDirection[2]);
Vec3f direction(directionX, directionY, directionZ);
direction.normalize();
particle->m_velocity[0] = directionX * speed;
particle->m_velocity[1] = directionY * speed;
particle->m_velocity[2] = directionZ * speed;
}
void
RenderParticleEmitter::interpolateColor(Color& destination,
const Color& sourceA,
const Color& sourceB,
const float alpha)
{
destination.r = (sourceA.r * alpha) + (sourceB.r * (1.0f - alpha));
destination.g = (sourceA.g * alpha) + (sourceB.g * (1.0f - alpha));
destination.b = (sourceA.b * alpha) + (sourceB.b * (1.0f - alpha));
destination.a = (sourceA.a * alpha) + (sourceB.a * (1.0f - alpha));
}
float
RenderParticleEmitter::getRandomNormalizedFloat()
{
return (float)std::rand() / RAND_MAX;
}
}
/*=========================================================================
Library: iMSTK
Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
& Imaging in Medicine, Rensselaer Polytechnic Institute.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.txt
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,