Blender V4.5
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 = {"bke.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 / FPS) * 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,
1802 const float (*mloopuv)[2],
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 (mloopuv) {
1924 const float *uv[3];
1925 uv[0] = mloopuv[corner_tris[tri_i][0]];
1926 uv[1] = mloopuv[corner_tris[tri_i][1]];
1927 uv[2] = mloopuv[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 const float (*mloopuv)[2];
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->mloopuv,
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 float(*mloopuv)[2] = static_cast<const float(*)[2]>(
2087
2088 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
2089 vert_vel = MEM_calloc_arrayN<float>(3 * size_t(numverts), "manta_flow_velocity");
2090
2091 if (ffs->numverts != numverts || !ffs->verts_old) {
2092 if (ffs->verts_old) {
2093 MEM_freeN(ffs->verts_old);
2094 }
2095 ffs->verts_old = MEM_calloc_arrayN<float>(3 * size_t(numverts), "manta_flow_verts_old");
2096 ffs->numverts = numverts;
2097 }
2098 else {
2099 has_velocity = true;
2100 }
2101 }
2102
2103 /* Transform mesh vertices to domain grid space for fast lookups.
2104 * This is valid because the mesh is copied above. */
2105 for (i = 0; i < numverts; i++) {
2106 /* Vertex position. */
2107 mul_m4_v3(flow_ob->object_to_world().ptr(), positions[i]);
2108 manta_pos_to_cell(fds, positions[i]);
2109
2110 /* Vertex velocity. */
2111 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
2112 float co[3];
2113 add_v3fl_v3fl_v3i(co, positions[i], fds->shift);
2114 if (has_velocity) {
2115 sub_v3_v3v3(&vert_vel[i * 3], co, &ffs->verts_old[i * 3]);
2116 mul_v3_fl(&vert_vel[i * 3], 1.0 / dt);
2117 }
2118 copy_v3_v3(&ffs->verts_old[i * 3], co);
2119 }
2120
2121 /* Calculate emission map bounds. */
2122 bb_boundInsert(bb, positions[i]);
2123 }
2124 mesh->tag_positions_changed();
2125 mul_m4_v3(flow_ob->object_to_world().ptr(), flow_center);
2126 manta_pos_to_cell(fds, flow_center);
2127
2128 /* Set emission map.
2129 * Use 3 cell diagonals as margin (3 * 1.732 = 5.196). */
2130 int bounds_margin = int(ceil(5.196));
2131 clamp_bounds_in_domain(fds, bb->min, bb->max, nullptr, nullptr, bounds_margin, dt);
2132 bb_allocateData(bb, ffs->flags & FLUID_FLOW_INITVELOCITY, true);
2133
2134 /* Setup loop bounds. */
2135 for (i = 0; i < 3; i++) {
2136 min[i] = bb->min[i];
2137 max[i] = bb->max[i];
2138 res[i] = bb->res[i];
2139 }
2140
2141 /* Skip flow sampling loop if object has disabled flow. */
2142 bool use_flow = ffs->flags & FLUID_FLOW_USE_INFLOW;
2143 blender::bke::BVHTreeFromMesh tree_data = mesh->bvh_corner_tris();
2144 if (use_flow && tree_data.tree != nullptr) {
2145
2146 EmitFromDMData data{};
2147 data.fds = fds;
2148 data.ffs = ffs;
2149 data.vert_positions = positions;
2150 data.vert_normals = mesh->vert_normals();
2151 data.corner_verts = corner_verts;
2152 data.corner_tris = corner_tris;
2153 data.mloopuv = mloopuv;
2154 data.dvert = dvert;
2155 data.defgrp_index = defgrp_index;
2156 data.tree = &tree_data;
2157 data.bb = bb;
2158 data.has_velocity = has_velocity;
2159 data.vert_vel = vert_vel;
2160 data.flow_center = flow_center;
2161 data.min = min;
2162 data.max = max;
2163 data.res = res;
2164
2165 TaskParallelSettings settings;
2167 settings.min_iter_per_thread = 2;
2168 BLI_task_parallel_range(min[2], max[2], &data, emit_from_mesh_task_cb, &settings);
2169 }
2170
2171 if (vert_vel) {
2172 MEM_freeN(vert_vel);
2173 }
2174 BKE_id_free(nullptr, mesh);
2175 }
2176}
2177
2179
2180/* -------------------------------------------------------------------- */
2183
2184static void adaptive_domain_adjust(
2185 FluidDomainSettings *fds, Object *ob, FluidObjectBB *bb_maps, uint numflowobj, float dt)
2186{
2187 /* calculate domain shift for current frame */
2188 int new_shift[3] = {0};
2189 int total_shift[3];
2190 float frame_shift_f[3];
2191 float ob_loc[3] = {0};
2192
2193 mul_m4_v3(ob->object_to_world().ptr(), ob_loc);
2194
2195 sub_v3_v3v3(frame_shift_f, ob_loc, fds->prev_loc);
2196 copy_v3_v3(fds->prev_loc, ob_loc);
2197 /* convert global space shift to local "cell" space */
2198 mul_mat3_m4_v3(fds->imat, frame_shift_f);
2199 frame_shift_f[0] = frame_shift_f[0] / fds->cell_size[0];
2200 frame_shift_f[1] = frame_shift_f[1] / fds->cell_size[1];
2201 frame_shift_f[2] = frame_shift_f[2] / fds->cell_size[2];
2202 /* add to total shift */
2203 add_v3_v3(fds->shift_f, frame_shift_f);
2204 /* convert to integer */
2205 total_shift[0] = int(floorf(fds->shift_f[0]));
2206 total_shift[1] = int(floorf(fds->shift_f[1]));
2207 total_shift[2] = int(floorf(fds->shift_f[2]));
2208 int temp_shift[3];
2209 copy_v3_v3_int(temp_shift, fds->shift);
2210 sub_v3_v3v3_int(new_shift, total_shift, fds->shift);
2211 copy_v3_v3_int(fds->shift, total_shift);
2212
2213 /* calculate new domain boundary points so that smoke doesn't slide on sub-cell movement */
2214 fds->p0[0] = fds->dp0[0] - fds->cell_size[0] * (fds->shift_f[0] - total_shift[0] - 0.5f);
2215 fds->p0[1] = fds->dp0[1] - fds->cell_size[1] * (fds->shift_f[1] - total_shift[1] - 0.5f);
2216 fds->p0[2] = fds->dp0[2] - fds->cell_size[2] * (fds->shift_f[2] - total_shift[2] - 0.5f);
2217 fds->p1[0] = fds->p0[0] + fds->cell_size[0] * fds->base_res[0];
2218 fds->p1[1] = fds->p0[1] + fds->cell_size[1] * fds->base_res[1];
2219 fds->p1[2] = fds->p0[2] + fds->cell_size[2] * fds->base_res[2];
2220
2221 /* adjust domain resolution */
2222 const int block_size = fds->noise_scale;
2223 int min[3] = {32767, 32767, 32767}, max[3] = {-32767, -32767, -32767}, res[3];
2224 int total_cells = 1, res_changed = 0, shift_changed = 0;
2225 float min_vel[3], max_vel[3];
2226 int x, y, z;
2227 float *density = manta_smoke_get_density(fds->fluid);
2228 float *fuel = manta_smoke_get_fuel(fds->fluid);
2229 float *bigdensity = manta_noise_get_density(fds->fluid);
2230 float *bigfuel = manta_noise_get_fuel(fds->fluid);
2231 float *vx = manta_get_velocity_x(fds->fluid);
2232 float *vy = manta_get_velocity_y(fds->fluid);
2233 float *vz = manta_get_velocity_z(fds->fluid);
2234 int wt_res[3];
2235
2236 if (fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
2237 manta_noise_get_res(fds->fluid, wt_res);
2238 }
2239
2240 INIT_MINMAX(min_vel, max_vel);
2241
2242 /* Calculate bounds for current domain content */
2243 for (x = fds->res_min[0]; x < fds->res_max[0]; x++) {
2244 for (y = fds->res_min[1]; y < fds->res_max[1]; y++) {
2245 for (z = fds->res_min[2]; z < fds->res_max[2]; z++) {
2246 int xn = x - new_shift[0];
2247 int yn = y - new_shift[1];
2248 int zn = z - new_shift[2];
2249 int index;
2250 float max_den;
2251
2252 /* skip if cell already belongs to new area */
2253 if (xn >= min[0] && xn <= max[0] && yn >= min[1] && yn <= max[1] && zn >= min[2] &&
2254 zn <= max[2])
2255 {
2256 continue;
2257 }
2258
2259 index = manta_get_index(x - fds->res_min[0],
2260 fds->res[0],
2261 y - fds->res_min[1],
2262 fds->res[1],
2263 z - fds->res_min[2]);
2264 max_den = (fuel) ? std::max(density[index], fuel[index]) : density[index];
2265
2266 /* Check high resolution bounds if max density isn't already high enough. */
2267 if (max_den < fds->adapt_threshold && fds->flags & FLUID_DOMAIN_USE_NOISE && fds->fluid) {
2268 int i, j, k;
2269 /* high res grid index */
2270 int xx = (x - fds->res_min[0]) * block_size;
2271 int yy = (y - fds->res_min[1]) * block_size;
2272 int zz = (z - fds->res_min[2]) * block_size;
2273
2274 for (i = 0; i < block_size; i++) {
2275 for (j = 0; j < block_size; j++) {
2276 for (k = 0; k < block_size; k++) {
2277 int big_index = manta_get_index(xx + i, wt_res[0], yy + j, wt_res[1], zz + k);
2278 float den = (bigfuel) ? std::max(bigdensity[big_index], bigfuel[big_index]) :
2279 bigdensity[big_index];
2280 max_den = std::max(den, max_den);
2281 }
2282 }
2283 }
2284 }
2285
2286 /* content bounds (use shifted coordinates) */
2287 if (max_den >= fds->adapt_threshold) {
2288 min[0] = std::min(min[0], xn);
2289 min[1] = std::min(min[1], yn);
2290 min[2] = std::min(min[2], zn);
2291 max[0] = std::max(max[0], xn);
2292 max[1] = std::max(max[1], yn);
2293 max[2] = std::max(max[2], zn);
2294 }
2295
2296 /* velocity bounds */
2297 min_vel[0] = std::min(min_vel[0], vx[index]);
2298 min_vel[1] = std::min(min_vel[1], vy[index]);
2299 min_vel[2] = std::min(min_vel[2], vz[index]);
2300 max_vel[0] = std::max(max_vel[0], vx[index]);
2301 max_vel[1] = std::max(max_vel[1], vy[index]);
2302 max_vel[2] = std::max(max_vel[2], vz[index]);
2303 }
2304 }
2305 }
2306
2307 /* also apply emission maps */
2308 for (int i = 0; i < numflowobj; i++) {
2309 FluidObjectBB *bb = &bb_maps[i];
2310
2311 for (x = bb->min[0]; x < bb->max[0]; x++) {
2312 for (y = bb->min[1]; y < bb->max[1]; y++) {
2313 for (z = bb->min[2]; z < bb->max[2]; z++) {
2314 int index = manta_get_index(
2315 x - bb->min[0], bb->res[0], y - bb->min[1], bb->res[1], z - bb->min[2]);
2316 float max_den = bb->influence[index];
2317
2318 /* density bounds */
2319 if (max_den >= fds->adapt_threshold) {
2320 min[0] = std::min(min[0], x);
2321 min[1] = std::min(min[1], y);
2322 min[2] = std::min(min[2], z);
2323 max[0] = std::max(max[0], x);
2324 max[1] = std::max(max[1], y);
2325 max[2] = std::max(max[2], z);
2326 }
2327 }
2328 }
2329 }
2330 }
2331
2332 /* calculate new bounds based on these values */
2333 clamp_bounds_in_domain(fds, min, max, min_vel, max_vel, fds->adapt_margin + 1, dt);
2334
2335 for (int i = 0; i < 3; i++) {
2336 /* calculate new resolution */
2337 res[i] = max[i] - min[i];
2338 total_cells *= res[i];
2339
2340 if (new_shift[i]) {
2341 shift_changed = 1;
2342 }
2343
2344 /* if no content set minimum dimensions */
2345 if (res[i] <= 0) {
2346 int j;
2347 for (j = 0; j < 3; j++) {
2348 min[j] = 0;
2349 max[j] = 1;
2350 res[j] = 1;
2351 }
2352 res_changed = 1;
2353 total_cells = 1;
2354 break;
2355 }
2356 if (min[i] != fds->res_min[i] || max[i] != fds->res_max[i]) {
2357 res_changed = 1;
2358 }
2359 }
2360
2361 if (res_changed || shift_changed) {
2363 fds, fds->res, res, fds->res_min, min, fds->res_max, temp_shift, total_shift);
2364
2365 /* set new domain dimensions */
2366 copy_v3_v3_int(fds->res_min, min);
2367 copy_v3_v3_int(fds->res_max, max);
2368 copy_v3_v3_int(fds->res, res);
2369 fds->total_cells = total_cells;
2370
2371 /* Redo adapt time step in manta to refresh solver state (ie time variables) */
2373 }
2374}
2375
2376BLI_INLINE void apply_outflow_fields(int index,
2377 float distance_value,
2378 float *density,
2379 float *heat,
2380 float *fuel,
2381 float *react,
2382 float *color_r,
2383 float *color_g,
2384 float *color_b,
2385 float *phiout)
2386{
2387 /* Set levelset value for liquid inflow.
2388 * Ensure that distance value is "joined" into the levelset. */
2389 if (phiout) {
2390 phiout[index] = std::min(distance_value, phiout[index]);
2391 }
2392
2393 /* Set smoke outflow, i.e. reset cell to zero. */
2394 if (density) {
2395 density[index] = 0.0f;
2396 }
2397 if (heat) {
2398 heat[index] = 0.0f;
2399 }
2400 if (fuel) {
2401 fuel[index] = 0.0f;
2402 react[index] = 0.0f;
2403 }
2404 if (color_r) {
2405 color_r[index] = 0.0f;
2406 color_g[index] = 0.0f;
2407 color_b[index] = 0.0f;
2408 }
2409}
2410
2411BLI_INLINE void apply_inflow_fields(FluidFlowSettings *ffs,
2412 float emission_value,
2413 float distance_value,
2414 int index,
2415 float *density_in,
2416 const float *density,
2417 float *heat_in,
2418 const float *heat,
2419 float *fuel_in,
2420 const float *fuel,
2421 float *react_in,
2422 const float *react,
2423 float *color_r_in,
2424 const float *color_r,
2425 float *color_g_in,
2426 const float *color_g,
2427 float *color_b_in,
2428 const float *color_b,
2429 float *phi_in,
2430 float *emission_in)
2431{
2432 /* Set levelset value for liquid inflow.
2433 * Ensure that distance value is "joined" into the levelset. */
2434 if (phi_in) {
2435 phi_in[index] = std::min(distance_value, phi_in[index]);
2436 }
2437
2438 /* Set emission value for smoke inflow.
2439 * Ensure that emission value is "maximized". */
2440 if (emission_in) {
2441 emission_in[index] = std::max(emission_value, emission_in[index]);
2442 }
2443
2444 /* Set inflow for smoke from here on. */
2445 int absolute_flow = (ffs->flags & FLUID_FLOW_ABSOLUTE);
2446 float dens_old = (density) ? density[index] : 0.0;
2447 // float fuel_old = (fuel) ? fuel[index] : 0.0f; /* UNUSED */
2448 float dens_flow = (ffs->type == FLUID_FLOW_TYPE_FIRE) ? 0.0f : emission_value * ffs->density;
2449 float fuel_flow = (fuel) ? emission_value * ffs->fuel_amount : 0.0f;
2450 /* Set heat inflow. */
2451 if (heat && heat_in) {
2452 if (emission_value > 0.0f) {
2453 heat_in[index] = ADD_IF_LOWER(heat[index], ffs->temperature);
2454 }
2455 }
2456
2457 /* Set density and fuel - absolute mode. */
2458 if (absolute_flow) {
2459 if (density && density_in) {
2460 if (ffs->type != FLUID_FLOW_TYPE_FIRE && dens_flow > density[index]) {
2461 /* Use std::max to preserve values from other emitters at this cell. */
2462 density_in[index] = std::max(dens_flow, density_in[index]);
2463 }
2464 }
2465 if (fuel && fuel_in) {
2466 if (ffs->type != FLUID_FLOW_TYPE_SMOKE && fuel_flow && fuel_flow > fuel[index]) {
2467 /* Use std::max to preserve values from other emitters at this cell. */
2468 fuel_in[index] = std::max(fuel_flow, fuel_in[index]);
2469 }
2470 }
2471 }
2472 /* Set density and fuel - additive mode. */
2473 else {
2474 if (density && density_in) {
2475 if (ffs->type != FLUID_FLOW_TYPE_FIRE) {
2476 density_in[index] += dens_flow;
2477 CLAMP(density_in[index], 0.0f, 1.0f);
2478 }
2479 }
2480 if (fuel && fuel_in) {
2481 if (ffs->type != FLUID_FLOW_TYPE_SMOKE && ffs->fuel_amount) {
2482 fuel_in[index] += fuel_flow;
2483 CLAMP(fuel_in[index], 0.0f, 10.0f);
2484 }
2485 }
2486 }
2487
2488 /* Set color. */
2489 if (color_r && color_r_in) {
2490 if (dens_flow) {
2491 float total_dens = density[index] / (dens_old + dens_flow);
2492 color_r_in[index] = (color_r[index] + ffs->color[0] * dens_flow) * total_dens;
2493 color_g_in[index] = (color_g[index] + ffs->color[1] * dens_flow) * total_dens;
2494 color_b_in[index] = (color_b[index] + ffs->color[2] * dens_flow) * total_dens;
2495 }
2496 }
2497
2498 /* Set fire reaction coordinate. */
2499 if (fuel && fuel_in) {
2500 /* Instead of using 1.0 for all new fuel add slight falloff to reduce flow blocky-ness. */
2501 float value = 1.0f - pow2f(1.0f - emission_value);
2502
2503 if (fuel_in[index] > FLT_EPSILON && value > react[index]) {
2504 float f = fuel_flow / fuel_in[index];
2505 react_in[index] = value * f + (1.0f - f) * react[index];
2506 CLAMP(react_in[index], 0.0f, value);
2507 }
2508 }
2509}
2510
2511static void ensure_flowsfields(FluidDomainSettings *fds)
2512{
2514 manta_ensure_invelocity(fds->fluid, fds->fmd);
2515 }
2517 manta_ensure_outflow(fds->fluid, fds->fmd);
2518 }
2520 manta_smoke_ensure_heat(fds->fluid, fds->fmd);
2521 }
2523 manta_smoke_ensure_fire(fds->fluid, fds->fmd);
2524 }
2526 /* Initialize all smoke with "active_color". */
2528 }
2532 {
2534 }
2535 manta_update_pointers(fds->fluid, fds->fmd, false);
2536}
2537
2538static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int numflowobj)
2539{
2540 int active_fields = fds->active_fields;
2541 uint flow_index;
2542
2543 /* First, remove all flags that we want to update. */
2546 active_fields &= ~prev_flags;
2547
2548 /* Monitor active fields based on flow settings. */
2549 for (flow_index = 0; flow_index < numflowobj; flow_index++) {
2550 Object *flow_ob = flowobjs[flow_index];
2553
2554 /* Sanity check. */
2555 if (!fmd2) {
2556 continue;
2557 }
2558
2559 /* Activate specific grids if at least one flow object requires this grid. */
2560 if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
2561 FluidFlowSettings *ffs = fmd2->flow;
2562 if (!ffs) {
2563 break;
2564 }
2565 if (ffs->flags & FLUID_FLOW_NEEDS_UPDATE) {
2568 }
2569 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
2570 active_fields |= FLUID_DOMAIN_ACTIVE_INVEL;
2571 }
2573 active_fields |= FLUID_DOMAIN_ACTIVE_OUTFLOW;
2574 }
2575 /* liquids done from here */
2576 if (fds->type == FLUID_DOMAIN_TYPE_LIQUID) {
2577 continue;
2578 }
2579
2580 /* Activate heat field if a flow object produces any heat. */
2581 if (ffs->temperature != 0.0) {
2582 active_fields |= FLUID_DOMAIN_ACTIVE_HEAT;
2583 }
2584 /* Activate fuel field if a flow object is of fire type. */
2585 if (ffs->fuel_amount != 0.0 || ffs->type == FLUID_FLOW_TYPE_FIRE ||
2587 {
2588 active_fields |= FLUID_DOMAIN_ACTIVE_FIRE;
2589 }
2590 /* Activate color field if flows add smoke with varying colors. */
2592 {
2593 if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) {
2594 copy_v3_v3(fds->active_color, ffs->color);
2595 active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET;
2596 }
2597 else if (!equals_v3v3(fds->active_color, ffs->color)) {
2598 copy_v3_v3(fds->active_color, ffs->color);
2599 active_fields |= FLUID_DOMAIN_ACTIVE_COLORS;
2600 }
2601 }
2602 }
2603 }
2604 /* Monitor active fields based on domain settings. */
2605 if (fds->type == FLUID_DOMAIN_TYPE_GAS && active_fields & FLUID_DOMAIN_ACTIVE_FIRE) {
2606 /* Heat is always needed for fire. */
2607 active_fields |= FLUID_DOMAIN_ACTIVE_HEAT;
2608 /* Also activate colors if domain smoke color differs from active color. */
2609 if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) {
2611 active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET;
2612 }
2613 else if (!equals_v3v3(fds->active_color, fds->flame_smoke_color)) {
2615 active_fields |= FLUID_DOMAIN_ACTIVE_COLORS;
2616 }
2617 }
2618 fds->active_fields = active_fields;
2619}
2620
2621static bool escape_flowsobject(Object *flowobj,
2623 FluidFlowSettings *ffs,
2624 int frame)
2625{
2626 bool use_velocity = (ffs->flags & FLUID_FLOW_INITVELOCITY);
2627 bool is_static = is_static_object(flowobj);
2628
2629 bool liquid_flow = ffs->type == FLUID_FLOW_TYPE_LIQUID;
2630 bool gas_flow = ELEM(
2632 bool is_geometry = (ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
2633
2634 bool liquid_domain = fds->type == FLUID_DOMAIN_TYPE_LIQUID;
2635 bool gas_domain = fds->type == FLUID_DOMAIN_TYPE_GAS;
2637 bool is_resume = (fds->cache_frame_pause_data == frame);
2638 bool is_first_frame = (fds->cache_frame_start == frame);
2639
2640 /* Cannot use static mode with adaptive domain.
2641 * The adaptive domain might expand and only later discover the static object. */
2642 if (is_adaptive) {
2643 is_static = false;
2644 }
2645 /* No need to compute emission value if it won't be applied. */
2646 if (liquid_flow && is_geometry && !is_first_frame) {
2647 return true;
2648 }
2649 /* Skip flow object if it does not "belong" to this domain type. */
2650 if ((liquid_flow && gas_domain) || (gas_flow && liquid_domain)) {
2651 return true;
2652 }
2653 /* Optimization: Static liquid flow objects don't need emission after first frame.
2654 * TODO(sebbas): Also do not use static mode if initial velocities are enabled. */
2655 if (liquid_flow && is_static && !is_first_frame && !is_resume && !use_velocity) {
2656 return true;
2657 }
2658 return false;
2659}
2660
2661static void compute_flowsemission(Scene *scene,
2662 FluidObjectBB *bb_maps,
2663 Depsgraph *depsgraph,
2664 float dt,
2665 Object **flowobjs,
2666 int frame,
2667 float frame_length,
2669 uint numflowobjs,
2670 float time_per_frame)
2671{
2672 bool is_first_frame = (frame == fds->cache_frame_start);
2673
2674 /* Prepare flow emission maps. */
2675 for (int flow_index = 0; flow_index < numflowobjs; flow_index++) {
2676 Object *flowobj = flowobjs[flow_index];
2679
2680 /* Sanity check. */
2681 if (!fmd2) {
2682 continue;
2683 }
2684
2685 /* Check for initialized flow object. */
2686 if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
2687 FluidFlowSettings *ffs = fmd2->flow;
2688 int subframes = ffs->subframes;
2689 FluidObjectBB *bb = &bb_maps[flow_index];
2690
2691 /* Optimization: Skip this object under certain conditions. */
2692 if (escape_flowsobject(flowobj, fds, ffs, frame)) {
2693 continue;
2694 }
2695
2696 /* First frame cannot have any subframes because there is (obviously) no previous frame from
2697 * where subframes could come from. */
2698 if (is_first_frame) {
2699 subframes = 0;
2700 }
2701
2702 /* More splitting because of emission subframe: If no subframes present, sample_size is 1. */
2703 float sample_size = 1.0f / float(subframes + 1);
2704 float subframe_dt = dt * sample_size;
2705
2706 /* Emission loop. When not using subframes this will loop only once. */
2707 for (int subframe = 0; subframe <= subframes; subframe++) {
2708 /* Temporary emission map used when subframes are enabled, i.e. at least one subframe. */
2709 FluidObjectBB bb_temp = {nullptr};
2710
2711 /* Set scene time */
2712 if ((subframe < subframes || time_per_frame + dt + FLT_EPSILON < frame_length) &&
2713 !is_first_frame)
2714 {
2715 scene->r.subframe = (time_per_frame + (subframe + 1.0f) * subframe_dt) / frame_length;
2716 scene->r.cfra = frame - 1;
2717 }
2718 else {
2719 scene->r.subframe = 0.0f;
2720 scene->r.cfra = frame;
2721 }
2722
2723 /* Sanity check: subframe portion must be between 0 and 1. */
2724 CLAMP(scene->r.subframe, 0.0f, 1.0f);
2725# ifdef DEBUG_PRINT
2726 /* Debugging: Print subframe information. */
2727 printf(
2728 "flow: frame (is first: %d): %d // scene current frame: %d // scene current subframe: "
2729 "%f\n",
2730 is_first_frame,
2731 frame,
2732 scene->r.cfra,
2733 scene->r.subframe);
2734# endif
2735 /* Update frame time, this is considering current subframe fraction
2736 * BLI_mutex_lock() called in manta_step(), so safe to update subframe here
2737 * TODO(sebbas): Using BKE_scene_ctime_get(scene) instead of new DEG_get_ctime(depsgraph)
2738 * as subframes don't work with the latter yet. */
2740 scene,
2741 flowobj,
2742 true,
2744 BKE_scene_ctime_get(scene),
2746
2747 /* Emission from particles. */
2748 if (ffs->source == FLUID_FLOW_SOURCE_PARTICLES) {
2749 if (subframes) {
2750 emit_from_particles(flowobj, fds, ffs, &bb_temp, depsgraph, scene, subframe_dt);
2751 }
2752 else {
2753 emit_from_particles(flowobj, fds, ffs, bb, depsgraph, scene, subframe_dt);
2754 }
2755 }
2756 /* Emission from mesh. */
2757 else if (ffs->source == FLUID_FLOW_SOURCE_MESH) {
2758 if (subframes) {
2759 emit_from_mesh(flowobj, fds, ffs, &bb_temp, subframe_dt);
2760 }
2761 else {
2762 emit_from_mesh(flowobj, fds, ffs, bb, subframe_dt);
2763 }
2764 }
2765 else {
2766 printf("Error: unknown flow emission source\n");
2767 }
2768
2769 /* If this we emitted with temp emission map in this loop (subframe emission), we combine
2770 * the temp map with the original emission map. */
2771 if (subframes) {
2772 /* Combine emission maps. */
2773 bb_combineMaps(bb, &bb_temp, !(ffs->flags & FLUID_FLOW_ABSOLUTE), sample_size);
2774 bb_freeData(&bb_temp);
2775 }
2776 }
2777 }
2778 }
2779# ifdef DEBUG_PRINT
2780 /* Debugging: Print time information. */
2781 printf("flow: frame: %d // time per frame: %f // frame length: %f // dt: %f\n",
2782 frame,
2783 time_per_frame,
2784 frame_length,
2785 dt);
2786# endif
2787}
2788
2789static void update_flowsfluids(Depsgraph *depsgraph,
2790 Scene *scene,
2791 Object *ob,
2793 float time_per_frame,
2794 float frame_length,
2795 int frame,
2796 float dt)
2797{
2798 FluidObjectBB *bb_maps = nullptr;
2799 Object **flowobjs = nullptr;
2800 uint numflowobjs = 0;
2801 bool is_resume = (fds->cache_frame_pause_data == frame);
2802 bool is_first_frame = (fds->cache_frame_start == frame);
2803
2805 depsgraph, ob, fds->fluid_group, &numflowobjs, eModifierType_Fluid);
2806
2807 /* Update all flow related flags and ensure that corresponding grids get initialized. */
2808 update_flowsflags(fds, flowobjs, numflowobjs);
2809 ensure_flowsfields(fds);
2810
2811 /* Allocate emission map for each flow object. */
2812 bb_maps = MEM_calloc_arrayN<FluidObjectBB>(numflowobjs, "fluid_flow_bb_maps");
2813
2814 /* Initialize emission map for each flow object. */
2815 compute_flowsemission(scene,
2816 bb_maps,
2817 depsgraph,
2818 dt,
2819 flowobjs,
2820 frame,
2821 frame_length,
2822 fds,
2823 numflowobjs,
2824 time_per_frame);
2825
2826 /* Adjust domain size if needed. Only do this once for every frame. */
2828 adaptive_domain_adjust(fds, ob, bb_maps, numflowobjs, dt);
2829 }
2830
2831 float *phi_in = manta_get_phi_in(fds->fluid);
2832 float *phistatic_in = manta_get_phistatic_in(fds->fluid);
2833 float *phiout_in = manta_get_phiout_in(fds->fluid);
2834 float *phioutstatic_in = manta_get_phioutstatic_in(fds->fluid);
2835
2836 float *density = manta_smoke_get_density(fds->fluid);
2837 float *color_r = manta_smoke_get_color_r(fds->fluid);
2838 float *color_g = manta_smoke_get_color_g(fds->fluid);
2839 float *color_b = manta_smoke_get_color_b(fds->fluid);
2840 float *fuel = manta_smoke_get_fuel(fds->fluid);
2841 float *heat = manta_smoke_get_heat(fds->fluid);
2842 float *react = manta_smoke_get_react(fds->fluid);
2843
2844 float *density_in = manta_smoke_get_density_in(fds->fluid);
2845 float *heat_in = manta_smoke_get_heat_in(fds->fluid);
2846 float *color_r_in = manta_smoke_get_color_r_in(fds->fluid);
2847 float *color_g_in = manta_smoke_get_color_g_in(fds->fluid);
2848 float *color_b_in = manta_smoke_get_color_b_in(fds->fluid);
2849 float *fuel_in = manta_smoke_get_fuel_in(fds->fluid);
2850 float *react_in = manta_smoke_get_react_in(fds->fluid);
2851 float *emission_in = manta_smoke_get_emission_in(fds->fluid);
2852
2853 float *velx_initial = manta_get_in_velocity_x(fds->fluid);
2854 float *vely_initial = manta_get_in_velocity_y(fds->fluid);
2855 float *velz_initial = manta_get_in_velocity_z(fds->fluid);
2856
2857 float *forcex = manta_get_force_x(fds->fluid);
2858 float *forcey = manta_get_force_y(fds->fluid);
2859 float *forcez = manta_get_force_z(fds->fluid);
2860
2861 BLI_assert(forcex && forcey && forcez);
2862
2863 /* Either all or no components have to exist. */
2864 BLI_assert((color_r && color_g && color_b) || (!color_r && !color_g && !color_b));
2865 BLI_assert((color_r_in && color_g_in && color_b_in) ||
2866 (!color_r_in && !color_g_in && !color_b_in));
2867 BLI_assert((velx_initial && vely_initial && velz_initial) ||
2868 (!velx_initial && !vely_initial && !velz_initial));
2869
2870 uint z;
2871 /* Grid reset before writing again. */
2872 for (z = 0; z < fds->res[0] * fds->res[1] * fds->res[2]; z++) {
2873 /* Only reset static phi on first frame, dynamic phi gets reset every time. */
2874 if (phistatic_in && is_first_frame) {
2875 phistatic_in[z] = PHI_MAX;
2876 }
2877 if (phi_in) {
2878 phi_in[z] = PHI_MAX;
2879 }
2880 /* Only reset static phi on first frame, dynamic phi gets reset every time. */
2881 if (phioutstatic_in && is_first_frame) {
2882 phioutstatic_in[z] = PHI_MAX;
2883 }
2884 if (phiout_in) {
2885 phiout_in[z] = PHI_MAX;
2886 }
2887 /* Sync smoke inflow grids with their counterparts (simulation grids). */
2888 if (density_in) {
2889 density_in[z] = density[z];
2890 }
2891 if (heat_in) {
2892 heat_in[z] = heat[z];
2893 }
2894 if (color_r_in && color_g_in && color_b_in) {
2895 color_r_in[z] = color_r[z];
2896 color_g_in[z] = color_b[z];
2897 color_b_in[z] = color_g[z];
2898 }
2899 if (fuel_in) {
2900 fuel_in[z] = fuel[z];
2901 react_in[z] = react[z];
2902 }
2903 if (emission_in) {
2904 emission_in[z] = 0.0f;
2905 }
2906 if (velx_initial && vely_initial && velz_initial) {
2907 velx_initial[z] = 0.0f;
2908 vely_initial[z] = 0.0f;
2909 velz_initial[z] = 0.0f;
2910 }
2911 /* Reset forces here as update_effectors() is skipped when no external forces are present. */
2912 forcex[z] = 0.0f;
2913 forcey[z] = 0.0f;
2914 forcez[z] = 0.0f;
2915 }
2916
2917 /* Apply emission data for every flow object. */
2918 for (int flow_index = 0; flow_index < numflowobjs; flow_index++) {
2919 Object *flowobj = flowobjs[flow_index];
2922
2923 /* Sanity check. */
2924 if (!fmd2) {
2925 continue;
2926 }
2927
2928 /* Check for initialized flow object. */
2929 if ((fmd2->type & MOD_FLUID_TYPE_FLOW) && fmd2->flow) {
2930 FluidFlowSettings *ffs = fmd2->flow;
2931
2932 bool is_inflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW);
2933 bool is_geometry = (ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY);
2934 bool is_outflow = (ffs->behavior == FLUID_FLOW_BEHAVIOR_OUTFLOW);
2935 bool is_static = is_static_object(flowobj) &&
2937
2938 FluidObjectBB *bb = &bb_maps[flow_index];
2939 float *velocity_map = bb->velocity;
2940 float *emission_map = bb->influence;
2941 float *distance_map = bb->distances;
2942
2943 int gx, gy, gz, ex, ey, ez, dx, dy, dz;
2944 size_t e_index, d_index;
2945
2946 /* Loop through every emission map cell. */
2947 for (gx = bb->min[0]; gx < bb->max[0]; gx++) {
2948 for (gy = bb->min[1]; gy < bb->max[1]; gy++) {
2949 for (gz = bb->min[2]; gz < bb->max[2]; gz++) {
2950 /* Compute emission map index. */
2951 ex = gx - bb->min[0];
2952 ey = gy - bb->min[1];
2953 ez = gz - bb->min[2];
2954 e_index = manta_get_index(ex, bb->res[0], ey, bb->res[1], ez);
2955
2956 /* Get domain index. */
2957 dx = gx - fds->res_min[0];
2958 dy = gy - fds->res_min[1];
2959 dz = gz - fds->res_min[2];
2960 d_index = manta_get_index(dx, fds->res[0], dy, fds->res[1], dz);
2961 /* Make sure emission cell is inside the new domain boundary. */
2962 if (dx < 0 || dy < 0 || dz < 0 || dx >= fds->res[0] || dy >= fds->res[1] ||
2963 dz >= fds->res[2])
2964 {
2965 continue;
2966 }
2967
2968 /* Delete fluid in outflow regions. */
2969 if (is_outflow) {
2970 float *levelset = ((is_first_frame || is_resume) && is_static) ? phioutstatic_in :
2971 phiout_in;
2972 apply_outflow_fields(d_index,
2973 distance_map[e_index],
2974 density_in,
2975 heat_in,
2976 fuel_in,
2977 react_in,
2978 color_r_in,
2979 color_g_in,
2980 color_b_in,
2981 levelset);
2982 }
2983 /* Do not apply inflow after the first frame when in geometry mode. */
2984 else if (is_geometry && !is_first_frame) {
2985 apply_inflow_fields(ffs,
2986 0.0f,
2987 PHI_MAX,
2988 d_index,
2989 density_in,
2990 density,
2991 heat_in,
2992 heat,
2993 fuel_in,
2994 fuel,
2995 react_in,
2996 react,
2997 color_r_in,
2998 color_r,
2999 color_g_in,
3000 color_g,
3001 color_b_in,
3002 color_b,
3003 phi_in,
3004 emission_in);
3005 }
3006 /* Main inflow application. */
3007 else if (is_geometry || is_inflow) {
3008 float *levelset = ((is_first_frame || is_resume) && is_static && !is_geometry) ?
3009 phistatic_in :
3010 phi_in;
3011 apply_inflow_fields(ffs,
3012 emission_map[e_index],
3013 distance_map[e_index],
3014 d_index,
3015 density_in,
3016 density,
3017 heat_in,
3018 heat,
3019 fuel_in,
3020 fuel,
3021 react_in,
3022 react,
3023 color_r_in,
3024 color_r,
3025 color_g_in,
3026 color_g,
3027 color_b_in,
3028 color_b,
3029 levelset,
3030 emission_in);
3031 if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
3032 /* Use the initial velocity from the inflow object with the highest velocity for
3033 * now. */
3034 float vel_initial[3];
3035 vel_initial[0] = velx_initial[d_index];
3036 vel_initial[1] = vely_initial[d_index];
3037 vel_initial[2] = velz_initial[d_index];
3038 float vel_initial_strength = len_squared_v3(vel_initial);
3039 float vel_map_strength = len_squared_v3(velocity_map + 3 * e_index);
3040 if (vel_map_strength > vel_initial_strength) {
3041 velx_initial[d_index] = velocity_map[e_index * 3];
3042 vely_initial[d_index] = velocity_map[e_index * 3 + 1];
3043 velz_initial[d_index] = velocity_map[e_index * 3 + 2];
3044 }
3045 }
3046 }
3047 }
3048 }
3049 } /* End of flow emission map loop. */
3050 bb_freeData(bb);
3051 } /* End of flow object loop. */
3052 }
3053
3055 if (bb_maps) {
3056 MEM_freeN(bb_maps);
3057 }
3058}
3059
3060struct UpdateEffectorsData {
3061 Scene *scene;
3063 ListBase *effectors;
3064
3065 float *density;
3066 float *fuel;
3067 float *force_x;
3068 float *force_y;
3069 float *force_z;
3070 float *velocity_x;
3071 float *velocity_y;
3072 float *velocity_z;
3073 int *flags;
3074 float *phi_obs_in;
3075};
3076
3077static void update_effectors_task_cb(void *__restrict userdata,
3078 const int x,
3079 const TaskParallelTLS *__restrict /*tls*/)
3080{
3081 UpdateEffectorsData *data = static_cast<UpdateEffectorsData *>(userdata);
3082 FluidDomainSettings *fds = data->fds;
3083
3084 for (int y = 0; y < fds->res[1]; y++) {
3085 for (int z = 0; z < fds->res[2]; z++) {
3086 EffectedPoint epoint;
3087 float mag;
3088 float voxel_center[3] = {0, 0, 0}, vel[3] = {0, 0, 0}, retvel[3] = {0, 0, 0};
3089 const uint index = manta_get_index(x, fds->res[0], y, fds->res[1], z);
3090
3091 if ((data->fuel && std::max(data->density[index], data->fuel[index]) < FLT_EPSILON) ||
3092 (!data->fuel && data->density && data->density[index] < FLT_EPSILON) ||
3093 (data->phi_obs_in && data->phi_obs_in[index] < 0.0f) ||
3094 data->flags[index] & 2) /* Manta-flow convention: `2 == FlagObstacle`. */
3095 {
3096 continue;
3097 }
3098
3099 /* Get velocities from manta grid space and convert to blender units. */
3100 vel[0] = data->velocity_x[index];
3101 vel[1] = data->velocity_y[index];
3102 vel[2] = data->velocity_z[index];
3103 mul_v3_fl(vel, fds->dx);
3104
3105 /* Convert vel to global space. */
3106 mag = len_v3(vel);
3107 mul_mat3_m4_v3(fds->obmat, vel);
3108 normalize_v3(vel);
3109 mul_v3_fl(vel, mag);
3110
3111 voxel_center[0] = fds->p0[0] + fds->cell_size[0] * (float(x + fds->res_min[0]) + 0.5f);
3112 voxel_center[1] = fds->p0[1] + fds->cell_size[1] * (float(y + fds->res_min[1]) + 0.5f);
3113 voxel_center[2] = fds->p0[2] + fds->cell_size[2] * (float(z + fds->res_min[2]) + 0.5f);
3114 mul_m4_v3(fds->obmat, voxel_center);
3115
3116 /* Do effectors. */
3117 pd_point_from_loc(data->scene, voxel_center, vel, index, &epoint);
3119 data->effectors, nullptr, fds->effector_weights, &epoint, retvel, nullptr, nullptr);
3120
3121 /* Convert retvel to local space. */
3122 mag = len_v3(retvel);
3123 mul_mat3_m4_v3(fds->imat, retvel);
3124 normalize_v3(retvel);
3125 mul_v3_fl(retvel, mag);
3126
3127 /* Copy computed force to fluid solver forces. */
3128 mul_v3_fl(retvel, 0.2f); /* Factor from 0e6820cc5d62. */
3129 clamp_v3(retvel, -1.0f, 1.0f); /* Restrict forces to +-1 interval. */
3130 data->force_x[index] = retvel[0];
3131 data->force_y[index] = retvel[1];
3132 data->force_z[index] = retvel[2];
3133
3134# ifdef DEBUG_PRINT
3135 /* Debugging: Print forces. */
3136 printf("setting force: [%f, %f, %f]\n",
3137 data->force_x[index],
3138 data->force_y[index],
3139 data->force_z[index]);
3140# endif
3141 }
3142 }
3143}
3144
3145static void update_effectors(
3146 Depsgraph *depsgraph, Scene *scene, Object *ob, FluidDomainSettings *fds, float /*dt*/)
3147{
3148 ListBase *effectors;
3149 /* make sure smoke flow influence is 0.0f */
3151 effectors = BKE_effectors_create(depsgraph, ob, nullptr, fds->effector_weights, false);
3152
3153 if (effectors) {
3154 /* Precalculate wind forces. */
3155 UpdateEffectorsData data;
3156 data.scene = scene;
3157 data.fds = fds;
3158 data.effectors = effectors;
3159 data.density = manta_smoke_get_density(fds->fluid);
3160 data.fuel = manta_smoke_get_fuel(fds->fluid);
3161 data.force_x = manta_get_force_x(fds->fluid);
3162 data.force_y = manta_get_force_y(fds->fluid);
3163 data.force_z = manta_get_force_z(fds->fluid);
3164 data.velocity_x = manta_get_velocity_x(fds->fluid);
3165 data.velocity_y = manta_get_velocity_y(fds->fluid);
3166 data.velocity_z = manta_get_velocity_z(fds->fluid);
3167 data.flags = manta_smoke_get_flags(fds->fluid);
3168 data.phi_obs_in = manta_get_phiobs_in(fds->fluid);
3169
3170 TaskParallelSettings settings;
3172 settings.min_iter_per_thread = 2;
3173 BLI_task_parallel_range(0, fds->res[0], &data, update_effectors_task_cb, &settings);
3174 }
3175
3176 BKE_effectors_free(effectors);
3177}
3178
3179static Mesh *create_liquid_geometry(FluidDomainSettings *fds,
3180 Scene *scene,
3181 Mesh *orgmesh,
3182 Object *ob)
3183{
3184 using namespace blender;
3185 using namespace blender::bke;
3186 Mesh *mesh;
3187 float min[3];
3188 float max[3];
3189 float size[3];
3190 float cell_size_scaled[3];
3191
3192 const AttributeAccessor orig_attributes = orgmesh->attributes();
3193 const VArraySpan orig_material_indices = *orig_attributes.lookup<int>("material_index",
3195 const short mp_mat_nr = orig_material_indices.is_empty() ? 0 : orig_material_indices[0];
3196
3197 int i;
3198 int num_verts, num_faces;
3199
3200 if (!fds->fluid) {
3201 return nullptr;
3202 }
3203
3204 num_verts = manta_liquid_get_num_verts(fds->fluid);
3205 num_faces = manta_liquid_get_num_triangles(fds->fluid);
3206
3207# ifdef DEBUG_PRINT
3208 /* Debugging: Print number of vertices, normals, and faces. */
3209 printf("num_verts: %d, num_faces: %d\n", num_verts, num_faces);
3210# endif
3211
3212 if (!num_verts || !num_faces) {
3213 return nullptr;
3214 }
3215
3216 mesh = BKE_mesh_new_nomain(num_verts, 0, num_faces, num_faces * 3);
3217 if (!mesh) {
3218 return nullptr;
3219 }
3220 blender::MutableSpan<blender::float3> positions = mesh->vert_positions_for_write();
3221 blender::MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
3222 blender::MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
3223
3224 const bool is_sharp = orgmesh->attributes()
3225 .lookup_or_default<bool>("sharp_face", AttrDomain::Face, false)
3226 .varray[0];
3227 mesh_smooth_set(*mesh, !is_sharp);
3228
3229 /* Get size (dimension) but considering scaling. */
3230 copy_v3_v3(cell_size_scaled, fds->cell_size);
3231 mul_v3_v3(cell_size_scaled, ob->scale);
3232 madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min);
3233 madd_v3fl_v3fl_v3fl_v3i(max, fds->p0, cell_size_scaled, fds->res_max);
3235
3236 /* Biggest dimension will be used for up-scaling. */
3237 float max_size = std::max({size[0], size[1], size[2]});
3238
3239 float co_scale[3];
3240 co_scale[0] = max_size / ob->scale[0];
3241 co_scale[1] = max_size / ob->scale[1];
3242 co_scale[2] = max_size / ob->scale[2];
3243
3244 float co_offset[3];
3245 co_offset[0] = (fds->p0[0] + fds->p1[0]) / 2.0f;
3246 co_offset[1] = (fds->p0[1] + fds->p1[1]) / 2.0f;
3247 co_offset[2] = (fds->p0[2] + fds->p1[2]) / 2.0f;
3248
3249 /* Velocities. */
3250 /* If needed, vertex velocities will be read too. */
3251 bool use_speedvectors = fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS;
3252 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3253 SpanAttributeWriter<float3> velocities;
3254 float time_mult = fds->dx / (DT_DEFAULT * (25.0f / FPS));
3255
3256 if (use_speedvectors) {
3257 velocities = attributes.lookup_or_add_for_write_only_span<float3>("velocity",
3259 }
3260
3261 /* Loop for vertices and normals. */
3262 for (i = 0; i < num_verts; i++) {
3263
3264 /* Vertices (data is normalized cube around domain origin). */
3265 positions[i][0] = manta_liquid_get_vertex_x_at(fds->fluid, i);
3266 positions[i][1] = manta_liquid_get_vertex_y_at(fds->fluid, i);
3267 positions[i][2] = manta_liquid_get_vertex_z_at(fds->fluid, i);
3268
3269 /* Adjust coordinates from Mantaflow to match viewport scaling. */
3270 float tmp[3] = {float(fds->res[0]), float(fds->res[1]), float(fds->res[2])};
3271 /* Scale to unit cube around 0. */
3272 mul_v3_fl(tmp, fds->mesh_scale * 0.5f);
3273 sub_v3_v3(positions[i], tmp);
3274 /* Apply scaling of domain object. */
3275 mul_v3_fl(positions[i], fds->dx / fds->mesh_scale);
3276
3277 mul_v3_v3(positions[i], co_scale);
3278 add_v3_v3(positions[i], co_offset);
3279
3280# ifdef DEBUG_PRINT
3281 /* Debugging: Print coordinates of vertices. */
3282 printf("positions[i][0]: %f, positions[i][1]: %f, positions[i][2]: %f\n",
3283 positions[i][0],
3284 positions[i][1],
3285 positions[i][2]);
3286# endif
3287
3288# ifdef DEBUG_PRINT
3289 /* Debugging: Print coordinates of normals. */
3290 printf("no_s[0]: %d, no_s[1]: %d, no_s[2]: %d\n", no_s[0], no_s[1], no_s[2]);
3291# endif
3292
3293 if (use_speedvectors) {
3294 velocities.span[i].x = manta_liquid_get_vertvel_x_at(fds->fluid, i) * time_mult;
3295 velocities.span[i].y = manta_liquid_get_vertvel_y_at(fds->fluid, i) * time_mult;
3296 velocities.span[i].z = manta_liquid_get_vertvel_z_at(fds->fluid, i) * time_mult;
3297# ifdef DEBUG_PRINT
3298 /* Debugging: Print velocities of vertices. */
3299 printf("velocities[%d].x: %f, velocities[%d].y: %f, velocities[%d].z: %f\n",
3300 i,
3301 velocities.span[i].x,
3302 i,
3303 velocities.span[i].y,
3304 i,
3305 velocities.span[i].z);
3306# endif
3307 }
3308 }
3309
3310 bke::SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_span<int>(
3311 "material_index", AttrDomain::Face);
3312
3313 /* Loop for triangles. */
3314 for (const int i : face_offsets.index_range().drop_back(1)) {
3315 /* Initialize from existing face. */
3316 material_indices.span[i] = mp_mat_nr;
3317
3318 face_offsets[i] = i * 3;
3319
3320 corner_verts[i * 3 + 0] = manta_liquid_get_triangle_x_at(fds->fluid, i);
3321 corner_verts[i * 3 + 1] = manta_liquid_get_triangle_y_at(fds->fluid, i);
3322 corner_verts[i * 3 + 2] = manta_liquid_get_triangle_z_at(fds->fluid, i);
3323# ifdef DEBUG_PRINT
3324 /* Debugging: Print mesh faces. */
3325 printf("mloops[0].v: %d, mloops[1].v: %d, mloops[2].v: %d\n",
3326 mloops[0].v,
3327 mloops[1].v,
3328 mloops[2].v);
3329# endif
3330 }
3331
3332 velocities.finish();
3333 material_indices.finish();
3334
3335 mesh_calc_edges(*mesh, false, false);
3336
3337 return mesh;
3338}
3339
3340static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Object *ob)
3341{
3342 using namespace blender;
3343 using namespace blender::bke;
3344 Mesh *result;
3345 float min[3];
3346 float max[3];
3347 float *co;
3348 int *corner_vert;
3349
3350 int num_verts = 8;
3351 int num_faces = 6;
3352 float ob_loc[3] = {0};
3353 float ob_cache_loc[3] = {0};
3354
3355 /* Just copy existing mesh if there is no content or if the adaptive domain is not being used. */
3356 if (fds->total_cells <= 1 || (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) == 0) {
3357 return BKE_mesh_copy_for_eval(*orgmesh);
3358 }
3359
3360 result = BKE_mesh_new_nomain(num_verts, 0, num_faces, num_faces * 4);
3361 blender::MutableSpan<blender::float3> positions = result->vert_positions_for_write();
3362 blender::MutableSpan<int> face_offsets = result->face_offsets_for_write();
3363 blender::MutableSpan<int> corner_verts = result->corner_verts_for_write();
3364
3365 if (num_verts) {
3366 /* Volume bounds. */
3367 madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, fds->cell_size, fds->res_min);
3368 madd_v3fl_v3fl_v3fl_v3i(max, fds->p0, fds->cell_size, fds->res_max);
3369
3370 /* Set vertices of smoke BB. Especially important, when BB changes (adaptive domain). */
3371 /* Top slab */
3372 co = positions[0];
3373 co[0] = min[0];
3374 co[1] = min[1];
3375 co[2] = max[2];
3376 co = positions[1];
3377 co[0] = max[0];
3378 co[1] = min[1];
3379 co[2] = max[2];
3380 co = positions[2];
3381 co[0] = max[0];
3382 co[1] = max[1];
3383 co[2] = max[2];
3384 co = positions[3];
3385 co[0] = min[0];
3386 co[1] = max[1];
3387 co[2] = max[2];
3388 /* Bottom slab. */
3389 co = positions[4];
3390 co[0] = min[0];
3391 co[1] = min[1];
3392 co[2] = min[2];
3393 co = positions[5];
3394 co[0] = max[0];
3395 co[1] = min[1];
3396 co[2] = min[2];
3397 co = positions[6];
3398 co[0] = max[0];
3399 co[1] = max[1];
3400 co[2] = min[2];
3401 co = positions[7];
3402 co[0] = min[0];
3403 co[1] = max[1];
3404 co[2] = min[2];
3405
3406 face_offsets.fill(4);
3408
3409 /* Create faces. */
3410 /* Top side. */
3411 corner_vert = &corner_verts[0 * 4];
3412 corner_vert[0] = 0;
3413 corner_vert[1] = 1;
3414 corner_vert[2] = 2;
3415 corner_vert[3] = 3;
3416 /* Right side. */
3417 corner_vert = &corner_verts[1 * 4];
3418 corner_vert[0] = 2;
3419 corner_vert[1] = 1;
3420 corner_vert[2] = 5;
3421 corner_vert[3] = 6;
3422 /* Bottom side. */
3423 corner_vert = &corner_verts[2 * 4];
3424 corner_vert[0] = 7;
3425 corner_vert[1] = 6;
3426 corner_vert[2] = 5;
3427 corner_vert[3] = 4;
3428 /* Left side. */
3429 corner_vert = &corner_verts[3 * 4];
3430 corner_vert[0] = 0;
3431 corner_vert[1] = 3;
3432 corner_vert[2] = 7;
3433 corner_vert[3] = 4;
3434 /* Front side. */
3435 corner_vert = &corner_verts[4 * 4];
3436 corner_vert[0] = 3;
3437 corner_vert[1] = 2;
3438 corner_vert[2] = 6;
3439 corner_vert[3] = 7;
3440 /* Back side. */
3441 corner_vert = &corner_verts[5 * 4];
3442 corner_vert[0] = 1;
3443 corner_vert[1] = 0;
3444 corner_vert[2] = 4;
3445 corner_vert[3] = 5;
3446
3447 /* Calculate required shift to match domain's global position
3448 * it was originally simulated at (if object moves without manta step). */
3449 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
3450 mul_m4_v3(ob->object_to_world().ptr(), ob_loc);
3451 mul_m4_v3(fds->obmat, ob_cache_loc);
3452 sub_v3_v3v3(fds->obj_shift_f, ob_cache_loc, ob_loc);
3453 /* Convert shift to local space and apply to vertices. */
3454 mul_mat3_m4_v3(ob->world_to_object().ptr(), fds->obj_shift_f);
3455 /* Apply shift to vertices. */
3456 for (int i = 0; i < num_verts; i++) {
3457 add_v3_v3(positions[i], fds->obj_shift_f);
3458 }
3459 }
3460
3461 mesh_calc_edges(*result, false, false);
3462 return result;
3463}
3464
3465static int manta_step(
3466 Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh, FluidModifierData *fmd, int frame)
3467{
3468 FluidDomainSettings *fds = fmd->domain;
3469 float dt, frame_length, time_total, time_total_old;
3470 float time_per_frame;
3471 bool init_resolution = true;
3472
3473 /* Store baking success - bake might be aborted anytime by user. */
3474 int result = 1;
3475 int mode = fds->cache_type;
3476 bool mode_replay = (mode == FLUID_DOMAIN_CACHE_REPLAY);
3477
3478 /* Update object state. */
3479 invert_m4_m4(fds->imat, ob->object_to_world().ptr());
3480 copy_m4_m4(fds->obmat, ob->object_to_world().ptr());
3481
3482 /* Gas domain might use adaptive domain. */
3483 if (fds->type == FLUID_DOMAIN_TYPE_GAS) {
3484 init_resolution = (fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) != 0;
3485 }
3486 manta_set_domain_from_mesh(fds, ob, mesh, init_resolution);
3487
3488 /* Use local variables for adaptive loop, dt can change. */
3489 frame_length = fds->frame_length;
3490 dt = fds->dt;
3491 time_per_frame = 0;
3492 time_total = fds->time_total;
3493 /* Keep track of original total time to correct small errors at end of step. */
3494 time_total_old = fds->time_total;
3495
3496 std::scoped_lock lock(object_update_lock);
3497
3498 /* Loop as long as time_per_frame (sum of sub dt's) does not exceed actual frame-length. */
3499 while (time_per_frame + FLT_EPSILON < frame_length) {
3501 dt = manta_get_timestep(fds->fluid);
3502
3503 /* Save adapted dt so that MANTA object can access it (important when adaptive domain creates
3504 * new MANTA object). */
3505 fds->dt = dt;
3506
3507 /* Calculate inflow geometry. */
3508 update_flowsfluids(depsgraph, scene, ob, fds, time_per_frame, frame_length, frame, dt);
3509
3510 /* If user requested stop, quit baking */
3511 if (G.is_break && !mode_replay) {
3512 result = 0;
3513 break;
3514 }
3515
3516 manta_update_variables(fds->fluid, fmd);
3517
3518 /* Calculate obstacle geometry. */
3519 update_obstacles(depsgraph, scene, ob, fds, time_per_frame, frame_length, frame, dt);
3520
3521 /* If user requested stop, quit baking */
3522 if (G.is_break && !mode_replay) {
3523 result = 0;
3524 break;
3525 }
3526
3527 /* Only bake if the domain is bigger than one cell (important for adaptive domain). */
3528 if (fds->total_cells > 1) {
3529 update_effectors(depsgraph, scene, ob, fds, dt);
3530 manta_bake_data(fds->fluid, fmd, frame);
3531 }
3532
3533 /* Count for how long this while loop is running. */
3534 time_per_frame += dt;
3535 time_total += dt;
3536
3537 fds->time_per_frame = time_per_frame;
3538 fds->time_total = time_total;
3539 }
3540
3541 /* Total time must not exceed frame-count times frame-length. Correct tiny errors here. */
3542 CLAMP_MAX(fds->time_total, time_total_old + fds->frame_length);
3543
3544 /* Compute shadow grid for gas simulations. Make sure to skip if bake job was canceled early. */
3545 if (fds->type == FLUID_DOMAIN_TYPE_GAS && result) {
3546 manta_smoke_calc_transparency(
3548 }
3549
3550 return result;
3551}
3552
3553static void manta_guiding(
3554 Depsgraph *depsgraph, Scene *scene, Object *ob, FluidModifierData *fmd, int frame)
3555{
3556 FluidDomainSettings *fds = fmd->domain;
3557 float dt = DT_DEFAULT * (25.0f / FPS) * fds->time_scale;
3558
3559 std::scoped_lock lock(object_update_lock);
3560
3561 update_obstacles(depsgraph, scene, ob, fds, dt, dt, frame, dt);
3562 manta_bake_guiding(fds->fluid, fmd, frame);
3563}
3564
3565static void fluid_modifier_processFlow(FluidModifierData *fmd,
3566 Depsgraph *depsgraph,
3567 Scene *scene,
3568 Object *ob,
3569 Mesh *mesh,
3570 const int scene_framenr)
3571{
3572 if (scene_framenr >= fmd->time) {
3573 fluid_modifier_init(fmd, depsgraph, ob, scene, mesh);
3574 }
3575
3576 if (fmd->flow) {
3577 if (fmd->flow->mesh) {
3578 BKE_id_free(nullptr, fmd->flow->mesh);
3579 }
3581 }
3582
3583 if (scene_framenr > fmd->time) {
3584 fmd->time = scene_framenr;
3585 }
3586 else if (scene_framenr < fmd->time) {
3587 fmd->time = scene_framenr;
3588 fluid_modifier_reset_ex(fmd, false);
3589 }
3590}
3591
3592static void fluid_modifier_processEffector(FluidModifierData *fmd,
3593 Depsgraph *depsgraph,
3594 Scene *scene,
3595 Object *ob,
3596 Mesh *mesh,
3597 const int scene_framenr)
3598{
3599 if (scene_framenr >= fmd->time) {
3600 fluid_modifier_init(fmd, depsgraph, ob, scene, mesh);
3601 }
3602
3603 if (fmd->effector) {
3604 if (fmd->effector->mesh) {
3605 BKE_id_free(nullptr, fmd->effector->mesh);
3606 }
3608 }
3609
3610 if (scene_framenr > fmd->time) {
3611 fmd->time = scene_framenr;
3612 }
3613 else if (scene_framenr < fmd->time) {
3614 fmd->time = scene_framenr;
3615 fluid_modifier_reset_ex(fmd, false);
3616 }
3617}
3618
3619static void fluid_modifier_processDomain(FluidModifierData *fmd,
3620 Depsgraph *depsgraph,
3621 Scene *scene,
3622 Object *ob,
3623 Mesh *mesh,
3624 const int scene_framenr)
3625{
3626 FluidDomainSettings *fds = fmd->domain;
3627 Object *guide_parent = nullptr;
3628 Object **objs = nullptr;
3629 uint numobj = 0;
3630 FluidModifierData *fmd_parent = nullptr;
3631
3632 bool is_startframe, has_advanced;
3633 is_startframe = (scene_framenr == fds->cache_frame_start);
3634 has_advanced = (scene_framenr == fmd->time + 1);
3635 int mode = fds->cache_type;
3636
3637 /* Do not process modifier if current frame is out of cache range. */
3638 bool escape = false;
3639 switch (mode) {
3642 if (fds->cache_frame_offset > 0) {
3643 if (scene_framenr < fds->cache_frame_start ||
3644 scene_framenr > fds->cache_frame_end + fds->cache_frame_offset)
3645 {
3646 escape = true;
3647 }
3648 }
3649 else {
3650 if (scene_framenr < fds->cache_frame_start + fds->cache_frame_offset ||
3651 scene_framenr > fds->cache_frame_end)
3652 {
3653 escape = true;
3654 }
3655 }
3656 break;
3658 default:
3659 if (scene_framenr < fds->cache_frame_start || scene_framenr > fds->cache_frame_end) {
3660 escape = true;
3661 }
3662 break;
3663 }
3664 /* If modifier will not be processed, update/flush pointers from (old) fluid object once more. */
3665 if (escape && fds->fluid) {
3666 manta_update_pointers(fds->fluid, fmd, true);
3667 return;
3668 }
3669
3670 /* Reset fluid if no fluid present. Also resets active fields. */
3671 if (!fds->fluid) {
3672 fluid_modifier_reset_ex(fmd, false);
3673 }
3674
3675 /* Ensure cache directory is not relative. */
3676 const char *relbase = BKE_modifier_path_relbase_from_global(ob);
3677 BLI_path_abs(fds->cache_directory, relbase);
3678
3679 /* If 'outdated', reset the cache here. */
3680 if (is_startframe && mode == FLUID_DOMAIN_CACHE_REPLAY) {
3681 PTCacheID pid;
3682 BKE_ptcache_id_from_smoke(&pid, ob, fmd);
3683 if (pid.cache->flag & PTCACHE_OUTDATED) {
3685 BKE_fluid_cache_free_all(fds, ob);
3686 fluid_modifier_reset_ex(fmd, false);
3687 }
3688 }
3689
3690 /* Ensure that all flags are up to date before doing any baking and/or cache reading. */
3692 depsgraph, ob, fds->fluid_group, &numobj, eModifierType_Fluid);
3693 update_flowsflags(fds, objs, numobj);
3694 if (objs) {
3695 MEM_freeN(objs);
3696 }
3698 depsgraph, ob, fds->effector_group, &numobj, eModifierType_Fluid);
3699 update_obstacleflags(fds, objs, numobj);
3700 if (objs) {
3701 MEM_freeN(objs);
3702 }
3703
3704 /* Fluid domain init must not fail in order to continue modifier evaluation. */
3705 if (!fds->fluid && !fluid_modifier_init(fmd, depsgraph, ob, scene, mesh)) {
3706 CLOG_ERROR(&LOG, "Fluid initialization failed. Should not happen!");
3707 return;
3708 }
3709 BLI_assert(fds->fluid);
3710
3711 /* Guiding parent res pointer needs initialization. */
3712 guide_parent = fds->guide_parent;
3713 if (guide_parent) {
3715 if (fmd_parent && fmd_parent->domain) {
3716 copy_v3_v3_int(fds->guide_res, fmd_parent->domain->res);
3717 }
3718 }
3719
3720 /* Adaptive domain needs to know about current state, so save it here. */
3721 int o_res[3], o_min[3], o_max[3], o_shift[3];
3722 copy_v3_v3_int(o_res, fds->res);
3723 copy_v3_v3_int(o_min, fds->res_min);
3724 copy_v3_v3_int(o_max, fds->res_max);
3725 copy_v3_v3_int(o_shift, fds->shift);
3726
3727 /* Ensure that time parameters are initialized correctly before every step. */
3728 fds->frame_length = DT_DEFAULT * (25.0f / FPS) * fds->time_scale;
3729 fds->dt = fds->frame_length;
3730 fds->time_per_frame = 0;
3731
3732 /* Ensure that gravity is copied over every frame (could be keyframed). */
3733 update_final_gravity(fds, scene);
3734
3735 int next_frame = scene_framenr + 1;
3736 int prev_frame = scene_framenr - 1;
3737 /* Ensure positive of previous frame. */
3738 CLAMP_MIN(prev_frame, fds->cache_frame_start);
3739
3740 int data_frame = scene_framenr, noise_frame = scene_framenr;
3741 int mesh_frame = scene_framenr, particles_frame = scene_framenr, guide_frame = scene_framenr;
3742
3743 bool with_smoke, with_liquid;
3744 with_smoke = fds->type == FLUID_DOMAIN_TYPE_GAS;
3745 with_liquid = fds->type == FLUID_DOMAIN_TYPE_LIQUID;
3746
3747 bool drops, bubble, floater;
3751
3752 bool with_resumable_cache = fds->flags & FLUID_DOMAIN_USE_RESUMABLE_CACHE;
3753 bool with_script, with_noise, with_mesh, with_particles, with_guide;
3754 with_script = fds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT;
3755 with_noise = fds->flags & FLUID_DOMAIN_USE_NOISE;
3756 with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH;
3757 with_guide = fds->flags & FLUID_DOMAIN_USE_GUIDE;
3758 with_particles = drops || bubble || floater;
3759
3760 bool has_data, has_noise, has_mesh, has_particles, has_guide, has_config;
3761 has_data = manta_has_data(fds->fluid, fmd, scene_framenr);
3762 has_noise = manta_has_noise(fds->fluid, fmd, scene_framenr);
3763 has_mesh = manta_has_mesh(fds->fluid, fmd, scene_framenr);
3764 has_particles = manta_has_particles(fds->fluid, fmd, scene_framenr);
3765 has_guide = manta_has_guiding(fds->fluid, fmd, scene_framenr, guide_parent);
3766 has_config = manta_read_config(fds->fluid, fmd, scene_framenr);
3767
3768 /* When reading data from cache (has_config == true) ensure that active fields are allocated.
3769 * update_flowsflags() and update_obstacleflags() will not find flow sources hidden from renders.
3770 * See also: #72192. */
3771 if (has_config) {
3772 ensure_flowsfields(fds);
3773 ensure_obstaclefields(fds);
3774 }
3775
3776 bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide;
3777 baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
3778 baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
3779 baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
3780 baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
3781 baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
3782
3783 bool resume_data, resume_noise, resume_mesh, resume_particles, resume_guide;
3784 resume_data = (!is_startframe) && (fds->cache_frame_pause_data == scene_framenr);
3785 resume_noise = (!is_startframe) && (fds->cache_frame_pause_noise == scene_framenr);
3786 resume_mesh = (!is_startframe) && (fds->cache_frame_pause_mesh == scene_framenr);
3787 resume_particles = (!is_startframe) && (fds->cache_frame_pause_particles == scene_framenr);
3788 resume_guide = (!is_startframe) && (fds->cache_frame_pause_guide == scene_framenr);
3789
3790 bool read_cache, bake_cache;
3791 read_cache = false;
3792 bake_cache = baking_data || baking_noise || baking_mesh || baking_particles || baking_guide;
3793
3794 bool next_data, next_noise, next_mesh, next_particles, next_guide;
3795 next_data = manta_has_data(fds->fluid, fmd, next_frame);
3796 next_noise = manta_has_noise(fds->fluid, fmd, next_frame);
3797 next_mesh = manta_has_mesh(fds->fluid, fmd, next_frame);
3798 next_particles = manta_has_particles(fds->fluid, fmd, next_frame);
3799 next_guide = manta_has_guiding(fds->fluid, fmd, next_frame, guide_parent);
3800
3801 bool prev_data, prev_noise, prev_mesh, prev_particles, prev_guide;
3802 prev_data = manta_has_data(fds->fluid, fmd, prev_frame);
3803 prev_noise = manta_has_noise(fds->fluid, fmd, prev_frame);
3804 prev_mesh = manta_has_mesh(fds->fluid, fmd, prev_frame);
3805 prev_particles = manta_has_particles(fds->fluid, fmd, prev_frame);
3806 prev_guide = manta_has_guiding(fds->fluid, fmd, prev_frame, guide_parent);
3807
3808 /* Unused for now. */
3809 UNUSED_VARS(next_mesh, next_guide);
3810
3811 bool with_gdomain;
3812 with_gdomain = (fds->guide_source == FLUID_DOMAIN_GUIDE_SRC_DOMAIN);
3813
3814 /* Cache mode specific settings. */
3815 switch (mode) {
3818 /* Just load the data that has already been baked */
3819 if (!baking_data && !baking_noise && !baking_mesh && !baking_particles && !baking_guide) {
3820 read_cache = true;
3821 bake_cache = false;
3822
3823 /* Apply frame offset. */
3824 data_frame -= fmd->domain->cache_frame_offset;
3825 noise_frame -= fmd->domain->cache_frame_offset;
3826 mesh_frame -= fmd->domain->cache_frame_offset;
3827 particles_frame -= fmd->domain->cache_frame_offset;
3828 break;
3829 }
3830
3831 /* Set to previous frame if the bake was resumed
3832 * ie don't read all of the already baked frames, just the one before bake resumes */
3833 if (baking_data && resume_data) {
3834 data_frame = prev_frame;
3835 }
3836 if (baking_noise && resume_noise) {
3837 noise_frame = prev_frame;
3838 }
3839 if (baking_mesh && resume_mesh) {
3840 mesh_frame = prev_frame;
3841 }
3842 if (baking_particles && resume_particles) {
3843 particles_frame = prev_frame;
3844 }
3845 if (baking_guide && resume_guide) {
3846 guide_frame = prev_frame;
3847 }
3848
3849 /* Noise, mesh and particles can never be baked more than data. */
3850 CLAMP_MAX(noise_frame, data_frame);
3851 CLAMP_MAX(mesh_frame, data_frame);
3852 CLAMP_MAX(particles_frame, data_frame);
3853 CLAMP_MAX(guide_frame, fds->cache_frame_end);
3854
3855 /* Force to read cache as we're resuming the bake */
3856 read_cache = true;
3857 break;
3859 default:
3860 baking_data = !has_data && (is_startframe || prev_data);
3861 if (with_smoke && with_noise) {
3862 baking_noise = !has_noise && (is_startframe || prev_noise);
3863 }
3864 if (with_liquid && with_mesh) {
3865 baking_mesh = !has_mesh && (is_startframe || prev_mesh);
3866 }
3867 if (with_liquid && with_particles) {
3868 baking_particles = !has_particles && (is_startframe || prev_particles);
3869 }
3870
3871 /* Always trying to read the cache in replay mode. */
3872 read_cache = true;
3873 bake_cache = false;
3874 break;
3875 }
3876
3877 bool read_partial = false, read_all = false;
3878 bool grid_display = fds->use_coba;
3879
3880 /* Try to read from cache and keep track of read success. */
3881 if (read_cache) {
3882
3883 /* Read mesh cache. */
3884 if (with_liquid && with_mesh) {
3885 if (mesh_frame != scene_framenr) {
3886 has_config = manta_read_config(fds->fluid, fmd, mesh_frame);
3887 }
3888
3889 /* Only load the mesh at the resolution it ways originally simulated at.
3890 * The mesh files don't have a header, i.e. the don't store the grid resolution. */
3891 if (!manta_needs_realloc(fds->fluid, fmd)) {
3892 has_mesh = manta_read_mesh(fds->fluid, fmd, mesh_frame);
3893 }
3894 }
3895
3896 /* Read particles cache. */
3897 if (with_liquid && with_particles) {
3898 if (particles_frame != scene_framenr) {
3899 has_config = manta_read_config(fds->fluid, fmd, particles_frame);
3900 }
3901
3902 read_partial = !baking_data && !baking_particles && next_particles;
3903 read_all = !read_partial && with_resumable_cache;
3904 has_particles = manta_read_particles(fds->fluid, fmd, particles_frame, read_all);
3905 }
3906
3907 /* Read guide cache. */
3908 if (with_guide) {
3909 FluidModifierData *fmd2 = (with_gdomain) ? fmd_parent : fmd;
3910 has_guide = manta_read_guiding(fds->fluid, fmd2, scene_framenr, with_gdomain);
3911 }
3912
3913 /* Read noise and data cache */
3914 if (with_smoke && with_noise) {
3915 if (noise_frame != scene_framenr) {
3916 has_config = manta_read_config(fds->fluid, fmd, noise_frame);
3917 }
3918
3919 /* Only reallocate when just reading cache or when resuming during bake. */
3920 if (has_data && has_config && manta_needs_realloc(fds->fluid, fmd)) {
3922 fds, o_res, fds->res, o_min, fds->res_min, o_max, o_shift, fds->shift);
3923 }
3924
3925 read_partial = !baking_data && !baking_noise && next_noise;
3926 read_all = !read_partial && with_resumable_cache;
3927 has_noise = manta_read_noise(fds->fluid, fmd, noise_frame, read_all);
3928
3929 read_partial = !baking_data && !baking_noise && next_data && next_noise;
3930 read_all = !read_partial && with_resumable_cache;
3931 has_data = manta_read_data(fds->fluid, fmd, data_frame, read_all);
3932 }
3933 /* Read data cache only */
3934 else {
3935 if (data_frame != scene_framenr) {
3936 has_config = manta_read_config(fds->fluid, fmd, data_frame);
3937 }
3938
3939 if (with_smoke || with_liquid) {
3940 /* Read config and realloc fluid object if needed. */
3941 if (has_config && manta_needs_realloc(fds->fluid, fmd)) {
3942 BKE_fluid_reallocate_fluid(fds, fds->res, 1);
3943 }
3944 }
3945
3946 read_partial = !baking_data && !baking_particles && !baking_mesh && next_data &&
3947 !grid_display;
3948 read_all = !read_partial && with_resumable_cache;
3949 has_data = manta_read_data(fds->fluid, fmd, data_frame, read_all);
3950 }
3951 }
3952
3953 /* Cache mode specific settings */
3954 switch (mode) {
3957 if (!baking_data && !baking_noise && !baking_mesh && !baking_particles && !baking_guide) {
3958 bake_cache = false;
3959 }
3960 break;
3962 default:
3963 if (with_guide) {
3964 baking_guide = !has_guide && (is_startframe || prev_guide);
3965 }
3966 baking_data = !has_data && (is_startframe || prev_data);
3967 if (with_smoke && with_noise) {
3968 baking_noise = !has_noise && (is_startframe || prev_noise);
3969 }
3970 if (with_liquid && with_mesh) {
3971 baking_mesh = !has_mesh && (is_startframe || prev_mesh);
3972 }
3973 if (with_liquid && with_particles) {
3974 baking_particles = !has_particles && (is_startframe || prev_particles);
3975 }
3976
3977 /* Only bake if time advanced by one frame. */
3978 if (is_startframe || has_advanced) {
3979 bake_cache = baking_data || baking_noise || baking_mesh || baking_particles;
3980 }
3981 break;
3982 }
3983
3984 /* Trigger bake calls individually */
3985 if (bake_cache) {
3986 /* Ensure fresh variables at every animation step */
3987 manta_update_variables(fds->fluid, fmd);
3988
3989 /* Export mantaflow python script on first frame (once only) and for any bake type */
3990 if (with_script && is_startframe) {
3991 if (with_smoke) {
3993 }
3994 if (with_liquid) {
3996 }
3997 }
3998
3999 if (baking_guide && with_guide) {
4000 manta_guiding(depsgraph, scene, ob, fmd, scene_framenr);
4001 }
4002 if (baking_data) {
4003 /* Only save baked data if all of it completed successfully. */
4004 if (manta_step(depsgraph, scene, ob, mesh, fmd, scene_framenr)) {
4005 manta_write_config(fds->fluid, fmd, scene_framenr);
4006 manta_write_data(fds->fluid, fmd, scene_framenr);
4007 }
4008 }
4009 if (has_data || baking_data) {
4010 if (baking_noise && with_smoke && with_noise) {
4011 /* Ensure that no bake occurs if domain was minimized by adaptive domain. */
4012 if (fds->total_cells > 1) {
4013 manta_bake_noise(fds->fluid, fmd, scene_framenr);
4014 }
4015 manta_write_noise(fds->fluid, fmd, scene_framenr);
4016 }
4017 if (baking_mesh && with_liquid && with_mesh) {
4018 manta_bake_mesh(fds->fluid, fmd, scene_framenr);
4019 }
4020 if (baking_particles && with_liquid && with_particles) {
4021 manta_bake_particles(fds->fluid, fmd, scene_framenr);
4022 }
4023 }
4024 }
4025
4026 /* Ensure that fluid pointers are always up to date at the end of modifier processing. */
4027 manta_update_pointers(fds->fluid, fmd, false);
4028
4030 fmd->time = scene_framenr;
4031}
4032
4033static void fluid_modifier_process(
4034 FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
4035{
4036 const int scene_framenr = int(DEG_get_ctime(depsgraph));
4037
4038 if (fmd->type & MOD_FLUID_TYPE_FLOW) {
4039 fluid_modifier_processFlow(fmd, depsgraph, scene, ob, mesh, scene_framenr);
4040 }
4041 else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
4042 fluid_modifier_processEffector(fmd, depsgraph, scene, ob, mesh, scene_framenr);
4043 }
4044 else if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
4045 fluid_modifier_processDomain(fmd, depsgraph, scene, ob, mesh, scene_framenr);
4046 }
4047}
4048
4050 FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
4051{
4052 /* Optimization: Do not update viewport during bakes (except in replay mode)
4053 * Reason: UI is locked and updated liquid / smoke geometry is not visible anyways. */
4054 bool needs_viewport_update = false;
4055
4056 /* Optimization: Only process modifier if object is not being altered. */
4057 if (!G.moving) {
4058 /* Lock so preview render does not read smoke data while it gets modified. */
4059 if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
4061 }
4062
4063 fluid_modifier_process(fmd, depsgraph, scene, ob, mesh);
4064
4065 if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
4066 BLI_rw_mutex_unlock(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex));
4067 }
4068
4069 if (fmd->domain) {
4070 FluidDomainSettings *fds = fmd->domain;
4071
4072 /* Always update viewport in cache replay mode. */
4075 {
4076 needs_viewport_update = true;
4077 }
4078 /* In other cache modes, only update the viewport when no bake is going on. */
4079 else {
4080 bool with_mesh;
4081 with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH;
4082 bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide;
4083 baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA;
4084 baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE;
4085 baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH;
4086 baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES;
4087 baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE;
4088
4089 if (with_mesh && !baking_data && !baking_noise && !baking_mesh && !baking_particles &&
4090 !baking_guide)
4091 {
4092 needs_viewport_update = true;
4093 }
4094 }
4095 }
4096 }
4097
4098 Mesh *result = nullptr;
4099 if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
4100 if (needs_viewport_update) {
4101 /* Return generated geometry depending on domain type. */
4102 if (fmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) {
4103 result = create_liquid_geometry(fmd->domain, scene, mesh, ob);
4104 }
4105 if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) {
4106 result = create_smoke_geometry(fmd->domain, mesh, ob);
4107 }
4108 }
4109
4110 /* Clear flag outside of locked block (above). */
4116 }
4117
4118 if (!result) {
4120 }
4121 else {
4123 }
4124
4125 /* Liquid simulation has a texture space that based on the bounds of the fluid mesh.
4126 * This does not seem particularly useful, but it's backwards compatible.
4127 *
4128 * Smoke simulation needs a texture space relative to the adaptive domain bounds, not the
4129 * original mesh. So recompute it at this point in the modifier stack. See #58492. */
4131
4132 return result;
4133}
4134
4135static float calc_voxel_transp(
4136 float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct)
4137{
4138 const size_t index = manta_get_index(pixel[0], res[0], pixel[1], res[1], pixel[2]);
4139
4140 /* `T_ray *= T_vox`. */
4141 *t_ray *= expf(input[index] * correct);
4142
4143 if (result[index] < 0.0f) {
4144 result[index] = *t_ray;
4145 }
4146
4147 return *t_ray;
4148}
4149
4150static void bresenham_linie_3D(int x1,
4151 int y1,
4152 int z1,
4153 int x2,
4154 int y2,
4155 int z2,
4156 float *t_ray,
4158 float *result,
4159 float *input,
4160 int res[3],
4161 float correct)
4162{
4163 int dx, dy, dz, i, l, m, n, x_inc, y_inc, z_inc, err_1, err_2, dx2, dy2, dz2;
4164 int pixel[3];
4165
4166 pixel[0] = x1;
4167 pixel[1] = y1;
4168 pixel[2] = z1;
4169
4170 dx = x2 - x1;
4171 dy = y2 - y1;
4172 dz = z2 - z1;
4173
4174 x_inc = (dx < 0) ? -1 : 1;
4175 l = abs(dx);
4176 y_inc = (dy < 0) ? -1 : 1;
4177 m = abs(dy);
4178 z_inc = (dz < 0) ? -1 : 1;
4179 n = abs(dz);
4180 dx2 = l << 1;
4181 dy2 = m << 1;
4182 dz2 = n << 1;
4183
4184 if ((l >= m) && (l >= n)) {
4185 err_1 = dy2 - l;
4186 err_2 = dz2 - l;
4187 for (i = 0; i < l; i++) {
4188 if (cb(result, input, res, pixel, t_ray, correct) <= FLT_EPSILON) {
4189 break;
4190 }
4191 if (err_1 > 0) {
4192 pixel[1] += y_inc;
4193 err_1 -= dx2;
4194 }
4195 if (err_2 > 0) {
4196 pixel[2] += z_inc;
4197 err_2 -= dx2;
4198 }
4199 err_1 += dy2;
4200 err_2 += dz2;
4201 pixel[0] += x_inc;
4202 }
4203 }
4204 else if ((m >= l) && (m >= n)) {
4205 err_1 = dx2 - m;
4206 err_2 = dz2 - m;
4207 for (i = 0; i < m; i++) {
4208 if (cb(result, input, res, pixel, t_ray, correct) <= FLT_EPSILON) {
4209 break;
4210 }
4211 if (err_1 > 0) {
4212 pixel[0] += x_inc;
4213 err_1 -= dy2;
4214 }
4215 if (err_2 > 0) {
4216 pixel[2] += z_inc;
4217 err_2 -= dy2;
4218 }
4219 err_1 += dx2;
4220 err_2 += dz2;
4221 pixel[1] += y_inc;
4222 }
4223 }
4224 else {
4225 err_1 = dy2 - n;
4226 err_2 = dx2 - n;
4227 for (i = 0; i < n; i++) {
4228 if (cb(result, input, res, pixel, t_ray, correct) <= FLT_EPSILON) {
4229 break;
4230 }
4231 if (err_1 > 0) {
4232 pixel[1] += y_inc;
4233 err_1 -= dz2;
4234 }
4235 if (err_2 > 0) {
4236 pixel[0] += x_inc;
4237 err_2 -= dz2;
4238 }
4239 err_1 += dy2;
4240 err_2 += dx2;
4241 pixel[2] += z_inc;
4242 }
4243 }
4244 cb(result, input, res, pixel, t_ray, correct);
4245}
4246
4247static void manta_smoke_calc_transparency(FluidDomainSettings *fds,
4248 Scene *scene,
4249 ViewLayer *view_layer)
4250{
4251 float bv[6] = {0};
4252 float light[3];
4253 int slabsize = fds->res[0] * fds->res[1];
4254 float *density = manta_smoke_get_density(fds->fluid);
4255 float *shadow = manta_smoke_get_shadow(fds->fluid);
4256 float correct = -7.0f * fds->dx;
4257
4258 if (!get_light(scene, view_layer, light)) {
4259 return;
4260 }
4261
4262 /* Convert light pos to sim cell space. */
4263 mul_m4_v3(fds->imat, light);
4264 light[0] = (light[0] - fds->p0[0]) / fds->cell_size[0] - 0.5f - float(fds->res_min[0]);
4265 light[1] = (light[1] - fds->p0[1]) / fds->cell_size[1] - 0.5f - float(fds->res_min[1]);
4266 light[2] = (light[2] - fds->p0[2]) / fds->cell_size[2] - 0.5f - float(fds->res_min[2]);
4267
4268 /* Calculate domain bounds in sim cell space. */
4269 /* 0,2,4 = 0.0f */
4270 bv[1] = float(fds->res[0]); /* X */
4271 bv[3] = float(fds->res[1]); /* Y */
4272 bv[5] = float(fds->res[2]); /* Z */
4273
4274 for (int z = 0; z < fds->res[2]; z++) {
4275 size_t index = z * slabsize;
4276
4277 for (int y = 0; y < fds->res[1]; y++) {
4278 for (int x = 0; x < fds->res[0]; x++, index++) {
4279 float voxel_center[3];
4280 float pos[3];
4281 int cell[3];
4282 float t_ray = 1.0;
4283
4284 /* Reset shadow value. */
4285 shadow[index] = -1.0f;
4286
4287 voxel_center[0] = float(x);
4288 voxel_center[1] = float(y);
4289 voxel_center[2] = float(z);
4290
4291 /* Get starting cell (light pos). */
4292 if (BLI_bvhtree_bb_raycast(bv, light, voxel_center, pos) > FLT_EPSILON) {
4293 /* We're outside -> use point on side of domain. */
4294 cell[0] = int(floor(pos[0]));
4295 cell[1] = int(floor(pos[1]));
4296 cell[2] = int(floor(pos[2]));
4297 }
4298 else {
4299 /* We're inside -> use light itself. */
4300 cell[0] = int(floor(light[0]));
4301 cell[1] = int(floor(light[1]));
4302 cell[2] = int(floor(light[2]));
4303 }
4304 /* Clamp within grid bounds */
4305 CLAMP(cell[0], 0, fds->res[0] - 1);
4306 CLAMP(cell[1], 0, fds->res[1] - 1);
4307 CLAMP(cell[2], 0, fds->res[2] - 1);
4308
4309 bresenham_linie_3D(cell[0],
4310 cell[1],
4311 cell[2],
4312 x,
4313 y,
4314 z,
4315 &t_ray,
4316 calc_voxel_transp,
4317 shadow,
4318 density,
4319 fds->res,
4320 correct);
4321
4322 /* Convention -> from a RGBA float array, use G value for t_ray. */
4323 shadow[index] = t_ray;
4324 }
4325 }
4326 }
4327}
4328
4329float BKE_fluid_get_velocity_at(Object *ob, float position[3], float velocity[3])
4330{
4332 zero_v3(velocity);
4333
4334 if (fmd && (fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain && fmd->domain->fluid) {
4335 FluidDomainSettings *fds = fmd->domain;
4336 float time_mult = 25.0f * DT_DEFAULT;
4337 float size_mult = std::max({fds->global_size[0], fds->global_size[1], fds->global_size[2]}) /
4338 std::max({fds->base_res[0], fds->base_res[1], fds->base_res[2]});
4339 float vel_mag;
4340 float density = 0.0f, fuel = 0.0f;
4341 float pos[3];
4342 copy_v3_v3(pos, position);
4343 manta_pos_to_cell(fds, pos);
4344
4345 /* Check if position is outside domain max bounds. */
4346 if (pos[0] < fds->res_min[0] || pos[1] < fds->res_min[1] || pos[2] < fds->res_min[2]) {
4347 return -1.0f;
4348 }
4349 if (pos[0] > fds->res_max[0] || pos[1] > fds->res_max[1] || pos[2] > fds->res_max[2]) {
4350 return -1.0f;
4351 }
4352
4353 /* map pos between 0.0 - 1.0 */
4354 pos[0] = (pos[0] - fds->res_min[0]) / float(fds->res[0]);
4355 pos[1] = (pos[1] - fds->res_min[1]) / float(fds->res[1]);
4356 pos[2] = (pos[2] - fds->res_min[2]) / float(fds->res[2]);
4357
4358 /* Check if position is outside active area. */
4360 if (pos[0] < 0.0f || pos[1] < 0.0f || pos[2] < 0.0f) {
4361 return 0.0f;
4362 }
4363 if (pos[0] > 1.0f || pos[1] > 1.0f || pos[2] > 1.0f) {
4364 return 0.0f;
4365 }
4366 }
4367
4368 /* Get interpolated velocity at given position. */
4369 velocity[0] = BLI_voxel_sample_trilinear(manta_get_velocity_x(fds->fluid), fds->res, pos);
4370 velocity[1] = BLI_voxel_sample_trilinear(manta_get_velocity_y(fds->fluid), fds->res, pos);
4371 velocity[2] = BLI_voxel_sample_trilinear(manta_get_velocity_z(fds->fluid), fds->res, pos);
4372
4373 /* Convert simulation units to Blender units. */
4374 mul_v3_fl(velocity, size_mult);
4375 mul_v3_fl(velocity, time_mult);
4376
4377 /* Convert velocity direction to global space. */
4378 vel_mag = len_v3(velocity);
4379 mul_mat3_m4_v3(fds->obmat, velocity);
4380 normalize_v3(velocity);
4381 mul_v3_fl(velocity, vel_mag);
4382
4383 /* Use max value of fuel or smoke density. */
4385 if (manta_smoke_has_fuel(fds->fluid)) {
4387 }
4388 return std::max(density, fuel);
4389 }
4390 return -1.0f;
4391}
4392
4394{
4395 int flags = 0;
4396
4397 if (fds->fluid) {
4398 if (manta_smoke_has_heat(fds->fluid)) {
4399 flags |= FLUID_DOMAIN_ACTIVE_HEAT;
4400 }
4401 if (manta_smoke_has_fuel(fds->fluid)) {
4402 flags |= FLUID_DOMAIN_ACTIVE_FIRE;
4403 }
4404 if (manta_smoke_has_colors(fds->fluid)) {
4406 }
4407 }
4408
4409 return flags;
4410}
4411
4413 Object *ob,
4414 const char *pset_name,
4415 const char *parts_name,
4416 const char *psys_name,
4417 const int psys_type)
4418{
4419 ParticleSystem *psys;
4420 ParticleSettings *part;
4422
4423 /* add particle system */
4424 part = BKE_particlesettings_add(bmain, pset_name);
4425 psys = MEM_callocN<ParticleSystem>(__func__);
4426
4427 part->type = psys_type;
4428 part->totpart = 0;
4429 part->draw_size = 0.01f; /* Make fluid particles more subtle in viewport. */
4431 part->phystype = PART_PHYS_NO; /* No physics needed, part system only used to display data. */
4432 psys->part = part;
4433 psys->pointcache = BKE_ptcache_add(&psys->ptcaches);
4434 STRNCPY(psys->name, parts_name);
4435 BLI_addtail(&ob->particlesystem, psys);
4436
4437 /* add modifier */
4439 STRNCPY(pfmd->modifier.name, psys_name);
4440 pfmd->psys = psys;
4441 BLI_addtail(&ob->modifiers, pfmd);
4444}
4445
4446void BKE_fluid_particle_system_destroy(Object *ob, const int particle_type)
4447{
4449 ParticleSystem *psys, *next_psys;
4450
4451 for (psys = static_cast<ParticleSystem *>(ob->particlesystem.first); psys; psys = next_psys) {
4452 next_psys = psys->next;
4453 if (psys->part->type == particle_type) {
4454 /* clear modifier */
4455 pfmd = psys_get_modifier(ob, psys);
4458
4459 /* clear particle system */
4460 BLI_remlink(&ob->particlesystem, psys);
4461 psys_free(ob, psys);
4462 }
4463 }
4464}
4465
4467
4468#endif /* WITH_FLUID */
4469
4470/* -------------------------------------------------------------------- */
4475
4477{
4478 settings->cache_frame_start = (value > settings->cache_frame_end) ? settings->cache_frame_end :
4479 value;
4480}
4481
4483{
4484 settings->cache_frame_end = (value < settings->cache_frame_start) ? settings->cache_frame_start :
4485 value;
4486}
4487
4488void BKE_fluid_cachetype_mesh_set(FluidDomainSettings *settings, int cache_mesh_format)
4489{
4490 if (cache_mesh_format == settings->cache_mesh_format) {
4491 return;
4492 }
4493 /* TODO(sebbas): Clear old caches. */
4494 settings->cache_mesh_format = cache_mesh_format;
4495}
4496
4497void BKE_fluid_cachetype_data_set(FluidDomainSettings *settings, int cache_data_format)
4498{
4499 if (cache_data_format == settings->cache_data_format) {
4500 return;
4501 }
4502 /* TODO(sebbas): Clear old caches. */
4503 settings->cache_data_format = cache_data_format;
4504}
4505
4506void BKE_fluid_cachetype_particle_set(FluidDomainSettings *settings, int cache_particle_format)
4507{
4508 if (cache_particle_format == settings->cache_particle_format) {
4509 return;
4510 }
4511 /* TODO(sebbas): Clear old caches. */
4512 settings->cache_particle_format = cache_particle_format;
4513}
4514
4515void BKE_fluid_cachetype_noise_set(FluidDomainSettings *settings, int cache_noise_format)
4516{
4517 if (cache_noise_format == settings->cache_noise_format) {
4518 return;
4519 }
4520 /* TODO(sebbas): Clear old caches. */
4521 settings->cache_noise_format = cache_noise_format;
4522}
4523
4525{
4526 if (clear) {
4527 settings->border_collisions &= value;
4528 }
4529 else {
4530 settings->border_collisions |= value;
4531 }
4532}
4533
4534void BKE_fluid_particles_set(FluidDomainSettings *settings, int value, bool clear)
4535{
4536 if (clear) {
4537 settings->particle_type &= ~value;
4538 }
4539 else {
4540 settings->particle_type |= value;
4541 }
4542}
4543
4544void BKE_fluid_domain_type_set(Object *object, FluidDomainSettings *settings, int type)
4545{
4546 /* Set values for border collision:
4547 * Liquids should have a closed domain, smoke domains should be open. */
4548 if (type == FLUID_DOMAIN_TYPE_GAS) {
4555 object->dt = OB_WIRE;
4556 }
4557 else if (type == FLUID_DOMAIN_TYPE_LIQUID) {
4564 object->dt = OB_SOLID;
4565 }
4566
4567 /* Set actual domain type. */
4568 settings->type = type;
4569}
4570
4571void BKE_fluid_flow_behavior_set(Object * /*object*/, FluidFlowSettings *settings, int behavior)
4572{
4573 settings->behavior = behavior;
4574}
4575
4576void BKE_fluid_flow_type_set(Object *object, FluidFlowSettings *settings, int type)
4577{
4578 /* By default, liquid flow objects should behave like their geometry (geometry behavior),
4579 * gas flow objects should continuously produce smoke (inflow behavior). */
4580 if (type == FLUID_FLOW_TYPE_LIQUID) {
4582 }
4583 else {
4585 }
4586
4587 /* Set actual flow type. */
4588 settings->type = type;
4589}
4590
4591void BKE_fluid_effector_type_set(Object * /*object*/, FluidEffectorSettings *settings, int type)
4592{
4593 settings->type = type;
4594}
4595
4597{
4598 /* Based on the domain type, certain fields are defaulted accordingly if the selected field
4599 * is unsupported. */
4600 const char coba_field = settings->coba_field;
4601 const char data_depth = settings->openvdb_data_depth;
4602
4603 if (settings->type == FLUID_DOMAIN_TYPE_GAS) {
4604 if (ELEM(coba_field,
4609 {
4610 /* Defaulted to density for gas domain. */
4612 }
4613
4614 /* Gas domains do not support VDB mini precision. */
4615 if (data_depth == VDB_PRECISION_MINI_FLOAT) {
4617 }
4618 }
4619 else if (settings->type == FLUID_DOMAIN_TYPE_LIQUID) {
4620 if (ELEM(coba_field,
4628 {
4629 /* Defaulted to phi for liquid domain. */
4631 }
4632 }
4633}
4634
4636
4637/* -------------------------------------------------------------------- */
4642
4644{
4645 if (fmd->domain) {
4646 if (fmd->domain->fluid) {
4647#ifdef WITH_FLUID
4648 manta_free(fmd->domain->fluid);
4649#endif
4650 }
4651
4652 if (fmd->domain->fluid_mutex) {
4653 BLI_rw_mutex_free(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex));
4654 }
4655
4657
4660 fmd->domain->point_cache[0] = nullptr;
4661 }
4662
4663 if (fmd->domain->coba) {
4664 MEM_freeN(fmd->domain->coba);
4665 }
4666
4667 MEM_freeN(fmd->domain);
4668 fmd->domain = nullptr;
4669 }
4670}
4671
4673{
4674 if (fmd->flow) {
4675 if (fmd->flow->mesh) {
4676 BKE_id_free(nullptr, fmd->flow->mesh);
4677 }
4678 fmd->flow->mesh = nullptr;
4679
4681 fmd->flow->numverts = 0;
4683
4684 MEM_freeN(fmd->flow);
4685 fmd->flow = nullptr;
4686 }
4687}
4688
4690{
4691 if (fmd->effector) {
4692 if (fmd->effector->mesh) {
4693 BKE_id_free(nullptr, fmd->effector->mesh);
4694 }
4695 fmd->effector->mesh = nullptr;
4696
4698 fmd->effector->numverts = 0;
4700
4701 MEM_freeN(fmd->effector);
4702 fmd->effector = nullptr;
4703 }
4704}
4705
4706static void fluid_modifier_reset_ex(FluidModifierData *fmd, bool need_lock)
4707{
4708 if (!fmd) {
4709 return;
4710 }
4711
4712 if (fmd->domain) {
4713 if (fmd->domain->fluid) {
4714 if (need_lock) {
4715 BLI_rw_mutex_lock(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex),
4717 }
4718
4719#ifdef WITH_FLUID
4720 manta_free(fmd->domain->fluid);
4721#endif
4722 fmd->domain->fluid = nullptr;
4723
4724 if (need_lock) {
4725 BLI_rw_mutex_unlock(static_cast<ThreadRWMutex *>(fmd->domain->fluid_mutex));
4726 }
4727 }
4728
4729 fmd->time = -1;
4730 fmd->domain->total_cells = 0;
4731 fmd->domain->active_fields = 0;
4732 }
4733 else if (fmd->flow) {
4735 fmd->flow->numverts = 0;
4737 }
4738 else if (fmd->effector) {
4740 fmd->effector->numverts = 0;
4742 }
4743}
4744
4749
4751{
4752 if (!fmd) {
4753 return;
4754 }
4755
4759}
4760
4762{
4763 if (!fmd) {
4764 return;
4765 }
4766
4767 if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
4768 if (fmd->domain) {
4770 }
4771
4773 fmd->domain->fmd = fmd;
4774
4775 /* Turn off incompatible options. */
4776#ifndef WITH_OPENVDB
4780#endif
4781#ifndef WITH_OPENVDB_BLOSC
4783#endif
4784
4787
4788 char cache_name[64];
4789 BKE_fluid_cache_new_name_for_current_session(sizeof(cache_name), cache_name);
4791 fmd->domain->cache_directory, sizeof(fmd->domain->cache_directory), cache_name);
4792
4793 /* pointcache options */
4794 fmd->domain->point_cache[0] = BKE_ptcache_add(&(fmd->domain->ptcaches[0]));
4796 fmd->domain->point_cache[0]->step = 1;
4797 fmd->domain->point_cache[1] = nullptr; /* Deprecated */
4798 }
4799 else if (fmd->type & MOD_FLUID_TYPE_FLOW) {
4800 if (fmd->flow) {
4802 }
4803
4805 fmd->flow->fmd = fmd;
4806 }
4807 else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
4808 if (fmd->effector) {
4810 }
4811
4813 fmd->effector->fmd = fmd;
4814 }
4815}
4816
4818{
4819 tfmd->type = fmd->type;
4820 tfmd->time = fmd->time;
4821
4823
4824 if (tfmd->domain) {
4825 FluidDomainSettings *tfds = tfmd->domain;
4826 FluidDomainSettings *fds = fmd->domain;
4827
4828 /* domain object data */
4829 tfds->fluid_group = fds->fluid_group;
4830 tfds->force_group = fds->force_group;
4831 tfds->effector_group = fds->effector_group;
4832 if (tfds->effector_weights) {
4834 }
4835 tfds->effector_weights = static_cast<EffectorWeights *>(MEM_dupallocN(fds->effector_weights));
4836
4837 /* adaptive domain options */
4838 tfds->adapt_margin = fds->adapt_margin;
4839 tfds->adapt_res = fds->adapt_res;
4840 tfds->adapt_threshold = fds->adapt_threshold;
4841
4842 /* fluid domain options */
4843 tfds->maxres = fds->maxres;
4844 tfds->solver_res = fds->solver_res;
4846 tfds->flags = fds->flags;
4847 tfds->gravity[0] = fds->gravity[0];
4848 tfds->gravity[1] = fds->gravity[1];
4849 tfds->gravity[2] = fds->gravity[2];
4850 tfds->active_fields = fds->active_fields;
4851 tfds->type = fds->type;
4852 tfds->boundary_width = fds->boundary_width;
4853
4854 /* smoke domain options */
4855 tfds->alpha = fds->alpha;
4856 tfds->beta = fds->beta;
4857 tfds->diss_speed = fds->diss_speed;
4858 tfds->vorticity = fds->vorticity;
4860
4861 /* flame options */
4862 tfds->burning_rate = fds->burning_rate;
4863 tfds->flame_smoke = fds->flame_smoke;
4864 tfds->flame_vorticity = fds->flame_vorticity;
4865 tfds->flame_ignition = fds->flame_ignition;
4866 tfds->flame_max_temp = fds->flame_max_temp;
4868
4869 /* noise options */
4870 tfds->noise_strength = fds->noise_strength;
4871 tfds->noise_pos_scale = fds->noise_pos_scale;
4872 tfds->noise_time_anim = fds->noise_time_anim;
4873 tfds->noise_scale = fds->noise_scale;
4874
4875 /* liquid domain options */
4876 tfds->flip_ratio = fds->flip_ratio;
4878 tfds->particle_number = fds->particle_number;
4881 tfds->particle_radius = fds->particle_radius;
4887
4888 /* viscosity options */
4889 tfds->viscosity_value = fds->viscosity_value;
4890
4891 /* Diffusion options. */
4892 tfds->surface_tension = fds->surface_tension;
4893 tfds->viscosity_base = fds->viscosity_base;
4895
4896 /* mesh options */
4902 tfds->mesh_scale = fds->mesh_scale;
4903 tfds->mesh_generator = fds->mesh_generator;
4904
4905 /* secondary particle options */
4906 tfds->sndparticle_k_b = fds->sndparticle_k_b;
4907 tfds->sndparticle_k_d = fds->sndparticle_k_d;
4922 tfds->particle_type = fds->particle_type;
4923 tfds->particle_scale = fds->particle_scale;
4924
4925 /* fluid guide options */
4926 tfds->guide_parent = fds->guide_parent;
4927 tfds->guide_alpha = fds->guide_alpha;
4928 tfds->guide_beta = fds->guide_beta;
4930 copy_v3_v3_int(tfds->guide_res, fds->guide_res);
4931 tfds->guide_source = fds->guide_source;
4932
4933 /* cache options */
4935 tfds->cache_frame_end = fds->cache_frame_end;
4942 tfds->cache_flag = fds->cache_flag;
4943 tfds->cache_type = fds->cache_type;
4949
4950 /* time options */
4951 tfds->time_scale = fds->time_scale;
4952 tfds->cfl_condition = fds->cfl_condition;
4955
4956 /* display options */
4958 tfds->slice_axis = fds->slice_axis;
4959 tfds->interp_method = fds->interp_method;
4960 tfds->draw_velocity = fds->draw_velocity;
4961 tfds->slice_per_voxel = fds->slice_per_voxel;
4962 tfds->slice_depth = fds->slice_depth;
4964 tfds->show_gridlines = fds->show_gridlines;
4965 if (fds->coba) {
4966 tfds->coba = static_cast<ColorBand *>(MEM_dupallocN(fds->coba));
4967 }
4968 tfds->vector_scale = fds->vector_scale;
4970 tfds->vector_field = fds->vector_field;
4973 tfds->use_coba = fds->use_coba;
4974 tfds->coba_field = fds->coba_field;
4975 tfds->grid_scale = fds->grid_scale;
4981
4982 /* -- Deprecated / unused options (below)-- */
4983
4984 /* pointcache options */
4985 BKE_ptcache_free_list(&(tfds->ptcaches[0]));
4987 /* Share the cache with the original object's modifier. */
4989 tfds->point_cache[0] = fds->point_cache[0];
4990 tfds->ptcaches[0] = fds->ptcaches[0];
4991 }
4992 else {
4994 &(tfds->ptcaches[0]), &(fds->ptcaches[0]), flag);
4995 }
4996
4997 /* OpenVDB cache options */
4999 tfds->clipping = fds->clipping;
5001
5002 /* Render options. */
5003 tfds->velocity_scale = fds->velocity_scale;
5004 }
5005 else if (tfmd->flow) {
5006 FluidFlowSettings *tffs = tfmd->flow;
5007 FluidFlowSettings *ffs = fmd->flow;
5008
5009 /* NOTE: This is dangerous, as it will generate invalid data in case we are copying between
5010 * different objects. Extra external code has to be called then to ensure proper remapping of
5011 * that pointer. See e.g. `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */
5012 tffs->psys = ffs->psys;
5013 tffs->noise_texture = ffs->noise_texture;
5014
5015 /* initial velocity */
5016 tffs->vel_multi = ffs->vel_multi;
5017 tffs->vel_normal = ffs->vel_normal;
5018 tffs->vel_random = ffs->vel_random;
5019 tffs->vel_coord[0] = ffs->vel_coord[0];
5020 tffs->vel_coord[1] = ffs->vel_coord[1];
5021 tffs->vel_coord[2] = ffs->vel_coord[2];
5022
5023 /* emission */
5024 tffs->density = ffs->density;
5025 copy_v3_v3(tffs->color, ffs->color);
5026 tffs->fuel_amount = ffs->fuel_amount;
5027 tffs->temperature = ffs->temperature;
5028 tffs->volume_density = ffs->volume_density;
5030 tffs->particle_size = ffs->particle_size;
5031 tffs->subframes = ffs->subframes;
5032
5033 /* texture control */
5034 tffs->texture_size = ffs->texture_size;
5035 tffs->texture_offset = ffs->texture_offset;
5036 STRNCPY(tffs->uvlayer_name, ffs->uvlayer_name);
5037 tffs->vgroup_density = ffs->vgroup_density;
5038
5039 tffs->type = ffs->type;
5040 tffs->behavior = ffs->behavior;
5041 tffs->source = ffs->source;
5042 tffs->texture_type = ffs->texture_type;
5043 tffs->flags = ffs->flags;
5044 }
5045 else if (tfmd->effector) {
5046 FluidEffectorSettings *tfes = tfmd->effector;
5047 FluidEffectorSettings *fes = fmd->effector;
5048
5050 tfes->type = fes->type;
5051 tfes->flags = fes->flags;
5052 tfes->subframes = fes->subframes;
5053
5054 /* guide options */
5055 tfes->guide_mode = fes->guide_mode;
5056 tfes->vel_multi = fes->vel_multi;
5057 }
5058}
5059
5061{
5062 static int counter = 1;
5063 BLI_snprintf(r_name, maxlen, FLUID_DOMAIN_DIR_DEFAULT "_%x", BLI_hash_int(counter));
5064 counter++;
5065}
5066
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.
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
support for deformation groups and hooks.
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:763
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:1107
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)
@ LIB_ID_COPY_SET_COPIED_ON_WRITE
void BKE_id_free(Main *bmain, void *idv)
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:2153
void psys_sim_data_free(struct ParticleSimulationData *sim)
Definition particle.cc:632
void psys_free(struct Object *ob, struct ParticleSystem *psys)
Definition particle.cc:977
struct ParticleSettings * BKE_particlesettings_add(struct Main *bmain, const char *name)
Definition particle.cc:4086
void psys_sim_data_init(struct ParticleSimulationData *sim)
Definition particle.cc:591
bool psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, bool always)
Definition particle.cc:4877
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:2367
void BKE_texture_get_value(struct Tex *texture, const float *tex_co, struct TexResult *texres, bool use_color_management)
Definition texture.cc:712
#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:373
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:688
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:182
float DEG_get_ctime(const Depsgraph *graph)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
ViewLayer * DEG_get_evaluated_view_layer(const Depsgraph *graph)
@ CD_PROP_FLOAT2
#define DNA_struct_default_alloc(struct_name)
#define FLUID_DOMAIN_DIR_DATA
#define FLUID_DOMAIN_DIR_PARTICLES
struct FluidFlowSettings FluidFlowSettings
@ FLUID_EFFECTOR_GUIDE_MAX
@ FLUID_EFFECTOR_GUIDE_OVERRIDE
@ FLUID_EFFECTOR_GUIDE_AVERAGED
@ FLUID_EFFECTOR_GUIDE_MIN
#define FLUID_DOMAIN_DIR_DEFAULT
@ 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_FLOW_BEHAVIOR_GEOMETRY
@ FLUID_FLOW_BEHAVIOR_OUTFLOW
@ FLUID_FLOW_BEHAVIOR_INFLOW
@ VDB_COMPRESSION_ZIP
@ FLUID_FLOW_TYPE_FIRE
@ FLUID_FLOW_TYPE_SMOKEFIRE
@ FLUID_FLOW_TYPE_LIQUID
@ FLUID_FLOW_TYPE_SMOKE
#define FLUID_DOMAIN_DIR_MESH
#define FLUID_DOMAIN_DIR_GUIDE
#define FLUID_DOMAIN_DIR_SCRIPT
@ 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
@ FLUID_DOMAIN_FILE_UNI
@ 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
@ 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
@ FLUID_DOMAIN_TYPE_GAS
@ FLUID_DOMAIN_TYPE_LIQUID
@ FLUID_DOMAIN_BORDER_BOTTOM
@ FLUID_DOMAIN_BORDER_LEFT
@ FLUID_DOMAIN_BORDER_RIGHT
@ FLUID_DOMAIN_BORDER_FRONT
@ FLUID_DOMAIN_BORDER_TOP
@ FLUID_DOMAIN_BORDER_BACK
@ FLUID_DOMAIN_CACHE_ALL
@ FLUID_DOMAIN_CACHE_REPLAY
@ FLUID_DOMAIN_CACHE_MODULAR
@ 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
struct FluidEffectorSettings FluidEffectorSettings
#define FLUID_DOMAIN_DIR_CONFIG
@ VDB_PRECISION_MINI_FLOAT
@ VDB_PRECISION_HALF_FLOAT
@ FLUID_FLOW_TEXTURE_MAP_AUTO
@ FLUID_FLOW_SOURCE_PARTICLES
@ FLUID_FLOW_SOURCE_MESH
@ FLUID_DOMAIN_GUIDE_SRC_DOMAIN
struct FluidDomainSettings FluidDomainSettings
@ FLUID_EFFECTOR_NEEDS_UPDATE
@ FLUID_EFFECTOR_USE_EFFEC
@ FLUID_EFFECTOR_USE_PLANE_INIT
#define FLUID_DOMAIN_DIR_NOISE
@ 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
@ eModifierType_ParticleSystem
@ eModifierType_Explode
@ eModifierType_Cloth
@ eModifierType_Fluid
@ eModifierType_ShapeKey
@ eModifierType_Ocean
@ eModifierType_Nodes
@ eModifierType_DynamicPaint
@ eModifierType_Softbody
@ MOD_FLUID_TYPE_EFFEC
@ MOD_FLUID_TYPE_DOMAIN
@ MOD_FLUID_TYPE_FLOW
@ OB_WIRE
@ OB_SOLID
@ PFIELD_FLUIDFLOW
Object is a sort of wrapper for general info.
@ OB_LAMP
@ PART_DRAW_COL_VEL
@ PART_EMITTER
@ PART_FLUID
@ PART_HAIR
@ PART_PHYS_NO
@ PARS_NO_DISP
@ PARS_UNEXIST
@ PTCACHE_OUTDATED
@ PTCACHE_DISK_CACHE
Types and defines for representing Rigid Body entities.
@ RBO_TYPE_ACTIVE
struct Scene Scene
@ PHYS_GLOBAL_GRAVITY
#define FPS
Read Guarded memory(de)allocation.
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, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
#define expf(x)
#define floorf(x)
#define fabsf(x)
#define sqrtf(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:4515
void BKE_fluid_flow_behavior_set(Object *, FluidFlowSettings *settings, int behavior)
Definition fluid.cc:4571
static void fluid_modifier_reset_ex(FluidModifierData *fmd, bool need_lock)
Definition fluid.cc:4706
void BKE_fluid_cachetype_particle_set(FluidDomainSettings *settings, int cache_particle_format)
Definition fluid.cc:4506
static void fluid_modifier_freeDomain(FluidModifierData *fmd)
Definition fluid.cc:4643
static void fluid_modifier_freeFlow(FluidModifierData *fmd)
Definition fluid.cc:4672
void BKE_fluid_domain_type_set(Object *object, FluidDomainSettings *settings, int type)
Definition fluid.cc:4544
void BKE_fluid_particles_set(FluidDomainSettings *settings, int value, bool clear)
Definition fluid.cc:4534
void BKE_fluid_cachetype_data_set(FluidDomainSettings *settings, int cache_data_format)
Definition fluid.cc:4497
static void fluid_modifier_freeEffector(FluidModifierData *fmd)
Definition fluid.cc:4689
void BKE_fluid_collisionextents_set(FluidDomainSettings *settings, int value, bool clear)
Definition fluid.cc:4524
void BKE_fluid_modifier_copy(const FluidModifierData *fmd, FluidModifierData *tfmd, const int flag)
Definition fluid.cc:4817
void BKE_fluid_modifier_create_type_data(FluidModifierData *fmd)
Definition fluid.cc:4761
void BKE_fluid_cache_new_name_for_current_session(int maxlen, char *r_name)
Definition fluid.cc:5060
void BKE_fluid_cache_endframe_set(FluidDomainSettings *settings, int value)
Definition fluid.cc:4482
void BKE_fluid_fields_sanitize(FluidDomainSettings *settings)
Definition fluid.cc:4596
void BKE_fluid_cache_startframe_set(FluidDomainSettings *settings, int value)
Definition fluid.cc:4476
void BKE_fluid_modifier_reset(FluidModifierData *fmd)
Definition fluid.cc:4745
void BKE_fluid_flow_type_set(Object *object, FluidFlowSettings *settings, int type)
Definition fluid.cc:4576
void BKE_fluid_cachetype_mesh_set(FluidDomainSettings *settings, int cache_mesh_format)
Definition fluid.cc:4488
void BKE_fluid_effector_type_set(Object *, FluidEffectorSettings *settings, int type)
Definition fluid.cc:4591
void BKE_fluid_modifier_free(FluidModifierData *fmd)
Definition fluid.cc:4750
uint pos
#define input
#define pow
#define abs
#define floor
#define ceil
#define printf(...)
#define output
#define MEM_SAFE_FREE(v)
#define LOG(severity)
Definition log.h:32
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< int32_t, 3 > int3
std::mutex Mutex
Definition BLI_mutex.hh:47
VecBase< float, 3 > float3
static void update_velocities(PTCacheEdit *edit)
#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
CustomData corner_data
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:139