diff --git a/Projects/Skylicht/Components/Source/GridPlane/CGridPlaneData.cpp b/Projects/Skylicht/Components/Source/GridPlane/CGridPlaneData.cpp index e85f89b67..117bd492d 100644 --- a/Projects/Skylicht/Components/Source/GridPlane/CGridPlaneData.cpp +++ b/Projects/Skylicht/Components/Source/GridPlane/CGridPlaneData.cpp @@ -43,7 +43,7 @@ namespace Skylicht { clearBuffer(); - SColor color(255, 100, 100, 100); + SColor color(255, 50, 50, 50); core::vector3df start; core::vector3df end; @@ -56,9 +56,9 @@ namespace Skylicht for (int i = 0; i <= NumGrid; i++) { if (i == NumGrid / 2) - color = SColor(255, 255, 255, 255); - else color = SColor(255, 100, 100, 100); + else + color = SColor(255, 50, 50, 50); addLineVertexBatch(start, end, color); @@ -72,9 +72,9 @@ namespace Skylicht for (int i = 0; i <= NumGrid; i++) { if (i == NumGrid / 2) - color = SColor(255, 255, 255, 255); - else color = SColor(255, 100, 100, 100); + else + color = SColor(255, 50, 50, 50); addLineVertexBatch(start, end, color); diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/CParticleComponent.h b/Projects/Skylicht/Components/Source/ParticleSystem/CParticleComponent.h index 0bc37ac36..f8960243f 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/CParticleComponent.h +++ b/Projects/Skylicht/Components/Source/ParticleSystem/CParticleComponent.h @@ -55,8 +55,23 @@ namespace Skylicht return &m_factory; } + inline CParticleBufferData* getData() + { + return m_data; + } + CGroup* createParticleGroup(); + inline u32 getNumOfGroup() + { + return m_data->Groups.size(); + } + + inline CGroup* getGroup(int i) + { + return m_data->Groups[i]; + } + void removeParticleGroup(CGroup* group); }; } diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.cpp b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.cpp index e594dd484..fd6735580 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.cpp +++ b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.cpp @@ -83,7 +83,7 @@ namespace Skylicht return p; } - void CGroup::update(bool updateBuffer) + void CGroup::update(bool visible) { if (m_zone == NULL) return; @@ -108,11 +108,19 @@ namespace Skylicht CParticle *particles = m_particles.pointer(); u32 numParticles = m_particles.size(); - // update particle system - m_particleSystem->update(particles, numParticles, this, dt); + if (visible == true) + { + // update particle system + m_particleSystem->update(particles, numParticles, this, dt); - for (ISystem *s : m_systems) - s->update(particles, numParticles, this, dt); + for (ISystem *s : m_systems) + s->update(particles, numParticles, this, dt); + } + else + { + // we just update life time of hide particle + m_particleSystem->updateLifeTime(particles, numParticles, this, dt); + } if (numParticles > 0) m_bbox.reset(particles[0].Position); @@ -136,8 +144,8 @@ namespace Skylicht } } - // update instancing buffer - if (updateBuffer == true) + // update instancing buffer + if (visible == true) m_bufferSystem->update(particles, numParticles, this, dt); u32 emiterId = 0; diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.h b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.h index 920ccffe7..815168e32 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.h +++ b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CGroup.h @@ -81,7 +81,7 @@ namespace Skylicht virtual ~CGroup(); - void update(bool updateBuffer); + void update(bool visible); inline void setWorldMatrix(const core::matrix4& m) { diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CModel.h b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CModel.h index 3b425c9a3..4d4efccb2 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CModel.h +++ b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CModel.h @@ -68,6 +68,7 @@ namespace Skylicht { m_start1 = f1; m_start2 = f2; + m_haveStart = true; return this; } @@ -84,6 +85,7 @@ namespace Skylicht { m_end1 = f1; m_end2 = f2; + m_haveEnd = true; return this; } @@ -98,6 +100,26 @@ namespace Skylicht return m_haveEnd; } + float getStartValue1() + { + return m_start1; + } + + float getStartValue2() + { + return m_start2; + } + + float getEndValue1() + { + return m_end1; + } + + float getEndValue2() + { + return m_end2; + } + float getRandomStart(); float getRandomEnd(); diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CParticle.h b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CParticle.h index 26784fd64..3e9748080 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CParticle.h +++ b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/CParticle.h @@ -32,7 +32,8 @@ namespace Skylicht { enum EParticleParams { - ScaleX = 0, + Scale = 0, + ScaleX, ScaleY, ScaleZ, ColorR, diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Emitters/CEmitter.cpp b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Emitters/CEmitter.cpp index 77b553064..fae6de472 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Emitters/CEmitter.cpp +++ b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Emitters/CEmitter.cpp @@ -59,7 +59,13 @@ namespace Skylicht nbBorn = core::max_(0, m_tank); m_tank = 0; } - else if (m_tank >= 0) + else if (m_tank < 0) + { + m_fraction += m_flow * deltaTime * 0.001f; + nbBorn = static_cast(m_fraction); + m_fraction -= nbBorn; + } + else if (m_tank > 0) { m_fraction += m_flow * deltaTime * 0.001f; nbBorn = static_cast(m_fraction); diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.cpp b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.cpp index 36fa442b5..c98ec2ccd 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.cpp +++ b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.cpp @@ -42,6 +42,25 @@ namespace Skylicht } + void CParticleSystem::updateLifeTime(CParticle *particles, int num, CGroup *group, float dt) + { + dt = dt * 0.001f; + + CParticle *p; + +#pragma omp parallel for private(p) + for (int i = 0; i < num; i++) + { + p = particles + i; + + // update life time + p->Age = p->Age + dt; + + if (!p->Immortal) + p->Life -= dt; + } + } + void CParticleSystem::update(CParticle *particles, int num, CGroup *group, float dt) { dt = dt * 0.001f; @@ -137,6 +156,13 @@ namespace Skylicht // update param value params[t] = p->StartValue[t] + (p->EndValue[t] - p->StartValue[t]) * y; + + if (t == Scale) + { + params[ScaleX] = params[Scale]; + params[ScaleY] = params[Scale]; + params[ScaleZ] = params[Scale]; + } } } } diff --git a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.h b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.h index 7be51d274..f6e32ab21 100644 --- a/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.h +++ b/Projects/Skylicht/Components/Source/ParticleSystem/Particles/Systems/CParticleSystem.h @@ -37,6 +37,8 @@ namespace Skylicht virtual ~CParticleSystem(); + void updateLifeTime(CParticle *particles, int num, CGroup *group, float dt); + virtual void update(CParticle *particles, int num, CGroup *group, float dt); }; } diff --git a/Samples/Particles/Source/SampleParticles.cpp b/Samples/Particles/Source/SampleParticles.cpp index f8c796cb2..23ba43b48 100644 --- a/Samples/Particles/Source/SampleParticles.cpp +++ b/Samples/Particles/Source/SampleParticles.cpp @@ -2,6 +2,9 @@ #include "SkylichtEngine.h" #include "SampleParticles.h" +#include "imgui.h" +#include "CImguiManager.h" + #include "GridPlane/CGridPlane.h" #include "ParticleSystem/CParticleComponent.h" @@ -12,15 +15,19 @@ void installApplication(const std::vector& argv) } SampleParticles::SampleParticles() : - m_scene(NULL) + m_scene(NULL), + m_label(NULL), + m_currentParticleObj(NULL) { - + CImguiManager::createGetInstance(); } SampleParticles::~SampleParticles() { delete m_scene; delete m_font; + + CImguiManager::releaseInstance(); } void SampleParticles::onInitApp() @@ -67,49 +74,17 @@ void SampleParticles::onInitApp() grid->addComponent(); // Particles - float dis = 4.0f; - - const char *zoneName[] = { - "Point", - "Sphere", - "AABox", - "Cylinder", - "Line", - "PolyLine", - "Ring", - }; - - const char *emitterName[] = { - "Random", - "Straight", - "Spheric", - "Normal" - }; - - for (int i = 0, n = (int)Particle::NumOfZone; i < n; i++) - { - for (int j = 0, m = (int)Particle::NumOfEmitter; j < m; j++) - { - core::vector3df position((j - m / 2) * dis, 0.0f, (i - n / 2) * dis); + m_currentParticleObj = zone->createEmptyObject(); + Particle::CParticleComponent *particleComponent = m_currentParticleObj->addComponent(); - initParticle( - zone->createEmptyObject()->addComponent(), - (Particle::EZone)i, - (Particle::EEmitter)j, - position - ); + updateParticleZone(particleComponent, Particle::Sphere); + updateParticleEmitter(particleComponent, Particle::Random); + updateParticleRenderer(particleComponent); - position.Y = 2.0f; + // 3D text + createCanvasText("", core::vector3df(0.0f, 2.0f, 0.0f)); - std::string label = zoneName[i]; - label += "+"; - label += emitterName[j]; - - createCanvasText(label.c_str(), position); - } - } - - // Rendering + // Rendering pipe line m_forwardRP = new CForwardRP(); } @@ -128,68 +103,105 @@ void SampleParticles::createCanvasText(const char *text, const core::vector3df& rootGUI->setPosition(position); canvas->enable3DBillboard(true); + + m_label = guiText; } -void SampleParticles::initParticle(Particle::CParticleComponent *particleComponent, Particle::EZone zoneType, Particle::EEmitter emitterType, const core::vector3df& position) +Particle::CParticleBufferData* SampleParticles::getParticleData(CGameObject *obj) +{ + return obj->getComponent()->getData(); +} + +Particle::CZone* SampleParticles::updateParticleZone(Particle::CParticleComponent *particleComponent, Particle::EZone zoneType) { Particle::CFactory *factory = particleComponent->getParticleFactory(); - Particle::CGroup *group = particleComponent->createParticleGroup(); - Particle::CZone *zone = NULL; - float flow = 0.0f; + Particle::CGroup *group = NULL; + if (particleComponent->getNumOfGroup() == 0) + group = particleComponent->createParticleGroup(); + else + group = particleComponent->getGroup(0); + + Particle::CZone *zone = group->getZone(); + + // delete old zone + if (zone != NULL) + { + group->setZone(NULL); + factory->deleteZone(zone); + zone = NULL; + } + + float size = 1.5f; switch (zoneType) { case Particle::Point: - flow = 100.0f; zone = group->setZone(factory->createPointZone()); break; case Particle::Sphere: - flow = 200.0f; - zone = group->setZone(factory->createSphereZone(core::vector3df(0.0f, 0.0f, 0.0f), 1.0f)); + zone = group->setZone(factory->createSphereZone(core::vector3df(0.0f, 0.0f, 0.0f), size)); break; case Particle::AABox: - flow = 200.0f; - zone = group->setZone(factory->createAABoxZone(core::vector3df(0.0f, 0.0f, 0.0f), core::vector3df(1.0f, 1.0f, 1.0f))); + zone = group->setZone(factory->createAABoxZone(core::vector3df(0.0f, 0.0f, 0.0f), core::vector3df(size, size, size))); break; case Particle::Cylinder: - flow = 200.0f; - zone = group->setZone(factory->createCylinderZone(core::vector3df(0.0f, 0.0f, 0.0f), core::vector3df(0.0f, 1.0f, 0.0f), 0.5f, 1.0f)); + zone = group->setZone(factory->createCylinderZone(core::vector3df(0.0f, 0.0f, 0.0f), core::vector3df(0.0f, size, 0.0f), size * 0.5f, 1.0f)); break; case Particle::Line: - flow = 100.0f; - zone = group->setZone(factory->createLineZone(core::vector3df(-1.0f, 0.0f, 0.0f), core::vector3df(1.0f, 0.0f, 0.0f))); + zone = group->setZone(factory->createLineZone(core::vector3df(-size, 0.0f, 0.0f), core::vector3df(size, 0.0f, 0.0f))); break; case Particle::PolyLine: { - flow = 200.0f; core::array points; - points.push_back(core::vector3df(-1.0f, 0.0f, -1.0f)); - points.push_back(core::vector3df(1.0f, 0.0f, -1.0f)); - points.push_back(core::vector3df(1.0f, 0.0f, 1.0f)); - points.push_back(core::vector3df(-1.0f, 0.0f, 1.0f)); - points.push_back(core::vector3df(-1.0f, 0.0f, -1.0f)); + points.push_back(core::vector3df(-size, 0.0f, -size)); + points.push_back(core::vector3df(size, 0.0f, -size)); + points.push_back(core::vector3df(size, 0.0f, size)); + points.push_back(core::vector3df(-size, 0.0f, size)); + points.push_back(core::vector3df(-size, 0.0f, -size)); zone = group->setZone(factory->createPolyLineZone(points)); } break; case Particle::Ring: - flow = 200.0f; - zone = group->setZone(factory->createRingZone(core::vector3df(0.0f, 0.0f, 0.0f), core::vector3df(0.0f, 1.0f, 0.0f), 0.5, 1.0f)); + zone = group->setZone(factory->createRingZone(core::vector3df(0.0f, 0.0f, 0.0f), core::vector3df(0.0f, 1.0f, 0.0f), size, size)); break; default: break; } + return zone; +} + +Particle::CEmitter* SampleParticles::updateParticleEmitter(Particle::CParticleComponent *particleComponent, Particle::EEmitter emitterType) +{ + Particle::CFactory *factory = particleComponent->getParticleFactory(); + + Particle::CGroup *group = NULL; + if (particleComponent->getNumOfGroup() == 0) + group = particleComponent->createParticleGroup(); + else + group = particleComponent->getGroup(0); + Particle::CEmitter *emitter = NULL; - float minForce = 1.0f; - float maxForce = 2.0f; + if (group->getEmitters().size() > 0) + emitter = group->getEmitters()[0]; + + // delete old emitter + if (emitter != NULL) + { + group->removeEmitter(emitter); + factory->deleteEmitter(emitter); + emitter = NULL; + } + + float minForce = 0.0f; + float maxForce = 0.2f; + float flow = 2000.0f; switch (emitterType) { case Particle::Random: - minForce = 0.2f; - maxForce = 0.5f; emitter = group->addEmitter(factory->createRandomEmitter()); break; case Particle::Straight: @@ -199,18 +211,36 @@ void SampleParticles::initParticle(Particle::CParticleComponent *particleCompone emitter = group->addEmitter(factory->createSphericEmitter(core::vector3df(0.0f, 1.0f, 0.0f), core::PI * 0.0f, core::PI * 0.5f)); break; case Particle::Normal: - minForce = 1.0f; - maxForce = 1.0f; emitter = group->addEmitter(factory->createNormalEmitter(false)); break; default: break; } - emitter->setTank(0); + emitter->setTank(-1); emitter->setFlow(flow); emitter->setForce(minForce, maxForce); - emitter->setEmitFullZone(false); + emitter->setEmitFullZone(true); + + return emitter; +} + +Particle::IRenderer* SampleParticles::updateParticleRenderer(Particle::CParticleComponent *particleComponent) +{ + Particle::CFactory *factory = particleComponent->getParticleFactory(); + + Particle::CGroup *group = NULL; + if (particleComponent->getNumOfGroup() == 0) + group = particleComponent->createParticleGroup(); + else + group = particleComponent->getGroup(0); + + // delete old emitter + if (group->getRenderer() != NULL) + { + factory->deleteRenderer(group->getRenderer()); + group->setRenderer(NULL); + } // create renderer Particle::CQuadRenderer *quadRenderer = factory->createQuadRenderer(); @@ -222,37 +252,50 @@ void SampleParticles::initParticle(Particle::CParticleComponent *particleCompone ITexture *texture = CTextureManager::getInstance()->getTexture("Particles/Textures/point.png"); quadRenderer->setAtlas(1, 1); + quadRenderer->getMaterial()->setUniformTexture("uTexture", texture); quadRenderer->getMaterial()->applyMaterial(); // create model - group->Gravity.set(0.0f, 0.2f, 0.0f); + group->Gravity.set(0.0f, -0.2f, 0.0f); group->createModel(Particle::RotateSpeedZ)->setStart(-2.0f, 2.0f); group->createModel(Particle::FrameIndex)->setStart(0.0f, 3.0f); - - group->createModel(Particle::ScaleX)->setStart(0.1f)->setEnd(0.15f, 0.2f); - group->createModel(Particle::ScaleY)->setStart(0.1f)->setEnd(0.15f, 0.2f); - group->createModel(Particle::ScaleZ)->setStart(0.15f)->setEnd(0.15f, 0.2f); - + group->createModel(Particle::Scale)->setStart(0.05f)->setEnd(0.1f, 0.13f); group->createModel(Particle::ColorA)->setStart(1.0f)->setEnd(0.0f); - float r = Particle::random(0.5f, 1.0f); - float g = Particle::random(0.5f, 1.0f); - float b = Particle::random(0.5f, 1.0f); + float r = Particle::random(0.5f, 0.7f); + float g = Particle::random(0.1f, 0.2f); + float b = Particle::random(0.7f, 1.0f); - group->createModel(Particle::ColorR)->setStart(r)->setEnd(1.0f); - group->createModel(Particle::ColorG)->setStart(g)->setEnd(1.0f); + group->createModel(Particle::ColorR)->setStart(r)->setEnd(0.0f); + group->createModel(Particle::ColorG)->setStart(g)->setEnd(0.0f); group->createModel(Particle::ColorB)->setStart(b)->setEnd(1.0f); - CTransformEuler *t = particleComponent->getGameObject()->getTransformEuler(); - t->setPosition(position); + return quadRenderer; } void SampleParticles::onUpdate() { // update application m_scene->update(); + + // write current particle + static float textUpdate = 0.0f; + + textUpdate = textUpdate - getTimeStep(); + if (textUpdate < 0.0f) + { + Particle::CParticleComponent *psComponent = m_currentParticleObj->getComponent(); + u32 totalParticle = psComponent->getGroup(0)->getNumParticles(); + char text[64]; + sprintf(text, "%ld", totalParticle); + m_label->setText(text); + textUpdate = 300.0f; + } + + // imgui update + CImguiManager::getInstance()->onNewFrame(); } void SampleParticles::onRender() @@ -262,6 +305,543 @@ void SampleParticles::onRender() // Render 2d gui camera CGraphics2D::getInstance()->render(m_camera); + + // ImGUI render + onGUI(); + + CImguiManager::getInstance()->onRender(); +} + +void SampleParticles::onGUI() +{ + bool open = true; + + ImGuiWindowFlags window_flags = 0; + + // We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming. + ImGui::SetNextWindowPos(ImVec2(935, 15), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(340, 760), ImGuiCond_FirstUseEver); + + if (!ImGui::Begin("Particle System", &open, window_flags)) + { + // Early out if the window is collapsed, as an optimization. + ImGui::End(); + return; + } + + // BEGIN WINDOW + { + onGUIBasic(); + } + + ImGui::End(); + + // DEMO HOW TO USE IMGUI (SEE THIS FUNCTION) + // ImGui::ShowDemoWindow(&open); +} + +void SampleParticles::onGUIBasic() +{ + const char *zoneName[] = { + "Point", + "Sphere", + "AABox", + "Cylinder", + "Line", + "PolyLine", + "Ring", + }; + + const char *emitterName[] = { + "Random", + "Straight", + "Spheric", + "Normal" + }; + + static int currentZone = 1; // default is sphere + static int currentEmitter = 0; + + bool changeZone = false; + bool changeEmitter = false; + + if (ImGui::CollapsingHeader("Zone & Emitter", ImGuiTreeNodeFlags_DefaultOpen)) + { + int lastZone = currentZone; + int lastEmitter = currentEmitter; + + ImGui::Indent(); + + ImGui::Combo("Zone", ¤tZone, zoneName, IM_ARRAYSIZE(zoneName)); + ImGui::SameLine(); + imguiHelpMarker("The shape that spawn particles"); + + if (lastZone != currentZone) + changeZone = true; + + ImGui::Combo("Emiter", ¤tEmitter, emitterName, IM_ARRAYSIZE(emitterName)); + ImGui::SameLine(); + imguiHelpMarker("The force type spawn particle"); + + if (lastEmitter != currentEmitter) + changeEmitter = true; + + if (changeZone == true) + updateParticleZone(m_currentParticleObj->getComponent(), (Particle::EZone)currentZone); + + if (changeEmitter == true) + updateParticleEmitter(m_currentParticleObj->getComponent(), (Particle::EEmitter)currentEmitter); + + ImGui::Unindent(); + } + + if (ImGui::CollapsingHeader("Particle Config", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::Indent(); + + onGUIZoneNode(currentZone); + + ImGui::Separator(); + + onGUIEmitterNode(currentEmitter); + + ImGui::Separator(); + + onGUIRendererNode(); + + ImGui::Unindent(); + } + + if (ImGui::CollapsingHeader("Particle Function", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::Indent(); + + ImGui::PushItemWidth(-1); + + float width = ImGui::GetWindowWidth(); + ImVec2 btnSize(width, 0.0f); + + if (ImGui::TreeNode("Stop Emitter")) + { + if (ImGui::Button("Stop Particle", btnSize)) + { + Particle::CEmitter *emitter = getParticleData(m_currentParticleObj)->Groups[0]->getEmitters()[0]; + emitter->setFlow(0); + } + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Tank Emitter")) + { + static float flow = 100; + static int tank = 100; + + ImGui::SliderFloat("Tank flow", &flow, 0.0f, 10000.0f, "flow = %.3f"); + ImGui::SliderInt("Tank", &tank, 0, 10000, "tank = %d"); + + if (ImGui::Button("Tank Particle", btnSize)) + { + Particle::CEmitter *emitter = getParticleData(m_currentParticleObj)->Groups[0]->getEmitters()[0]; + emitter->setFlow(flow); + emitter->setTank(tank); + } + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Play Emitter")) + { + if (ImGui::Button("Play Particle", btnSize)) + { + Particle::CEmitter *emitter = getParticleData(m_currentParticleObj)->Groups[0]->getEmitters()[0]; + emitter->setTank(-1); + emitter->setFlow(2000); + } + + ImGui::TreePop(); + } + + ImGui::PopItemWidth(); + + ImGui::Unindent(); + } +} + +void SampleParticles::onGUIZoneNode(int currentZone) +{ + float minSize = 0.1f; + float maxSize = 5.0f; + + if (currentZone == (int)Particle::Sphere) + { + if (ImGui::TreeNode("Sphere (Zone)")) + { + static float r = 0.0f; + + if (r == 0.0f) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CSphere *sphere = dynamic_cast(particleData->Groups[0]->getZone()); + r = sphere->getRadius(); + } + + float lastR = r; + ImGui::SliderFloat("Radius (Zone)", &r, minSize, maxSize, "radius = %.3f"); + + if (!core::equals(lastR, r)) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CSphere *sphere = dynamic_cast(particleData->Groups[0]->getZone()); + sphere->setRadius(r); + } + + ImGui::TreePop(); + } + } + else if (currentZone == (int)Particle::AABox) + { + if (ImGui::TreeNode("AABox (Zone)")) + { + static float sizeX = 0.0f; + static float sizeY = 0.0f; + static float sizeZ = 0.0f; + + if (sizeX == 0.0f) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CAABox *aabox = dynamic_cast(particleData->Groups[0]->getZone()); + const core::vector3df& s = aabox->getDimension(); + sizeX = s.X; + sizeY = s.Y; + sizeZ = s.Z; + } + + float lastX = sizeX; + float lastY = sizeY; + float lastZ = sizeZ; + + ImGui::SliderFloat("X", &sizeX, minSize, maxSize, "x = %.3f"); + ImGui::SliderFloat("Y", &sizeY, minSize, maxSize, "y = %.3f"); + ImGui::SliderFloat("Z", &sizeZ, minSize, maxSize, "z = %.3f"); + + if (!core::equals(lastX, sizeX) || !core::equals(lastY, sizeY) || !core::equals(lastZ, sizeZ)) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CAABox *aabox = dynamic_cast(particleData->Groups[0]->getZone()); + aabox->setDimension(core::vector3df(sizeX, sizeY, sizeZ)); + } + + ImGui::TreePop(); + } + } + else if (currentZone == (int)Particle::Cylinder) + { + if (ImGui::TreeNode("Cylinder (Zone)")) + { + static float r = 0.0f; + static float l = 0.0f; + static float rx = 0.0f; + static float ry = 0.0f; + static float rz = 0.0f; + + if (r == 0.0f) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CCylinder *cylinder = dynamic_cast(particleData->Groups[0]->getZone()); + l = cylinder->getLength(); + r = cylinder->getRadius(); + } + + float lastRadius = r; + float lastLength = l; + float lastX = rx; + float lastY = ry; + float lastZ = rz; + + ImGui::SliderFloat("Radius", &r, minSize, maxSize, "r = %.3f"); + ImGui::SliderFloat("Length", &l, minSize, maxSize, "l = %.3f"); + + core::vector3df direction = imguiDirection(CTransform::s_oy, rx, ry, rz, "Direction", "%.3f"); + + if (!core::equals(lastRadius, r) || + !core::equals(lastLength, l) || + !core::equals(lastX, rx) || + !core::equals(lastY, ry) || + !core::equals(lastZ, rz)) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CCylinder *cylinder = dynamic_cast(particleData->Groups[0]->getZone()); + cylinder->setRadius(r); + cylinder->setLength(l); + cylinder->setDirection(direction); + } + + ImGui::TreePop(); + } + } + else if (currentZone == (int)Particle::Ring) + { + if (ImGui::TreeNode("Ring (Zone)")) + { + static float r1 = 0.0f; + static float r2 = 0.0f; + + if (r1 == 0.0f) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CRing *ring = dynamic_cast(particleData->Groups[0]->getZone()); + r1 = ring->getMinRadius(); + r2 = ring->getMaxRadius(); + } + + float lastRadius1 = r1; + float lastRadius2 = r2; + + ImGui::DragFloatRange2("Radius", &r1, &r2, 0.01f, minSize, maxSize, "Min: %.3f", "Max: %.3f"); + + if (!core::equals(lastRadius1, r1) || !core::equals(lastRadius2, r2)) + { + Particle::CParticleBufferData *particleData = getParticleData(m_currentParticleObj); + Particle::CRing *ring = dynamic_cast(particleData->Groups[0]->getZone()); + + float min = r1; + float max = r2; + if (r1 > r2) + { + min = r2; + max = r1; + } + + ring->setRadius(min, max); + } + + ImGui::TreePop(); + } + } +} + +void SampleParticles::onGUIEmitterNode(int currentEmitter) +{ + float minForce = 0.0f; + float maxForce = 0.0f; + float flow = 0.0f; + bool fullZone = false; + + float lifeMin = 0.0f; + float lifeMax = 0.0f; + float friction = 0.0f; + + Particle::CParticleBufferData* particleData = getParticleData(m_currentParticleObj); + Particle::CGroup *group = particleData->Groups[0]; + Particle::CEmitter *emitter = group->getEmitters()[0]; + + minForce = emitter->getForceMin(); + maxForce = emitter->getForceMax(); + flow = emitter->getFlow(); + fullZone = emitter->isEmitFullZone(); + + lifeMin = group->LifeMin; + lifeMax = group->LifeMax; + friction = group->Friction; + + if (ImGui::TreeNode("Global variable (Group)")) + { + if (ImGui::TreeNode("Life")) + { + ImGui::DragFloatRange2("Life", &lifeMin, &lifeMax, 0.01f, 0.01f, 6.0f, "Min: %.3f", "Max: %.3f"); + group->LifeMin = lifeMin; + group->LifeMax = lifeMax; + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Friction")) + { + ImGui::SliderFloat("Friction", &friction, 0.0f, 30.0f, "friction = %.3f"); + group->Friction = friction; + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Gravity")) + { + core::vector3df gravity = group->Gravity; + gravity.normalize(); + + float gravityValue = group->Gravity.getLength(); + + ImGui::SliderFloat("Gravity", &gravityValue, 0.0f, 30.0f, "gravity = %.3f"); + + static float rx = 0.0f; + static float ry = 0.0f; + static float rz = 0.0f; + core::vector3df direction = imguiDirection(-CTransform::s_oy, rx, ry, rz, "Direction", "%.3f"); + + group->Gravity = direction * gravityValue; + + ImGui::TreePop(); + } + + ImGui::TreePop(); + } + + ImGui::Separator(); + + if (ImGui::TreeNode("Force & Born (Emitter)")) + { + ImGui::DragFloatRange2("Force", &minForce, &maxForce, 0.01f, 0.0f, 10.0f, "Min: %.3f", "Max: %.3f"); + emitter->setForce(minForce, maxForce); + + ImGui::SliderFloat("Flow", &flow, 0.0f, 10000.0f, "flow = %.3f"); + emitter->setFlow(flow); + + ImGui::Checkbox("Spawn particle at full zone", &fullZone); + emitter->setEmitFullZone(fullZone); + + ImGui::TreePop(); + } + + if (currentEmitter == (int)Particle::Straight) + { + if (ImGui::TreeNode("Straight (Emitter)")) + { + Particle::CStraightEmitter *e = (Particle::CStraightEmitter*)emitter; + + static float rx = 0.0f; + static float ry = 0.0f; + static float rz = 0.0f; + core::vector3df direction = imguiDirection(CTransform::s_oy, rx, ry, rz, "Direction", "%.3f"); + e->setDirection(direction); + + ImGui::TreePop(); + } + } + + if (currentEmitter == (int)Particle::Spheric) + { + if (ImGui::TreeNode("Spheric (Emitter)")) + { + Particle::CSphericEmitter *e = (Particle::CSphericEmitter*)emitter; + + static float rx = 0.0f; + static float ry = 0.0f; + static float rz = 0.0f; + core::vector3df direction = imguiDirection(CTransform::s_oy, rx, ry, rz, "Direction", "%.3f"); + e->setDirection(direction); + + float angleMin = core::radToDeg(e->getAngleA()); + float angleMax = core::radToDeg(e->getAngleB()); + ImGui::DragFloatRange2("Angle", &angleMin, &angleMax, 0.1f, 0.0f, 360.0f, "Min: %.3f", "Max: %.3f"); + e->setAngles(core::degToRad(angleMin), core::degToRad(angleMax)); + + ImGui::TreePop(); + } + } +} + +void SampleParticles::onGUIRendererNode() +{ + Particle::CParticleBufferData* particleData = getParticleData(m_currentParticleObj); + Particle::CGroup *group = particleData->Groups[0]; + + if (ImGui::TreeNode("Renderer")) + { + if (ImGui::TreeNode("Scale")) + { + Particle::CModel *scale = group->getModel(Particle::Scale); + + float startS1 = scale->getStartValue1(); + float startS2 = scale->getStartValue2(); + + float endS1 = scale->getEndValue1(); + float endS2 = scale->getEndValue2(); + + ImGui::DragFloatRange2("Start Size", &startS1, &startS2, 0.01f, 0.0f, 1.0f, "Start 1: %.3f", "Start 2: %.3f"); + ImGui::DragFloatRange2("End Size", &endS1, &endS2, 0.01f, 0.0f, 1.0f, "End 1: %.3f", "End 2: %.3f"); + + scale->setStart(startS1, startS2)->setEnd(endS1, endS2); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Color")) + { + Particle::CModel *modelR = group->getModel(Particle::ColorR); + Particle::CModel *modelG = group->getModel(Particle::ColorG); + Particle::CModel *modelB = group->getModel(Particle::ColorB); + Particle::CModel *modelA = group->getModel(Particle::ColorA); + + float startColor1[4]; + float startColor2[4]; + float endColor1[4]; + float endColor2[4]; + + startColor1[0] = modelR->getStartValue1(); + startColor1[1] = modelG->getStartValue1(); + startColor1[2] = modelB->getStartValue1(); + startColor1[3] = modelA->getStartValue1(); + + startColor2[0] = modelR->getStartValue2(); + startColor2[1] = modelG->getStartValue2(); + startColor2[2] = modelB->getStartValue2(); + startColor2[3] = modelA->getStartValue2(); + + endColor1[0] = modelR->getEndValue1(); + endColor1[1] = modelG->getEndValue1(); + endColor1[2] = modelB->getEndValue1(); + endColor1[3] = modelA->getEndValue1(); + + endColor2[0] = modelR->getEndValue2(); + endColor2[1] = modelG->getEndValue2(); + endColor2[2] = modelB->getEndValue2(); + endColor2[3] = modelA->getEndValue2(); + + ImGui::ColorEdit4("Start 1", startColor1); + ImGui::ColorEdit4("Start 2", startColor2); + + ImGui::ColorEdit4("End 1", endColor1); + ImGui::ColorEdit4("End 2", endColor2); + + modelR->setStart(startColor1[0], startColor2[0])->setEnd(endColor1[0], endColor2[0]); + modelG->setStart(startColor1[1], startColor2[1])->setEnd(endColor1[1], endColor2[1]); + modelB->setStart(startColor1[2], startColor2[2])->setEnd(endColor1[2], endColor2[2]); + modelA->setStart(startColor1[3], startColor2[3])->setEnd(endColor1[3], endColor2[3]); + + ImGui::TreePop(); + } + + ImGui::TreePop(); + } +} + +core::vector3df SampleParticles::imguiDirection(core::vector3df baseDirection, float &x, float &y, float &z, const char *name, const char *fmt) +{ + float v[] = { x, y, z }; + ImGui::DragFloat3(name, v, 0.1f, -180.0f, 180.0f, fmt); + + x = v[0]; + y = v[1]; + z = v[2]; + + core::matrix4 m; + m.setRotationDegrees(core::vector3df(x, y, z)); + + core::vector3df direction = baseDirection; + m.rotateVect(direction); + direction.normalize(); + return direction; +} + +void SampleParticles::imguiHelpMarker(const char* desc) +{ + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } } void SampleParticles::onPostRender() diff --git a/Samples/Particles/Source/SampleParticles.h b/Samples/Particles/Source/SampleParticles.h index 184ffdbb3..7559373ec 100644 --- a/Samples/Particles/Source/SampleParticles.h +++ b/Samples/Particles/Source/SampleParticles.h @@ -14,8 +14,15 @@ class SampleParticles : public IApplicationEventReceiver CForwardRP *m_forwardRP; CGlyphFont *m_font; + + CGameObject* m_currentParticleObj; + + CGUIText *m_label; + public: + SampleParticles(); + virtual ~SampleParticles(); virtual void onUpdate(); @@ -38,6 +45,25 @@ class SampleParticles : public IApplicationEventReceiver void createCanvasText(const char *text, const core::vector3df& position); - void initParticle(Particle::CParticleComponent *particleComponent, Particle::EZone zoneType, Particle::EEmitter emitterType, const core::vector3df& position); + void onGUI(); + + void onGUIBasic(); + + void onGUIZoneNode(int currentZone); + + void onGUIEmitterNode(int currentEmitter); + + void onGUIRendererNode(); + + void imguiHelpMarker(const char* desc); + + core::vector3df imguiDirection(core::vector3df baseDirection, float &x, float &y, float &z, const char *name, const char *fmt); + + Particle::CZone* updateParticleZone(Particle::CParticleComponent *particleComponent, Particle::EZone zoneType); + + Particle::CEmitter* updateParticleEmitter(Particle::CParticleComponent *particleComponent, Particle::EEmitter emitterType); + + Particle::IRenderer* updateParticleRenderer(Particle::CParticleComponent *particleComponent); + Particle::CParticleBufferData* getParticleData(CGameObject *obj); }; \ No newline at end of file