Merge branch 'blender-v4.3-release'

This commit is contained in:
Lukas Tönne
2024-10-09 10:28:39 +02:00
47 changed files with 26 additions and 8139 deletions
-1
View File
@@ -26,7 +26,6 @@ _modules = [
"properties_data_curve",
"properties_data_curves",
"properties_data_empty",
"properties_data_gpencil",
"properties_data_grease_pencil",
"properties_data_light",
"properties_data_lattice",
@@ -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);
}
}
}
}
}
}
/** \} */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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;
-2
View File
@@ -56,7 +56,6 @@ set(SRC
intern/eval/deg_eval_flush.cc
intern/eval/deg_eval_runtime_backup.cc
intern/eval/deg_eval_runtime_backup_animation.cc
intern/eval/deg_eval_runtime_backup_gpencil.cc
intern/eval/deg_eval_runtime_backup_modifier.cc
intern/eval/deg_eval_runtime_backup_movieclip.cc
intern/eval/deg_eval_runtime_backup_object.cc
@@ -126,7 +125,6 @@ set(SRC
intern/eval/deg_eval_flush.h
intern/eval/deg_eval_runtime_backup.h
intern/eval/deg_eval_runtime_backup_animation.h
intern/eval/deg_eval_runtime_backup_gpencil.h
intern/eval/deg_eval_runtime_backup_modifier.h
intern/eval/deg_eval_runtime_backup_movieclip.h
intern/eval/deg_eval_runtime_backup_object.h
@@ -1783,18 +1783,6 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
break;
}
case ID_GD_LEGACY: {
/* GPencil evaluation operations. */
op_node = add_operation_node(obdata,
NodeType::GEOMETRY,
OperationCode::GEOMETRY_EVAL,
[obdata_cow](::Depsgraph *depsgraph) {
BKE_gpencil_frame_active_set(depsgraph,
(bGPdata *)obdata_cow);
});
op_node->set_as_entry();
break;
}
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(obdata);
@@ -888,14 +888,6 @@ ID *deg_update_eval_copy_datablock(const Depsgraph *depsgraph, const IDNode *id_
update_edit_mode_pointers(depsgraph, id_orig, id_cow);
return id_cow;
}
/* In case we don't need to do a copy-on-evaluation, we can use the update cache of the grease
* pencil data to do an update-on-write. */
if (id_type == ID_GD_LEGACY && BKE_gpencil_can_avoid_full_copy_on_write(
(const ::Depsgraph *)depsgraph, (bGPdata *)id_orig))
{
BKE_gpencil_update_on_write((bGPdata *)id_orig, (bGPdata *)id_cow);
return id_cow;
}
}
RuntimeBackup backup(depsgraph);
@@ -25,8 +25,7 @@ RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
object_backup(depsgraph),
drawdata_ptr(nullptr),
movieclip_backup(depsgraph),
volume_backup(depsgraph),
gpencil_backup(depsgraph)
volume_backup(depsgraph)
{
drawdata_backup.first = drawdata_backup.last = nullptr;
}
@@ -61,9 +60,6 @@ void RuntimeBackup::init_from_id(ID *id)
case ID_VO:
volume_backup.init_from_volume(reinterpret_cast<Volume *>(id));
break;
case ID_GD_LEGACY:
gpencil_backup.init_from_gpencil(reinterpret_cast<bGPdata *>(id));
break;
default:
break;
}
@@ -104,9 +100,6 @@ void RuntimeBackup::restore_to_id(ID *id)
case ID_VO:
volume_backup.restore_to_volume(reinterpret_cast<Volume *>(id));
break;
case ID_GD_LEGACY:
gpencil_backup.restore_to_gpencil(reinterpret_cast<bGPdata *>(id));
break;
default:
break;
}
@@ -11,7 +11,6 @@
#include "DNA_ID.h"
#include "intern/eval/deg_eval_runtime_backup_animation.h"
#include "intern/eval/deg_eval_runtime_backup_gpencil.h"
#include "intern/eval/deg_eval_runtime_backup_movieclip.h"
#include "intern/eval/deg_eval_runtime_backup_object.h"
#include "intern/eval/deg_eval_runtime_backup_scene.h"
@@ -56,7 +55,6 @@ class RuntimeBackup {
DrawDataList *drawdata_ptr;
MovieClipBackup movieclip_backup;
VolumeBackup volume_backup;
GPencilBackup gpencil_backup;
};
} // namespace blender::deg
@@ -1,40 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/eval/deg_eval_runtime_backup_gpencil.h"
#include "intern/depsgraph.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_gpencil_update_cache_legacy.h"
#include "DNA_gpencil_legacy_types.h"
namespace blender::deg {
GPencilBackup::GPencilBackup(const Depsgraph *depsgraph) : depsgraph(depsgraph) {}
void GPencilBackup::init_from_gpencil(bGPdata * /*gpd*/) {}
void GPencilBackup::restore_to_gpencil(bGPdata *gpd)
{
bGPdata *gpd_orig = reinterpret_cast<bGPdata *>(gpd->id.orig_id);
/* We check for the active depsgraph here to avoid freeing the cache on the original object
* multiple times. This free is only needed for the case where we tagged a full update in the
* update cache and did not do an update-on-write. */
if (depsgraph->is_active) {
BKE_gpencil_free_update_cache(gpd_orig);
}
/* Doing a copy-on-evaluation copies the update cache pointer. Make sure to reset it
* to null as we should never use the update cache from eval data. */
gpd->runtime.update_cache = nullptr;
/* Make sure to update the original runtime pointers in the eval data. */
BKE_gpencil_data_update_orig_pointers(gpd_orig, gpd);
}
} // namespace blender::deg
@@ -1,28 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
struct bGPdata;
namespace blender::deg {
struct Depsgraph;
/* Backup of volume datablocks runtime data. */
class GPencilBackup {
public:
GPencilBackup(const Depsgraph *depsgraph);
void init_from_gpencil(bGPdata *gpd);
void restore_to_gpencil(bGPdata *gpd);
const Depsgraph *depsgraph;
};
} // namespace blender::deg
@@ -294,9 +294,6 @@ void Instance::object_sync(Object *ob)
case OB_CURVES:
sync.sync_curves(ob, ob_handle, res_handle, ob_ref);
break;
case OB_GREASE_PENCIL:
sync.sync_gpencil(ob, ob_handle, res_handle);
break;
case OB_LIGHTPROBE:
light_probes.sync_probe(ob, ob_handle);
break;
@@ -393,149 +393,6 @@ void SyncModule::sync_volume(Object *ob,
/** \} */
/* -------------------------------------------------------------------- */
/** \name GPencil
* \{ */
#define DO_BATCHING true
struct gpIterData {
Instance &inst;
Object *ob;
MaterialArray &material_array;
int cfra;
/* Drawcall batching. */
gpu::Batch *geom = nullptr;
Material *material = nullptr;
int vfirst = 0;
int vcount = 0;
bool instancing = false;
gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle, ResourceHandle resource_handle)
: inst(inst_),
ob(ob_),
material_array(inst_.materials.material_array_get(
ob_,
inst_.velocity.step_object_sync(
ob, ob_handle.object_key, resource_handle, ob_handle.recalc)))
{
cfra = DEG_get_ctime(inst.depsgraph);
};
};
static void gpencil_drawcall_flush(gpIterData &iter)
{
#if 0 /* Incompatible with new draw manager. */
if (iter.geom != nullptr) {
geometry_call(iter.material->shading.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
geometry_call(iter.material->prepass.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
geometry_call(iter.material->shadow.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
}
#endif
iter.geom = nullptr;
iter.vfirst = -1;
iter.vcount = 0;
}
/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */
static void gpencil_drawcall_add(gpIterData &iter,
gpu::Batch *geom,
Material *material,
int v_first,
int v_count,
bool instancing)
{
int last = iter.vfirst + iter.vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if (!DO_BATCHING || (geom != iter.geom) || (material != iter.material) || (v_first - last > 3)) {
gpencil_drawcall_flush(iter);
}
iter.geom = geom;
iter.material = material;
iter.instancing = instancing;
if (iter.vfirst == -1) {
iter.vfirst = v_first;
}
iter.vcount = v_first + v_count - iter.vfirst;
}
static void gpencil_stroke_sync(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
gpIterData &iter = *(gpIterData *)thunk;
Material *material = &iter.material_array.materials[gps->mat_nr];
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter.ob, gps->mat_nr + 1);
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
(!DRW_state_is_image_render() && ((gps->flag & GP_STROKE_NOFILL) != 0));
bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0);
if (hide_material) {
return;
}
gpu::Batch *geom = DRW_cache_gpencil_get(iter.ob, iter.cfra);
if (show_fill) {
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, false);
}
if (show_stroke) {
/* Start one vert before to have gl_InstanceID > 0 (see shader). */
int vfirst = gps->runtime.stroke_start * 3;
/* Include "potential" cyclic vertex and start adj vertex (see shader). */
int vcount = gps->totpoints + 1 + 1;
gpencil_drawcall_add(iter, geom, material, vfirst, vcount, true);
}
}
void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle)
{
/* TODO(fclem): Waiting for a user option to use the render engine instead of gpencil engine. */
return;
/* Is this a surface or curves? */
if (!inst_.use_surfaces) {
return;
}
UNUSED_VARS(res_handle);
gpIterData iter(inst_, ob, ob_handle, res_handle);
BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter);
gpencil_drawcall_flush(iter);
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
bool has_transparent_shadows = true; /* TODO material.has_transparent_shadows. */
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Hair
* \{ */
@@ -178,7 +178,6 @@ class SyncModule {
ObjectHandle &ob_handle,
ResourceHandle res_handle,
const ObjectRef &ob_ref);
void sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle);
void sync_curves(Object *ob,
ObjectHandle &ob_handle,
ResourceHandle res_handle,
@@ -244,16 +244,6 @@ void GPENCIL_cache_init(void *ved)
pd->do_fast_drawing = false;
pd->obact = draw_ctx->obact;
if (pd->obact && pd->obact->type == OB_GPENCIL_LEGACY && !(pd->draw_depth_only)) {
/* Check if active object has a temp stroke data. */
bGPdata *gpd = (bGPdata *)pd->obact->data;
if (gpd->runtime.sbuffer_used > 0) {
pd->sbuffer_gpd = gpd;
pd->sbuffer_stroke = DRW_cache_gpencil_sbuffer_stroke_data_get(pd->obact);
pd->sbuffer_layer = BKE_gpencil_layer_active_get(pd->sbuffer_gpd);
pd->do_fast_drawing = false; /* TODO: option. */
}
}
}
if (pd->do_fast_drawing) {
@@ -332,273 +322,8 @@ void GPENCIL_cache_init(void *ved)
}
}
#define DRAW_NOW 2
struct gpIterPopulateData {
Object *ob;
GPENCIL_tObject *tgp_ob;
GPENCIL_PrivateData *pd;
GPENCIL_MaterialPool *matpool;
DRWShadingGroup *grp;
/* Last material UBO bound. Used to avoid unneeded buffer binding. */
GPUUniformBuf *ubo_mat;
GPUUniformBuf *ubo_lights;
/* Last texture bound. */
GPUTexture *tex_fill;
GPUTexture *tex_stroke;
/* Offset in the material pool to the first material of this object. */
int mat_ofs;
/* Is the sbuffer call need to be issued. */
int do_sbuffer_call;
/* Indices to do correct insertion of the sbuffer stroke. */
int stroke_index_last;
int stroke_index_offset;
/* Infos for call batching. */
blender::gpu::Batch *geom;
int vfirst, vcount;
};
#define DISABLE_BATCHING 0
static void gpencil_drawcall_flush(gpIterPopulateData *iter)
{
#if !DISABLE_BATCHING
if (iter->geom != nullptr) {
DRW_shgroup_call_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount);
}
#endif
iter->geom = nullptr;
iter->vfirst = -1;
iter->vcount = 0;
}
/* Group draw-calls that are consecutive and with the same type. Reduces GPU driver overhead. */
static void gpencil_drawcall_add(gpIterPopulateData *iter,
blender::gpu::Batch *geom,
int v_first,
int v_count)
{
#if DISABLE_BATCHING
DRW_shgroup_call_range(iter->grp, iter->ob, geom, v_first, v_count);
return;
#endif
int last = iter->vfirst + iter->vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if ((geom != iter->geom) || (v_first - last > 0)) {
gpencil_drawcall_flush(iter);
}
iter->geom = geom;
if (iter->vfirst == -1) {
iter->vfirst = v_first;
}
iter->vcount = v_first + v_count - iter->vfirst;
}
static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
void *thunk);
static void gpencil_sbuffer_cache_populate(gpIterPopulateData *iter)
{
iter->do_sbuffer_call = DRAW_NOW;
/* In order to draw the sbuffer stroke correctly mixed with other strokes,
* we need to offset the stroke index of the sbuffer stroke and the subsequent strokes.
* Remember, sbuffer stroke indices start from 0. So we add last index to avoid
* masking issues. */
iter->grp = DRW_shgroup_create_sub(iter->grp);
DRW_shgroup_uniform_block(iter->grp, "gp_materials", iter->ubo_mat);
DRW_shgroup_uniform_float_copy(iter->grp, "gpStrokeIndexOffset", iter->stroke_index_last);
const DRWContextState *ctx = DRW_context_state_get();
ToolSettings *ts = ctx->scene->toolsettings;
if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) {
/* In this case we can't do correct projection during stroke. We just disable depth test. */
DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx);
}
gpencil_stroke_cache_populate(nullptr, nullptr, iter->pd->sbuffer_stroke, iter);
gpencil_drawcall_flush(iter);
iter->stroke_index_offset = iter->pd->sbuffer_stroke->totpoints + 1;
iter->do_sbuffer_call = 0;
}
static void gpencil_layer_cache_populate(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke * /*gps*/,
void *thunk)
{
gpIterPopulateData *iter = (gpIterPopulateData *)thunk;
GPENCIL_PrivateData *pd = iter->pd;
bGPdata *gpd = (bGPdata *)iter->ob->data;
gpencil_drawcall_flush(iter);
if (iter->do_sbuffer_call) {
gpencil_sbuffer_cache_populate(iter);
}
else {
iter->do_sbuffer_call = !pd->do_fast_drawing && (gpd == pd->sbuffer_gpd) &&
(gpl == pd->sbuffer_layer) &&
(gpf == nullptr || gpf->runtime.onion_id == 0.0f);
}
GPENCIL_tLayer *tgp_layer = gpencil_layer_cache_add(pd, iter->ob, gpl, gpf, iter->tgp_ob);
const bool use_lights = pd->use_lighting && ((gpl->flag & GP_LAYER_USE_LIGHTS) != 0) &&
(iter->ob->dtx & OB_USE_GPENCIL_LIGHTS);
iter->ubo_lights = (use_lights) ? pd->global_light_pool->ubo : pd->shadeless_light_pool->ubo;
gpencil_material_resources_get(iter->matpool, 0, nullptr, nullptr, &iter->ubo_mat);
/* Iterator dependent uniforms. */
DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp;
DRW_shgroup_uniform_block(grp, "gp_lights", iter->ubo_lights);
DRW_shgroup_uniform_block(grp, "gp_materials", iter->ubo_mat);
DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill);
DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke);
DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs);
DRW_shgroup_uniform_float_copy(grp, "gpStrokeIndexOffset", iter->stroke_index_offset);
DRW_shgroup_uniform_vec2_copy(grp, "viewportSize", DRW_viewport_size_get());
}
static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
void *thunk)
{
using namespace blender::draw;
gpIterPopulateData *iter = (gpIterPopulateData *)thunk;
bGPdata *gpd = static_cast<bGPdata *>(iter->ob->data);
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1);
const bool is_render = iter->pd->is_render;
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) ||
(!is_render && ((gps->flag & GP_STROKE_NOFILL) != 0));
bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) &&
(!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0);
bool only_lines = !GPENCIL_PAINT_MODE(gpd) && gpl && gpf && gpl->actframe != gpf &&
iter->pd->use_multiedit_lines_only;
bool is_onion = gpl && gpf && gpf->runtime.onion_id != 0;
bool hide_onion = is_onion && ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0);
if ((hide_material) || (!show_stroke && !show_fill) || (only_lines && !is_onion) || (hide_onion))
{
return;
}
GPUUniformBuf *ubo_mat;
GPUTexture *tex_stroke, *tex_fill;
gpencil_material_resources_get(
iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat);
bool resource_changed = (iter->ubo_mat != ubo_mat) ||
(tex_fill && (iter->tex_fill != tex_fill)) ||
(tex_stroke && (iter->tex_stroke != tex_stroke));
if (resource_changed) {
gpencil_drawcall_flush(iter);
iter->grp = DRW_shgroup_create_sub(iter->grp);
if (iter->ubo_mat != ubo_mat) {
DRW_shgroup_uniform_block(iter->grp, "gp_materials", ubo_mat);
iter->ubo_mat = ubo_mat;
}
if (tex_fill) {
DRW_shgroup_uniform_texture(iter->grp, "gpFillTexture", tex_fill);
iter->tex_fill = tex_fill;
}
if (tex_stroke) {
DRW_shgroup_uniform_texture(iter->grp, "gpStrokeTexture", tex_stroke);
iter->tex_stroke = tex_stroke;
}
}
bool do_sbuffer = (iter->do_sbuffer_call == DRAW_NOW);
blender::gpu::Batch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_get(iter->ob, show_fill) :
DRW_cache_gpencil_get(iter->ob, iter->pd->cfra);
if (geom != iter->geom) {
gpencil_drawcall_flush(iter);
blender::gpu::VertBuf *position_tx =
do_sbuffer ? DRW_cache_gpencil_sbuffer_position_buffer_get(iter->ob, show_fill) :
DRW_cache_gpencil_position_buffer_get(iter->ob, iter->pd->cfra);
blender::gpu::VertBuf *color_tx = do_sbuffer ?
DRW_cache_gpencil_sbuffer_color_buffer_get(iter->ob,
show_fill) :
DRW_cache_gpencil_color_buffer_get(iter->ob,
iter->pd->cfra);
DRW_shgroup_buffer_texture(iter->grp, "gp_pos_tx", position_tx);
DRW_shgroup_buffer_texture(iter->grp, "gp_col_tx", color_tx);
}
if (show_fill) {
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
gpencil_drawcall_add(iter, geom, vfirst, vcount);
}
if (show_stroke) {
int vfirst = gps->runtime.stroke_start * 3;
bool is_cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2);
int vcount = (gps->totpoints + int(is_cyclic)) * 2 * 3;
gpencil_drawcall_add(iter, geom, vfirst, vcount);
}
iter->stroke_index_last = gps->runtime.vertex_start + gps->totpoints + 1;
}
static void gpencil_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter)
{
bGPdata *gpd = (bGPdata *)iter->ob->data;
if (gpd != iter->pd->sbuffer_gpd) {
return;
}
GPENCIL_TextureList *txl = vedata->txl;
GPUTexture *depth_texture = iter->pd->scene_depth_tx;
GPENCIL_tObject *last_tgp_ob = iter->pd->tobjects.last;
/* Create another temp object that only contain the stroke. */
const blender::Bounds<float3> bounds = BKE_gpencil_data_minmax(gpd).value_or(
blender::Bounds(float3(0)));
iter->tgp_ob = gpencil_object_cache_add(
iter->pd, iter->ob, (gpd->draw_mode == GP_DRAWMODE_3D), bounds);
/* Remove from the main list. */
iter->pd->tobjects.last = last_tgp_ob;
last_tgp_ob->next = nullptr;
/* Add to sbuffer tgpobject list. */
BLI_LINKS_APPEND(&iter->pd->sbuffer_tobjects, iter->tgp_ob);
/* Remove depth test with scene (avoid self occlusion). */
iter->pd->scene_depth_tx = txl->dummy_texture;
gpencil_layer_cache_populate(
iter->pd->sbuffer_layer, iter->pd->sbuffer_layer->actframe, nullptr, iter);
const DRWContextState *ctx = DRW_context_state_get();
ToolSettings *ts = ctx->scene->toolsettings;
if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) {
/* In this case we can't do correct projection during stroke. We just disable depth test. */
DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx);
}
iter->do_sbuffer_call = DRAW_NOW;
gpencil_stroke_cache_populate(nullptr, nullptr, iter->pd->sbuffer_stroke, iter);
gpencil_drawcall_flush(iter);
gpencil_vfx_cache_populate(
vedata, iter->ob, iter->tgp_ob, (gpd != nullptr && GPENCIL_ANY_EDIT_MODE(gpd)));
/* Restore state. */
iter->do_sbuffer_call = 0;
iter->pd->scene_depth_tx = depth_texture;
}
/* Check if the passed in layer is used by any other layer as a mask (in the viewlayer). */
static bool is_used_as_layer_mask_in_viewlayer(const GreasePencil &grease_pencil,
const blender::bke::greasepencil::Layer &mask_layer,
@@ -877,59 +602,13 @@ void GPENCIL_cache_populate(void *ved, Object *ob)
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
GPENCIL_PrivateData *pd = vedata->stl->pd;
GPENCIL_TextureList *txl = vedata->txl;
const bool is_final_render = DRW_state_is_image_render();
/* object must be visible */
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
return;
}
if (ob->data && (ob->type == OB_GPENCIL_LEGACY) && (ob->dt >= OB_SOLID)) {
bGPdata *gpd = (bGPdata *)ob->data;
const blender::Bounds<float3> bounds = BKE_gpencil_data_minmax(gpd).value_or(
blender::Bounds(float3(0)));
gpIterPopulateData iter = {nullptr};
iter.ob = ob;
iter.pd = pd;
iter.tgp_ob = gpencil_object_cache_add(pd, ob, (gpd->draw_mode == GP_DRAWMODE_3D), bounds);
iter.matpool = gpencil_material_pool_create(pd, ob, &iter.mat_ofs, GPENCIL_VERTEX_MODE(gpd));
iter.tex_fill = txl->dummy_texture;
iter.tex_stroke = txl->dummy_texture;
/* Special case for rendering onion skin. */
bool do_onion = (!pd->is_render) ? pd->do_onion : (gpd->onion_flag & GP_ONION_GHOST_ALWAYS);
gpd->runtime.playing = short(pd->playing);
/* When render in background the active frame could not be properly set due thread priority,
* better set again. This is not required in viewport. */
if (txl->render_depth_tx) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, pd->cfra, GP_GETFRAME_USE_PREV);
}
}
BKE_gpencil_visible_stroke_advanced_iter(is_final_render ? pd->view_layer : nullptr,
ob,
gpencil_layer_cache_populate,
gpencil_stroke_cache_populate,
&iter,
do_onion,
pd->cfra);
gpencil_drawcall_flush(&iter);
if (iter.do_sbuffer_call) {
gpencil_sbuffer_cache_populate(&iter);
}
gpencil_vfx_cache_populate(
vedata, ob, iter.tgp_ob, (gpd != nullptr && GPENCIL_ANY_EDIT_MODE(gpd)));
if (pd->do_fast_drawing) {
gpencil_sbuffer_cache_populate_fast(vedata, &iter);
}
}
else if (ob->data && (ob->type == OB_GREASE_PENCIL) && (ob->dt >= OB_SOLID)) {
if (ob->data && (ob->type == OB_GREASE_PENCIL) && (ob->dt >= OB_SOLID)) {
GPENCIL_tObject *tgp_ob = grease_pencil_object_cache_populate(pd, txl, ob);
gpencil_vfx_cache_populate(
vedata, ob, tgp_ob, ELEM(ob->mode, OB_MODE_EDIT, OB_MODE_SCULPT, OB_MODE_WEIGHT_PAINT));
@@ -518,9 +518,6 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OVERLAY_metaball_cache_populate(data, ob);
}
break;
case OB_GPENCIL_LEGACY:
OVERLAY_gpencil_legacy_cache_populate(data, ob);
break;
}
}
/* Non-Meshes */
@@ -322,52 +322,6 @@ void OVERLAY_gpencil_legacy_cache_init(OVERLAY_Data *vedata)
}
}
static void OVERLAY_edit_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
using namespace blender::draw;
OVERLAY_PrivateData *pd = vedata->stl->pd;
bGPdata *gpd = (bGPdata *)ob->data;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
/* Overlay is only for active object. */
if (ob != draw_ctx->obact) {
return;
}
if (pd->edit_gpencil_wires_grp) {
DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_wires_grp);
DRW_shgroup_uniform_vec4_copy(grp, "gpEditColor", gpd->line_color);
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_lines_get(ob, pd->cfra);
DRW_shgroup_call_no_cull(pd->edit_gpencil_wires_grp, geom, ob);
}
if (pd->edit_gpencil_points_grp) {
const bool show_direction = (v3d->gp_flag & V3D_GP_SHOW_STROKE_DIRECTION) != 0;
DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_points_grp);
DRW_shgroup_uniform_float_copy(grp, "doStrokeEndpoints", show_direction);
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_points_get(ob, pd->cfra);
DRW_shgroup_call_no_cull(grp, geom, ob);
}
if (pd->edit_gpencil_curve_handle_grp) {
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_curve_handles_get(ob, pd->cfra);
if (geom) {
DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_handle_grp, geom, ob);
}
}
if (pd->edit_gpencil_curve_points_grp) {
blender::gpu::Batch *geom = DRW_cache_gpencil_edit_curve_points_get(ob, pd->cfra);
if (geom) {
DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_points_grp, geom, ob);
}
}
}
static void overlay_gpencil_draw_stroke_color_name(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
@@ -427,30 +381,6 @@ static void OVERLAY_gpencil_color_names(Object *ob)
nullptr, ob, nullptr, overlay_gpencil_draw_stroke_color_name, ob, false, cfra);
}
void OVERLAY_gpencil_legacy_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
bGPdata *gpd = (bGPdata *)ob->data;
if (gpd == nullptr) {
return;
}
if (GPENCIL_ANY_MODE(gpd)) {
OVERLAY_edit_gpencil_cache_populate(vedata, ob);
}
/* don't show object extras in set's */
if ((ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) == 0) {
if ((v3d->gp_flag & V3D_GP_SHOW_MATERIAL_NAME) && (ob->mode == OB_MODE_EDIT_GPENCIL_LEGACY) &&
DRW_state_show_text())
{
OVERLAY_gpencil_color_names(ob);
}
}
}
void OVERLAY_gpencil_legacy_draw(OVERLAY_Data *vedata)
{
OVERLAY_PassList *psl = vedata->psl;
@@ -186,97 +186,6 @@ struct iterData {
float plane[4];
};
static void gpencil_layer_cache_populate(bGPDlayer *gpl,
bGPDframe * /*gpf*/,
bGPDstroke * /*gps*/,
void *thunk)
{
using namespace blender::draw;
iterData *iter = (iterData *)thunk;
bGPdata *gpd = (bGPdata *)iter->ob->data;
const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0;
const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D);
float object_scale = mat4_to_scale(iter->ob->object_to_world().ptr());
/* Negate thickness sign to tag that strokes are in screen space.
* Convert to world units (by default, 1 meter = 2000 pixels). */
float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f);
blender::gpu::VertBuf *position_tx = DRW_cache_gpencil_position_buffer_get(iter->ob, iter->cfra);
blender::gpu::VertBuf *color_tx = DRW_cache_gpencil_color_buffer_get(iter->ob, iter->cfra);
DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp);
DRW_shgroup_uniform_bool_copy(grp, "gpStrokeOrder3d", is_stroke_order_3d);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", object_scale);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", float(gpl->line_change));
DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale);
DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", iter->plane);
DRW_shgroup_buffer_texture(grp, "gp_pos_tx", position_tx);
DRW_shgroup_buffer_texture(grp, "gp_col_tx", color_tx);
}
static void gpencil_stroke_cache_populate(bGPDlayer * /*gpl*/,
bGPDframe * /*gpf*/,
bGPDstroke *gps,
void *thunk)
{
using namespace blender::draw;
iterData *iter = (iterData *)thunk;
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1);
bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
/* TODO: What about simplify Fill? */
bool show_fill = (gps->tot_triangles > 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
if (hide_material) {
return;
}
blender::gpu::Batch *geom = DRW_cache_gpencil_get(iter->ob, iter->cfra);
if (show_fill) {
int vfirst = gps->runtime.fill_start * 3;
int vcount = gps->tot_triangles * 3;
DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount);
}
if (show_stroke) {
int vfirst = gps->runtime.stroke_start * 3;
bool is_cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2);
int vcount = (gps->totpoints + int(is_cyclic)) * 2 * 3;
DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount);
}
}
static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob)
{
/* No outlines in edit mode. */
bGPdata *gpd = (bGPdata *)ob->data;
if (gpd && GPENCIL_ANY_MODE(gpd)) {
return;
}
iterData iter{};
iter.ob = ob;
iter.stroke_grp = pd->outlines_gpencil_grp;
iter.cfra = pd->cfra;
if (gpd->draw_mode == GP_DRAWMODE_2D) {
gpencil_depth_plane(ob, iter.plane);
}
BKE_gpencil_visible_stroke_advanced_iter(nullptr,
ob,
gpencil_layer_cache_populate,
gpencil_stroke_cache_populate,
&iter,
false,
pd->cfra);
}
static void OVERLAY_outline_grease_pencil(OVERLAY_PrivateData *pd, Scene *scene, Object *ob)
{
using namespace blender;
@@ -413,11 +322,6 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
return;
}
if (ob->type == OB_GPENCIL_LEGACY) {
OVERLAY_outline_gpencil(pd, ob);
return;
}
if (ob->type == OB_GREASE_PENCIL) {
OVERLAY_outline_grease_pencil(pd, draw_ctx->scene, ob);
return;
@@ -608,7 +608,6 @@ void OVERLAY_edit_curve_draw(OVERLAY_Data *vedata);
void OVERLAY_edit_gpencil_legacy_cache_init(OVERLAY_Data *vedata);
void OVERLAY_gpencil_legacy_cache_init(OVERLAY_Data *vedata);
void OVERLAY_gpencil_legacy_cache_populate(OVERLAY_Data *vedata, Object *ob);
void OVERLAY_gpencil_legacy_draw(OVERLAY_Data *vedata);
void OVERLAY_edit_gpencil_legacy_draw(OVERLAY_Data *vedata);
-4
View File
@@ -890,8 +890,6 @@ blender::gpu::Batch *DRW_cache_object_face_wireframe_get(const Scene *scene, Obj
return DRW_pointcloud_batch_cache_get_dots(ob);
case OB_VOLUME:
return DRW_cache_volume_face_wireframe_get(ob);
case OB_GPENCIL_LEGACY:
return DRW_cache_gpencil_face_wireframe_get(ob);
case OB_GREASE_PENCIL:
return DRW_cache_grease_pencil_face_wireframe_get(scene, ob);
default:
@@ -961,8 +959,6 @@ int DRW_cache_object_material_count_get(const Object *ob)
return DRW_pointcloud_material_count_get(static_cast<const PointCloud *>(ob->data));
case OB_VOLUME:
return DRW_volume_material_count_get(static_cast<const Volume *>(ob->data));
case OB_GPENCIL_LEGACY:
return DRW_gpencil_material_count_get(static_cast<const bGPdata *>(ob->data));
default:
BLI_assert(0);
return 0;
-17
View File
@@ -258,23 +258,6 @@ DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume,
blender::gpu::Batch *DRW_cache_volume_face_wireframe_get(Object *ob);
blender::gpu::Batch *DRW_cache_volume_selection_surface_get(Object *ob);
/* GPencil (legacy) */
blender::gpu::Batch *DRW_cache_gpencil_get(Object *ob, int cfra);
gpu::VertBuf *DRW_cache_gpencil_position_buffer_get(Object *ob, int cfra);
gpu::VertBuf *DRW_cache_gpencil_color_buffer_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_lines_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_points_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_curve_handles_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_edit_curve_points_get(Object *ob, int cfra);
blender::gpu::Batch *DRW_cache_gpencil_sbuffer_get(Object *ob, bool show_fill);
gpu::VertBuf *DRW_cache_gpencil_sbuffer_position_buffer_get(Object *ob, bool show_fill);
gpu::VertBuf *DRW_cache_gpencil_sbuffer_color_buffer_get(Object *ob, bool show_fill);
int DRW_gpencil_material_count_get(const bGPdata *gpd);
blender::gpu::Batch *DRW_cache_gpencil_face_wireframe_get(Object *ob);
bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob);
/**
* Sbuffer batches are temporary. We need to clear it after drawing.
*/
@@ -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,