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
Line 
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.
6
7#include "nv/engine/particle_engine.hh"
8
9#include <nv/interface/device.hh>
10#include <nv/core/random.hh>
11#include <nv/stl/utility.hh>
12#include <nv/lua/lua_math.hh>
13#include <nv/core/logging.hh>
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"
22        "varying vec3 v_position;\n"
23        "uniform mat4 nv_m_view;\n"
24        "uniform mat4 nv_m_projection;\n"
25        "void main(void)\n"
26        "{\n"
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"
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"
39        "varying vec3 v_position;\n"
40        "uniform mat4 nv_m_mvp;\n"
41        "void main(void)\n"
42        "{\n"
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"
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"
53        "varying vec3 v_position;\n"
54        "void main(void)\n"
55        "{\n"
56        "       vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord );\n"
57        "       float edge = smoothstep( 0.0, 0.1, v_position.y );\n"
58        "       gl_FragColor   = v_color * tex_color * edge;\n"
59        "}\n";
60
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{
202        nvpe_linear_force_data* datap = reinterpret_cast<nvpe_linear_force_data*>( data->paramters );
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{
210        const nvpe_linear_force_data* datap = reinterpret_cast<const nvpe_linear_force_data*>( data->paramters );
211        if ( datap->average )
212        {
213                float norm_factor = nv::min( factor, 1.0f, p->lifetime );
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        {
219                vec3 scvector = datap->force_vector * nv::min( factor, p->lifetime );
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{
234        nvpe_deflector_plane_data* datap = reinterpret_cast<nvpe_deflector_plane_data*>( data->paramters );
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 );
239        datap->distance     = -math::dot( datap->plane_normal, datap->plane_point ) / sqrt( math::dot( datap->plane_normal, datap->plane_normal ) );
240        return true;
241}
242
243static void nv_particle_affector_deflector_plane( const particle_affector_data* data, particle* p, float factor, uint32 count )
244{
245        const nvpe_deflector_plane_data* datap = reinterpret_cast<const nvpe_deflector_plane_data*>( data->paramters );
246        for ( uint32 i = 0; i < count; ++i )
247        {
248                particle& pt = p[i];
249                vec3 direction  = pt.velocity * nv::min( factor, p->lifetime );
250                if ( math::dot( datap->plane_normal, pt.position + direction ) + datap->distance <= 0.0f )
251                {
252                        float val = math::dot( datap->plane_normal, pt.position ) + datap->distance;
253                        if ( val > 0.0f )
254                        {
255                                vec3 part_dir = direction * ( -val / math::dot( datap->plane_normal, direction ) );
256                                pt.position = pt.position + part_dir + ( part_dir - direction ) * datap->bounce;
257                                pt.velocity = math::reflect( pt.velocity, datap->plane_normal ) * datap->bounce;
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{
270        nvpe_color_fader_data* datap = reinterpret_cast<nvpe_color_fader_data*>( data->paramters );
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{
277        const nvpe_color_fader_data* datap = reinterpret_cast<const nvpe_color_fader_data*>( data->paramters );
278        vec4 adjustment = datap->adjustment * nv::min( factor, p->lifetime );
279        for ( uint32 i = 0; i < count; ++i )
280        {
281                p[i].color = math::clamp( p[i].color + adjustment, 0.0f, 1.0f );
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{
292        nvpe_scaler_data* datap = reinterpret_cast<nvpe_scaler_data*>( data->paramters );
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{
300        const nvpe_scaler_data* datap = reinterpret_cast<const nvpe_scaler_data*>( data->paramters );
301        vec2 adjustment = datap->adjustment * nv::min( factor, p->lifetime );
302        for ( uint32 i = 0; i < count; ++i )
303        {
304                p[i].size = math::max( p[i].size + adjustment, vec2() );
305        }
306}
307
308void nv::particle_engine::load( lua::table_guard& table )
309{
310        shash64 id = table.get_string_hash_64( "id" );
311        if ( !id )
312        {
313                NV_LOG_ERROR( "Bad table passed to particle_engine!" )
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 );
322        data.local   = table.get<bool>("local_space", false );
323        data.accurate_facing = table.get<bool>("accurate_facing", false );
324        data.emmiter_count   = 0;
325        data.affector_count  = 0;
326
327        const_string orientation = table.get_string( "orientation", "point" );
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        {
335                NV_LOG_ERROR( "Unknown orientation type! (", orientation, ")!" );
336                data.orientation = particle_orientation::POINT;
337        }
338
339        const_string origin = table.get_string( "origin", "center" );
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        {
351                NV_LOG_ERROR( "Unknown particle origin! (", origin, ")!" );
352                data.origin = particle_origin::CENTER;
353        }
354
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) ) );
357
358        vec2 def_size        = table.get<vec2>("size", vec2(0.1,0.1) );
359        uint32 elements = table.get_size();
360        for ( uint32 i = 0; i < elements; ++i )
361        {
362                lua::table_guard element( table, i+1 );
363                const_string type     = element.get_string( "type" );
364                const_string sub_type = element.get_string( "sub_type" );
365                if ( type == "emmiter" )
366                {
367                        if ( data.emmiter_count < MAX_PARTICLE_EMMITERS )
368                        {
369                                particle_emmiter_data& edata = data.emmiters[ data.emmiter_count ];
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                                }
375                                else
376                                {
377                                        edata.emmiter_func = nv_particle_emmiter_point;
378                                        NV_LOG_WARNING( "Unknown emmiter type in particle system! (", sub_type, ")" );
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 );
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 );
399                                vec2 size          = element.get<vec2>("size", def_size );
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 );
407                                edata.lifetime_min = element.get<float>("lifetime_min", lifetime );
408                                edata.lifetime_max = element.get<float>("lifetime_max", lifetime );
409                                float duration     = element.get<float>("duration", 0.0f );
410                                edata.duration_min = element.get<float>("duration_min", duration );
411                                edata.duration_max = element.get<float>("duration_max", duration );
412                                float repeat       = element.get<float>("repeat_delay", 0.0f );
413                                edata.repeat_min   = element.get<float>("repeat_delay_min", repeat );
414                                edata.repeat_max   = element.get<float>("repeat_delay_max", repeat );
415
416                                edata.rate         = element.get<float>("rate", 1.0f );
417                                edata.dir          = math::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
418                               
419                                edata.odir = vec3( 0, 0, 1 );
420                                if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
421                                        edata.odir = math::normalize( math::cross( edata.dir, vec3( 0, 1, 0 ) ) );
422                                edata.cdir = math::cross( edata.dir, edata.odir );
423
424                                data.emmiter_count++;
425                        }
426                        else
427                        {
428                                NV_LOG_ERROR( "Too many emmiters (", MAX_PARTICLE_EMMITERS, " is MAX)!" );
429                        }
430                }
431                else if ( type == "affector" )
432                {
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--;
444                                                NV_LOG_WARNING( "Bad data passed to ", sub_type, " affector in particle system!" );
445                                        }
446                                }
447                                else
448                                {
449                                        data.affector_count--;
450                                        NV_LOG_WARNING( "Unknown affector type in particle system! (", sub_type, ")" );
451                                }
452                        }
453                        else
454                        {
455                                NV_LOG_ERROR( "Too many affectors (", MAX_PARTICLE_AFFECTORS, " is MAX)!" );
456                        }
457                }
458                else
459                {
460                        NV_LOG_WARNING( "Unknown element in particle system! (", type, ")" );
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 );
472
473        register_standard_emmiters();
474        register_standard_affectors();
475}
476
477nv::particle_system nv::particle_engine::create_system( const string_view& id, particle_system_group group )
478{
479        particle_system_group_info* ginfo = m_groups.get( group );
480        if ( !ginfo ) return nv::particle_system();
481
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 );
490        info->group = group;
491        info->data     = data;
492        uint32 ecount = data->emmiter_count;
493        for ( uint32 i = 0; i < ecount; ++i )
494        {
495                info->emmiters[i].active      = true;
496                if ( data->emmiters[i].duration_max == 0.0f )
497                        info->emmiters[i].pause = 0;
498                else
499                        info->emmiters[i].pause = random::get().frange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
500
501        }
502
503        info->count = 0;
504        info->particles = new particle[ data->quota ];
505        ginfo->ref_counter++;
506
507        return result;
508}
509
510void nv::particle_engine::prepare( particle_system_group group )
511{
512        particle_system_group_info* info = m_groups.get( group );
513        if ( info )
514        {
515                info->count = 0;
516        }
517}
518
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];
540        info->ref_counter = 0;
541        return result;
542}
543
544nv::particle_engine::~particle_engine()
545{
546        clear();
547        m_device->release( m_program_world );
548        m_device->release( m_program_local );
549}
550
551void nv::particle_engine::reset()
552{
553        clear();
554        register_standard_emmiters();
555        register_standard_affectors();
556}
557
558bool nv::particle_engine::loaded( const string_view& system_id )
559{
560        return m_names.find( system_id ) != m_names.end();
561}
562
563void nv::particle_engine::clear()
564{
565        while ( m_systems.size() > 0 )
566                release( m_systems.get_handle( 0 ) );
567        while ( m_groups.size() > 0 )
568                release( m_groups.get_handle( 0 ) );
569        m_emmiters.clear();
570        m_affectors.clear();
571        m_names.clear();
572        m_data.clear();
573}
574
575
576void nv::particle_engine::release( particle_system system )
577{
578        particle_system_info* info = m_systems.get( system );
579        if ( info )
580        {
581                particle_system_group_info* ginfo = m_groups.get( info->group );
582                if ( ginfo ) ginfo->ref_counter--;
583                                delete[] info->particles;
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        {
593                delete[] info->quads;
594                m_context->release( info->vtx_array );
595                m_groups.destroy( group );
596        }
597}
598
599void nv::particle_engine::render( particle_system system, const scene_state& s )
600{
601        particle_system_info* info = m_systems.get( system );
602        if ( info )
603        {
604                generate_data( info, s );
605        }
606}
607
608void nv::particle_engine::update( particle_system system, float dtime )
609{
610        particle_system_info* info = m_systems.get( system );
611        if ( info )
612        {
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//              }
621
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 );
628        }
629}
630
631void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
632{
633
634        particle_system_info* info = m_systems.get( system );
635        if ( info )
636        {
637                info->texcoords[0] = a;
638                info->texcoords[1] = b;
639        }
640}
641
642void nv::particle_engine::generate_data( particle_system_info* info, const scene_state& s )
643{
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
653        vec2 lb     = vec2( -0.5f, -0.5f );
654        vec2 rt     = vec2( 0.5f, 0.5f );
655
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;
660        case particle_origin::TOP_CENTER    : lb.y = -1.f; rt.y = 0.f; break;
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 ),
675        };
676        vec3 z( 0.0f, 0.0f ,1.0f );
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;
682        mat3 rot_mat;
683        vec3 right;
684        vec3 pdir( 0.0f, 1.0f, 0.0f );
685
686        vec3 camera_pos   = s.get_camera().get_position();
687        vec3 inv_view_dir = math::normalize( -s.get_camera().get_direction() );
688
689        for ( uint32 i = 0; i < info->count; ++i )
690        {
691                const particle& pdata = info->particles[i];
692//              particle_quad& rdata  = quads[i];
693                particle_quad& rdata  = group->quads[i + group->count];
694
695                vec3 view_dir( inv_view_dir );
696                if ( accurate_facing ) view_dir = math::normalize( camera_pos - pdata.position );
697
698                switch ( orientation )
699                {
700                case particle_orientation::POINT :
701                        right   = math::normalize( math::cross( view_dir, vec3( 0, 1, 0 ) ) );
702                        right   = math::rotate( right, pdata.rotation, view_dir );
703                        rot_mat = mat3( right, math::cross( right, -view_dir ), -view_dir );
704                        break;
705                case particle_orientation::ORIENTED :
706                        pdir    = normalize_safe( pdata.velocity, pdir );
707                        right   = math::normalize( math::cross( pdir, view_dir ) );
708                        rot_mat = mat3( right, pdir, math::cross( pdir, right ) );
709                        break;
710                case particle_orientation::ORIENTED_COMMON :
711                        right   = math::normalize( math::cross( common_dir, view_dir ) );
712                        rot_mat = mat3( right, common_dir, math::cross( common_dir, right ) );
713                        break;
714                case particle_orientation::PERPENDICULAR :
715                        pdir    = normalize_safe( pdata.velocity, pdir );
716                        right   = math::normalize( math::cross( common_up, pdir ) );
717                        rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
718                        break;
719                case particle_orientation::PERPENDICULAR_COMMON :
720                        right   = math::normalize( math::cross( common_up, common_dir ) );
721                        rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
722                        break;
723                }
724
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
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;
741                rdata.data[0].texcoord = texcoords[0];
742
743                rdata.data[1].position = pdata.position + s1;
744                rdata.data[1].color    = pdata.color;
745                rdata.data[1].texcoord = texcoords[1];
746
747                rdata.data[2].position = pdata.position + s2;
748                rdata.data[2].color    = pdata.color;
749                rdata.data[2].texcoord = texcoords[2];
750
751                rdata.data[3].position = pdata.position + s3;
752                rdata.data[3].color    = pdata.color;
753                rdata.data[3].texcoord = texcoords[3];
754
755                rdata.data[4] = rdata.data[2];
756                rdata.data[5] = rdata.data[1];
757        }
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;
762}
763
764void nv::particle_engine::destroy_particles( particle_system_info* info, float dtime )
765{
766        if ( info->count > 0 )
767                for ( sint32 i = sint32( info->count ) - 1; i >= 0; --i )
768                {
769                        particle& pinfo = info->particles[i];
770                        pinfo.lifetime += dtime;
771                        if ( pinfo.lifetime >= pinfo.death )
772                        {
773                                info->count--;
774                                swap( info->particles[i], info->particles[info->count] );
775                        }
776                }
777}
778
779void nv::particle_engine::create_particles( particle_system_info* info, float dtime )
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;
787        bool local = info->data->local;
788//      if ( !local )
789//      {
790//              source = vec3( m_model_matrix[3] );
791//              orient = mat3( m_model_matrix );
792//      }
793
794        for ( uint32 i = 0; i < ecount; ++i )
795        {
796                const auto& edata = info->data->emmiters[i];
797                auto& einfo = info->emmiters[i];
798                if ( einfo.active )
799                {
800                        einfo.next -= dtime;
801                        float fperiod = 1.0f / edata.rate;
802                        while ( einfo.next < 0.0f )
803                        {
804                                if ( info->count < info->data->quota-1 )
805                                {
806                                        particle& pinfo = info->particles[info->count];
807                                        edata.emmiter_func( &(info->data->emmiters[i]), &pinfo, 1 );
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 ?
812                                                edata.color_min : r.range( edata.color_min, edata.color_max );
813                                        pinfo.size     = edata.size_min == edata.size_max ?
814                                                edata.size_min : r.range( edata.size_min, edata.size_max );
815                                        if ( edata.square ) pinfo.size.y = pinfo.size.x;
816                                        float velocity = edata.velocity_min == edata.velocity_max ?
817                                                edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max );
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];
824
825                                        pinfo.velocity = edata.dir;
826                                        if ( edata.angle > 0.0f )
827                                        {
828                                                float emission_angle = math::radians( edata.angle );
829                                                float cos_theta = r.frange( cos( emission_angle ), 1.0f );
830                                                float sin_theta = sqrt(1.0f - cos_theta * cos_theta );
831                                                float phi       = r.frange( 0.0f, 2* math::pi<float>() );
832                                                pinfo.velocity  = orient *
833                                                        ( edata.odir * ( cos(phi) * sin_theta ) +
834                                                        edata.cdir * ( sin(phi)*sin_theta ) +
835                                                        edata.dir  * cos_theta );
836                                        }
837
838                                        pinfo.velocity *= velocity;
839
840                                        info->count++;
841                                }
842                                einfo.next += fperiod;
843                        }
844
845                }
846        }
847}
848
849void nv::particle_engine::update_particles( particle_system_info* info, float dtime )
850{
851        if ( dtime <= 0.0f ) return;
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]);
857                padata->process( padata, info->particles, dtime, info->count );
858        }
859
860
861        for ( uint32 i = 0; i < info->count; ++i )
862        {
863                particle& pdata = info->particles[i];
864                float factor = min( dtime, pdata.lifetime );
865                pdata.position += pdata.velocity * factor;
866        }
867}
868
869void nv::particle_engine::update_emmiters( particle_system_info* info, float dtime )
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
880                if ( einfo.pause > 0.0f )
881                {
882                        einfo.pause -= dtime;
883                        if ( einfo.pause == 0.0f ) einfo.pause = -0.001f;
884                }
885
886                if ( einfo.pause < 0.0f )
887                {
888                        if ( einfo.active )
889                        {
890                                einfo.active = false;
891                                if ( edata.repeat_min > 0.0f )
892                                        einfo.pause += r.frange( edata.repeat_min, edata.repeat_max );
893                                else
894                                        einfo.pause = 0.0f;
895                        }
896                        else
897                        {
898                                einfo.active = true;
899                                einfo.pause += r.frange( edata.duration_min, edata.duration_max );
900                        }
901                }
902        }
903
904}
905
906void nv::particle_engine::register_emmiter_type( const string_view& name, particle_emmiter_func func )
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
925void nv::particle_engine::register_affector_type( const string_view& name, particle_affector_init_func init, particle_affector_func process )
926{
927        m_affectors[ name ].init    = init;
928        m_affectors[ name ].process = process;
929}
930
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
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.