GPv3: Onion Skinning #119792

Merged
Falk David merged 20 commits from filedescriptor/blender:gpv3-old-onion-skinning into main 2024-04-03 15:34:51 +02:00
8 changed files with 287 additions and 17 deletions
Showing only changes of commit 0b0028df3f - Show all commits

View File

@ -229,6 +229,50 @@ class DATA_PT_grease_pencil_layer_relations(LayerDataButtonsPanel, Panel):
col = layout.row(align=True)
col.prop_search(layer, "viewlayer_render", context.scene, "view_layers", text="View Layer")
class DATA_PT_grease_pencil_onion_skinning(DataButtonsPanel, Panel):
bl_label = "Onion Skinning"
def draw(self, context):
grease_pencil = context.grease_pencil
layout = self.layout
layout.use_property_split = True
col = layout.column()
col.prop(grease_pencil, "onion_mode")
col.prop(grease_pencil, "onion_factor", text="Opacity", slider=True)
col.prop(grease_pencil, "onion_keyframe_type")
if grease_pencil.onion_mode == 'ABSOLUTE':
col = layout.column(align=True)
col.prop(grease_pencil, "ghost_before_range", text="Frames Before")
col.prop(grease_pencil, "ghost_after_range", text="Frames After")
elif grease_pencil.onion_mode == 'RELATIVE':
col = layout.column(align=True)
col.prop(grease_pencil, "ghost_before_range", text="Keyframes Before")
col.prop(grease_pencil, "ghost_after_range", text="Keyframes After")
class DATA_PT_grease_pencil_onion_skinning_custom_colors(DataButtonsPanel, Panel):
bl_parent_id = "DATA_PT_gpencil_onion_skinning"
bl_label = "Custom Colors"
bl_options = {'DEFAULT_CLOSED'}
# def draw_header(self, context):
# gpd = context.gpencil
# self.layout.prop(gpd, "use_ghost_custom_colors", text="")
def draw(self, context):
grease_pencil = context.grease_pencil
layout = self.layout
layout.use_property_split = True
layout.enabled = grease_pencil.users <= 1# and grease_pencil.use_ghost_custom_colors
layout.prop(grease_pencil, "before_color", text="Before")
layout.prop(grease_pencil, "after_color", text="After")
class DATA_PT_grease_pencil_settings(DataButtonsPanel, Panel):
bl_label = "Settings"
@ -245,7 +289,7 @@ class DATA_PT_grease_pencil_settings(DataButtonsPanel, Panel):
class DATA_PT_grease_pencil_custom_props(DataButtonsPanel, PropertyPanel, Panel):
_context_path = "object.data"
_property_type = bpy.types.GreasePencilv3
_property_type = getattr(bpy.types, "GreasePencilv3", None)
classes = (
@ -256,6 +300,8 @@ classes = (
DATA_PT_grease_pencil_layer_masks,
DATA_PT_grease_pencil_layer_transform,
DATA_PT_grease_pencil_layer_relations,
DATA_PT_grease_pencil_onion_skinning,
DATA_PT_grease_pencil_onion_skinning_custom_colors,
DATA_PT_grease_pencil_settings,
DATA_PT_grease_pencil_custom_props,
GREASE_PENCIL_MT_grease_pencil_add_layer_extra,

View File

@ -500,6 +500,7 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b
/* Convert the onion skinning settings. */
grease_pencil.onion_skinning_settings.opacity = gpd.onion_factor;
grease_pencil.onion_skinning_settings.mode = gpd.onion_mode;
/* Convert keytype filter to a bit flag. */
if (gpd.onion_keytype == -1) {
grease_pencil.onion_skinning_settings.filter = GREASE_PENCIL_ONION_SKINNING_FILTER_ALL;
}

View File

@ -656,7 +656,7 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
};
int t_offset = 0;
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*pd->scene, grease_pencil);
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*pd->scene, grease_pencil, true);
const Span<const Layer *> layers = grease_pencil.layers();
for (const DrawingInfo info : drawings) {
const Layer &layer = *layers[info.layer_index];

View File

@ -293,7 +293,7 @@ static void OVERLAY_outline_grease_pencil(OVERLAY_PrivateData *pd, Scene *scene,
}
int t_offset = 0;
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*scene, grease_pencil);
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*scene, grease_pencil, false);
for (const DrawingInfo info : drawings) {
const bool is_screenspace = false;
const bool is_stroke_order_3d = (grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) != 0;

View File

@ -229,7 +229,7 @@ static void grease_pencil_edit_batch_ensure(Object &object,
/* Get the visible drawings. */
const Vector<ed::greasepencil::DrawingInfo> drawings =
ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil);
ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
const Span<const Layer *> layers = grease_pencil.layers();
@ -423,7 +423,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
/* Get the visible drawings. */
const Vector<ed::greasepencil::DrawingInfo> drawings =
ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil);
ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, true);
/* First, count how many vertices and triangles are needed for the whole object. Also record the
* offsets into the curves for the vertices and triangles. */

View File

@ -230,9 +230,88 @@ static std::pair<int, int> get_minmax_selected_frame_numbers(const GreasePencil
return std::pair<int, int>(frame_min, frame_max);
}
static Array<int> get_frame_numbers_for_layer(const bke::greasepencil::Layer &layer,
const int current_frame,
const bool use_multi_frame_editing)
static std::optional<int> get_frame_id(const bke::greasepencil::Layer &layer,
const GreasePencilFrame &frame,
const int frame_number,
const int frame_index,
const int current_frame,
const int current_frame_index,
const bool use_multi_frame_editing,
const bool do_onion_skinning,
const GreasePencilOnionSkinningSettings onion_settings)
{
if (use_multi_frame_editing) {
if (frame.is_selected()) {
if (do_onion_skinning) {
return (frame_number < current_frame) ? -1 : 1;
}
return 0;
}
return {};
}
else if (do_onion_skinning && layer.use_onion_skinning()) {
/* Keyframe type filter. */
if (onion_settings.filter != 0 && (onion_settings.filter & (1 << frame.type)) == 0) {
return {};
filedescriptor marked this conversation as resolved Outdated

else after return

else after return
}
/* Selected mode filter. */
if (onion_settings.mode == GP_ONION_SKINNING_MODE_SELECTED && !frame.is_selected()) {
return {};
}
int delta = (onion_settings.mode == GP_ONION_SKINNING_MODE_ABSOLUTE) ?
frame_number - current_frame :
frame_index - current_frame_index;
if (-delta > onion_settings.num_frames_before || delta > onion_settings.num_frames_after) {
return {};
}
return delta;
}
return {};
}
static Array<std::pair<int, int>> get_visible_frames_for_layer(
const GreasePencil &grease_pencil,
const bke::greasepencil::Layer &layer,
const int current_frame,
const bool use_multi_frame_editing,
const bool do_onion_skinning)
{
GreasePencilOnionSkinningSettings onion_settings = grease_pencil.onion_skinning_settings;
Vector<std::pair<int, int>> frame_numbers;
const std::optional<int> current_frame_index = layer.frame_key_at(current_frame);
const Span<int> sorted_keys = layer.sorted_keys();
for (const int frame_i : sorted_keys.index_range()) {
const int frame_number = sorted_keys[frame_i];
if (frame_number == current_frame) {
continue;
}
const GreasePencilFrame &frame = layer.frames().lookup(frame_number);
const std::optional<int> frame_id = get_frame_id(layer,
frame,
frame_number,
frame_i,
current_frame,
*current_frame_index,
use_multi_frame_editing,
do_onion_skinning,
onion_settings);
if (!frame_id.has_value()) {
continue;
}
frame_numbers.append({frame_number, *frame_id});
}
frame_numbers.append({current_frame, 0});
return frame_numbers.as_span();
}
static Array<int> get_editable_frames_for_layer(const bke::greasepencil::Layer &layer,
const int current_frame,
const bool use_multi_frame_editing)
{
Vector<int> frame_numbers;
if (use_multi_frame_editing) {
@ -271,7 +350,7 @@ Vector<MutableDrawingInfo> retrieve_editable_drawings(const Scene &scene,
if (!layer.is_editable()) {
continue;
}
const Array<int> frame_numbers = get_frame_numbers_for_layer(
const Array<int> frame_numbers = get_editable_frames_for_layer(
layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
@ -309,7 +388,7 @@ Vector<MutableDrawingInfo> retrieve_editable_drawings_with_falloff(const Scene &
if (!layer.is_editable()) {
continue;
}
const Array<int> frame_numbers = get_frame_numbers_for_layer(
const Array<int> frame_numbers = get_editable_frames_for_layer(
layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
@ -340,7 +419,7 @@ Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer(
GP_USE_MULTI_FRAME_EDITING) != 0;
Vector<MutableDrawingInfo> editable_drawings;
const Array<int> frame_numbers = get_frame_numbers_for_layer(
const Array<int> frame_numbers = get_editable_frames_for_layer(
layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
@ -353,7 +432,8 @@ Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer(
}
Vector<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
const GreasePencil &grease_pencil)
const GreasePencil &grease_pencil,
const bool do_onion_skinning)
{
using namespace blender::bke::greasepencil;
const int current_frame = scene.r.cfra;
@ -368,11 +448,11 @@ Vector<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
if (!layer.is_visible()) {
continue;
}
const Array<int> frame_numbers = get_frame_numbers_for_layer(
layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
const Array<std::pair<int, int>> frames = get_visible_frames_for_layer(
grease_pencil, layer, current_frame, use_multi_frame_editing, do_onion_skinning);
for (const auto &[frame_number, onion_id] : frames) {
if (const Drawing *drawing = grease_pencil.get_drawing_at(layer, frame_number)) {
visible_drawings.append({*drawing, layer_i, frame_number});
visible_drawings.append({*drawing, layer_i, frame_number, onion_id});
}
}
}

View File

@ -178,6 +178,7 @@ struct DrawingInfo {
const bke::greasepencil::Drawing &drawing;
const int layer_index;
const int frame_number;
const int onion_id;
filedescriptor marked this conversation as resolved Outdated

Add a comment what the onion_id is (distance from current frame i suppose).

Add a comment what the `onion_id` is (distance from current frame i suppose).
};
struct MutableDrawingInfo {
bke::greasepencil::Drawing &drawing;
@ -192,7 +193,8 @@ Vector<MutableDrawingInfo> retrieve_editable_drawings_with_falloff(const Scene &
Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer(
const Scene &scene, GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer);
Vector<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
const GreasePencil &grease_pencil);
const GreasePencil &grease_pencil,
bool do_onion_skinning);
IndexMask retrieve_editable_strokes(Object &grease_pencil_object,
const bke::greasepencil::Drawing &drawing,

View File

@ -559,6 +559,50 @@ static void rna_def_grease_pencil_data(BlenderRNA *brna)
{0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem prop_enum_onion_modes_items[] = {
{GP_ONION_SKINNING_MODE_ABSOLUTE,
"ABSOLUTE",
0,
"Frames",
"Frames in absolute range of the scene frame"},
{GP_ONION_SKINNING_MODE_RELATIVE,
"RELATIVE",
0,
"Keyframes",
"Frames in relative range of the Grease Pencil keyframes"},
{GP_ONION_SKINNING_MODE_SELECTED, "SELECTED", 0, "Selected", "Only selected keyframes"},
{0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem prop_enum_onion_keyframe_type_items[] = {
{GP_ONION_SKINNING_FILTER_KEYTYPE_KEYFRAME,
"KEYFRAME",
ICON_KEYTYPE_KEYFRAME_VEC,
"Keyframe",
"Normal keyframe, e.g. for key poses"},
{GP_ONION_SKINNING_FILTER_KEYTYPE_BREAKDOWN,
"BREAKDOWN",
ICON_KEYTYPE_BREAKDOWN_VEC,
"Breakdown",
"A breakdown pose, e.g. for transitions between key poses"},
{GP_ONION_SKINNING_FILTER_KEYTYPE_MOVEHOLD,
"MOVING_HOLD",
ICON_KEYTYPE_MOVING_HOLD_VEC,
"Moving Hold",
"A keyframe that is part of a moving hold"},
{GP_ONION_SKINNING_FILTER_KEYTYPE_EXTREME,
"EXTREME",
ICON_KEYTYPE_EXTREME_VEC,
"Extreme",
"An 'extreme' pose, or some other purpose as needed"},
{GP_ONION_SKINNING_FILTER_KEYTYPE_JITTER,
"JITTER",
ICON_KEYTYPE_JITTER_VEC,
"Jitter",
"A filler or baked keyframe for keying on ones, or some other purpose as needed"},
{0, nullptr, 0, nullptr, nullptr},
};
srna = RNA_def_struct(brna, "GreasePencilv3", "ID");
RNA_def_struct_sdna(srna, "GreasePencil");
RNA_def_struct_ui_text(srna, "Grease Pencil", "Grease Pencil data-block");
@ -632,6 +676,103 @@ static void rna_def_grease_pencil_data(BlenderRNA *brna)
"Stroke Depth Order",
"Defines how the strokes are ordered in 3D space (for objects not displayed 'In Front')");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
/* Onion Skinning */
prop = RNA_def_property(srna, "ghost_before_range", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, "GreasePencilOnionSkinningSettings", "num_frames_before");
RNA_def_property_range(prop, 0, 120);
RNA_def_property_int_default(prop, 1);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
RNA_def_property_ui_text(prop,
"Frames Before",
"Maximum number of frames to show before current frame "
"(0 = don't show any frames before current)");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
prop = RNA_def_property(srna, "ghost_after_range", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, "GreasePencilOnionSkinningSettings", "num_frames_after");
RNA_def_property_range(prop, 0, 120);
RNA_def_property_int_default(prop, 1);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
RNA_def_property_ui_text(prop,
"Frames After",
"Maximum number of frames to show after current frame "
"(0 = don't show any frames after current)");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
// prop = RNA_def_property(srna, "use_ghost_custom_colors", PROP_BOOLEAN, PROP_NONE);
// RNA_def_property_boolean_sdna(
// prop, nullptr, "onion_flag", GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL);
// RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
// RNA_def_property_ui_text(prop, "Use Custom Ghost Colors", "Use custom colors for ghost
// frames"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "before_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_sdna(prop, "GreasePencilOnionSkinningSettings", "color_before");
RNA_def_property_array(prop, 3);
RNA_def_property_range(prop, 0.0f, 1.0f);
// RNA_def_property_float_array_default(prop, onion_dft1);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
RNA_def_property_ui_text(prop, "Before Color", "Base color for ghosts before the active frame");
RNA_def_property_update(prop,
NC_SCREEN | NC_SCENE | ND_TOOLSETTINGS | ND_DATA | NC_GPENCIL,
"rna_grease_pencil_update");
prop = RNA_def_property(srna, "after_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_sdna(prop, "GreasePencilOnionSkinningSettings", "color_after");
RNA_def_property_array(prop, 3);
RNA_def_property_range(prop, 0.0f, 1.0f);
// RNA_def_property_float_array_default(prop, onion_dft2);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame");
RNA_def_property_update(prop,
NC_SCREEN | NC_SCENE | ND_TOOLSETTINGS | ND_DATA | NC_GPENCIL,
"rna_grease_pencil_update");
// prop = RNA_def_property(srna, "use_ghosts_always", PROP_BOOLEAN, PROP_NONE);
// RNA_def_property_boolean_sdna(prop, nullptr, "onion_flag", GP_ONION_GHOST_ALWAYS);
// RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
// RNA_def_property_ui_text(prop,
// "Always Show Ghosts",
// "Ghosts are shown in renders and animation playback. Useful for "
// "special effects (e.g. motion blur)");
// RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "onion_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, "GreasePencilOnionSkinningSettings", "mode");
RNA_def_property_enum_items(prop, prop_enum_onion_modes_items);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
RNA_def_property_ui_text(prop, "Mode", "Mode to display frames");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
prop = RNA_def_property(srna, "onion_keyframe_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, "GreasePencilOnionSkinningSettings", "filter");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
RNA_def_property_enum_items(prop, prop_enum_onion_keyframe_type_items);
RNA_def_property_ui_text(prop, "Filter by Type", "Type of keyframe (for filtering)");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
// prop = RNA_def_property(srna, "use_onion_fade", PROP_BOOLEAN, PROP_NONE);
// RNA_def_property_boolean_sdna(prop, nullptr, "onion_flag", GP_ONION_FADE);
// RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
// RNA_def_property_ui_text(
// prop, "Fade", "Display onion keyframes with a fade in color transparency");
// RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
// prop = RNA_def_property(srna, "use_onion_loop", PROP_BOOLEAN, PROP_NONE);
// RNA_def_property_boolean_sdna(prop, nullptr, "onion_flag", GP_ONION_LOOP);
// RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
// RNA_def_property_ui_text(
// prop, "Show Start Frame", "Display onion keyframes for looping animations");
// RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "onion_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, "GreasePencilOnionSkinningSettings", "opacity");
RNA_def_property_float_default(prop, 0.5f);
RNA_def_property_range(prop, 0.0, 1.0f);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, ParameterFlag(0));
RNA_def_property_ui_text(prop, "Onion Opacity", "Change fade opacity of displayed onion frames");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
}
void RNA_def_grease_pencil(BlenderRNA *brna)