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

Last change on this file since 501 was 501, checked in by epyon, 9 years ago
  • particle engine updates
  • device/context redesign
  • caching of GL state - texture bindings and programs
  • camera view_perspective and view_perspective_inv
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 );
[501]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;
[501]469        m_program_local = m_context->create_program( nv_particle_engine_vertex_shader_local, nv_particle_engine_fragment_shader );
470        m_program_world = m_context->create_program( nv_particle_engine_vertex_shader_world, nv_particle_engine_fragment_shader );
[312]471
472        register_standard_emmiters();
473        register_standard_affectors();
[306]474}
475
[499]476nv::particle_system nv::particle_engine::create_system( const string_view& id, particle_system_group group )
[306]477{
[500]478        particle_system_group_info* ginfo = m_groups.get( group );
479        if ( !ginfo ) return nv::particle_system();
480
[306]481        auto it = m_names.find( id );
482        if ( it == m_names.end() )
483        {
484                return particle_system();
485        }
486        const particle_system_data* data = &(m_data[it->second]);
487        particle_system result = m_systems.create();
488        particle_system_info* info = m_systems.get( result );
[499]489        info->group = group;
[306]490        info->data     = data;
491        uint32 ecount = data->emmiter_count;
492        for ( uint32 i = 0; i < ecount; ++i )
493        {
[307]494                info->emmiters[i].active      = true;
[500]495                if ( data->emmiters[i].duration_max == 0.0f )
496                        info->emmiters[i].pause = 0;
[353]497                else
[500]498                        info->emmiters[i].pause = random::get().frange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
[353]499
[306]500        }
501
502        info->count = 0;
503        info->particles = new particle[ data->quota ];
[500]504        ginfo->ref_counter++;
[306]505
506        return result;
507}
508
[499]509void nv::particle_engine::prepare( particle_system_group group )
[306]510{
[499]511        particle_system_group_info* info = m_groups.get( group );
[306]512        if ( info )
513        {
[499]514                info->count = 0;
[306]515        }
516}
517
[499]518void nv::particle_engine::draw( particle_system_group group, const render_state& rs, const scene_state& ss )
519{
520        particle_system_group_info* info = m_groups.get( group );
521        if ( info )
522        {
523                m_context->draw( nv::TRIANGLES, rs, ss, info->local ? m_program_local : m_program_world, info->vtx_array, info->count * 6, 0 );
524        }
525}
526
527nv::particle_system_group nv::particle_engine::create_group( uint32 max_particles )
528{
529        particle_system_group result     = m_groups.create();
530        particle_system_group_info* info = m_groups.get( result );
531        info->local = false;
532        info->count = 0;
533        info->quota = max_particles;
[501]534        info->vtx_buffer = m_context->create_buffer( VERTEX_BUFFER, STREAM_DRAW, info->quota * sizeof( particle_quad )/*, info->quads_[0].data*/ );
[499]535        vertex_array_desc desc;
536        desc.add_vertex_buffers< particle_vtx >( info->vtx_buffer, true );
537        info->vtx_array = m_context->create_vertex_array( desc );
538        info->quads     = new particle_quad[info->quota];
[500]539        info->ref_counter = 0;
[499]540        return result;
541}
542
[306]543nv::particle_engine::~particle_engine()
544{
[353]545        clear();
[501]546        m_context->release( m_program_world );
547        m_context->release( m_program_local );
[306]548}
549
[353]550void nv::particle_engine::reset()
551{
552        clear();
553        register_standard_emmiters();
554        register_standard_affectors();
555}
556
[500]557bool nv::particle_engine::loaded( const string_view& system_id )
558{
559        return m_names.find( system_id ) != m_names.end();
560}
561
[353]562void nv::particle_engine::clear()
563{
564        while ( m_systems.size() > 0 )
565                release( m_systems.get_handle( 0 ) );
[499]566        while ( m_groups.size() > 0 )
567                release( m_groups.get_handle( 0 ) );
[353]568        m_emmiters.clear();
569        m_affectors.clear();
570        m_names.clear();
571        m_data.clear();
572}
573
574
[306]575void nv::particle_engine::release( particle_system system )
576{
577        particle_system_info* info = m_systems.get( system );
578        if ( info )
579        {
[500]580                particle_system_group_info* ginfo = m_groups.get( info->group );
581                if ( ginfo ) ginfo->ref_counter--;
582                                delete[] info->particles;
[499]583                m_systems.destroy( system );
584        }
585}
586
587void nv::particle_engine::release( particle_system_group group )
588{
589        particle_system_group_info* info = m_groups.get( group );
590        if ( info )
591        {
[306]592                delete[] info->quads;
[313]593                m_context->release( info->vtx_array );
[499]594                m_groups.destroy( group );
[306]595        }
596}
597
[500]598void nv::particle_engine::render( particle_system system, const scene_state& s )
[306]599{
[500]600        particle_system_info* info = m_systems.get( system );
601        if ( info )
602        {
603                generate_data( info, s );
604        }
[499]605}
606
[500]607void nv::particle_engine::update( particle_system system, float dtime )
[499]608{
[306]609        particle_system_info* info = m_systems.get( system );
610        if ( info )
611        {
[500]612//              while ( dtime > 0.2 )
613//              {
614//                      update_emmiters( info, 0.2 );
615//                      destroy_particles( info, 0.2 );
616//                      create_particles( info, 0.2 );
617//                      update_particles( info, 0.2 );
618//                      dtime -= 0.2;
619//              }
[306]620
[500]621                update_emmiters( info, dtime );
622                destroy_particles( info, dtime );
623                create_particles( info, dtime );
624                update_particles( info, dtime );
625
626//              generate_data( info );
[306]627        }
628}
629
630void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
631{
[500]632
[306]633        particle_system_info* info = m_systems.get( system );
634        if ( info )
635        {
[500]636                info->texcoords[0] = a;
637                info->texcoords[1] = b;
[306]638        }
639}
640
[500]641void nv::particle_engine::generate_data( particle_system_info* info, const scene_state& s )
[306]642{
[499]643//      void* rawptr = m_context->map_buffer( info->vtx_buffer, nv::WRITE_UNSYNCHRONIZED, offset, info->count*sizeof( particle_quad ) );
644//      particle_quad* quads = reinterpret_cast<particle_quad*>( rawptr );
645
646        particle_system_group_info* group = m_groups.get( info->group );
647        if ( !info )
648        {
649                return;
650        }
651
[309]652        vec2 lb     = vec2( -0.5f, -0.5f );
653        vec2 rt     = vec2( 0.5f, 0.5f );
[306]654
[309]655        switch ( info->data->origin )
656        {
657        case particle_origin::CENTER        : break;
658        case particle_origin::TOP_LEFT      : lb = vec2(0.f,-1.f); rt = vec2(1.f,0.f);  break;
[323]659        case particle_origin::TOP_CENTER    : lb.y = -1.f; rt.y = 0.f; break;
[309]660        case particle_origin::TOP_RIGHT     : lb = vec2(-1.f,-1.f); rt = vec2(); break;
661        case particle_origin::CENTER_LEFT   : lb.x = 0.f; rt.x = 1.f; break;
662        case particle_origin::CENTER_RIGHT  : lb.x = -1.f; rt.x = 0.f; break;
663        case particle_origin::BOTTOM_LEFT   : lb = vec2(); rt = vec2(1.f,1.f); break;
664        case particle_origin::BOTTOM_CENTER : lb.y = 0.f; rt.y = 1.f; break;
665        case particle_origin::BOTTOM_RIGHT  : lb = vec2(-1.f,0.f); rt = vec2(.0f,1.f); break;
666        }
667
668        const vec3 sm[4] =
669        {
670                vec3( lb.x, lb.y, 0.0f ),
671                vec3( rt.x, lb.y, 0.0f ),
672                vec3( lb.x, rt.y, 0.0f ),
673                vec3( rt.x, rt.y, 0.0f ),
[306]674        };
[309]675        vec3 z( 0.0f, 0.0f ,1.0f );
[306]676
677        particle_orientation orientation = info->data->orientation;
678        vec3 common_up ( info->data->common_up );
679        vec3 common_dir( info->data->common_dir );
680        bool accurate_facing = info->data->accurate_facing;
[312]681        mat3 rot_mat;
682        vec3 right;
683        vec3 pdir( 0.0f, 1.0f, 0.0f );
[306]684
[500]685        vec3 camera_pos   = s.get_camera().get_position();
686        vec3 inv_view_dir = math::normalize( -s.get_camera().get_direction() );
[499]687
[306]688        for ( uint32 i = 0; i < info->count; ++i )
689        {
690                const particle& pdata = info->particles[i];
[499]691//              particle_quad& rdata  = quads[i];
692                particle_quad& rdata  = group->quads[i + group->count];
[306]693
[500]694                vec3 view_dir( inv_view_dir );
695                if ( accurate_facing ) view_dir = math::normalize( camera_pos - pdata.position );
[306]696
697                switch ( orientation )
698                {
699                case particle_orientation::POINT :
[454]700                        right   = math::normalize( math::cross( view_dir, vec3( 0, 1, 0 ) ) );
[500]701                        right   = math::rotate( right, pdata.rotation, view_dir );
[454]702                        rot_mat = mat3( right, math::cross( right, -view_dir ), -view_dir );
[306]703                        break;
704                case particle_orientation::ORIENTED :
[312]705                        pdir    = normalize_safe( pdata.velocity, pdir );
[454]706                        right   = math::normalize( math::cross( pdir, view_dir ) );
707                        rot_mat = mat3( right, pdir, math::cross( pdir, right ) );
[306]708                        break;
709                case particle_orientation::ORIENTED_COMMON :
[454]710                        right   = math::normalize( math::cross( common_dir, view_dir ) );
711                        rot_mat = mat3( right, common_dir, math::cross( common_dir, right ) );
[306]712                        break;
713                case particle_orientation::PERPENDICULAR :
[312]714                        pdir    = normalize_safe( pdata.velocity, pdir );
[454]715                        right   = math::normalize( math::cross( common_up, pdir ) );
716                        rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
[306]717                        break;
718                case particle_orientation::PERPENDICULAR_COMMON :
[454]719                        right   = math::normalize( math::cross( common_up, common_dir ) );
720                        rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
[306]721                        break;
722                }
723
[500]724                vec2 texcoords[4] =
725                {
726                        pdata.tcoord_a,
727                        vec2( pdata.tcoord_b.x, pdata.tcoord_a.y ),
728                        vec2( pdata.tcoord_a.x, pdata.tcoord_b.y ),
729                        pdata.tcoord_b
730                };
731
[306]732                vec3 size( pdata.size.x, pdata.size.y, 0.0f );
733                vec3 s0 = rot_mat * ( ( size * sm[0] ) );
734                vec3 s1 = rot_mat * ( ( size * sm[1] ) );
735                vec3 s2 = rot_mat * ( ( size * sm[2] ) );
736                vec3 s3 = rot_mat * ( ( size * sm[3] ) );
737
738                rdata.data[0].position = pdata.position + s0;
739                rdata.data[0].color    = pdata.color;
[499]740                rdata.data[0].texcoord = texcoords[0];
[306]741
742                rdata.data[1].position = pdata.position + s1;
743                rdata.data[1].color    = pdata.color;
[499]744                rdata.data[1].texcoord = texcoords[1];
[306]745
746                rdata.data[2].position = pdata.position + s2;
747                rdata.data[2].color    = pdata.color;
[499]748                rdata.data[2].texcoord = texcoords[2];
[306]749
750                rdata.data[3].position = pdata.position + s3;
751                rdata.data[3].color    = pdata.color;
[499]752                rdata.data[3].texcoord = texcoords[3];
[306]753
754                rdata.data[4] = rdata.data[2];
755                rdata.data[5] = rdata.data[1];
756        }
[499]757//      m_context->unmap_buffer( info->vtx_buffer );
758
759        m_context->update( group->vtx_buffer, group->quads, group->count*sizeof(particle_quad), info->count*sizeof( particle_quad ) );
760        group->count += info->count;
[306]761}
762
[500]763void nv::particle_engine::destroy_particles( particle_system_info* info, float dtime )
[306]764{
765        if ( info->count > 0 )
[406]766                for ( sint32 i = sint32( info->count ) - 1; i >= 0; --i )
[306]767                {
768                        particle& pinfo = info->particles[i];
[500]769                        pinfo.lifetime += dtime;
770                        if ( pinfo.lifetime >= pinfo.death )
[306]771                        {
772                                info->count--;
[374]773                                swap( info->particles[i], info->particles[info->count] );
[306]774                        }
775                }
776}
777
[500]778void nv::particle_engine::create_particles( particle_system_info* info, float dtime )
[306]779{
780        uint32 ecount = info->data->emmiter_count;
781        if ( ecount == 0 ) return;
782
783        random& r = random::get();
784        vec3 source;
785        mat3 orient;
[501]786//      bool local = info->data->local;
[500]787//      if ( !local )
788//      {
789//              source = vec3( m_model_matrix[3] );
790//              orient = mat3( m_model_matrix );
791//      }
[312]792
[306]793        for ( uint32 i = 0; i < ecount; ++i )
794        {
795                const auto& edata = info->data->emmiters[i];
796                auto& einfo = info->emmiters[i];
[307]797                if ( einfo.active )
[306]798                {
[500]799                        einfo.next -= dtime;
800                        float fperiod = 1.0f / edata.rate;
801                        while ( einfo.next < 0.0f )
[306]802                        {
[307]803                                if ( info->count < info->data->quota-1 )
804                                {
805                                        particle& pinfo = info->particles[info->count];
[312]806                                        edata.emmiter_func( &(info->data->emmiters[i]), &pinfo, 1 );
[500]807                                        pinfo.position = vec3();
808//                                      if ( !local ) pinfo.position  = orient * pinfo.position + source;
809                                        pinfo.position+= edata.position;
810                                        pinfo.color    = edata.color_min == edata.color_max ?
[307]811                                                edata.color_min : r.range( edata.color_min, edata.color_max );
[500]812                                        pinfo.size     = edata.size_min == edata.size_max ?
[307]813                                                edata.size_min : r.range( edata.size_min, edata.size_max );
814                                        if ( edata.square ) pinfo.size.y = pinfo.size.x;
[500]815                                        float velocity = edata.velocity_min == edata.velocity_max ?
[307]816                                                edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max );
[500]817                                        pinfo.lifetime = dtime + einfo.next;
818                                        pinfo.death = ( edata.lifetime_min == edata.lifetime_max ?
819                                                edata.lifetime_min : r.frange( edata.lifetime_min, edata.lifetime_max ) );
820                                        pinfo.rotation = r.frand( 2* math::pi<float>() );
821                                        pinfo.tcoord_a = info->texcoords[0];
822                                        pinfo.tcoord_b = info->texcoords[1];
[306]823
[312]824                                        pinfo.velocity = edata.dir;
[307]825                                        if ( edata.angle > 0.0f )
826                                        {
[453]827                                                float emission_angle = math::radians( edata.angle );
[471]828                                                float cos_theta = r.frange( cos( emission_angle ), 1.0f );
829                                                float sin_theta = sqrt(1.0f - cos_theta * cos_theta );
[451]830                                                float phi       = r.frange( 0.0f, 2* math::pi<float>() );
[312]831                                                pinfo.velocity  = orient *
[471]832                                                        ( edata.odir * ( cos(phi) * sin_theta ) +
833                                                        edata.cdir * ( sin(phi)*sin_theta ) +
[307]834                                                        edata.dir  * cos_theta );
835                                        }
836
[312]837                                        pinfo.velocity *= velocity;
838
[307]839                                        info->count++;
[306]840                                }
[500]841                                einfo.next += fperiod;
[306]842                        }
[500]843
[306]844                }
845        }
846}
847
[500]848void nv::particle_engine::update_particles( particle_system_info* info, float dtime )
[306]849{
[500]850        if ( dtime <= 0.0f ) return;
[312]851
852        uint32 acount = info->data->affector_count;
853        for ( uint32 i = 0; i < acount; ++i )
854        {
855                const particle_affector_data* padata = &(info->data->affectors[i]);
[500]856                padata->process( padata, info->particles, dtime, info->count );
[312]857        }
858
859
860        for ( uint32 i = 0; i < info->count; ++i )
861        {
862                particle& pdata = info->particles[i];
[500]863                float factor = min( dtime, pdata.lifetime );
[312]864                pdata.position += pdata.velocity * factor;
865        }
[307]866}
867
[500]868void nv::particle_engine::update_emmiters( particle_system_info* info, float dtime )
[307]869{
870        uint32 ecount = info->data->emmiter_count;
871        if ( ecount == 0 ) return;
872        random& r = random::get();
873
874        for ( uint32 i = 0; i < ecount; ++i )
875        {
876                const auto& edata = info->data->emmiters[i];
877                auto& einfo = info->emmiters[i];
878
[500]879                if ( einfo.pause > 0.0f )
[307]880                {
[500]881                        einfo.pause -= dtime;
882                        if ( einfo.pause == 0.0f ) einfo.pause = -0.001f;
883                }
884
885                if ( einfo.pause < 0.0f )
886                {
[307]887                        if ( einfo.active )
888                        {
889                                einfo.active = false;
[500]890                                if ( edata.repeat_min > 0.0f )
891                                        einfo.pause += r.frange( edata.repeat_min, edata.repeat_max );
[307]892                                else
[500]893                                        einfo.pause = 0.0f;
[307]894                        }
895                        else
896                        {
897                                einfo.active = true;
[500]898                                einfo.pause += r.frange( edata.duration_min, edata.duration_max );
[307]899                        }
900                }
901        }
902
903}
[312]904
[439]905void nv::particle_engine::register_emmiter_type( const string_view& name, particle_emmiter_func func )
[312]906{
907        m_emmiters[ name ] = func;
908}
909
910void nv::particle_engine::register_standard_emmiters()
911{
912        register_emmiter_type( "point",             nv_particle_emmiter_point );
913        register_emmiter_type( "box",               nv_particle_emmiter_box );
914        register_emmiter_type( "cylinder",          nv_particle_emmiter_cylinder );
915        register_emmiter_type( "sphere",            nv_particle_emmiter_sphere );
916        register_emmiter_type( "cylindroid",        nv_particle_emmiter_cylindroid );
917        register_emmiter_type( "ellipsoid",         nv_particle_emmiter_ellipsoid );
918        register_emmiter_type( "hollow_cylinder",   nv_particle_emmiter_hollow_cylinder );
919        register_emmiter_type( "hollow_sphere",     nv_particle_emmiter_hollow_sphere );
920        register_emmiter_type( "hollow_cylindroid", nv_particle_emmiter_hollow_cylindroid );
921        register_emmiter_type( "hollow_ellipsoid",  nv_particle_emmiter_hollow_ellipsoid );
922}
923
[439]924void nv::particle_engine::register_affector_type( const string_view& name, particle_affector_init_func init, particle_affector_func process )
[312]925{
926        m_affectors[ name ].init    = init;
927        m_affectors[ name ].process = process;
928}
929
[500]930nv::particle_render_data nv::particle_engine::get_render_data( particle_system_group group )
931{
932        const particle_system_group_info* info = m_groups.get( group );
933        if ( info )
934        {
935                return{ info->count, info->vtx_array };
936        }
937        return { 0, nv::vertex_array() };
938}
939
[312]940void nv::particle_engine::register_standard_affectors()
941{
942        register_affector_type( "linear_force",    nv_particle_affector_linear_force_init, nv_particle_affector_linear_force );
943        register_affector_type( "deflector_plane", nv_particle_affector_deflector_plane_init, nv_particle_affector_deflector_plane );
944        register_affector_type( "color_fader",     nv_particle_affector_color_fader_init, nv_particle_affector_color_fader );
945        register_affector_type( "scaler",          nv_particle_affector_scaler_init, nv_particle_affector_scaler );
946}
947
Note: See TracBrowser for help on using the repository browser.