source: trunk/src/engine/particle_engine.cc @ 500

Last change on this file since 500 was 500, checked in by epyon, 9 years ago
  • massive particle_engine updates
  • more image data types
  • texture layers for framebuffers
File size: 31.8 KB
RevLine 
[395]1// Copyright (C) 2014-2015 ChaosForge Ltd
2// http://chaosforge.org/
3//
4// This file is part of Nova libraries.
5// For conditions of distribution and use, see copying.txt file in root folder.
[319]6
[320]7#include "nv/engine/particle_engine.hh"
[306]8
9#include <nv/interface/device.hh>
[319]10#include <nv/core/random.hh>
[374]11#include <nv/stl/utility.hh>
[452]12#include <nv/lua/lua_math.hh>
[319]13#include <nv/core/logging.hh>
[306]14
15static const char *nv_particle_engine_vertex_shader_world =
16        "#version 120\n"
17        "attribute vec3 nv_position;\n"
18        "attribute vec2 nv_texcoord;\n"
19        "attribute vec4 nv_color;\n"
20        "varying vec4 v_color;\n"
21        "varying vec2 v_texcoord;\n"
[500]22        "varying vec3 v_position;\n"
[306]23        "uniform mat4 nv_m_view;\n"
24        "uniform mat4 nv_m_projection;\n"
25        "void main(void)\n"
26        "{\n"
[500]27        "       v_position    = nv_position;\n"
28        "       v_texcoord    = nv_texcoord;\n"
29        "       v_color       = nv_color;\n"
30        "       gl_Position   = nv_m_projection * nv_m_view * vec4 (nv_position, 1.0);\n"
[306]31        "}\n";
32static const char *nv_particle_engine_vertex_shader_local =
33        "#version 120\n"
34        "attribute vec3 nv_position;\n"
35        "attribute vec2 nv_texcoord;\n"
36        "attribute vec4 nv_color;\n"
37        "varying vec4 v_color;\n"
38        "varying vec2 v_texcoord;\n"
[500]39        "varying vec3 v_position;\n"
[306]40        "uniform mat4 nv_m_mvp;\n"
41        "void main(void)\n"
42        "{\n"
[500]43        "       v_position    = nv_position;\n"
44        "       v_texcoord    = nv_texcoord;\n"
45        "       v_color       = nv_color;\n"
46        "       gl_Position   = nv_m_mvp * vec4 (nv_position, 1.0);\n"
[306]47        "}\n";
48static const char *nv_particle_engine_fragment_shader =
49        "#version 120\n"
50        "uniform sampler2D nv_t_diffuse;\n"
51        "varying vec4 v_color;\n"
52        "varying vec2 v_texcoord;\n"
[500]53        "varying vec3 v_position;\n"
[306]54        "void main(void)\n"
55        "{\n"
56        "       vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord );\n"
[500]57        "       float edge = smoothstep( 0.0, 0.1, v_position.y );\n"
58        "       gl_FragColor   = v_color * tex_color * edge;\n"
[306]59        "}\n";
60
[312]61using namespace nv;
62
63static void nv_particle_emmiter_point( const particle_emmiter_data*, particle* p, uint32 count )
64{
65        for ( uint32 i = 0; i < count; ++i )
66        {
67                p[i].position = vec3();
68        }
69
70}
71
72static void nv_particle_emmiter_box( const particle_emmiter_data* pe, particle* p, uint32 count )
73{
74        random& r = random::get();
75        for ( uint32 i = 0; i < count; ++i )
76        {
77                p[i].position =
78                        r.frange( -pe->hextents[0], pe->hextents[0] ) * pe->cdir +
79                        r.frange( 0.0f, pe->extents[1] ) * pe->dir +
80                        r.frange( -pe->hextents[2], pe->hextents[2] ) * pe->odir;
81        }
82}
83
84static void nv_particle_emmiter_cylinder( const particle_emmiter_data* pe, particle* p, uint32 count )
85{
86        random& r = random::get();
87        for ( uint32 i = 0; i < count; ++i )
88        {
89                vec2 rellipse( r.disk_point( pe->precise ) * pe->extents[0] );
90                p[i].position =
91                        rellipse.x * pe->cdir +
92                        r.frange( 0.0f, pe->extents[1] ) * pe->dir +
93                        rellipse.y * pe->odir;
94        }
95}
96
97static void nv_particle_emmiter_sphere( const particle_emmiter_data* pe, particle* p, uint32 count )
98{
99        random& r = random::get();
100        for ( uint32 i = 0; i < count; ++i )
101        {
102                vec3 rsphere = r.sphere_point( pe->precise ) * pe->extents[0];
103                p[i].position =
104                        rsphere.x * pe->cdir +
105                        rsphere.y * pe->dir +
106                        rsphere.z * pe->odir;
107        }
108}
109
110static void nv_particle_emmiter_cylindroid( const particle_emmiter_data* pe, particle* p, uint32 count )
111{
112        random& r = random::get();
113        for ( uint32 i = 0; i < count; ++i )
114        {
115                vec2 rellipse = r.ellipse_point( vec2( pe->hextents[0], pe->hextents[2] ), pe->precise );
116                p[i].position =
117                        rellipse.x * pe->cdir +
118                        r.frange( 0.0f, pe->extents[1] ) * pe->dir +
119                        rellipse.y * pe->odir;
120        }
121}
122
123static void nv_particle_emmiter_ellipsoid( const particle_emmiter_data* pe, particle* p, uint32 count )
124{
125        random& r = random::get();
126        for ( uint32 i = 0; i < count; ++i )
127        {
128                vec3 rsphere = r.ellipsoid_point( pe->hextents, pe->precise );
129                p[i].position =
130                        rsphere.x * pe->cdir +
131                        rsphere.y * pe->dir +
132                        rsphere.z * pe->odir;
133        }
134}
135
136static void nv_particle_emmiter_hollow_cylinder( const particle_emmiter_data* pe, particle* p, uint32 count )
137{
138        random& r = random::get();
139        for ( uint32 i = 0; i < count; ++i )
140        {
141                vec2 rellipse = r.hollow_disk_point(
142                        pe->ihextents[0],
143                        pe->hextents[0],
144                        pe->precise );
145                p[i].position =
146                        rellipse.x * pe->cdir +
147                        r.frange( 0.0f, pe->extents[1] ) * pe->dir +
148                        rellipse.y * pe->odir;
149        }
150}
151
152static void nv_particle_emmiter_hollow_sphere( const particle_emmiter_data* pe, particle* p, uint32 count )
153{
154        random& r = random::get();
155        for ( uint32 i = 0; i < count; ++i )
156        {
157                vec3 rellipse = r.hollow_sphere_point( pe->ihextents[0], pe->hextents[0], pe->precise );
158                p[i].position =
159                        rellipse.x * pe->cdir +
160                        rellipse.y * pe->dir +
161                        rellipse.z * pe->odir;
162        }
163}
164
165static void nv_particle_emmiter_hollow_cylindroid( const particle_emmiter_data* pe, particle* p, uint32 count )
166{
167        random& r = random::get();
168        for ( uint32 i = 0; i < count; ++i )
169        {
170                vec2 rellipse = r.hollow_ellipse_point(
171                        vec2( pe->ihextents[0], pe->ihextents[2] ),
172                        vec2( pe->hextents[0], pe->hextents[2] ),
173                        pe->precise );
174                p[i].position =
175                        rellipse.x * pe->cdir +
176                        r.frange( 0.0f, pe->extents[1] ) * pe->dir +
177                        rellipse.y * pe->odir;
178        }
179}
180
181static void nv_particle_emmiter_hollow_ellipsoid( const particle_emmiter_data* pe, particle* p, uint32 count )
182{
183        random& r = random::get();
184        for ( uint32 i = 0; i < count; ++i )
185        {
186                vec3 rellipse = r.hollow_ellipsoid_point( pe->ihextents, pe->hextents, pe->precise );
187                p[i].position =
188                        rellipse.x * pe->cdir +
189                        rellipse.y * pe->dir +
190                        rellipse.z * pe->odir;
191        }
192}
193
194struct nvpe_linear_force_data
195{
196        nv::vec3 force_vector;
197        bool     average;
198};
199
200static bool nv_particle_affector_linear_force_init( lua::table_guard* table, particle_affector_data* data )
201{
[406]202        nvpe_linear_force_data* datap = reinterpret_cast<nvpe_linear_force_data*>( data->paramters );
[312]203        datap->force_vector = table->get<vec3>("force_vector", vec3() );
204        datap->average      = table->get<bool>("average", false );
205        return true;
206}
207
208static void nv_particle_affector_linear_force( const particle_affector_data* data, particle* p, float factor, uint32 count )
209{
[406]210        const nvpe_linear_force_data* datap = reinterpret_cast<const nvpe_linear_force_data*>( data->paramters );
[312]211        if ( datap->average )
212        {
[500]213                float norm_factor = nv::min( factor, 1.0f, p->lifetime );
[312]214                for ( uint32 i = 0; i < count; ++i )
215                        p[i].velocity = datap->force_vector * norm_factor + p[i].velocity * ( 1.0f - norm_factor );
216        }
217        else
218        {
[500]219                vec3 scvector = datap->force_vector * nv::min( factor, p->lifetime );
[312]220                for ( uint32 i = 0; i < count; ++i ) p[i].velocity += scvector;
221        }
222}
223
224struct nvpe_deflector_plane_data
225{
226        nv::vec3 plane_point;
227        nv::vec3 plane_normal;
228        float    bounce;
229        float    distance;
230};
231
232static bool nv_particle_affector_deflector_plane_init( lua::table_guard* table, particle_affector_data* data )
233{
[406]234        nvpe_deflector_plane_data* datap = reinterpret_cast<nvpe_deflector_plane_data*>( data->paramters );
[312]235        datap->plane_point  = table->get<vec3>("plane_point",  vec3() );
236        datap->plane_normal = table->get<vec3>("plane_normal", vec3(0.0f,1.0f,0.0f) );
237        datap->plane_normal = normalize_safe( datap->plane_normal, vec3(0.0f,1.0f,0.0f) );
238        datap->bounce       = table->get<float>("bounce", 0.0f );
[471]239        datap->distance     = -math::dot( datap->plane_normal, datap->plane_point ) / sqrt( math::dot( datap->plane_normal, datap->plane_normal ) );
[312]240        return true;
241}
242
243static void nv_particle_affector_deflector_plane( const particle_affector_data* data, particle* p, float factor, uint32 count )
244{
[406]245        const nvpe_deflector_plane_data* datap = reinterpret_cast<const nvpe_deflector_plane_data*>( data->paramters );
[312]246        for ( uint32 i = 0; i < count; ++i )
247        {
248                particle& pt = p[i];
[500]249                vec3 direction  = pt.velocity * nv::min( factor, p->lifetime );
[454]250                if ( math::dot( datap->plane_normal, pt.position + direction ) + datap->distance <= 0.0f )
[312]251                {
[454]252                        float val = math::dot( datap->plane_normal, pt.position ) + datap->distance;
[312]253                        if ( val > 0.0f )
254                        {
[454]255                                vec3 part_dir = direction * ( -val / math::dot( datap->plane_normal, direction ) );
[312]256                                pt.position = pt.position + part_dir + ( part_dir - direction ) * datap->bounce;
[454]257                                pt.velocity = math::reflect( pt.velocity, datap->plane_normal ) * datap->bounce;
[312]258                        }
259                }
260        }
261}
262
263struct nvpe_color_fader_data
264{
265        nv::vec4 adjustment;
266};
267
268static bool nv_particle_affector_color_fader_init( lua::table_guard* table, particle_affector_data* data )
269{
[406]270        nvpe_color_fader_data* datap = reinterpret_cast<nvpe_color_fader_data*>( data->paramters );
[312]271        datap->adjustment = table->get<vec4>("adjustment",  vec4() );
272        return true;
273}
274
275static void nv_particle_affector_color_fader( const particle_affector_data* data, particle* p, float factor, uint32 count )
276{
[406]277        const nvpe_color_fader_data* datap = reinterpret_cast<const nvpe_color_fader_data*>( data->paramters );
[500]278        vec4 adjustment = datap->adjustment * nv::min( factor, p->lifetime );
[312]279        for ( uint32 i = 0; i < count; ++i )
280        {
[454]281                p[i].color = math::clamp( p[i].color + adjustment, 0.0f, 1.0f );
[312]282        }
283}
284
285struct nvpe_scaler_data
286{
287        nv::vec2 adjustment;
288};
289
290static bool nv_particle_affector_scaler_init( lua::table_guard* table, particle_affector_data* data )
291{
[406]292        nvpe_scaler_data* datap = reinterpret_cast<nvpe_scaler_data*>( data->paramters );
[312]293        float rate        = table->get<float>("rate", 0.0f );
294        datap->adjustment = table->get<vec2>("adjustment",  vec2(rate,rate) );
295        return true;
296}
297
298static void nv_particle_affector_scaler( const particle_affector_data* data, particle* p, float factor, uint32 count )
299{
[406]300        const nvpe_scaler_data* datap = reinterpret_cast<const nvpe_scaler_data*>( data->paramters );
[500]301        vec2 adjustment = datap->adjustment * nv::min( factor, p->lifetime );
[312]302        for ( uint32 i = 0; i < count; ++i )
303        {
[454]304                p[i].size = math::max( p[i].size + adjustment, vec2() );
[312]305        }
306}
307
[306]308void nv::particle_engine::load( lua::table_guard& table )
309{
[439]310        shash64 id = table.get_string_hash_64( "id" );
[479]311        if ( !id )
[306]312        {
[365]313                NV_LOG_ERROR( "Bad table passed to particle_engine!" )
[306]314        }
315        // TODO : overwrite check
316        m_names[ id ] = m_data.size();
317
318        m_data.emplace_back();
319        auto& data = m_data.back();
320
321        data.quota   = table.get<uint32>("quota", 1024 );
[309]322        data.local   = table.get<bool>("local_space", false );
[306]323        data.accurate_facing = table.get<bool>("accurate_facing", false );
324        data.emmiter_count   = 0;
[312]325        data.affector_count  = 0;
[306]326
[361]327        const_string orientation = table.get_string( "orientation", "point" );
[306]328        if ( orientation == "point" )                     { data.orientation = particle_orientation::POINT; }
329        else if ( orientation == "oriented" )             { data.orientation = particle_orientation::ORIENTED; }
330        else if ( orientation == "oriented_common" )      { data.orientation = particle_orientation::ORIENTED_COMMON; }
331        else if ( orientation == "perpendicular" )        { data.orientation = particle_orientation::PERPENDICULAR; }
332        else if ( orientation == "perpendicular_common" ) { data.orientation = particle_orientation::PERPENDICULAR_COMMON; }
333        else
334        {
[365]335                NV_LOG_ERROR( "Unknown orientation type! (", orientation, ")!" );
[306]336                data.orientation = particle_orientation::POINT;
337        }
[309]338
[361]339        const_string origin = table.get_string( "origin", "center" );
[309]340        if      ( origin == "center" )        { data.origin = particle_origin::CENTER; }
341        else if ( origin == "top_left" )      { data.origin = particle_origin::TOP_LEFT; }
342        else if ( origin == "top_center" )    { data.origin = particle_origin::TOP_CENTER; }
343        else if ( origin == "top_right" )     { data.origin = particle_origin::TOP_RIGHT; }
344        else if ( origin == "center_left" )   { data.origin = particle_origin::CENTER_LEFT; }
345        else if ( origin == "center_right" )  { data.origin = particle_origin::CENTER_RIGHT; }
346        else if ( origin == "bottom_left" )   { data.origin = particle_origin::BOTTOM_LEFT; }
347        else if ( origin == "bottom_center" ) { data.origin = particle_origin::BOTTOM_CENTER; }
348        else if ( origin == "bottom_right" )  { data.origin = particle_origin::BOTTOM_RIGHT; }
349        else
350        {
[365]351                NV_LOG_ERROR( "Unknown particle origin! (", origin, ")!" );
[309]352                data.origin = particle_origin::CENTER;
353        }
354
[454]355        data.common_up  = math::normalize( table.get<vec3>("common_up",  vec3(1,0,0) ) );
356        data.common_dir = math::normalize( table.get<vec3>("common_dir", vec3(0,1,0) ) );
[306]357
[309]358        vec2 def_size        = table.get<vec2>("size", vec2(0.1,0.1) );
[306]359        uint32 elements = table.get_size();
360        for ( uint32 i = 0; i < elements; ++i )
361        {
362                lua::table_guard element( table, i+1 );
[439]363                const_string type     = element.get_string( "type" );
364                const_string sub_type = element.get_string( "sub_type" );
[306]365                if ( type == "emmiter" )
366                {
367                        if ( data.emmiter_count < MAX_PARTICLE_EMMITERS )
368                        {
369                                particle_emmiter_data& edata = data.emmiters[ data.emmiter_count ];
[312]370                                auto emmiter_iter = m_emmiters.find( sub_type );
371                                if ( emmiter_iter != m_emmiters.end() )
372                                {
373                                        edata.emmiter_func = emmiter_iter->second;
374                                }
[309]375                                else
376                                {
[312]377                                        edata.emmiter_func = nv_particle_emmiter_point;
[439]378                                        NV_LOG_WARNING( "Unknown emmiter type in particle system! (", sub_type, ")" );
[309]379                                }
380
381                                edata.position     = element.get<vec3>("position", vec3() );
382                                edata.extents      = element.get<vec3>("extents", vec3(1,1,1) );
383                                edata.extents[0]   = element.get<float>("width",  edata.extents[0] );
384                                edata.extents[1]   = element.get<float>("depth",  edata.extents[1] );
385                                edata.extents[2]   = element.get<float>("height", edata.extents[2] );
386                                edata.extents[0]   = element.get<float>("radius",  edata.extents[0] );
387                                edata.iextents     = element.get<vec3>("inner_extents", vec3() );
388                                edata.iextents[0]  = element.get<float>("inner_width",  edata.iextents[0] );
389                                edata.iextents[1]  = element.get<float>("inner_depth",  edata.iextents[1] );
390                                edata.iextents[2]  = element.get<float>("inner_height", edata.iextents[2] );
391                                edata.iextents[0]  = element.get<float>("inner_radius",  edata.iextents[0] );
392                                edata.hextents     = 0.5f * edata.extents;
393                                edata.ihextents    = 0.5f * edata.iextents;
394                                edata.precise      = element.get<bool>("precise", false );
395                                edata.square       = element.get<bool>("square", true );
[306]396                                vec4 color         = element.get<vec4>("color", vec4(1,1,1,1) );
397                                edata.color_min    = element.get<vec4>("color_min", color );
398                                edata.color_max    = element.get<vec4>("color_max", color );
[309]399                                vec2 size          = element.get<vec2>("size", def_size );
[306]400                                edata.size_min     = element.get<vec2>("size_min", size );
401                                edata.size_max     = element.get<vec2>("size_max", size );
402                                edata.angle        = element.get<float>("angle", 0.0f );
403                                float velocity     = element.get<float>("velocity", 0.0f );
404                                edata.velocity_min = element.get<float>("velocity_min", velocity );
405                                edata.velocity_max = element.get<float>("velocity_max", velocity );
406                                float lifetime     = element.get<float>("lifetime", 1.0f );
[500]407                                edata.lifetime_min = element.get<float>("lifetime_min", lifetime );
408                                edata.lifetime_max = element.get<float>("lifetime_max", lifetime );
[307]409                                float duration     = element.get<float>("duration", 0.0f );
[500]410                                edata.duration_min = element.get<float>("duration_min", duration );
411                                edata.duration_max = element.get<float>("duration_max", duration );
[307]412                                float repeat       = element.get<float>("repeat_delay", 0.0f );
[500]413                                edata.repeat_min   = element.get<float>("repeat_delay_min", repeat );
414                                edata.repeat_max   = element.get<float>("repeat_delay_max", repeat );
[307]415
[306]416                                edata.rate         = element.get<float>("rate", 1.0f );
[454]417                                edata.dir          = math::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
[306]418                               
[451]419                                edata.odir = vec3( 0, 0, 1 );
[306]420                                if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
[454]421                                        edata.odir = math::normalize( math::cross( edata.dir, vec3( 0, 1, 0 ) ) );
422                                edata.cdir = math::cross( edata.dir, edata.odir );
[306]423
424                                data.emmiter_count++;
425                        }
426                        else
427                        {
[365]428                                NV_LOG_ERROR( "Too many emmiters (", MAX_PARTICLE_EMMITERS, " is MAX)!" );
[306]429                        }
430                }
431                else if ( type == "affector" )
432                {
[312]433                        if ( data.affector_count < MAX_PARTICLE_AFFECTORS )
434                        {
435                                particle_affector_data& adata = data.affectors[ data.affector_count ];
436                                data.affector_count++;
437                                auto affector_iter = m_affectors.find( sub_type );
438                                if ( affector_iter != m_affectors.end() )
439                                {
440                                        adata.process = affector_iter->second.process;
441                                        if ( !affector_iter->second.init( &element, &adata ) )
442                                        {
443                                                data.affector_count--;
[439]444                                                NV_LOG_WARNING( "Bad data passed to ", sub_type, " affector in particle system!" );
[312]445                                        }
446                                }
447                                else
448                                {
449                                        data.affector_count--;
[439]450                                        NV_LOG_WARNING( "Unknown affector type in particle system! (", sub_type, ")" );
[312]451                                }
452                        }
453                        else
454                        {
[365]455                                NV_LOG_ERROR( "Too many affectors (", MAX_PARTICLE_AFFECTORS, " is MAX)!" );
[312]456                        }
[306]457                }
458                else
459                {
[365]460                        NV_LOG_WARNING( "Unknown element in particle system! (", type, ")" );
[306]461                }
462        }
463
464}
465
466nv::particle_engine::particle_engine( context* a_context )
467{
468        m_context       = a_context;
469        m_device        = a_context->get_device();
470        m_program_local = m_device->create_program( nv_particle_engine_vertex_shader_local, nv_particle_engine_fragment_shader );
471        m_program_world = m_device->create_program( nv_particle_engine_vertex_shader_world, nv_particle_engine_fragment_shader );
[312]472
473        register_standard_emmiters();
474        register_standard_affectors();
[306]475}
476
[499]477nv::particle_system nv::particle_engine::create_system( const string_view& id, particle_system_group group )
[306]478{
[500]479        particle_system_group_info* ginfo = m_groups.get( group );
480        if ( !ginfo ) return nv::particle_system();
481
[306]482        auto it = m_names.find( id );
483        if ( it == m_names.end() )
484        {
485                return particle_system();
486        }
487        const particle_system_data* data = &(m_data[it->second]);
488        particle_system result = m_systems.create();
489        particle_system_info* info = m_systems.get( result );
[499]490        info->group = group;
[306]491        info->data     = data;
492        uint32 ecount = data->emmiter_count;
493        for ( uint32 i = 0; i < ecount; ++i )
494        {
[307]495                info->emmiters[i].active      = true;
[500]496                if ( data->emmiters[i].duration_max == 0.0f )
497                        info->emmiters[i].pause = 0;
[353]498                else
[500]499                        info->emmiters[i].pause = random::get().frange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
[353]500
[306]501        }
502
503        info->count = 0;
504        info->particles = new particle[ data->quota ];
[500]505        ginfo->ref_counter++;
[306]506
507        return result;
508}
509
[499]510void nv::particle_engine::prepare( particle_system_group group )
[306]511{
[499]512        particle_system_group_info* info = m_groups.get( group );
[306]513        if ( info )
514        {
[499]515                info->count = 0;
[306]516        }
517}
518
[499]519void nv::particle_engine::draw( particle_system_group group, const render_state& rs, const scene_state& ss )
520{
521        particle_system_group_info* info = m_groups.get( group );
522        if ( info )
523        {
524                m_context->draw( nv::TRIANGLES, rs, ss, info->local ? m_program_local : m_program_world, info->vtx_array, info->count * 6, 0 );
525        }
526}
527
528nv::particle_system_group nv::particle_engine::create_group( uint32 max_particles )
529{
530        particle_system_group result     = m_groups.create();
531        particle_system_group_info* info = m_groups.get( result );
532        info->local = false;
533        info->count = 0;
534        info->quota = max_particles;
535        info->vtx_buffer = m_device->create_buffer( VERTEX_BUFFER, STREAM_DRAW, info->quota * sizeof( particle_quad )/*, info->quads_[0].data*/ );
536        vertex_array_desc desc;
537        desc.add_vertex_buffers< particle_vtx >( info->vtx_buffer, true );
538        info->vtx_array = m_context->create_vertex_array( desc );
539        info->quads     = new particle_quad[info->quota];
[500]540        info->ref_counter = 0;
[499]541        return result;
542}
543
[306]544nv::particle_engine::~particle_engine()
545{
[353]546        clear();
[306]547        m_device->release( m_program_world );
548        m_device->release( m_program_local );
549}
550
[353]551void nv::particle_engine::reset()
552{
553        clear();
554        register_standard_emmiters();
555        register_standard_affectors();
556}
557
[500]558bool nv::particle_engine::loaded( const string_view& system_id )
559{
560        return m_names.find( system_id ) != m_names.end();
561}
562
[353]563void nv::particle_engine::clear()
564{
565        while ( m_systems.size() > 0 )
566                release( m_systems.get_handle( 0 ) );
[499]567        while ( m_groups.size() > 0 )
568                release( m_groups.get_handle( 0 ) );
[353]569        m_emmiters.clear();
570        m_affectors.clear();
571        m_names.clear();
572        m_data.clear();
573}
574
575
[306]576void nv::particle_engine::release( particle_system system )
577{
578        particle_system_info* info = m_systems.get( system );
579        if ( info )
580        {
[500]581                particle_system_group_info* ginfo = m_groups.get( info->group );
582                if ( ginfo ) ginfo->ref_counter--;
583                                delete[] info->particles;
[499]584                m_systems.destroy( system );
585        }
586}
587
588void nv::particle_engine::release( particle_system_group group )
589{
590        particle_system_group_info* info = m_groups.get( group );
591        if ( info )
592        {
[306]593                delete[] info->quads;
[313]594                m_context->release( info->vtx_array );
[499]595                m_groups.destroy( group );
[306]596        }
597}
598
[500]599void nv::particle_engine::render( particle_system system, const scene_state& s )
[306]600{
[500]601        particle_system_info* info = m_systems.get( system );
602        if ( info )
603        {
604                generate_data( info, s );
605        }
[499]606}
607
[500]608void nv::particle_engine::update( particle_system system, float dtime )
[499]609{
[306]610        particle_system_info* info = m_systems.get( system );
611        if ( info )
612        {
[500]613//              while ( dtime > 0.2 )
614//              {
615//                      update_emmiters( info, 0.2 );
616//                      destroy_particles( info, 0.2 );
617//                      create_particles( info, 0.2 );
618//                      update_particles( info, 0.2 );
619//                      dtime -= 0.2;
620//              }
[306]621
[500]622                update_emmiters( info, dtime );
623                destroy_particles( info, dtime );
624                create_particles( info, dtime );
625                update_particles( info, dtime );
626
627//              generate_data( info );
[306]628        }
629}
630
631void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
632{
[500]633
[306]634        particle_system_info* info = m_systems.get( system );
635        if ( info )
636        {
[500]637                info->texcoords[0] = a;
638                info->texcoords[1] = b;
[306]639        }
640}
641
[500]642void nv::particle_engine::generate_data( particle_system_info* info, const scene_state& s )
[306]643{
[499]644//      void* rawptr = m_context->map_buffer( info->vtx_buffer, nv::WRITE_UNSYNCHRONIZED, offset, info->count*sizeof( particle_quad ) );
645//      particle_quad* quads = reinterpret_cast<particle_quad*>( rawptr );
646
647        particle_system_group_info* group = m_groups.get( info->group );
648        if ( !info )
649        {
650                return;
651        }
652
[309]653        vec2 lb     = vec2( -0.5f, -0.5f );
654        vec2 rt     = vec2( 0.5f, 0.5f );
[306]655
[309]656        switch ( info->data->origin )
657        {
658        case particle_origin::CENTER        : break;
659        case particle_origin::TOP_LEFT      : lb = vec2(0.f,-1.f); rt = vec2(1.f,0.f);  break;
[323]660        case particle_origin::TOP_CENTER    : lb.y = -1.f; rt.y = 0.f; break;
[309]661        case particle_origin::TOP_RIGHT     : lb = vec2(-1.f,-1.f); rt = vec2(); break;
662        case particle_origin::CENTER_LEFT   : lb.x = 0.f; rt.x = 1.f; break;
663        case particle_origin::CENTER_RIGHT  : lb.x = -1.f; rt.x = 0.f; break;
664        case particle_origin::BOTTOM_LEFT   : lb = vec2(); rt = vec2(1.f,1.f); break;
665        case particle_origin::BOTTOM_CENTER : lb.y = 0.f; rt.y = 1.f; break;
666        case particle_origin::BOTTOM_RIGHT  : lb = vec2(-1.f,0.f); rt = vec2(.0f,1.f); break;
667        }
668
669        const vec3 sm[4] =
670        {
671                vec3( lb.x, lb.y, 0.0f ),
672                vec3( rt.x, lb.y, 0.0f ),
673                vec3( lb.x, rt.y, 0.0f ),
674                vec3( rt.x, rt.y, 0.0f ),
[306]675        };
[309]676        vec3 z( 0.0f, 0.0f ,1.0f );
[306]677
678        particle_orientation orientation = info->data->orientation;
679        vec3 common_up ( info->data->common_up );
680        vec3 common_dir( info->data->common_dir );
681        bool accurate_facing = info->data->accurate_facing;
[312]682        mat3 rot_mat;
683        vec3 right;
684        vec3 pdir( 0.0f, 1.0f, 0.0f );
[306]685
[500]686        vec3 camera_pos   = s.get_camera().get_position();
687        vec3 inv_view_dir = math::normalize( -s.get_camera().get_direction() );
[499]688
[306]689        for ( uint32 i = 0; i < info->count; ++i )
690        {
691                const particle& pdata = info->particles[i];
[499]692//              particle_quad& rdata  = quads[i];
693                particle_quad& rdata  = group->quads[i + group->count];
[306]694
[500]695                vec3 view_dir( inv_view_dir );
696                if ( accurate_facing ) view_dir = math::normalize( camera_pos - pdata.position );
[306]697
698                switch ( orientation )
699                {
700                case particle_orientation::POINT :
[454]701                        right   = math::normalize( math::cross( view_dir, vec3( 0, 1, 0 ) ) );
[500]702                        right   = math::rotate( right, pdata.rotation, view_dir );
[454]703                        rot_mat = mat3( right, math::cross( right, -view_dir ), -view_dir );
[306]704                        break;
705                case particle_orientation::ORIENTED :
[312]706                        pdir    = normalize_safe( pdata.velocity, pdir );
[454]707                        right   = math::normalize( math::cross( pdir, view_dir ) );
708                        rot_mat = mat3( right, pdir, math::cross( pdir, right ) );
[306]709                        break;
710                case particle_orientation::ORIENTED_COMMON :
[454]711                        right   = math::normalize( math::cross( common_dir, view_dir ) );
712                        rot_mat = mat3( right, common_dir, math::cross( common_dir, right ) );
[306]713                        break;
714                case particle_orientation::PERPENDICULAR :
[312]715                        pdir    = normalize_safe( pdata.velocity, pdir );
[454]716                        right   = math::normalize( math::cross( common_up, pdir ) );
717                        rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
[306]718                        break;
719                case particle_orientation::PERPENDICULAR_COMMON :
[454]720                        right   = math::normalize( math::cross( common_up, common_dir ) );
721                        rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
[306]722                        break;
723                }
724
[500]725                vec2 texcoords[4] =
726                {
727                        pdata.tcoord_a,
728                        vec2( pdata.tcoord_b.x, pdata.tcoord_a.y ),
729                        vec2( pdata.tcoord_a.x, pdata.tcoord_b.y ),
730                        pdata.tcoord_b
731                };
732
[306]733                vec3 size( pdata.size.x, pdata.size.y, 0.0f );
734                vec3 s0 = rot_mat * ( ( size * sm[0] ) );
735                vec3 s1 = rot_mat * ( ( size * sm[1] ) );
736                vec3 s2 = rot_mat * ( ( size * sm[2] ) );
737                vec3 s3 = rot_mat * ( ( size * sm[3] ) );
738
739                rdata.data[0].position = pdata.position + s0;
740                rdata.data[0].color    = pdata.color;
[499]741                rdata.data[0].texcoord = texcoords[0];
[306]742
743                rdata.data[1].position = pdata.position + s1;
744                rdata.data[1].color    = pdata.color;
[499]745                rdata.data[1].texcoord = texcoords[1];
[306]746
747                rdata.data[2].position = pdata.position + s2;
748                rdata.data[2].color    = pdata.color;
[499]749                rdata.data[2].texcoord = texcoords[2];
[306]750
751                rdata.data[3].position = pdata.position + s3;
752                rdata.data[3].color    = pdata.color;
[499]753                rdata.data[3].texcoord = texcoords[3];
[306]754
755                rdata.data[4] = rdata.data[2];
756                rdata.data[5] = rdata.data[1];
757        }
[499]758//      m_context->unmap_buffer( info->vtx_buffer );
759
760        m_context->update( group->vtx_buffer, group->quads, group->count*sizeof(particle_quad), info->count*sizeof( particle_quad ) );
761        group->count += info->count;
[306]762}
763
[500]764void nv::particle_engine::destroy_particles( particle_system_info* info, float dtime )
[306]765{
766        if ( info->count > 0 )
[406]767                for ( sint32 i = sint32( info->count ) - 1; i >= 0; --i )
[306]768                {
769                        particle& pinfo = info->particles[i];
[500]770                        pinfo.lifetime += dtime;
771                        if ( pinfo.lifetime >= pinfo.death )
[306]772                        {
773                                info->count--;
[374]774                                swap( info->particles[i], info->particles[info->count] );
[306]775                        }
776                }
777}
778
[500]779void nv::particle_engine::create_particles( particle_system_info* info, float dtime )
[306]780{
781        uint32 ecount = info->data->emmiter_count;
782        if ( ecount == 0 ) return;
783
784        random& r = random::get();
785        vec3 source;
786        mat3 orient;
[312]787        bool local = info->data->local;
[500]788//      if ( !local )
789//      {
790//              source = vec3( m_model_matrix[3] );
791//              orient = mat3( m_model_matrix );
792//      }
[312]793
[306]794        for ( uint32 i = 0; i < ecount; ++i )
795        {
796                const auto& edata = info->data->emmiters[i];
797                auto& einfo = info->emmiters[i];
[307]798                if ( einfo.active )
[306]799                {
[500]800                        einfo.next -= dtime;
801                        float fperiod = 1.0f / edata.rate;
802                        while ( einfo.next < 0.0f )
[306]803                        {
[307]804                                if ( info->count < info->data->quota-1 )
805                                {
806                                        particle& pinfo = info->particles[info->count];
[312]807                                        edata.emmiter_func( &(info->data->emmiters[i]), &pinfo, 1 );
[500]808                                        pinfo.position = vec3();
809//                                      if ( !local ) pinfo.position  = orient * pinfo.position + source;
810                                        pinfo.position+= edata.position;
811                                        pinfo.color    = edata.color_min == edata.color_max ?
[307]812                                                edata.color_min : r.range( edata.color_min, edata.color_max );
[500]813                                        pinfo.size     = edata.size_min == edata.size_max ?
[307]814                                                edata.size_min : r.range( edata.size_min, edata.size_max );
815                                        if ( edata.square ) pinfo.size.y = pinfo.size.x;
[500]816                                        float velocity = edata.velocity_min == edata.velocity_max ?
[307]817                                                edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max );
[500]818                                        pinfo.lifetime = dtime + einfo.next;
819                                        pinfo.death = ( edata.lifetime_min == edata.lifetime_max ?
820                                                edata.lifetime_min : r.frange( edata.lifetime_min, edata.lifetime_max ) );
821                                        pinfo.rotation = r.frand( 2* math::pi<float>() );
822                                        pinfo.tcoord_a = info->texcoords[0];
823                                        pinfo.tcoord_b = info->texcoords[1];
[306]824
[312]825                                        pinfo.velocity = edata.dir;
[307]826                                        if ( edata.angle > 0.0f )
827                                        {
[453]828                                                float emission_angle = math::radians( edata.angle );
[471]829                                                float cos_theta = r.frange( cos( emission_angle ), 1.0f );
830                                                float sin_theta = sqrt(1.0f - cos_theta * cos_theta );
[451]831                                                float phi       = r.frange( 0.0f, 2* math::pi<float>() );
[312]832                                                pinfo.velocity  = orient *
[471]833                                                        ( edata.odir * ( cos(phi) * sin_theta ) +
834                                                        edata.cdir * ( sin(phi)*sin_theta ) +
[307]835                                                        edata.dir  * cos_theta );
836                                        }
837
[312]838                                        pinfo.velocity *= velocity;
839
[307]840                                        info->count++;
[306]841                                }
[500]842                                einfo.next += fperiod;
[306]843                        }
[500]844
[306]845                }
846        }
847}
848
[500]849void nv::particle_engine::update_particles( particle_system_info* info, float dtime )
[306]850{
[500]851        if ( dtime <= 0.0f ) return;
[312]852
853        uint32 acount = info->data->affector_count;
854        for ( uint32 i = 0; i < acount; ++i )
855        {
856                const particle_affector_data* padata = &(info->data->affectors[i]);
[500]857                padata->process( padata, info->particles, dtime, info->count );
[312]858        }
859
860
861        for ( uint32 i = 0; i < info->count; ++i )
862        {
863                particle& pdata = info->particles[i];
[500]864                float factor = min( dtime, pdata.lifetime );
[312]865                pdata.position += pdata.velocity * factor;
866        }
[307]867}
868
[500]869void nv::particle_engine::update_emmiters( particle_system_info* info, float dtime )
[307]870{
871        uint32 ecount = info->data->emmiter_count;
872        if ( ecount == 0 ) return;
873        random& r = random::get();
874
875        for ( uint32 i = 0; i < ecount; ++i )
876        {
877                const auto& edata = info->data->emmiters[i];
878                auto& einfo = info->emmiters[i];
879
[500]880                if ( einfo.pause > 0.0f )
[307]881                {
[500]882                        einfo.pause -= dtime;
883                        if ( einfo.pause == 0.0f ) einfo.pause = -0.001f;
884                }
885
886                if ( einfo.pause < 0.0f )
887                {
[307]888                        if ( einfo.active )
889                        {
890                                einfo.active = false;
[500]891                                if ( edata.repeat_min > 0.0f )
892                                        einfo.pause += r.frange( edata.repeat_min, edata.repeat_max );
[307]893                                else
[500]894                                        einfo.pause = 0.0f;
[307]895                        }
896                        else
897                        {
898                                einfo.active = true;
[500]899                                einfo.pause += r.frange( edata.duration_min, edata.duration_max );
[307]900                        }
901                }
902        }
903
904}
[312]905
[439]906void nv::particle_engine::register_emmiter_type( const string_view& name, particle_emmiter_func func )
[312]907{
908        m_emmiters[ name ] = func;
909}
910
911void nv::particle_engine::register_standard_emmiters()
912{
913        register_emmiter_type( "point",             nv_particle_emmiter_point );
914        register_emmiter_type( "box",               nv_particle_emmiter_box );
915        register_emmiter_type( "cylinder",          nv_particle_emmiter_cylinder );
916        register_emmiter_type( "sphere",            nv_particle_emmiter_sphere );
917        register_emmiter_type( "cylindroid",        nv_particle_emmiter_cylindroid );
918        register_emmiter_type( "ellipsoid",         nv_particle_emmiter_ellipsoid );
919        register_emmiter_type( "hollow_cylinder",   nv_particle_emmiter_hollow_cylinder );
920        register_emmiter_type( "hollow_sphere",     nv_particle_emmiter_hollow_sphere );
921        register_emmiter_type( "hollow_cylindroid", nv_particle_emmiter_hollow_cylindroid );
922        register_emmiter_type( "hollow_ellipsoid",  nv_particle_emmiter_hollow_ellipsoid );
923}
924
[439]925void nv::particle_engine::register_affector_type( const string_view& name, particle_affector_init_func init, particle_affector_func process )
[312]926{
927        m_affectors[ name ].init    = init;
928        m_affectors[ name ].process = process;
929}
930
[500]931nv::particle_render_data nv::particle_engine::get_render_data( particle_system_group group )
932{
933        const particle_system_group_info* info = m_groups.get( group );
934        if ( info )
935        {
936                return{ info->count, info->vtx_array };
937        }
938        return { 0, nv::vertex_array() };
939}
940
[312]941void nv::particle_engine::register_standard_affectors()
942{
943        register_affector_type( "linear_force",    nv_particle_affector_linear_force_init, nv_particle_affector_linear_force );
944        register_affector_type( "deflector_plane", nv_particle_affector_deflector_plane_init, nv_particle_affector_deflector_plane );
945        register_affector_type( "color_fader",     nv_particle_affector_color_fader_init, nv_particle_affector_color_fader );
946        register_affector_type( "scaler",          nv_particle_affector_scaler_init, nv_particle_affector_scaler );
947}
948
Note: See TracBrowser for help on using the repository browser.