Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-07-23 23:30:05 +0300
committerJacques Lucke <jacques@blender.org>2020-07-23 23:30:22 +0300
commitf359672c3589c5c474abe1de78fcf39dd59e3532 (patch)
treebafb3806a8eab57f2def3b28c64e71450d9bec9b /source/blender/simulation
parentc049fe7979de5a3a904a6528878df95ade8e358a (diff)
Particles: spawn particles on mesh surface
Diffstat (limited to 'source/blender/simulation')
-rw-r--r--source/blender/simulation/intern/particle_mesh_emitter.cc255
1 files changed, 225 insertions, 30 deletions
diff --git a/source/blender/simulation/intern/particle_mesh_emitter.cc b/source/blender/simulation/intern/particle_mesh_emitter.cc
index ab2ee0c81ed..486cd1142c3 100644
--- a/source/blender/simulation/intern/particle_mesh_emitter.cc
+++ b/source/blender/simulation/intern/particle_mesh_emitter.cc
@@ -18,6 +18,9 @@
#include "BLI_float4x4.hh"
#include "BLI_rand.hh"
+#include "BLI_vector_adaptor.hh"
+
+#include "BKE_mesh_runtime.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -26,14 +29,14 @@
namespace blender::sim {
struct EmitterSettings {
- const Object *object;
+ Object *object;
float rate;
};
-static void compute_birth_times(float rate,
- TimeInterval emit_interval,
- ParticleMeshEmitterSimulationState &state,
- Vector<float> &r_birth_times)
+static BLI_NOINLINE void compute_birth_times(float rate,
+ TimeInterval emit_interval,
+ ParticleMeshEmitterSimulationState &state,
+ Vector<float> &r_birth_times)
{
const float time_between_particles = 1.0f / rate;
int counter = 0;
@@ -51,51 +54,237 @@ static void compute_birth_times(float rate,
}
}
-static void compute_new_particle_attributes(EmitterSettings &settings,
- TimeInterval emit_interval,
- ParticleMeshEmitterSimulationState &state,
- Vector<float3> &r_positions,
- Vector<float3> &r_velocities,
- Vector<float> &r_birth_times)
+static BLI_NOINLINE Span<MLoopTri> get_mesh_triangles(Mesh &mesh)
{
- if (settings.object == nullptr) {
+ const MLoopTri *triangles = BKE_mesh_runtime_looptri_ensure(&mesh);
+ int amount = BKE_mesh_runtime_looptri_len(&mesh);
+ return Span(triangles, amount);
+}
+
+static BLI_NOINLINE void compute_triangle_areas(Mesh &mesh,
+ Span<MLoopTri> triangles,
+ MutableSpan<float> r_areas)
+{
+ assert_same_size(triangles, r_areas);
+
+ for (int i : triangles.index_range()) {
+ const MLoopTri &tri = triangles[i];
+
+ const float3 v1 = mesh.mvert[mesh.mloop[tri.tri[0]].v].co;
+ const float3 v2 = mesh.mvert[mesh.mloop[tri.tri[1]].v].co;
+ const float3 v3 = mesh.mvert[mesh.mloop[tri.tri[2]].v].co;
+
+ const float area = area_tri_v3(v1, v2, v3);
+ r_areas[i] = area;
+ }
+}
+
+static BLI_NOINLINE void compute_triangle_weights(Mesh &mesh,
+ Span<MLoopTri> triangles,
+ MutableSpan<float> r_weights)
+{
+ assert_same_size(triangles, r_weights);
+ compute_triangle_areas(mesh, triangles, r_weights);
+}
+
+static BLI_NOINLINE void compute_cumulative_distribution(Span<float> weights,
+ MutableSpan<float> r_cumulative_weights)
+{
+ BLI_assert(weights.size() + 1 == r_cumulative_weights.size());
+
+ r_cumulative_weights[0] = 0;
+ for (int i : weights.index_range()) {
+ r_cumulative_weights[i + 1] = r_cumulative_weights[i] + weights[i];
+ }
+}
+
+static void sample_cumulative_distribution_recursive(RandomNumberGenerator &rng,
+ int amount,
+ int start,
+ int one_after_end,
+ Span<float> cumulative_weights,
+ VectorAdaptor<int> &r_sampled_indices)
+{
+ BLI_assert(start <= one_after_end);
+ const int size = one_after_end - start;
+ if (size == 0) {
+ BLI_assert(amount == 0);
+ }
+ else if (amount == 0) {
return;
}
+ else if (size == 1) {
+ r_sampled_indices.append_n_times(start, amount);
+ }
+ else {
+ const int middle = start + size / 2;
+ const float left_weight = cumulative_weights[middle] - cumulative_weights[start];
+ const float right_weight = cumulative_weights[one_after_end] - cumulative_weights[middle];
+ BLI_assert(left_weight >= 0.0f && right_weight >= 0.0f);
+ const float weight_sum = left_weight + right_weight;
+ BLI_assert(weight_sum > 0.0f);
+
+ const float left_factor = left_weight / weight_sum;
+ const float right_factor = right_weight / weight_sum;
+
+ int left_amount = amount * left_factor;
+ int right_amount = amount * right_factor;
+
+ if (left_amount + right_amount < amount) {
+ BLI_assert(left_amount + right_amount + 1 == amount);
+ const float weight_per_item = weight_sum / amount;
+ const float total_remaining_weight = weight_sum -
+ (left_amount + right_amount) * weight_per_item;
+ const float left_remaining_weight = left_weight - left_amount * weight_per_item;
+ const float left_remaining_factor = left_remaining_weight / total_remaining_weight;
+ if (rng.get_float() < left_remaining_factor) {
+ left_amount++;
+ }
+ else {
+ right_amount++;
+ }
+ }
+
+ sample_cumulative_distribution_recursive(
+ rng, left_amount, start, middle, cumulative_weights, r_sampled_indices);
+ sample_cumulative_distribution_recursive(
+ rng, right_amount, middle, one_after_end, cumulative_weights, r_sampled_indices);
+ }
+}
+
+static BLI_NOINLINE void sample_cumulative_distribution(RandomNumberGenerator &rng,
+ Span<float> cumulative_weights,
+ MutableSpan<int> r_samples)
+{
+ VectorAdaptor<int> sampled_indices(r_samples);
+ sample_cumulative_distribution_recursive(rng,
+ r_samples.size(),
+ 0,
+ cumulative_weights.size() - 1,
+ cumulative_weights,
+ sampled_indices);
+ BLI_assert(sampled_indices.is_full());
+}
+
+static BLI_NOINLINE bool sample_weighted_buckets(RandomNumberGenerator &rng,
+ Span<float> weights,
+ MutableSpan<int> r_samples)
+{
+ Array<float> cumulative_weights(weights.size() + 1);
+ compute_cumulative_distribution(weights, cumulative_weights);
+
+ if (r_samples.size() > 0 && cumulative_weights.as_span().last() == 0.0f) {
+ /* All weights are zero. */
+ return false;
+ }
+
+ sample_cumulative_distribution(rng, cumulative_weights, r_samples);
+ return true;
+}
+
+static BLI_NOINLINE void sample_looptris(RandomNumberGenerator &rng,
+ Mesh &mesh,
+ Span<MLoopTri> triangles,
+ Span<int> triangles_to_sample,
+ MutableSpan<float3> r_sampled_positions,
+ MutableSpan<float3> r_sampled_normals)
+{
+ assert_same_size(triangles_to_sample, r_sampled_positions, r_sampled_normals);
+
+ MLoop *loops = mesh.mloop;
+ MVert *verts = mesh.mvert;
+
+ for (uint i : triangles_to_sample.index_range()) {
+ const uint triangle_index = triangles_to_sample[i];
+ const MLoopTri &triangle = triangles[triangle_index];
+
+ const float3 v1 = verts[loops[triangle.tri[0]].v].co;
+ const float3 v2 = verts[loops[triangle.tri[1]].v].co;
+ const float3 v3 = verts[loops[triangle.tri[2]].v].co;
+
+ const float3 bary_coords = rng.get_barycentric_coordinates();
+
+ float3 position;
+ interp_v3_v3v3v3(position, v1, v2, v3, bary_coords);
+
+ float3 normal;
+ normal_tri_v3(normal, v1, v2, v3);
+
+ r_sampled_positions[i] = position;
+ r_sampled_normals[i] = normal;
+ }
+}
+
+static BLI_NOINLINE bool compute_new_particle_attributes(EmitterSettings &settings,
+ TimeInterval emit_interval,
+ ParticleMeshEmitterSimulationState &state,
+ Vector<float3> &r_positions,
+ Vector<float3> &r_velocities,
+ Vector<float> &r_birth_times)
+{
+ if (settings.object == nullptr) {
+ return false;
+ }
if (settings.rate <= 0.000001f) {
- return;
+ return false;
}
if (settings.object->type != OB_MESH) {
- return;
+ return false;
}
- const Mesh &mesh = *(Mesh *)settings.object->data;
+ Mesh &mesh = *(Mesh *)settings.object->data;
if (mesh.totvert == 0) {
- return;
+ return false;
}
- float4x4 local_to_world = settings.object->obmat;
+ const float4x4 local_to_world = settings.object->obmat;
+ float4x4 local_to_world_normal = local_to_world.inverted_affine().transposed();
const float start_time = emit_interval.start();
const uint32_t seed = DefaultHash<StringRef>{}(state.head.name);
RandomNumberGenerator rng{(*(uint32_t *)&start_time) ^ seed};
compute_birth_times(settings.rate, emit_interval, state, r_birth_times);
- if (r_birth_times.is_empty()) {
- return;
+ const int particle_amount = r_birth_times.size();
+ if (particle_amount == 0) {
+ return false;
}
- state.last_birth_time = r_birth_times.last();
+ rng.shuffle(r_birth_times.as_mutable_span());
+
+ Span<MLoopTri> triangles = get_mesh_triangles(mesh);
+ if (triangles.is_empty()) {
+ return false;
+ }
+
+ Array<float> triangle_weights(triangles.size());
+ compute_triangle_weights(mesh, triangles, triangle_weights);
- for (int i : r_birth_times.index_range()) {
- UNUSED_VARS(i);
- const int vertex_index = rng.get_int32() % mesh.totvert;
- float3 vertex_position = mesh.mvert[vertex_index].co;
- r_positions.append(local_to_world * vertex_position);
- r_velocities.append(rng.get_unit_float3());
+ Array<int> triangles_to_sample(particle_amount);
+ if (!sample_weighted_buckets(rng, triangle_weights, triangles_to_sample)) {
+ return false;
}
+
+ Array<float3> local_positions(particle_amount);
+ Array<float3> local_normals(particle_amount);
+ sample_looptris(rng, mesh, triangles, triangles_to_sample, local_positions, local_normals);
+
+ r_positions.reserve(particle_amount);
+ r_velocities.reserve(particle_amount);
+ for (int i : IndexRange(particle_amount)) {
+ float3 position = local_to_world * local_positions[i];
+ float3 normal = local_to_world_normal.ref_3x3() * local_normals[i];
+ normal.normalize();
+
+ r_positions.append(position);
+ r_velocities.append(normal);
+ }
+
+ state.last_birth_time = r_birth_times.last();
+ return true;
}
-static EmitterSettings compute_settings(const fn::MultiFunction &inputs_fn,
- ParticleEmitterContext &context)
+static BLI_NOINLINE EmitterSettings compute_settings(const fn::MultiFunction &inputs_fn,
+ ParticleEmitterContext &context)
{
EmitterSettings parameters;
@@ -126,8 +315,14 @@ void ParticleMeshEmitter::emit(ParticleEmitterContext &context) const
Vector<float3> new_velocities;
Vector<float> new_birth_times;
- compute_new_particle_attributes(
- settings, context.emit_interval(), *state, new_positions, new_velocities, new_birth_times);
+ if (!compute_new_particle_attributes(settings,
+ context.emit_interval(),
+ *state,
+ new_positions,
+ new_velocities,
+ new_birth_times)) {
+ return;
+ }
for (StringRef name : particle_names_) {
ParticleAllocator *allocator = context.try_get_particle_allocator(name);