WIP: Ghosting System #109552

Draft
Falk David wants to merge 6 commits from filedescriptor/blender:ghosting-system into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
27 changed files with 503 additions and 29 deletions

View File

@ -393,6 +393,7 @@ class OUTLINER_PT_filter(Panel):
row.prop(space, "show_restrict_column_hide", text="")
row.prop(space, "show_restrict_column_viewport", text="")
row.prop(space, "show_restrict_column_render", text="")
row.prop(space, "show_restrict_column_ghosts", text="")
row.prop(space, "show_restrict_column_holdout", text="")
row.prop(space, "show_restrict_column_indirect_only", text="")
layout.separator()
@ -403,6 +404,7 @@ class OUTLINER_PT_filter(Panel):
row.prop(space, "show_restrict_column_hide", text="")
row.prop(space, "show_restrict_column_viewport", text="")
row.prop(space, "show_restrict_column_render", text="")
row.prop(space, "show_restrict_column_ghosts", text="")
layout.separator()
if display_mode != 'DATA_API':

View File

@ -920,6 +920,14 @@ class VIEW3D_HT_header(Header):
sub.active = overlay.show_overlays
sub.popover(panel="VIEW3D_PT_overlay", text="")
# Ghosting toggle & popover
ghosts = view.ghosts
row = layout.row(align=True)
row.prop(ghosts, "show_ghosts", text="")
sub = row.row(align=True)
sub.active = ghosts.show_ghosts
sub.popover(panel="VIEW3D_PT_ghosts", text="")
row = layout.row()
row.active = (object_mode == 'EDIT') or (shading.type in {'WIREFRAME', 'SOLID'})
@ -7197,6 +7205,30 @@ class VIEW3D_PT_overlay_weight_paint(Panel):
col.prop(overlay, "show_paint_wire")
class VIEW3D_PT_ghosts(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Ghosts"
bl_ui_units_x = 13
def draw(self, context):
layout = self.layout
layout.label(text="Ghosts")
view = context.space_data
ghosts = view.ghosts
display_all = ghosts.show_ghosts
col = layout.column(align=True)
col.active = display_all
split = col.split()
sub = split.row(align=True)
sub.prop(ghosts, "color_before", text="")
sub.prop(ghosts, "color_after", text="")
class VIEW3D_PT_snapping(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
@ -8558,6 +8590,7 @@ classes = (
VIEW3D_PT_overlay_bones,
VIEW3D_PT_overlay_sculpt,
VIEW3D_PT_overlay_sculpt_curves,
VIEW3D_PT_ghosts,
VIEW3D_PT_snapping,
VIEW3D_PT_proportional_edit,
VIEW3D_PT_gpencil_origin,

View File

@ -0,0 +1,54 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
* \brief Ghosting system.
*/
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_utility_mixins.hh"
struct Main;
struct Depsgraph;
struct Scene;
struct ViewLayer;
struct Object;
namespace blender::bke::ghosts {
struct GhostFrame {
Depsgraph *depsgraph;
Set<Object *> objects;
};
class GhostingSystem : NonCopyable, NonMovable {
private:
Map<int, GhostFrame> ghost_frames_;
/* Main, scene, and view layer this ghosting system is built for. */
Main *bmain_;
Scene *scene_;
ViewLayer *view_layer_;
public:
GhostingSystem();
~GhostingSystem();
bool is_empty();
void request_ghost(Main *bmain, Scene *scene, ViewLayer *view_layer, Object *object, int frame);
void evaluate_all_frames();
void evaluate_frames(Span<int> frames);
void evaluate_on_framechange();
void foreach_ghost_frame(FunctionRef<void(int, GhostFrame&)> function);
};
} // namespace blender::bke::ghosts

View File

@ -143,6 +143,7 @@ set(SRC
intern/geometry_fields.cc
intern/geometry_set.cc
intern/geometry_set_instances.cc
intern/ghosting_system.cc
intern/gpencil_curve_legacy.cc
intern/gpencil_geom_legacy.cc
intern/gpencil_legacy.cc
@ -391,6 +392,7 @@ set(SRC
BKE_geometry_fields.hh
BKE_geometry_set.hh
BKE_geometry_set_instances.hh
BKE_ghosting_system.hh
BKE_global.h
BKE_gpencil_curve_legacy.h
BKE_gpencil_geom_legacy.h

View File

@ -0,0 +1,114 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "BKE_ghosting_system.hh"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DNA_object_types.h"
namespace blender::bke::ghosts {
GhostingSystem::GhostingSystem() {}
GhostingSystem::~GhostingSystem()
{
for (auto [key, ghost_frame] : this->ghost_frames_.items()) {
DEG_graph_free(ghost_frame.depsgraph);
}
}
bool GhostingSystem::is_empty()
{
return ghost_frames_.is_empty();
}
void GhostingSystem::request_ghost(
Main *bmain, Scene *scene, ViewLayer *view_layer, Object *object, const int frame)
{
if (!ghost_frames_.contains(frame)) {
GhostFrame new_ghost_frame;
new_ghost_frame.depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT);
new_ghost_frame.objects.add_new(object);
ID *id = &object->id;
DEG_graph_build_from_ids(new_ghost_frame.depsgraph, &id, 1);
ghost_frames_.add_new(frame, new_ghost_frame);
return;
}
GhostFrame &ghost_frame = ghost_frames_.lookup(frame);
if (ghost_frame.objects.contains(object)) {
/* Object already in the depsgraph. */
return;
}
/* Ghost frame exists, but doesn't contain the object. Recreate the depsgraph with the new
* object. */
DEG_graph_free(ghost_frame.depsgraph);
ghost_frame.depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT);
ghost_frame.objects.add_new(object);
/* Collect all the IDs. */
Array<ID *> ids(ghost_frame.objects.size());
int i = 0;
for (Object *ob : ghost_frame.objects) {
ids[i] = &ob->id;
i++;
}
/* Build depsgraph. */
DEG_graph_build_from_ids(ghost_frame.depsgraph, ids.data(), ids.size());
}
void GhostingSystem::evaluate_all_frames()
{
if (ghost_frames_.is_empty()) {
/* No ghost frames are built. */
return;
}
for (auto [key, ghost_frame] : ghost_frames_.items()) {
DEG_evaluate_on_refresh(ghost_frame.depsgraph, false);
}
}
void GhostingSystem::evaluate_frames(Span<int> frames)
{
if (ghost_frames_.is_empty()) {
/* No ghost frames are built. */
return;
}
for (const int frame : frames) {
if (!ghost_frames_.contains(frame)) {
continue;
}
GhostFrame &ghost_frame = ghost_frames_.lookup(frame);
DEG_evaluate_on_refresh(ghost_frame.depsgraph, false);
}
}
void GhostingSystem::evaluate_on_framechange()
{
if (ghost_frames_.is_empty()) {
/* No ghost frames are built. */
return;
}
for (auto [key, ghost_frame] : this->ghost_frames_.items()) {
DEG_evaluate_on_framechange(ghost_frame.depsgraph, float(key));
}
}
void GhostingSystem::foreach_ghost_frame(FunctionRef<void(int, GhostFrame &)> function)
{
if (ghost_frames_.is_empty()) {
/* No ghost frames are built. */
return;
}
for (auto [key, ghost_frame] : ghost_frames_.items()) {
function(key, ghost_frame);
}
}
} // namespace blender::bke::ghosts

View File

@ -66,6 +66,7 @@
#include "BKE_effect.h"
#include "BKE_fcurve.h"
#include "BKE_freestyle.h"
#include "BKE_ghosting_system.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
@ -247,6 +248,8 @@ static void scene_init_data(ID *id)
scene->master_collection = BKE_collection_master_add(scene);
BKE_view_layer_add(scene, DATA_("ViewLayer"), nullptr, VIEWLAYER_ADD_NEW);
scene->ghosting_system = MEM_new<blender::bke::ghosts::GhostingSystem>(__func__);
}
static void scene_copy_markers(Scene *scene_dst, const Scene *scene_src, const int flag)
@ -367,6 +370,8 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
}
BKE_scene_copy_data_eevee(scene_dst, scene_src);
scene_dst->ghosting_system = MEM_new<blender::bke::ghosts::GhostingSystem>(__func__);
}
static void scene_free_markers(Scene *scene, bool do_id_user)
@ -462,6 +467,9 @@ static void scene_free_data(ID *id)
/* These are freed on `do_versions`. */
BLI_assert(scene->layer_properties == nullptr);
MEM_delete(scene->ghosting_system);
scene->ghosting_system = nullptr;
}
static void scene_foreach_rigidbodyworldSceneLooper(RigidBodyWorld * /*rbw*/,
@ -1498,6 +1506,8 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
BLO_read_data_address(reader, &sce->layer_properties);
IDP_BlendDataRead(reader, &sce->layer_properties);
sce->ghosting_system = MEM_new<blender::bke::ghosts::GhostingSystem>(__func__);
}
/* patch for missing scene IDs, can't be in do-versions */
@ -2709,7 +2719,13 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
prepare_mesh_for_viewport_render(bmain, scene, view_layer);
/* Update all objects: drivers, matrices, etc. flags set
* by depsgraph or manual, no layer check here, gets correct flushed. */
DEG_evaluate_on_refresh(depsgraph);
DEG_evaluate_on_refresh(depsgraph, true);
if (pass == 0) {
/* Evaluate ghosting depsgraphs here (if needed). */
scene->ghosting_system->evaluate_all_frames();
}
/* Update sound system. */
BKE_scene_update_sound(depsgraph, bmain);
/* Notify python about depsgraph update. */
@ -2791,9 +2807,12 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle
if (pass == 0) {
const float frame = BKE_scene_frame_get(scene);
DEG_evaluate_on_framechange(depsgraph, frame);
/* Evaluate ghosting depsgraphs. */
scene->ghosting_system->evaluate_on_framechange();
}
else {
DEG_evaluate_on_refresh(depsgraph);
DEG_evaluate_on_refresh(depsgraph, true);
}
/* Update sound system animation. */
BKE_scene_update_sound(depsgraph, bmain);

View File

@ -710,5 +710,30 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
scene->eevee.gi_irradiance_pool_size = 16;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "View3DGhosts", "float", "color_before[3]")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
copy_v3_fl3(v3d->ghosts.color_before, 1.0f, 0.0f, 0.0f);
}
}
}
}
}
if (!DNA_struct_elem_find(fd->filesdna, "View3DGhosts", "float", "color_after[3]")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
copy_v3_fl3(v3d->ghosts.color_after, 0.0f, 0.0f, 1.0f);
}
}
}
}
}
}
}

View File

@ -186,7 +186,7 @@ void DEG_evaluate_on_framechange(Depsgraph *graph, float frame);
* Data changed recalculation entry point.
* Evaluate all nodes tagged for updating.
*/
void DEG_evaluate_on_refresh(Depsgraph *graph);
void DEG_evaluate_on_refresh(Depsgraph *graph, bool update_time);
/** \} */

View File

@ -45,25 +45,27 @@ static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph)
deg::deg_evaluate_on_refresh(deg_graph);
}
void DEG_evaluate_on_refresh(Depsgraph *graph)
void DEG_evaluate_on_refresh(Depsgraph *graph, const bool update_time)
{
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
const Scene *scene = DEG_get_input_scene(graph);
const float frame = BKE_scene_frame_get(scene);
const float ctime = BKE_scene_ctime_get(scene);
if (update_time) {
const Scene *scene = DEG_get_input_scene(graph);
const float frame = BKE_scene_frame_get(scene);
const float ctime = BKE_scene_ctime_get(scene);
if (deg_graph->frame != frame || ctime != deg_graph->ctime) {
deg_graph->tag_time_source();
deg_graph->frame = frame;
deg_graph->ctime = ctime;
}
else if (scene->id.recalc & ID_RECALC_FRAME_CHANGE) {
/* Comparing depsgraph & scene frame fails in the case of undo,
* since the undo state is stored before updates from the frame change have been applied.
* In this case reading back the undo state will behave as if no updates on frame change
* is needed as the #Depsgraph.ctime & frame will match the values in the input scene.
* Use #ID_RECALC_FRAME_CHANGE to detect that recalculation is necessary. see: #66913. */
deg_graph->tag_time_source();
if (deg_graph->frame != frame || ctime != deg_graph->ctime) {
deg_graph->tag_time_source();
deg_graph->frame = frame;
deg_graph->ctime = ctime;
}
else if (scene->id.recalc & ID_RECALC_FRAME_CHANGE) {
/* Comparing depsgraph & scene frame fails in the case of undo,
* since the undo state is stored before updates from the frame change have been applied.
* In this case reading back the undo state will behave as if no updates on frame change
* is needed as the #Depsgraph.ctime & frame will match the values in the input scene.
* Use #ID_RECALC_FRAME_CHANGE to detect that recalculation is necessary. see: #66913. */
deg_graph->tag_time_source();
}
}
deg_flush_updates_and_refresh(deg_graph);

View File

@ -189,7 +189,7 @@ Scene *DEG_get_evaluated_scene(const Depsgraph *graph)
Scene *scene_cow = deg_graph->scene_cow;
/* TODO(sergey): Shall we expand data-block here? Or is it OK to assume
* that caller is OK with just a pointer in case scene is not updated yet? */
BLI_assert(scene_cow != nullptr && deg::deg_copy_on_write_is_expanded(&scene_cow->id));
// BLI_assert(scene_cow != nullptr && deg::deg_copy_on_write_is_expanded(&scene_cow->id));
return scene_cow;
}

View File

@ -382,7 +382,8 @@ void workbench_cache_populate(void *ved, Object *ob)
WORKBENCH_StorageList *stl = vedata->stl;
WORKBENCH_PrivateData *wpd = stl->wpd;
if (!DRW_object_is_renderable(ob)) {
/* Ghost frames are currently rendered using the workbench engine. */
if ((ob->base_flag & BASE_IS_GHOST_FRAME) == 0 && !DRW_object_is_renderable(ob)) {
return;
}

View File

@ -24,6 +24,7 @@
#include "BKE_curves.h"
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
#include "BKE_ghosting_system.hh"
#include "BKE_global.h"
#include "BKE_gpencil_legacy.h"
#include "BKE_grease_pencil.h"
@ -45,6 +46,7 @@
#include "DNA_camera_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
#include "DNA_view3d_types.h"
#include "DNA_world_types.h"
@ -195,6 +197,10 @@ bool DRW_object_is_renderable(const Object *ob)
}
}
if ((ob->base_flag & BASE_IS_GHOST_FRAME) != 0) {
return false;
}
return true;
}
@ -1655,6 +1661,8 @@ void DRW_draw_render_loop_ex(Depsgraph *depsgraph,
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
Scene *scene_orig = reinterpret_cast<Scene *>(DEG_get_original_id(&scene->id));
BKE_view_layer_synced_ensure(scene, view_layer);
DST.draw_ctx = {};
DST.draw_ctx.region = region;
@ -1680,9 +1688,10 @@ void DRW_draw_render_loop_ex(Depsgraph *depsgraph,
const bool internal_engine = (engine_type->flag & RE_INTERNAL) != 0;
const bool draw_type_render = v3d->shading.type == OB_RENDER;
const bool overlays_on = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0;
const bool ghosts_on = (v3d->flag2 & V3D_HIDE_GHOSTS) == 0;
const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d);
const bool do_populate_loop = internal_engine || overlays_on || !draw_type_render ||
gpencil_engine_needed;
gpencil_engine_needed || ghosts_on;
/* Get list of enabled engines */
drw_engines_enable(view_layer, engine_type, gpencil_engine_needed);
@ -1732,6 +1741,37 @@ void DRW_draw_render_loop_ex(Depsgraph *depsgraph,
drw_engines_cache_populate(ob);
}
DEG_OBJECT_ITER_END;
if (!scene_orig->ghosting_system->is_empty() && !draw_type_render && ghosts_on) {
scene_orig->ghosting_system->foreach_ghost_frame(
[&](int frame, blender::bke::ghosts::GhostFrame &ghost_frame) {
if (!ghost_frame.depsgraph) {
return;
}
DEGObjectIterSettings deg_ghost_iter_settings = {0};
deg_ghost_iter_settings.depsgraph = ghost_frame.depsgraph;
deg_ghost_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
DEG_OBJECT_ITER_BEGIN (&deg_ghost_iter_settings, ob) {
Object *ob_orig = DEG_get_original_object(ob);
if (!BKE_object_is_visible_in_viewport(v3d, ob_orig)) {
continue;
}
ob->base_flag |= BASE_IS_GHOST_FRAME;
ob->base_flag &= ~BASE_SELECTED;
const bool before = frame < scene_orig->r.cfra;
if (before) {
copy_v3_v3(ob->color, v3d->ghosts.color_before);
}
else {
copy_v3_v3(ob->color, v3d->ghosts.color_after);
}
// ob->color[3] = (before) ? 0.1f + 0.2f * i : 0.7f - (i - 4) * 0.2f;
drw_engines_cache_populate(ob);
}
DEG_OBJECT_ITER_END;
});
}
}
drw_duplidata_free();

View File

@ -1186,7 +1186,7 @@ bool ED_object_modifier_apply(Main *bmain,
ID *ids[] = {&ob->id};
DEG_graph_build_from_ids(local_depsgraph, ids, 1);
DEG_evaluate_on_refresh(local_depsgraph);
DEG_evaluate_on_refresh(local_depsgraph, true);
apply_depsgraph = local_depsgraph;

View File

@ -827,7 +827,7 @@ static Scene *object_preview_scene_create(const ObjectPreviewData *preview_data,
preview_base->flag |= BASE_SELECTED;
DEG_graph_build_from_view_layer(depsgraph);
DEG_evaluate_on_refresh(depsgraph);
DEG_evaluate_on_refresh(depsgraph, true);
ED_view3d_camera_to_view_selected_with_set_clipping(
preview_data->pr_main, depsgraph, scene, camera_object);
@ -958,7 +958,7 @@ static PoseBackup *action_preview_render_prepare(IconPreview *preview)
/* Force evaluation of the new pose, before the preview is rendered. */
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
DEG_evaluate_on_refresh(preview->depsgraph);
DEG_evaluate_on_refresh(preview->depsgraph, true);
return pose_backup;
}

View File

@ -16,6 +16,7 @@
#include "DNA_sequence_types.h"
#include "BKE_context.h"
#include "BKE_ghosting_system.hh"
#include "BKE_global.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
@ -422,6 +423,43 @@ static void SCENE_OT_delete(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static bool scene_enable_ghosting_poll(bContext *C)
{
Scene *scene = CTX_data_scene(C);
Object *object = CTX_data_active_object(C);
return scene != nullptr && object != nullptr;
}
static int scene_enable_ghosting_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *active_object = CTX_data_active_object(C);
for (int i = 0; i < 8; i++) {
scene->ghosting_system->request_ghost(bmain, scene, view_layer, active_object, scene->r.cfra + 4 - i);
}
return OPERATOR_FINISHED;
}
static void SCENE_OT_enable_ghosting(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Create Ghost Frames for Active Object";
ot->description =
"Clears and re-creates the ghost frames for the active object and all of its dependencies";
ot->idname = "SCENE_OT_enable_ghosting";
/* api callbacks */
ot->exec = scene_enable_ghosting_exec;
ot->poll = scene_enable_ghosting_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -433,6 +471,8 @@ void ED_operatortypes_scene()
WM_operatortype_append(SCENE_OT_new);
WM_operatortype_append(SCENE_OT_delete);
WM_operatortype_append(SCENE_OT_new_sequencer);
WM_operatortype_append(SCENE_OT_enable_ghosting);
}
/** \} */

View File

@ -237,7 +237,7 @@ static void compo_initjob(void *cjv)
/* NOTE: Don't update animation to preserve unkeyed changes, this means can not use
* evaluate_on_framechange. */
DEG_evaluate_on_refresh(cj->compositor_depsgraph);
DEG_evaluate_on_refresh(cj->compositor_depsgraph, true);
bNodeTree *ntree_eval = (bNodeTree *)DEG_get_evaluated_id(cj->compositor_depsgraph,
&cj->ntree->id);

View File

@ -906,7 +906,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
struct RestrictProperties {
bool initialized;
PropertyRNA *object_hide_viewport, *object_hide_select, *object_hide_render;
PropertyRNA *object_hide_viewport, *object_hide_select, *object_hide_render, *object_show_ghosts;
PropertyRNA *base_hide_viewport;
PropertyRNA *collection_hide_viewport, *collection_hide_select, *collection_hide_render;
PropertyRNA *layer_collection_exclude, *layer_collection_holdout,
@ -922,6 +922,7 @@ struct RestrictPropertiesActive {
bool object_hide_viewport;
bool object_hide_select;
bool object_hide_render;
bool object_show_ghosts;
bool base_hide_viewport;
bool collection_hide_viewport;
bool collection_hide_select;
@ -1066,6 +1067,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
props.object_hide_viewport = RNA_struct_type_find_property(&RNA_Object, "hide_viewport");
props.object_hide_select = RNA_struct_type_find_property(&RNA_Object, "hide_select");
props.object_hide_render = RNA_struct_type_find_property(&RNA_Object, "hide_render");
props.object_show_ghosts = RNA_struct_type_find_property(&RNA_Object, "show_ghosts");
props.base_hide_viewport = RNA_struct_type_find_property(&RNA_ObjectBase, "hide_viewport");
props.collection_hide_viewport = RNA_struct_type_find_property(&RNA_Collection,
"hide_viewport");
@ -1095,6 +1097,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
int hide;
int viewport;
int render;
int ghosts;
int indirect_only;
int holdout;
} restrict_offsets = {0};
@ -1118,6 +1121,9 @@ static void outliner_draw_restrictbuts(uiBlock *block,
if (space_outliner->show_restrict_flags & SO_RESTRICT_HIDE) {
restrict_offsets.hide = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
}
if (space_outliner->show_restrict_flags & SO_RESTRICT_SHOW_GHOSTS) {
restrict_offsets.ghosts = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
}
if (space_outliner->show_restrict_flags & SO_RESTRICT_SELECT) {
restrict_offsets.select = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
}
@ -1281,6 +1287,32 @@ static void outliner_draw_restrictbuts(uiBlock *block,
UI_but_flag_enable(bt, UI_BUT_INACTIVE);
}
}
if (space_outliner->show_restrict_flags & SO_RESTRICT_SHOW_GHOSTS &&
OB_TYPE_SUPPORT_GHOSTING(ob->type))
{
bt = uiDefIconButR_prop(block,
UI_BTYPE_ICON_TOGGLE,
0,
ICON_NONE,
int(region->v2d.cur.xmax - restrict_offsets.ghosts),
te->ys,
UI_UNIT_X,
UI_UNIT_Y,
&ptr,
props.object_show_ghosts,
-1,
0,
0,
-1,
-1,
TIP_("Show ghosts in viewport"));
UI_but_func_set(bt, outliner__object_set_flag_recursive_fn, ob, (char *)"show_ghosts");
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (!props_active.object_show_ghosts) {
UI_but_flag_enable(bt, UI_BUT_INACTIVE);
}
}
}
else if (tselem->type == TSE_CONSTRAINT) {
bConstraint *con = (bConstraint *)te->directdata;

View File

@ -337,6 +337,9 @@ float outliner_right_columns_width(const SpaceOutliner *space_outliner)
if (space_outliner->show_restrict_flags & SO_RESTRICT_INDIRECT_ONLY) {
num_columns++;
}
if (space_outliner->show_restrict_flags & SO_RESTRICT_SHOW_GHOSTS) {
num_columns++;
}
ATTR_FALLTHROUGH;
case SO_SCENES:
if (space_outliner->show_restrict_flags & SO_RESTRICT_SELECT) {

View File

@ -1471,6 +1471,7 @@ static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeP
&RNA_View3DCursor,
&RNA_View3DOverlay,
&RNA_View3DShading,
&RNA_View3DGhosts,
&RNA_World,
};
@ -1979,8 +1980,10 @@ static void space_view3d_refresh(const bContext *C, ScrArea *area)
view3d_lightcache_update((bContext *)C);
}
View3D *v3d = (View3D *)area->spacedata.first;
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
MEM_SAFE_FREE(v3d->runtime.local_stats);
// view3d_ghosts_update();
}
const char *view3d_context_dir[] = {

View File

@ -247,6 +247,8 @@ enum {
BASE_HOLDOUT = (1 << 10),
/* Object only contributes indirectly to render */
BASE_INDIRECT_ONLY = (1 << 11),
/* Object is used for rendering ghost frames. */
BASE_IS_GHOST_FRAME = (1 << 12),
};
/* LayerCollection->flag */

View File

@ -657,6 +657,8 @@ typedef enum ObjectType {
case ID_VO: \
case ID_GP
#define OB_TYPE_SUPPORT_GHOSTING(_type) (ELEM(_type, OB_MESH, OB_GREASE_PENCIL))
/** #Object.partype: first 4 bits: type. */
enum {
PARTYPE = (1 << 4) - 1,
@ -801,7 +803,8 @@ enum {
OB_HIDE_VOLUME_SCATTER = 1 << 7,
OB_HIDE_SHADOW = 1 << 8,
OB_HOLDOUT = 1 << 9,
OB_SHADOW_CATCHER = 1 << 10
OB_SHADOW_CATCHER = 1 << 10,
OB_HIDE_GHOSTS = 1 << 11
};
/** #Object.shapeflag */

View File

@ -47,6 +47,7 @@ struct Scene;
struct World;
struct bGPdata;
struct bNodeTree;
struct Depsgraph;
/* -------------------------------------------------------------------- */
/** \name FFMPEG
@ -1935,6 +1936,15 @@ enum {
/** \} */
#ifdef __cplusplus
namespace blender::bke::ghosts {
class GhostingSystem;
} // namespace blender::bke::ghosts
using GhostingSystemHandle = blender::bke::ghosts::GhostingSystem;
#else
typedef struct GhostingSystemHandle GhostingSystemHandle;
#endif
/* -------------------------------------------------------------------- */
/** \name Scene ID-Block
* \{ */
@ -2057,6 +2067,8 @@ typedef struct Scene {
struct SceneEEVEE eevee;
struct SceneGpencil grease_pencil_settings;
struct SceneHydra hydra;
GhostingSystemHandle *ghosting_system;
void *_pad9;
} Scene;
/** \} */

View File

@ -393,6 +393,7 @@ typedef enum eSpaceOutliner_ShowRestrictFlag {
SO_RESTRICT_RENDER = (1 << 4),
SO_RESTRICT_HOLDOUT = (1 << 5),
SO_RESTRICT_INDIRECT_ONLY = (1 << 6),
SO_RESTRICT_SHOW_GHOSTS = (1 << 7),
} eSpaceOutliner_Restrict;
/** #SpaceOutliner.outlinevis */

View File

@ -66,6 +66,12 @@
.normals_constant_screen_size = 7.0f, \
}
#define _DNA_DEFAULT_View3DGhosts \
{ \
.color_before = {1.0f, 0.0f, 0.0f}, \
.color_after = {0.0f, 0.0f, 1.0f}, \
} \
#define _DNA_DEFAULT_View3DCursor \
{ \
.rotation_mode = ROT_MODE_XYZ, \
@ -82,6 +88,7 @@
.gridsubdiv = 10, \
.shading = _DNA_DEFAULT_View3DShading, \
.overlay = _DNA_DEFAULT_View3DOverlay, \
.ghosts = _DNA_DEFAULT_View3DGhosts, \
\
.gridflag = V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_FLOOR | V3D_SHOW_ORTHO_GRID, \
\

View File

@ -249,6 +249,12 @@ typedef enum eHandleDisplay {
CURVE_HANDLE_NONE = 2,
} eHandleDisplay;
/* 3D View Ghosting settings. */
typedef struct View3DGhosts {
float color_before[3];
float color_after[3];
} View3DGhosts;
typedef struct View3D_Runtime {
/** Nkey panel stores stuff here. */
void *properties_storage;
@ -357,6 +363,7 @@ typedef struct View3D {
/** Display settings. */
View3DShading shading;
View3DOverlay overlay;
View3DGhosts ghosts;
/** Path to the viewer node that is currently previewed. This is retrieved from the workspace. */
ViewerPath viewer_path;
@ -484,6 +491,7 @@ enum {
V3D_FLAG2_UNUSED_15 = 1 << 15, /* cleared */
V3D_XR_SHOW_CONTROLLERS = 1 << 16,
V3D_XR_SHOW_CUSTOM_OVERLAYS = 1 << 17,
V3D_HIDE_GHOSTS = 1 << 18,
};
/** #View3D::gp_flag (short) */

View File

@ -376,6 +376,13 @@ static void rna_Object_hide_update(Main *bmain, Scene * /*scene*/, PointerRNA *p
WM_main_add_notifier(NC_OBJECT | ND_DRAW, &ob->id);
}
static void rna_Object_show_ghosts_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr)
{
Object *ob = reinterpret_cast<Object *>(ptr->owner_id);
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, &ob->id);
}
static void rna_Object_duplicator_visibility_flag_update(Main * /*bmain*/,
Scene * /*scene*/,
PointerRNA *ptr)
@ -2973,6 +2980,12 @@ static void rna_def_object_visibility(StructRNA *srna)
"footage. Objects with this setting are considered to already exist in the footage, "
"objects without it are synthetic objects being composited into it");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
prop = RNA_def_property(srna, "show_ghosts", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, nullptr, "visibility_flag", OB_HIDE_GHOSTS);
RNA_def_property_ui_text(prop, "Show Ghosts", "Show ghost frames in viewport");
RNA_def_property_ui_icon(prop, ICON_ONIONSKIN_ON, -1);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_show_ghosts_update");
}
static void rna_def_object(BlenderRNA *brna)

View File

@ -1637,6 +1637,16 @@ static char *rna_View3DOverlay_path(const PointerRNA * /*ptr*/)
return BLI_strdup("overlay");
}
static char *rna_View3DGhosts_path(const PointerRNA * /*ptr*/)
{
return BLI_strdup("ghosts");
}
static PointerRNA rna_SpaceView3D_ghosts_get(PointerRNA *ptr)
{
return rna_pointer_inherit_refine(ptr, &RNA_View3DGhosts, ptr->data);
}
/* Space Image Editor */
static PointerRNA rna_SpaceImage_overlay_get(PointerRNA *ptr)
@ -3882,6 +3892,12 @@ static void rna_def_space_outliner(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, nullptr);
prop = RNA_def_property(srna, "show_restrict_column_ghosts", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "show_restrict_flags", SO_RESTRICT_SHOW_GHOSTS);
RNA_def_property_ui_text(prop, "Show Ghosts", "Show ghost frames in viewport");
RNA_def_property_ui_icon(prop, ICON_ONIONSKIN_ON, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, nullptr);
prop = RNA_def_property(srna, "show_restrict_column_holdout", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "show_restrict_flags", SO_RESTRICT_HOLDOUT);
RNA_def_property_ui_text(prop, "Holdout", "Holdout");
@ -4931,6 +4947,40 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
}
static void rna_def_space_view3d_ghosts(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "View3DGhosts", nullptr);
RNA_def_struct_sdna(srna, "View3D");
RNA_def_struct_nested(brna, srna, "SpaceView3D");
RNA_def_struct_path_func(srna, "rna_View3DGhosts_path");
RNA_def_struct_ui_text(
srna, "3D View Ghosting Settings", "Settings for display of ghosts in the 3D viewport");
prop = RNA_def_property(srna, "show_ghosts", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, nullptr, "flag2", V3D_HIDE_GHOSTS);
RNA_def_property_ui_text(prop, "Show Ghosts", "Display ghosts in the viewport");
RNA_def_property_ui_icon(prop, ICON_GHOST_ENABLED, 0);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
prop = RNA_def_property(srna, "color_before", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_sdna(prop, nullptr, "ghosts.color_before");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Before", "Color of the ghosts before the current frame");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
prop = RNA_def_property(srna, "color_after", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_sdna(prop, nullptr, "ghosts.color_after");
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "After", "Color of the ghosts after the current frame");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
}
static void rna_def_space_view3d(BlenderRNA *brna)
{
StructRNA *srna;
@ -5283,8 +5333,16 @@ static void rna_def_space_view3d(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Overlay Settings", "Settings for display of overlays in the 3D viewport");
prop = RNA_def_property(srna, "ghosts", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "View3DGhosts");
RNA_def_property_pointer_funcs(prop, "rna_SpaceView3D_ghosts_get", nullptr, nullptr, nullptr);
RNA_def_property_ui_text(
prop, "Ghosting Settings", "Settings for display of ghosts in the 3D viewport");
rna_def_space_view3d_shading(brna);
rna_def_space_view3d_overlay(brna);
rna_def_space_view3d_ghosts(brna);
/* *** Animated *** */
RNA_define_animate_sdna(true);