Merge branch 'blender-v4.3-release'

This commit is contained in:
Lukas Tönne
2024-10-09 10:28:39 +02:00
47 changed files with 26 additions and 8139 deletions

View File

@@ -26,7 +26,6 @@ _modules = [
"properties_data_curve",
"properties_data_curves",
"properties_data_empty",
"properties_data_gpencil",
"properties_data_grease_pencil",
"properties_data_light",
"properties_data_lattice",

View File

@@ -1,236 +0,0 @@
# SPDX-FileCopyrightText: 2018-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu, Panel, UIList
from rna_prop_ui import PropertyPanel
from .space_properties import PropertiesAnimationMixin
###############################
# Base-Classes (for shared stuff - e.g. poll, attributes, etc.)
class DataButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
return context.gpencil
class ObjectButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
ob = context.object
return ob and ob.type == 'GPENCIL'
class LayerDataButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
gpencil = context.gpencil
return gpencil and gpencil.layers.active
###############################
# GP Object Properties Panels and Helper Classes
class DATA_PT_context_gpencil(DataButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
def draw(self, context):
layout = self.layout
ob = context.object
space = context.space_data
if ob:
layout.template_ID(ob, "data")
else:
layout.template_ID(space, "pin_id")
class DATA_PT_gpencil_onion_skinning(DataButtonsPanel, Panel):
bl_label = "Onion Skinning"
def draw(self, context):
gpd = context.gpencil
layout = self.layout
layout.use_property_split = True
col = layout.column()
col.prop(gpd, "onion_mode")
col.prop(gpd, "onion_factor", text="Opacity", slider=True)
col.prop(gpd, "onion_keyframe_type")
if gpd.onion_mode == 'ABSOLUTE':
col = layout.column(align=True)
col.prop(gpd, "ghost_before_range", text="Frames Before")
col.prop(gpd, "ghost_after_range", text="Frames After")
elif gpd.onion_mode == 'RELATIVE':
col = layout.column(align=True)
col.prop(gpd, "ghost_before_range", text="Keyframes Before")
col.prop(gpd, "ghost_after_range", text="Keyframes After")
class DATA_PT_gpencil_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):
gpd = context.gpencil
layout = self.layout
layout.use_property_split = True
layout.enabled = gpd.users <= 1 and gpd.use_ghost_custom_colors
layout.prop(gpd, "before_color", text="Before")
layout.prop(gpd, "after_color", text="After")
class DATA_PT_gpencil_onion_skinning_display(DataButtonsPanel, Panel):
bl_parent_id = "DATA_PT_gpencil_onion_skinning"
bl_label = "Display"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
gpd = context.gpencil
layout = self.layout
layout.use_property_split = True
layout.enabled = gpd.users <= 1
layout.prop(gpd, "use_ghosts_always", text="View in Render")
col = layout.column(align=True)
col.prop(gpd, "use_onion_fade", text="Fade")
sub = layout.column()
sub.active = gpd.onion_mode in {'RELATIVE', 'SELECTED'}
sub.prop(gpd, "use_onion_loop", text="Show Start Frame")
class GPENCIL_UL_vgroups(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
vgroup = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)
class DATA_PT_gpencil_strokes(DataButtonsPanel, Panel):
bl_label = "Strokes"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
ob = context.object
gpd = context.gpencil
col = layout.column(align=True)
col.prop(gpd, "stroke_depth_order")
if ob:
col.enabled = not ob.show_in_front
col = layout.column(align=True)
col.prop(gpd, "stroke_thickness_space")
sub = col.column()
sub.active = gpd.stroke_thickness_space == 'WORLDSPACE'
sub.prop(gpd, "pixel_factor", text="Thickness Scale")
col.prop(gpd, "edit_curve_resolution")
class DATA_PT_gpencil_display(DataButtonsPanel, Panel):
bl_label = "Viewport Display"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
gpd = context.gpencil
layout.prop(gpd, "edit_line_color", text="Edit Line Color")
class DATA_PT_gpencil_canvas(DataButtonsPanel, Panel):
bl_label = "Canvas"
bl_parent_id = "DATA_PT_gpencil_display"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
gpd = context.gpencil
grid = gpd.grid
row = layout.row(align=True)
col = row.column()
col.prop(grid, "color", text="Color")
col.prop(grid, "scale", text="Scale")
col.prop(grid, "offset")
row = layout.row(align=True)
col = row.column()
col.prop(grid, "lines", text="Subdivisions")
class DATA_PT_gpencil_animation(DataButtonsPanel, PropertiesAnimationMixin, PropertyPanel, Panel):
_animated_id_context_property = "gpencil"
class DATA_PT_custom_props_gpencil(DataButtonsPanel, PropertyPanel, Panel):
_context_path = "object.data"
_property_type = bpy.types.GreasePencil
###############################
classes = (
DATA_PT_context_gpencil,
DATA_PT_gpencil_onion_skinning,
DATA_PT_gpencil_onion_skinning_custom_colors,
DATA_PT_gpencil_onion_skinning_display,
DATA_PT_gpencil_strokes,
DATA_PT_gpencil_display,
DATA_PT_gpencil_canvas,
DATA_PT_gpencil_animation,
DATA_PT_custom_props_gpencil,
GPENCIL_UL_vgroups,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@@ -20,25 +20,6 @@ struct bGPDlayer;
struct bGPDstroke;
struct bGPdata;
/**
* Convert a curve object to grease pencil stroke.
*
* \param bmain: Main thread pointer
* \param scene: Original scene.
* \param ob_gp: Grease pencil object to add strokes.
* \param ob_cu: Curve to convert.
* \param use_collections: Create layers using collection names.
* \param scale_thickness: Scale thickness factor.
* \param sample: Sample distance, zero to disable.
*/
void BKE_gpencil_convert_curve(struct Main *bmain,
struct Scene *scene,
struct Object *ob_gp,
struct Object *ob_cu,
bool use_collections,
float scale_thickness,
float sample);
/**
* Creates a bGPDcurve by doing a cubic curve fitting on the grease pencil stroke points.
*/
@@ -46,32 +27,12 @@ struct bGPDcurve *BKE_gpencil_stroke_editcurve_generate(struct bGPDstroke *gps,
float error_threshold,
float corner_angle,
float stroke_radius);
/**
* Updates the edit-curve for a stroke. Frees the old curve if one exists and generates a new one.
*/
void BKE_gpencil_stroke_editcurve_update(struct bGPdata *gpd,
struct bGPDlayer *gpl,
struct bGPDstroke *gps);
/**
* Sync the selection from stroke to edit-curve.
*/
void BKE_gpencil_editcurve_stroke_sync_selection(struct bGPdata *gpd,
struct bGPDstroke *gps,
struct bGPDcurve *gpc);
/**
* Sync the selection from edit-curve to stroke.
*/
void BKE_gpencil_stroke_editcurve_sync_selection(struct bGPdata *gpd,
struct bGPDstroke *gps,
struct bGPDcurve *gpc);
void BKE_gpencil_strokes_selected_update_editcurve(struct bGPdata *gpd);
void BKE_gpencil_strokes_selected_sync_selection_editcurve(struct bGPdata *gpd);
/**
* Recalculate stroke points with the edit-curve of the stroke.
*/
void BKE_gpencil_stroke_update_geometry_from_editcurve(struct bGPDstroke *gps,
uint resolution,
bool is_adaptive);
/**
* Recalculate the handles of the edit curve of a grease pencil stroke.
*/

View File

@@ -67,57 +67,12 @@ void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps);
* \param r_normal: Return Normal vector normalized
*/
void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]);
/**
* Reduce a series of points to a simplified version,
* but maintains the general shape of the series.
*
* Ramer - Douglas - Peucker algorithm
* by http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
* \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
* \param epsilon: Epsilon value to define precision of the algorithm
*/
void BKE_gpencil_stroke_simplify_adaptive(struct bGPdata *gpd,
struct bGPDstroke *gps,
float epsilon);
/**
* Simplify alternate vertex of stroke except extremes.
* \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_stroke_simplify_fixed(struct bGPdata *gpd, struct bGPDstroke *gps);
/**
* Subdivide a stroke
* \param gpd: Grease pencil data-block
* \param gps: Stroke
* \param level: Level of subdivision
* \param type: Type of subdivision
*/
void BKE_gpencil_stroke_subdivide(struct bGPdata *gpd,
struct bGPDstroke *gps,
int level,
int type);
/**
* Trim stroke to the first intersection or loop.
* \param gps: Stroke data
*/
bool BKE_gpencil_stroke_trim(struct bGPdata *gpd, struct bGPDstroke *gps);
/**
* Reduce a series of points when the distance is below a threshold.
* Special case for first and last points (both are kept) for other points,
* the merge point always is at first point.
*
* \param gpd: Grease pencil data-block.
* \param gpf: Grease Pencil frame.
* \param gps: Grease Pencil stroke.
* \param threshold: Distance between points.
* \param use_unselected: Set to true to analyze all stroke and not only selected points.
*/
void BKE_gpencil_stroke_merge_distance(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
float threshold,
bool use_unselected);
/**
* Get points of stroke always flat to view not affected
@@ -199,18 +154,6 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd,
const GPencilPointCoordinates *elem_data,
const float mat[4][4]);
/**
* Resample a stroke
* \param gpd: Grease pencil data-block
* \param gps: Stroke to sample
* \param dist: Distance of one segment
* \param sharp_threshold: Threshold for preserving sharp corners
*/
bool BKE_gpencil_stroke_sample(struct bGPdata *gpd,
struct bGPDstroke *gps,
const float dist,
const bool select,
const float sharp_threshold);
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth
@@ -293,47 +236,7 @@ void BKE_gpencil_stroke_smooth(struct bGPDstroke *gps,
* \param gps: Stroke to close
*/
bool BKE_gpencil_stroke_close(struct bGPDstroke *gps);
/**
* Dissolve points in stroke.
* \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param gps: Grease pencil stroke
* \param tag: Type of tag for point
*/
void BKE_gpencil_dissolve_points(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
short tag);
/**
* Backbone stretch similar to Freestyle.
* \param gps: Stroke to sample.
* \param dist: Length of the added section.
* \param overshoot_fac: Relative length of the curve which is used to determine the extension.
* \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End).
* \param follow_curvature: True for approximating curvature of given overshoot.
* \param extra_point_count: When follow_curvature is true, use this amount of extra points.
*/
bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps,
float dist,
float overshoot_fac,
short mode,
bool follow_curvature,
int extra_point_count,
float segment_influence,
float max_angle,
bool invert_curvature);
/**
* Trim stroke to needed segments.
* \param gps: Target stroke.
* \param index_from: the index of the first point to be used in the trimmed result.
* \param index_to: the index of the last point to be used in the trimmed result.
* \param keep_point: Keep strokes with one point. False remove the single points strokes
*/
bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
int index_from,
int index_to,
const bool keep_point);
/**
* Split the given stroke into several new strokes, partitioning
* it based on whether the stroke points have a particular flag
@@ -347,38 +250,11 @@ struct bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(struct bGPdata *gpd,
bool select,
bool flat_cap,
int limit);
void BKE_gpencil_curve_delete_tagged_points(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
struct bGPDstroke *next_stroke,
struct bGPDcurve *gpc,
int tag_flags);
/**
* Flip stroke.
*/
void BKE_gpencil_stroke_flip(struct bGPDstroke *gps);
/**
* Split stroke.
* \param gpd: Grease pencil data-block.
* \param gpf: Grease pencil frame.
* \param gps: Grease pencil original stroke.
* \param before_index: Position of the point to split.
* \param remaining_gps: Secondary stroke after split.
* \return True if the split was done
*/
bool BKE_gpencil_stroke_split(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
int before_index,
struct bGPDstroke **remaining_gps);
/**
* Shrink the stroke by length.
* \param gps: Stroke to shrink
* \param dist: delta length
* \param mode: 1->Start, 2->End
*/
bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, float dist, short mode);
/**
* Calculate grease pencil stroke length.
@@ -409,63 +285,6 @@ void BKE_gpencil_stroke_join(struct bGPDstroke *gps_a,
bool fit_thickness,
bool smooth,
bool auto_flip);
/**
* Set stroke start point in the selected index. Only works for Cyclic strokes.
* \param start_idx: Index of the point to be the start point.
*/
void BKE_gpencil_stroke_start_set(struct bGPDstroke *gps, int start_idx);
/**
* Copy the stroke of the frame to all frames selected (except current).
*/
void BKE_gpencil_stroke_copy_to_keyframes(struct bGPdata *gpd,
struct bGPDlayer *gpl,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
bool tail);
/**
* Convert a mesh object to grease pencil stroke.
*
* \param bmain: Main thread pointer.
* \param depsgraph: Original depsgraph.
* \param scene: Original scene.
* \param ob_gp: Grease pencil object to add strokes.
* \param ob_mesh: Mesh to convert.
* \param angle: Limit angle to consider a edge-loop ends.
* \param thickness: Thickness of the strokes.
* \param offset: Offset along the normals.
* \param matrix: Transformation matrix.
* \param frame_offset: Destination frame number offset.
* \param use_seams: Only export seam edges.
* \param use_faces: Export faces as filled strokes.
*/
bool BKE_gpencil_convert_mesh(struct Main *bmain,
struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob_gp,
struct Object *ob_mesh,
float angle,
int thickness,
float offset,
const float matrix[4][4],
int frame_offset,
bool use_seams,
bool use_faces,
bool use_vgroups);
/**
* Subdivide the grease pencil stroke so the number of points is target_number.
* Does not change the shape of the stroke. The new points will be distributed as
* uniformly as possible by repeatedly subdividing the current longest edge.
*
* \param gps: The stroke to be up-sampled.
* \param target_number: The number of points the up-sampled stroke should have.
* \param select: Select/Deselect the stroke.
*/
void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd,
struct bGPDstroke *gps,
uint32_t target_number,
bool select);
/**
* Stroke to view space
@@ -485,18 +304,6 @@ void BKE_gpencil_stroke_to_view_space(struct bGPDstroke *gps,
void BKE_gpencil_stroke_from_view_space(struct bGPDstroke *gps,
float viewinv[4][4],
const float diff_mat[4][4]);
/**
* Calculates the perimeter of a stroke projected from the view and returns it as a new stroke.
* \param subdivisions: Number of subdivisions for the start and end caps.
* \return bGPDstroke pointer to stroke perimeter.
*/
struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(float viewmat[4][4],
struct bGPdata *gpd,
const struct bGPDlayer *gpl,
struct bGPDstroke *gps,
int subdivisions,
const float diff_mat[4][4],
const float thickness_chg);
/**
* Get average pressure.
*/

View File

@@ -42,8 +42,6 @@ struct bGPdata;
#define GPENCIL_SIMPLIFY_FILL(scene, playing) \
((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \
(scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL)))
#define GPENCIL_SIMPLIFY_MODIF(scene) \
((GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER)))
#define GPENCIL_SIMPLIFY_FX(scene, playing) \
((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \
(scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FX)))
@@ -55,20 +53,6 @@ struct bGPdata;
/* Vertex Color macros. */
#define GPENCIL_USE_VERTEX_COLOR(toolsettings) \
(((toolsettings)->gp_paint->mode == GPPAINT_FLAG_USE_VERTEXCOLOR))
#define GPENCIL_USE_VERTEX_COLOR_STROKE(toolsettings, brush) \
((GPENCIL_USE_VERTEX_COLOR(toolsettings) && \
(((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_STROKE) || \
((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH))))
#define GPENCIL_USE_VERTEX_COLOR_FILL(toolsettings, brush) \
((GPENCIL_USE_VERTEX_COLOR(toolsettings) && \
(((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_FILL) || \
((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH))))
#define GPENCIL_TINT_VERTEX_COLOR_STROKE(brush) \
(((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_STROKE) || \
((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH))
#define GPENCIL_TINT_VERTEX_COLOR_FILL(brush) \
(((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_FILL) || \
((brush)->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH))
/* ------------ Grease-Pencil API ------------------ */
@@ -88,11 +72,6 @@ void BKE_gpencil_free_layers(struct ListBase *list);
void BKE_gpencil_free_legacy_palette_data(struct ListBase *list);
/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */
void BKE_gpencil_free_data(struct bGPdata *gpd, bool free_all);
/**
* Delete grease pencil evaluated data
* \param gpd_eval: Grease pencil data-block
*/
void BKE_gpencil_eval_delete(struct bGPdata *gpd_eval);
void BKE_gpencil_free_layer_masks(struct bGPDlayer *gpl);
/**
* Tag data-block for depsgraph update.
@@ -104,17 +83,6 @@ void BKE_gpencil_tag(struct bGPdata *gpd);
void BKE_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd);
void BKE_gpencil_batch_cache_free(struct bGPdata *gpd);
/**
* Ensure selection status of stroke is in sync with its points.
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_stroke_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps);
void BKE_gpencil_curve_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps);
/** Assign unique stroke ID for selection. */
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps);
/** Reset unique stroke ID for selection. */
void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps);
/**
* Add a new gp-frame to the given layer.
* \param gpl: Grease pencil layer
@@ -164,38 +132,6 @@ struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src,
bool dup_frames,
bool dup_strokes);
/**
* Make a copy of a given gpencil data settings.
*/
void BKE_gpencil_data_copy_settings(const struct bGPdata *gpd_src, struct bGPdata *gpd_dst);
/**
* Make a copy of a given gpencil layer settings.
*/
void BKE_gpencil_layer_copy_settings(const struct bGPDlayer *gpl_src, struct bGPDlayer *gpl_dst);
/**
* Make a copy of a given gpencil frame settings.
*/
void BKE_gpencil_frame_copy_settings(const struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst);
/**
* Make a copy of a given gpencil stroke settings.
*/
void BKE_gpencil_stroke_copy_settings(const struct bGPDstroke *gps_src,
struct bGPDstroke *gps_dst);
/**
* Make a copy of strokes between gpencil frames.
* \param gpf_src: Source grease pencil frame
* \param gpf_dst: Destination grease pencil frame
*/
void BKE_gpencil_frame_copy_strokes(struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst);
/* Create a hash with the list of selected frame number. */
void BKE_gpencil_frame_selected_hash(struct bGPdata *gpd, struct GHash *r_list);
/* Make a copy of a given gpencil stroke editcurve */
struct bGPDcurve *BKE_gpencil_stroke_curve_duplicate(struct bGPDcurve *gpc_src);
/**
* Make a copy of a given grease-pencil stroke.
* \param gps_src: Source grease pencil strokes.
@@ -216,73 +152,6 @@ struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain,
const struct bGPdata *gpd,
bool internal_copy);
/**
* Delete the last stroke of the given frame.
* \param gpl: Grease pencil layer
* \param gpf: Grease pencil frame
*/
void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf);
/* materials */
/**
* Reassign strokes using a material.
* \param gpd: Grease pencil data-block
* \param totcol: Total materials
* \param index: Index of the material
*/
void BKE_gpencil_material_index_reassign(struct bGPdata *gpd, int totcol, int index);
/**
* Remove strokes using a material.
* \param gpd: Grease pencil data-block
* \param index: Index of the material
* \return True if removed
*/
bool BKE_gpencil_material_index_used(struct bGPdata *gpd, int index);
/**
* Remap material
* \param gpd: Grease pencil data-block
* \param remap: Remap index
* \param remap_len: Remap length
*/
void BKE_gpencil_material_remap(struct bGPdata *gpd,
const unsigned int *remap,
unsigned int remap_len);
/**
* Load a table with material conversion index for merged materials.
* \param ob: Grease pencil object.
* \param hue_threshold: Threshold for Hue.
* \param sat_threshold: Threshold for Saturation.
* \param val_threshold: Threshold for Value.
* \param r_mat_table: return material table.
* \return True if done.
*/
bool BKE_gpencil_merge_materials_table_get(struct Object *ob,
float hue_threshold,
float sat_threshold,
float val_threshold,
struct GHash *r_mat_table);
/**
* Merge similar materials
* \param ob: Grease pencil object
* \param hue_threshold: Threshold for Hue
* \param sat_threshold: Threshold for Saturation
* \param val_threshold: Threshold for Value
* \param r_removed: Number of materials removed
* \return True if done
*/
bool BKE_gpencil_merge_materials(struct Object *ob,
float hue_threshold,
float sat_threshold,
float val_threshold,
int *r_removed);
/* statistics functions */
/**
* Calc grease pencil statistics functions.
* \param gpd: Grease pencil data-block
*/
void BKE_gpencil_stats_update(struct bGPdata *gpd);
/**
* Create a new stroke, with pre-allocated data buffers.
* \param mat_idx: Index of the material
@@ -303,22 +172,6 @@ struct bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thic
struct bGPDstroke *BKE_gpencil_stroke_add(
struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness, bool insert_at_head);
/**
* Add a stroke and copy the temporary drawing color value
* from one of the existing stroke.
* \param gpf: Grease pencil frame
* \param existing: Stroke with the style to copy
* \param mat_idx: Material index
* \param totpoints: Total points
* \param thickness: Stroke thickness
* \return Pointer to new stroke
*/
struct bGPDstroke *BKE_gpencil_stroke_add_existing_style(struct bGPDframe *gpf,
struct bGPDstroke *existing,
int mat_idx,
int totpoints,
short thickness);
struct bGPDcurve *BKE_gpencil_stroke_editcurve_new(int tot_curve_points);
/* Stroke and Fill - Alpha Visibility Threshold */
@@ -405,13 +258,6 @@ void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl);
*/
void BKE_gpencil_layer_autolock_set(struct bGPdata *gpd, bool unlock);
/**
* Add grease pencil mask layer.
* \param gpl: Grease pencil layer
* \param name: Name of the mask
* \return Pointer to new mask layer
*/
struct bGPDlayer_Mask *BKE_gpencil_layer_mask_add(struct bGPDlayer *gpl, const char *name);
/**
* Remove grease pencil mask layer.
* \param gpl: Grease pencil layer
@@ -424,13 +270,6 @@ void BKE_gpencil_layer_mask_remove(struct bGPDlayer *gpl, struct bGPDlayer_Mask
* \param name: Name of the mask layer
*/
void BKE_gpencil_layer_mask_remove_ref(struct bGPdata *gpd, const char *name);
/**
* Get mask layer by name.
* \param gpl: Grease pencil layer
* \param name: Mask name
* \return Pointer to mask layer
*/
struct bGPDlayer_Mask *BKE_gpencil_layer_mask_named_get(struct bGPDlayer *gpl, const char *name);
/**
* Sort grease pencil mask layers.
* \param gpd: Grease pencil data-block
@@ -450,10 +289,6 @@ void BKE_gpencil_layer_mask_copy(const struct bGPDlayer *gpl_src, struct bGPDlay
* Clean any invalid mask layer.
*/
void BKE_gpencil_layer_mask_cleanup(struct bGPdata *gpd, struct bGPDlayer *gpl);
/**
* Clean any invalid mask layer for all layers.
*/
void BKE_gpencil_layer_mask_cleanup_all_layers(struct bGPdata *gpd);
/**
* Sort grease pencil frames.
@@ -462,17 +297,7 @@ void BKE_gpencil_layer_mask_cleanup_all_layers(struct bGPdata *gpd);
*/
void BKE_gpencil_layer_frames_sort(struct bGPDlayer *gpl, bool *r_has_duplicate_frames);
struct bGPDlayer *BKE_gpencil_layer_get_by_name(struct bGPdata *gpd,
const char *name,
int first_if_not_found);
/* Brush */
/**
* Get grease pencil material from brush.
* \param brush: Brush
* \return Pointer to material
*/
struct Material *BKE_gpencil_brush_material_get(struct Brush *brush);
/**
* Set grease pencil brush material.
* \param brush: Brush
@@ -480,121 +305,7 @@ struct Material *BKE_gpencil_brush_material_get(struct Brush *brush);
*/
void BKE_gpencil_brush_material_set(struct Brush *brush, struct Material *material);
/* Object */
/**
* Get active color, and add all default settings if we don't find anything.
* \param ob: Grease pencil object
* \return Material pointer
*/
struct Material *BKE_gpencil_object_material_ensure_active(struct Object *ob);
/**
* Adds the pinned material to the object if necessary.
* \param bmain: Main pointer
* \param ob: Grease pencil object
* \param brush: Brush
* \return Pointer to material
*/
struct Material *BKE_gpencil_object_material_ensure_from_brush(struct Main *bmain,
struct Object *ob,
struct Brush *brush);
/**
* Assigns the material to object (if not already present) and returns its index (mat_nr).
* \param bmain: Main pointer
* \param ob: Grease pencil object
* \param material: Material
* \return Index of the material
*/
int BKE_gpencil_object_material_ensure(struct Main *bmain,
struct Object *ob,
struct Material *material);
struct Material *BKE_gpencil_object_material_ensure_by_name(struct Main *bmain,
struct Object *ob,
const char *name,
int *r_index);
/**
* Creates a new grease-pencil material and assigns it to object.
* \param bmain: Main pointer
* \param ob: Grease pencil object
* \param name: Material name
* \param r_index: value is set to zero based index of the new material if \a r_index is not NULL.
* \return Material pointer.
*/
struct Material *BKE_gpencil_object_material_new(struct Main *bmain,
struct Object *ob,
const char *name,
int *r_index);
/**
* Get material index (0-based like mat_nr not #Object::actcol).
* \param ob: Grease pencil object
* \param ma: Material
* \return Index of the material
*/
int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma);
int BKE_gpencil_object_material_index_get_by_name(struct Object *ob, const char *name);
/**
* Returns the material for a brush with respect to its pinned state.
* \param ob: Grease pencil object
* \param brush: Brush
* \return Material pointer
*/
struct Material *BKE_gpencil_object_material_from_brush_get(struct Object *ob,
struct Brush *brush);
/**
* Returns the material index for a brush with respect to its pinned state.
* \param ob: Grease pencil object
* \param brush: Brush
* \return Material index.
*/
int BKE_gpencil_object_material_get_index_from_brush(struct Object *ob, struct Brush *brush);
/**
* Guaranteed to return a material assigned to object. Returns never NULL.
* \param bmain: Main pointer
* \param ob: Grease pencil object
* \return Material pointer.
*/
struct Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(
struct Main *bmain, struct Object *ob, struct ToolSettings *ts);
/**
* Guaranteed to return a material assigned to object. Returns never NULL.
* \param bmain: Main pointer
* \param ob: Grease pencil object.
* \param brush: Brush
* \return Material pointer
*/
struct Material *BKE_gpencil_object_material_ensure_from_active_input_brush(struct Main *bmain,
struct Object *ob,
struct Brush *brush);
/**
* Guaranteed to return a material assigned to object. Returns never NULL.
* Only use this for materials unrelated to user input.
* \param ob: Grease pencil object
* \return Material pointer
*/
struct Material *BKE_gpencil_object_material_ensure_from_active_input_material(struct Object *ob);
/**
* Check if stroke has any point selected
* \param gps: Grease pencil stroke
* \return True if selected
*/
bool BKE_gpencil_stroke_select_check(const struct bGPDstroke *gps);
/* vertex groups */
/**
* Ensure stroke has vertex group.
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_dvert_ensure(struct bGPDstroke *gps);
/**
* Remove a vertex group.
* \param ob: Grease pencil object
* \param defgroup: deform group
*/
void BKE_gpencil_vgroup_remove(struct Object *ob, struct bDeformGroup *defgroup);
/**
* Make a copy of a given gpencil weights.
* \param gps_src: Source grease pencil stroke
@@ -602,14 +313,6 @@ void BKE_gpencil_vgroup_remove(struct Object *ob, struct bDeformGroup *defgroup)
*/
void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGPDstroke *gps_dst);
/* Set active frame by layer. */
/**
* Set current grease pencil active frame.
* \param depsgraph: Current depsgraph
* \param gpd: Grease pencil data-block.
*/
void BKE_gpencil_frame_active_set(struct Depsgraph *depsgraph, struct bGPdata *gpd);
/**
* Get range of selected frames in layer.
* Always the active frame is considered as selected, so if no more selected the range
@@ -636,19 +339,6 @@ float BKE_gpencil_multiframe_falloff_calc(
* \param scene: Scene
*/
void BKE_gpencil_palette_ensure(struct Main *bmain, struct Scene *scene);
/**
* Create grease pencil strokes from image
* \param sima: Image
* \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param size: Size
* \param mask: Mask
* \return True if done
*/
bool BKE_gpencil_from_image(
struct SpaceImage *sima, struct bGPdata *gpd, struct bGPDframe *gpf, float size, bool mask);
/* Iterators */
/**
* Frame & stroke are NULL if it is a layer callback.
@@ -658,11 +348,6 @@ typedef void (*gpIterCb)(struct bGPDlayer *layer,
struct bGPDstroke *stroke,
void *thunk);
void BKE_gpencil_visible_stroke_iter(struct bGPdata *gpd,
gpIterCb layer_cb,
gpIterCb stroke_cb,
void *thunk);
void BKE_gpencil_visible_stroke_advanced_iter(struct ViewLayer *view_layer,
struct Object *ob,
gpIterCb layer_cb,
@@ -733,17 +418,6 @@ int BKE_gpencil_material_find_index_by_name_prefix(struct Object *ob, const char
void BKE_gpencil_blend_read_data(struct BlendDataReader *reader, struct bGPdata *gpd);
bool BKE_gpencil_can_avoid_full_copy_on_write(const struct Depsgraph *depsgraph,
struct bGPdata *gpd);
/**
* Update the geometry of the evaluated bGPdata.
* This function will:
* 1) Copy the original data over to the evaluated object.
* 2) Update the original pointers in the runtime structs.
*/
void BKE_gpencil_update_on_write(struct bGPdata *gpd_orig, struct bGPdata *gpd_eval);
#ifdef __cplusplus
}
#endif

View File

@@ -26,10 +26,6 @@ typedef void (*GreasePencilIDWalkFunc)(void *user_data,
struct Object *ob,
struct ID **idpoin,
int cb_flag);
typedef void (*GreasePencilTexWalkFunc)(void *user_data,
struct Object *ob,
struct GpencilModifierData *md,
const char *propname);
/**
* Free grease pencil modifier data

View File

@@ -102,31 +102,6 @@ void BKE_gpencil_traverse_update_cache(GPencilUpdateCache *cache,
GPencilUpdateCacheTraverseSettings *ts,
void *user_data);
/**
* Tags an element (bGPdata, bGPDlayer, bGPDframe, or bGPDstroke) and all of its containing data to
* be updated in the next update-on-write operation.
*
* The function assumes that when a parameter is NULL all of the following parameters are NULL too.
* E.g. in order to tag a layer (gpl), the parameters would *have* to be (gpd, gpl, NULL, NULL).
*/
void BKE_gpencil_tag_full_update(struct bGPdata *gpd,
struct bGPDlayer *gpl,
struct bGPDframe *gpf,
struct bGPDstroke *gps);
/**
* Tags an element (bGPdata, bGPDlayer, bGPDframe, or bGPDstroke) to be updated in the next
* update-on-write operation. This function will not update any of the containing data, only the
* struct itself.
*
* The function assumes that when a parameter is NULL all of the following parameters are NULL too.
* E.g. in order to tag a layer (gpl), the parameters would *have* to be (gpd, gpl, NULL, NULL).
*/
void BKE_gpencil_tag_light_update(struct bGPdata *gpd,
struct bGPDlayer *gpl,
struct bGPDframe *gpf,
struct bGPDstroke *gps);
/**
* Frees the GPencilUpdateCache on the gpd->runtime. This will not free the data that the cache
* node might point to. It assumes that the cache does not own the data.

View File

@@ -100,18 +100,6 @@ struct ObjectRuntime {
*/
Mesh *editmesh_eval_cage = nullptr;
/**
* Original grease pencil bGPdata pointer, before object->data was changed to point
* to gpd_eval.
* Is assigned by dependency graph's copy-on-evaluation.
*/
bGPdata *gpd_orig = nullptr;
/**
* bGPdata structure created during object evaluation.
* It has all modifiers applied.
*/
bGPdata *gpd_eval = nullptr;
/**
* This is a mesh representation of corresponding object.
* It created when Python calls `object.to_mesh()`.

View File

@@ -44,415 +44,6 @@ extern "C" {
/** \name Convert to curve object
* \{ */
/* Helper: Check materials with same color. */
static int gpencil_check_same_material_color(Object *ob_gp,
const float color_stroke[4],
const float color_fill[4],
const bool do_stroke,
const bool do_fill,
Material **r_mat)
{
int index = -1;
Material *ma = nullptr;
*r_mat = nullptr;
float color_cu[4];
float hsv_stroke[4], hsv_fill[4];
copy_v4_v4(color_cu, color_stroke);
zero_v3(hsv_stroke);
rgb_to_hsv_v(color_cu, hsv_stroke);
hsv_stroke[3] = color_stroke[3];
copy_v4_v4(color_cu, color_fill);
zero_v3(hsv_fill);
rgb_to_hsv_v(color_cu, hsv_fill);
hsv_fill[3] = color_fill[3];
bool match_stroke = false;
bool match_fill = false;
for (int i = 1; i <= ob_gp->totcol; i++) {
ma = BKE_object_material_get(ob_gp, i);
MaterialGPencilStyle *gp_style = ma->gp_style;
const bool fill = (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID);
const bool stroke = (gp_style->fill_style == GP_MATERIAL_STROKE_STYLE_SOLID);
if (do_fill && !fill) {
continue;
}
if (do_stroke && !stroke) {
continue;
}
/* Check color with small tolerance (better result in HSV). */
float hsv2[4];
if (do_fill) {
zero_v3(hsv2);
rgb_to_hsv_v(gp_style->fill_rgba, hsv2);
hsv2[3] = gp_style->fill_rgba[3];
if (compare_v4v4(hsv_fill, hsv2, 0.01f)) {
*r_mat = ma;
index = i - 1;
match_fill = true;
}
}
else {
match_fill = true;
}
if (do_stroke) {
zero_v3(hsv2);
rgb_to_hsv_v(gp_style->stroke_rgba, hsv2);
hsv2[3] = gp_style->stroke_rgba[3];
if (compare_v4v4(hsv_stroke, hsv2, 0.01f)) {
*r_mat = ma;
index = i - 1;
match_stroke = true;
}
}
else {
match_stroke = true;
}
/* If match, don't look for more. */
if (match_stroke || match_fill) {
break;
}
}
if (!match_stroke || !match_fill) {
*r_mat = nullptr;
index = -1;
}
return index;
}
/* Helper: Add gpencil material using curve material as base. */
static Material *gpencil_add_from_curve_material(Main *bmain,
Object *ob_gp,
const float stroke_color[4],
const float fill_color[4],
const bool stroke,
const bool fill,
int *r_index)
{
Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, "Material", r_index);
MaterialGPencilStyle *gp_style = mat_gp->gp_style;
/* Stroke color. */
if (stroke) {
copy_v4_v4(mat_gp->gp_style->stroke_rgba, stroke_color);
gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
}
/* Fill color. */
if (fill) {
copy_v4_v4(mat_gp->gp_style->fill_rgba, fill_color);
gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
/* Check at least one is enabled. */
if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0))
{
gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
}
return mat_gp;
}
/* Helper: Create new stroke section. */
static void gpencil_add_new_points(bGPDstroke *gps,
const float *coord_array,
const float pressure_start,
const float pressure_end,
const int init,
const int totpoints,
const float init_co[3],
const bool last)
{
BLI_assert(totpoints > 0);
const float step = 1.0f / (float(totpoints) - 1.0f);
float factor = 0.0f;
for (int i = 0; i < totpoints; i++) {
bGPDspoint *pt = &gps->points[i + init];
copy_v3_v3(&pt->x, &coord_array[3 * i]);
/* Be sure the last point is not on top of the first point of the curve or
* the close of the stroke will produce glitches. */
if ((last) && (i > 0) && (i == totpoints - 1)) {
float dist = len_v3v3(init_co, &pt->x);
if (dist < 0.1f) {
/* Interpolate between previous point and current to back slightly. */
bGPDspoint *pt_prev = &gps->points[i + init - 1];
interp_v3_v3v3(&pt->x, &pt_prev->x, &pt->x, 0.95f);
}
}
pt->strength = 1.0f;
pt->pressure = interpf(pressure_end, pressure_start, factor);
factor += step;
}
}
/* Helper: Get the first collection that includes the object. */
static Collection *gpencil_get_parent_collection(Scene *scene, Object *ob)
{
Collection *mycol = nullptr;
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
if ((mycol == nullptr) && (cob->ob == ob)) {
mycol = collection;
}
}
}
FOREACH_SCENE_COLLECTION_END;
return mycol;
}
static int gpencil_get_stroke_material_fromcurve(
Main *bmain, Object *ob_gp, Object *ob_cu, bool *r_do_stroke, bool *r_do_fill)
{
Curve *cu = (Curve *)ob_cu->data;
Material *mat_gp = nullptr;
Material *mat_curve_stroke = nullptr;
Material *mat_curve_fill = nullptr;
float color_stroke[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float color_fill[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* If the curve has 2 materials, the first is considered as Fill and the second as Stroke.
* If the has only one material, if the name contains "_stroke",
* it's used as a stroke, otherwise as fill. */
if (ob_cu->totcol >= 2) {
*r_do_stroke = true;
*r_do_fill = true;
mat_curve_fill = BKE_object_material_get(ob_cu, 1);
mat_curve_stroke = BKE_object_material_get(ob_cu, 2);
}
else if (ob_cu->totcol == 1) {
mat_curve_stroke = BKE_object_material_get(ob_cu, 1);
if ((mat_curve_stroke) && (strstr(mat_curve_stroke->id.name, "_stroke") != nullptr)) {
*r_do_stroke = true;
*r_do_fill = false;
mat_curve_fill = nullptr;
}
else {
*r_do_stroke = false;
*r_do_fill = true;
/* Invert materials. */
mat_curve_fill = mat_curve_stroke;
mat_curve_stroke = nullptr;
}
}
else {
/* No materials in the curve. */
*r_do_fill = false;
return -1;
}
if (mat_curve_stroke) {
copy_v4_v4(color_stroke, &mat_curve_stroke->r);
}
if (mat_curve_fill) {
copy_v4_v4(color_fill, &mat_curve_fill->r);
}
int index = gpencil_check_same_material_color(
ob_gp, color_stroke, color_fill, *r_do_stroke, *r_do_fill, &mat_gp);
if ((ob_gp->totcol < index) || (index < 0)) {
mat_gp = gpencil_add_from_curve_material(
bmain, ob_gp, color_stroke, color_fill, *r_do_stroke, *r_do_fill, &index);
}
/* Set fill and stroke depending of curve type (3D or 2D). */
if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) {
mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;
}
else {
mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
return index;
}
/* Helper: Convert one spline to grease pencil stroke. */
static void gpencil_convert_spline(Main *bmain,
Object *ob_gp,
Object *ob_cu,
const float scale_thickness,
const float sample,
bGPDframe *gpf,
Nurb *nu)
{
bGPdata *gpd = (bGPdata *)ob_gp->data;
bool cyclic = true;
/* Create Stroke. */
bGPDstroke *gps = static_cast<bGPDstroke *>(MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"));
gps->thickness = 1.0f;
gps->fill_opacity_fac = 1.0f;
gps->hardness = 1.0f;
gps->uv_scale = 1.0f;
ARRAY_SET_ITEMS(gps->aspect_ratio, 1.0f, 1.0f);
ARRAY_SET_ITEMS(gps->caps, GP_STROKE_CAP_ROUND, GP_STROKE_CAP_ROUND);
gps->inittime = 0.0f;
gps->flag &= ~GP_STROKE_SELECT;
gps->flag |= GP_STROKE_3DSPACE;
gps->mat_nr = 0;
/* Count total points
* The total of points must consider that last point of each segment is equal to the first
* point of next segment.
*/
int totpoints = 0;
int segments = 0;
int resolu = nu->resolu + 1;
segments = nu->pntsu;
if ((nu->flagu & CU_NURB_CYCLIC) == 0) {
segments--;
cyclic = false;
}
totpoints = (resolu * segments) - (segments - 1);
/* Materials
* Notice: The color of the material is the color of viewport and not the final shader color.
*/
bool do_stroke, do_fill;
int index = gpencil_get_stroke_material_fromcurve(bmain, ob_gp, ob_cu, &do_stroke, &do_fill);
CLAMP_MIN(index, 0);
/* Assign material index to stroke. */
gps->mat_nr = index;
/* Add stroke to frame. */
BLI_addtail(&gpf->strokes, gps);
float init_co[3];
switch (nu->type) {
case CU_POLY: {
/* Allocate memory for storage points. */
gps->totpoints = nu->pntsu;
gps->points = static_cast<bGPDspoint *>(
MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"));
/* Increase thickness for this type. */
gps->thickness = 10.0f;
/* Get all curve points */
for (int s = 0; s < gps->totpoints; s++) {
BPoint *bp = &nu->bp[s];
bGPDspoint *pt = &gps->points[s];
copy_v3_v3(&pt->x, bp->vec);
pt->pressure = bp->radius;
pt->strength = 1.0f;
}
break;
}
case CU_BEZIER: {
/* Allocate memory for storage points. */
gps->totpoints = totpoints;
gps->points = static_cast<bGPDspoint *>(
MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"));
int init = 0;
resolu = nu->resolu + 1;
segments = nu->pntsu;
if ((nu->flagu & CU_NURB_CYCLIC) == 0) {
segments--;
}
/* Get all interpolated curve points of Bezier. */
for (int s = 0; s < segments; s++) {
int inext = (s + 1) % nu->pntsu;
BezTriple *prevbezt = &nu->bezt[s];
BezTriple *bezt = &nu->bezt[inext];
bool last = bool(s == segments - 1);
float *coord_array = static_cast<float *>(
MEM_callocN(sizeof(float[3]) * resolu, __func__));
for (int j = 0; j < 3; j++) {
BKE_curve_forward_diff_bezier(prevbezt->vec[1][j],
prevbezt->vec[2][j],
bezt->vec[0][j],
bezt->vec[1][j],
coord_array + j,
resolu - 1,
sizeof(float[3]));
}
/* Save first point coordinates. */
if (s == 0) {
copy_v3_v3(init_co, &coord_array[0]);
}
/* Add points to the stroke */
float radius_start = prevbezt->radius * scale_thickness;
float radius_end = bezt->radius * scale_thickness;
gpencil_add_new_points(
gps, coord_array, radius_start, radius_end, init, resolu, init_co, last);
/* Free memory. */
MEM_freeN(coord_array);
/* As the last point of segment is the first point of next segment, back one array
* element to avoid duplicated points on the same location.
*/
init += resolu - 1;
}
break;
}
case CU_NURBS: {
if (nu->pntsv == 1) {
int nurb_points;
if (nu->flagu & CU_NURB_CYCLIC) {
resolu++;
nurb_points = nu->pntsu * resolu;
}
else {
nurb_points = (nu->pntsu - 1) * resolu;
}
/* Get all curve points. */
float *coord_array = static_cast<float *>(
MEM_callocN(sizeof(float[3]) * nurb_points, __func__));
BKE_nurb_makeCurve(nu, coord_array, nullptr, nullptr, nullptr, resolu, sizeof(float[3]));
/* Allocate memory for storage points. */
gps->totpoints = nurb_points;
gps->points = static_cast<bGPDspoint *>(
MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"));
/* Add points. */
gpencil_add_new_points(gps, coord_array, 1.0f, 1.0f, 0, gps->totpoints, init_co, false);
MEM_freeN(coord_array);
}
break;
}
default: {
break;
}
}
/* Cyclic curve, close stroke. */
if (cyclic) {
BKE_gpencil_stroke_close(gps);
}
if (sample > 0.0f) {
BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0);
}
/* Recalc fill geometry. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
static void gpencil_editstroke_deselect_all(bGPDcurve *gpc)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
@@ -464,80 +55,6 @@ static void gpencil_editstroke_deselect_all(bGPDcurve *gpc)
gpc->flag &= ~GP_CURVE_SELECT;
}
void BKE_gpencil_convert_curve(Main *bmain,
Scene *scene,
Object *ob_gp,
Object *ob_cu,
const bool use_collections,
const float scale_thickness,
const float sample)
{
if (ELEM(nullptr, ob_gp, ob_cu) || (ob_gp->type != OB_GPENCIL_LEGACY) ||
(ob_gp->data == nullptr))
{
return;
}
Curve *cu = (Curve *)ob_cu->data;
bGPdata *gpd = (bGPdata *)ob_gp->data;
bGPDlayer *gpl = nullptr;
/* If the curve is empty, cancel. */
if (cu->nurb.first == nullptr) {
return;
}
/* Check if there is an active layer. */
if (use_collections) {
Collection *collection = gpencil_get_parent_collection(scene, ob_cu);
if (collection != nullptr) {
gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2);
if (gpl == nullptr) {
gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true, false);
}
}
}
if (gpl == nullptr) {
gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl == nullptr) {
gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
}
/* Check if there is an active frame and add if needed. */
bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_COPY);
/* Read all splines of the curve and create a stroke for each. */
LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
gpencil_convert_spline(bmain, ob_gp, ob_cu, scale_thickness, sample, gpf, nu);
}
/* Merge any similar material. */
int removed = 0;
BKE_gpencil_merge_materials(ob_gp, 0.001f, 0.001f, 0.001f, &removed);
/* Remove any unused slot. */
int actcol = ob_gp->actcol;
for (int slot = 1; slot <= ob_gp->totcol; slot++) {
while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp, slot)) {
ob_gp->actcol = slot;
BKE_object_material_slot_remove(bmain, ob_gp);
if (actcol >= slot) {
actcol--;
}
}
}
ob_gp->actcol = actcol;
/* Tag for recalculation */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
DEG_id_tag_update(&ob_gp->id, ID_RECALC_GEOMETRY);
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -729,28 +246,6 @@ bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps,
return editcurve;
}
void BKE_gpencil_stroke_editcurve_update(bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps)
{
if (gps == nullptr || gps->totpoints < 0) {
return;
}
if (gps->editcurve != nullptr) {
BKE_gpencil_free_stroke_editcurve(gps);
}
float defaultpixsize = 1000.0f / gpd->pixfactor;
float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_generate(
gps, gpd->curve_edit_threshold, gpd->curve_edit_corner_angle, stroke_radius);
if (editcurve == nullptr) {
return;
}
gps->editcurve = editcurve;
}
void BKE_gpencil_editcurve_stroke_sync_selection(bGPdata * /*gpd*/,
bGPDstroke *gps,
bGPDcurve *gpc)
@@ -777,346 +272,6 @@ void BKE_gpencil_editcurve_stroke_sync_selection(bGPdata * /*gpd*/,
}
}
void BKE_gpencil_stroke_editcurve_sync_selection(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc)
{
if (gpc->flag & GP_CURVE_SELECT) {
gps->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, gps);
for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
bGPDspoint *pt = &gps->points[gpc_pt->point_index];
bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + 1];
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
pt->flag |= GP_SPOINT_SELECT;
if (gpc_pt_next->flag & GP_CURVE_POINT_SELECT) {
/* select all the points after */
for (int j = gpc_pt->point_index + 1; j < gpc_pt_next->point_index; j++) {
bGPDspoint *pt_next = &gps->points[j];
pt_next->flag |= GP_SPOINT_SELECT;
}
}
}
else {
pt->flag &= ~GP_SPOINT_SELECT;
/* deselect all points after */
for (int j = gpc_pt->point_index + 1; j < gpc_pt_next->point_index; j++) {
bGPDspoint *pt_next = &gps->points[j];
pt_next->flag &= ~GP_SPOINT_SELECT;
}
}
}
bGPDcurve_point *gpc_first = &gpc->curve_points[0];
bGPDcurve_point *gpc_last = &gpc->curve_points[gpc->tot_curve_points - 1];
bGPDspoint *last_pt = &gps->points[gpc_last->point_index];
if (gpc_last->flag & GP_CURVE_POINT_SELECT) {
last_pt->flag |= GP_SPOINT_SELECT;
}
else {
last_pt->flag &= ~GP_SPOINT_SELECT;
}
if (gps->flag & GP_STROKE_CYCLIC) {
if (gpc_first->flag & GP_CURVE_POINT_SELECT && gpc_last->flag & GP_CURVE_POINT_SELECT) {
for (int i = gpc_last->point_index + 1; i < gps->totpoints; i++) {
bGPDspoint *pt_next = &gps->points[i];
pt_next->flag |= GP_SPOINT_SELECT;
}
}
else {
for (int i = gpc_last->point_index + 1; i < gps->totpoints; i++) {
bGPDspoint *pt_next = &gps->points[i];
pt_next->flag &= ~GP_SPOINT_SELECT;
}
}
}
}
else {
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_SELECT;
}
}
}
static void gpencil_interpolate_fl_from_to(
float from, float to, float *point_offset, int it, int stride)
{
/* smooth interpolation */
float *r = point_offset;
for (int i = 0; i <= it; i++) {
float fac = float(i) / float(it);
fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; /* Smooth. */
*r = interpf(to, from, fac);
r = static_cast<float *>(POINTER_OFFSET(r, stride));
}
}
static void gpencil_interpolate_v4_from_to(
float from[4], float to[4], float *point_offset, int it, int stride)
{
/* smooth interpolation */
float *r = point_offset;
for (int i = 0; i <= it; i++) {
float fac = float(i) / float(it);
fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; /* Smooth. */
interp_v4_v4v4(r, from, to, fac);
r = static_cast<float *>(POINTER_OFFSET(r, stride));
}
}
static float gpencil_approximate_curve_segment_arclength(bGPDcurve_point *cpt_start,
bGPDcurve_point *cpt_end)
{
BezTriple *bezt_start = &cpt_start->bezt;
BezTriple *bezt_end = &cpt_end->bezt;
float chord_len = len_v3v3(bezt_start->vec[1], bezt_end->vec[1]);
float net_len = len_v3v3(bezt_start->vec[1], bezt_start->vec[2]);
net_len += len_v3v3(bezt_start->vec[2], bezt_end->vec[0]);
net_len += len_v3v3(bezt_end->vec[0], bezt_end->vec[1]);
return (chord_len + net_len) / 2.0f;
}
static void gpencil_calculate_stroke_points_curve_segment(
bGPDcurve_point *cpt, bGPDcurve_point *cpt_next, float *points_offset, int resolu, int stride)
{
/* sample points on all 3 axis between two curve points */
for (uint axis = 0; axis < 3; axis++) {
BKE_curve_forward_diff_bezier(
cpt->bezt.vec[1][axis],
cpt->bezt.vec[2][axis],
cpt_next->bezt.vec[0][axis],
cpt_next->bezt.vec[1][axis],
static_cast<float *>(POINTER_OFFSET(points_offset, sizeof(float) * axis)),
int(resolu),
stride);
}
/* interpolate other attributes */
gpencil_interpolate_fl_from_to(
cpt->pressure,
cpt_next->pressure,
static_cast<float *>(POINTER_OFFSET(points_offset, sizeof(float) * 3)),
resolu,
stride);
gpencil_interpolate_fl_from_to(
cpt->strength,
cpt_next->strength,
static_cast<float *>(POINTER_OFFSET(points_offset, sizeof(float) * 4)),
resolu,
stride);
gpencil_interpolate_v4_from_to(
cpt->vert_color,
cpt_next->vert_color,
static_cast<float *>(POINTER_OFFSET(points_offset, sizeof(float) * 5)),
resolu,
stride);
}
static float *gpencil_stroke_points_from_editcurve_adaptive_resolu(
bGPDcurve_point *curve_point_array,
int curve_point_array_len,
int resolution,
bool is_cyclic,
int *r_points_len)
{
/* One stride contains: `x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor`. */
const uint stride = sizeof(float[9]);
const uint cpt_last = curve_point_array_len - 1;
const uint num_segments = (is_cyclic) ? curve_point_array_len : curve_point_array_len - 1;
int *segment_point_lengths = static_cast<int *>(
MEM_callocN(sizeof(int) * num_segments, __func__));
uint points_len = 1;
for (int i = 0; i < cpt_last; i++) {
bGPDcurve_point *cpt = &curve_point_array[i];
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
float arclen = gpencil_approximate_curve_segment_arclength(cpt, cpt_next);
int segment_resolu = int(floorf(arclen * resolution));
CLAMP_MIN(segment_resolu, 1);
segment_point_lengths[i] = segment_resolu;
points_len += segment_resolu;
}
if (is_cyclic) {
bGPDcurve_point *cpt = &curve_point_array[cpt_last];
bGPDcurve_point *cpt_next = &curve_point_array[0];
float arclen = gpencil_approximate_curve_segment_arclength(cpt, cpt_next);
int segment_resolu = int(floorf(arclen * resolution));
CLAMP_MIN(segment_resolu, 1);
segment_point_lengths[cpt_last] = segment_resolu;
points_len += segment_resolu;
}
float(*r_points)[9] = static_cast<float(*)[9]>(
MEM_callocN((stride * points_len * (is_cyclic ? 2 : 1)), __func__));
float *points_offset = &r_points[0][0];
int point_index = 0;
for (int i = 0; i < cpt_last; i++) {
bGPDcurve_point *cpt_curr = &curve_point_array[i];
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
int segment_resolu = segment_point_lengths[i];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, segment_resolu, stride);
/* update the index */
cpt_curr->point_index = point_index;
point_index += segment_resolu;
points_offset = static_cast<float *>(POINTER_OFFSET(points_offset, segment_resolu * stride));
}
bGPDcurve_point *cpt_curr = &curve_point_array[cpt_last];
cpt_curr->point_index = point_index;
if (is_cyclic) {
bGPDcurve_point *cpt_next = &curve_point_array[0];
int segment_resolu = segment_point_lengths[cpt_last];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, segment_resolu, stride);
}
MEM_freeN(segment_point_lengths);
*r_points_len = points_len;
return (float *)r_points;
}
/**
* Helper: calculate the points on a curve with a fixed resolution.
*/
static float *gpencil_stroke_points_from_editcurve_fixed_resolu(bGPDcurve_point *curve_point_array,
int curve_point_array_len,
int resolution,
bool is_cyclic,
int *r_points_len)
{
/* One stride contains: `x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor`. */
const uint stride = sizeof(float[9]);
const uint array_last = curve_point_array_len - 1;
const uint resolu_stride = resolution * stride;
const uint points_len = BKE_curve_calc_coords_axis_len(
curve_point_array_len, resolution, is_cyclic, false);
float(*r_points)[9] = static_cast<float(*)[9]>(
MEM_callocN((stride * points_len * (is_cyclic ? 2 : 1)), __func__));
float *points_offset = &r_points[0][0];
for (uint i = 0; i < array_last; i++) {
bGPDcurve_point *cpt_curr = &curve_point_array[i];
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, resolution, stride);
/* update the index */
cpt_curr->point_index = i * resolution;
points_offset = static_cast<float *>(POINTER_OFFSET(points_offset, resolu_stride));
}
bGPDcurve_point *cpt_curr = &curve_point_array[array_last];
cpt_curr->point_index = array_last * resolution;
if (is_cyclic) {
bGPDcurve_point *cpt_next = &curve_point_array[0];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, resolution, stride);
}
*r_points_len = points_len;
return (float *)r_points;
}
void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
const uint resolution,
const bool adaptive)
{
if (gps == nullptr || gps->editcurve == nullptr) {
return;
}
bGPDcurve *editcurve = gps->editcurve;
bGPDcurve_point *curve_point_array = editcurve->curve_points;
int curve_point_array_len = editcurve->tot_curve_points;
if (curve_point_array_len == 0) {
return;
}
/* Handle case for single curve point. */
if (curve_point_array_len == 1) {
bGPDcurve_point *cpt = &curve_point_array[0];
/* resize stroke point array */
gps->totpoints = 1;
gps->points = static_cast<bGPDspoint *>(
MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints));
if (gps->dvert != nullptr) {
gps->dvert = static_cast<MDeformVert *>(
MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints));
}
bGPDspoint *pt = &gps->points[0];
copy_v3_v3(&pt->x, cpt->bezt.vec[1]);
pt->pressure = cpt->pressure;
pt->strength = cpt->strength;
copy_v4_v4(pt->vert_color, cpt->vert_color);
/* deselect */
pt->flag &= ~GP_SPOINT_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
return;
}
bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
int points_len = 0;
float(*points)[9] = nullptr;
if (adaptive) {
points = (float(*)[9])gpencil_stroke_points_from_editcurve_adaptive_resolu(
curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
}
else {
points = (float(*)[9])gpencil_stroke_points_from_editcurve_fixed_resolu(
curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
}
if (points == nullptr || points_len == 0) {
return;
}
/* resize stroke point array */
gps->totpoints = points_len;
gps->points = static_cast<bGPDspoint *>(
MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints));
if (gps->dvert != nullptr) {
gps->dvert = static_cast<MDeformVert *>(
MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints));
}
/* write new data to stroke point array */
for (int i = 0; i < points_len; i++) {
bGPDspoint *pt = &gps->points[i];
copy_v3_v3(&pt->x, &points[i][0]);
pt->pressure = points[i][3];
pt->strength = points[i][4];
copy_v4_v4(pt->vert_color, &points[i][5]);
/* deselect points */
pt->flag &= ~GP_SPOINT_SELECT;
}
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
/* free temp data */
MEM_freeN(points);
}
void BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps)
{
if (gps == nullptr || gps->editcurve == nullptr) {
@@ -1325,68 +480,4 @@ void BKE_gpencil_editcurve_subdivide(bGPDstroke *gps, const int cuts)
}
}
void BKE_gpencil_strokes_selected_update_editcurve(bGPdata *gpd)
{
const bool is_multiedit = bool(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd));
/* For all selected strokes, update edit curve. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (!BKE_gpencil_layer_is_editable(gpl)) {
continue;
}
bGPDframe *init_gpf = static_cast<bGPDframe *>((is_multiedit) ? gpl->frames.first :
gpl->actframe);
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip deselected stroke */
if (!(gps->flag & GP_STROKE_SELECT)) {
continue;
}
/* Generate the curve if there is none or the stroke was changed */
if (gps->editcurve == nullptr) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Continue if curve could not be generated. */
if (gps->editcurve == nullptr) {
continue;
}
}
else if (gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
}
/* Update the selection from the stroke to the curve. */
BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
}
}
void BKE_gpencil_strokes_selected_sync_selection_editcurve(bGPdata *gpd)
{
const bool is_multiedit = bool(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd));
/* Sync selection for all strokes with editcurve. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (!BKE_gpencil_layer_is_editable(gpl)) {
continue;
}
bGPDframe *init_gpf = static_cast<bGPDframe *>((is_multiedit) ? gpl->frames.first :
gpl->actframe);
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDcurve *gpc = gps->editcurve;
if (gpc != nullptr) {
/* Update the selection of every stroke that has an editcurve */
BKE_gpencil_stroke_editcurve_sync_selection(gpd, gps, gpc);
}
}
}
}
}
}
/** \} */

View File

@@ -122,833 +122,6 @@ void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Sample
* \{ */
static int stroke_march_next_point(const bGPDstroke *gps,
const int index_next_pt,
const float *current,
const float dist,
float *result,
float *pressure,
float *strength,
float *vert_color,
float *uv_fac,
float *uv_fill,
float *uv_rot,
float *ratio_result,
int *index_from,
int *index_to)
{
float remaining_till_next = 0.0f;
float remaining_march = dist;
float step_start[3];
float point[3];
int next_point_index = index_next_pt;
bGPDspoint *pt = nullptr;
if (next_point_index == gps->totpoints) {
next_point_index = 0;
}
copy_v3_v3(step_start, current);
pt = &gps->points[next_point_index];
copy_v3_v3(point, &pt->x);
remaining_till_next = len_v3v3(point, step_start);
while (remaining_till_next < remaining_march && next_point_index) {
remaining_march -= remaining_till_next;
pt = &gps->points[next_point_index];
if (pt->flag & GP_SPOINT_TEMP_TAG) {
pt = &gps->points[next_point_index];
copy_v3_v3(result, &pt->x);
*pressure = gps->points[next_point_index].pressure;
*strength = gps->points[next_point_index].strength;
memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
*index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
*index_to = next_point_index;
*ratio_result = 1.0f;
next_point_index++;
return next_point_index == 0 ? gps->totpoints : next_point_index;
}
next_point_index++;
copy_v3_v3(point, &pt->x);
copy_v3_v3(step_start, point);
if (!(next_point_index < gps->totpoints)) {
if (gps->flag & GP_STROKE_CYCLIC) {
next_point_index = 0;
}
else {
next_point_index = gps->totpoints - 1;
remaining_till_next = 0;
break;
}
}
pt = &gps->points[next_point_index];
copy_v3_v3(point, &pt->x);
remaining_till_next = len_v3v3(point, step_start);
}
if (remaining_till_next < remaining_march) {
pt = &gps->points[next_point_index];
copy_v3_v3(result, &pt->x);
*pressure = gps->points[next_point_index].pressure;
*strength = gps->points[next_point_index].strength;
memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
*index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
*index_to = next_point_index;
*ratio_result = 1.0f;
return 0;
}
*index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
*index_to = next_point_index;
float ratio = remaining_march / remaining_till_next;
interp_v3_v3v3(result, step_start, point, ratio);
*ratio_result = ratio;
float d1 = len_v3v3(result, &gps->points[*index_from].x);
float d2 = len_v3v3(result, &gps->points[next_point_index].x);
float vratio = d1 / (d1 + d2);
*pressure = interpf(
gps->points[next_point_index].pressure, gps->points[*index_from].pressure, vratio);
*strength = interpf(
gps->points[next_point_index].strength, gps->points[*index_from].strength, vratio);
*uv_fac = interpf(gps->points[next_point_index].uv_fac, gps->points[*index_from].uv_fac, vratio);
*uv_rot = interpf(gps->points[next_point_index].uv_rot, gps->points[*index_from].uv_rot, vratio);
interp_v2_v2v2(
uv_fill, gps->points[*index_from].uv_fill, gps->points[next_point_index].uv_fill, vratio);
interp_v4_v4v4(vert_color,
gps->points[*index_from].vert_color,
gps->points[next_point_index].vert_color,
vratio);
return next_point_index == 0 ? gps->totpoints : next_point_index;
}
static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
const int index_next_pt,
const float *current,
const float dist,
const float sharp_threshold,
float *result)
{
float remaining_till_next = 0.0f;
float remaining_march = dist;
float step_start[3];
float point[3];
int next_point_index = index_next_pt;
bGPDspoint *pt = nullptr;
if (next_point_index == gps->totpoints) {
next_point_index = 0;
}
copy_v3_v3(step_start, current);
pt = &gps->points[next_point_index];
copy_v3_v3(point, &pt->x);
remaining_till_next = len_v3v3(point, step_start);
while (remaining_till_next < remaining_march && next_point_index) {
remaining_march -= remaining_till_next;
pt = &gps->points[next_point_index];
if (next_point_index < gps->totpoints - 1 &&
angle_v3v3v3(&gps->points[next_point_index - 1].x,
&gps->points[next_point_index].x,
&gps->points[next_point_index + 1].x) < sharp_threshold)
{
copy_v3_v3(result, &pt->x);
pt->flag |= GP_SPOINT_TEMP_TAG;
next_point_index++;
return next_point_index == 0 ? gps->totpoints : next_point_index;
}
next_point_index++;
copy_v3_v3(point, &pt->x);
copy_v3_v3(step_start, point);
if (!(next_point_index < gps->totpoints)) {
if (gps->flag & GP_STROKE_CYCLIC) {
next_point_index = 0;
}
else {
next_point_index = gps->totpoints - 1;
remaining_till_next = 0;
break;
}
}
pt = &gps->points[next_point_index];
copy_v3_v3(point, &pt->x);
remaining_till_next = len_v3v3(point, step_start);
}
if (remaining_till_next < remaining_march) {
pt = &gps->points[next_point_index];
copy_v3_v3(result, &pt->x);
/* Stroke marching only terminates here. */
return 0;
}
float ratio = remaining_march / remaining_till_next;
interp_v3_v3v3(result, step_start, point, ratio);
return next_point_index == 0 ? gps->totpoints : next_point_index;
}
static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold)
{
int point_count = 0;
float point[3];
int next_point_index = 1;
bGPDspoint *pt = nullptr;
pt = &gps->points[0];
copy_v3_v3(point, &pt->x);
point_count++;
/* Sharp points will be tagged by the stroke_march_next_point_no_interp() call below. */
for (int i = 0; i < gps->totpoints; i++) {
gps->points[i].flag &= (~GP_SPOINT_TEMP_TAG);
}
while ((next_point_index = stroke_march_next_point_no_interp(
gps, next_point_index, point, dist, sharp_threshold, point)) > -1)
{
point_count++;
if (next_point_index == 0) {
break; /* last point finished */
}
}
return point_count;
}
static void stroke_defvert_create_nr_list(MDeformVert *dv_list,
int count,
ListBase *result,
int *totweight)
{
LinkData *ld;
MDeformVert *dv;
MDeformWeight *dw;
int i, j;
int tw = 0;
for (i = 0; i < count; i++) {
dv = &dv_list[i];
/* find def_nr in list, if not exist, then create one */
for (j = 0; j < dv->totweight; j++) {
bool found = false;
dw = &dv->dw[j];
LISTBASE_FOREACH (LinkData *, ld, result) {
if (ld->data == POINTER_FROM_INT(dw->def_nr)) {
found = true;
break;
}
}
if (!found) {
ld = MEM_cnew<LinkData>("def_nr_item");
ld->data = POINTER_FROM_INT(dw->def_nr);
BLI_addtail(result, ld);
tw++;
}
}
}
*totweight = tw;
}
static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list)
{
int i, j;
MDeformVert *dst = (MDeformVert *)MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert");
for (i = 0; i < count; i++) {
dst[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * totweight,
"new_deformWeight");
dst[i].totweight = totweight;
j = 0;
/* re-assign deform groups */
LISTBASE_FOREACH (LinkData *, ld, def_nr_list) {
dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data);
j++;
}
}
return dst;
}
static void stroke_interpolate_deform_weights(
bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert)
{
const MDeformVert *vl = &gps->dvert[index_from];
const MDeformVert *vr = &gps->dvert[index_to];
for (int i = 0; i < vert->totweight; i++) {
float wl = BKE_defvert_find_weight(vl, vert->dw[i].def_nr);
float wr = BKE_defvert_find_weight(vr, vert->dw[i].def_nr);
vert->dw[i].weight = interpf(wr, wl, ratio);
}
}
bool BKE_gpencil_stroke_sample(bGPdata *gpd,
bGPDstroke *gps,
const float dist,
const bool select,
const float sharp_threshold)
{
bGPDspoint *pt = gps->points;
bGPDspoint *pt1 = nullptr;
bGPDspoint *pt2 = nullptr;
ListBase def_nr_list = {nullptr};
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
return false;
}
/* TODO: Implement feature point preservation. */
int count = stroke_march_count(gps, dist, sharp_threshold);
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
if (is_cyclic) {
count--;
}
bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count,
"gp_stroke_points_sampled");
MDeformVert *new_dv = nullptr;
int result_totweight;
if (gps->dvert != nullptr) {
stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight);
new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list);
}
int next_point_index = 1;
int i = 0;
float pressure, strength, ratio_result;
float uv_fac, uv_rot, uv_fill[2];
float vert_color[4];
int index_from, index_to;
float last_coord[3];
/* 1st point is always at the start */
pt1 = &gps->points[0];
copy_v3_v3(last_coord, &pt1->x);
pt2 = &new_pt[i];
copy_v3_v3(&pt2->x, last_coord);
new_pt[i].pressure = pt[0].pressure;
new_pt[i].strength = pt[0].strength;
copy_v3_v3(&pt2->x, last_coord);
new_pt[i].pressure = pt[0].pressure;
new_pt[i].strength = pt[0].strength;
new_pt[i].uv_fac = pt[0].uv_fac;
new_pt[i].uv_rot = pt[0].uv_rot;
copy_v2_v2(new_pt[i].uv_fill, pt[0].uv_fill);
copy_v4_v4(new_pt[i].vert_color, pt[0].vert_color);
if (select) {
new_pt[i].flag |= GP_SPOINT_SELECT;
}
i++;
if (new_dv) {
stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]);
}
/* The rest. */
while ((next_point_index = stroke_march_next_point(gps,
next_point_index,
last_coord,
dist,
last_coord,
&pressure,
&strength,
vert_color,
&uv_fac,
uv_fill,
&uv_rot,
&ratio_result,
&index_from,
&index_to)) > -1)
{
if (is_cyclic && next_point_index == 0) {
break; /* last point finished */
}
pt2 = &new_pt[i];
copy_v3_v3(&pt2->x, last_coord);
new_pt[i].pressure = pressure;
new_pt[i].strength = strength;
new_pt[i].uv_fac = uv_fac;
new_pt[i].uv_rot = uv_rot;
copy_v2_v2(new_pt[i].uv_fill, uv_fill);
memcpy(new_pt[i].vert_color, vert_color, sizeof(float[4]));
if (select) {
new_pt[i].flag |= GP_SPOINT_SELECT;
}
if (new_dv) {
stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]);
}
i++;
if (next_point_index == 0) {
break; /* last point finished */
}
}
gps->points = new_pt;
/* Free original vertex list. */
MEM_freeN(pt);
if (new_dv) {
/* Free original weight data. */
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
while (LinkData *ld = (LinkData *)BLI_pophead(&def_nr_list)) {
MEM_freeN(ld);
}
gps->dvert = new_dv;
}
BLI_assert(i == count);
gps->totpoints = i;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
return true;
}
/**
* Give extra stroke points before and after the original tip points.
* \param gps: Target stroke
* \param count_before: how many extra points to be added before a stroke
* \param count_after: how many extra points to be added after a stroke
*/
static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps,
const int count_before,
const int count_after)
{
bGPDspoint *pts = gps->points;
BLI_assert(count_before >= 0);
BLI_assert(count_after >= 0);
if (!count_before && !count_after) {
return false;
}
const int new_count = count_before + count_after + gps->totpoints;
bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__);
for (int i = 0; i < count_before; i++) {
new_pts[i] = blender::dna::shallow_copy(pts[0]);
}
memcpy(static_cast<void *>(&new_pts[count_before]), pts, sizeof(bGPDspoint) * gps->totpoints);
for (int i = new_count - count_after; i < new_count; i++) {
new_pts[i] = blender::dna::shallow_copy(pts[gps->totpoints - 1]);
}
if (gps->dvert) {
MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__);
for (int i = 0; i < new_count; i++) {
MDeformVert *dv = &gps->dvert[std::clamp(i - count_before, 0, gps->totpoints - 1)];
int inew = i;
new_dv[inew].flag = dv->flag;
new_dv[inew].totweight = dv->totweight;
new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
__func__);
memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight);
}
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
gps->dvert = new_dv;
}
MEM_freeN(gps->points);
gps->points = new_pts;
gps->totpoints = new_count;
return true;
}
bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
const float dist,
const float overshoot_fac,
const short mode,
const bool follow_curvature,
const int extra_point_count,
const float segment_influence,
const float max_angle,
const bool invert_curvature)
{
#define BOTH 0
#define START 1
#define END 2
const bool do_start = ELEM(mode, BOTH, START);
const bool do_end = ELEM(mode, BOTH, END);
float used_percent_length = overshoot_fac;
CLAMP(used_percent_length, 1e-4f, 1.0f);
if (!isfinite(used_percent_length)) {
/* #used_percent_length must always be finite, otherwise a segfault occurs.
* Since this function should never segfault, set #used_percent_length to a safe fallback. */
/* NOTE: This fallback is used if `gps->totpoints == 2`, see `MOD_gpencil_legacy_length.cc`. */
used_percent_length = 0.1f;
}
if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) {
return false;
}
/* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */
if (!follow_curvature || gps->totpoints <= 2) {
/* Not following curvature, just straight line. */
/* NOTE: #overshoot_point_param can not be zero. */
float overshoot_point_param = used_percent_length * (gps->totpoints - 1);
float result[3];
if (do_start) {
int index1 = floor(overshoot_point_param);
int index2 = ceil(overshoot_point_param);
interp_v3_v3v3(result,
&gps->points[index1].x,
&gps->points[index2].x,
fmodf(overshoot_point_param, 1.0f));
sub_v3_v3(result, &gps->points[0].x);
if (UNLIKELY(is_zero_v3(result))) {
sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x);
}
madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result));
}
if (do_end) {
int index1 = gps->totpoints - 1 - floor(overshoot_point_param);
int index2 = gps->totpoints - 1 - ceil(overshoot_point_param);
interp_v3_v3v3(result,
&gps->points[index1].x,
&gps->points[index2].x,
fmodf(overshoot_point_param, 1.0f));
sub_v3_v3(result, &gps->points[gps->totpoints - 1].x);
if (UNLIKELY(is_zero_v3(result))) {
sub_v3_v3v3(
result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x);
}
madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result));
}
return true;
}
/* Curvature calculation. */
/* First allocate the new stroke size. */
const int first_old_index = do_start ? extra_point_count : 0;
const int last_old_index = gps->totpoints - 1 + first_old_index;
const int orig_totpoints = gps->totpoints;
BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0);
/* The fractional amount of points to query when calculating the average curvature of the
* strokes. */
const float overshoot_parameter = used_percent_length * (orig_totpoints - 2);
int overshoot_pointcount = ceil(overshoot_parameter);
CLAMP(overshoot_pointcount, 1, orig_totpoints - 2);
/* Do for both sides without code duplication. */
float no[3], vec1[3], vec2[3], total_angle[3];
for (int k = 0; k < 2; k++) {
if ((k == 0 && !do_start) || (k == 1 && !do_end)) {
continue;
}
const int start_i = k == 0 ? first_old_index :
last_old_index; // first_old_index, last_old_index
const int dir_i = 1 - k * 2; // 1, -1
sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x);
zero_v3(total_angle);
float segment_length = normalize_v3(vec1);
float overshoot_length = 0.0f;
/* Accumulate rotation angle and length. */
int j = 0;
for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) {
/* Don't fully add last segment to get continuity in overshoot_fac. */
float fac = fmin(overshoot_parameter - j, 1.0f);
/* Read segments. */
copy_v3_v3(vec2, vec1);
sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x);
const float len = normalize_v3(vec1);
float angle = angle_normalized_v3v3(vec1, vec2) * fac;
/* Add half of both adjacent legs of the current angle. */
const float added_len = (segment_length + len) * 0.5f * fac;
overshoot_length += added_len;
segment_length = len;
if (angle > max_angle) {
continue;
}
if (angle > M_PI * 0.995f) {
continue;
}
angle *= powf(added_len, segment_influence);
cross_v3_v3v3(no, vec1, vec2);
normalize_v3_length(no, angle);
add_v3_v3(total_angle, no);
}
if (UNLIKELY(overshoot_length == 0.0f)) {
/* Don't do a proper extension if the used points are all in the same position. */
continue;
}
sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x);
/* In general curvature = 1/radius. For the case without the
* weights introduced by #segment_influence, the calculation is:
* `curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length` */
float curvature = normalize_v3(total_angle) / overshoot_length;
/* Compensate for the weights powf(added_len, segment_influence). */
curvature /= powf(overshoot_length / fminf(overshoot_parameter, float(j)), segment_influence);
if (invert_curvature) {
curvature = -curvature;
}
const float angle_step = curvature * dist / extra_point_count;
float step_length = dist / extra_point_count;
if (fabsf(angle_step) > FLT_EPSILON) {
/* Make a direct step length from the assigned arc step length. */
step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f);
}
else {
zero_v3(total_angle);
}
const float prev_length = normalize_v3_length(vec1, step_length);
/* Build rotation matrix here to get best performance. */
float rot[3][3];
float q[4];
axis_angle_to_quat(q, total_angle, angle_step);
quat_to_mat3(rot, q);
/* Rotate the starting direction to account for change in edge lengths. */
axis_angle_to_quat(q,
total_angle,
fmaxf(0.0f, 1.0f - fabs(segment_influence)) *
(curvature * prev_length - angle_step) / 2.0f);
mul_qt_v3(q, vec1);
/* Now iteratively accumulate the segments with a rotating added direction. */
for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) {
mul_v3_m3v3(vec1, rot, vec1);
add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x);
}
}
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Trim
* \{ */
bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps,
const int index_from,
const int index_to,
const bool keep_point)
{
bGPDspoint *pt = gps->points, *new_pt;
MDeformVert *dv, *new_dv;
const int new_count = index_to - index_from + 1;
if (new_count >= gps->totpoints) {
return false;
}
if ((!keep_point) && (new_count == 1)) {
if (gps->dvert) {
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
}
MEM_freeN(gps->points);
gps->points = nullptr;
gps->dvert = nullptr;
gps->totpoints = 0;
return false;
}
new_pt = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
memcpy(static_cast<void *>(new_pt), &pt[index_from], sizeof(bGPDspoint) * new_count);
if (gps->dvert) {
new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
"gp_stroke_dverts_trimmed");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + index_from];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw_trimmed");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
}
}
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
gps->dvert = new_dv;
}
MEM_freeN(gps->points);
gps->points = new_pt;
gps->totpoints = new_count;
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Split
* \{ */
bool BKE_gpencil_stroke_split(bGPdata *gpd,
bGPDframe *gpf,
bGPDstroke *gps,
const int before_index,
bGPDstroke **remaining_gps)
{
bGPDstroke *new_gps;
bGPDspoint *pt = gps->points, *new_pt;
MDeformVert *dv, *new_dv;
if (before_index >= gps->totpoints || before_index == 0) {
return false;
}
const int new_count = gps->totpoints - before_index;
const int old_count = before_index;
/* Handle remaining segments first. */
new_gps = BKE_gpencil_stroke_add_existing_style(
gpf, gps, gps->mat_nr, new_count, gps->thickness);
new_pt = new_gps->points; /* Allocated from above. */
memcpy(static_cast<void *>(new_pt), &pt[before_index], sizeof(bGPDspoint) * new_count);
if (gps->dvert) {
new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
"gp_stroke_dverts_remaining(MDeformVert)");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + before_index];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw_remaining(MDeformWeight)");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
}
}
new_gps->dvert = new_dv;
}
(*remaining_gps) = new_gps;
/* Trim the original stroke into a shorter one.
* Keep the end point. */
BKE_gpencil_stroke_trim_points(gps, 0, old_count, false);
BKE_gpencil_stroke_geometry_update(gpd, gps);
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Shrink
* \{ */
bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
{
#define START 1
#define END 2
bGPDspoint *pt = gps->points, *second_last;
int i;
if (gps->totpoints < 2) {
if (gps->totpoints == 1) {
second_last = &pt[1];
if (len_v3v3(&second_last->x, &pt->x) < dist) {
BKE_gpencil_stroke_trim_points(gps, 0, 0, false);
return true;
}
}
return false;
}
second_last = &pt[gps->totpoints - 2];
float len;
float len1, cut_len1;
float len2, cut_len2;
len1 = len2 = cut_len1 = cut_len2 = 0.0f;
int index_start = 0;
int index_end = 0;
if (mode == START) {
i = 0;
index_end = gps->totpoints - 1;
while (len1 < dist && gps->totpoints > i + 1) {
len = len_v3v3(&pt[i].x, &pt[i + 1].x);
len1 += len;
cut_len1 = len1 - dist;
i++;
}
index_start = i - 1;
interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len);
}
if (mode == END) {
index_start = 0;
i = 2;
while (len2 < dist && gps->totpoints >= i) {
second_last = &pt[gps->totpoints - i];
len = len_v3v3(&second_last[1].x, &second_last->x);
len2 += len;
cut_len2 = len2 - dist;
i++;
}
index_end = gps->totpoints - i + 2;
interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len);
}
if (index_end <= index_start) {
index_start = index_end = 0; /* empty stroke */
}
if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) {
index_start = index_end = 0; /* no length left to cut */
}
BKE_gpencil_stroke_trim_points(gps, index_start, index_end, false);
if (gps->totpoints == 0) {
return false;
}
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Smooth Positions
* \{ */
@@ -1589,28 +762,12 @@ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
}
}
void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
void BKE_gpencil_stroke_geometry_update(bGPdata * /*gpd*/, bGPDstroke *gps)
{
if (gps == nullptr) {
return;
}
if (gps->editcurve != nullptr) {
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
/* curve geometry was updated: stroke needs recalculation */
if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) {
bool is_adaptive = gpd->flag & GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
BKE_gpencil_stroke_update_geometry_from_editcurve(
gps, gpd->curve_edit_resolution, is_adaptive);
gps->flag &= ~GP_STROKE_NEEDS_CURVE_UPDATE;
}
}
else {
/* stroke geometry was updated: editcurve needs recalculation */
gps->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
}
}
if (gps->totpoints > 2) {
BKE_gpencil_stroke_fill_triangulate(gps);
}
@@ -1856,94 +1013,6 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dissolve Points
* \{ */
void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
{
bGPDspoint *pt;
MDeformVert *dvert = nullptr;
int i;
int tot = gps->totpoints; /* number of points in new buffer */
/* first pass: count points to remove */
/* Count how many points are selected (i.e. how many to remove) */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & tag) {
/* selected point - one of the points to remove */
tot--;
}
}
/* if no points are left, we simply delete the entire stroke */
if (tot <= 0) {
/* remove the entire stroke */
if (gps->points) {
MEM_freeN(gps->points);
}
if (gps->dvert) {
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
}
if (gps->triangles) {
MEM_freeN(gps->triangles);
}
BLI_freelinkN(&gpf->strokes, gps);
}
else {
/* just copy all points to keep into a smaller buffer */
bGPDspoint *new_points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * tot,
"new gp stroke points copy");
bGPDspoint *npt = new_points;
MDeformVert *new_dvert = nullptr;
MDeformVert *ndvert = nullptr;
if (gps->dvert != nullptr) {
new_dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * tot,
"new gp stroke weights copy");
ndvert = new_dvert;
}
(gps->dvert != nullptr) ? dvert = gps->dvert : nullptr;
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if ((pt->flag & tag) == 0) {
*npt = blender::dna::shallow_copy(*pt);
npt++;
if (gps->dvert != nullptr) {
*ndvert = *dvert;
ndvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
ndvert++;
}
}
if (gps->dvert != nullptr) {
dvert++;
}
}
/* free the old buffer */
if (gps->points) {
MEM_freeN(gps->points);
}
if (gps->dvert) {
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
}
/* save the new buffer */
gps->points = new_points;
gps->dvert = new_dvert;
gps->totpoints = tot;
/* triangles cache needs to be recalculated */
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Normal Calculation
* \{ */
@@ -1980,819 +1049,6 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Simplify
* \{ */
void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float epsilon)
{
bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
int totpoints = gps->totpoints;
char *marked = nullptr;
char work;
int start = 0;
int end = gps->totpoints - 1;
marked = (char *)MEM_callocN(totpoints, "GP marked array");
marked[start] = 1;
marked[end] = 1;
work = 1;
int totmarked = 0;
/* while still reducing */
while (work) {
int ls, le;
work = 0;
ls = start;
le = start + 1;
/* while not over interval */
while (ls < end) {
int max_i = 0;
/* divided to get more control */
float max_dist = epsilon / 10.0f;
/* find the next marked point */
while (marked[le] == 0) {
le++;
}
for (int i = ls + 1; i < le; i++) {
float point_on_line[3];
float dist;
closest_to_line_segment_v3(
point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x);
dist = len_v3v3(point_on_line, &old_points[i].x);
if (dist > max_dist) {
max_dist = dist;
max_i = i;
}
}
if (max_i != 0) {
work = 1;
marked[max_i] = 1;
totmarked++;
}
ls = le;
le = ls + 1;
}
}
(void)totmarked; /* Quiet set-but-unused warning (may be removed). */
/* adding points marked */
MDeformVert *old_dvert = nullptr;
MDeformVert *dvert_src = nullptr;
if (gps->dvert != nullptr) {
old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
}
/* resize gps */
int j = 0;
for (int i = 0; i < totpoints; i++) {
bGPDspoint *pt_src = &old_points[i];
bGPDspoint *pt = &gps->points[j];
if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
*pt = blender::dna::shallow_copy(*pt_src);
if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
MDeformVert *dvert = &gps->dvert[j];
memcpy(dvert, dvert_src, sizeof(MDeformVert));
if (dvert_src->dw) {
memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
}
}
j++;
}
else {
if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
BKE_gpencil_free_point_weights(dvert_src);
}
}
}
gps->totpoints = j;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
MEM_SAFE_FREE(marked);
}
void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
{
if (gps->totpoints < 4) {
return;
}
/* save points */
bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
MDeformVert *old_dvert = nullptr;
MDeformVert *dvert_src = nullptr;
if (gps->dvert != nullptr) {
old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
}
/* resize gps */
int newtot = (gps->totpoints - 2) / 2;
if ((gps->totpoints % 2) != 0) {
newtot++;
}
newtot += 2;
gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
if (gps->dvert != nullptr) {
gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
}
int j = 0;
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt_src = &old_points[i];
bGPDspoint *pt = &gps->points[j];
if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
*pt = blender::dna::shallow_copy(*pt_src);
if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
MDeformVert *dvert = &gps->dvert[j];
memcpy(dvert, dvert_src, sizeof(MDeformVert));
if (dvert_src->dw) {
memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
}
}
j++;
}
else {
if (gps->dvert != nullptr) {
dvert_src = &old_dvert[i];
BKE_gpencil_free_point_weights(dvert_src);
}
}
}
gps->totpoints = j;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
}
void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type)
{
bGPDspoint *temp_points;
MDeformVert *temp_dverts = nullptr;
MDeformVert *dvert = nullptr;
MDeformVert *dvert_final = nullptr;
MDeformVert *dvert_next = nullptr;
int totnewpoints, oldtotpoints;
bool cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
for (int s = 0; s < level; s++) {
totnewpoints = gps->totpoints;
if (!cyclic) {
totnewpoints--;
}
/* duplicate points in a temp area */
temp_points = gps->points;
oldtotpoints = gps->totpoints;
/* resize the points arrays */
gps->totpoints += totnewpoints;
gps->points = (bGPDspoint *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->points), __func__);
if (gps->dvert != nullptr) {
temp_dverts = gps->dvert;
gps->dvert = (MDeformVert *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->dvert), __func__);
}
/* move points from last to first to new place */
for (int i = 0; i < oldtotpoints; i++) {
bGPDspoint *pt = &temp_points[i];
bGPDspoint *pt_final = &gps->points[i * 2];
copy_v3_v3(&pt_final->x, &pt->x);
pt_final->pressure = pt->pressure;
pt_final->strength = pt->strength;
pt_final->uv_rot = pt->uv_rot;
pt_final->uv_fac = pt->uv_fac;
pt_final->time = pt->time;
pt_final->flag = pt->flag;
pt_final->runtime.pt_orig = pt->runtime.pt_orig;
pt_final->runtime.idx_orig = pt->runtime.idx_orig;
copy_v4_v4(pt_final->vert_color, pt->vert_color);
copy_v4_v4(pt_final->uv_fill, pt->uv_fill);
if (gps->dvert != nullptr) {
dvert = &temp_dverts[i];
dvert_final = &gps->dvert[i * 2];
dvert_final->totweight = dvert->totweight;
dvert_final->dw = dvert->dw;
}
}
/* interpolate mid points */
for (int i = cyclic ? 0 : 1, j = cyclic ? oldtotpoints - 1 : 0; i < oldtotpoints; j = i, i++) {
bGPDspoint *pt = &temp_points[j];
bGPDspoint *next = &temp_points[i];
bGPDspoint *pt_final = &gps->points[j * 2 + 1];
/* add a half way point */
interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f);
pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f);
interp_v4_v4v4(pt_final->uv_fill, pt->uv_fill, next->uv_fill, 0.5f);
CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt_final->time = 0;
pt_final->runtime.pt_orig = nullptr;
pt_final->flag = 0;
interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
if (gps->dvert != nullptr) {
dvert = &temp_dverts[j];
dvert_next = &temp_dverts[i];
dvert_final = &gps->dvert[j * 2 + 1];
dvert_final->totweight = dvert->totweight;
dvert_final->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
/* interpolate weight values */
for (int d = 0; d < dvert->totweight; d++) {
MDeformWeight *dw_a = &dvert->dw[d];
if (dvert_next->totweight > d) {
MDeformWeight *dw_b = &dvert_next->dw[d];
MDeformWeight *dw_final = &dvert_final->dw[d];
dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
}
}
}
}
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
/* Move points to smooth stroke (not simple type). */
if (type != GP_SUBDIV_SIMPLE) {
float mid[3];
/* extreme points are not changed */
for (int i = cyclic ? 0 : 2, j = cyclic ? gps->totpoints - 2 : 0; i < gps->totpoints - 2;
j = i, i += 2)
{
bGPDspoint *prev = &gps->points[j + 1];
bGPDspoint *pt = &gps->points[i];
bGPDspoint *next = &gps->points[i + 1];
/* move point */
interp_v3_v3v3(mid, &prev->x, &next->x, 0.5f);
interp_v3_v3v3(&pt->x, mid, &pt->x, 0.5f);
}
}
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Merge by Distance
* \{ */
void BKE_gpencil_stroke_merge_distance(bGPdata *gpd,
bGPDframe *gpf,
bGPDstroke *gps,
const float threshold,
const bool use_unselected)
{
bGPDspoint *pt = nullptr;
bGPDspoint *pt_next = nullptr;
float tagged = false;
/* Use square distance to speed up loop */
const float th_square = threshold * threshold;
/* Need to have something to merge. */
if (gps->totpoints < 2) {
return;
}
int i = 0;
int step = 1;
while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) {
pt = &gps->points[i];
if (pt->flag & GP_SPOINT_TAG) {
i++;
step = 1;
continue;
}
pt_next = &gps->points[i + step];
/* Do not recalc tagged points. */
if (pt_next->flag & GP_SPOINT_TAG) {
step++;
continue;
}
/* Check if contiguous points are selected. */
if (!use_unselected) {
if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) {
i++;
step = 1;
continue;
}
}
float len_square = len_squared_v3v3(&pt->x, &pt_next->x);
if (len_square <= th_square) {
tagged = true;
if (i != gps->totpoints - 1) {
/* Tag second point for delete. */
pt_next->flag |= GP_SPOINT_TAG;
}
else {
pt->flag |= GP_SPOINT_TAG;
}
/* Jump to next pair of points, keeping first point segment equals. */
step++;
}
else {
/* Analyze next point. */
i++;
step = 1;
}
}
/* Always untag extremes. */
pt = &gps->points[0];
pt->flag &= ~GP_SPOINT_TAG;
pt = &gps->points[gps->totpoints - 1];
pt->flag &= ~GP_SPOINT_TAG;
/* Dissolve tagged points */
if (tagged) {
BKE_gpencil_dissolve_points(gpd, gpf, gps, GP_SPOINT_TAG);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
struct GpEdge {
uint v1, v2;
/* Coordinates. */
float v1_co[3], v2_co[3];
/* Normals. */
float n1[3], n2[3];
/* Direction of the segment. */
float vec[3];
int flag;
};
static int gpencil_next_edge(
GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse)
{
int edge = -1;
float last_angle = 999999.0f;
for (int i = 0; i < totedges; i++) {
GpEdge *gped = &gp_edges[i];
if (gped->flag != 0) {
continue;
}
if (reverse) {
if (gped_init->v1 != gped->v2) {
continue;
}
}
else {
if (gped_init->v2 != gped->v1) {
continue;
}
}
/* Look for straight lines. */
float angle = angle_v3v3(gped->vec, gped_init->vec);
if ((angle < threshold) && (angle <= last_angle)) {
edge = i;
last_angle = angle;
}
}
return edge;
}
static int gpencil_walk_edge(GHash *v_table,
GpEdge *gp_edges,
int totedges,
uint *stroke_array,
int init_idx,
const float angle,
const bool reverse)
{
GpEdge *gped_init = &gp_edges[init_idx];
int idx = 1;
int edge = 0;
while (edge > -1) {
edge = gpencil_next_edge(gp_edges, totedges, gped_init, angle, reverse);
if (edge > -1) {
GpEdge *gped = &gp_edges[edge];
stroke_array[idx] = edge;
gped->flag = 1;
gped_init = &gp_edges[edge];
idx++;
/* Avoid following already visited vertices. */
if (reverse) {
if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v1))) {
edge = -1;
}
else {
BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v1), POINTER_FROM_INT(gped->v1));
}
}
else {
if (BLI_ghash_haskey(v_table, POINTER_FROM_INT(gped->v2))) {
edge = -1;
}
else {
BLI_ghash_insert(v_table, POINTER_FROM_INT(gped->v2), POINTER_FROM_INT(gped->v2));
}
}
}
}
return idx;
}
static void gpencil_generate_edgeloops(Object *ob,
bGPdata *gpd,
bGPDframe *gpf_stroke,
int stroke_mat_index,
const float angle,
const int thickness,
const float offset,
const float matrix[4][4],
const bool use_seams,
const bool use_vgroups)
{
using namespace blender;
using namespace blender::bke;
Mesh *mesh = (Mesh *)ob->data;
if (mesh->edges_num == 0) {
return;
}
const Span<float3> vert_positions = mesh->vert_positions();
const Span<int2> edges = mesh->edges();
const Span<MDeformVert> dverts = mesh->deform_verts();
const blender::Span<blender::float3> vert_normals = mesh->vert_normals();
const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<bool> uv_seams = *attributes.lookup_or_default<bool>(
".uv_seam", AttrDomain::Edge, false);
/* Arrays for all edge vertices (forward and backward) that form a edge loop.
* This is reused for each edge-loop to create gpencil stroke. */
uint *stroke = (uint *)MEM_mallocN(sizeof(uint) * mesh->edges_num * 2, __func__);
uint *stroke_fw = (uint *)MEM_mallocN(sizeof(uint) * mesh->edges_num, __func__);
uint *stroke_bw = (uint *)MEM_mallocN(sizeof(uint) * mesh->edges_num, __func__);
/* Create array with all edges. */
GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * mesh->edges_num, __func__);
GpEdge *gped = nullptr;
for (int i = 0; i < mesh->edges_num; i++) {
const blender::int2 &edge = edges[i];
gped = &gp_edges[i];
copy_v3_v3(gped->n1, vert_normals[edge[0]]);
gped->v1 = edge[0];
copy_v3_v3(gped->v1_co, vert_positions[edge[0]]);
copy_v3_v3(gped->n2, vert_normals[edge[1]]);
gped->v2 = edge[1];
copy_v3_v3(gped->v2_co, vert_positions[edge[1]]);
sub_v3_v3v3(gped->vec, vert_positions[edge[0]], vert_positions[edge[1]]);
/* If use seams, mark as done if not a seam. */
if ((use_seams) && !uv_seams[i]) {
gped->flag = 1;
}
}
/* Loop edges to find edgeloops */
bool pending = true;
int e = 0;
while (pending) {
gped = &gp_edges[e];
/* Look first unused edge. */
if (gped->flag != 0) {
e++;
if (e == mesh->edges_num) {
pending = false;
}
continue;
}
/* Add current edge to arrays. */
stroke_fw[0] = e;
stroke_bw[0] = e;
gped->flag = 1;
/* Hash used to avoid loop over same vertices. */
GHash *v_table = BLI_ghash_int_new(__func__);
/* Look forward edges. */
int totedges = gpencil_walk_edge(
v_table, gp_edges, mesh->edges_num, stroke_fw, e, angle, false);
/* Look backward edges. */
int totbw = gpencil_walk_edge(v_table, gp_edges, mesh->edges_num, stroke_bw, e, angle, true);
BLI_ghash_free(v_table, nullptr, nullptr);
/* Join both arrays. */
int array_len = 0;
for (int i = totbw - 1; i > 0; i--) {
stroke[array_len] = stroke_bw[i];
array_len++;
}
for (int i = 0; i < totedges; i++) {
stroke[array_len] = stroke_fw[i];
array_len++;
}
/* Create Stroke. */
bGPDstroke *gps_stroke = BKE_gpencil_stroke_add(
gpf_stroke, std::max(stroke_mat_index, 0), array_len + 1, thickness * thickness, false);
/* Create dvert data. */
if (use_vgroups && !dverts.is_empty()) {
gps_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (array_len + 1),
"gp_stroke_dverts");
}
/* Create first segment. */
float fpt[3];
for (int i = 0; i < array_len + 1; i++) {
int vertex_index = i == 0 ? gp_edges[stroke[0]].v1 : gp_edges[stroke[i - 1]].v2;
/* Add segment. */
bGPDspoint *pt = &gps_stroke->points[i];
copy_v3_v3(fpt, vert_normals[vertex_index]);
mul_v3_v3fl(fpt, fpt, offset);
add_v3_v3v3(&pt->x, vert_positions[vertex_index], fpt);
mul_m4_v3(matrix, &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
/* Copy vertex groups from mesh. Assuming they already exist in the same order. */
if (use_vgroups && !dverts.is_empty()) {
MDeformVert *dv = &gps_stroke->dvert[i];
const MDeformVert *src_dv = &dverts[vertex_index];
dv->totweight = src_dv->totweight;
dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw");
for (int j = 0; j < dv->totweight; j++) {
dv->dw[j].weight = src_dv->dw[j].weight;
dv->dw[j].def_nr = src_dv->dw[j].def_nr;
}
}
}
BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
}
/* Free memory. */
MEM_SAFE_FREE(stroke);
MEM_SAFE_FREE(stroke_fw);
MEM_SAFE_FREE(stroke_bw);
MEM_SAFE_FREE(gp_edges);
}
/* Helper: Add gpencil material using material as base. */
static Material *gpencil_add_material(Main *bmain,
Object *ob_gp,
const char *name,
const float color[4],
const bool use_stroke,
const bool use_fill,
int *r_idx)
{
Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob_gp, name, r_idx);
MaterialGPencilStyle *gp_style = mat_gp->gp_style;
/* Stroke color. */
if (use_stroke) {
ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
}
else {
copy_v4_v4(gp_style->stroke_rgba, color);
gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
}
/* Fill color. */
copy_v4_v4(gp_style->fill_rgba, color);
if (use_fill) {
gp_style->flag |= GP_MATERIAL_FILL_SHOW;
}
/* Check at least one is enabled. */
if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) &&
((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0))
{
gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
}
return mat_gp;
}
static int gpencil_material_find_index_by_name(Object *ob, const char *name)
{
for (int i = 0; i < ob->totcol; i++) {
Material *ma = BKE_object_material_get(ob, i + 1);
if ((ma != nullptr) && (ma->gp_style != nullptr) && STREQ(ma->id.name + 2, name)) {
return i;
}
}
return -1;
}
/**
* Create the name with the object name and a suffix.
*/
static void make_element_name(const char *obname, const char *name, const int maxlen, char *r_name)
{
char str[256];
SNPRINTF(str, "%s_%s", obname, name);
/* Replace any point by underscore. */
BLI_string_replace_char(str, '.', '_');
BLI_strncpy_utf8(r_name, str, maxlen);
}
bool BKE_gpencil_convert_mesh(Main *bmain,
Depsgraph *depsgraph,
Scene *scene,
Object *ob_gp,
Object *ob_mesh,
const float angle,
const int thickness,
const float offset,
const float matrix[4][4],
const int frame_offset,
const bool use_seams,
const bool use_faces,
const bool use_vgroups)
{
using namespace blender;
using namespace blender::bke;
if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL_LEGACY) ||
(ob_gp->data == nullptr))
{
return false;
}
bGPdata *gpd = (bGPdata *)ob_gp->data;
/* Use evaluated data to get mesh with all modifiers on top. */
Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, ob_mesh);
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
const Span<float3> positions = mesh_eval->vert_positions();
const OffsetIndices faces = mesh_eval->faces();
const Span<int> corner_verts = mesh_eval->corner_verts();
int faces_len = mesh_eval->faces_num;
char element_name[200];
/* Need at least an edge. */
if (mesh_eval->edges_num < 1) {
return false;
}
/* Create matching vertex groups. */
BKE_defgroup_copy_list(&gpd->vertex_group_names, &mesh_eval->vertex_group_names);
gpd->vertex_group_active_index = mesh_eval->vertex_group_active_index;
const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}};
/* Lookup existing stroke material on gp object. */
make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name);
int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name);
if (stroke_mat_index == -1) {
/* Create new default stroke material as there is no existing material. */
gpencil_add_material(
bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index);
}
/* Export faces as filled strokes. */
if (use_faces && faces_len > 0) {
/* Read all polygons and create fill for each. */
make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_fill == nullptr) {
gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW);
int i;
const VArray<int> mesh_material_indices = *mesh_eval->attributes().lookup_or_default<int>(
"material_index", AttrDomain::Face, 0);
for (i = 0; i < faces_len; i++) {
const IndexRange face = faces[i];
/* Find material. */
int mat_idx = 0;
Material *ma = BKE_object_material_get(ob_mesh, mesh_material_indices[i] + 1);
make_element_name(
ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
if (mat_idx == -1) {
float color[4];
if (ma != nullptr) {
copy_v3_v3(color, &ma->r);
color[3] = 1.0f;
}
else {
copy_v4_v4(color, default_colors[1]);
}
gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
}
bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, face.size(), 10, false);
gps_fill->flag |= GP_STROKE_CYCLIC;
/* Create dvert data. */
const Span<MDeformVert> dverts = mesh_eval->deform_verts();
if (use_vgroups && !dverts.is_empty()) {
gps_fill->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * face.size(),
"gp_fill_dverts");
}
/* Add points to strokes. */
for (int j = 0; j < face.size(); j++) {
const int vert = corner_verts[face[j]];
bGPDspoint *pt = &gps_fill->points[j];
copy_v3_v3(&pt->x, positions[vert]);
mul_m4_v3(matrix, &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
/* Copy vertex groups from mesh. Assuming they already exist in the same order. */
if (use_vgroups && !dverts.is_empty()) {
MDeformVert *dv = &gps_fill->dvert[j];
const MDeformVert *src_dv = &dverts[vert];
dv->totweight = src_dv->totweight;
dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
"gp_fill_dverts_dw");
for (int k = 0; k < dv->totweight; k++) {
dv->dw[k].weight = src_dv->dw[k].weight;
dv->dw[k].def_nr = src_dv->dw[k].def_nr;
}
}
}
/* If has only 3 points subdivide. */
if (face.size() == 3) {
BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
}
BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
}
}
/* Create stroke from edges. */
/* Create Layer and Frame. */
make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_stroke == nullptr) {
gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
gpl_stroke, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW);
gpencil_generate_edgeloops(ob_eval,
gpd,
gpf_stroke,
stroke_mat_index,
angle,
thickness,
offset,
matrix,
use_seams,
use_vgroups);
/* Tag for recalculation */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
return true;
}
void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
{
if (gpd == nullptr) {
@@ -3243,112 +1499,6 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
return new_stroke;
}
void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd,
bGPDframe *gpf,
bGPDstroke *gps,
bGPDstroke *next_stroke,
bGPDcurve *gpc,
int tag_flags)
{
if (gpc == nullptr) {
return;
}
const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
const int idx_last = gpc->tot_curve_points - 1;
bGPDstroke *gps_first = nullptr;
bGPDstroke *gps_last = nullptr;
int idx_start = 0;
int idx_end = 0;
bool prev_selected = gpc->curve_points[0].flag & tag_flags;
for (int i = 1; i < gpc->tot_curve_points; i++) {
bool selected = gpc->curve_points[i].flag & tag_flags;
if (prev_selected == true && selected == false) {
idx_start = i;
}
/* Island ends if the current point is selected or if we reached the end of the stroke */
if ((prev_selected == false && selected == true) || (selected == false && i == idx_last)) {
idx_end = selected ? i - 1 : i;
int island_length = idx_end - idx_start + 1;
/* If an island has only a single curve point, there is no curve segment, so skip island */
if (island_length == 1) {
if (is_cyclic) {
if (idx_start > 0 && idx_end < idx_last) {
prev_selected = selected;
continue;
}
}
else {
prev_selected = selected;
continue;
}
}
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false);
new_stroke->points = nullptr;
new_stroke->flag &= ~GP_STROKE_CYCLIC;
new_stroke->editcurve = BKE_gpencil_stroke_editcurve_new(island_length);
if (gps_first == nullptr) {
gps_first = new_stroke;
}
bGPDcurve *new_gpc = new_stroke->editcurve;
memcpy(new_gpc->curve_points,
gpc->curve_points + idx_start,
sizeof(bGPDcurve_point) * island_length);
BKE_gpencil_editcurve_recalculate_handles(new_stroke);
new_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
if (next_stroke) {
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
}
else {
BLI_addtail(&gpf->strokes, new_stroke);
}
gps_last = new_stroke;
}
prev_selected = selected;
}
/* join first and last stroke if cyclic */
if (is_cyclic && gps_first != nullptr && gps_last != nullptr && gps_first != gps_last) {
bGPDcurve *gpc_first = gps_first->editcurve;
bGPDcurve *gpc_last = gps_last->editcurve;
int first_tot_points = gpc_first->tot_curve_points;
int old_tot_points = gpc_last->tot_curve_points;
gpc_last->tot_curve_points = first_tot_points + old_tot_points;
gpc_last->curve_points = (bGPDcurve_point *)MEM_recallocN(
gpc_last->curve_points, sizeof(bGPDcurve_point) * gpc_last->tot_curve_points);
/* copy data from first to last */
memcpy(gpc_last->curve_points + old_tot_points,
gpc_first->curve_points,
sizeof(bGPDcurve_point) * first_tot_points);
BKE_gpencil_editcurve_recalculate_handles(gps_last);
gps_last->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps_last);
/* remove first one */
BLI_remlink(&gpf->strokes, gps_first);
BKE_gpencil_free_stroke(gps_first);
}
/* Delete the old stroke */
BLI_remlink(&gpf->strokes, gps);
BKE_gpencil_free_stroke(gps);
}
/* Helper: copy point between strokes */
static void gpencil_stroke_copy_point(bGPDstroke *gps,
MDeformVert *dvert,
@@ -3531,254 +1681,7 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
}
}
void BKE_gpencil_stroke_start_set(bGPDstroke *gps, int start_idx)
{
if ((start_idx < 1) || (start_idx >= gps->totpoints) || (gps->totpoints < 2)) {
return;
}
/* Only cyclic strokes. */
if ((gps->flag & GP_STROKE_CYCLIC) == 0) {
return;
}
bGPDstroke *gps_b = BKE_gpencil_stroke_duplicate(gps, true, false);
BKE_gpencil_stroke_trim_points(gps_b, 0, start_idx - 1, true);
BKE_gpencil_stroke_trim_points(gps, start_idx, gps->totpoints - 1, true);
/* Join both strokes. */
BKE_gpencil_stroke_join(gps, gps_b, false, false, false, false);
BKE_gpencil_free_stroke(gps_b);
}
void BKE_gpencil_stroke_copy_to_keyframes(
bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const bool tail)
{
GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64);
BKE_gpencil_frame_selected_hash(gpd, frame_list);
GHashIterator gh_iter;
GHASH_ITER (gh_iter, frame_list) {
int cfra = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter));
if (gpf->framenum != cfra) {
bGPDframe *gpf_new = BKE_gpencil_layer_frame_find(gpl, cfra);
if (gpf_new == nullptr) {
gpf_new = BKE_gpencil_frame_addnew(gpl, cfra);
}
if (gpf_new == nullptr) {
continue;
}
bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, true, true);
if (gps_new == nullptr) {
continue;
}
if (tail) {
BLI_addhead(&gpf_new->strokes, gps_new);
}
else {
BLI_addtail(&gpf_new->strokes, gps_new);
}
}
}
/* Free hash table. */
BLI_ghash_free(frame_list, nullptr, nullptr);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Uniform Subdivide
* \{ */
struct tSamplePoint {
tSamplePoint *next, *prev;
float x, y, z;
float pressure, strength, time;
float vertex_color[4];
MDeformWeight *dw;
int totweight;
};
struct tSampleEdge {
float length_sq;
tSamplePoint *from;
tSamplePoint *to;
};
/* Helper: creates a tSamplePoint from a bGPDspoint and (optionally) a MDeformVert. */
static tSamplePoint *new_sample_point_from_gp_point(const bGPDspoint *pt, const MDeformVert *dvert)
{
tSamplePoint *new_pt = MEM_cnew<tSamplePoint>(__func__);
copy_v3_v3(&new_pt->x, &pt->x);
new_pt->pressure = pt->pressure;
new_pt->strength = pt->strength;
new_pt->time = pt->time;
copy_v4_v4((float *)&new_pt->vertex_color, (float *)&pt->vert_color);
if (dvert != nullptr) {
new_pt->totweight = dvert->totweight;
new_pt->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__);
for (uint i = 0; i < new_pt->totweight; ++i) {
MDeformWeight *dw = &new_pt->dw[i];
MDeformWeight *dw_from = &dvert->dw[i];
dw->def_nr = dw_from->def_nr;
dw->weight = dw_from->weight;
}
}
return new_pt;
}
/* Helper: creates a tSampleEdge from two tSamplePoints. Also calculates the length (squared) of
* the edge. */
static tSampleEdge *new_sample_edge_from_sample_points(tSamplePoint *from, tSamplePoint *to)
{
tSampleEdge *new_edge = MEM_cnew<tSampleEdge>(__func__);
new_edge->from = from;
new_edge->to = to;
new_edge->length_sq = len_squared_v3v3(&from->x, &to->x);
return new_edge;
}
void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
bGPDstroke *gps,
const uint32_t target_number,
const bool select)
{
/* Stroke needs at least two points and strictly less points than the target number. */
if (gps == nullptr || gps->totpoints < 2 || gps->totpoints >= target_number) {
return;
}
const int totpoints = gps->totpoints;
const bool has_dverts = (gps->dvert != nullptr);
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
ListBase points = {nullptr, nullptr};
Heap *edges = BLI_heap_new();
/* Add all points into list. */
for (uint32_t i = 0; i < totpoints; ++i) {
bGPDspoint *pt = &gps->points[i];
MDeformVert *dvert = has_dverts ? &gps->dvert[i] : nullptr;
tSamplePoint *sp = new_sample_point_from_gp_point(pt, dvert);
BLI_addtail(&points, sp);
}
/* Iterate over edges and insert them into the heap. */
for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != nullptr; pt = pt->next) {
tSampleEdge *se = new_sample_edge_from_sample_points(pt->prev, pt);
/* BLI_heap is a min-heap, but we need the largest key to be at the top, so we take the
* negative of the squared length. */
BLI_heap_insert(edges, -(se->length_sq), se);
}
if (is_cyclic) {
tSamplePoint *sp_first = (tSamplePoint *)points.first;
tSamplePoint *sp_last = (tSamplePoint *)points.last;
tSampleEdge *se = new_sample_edge_from_sample_points(sp_last, sp_first);
BLI_heap_insert(edges, -(se->length_sq), se);
}
int num_points_needed = target_number - totpoints;
BLI_assert(num_points_needed > 0);
while (num_points_needed > 0) {
tSampleEdge *se = (tSampleEdge *)BLI_heap_pop_min(edges);
tSamplePoint *sp = se->from;
tSamplePoint *sp_next = se->to;
/* Subdivide the edge. */
tSamplePoint *new_sp = MEM_cnew<tSamplePoint>(__func__);
interp_v3_v3v3(&new_sp->x, &sp->x, &sp_next->x, 0.5f);
new_sp->pressure = interpf(sp->pressure, sp_next->pressure, 0.5f);
new_sp->strength = interpf(sp->strength, sp_next->strength, 0.5f);
new_sp->time = interpf(sp->time, sp_next->time, 0.5f);
interp_v4_v4v4((float *)&new_sp->vertex_color,
(float *)&sp->vertex_color,
(float *)&sp_next->vertex_color,
0.5f);
if (sp->dw && sp_next->dw) {
new_sp->totweight = std::min(sp->totweight, sp_next->totweight);
new_sp->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight,
__func__);
for (uint32_t i = 0; i < new_sp->totweight; ++i) {
MDeformWeight *dw = &new_sp->dw[i];
MDeformWeight *dw_from = &sp->dw[i];
MDeformWeight *dw_to = &sp_next->dw[i];
dw->def_nr = dw_from->def_nr;
dw->weight = interpf(dw_from->weight, dw_to->weight, 0.5f);
}
}
BLI_insertlinkafter(&points, sp, new_sp);
tSampleEdge *se_prev = new_sample_edge_from_sample_points(sp, new_sp);
tSampleEdge *se_next = new_sample_edge_from_sample_points(new_sp, sp_next);
BLI_heap_insert(edges, -(se_prev->length_sq), se_prev);
BLI_heap_insert(edges, -(se_next->length_sq), se_next);
MEM_freeN(se);
num_points_needed--;
}
/* Edges are no longer needed. Heap is freed. */
BLI_heap_free(edges, (HeapFreeFP)MEM_freeN);
gps->totpoints = target_number;
gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
if (has_dverts) {
gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
}
/* Convert list back to stroke point array. */
tSamplePoint *sp = (tSamplePoint *)points.first;
for (uint32_t i = 0; i < gps->totpoints && sp; ++i, sp = sp->next) {
bGPDspoint *pt = &gps->points[i];
MDeformVert *dvert = &gps->dvert[i];
copy_v3_v3(&pt->x, &sp->x);
pt->pressure = sp->pressure;
pt->strength = sp->strength;
pt->time = sp->time;
copy_v4_v4((float *)&pt->vert_color, (float *)&sp->vertex_color);
if (sp->dw) {
dvert->totweight = sp->totweight;
dvert->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__);
for (uint32_t j = 0; j < dvert->totweight; ++j) {
MDeformWeight *dw = &dvert->dw[j];
MDeformWeight *dw_from = &sp->dw[j];
dw->def_nr = dw_from->def_nr;
dw->weight = dw_from->weight;
}
}
if (select) {
pt->flag |= GP_SPOINT_SELECT;
}
}
if (select) {
gps->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
/* Free the sample points. Important to use the mutable loop here because we are erasing the list
* elements. */
LISTBASE_FOREACH_MUTABLE (tSamplePoint *, temp, &points) {
if (temp->dw != nullptr) {
MEM_freeN(temp->dw);
}
MEM_SAFE_FREE(temp);
}
/* Update the geometry of the stroke. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
void BKE_gpencil_stroke_to_view_space(bGPDstroke *gps,
float viewmat[4][4],
const float diff_mat[4][4])
@@ -3808,494 +1711,6 @@ void BKE_gpencil_stroke_from_view_space(bGPDstroke *gps,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke to Perimeter
* \{ */
struct tPerimeterPoint {
tPerimeterPoint *next, *prev;
float x, y, z;
};
static tPerimeterPoint *new_perimeter_point(const float pt[3])
{
tPerimeterPoint *new_pt = MEM_cnew<tPerimeterPoint>(__func__);
copy_v3_v3(&new_pt->x, pt);
return new_pt;
}
static int generate_arc_from_point_to_point(ListBase *list,
tPerimeterPoint *from,
tPerimeterPoint *to,
float center_pt[3],
int subdivisions,
bool clockwise)
{
float vec_from[2];
float vec_to[2];
sub_v2_v2v2(vec_from, &from->x, center_pt);
sub_v2_v2v2(vec_to, &to->x, center_pt);
if (is_zero_v2(vec_from) || is_zero_v2(vec_to)) {
return 0;
}
float dot = dot_v2v2(vec_from, vec_to);
float det = cross_v2v2(vec_from, vec_to);
float angle = clockwise ? M_PI - atan2f(-det, -dot) : atan2f(-det, -dot) + M_PI;
/* Number of points is 2^(n+1) + 1 on half a circle (n=subdivisions)
* so we multiply by (angle / pi) to get the right amount of
* points to insert. */
int num_points = int(((1 << (subdivisions + 1)) - 1) * (angle / M_PI));
if (num_points > 0) {
float angle_incr = angle / float(num_points);
float vec_p[3];
float vec_t[3];
float tmp_angle;
tPerimeterPoint *last_point;
if (clockwise) {
last_point = to;
copy_v2_v2(vec_t, vec_to);
}
else {
last_point = from;
copy_v2_v2(vec_t, vec_from);
}
for (int i = 0; i < num_points - 1; i++) {
tmp_angle = (i + 1) * angle_incr;
rotate_v2_v2fl(vec_p, vec_t, tmp_angle);
add_v2_v2(vec_p, center_pt);
vec_p[2] = center_pt[2];
tPerimeterPoint *new_point = new_perimeter_point(vec_p);
if (clockwise) {
BLI_insertlinkbefore(list, last_point, new_point);
}
else {
BLI_insertlinkafter(list, last_point, new_point);
}
last_point = new_point;
}
return num_points - 1;
}
return 0;
}
static int generate_semi_circle_from_point_to_point(ListBase *list,
tPerimeterPoint *from,
tPerimeterPoint *to,
int subdivisions)
{
int num_points = (1 << (subdivisions + 1)) + 1;
float center_pt[3];
interp_v3_v3v3(center_pt, &from->x, &to->x, 0.5f);
float vec_center[2];
sub_v2_v2v2(vec_center, &from->x, center_pt);
if (is_zero_v2(vec_center)) {
return 0;
}
float vec_p[3];
float angle_incr = M_PI / (float(num_points) - 1);
tPerimeterPoint *last_point = from;
for (int i = 1; i < num_points; i++) {
float angle = i * angle_incr;
/* Rotate vector around point to get perimeter points. */
rotate_v2_v2fl(vec_p, vec_center, angle);
add_v2_v2(vec_p, center_pt);
vec_p[2] = center_pt[2];
tPerimeterPoint *new_point = new_perimeter_point(vec_p);
BLI_insertlinkafter(list, last_point, new_point);
last_point = new_point;
}
return num_points - 1;
}
static int generate_perimeter_cap(const float point[4],
const float other_point[4],
float radius,
ListBase *list,
int subdivisions,
short cap_type)
{
float cap_vec[2];
sub_v2_v2v2(cap_vec, other_point, point);
normalize_v2(cap_vec);
float cap_nvec[2];
if (is_zero_v2(cap_vec)) {
cap_nvec[0] = 0;
cap_nvec[1] = radius;
}
else {
cap_nvec[0] = -cap_vec[1];
cap_nvec[1] = cap_vec[0];
mul_v2_fl(cap_nvec, radius);
}
float cap_nvec_inv[2];
negate_v2_v2(cap_nvec_inv, cap_nvec);
float vec_perimeter[3];
copy_v3_v3(vec_perimeter, point);
add_v2_v2(vec_perimeter, cap_nvec);
float vec_perimeter_inv[3];
copy_v3_v3(vec_perimeter_inv, point);
add_v2_v2(vec_perimeter_inv, cap_nvec_inv);
tPerimeterPoint *p_pt = new_perimeter_point(vec_perimeter);
tPerimeterPoint *p_pt_inv = new_perimeter_point(vec_perimeter_inv);
BLI_addtail(list, p_pt);
BLI_addtail(list, p_pt_inv);
int num_points = 0;
if (cap_type == GP_STROKE_CAP_ROUND) {
num_points += generate_semi_circle_from_point_to_point(list, p_pt, p_pt_inv, subdivisions);
}
return num_points + 2;
}
/**
* Calculate the perimeter (outline) of a stroke as list of tPerimeterPoint.
* \param subdivisions: Number of subdivisions for the start and end caps
* \return list of tPerimeterPoint.
*/
static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
const bGPDlayer *gpl,
const bGPDstroke *gps,
int subdivisions,
const float thickness_chg,
int *r_num_perimeter_points)
{
/* sanity check */
if (gps->totpoints < 1) {
return nullptr;
}
float defaultpixsize = 1000.0f / gpd->pixfactor;
float ovr_radius = thickness_chg / defaultpixsize / 2.0f;
float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
stroke_radius = max_ff(stroke_radius - ovr_radius, 0.0f);
ListBase *perimeter_right_side = MEM_cnew<ListBase>(__func__);
ListBase *perimeter_left_side = MEM_cnew<ListBase>(__func__);
int num_perimeter_points = 0;
bGPDspoint *first = &gps->points[0];
bGPDspoint *last = &gps->points[gps->totpoints - 1];
float first_radius = stroke_radius * first->pressure;
float last_radius = stroke_radius * last->pressure;
bGPDspoint *first_next;
bGPDspoint *last_prev;
if (gps->totpoints > 1) {
first_next = &gps->points[1];
last_prev = &gps->points[gps->totpoints - 2];
}
else {
first_next = first;
last_prev = last;
}
float first_pt[3];
float last_pt[3];
float first_next_pt[3];
float last_prev_pt[3];
copy_v3_v3(first_pt, &first->x);
copy_v3_v3(last_pt, &last->x);
copy_v3_v3(first_next_pt, &first_next->x);
copy_v3_v3(last_prev_pt, &last_prev->x);
/* Edge-case if single point. */
if (gps->totpoints == 1) {
first_next_pt[0] += 1.0f;
last_prev_pt[0] -= 1.0f;
}
/* Generate points for start cap. */
num_perimeter_points += generate_perimeter_cap(
first_pt, first_next_pt, first_radius, perimeter_right_side, subdivisions, gps->caps[0]);
/* Generate perimeter points. */
float curr_pt[3], next_pt[3], prev_pt[3];
float vec_next[2], vec_prev[2];
float nvec_next[2], nvec_prev[2];
float nvec_next_pt[3], nvec_prev_pt[3];
float vec_tangent[2];
float vec_miter_left[2], vec_miter_right[2];
float miter_left_pt[3], miter_right_pt[3];
for (int i = 1; i < gps->totpoints - 1; i++) {
bGPDspoint *curr = &gps->points[i];
bGPDspoint *prev = &gps->points[i - 1];
bGPDspoint *next = &gps->points[i + 1];
float radius = stroke_radius * curr->pressure;
copy_v3_v3(curr_pt, &curr->x);
copy_v3_v3(next_pt, &next->x);
copy_v3_v3(prev_pt, &prev->x);
sub_v2_v2v2(vec_prev, curr_pt, prev_pt);
sub_v2_v2v2(vec_next, next_pt, curr_pt);
float prev_length = len_v2(vec_prev);
float next_length = len_v2(vec_next);
if (normalize_v2(vec_prev) == 0.0f) {
vec_prev[0] = 1.0f;
vec_prev[1] = 0.0f;
}
if (normalize_v2(vec_next) == 0.0f) {
vec_next[0] = 1.0f;
vec_next[1] = 0.0f;
}
nvec_prev[0] = -vec_prev[1];
nvec_prev[1] = vec_prev[0];
nvec_next[0] = -vec_next[1];
nvec_next[1] = vec_next[0];
add_v2_v2v2(vec_tangent, vec_prev, vec_next);
if (normalize_v2(vec_tangent) == 0.0f) {
copy_v2_v2(vec_tangent, nvec_prev);
}
vec_miter_left[0] = -vec_tangent[1];
vec_miter_left[1] = vec_tangent[0];
/* calculate miter length */
float an1 = dot_v2v2(vec_miter_left, nvec_prev);
if (an1 == 0.0f) {
an1 = 1.0f;
}
float miter_length = radius / an1;
if (miter_length <= 0.0f) {
miter_length = 0.01f;
}
normalize_v2_length(vec_miter_left, miter_length);
copy_v2_v2(vec_miter_right, vec_miter_left);
negate_v2(vec_miter_right);
float angle = dot_v2v2(vec_next, nvec_prev);
/* Add two points if angle is close to being straight. */
if (fabsf(angle) < 0.0001f) {
normalize_v2_length(nvec_prev, radius);
normalize_v2_length(nvec_next, radius);
copy_v3_v3(nvec_prev_pt, curr_pt);
add_v2_v2(nvec_prev_pt, nvec_prev);
copy_v3_v3(nvec_next_pt, curr_pt);
negate_v2(nvec_next);
add_v2_v2(nvec_next_pt, nvec_next);
tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
BLI_addtail(perimeter_left_side, normal_prev);
BLI_addtail(perimeter_right_side, normal_next);
num_perimeter_points += 2;
}
else {
/* bend to the left */
if (angle < 0.0f) {
normalize_v2_length(nvec_prev, radius);
normalize_v2_length(nvec_next, radius);
copy_v3_v3(nvec_prev_pt, curr_pt);
add_v2_v2(nvec_prev_pt, nvec_prev);
copy_v3_v3(nvec_next_pt, curr_pt);
add_v2_v2(nvec_next_pt, nvec_next);
tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
BLI_addtail(perimeter_left_side, normal_prev);
BLI_addtail(perimeter_left_side, normal_next);
num_perimeter_points += 2;
num_perimeter_points += generate_arc_from_point_to_point(
perimeter_left_side, normal_prev, normal_next, curr_pt, subdivisions, true);
if (miter_length < prev_length && miter_length < next_length) {
copy_v3_v3(miter_right_pt, curr_pt);
add_v2_v2(miter_right_pt, vec_miter_right);
}
else {
copy_v3_v3(miter_right_pt, curr_pt);
negate_v2(nvec_next);
add_v2_v2(miter_right_pt, nvec_next);
}
tPerimeterPoint *miter_right = new_perimeter_point(miter_right_pt);
BLI_addtail(perimeter_right_side, miter_right);
num_perimeter_points++;
}
/* bend to the right */
else {
normalize_v2_length(nvec_prev, -radius);
normalize_v2_length(nvec_next, -radius);
copy_v3_v3(nvec_prev_pt, curr_pt);
add_v2_v2(nvec_prev_pt, nvec_prev);
copy_v3_v3(nvec_next_pt, curr_pt);
add_v2_v2(nvec_next_pt, nvec_next);
tPerimeterPoint *normal_prev = new_perimeter_point(nvec_prev_pt);
tPerimeterPoint *normal_next = new_perimeter_point(nvec_next_pt);
BLI_addtail(perimeter_right_side, normal_prev);
BLI_addtail(perimeter_right_side, normal_next);
num_perimeter_points += 2;
num_perimeter_points += generate_arc_from_point_to_point(
perimeter_right_side, normal_prev, normal_next, curr_pt, subdivisions, false);
if (miter_length < prev_length && miter_length < next_length) {
copy_v3_v3(miter_left_pt, curr_pt);
add_v2_v2(miter_left_pt, vec_miter_left);
}
else {
copy_v3_v3(miter_left_pt, curr_pt);
negate_v2(nvec_prev);
add_v2_v2(miter_left_pt, nvec_prev);
}
tPerimeterPoint *miter_left = new_perimeter_point(miter_left_pt);
BLI_addtail(perimeter_left_side, miter_left);
num_perimeter_points++;
}
}
}
/* generate points for end cap */
num_perimeter_points += generate_perimeter_cap(
last_pt, last_prev_pt, last_radius, perimeter_right_side, subdivisions, gps->caps[1]);
/* merge both sides to one list */
BLI_listbase_reverse(perimeter_right_side);
BLI_movelisttolist(perimeter_left_side,
perimeter_right_side); // perimeter_left_side contains entire list
ListBase *perimeter_list = perimeter_left_side;
/* close by creating a point close to the first (make a small gap) */
float close_pt[3];
tPerimeterPoint *close_first = (tPerimeterPoint *)perimeter_list->first;
tPerimeterPoint *close_last = (tPerimeterPoint *)perimeter_list->last;
interp_v3_v3v3(close_pt, &close_last->x, &close_first->x, 0.99f);
if (compare_v3v3(close_pt, &close_first->x, FLT_EPSILON) == false) {
tPerimeterPoint *close_p_pt = new_perimeter_point(close_pt);
BLI_addtail(perimeter_list, close_p_pt);
num_perimeter_points++;
}
/* free temp data */
BLI_freelistN(perimeter_right_side);
MEM_freeN(perimeter_right_side);
*r_num_perimeter_points = num_perimeter_points;
return perimeter_list;
}
bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(float viewmat[4][4],
bGPdata *gpd,
const bGPDlayer *gpl,
bGPDstroke *gps,
const int subdivisions,
const float diff_mat[4][4],
const float thickness_chg)
{
if (gps->totpoints == 0) {
return nullptr;
}
float viewinv[4][4];
invert_m4_m4(viewinv, viewmat);
/* Duplicate only points and fill data. Weight and Curve are not needed. */
bGPDstroke *gps_temp = (bGPDstroke *)MEM_dupallocN(gps);
gps_temp->prev = gps_temp->next = nullptr;
gps_temp->triangles = (bGPDtriangle *)MEM_dupallocN(gps->triangles);
gps_temp->points = (bGPDspoint *)MEM_dupallocN(gps->points);
gps_temp->dvert = nullptr;
gps_temp->editcurve = nullptr;
const bool cyclic = ((gps_temp->flag & GP_STROKE_CYCLIC) != 0);
/* If Cyclic, add a new point. */
if (cyclic && (gps_temp->totpoints > 1)) {
gps_temp->totpoints++;
gps_temp->points = (bGPDspoint *)MEM_recallocN(
gps_temp->points, sizeof(*gps_temp->points) * gps_temp->totpoints);
bGPDspoint *pt_src = &gps_temp->points[0];
bGPDspoint *pt_dst = &gps_temp->points[gps_temp->totpoints - 1];
copy_v3_v3(&pt_dst->x, &pt_src->x);
pt_dst->pressure = pt_src->pressure;
pt_dst->strength = pt_src->strength;
pt_dst->uv_fac = 1.0f;
pt_dst->uv_rot = 0;
}
BKE_gpencil_stroke_to_view_space(gps_temp, viewmat, diff_mat);
int num_perimeter_points = 0;
ListBase *perimeter_points = gpencil_stroke_perimeter_ex(
gpd, gpl, gps_temp, subdivisions, thickness_chg, &num_perimeter_points);
if (num_perimeter_points == 0) {
return nullptr;
}
/* Create new stroke. */
bGPDstroke *perimeter_stroke = BKE_gpencil_stroke_new(gps_temp->mat_nr, num_perimeter_points, 1);
int i = 0;
LISTBASE_FOREACH_INDEX (tPerimeterPoint *, curr, perimeter_points, i) {
bGPDspoint *pt = &perimeter_stroke->points[i];
copy_v3_v3(&pt->x, &curr->x);
pt->pressure = 0.0f;
pt->strength = 1.0f;
pt->flag |= GP_SPOINT_SELECT;
}
BKE_gpencil_stroke_from_view_space(perimeter_stroke, viewinv, diff_mat);
/* Free temp data. */
BLI_freelistN(perimeter_points);
MEM_freeN(perimeter_points);
/* Triangles cache needs to be recalculated. */
BKE_gpencil_stroke_geometry_update(gpd, perimeter_stroke);
perimeter_stroke->flag |= GP_STROKE_SELECT | GP_STROKE_CYCLIC;
BKE_gpencil_free_stroke(gps_temp);
return perimeter_stroke;
}
float BKE_gpencil_stroke_average_pressure_get(bGPDstroke *gps)
{

View File

@@ -477,14 +477,6 @@ void BKE_gpencil_free_data(bGPdata *gpd, bool free_all)
}
}
void BKE_gpencil_eval_delete(bGPdata *gpd_eval)
{
BKE_gpencil_free_data(gpd_eval, true);
BKE_libblock_free_data(&gpd_eval->id, false);
BLI_assert(!gpd_eval->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(gpd_eval);
}
void BKE_gpencil_tag(bGPdata *gpd)
{
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -790,17 +782,6 @@ bGPDstroke *BKE_gpencil_stroke_add(
return gps;
}
bGPDstroke *BKE_gpencil_stroke_add_existing_style(
bGPDframe *gpf, bGPDstroke *existing, int mat_idx, int totpoints, short thickness)
{
bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, mat_idx, totpoints, thickness, false);
/* Copy run-time color data so that strokes added in the modifier has the style.
* There are depsgraph reference pointers inside,
* change the copy function if interfere with future drawing implementation. */
gps->runtime = blender::dna::shallow_copy(existing->runtime);
return gps;
}
bGPDcurve *BKE_gpencil_stroke_editcurve_new(const int tot_curve_points)
{
bGPDcurve *new_gp_curve = (bGPDcurve *)MEM_callocN(sizeof(bGPDcurve), __func__);
@@ -824,20 +805,9 @@ void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_d
BKE_defvert_array_copy(gps_dst->dvert, gps_src->dvert, gps_src->totpoints);
}
bGPDcurve *BKE_gpencil_stroke_curve_duplicate(bGPDcurve *gpc_src)
{
bGPDcurve *gpc_dst = static_cast<bGPDcurve *>(MEM_dupallocN(gpc_src));
if (gpc_src->curve_points != nullptr) {
gpc_dst->curve_points = static_cast<bGPDcurve_point *>(MEM_dupallocN(gpc_src->curve_points));
}
return gpc_dst;
}
bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src,
const bool dup_points,
const bool dup_curve)
const bool /*dup_curve*/)
{
bGPDstroke *gps_dst = nullptr;
@@ -861,12 +831,7 @@ bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src,
gps_dst->dvert = nullptr;
}
if (dup_curve && gps_src->editcurve != nullptr) {
gps_dst->editcurve = BKE_gpencil_stroke_curve_duplicate(gps_src->editcurve);
}
else {
gps_dst->editcurve = nullptr;
}
gps_dst->editcurve = nullptr;
/* return new stroke */
return gps_dst;
@@ -900,23 +865,6 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src, const bool dup_
return gpf_dst;
}
void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, bGPDframe *gpf_dst)
{
bGPDstroke *gps_dst = nullptr;
/* error checking */
if ((gpf_src == nullptr) || (gpf_dst == nullptr)) {
return;
}
/* copy strokes */
BLI_listbase_clear(&gpf_dst->strokes);
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
/* make copy of source stroke */
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
}
bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src,
const bool dup_frames,
const bool dup_strokes)
@@ -955,91 +903,6 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src,
return gpl_dst;
}
void BKE_gpencil_data_copy_settings(const bGPdata *gpd_src, bGPdata *gpd_dst)
{
gpd_dst->flag = gpd_src->flag;
gpd_dst->curve_edit_resolution = gpd_src->curve_edit_resolution;
gpd_dst->curve_edit_threshold = gpd_src->curve_edit_threshold;
gpd_dst->curve_edit_corner_angle = gpd_src->curve_edit_corner_angle;
gpd_dst->pixfactor = gpd_src->pixfactor;
copy_v4_v4(gpd_dst->line_color, gpd_src->line_color);
gpd_dst->onion_factor = gpd_src->onion_factor;
gpd_dst->onion_mode = gpd_src->onion_mode;
gpd_dst->onion_flag = gpd_src->onion_flag;
gpd_dst->gstep = gpd_src->gstep;
gpd_dst->gstep_next = gpd_src->gstep_next;
copy_v3_v3(gpd_dst->gcolor_prev, gpd_src->gcolor_prev);
copy_v3_v3(gpd_dst->gcolor_next, gpd_src->gcolor_next);
gpd_dst->zdepth_offset = gpd_src->zdepth_offset;
gpd_dst->totlayer = gpd_src->totlayer;
gpd_dst->totframe = gpd_src->totframe;
gpd_dst->totstroke = gpd_src->totstroke;
gpd_dst->totpoint = gpd_src->totpoint;
gpd_dst->draw_mode = gpd_src->draw_mode;
gpd_dst->onion_keytype = gpd_src->onion_keytype;
gpd_dst->select_last_index = gpd_src->select_last_index;
gpd_dst->vertex_group_active_index = gpd_src->vertex_group_active_index;
copy_v3_v3(gpd_dst->grid.color, gpd_src->grid.color);
copy_v2_v2(gpd_dst->grid.scale, gpd_src->grid.scale);
copy_v2_v2(gpd_dst->grid.offset, gpd_src->grid.offset);
gpd_dst->grid.lines = gpd_src->grid.lines;
}
void BKE_gpencil_layer_copy_settings(const bGPDlayer *gpl_src, bGPDlayer *gpl_dst)
{
gpl_dst->line_change = gpl_src->line_change;
copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
gpl_dst->opacity = gpl_src->opacity;
gpl_dst->vertex_paint_opacity = gpl_src->vertex_paint_opacity;
gpl_dst->pass_index = gpl_src->pass_index;
gpl_dst->parent = gpl_src->parent;
copy_m4_m4(gpl_dst->inverse, gpl_src->inverse);
STRNCPY(gpl_dst->parsubstr, gpl_src->parsubstr);
gpl_dst->partype = gpl_src->partype;
STRNCPY(gpl_dst->viewlayername, gpl_src->viewlayername);
copy_v3_v3(gpl_dst->location, gpl_src->location);
copy_v3_v3(gpl_dst->rotation, gpl_src->rotation);
copy_v3_v3(gpl_dst->scale, gpl_src->scale);
copy_m4_m4(gpl_dst->layer_mat, gpl_src->layer_mat);
copy_m4_m4(gpl_dst->layer_invmat, gpl_src->layer_invmat);
gpl_dst->blend_mode = gpl_src->blend_mode;
gpl_dst->flag = gpl_src->flag;
gpl_dst->onion_flag = gpl_src->onion_flag;
}
void BKE_gpencil_frame_copy_settings(const bGPDframe *gpf_src, bGPDframe *gpf_dst)
{
gpf_dst->flag = gpf_src->flag;
gpf_dst->key_type = gpf_src->key_type;
gpf_dst->framenum = gpf_src->framenum;
}
void BKE_gpencil_stroke_copy_settings(const bGPDstroke *gps_src, bGPDstroke *gps_dst)
{
gps_dst->thickness = gps_src->thickness;
gps_dst->flag = gps_src->flag;
gps_dst->inittime = gps_src->inittime;
gps_dst->mat_nr = gps_src->mat_nr;
copy_v2_v2_short(gps_dst->caps, gps_src->caps);
gps_dst->hardness = gps_src->hardness;
copy_v2_v2(gps_dst->aspect_ratio, gps_src->aspect_ratio);
gps_dst->fill_opacity_fac = gps_src->fill_opacity_fac;
copy_v3_v3(gps_dst->boundbox_min, gps_src->boundbox_min);
copy_v3_v3(gps_dst->boundbox_max, gps_src->boundbox_max);
gps_dst->uv_rotation = gps_src->uv_rotation;
copy_v2_v2(gps_dst->uv_translation, gps_src->uv_translation);
gps_dst->uv_scale = gps_src->uv_scale;
gps_dst->select_index = gps_src->select_index;
copy_v4_v4(gps_dst->vert_color_fill, gps_src->vert_color_fill);
}
bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool internal_copy)
{
bGPdata *gpd_dst;
@@ -1069,114 +932,6 @@ bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool in
return gpd_dst;
}
/* ************************************************** */
/* GP Stroke API */
void BKE_gpencil_stroke_sync_selection(bGPdata *gpd, bGPDstroke *gps)
{
bGPDspoint *pt;
int i;
/* error checking */
if (gps == nullptr) {
return;
}
/* we'll stop when we find the first selected point,
* so initially, we must deselect
*/
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
gps->flag |= GP_STROKE_SELECT;
break;
}
}
if (gps->flag & GP_STROKE_SELECT) {
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
}
void BKE_gpencil_curve_sync_selection(bGPdata *gpd, bGPDstroke *gps)
{
bGPDcurve *gpc = gps->editcurve;
if (gpc == nullptr) {
return;
}
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
gpc->flag &= ~GP_CURVE_SELECT;
bool is_selected = false;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
if (BEZT_ISSEL_ANY(bezt)) {
gpc_pt->flag |= GP_SPOINT_SELECT;
}
else {
gpc_pt->flag &= ~GP_SPOINT_SELECT;
}
if (gpc_pt->flag & GP_SPOINT_SELECT) {
is_selected = true;
}
}
if (is_selected) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
}
void BKE_gpencil_stroke_select_index_set(bGPdata *gpd, bGPDstroke *gps)
{
gpd->select_last_index++;
gps->select_index = gpd->select_last_index;
}
void BKE_gpencil_stroke_select_index_reset(bGPDstroke *gps)
{
gps->select_index = 0;
}
/* ************************************************** */
/* GP Frame API */
void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
{
bGPDstroke *gps = static_cast<bGPDstroke *>((gpf) ? gpf->strokes.last : nullptr);
int cfra = (gpf) ? gpf->framenum : 0; /* assume that the current frame was not locked */
/* error checking */
if (ELEM(nullptr, gpf, gps)) {
return;
}
/* free the stroke and its data */
if (gps->points) {
MEM_freeN(gps->points);
}
if (gps->dvert) {
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
}
MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps);
/* if frame has no strokes after this, delete it */
if (BLI_listbase_is_empty(&gpf->strokes)) {
BKE_gpencil_layer_frame_delete(gpl, gpf);
BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_USE_PREV);
}
}
/* ************************************************** */
/* GP Layer API */
@@ -1404,27 +1159,6 @@ bGPDlayer *BKE_gpencil_layer_named_get(bGPdata *gpd, const char *name)
return static_cast<bGPDlayer *>(BLI_findstring(&gpd->layers, name, offsetof(bGPDlayer, info)));
}
bGPDlayer_Mask *BKE_gpencil_layer_mask_named_get(bGPDlayer *gpl, const char *name)
{
if (name[0] == '\0') {
return nullptr;
}
return static_cast<bGPDlayer_Mask *>(
BLI_findstring(&gpl->mask_layers, name, offsetof(bGPDlayer_Mask, name)));
}
bGPDlayer_Mask *BKE_gpencil_layer_mask_add(bGPDlayer *gpl, const char *name)
{
bGPDlayer_Mask *mask = static_cast<bGPDlayer_Mask *>(
MEM_callocN(sizeof(bGPDlayer_Mask), "bGPDlayer_Mask"));
BLI_addtail(&gpl->mask_layers, mask);
STRNCPY(mask->name, name);
gpl->act_mask++;
return mask;
}
void BKE_gpencil_layer_mask_remove(bGPDlayer *gpl, bGPDlayer_Mask *mask)
{
BLI_freelinkN(&gpl->mask_layers, mask);
@@ -1506,13 +1240,6 @@ void BKE_gpencil_layer_mask_cleanup(bGPdata *gpd, bGPDlayer *gpl)
}
}
void BKE_gpencil_layer_mask_cleanup_all_layers(bGPdata *gpd)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
BKE_gpencil_layer_mask_cleanup(gpd, gpl);
}
}
static int gpencil_cb_cmp_frame(void *thunk, const void *a, const void *b)
{
const bGPDframe *frame_a = static_cast<const bGPDframe *>(a);
@@ -1557,27 +1284,6 @@ bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd)
return nullptr;
}
bGPDlayer *BKE_gpencil_layer_get_by_name(bGPdata *gpd, const char *name, int first_if_not_found)
{
/* error checking */
if (ELEM(nullptr, gpd, gpd->layers.first)) {
return nullptr;
}
/* loop over layers until found (assume only one active) */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (STREQ(name, gpl->info)) {
return gpl;
}
}
/* no such layer */
if (first_if_not_found) {
return static_cast<bGPDlayer *>(gpd->layers.first);
}
return nullptr;
}
void BKE_gpencil_layer_active_set(bGPdata *gpd, bGPDlayer *active)
{
/* error checking */
@@ -1652,19 +1358,6 @@ void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl)
BLI_freelinkN(&gpd->layers, gpl);
}
Material *BKE_gpencil_brush_material_get(Brush *brush)
{
Material *ma = nullptr;
if ((brush != nullptr) && (brush->gpencil_settings != nullptr) &&
(brush->gpencil_settings->material != nullptr))
{
ma = brush->gpencil_settings->material;
}
return ma;
}
void BKE_gpencil_brush_material_set(Brush *brush, Material *ma)
{
BLI_assert(brush);
@@ -1680,208 +1373,6 @@ void BKE_gpencil_brush_material_set(Brush *brush, Material *ma)
}
}
Material *BKE_gpencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush)
{
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
Material *ma = BKE_gpencil_brush_material_get(brush);
/* check if the material is already on object material slots and add it if missing */
if (ma && BKE_gpencil_object_material_index_get(ob, ma) < 0) {
BKE_object_material_slot_add(bmain, ob);
BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
}
return ma;
}
/* using active material instead */
return BKE_object_material_get(ob, ob->actcol);
}
int BKE_gpencil_object_material_ensure(Main *bmain, Object *ob, Material *material)
{
if (!material) {
return -1;
}
int index = BKE_gpencil_object_material_index_get(ob, material);
if (index < 0) {
BKE_object_material_slot_add(bmain, ob);
BKE_object_material_assign(bmain, ob, material, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
return ob->totcol - 1;
}
return index;
}
Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index)
{
Material *ma = BKE_gpencil_material_add(bmain, name);
id_us_min(&ma->id); /* no users yet */
BKE_object_material_slot_add(bmain, ob);
BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF);
if (r_index) {
*r_index = ob->actcol - 1;
}
return ma;
}
Material *BKE_gpencil_object_material_from_brush_get(Object *ob, Brush *brush)
{
if ((brush) && (brush->gpencil_settings) &&
(brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED))
{
Material *ma = BKE_gpencil_brush_material_get(brush);
return ma;
}
return BKE_object_material_get(ob, ob->actcol);
}
int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush)
{
if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) {
return BKE_gpencil_object_material_index_get(ob, brush->gpencil_settings->material);
}
return ob->actcol - 1;
}
Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main *bmain,
Object *ob,
ToolSettings *ts)
{
if (ts && ts->gp_paint && BKE_paint_brush(&ts->gp_paint->paint)) {
return BKE_gpencil_object_material_ensure_from_active_input_brush(
bmain, ob, BKE_paint_brush(&ts->gp_paint->paint));
}
return BKE_gpencil_object_material_ensure_from_active_input_brush(bmain, ob, nullptr);
}
Material *BKE_gpencil_object_material_ensure_from_active_input_brush(Main *bmain,
Object *ob,
Brush *brush)
{
if (brush) {
Material *ma = BKE_gpencil_object_material_ensure_from_brush(bmain, ob, brush);
if (ma) {
return ma;
}
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
/* it is easier to just unpin a nullptr material, instead of setting a new one */
brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
}
}
return BKE_gpencil_object_material_ensure_from_active_input_material(ob);
}
Material *BKE_gpencil_object_material_ensure_from_active_input_material(Object *ob)
{
Material *ma = BKE_object_material_get(ob, ob->actcol);
if (ma) {
return ma;
}
return BKE_material_default_gpencil();
}
Material *BKE_gpencil_object_material_ensure_active(Object *ob)
{
Material *ma = nullptr;
/* sanity checks */
if (ob == nullptr) {
return nullptr;
}
ma = BKE_gpencil_object_material_ensure_from_active_input_material(ob);
if (ma->gp_style == nullptr) {
BKE_gpencil_material_attr_init(ma);
}
return ma;
}
/* ************************************************** */
bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps)
{
const bGPDspoint *pt;
int i;
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
return true;
}
}
return false;
}
/* ************************************************** */
/* GP Object - Vertex Groups */
void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup)
{
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
MDeformVert *dvert = nullptr;
const int def_nr = BLI_findindex(&gpd->vertex_group_names, defgroup);
const int totgrp = BLI_listbase_count(&gpd->vertex_group_names);
/* Remove points data */
if (gpd) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->dvert != nullptr) {
for (int i = 0; i < gps->totpoints; i++) {
dvert = &gps->dvert[i];
MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
if (dw != nullptr) {
BKE_defvert_remove_group(dvert, dw);
}
/* Reorganize weights for other groups after deleted one. */
for (int g = 0; g < totgrp; g++) {
dw = BKE_defvert_find_index(dvert, g);
if ((dw != nullptr) && (dw->def_nr > def_nr)) {
dw->def_nr--;
}
}
}
}
}
}
}
}
/* Remove the group */
BLI_freelinkN(&gpd->vertex_group_names, defgroup);
/* Update the active deform index if necessary. */
const int active_index = BKE_object_defgroup_active_index_get(ob);
if (active_index > def_nr) {
BKE_object_defgroup_active_index_set(ob, active_index - 1);
}
/* Keep a valid active index if we still have some vertex groups. */
if (!BLI_listbase_is_empty(&gpd->vertex_group_names) &&
BKE_object_defgroup_active_index_get(ob) < 1)
{
BKE_object_defgroup_active_index_set(ob, 1);
}
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
void BKE_gpencil_dvert_ensure(bGPDstroke *gps)
{
if (gps->dvert == nullptr) {
gps->dvert = static_cast<MDeformVert *>(
MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"));
}
}
/* ************************************************** */
void BKE_gpencil_frame_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe)
{
*r_initframe = gpl->actframe->framenum;
@@ -1930,305 +1421,6 @@ float BKE_gpencil_multiframe_falloff_calc(
return value;
}
void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* reassign strokes */
if ((gps->mat_nr > index) || (gps->mat_nr > totcol - 1)) {
gps->mat_nr--;
CLAMP_MIN(gps->mat_nr, 0);
}
}
}
}
}
bool BKE_gpencil_material_index_used(bGPdata *gpd, int index)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->mat_nr == index) {
return true;
}
}
}
}
return false;
}
void BKE_gpencil_material_remap(bGPdata *gpd, const uint *remap, uint remap_len)
{
const short remap_len_short = short(remap_len);
#define MAT_NR_REMAP(n) \
if (n < remap_len_short) { \
BLI_assert(n >= 0 && remap[n] < remap_len_short); \
n = remap[n]; \
} \
((void)0)
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* reassign strokes */
MAT_NR_REMAP(gps->mat_nr);
}
}
}
#undef MAT_NR_REMAP
}
bool BKE_gpencil_merge_materials_table_get(Object *ob,
const float hue_threshold,
const float sat_threshold,
const float val_threshold,
GHash *r_mat_table)
{
bool changed = false;
Material *ma_primary = nullptr;
Material *ma_secondary = nullptr;
MaterialGPencilStyle *gp_style_primary = nullptr;
MaterialGPencilStyle *gp_style_secondary = nullptr;
GHash *mat_used = BLI_ghash_int_new(__func__);
short *totcol = BKE_object_material_len_p(ob);
if (totcol == nullptr) {
return changed;
}
for (int idx_primary = 0; idx_primary < *totcol; idx_primary++) {
/* Read primary material to compare. */
ma_primary = BKE_gpencil_material(ob, idx_primary + 1);
if (ma_primary == nullptr) {
continue;
}
for (int idx_secondary = 0; idx_secondary < *totcol; idx_secondary++) {
if ((idx_secondary == idx_primary) ||
BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary)))
{
continue;
}
if (BLI_ghash_haskey(mat_used, POINTER_FROM_INT(idx_secondary))) {
continue;
}
/* Read secondary material to compare with primary material. */
ma_secondary = BKE_gpencil_material(ob, idx_secondary + 1);
if ((ma_secondary == nullptr) ||
BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary)))
{
continue;
}
gp_style_primary = ma_primary->gp_style;
gp_style_secondary = ma_secondary->gp_style;
if ((gp_style_primary == nullptr) || (gp_style_secondary == nullptr) ||
(gp_style_secondary->flag & GP_MATERIAL_LOCKED))
{
continue;
}
/* Check materials have the same mode. */
if (gp_style_primary->mode != gp_style_secondary->mode) {
continue;
}
/* Check materials have same stroke and fill attributes. */
if ((gp_style_primary->flag & GP_MATERIAL_STROKE_SHOW) !=
(gp_style_secondary->flag & GP_MATERIAL_STROKE_SHOW))
{
continue;
}
if ((gp_style_primary->flag & GP_MATERIAL_FILL_SHOW) !=
(gp_style_secondary->flag & GP_MATERIAL_FILL_SHOW))
{
continue;
}
/* Check materials have the same type. */
if ((gp_style_primary->stroke_style != gp_style_secondary->stroke_style) ||
(gp_style_primary->fill_style != gp_style_secondary->fill_style))
{
continue;
}
float s_hsv_a[3], s_hsv_b[3], f_hsv_a[3], f_hsv_b[3], col[3];
zero_v3(s_hsv_a);
zero_v3(s_hsv_b);
zero_v3(f_hsv_a);
zero_v3(f_hsv_b);
copy_v3_v3(col, gp_style_primary->stroke_rgba);
rgb_to_hsv_compat_v(col, s_hsv_a);
copy_v3_v3(col, gp_style_secondary->stroke_rgba);
rgb_to_hsv_compat_v(col, s_hsv_b);
copy_v3_v3(col, gp_style_primary->fill_rgba);
rgb_to_hsv_compat_v(col, f_hsv_a);
copy_v3_v3(col, gp_style_secondary->fill_rgba);
rgb_to_hsv_compat_v(col, f_hsv_b);
/* Check stroke and fill color. */
if (!compare_ff(s_hsv_a[0], s_hsv_b[0], hue_threshold) ||
!compare_ff(s_hsv_a[1], s_hsv_b[1], sat_threshold) ||
!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold) ||
!compare_ff(f_hsv_a[0], f_hsv_b[0], hue_threshold) ||
!compare_ff(f_hsv_a[1], f_hsv_b[1], sat_threshold) ||
!compare_ff(f_hsv_a[2], f_hsv_b[2], val_threshold) ||
!compare_ff(gp_style_primary->stroke_rgba[3],
gp_style_secondary->stroke_rgba[3],
val_threshold) ||
!compare_ff(
gp_style_primary->fill_rgba[3], gp_style_secondary->fill_rgba[3], val_threshold))
{
continue;
}
/* Save conversion indexes. */
if (!BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary))) {
BLI_ghash_insert(
r_mat_table, POINTER_FROM_INT(idx_secondary), POINTER_FROM_INT(idx_primary));
changed = true;
if (!BLI_ghash_haskey(mat_used, POINTER_FROM_INT(idx_primary))) {
BLI_ghash_insert(mat_used, POINTER_FROM_INT(idx_primary), POINTER_FROM_INT(idx_primary));
}
}
}
}
/* Free hash memory. */
BLI_ghash_free(mat_used, nullptr, nullptr);
return changed;
}
bool BKE_gpencil_merge_materials(Object *ob,
const float hue_threshold,
const float sat_threshold,
const float val_threshold,
int *r_removed)
{
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
short *totcol = BKE_object_material_len_p(ob);
if (totcol == nullptr) {
*r_removed = 0;
return false;
}
/* Review materials. */
GHash *mat_table = BLI_ghash_int_new(__func__);
bool changed = BKE_gpencil_merge_materials_table_get(
ob, hue_threshold, sat_threshold, val_threshold, mat_table);
*r_removed = BLI_ghash_len(mat_table);
/* Update stroke material index. */
if (changed) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->flag & GP_LAYER_HIDE) {
continue;
}
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* Check if the color is editable. */
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
if (gp_style != nullptr) {
if (gp_style->flag & GP_MATERIAL_HIDE) {
continue;
}
if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) &&
(gp_style->flag & GP_MATERIAL_LOCKED))
{
continue;
}
}
if (BLI_ghash_haskey(mat_table, POINTER_FROM_INT(gps->mat_nr))) {
int *idx = static_cast<int *>(
BLI_ghash_lookup(mat_table, POINTER_FROM_INT(gps->mat_nr)));
gps->mat_nr = POINTER_AS_INT(idx);
}
}
}
}
}
/* Free hash memory. */
BLI_ghash_free(mat_table, nullptr, nullptr);
return changed;
}
void BKE_gpencil_stats_update(bGPdata *gpd)
{
gpd->totlayer = 0;
gpd->totframe = 0;
gpd->totstroke = 0;
gpd->totpoint = 0;
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
gpd->totlayer++;
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
gpd->totframe++;
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
gpd->totstroke++;
gpd->totpoint += gps->totpoints;
}
}
}
}
int BKE_gpencil_object_material_index_get(Object *ob, Material *ma)
{
short *totcol = BKE_object_material_len_p(ob);
Material *read_ma = nullptr;
for (short i = 0; i < *totcol; i++) {
read_ma = BKE_object_material_get(ob, i + 1);
if (ma == read_ma) {
return i;
}
}
return -1;
}
int BKE_gpencil_object_material_index_get_by_name(Object *ob, const char *name)
{
short *totcol = BKE_object_material_len_p(ob);
Material *read_ma = nullptr;
for (short i = 0; i < *totcol; i++) {
read_ma = BKE_object_material_get(ob, i + 1);
if (STREQ(name, read_ma->id.name + 2)) {
return i;
}
}
return -1;
}
Material *BKE_gpencil_object_material_ensure_by_name(Main *bmain,
Object *ob,
const char *name,
int *r_index)
{
int index = BKE_gpencil_object_material_index_get_by_name(ob, name);
if (index != -1) {
*r_index = index;
return BKE_object_material_get(ob, index + 1);
}
return BKE_gpencil_object_material_new(bmain, ob, name, r_index);
}
void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
{
const char *hexcol[] = {
@@ -2278,71 +1470,6 @@ void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
BKE_paint_palette_set(&ts->gp_paint->paint, palette);
BKE_paint_palette_set(&ts->gp_vertexpaint->paint, palette);
}
bool BKE_gpencil_from_image(
SpaceImage *sima, bGPdata *gpd, bGPDframe *gpf, const float size, const bool mask)
{
Image *image = sima->image;
bool done = false;
if (image == nullptr) {
return false;
}
ImageUser iuser = sima->iuser;
void *lock;
ImBuf *ibuf;
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
if (ibuf && ibuf->byte_buffer.data) {
int img_x = ibuf->x;
int img_y = ibuf->y;
float color[4];
bGPDspoint *pt;
for (int row = 0; row < img_y; row++) {
/* Create new stroke */
bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, 0, img_x, size * 1000, false);
done = true;
for (int col = 0; col < img_x; col++) {
IMB_sampleImageAtLocation(ibuf, col, row, true, color);
pt = &gps->points[col];
pt->pressure = 1.0f;
pt->x = col * size;
pt->z = row * size;
if (!mask) {
copy_v3_v3(pt->vert_color, color);
pt->vert_color[3] = 1.0f;
pt->strength = color[3];
}
else {
zero_v3(pt->vert_color);
pt->vert_color[3] = 1.0f;
pt->strength = 1.0f - color[3];
}
/* Select Alpha points. */
if (pt->strength < 0.03f) {
gps->flag |= GP_STROKE_SELECT;
pt->flag |= GP_SPOINT_SELECT;
}
}
if (gps->flag & GP_STROKE_SELECT) {
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
/* Free memory. */
BKE_image_release_ibuf(image, ibuf, lock);
return done;
}
/**
* Helper to check if a layers is used as mask
* \param view_layer: Actual view layer.
@@ -2372,46 +1499,6 @@ static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer
return false;
}
/* -------------------------------------------------------------------- */
/** \name Iterator
*
* Iterate over all visible stroke of all visible layers inside a grease pencil datablock.
* \{ */
void BKE_gpencil_visible_stroke_iter(bGPdata *gpd,
gpIterCb layer_cb,
gpIterCb stroke_cb,
void *thunk)
{
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->flag & GP_LAYER_HIDE) {
continue;
}
/* If scale to 0 the layer must be invisible. */
if (is_zero_v3(gpl->scale)) {
continue;
}
bGPDframe *act_gpf = gpl->actframe;
if (layer_cb) {
layer_cb(gpl, act_gpf, nullptr, thunk);
}
if (act_gpf) {
LISTBASE_FOREACH (bGPDstroke *, gps, &act_gpf->strokes) {
if (gps->totpoints == 0) {
continue;
}
stroke_cb(gpl, act_gpf, gps, thunk);
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Advanced Iterator
*
@@ -2856,198 +1943,4 @@ int BKE_gpencil_material_find_index_by_name_prefix(Object *ob, const char *name_
return -1;
}
void BKE_gpencil_frame_selected_hash(bGPdata *gpd, GHash *r_list)
{
const bool is_multiedit = bool(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd));
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
LISTBASE_FOREACH (bGPDlayer *, gpl_iter, &gpd->layers) {
if ((gpl != nullptr) && (!is_multiedit) && (gpl != gpl_iter)) {
continue;
}
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_iter->frames) {
if (((gpf == gpl->actframe) && (!is_multiedit)) ||
((gpf->flag & GP_FRAME_SELECT) && (is_multiedit)))
{
if (!BLI_ghash_lookup(r_list, POINTER_FROM_INT(gpf->framenum))) {
BLI_ghash_insert(r_list, POINTER_FROM_INT(gpf->framenum), gpf);
}
}
}
}
}
bool BKE_gpencil_can_avoid_full_copy_on_write(const Depsgraph *depsgraph, bGPdata *gpd)
{
/* For now, we only use the update cache in the active depsgraph. Otherwise we might access the
* cache while another depsgraph frees it. */
if (!DEG_is_active(depsgraph)) {
return false;
}
GPencilUpdateCache *update_cache = gpd->runtime.update_cache;
return update_cache != nullptr && update_cache->flag != GP_UPDATE_NODE_FULL_COPY;
}
struct tGPencilUpdateOnWriteTraverseData {
bGPdata *gpd_eval;
bGPDlayer *gpl_eval;
bGPDframe *gpf_eval;
bGPDstroke *gps_eval;
int gpl_index;
int gpf_index;
int gps_index;
};
static bool gpencil_update_on_write_layer_cb(GPencilUpdateCache *gpl_cache, void *user_data)
{
tGPencilUpdateOnWriteTraverseData *td = (tGPencilUpdateOnWriteTraverseData *)user_data;
td->gpl_eval = static_cast<bGPDlayer *>(
BLI_findlinkfrom((Link *)td->gpl_eval, gpl_cache->index - td->gpl_index));
td->gpl_index = gpl_cache->index;
bGPDlayer *gpl = (bGPDlayer *)gpl_cache->data;
if (gpl_cache->flag == GP_UPDATE_NODE_FULL_COPY) {
bGPDlayer *gpl_eval_next = td->gpl_eval->next;
BLI_assert(gpl != nullptr);
BKE_gpencil_layer_delete(td->gpd_eval, td->gpl_eval);
td->gpl_eval = BKE_gpencil_layer_duplicate(gpl, true, true);
BLI_insertlinkbefore(&td->gpd_eval->layers, gpl_eval_next, td->gpl_eval);
BKE_gpencil_layer_original_pointers_update(gpl, td->gpl_eval);
td->gpl_eval->runtime.gpl_orig = gpl;
return true;
}
if (gpl_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) {
BLI_assert(gpl != nullptr);
BKE_gpencil_layer_copy_settings(gpl, td->gpl_eval);
td->gpl_eval->runtime.gpl_orig = gpl;
}
td->gpf_eval = static_cast<bGPDframe *>(td->gpl_eval->frames.first);
td->gpf_index = 0;
return false;
}
static bool gpencil_update_on_write_frame_cb(GPencilUpdateCache *gpf_cache, void *user_data)
{
tGPencilUpdateOnWriteTraverseData *td = (tGPencilUpdateOnWriteTraverseData *)user_data;
td->gpf_eval = static_cast<bGPDframe *>(
BLI_findlinkfrom((Link *)td->gpf_eval, gpf_cache->index - td->gpf_index));
td->gpf_index = gpf_cache->index;
bGPDframe *gpf = (bGPDframe *)gpf_cache->data;
if (gpf_cache->flag == GP_UPDATE_NODE_FULL_COPY) {
/* Do a full copy of the frame. */
bGPDframe *gpf_eval_next = td->gpf_eval->next;
BLI_assert(gpf != nullptr);
bool update_actframe = (td->gpl_eval->actframe == td->gpf_eval) ? true : false;
BKE_gpencil_free_strokes(td->gpf_eval);
BLI_freelinkN(&td->gpl_eval->frames, td->gpf_eval);
td->gpf_eval = BKE_gpencil_frame_duplicate(gpf, true);
BLI_insertlinkbefore(&td->gpl_eval->frames, gpf_eval_next, td->gpf_eval);
BKE_gpencil_frame_original_pointers_update(gpf, td->gpf_eval);
td->gpf_eval->runtime.gpf_orig = gpf;
if (update_actframe) {
td->gpl_eval->actframe = td->gpf_eval;
}
return true;
}
if (gpf_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) {
BLI_assert(gpf != nullptr);
BKE_gpencil_frame_copy_settings(gpf, td->gpf_eval);
td->gpf_eval->runtime.gpf_orig = gpf;
}
td->gps_eval = static_cast<bGPDstroke *>(td->gpf_eval->strokes.first);
td->gps_index = 0;
return false;
}
static bool gpencil_update_on_write_stroke_cb(GPencilUpdateCache *gps_cache, void *user_data)
{
tGPencilUpdateOnWriteTraverseData *td = (tGPencilUpdateOnWriteTraverseData *)user_data;
td->gps_eval = static_cast<bGPDstroke *>(
BLI_findlinkfrom((Link *)td->gps_eval, gps_cache->index - td->gps_index));
td->gps_index = gps_cache->index;
bGPDstroke *gps = (bGPDstroke *)gps_cache->data;
if (gps_cache->flag == GP_UPDATE_NODE_FULL_COPY) {
/* Do a full copy of the stroke. */
bGPDstroke *gps_eval_next = td->gps_eval->next;
BLI_assert(gps != nullptr);
BLI_remlink(&td->gpf_eval->strokes, td->gps_eval);
BKE_gpencil_free_stroke(td->gps_eval);
td->gps_eval = BKE_gpencil_stroke_duplicate(gps, true, true);
BLI_insertlinkbefore(&td->gpf_eval->strokes, gps_eval_next, td->gps_eval);
td->gps_eval->runtime.gps_orig = gps;
/* Assign original pt pointers. */
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt_orig = &gps->points[i];
bGPDspoint *pt_eval = &td->gps_eval->points[i];
pt_orig->runtime.pt_orig = nullptr;
pt_orig->runtime.idx_orig = i;
pt_eval->runtime.pt_orig = pt_orig;
pt_eval->runtime.idx_orig = i;
}
}
else if (gps_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) {
BLI_assert(gps != nullptr);
BKE_gpencil_stroke_copy_settings(gps, td->gps_eval);
td->gps_eval->runtime.gps_orig = gps;
}
return false;
}
void BKE_gpencil_update_on_write(bGPdata *gpd_orig, bGPdata *gpd_eval)
{
GPencilUpdateCache *update_cache = gpd_orig->runtime.update_cache;
/* We assume that a full copy is not needed and the update cache is populated. */
if (update_cache == nullptr || update_cache->flag == GP_UPDATE_NODE_FULL_COPY) {
return;
}
if (update_cache->flag == GP_UPDATE_NODE_LIGHT_COPY) {
BKE_gpencil_data_copy_settings(gpd_orig, gpd_eval);
}
GPencilUpdateCacheTraverseSettings ts = {{
gpencil_update_on_write_layer_cb,
gpencil_update_on_write_frame_cb,
gpencil_update_on_write_stroke_cb,
}};
tGPencilUpdateOnWriteTraverseData data{};
data.gpd_eval = gpd_eval;
data.gpl_eval = static_cast<bGPDlayer *>(gpd_eval->layers.first);
data.gpf_eval = nullptr;
data.gps_eval = nullptr;
data.gpl_index = 0;
data.gpf_index = 0;
data.gps_index = 0;
BKE_gpencil_traverse_update_cache(update_cache, &ts, &data);
gpd_eval->flag |= GP_DATA_CACHE_IS_DIRTY;
/* TODO: This might cause issues when we have multiple depsgraphs? */
BKE_gpencil_free_update_cache(gpd_orig);
}
/** \} */

View File

@@ -419,29 +419,6 @@ static void gpencil_modifier_foreach_ID_link(GpencilModifierData *md,
/* *************************************************** */
/* Modifier Methods - Evaluation Loops, etc. */
void BKE_gpencil_frame_active_set(Depsgraph *depsgraph, bGPdata *gpd)
{
DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd);
int ctime = int(DEG_get_ctime(depsgraph));
/* update active frame */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, ctime, GP_GETFRAME_USE_PREV);
}
if (DEG_is_active(depsgraph)) {
bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id);
/* sync "actframe" changes back to main-db too,
* so that editing tools work with copy-on-evaluation
* when the current frame changes
*/
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, ctime, GP_GETFRAME_USE_PREV);
}
}
}
static void modifier_free_data_id_us_cb(void * /*user_data*/,
Object * /*ob*/,
ID **idpoin,

View File

@@ -29,24 +29,6 @@ static GPencilUpdateCache *update_cache_alloc(int index, int flag, void *data)
return new_cache;
}
static short cache_node_compare(void *node, void *data)
{
int index_a = ((GPencilUpdateCacheNode *)node)->cache->index;
int index_b = ((GPencilUpdateCache *)data)->index;
if (index_a == index_b) {
return 0;
}
return index_a < index_b ? 1 : -1;
}
static DLRBT_Node *cache_node_alloc(void *data)
{
GPencilUpdateCacheNode *new_node = static_cast<GPencilUpdateCacheNode *>(
MEM_callocN(sizeof(GPencilUpdateCacheNode), __func__));
new_node->cache = ((GPencilUpdateCache *)data);
return (DLRBT_Node *)new_node;
}
static void cache_node_free(void *node);
static void update_cache_free(GPencilUpdateCache *cache)
@@ -65,136 +47,6 @@ static void cache_node_free(void *node)
MEM_freeN(node);
}
static void cache_node_update(void *node, void *data)
{
GPencilUpdateCache *update_cache = ((GPencilUpdateCacheNode *)node)->cache;
GPencilUpdateCache *new_update_cache = (GPencilUpdateCache *)data;
/* If the new cache is already "covered" by the current cache, just free it and return. */
if (new_update_cache->flag <= update_cache->flag) {
update_cache_free(new_update_cache);
return;
}
update_cache->data = new_update_cache->data;
update_cache->flag = new_update_cache->flag;
/* In case the new cache does a full update, remove its children since they will be all
* updated by this cache. */
if (new_update_cache->flag == GP_UPDATE_NODE_FULL_COPY) {
BLI_dlrbTree_free(update_cache->children, cache_node_free);
}
update_cache_free(new_update_cache);
}
static void update_cache_node_create_ex(GPencilUpdateCache *root_cache,
void *data,
int gpl_index,
int gpf_index,
int gps_index,
bool full_copy)
{
if (root_cache->flag == GP_UPDATE_NODE_FULL_COPY) {
/* Entire data-block has to be recalculated, e.g. nothing else needs to be added to the cache.
*/
return;
}
const int node_flag = full_copy ? GP_UPDATE_NODE_FULL_COPY : GP_UPDATE_NODE_LIGHT_COPY;
if (gpl_index == -1) {
root_cache->data = (bGPdata *)data;
root_cache->flag = node_flag;
if (full_copy) {
/* Entire data-block has to be recalculated, remove all caches of "lower" elements. */
BLI_dlrbTree_free(root_cache->children, cache_node_free);
}
return;
}
const bool is_layer_update_node = (gpf_index == -1);
/* If the data pointer in #GPencilUpdateCache is nullptr, this element is not actually cached
* and does not need to be updated, but we do need the index to find elements that are in
* levels below. E.g. if a stroke needs to be updated, the frame it is in would not hold a
* pointer to it's data. */
GPencilUpdateCache *gpl_cache = update_cache_alloc(
gpl_index,
is_layer_update_node ? node_flag : GP_UPDATE_NODE_NO_COPY,
is_layer_update_node ? (bGPDlayer *)data : nullptr);
GPencilUpdateCacheNode *gpl_node = (GPencilUpdateCacheNode *)BLI_dlrbTree_add(
root_cache->children, cache_node_compare, cache_node_alloc, cache_node_update, gpl_cache);
BLI_dlrbTree_linkedlist_sync(root_cache->children);
if (gpl_node->cache->flag == GP_UPDATE_NODE_FULL_COPY || is_layer_update_node) {
return;
}
const bool is_frame_update_node = (gps_index == -1);
GPencilUpdateCache *gpf_cache = update_cache_alloc(
gpf_index,
is_frame_update_node ? node_flag : GP_UPDATE_NODE_NO_COPY,
is_frame_update_node ? (bGPDframe *)data : nullptr);
GPencilUpdateCacheNode *gpf_node = (GPencilUpdateCacheNode *)BLI_dlrbTree_add(
gpl_node->cache->children,
cache_node_compare,
cache_node_alloc,
cache_node_update,
gpf_cache);
BLI_dlrbTree_linkedlist_sync(gpl_node->cache->children);
if (gpf_node->cache->flag == GP_UPDATE_NODE_FULL_COPY || is_frame_update_node) {
return;
}
GPencilUpdateCache *gps_cache = update_cache_alloc(gps_index, node_flag, (bGPDstroke *)data);
BLI_dlrbTree_add(gpf_node->cache->children,
cache_node_compare,
cache_node_alloc,
cache_node_update,
gps_cache);
BLI_dlrbTree_linkedlist_sync(gpf_node->cache->children);
}
static void update_cache_node_create(
bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, bool full_copy)
{
if (gpd == nullptr) {
return;
}
GPencilUpdateCache *root_cache = gpd->runtime.update_cache;
if (root_cache == nullptr) {
gpd->runtime.update_cache = update_cache_alloc(0, GP_UPDATE_NODE_NO_COPY, nullptr);
root_cache = gpd->runtime.update_cache;
}
if (root_cache->flag == GP_UPDATE_NODE_FULL_COPY) {
/* Entire data-block has to be recalculated, e.g. nothing else needs to be added to the cache.
*/
return;
}
const int gpl_index = (gpl != nullptr) ? BLI_findindex(&gpd->layers, gpl) : -1;
const int gpf_index = (gpl != nullptr && gpf != nullptr) ? BLI_findindex(&gpl->frames, gpf) : -1;
const int gps_index = (gpf != nullptr && gps != nullptr) ? BLI_findindex(&gpf->strokes, gps) :
-1;
void *data = gps;
if (!data) {
data = gpf;
}
if (!data) {
data = gpl;
}
if (!data) {
data = gpd;
}
update_cache_node_create_ex(root_cache, data, gpl_index, gpf_index, gps_index, full_copy);
}
static void gpencil_traverse_update_cache_ex(GPencilUpdateCache *parent_cache,
GPencilUpdateCacheTraverseSettings *ts,
int depth,
@@ -237,16 +89,6 @@ void BKE_gpencil_traverse_update_cache(GPencilUpdateCache *cache,
gpencil_traverse_update_cache_ex(cache, ts, 0, user_data);
}
void BKE_gpencil_tag_full_update(bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps)
{
update_cache_node_create(gpd, gpl, gpf, gps, true);
}
void BKE_gpencil_tag_light_update(bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps)
{
update_cache_node_create(gpd, gpl, gpf, gps, false);
}
void BKE_gpencil_free_update_cache(bGPdata *gpd)
{
GPencilUpdateCache *gpd_cache = gpd->runtime.update_cache;

View File

@@ -511,8 +511,6 @@ bool BKE_object_material_slot_used(Object *object, short actcol)
case ID_MB:
/* Meta-elements don't support materials at the moment. */
return false;
case ID_GD_LEGACY:
return BKE_gpencil_material_index_used((bGPdata *)ob_data, actcol - 1);
case ID_GP:
return BKE_grease_pencil_material_index_used(reinterpret_cast<GreasePencil *>(ob_data),
actcol - 1);
@@ -1172,9 +1170,6 @@ void BKE_object_material_remap(Object *ob, const uint *remap)
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
BKE_curve_material_remap(static_cast<Curve *>(ob->data), remap, ob->totcol);
}
else if (ob->type == OB_GPENCIL_LEGACY) {
BKE_gpencil_material_remap(static_cast<bGPdata *>(ob->data), remap, ob->totcol);
}
else if (ob->type == OB_GREASE_PENCIL) {
BKE_grease_pencil_material_remap(static_cast<GreasePencil *>(ob->data), remap, ob->totcol);
}
@@ -1427,10 +1422,6 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob)
BKE_displist_free(&ob->runtime->curve_cache->disp);
}
}
/* check indices from gpencil legacy. */
else if (ob->type == OB_GPENCIL_LEGACY) {
BKE_gpencil_material_index_reassign((bGPdata *)ob->data, ob->totcol, actcol - 1);
}
return true;
}

View File

@@ -1667,12 +1667,6 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_crazyspace_api_eval_clear(ob);
/* Clear grease pencil data. */
if (ob->runtime->gpd_eval != nullptr) {
BKE_gpencil_eval_delete(ob->runtime->gpd_eval);
ob->runtime->gpd_eval = nullptr;
}
if (ob->runtime->geometry_set_eval != nullptr) {
delete ob->runtime->geometry_set_eval;
ob->runtime->geometry_set_eval = nullptr;
@@ -3865,30 +3859,6 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
return ok;
}
struct GPencilStrokePointIterData {
const float (*obmat)[4];
void (*point_func_cb)(const float co[3], void *user_data);
void *user_data;
};
static void foreach_display_point_gpencil_stroke_fn(bGPDlayer * /*layer*/,
bGPDframe * /*frame*/,
bGPDstroke *stroke,
void *thunk)
{
GPencilStrokePointIterData *iter_data = (GPencilStrokePointIterData *)thunk;
{
bGPDspoint *pt;
int i;
for (i = 0, pt = stroke->points; i < stroke->totpoints; i++, pt++) {
float3 co;
mul_v3_m4v3(co, iter_data->obmat, &pt->x);
iter_data->point_func_cb(co, iter_data->user_data);
}
}
}
void BKE_object_foreach_display_point(Object *ob,
const float obmat[4][4],
void (*func_cb)(const float[3], void *),
@@ -3905,15 +3875,6 @@ void BKE_object_foreach_display_point(Object *ob,
func_cb(co, user_data);
}
}
else if (ob->type == OB_GPENCIL_LEGACY) {
GPencilStrokePointIterData iter_data{};
iter_data.obmat = obmat;
iter_data.point_func_cb = func_cb;
iter_data.user_data = user_data;
BKE_gpencil_visible_stroke_iter(
(bGPdata *)ob->data, nullptr, foreach_display_point_gpencil_stroke_fn, &iter_data);
}
else if (ob->runtime->curve_cache && ob->runtime->curve_cache->disp.first) {
LISTBASE_FOREACH (DispList *, dl, &ob->runtime->curve_cache->disp) {
const float *v3 = dl->verts;
@@ -4880,7 +4841,6 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int /*flag*/)
{
blender::bke::ObjectRuntime *runtime = object->runtime;
runtime->data_eval = nullptr;
runtime->gpd_eval = nullptr;
runtime->mesh_deform_eval = nullptr;
runtime->curve_cache = nullptr;
runtime->object_as_temp_mesh = nullptr;

View File

@@ -390,24 +390,19 @@ static void object_defgroup_remove_edit_mode(Object *ob, bDeformGroup *dg)
void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup)
{
if (ob->type == OB_GPENCIL_LEGACY) {
BKE_gpencil_vgroup_remove(ob, defgroup);
if (BKE_object_is_in_editmode_vgroup(ob)) {
object_defgroup_remove_edit_mode(ob, defgroup);
}
else {
if (BKE_object_is_in_editmode_vgroup(ob)) {
object_defgroup_remove_edit_mode(ob, defgroup);
}
else {
object_defgroup_remove_object_mode(ob, defgroup);
}
if (ob->type == OB_GREASE_PENCIL) {
blender::bke::greasepencil::validate_drawing_vertex_groups(
*static_cast<GreasePencil *>(ob->data));
}
BKE_object_batch_cache_dirty_tag(ob);
object_defgroup_remove_object_mode(ob, defgroup);
}
if (ob->type == OB_GREASE_PENCIL) {
blender::bke::greasepencil::validate_drawing_vertex_groups(
*static_cast<GreasePencil *>(ob->data));
}
BKE_object_batch_cache_dirty_tag(ob);
}
void BKE_object_defgroup_remove_all_ex(Object *ob, bool only_unlocked)

View File

@@ -282,9 +282,6 @@ void BKE_object_batch_cache_dirty_tag(Object *ob)
}
break;
}
case OB_GPENCIL_LEGACY:
BKE_gpencil_batch_cache_dirty_tag((bGPdata *)ob->data);
break;
case OB_CURVES:
BKE_curves_batch_cache_dirty_tag((Curves *)ob->data, BKE_CURVES_BATCH_DIRTY_ALL);
break;

View File

@@ -56,7 +56,6 @@ set(SRC
intern/eval/deg_eval_flush.cc
intern/eval/deg_eval_runtime_backup.cc
intern/eval/deg_eval_runtime_backup_animation.cc
intern/eval/deg_eval_runtime_backup_gpencil.cc
intern/eval/deg_eval_runtime_backup_modifier.cc
intern/eval/deg_eval_runtime_backup_movieclip.cc
intern/eval/deg_eval_runtime_backup_object.cc
@@ -126,7 +125,6 @@ set(SRC
intern/eval/deg_eval_flush.h
intern/eval/deg_eval_runtime_backup.h
intern/eval/deg_eval_runtime_backup_animation.h
intern/eval/deg_eval_runtime_backup_gpencil.h
intern/eval/deg_eval_runtime_backup_modifier.h
intern/eval/deg_eval_runtime_backup_movieclip.h
intern/eval/deg_eval_runtime_backup_object.h

View File

@@ -1783,18 +1783,6 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
break;
}
case ID_GD_LEGACY: {
/* GPencil evaluation operations. */
op_node = add_operation_node(obdata,
NodeType::GEOMETRY,
OperationCode::GEOMETRY_EVAL,
[obdata_cow](::Depsgraph *depsgraph) {
BKE_gpencil_frame_active_set(depsgraph,
(bGPdata *)obdata_cow);
});
op_node->set_as_entry();
break;
}
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(obdata);

View File

@@ -888,14 +888,6 @@ ID *deg_update_eval_copy_datablock(const Depsgraph *depsgraph, const IDNode *id_
update_edit_mode_pointers(depsgraph, id_orig, id_cow);
return id_cow;
}
/* In case we don't need to do a copy-on-evaluation, we can use the update cache of the grease
* pencil data to do an update-on-write. */
if (id_type == ID_GD_LEGACY && BKE_gpencil_can_avoid_full_copy_on_write(
(const ::Depsgraph *)depsgraph, (bGPdata *)id_orig))
{
BKE_gpencil_update_on_write((bGPdata *)id_orig, (bGPdata *)id_cow);
return id_cow;
}
}
RuntimeBackup backup(depsgraph);

View File

@@ -25,8 +25,7 @@ RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
object_backup(depsgraph),
drawdata_ptr(nullptr),
movieclip_backup(depsgraph),
volume_backup(depsgraph),
gpencil_backup(depsgraph)
volume_backup(depsgraph)
{
drawdata_backup.first = drawdata_backup.last = nullptr;
}
@@ -61,9 +60,6 @@ void RuntimeBackup::init_from_id(ID *id)
case ID_VO:
volume_backup.init_from_volume(reinterpret_cast<Volume *>(id));
break;
case ID_GD_LEGACY:
gpencil_backup.init_from_gpencil(reinterpret_cast<bGPdata *>(id));
break;
default:
break;
}
@@ -104,9 +100,6 @@ void RuntimeBackup::restore_to_id(ID *id)
case ID_VO:
volume_backup.restore_to_volume(reinterpret_cast<Volume *>(id));
break;
case ID_GD_LEGACY:
gpencil_backup.restore_to_gpencil(reinterpret_cast<bGPdata *>(id));
break;
default:
break;
}

View File

@@ -11,7 +11,6 @@
#include "DNA_ID.h"
#include "intern/eval/deg_eval_runtime_backup_animation.h"
#include "intern/eval/deg_eval_runtime_backup_gpencil.h"
#include "intern/eval/deg_eval_runtime_backup_movieclip.h"
#include "intern/eval/deg_eval_runtime_backup_object.h"
#include "intern/eval/deg_eval_runtime_backup_scene.h"
@@ -56,7 +55,6 @@ class RuntimeBackup {
DrawDataList *drawdata_ptr;
MovieClipBackup movieclip_backup;
VolumeBackup volume_backup;
GPencilBackup gpencil_backup;
};
} // namespace blender::deg

View File

@@ -1,40 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_gpencil.h"
#include "intern/depsgraph.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_gpencil_update_cache_legacy.h"
#include "DNA_gpencil_legacy_types.h"
namespace blender::deg {
GPencilBackup::GPencilBackup(const Depsgraph *depsgraph) : depsgraph(depsgraph) {}
void GPencilBackup::init_from_gpencil(bGPdata * /*gpd*/) {}
void GPencilBackup::restore_to_gpencil(bGPdata *gpd)
{
bGPdata *gpd_orig = reinterpret_cast<bGPdata *>(gpd->id.orig_id);
/* We check for the active depsgraph here to avoid freeing the cache on the original object
* multiple times. This free is only needed for the case where we tagged a full update in the
* update cache and did not do an update-on-write. */
if (depsgraph->is_active) {
BKE_gpencil_free_update_cache(gpd_orig);
}
/* Doing a copy-on-evaluation copies the update cache pointer. Make sure to reset it
* to null as we should never use the update cache from eval data. */
gpd->runtime.update_cache = nullptr;
/* Make sure to update the original runtime pointers in the eval data. */
BKE_gpencil_data_update_orig_pointers(gpd_orig, gpd);
}
} // namespace blender::deg

View File

@@ -1,28 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
struct bGPdata;
namespace blender::deg {
struct Depsgraph;
/* Backup of volume datablocks runtime data. */
class GPencilBackup {
public:
GPencilBackup(const Depsgraph *depsgraph);
void init_from_gpencil(bGPdata *gpd);
void restore_to_gpencil(bGPdata *gpd);
const Depsgraph *depsgraph;
};
} // namespace blender::deg

View File

@@ -294,9 +294,6 @@ void Instance::object_sync(Object *ob)
case OB_CURVES:
sync.sync_curves(ob, ob_handle, res_handle, ob_ref);
break;
case OB_GREASE_PENCIL:
sync.sync_gpencil(ob, ob_handle, res_handle);
break;
case OB_LIGHTPROBE:
light_probes.sync_probe(ob, ob_handle);
break;

View File

@@ -393,149 +393,6 @@ void SyncModule::sync_volume(Object *ob,
/** \} */
/* -------------------------------------------------------------------- */
/** \name GPencil
* \{ */
#define DO_BATCHING true
struct gpIterData {
Instance &inst;
Object *ob;
MaterialArray &material_array;
int cfra;
/* Drawcall batching. */
gpu::Batch *geom = nullptr;
Material *material = nullptr;
int vfirst = 0;
int vcount = 0;
bool instancing = false;
gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle, ResourceHandle resource_handle)
: inst(inst_),
ob(ob_),
material_array(inst_.materials.material_array_get(
ob_,
inst_.velocity.step_object_sync(
ob, ob_handle.object_key, resource_handle, ob_handle.recalc)))
{
cfra = DEG_get_ctime(inst.depsgraph);
};
};
static void gpencil_drawcall_flush(gpIterData &iter)
{
#if 0 /* Incompatible with new draw manager. */
if (iter.geom != nullptr) {
geometry_call(iter.material->shading.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
geometry_call(iter.material->prepass.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
geometry_call(iter.material->shadow.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
}
#endif
iter.geom = nullptr;
iter.vfirst = -1;
iter.vcount = 0;
}
/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */
static void gpencil_drawcall_add(gpIterData &iter,
gpu::Batch *geom,
Material *material,
int v_first,
int v_count,
bool instancing)
{
int last = iter.vfirst + iter.vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if (!DO_BATCHING || (geom != iter.geom) || (material != iter.material) || (v_first - last > 3)) {
gpencil_drawcall_flush(iter);
}
iter.geom = geom;
iter.material = material;
iter.instancing = instancing;
if (iter.vfirst == -1) {
iter.vfirst = v_first;
}
iter.vcount = v_first + v_count - iter.vfirst;
}
static void gpencil_stroke_sync(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
gpIterData &iter = *(gpIterData *)thunk;
Material *material = &iter.material_array.materials[gps->mat_nr];
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter.ob, gps->mat_nr + 1);
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
(!DRW_state_is_image_render() && ((gps->flag & GP_STROKE_NOFILL) != 0));
bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0);
if (hide_material) {
return;
}
gpu::Batch *geom = DRW_cache_gpencil_get(iter.ob, iter.cfra);
if (show_fill) {
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, false);
}
if (show_stroke) {
/* Start one vert before to have gl_InstanceID > 0 (see shader). */
int vfirst = gps->runtime.stroke_start * 3;
/* Include "potential" cyclic vertex and start adj vertex (see shader). */
int vcount = gps->totpoints + 1 + 1;
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, true);
}
}
void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle)
{
/* TODO(fclem): Waiting for a user option to use the render engine instead of gpencil engine. */
return;
/* Is this a surface or curves? */
if (!inst_.use_surfaces) {
return;
}
UNUSED_VARS(res_handle);
gpIterData iter(inst_, ob, ob_handle, res_handle);
BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter);
gpencil_drawcall_flush(iter);
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
bool has_transparent_shadows = true; /* TODO material.has_transparent_shadows. */
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Hair
* \{ */

View File

@@ -178,7 +178,6 @@ class SyncModule {
ObjectHandle &ob_handle,
ResourceHandle res_handle,
const ObjectRef &ob_ref);
void sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle);
void sync_curves(Object *ob,
ObjectHandle &ob_handle,
ResourceHandle res_handle,

View File

@@ -244,16 +244,6 @@ void GPENCIL_cache_init(void *ved)
pd->do_fast_drawing = false;
pd->obact = draw_ctx->obact;
if (pd->obact && pd->obact->type == OB_GPENCIL_LEGACY && !(pd->draw_depth_only)) {
/* Check if active object has a temp stroke data. */
bGPdata *gpd = (bGPdata *)pd->obact->data;
if (gpd->runtime.sbuffer_used > 0) {
pd->sbuffer_gpd = gpd;
pd->sbuffer_stroke = DRW_cache_gpencil_sbuffer_stroke_data_get(pd->obact);
pd->sbuffer_layer = BKE_gpencil_layer_active_get(pd->sbuffer_gpd);
pd->do_fast_drawing = false; /* TODO: option. */
}
}
}
if (pd->do_fast_drawing) {
@@ -332,273 +322,8 @@ void GPENCIL_cache_init(void *ved)
}
}
#define DRAW_NOW 2
struct gpIterPopulateData {
Object *ob;
GPENCIL_tObject *tgp_ob;
GPENCIL_PrivateData *pd;
GPENCIL_MaterialPool *matpool;
DRWShadingGroup *grp;
/* Last material UBO bound. Used to avoid unneeded buffer binding. */
GPUUniformBuf *ubo_mat;
GPUUniformBuf *ubo_lights;
/* Last texture bound. */
GPUTexture *tex_fill;
GPUTexture *tex_stroke;
/* Offset in the material pool to the first material of this object. */
int mat_ofs;
/* Is the sbuffer call need to be issued. */
int do_sbuffer_call;
/* Indices to do correct insertion of the sbuffer stroke. */
int stroke_index_last;
int stroke_index_offset;
/* Infos for call batching. */
blender::gpu::Batch *geom;
int vfirst, vcount;
};
#define DISABLE_BATCHING 0
static void gpencil_drawcall_flush(gpIterPopulateData *iter)
{
#if !DISABLE_BATCHING
if (iter->geom != nullptr) {
DRW_shgroup_call_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount);
}
#endif
iter->geom = nullptr;
iter->vfirst = -1;
iter->vcount = 0;
}
/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */
static void gpencil_drawcall_add(gpIterPopulateData *iter,
blender::gpu::Batch *geom,
int v_first,
int v_count)
{
#if DISABLE_BATCHING
DRW_shgroup_call_range(iter->grp, iter->ob, geom, v_first, v_count);
return;
#endif
int last = iter->vfirst + iter->vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if ((geom != iter->geom) || (v_first - last > 0)) {
gpencil_drawcall_flush(iter);
}
iter->geom = geom;
if (iter->vfirst == -1) {
iter->vfirst = v_first;
}
iter->vcount = v_first + v_count - iter->vfirst;
}
static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
void *thunk);
static void gpencil_sbuffer_cache_populate(gpIterPopulateData *iter)
{
iter->do_sbuffer_call = DRAW_NOW;
/* In order to draw the sbuffer stroke correctly mixed with other strokes,
* we need to offset the stroke index of the sbuffer stroke and the subsequent strokes.
* Remember, sbuffer stroke indices start from 0. So we add last index to avoid
* masking issues. */
iter->grp = DRW_shgroup_create_sub(iter->grp);
DRW_shgroup_uniform_block(iter->grp, "gp_materials", iter->ubo_mat);
DRW_shgroup_uniform_float_copy(iter->grp, "gpStrokeIndexOffset", iter->stroke_index_last);
const DRWContextState *ctx = DRW_context_state_get();
ToolSettings *ts = ctx->scene->toolsettings;
if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) {
/* In this case we can't do correct projection during stroke. We just disable depth test. */
DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx);
}
gpencil_stroke_cache_populate(nullptr, nullptr, iter->pd->sbuffer_stroke, iter);
gpencil_drawcall_flush(iter);
iter->stroke_index_offset = iter->pd->sbuffer_stroke->totpoints + 1;
iter->do_sbuffer_call = 0;
}
static void gpencil_layer_cache_populate(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke * /*gps*/,
void *thunk)
{
gpIterPopulateData *iter = (gpIterPopulateData *)thunk;
GPENCIL_PrivateData *pd = iter->pd;
bGPdata *gpd = (bGPdata *)iter->ob->data;
gpencil_drawcall_flush(iter);
if (iter->do_sbuffer_call) {
gpencil_sbuffer_cache_populate(iter);
}
else {
iter->do_sbuffer_call = !pd->do_fast_drawing && (gpd == pd->sbuffer_gpd) &&
(gpl == pd->sbuffer_layer) &&
(gpf == nullptr || gpf->runtime.onion_id == 0.0f);
}
GPENCIL_tLayer *tgp_layer = gpencil_layer_cache_add(pd, iter->ob, gpl, gpf, iter->tgp_ob);
const bool use_lights = pd->use_lighting && ((gpl->flag & GP_LAYER_USE_LIGHTS) != 0) &&
(iter->ob->dtx & OB_USE_GPENCIL_LIGHTS);
iter->ubo_lights = (use_lights) ? pd->global_light_pool->ubo : pd->shadeless_light_pool->ubo;
gpencil_material_resources_get(iter->matpool, 0, nullptr, nullptr, &iter->ubo_mat);
/* Iterator dependent uniforms. */
DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp;
DRW_shgroup_uniform_block(grp, "gp_lights", iter->ubo_lights);
DRW_shgroup_uniform_block(grp, "gp_materials", iter->ubo_mat);
DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill);
DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke);
DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs);
DRW_shgroup_uniform_float_copy(grp, "gpStrokeIndexOffset", iter->stroke_index_offset);
DRW_shgroup_uniform_vec2_copy(grp, "viewportSize", DRW_viewport_size_get());
}
static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
void *thunk)
{
using namespace blender::draw;
gpIterPopulateData *iter = (gpIterPopulateData *)thunk;
bGPdata *gpd = static_cast<bGPdata *>(iter->ob->data);
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1);
const bool is_render = iter->pd->is_render;
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
(!is_render && ((gps->flag & GP_STROKE_NOFILL) != 0));
bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) &&
(!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0);
bool only_lines = !GPENCIL_PAINT_MODE(gpd) && gpl && gpf && gpl->actframe != gpf &&
iter->pd->use_multiedit_lines_only;
bool is_onion = gpl && gpf && gpf->runtime.onion_id != 0;
bool hide_onion = is_onion && ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0);
if ((hide_material) || (!show_stroke && !show_fill) || (only_lines && !is_onion) || (hide_onion))
{
return;
}
GPUUniformBuf *ubo_mat;
GPUTexture *tex_stroke, *tex_fill;
gpencil_material_resources_get(
iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat);
bool resource_changed = (iter->ubo_mat != ubo_mat) ||
(tex_fill && (iter->tex_fill != tex_fill)) ||
(tex_stroke && (iter->tex_stroke != tex_stroke));
if (resource_changed) {
gpencil_drawcall_flush(iter);
iter->grp = DRW_shgroup_create_sub(iter->grp);
if (iter->ubo_mat != ubo_mat) {
DRW_shgroup_uniform_block(iter->grp, "gp_materials", ubo_mat);
iter->ubo_mat = ubo_mat;
}
if (tex_fill) {
DRW_shgroup_uniform_texture(iter->grp, "gpFillTexture", tex_fill);
iter->tex_fill = tex_fill;
}
if (tex_stroke) {
DRW_shgroup_uniform_texture(iter->grp, "gpStrokeTexture", tex_stroke);
iter->tex_stroke = tex_stroke;
}
}
bool do_sbuffer = (iter->do_sbuffer_call == DRAW_NOW);
blender::gpu::Batch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_get(iter->ob, show_fill) :
DRW_cache_gpencil_get(iter->ob, iter->pd->cfra);
if (geom != iter->geom) {
gpencil_drawcall_flush(iter);
blender::gpu::VertBuf *position_tx =
do_sbuffer ? DRW_cache_gpencil_sbuffer_position_buffer_get(iter->ob, show_fill) :
DRW_cache_gpencil_position_buffer_get(iter->ob, iter->pd->cfra);
blender::gpu::VertBuf *color_tx = do_sbuffer ?
DRW_cache_gpencil_sbuffer_color_buffer_get(iter->ob,
show_fill) :
DRW_cache_gpencil_color_buffer_get(iter->ob,
iter->pd->cfra);
DRW_shgroup_buffer_texture(iter->grp, "gp_pos_tx", position_tx);
DRW_shgroup_buffer_texture(iter->grp, "gp_col_tx", color_tx);
}
if (show_fill) {
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
gpencil_drawcall_add(iter, geom, vfirst, vcount);
}
if (show_stroke) {
int vfirst = gps->runtime.stroke_start * 3;
bool is_cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2);
int vcount = (gps->totpoints + int(is_cyclic)) * 2 * 3;
gpencil_drawcall_add(iter, geom, vfirst, vcount);
}
iter->stroke_index_last = gps->runtime.vertex_start + gps->totpoints + 1;
}
static void gpencil_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter)
{
bGPdata *gpd = (bGPdata *)iter->ob->data;
if (gpd != iter->pd->sbuffer_gpd) {
return;
}
GPENCIL_TextureList *txl = vedata->txl;
GPUTexture *depth_texture = iter->pd->scene_depth_tx;
GPENCIL_tObject *last_tgp_ob = iter->pd->tobjects.last;
/* Create another temp object that only contain the stroke. */
const blender::Bounds<float3> bounds = BKE_gpencil_data_minmax(gpd).value_or(
blender::Bounds(float3(0)));
iter->tgp_ob = gpencil_object_cache_add(
iter->pd, iter->ob, (gpd->draw_mode == GP_DRAWMODE_3D), bounds);
/* Remove from the main list. */
iter->pd->tobjects.last = last_tgp_ob;
last_tgp_ob->next = nullptr;
/* Add to sbuffer tgpobject list. */
BLI_LINKS_APPEND(&iter->pd->sbuffer_tobjects, iter->tgp_ob);
/* Remove depth test with scene (avoid self occlusion). */
iter->pd->scene_depth_tx = txl->dummy_texture;
gpencil_layer_cache_populate(
iter->pd->sbuffer_layer, iter->pd->sbuffer_layer->actframe, nullptr, iter);
const DRWContextState *ctx = DRW_context_state_get();
ToolSettings *ts = ctx->scene->toolsettings;
if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) {
/* In this case we can't do correct projection during stroke. We just disable depth test. */
DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx);
}
iter->do_sbuffer_call = DRAW_NOW;
gpencil_stroke_cache_populate(nullptr, nullptr, iter->pd->sbuffer_stroke, iter);
gpencil_drawcall_flush(iter);
gpencil_vfx_cache_populate(
vedata, iter->ob, iter->tgp_ob, (gpd != nullptr && GPENCIL_ANY_EDIT_MODE(gpd)));
/* Restore state. */
iter->do_sbuffer_call = 0;
iter->pd->scene_depth_tx = depth_texture;
}
/* Check if the passed in layer is used by any other layer as a mask (in the viewlayer). */
static bool is_used_as_layer_mask_in_viewlayer(const GreasePencil &grease_pencil,
const blender::bke::greasepencil::Layer &mask_layer,
@@ -877,59 +602,13 @@ void GPENCIL_cache_populate(void *ved, Object *ob)
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
GPENCIL_PrivateData *pd = vedata->stl->pd;
GPENCIL_TextureList *txl = vedata->txl;
const bool is_final_render = DRW_state_is_image_render();
/* object must be visible */
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
return;
}
if (ob->data && (ob->type == OB_GPENCIL_LEGACY) && (ob->dt >= OB_SOLID)) {
bGPdata *gpd = (bGPdata *)ob->data;
const blender::Bounds<float3> bounds = BKE_gpencil_data_minmax(gpd).value_or(
blender::Bounds(float3(0)));
gpIterPopulateData iter = {nullptr};
iter.ob = ob;
iter.pd = pd;
iter.tgp_ob = gpencil_object_cache_add(pd, ob, (gpd->draw_mode == GP_DRAWMODE_3D), bounds);
iter.matpool = gpencil_material_pool_create(pd, ob, &iter.mat_ofs, GPENCIL_VERTEX_MODE(gpd));
iter.tex_fill = txl->dummy_texture;
iter.tex_stroke = txl->dummy_texture;
/* Special case for rendering onion skin. */
bool do_onion = (!pd->is_render) ? pd->do_onion : (gpd->onion_flag & GP_ONION_GHOST_ALWAYS);
gpd->runtime.playing = short(pd->playing);
/* When render in background the active frame could not be properly set due thread priority,
* better set again. This is not required in viewport. */
if (txl->render_depth_tx) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, pd->cfra, GP_GETFRAME_USE_PREV);
}
}
BKE_gpencil_visible_stroke_advanced_iter(is_final_render ? pd->view_layer : nullptr,
ob,
gpencil_layer_cache_populate,
gpencil_stroke_cache_populate,
&iter,
do_onion,
pd->cfra);
gpencil_drawcall_flush(&iter);
if (iter.do_sbuffer_call) {
gpencil_sbuffer_cache_populate(&iter);
}
gpencil_vfx_cache_populate(
vedata, ob, iter.tgp_ob, (gpd != nullptr && GPENCIL_ANY_EDIT_MODE(gpd)));
if (pd->do_fast_drawing) {
gpencil_sbuffer_cache_populate_fast(vedata, &iter);
}
}
else if (ob->data && (ob->type == OB_GREASE_PENCIL) && (ob->dt >= OB_SOLID)) {
if (ob->data && (ob->type == OB_GREASE_PENCIL) && (ob->dt >= OB_SOLID)) {
GPENCIL_tObject *tgp_ob = grease_pencil_object_cache_populate(pd, txl, ob);
gpencil_vfx_cache_populate(
vedata, ob, tgp_ob, ELEM(ob->mode, OB_MODE_EDIT, OB_MODE_SCULPT, OB_MODE_WEIGHT_PAINT));

View File

@@ -518,9 +518,6 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OVERLAY_metaball_cache_populate(data, ob);
}
break;
case OB_GPENCIL_LEGACY:
OVERLAY_gpencil_legacy_cache_populate(data, ob);
break;
}
}
/* Non-Meshes */

View File

@@ -322,52 +322,6 @@ void OVERLAY_gpencil_legacy_cache_init(OVERLAY_Data *vedata)
}
}
static void OVERLAY_edit_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
using namespace blender::draw;
OVERLAY_PrivateData *pd = vedata->stl->pd;
bGPdata *gpd = (bGPdata *)ob->data;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
/* Overlay is only for active object. */
if (ob != draw_ctx->obact) {
return;
}
if (pd->edit_gpencil_wires_grp) {
DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_wires_grp);
DRW_shgroup_uniform_vec4_copy(grp, "gpEditColor", gpd->line_color);
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_lines_get(ob, pd->cfra);
DRW_shgroup_call_no_cull(pd->edit_gpencil_wires_grp, geom, ob);
}
if (pd->edit_gpencil_points_grp) {
const bool show_direction = (v3d->gp_flag & V3D_GP_SHOW_STROKE_DIRECTION) != 0;
DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_points_grp);
DRW_shgroup_uniform_float_copy(grp, "doStrokeEndpoints", show_direction);
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_points_get(ob, pd->cfra);
DRW_shgroup_call_no_cull(grp, geom, ob);
}
if (pd->edit_gpencil_curve_handle_grp) {
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_curve_handles_get(ob, pd->cfra);
if (geom) {
DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_handle_grp, geom, ob);
}
}
if (pd->edit_gpencil_curve_points_grp) {
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_curve_points_get(ob, pd->cfra);
if (geom) {
DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_points_grp, geom, ob);
}
}
}
static void overlay_gpencil_draw_stroke_color_name(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
@@ -427,30 +381,6 @@ static void OVERLAY_gpencil_color_names(Object *ob)
nullptr, ob, nullptr, overlay_gpencil_draw_stroke_color_name, ob, false, cfra);
}
void OVERLAY_gpencil_legacy_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
bGPdata *gpd = (bGPdata *)ob->data;
if (gpd == nullptr) {
return;
}
if (GPENCIL_ANY_MODE(gpd)) {
OVERLAY_edit_gpencil_cache_populate(vedata, ob);
}
/* don't show object extras in set's */
if ((ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) == 0) {
if ((v3d->gp_flag & V3D_GP_SHOW_MATERIAL_NAME) && (ob->mode == OB_MODE_EDIT_GPENCIL_LEGACY) &&
DRW_state_show_text())
{
OVERLAY_gpencil_color_names(ob);
}
}
}
void OVERLAY_gpencil_legacy_draw(OVERLAY_Data *vedata)
{
OVERLAY_PassList *psl = vedata->psl;

View File

@@ -186,97 +186,6 @@ struct iterData {
float plane[4];
};
static void gpencil_layer_cache_populate(bGPDlayer *gpl,
bGPDframe * /*gpf*/,
bGPDstroke * /*gps*/,
void *thunk)
{
using namespace blender::draw;
iterData *iter = (iterData *)thunk;
bGPdata *gpd = (bGPdata *)iter->ob->data;
const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0;
const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D);
float object_scale = mat4_to_scale(iter->ob->object_to_world().ptr());
/* Negate thickness sign to tag that strokes are in screen space.
* Convert to world units (by default, 1 meter = 2000 pixels). */
float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f);
blender::gpu::VertBuf *position_tx = DRW_cache_gpencil_position_buffer_get(iter->ob, iter->cfra);
blender::gpu::VertBuf *color_tx = DRW_cache_gpencil_color_buffer_get(iter->ob, iter->cfra);
DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp);
DRW_shgroup_uniform_bool_copy(grp, "gpStrokeOrder3d", is_stroke_order_3d);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", object_scale);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", float(gpl->line_change));
DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale);
DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", iter->plane);
DRW_shgroup_buffer_texture(grp, "gp_pos_tx", position_tx);
DRW_shgroup_buffer_texture(grp, "gp_col_tx", color_tx);
}
static void gpencil_stroke_cache_populate(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
using namespace blender::draw;
iterData *iter = (iterData *)thunk;
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1);
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
/* TODO: What about simplify Fill? */
bool show_fill = (gps->tot_triangles > 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
if (hide_material) {
return;
}
blender::gpu::Batch *geom = DRW_cache_gpencil_get(iter->ob, iter->cfra);
if (show_fill) {
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount);
}
if (show_stroke) {
int vfirst = gps->runtime.stroke_start * 3;
bool is_cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2);
int vcount = (gps->totpoints + int(is_cyclic)) * 2 * 3;
DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount);
}
}
static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob)
{
/* No outlines in edit mode. */
bGPdata *gpd = (bGPdata *)ob->data;
if (gpd && GPENCIL_ANY_MODE(gpd)) {
return;
}
iterData iter{};
iter.ob = ob;
iter.stroke_grp = pd->outlines_gpencil_grp;
iter.cfra = pd->cfra;
if (gpd->draw_mode == GP_DRAWMODE_2D) {
gpencil_depth_plane(ob, iter.plane);
}
BKE_gpencil_visible_stroke_advanced_iter(nullptr,
ob,
gpencil_layer_cache_populate,
gpencil_stroke_cache_populate,
&iter,
false,
pd->cfra);
}
static void OVERLAY_outline_grease_pencil(OVERLAY_PrivateData *pd, Scene *scene, Object *ob)
{
using namespace blender;
@@ -413,11 +322,6 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
return;
}
if (ob->type == OB_GPENCIL_LEGACY) {
OVERLAY_outline_gpencil(pd, ob);
return;
}
if (ob->type == OB_GREASE_PENCIL) {
OVERLAY_outline_grease_pencil(pd, draw_ctx->scene, ob);
return;

View File

@@ -608,7 +608,6 @@ void OVERLAY_edit_curve_draw(OVERLAY_Data *vedata);
void OVERLAY_edit_gpencil_legacy_cache_init(OVERLAY_Data *vedata);
void OVERLAY_gpencil_legacy_cache_init(OVERLAY_Data *vedata);
void OVERLAY_gpencil_legacy_cache_populate(OVERLAY_Data *vedata, Object *ob);
void OVERLAY_gpencil_legacy_draw(OVERLAY_Data *vedata);
void OVERLAY_edit_gpencil_legacy_draw(OVERLAY_Data *vedata);

View File

@@ -890,8 +890,6 @@ blender::gpu::Batch *DRW_cache_object_face_wireframe_get(const Scene *scene, Obj
return DRW_pointcloud_batch_cache_get_dots(ob);
case OB_VOLUME:
return DRW_cache_volume_face_wireframe_get(ob);
case OB_GPENCIL_LEGACY:
return DRW_cache_gpencil_face_wireframe_get(ob);
case OB_GREASE_PENCIL:
return DRW_cache_grease_pencil_face_wireframe_get(scene, ob);
default:
@@ -961,8 +959,6 @@ int DRW_cache_object_material_count_get(const Object *ob)
return DRW_pointcloud_material_count_get(static_cast<const PointCloud *>(ob->data));
case OB_VOLUME:
return DRW_volume_material_count_get(static_cast<const Volume *>(ob->data));
case OB_GPENCIL_LEGACY:
return DRW_gpencil_material_count_get(static_cast<const bGPdata *>(ob->data));
default:
BLI_assert(0);
return 0;

View File

@@ -258,23 +258,6 @@ DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume,
blender::gpu::Batch *DRW_cache_volume_face_wireframe_get(Object *ob);
blender::gpu::Batch *DRW_cache_volume_selection_surface_get(Object *ob);
/* GPencil (legacy) */
blender::gpu::Batch *DRW_cache_gpencil_get(Object *ob, int cfra);
gpu::VertBuf *DRW_cache_gpencil_position_buffer_get(Object *ob, int cfra);
gpu::VertBuf *DRW_cache_gpencil_color_buffer_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_lines_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_points_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_curve_handles_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_curve_points_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_sbuffer_get(Object *ob, bool show_fill);
gpu::VertBuf *DRW_cache_gpencil_sbuffer_position_buffer_get(Object *ob, bool show_fill);
gpu::VertBuf *DRW_cache_gpencil_sbuffer_color_buffer_get(Object *ob, bool show_fill);
int DRW_gpencil_material_count_get(const bGPdata *gpd);
blender::gpu::Batch *DRW_cache_gpencil_face_wireframe_get(Object *ob);
bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob);
/**
* Sbuffer batches are temporary. We need to clear it after drawing.
*/

View File

@@ -66,57 +66,10 @@ struct GpencilBatchCache {
namespace blender::draw {
/* -------------------------------------------------------------------- */
/** \name Internal Types
* \{ */
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internal Utilities
* \{ */
static bool gpencil_batch_cache_valid(GpencilBatchCache *cache, bGPdata *gpd, int cfra)
{
bool valid = true;
if (cache == nullptr) {
return false;
}
if (cfra != cache->cache_frame) {
valid = false;
}
else if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) {
valid = false;
}
else if (cache->is_dirty) {
valid = false;
}
return valid;
}
static GpencilBatchCache *gpencil_batch_cache_init(Object *ob, int cfra)
{
bGPdata *gpd = (bGPdata *)ob->data;
GpencilBatchCache *cache = gpd->runtime.gpencil_cache;
if (!cache) {
cache = gpd->runtime.gpencil_cache = (GpencilBatchCache *)MEM_callocN(sizeof(*cache),
__func__);
}
else {
memset(cache, 0, sizeof(*cache));
}
cache->is_dirty = true;
cache->cache_frame = cfra;
return cache;
}
static void gpencil_batch_cache_clear(GpencilBatchCache *cache)
{
if (!cache) {
@@ -140,19 +93,6 @@ static void gpencil_batch_cache_clear(GpencilBatchCache *cache)
cache->is_dirty = true;
}
static GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra)
{
bGPdata *gpd = (bGPdata *)ob->data;
GpencilBatchCache *cache = gpd->runtime.gpencil_cache;
if (!gpencil_batch_cache_valid(cache, gpd, cfra)) {
gpencil_batch_cache_clear(cache);
return gpencil_batch_cache_init(ob, cfra);
}
return cache;
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -173,539 +113,10 @@ void DRW_gpencil_batch_cache_free(bGPdata *gpd)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Vertex Formats
* \{ */
/* MUST match the format below. */
struct gpStrokeVert {
/** Position and thickness packed in the same attribute. */
float pos[3], thickness;
/** Material Index, Stroke Index, Point Index, Packed aspect + hardness + rotation. */
int32_t mat, stroke_id, point_id, packed_asp_hard_rot;
/** UV and strength packed in the same attribute. */
float uv_fill[2], u_stroke, strength;
};
static GPUVertFormat *gpencil_stroke_format()
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "ma", GPU_COMP_I32, 4, GPU_FETCH_INT);
GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
return &format;
}
/* MUST match the format below. */
struct gpEditVert {
uint vflag;
float weight;
};
static GPUVertFormat *gpencil_edit_stroke_format()
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
return &format;
}
/* MUST match the format below. */
struct gpEditCurveVert {
float pos[3];
uint32_t data;
};
static GPUVertFormat *gpencil_edit_curve_format()
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* initialize vertex formats */
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
}
return &format;
}
/* MUST match the format below. */
struct gpColorVert {
float vcol[4]; /* Vertex color */
float fcol[4]; /* Fill color */
};
static GPUVertFormat *gpencil_color_format()
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
GPU_vertformat_attr_add(&format, "col", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "fcol", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
return &format;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Vertex Buffers
* \{ */
struct gpIterData {
bGPdata *gpd;
gpStrokeVert *verts;
gpColorVert *cols;
GPUIndexBufBuilder ibo;
int vert_len;
int tri_len;
int curve_len;
};
static gpu::VertBuf *gpencil_dummy_buffer_get()
{
blender::gpu::Batch *batch = DRW_gpencil_dummy_buffer_get();
return batch->verts[0];
}
static int gpencil_stroke_is_cyclic(const bGPDstroke *gps)
{
return ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2);
}
BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float hard)
{
int32_t packed = 0;
/* Aspect uses 9 bits */
float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
/* Store if inversed in the 9th bit. */
if (asp > 1.0f) {
packed |= 1 << 8;
}
/* Rotation uses 9 bits */
/* Rotation are in [-90..90 degree] range, so we can encode the sign of the angle + the cosine
* because the cosine will always be positive. */
packed |= int32_t(unit_float_to_uchar_clamp(cosf(rot))) << 9;
/* Store sine sign in 9th bit. */
if (rot < 0.0f) {
packed |= 1 << 17;
}
/* Hardness uses 8 bits */
packed |= int32_t(unit_float_to_uchar_clamp(hard)) << 18;
return packed;
}
static void gpencil_buffer_add_point(GPUIndexBufBuilder *ibo,
gpStrokeVert *verts,
gpColorVert *cols,
const bGPDstroke *gps,
const bGPDspoint *pt,
int v,
bool is_endpoint)
{
/* NOTE: we use the sign of strength and thickness to pass cap flag. */
const bool round_cap0 = (gps->caps[0] == GP_STROKE_CAP_ROUND);
const bool round_cap1 = (gps->caps[1] == GP_STROKE_CAP_ROUND);
gpStrokeVert *vert = &verts[v];
gpColorVert *col = &cols[v];
copy_v3_v3(vert->pos, &pt->x);
copy_v2_v2(vert->uv_fill, pt->uv_fill);
copy_v4_v4(col->vcol, pt->vert_color);
copy_v4_v4(col->fcol, gps->vert_color_fill);
/* Encode fill opacity defined by opacity modifier in vertex color alpha. If
* no opacity modifier, the value will be always 1.0f. The opacity factor can be any
* value between 0.0f and 2.0f */
col->fcol[3] = (int(col->fcol[3] * 10000.0f) * 10.0f) + gps->fill_opacity_fac;
vert->strength = (round_cap0) ? pt->strength : -pt->strength;
vert->u_stroke = pt->uv_fac;
vert->stroke_id = gps->runtime.vertex_start;
vert->point_id = v;
vert->thickness = max_ff(0.0f, gps->thickness * pt->pressure) * (round_cap1 ? 1.0f : -1.0f);
/* Tag endpoint material to -1 so they get discarded by vertex shader. */
vert->mat = (is_endpoint) ? -1 : (gps->mat_nr % GPENCIL_MATERIAL_BUFFER_LEN);
float aspect_ratio = gps->aspect_ratio[0] / max_ff(gps->aspect_ratio[1], 1e-8);
vert->packed_asp_hard_rot = pack_rotation_aspect_hardness(
pt->uv_rot, aspect_ratio, gps->hardness);
if (!is_endpoint) {
/* Issue a Quad per point. */
/* The attribute loading uses a different shader and will undo this bit packing. */
int v_mat = (v << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT;
GPU_indexbuf_add_tri_verts(ibo, v_mat + 0, v_mat + 1, v_mat + 2);
GPU_indexbuf_add_tri_verts(ibo, v_mat + 2, v_mat + 1, v_mat + 3);
}
}
static void gpencil_buffer_add_stroke(GPUIndexBufBuilder *ibo,
gpStrokeVert *verts,
gpColorVert *cols,
const bGPDstroke *gps)
{
const bGPDspoint *pts = gps->points;
int pts_len = gps->totpoints;
bool is_cyclic = gpencil_stroke_is_cyclic(gps);
int v = gps->runtime.vertex_start;
/* First point for adjacency (not drawn). */
int adj_idx = (is_cyclic) ? (pts_len - 1) : min_ii(pts_len - 1, 1);
gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[adj_idx], v++, true);
for (int i = 0; i < pts_len; i++) {
gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[i], v++, false);
}
/* Draw line to first point to complete the loop for cyclic strokes. */
if (is_cyclic) {
gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[0], v, false);
/* UV factor needs to be adjusted for the last point to not be equal to the UV factor of the
* first point. It should be the factor of the last point plus the distance from the last point
* to the first.
*/
gpStrokeVert *vert = &verts[v];
vert->u_stroke = verts[v - 1].u_stroke + len_v3v3(&pts[pts_len - 1].x, &pts[0].x);
v++;
}
/* Last adjacency point (not drawn). */
adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2);
gpencil_buffer_add_point(ibo, verts, cols, gps, &pts[adj_idx], v++, true);
}
static void gpencil_buffer_add_fill(GPUIndexBufBuilder *ibo, const bGPDstroke *gps)
{
int tri_len = gps->tot_triangles;
int v = gps->runtime.vertex_start + 1;
for (int i = 0; i < tri_len; i++) {
uint *tri = gps->triangles[i].verts;
/* The attribute loading uses a different shader and will undo this bit packing. */
GPU_indexbuf_add_tri_verts(ibo,
(v + tri[0]) << GP_VERTEX_ID_SHIFT,
(v + tri[1]) << GP_VERTEX_ID_SHIFT,
(v + tri[2]) << GP_VERTEX_ID_SHIFT);
}
}
static void gpencil_stroke_iter_cb(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
gpIterData *iter = (gpIterData *)thunk;
if (gps->tot_triangles > 0) {
gpencil_buffer_add_fill(&iter->ibo, gps);
}
gpencil_buffer_add_stroke(&iter->ibo, iter->verts, iter->cols, gps);
}
static void gpencil_object_verts_count_cb(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
gpIterData *iter = (gpIterData *)thunk;
int stroke_vert_len = gps->totpoints + gpencil_stroke_is_cyclic(gps);
gps->runtime.vertex_start = iter->vert_len;
/* Add additional padding at the start and end. */
iter->vert_len += 1 + stroke_vert_len + 1;
/* Store first index offset. */
gps->runtime.fill_start = iter->tri_len;
iter->tri_len += gps->tot_triangles;
gps->runtime.stroke_start = iter->tri_len;
iter->tri_len += stroke_vert_len * 2;
}
static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra)
{
bGPdata *gpd = (bGPdata *)ob->data;
if (cache->vbo == nullptr) {
/* Should be discarded together. */
BLI_assert(cache->vbo == nullptr && cache->ibo == nullptr);
BLI_assert(cache->geom_batch == nullptr);
/* TODO/PERF: Could be changed to only do it if needed.
* For now it's simpler to assume we always need it
* since multiple viewport could or could not need it.
* Ideally we should have a dedicated onion skin geom batch. */
/* IMPORTANT: Keep in sync with gpencil_edit_batches_ensure() */
bool do_onion = true;
/* First count how many vertices and triangles are needed for the whole object. */
gpIterData iter = {};
iter.gpd = gpd;
iter.verts = nullptr;
iter.ibo = {0};
iter.vert_len = 0;
iter.tri_len = 0;
iter.curve_len = 0;
BKE_gpencil_visible_stroke_advanced_iter(
nullptr, ob, nullptr, gpencil_object_verts_count_cb, &iter, do_onion, cfra);
GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
/* Create VBOs. */
GPUVertFormat *format = gpencil_stroke_format();
GPUVertFormat *format_col = gpencil_color_format();
cache->vbo = GPU_vertbuf_create_with_format_ex(*format, vbo_flag);
cache->vbo_col = GPU_vertbuf_create_with_format_ex(*format_col, vbo_flag);
/* Add extra space at the end of the buffer because of quad load. */
GPU_vertbuf_data_alloc(*cache->vbo, iter.vert_len + 2);
GPU_vertbuf_data_alloc(*cache->vbo_col, iter.vert_len + 2);
iter.verts = cache->vbo->data<gpStrokeVert>().data();
iter.cols = cache->vbo_col->data<gpColorVert>().data();
/* Create IBO. */
GPU_indexbuf_init(&iter.ibo, GPU_PRIM_TRIS, iter.tri_len, 0xFFFFFFFFu);
/* Fill buffers with data. */
BKE_gpencil_visible_stroke_advanced_iter(
nullptr, ob, nullptr, gpencil_stroke_iter_cb, &iter, do_onion, cfra);
/* Mark last 2 verts as invalid. */
for (int i = 0; i < 2; i++) {
iter.verts[iter.vert_len + i].mat = -1;
}
/* Also mark first vert as invalid. */
iter.verts[0].mat = -1;
/* Finish the IBO. */
cache->ibo = GPU_indexbuf_build(&iter.ibo);
/* Create the batches */
cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
/* Allow creation of buffer texture. */
GPU_vertbuf_use(cache->vbo);
GPU_vertbuf_use(cache->vbo_col);
gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
cache->is_dirty = false;
}
}
blender::gpu::Batch *DRW_cache_gpencil_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
return cache->geom_batch;
}
gpu::VertBuf *DRW_cache_gpencil_position_buffer_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
return cache->vbo;
}
gpu::VertBuf *DRW_cache_gpencil_color_buffer_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
return cache->vbo_col;
}
static void gpencil_lines_indices_cb(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
gpIterData *iter = (gpIterData *)thunk;
int pts_len = gps->totpoints + gpencil_stroke_is_cyclic(gps);
int start = gps->runtime.vertex_start + 1;
int end = start + pts_len;
for (int i = start; i < end; i++) {
GPU_indexbuf_add_generic_vert(&iter->ibo, i);
}
GPU_indexbuf_add_primitive_restart(&iter->ibo);
}
blender::gpu::Batch *DRW_cache_gpencil_face_wireframe_get(Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
int cfra = DEG_get_ctime(draw_ctx->depsgraph);
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
if (cache->lines_batch == nullptr) {
gpu::VertBuf *vbo = cache->vbo;
gpIterData iter = {};
iter.gpd = (bGPdata *)ob->data;
iter.ibo = {0};
uint vert_len = GPU_vertbuf_get_vertex_len(vbo);
GPU_indexbuf_init_ex(&iter.ibo, GPU_PRIM_LINE_STRIP, vert_len, vert_len);
/* IMPORTANT: Keep in sync with gpencil_edit_batches_ensure() */
bool do_onion = true;
BKE_gpencil_visible_stroke_advanced_iter(
nullptr, ob, nullptr, gpencil_lines_indices_cb, &iter, do_onion, cfra);
blender::gpu::IndexBuf *ibo = GPU_indexbuf_build(&iter.ibo);
cache->lines_batch = GPU_batch_create_ex(GPU_PRIM_LINE_STRIP, vbo, ibo, GPU_BATCH_OWNS_INDEX);
}
return cache->lines_batch;
}
/** \} */
/* ---------------------------------------------------------------------- */
/** \name Sbuffer stroke batches.
* \{ */
bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob)
{
bGPdata *gpd = (bGPdata *)ob->data;
Brush *brush = gpd->runtime.sbuffer_brush;
/* Convert the sbuffer to a bGPDstroke. */
if (gpd->runtime.sbuffer_gps == nullptr) {
bGPDstroke *gps = (bGPDstroke *)MEM_callocN(sizeof(*gps), "bGPDstroke sbuffer");
gps->totpoints = gpd->runtime.sbuffer_used;
gps->mat_nr = max_ii(0, gpd->runtime.matid - 1);
gps->flag = gpd->runtime.sbuffer_sflag;
gps->thickness = brush->size;
gps->hardness = brush->gpencil_settings->hardness;
copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio);
gps->fill_opacity_fac = gpd->runtime.fill_opacity_fac;
gps->tot_triangles = max_ii(0, gpd->runtime.sbuffer_used - 2);
gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND;
gps->runtime.vertex_start = 0;
gps->runtime.fill_start = 0;
gps->runtime.stroke_start = 0;
copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill);
/* Caps. */
gps->caps[0] = gps->caps[1] = short(brush->gpencil_settings->caps_type);
gpd->runtime.sbuffer_gps = gps;
}
return gpd->runtime.sbuffer_gps;
}
static void gpencil_sbuffer_stroke_ensure(bGPdata *gpd, bool do_fill)
{
tGPspoint *tpoints = (tGPspoint *)gpd->runtime.sbuffer;
bGPDstroke *gps = gpd->runtime.sbuffer_gps;
int vert_len = gpd->runtime.sbuffer_used;
/* DRW_cache_gpencil_sbuffer_stroke_data_get need to have been called previously. */
BLI_assert(gps != nullptr);
if (gpd->runtime.sbuffer_batch == nullptr) {
gps->points = (bGPDspoint *)MEM_mallocN(vert_len * sizeof(*gps->points), __func__);
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
ARegion *region = draw_ctx->region;
Object *ob = draw_ctx->obact;
BLI_assert(ob && (ob->type == OB_GPENCIL_LEGACY));
/* Get origin to reproject points. */
float origin[3];
ToolSettings *ts = scene->toolsettings;
ED_gpencil_drawing_reference_get(scene, ob, ts->gpencil_v3d_align, origin);
for (int i = 0; i < vert_len; i++) {
ED_gpencil_tpoint_to_point(region, origin, &tpoints[i], &gps->points[i]);
mul_m4_v3(ob->world_to_object().ptr(), &gps->points[i].x);
bGPDspoint *pt = &gps->points[i];
copy_v4_v4(pt->vert_color, tpoints[i].vert_color);
}
/* Calc uv data along the stroke. */
BKE_gpencil_stroke_uv_update(gps);
int tri_len = gps->tot_triangles + (gps->totpoints + gpencil_stroke_is_cyclic(gps)) * 2;
/* Create IBO. */
GPUIndexBufBuilder ibo_builder;
GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, tri_len, 0xFFFFFFFFu);
/* Create VBO. */
GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
GPUVertFormat *format = gpencil_stroke_format();
GPUVertFormat *format_color = gpencil_color_format();
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format_ex(*format, vbo_flag);
gpu::VertBuf *vbo_col = GPU_vertbuf_create_with_format_ex(*format_color, vbo_flag);
/* Add extra space at the start and end the buffer because of quad load and cyclic. */
GPU_vertbuf_data_alloc(*vbo, 1 + vert_len + 1 + 2);
GPU_vertbuf_data_alloc(*vbo_col, 1 + vert_len + 1 + 2);
gpStrokeVert *verts = vbo->data<gpStrokeVert>().data();
gpColorVert *cols = vbo_col->data<gpColorVert>().data();
/* Create fill indices. */
if (do_fill && gps->tot_triangles > 0) {
float(*tpoints2d)[2] = (float(*)[2])MEM_mallocN(sizeof(*tpoints2d) * vert_len, __func__);
/* Triangulate in 2D. */
for (int i = 0; i < vert_len; i++) {
copy_v2_v2(tpoints2d[i], tpoints[i].m_xy);
}
/* Compute directly inside the IBO data buffer. */
/* OPTI: This is a bottleneck if the stroke is very long. */
BLI_polyfill_calc(tpoints2d, uint(vert_len), 0, (uint(*)[3])ibo_builder.data);
/* Add stroke start offset and shift. */
for (int i = 0; i < gps->tot_triangles * 3; i++) {
ibo_builder.data[i] = (ibo_builder.data[i] + 1) << GP_VERTEX_ID_SHIFT;
}
/* HACK since we didn't use the builder API to avoid another malloc and copy,
* we need to set the number of indices manually. */
ibo_builder.index_len = gps->tot_triangles * 3;
ibo_builder.index_min = 0;
/* For this case, do not allow index compaction to avoid yet another preprocessing step. */
ibo_builder.index_max = 0xFFFFFFFFu - 1u;
gps->runtime.stroke_start = gps->tot_triangles;
MEM_freeN(tpoints2d);
}
/* Fill buffers with data. */
gpencil_buffer_add_stroke(&ibo_builder, verts, cols, gps);
blender::gpu::Batch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS,
gpencil_dummy_buffer_get(),
GPU_indexbuf_build(&ibo_builder),
GPU_BATCH_OWNS_INDEX);
gpd->runtime.sbuffer_position_buf = vbo;
gpd->runtime.sbuffer_color_buf = vbo_col;
gpd->runtime.sbuffer_batch = batch;
MEM_freeN(gps->points);
}
}
blender::gpu::Batch *DRW_cache_gpencil_sbuffer_get(Object *ob, bool show_fill)
{
bGPdata *gpd = (bGPdata *)ob->data;
/* Fill batch also need stroke batch to be created (vbo is shared). */
gpencil_sbuffer_stroke_ensure(gpd, show_fill);
return gpd->runtime.sbuffer_batch;
}
gpu::VertBuf *DRW_cache_gpencil_sbuffer_position_buffer_get(Object *ob, bool show_fill)
{
bGPdata *gpd = (bGPdata *)ob->data;
/* Fill batch also need stroke batch to be created (vbo is shared). */
gpencil_sbuffer_stroke_ensure(gpd, show_fill);
return gpd->runtime.sbuffer_position_buf;
}
gpu::VertBuf *DRW_cache_gpencil_sbuffer_color_buffer_get(Object *ob, bool show_fill)
{
bGPdata *gpd = (bGPdata *)ob->data;
/* Fill batch also need stroke batch to be created (vbo is shared). */
gpencil_sbuffer_stroke_ensure(gpd, show_fill);
return gpd->runtime.sbuffer_color_buf;
}
void DRW_cache_gpencil_sbuffer_clear(Object *ob)
{
bGPdata *gpd = (bGPdata *)ob->data;
@@ -717,285 +128,4 @@ void DRW_cache_gpencil_sbuffer_clear(Object *ob)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edit GPencil Batches
* \{ */
#define GP_EDIT_POINT_SELECTED (1 << 0)
#define GP_EDIT_STROKE_SELECTED (1 << 1)
#define GP_EDIT_MULTIFRAME (1 << 2)
#define GP_EDIT_STROKE_START (1 << 3)
#define GP_EDIT_STROKE_END (1 << 4)
#define GP_EDIT_POINT_DIMMED (1 << 5)
struct gpEditIterData {
gpEditVert *verts;
int vgindex;
};
struct gpEditCurveIterData {
gpEditCurveVert *verts;
int vgindex;
};
static uint32_t gpencil_point_edit_flag(const bool layer_lock,
const bGPDspoint *pt,
int v,
int v_len)
{
uint32_t sflag = 0;
SET_FLAG_FROM_TEST(sflag, (!layer_lock) && pt->flag & GP_SPOINT_SELECT, GP_EDIT_POINT_SELECTED);
SET_FLAG_FROM_TEST(sflag, v == 0, GP_EDIT_STROKE_START);
SET_FLAG_FROM_TEST(sflag, v == (v_len - 1), GP_EDIT_STROKE_END);
SET_FLAG_FROM_TEST(sflag, pt->runtime.pt_orig == nullptr, GP_EDIT_POINT_DIMMED);
return sflag;
}
static float gpencil_point_edit_weight(const MDeformVert *dvert, int v, int vgindex)
{
return (dvert && dvert[v].dw) ? BKE_defvert_find_weight(&dvert[v], vgindex) : -1.0f;
}
static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
void *thunk)
{
gpEditIterData *iter = (gpEditIterData *)thunk;
const int v_len = gps->totpoints;
const int v = gps->runtime.vertex_start + 1;
MDeformVert *dvert = ((iter->vgindex > -1) && gps->dvert) ? gps->dvert : nullptr;
gpEditVert *vert_ptr = iter->verts + v;
const bool layer_lock = (gpl->flag & GP_LAYER_LOCKED);
uint32_t sflag = 0;
SET_FLAG_FROM_TEST(
sflag, (!layer_lock) && gps->flag & GP_STROKE_SELECT, GP_EDIT_STROKE_SELECTED);
SET_FLAG_FROM_TEST(sflag, gpf->runtime.onion_id != 0.0f, GP_EDIT_MULTIFRAME);
for (int i = 0; i < v_len; i++) {
vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[i], i, v_len);
vert_ptr->weight = gpencil_point_edit_weight(dvert, i, iter->vgindex);
vert_ptr++;
}
if (gpencil_stroke_is_cyclic(gps)) {
/* Draw line to first point to complete the loop for cyclic strokes. */
vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[0], 0, v_len);
vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex);
}
}
static void gpencil_edit_curve_stroke_count_cb(bGPDlayer *gpl,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
if (gpl->flag & GP_LAYER_LOCKED) {
return;
}
gpIterData *iter = (gpIterData *)thunk;
if (gps->editcurve == nullptr) {
return;
}
/* Store first index offset */
gps->runtime.curve_start = iter->curve_len;
iter->curve_len += gps->editcurve->tot_curve_points * 4;
}
static uint32_t gpencil_beztriple_vflag_get(char flag,
char col_id,
bool handle_point,
const bool handle_selected)
{
uint32_t vflag = 0;
SET_FLAG_FROM_TEST(vflag, (flag & SELECT), VFLAG_VERT_SELECTED);
SET_FLAG_FROM_TEST(vflag, handle_point, BEZIER_HANDLE);
SET_FLAG_FROM_TEST(vflag, handle_selected, VFLAG_VERT_SELECTED_BEZT_HANDLE);
vflag |= VFLAG_VERT_GPENCIL_BEZT_HANDLE;
/* Handle color id. */
vflag |= col_id << COLOR_SHIFT;
return vflag;
}
static void gpencil_edit_curve_stroke_iter_cb(bGPDlayer *gpl,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
if (gpl->flag & GP_LAYER_LOCKED) {
return;
}
if (gps->editcurve == nullptr) {
return;
}
bGPDcurve *editcurve = gps->editcurve;
gpEditCurveIterData *iter = (gpEditCurveIterData *)thunk;
const int v = gps->runtime.curve_start;
gpEditCurveVert *vert_ptr = iter->verts + v;
/* Hide points when the curve is unselected. Passing the control point
* as handle produces the point shader skip it if you are not in ALL mode. */
const bool hide = !(editcurve->flag & GP_CURVE_SELECT);
for (int i = 0; i < editcurve->tot_curve_points; i++) {
BezTriple *bezt = &editcurve->curve_points[i].bezt;
const bool handle_selected = BEZT_ISSEL_ANY(bezt);
const uint32_t vflag[3] = {
gpencil_beztriple_vflag_get(bezt->f1, bezt->h1, true, handle_selected),
gpencil_beztriple_vflag_get(bezt->f2, bezt->h1, hide, handle_selected),
gpencil_beztriple_vflag_get(bezt->f3, bezt->h2, true, handle_selected),
};
/* First segment. */
mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[0]);
vert_ptr->data = vflag[0];
vert_ptr++;
mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[1]);
vert_ptr->data = vflag[1];
vert_ptr++;
/* Second segment. */
mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[1]);
vert_ptr->data = vflag[1];
vert_ptr++;
mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[2]);
vert_ptr->data = vflag[2];
vert_ptr++;
}
}
static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra)
{
bGPdata *gpd = (bGPdata *)ob->data;
if (cache->edit_vbo == nullptr) {
/* TODO/PERF: Could be changed to only do it if needed.
* For now it's simpler to assume we always need it
* since multiple viewport could or could not need it.
* Ideally we should have a dedicated onion skin geom batch. */
/* IMPORTANT: Keep in sync with gpencil_batches_ensure() */
bool do_onion = true;
/* Vertex counting has already been done for cache->vbo. */
BLI_assert(cache->vbo);
int vert_len = GPU_vertbuf_get_vertex_len(cache->vbo);
gpEditIterData iter;
iter.vgindex = gpd->vertex_group_active_index - 1;
if (!BLI_findlink(&gpd->vertex_group_names, iter.vgindex)) {
iter.vgindex = -1;
}
/* Create VBO. */
GPUVertFormat *format = gpencil_edit_stroke_format();
cache->edit_vbo = GPU_vertbuf_create_with_format(*format);
/* Add extra space at the end of the buffer because of quad load. */
GPU_vertbuf_data_alloc(*cache->edit_vbo, vert_len);
iter.verts = cache->edit_vbo->data<gpEditVert>().data();
/* Fill buffers with data. */
BKE_gpencil_visible_stroke_advanced_iter(
nullptr, ob, nullptr, gpencil_edit_stroke_iter_cb, &iter, do_onion, cfra);
/* Create the batches */
cache->edit_points_batch = GPU_batch_create(GPU_PRIM_POINTS, cache->vbo, nullptr);
GPU_batch_vertbuf_add(cache->edit_points_batch, cache->edit_vbo, false);
cache->edit_lines_batch = GPU_batch_create(GPU_PRIM_LINE_STRIP, cache->vbo, nullptr);
GPU_batch_vertbuf_add(cache->edit_lines_batch, cache->edit_vbo, false);
}
/* Curve Handles and Points for Editing. */
if (cache->edit_curve_vbo == nullptr) {
gpIterData iterdata = {};
iterdata.gpd = gpd;
iterdata.verts = nullptr;
iterdata.ibo = {0};
iterdata.vert_len = 0;
iterdata.tri_len = 0;
iterdata.curve_len = 0;
/* Create VBO. */
GPUVertFormat *format = gpencil_edit_curve_format();
cache->edit_curve_vbo = GPU_vertbuf_create_with_format(*format);
/* Count data. */
BKE_gpencil_visible_stroke_advanced_iter(
nullptr, ob, nullptr, gpencil_edit_curve_stroke_count_cb, &iterdata, false, cfra);
gpEditCurveIterData iter;
int vert_len = iterdata.curve_len;
if (vert_len > 0) {
GPU_vertbuf_data_alloc(*cache->edit_curve_vbo, vert_len);
iter.verts = cache->edit_curve_vbo->data<gpEditCurveVert>().data();
/* Fill buffers with data. */
BKE_gpencil_visible_stroke_advanced_iter(
nullptr, ob, nullptr, gpencil_edit_curve_stroke_iter_cb, &iter, false, cfra);
cache->edit_curve_handles_batch = GPU_batch_create(
GPU_PRIM_LINES, cache->edit_curve_vbo, nullptr);
GPU_batch_vertbuf_add(cache->edit_curve_handles_batch, cache->edit_curve_vbo, false);
cache->edit_curve_points_batch = GPU_batch_create(
GPU_PRIM_POINTS, cache->edit_curve_vbo, nullptr);
GPU_batch_vertbuf_add(cache->edit_curve_points_batch, cache->edit_curve_vbo, false);
}
gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
cache->is_dirty = false;
}
}
blender::gpu::Batch *DRW_cache_gpencil_edit_lines_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
gpencil_edit_batches_ensure(ob, cache, cfra);
return cache->edit_lines_batch;
}
blender::gpu::Batch *DRW_cache_gpencil_edit_points_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
gpencil_edit_batches_ensure(ob, cache, cfra);
return cache->edit_points_batch;
}
blender::gpu::Batch *DRW_cache_gpencil_edit_curve_handles_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
gpencil_edit_batches_ensure(ob, cache, cfra);
return cache->edit_curve_handles_batch;
}
blender::gpu::Batch *DRW_cache_gpencil_edit_curve_points_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
gpencil_edit_batches_ensure(ob, cache, cfra);
return cache->edit_curve_points_batch;
}
int DRW_gpencil_material_count_get(const bGPdata *gpd)
{
return max_ii(1, gpd->totcol);
}
/** \} */
} // namespace blender::draw

View File

@@ -35,7 +35,6 @@ set(SRC
eyedroppers/eyedropper_datablock.cc
eyedroppers/eyedropper_depth.cc
eyedroppers/eyedropper_driver.cc
eyedroppers/eyedropper_gpencil_color.cc
eyedroppers/eyedropper_grease_pencil_color.cc
eyedroppers/interface_eyedropper.cc
interface.cc

View File

@@ -1,374 +0,0 @@
/* SPDX-FileCopyrightText: 2009 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*
* Eyedropper (RGB Color)
*
* Defines:
* - #UI_OT_eyedropper_gpencil_color
*/
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLT_translation.hh"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_material_types.h"
#include "DNA_space_types.h"
#include "BKE_context.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_lib_id.hh"
#include "BKE_material.h"
#include "BKE_paint.hh"
#include "UI_interface.hh"
#include "IMB_colormanagement.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "ED_screen.hh"
#include "ED_undo.hh"
#include "DEG_depsgraph_build.hh"
#include "eyedropper_intern.hh"
#include "interface_intern.hh"
enum eGP_EyeMode {
GP_EYE_MATERIAL = 0,
GP_EYE_PALETTE = 1,
};
struct EyedropperGPencil {
ColorManagedDisplay *display;
/** color under cursor RGB */
float color[3];
/** Mode */
eGP_EyeMode mode;
};
/* Helper: Draw status message while the user is running the operator */
static void eyedropper_gpencil_status_indicators(bContext *C)
{
char msg_str[UI_MAX_DRAW_STR];
STRNCPY(msg_str, IFACE_("LMB: Stroke - Shift: Fill - Shift+Ctrl: Stroke + Fill"));
ED_workspace_status_text(C, msg_str);
}
/* Initialize. */
static bool eyedropper_gpencil_init(bContext *C, wmOperator *op)
{
EyedropperGPencil *eye = MEM_cnew<EyedropperGPencil>(__func__);
op->customdata = eye;
Scene *scene = CTX_data_scene(C);
const char *display_device;
display_device = scene->display_settings.display_device;
eye->display = IMB_colormanagement_display_get_named(display_device);
eye->mode = (eGP_EyeMode)RNA_enum_get(op->ptr, "mode");
return true;
}
/* Exit and free memory. */
static void eyedropper_gpencil_exit(bContext *C, wmOperator *op)
{
/* Clear status message area. */
ED_workspace_status_text(C, nullptr);
MEM_SAFE_FREE(op->customdata);
}
static void eyedropper_add_material(bContext *C,
const float col_conv[4],
const bool only_stroke,
const bool only_fill,
const bool both)
{
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
Material *ma = nullptr;
bool found = false;
/* Look for a similar material in grease pencil slots. */
short *totcol = BKE_object_material_len_p(ob);
for (short i = 0; i < *totcol; i++) {
ma = BKE_object_material_get(ob, i + 1);
if (ma == nullptr) {
continue;
}
MaterialGPencilStyle *gp_style = ma->gp_style;
if (gp_style != nullptr) {
/* Check stroke color. */
bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) &&
(gp_style->flag & GP_MATERIAL_STROKE_SHOW);
/* Check fill color. */
bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) &&
(gp_style->flag & GP_MATERIAL_FILL_SHOW);
if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) {
found = true;
}
else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0)) {
found = true;
}
else if ((both) && (found_stroke) && (found_fill)) {
found = true;
}
/* Found existing material. */
if (found) {
ob->actcol = i + 1;
WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, nullptr);
WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr);
return;
}
}
}
/* If material was not found add a new material with stroke and/or fill color
* depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill)
*/
int idx;
Material *ma_new = BKE_gpencil_object_material_new(bmain, ob, "Material", &idx);
WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, &ob->id);
WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, nullptr);
DEG_relations_tag_update(bmain);
BLI_assert(ma_new != nullptr);
MaterialGPencilStyle *gp_style_new = ma_new->gp_style;
BLI_assert(gp_style_new != nullptr);
/* Only create Stroke (default option). */
if (only_stroke) {
/* Stroke color. */
gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW;
gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW;
copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
zero_v4(gp_style_new->fill_rgba);
}
/* Fill Only. */
else if (only_fill) {
/* Fill color. */
gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW;
gp_style_new->flag |= GP_MATERIAL_FILL_SHOW;
zero_v4(gp_style_new->stroke_rgba);
copy_v3_v3(gp_style_new->fill_rgba, col_conv);
}
/* Stroke and Fill. */
else if (both) {
gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW | GP_MATERIAL_FILL_SHOW;
copy_v3_v3(gp_style_new->stroke_rgba, col_conv);
copy_v3_v3(gp_style_new->fill_rgba, col_conv);
}
/* Push undo for new created material. */
ED_undo_push(C, "Add Grease Pencil Material");
}
/* Create a new palette color and palette if needed. */
static void eyedropper_add_palette_color(bContext *C, const float col_conv[4])
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
GpPaint *gp_paint = ts->gp_paint;
GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint;
Paint *paint = &gp_paint->paint;
Paint *vertexpaint = &gp_vertexpaint->paint;
/* Check for Palette in Draw and Vertex Paint Mode. */
if (paint->palette == nullptr) {
Palette *palette = BKE_palette_add(bmain, "Grease Pencil");
id_us_min(&palette->id);
BKE_paint_palette_set(paint, palette);
if (vertexpaint->palette == nullptr) {
BKE_paint_palette_set(vertexpaint, palette);
}
}
/* Check if the color exist already. */
Palette *palette = paint->palette;
LISTBASE_FOREACH (PaletteColor *, palcolor, &palette->colors) {
if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) {
return;
}
}
/* Create Colors. */
PaletteColor *palcol = BKE_palette_color_add(palette);
if (palcol) {
copy_v3_v3(palcol->rgb, col_conv);
}
}
/* Set the material or the palette color. */
static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye)
{
const bool only_stroke = (event->modifier & (KM_CTRL | KM_SHIFT)) == 0;
const bool only_fill = ((event->modifier & KM_CTRL) == 0 && (event->modifier & KM_SHIFT));
const bool both = ((event->modifier & KM_CTRL) && (event->modifier & KM_SHIFT));
float col_conv[4];
/* Convert from linear rgb space to display space because palette colors are in display
* space, and this conversion is needed to undo the conversion to linear performed by
* eyedropper_color_sample_fl. */
if ((eye->display) && (eye->mode == GP_EYE_PALETTE)) {
copy_v3_v3(col_conv, eye->color);
IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
}
else {
copy_v3_v3(col_conv, eye->color);
}
/* Add material or Palette color. */
if (eye->mode == GP_EYE_MATERIAL) {
eyedropper_add_material(C, col_conv, only_stroke, only_fill, both);
}
else {
eyedropper_add_palette_color(C, col_conv);
}
}
/* Sample the color below cursor. */
static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, const int m_xy[2])
{
eyedropper_color_sample_fl(C, nullptr, m_xy, eye->color);
}
/* Cancel operator. */
static void eyedropper_gpencil_cancel(bContext *C, wmOperator *op)
{
eyedropper_gpencil_exit(C, op);
}
/* Main modal status check. */
static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
EyedropperGPencil *eye = (EyedropperGPencil *)op->customdata;
/* Handle modal keymap */
switch (event->type) {
case EVT_MODAL_MAP: {
switch (event->val) {
case EYE_MODAL_SAMPLE_BEGIN: {
return OPERATOR_RUNNING_MODAL;
}
case EYE_MODAL_CANCEL: {
eyedropper_gpencil_cancel(C, op);
return OPERATOR_CANCELLED;
}
case EYE_MODAL_SAMPLE_CONFIRM: {
eyedropper_gpencil_color_sample(C, eye, event->xy);
/* Create material. */
eyedropper_gpencil_color_set(C, event, eye);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, nullptr);
eyedropper_gpencil_exit(C, op);
return OPERATOR_FINISHED;
}
default: {
break;
}
}
break;
}
case MOUSEMOVE:
case INBETWEEN_MOUSEMOVE: {
eyedropper_gpencil_color_sample(C, eye, event->xy);
break;
}
default: {
break;
}
}
return OPERATOR_RUNNING_MODAL;
}
/* Modal Operator init */
static int eyedropper_gpencil_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
/* Init. */
if (eyedropper_gpencil_init(C, op)) {
/* Add modal temp handler. */
WM_event_add_modal_handler(C, op);
/* Status message. */
eyedropper_gpencil_status_indicators(C);
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_PASS_THROUGH;
}
/* Repeat operator */
static int eyedropper_gpencil_exec(bContext *C, wmOperator *op)
{
/* init */
if (eyedropper_gpencil_init(C, op)) {
/* cleanup */
eyedropper_gpencil_exit(C, op);
return OPERATOR_FINISHED;
}
return OPERATOR_PASS_THROUGH;
}
static bool eyedropper_gpencil_poll(bContext *C)
{
/* Only valid if the current active object is grease pencil. */
Object *obact = CTX_data_active_object(C);
if ((obact == nullptr) || (obact->type != OB_GPENCIL_LEGACY)) {
return false;
}
/* Test we have a window below. */
return (CTX_wm_window(C) != nullptr);
}
void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot)
{
static const EnumPropertyItem items_mode[] = {
{GP_EYE_MATERIAL, "MATERIAL", 0, "Material", ""},
{GP_EYE_PALETTE, "PALETTE", 0, "Palette", ""},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Grease Pencil Eyedropper";
ot->idname = "UI_OT_eyedropper_gpencil_color";
ot->description = "Sample a color from the Blender Window and create Grease Pencil material";
/* api callbacks */
ot->invoke = eyedropper_gpencil_invoke;
ot->modal = eyedropper_gpencil_modal;
ot->cancel = eyedropper_gpencil_cancel;
ot->exec = eyedropper_gpencil_exec;
ot->poll = eyedropper_gpencil_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, GP_EYE_MATERIAL, "Mode", "");
}

View File

@@ -24,7 +24,7 @@
#include "DNA_space_types.h"
#include "BKE_context.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_id.hh"
#include "BKE_material.h"
#include "BKE_paint.hh"
@@ -203,7 +203,7 @@ static void eyedropper_add_material(bContext *C,
* depending of the secondary key (LMB: Stroke, Shift: Fill, Shift+Ctrl: Stroke/Fill)
*/
int idx;
Material *ma_new = BKE_gpencil_object_material_new(bmain, ob, "Material", &idx);
Material *ma_new = BKE_grease_pencil_object_material_new(bmain, ob, "Material", &idx);
WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, &ob->id);
WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, nullptr);
DEG_relations_tag_update(bmain);
@@ -295,9 +295,9 @@ static void eyedropper_set_brush_color(bContext *C, const float3 &col_conv)
}
/* Set the material or the palette color. */
static void eyedropper_gpencil_color_set(bContext *C,
const wmEvent *event,
EyedropperGreasePencil *eye)
static void eyedropper_grease_pencil_color_set(bContext *C,
const wmEvent *event,
EyedropperGreasePencil *eye)
{
const bool is_ctrl = (event->modifier & KM_CTRL) != 0;
const bool is_shift = (event->modifier & KM_SHIFT) != 0;
@@ -386,7 +386,7 @@ static int eyedropper_grease_pencil_modal(bContext *C, wmOperator *op, const wmE
eyedropper_grease_pencil_color_sample(C, eye, event->xy);
/* Create material. */
eyedropper_gpencil_color_set(C, event, eye);
eyedropper_grease_pencil_color_set(C, event, eye);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, nullptr);
eyedropper_grease_pencil_exit(C, op);

View File

@@ -56,7 +56,6 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_bone");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_gpencil_color");
WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_grease_pencil_color");
return keymap;

View File

@@ -1570,10 +1570,6 @@ void UI_OT_eyedropper_depth(wmOperatorType *ot);
void UI_OT_eyedropper_driver(wmOperatorType *ot);
/* interface_eyedropper_gpencil_color.c */
void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot);
/* eyedropper_grease_pencil_color.cc */
void UI_OT_eyedropper_grease_pencil_color(wmOperatorType *ot);

View File

@@ -2845,7 +2845,6 @@ void ED_operatortypes_ui()
WM_operatortype_append(UI_OT_eyedropper_id);
WM_operatortype_append(UI_OT_eyedropper_depth);
WM_operatortype_append(UI_OT_eyedropper_driver);
WM_operatortype_append(UI_OT_eyedropper_gpencil_color);
WM_operatortype_append(UI_OT_eyedropper_bone);
WM_operatortype_append(UI_OT_eyedropper_grease_pencil_color);
}

View File

@@ -171,24 +171,6 @@ static void stats_object(Object *ob,
stats->totlampsel++;
}
break;
case OB_GPENCIL_LEGACY: {
if (is_selected) {
bGPdata *gpd = (bGPdata *)ob->data;
if (!BLI_gset_add(objects_gset, gpd)) {
break;
}
/* GPXX Review if we can move to other place when object change
* maybe to depsgraph evaluation
*/
BKE_gpencil_stats_update(gpd);
stats->totgplayer += gpd->totlayer;
stats->totgpframe += gpd->totframe;
stats->totgpstroke += gpd->totstroke;
stats->totgppoint += gpd->totpoint;
}
break;
}
case OB_GREASE_PENCIL: {
if (!is_selected) {
break;

View File

@@ -5,19 +5,18 @@
#include "BLI_color.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BKE_attribute.hh"
#include "BKE_camera.h"
#include "BKE_context.hh"
#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_layer.hh"
#include "BKE_material.h"
#include "BKE_scene.hh"
#include "BLI_math_vector.hh"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_object_types.h"
@@ -126,12 +125,13 @@ int GreasePencilImporter::create_material(const StringRefNull name,
{
const ColorGeometry4f default_stroke_color = {0.0f, 0.0f, 0.0f, 1.0f};
const ColorGeometry4f default_fill_color = {0.5f, 0.5f, 0.5f, 1.0f};
int mat_index = BKE_gpencil_object_material_index_get_by_name(object_, name.c_str());
int mat_index = BKE_grease_pencil_object_material_index_get_by_name(object_, name.c_str());
/* Stroke and Fill material. */
if (mat_index == -1) {
Main *bmain = CTX_data_main(&context_.C);
int new_idx;
Material *mat_gp = BKE_gpencil_object_material_new(bmain, object_, name.c_str(), &new_idx);
Material *mat_gp = BKE_grease_pencil_object_material_new(
bmain, object_, name.c_str(), &new_idx);
MaterialGPencilStyle *gp_style = mat_gp->gp_style;
gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
gp_style->flag &= ~GP_MATERIAL_FILL_SHOW;

View File

@@ -333,9 +333,6 @@ typedef struct bGPDstroke {
/** Curve used to edit the stroke using Bezier handlers. */
struct bGPDcurve *editcurve;
/* NOTE: When adding new members, make sure to add them to BKE_gpencil_stroke_copy_settings as
* well! */
bGPDstroke_Runtime runtime;
void *_pad5;
} bGPDstroke;
@@ -429,9 +426,6 @@ typedef struct bGPDframe {
/** Keyframe type (eBezTriple_KeyframeType). */
short key_type;
/* NOTE: When adding new members, make sure to add them to BKE_gpencil_frame_copy_settings as
* well! */
bGPDframe_Runtime runtime;
} bGPDframe;
@@ -561,9 +555,6 @@ typedef struct bGPDlayer {
float layer_mat[4][4], layer_invmat[4][4];
char _pad3[4];
/* NOTE: When adding new members, make sure to add them to BKE_gpencil_layer_copy_settings as
* well! */
bGPDlayer_Runtime runtime;
} bGPDlayer;
@@ -769,9 +760,6 @@ typedef struct bGPdata {
bGPgrid grid;
/* NOTE: When adding new members, make sure to add them to BKE_gpencil_data_copy_settings as
* well! */
bGPdata_Runtime runtime;
} bGPdata;

View File

@@ -800,31 +800,6 @@ static bool rna_Object_update_from_editmode(Object *ob, Main *bmain)
return result;
}
bool rna_Object_generate_gpencil_strokes(Object *ob,
bContext *C,
ReportList *reports,
Object *ob_gpencil,
bool use_collections,
float scale_thickness,
float sample)
{
if (ob->type != OB_CURVES_LEGACY) {
BKE_reportf(reports,
RPT_ERROR,
"Object '%s' is not valid for this operation! Only curves are supported",
ob->id.name + 2);
return false;
}
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
BKE_gpencil_convert_curve(
bmain, scene, ob_gpencil, ob, use_collections, scale_thickness, sample);
WM_main_add_notifier(NC_GPENCIL | ND_DATA, nullptr);
return true;
}
#else /* RNA_RUNTIME */
void RNA_api_object(StructRNA *srna)
@@ -1356,25 +1331,6 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_ui_description(func,
"Release memory used by caches associated with this object. "
"Intended to be used by render engines only.");
/* Convert curve object to gpencil strokes. */
func = RNA_def_function(srna, "generate_gpencil_strokes", "rna_Object_generate_gpencil_strokes");
RNA_def_function_ui_description(func, "Convert a curve object to grease pencil strokes.");
RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
parm = RNA_def_pointer(func,
"grease_pencil_object",
"Object",
"",
"Grease Pencil object used to create new strokes");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_boolean(func, "use_collections", true, "", "Use Collections");
parm = RNA_def_float(
func, "scale_thickness", 1.0f, 0.0f, FLT_MAX, "", "Thickness scaling factor", 0.0f, 100.0f);
parm = RNA_def_float(
func, "sample", 0.0f, 0.0f, FLT_MAX, "", "Sample distance, zero to disable", 0.0f, 100.0f);
parm = RNA_def_boolean(func, "result", false, "", "Result");
RNA_def_function_return(func, parm);
}
#endif /* RNA_RUNTIME */

View File

@@ -871,7 +871,6 @@ struct GreasePencilLineartModifierData;
struct LineartData;
struct Scene;
void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd_legacy);
void MOD_lineart_destroy_render_data_v3(struct GreasePencilLineartModifierData *lmd);
void MOD_lineart_chain_feature_lines(LineartData *ld);
@@ -902,10 +901,6 @@ void MOD_lineart_finalize_chains(LineartData *ld);
*
* \return True when a change is made.
*/
bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph,
struct LineartGpencilModifierData *lmd_legacy,
struct LineartCache **cached_result,
bool enable_stroke_depth_offset);
bool MOD_lineart_compute_feature_lines_v3(struct Depsgraph *depsgraph,
struct GreasePencilLineartModifierData &lmd,
struct LineartCache **cached_result,
@@ -921,32 +916,6 @@ LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartData *ld, doubl
*/
LineartBoundingArea *MOD_lineart_get_bounding_area(LineartData *ld, double x, double y);
/**
* Wrapper for external calls.
*/
void MOD_lineart_gpencil_generate(LineartCache *cache,
struct Depsgraph *depsgraph,
struct Object *ob,
struct bGPDlayer *gpl,
struct bGPDframe *gpf,
int8_t source_type,
void *source_reference,
int level_start,
int level_end,
int mat_nr,
int16_t edge_types,
uint8_t mask_switches,
uint8_t material_mask_bits,
uint8_t intersection_mask,
int16_t thickness,
float opacity,
uint8_t shadow_selection,
uint8_t silhouette_mode,
const char *source_vgname,
const char *vgname,
int modifier_flags,
int modifier_calculation_flags);
namespace blender::bke::greasepencil {
class Drawing;
}

View File

@@ -3576,14 +3576,6 @@ void MOD_lineart_destroy_render_data_v3(GreasePencilLineartModifierData *lmd)
}
}
void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd_legacy)
{
GreasePencilLineartModifierData lmd;
greasepencil::convert::lineart_wrap_v3(lmd_legacy, &lmd);
MOD_lineart_destroy_render_data_v3(&lmd);
greasepencil::convert::lineart_unwrap_v3(lmd_legacy, &lmd);
}
LineartCache *MOD_lineart_init_cache()
{
LineartCache *lc = static_cast<LineartCache *>(
@@ -5229,327 +5221,11 @@ bool MOD_lineart_compute_feature_lines_v3(Depsgraph *depsgraph,
return true;
}
bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
LineartGpencilModifierData *lmd_legacy,
LineartCache **cached_result,
bool enable_stroke_depth_offset)
{
bool ret = false;
GreasePencilLineartModifierData lmd;
greasepencil::convert::lineart_wrap_v3(lmd_legacy, &lmd);
ret = MOD_lineart_compute_feature_lines_v3(
depsgraph, lmd, cached_result, enable_stroke_depth_offset);
greasepencil::convert::lineart_unwrap_v3(lmd_legacy, &lmd);
return ret;
}
static void lineart_gpencil_generate(LineartCache *cache,
Depsgraph *depsgraph,
Object *gpencil_object,
float (*gp_obmat_inverse)[4],
bGPDlayer * /*gpl*/,
bGPDframe *gpf,
int level_start,
int level_end,
int material_nr,
Object *source_object,
Collection *source_collection,
int types,
uchar mask_switches,
uchar material_mask_bits,
uchar intersection_mask,
int16_t thickness,
float opacity,
uchar shaodow_selection,
uchar silhouette_mode,
const char *source_vgname,
const char *vgname,
int modifier_flags,
int modifier_calculation_flags)
{
if (cache == nullptr) {
if (G.debug_value == 4000) {
printf("nullptr Lineart cache!\n");
}
return;
}
int stroke_count = 0;
int color_idx = 0;
Object *orig_ob = nullptr;
if (source_object) {
orig_ob = source_object->id.orig_id ? (Object *)source_object->id.orig_id : source_object;
}
Collection *orig_col = nullptr;
if (source_collection) {
orig_col = source_collection->id.orig_id ? (Collection *)source_collection->id.orig_id :
source_collection;
}
/* (!orig_col && !orig_ob) means the whole scene is selected. */
int enabled_types = cache->all_enabled_edge_types;
bool invert_input = modifier_calculation_flags & MOD_LINEART_INVERT_SOURCE_VGROUP;
bool match_output = modifier_calculation_flags & MOD_LINEART_MATCH_OUTPUT_VGROUP;
bool inverse_silhouette = modifier_flags & MOD_LINEART_INVERT_SILHOUETTE_FILTER;
LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) {
if (ec->picked) {
continue;
}
if (!(ec->type & (types & enabled_types))) {
continue;
}
if (ec->level > level_end || ec->level < level_start) {
continue;
}
if (orig_ob && orig_ob != ec->object_ref) {
continue;
}
if (orig_col && ec->object_ref) {
if (BKE_collection_has_object_recursive_instanced(orig_col, (Object *)ec->object_ref)) {
if (modifier_flags & MOD_LINEART_INVERT_COLLECTION) {
continue;
}
}
else {
if (!(modifier_flags & MOD_LINEART_INVERT_COLLECTION)) {
continue;
}
}
}
if (mask_switches & MOD_LINEART_MATERIAL_MASK_ENABLE) {
if (mask_switches & MOD_LINEART_MATERIAL_MASK_MATCH) {
if (ec->material_mask_bits != material_mask_bits) {
continue;
}
}
else {
if (!(ec->material_mask_bits & material_mask_bits)) {
continue;
}
}
}
if (ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) {
if (mask_switches & MOD_LINEART_INTERSECTION_MATCH) {
if (ec->intersection_mask != intersection_mask) {
continue;
}
}
else {
if ((intersection_mask) && !(ec->intersection_mask & intersection_mask)) {
continue;
}
}
}
if (shaodow_selection) {
if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) {
/* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */
if (shaodow_selection == LINEART_SHADOW_FILTER_ILLUMINATED &&
!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED))
{
continue;
}
if (shaodow_selection == LINEART_SHADOW_FILTER_SHADED &&
!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED))
{
continue;
}
if (shaodow_selection == LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) {
uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS;
if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) &&
(test_bits != (LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_ILLUMINATED_SHAPE)))
{
continue;
}
}
}
}
if (silhouette_mode && (ec->type & (MOD_LINEART_EDGE_FLAG_CONTOUR))) {
bool is_silhouette = false;
if (orig_col) {
if (!ec->silhouette_backdrop) {
is_silhouette = true;
}
else if (!BKE_collection_has_object_recursive_instanced(orig_col, ec->silhouette_backdrop))
{
is_silhouette = true;
}
}
else {
if ((!orig_ob) && (!ec->silhouette_backdrop)) {
is_silhouette = true;
}
}
if ((silhouette_mode == LINEART_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) &&
ec->silhouette_backdrop != ec->object_ref)
{
is_silhouette = true;
}
if (inverse_silhouette) {
is_silhouette = !is_silhouette;
}
if (!is_silhouette) {
continue;
}
}
/* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
// ec->picked = 1;
const int count = MOD_lineart_chain_count(ec);
if (count < 2) {
continue;
}
bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false);
int i;
LISTBASE_FOREACH_INDEX (LineartEdgeChainItem *, eci, &ec->chain, i) {
bGPDspoint *point = &gps->points[i];
mul_v3_m4v3(&point->x, gp_obmat_inverse, eci->gpos);
point->pressure = 1.0f;
point->strength = opacity;
}
BKE_gpencil_dvert_ensure(gps);
gps->mat_nr = max_ii(material_nr, 0);
if (source_vgname && vgname) {
Object *eval_ob = DEG_get_evaluated_object(depsgraph, ec->object_ref);
int gpdg = -1;
if (match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0) {
if (eval_ob && eval_ob->type == OB_MESH) {
int dindex = 0;
Mesh *mesh = BKE_object_get_evaluated_mesh(eval_ob);
MDeformVert *dvert = mesh->deform_verts_for_write().data();
if (dvert) {
LISTBASE_FOREACH (bDeformGroup *, db, &mesh->vertex_group_names) {
if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) {
if (match_output) {
gpdg = BKE_object_defgroup_name_index(gpencil_object, db->name);
if (gpdg < 0) {
continue;
}
}
int sindex = 0, vindex;
LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
vindex = eci->index;
if (vindex >= mesh->verts_num) {
break;
}
MDeformWeight *mdw = BKE_defvert_ensure_index(&dvert[vindex], dindex);
MDeformWeight *gdw = BKE_defvert_ensure_index(&gps->dvert[sindex], gpdg);
float use_weight = mdw->weight;
if (invert_input) {
use_weight = 1 - use_weight;
}
gdw->weight = std::max(use_weight, gdw->weight);
sindex++;
}
}
dindex++;
}
}
}
}
}
if (G.debug_value == 4000) {
BKE_gpencil_stroke_set_random_color(gps);
}
BKE_gpencil_stroke_geometry_update(static_cast<bGPdata *>(gpencil_object->data), gps);
stroke_count++;
}
if (G.debug_value == 4000) {
printf("LRT: Generated %d strokes.\n", stroke_count);
}
}
typedef struct LineartChainWriteInfo {
LineartEdgeChain *chain;
int point_count;
} LineartChainWriteInfo;
void MOD_lineart_gpencil_generate(LineartCache *cache,
Depsgraph *depsgraph,
Object *ob,
bGPDlayer *gpl,
bGPDframe *gpf,
int8_t source_type,
void *source_reference,
int level_start,
int level_end,
int mat_nr,
int16_t edge_types,
uchar mask_switches,
uchar material_mask_bits,
uchar intersection_mask,
int16_t thickness,
float opacity,
uchar shadow_selection,
uchar silhouette_mode,
const char *source_vgname,
const char *vgname,
int modifier_flags,
int modifier_calculation_flags)
{
if (!gpl || !gpf || !ob) {
return;
}
Object *source_object = nullptr;
Collection *source_collection = nullptr;
int16_t use_types = edge_types;
if (source_type == LINEART_SOURCE_OBJECT) {
if (!source_reference) {
return;
}
source_object = (Object *)source_reference;
}
else if (source_type == LINEART_SOURCE_COLLECTION) {
if (!source_reference) {
return;
}
source_collection = (Collection *)source_reference;
}
float gp_obmat_inverse[4][4];
invert_m4_m4(gp_obmat_inverse, ob->object_to_world().ptr());
lineart_gpencil_generate(cache,
depsgraph,
ob,
gp_obmat_inverse,
gpl,
gpf,
level_start,
level_end,
mat_nr,
source_object,
source_collection,
use_types,
mask_switches,
material_mask_bits,
intersection_mask,
thickness,
opacity,
shadow_selection,
silhouette_mode,
source_vgname,
vgname,
modifier_flags,
modifier_calculation_flags);
}
void MOD_lineart_gpencil_generate_v3(const LineartCache *cache,
const blender::float4x4 &inverse_mat,
Depsgraph *depsgraph,