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