Merge branch 'blender-v4.3-release'
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()`.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
* \{ */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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", "");
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user