212 lines
6.4 KiB
C++
212 lines
6.4 KiB
C++
#include "particle_batch.hpp"
|
|
|
|
#include <iostream>
|
|
|
|
// TODO: use VAO's as soon as this is working
|
|
|
|
int getDivisorForIndex(size_t index)
|
|
{
|
|
// 0 or 1?
|
|
return (index == 0) ? 0 : 3;
|
|
}
|
|
|
|
namespace endofthejedi {
|
|
ParticleBatch::ParticleBatch(size_t id, size_t numParticles, float particleSize, float halfAge)
|
|
: m_id(id)
|
|
, m_numParticles(numParticles)
|
|
, m_particleRadius(particleSize)
|
|
, m_halfAge(halfAge)
|
|
, m_age(0.0)
|
|
, m_maxVelocity(1.0)
|
|
, m_center(glm::vec3(0.0f, 0.0f, 0.0f))
|
|
|
|
// 2d quad drawn as a triangle fan.
|
|
//
|
|
// TODO
|
|
// it is transformed before uploading so it looks at the camera
|
|
, m_data_geometry({
|
|
0.5f, -0.5f,
|
|
0.5f, 0.5f,
|
|
-0.5f, 0.5f,
|
|
-0.5f, -0.5f})
|
|
{
|
|
m_num_vertex_buffers = 4;
|
|
|
|
//std::cout<<"[ParticleBatch] create for " << numParticles << " num particles" << std::endl;
|
|
m_attr_locations.resize(m_num_vertex_buffers);
|
|
m_data_vbos.resize(m_num_vertex_buffers);
|
|
|
|
m_data_position.resize(m_numParticles);
|
|
m_data_velocity.resize(m_numParticles);
|
|
m_data_max_distance.resize(m_numParticles);
|
|
|
|
for (size_t i=0; i<m_num_vertex_buffers; i++) {
|
|
m_attr_locations[i] = -1;
|
|
m_data_vbos[i] = 0;
|
|
}
|
|
}
|
|
|
|
ParticleBatch::~ParticleBatch()
|
|
{
|
|
// TODO: find out if stuff must be deallocated
|
|
glDeleteBuffers(m_num_vertex_buffers, m_data_vbos.data());
|
|
}
|
|
|
|
void ParticleBatch::setup(Shader *shader)
|
|
{
|
|
static const char *names[] = {
|
|
"in_geometry",
|
|
"in_position",
|
|
"in_velocity",
|
|
"in_maxDist"
|
|
};
|
|
|
|
for (size_t i=0; i<m_num_vertex_buffers; i++) {
|
|
const char *name = names[i];
|
|
GLint loc = glGetAttribLocation(shader->program(), name);
|
|
m_attr_locations[i] = loc;
|
|
//std::cout<<"attr location for " << name << " (#" << i << ") is " << loc << std::endl;
|
|
}
|
|
}
|
|
|
|
void ParticleBatch::setCenter(const glm::vec3 ¢er)
|
|
{
|
|
m_center = center;
|
|
}
|
|
|
|
void ParticleBatch::setMaxVelocity(float maxVelocity)
|
|
{
|
|
m_maxVelocity = maxVelocity;
|
|
}
|
|
|
|
void ParticleBatch::setParticle(size_t index, const glm::vec3 &p, const glm::vec3 &v, float maxDist)
|
|
{
|
|
if (index >= m_numParticles) {
|
|
return;
|
|
}
|
|
|
|
//std::cout<<"[ParticleBatch] setParticle " << index << std::endl;
|
|
|
|
m_data_position[index] = p;
|
|
m_data_velocity[index] = v;
|
|
m_data_max_distance[index] = maxDist;
|
|
}
|
|
|
|
void ParticleBatch::bind()
|
|
{
|
|
//std::cout<<"[ParticleBatch] bind" << std::endl;
|
|
|
|
for (size_t i=0; i<m_num_vertex_buffers; i++) {
|
|
//std::cout<<"vbo #" << i << ": " << m_data_vbos[i] << std::endl;
|
|
glEnableVertexAttribArray(m_attr_locations[i]);
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_data_vbos[i]);
|
|
|
|
glVertexAttribDivisor(m_attr_locations[i], getDivisorForIndex(i));
|
|
|
|
// TODO: i or index? at first argument?
|
|
glVertexAttribPointer(
|
|
m_attr_locations[i],
|
|
dataSizeForIndex(i),
|
|
GL_FLOAT, // Data is floating point type
|
|
GL_FALSE, // No fixed point scaling
|
|
0, // stride: no
|
|
NULL); // No offset
|
|
}
|
|
}
|
|
|
|
void ParticleBatch::upload()
|
|
{
|
|
//std::cout<<"[ParticleBatch] upload to vbo's " << std::endl;
|
|
|
|
glGenBuffers(m_num_vertex_buffers, m_data_vbos.data()); // Generate buffer
|
|
|
|
for (size_t i=0; i<m_num_vertex_buffers; i++) {
|
|
size_t bufferDataSize = dataSizeForIndex(i) * m_numParticles * sizeof(float);
|
|
|
|
glEnableVertexAttribArray(i);
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_data_vbos[i]);
|
|
|
|
// fill buffer with the loaded mesh position data
|
|
glBufferData(
|
|
GL_ARRAY_BUFFER, // Buffer target
|
|
bufferDataSize, // Buffer data size
|
|
dataSourceForIndex(i), // Buffer data pointer
|
|
GL_STATIC_DRAW); // Usage - Data never changes;
|
|
}
|
|
}
|
|
|
|
void ParticleBatch::render(Shader *shader)
|
|
{
|
|
//std::cout<<"[ParticleBatch] render " << std::endl;
|
|
glUniform1f(shader->location("age"), m_age);
|
|
glUniform1f(shader->location("maxVelocity"), m_maxVelocity);
|
|
glUniform1f(shader->location("halfAge"), m_halfAge);
|
|
glUniform1f(shader->location("size"), m_particleRadius);
|
|
glUniform3f(shader->location("explCenter"), m_center.x, m_center.y, m_center.z);
|
|
|
|
bind();
|
|
|
|
//glDrawArrays(GL_TRIANGLE_FAN, 0, 4*m_numParticles);
|
|
|
|
//glDrawArraysInstanced( GLenum mode, GLint first, GLsizei count, GLsizei primcount);
|
|
// XXX the 3 is a magical number.
|
|
// I dont know why this works
|
|
// without it, it will render 1/3 of the particles
|
|
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, 3*m_numParticles);
|
|
|
|
#if 0
|
|
glBegin(GL_QUADS);
|
|
for (size_t index=0; index<m_numParticles; index++) {
|
|
for (size_t i=0; i<4; i++) {
|
|
glm::vec2 p = m_data_position[index] + triangles[i];
|
|
//glColor3f(1.0, 0.0, 0.0);
|
|
glVertex2f(p.x, p.y);
|
|
}
|
|
}
|
|
glEnd();
|
|
#endif
|
|
|
|
}
|
|
|
|
size_t ParticleBatch::dataSizeForIndex(size_t index)
|
|
{
|
|
switch(index) {
|
|
case 0:
|
|
return 2;
|
|
|
|
case 1:
|
|
case 2:
|
|
return 3;
|
|
|
|
case 3:
|
|
return 1;
|
|
|
|
default:
|
|
std::cerr << "dataSizeForIndex() bad" << std::endl;
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void *ParticleBatch::dataSourceForIndex(size_t index)
|
|
{
|
|
switch(index) {
|
|
case 0: return (void *) m_data_geometry.data();
|
|
case 1: return (void *) m_data_position.data();
|
|
case 2: return (void *) m_data_velocity.data();
|
|
case 3: return (void *) m_data_max_distance.data();
|
|
default:
|
|
std::cerr << "dataSourceForIndex() bad" << std::endl;
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
void ParticleBatch::tick(float dt)
|
|
{
|
|
m_age += dt;
|
|
}
|
|
|
|
bool ParticleBatch::done() const
|
|
{
|
|
return m_age >= 5.0*m_halfAge;
|
|
}
|
|
}
|