Features completed cpu particle
This commit is contained in:
parent
63ecbefbdd
commit
0066722dab
@ -23,7 +23,7 @@
|
||||
<size min="0.10"
|
||||
max="0.30" />
|
||||
|
||||
<color min="40 40 255"
|
||||
<color min="255 255 255"
|
||||
max="255 255 255" />
|
||||
|
||||
<!-- How much time in milliseconds before the particle is fully faded out -->
|
||||
|
15
data/shaders/alphatest_particle.frag
Normal file
15
data/shaders/alphatest_particle.frag
Normal file
@ -0,0 +1,15 @@
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec2 tc;
|
||||
in vec4 pc;
|
||||
out vec4 FragColor;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 color = texture(tex, tc);
|
||||
if (color.a < 0.5)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
FragColor = color * pc;
|
||||
}
|
89
data/shaders/alphatest_particle.vert
Normal file
89
data/shaders/alphatest_particle.vert
Normal file
@ -0,0 +1,89 @@
|
||||
uniform vec3 color_from;
|
||||
uniform vec3 color_to;
|
||||
uniform int flips;
|
||||
|
||||
#ifdef Explicit_Attrib_Location_Usable
|
||||
|
||||
layout(location = 0) in vec3 Position;
|
||||
layout(location = 1) in float lifetime;
|
||||
layout(location = 2) in float size;
|
||||
|
||||
layout(location = 3) in vec2 Texcoord;
|
||||
layout(location = 4) in vec2 quadcorner;
|
||||
|
||||
layout(location = 5) in vec3 rotationvec;
|
||||
layout(location = 6) in float anglespeed;
|
||||
#else
|
||||
|
||||
in vec3 Position;
|
||||
in float lifetime;
|
||||
in float size;
|
||||
|
||||
in vec2 Texcoord;
|
||||
in vec2 quadcorner;
|
||||
|
||||
in vec3 rotationvec;
|
||||
float anglespeed;
|
||||
#endif
|
||||
|
||||
out vec2 tc;
|
||||
out vec4 pc;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
if (size == 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
tc = Texcoord;
|
||||
pc = vec4(vec3(color_from + (color_to - color_from) * lifetime), 1.0);
|
||||
#if !defined(sRGB_Framebuffer_Usable) && !defined(Advanced_Lighting_Enabled)
|
||||
pc.rgb = pow(pc.rgb, vec3(1. / 2.2));
|
||||
#endif
|
||||
vec4 viewpos = vec4(0.);
|
||||
if (flips == 1)
|
||||
{
|
||||
// from http://jeux.developpez.com/faq/math
|
||||
float angle = lifetime * anglespeed;
|
||||
float sin_a = sin(angle / 2.);
|
||||
float cos_a = cos(angle / 2.);
|
||||
|
||||
vec4 quaternion = normalize(vec4(rotationvec * sin_a, cos_a));
|
||||
float xx = quaternion.x * quaternion.x;
|
||||
float xy = quaternion.x * quaternion.y;
|
||||
float xz = quaternion.x * quaternion.z;
|
||||
float xw = quaternion.x * quaternion.w;
|
||||
float yy = quaternion.y * quaternion.y;
|
||||
float yz = quaternion.y * quaternion.z;
|
||||
float yw = quaternion.y * quaternion.w;
|
||||
float zz = quaternion.z * quaternion.z;
|
||||
float zw = quaternion.z * quaternion.w;
|
||||
|
||||
vec4 col1 = vec4(
|
||||
1. - 2. * ( yy + zz ),
|
||||
2. * ( xy + zw ),
|
||||
2. * ( xz - yw ),
|
||||
0.);
|
||||
vec4 col2 = vec4(
|
||||
2. * ( xy - zw ),
|
||||
1. - 2. * ( xx + zz ),
|
||||
2. * ( yz + xw ),
|
||||
0.);
|
||||
vec4 col3 = vec4(
|
||||
2. * ( xz + yw ),
|
||||
2. * ( yz - xw ),
|
||||
1. - 2. * ( xx + yy ),
|
||||
0.);
|
||||
vec4 col4 = vec4(0., 0., 0., 1.);
|
||||
mat4 rotationMatrix = mat4(col1, col2, col3, col4);
|
||||
vec3 newquadcorner = size * vec3(quadcorner, 0.);
|
||||
newquadcorner = (rotationMatrix * vec4(newquadcorner, 0.)).xyz;
|
||||
viewpos = ViewMatrix * vec4(Position + newquadcorner, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewpos = ViewMatrix * vec4(Position, 1.0);
|
||||
viewpos += size * vec4(quadcorner, 0., 0.);
|
||||
}
|
||||
gl_Position = ProjectionMatrix * viewpos;
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
#ifdef Explicit_Attrib_Location_Usable
|
||||
layout(location=0) in vec3 Position;
|
||||
layout(location = 1) in float lifetime;
|
||||
layout(location = 2) in float size;
|
||||
|
||||
layout(location = 3) in vec2 Texcoord;
|
||||
layout(location = 4) in vec2 quadcorner;
|
||||
|
||||
layout(location = 5) in vec3 rotationvec;
|
||||
layout(location = 6) in float anglespeed;
|
||||
#else
|
||||
in vec3 Position;
|
||||
in float lifetime;
|
||||
in float size;
|
||||
|
||||
in vec2 Texcoord;
|
||||
in vec2 quadcorner;
|
||||
|
||||
in vec3 rotationvec;
|
||||
float anglespeed;
|
||||
#endif
|
||||
|
||||
out float lf;
|
||||
out vec2 tc;
|
||||
out vec4 pc;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
tc = Texcoord;
|
||||
lf = lifetime;
|
||||
vec3 newposition = Position;
|
||||
|
||||
// from http://jeux.developpez.com/faq/math
|
||||
float angle = lf * anglespeed;
|
||||
float sin_a = sin(angle / 2.);
|
||||
float cos_a = cos(angle / 2.);
|
||||
|
||||
vec4 quaternion = normalize(vec4(rotationvec * sin_a, cos_a));
|
||||
float xx = quaternion.x * quaternion.x;
|
||||
float xy = quaternion.x * quaternion.y;
|
||||
float xz = quaternion.x * quaternion.z;
|
||||
float xw = quaternion.x * quaternion.w;
|
||||
float yy = quaternion.y * quaternion.y;
|
||||
float yz = quaternion.y * quaternion.z;
|
||||
float yw = quaternion.y * quaternion.w;
|
||||
float zz = quaternion.z * quaternion.z;
|
||||
float zw = quaternion.z * quaternion.w;
|
||||
|
||||
vec4 col1 = vec4(
|
||||
1. - 2. * ( yy + zz ),
|
||||
2. * ( xy + zw ),
|
||||
2. * ( xz - yw ),
|
||||
0.);
|
||||
vec4 col2 = vec4(
|
||||
2. * ( xy - zw ),
|
||||
1. - 2. * ( xx + zz ),
|
||||
2. * ( yz + xw ),
|
||||
0.);
|
||||
vec4 col3 = vec4(
|
||||
2. * ( xz + yw ),
|
||||
2. * ( yz - xw ),
|
||||
1. - 2. * ( xx + yy ),
|
||||
0.);
|
||||
vec4 col4 = vec4(0., 0., 0., 1.);
|
||||
mat4 rotationMatrix = mat4(col1, col2, col3, col4);
|
||||
vec3 newquadcorner = size * vec3(quadcorner, 0.);
|
||||
newquadcorner = (rotationMatrix * vec4(newquadcorner, 0.)).xyz;
|
||||
|
||||
vec4 viewpos = ViewMatrix * vec4(newposition + newquadcorner, 1.0);
|
||||
gl_Position = ProjectionMatrix * viewpos;
|
||||
pc = vec4(1.);
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
uniform vec3 color_from;
|
||||
uniform vec3 color_to;
|
||||
|
||||
#ifdef Explicit_Attrib_Location_Usable
|
||||
layout(location=0) in vec3 Position;
|
||||
layout(location = 1) in float lifetime;
|
||||
layout(location = 2) in float size;
|
||||
|
||||
layout(location=3) in vec2 Texcoord;
|
||||
layout(location = 4) in vec2 quadcorner;
|
||||
#else
|
||||
in vec3 Position;
|
||||
in float lifetime;
|
||||
in float size;
|
||||
|
||||
in vec2 Texcoord;
|
||||
in vec2 quadcorner;
|
||||
#endif
|
||||
|
||||
out float lf;
|
||||
out vec2 tc;
|
||||
out vec4 pc;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
tc = Texcoord;
|
||||
lf = lifetime;
|
||||
pc = vec4(vec3(color_from + (color_to - color_from) * lf), 1.0) * smoothstep(1., 0.8, lf);
|
||||
#if !defined(sRGB_Framebuffer_Usable) && !defined(Advanced_Lighting_Enabled)
|
||||
pc.rgb = pow(pc.rgb, vec3(1. / 2.2));
|
||||
#endif
|
||||
vec3 newposition = Position;
|
||||
|
||||
vec4 viewpos = ViewMatrix * vec4(newposition, 1.0);
|
||||
viewpos += size * vec4(quadcorner, 0., 0.);
|
||||
gl_Position = ProjectionMatrix * viewpos;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
uniform int dt;
|
||||
uniform mat4 sourcematrix;
|
||||
uniform int level;
|
||||
uniform float size_increase_factor;
|
||||
|
||||
uniform float track_x;
|
||||
uniform float track_z;
|
||||
uniform float track_x_len;
|
||||
uniform float track_z_len;
|
||||
|
||||
#ifndef GL_ES
|
||||
uniform samplerBuffer heightmap;
|
||||
#else
|
||||
uniform sampler2D heightmap;
|
||||
#endif
|
||||
|
||||
#ifdef Explicit_Attrib_Location_Usable
|
||||
layout (location = 4) in vec3 particle_position_initial;
|
||||
layout (location = 5) in float lifetime_initial;
|
||||
layout (location = 6) in vec3 particle_velocity_initial;
|
||||
layout (location = 7) in float size_initial;
|
||||
|
||||
layout (location = 0) in vec3 particle_position;
|
||||
layout (location = 1) in float lifetime;
|
||||
layout (location = 2) in vec3 particle_velocity;
|
||||
layout (location = 3) in float size;
|
||||
#else
|
||||
in vec3 particle_position_initial;
|
||||
in float lifetime_initial;
|
||||
in vec3 particle_velocity_initial;
|
||||
in float size_initial;
|
||||
|
||||
in vec3 particle_position;
|
||||
in float lifetime;
|
||||
in vec3 particle_velocity;
|
||||
in float size;
|
||||
#endif
|
||||
|
||||
out vec3 new_particle_position;
|
||||
out float new_lifetime;
|
||||
out vec3 new_particle_velocity;
|
||||
out float new_size;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bool reset = false;
|
||||
|
||||
float i_as_float = clamp(256. * (particle_position.x - track_x) / track_x_len, 0., 255.);
|
||||
float j_as_float = clamp(256. * (particle_position.z - track_z) / track_z_len, 0., 255.);
|
||||
int i = int(i_as_float);
|
||||
int j = int(j_as_float);
|
||||
|
||||
#ifndef GL_ES
|
||||
float h = particle_position.y - texelFetch(heightmap, i * 256 + j).r;
|
||||
#else
|
||||
float h = particle_position.y - texelFetch(heightmap, ivec2(j, i), 0).r;
|
||||
#endif
|
||||
|
||||
reset = h < 0.;
|
||||
|
||||
vec4 initialposition = sourcematrix * vec4(particle_position_initial, 1.0);
|
||||
vec4 adjusted_initial_velocity = sourcematrix * vec4(particle_position_initial + particle_velocity_initial, 1.0) - initialposition;
|
||||
float adjusted_lifetime = lifetime + (float(dt)/lifetime_initial);
|
||||
reset = reset || (adjusted_lifetime > 1.) && (gl_VertexID <= level);
|
||||
reset = reset || (lifetime < 0.);
|
||||
new_particle_position = !reset ? particle_position + particle_velocity.xyz * float(dt) : initialposition.xyz;
|
||||
new_lifetime = !reset ? adjusted_lifetime : 0.;
|
||||
new_particle_velocity = !reset ? particle_velocity : adjusted_initial_velocity.xyz;
|
||||
new_size = !reset ? mix(size_initial, size_initial * size_increase_factor, adjusted_lifetime) : size_initial;
|
||||
gl_Position = vec4(0.);
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
uniform int dt;
|
||||
uniform mat4 previous_frame_sourcematrix;
|
||||
uniform mat4 sourcematrix;
|
||||
uniform int level;
|
||||
uniform float size_increase_factor;
|
||||
|
||||
#ifdef Explicit_Attrib_Location_Usable
|
||||
layout (location = 4) in vec3 particle_position_initial;
|
||||
layout (location = 5) in float lifetime_initial;
|
||||
layout (location = 6) in vec3 particle_velocity_initial;
|
||||
layout (location = 7) in float size_initial;
|
||||
|
||||
layout (location = 0) in vec3 particle_position;
|
||||
layout (location = 1) in float lifetime;
|
||||
layout (location = 2) in vec3 particle_velocity;
|
||||
layout (location = 3) in float size;
|
||||
|
||||
#ifdef Needs_Vertex_Id_Workaround
|
||||
layout (location = 8) in int vertex_id;
|
||||
#endif
|
||||
|
||||
#else
|
||||
in vec3 particle_position_initial;
|
||||
in float lifetime_initial;
|
||||
in vec3 particle_velocity_initial;
|
||||
in float size_initial;
|
||||
|
||||
in vec3 particle_position;
|
||||
in float lifetime;
|
||||
in vec3 particle_velocity;
|
||||
in float size;
|
||||
|
||||
#ifdef Needs_Vertex_Id_Workaround
|
||||
in int vertex_id;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
out vec3 new_particle_position;
|
||||
out float new_lifetime;
|
||||
out vec3 new_particle_velocity;
|
||||
out float new_size;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
float updated_lifetime = lifetime + (float(dt)/lifetime_initial);
|
||||
if (updated_lifetime > 1.)
|
||||
{
|
||||
#ifdef Needs_Vertex_Id_Workaround
|
||||
if (vertex_id < level)
|
||||
#else
|
||||
if (gl_VertexID < level)
|
||||
#endif
|
||||
{
|
||||
float dt_from_last_frame = fract(updated_lifetime) * lifetime_initial;
|
||||
float coeff = dt_from_last_frame / float(dt);
|
||||
|
||||
vec4 previous_frame_position = previous_frame_sourcematrix * vec4(particle_position_initial, 1.0);
|
||||
vec4 current_frame_position = sourcematrix * vec4(particle_position_initial, 1.0);
|
||||
|
||||
vec4 updated_initialposition = mix(current_frame_position,
|
||||
previous_frame_position,
|
||||
coeff);
|
||||
|
||||
vec4 updated_initial_velocity = mix(sourcematrix * vec4(particle_velocity_initial, 0.0),
|
||||
previous_frame_sourcematrix * vec4(particle_velocity_initial, 0.0),
|
||||
coeff);
|
||||
//+ (current_frame_position - previous_frame_position) / dt;
|
||||
//To be accurate, emitter speed should be added.
|
||||
//But the simple formula ( (current_frame_position - previous_frame_position) / dt ) with a constant speed
|
||||
//between 2 frames creates visual artifacts when the framerate is low, and a more accurate formula would need
|
||||
//more complex computations.
|
||||
|
||||
new_particle_position = updated_initialposition.xyz + dt_from_last_frame * updated_initial_velocity.xyz;
|
||||
new_particle_velocity = updated_initial_velocity.xyz;
|
||||
|
||||
new_lifetime = fract(updated_lifetime);
|
||||
new_size = mix(size_initial, size_initial * size_increase_factor, fract(updated_lifetime));
|
||||
}
|
||||
else
|
||||
{
|
||||
new_lifetime = fract(updated_lifetime);
|
||||
new_size = 0.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new_particle_position = particle_position + particle_velocity.xyz * float(dt);
|
||||
new_particle_velocity = particle_velocity;
|
||||
new_lifetime = updated_lifetime;
|
||||
new_size = (size == 0.0) ? 0. : mix(size_initial, size_initial * size_increase_factor, updated_lifetime);
|
||||
}
|
||||
gl_Position = vec4(0.);
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D dtex;
|
||||
uniform mat4 invproj;
|
||||
|
||||
in float lf;
|
||||
in vec2 tc;
|
||||
in vec4 pc;
|
||||
out vec4 FragColor;
|
||||
|
||||
#stk_include "utils/getPosFromUVDepth.frag"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec2 xy = gl_FragCoord.xy / screen;
|
||||
float FragZ = gl_FragCoord.z;
|
||||
vec4 FragmentPos = getPosFromUVDepth(vec3(xy, FragZ), InverseProjectionMatrix);
|
||||
float EnvZ = texture(dtex, xy).x;
|
||||
vec4 EnvPos = getPosFromUVDepth(vec3(xy, EnvZ), InverseProjectionMatrix);
|
||||
float alpha = clamp((EnvPos.z - FragmentPos.z) * 0.3, 0., 1.);
|
||||
FragColor = texture(tex, tc) * pc * alpha;
|
||||
}
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D dtex;
|
||||
|
||||
in vec2 tc;
|
||||
in vec4 pc;
|
||||
out vec4 FragColor;
|
||||
|
||||
#stk_include "utils/getPosFromUVDepth.frag"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec2 xy = gl_FragCoord.xy / screen;
|
||||
float FragZ = gl_FragCoord.z;
|
||||
vec4 FragmentPos = getPosFromUVDepth(vec3(xy, FragZ), InverseProjectionMatrix);
|
||||
float EnvZ = texture(dtex, xy).x;
|
||||
vec4 EnvPos = getPosFromUVDepth(vec3(xy, EnvZ), InverseProjectionMatrix);
|
||||
float alpha = clamp((EnvPos.z - FragmentPos.z) * 0.3, 0., 1.);
|
||||
FragColor = texture(tex, tc) * pc * alpha;
|
||||
}
|
91
data/shaders/simple_particle.vert
Normal file
91
data/shaders/simple_particle.vert
Normal file
@ -0,0 +1,91 @@
|
||||
uniform vec3 color_from;
|
||||
uniform vec3 color_to;
|
||||
uniform int flips;
|
||||
|
||||
#ifdef Explicit_Attrib_Location_Usable
|
||||
|
||||
layout(location = 0) in vec3 Position;
|
||||
layout(location = 1) in float lifetime;
|
||||
layout(location = 2) in float size;
|
||||
|
||||
layout(location = 3) in vec2 Texcoord;
|
||||
layout(location = 4) in vec2 quadcorner;
|
||||
|
||||
layout(location = 5) in vec3 rotationvec;
|
||||
layout(location = 6) in float anglespeed;
|
||||
#else
|
||||
|
||||
in vec3 Position;
|
||||
in float lifetime;
|
||||
in float size;
|
||||
|
||||
in vec2 Texcoord;
|
||||
in vec2 quadcorner;
|
||||
|
||||
in vec3 rotationvec;
|
||||
float anglespeed;
|
||||
#endif
|
||||
|
||||
out float lf;
|
||||
out vec2 tc;
|
||||
out vec4 pc;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
if (size == 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
tc = Texcoord;
|
||||
lf = lifetime;
|
||||
pc = vec4(vec3(color_from + (color_to - color_from) * lf), 1.0) * smoothstep(1., 0.8, lf);
|
||||
#if !defined(sRGB_Framebuffer_Usable) && !defined(Advanced_Lighting_Enabled)
|
||||
pc.rgb = pow(pc.rgb, vec3(1. / 2.2));
|
||||
#endif
|
||||
vec4 viewpos = vec4(0.);
|
||||
if (flips == 1)
|
||||
{
|
||||
// from http://jeux.developpez.com/faq/math
|
||||
float angle = lifetime * anglespeed;
|
||||
float sin_a = sin(angle / 2.);
|
||||
float cos_a = cos(angle / 2.);
|
||||
|
||||
vec4 quaternion = normalize(vec4(rotationvec * sin_a, cos_a));
|
||||
float xx = quaternion.x * quaternion.x;
|
||||
float xy = quaternion.x * quaternion.y;
|
||||
float xz = quaternion.x * quaternion.z;
|
||||
float xw = quaternion.x * quaternion.w;
|
||||
float yy = quaternion.y * quaternion.y;
|
||||
float yz = quaternion.y * quaternion.z;
|
||||
float yw = quaternion.y * quaternion.w;
|
||||
float zz = quaternion.z * quaternion.z;
|
||||
float zw = quaternion.z * quaternion.w;
|
||||
|
||||
vec4 col1 = vec4(
|
||||
1. - 2. * ( yy + zz ),
|
||||
2. * ( xy + zw ),
|
||||
2. * ( xz - yw ),
|
||||
0.);
|
||||
vec4 col2 = vec4(
|
||||
2. * ( xy - zw ),
|
||||
1. - 2. * ( xx + zz ),
|
||||
2. * ( yz + xw ),
|
||||
0.);
|
||||
vec4 col3 = vec4(
|
||||
2. * ( xz + yw ),
|
||||
2. * ( yz - xw ),
|
||||
1. - 2. * ( xx + yy ),
|
||||
0.);
|
||||
vec4 col4 = vec4(0., 0., 0., 1.);
|
||||
mat4 rotationMatrix = mat4(col1, col2, col3, col4);
|
||||
vec3 newquadcorner = size * vec3(quadcorner, 0.);
|
||||
newquadcorner = (rotationMatrix * vec4(newquadcorner, 0.)).xyz;
|
||||
viewpos = ViewMatrix * vec4(Position + newquadcorner, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewpos = ViewMatrix * vec4(Position, 1.0);
|
||||
viewpos += size * vec4(quadcorner, 0., 0.);
|
||||
}
|
||||
gl_Position = ProjectionMatrix * viewpos;
|
||||
}
|
306
src/graphics/cpu_particle_manager.cpp
Normal file
306
src/graphics/cpu_particle_manager.cpp
Normal file
@ -0,0 +1,306 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2017 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
#include "graphics/cpu_particle_manager.hpp"
|
||||
#include "graphics/stk_particle.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
#include "graphics/shared_gpu_objects.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
#include "graphics/texture_shader.hpp"
|
||||
|
||||
// ============================================================================
|
||||
/** A Shader to render particles.
|
||||
*/
|
||||
class ParticleRenderer : public TextureShader
|
||||
<ParticleRenderer, 2, video::SColorf, video::SColorf, int>
|
||||
{
|
||||
public:
|
||||
ParticleRenderer()
|
||||
{
|
||||
loadProgram(PARTICLES_RENDERING,
|
||||
GL_VERTEX_SHADER, "simple_particle.vert",
|
||||
GL_FRAGMENT_SHADER, "simple_particle.frag");
|
||||
assignUniforms("color_from", "color_to", "flips");
|
||||
assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED,
|
||||
1, "dtex", ST_NEAREST_FILTERED);
|
||||
} // ParticleRenderer
|
||||
}; // ParticleRenderer
|
||||
|
||||
// ============================================================================
|
||||
/** A Shader to render alpha-test particles.
|
||||
*/
|
||||
class AlphaTestParticleRenderer : public TextureShader
|
||||
<AlphaTestParticleRenderer, 1, video::SColorf, video::SColorf, int>
|
||||
{
|
||||
public:
|
||||
AlphaTestParticleRenderer()
|
||||
{
|
||||
loadProgram(PARTICLES_RENDERING,
|
||||
GL_VERTEX_SHADER, "alphatest_particle.vert",
|
||||
GL_FRAGMENT_SHADER, "alphatest_particle.frag");
|
||||
assignUniforms("color_from", "color_to", "flips");
|
||||
assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED);
|
||||
} // AlphaTestParticleRenderer
|
||||
}; // AlphaTestParticleRenderer
|
||||
|
||||
// ============================================================================
|
||||
CPUParticleManager::~CPUParticleManager()
|
||||
{
|
||||
for (auto& p : m_gl_particles)
|
||||
{
|
||||
glDeleteVertexArrays(1, &std::get<0>(p.second));
|
||||
glDeleteBuffers(1, &std::get<1>(p.second));
|
||||
}
|
||||
STKParticle::destroyFlipsBuffer();
|
||||
} // ~CPUParticleManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void CPUParticleManager::addParticleNode(STKParticle* node)
|
||||
{
|
||||
if (node->getMaterialCount() != 1)
|
||||
{
|
||||
Log::error("CPUParticleManager", "More than 1 material");
|
||||
return;
|
||||
}
|
||||
video::ITexture* t = node->getMaterial(0).getTexture(0);
|
||||
assert(t != NULL);
|
||||
std::string tex_name = t->getName().getPtr();
|
||||
Material* m = NULL;
|
||||
if (m_material_map.find(tex_name) == m_material_map.end())
|
||||
{
|
||||
m = material_manager->getMaterialFor(t);
|
||||
m_material_map[tex_name] = m;
|
||||
if (m == NULL)
|
||||
{
|
||||
Log::error("CPUParticleManager", "Missing material");
|
||||
}
|
||||
}
|
||||
m = m_material_map.at(tex_name);
|
||||
if (m == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_particles_queue[tex_name].push_back(node);
|
||||
} // addParticleNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void CPUParticleManager::generateAll()
|
||||
{
|
||||
for (auto& p : m_particles_queue)
|
||||
{
|
||||
if (p.second.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool flips = false;
|
||||
for (auto& q : p.second)
|
||||
{
|
||||
if (q->getFlips() && !flips)
|
||||
{
|
||||
flips = q->getFlips();
|
||||
}
|
||||
q->generate(&m_particles_generated[p.first]);
|
||||
}
|
||||
if (flips)
|
||||
{
|
||||
STKParticle::updateFlips(m_particles_queue[p.first].size() *
|
||||
m_particles_queue[p.first][0]->getMaxCount());
|
||||
}
|
||||
}
|
||||
} // generateAll
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void CPUParticleManager::uploadAll()
|
||||
{
|
||||
for (auto& p : m_particles_generated)
|
||||
{
|
||||
if (p.second.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool first_upload = false;
|
||||
if (m_gl_particles.find(p.first) == m_gl_particles.end())
|
||||
{
|
||||
first_upload = true;
|
||||
m_gl_particles[p.first] = std::make_tuple(0, 0, 100);
|
||||
}
|
||||
if (first_upload)
|
||||
{
|
||||
glGenBuffers(1, &std::get<1>(m_gl_particles[p.first]));
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
std::get<1>(m_gl_particles[p.first]));
|
||||
glBufferData(GL_ARRAY_BUFFER, 2000, NULL, GL_DYNAMIC_DRAW);
|
||||
glGenVertexArrays(1, &std::get<0>(m_gl_particles[p.first]));
|
||||
glBindVertexArray(std::get<0>(m_gl_particles[p.first]));
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
SharedGPUObjects::getParticleQuadVBO());
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 16, 0);
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 16, (void*)8);
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
std::get<1>(m_gl_particles[p.first]));
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 20, 0);
|
||||
glVertexAttribDivisorARB(0, 1);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 20, (void*)12);
|
||||
glVertexAttribDivisorARB(1, 1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 20, (void*)16);
|
||||
glVertexAttribDivisorARB(2, 1);
|
||||
bool flips = false;
|
||||
for (unsigned i = 0; i < m_particles_queue[p.first].size(); i++)
|
||||
{
|
||||
if (m_particles_queue[p.first][i]->getFlips())
|
||||
{
|
||||
flips = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flips)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, STKParticle::getFlipsBuffer());
|
||||
glEnableVertexAttribArray(5);
|
||||
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, 16, 0);
|
||||
glVertexAttribDivisorARB(5, 1);
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, 16, (void*)12);
|
||||
glVertexAttribDivisorARB(6, 1);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
glBindBuffer(GL_ARRAY_BUFFER, std::get<1>(m_gl_particles[p.first]));
|
||||
// Check "real" particle buffer size in opengl
|
||||
if (std::get<2>(m_gl_particles[p.first]) <
|
||||
m_particles_generated[p.first].size())
|
||||
{
|
||||
std::get<2>(m_gl_particles[p.first]) =
|
||||
m_particles_generated[p.first].size() * 2;
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
m_particles_generated[p.first].size() * 2 * 20, NULL,
|
||||
GL_DYNAMIC_DRAW);
|
||||
}
|
||||
void* ptr = glMapBufferRange(GL_ARRAY_BUFFER, 0,
|
||||
m_particles_generated[p.first].size() * 20, GL_MAP_WRITE_BIT |
|
||||
GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
|
||||
memcpy(ptr, m_particles_generated[p.first].data(),
|
||||
m_particles_generated[p.first].size() * 20);
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
} // uploadAll
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void CPUParticleManager::drawAll()
|
||||
{
|
||||
std::vector<std::pair<Material*, std::string> > particle_drawn;
|
||||
for (auto& p : m_particles_generated)
|
||||
{
|
||||
if (!p.second.empty())
|
||||
{
|
||||
particle_drawn.emplace_back(m_material_map.at(p.first), p.first);
|
||||
}
|
||||
}
|
||||
std::sort(particle_drawn.begin(), particle_drawn.end(),
|
||||
[](const std::pair<Material*, std::string>& a,
|
||||
const std::pair<Material*, std::string>& b)->bool
|
||||
{
|
||||
return a.first->getShaderType() > b.first->getShaderType();
|
||||
});
|
||||
|
||||
Material::ShaderType st = Material::SHADERTYPE_COUNT;
|
||||
for (auto& p : particle_drawn)
|
||||
{
|
||||
bool flips = false;
|
||||
for (unsigned i = 0; i < m_particles_queue[p.second].size(); i++)
|
||||
{
|
||||
if (m_particles_queue[p.second][i]->getFlips())
|
||||
{
|
||||
flips = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Material* cur_mat = p.first;
|
||||
if (cur_mat->getShaderType() != st)
|
||||
{
|
||||
st = cur_mat->getShaderType();
|
||||
if (cur_mat->getShaderType() == Material::SHADERTYPE_ADDITIVE)
|
||||
{
|
||||
ParticleRenderer::getInstance()->use();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
}
|
||||
else if (cur_mat
|
||||
->getShaderType() == Material::SHADERTYPE_ALPHA_BLEND)
|
||||
{
|
||||
ParticleRenderer::getInstance()->use();
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlphaTestParticleRenderer::getInstance()->use();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_mat->getShaderType() == Material::SHADERTYPE_ADDITIVE ||
|
||||
cur_mat->getShaderType() == Material::SHADERTYPE_ALPHA_BLEND)
|
||||
{
|
||||
ParticleRenderer::getInstance()->setTextureUnits
|
||||
(cur_mat->getTexture()->getOpenGLTextureName(),
|
||||
irr_driver->getDepthStencilTexture());
|
||||
ParticleRenderer::getInstance()->setUniforms(
|
||||
m_particles_queue.at(p.second)[0]->getColorFrom(),
|
||||
m_particles_queue.at(p.second)[0]->getColorTo(), flips);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlphaTestParticleRenderer::getInstance()->setTextureUnits
|
||||
(cur_mat->getTexture()->getOpenGLTextureName());
|
||||
AlphaTestParticleRenderer::getInstance()->setUniforms(
|
||||
m_particles_queue.at(p.second)[0]->getColorFrom(),
|
||||
m_particles_queue.at(p.second)[0]->getColorTo(), flips);
|
||||
}
|
||||
glBindVertexArray(std::get<0>(m_gl_particles[p.second]));
|
||||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4,
|
||||
m_particles_generated.at(p.second).size());
|
||||
}
|
||||
|
||||
} // drawAll
|
||||
|
||||
#endif
|
101
src/graphics/cpu_particle_manager.hpp
Normal file
101
src/graphics/cpu_particle_manager.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2017 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef HEADER_CPU_PARTICLE_MANAGER_HPP
|
||||
#define HEADER_CPU_PARTICLE_MANAGER_HPP
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "graphics/gl_headers.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/singleton.hpp"
|
||||
|
||||
#include <dimension2d.h>
|
||||
#include <vector3d.h>
|
||||
#include <SColor.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
using namespace irr;
|
||||
|
||||
struct CPUParticle
|
||||
{
|
||||
core::vector3df m_position;
|
||||
float m_lifetime;
|
||||
float m_size;
|
||||
// ------------------------------------------------------------------------
|
||||
CPUParticle(const core::vector3df& position, float lifetime, float size)
|
||||
: m_position(position), m_lifetime(lifetime), m_size(size) {}
|
||||
};
|
||||
|
||||
class STKParticle;
|
||||
class Material;
|
||||
|
||||
class CPUParticleManager : public Singleton<CPUParticleManager>, NoCopy
|
||||
{
|
||||
private:
|
||||
std::unordered_map<std::string, std::vector<STKParticle*> >
|
||||
m_particles_queue;
|
||||
|
||||
std::unordered_map<std::string, std::vector<CPUParticle> >
|
||||
m_particles_generated;
|
||||
|
||||
std::unordered_map<std::string, std::tuple<GLuint/*VAO*/,
|
||||
GLuint/*VBO*/, unsigned/*VBO*/> > m_gl_particles;
|
||||
|
||||
std::unordered_map<std::string, Material*> m_material_map;
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
CPUParticleManager() {}
|
||||
// ------------------------------------------------------------------------
|
||||
~CPUParticleManager();
|
||||
// ------------------------------------------------------------------------
|
||||
void addParticleNode(STKParticle* node);
|
||||
// ------------------------------------------------------------------------
|
||||
void generateAll();
|
||||
// ------------------------------------------------------------------------
|
||||
void uploadAll();
|
||||
// ------------------------------------------------------------------------
|
||||
void drawAll();
|
||||
// ------------------------------------------------------------------------
|
||||
void reset()
|
||||
{
|
||||
for (auto& p : m_particles_queue)
|
||||
{
|
||||
p.second.clear();
|
||||
}
|
||||
for (auto& p : m_particles_generated)
|
||||
{
|
||||
p.second.clear();
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void cleanMaterialMap()
|
||||
{
|
||||
m_material_map.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -20,8 +20,8 @@
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/command_buffer.hpp"
|
||||
#include "graphics/cpu_particle_manager.hpp"
|
||||
#include "graphics/draw_tools.hpp"
|
||||
#include "graphics/gpu_particles.hpp"
|
||||
#include "graphics/lod_node.hpp"
|
||||
#include "graphics/materials.hpp"
|
||||
#include "graphics/render_info.hpp"
|
||||
@ -30,6 +30,7 @@
|
||||
#include "graphics/stk_animated_mesh.hpp"
|
||||
#include "graphics/stk_billboard.hpp"
|
||||
#include "graphics/stk_mesh.hpp"
|
||||
#include "graphics/stk_particle.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
#include "utils/profiler.hpp"
|
||||
|
||||
@ -64,6 +65,7 @@ void DrawCalls::clearLists()
|
||||
m_immediate_draw_list.clear();
|
||||
m_billboard_list.clear();
|
||||
m_particles_list.clear();
|
||||
CPUParticleManager::getInstance()->reset();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -562,10 +564,10 @@ void DrawCalls::parseSceneManager(core::list<scene::ISceneNode*> &List,
|
||||
if (!(*I)->isVisible())
|
||||
continue;
|
||||
|
||||
if (ParticleSystemProxy *node = dynamic_cast<ParticleSystemProxy *>(*I))
|
||||
if (STKParticle *node = dynamic_cast<STKParticle *>(*I))
|
||||
{
|
||||
if (!isCulledPrecise(cam, *I))
|
||||
m_particles_list.push_back(node);
|
||||
if (!isCulledPrecise(cam, *I, irr_driver->getBoundingBoxesViz()))
|
||||
CPUParticleManager::getInstance()->addParticleNode(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -604,7 +606,7 @@ DrawCalls::DrawCalls()
|
||||
|
||||
DrawCalls::~DrawCalls()
|
||||
{
|
||||
|
||||
CPUParticleManager::kill();
|
||||
#if !defined(USE_GLES2)
|
||||
delete m_solid_cmd_buffer;
|
||||
delete m_shadow_cmd_buffer;
|
||||
@ -646,6 +648,9 @@ void DrawCalls::prepareDrawCalls( ShadowMatrices& shadow_matrices,
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
irr_driver->setSkinningJoint(getSkinningOffset());
|
||||
PROFILER_PUSH_CPU_MARKER("- cpu particle generation", 0x2F, 0x1F, 0x11);
|
||||
CPUParticleManager::getInstance()->generateAll();
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
// Add a 1 s timeout
|
||||
if (!m_sync)
|
||||
m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
@ -684,6 +689,10 @@ void DrawCalls::prepareDrawCalls( ShadowMatrices& shadow_matrices,
|
||||
m_deferred_update[i]->updateGL();
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
PROFILER_PUSH_CPU_MARKER("- cpu particle upload", 0x3F, 0x03, 0x61);
|
||||
CPUParticleManager::getInstance()->uploadAll();
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
if (!CVS->supportsIndirectInstancingRendering())
|
||||
return;
|
||||
|
||||
@ -726,9 +735,7 @@ void DrawCalls::renderBillboardList() const
|
||||
// ----------------------------------------------------------------------------
|
||||
void DrawCalls::renderParticlesList() const
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
for(auto particles: m_particles_list)
|
||||
particles->render();
|
||||
CPUParticleManager::getInstance()->drawAll();
|
||||
}
|
||||
|
||||
#if !defined(USE_GLES2)
|
||||
|
@ -1,684 +0,0 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2014-2015 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "graphics/gpu_particles.hpp"
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/particle_emitter.hpp"
|
||||
#include "graphics/shared_gpu_objects.hpp"
|
||||
#include "graphics/texture_shader.hpp"
|
||||
#include "guiengine/engine.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IParticleSystemSceneNode.h>
|
||||
#include "../../lib/irrlicht/source/Irrlicht/os.h"
|
||||
#define COMPONENTCOUNT 8
|
||||
|
||||
|
||||
// ============================================================================
|
||||
/** Transform feedback shader that simulates the particles on GPU.
|
||||
*/
|
||||
class PointEmitterShader : public Shader
|
||||
< PointEmitterShader, core::matrix4, core::matrix4, int, int, float >
|
||||
{
|
||||
public:
|
||||
PointEmitterShader()
|
||||
{
|
||||
const char *varyings[] = { "new_particle_position", "new_lifetime",
|
||||
"new_particle_velocity", "new_size" };
|
||||
loadTFBProgram("pointemitter.vert", varyings, 4);
|
||||
assignUniforms("previous_frame_sourcematrix", "sourcematrix",
|
||||
"dt", "level", "size_increase_factor");
|
||||
} // PointEmitterShader
|
||||
|
||||
}; // PointEmitterShader
|
||||
|
||||
// ============================================================================
|
||||
|
||||
/** A Shader to render particles.
|
||||
*/
|
||||
class SimpleParticleRender : public TextureShader<SimpleParticleRender, 2,
|
||||
video::SColorf, video::SColorf>
|
||||
{
|
||||
public:
|
||||
SimpleParticleRender()
|
||||
{
|
||||
loadProgram(PARTICLES_RENDERING,
|
||||
GL_VERTEX_SHADER, "particle.vert",
|
||||
GL_FRAGMENT_SHADER, "particle.frag");
|
||||
|
||||
assignUniforms("color_from", "color_to");
|
||||
assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED,
|
||||
1, "dtex", ST_NEAREST_FILTERED);
|
||||
} // SimpleParticleRender
|
||||
|
||||
}; // SimpleParticleRender
|
||||
|
||||
// ============================================================================
|
||||
|
||||
class FlipParticleRender : public TextureShader<FlipParticleRender, 2>
|
||||
{
|
||||
public:
|
||||
FlipParticleRender()
|
||||
{
|
||||
loadProgram(PARTICLES_RENDERING,
|
||||
GL_VERTEX_SHADER, "flipparticle.vert",
|
||||
GL_FRAGMENT_SHADER, "particle.frag");
|
||||
assignUniforms();
|
||||
assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED,
|
||||
1, "dtex", ST_NEAREST_FILTERED);
|
||||
}
|
||||
|
||||
}; // FlipParticleShader
|
||||
|
||||
// ============================================================================
|
||||
/** */
|
||||
class HeightmapSimulationShader :
|
||||
public TextureShader<HeightmapSimulationShader, 1,
|
||||
core::matrix4, int, int,
|
||||
float, float, float, float,
|
||||
float>
|
||||
{
|
||||
public:
|
||||
HeightmapSimulationShader()
|
||||
{
|
||||
const char *varyings[] = {"new_particle_position", "new_lifetime",
|
||||
"new_particle_velocity", "new_size" };
|
||||
loadTFBProgram("particlesimheightmap.vert", varyings, 4);
|
||||
assignUniforms("sourcematrix", "dt", "level", "size_increase_factor",
|
||||
"track_x", "track_x_len", "track_z", "track_z_len");
|
||||
#if !defined(USE_GLES2)
|
||||
assignSamplerNames(0, "heightmap", ST_TEXTURE_BUFFER);
|
||||
#else
|
||||
assignSamplerNames(0, "heightmap", ST_NEAREST_FILTERED);
|
||||
#endif
|
||||
} // HeightmapSimulationShader
|
||||
|
||||
|
||||
}; // class HeightmapSimulationShader
|
||||
|
||||
// ============================================================================
|
||||
|
||||
scene::IParticleSystemSceneNode *ParticleSystemProxy::addParticleNode(
|
||||
bool withDefaultEmitter, bool randomize_initial_y, ISceneNode* parent, s32 id,
|
||||
const core::vector3df& position,
|
||||
const core::vector3df& rotation,
|
||||
const core::vector3df& scale)
|
||||
{
|
||||
if (!parent)
|
||||
parent = irr_driver->getSceneManager()->getRootSceneNode();
|
||||
|
||||
IParticleSystemSceneNode* node = new ParticleSystemProxy(withDefaultEmitter,
|
||||
parent, irr_driver->getSceneManager(), id, position, rotation, scale, randomize_initial_y);
|
||||
node->drop();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
ParticleSystemProxy::ParticleSystemProxy(bool createDefaultEmitter,
|
||||
ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
|
||||
const core::vector3df& position,
|
||||
const core::vector3df& rotation,
|
||||
const core::vector3df& scale,
|
||||
bool randomize_initial_y) : CParticleSystemSceneNode(createDefaultEmitter, parent, mgr, id, position, rotation, scale), m_alpha_additive(false), m_first_execution(true)
|
||||
{
|
||||
if (randomize_initial_y)
|
||||
m_randomize_initial_y = randomize_initial_y;
|
||||
|
||||
m_randomize_initial_y = randomize_initial_y;
|
||||
size_increase_factor = 0.;
|
||||
ParticleParams = NULL;
|
||||
InitialValues = NULL;
|
||||
m_vertex_id_values = NULL;
|
||||
|
||||
m_color_from[0] = m_color_from[1] = m_color_from[2] = 1.0;
|
||||
m_color_to[0] = m_color_to[1] = m_color_to[2] = 1.0;
|
||||
|
||||
// We set these later but avoid coverity report them
|
||||
heighmapbuffer = 0;
|
||||
heightmaptexture = 0;
|
||||
has_height_map = false;
|
||||
flip = false;
|
||||
track_x = 0;
|
||||
track_z = 0;
|
||||
track_x_len = 0;
|
||||
track_z_len = 0;
|
||||
m_texture_name = 0;
|
||||
}
|
||||
|
||||
ParticleSystemProxy::~ParticleSystemProxy()
|
||||
{
|
||||
delete[] m_vertex_id_values;
|
||||
|
||||
if (InitialValues)
|
||||
free(InitialValues);
|
||||
if (ParticleParams)
|
||||
free(ParticleParams);
|
||||
if (!m_first_execution)
|
||||
cleanGL();
|
||||
if (heighmapbuffer)
|
||||
glDeleteBuffers(1, &heighmapbuffer);
|
||||
if (heightmaptexture)
|
||||
glDeleteTextures(1, &heightmaptexture);
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::setFlip()
|
||||
{
|
||||
flip = true;
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::setHeightmap(const std::vector<std::vector<float> > &hm,
|
||||
float f1, float f2, float f3, float f4)
|
||||
{
|
||||
track_x = f1, track_z = f2, track_x_len = f3, track_z_len = f4;
|
||||
|
||||
unsigned width = (unsigned)hm.size();
|
||||
unsigned height = (unsigned)hm[0].size();
|
||||
float *hm_array = new float[width * height];
|
||||
for (unsigned i = 0; i < width; i++)
|
||||
{
|
||||
for (unsigned j = 0; j < height; j++)
|
||||
{
|
||||
hm_array[i * height + j] = hm[i][j];
|
||||
}
|
||||
}
|
||||
has_height_map = true;
|
||||
|
||||
#if !defined(USE_GLES2)
|
||||
glGenBuffers(1, &heighmapbuffer);
|
||||
glBindBuffer(GL_TEXTURE_BUFFER, heighmapbuffer);
|
||||
glBufferData(GL_TEXTURE_BUFFER, width * height * sizeof(float), hm_array, GL_STREAM_COPY);
|
||||
glGenTextures(1, &heightmaptexture);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, heightmaptexture);
|
||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, heighmapbuffer);
|
||||
glBindBuffer(GL_TEXTURE_BUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, 0);
|
||||
#else
|
||||
glGenTextures(1, &heightmaptexture);
|
||||
glBindTexture(GL_TEXTURE_2D, heightmaptexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, hm_array);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
#endif
|
||||
|
||||
delete[] hm_array;
|
||||
}
|
||||
|
||||
static
|
||||
void generateLifetimeSizeDirection(scene::IParticleEmitter *emitter, float &lifetime, float &size, float &dirX, float &dirY, float &dirZ)
|
||||
{
|
||||
float sizeMin = emitter->getMinStartSize().Height;
|
||||
float sizeMax = emitter->getMaxStartSize().Height;
|
||||
float lifetime_range = float(emitter->getMaxLifeTime() - emitter->getMinLifeTime());
|
||||
|
||||
lifetime = os::Randomizer::frand() * lifetime_range;
|
||||
lifetime += emitter->getMinLifeTime();
|
||||
|
||||
size = os::Randomizer::frand();
|
||||
size *= (sizeMax - sizeMin);
|
||||
size += sizeMin;
|
||||
|
||||
core::vector3df particledir = emitter->getDirection();
|
||||
particledir.rotateXYBy(os::Randomizer::frand() * emitter->getMaxAngleDegrees());
|
||||
particledir.rotateYZBy(os::Randomizer::frand() * emitter->getMaxAngleDegrees());
|
||||
particledir.rotateXZBy(os::Randomizer::frand() * emitter->getMaxAngleDegrees());
|
||||
|
||||
dirX = particledir.X;
|
||||
dirY = particledir.Y;
|
||||
dirZ = particledir.Z;
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::generateParticlesFromPointEmitter(scene::IParticlePointEmitter *emitter)
|
||||
{
|
||||
ParticleData* ParticleParamsTmp = (ParticleData *) realloc(ParticleParams, sizeof(ParticleData) * m_count);
|
||||
ParticleData* InitialValuesTmp = (ParticleData *)realloc(InitialValues, sizeof(ParticleData)* m_count);
|
||||
|
||||
if (ParticleParamsTmp != NULL) // In case memory allocation succeeded
|
||||
{
|
||||
ParticleParams = ParticleParamsTmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("GPUParticles", "Not enough memory for %d from point particles.", m_count);
|
||||
m_count = m_previous_count;
|
||||
}
|
||||
if (InitialValuesTmp != NULL)
|
||||
{
|
||||
InitialValues = InitialValuesTmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::fatal("GPUParticles", "Not enough memory for %d from point particles.", m_count);
|
||||
m_count = m_previous_count;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_count; i++)
|
||||
{
|
||||
ParticleParams[i].PositionX = 0;
|
||||
ParticleParams[i].PositionY = 0;
|
||||
ParticleParams[i].PositionZ = 0;
|
||||
// Initial lifetime is >1
|
||||
InitialValues[i].Lifetime = 2.;
|
||||
|
||||
memcpy(&(InitialValues[i].PositionX), &(ParticleParams[i].PositionX), 3 * sizeof(float));
|
||||
|
||||
generateLifetimeSizeDirection(emitter, ParticleParams[i].Lifetime, ParticleParams[i].Size,
|
||||
ParticleParams[i].DirectionX, ParticleParams[i].DirectionY, ParticleParams[i].DirectionZ);
|
||||
|
||||
memcpy(&(InitialValues[i].DirectionX), &(ParticleParams[i].DirectionX), 4 * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::generateParticlesFromBoxEmitter(scene::IParticleBoxEmitter *emitter)
|
||||
{
|
||||
ParticleData* ParticleParamsTmp = (ParticleData *) realloc(ParticleParams, sizeof(ParticleData) * m_count);
|
||||
ParticleData* InitialValuesTmp = (ParticleData *)realloc(InitialValues, sizeof(ParticleData)* m_count);
|
||||
|
||||
if (ParticleParamsTmp != NULL) // In case memory allocation succeeded
|
||||
{
|
||||
ParticleParams = ParticleParamsTmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("GPUParticles", "Not enough memory for %d from box particles.", m_count);
|
||||
m_count = m_previous_count;
|
||||
}
|
||||
if (InitialValuesTmp != NULL)
|
||||
{
|
||||
InitialValues = InitialValuesTmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("GPUParticles", "Not enough memory for %d from box particles.", m_count);
|
||||
m_count = m_previous_count;
|
||||
}
|
||||
|
||||
const core::vector3df& extent = emitter->getBox().getExtent();
|
||||
|
||||
for (unsigned i = 0; i < m_count; i++)
|
||||
{
|
||||
ParticleParams[i].PositionX = emitter->getBox().MinEdge.X + os::Randomizer::frand() * extent.X;
|
||||
ParticleParams[i].PositionY = emitter->getBox().MinEdge.Y + os::Randomizer::frand() * extent.Y;
|
||||
ParticleParams[i].PositionZ = emitter->getBox().MinEdge.Z + os::Randomizer::frand() * extent.Z;
|
||||
// Initial lifetime is random
|
||||
InitialValues[i].Lifetime = os::Randomizer::frand();
|
||||
if (!m_randomize_initial_y)
|
||||
InitialValues[i].Lifetime += 1.;
|
||||
|
||||
memcpy(&(InitialValues[i].PositionX), &(ParticleParams[i].PositionX), 3 * sizeof(float));
|
||||
generateLifetimeSizeDirection(emitter, ParticleParams[i].Lifetime, ParticleParams[i].Size,
|
||||
ParticleParams[i].DirectionX, ParticleParams[i].DirectionY, ParticleParams[i].DirectionZ);
|
||||
memcpy(&(InitialValues[i].DirectionX), &(ParticleParams[i].DirectionX), 4 * sizeof(float));
|
||||
|
||||
if (m_randomize_initial_y)
|
||||
InitialValues[i].PositionY = os::Randomizer::frand()*50.0f; // -100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::generateParticlesFromSphereEmitter(scene::IParticleSphereEmitter *emitter)
|
||||
{
|
||||
ParticleData* ParticleParamsTmp = (ParticleData *) realloc(ParticleParams, sizeof(ParticleData) * m_count);
|
||||
ParticleData* InitialValuesTmp = (ParticleData *)realloc(InitialValues, sizeof(ParticleData)* m_count);
|
||||
|
||||
if(ParticleParamsTmp != NULL) // In case memory allocation succeeded
|
||||
ParticleParams = ParticleParamsTmp;
|
||||
if(InitialValuesTmp != NULL)
|
||||
InitialValues = InitialValuesTmp;
|
||||
|
||||
for (unsigned i = 0; i < m_count; i++) {
|
||||
// Random distance from center
|
||||
const f32 distance = os::Randomizer::frand() * emitter->getRadius();
|
||||
|
||||
// Random direction from center
|
||||
vector3df pos = emitter->getCenter() + distance;
|
||||
pos.rotateXYBy(os::Randomizer::frand() * 360.f, emitter->getCenter());
|
||||
pos.rotateYZBy(os::Randomizer::frand() * 360.f, emitter->getCenter());
|
||||
pos.rotateXZBy(os::Randomizer::frand() * 360.f, emitter->getCenter());
|
||||
|
||||
ParticleParams[i].PositionX = pos.X;
|
||||
ParticleParams[i].PositionY = pos.Y;
|
||||
ParticleParams[i].PositionZ = pos.Z;
|
||||
// Initial lifetime is > 1
|
||||
InitialValues[i].Lifetime = 2.;
|
||||
|
||||
memcpy(&(InitialValues[i].PositionX), &(ParticleParams[i].PositionX), 3 * sizeof(float));
|
||||
generateLifetimeSizeDirection(emitter, ParticleParams[i].Lifetime, ParticleParams[i].Size,
|
||||
ParticleParams[i].DirectionX, ParticleParams[i].DirectionY, ParticleParams[i].DirectionZ);
|
||||
memcpy(&(InitialValues[i].DirectionX), &(ParticleParams[i].DirectionX), 4 * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
static bool isGPUParticleType(scene::E_PARTICLE_EMITTER_TYPE type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case scene::EPET_POINT:
|
||||
case scene::EPET_BOX:
|
||||
case scene::EPET_SPHERE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::setEmitter(scene::IParticleEmitter* emitter)
|
||||
{
|
||||
CParticleSystemSceneNode::setEmitter(emitter);
|
||||
if (!emitter || !isGPUParticleType(emitter->getType()))
|
||||
return;
|
||||
if (!m_first_execution)
|
||||
cleanGL();
|
||||
has_height_map = false;
|
||||
flip = false;
|
||||
m_first_execution = true;
|
||||
|
||||
m_previous_count = m_count; // save to handle out of memory errors
|
||||
m_count = emitter->getMaxParticlesPerSecond() * emitter->getMaxLifeTime() / 1000;
|
||||
|
||||
switch (emitter->getType())
|
||||
{
|
||||
case scene::EPET_POINT:
|
||||
generateParticlesFromPointEmitter(emitter);
|
||||
break;
|
||||
case scene::EPET_BOX:
|
||||
generateParticlesFromBoxEmitter(static_cast<scene::IParticleBoxEmitter *>(emitter));
|
||||
break;
|
||||
case scene::EPET_SPHERE:
|
||||
generateParticlesFromSphereEmitter(static_cast<scene::IParticleSphereEmitter *>(emitter));
|
||||
break;
|
||||
default:
|
||||
assert(0 && "Wrong particle type");
|
||||
}
|
||||
|
||||
if (CVS->needsVertexIdWorkaround())
|
||||
{
|
||||
if (m_count != m_previous_count)
|
||||
{
|
||||
delete[] m_vertex_id_values;
|
||||
m_vertex_id_values = NULL;
|
||||
}
|
||||
|
||||
if (m_vertex_id_values == NULL && m_count > 0)
|
||||
{
|
||||
m_vertex_id_values = new int[m_count];
|
||||
|
||||
for (unsigned int i = 0; i < m_count; i++)
|
||||
{
|
||||
m_vertex_id_values[i] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_texture_name = getMaterial(0).getTexture(0)->getOpenGLTextureName();
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::cleanGL()
|
||||
{
|
||||
if (flip)
|
||||
glDeleteBuffers(1, &quaternionsbuffer);
|
||||
glDeleteBuffers(2, tfb_buffers);
|
||||
glDeleteBuffers(1, &initial_values_buffer);
|
||||
glDeleteVertexArrays(1, ¤t_rendering_vao);
|
||||
glDeleteVertexArrays(1, &non_current_rendering_vao);
|
||||
glDeleteVertexArrays(1, ¤t_simulation_vao);
|
||||
glDeleteVertexArrays(1, &non_current_simulation_vao);
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::CommonRenderingVAO(GLuint PositionBuffer)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, SharedGPUObjects::getParticleQuadVBO());
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (GLvoid *)(2 * sizeof(float)));
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, PositionBuffer);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ParticleData), 0);
|
||||
glVertexAttribDivisorARB(0, 1);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, sizeof(ParticleData), (GLvoid *)(3 * sizeof(float)));
|
||||
glVertexAttribDivisorARB(1, 1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(ParticleData), (GLvoid *)(7 * sizeof(float)));
|
||||
glVertexAttribDivisorARB(2, 1);
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::AppendQuaternionRenderingVAO(GLuint QuaternionBuffer)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, QuaternionBuffer);
|
||||
glEnableVertexAttribArray(5);
|
||||
|
||||
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
|
||||
glVertexAttribDivisorARB(5, 1);
|
||||
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (GLvoid *)(3 * sizeof(float)));
|
||||
glVertexAttribDivisorARB(6, 1);
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::CommonSimulationVAO(GLuint position_vbo, GLuint initialValues_vbo, GLuint vertex_id_buffer)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, position_vbo);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)(3 * sizeof(float)));
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)(4 * sizeof(float)));
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)(7 * sizeof(float)));
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, initialValues_vbo);
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)0);
|
||||
glEnableVertexAttribArray(5);
|
||||
glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)(3 * sizeof(float)));
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)(4 * sizeof(float)));
|
||||
glEnableVertexAttribArray(7);
|
||||
glVertexAttribPointer(7, 1, GL_FLOAT, GL_FALSE, sizeof(ParticleSystemProxy::ParticleData), (GLvoid*)(7 * sizeof(float)));
|
||||
|
||||
if (CVS->needsVertexIdWorkaround())
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_id_buffer);
|
||||
glEnableVertexAttribArray(8);
|
||||
glVertexAttribIPointer(8, 1, GL_INT, sizeof(int), (GLvoid*)0);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::simulate()
|
||||
{
|
||||
int timediff = int(GUIEngine::getLatestDt() * 1000.f);
|
||||
int active_count = getEmitter()->getMaxLifeTime() * getEmitter()->getMaxParticlesPerSecond() / 1000;
|
||||
core::matrix4 matrix = getAbsoluteTransformation();
|
||||
|
||||
glEnable(GL_RASTERIZER_DISCARD);
|
||||
if (has_height_map)
|
||||
{
|
||||
HeightmapSimulationShader::getInstance()->use();
|
||||
HeightmapSimulationShader::getInstance()->setTextureUnits(heightmaptexture);
|
||||
HeightmapSimulationShader::getInstance()->setUniforms(matrix, timediff, active_count, size_increase_factor, track_x, track_x_len, track_z, track_z_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
PointEmitterShader::getInstance()->use();
|
||||
PointEmitterShader::getInstance()->setUniforms(m_previous_frame_matrix, matrix, timediff, active_count, size_increase_factor);
|
||||
}
|
||||
m_previous_frame_matrix = matrix;
|
||||
|
||||
glBindVertexArray(current_simulation_vao);
|
||||
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfb_buffers[1]);
|
||||
|
||||
glBeginTransformFeedback(GL_POINTS);
|
||||
glDrawArrays(GL_POINTS, 0, m_count);
|
||||
glEndTransformFeedback();
|
||||
glBindVertexArray(0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glDisable(GL_RASTERIZER_DISCARD);
|
||||
#ifdef DEBUG_PARTICLES
|
||||
// This code maps the data from the particles (initial data, current data
|
||||
// and output of transform feedback shader) into memory. Useful for debugging.
|
||||
if (std::string(getName()) == std::string("particles(C:/Users/jhenrich/supertuxkart/stk-assets/textures/skid-particle1.png)"))
|
||||
{
|
||||
glBindVertexArray(current_simulation_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, initial_values_buffer);
|
||||
ParticleData *p_initial = (ParticleData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, m_count*sizeof(ParticleData), GL_MAP_READ_BIT);
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_buffers[0]);
|
||||
ParticleData *p_prev = (ParticleData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, m_count*sizeof(ParticleData), GL_MAP_READ_BIT);
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_buffers[1]);
|
||||
ParticleData *p_new = (ParticleData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, m_count*sizeof(ParticleData), GL_MAP_READ_BIT);
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::swap(tfb_buffers[0], tfb_buffers[1]);
|
||||
std::swap(current_rendering_vao, non_current_rendering_vao);
|
||||
std::swap(current_simulation_vao, non_current_simulation_vao);
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::drawFlip()
|
||||
{
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
FlipParticleRender::getInstance()->use();
|
||||
|
||||
FlipParticleRender::getInstance()->setTextureUnits(m_texture_name, irr_driver->getDepthStencilTexture());
|
||||
FlipParticleRender::getInstance()->setUniforms();
|
||||
|
||||
glBindVertexArray(current_rendering_vao);
|
||||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, m_count);
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::drawNotFlip()
|
||||
{
|
||||
if (m_alpha_additive)
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
else
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
SimpleParticleRender::getInstance()->use();
|
||||
|
||||
SimpleParticleRender::getInstance()->setTextureUnits(m_texture_name, irr_driver->getDepthStencilTexture());
|
||||
video::SColorf ColorFrom = video::SColorf(getColorFrom()[0], getColorFrom()[1], getColorFrom()[2]);
|
||||
video::SColorf ColorTo = video::SColorf(getColorTo()[0], getColorTo()[1], getColorTo()[2]);
|
||||
|
||||
SimpleParticleRender::getInstance()->setUniforms(ColorFrom, ColorTo);
|
||||
|
||||
glBindVertexArray(current_rendering_vao);
|
||||
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, m_count);
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::draw()
|
||||
{
|
||||
if (flip)
|
||||
drawFlip();
|
||||
else
|
||||
drawNotFlip();
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::generateVAOs()
|
||||
{
|
||||
glBindVertexArray(0);
|
||||
glGenBuffers(1, &initial_values_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, initial_values_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_count * sizeof(ParticleData), ParticleParams, GL_STREAM_COPY);
|
||||
glGenBuffers(2, tfb_buffers);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_buffers[0]);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_count * sizeof(ParticleData), InitialValues, GL_STREAM_COPY);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_buffers[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_count * sizeof(ParticleData), 0, GL_STREAM_COPY);
|
||||
|
||||
if (CVS->needsVertexIdWorkaround())
|
||||
{
|
||||
glGenBuffers(1, &vertex_id_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vertex_id_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_count * sizeof(int),
|
||||
m_vertex_id_values, GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glGenVertexArrays(1, ¤t_rendering_vao);
|
||||
glGenVertexArrays(1, &non_current_rendering_vao);
|
||||
glGenVertexArrays(1, ¤t_simulation_vao);
|
||||
glGenVertexArrays(1, &non_current_simulation_vao);
|
||||
|
||||
// TODO: Don't execute CommonSimulationVAO twice because it unnecessarily
|
||||
// initializes initial_values_buffer and vertex_id_buffer twice.
|
||||
glBindVertexArray(current_simulation_vao);
|
||||
CommonSimulationVAO(tfb_buffers[0], initial_values_buffer, vertex_id_buffer);
|
||||
glBindVertexArray(non_current_simulation_vao);
|
||||
CommonSimulationVAO(tfb_buffers[1], initial_values_buffer, vertex_id_buffer);
|
||||
|
||||
|
||||
glBindVertexArray(0);
|
||||
if (flip)
|
||||
{
|
||||
float *quaternions = new float[4 * m_count];
|
||||
for (unsigned i = 0; i < m_count; i++)
|
||||
{
|
||||
core::vector3df rotationdir(0., 1., 0.);
|
||||
|
||||
quaternions[4 * i] = rotationdir.X;
|
||||
quaternions[4 * i + 1] = rotationdir.Y;
|
||||
quaternions[4 * i + 2] = rotationdir.Z;
|
||||
quaternions[4 * i + 3] = 3.14f * 3.f * (2.f * os::Randomizer::frand() - 1.f); // 3 half rotation during lifetime at max
|
||||
}
|
||||
glGenBuffers(1, &quaternionsbuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quaternionsbuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, 4 * m_count * sizeof(float), quaternions, GL_STREAM_COPY);
|
||||
delete[] quaternions;
|
||||
}
|
||||
|
||||
glBindVertexArray(current_rendering_vao);
|
||||
CommonRenderingVAO(tfb_buffers[0]);
|
||||
if (flip)
|
||||
AppendQuaternionRenderingVAO(quaternionsbuffer);
|
||||
|
||||
glBindVertexArray(non_current_rendering_vao);
|
||||
CommonRenderingVAO(tfb_buffers[1]);
|
||||
if (flip)
|
||||
AppendQuaternionRenderingVAO(quaternionsbuffer);
|
||||
glBindVertexArray(0);
|
||||
|
||||
}
|
||||
|
||||
void ParticleSystemProxy::render() {
|
||||
if (!getEmitter() || !isGPUParticleType(getEmitter()->getType()))
|
||||
{
|
||||
CParticleSystemSceneNode::render();
|
||||
return;
|
||||
}
|
||||
if (m_first_execution)
|
||||
{
|
||||
generateVAOs();
|
||||
m_previous_frame_matrix = getAbsoluteTransformation();
|
||||
m_first_execution = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
simulate();
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SERVER_ONLY
|
@ -1,119 +0,0 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2014-2015 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#ifndef HEADER_GPU_PARTICLES_HPP
|
||||
#define HEADER_GPU_PARTICLES_HPP
|
||||
|
||||
#include "graphics/shader.hpp"
|
||||
|
||||
#include "../lib/irrlicht/source/Irrlicht/CParticleSystemSceneNode.h"
|
||||
#include <ISceneManager.h>
|
||||
#include <IParticleSystemSceneNode.h>
|
||||
|
||||
namespace irr { namespace video{ class ITexture; } }
|
||||
|
||||
using namespace irr;
|
||||
|
||||
class ParticleSystemProxy : public scene::CParticleSystemSceneNode
|
||||
{
|
||||
protected:
|
||||
GLuint tfb_buffers[2], initial_values_buffer, heighmapbuffer,
|
||||
heightmaptexture, quaternionsbuffer, vertex_id_buffer;
|
||||
GLuint current_simulation_vao, non_current_simulation_vao;
|
||||
GLuint current_rendering_vao, non_current_rendering_vao;
|
||||
bool m_alpha_additive, has_height_map, flip;
|
||||
float size_increase_factor, track_x, track_z, track_x_len, track_z_len;
|
||||
float m_color_from[3];
|
||||
float m_color_to[3];
|
||||
bool m_first_execution;
|
||||
bool m_randomize_initial_y;
|
||||
|
||||
GLuint m_texture_name;
|
||||
|
||||
/** Previous frame particles emitter source matrix */
|
||||
core::matrix4 m_previous_frame_matrix;
|
||||
|
||||
/** Current count of particles. */
|
||||
unsigned m_count;
|
||||
/** Previous count - for error handling only. */
|
||||
unsigned m_previous_count;
|
||||
|
||||
static void CommonRenderingVAO(GLuint PositionBuffer);
|
||||
static void AppendQuaternionRenderingVAO(GLuint QuaternionBuffer);
|
||||
static void CommonSimulationVAO(GLuint position_vbo,
|
||||
GLuint initialValues_vbo,
|
||||
GLuint vertex_id_buffer);
|
||||
|
||||
void generateVAOs();
|
||||
void cleanGL();
|
||||
|
||||
void drawFlip();
|
||||
void drawNotFlip();
|
||||
virtual void simulate();
|
||||
virtual void draw();
|
||||
|
||||
struct ParticleData
|
||||
{
|
||||
float PositionX;
|
||||
float PositionY;
|
||||
float PositionZ;
|
||||
float Lifetime;
|
||||
float DirectionX;
|
||||
float DirectionY;
|
||||
float DirectionZ;
|
||||
float Size;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
ParticleData *ParticleParams, *InitialValues;
|
||||
int* m_vertex_id_values;
|
||||
void generateParticlesFromPointEmitter(scene::IParticlePointEmitter *);
|
||||
void generateParticlesFromBoxEmitter(scene::IParticleBoxEmitter *);
|
||||
void generateParticlesFromSphereEmitter(scene::IParticleSphereEmitter *);
|
||||
public:
|
||||
static IParticleSystemSceneNode *addParticleNode(
|
||||
bool withDefaultEmitter = true, bool randomize_initial_y = false, ISceneNode* parent = 0, s32 id = -1,
|
||||
const core::vector3df& position = core::vector3df(0, 0, 0),
|
||||
const core::vector3df& rotation = core::vector3df(0, 0, 0),
|
||||
const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f));
|
||||
|
||||
ParticleSystemProxy(bool createDefaultEmitter,
|
||||
ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
|
||||
const core::vector3df& position,
|
||||
const core::vector3df& rotation,
|
||||
const core::vector3df& scale,
|
||||
bool randomize_initial_y);
|
||||
~ParticleSystemProxy();
|
||||
|
||||
virtual void setEmitter(scene::IParticleEmitter* emitter);
|
||||
virtual void render();
|
||||
void setAlphaAdditive(bool val) { m_alpha_additive = val; }
|
||||
void setIncreaseFactor(float val) { size_increase_factor = val; }
|
||||
void setColorFrom(float r, float g, float b) { m_color_from[0] = r; m_color_from[1] = g; m_color_from[2] = b; }
|
||||
void setColorTo(float r, float g, float b) { m_color_to[0] = r; m_color_to[1] = g; m_color_to[2] = b; }
|
||||
const float* getColorFrom() const { return m_color_from; }
|
||||
const float* getColorTo() const { return m_color_to; }
|
||||
void setHeightmap(const std::vector<std::vector<float> >&, float, float, float, float);
|
||||
void setFlip();
|
||||
};
|
||||
|
||||
#endif // GPUPARTICLES_H
|
||||
|
||||
#endif // !SERVER_ONLY
|
@ -21,11 +21,11 @@
|
||||
#include "graphics/particle_emitter.hpp"
|
||||
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/gpu_particles.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
#include "graphics/particle_kind.hpp"
|
||||
#include "graphics/stk_particle.hpp"
|
||||
#include "graphics/wind.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
@ -271,7 +271,9 @@ public:
|
||||
{
|
||||
const u32 maxdiff = particlearray[i].endTime - particlearray[i].startTime;
|
||||
const u32 curdiff = now - particlearray[i].startTime;
|
||||
const f32 timefraction = (f32)curdiff / maxdiff;
|
||||
f32 timefraction = 0.0f;
|
||||
if (maxdiff > 0)
|
||||
timefraction = (f32)curdiff / maxdiff;
|
||||
core::vector3df curr_color = m_color_from + (m_color_to - m_color_from)* timefraction;
|
||||
particlearray[i].color = video::SColor(255, (int)curr_color.X, (int)curr_color.Y, (int)curr_color.Z);
|
||||
}
|
||||
@ -464,19 +466,9 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
|
||||
else
|
||||
{
|
||||
if (m_is_glsl)
|
||||
m_node = ParticleSystemProxy::addParticleNode(m_is_glsl, type->randomizeInitialY());
|
||||
m_node = STKParticle::addParticleNode(m_is_glsl, type->randomizeInitialY());
|
||||
else
|
||||
m_node = irr_driver->addParticleNode();
|
||||
|
||||
if (m_is_glsl)
|
||||
{
|
||||
Material* material = type->getMaterial();
|
||||
if (material != nullptr)
|
||||
{
|
||||
bool additive = (material->getShaderType() == Material::SHADERTYPE_ADDITIVE);
|
||||
static_cast<ParticleSystemProxy *>(m_node)->setAlphaAdditive(additive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_parent != NULL)
|
||||
@ -645,7 +637,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
|
||||
{
|
||||
if (m_is_glsl)
|
||||
{
|
||||
static_cast<ParticleSystemProxy *>(m_node)->setIncreaseFactor(type->getScaleAffectorFactorX());
|
||||
static_cast<STKParticle*>(m_node)->setIncreaseFactor(type->getScaleAffectorFactorX());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -662,12 +654,12 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
|
||||
if (m_is_glsl)
|
||||
{
|
||||
video::SColor color_from = type->getMinColor();
|
||||
static_cast<ParticleSystemProxy *>(m_node)->setColorFrom(color_from.getRed() / 255.0f,
|
||||
static_cast<STKParticle*>(m_node)->setColorFrom(color_from.getRed() / 255.0f,
|
||||
color_from.getGreen() / 255.0f,
|
||||
color_from.getBlue() / 255.0f);
|
||||
|
||||
video::SColor color_to = type->getMaxColor();
|
||||
static_cast<ParticleSystemProxy *>(m_node)->setColorTo(color_to.getRed() / 255.0f,
|
||||
static_cast<STKParticle*>(m_node)->setColorTo(color_to.getRed() / 255.0f,
|
||||
color_to.getGreen() / 255.0f,
|
||||
color_to.getBlue() / 255.0f);
|
||||
}
|
||||
@ -704,7 +696,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
|
||||
if (flips)
|
||||
{
|
||||
if (m_is_glsl)
|
||||
static_cast<ParticleSystemProxy *>(m_node)->setFlip();
|
||||
static_cast<STKParticle*>(m_node)->setFlips();
|
||||
}
|
||||
}
|
||||
} // setParticleType
|
||||
@ -722,7 +714,8 @@ void ParticleEmitter::addHeightMapAffector(Track* t)
|
||||
float track_z = aabb_min->getZ();
|
||||
const float track_x_len = aabb_max->getX() - aabb_min->getX();
|
||||
const float track_z_len = aabb_max->getZ() - aabb_min->getZ();
|
||||
static_cast<ParticleSystemProxy *>(m_node)->setHeightmap(t->buildHeightMap(),
|
||||
std::vector<std::vector<float> > array = t->buildHeightMap();
|
||||
static_cast<STKParticle*>(m_node)->setHeightmap(array,
|
||||
track_x, track_z, track_x_len, track_z_len);
|
||||
}
|
||||
else
|
||||
|
@ -261,7 +261,11 @@ Material* ParticleKind::getMaterial() const
|
||||
if (material_manager->hasMaterial(m_material_file))
|
||||
{
|
||||
Material* material = material_manager->getMaterial(m_material_file);
|
||||
if (material == NULL || material->getTexture(true/*srgb*/, true/*premul_alpha*/) == NULL)
|
||||
if (material == NULL ||
|
||||
material->getTexture(true/*srgb*/,
|
||||
material->getShaderType() == Material::SHADERTYPE_ADDITIVE ||
|
||||
material->getShaderType() == Material::SHADERTYPE_ALPHA_BLEND ?
|
||||
true : false/*premul_alpha*/) == NULL)
|
||||
{
|
||||
throw std::runtime_error("[ParticleKind] Cannot locate file " + m_material_file);
|
||||
}
|
||||
|
@ -512,13 +512,7 @@ void ShaderBasedRenderer::renderScene(scene::ICameraSceneNode * const camnode,
|
||||
// ----------------------------------------------------------------------------
|
||||
void ShaderBasedRenderer::renderParticles()
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
m_draw_calls.renderParticlesList();
|
||||
|
||||
// m_scene_manager->drawAll(scene::ESNRP_TRANSPARENT_EFFECT);
|
||||
} //renderParticles
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -971,9 +965,10 @@ void ShaderBasedRenderer::preloadShaderFiles()
|
||||
|
||||
sfm->addShaderFile("billboard.vert", GL_VERTEX_SHADER);
|
||||
sfm->addShaderFile("billboard.frag", GL_FRAGMENT_SHADER);
|
||||
sfm->addShaderFile("pointemitter.vert", GL_VERTEX_SHADER);
|
||||
sfm->addShaderFile("particle.vert", GL_VERTEX_SHADER);
|
||||
sfm->addShaderFile("particle.frag", GL_FRAGMENT_SHADER);
|
||||
sfm->addShaderFile("alphatest_particle.vert", GL_VERTEX_SHADER);
|
||||
sfm->addShaderFile("alphatest_particle.frag", GL_FRAGMENT_SHADER);
|
||||
sfm->addShaderFile("simple_particle.vert", GL_VERTEX_SHADER);
|
||||
sfm->addShaderFile("simple_particle.frag", GL_FRAGMENT_SHADER);
|
||||
|
||||
if (CVS->supportsIndirectInstancingRendering())
|
||||
{
|
||||
|
@ -97,7 +97,6 @@
|
||||
#include "graphics/callbacks.hpp"
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/glwrap.hpp"
|
||||
#include "graphics/gpu_particles.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/shared_gpu_objects.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
|
490
src/graphics/stk_particle.cpp
Normal file
490
src/graphics/stk_particle.cpp
Normal file
@ -0,0 +1,490 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2017 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "graphics/stk_particle.hpp"
|
||||
#include "graphics/cpu_particle_manager.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "guiengine/engine.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include "../../lib/irrlicht/source/Irrlicht/os.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
std::vector<Vec3> STKParticle::m_flips_data;
|
||||
GLuint STKParticle::m_flips_buffer = 0;
|
||||
// ----------------------------------------------------------------------------
|
||||
scene::IParticleSystemSceneNode* STKParticle::addParticleNode(
|
||||
bool withDefaultEmitter, bool randomize_initial_y, ISceneNode* parent,
|
||||
s32 id, const core::vector3df& position, const core::vector3df& rotation,
|
||||
const core::vector3df& scale)
|
||||
{
|
||||
if (!parent)
|
||||
{
|
||||
parent = irr_driver->getSceneManager()->getRootSceneNode();
|
||||
}
|
||||
IParticleSystemSceneNode* node = new STKParticle(withDefaultEmitter,
|
||||
parent, irr_driver->getSceneManager(), id, position, rotation, scale,
|
||||
randomize_initial_y);
|
||||
node->drop();
|
||||
|
||||
return node;
|
||||
} // addParticleNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
STKParticle::STKParticle(bool createDefaultEmitter,
|
||||
ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
|
||||
const core::vector3df& position,
|
||||
const core::vector3df& rotation,
|
||||
const core::vector3df& scale,
|
||||
bool randomize_initial_y)
|
||||
: CParticleSystemSceneNode(createDefaultEmitter, parent, mgr, id,
|
||||
position, rotation, scale)
|
||||
{
|
||||
m_hm = NULL;
|
||||
m_color_to = video::SColorf(video::SColor(-1));
|
||||
m_color_from = m_color_to;
|
||||
m_size_increase_factor = 0.0f;
|
||||
m_first_execution = true;
|
||||
m_randomize_initial_y = randomize_initial_y;
|
||||
m_flips = false;
|
||||
m_max_count = 0;
|
||||
} // STKParticle
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static void generateLifetimeSizeDirection(scene::IParticleEmitter *emitter,
|
||||
float& lifetime, float& size,
|
||||
core::vector3df& direction)
|
||||
{
|
||||
float size_min = emitter->getMinStartSize().Height;
|
||||
float size_max = emitter->getMaxStartSize().Height;
|
||||
float lifetime_range =
|
||||
float(emitter->getMaxLifeTime() - emitter->getMinLifeTime());
|
||||
|
||||
lifetime = os::Randomizer::frand() * lifetime_range;
|
||||
lifetime += emitter->getMinLifeTime();
|
||||
|
||||
size = os::Randomizer::frand();
|
||||
size *= (size_max - size_min);
|
||||
size += size_min;
|
||||
|
||||
core::vector3df particledir = emitter->getDirection();
|
||||
particledir.rotateXYBy(os::Randomizer::frand() *
|
||||
emitter->getMaxAngleDegrees());
|
||||
particledir.rotateYZBy(os::Randomizer::frand() *
|
||||
emitter->getMaxAngleDegrees());
|
||||
particledir.rotateXZBy(os::Randomizer::frand() *
|
||||
emitter->getMaxAngleDegrees());
|
||||
direction = particledir;
|
||||
} // generateLifetimeSizeDirection
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::generateParticlesFromPointEmitter
|
||||
(scene::IParticlePointEmitter *emitter)
|
||||
{
|
||||
m_particles_generating.clear();
|
||||
m_initial_particles.clear();
|
||||
m_particles_generating.resize(m_max_count);
|
||||
m_initial_particles.resize(m_max_count);
|
||||
for (unsigned i = 0; i < m_max_count; i++)
|
||||
{
|
||||
// Initial lifetime is > 1
|
||||
m_particles_generating[i].m_lifetime = 2.0f;
|
||||
|
||||
generateLifetimeSizeDirection(emitter,
|
||||
m_initial_particles[i].m_lifetime,
|
||||
m_particles_generating[i].m_size,
|
||||
m_particles_generating[i].m_direction);
|
||||
|
||||
m_initial_particles[i].m_direction =
|
||||
m_particles_generating[i].m_direction;
|
||||
m_initial_particles[i].m_size = m_particles_generating[i].m_size;
|
||||
}
|
||||
} // generateParticlesFromPointEmitter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::generateParticlesFromBoxEmitter
|
||||
(scene::IParticleBoxEmitter *emitter)
|
||||
{
|
||||
m_particles_generating.clear();
|
||||
m_initial_particles.clear();
|
||||
m_particles_generating.resize(m_max_count);
|
||||
m_initial_particles.resize(m_max_count);
|
||||
const core::vector3df& extent = emitter->getBox().getExtent();
|
||||
for (unsigned i = 0; i < m_max_count; i++)
|
||||
{
|
||||
m_particles_generating[i].m_position.X =
|
||||
emitter->getBox().MinEdge.X + os::Randomizer::frand() * extent.X;
|
||||
m_particles_generating[i].m_position.Y =
|
||||
emitter->getBox().MinEdge.Y + os::Randomizer::frand() * extent.Y;
|
||||
m_particles_generating[i].m_position.Z =
|
||||
emitter->getBox().MinEdge.Z + os::Randomizer::frand() * extent.Z;
|
||||
|
||||
// Initial lifetime is random
|
||||
m_particles_generating[i].m_lifetime = os::Randomizer::frand();
|
||||
if (!m_randomize_initial_y)
|
||||
{
|
||||
m_particles_generating[i].m_lifetime += 1.0f;
|
||||
}
|
||||
m_initial_particles[i].m_position =
|
||||
m_particles_generating[i].m_position;
|
||||
|
||||
generateLifetimeSizeDirection(emitter,
|
||||
m_initial_particles[i].m_lifetime,
|
||||
m_particles_generating[i].m_size,
|
||||
m_particles_generating[i].m_direction);
|
||||
|
||||
m_initial_particles[i].m_direction =
|
||||
m_particles_generating[i].m_direction;
|
||||
m_initial_particles[i].m_size = m_particles_generating[i].m_size;
|
||||
|
||||
if (m_randomize_initial_y)
|
||||
{
|
||||
m_initial_particles[i].m_position.Y =
|
||||
os::Randomizer::frand() * 50.0f; // -100.0f;
|
||||
}
|
||||
}
|
||||
} // generateParticlesFromBoxEmitter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::generateParticlesFromSphereEmitter
|
||||
(scene::IParticleSphereEmitter *emitter)
|
||||
{
|
||||
m_particles_generating.clear();
|
||||
m_initial_particles.clear();
|
||||
m_particles_generating.resize(m_max_count);
|
||||
m_initial_particles.resize(m_max_count);
|
||||
for (unsigned i = 0; i < m_max_count; i++)
|
||||
{
|
||||
// Random distance from center
|
||||
const f32 distance = os::Randomizer::frand() * emitter->getRadius();
|
||||
|
||||
// Random direction from center
|
||||
vector3df pos = emitter->getCenter() + distance;
|
||||
pos.rotateXYBy(os::Randomizer::frand() * 360.f, emitter->getCenter());
|
||||
pos.rotateYZBy(os::Randomizer::frand() * 360.f, emitter->getCenter());
|
||||
pos.rotateXZBy(os::Randomizer::frand() * 360.f, emitter->getCenter());
|
||||
|
||||
m_particles_generating[i].m_position = pos;
|
||||
|
||||
// Initial lifetime is > 1
|
||||
m_particles_generating[i].m_lifetime = 2.0f;
|
||||
m_initial_particles[i].m_position =
|
||||
m_particles_generating[i].m_position;
|
||||
|
||||
generateLifetimeSizeDirection(emitter,
|
||||
m_initial_particles[i].m_lifetime,
|
||||
m_particles_generating[i].m_size,
|
||||
m_particles_generating[i].m_direction);
|
||||
|
||||
m_initial_particles[i].m_direction =
|
||||
m_particles_generating[i].m_direction;
|
||||
m_initial_particles[i].m_size = m_particles_generating[i].m_size;
|
||||
}
|
||||
} // generateParticlesFromSphereEmitter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static bool isSTKParticleType(scene::E_PARTICLE_EMITTER_TYPE type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case scene::EPET_POINT:
|
||||
case scene::EPET_BOX:
|
||||
case scene::EPET_SPHERE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} // isSTKParticleType
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::setEmitter(scene::IParticleEmitter* emitter)
|
||||
{
|
||||
CParticleSystemSceneNode::setEmitter(emitter);
|
||||
if (!emitter || !isSTKParticleType(emitter->getType()))
|
||||
{
|
||||
CParticleSystemSceneNode::setEmitter(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
delete m_hm;
|
||||
m_hm = NULL;
|
||||
m_first_execution = true;
|
||||
m_flips = false;
|
||||
m_max_count = emitter->getMaxParticlesPerSecond() * emitter->getMaxLifeTime() / 1000;
|
||||
|
||||
switch (emitter->getType())
|
||||
{
|
||||
case scene::EPET_POINT:
|
||||
generateParticlesFromPointEmitter(emitter);
|
||||
break;
|
||||
case scene::EPET_BOX:
|
||||
generateParticlesFromBoxEmitter
|
||||
(static_cast<scene::IParticleBoxEmitter*>(emitter));
|
||||
break;
|
||||
case scene::EPET_SPHERE:
|
||||
generateParticlesFromSphereEmitter
|
||||
(static_cast<scene::IParticleSphereEmitter*>(emitter));
|
||||
break;
|
||||
default:
|
||||
assert(false && "Wrong particle type");
|
||||
}
|
||||
} // setEmitter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::generate(std::vector<CPUParticle>* out)
|
||||
{
|
||||
if (!getEmitter())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Buffer->BoundingBox.reset(AbsoluteTransformation.getTranslation());
|
||||
if (m_first_execution)
|
||||
{
|
||||
m_previous_frame_matrix = AbsoluteTransformation;
|
||||
m_first_execution = false;
|
||||
}
|
||||
|
||||
float dt = GUIEngine::getLatestDt() * 1000.f;
|
||||
int active_count = getEmitter()->getMaxLifeTime() *
|
||||
getEmitter()->getMaxParticlesPerSecond() / 1000;
|
||||
|
||||
if (m_hm != NULL)
|
||||
{
|
||||
stimulateHeightMap(dt, active_count, out);
|
||||
}
|
||||
else
|
||||
{
|
||||
stimulateNormal(dt, active_count, out);
|
||||
}
|
||||
m_previous_frame_matrix = AbsoluteTransformation;
|
||||
|
||||
core::matrix4 inv(AbsoluteTransformation, core::matrix4::EM4CONST_INVERSE);
|
||||
inv.transformBoxEx(Buffer->BoundingBox);
|
||||
|
||||
} // generate
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
inline float glslFract(float val)
|
||||
{
|
||||
return val - (float)floor(val);
|
||||
} // glslFract
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
inline float glslMix(float x, float y, float a)
|
||||
{
|
||||
return x * (1.0f - a) + y * a;
|
||||
} // glslMix
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::stimulateHeightMap(float dt, unsigned int active_count,
|
||||
std::vector<CPUParticle>* out)
|
||||
{
|
||||
assert(m_hm != NULL);
|
||||
const core::matrix4 cur_matrix = AbsoluteTransformation;
|
||||
for (unsigned i = 0; i < m_max_count; i++)
|
||||
{
|
||||
core::vector3df new_particle_position;
|
||||
core::vector3df new_particle_direction;
|
||||
float new_size = 0.0f;
|
||||
float new_lifetime = 0.0f;
|
||||
|
||||
const core::vector3df particle_position =
|
||||
m_particles_generating[i].m_position;
|
||||
const float lifetime = m_particles_generating[i].m_lifetime;
|
||||
const core::vector3df particle_direction =
|
||||
m_particles_generating[i].m_direction;
|
||||
|
||||
const core::vector3df particle_position_initial =
|
||||
m_initial_particles[i].m_position;
|
||||
const float lifetime_initial = m_initial_particles[i].m_lifetime;
|
||||
const core::vector3df particle_direction_initial =
|
||||
m_initial_particles[i].m_direction;
|
||||
const float size_initial = m_initial_particles[i].m_size;
|
||||
|
||||
bool reset = false;
|
||||
const int px = core::clamp((int)(256.0f *
|
||||
(particle_position.X - m_hm->m_x) / m_hm->m_x_len), 0, 255);
|
||||
const int py = core::clamp((int)(256.0f *
|
||||
(particle_position.Z - m_hm->m_z) / m_hm->m_z_len), 0, 255);
|
||||
const float h = particle_position.Y - m_hm->m_array[px][py];
|
||||
reset = h < 0.0f;
|
||||
|
||||
core::vector3df initial_position, initial_new_position;
|
||||
cur_matrix.transformVect(initial_position, particle_position_initial);
|
||||
cur_matrix.transformVect(initial_new_position,
|
||||
particle_position_initial + particle_direction_initial);
|
||||
|
||||
core::vector3df adjusted_initial_direction =
|
||||
initial_new_position - initial_position;
|
||||
float adjusted_lifetime = lifetime + (dt / lifetime_initial);
|
||||
reset = reset || ((adjusted_lifetime > 1.0f) && (i <= active_count));
|
||||
reset = reset || (lifetime < 0.0f);
|
||||
|
||||
new_particle_position = !reset ?
|
||||
(particle_position + particle_direction * dt) : initial_position;
|
||||
new_lifetime = !reset ? adjusted_lifetime : 0.0f;
|
||||
new_particle_direction = !reset ?
|
||||
particle_direction : adjusted_initial_direction;
|
||||
new_size = !reset ?
|
||||
glslMix(size_initial, size_initial * m_size_increase_factor,
|
||||
adjusted_lifetime) : 0.0f;
|
||||
|
||||
m_particles_generating[i].m_position = new_particle_position;
|
||||
m_particles_generating[i].m_lifetime = new_lifetime;
|
||||
m_particles_generating[i].m_direction = new_particle_direction;
|
||||
m_particles_generating[i].m_size = new_size;
|
||||
if (m_flips || new_size != 0.0f)
|
||||
{
|
||||
if (new_size != 0.0f)
|
||||
{
|
||||
out->emplace_back(new_particle_position, new_lifetime,
|
||||
new_size);
|
||||
}
|
||||
Buffer->BoundingBox.addInternalPoint(new_particle_position);
|
||||
}
|
||||
}
|
||||
} // stimulateHeightMap
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::stimulateNormal(float dt, unsigned int active_count,
|
||||
std::vector<CPUParticle>* out)
|
||||
{
|
||||
const core::matrix4 cur_matrix = AbsoluteTransformation;
|
||||
core::vector3df previous_frame_position, current_frame_position,
|
||||
previous_frame_direction, current_frame_direction;
|
||||
for (unsigned i = 0; i < m_max_count; i++)
|
||||
{
|
||||
core::vector3df new_particle_position;
|
||||
core::vector3df new_particle_direction;
|
||||
float new_size = 0.0f;
|
||||
float new_lifetime = 0.0f;
|
||||
|
||||
const core::vector3df particle_position =
|
||||
m_particles_generating[i].m_position;
|
||||
const float lifetime = m_particles_generating[i].m_lifetime;
|
||||
const core::vector3df particle_direction =
|
||||
m_particles_generating[i].m_direction;
|
||||
const float size = m_particles_generating[i].m_size;
|
||||
|
||||
const core::vector3df particle_position_initial =
|
||||
m_initial_particles[i].m_position;
|
||||
const float lifetime_initial = m_initial_particles[i].m_lifetime;
|
||||
const core::vector3df particle_direction_initial =
|
||||
m_initial_particles[i].m_direction;
|
||||
const float size_initial = m_initial_particles[i].m_size;
|
||||
|
||||
float updated_lifetime = lifetime + (dt / lifetime_initial);
|
||||
if (updated_lifetime > 1.0f)
|
||||
{
|
||||
if (i < active_count)
|
||||
{
|
||||
float dt_from_last_frame =
|
||||
glslFract(updated_lifetime) * lifetime_initial;
|
||||
float coeff = dt_from_last_frame / dt;
|
||||
|
||||
m_previous_frame_matrix.transformVect(previous_frame_position,
|
||||
particle_position_initial);
|
||||
cur_matrix.transformVect(current_frame_position,
|
||||
particle_position_initial);
|
||||
|
||||
core::vector3df updated_position = previous_frame_position
|
||||
.getInterpolated(current_frame_position, coeff);
|
||||
|
||||
m_previous_frame_matrix.rotateVect(previous_frame_direction,
|
||||
particle_direction_initial);
|
||||
cur_matrix.rotateVect(current_frame_direction,
|
||||
particle_direction_initial);
|
||||
|
||||
core::vector3df updated_direction = previous_frame_direction
|
||||
.getInterpolated(current_frame_direction, coeff);
|
||||
// + (current_frame_position - previous_frame_position) / dt;
|
||||
|
||||
// To be accurate, emitter speed should be added.
|
||||
// But the simple formula
|
||||
// ( (current_frame_position - previous_frame_position) / dt )
|
||||
// with a constant speed between 2 frames creates visual
|
||||
// artifacts when the framerate is low, and a more accurate
|
||||
// formula would need more complex computations.
|
||||
|
||||
new_particle_position = updated_position + dt_from_last_frame * updated_direction;
|
||||
new_particle_direction = updated_direction;
|
||||
|
||||
new_lifetime = glslFract(updated_lifetime);
|
||||
new_size = glslMix(size_initial,
|
||||
size_initial * m_size_increase_factor,
|
||||
glslFract(updated_lifetime));
|
||||
}
|
||||
else
|
||||
{
|
||||
new_lifetime = glslFract(updated_lifetime);
|
||||
new_size = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new_particle_position = particle_position +
|
||||
particle_direction * dt;
|
||||
new_particle_direction = particle_direction;
|
||||
new_lifetime = updated_lifetime;
|
||||
new_size = (size == 0.0f) ? 0.0f :
|
||||
glslMix(size_initial, size_initial * m_size_increase_factor,
|
||||
updated_lifetime);
|
||||
}
|
||||
m_particles_generating[i].m_position = new_particle_position;
|
||||
m_particles_generating[i].m_lifetime = new_lifetime;
|
||||
m_particles_generating[i].m_direction = new_particle_direction;
|
||||
m_particles_generating[i].m_size = new_size;
|
||||
if (m_flips || new_size != 0.0f)
|
||||
{
|
||||
if (new_size != 0.0f)
|
||||
{
|
||||
out->emplace_back(new_particle_position, new_lifetime,
|
||||
new_size);
|
||||
}
|
||||
Buffer->BoundingBox.addInternalPoint(new_particle_position);
|
||||
}
|
||||
}
|
||||
} // stimulateNormal
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void STKParticle::updateFlips(unsigned maximum_particle_count)
|
||||
{
|
||||
bool updated = false;
|
||||
while (maximum_particle_count > m_flips_data.size())
|
||||
{
|
||||
if (m_flips_buffer == 0)
|
||||
{
|
||||
glGenBuffers(1, &m_flips_buffer);
|
||||
}
|
||||
updated = true;
|
||||
// 3 half rotation during lifetime at max
|
||||
m_flips_data.emplace_back(0.0f, 1.0f, 0.0f,
|
||||
3.14f * 3.0f * (2.0f * os::Randomizer::frand() - 1.0f));
|
||||
}
|
||||
if (updated)
|
||||
{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_flips_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_flips_data.size() * 16,
|
||||
m_flips_data.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
} // updateFlips
|
||||
|
||||
#endif // SERVER_ONLY
|
163
src/graphics/stk_particle.hpp
Normal file
163
src/graphics/stk_particle.hpp
Normal file
@ -0,0 +1,163 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2017 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#ifndef HEADER_STK_PARTICLE_HPP
|
||||
#define HEADER_STK_PARTICLE_HPP
|
||||
|
||||
#include "graphics/gl_headers.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
#include "../lib/irrlicht/source/Irrlicht/CParticleSystemSceneNode.h"
|
||||
#include <vector>
|
||||
|
||||
using namespace irr;
|
||||
|
||||
struct CPUParticle;
|
||||
|
||||
class STKParticle : public scene::CParticleSystemSceneNode
|
||||
{
|
||||
private:
|
||||
// ------------------------------------------------------------------------
|
||||
struct HeightMapData
|
||||
{
|
||||
const std::vector<std::vector<float> > m_array;
|
||||
const float m_x;
|
||||
const float m_z;
|
||||
const float m_x_len;
|
||||
const float m_z_len;
|
||||
// --------------------------------------------------------------------
|
||||
HeightMapData(std::vector<std::vector<float> >& array,
|
||||
float track_x, float track_z, float track_x_len,
|
||||
float track_z_len)
|
||||
: m_array(std::move(array)), m_x(track_x), m_z(track_z),
|
||||
m_x_len(track_x_len), m_z_len(track_z_len) {}
|
||||
};
|
||||
// ------------------------------------------------------------------------
|
||||
struct ParticleData
|
||||
{
|
||||
core::vector3df m_position;
|
||||
float m_lifetime;
|
||||
core::vector3df m_direction;
|
||||
float m_size;
|
||||
};
|
||||
// ------------------------------------------------------------------------
|
||||
HeightMapData* m_hm;
|
||||
|
||||
std::vector<ParticleData> m_particles_generating, m_initial_particles;
|
||||
|
||||
video::SColorf m_color_from, m_color_to;
|
||||
|
||||
float m_size_increase_factor;
|
||||
|
||||
bool m_first_execution, m_randomize_initial_y, m_flips;
|
||||
|
||||
/** Previous frame particles emitter source matrix */
|
||||
core::matrix4 m_previous_frame_matrix;
|
||||
|
||||
/** Maximum count of particles. */
|
||||
unsigned m_max_count;
|
||||
|
||||
static std::vector<Vec3> m_flips_data;
|
||||
|
||||
static GLuint m_flips_buffer;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void generateParticlesFromPointEmitter(scene::IParticlePointEmitter*);
|
||||
// ------------------------------------------------------------------------
|
||||
void generateParticlesFromBoxEmitter(scene::IParticleBoxEmitter*);
|
||||
// ------------------------------------------------------------------------
|
||||
void generateParticlesFromSphereEmitter(scene::IParticleSphereEmitter*);
|
||||
// ------------------------------------------------------------------------
|
||||
void stimulateHeightMap(float, unsigned int, std::vector<CPUParticle>*);
|
||||
// ------------------------------------------------------------------------
|
||||
void stimulateNormal(float, unsigned int, std::vector<CPUParticle>*);
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
static IParticleSystemSceneNode *addParticleNode(
|
||||
bool withDefaultEmitter = true, bool randomize_initial_y = false,
|
||||
ISceneNode* parent = 0, s32 id = -1,
|
||||
const core::vector3df& position = core::vector3df(0, 0, 0),
|
||||
const core::vector3df& rotation = core::vector3df(0, 0, 0),
|
||||
const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f));
|
||||
// ------------------------------------------------------------------------
|
||||
STKParticle(bool createDefaultEmitter,
|
||||
ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
|
||||
const core::vector3df& position,
|
||||
const core::vector3df& rotation,
|
||||
const core::vector3df& scale,
|
||||
bool randomize_initial_y);
|
||||
// ------------------------------------------------------------------------
|
||||
~STKParticle()
|
||||
{
|
||||
delete m_hm;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setColorFrom(float r, float g, float b)
|
||||
{
|
||||
m_color_from.r = r;
|
||||
m_color_from.g = g;
|
||||
m_color_from.b = b;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setColorTo(float r, float g, float b)
|
||||
{
|
||||
m_color_to.r = r;
|
||||
m_color_to.g = g;
|
||||
m_color_to.b = b;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
const video::SColorf getColorFrom() const { return m_color_from; }
|
||||
// ------------------------------------------------------------------------
|
||||
const video::SColorf getColorTo() const { return m_color_to; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void setEmitter(scene::IParticleEmitter* emitter);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void render() {}
|
||||
// ------------------------------------------------------------------------
|
||||
void setIncreaseFactor(float val) { m_size_increase_factor = val; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setHeightmap(std::vector<std::vector<float> >& array, float track_x,
|
||||
float track_z, float track_x_len, float track_z_len)
|
||||
{
|
||||
m_hm = new HeightMapData(array, track_x, track_z, track_x_len,
|
||||
track_z_len);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void generate(std::vector<CPUParticle>* out);
|
||||
// ------------------------------------------------------------------------
|
||||
void setFlips() { m_flips = true; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool getFlips() const { return m_flips; }
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned getMaxCount() const { return m_max_count; }
|
||||
// ------------------------------------------------------------------------
|
||||
static void updateFlips(unsigned maximum_particle_count);
|
||||
// ------------------------------------------------------------------------
|
||||
static void destroyFlipsBuffer()
|
||||
{
|
||||
glDeleteBuffers(1, &m_flips_buffer);
|
||||
m_flips_buffer = 0;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
static GLuint getFlipsBuffer() { return m_flips_buffer; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // !SERVER_ONLY
|
@ -29,6 +29,7 @@
|
||||
#include "graphics/camera_end.hpp"
|
||||
#include "graphics/CBatchingMesh.hpp"
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/cpu_particle_manager.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/lod_node.hpp"
|
||||
#include "graphics/material.hpp"
|
||||
@ -294,6 +295,7 @@ void Track::cleanup()
|
||||
ItemManager::destroy();
|
||||
#ifndef SERVER_ONLY
|
||||
VAOManager::kill();
|
||||
CPUParticleManager::getInstance()->cleanMaterialMap();
|
||||
ParticleKindManager::get()->cleanUpTrackSpecificGfx();
|
||||
#endif
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user