WIP: Experiment: Geometry Nodes: support baking individual simulations #112179
@ -312,6 +312,24 @@ class SCENE_PT_physics(SceneButtonsPanel, Panel):
|
||||
layout.prop(scene, "gravity")
|
||||
|
||||
|
||||
class SCENE_PT_simulation(SceneButtonsPanel, Panel):
|
||||
bl_label = "Simulation"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
scene = context.scene
|
||||
|
||||
col = layout.column()
|
||||
col.prop(scene, "use_custom_simulation_range", text="Simulation Range")
|
||||
subcol = col.column(align=True)
|
||||
subcol.active = scene.use_custom_simulation_range
|
||||
subcol.prop(scene, "simulation_frame_start", text="Start")
|
||||
subcol.prop(scene, "simulation_frame_end", text="End")
|
||||
|
||||
|
||||
class SCENE_PT_rigid_body_world(SceneButtonsPanel, Panel):
|
||||
bl_label = "Rigid Body World"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
@ -409,6 +427,7 @@ classes = (
|
||||
SCENE_PT_scene,
|
||||
SCENE_PT_unit,
|
||||
SCENE_PT_physics,
|
||||
SCENE_PT_simulation,
|
||||
SCENE_PT_keying_sets,
|
||||
SCENE_PT_keying_set_paths,
|
||||
SCENE_PT_keyframing_settings,
|
||||
|
@ -48,6 +48,9 @@ class TIME_HT_editor_buttons:
|
||||
row.operator("screen.keyframe_jump", text="", icon='NEXT_KEYFRAME').next = True
|
||||
row.operator("screen.frame_jump", text="", icon='FF').end = True
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("screen.animation_play_with_preroll", text="Play with Preroll")
|
||||
|
||||
layout.separator_spacer()
|
||||
|
||||
row = layout.row()
|
||||
|
@ -84,6 +84,10 @@ std::optional<BakePath> get_node_bake_path(const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id);
|
||||
std::optional<IndexRange> get_node_bake_frame_range(const Scene &scene,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id);
|
||||
std::optional<std::string> get_modifier_bake_path(const Main &bmain,
|
||||
const Object &object,
|
||||
const NodesModifierData &nmd);
|
||||
|
@ -71,17 +71,52 @@ std::optional<bake::BakePath> get_node_bake_path(const Main &bmain,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id)
|
||||
{
|
||||
const NodesModifierBake *bake = nmd.find_bake(node_id);
|
||||
if (bake == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH) {
|
||||
if (StringRef(bake->directory).is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const char *base_path = ID_BLEND_PATH(&bmain, &object.id);
|
||||
char absolute_bake_dir[FILE_MAX];
|
||||
STRNCPY(absolute_bake_dir, bake->directory);
|
||||
BLI_path_abs(absolute_bake_dir, base_path);
|
||||
return bake::BakePath::from_single_root(absolute_bake_dir);
|
||||
}
|
||||
const std::optional<std::string> modifier_bake_path = get_modifier_bake_path(bmain, object, nmd);
|
||||
if (!modifier_bake_path) {
|
||||
return std::nullopt;
|
||||
}
|
||||
char bake_dir[FILE_MAX];
|
||||
BLI_path_join(
|
||||
bake_dir, sizeof(bake_dir), modifier_bake_path->c_str(), std::to_string(node_id).c_str());
|
||||
return bake::BakePath::from_single_root(bake_dir);
|
||||
}
|
||||
|
||||
char zone_bake_dir[FILE_MAX];
|
||||
BLI_path_join(zone_bake_dir,
|
||||
sizeof(zone_bake_dir),
|
||||
modifier_bake_path->c_str(),
|
||||
std::to_string(node_id).c_str());
|
||||
return bake::BakePath::from_single_root(zone_bake_dir);
|
||||
static IndexRange fix_frame_range(const int start, const int end)
|
||||
{
|
||||
const int num_frames = std::max(1, end - start + 1);
|
||||
return IndexRange(start, num_frames);
|
||||
}
|
||||
|
||||
std::optional<IndexRange> get_node_bake_frame_range(const Scene &scene,
|
||||
const Object & /*object*/,
|
||||
const NodesModifierData &nmd,
|
||||
int node_id)
|
||||
{
|
||||
const NodesModifierBake *bake = nmd.find_bake(node_id);
|
||||
if (bake == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (bake->flag & NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE) {
|
||||
return fix_frame_range(bake->frame_start, bake->frame_end);
|
||||
}
|
||||
if (scene.flag & SCE_CUSTOM_SIMULATION_RANGE) {
|
||||
return fix_frame_range(scene.simulation_frame_start, scene.simulation_frame_end);
|
||||
}
|
||||
return fix_frame_range(scene.r.sfra, scene.r.efra);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -607,3 +607,24 @@ bool bNodeTree::node_id_path_from_nested_node_ref(const int32_t nested_node_id,
|
||||
}
|
||||
return group->node_id_path_from_nested_node_ref(ref->path.id_in_node, r_node_ids);
|
||||
}
|
||||
|
||||
const bNode *bNodeTree::find_nested_node(const int32_t nested_node_id) const
|
||||
{
|
||||
const bNestedNodeRef *ref = this->find_nested_node_ref(nested_node_id);
|
||||
if (ref == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
const int32_t node_id = ref->path.node_id;
|
||||
const bNode *node = this->node_by_id(node_id);
|
||||
if (node == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!node->is_group()) {
|
||||
return node;
|
||||
}
|
||||
const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node->id);
|
||||
if (group == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return group->find_nested_node(ref->path.id_in_node);
|
||||
}
|
||||
|
@ -1232,5 +1232,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
scene->simulation_frame_start = scene->r.sfra;
|
||||
scene->simulation_frame_end = scene->r.efra;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_vector_set.hh"
|
||||
#include "ED_node_c.hh"
|
||||
|
||||
@ -33,4 +35,9 @@ void node_insert_on_link_flags_clear(bNodeTree &node_tree);
|
||||
*/
|
||||
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale);
|
||||
|
||||
/**
|
||||
* Find the nested node id of a currently visible node in the root tree.
|
||||
*/
|
||||
std::optional<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &node);
|
||||
|
||||
} // namespace blender::ed::space_node
|
||||
|
@ -308,7 +308,8 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen);
|
||||
* redraws: uses defines from `stime->redraws`
|
||||
* \param enable: 1 - forward on, -1 - backwards on, 0 - off.
|
||||
*/
|
||||
void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable);
|
||||
void ED_screen_animation_timer(
|
||||
bContext *C, int redraws, int sync, int enable, bool fix_start_frame);
|
||||
void ED_screen_animation_timer_update(bScreen *screen, int redraws);
|
||||
void ED_screen_restore_temp_type(bContext *C, ScrArea *area);
|
||||
ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type);
|
||||
@ -445,7 +446,7 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph);
|
||||
/**
|
||||
* Toggle operator.
|
||||
*/
|
||||
int ED_screen_animation_play(bContext *C, int sync, int mode);
|
||||
int ED_screen_animation_play(bContext *C, int sync, int mode, bool fix_start_frame);
|
||||
/**
|
||||
* Find window that owns the animation timer.
|
||||
*/
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_pointcloud.h"
|
||||
@ -222,15 +223,17 @@ static bool bake_simulation_poll(bContext *C)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ZoneBakeData {
|
||||
int zone_id;
|
||||
struct NodeBakeData {
|
||||
int id;
|
||||
bake::BakePath path;
|
||||
int frame_start;
|
||||
int frame_end;
|
||||
std::unique_ptr<bake::BlobSharing> blob_sharing;
|
||||
};
|
||||
|
||||
struct ModifierBakeData {
|
||||
NodesModifierData *nmd;
|
||||
Vector<ZoneBakeData> zones;
|
||||
Vector<NodeBakeData> nodes;
|
||||
};
|
||||
|
||||
struct ObjectBakeData {
|
||||
@ -243,7 +246,7 @@ struct BakeSimulationJob {
|
||||
Main *bmain;
|
||||
Depsgraph *depsgraph;
|
||||
Scene *scene;
|
||||
Vector<Object *> objects;
|
||||
Vector<ObjectBakeData> objects;
|
||||
};
|
||||
|
||||
static void bake_simulation_job_startjob(void *customdata,
|
||||
@ -256,9 +259,169 @@ static void bake_simulation_job_startjob(void *customdata,
|
||||
G.is_break = false;
|
||||
WM_set_locked_interface(job.wm, true);
|
||||
|
||||
int global_bake_start_frame = INT32_MAX;
|
||||
int global_bake_end_frame = INT32_MIN;
|
||||
|
||||
for (ObjectBakeData &object_bake : job.objects) {
|
||||
for (ModifierBakeData &modifier_bake : object_bake.modifiers) {
|
||||
for (NodeBakeData &node_bake : modifier_bake.nodes) {
|
||||
global_bake_start_frame = std::min(global_bake_start_frame, node_bake.frame_start);
|
||||
global_bake_end_frame = std::max(global_bake_end_frame, node_bake.frame_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*progress = 0.0f;
|
||||
*do_update = true;
|
||||
|
||||
const int frames_to_bake = global_bake_end_frame - global_bake_start_frame + 1;
|
||||
|
||||
const float frame_step_size = 1.0f;
|
||||
const float progress_per_frame = frame_step_size / frames_to_bake;
|
||||
const int old_frame = job.scene->r.cfra;
|
||||
|
||||
for (float frame_f = global_bake_start_frame; frame_f <= global_bake_end_frame;
|
||||
frame_f += frame_step_size)
|
||||
{
|
||||
const SubFrame frame{frame_f};
|
||||
|
||||
if (G.is_break || (stop != nullptr && *stop)) {
|
||||
break;
|
||||
}
|
||||
|
||||
job.scene->r.cfra = frame.frame();
|
||||
job.scene->r.subframe = frame.subframe();
|
||||
|
||||
BKE_scene_graph_update_for_newframe(job.depsgraph);
|
||||
|
||||
const std::string frame_file_name = bake::frame_to_file_name(frame);
|
||||
|
||||
for (ObjectBakeData &object_bake_data : job.objects) {
|
||||
for (ModifierBakeData &modifier_bake_data : object_bake_data.modifiers) {
|
||||
NodesModifierData &nmd = *modifier_bake_data.nmd;
|
||||
const bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
|
||||
for (NodeBakeData &node_bake_data : modifier_bake_data.nodes) {
|
||||
if (!modifier_cache.cache_by_id.contains(node_bake_data.id)) {
|
||||
continue;
|
||||
}
|
||||
const bake::NodeCache &node_cache = *modifier_cache.cache_by_id.lookup(
|
||||
node_bake_data.id);
|
||||
if (node_cache.frame_caches.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
const bake::FrameCache &frame_cache = *node_cache.frame_caches.last();
|
||||
if (frame_cache.frame != frame) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bake::BakePath path = node_bake_data.path;
|
||||
|
||||
const std::string blob_file_name = frame_file_name + ".blob";
|
||||
|
||||
char blob_path[FILE_MAX];
|
||||
BLI_path_join(
|
||||
blob_path, sizeof(blob_path), path.blobs_dir.c_str(), blob_file_name.c_str());
|
||||
char meta_path[FILE_MAX];
|
||||
BLI_path_join(meta_path,
|
||||
sizeof(meta_path),
|
||||
path.meta_dir.c_str(),
|
||||
(frame_file_name + ".json").c_str());
|
||||
BLI_file_ensure_parent_dir_exists(meta_path);
|
||||
BLI_file_ensure_parent_dir_exists(blob_path);
|
||||
fstream blob_file{blob_path, std::ios::out | std::ios::binary};
|
||||
bake::DiskBlobWriter blob_writer{blob_file_name, blob_file, 0};
|
||||
fstream meta_file{meta_path, std::ios::out};
|
||||
bake::serialize_bake(
|
||||
frame_cache.state, blob_writer, *node_bake_data.blob_sharing, meta_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*progress += progress_per_frame;
|
||||
*do_update = true;
|
||||
}
|
||||
|
||||
for (ObjectBakeData &object_bake_data : job.objects) {
|
||||
for (ModifierBakeData &modifier_bake_data : object_bake_data.modifiers) {
|
||||
NodesModifierData &nmd = *modifier_bake_data.nmd;
|
||||
for (NodeBakeData &node_bake_data : modifier_bake_data.nodes) {
|
||||
if (std::unique_ptr<bake::NodeCache> *node_cache_ptr =
|
||||
nmd.runtime->cache->cache_by_id.lookup_ptr(node_bake_data.id))
|
||||
{
|
||||
bake::NodeCache &node_cache = **node_cache_ptr;
|
||||
if (!node_cache.frame_caches.is_empty()) {
|
||||
/* Tag the caches as being baked so that they are not changed anymore. */
|
||||
node_cache.cache_status = bake::CacheStatus::Baked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DEG_id_tag_update(&object_bake_data.object->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
job.scene->r.cfra = old_frame;
|
||||
DEG_time_tag_update(job.bmain);
|
||||
|
||||
*progress = 1.0f;
|
||||
*do_update = true;
|
||||
}
|
||||
|
||||
static void bake_simulation_job_endjob(void *customdata)
|
||||
{
|
||||
BakeSimulationJob &job = *static_cast<BakeSimulationJob *>(customdata);
|
||||
WM_set_locked_interface(job.wm, false);
|
||||
G.is_rendering = false;
|
||||
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
|
||||
}
|
||||
|
||||
static int start_bake_job(bContext *C, Vector<ObjectBakeData> objects_to_bake, wmOperator *op)
|
||||
{
|
||||
BakeSimulationJob *job = MEM_new<BakeSimulationJob>(__func__);
|
||||
job->wm = CTX_wm_manager(C);
|
||||
job->bmain = CTX_data_main(C);
|
||||
job->depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
job->scene = CTX_data_scene(C);
|
||||
job->objects = std::move(objects_to_bake);
|
||||
|
||||
wmJob *wm_job = WM_jobs_get(job->wm,
|
||||
CTX_wm_window(C),
|
||||
job->scene,
|
||||
"Bake Nodes",
|
||||
WM_JOB_PROGRESS,
|
||||
WM_JOB_TYPE_BAKE_SIMULATION_NODES);
|
||||
|
||||
WM_jobs_customdata_set(
|
||||
wm_job, job, [](void *job) { MEM_delete(static_cast<BakeSimulationJob *>(job)); });
|
||||
WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
|
||||
WM_jobs_callbacks(
|
||||
wm_job, bake_simulation_job_startjob, nullptr, nullptr, bake_simulation_job_endjob);
|
||||
|
||||
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int bake_simulation_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
Vector<Object *> objects;
|
||||
if (RNA_boolean_get(op->ptr, "selected")) {
|
||||
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
|
||||
objects.append(object);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
else {
|
||||
if (Object *object = CTX_data_active_object(C)) {
|
||||
objects.append(object);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<ObjectBakeData> objects_to_bake;
|
||||
for (Object *object : job.objects) {
|
||||
if (!BKE_id_is_editable(job.bmain, &object->id)) {
|
||||
for (Object *object : objects) {
|
||||
if (!BKE_id_is_editable(bmain, &object->id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -281,162 +444,38 @@ static void bake_simulation_job_startjob(void *customdata,
|
||||
}
|
||||
|
||||
for (const bNestedNodeRef &nested_node_ref : nmd->node_group->nested_node_refs_span()) {
|
||||
ZoneBakeData zone_bake_data;
|
||||
zone_bake_data.zone_id = nested_node_ref.id;
|
||||
zone_bake_data.blob_sharing = std::make_unique<bake::BlobSharing>();
|
||||
if (std::optional<bake::BakePath> path = bake::get_node_bake_path(
|
||||
*job.bmain, *object, *nmd, nested_node_ref.id))
|
||||
{
|
||||
zone_bake_data.path = std::move(*path);
|
||||
modifier_bake_data.zones.append(std::move(zone_bake_data));
|
||||
NodeBakeData node_bake_data;
|
||||
node_bake_data.id = nested_node_ref.id;
|
||||
node_bake_data.blob_sharing = std::make_unique<bake::BlobSharing>();
|
||||
std::optional<bake::BakePath> path = bake::get_node_bake_path(
|
||||
*bmain, *object, *nmd, nested_node_ref.id);
|
||||
if (!path) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
|
||||
*scene, *object, *nmd, nested_node_ref.id);
|
||||
if (!frame_range) {
|
||||
continue;
|
||||
}
|
||||
node_bake_data.path = std::move(*path);
|
||||
node_bake_data.frame_start = frame_range->first();
|
||||
node_bake_data.frame_end = frame_range->last();
|
||||
|
||||
modifier_bake_data.nodes.append(std::move(node_bake_data));
|
||||
}
|
||||
if (modifier_bake_data.nodes.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
bake_data.modifiers.append(std::move(modifier_bake_data));
|
||||
}
|
||||
}
|
||||
if (bake_data.modifiers.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
objects_to_bake.append(std::move(bake_data));
|
||||
}
|
||||
|
||||
*progress = 0.0f;
|
||||
*do_update = true;
|
||||
|
||||
const float frame_step_size = 1.0f;
|
||||
const float progress_per_frame = 1.0f / (float(job.scene->r.efra - job.scene->r.sfra + 1) /
|
||||
frame_step_size);
|
||||
const int old_frame = job.scene->r.cfra;
|
||||
|
||||
for (float frame_f = job.scene->r.sfra; frame_f <= job.scene->r.efra; frame_f += frame_step_size)
|
||||
{
|
||||
const SubFrame frame{frame_f};
|
||||
|
||||
if (G.is_break || (stop != nullptr && *stop)) {
|
||||
break;
|
||||
}
|
||||
|
||||
job.scene->r.cfra = frame.frame();
|
||||
job.scene->r.subframe = frame.subframe();
|
||||
|
||||
BKE_scene_graph_update_for_newframe(job.depsgraph);
|
||||
|
||||
const std::string frame_file_name = bake::frame_to_file_name(frame);
|
||||
|
||||
for (ObjectBakeData &object_bake_data : objects_to_bake) {
|
||||
for (ModifierBakeData &modifier_bake_data : object_bake_data.modifiers) {
|
||||
NodesModifierData &nmd = *modifier_bake_data.nmd;
|
||||
const bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
|
||||
for (ZoneBakeData &zone_bake_data : modifier_bake_data.zones) {
|
||||
if (!modifier_cache.cache_by_id.contains(zone_bake_data.zone_id)) {
|
||||
continue;
|
||||
}
|
||||
const bake::NodeCache &node_cache = *modifier_cache.cache_by_id.lookup(
|
||||
zone_bake_data.zone_id);
|
||||
if (node_cache.frame_caches.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
const bake::FrameCache &frame_cache = *node_cache.frame_caches.last();
|
||||
if (frame_cache.frame != frame) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bake::BakePath path = zone_bake_data.path;
|
||||
|
||||
const std::string blob_file_name = frame_file_name + ".blob";
|
||||
|
||||
char blob_path[FILE_MAX];
|
||||
BLI_path_join(
|
||||
blob_path, sizeof(blob_path), path.blobs_dir.c_str(), blob_file_name.c_str());
|
||||
char meta_path[FILE_MAX];
|
||||
BLI_path_join(meta_path,
|
||||
sizeof(meta_path),
|
||||
path.meta_dir.c_str(),
|
||||
(frame_file_name + ".json").c_str());
|
||||
BLI_file_ensure_parent_dir_exists(meta_path);
|
||||
BLI_file_ensure_parent_dir_exists(blob_path);
|
||||
fstream blob_file{blob_path, std::ios::out | std::ios::binary};
|
||||
bake::DiskBlobWriter blob_writer{blob_file_name, blob_file, 0};
|
||||
fstream meta_file{meta_path, std::ios::out};
|
||||
bake::serialize_bake(
|
||||
frame_cache.state, blob_writer, *zone_bake_data.blob_sharing, meta_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*progress += progress_per_frame;
|
||||
*do_update = true;
|
||||
}
|
||||
|
||||
for (ObjectBakeData &object_bake_data : objects_to_bake) {
|
||||
for (ModifierBakeData &modifier_bake_data : object_bake_data.modifiers) {
|
||||
NodesModifierData &nmd = *modifier_bake_data.nmd;
|
||||
for (ZoneBakeData &zone_bake_data : modifier_bake_data.zones) {
|
||||
if (std::unique_ptr<bake::NodeCache> &node_cache = nmd.runtime->cache->cache_by_id.lookup(
|
||||
zone_bake_data.zone_id))
|
||||
{
|
||||
/* Tag the caches as being baked so that they are not changed anymore. */
|
||||
node_cache->cache_status = bake::CacheStatus::Baked;
|
||||
}
|
||||
}
|
||||
}
|
||||
DEG_id_tag_update(&object_bake_data.object->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
job.scene->r.cfra = old_frame;
|
||||
DEG_time_tag_update(job.bmain);
|
||||
|
||||
*progress = 1.0f;
|
||||
*do_update = true;
|
||||
}
|
||||
|
||||
static void bake_simulation_job_endjob(void *customdata)
|
||||
{
|
||||
BakeSimulationJob &job = *static_cast<BakeSimulationJob *>(customdata);
|
||||
WM_set_locked_interface(job.wm, false);
|
||||
G.is_rendering = false;
|
||||
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, nullptr);
|
||||
}
|
||||
|
||||
static int bake_simulation_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
BakeSimulationJob *job = MEM_new<BakeSimulationJob>(__func__);
|
||||
job->wm = wm;
|
||||
job->bmain = bmain;
|
||||
job->depsgraph = depsgraph;
|
||||
job->scene = scene;
|
||||
|
||||
if (RNA_boolean_get(op->ptr, "selected")) {
|
||||
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
|
||||
job->objects.append(object);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
else {
|
||||
if (Object *object = CTX_data_active_object(C)) {
|
||||
job->objects.append(object);
|
||||
}
|
||||
}
|
||||
|
||||
wmJob *wm_job = WM_jobs_get(wm,
|
||||
CTX_wm_window(C),
|
||||
CTX_data_scene(C),
|
||||
"Bake Simulation Nodes",
|
||||
WM_JOB_PROGRESS,
|
||||
WM_JOB_TYPE_BAKE_SIMULATION_NODES);
|
||||
|
||||
WM_jobs_customdata_set(
|
||||
wm_job, job, [](void *job) { MEM_delete(static_cast<BakeSimulationJob *>(job)); });
|
||||
WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
|
||||
WM_jobs_callbacks(
|
||||
wm_job, bake_simulation_job_startjob, nullptr, nullptr, bake_simulation_job_endjob);
|
||||
|
||||
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
return start_bake_job(C, std::move(objects_to_bake), op);
|
||||
}
|
||||
|
||||
struct PathStringHash {
|
||||
@ -601,10 +640,52 @@ static int bake_simulation_modal(bContext *C, wmOperator * /*op*/, const wmEvent
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
|
||||
static void try_delete_bake(
|
||||
bContext *C, Object &object, NodesModifierData &nmd, const int bake_id, ReportList *reports)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
if (!nmd.runtime->cache) {
|
||||
return;
|
||||
}
|
||||
bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
|
||||
std::lock_guard lock{modifier_cache.mutex};
|
||||
if (!modifier_cache.cache_by_id.contains(bake_id)) {
|
||||
return;
|
||||
}
|
||||
bake::NodeCache &node_cache = *modifier_cache.cache_by_id.lookup(bake_id);
|
||||
node_cache.reset();
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, object, nmd, bake_id);
|
||||
if (!bake_path) {
|
||||
return;
|
||||
}
|
||||
const char *meta_dir = bake_path->meta_dir.c_str();
|
||||
if (BLI_exists(meta_dir)) {
|
||||
if (BLI_delete(meta_dir, true, true)) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Failed to remove meta directory %s", meta_dir);
|
||||
}
|
||||
}
|
||||
const char *blobs_dir = bake_path->blobs_dir.c_str();
|
||||
if (BLI_exists(blobs_dir)) {
|
||||
if (BLI_delete(blobs_dir, true, true)) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Failed to remove blobs directory %s", blobs_dir);
|
||||
}
|
||||
}
|
||||
if (bake_path->bake_dir.has_value()) {
|
||||
const char *zone_bake_dir = bake_path->bake_dir->c_str();
|
||||
/* Try to delete zone bake directory if it is empty. */
|
||||
BLI_delete(zone_bake_dir, true, false);
|
||||
}
|
||||
if (const std::optional<std::string> modifier_bake_dir = bake::get_modifier_bake_path(
|
||||
*bmain, object, nmd))
|
||||
{
|
||||
/* Try to delete modifier bake directory if it is empty. */
|
||||
BLI_delete(modifier_bake_dir->c_str(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Vector<Object *> objects;
|
||||
if (RNA_boolean_get(op->ptr, "selected")) {
|
||||
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
|
||||
@ -626,42 +707,8 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
|
||||
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
|
||||
if (md->type == eModifierType_Nodes) {
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
|
||||
if (!nmd->runtime->cache) {
|
||||
continue;
|
||||
}
|
||||
for (auto item : nmd->runtime->cache->cache_by_id.items()) {
|
||||
item.value->reset();
|
||||
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, *object, *nmd, item.key);
|
||||
if (!bake_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *meta_dir = bake_path->meta_dir.c_str();
|
||||
if (BLI_exists(meta_dir)) {
|
||||
if (BLI_delete(meta_dir, true, true)) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "Failed to remove meta directory %s", meta_dir);
|
||||
}
|
||||
}
|
||||
const char *blobs_dir = bake_path->blobs_dir.c_str();
|
||||
if (BLI_exists(blobs_dir)) {
|
||||
if (BLI_delete(blobs_dir, true, true)) {
|
||||
BKE_reportf(
|
||||
op->reports, RPT_ERROR, "Failed to remove blobs directory %s", blobs_dir);
|
||||
}
|
||||
}
|
||||
if (bake_path->bake_dir.has_value()) {
|
||||
const char *zone_bake_dir = bake_path->bake_dir->c_str();
|
||||
/* Try to delete zone bake directory if it is empty. */
|
||||
BLI_delete(zone_bake_dir, true, false);
|
||||
}
|
||||
}
|
||||
if (const std::optional<std::string> modifier_bake_dir = bake::get_modifier_bake_path(
|
||||
*bmain, *object, *nmd))
|
||||
{
|
||||
/* Try to delete modifier bake directory if it is empty. */
|
||||
BLI_delete(modifier_bake_dir->c_str(), true, false);
|
||||
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
|
||||
try_delete_bake(C, *object, *nmd, bake.id, op->reports);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -674,6 +721,102 @@ static int delete_baked_simulation_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int geometry_nodes_bake_node_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *object = reinterpret_cast<Object *>(
|
||||
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
|
||||
if (object == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
|
||||
if (modifier_name == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(modifier_name); });
|
||||
|
||||
ModifierData *md = BKE_modifiers_findby_name(object, modifier_name);
|
||||
if (md == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
|
||||
|
||||
if (StringRef(nmd.simulation_bake_directory).is_empty()) {
|
||||
const std::string directory = bake::get_default_modifier_bake_directory(*bmain, *object, nmd);
|
||||
nmd.simulation_bake_directory = BLI_strdup(directory.c_str());
|
||||
}
|
||||
|
||||
const int bake_id = RNA_int_get(op->ptr, "bake_id");
|
||||
const std::optional<bake::BakePath> bake_path = bake::get_node_bake_path(
|
||||
*bmain, *object, nmd, bake_id);
|
||||
if (!bake_path.has_value()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const std::optional<IndexRange> frame_range = bake::get_node_bake_frame_range(
|
||||
*scene, *object, nmd, bake_id);
|
||||
if (!frame_range.has_value()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (frame_range->is_empty()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
NodeBakeData node_bake_data;
|
||||
node_bake_data.id = bake_id;
|
||||
node_bake_data.path = std::move(*bake_path);
|
||||
node_bake_data.frame_start = frame_range->first();
|
||||
node_bake_data.frame_end = frame_range->last();
|
||||
node_bake_data.blob_sharing = std::make_unique<bake::BlobSharing>();
|
||||
|
||||
ModifierBakeData modifier_bake_data;
|
||||
modifier_bake_data.nmd = &nmd;
|
||||
modifier_bake_data.nodes.append(std::move(node_bake_data));
|
||||
|
||||
ObjectBakeData object_bake_data;
|
||||
object_bake_data.object = object;
|
||||
object_bake_data.modifiers.append(std::move(modifier_bake_data));
|
||||
|
||||
Vector<ObjectBakeData> objects_to_bake;
|
||||
objects_to_bake.append(std::move(object_bake_data));
|
||||
return start_bake_job(C, std::move(objects_to_bake), op);
|
||||
}
|
||||
|
||||
static int geometry_nodes_bake_node_modal(bContext *C,
|
||||
wmOperator * /*op*/,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
if (!WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_BAKE_SIMULATION_NODES)) {
|
||||
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
static int geometry_nodes_delete_bake_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Object *object = reinterpret_cast<Object *>(
|
||||
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
|
||||
if (object == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
char *modifier_name = RNA_string_get_alloc(op->ptr, "modifier_name", nullptr, 0, nullptr);
|
||||
if (modifier_name == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(modifier_name); });
|
||||
|
||||
ModifierData *md = BKE_modifiers_findby_name(object, modifier_name);
|
||||
if (md == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
|
||||
const int bake_id = RNA_int_get(op->ptr, "bake_id");
|
||||
|
||||
try_delete_bake(C, *object, nmd, bake_id, op->reports);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::object::bake_simulation
|
||||
|
||||
void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot)
|
||||
@ -725,3 +868,55 @@ void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot)
|
||||
|
||||
RNA_def_boolean(ot->srna, "selected", false, "Selected", "Delete cache on all selected objects");
|
||||
}
|
||||
|
||||
void OBJECT_OT_geometry_nodes_bake_node(wmOperatorType *ot)
|
||||
{
|
||||
using namespace blender::ed::object::bake_simulation;
|
||||
|
||||
ot->name = "Bake Single Geometry Node";
|
||||
ot->description = "Bake a single geometry node";
|
||||
ot->idname = "OBJECT_OT_geometry_nodes_bake_node";
|
||||
|
||||
ot->exec = geometry_nodes_bake_node_exec;
|
||||
ot->modal = geometry_nodes_bake_node_modal;
|
||||
|
||||
WM_operator_properties_id_lookup(ot, false);
|
||||
|
||||
RNA_def_string(ot->srna,
|
||||
"modifier_name",
|
||||
nullptr,
|
||||
0,
|
||||
"Modifier Name",
|
||||
"Name of the modifier that contains the node to bake");
|
||||
RNA_def_int(
|
||||
ot->srna, "bake_id", 0, 0, INT32_MAX, "Bake ID", "ID of the node to bake", 0, INT32_MAX);
|
||||
}
|
||||
|
||||
void OBJECT_OT_geometry_nodes_delete_bake(wmOperatorType *ot)
|
||||
{
|
||||
using namespace blender::ed::object::bake_simulation;
|
||||
|
||||
ot->name = "Delete Baked Data";
|
||||
ot->description = "Delete baked data of a single geometry node";
|
||||
ot->idname = "OBJECT_OT_geometry_nodes_delete_bake";
|
||||
|
||||
ot->exec = geometry_nodes_delete_bake_exec;
|
||||
|
||||
WM_operator_properties_id_lookup(ot, false);
|
||||
|
||||
RNA_def_string(ot->srna,
|
||||
"modifier_name",
|
||||
nullptr,
|
||||
0,
|
||||
"Modifier Name",
|
||||
"Name of the modifier that contains the node");
|
||||
RNA_def_int(ot->srna,
|
||||
"bake_id",
|
||||
0,
|
||||
0,
|
||||
INT32_MAX,
|
||||
"Bake ID",
|
||||
"Nested node id of the bake to delete",
|
||||
0,
|
||||
INT32_MAX);
|
||||
}
|
||||
|
@ -347,6 +347,8 @@ void OBJECT_OT_bake(wmOperatorType *ot);
|
||||
void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot);
|
||||
void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot);
|
||||
void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_nodes_bake_node(wmOperatorType *ot);
|
||||
void OBJECT_OT_geometry_nodes_delete_bake(wmOperatorType *ot);
|
||||
|
||||
/* `object_random.cc` */
|
||||
|
||||
|
@ -259,6 +259,8 @@ void ED_operatortypes_object()
|
||||
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_calculate_to_frame);
|
||||
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_bake);
|
||||
WM_operatortype_append(OBJECT_OT_simulation_nodes_cache_delete);
|
||||
WM_operatortype_append(OBJECT_OT_geometry_nodes_bake_node);
|
||||
WM_operatortype_append(OBJECT_OT_geometry_nodes_delete_bake);
|
||||
WM_operatortype_append(OBJECT_OT_drop_named_material);
|
||||
WM_operatortype_append(OBJECT_OT_drop_geometry_nodes);
|
||||
WM_operatortype_append(OBJECT_OT_unlink_data);
|
||||
|
@ -1017,7 +1017,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even
|
||||
|
||||
/* cancel animation playback */
|
||||
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
|
||||
ED_screen_animation_play(C, 0, 0);
|
||||
ED_screen_animation_play(C, 0, 0, true);
|
||||
}
|
||||
|
||||
/* handle UI stuff */
|
||||
|
@ -15,6 +15,7 @@ set(INC
|
||||
../../gpu
|
||||
../../imbuf
|
||||
../../makesrna
|
||||
../../modifiers
|
||||
../../sequencer
|
||||
../../windowmanager
|
||||
# RNA_prototypes.h
|
||||
|
@ -1679,7 +1679,8 @@ ScrArea *ED_screen_temp_space_open(bContext *C,
|
||||
return area;
|
||||
}
|
||||
|
||||
void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable)
|
||||
void ED_screen_animation_timer(
|
||||
bContext *C, int redraws, int sync, int enable, const bool fix_start_frame)
|
||||
{
|
||||
bScreen *screen = CTX_wm_screen(C);
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
@ -1699,24 +1700,26 @@ void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable)
|
||||
screen->animtimer = WM_event_timer_add(wm, win, TIMER0, (1.0 / FPS));
|
||||
|
||||
sad->region = CTX_wm_region(C);
|
||||
/* If start-frame is larger than current frame, we put current-frame on start-frame.
|
||||
* NOTE(ton): first frame then is not drawn! */
|
||||
if (PRVRANGEON) {
|
||||
if (scene->r.psfra > scene->r.cfra) {
|
||||
sad->sfra = scene->r.cfra;
|
||||
scene->r.cfra = scene->r.psfra;
|
||||
if (fix_start_frame) {
|
||||
/* If start-frame is larger than current frame, we put current-frame on start-frame.
|
||||
* NOTE(ton): first frame then is not drawn! */
|
||||
if (PRVRANGEON) {
|
||||
if (scene->r.psfra > scene->r.cfra) {
|
||||
sad->sfra = scene->r.cfra;
|
||||
scene->r.cfra = scene->r.psfra;
|
||||
}
|
||||
else {
|
||||
sad->sfra = scene->r.cfra;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sad->sfra = scene->r.cfra;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (scene->r.sfra > scene->r.cfra) {
|
||||
sad->sfra = scene->r.cfra;
|
||||
scene->r.cfra = scene->r.sfra;
|
||||
}
|
||||
else {
|
||||
sad->sfra = scene->r.cfra;
|
||||
if (scene->r.sfra > scene->r.cfra) {
|
||||
sad->sfra = scene->r.cfra;
|
||||
scene->r.cfra = scene->r.sfra;
|
||||
}
|
||||
else {
|
||||
sad->sfra = scene->r.cfra;
|
||||
}
|
||||
}
|
||||
}
|
||||
sad->redraws = redraws;
|
||||
|
@ -26,21 +26,25 @@
|
||||
#include "DNA_mask_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_callbacks.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_editmesh.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_icons.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mask.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
@ -81,6 +85,8 @@
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
|
||||
#include "MOD_nodes.hh"
|
||||
|
||||
#include "screen_intern.h" /* own module include */
|
||||
|
||||
#define KM_MODAL_CANCEL 1
|
||||
@ -4952,7 +4958,7 @@ bScreen *ED_screen_animation_no_scrub(const wmWindowManager *wm)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ED_screen_animation_play(bContext *C, int sync, int mode)
|
||||
int ED_screen_animation_play(bContext *C, int sync, int mode, const bool fix_start_frame)
|
||||
{
|
||||
bScreen *screen = CTX_wm_screen(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
@ -4962,7 +4968,7 @@ int ED_screen_animation_play(bContext *C, int sync, int mode)
|
||||
|
||||
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
|
||||
/* stop playback now */
|
||||
ED_screen_animation_timer(C, 0, 0, 0);
|
||||
ED_screen_animation_timer(C, 0, 0, 0, false);
|
||||
ED_scene_fps_average_clear(scene);
|
||||
BKE_sound_stop_scene(scene_eval);
|
||||
|
||||
@ -4984,7 +4990,7 @@ int ED_screen_animation_play(bContext *C, int sync, int mode)
|
||||
BKE_sound_play_scene(scene_eval);
|
||||
}
|
||||
|
||||
ED_screen_animation_timer(C, screen->redraws_flag, sync, mode);
|
||||
ED_screen_animation_timer(C, screen->redraws_flag, sync, mode, fix_start_frame);
|
||||
ED_scene_fps_average_clear(scene);
|
||||
|
||||
if (screen->animtimer) {
|
||||
@ -5007,7 +5013,7 @@ static int screen_animation_play_exec(bContext *C, wmOperator *op)
|
||||
sync = RNA_boolean_get(op->ptr, "sync");
|
||||
}
|
||||
|
||||
return ED_screen_animation_play(C, sync, mode);
|
||||
return ED_screen_animation_play(C, sync, mode, true);
|
||||
}
|
||||
|
||||
static void SCREEN_OT_animation_play(wmOperatorType *ot)
|
||||
@ -5033,6 +5039,94 @@ static void SCREEN_OT_animation_play(wmOperatorType *ot)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Animation Playback with Preroll Operator
|
||||
* \{ */
|
||||
|
||||
static int screen_animation_play_with_preroll_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
namespace bake = blender::bke::bake;
|
||||
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
std::optional<int> playback_start_frame;
|
||||
|
||||
FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
|
||||
LISTBASE_FOREACH (const ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type != eModifierType_Nodes) {
|
||||
continue;
|
||||
}
|
||||
const NodesModifierData &nmd = *reinterpret_cast<const NodesModifierData *>(md);
|
||||
if (!nmd.runtime->cache) {
|
||||
continue;
|
||||
}
|
||||
const bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
|
||||
std::lock_guard lock{modifier_cache.mutex};
|
||||
for (const NodesModifierBake &bake : blender::Span(nmd.bakes, nmd.bakes_num)) {
|
||||
const std::unique_ptr<bake::NodeCache> *node_cache_ptr =
|
||||
modifier_cache.cache_by_id.lookup_ptr(bake.id);
|
||||
const bool cache_is_invalid = (node_cache_ptr == nullptr) ||
|
||||
(*node_cache_ptr)->cache_status ==
|
||||
bake::CacheStatus::Invalid ||
|
||||
(*node_cache_ptr)->frame_caches.is_empty();
|
||||
if (!cache_is_invalid) {
|
||||
continue;
|
||||
}
|
||||
const std::optional<blender::IndexRange> frame_range = bake::get_node_bake_frame_range(
|
||||
*scene, *ob, nmd, bake.id);
|
||||
if (!frame_range.has_value()) {
|
||||
continue;
|
||||
}
|
||||
const int sim_start_frame = frame_range->start();
|
||||
if (playback_start_frame) {
|
||||
if (sim_start_frame < *playback_start_frame) {
|
||||
playback_start_frame = sim_start_frame;
|
||||
}
|
||||
}
|
||||
else {
|
||||
playback_start_frame = sim_start_frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_OBJECT_END;
|
||||
|
||||
const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C));
|
||||
|
||||
if (playback_start_frame.has_value()) {
|
||||
if (wmTimer *animtimer = CTX_wm_screen(C)->animtimer) {
|
||||
ScreenAnimData *sad = static_cast<ScreenAnimData *>(animtimer->customdata);
|
||||
sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
|
||||
sad->nextfra = *playback_start_frame;
|
||||
}
|
||||
else {
|
||||
scene->r.cfra = *playback_start_frame;
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_FRAME_CHANGE);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
|
||||
}
|
||||
}
|
||||
if (!is_playing) {
|
||||
return ED_screen_animation_play(C, ANIMPLAY_FLAG_NO_SYNC, 1, false);
|
||||
}
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void SCREEN_OT_animation_play_with_preroll(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Play Animation with Preroll";
|
||||
ot->description = "Play animation starting at the earliest invalidated simulation";
|
||||
ot->idname = "SCREEN_OT_animation_play_with_preroll";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = screen_animation_play_with_preroll_exec;
|
||||
|
||||
ot->poll = ED_operator_screenactive_norender;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Animation Cancel Operator
|
||||
* \{ */
|
||||
@ -5055,7 +5149,7 @@ static int screen_animation_cancel_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
/* call the other "toggling" operator to clean up now */
|
||||
ED_screen_animation_play(C, 0, 0);
|
||||
ED_screen_animation_play(C, 0, 0, false);
|
||||
}
|
||||
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
@ -5891,6 +5985,7 @@ void ED_operatortypes_screen()
|
||||
|
||||
WM_operatortype_append(SCREEN_OT_animation_step);
|
||||
WM_operatortype_append(SCREEN_OT_animation_play);
|
||||
WM_operatortype_append(SCREEN_OT_animation_play_with_preroll);
|
||||
WM_operatortype_append(SCREEN_OT_animation_cancel);
|
||||
|
||||
/* New/delete. */
|
||||
|
@ -748,56 +748,112 @@ static void timeline_cache_draw_single(PTCacheID *pid, float y_offset, float hei
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
|
||||
static void timeline_cache_draw_simulation_nodes(const blender::bke::bake::ModifierCache &cache,
|
||||
const float y_offset,
|
||||
const float height,
|
||||
const uint pos_id)
|
||||
struct SimulationRange {
|
||||
blender::IndexRange frames;
|
||||
blender::bke::bake::CacheStatus status;
|
||||
};
|
||||
|
||||
static void timeline_cache_draw_simulation_nodes(
|
||||
const blender::Span<SimulationRange> simulation_ranges,
|
||||
const bool all_simulations_baked,
|
||||
float *y_offset,
|
||||
const float line_height,
|
||||
const uint pos_id)
|
||||
{
|
||||
std::lock_guard lock{cache.mutex};
|
||||
if (cache.cache_by_id.is_empty()) {
|
||||
if (simulation_ranges.is_empty()) {
|
||||
return;
|
||||
}
|
||||
/* Draw the state if one of the simulation zones. This is fine for now, because there is no ui
|
||||
* that allows caching zones independently. */
|
||||
const blender::bke::bake::NodeCache &node_cache = **cache.cache_by_id.values().begin();
|
||||
if (node_cache.frame_caches.is_empty()) {
|
||||
return;
|
||||
|
||||
bool has_bake = false;
|
||||
|
||||
for (const SimulationRange &sim_range : simulation_ranges) {
|
||||
switch (sim_range.status) {
|
||||
case blender::bke::bake::CacheStatus::Invalid:
|
||||
case blender::bke::bake::CacheStatus::Valid:
|
||||
break;
|
||||
case blender::bke::bake::CacheStatus::Baked:
|
||||
has_bake = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
blender::Set<int> status_change_frames_set;
|
||||
for (const SimulationRange &sim_range : simulation_ranges) {
|
||||
status_change_frames_set.add(sim_range.frames.first());
|
||||
status_change_frames_set.add(sim_range.frames.one_after_last());
|
||||
}
|
||||
blender::Vector<int> status_change_frames;
|
||||
status_change_frames.extend(status_change_frames_set.begin(), status_change_frames_set.end());
|
||||
std::sort(status_change_frames.begin(), status_change_frames.end());
|
||||
const blender::OffsetIndices<int> frame_ranges = status_change_frames.as_span();
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_translate_2f(0.0, float(V2D_SCROLL_HANDLE_HEIGHT) + y_offset);
|
||||
GPU_matrix_scale_2f(1.0, height);
|
||||
GPU_matrix_translate_2f(0.0, float(V2D_SCROLL_HANDLE_HEIGHT) + *y_offset);
|
||||
GPU_matrix_scale_2f(1.0, line_height);
|
||||
|
||||
float color[4];
|
||||
UI_GetThemeColor4fv(TH_SIMULATED_FRAMES, color);
|
||||
switch (node_cache.cache_status) {
|
||||
case blender::bke::bake::CacheStatus::Invalid: {
|
||||
color[3] = 0.4f;
|
||||
break;
|
||||
blender::float4 base_color;
|
||||
UI_GetThemeColor4fv(TH_SIMULATED_FRAMES, base_color);
|
||||
blender::float4 invalid_color = base_color;
|
||||
invalid_color.w *= 0.4f;
|
||||
blender::float4 valid_color = base_color;
|
||||
valid_color.w *= 0.7f;
|
||||
blender::float4 baked_color = base_color;
|
||||
|
||||
float max_used_height = 1.0f;
|
||||
for (const int range_i : frame_ranges.index_range()) {
|
||||
const blender::IndexRange frame_range = frame_ranges[range_i];
|
||||
const int start_frame = frame_range.first();
|
||||
const int end_frame = frame_range.last();
|
||||
|
||||
bool has_bake_at_frame = false;
|
||||
bool has_valid_at_frame = false;
|
||||
bool has_invalid_at_frame = false;
|
||||
for (const SimulationRange &sim_range : simulation_ranges) {
|
||||
if (sim_range.frames.contains(start_frame)) {
|
||||
switch (sim_range.status) {
|
||||
case blender::bke::bake::CacheStatus::Invalid:
|
||||
has_invalid_at_frame = true;
|
||||
break;
|
||||
case blender::bke::bake::CacheStatus::Valid:
|
||||
has_valid_at_frame = true;
|
||||
break;
|
||||
case blender::bke::bake::CacheStatus::Baked:
|
||||
has_bake_at_frame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
case blender::bke::bake::CacheStatus::Valid: {
|
||||
color[3] = 0.7f;
|
||||
break;
|
||||
if (!(has_bake_at_frame || has_valid_at_frame || has_invalid_at_frame)) {
|
||||
continue;
|
||||
}
|
||||
case blender::bke::bake::CacheStatus::Baked: {
|
||||
color[3] = 1.0f;
|
||||
break;
|
||||
|
||||
if (all_simulations_baked) {
|
||||
immUniformColor4fv(baked_color);
|
||||
immBeginAtMost(GPU_PRIM_TRIS, 6);
|
||||
immRectf_fast(pos_id, start_frame, 0, end_frame + 1.0f, 1.0f);
|
||||
immEnd();
|
||||
}
|
||||
else {
|
||||
if (has_valid_at_frame || has_invalid_at_frame) {
|
||||
immUniformColor4fv(has_invalid_at_frame ? invalid_color : valid_color);
|
||||
immBeginAtMost(GPU_PRIM_TRIS, 6);
|
||||
const float top = has_bake ? 2.0f : 1.0f;
|
||||
immRectf_fast(pos_id, start_frame, 0.0f, end_frame + 1.0f, top);
|
||||
immEnd();
|
||||
max_used_height = top;
|
||||
}
|
||||
if (has_bake_at_frame) {
|
||||
immUniformColor4fv(baked_color);
|
||||
immBeginAtMost(GPU_PRIM_TRIS, 6);
|
||||
immRectf_fast(pos_id, start_frame, 0, end_frame + 1.0f, 1.0f);
|
||||
immEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
immUniformColor4fv(color);
|
||||
|
||||
immBeginAtMost(GPU_PRIM_TRIS, node_cache.frame_caches.size() * 6);
|
||||
|
||||
for (const std::unique_ptr<blender::bke::bake::FrameCache> &frame_cache :
|
||||
node_cache.frame_caches.as_span())
|
||||
{
|
||||
const int frame = frame_cache->frame.frame();
|
||||
immRectf_fast(pos_id, frame - 0.5f, 0, frame + 0.5f, 1.0f);
|
||||
}
|
||||
immEnd();
|
||||
|
||||
GPU_matrix_pop();
|
||||
|
||||
*y_offset += max_used_height * 2;
|
||||
}
|
||||
|
||||
void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Scene *scene)
|
||||
@ -832,6 +888,8 @@ void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Sce
|
||||
y_offset += cache_draw_height;
|
||||
}
|
||||
if (saction->cache_display & TIME_CACHE_SIMULATION_NODES) {
|
||||
blender::Vector<SimulationRange> simulation_ranges;
|
||||
bool all_simulations_baked = true;
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type != eModifierType_Nodes) {
|
||||
continue;
|
||||
@ -846,10 +904,29 @@ void timeline_draw_cache(const SpaceAction *saction, const Object *ob, const Sce
|
||||
if ((nmd->node_group->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_SIMULATION_ZONE) == 0) {
|
||||
continue;
|
||||
}
|
||||
timeline_cache_draw_simulation_nodes(
|
||||
*nmd->runtime->cache, y_offset, cache_draw_height, pos_id);
|
||||
y_offset += cache_draw_height;
|
||||
const blender::bke::bake::ModifierCache &modifier_cache = *nmd->runtime->cache;
|
||||
{
|
||||
std::lock_guard lock{modifier_cache.mutex};
|
||||
for (const std::unique_ptr<blender::bke::bake::NodeCache> &node_cache_ptr :
|
||||
modifier_cache.cache_by_id.values())
|
||||
{
|
||||
const blender::bke::bake::NodeCache &node_cache = *node_cache_ptr;
|
||||
if (node_cache.frame_caches.is_empty()) {
|
||||
all_simulations_baked = false;
|
||||
continue;
|
||||
}
|
||||
if (node_cache.cache_status != blender::bke::bake::CacheStatus::Baked) {
|
||||
all_simulations_baked = false;
|
||||
}
|
||||
const int start_frame = node_cache.frame_caches.first()->frame.frame();
|
||||
const int end_frame = node_cache.frame_caches.last()->frame.frame();
|
||||
const blender::IndexRange frame_range{start_frame, end_frame - start_frame + 1};
|
||||
simulation_ranges.append({frame_range, node_cache.cache_status});
|
||||
}
|
||||
}
|
||||
}
|
||||
timeline_cache_draw_simulation_nodes(
|
||||
simulation_ranges, all_simulations_baked, &y_offset, cache_draw_height, pos_id);
|
||||
}
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
@ -241,6 +241,52 @@ float2 space_node_group_offset(const SpaceNode &snode)
|
||||
return float2(0);
|
||||
}
|
||||
|
||||
static const bNode *group_node_by_name(const bNodeTree &ntree, StringRef name)
|
||||
{
|
||||
for (const bNode *node : ntree.group_nodes()) {
|
||||
if (node->name == name) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &query_node)
|
||||
{
|
||||
BLI_assert(snode.edittree->runtime->nodes_by_id.contains(const_cast<bNode *>(&query_node)));
|
||||
|
||||
std::optional<int32_t> id_in_node;
|
||||
const char *group_node_name = nullptr;
|
||||
const bNode *node = &query_node;
|
||||
LISTBASE_FOREACH_BACKWARD (const bNodeTreePath *, path, &snode.treepath) {
|
||||
const bNodeTree *ntree = path->nodetree;
|
||||
if (group_node_name) {
|
||||
node = group_node_by_name(*ntree, group_node_name);
|
||||
}
|
||||
bool found = false;
|
||||
for (const bNestedNodeRef &ref : ntree->nested_node_refs_span()) {
|
||||
if (node->is_group()) {
|
||||
if (ref.path.node_id == node->identifier && ref.path.id_in_node == id_in_node) {
|
||||
group_node_name = path->node_name;
|
||||
id_in_node = ref.id;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (ref.path.node_id == node->identifier) {
|
||||
group_node_name = path->node_name;
|
||||
id_in_node = ref.id;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return id_in_node;
|
||||
}
|
||||
|
||||
/* ******************** default callbacks for node space ***************** */
|
||||
|
||||
static SpaceLink *node_create(const ScrArea * /*area*/, const Scene * /*scene*/)
|
||||
|
@ -38,6 +38,7 @@ set(SRC
|
||||
space_outliner.cc
|
||||
tree/common.cc
|
||||
tree/tree_display.cc
|
||||
tree/tree_display_bakes.cc
|
||||
tree/tree_display_data.cc
|
||||
tree/tree_display_libraries.cc
|
||||
tree/tree_display_orphaned.cc
|
||||
|
@ -3993,7 +3993,8 @@ void draw_outliner(const bContext *C)
|
||||
SO_LIBRARIES,
|
||||
SO_OVERRIDES_LIBRARY,
|
||||
SO_DATA_API,
|
||||
SO_ID_ORPHANS) &&
|
||||
SO_ID_ORPHANS,
|
||||
SO_BAKES) &&
|
||||
space_outliner->flag & SO_SYNC_SELECT)
|
||||
{
|
||||
outliner_sync_selection(C, space_outliner);
|
||||
|
@ -360,7 +360,8 @@ void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_out
|
||||
SO_LIBRARIES,
|
||||
SO_OVERRIDES_LIBRARY,
|
||||
SO_DATA_API,
|
||||
SO_ID_ORPHANS))
|
||||
SO_ID_ORPHANS,
|
||||
SO_BAKES))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -313,6 +313,7 @@ float outliner_right_columns_width(const SpaceOutliner *space_outliner)
|
||||
case SO_DATA_API:
|
||||
case SO_SEQUENCE:
|
||||
case SO_LIBRARIES:
|
||||
case SO_BAKES:
|
||||
return 0.0f;
|
||||
case SO_OVERRIDES_LIBRARY:
|
||||
switch ((eSpaceOutliner_LibOverrideViewMode)space_outliner->lib_override_view_mode) {
|
||||
|
@ -41,6 +41,8 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::create_from_display_mo
|
||||
break;
|
||||
case SO_VIEW_LAYER:
|
||||
return std::make_unique<TreeDisplayViewLayer>(space_outliner);
|
||||
case SO_BAKES:
|
||||
return std::make_unique<TreeDisplayBakes>(space_outliner);
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
|
@ -287,4 +287,14 @@ class TreeDisplayDataAPI final : public AbstractTreeDisplay {
|
||||
bool is_lazy_built() const override;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Data API Tree-Display */
|
||||
|
||||
class TreeDisplayBakes final : public AbstractTreeDisplay {
|
||||
public:
|
||||
TreeDisplayBakes(SpaceOutliner &space_outliner);
|
||||
|
||||
ListBase build_tree(const TreeSourceData &source_data) override;
|
||||
};
|
||||
|
||||
} // namespace blender::ed::outliner
|
||||
|
@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "../outliner_intern.hh"
|
||||
#include "common.hh"
|
||||
#include "tree_display.hh"
|
||||
#include "tree_element.hh"
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
TreeDisplayBakes::TreeDisplayBakes(SpaceOutliner &space_outliner)
|
||||
: AbstractTreeDisplay(space_outliner)
|
||||
{
|
||||
}
|
||||
|
||||
ListBase TreeDisplayBakes::build_tree(const TreeSourceData & /*source_data*/)
|
||||
{
|
||||
ListBase tree{};
|
||||
this->add_element(&tree, nullptr, (void *)"Hello World", nullptr, TSE_GENERIC_LABEL, 0);
|
||||
return tree;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::outliner
|
@ -2325,6 +2325,21 @@ typedef struct NodesModifierSettings {
|
||||
struct IDProperty *properties;
|
||||
} NodesModifierSettings;
|
||||
|
||||
typedef struct NodesModifierBake {
|
||||
/** An id that references a nested node in the node tree. Also see #bNestedNodeRef. */
|
||||
int id;
|
||||
/** #NodesModifierBakeFlag. */
|
||||
uint32_t flag;
|
||||
char *directory;
|
||||
int frame_start;
|
||||
int frame_end;
|
||||
} NodesModifierBake;
|
||||
|
||||
typedef enum NodesModifierBakeFlag {
|
||||
NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE = 1 << 0,
|
||||
NODES_MODIFIER_BAKE_CUSTOM_PATH = 1 << 1,
|
||||
} NodesModifierBakeFlag;
|
||||
|
||||
typedef struct NodesModifierData {
|
||||
ModifierData modifier;
|
||||
struct bNodeTree *node_group;
|
||||
@ -2333,13 +2348,20 @@ typedef struct NodesModifierData {
|
||||
* Directory where baked simulation states are stored. This may be relative to the .blend file.
|
||||
*/
|
||||
char *simulation_bake_directory;
|
||||
|
||||
/** NodesModifierFlag. */
|
||||
int8_t flag;
|
||||
|
||||
char _pad[7];
|
||||
char _pad[3];
|
||||
int bakes_num;
|
||||
NodesModifierBake *bakes;
|
||||
void *_pad2;
|
||||
|
||||
NodesModifierRuntimeHandle *runtime;
|
||||
|
||||
#ifdef __cplusplus
|
||||
NodesModifierBake *find_bake(int id);
|
||||
const NodesModifierBake *find_bake(int id) const;
|
||||
#endif
|
||||
} NodesModifierData;
|
||||
|
||||
typedef enum NodesModifierFlag {
|
||||
|
@ -716,6 +716,7 @@ typedef struct bNodeTree {
|
||||
const bNestedNodeRef *nested_node_ref_from_node_id_path(blender::Span<int> node_ids) const;
|
||||
[[nodiscard]] bool node_id_path_from_nested_node_ref(const int32_t nested_node_id,
|
||||
blender::Vector<int32_t> &r_node_ids) const;
|
||||
const bNode *find_nested_node(int32_t nested_node_id) const;
|
||||
|
||||
/**
|
||||
* Update a run-time cache for the node tree based on it's current state. This makes many methods
|
||||
|
@ -266,6 +266,8 @@
|
||||
.eevee = _DNA_DEFAULT_SceneEEVEE, \
|
||||
\
|
||||
.hydra = _DNA_DEFAULT_SceneHydra, \
|
||||
.simulation_frame_start = 1, \
|
||||
.simulation_frame_end = 250, \
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -2052,6 +2052,9 @@ typedef struct Scene {
|
||||
/** Settings to be override by work-spaces. */
|
||||
IDProperty *layer_properties;
|
||||
|
||||
int simulation_frame_start;
|
||||
int simulation_frame_end;
|
||||
|
||||
struct SceneDisplay display;
|
||||
struct SceneEEVEE eevee;
|
||||
struct SceneGpencil grease_pencil_settings;
|
||||
@ -2491,6 +2494,7 @@ enum {
|
||||
SCE_FRAME_DROP = 1 << 3,
|
||||
SCE_KEYS_NO_SELONLY = 1 << 4,
|
||||
SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK = 1 << 5,
|
||||
SCE_CUSTOM_SIMULATION_RANGE = 1 << 6,
|
||||
};
|
||||
|
||||
/* Return flag BKE_scene_base_iter_next functions. */
|
||||
|
@ -410,6 +410,7 @@ typedef enum eSpaceOutliner_Mode {
|
||||
SO_ID_ORPHANS = 14,
|
||||
SO_VIEW_LAYER = 15,
|
||||
SO_OVERRIDES_LIBRARY = 16,
|
||||
SO_BAKES = 17,
|
||||
} eSpaceOutliner_Mode;
|
||||
|
||||
/** #SpaceOutliner.outlinevis */
|
||||
|
@ -7050,11 +7050,57 @@ static void rna_def_modifier_weightednormal(BlenderRNA *brna)
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "NodesModifierBake", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "Nodes Modifier Bake", "");
|
||||
|
||||
prop = RNA_def_property(srna, "directory", PROP_STRING, PROP_DIRPATH);
|
||||
RNA_def_property_ui_text(prop, "Directory", "Location on disk where the bake data is stored");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME);
|
||||
RNA_def_property_ui_text(prop, "Start Frame", "Frame where the baking starts");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_TIME);
|
||||
RNA_def_property_ui_text(prop, "End Frame", "Frame where the baking ends");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_custom_simulation_frame_range", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(
|
||||
prop, nullptr, "flag", NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Custom Simulation Frame Range", "Override the simulation frame range from the scene");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_custom_path", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODES_MODIFIER_BAKE_CUSTOM_PATH);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Auto Path", "Specify a path where the baked data should be stored manually");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
}
|
||||
|
||||
static void rna_def_modifier_nodes_bakes(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, "NodesModifierBakes", nullptr);
|
||||
RNA_def_struct_sdna(srna, "NodesModifierData");
|
||||
RNA_def_struct_ui_text(srna, "Bakes", "Bake data for every bake node");
|
||||
}
|
||||
|
||||
static void rna_def_modifier_nodes(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
rna_def_modifier_nodes_bake(brna);
|
||||
rna_def_modifier_nodes_bakes(brna);
|
||||
|
||||
srna = RNA_def_struct(brna, "NodesModifier", "Modifier");
|
||||
RNA_def_struct_ui_text(srna, "Nodes Modifier", "");
|
||||
RNA_def_struct_sdna(srna, "NodesModifierData");
|
||||
@ -7075,6 +7121,11 @@ static void rna_def_modifier_nodes(BlenderRNA *brna)
|
||||
prop, "Simulation Bake Directory", "Location on disk where the bake data is stored");
|
||||
RNA_def_property_update(prop, 0, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "bakes", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "NodesModifierBake");
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "bakes", "bakes_num");
|
||||
RNA_def_property_srna(prop, "NodesModifierBakes");
|
||||
|
||||
prop = RNA_def_property(srna, "show_group_selector", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_negative_sdna(
|
||||
prop, nullptr, "flag", NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR);
|
||||
|
@ -8373,6 +8373,26 @@ void RNA_def_scene(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, NC_SCENE, nullptr);
|
||||
# endif
|
||||
|
||||
prop = RNA_def_property(srna, "use_custom_simulation_range", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SCE_CUSTOM_SIMULATION_RANGE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Custom Simulation Range",
|
||||
"Use a simulation range that is different from the scene range for "
|
||||
"simulation nodes that don't override the frame range themselves");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_SCENE, "rna_Scene_set_update");
|
||||
|
||||
prop = RNA_def_property(srna, "simulation_frame_start", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Simulation Frame Start", "Frame at which simulations start running");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_SCENE, "rna_Scene_set_update");
|
||||
|
||||
prop = RNA_def_property(srna, "simulation_frame_end", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Simulation Frame End", "Frame at which simulations end running");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_SCENE, "rna_Scene_set_update");
|
||||
|
||||
prop = RNA_def_property(srna, "sync_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_funcs(prop, "rna_Scene_sync_mode_get", "rna_Scene_sync_mode_set", nullptr);
|
||||
RNA_def_property_enum_items(prop, sync_mode_items);
|
||||
|
@ -3710,6 +3710,7 @@ static void rna_def_space_outliner(BlenderRNA *brna)
|
||||
ICON_ORPHAN_DATA,
|
||||
"Orphan Data",
|
||||
"Display data-blocks which are unused and/or will be lost when the file is reloaded"},
|
||||
{SO_BAKES, "BAKES", ICON_PHYSICS, "Bakes", "Display bake-able nodes"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
|
@ -383,11 +383,8 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
||||
static void update_id_properties_from_node_group(NodesModifierData *nmd)
|
||||
{
|
||||
using namespace blender;
|
||||
if (nmd->node_group == nullptr) {
|
||||
if (nmd->settings.properties) {
|
||||
IDP_FreeProperty(nmd->settings.properties);
|
||||
@ -411,10 +408,113 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
||||
if (old_properties != nullptr) {
|
||||
IDP_FreeProperty(old_properties);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_existing_bake_caches(NodesModifierData &nmd)
|
||||
{
|
||||
if (!nmd.runtime->cache) {
|
||||
if (nmd.bakes_num == 0) {
|
||||
return;
|
||||
}
|
||||
nmd.runtime->cache = std::make_shared<bake::ModifierCache>();
|
||||
}
|
||||
bake::ModifierCache &modifier_cache = *nmd.runtime->cache;
|
||||
std::lock_guard lock{modifier_cache.mutex};
|
||||
|
||||
Map<int, std::unique_ptr<bake::NodeCache>> &old_cache_by_id = modifier_cache.cache_by_id;
|
||||
Map<int, std::unique_ptr<bake::NodeCache>> new_cache_by_id;
|
||||
for (const NodesModifierBake &bake : Span{nmd.bakes, nmd.bakes_num}) {
|
||||
std::unique_ptr<bake::NodeCache> node_cache;
|
||||
std::unique_ptr<bake::NodeCache> *old_node_cache_ptr = old_cache_by_id.lookup_ptr(bake.id);
|
||||
if (old_node_cache_ptr == nullptr) {
|
||||
node_cache = std::make_unique<bake::NodeCache>();
|
||||
}
|
||||
else {
|
||||
node_cache = std::move(*old_node_cache_ptr);
|
||||
}
|
||||
new_cache_by_id.add(bake.id, std::move(node_cache));
|
||||
}
|
||||
modifier_cache.cache_by_id = std::move(new_cache_by_id);
|
||||
}
|
||||
|
||||
static void update_bakes_from_node_group(NodesModifierData &nmd)
|
||||
{
|
||||
Map<int, const NodesModifierBake *> old_bake_by_id;
|
||||
for (const NodesModifierBake &bake : Span(nmd.bakes, nmd.bakes_num)) {
|
||||
old_bake_by_id.add(bake.id, &bake);
|
||||
}
|
||||
|
||||
Vector<int> new_bake_ids;
|
||||
for (const bNestedNodeRef &ref : nmd.node_group->nested_node_refs_span()) {
|
||||
const bNode *node = nmd.node_group->find_nested_node(ref.id);
|
||||
if (node) {
|
||||
if (node->type == GEO_NODE_SIMULATION_OUTPUT) {
|
||||
new_bake_ids.append(ref.id);
|
||||
}
|
||||
}
|
||||
else if (old_bake_by_id.contains(ref.id)) {
|
||||
/* Keep baked data in case linked data is missing so that it still exists when the linked
|
||||
* data has been found. */
|
||||
new_bake_ids.append(ref.id);
|
||||
}
|
||||
}
|
||||
|
||||
NodesModifierBake *new_bake_data = static_cast<NodesModifierBake *>(
|
||||
MEM_callocN(sizeof(NodesModifierBake) * new_bake_ids.size(), __func__));
|
||||
for (const int i : new_bake_ids.index_range()) {
|
||||
const int id = new_bake_ids[i];
|
||||
const NodesModifierBake *old_bake = old_bake_by_id.lookup_default(id, nullptr);
|
||||
NodesModifierBake &new_bake = new_bake_data[i];
|
||||
if (old_bake) {
|
||||
new_bake = *old_bake;
|
||||
if (new_bake.directory) {
|
||||
new_bake.directory = BLI_strdup(new_bake.directory);
|
||||
}
|
||||
}
|
||||
else {
|
||||
new_bake.id = id;
|
||||
new_bake.frame_start = 1;
|
||||
new_bake.frame_end = 100;
|
||||
}
|
||||
}
|
||||
|
||||
for (NodesModifierBake &old_bake : MutableSpan(nmd.bakes, nmd.bakes_num)) {
|
||||
MEM_SAFE_FREE(old_bake.directory);
|
||||
}
|
||||
MEM_SAFE_FREE(nmd.bakes);
|
||||
|
||||
nmd.bakes = new_bake_data;
|
||||
nmd.bakes_num = new_bake_ids.size();
|
||||
|
||||
update_existing_bake_caches(nmd);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
|
||||
{
|
||||
using namespace blender;
|
||||
update_id_properties_from_node_group(nmd);
|
||||
update_bakes_from_node_group(*nmd);
|
||||
|
||||
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
NodesModifierBake *NodesModifierData::find_bake(const int id)
|
||||
{
|
||||
return const_cast<NodesModifierBake *>(std::as_const(*this).find_bake(id));
|
||||
}
|
||||
|
||||
const NodesModifierBake *NodesModifierData::find_bake(const int id) const
|
||||
{
|
||||
for (const NodesModifierBake &bake : blender::Span{this->bakes, this->bakes_num}) {
|
||||
if (bake.id == id) {
|
||||
return &bake;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void find_side_effect_nodes_for_viewer_path(
|
||||
@ -678,13 +778,13 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
const NodesModifierData &nmd_;
|
||||
const ModifierEvalContext &ctx_;
|
||||
const Main *bmain_;
|
||||
const Scene *scene_;
|
||||
SubFrame current_frame_;
|
||||
SubFrame start_frame_;
|
||||
bool is_start_frame_;
|
||||
bool use_frame_cache_;
|
||||
bool depsgraph_is_active_;
|
||||
bake::ModifierCache *modifier_cache_;
|
||||
float fps_;
|
||||
bool has_invalid_simulation_ = false;
|
||||
|
||||
public:
|
||||
NodesModifierSimulationParams(NodesModifierData &nmd, const ModifierEvalContext &ctx)
|
||||
@ -694,8 +794,7 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
bmain_ = DEG_get_bmain(depsgraph);
|
||||
current_frame_ = DEG_get_ctime(depsgraph);
|
||||
const Scene *scene = DEG_get_input_scene(depsgraph);
|
||||
start_frame_ = scene->r.sfra;
|
||||
is_start_frame_ = current_frame_ == start_frame_;
|
||||
scene_ = scene;
|
||||
use_frame_cache_ = ctx_.object->flag & OB_FLAG_USE_SIMULATION_CACHE;
|
||||
depsgraph_is_active_ = DEG_is_active(depsgraph);
|
||||
modifier_cache_ = nmd.runtime->cache.get();
|
||||
@ -715,14 +814,34 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Reset cached data if necessary. */
|
||||
if (is_start_frame_) {
|
||||
for (std::unique_ptr<bake::NodeCache> &node_cache : modifier_cache_->cache_by_id.values())
|
||||
{
|
||||
if (node_cache->cache_status == bake::CacheStatus::Invalid) {
|
||||
node_cache->reset();
|
||||
}
|
||||
for (auto item : modifier_cache_->cache_by_id.items()) {
|
||||
const int id = item.key;
|
||||
bake::NodeCache &node_cache = *item.value;
|
||||
if (node_cache.cache_status != bake::CacheStatus::Invalid) {
|
||||
continue;
|
||||
}
|
||||
const std::optional<IndexRange> sim_frame_range = bake::get_node_bake_frame_range(
|
||||
*scene_, *ctx_.object, nmd_, id);
|
||||
if (!sim_frame_range.has_value()) {
|
||||
continue;
|
||||
}
|
||||
const SubFrame start_frame{int(sim_frame_range->start())};
|
||||
if (current_frame_ <= start_frame) {
|
||||
node_cache.reset();
|
||||
}
|
||||
if (!node_cache.frame_caches.is_empty() &&
|
||||
current_frame_ < node_cache.frame_caches.first()->frame) {
|
||||
node_cache.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const std::unique_ptr<bake::NodeCache> &node_cache_ptr :
|
||||
modifier_cache_->cache_by_id.values())
|
||||
{
|
||||
const bake::NodeCache &node_cache = *node_cache_ptr;
|
||||
if (node_cache.cache_status == bake::CacheStatus::Invalid) {
|
||||
has_invalid_simulation_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -751,8 +870,15 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
|
||||
void init_simulation_info(const int zone_id, nodes::SimulationZoneBehavior &zone_behavior) const
|
||||
{
|
||||
bake::NodeCache &node_cache = *modifier_cache_->cache_by_id.lookup_or_add_cb(
|
||||
zone_id, []() { return std::make_unique<bake::NodeCache>(); });
|
||||
if (!modifier_cache_->cache_by_id.contains(zone_id)) {
|
||||
/* Should have been created in #update_existing_bake_caches. */
|
||||
return;
|
||||
}
|
||||
bake::NodeCache &node_cache = *modifier_cache_->cache_by_id.lookup(zone_id);
|
||||
const IndexRange sim_frame_range = *bake::get_node_bake_frame_range(
|
||||
*scene_, *ctx_.object, nmd_, zone_id);
|
||||
const SubFrame sim_start_frame{int(sim_frame_range.first())};
|
||||
const SubFrame sim_end_frame{int(sim_frame_range.last())};
|
||||
|
||||
/* Try load baked data. */
|
||||
if (!node_cache.failed_finding_bake) {
|
||||
@ -793,25 +919,31 @@ class NodesModifierSimulationParams : public nodes::GeoNodesSimulationParams {
|
||||
* is read-only. */
|
||||
if (depsgraph_is_active_) {
|
||||
if (node_cache.frame_caches.is_empty()) {
|
||||
if (current_frame_ < sim_start_frame || current_frame_ > sim_end_frame) {
|
||||
/* Outside of simulation frame range, so ignore the simulation if there is no cache. */
|
||||
this->input_pass_through(zone_behavior);
|
||||
this->output_pass_through(zone_behavior);
|
||||
return;
|
||||
}
|
||||
/* Initialize the simulation. */
|
||||
this->input_pass_through(zone_behavior);
|
||||
this->output_store_frame_cache(node_cache, zone_behavior);
|
||||
if (!is_start_frame_) {
|
||||
/* If we initialize at a frame that is not the start frame, the simulation is not
|
||||
* valid. */
|
||||
if (current_frame_ > sim_start_frame || has_invalid_simulation_) {
|
||||
node_cache.cache_status = bake::CacheStatus::Invalid;
|
||||
}
|
||||
this->input_pass_through(zone_behavior);
|
||||
this->output_store_frame_cache(node_cache, zone_behavior);
|
||||
return;
|
||||
}
|
||||
if (frame_indices.prev && !frame_indices.current && !frame_indices.next) {
|
||||
if (frame_indices.prev && !frame_indices.current && !frame_indices.next &&
|
||||
current_frame_ <= sim_end_frame)
|
||||
{
|
||||
/* Read the previous frame's data and store the newly computed simulation state. */
|
||||
auto &output_copy_info = zone_behavior.input.emplace<sim_input::OutputCopy>();
|
||||
const bake::FrameCache &prev_frame_cache = *node_cache.frame_caches[*frame_indices.prev];
|
||||
const float delta_frames = std::min(
|
||||
max_delta_frames, float(current_frame_) - float(prev_frame_cache.frame));
|
||||
if (delta_frames != 1) {
|
||||
const float real_delta_frames = float(current_frame_) - float(prev_frame_cache.frame);
|
||||
if (real_delta_frames != 1) {
|
||||
node_cache.cache_status = bake::CacheStatus::Invalid;
|
||||
}
|
||||
const float delta_frames = std::min(max_delta_frames, real_delta_frames);
|
||||
output_copy_info.delta_time = delta_frames / fps_;
|
||||
output_copy_info.state = prev_frame_cache.state;
|
||||
this->output_store_frame_cache(node_cache, zone_behavior);
|
||||
@ -1680,6 +1812,11 @@ static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const Modi
|
||||
* and don't necessarily need to be written, but we can't just free them. */
|
||||
IDP_BlendWrite(writer, nmd->settings.properties);
|
||||
|
||||
BLO_write_struct_array(writer, NodesModifierBake, nmd->bakes_num, nmd->bakes);
|
||||
for (const NodesModifierBake &bake : Span(nmd->bakes, nmd->bakes_num)) {
|
||||
BLO_write_string(writer, bake.directory);
|
||||
}
|
||||
|
||||
if (!BLO_write_is_undo(writer)) {
|
||||
LISTBASE_FOREACH (IDProperty *, prop, &nmd->settings.properties->data.group) {
|
||||
if (prop->type == IDP_INT) {
|
||||
@ -1706,6 +1843,12 @@ static void blend_read(BlendDataReader *reader, ModifierData *md)
|
||||
BLO_read_data_address(reader, &nmd->settings.properties);
|
||||
IDP_BlendDataRead(reader, &nmd->settings.properties);
|
||||
}
|
||||
|
||||
BLO_read_data_address(reader, &nmd->bakes);
|
||||
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
|
||||
BLO_read_data_address(reader, &bake.directory);
|
||||
}
|
||||
|
||||
nmd->runtime = MEM_new<NodesModifierRuntime>(__func__);
|
||||
nmd->runtime->cache = std::make_shared<bake::ModifierCache>();
|
||||
}
|
||||
@ -1717,6 +1860,16 @@ static void copy_data(const ModifierData *md, ModifierData *target, const int fl
|
||||
|
||||
BKE_modifier_copydata_generic(md, target, flag);
|
||||
|
||||
if (nmd->bakes) {
|
||||
tnmd->bakes = static_cast<NodesModifierBake *>(MEM_dupallocN(nmd->bakes));
|
||||
for (const int i : IndexRange(nmd->bakes_num)) {
|
||||
NodesModifierBake &bake = tnmd->bakes[i];
|
||||
if (bake.directory) {
|
||||
bake.directory = BLI_strdup(bake.directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tnmd->runtime = MEM_new<NodesModifierRuntime>(__func__);
|
||||
|
||||
if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
|
||||
@ -1746,6 +1899,11 @@ static void free_data(ModifierData *md)
|
||||
nmd->settings.properties = nullptr;
|
||||
}
|
||||
|
||||
for (NodesModifierBake &bake : MutableSpan(nmd->bakes, nmd->bakes_num)) {
|
||||
MEM_SAFE_FREE(bake.directory);
|
||||
}
|
||||
MEM_SAFE_FREE(nmd->bakes);
|
||||
|
||||
MEM_SAFE_FREE(nmd->simulation_bake_directory);
|
||||
MEM_delete(nmd->runtime);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ set(INC
|
||||
../../gpu
|
||||
../../imbuf
|
||||
../../makesrna
|
||||
../../modifiers
|
||||
../../render
|
||||
../../windowmanager
|
||||
../../../../extern/fmtlib/include
|
||||
|
@ -8,10 +8,14 @@
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_bake_geometry_nodes_modifier.hh"
|
||||
#include "BKE_bake_items_socket.hh"
|
||||
#include "BKE_compute_contexts.hh"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_instances.hh"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
@ -26,10 +30,21 @@
|
||||
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "ED_node.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "MOD_nodes.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes {
|
||||
@ -835,6 +850,150 @@ static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const b
|
||||
dst_node->storage = dst_storage;
|
||||
}
|
||||
|
||||
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
const bNode *node = static_cast<bNode *>(ptr->data);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
if (snode == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (snode->id == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (GS(snode->id->name) != ID_OB) {
|
||||
return;
|
||||
}
|
||||
Object *object = reinterpret_cast<Object *>(snode->id);
|
||||
/* TODO: Better handle pinned node tree. */
|
||||
ModifierData *md = BKE_object_active_modifier(object);
|
||||
if (md == nullptr || md->type != eModifierType_Nodes) {
|
||||
return;
|
||||
}
|
||||
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
|
||||
if (nmd.node_group != snode->nodetree) {
|
||||
return;
|
||||
}
|
||||
const std::optional<int32_t> bake_id = ed::space_node::find_nested_node_id_in_root(*snode,
|
||||
*node);
|
||||
if (!bake_id.has_value()) {
|
||||
return;
|
||||
}
|
||||
const NodesModifierBake *bake = nullptr;
|
||||
for (const NodesModifierBake &iter_bake : Span{nmd.bakes, nmd.bakes_num}) {
|
||||
if (iter_bake.id == *bake_id) {
|
||||
bake = &iter_bake;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bake == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA bake_rna = RNA_pointer_create(&object->id, &RNA_NodesModifierBake, (void *)bake);
|
||||
|
||||
const std::optional<IndexRange> simulation_range = bke::bake::get_node_bake_frame_range(
|
||||
*scene, *object, nmd, *bake_id);
|
||||
|
||||
std::optional<IndexRange> baked_range;
|
||||
if (nmd.runtime->cache) {
|
||||
const bke::bake::ModifierCache &cache = *nmd.runtime->cache;
|
||||
std::lock_guard lock{cache.mutex};
|
||||
if (const std::unique_ptr<bke::bake::NodeCache> *node_cache_ptr = cache.cache_by_id.lookup_ptr(
|
||||
*bake_id))
|
||||
{
|
||||
const bke::bake::NodeCache &node_cache = **node_cache_ptr;
|
||||
if (node_cache.cache_status == bke::bake::CacheStatus::Baked &&
|
||||
!node_cache.frame_caches.is_empty())
|
||||
{
|
||||
const int first_frame = node_cache.frame_caches.first()->frame.frame();
|
||||
const int last_frame = node_cache.frame_caches.last()->frame.frame();
|
||||
baked_range = IndexRange(first_frame, last_frame - first_frame + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool is_baked = baked_range.has_value();
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayout *row = uiLayoutRow(col, true);
|
||||
{
|
||||
char bake_label[1024] = N_("Bake");
|
||||
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(row,
|
||||
"OBJECT_OT_geometry_nodes_bake_node",
|
||||
bake_label,
|
||||
ICON_NONE,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
WM_operator_properties_id_lookup_set_from_id(&ptr, &object->id);
|
||||
RNA_string_set(&ptr, "modifier_name", nmd.modifier.name);
|
||||
RNA_int_set(&ptr, "bake_id", bake->id);
|
||||
}
|
||||
{
|
||||
PointerRNA ptr;
|
||||
uiItemFullO(row,
|
||||
"OBJECT_OT_geometry_nodes_delete_bake",
|
||||
"",
|
||||
ICON_TRASH,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
WM_operator_properties_id_lookup_set_from_id(&ptr, &object->id);
|
||||
RNA_string_set(&ptr, "modifier_name", nmd.modifier.name);
|
||||
RNA_int_set(&ptr, "bake_id", bake->id);
|
||||
}
|
||||
if (is_baked) {
|
||||
char baked_range_label[64];
|
||||
SNPRINTF(baked_range_label,
|
||||
N_("Baked %d - %d"),
|
||||
int(baked_range->first()),
|
||||
int(baked_range->last()));
|
||||
uiItemL(layout, baked_range_label, ICON_NONE);
|
||||
}
|
||||
else if (simulation_range.has_value()) {
|
||||
char simulation_range_label[64];
|
||||
SNPRINTF(simulation_range_label,
|
||||
N_("Frames %d - %d"),
|
||||
int(simulation_range->first()),
|
||||
int(simulation_range->last()));
|
||||
uiItemL(layout, simulation_range_label, ICON_NONE);
|
||||
}
|
||||
}
|
||||
{
|
||||
uiLayout *settings_col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetActive(settings_col, !is_baked);
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(settings_col, true);
|
||||
uiLayoutSetActive(col, !is_baked);
|
||||
uiItemR(col, &bake_rna, "use_custom_path", UI_ITEM_NONE, "Custom Path", ICON_NONE);
|
||||
uiLayout *subcol = uiLayoutColumn(col, true);
|
||||
uiLayoutSetActive(subcol, bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH);
|
||||
uiItemR(subcol, &bake_rna, "directory", UI_ITEM_NONE, "Path", ICON_NONE);
|
||||
}
|
||||
{
|
||||
uiLayout *col = uiLayoutColumn(settings_col, true);
|
||||
uiItemR(col,
|
||||
&bake_rna,
|
||||
"use_custom_simulation_frame_range",
|
||||
UI_ITEM_NONE,
|
||||
"Custom Range",
|
||||
ICON_NONE);
|
||||
uiLayout *subcol = uiLayoutColumn(col, true);
|
||||
uiLayoutSetActive(subcol, bake->flag & NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE);
|
||||
uiItemR(subcol, &bake_rna, "frame_start", UI_ITEM_NONE, "Start", ICON_NONE);
|
||||
uiItemR(subcol, &bake_rna, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
|
||||
{
|
||||
NodeGeometrySimulationOutput &storage = node_storage(*node);
|
||||
@ -880,6 +1039,7 @@ static void node_register()
|
||||
ntype.declare_dynamic = node_declare_dynamic;
|
||||
ntype.gather_link_search_ops = nullptr;
|
||||
ntype.insert_link = node_insert_link;
|
||||
ntype.draw_buttons_ex = node_layout_ex;
|
||||
node_type_storage(&ntype, "NodeGeometrySimulationOutput", node_free_storage, node_copy_storage);
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
@ -3944,7 +3944,7 @@ void wm_event_do_handlers(bContext *C)
|
||||
CTX_wm_screen_set(C, screen);
|
||||
CTX_data_scene_set(C, scene);
|
||||
|
||||
ED_screen_animation_play(C, -1, 1);
|
||||
ED_screen_animation_play(C, -1, 1, true);
|
||||
|
||||
CTX_data_scene_set(C, scene_ctx);
|
||||
CTX_wm_screen_set(C, screen_stx);
|
||||
|
Loading…
Reference in New Issue
Block a user