/* QGLView Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "glparticles_system.h" GLParticlesSystem::GLParticlesSystem(const QVector3D & pos): GLObjectBase() { pass_ = GLObjectBase::Transparent; freq = 40.f; birthRate_ = 10.f; lifeDuration_ = 2.f; fade_time = 0.5f; size_ = 1.f; additionalSpeed = 0.f; initialSpeed_ = 1.f; need_birth = -1.f; tex_rect.setRect(0., 0., 1., 1.); tex_scale = QSizeF(); emitterPosition_ = pos; emitterDirection_.setZ(1.); speedDirection_.setZ(1.); speedDecay_ = initialAngle_ = enlargeSpeed_ = enlargeSpeedJitter_ = baseAngle_ = 0.f; lifeDurationJitter_ = speedJitter_ = speedDirectionJitter_ = sizeJitter_ = angleJitter_ = 0.f; active_ = birthEnabled_ = true; is_diffuse_anim = add_vert_face = false; emitterType_ = Cone; tick_life = 1.f / freq; tick_birth = birthRate_ / freq; } void GLParticlesSystem::update() { // qDebug() << "update" << need_birth << tick_birth; if (!active_) return; // QMutexLocker locker(&mutex); Particle cp(lifeDuration_); if (birthEnabled_) need_birth += tick_birth; qDebug() << "update" << particles.size(); if (need_birth >= 1.f) { cp.pos = emitterPosition_; // qDebug() << "speed" << cp.speed; cp.speedDecay = 1.f + speedDecay_; for (int i = 0; i < floor(need_birth); ++i) { cp.lifeDuration = lifeDuration_ + urand(lifeDurationJitter_); switch (emitterType_) { case Omni: cp.speed = QVector3D(urand(), urand(), urand()).normalized() * initialSpeed_ * (1.f + urand(speedJitter_)); break; case Cone: case Box: cp.speed = emitterDirection_ * initialSpeed_ * (1.f + urand(speedJitter_)); cp.speed += orthToVector(cp.speed, speedDirectionJitter_); break; } if (emitterType_ == Box) cp.pos = emitterRect_.randomPoint(); // qDebug() << "before" << cp.speed.length(); lengthenVector(cp.speed, additionalSpeed); // qDebug() << "after" << cp.speed.length(); cp.size = size_ + urand(sizeJitter_); cp.angle = initialAngle_ + urand(angleJitter_); cp.enlargeSpeed = (enlargeSpeed_ + urand(enlargeSpeedJitter_)) * tick_life; /*if (is_diffuse_anim) { if (material_.diffuse.animation_frame_rate < 0 && animation->bitmaps.size() > 0) cp.animationFrameRate = animation->bitmaps.size() / cp.lifeDuration; else cp.animationFrameRate = material_.diffuse.animation_frame_rate; }*/ if (tex_scale.isEmpty()) cp.tex_rect.setSize(tex_rect.size()); else cp.tex_rect.setSize(tex_rect.size() * tex_scale); cp.tex_rect.moveTopLeft(tex_rect.topLeft() + QPointF(uprand(tex_rect.width() - cp.tex_rect.width()), uprand(tex_rect.height() - cp.tex_rect.height()))); // cp.tex_rect = tex_rect; particles.push_back(cp); } need_birth -= floor(need_birth); } for (int i = 0; i < particles.size(); ++i) { Particle & c(particles[i]); foreach(const QVector3D & f, forces) c.speed += f; c.lifeCurrent += tick_life; // qDebug() << "life" << c.lifeCurrent << c.lifeDuration; if (c.lifeCurrent > c.lifeDuration) { // qDebug() << "remove" << i; particles.remove(i); i--; continue; } c.pos += c.speed * tick_life; c.speed /= c.speedDecay; c.size += c.enlargeSpeed; // if (c.lifeCurrent > 1.) c.angle += urand(5.); } } void GLParticlesSystem::draw(QOpenGLShaderProgram * prog, bool) { if (particles.isEmpty()) return; if (view_ == nullptr) return; pass_ = GLObjectBase::Transparent; Camera * camera(view_->camera()); QVector3D apos = camera->pos(), dir = camera->direction(); // qDebug() << dir; // qDebug() << camera.angles(); // qDebug() << camera.angle_xy; GLfloat cxyc, czs, czc; GLfloat dx, dy, cdx, cdy, cdz, a, tr_r = material_.color_diffuse.redF(), tr_g = material_.color_diffuse.greenF(), tr_b = material_.color_diffuse.blueF(), tr_a = material_.color_diffuse.alphaF() * (1.f - material_.transparency); // cxys = sin(camera.angle_xy * deg2rad); cxyc = cosf(camera->angles_.y() * deg2rad); czs = sinf(camera->angles_.z() * deg2rad); czc = cosf(camera->angles_.z() * deg2rad); dx = -czc; dy = czs; vertices.clear(); texcoords.clear(); colors.clear(); for (int i = 0; i < particles.size(); ++i) // particles[i].pos_h.setZ((particles[i].pos - apos).lengthSquared()); particles[i].pos_h.setZ(particles[i].pos.distanceToPlane(apos, dir)); std::sort(particles.begin(), particles.end()); glBegin(GL_POINTS); foreach(const Particle & i, particles) { // glVertex3f(i.pos.x(), i.pos.y(), i.pos.z()); a = (i.lifeDuration - i.lifeCurrent) / fade_time; if (a > 1.f) a = 1.f; a *= tr_a; cdx = dx * i.size; cdy = dy * i.size; cdz = i.size; vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z() - cdz; vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z() + cdz; vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z() + cdz; vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z() - cdz; cdx = i.size; cdy = i.size; texcoords << i.tex_rect.right() << i.tex_rect.top(); texcoords << i.tex_rect.right() << i.tex_rect.bottom(); texcoords << i.tex_rect.left() << i.tex_rect.bottom(); texcoords << i.tex_rect.left() << i.tex_rect.top(); colors << tr_r << tr_g << tr_b << a; colors << tr_r << tr_g << tr_b << a; colors << tr_r << tr_g << tr_b << a; colors << tr_r << tr_g << tr_b << a; if (add_vert_face) { if (cxyc > 0.f) { vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z(); vertices << i.pos.x() + cdx << i.pos.y() - cdy << i.pos.z(); vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z(); vertices << i.pos.x() - cdx << i.pos.y() + cdy << i.pos.z(); } else { vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z(); vertices << i.pos.x() - cdx << i.pos.y() + cdy << i.pos.z(); vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z(); vertices << i.pos.x() + cdx << i.pos.y() - cdy << i.pos.z(); } texcoords << i.tex_rect.right() << i.tex_rect.top(); texcoords << i.tex_rect.right() << i.tex_rect.bottom(); texcoords << i.tex_rect.left() << i.tex_rect.bottom(); texcoords << i.tex_rect.left() << i.tex_rect.top(); colors << tr_r << tr_g << tr_b << a; colors << tr_r << tr_g << tr_b << a; colors << tr_r << tr_g << tr_b << a; colors << tr_r << tr_g << tr_b << a; } } glEnd(); // bool cae = glIsEnabled(GL_COLOR_ARRAY), nae = glIsEnabled(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, 0); // glNormal3f(vn.x(), vn.y(), vn.z()); glNormal3f(0., 0., 1.); glDepthMask(false); glEnable(GL_COLOR_ARRAY); glDisable(GL_NORMAL_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertices.constData()); glTexCoordPointer(2, GL_FLOAT, 0, texcoords.constData()); glColorPointer(4, GL_FLOAT, 0, colors.constData()); // glEnable(GL_ALPHA_TEST); // glAlphaFunc(); glDrawArrays(GL_QUADS, 0, vertices.size() / 3); glDepthMask(true); // glDisable(GL_ALPHA_TEST); // if (!cae) glDisable(GL_COLOR_ARRAY); // if (nae) glEnable(GL_NORMAL_ARRAY); } GLParticlesSystem::Particle::Particle(float life_dur) { size = 1.; angle = lifeCurrent = 0.; speedDecay = 0.; lifeDuration = life_dur; tex_rect = QRectF(0, 0, 1, 1); }