// Copyright (C) 2016-2016 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/engine/particle_group.hh" using namespace nv; particle_group_manager::particle_group_manager( context* a_context ) : m_context( a_context ) { } particle_group particle_group_manager::create_group( uint32 max_particles ) { particle_group result = m_groups.create(); particle_group_info* info = m_groups.get( result ); info->local = false; info->count = 0; info->quota = max_particles; info->vtx_buffer = m_context->create_buffer( VERTEX_BUFFER, STREAM_DRAW, info->quota * sizeof( particle_quad )/*, info->quads_[0].data*/ ); vertex_array_desc desc; desc.add_vertex_buffers< particle_vtx >( info->vtx_buffer, true ); info->vtx_array = m_context->create_vertex_array( desc ); info->quads = new particle_quad[info->quota]; info->ref_counter = 0; return result; } void particle_group_manager::release( particle_group group ) { if ( particle_group_info* info = m_groups.get( group ) ) { release( info ); m_groups.destroy( group ); } } void particle_group_manager::prepare( particle_group group ) { particle_group_info* info = m_groups.get( group ); if ( info ) { info->count = 0; } } void particle_group_manager::reset() { clear(); } void particle_group_manager::generate_data( array_view< particle > particles, const particle_group_settings* data, particle_group pgroup, const scene_state& s ) { // void* rawptr = m_context->map_buffer( info->vtx_buffer, nv::WRITE_UNSYNCHRONIZED, offset, info->count*sizeof( particle_quad ) ); // particle_quad* quads = reinterpret_cast( rawptr ); particle_group_info* group = m_groups.get( pgroup ); if ( particles.size() == 0 || !data || !group ) { return; } vec2 lb = vec2( -0.5f, -0.5f ); vec2 rt = vec2( 0.5f, 0.5f ); switch ( data->origin ) { case particle_origin::CENTER: break; case particle_origin::TOP_LEFT: lb = vec2( 0.f, -1.f ); rt = vec2( 1.f, 0.f ); break; case particle_origin::TOP_CENTER: lb.y = -1.f; rt.y = 0.f; break; case particle_origin::TOP_RIGHT: lb = vec2( -1.f, -1.f ); rt = vec2(); break; case particle_origin::CENTER_LEFT: lb.x = 0.f; rt.x = 1.f; break; case particle_origin::CENTER_RIGHT: lb.x = -1.f; rt.x = 0.f; break; case particle_origin::BOTTOM_LEFT: lb = vec2(); rt = vec2( 1.f, 1.f ); break; case particle_origin::BOTTOM_CENTER: lb.y = 0.f; rt.y = 1.f; break; case particle_origin::BOTTOM_RIGHT: lb = vec2( -1.f, 0.f ); rt = vec2( .0f, 1.f ); break; } const vec3 sm[4] = { vec3( lb.x, lb.y, 0.0f ), vec3( rt.x, lb.y, 0.0f ), vec3( lb.x, rt.y, 0.0f ), vec3( rt.x, rt.y, 0.0f ), }; vec3 z( 0.0f, 0.0f, 1.0f ); particle_orientation orientation = data->orientation; vec3 common_up( data->common_up ); vec3 common_dir( data->common_dir ); bool accurate_facing = data->accurate_facing; mat3 rot_mat; vec3 right; vec3 pdir( 0.0f, 1.0f, 0.0f ); vec3 camera_pos = s.get_camera().get_position(); vec3 inv_view_dir = math::normalize( -s.get_camera().get_direction() ); for ( uint32 i = 0; i < particles.size(); ++i ) { const particle& pdata = particles[i]; // particle_quad& rdata = quads[i]; particle_quad& rdata = group->quads[i + group->count]; vec3 view_dir( inv_view_dir ); if ( accurate_facing ) view_dir = math::normalize( camera_pos - pdata.position ); switch ( orientation ) { case particle_orientation::POINT: right = math::normalize( math::cross( view_dir, vec3( 0, 1, 0 ) ) ); right = math::rotate( right, pdata.rotation, view_dir ); rot_mat = mat3( right, math::cross( right, -view_dir ), -view_dir ); break; case particle_orientation::ORIENTED: pdir = normalize_safe( pdata.velocity, pdir ); right = math::normalize( math::cross( pdir, view_dir ) ); rot_mat = mat3( right, pdir, math::cross( pdir, right ) ); break; case particle_orientation::ORIENTED_COMMON: right = math::normalize( math::cross( common_dir, view_dir ) ); rot_mat = mat3( right, common_dir, math::cross( common_dir, right ) ); break; case particle_orientation::PERPENDICULAR: pdir = normalize_safe( pdata.velocity, pdir ); right = math::normalize( math::cross( common_up, pdir ) ); rot_mat = mat3( right, common_up, math::cross( common_up, right ) ); break; case particle_orientation::PERPENDICULAR_COMMON: right = math::normalize( math::cross( common_up, common_dir ) ); rot_mat = mat3( right, common_up, math::cross( common_up, right ) ); break; } vec2 texcoords[4] = { pdata.tcoord_a, vec2( pdata.tcoord_b.x, pdata.tcoord_a.y ), vec2( pdata.tcoord_a.x, pdata.tcoord_b.y ), pdata.tcoord_b }; vec3 size( pdata.size.x, pdata.size.y, 0.0f ); vec3 s0 = rot_mat * ( ( size * sm[0] ) ); vec3 s1 = rot_mat * ( ( size * sm[1] ) ); vec3 s2 = rot_mat * ( ( size * sm[2] ) ); vec3 s3 = rot_mat * ( ( size * sm[3] ) ); rdata.data[0].position = pdata.position + s0; rdata.data[0].color = pdata.color; rdata.data[0].texcoord = texcoords[0]; rdata.data[1].position = pdata.position + s1; rdata.data[1].color = pdata.color; rdata.data[1].texcoord = texcoords[1]; rdata.data[2].position = pdata.position + s2; rdata.data[2].color = pdata.color; rdata.data[2].texcoord = texcoords[2]; rdata.data[3].position = pdata.position + s3; rdata.data[3].color = pdata.color; rdata.data[3].texcoord = texcoords[3]; rdata.data[4] = rdata.data[2]; rdata.data[5] = rdata.data[1]; } // m_context->unmap_buffer( info->vtx_buffer ); m_context->update( group->vtx_buffer, group->quads, group->count * sizeof( particle_quad ), particles.size() * sizeof( particle_quad ) ); group->count += particles.size(); } particle_render_data nv::particle_group_manager::get_render_data( particle_group group ) { const particle_group_info* info = m_groups.get( group ); if ( info ) { return{ info->count, info->vtx_array }; } return{ 0, nv::vertex_array() }; } void nv::particle_group_manager::release( particle_group_info* info ) { if ( info ) { delete[] info->quads; m_context->release( info->vtx_array ); } } particle_group_manager::~particle_group_manager() { clear(); } void particle_group_manager::clear() { for ( auto& g : m_groups ) release( &g ); m_groups.clear(); } bool particle_group_manager::ref( particle_group group ) { particle_group_info* info = m_groups.get( group ); if ( !info ) return false; info->ref_counter++; return true; } bool particle_group_manager::unref( particle_group group ) { particle_group_info* info = m_groups.get( group ); if ( !info ) return false; info->ref_counter--; return true; }