Commit dde6305c authored by Florian Oetke's avatar Florian Oetke
Browse files

particle effectors

parent 3caaefe8
......@@ -6,3 +6,4 @@ blueprint:cube = blueprints/cube.json
blueprint:billboard = blueprints/billboard.json
blueprint:decal = blueprints/decal.json
blueprint:test_particle_emitter = blueprints/test_particle_emitter.json
blueprint:test_smoke_emitter = blueprints/test_smoke_emitter.json
......@@ -3,3 +3,6 @@ particle: = particles/
particle_def:test_particles = particles/test_particles.json
particle_sys:test_particles = particles/test_particle_system.json
particle_def:test_smoke_particles = particles/test_smoke_particles.json
particle_sys:test_smoke_particles = particles/test_smoke_system.json
{
"Transform":{
"scale": {"x": 0.1, "y": 0.1, "z": 0.1}
},
"Model": {
"aid": "model:cube"
},
"Particle_system": {
"cfg": "particle_sys:test_smoke_particles"
}
}
......@@ -2,7 +2,7 @@
"emitters": [{
"spawn": [
{"particles_per_second": 20000, "stddev":0, "time": 1},
{"particles_per_second": 0, "stddev":0, "time": 4}
{"particles_per_second": 0, "stddev":0, "time": 20}
],
"spawn_loop": true,
......@@ -11,10 +11,19 @@
"ttl": {"mean": 4, "stddev": 0.5},
"velocity": {"mean": 3, "stddev": 0.6},
"velocity": {"mean": 32, "stddev": 8.0},
"emit_script_id": "comp_shader:particle_spawn_sphere",
"type_id": "particle_def:test_particles"
}]
}],
"effectors": [
{
"force": 10,
"force_dir": {"y":-1},
"distance_decay": 0,
"scale_with_mass": false
}
]
}
{
"keyframes": [
{
"time": 0,
"color": {"mean":{"value":0.5}, "stddev":{"value":0.3}},
"size": {"mean": {"x": 0.08}, "stddev":{"x": 0.04}},
"rotation": {"mean":{"angle":0}, "stddev":{"angle": 1}},
"drag": 0.5
},
{
"time": 4,
"color": {"mean":{"value":0.5}, "stddev":{"value":0.3}},
"size": {"mean": {"x": 0.1}, "stddev":{"x": 0.04}},
"rotation": {"mean":{"angle":1}, "stddev":{"angle": 1}},
"drag": 0.5
}
],
"symmetric_scaling": true,
"rotate_with_velocity": false,
"blend": "solid",
"geometry": "billboard",
"material_id": "mat:billboard_material.msf",
"update_script_id": "comp_shader:particle_update_simple"
}
{
"emitters": [{
"spawn": [
{"particles_per_second": 200, "stddev":5, "time": 1}
],
"spawn_loop": true,
"size": {"x":0, "y":0},
"direction": {"mean":{"elevation":-1, "azimuth":0}, "stddev":{"elevation":0.4, "azimuth":0.4}},
"ttl": {"mean": 20, "stddev": 4},
"velocity": {"mean": 0.5, "stddev": 0.3},
"emit_script_id": "comp_shader:particle_spawn_sphere",
"type_id": "particle_def:test_smoke_particles"
}]
}
......@@ -100,6 +100,9 @@ namespace mirrage {
_meta_system.entities().emplace("test_particle_emitter").process<Transform_comp>([&](auto& transform) {
transform.position = {-6, 2, 1};
});
_meta_system.entities().emplace("test_smoke_emitter").process<Transform_comp>([&](auto& transform) {
transform.position = {-6, 1, -1};
});
auto billboard = _meta_system.entities().emplace("billboard");
billboard.get<Transform_comp>().process([](auto& transform) {
......
......@@ -23,7 +23,7 @@ namespace mirrage::renderer {
glm::vec4 position; // xyz + ttl_left
glm::vec4 velocity; // xyz + ttl_initial
glm::uvec4
ttl; // last_feedback_buffer_index, seed, keyframe, floatBitsToUint(keyframe_interpolation_factor)
data; // last_feedback_buffer_index, seed, keyframe, floatBitsToUint(keyframe_interpolation_factor)
};
class Particle_script {
......@@ -51,9 +51,9 @@ namespace mirrage::renderer {
sf2_structDef(Particle_color, hue, saturation, value, alpha);
/// angle + axis rotation with the axis expressed in spherical coordinates
/// elevation=1 and azimuth=0 is (0,0,1) and the base-plane is XZ
/// elevation=0 and azimuth=0 is (0,0,1) and the base-plane is XZ
struct Particle_rotation {
float elevation = 1.f; //< 0=0°, 1= 90°
float elevation = 0.f; //< 0=0°, 1= 90°
float azimuth = 0.f; //< 0=0°, 1=360°
float angle = 0.f; //< 0=0°, 1=360°
float padding;
......@@ -82,33 +82,34 @@ namespace mirrage::renderer {
/// modify velocities of living particles
/// e.g.
/// gravity: {..., force=10, dir={0,-1,0}, decay=0, fixed_dir=true}
/// gravity: {..., force=10, dir={0,-1,0}, decay=0}
/// point: {position=? dir={0,0,0}, decay=2}
/// flow: {position=? dir={1,0,0}, decay=2}
struct Particle_effector_config {
glm::quat rotation{1, 0, 0, 0};
glm::vec3 position{0, 0, 0};
bool absolute = false;
float force = 0.f;
glm::vec3 force_dir{0, 0, 0};
float distance_decay = 2.f;
bool fixed_dir = false; //< ignore position of effector when calculating the force
bool scale_with_mass = true;
bool absolute = false;
bool scale_with_mass = true;
float negative_mass_scale = 0.f; //< 0: clamp mass to >=0; >0: inverte direction; 0.5: force/2
};
sf2_structDef(Particle_effector_config,
position,
rotation,
absolute,
force,
force_dir,
distance_decay,
fixed_dir,
scale_with_mass);
scale_with_mass,
negative_mass_scale);
struct Particle_keyframe {
Random_value<Particle_color> color = {{1, 1, 1, 1}};
Random_value<Particle_rotation> rotation = {{0.f, 0.f, 0.f, 0.f}};
Random_value<Particle_rotation> rotation = {};
Random_value<glm::vec4> size = {{1.f, 1.f, 1.f, 0.f}};
float time = 0;
......
......@@ -75,8 +75,8 @@ void calc_random(uint seed, out vec3 rotation, out vec3 size, out vec4 color) {
size.x = (particle_config.normal_distribution_flags & 16)!=0 ? normal_shared.y
: uniform_shared.y*2-1;
size.y = (particle_config.normal_distribution_flags & 32)!=0 ? normal_rotation.x
: uniform_rotation.x*2-1;
size.y = (particle_config.normal_distribution_flags & 32)!=0 ? normal_size.x
: uniform_size.x*2-1;
size.z = (particle_config.normal_distribution_flags & 64)!=0 ? normal_size.x
: uniform_size.x*2-1;
......
#include "data_structures.glsl"
layout(std140, set=0, binding = 0) uniform Shared_uniforms {
layout(std140, set=0, binding = 0) readonly buffer Shared_uniforms {
int effector_count;
int global_effector_count;
......
......@@ -40,10 +40,11 @@ vec3 rand_xyz(Random_vec4 range, vec3 rand) {
return range.mean.xyz + range.stddev.xyz*rand;
}
vec4 rand_quat(Random_vec4 range, vec3 rand) {
range.mean *= vec4(0.5, 1, 1, 1) * 3.14159265359;
range.stddev *= vec4(0.5, 1, 1, 1) * 3.14159265359;
range.mean *= vec4(0.5, 1, 1, 1) * 2 * 3.14159265359;
range.stddev *= vec4(0.5, 1, 1, 1) * 2 * 3.14159265359;
vec2 angles = range.mean.xy + range.stddev.xy * rand.xy;
angles.y += 3.14159265359 * 0.5;
vec3 axis = vec3(cos(angles.x)*cos(angles.y),
cos(angles.x)*sin(angles.y),
sin(angles.x)).xzy;
......@@ -69,8 +70,8 @@ vec3 rand_dir(vec2 mean_angles, vec2 stddev_angles, vec2 rand) {
} else if(base_dir.x < 1.0) {
vec3 mx = normalize(base_dir);
vec3 my = normalize(cross(mx, vec3(1,0,0)));
vec3 mz = normalize(cross(mx, my));
vec3 mz = normalize(cross(mx, vec3(1,0,0)));
vec3 my = normalize(cross(mx, mz));
rand_dir = mat3(mx,my,mz) * rand_dir;
}
......@@ -85,7 +86,7 @@ struct Effector {
float force;
float distance_decay;
float mass_scale; // used as a=mix(F/m, F, mass_scale)
float fixed_dir;
float negative_mass_scale;
};
struct Particle {
......@@ -134,4 +135,3 @@ struct Particle_keyframe {
uint keyframe_count; \
Particle_keyframe[] keyframes;
......@@ -20,6 +20,37 @@ layout(push_constant) uniform Push_constants {
} update_cfg;
vec3 calc_size(uint keyframe_a, uint keyframe_b, float t, vec3 rand) {
return mix(rand_xyz(particle_config.keyframes[keyframe_a].size, rand),
rand_xyz(particle_config.keyframes[keyframe_b].size, rand),
t);
}
vec3 calc_effector_force(uint effector_idx, vec3 p, float inv_mass, float mass_sign) {
vec3 p_diff = shared_uniforms.effectors[effector_idx].position.xyz - p;
float dist = length(p_diff);
vec3 dir = shared_uniforms.effectors[effector_idx].force_dir.xyz;
if(dot(dir,dir) < 0.000001 && dist>0.000001) {
dir = p_diff / dist;
} else {
dir = normalize(dir);
}
float force = shared_uniforms.effectors[effector_idx].force;
// distance decay
if(shared_uniforms.effectors[effector_idx].distance_decay > 0)
force /= pow(dist, shared_uniforms.effectors[effector_idx].distance_decay);
float accel = mix(force, force*inv_mass, shared_uniforms.effectors[effector_idx].mass_scale);
if(mass_sign<0) {
accel *= mass_sign * shared_uniforms.effectors[effector_idx].negative_mass_scale;
}
return clamp(accel, -30, 30) * dir;
}
void main() {
uint offset = gl_GlobalInvocationID.x;
......@@ -34,6 +65,8 @@ void main() {
} else {
const float dt = update_cfg.timestep;
uint seed = pin.particles[index].data.y;
// incr count and get output index
uint old_feedback_index = pin.particles[index].data.x;
uint feedback_index = feedback_mapping.new_feedback_index[old_feedback_index];
......@@ -59,18 +92,53 @@ void main() {
float keyframe_t = time_diff>0.0 ? clamp((age - time_a) / time_diff, 0.0, 1.0) : 0.0;
// update position / velocity
vec3 position = pin.particles[index].position.xyz;
vec3 velocity = pin.particles[index].velocity.xyz;
velocity *= (1.0 - dt*mix(particle_config.keyframes[keyframe].drag,
particle_config.keyframes[keyframe_b].drag,
keyframe_t));
// TODO: apply global and local effectors
pout.particles[out_index].position.xyz = pin.particles[index].position.xyz + velocity * dt;
// apply effectors
vec2 uniform_shared = uniform_rand(seed, 22, 23);
vec2 uniform_size = uniform_rand(seed, 24, 25);
vec2 normal_shared = uniform_to_normal_rand(uniform_shared);
vec2 normal_size = uniform_to_normal_rand(uniform_size);
vec3 size_rand;
size_rand.x = (particle_config.normal_distribution_flags & 16)!=0 ? normal_shared.y
: uniform_shared.y*2-1;
size_rand.y = (particle_config.normal_distribution_flags & 32)!=0 ? normal_size.x
: uniform_size.x*2-1;
size_rand.z = (particle_config.normal_distribution_flags & 64)!=0 ? normal_size.x
: uniform_size.x*2-1;
vec3 size = calc_size(keyframe, keyframe_b, keyframe_t, size_rand);
float volumn = size.x * size.y * size.z;
float mass = mix(particle_config.keyframes[keyframe].base_mass,
particle_config.keyframes[keyframe_b].base_mass,
keyframe_t);
mass += volumn * mix(particle_config.keyframes[keyframe].density,
particle_config.keyframes[keyframe_b].density,
keyframe_t);
float inv_mass = mass>0.0000001 ? abs(1.0/mass) : 0.0;
float mass_sign = mass<0 ? -1.0 : 1.0;
for(uint i=0; i<shared_uniforms.global_effector_count; i++) {
velocity += dt * calc_effector_force(i, position, inv_mass, mass_sign);
}
for(uint i=0; i<update_cfg.effector_count; i++) {
velocity += dt * calc_effector_force(i+update_cfg.effector_offset, position, inv_mass, mass_sign);
}
pout.particles[out_index].position.xyz = position + velocity * dt;
pout.particles[out_index].velocity.xyz = velocity;
pout.particles[out_index].position.w = ttl.x;
pout.particles[out_index].velocity.w = ttl.y;
pout.particles[out_index].data = uvec4(feedback_index, pin.particles[index].data.y,
pout.particles[out_index].data = uvec4(feedback_index, seed,
keyframe, floatBitsToUint(keyframe_t));
}
}
......
......@@ -143,7 +143,7 @@ namespace mirrage::renderer {
const auto stage = vk::ShaderStageFlagBits::eCompute;
auto bindings = std::array<vk::DescriptorSetLayoutBinding, 5>{
vk::DescriptorSetLayoutBinding{0, vk::DescriptorType::eUniformBuffer, 1, stage},
vk::DescriptorSetLayoutBinding{0, vk::DescriptorType::eStorageBuffer, 1, stage},
vk::DescriptorSetLayoutBinding{1, vk::DescriptorType::eStorageBuffer, 1, stage},
vk::DescriptorSetLayoutBinding{2, vk::DescriptorType::eStorageBuffer, 1, stage},
vk::DescriptorSetLayoutBinding{3, vk::DescriptorType::eStorageBuffer, 1, stage},
......
......@@ -205,7 +205,7 @@ namespace mirrage::renderer {
p.vertex<Model_vertex>(
0, false, 0, &Model_vertex::position, 1, &Model_vertex::normal, 2, &Model_vertex::tex_coords);
p.vertex<Particle>(1, true, 3, &Particle::position, 4, &Particle::velocity, 5, &Particle::ttl);
p.vertex<Particle>(1, true, 3, &Particle::position, 4, &Particle::velocity, 5, &Particle::data);
}
void Deferred_geometry_subpass::configure_particle_subpass(Deferred_renderer&,
graphic::Subpass_builder& pass)
......@@ -394,7 +394,8 @@ namespace mirrage::renderer {
frame.main_command_buffer.bindVertexBuffers(
1,
{particle.emitter->particle_buffer()},
{std::uint32_t(particle.emitter->particle_offset())});
{vk::DeviceSize(particle.emitter->particle_offset())
* vk::DeviceSize(sizeof(Particle))});
dpc.model = glm::mat4(1);
if(particle.type_cfg->geometry == Particle_geometry::billboard) {
......
......@@ -27,7 +27,7 @@ namespace mirrage::renderer {
struct Shared_uniform_data {
std::int32_t effector_count;
std::int32_t global_effector_count;
std::int32_t padding[3];
std::int32_t padding[2];
// + effector array
};
struct Uniform_Effector {
......@@ -37,7 +37,7 @@ namespace mirrage::renderer {
float force;
float distance_decay;
float mass_scale; // used as a=mix(F/m, F, mass_scale)
float fixed_dir;
float negative_mass_scale;
};
void effector_to_uniform(const Particle_effector_config& effector,
const glm::vec3& parent_position,
......@@ -49,10 +49,10 @@ namespace mirrage::renderer {
out.force_dir = orientation * glm::vec4(effector.force_dir, 0.f);
out.position = glm::vec4(
effector.absolute ? effector.position : parent_position + effector.position, 1.f);
out.force = effector.force;
out.distance_decay = effector.distance_decay;
out.mass_scale = effector.scale_with_mass ? 1.f : 0.f;
out.fixed_dir = effector.fixed_dir ? 1.f : 0.f;
out.force = effector.force;
out.distance_decay = effector.distance_decay;
out.mass_scale = effector.scale_with_mass ? 1.f : 0.f;
out.negative_mass_scale = effector.negative_mass_scale;
}
......@@ -100,8 +100,7 @@ namespace mirrage::renderer {
vk::SharingMode::eConcurrent,
gsl::narrow<std::uint32_t>(allowed_queues.size()),
allowed_queues.data()};
particles =
renderer.device().create_buffer(create_info, false, graphic::Memory_lifetime::temporary);
particles = renderer.device().create_buffer(create_info, false, graphic::Memory_lifetime::normal);
}
if(effector_capacity < global_effector_count) {
......@@ -112,7 +111,7 @@ namespace mirrage::renderer {
auto create_info = vk::BufferCreateInfo{
vk::BufferCreateFlags{},
size_bytes,
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eUniformBuffer};
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eStorageBuffer};
shared_uniforms =
renderer.device().create_buffer(create_info, true, graphic::Memory_lifetime::normal);
}
......@@ -463,7 +462,7 @@ namespace mirrage::renderer {
});
auto shared_uniforms = vk::DescriptorBufferInfo{*data.shared_uniforms, 0, VK_WHOLE_SIZE};
desc_writes[0].descriptorType = vk::DescriptorType::eUniformBuffer;
desc_writes[0].descriptorType = vk::DescriptorType::eStorageBuffer;
desc_writes[0].pBufferInfo = &shared_uniforms;
auto particles_old_buffer = prev_data.process(*data.particles, [](auto& d) { return *d.particles; });
......@@ -548,13 +547,19 @@ namespace mirrage::renderer {
auto type = &*iter->emitter->cfg().type;
if(type != batch_type) {
submit_batch(batch_begin, iter - 1, *batch_type);
if(batch_type)
submit_batch(batch_begin, iter, *batch_type);
batch_type = type;
batch_begin = iter;
}
}
submit_batch(batch_begin, frame.particle_queue.end(), *batch_type);
for(auto& p : frame.particle_queue) {
MIRRAGE_INVARIANT(p.emitter->gpu_data()->next_uniforms(), "no particle uniform set");
}
}
namespace {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment