Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
# SPDX-FileCopyrightText: 2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC
../include
../../makesrna
../../../../intern/mantaflow/extern
# RNA_prototypes.hh
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
set(INC_SYS
)
set(SRC
dynamicpaint_ops.cc
particle_boids.cc
particle_edit.cc
particle_edit_undo.cc
particle_object.cc
physics_fluid.cc
physics_ops.cc
physics_pointcache.cc
rigidbody_constraint.cc
rigidbody_object.cc
rigidbody_world.cc
particle_edit_utildefines.h
physics_intern.hh
)
set(LIB
PRIVATE bf::blenkernel
PRIVATE bf::blenlib
PRIVATE bf::blentranslation
PRIVATE bf::depsgraph
PRIVATE bf::dna
PRIVATE bf::gpu
PRIVATE bf::intern::clog
PRIVATE bf::intern::guardedalloc
PRIVATE bf::windowmanager
)
if(WITH_MOD_FLUID)
list(APPEND LIB
bf_intern_mantaflow
)
add_definitions(-DWITH_FLUID)
endif()
if(WITH_BULLET)
list(APPEND INC
../../../../intern/rigidbody
)
add_definitions(-DWITH_BULLET)
endif()
blender_add_lib(bf_editor_physics "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.hh
add_dependencies(bf_editor_physics bf_rna)

View File

@@ -0,0 +1,534 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_path_utils.hh"
#include "BLI_string_utf8.h"
#include "BLI_time.h"
#include "BLT_translation.hh"
#include "DNA_dynamicpaint_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_attribute.h"
#include "BKE_attribute.hh"
#include "BKE_context.hh"
#include "BKE_deform.hh"
#include "BKE_dynamicpaint.h"
#include "BKE_global.hh"
#include "BKE_main.hh"
#include "BKE_modifier.hh"
#include "BKE_object_deform.h"
#include "BKE_report.hh"
#include "BKE_screen.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "ED_mesh.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "physics_intern.hh" /* own include */
namespace blender {
static wmOperatorStatus surface_slot_add_exec(bContext *C, wmOperator * /*op*/)
{
DynamicPaintModifierData *pmd = nullptr;
Object *cObject = ed::object::context_active_object(C);
DynamicPaintCanvasSettings *canvas;
DynamicPaintSurface *surface;
/* Make sure we're dealing with a canvas */
pmd = reinterpret_cast<DynamicPaintModifierData *>(
BKE_modifiers_findby_type(cObject, eModifierType_DynamicPaint));
if (!pmd || !pmd->canvas) {
return OPERATOR_CANCELLED;
}
canvas = pmd->canvas;
surface = dynamicPaint_createNewSurface(canvas, CTX_data_scene(C));
if (!surface) {
return OPERATOR_CANCELLED;
}
canvas->active_sur = 0;
for (surface = surface->prev; surface; surface = surface->prev) {
canvas->active_sur++;
}
return OPERATOR_FINISHED;
}
void DPAINT_OT_surface_slot_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Surface Slot";
ot->idname = "DPAINT_OT_surface_slot_add";
ot->description = "Add a new Dynamic Paint surface slot";
/* API callbacks. */
ot->exec = surface_slot_add_exec;
ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus surface_slot_remove_exec(bContext *C, wmOperator * /*op*/)
{
DynamicPaintModifierData *pmd = nullptr;
Object *obj_ctx = ed::object::context_active_object(C);
DynamicPaintCanvasSettings *canvas;
DynamicPaintSurface *surface;
int id = 0;
/* Make sure we're dealing with a canvas */
pmd = reinterpret_cast<DynamicPaintModifierData *>(
BKE_modifiers_findby_type(obj_ctx, eModifierType_DynamicPaint));
if (!pmd || !pmd->canvas) {
return OPERATOR_CANCELLED;
}
canvas = pmd->canvas;
surface = static_cast<DynamicPaintSurface *>(canvas->surfaces.first);
/* find active surface and remove it */
for (; surface; surface = surface->next) {
if (id == canvas->active_sur) {
canvas->active_sur -= 1;
dynamicPaint_freeSurface(pmd, surface);
break;
}
id++;
}
DEG_id_tag_update(&obj_ctx->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obj_ctx);
return OPERATOR_FINISHED;
}
void DPAINT_OT_surface_slot_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Surface Slot";
ot->idname = "DPAINT_OT_surface_slot_remove";
ot->description = "Remove the selected surface slot";
/* API callbacks. */
ot->exec = surface_slot_remove_exec;
ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus type_toggle_exec(bContext *C, wmOperator *op)
{
Object *cObject = ed::object::context_active_object(C);
Scene *scene = CTX_data_scene(C);
DynamicPaintModifierData *pmd = reinterpret_cast<DynamicPaintModifierData *>(
BKE_modifiers_findby_type(cObject, eModifierType_DynamicPaint));
int type = RNA_enum_get(op->ptr, "type");
if (!pmd) {
return OPERATOR_CANCELLED;
}
/* if type is already enabled, toggle it off */
if (type == MOD_DYNAMICPAINT_TYPE_CANVAS && pmd->canvas) {
dynamicPaint_freeCanvas(pmd);
}
else if (type == MOD_DYNAMICPAINT_TYPE_BRUSH && pmd->brush) {
dynamicPaint_freeBrush(pmd);
}
/* else create a new type */
else {
if (!dynamicPaint_createType(pmd, type, scene)) {
return OPERATOR_CANCELLED;
}
}
/* update dependency */
DEG_id_tag_update(&cObject->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, cObject);
return OPERATOR_FINISHED;
}
void DPAINT_OT_type_toggle(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Toggle Type Active";
ot->idname = "DPAINT_OT_type_toggle";
ot->description = "Toggle whether given type is active or not";
/* API callbacks. */
ot->exec = type_toggle_exec;
ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna,
"type",
rna_enum_prop_dynamicpaint_type_items,
MOD_DYNAMICPAINT_TYPE_CANVAS,
"Type",
"");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SIMULATION);
ot->prop = prop;
}
static wmOperatorStatus output_toggle_exec(bContext *C, wmOperator *op)
{
Object *ob = ed::object::context_active_object(C);
DynamicPaintSurface *surface;
DynamicPaintModifierData *pmd = reinterpret_cast<DynamicPaintModifierData *>(
BKE_modifiers_findby_type(ob, eModifierType_DynamicPaint));
int output = RNA_enum_get(op->ptr, "output"); /* currently only 1/0 */
if (!pmd || !pmd->canvas) {
return OPERATOR_CANCELLED;
}
surface = get_activeSurface(pmd->canvas);
/* if type is already enabled, toggle it off */
if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
bool exists = dynamicPaint_outputLayerExists(surface, ob, output);
const char *name;
if (output == 0) {
name = surface->output_name;
}
else {
name = surface->output_name2;
}
/* Vertex Color Layer */
if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
if (!exists) {
ED_mesh_color_add(id_cast<Mesh *>(ob->data), name, true, true, op->reports);
}
else {
AttributeOwner owner = AttributeOwner::from_id(ob->data);
BKE_attribute_remove(owner, name, nullptr);
}
}
/* Vertex Weight Layer */
else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
if (!exists) {
BKE_object_defgroup_add_name(ob, name);
DEG_relations_tag_update(CTX_data_main(C));
}
else {
bDeformGroup *defgroup = BKE_object_defgroup_find_name(ob, name);
if (defgroup) {
BKE_object_defgroup_remove(ob, defgroup);
DEG_relations_tag_update(CTX_data_main(C));
}
}
}
}
return OPERATOR_FINISHED;
}
void DPAINT_OT_output_toggle(wmOperatorType *ot)
{
static const EnumPropertyItem prop_output_toggle_types[] = {
{0, "A", 0, "Output A", ""},
{1, "B", 0, "Output B", ""},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Toggle Output Layer";
ot->idname = "DPAINT_OT_output_toggle";
ot->description = "Add or remove Dynamic Paint output data layer";
/* API callbacks. */
ot->exec = output_toggle_exec;
ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "output", prop_output_toggle_types, 0, "Output Toggle", "");
}
/***************************** Image Sequence Baking ******************************/
struct DynamicPaintBakeJob {
/* from wmJob */
void *owner;
bool *stop, *do_update;
float *progress;
Main *bmain;
Scene *scene;
Depsgraph *depsgraph;
Object *ob;
DynamicPaintSurface *surface;
DynamicPaintCanvasSettings *canvas;
int success;
double start;
};
static void dpaint_bake_free(void *customdata)
{
DynamicPaintBakeJob *job = static_cast<DynamicPaintBakeJob *>(customdata);
MEM_delete(job);
}
static void dpaint_bake_endjob(void *customdata)
{
DynamicPaintBakeJob *job = static_cast<DynamicPaintBakeJob *>(customdata);
DynamicPaintCanvasSettings *canvas = job->canvas;
canvas->flags &= ~MOD_DPAINT_BAKING;
dynamicPaint_freeSurfaceData(job->surface);
G.is_rendering = false;
WM_locked_interface_set(static_cast<wmWindowManager *>(G_MAIN->wm.first), false);
/* Bake was successful:
* Report for ended bake and how long it took */
if (job->success) {
/* Show bake info */
WM_global_reportf(
RPT_INFO, "DynamicPaint: Bake complete! (%.2f)", BLI_time_now_seconds() - job->start);
}
else {
if (strlen(canvas->error)) { /* If an error occurred */
WM_global_reportf(RPT_ERROR, "DynamicPaint: Bake failed: %s", canvas->error);
}
else { /* User canceled the bake */
WM_global_report(RPT_WARNING, "Baking canceled!");
}
}
}
/*
* Do actual bake operation. Loop through to-be-baked frames.
* Returns 0 on failure.
*/
static void dynamicPaint_bakeImageSequence(DynamicPaintBakeJob *job)
{
DynamicPaintSurface *surface = job->surface;
Object *cObject = job->ob;
DynamicPaintCanvasSettings *canvas = surface->canvas;
Scene *input_scene = DEG_get_input_scene(job->depsgraph);
Scene *scene = job->scene;
int frame = 1, orig_frame;
int frames;
frames = surface->end_frame - surface->start_frame + 1;
if (frames <= 0) {
STRNCPY_UTF8(canvas->error, N_("No frames to bake"));
return;
}
/* Show progress bar. */
*(job->do_update) = true;
/* Set frame to start point (also initializes modifier data). */
frame = surface->start_frame;
orig_frame = input_scene->r.cfra;
input_scene->r.cfra = frame;
ED_update_for_newframe(job->bmain, job->depsgraph);
/* Init surface */
if (!dynamicPaint_createUVSurface(scene, surface, job->progress, job->do_update)) {
job->success = 0;
return;
}
/* Loop through selected frames */
for (frame = surface->start_frame; frame <= surface->end_frame; frame++) {
/* The first 10% are for createUVSurface... */
const float progress = 0.1f + 0.9f * (frame - surface->start_frame) / float(frames);
surface->current_frame = frame;
/* If user requested stop, quit baking */
if (G.is_break) {
job->success = 0;
return;
}
/* Update progress bar */
*(job->do_update) = true;
*(job->progress) = progress;
/* calculate a frame */
input_scene->r.cfra = frame;
ED_update_for_newframe(job->bmain, job->depsgraph);
if (!dynamicPaint_calculateFrame(surface, job->depsgraph, scene, cObject, frame)) {
job->success = 0;
return;
}
/*
* Save output images
*/
{
char filepath[FILE_MAX];
/* primary output layer */
if (surface->flags & MOD_DPAINT_OUT1) {
/* set filepath */
BLI_path_join(
filepath, sizeof(filepath), surface->image_output_path, surface->output_name);
BLI_path_frame(filepath, sizeof(filepath), frame, 4);
/* save image */
dynamicPaint_outputSurfaceImage(surface, filepath, 0);
}
/* secondary output */
if (surface->flags & MOD_DPAINT_OUT2 && surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
/* set filepath */
BLI_path_join(
filepath, sizeof(filepath), surface->image_output_path, surface->output_name2);
BLI_path_frame(filepath, sizeof(filepath), frame, 4);
/* save image */
dynamicPaint_outputSurfaceImage(surface, filepath, 1);
}
}
}
input_scene->r.cfra = orig_frame;
ED_update_for_newframe(job->bmain, job->depsgraph);
}
static void dpaint_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
{
DynamicPaintBakeJob *job = static_cast<DynamicPaintBakeJob *>(customdata);
job->stop = &worker_status->stop;
job->do_update = &worker_status->do_update;
job->progress = &worker_status->progress;
job->start = BLI_time_now_seconds();
job->success = 1;
G.is_break = false;
/* XXX annoying hack: needed to prevent data corruption when changing
* scene frame in separate threads
*/
G.is_rendering = true;
BKE_spacedata_draw_locks(REGION_DRAW_LOCK_BAKING);
dynamicPaint_bakeImageSequence(job);
worker_status->do_update = true;
worker_status->stop = false;
}
/*
* Bake Dynamic Paint image sequence surface
*/
static wmOperatorStatus dynamicpaint_bake_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *ob_ = ed::object::context_active_object(C);
Object *object_eval = DEG_get_evaluated(depsgraph, ob_);
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
DynamicPaintSurface *surface;
/*
* Get modifier data
*/
DynamicPaintModifierData *pmd = reinterpret_cast<DynamicPaintModifierData *>(
BKE_modifiers_findby_type(object_eval, eModifierType_DynamicPaint));
if (pmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Bake failed: no Dynamic Paint modifier found");
return OPERATOR_CANCELLED;
}
/* Make sure we're dealing with a canvas */
DynamicPaintCanvasSettings *canvas = pmd->canvas;
if (canvas == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Bake failed: invalid canvas");
return OPERATOR_CANCELLED;
}
surface = get_activeSurface(canvas);
/* Set state to baking and init surface */
canvas->error[0] = '\0';
canvas->flags |= MOD_DPAINT_BAKING;
DynamicPaintBakeJob *job = MEM_new_uninitialized<DynamicPaintBakeJob>("DynamicPaintBakeJob");
job->bmain = CTX_data_main(C);
job->scene = scene_eval;
job->depsgraph = depsgraph;
job->ob = object_eval;
job->canvas = canvas;
job->surface = surface;
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
CTX_data_scene(C),
"Baking Dynamic Paint...",
WM_JOB_PROGRESS,
WM_JOB_TYPE_DPAINT_BAKE);
WM_jobs_customdata_set(wm_job, job, dpaint_bake_free);
WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
WM_jobs_callbacks(wm_job, dpaint_bake_startjob, nullptr, nullptr, dpaint_bake_endjob);
WM_locked_interface_set_with_flags(CTX_wm_manager(C), REGION_DRAW_LOCK_BAKING);
/* Bake Dynamic Paint */
WM_jobs_start(CTX_wm_manager(C), wm_job);
return OPERATOR_FINISHED;
}
void DPAINT_OT_bake(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Dynamic Paint Bake";
ot->description = "Bake dynamic paint image sequence surface";
ot->idname = "DPAINT_OT_bake";
/* API callbacks. */
ot->exec = dynamicpaint_bake_exec;
ot->poll = ED_operator_object_active_local_editable;
}
} // namespace blender

View File

@@ -0,0 +1,359 @@
/* SPDX-FileCopyrightText: 2009 Janne Karhu. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#include <cstdlib>
#include "MEM_guardedalloc.h"
#include "DNA_particle_types.h"
#include "BLI_listbase.h"
#include "BKE_boids.h"
#include "BKE_context.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "physics_intern.hh"
namespace blender {
/************************ add/del boid rule operators *********************/
static wmOperatorStatus rule_add_exec(bContext *C, wmOperator *op)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
eBoidRuleType type = eBoidRuleType(RNA_enum_get(op->ptr, "type"));
BoidRule *rule;
BoidState *state;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
state = boid_get_current_state(part->boids);
for (BoidRule &rule : state->rules) {
rule.flag &= ~BOIDRULE_CURRENT;
}
rule = boid_new_rule(type);
rule->flag |= BOIDRULE_CURRENT;
BLI_addtail(&state->rules, rule);
DEG_id_tag_update(&part->id, ID_RECALC_GEOMETRY | ID_RECALC_PSYS_RESET);
return OPERATOR_FINISHED;
}
void BOID_OT_rule_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Boid Rule";
ot->description = "Add a boid rule to the current boid state";
ot->idname = "BOID_OT_rule_add";
/* API callbacks. */
ot->invoke = WM_menu_invoke;
ot->exec = rule_add_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_boidrule_type_items, 0, "Type", "");
}
static wmOperatorStatus rule_del_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
BoidRule *rule;
BoidState *state;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
state = boid_get_current_state(part->boids);
for (BoidRule &rule : state->rules) {
if (rule.flag & BOIDRULE_CURRENT) {
BLI_remlink(&state->rules, &rule);
MEM_delete(&rule);
break;
}
}
rule = static_cast<BoidRule *>(state->rules.first);
if (rule) {
rule->flag |= BOIDRULE_CURRENT;
}
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&part->id, ID_RECALC_GEOMETRY | ID_RECALC_PSYS_RESET);
return OPERATOR_FINISHED;
}
void BOID_OT_rule_del(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Boid Rule";
ot->idname = "BOID_OT_rule_del";
ot->description = "Delete current boid rule";
/* API callbacks. */
ot->exec = rule_del_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/************************ move up/down boid rule operators *********************/
static wmOperatorStatus rule_move_up_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
BoidState *state;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
state = boid_get_current_state(part->boids);
for (BoidRule &rule : state->rules) {
if (rule.flag & BOIDRULE_CURRENT && rule.prev) {
BLI_remlink(&state->rules, &rule);
BLI_insertlinkbefore(&state->rules, rule.prev, &rule);
DEG_id_tag_update(&part->id, ID_RECALC_GEOMETRY | ID_RECALC_PSYS_RESET);
break;
}
}
return OPERATOR_FINISHED;
}
void BOID_OT_rule_move_up(wmOperatorType *ot)
{
ot->name = "Move Up Boid Rule";
ot->description = "Move boid rule up in the list";
ot->idname = "BOID_OT_rule_move_up";
ot->exec = rule_move_up_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus rule_move_down_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
BoidState *state;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
state = boid_get_current_state(part->boids);
for (BoidRule &rule : state->rules) {
if (rule.flag & BOIDRULE_CURRENT && rule.next) {
BLI_remlink(&state->rules, &rule);
BLI_insertlinkafter(&state->rules, rule.next, &rule);
DEG_id_tag_update(&part->id, ID_RECALC_GEOMETRY | ID_RECALC_PSYS_RESET);
break;
}
}
return OPERATOR_FINISHED;
}
void BOID_OT_rule_move_down(wmOperatorType *ot)
{
ot->name = "Move Down Boid Rule";
ot->description = "Move boid rule down in the list";
ot->idname = "BOID_OT_rule_move_down";
ot->exec = rule_move_down_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/************************ add/del boid state operators *********************/
static wmOperatorStatus state_add_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
BoidState *state;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
for (BoidState &state : part->boids->states) {
state.flag &= ~BOIDSTATE_CURRENT;
}
state = boid_new_state(part->boids);
state->flag |= BOIDSTATE_CURRENT;
BLI_addtail(&part->boids->states, state);
return OPERATOR_FINISHED;
}
void BOID_OT_state_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Boid State";
ot->description = "Add a boid state to the particle system";
ot->idname = "BOID_OT_state_add";
/* API callbacks. */
ot->exec = state_add_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus state_del_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
BoidState *state;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
for (BoidState &state : part->boids->states) {
if (state.flag & BOIDSTATE_CURRENT) {
BLI_remlink(&part->boids->states, &state);
MEM_delete(&state);
break;
}
}
/* there must be at least one state */
if (!part->boids->states.first) {
state = boid_new_state(part->boids);
BLI_addtail(&part->boids->states, state);
}
else {
state = static_cast<BoidState *>(part->boids->states.first);
}
state->flag |= BOIDSTATE_CURRENT;
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&part->id, ID_RECALC_GEOMETRY | ID_RECALC_PSYS_RESET);
return OPERATOR_FINISHED;
}
void BOID_OT_state_del(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Boid State";
ot->idname = "BOID_OT_state_del";
ot->description = "Delete current boid state";
/* API callbacks. */
ot->exec = state_del_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/************************ move up/down boid state operators *********************/
static wmOperatorStatus state_move_up_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
BoidSettings *boids;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
boids = part->boids;
for (BoidState &state : boids->states) {
if (state.flag & BOIDSTATE_CURRENT && state.prev) {
BLI_remlink(&boids->states, &state);
BLI_insertlinkbefore(&boids->states, state.prev, &state);
break;
}
}
return OPERATOR_FINISHED;
}
void BOID_OT_state_move_up(wmOperatorType *ot)
{
ot->name = "Move Up Boid State";
ot->description = "Move boid state up in the list";
ot->idname = "BOID_OT_state_move_up";
ot->exec = state_move_up_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus state_move_down_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "particle_settings", RNA_ParticleSettings);
ParticleSettings *part = static_cast<ParticleSettings *>(ptr.data);
BoidSettings *boids;
if (!part || part->phystype != PART_PHYS_BOIDS) {
return OPERATOR_CANCELLED;
}
boids = part->boids;
for (BoidState &state : boids->states) {
if (state.flag & BOIDSTATE_CURRENT && state.next) {
BLI_remlink(&boids->states, &state);
BLI_insertlinkafter(&boids->states, state.next, &state);
DEG_id_tag_update(&part->id, ID_RECALC_GEOMETRY | ID_RECALC_PSYS_RESET);
break;
}
}
return OPERATOR_FINISHED;
}
void BOID_OT_state_move_down(wmOperatorType *ot)
{
ot->name = "Move Down Boid State";
ot->description = "Move boid state down in the list";
ot->idname = "BOID_OT_state_move_down";
ot->exec = state_move_down_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
/* SPDX-FileCopyrightText: 2007 by Janne Karhu. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "CLG_log.h"
#include "DNA_scene_types.h"
#include "DNA_windowmanager_types.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_layer.hh"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_undo_system.hh"
#include "DEG_depsgraph.hh"
#include "ED_object.hh"
#include "ED_particle.hh"
#include "ED_physics.hh"
#include "ED_undo.hh"
#include "particle_edit_utildefines.h"
namespace blender {
/** Only needed this locally. */
static CLG_LogRef LOG = {"undo.particle"};
/* -------------------------------------------------------------------- */
/** \name Undo Conversion
* \{ */
static void undoptcache_from_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
{
PTCacheEditPoint *point;
size_t mem_used_prev = MEM_get_memory_in_use();
undo->totpoint = edit->totpoint;
if (edit->psys) {
ParticleData *pa;
pa = undo->particles = MEM_dupalloc(edit->psys->particles);
for (int i = 0; i < edit->totpoint; i++, pa++) {
pa->hair = MEM_dupalloc(pa->hair);
}
undo->psys_flag = edit->psys->flag;
}
else {
PTCacheMem *pm;
BLI_duplicatelist(&undo->mem_cache, &edit->pid.cache->mem_cache);
pm = static_cast<PTCacheMem *>(undo->mem_cache.first);
for (; pm; pm = pm->next) {
for (int i = 0; i < BPHYS_TOT_DATA; i++) {
pm->data[i] = MEM_dupalloc_void(pm->data[i]);
}
}
}
point = undo->points = MEM_dupalloc(edit->points);
undo->totpoint = edit->totpoint;
for (int i = 0; i < edit->totpoint; i++, point++) {
point->keys = MEM_dupalloc(point->keys);
/* no need to update edit key->co & key->time pointers here */
}
size_t mem_used_curr = MEM_get_memory_in_use();
undo->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev :
sizeof(PTCacheUndo);
}
static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
{
ParticleSystem *psys = edit->psys;
ParticleData *pa;
HairKey *hkey;
POINT_P;
KEY_K;
LOOP_POINTS {
if (psys && psys->particles[p].hair) {
MEM_delete(psys->particles[p].hair);
}
if (point->keys) {
MEM_delete(point->keys);
}
}
if (psys && psys->particles) {
MEM_delete(psys->particles);
}
if (edit->points) {
MEM_delete(edit->points);
}
MEM_SAFE_DELETE(edit->mirror_cache);
edit->points = MEM_dupalloc(undo->points);
edit->totpoint = undo->totpoint;
LOOP_POINTS {
point->keys = MEM_dupalloc(point->keys);
}
if (psys) {
psys->particles = MEM_dupalloc(undo->particles);
psys->totpart = undo->totpoint;
LOOP_POINTS {
pa = psys->particles + p;
hkey = pa->hair = MEM_dupalloc(pa->hair);
LOOP_KEYS {
key->co = hkey->co;
key->time = &hkey->time;
hkey++;
}
}
psys->flag = undo->psys_flag;
}
else {
PTCacheMem *pm;
int i;
BKE_ptcache_free_mem(&edit->pid.cache->mem_cache);
BLI_duplicatelist(&edit->pid.cache->mem_cache, &undo->mem_cache);
pm = static_cast<PTCacheMem *>(edit->pid.cache->mem_cache.first);
for (; pm; pm = pm->next) {
for (i = 0; i < BPHYS_TOT_DATA; i++) {
pm->data[i] = MEM_dupalloc_void(pm->data[i]);
}
void *cur[BPHYS_TOT_DATA];
BKE_ptcache_mem_pointers_init(pm, cur);
LOOP_POINTS {
LOOP_KEYS {
if (int(key->ftime) == int(pm->frame)) {
key->co = static_cast<float *>(cur[BPHYS_DATA_LOCATION]);
key->vel = static_cast<float *>(cur[BPHYS_DATA_VELOCITY]);
key->rot = static_cast<float *>(cur[BPHYS_DATA_ROTATION]);
key->time = &key->ftime;
}
}
BKE_ptcache_mem_pointers_incr(cur);
}
}
}
}
static void undoptcache_free_data(PTCacheUndo *undo)
{
PTCacheEditPoint *point;
int i;
for (i = 0, point = undo->points; i < undo->totpoint; i++, point++) {
if (undo->particles && (undo->particles + i)->hair) {
MEM_delete((undo->particles + i)->hair);
}
if (point->keys) {
MEM_delete(point->keys);
}
}
if (undo->points) {
MEM_delete(undo->points);
}
if (undo->particles) {
MEM_delete(undo->particles);
}
BKE_ptcache_free_mem(&undo->mem_cache);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Implements ED Undo System
* \{ */
struct ParticleUndoStep {
UndoStep step;
/** See #ED_undo_object_editmode_validate_scene_from_windows code comment for details. */
UndoRefID_Scene scene_ref;
UndoRefID_Object object_ref;
PTCacheUndo data;
};
static bool particle_undosys_poll(bContext *C)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
BKE_view_layer_synced_ensure(*bmain, scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
return (edit != nullptr);
}
static bool particle_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
ParticleUndoStep *us = reinterpret_cast<ParticleUndoStep *>(us_p);
ViewLayer *view_layer = CTX_data_view_layer(C);
us->scene_ref.ptr = CTX_data_scene(C);
BKE_view_layer_synced_ensure(*bmain, us->scene_ref.ptr, view_layer);
us->object_ref.ptr = BKE_view_layer_active_object_get(view_layer);
PTCacheEdit *edit = PE_get_current(depsgraph, us->scene_ref.ptr, us->object_ref.ptr);
undoptcache_from_editcache(&us->data, edit);
return true;
}
static void particle_undosys_step_decode(
bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir /*dir*/, bool /*is_final*/)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
ParticleUndoStep *us = reinterpret_cast<ParticleUndoStep *>(us_p);
Scene *scene = us->scene_ref.ptr;
ViewLayer *view_layer = CTX_data_view_layer(C);
/* Only to correct the `view_layer` which might not match the scene
* (in the case of undoing with multiple windows). */
ED_undo_object_editmode_validate_scene_from_windows(
CTX_wm_manager(C), us->scene_ref.ptr, &scene, &view_layer);
Object *ob = us->object_ref.ptr;
ED_object_particle_edit_mode_enter_ex(depsgraph, scene, ob);
PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
/* While this shouldn't happen, entering particle edit-mode uses a more complex
* setup compared to most other modes which we can't ensure succeeds. */
if (UNLIKELY(edit == nullptr)) {
BLI_assert(0);
return;
}
undoptcache_to_editcache(&us->data, edit);
ParticleEditSettings *pset = &scene->toolsettings->particle;
if ((pset->flag & PE_DRAW_PART) != 0) {
psys_free_path_cache(nullptr, edit);
BKE_particle_batch_cache_dirty_tag(edit->psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
ED_undo_object_set_active_or_warn(*bmain, scene, view_layer, ob, us_p->name, &LOG);
/* Check after setting active (unless undoing into another scene). */
BLI_assert(particle_undosys_poll(C) || (scene != CTX_data_scene(C)));
}
static void particle_undosys_step_free(UndoStep *us_p)
{
ParticleUndoStep *us = reinterpret_cast<ParticleUndoStep *>(us_p);
undoptcache_free_data(&us->data);
}
static void particle_undosys_foreach_ID_ref(UndoStep *us_p,
UndoTypeForEachIDRefFn foreach_ID_ref_fn,
void *user_data)
{
ParticleUndoStep *us = reinterpret_cast<ParticleUndoStep *>(us_p);
foreach_ID_ref_fn(user_data, (reinterpret_cast<UndoRefID *>(&us->scene_ref)));
foreach_ID_ref_fn(user_data, (reinterpret_cast<UndoRefID *>(&us->object_ref)));
}
void ED_particle_undosys_type(UndoType *ut)
{
ut->name = "Edit Particle";
ut->poll = particle_undosys_poll;
ut->step_encode = particle_undosys_step_encode;
ut->step_decode = particle_undosys_step_decode;
ut->step_free = particle_undosys_step_free;
ut->step_foreach_ID_ref = particle_undosys_foreach_ID_ref;
ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE;
ut->step_size = sizeof(ParticleUndoStep);
}
/** \} */
} // namespace blender

View File

@@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2007 by Janne Karhu. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#pragma once
#define KEY_K \
PTCacheEditKey *key; \
int k
#define POINT_P \
[[maybe_unused]] PTCacheEditPoint *point; \
int p
#define LOOP_POINTS for (p = 0, point = edit->points; p < edit->totpoint; p++, point++)
#define LOOP_VISIBLE_POINTS \
for (p = 0, point = edit->points; p < edit->totpoint; p++, point++) \
if (!(point->flag & PEP_HIDE))
#define LOOP_SELECTED_POINTS \
for (p = 0, point = edit->points; p < edit->totpoint; p++, point++) \
if (point_is_selected(point))
#define LOOP_UNSELECTED_POINTS \
for (p = 0, point = edit->points; p < edit->totpoint; p++, point++) \
if (!point_is_selected(point))
#define LOOP_EDITED_POINTS \
for (p = 0, point = edit->points; p < edit->totpoint; p++, point++) \
if (point->flag & PEP_EDIT_RECALC)
#define LOOP_TAGGED_POINTS \
for (p = 0, point = edit->points; p < edit->totpoint; p++, point++) \
if (point->flag & PEP_TAG)
#define LOOP_KEYS for (k = 0, key = point->keys; k < point->totkey; k++, key++)
#define LOOP_VISIBLE_KEYS \
for (k = 0, key = point->keys; k < point->totkey; k++, key++) \
if (!(key->flag & PEK_HIDE))
#define LOOP_SELECTED_KEYS \
for (k = 0, key = point->keys; k < point->totkey; k++, key++) \
if ((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE))
#define LOOP_TAGGED_KEYS \
for (k = 0, key = point->keys; k < point->totkey; k++, key++) \
if (key->flag & PEK_TAG)
#define KEY_WCO ((key->flag & PEK_USE_WCO) ? key->world_co : key->co)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,867 @@
/* SPDX-FileCopyrightText: Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#include <cstdlib>
#include <cstring>
#include <sys/stat.h>
#include "MEM_guardedalloc.h"
/* types */
#include "DNA_object_types.h"
#include "BLI_fileops.h"
#include "BLI_path_utils.hh"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_time.h"
#include "BLI_utildefines.h"
#include "BLT_translation.hh"
#include "BKE_context.hh"
#include "BKE_fluid.h"
#include "BKE_global.hh"
#include "BKE_main.hh"
#include "BKE_modifier.hh"
#include "BKE_report.hh"
#include "BKE_screen.hh"
#include "DEG_depsgraph.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "physics_intern.hh" /* own include */
#include "DNA_fluid_types.h"
#include "DNA_scene_types.h"
namespace blender {
#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"
struct FluidJob {
/* from wmJob */
void *owner;
bool *stop, *do_update;
float *progress;
const char *type;
const char *name;
Main *bmain;
Scene *scene;
Depsgraph *depsgraph;
Object *ob;
FluidModifierData *fmd;
int success;
double start;
int *pause_frame;
};
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_job_init(
bContext *C, FluidJob *job, wmOperator *op, char *error_msg, int error_size)
{
FluidModifierData *fmd = nullptr;
FluidDomainSettings *fds;
Object *ob = ed::object::context_active_object(C);
fmd = reinterpret_cast<FluidModifierData *>(BKE_modifiers_findby_type(ob, eModifierType_Fluid));
if (!fmd) {
BLI_strncpy_utf8(error_msg, N_("No Fluid modifier found"), error_size);
return false;
}
fds = fmd->domain;
if (!fds) {
BLI_strncpy_utf8(error_msg, N_("Invalid domain"), error_size);
return false;
}
if (fds->cache_flag & (FLUID_DOMAIN_BAKING_DATA | FLUID_DOMAIN_BAKING_NOISE |
FLUID_DOMAIN_BAKING_MESH | FLUID_DOMAIN_BAKING_PARTICLES))
{
BKE_report(op->reports, RPT_ERROR, "Pending bake jobs found");
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_validate_paths(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_job_free(void *customdata)
{
FluidJob *job = static_cast<FluidJob *>(customdata);
MEM_delete(job);
}
static FluidJob *fluid_job_create(bContext *C, wmOperator *op)
{
FluidJob *job = MEM_new_uninitialized<FluidJob>("FluidJob");
char error_msg[256] = "\0";
if (!fluid_job_init(C, job, op, error_msg, sizeof(error_msg))) {
if (error_msg[0]) {
BKE_report(op->reports, RPT_ERROR, error_msg);
}
fluid_job_free(job);
return nullptr;
}
if (!fluid_validate_paths(job, op->reports)) {
fluid_job_free(job);
return nullptr;
}
WM_report_banners_cancel(job->bmain);
return 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 = nullptr;
bool is_first_frame;
frames = fds->cache_frame_end - fds->cache_frame_start + 1;
if (frames <= 0) {
STRNCPY_UTF8(fds->error, N_("No frames to bake"));
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 = static_cast<FluidJob *>(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;
WM_locked_interface_set(static_cast<wmWindowManager *>(G_MAIN->wm.first), false);
/* Bake was successful:
* Report for ended bake and how long it took. */
if (job->success) {
/* Show bake info. */
WM_global_reportf(RPT_INFO,
"Fluid: %s complete (%.2fs)",
CTX_RPT_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, job->name),
BLI_time_now_seconds() - job->start);
}
else {
if (fds->error[0] != '\0') {
WM_global_reportf(RPT_ERROR,
"Fluid: %s failed at frame %d: %s",
CTX_RPT_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, job->name),
*job->pause_frame,
fds->error);
}
else { /* User canceled the bake. */
WM_global_reportf(RPT_WARNING,
"Fluid: %s canceled at frame %d!",
CTX_RPT_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, job->name),
*job->pause_frame);
}
}
}
static void fluid_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
{
FluidJob *job = static_cast<FluidJob *>(customdata);
FluidDomainSettings *fds = job->fmd->domain;
char temp_dir[FILE_MAX];
const char *relbase = BKE_modifier_path_relbase_from_global(job->ob);
job->stop = &worker_status->stop;
job->do_update = &worker_status->do_update;
job->progress = &worker_status->progress;
job->start = BLI_time_now_seconds();
job->success = 1;
G.is_break = false;
G.is_rendering = true;
BKE_spacedata_draw_locks(REGION_DRAW_LOCK_BAKING);
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);
worker_status->do_update = true;
worker_status->stop = false;
}
static void fluid_free_endjob(void *customdata)
{
FluidJob *job = static_cast<FluidJob *>(customdata);
FluidDomainSettings *fds = job->fmd->domain;
G.is_rendering = false;
WM_locked_interface_set(static_cast<wmWindowManager *>(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_global_reportf(RPT_INFO,
"Fluid: %s complete (%.2fs)",
CTX_RPT_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, job->name),
BLI_time_now_seconds() - job->start);
}
else {
if (fds->error[0] != '\0') {
WM_global_reportf(RPT_ERROR,
"Fluid: %s failed at frame %d: %s",
CTX_RPT_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, job->name),
*job->pause_frame,
fds->error);
}
else { /* User canceled the free job */
WM_global_reportf(RPT_WARNING,
"Fluid: %s canceled at frame %d!",
CTX_RPT_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, job->name),
*job->pause_frame);
}
}
}
static void fluid_free_startjob(void *customdata, wmJobWorkerStatus *worker_status)
{
FluidJob *job = static_cast<FluidJob *>(customdata);
FluidDomainSettings *fds = job->fmd->domain;
job->stop = &worker_status->stop;
job->do_update = &worker_status->do_update;
job->progress = &worker_status->progress;
job->start = BLI_time_now_seconds();
job->success = 1;
G.is_break = false;
G.is_rendering = true;
BKE_spacedata_draw_locks(REGION_DRAW_LOCK_BAKING);
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
worker_status->do_update = true;
worker_status->stop = false;
/* Update scene so that viewport shows freed up scene */
ED_update_for_newframe(job->bmain, job->depsgraph);
}
/***************************** Operators ******************************/
static wmOperatorStatus fluid_bake_exec(bContext *C, wmOperator *op)
{
FluidJob *job = fluid_job_create(C, op);
if (job == nullptr) {
return OPERATOR_CANCELLED;
}
wmJobWorkerStatus worker_status = {};
fluid_bake_startjob(job, &worker_status);
fluid_bake_endjob(job);
fluid_job_free(job);
return OPERATOR_FINISHED;
}
static wmOperatorStatus fluid_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*_event*/)
{
FluidJob *job = fluid_job_create(C, op);
if (job == nullptr) {
return OPERATOR_CANCELLED;
}
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
CTX_data_scene(C),
"Baking fluid...",
WM_JOB_PROGRESS,
WM_JOB_TYPE_OBJECT_SIM_FLUID);
WM_jobs_customdata_set(wm_job, job, fluid_job_free);
WM_jobs_timer(wm_job, 0.01, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
WM_jobs_callbacks(wm_job, fluid_bake_startjob, nullptr, nullptr, fluid_bake_endjob);
WM_locked_interface_set_with_flags(CTX_wm_manager(C), REGION_DRAW_LOCK_BAKING);
WM_jobs_start(CTX_wm_manager(C), wm_job);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static wmOperatorStatus fluid_bake_modal(bContext *C, wmOperator * /*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;
default: {
break;
}
}
return OPERATOR_PASS_THROUGH;
}
static wmOperatorStatus fluid_free_exec(bContext *C, wmOperator *op)
{
FluidJob *job = fluid_job_create(C, op);
if (job == nullptr) {
return OPERATOR_CANCELLED;
}
wmJobWorkerStatus worker_status = {};
fluid_free_startjob(job, &worker_status);
fluid_free_endjob(job);
fluid_job_free(job);
return OPERATOR_FINISHED;
}
static wmOperatorStatus fluid_free_invoke(bContext *C, wmOperator *op, const wmEvent * /*_event*/)
{
FluidJob *job = fluid_job_create(C, op);
if (job == nullptr) {
return OPERATOR_CANCELLED;
}
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
CTX_data_scene(C),
"Freeing fluid...",
WM_JOB_PROGRESS,
WM_JOB_TYPE_OBJECT_SIM_FLUID);
WM_jobs_customdata_set(wm_job, job, fluid_job_free);
WM_jobs_timer(wm_job, 0.01, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
WM_jobs_callbacks(wm_job, fluid_free_startjob, nullptr, nullptr, fluid_free_endjob);
WM_locked_interface_set_with_flags(CTX_wm_manager(C), REGION_DRAW_LOCK_BAKING);
WM_jobs_start(CTX_wm_manager(C), wm_job);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static wmOperatorStatus fluid_free_modal(bContext *C, wmOperator * /*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;
default: {
break;
}
}
return OPERATOR_PASS_THROUGH;
}
static wmOperatorStatus fluid_pause_exec(bContext *C, wmOperator *op)
{
FluidModifierData *fmd = nullptr;
FluidDomainSettings *fds;
Object *ob = ed::object::context_active_object(C);
/*
* Get modifier data
*/
fmd = reinterpret_cast<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->invoke = fluid_free_invoke;
ot->modal = fluid_free_modal;
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;
}
} // namespace blender

View File

@@ -0,0 +1,162 @@
/* SPDX-FileCopyrightText: 2007 by Janne Karhu. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#pragma once
namespace blender {
struct Depsgraph;
struct Object;
struct PTCacheEdit;
struct ParticleSystem;
struct PointCache;
struct Scene;
struct wmOperatorType;
/* `particle_edit.cc` */
void PARTICLE_OT_select_all(wmOperatorType *ot);
void PARTICLE_OT_select_roots(wmOperatorType *ot);
void PARTICLE_OT_select_tips(wmOperatorType *ot);
void PARTICLE_OT_select_random(wmOperatorType *ot);
void PARTICLE_OT_select_linked(wmOperatorType *ot);
void PARTICLE_OT_select_linked_pick(wmOperatorType *ot);
void PARTICLE_OT_select_less(wmOperatorType *ot);
void PARTICLE_OT_select_more(wmOperatorType *ot);
void PARTICLE_OT_hide(wmOperatorType *ot);
void PARTICLE_OT_reveal(wmOperatorType *ot);
void PARTICLE_OT_rekey(wmOperatorType *ot);
void PARTICLE_OT_subdivide(wmOperatorType *ot);
void PARTICLE_OT_remove_doubles(wmOperatorType *ot);
void PARTICLE_OT_weight_set(wmOperatorType *ot);
void PARTICLE_OT_delete(wmOperatorType *ot);
void PARTICLE_OT_mirror(wmOperatorType *ot);
void PARTICLE_OT_brush_edit(wmOperatorType *ot);
void PARTICLE_OT_shape_cut(wmOperatorType *ot);
void PARTICLE_OT_particle_edit_toggle(wmOperatorType *ot);
void PARTICLE_OT_edited_clear(wmOperatorType *ot);
void PARTICLE_OT_unify_length(wmOperatorType *ot);
/**
* Initialize needed data for bake edit.
*/
void PE_create_particle_edit(
Depsgraph *depsgraph, Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
/**
* Set current distances to be kept between neighboring keys.
*/
void recalc_lengths(PTCacheEdit *edit);
/**
* Calculate a tree for finding nearest emitter's vertices.
*/
void recalc_emitter_field(Depsgraph *depsgraph, Object *ob, ParticleSystem *psys);
void update_world_cos(Object *ob, PTCacheEdit *edit);
/* `particle_object.cc` */
void OBJECT_OT_particle_system_add(wmOperatorType *ot);
void OBJECT_OT_particle_system_remove(wmOperatorType *ot);
void PARTICLE_OT_new(wmOperatorType *ot);
void PARTICLE_OT_new_target(wmOperatorType *ot);
void PARTICLE_OT_target_remove(wmOperatorType *ot);
void PARTICLE_OT_target_move_up(wmOperatorType *ot);
void PARTICLE_OT_target_move_down(wmOperatorType *ot);
void PARTICLE_OT_connect_hair(wmOperatorType *ot);
void PARTICLE_OT_disconnect_hair(wmOperatorType *ot);
void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot);
void PARTICLE_OT_duplicate_particle_system(wmOperatorType *ot);
void PARTICLE_OT_particle_system_remove_all(wmOperatorType *ot);
void PARTICLE_OT_dupliob_copy(wmOperatorType *ot);
void PARTICLE_OT_dupliob_remove(wmOperatorType *ot);
void PARTICLE_OT_dupliob_move_up(wmOperatorType *ot);
void PARTICLE_OT_dupliob_move_down(wmOperatorType *ot);
void PARTICLE_OT_dupliob_refresh(wmOperatorType *ot);
/* `particle_boids.cc` */
void BOID_OT_rule_add(wmOperatorType *ot);
void BOID_OT_rule_del(wmOperatorType *ot);
void BOID_OT_rule_move_up(wmOperatorType *ot);
void BOID_OT_rule_move_down(wmOperatorType *ot);
void BOID_OT_state_add(wmOperatorType *ot);
void BOID_OT_state_del(wmOperatorType *ot);
void BOID_OT_state_move_up(wmOperatorType *ot);
void BOID_OT_state_move_down(wmOperatorType *ot);
/* `physics_fluid.cc` */
void FLUID_OT_bake_all(wmOperatorType *ot);
void FLUID_OT_free_all(wmOperatorType *ot);
void FLUID_OT_bake_data(wmOperatorType *ot);
void FLUID_OT_free_data(wmOperatorType *ot);
void FLUID_OT_bake_noise(wmOperatorType *ot);
void FLUID_OT_free_noise(wmOperatorType *ot);
void FLUID_OT_bake_mesh(wmOperatorType *ot);
void FLUID_OT_free_mesh(wmOperatorType *ot);
void FLUID_OT_bake_particles(wmOperatorType *ot);
void FLUID_OT_free_particles(wmOperatorType *ot);
void FLUID_OT_bake_guides(wmOperatorType *ot);
void FLUID_OT_free_guides(wmOperatorType *ot);
void FLUID_OT_pause_bake(wmOperatorType *ot);
/* `dynamicpaint.cc` */
void DPAINT_OT_bake(wmOperatorType *ot);
/**
* Add surface slot.
*/
void DPAINT_OT_surface_slot_add(wmOperatorType *ot);
/**
* Remove surface slot.
*/
void DPAINT_OT_surface_slot_remove(wmOperatorType *ot);
void DPAINT_OT_type_toggle(wmOperatorType *ot);
void DPAINT_OT_output_toggle(wmOperatorType *ot);
/* `physics_pointcache.cc` */
void PTCACHE_OT_bake_all(wmOperatorType *ot);
void PTCACHE_OT_free_bake_all(wmOperatorType *ot);
void PTCACHE_OT_bake(wmOperatorType *ot);
void PTCACHE_OT_free_bake(wmOperatorType *ot);
void PTCACHE_OT_bake_from_cache(wmOperatorType *ot);
void PTCACHE_OT_add(wmOperatorType *ot);
void PTCACHE_OT_remove(wmOperatorType *ot);
/* `rigidbody_object.cc` */
void RIGIDBODY_OT_object_add(wmOperatorType *ot);
void RIGIDBODY_OT_object_remove(wmOperatorType *ot);
void RIGIDBODY_OT_objects_add(wmOperatorType *ot);
void RIGIDBODY_OT_objects_remove(wmOperatorType *ot);
void RIGIDBODY_OT_shape_change(wmOperatorType *ot);
void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot);
/* `rigidbody_constraint.cc` */
void RIGIDBODY_OT_constraint_add(wmOperatorType *ot);
void RIGIDBODY_OT_constraint_remove(wmOperatorType *ot);
/* `rigidbody_world.cc` */
void RIGIDBODY_OT_world_add(wmOperatorType *ot);
void RIGIDBODY_OT_world_remove(wmOperatorType *ot);
void RIGIDBODY_OT_world_export(wmOperatorType *ot);
} // namespace blender

View File

@@ -0,0 +1,194 @@
/* SPDX-FileCopyrightText: 2009 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#include <cstdlib>
#include "DNA_space_types.h"
#include "WM_api.hh"
#include "ED_physics.hh"
#include "physics_intern.hh" /* own include */
namespace blender {
/* -------------------------------------------------------------------- */
/** \name Particles
* \{ */
static void operatortypes_particle()
{
WM_operatortype_append(PARTICLE_OT_select_all);
WM_operatortype_append(PARTICLE_OT_select_roots);
WM_operatortype_append(PARTICLE_OT_select_tips);
WM_operatortype_append(PARTICLE_OT_select_random);
WM_operatortype_append(PARTICLE_OT_select_linked);
WM_operatortype_append(PARTICLE_OT_select_linked_pick);
WM_operatortype_append(PARTICLE_OT_select_less);
WM_operatortype_append(PARTICLE_OT_select_more);
WM_operatortype_append(PARTICLE_OT_hide);
WM_operatortype_append(PARTICLE_OT_reveal);
WM_operatortype_append(PARTICLE_OT_rekey);
WM_operatortype_append(PARTICLE_OT_subdivide);
WM_operatortype_append(PARTICLE_OT_remove_doubles);
WM_operatortype_append(PARTICLE_OT_weight_set);
WM_operatortype_append(PARTICLE_OT_delete);
WM_operatortype_append(PARTICLE_OT_mirror);
WM_operatortype_append(PARTICLE_OT_brush_edit);
WM_operatortype_append(PARTICLE_OT_shape_cut);
WM_operatortype_append(PARTICLE_OT_particle_edit_toggle);
WM_operatortype_append(PARTICLE_OT_edited_clear);
WM_operatortype_append(PARTICLE_OT_unify_length);
WM_operatortype_append(OBJECT_OT_particle_system_add);
WM_operatortype_append(OBJECT_OT_particle_system_remove);
WM_operatortype_append(PARTICLE_OT_new);
WM_operatortype_append(PARTICLE_OT_new_target);
WM_operatortype_append(PARTICLE_OT_target_remove);
WM_operatortype_append(PARTICLE_OT_target_move_up);
WM_operatortype_append(PARTICLE_OT_target_move_down);
WM_operatortype_append(PARTICLE_OT_connect_hair);
WM_operatortype_append(PARTICLE_OT_disconnect_hair);
WM_operatortype_append(PARTICLE_OT_copy_particle_systems);
WM_operatortype_append(PARTICLE_OT_duplicate_particle_system);
WM_operatortype_append(PARTICLE_OT_particle_system_remove_all);
WM_operatortype_append(PARTICLE_OT_dupliob_refresh);
WM_operatortype_append(PARTICLE_OT_dupliob_copy);
WM_operatortype_append(PARTICLE_OT_dupliob_remove);
WM_operatortype_append(PARTICLE_OT_dupliob_move_up);
WM_operatortype_append(PARTICLE_OT_dupliob_move_down);
WM_operatortype_append(RIGIDBODY_OT_object_add);
WM_operatortype_append(RIGIDBODY_OT_object_remove);
WM_operatortype_append(RIGIDBODY_OT_objects_add);
WM_operatortype_append(RIGIDBODY_OT_objects_remove);
WM_operatortype_append(RIGIDBODY_OT_shape_change);
WM_operatortype_append(RIGIDBODY_OT_mass_calculate);
WM_operatortype_append(RIGIDBODY_OT_constraint_add);
WM_operatortype_append(RIGIDBODY_OT_constraint_remove);
WM_operatortype_append(RIGIDBODY_OT_world_add);
WM_operatortype_append(RIGIDBODY_OT_world_remove);
// WM_operatortype_append(RIGIDBODY_OT_world_export);
}
static void keymap_particle(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Particle", SPACE_EMPTY, RGN_TYPE_WINDOW);
keymap->poll = PE_poll;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Boids
* \{ */
static void operatortypes_boids()
{
WM_operatortype_append(BOID_OT_rule_add);
WM_operatortype_append(BOID_OT_rule_del);
WM_operatortype_append(BOID_OT_rule_move_up);
WM_operatortype_append(BOID_OT_rule_move_down);
WM_operatortype_append(BOID_OT_state_add);
WM_operatortype_append(BOID_OT_state_del);
WM_operatortype_append(BOID_OT_state_move_up);
WM_operatortype_append(BOID_OT_state_move_down);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Fluid
* \{ */
static void operatortypes_fluid()
{
WM_operatortype_append(FLUID_OT_bake_all);
WM_operatortype_append(FLUID_OT_free_all);
WM_operatortype_append(FLUID_OT_bake_data);
WM_operatortype_append(FLUID_OT_free_data);
WM_operatortype_append(FLUID_OT_bake_noise);
WM_operatortype_append(FLUID_OT_free_noise);
WM_operatortype_append(FLUID_OT_bake_mesh);
WM_operatortype_append(FLUID_OT_free_mesh);
WM_operatortype_append(FLUID_OT_bake_particles);
WM_operatortype_append(FLUID_OT_free_particles);
WM_operatortype_append(FLUID_OT_bake_guides);
WM_operatortype_append(FLUID_OT_free_guides);
WM_operatortype_append(FLUID_OT_pause_bake);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Point Cache
* \{ */
static void operatortypes_pointcache()
{
WM_operatortype_append(PTCACHE_OT_bake_all);
WM_operatortype_append(PTCACHE_OT_free_bake_all);
WM_operatortype_append(PTCACHE_OT_bake);
WM_operatortype_append(PTCACHE_OT_free_bake);
WM_operatortype_append(PTCACHE_OT_bake_from_cache);
WM_operatortype_append(PTCACHE_OT_add);
WM_operatortype_append(PTCACHE_OT_remove);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dynamic Paint
* \{ */
static void operatortypes_dynamicpaint()
{
WM_operatortype_append(DPAINT_OT_bake);
WM_operatortype_append(DPAINT_OT_surface_slot_add);
WM_operatortype_append(DPAINT_OT_surface_slot_remove);
WM_operatortype_append(DPAINT_OT_type_toggle);
WM_operatortype_append(DPAINT_OT_output_toggle);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Registration
* \{ */
void ED_operatortypes_physics()
{
operatortypes_particle();
operatortypes_boids();
operatortypes_fluid();
operatortypes_pointcache();
operatortypes_dynamicpaint();
}
void ED_keymap_physics(wmKeyConfig *keyconf)
{
keymap_particle(keyconf);
}
/** \} */
} // namespace blender

View File

@@ -0,0 +1,466 @@
/* SPDX-FileCopyrightText: 2007 by Janne Karhu. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edphys
*/
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_duplilist.hh"
#include "BKE_global.hh"
#include "BKE_layer.hh"
#include "BKE_library.hh"
#include "BKE_pointcache.h"
#include "DEG_depsgraph.hh"
#include "ED_particle.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.hh"
#include "physics_intern.hh"
namespace blender {
static bool ptcache_bake_all_poll(bContext *C)
{
return CTX_data_scene(C) != nullptr;
}
static bool ptcache_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", RNA_PointCache);
ID *id = ptr.owner_id;
PointCache *point_cache = static_cast<PointCache *>(ptr.data);
if (id == nullptr || point_cache == nullptr) {
return false;
}
if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && (point_cache->flag & PTCACHE_DISK_CACHE) == false) {
CTX_wm_operator_poll_msg_set(C,
"Library override data-blocks only support Disk Cache storage");
return false;
}
if (!ID_IS_EDITABLE(id) && (point_cache->flag & PTCACHE_DISK_CACHE) == false) {
CTX_wm_operator_poll_msg_set(C, "Linked data-blocks do not allow editing caches");
return false;
}
return true;
}
static bool ptcache_add_remove_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", RNA_PointCache);
ID *id = ptr.owner_id;
PointCache *point_cache = static_cast<PointCache *>(ptr.data);
if (id == nullptr || point_cache == nullptr) {
return false;
}
if (ID_IS_OVERRIDE_LIBRARY_REAL(id) || !ID_IS_EDITABLE(id)) {
CTX_wm_operator_poll_msg_set(
C, "Linked or library override data-blocks do not allow adding or removing caches");
return false;
}
return true;
}
struct PointCacheJob {
wmWindowManager *wm;
void *owner;
bool *stop, *do_update;
float *progress;
PTCacheBaker *baker;
};
static void ptcache_job_free(void *customdata)
{
PointCacheJob *job = static_cast<PointCacheJob *>(customdata);
MEM_delete(job->baker);
MEM_delete(job);
}
static int ptcache_job_break(void *customdata)
{
PointCacheJob *job = static_cast<PointCacheJob *>(customdata);
if (G.is_break) {
return 1;
}
if (job->stop && *(job->stop)) {
return 1;
}
return 0;
}
static void ptcache_job_update(void *customdata, float progress, int *cancel)
{
PointCacheJob *job = static_cast<PointCacheJob *>(customdata);
if (ptcache_job_break(job)) {
*cancel = 1;
}
*(job->do_update) = true;
*(job->progress) = progress;
}
static void ptcache_job_startjob(void *customdata, wmJobWorkerStatus *worker_status)
{
PointCacheJob *job = static_cast<PointCacheJob *>(customdata);
job->stop = &worker_status->stop;
job->do_update = &worker_status->do_update;
job->progress = &worker_status->progress;
G.is_break = false;
/* XXX annoying hack: needed to prevent data corruption when changing
* scene frame in separate threads
*/
WM_locked_interface_set(job->wm, true);
BKE_ptcache_bake(job->baker);
worker_status->do_update = true;
worker_status->stop = false;
}
static void ptcache_job_endjob(void *customdata)
{
PointCacheJob *job = static_cast<PointCacheJob *>(customdata);
Scene *scene = job->baker->scene;
WM_locked_interface_set(job->wm, false);
WM_main_add_notifier(NC_SCENE | ND_FRAME, scene);
WM_main_add_notifier(NC_OBJECT | ND_POINTCACHE, job->baker->pid.owner_id);
}
static void ptcache_free_bake(PointCache *cache)
{
if (cache->edit) {
if (!cache->edit->edited || true) { // XXX okee("Lose changes done in particle mode?")) {
PE_free_ptcache_edit(cache->edit);
cache->edit = nullptr;
cache->flag &= ~PTCACHE_BAKED;
}
}
else {
cache->flag &= ~PTCACHE_BAKED;
}
}
static PTCacheBaker *ptcache_baker_create(bContext *C, wmOperator *op, bool all)
{
PTCacheBaker *baker = MEM_new_zeroed<PTCacheBaker>("PTCacheBaker");
baker->bmain = CTX_data_main(C);
baker->scene = CTX_data_scene(C);
baker->view_layer = CTX_data_view_layer(C);
/* Depsgraph is used to sweep the frame range and evaluate scene at different times. */
baker->depsgraph = CTX_data_depsgraph_pointer(C);
baker->bake = RNA_boolean_get(op->ptr, "bake");
baker->render = false;
baker->anim_init = false;
baker->quick_step = 1;
if (!all) {
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", RNA_PointCache);
ID *id = ptr.owner_id;
Object *ob = (GS(id->name) == ID_OB) ? id_cast<Object *>(id) : nullptr;
PointCache *cache = static_cast<PointCache *>(ptr.data);
baker->pid = BKE_ptcache_id_find(ob, baker->scene, cache);
}
return baker;
}
static wmOperatorStatus ptcache_bake_exec(bContext *C, wmOperator *op)
{
bool all = STREQ(op->type->idname, "PTCACHE_OT_bake_all");
PTCacheBaker *baker = ptcache_baker_create(C, op, all);
BKE_ptcache_bake(baker);
MEM_delete(baker);
return OPERATOR_FINISHED;
}
static wmOperatorStatus ptcache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
bool all = STREQ(op->type->idname, "PTCACHE_OT_bake_all");
PointCacheJob *job = MEM_new_uninitialized<PointCacheJob>("PointCacheJob");
job->wm = CTX_wm_manager(C);
job->baker = ptcache_baker_create(C, op, all);
job->baker->bake_job = job;
job->baker->update_progress = ptcache_job_update;
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
CTX_data_scene(C),
"Baking point cache...",
WM_JOB_PROGRESS,
WM_JOB_TYPE_POINTCACHE);
WM_jobs_customdata_set(wm_job, job, ptcache_job_free);
WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_POINTCACHE, NC_OBJECT | ND_POINTCACHE);
WM_jobs_callbacks(wm_job, ptcache_job_startjob, nullptr, nullptr, ptcache_job_endjob);
WM_locked_interface_set(CTX_wm_manager(C), true);
WM_jobs_start(CTX_wm_manager(C), wm_job);
WM_event_add_modal_handler(C, op);
/* we must run modal until the bake job is done, otherwise the undo push
* happens before the job ends, which can lead to race conditions between
* the baking and file writing code */
return OPERATOR_RUNNING_MODAL;
}
static wmOperatorStatus ptcache_bake_modal(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Scene *scene = static_cast<Scene *>(op->customdata);
/* no running blender, remove handler and pass through */
if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_POINTCACHE)) {
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
return OPERATOR_PASS_THROUGH;
}
static void ptcache_bake_cancel(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
Scene *scene = static_cast<Scene *>(op->customdata);
/* kill on cancel, because job is using op->reports */
WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_POINTCACHE);
}
static wmOperatorStatus ptcache_free_bake_all_exec(bContext *C, wmOperator * /*op*/)
{
Scene *scene = CTX_data_scene(C);
ListBaseT<PTCacheID> pidlist;
FOREACH_SCENE_OBJECT_BEGIN (scene, ob) {
BKE_ptcache_ids_from_object(&pidlist, ob, scene, MAX_DUPLI_RECUR);
for (PTCacheID &pid : pidlist) {
ptcache_free_bake(pid.cache);
}
pidlist.free_no_destruct();
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
}
FOREACH_SCENE_OBJECT_END;
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
return OPERATOR_FINISHED;
}
void PTCACHE_OT_bake_all(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Bake All Physics";
ot->description = "Bake all physics simulations in the current scene";
ot->idname = "PTCACHE_OT_bake_all";
/* API callbacks. */
ot->exec = ptcache_bake_exec;
ot->invoke = ptcache_bake_invoke;
ot->modal = ptcache_bake_modal;
ot->cancel = ptcache_bake_cancel;
ot->poll = ptcache_bake_all_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "bake", true, "Bake", "");
}
void PTCACHE_OT_free_bake_all(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete All Physics Bakes";
ot->idname = "PTCACHE_OT_free_bake_all";
ot->description = "Delete all baked caches of all objects in the current scene";
/* API callbacks. */
ot->exec = ptcache_free_bake_all_exec;
ot->poll = ptcache_bake_all_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus ptcache_free_bake_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", RNA_PointCache);
PointCache *cache = static_cast<PointCache *>(ptr.data);
Object *ob = id_cast<Object *>(ptr.owner_id);
ptcache_free_bake(cache);
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
return OPERATOR_FINISHED;
}
static wmOperatorStatus ptcache_bake_from_cache_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", RNA_PointCache);
PointCache *cache = static_cast<PointCache *>(ptr.data);
Object *ob = id_cast<Object *>(ptr.owner_id);
cache->flag |= PTCACHE_BAKED;
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
return OPERATOR_FINISHED;
}
void PTCACHE_OT_bake(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Bake Physics";
ot->description = "Bake physics";
ot->idname = "PTCACHE_OT_bake";
/* API callbacks. */
ot->exec = ptcache_bake_exec;
ot->invoke = ptcache_bake_invoke;
ot->modal = ptcache_bake_modal;
ot->cancel = ptcache_bake_cancel;
ot->poll = ptcache_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "bake", false, "Bake", "");
}
void PTCACHE_OT_free_bake(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Physics Bake";
ot->description = "Delete physics bake";
ot->idname = "PTCACHE_OT_free_bake";
/* API callbacks. */
ot->exec = ptcache_free_bake_exec;
ot->poll = ptcache_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void PTCACHE_OT_bake_from_cache(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Bake from Cache";
ot->description = "Bake from cache";
ot->idname = "PTCACHE_OT_bake_from_cache";
/* API callbacks. */
ot->exec = ptcache_bake_from_cache_exec;
ot->poll = ptcache_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus ptcache_add_new_exec(bContext *C, wmOperator * /*op*/)
{
Scene *scene = CTX_data_scene(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", RNA_PointCache);
Object *ob = id_cast<Object *>(ptr.owner_id);
PointCache *cache = static_cast<PointCache *>(ptr.data);
PTCacheID pid = BKE_ptcache_id_find(ob, scene, cache);
if (pid.cache) {
PointCache *cache_new = BKE_ptcache_add(pid.ptcaches);
cache_new->step = pid.default_step;
*(pid.cache_ptr) = cache_new;
DEG_id_tag_update(&ob->id, ID_RECALC_POINT_CACHE);
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
}
return OPERATOR_FINISHED;
}
static wmOperatorStatus ptcache_remove_exec(bContext *C, wmOperator * /*op*/)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "point_cache", RNA_PointCache);
Scene *scene = CTX_data_scene(C);
Object *ob = id_cast<Object *>(ptr.owner_id);
PointCache *cache = static_cast<PointCache *>(ptr.data);
PTCacheID pid = BKE_ptcache_id_find(ob, scene, cache);
/* don't delete last cache */
if (pid.cache && pid.ptcaches->first != pid.ptcaches->last) {
BLI_remlink(pid.ptcaches, pid.cache);
BKE_ptcache_free(pid.cache);
*(pid.cache_ptr) = static_cast<PointCache *>(pid.ptcaches->first);
DEG_id_tag_update(&ob->id, ID_RECALC_SYNC_TO_EVAL);
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
}
return OPERATOR_FINISHED;
}
void PTCACHE_OT_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add New Cache";
ot->description = "Add new cache";
ot->idname = "PTCACHE_OT_add";
/* API callbacks. */
ot->exec = ptcache_add_new_exec;
ot->poll = ptcache_add_remove_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void PTCACHE_OT_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Current Cache";
ot->description = "Delete current cache";
ot->idname = "PTCACHE_OT_remove";
/* API callbacks. */
ot->exec = ptcache_remove_exec;
ot->poll = ptcache_add_remove_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
} // namespace blender

View File

@@ -0,0 +1,219 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup editor_physics
* \brief Rigid Body constraint editing operators
*/
#include <cstdlib>
#include <cstring>
#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "BKE_collection.hh"
#include "BKE_context.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_library.hh"
#include "BKE_report.hh"
#include "BKE_rigidbody.h"
#include "BKE_scene.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_object.hh"
#include "ED_physics.hh"
#include "ED_screen.hh"
#include "physics_intern.hh"
namespace blender {
/* ********************************************** */
/* Helper API's for RigidBody Constraint Editing */
static bool operator_rigidbody_constraints_editable_poll(Scene *scene)
{
if (scene == nullptr || !ID_IS_EDITABLE(scene) || ID_IS_OVERRIDE_LIBRARY(scene) ||
(scene->rigidbody_world != nullptr && scene->rigidbody_world->constraints != nullptr &&
(!ID_IS_EDITABLE(scene->rigidbody_world->constraints) ||
ID_IS_OVERRIDE_LIBRARY(scene->rigidbody_world->constraints))))
{
return false;
}
return true;
}
static bool operator_rigidbody_con_active_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
if (!operator_rigidbody_constraints_editable_poll(scene)) {
return false;
}
Object *ob = ed::object::context_active_object(C);
return (ob && ob->rigidbody_constraint && ED_operator_object_active_editable_ex(C, ob));
}
static bool operator_rigidbody_con_add_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
if (!operator_rigidbody_constraints_editable_poll(scene)) {
return false;
}
return ED_operator_object_active_editable(C);
}
bool ED_rigidbody_constraint_add(
Main *bmain, Scene *scene, Object *ob, eRigidBodyCon_Type type, ReportList *reports)
{
RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
/* check that object doesn't already have a constraint */
if (ob->rigidbody_constraint) {
BKE_reportf(
reports, RPT_INFO, "Object '%s' already has a Rigid Body Constraint", ob->id.name + 2);
return false;
}
/* create constraint group if it doesn't already exits */
if (rbw->constraints == nullptr) {
rbw->constraints = BKE_collection_add(bmain, nullptr, "RigidBodyConstraints");
id_us_plus(&rbw->constraints->id);
}
/* make rigidbody constraint settings */
ob->rigidbody_constraint = BKE_rigidbody_create_constraint(scene, ob, type);
/* add constraint to rigid body constraint group */
BKE_collection_object_add(bmain, rbw->constraints, ob);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
DEG_id_tag_update(&rbw->constraints->id, ID_RECALC_SYNC_TO_EVAL);
return true;
}
void ED_rigidbody_constraint_remove(Main *bmain, Scene *scene, Object *ob)
{
BKE_rigidbody_remove_constraint(bmain, scene, ob, false);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
/* ********************************************** */
/* Active Object Add/Remove Operators */
/* ************ Add Rigid Body Constraint ************** */
static wmOperatorStatus rigidbody_con_add_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
Object *ob = ed::object::context_active_object(C);
eRigidBodyCon_Type type = eRigidBodyCon_Type(RNA_enum_get(op->ptr, "type"));
bool changed;
/* Poll ensures. */
BLI_assert(scene && ob);
/* The rigid body world is not ensured by the poll. */
if (rbw == nullptr) {
BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to add Rigid Body Constraint to");
return OPERATOR_CANCELLED;
}
/* Pinned objects could be from another scene. */
if (!BKE_scene_object_find(*bmain, scene, ob)) {
BKE_report(op->reports, RPT_ERROR, "No object in the scene to add Rigid Body Constraint to");
return OPERATOR_CANCELLED;
}
/* apply to active object */
changed = ED_rigidbody_constraint_add(bmain, scene, ob, type, op->reports);
if (changed) {
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
/* done */
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_constraint_add(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_constraint_add";
ot->name = "Add Rigid Body Constraint";
ot->description = "Add Rigid Body Constraint to active object";
/* callbacks */
ot->exec = rigidbody_con_add_exec;
ot->poll = operator_rigidbody_con_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna,
"type",
rna_enum_rigidbody_constraint_type_items,
RBC_TYPE_FIXED,
"Rigid Body Constraint Type",
"");
}
/* ************ Remove Rigid Body Constraint ************** */
static wmOperatorStatus rigidbody_con_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *ob = ed::object::context_active_object(C);
/* Poll ensures. */
BLI_assert(scene && ob && ob->rigidbody_constraint);
/* Pinned objects could be from another scene. */
if (!BKE_scene_object_find(*bmain, scene, ob)) {
BKE_report(
op->reports, RPT_ERROR, "No object in the scene to remove Rigid Body Constraint from");
return OPERATOR_CANCELLED;
}
ED_rigidbody_constraint_remove(bmain, scene, ob);
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
/* done */
return OPERATOR_FINISHED;
}
void RIGIDBODY_OT_constraint_remove(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_constraint_remove";
ot->name = "Remove Rigid Body Constraint";
ot->description = "Remove Rigid Body Constraint from Object";
/* callbacks */
ot->exec = rigidbody_con_remove_exec;
ot->poll = operator_rigidbody_con_active_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
} // namespace blender

View File

@@ -0,0 +1,575 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup editor_physics
* \brief Rigid Body object editing operators
*/
#include <cstdlib>
#include <cstring>
#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "BLT_translation.hh"
#include "BKE_context.hh"
#include "BKE_library.hh"
#include "BKE_report.hh"
#include "BKE_rigidbody.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_object.hh"
#include "ED_physics.hh"
#include "ED_screen.hh"
#include "physics_intern.hh"
namespace blender {
/* ********************************************** */
/* Helper API's for RigidBody Objects Editing */
static bool operator_rigidbody_editable_poll(Scene *scene)
{
if (scene == nullptr || !ID_IS_EDITABLE(scene) || ID_IS_OVERRIDE_LIBRARY(scene) ||
(scene->rigidbody_world != nullptr && scene->rigidbody_world->group != nullptr &&
(!ID_IS_EDITABLE(scene->rigidbody_world->group) ||
ID_IS_OVERRIDE_LIBRARY(scene->rigidbody_world->group))))
{
return false;
}
return true;
}
static bool operator_rigidbody_active_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
if (!operator_rigidbody_editable_poll(scene)) {
return false;
}
if (ED_operator_object_active_editable(C)) {
Object *ob = ed::object::context_active_object(C);
return (ob && ob->rigidbody_object);
}
return false;
}
static bool operator_rigidbody_add_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
if (!operator_rigidbody_editable_poll(scene)) {
return false;
}
if (ED_operator_object_active_editable(C)) {
Object *ob = ed::object::context_active_object(C);
return (ob && ob->type == OB_MESH);
}
return false;
}
/* ----------------- */
bool ED_rigidbody_object_add(
Main *bmain, Scene *scene, Object *ob, eRigidBodyOb_Type type, ReportList *reports)
{
return BKE_rigidbody_add_object(bmain, scene, ob, type, reports);
}
void ED_rigidbody_object_remove(Main *bmain, Scene *scene, Object *ob)
{
BKE_rigidbody_remove_object(bmain, scene, ob, false);
}
/* ********************************************** */
/* Active Object Add/Remove Operators */
/* ************ Add Rigid Body ************** */
static wmOperatorStatus rigidbody_object_add_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *ob = ed::object::context_active_object(C);
eRigidBodyOb_Type type = eRigidBodyOb_Type(RNA_enum_get(op->ptr, "type"));
bool changed;
/* apply to active object */
changed = ED_rigidbody_object_add(bmain, scene, ob, type, op->reports);
if (changed) {
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, nullptr);
/* done */
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_object_add(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_object_add";
ot->name = "Add Rigid Body";
ot->description = "Add active object as Rigid Body";
/* callbacks */
ot->exec = rigidbody_object_add_exec;
ot->poll = operator_rigidbody_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna,
"type",
rna_enum_rigidbody_object_type_items,
RBO_TYPE_ACTIVE,
"Rigid Body Type",
"");
}
/* ************ Remove Rigid Body ************** */
static wmOperatorStatus rigidbody_object_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *ob = ed::object::context_active_object(C);
bool changed = false;
/* apply to active object */
if (!ELEM(nullptr, ob, ob->rigidbody_object)) {
ED_rigidbody_object_remove(bmain, scene, ob);
changed = true;
}
if (changed) {
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, nullptr);
/* done */
return OPERATOR_FINISHED;
}
BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_object_remove(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_object_remove";
ot->name = "Remove Rigid Body";
ot->description = "Remove Rigid Body settings from Object";
/* callbacks */
ot->exec = rigidbody_object_remove_exec;
ot->poll = operator_rigidbody_active_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ********************************************** */
/* Selected Object Add/Remove Operators */
/* ************ Add Rigid Bodies ************** */
static wmOperatorStatus rigidbody_objects_add_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
eRigidBodyOb_Type type = eRigidBodyOb_Type(RNA_enum_get(op->ptr, "type"));
bool changed = false;
/* create rigid body objects and add them to the world's group */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
changed |= ED_rigidbody_object_add(bmain, scene, ob, type, op->reports);
}
CTX_DATA_END;
if (changed) {
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, nullptr);
/* done */
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_objects_add(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_objects_add";
ot->name = "Add Rigid Bodies";
ot->description = "Add selected objects as Rigid Bodies";
/* callbacks */
ot->exec = rigidbody_objects_add_exec;
ot->poll = operator_rigidbody_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna,
"type",
rna_enum_rigidbody_object_type_items,
RBO_TYPE_ACTIVE,
"Rigid Body Type",
"");
}
/* ************ Remove Rigid Bodies ************** */
static wmOperatorStatus rigidbody_objects_remove_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
bool changed = false;
/* apply this to all selected objects... */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob->rigidbody_object) {
ED_rigidbody_object_remove(bmain, scene, ob);
changed = true;
}
}
CTX_DATA_END;
if (changed) {
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, nullptr);
/* done */
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_objects_remove(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_objects_remove";
ot->name = "Remove Rigid Bodies";
ot->description = "Remove selected objects from Rigid Body simulation";
/* callbacks */
ot->exec = rigidbody_objects_remove_exec;
ot->poll = operator_rigidbody_active_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ********************************************** */
/* Utility Operators */
/* ************ Change Collision Shapes ************** */
static wmOperatorStatus rigidbody_objects_shape_change_exec(bContext *C, wmOperator *op)
{
int shape = RNA_enum_get(op->ptr, "type");
bool changed = false;
/* apply this to all selected objects... */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob->rigidbody_object) {
/* use RNA-system to change the property and perform all necessary changes */
PointerRNA ptr = RNA_pointer_create_discrete(
&ob->id, RNA_RigidBodyObject, ob->rigidbody_object);
RNA_enum_set(&ptr, "collision_shape", shape);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
changed = true;
}
}
CTX_DATA_END;
if (changed) {
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, nullptr);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
/* done */
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void RIGIDBODY_OT_shape_change(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_shape_change";
ot->name = "Change Collision Shape";
ot->description = "Change collision shapes for selected Rigid Body Objects";
/* callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = rigidbody_objects_shape_change_exec;
ot->poll = operator_rigidbody_active_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna,
"type",
rna_enum_rigidbody_object_shape_items,
RB_SHAPE_TRIMESH,
"Rigid Body Shape",
"");
}
/* ************ Calculate Mass ************** */
/* Entry in material density table */
struct rbMaterialDensityItem {
const char *name; /* Name of material */
float density; /* Density (kg/m^3) */
};
/* Preset density values for materials (kg/m^3)
* Selected values obtained from:
* 1) http://www.jaredzone.info/2010/09/densities.html
* 2) http://www.avlandesign.com/density_construction.htm
* 3) http://www.avlandesign.com/density_metal.htm
*/
static rbMaterialDensityItem RB_MATERIAL_DENSITY_TABLE[] = {
{N_("Air"), 1.0f}, /* not quite; adapted from 1.43 for oxygen for use as default */
{N_("Acrylic"), 1400.0f},
{N_("Asphalt (Crushed)"), 721.0f},
{N_("Bark"), 240.0f},
{N_("Beans (Cocoa)"), 593.0f},
{N_("Beans (Soy)"), 721.0f},
{N_("Brick (Pressed)"), 2400.0f},
{N_("Brick (Common)"), 2000.0f},
{N_("Brick (Soft)"), 1600.0f},
{N_("Brass"), 8216.0f},
{N_("Bronze"), 8860.0f},
{N_("Carbon (Solid)"), 2146.0f},
{N_("Cardboard"), 689.0f},
{N_("Cast Iron"), 7150.0f}, /* {N_("Cement"), 1442.0f}, */
{N_("Chalk (Solid)"), 2499.0f}, /* {N_("Coffee (Fresh/Roast)"), ~500}, */
{N_("Concrete"), 2320.0f},
{N_("Charcoal"), 208.0f},
{N_("Cork"), 240.0f},
{N_("Copper"), 8933.0f},
{N_("Garbage"), 481.0f},
{N_("Glass (Broken)"), 1940.0f},
{N_("Glass (Solid)"), 2190.0f},
{N_("Gold"), 19282.0f},
{N_("Granite (Broken)"), 1650.0f},
{N_("Granite (Solid)"), 2691.0f},
{N_("Gravel"), 2780.0f},
{N_("Ice (Crushed)"), 593.0f},
{N_("Ice (Solid)"), 919.0f},
{N_("Iron"), 7874.0f},
{N_("Lead"), 11342.0f},
{N_("Limestone (Broken)"), 1554.0f},
{N_("Limestone (Solid)"), 2611.0f},
{N_("Marble (Broken)"), 1570.0f},
{N_("Marble (Solid)"), 2563.0f},
{N_("Paper"), 1201.0f},
{N_("Peanuts (Shelled)"), 641.0f},
{N_("Peanuts (Not Shelled)"), 272.0f},
{N_("Plaster"), 849.0f},
{N_("Plastic"), 1200.0f},
{N_("Polystyrene"), 1050.0f},
{N_("Rubber"), 1522.0f},
{N_("Silver"), 10501.0f},
{N_("Steel"), 7860.0f},
{N_("Stone"), 2515.0f},
{N_("Stone (Crushed)"), 1602.0f},
{N_("Timber"), 610.0f},
};
static const int NUM_RB_MATERIAL_PRESETS = sizeof(RB_MATERIAL_DENSITY_TABLE) /
sizeof(rbMaterialDensityItem);
/* dynamically generate list of items
* - Although there is a runtime cost, this has a lower maintenance cost
* in the long run than other two-list solutions...
*/
static const EnumPropertyItem *rigidbody_materials_itemf(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
EnumPropertyItem item_tmp = {0};
EnumPropertyItem *item = nullptr;
int totitem = 0;
int i = 0;
/* add each preset to the list */
for (i = 0; i < NUM_RB_MATERIAL_PRESETS; i++) {
rbMaterialDensityItem *preset = &RB_MATERIAL_DENSITY_TABLE[i];
item_tmp.identifier = preset->name;
item_tmp.name = IFACE_(preset->name);
item_tmp.value = i;
RNA_enum_item_add(&item, &totitem, &item_tmp);
}
/* add special "custom" entry to the end of the list */
{
item_tmp.identifier = "Custom";
item_tmp.name = IFACE_("Custom");
item_tmp.value = -1;
RNA_enum_item_add(&item, &totitem, &item_tmp);
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
/* ------------------------------------------ */
static wmOperatorStatus rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
int material = RNA_enum_get(op->ptr, "material");
float density;
bool changed = false;
/* get density (kg/m^3) to apply */
if (material >= 0) {
/* get density from table, and store in props for later repeating */
if (material >= NUM_RB_MATERIAL_PRESETS) {
material = 0;
}
density = RB_MATERIAL_DENSITY_TABLE[material].density;
RNA_float_set(op->ptr, "density", density);
}
else {
/* custom - grab from whatever value is set */
density = RNA_float_get(op->ptr, "density");
}
/* Apply this to all selected objects (with rigid-bodies). */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob->rigidbody_object) {
float volume; /* m^3 */
float mass; /* kg */
/* mass is calculated from the approximate volume of the object,
* and the density of the material we're simulating
*/
Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
BKE_rigidbody_calc_volume(ob_eval, &volume);
mass = volume * density;
/* use RNA-system to change the property and perform all necessary changes */
PointerRNA ptr = RNA_pointer_create_discrete(
&ob->id, RNA_RigidBodyObject, ob->rigidbody_object);
RNA_float_set(&ptr, "mass", mass);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
changed = true;
}
}
CTX_DATA_END;
if (changed) {
/* send updates */
WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, nullptr);
/* done */
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static bool mass_calculate_poll_property(const bContext * /*C*/,
wmOperator *op,
const PropertyRNA *prop)
{
const char *prop_id = RNA_property_identifier(prop);
/* Disable density input when not using the 'Custom' preset. */
if (STREQ(prop_id, "density")) {
int material = RNA_enum_get(op->ptr, "material");
if (material >= 0) {
RNA_def_property_clear_flag(const_cast<PropertyRNA *>(prop), PROP_EDITABLE);
}
else {
RNA_def_property_flag(const_cast<PropertyRNA *>(prop), PROP_EDITABLE);
}
}
return true;
}
void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->idname = "RIGIDBODY_OT_mass_calculate";
ot->name = "Calculate Mass";
ot->description = "Automatically calculate mass values for Rigid Body Objects based on volume";
/* callbacks */
ot->invoke = WM_menu_invoke; /* XXX */
ot->exec = rigidbody_objects_calc_mass_exec;
ot->poll = operator_rigidbody_active_poll;
ot->poll_property = mass_calculate_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = prop = RNA_def_enum(
ot->srna,
"material",
rna_enum_dummy_DEFAULT_items,
0,
"Material Preset",
"Type of material that objects are made of (determines material density)");
RNA_def_enum_funcs(prop, rigidbody_materials_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
RNA_def_float(ot->srna,
"density",
1.0,
FLT_MIN,
FLT_MAX,
"Density",
"Density value (kg/m^3), allows custom value if the 'Custom' preset is used",
1.0f,
2500.0f);
}
/* ********************************************** */
} // namespace blender

View File

@@ -0,0 +1,205 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup editor_physics
* \brief Rigid Body world editing operators
*/
#include <cstdlib>
#include <cstring>
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#ifdef WITH_BULLET
# include "RBI_api.h"
#endif
#include "BKE_context.hh"
#include "BKE_report.hh"
#include "BKE_rigidbody.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "RNA_access.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_screen.hh"
#include "physics_intern.hh"
namespace blender {
/* ********************************************** */
/* API */
/* check if there is an active rigid body world */
static bool rigidbody_world_active_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
return (scene && scene->rigidbody_world);
}
static bool rigidbody_world_add_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
return (scene && scene->rigidbody_world == nullptr);
}
/* ********************************************** */
/* OPERATORS - Management */
/* ********** Add RigidBody World **************** */
static wmOperatorStatus rigidbody_world_add_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
RigidBodyWorld *rbw;
rbw = BKE_rigidbody_create_world(scene);
// BKE_rigidbody_validate_sim_world(scene, rbw, false);
scene->rigidbody_world = rbw;
/* Full rebuild of DEG! */
DEG_relations_tag_update(bmain);
DEG_id_tag_update_ex(bmain, &scene->id, ID_RECALC_ANIMATION);
return OPERATOR_FINISHED;
}
void RIGIDBODY_OT_world_add(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_world_add";
ot->name = "Add Rigid Body World";
ot->description = "Add Rigid Body simulation world to the current scene";
/* callbacks */
ot->exec = rigidbody_world_add_exec;
ot->poll = rigidbody_world_add_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ********** Remove RigidBody World ************* */
static wmOperatorStatus rigidbody_world_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
RigidBodyWorld *rbw = scene->rigidbody_world;
/* sanity checks */
if (ELEM(nullptr, scene, rbw)) {
BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to remove");
return OPERATOR_CANCELLED;
}
BKE_rigidbody_free_world(scene);
/* Full rebuild of DEG! */
DEG_relations_tag_update(bmain);
DEG_id_tag_update_ex(bmain, &scene->id, ID_RECALC_ANIMATION);
/* done */
return OPERATOR_FINISHED;
}
void RIGIDBODY_OT_world_remove(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_world_remove";
ot->name = "Remove Rigid Body World";
ot->description = "Remove Rigid Body simulation world from the current scene";
/* callbacks */
ot->exec = rigidbody_world_remove_exec;
ot->poll = rigidbody_world_active_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ********************************************** */
/* UTILITY OPERATORS */
/* ********** Export RigidBody World ************* */
static wmOperatorStatus rigidbody_world_export_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
RigidBodyWorld *rbw = scene->rigidbody_world;
char filepath[FILE_MAX];
/* sanity checks */
if (ELEM(nullptr, scene, rbw)) {
BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to export");
return OPERATOR_CANCELLED;
}
rbDynamicsWorld *physics_world = BKE_rigidbody_world_physics(rbw);
if (physics_world == nullptr) {
BKE_report(
op->reports, RPT_ERROR, "Rigid Body World has no associated physics data to export");
return OPERATOR_CANCELLED;
}
RNA_string_get(op->ptr, "filepath", filepath);
#ifdef WITH_BULLET
RB_dworld_export(physics_world, filepath);
#endif
return OPERATOR_FINISHED;
}
static wmOperatorStatus rigidbody_world_export_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (!RNA_struct_property_is_set(op->ptr, "relative_path")) {
RNA_boolean_set(op->ptr, "relative_path", (U.flag & USER_RELPATHS) != 0);
}
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return rigidbody_world_export_exec(C, op);
}
/* TODO: use the actual rigidbody world's name + .bullet instead of this temp crap */
RNA_string_set(op->ptr, "filepath", "rigidbodyworld_export.bullet");
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
void RIGIDBODY_OT_world_export(wmOperatorType *ot)
{
/* identifiers */
ot->idname = "RIGIDBODY_OT_world_export";
ot->name = "Export Rigid Body World";
ot->description =
"Export Rigid Body world to the simulator's own file-format "
"(i.e. '.bullet' for Bullet Physics)";
/* callbacks */
ot->invoke = rigidbody_world_export_invoke;
ot->exec = rigidbody_world_export_exec;
ot->poll = rigidbody_world_active_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER,
FILE_SPECIAL,
FILE_SAVE,
WM_FILESEL_RELPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
}
} // namespace blender