source: trunk/src/gfx/particle_engine.cc @ 307

Last change on this file since 307 was 307, checked in by epyon, 11 years ago
  • particle_system - duration (duration_min/duration_max) added to emmiter options
  • particle_system - repeat_delay (repeat_delay_min/repeat_delay_max) added to emmiter options
File size: 15.4 KB
RevLine 
[306]1#include "nv/gfx/particle_engine.hh"
2
3#include <nv/interface/device.hh>
4#include <nv/random.hh>
5#include <nv/lua/lua_glm.hh>
6#include <nv/logging.hh>
7
8static const char *nv_particle_engine_vertex_shader_world =
9        "#version 120\n"
10        "attribute vec3 nv_position;\n"
11        "attribute vec2 nv_texcoord;\n"
12        "attribute vec4 nv_color;\n"
13        "varying vec4 v_color;\n"
14        "varying vec2 v_texcoord;\n"
15        "uniform mat4 nv_m_view;\n"
16        "uniform mat4 nv_m_projection;\n"
17        "void main(void)\n"
18        "{\n"
19        "       gl_Position = nv_m_projection * nv_m_view * vec4 (nv_position, 1.0);\n"
20        "       v_texcoord  = nv_texcoord;\n"
21        "       v_color     = nv_color;\n"
22        "}\n";
23static const char *nv_particle_engine_vertex_shader_local =
24        "#version 120\n"
25        "attribute vec3 nv_position;\n"
26        "attribute vec2 nv_texcoord;\n"
27        "attribute vec4 nv_color;\n"
28        "varying vec4 v_color;\n"
29        "varying vec2 v_texcoord;\n"
30        "uniform mat4 nv_m_mvp;\n"
31        "void main(void)\n"
32        "{\n"
33        "       gl_Position = nv_m_mvp * vec4 (nv_position, 1.0);\n"
34        "       v_texcoord  = nv_texcoord;\n"
35        "       v_color     = nv_color;\n"
36        "}\n";
37static const char *nv_particle_engine_fragment_shader =
38        "#version 120\n"
39        "uniform sampler2D nv_t_diffuse;\n"
40        "varying vec4 v_color;\n"
41        "varying vec2 v_texcoord;\n"
42        "void main(void)\n"
43        "{\n"
44        "       vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord );\n"
45        "       gl_FragColor   = v_color * tex_color;\n"
46        "}\n";
47
48void nv::particle_engine::load( lua::table_guard& table )
49{
50        std::string id = table.get_string( "id" );
51        if ( id == "" )
52        {
53                NV_LOG( LOG_ERROR, "Bad table passed to particle_engine!" )
54        }
55        // TODO : overwrite check
56        m_names[ id ] = m_data.size();
57
58        m_data.emplace_back();
59        auto& data = m_data.back();
60
61        data.gravity = table.get<vec3>("gravity", vec3() );
62        data.quota   = table.get<uint32>("quota", 1024 );
63        data.local   = table.get<bool>("local", false );
64        data.accurate_facing = table.get<bool>("accurate_facing", false );
65        data.emmiter_count   = 0;
66
67        std::string orientation = table.get_string( "orientation", "point" );
68        if ( orientation == "point" )                     { data.orientation = particle_orientation::POINT; }
69        else if ( orientation == "oriented" )             { data.orientation = particle_orientation::ORIENTED; }
70        else if ( orientation == "oriented_common" )      { data.orientation = particle_orientation::ORIENTED_COMMON; }
71        else if ( orientation == "perpendicular" )        { data.orientation = particle_orientation::PERPENDICULAR; }
72        else if ( orientation == "perpendicular_common" ) { data.orientation = particle_orientation::PERPENDICULAR_COMMON; }
73        else
74        {
75                NV_LOG( LOG_ERROR, "Unknown orientation type! (" << orientation << " is MAX)!" );
76                data.orientation = particle_orientation::POINT;
77        }
78        data.common_up  = glm::normalize( table.get<vec3>("common_up",  vec3(1,0,0) ) );
79        data.common_dir = glm::normalize( table.get<vec3>("common_dir", vec3(0,1,0) ) );
80
81        uint32 elements = table.get_size();
82        for ( uint32 i = 0; i < elements; ++i )
83        {
84                lua::table_guard element( table, i+1 );
85                std::string type  = element.get_string("type");
86                std::string etype = element.get_string("etype");
87                if ( type == "emmiter" )
88                {
89                        if ( data.emmiter_count < MAX_PARTICLE_EMMITERS )
90                        {
91                                particle_emmiter_data& edata = data.emmiters[ data.emmiter_count ];
92                                vec4 color         = element.get<vec4>("color", vec4(1,1,1,1) );
93                                edata.color_min    = element.get<vec4>("color_min", color );
94                                edata.color_max    = element.get<vec4>("color_max", color );
95                                vec2 size          = element.get<vec2>("size", vec2(0.1,0.1) );
96                                edata.size_min     = element.get<vec2>("size_min", size );
97                                edata.size_max     = element.get<vec2>("size_max", size );
98                                edata.square       = element.get<bool>("square", true );
99                                edata.angle        = element.get<float>("angle", 0.0f );
100                                float velocity     = element.get<float>("velocity", 0.0f );
101                                edata.velocity_min = element.get<float>("velocity_min", velocity );
102                                edata.velocity_max = element.get<float>("velocity_max", velocity );
103                                float lifetime     = element.get<float>("lifetime", 1.0f );
104                                edata.lifetime_min = uint32( element.get<float>("lifetime_min", lifetime ) * 1000.f );
105                                edata.lifetime_max = uint32( element.get<float>("lifetime_max", lifetime ) * 1000.f );
[307]106                                float duration     = element.get<float>("duration", 0.0f );
107                                edata.duration_min = uint32( element.get<float>("duration_min", duration ) * 1000.f );
108                                edata.duration_max = uint32( element.get<float>("duration_max", duration ) * 1000.f );
109                                float repeat       = element.get<float>("repeat_delay", 0.0f );
110                                edata.repeat_min   = uint32( element.get<float>("repeat_delay_min", repeat ) * 1000.f );
111                                edata.repeat_max   = uint32( element.get<float>("repeat_delay_max", repeat ) * 1000.f );
112
[306]113                                edata.rate         = element.get<float>("rate", 1.0f );
114                                edata.dir          = glm::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
115                               
116                                edata.odir = glm::vec3( 0, 0, 1 );
117                                if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
118                                        edata.odir = glm::normalize( glm::cross( edata.dir, vec3( 0, 1, 0 ) ) );                edata.cdir = glm::cross( edata.dir, edata.odir );
119
120                                data.emmiter_count++;
121                        }
122                        else
123                        {
124                                NV_LOG( LOG_ERROR, "Too many emmiters (" << MAX_PARTICLE_EMMITERS << " is MAX)!" );
125                        }
126                }
127                else if ( type == "affector" )
128                {
129
130                }
131                else
132                {
133                        NV_LOG( LOG_WARNING, "Unknown element in particle system! (" << type << ")" );
134                }
135        }
136
137}
138
139nv::particle_engine::particle_engine( context* a_context )
140{
141        m_context       = a_context;
142        m_device        = a_context->get_device();
143        m_program_local = m_device->create_program( nv_particle_engine_vertex_shader_local, nv_particle_engine_fragment_shader );
144        m_program_world = m_device->create_program( nv_particle_engine_vertex_shader_world, nv_particle_engine_fragment_shader );
145}
146
147nv::particle_system nv::particle_engine::create_system( const std::string& id )
148{
149        auto it = m_names.find( id );
150        if ( it == m_names.end() )
151        {
152                return particle_system();
153        }
154        const particle_system_data* data = &(m_data[it->second]);
155        particle_system result = m_systems.create();
156        particle_system_info* info = m_systems.get( result );
157
158        info->data     = data;
159        uint32 ecount = data->emmiter_count;
160        for ( uint32 i = 0; i < ecount; ++i )
161        {
[307]162                info->emmiters[i].active      = true;
[306]163                info->emmiters[i].last_create = 0;
[307]164                info->emmiters[i].next_toggle = random::get().urange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
[306]165        }
166
167        info->count = 0;
168        info->particles = new particle[ data->quota ];
169        info->quads     = new particle_quad[ data->quota ];
170        info->vtx_array = m_device->create_vertex_array<particle_vtx>(
171                (particle_vtx*)info->quads, data->quota*6, STREAM_DRAW );
172        info->vtx_buffer = m_device->find_buffer( info->vtx_array, slot::POSITION );
173        info->last_update = 0;
174        info->test = false;
175//      result->m_own_va      = true;
176//      result->m_offset      = 0;
177
178        return result;
179}
180
181void nv::particle_engine::draw( particle_system system, const render_state& rs, const scene_state& ss )
182{
183        particle_system_info* info = m_systems.get( system );
184        if ( info )
185        {
186                m_context->draw( nv::TRIANGLES, rs, ss, info->data->local ?  m_program_local : m_program_world, info->vtx_array, info->count * 6 );
187        }
188}
189
190nv::particle_engine::~particle_engine()
191{
192        m_device->release( m_program_world );
193        m_device->release( m_program_local );
194}
195
196void nv::particle_engine::release( particle_system system )
197{
198        particle_system_info* info = m_systems.get( system );
199        if ( info )
200        {
201                delete[] info->particles;
202                delete[] info->quads;
203                //if ( system->own_va )
204                m_device->release( info->vtx_array );
205                m_systems.destroy( system );
206        }
207}
208
209void nv::particle_engine::update( particle_system system, const scene_state& s, uint32 ms )
210{
211        particle_system_info* info = m_systems.get( system );
212        if ( info )
213        {
214                m_view_matrix  = s.get_view();
215                m_model_matrix = s.get_model();
216                m_camera_pos   = s.get_camera().get_position();
217                m_inv_view_dir = glm::normalize(-s.get_camera().get_direction());
218
[307]219                update_emmiters( info, ms );
[306]220                destroy_particles( info, ms );
221                create_particles( info, ms );
222                update_particles( info, ms );
223
224                generate_data( info );
225                m_context->update( info->vtx_buffer, info->quads, /*system->m_offset*sizeof(particle_quad)*/ 0, info->count*sizeof(particle_quad) );
226        }
227}
228
229void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
230{
231        particle_system_info* info = m_systems.get( system );
232        if ( info )
233        {
234                vec2 texcoords[4] = { a, vec2( b.x, a.y ), vec2( a.x, b.y ), b };
235
236                for ( uint32 i = 0; i < info->data->quota; ++i )
237                {
238                        particle_quad& rdata   = info->quads[i];
239                        rdata.data[0].texcoord = texcoords[0];
240                        rdata.data[1].texcoord = texcoords[1];
241                        rdata.data[2].texcoord = texcoords[2];
242                        rdata.data[3].texcoord = texcoords[3];
243                        rdata.data[4].texcoord = texcoords[2];
244                        rdata.data[5].texcoord = texcoords[1];
245                }
246        }
247}
248
249void nv::particle_engine::generate_data( particle_system_info* info )
250{
251        const vec3 x( 0.5f, 0.0f, 0.0f );
252        const vec3 y( 0.0f, 0.5f, 0.0f );
253        const vec3 z( 0.0f, 0.0f ,1.0f );
254
255        const vec3 sm[4] = {
256                vec3( -x-y ),
257                vec3(  x-y ),
258                vec3( -x+y ),
259                vec3(  x+y )
260        };
261
262        mat3 rot_mat;
263        vec3 right;
264        mat3 irot_mat = glm::transpose( mat3( m_view_matrix ) );
265        particle_orientation orientation = info->data->orientation;
266        vec3 common_up ( info->data->common_up );
267        vec3 common_dir( info->data->common_dir );
268        bool accurate_facing = info->data->accurate_facing;
269
270
271        for ( uint32 i = 0; i < info->count; ++i )
272        {
273                const particle& pdata = info->particles[i];
274                particle_quad& rdata  = info->quads[i];
275
276                vec3 view_dir( m_inv_view_dir );
277                if ( accurate_facing ) view_dir = glm::normalize( m_camera_pos - pdata.position );
278
279                switch ( orientation )
280                {
281                case particle_orientation::POINT :
282                        right   = glm::normalize( glm::cross( view_dir, vec3( 0, 1, 0 ) ) );
283                        rot_mat = mat3( right, glm::cross( right, -view_dir ), -view_dir );
284//                      rot_mat = irot_mat * glm::mat3_cast( glm::angleAxis( pdata.rotation, z ) );
285                        break;
286                case particle_orientation::ORIENTED :
287                        right   = glm::normalize( glm::cross( pdata.dir, view_dir ) );
288                        rot_mat = mat3( right, pdata.dir, glm::cross( pdata.dir, right ) );
289                        break;
290                case particle_orientation::ORIENTED_COMMON :
291                        right   = glm::normalize( glm::cross( common_dir, view_dir ) );
292                        rot_mat = mat3( right, common_dir, glm::cross( common_dir, right ) );
293                        break;
294                case particle_orientation::PERPENDICULAR :
295                        right   = glm::normalize( glm::cross( common_up, pdata.dir ) );
296                        rot_mat = mat3( right, common_up, glm::cross( common_up, right ) );
297                        break;
298                case particle_orientation::PERPENDICULAR_COMMON :
299                        right   = glm::normalize( glm::cross( common_up, common_dir ) );
300                        rot_mat = mat3( right, common_up, glm::cross( common_up, right ) );
301                        break;
302                }
303
304                vec3 size( pdata.size.x, pdata.size.y, 0.0f );
305                vec3 s0 = rot_mat * ( ( size * sm[0] ) );
306                vec3 s1 = rot_mat * ( ( size * sm[1] ) );
307                vec3 s2 = rot_mat * ( ( size * sm[2] ) );
308                vec3 s3 = rot_mat * ( ( size * sm[3] ) );
309
310                rdata.data[0].position = pdata.position + s0;
311                rdata.data[0].color    = pdata.color;
312
313                rdata.data[1].position = pdata.position + s1;
314                rdata.data[1].color    = pdata.color;
315
316                rdata.data[2].position = pdata.position + s2;
317                rdata.data[2].color    = pdata.color;
318
319                rdata.data[3].position = pdata.position + s3;
320                rdata.data[3].color    = pdata.color;
321
322                rdata.data[4] = rdata.data[2];
323                rdata.data[5] = rdata.data[1];
324        }
325}
326
327void nv::particle_engine::destroy_particles( particle_system_info* info, uint32 ms )
328{
329        if ( info->count > 0 )
330                for ( sint32 i = info->count-1; i >= 0; --i )
331                {
332                        particle& pinfo = info->particles[i];
333                        if ( //pdata.position.y < 0.0f ||
334                                ms >= pinfo.death )
335                        {
336                                info->count--;
337                                std::swap( info->particles[i], info->particles[info->count] );
338                        }
339                }
340}
341
342void nv::particle_engine::create_particles( particle_system_info* info, uint32 ms )
343{
344        uint32 ecount = info->data->emmiter_count;
345        if ( ecount == 0 ) return;
346
347        random& r = random::get();
348        vec3 source;
349        mat3 orient;
350        if ( !info->data->local )
351        {
352                source = vec3( m_model_matrix[3] );
353                orient = mat3( m_model_matrix );
354        }
355
356        for ( uint32 i = 0; i < ecount; ++i )
357        {
358                const auto& edata = info->data->emmiters[i];
359                auto& einfo = info->emmiters[i];
[307]360                if ( einfo.active )
[306]361                {
[307]362                        uint32 period = glm::max<uint32>( uint32(1000.f / edata.rate), 1 );
363                        while ( ms - einfo.last_create > period )
[306]364                        {
[307]365                                if ( info->count < info->data->quota-1 )
366                                {
367                                        particle& pinfo = info->particles[info->count];
368                                        pinfo.position = source;
369                                        pinfo.color    = edata.color_min == edata.color_max ?
370                                                edata.color_min : r.range( edata.color_min, edata.color_max );
371                                        pinfo.size     = edata.size_min == edata.size_max ?
372                                                edata.size_min : r.range( edata.size_min, edata.size_max );
373                                        if ( edata.square ) pinfo.size.y = pinfo.size.x;
374                                        pinfo.velocity  = edata.velocity_min == edata.velocity_max ?
375                                                edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max );
376                                        pinfo.death     = ms + ( edata.lifetime_min == edata.lifetime_max ?
377                                                edata.lifetime_min : r.urange( edata.lifetime_min, edata.lifetime_max ) );
378                                        //pinfo.rotation = r.frand( 360.0f );
[306]379
[307]380                                        pinfo.dir = edata.dir;
381                                        if ( edata.angle > 0.0f )
382                                        {
383                                                float emission_angle = glm::radians( edata.angle );
384                                                float cos_theta = r.frange( cos( emission_angle ), 1.0f );
385                                                float sin_theta = glm::sqrt(1.0f - cos_theta * cos_theta );
386                                                float phi       = r.frange( 0.0f, 2*glm::pi<float>() );
387                                                pinfo.dir = orient *
388                                                        ( edata.odir * ( glm::cos(phi) * sin_theta ) +
389                                                        edata.cdir * ( glm::sin(phi)*sin_theta ) +
390                                                        edata.dir  * cos_theta );
391                                        }
392
393                                        info->count++;
[306]394                                }
[307]395                                einfo.last_create += period;
[306]396                        }
397                }
398        }
399}
400
401void nv::particle_engine::update_particles( particle_system_info* system, uint32 ms )
402{
403        uint32 ticks = ms - system->last_update;
404        if ( ticks > 0 )
405                for ( uint32 i = 0; i < system->count; ++i )
406                {
407                        particle& pdata = system->particles[i];
408                        vec3 velocity_vec = pdata.dir * pdata.velocity;
409                        pdata.position += velocity_vec * ( 0.001f * ticks );
410                        velocity_vec   += system->data->gravity * ( 0.001f * ticks );
411                        pdata.velocity  = glm::length( velocity_vec );
412                        if ( pdata.velocity > 0.0f ) pdata.dir       = glm::normalize( velocity_vec );
413                        if ( pdata.position.y < 0.0f )
414                        {
415                                if ( pdata.velocity > 1.0f )
416                                {
417                                        pdata.position.y = -pdata.position.y;
418                                        pdata.velocity   = 0.5f*pdata.velocity;
419                                        pdata.dir.y      = -pdata.dir.y;
420                                }
421                                else
422                                {
423                                        pdata.position.y = 0.0f;
424                                        pdata.velocity   = 0.0f;
425                                }
426                        }
427                }
428                system->last_update = ms;
[307]429}
430
431void nv::particle_engine::update_emmiters( particle_system_info* info, uint32 ms )
432{
433        uint32 ecount = info->data->emmiter_count;
434        if ( ecount == 0 ) return;
435        random& r = random::get();
436
437        for ( uint32 i = 0; i < ecount; ++i )
438        {
439                const auto& edata = info->data->emmiters[i];
440                auto& einfo = info->emmiters[i];
441
442                if ( einfo.next_toggle != 0 && ms >= einfo.next_toggle )
443                {
444                        if ( einfo.active )
445                        {
446                                einfo.active = false;
447                                if ( edata.repeat_min > 0 )
448                                        einfo.next_toggle += r.urange( edata.repeat_min, edata.repeat_max );
449                                else
450                                        einfo.next_toggle = 0;
451                        }
452                        else
453                        {
454                                einfo.active = true;
455                                einfo.last_create = einfo.next_toggle;
456                                einfo.next_toggle += r.urange( edata.duration_min, edata.duration_max );
457                        }
458                }
459        }
460
461}
Note: See TracBrowser for help on using the repository browser.