**Changes**
As described in T93602, this patch removes all use of the `MVert`
struct, replacing it with a generic named attribute with the name
`"position"`, consistent with other geometry types.
Variable names have been changed from `verts` to `positions`, to align
with the attribute name and the more generic design (positions are not
vertices, they are just an attribute stored on the point domain).
This change is made possible by previous commits that moved all other
data out of `MVert` to runtime data or other generic attributes. What
remains is mostly a simple type change. Though, the type still shows up
859 times, so the patch is quite large.
One compromise is that now `CD_MASK_BAREMESH` now contains
`CD_PROP_FLOAT3`. With the general move towards generic attributes
over custom data types, we are removing use of these type masks anyway.
**Benefits**
The most obvious benefit is reduced memory usage and the benefits
that brings in memory-bound situations. `float3` is only 3 bytes, in
comparison to `MVert` which was 4. When there are millions of vertices
this starts to matter more.
The other benefits come from using a more generic type. Instead of
writing algorithms specifically for `MVert`, code can just use arrays
of vectors. This will allow eliminating many temporary arrays or
wrappers used to extract positions.
Many possible improvements aren't implemented in this patch, though
I did switch simplify or remove the process of creating temporary
position arrays in a few places.
The design clarity that "positions are just another attribute" brings
allows removing explicit copying of vertices in some procedural
operations-- they are just processed like most other attributes.
**Performance**
This touches so many areas that it's hard to benchmark exhaustively,
but I observed some areas as examples.
* The mesh line node with 4 million count was 1.5x (8ms to 12ms) faster.
* The Spring splash screen went from ~4.3 to ~4.5 fps.
* The subdivision surface modifier/node was slightly faster
RNA access through Python may be slightly slower, since now we need
a name lookup instead of just a custom data type lookup for each index.
**Future Improvements**
* Remove uses of "vert_coords" functions:
* `BKE_mesh_vert_coords_alloc`
* `BKE_mesh_vert_coords_get`
* `BKE_mesh_vert_coords_apply{_with_mat4}`
* Remove more hidden copying of positions
* General simplification now possible in many areas
* Convert more code to C++ to use `float3` instead of `float[3]`
* Currently `reinterpret_cast` is used for those C-API functions
Differential Revision: https://developer.blender.org/D15982
1262 lines
40 KiB
C++
1262 lines
40 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup sim
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_texture_types.h"
|
|
|
|
#include "BKE_effect.h"
|
|
|
|
#include "eigen_utils.h"
|
|
#include "implicit.h"
|
|
|
|
/* ================ Volumetric Hair Interaction ================
|
|
* adapted from
|
|
*
|
|
* Volumetric Methods for Simulation and Rendering of Hair
|
|
* (Petrovic, Henne, Anderson, Pixar Technical Memo #06-08, Pixar Animation Studios)
|
|
*
|
|
* as well as
|
|
*
|
|
* "Detail Preserving Continuum Simulation of Straight Hair"
|
|
* (McAdams, Selle 2009)
|
|
*/
|
|
|
|
/* Note about array indexing:
|
|
* Generally the arrays here are one-dimensional.
|
|
* The relation between 3D indices and the array offset is
|
|
* offset = x + res_x * y + res_x * res_y * z
|
|
*/
|
|
|
|
static float I[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
|
|
|
|
BLI_INLINE int floor_int(float value)
|
|
{
|
|
return value > 0.0f ? int(value) : int(value) - 1;
|
|
}
|
|
|
|
BLI_INLINE float floor_mod(float value)
|
|
{
|
|
return value - floorf(value);
|
|
}
|
|
|
|
BLI_INLINE int hair_grid_size(const int res[3])
|
|
{
|
|
return res[0] * res[1] * res[2];
|
|
}
|
|
|
|
struct HairGridVert {
|
|
int samples;
|
|
float velocity[3];
|
|
float density;
|
|
|
|
float velocity_smooth[3];
|
|
};
|
|
|
|
struct HairGrid {
|
|
HairGridVert *verts;
|
|
int res[3];
|
|
float gmin[3], gmax[3];
|
|
float cellsize, inv_cellsize;
|
|
};
|
|
|
|
#define HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, axis) \
|
|
min_ii(max_ii(int((vec[axis] - gmin[axis]) * scale), 0), res[axis] - 2)
|
|
|
|
BLI_INLINE int hair_grid_offset(const float vec[3],
|
|
const int res[3],
|
|
const float gmin[3],
|
|
float scale)
|
|
{
|
|
int i, j, k;
|
|
i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0);
|
|
j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1);
|
|
k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2);
|
|
return i + (j + k * res[1]) * res[0];
|
|
}
|
|
|
|
BLI_INLINE int hair_grid_interp_weights(
|
|
const int res[3], const float gmin[3], float scale, const float vec[3], float uvw[3])
|
|
{
|
|
int i, j, k, offset;
|
|
|
|
i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0);
|
|
j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1);
|
|
k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2);
|
|
offset = i + (j + k * res[1]) * res[0];
|
|
|
|
uvw[0] = (vec[0] - gmin[0]) * scale - float(i);
|
|
uvw[1] = (vec[1] - gmin[1]) * scale - float(j);
|
|
uvw[2] = (vec[2] - gmin[2]) * scale - float(k);
|
|
|
|
#if 0
|
|
BLI_assert(0.0f <= uvw[0] && uvw[0] <= 1.0001f);
|
|
BLI_assert(0.0f <= uvw[1] && uvw[1] <= 1.0001f);
|
|
BLI_assert(0.0f <= uvw[2] && uvw[2] <= 1.0001f);
|
|
#endif
|
|
|
|
return offset;
|
|
}
|
|
|
|
BLI_INLINE void hair_grid_interpolate(const HairGridVert *grid,
|
|
const int res[3],
|
|
const float gmin[3],
|
|
float scale,
|
|
const float vec[3],
|
|
float *density,
|
|
float velocity[3],
|
|
float vel_smooth[3],
|
|
float density_gradient[3],
|
|
float velocity_gradient[3][3])
|
|
{
|
|
HairGridVert data[8];
|
|
float uvw[3], muvw[3];
|
|
int res2 = res[1] * res[0];
|
|
int offset;
|
|
|
|
offset = hair_grid_interp_weights(res, gmin, scale, vec, uvw);
|
|
muvw[0] = 1.0f - uvw[0];
|
|
muvw[1] = 1.0f - uvw[1];
|
|
muvw[2] = 1.0f - uvw[2];
|
|
|
|
data[0] = grid[offset];
|
|
data[1] = grid[offset + 1];
|
|
data[2] = grid[offset + res[0]];
|
|
data[3] = grid[offset + res[0] + 1];
|
|
data[4] = grid[offset + res2];
|
|
data[5] = grid[offset + res2 + 1];
|
|
data[6] = grid[offset + res2 + res[0]];
|
|
data[7] = grid[offset + res2 + res[0] + 1];
|
|
|
|
if (density) {
|
|
*density = muvw[2] * (muvw[1] * (muvw[0] * data[0].density + uvw[0] * data[1].density) +
|
|
uvw[1] * (muvw[0] * data[2].density + uvw[0] * data[3].density)) +
|
|
uvw[2] * (muvw[1] * (muvw[0] * data[4].density + uvw[0] * data[5].density) +
|
|
uvw[1] * (muvw[0] * data[6].density + uvw[0] * data[7].density));
|
|
}
|
|
|
|
if (velocity) {
|
|
int k;
|
|
for (k = 0; k < 3; k++) {
|
|
velocity[k] = muvw[2] *
|
|
(muvw[1] * (muvw[0] * data[0].velocity[k] + uvw[0] * data[1].velocity[k]) +
|
|
uvw[1] * (muvw[0] * data[2].velocity[k] + uvw[0] * data[3].velocity[k])) +
|
|
uvw[2] *
|
|
(muvw[1] * (muvw[0] * data[4].velocity[k] + uvw[0] * data[5].velocity[k]) +
|
|
uvw[1] * (muvw[0] * data[6].velocity[k] + uvw[0] * data[7].velocity[k]));
|
|
}
|
|
}
|
|
|
|
if (vel_smooth) {
|
|
int k;
|
|
for (k = 0; k < 3; k++) {
|
|
vel_smooth[k] = muvw[2] * (muvw[1] * (muvw[0] * data[0].velocity_smooth[k] +
|
|
uvw[0] * data[1].velocity_smooth[k]) +
|
|
uvw[1] * (muvw[0] * data[2].velocity_smooth[k] +
|
|
uvw[0] * data[3].velocity_smooth[k])) +
|
|
uvw[2] * (muvw[1] * (muvw[0] * data[4].velocity_smooth[k] +
|
|
uvw[0] * data[5].velocity_smooth[k]) +
|
|
uvw[1] * (muvw[0] * data[6].velocity_smooth[k] +
|
|
uvw[0] * data[7].velocity_smooth[k]));
|
|
}
|
|
}
|
|
|
|
if (density_gradient) {
|
|
density_gradient[0] = muvw[1] * muvw[2] * (data[0].density - data[1].density) +
|
|
uvw[1] * muvw[2] * (data[2].density - data[3].density) +
|
|
muvw[1] * uvw[2] * (data[4].density - data[5].density) +
|
|
uvw[1] * uvw[2] * (data[6].density - data[7].density);
|
|
|
|
density_gradient[1] = muvw[2] * muvw[0] * (data[0].density - data[2].density) +
|
|
uvw[2] * muvw[0] * (data[4].density - data[6].density) +
|
|
muvw[2] * uvw[0] * (data[1].density - data[3].density) +
|
|
uvw[2] * uvw[0] * (data[5].density - data[7].density);
|
|
|
|
density_gradient[2] = muvw[2] * muvw[0] * (data[0].density - data[4].density) +
|
|
uvw[2] * muvw[0] * (data[1].density - data[5].density) +
|
|
muvw[2] * uvw[0] * (data[2].density - data[6].density) +
|
|
uvw[2] * uvw[0] * (data[3].density - data[7].density);
|
|
}
|
|
|
|
if (velocity_gradient) {
|
|
/* XXX TODO: */
|
|
zero_m3(velocity_gradient);
|
|
}
|
|
}
|
|
|
|
void SIM_hair_volume_vertex_grid_forces(HairGrid *grid,
|
|
const float x[3],
|
|
const float v[3],
|
|
float smoothfac,
|
|
float pressurefac,
|
|
float minpressure,
|
|
float f[3],
|
|
float dfdx[3][3],
|
|
float dfdv[3][3])
|
|
{
|
|
float gdensity, gvelocity[3], ggrad[3], gvelgrad[3][3], gradlen;
|
|
|
|
hair_grid_interpolate(grid->verts,
|
|
grid->res,
|
|
grid->gmin,
|
|
grid->inv_cellsize,
|
|
x,
|
|
&gdensity,
|
|
gvelocity,
|
|
nullptr,
|
|
ggrad,
|
|
gvelgrad);
|
|
|
|
zero_v3(f);
|
|
sub_v3_v3(gvelocity, v);
|
|
mul_v3_v3fl(f, gvelocity, smoothfac);
|
|
|
|
gradlen = normalize_v3(ggrad) - minpressure;
|
|
if (gradlen > 0.0f) {
|
|
mul_v3_fl(ggrad, gradlen);
|
|
madd_v3_v3fl(f, ggrad, pressurefac);
|
|
}
|
|
|
|
zero_m3(dfdx);
|
|
|
|
sub_m3_m3m3(dfdv, gvelgrad, I);
|
|
mul_m3_fl(dfdv, smoothfac);
|
|
}
|
|
|
|
void SIM_hair_volume_grid_interpolate(HairGrid *grid,
|
|
const float x[3],
|
|
float *density,
|
|
float velocity[3],
|
|
float velocity_smooth[3],
|
|
float density_gradient[3],
|
|
float velocity_gradient[3][3])
|
|
{
|
|
hair_grid_interpolate(grid->verts,
|
|
grid->res,
|
|
grid->gmin,
|
|
grid->inv_cellsize,
|
|
x,
|
|
density,
|
|
velocity,
|
|
velocity_smooth,
|
|
density_gradient,
|
|
velocity_gradient);
|
|
}
|
|
|
|
void SIM_hair_volume_grid_velocity(
|
|
HairGrid *grid, const float x[3], const float v[3], float fluid_factor, float r_v[3])
|
|
{
|
|
float gdensity, gvelocity[3], gvel_smooth[3], ggrad[3], gvelgrad[3][3];
|
|
float v_pic[3], v_flip[3];
|
|
|
|
hair_grid_interpolate(grid->verts,
|
|
grid->res,
|
|
grid->gmin,
|
|
grid->inv_cellsize,
|
|
x,
|
|
&gdensity,
|
|
gvelocity,
|
|
gvel_smooth,
|
|
ggrad,
|
|
gvelgrad);
|
|
|
|
/* velocity according to PIC method (Particle-in-Cell) */
|
|
copy_v3_v3(v_pic, gvel_smooth);
|
|
|
|
/* velocity according to FLIP method (Fluid-Implicit-Particle) */
|
|
sub_v3_v3v3(v_flip, gvel_smooth, gvelocity);
|
|
add_v3_v3(v_flip, v);
|
|
|
|
interp_v3_v3v3(r_v, v_pic, v_flip, fluid_factor);
|
|
}
|
|
|
|
void SIM_hair_volume_grid_clear(HairGrid *grid)
|
|
{
|
|
const int size = hair_grid_size(grid->res);
|
|
int i;
|
|
for (i = 0; i < size; i++) {
|
|
zero_v3(grid->verts[i].velocity);
|
|
zero_v3(grid->verts[i].velocity_smooth);
|
|
grid->verts[i].density = 0.0f;
|
|
grid->verts[i].samples = 0;
|
|
}
|
|
}
|
|
|
|
BLI_INLINE bool hair_grid_point_valid(const float vec[3], const float gmin[3], const float gmax[3])
|
|
{
|
|
return !(vec[0] < gmin[0] || vec[1] < gmin[1] || vec[2] < gmin[2] || vec[0] > gmax[0] ||
|
|
vec[1] > gmax[1] || vec[2] > gmax[2]);
|
|
}
|
|
|
|
BLI_INLINE float dist_tent_v3f3(const float a[3], float x, float y, float z)
|
|
{
|
|
float w = (1.0f - fabsf(a[0] - x)) * (1.0f - fabsf(a[1] - y)) * (1.0f - fabsf(a[2] - z));
|
|
return w;
|
|
}
|
|
|
|
BLI_INLINE float weights_sum(const float weights[8])
|
|
{
|
|
float totweight = 0.0f;
|
|
int i;
|
|
for (i = 0; i < 8; i++) {
|
|
totweight += weights[i];
|
|
}
|
|
return totweight;
|
|
}
|
|
|
|
/* returns the grid array offset as well to avoid redundant calculation */
|
|
BLI_INLINE int hair_grid_weights(
|
|
const int res[3], const float gmin[3], float scale, const float vec[3], float weights[8])
|
|
{
|
|
int i, j, k, offset;
|
|
float uvw[3];
|
|
|
|
i = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 0);
|
|
j = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 1);
|
|
k = HAIR_GRID_INDEX_AXIS(vec, res, gmin, scale, 2);
|
|
offset = i + (j + k * res[1]) * res[0];
|
|
|
|
uvw[0] = (vec[0] - gmin[0]) * scale;
|
|
uvw[1] = (vec[1] - gmin[1]) * scale;
|
|
uvw[2] = (vec[2] - gmin[2]) * scale;
|
|
|
|
weights[0] = dist_tent_v3f3(uvw, float(i), float(j), float(k));
|
|
weights[1] = dist_tent_v3f3(uvw, float(i + 1), float(j), float(k));
|
|
weights[2] = dist_tent_v3f3(uvw, float(i), float(j + 1), float(k));
|
|
weights[3] = dist_tent_v3f3(uvw, float(i + 1), float(j + 1), float(k));
|
|
weights[4] = dist_tent_v3f3(uvw, float(i), float(j), float(k + 1));
|
|
weights[5] = dist_tent_v3f3(uvw, float(i + 1), float(j), float(k + 1));
|
|
weights[6] = dist_tent_v3f3(uvw, float(i), float(j + 1), float(k + 1));
|
|
weights[7] = dist_tent_v3f3(uvw, float(i + 1), float(j + 1), float(k + 1));
|
|
|
|
// BLI_assert(fabsf(weights_sum(weights) - 1.0f) < 0.0001f);
|
|
|
|
return offset;
|
|
}
|
|
|
|
BLI_INLINE void grid_to_world(HairGrid *grid, float vecw[3], const float vec[3])
|
|
{
|
|
copy_v3_v3(vecw, vec);
|
|
mul_v3_fl(vecw, grid->cellsize);
|
|
add_v3_v3(vecw, grid->gmin);
|
|
}
|
|
|
|
void SIM_hair_volume_add_vertex(HairGrid *grid, const float x[3], const float v[3])
|
|
{
|
|
const int res[3] = {grid->res[0], grid->res[1], grid->res[2]};
|
|
float weights[8];
|
|
int di, dj, dk;
|
|
int offset;
|
|
|
|
if (!hair_grid_point_valid(x, grid->gmin, grid->gmax)) {
|
|
return;
|
|
}
|
|
|
|
offset = hair_grid_weights(res, grid->gmin, grid->inv_cellsize, x, weights);
|
|
|
|
for (di = 0; di < 2; di++) {
|
|
for (dj = 0; dj < 2; dj++) {
|
|
for (dk = 0; dk < 2; dk++) {
|
|
int voffset = offset + di + (dj + dk * res[1]) * res[0];
|
|
int iw = di + dj * 2 + dk * 4;
|
|
|
|
grid->verts[voffset].density += weights[iw];
|
|
madd_v3_v3fl(grid->verts[voffset].velocity, v, weights[iw]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
BLI_INLINE void hair_volume_eval_grid_vertex(HairGridVert *vert,
|
|
const float loc[3],
|
|
float radius,
|
|
float dist_scale,
|
|
const float x2[3],
|
|
const float v2[3],
|
|
const float x3[3],
|
|
const float v3[3])
|
|
{
|
|
float closest[3], lambda, dist, weight;
|
|
|
|
lambda = closest_to_line_v3(closest, loc, x2, x3);
|
|
dist = len_v3v3(closest, loc);
|
|
|
|
weight = (radius - dist) * dist_scale;
|
|
|
|
if (weight > 0.0f) {
|
|
float vel[3];
|
|
|
|
interp_v3_v3v3(vel, v2, v3, lambda);
|
|
madd_v3_v3fl(vert->velocity, vel, weight);
|
|
vert->density += weight;
|
|
vert->samples += 1;
|
|
}
|
|
}
|
|
|
|
BLI_INLINE int major_axis_v3(const float v[3])
|
|
{
|
|
const float a = fabsf(v[0]);
|
|
const float b = fabsf(v[1]);
|
|
const float c = fabsf(v[2]);
|
|
return a > b ? (a > c ? 0 : 2) : (b > c ? 1 : 2);
|
|
}
|
|
|
|
BLI_INLINE void hair_volume_add_segment_2D(HairGrid *grid,
|
|
const float UNUSED(x1[3]),
|
|
const float UNUSED(v1[3]),
|
|
const float x2[3],
|
|
const float v2[3],
|
|
const float x3[3],
|
|
const float v3[3],
|
|
const float UNUSED(x4[3]),
|
|
const float UNUSED(v4[3]),
|
|
const float UNUSED(dir1[3]),
|
|
const float dir2[3],
|
|
const float UNUSED(dir3[3]),
|
|
int resj,
|
|
int resk,
|
|
int jmin,
|
|
int jmax,
|
|
int kmin,
|
|
int kmax,
|
|
HairGridVert *vert,
|
|
int stride_j,
|
|
int stride_k,
|
|
const float loc[3],
|
|
int axis_j,
|
|
int axis_k,
|
|
int debug_i)
|
|
{
|
|
const float radius = 1.5f;
|
|
const float dist_scale = grid->inv_cellsize;
|
|
|
|
int j, k;
|
|
|
|
/* boundary checks to be safe */
|
|
CLAMP_MIN(jmin, 0);
|
|
CLAMP_MAX(jmax, resj - 1);
|
|
CLAMP_MIN(kmin, 0);
|
|
CLAMP_MAX(kmax, resk - 1);
|
|
|
|
HairGridVert *vert_j = vert + jmin * stride_j;
|
|
float loc_j[3] = {loc[0], loc[1], loc[2]};
|
|
loc_j[axis_j] += float(jmin);
|
|
for (j = jmin; j <= jmax; j++, vert_j += stride_j, loc_j[axis_j] += 1.0f) {
|
|
|
|
HairGridVert *vert_k = vert_j + kmin * stride_k;
|
|
float loc_k[3] = {loc_j[0], loc_j[1], loc_j[2]};
|
|
loc_k[axis_k] += float(kmin);
|
|
for (k = kmin; k <= kmax; k++, vert_k += stride_k, loc_k[axis_k] += 1.0f) {
|
|
|
|
hair_volume_eval_grid_vertex(vert_k, loc_k, radius, dist_scale, x2, v2, x3, v3);
|
|
|
|
# if 0
|
|
{
|
|
float wloc[3], x2w[3], x3w[3];
|
|
grid_to_world(grid, wloc, loc_k);
|
|
grid_to_world(grid, x2w, x2);
|
|
grid_to_world(grid, x3w, x3);
|
|
|
|
if (vert_k->samples > 0) {
|
|
BKE_sim_debug_data_add_circle(wloc, 0.01f, 1.0, 1.0, 0.3, "grid", 2525, debug_i, j, k);
|
|
}
|
|
|
|
if (grid->debug_value) {
|
|
BKE_sim_debug_data_add_dot(wloc, 1, 0, 0, "grid", 93, debug_i, j, k);
|
|
BKE_sim_debug_data_add_dot(x2w, 0.1, 0.1, 0.7, "grid", 649, debug_i, j, k);
|
|
BKE_sim_debug_data_add_line(wloc, x2w, 0.3, 0.8, 0.3, "grid", 253, debug_i, j, k);
|
|
BKE_sim_debug_data_add_line(wloc, x3w, 0.8, 0.3, 0.3, "grid", 254, debug_i, j, k);
|
|
# if 0
|
|
BKE_sim_debug_data_add_circle(
|
|
x2w, len_v3v3(wloc, x2w), 0.2, 0.7, 0.2, "grid", 255, i, j, k);
|
|
# endif
|
|
}
|
|
}
|
|
# endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Uses a variation of Bresenham's algorithm for rasterizing a 3D grid with a line segment.
|
|
*
|
|
* The radius of influence around a segment is assumed to be at most 2*cellsize,
|
|
* i.e. only cells containing the segment and their direct neighbors are examined.
|
|
*/
|
|
void SIM_hair_volume_add_segment(HairGrid *grid,
|
|
const float x1[3],
|
|
const float v1[3],
|
|
const float x2[3],
|
|
const float v2[3],
|
|
const float x3[3],
|
|
const float v3[3],
|
|
const float x4[3],
|
|
const float v4[3],
|
|
const float dir1[3],
|
|
const float dir2[3],
|
|
const float dir3[3])
|
|
{
|
|
const int res[3] = {grid->res[0], grid->res[1], grid->res[2]};
|
|
|
|
/* find the primary direction from the major axis of the direction vector */
|
|
const int axis0 = major_axis_v3(dir2);
|
|
const int axis1 = (axis0 + 1) % 3;
|
|
const int axis2 = (axis0 + 2) % 3;
|
|
|
|
/* vertex buffer offset factors along cardinal axes */
|
|
const int strides[3] = {1, res[0], res[0] * res[1]};
|
|
const int stride0 = strides[axis0];
|
|
const int stride1 = strides[axis1];
|
|
const int stride2 = strides[axis2];
|
|
|
|
/* increment of secondary directions per step in the primary direction
|
|
* NOTE: we always go in the positive direction along axis0, so the sign can be inverted
|
|
*/
|
|
const float inc1 = dir2[axis1] / dir2[axis0];
|
|
const float inc2 = dir2[axis2] / dir2[axis0];
|
|
|
|
/* start/end points, so increment along axis0 is always positive */
|
|
const float *start = x2[axis0] < x3[axis0] ? x2 : x3;
|
|
const float *end = x2[axis0] < x3[axis0] ? x3 : x2;
|
|
const float start0 = start[axis0], start1 = start[axis1], start2 = start[axis2];
|
|
const float end0 = end[axis0];
|
|
|
|
/* range along primary direction */
|
|
const int imin = max_ii(floor_int(start[axis0]) - 1, 0);
|
|
const int imax = min_ii(floor_int(end[axis0]) + 2, res[axis0] - 1);
|
|
|
|
float h = 0.0f;
|
|
HairGridVert *vert0;
|
|
float loc0[3];
|
|
int j0, k0, j0_prev, k0_prev;
|
|
int i;
|
|
|
|
for (i = imin; i <= imax; i++) {
|
|
float shift1, shift2; /* fraction of a full cell shift [0.0, 1.0) */
|
|
int jmin, jmax, kmin, kmax;
|
|
|
|
h = CLAMPIS(float(i), start0, end0);
|
|
|
|
shift1 = start1 + (h - start0) * inc1;
|
|
shift2 = start2 + (h - start0) * inc2;
|
|
|
|
j0_prev = j0;
|
|
j0 = floor_int(shift1);
|
|
|
|
k0_prev = k0;
|
|
k0 = floor_int(shift2);
|
|
|
|
if (i > imin) {
|
|
jmin = min_ii(j0, j0_prev);
|
|
jmax = max_ii(j0, j0_prev);
|
|
kmin = min_ii(k0, k0_prev);
|
|
kmax = max_ii(k0, k0_prev);
|
|
}
|
|
else {
|
|
jmin = jmax = j0;
|
|
kmin = kmax = k0;
|
|
}
|
|
|
|
vert0 = grid->verts + i * stride0;
|
|
loc0[axis0] = float(i);
|
|
loc0[axis1] = 0.0f;
|
|
loc0[axis2] = 0.0f;
|
|
|
|
hair_volume_add_segment_2D(grid,
|
|
x1,
|
|
v1,
|
|
x2,
|
|
v2,
|
|
x3,
|
|
v3,
|
|
x4,
|
|
v4,
|
|
dir1,
|
|
dir2,
|
|
dir3,
|
|
res[axis1],
|
|
res[axis2],
|
|
jmin - 1,
|
|
jmax + 2,
|
|
kmin - 1,
|
|
kmax + 2,
|
|
vert0,
|
|
stride1,
|
|
stride2,
|
|
loc0,
|
|
axis1,
|
|
axis2,
|
|
i);
|
|
}
|
|
}
|
|
#else
|
|
BLI_INLINE void hair_volume_eval_grid_vertex_sample(HairGridVert *vert,
|
|
const float loc[3],
|
|
float radius,
|
|
float dist_scale,
|
|
const float x[3],
|
|
const float v[3])
|
|
{
|
|
float dist, weight;
|
|
|
|
dist = len_v3v3(x, loc);
|
|
|
|
weight = (radius - dist) * dist_scale;
|
|
|
|
if (weight > 0.0f) {
|
|
madd_v3_v3fl(vert->velocity, v, weight);
|
|
vert->density += weight;
|
|
vert->samples += 1;
|
|
}
|
|
}
|
|
|
|
void SIM_hair_volume_add_segment(HairGrid *grid,
|
|
const float /*x1*/[3],
|
|
const float /*v1*/[3],
|
|
const float x2[3],
|
|
const float v2[3],
|
|
const float x3[3],
|
|
const float v3[3],
|
|
const float /*x4*/[3],
|
|
const float /*v4*/[3],
|
|
const float /*dir1*/[3],
|
|
const float /*dir2*/[3],
|
|
const float /*dir3*/[3])
|
|
{
|
|
/* XXX simplified test implementation using a series of discrete sample along the segment,
|
|
* instead of finding the closest point for all affected grid vertices. */
|
|
|
|
const float radius = 1.5f;
|
|
const float dist_scale = grid->inv_cellsize;
|
|
|
|
const int res[3] = {grid->res[0], grid->res[1], grid->res[2]};
|
|
const int stride[3] = {1, res[0], res[0] * res[1]};
|
|
const int num_samples = 10;
|
|
|
|
int s;
|
|
|
|
for (s = 0; s < num_samples; s++) {
|
|
float x[3], v[3];
|
|
int i, j, k;
|
|
|
|
float f = float(s) / float(num_samples - 1);
|
|
interp_v3_v3v3(x, x2, x3, f);
|
|
interp_v3_v3v3(v, v2, v3, f);
|
|
|
|
int imin = max_ii(floor_int(x[0]) - 2, 0);
|
|
int imax = min_ii(floor_int(x[0]) + 2, res[0] - 1);
|
|
int jmin = max_ii(floor_int(x[1]) - 2, 0);
|
|
int jmax = min_ii(floor_int(x[1]) + 2, res[1] - 1);
|
|
int kmin = max_ii(floor_int(x[2]) - 2, 0);
|
|
int kmax = min_ii(floor_int(x[2]) + 2, res[2] - 1);
|
|
|
|
for (k = kmin; k <= kmax; k++) {
|
|
for (j = jmin; j <= jmax; j++) {
|
|
for (i = imin; i <= imax; i++) {
|
|
float loc[3] = {float(i), float(j), float(k)};
|
|
HairGridVert *vert = grid->verts + i * stride[0] + j * stride[1] + k * stride[2];
|
|
|
|
hair_volume_eval_grid_vertex_sample(vert, loc, radius, dist_scale, x, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void SIM_hair_volume_normalize_vertex_grid(HairGrid *grid)
|
|
{
|
|
int i, size = hair_grid_size(grid->res);
|
|
/* divide velocity with density */
|
|
for (i = 0; i < size; i++) {
|
|
float density = grid->verts[i].density;
|
|
if (density > 0.0f) {
|
|
mul_v3_fl(grid->verts[i].velocity, 1.0f / density);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Cells with density below this are considered empty. */
|
|
static const float density_threshold = 0.001f;
|
|
|
|
/* Contribution of target density pressure to the laplacian in the pressure poisson equation.
|
|
* This is based on the model found in
|
|
* "Two-way Coupled SPH and Particle Level Set Fluid Simulation" (Losasso et al., 2008)
|
|
*/
|
|
BLI_INLINE float hair_volume_density_divergence(float density,
|
|
float target_density,
|
|
float strength)
|
|
{
|
|
if (density > density_threshold && density > target_density) {
|
|
return strength * logf(target_density / density);
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
bool SIM_hair_volume_solve_divergence(HairGrid *grid,
|
|
float /*dt*/,
|
|
float target_density,
|
|
float target_strength)
|
|
{
|
|
const float flowfac = grid->cellsize;
|
|
const float inv_flowfac = 1.0f / grid->cellsize;
|
|
|
|
// const int num_cells = hair_grid_size(grid->res);
|
|
const int res[3] = {grid->res[0], grid->res[1], grid->res[2]};
|
|
const int resA[3] = {grid->res[0] + 2, grid->res[1] + 2, grid->res[2] + 2};
|
|
|
|
const int stride0 = 1;
|
|
const int stride1 = grid->res[0];
|
|
const int stride2 = grid->res[1] * grid->res[0];
|
|
const int strideA0 = 1;
|
|
const int strideA1 = grid->res[0] + 2;
|
|
const int strideA2 = (grid->res[1] + 2) * (grid->res[0] + 2);
|
|
|
|
const int num_cells = res[0] * res[1] * res[2];
|
|
const int num_cellsA = (res[0] + 2) * (res[1] + 2) * (res[2] + 2);
|
|
|
|
HairGridVert *vert_start = grid->verts - (stride0 + stride1 + stride2);
|
|
HairGridVert *vert;
|
|
int i, j, k;
|
|
|
|
#define MARGIN_i0 (i < 1)
|
|
#define MARGIN_j0 (j < 1)
|
|
#define MARGIN_k0 (k < 1)
|
|
#define MARGIN_i1 (i >= resA[0] - 1)
|
|
#define MARGIN_j1 (j >= resA[1] - 1)
|
|
#define MARGIN_k1 (k >= resA[2] - 1)
|
|
|
|
#define NEIGHBOR_MARGIN_i0 (i < 2)
|
|
#define NEIGHBOR_MARGIN_j0 (j < 2)
|
|
#define NEIGHBOR_MARGIN_k0 (k < 2)
|
|
#define NEIGHBOR_MARGIN_i1 (i >= resA[0] - 2)
|
|
#define NEIGHBOR_MARGIN_j1 (j >= resA[1] - 2)
|
|
#define NEIGHBOR_MARGIN_k1 (k >= resA[2] - 2)
|
|
|
|
BLI_assert(num_cells >= 1);
|
|
|
|
/* Calculate divergence */
|
|
lVector B(num_cellsA);
|
|
for (k = 0; k < resA[2]; k++) {
|
|
for (j = 0; j < resA[1]; j++) {
|
|
for (i = 0; i < resA[0]; i++) {
|
|
int u = i * strideA0 + j * strideA1 + k * strideA2;
|
|
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 ||
|
|
MARGIN_k1;
|
|
|
|
if (is_margin) {
|
|
B[u] = 0.0f;
|
|
continue;
|
|
}
|
|
|
|
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
|
|
|
|
const float *v0 = vert->velocity;
|
|
float dx = 0.0f, dy = 0.0f, dz = 0.0f;
|
|
if (!NEIGHBOR_MARGIN_i0) {
|
|
dx += v0[0] - (vert - stride0)->velocity[0];
|
|
}
|
|
if (!NEIGHBOR_MARGIN_i1) {
|
|
dx += (vert + stride0)->velocity[0] - v0[0];
|
|
}
|
|
if (!NEIGHBOR_MARGIN_j0) {
|
|
dy += v0[1] - (vert - stride1)->velocity[1];
|
|
}
|
|
if (!NEIGHBOR_MARGIN_j1) {
|
|
dy += (vert + stride1)->velocity[1] - v0[1];
|
|
}
|
|
if (!NEIGHBOR_MARGIN_k0) {
|
|
dz += v0[2] - (vert - stride2)->velocity[2];
|
|
}
|
|
if (!NEIGHBOR_MARGIN_k1) {
|
|
dz += (vert + stride2)->velocity[2] - v0[2];
|
|
}
|
|
|
|
float divergence = -0.5f * flowfac * (dx + dy + dz);
|
|
|
|
/* adjustment term for target density */
|
|
float target = hair_volume_density_divergence(
|
|
vert->density, target_density, target_strength);
|
|
|
|
/* B vector contains the finite difference approximation of the velocity divergence.
|
|
* NOTE: according to the discretized Navier-Stokes equation the RHS vector
|
|
* and resulting pressure gradient should be multiplied by the (inverse) density;
|
|
* however, this is already included in the weighting of hair velocities on the grid!
|
|
*/
|
|
B[u] = divergence - target;
|
|
|
|
#if 0
|
|
{
|
|
float wloc[3], loc[3];
|
|
float col0[3] = {0.0, 0.0, 0.0};
|
|
float colp[3] = {0.0, 1.0, 1.0};
|
|
float coln[3] = {1.0, 0.0, 1.0};
|
|
float col[3];
|
|
float fac;
|
|
|
|
loc[0] = float(i - 1);
|
|
loc[1] = float(j - 1);
|
|
loc[2] = float(k - 1);
|
|
grid_to_world(grid, wloc, loc);
|
|
|
|
if (divergence > 0.0f) {
|
|
fac = CLAMPIS(divergence * target_strength, 0.0, 1.0);
|
|
interp_v3_v3v3(col, col0, colp, fac);
|
|
}
|
|
else {
|
|
fac = CLAMPIS(-divergence * target_strength, 0.0, 1.0);
|
|
interp_v3_v3v3(col, col0, coln, fac);
|
|
}
|
|
if (fac > 0.05f) {
|
|
BKE_sim_debug_data_add_circle(
|
|
grid->debug_data, wloc, 0.01f, col[0], col[1], col[2], "grid", 5522, i, j, k);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Main Poisson equation system:
|
|
* This is derived from the discretization of the Poisson equation:
|
|
* `div(grad(p)) = div(v)`
|
|
*
|
|
* The finite difference approximation yields the linear equation system described here:
|
|
* https://en.wikipedia.org/wiki/Discrete_Poisson_equation
|
|
*/
|
|
lMatrix A(num_cellsA, num_cellsA);
|
|
/* Reserve space for the base equation system (without boundary conditions).
|
|
* Each column contains a factor 6 on the diagonal
|
|
* and up to 6 factors -1 on other places.
|
|
*/
|
|
A.reserve(Eigen::VectorXi::Constant(num_cellsA, 7));
|
|
|
|
for (k = 0; k < resA[2]; k++) {
|
|
for (j = 0; j < resA[1]; j++) {
|
|
for (i = 0; i < resA[0]; i++) {
|
|
int u = i * strideA0 + j * strideA1 + k * strideA2;
|
|
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 ||
|
|
MARGIN_k1;
|
|
|
|
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
|
|
if (!is_margin && vert->density > density_threshold) {
|
|
int neighbors_lo = 0;
|
|
int neighbors_hi = 0;
|
|
int non_solid_neighbors = 0;
|
|
int neighbor_lo_index[3];
|
|
int neighbor_hi_index[3];
|
|
int n;
|
|
|
|
/* check for upper bounds in advance
|
|
* to get the correct number of neighbors,
|
|
* needed for the diagonal element
|
|
*/
|
|
if (!NEIGHBOR_MARGIN_k0 && (vert - stride2)->density > density_threshold) {
|
|
neighbor_lo_index[neighbors_lo++] = u - strideA2;
|
|
}
|
|
if (!NEIGHBOR_MARGIN_j0 && (vert - stride1)->density > density_threshold) {
|
|
neighbor_lo_index[neighbors_lo++] = u - strideA1;
|
|
}
|
|
if (!NEIGHBOR_MARGIN_i0 && (vert - stride0)->density > density_threshold) {
|
|
neighbor_lo_index[neighbors_lo++] = u - strideA0;
|
|
}
|
|
if (!NEIGHBOR_MARGIN_i1 && (vert + stride0)->density > density_threshold) {
|
|
neighbor_hi_index[neighbors_hi++] = u + strideA0;
|
|
}
|
|
if (!NEIGHBOR_MARGIN_j1 && (vert + stride1)->density > density_threshold) {
|
|
neighbor_hi_index[neighbors_hi++] = u + strideA1;
|
|
}
|
|
if (!NEIGHBOR_MARGIN_k1 && (vert + stride2)->density > density_threshold) {
|
|
neighbor_hi_index[neighbors_hi++] = u + strideA2;
|
|
}
|
|
|
|
// int liquid_neighbors = neighbors_lo + neighbors_hi;
|
|
non_solid_neighbors = 6;
|
|
|
|
for (n = 0; n < neighbors_lo; n++) {
|
|
A.insert(neighbor_lo_index[n], u) = -1.0f;
|
|
}
|
|
A.insert(u, u) = float(non_solid_neighbors);
|
|
for (n = 0; n < neighbors_hi; n++) {
|
|
A.insert(neighbor_hi_index[n], u) = -1.0f;
|
|
}
|
|
}
|
|
else {
|
|
A.insert(u, u) = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ConjugateGradient cg;
|
|
cg.setMaxIterations(100);
|
|
cg.setTolerance(0.01f);
|
|
|
|
cg.compute(A);
|
|
|
|
lVector p = cg.solve(B);
|
|
|
|
if (cg.info() == Eigen::Success) {
|
|
/* Calculate velocity = grad(p) */
|
|
for (k = 0; k < resA[2]; k++) {
|
|
for (j = 0; j < resA[1]; j++) {
|
|
for (i = 0; i < resA[0]; i++) {
|
|
int u = i * strideA0 + j * strideA1 + k * strideA2;
|
|
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 ||
|
|
MARGIN_k1;
|
|
if (is_margin) {
|
|
continue;
|
|
}
|
|
|
|
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
|
|
if (vert->density > density_threshold) {
|
|
float p_left = p[u - strideA0];
|
|
float p_right = p[u + strideA0];
|
|
float p_down = p[u - strideA1];
|
|
float p_up = p[u + strideA1];
|
|
float p_bottom = p[u - strideA2];
|
|
float p_top = p[u + strideA2];
|
|
|
|
/* finite difference estimate of pressure gradient */
|
|
float dvel[3];
|
|
dvel[0] = p_right - p_left;
|
|
dvel[1] = p_up - p_down;
|
|
dvel[2] = p_top - p_bottom;
|
|
mul_v3_fl(dvel, -0.5f * inv_flowfac);
|
|
|
|
/* pressure gradient describes velocity delta */
|
|
add_v3_v3v3(vert->velocity_smooth, vert->velocity, dvel);
|
|
}
|
|
else {
|
|
zero_v3(vert->velocity_smooth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
{
|
|
int axis = 0;
|
|
float offset = 0.0f;
|
|
|
|
int slice = (offset - grid->gmin[axis]) / grid->cellsize;
|
|
|
|
for (k = 0; k < resA[2]; k++) {
|
|
for (j = 0; j < resA[1]; j++) {
|
|
for (i = 0; i < resA[0]; i++) {
|
|
int u = i * strideA0 + j * strideA1 + k * strideA2;
|
|
bool is_margin = MARGIN_i0 || MARGIN_i1 || MARGIN_j0 || MARGIN_j1 || MARGIN_k0 ||
|
|
MARGIN_k1;
|
|
if (i != slice) {
|
|
continue;
|
|
}
|
|
|
|
vert = vert_start + i * stride0 + j * stride1 + k * stride2;
|
|
|
|
float wloc[3], loc[3];
|
|
float col0[3] = {0.0, 0.0, 0.0};
|
|
float colp[3] = {0.0, 1.0, 1.0};
|
|
float coln[3] = {1.0, 0.0, 1.0};
|
|
float col[3];
|
|
float fac;
|
|
|
|
loc[0] = float(i - 1);
|
|
loc[1] = float(j - 1);
|
|
loc[2] = float(k - 1);
|
|
grid_to_world(grid, wloc, loc);
|
|
|
|
float pressure = p[u];
|
|
if (pressure > 0.0f) {
|
|
fac = CLAMPIS(pressure * grid->debug1, 0.0, 1.0);
|
|
interp_v3_v3v3(col, col0, colp, fac);
|
|
}
|
|
else {
|
|
fac = CLAMPIS(-pressure * grid->debug1, 0.0, 1.0);
|
|
interp_v3_v3v3(col, col0, coln, fac);
|
|
}
|
|
if (fac > 0.05f) {
|
|
BKE_sim_debug_data_add_circle(
|
|
grid->debug_data, wloc, 0.01f, col[0], col[1], col[2], "grid", 5533, i, j, k);
|
|
}
|
|
|
|
if (!is_margin) {
|
|
float dvel[3];
|
|
sub_v3_v3v3(dvel, vert->velocity_smooth, vert->velocity);
|
|
# if 0
|
|
BKE_sim_debug_data_add_vector(
|
|
grid->debug_data, wloc, dvel, 1, 1, 1, "grid", 5566, i, j, k);
|
|
# endif
|
|
}
|
|
|
|
if (!is_margin) {
|
|
float d = CLAMPIS(vert->density * grid->debug2, 0.0f, 1.0f);
|
|
float col0[3] = {0.3, 0.3, 0.3};
|
|
float colp[3] = {0.0, 0.0, 1.0};
|
|
float col[3];
|
|
|
|
interp_v3_v3v3(col, col0, colp, d);
|
|
# if 0
|
|
if (d > 0.05f) {
|
|
BKE_sim_debug_data_add_dot(
|
|
grid->debug_data, wloc, col[0], col[1], col[2], "grid", 5544, i, j, k);
|
|
}
|
|
# endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Clear result in case of error */
|
|
for (i = 0, vert = grid->verts; i < num_cells; i++, vert++) {
|
|
zero_v3(vert->velocity_smooth);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if 0 /* XXX weighting is incorrect, disabled for now */
|
|
/* Velocity filter kernel
|
|
* See https://en.wikipedia.org/wiki/Filter_%28large_eddy_simulation%29
|
|
*/
|
|
|
|
BLI_INLINE void hair_volume_filter_box_convolute(
|
|
HairVertexGrid *grid, float invD, const int kernel_size[3], int i, int j, int k)
|
|
{
|
|
int res = grid->res;
|
|
int p, q, r;
|
|
int minp = max_ii(i - kernel_size[0], 0), maxp = min_ii(i + kernel_size[0], res - 1);
|
|
int minq = max_ii(j - kernel_size[1], 0), maxq = min_ii(j + kernel_size[1], res - 1);
|
|
int minr = max_ii(k - kernel_size[2], 0), maxr = min_ii(k + kernel_size[2], res - 1);
|
|
int offset, kernel_offset, kernel_dq, kernel_dr;
|
|
HairGridVert *verts;
|
|
float *vel_smooth;
|
|
|
|
offset = i + (j + k * res) * res;
|
|
verts = grid->verts;
|
|
vel_smooth = verts[offset].velocity_smooth;
|
|
|
|
kernel_offset = minp + (minq + minr * res) * res;
|
|
kernel_dq = res;
|
|
kernel_dr = res * res;
|
|
for (r = minr; r <= maxr; r++) {
|
|
for (q = minq; q <= maxq; q++) {
|
|
for (p = minp; p <= maxp; p++) {
|
|
|
|
madd_v3_v3fl(vel_smooth, verts[kernel_offset].velocity, invD);
|
|
|
|
kernel_offset += 1;
|
|
}
|
|
kernel_offset += kernel_dq;
|
|
}
|
|
kernel_offset += kernel_dr;
|
|
}
|
|
}
|
|
|
|
void SIM_hair_volume_vertex_grid_filter_box(HairVertexGrid *grid, int kernel_size)
|
|
{
|
|
int size = hair_grid_size(grid->res);
|
|
int kernel_sizev[3] = {kernel_size, kernel_size, kernel_size};
|
|
int tot;
|
|
float invD;
|
|
int i, j, k;
|
|
|
|
if (kernel_size <= 0) {
|
|
return;
|
|
}
|
|
|
|
tot = kernel_size * 2 + 1;
|
|
invD = 1.0f / float(tot * tot * tot);
|
|
|
|
/* clear values for convolution */
|
|
for (i = 0; i < size; i++) {
|
|
zero_v3(grid->verts[i].velocity_smooth);
|
|
}
|
|
|
|
for (i = 0; i < grid->res; i++) {
|
|
for (j = 0; j < grid->res; j++) {
|
|
for (k = 0; k < grid->res; k++) {
|
|
hair_volume_filter_box_convolute(grid, invD, kernel_sizev, i, j, k);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* apply as new velocity */
|
|
for (i = 0; i < size; i++) {
|
|
copy_v3_v3(grid->verts[i].velocity, grid->verts[i].velocity_smooth);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
HairGrid *SIM_hair_volume_create_vertex_grid(float cellsize,
|
|
const float gmin[3],
|
|
const float gmax[3])
|
|
{
|
|
float scale;
|
|
float extent[3];
|
|
int resmin[3], resmax[3], res[3];
|
|
float gmin_margin[3], gmax_margin[3];
|
|
int size;
|
|
HairGrid *grid;
|
|
int i;
|
|
|
|
/* sanity check */
|
|
if (cellsize <= 0.0f) {
|
|
cellsize = 1.0f;
|
|
}
|
|
scale = 1.0f / cellsize;
|
|
|
|
sub_v3_v3v3(extent, gmax, gmin);
|
|
for (i = 0; i < 3; i++) {
|
|
resmin[i] = floor_int(gmin[i] * scale);
|
|
resmax[i] = floor_int(gmax[i] * scale) + 1;
|
|
|
|
/* add margin of 1 cell */
|
|
resmin[i] -= 1;
|
|
resmax[i] += 1;
|
|
|
|
res[i] = resmax[i] - resmin[i] + 1;
|
|
/* sanity check: avoid null-sized grid */
|
|
if (res[i] < 4) {
|
|
res[i] = 4;
|
|
resmax[i] = resmin[i] + 4;
|
|
}
|
|
/* sanity check: avoid too large grid size */
|
|
if (res[i] > MAX_HAIR_GRID_RES) {
|
|
res[i] = MAX_HAIR_GRID_RES;
|
|
resmax[i] = resmin[i] + MAX_HAIR_GRID_RES;
|
|
}
|
|
|
|
gmin_margin[i] = float(resmin[i]) * cellsize;
|
|
gmax_margin[i] = float(resmax[i]) * cellsize;
|
|
}
|
|
size = hair_grid_size(res);
|
|
|
|
grid = MEM_cnew<HairGrid>("hair grid");
|
|
grid->res[0] = res[0];
|
|
grid->res[1] = res[1];
|
|
grid->res[2] = res[2];
|
|
copy_v3_v3(grid->gmin, gmin_margin);
|
|
copy_v3_v3(grid->gmax, gmax_margin);
|
|
grid->cellsize = cellsize;
|
|
grid->inv_cellsize = scale;
|
|
grid->verts = (HairGridVert *)MEM_callocN(sizeof(HairGridVert) * size, "hair voxel data");
|
|
|
|
return grid;
|
|
}
|
|
|
|
void SIM_hair_volume_free_vertex_grid(HairGrid *grid)
|
|
{
|
|
if (grid) {
|
|
if (grid->verts) {
|
|
MEM_freeN(grid->verts);
|
|
}
|
|
MEM_freeN(grid);
|
|
}
|
|
}
|
|
|
|
void SIM_hair_volume_grid_geometry(
|
|
HairGrid *grid, float *cellsize, int res[3], float gmin[3], float gmax[3])
|
|
{
|
|
if (cellsize) {
|
|
*cellsize = grid->cellsize;
|
|
}
|
|
if (res) {
|
|
copy_v3_v3_int(res, grid->res);
|
|
}
|
|
if (gmin) {
|
|
copy_v3_v3(gmin, grid->gmin);
|
|
}
|
|
if (gmax) {
|
|
copy_v3_v3(gmax, grid->gmax);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static HairGridVert *hair_volume_create_collision_grid(ClothModifierData *clmd,
|
|
lfVector *lX,
|
|
uint numverts)
|
|
{
|
|
int res = hair_grid_res;
|
|
int size = hair_grid_size(res);
|
|
HairGridVert *collgrid;
|
|
ListBase *colliders;
|
|
ColliderCache *col = NULL;
|
|
float gmin[3], gmax[3], scale[3];
|
|
/* 2.0f is an experimental value that seems to give good results */
|
|
float collfac = 2.0f * clmd->sim_parms->collider_friction;
|
|
uint v = 0;
|
|
int i = 0;
|
|
|
|
hair_volume_get_boundbox(lX, numverts, gmin, gmax);
|
|
hair_grid_get_scale(res, gmin, gmax, scale);
|
|
|
|
collgrid = MEM_mallocN(sizeof(HairGridVert) * size, "hair collider voxel data");
|
|
|
|
/* initialize grid */
|
|
for (i = 0; i < size; i++) {
|
|
zero_v3(collgrid[i].velocity);
|
|
collgrid[i].density = 0.0f;
|
|
}
|
|
|
|
/* gather colliders */
|
|
colliders = BKE_collider_cache_create(depsgraph, NULL, NULL);
|
|
if (colliders && collfac > 0.0f) {
|
|
for (col = colliders->first; col; col = col->next) {
|
|
float3 *loc0 = col->collmd->x;
|
|
float3 *loc1 = col->collmd->xnew;
|
|
float vel[3];
|
|
float weights[8];
|
|
int di, dj, dk;
|
|
|
|
for (v = 0; v < col->collmd->numverts; v++, loc0++, loc1++) {
|
|
int offset;
|
|
|
|
if (!hair_grid_point_valid(loc1->co, gmin, gmax)) {
|
|
continue;
|
|
}
|
|
|
|
offset = hair_grid_weights(res, gmin, scale, lX[v], weights);
|
|
|
|
sub_v3_v3v3(vel, loc1->co, loc0->co);
|
|
|
|
for (di = 0; di < 2; di++) {
|
|
for (dj = 0; dj < 2; dj++) {
|
|
for (dk = 0; dk < 2; dk++) {
|
|
int voffset = offset + di + (dj + dk * res) * res;
|
|
int iw = di + dj * 2 + dk * 4;
|
|
|
|
collgrid[voffset].density += weights[iw];
|
|
madd_v3_v3fl(collgrid[voffset].velocity, vel, weights[iw]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
BKE_collider_cache_free(&colliders);
|
|
|
|
/* divide velocity with density */
|
|
for (i = 0; i < size; i++) {
|
|
float density = collgrid[i].density;
|
|
if (density > 0.0f) {
|
|
mul_v3_fl(collgrid[i].velocity, 1.0f / density);
|
|
}
|
|
}
|
|
|
|
return collgrid;
|
|
}
|
|
#endif
|