Commit 922eda28 authored by Nicholas Milef's avatar Nicholas Milef Committed by Nicholas Milef

ENH: added particle system

parent 226892e9
......@@ -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)
......
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 "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
{
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);
auto particleEmitter = std::make_shared<RenderParticleEmitter>(128, 2000.0f);
particleEmitter->setInitialVelocityRange(Vec3f(-1, 5, -1), Vec3f(1, 5, 1),
0.5, 1.0,
-1.0, 1.0);
particleEmitter->setEmitterSize(0.3f);
particleEmitter->setParticleSize(0.4f);
auto startKeyFrame = particleEmitter->getStartKeyFrame();
startKeyFrame->m_color = Color(1.0, 0.7, 0.0, 1.0);
RenderParticleKeyFrame midFrame0;
midFrame0.m_time = 700.0f;
midFrame0.m_color = Color::Red;
midFrame0.m_scale = 1.5f;
particleEmitter->addKeyFrame(midFrame0);
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);
auto endKeyFrame = particleEmitter->getEndKeyFrame();
endKeyFrame->m_color = Color::Black;
endKeyFrame->m_color.a = 0.0;
endKeyFrame->m_scale = 4.0;
auto particleObject = std::make_shared<VisualObject>("Smoke");
auto particleModel = std::make_shared<VisualModel>(particleEmitter);
particleModel->setRenderMaterial(particleMaterial);
particleObject->addVisualModel(particleModel);
scene->addSceneObject(particleObject);
}
// Sparks
{
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);
auto particleEmitter = std::make_shared<RenderParticleEmitter>(128,
850.0f, RenderParticleEmitter::Mode::BURST);
particleEmitter->setTranslation(2, 0.1, 0);
particleEmitter->setInitialVelocityRange(Vec3f(-1, 5, -1), Vec3f(1, 5, 1),
4.0, 5.0,
-1.0, 1.0);
particleEmitter->setEmitterSize(0.1f);
particleEmitter->setParticleSize(0.3f);
auto startKeyFrame = particleEmitter->getStartKeyFrame();
startKeyFrame->m_acceleration = Vec3f(0, -9.8, 0);
startKeyFrame->m_color = Color::Yellow;
auto endKeyFrame = particleEmitter->getEndKeyFrame();
endKeyFrame->m_color = Color::Orange;
auto particleObject = std::make_shared<VisualObject>("Sparks");
auto particleModel = std::make_shared<VisualModel>(particleEmitter);
particleModel->setRenderMaterial(particleMaterial);
particleObject->addVisualModel(particleModel);
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 geometry = scene->getSceneObject("Sparks")->getVisualModel(0)->getGeometry();
auto sparks = std::static_pointer_cast<RenderParticleEmitter>(geometry);
sparks->reset();
return false;
});
// Run
sdk->setActiveScene(scene);
sdk->startSimulation(SimulationStatus::PAUSED);
return 0;
}
......@@ -33,7 +33,7 @@ DecalPool::DecalPool(unsigned int maxNumDecals /*= 128*/)
else
{
m_maxNumDecals = 128;
LOG(WARNING) << "The maximum number of decals is 256";
LOG(WARNING) << "The maximum number of decals is 128";
}
m_vertexPositions[0] = glm::vec3(0.5, 0.5, 0.5);
......
This diff is collapsed.
/*=========================================================================
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.
=========================================================================*/
#ifndef imstkRenderParticleEmitter_h
#define imstkRenderParticleEmitter_h
#include <vector>
#include <climits>
#include <memory>
#include "glm/glm.hpp"
#include "imstkGeometry.h"
#include "imstkMath.h"
#include "imstkColor.h"
#include "imstkTimer.h"
namespace imstk
{
struct RenderParticle
{
Vec3f m_position = Vec3f(0, 0, 0);
Vec3f m_velocity = Vec3f(0, 0, 0);
Vec3f m_acceleration = Vec3f(0, 0, 0);
Color m_color = Color::White;
float m_age = 0;
bool m_created = false;
float m_scale = 1.0f;
float m_rotation = 0;
float m_rotationalVelocity = 0;
float m_rotationalAcceleration = 0;
};
struct RenderParticleKeyFrame
{
float m_time = 0;
Color m_color = Color::White;
Vec3f m_acceleration = Vec3f(0, 0, 0);
float m_rotationalAcceleration = 0;
float m_scale = 1.0f;
};
///
/// \class RenderParticleEmitter
///
/// \brief Particle emitter
///
class RenderParticleEmitter : public Geometry
{
public:
///
/// \brief Shape of emitter
///
enum class Shape
{
CUBE
};
///
/// \brief Mode of emitter
///
enum class Mode
{
CONTINUOUS, ///< Emitter continuously releases/recycles particles
BURST ///< Emitter releases particles once until manually reset
};
///
/// \brief Constructor
/// \param maxNumParticles Number of particles this emitter can produce
/// \param time Lifespan of each particle (in milliseconds)
/// \param mode Mode for emitter
///
RenderParticleEmitter(unsigned int maxNumParticles = 128,
float time = 3000.0f,
Mode mode = Mode::CONTINUOUS);
///
/// \brief Add keyframe to particle emitter
/// \param keyFrame key frame to add
/// \returns True if key frame added, false if too many key frames
///
bool addKeyFrame(RenderParticleKeyFrame keyFrame);
///
/// \brief Get mode of emitter
/// \returns mode Mode of emitter
///
RenderParticleEmitter::Mode getEmitterMode();
///
/// \brief Set size of emitter
/// \param size Width of emitter
///
void setEmitterSize(float size);
///
/// \brief Set size of particle
/// \param size Particle size, this determines how much each keyframe
/// scales by
///
void setParticleSize(float size);
///
/// \brief Update function
///
void updateParticleEmitter(Vec3d cameraPosition);
///
/// \brief Emit particle
///
void emitParticle(std::unique_ptr<RenderParticle>& particle);
///
/// \brief Get number of particles
///
unsigned int getNumParticles();
///
/// \brief Get particles
/// \returns particles
///
std::vector<std::unique_ptr<RenderParticle>>& getParticles();
///
/// \brief Get start and end frames
///
RenderParticleKeyFrame * getStartKeyFrame();
RenderParticleKeyFrame * getEndKeyFrame();
///
/// \brief Get key frames
/// \returns key frames that are unsorted
///
std::vector<RenderParticleKeyFrame>& getKeyFrames();
///
/// \brief Set velocity range
/// This functions sets minimum and maximum rotation values for determining
/// the initial trajectory of the particles. The values are randomly
/// selected (according to a uniform distribution) between the min and max
/// values. If the values are the same, then the particle direction will
/// not behave randomly.
/// \param minDirection Maximum initial angle of trajectory
/// \param maxDirection Minimum initial angle of trajectory
/// \param minSpeed Minimum initial speed
/// \param maxSpeed Maximum initial speed
/// \param minRotationSpeed Minimum initial rotation speed
/// \param maxRotationSpeed Maximum initial rotation speed
///
void setInitialVelocityRange(Vec3f minDirection,
Vec3f maxDirection,
float minSpeed,
float maxSpeed,
float minRotationSpeed,
float maxRotationSpeed);
///
/// \brief Get uniformly-distributed float
/// \returns float in the range of [0, 1]
///
float getRandomNormalizedFloat();
///
/// \brief Get volume
/// As these are particles, the volume is 0
///
double getVolume() const override { return 0; };
///
/// \brief Reset the emitter
/// Only works for burst particles
///
void reset();
protected:
friend class VulkanParticleRenderDelegate;
///
/// \brief Interpolate color
///
void interpolateColor(Color& destination,
Color& sourceA,
Color& sourceB,
float alpha);
///
/// \brief Initialize particles
///
void initializeParticles();
const int c_maxNumKeyFrames = 16; ///< Maximum key frames
unsigned int m_maxNumParticles = 128; ///< Maximum particles
RenderParticleEmitter::Mode m_mode
= RenderParticleEmitter::Mode::CONTINUOUS;
RenderParticleEmitter::Shape m_shape
= RenderParticleEmitter::Shape::CUBE;
float m_emitterSize = 1.0f;
float m_particleSize = 0.1f;
std::vector<std::unique_ptr<RenderParticle>> m_particles; ///< Particle objects
std::vector<RenderParticleKeyFrame> m_keyFrames; ///< Particle keyframes
imstk::StopWatch m_stopWatch;
glm::vec3 m_vertexPositions[4];
glm::vec3 m_vertexNormals[4];
glm::vec3 m_vertexTangents[4];
glm::vec2 m_vertexUVs[4];
glm::ivec3 m_triangles[2];
Vec3f m_minDirection;
Vec3f m_maxDirection;
float m_minSpeed;
float m_maxSpeed;
float m_minRotationSpeed;
float m_maxRotationSpeed;
void applyTranslation(const Vec3d t) override {};
void applyRotation(const Mat3d r) override {};
void applyScaling(const double s) override {};
virtual void updatePostTransformData() override {};
float m_time; ///< total time for particle system
float m_emitTime;
unsigned int m_numParticles = 0;
double m_lastUpdateTime = 0.0;
bool m_started = false;
};
}
#endif
\ No newline at end of file
......@@ -53,7 +53,8 @@ public:
LineMesh,
Capsule,
Decal,
DecalPool
DecalPool,
RenderParticleEmitter
};
///
......
......@@ -248,17 +248,36 @@ RenderMaterial::setCastsShadows(const bool castsShadows)
m_castsShadows = castsShadows;
}
bool RenderMaterial::getCastsShadows() const
bool
RenderMaterial::getCastsShadows() const
{
return m_castsShadows;
}
void
RenderMaterial::setBlendMode(const RenderMaterial::BlendMode blendMode)
{
m_blendMode = blendMode;
}
const RenderMaterial::BlendMode
RenderMaterial::getBlendMode()
{
return m_blendMode;
}
bool
RenderMaterial::isDecal()
{
return m_isDecal;
}
bool
RenderMaterial::isParticle()
{
return m_isParticle;
}
bool
RenderMaterial::isLineMesh()
{
......
......@@ -44,6 +44,12 @@ public:
WIREFRAME_SURFACE
};
enum class BlendMode
{
ALPHA,
ADDITIVE
};
///
/// \brief Constructor
///
......@@ -137,10 +143,17 @@ public:
bool getCastsShadows() const;
///
/// \brief Checks if the material belongs to a decal
/// \brief Get/Set blend mode
/// This function only works for particles and decals currently
///
bool isDecal();
void setBlendMode(const BlendMode blendMode);
const BlendMode getBlendMode();
///
/// \brief Checks if the material must be handled uniquely
///
bool isDecal();
bool isParticle();
bool isLineMesh();
protected:
......@@ -148,6 +161,7 @@ protected:
friend class VulkanRenderDelegate;
friend class VulkanDecalRenderDelegate;
friend class VulkanLineMeshRenderDelegate;
friend class VulkanParticleRenderDelegate;
friend class VTKdbgLinesRenderDelegate;
// State
......@@ -158,6 +172,7 @@ protected:
bool m_backfaceCulling = true; ///< For performance, uncommon for this to be false
bool m_isDecal = false;
bool m_isLineMesh = false;
bool m_isParticle = false;
// Sphere size used for glyph in rendering (valid only for point set)
double m_sphereGlyphSize = 0.05;
......@@ -184,6 +199,8 @@ protected:
bool m_stateModified = true; ///< Flag for expensive state changes
bool m_modified = true; ///< Flag for any material property changes
bool m_flatShading = false;
BlendMode m_blendMode = BlendMode::ALPHA;
};
}
......
......@@ -46,6 +46,7 @@ set(VULKAN_H_FILES
VulkanRenderer/RenderDelegate/imstkVulkanCubeRenderDelegate.h
VulkanRenderer/RenderDelegate/imstkVulkanDecalRenderDelegate.h
VulkanRenderer/RenderDelegate/imstkVulkanLineMeshRenderDelegate.h
VulkanRenderer/RenderDelegate/imstkVulkanParticleRenderDelegate.h
VulkanRenderer/RenderDelegate/imstkVulkanPlaneRenderDelegate.h
VulkanRenderer/RenderDelegate/imstkVulkanRenderDelegate.h
VulkanRenderer/RenderDelegate/imstkVulkanSphereRenderDelegate.h
......@@ -69,6 +70,7 @@ set(VULKAN_CPP_FILES
VulkanRenderer/RenderDelegate/imstkVulkanCubeRenderDelegate.cpp
VulkanRenderer/RenderDelegate/imstkVulkanDecalRenderDelegate.cpp
VulkanRenderer/RenderDelegate/imstkVulkanLineMeshRenderDelegate.cpp
VulkanRenderer/RenderDelegate/imstkVulkanParticleRenderDelegate.cpp
VulkanRenderer/RenderDelegate/imstkVulkanPlaneRenderDelegate.cpp
VulkanRenderer/RenderDelegate/imstkVulkanRenderDelegate.cpp
VulkanRenderer/RenderDelegate/imstkVulkanSphereRenderDelegate.cpp
......
/*=========================================================================
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 "imstkVulkanParticleRenderDelegate.h"
namespace imstk
{
VulkanParticleRenderDelegate::VulkanParticleRenderDelegate(std::shared_ptr<VisualModel> visualModel,
SceneObject::Type type,
VulkanMemoryManager& memoryManager)
{
this->initialize(visualModel);
auto geometry = std::static_pointer_cast<RenderParticleEmitter>(visualModel->getGeometry());
m_numVertices = 4;
m_numTriangles = 2;
m_vertexSize = sizeof(VulkanBasicVertex);
m_visualModel->getRenderMaterial()->m_isParticle = true;
m_particles.reserve(geometry->m_maxNumParticles);
m_particleIndices.reserve(geometry->m_maxNumParticles);
m_particleDistances.reserve(geometry->m_maxNumParticles);
this->initializeData(memoryManager, this->getVisualModel()->getRenderMaterial());
this->updateVertexBuffer();
}
void
VulkanParticleRenderDelegate::updateVertexBuffer()
{
auto vertices = (VulkanBasicVertex *)m_vertexBuffer->getVertexMemory();
auto geometry = std::static_pointer_cast<RenderParticleEmitter>(m_visualModel->getGeometry());
for (unsigned i = 0; i < m_numVertices; i++)
{
vertices[i].position = geometry->m_vertexPositions[i];
vertices[i].uv = geometry->m_vertexUVs[i];
vertices[i].normal = geometry->m_vertexNormals[i];
}
auto triangles = (std::array<uint32_t, 3> *)m_vertexBuffer->getIndexMemory();
for (unsigned i = 0; i < m_numTriangles; i++)
{
triangles[i][0] = (uint32_t)geometry->m_triangles[i].x;
triangles[i][1] = (uint32_t)geometry->m_triangles[i].y;
triangles[i][2] = (uint32_t)geometry->m_triangles[i].z;
}
}
void
VulkanParticleRenderDelegate::initializeData(VulkanMemoryManager& memoryManager, std::shared_ptr<RenderMaterial> material)
{
m_vertexUniformBuffer = std::make_shared<VulkanUniformBuffer>(memoryManager, (uint32_t)sizeof(VulkanLocalDecalVertexUniforms));
m_fragmentUniformBuffer = std::make_shared<VulkanUniformBuffer>(memoryManager, (uint32_t)sizeof(VulkanLocalDecalFragmentUniforms));
m_material = std::make_shared<VulkanMaterialDelegate>(m_vertexUniformBuffer,
m_fragmentUniformBuffer,
material,
memoryManager);
m_vertexBuffer = std::make_shared<VulkanVertexBuffer>(memoryManager, m_numVertices, m_vertexSize, m_numTriangles);
}
void
VulkanParticleRenderDelegate::update(const uint32_t frameIndex, std::shared_ptr<Camera> camera)
{
unsigned int index = 0;
auto geometry = std::static_pointer_cast<RenderParticleEmitter>(m_visualModel->getGeometry());
geometry->updateParticleEmitter(camera->getPosition());
auto mat = this->getVisualModel()->getRenderMaterial();
auto cameraPosition = glm::vec3(camera->getPosition()[0],
camera->getPosition()[1],
camera->getPosition()[2]);
auto cameraUp = glm::vec3(camera->getViewUp()[0],
camera->getViewUp()[1],
camera->getViewUp()[2]);
auto matColor = mat->getColor();
this->sortParticles(geometry->getParticles(), geometry->getNumParticles(), cameraPosition);
for (unsigned int i = 0; i < geometry->getNumParticles(); i++)
{
auto particlePosition = glm::vec3(m_particles[i]->m_position[0],
m_particles[i]->m_position[1],
m_particles[i]->m_position[2]);
// Point the particle to the camera