diff --git a/SPlisHSPlasH/Emitter.cpp b/SPlisHSPlasH/Emitter.cpp index 9726e58f..4902c4c8 100644 --- a/SPlisHSPlasH/Emitter.cpp +++ b/SPlisHSPlasH/Emitter.cpp @@ -1,401 +1,216 @@ #include "Emitter.h" +#include "FluidModel.h" #include "SPHKernels.h" -#include +#include "Simulation.h" #include "TimeManager.h" #include "TimeStep.h" -#include "FluidModel.h" -#include "Simulation.h" +#include "Utilities/Logger.h" +#include using namespace SPH; -Emitter::Emitter(FluidModel *model, - const unsigned int width, const unsigned int height, - const Vector3r &pos, const Matrix3r & rotation, - const Real velocity, - const unsigned int type) - : m_model(model) - , m_width(width) - , m_height(height) - , m_x(pos) - , m_rotation(rotation) - , m_velocity(velocity) - , m_type(type) - , m_nextEmitTime(0) - , m_emitStartTime(0) - , m_emitEndTime(std::numeric_limits::max()) - , m_emitCounter(0) +Emitter::Emitter(FluidModel* model, const unsigned int width, const unsigned int height, const Vector3r& pos, + const Matrix3r& rotation, const Real velocity, const unsigned int type, const bool useBoundary, + const unsigned int velocityProfile) + : m_model(model), m_width(width), m_x(pos), m_rotation(rotation), m_velocity(velocity), m_type(type), + m_useBoundary(useBoundary), m_velocityProfile(velocityProfile) { + Simulation* sim = Simulation::getCurrent(); + m_depth = getDepth(); + + if (m_type == 1) { + // for cylindrical emitters, the height must not be smaller than the width, to spawn the initial particles. + m_height = width; + } + else { + m_height = height; + } + + m_size = getSize(static_cast(m_width), static_cast(m_height), m_type); } -Emitter::~Emitter(void) -{ -} +Emitter::~Emitter(void) {} -void Emitter::reset() -{ - m_nextEmitTime = m_emitStartTime; - m_emitCounter = 0; -} +void Emitter::reset() { m_emitCounter = 0; } -Vector3r Emitter::getSize(const Real width, const Real height, const int type) +int Emitter::getDepth() { - Simulation *sim = Simulation::getCurrent(); - const Real radius = sim->getParticleRadius(); - const Real diam = static_cast(2.0)*radius; - - const Real supportRadius = sim->getSupportRadius(); - const Real animationMarginAround = diam; - Vector3r size; - if (type == 0) - { - if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) - { - size = { - 2 * supportRadius, - height * diam + 2 * animationMarginAround, - width * diam + 2 * animationMarginAround - }; - } - else - { - size = { - static_cast(2.0)* supportRadius, - height * diam + static_cast(2.5) * animationMarginAround, - width * diam + static_cast(2.5) * animationMarginAround - }; - } - } - else - { - if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) - { - // height and radius of cylinder - const Real h = 2 * supportRadius; - const Real r = 0.5f * width * diam + animationMarginAround; - size = { h, 2 * r, 2 * r }; - } - else - { - // height and radius of cylinder - const Real h = static_cast(2.25) * supportRadius; - const Real r = 0.5f * width * diam + animationMarginAround; - size = { h, static_cast(2.25) * r, static_cast(2.25) * r }; - } - } - - return size; + // This is its own function to not repeat the calculation in the constructor but still have it available for the + // static getSize(). + Simulation* sim = Simulation::getCurrent(); + return static_cast(std::ceil(sim->getSupportRadius() / sim->getParticleRadius())); } -void Emitter::emitParticles(std::vector &reusedParticles, unsigned int &indexReuse, unsigned int &numEmittedParticles) +Vector3r Emitter::getSize(const Real width, const Real height, const int type) { - TimeManager *tm = TimeManager::getCurrent(); - const Real t = tm->getTime(); - const Real timeStepSize = tm->getTimeStepSize(); - const Vector3r & emitDir = m_rotation.col(0); - Vector3r emitVel = m_velocity * emitDir; - Simulation *sim = Simulation::getCurrent(); - const Real radius = sim->getParticleRadius(); - const Real diam = static_cast(2.0)*radius; - - // shortly before the emitter starts, cleanup the emitter from particles - if (t < m_emitStartTime || t > m_emitEndTime) - emitVel = emitDir * radius * 10 / 0.25; - - if (t >= m_emitStartTime - 0.25 && t <= m_emitEndTime) - { - // animate emitted particles - const Vector3r & x0 = m_x; - - const Real animationMarginAhead = sim->getSupportRadius(); - const Vector3r size = getSize(static_cast(m_width), static_cast(m_height), m_type); - const Vector3r halfSize = 0.5 * size; - const Vector3r pos = x0 + static_cast(0.5) * animationMarginAhead * emitDir; - - const unsigned int nModels = sim->numberOfFluidModels(); - for (unsigned int m = 0; m < nModels; m++) - { - FluidModel *fm = sim->getFluidModel(m); - const unsigned int numParticles = fm->numActiveParticles(); - #pragma omp parallel for schedule(static) default(shared) - for (int i = 0; i < (int)numParticles; i++) - { - Vector3r &xi = fm->getPosition(i); - if (inBox(xi, pos, m_rotation, halfSize)) - { - fm->getVelocity(i) = emitVel; - fm->getPosition(i) += timeStepSize * emitVel; - fm->setParticleState(i, ParticleState::AnimatedByEmitter); - fm->setObjectId(i, m_objectId); - } - } - } - } - if (t < m_nextEmitTime || t > m_emitEndTime) - { - return; - } - - const Vector3r axisHeight = m_rotation.col(1); - const Vector3r axisWidth = m_rotation.col(2); - - const Real startX = -static_cast(0.5)*(m_width - 1)*diam; - const Real startZ = -static_cast(0.5)*(m_height - 1)*diam; - - // t-m_nextEmitTime is the time that has passed between the time the particle has been emitted and the current time step. - // timeStepSize is added because emission happens at the end of the time step, but the particles are not animated anymore. - const Real dt = t - m_nextEmitTime + timeStepSize; - - const Vector3r velocityOffset = dt * emitVel; - const Vector3r offset = m_x + velocityOffset; - - if ((m_model->numActiveParticles() < m_model->numParticles()) || - (reusedParticles.size() > 0)) - { - unsigned int indexNotReuse = m_model->numActiveParticles(); - for (unsigned int i = 0; i < m_width; i++) - { - for (unsigned int j = 0; j < m_height; j++) - { - unsigned int index = 0; - bool reused = false; - if (indexReuse < reusedParticles.size()) - { - index = reusedParticles[indexReuse]; - reused = true; - } - else - { - index = indexNotReuse; - } - if (index < m_model->numParticles()) - { - m_model->getPosition(index) = (i*diam + startX)*axisWidth + (j*diam + startZ)*axisHeight + offset; - m_model->getVelocity(index) = emitVel; - m_model->setParticleState(index, ParticleState::AnimatedByEmitter); - m_model->setObjectId(index, m_objectId); - - if (reused) - { - indexReuse++; - } - else - { - numEmittedParticles++; - indexNotReuse++; - } - index++; - } - } - } - - if (numEmittedParticles != 0) - { - m_model->setNumActiveParticles(m_model->numActiveParticles() + numEmittedParticles); - sim->emittedParticles(m_model, m_model->numActiveParticles() - numEmittedParticles); - sim->getNeighborhoodSearch()->resize_point_set(m_model->getPointSetIndex(), &m_model->getPosition(0)[0], m_model->numActiveParticles()); - } - } - else - { - if (m_model->numActiveParticles() < m_model->numParticles()) - { - unsigned int index = m_model->numActiveParticles(); - for (unsigned int i = 0; i < m_width; i++) - { - for (unsigned int j = 0; j < m_height; j++) - { - if (index < m_model->numParticles()) - { - m_model->getPosition(index) = (i*diam + startX)*axisWidth + (j*diam + startZ)*axisHeight + offset; - m_model->getVelocity(index) = emitVel; - m_model->setParticleState(index, ParticleState::AnimatedByEmitter); - m_model->setObjectId(index, m_objectId); - numEmittedParticles++; - } - index++; - } - } - m_model->setNumActiveParticles(m_model->numActiveParticles() + numEmittedParticles); - sim->emittedParticles(m_model, m_model->numActiveParticles() - numEmittedParticles); - sim->getNeighborhoodSearch()->resize_point_set(m_model->getPointSetIndex(), &m_model->getPosition(0)[0], m_model->numActiveParticles()); - } - } - - m_nextEmitTime += diam / m_velocity; - m_emitCounter++; + const Simulation* sim = Simulation::getCurrent(); + const Real particleDiameter = 2 * sim->getParticleRadius(); + const Real depth = static_cast(getDepth()); + Vector3r size; + + // The emitter must be larger in emit direction, else particles may spawn just outside. + if (type == 0) { + // box emitter + size = {depth + 1, height, width}; + } + else if (type == 1) { + // cylindrical emitter + size = {depth + 1, width, width}; + } + + return size * particleDiameter; } - - -void Emitter::emitParticlesCircle(std::vector &reusedParticles, unsigned int &indexReuse, unsigned int &numEmittedParticles) +Vector3r Emitter::getSizeExtraMargin(const Real width, const Real height, const int type) { - TimeManager *tm = TimeManager::getCurrent(); - const Real t = tm->getTime(); - const Real timeStepSize = tm->getTimeStepSize(); - const Vector3r & emitDir = m_rotation.col(0); - Simulation *sim = Simulation::getCurrent(); - const Real particleRadius = sim->getParticleRadius(); - const Real diam = static_cast(2.0)*particleRadius; - Vector3r emitVel = m_velocity * emitDir; - - // shortly before the emitter starts, cleanup the emitter from particles - if (t < m_emitStartTime || t > m_emitEndTime) - emitVel = emitDir * particleRadius * 10 / 0.25; - - if (t >= m_emitStartTime - 0.25 && t <= m_emitEndTime) - { - // animate emitted particles - const Vector3r & x0 = m_x; - - const Real animationMarginAhead = sim->getSupportRadius(); - const Vector3r size = getSize(static_cast(m_width), static_cast(m_height), m_type); - const Real h = size[0]; - const Real r = 0.5f * size[1]; - const Real r2 = r * r; - const Vector3r pos = x0 + 0.5f * animationMarginAhead * emitDir; - - - const unsigned int nModels = sim->numberOfFluidModels(); - for (unsigned int m = 0; m < nModels; m++) - { - FluidModel *fm = sim->getFluidModel(m); - const unsigned int numParticles = fm->numActiveParticles(); - #pragma omp parallel for schedule(static) default(shared) - for (int i = 0; i < (int)numParticles; i++) - { - Vector3r &xi = fm->getPosition(i); - if (inCylinder(xi, pos, m_rotation, h, r2)) - { - fm->getVelocity(i) = emitVel; - fm->getPosition(i) += timeStepSize * emitVel; - fm->setParticleState(i, ParticleState::AnimatedByEmitter); - fm->setObjectId(i, m_objectId); - } - } - } - } - if (t < m_nextEmitTime || t > m_emitEndTime) - { - return; - } - - Vector3r axisHeight = m_rotation.col(1); - Vector3r axisWidth = m_rotation.col(2); - - const Real radius = (static_cast(0.5) * (Real)m_width * diam); - const Real radius2 = radius*radius; - - const Real startX = -static_cast(0.5)*(m_width - 1)*diam; - const Real startZ = -static_cast(0.5)*(m_width - 1)*diam; - - // t-m_nextEmitTime is the time that has passed between the time the particle has been emitted and the current time step. - // timeStepSize is added because emission happens at the end of the time step, but the particles are not animated anymore. - const Real dt = t - m_nextEmitTime + timeStepSize; - const Vector3r velocity = emitVel; - const Vector3r velocityOffset = dt * velocity; - const Vector3r offset = m_x + velocityOffset; - - if ((m_model->numActiveParticles() < m_model->numParticles()) || - (reusedParticles.size() > 0)) - { - unsigned int indexNotReuse = m_model->numActiveParticles(); - for (unsigned int i = 0; i < m_width; i++) - { - for (unsigned int j = 0; j < m_width; j++) - { - const Real x = (i*diam + startX); - const Real y = (j*diam + startZ); - - unsigned int index = 0; - bool reused = false; - if (indexReuse < reusedParticles.size()) - { - index = reusedParticles[indexReuse]; - reused = true; - } - else - { - index = indexNotReuse; - } - - if ((index < m_model->numParticles()) && (x*x + y*y <= radius2)) - { - m_model->getPosition(index) = x*axisWidth + y*axisHeight + offset; - m_model->getVelocity(index) = velocity; - m_model->setParticleState(index, ParticleState::AnimatedByEmitter); - m_model->setObjectId(index, m_objectId); - - if (reused) - { - indexReuse++; - } - else - { - numEmittedParticles++; - indexNotReuse++; - } - index++; - } - } - } - - if (numEmittedParticles != 0) - { - m_model->setNumActiveParticles(m_model->numActiveParticles() + numEmittedParticles); - sim->emittedParticles(m_model, m_model->numActiveParticles() - numEmittedParticles); - sim->getNeighborhoodSearch()->resize_point_set(m_model->getPointSetIndex(), &m_model->getPosition(0)[0], m_model->numActiveParticles()); - } - } - else - { - if (m_model->numActiveParticles() < m_model->numParticles()) - { - unsigned int index = m_model->numActiveParticles(); - for (unsigned int i = 0; i < m_width; i++) - { - for (unsigned int j = 0; j < m_width; j++) - { - const Real x = (i*diam + startX); - const Real y = (j*diam + startZ); - if ((index < m_model->numParticles()) && (x*x + y*y <= radius2)) - { - m_model->getPosition(index) = x*axisWidth + y*axisHeight + offset; - m_model->getVelocity(index) = velocity; - m_model->setParticleState(index, ParticleState::AnimatedByEmitter); - m_model->setObjectId(index, m_objectId); - numEmittedParticles++; - index++; - } - } - } - m_model->setNumActiveParticles(m_model->numActiveParticles() + numEmittedParticles); - sim->emittedParticles(m_model, m_model->numActiveParticles() - numEmittedParticles); - sim->getNeighborhoodSearch()->resize_point_set(m_model->getPointSetIndex(), &m_model->getPosition(0)[0], m_model->numActiveParticles()); - } - } - - m_nextEmitTime += diam / m_velocity; - m_emitCounter++; + // The box or cylinder around the emitter needs some padding. + const Simulation* sim = Simulation::getCurrent(); + // const BoundaryHandlingMethods* boundaryMethod = sim->getBoundaryHandlingMethod(); + Real extraMargin; + if (type == 0) { + // box emitter + if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) { + extraMargin = static_cast(2.0); + } + else { + extraMargin = static_cast(2.5); + } + } + else if (type == 1) { + // cylindrical emitter + if (sim->getBoundaryHandlingMethod() == BoundaryHandlingMethods::Akinci2012) { + extraMargin = static_cast(2.0); + } + else { + extraMargin = static_cast(2.5); + } + } + else { + extraMargin = 0.0; + } + + return getSize(width + extraMargin, height + extraMargin, type); } -void Emitter::step(std::vector &reusedParticles, unsigned int &indexReuse, unsigned int &numEmittedParticles) +void Emitter::emitParticles(std::vector& reusedParticles, unsigned int& indexReuse, + unsigned int& numEmittedParticles) { - if (m_type == 1) - emitParticlesCircle(reusedParticles, indexReuse, numEmittedParticles); - else - emitParticles(reusedParticles, indexReuse, numEmittedParticles); + TimeManager* tm = TimeManager::getCurrent(); + const Real t = tm->getTime(); + const Real timeStepSize = tm->getTimeStepSize(); + const Vector3r axisDepth = m_rotation.col(0); + const Vector3r axisHeight = m_rotation.col(1); + const Vector3r axisWidth = m_rotation.col(2); + Vector3r bulkEmitVel = m_velocity * axisDepth; + Simulation* sim = Simulation::getCurrent(); + const Real particleRadius = sim->getParticleRadius(); + const Real particleDiameter = static_cast(2.0) * particleRadius; + + unsigned int indexNextNewParticle = m_model->numActiveParticles(); + + // fill the emitter at the start of the simulation + // The emitter is filled starting from the plane the particles leave the emitter. + // TODO: maybe this should be done in the constructor or somewhere else? + const Vector3r startPos = m_x + m_depth * particleRadius * axisDepth - (m_width - 1) * particleRadius * axisWidth + - (m_height - 1) * particleRadius * axisHeight; + if (t == 0.0) { + for (unsigned int i = 0; i < m_depth; i++) { + for (unsigned int j = 0; j < m_width; j++) { + for (unsigned int k = 0; k < m_height; k++) { + const Vector3r spawnPos = startPos - i * axisDepth * particleDiameter + + j * axisWidth * particleDiameter + k * axisHeight * particleDiameter; + + // Spawn particles in a grid. + // Skip the particles outside when a cylindrical emitter is used. + if (m_type == 0 + || (m_type == 1 + && inCylinder(spawnPos, m_x, m_rotation, m_size[0], (m_size[1] * m_size[1]) / 4))) + { + m_model->setPosition(indexNextNewParticle, spawnPos); + m_model->setParticleState(indexNextNewParticle, ParticleState::AnimatedByEmitter); + m_model->setObjectId(indexNextNewParticle, m_objectId); + + indexNextNewParticle++; + numEmittedParticles++; + } + } + } + } + } + + if (t >= m_emitStartTime && t < m_emitEndTime) { + // emitter is active + for (unsigned int i = 0; i < indexNextNewParticle; i++) { + const Vector3r tempPos = m_model->getPosition(i); + // Check if the particle is inside the emitter. + const bool insideEmitter = (m_type == 0 && inBox(tempPos, m_x, m_rotation, 0.5 * m_size)) + || (m_type == 1 && inCylinder(tempPos, m_x, m_rotation, m_size[0], (m_size[1] * m_size[1]) / 4)); + + if (insideEmitter) { + // Advect ALL particles inside the emitter. + // + // TODO: Doesn't get all particles within the rigid body. Perhaps use getSizeExtraMargin()? + Vector3r localEmitVel; + if (m_type == 1 && m_velocityProfile != 0) { + // different velocity profiles for circular emitters + const Vector3r localPos = tempPos - m_x; + const Real r = sqrt(pow(axisWidth.dot(localPos), 2.0) + (pow(axisHeight.dot(localPos), 2.0))); + const Vector3r maxEmitVel = + (1.0 / (1.0 - (2.0 / (static_cast(m_velocityProfile) + 2.0)))) * bulkEmitVel; + const Real relativeRadius = r / (m_size[1] / 2.0); + localEmitVel = maxEmitVel * (1.0 - pow(relativeRadius, static_cast(m_velocityProfile))); + } + else {// box shaped emitter or uniform velocity profile + localEmitVel = bulkEmitVel; + } + + m_model->setPosition(i, tempPos + timeStepSize * localEmitVel); + m_model->setVelocity(i, localEmitVel); + } + if (!insideEmitter && m_model->getParticleState(i) == ParticleState::AnimatedByEmitter + && m_model->getObjectId(i) == m_objectId) + { + // particle has left the emitter during last step + m_model->setParticleState(i, ParticleState::Active); + m_model->setObjectId(i, 0); + // reuse or spawn a new particle upstream + if (indexReuse < reusedParticles.size()) { + // reuse a particle + m_model->setPosition(reusedParticles[indexReuse], tempPos - axisDepth * particleDiameter * m_depth); + m_model->setParticleState(reusedParticles[indexReuse], ParticleState::AnimatedByEmitter); + m_model->setVelocity(reusedParticles[indexReuse], bulkEmitVel); + m_model->setObjectId(reusedParticles[indexReuse], m_objectId); + indexReuse++; + } + else if (m_model->numActiveParticles() < m_model->numParticles()) { + // spawn a new particle + m_model->setPosition(indexNextNewParticle, tempPos - axisDepth * particleDiameter * m_depth); + m_model->setParticleState(indexNextNewParticle, ParticleState::AnimatedByEmitter); + m_model->setObjectId(indexNextNewParticle, m_objectId); + + indexNextNewParticle++; + numEmittedParticles++; + } + else { + LOG_INFO << "No particles left for the emitter to reuse or activate!"; + } + } + } + } + + m_model->setNumActiveParticles(m_model->numActiveParticles() + numEmittedParticles); + sim->emittedParticles(m_model, m_model->numActiveParticles() - numEmittedParticles); + sim->getNeighborhoodSearch()->resize_point_set(m_model->getPointSetIndex(), &m_model->getPosition(0)[0], + m_model->numActiveParticles()); } -void SPH::Emitter::saveState(BinaryFileWriter &binWriter) +void Emitter::step(std::vector& reusedParticles, unsigned int& indexReuse, + unsigned int& numEmittedParticles) { - binWriter.write(m_nextEmitTime); - binWriter.write(m_emitCounter); + emitParticles(reusedParticles, indexReuse, numEmittedParticles); } -void SPH::Emitter::loadState(BinaryFileReader &binReader) -{ - binReader.read(m_nextEmitTime); - binReader.read(m_emitCounter); -} +void SPH::Emitter::saveState(BinaryFileWriter& binWriter) { binWriter.write(m_emitCounter); } +void SPH::Emitter::loadState(BinaryFileReader& binReader) { binReader.read(m_emitCounter); } diff --git a/SPlisHSPlasH/Emitter.h b/SPlisHSPlasH/Emitter.h index 6425f685..e9edbf37 100644 --- a/SPlisHSPlasH/Emitter.h +++ b/SPlisHSPlasH/Emitter.h @@ -11,26 +11,27 @@ namespace SPH class Emitter { public: - Emitter(FluidModel *model, - const unsigned int width, const unsigned int height, - const Vector3r &pos, const Matrix3r & rotation, - const Real velocity, - const unsigned int type = 0); - virtual ~Emitter(); + Emitter(FluidModel* model, const unsigned int width, const unsigned int height, const Vector3r& pos, + const Matrix3r& rotation, const Real velocity, const unsigned int type = 0, const bool useBoundary = false, + const unsigned int velocityProfile = 0); + virtual ~Emitter(); protected: FluidModel *m_model; unsigned int m_width; unsigned int m_height; + unsigned int m_depth; + Vector3r m_size{0, 0, 0}; Vector3r m_x; Matrix3r m_rotation; Real m_velocity; unsigned int m_type; - Real m_nextEmitTime; - Real m_emitStartTime; - Real m_emitEndTime; - unsigned int m_emitCounter; + Real m_emitStartTime{0}; + Real m_emitEndTime{std::numeric_limits::max()}; + unsigned int m_emitCounter{0}; unsigned int m_objectId; + bool m_useBoundary; + unsigned int m_velocityProfile;// 0: constant, 1: linear, 2: quadratic FORCE_INLINE bool inBox(const Vector3r &x, const Vector3r &xBox, const Matrix3r &rotBox, const Vector3r &scaleBox) { @@ -53,12 +54,11 @@ namespace SPH public: void emitParticles(std::vector &reusedParticles, unsigned int &indexReuse, unsigned int &numEmittedParticles); - void emitParticlesCircle(std::vector &reusedParticles, unsigned int &indexReuse, unsigned int &numEmittedParticles); - Real getNextEmitTime() const { return m_nextEmitTime; } - void setNextEmitTime(Real val) { m_nextEmitTime = val; } - void setEmitStartTime(Real val) { m_emitStartTime = val; setNextEmitTime(val); } + void setEmitStartTime(Real val) { m_emitStartTime = val; } void setEmitEndTime(Real val) { m_emitEndTime = val; } + static int getDepth(); static Vector3r getSize(const Real width, const Real height, const int type); + static Vector3r getSizeExtraMargin(const Real width, const Real height, const int type); void step(std::vector &reusedParticles, unsigned int &indexReuse, unsigned int &numEmittedParticles); virtual void reset(); diff --git a/SPlisHSPlasH/EmitterSystem.cpp b/SPlisHSPlasH/EmitterSystem.cpp index 126092bb..e1c1cd9a 100644 --- a/SPlisHSPlasH/EmitterSystem.cpp +++ b/SPlisHSPlasH/EmitterSystem.cpp @@ -59,17 +59,6 @@ void EmitterSystem::step() // reset particle state Simulation *sim = Simulation::getCurrent(); const unsigned int nModels = sim->numberOfFluidModels(); - for (unsigned int m = 0; m < nModels; m++) - { - FluidModel *fm = sim->getFluidModel(m); - const unsigned int numParticles = fm->numActiveParticles(); - #pragma omp parallel for schedule(static) default(shared) - for (int i = 0; i < (int)numParticles; i++) - { - if (fm->getParticleState(i) == ParticleState::AnimatedByEmitter) - fm->setParticleState(i, ParticleState::Active); - } - } reuseParticles(); unsigned int indexReuse = 0; @@ -93,16 +82,12 @@ void EmitterSystem::reset() } } -void EmitterSystem::addEmitter(const unsigned int width, const unsigned int height, - const Vector3r &pos, const Matrix3r & rotation, - const Real velocity, - const unsigned int type) +void EmitterSystem::addEmitter(const unsigned int width, const unsigned int height, const Vector3r& pos, + const Matrix3r& rotation, const Real velocity, const unsigned int type, + const bool useBoundary, const unsigned int velocityProfile) { - m_emitters.push_back(new Emitter(m_model, - width, height, - pos, rotation, - velocity, - type)); + m_emitters.push_back( + new Emitter(m_model, width, height, pos, rotation, velocity, type, useBoundary, velocityProfile)); } void EmitterSystem::enableReuseParticles(const Vector3r &boxMin /*= Vector3r(-1, -1, -1)*/, const Vector3r &boxMax /*= Vector3r(1, 1, 1)*/) diff --git a/SPlisHSPlasH/EmitterSystem.h b/SPlisHSPlasH/EmitterSystem.h index a5d1dd7d..cab4d675 100644 --- a/SPlisHSPlasH/EmitterSystem.h +++ b/SPlisHSPlasH/EmitterSystem.h @@ -32,10 +32,9 @@ namespace SPH public: void enableReuseParticles(const Vector3r &boxMin = Vector3r(-1, -1, -1), const Vector3r &boxMax = Vector3r(1, 1, 1)); void disableReuseParticles(); - void addEmitter(const unsigned int width, const unsigned int height, - const Vector3r &pos, const Matrix3r & rotation, - const Real velocity, - const unsigned int type); + void addEmitter(const unsigned int width, const unsigned int height, const Vector3r& pos, const Matrix3r& rotation, + const Real velocity, const unsigned int type, const bool useBoundary = false, + const unsigned int velocityProfile = 0); unsigned int numEmitters() const { return static_cast(m_emitters.size()); } std::vector &getEmitters() { return m_emitters; } diff --git a/SPlisHSPlasH/Utilities/SceneParameterObjects.cpp b/SPlisHSPlasH/Utilities/SceneParameterObjects.cpp index 6be6bbc2..26c28e15 100644 --- a/SPlisHSPlasH/Utilities/SceneParameterObjects.cpp +++ b/SPlisHSPlasH/Utilities/SceneParameterObjects.cpp @@ -17,6 +17,7 @@ int FluidBlockParameterObject::FLUID_BLOCK_VISMESH = -1; int FluidBlockParameterObject::FLUID_BLOCK_MODE = -1; int FluidBlockParameterObject::FLUID_BLOCK_INITIAL_VEL = -1; int FluidBlockParameterObject::FLUID_BLOCK_INITIAL_ANGVEL = -1; +int EmitterParameterObject::EMITTER_USEBOUNDARY = -1; void FluidBlockParameterObject::initParameters() { @@ -138,6 +139,7 @@ int EmitterParameterObject::EMITTER_ROTANGLE = -1; int EmitterParameterObject::EMITTER_STARTTIME = -1; int EmitterParameterObject::EMITTER_ENDTIME = -1; int EmitterParameterObject::EMITTER_TYPE = -1; +int EmitterParameterObject::EMITTER_VELOCITYPROFILE = -1; void EmitterParameterObject::initParameters() { @@ -180,6 +182,15 @@ void EmitterParameterObject::initParameters() EMITTER_TYPE = createNumericParameter("type", "Emitter type", &type); setGroup(EMITTER_TYPE, "Emitter"); setDescription(EMITTER_TYPE, "Defines the shape of the emitter: 0: box, 1: circle."); + + EMITTER_USEBOUNDARY = createBoolParameter("useBoundary", "Use Boundary", &useBoundary); + setGroup(EMITTER_USEBOUNDARY, "Emitter"); + setDescription(EMITTER_USEBOUNDARY, "Creates a boundary model around the emitter if defined"); + + EMITTER_VELOCITYPROFILE = + createNumericParameter("velocityProfile", "Velocity Profile", &velocityProfile); + setGroup(EMITTER_VELOCITYPROFILE, "Emitter"); + setDescription(EMITTER_VELOCITYPROFILE, "Defines the velocity profile of the emitter."); } diff --git a/SPlisHSPlasH/Utilities/SceneParameterObjects.h b/SPlisHSPlasH/Utilities/SceneParameterObjects.h index 1eea317a..0e410f1e 100644 --- a/SPlisHSPlasH/Utilities/SceneParameterObjects.h +++ b/SPlisHSPlasH/Utilities/SceneParameterObjects.h @@ -144,6 +144,8 @@ namespace Utilities Real emitStartTime; Real emitEndTime; unsigned int type; // type: 0 = rectangular, 1 = circle + bool useBoundary; + unsigned int velocityProfile; EmitterParameterObject() { @@ -158,10 +160,13 @@ namespace Utilities emitStartTime = 0.0; emitEndTime = std::numeric_limits::max(); type = 0; + useBoundary = false; + velocityProfile = 0; } - EmitterParameterObject(std::string id_, unsigned int width_, unsigned int height_, Vector3r x_, Real velocity_, Vector3r axis_, - Real angle_, Real emitStartTime_, Real emitEndTime_, unsigned int type_) + EmitterParameterObject(std::string id_, unsigned int width_, unsigned int height_, Vector3r x_, Real velocity_, + Vector3r axis_, Real angle_, Real emitStartTime_, Real emitEndTime_, unsigned int type_, + bool useBoundary_, unsigned int velocityProfile_) { id = id_; width = width_; @@ -173,6 +178,8 @@ namespace Utilities emitStartTime = emitStartTime_; emitEndTime = emitEndTime_; type = type_; + useBoundary = useBoundary_; + velocityProfile = velocityProfile_; } static int EMITTER_ID; @@ -185,6 +192,8 @@ namespace Utilities static int EMITTER_STARTTIME; static int EMITTER_ENDTIME; static int EMITTER_TYPE; + static int EMITTER_USEBOUNDARY; + static int EMITTER_VELOCITYPROFILE; virtual void initParameters(); }; diff --git a/Simulator/PositionBasedDynamicsWrapper/PBDBoundarySimulator.cpp b/Simulator/PositionBasedDynamicsWrapper/PBDBoundarySimulator.cpp index 9586da21..18692d03 100644 --- a/Simulator/PositionBasedDynamicsWrapper/PBDBoundarySimulator.cpp +++ b/Simulator/PositionBasedDynamicsWrapper/PBDBoundarySimulator.cpp @@ -70,7 +70,7 @@ void PBDBoundarySimulator::initBoundaryData() PBDWrapper::RBData rb; rb.x = ed->x; rb.R = AngleAxisr(ed->angle, ed->axis).toRotationMatrix(); - rb.scale = Emitter::getSize(static_cast(ed->width), static_cast(ed->height), ed->type); + rb.scale = Emitter::getSizeExtraMargin(static_cast(ed->width), static_cast(ed->height), ed->type); rb.restitution = 0.6; rb.friction = 0.1; if (ed->type == 0) diff --git a/Simulator/SimulatorBase.cpp b/Simulator/SimulatorBase.cpp index 92a33c98..5bceec79 100644 --- a/Simulator/SimulatorBase.cpp +++ b/Simulator/SimulatorBase.cpp @@ -1314,39 +1314,41 @@ void SimulatorBase::createEmitters() ed->width = 1; ed->type = 0; } - Matrix3r rot = AngleAxisr(ed->angle, ed->axis).toRotationMatrix(); - model->getEmitterSystem()->addEmitter( - ed->width, ed->height, - ed->x, rot, - ed->velocity, - ed->type); - - // Generate boundary geometry around emitters - Emitter *emitter = model->getEmitterSystem()->getEmitters().back(); - BoundaryParameterObject *emitterBoundary = new BoundaryParameterObject(); - emitterBoundary->dynamic = false; - emitterBoundary->isWall = false; - emitterBoundary->color = { 0.2f, 0.2f, 0.2f, 1.0f }; - emitterBoundary->axis = ed->axis; - emitterBoundary->angle = ed->angle; - const Real supportRadius = sim->getSupportRadius(); - const Vector3r & emitDir = rot.col(0); - emitterBoundary->scale = Emitter::getSize(static_cast(ed->width), static_cast(ed->height), ed->type); - const Vector3r pos = ed->x; - emitterBoundary->translation = pos; - emitterBoundary->samplesFile = ""; - emitterBoundary->mapInvert = false; - emitterBoundary->mapResolution = Eigen::Matrix(20, 20, 20); - emitterBoundary->mapThickness = 0.0; - - if (sim->is2DSimulation()) - emitterBoundary->scale[2] = 2 * supportRadius; - - if (ed->type == 0) - emitterBoundary->meshFile = FileSystem::normalizePath(getExePath() + "/resources/emitter_boundary/EmitterBox.obj"); - else if (ed->type == 1) - emitterBoundary->meshFile = FileSystem::normalizePath(getExePath() + "/resources/emitter_boundary/EmitterCylinder.obj"); - scene.boundaryModels.push_back(emitterBoundary); + Matrix3r rot = AngleAxisr(ed->angle, ed->axis.normalized()).toRotationMatrix(); + model->getEmitterSystem()->addEmitter(ed->width, ed->height, ed->x, rot, ed->velocity, ed->type, + ed->useBoundary, ed->velocityProfile); + + Emitter* emitter = model->getEmitterSystem()->getEmitters().back(); + if (ed->useBoundary) { + // Generate boundary geometry around emitters + BoundaryParameterObject* emitterBoundary = new BoundaryParameterObject(); + emitterBoundary->dynamic = false; + emitterBoundary->isWall = false; + emitterBoundary->color = {0.2f, 0.2f, 0.2f, 1.0f}; + emitterBoundary->axis = ed->axis; + emitterBoundary->angle = ed->angle; + const Real supportRadius = sim->getSupportRadius(); + const Vector3r& emitDir = rot.col(0); + emitterBoundary->scale = + Emitter::getSizeExtraMargin(static_cast(ed->width), static_cast(ed->height), ed->type); + const Vector3r pos = ed->x; + emitterBoundary->translation = pos; + emitterBoundary->samplesFile = ""; + emitterBoundary->mapInvert = false; + emitterBoundary->mapResolution = Eigen::Matrix(20, 20, 20); + emitterBoundary->mapThickness = 0.0; + + if (sim->is2DSimulation()) + emitterBoundary->scale[2] = 2 * supportRadius; + + if (ed->type == 0) + emitterBoundary->meshFile = + FileSystem::normalizePath(getExePath() + "/resources/emitter_boundary/EmitterBox.obj"); + else if (ed->type == 1) + emitterBoundary->meshFile = + FileSystem::normalizePath(getExePath() + "/resources/emitter_boundary/EmitterCylinder.obj"); + scene.boundaryModels.push_back(emitterBoundary); + } // reuse particles if they are outside of a bounding box bool emitterReuseParticles = false; diff --git a/pySPlisHSPlasH/EmitterModule.cpp b/pySPlisHSPlasH/EmitterModule.cpp index 179bfaf4..8055d7e2 100644 --- a/pySPlisHSPlasH/EmitterModule.cpp +++ b/pySPlisHSPlasH/EmitterModule.cpp @@ -28,9 +28,6 @@ void EmitterModule(py::module m_sub){ return SPH::Emitter(model, width, height, pos, rotation, velocity, type); })) .def("emitParticles", &SPH::Emitter::emitParticles) - .def("emitParticlesCircle", &SPH::Emitter::emitParticlesCircle) - .def("getNextEmitTime", &SPH::Emitter::getNextEmitTime) - .def("setNextEmitTime", &SPH::Emitter::setNextEmitTime) .def("setEmitStartTime", &SPH::Emitter::setEmitStartTime) .def("setEmitEndTime", &SPH::Emitter::setEmitEndTime) .def("getPosition", &SPH::Emitter::getPosition) diff --git a/pySPlisHSPlasH/UtilitiesModule.cpp b/pySPlisHSPlasH/UtilitiesModule.cpp index 661d4e3d..e40ad2a4 100644 --- a/pySPlisHSPlasH/UtilitiesModule.cpp +++ b/pySPlisHSPlasH/UtilitiesModule.cpp @@ -339,10 +339,12 @@ void UtilitiesModule(py::module m) { py::class_(m_sub_sub, "EmitterData") .def(py::init<>()) - .def(py::init(), - "id"_a="Fluid", "width"_a=5, "height"_a=5, "x"_a=Vector3r::Zero(), - "velocity"_a=1, "axis"_a = Vector3r(1, 0, 0), "angle"_a = 0.0, "emitStartTime"_a=0, - "emitEndTime"_a=std::numeric_limits::max(), "type"_a=0) + .def(py::init(), + "id"_a = "Fluid", "width"_a = 5, "height"_a = 5, "x"_a = Vector3r::Zero(), "velocity"_a = 1, + "axis"_a = Vector3r(1, 0, 0), "angle"_a = 0.0, "emitStartTime"_a = 0, + "emitEndTime"_a = std::numeric_limits::max(), "type"_a = 0, "useBoundary"_a = false, + "velocityProfile"_a = 0) .def_readwrite("id", &Utilities::EmitterParameterObject::id) .def_readwrite("width", &Utilities::EmitterParameterObject::width) .def_readwrite("height", &Utilities::EmitterParameterObject::height) @@ -352,7 +354,8 @@ void UtilitiesModule(py::module m) { .def_readwrite("angle", &Utilities::EmitterParameterObject::angle) .def_readwrite("emitStartTime", &Utilities::EmitterParameterObject::emitStartTime) .def_readwrite("emitEndTime", &Utilities::EmitterParameterObject::emitEndTime) - .def_readwrite("type", &Utilities::EmitterParameterObject::type); + .def_readwrite("type", &Utilities::EmitterParameterObject::type) + .def_readwrite("useBoundary", &Utilities::EmitterParameterObject::useBoundary); py::class_(m_sub_sub, "AnimationFieldData") .def(py::init<>())