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

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