The goal is to solve confusion of the "All rights reserved" for licensing
code under an open-source license.
The phrase "All rights reserved" comes from a historical convention that
required this phrase for the copyright protection to apply. This convention
is no longer relevant.
However, even though the phrase has no meaning in establishing the copyright
it has not lost meaning in terms of licensing.
This change makes it so code under the Blender Foundation copyright does
not use "all rights reserved". This is also how the GPL license itself
states how to apply it to the source code:
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software ...
This change does not change copyright notice in cases when the copyright
is dual (BF and an author), or just an author of the code. It also does
mot change copyright which is inherited from NaN Holding BV as it needs
some further investigation about what is the proper way to handle it.
853 lines
25 KiB
C
853 lines
25 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright Blender Foundation */
|
|
|
|
/** \file
|
|
* \ingroup edphys
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
/* types */
|
|
#include "DNA_action_types.h"
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_path_util.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_fluid.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_modifier.h"
|
|
#include "BKE_report.h"
|
|
#include "BKE_screen.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "ED_object.h"
|
|
#include "ED_screen.h"
|
|
#include "PIL_time.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "physics_intern.h" /* own include */
|
|
|
|
#include "DNA_fluid_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#define FLUID_JOB_BAKE_ALL "FLUID_OT_bake_all"
|
|
#define FLUID_JOB_BAKE_DATA "FLUID_OT_bake_data"
|
|
#define FLUID_JOB_BAKE_NOISE "FLUID_OT_bake_noise"
|
|
#define FLUID_JOB_BAKE_MESH "FLUID_OT_bake_mesh"
|
|
#define FLUID_JOB_BAKE_PARTICLES "FLUID_OT_bake_particles"
|
|
#define FLUID_JOB_BAKE_GUIDES "FLUID_OT_bake_guides"
|
|
#define FLUID_JOB_FREE_ALL "FLUID_OT_free_all"
|
|
#define FLUID_JOB_FREE_DATA "FLUID_OT_free_data"
|
|
#define FLUID_JOB_FREE_NOISE "FLUID_OT_free_noise"
|
|
#define FLUID_JOB_FREE_MESH "FLUID_OT_free_mesh"
|
|
#define FLUID_JOB_FREE_PARTICLES "FLUID_OT_free_particles"
|
|
#define FLUID_JOB_FREE_GUIDES "FLUID_OT_free_guides"
|
|
#define FLUID_JOB_BAKE_PAUSE "FLUID_OT_pause_bake"
|
|
|
|
typedef struct FluidJob {
|
|
/* from wmJob */
|
|
void *owner;
|
|
bool *stop, *do_update;
|
|
float *progress;
|
|
const char *type;
|
|
const char *name;
|
|
|
|
struct Main *bmain;
|
|
Scene *scene;
|
|
Depsgraph *depsgraph;
|
|
Object *ob;
|
|
|
|
FluidModifierData *fmd;
|
|
|
|
int success;
|
|
double start;
|
|
|
|
int *pause_frame;
|
|
} FluidJob;
|
|
|
|
static inline bool fluid_is_bake_all(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_BAKE_ALL);
|
|
}
|
|
static inline bool fluid_is_bake_data(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_BAKE_DATA);
|
|
}
|
|
static inline bool fluid_is_bake_noise(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_BAKE_NOISE);
|
|
}
|
|
static inline bool fluid_is_bake_mesh(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_BAKE_MESH);
|
|
}
|
|
static inline bool fluid_is_bake_particle(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_BAKE_PARTICLES);
|
|
}
|
|
static inline bool fluid_is_bake_guiding(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_BAKE_GUIDES);
|
|
}
|
|
static inline bool fluid_is_free_all(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_FREE_ALL);
|
|
}
|
|
static inline bool fluid_is_free_data(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_FREE_DATA);
|
|
}
|
|
static inline bool fluid_is_free_noise(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_FREE_NOISE);
|
|
}
|
|
static inline bool fluid_is_free_mesh(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_FREE_MESH);
|
|
}
|
|
static inline bool fluid_is_free_particles(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_FREE_PARTICLES);
|
|
}
|
|
static inline bool fluid_is_free_guiding(FluidJob *job)
|
|
{
|
|
return STREQ(job->type, FLUID_JOB_FREE_GUIDES);
|
|
}
|
|
|
|
static bool fluid_initjob(
|
|
bContext *C, FluidJob *job, wmOperator *op, char *error_msg, int error_size)
|
|
{
|
|
FluidModifierData *fmd = NULL;
|
|
FluidDomainSettings *fds;
|
|
Object *ob = ED_object_active_context(C);
|
|
|
|
fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
|
|
if (!fmd) {
|
|
BLI_strncpy(error_msg, N_("Bake failed: no Fluid modifier found"), error_size);
|
|
return false;
|
|
}
|
|
fds = fmd->domain;
|
|
if (!fds) {
|
|
BLI_strncpy(error_msg, N_("Bake failed: invalid domain"), error_size);
|
|
return false;
|
|
}
|
|
|
|
job->bmain = CTX_data_main(C);
|
|
job->scene = CTX_data_scene(C);
|
|
job->depsgraph = CTX_data_depsgraph_pointer(C);
|
|
job->ob = ob;
|
|
job->fmd = fmd;
|
|
job->type = op->type->idname;
|
|
job->name = op->type->name;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool fluid_validatepaths(FluidJob *job, ReportList *reports)
|
|
{
|
|
FluidDomainSettings *fds = job->fmd->domain;
|
|
char temp_dir[FILE_MAX];
|
|
temp_dir[0] = '\0';
|
|
bool is_relative = false;
|
|
|
|
const char *relbase = BKE_modifier_path_relbase(job->bmain, job->ob);
|
|
|
|
/* We do not accept empty paths, they can end in random places silently, see #51176. */
|
|
if (fds->cache_directory[0] == '\0') {
|
|
char cache_name[64];
|
|
BKE_fluid_cache_new_name_for_current_session(sizeof(cache_name), cache_name);
|
|
BKE_modifier_path_init(fds->cache_directory, sizeof(fds->cache_directory), cache_name);
|
|
BKE_reportf(reports,
|
|
RPT_WARNING,
|
|
"Fluid: Empty cache path, reset to default '%s'",
|
|
fds->cache_directory);
|
|
}
|
|
|
|
BLI_strncpy(temp_dir, fds->cache_directory, FILE_MAXDIR);
|
|
is_relative = BLI_path_abs(temp_dir, relbase);
|
|
|
|
/* Ensure whole path exists */
|
|
const bool dir_exists = BLI_dir_create_recursive(temp_dir);
|
|
|
|
/* We change path to some presumably valid default value, but do not allow bake process to
|
|
* continue, this gives user chance to set manually another path. */
|
|
if (!dir_exists) {
|
|
char cache_name[64];
|
|
BKE_fluid_cache_new_name_for_current_session(sizeof(cache_name), cache_name);
|
|
BKE_modifier_path_init(fds->cache_directory, sizeof(fds->cache_directory), cache_name);
|
|
|
|
BKE_reportf(reports,
|
|
RPT_ERROR,
|
|
"Fluid: Could not create cache directory '%s', reset to default '%s'",
|
|
temp_dir,
|
|
fds->cache_directory);
|
|
|
|
/* Ensure whole path exists and is writable. */
|
|
if (!BLI_dir_create_recursive(temp_dir)) {
|
|
BKE_reportf(reports,
|
|
RPT_ERROR,
|
|
"Fluid: Could not use default cache directory '%s', "
|
|
"please define a valid cache path manually",
|
|
temp_dir);
|
|
return false;
|
|
}
|
|
/* Copy final dir back into domain settings */
|
|
BLI_strncpy(fds->cache_directory, temp_dir, FILE_MAXDIR);
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Change path back to is original state (ie relative or absolute). */
|
|
if (is_relative) {
|
|
BLI_path_rel(temp_dir, relbase);
|
|
}
|
|
|
|
/* Copy final dir back into domain settings */
|
|
BLI_strncpy(fds->cache_directory, temp_dir, FILE_MAXDIR);
|
|
return true;
|
|
}
|
|
|
|
static void fluid_bake_free(void *customdata)
|
|
{
|
|
FluidJob *job = customdata;
|
|
MEM_freeN(job);
|
|
}
|
|
|
|
static void fluid_bake_sequence(FluidJob *job)
|
|
{
|
|
FluidDomainSettings *fds = job->fmd->domain;
|
|
Scene *scene = job->scene;
|
|
int frame = 1, orig_frame;
|
|
int frames;
|
|
int *pause_frame = NULL;
|
|
bool is_first_frame;
|
|
|
|
frames = fds->cache_frame_end - fds->cache_frame_start + 1;
|
|
|
|
if (frames <= 0) {
|
|
BLI_strncpy(fds->error, N_("No frames to bake"), sizeof(fds->error));
|
|
return;
|
|
}
|
|
|
|
/* Show progress bar. */
|
|
if (job->do_update) {
|
|
*(job->do_update) = true;
|
|
}
|
|
|
|
/* Get current pause frame (pointer) - depending on bake type. */
|
|
pause_frame = job->pause_frame;
|
|
|
|
/* Set frame to start point (depending on current pause frame value). */
|
|
is_first_frame = ((*pause_frame) == 0);
|
|
frame = is_first_frame ? fds->cache_frame_start : (*pause_frame);
|
|
|
|
/* Save orig frame and update scene frame. */
|
|
orig_frame = scene->r.cfra;
|
|
scene->r.cfra = frame;
|
|
|
|
/* Loop through selected frames. */
|
|
for (; frame <= fds->cache_frame_end; frame++) {
|
|
const float progress = (frame - fds->cache_frame_start) / (float)frames;
|
|
|
|
/* Keep track of pause frame - needed to init future loop. */
|
|
(*pause_frame) = frame;
|
|
|
|
/* If user requested stop, quit baking. */
|
|
if (G.is_break) {
|
|
job->success = 0;
|
|
return;
|
|
}
|
|
|
|
/* Update progress bar. */
|
|
if (job->do_update) {
|
|
*(job->do_update) = true;
|
|
}
|
|
if (job->progress) {
|
|
*(job->progress) = progress;
|
|
}
|
|
|
|
scene->r.cfra = frame;
|
|
|
|
/* Update animation system. */
|
|
ED_update_for_newframe(job->bmain, job->depsgraph);
|
|
|
|
/* If user requested stop, quit baking. */
|
|
if (G.is_break) {
|
|
job->success = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Restore frame position that we were on before bake. */
|
|
scene->r.cfra = orig_frame;
|
|
}
|
|
|
|
static void fluid_bake_endjob(void *customdata)
|
|
{
|
|
FluidJob *job = customdata;
|
|
FluidDomainSettings *fds = job->fmd->domain;
|
|
|
|
if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) {
|
|
fds->cache_flag &= ~FLUID_DOMAIN_BAKING_NOISE;
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKED_NOISE;
|
|
fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_NOISE;
|
|
}
|
|
if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) {
|
|
fds->cache_flag &= ~FLUID_DOMAIN_BAKING_MESH;
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKED_MESH;
|
|
fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_MESH;
|
|
}
|
|
if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) {
|
|
fds->cache_flag &= ~FLUID_DOMAIN_BAKING_PARTICLES;
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKED_PARTICLES;
|
|
fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_PARTICLES;
|
|
}
|
|
if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) {
|
|
fds->cache_flag &= ~FLUID_DOMAIN_BAKING_GUIDE;
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKED_GUIDE;
|
|
fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_GUIDE;
|
|
}
|
|
if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) {
|
|
fds->cache_flag &= ~FLUID_DOMAIN_BAKING_DATA;
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKED_DATA;
|
|
fds->cache_flag &= ~FLUID_DOMAIN_OUTDATED_DATA;
|
|
}
|
|
DEG_id_tag_update(&job->ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
G.is_rendering = false;
|
|
BKE_spacedata_draw_locks(false);
|
|
WM_set_locked_interface(G_MAIN->wm.first, false);
|
|
|
|
/* Bake was successful:
|
|
* Report for ended bake and how long it took. */
|
|
if (job->success) {
|
|
/* Show bake info. */
|
|
WM_reportf(
|
|
RPT_INFO, "Fluid: %s complete! (%.2f)", job->name, PIL_check_seconds_timer() - job->start);
|
|
}
|
|
else {
|
|
if (fds->error[0] != '\0') {
|
|
WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, fds->error);
|
|
}
|
|
else { /* User canceled the bake. */
|
|
WM_reportf(RPT_WARNING, "Fluid: %s canceled!", job->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fluid_bake_startjob(void *customdata, bool *stop, bool *do_update, float *progress)
|
|
{
|
|
FluidJob *job = customdata;
|
|
FluidDomainSettings *fds = job->fmd->domain;
|
|
|
|
char temp_dir[FILE_MAX];
|
|
const char *relbase = BKE_modifier_path_relbase_from_global(job->ob);
|
|
|
|
job->stop = stop;
|
|
job->do_update = do_update;
|
|
job->progress = progress;
|
|
job->start = PIL_check_seconds_timer();
|
|
job->success = 1;
|
|
|
|
G.is_break = false;
|
|
G.is_rendering = true;
|
|
BKE_spacedata_draw_locks(true);
|
|
|
|
if (fluid_is_bake_noise(job) || fluid_is_bake_all(job)) {
|
|
BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_NOISE);
|
|
BLI_path_abs(temp_dir, relbase);
|
|
BLI_dir_create_recursive(temp_dir); /* Create 'noise' subdir if it does not exist already */
|
|
fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_NOISE | FLUID_DOMAIN_OUTDATED_NOISE);
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKING_NOISE;
|
|
job->pause_frame = &fds->cache_frame_pause_noise;
|
|
}
|
|
if (fluid_is_bake_mesh(job) || fluid_is_bake_all(job)) {
|
|
BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_MESH);
|
|
BLI_path_abs(temp_dir, relbase);
|
|
BLI_dir_create_recursive(temp_dir); /* Create 'mesh' subdir if it does not exist already */
|
|
fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_MESH | FLUID_DOMAIN_OUTDATED_MESH);
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKING_MESH;
|
|
job->pause_frame = &fds->cache_frame_pause_mesh;
|
|
}
|
|
if (fluid_is_bake_particle(job) || fluid_is_bake_all(job)) {
|
|
BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES);
|
|
BLI_path_abs(temp_dir, relbase);
|
|
|
|
/* Create 'particles' subdir if it does not exist already */
|
|
BLI_dir_create_recursive(temp_dir);
|
|
|
|
fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_PARTICLES | FLUID_DOMAIN_OUTDATED_PARTICLES);
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKING_PARTICLES;
|
|
job->pause_frame = &fds->cache_frame_pause_particles;
|
|
}
|
|
if (fluid_is_bake_guiding(job) || fluid_is_bake_all(job)) {
|
|
BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_GUIDE);
|
|
BLI_path_abs(temp_dir, relbase);
|
|
BLI_dir_create_recursive(temp_dir); /* Create 'guiding' subdir if it does not exist already */
|
|
fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_GUIDE | FLUID_DOMAIN_OUTDATED_GUIDE);
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKING_GUIDE;
|
|
job->pause_frame = &fds->cache_frame_pause_guide;
|
|
}
|
|
if (fluid_is_bake_data(job) || fluid_is_bake_all(job)) {
|
|
BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_CONFIG);
|
|
BLI_path_abs(temp_dir, relbase);
|
|
BLI_dir_create_recursive(temp_dir); /* Create 'config' subdir if it does not exist already */
|
|
|
|
BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_DATA);
|
|
BLI_path_abs(temp_dir, relbase);
|
|
BLI_dir_create_recursive(temp_dir); /* Create 'data' subdir if it does not exist already */
|
|
fds->cache_flag &= ~(FLUID_DOMAIN_BAKED_DATA | FLUID_DOMAIN_OUTDATED_DATA);
|
|
fds->cache_flag |= FLUID_DOMAIN_BAKING_DATA;
|
|
job->pause_frame = &fds->cache_frame_pause_data;
|
|
|
|
if (fds->flags & FLUID_DOMAIN_EXPORT_MANTA_SCRIPT) {
|
|
BLI_path_join(temp_dir, sizeof(temp_dir), fds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT);
|
|
BLI_path_abs(temp_dir, relbase);
|
|
BLI_dir_create_recursive(temp_dir); /* Create 'script' subdir if it does not exist already */
|
|
}
|
|
}
|
|
DEG_id_tag_update(&job->ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
fluid_bake_sequence(job);
|
|
|
|
if (do_update) {
|
|
*do_update = true;
|
|
}
|
|
if (stop) {
|
|
*stop = false;
|
|
}
|
|
}
|
|
|
|
static void fluid_free_endjob(void *customdata)
|
|
{
|
|
FluidJob *job = customdata;
|
|
FluidDomainSettings *fds = job->fmd->domain;
|
|
|
|
G.is_rendering = false;
|
|
BKE_spacedata_draw_locks(false);
|
|
WM_set_locked_interface(G_MAIN->wm.first, false);
|
|
|
|
/* Reflect the now empty cache in the viewport too. */
|
|
DEG_id_tag_update(&job->ob->id, ID_RECALC_GEOMETRY);
|
|
|
|
/* Free was successful:
|
|
* Report for ended free job and how long it took */
|
|
if (job->success) {
|
|
/* Show free job info */
|
|
WM_reportf(
|
|
RPT_INFO, "Fluid: %s complete! (%.2f)", job->name, PIL_check_seconds_timer() - job->start);
|
|
}
|
|
else {
|
|
if (fds->error[0] != '\0') {
|
|
WM_reportf(RPT_ERROR, "Fluid: %s failed: %s", job->name, fds->error);
|
|
}
|
|
else { /* User canceled the free job */
|
|
WM_reportf(RPT_WARNING, "Fluid: %s canceled!", job->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fluid_free_startjob(void *customdata, bool *stop, bool *do_update, float *progress)
|
|
{
|
|
FluidJob *job = customdata;
|
|
FluidDomainSettings *fds = job->fmd->domain;
|
|
|
|
job->stop = stop;
|
|
job->do_update = do_update;
|
|
job->progress = progress;
|
|
job->start = PIL_check_seconds_timer();
|
|
job->success = 1;
|
|
|
|
G.is_break = false;
|
|
G.is_rendering = true;
|
|
BKE_spacedata_draw_locks(true);
|
|
|
|
int cache_map = 0;
|
|
|
|
if (fluid_is_free_data(job) || fluid_is_free_all(job)) {
|
|
cache_map |= (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE |
|
|
FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES);
|
|
}
|
|
if (fluid_is_free_noise(job) || fluid_is_free_all(job)) {
|
|
cache_map |= FLUID_DOMAIN_OUTDATED_NOISE;
|
|
}
|
|
if (fluid_is_free_mesh(job) || fluid_is_free_all(job)) {
|
|
cache_map |= FLUID_DOMAIN_OUTDATED_MESH;
|
|
}
|
|
if (fluid_is_free_particles(job) || fluid_is_free_all(job)) {
|
|
cache_map |= FLUID_DOMAIN_OUTDATED_PARTICLES;
|
|
}
|
|
if (fluid_is_free_guiding(job) || fluid_is_free_all(job)) {
|
|
cache_map |= (FLUID_DOMAIN_OUTDATED_DATA | FLUID_DOMAIN_OUTDATED_NOISE |
|
|
FLUID_DOMAIN_OUTDATED_MESH | FLUID_DOMAIN_OUTDATED_PARTICLES |
|
|
FLUID_DOMAIN_OUTDATED_GUIDE);
|
|
}
|
|
#ifdef WITH_FLUID
|
|
BKE_fluid_cache_free(fds, job->ob, cache_map);
|
|
#else
|
|
UNUSED_VARS(fds);
|
|
UNUSED_VARS(cache_map);
|
|
#endif
|
|
|
|
*do_update = true;
|
|
*stop = false;
|
|
|
|
/* Update scene so that viewport shows freed up scene */
|
|
ED_update_for_newframe(job->bmain, job->depsgraph);
|
|
}
|
|
|
|
/***************************** Operators ******************************/
|
|
|
|
static int fluid_bake_exec(struct bContext *C, struct wmOperator *op)
|
|
{
|
|
FluidJob *job = MEM_mallocN(sizeof(FluidJob), "FluidJob");
|
|
char error_msg[256] = "\0";
|
|
|
|
if (!fluid_initjob(C, job, op, error_msg, sizeof(error_msg))) {
|
|
if (error_msg[0]) {
|
|
BKE_report(op->reports, RPT_ERROR, error_msg);
|
|
}
|
|
fluid_bake_free(job);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
if (!fluid_validatepaths(job, op->reports)) {
|
|
fluid_bake_free(job);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
WM_report_banners_cancel(job->bmain);
|
|
|
|
fluid_bake_startjob(job, NULL, NULL, NULL);
|
|
fluid_bake_endjob(job);
|
|
fluid_bake_free(job);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int fluid_bake_invoke(struct bContext *C,
|
|
struct wmOperator *op,
|
|
const wmEvent *UNUSED(_event))
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
FluidJob *job = MEM_mallocN(sizeof(FluidJob), "FluidJob");
|
|
char error_msg[256] = "\0";
|
|
|
|
if (!fluid_initjob(C, job, op, error_msg, sizeof(error_msg))) {
|
|
if (error_msg[0]) {
|
|
BKE_report(op->reports, RPT_ERROR, error_msg);
|
|
}
|
|
fluid_bake_free(job);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
if (!fluid_validatepaths(job, op->reports)) {
|
|
fluid_bake_free(job);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
/* Clear existing banners so that the upcoming progress bar from this job has more room. */
|
|
WM_report_banners_cancel(job->bmain);
|
|
|
|
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
|
CTX_wm_window(C),
|
|
scene,
|
|
"Fluid Bake",
|
|
WM_JOB_PROGRESS,
|
|
WM_JOB_TYPE_OBJECT_SIM_FLUID);
|
|
|
|
WM_jobs_customdata_set(wm_job, job, fluid_bake_free);
|
|
WM_jobs_timer(wm_job, 0.01, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
|
|
WM_jobs_callbacks(wm_job, fluid_bake_startjob, NULL, NULL, fluid_bake_endjob);
|
|
|
|
WM_set_locked_interface(CTX_wm_manager(C), true);
|
|
|
|
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
|
WM_event_add_modal_handler(C, op);
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
static int fluid_bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
|
{
|
|
/* no running blender, remove handler and pass through */
|
|
if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_SIM_FLUID)) {
|
|
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
switch (event->type) {
|
|
case EVT_ESCKEY:
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
static int fluid_free_exec(struct bContext *C, struct wmOperator *op)
|
|
{
|
|
FluidModifierData *fmd = NULL;
|
|
FluidDomainSettings *fds;
|
|
Object *ob = ED_object_active_context(C);
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
/*
|
|
* Get modifier data
|
|
*/
|
|
fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
|
|
if (!fmd) {
|
|
BKE_report(op->reports, RPT_ERROR, "Bake free failed: no Fluid modifier found");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
fds = fmd->domain;
|
|
if (!fds) {
|
|
BKE_report(op->reports, RPT_ERROR, "Bake free failed: invalid domain");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
/* Cannot free data if other bakes currently working */
|
|
if (fmd->domain->cache_flag & (FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKING_NOISE |
|
|
FLUID_DOMAIN_BAKING_MESH | FLUID_DOMAIN_BAKING_PARTICLES)) {
|
|
BKE_report(op->reports, RPT_ERROR, "Bake free failed: pending bake jobs found");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
FluidJob *job = MEM_mallocN(sizeof(FluidJob), "FluidJob");
|
|
job->bmain = CTX_data_main(C);
|
|
job->scene = scene;
|
|
job->depsgraph = CTX_data_depsgraph_pointer(C);
|
|
job->ob = ob;
|
|
job->fmd = fmd;
|
|
job->type = op->type->idname;
|
|
job->name = op->type->name;
|
|
|
|
if (!fluid_validatepaths(job, op->reports)) {
|
|
fluid_bake_free(job);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
/* Clear existing banners so that the upcoming progress bar from this job has more room. */
|
|
WM_report_banners_cancel(job->bmain);
|
|
|
|
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
|
CTX_wm_window(C),
|
|
scene,
|
|
"Fluid Free",
|
|
WM_JOB_PROGRESS,
|
|
WM_JOB_TYPE_OBJECT_SIM_FLUID);
|
|
|
|
WM_jobs_customdata_set(wm_job, job, fluid_bake_free);
|
|
WM_jobs_timer(wm_job, 0.01, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
|
|
WM_jobs_callbacks(wm_job, fluid_free_startjob, NULL, NULL, fluid_free_endjob);
|
|
|
|
WM_set_locked_interface(CTX_wm_manager(C), true);
|
|
|
|
/* Free Fluid Geometry */
|
|
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int fluid_pause_exec(struct bContext *C, struct wmOperator *op)
|
|
{
|
|
FluidModifierData *fmd = NULL;
|
|
FluidDomainSettings *fds;
|
|
Object *ob = ED_object_active_context(C);
|
|
|
|
/*
|
|
* Get modifier data
|
|
*/
|
|
fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
|
|
if (!fmd) {
|
|
BKE_report(op->reports, RPT_ERROR, "Bake free failed: no Fluid modifier found");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
fds = fmd->domain;
|
|
if (!fds) {
|
|
BKE_report(op->reports, RPT_ERROR, "Bake free failed: invalid domain");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
G.is_break = true;
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void FLUID_OT_bake_all(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Bake All";
|
|
ot->description = "Bake Entire Fluid Simulation";
|
|
ot->idname = FLUID_JOB_BAKE_ALL;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_bake_exec;
|
|
ot->invoke = fluid_bake_invoke;
|
|
ot->modal = fluid_bake_modal;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_free_all(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Free All";
|
|
ot->description = "Free Entire Fluid Simulation";
|
|
ot->idname = FLUID_JOB_FREE_ALL;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_free_exec;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_bake_data(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Bake Data";
|
|
ot->description = "Bake Fluid Data";
|
|
ot->idname = FLUID_JOB_BAKE_DATA;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_bake_exec;
|
|
ot->invoke = fluid_bake_invoke;
|
|
ot->modal = fluid_bake_modal;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_free_data(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Free Data";
|
|
ot->description = "Free Fluid Data";
|
|
ot->idname = FLUID_JOB_FREE_DATA;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_free_exec;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_bake_noise(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Bake Noise";
|
|
ot->description = "Bake Fluid Noise";
|
|
ot->idname = FLUID_JOB_BAKE_NOISE;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_bake_exec;
|
|
ot->invoke = fluid_bake_invoke;
|
|
ot->modal = fluid_bake_modal;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_free_noise(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Free Noise";
|
|
ot->description = "Free Fluid Noise";
|
|
ot->idname = FLUID_JOB_FREE_NOISE;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_free_exec;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_bake_mesh(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Bake Mesh";
|
|
ot->description = "Bake Fluid Mesh";
|
|
ot->idname = FLUID_JOB_BAKE_MESH;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_bake_exec;
|
|
ot->invoke = fluid_bake_invoke;
|
|
ot->modal = fluid_bake_modal;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_free_mesh(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Free Mesh";
|
|
ot->description = "Free Fluid Mesh";
|
|
ot->idname = FLUID_JOB_FREE_MESH;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_free_exec;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_bake_particles(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Bake Particles";
|
|
ot->description = "Bake Fluid Particles";
|
|
ot->idname = FLUID_JOB_BAKE_PARTICLES;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_bake_exec;
|
|
ot->invoke = fluid_bake_invoke;
|
|
ot->modal = fluid_bake_modal;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_free_particles(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Free Particles";
|
|
ot->description = "Free Fluid Particles";
|
|
ot->idname = FLUID_JOB_FREE_PARTICLES;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_free_exec;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_bake_guides(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Bake Guides";
|
|
ot->description = "Bake Fluid Guiding";
|
|
ot->idname = FLUID_JOB_BAKE_GUIDES;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_bake_exec;
|
|
ot->invoke = fluid_bake_invoke;
|
|
ot->modal = fluid_bake_modal;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_free_guides(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Free Guides";
|
|
ot->description = "Free Fluid Guiding";
|
|
ot->idname = FLUID_JOB_FREE_GUIDES;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_free_exec;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|
|
|
|
void FLUID_OT_pause_bake(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Pause Bake";
|
|
ot->description = "Pause Bake";
|
|
ot->idname = FLUID_JOB_BAKE_PAUSE;
|
|
|
|
/* api callbacks */
|
|
ot->exec = fluid_pause_exec;
|
|
ot->poll = ED_operator_object_active_editable;
|
|
}
|