Blender V5.0
fluid.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "BLI_listbase.h"
12
13#include "BLI_fileops.h"
14#include "BLI_hash.h"
15#include "BLI_math_geom.h"
16#include "BLI_math_matrix.h"
17#include "BLI_math_vector.h"
18#include "BLI_path_utils.hh"
19#include "BLI_string.h"
20#include "BLI_task.h"
21#include "BLI_utildefines.h"
22
23#include "DNA_colorband_types.h"
24#include "DNA_defaults.h"
25#include "DNA_fluid_types.h"
26#include "DNA_modifier_types.h"
27#include "DNA_object_types.h"
28#include "DNA_rigidbody_types.h"
29#include "DNA_texture_types.h"
30
31#include "BKE_attribute.hh"
32#include "BKE_effect.h"
33#include "BKE_fluid.h"
34#include "BKE_global.hh"
35#include "BKE_layer.hh"
36#include "BKE_lib_id.hh"
37#include "BKE_modifier.hh"
38#include "BKE_pointcache.h"
39
40#ifdef WITH_FLUID
41
42# include <algorithm>
43# include <cfloat>
44# include <cmath>
45# include <cstdio>
46# include <cstring> /* memset */
47
48# include "DNA_customdata_types.h"
49# include "DNA_light_types.h"
50# include "DNA_mesh_types.h"
51# include "DNA_meshdata_types.h"
52# include "DNA_particle_types.h"
53# include "DNA_scene_types.h"
54
55# include "BLI_kdopbvh.hh"
56# include "BLI_kdtree.h"
57# include "BLI_math_vector.hh"
58# include "BLI_mutex.hh"
59# include "BLI_threads.h"
60# include "BLI_voxel.h"
61
62# include "BKE_bvhutils.hh"
63# include "BKE_collision.h"
64# include "BKE_customdata.hh"
65# include "BKE_deform.hh"
66# include "BKE_mesh.hh"
67# include "BKE_mesh_runtime.hh"
68# include "BKE_object.hh"
69# include "BKE_particle.h"
70# include "BKE_scene.hh"
71# include "BKE_texture.h"
72
73# include "DEG_depsgraph.hh"
74# include "DEG_depsgraph_query.hh"
75
76# include "RE_texture.h"
77
78# include "CLG_log.h"
79
80# include "manta_fluid_API.h"
81
82#endif /* WITH_FLUID */
83
85#define DT_DEFAULT 0.1f
86
88#define PHI_MAX 9999.0f
89
90static void fluid_modifier_reset_ex(FluidModifierData *fmd, bool need_lock);
91
92#ifdef WITH_FLUID
93// #define DEBUG_PRINT
94
95static CLG_LogRef LOG = {"physics.fluid"};
96
97/* -------------------------------------------------------------------- */
100
101static blender::Mutex object_update_lock;
102
103# define ADD_IF_LOWER_POS(a, b) min_ff((a) + (b), max_ff((a), (b)))
104# define ADD_IF_LOWER_NEG(a, b) max_ff((a) + (b), min_ff((a), (b)))
105# define ADD_IF_LOWER(a, b) (((b) > 0) ? ADD_IF_LOWER_POS((a), (b)) : ADD_IF_LOWER_NEG((a), (b)))
106
107bool BKE_fluid_reallocate_fluid(FluidDomainSettings *fds, int res[3], int free_old)
108{
109 if (free_old && fds->fluid) {
110 manta_free(fds->fluid);
111 }
112 if (!min_iii(res[0], res[1], res[2])) {
113 fds->fluid = nullptr;
114 }
115 else {
116 fds->fluid = manta_init(res, fds->fmd);
117
118 fds->res_noise[0] = res[0] * fds->noise_scale;
119 fds->res_noise[1] = res[1] * fds->noise_scale;
120 fds->res_noise[2] = res[2] * fds->noise_scale;
121 }
122
123 return (fds->fluid != nullptr);
124}
125
127 int o_res[3],
128 int n_res[3],
129 const int o_min[3],
130 const int n_min[3],
131 const int o_max[3],
132 int o_shift[3],
133 int n_shift[3])
134{
135 MANTA *fluid_old = fds->fluid;
136 const int block_size = fds->noise_scale;
137 int new_shift[3] = {0};
138 sub_v3_v3v3_int(new_shift, n_shift, o_shift);
139
140 /* Allocate new fluid data. */
141 BKE_fluid_reallocate_fluid(fds, n_res, 0);
142
143 int o_total_cells = o_res[0] * o_res[1] * o_res[2];
144 int n_total_cells = n_res[0] * n_res[1] * n_res[2];
145
146 /* Copy values from old fluid to new fluid object. */
147 if (o_total_cells > 1 && n_total_cells > 1) {
148 float *o_dens = manta_smoke_get_density(fluid_old);
149 float *o_react = manta_smoke_get_react(fluid_old);
150 float *o_flame = manta_smoke_get_flame(fluid_old);
151 float *o_fuel = manta_smoke_get_fuel(fluid_old);
152 float *o_heat = manta_smoke_get_heat(fluid_old);
153 float *o_vx = manta_get_velocity_x(fluid_old);
154 float *o_vy = manta_get_velocity_y(fluid_old);
155 float *o_vz = manta_get_velocity_z(fluid_old);
156 float *o_r = manta_smoke_get_color_r(fluid_old);
157 float *o_g = manta_smoke_get_color_g(fluid_old);
158 float *o_b = manta_smoke_get_color_b(fluid_old);
159
160 float *n_dens = manta_smoke_get_density(fds->fluid);
161 float *n_react = manta_smoke_get_react(fds->fluid);
162 float *n_flame = manta_smoke_get_flame(fds->fluid);
163 float *n_fuel = manta_smoke_get_fuel(fds->fluid);
164 float *n_heat = manta_smoke_get_heat(fds->fluid);
165 float *n_vx = manta_get_velocity_x(fds->fluid);
166 float *n_vy = manta_get_velocity_y(fds->fluid);
167 float *n_vz = manta_get_velocity_z(fds->fluid);
168 float *n_r = manta_smoke_get_color_r(fds->fluid);
169 float *n_g = manta_smoke_get_color_g(fds->fluid);
170 float *n_b = manta_smoke_get_color_b(fds->fluid);
171
172 /* Noise smoke fields. */
173 float *o_wt_dens = manta_noise_get_density(fluid_old);
174 float *o_wt_react = manta_noise_get_react(fluid_old);
175 float *o_wt_flame = manta_noise_get_flame(fluid_old);
176 float *o_wt_fuel = manta_noise_get_fuel(fluid_old);
177 float *o_wt_r = manta_noise_get_color_r(fluid_old);
178 float *o_wt_g = manta_noise_get_color_g(fluid_old);
179 float *o_wt_b = manta_noise_get_color_b(fluid_old);
180 float *o_wt_tcu = manta_noise_get_texture_u(fluid_old);
181 float *o_wt_tcv = manta_noise_get_texture_v(fluid_old);
182 float *o_wt_tcw = manta_noise_get_texture_w(fluid_old);
183 float *o_wt_tcu2 = manta_noise_get_texture_u2(fluid_old);
184 float *o_wt_tcv2 = manta_noise_get_texture_v2(fluid_old);
185 float *o_wt_tcw2 = manta_noise_get_texture_w2(fluid_old);
186
187 float *n_wt_dens = manta_noise_get_density(fds->fluid);
188 float *n_wt_react = manta_noise_get_react(fds->fluid);
189 float *n_wt_flame = manta_noise_get_flame(fds->fluid);
190 float *n_wt_fuel = manta_noise_get_fuel(fds->fluid);
191 float *n_wt_r = manta_noise_get_color_r(fds->fluid);
192 float *n_wt_g = manta_noise_get_color_g(fds->fluid);
193 float *n_wt_b = manta_noise_get_color_b(fds->fluid);
194 float *n_wt_tcu = manta_noise_get_texture_u(fds->fluid);
195 float *n_wt_tcv = manta_noise_get_texture_v(fds->fluid);
196 float *n_wt_tcw = manta_noise_get_texture_w(fds->fluid);
197 float *n_wt_tcu2 = manta_noise_get_texture_u2(fds->fluid);
198 float *n_wt_tcv2 = manta_noise_get_texture_v2(fds->fluid);
199 float *n_wt_tcw2 = manta_noise_get_texture_w2(fds->fluid);
200
201 int wt_res_old[3];
202 manta_noise_get_res(fluid_old, wt_res_old);
203
204 for (int z = o_min[2]; z < o_max[2]; z++) {
205 for (int y = o_min[1]; y < o_max[1]; y++) {
206 for (int x = o_min[0]; x < o_max[0]; x++) {
207 /* old grid index */
208 int xo = x - o_min[0];
209 int yo = y - o_min[1];
210 int zo = z - o_min[2];
211 int index_old = manta_get_index(xo, o_res[0], yo, o_res[1], zo);
212 /* new grid index */
213 int xn = x - n_min[0] - new_shift[0];
214 int yn = y - n_min[1] - new_shift[1];
215 int zn = z - n_min[2] - new_shift[2];
216 int index_new = manta_get_index(xn, n_res[0], yn, n_res[1], zn);
217
218 /* Skip if outside new domain. */
219 if (xn < 0 || xn >= n_res[0] || yn < 0 || yn >= n_res[1] || zn < 0 || zn >= n_res[2]) {
220 continue;
221 }
222# if 0
223 /* Note (sebbas):
224 * Disabling this "skip section" as not copying borders results in weird cut-off effects.
225 * It is possible that this cutting off is the reason for line effects as seen in #74559.
226 * Since domain borders will be handled on the simulation side anyways,
227 * copying border values should not be an issue. */
228
229 /* boundary cells will be skipped when copying data */
230 int bwidth = fds->boundary_width;
231
232 /* Skip if trying to copy from old boundary cell. */
233 if (xo < bwidth || yo < bwidth || zo < bwidth || xo >= o_res[0] - bwidth ||
234 yo >= o_res[1] - bwidth || zo >= o_res[2] - bwidth)
235 {
236 continue;
237 }
238 /* Skip if trying to copy into new boundary cell. */
239 if (xn < bwidth || yn < bwidth || zn < bwidth || xn >= n_res[0] - bwidth ||
240 yn >= n_res[1] - bwidth || zn >= n_res[2] - bwidth)
241 {
242 continue;
243 }
244# endif
245
246 /* copy data */
247 if (fds->flags & FLUID_DOMAIN_USE_NOISE) {
248 int i, j, k;
249 /* old grid index */
250 int xx_o = xo * block_size;
251 int yy_o = yo * block_size;
252 int zz_o = zo * block_size;
253 /* new grid index */
254 int xx_n = xn * block_size;
255 int yy_n = yn * block_size;
256 int zz_n = zn * block_size;
257
258 /* insert old texture values into new texture grids */
259 n_wt_tcu[index_new] = o_wt_tcu[index_old];
260 n_wt_tcv[index_new] = o_wt_tcv[index_old];
261 n_wt_tcw[index_new] = o_wt_tcw[index_old];
262
263 n_wt_tcu2[index_new] = o_wt_tcu2[index_old];
264 n_wt_tcv2[index_new] = o_wt_tcv2[index_old];
265 n_wt_tcw2[index_new] = o_wt_tcw2[index_old];
266
267 for (i = 0; i < block_size; i++) {
268 for (j = 0; j < block_size; j++) {
269 for (k = 0; k < block_size; k++) {
270 int big_index_old = manta_get_index(
271 xx_o + i, wt_res_old[0], yy_o + j, wt_res_old[1], zz_o + k);
272 int big_index_new = manta_get_index(
273 xx_n + i, fds->res_noise[0], yy_n + j, fds->res_noise[1], zz_n + k);
274 /* copy data */
275 n_wt_dens[big_index_new] = o_wt_dens[big_index_old];
276 if (n_wt_flame && o_wt_flame) {
277 n_wt_flame[big_index_new] = o_wt_flame[big_index_old];
278 n_wt_fuel[big_index_new] = o_wt_fuel[big_index_old];
279 n_wt_react[big_index_new] = o_wt_react[big_index_old];
280 }
281 if (n_wt_r && o_wt_r) {
282 n_wt_r[big_index_new] = o_wt_r[big_index_old];
283 n_wt_g[big_index_new] = o_wt_g[big_index_old];
284 n_wt_b[big_index_new] = o_wt_b[big_index_old];
285 }
286 }
287 }
288 }
289 }
290
291 n_dens[index_new] = o_dens[index_old];
292 /* heat */
293 if (n_heat && o_heat) {
294 n_heat[index_new] = o_heat[index_old];
295 }
296 /* fuel */
297 if (n_fuel && o_fuel) {
298 n_flame[index_new] = o_flame[index_old];
299 n_fuel[index_new] = o_fuel[index_old];
300 n_react[index_new] = o_react[index_old];
301 }
302 /* color */
303 if (o_r && n_r) {
304 n_r[index_new] = o_r[index_old];
305 n_g[index_new] = o_g[index_old];
306 n_b[index_new] = o_b[index_old];
307 }
308 n_vx[index_new] = o_vx[index_old];
309 n_vy[index_new] = o_vy[index_old];
310 n_vz[index_new] = o_vz[index_old];
311 }
312 }
313 }
314 }
315 manta_free(fluid_old);
316}
317
319{
323 BKE_fluid_cache_free(fds, ob, cache_map);
324}
325
326void BKE_fluid_cache_free(FluidDomainSettings *fds, Object *ob, int cache_map)
327{
328 char temp_dir[FILE_MAX];
329 int flags = fds->cache_flag;
330 const char *relbase = BKE_modifier_path_relbase_from_global(ob);
331
332 if (cache_map & FLUID_DOMAIN_OUTDATED_DATA) {
334 BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG);
335 BLI_path_abs(temp_dir, relbase);
336 if (BLI_exists(temp_dir)) {
337 BLI_delete(temp_dir, true, true);
338 }
339 BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA);
340 BLI_path_abs(temp_dir, relbase);
341 if (BLI_exists(temp_dir)) {
342 BLI_delete(temp_dir, true, true);
343 }
344 BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT);
345 BLI_path_abs(temp_dir, relbase);
346 if (BLI_exists(temp_dir)) {
347 BLI_delete(temp_dir, true, true);
348 }
349 fds->cache_frame_pause_data = 0;
350 }
351 if (cache_map & FLUID_DOMAIN_OUTDATED_NOISE) {
353 BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE);
354 BLI_path_abs(temp_dir, relbase);
355 if (BLI_exists(temp_dir)) {
356 BLI_delete(temp_dir, true, true);
357 }
359 }
360 if (cache_map & FLUID_DOMAIN_OUTDATED_MESH) {
362 BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH);
363 BLI_path_abs(temp_dir, relbase);
364 if (BLI_exists(temp_dir)) {
365 BLI_delete(temp_dir, true, true);
366 }
367 fds->cache_frame_pause_mesh = 0;
368 }
369 if (cache_map & FLUID_DOMAIN_OUTDATED_PARTICLES) {
372 BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES);
373 BLI_path_abs(temp_dir, relbase);
374 if (BLI_exists(temp_dir)) {
375 BLI_delete(temp_dir, true, true);
376 }
378 }
379 if (cache_map & FLUID_DOMAIN_OUTDATED_GUIDE) {
381 BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE);
382 BLI_path_abs(temp_dir, relbase);
383 if (BLI_exists(temp_dir)) {
384 BLI_delete(temp_dir, true, true);
385 }
387 }
388 fds->cache_flag = flags;
389}
390
391/* convert global position to domain cell space */
392static void manta_pos_to_cell(FluidDomainSettings *fds, float pos[3])
393{
394 mul_m4_v3(fds->imat, pos);
395 sub_v3_v3(pos, fds->p0);
396 pos[0] *= 1.0f / fds->cell_size[0];
397 pos[1] *= 1.0f / fds->cell_size[1];
398 pos[2] *= 1.0f / fds->cell_size[2];
399}
400
401/* Set domain transformations and base resolution from object mesh. */
402static void manta_set_domain_from_mesh(FluidDomainSettings *fds,
403 Object *ob,
404 Mesh *mesh,
405 bool init_resolution)
406{
407 size_t i;
408 float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX}, max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
409 float size[3];
410
411 blender::MutableSpan<blender::float3> positions = mesh->vert_positions_for_write();
412 float scale = 0.0;
413 int res;
414
415 res = fds->maxres;
416
417 /* Set minimum and maximum coordinates of BB. */
418 for (i = 0; i < mesh->verts_num; i++) {
419 minmax_v3v3_v3(min, max, positions[i]);
420 }
421
422 /* Set domain bounds. */
423 copy_v3_v3(fds->p0, min);
424 copy_v3_v3(fds->p1, max);
425 fds->dx = 1.0f / res;
426
427 /* Calculate domain dimensions. */
429 if (init_resolution) {
430 zero_v3_int(fds->base_res);
432 }
433 /* Apply object scale. */
434 for (i = 0; i < 3; i++) {
435 size[i] = fabsf(size[i] * ob->scale[i]);
436 }
438 copy_v3_v3(fds->dp0, min);
439
440 invert_m4_m4(fds->imat, ob->object_to_world().ptr());
441
442 /* Prevent crash when initializing a plane as domain. */
443 if (!init_resolution || (size[0] < FLT_EPSILON) || (size[1] < FLT_EPSILON) ||
444 (size[2] < FLT_EPSILON))
445 {
446 return;
447 }
448
449 /* Define grid resolutions from longest domain side. */
450 if (size[0] >= std::max(size[1], size[2])) {
451 scale = res / size[0];
452 fds->scale = size[0] / fabsf(ob->scale[0]);
453 fds->base_res[0] = res;
454 fds->base_res[1] = max_ii(int(size[1] * scale + 0.5f), 4);
455 fds->base_res[2] = max_ii(int(size[2] * scale + 0.5f), 4);
456 }
457 else if (size[1] >= std::max(size[0], size[2])) {
458 scale = res / size[1];
459 fds->scale = size[1] / fabsf(ob->scale[1]);
460 fds->base_res[0] = max_ii(int(size[0] * scale + 0.5f), 4);
461 fds->base_res[1] = res;
462 fds->base_res[2] = max_ii(int(size[2] * scale + 0.5f), 4);
463 }
464 else {
465 scale = res / size[2];
466 fds->scale = size[2] / fabsf(ob->scale[2]);
467 fds->base_res[0] = max_ii(int(size[0] * scale + 0.5f), 4);
468 fds->base_res[1] = max_ii(int(size[1] * scale + 0.5f), 4);
469 fds->base_res[2] = res;
470 }
471
472 /* Set cell size. */
473 fds->cell_size[0] /= float(fds->base_res[0]);
474 fds->cell_size[1] /= float(fds->base_res[1]);
475 fds->cell_size[2] /= float(fds->base_res[2]);
476}
477
478static void update_final_gravity(FluidDomainSettings *fds, Scene *scene)
479{
482 }
483 else {
484 copy_v3_v3(fds->gravity_final, fds->gravity);
485 }
487}
488
489static bool fluid_modifier_init(
490 FluidModifierData *fmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *mesh)
491{
492 int scene_framenr = int(DEG_get_ctime(depsgraph));
493
494 if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain && !fmd->domain->fluid) {
495 FluidDomainSettings *fds = fmd->domain;
496 int res[3];
497 /* Set domain dimensions from mesh. */
498 manta_set_domain_from_mesh(fds, ob, mesh, true);
499 /* Set domain gravity, use global gravity if enabled. */
500 update_final_gravity(fds, scene);
501 /* Reset domain values. */
502 zero_v3_int(fds->shift);
503 zero_v3(fds->shift_f);
504 add_v3_fl(fds->shift_f, 0.5f);
505 zero_v3(fds->prev_loc);
506 mul_m4_v3(ob->object_to_world().ptr(), fds->prev_loc);
507 copy_m4_m4(fds->obmat, ob->object_to_world().ptr());
508
509 /* Set resolutions. */
510 if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS &&
512 {
513 res[0] = res[1] = res[2] = 1; /* Use minimum res for adaptive init. */
514 }
515 else {
516 copy_v3_v3_int(res, fds->base_res);
517 }
518 copy_v3_v3_int(fds->res, res);
519 fds->total_cells = fds->res[0] * fds->res[1] * fds->res[2];
520 fds->res_min[0] = fds->res_min[1] = fds->res_min[2] = 0;
521 copy_v3_v3_int(fds->res_max, res);
522
523 /* Set time, frame length = 0.1 is at 25fps. */
524 fds->frame_length = DT_DEFAULT * (25.0f / scene->frames_per_second()) * fds->time_scale;
525 /* Initially dt is equal to frame length (dt can change with adaptive-time stepping though). */
526 fds->dt = fds->frame_length;
527 fds->time_per_frame = 0;
528
529 fmd->time = scene_framenr;
530
531 /* Allocate fluid. */
532 return BKE_fluid_reallocate_fluid(fds, fds->res, 0);
533 }
534 if (fmd->type & MOD_FLUID_TYPE_FLOW) {
535 if (!fmd->flow) {
537 }
538 fmd->time = scene_framenr;
539 return true;
540 }
541 if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
542 if (!fmd->effector) {
544 }
545 fmd->time = scene_framenr;
546 return true;
547 }
548 return false;
549}
550
551/* Forward declarations. */
552static void manta_smoke_calc_transparency(FluidDomainSettings *fds,
553 Scene *scene,
554 ViewLayer *view_layer);
555static float calc_voxel_transp(
556 float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct);
557static void update_distances(int index,
558 float *distance_map,
560 const float ray_start[3],
561 float surface_thickness,
562 bool use_plane_init);
563
564static int get_light(Scene *scene, ViewLayer *view_layer, float *light)
565{
566 int found_light = 0;
567
568 /* Try to find a lamp, preferably local. */
569 BKE_view_layer_synced_ensure(scene, view_layer);
570 LISTBASE_FOREACH (Base *, base_tmp, BKE_view_layer_object_bases_get(view_layer)) {
571 if (base_tmp->object->type == OB_LAMP) {
572 Light *la = static_cast<Light *>(base_tmp->object->data);
573
574 if (la->type == LA_LOCAL) {
575 copy_v3_v3(light, base_tmp->object->object_to_world().location());
576 return 1;
577 }
578 if (!found_light) {
579 copy_v3_v3(light, base_tmp->object->object_to_world().location());
580 found_light = 1;
581 }
582 }
583 }
584
585 return found_light;
586}
587
588static void clamp_bounds_in_domain(FluidDomainSettings *fds,
589 int min[3],
590 int max[3],
591 const float *min_vel,
592 const float *max_vel,
593 int margin,
594 float dt)
595{
596 for (int i = 0; i < 3; i++) {
597 int adapt = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) ? fds->adapt_res : 0;
598 /* Add some margin. */
599 min[i] -= margin;
600 max[i] += margin;
601
602 /* Adapt to velocity. */
603 if (min_vel && min_vel[i] < 0.0f) {
604 min[i] += int(floor(min_vel[i] * dt));
605 }
606 if (max_vel && max_vel[i] > 0.0f) {
607 max[i] += int(ceil(max_vel[i] * dt));
608 }
609
610 /* Clamp within domain max size. */
611 CLAMP(min[i], -adapt, fds->base_res[i] + adapt);
612 CLAMP(max[i], -adapt, fds->base_res[i] + adapt);
613 }
614}
615
616static bool is_static_object(Object *ob)
617{
618 /* Check if the object has modifiers that might make the object "dynamic". */
619 VirtualModifierData virtual_modifier_data;
620 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtual_modifier_data);
621 for (; md; md = md->next) {
622 if (ELEM(md->type,
630 {
631 return false;
632 }
633 }
634
635 /* Active rigid body objects considered to be dynamic fluid objects. */
637 return false;
638 }
639
640 /* Finally, check if the object has animation data. If so, it is considered dynamic. */
641 return !BKE_object_moves_in_time(ob, true);
642}
643
645
646/* -------------------------------------------------------------------- */
649
650struct FluidObjectBB {
651 float *influence;
652 float *velocity;
653 float *distances;
654 float *numobjs;
655 int min[3], max[3], res[3];
656 int hmin[3], hmax[3], hres[3];
657 int total_cells, valid;
658};
659
660static void bb_boundInsert(FluidObjectBB *bb, const float point[3])
661{
662 int i = 0;
663 if (!bb->valid) {
664 for (; i < 3; i++) {
665 bb->min[i] = int(floor(point[i]));
666 bb->max[i] = int(ceil(point[i]));
667 }
668 bb->valid = 1;
669 }
670 else {
671 for (; i < 3; i++) {
672 if (point[i] < bb->min[i]) {
673 bb->min[i] = int(floor(point[i]));
674 }
675 if (point[i] > bb->max[i]) {
676 bb->max[i] = int(ceil(point[i]));
677 }
678 }
679 }
680}
681
682static void bb_allocateData(FluidObjectBB *bb, bool use_velocity, bool use_influence)
683{
684 int i, res[3];
685
686 for (i = 0; i < 3; i++) {
687 res[i] = bb->max[i] - bb->min[i];
688 if (res[i] <= 0) {
689 return;
690 }
691 }
692 bb->total_cells = res[0] * res[1] * res[2];
693 copy_v3_v3_int(bb->res, res);
694
695 bb->numobjs = MEM_calloc_arrayN<float>(bb->total_cells, "fluid_bb_numobjs");
696 if (use_influence) {
697 bb->influence = MEM_calloc_arrayN<float>(bb->total_cells, "fluid_bb_influence");
698 }
699 if (use_velocity) {
700 bb->velocity = MEM_calloc_arrayN<float>(3 * size_t(bb->total_cells), "fluid_bb_velocity");
701 }
702
703 bb->distances = MEM_malloc_arrayN<float>(size_t(bb->total_cells), "fluid_bb_distances");
704 copy_vn_fl(bb->distances, bb->total_cells, FLT_MAX);
705
706 bb->valid = true;
707}
708
709static void bb_freeData(FluidObjectBB *bb)
710{
711 if (bb->numobjs) {
712 MEM_freeN(bb->numobjs);
713 }
714 if (bb->influence) {
715 MEM_freeN(bb->influence);
716 }
717 if (bb->velocity) {
718 MEM_freeN(bb->velocity);
719 }
720 if (bb->distances) {
721 MEM_freeN(bb->distances);
722 }
723}
724
725static void bb_combineMaps(FluidObjectBB *output,
726 FluidObjectBB *bb2,
727 int additive,
728 float sample_size)
729{
730 int i, x, y, z;
731
732 /* Copy-fill input 1 struct and clear output for new allocation. */
733 FluidObjectBB bb1;
734 memcpy(&bb1, output, sizeof(FluidObjectBB));
735 memset(output, 0, sizeof(FluidObjectBB));
736
737 for (i = 0; i < 3; i++) {
738 if (bb1.valid) {
739 output->min[i] = std::min(bb1.min[i], bb2->min[i]);
740 output->max[i] = std::max(bb1.max[i], bb2->max[i]);
741 }
742 else {
743 output->min[i] = bb2->min[i];
744 output->max[i] = bb2->max[i];
745 }
746 }
747 /* Allocate output map. */
748 bb_allocateData(output, (bb1.velocity || bb2->velocity), (bb1.influence || bb2->influence));
749
750 /* Low through bounding box */
751 for (x = output->min[0]; x < output->max[0]; x++) {
752 for (y = output->min[1]; y < output->max[1]; y++) {
753 for (z = output->min[2]; z < output->max[2]; z++) {
754 int index_out = manta_get_index(x - output->min[0],
755 output->res[0],
756 y - output->min[1],
757 output->res[1],
758 z - output->min[2]);
759
760 /* Initialize with first input if in range. */
761 if (x >= bb1.min[0] && x < bb1.max[0] && y >= bb1.min[1] && y < bb1.max[1] &&
762 z >= bb1.min[2] && z < bb1.max[2])
763 {
764 int index_in = manta_get_index(
765 x - bb1.min[0], bb1.res[0], y - bb1.min[1], bb1.res[1], z - bb1.min[2]);
766
767 /* Values. */
768 output->numobjs[index_out] = bb1.numobjs[index_in];
769 if (output->influence && bb1.influence) {
770 output->influence[index_out] = bb1.influence[index_in];
771 }
772 output->distances[index_out] = bb1.distances[index_in];
773 if (output->velocity && bb1.velocity) {
774 copy_v3_v3(&output->velocity[index_out * 3], &bb1.velocity[index_in * 3]);
775 }
776 }
777
778 /* Apply second input if in range. */
779 if (x >= bb2->min[0] && x < bb2->max[0] && y >= bb2->min[1] && y < bb2->max[1] &&
780 z >= bb2->min[2] && z < bb2->max[2])
781 {
782 int index_in = manta_get_index(
783 x - bb2->min[0], bb2->res[0], y - bb2->min[1], bb2->res[1], z - bb2->min[2]);
784
785 /* Values. */
786 output->numobjs[index_out] = std::max(bb2->numobjs[index_in],
787 output->numobjs[index_out]);
788 if (output->influence && bb2->influence) {
789 if (additive) {
790 output->influence[index_out] += bb2->influence[index_in] * sample_size;
791 }
792 else {
793 output->influence[index_out] = std::max(bb2->influence[index_in],
794 output->influence[index_out]);
795 }
796 }
797 output->distances[index_out] = std::min(bb2->distances[index_in],
798 output->distances[index_out]);
799 if (output->velocity && bb2->velocity) {
800 /* Last sample replaces the velocity. */
801 output->velocity[index_out * 3] = ADD_IF_LOWER(output->velocity[index_out * 3],
802 bb2->velocity[index_in * 3]);
803 output->velocity[index_out * 3 + 1] = ADD_IF_LOWER(output->velocity[index_out * 3 + 1],
804 bb2->velocity[index_in * 3 + 1]);
805 output->velocity[index_out * 3 + 2] = ADD_IF_LOWER(output->velocity[index_out * 3 + 2],
806 bb2->velocity[index_in * 3 + 2]);
807 }
808 }
809 } /* Low res loop. */
810 }
811 }
812
813 /* Free original data. */
814 bb_freeData(&bb1);
815}
816
818
819/* -------------------------------------------------------------------- */
822
823BLI_INLINE void apply_effector_fields(FluidEffectorSettings * /*fes*/,
824 int index,
825 float src_distance_value,
826 float *dest_phi_in,
827 float src_numobjs_value,
828 float *dest_numobjs,
829 float const src_vel_value[3],
830 float *dest_vel_x,
831 float *dest_vel_y,
832 float *dest_vel_z)
833{
834 /* Ensure that distance value is "joined" into the levelset. */
835 if (dest_phi_in) {
836 dest_phi_in[index] = std::min(src_distance_value, dest_phi_in[index]);
837 }
838
839 /* Accumulate effector object count (important once effector object overlap). */
840 if (dest_numobjs && src_numobjs_value > 0) {
841 dest_numobjs[index] += 1;
842 }
843
844 /* Accumulate effector velocities for each cell. */
845 if (dest_vel_x && src_numobjs_value > 0) {
846 dest_vel_x[index] += src_vel_value[0];
847 dest_vel_y[index] += src_vel_value[1];
848 dest_vel_z[index] += src_vel_value[2];
849 }
850}
851
853 const blender::Span<blender::float3> vert_positions,
854 const int *corner_verts,
855 const blender::int3 *corner_tris,
856 float *velocity_map,
857 int index,
859 const float ray_start[3],
860 const float *vert_vel,
861 bool has_velocity)
862{
863 BVHTreeNearest nearest = {0};
864 nearest.index = -1;
865
866 /* Distance between two opposing vertices in a unit cube.
867 * I.e. the unit cube diagonal or `sqrt(3)`.
868 * This value is our nearest neighbor search distance. */
869 const float surface_distance = 1.732;
870 /* find_nearest uses squared distance */
871 nearest.dist_sq = surface_distance * surface_distance;
872
873 /* Find the nearest point on the mesh. */
874 if (has_velocity &&
876 tree_data->tree, ray_start, &nearest, tree_data->nearest_callback, tree_data) != -1)
877 {
878 float weights[3];
879 int v1, v2, v3, tri_i = nearest.index;
880
881 /* Calculate barycentric weights for nearest point. */
882 v1 = corner_verts[corner_tris[tri_i][0]];
883 v2 = corner_verts[corner_tris[tri_i][1]];
884 v3 = corner_verts[corner_tris[tri_i][2]];
886 weights, vert_positions[v1], vert_positions[v2], vert_positions[v3], nearest.co);
887
888 /* Apply object velocity. */
889 float hit_vel[3];
890 interp_v3_v3v3v3(hit_vel, &vert_vel[v1 * 3], &vert_vel[v2 * 3], &vert_vel[v3 * 3], weights);
891
892 /* Guiding has additional velocity multiplier */
893 if (fes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
894 mul_v3_fl(hit_vel, fes->vel_multi);
895
896 /* Absolute representation of new object velocity. */
897 blender::float3 abs_hit_vel = blender::math::abs(blender::float3(hit_vel));
898
899 /* Absolute representation of current object velocity. */
900 blender::float3 abs_vel = blender::math::abs(blender::float3(&velocity_map[index * 3]));
901
902 switch (fes->guide_mode) {
904 velocity_map[index * 3] = (velocity_map[index * 3] + hit_vel[0]) * 0.5f;
905 velocity_map[index * 3 + 1] = (velocity_map[index * 3 + 1] + hit_vel[1]) * 0.5f;
906 velocity_map[index * 3 + 2] = (velocity_map[index * 3 + 2] + hit_vel[2]) * 0.5f;
907 break;
909 velocity_map[index * 3] = hit_vel[0];
910 velocity_map[index * 3 + 1] = hit_vel[1];
911 velocity_map[index * 3 + 2] = hit_vel[2];
912 break;
914 velocity_map[index * 3] = std::min(abs_hit_vel[0], abs_vel[0]);
915 velocity_map[index * 3 + 1] = std::min(abs_hit_vel[1], abs_vel[1]);
916 velocity_map[index * 3 + 2] = std::min(abs_hit_vel[2], abs_vel[2]);
917 break;
919 default:
920 velocity_map[index * 3] = std::max(abs_hit_vel[0], abs_vel[0]);
921 velocity_map[index * 3 + 1] = std::max(abs_hit_vel[1], abs_vel[1]);
922 velocity_map[index * 3 + 2] = std::max(abs_hit_vel[2], abs_vel[2]);
923 break;
924 }
925 }
926 else if (fes->type == FLUID_EFFECTOR_TYPE_COLLISION) {
927 velocity_map[index * 3] = hit_vel[0];
928 velocity_map[index * 3 + 1] = hit_vel[1];
929 velocity_map[index * 3 + 2] = hit_vel[2];
930# ifdef DEBUG_PRINT
931 /* Debugging: Print object velocities. */
932 printf("setting effector object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]);
933# endif
934 }
935 else {
936 /* Should never reach this block. */
938 }
939 }
940 else {
941 /* Clear velocities at cells that are not moving. */
942 copy_v3_fl(velocity_map, 0.0);
943 }
944}
945
946struct ObstaclesFromDMData {
948
949 blender::Span<blender::float3> vert_positions;
950 blender::Span<int> corner_verts;
951 blender::Span<blender::int3> corner_tris;
952
953 blender::bke::BVHTreeFromMesh *tree;
954 FluidObjectBB *bb;
955
956 bool has_velocity;
957 float *vert_vel;
958 int *min, *max, *res;
959};
960
961static void obstacles_from_mesh_task_cb(void *__restrict userdata,
962 const int z,
963 const TaskParallelTLS *__restrict /*tls*/)
964{
965 ObstaclesFromDMData *data = static_cast<ObstaclesFromDMData *>(userdata);
966 FluidObjectBB *bb = data->bb;
967
968 for (int x = data->min[0]; x < data->max[0]; x++) {
969 for (int y = data->min[1]; y < data->max[1]; y++) {
970 const int index = manta_get_index(
971 x - bb->min[0], bb->res[0], y - bb->min[1], bb->res[1], z - bb->min[2]);
972 const float ray_start[3] = {float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f};
973
974 /* Calculate levelset values from meshes. Result in bb->distances. */
975 update_distances(index,
976 bb->distances,
977 data->tree,
978 ray_start,
979 data->fes->surface_distance,
980 data->fes->flags & FLUID_EFFECTOR_USE_PLANE_INIT);
981
982 /* Calculate object velocities. Result in bb->velocity. */
984 data->vert_positions,
985 data->corner_verts.data(),
986 data->corner_tris.data(),
987 bb->velocity,
988 index,
989 data->tree,
990 ray_start,
991 data->vert_vel,
992 data->has_velocity);
993
994 /* Increase obstacle count inside of moving obstacles. */
995 if (bb->distances[index] < 0) {
996 bb->numobjs[index]++;
997 }
998 }
999 }
1000}
1001
1002static void obstacles_from_mesh(Object *coll_ob,
1005 FluidObjectBB *bb,
1006 float dt)
1007{
1008 if (fes->mesh) {
1009 int numverts, i;
1010
1011 float *vert_vel = nullptr;
1012 bool has_velocity = false;
1013
1014 Mesh *mesh = BKE_mesh_copy_for_eval(*fes->mesh);
1015 blender::MutableSpan<blender::float3> positions = mesh->vert_positions_for_write();
1016
1017 int min[3], max[3], res[3];
1018
1019 const blender::Span<int> corner_verts = mesh->corner_verts();
1020 const blender::Span<blender::int3> corner_tris = mesh->corner_tris();
1021 numverts = mesh->verts_num;
1022
1023 /* TODO(sebbas): Make initialization of vertex velocities optional? */
1024 {
1025 vert_vel = MEM_calloc_arrayN<float>(3 * size_t(numverts), "manta_obs_velocity");
1026
1027 if (fes->numverts != numverts || !fes->verts_old) {
1028 if (fes->verts_old) {
1029 MEM_freeN(fes->verts_old);
1030 }
1031
1032 fes->verts_old = MEM_calloc_arrayN<float>(3 * size_t(numverts), "manta_obs_verts_old");
1033 fes->numverts = numverts;
1034 }
1035 else {
1036 has_velocity = true;
1037 }
1038 }
1039
1040 /* Transform mesh vertices to domain grid space for fast lookups.
1041 * This is valid because the mesh is copied above. */
1042 for (i = 0; i < numverts; i++) {
1043 float co[3];
1044
1045 /* Vertex position. */
1046 mul_m4_v3(coll_ob->object_to_world().ptr(), positions[i]);
1047 manta_pos_to_cell(fds, positions[i]);
1048
1049 /* Vertex velocity. */
1050 add_v3fl_v3fl_v3i(co, positions[i], fds->shift);
1051 if (has_velocity) {
1052 sub_v3_v3v3(&vert_vel[i * 3], co, &fes->verts_old[i * 3]);
1053 mul_v3_fl(&vert_vel[i * 3], 1.0f / dt);
1054 }
1055 copy_v3_v3(&fes->verts_old[i * 3], co);
1056
1057 /* Calculate emission map bounds. */
1058 bb_boundInsert(bb, positions[i]);
1059 }
1060
1061 mesh->tag_positions_changed();
1062
1063 /* Set emission map.
1064 * Use 3 cell diagonals as margin (3 * 1.732 = 5.196). */
1065 int bounds_margin = int(ceil(5.196));
1066 clamp_bounds_in_domain(fds, bb->min, bb->max, nullptr, nullptr, bounds_margin, dt);
1067 bb_allocateData(bb, true, false);
1068
1069 /* Setup loop bounds. */
1070 for (i = 0; i < 3; i++) {
1071 min[i] = bb->min[i];
1072 max[i] = bb->max[i];
1073 res[i] = bb->res[i];
1074 }
1075
1076 /* Skip effector sampling loop if object has disabled effector. */
1077 bool use_effector = fes->flags & FLUID_EFFECTOR_USE_EFFEC;
1078 blender::bke::BVHTreeFromMesh tree_data = mesh->bvh_corner_tris();
1079 if (use_effector && tree_data.tree != nullptr) {
1080 ObstaclesFromDMData data{};
1081 data.fes = fes;
1082 data.vert_positions = positions;
1083 data.corner_verts = corner_verts;
1084 data.corner_tris = corner_tris;
1085 data.tree = &tree_data;
1086 data.bb = bb;
1087 data.has_velocity = has_velocity;
1088 data.vert_vel = vert_vel;
1089 data.min = min;
1090 data.max = max;
1091 data.res = res;
1092
1093 TaskParallelSettings settings;
1095 settings.min_iter_per_thread = 2;
1096 BLI_task_parallel_range(min[2], max[2], &data, obstacles_from_mesh_task_cb, &settings);
1097 }
1098
1099 if (vert_vel) {
1100 MEM_freeN(vert_vel);
1101 }
1102 BKE_id_free(nullptr, mesh);
1103 }
1104}
1105
1106static void ensure_obstaclefields(FluidDomainSettings *fds)
1107{
1109 manta_ensure_obstacle(fds->fluid, fds->fmd);
1110 }
1112 manta_ensure_guiding(fds->fluid, fds->fmd);
1113 }
1114 manta_update_pointers(fds->fluid, fds->fmd, false);
1115}
1116
1117static void update_obstacleflags(FluidDomainSettings *fds,
1118 Object **coll_ob_array,
1119 int coll_ob_array_len)
1120{
1121 int active_fields = fds->active_fields;
1122 uint coll_index;
1123
1124 /* First, remove all flags that we want to update. */
1126 active_fields &= ~prev_flags;
1127
1128 /* Monitor active fields based on flow settings */
1129 for (coll_index = 0; coll_index < coll_ob_array_len; coll_index++) {
1130 Object *coll_ob = coll_ob_array[coll_index];
1133
1134 /* Sanity check. */
1135 if (!fmd2) {
1136 continue;
1137 }
1138
1139 if ((fmd2->type & MOD_FLUID_TYPE_EFFEC) && fmd2->effector) {
1140 FluidEffectorSettings *fes = fmd2->effector;
1141 if (!fes) {
1142 break;
1143 }
1147 }
1148 if (fes->type == FLUID_EFFECTOR_TYPE_COLLISION) {
1149 active_fields |= FLUID_DOMAIN_ACTIVE_OBSTACLE;
1150 }
1151 if (fes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
1152 active_fields |= FLUID_DOMAIN_ACTIVE_GUIDE;
1153 }
1154 }
1155 }
1156 fds->active_fields = active_fields;
1157}
1158
1159static bool escape_effectorobject(Object *flowobj,
1161 FluidEffectorSettings * /*fes*/,
1162 int frame)
1163{
1164 bool is_static = is_static_object(flowobj);
1165
1166 bool is_resume = (fds->cache_frame_pause_data == frame);
1168 bool is_first_frame = (frame == fds->cache_frame_start);
1169
1170 /* Cannot use static mode with adaptive domain.
1171 * The adaptive domain might expand and only later discover the static object. */
1172 if (is_adaptive) {
1173 is_static = false;
1174 }
1175 /* Skip static effector objects after initial frame. */
1176 if (is_static && !is_first_frame && !is_resume) {
1177 return true;
1178 }
1179 return false;
1180}
1181
1182static void compute_obstaclesemission(Scene *scene,
1183 FluidObjectBB *bb_maps,
1184 Depsgraph *depsgraph,
1185 float dt,
1186 Object **effecobjs,
1187 int frame,
1188 float frame_length,
1190 uint numeffecobjs,
1191 float time_per_frame)
1192{
1193 bool is_first_frame = (frame == fds->cache_frame_start);
1194
1195 /* Prepare effector maps. */
1196 for (int effec_index = 0; effec_index < numeffecobjs; effec_index++) {
1197 Object *effecobj = effecobjs[effec_index];
1200
1201 /* Sanity check. */
1202 if (!fmd2) {
1203 continue;
1204 }
1205
1206 /* Check for initialized effector object. */
1207 if ((fmd2->type & MOD_FLUID_TYPE_EFFEC) && fmd2->effector) {
1208 FluidEffectorSettings *fes = fmd2->effector;
1209 int subframes = fes->subframes;
1210 FluidObjectBB *bb = &bb_maps[effec_index];
1211
1212 /* Optimization: Skip this object under certain conditions. */
1213 if (escape_effectorobject(effecobj, fds, fes, frame)) {
1214 continue;
1215 }
1216
1217 /* First frame cannot have any subframes because there is (obviously) no previous frame from
1218 * where subframes could come from. */
1219 if (is_first_frame) {
1220 subframes = 0;
1221 }
1222
1223 /* More splitting because of emission subframe: If no subframes present, sample_size is 1. */
1224 float sample_size = 1.0f / float(subframes + 1);
1225 float subframe_dt = dt * sample_size;
1226
1227 /* Emission loop. When not using subframes this will loop only once. */
1228 for (int subframe = 0; subframe <= subframes; subframe++) {
1229
1230 /* Temporary emission map used when subframes are enabled, i.e. at least one subframe. */
1231 FluidObjectBB bb_temp = {nullptr};
1232
1233 /* Set scene time */
1234 /* Handle emission subframe */
1235 if ((subframe < subframes || time_per_frame + dt + FLT_EPSILON < frame_length) &&
1236 !is_first_frame)
1237 {
1238 scene->r.subframe = (time_per_frame + (subframe + 1.0f) * subframe_dt) / frame_length;
1239 scene->r.cfra = frame - 1;
1240 }
1241 else {
1242 scene->r.subframe = 0.0f;
1243 scene->r.cfra = frame;
1244 }
1245 /* Sanity check: subframe portion must be between 0 and 1. */
1246 CLAMP(scene->r.subframe, 0.0f, 1.0f);
1247# ifdef DEBUG_PRINT
1248 /* Debugging: Print subframe information. */
1249 printf(
1250 "effector: frame (is first: %d): %d // scene current frame: %d // scene current "
1251 "subframe: "
1252 "%f\n",
1253 is_first_frame,
1254 frame,
1255 scene->r.cfra,
1256 scene->r.subframe);
1257# endif
1258 /* Update frame time, this is considering current subframe fraction
1259 * BLI_mutex_lock() called in manta_step(), so safe to update subframe here
1260 * TODO(sebbas): Using BKE_scene_ctime_get(scene) instead of new DEG_get_ctime(depsgraph)
1261 * as subframes don't work with the latter yet. */
1263 scene,
1264 effecobj,
1265 true,
1267 BKE_scene_ctime_get(scene),
1269
1270 if (subframes) {
1271 obstacles_from_mesh(effecobj, fds, fes, &bb_temp, subframe_dt);
1272 }
1273 else {
1274 obstacles_from_mesh(effecobj, fds, fes, bb, subframe_dt);
1275 }
1276
1277 /* If this we emitted with temp emission map in this loop (subframe emission), we combine
1278 * the temp map with the original emission map. */
1279 if (subframes) {
1280 /* Combine emission maps. */
1281 bb_combineMaps(bb, &bb_temp, 0, 0.0f);
1282 bb_freeData(&bb_temp);
1283 }
1284 }
1285 }
1286 }
1287}
1288
1289static void update_obstacles(Depsgraph *depsgraph,
1290 Scene *scene,
1291 Object *ob,
1293 float time_per_frame,
1294 float frame_length,
1295 int frame,
1296 float dt)
1297{
1298 FluidObjectBB *bb_maps = nullptr;
1299 Object **effecobjs = nullptr;
1300 uint numeffecobjs = 0;
1301 bool is_resume = (fds->cache_frame_pause_data == frame);
1302 bool is_first_frame = (frame == fds->cache_frame_start);
1303
1304 effecobjs = BKE_collision_objects_create(
1305 depsgraph, ob, fds->effector_group, &numeffecobjs, eModifierType_Fluid);
1306
1307 /* Update all effector related flags and ensure that corresponding grids get initialized. */
1308 update_obstacleflags(fds, effecobjs, numeffecobjs);
1309 ensure_obstaclefields(fds);
1310
1311 /* Allocate effector map for each effector object. */
1312 bb_maps = MEM_calloc_arrayN<FluidObjectBB>(numeffecobjs, "fluid_effector_bb_maps");
1313
1314 /* Initialize effector map for each effector object. */
1315 compute_obstaclesemission(scene,
1316 bb_maps,
1317 depsgraph,
1318 dt,
1319 effecobjs,
1320 frame,
1321 frame_length,
1322 fds,
1323 numeffecobjs,
1324 time_per_frame);
1325
1326 float *vel_x = manta_get_ob_velocity_x(fds->fluid);
1327 float *vel_y = manta_get_ob_velocity_y(fds->fluid);
1328 float *vel_z = manta_get_ob_velocity_z(fds->fluid);
1329 float *vel_x_guide = manta_get_guide_velocity_x(fds->fluid);
1330 float *vel_y_guide = manta_get_guide_velocity_y(fds->fluid);
1331 float *vel_z_guide = manta_get_guide_velocity_z(fds->fluid);
1332 float *phi_obs_in = manta_get_phiobs_in(fds->fluid);
1333 float *phi_obsstatic_in = manta_get_phiobsstatic_in(fds->fluid);
1334 float *phi_guide_in = manta_get_phiguide_in(fds->fluid);
1335 float *num_obstacles = manta_get_num_obstacle(fds->fluid);
1336 float *num_guides = manta_get_num_guide(fds->fluid);
1337 uint z;
1338
1339 bool use_adaptivedomain = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN);
1340
1341 /* Grid reset before writing again. */
1342 for (z = 0; z < fds->res[0] * fds->res[1] * fds->res[2]; z++) {
1343
1344 /* Use big value that's not inf to initialize levelset grids. */
1345 if (phi_obs_in) {
1346 phi_obs_in[z] = PHI_MAX;
1347 }
1348 /* Only reset static effectors on first frame. Only use static effectors without adaptive
1349 * domains. */
1350 if (phi_obsstatic_in && (is_first_frame || use_adaptivedomain)) {
1351 phi_obsstatic_in[z] = PHI_MAX;
1352 }
1353 if (phi_guide_in) {
1354 phi_guide_in[z] = PHI_MAX;
1355 }
1356 if (num_obstacles) {
1357 num_obstacles[z] = 0;
1358 }
1359 if (num_guides) {
1360 num_guides[z] = 0;
1361 }
1362 if (vel_x && vel_y && vel_z) {
1363 vel_x[z] = 0.0f;
1364 vel_y[z] = 0.0f;
1365 vel_z[z] = 0.0f;
1366 }
1367 if (vel_x_guide && vel_y_guide && vel_z_guide) {
1368 vel_x_guide[z] = 0.0f;
1369 vel_y_guide[z] = 0.0f;
1370 vel_z_guide[z] = 0.0f;
1371 }
1372 }
1373
1374 /* Prepare grids from effector objects. */
1375 for (int effec_index = 0; effec_index < numeffecobjs; effec_index++) {
1376 Object *effecobj = effecobjs[effec_index];
1379
1380 /* Sanity check. */
1381 if (!fmd2) {
1382 continue;
1383 }
1384
1385 /* Cannot use static mode with adaptive domain.
1386 * The adaptive domain might expand and only later in the simulations discover the static
1387 * object. */
1388 bool is_static = is_static_object(effecobj) && !use_adaptivedomain;
1389
1390 /* Check for initialized effector object. */
1391 if ((fmd2->type & MOD_FLUID_TYPE_EFFEC) && fmd2->effector) {
1392 FluidEffectorSettings *fes = fmd2->effector;
1393
1394 /* Optimization: Skip effector objects with disabled effec flag. */
1395 if ((fes->flags & FLUID_EFFECTOR_USE_EFFEC) == 0) {
1396 continue;
1397 }
1398
1399 FluidObjectBB *bb = &bb_maps[effec_index];
1400 float *velocity_map = bb->velocity;
1401 float *numobjs_map = bb->numobjs;
1402 float *distance_map = bb->distances;
1403
1404 int gx, gy, gz, ex, ey, ez, dx, dy, dz;
1405 size_t e_index, d_index;
1406
1407 /* Loop through every emission map cell. */
1408 for (gx = bb->min[0]; gx < bb->max[0]; gx++) {
1409 for (gy = bb->min[1]; gy < bb->max[1]; gy++) {
1410 for (gz = bb->min[2]; gz < bb->max[2]; gz++) {
1411 /* Compute emission map index. */
1412 ex = gx - bb->min[0];
1413 ey = gy - bb->min[1];
1414 ez = gz - bb->min[2];
1415 e_index = manta_get_index(ex, bb->res[0], ey, bb->res[1], ez);
1416
1417 /* Get domain index. */
1418 dx = gx - fds->res_min[0];
1419 dy = gy - fds->res_min[1];
1420 dz = gz - fds->res_min[2];
1421 d_index = manta_get_index(dx, fds->res[0], dy, fds->res[1], dz);
1422 /* Make sure emission cell is inside the new domain boundary. */
1423 if (dx < 0 || dy < 0 || dz < 0 || dx >= fds->res[0] || dy >= fds->res[1] ||
1424 dz >= fds->res[2])
1425 {
1426 continue;
1427 }
1428
1429 if (fes->type == FLUID_EFFECTOR_TYPE_COLLISION) {
1430 float *levelset = ((is_first_frame || is_resume) && is_static) ? phi_obsstatic_in :
1431 phi_obs_in;
1432 apply_effector_fields(fes,
1433 d_index,
1434 distance_map[e_index],
1435 levelset,
1436 numobjs_map[e_index],
1437 num_obstacles,
1438 &velocity_map[e_index * 3],
1439 vel_x,
1440 vel_y,
1441 vel_z);
1442 }
1443 if (fes->type == FLUID_EFFECTOR_TYPE_GUIDE) {
1444 apply_effector_fields(fes,
1445 d_index,
1446 distance_map[e_index],
1447 phi_guide_in,
1448 numobjs_map[e_index],
1449 num_guides,
1450 &velocity_map[e_index * 3],
1451 vel_x_guide,
1452 vel_y_guide,
1453 vel_z_guide);
1454 }
1455 }
1456 }
1457 } /* End of effector map loop. */
1458 bb_freeData(bb);
1459 } /* End of effector object loop. */
1460 }
1461
1462 BKE_collision_objects_free(effecobjs);
1463 if (bb_maps) {
1464 MEM_freeN(bb_maps);
1465 }
1466}
1467
1469
1470/* -------------------------------------------------------------------- */
1473
1474struct EmitFromParticlesData {
1475 FluidFlowSettings *ffs;
1476 KDTree_3d *tree;
1477
1478 FluidObjectBB *bb;
1479 float *particle_vel;
1480 int *min, *max, *res;
1481
1482 float solid;
1483 float smooth;
1484};
1485
1486static void emit_from_particles_task_cb(void *__restrict userdata,
1487 const int z,
1488 const TaskParallelTLS *__restrict /*tls*/)
1489{
1490 EmitFromParticlesData *data = static_cast<EmitFromParticlesData *>(userdata);
1491 FluidFlowSettings *ffs = data->ffs;
1492 FluidObjectBB *bb = data->bb;
1493
1494 for (int x = data->min[0]; x < data->max[0]; x++) {
1495 for (int y = data->min[1]; y < data->max[1]; y++) {
1496 const int index = manta_get_index(
1497 x - bb->min[0], bb->res[0], y - bb->min[1], bb->res[1], z - bb->min[2]);
1498 const float ray_start[3] = {float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f};
1499
1500 /* Find particle distance from the kdtree. */
1501 KDTreeNearest_3d nearest;
1502 const float range = data->solid + data->smooth;
1503 BLI_kdtree_3d_find_nearest(data->tree, ray_start, &nearest);
1504
1505 if (nearest.dist < range) {
1506 bb->influence[index] = (nearest.dist < data->solid) ?
1507 1.0f :
1508 (1.0f - (nearest.dist - data->solid) / data->smooth);
1509 /* Uses particle velocity as initial velocity for smoke. */
1510 if (ffs->flags & FLUID_FLOW_INITVELOCITY && (ffs->psys->part->phystype != PART_PHYS_NO)) {
1511 madd_v3_v3fl(
1512 &bb->velocity[index * 3], &data->particle_vel[nearest.index * 3], ffs->vel_multi);
1513 }
1514 }
1515 }
1516 }
1517}
1518
1519static void emit_from_particles(Object *flow_ob,
1521 FluidFlowSettings *ffs,
1522 FluidObjectBB *bb,
1523 Depsgraph *depsgraph,
1524 Scene *scene,
1525 float dt)
1526{
1527 if (ffs && ffs->psys && ffs->psys->part &&
1528 ELEM(ffs->psys->part->type, PART_EMITTER, PART_FLUID)) /* Is particle system selected. */
1529 {
1531 ParticleSystem *psys = ffs->psys;
1532 float *particle_pos;
1533 float *particle_vel;
1534 int totpart = psys->totpart, totchild;
1535 int p = 0;
1536 int valid_particles = 0;
1537 int bounds_margin = 1;
1538
1539 /* radius based flow */
1540 const float solid = ffs->particle_size * 0.5f;
1541 const float smooth = 0.5f; /* add 0.5 cells of linear falloff to reduce aliasing */
1542 KDTree_3d *tree = nullptr;
1543
1544 sim.depsgraph = depsgraph;
1545 sim.scene = scene;
1546 sim.ob = flow_ob;
1547 sim.psys = psys;
1548
1549 psys_sim_data_init(&sim);
1550
1551 /* initialize particle cache */
1552 if (psys->part->type == PART_HAIR) {
1553 /* TODO: PART_HAIR not supported whatsoever. */
1554 totchild = 0;
1555 }
1556 else {
1557 totchild = psys->totchild * psys->part->disp / 100;
1558 }
1559
1560 particle_pos = MEM_calloc_arrayN<float>(3 * size_t(totpart + totchild),
1561 "manta_flow_particles_pos");
1562 particle_vel = MEM_calloc_arrayN<float>(3 * size_t(totpart + totchild),
1563 "manta_flow_particles_vel");
1564
1565 /* setup particle radius emission if enabled */
1566 if (ffs->flags & FLUID_FLOW_USE_PART_SIZE) {
1567 tree = BLI_kdtree_3d_new(psys->totpart + psys->totchild);
1568 bounds_margin = int(ceil(solid + smooth));
1569 }
1570
1571 /* calculate local position for each particle */
1572 for (p = 0; p < totpart + totchild; p++) {
1574 float *pos, *vel;
1575 if (p < totpart) {
1576 if (psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
1577 continue;
1578 }
1579 }
1580 else {
1581 /* handle child particle */
1582 ChildParticle *cpa = &psys->child[p - totpart];
1583 if (psys->particles[cpa->parent].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
1584 continue;
1585 }
1586 }
1587
1588 /* `DEG_get_ctime(depsgraph)` does not give sub-frame time. */
1589 state.time = BKE_scene_ctime_get(scene);
1590
1591 if (psys_get_particle_state(&sim, p, &state, false) == 0) {
1592 continue;
1593 }
1594
1595 /* location */
1596 pos = &particle_pos[valid_particles * 3];
1597 copy_v3_v3(pos, state.co);
1598 manta_pos_to_cell(fds, pos);
1599
1600 /* velocity */
1601 vel = &particle_vel[valid_particles * 3];
1602 copy_v3_v3(vel, state.vel);
1603 mul_mat3_m4_v3(fds->imat, &particle_vel[valid_particles * 3]);
1604
1605 if (ffs->flags & FLUID_FLOW_USE_PART_SIZE) {
1606 BLI_kdtree_3d_insert(tree, valid_particles, pos);
1607 }
1608
1609 /* calculate emission map bounds */
1610 bb_boundInsert(bb, pos);
1611 valid_particles++;
1612 }
1613
1614 /* set emission map */
1615 clamp_bounds_in_domain(fds, bb->min, bb->max, nullptr, nullptr, bounds_margin, dt);
1616 bb_allocateData(bb, ffs->flags & FLUID_FLOW_INITVELOCITY, true);
1617
1618 if (!(ffs->flags & FLUID_FLOW_USE_PART_SIZE)) {
1619 for (p = 0; p < valid_particles; p++) {
1620 int cell[3];
1621 size_t i = 0;
1622 size_t index = 0;
1623 int badcell = 0;
1624
1625 /* 1. get corresponding cell */
1626 cell[0] = floor(particle_pos[p * 3]) - bb->min[0];
1627 cell[1] = floor(particle_pos[p * 3 + 1]) - bb->min[1];
1628 cell[2] = floor(particle_pos[p * 3 + 2]) - bb->min[2];
1629 /* check if cell is valid (in the domain boundary) */
1630 for (i = 0; i < 3; i++) {
1631 if ((cell[i] > bb->res[i] - 1) || (cell[i] < 0)) {
1632 badcell = 1;
1633 break;
1634 }
1635 }
1636 if (badcell) {
1637 continue;
1638 }
1639 /* get cell index */
1640 index = manta_get_index(cell[0], bb->res[0], cell[1], bb->res[1], cell[2]);
1641 /* Add influence to emission map */
1642 bb->influence[index] = 1.0f;
1643 /* Uses particle velocity as initial velocity for smoke */
1644 if (ffs->flags & FLUID_FLOW_INITVELOCITY && (psys->part->phystype != PART_PHYS_NO)) {
1645 madd_v3_v3fl(&bb->velocity[index * 3], &particle_vel[p * 3], ffs->vel_multi);
1646 }
1647 } /* particles loop */
1648 }
1649 else if (valid_particles > 0) { /* #FLUID_FLOW_USE_PART_SIZE */
1650 int min[3], max[3], res[3];
1651
1652 /* setup loop bounds */
1653 for (int i = 0; i < 3; i++) {
1654 min[i] = bb->min[i];
1655 max[i] = bb->max[i];
1656 res[i] = bb->res[i];
1657 }
1658
1659 BLI_kdtree_3d_balance(tree);
1660
1661 EmitFromParticlesData data{};
1662 data.ffs = ffs;
1663 data.tree = tree;
1664 data.bb = bb;
1665 data.particle_vel = particle_vel;
1666 data.min = min;
1667 data.max = max;
1668 data.res = res;
1669 data.solid = solid;
1670 data.smooth = smooth;
1671
1672 TaskParallelSettings settings;
1674 settings.min_iter_per_thread = 2;
1675 BLI_task_parallel_range(min[2], max[2], &data, emit_from_particles_task_cb, &settings);
1676 }
1677
1678 if (ffs->flags & FLUID_FLOW_USE_PART_SIZE) {
1679 BLI_kdtree_3d_free(tree);
1680 }
1681
1682 /* free data */
1683 if (particle_pos) {
1684 MEM_freeN(particle_pos);
1685 }
1686 if (particle_vel) {
1687 MEM_freeN(particle_vel);
1688 }
1689
1690 psys_sim_data_free(&sim);
1691 }
1692}
1693
1694/* Calculate map of (minimum) distances to flow/obstacle surface. Distances outside mesh are
1695 * positive, inside negative. */
1696static void update_distances(int index,
1697 float *distance_map,
1699 const float ray_start[3],
1700 float surface_thickness,
1701 bool use_plane_init)
1702{
1703 float min_dist = PHI_MAX;
1704
1705 /* Planar initialization: Find nearest cells around mesh. */
1706 if (use_plane_init) {
1707 BVHTreeNearest nearest = {0};
1708 nearest.index = -1;
1709 /* Distance between two opposing vertices in a unit cube.
1710 * I.e. the unit cube diagonal or `sqrt(3)`.
1711 * This value is our nearest neighbor search distance. */
1712 const float surface_distance = 1.732;
1713 /* find_nearest uses squared distance. */
1714 nearest.dist_sq = surface_distance * surface_distance;
1715
1716 /* Subtract optional surface thickness value and virtually increase the object size. */
1717 if (surface_thickness) {
1718 nearest.dist_sq += surface_thickness;
1719 }
1720
1722 tree_data->tree, ray_start, &nearest, tree_data->nearest_callback, tree_data) != -1)
1723 {
1724 float ray[3] = {0};
1725 sub_v3_v3v3(ray, ray_start, nearest.co);
1726 min_dist = len_v3(ray);
1727 min_dist = (-1.0f) * fabsf(min_dist);
1728 }
1729 }
1730 /* Volumetric initialization: Ray-casts around mesh object. */
1731 else {
1732 /* Ray-casts in 26 directions.
1733 * (6 main axis + 12 quadrant diagonals (2D) + 8 octant diagonals (3D)). */
1734 float ray_dirs[26][3] = {
1735 {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f},
1736 {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f},
1737 {-1.0f, 1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, {1.0f, 0.0f, -1.0f},
1738 {-1.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 1.0f}, {0.0f, 1.0f, -1.0f},
1739 {0.0f, -1.0f, 1.0f}, {0.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, -1.0f, 1.0f},
1740 {-1.0f, 1.0f, 1.0f}, {-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, -1.0f}, {1.0f, -1.0f, -1.0f},
1741 {-1.0f, 1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}};
1742
1743 /* Count ray mesh misses (i.e. no face hit) and cases where the ray direction matches the face
1744 * normal direction. From this information it can be derived whether a cell is inside or
1745 * outside the mesh. */
1746 int miss_count = 0, dir_count = 0;
1747
1748 for (int i = 0; i < ARRAY_SIZE(ray_dirs); i++) {
1749 BVHTreeRayHit hit_tree = {0};
1750 hit_tree.index = -1;
1751 hit_tree.dist = PHI_MAX;
1752
1753 normalize_v3(ray_dirs[i]);
1754 BLI_bvhtree_ray_cast(tree_data->tree,
1755 ray_start,
1756 ray_dirs[i],
1757 0.0f,
1758 &hit_tree,
1759 tree_data->raycast_callback,
1760 tree_data);
1761
1762 /* Ray did not hit mesh.
1763 * Current point definitely not inside mesh. Inside mesh as all rays have to hit. */
1764 if (hit_tree.index == -1) {
1765 miss_count++;
1766 /* Skip this ray since nothing was hit. */
1767 continue;
1768 }
1769
1770 /* Ray and normal are pointing in opposite directions. */
1771 if (dot_v3v3(ray_dirs[i], hit_tree.no) <= 0) {
1772 dir_count++;
1773 }
1774
1775 min_dist = std::min(hit_tree.dist, min_dist);
1776 }
1777
1778 /* Point lies inside mesh. Use negative sign for distance value.
1779 * This "if statement" has 2 conditions that can be true for points outside mesh. */
1780 if (!(miss_count > 0 || dir_count == ARRAY_SIZE(ray_dirs))) {
1781 min_dist = (-1.0f) * fabsf(min_dist);
1782 }
1783
1784 /* Subtract optional surface thickness value and virtually increase the object size. */
1785 if (surface_thickness) {
1786 min_dist -= surface_thickness;
1787 }
1788 }
1789
1790 /* Update global distance array but ensure that older entries are not overridden. */
1791 distance_map[index] = std::min(distance_map[index], min_dist);
1792
1793 /* Sanity check: Ensure that distances don't explode. */
1794 CLAMP(distance_map[index], -PHI_MAX, PHI_MAX);
1795}
1796
1797static void sample_mesh(FluidFlowSettings *ffs,
1798 blender::Span<blender::float3> vert_positions,
1799 const blender::Span<blender::float3> vert_normals,
1800 const int *corner_verts,
1801 const blender::int3 *corner_tris,
1803 float *influence_map,
1804 float *velocity_map,
1805 int index,
1806 const int base_res[3],
1807 const float global_size[3],
1808 const float flow_center[3],
1810 const float ray_start[3],
1811 const float *vert_vel,
1812 bool has_velocity,
1813 int defgrp_index,
1814 const MDeformVert *dvert,
1815 float x,
1816 float y,
1817 float z)
1818{
1819 float ray_dir[3] = {1.0f, 0.0f, 0.0f};
1820 BVHTreeRayHit hit = {0};
1821 BVHTreeNearest nearest = {0};
1822
1823 float volume_factor = 0.0f;
1824
1825 hit.index = -1;
1826 hit.dist = PHI_MAX;
1827 nearest.index = -1;
1828
1829 /* Distance between two opposing vertices in a unit cube.
1830 * I.e. the unit cube diagonal or `sqrt(3)`.
1831 * This value is our nearest neighbor search distance. */
1832 const float surface_distance = 1.732;
1833 /* find_nearest uses squared distance. */
1834 nearest.dist_sq = surface_distance * surface_distance;
1835
1836 bool is_gas_flow = ELEM(
1838
1839 /* Emission strength for gases will be computed below.
1840 * For liquids it's not needed. Just set to non zero value
1841 * to allow initial velocity computation. */
1842 float emission_strength = (is_gas_flow) ? 0.0f : 1.0f;
1843
1844 /* Emission inside the flow object. */
1845 if (is_gas_flow && ffs->volume_density) {
1846 if (BLI_bvhtree_ray_cast(tree_data->tree,
1847 ray_start,
1848 ray_dir,
1849 0.0f,
1850 &hit,
1851 tree_data->raycast_callback,
1852 tree_data) != -1)
1853 {
1854 float dot = ray_dir[0] * hit.no[0] + ray_dir[1] * hit.no[1] + ray_dir[2] * hit.no[2];
1855 /* If ray and hit face normal are facing same direction hit point is inside a closed mesh. */
1856 if (dot >= 0) {
1857 /* Also cast a ray in opposite direction to make sure point is at least surrounded by two
1858 * faces. */
1859 negate_v3(ray_dir);
1860 hit.index = -1;
1861 hit.dist = PHI_MAX;
1862
1863 BLI_bvhtree_ray_cast(tree_data->tree,
1864 ray_start,
1865 ray_dir,
1866 0.0f,
1867 &hit,
1868 tree_data->raycast_callback,
1869 tree_data);
1870 if (hit.index != -1) {
1871 volume_factor = ffs->volume_density;
1872 }
1873 }
1874 }
1875 }
1876
1877 /* Find the nearest point on the mesh. */
1879 tree_data->tree, ray_start, &nearest, tree_data->nearest_callback, tree_data) != -1)
1880 {
1881 float weights[3];
1882 int v1, v2, v3, tri_i = nearest.index;
1883 float hit_normal[3];
1884
1885 /* Calculate barycentric weights for nearest point. */
1886 v1 = corner_verts[corner_tris[tri_i][0]];
1887 v2 = corner_verts[corner_tris[tri_i][1]];
1888 v3 = corner_verts[corner_tris[tri_i][2]];
1890 weights, vert_positions[v1], vert_positions[v2], vert_positions[v3], nearest.co);
1891
1892 /* Compute emission strength for smoke flow. */
1893 if (is_gas_flow) {
1894 /* Emission from surface is based on UI configurable distance value. */
1895 if (ffs->surface_distance) {
1896 emission_strength = sqrtf(nearest.dist_sq) / ffs->surface_distance;
1897 CLAMP(emission_strength, 0.0f, 1.0f);
1898 emission_strength = pow(1.0f - emission_strength, 0.5f);
1899 }
1900 else {
1901 emission_strength = 0.0f;
1902 }
1903
1904 /* Apply vertex group influence if it is being used. */
1905 if (defgrp_index != -1 && dvert) {
1906 float weight_mask = BKE_defvert_find_weight(&dvert[v1], defgrp_index) * weights[0] +
1907 BKE_defvert_find_weight(&dvert[v2], defgrp_index) * weights[1] +
1908 BKE_defvert_find_weight(&dvert[v3], defgrp_index) * weights[2];
1909 emission_strength *= weight_mask;
1910 }
1911
1912 /* Apply emission texture. */
1913 if ((ffs->flags & FLUID_FLOW_TEXTUREEMIT) && ffs->noise_texture) {
1914 float tex_co[3] = {0};
1915 TexResult texres;
1916
1918 tex_co[0] = ((x - flow_center[0]) / base_res[0]) / ffs->texture_size;
1919 tex_co[1] = ((y - flow_center[1]) / base_res[1]) / ffs->texture_size;
1920 tex_co[2] = ((z - flow_center[2]) / base_res[2] - ffs->texture_offset) /
1921 ffs->texture_size;
1922 }
1923 else if (!uv_map.is_empty()) {
1924 const float *uv[3];
1925 uv[0] = uv_map[corner_tris[tri_i][0]];
1926 uv[1] = uv_map[corner_tris[tri_i][1]];
1927 uv[2] = uv_map[corner_tris[tri_i][2]];
1928
1929 interp_v2_v2v2v2(tex_co, UNPACK3(uv), weights);
1930
1931 /* Map texture coord between -1.0f and 1.0f. */
1932 tex_co[0] = tex_co[0] * 2.0f - 1.0f;
1933 tex_co[1] = tex_co[1] * 2.0f - 1.0f;
1934 tex_co[2] = ffs->texture_offset;
1935 }
1936 BKE_texture_get_value(ffs->noise_texture, tex_co, &texres, false);
1937 emission_strength *= texres.tin;
1938 }
1939 }
1940
1941 /* Initial velocity of flow object. Only compute velocity if emission is present. */
1942 if (ffs->flags & FLUID_FLOW_INITVELOCITY && velocity_map && emission_strength != 0.0) {
1943 /* Apply normal directional velocity. */
1944 if (ffs->vel_normal) {
1945 /* Interpolate vertex normal vectors to get nearest point normal. */
1947 hit_normal, vert_normals[v1], vert_normals[v2], vert_normals[v3], weights);
1948 normalize_v3(hit_normal);
1949
1950 /* Apply normal directional velocity. */
1951 velocity_map[index * 3] += hit_normal[0] * ffs->vel_normal;
1952 velocity_map[index * 3 + 1] += hit_normal[1] * ffs->vel_normal;
1953 velocity_map[index * 3 + 2] += hit_normal[2] * ffs->vel_normal;
1954 }
1955 /* Apply object velocity. */
1956 if (has_velocity && ffs->vel_multi) {
1957 float hit_vel[3];
1959 hit_vel, &vert_vel[v1 * 3], &vert_vel[v2 * 3], &vert_vel[v3 * 3], weights);
1960 velocity_map[index * 3] += hit_vel[0] * ffs->vel_multi;
1961 velocity_map[index * 3 + 1] += hit_vel[1] * ffs->vel_multi;
1962 velocity_map[index * 3 + 2] += hit_vel[2] * ffs->vel_multi;
1963# ifdef DEBUG_PRINT
1964 /* Debugging: Print flow object velocities. */
1965 printf("adding flow object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]);
1966# endif
1967 }
1968 /* Convert XYZ velocities flow settings from world to grid space. */
1969 float convert_vel[3];
1970 copy_v3_v3(convert_vel, ffs->vel_coord);
1971 float time_mult = 1.0 / (25.0f * DT_DEFAULT);
1972 float size_mult = std::max({base_res[0], base_res[1], base_res[2]}) /
1973 std::max({global_size[0], global_size[1], global_size[2]});
1974 mul_v3_v3fl(convert_vel, ffs->vel_coord, size_mult * time_mult);
1975
1976 velocity_map[index * 3] += convert_vel[0];
1977 velocity_map[index * 3 + 1] += convert_vel[1];
1978 velocity_map[index * 3 + 2] += convert_vel[2];
1979# ifdef DEBUG_PRINT
1980 printf("initial vel: [%f, %f, %f]\n",
1981 velocity_map[index * 3],
1982 velocity_map[index * 3 + 1],
1983 velocity_map[index * 3 + 2]);
1984# endif
1985 }
1986 }
1987
1988 /* Apply final influence value but also consider volume initialization factor. */
1989 influence_map[index] = std::max(volume_factor, emission_strength);
1990}
1991
1992struct EmitFromDMData {
1994 FluidFlowSettings *ffs;
1995
1996 blender::Span<blender::float3> vert_positions;
1997 blender::Span<blender::float3> vert_normals;
1998 blender::Span<int> corner_verts;
1999 blender::Span<blender::int3> corner_tris;
2000 blender::Span<blender::float2> uv_map;
2001 const MDeformVert *dvert;
2002 int defgrp_index;
2003
2004 blender::bke::BVHTreeFromMesh *tree;
2005 FluidObjectBB *bb;
2006
2007 bool has_velocity;
2008 float *vert_vel;
2009 float *flow_center;
2010 int *min, *max, *res;
2011};
2012
2013static void emit_from_mesh_task_cb(void *__restrict userdata,
2014 const int z,
2015 const TaskParallelTLS *__restrict /*tls*/)
2016{
2017 EmitFromDMData *data = static_cast<EmitFromDMData *>(userdata);
2018 FluidObjectBB *bb = data->bb;
2019
2020 for (int x = data->min[0]; x < data->max[0]; x++) {
2021 for (int y = data->min[1]; y < data->max[1]; y++) {
2022 const int index = manta_get_index(
2023 x - bb->min[0], bb->res[0], y - bb->min[1], bb->res[1], z - bb->min[2]);
2024 const float ray_start[3] = {float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f};
2025
2026 /* Compute emission only for flow objects that produce fluid (i.e. skip outflow objects).
2027 * Result in bb->influence. Also computes initial velocities. Result in bb->velocity. */
2029 sample_mesh(data->ffs,
2030 data->vert_positions,
2031 data->vert_normals,
2032 data->corner_verts.data(),
2033 data->corner_tris.data(),
2034 data->uv_map,
2035 bb->influence,
2036 bb->velocity,
2037 index,
2038 data->fds->base_res,
2039 data->fds->global_size,
2040 data->flow_center,
2041 data->tree,
2042 ray_start,
2043 data->vert_vel,
2044 data->has_velocity,
2045 data->defgrp_index,
2046 data->dvert,
2047 float(x),
2048 float(y),
2049 float(z));
2050 }
2051
2052 /* Calculate levelset values from meshes. Result in bb->distances. */
2053 update_distances(index,
2054 bb->distances,
2055 data->tree,
2056 ray_start,
2057 data->ffs->surface_distance,
2058 data->ffs->flags & FLUID_FLOW_USE_PLANE_INIT);
2059 }
2060 }
2061}
2062
2063static void emit_from_mesh(
2064 Object *flow_ob, FluidDomainSettings *fds, FluidFlowSettings *ffs, FluidObjectBB *bb, float dt)
2065{
2066 if (ffs->mesh) {
2067 int i;
2068
2069 float *vert_vel = nullptr;
2070 bool has_velocity = false;
2071
2072 int defgrp_index = ffs->vgroup_density - 1;
2073 float flow_center[3] = {0};
2074 int min[3], max[3], res[3];
2075
2076 /* Copy mesh for thread safety as we modify it.
2077 * Main issue is its VertArray being modified, then replaced and freed. */
2078 Mesh *mesh = BKE_mesh_copy_for_eval(*ffs->mesh);
2079 blender::MutableSpan<blender::float3> positions = mesh->vert_positions_for_write();
2080
2081 const blender::Span<int> corner_verts = mesh->corner_verts();
2082 const blender::Span<blender::int3> corner_tris = mesh->corner_tris();
2083 const int numverts = mesh->verts_num;
2084 const MDeformVert *dvert = mesh->deform_verts().data();
2085 const blender::bke::AttributeAccessor attributes = mesh->attributes();
2086 const blender::VArraySpan uv_map = *attributes.lookup<blender::float2>(
2088
2089 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
2090 vert_vel = MEM_calloc_arrayN<float>(3 * size_t(numverts), "manta_flow_velocity");
2091
2092 if (ffs->numverts != numverts || !ffs->verts_old) {
2093 if (ffs->verts_old) {
2094 MEM_freeN(ffs->verts_old);
2095 }
2096 ffs->verts_old = MEM_calloc_arrayN<float>(3 * size_t(numverts), "manta_flow_verts_old");
2097 ffs->numverts = numverts;
2098 }
2099 else {
2100 has_velocity = true;
2101 }
2102 }
2103
2104 /* Transform mesh vertices to domain grid space for fast lookups.
2105 * This is valid because the mesh is copied above. */
2106 for (i = 0; i < numverts; i++) {
2107 /* Vertex position. */
2108 mul_m4_v3(flow_ob->object_to_world().ptr(), positions[i]);
2109 manta_pos_to_cell(fds, positions[i]);
2110
2111 /* Vertex velocity. */
2112 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
2113 float co[3];
2114 add_v3fl_v3fl_v3i(co, positions[i], fds->shift);
2115 if (has_velocity) {
2116 sub_v3_v3v3(&vert_vel[i * 3], co, &ffs->verts_old[i * 3]);
2117 mul_v3_fl(&vert_vel[i * 3], 1.0 / dt);
2118 }
2119 copy_v3_v3(&ffs->verts_old[i * 3], co);
2120 }
2121
2122 /* Calculate emission map bounds. */
2123 bb_boundInsert(bb, positions[i]);
2124 }
2125 mesh->tag_positions_changed();
2126 mul_m4_v3(flow_ob->object_to_world().ptr(), flow_center);
2127 manta_pos_to_cell(fds, flow_center);
2128
2129 /* Set emission map.
2130 * Use 3 cell diagonals as margin (3 * 1.732 = 5.196). */
2131 int bounds_margin = int(ceil(5.196));
2132 clamp_bounds_in_domain(fds, bb->min, bb->max, nullptr, nullptr, bounds_margin, dt);
2133 bb_allocateData(bb, ffs->flags & FLUID_FLOW_INITVELOCITY, true);
2134
2135 /* Setup loop bounds. */
2136 for (i = 0; i < 3; i++) {
2137 min[i] = bb->min[i];
2138 max[i] = bb->max[i];
2139 res[i] = bb->res[i];
2140 }
2141
2142 /* Skip flow sampling loop if object has disabled flow. */
2143 bool use_flow = ffs->flags & FLUID_FLOW_USE_INFLOW;
2144 blender::bke::BVHTreeFromMesh tree_data = mesh->bvh_corner_tris();
2145 if (use_flow && tree_data.tree != nullptr) {
2146
2147 EmitFromDMData data{};
2148 data.fds = fds;
2149 data.ffs = ffs;
2150 data.vert_positions = positions;
2151 data.vert_normals = mesh->vert_normals();
2152 data.corner_verts = corner_verts;
2153 data.corner_tris = corner_tris;
2154 data.uv_map = uv_map;
2155 data.dvert = dvert;
2156 data.defgrp_index = defgrp_index;
2157 data.tree = &tree_data;
2158 data.bb = bb;
2159 data.has_velocity = has_velocity;
2160 data.vert_vel = vert_vel;
2161 data.flow_center = flow_center;
2162 data.min = min;
2163 data.max = max;
2164 data.res = res;
2165
2166 TaskParallelSettings settings;
2168 settings.min_iter_per_thread = 2;
2169 BLI_task_parallel_range(min[2], max[2], &data, emit_from_mesh_task_cb, &settings);
2170 }
2171
2172 if (vert_vel) {
2173 MEM_freeN(vert_vel);
2174 }
2175 BKE_id_free(nullptr, mesh);
2176 }
2177}
2178
2180
2181/* -------------------------------------------------------------------- */
2184
2185static void adaptive_domain_adjust(
2186 FluidDomainSettings *fds, Object *ob, FluidObjectBB *bb_maps, uint numflowobj, float dt)
2187{
2188 /* calculate domain shift for current frame */
2189 int new_shift[3] = {0};
2190 int total_shift[3];
2191 float frame_shift_f[3];
2192 float ob_loc[3] = {0};
2193
2194 mul_m4_v3(ob->object_to_world().ptr(), ob_loc);
2195
2196 sub_v3_v3v3(frame_shift_f, ob_loc, fds->prev_loc);
2197 copy_v3_v3(fds->prev_loc, ob_loc);
2198 /* convert global space shift to local "cell" space */
2199 mul_mat3_m4_v3(fds->imat, frame_shift_f);
2200 frame_shift_f[0] = frame_shift_f[0] / fds->cell_size[0];
2201 frame_shift_f[1] = frame_shift_f[1] / fds->cell_size[1];
2202 frame_shift_f[2] = frame_shift_f[2] / fds->cell_size[2];
2203 /* add to total shift */
2204 add_v3_v3(fds->shift_f, frame_shift_f);
2205 /* convert to integer */
2206 total_shift[0] = int(floorf(fds->shift_f[0]));
2207 total_shift[1] = int(floorf(fds->shift_f[1]));
2208 total_shift[2] = int(floorf(fds->shift_f[2]));
2209 int temp_shift[3];
2210 copy_v3_v3_int(temp_shift, fds->shift);
2211 sub_v3_v3v3_int(new_shift, total_shift, fds->shift);
2212 copy_v3_v3_int(fds->shift, total_shift);
2213
2214 /* calculate new domain boundary points so that smoke doesn't slide on sub-cell movement */
2215 fds->p0[0] = fds->dp0[0] - fds->cell_size[0] * (fds->shift_f[0] - total_shift[0] - 0.5f);
2216 fds->p0[1] = fds->dp0[1] - fds->cell_size[1] * (fds->shift_f[1] - total_shift[1] - 0.5f);
2217 fds->p0[2] = fds->dp0[2] - fds->cell_size[2] * (fds->shift_f[2] - total_shift[2] - 0.5f);
2218 fds->p1[0] = fds->p0[0] + fds->cell_size[0] * fds->base_res[0];
2219 fds->p1[1] = fds->p0[1] + fds->cell_size[1] * fds->base_res[1];
2220 fds->p1[2] = fds->p0[2] + fds->cell_size[2] * fds->base_res[2];
2221
2222 /* adjust domain resolution */
2223 const int block_size = fds->noise_scale;
2224 int min[3] = {32767, 32767, 32767}, max[3] = {-32767, -32767, -32767}, res[3];
2225 int total_cells = 1, res_changed = 0, shift_changed = 0;
2226 float min_vel[3], max_vel[3];
2227 int x, y, z;
2228 float *density = manta_smoke_get_density(fds->fluid);
2229 float *fuel = manta_smoke_get_fuel(fds->fluid);
2230 float *heat = manta_smoke_get_heat(fds->fluid);
2231 float *bigdensity = manta_noise_get_density(fds->fluid);
2232 float *bigfuel = manta_noise_get_fuel(fds->fluid);
2233 float *vx = manta_get_velocity_x(fds->fluid);
2234 float *vy = manta_get_velocity_y(fds->fluid);
2235 float *vz = manta_get_velocity_z(fds->fluid);
2236 int wt_res[3];
2237
2238 if (fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
2239 manta_noise_get_res(fds->fluid, wt_res);
2240 }
2241
2242 INIT_MINMAX(min_vel, max_vel);
2243
2244 /* Calculate bounds for current domain content */
2245 for (x = fds->res_min[0]; x < fds->res_max[0]; x++) {
2246 for (y = fds->res_min[1]; y < fds->res_max[1]; y++) {
2247 for (z = fds->res_min[2]; z < fds->res_max[2]; z++) {
2248 int xn = x - new_shift[0];
2249 int yn = y - new_shift[1];
2250 int zn = z - new_shift[2];
2251 int index;
2252 float max_den;
2253
2254 /* skip if cell already belongs to new area */
2255 if (xn >= min[0] && xn <= max[0] && yn >= min[1] && yn <= max[1] && zn >= min[2] &&
2256 zn <= max[2])
2257 {
2258 continue;
2259 }
2260
2261 index = manta_get_index(x - fds->res_min[0],
2262 fds->res[0],
2263 y - fds->res_min[1],
2264 fds->res[1],
2265 z - fds->res_min[2]);
2266 max_den = (fuel) ? std::max(density[index], fuel[index]) : density[index];
2267 max_den = (heat) ? std::max(max_den, heat[index]) : max_den;
2268
2269 /* Check high resolution bounds if max density isn't already high enough. */
2270 if (max_den < fds->adapt_threshold && fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
2271 int i, j, k;
2272 /* high res grid index */
2273 int xx = (x - fds->res_min[0]) * block_size;
2274 int yy = (y - fds->res_min[1]) * block_size;
2275 int zz = (z - fds->res_min[2]) * block_size;
2276
2277 for (i = 0; i < block_size; i++) {
2278 for (j = 0; j < block_size; j++) {
2279 for (k = 0; k < block_size; k++) {
2280 int big_index = manta_get_index(xx + i, wt_res[0], yy + j, wt_res[1], zz + k);
2281 float den = (bigfuel) ? std::max(bigdensity[big_index], bigfuel[big_index]) :
2282 bigdensity[big_index];
2283 max_den = std::max(den, max_den);
2284 }
2285 }
2286 }
2287 }
2288
2289 /* content bounds (use shifted coordinates) */
2290 if (max_den >= fds->adapt_threshold) {
2291 min[0] = std::min(min[0], xn);
2292 min[1] = std::min(min[1], yn);
2293 min[2] = std::min(min[2], zn);
2294 max[0] = std::max(max[0], xn);
2295 max[1] = std::max(max[1], yn);
2296 max[2] = std::max(max[2], zn);
2297 }
2298
2299 /* velocity bounds */
2300 min_vel[0] = std::min(min_vel[0], vx[index]);
2301 min_vel[1] = std::min(min_vel[1], vy[index]);
2302 min_vel[2] = std::min(min_vel[2], vz[index]);
2303 max_vel[0] = std::max(max_vel[0], vx[index]);
2304 max_vel[1] = std::max(max_vel[1], vy[index]);
2305 max_vel[2] = std::max(max_vel[2], vz[index]);
2306 }
2307 }
2308 }
2309
2310 /* also apply emission maps */
2311 for (int i = 0; i < numflowobj; i++) {
2312 FluidObjectBB *bb = &bb_maps[i];
2313
2314 for (x = bb->min[0]; x < bb->max[0]; x++) {
2315 for (y = bb->min[1]; y < bb->max[1]; y++) {
2316 for (z = bb->min[2]; z < bb->max[2]; z++) {
2317 int index = manta_get_index(
2318 x - bb->min[0], bb->res[0], y - bb->min[1], bb->res[1], z - bb->min[2]);
2319 float max_den = bb->influence[index];
2320
2321 /* density bounds */
2322 if (max_den >= fds->adapt_threshold) {
2323 min[0] = std::min(min[0], x);
2324 min[1] = std::min(min[1], y);
2325 min[2] = std::min(min[2], z);
2326 max[0] = std::max(max[0], x);
2327 max[1] = std::max(max[1], y);
2328 max[2] = std::max(max[2], z);
2329 }
2330 }
2331 }
2332 }
2333 }
2334
2335 /* calculate new bounds based on these values */
2336 clamp_bounds_in_domain(fds, min, max, min_vel, max_vel, fds->adapt_margin + 1, dt);
2337
2338 for (int i = 0; i < 3; i++) {
2339 /* calculate new resolution */
2340 res[i] = max[i] - min[i];
2341 total_cells *= res[i];
2342
2343 if (new_shift[i]) {
2344 shift_changed = 1;
2345 }
2346
2347 /* if no content set minimum dimensions */
2348 if (res[i] <= 0) {
2349 int j;
2350 for (j = 0; j < 3; j++) {
2351 min[j] = 0;
2352 max[j] = 1;
2353 res[j] = 1;
2354 }
2355 res_changed = 1;
2356 total_cells = 1;
2357 break;
2358 }
2359 if (min[i] != fds->res_min[i] || max[i] != fds->res_max[i]) {
2360 res_changed = 1;
2361 }
2362 }
2363
2364 if (res_changed || shift_changed) {
2366 fds, fds->res, res, fds->res_min, min, fds->res_max, temp_shift, total_shift);
2367
2368 /* set new domain dimensions */
2369 copy_v3_v3_int(fds->res_min, min);
2370 copy_v3_v3_int(fds->res_max, max);
2371 copy_v3_v3_int(fds->res, res);
2372 fds->total_cells = total_cells;
2373
2374 /* Redo adapt time step in manta to refresh solver state (ie time variables) */
2376 }
2377}
2378
2379BLI_INLINE void apply_outflow_fields(int index,
2380 float distance_value,
2381 float *density,
2382 float *heat,
2383 float *fuel,
2384 float *react,
2385 float *color_r,
2386 float *color_g,
2387 float *color_b,
2388 float *phiout)
2389{
2390 /* Set levelset value for liquid inflow.
2391 * Ensure that distance value is "joined" into the levelset. */
2392 if (phiout) {
2393 phiout[index] = std::min(distance_value, phiout[index]);
2394 }
2395
2396 /* Set smoke outflow, i.e. reset cell to zero. */
2397 if (density) {
2398 density[index] = 0.0f;
2399 }
2400 if (heat) {
2401 heat[index] = 0.0f;
2402 }
2403 if (fuel) {
2404 fuel[index] = 0.0f;
2405 react[index] = 0.0f;
2406 }
2407 if (color_r) {
2408 color_r[index] = 0.0f;
2409 color_g[index] = 0.0f;
2410 color_b[index] = 0.0f;
2411 }
2412}
2413
2414BLI_INLINE void apply_inflow_fields(FluidFlowSettings *ffs,
2415 float emission_value,
2416 float distance_value,
2417 int index,
2418 float *density_in,
2419 const float *density,
2420 float *heat_in,
2421 const float *heat,
2422 float *fuel_in,
2423 const float *fuel,
2424 float *react_in,
2425 const float *react,
2426 float *color_r_in,
2427 const float *color_r,
2428 float *color_g_in,
2429 const float *color_g,
2430 float *color_b_in,
2431 const float *color_b,
2432 float *phi_in,
2433 float *emission_in)
2434{
2435 /* Set levelset value for liquid inflow.
2436 * Ensure that distance value is "joined" into the levelset. */
2437 if (phi_in) {
2438 phi_in[index] = std::min(distance_value, phi_in[index]);
2439 }
2440
2441 /* Set emission value for smoke inflow.
2442 * Ensure that emission value is "maximized". */
2443 if (emission_in) {
2444 emission_in[index] = std::max(emission_value, emission_in[index]);
2445 }
2446
2447 /* Set inflow for smoke from here on. */
2448 int absolute_flow = (ffs->flags & FLUID_FLOW_ABSOLUTE);
2449 float dens_old = (density) ? density[index] : 0.0;
2450 // float fuel_old = (fuel) ? fuel[index] : 0.0f; /* UNUSED */
2451 float dens_flow = (ffs->type == FLUID_FLOW_TYPE_FIRE) ? 0.0f : emission_value * ffs->density;
2452 float fuel_flow = (fuel) ? emission_value * ffs->fuel_amount : 0.0f;
2453 /* Set heat inflow. */
2454 if (heat && heat_in) {
2455 if (emission_value > 0.0f) {
2456 heat_in[index] = ADD_IF_LOWER(heat[index], ffs->temperature);
2457 }
2458 }
2459
2460 /* Set density and fuel - absolute mode. */
2461 if (absolute_flow) {
2462 if (density && density_in) {
2463 if (ffs->type != FLUID_FLOW_TYPE_FIRE && dens_flow > density[index]) {
2464 /* Use std::max to preserve values from other emitters at this cell. */
2465 density_in[index] = std::max(dens_flow, density_in[index]);
2466 }
2467 }
2468 if (fuel && fuel_in) {
2469 if (ffs->type != FLUID_FLOW_TYPE_SMOKE && fuel_flow && fuel_flow > fuel[index]) {
2470 /* Use std::max to preserve values from other emitters at this cell. */
2471 fuel_in[index] = std::max(fuel_flow, fuel_in[index]);
2472 }
2473 }
2474 }
2475 /* Set density and fuel - additive mode. */
2476 else {
2477 if (density && density_in) {
2478 if (ffs->type != FLUID_FLOW_TYPE_FIRE) {
2479 density_in[index] += dens_flow;
2480 CLAMP(density_in[index], 0.0f, 1.0f);
2481 }
2482 }
2483 if (fuel && fuel_in) {
2484 if (ffs->type != FLUID_FLOW_TYPE_SMOKE && ffs->fuel_amount) {
2485 fuel_in[index] += fuel_flow;
2486 CLAMP(fuel_in[index], 0.0f, 10.0f);
2487 }
2488 }
2489 }
2490
2491 /* Set color. */
2492 if (color_r && color_r_in) {
2493 if (dens_flow) {
2494 float total_dens = density[index] / (dens_old + dens_flow);
2495 color_r_in[index] = (color_r[index] + ffs->color[0] * dens_flow) * total_dens;
2496 color_g_in[index] = (color_g[index] + ffs->color[1] * dens_flow) * total_dens;
2497 color_b_in[index] = (color_b[index] + ffs->color[2] * dens_flow) * total_dens;
2498 }
2499 }
2500
2501 /* Set fire reaction coordinate. */
2502 if (fuel && fuel_in) {
2503 /* Instead of using 1.0 for all new fuel add slight falloff to reduce flow blocky-ness. */
2504 float value = 1.0f - pow2f(1.0f - emission_value);
2505
2506 if (fuel_in[index] > FLT_EPSILON && value > react[index]) {
2507 float f = fuel_flow / fuel_in[index];
2508 react_in[index] = value * f + (1.0f - f) * react[index];
2509 CLAMP(react_in[index], 0.0f, value);
2510 }
2511 }
2512}
2513
2514static void ensure_flowsfields(FluidDomainSettings *fds)
2515{
2517 manta_ensure_invelocity(fds->fluid, fds->fmd);
2518 }
2520 manta_ensure_outflow(fds->fluid, fds->fmd);
2521 }
2523 manta_smoke_ensure_heat(fds->fluid, fds->fmd);
2524 }
2526 manta_smoke_ensure_fire(fds->fluid, fds->fmd);
2527 }
2529 /* Initialize all smoke with "active_color". */
2531 }
2535 {
2537 }
2538 manta_update_pointers(fds->fluid, fds->fmd, false);
2539}
2540
2541static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int numflowobj)
2542{
2543 int active_fields = fds->active_fields;
2544 uint flow_index;
2545
2546 /* First, remove all flags that we want to update. */
2549 active_fields &= ~prev_flags;
2550
2551 /* Monitor active fields based on flow settings. */
2552 for (flow_index = 0; flow_index < numflowobj; flow_index++) {
2553 Object *flow_ob = flowobjs[flow_index];
2556
2557 /* Sanity check. */
2558 if (!fmd2) {
2559 continue;
2560 }
2561
2562 /* Activate specific grids if at least one flow object requires this grid. */
2563 if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
2564 FluidFlowSettings *ffs = fmd2->flow;
2565 if (!ffs) {
2566 break;
2567 }
2568 if (ffs->flags & FLUID_FLOW_NEEDS_UPDATE) {
2571 }
2572 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
2573 active_fields |= FLUID_DOMAIN_ACTIVE_INVEL;
2574 }
2576 active_fields |= FLUID_DOMAIN_ACTIVE_OUTFLOW;
2577 }
2578 /* liquids done from here */
2579 if (fds->type == FLUID_DOMAIN_TYPE_LIQUID) {
2580 continue;
2581 }
2582
2583 /* Activate heat field if a flow object produces any heat. */
2584 if (ffs->temperature != 0.0) {
2585 active_fields |= FLUID_DOMAIN_ACTIVE_HEAT;
2586 }
2587 /* Activate fuel field if a flow object is of fire type. */
2588 if (ffs->fuel_amount != 0.0 || ffs->type == FLUID_FLOW_TYPE_FIRE ||
2590 {
2591 active_fields |= FLUID_DOMAIN_ACTIVE_FIRE;
2592 }
2593 /* Activate color field if flows add smoke with varying colors. */
2595 {
2596 if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) {
2597 copy_v3_v3(fds->active_color, ffs->color);
2598 active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET;
2599 }
2600 else if (!equals_v3v3(fds->active_color, ffs->color)) {
2601 copy_v3_v3(fds->active_color, ffs->color);
2602 active_fields |= FLUID_DOMAIN_ACTIVE_COLORS;
2603 }
2604 }
2605 }
2606 }
2607 /* Monitor active fields based on domain settings. */
2608 if (fds->type == FLUID_DOMAIN_TYPE_GAS && active_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
2609 /* Heat is always needed for fire. */
2610 active_fields |= FLUID_DOMAIN_ACTIVE_HEAT;
2611 /* Also activate colors if domain smoke color differs from active color. */
2612 if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) {
2614 active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET;
2615 }
2616 else if (!equals_v3v3(fds->active_color, fds->flame_smoke_color)) {
2618 active_fields |= FLUID_DOMAIN_ACTIVE_COLORS;
2619 }
2620 }
2621 fds->active_fields = active_fields;
2622}
2623
2624static bool escape_flowsobject(Object *flowobj,
2626 FluidFlowSettings *ffs,
2627 int frame)
2628{
2629 bool use_velocity = (ffs->flags & FLUID_FLOW_INITVELOCITY);
2630 bool is_static = is_static_object(flowobj);
2631
2632 bool liquid_flow = ffs->type == FLUID_FLOW_TYPE_LIQUID;
2633 bool gas_flow = ELEM(
2635 bool is_geometry = (ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
2636
2637 bool liquid_domain = fds->type == FLUID_DOMAIN_TYPE_LIQUID;
2638 bool gas_domain = fds->type == FLUID_DOMAIN_TYPE_GAS;
2640 bool is_resume = (fds->cache_frame_pause_data == frame);
2641 bool is_first_frame = (fds->cache_frame_start == frame);
2642
2643 /* Cannot use static mode with adaptive domain.
2644 * The adaptive domain might expand and only later discover the static object. */
2645 if (is_adaptive) {
2646 is_static = false;
2647 }
2648 /* No need to compute emission value if it won't be applied. */
2649 if (liquid_flow && is_geometry && !is_first_frame) {
2650 return true;
2651 }
2652 /* Skip flow object if it does not "belong" to this domain type. */
2653 if ((liquid_flow && gas_domain) || (gas_flow && liquid_domain)) {
2654 return true;
2655 }
2656 /* Optimization: Static liquid flow objects don't need emission after first frame.
2657 * TODO(sebbas): Also do not use static mode if initial velocities are enabled. */
2658 if (liquid_flow && is_static && !is_first_frame && !is_resume && !use_velocity) {
2659 return true;
2660 }
2661 return false;
2662}
2663
2664static void compute_flowsemission(Scene *scene,
2665 FluidObjectBB *bb_maps,
2666 Depsgraph *depsgraph,
2667 float dt,
2668 Object **flowobjs,
2669 int frame,
2670 float frame_length,
2672 uint numflowobjs,
2673 float time_per_frame)
2674{
2675 bool is_first_frame = (frame == fds->cache_frame_start);
2676
2677 /* Prepare flow emission maps. */
2678 for (int flow_index = 0; flow_index < numflowobjs; flow_index++) {
2679 Object *flowobj = flowobjs[flow_index];
2682
2683 /* Sanity check. */
2684 if (!fmd2) {
2685 continue;
2686 }
2687
2688 /* Check for initialized flow object. */
2689 if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
2690 FluidFlowSettings *ffs = fmd2->flow;
2691 int subframes = ffs->subframes;
2692 FluidObjectBB *bb = &bb_maps[flow_index];
2693
2694 /* Optimization: Skip this object under certain conditions. */
2695 if (escape_flowsobject(flowobj, fds, ffs, frame)) {
2696 continue;
2697 }
2698
2699 /* First frame cannot have any subframes because there is (obviously) no previous frame from
2700 * where subframes could come from. */
2701 if (is_first_frame) {
2702 subframes = 0;
2703 }
2704
2705 /* More splitting because of emission subframe: If no subframes present, sample_size is 1. */
2706 float sample_size = 1.0f / float(subframes + 1);
2707 float subframe_dt = dt * sample_size;
2708
2709 /* Emission loop. When not using subframes this will loop only once. */
2710 for (int subframe = 0; subframe <= subframes; subframe++) {
2711 /* Temporary emission map used when subframes are enabled, i.e. at least one subframe. */
2712 FluidObjectBB bb_temp = {nullptr};
2713
2714 /* Set scene time */
2715 if ((subframe < subframes || time_per_frame + dt + FLT_EPSILON < frame_length) &&
2716 !is_first_frame)
2717 {
2718 scene->r.subframe = (time_per_frame + (subframe + 1.0f) * subframe_dt) / frame_length;
2719 scene->r.cfra = frame - 1;
2720 }
2721 else {
2722 scene->r.subframe = 0.0f;
2723 scene->r.cfra = frame;
2724 }
2725
2726 /* Sanity check: subframe portion must be between 0 and 1. */
2727 CLAMP(scene->r.subframe, 0.0f, 1.0f);
2728# ifdef DEBUG_PRINT
2729 /* Debugging: Print subframe information. */
2730 printf(
2731 "flow: frame (is first: %d): %d // scene current frame: %d // scene current subframe: "
2732 "%f\n",
2733 is_first_frame,
2734 frame,
2735 scene->r.cfra,
2736 scene->r.subframe);
2737# endif
2738 /* Update frame time, this is considering current subframe fraction
2739 * BLI_mutex_lock() called in manta_step(), so safe to update subframe here
2740 * TODO(sebbas): Using BKE_scene_ctime_get(scene) instead of new DEG_get_ctime(depsgraph)
2741 * as subframes don't work with the latter yet. */
2743 scene,
2744 flowobj,
2745 true,
2747 BKE_scene_ctime_get(scene),
2749
2750 /* Emission from particles. */
2751 if (ffs->source == FLUID_FLOW_SOURCE_PARTICLES) {
2752 if (subframes) {
2753 emit_from_particles(flowobj, fds, ffs, &bb_temp, depsgraph, scene, subframe_dt);
2754 }
2755 else {
2756 emit_from_particles(flowobj, fds, ffs, bb, depsgraph, scene, subframe_dt);
2757 }
2758 }
2759 /* Emission from mesh. */
2760 else if (ffs->source == FLUID_FLOW_SOURCE_MESH) {
2761 if (subframes) {
2762 emit_from_mesh(flowobj, fds, ffs, &bb_temp, subframe_dt);
2763 }
2764 else {
2765 emit_from_mesh(flowobj, fds, ffs, bb, subframe_dt);
2766 }
2767 }
2768 else {
2769 printf("Error: unknown flow emission source\n");
2770 }
2771
2772 /* If this we emitted with temp emission map in this loop (subframe emission), we combine
2773 * the temp map with the original emission map. */
2774 if (subframes) {
2775 /* Combine emission maps. */
2776 bb_combineMaps(bb, &bb_temp, !(ffs->flags & FLUID_FLOW_ABSOLUTE), sample_size);
2777 bb_freeData(&bb_temp);
2778 }
2779 }
2780 }
2781 }
2782# ifdef DEBUG_PRINT
2783 /* Debugging: Print time information. */
2784 printf("flow: frame: %d // time per frame: %f // frame length: %f // dt: %f\n",
2785 frame,
2786 time_per_frame,
2787 frame_length,
2788 dt);
2789# endif
2790}
2791
2792static void update_flowsfluids(Depsgraph *depsgraph,
2793 Scene *scene,
2794 Object *ob,
2796 float time_per_frame,
2797 float frame_length,
2798 int frame,
2799 float dt)
2800{
2801 FluidObjectBB *bb_maps = nullptr;
2802 Object **flowobjs = nullptr;
2803 uint numflowobjs = 0;
2804 bool is_resume = (fds->cache_frame_pause_data == frame);
2805 bool is_first_frame = (fds->cache_frame_start == frame);
2806
2808 depsgraph, ob, fds->fluid_group, &numflowobjs, eModifierType_Fluid);
2809
2810 /* Update all flow related flags and ensure that corresponding grids get initialized. */
2811 update_flowsflags(fds, flowobjs, numflowobjs);
2812 ensure_flowsfields(fds);
2813
2814 /* Allocate emission map for each flow object. */
2815 bb_maps = MEM_calloc_arrayN<FluidObjectBB>(numflowobjs, "fluid_flow_bb_maps");
2816
2817 /* Initialize emission map for each flow object. */
2818 compute_flowsemission(scene,
2819 bb_maps,
2820 depsgraph,
2821 dt,
2822 flowobjs,
2823 frame,
2824 frame_length,
2825 fds,
2826 numflowobjs,
2827 time_per_frame);
2828
2829 /* Adjust domain size if needed. Only do this once for every frame. */
2831 adaptive_domain_adjust(fds, ob, bb_maps, numflowobjs, dt);
2832 }
2833
2834 float *phi_in = manta_get_phi_in(fds->fluid);
2835 float *phistatic_in = manta_get_phistatic_in(fds->fluid);
2836 float *phiout_in = manta_get_phiout_in(fds->fluid);
2837 float *phioutstatic_in = manta_get_phioutstatic_in(fds->fluid);
2838
2839 float *density = manta_smoke_get_density(fds->fluid);
2840 float *color_r = manta_smoke_get_color_r(fds->fluid);
2841 float *color_g = manta_smoke_get_color_g(fds->fluid);
2842 float *color_b = manta_smoke_get_color_b(fds->fluid);
2843 float *fuel = manta_smoke_get_fuel(fds->fluid);
2844 float *heat = manta_smoke_get_heat(fds->fluid);
2845 float *react = manta_smoke_get_react(fds->fluid);
2846
2847 float *density_in = manta_smoke_get_density_in(fds->fluid);
2848 float *heat_in = manta_smoke_get_heat_in(fds->fluid);
2849 float *color_r_in = manta_smoke_get_color_r_in(fds->fluid);
2850 float *color_g_in = manta_smoke_get_color_g_in(fds->fluid);
2851 float *color_b_in = manta_smoke_get_color_b_in(fds->fluid);
2852 float *fuel_in = manta_smoke_get_fuel_in(fds->fluid);
2853 float *react_in = manta_smoke_get_react_in(fds->fluid);
2854 float *emission_in = manta_smoke_get_emission_in(fds->fluid);
2855
2856 float *velx_initial = manta_get_in_velocity_x(fds->fluid);
2857 float *vely_initial = manta_get_in_velocity_y(fds->fluid);
2858 float *velz_initial = manta_get_in_velocity_z(fds->fluid);
2859
2860 float *forcex = manta_get_force_x(fds->fluid);
2861 float *forcey = manta_get_force_y(fds->fluid);
2862 float *forcez = manta_get_force_z(fds->fluid);
2863
2864 BLI_assert(forcex && forcey && forcez);
2865
2866 /* Either all or no components have to exist. */
2867 BLI_assert((color_r && color_g && color_b) || (!color_r && !color_g && !color_b));
2868 BLI_assert((color_r_in && color_g_in && color_b_in) ||
2869 (!color_r_in && !color_g_in && !color_b_in));
2870 BLI_assert((velx_initial && vely_initial && velz_initial) ||
2871 (!velx_initial && !vely_initial && !velz_initial));
2872
2873 uint z;
2874 /* Grid reset before writing again. */
2875 for (z = 0; z < fds->res[0] * fds->res[1] * fds->res[2]; z++) {
2876 /* Only reset static phi on first frame, dynamic phi gets reset every time. */
2877 if (phistatic_in && is_first_frame) {
2878 phistatic_in[z] = PHI_MAX;
2879 }
2880 if (phi_in) {
2881 phi_in[z] = PHI_MAX;
2882 }
2883 /* Only reset static phi on first frame, dynamic phi gets reset every time. */
2884 if (phioutstatic_in && is_first_frame) {
2885 phioutstatic_in[z] = PHI_MAX;
2886 }
2887 if (phiout_in) {
2888 phiout_in[z] = PHI_MAX;
2889 }
2890 /* Sync smoke inflow grids with their counterparts (simulation grids). */
2891 if (density_in) {
2892 density_in[z] = density[z];
2893 }
2894 if (heat_in) {
2895 heat_in[z] = heat[z];
2896 }
2897 if (color_r_in && color_g_in && color_b_in) {
2898 color_r_in[z] = color_r[z];
2899 color_g_in[z] = color_b[z];
2900 color_b_in[z] = color_g[z];
2901 }
2902 if (fuel_in) {
2903 fuel_in[z] = fuel[z];
2904 react_in[z] = react[z];
2905 }
2906 if (emission_in) {
2907 emission_in[z] = 0.0f;
2908 }
2909 if (velx_initial && vely_initial && velz_initial) {
2910 velx_initial[z] = 0.0f;
2911 vely_initial[z] = 0.0f;
2912 velz_initial[z] = 0.0f;
2913 }
2914 /* Reset forces here as update_effectors() is skipped when no external forces are present. */
2915 forcex[z] = 0.0f;
2916 forcey[z] = 0.0f;
2917 forcez[z] = 0.0f;
2918 }
2919
2920 /* Apply emission data for every flow object. */
2921 for (int flow_index = 0; flow_index < numflowobjs; flow_index++) {
2922 Object *flowobj = flowobjs[flow_index];
2925
2926 /* Sanity check. */
2927 if (!fmd2) {
2928 continue;
2929 }
2930
2931 /* Check for initialized flow object. */
2932 if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
2933 FluidFlowSettings *ffs = fmd2->flow;
2934
2935 bool is_inflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW);
2936 bool is_geometry = (ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
2937 bool is_outflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW);
2938 bool is_static = is_static_object(flowobj) &&
2940
2941 FluidObjectBB *bb = &bb_maps[flow_index];
2942 float *velocity_map = bb->velocity;
2943 float *emission_map = bb->influence;
2944 float *distance_map = bb->distances;
2945
2946 int gx, gy, gz, ex, ey, ez, dx, dy, dz;
2947 size_t e_index, d_index;
2948
2949 /* Loop through every emission map cell. */
2950 for (gx = bb->min[0]; gx < bb->max[0]; gx++) {
2951 for (gy = bb->min[1]; gy < bb->max[1]; gy++) {
2952 for (gz = bb->min[2]; gz < bb->max[2]; gz++) {
2953 /* Compute emission map index. */
2954 ex = gx - bb->min[0];
2955 ey = gy - bb->min[1];
2956 ez = gz - bb->min[2];
2957 e_index = manta_get_index(ex, bb->res[0], ey, bb->res[1], ez);
2958
2959 /* Get domain index. */
2960 dx = gx - fds->res_min[0];
2961 dy = gy - fds->res_min[1];
2962 dz = gz - fds->res_min[2];
2963 d_index = manta_get_index(dx, fds->res[0], dy, fds->res[1], dz);
2964 /* Make sure emission cell is inside the new domain boundary. */
2965 if (dx < 0 || dy < 0 || dz < 0 || dx >= fds->res[0] || dy >= fds->res[1] ||
2966 dz >= fds->res[2])
2967 {
2968 continue;
2969 }
2970
2971 /* Delete fluid in outflow regions. */
2972 if (is_outflow) {
2973 float *levelset = ((is_first_frame || is_resume) && is_static) ? phioutstatic_in :
2974 phiout_in;
2975 apply_outflow_fields(d_index,
2976 distance_map[e_index],
2977 density_in,
2978 heat_in,
2979 fuel_in,
2980 react_in,
2981 color_r_in,
2982 color_g_in,
2983 color_b_in,
2984 levelset);
2985 }
2986 /* Do not apply inflow after the first frame when in geometry mode. */
2987 else if (is_geometry && !is_first_frame) {
2988 apply_inflow_fields(ffs,
2989 0.0f,
2990 PHI_MAX,
2991 d_index,
2992 density_in,
2993 density,
2994 heat_in,
2995 heat,
2996 fuel_in,
2997 fuel,
2998 react_in,
2999 react,
3000 color_r_in,
3001 color_r,
3002 color_g_in,
3003 color_g,
3004 color_b_in,
3005 color_b,
3006 phi_in,
3007 emission_in);
3008 }
3009 /* Main inflow application. */
3010 else if (is_geometry || is_inflow) {
3011 float *levelset = ((is_first_frame || is_resume) && is_static && !is_geometry) ?
3012 phistatic_in :
3013 phi_in;
3014 apply_inflow_fields(ffs,
3015 emission_map[e_index],
3016 distance_map[e_index],
3017 d_index,
3018 density_in,
3019 density,
3020 heat_in,
3021 heat,
3022 fuel_in,
3023 fuel,
3024 react_in,
3025 react,
3026 color_r_in,
3027 color_r,
3028 color_g_in,
3029 color_g,
3030 color_b_in,
3031 color_b,
3032 levelset,
3033 emission_in);
3034 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
3035 /* Use the initial velocity from the inflow object with the highest velocity for
3036 * now. */
3037 float vel_initial[3];
3038 vel_initial[0] = velx_initial[d_index];
3039 vel_initial[1] = vely_initial[d_index];
3040 vel_initial[2] = velz_initial[d_index];
3041 float vel_initial_strength = len_squared_v3(vel_initial);
3042 float vel_map_strength = len_squared_v3(velocity_map + 3 * e_index);
3043 if (vel_map_strength > vel_initial_strength) {
3044 velx_initial[d_index] = velocity_map[e_index * 3];
3045 vely_initial[d_index] = velocity_map[e_index * 3 + 1];
3046 velz_initial[d_index] = velocity_map[e_index * 3 + 2];
3047 }
3048 }
3049 }
3050 }
3051 }
3052 } /* End of flow emission map loop. */
3053 bb_freeData(bb);
3054 } /* End of flow object loop. */
3055 }
3056
3058 if (bb_maps) {
3059 MEM_freeN(bb_maps);
3060 }
3061}
3062
3063struct UpdateEffectorsData {
3064 Scene *scene;
3066 ListBase *effectors;
3067
3068 float *density;
3069 float *fuel;
3070 float *force_x;
3071 float *force_y;
3072 float *force_z;
3073 float *velocity_x;
3074 float *velocity_y;
3075 float *velocity_z;
3076 int *flags;
3077 float *phi_obs_in;
3078};
3079
3080static void update_effectors_task_cb(void *__restrict userdata,
3081 const int x,
3082 const TaskParallelTLS *__restrict /*tls*/)
3083{
3084 UpdateEffectorsData *data = static_cast<UpdateEffectorsData *>(userdata);
3085 FluidDomainSettings *fds = data->fds;
3086
3087 for (int y = 0; y < fds->res[1]; y++) {
3088 for (int z = 0; z < fds->res[2]; z++) {
3089 EffectedPoint epoint;
3090 float mag;
3091 float voxel_center[3] = {0, 0, 0}, vel[3] = {0, 0, 0}, retvel[3] = {0, 0, 0};
3092 const uint index = manta_get_index(x, fds->res[0], y, fds->res[1], z);
3093
3094 if ((data->fuel && std::max(data->density[index], data->fuel[index]) < FLT_EPSILON) ||
3095 (!data->fuel && data->density && data->density[index] < FLT_EPSILON) ||
3096 (data->phi_obs_in && data->phi_obs_in[index] < 0.0f) ||
3097 data->flags[index] & 2) /* Manta-flow convention: `2 == FlagObstacle`. */
3098 {
3099 continue;
3100 }
3101
3102 /* Get velocities from manta grid space and convert to blender units. */
3103 vel[0] = data->velocity_x[index];
3104 vel[1] = data->velocity_y[index];
3105 vel[2] = data->velocity_z[index];
3106 mul_v3_fl(vel, fds->dx);
3107
3108 /* Convert vel to global space. */
3109 mag = len_v3(vel);
3110 mul_mat3_m4_v3(fds->obmat, vel);
3111 normalize_v3(vel);
3112 mul_v3_fl(vel, mag);
3113
3114 voxel_center[0] = fds->p0[0] + fds->cell_size[0] * (float(x + fds->res_min[0]) + 0.5f);
3115 voxel_center[1] = fds->p0[1] + fds->cell_size[1] * (float(y + fds->res_min[1]) + 0.5f);
3116 voxel_center[2] = fds->p0[2] + fds->cell_size[2] * (float(z + fds->res_min[2]) + 0.5f);
3117 mul_m4_v3(fds->obmat, voxel_center);
3118
3119 /* Do effectors. */
3120 pd_point_from_loc(data->scene, voxel_center, vel, index, &epoint);
3122 data->effectors, nullptr, fds->effector_weights, &epoint, retvel, nullptr, nullptr);
3123
3124 /* Convert retvel to local space. */
3125 mag = len_v3(retvel);
3126 mul_mat3_m4_v3(fds->imat, retvel);
3127 normalize_v3(retvel);
3128 mul_v3_fl(retvel, mag);
3129
3130 /* Copy computed force to fluid solver forces. */
3131 mul_v3_fl(retvel, 0.2f); /* Factor from 0e6820cc5d62. */
3132 clamp_v3(retvel, -1.0f, 1.0f); /* Restrict forces to +-1 interval. */
3133 data->force_x[index] = retvel[0];
3134 data->force_y[index] = retvel[1];
3135 data->force_z[index] = retvel[2];
3136
3137# ifdef DEBUG_PRINT
3138 /* Debugging: Print forces. */
3139 printf("setting force: [%f, %f, %f]\n",
3140 data->force_x[index],
3141 data->force_y[index],
3142 data->force_z[index]);
3143# endif
3144 }
3145 }
3146}
3147
3148static void update_effectors(
3149 Depsgraph *depsgraph, Scene *scene, Object *ob, FluidDomainSettings *fds, float /*dt*/)
3150{
3151 ListBase *effectors;
3152 /* make sure smoke flow influence is 0.0f */
3154 effectors = BKE_effectors_create(depsgraph, ob, nullptr, fds->effector_weights, false);
3155
3156 if (effectors) {
3157 /* Precalculate wind forces. */
3158 UpdateEffectorsData data;
3159 data.scene = scene;
3160 data.fds = fds;
3161 data.effectors = effectors;
3162 data.density = manta_smoke_get_density(fds->fluid);
3163 data.fuel = manta_smoke_get_fuel(fds->fluid);
3164 data.force_x = manta_get_force_x(fds->fluid);
3165 data.force_y = manta_get_force_y(fds->fluid);
3166 data.force_z = manta_get_force_z(fds->fluid);
3167 data.velocity_x = manta_get_velocity_x(fds->fluid);
3168 data.velocity_y = manta_get_velocity_y(fds->fluid);
3169 data.velocity_z = manta_get_velocity_z(fds->fluid);
3170 data.flags = manta_smoke_get_flags(fds->fluid);
3171 data.phi_obs_in = manta_get_phiobs_in(fds->fluid);
3172
3173 TaskParallelSettings settings;
3175 settings.min_iter_per_thread = 2;
3176 BLI_task_parallel_range(0, fds->res[0], &data, update_effectors_task_cb, &settings);
3177 }
3178
3179 BKE_effectors_free(effectors);
3180}
3181
3182static Mesh *create_liquid_geometry(FluidDomainSettings *fds,
3183 Scene *scene,
3184 Mesh *orgmesh,
3185 Object *ob)
3186{
3187 using namespace blender;
3188 using namespace blender::bke;
3189 Mesh *mesh;
3190 float min[3];
3191 float max[3];
3192 float size[3];
3193 float cell_size_scaled[3];
3194
3195 const AttributeAccessor orig_attributes = orgmesh->attributes();
3196 const VArraySpan orig_material_indices = *orig_attributes.lookup<int>("material_index",
3198 const short mp_mat_nr = orig_material_indices.is_empty() ? 0 : orig_material_indices[0];
3199
3200 int i;
3201 int num_verts, num_faces;
3202
3203 if (!fds->fluid) {
3204 return nullptr;
3205 }
3206
3207 num_verts = manta_liquid_get_num_verts(fds->fluid);
3208 num_faces = manta_liquid_get_num_triangles(fds->fluid);
3209
3210# ifdef DEBUG_PRINT
3211 /* Debugging: Print number of vertices, normals, and faces. */
3212 printf("num_verts: %d, num_faces: %d\n", num_verts, num_faces);
3213# endif
3214
3215 if (!num_verts || !num_faces) {
3216 return nullptr;
3217 }
3218
3219 mesh = BKE_mesh_new_nomain(num_verts, 0, num_faces, num_faces * 3);
3220 if (!mesh) {
3221 return nullptr;
3222 }
3223 blender::MutableSpan<blender::float3> positions = mesh->vert_positions_for_write();
3224 blender::MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
3225 blender::MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
3226
3227 if (orgmesh->attributes().domain_size(AttrDomain::Face) > 0) {
3228 const bool is_sharp = orgmesh->attributes()
3229 .lookup_or_default<bool>("sharp_face", AttrDomain::Face, false)
3230 .varray[0];
3231 mesh_smooth_set(*mesh, !is_sharp);
3232 }
3233
3234 /* Get size (dimension) but considering scaling. */
3235 copy_v3_v3(cell_size_scaled, fds->cell_size);
3236 mul_v3_v3(cell_size_scaled, ob->scale);
3237 madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min);
3238 madd_v3fl_v3fl_v3fl_v3i(max, fds->p0, cell_size_scaled, fds->res_max);
3240
3241 /* Biggest dimension will be used for up-scaling. */
3242 float max_size = std::max({size[0], size[1], size[2]});
3243
3244 float co_scale[3];
3245 co_scale[0] = max_size / ob->scale[0];
3246 co_scale[1] = max_size / ob->scale[1];
3247 co_scale[2] = max_size / ob->scale[2];
3248
3249 float co_offset[3];
3250 co_offset[0] = (fds->p0[0] + fds->p1[0]) / 2.0f;
3251 co_offset[1] = (fds->p0[1] + fds->p1[1]) / 2.0f;
3252 co_offset[2] = (fds->p0[2] + fds->p1[2]) / 2.0f;
3253
3254 /* Velocities. */
3255 /* If needed, vertex velocities will be read too. */
3256 bool use_speedvectors = fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS;
3257 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3258 SpanAttributeWriter<float3> velocities;
3259 float time_mult = fds->dx / (DT_DEFAULT * (25.0f / scene->frames_per_second()));
3260
3261 if (use_speedvectors) {
3262 velocities = attributes.lookup_or_add_for_write_only_span<float3>("velocity",
3264 }
3265
3266 /* Loop for vertices and normals. */
3267 for (i = 0; i < num_verts; i++) {
3268
3269 /* Vertices (data is normalized cube around domain origin). */
3270 positions[i][0] = manta_liquid_get_vertex_x_at(fds->fluid, i);
3271 positions[i][1] = manta_liquid_get_vertex_y_at(fds->fluid, i);
3272 positions[i][2] = manta_liquid_get_vertex_z_at(fds->fluid, i);
3273
3274 /* Adjust coordinates from Mantaflow to match viewport scaling. */
3275 float tmp[3] = {float(fds->res[0]), float(fds->res[1]), float(fds->res[2])};
3276 /* Scale to unit cube around 0. */
3277 mul_v3_fl(tmp, fds->mesh_scale * 0.5f);
3278 sub_v3_v3(positions[i], tmp);
3279 /* Apply scaling of domain object. */
3280 mul_v3_fl(positions[i], fds->dx / fds->mesh_scale);
3281
3282 mul_v3_v3(positions[i], co_scale);
3283 add_v3_v3(positions[i], co_offset);
3284
3285# ifdef DEBUG_PRINT
3286 /* Debugging: Print coordinates of vertices. */
3287 printf("positions[i][0]: %f, positions[i][1]: %f, positions[i][2]: %f\n",
3288 positions[i][0],
3289 positions[i][1],
3290 positions[i][2]);
3291# endif
3292
3293# ifdef DEBUG_PRINT
3294 /* Debugging: Print coordinates of normals. */
3295 printf("no_s[0]: %d, no_s[1]: %d, no_s[2]: %d\n", no_s[0], no_s[1], no_s[2]);
3296# endif
3297
3298 if (use_speedvectors) {
3299 velocities.span[i].x = manta_liquid_get_vertvel_x_at(fds->fluid, i) * time_mult;
3300 velocities.span[i].y = manta_liquid_get_vertvel_y_at(fds->fluid, i) * time_mult;
3301 velocities.span[i].z = manta_liquid_get_vertvel_z_at(fds->fluid, i) * time_mult;
3302# ifdef DEBUG_PRINT
3303 /* Debugging: Print velocities of vertices. */
3304 printf("velocities[%d].x: %f, velocities[%d].y: %f, velocities[%d].z: %f\n",
3305 i,
3306 velocities.span[i].x,
3307 i,
3308 velocities.span[i].y,
3309 i,
3310 velocities.span[i].z);
3311# endif
3312 }
3313 }
3314
3315 bke::SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_span<int>(
3316 "material_index", AttrDomain::Face);
3317
3318 /* Loop for triangles. */
3319 for (const int i : face_offsets.index_range().drop_back(1)) {
3320 /* Initialize from existing face. */
3321 material_indices.span[i] = mp_mat_nr;
3322
3323 face_offsets[i] = i * 3;
3324
3325 corner_verts[i * 3 + 0] = manta_liquid_get_triangle_x_at(fds->fluid, i);
3326 corner_verts[i * 3 + 1] = manta_liquid_get_triangle_y_at(fds->fluid, i);
3327 corner_verts[i * 3 + 2] = manta_liquid_get_triangle_z_at(fds->fluid, i);
3328# ifdef DEBUG_PRINT
3329 /* Debugging: Print mesh faces. */
3330 printf("mloops[0].v: %d, mloops[1].v: %d, mloops[2].v: %d\n",
3331 mloops[0].v,
3332 mloops[1].v,
3333 mloops[2].v);
3334# endif
3335 }
3336
3337 velocities.finish();
3338 material_indices.finish();
3339
3340 mesh_calc_edges(*mesh, false, false);
3341
3342 return mesh;
3343}
3344
3345static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Object *ob)
3346{
3347 using namespace blender;
3348 using namespace blender::bke;
3349 Mesh *result;
3350 float min[3];
3351 float max[3];
3352 float *co;
3353 int *corner_vert;
3354
3355 int num_verts = 8;
3356 int num_faces = 6;
3357 float ob_loc[3] = {0};
3358 float ob_cache_loc[3] = {0};
3359
3360 /* Just copy existing mesh if there is no content or if the adaptive domain is not being used. */
3361 if (fds->total_cells <= 1 || (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) == 0) {
3362 return BKE_mesh_copy_for_eval(*orgmesh);
3363 }
3364
3365 result = BKE_mesh_new_nomain(num_verts, 0, num_faces, num_faces * 4);
3366 blender::MutableSpan<blender::float3> positions = result->vert_positions_for_write();
3367 blender::MutableSpan<int> face_offsets = result->face_offsets_for_write();
3368 blender::MutableSpan<int> corner_verts = result->corner_verts_for_write();
3369
3370 if (num_verts) {
3371 /* Volume bounds. */
3372 madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, fds->cell_size, fds->res_min);
3373 madd_v3fl_v3fl_v3fl_v3i(max, fds->p0, fds->cell_size, fds->res_max);
3374
3375 /* Set vertices of smoke BB. Especially important, when BB changes (adaptive domain). */
3376 /* Top slab */
3377 co = positions[0];
3378 co[0] = min[0];
3379 co[1] = min[1];
3380 co[2] = max[2];
3381 co = positions[1];
3382 co[0] = max[0];
3383 co[1] = min[1];
3384 co[2] = max[2];
3385 co = positions[2];
3386 co[0] = max[0];
3387 co[1] = max[1];
3388 co[2] = max[2];
3389 co = positions[3];
3390 co[0] = min[0];
3391 co[1] = max[1];
3392 co[2] = max[2];
3393 /* Bottom slab. */
3394 co = positions[4];
3395 co[0] = min[0];
3396 co[1] = min[1];
3397 co[2] = min[2];
3398 co = positions[5];
3399 co[0] = max[0];
3400 co[1] = min[1];
3401 co[2] = min[2];
3402 co = positions[6];
3403 co[0] = max[0];
3404 co[1] = max[1];
3405 co[2] = min[2];
3406 co = positions[7];
3407 co[0] = min[0];
3408 co[1] = max[1];
3409 co[2] = min[2];
3410
3411 face_offsets.fill(4);
3413
3414 /* Create faces. */
3415 /* Top side. */
3416 corner_vert = &corner_verts[0 * 4];
3417 corner_vert[0] = 0;
3418 corner_vert[1] = 1;
3419 corner_vert[2] = 2;
3420 corner_vert[3] = 3;
3421 /* Right side. */
3422 corner_vert = &corner_verts[1 * 4];
3423 corner_vert[0] = 2;
3424 corner_vert[1] = 1;
3425 corner_vert[2] = 5;
3426 corner_vert[3] = 6;
3427 /* Bottom side. */
3428 corner_vert = &corner_verts[2 * 4];
3429 corner_vert[0] = 7;
3430 corner_vert[1] = 6;
3431 corner_vert[2] = 5;
3432 corner_vert[3] = 4;
3433 /* Left side. */
3434 corner_vert = &corner_verts[3 * 4];
3435 corner_vert[0] = 0;
3436 corner_vert[1] = 3;
3437 corner_vert[2] = 7;
3438 corner_vert[3] = 4;
3439 /* Front side. */
3440 corner_vert = &corner_verts[4 * 4];
3441 corner_vert[0] = 3;
3442 corner_vert[1] = 2;
3443 corner_vert[2] = 6;
3444 corner_vert[3] = 7;
3445 /* Back side. */
3446 corner_vert = &corner_verts[5 * 4];
3447 corner_vert[0] = 1;
3448 corner_vert[1] = 0;
3449 corner_vert[2] = 4;
3450 corner_vert[3] = 5;
3451
3452 /* Calculate required shift to match domain's global position
3453 * it was originally simulated at (if object moves without manta step). */
3454 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
3455 mul_m4_v3(ob->object_to_world().ptr(), ob_loc);
3456 mul_m4_v3(fds->obmat, ob_cache_loc);
3457 sub_v3_v3v3(fds->obj_shift_f, ob_cache_loc, ob_loc);
3458 /* Convert shift to local space and apply to vertices. */
3459 mul_mat3_m4_v3(ob->world_to_object().ptr(), fds->obj_shift_f);
3460 /* Apply shift to vertices. */
3461 for (int i = 0; i < num_verts; i++) {
3462 add_v3_v3(positions[i], fds->obj_shift_f);
3463 }
3464 }
3465
3466 mesh_calc_edges(*result, false, false);
3467 return result;
3468}
3469
3470static int manta_step(
3471 Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh, FluidModifierData *fmd, int frame)
3472{
3473 FluidDomainSettings *fds = fmd->domain;
3474 float dt, frame_length, time_total, time_total_old;
3475 float time_per_frame;
3476 bool init_resolution = true;
3477
3478 /* Store baking success - bake might be aborted anytime by user. */
3479 int result = 1;
3480 int mode = fds->cache_type;
3481 bool mode_replay = (mode == FLUID_DOMAIN_CACHE_REPLAY);
3482
3483 /* Update object state. */
3484 invert_m4_m4(fds->imat, ob->object_to_world().ptr());
3485 copy_m4_m4(fds->obmat, ob->object_to_world().ptr());
3486
3487 /* Gas domain might use adaptive domain. */
3488 if (fds->type == FLUID_DOMAIN_TYPE_GAS) {
3489 init_resolution = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) != 0;
3490 }
3491 manta_set_domain_from_mesh(fds, ob, mesh, init_resolution);
3492
3493 /* Use local variables for adaptive loop, dt can change. */
3494 frame_length = fds->frame_length;
3495 dt = fds->dt;
3496 time_per_frame = 0;
3497 time_total = fds->time_total;
3498 /* Keep track of original total time to correct small errors at end of step. */
3499 time_total_old = fds->time_total;
3500
3501 std::scoped_lock lock(object_update_lock);
3502
3503 /* Loop as long as time_per_frame (sum of sub dt's) does not exceed actual frame-length. */
3504 while (time_per_frame + FLT_EPSILON < frame_length) {
3506 dt = manta_get_timestep(fds->fluid);
3507
3508 /* Save adapted dt so that MANTA object can access it (important when adaptive domain creates
3509 * new MANTA object). */
3510 fds->dt = dt;
3511
3512 /* Calculate inflow geometry. */
3513 update_flowsfluids(depsgraph, scene, ob, fds, time_per_frame, frame_length, frame, dt);
3514
3515 /* If user requested stop, quit baking */
3516 if (G.is_break && !mode_replay) {
3517 result = 0;
3518 break;
3519 }
3520
3521 manta_update_variables(fds->fluid, fmd);
3522
3523 /* Calculate obstacle geometry. */
3524 update_obstacles(depsgraph, scene, ob, fds, time_per_frame, frame_length, frame, dt);
3525
3526 /* If user requested stop, quit baking */
3527 if (G.is_break && !mode_replay) {
3528 result = 0;
3529 break;
3530 }
3531
3532 /* Only bake if the domain is bigger than one cell (important for adaptive domain). */
3533 if (fds->total_cells > 1) {
3534 update_effectors(depsgraph, scene, ob, fds, dt);
3535 manta_bake_data(fds->fluid, fmd, frame);
3536 }
3537
3538 /* Count for how long this while loop is running. */
3539 time_per_frame += dt;
3540 time_total += dt;
3541
3542 fds->time_per_frame = time_per_frame;
3543 fds->time_total = time_total;
3544 }
3545
3546 /* Total time must not exceed frame-count times frame-length. Correct tiny errors here. */
3547 CLAMP_MAX(fds->time_total, time_total_old + fds->frame_length);
3548
3549 /* Compute shadow grid for gas simulations. Make sure to skip if bake job was canceled early. */
3550 if (fds->type == FLUID_DOMAIN_TYPE_GAS && result) {
3551 manta_smoke_calc_transparency(
3553 }
3554
3555 return result;
3556}
3557
3558static void manta_guiding(
3559 Depsgraph *depsgraph, Scene *scene, Object *ob, FluidModifierData *fmd, int frame)
3560{
3561 FluidDomainSettings *fds = fmd->domain;
3562 float dt = DT_DEFAULT * (25.0f / scene->frames_per_second()) * fds->time_scale;
3563
3564 std::scoped_lock lock(object_update_lock);
3565
3566 update_obstacles(depsgraph, scene, ob, fds, dt, dt, frame, dt);
3567 manta_bake_guiding(fds->fluid, fmd, frame);
3568}
3569
3570static void fluid_modifier_processFlow(FluidModifierData *fmd,
3571 Depsgraph *depsgraph,
3572 Scene *scene,
3573 Object *ob,
3574 Mesh *mesh,
3575 const int scene_framenr)
3576{
3577 if (scene_framenr >= fmd->time) {
3578 fluid_modifier_init(fmd, depsgraph, ob, scene, mesh);
3579 }
3580
3581 if (fmd->flow) {
3582 if (fmd->flow->mesh) {
3583 BKE_id_free(nullptr, fmd->flow->mesh);
3584 }
3586 }
3587
3588 if (scene_framenr > fmd->time) {
3589 fmd->time = scene_framenr;
3590 }
3591 else if (scene_framenr < fmd->time) {
3592 fmd->time = scene_framenr;
3593 fluid_modifier_reset_ex(fmd, false);
3594 }
3595}
3596
3597static void fluid_modifier_processEffector(FluidModifierData *fmd,
3598 Depsgraph *depsgraph,
3599 Scene *scene,
3600 Object *ob,
3601 Mesh *mesh,
3602 const int scene_framenr)
3603{
3604 if (scene_framenr >= fmd->time) {
3605 fluid_modifier_init(fmd, depsgraph, ob, scene, mesh);
3606 }
3607
3608 if (fmd->effector) {
3609 if (fmd->effector->mesh) {
3610 BKE_id_free(nullptr, fmd->effector->mesh);
3611 }
3613 }
3614
3615 if (scene_framenr > fmd->time) {
3616 fmd->time = scene_framenr;
3617 }
3618 else if (scene_framenr < fmd->time) {
3619 fmd->time = scene_framenr;
3620 fluid_modifier_reset_ex(fmd, false);
3621 }
3622}
3623
3624static void fluid_modifier_processDomain(FluidModifierData *fmd,
3625 Depsgraph *depsgraph,
3626 Scene *scene,
3627 Object *ob,
3628 Mesh *mesh,
3629 const int scene_framenr)
3630{
3631 FluidDomainSettings *fds = fmd->domain;
3632 Object *guide_parent = nullptr;
3633 Object **objs = nullptr;
3634 uint numobj = 0;
3635 FluidModifierData *fmd_parent = nullptr;
3636
3637 bool is_startframe, has_advanced;
3638 is_startframe = (scene_framenr == fds->cache_frame_start);
3639 has_advanced = (scene_framenr == fmd->time + 1);
3640 int mode = fds->cache_type;
3641
3642 /* Do not process modifier if current frame is out of cache range. */
3643 bool escape = false;
3644 switch (mode) {
3647 if (fds->cache_frame_offset > 0) {
3648 if (scene_framenr < fds->cache_frame_start ||
3649 scene_framenr > fds->cache_frame_end + fds->cache_frame_offset)
3650 {
3651 escape = true;
3652 }
3653 }
3654 else {
3655 if (scene_framenr < fds->cache_frame_start + fds->cache_frame_offset ||
3656 scene_framenr > fds->cache_frame_end)
3657 {
3658 escape = true;
3659 }
3660 }
3661 break;
3663 default:
3664 if (scene_framenr < fds->cache_frame_start || scene_framenr > fds->cache_frame_end) {
3665 escape = true;
3666 }
3667 break;
3668 }
3669 /* If modifier will not be processed, update/flush pointers from (old) fluid object once more. */
3670 if (escape && fds->fluid) {
3671 manta_update_pointers(fds->fluid, fmd, true);
3672 return;
3673 }
3674
3675 /* Reset fluid if no fluid present. Also resets active fields. */
3676 if (!fds->fluid) {
3677 fluid_modifier_reset_ex(fmd, false);
3678 }
3679
3680 /* Ensure cache directory is not relative. */
3681 const char *relbase = BKE_modifier_path_relbase_from_global(ob);
3682 BLI_path_abs(fds->cache_directory, relbase);
3683
3684 /* If 'outdated', reset the cache here. */
3685 if (is_startframe && mode == FLUID_DOMAIN_CACHE_REPLAY) {
3686 PTCacheID pid;
3687 BKE_ptcache_id_from_smoke(&pid, ob, fmd);
3688 if (pid.cache->flag & PTCACHE_OUTDATED) {
3690 BKE_fluid_cache_free_all(fds, ob);
3691 fluid_modifier_reset_ex(fmd, false);
3692 }
3693 }
3694
3695 /* Ensure that all flags are up to date before doing any baking and/or cache reading. */
3697 depsgraph, ob, fds->fluid_group, &numobj, eModifierType_Fluid);
3698 update_flowsflags(fds, objs, numobj);
3699 if (objs) {
3700 MEM_freeN(objs);
3701 }
3703 depsgraph, ob, fds->effector_group, &numobj, eModifierType_Fluid);
3704 update_obstacleflags(fds, objs, numobj);
3705 if (objs) {
3706 MEM_freeN(objs);
3707 }
3708
3709 /* Fluid domain init must not fail in order to continue modifier evaluation. */
3710 if (!fds->fluid && !fluid_modifier_init(fmd, depsgraph, ob, scene, mesh)) {
3711 CLOG_ERROR(&LOG, "Fluid initialization failed. Should not happen!");
3712 return;
3713 }
3714 BLI_assert(fds->fluid);
3715
3716 /* Guiding parent res pointer needs initialization. */
3717 guide_parent = fds->guide_parent;
3718 if (guide_parent) {
3720 if (fmd_parent && fmd_parent->domain) {
3721 copy_v3_v3_int(fds->guide_res, fmd_parent->domain->res);
3722 }
3723 }
3724
3725 /* Adaptive domain needs to know about current state, so save it here. */
3726 int o_res[3], o_min[3], o_max[3], o_shift[3];
3727 copy_v3_v3_int(o_res, fds->res);
3728 copy_v3_v3_int(o_min, fds->res_min);
3729 copy_v3_v3_int(o_max, fds->res_max);
3730 copy_v3_v3_int(o_shift, fds->shift);
3731
3732 /* Ensure that time parameters are initialized correctly before every step. */
3733 fds->frame_length = DT_DEFAULT * (25.0f / scene->frames_per_second()) * fds->time_scale;
3734 fds->dt = fds->frame_length;
3735 fds->time_per_frame = 0;
3736
3737 /* Ensure that gravity is copied over every frame (could be keyframed). */
3738 update_final_gravity(fds, scene);
3739
3740 int next_frame = scene_framenr + 1;
3741 int prev_frame = scene_framenr - 1;
3742 /* Ensure positive of previous frame. */
3743 CLAMP_MIN(prev_frame, fds->cache_frame_start);
3744
3745 int data_frame = scene_framenr, noise_frame = scene_framenr;
3746 int mesh_frame = scene_framenr, particles_frame = scene_framenr, guide_frame = scene_framenr;
3747
3748 bool with_smoke, with_liquid;
3749 with_smoke = fds->type == FLUID_DOMAIN_TYPE_GAS;
3750 with_liquid = fds->type == FLUID_DOMAIN_TYPE_LIQUID;
3751
3752 bool drops, bubble, floater;
3756
3757 bool with_resumable_cache = fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE;
3758 bool with_script, with_noise, with_mesh, with_particles, with_guide;
3759 with_script = fds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT;
3760 with_noise = fds->flags & FLUID_DOMAIN_USE_NOISE;
3761 with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH;
3762 with_guide = fds->flags & FLUID_DOMAIN_USE_GUIDE;
3763 with_particles = drops || bubble || floater;
3764
3765 bool has_data, has_noise, has_mesh, has_particles, has_guide, has_config;
3766 has_data = manta_has_data(fds->fluid, fmd, scene_framenr);
3767 has_noise = manta_has_noise(fds->fluid, fmd, scene_framenr);
3768 has_mesh = manta_has_mesh(fds->fluid, fmd, scene_framenr);
3769 has_particles = manta_has_particles(fds->fluid, fmd, scene_framenr);
3770 has_guide = manta_has_guiding(fds->fluid, fmd, scene_framenr, guide_parent);
3771 has_config = manta_read_config(fds->fluid, fmd, scene_framenr);
3772
3773 /* When reading data from cache (has_config == true) ensure that active fields are allocated.
3774 * update_flowsflags() and update_obstacleflags() will not find flow sources hidden from renders.
3775 * See also: #72192. */
3776 if (has_config) {
3777 ensure_flowsfields(fds);
3778 ensure_obstaclefields(fds);
3779 }
3780
3781 bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide;
3782 baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
3783 baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
3784 baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
3785 baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
3786 baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
3787
3788 bool resume_data, resume_noise, resume_mesh, resume_particles, resume_guide;
3789 resume_data = (!is_startframe) && (fds->cache_frame_pause_data == scene_framenr);
3790 resume_noise = (!is_startframe) && (fds->cache_frame_pause_noise == scene_framenr);
3791 resume_mesh = (!is_startframe) && (fds->cache_frame_pause_mesh == scene_framenr);
3792 resume_particles = (!is_startframe) && (fds->cache_frame_pause_particles == scene_framenr);
3793 resume_guide = (!is_startframe) && (fds->cache_frame_pause_guide == scene_framenr);
3794
3795 bool read_cache, bake_cache;
3796 read_cache = false;
3797 bake_cache = baking_data || baking_noise || baking_mesh || baking_particles || baking_guide;
3798
3799 bool next_data, next_noise, next_mesh, next_particles, next_guide;
3800 next_data = manta_has_data(fds->fluid, fmd, next_frame);
3801 next_noise = manta_has_noise(fds->fluid, fmd, next_frame);
3802 next_mesh = manta_has_mesh(fds->fluid, fmd, next_frame);
3803 next_particles = manta_has_particles(fds->fluid, fmd, next_frame);
3804 next_guide = manta_has_guiding(fds->fluid, fmd, next_frame, guide_parent);
3805
3806 bool prev_data, prev_noise, prev_mesh, prev_particles, prev_guide;
3807 prev_data = manta_has_data(fds->fluid, fmd, prev_frame);
3808 prev_noise = manta_has_noise(fds->fluid, fmd, prev_frame);
3809 prev_mesh = manta_has_mesh(fds->fluid, fmd, prev_frame);
3810 prev_particles = manta_has_particles(fds->fluid, fmd, prev_frame);
3811 prev_guide = manta_has_guiding(fds->fluid, fmd, prev_frame, guide_parent);
3812
3813 /* Unused for now. */
3814 UNUSED_VARS(next_mesh, next_guide);
3815
3816 bool with_gdomain;
3817 with_gdomain = (fds->guide_source == FLUID_DOMAIN_GUIDE_SRC_DOMAIN);
3818
3819 /* Cache mode specific settings. */
3820 switch (mode) {
3823 /* Just load the data that has already been baked */
3824 if (!baking_data && !baking_noise && !baking_mesh && !baking_particles && !baking_guide) {
3825 read_cache = true;
3826 bake_cache = false;
3827
3828 /* Apply frame offset. */
3829 data_frame -= fmd->domain->cache_frame_offset;
3830 noise_frame -= fmd->domain->cache_frame_offset;
3831 mesh_frame -= fmd->domain->cache_frame_offset;
3832 particles_frame -= fmd->domain->cache_frame_offset;
3833 break;
3834 }
3835
3836 /* Set to previous frame if the bake was resumed
3837 * ie don't read all of the already baked frames, just the one before bake resumes */
3838 if (baking_data && resume_data) {
3839 data_frame = prev_frame;
3840 }
3841 if (baking_noise && resume_noise) {
3842 noise_frame = prev_frame;
3843 }
3844 if (baking_mesh && resume_mesh) {
3845 mesh_frame = prev_frame;
3846 }
3847 if (baking_particles && resume_particles) {
3848 particles_frame = prev_frame;
3849 }
3850 if (baking_guide && resume_guide) {
3851 guide_frame = prev_frame;
3852 }
3853
3854 /* Noise, mesh and particles can never be baked more than data. */
3855 CLAMP_MAX(noise_frame, data_frame);
3856 CLAMP_MAX(mesh_frame, data_frame);
3857 CLAMP_MAX(particles_frame, data_frame);
3858 CLAMP_MAX(guide_frame, fds->cache_frame_end);
3859
3860 /* Force to read cache as we're resuming the bake */
3861 read_cache = true;
3862 break;
3864 default:
3865 baking_data = !has_data && (is_startframe || prev_data);
3866 if (with_smoke && with_noise) {
3867 baking_noise = !has_noise && (is_startframe || prev_noise);
3868 }
3869 if (with_liquid && with_mesh) {
3870 baking_mesh = !has_mesh && (is_startframe || prev_mesh);
3871 }
3872 if (with_liquid && with_particles) {
3873 baking_particles = !has_particles && (is_startframe || prev_particles);
3874 }
3875
3876 /* Always trying to read the cache in replay mode. */
3877 read_cache = true;
3878 bake_cache = false;
3879 break;
3880 }
3881
3882 bool read_partial = false, read_all = false;
3883 bool grid_display = fds->use_coba;
3884
3885 /* Try to read from cache and keep track of read success. */
3886 if (read_cache) {
3887
3888 /* Read mesh cache. */
3889 if (with_liquid && with_mesh) {
3890 if (mesh_frame != scene_framenr) {
3891 has_config = manta_read_config(fds->fluid, fmd, mesh_frame);
3892 }
3893
3894 /* Only load the mesh at the resolution it ways originally simulated at.
3895 * The mesh files don't have a header, i.e. the don't store the grid resolution. */
3896 if (!manta_needs_realloc(fds->fluid, fmd)) {
3897 has_mesh = manta_read_mesh(fds->fluid, fmd, mesh_frame);
3898 }
3899 }
3900
3901 /* Read particles cache. */
3902 if (with_liquid && with_particles) {
3903 if (particles_frame != scene_framenr) {
3904 has_config = manta_read_config(fds->fluid, fmd, particles_frame);
3905 }
3906
3907 read_partial = !baking_data && !baking_particles && next_particles;
3908 read_all = !read_partial && with_resumable_cache;
3909 has_particles = manta_read_particles(fds->fluid, fmd, particles_frame, read_all);
3910 }
3911
3912 /* Read guide cache. */
3913 if (with_guide) {
3914 FluidModifierData *fmd2 = (with_gdomain) ? fmd_parent : fmd;
3915 has_guide = manta_read_guiding(fds->fluid, fmd2, scene_framenr, with_gdomain);
3916 }
3917
3918 /* Read noise and data cache */
3919 if (with_smoke && with_noise) {
3920 if (noise_frame != scene_framenr) {
3921 has_config = manta_read_config(fds->fluid, fmd, noise_frame);
3922 }
3923
3924 /* Only reallocate when just reading cache or when resuming during bake. */
3925 if (has_data && has_config && manta_needs_realloc(fds->fluid, fmd)) {
3927 fds, o_res, fds->res, o_min, fds->res_min, o_max, o_shift, fds->shift);
3928 }
3929
3930 read_partial = !baking_data && !baking_noise && next_noise;
3931 read_all = !read_partial && with_resumable_cache;
3932 has_noise = manta_read_noise(fds->fluid, fmd, noise_frame, read_all);
3933
3934 read_partial = !baking_data && !baking_noise && next_data && next_noise;
3935 read_all = !read_partial && with_resumable_cache;
3936 has_data = manta_read_data(fds->fluid, fmd, data_frame, read_all);
3937 }
3938 /* Read data cache only */
3939 else {
3940 if (data_frame != scene_framenr) {
3941 has_config = manta_read_config(fds->fluid, fmd, data_frame);
3942 }
3943
3944 if (with_smoke || with_liquid) {
3945 /* Read config and realloc fluid object if needed. */
3946 if (has_config && manta_needs_realloc(fds->fluid, fmd)) {
3947 BKE_fluid_reallocate_fluid(fds, fds->res, 1);
3948 }
3949 }
3950
3951 read_partial = !baking_data && !baking_particles && !baking_mesh && next_data &&
3952 !grid_display;
3953 read_all = !read_partial && with_resumable_cache;
3954 has_data = manta_read_data(fds->fluid, fmd, data_frame, read_all);
3955 }
3956 }
3957
3958 /* Cache mode specific settings */
3959 switch (mode) {
3962 if (!baking_data && !baking_noise && !baking_mesh && !baking_particles && !baking_guide) {
3963 bake_cache = false;
3964 }
3965 break;
3967 default:
3968 if (with_guide) {
3969 baking_guide = !has_guide && (is_startframe || prev_guide);
3970 }
3971 baking_data = !has_data && (is_startframe || prev_data);
3972 if (with_smoke && with_noise) {
3973 baking_noise = !has_noise && (is_startframe || prev_noise);
3974 }
3975 if (with_liquid && with_mesh) {
3976 baking_mesh = !has_mesh && (is_startframe || prev_mesh);
3977 }
3978 if (with_liquid && with_particles) {
3979 baking_particles = !has_particles && (is_startframe || prev_particles);
3980 }
3981
3982 /* Only bake if time advanced by one frame. */
3983 if (is_startframe || has_advanced) {
3984 bake_cache = baking_data || baking_noise || baking_mesh || baking_particles;
3985 }
3986 break;
3987 }
3988
3989 /* Trigger bake calls individually */
3990 if (bake_cache) {
3991 /* Ensure fresh variables at every animation step */
3992 manta_update_variables(fds->fluid, fmd);
3993
3994 /* Export mantaflow python script on first frame (once only) and for any bake type */
3995 if (with_script && is_startframe) {
3996 if (with_smoke) {
3998 }
3999 if (with_liquid) {
4001 }
4002 }
4003
4004 if (baking_guide && with_guide) {
4005 manta_guiding(depsgraph, scene, ob, fmd, scene_framenr);
4006 }
4007 if (baking_data) {
4008 /* Only save baked data if all of it completed successfully. */
4009 if (manta_step(depsgraph, scene, ob, mesh, fmd, scene_framenr)) {
4010 manta_write_config(fds->fluid, fmd, scene_framenr);
4011 manta_write_data(fds->fluid, fmd, scene_framenr);
4012 }
4013 }
4014 if (has_data || baking_data) {
4015 if (baking_noise && with_smoke && with_noise) {
4016 /* Ensure that no bake occurs if domain was minimized by adaptive domain. */
4017 if (fds->total_cells > 1) {
4018 manta_bake_noise(fds->fluid, fmd, scene_framenr);
4019 }
4020 manta_write_noise(fds->fluid, fmd, scene_framenr);
4021 }
4022 if (baking_mesh && with_liquid && with_mesh) {
4023 manta_bake_mesh(fds->fluid, fmd, scene_framenr);
4024 }
4025 if (baking_particles && with_liquid && with_particles) {
4026 manta_bake_particles(fds->fluid, fmd, scene_framenr);
4027 }
4028 }
4029 }
4030
4031 /* Ensure that fluid pointers are always up to date at the end of modifier processing. */
4032 manta_update_pointers(fds->fluid, fmd, false);
4033
4035 fmd->time = scene_framenr;
4036}
4037
4038static void fluid_modifier_process(
4039 FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
4040{
4041 const int scene_framenr = int(DEG_get_ctime(depsgraph));
4042
4043 if (fmd->type & MOD_FLUID_TYPE_FLOW) {
4044 fluid_modifier_processFlow(fmd, depsgraph, scene, ob, mesh, scene_framenr);
4045 }
4046 else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
4047 fluid_modifier_processEffector(fmd, depsgraph, scene, ob, mesh, scene_framenr);
4048 }
4049 else if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
4050 fluid_modifier_processDomain(fmd, depsgraph, scene, ob, mesh, scene_framenr);
4051 }
4052}
4053
4055 FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
4056{
4057 /* Optimization: Do not update viewport during bakes (except in replay mode)
4058 * Reason: UI is locked and updated liquid / smoke geometry is not visible anyways. */
4059 bool needs_viewport_update = false;
4060
4061 /* Optimization: Only process modifier if object is not being altered. */
4062 if (!G.moving) {
4063 /* Lock so preview render does not read smoke data while it gets modified. */
4064 if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
4066 }
4067
4068 fluid_modifier_process(fmd, depsgraph, scene, ob, mesh);
4069
4070 if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
4071 BLI_rw_mutex_unlock(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex));
4072 }
4073
4074 if (fmd->domain) {
4075 FluidDomainSettings *fds = fmd->domain;
4076
4077 /* Always update viewport in cache replay mode. */
4080 {
4081 needs_viewport_update = true;
4082 }
4083 /* In other cache modes, only update the viewport when no bake is going on. */
4084 else {
4085 bool with_mesh;
4086 with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH;
4087 bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide;
4088 baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
4089 baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
4090 baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
4091 baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
4092 baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
4093
4094 if (with_mesh && !baking_data && !baking_noise && !baking_mesh && !baking_particles &&
4095 !baking_guide)
4096 {
4097 needs_viewport_update = true;
4098 }
4099 }
4100 }
4101 }
4102
4103 Mesh *result = nullptr;
4104 if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
4105 if (needs_viewport_update) {
4106 /* Return generated geometry depending on domain type. */
4107 if (fmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) {
4108 result = create_liquid_geometry(fmd->domain, scene, mesh, ob);
4109 }
4110 if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) {
4111 result = create_smoke_geometry(fmd->domain, mesh, ob);
4112 }
4113 }
4114
4115 /* Clear flag outside of locked block (above). */
4121 }
4122
4123 if (!result) {
4125 }
4126 else {
4128 }
4129
4130 /* Liquid simulation has a texture space that based on the bounds of the fluid mesh.
4131 * This does not seem particularly useful, but it's backwards compatible.
4132 *
4133 * Smoke simulation needs a texture space relative to the adaptive domain bounds, not the
4134 * original mesh. So recompute it at this point in the modifier stack. See #58492. */
4136
4137 return result;
4138}
4139
4140static float calc_voxel_transp(
4141 float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct)
4142{
4143 const size_t index = manta_get_index(pixel[0], res[0], pixel[1], res[1], pixel[2]);
4144
4145 /* `T_ray *= T_vox`. */
4146 *t_ray *= expf(input[index] * correct);
4147
4148 if (result[index] < 0.0f) {
4149 result[index] = *t_ray;
4150 }
4151
4152 return *t_ray;
4153}
4154
4155static void bresenham_linie_3D(int x1,
4156 int y1,
4157 int z1,
4158 int x2,
4159 int y2,
4160 int z2,
4161 float *t_ray,
4163 float *result,
4164 float *input,
4165 int res[3],
4166 float correct)
4167{
4168 int dx, dy, dz, i, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2;
4169 int pixel[3];
4170
4171 pixel[0] = x1;
4172 pixel[1] = y1;
4173 pixel[2] = z1;
4174
4175 dx = x2 - x1;
4176 dy = y2 - y1;
4177 dz = z2 - z1;
4178
4179 x_inc = (dx < 0) ? -1 : 1;
4180 l = abs(dx);
4181 y_inc = (dy < 0) ? -1 : 1;
4182 m = abs(dy);
4183 z_inc = (dz < 0) ? -1 : 1;
4184 n = abs(dz);
4185 dx2 = l << 1;
4186 dy2 = m << 1;
4187 dz2 = n << 1;
4188
4189 if ((l >= m) && (l >= n)) {
4190 err_1 = dy2 - l;
4191 err_2 = dz2 - l;
4192 for (i = 0; i < l; i++) {
4193 if (cb(result, input, res, pixel, t_ray, correct) <= FLT_EPSILON) {
4194 break;
4195 }
4196 if (err_1 > 0) {
4197 pixel[1] += y_inc;
4198 err_1 -= dx2;
4199 }
4200 if (err_2 > 0) {
4201 pixel[2] += z_inc;
4202 err_2 -= dx2;
4203 }
4204 err_1 += dy2;
4205 err_2 += dz2;
4206 pixel[0] += x_inc;
4207 }
4208 }
4209 else if ((m >= l) && (m >= n)) {
4210 err_1 = dx2 - m;
4211 err_2 = dz2 - m;
4212 for (i = 0; i < m; i++) {
4213 if (cb(result, input, res, pixel, t_ray, correct) <= FLT_EPSILON) {
4214 break;
4215 }
4216 if (err_1 > 0) {
4217 pixel[0] += x_inc;
4218 err_1 -= dy2;
4219 }
4220 if (err_2 > 0) {
4221 pixel[2] += z_inc;
4222 err_2 -= dy2;
4223 }
4224 err_1 += dx2;
4225 err_2 += dz2;
4226 pixel[1] += y_inc;
4227 }
4228 }
4229 else {
4230 err_1 = dy2 - n;
4231 err_2 = dx2 - n;
4232 for (i = 0; i < n; i++) {
4233 if (cb(result, input, res, pixel, t_ray, correct) <= FLT_EPSILON) {
4234 break;
4235 }
4236 if (err_1 > 0) {
4237 pixel[1] += y_inc;
4238 err_1 -= dz2;
4239 }
4240 if (err_2 > 0) {
4241 pixel[0] += x_inc;
4242 err_2 -= dz2;
4243 }
4244 err_1 += dy2;
4245 err_2 += dx2;
4246 pixel[2] += z_inc;
4247 }
4248 }
4249 cb(result, input, res, pixel, t_ray, correct);
4250}
4251
4252static void manta_smoke_calc_transparency(FluidDomainSettings *fds,
4253 Scene *scene,
4254 ViewLayer *view_layer)
4255{
4256 float bv[6] = {0};
4257 float light[3];
4258 int slabsize = fds->res[0] * fds->res[1];
4259 float *density = manta_smoke_get_density(fds->fluid);
4260 float *shadow = manta_smoke_get_shadow(fds->fluid);
4261 float correct = -7.0f * fds->dx;
4262
4263 if (!get_light(scene, view_layer, light)) {
4264 return;
4265 }
4266
4267 /* Convert light pos to sim cell space. */
4268 mul_m4_v3(fds->imat, light);
4269 light[0] = (light[0] - fds->p0[0]) / fds->cell_size[0] - 0.5f - float(fds->res_min[0]);
4270 light[1] = (light[1] - fds->p0[1]) / fds->cell_size[1] - 0.5f - float(fds->res_min[1]);
4271 light[2] = (light[2] - fds->p0[2]) / fds->cell_size[2] - 0.5f - float(fds->res_min[2]);
4272
4273 /* Calculate domain bounds in sim cell space. */
4274 /* 0,2,4 = 0.0f */
4275 bv[1] = float(fds->res[0]); /* X */
4276 bv[3] = float(fds->res[1]); /* Y */
4277 bv[5] = float(fds->res[2]); /* Z */
4278
4279 for (int z = 0; z < fds->res[2]; z++) {
4280 size_t index = z * slabsize;
4281
4282 for (int y = 0; y < fds->res[1]; y++) {
4283 for (int x = 0; x < fds->res[0]; x++, index++) {
4284 float voxel_center[3];
4285 float pos[3];
4286 int cell[3];
4287 float t_ray = 1.0;
4288
4289 /* Reset shadow value. */
4290 shadow[index] = -1.0f;
4291
4292 voxel_center[0] = float(x);
4293 voxel_center[1] = float(y);
4294 voxel_center[2] = float(z);
4295
4296 /* Get starting cell (light pos). */
4297 if (BLI_bvhtree_bb_raycast(bv, light, voxel_center, pos) > FLT_EPSILON) {
4298 /* We're outside -> use point on side of domain. */
4299 cell[0] = int(floor(pos[0]));
4300 cell[1] = int(floor(pos[1]));
4301 cell[2] = int(floor(pos[2]));
4302 }
4303 else {
4304 /* We're inside -> use light itself. */
4305 cell[0] = int(floor(light[0]));
4306 cell[1] = int(floor(light[1]));
4307 cell[2] = int(floor(light[2]));
4308 }
4309 /* Clamp within grid bounds */
4310 CLAMP(cell[0], 0, fds->res[0] - 1);
4311 CLAMP(cell[1], 0, fds->res[1] - 1);
4312 CLAMP(cell[2], 0, fds->res[2] - 1);
4313
4314 bresenham_linie_3D(cell[0],
4315 cell[1],
4316 cell[2],
4317 x,
4318 y,
4319 z,
4320 &t_ray,
4321 calc_voxel_transp,
4322 shadow,
4323 density,
4324 fds->res,
4325 correct);
4326
4327 /* Convention -> from a RGBA float array, use G value for t_ray. */
4328 shadow[index] = t_ray;
4329 }
4330 }
4331 }
4332}
4333
4334float BKE_fluid_get_velocity_at(Object *ob, float position[3], float velocity[3])
4335{
4337 zero_v3(velocity);
4338
4339 if (fmd && (fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain && fmd->domain->fluid) {
4340 FluidDomainSettings *fds = fmd->domain;
4341 float time_mult = 25.0f * DT_DEFAULT;
4342 float size_mult = std::max({fds->global_size[0], fds->global_size[1], fds->global_size[2]}) /
4343 std::max({fds->base_res[0], fds->base_res[1], fds->base_res[2]});
4344 float vel_mag;
4345 float density = 0.0f, fuel = 0.0f;
4346 float pos[3];
4347 copy_v3_v3(pos, position);
4348 manta_pos_to_cell(fds, pos);
4349
4350 /* Check if position is outside domain max bounds. */
4351 if (pos[0] < fds->res_min[0] || pos[1] < fds->res_min[1] || pos[2] < fds->res_min[2]) {
4352 return -1.0f;
4353 }
4354 if (pos[0] > fds->res_max[0] || pos[1] > fds->res_max[1] || pos[2] > fds->res_max[2]) {
4355 return -1.0f;
4356 }
4357
4358 /* map pos between 0.0 - 1.0 */
4359 pos[0] = (pos[0] - fds->res_min[0]) / float(fds->res[0]);
4360 pos[1] = (pos[1] - fds->res_min[1]) / float(fds->res[1]);
4361 pos[2] = (pos[2] - fds->res_min[2]) / float(fds->res[2]);
4362
4363 /* Check if position is outside active area. */
4365 if (pos[0] < 0.0f || pos[1] < 0.0f || pos[2] < 0.0f) {
4366 return 0.0f;
4367 }
4368 if (pos[0] > 1.0f || pos[1] > 1.0f || pos[2] > 1.0f) {
4369 return 0.0f;
4370 }
4371 }
4372
4373 /* Get interpolated velocity at given position. */
4374 velocity[0] = BLI_voxel_sample_trilinear(manta_get_velocity_x(fds->fluid), fds->res, pos);
4375 velocity[1] = BLI_voxel_sample_trilinear(manta_get_velocity_y(fds->fluid), fds->res, pos);
4376 velocity[2] = BLI_voxel_sample_trilinear(manta_get_velocity_z(fds->fluid), fds->res, pos);
4377
4378 /* Convert simulation units to Blender units. */
4379 mul_v3_fl(velocity, size_mult);
4380 mul_v3_fl(velocity, time_mult);
4381
4382 /* Convert velocity direction to global space. */
4383 vel_mag = len_v3(velocity);
4384 mul_mat3_m4_v3(fds->obmat, velocity);
4385 normalize_v3(velocity);
4386 mul_v3_fl(velocity, vel_mag);
4387
4388 /* Use max value of fuel or smoke density. */
4390 if (manta_smoke_has_fuel(fds->fluid)) {
4392 }
4393 return std::max(density, fuel);
4394 }
4395 return -1.0f;
4396}
4397
4399{
4400 int flags = 0;
4401
4402 if (fds->fluid) {
4403 if (manta_smoke_has_heat(fds->fluid)) {
4404 flags |= FLUID_DOMAIN_ACTIVE_HEAT;
4405 }
4406 if (manta_smoke_has_fuel(fds->fluid)) {
4407 flags |= FLUID_DOMAIN_ACTIVE_FIRE;
4408 }
4409 if (manta_smoke_has_colors(fds->fluid)) {
4411 }
4412 }
4413
4414 return flags;
4415}
4416
4418 Object *ob,
4419 const char *pset_name,
4420 const char *parts_name,
4421 const char *psys_name,
4422 const int psys_type)
4423{
4424 ParticleSystem *psys;
4425 ParticleSettings *part;
4427
4428 /* add particle system */
4429 part = BKE_particlesettings_add(bmain, pset_name);
4430 psys = MEM_callocN<ParticleSystem>(__func__);
4431
4432 part->type = psys_type;
4433 part->totpart = 0;
4434 part->draw_size = 0.01f; /* Make fluid particles more subtle in viewport. */
4436
4437 /* Use different shape and color for fluid particles to be able to find issues in Viewport */
4438 if (psys_type == PART_FLUID_BUBBLE) {
4439 part->draw_as = PART_DRAW_CIRC;
4440 }
4441 if (psys_type == PART_FLUID_FOAM) {
4442 part->draw_as = PART_DRAW_CROSS;
4443 }
4444
4445 part->phystype = PART_PHYS_NO; /* No physics needed, part system only used to display data. */
4446 psys->part = part;
4447 psys->pointcache = BKE_ptcache_add(&psys->ptcaches);
4448 STRNCPY(psys->name, parts_name);
4449 BLI_addtail(&ob->particlesystem, psys);
4450
4451 /* add modifier */
4453 STRNCPY(pfmd->modifier.name, psys_name);
4454 pfmd->psys = psys;
4455 BLI_addtail(&ob->modifiers, pfmd);
4458}
4459
4460void BKE_fluid_particle_system_destroy(Object *ob, const int particle_type)
4461{
4463 ParticleSystem *psys, *next_psys;
4464
4465 for (psys = static_cast<ParticleSystem *>(ob->particlesystem.first); psys; psys = next_psys) {
4466 next_psys = psys->next;
4467 if (psys->part->type == particle_type) {
4468 /* clear modifier */
4469 pfmd = psys_get_modifier(ob, psys);
4472
4473 /* clear particle system */
4474 BLI_remlink(&ob->particlesystem, psys);
4475 psys_free(ob, psys);
4476 }
4477 }
4478}
4479
4481
4482#endif /* WITH_FLUID */
4483
4484/* -------------------------------------------------------------------- */
4489
4491{
4492 settings->cache_frame_start = (value > settings->cache_frame_end) ? settings->cache_frame_end :
4493 value;
4494}
4495
4497{
4498 settings->cache_frame_end = (value < settings->cache_frame_start) ? settings->cache_frame_start :
4499 value;
4500}
4501
4502void BKE_fluid_cachetype_mesh_set(FluidDomainSettings *settings, int cache_mesh_format)
4503{
4504 if (cache_mesh_format == settings->cache_mesh_format) {
4505 return;
4506 }
4507 /* TODO(sebbas): Clear old caches. */
4508 settings->cache_mesh_format = cache_mesh_format;
4509}
4510
4511void BKE_fluid_cachetype_data_set(FluidDomainSettings *settings, int cache_data_format)
4512{
4513 if (cache_data_format == settings->cache_data_format) {
4514 return;
4515 }
4516 /* TODO(sebbas): Clear old caches. */
4517 settings->cache_data_format = cache_data_format;
4518}
4519
4520void BKE_fluid_cachetype_particle_set(FluidDomainSettings *settings, int cache_particle_format)
4521{
4522 if (cache_particle_format == settings->cache_particle_format) {
4523 return;
4524 }
4525 /* TODO(sebbas): Clear old caches. */
4526 settings->cache_particle_format = cache_particle_format;
4527}
4528
4529void BKE_fluid_cachetype_noise_set(FluidDomainSettings *settings, int cache_noise_format)
4530{
4531 if (cache_noise_format == settings->cache_noise_format) {
4532 return;
4533 }
4534 /* TODO(sebbas): Clear old caches. */
4535 settings->cache_noise_format = cache_noise_format;
4536}
4537
4539{
4540 if (clear) {
4541 settings->border_collisions &= value;
4542 }
4543 else {
4544 settings->border_collisions |= value;
4545 }
4546}
4547
4548void BKE_fluid_particles_set(FluidDomainSettings *settings, int value, bool clear)
4549{
4550 if (clear) {
4551 settings->particle_type &= ~value;
4552 }
4553 else {
4554 settings->particle_type |= value;
4555 }
4556}
4557
4558void BKE_fluid_domain_type_set(Object *object, FluidDomainSettings *settings, int type)
4559{
4560 /* Set values for border collision:
4561 * Liquids should have a closed domain, smoke domains should be open. */
4562 if (type == FLUID_DOMAIN_TYPE_GAS) {
4569 object->dt = OB_WIRE;
4570 }
4571 else if (type == FLUID_DOMAIN_TYPE_LIQUID) {
4578 object->dt = OB_SOLID;
4579 }
4580
4581 /* Set actual domain type. */
4582 settings->type = type;
4583}
4584
4585void BKE_fluid_flow_behavior_set(Object * /*object*/, FluidFlowSettings *settings, int behavior)
4586{
4587 settings->behavior = behavior;
4588}
4589
4590void BKE_fluid_flow_type_set(Object *object, FluidFlowSettings *settings, int type)
4591{
4592 /* By default, liquid flow objects should behave like their geometry (geometry behavior),
4593 * gas flow objects should continuously produce smoke (inflow behavior). */
4594 if (type == FLUID_FLOW_TYPE_LIQUID) {
4596 }
4597 else {
4599 }
4600
4601 /* Set actual flow type. */
4602 settings->type = type;
4603}
4604
4605void BKE_fluid_effector_type_set(Object * /*object*/, FluidEffectorSettings *settings, int type)
4606{
4607 settings->type = type;
4608}
4609
4611{
4612 /* Based on the domain type, certain fields are defaulted accordingly if the selected field
4613 * is unsupported. */
4614 const char coba_field = settings->coba_field;
4615 const char data_depth = settings->openvdb_data_depth;
4616
4617 if (settings->type == FLUID_DOMAIN_TYPE_GAS) {
4618 if (ELEM(coba_field,
4623 {
4624 /* Defaulted to density for gas domain. */
4626 }
4627
4628 /* Gas domains do not support VDB mini precision. */
4629 if (data_depth == VDB_PRECISION_MINI_FLOAT) {
4631 }
4632 }
4633 else if (settings->type == FLUID_DOMAIN_TYPE_LIQUID) {
4634 if (ELEM(coba_field,
4642 {
4643 /* Defaulted to phi for liquid domain. */
4645 }
4646 }
4647}
4648
4650
4651/* -------------------------------------------------------------------- */
4656
4658{
4659 if (fmd->domain) {
4660 if (fmd->domain->fluid) {
4661#ifdef WITH_FLUID
4662 manta_free(fmd->domain->fluid);
4663#endif
4664 }
4665
4666 if (fmd->domain->fluid_mutex) {
4667 BLI_rw_mutex_free(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex));
4668 }
4669
4671
4674 fmd->domain->point_cache[0] = nullptr;
4675 }
4676
4677 if (fmd->domain->coba) {
4678 MEM_freeN(fmd->domain->coba);
4679 }
4680
4681 MEM_freeN(fmd->domain);
4682 fmd->domain = nullptr;
4683 }
4684}
4685
4687{
4688 if (fmd->flow) {
4689 if (fmd->flow->mesh) {
4690 BKE_id_free(nullptr, fmd->flow->mesh);
4691 }
4692 fmd->flow->mesh = nullptr;
4693
4695 fmd->flow->numverts = 0;
4697
4698 MEM_freeN(fmd->flow);
4699 fmd->flow = nullptr;
4700 }
4701}
4702
4704{
4705 if (fmd->effector) {
4706 if (fmd->effector->mesh) {
4707 BKE_id_free(nullptr, fmd->effector->mesh);
4708 }
4709 fmd->effector->mesh = nullptr;
4710
4712 fmd->effector->numverts = 0;
4714
4715 MEM_freeN(fmd->effector);
4716 fmd->effector = nullptr;
4717 }
4718}
4719
4720static void fluid_modifier_reset_ex(FluidModifierData *fmd, bool need_lock)
4721{
4722 if (!fmd) {
4723 return;
4724 }
4725
4726 if (fmd->domain) {
4727 if (fmd->domain->fluid) {
4728 if (need_lock) {
4729 BLI_rw_mutex_lock(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex),
4731 }
4732
4733#ifdef WITH_FLUID
4734 manta_free(fmd->domain->fluid);
4735#endif
4736 fmd->domain->fluid = nullptr;
4737
4738 if (need_lock) {
4739 BLI_rw_mutex_unlock(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex));
4740 }
4741 }
4742
4743 fmd->time = -1;
4744 fmd->domain->total_cells = 0;
4745 fmd->domain->active_fields = 0;
4746 }
4747 else if (fmd->flow) {
4749 fmd->flow->numverts = 0;
4751 }
4752 else if (fmd->effector) {
4754 fmd->effector->numverts = 0;
4756 }
4757}
4758
4763
4765{
4766 if (!fmd) {
4767 return;
4768 }
4769
4773}
4774
4776{
4777 if (!fmd) {
4778 return;
4779 }
4780
4781 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
4782 if (fmd->domain) {
4784 }
4785
4787 fmd->domain->fmd = fmd;
4788
4789 /* Turn off incompatible options. */
4790#ifndef WITH_OPENVDB
4794#endif
4795#ifndef WITH_OPENVDB_BLOSC
4797#endif
4798
4801
4802 char cache_name[64];
4803 BKE_fluid_cache_new_name_for_current_session(sizeof(cache_name), cache_name);
4805 fmd->domain->cache_directory, sizeof(fmd->domain->cache_directory), cache_name);
4806
4807 /* pointcache options */
4808 fmd->domain->point_cache[0] = BKE_ptcache_add(&(fmd->domain->ptcaches[0]));
4810 fmd->domain->point_cache[0]->step = 1;
4811 fmd->domain->point_cache[1] = nullptr; /* Deprecated */
4812 }
4813 else if (fmd->type & MOD_FLUID_TYPE_FLOW) {
4814 if (fmd->flow) {
4816 }
4817
4819 fmd->flow->fmd = fmd;
4820 }
4821 else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
4822 if (fmd->effector) {
4824 }
4825
4827 fmd->effector->fmd = fmd;
4828 }
4829}
4830
4832{
4833 tfmd->type = fmd->type;
4834 tfmd->time = fmd->time;
4835
4837
4838 if (tfmd->domain) {
4839 FluidDomainSettings *tfds = tfmd->domain;
4840 FluidDomainSettings *fds = fmd->domain;
4841
4842 /* domain object data */
4843 tfds->fluid_group = fds->fluid_group;
4844 tfds->force_group = fds->force_group;
4845 tfds->effector_group = fds->effector_group;
4846 if (tfds->effector_weights) {
4848 }
4849 tfds->effector_weights = static_cast<EffectorWeights *>(MEM_dupallocN(fds->effector_weights));
4850
4851 /* adaptive domain options */
4852 tfds->adapt_margin = fds->adapt_margin;
4853 tfds->adapt_res = fds->adapt_res;
4854 tfds->adapt_threshold = fds->adapt_threshold;
4855
4856 /* fluid domain options */
4857 tfds->maxres = fds->maxres;
4858 tfds->solver_res = fds->solver_res;
4860 tfds->flags = fds->flags;
4861 tfds->gravity[0] = fds->gravity[0];
4862 tfds->gravity[1] = fds->gravity[1];
4863 tfds->gravity[2] = fds->gravity[2];
4864 tfds->active_fields = fds->active_fields;
4865 tfds->type = fds->type;
4866 tfds->boundary_width = fds->boundary_width;
4867
4868 /* smoke domain options */
4869 tfds->alpha = fds->alpha;
4870 tfds->beta = fds->beta;
4871 tfds->diss_speed = fds->diss_speed;
4872 tfds->vorticity = fds->vorticity;
4874
4875 /* flame options */
4876 tfds->burning_rate = fds->burning_rate;
4877 tfds->flame_smoke = fds->flame_smoke;
4878 tfds->flame_vorticity = fds->flame_vorticity;
4879 tfds->flame_ignition = fds->flame_ignition;
4880 tfds->flame_max_temp = fds->flame_max_temp;
4882
4883 /* noise options */
4884 tfds->noise_strength = fds->noise_strength;
4885 tfds->noise_pos_scale = fds->noise_pos_scale;
4886 tfds->noise_time_anim = fds->noise_time_anim;
4887 tfds->noise_scale = fds->noise_scale;
4888
4889 /* liquid domain options */
4890 tfds->flip_ratio = fds->flip_ratio;
4892 tfds->particle_number = fds->particle_number;
4895 tfds->particle_radius = fds->particle_radius;
4901
4902 /* viscosity options */
4903 tfds->viscosity_value = fds->viscosity_value;
4904
4905 /* Diffusion options. */
4906 tfds->surface_tension = fds->surface_tension;
4907 tfds->viscosity_base = fds->viscosity_base;
4909
4910 /* mesh options */
4916 tfds->mesh_scale = fds->mesh_scale;
4917 tfds->mesh_generator = fds->mesh_generator;
4918
4919 /* secondary particle options */
4920 tfds->sndparticle_k_b = fds->sndparticle_k_b;
4921 tfds->sndparticle_k_d = fds->sndparticle_k_d;
4936 tfds->particle_type = fds->particle_type;
4937 tfds->particle_scale = fds->particle_scale;
4938
4939 /* fluid guide options */
4940 tfds->guide_parent = fds->guide_parent;
4941 tfds->guide_alpha = fds->guide_alpha;
4942 tfds->guide_beta = fds->guide_beta;
4944 copy_v3_v3_int(tfds->guide_res, fds->guide_res);
4945 tfds->guide_source = fds->guide_source;
4946
4947 /* cache options */
4949 tfds->cache_frame_end = fds->cache_frame_end;
4956 tfds->cache_flag = fds->cache_flag;
4957 tfds->cache_type = fds->cache_type;
4963
4964 /* time options */
4965 tfds->time_scale = fds->time_scale;
4966 tfds->cfl_condition = fds->cfl_condition;
4969
4970 /* display options */
4972 tfds->slice_axis = fds->slice_axis;
4973 tfds->interp_method = fds->interp_method;
4974 tfds->draw_velocity = fds->draw_velocity;
4975 tfds->slice_per_voxel = fds->slice_per_voxel;
4976 tfds->slice_depth = fds->slice_depth;
4978 tfds->show_gridlines = fds->show_gridlines;
4979 if (fds->coba) {
4980 tfds->coba = static_cast<ColorBand *>(MEM_dupallocN(fds->coba));
4981 }
4982 tfds->vector_scale = fds->vector_scale;
4984 tfds->vector_field = fds->vector_field;
4987 tfds->use_coba = fds->use_coba;
4988 tfds->coba_field = fds->coba_field;
4989 tfds->grid_scale = fds->grid_scale;
4995
4996 /* -- Deprecated / unused options (below)-- */
4997
4998 /* pointcache options */
4999 BKE_ptcache_free_list(&(tfds->ptcaches[0]));
5001 /* Share the cache with the original object's modifier. */
5003 tfds->point_cache[0] = fds->point_cache[0];
5004 tfds->ptcaches[0] = fds->ptcaches[0];
5005 }
5006 else {
5008 &(tfds->ptcaches[0]), &(fds->ptcaches[0]), flag);
5009 }
5010
5011 /* OpenVDB cache options */
5013 tfds->clipping = fds->clipping;
5015
5016 /* Render options. */
5017 tfds->velocity_scale = fds->velocity_scale;
5018 }
5019 else if (tfmd->flow) {
5020 FluidFlowSettings *tffs = tfmd->flow;
5021 FluidFlowSettings *ffs = fmd->flow;
5022
5023 /* NOTE: This is dangerous, as it will generate invalid data in case we are copying between
5024 * different objects. Extra external code has to be called to ensure proper remapping of
5025 * that pointer. See e.g. `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */
5026 tffs->psys = ffs->psys;
5027 tffs->noise_texture = ffs->noise_texture;
5028
5029 /* initial velocity */
5030 tffs->vel_multi = ffs->vel_multi;
5031 tffs->vel_normal = ffs->vel_normal;
5032 tffs->vel_random = ffs->vel_random;
5033 tffs->vel_coord[0] = ffs->vel_coord[0];
5034 tffs->vel_coord[1] = ffs->vel_coord[1];
5035 tffs->vel_coord[2] = ffs->vel_coord[2];
5036
5037 /* emission */
5038 tffs->density = ffs->density;
5039 copy_v3_v3(tffs->color, ffs->color);
5040 tffs->fuel_amount = ffs->fuel_amount;
5041 tffs->temperature = ffs->temperature;
5042 tffs->volume_density = ffs->volume_density;
5044 tffs->particle_size = ffs->particle_size;
5045 tffs->subframes = ffs->subframes;
5046
5047 /* texture control */
5048 tffs->texture_size = ffs->texture_size;
5049 tffs->texture_offset = ffs->texture_offset;
5050 STRNCPY(tffs->uvlayer_name, ffs->uvlayer_name);
5051 tffs->vgroup_density = ffs->vgroup_density;
5052
5053 tffs->type = ffs->type;
5054 tffs->behavior = ffs->behavior;
5055 tffs->source = ffs->source;
5056 tffs->texture_type = ffs->texture_type;
5057 tffs->flags = ffs->flags;
5058 }
5059 else if (tfmd->effector) {
5060 FluidEffectorSettings *tfes = tfmd->effector;
5061 FluidEffectorSettings *fes = fmd->effector;
5062
5064 tfes->type = fes->type;
5065 tfes->flags = fes->flags;
5066 tfes->subframes = fes->subframes;
5067
5068 /* guide options */
5069 tfes->guide_mode = fes->guide_mode;
5070 tfes->vel_multi = fes->vel_multi;
5071 }
5072}
5073
5075{
5076 static int counter = 1;
5077 BLI_snprintf(r_name, maxlen, FLUID_DOMAIN_DIR_DEFAULT "_%x", BLI_hash_int(counter));
5078 counter++;
5079}
5080
void BKE_collision_objects_free(struct Object **objects)
struct Object ** BKE_collision_objects_create(struct Depsgraph *depsgraph, struct Object *self, struct Collection *collection, unsigned int *numcollobj, unsigned int modifier_type)
CustomData interface, see also DNA_customdata_types.h.
support for deformation groups and hooks.
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:774
struct EffectorWeights * BKE_effector_add_weights(struct Collection *collection)
Definition effect.cc:58
void BKE_effectors_free(struct ListBase *lb)
Definition effect.cc:361
void BKE_effectors_apply(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *wind_force, float *impulse)
Definition effect.cc:1106
struct ListBase * BKE_effectors_create(struct Depsgraph *depsgraph, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool use_rotation)
Definition effect.cc:306
void pd_point_from_loc(struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point)
Definition effect.cc:414
void BKE_fluid_cache_free_all(struct FluidDomainSettings *fds, struct Object *ob)
int BKE_fluid_get_data_flags(struct FluidDomainSettings *fds)
bool BKE_fluid_reallocate_fluid(struct FluidDomainSettings *fds, int res[3], int free_old)
void BKE_fluid_particle_system_destroy(struct Object *ob, int particle_type)
struct Mesh * BKE_fluid_modifier_do(struct FluidModifierData *fmd, struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, struct Mesh *mesh)
void BKE_fluid_particle_system_create(struct Main *bmain, struct Object *ob, const char *pset_name, const char *parts_name, const char *psys_name, int psys_type)
float(* BKE_Fluid_BresenhamFn)(float *result, const float *input, int res[3], int *pixel, float *tRay, float correct)
Definition BKE_fluid.h:19
void BKE_fluid_reallocate_copy_fluid(struct FluidDomainSettings *fds, int o_res[3], int n_res[3], const int o_min[3], const int n_min[3], const int o_max[3], int o_shift[3], int n_shift[3])
float BKE_fluid_get_velocity_at(struct Object *ob, float position[3], float velocity[3])
void BKE_fluid_cache_free(struct FluidDomainSettings *fds, struct Object *ob, int cache_map)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
void BKE_id_free(Main *bmain, void *idv)
@ LIB_ID_COPY_SET_COPIED_ON_WRITE
void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
void BKE_mesh_texspace_calc(Mesh *mesh)
void BKE_modifier_path_init(char *path, int path_maxncpy, const char *name)
ModifierData * BKE_modifiers_findby_type(const Object *ob, ModifierType type)
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
const char * BKE_modifier_path_relbase_from_global(Object *ob)
void BKE_modifier_unique_name(ListBase *modifiers, ModifierData *md)
void BKE_modifier_free(ModifierData *md)
void BKE_modifier_remove_from_list(Object *ob, ModifierData *md)
ModifierData * BKE_modifiers_get_virtual_modifierlist(const Object *ob, VirtualModifierData *data)
ModifierData * BKE_modifier_new(int type)
General operations, lookup, etc. for blender objects.
#define OBJECT_MODIFIER_UPDATE_SUBFRAME_RECURSION_DEFAULT
bool BKE_object_moves_in_time(const Object *object, bool recurse_parent)
void BKE_object_modifier_update_subframe(Depsgraph *depsgraph, Scene *scene, Object *ob, bool update_mesh, int parent_recursion_limit, float frame, int modifier_type)
struct ParticleSystemModifierData * psys_get_modifier(struct Object *ob, struct ParticleSystem *psys)
Definition particle.cc:2155
void psys_sim_data_free(struct ParticleSimulationData *sim)
Definition particle.cc:633
void psys_free(struct Object *ob, struct ParticleSystem *psys)
Definition particle.cc:978
struct ParticleSettings * BKE_particlesettings_add(struct Main *bmain, const char *name)
Definition particle.cc:4088
void psys_sim_data_init(struct ParticleSimulationData *sim)
Definition particle.cc:592
bool psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, bool always)
Definition particle.cc:4879
struct PointCache * BKE_ptcache_copy_list(struct ListBase *ptcaches_new, const struct ListBase *ptcaches_old, int flag)
struct PointCache * BKE_ptcache_add(struct ListBase *ptcaches)
void BKE_ptcache_free_list(struct ListBase *ptcaches)
#define PTCACHE_RESET_OUTDATED
int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *pid, int mode)
void BKE_ptcache_id_from_smoke(PTCacheID *pid, struct Object *ob, struct FluidModifierData *fmd)
float BKE_scene_ctime_get(const Scene *scene)
Definition scene.cc:2370
void BKE_texture_get_value(struct Tex *texture, const float *tex_co, struct TexResult *texres, bool use_color_management)
Definition texture.cc:638
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
BLI_INLINE unsigned int BLI_hash_int(unsigned int k)
Definition BLI_hash.h:87
float BLI_bvhtree_bb_raycast(const float bv[6], const float light_start[3], const float light_end[3], float pos[3])
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
MINLINE float pow2f(float x)
MINLINE int max_ii(int a, int b)
MINLINE int min_iii(int a, int b, int c)
void interp_weights_tri_v3(float w[3], const float v1[3], const float v2[3], const float v3[3], const float co[3])
void mul_m4_v3(const float M[4][4], float r[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void add_v3fl_v3fl_v3i(float r[3], const float a[3], const int b[3])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_fl(float r[3], float f)
MINLINE void madd_v3fl_v3fl_v3fl_v3i(float r[3], const float a[3], const float b[3], const int c[3])
MINLINE void clamp_v3(float vec[3], float min, float max)
MINLINE void mul_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], const float w[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void interp_v2_v2v2v2(float r[2], const float a[2], const float b[2], const float c[2], const float t[3])
MINLINE void copy_v3_v3_int(int r[3], const int a[3])
void copy_vn_fl(float *array_tar, int size, float val)
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3_int(int r[3])
MINLINE void negate_v3(float r[3])
MINLINE void sub_v3_v3v3_int(int r[3], const int a[3], const int b[3])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
#define BLI_path_join(...)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned int uint
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
ThreadRWMutex * BLI_rw_mutex_alloc(void)
Definition threads.cc:487
pthread_rwlock_t ThreadRWMutex
#define THREAD_LOCK_WRITE
void BLI_rw_mutex_lock(ThreadRWMutex *mutex, int mode)
Definition threads.cc:467
void BLI_rw_mutex_free(ThreadRWMutex *mutex)
Definition threads.cc:494
void BLI_rw_mutex_unlock(ThreadRWMutex *mutex)
Definition threads.cc:477
#define CLAMP(a, b, c)
#define CLAMP_MAX(a, c)
#define INIT_MINMAX(min, max)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define UNPACK3(a)
#define ELEM(...)
#define CLAMP_MIN(a, b)
float BLI_voxel_sample_trilinear(const float *data, const int res[3], const float co[3])
Definition voxel.cc:44
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
float DEG_get_ctime(const Depsgraph *graph)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
ViewLayer * DEG_get_evaluated_view_layer(const Depsgraph *graph)
#define DNA_struct_default_alloc(struct_name)
@ FLUID_DOMAIN_GUIDE_SRC_DOMAIN
#define FLUID_DOMAIN_DIR_DATA
@ FLUID_FLOW_ABSOLUTE
@ FLUID_FLOW_TEXTUREEMIT
@ FLUID_FLOW_USE_PART_SIZE
@ FLUID_FLOW_NEEDS_UPDATE
@ FLUID_FLOW_USE_PLANE_INIT
@ FLUID_FLOW_INITVELOCITY
@ FLUID_FLOW_USE_INFLOW
#define FLUID_DOMAIN_DIR_PARTICLES
@ FLUID_DOMAIN_BORDER_BOTTOM
@ FLUID_DOMAIN_BORDER_LEFT
@ FLUID_DOMAIN_BORDER_RIGHT
@ FLUID_DOMAIN_BORDER_FRONT
@ FLUID_DOMAIN_BORDER_TOP
@ FLUID_DOMAIN_BORDER_BACK
struct FluidFlowSettings FluidFlowSettings
#define FLUID_DOMAIN_DIR_DEFAULT
@ VDB_COMPRESSION_ZIP
@ FLUID_DOMAIN_FIELD_COLOR_B
@ FLUID_DOMAIN_FIELD_FLAME
@ FLUID_DOMAIN_FIELD_PHI_OUT
@ FLUID_DOMAIN_FIELD_PHI_OBSTACLE
@ FLUID_DOMAIN_FIELD_PHI
@ FLUID_DOMAIN_FIELD_DENSITY
@ FLUID_DOMAIN_FIELD_PHI_IN
@ FLUID_DOMAIN_FIELD_HEAT
@ FLUID_DOMAIN_FIELD_COLOR_G
@ FLUID_DOMAIN_FIELD_FUEL
@ FLUID_DOMAIN_FIELD_COLOR_R
#define FLUID_DOMAIN_DIR_MESH
#define FLUID_DOMAIN_DIR_GUIDE
#define FLUID_DOMAIN_DIR_SCRIPT
@ FLUID_FLOW_TYPE_FIRE
@ FLUID_FLOW_TYPE_SMOKEFIRE
@ FLUID_FLOW_TYPE_LIQUID
@ FLUID_FLOW_TYPE_SMOKE
@ FLUID_DOMAIN_TYPE_GAS
@ FLUID_DOMAIN_TYPE_LIQUID
@ VDB_PRECISION_MINI_FLOAT
@ VDB_PRECISION_HALF_FLOAT
@ FLUID_EFFECTOR_NEEDS_UPDATE
@ FLUID_EFFECTOR_USE_EFFEC
@ FLUID_EFFECTOR_USE_PLANE_INIT
struct FluidEffectorSettings FluidEffectorSettings
@ FLUID_DOMAIN_BAKED_DATA
@ FLUID_DOMAIN_OUTDATED_GUIDE
@ FLUID_DOMAIN_OUTDATED_PARTICLES
@ FLUID_DOMAIN_BAKING_MESH
@ FLUID_DOMAIN_BAKING_NOISE
@ FLUID_DOMAIN_BAKING_GUIDE
@ FLUID_DOMAIN_OUTDATED_NOISE
@ FLUID_DOMAIN_BAKED_NOISE
@ FLUID_DOMAIN_BAKED_MESH
@ FLUID_DOMAIN_OUTDATED_MESH
@ FLUID_DOMAIN_BAKING_DATA
@ FLUID_DOMAIN_BAKED_GUIDE
@ FLUID_DOMAIN_BAKED_PARTICLES
@ FLUID_DOMAIN_OUTDATED_DATA
@ FLUID_DOMAIN_BAKING_PARTICLES
#define FLUID_DOMAIN_DIR_CONFIG
struct FluidDomainSettings FluidDomainSettings
@ FLUID_EFFECTOR_GUIDE_MAX
@ FLUID_EFFECTOR_GUIDE_OVERRIDE
@ FLUID_EFFECTOR_GUIDE_AVERAGED
@ FLUID_EFFECTOR_GUIDE_MIN
@ FLUID_DOMAIN_ACTIVE_COLORS
@ FLUID_DOMAIN_ACTIVE_FIRE
@ FLUID_DOMAIN_ACTIVE_INVEL
@ FLUID_DOMAIN_ACTIVE_GUIDE
@ FLUID_DOMAIN_ACTIVE_OUTFLOW
@ FLUID_DOMAIN_ACTIVE_COLOR_SET
@ FLUID_DOMAIN_ACTIVE_HEAT
@ FLUID_DOMAIN_ACTIVE_OBSTACLE
@ FLUID_DOMAIN_FILE_UNI
#define FLUID_DOMAIN_DIR_NOISE
@ FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN
@ FLUID_DOMAIN_USE_MESH
@ FLUID_DOMAIN_USE_RESUMABLE_CACHE
@ FLUID_DOMAIN_USE_GUIDE
@ FLUID_DOMAIN_EXPORT_MANTA_SCRIPT
@ FLUID_DOMAIN_USE_SPEED_VECTORS
@ FLUID_DOMAIN_USE_NOISE
@ FLUID_DOMAIN_FILE_LOAD
@ FLUID_DOMAIN_CACHE_ALL
@ FLUID_DOMAIN_CACHE_REPLAY
@ FLUID_DOMAIN_CACHE_MODULAR
@ FLUID_FLOW_BEHAVIOR_GEOMETRY
@ FLUID_FLOW_BEHAVIOR_OUTFLOW
@ FLUID_FLOW_BEHAVIOR_INFLOW
@ FLUID_FLOW_TEXTURE_MAP_AUTO
@ FLUID_FLOW_SOURCE_PARTICLES
@ FLUID_FLOW_SOURCE_MESH
@ FLUID_DOMAIN_PARTICLE_SPRAY
@ FLUID_DOMAIN_PARTICLE_FOAM
@ FLUID_DOMAIN_PARTICLE_TRACER
@ FLUID_DOMAIN_PARTICLE_BUBBLE
@ FLUID_EFFECTOR_TYPE_GUIDE
@ FLUID_EFFECTOR_TYPE_COLLISION
@ LA_LOCAL
struct ListBase ListBase
struct MDeformVert MDeformVert
@ eModifierFlag_SharedCaches
@ MOD_FLUID_TYPE_EFFEC
@ MOD_FLUID_TYPE_DOMAIN
@ MOD_FLUID_TYPE_FLOW
@ eModifierType_ParticleSystem
@ eModifierType_Explode
@ eModifierType_Cloth
@ eModifierType_Fluid
@ eModifierType_ShapeKey
@ eModifierType_Ocean
@ eModifierType_Nodes
@ eModifierType_DynamicPaint
@ eModifierType_Softbody
@ OB_WIRE
@ OB_SOLID
@ PFIELD_FLUIDFLOW
Object is a sort of wrapper for general info.
@ OB_LAMP
@ PART_EMITTER
@ PART_FLUID_BUBBLE
@ PART_FLUID
@ PART_FLUID_FOAM
@ PART_HAIR
@ PART_DRAW_COL_VEL
@ PART_DRAW_CIRC
@ PART_DRAW_CROSS
@ PARS_NO_DISP
@ PARS_UNEXIST
@ PART_PHYS_NO
@ PTCACHE_OUTDATED
@ PTCACHE_DISK_CACHE
Types and defines for representing Rigid Body entities.
@ RBO_TYPE_ACTIVE
struct Scene Scene
@ PHYS_GLOBAL_GRAVITY
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
volatile int lock
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
AttributeSet attributes
constexpr IndexRange drop_back(int64_t n) const
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr bool is_empty() const
Definition BLI_span.hh:260
GAttributeReader lookup(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
nullptr float
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
#define expf(x)
KDTree_3d * tree
#define PHI_MAX
Definition fluid.cc:88
#define DT_DEFAULT
Definition fluid.cc:85
void BKE_fluid_cachetype_noise_set(FluidDomainSettings *settings, int cache_noise_format)
Definition fluid.cc:4529
void BKE_fluid_flow_behavior_set(Object *, FluidFlowSettings *settings, int behavior)
Definition fluid.cc:4585
static void fluid_modifier_reset_ex(FluidModifierData *fmd, bool need_lock)
Definition fluid.cc:4720
void BKE_fluid_cachetype_particle_set(FluidDomainSettings *settings, int cache_particle_format)
Definition fluid.cc:4520
static void fluid_modifier_freeDomain(FluidModifierData *fmd)
Definition fluid.cc:4657
static void fluid_modifier_freeFlow(FluidModifierData *fmd)
Definition fluid.cc:4686
void BKE_fluid_domain_type_set(Object *object, FluidDomainSettings *settings, int type)
Definition fluid.cc:4558
void BKE_fluid_particles_set(FluidDomainSettings *settings, int value, bool clear)
Definition fluid.cc:4548
void BKE_fluid_cachetype_data_set(FluidDomainSettings *settings, int cache_data_format)
Definition fluid.cc:4511
static void fluid_modifier_freeEffector(FluidModifierData *fmd)
Definition fluid.cc:4703
void BKE_fluid_collisionextents_set(FluidDomainSettings *settings, int value, bool clear)
Definition fluid.cc:4538
void BKE_fluid_modifier_copy(const FluidModifierData *fmd, FluidModifierData *tfmd, const int flag)
Definition fluid.cc:4831
void BKE_fluid_modifier_create_type_data(FluidModifierData *fmd)
Definition fluid.cc:4775
void BKE_fluid_cache_new_name_for_current_session(int maxlen, char *r_name)
Definition fluid.cc:5074
void BKE_fluid_cache_endframe_set(FluidDomainSettings *settings, int value)
Definition fluid.cc:4496
void BKE_fluid_fields_sanitize(FluidDomainSettings *settings)
Definition fluid.cc:4610
void BKE_fluid_cache_startframe_set(FluidDomainSettings *settings, int value)
Definition fluid.cc:4490
void BKE_fluid_modifier_reset(FluidModifierData *fmd)
Definition fluid.cc:4759
void BKE_fluid_flow_type_set(Object *object, FluidFlowSettings *settings, int type)
Definition fluid.cc:4590
void BKE_fluid_cachetype_mesh_set(FluidDomainSettings *settings, int cache_mesh_format)
Definition fluid.cc:4502
void BKE_fluid_effector_type_set(Object *, FluidEffectorSettings *settings, int type)
Definition fluid.cc:4605
void BKE_fluid_modifier_free(FluidModifierData *fmd)
Definition fluid.cc:4764
uint pos
#define input
#define printf(...)
#define output
#define pow
#define abs
#define floor
#define ceil
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
float * manta_noise_get_density(struct MANTA *smoke)
bool manta_has_particles(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_smoke_get_color_r(struct MANTA *smoke)
float * manta_noise_get_texture_v(struct MANTA *smoke)
float * manta_smoke_get_shadow(struct MANTA *smoke)
float * manta_smoke_get_heat_in(struct MANTA *smoke)
bool manta_has_mesh(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float manta_liquid_get_vertex_y_at(struct MANTA *liquid, int i)
float * manta_noise_get_react(struct MANTA *smoke)
float * manta_get_force_z(struct MANTA *fluid)
bool manta_ensure_invelocity(struct MANTA *fluid, struct FluidModifierData *fmd)
bool manta_read_guiding(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr, bool sourceDomain)
bool manta_has_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
bool manta_bake_particles(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
bool manta_smoke_ensure_colors(struct MANTA *smoke, struct FluidModifierData *fmd)
float * manta_smoke_get_fuel_in(struct MANTA *smoke)
float * manta_noise_get_texture_u(struct MANTA *smoke)
void manta_free(struct MANTA *fluid)
float * manta_get_num_guide(struct MANTA *fluid)
bool manta_smoke_has_heat(struct MANTA *smoke)
int manta_liquid_get_num_triangles(struct MANTA *liquid)
float * manta_get_force_y(struct MANTA *fluid)
bool manta_bake_mesh(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float manta_get_timestep(struct MANTA *fluid)
float * manta_get_guide_velocity_y(struct MANTA *fluid)
float * manta_smoke_get_react(struct MANTA *smoke)
float * manta_get_in_velocity_y(struct MANTA *fluid)
bool manta_bake_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_get_ob_velocity_x(struct MANTA *fluid)
float * manta_get_ob_velocity_z(struct MANTA *fluid)
float * manta_noise_get_texture_w(struct MANTA *smoke)
bool manta_read_mesh(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_get_guide_velocity_z(struct MANTA *fluid)
float * manta_get_in_velocity_x(struct MANTA *fluid)
struct MANTA * manta_init(int *res, struct FluidModifierData *fmd)
float * manta_get_guide_velocity_x(struct MANTA *fluid)
bool manta_liquid_ensure_sndparts(struct MANTA *liquid, struct FluidModifierData *fmd)
float * manta_smoke_get_color_g_in(struct MANTA *smoke)
float * manta_noise_get_fuel(struct MANTA *smoke)
bool manta_write_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_noise_get_texture_v2(struct MANTA *smoke)
float * manta_get_phioutstatic_in(struct MANTA *fluid)
bool manta_smoke_ensure_fire(struct MANTA *smoke, struct FluidModifierData *fmd)
void manta_noise_get_res(struct MANTA *smoke, int *res)
float * manta_get_velocity_y(struct MANTA *fluid)
float manta_liquid_get_vertvel_y_at(struct MANTA *liquid, int i)
float * manta_noise_get_color_b(struct MANTA *smoke)
int manta_liquid_get_num_verts(struct MANTA *liquid)
float * manta_smoke_get_color_b_in(struct MANTA *smoke)
bool manta_ensure_obstacle(struct MANTA *fluid, struct FluidModifierData *fmd)
bool manta_write_config(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
bool manta_needs_realloc(struct MANTA *fluid, struct FluidModifierData *fmd)
float * manta_noise_get_texture_w2(struct MANTA *smoke)
float * manta_get_force_x(struct MANTA *fluid)
bool manta_has_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_get_phiguide_in(struct MANTA *fluid)
float manta_liquid_get_vertvel_z_at(struct MANTA *liquid, int i)
bool manta_read_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr, bool resumable)
size_t manta_get_index(int x, int max_x, int y, int max_y, int z)
float * manta_get_ob_velocity_y(struct MANTA *fluid)
bool manta_read_particles(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr, bool resumable)
bool manta_write_noise(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_smoke_get_density(struct MANTA *smoke)
float * manta_get_phiobsstatic_in(struct MANTA *fluid)
float manta_liquid_get_vertvel_x_at(struct MANTA *liquid, int i)
float * manta_get_in_velocity_z(struct MANTA *fluid)
float * manta_noise_get_color_g(struct MANTA *smoke)
float * manta_smoke_get_color_r_in(struct MANTA *smoke)
bool manta_smoke_ensure_heat(struct MANTA *smoke, struct FluidModifierData *fmd)
float * manta_get_num_obstacle(struct MANTA *fluid)
void manta_update_pointers(struct MANTA *fluid, struct FluidModifierData *fmd, bool flush)
bool manta_ensure_guiding(struct MANTA *fluid, struct FluidModifierData *fmd)
float * manta_get_phi_in(struct MANTA *fluid)
bool manta_read_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr, bool resumable)
float * manta_smoke_get_react_in(struct MANTA *smoke)
float * manta_get_velocity_z(struct MANTA *fluid)
bool manta_has_guiding(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr, bool domain)
bool manta_liquid_export_script(struct MANTA *liquid, struct FluidModifierData *fmd)
bool manta_smoke_has_colors(struct MANTA *smoke)
bool manta_read_config(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_smoke_get_density_in(struct MANTA *smoke)
float manta_liquid_get_vertex_z_at(struct MANTA *liquid, int i)
float * manta_noise_get_texture_u2(struct MANTA *smoke)
float * manta_get_phiout_in(struct MANTA *fluid)
float * manta_get_phiobs_in(struct MANTA *fluid)
int * manta_smoke_get_flags(struct MANTA *smoke)
float * manta_get_velocity_x(struct MANTA *fluid)
int manta_liquid_get_triangle_z_at(struct MANTA *liquid, int i)
bool manta_smoke_has_fuel(struct MANTA *smoke)
float * manta_smoke_get_emission_in(struct MANTA *smoke)
float manta_liquid_get_vertex_x_at(struct MANTA *liquid, int i)
float * manta_smoke_get_color_g(struct MANTA *smoke)
bool manta_ensure_outflow(struct MANTA *fluid, struct FluidModifierData *fmd)
void manta_update_variables(struct MANTA *fluid, struct FluidModifierData *fmd)
float * manta_smoke_get_heat(struct MANTA *smoke)
int manta_liquid_get_triangle_x_at(struct MANTA *liquid, int i)
float * manta_get_phistatic_in(struct MANTA *fluid)
float * manta_smoke_get_color_b(struct MANTA *smoke)
void manta_adapt_timestep(struct MANTA *fluid)
int manta_liquid_get_triangle_y_at(struct MANTA *liquid, int i)
bool manta_bake_data(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
float * manta_smoke_get_fuel(struct MANTA *smoke)
bool manta_smoke_export_script(struct MANTA *smoke, struct FluidModifierData *fmd)
float * manta_smoke_get_flame(struct MANTA *smoke)
float * manta_noise_get_flame(struct MANTA *smoke)
float * manta_noise_get_color_r(struct MANTA *smoke)
bool manta_bake_guiding(struct MANTA *fluid, struct FluidModifierData *fmd, int framenr)
static ulong state[N]
#define G(x, y, z)
static void clear(Message &msg)
Definition msgfmt.cc:213
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
T abs(const T &a)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
bool is_adaptive(const CpuPatchTable *patch_table)
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
std::mutex Mutex
Definition BLI_mutex.hh:47
VecBase< float, 3 > float3
static void update_velocities(PTCacheEdit *edit)
#define floorf
#define fabsf
#define sqrtf
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
struct ListBase ptcaches[2]
struct FluidModifierData * fmd
struct Collection * force_group
struct Collection * effector_group
struct PointCache * point_cache[2]
struct ColorBand * coba
struct Object * guide_parent
struct Collection * fluid_group
struct EffectorWeights * effector_weights
struct FluidModifierData * fmd
struct FluidModifierData * fmd
struct ParticleSystem * psys
struct Tex * noise_texture
struct FluidDomainSettings * domain
struct FluidEffectorSettings * effector
struct FluidFlowSettings * flow
short type
void * first
int verts_num
struct ModifierData * next
ListBase particlesystem
ObjectRuntimeHandle * runtime
ListBase modifiers
struct RigidBodyOb * rigidbody_object
float scale[3]
struct PointCache * cache
struct Depsgraph * depsgraph
struct Scene * scene
struct ParticleSystem * psys
struct Object * ob
struct ParticleSystem * psys
ChildParticle * child
struct ListBase ptcaches
ParticleData * particles
ParticleSettings * part
struct ParticleSystem * next
struct PointCache * pointcache
struct PhysicsSettings physics_settings
struct RenderData r
BVHTree_NearestPointCallback nearest_callback
BVHTree_RayCastCallback raycast_callback
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
uint8_t flag
Definition wm_window.cc:145