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

Last change on this file since 306 was 306, checked in by epyon, 11 years ago
  • particle_engine module (WIP, but very robust already)
File size: 13.9 KB
Line 
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 );
106                                edata.rate         = element.get<float>("rate", 1.0f );
107                                edata.dir          = glm::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
108                               
109                                edata.odir = glm::vec3( 0, 0, 1 );
110                                if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
111                                        edata.odir = glm::normalize( glm::cross( edata.dir, vec3( 0, 1, 0 ) ) );                edata.cdir = glm::cross( edata.dir, edata.odir );
112
113                                data.emmiter_count++;
114                        }
115                        else
116                        {
117                                NV_LOG( LOG_ERROR, "Too many emmiters (" << MAX_PARTICLE_EMMITERS << " is MAX)!" );
118                        }
119                }
120                else if ( type == "affector" )
121                {
122
123                }
124                else
125                {
126                        NV_LOG( LOG_WARNING, "Unknown element in particle system! (" << type << ")" );
127                }
128        }
129
130}
131
132nv::particle_engine::particle_engine( context* a_context )
133{
134        m_context       = a_context;
135        m_device        = a_context->get_device();
136        m_program_local = m_device->create_program( nv_particle_engine_vertex_shader_local, nv_particle_engine_fragment_shader );
137        m_program_world = m_device->create_program( nv_particle_engine_vertex_shader_world, nv_particle_engine_fragment_shader );
138}
139
140nv::particle_system nv::particle_engine::create_system( const std::string& id )
141{
142        auto it = m_names.find( id );
143        if ( it == m_names.end() )
144        {
145                return particle_system();
146        }
147        const particle_system_data* data = &(m_data[it->second]);
148        particle_system result = m_systems.create();
149        particle_system_info* info = m_systems.get( result );
150
151        info->data     = data;
152        uint32 ecount = data->emmiter_count;
153        for ( uint32 i = 0; i < ecount; ++i )
154        {
155                info->emmiters[i].last_create = 0;
156        }
157
158        info->count = 0;
159        info->particles = new particle[ data->quota ];
160        info->quads     = new particle_quad[ data->quota ];
161        info->vtx_array = m_device->create_vertex_array<particle_vtx>(
162                (particle_vtx*)info->quads, data->quota*6, STREAM_DRAW );
163        info->vtx_buffer = m_device->find_buffer( info->vtx_array, slot::POSITION );
164        info->last_update = 0;
165        info->test = false;
166//      result->m_own_va      = true;
167//      result->m_offset      = 0;
168
169        return result;
170}
171
172void nv::particle_engine::draw( particle_system system, const render_state& rs, const scene_state& ss )
173{
174        particle_system_info* info = m_systems.get( system );
175        if ( info )
176        {
177                m_context->draw( nv::TRIANGLES, rs, ss, info->data->local ?  m_program_local : m_program_world, info->vtx_array, info->count * 6 );
178        }
179}
180
181nv::particle_engine::~particle_engine()
182{
183        m_device->release( m_program_world );
184        m_device->release( m_program_local );
185}
186
187void nv::particle_engine::release( particle_system system )
188{
189        particle_system_info* info = m_systems.get( system );
190        if ( info )
191        {
192                delete[] info->particles;
193                delete[] info->quads;
194                //if ( system->own_va )
195                m_device->release( info->vtx_array );
196                m_systems.destroy( system );
197        }
198}
199
200void nv::particle_engine::update( particle_system system, const scene_state& s, uint32 ms )
201{
202        particle_system_info* info = m_systems.get( system );
203        if ( info )
204        {
205                m_view_matrix  = s.get_view();
206                m_model_matrix = s.get_model();
207                m_camera_pos   = s.get_camera().get_position();
208                m_inv_view_dir = glm::normalize(-s.get_camera().get_direction());
209
210                destroy_particles( info, ms );
211                create_particles( info, ms );
212                update_particles( info, ms );
213
214                generate_data( info );
215                m_context->update( info->vtx_buffer, info->quads, /*system->m_offset*sizeof(particle_quad)*/ 0, info->count*sizeof(particle_quad) );
216        }
217}
218
219void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
220{
221        particle_system_info* info = m_systems.get( system );
222        if ( info )
223        {
224                vec2 texcoords[4] = { a, vec2( b.x, a.y ), vec2( a.x, b.y ), b };
225
226                for ( uint32 i = 0; i < info->data->quota; ++i )
227                {
228                        particle_quad& rdata   = info->quads[i];
229                        rdata.data[0].texcoord = texcoords[0];
230                        rdata.data[1].texcoord = texcoords[1];
231                        rdata.data[2].texcoord = texcoords[2];
232                        rdata.data[3].texcoord = texcoords[3];
233                        rdata.data[4].texcoord = texcoords[2];
234                        rdata.data[5].texcoord = texcoords[1];
235                }
236        }
237}
238
239void nv::particle_engine::generate_data( particle_system_info* info )
240{
241        const vec3 x( 0.5f, 0.0f, 0.0f );
242        const vec3 y( 0.0f, 0.5f, 0.0f );
243        const vec3 z( 0.0f, 0.0f ,1.0f );
244
245        const vec3 sm[4] = {
246                vec3( -x-y ),
247                vec3(  x-y ),
248                vec3( -x+y ),
249                vec3(  x+y )
250        };
251
252        mat3 rot_mat;
253        vec3 right;
254        mat3 irot_mat = glm::transpose( mat3( m_view_matrix ) );
255        particle_orientation orientation = info->data->orientation;
256        vec3 common_up ( info->data->common_up );
257        vec3 common_dir( info->data->common_dir );
258        bool accurate_facing = info->data->accurate_facing;
259
260
261        for ( uint32 i = 0; i < info->count; ++i )
262        {
263                const particle& pdata = info->particles[i];
264                particle_quad& rdata  = info->quads[i];
265
266                vec3 view_dir( m_inv_view_dir );
267                if ( accurate_facing ) view_dir = glm::normalize( m_camera_pos - pdata.position );
268
269                switch ( orientation )
270                {
271                case particle_orientation::POINT :
272                        right   = glm::normalize( glm::cross( view_dir, vec3( 0, 1, 0 ) ) );
273                        rot_mat = mat3( right, glm::cross( right, -view_dir ), -view_dir );
274//                      rot_mat = irot_mat * glm::mat3_cast( glm::angleAxis( pdata.rotation, z ) );
275                        break;
276                case particle_orientation::ORIENTED :
277                        right   = glm::normalize( glm::cross( pdata.dir, view_dir ) );
278                        rot_mat = mat3( right, pdata.dir, glm::cross( pdata.dir, right ) );
279                        break;
280                case particle_orientation::ORIENTED_COMMON :
281                        right   = glm::normalize( glm::cross( common_dir, view_dir ) );
282                        rot_mat = mat3( right, common_dir, glm::cross( common_dir, right ) );
283                        break;
284                case particle_orientation::PERPENDICULAR :
285                        right   = glm::normalize( glm::cross( common_up, pdata.dir ) );
286                        rot_mat = mat3( right, common_up, glm::cross( common_up, right ) );
287                        break;
288                case particle_orientation::PERPENDICULAR_COMMON :
289                        right   = glm::normalize( glm::cross( common_up, common_dir ) );
290                        rot_mat = mat3( right, common_up, glm::cross( common_up, right ) );
291                        break;
292                }
293
294                vec3 size( pdata.size.x, pdata.size.y, 0.0f );
295                vec3 s0 = rot_mat * ( ( size * sm[0] ) );
296                vec3 s1 = rot_mat * ( ( size * sm[1] ) );
297                vec3 s2 = rot_mat * ( ( size * sm[2] ) );
298                vec3 s3 = rot_mat * ( ( size * sm[3] ) );
299
300                rdata.data[0].position = pdata.position + s0;
301                rdata.data[0].color    = pdata.color;
302
303                rdata.data[1].position = pdata.position + s1;
304                rdata.data[1].color    = pdata.color;
305
306                rdata.data[2].position = pdata.position + s2;
307                rdata.data[2].color    = pdata.color;
308
309                rdata.data[3].position = pdata.position + s3;
310                rdata.data[3].color    = pdata.color;
311
312                rdata.data[4] = rdata.data[2];
313                rdata.data[5] = rdata.data[1];
314        }
315}
316
317void nv::particle_engine::destroy_particles( particle_system_info* info, uint32 ms )
318{
319        if ( info->count > 0 )
320                for ( sint32 i = info->count-1; i >= 0; --i )
321                {
322                        particle& pinfo = info->particles[i];
323                        if ( //pdata.position.y < 0.0f ||
324                                ms >= pinfo.death )
325                        {
326                                info->count--;
327                                std::swap( info->particles[i], info->particles[info->count] );
328                        }
329                }
330}
331
332void nv::particle_engine::create_particles( particle_system_info* info, uint32 ms )
333{
334        uint32 ecount = info->data->emmiter_count;
335        if ( ecount == 0 ) return;
336
337        random& r = random::get();
338        vec3 source;
339        mat3 orient;
340        if ( !info->data->local )
341        {
342                source = vec3( m_model_matrix[3] );
343                orient = mat3( m_model_matrix );
344        }
345
346        for ( uint32 i = 0; i < ecount; ++i )
347        {
348                const auto& edata = info->data->emmiters[i];
349                auto& einfo = info->emmiters[i];
350
351                uint32 period = glm::max<uint32>( uint32(1000.f / edata.rate), 1 );
352                while ( ms - einfo.last_create > period )
353                {
354                        if ( info->count < info->data->quota-1 )
355                        {
356                                particle& pinfo = info->particles[info->count];
357                                pinfo.position = source;
358                                pinfo.color    = edata.color_min == edata.color_max ?
359                                        edata.color_min : r.range( edata.color_min, edata.color_max );
360                                pinfo.size     = edata.size_min == edata.size_max ?
361                                        edata.size_min : r.range( edata.size_min, edata.size_max );
362                                if ( edata.square ) pinfo.size.y = pinfo.size.x;
363                                pinfo.velocity  = edata.velocity_min == edata.velocity_max ?
364                                        edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max );
365                                pinfo.death     = ms + ( edata.lifetime_min == edata.lifetime_max ?
366                                        edata.lifetime_min : r.urange( edata.lifetime_min, edata.lifetime_max ) );
367                                //pinfo.rotation = r.frand( 360.0f );
368
369                                pinfo.dir = edata.dir;
370                                if ( edata.angle > 0.0f )
371                                {
372                                        float emission_angle = glm::radians( edata.angle );
373                                        float cos_theta = r.frange( cos( emission_angle ), 1.0f );
374                                        float sin_theta = glm::sqrt(1.0f - cos_theta * cos_theta );
375                                        float phi       = r.frange( 0.0f, 2*glm::pi<float>() );
376                                        pinfo.dir = orient *
377                                                ( edata.odir * ( glm::cos(phi) * sin_theta ) +
378                                                edata.cdir * ( glm::sin(phi)*sin_theta ) +
379                                                edata.dir  * cos_theta );
380                                }
381
382                                info->count++;
383                        }
384                        einfo.last_create += period;
385                }
386        }
387}
388
389void nv::particle_engine::update_particles( particle_system_info* system, uint32 ms )
390{
391        uint32 ticks = ms - system->last_update;
392        if ( ticks > 0 )
393                for ( uint32 i = 0; i < system->count; ++i )
394                {
395                        particle& pdata = system->particles[i];
396                        vec3 velocity_vec = pdata.dir * pdata.velocity;
397                        pdata.position += velocity_vec * ( 0.001f * ticks );
398                        velocity_vec   += system->data->gravity * ( 0.001f * ticks );
399                        pdata.velocity  = glm::length( velocity_vec );
400                        if ( pdata.velocity > 0.0f ) pdata.dir       = glm::normalize( velocity_vec );
401                        if ( pdata.position.y < 0.0f )
402                        {
403                                if ( pdata.velocity > 1.0f )
404                                {
405                                        pdata.position.y = -pdata.position.y;
406                                        pdata.velocity   = 0.5f*pdata.velocity;
407                                        pdata.dir.y      = -pdata.dir.y;
408                                }
409                                else
410                                {
411                                        pdata.position.y = 0.0f;
412                                        pdata.velocity   = 0.0f;
413                                }
414                        }
415                }
416                system->last_update = ms;
417}
Note: See TracBrowser for help on using the repository browser.