Cleanup: GPv3: Remove unused BKE functions from GPv2
Removes unused GPv2 functions in blenkernel. Notes: - Functions for layer masks are still in use, but annotations never have layer masks in the first place. Would be good to remove the data structures so we can remove the functions too. - Some multi-frame edit functions are also still nominally used, but multi-frame editing is not an active feature for annotations. This should also be removed. Pull Request: https://projects.blender.org/blender/blender/pulls/128709
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;
|
||||
|
||||
@@ -607,7 +607,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