231 lines
7.4 KiB
C++
231 lines
7.4 KiB
C++
#include "ParticleSimGLCanvas.h"
|
|
#include "ParticleSystem.h"
|
|
|
|
#include <nanogui/opengl.h>
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include <iostream>
|
|
|
|
using namespace nanogui;
|
|
|
|
namespace
|
|
{
|
|
static const float r = 6.0f;
|
|
|
|
static inline gti320::Particle* pickParticle(std::vector<gti320::Particle>& particles, const gti320::Vector2f& mousePos)
|
|
{
|
|
for (gti320::Particle& particle : particles)
|
|
{
|
|
const float dist = (particle.x - mousePos).norm();
|
|
if (dist <= r)
|
|
{
|
|
return &particle;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static const float fixedColor[3] = { 1.0f, 1.0f, 0.68f };
|
|
static const float defaultColor[3] = { 1.0f, 0.0f, 0.0f };
|
|
static const int maxColors = 8;
|
|
static const int colorMap[maxColors * 3] = {
|
|
228,26,28,
|
|
55,126,184,
|
|
77,175,74,
|
|
152,78,163,
|
|
255,127,0,
|
|
255,255,51,
|
|
166,86,40,
|
|
247,129,191
|
|
};
|
|
}
|
|
|
|
ParticleSimGLCanvas::ParticleSimGLCanvas(ParticleSimApplication* _app) : nanogui::Canvas(_app->getWindow()), m_app(_app), m_selectedParticle(nullptr)
|
|
{
|
|
|
|
// Un shader minimaliste pour afficher les particules
|
|
m_particleShader = new Shader(render_pass(),
|
|
// Nom du shader
|
|
"particle_shader",
|
|
// Nuanceur de particules
|
|
R"(#version 410
|
|
uniform mat4 modelViewProj;
|
|
uniform vec4 color;
|
|
in vec2 position;
|
|
void main() {
|
|
gl_Position = modelViewProj * vec4(position.xy, -1, 1);
|
|
})",
|
|
|
|
// Fragment shader
|
|
R"(#version 410
|
|
uniform vec4 color;
|
|
out vec4 frag_color;
|
|
void main() {
|
|
frag_color = color;
|
|
})"
|
|
);
|
|
|
|
|
|
// Initialise la géométrie pour un cercle
|
|
static const int numTris = 8;
|
|
m_circle.resize(4 * (numTris+1));
|
|
m_circle.setZero();
|
|
for (int i = 0; i <= numTris; ++i)
|
|
{
|
|
const float angle = 2.0f * M_PI * ((float)i / numTris);
|
|
const float x = r * sin(angle);
|
|
const float y = r * cos(angle);
|
|
m_circle(4 * i) = x;
|
|
m_circle(4 * i + 1) = y;
|
|
m_circle(4 * i + 2) = 0;
|
|
m_circle(4 * i + 3) = 0;
|
|
}
|
|
|
|
const Matrix4f mvp = Matrix4f::scale(Vector4f(1.0f, 1.0f, 1.0f, 1.0f));
|
|
m_particleShader->set_uniform("modelViewProj", mvp);
|
|
m_particleShader->set_uniform("color", Vector4f(0.0f, 0.0f, 1.0f, 10.0f));
|
|
m_particleShader->set_buffer("position", VariableType::Float32, { (uint32_t)m_circle.rows(), (int)2 }, m_circle.storage().data());
|
|
}
|
|
|
|
ParticleSimGLCanvas::~ParticleSimGLCanvas() {
|
|
}
|
|
|
|
void ParticleSimGLCanvas::draw_contents()
|
|
{
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
m_particleShader->set_uniform("color", Vector4f(1.0f, 0.0f, 0.0f, 1.0f));
|
|
|
|
// Matrice de projection orthographique
|
|
const Matrix4f projMat = Matrix4f::ortho(0, width() - 1, 0, height() - 1, 0.1f, 1.0f);
|
|
const gti320::ParticleSystem& particleSystem = m_app->getParticleSystem();
|
|
const auto particles = particleSystem.getParticles();
|
|
const int numParticles = particles.size();
|
|
|
|
// Affichage des ressorts
|
|
const auto springs = particleSystem.getSprings();
|
|
const int numSprings = springs.size();
|
|
std::vector<float> points(4 * numSprings);
|
|
for (int i = 0; i < numSprings; ++i)
|
|
{
|
|
const int index0 = springs[i].index0;
|
|
const int index1 = springs[i].index1;
|
|
const int k = 4 * i;
|
|
memcpy(&points[k], particles[index0].x.data(), sizeof(float) * 2);
|
|
memcpy(&points[k+2], particles[index1].x.data(), sizeof(float) * 2);
|
|
}
|
|
m_particleShader->set_uniform("modelViewProj", projMat);
|
|
m_particleShader->set_uniform("color", Vector4f(0.0f, 0.0f, 1.0f, 1.0f));
|
|
m_particleShader->set_buffer("position", VariableType::Float32, { (uint32_t)points.size() / 2, 2 }, points.data());
|
|
|
|
m_particleShader->begin();
|
|
m_particleShader->draw_array(Shader::PrimitiveType::Line, 0, points.size() / 2);
|
|
m_particleShader->end();
|
|
|
|
// Affichage des particules
|
|
m_particleShader->set_buffer("position", VariableType::Float32, { (uint32_t)m_circle.rows() / 2, 2 }, m_circle.data());
|
|
for (int i = 0; i < numParticles; ++i)
|
|
{
|
|
Matrix4f modelMat = Matrix4f::scale(Vector4f(1.0f, 1.0f, 1.0f, 1.0f));
|
|
modelMat.m[3][0] = particles[i].x(0);
|
|
modelMat.m[3][1] = particles[i].x(1);
|
|
const Matrix4f mvp = projMat * modelMat;
|
|
m_particleShader->set_uniform("modelViewProj", mvp);
|
|
|
|
if (particles[i].color > -1)
|
|
{
|
|
const int c = std::min(particles[i].color, maxColors - 1);
|
|
const int i = 3 * c;
|
|
m_particleShader->set_uniform("color", Vector4f((float)colorMap[i]/255.0f, (float)colorMap[i+1] / 255.0f, (float)colorMap[i+2] / 255.0f, 1.0f));
|
|
}
|
|
else
|
|
{
|
|
m_particleShader->set_uniform("color", Vector4f(defaultColor[0], defaultColor[1], defaultColor[2], 1.0f));
|
|
}
|
|
|
|
m_particleShader->begin();
|
|
m_particleShader->draw_array(Shader::PrimitiveType::TriangleStrip, 0, m_circle.rows() / 2);
|
|
m_particleShader->end();
|
|
}
|
|
|
|
// Affichage du ressort déféni par la souris
|
|
if (m_selectedParticle)
|
|
{
|
|
const gti320::Vector2f p = m_selectedParticle->x;
|
|
const float coords[4] = { m_mousePos(0), m_mousePos(1), p(0), p(1) };
|
|
m_particleShader->set_uniform("modelViewProj", projMat);
|
|
m_particleShader->set_uniform("color", Vector4f(0.0f, 1.0f, 0.0f, 1.0f));
|
|
m_particleShader->set_buffer("position", VariableType::Float32, { 2, 2 }, coords);
|
|
|
|
m_particleShader->begin();
|
|
m_particleShader->draw_array(Shader::PrimitiveType::Line, 0, 2);
|
|
m_particleShader->end();
|
|
}
|
|
|
|
}
|
|
|
|
bool ParticleSimGLCanvas::mouse_button_event(const Vector2i& p, int button, bool down, int modifiers)
|
|
{
|
|
if (modifiers == GLFW_MOD_SHIFT)
|
|
{
|
|
if (button == GLFW_MOUSE_BUTTON_1 && down)
|
|
{
|
|
convertAndStoreMousePos(p);
|
|
m_selectedParticle = pickParticle(m_app->getParticleSystem().getParticles(), m_mousePos);
|
|
if (m_selectedParticle != nullptr)
|
|
{
|
|
m_selectedParticle->fixed = !(m_selectedParticle->fixed);
|
|
m_selectedParticle = nullptr;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (button == GLFW_MOUSE_BUTTON_1 && down)
|
|
{
|
|
convertAndStoreMousePos(p);
|
|
m_selectedParticle = pickParticle(m_app->getParticleSystem().getParticles(), m_mousePos);
|
|
return true;
|
|
}
|
|
else if (button == 0)
|
|
{
|
|
m_selectedParticle = nullptr;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ParticleSimGLCanvas::mouse_drag_event(const Vector2i& p, const Vector2i& rel, int button, int modifiers)
|
|
{
|
|
if (button == GLFW_MOUSE_BUTTON_2 && modifiers == 0 && m_selectedParticle != nullptr)
|
|
{
|
|
convertAndStoreMousePos(p);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ParticleSimGLCanvas::convertAndStoreMousePos(const Vector2i& mousePos)
|
|
{
|
|
const Vector2i& pos = position();
|
|
const int y = height() - (mousePos.y() - pos.y()) - 1;
|
|
m_mousePos(0) = (float)(mousePos.x() - pos.x());
|
|
m_mousePos(1) = (float)y;
|
|
}
|
|
|
|
void ParticleSimGLCanvas::applyMouseSpring()
|
|
{
|
|
if (m_selectedParticle != nullptr)
|
|
{
|
|
const float k = 20.0f * m_selectedParticle->m;
|
|
const gti320::Vector2f f = k * (m_mousePos - m_selectedParticle->x);
|
|
m_selectedParticle->f = m_selectedParticle->f + f;
|
|
}
|
|
}
|