/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" #include "opengl33particles.h" #include "particles.h" #include "openglcamera.h" #include "simulation.h" #include "simulationenvironment.h" std::vector> const billboard_vertices { { { -0.5f, -0.5f, 0.f }, { 0.f, 0.f } }, { { 0.5f, -0.5f, 0.f }, { 1.f, 0.f } }, { { 0.5f, 0.5f, 0.f }, { 1.f, 1.f } }, { { -0.5f, -0.5f, 0.f }, { 0.f, 0.f } }, { { 0.5f, 0.5f, 0.f }, { 1.f, 1.f } }, { { -0.5f, 0.5f, 0.f }, { 0.f, 1.f } }, }; void opengl33_particles::update( opengl_camera const &Camera ) { if (!Global.Smoke) return; m_particlevertices.clear(); // build a list of visible smoke sources // NOTE: arranged by distance to camera, if we ever need sorting and/or total amount cap-based culling std::multimap sources; for( auto const &source : simulation::Particles.sequence() ) { if( false == Camera.visible( source.area() ) ) { continue; } // NOTE: the distance is negative when the camera is inside the source's bounding area sources.emplace( static_cast( glm::length( Camera.position() - source.area().center ) - source.area().radius ), source ); } if( true == sources.empty() ) { return; } // build billboard data for particles from visible sources auto const camerarotation { glm::mat3( Camera.modelview() ) }; particle_vertex vertex; for( auto const &source : sources ) { auto const particlecolor { glm::clamp( source.second.color() * ( glm::vec3 { Global.DayLight.ambient } + 0.35f * glm::vec3{ Global.DayLight.diffuse } ) * simulation::Environment.light_intensity(), glm::vec3{ 0.f }, glm::vec3{ 1.f } ) }; auto const &particles { source.second.sequence() }; // TODO: put sanity cap on the overall amount of particles that can be drawn auto const sizestep { 256.0 * billboard_vertices.size() }; m_particlevertices.reserve( sizestep * std::ceil( m_particlevertices.size() + ( particles.size() * billboard_vertices.size() ) / sizestep ) ); for( auto const &particle : particles ) { // TODO: particle color support vertex.color[ 0 ] = particlecolor.r; vertex.color[ 1 ] = particlecolor.g; vertex.color[ 2 ] = particlecolor.b; vertex.color.a = clamp(particle.opacity, 0.0f, 1.0f); auto const offset { glm::vec3{ particle.position - Camera.position() } }; auto const rotation { glm::angleAxis( particle.rotation, glm::vec3{ 0.f, 0.f, 1.f } ) }; for( auto const &billboardvertex : billboard_vertices ) { vertex.position = offset + ( rotation * billboardvertex.first * particle.size ) * camerarotation; vertex.texture = billboardvertex.second; m_particlevertices.emplace_back( vertex ); } } } // ship the billboard data to the gpu: // make sure we have enough room... if( m_buffercapacity < m_particlevertices.size() ) { m_buffercapacity = m_particlevertices.size(); if (!m_buffer) m_buffer.emplace(); m_buffer->allocate(gl::buffer::ARRAY_BUFFER, m_buffercapacity * sizeof(particle_vertex), GL_STREAM_DRAW); } if (m_buffer) { // ...send the data... m_buffer->upload(gl::buffer::ARRAY_BUFFER, m_particlevertices.data(), 0, m_particlevertices.size() * sizeof(particle_vertex)); } } void opengl33_particles::render() { if (!Global.Smoke) return; if( m_buffercapacity == 0 ) { return; } if( m_particlevertices.empty() ) { return; } if (!m_vao) { m_vao.emplace(); m_vao->setup_attrib(*m_buffer, 0, 3, GL_FLOAT, sizeof(particle_vertex), 0); m_vao->setup_attrib(*m_buffer, 1, 4, GL_FLOAT, sizeof(particle_vertex), 12); m_vao->setup_attrib(*m_buffer, 2, 2, GL_FLOAT, sizeof(particle_vertex), 28); m_buffer->unbind(gl::buffer::ARRAY_BUFFER); m_vao->unbind(); } if (!m_shader) { gl::shader vert("smoke.vert"); gl::shader frag("smoke.frag"); gl::program *prog = new gl::program({vert, frag}); m_shader = std::unique_ptr(prog); } m_buffer->bind(gl::buffer::ARRAY_BUFFER); m_shader->bind(); m_vao->bind(); glDrawArrays(GL_TRIANGLES, 0, m_particlevertices.size()); m_shader->unbind(); m_vao->unbind(); m_buffer->unbind(gl::buffer::ARRAY_BUFFER); } //---------------------------------------------------------------------------