// Copyright (C) 2014-2015 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_engine.hh" #include #include #include #include #include nv::hash_store< nv::shash64, nv::particle_emitter_func > nv::particle_engine::m_emitters; nv::hash_store< nv::shash64, nv::particle_affector_funcs > nv::particle_engine::m_affectors; nv::hash_map< nv::particle_emitter_func, nv::const_string >* nv::particle_engine::m_debug_emitter_names = nullptr; nv::hash_map< nv::particle_affector_func, nv::const_string >* nv::particle_engine::m_debug_affector_names = nullptr; nv::hash_map< nv::particle_affector_func, nv::type_entry* >* nv::particle_engine::m_debug_affector_types = nullptr; using namespace nv; static void nv_particle_emitter_point( const particle_emitter_data*, particle* p, uint32 count ) { for ( uint32 i = 0; i < count; ++i ) { p[i].position = vec3(); } } static void nv_particle_emitter_box( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { p[i].position = r.frange( -pe->hextents[0], pe->hextents[0] ) * pe->cdir + r.frange( 0.0f, pe->extents[1] ) * pe->dir + r.frange( -pe->hextents[2], pe->hextents[2] ) * pe->odir; } } static void nv_particle_emitter_cylinder( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec2 rellipse( r.disk_point( pe->precise ) * pe->extents[0] ); p[i].position = rellipse.x * pe->cdir + r.frange( 0.0f, pe->extents[1] ) * pe->dir + rellipse.y * pe->odir; } } static void nv_particle_emitter_sphere( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec3 rsphere = r.sphere_point( pe->precise ) * pe->extents[0]; p[i].position = rsphere.x * pe->cdir + rsphere.y * pe->dir + rsphere.z * pe->odir; } } static void nv_particle_emitter_cylindroid( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec2 rellipse = r.ellipse_point( vec2( pe->hextents[0], pe->hextents[2] ), pe->precise ); p[i].position = rellipse.x * pe->cdir + r.frange( 0.0f, pe->extents[1] ) * pe->dir + rellipse.y * pe->odir; } } static void nv_particle_emitter_ellipsoid( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec3 rsphere = r.ellipsoid_point( pe->hextents, pe->precise ); p[i].position = rsphere.x * pe->cdir + rsphere.y * pe->dir + rsphere.z * pe->odir; } } static void nv_particle_emitter_hollow_cylinder( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec2 rellipse = r.hollow_disk_point( pe->ihextents[0], pe->hextents[0], pe->precise ); p[i].position = rellipse.x * pe->cdir + r.frange( 0.0f, pe->extents[1] ) * pe->dir + rellipse.y * pe->odir; } } static void nv_particle_emitter_hollow_sphere( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec3 rellipse = r.hollow_sphere_point( pe->ihextents[0], pe->hextents[0], pe->precise ); p[i].position = rellipse.x * pe->cdir + rellipse.y * pe->dir + rellipse.z * pe->odir; } } static void nv_particle_emitter_hollow_cylindroid( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec2 rellipse = r.hollow_ellipse_point( vec2( pe->ihextents[0], pe->ihextents[2] ), vec2( pe->hextents[0], pe->hextents[2] ), pe->precise ); p[i].position = rellipse.x * pe->cdir + r.frange( 0.0f, pe->extents[1] ) * pe->dir + rellipse.y * pe->odir; } } static void nv_particle_emitter_hollow_ellipsoid( const particle_emitter_data* pe, particle* p, uint32 count ) { random& r = random::get(); for ( uint32 i = 0; i < count; ++i ) { vec3 rellipse = r.hollow_ellipsoid_point( pe->ihextents, pe->hextents, pe->precise ); p[i].position = rellipse.x * pe->cdir + rellipse.y * pe->dir + rellipse.z * pe->odir; } } struct nvpe_linear_force_data { nv::vec3 force_vector; bool average; }; static bool nv_particle_affector_linear_force_init( lua::table_guard* table, particle_affector_data* data ) { nvpe_linear_force_data* datap = reinterpret_cast( data->paramters ); datap->force_vector = table->get("force_vector", vec3() ); datap->average = table->get("average", false ); return true; } static void nv_particle_affector_linear_force( const particle_affector_data* data, particle* p, float factor, uint32 count ) { const nvpe_linear_force_data* datap = reinterpret_cast( data->paramters ); if ( datap->average ) { float norm_factor = nv::min( factor, 1.0f, p->lifetime ); for ( uint32 i = 0; i < count; ++i ) p[i].velocity = datap->force_vector * norm_factor + p[i].velocity * ( 1.0f - norm_factor ); } else { vec3 scvector = datap->force_vector * nv::min( factor, p->lifetime ); for ( uint32 i = 0; i < count; ++i ) p[i].velocity += scvector; } } struct nvpe_deflector_plane_data { nv::vec3 plane_point; nv::vec3 plane_normal; float bounce; float distance; }; static bool nv_particle_affector_deflector_plane_init( lua::table_guard* table, particle_affector_data* data ) { nvpe_deflector_plane_data* datap = reinterpret_cast( data->paramters ); datap->plane_point = table->get("plane_point", vec3() ); datap->plane_normal = table->get("plane_normal", vec3(0.0f,1.0f,0.0f) ); datap->plane_normal = normalize_safe( datap->plane_normal, vec3(0.0f,1.0f,0.0f) ); datap->bounce = table->get("bounce", 0.0f ); datap->distance = -math::dot( datap->plane_normal, datap->plane_point ) / sqrt( math::dot( datap->plane_normal, datap->plane_normal ) ); return true; } static void nv_particle_affector_deflector_plane( const particle_affector_data* data, particle* p, float factor, uint32 count ) { const nvpe_deflector_plane_data* datap = reinterpret_cast( data->paramters ); for ( uint32 i = 0; i < count; ++i ) { particle& pt = p[i]; vec3 direction = pt.velocity * nv::min( factor, p->lifetime ); if ( math::dot( datap->plane_normal, pt.position + direction ) + datap->distance <= 0.0f ) { float val = math::dot( datap->plane_normal, pt.position ) + datap->distance; if ( val > 0.0f ) { vec3 part_dir = direction * ( -val / math::dot( datap->plane_normal, direction ) ); pt.position = pt.position + part_dir + ( part_dir - direction ) * datap->bounce; pt.velocity = math::reflect( pt.velocity, datap->plane_normal ) * datap->bounce; } } } } struct nvpe_color_fader_data { nv::vec4 adjustment; }; static bool nv_particle_affector_color_fader_init( lua::table_guard* table, particle_affector_data* data ) { nvpe_color_fader_data* datap = reinterpret_cast( data->paramters ); datap->adjustment = table->get("adjustment", vec4() ); return true; } static void nv_particle_affector_color_fader( const particle_affector_data* data, particle* p, float factor, uint32 count ) { const nvpe_color_fader_data* datap = reinterpret_cast( data->paramters ); vec4 adjustment = datap->adjustment * nv::min( factor, p->lifetime ); for ( uint32 i = 0; i < count; ++i ) { p[i].color = math::clamp( p[i].color + adjustment, 0.0f, 1.0f ); } } struct nvpe_scaler_data { nv::vec2 adjustment; }; static bool nv_particle_affector_scaler_init( lua::table_guard* table, particle_affector_data* data ) { nvpe_scaler_data* datap = reinterpret_cast( data->paramters ); float rate = table->get("rate", 0.0f ); datap->adjustment = table->get("adjustment", vec2(rate,rate) ); return true; } static void nv_particle_affector_scaler( const particle_affector_data* data, particle* p, float factor, uint32 count ) { const nvpe_scaler_data* datap = reinterpret_cast( data->paramters ); vec2 adjustment = datap->adjustment * nv::min( factor, p->lifetime ); for ( uint32 i = 0; i < count; ++i ) { p[i].size = math::max( p[i].size + adjustment, vec2() ); } } nv::particle_engine::particle_engine( context* a_context, bool debug_data ) { m_context = a_context; if ( debug_data ) { m_debug_emitter_names = new nv::hash_map< nv::particle_emitter_func, nv::const_string >; m_debug_affector_names = new nv::hash_map< nv::particle_affector_func, nv::const_string >; m_debug_affector_types = new nv::hash_map< nv::particle_affector_func, nv::type_entry* >; } register_standard_emitters(); register_standard_affectors(); } nv::particle_system nv::particle_engine::create_system( const particle_system_data* data, particle_system_group group ) { particle_system_group_info* ginfo = m_groups.get( group ); if ( !ginfo ) return nv::particle_system(); particle_system result = m_systems.create(); particle_system_info* info = m_systems.get( result ); info->group = group; info->data = data; uint32 ecount = data->emitter_count; for ( uint32 i = 0; i < ecount; ++i ) { info->emitters[i].active = true; if ( data->emitters[i].duration_max == 0.0f ) info->emitters[i].pause = 0; else info->emitters[i].pause = random::get().frange( data->emitters[i].duration_min, data->emitters[i].duration_max ); } info->count = 0; info->particles = new particle[data->quota]; ginfo->ref_counter++; return result; } nv::particle_system nv::particle_engine::create_system( resource< nv::particle_system_data > rdata, particle_system_group group ) { if ( auto data = rdata.lock() ) return create_system( &*data, group ); return {}; } void nv::particle_engine::prepare( particle_system_group group ) { particle_system_group_info* info = m_groups.get( group ); if ( info ) { info->count = 0; } } nv::particle_system_group nv::particle_engine::create_group( uint32 max_particles ) { particle_system_group result = m_groups.create(); particle_system_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; } nv::particle_engine::~particle_engine() { clear(); } void nv::particle_engine::reset() { clear(); register_standard_emitters( ); register_standard_affectors(); } void nv::particle_engine::clear() { while ( m_systems.size() > 0 ) release( m_systems.get_handle( 0 ) ); while ( m_groups.size() > 0 ) release( m_groups.get_handle( 0 ) ); m_emitters.clear(); m_affectors.clear(); } void nv::particle_engine::release( particle_system system ) { particle_system_info* info = m_systems.get( system ); if ( info ) { particle_system_group_info* ginfo = m_groups.get( info->group ); if ( ginfo ) ginfo->ref_counter--; delete[] info->particles; m_systems.destroy( system ); } } void nv::particle_engine::release( particle_system_group group ) { particle_system_group_info* info = m_groups.get( group ); if ( info ) { delete[] info->quads; m_context->release( info->vtx_array ); m_groups.destroy( group ); } } void nv::particle_engine::render( particle_system system, const scene_state& s ) { particle_system_info* info = m_systems.get( system ); if ( info ) { generate_data( info, s ); } } void nv::particle_engine::update( particle_system system, transform model, float dtime ) { particle_system_info* info = m_systems.get( system ); if ( info ) { // while ( dtime > 0.2 ) // { // update_emitters( info, 0.2 ); // destroy_particles( info, 0.2 ); // create_particles( info, 0.2 ); // update_particles( info, 0.2 ); // dtime -= 0.2; // } update_emitters( info, dtime ); destroy_particles( info, dtime ); create_particles( info, model, dtime ); update_particles( info, dtime ); // generate_data( info ); } } void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b ) { particle_system_info* info = m_systems.get( system ); if ( info ) { info->texcoords[0] = a; info->texcoords[1] = b; } } void nv::particle_engine::generate_data( particle_system_info* info, 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_system_group_info* group = m_groups.get( info->group ); if ( !info ) { return; } vec2 lb = vec2( -0.5f, -0.5f ); vec2 rt = vec2( 0.5f, 0.5f ); switch ( info->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 = info->data->orientation; vec3 common_up ( info->data->common_up ); vec3 common_dir( info->data->common_dir ); bool accurate_facing = info->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 < info->count; ++i ) { const particle& pdata = info->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), info->count*sizeof( particle_quad ) ); group->count += info->count; } void nv::particle_engine::destroy_particles( particle_system_info* info, float dtime ) { if ( info->count > 0 ) for ( sint32 i = sint32( info->count ) - 1; i >= 0; --i ) { particle& pinfo = info->particles[i]; pinfo.lifetime += dtime; if ( pinfo.lifetime >= pinfo.death ) { info->count--; swap( info->particles[i], info->particles[info->count] ); } } } void nv::particle_engine::create_particles( particle_system_info* info, transform model, float dtime ) { uint32 ecount = info->data->emitter_count; if ( ecount == 0 ) return; random& r = random::get(); bool local = model.is_identity(); // if ( !local ) // { // source = vec3( m_model_matrix[3] ); // orient = mat3( m_model_matrix ); // } for ( uint32 i = 0; i < ecount; ++i ) { const auto& edata = info->data->emitters[i]; auto& einfo = info->emitters[i]; if ( einfo.active ) { einfo.next -= dtime; float fperiod = 1.0f / edata.rate; while ( einfo.next < 0.0f ) { if ( info->count < info->data->quota-1 ) { particle& pinfo = info->particles[info->count]; edata.emitter_func( &(info->data->emitters[i]), &pinfo, 1 ); pinfo.position = vec3(); pinfo.position+= edata.position; // if ( !local ) pinfo.position = pinfo.position * model; pinfo.color = edata.color_min == edata.color_max ? edata.color_min : r.range( edata.color_min, edata.color_max ); pinfo.size = edata.size_min == edata.size_max ? edata.size_min : r.range( edata.size_min, edata.size_max ); if ( edata.square ) pinfo.size.y = pinfo.size.x; float velocity = edata.velocity_min == edata.velocity_max ? edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max ); pinfo.lifetime = dtime + einfo.next; pinfo.death = ( edata.lifetime_min == edata.lifetime_max ? edata.lifetime_min : r.frange( edata.lifetime_min, edata.lifetime_max ) ); pinfo.rotation = r.frand( 2* math::pi() ); pinfo.tcoord_a = info->texcoords[0]; pinfo.tcoord_b = info->texcoords[1]; pinfo.velocity = edata.dir; if ( edata.angle > 0.0f ) { float emission_angle = math::radians( edata.angle ); float cos_theta = r.frange( cos( emission_angle ), 1.0f ); float sin_theta = sqrt(1.0f - cos_theta * cos_theta ); float phi = r.frange( 0.0f, 2* math::pi() ); pinfo.velocity = model.get_orientation() * ( edata.odir * ( cos(phi) * sin_theta ) + edata.cdir * ( sin(phi)*sin_theta ) + edata.dir * cos_theta ); } pinfo.velocity *= velocity; info->count++; } einfo.next += fperiod; } } } } void nv::particle_engine::update_particles( particle_system_info* info, float dtime ) { if ( dtime <= 0.0f ) return; uint32 acount = info->data->affector_count; for ( uint32 i = 0; i < acount; ++i ) { const particle_affector_data* padata = &(info->data->affectors[i]); padata->process( padata, info->particles, dtime, info->count ); } for ( uint32 i = 0; i < info->count; ++i ) { particle& pdata = info->particles[i]; float factor = min( dtime, pdata.lifetime ); pdata.position += pdata.velocity * factor; } } void nv::particle_engine::update_emitters( particle_system_info* info, float dtime ) { uint32 ecount = info->data->emitter_count; if ( ecount == 0 ) return; random& r = random::get(); for ( uint32 i = 0; i < ecount; ++i ) { const auto& edata = info->data->emitters[i]; auto& einfo = info->emitters[i]; if ( einfo.pause > 0.0f ) { einfo.pause -= dtime; if ( einfo.pause == 0.0f ) einfo.pause = -0.001f; } if ( einfo.pause < 0.0f ) { if ( einfo.active ) { einfo.active = false; if ( edata.repeat_min > 0.0f ) einfo.pause += r.frange( edata.repeat_min, edata.repeat_max ); else einfo.pause = 0.0f; } else { einfo.active = true; einfo.pause += r.frange( edata.duration_min, edata.duration_max ); } } } } void nv::particle_engine::register_emitter_type( const string_view& name, particle_emitter_func func ) { if ( m_debug_emitter_names ) ( *m_debug_emitter_names )[func] = name; m_emitters[ name ] = func; } void nv::particle_engine::register_standard_emitters() { register_emitter_type( "point", nv_particle_emitter_point ); register_emitter_type( "box", nv_particle_emitter_box ); register_emitter_type( "cylinder", nv_particle_emitter_cylinder ); register_emitter_type( "sphere", nv_particle_emitter_sphere ); register_emitter_type( "cylindroid", nv_particle_emitter_cylindroid ); register_emitter_type( "ellipsoid", nv_particle_emitter_ellipsoid ); register_emitter_type( "hollow_cylinder", nv_particle_emitter_hollow_cylinder ); register_emitter_type( "hollow_sphere", nv_particle_emitter_hollow_sphere ); register_emitter_type( "hollow_cylindroid", nv_particle_emitter_hollow_cylindroid ); register_emitter_type( "hollow_ellipsoid", nv_particle_emitter_hollow_ellipsoid ); } void nv::particle_engine::register_affector_type( const string_view& name, particle_affector_init_func init, particle_affector_func process ) { if ( m_debug_affector_names ) ( *m_debug_affector_names )[process] = name; m_affectors[ name ].init = init; m_affectors[ name ].process = process; } nv::particle_emitter_func nv::particle_engine::get_emitter( shash64 emitter ) { auto emitter_iter = m_emitters.find( emitter ); if ( emitter_iter != m_emitters.end() ) { return emitter_iter->second; } return nullptr; } nv::particle_affector_funcs nv::particle_engine::get_affector( shash64 affector ) { auto affector_iter = m_affectors.find( affector ); if ( affector_iter != m_affectors.end() ) { return affector_iter->second; } return { nullptr, nullptr }; } nv::string_view nv::particle_engine::get_debug_name( particle_emitter_func f ) { auto i = m_debug_emitter_names->find( f ); if ( i != m_debug_emitter_names->end() ) return i->second; return {}; } nv::string_view nv::particle_engine::get_debug_name( particle_affector_func f ) { auto i = m_debug_affector_names->find( f ); if ( i != m_debug_affector_names->end() ) return i->second; return {}; } nv::particle_render_data nv::particle_engine::get_render_data( particle_system_group group ) { const particle_system_group_info* info = m_groups.get( group ); if ( info ) { return{ info->count, info->vtx_array }; } return { 0, nv::vertex_array() }; } void nv::particle_engine::register_standard_affectors() { register_affector_type( "linear_force", nv_particle_affector_linear_force_init, nv_particle_affector_linear_force ); register_affector_type( "deflector_plane", nv_particle_affector_deflector_plane_init, nv_particle_affector_deflector_plane ); register_affector_type( "color_fader", nv_particle_affector_color_fader_init, nv_particle_affector_color_fader ); register_affector_type( "scaler", nv_particle_affector_scaler_init, nv_particle_affector_scaler ); } void nv::particle_engine::register_types( type_database* db ) { db->create_type() .value( "PS_POINT", sint32( particle_orientation::POINT ), "Point" ) .value( "PS_ORIENTED", sint32( particle_orientation::ORIENTED ), "Oriented" ) .value( "PS_ORIENTED_COMMON", sint32( particle_orientation::ORIENTED_COMMON ), "Oriented Common" ) .value( "PS_PERPENDICULAR", sint32( particle_orientation::PERPENDICULAR ), "Perpendicular" ) .value( "PS_PERPENDICULAR_COMMON", sint32( particle_orientation::PERPENDICULAR_COMMON ), "Perpendicular Common" ) ; db->create_type() .value( "PS_CENTER", sint32( particle_origin::CENTER ), "Center" ) .value( "PS_TOP_LEFT", sint32( particle_origin::TOP_LEFT ), "Top left" ) .value( "PS_TOP_CENTER", sint32( particle_origin::TOP_CENTER ), "Top center" ) .value( "PS_TOP_RIGHT", sint32( particle_origin::TOP_RIGHT ), "Top right" ) .value( "PS_CENTER_LEFT", sint32( particle_origin::CENTER_LEFT ), "Center left" ) .value( "PS_CENTER_RIGHT", sint32( particle_origin::CENTER_RIGHT ), "Center right" ) .value( "PS_BOTTOM_LEFT", sint32( particle_origin::BOTTOM_LEFT ), "Bottom left" ) .value( "PS_BOTTOM_CENTER", sint32( particle_origin::BOTTOM_CENTER ), "Bottom center" ) .value( "PS_BOTTOM_RIGHT", sint32( particle_origin::BOTTOM_RIGHT ), "Bottom right" ) ; if ( m_debug_affector_types ) { int you_could_get_rid_of_the_init_function_using_these; int error; (*m_debug_affector_types)[ nv_particle_affector_linear_force ] = db->create_type( "linear force" ) .field( "force_vector", &nvpe_linear_force_data::force_vector ) .field( "average", &nvpe_linear_force_data::average ) .get(); ( *m_debug_affector_types )[nv_particle_affector_deflector_plane] = db->create_type( "deflector plane" ) .field( "plane_point", &nvpe_deflector_plane_data::plane_point ) .field( "plane_normal", &nvpe_deflector_plane_data::plane_normal ) .field( "bounce", &nvpe_deflector_plane_data::bounce ) .field( "distance", &nvpe_deflector_plane_data::distance ) .get(); ( *m_debug_affector_types )[nv_particle_affector_color_fader] = db->create_type( "color fader" ) .field( "adjustment", & nvpe_color_fader_data::adjustment ) .get(); ( *m_debug_affector_types )[nv_particle_affector_scaler] = db->create_type( "scaler" ) .field( "adjustment", & nvpe_scaler_data::adjustment ) .get(); } }