Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py5
-rw-r--r--source/blender/editors/object/object_add.c5
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c119
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h43
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c53
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c573
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h18
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_util.c36
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h20
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h3
-rw-r--r--source/blender/makesdna/DNA_material_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c1
14 files changed, 774 insertions, 112 deletions
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index b217e33de12..5df58f8ac4c 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -304,6 +304,11 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
sub.active = lineart.use_mat_occlusion
sub.prop(lineart, "mat_occlusion", slider=True, text="Levels")
+ row = layout.row(align=True, heading="Custom occlusion effectiveness")
+ row.active = lineart.use_occlusion_effectiveness
+ row.prop(lineart, "use_occlusion_effectiveness", text="")
+ row.prop(lineart, "occlusion_effectiveness", slider=True)
+
classes = (
MATERIAL_MT_context_menu,
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 11cf3ea5083..f6b65170d1b 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1439,10 +1439,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
id_us_plus(&md->target_material->id);
}
- if (use_lights) {
- ob->dtx |= OB_USE_GPENCIL_LIGHTS;
- }
- else {
+ if (!use_lights) {
ob->dtx &= ~OB_USE_GPENCIL_LIGHTS;
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index 9593a1364e7..f58de44b04c 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -59,6 +59,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+#include "MOD_gpencil_lineart.h"
#include "MOD_gpencil_modifiertypes.h"
#include "MOD_gpencil_ui_common.h"
@@ -160,12 +161,14 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
LineartCache *local_lc = gpd->runtime.lineart_cache;
if (!gpd->runtime.lineart_cache) {
- MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache);
+ MOD_lineart_compute_feature_lines(
+ depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
else {
if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) {
- MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc);
+ MOD_lineart_compute_feature_lines(
+ depsgraph, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
MOD_lineart_chain_clear_picked_flag(local_lc);
@@ -205,7 +208,8 @@ static void bakeModifier(Main *UNUSED(bmain),
}
if (!gpd->runtime.lineart_cache) {
- MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache);
+ MOD_lineart_compute_feature_lines(
+ depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
@@ -254,6 +258,7 @@ static void updateDepsgraph(GpencilModifierData *md,
else {
add_this_collection(ctx->scene->master_collection, ctx, mode);
}
+
DEG_add_object_relation(
ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
DEG_add_object_relation(
@@ -268,6 +273,8 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP);
walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&lmd->light_contour_object, IDWALK_CB_NOP);
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -404,14 +411,23 @@ static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
- PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+ const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
+ const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
+
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
- const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels");
+ if (!show_in_front) {
+ uiItemL(layout, IFACE_("Object is not in front"), ICON_INFO);
+ }
+
+ layout = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(layout, show_in_front);
uiItemR(layout, ptr, "use_multiple_levels", 0, IFACE_("Range"), ICON_NONE);
@@ -439,10 +455,14 @@ static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *pa
static void material_mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
- PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+ const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
+
uiLayoutSetEnabled(layout, !is_baked);
+ uiLayoutSetActive(layout, show_in_front);
uiLayoutSetPropSep(layout, true);
@@ -463,17 +483,33 @@ static void material_mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(col, ptr, "use_material_mask_match", 0, IFACE_("Match All Masks"), ICON_NONE);
}
+static void intersection_panel_draw_header(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+ const bool use_isec = RNA_boolean_get(ptr, "use_intersection");
+
+ uiLayoutSetEnabled(layout, !is_baked);
+ uiLayoutSetActive(layout, use_isec);
+
+ uiItemR(layout, ptr, "use_intersection_filter", 0, IFACE_("Filter Intersection"), ICON_NONE);
+}
+
static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
+ const bool use_isec = RNA_boolean_get(ptr, "use_intersection");
+ const bool use_isec_filter = RNA_boolean_get(ptr, "use_intersection_filter");
uiLayoutSetEnabled(layout, !is_baked);
-
uiLayoutSetPropSep(layout, true);
- uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_intersection"));
+ uiLayoutSetActive(layout, use_isec && use_isec_filter);
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetPropDecorate(row, false);
@@ -489,6 +525,7 @@ static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayout *col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "use_intersection_match", 0, IFACE_("Match All Masks"), ICON_NONE);
}
+
static void face_mark_panel_draw_header(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -496,16 +533,13 @@ static void face_mark_panel_draw_header(const bContext *UNUSED(C), Panel *panel)
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
- const bool use_cache = RNA_boolean_get(ptr, "use_cache");
- const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data);
+ const bool use_cache = RNA_boolean_get(ptr, "use_cached_result");
- if (!use_cache || is_first) {
- uiLayoutSetEnabled(layout, !is_baked);
- uiItemR(layout, ptr, "use_face_mark", 0, IFACE_("Face Mark Filtering"), ICON_NONE);
- }
- else {
- uiItemL(layout, IFACE_("Face Mark Filtering"), ICON_NONE);
- }
+ uiLayoutSetEnabled(
+ layout,
+ !is_baked && (!use_cache || BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data)));
+
+ uiItemR(layout, ptr, "use_face_mark", 0, IFACE_("Filter Face Mark"), ICON_NONE);
}
static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -516,15 +550,11 @@ static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel)
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
const bool use_mark = RNA_boolean_get(ptr, "use_face_mark");
- const bool use_cache = RNA_boolean_get(ptr, "use_cache");
- const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data);
+ const bool use_cache = RNA_boolean_get(ptr, "use_cached_result");
- uiLayoutSetEnabled(layout, !is_baked);
-
- if (use_cache && !is_first) {
- uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO);
- return;
- }
+ uiLayoutSetEnabled(
+ layout,
+ !is_baked && (!use_cache || BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data)));
uiLayoutSetPropSep(layout, true);
@@ -542,7 +572,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayout *layout = panel->layout;
const bool is_baked = RNA_boolean_get(ptr, "is_baked");
- const bool use_cache = RNA_boolean_get(ptr, "use_cache");
+ const bool use_cache = RNA_boolean_get(ptr, "use_cached_result");
const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data);
const bool is_geom = RNA_boolean_get(ptr, "use_geometry_space_chain");
@@ -568,6 +598,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
is_geom ? IFACE_("Geometry Threshold") : NULL,
ICON_NONE);
+ uiItemR(layout, ptr, "smooth_tolerance", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
@@ -583,7 +614,9 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel)
const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data);
uiLayoutSetPropSep(layout, true);
- uiLayoutSetEnabled(layout, !is_baked);
+ uiLayoutSetEnabled(
+ layout,
+ !is_baked && (!use_cache || BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data)));
if (use_cache && !is_first) {
uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO);
@@ -634,6 +667,28 @@ static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all");
}
+static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ uiLayout *layout = panel->layout;
+
+ const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front");
+
+ uiLayoutSetPropSep(layout, true);
+
+ if (show_in_front) {
+ uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR);
+ }
+
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, !show_in_front);
+
+ uiItemR(col, ptr, "stroke_offset", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE);
+}
+
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
@@ -653,6 +708,14 @@ static void panelRegister(ARegionType *region_type)
material_mask_panel_draw_header,
material_mask_panel_draw,
occlusion_panel);
+ gpencil_modifier_subpanel_register(region_type,
+ "intersection",
+ "",
+ intersection_panel_draw_header,
+ intersection_panel_draw,
+ panel_type);
+ gpencil_modifier_subpanel_register(
+ region_type, "face_mark", "", face_mark_panel_draw_header, face_mark_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
region_type, "intersection", "Intersection", NULL, intersection_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
@@ -662,6 +725,8 @@ static void panelRegister(ARegionType *region_type)
gpencil_modifier_subpanel_register(
region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
+ region_type, "composition", "Composition", NULL, composition_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
region_type, "baking", "Baking", NULL, baking_panel_draw, panel_type);
}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 247b0b3f57b..d77e0900535 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -216,6 +216,14 @@ enum eLineArtTileRecursiveLimit {
#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100
#define LRT_TILE_EDGE_COUNT_INITIAL 32
+struct BVHTree;
+
+typedef struct LineartTriangleBufferIndexer {
+ int start_index; /* The first one. */
+ int end_index; /* Not include the last one. */
+ LineartElementLinkNode *eln;
+} LineartTriangleBufferIndexer;
+
typedef struct LineartRenderBuffer {
struct LineartRenderBuffer *prev, *next;
@@ -228,17 +236,26 @@ typedef struct LineartRenderBuffer {
double view_projection[4][4];
double view[4][4];
+ float overscan;
+
struct LineartBoundingArea *initial_bounding_areas;
unsigned int bounding_area_count;
- /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there
- * will be a lot of triangles aligned in line which can not be separated by continue subdividing
- * the tile. So we set a strict limit when using ortho camera. See eLineArtTileRecursiveLimit. */
+ struct BVHTree *bvh_main;
+ int bvh_face_count;
+ struct LineartTriangleBufferIndexer *bvh_triangle_indexer;
+ int bvh_inderxer_count;
+
+ /* When splitting bounding areas, if there's an ortho camera placed at a straight angle,
+ * there will be a lot of triangles aligned in line which can not be separated by continue
+ * subdividing the tile. So we set a strict limit when using ortho camera. See
+ * eLineArtTileRecursiveLimit. */
int tile_recursive_level;
ListBase vertex_buffer_pointers;
ListBase line_buffer_pointers;
ListBase triangle_buffer_pointers;
+ LineartTriangle **triangle_lookup;
/** This one's memory is not from main pool and is free()ed after culling stage. */
ListBase triangle_adjacent_pointers;
@@ -288,6 +305,7 @@ typedef struct LineartRenderBuffer {
bool use_edge_marks;
bool use_intersections;
bool use_loose;
+ bool use_light_contour;
bool fuzzy_intersections;
bool fuzzy_everything;
bool allow_boundaries;
@@ -306,11 +324,17 @@ typedef struct LineartRenderBuffer {
bool cam_is_persp;
float cam_obmat[4][4];
double camera_pos[3];
+ double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */
double near_clip, far_clip;
float shift_x, shift_y;
float crease_threshold;
float chaining_image_threshold;
float angle_splitting_threshold;
+ float chain_smooth_tolerance;
+
+ /* Could be direction or position, depends on light_is_sun. */
+ double light_vector[3];
+ bool light_is_sun;
/* FIXME(Yiming): Temporary solution for speeding up calculation by not including lines that
* are not in the selected source. This will not be needed after we have a proper scene-wise
@@ -320,6 +344,8 @@ typedef struct LineartRenderBuffer {
struct Collection *_source_collection;
struct Object *_source_object;
+ int debug_triangle_isec_test_count;
+
} LineartRenderBuffer;
typedef struct LineartCache {
@@ -368,6 +394,7 @@ typedef struct LineartRenderTaskInfo {
ListBase material;
ListBase edge_mark;
ListBase floating;
+ ListBase light_contour;
} LineartRenderTaskInfo;
@@ -393,6 +420,7 @@ typedef struct LineartObjectInfo {
ListBase material;
ListBase edge_mark;
ListBase floating;
+ ListBase light_contour;
} LineartObjectInfo;
@@ -402,7 +430,7 @@ typedef struct LineartObjectLoadTaskInfo {
/* LinkNode styled list */
LineartObjectInfo *pending;
/* Used to spread the load across several threads. This can not overflow. */
- long unsigned int total_faces;
+ uint64_t total_faces;
} LineartObjectLoadTaskInfo;
/**
@@ -586,13 +614,18 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb);
void MOD_lineart_chain_connect(LineartRenderBuffer *rb);
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
+void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance);
+void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb,
+ float dist,
+ bool use_custom_camera);
int MOD_lineart_chain_count(const LineartEdgeChain *ec);
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc);
bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph,
struct LineartGpencilModifierData *lmd,
- LineartCache **cached_result);
+ struct LineartCache **cached_result,
+ bool enable_stroke_offset);
struct Scene;
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 52485648ee0..1471210d18b 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -520,7 +520,7 @@ static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb,
{
if (root->child == NULL) {
LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized(
- &root->linked_chains, &rb->render_data_pool, ec, sizeof(LineartChainRegisterEntry));
+ &root->linked_chains, rb->chain_data_pool, ec, sizeof(LineartChainRegisterEntry));
cre->eci = eci;
@@ -924,6 +924,34 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
}
}
+void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance)
+{
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
+ LineartEdgeChainItem *next_eci;
+ for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) {
+ next_eci = eci->next;
+ LineartEdgeChainItem *eci2, *eci3, *eci4;
+
+ /* Not enough point to do simplify. */
+ if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) {
+ continue;
+ }
+
+ /* No need to care for different line types/occlusion and so on, because at this stage they
+ * are all the same within a chain. */
+
+ /* If p3 is within the p1-p2 segment of a width of "tolerance" */
+ if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) {
+ /* And if p4 is on the extension of p1-p2 , we remove p3. */
+ if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) {
+ BLI_remlink(&ec->chain, eci3);
+ next_eci = eci;
+ }
+ }
+ }
+ }
+}
+
/**
* This should always be the last stage!, see the end of
* #MOD_lineart_chain_split_for_fixed_occlusion().
@@ -980,3 +1008,26 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol
}
}
}
+
+void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb,
+ float dist,
+ bool use_custom_camera)
+{
+ float dir[3];
+ float cam[3];
+
+ if (use_custom_camera) {
+ copy_v3fl_v3db(cam, rb->camera_pos);
+ }
+ else {
+ copy_v3fl_v3db(cam, rb->active_camera_pos);
+ }
+
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ sub_v3_v3v3(dir, cam, eci->gpos);
+ normalize_v3_length(dir, dist);
+ add_v3_v3(eci->gpos, dir);
+ }
+ }
+}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index c05749061a9..9ef3cb895ac 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -24,6 +24,7 @@
#include "MOD_gpencil_lineart.h"
#include "MOD_lineart.h"
+#include "BLI_kdopbvh.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -50,6 +51,7 @@
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_light_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -63,6 +65,10 @@
#include "lineart_intern.h"
+//#define LINEART_WITH_BVH
+#define LINEART_WITH_BVH_THREAD
+#define LINEART_BVH_OWN_ISEC
+
static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb,
LineartEdge *e);
@@ -340,7 +346,7 @@ static void lineart_bounding_area_triangle_add(LineartRenderBuffer *rb,
static void lineart_bounding_area_line_add(LineartRenderBuffer *rb,
LineartBoundingArea *ba,
- LineartEdge *e)
+ LineartEdge *edge)
{
if (ba->line_count >= ba->max_line_count) {
LineartEdge **new_array = lineart_mem_acquire(&rb->render_data_pool,
@@ -349,7 +355,7 @@ static void lineart_bounding_area_line_add(LineartRenderBuffer *rb,
ba->max_line_count *= 2;
ba->linked_lines = new_array;
}
- ba->linked_lines[ba->line_count] = e;
+ ba->linked_lines[ba->line_count] = edge;
ba->line_count++;
}
@@ -787,7 +793,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
int e_count = *r_e_count;
int t_count = *r_t_count;
int v1_obi, v2_obi;
- char new_flag = 0;
+ uint16_t new_flag = 0;
LineartEdge *new_e, *e, *old_e;
LineartEdgeSegment *es;
@@ -1319,6 +1325,8 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
/* Then go through all the other triangles. */
LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ rb->bvh_face_count += eln->element_count;
+ rb->bvh_inderxer_count++;
if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) {
continue;
}
@@ -1621,31 +1629,32 @@ static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartOb
}
static void lineart_triangle_adjacent_assign(LineartTriangle *tri,
- LineartTriangleAdjacent *ta,
+ LineartTriangleAdjacent *rta,
LineartEdge *e)
{
if (lineart_edge_match(tri, e, 0, 1)) {
- ta->e[0] = e;
+ rta->e[0] = e;
}
else if (lineart_edge_match(tri, e, 1, 2)) {
- ta->e[1] = e;
+ rta->e[1] = e;
}
else if (lineart_edge_match(tri, e, 2, 0)) {
- ta->e[2] = e;
+ rta->e[2] = e;
}
}
-static int lineart_edge_type_duplication_count(char eflag)
+static int lineart_edge_type_duplication_count(uint8_t eflag)
{
int count = 0;
/* See eLineartEdgeFlag for details. */
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) {
if (eflag & (1 << i)) {
count++;
}
}
return count;
}
+
static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb)
{
BMesh *bm;
@@ -1781,8 +1790,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
orv[i].index = i;
}
/* Register a global index increment. See #lineart_triangle_share_edge() and
- * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually
- * overflow, in such large scene it's virtually impossible for two vertex of the same numeric
+ * #lineart_main_load_geometries() for details. It's okay that global_vindex might eventually
+ * overflow, in such large scene it's virtually impossible for two vertices of the same numeric
* index to come close together. */
obi->global_i_offset = bm->totvert;
@@ -1851,7 +1860,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
/* Here we just use bm's flag for when loading actual lines, then we don't need to call
* lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always
* set the flag, so hflag stays 0 for lines that are not feature lines. */
- e->head.hflag = eflag;
+ e->head.hflag = 0;
+ e->head.hflag |= eflag;
}
o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
@@ -2003,7 +2013,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_
int this_face_count)
{
LineartObjectLoadTaskInfo *use_olti = olti_list;
- long unsigned int min_face = use_olti->total_faces;
+ uint64_t min_face = use_olti->total_faces;
for (int i = 0; i < thread_count; i++) {
if (olti_list[i].total_faces < min_face) {
min_face = olti_list[i].total_faces;
@@ -2064,11 +2074,6 @@ static void lineart_main_load_geometries(
int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h);
double asp = ((double)rb->w / (double)rb->h);
- double t_start;
-
- if (G.debug_value == 4000) {
- t_start = PIL_check_seconds_timer();
- }
int bound_box_discard_count = 0;
if (cam->type == CAM_PERSP) {
@@ -2078,7 +2083,7 @@ static void lineart_main_load_geometries(
if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
sensor /= asp;
}
- double fov = focallength_to_fov(cam->lens, sensor);
+ double fov = focallength_to_fov(cam->lens / (1 + rb->overscan), sensor);
lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
}
else if (cam->type == CAM_ORTHO) {
@@ -2086,6 +2091,12 @@ static void lineart_main_load_geometries(
lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
}
+ double t_start;
+
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
invert_m4_m4(inv, rb->cam_obmat);
mul_m4db_m4db_m4fl_uniq(result, proj, inv);
copy_m4_m4_db(proj, result);
@@ -2107,7 +2118,8 @@ static void lineart_main_load_geometries(
int thread_count = rb->thread_count;
- /* This memory is in render buffer memory pool. so we don't need to free those after loading. */
+ /* This memory is in render buffer memory pool. So we don't need to free those after loading.
+ */
LineartObjectLoadTaskInfo *olti = lineart_mem_acquire(
&rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count);
@@ -2196,6 +2208,23 @@ static void lineart_main_load_geometries(
}
}
+ int tri_count = 0;
+ int global_tri_i = 0;
+ LineartTriangle *tri;
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ tri_count += eln->element_count;
+ }
+
+ rb->triangle_lookup = lineart_mem_acquire(&rb->render_data_pool, sizeof(void *) * tri_count);
+
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ for (int i = 0; i < eln->element_count; i++) {
+ tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i);
+ rb->triangle_lookup[global_tri_i] = tri;
+ global_tri_i++;
+ }
+ }
+
if (G.debug_value == 4000) {
double t_elapsed = PIL_check_seconds_timer() - t_start;
printf("Line art loading time: %lf\n", t_elapsed);
@@ -2898,6 +2927,8 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb,
continue;
}
+ rb->debug_triangle_isec_test_count++;
+
/* If we do need to compute intersection, then finally do it. */
lineart_triangle_intersect(rb, tri, testing_triangle);
}
@@ -2968,7 +2999,7 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd)
}
}
-static LineartCache *lineart_init_cache()
+static LineartCache *lineart_init_cache(void)
{
LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache");
return lc;
@@ -2986,18 +3017,20 @@ void MOD_lineart_clear_cache(struct LineartCache **lc)
static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
LineartGpencilModifierData *lmd,
+ Object *camera,
+ Object *active_camera,
LineartCache *lc)
{
LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer");
lmd->cache = lc;
lmd->render_buffer_ptr = rb;
- lc->rb_edge_types = lmd->edge_types_override;
+ lc->rb_edge_types = LRT_EDGE_FLAG_ALL_TYPE;
- if (!scene || !scene->camera || !lc) {
+ if (!scene || !camera || !lc) {
return NULL;
}
- Camera *c = scene->camera->data;
+ Camera *c = camera->data;
double clipping_offset = 0;
if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) {
@@ -3005,8 +3038,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
clipping_offset = 0.0001;
}
- copy_v3db_v3fl(rb->camera_pos, scene->camera->obmat[3]);
- copy_m4_m4(rb->cam_obmat, scene->camera->obmat);
+ copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]);
+ if (active_camera) {
+ copy_v3db_v3fl(rb->active_camera_pos, active_camera->obmat[3]);
+ }
+ copy_m4_m4(rb->cam_obmat, camera->obmat);
rb->cam_is_persp = (c->type == CAM_PERSP);
rb->near_clip = c->clip_start + clipping_offset;
rb->far_clip = c->clip_end - clipping_offset;
@@ -3025,9 +3061,27 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp;
rb->shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp;
+ rb->overscan = lmd->overscan;
+
+ rb->shift_x /= (1 + rb->overscan);
+ rb->shift_y /= (1 + rb->overscan);
+
+ if (lmd->light_contour_object) {
+ Object *lo = lmd->light_contour_object;
+ if (lo->type == OB_LAMP && ((Light *)lo->data)->type == LA_SUN) {
+ rb->light_is_sun = true;
+ float vec[3] = {0.0f, 0.0f, 1.0f};
+ mul_mat3_m4_v3(lo->obmat, vec);
+ copy_v3db_v3fl(rb->light_vector, vec);
+ }
+ else {
+ copy_v3db_v3fl(rb->light_vector, lmd->light_contour_object->obmat[3]);
+ }
+ }
+
rb->crease_threshold = cos(M_PI - lmd->crease_threshold);
- rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
rb->chaining_image_threshold = lmd->chaining_image_threshold;
+ rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0;
rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0;
@@ -3450,6 +3504,16 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
return false;
}
+static void lineart_triangle_bbox(LineartTriangle *tri, float *result)
+{
+ result[0] = MIN3(tri->v[0]->gloc[0], tri->v[1]->gloc[0], tri->v[2]->gloc[0]);
+ result[1] = MIN3(tri->v[0]->gloc[1], tri->v[1]->gloc[1], tri->v[2]->gloc[1]);
+ result[2] = MIN3(tri->v[0]->gloc[2], tri->v[1]->gloc[2], tri->v[2]->gloc[2]);
+ result[3] = MAX3(tri->v[0]->gloc[0], tri->v[1]->gloc[0], tri->v[2]->gloc[0]);
+ result[4] = MAX3(tri->v[0]->gloc[1], tri->v[1]->gloc[1], tri->v[2]->gloc[1]);
+ result[5] = MAX3(tri->v[0]->gloc[2], tri->v[1]->gloc[2], tri->v[2]->gloc[2]);
+}
+
/**
* 1) Link triangles with bounding areas for later occlusion test.
* 2) Test triangles with existing(added previously) triangles for intersection lines.
@@ -3475,9 +3539,11 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
recursive_level < rb->tile_recursive_level) {
lineart_bounding_area_split(rb, root_ba, recursive_level);
}
+#ifndef LINEART_WITH_BVH
if (recursive && do_intersection && rb->use_intersections) {
lineart_triangle_intersect_in_bounding_area(rb, tri, root_ba);
}
+#endif
}
else {
LineartBoundingArea *ba = root_ba->child;
@@ -3744,15 +3810,41 @@ static void lineart_main_add_triangles(LineartRenderBuffer *rb)
int i, lim;
int x1, x2, y1, y2;
int r, co;
+ int i_indexer = 0, i_triangle = 0;
+
+#ifdef LINEART_WITH_BVH
+ if (rb->use_intersections) {
+ rb->bvh_main = BLI_bvhtree_new(rb->bvh_face_count, 0, 8, 6);
+ rb->bvh_triangle_indexer = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartTriangleBufferIndexer) * rb->bvh_inderxer_count);
+ }
+#endif
LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
tri = eln->pointer;
lim = eln->element_count;
+#ifdef LINEART_WITH_BVH
+ if (rb->use_intersections) {
+ rb->bvh_triangle_indexer[i_indexer].eln = eln;
+ rb->bvh_triangle_indexer[i_indexer].start_index = i_triangle;
+ rb->bvh_triangle_indexer[i_indexer].end_index = i_triangle + lim;
+ }
+ i_indexer++;
+#endif
for (i = 0; i < lim; i++) {
if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) {
tri = (void *)(((uchar *)tri) + rb->triangle_size);
continue;
}
+
+#ifdef LINEART_WITH_BVH
+ float bounding[6];
+ lineart_triangle_bbox(tri, bounding);
+ if (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)) {
+ BLI_bvhtree_insert(rb->bvh_main, i_triangle + i, bounding, 2);
+ }
+#endif
+
if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) {
for (co = x1; co <= x2; co++) {
for (r = y1; r <= y2; r++) {
@@ -3768,7 +3860,374 @@ static void lineart_main_add_triangles(LineartRenderBuffer *rb)
} /* Else throw away. */
tri = (void *)(((uchar *)tri) + rb->triangle_size);
}
+ i_triangle += lim;
+ }
+}
+
+static LineartTriangle *lineart_get_triangle_from_index(LineartRenderBuffer *rb, int index)
+{
+ return rb->triangle_lookup[index];
+ int left = 0;
+ int right = rb->bvh_inderxer_count - 1;
+ while (left <= right) {
+ int mid = (left + right) / 2;
+ LineartTriangleBufferIndexer *tbi = &rb->bvh_triangle_indexer[mid];
+ if (tbi->end_index <= index) {
+ left = mid + 1;
+ }
+ else if (tbi->start_index > index) {
+ right = mid - 1;
+ }
+ else {
+ return (LineartTriangle *)(((uint8_t *)tbi->eln->pointer) +
+ (rb->triangle_size * (index - tbi->start_index)));
+ }
+ }
+ return NULL;
+}
+
+static bool lineart_triangle_2v_intersection_math(LineartVert *v1,
+ LineartVert *v2,
+ LineartTriangle *tri,
+ LineartTriangle *t2,
+ float *last,
+ float *rv)
+{
+ double Lv[3];
+ double Rv[3];
+ double dot_l, dot_r;
+ LineartVert *result;
+ double gloc[3];
+ LineartVert *l = v1, *r = v2;
+
+ sub_v3_v3v3_db(Lv, l->gloc, t2->v[0]->gloc);
+ sub_v3_v3v3_db(Rv, r->gloc, t2->v[0]->gloc);
+
+ dot_l = dot_v3v3_db(Lv, t2->gn);
+ dot_r = dot_v3v3_db(Rv, t2->gn);
+
+ if (dot_l * dot_r > 0 || (!dot_l && !dot_r)) {
+ return false;
+ }
+
+ dot_l = fabs(dot_l);
+ dot_r = fabs(dot_r);
+
+ interp_v3_v3v3_db(gloc, l->gloc, r->gloc, dot_l / (dot_l + dot_r));
+
+ /* Due to precision issue, we might end up with the same point as the one we already detected.
+ */
+ if (last && LRT_DOUBLE_CLOSE_ENOUGH(last[0], gloc[0]) &&
+ LRT_DOUBLE_CLOSE_ENOUGH(last[1], gloc[1]) && LRT_DOUBLE_CLOSE_ENOUGH(last[2], gloc[2])) {
+ return false;
+ }
+
+ if (!(lineart_point_inside_triangle3d(gloc, t2->v[0]->gloc, t2->v[1]->gloc, t2->v[2]->gloc))) {
+ return false;
+ }
+
+ copy_v3fl_v3db(rv, gloc);
+
+ return true;
+}
+
+static bool lineart_triangle_intersect_math(LineartTriangle *tri,
+ LineartTriangle *t2,
+ float *v1,
+ float *v2)
+{
+ float *next = v1, *last = NULL;
+ LineartVert *sv1, *sv2;
+ double cl[3];
+
+ LineartVert *share = lineart_triangle_share_point(t2, tri);
+
+ if (share) {
+ /* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc`
+ * against `acd` or `cd` against `abc`. */
+
+ LineartVert *new_share;
+ lineart_triangle_get_other_verts(tri, share, &sv1, &sv2);
+
+ copy_v3fl_v3db(v1, share->gloc);
+
+ if (!lineart_triangle_2v_intersection_math(sv1, sv2, tri, t2, 0, v2)) {
+ lineart_triangle_get_other_verts(t2, share, &sv1, &sv2);
+ if (lineart_triangle_2v_intersection_math(sv1, sv2, t2, tri, 0, v2)) {
+ return true;
+ }
+ }
+ }
+ else {
+ /* If not sharing any points, then we need to try all the possibilities. */
+
+ if (lineart_triangle_2v_intersection_math(tri->v[0], tri->v[1], tri, t2, 0, v1)) {
+ next = v2;
+ last = v1;
+ }
+
+ if (lineart_triangle_2v_intersection_math(tri->v[1], tri->v[2], tri, t2, last, next)) {
+ if (last) {
+ return true;
+ }
+ next = v2;
+ last = v1;
+ }
+ if (lineart_triangle_2v_intersection_math(tri->v[2], tri->v[0], tri, t2, last, next)) {
+ if (last) {
+ return true;
+ }
+ next = v2;
+ last = v1;
+ }
+
+ if (lineart_triangle_2v_intersection_math(t2->v[0], t2->v[1], t2, tri, last, next)) {
+ if (last) {
+ return true;
+ }
+ next = v2;
+ last = v1;
+ }
+ if (lineart_triangle_2v_intersection_math(t2->v[1], t2->v[2], t2, tri, last, next)) {
+ if (last) {
+ return true;
+ }
+ next = v2;
+ last = v1;
+ }
+ if (lineart_triangle_2v_intersection_math(t2->v[2], t2->v[0], t2, tri, last, next)) {
+ if (last) {
+ return true;
+ }
+ next = v2;
+ last = v1;
+ }
}
+ return false;
+}
+
+typedef struct LineartIsecSingle {
+ float v1[3], v2[3];
+ LineartTriangle *tri1, *tri2;
+} LineartIsecSingle;
+typedef struct LineartIsecThread {
+ LineartIsecSingle *array;
+ int current;
+ int max;
+ int count_test;
+} LineartIsecThread;
+typedef struct LineartIsecData {
+ LineartRenderBuffer *rb;
+ LineartIsecThread *threads;
+ int thread_count;
+} LineartIsecData;
+
+static void lineart_add_isec_thread(LineartIsecData *data,
+ const int thread,
+ const float *v1,
+ const float *v2,
+ LineartTriangle *tri1,
+ LineartTriangle *tri2)
+{
+ LineartIsecThread *th = &data->threads[thread];
+ if (th->current == th->max) {
+
+ LineartIsecSingle *new_array = MEM_mallocN(sizeof(LineartIsecSingle) * th->max * 2,
+ "LineartIsecSingle");
+ memcpy(new_array, th->array, sizeof(LineartIsecSingle) * th->max);
+ th->max *= 2;
+ MEM_freeN(th->array);
+ th->array = new_array;
+ }
+ LineartIsecSingle *is = &th->array[th->current];
+ copy_v3_v3(is->v1, v1);
+ copy_v3_v3(is->v2, v2);
+ is->tri1 = tri1;
+ is->tri2 = tri2;
+ th->current++;
+}
+
+static void lineart_init_isec_thread(LineartIsecData *d, LineartRenderBuffer *rb, int thread_count)
+{
+ d->threads = MEM_callocN(sizeof(LineartIsecThread) * thread_count, "LineartIsecThread arr");
+ d->rb = rb;
+ d->thread_count = thread_count;
+ for (int i = 0; i < thread_count; i++) {
+ LineartIsecThread *it = &d->threads[i];
+ it->array = MEM_mallocN(sizeof(LineartIsecSingle) * 100, "LineartIsecSingle arr");
+ it->max = 100;
+ it->current = 0;
+ }
+}
+
+static void lineart_destroy_isec_thread(LineartIsecData *d)
+{
+ for (int i = 0; i < d->thread_count; i++) {
+ LineartIsecThread *it = &d->threads[i];
+ MEM_freeN(it->array);
+ }
+ MEM_freeN(d->threads);
+}
+
+static bool lineart_bvh_isec_callback(void *userdata, int index_a, int index_b, int thread)
+{
+ LineartIsecData *d = (LineartIsecData *)userdata;
+ LineartRenderBuffer *rb = d->rb;
+
+ LineartTriangle *tri1 = lineart_get_triangle_from_index(rb, index_a);
+ LineartTriangle *tri2 = lineart_get_triangle_from_index(rb, index_b);
+
+ if (!tri1 || !tri2) {
+ return false;
+ }
+
+ if ((tri2->flags & LRT_TRIANGLE_INTERSECTION_ONLY) &&
+ (tri1->flags & LRT_TRIANGLE_INTERSECTION_ONLY)) {
+ return false;
+ }
+ /* Bounding box not overlapping or triangles share edges, not potential of intersecting. */
+ if (lineart_triangle_share_edge(tri1, tri2)) {
+ return false;
+ }
+
+ d->threads[thread].count_test++;
+
+#ifdef LINEART_BVH_OWN_ISEC
+ float e1[3], e2[3];
+
+ if (lineart_triangle_intersect_math(tri1, tri2, e1, e2)) {
+ lineart_add_isec_thread(d, thread, e1, e2, tri1, tri2);
+ }
+
+#else
+
+ float t_a0[3], t_a1[3], t_a2[3], t_b0[3], t_b1[3], t_b2[3], e1[3], e2[3];
+ copy_v3fl_v3db(t_a0, tri1->v[0]->gloc);
+ copy_v3fl_v3db(t_a1, tri1->v[1]->gloc);
+ copy_v3fl_v3db(t_a2, tri1->v[2]->gloc);
+ copy_v3fl_v3db(t_b0, tri2->v[0]->gloc);
+ copy_v3fl_v3db(t_b1, tri2->v[1]->gloc);
+ copy_v3fl_v3db(t_b2, tri2->v[2]->gloc);
+
+ if (isect_tri_tri_v3(t_a0, t_a1, t_a2, t_b0, t_b1, t_b2, e1, e2)) {
+ lineart_add_isec_thread(d, thread, e1, e2, tri1, tri2);
+ };
+#endif
+
+ return true;
+}
+
+static void lineart_create_edges_from_isec_data(LineartIsecData *d)
+{
+ LineartRenderBuffer *rb = d->rb;
+ double ZMax = rb->far_clip;
+ double ZMin = rb->near_clip;
+
+ for (int i = 0; i < d->thread_count; i++) {
+ LineartIsecThread *th = &d->threads[i];
+ if (!th->current) {
+ continue;
+ }
+ /* We don't care about removing duplicated vert in this method, chaning can handle that, and it
+ * saves us from using locks and look up tables. */
+ LineartVert *v = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartVert) * th->current * 2);
+ LineartEdge *e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * th->current);
+ LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdgeSegment) * th->current);
+ for (int j = 0; j < th->current; j++) {
+ LineartVert *v1 = v;
+ LineartVert *v2 = v + 1;
+ LineartIsecSingle *is = &th->array[j];
+ copy_v3db_v3fl(v1->gloc, is->v1);
+ copy_v3db_v3fl(v2->gloc, is->v2);
+ /* The intersection line has been generated only in geometry space, so we need to transform
+ * them as well. */
+ mul_v4_m4v3_db(v1->fbcoord, rb->view_projection, v1->gloc);
+ mul_v4_m4v3_db(v2->fbcoord, rb->view_projection, v2->gloc);
+ mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3]));
+ mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3]));
+
+ v1->fbcoord[0] -= rb->shift_x * 2;
+ v1->fbcoord[1] -= rb->shift_y * 2;
+ v2->fbcoord[0] -= rb->shift_x * 2;
+ v2->fbcoord[1] -= rb->shift_y * 2;
+
+ /* This z transformation is not the same as the rest of the part, because the data don't go
+ * through normal perspective division calls in the pipeline, but this way the 3D result and
+ * occlusion on the generated line is correct, and we don't really use 2D for viewport stroke
+ * generation anyway. */
+ v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin));
+ v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin));
+ e->v1 = v1;
+ e->v2 = v2;
+ e->t1 = is->tri1;
+ e->t2 = is->tri2;
+ e->flags = LRT_EDGE_FLAG_INTERSECTION;
+ e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask);
+ BLI_addtail(&e->segments, es);
+
+ lineart_prepend_edge_direct(&rb->intersection.first, e);
+
+ v += 2;
+ e++;
+ es++;
+ }
+ }
+}
+
+static void lineart_do_intersections(LineartRenderBuffer *rb)
+{
+ BLI_bvhtree_balance(rb->bvh_main);
+ unsigned int overlap_tot;
+
+#ifdef LINEART_WITH_BVH_THREAD
+ LineartIsecData d;
+ lineart_init_isec_thread(&d, rb, rb->thread_count);
+
+ BVHTreeOverlap *overlap = BLI_bvhtree_overlap(
+ rb->bvh_main, rb->bvh_main, &overlap_tot, lineart_bvh_isec_callback, &d);
+
+ if (G.debug_value == 4000) {
+ int total_test_count = 0;
+ for (int i = 0; i < rb->thread_count; i++) {
+ total_test_count += d.threads[i].count_test;
+ }
+ printf("BVH has done %d pairs.\n", total_test_count);
+ }
+
+ lineart_create_edges_from_isec_data(&d);
+ lineart_destroy_isec_thread(&d);
+#else
+ BVHTreeOverlap *overlap = BLI_bvhtree_overlap(
+ rb->bvh_main, rb->bvh_main, &overlap_tot, NULL, NULL);
+
+ for (unsigned int i = 0; i < overlap_tot; i++) {
+ BVHTreeOverlap *ov = &overlap[i];
+ LineartTriangle *tri1 = lineart_get_triangle_from_index(rb, ov->indexA);
+ LineartTriangle *tri2 = lineart_get_triangle_from_index(rb, ov->indexB);
+
+ if (!tri1 || !tri2) {
+ continue;
+ }
+
+ if ((tri2->flags & LRT_TRIANGLE_INTERSECTION_ONLY) &&
+ (tri1->flags & LRT_TRIANGLE_INTERSECTION_ONLY)) {
+ continue;
+ }
+ /* Bounding box not overlapping or triangles share edges, not potential of intersecting. */
+ if (lineart_triangle_share_edge(tri1, tri2)) {
+ continue;
+ }
+
+ /* If we do need to compute intersection, then finally do it. */
+ lineart_triangle_intersect(rb, tri1, tri2);
+ }
+
+#endif
+
+ MEM_freeN(overlap);
+ BLI_bvhtree_free(rb->bvh_main);
}
/**
@@ -4037,11 +4496,13 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this
*/
bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
LineartGpencilModifierData *lmd,
- LineartCache **cached_result)
+ LineartCache **cached_result,
+ bool enable_stroke_offset)
{
LineartRenderBuffer *rb;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
int intersections_only = 0; /* Not used right now, but preserve for future. */
+ Object *use_camera;
double t_start;
@@ -4054,11 +4515,12 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
if (!scene->camera) {
return false;
}
+ use_camera = scene->camera;
LineartCache *lc = lineart_init_cache();
- *cached_result = lc;
+ (*cached_result) = lc;
- rb = lineart_create_render_buffer(scene, lmd, lc);
+ rb = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc);
/* Triangle thread testing data size varies depending on the thread count.
* See definition of LineartTriangleThread for details. */
@@ -4066,9 +4528,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
/* This is used to limit calculation to a certain level to save time, lines who have higher
* occlusion levels will get ignored. */
- rb->max_occlusion_level = (lmd->flags & LRT_GPENCIL_USE_CACHE) ?
- lmd->level_end_override :
- (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start);
+ rb->max_occlusion_level = lmd->level_end_override;
/* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */
rb->_source_type = lmd->source_type;
@@ -4078,7 +4538,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
/* Get view vector before loading geometries, because we detect feature lines there. */
lineart_main_get_view_vector(rb);
lineart_main_load_geometries(
- depsgraph, scene, scene->camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS);
+ depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS);
if (!rb->vertex_buffer_pointers.first) {
/* No geometry loaded, return early. */
@@ -4108,6 +4568,14 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
* can do its job. */
lineart_main_add_triangles(rb);
+#ifdef LINEART_WITH_BVH
+ lineart_do_intersections(rb);
+#else
+ if (G.debug_value == 4000) {
+ printf("Legacy triangle isec pairs: %d\n", rb->debug_triangle_isec_test_count);
+ }
+#endif
+
/* Link lines to acceleration structure, this can only be done after perspective division, if
* we do it after triangles being added, the acceleration structure has already been
* subdivided, this way we do less list manipulations. */
@@ -4139,10 +4607,22 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
/* This configuration ensures there won't be accidental lost of short unchained segments. */
MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON);
+ if (rb->chain_smooth_tolerance > FLT_EPSILON) {
+ /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best
+ * effective range in image-space (Coordinate only goes from -1 to 1). This value is
+ * somewhat arbitrary, but works best for the moment. */
+ MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50);
+ }
+
if (rb->angle_splitting_threshold > FLT_EPSILON) {
MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold);
}
+ if (enable_stroke_offset && lmd->stroke_offset > FLT_EPSILON) {
+ MOD_lineart_chain_offset_towards_camera(
+ rb, lmd->stroke_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA);
+ }
+
/* Finally transfer the result list into cache. */
memcpy(&lc->chains, &rb->chains, sizeof(ListBase));
@@ -4234,7 +4714,7 @@ static void lineart_gpencil_generate(LineartCache *cache,
if (ec->level > level_end || ec->level < level_start) {
continue;
}
- if (orig_ob && orig_ob != ec->object_ref) {
+ if (orig_ob && orig_ob != ec->object_ref && ec->type != LRT_EDGE_FLAG_INTERSECTION) {
continue;
}
if (orig_col && ec->object_ref) {
@@ -4266,6 +4746,19 @@ static void lineart_gpencil_generate(LineartCache *cache,
}
}
}
+ if (ec->type == LRT_EDGE_FLAG_INTERSECTION &&
+ (mask_switches & LRT_GPENCIL_INTERSECTION_FILTER)) {
+ if (mask_switches & LRT_GPENCIL_INTERSECTION_MATCH) {
+ if (ec->intersection_mask != intersection_mask) {
+ continue;
+ }
+ }
+ else {
+ if (!(ec->intersection_mask & intersection_mask)) {
+ continue;
+ }
+ }
+ }
/* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
// ec->picked = 1;
@@ -4376,26 +4869,20 @@ void MOD_lineart_gpencil_generate(LineartCache *cache,
Object *source_object = NULL;
Collection *source_collection = NULL;
- short use_types = 0;
+ short use_types = edge_types;
if (source_type == LRT_SOURCE_OBJECT) {
if (!source_reference) {
return;
}
source_object = (Object *)source_reference;
- /* Note that intersection lines will only be in collection. */
- use_types = edge_types & (~LRT_EDGE_FLAG_INTERSECTION);
}
else if (source_type == LRT_SOURCE_COLLECTION) {
if (!source_reference) {
return;
}
source_collection = (Collection *)source_reference;
- use_types = edge_types;
- }
- else {
- /* Whole scene. */
- use_types = edge_types;
}
+
float gp_obmat_inverse[4][4];
invert_m4_m4(gp_obmat_inverse, ob->obmat);
lineart_gpencil_generate(cache,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 9d109320f09..c862c488cf5 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -38,26 +38,28 @@ struct LineartStaticMemPoolNode;
struct LineartEdge;
struct LineartRenderBuffer;
-void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data);
-void *lineart_list_append_pointer_pool_sized(ListBase *h,
+void *lineart_list_append_pointer_pool(ListBase *list,
+ struct LineartStaticMemPool *smp,
+ void *data);
+void *lineart_list_append_pointer_pool_sized(ListBase *list,
struct LineartStaticMemPool *smp,
void *data,
int size);
-void *lineart_list_append_pointer_pool_thread(ListBase *h,
+void *lineart_list_append_pointer_pool_thread(ListBase *list,
struct LineartStaticMemPool *smp,
void *data);
-void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
+void *lineart_list_append_pointer_pool_sized_thread(ListBase *list,
LineartStaticMemPool *smp,
void *data,
int size);
-void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p);
-void *list_push_pointer_static_sized(ListBase *h,
+void *list_push_pointer_static(ListBase *list, struct LineartStaticMemPool *smp, void *p);
+void *list_push_pointer_static_sized(ListBase *list,
struct LineartStaticMemPool *smp,
void *p,
int size);
-void *lineart_list_pop_pointer_no_free(ListBase *h);
-void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
+void *lineart_list_pop_pointer_no_free(ListBase *list);
+void lineart_list_remove_pointer_item_no_free(ListBase *list, LinkData *lip);
struct LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp,
size_t size);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
index 988c90483a6..b74499daf6b 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
@@ -118,12 +118,12 @@ static bool bake_strokes(Object *ob,
}
LineartCache *local_lc = *lc;
if (!(*lc)) {
- MOD_lineart_compute_feature_lines(dg, lmd, lc);
+ MOD_lineart_compute_feature_lines(dg, lmd, lc, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
else {
if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) {
- MOD_lineart_compute_feature_lines(dg, lmd, &local_lc);
+ MOD_lineart_compute_feature_lines(dg, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT)));
MOD_lineart_destroy_render_data(lmd);
}
MOD_lineart_chain_clear_picked_flag(local_lc);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
index 47cca0ecd61..f905a305802 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
@@ -37,71 +37,73 @@
/* Line art memory and list helper */
-void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, void *data)
+void *lineart_list_append_pointer_pool(ListBase *list, LineartStaticMemPool *smp, void *data)
{
LinkData *lip;
- if (h == NULL) {
+ if (list == NULL) {
return 0;
}
lip = lineart_mem_acquire(smp, sizeof(LinkData));
lip->data = data;
- BLI_addtail(h, lip);
+ BLI_addtail(list, lip);
return lip;
}
-void *lineart_list_append_pointer_pool_sized(ListBase *h,
+void *lineart_list_append_pointer_pool_sized(ListBase *list,
LineartStaticMemPool *smp,
void *data,
int size)
{
LinkData *lip;
- if (h == NULL) {
+ if (list == NULL) {
return 0;
}
lip = lineart_mem_acquire(smp, size);
lip->data = data;
- BLI_addtail(h, lip);
+ BLI_addtail(list, lip);
return lip;
}
-void *lineart_list_append_pointer_pool_thread(ListBase *h, LineartStaticMemPool *smp, void *data)
+void *lineart_list_append_pointer_pool_thread(ListBase *list,
+ LineartStaticMemPool *smp,
+ void *data)
{
LinkData *lip;
- if (h == NULL) {
+ if (list == NULL) {
return 0;
}
lip = lineart_mem_acquire_thread(smp, sizeof(LinkData));
lip->data = data;
- BLI_addtail(h, lip);
+ BLI_addtail(list, lip);
return lip;
}
-void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
+void *lineart_list_append_pointer_pool_sized_thread(ListBase *list,
LineartStaticMemPool *smp,
void *data,
int size)
{
LinkData *lip;
- if (h == NULL) {
+ if (list == NULL) {
return 0;
}
lip = lineart_mem_acquire_thread(smp, size);
lip->data = data;
- BLI_addtail(h, lip);
+ BLI_addtail(list, lip);
return lip;
}
-void *lineart_list_pop_pointer_no_free(ListBase *h)
+void *lineart_list_pop_pointer_no_free(ListBase *list)
{
LinkData *lip;
void *rev = 0;
- if (h == NULL) {
+ if (list == NULL) {
return 0;
}
- lip = BLI_pophead(h);
+ lip = BLI_pophead(list);
rev = lip ? lip->data : 0;
return rev;
}
-void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
+void lineart_list_remove_pointer_item_no_free(ListBase *list, LinkData *lip)
{
- BLI_remlink(h, (void *)lip);
+ BLI_remlink(list, (void *)lip);
}
LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size)
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index b9697beeea9..1bd2a2a56c6 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -307,6 +307,8 @@
.calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_image_threshold = 0.001f, \
+ .stroke_offset = 0.05,\
+ .overscan = 0.1f,\
}
#define _DNA_DEFAULT_LengthGpencilModifierData \
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index fac5bd3d4f4..378599015e8 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -926,13 +926,15 @@ typedef enum eLineArtGPencilModifierFlags {
LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */,
LRT_GPENCIL_IS_BAKED = (1 << 3),
LRT_GPENCIL_USE_CACHE = (1 << 4),
+ LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5),
} eLineArtGPencilModifierFlags;
typedef enum eLineartGpencilMaskSwitches {
LRT_GPENCIL_MATERIAL_MASK_ENABLE = (1 << 0),
- /** When set, material mask bit comparisons are done with bit wise "AND" instead of "OR". */
+ /** Set to true means using "and" instead of "or" logic on mask bits. */
LRT_GPENCIL_MATERIAL_MASK_MATCH = (1 << 1),
- LRT_GPENCIL_INTERSECTION_MATCH = (1 << 2),
+ LRT_GPENCIL_INTERSECTION_FILTER = (1 << 2),
+ LRT_GPENCIL_INTERSECTION_MATCH = (1 << 3),
} eLineartGpencilMaskSwitches;
struct LineartCache;
@@ -942,7 +944,7 @@ struct LineartCache;
typedef struct LineartGpencilModifierData {
GpencilModifierData modifier;
- short edge_types; /* line type enable flags, bits in eLineartEdgeFlag */
+ uint16_t edge_types; /* line type enable flags, bits in eLineartEdgeFlag */
char source_type; /* Object or Collection, from eLineartGpencilModifierSource */
@@ -950,6 +952,9 @@ typedef struct LineartGpencilModifierData {
short level_start;
short level_end;
+ struct Object *source_camera;
+ struct Object *light_contour_object;
+
struct Object *source_object;
struct Collection *source_collection;
@@ -963,6 +968,12 @@ typedef struct LineartGpencilModifierData {
char source_vertex_group[64];
char vgname[64];
+ /* Camera focal length is divided by (1 + overscan), before caluclation, which give a wider FOV,
+ * this doesn't change coordinates range internally (-1, 1), but makes the caluclated frame
+ * bigger than actual output. This is for the easier shifting calculation. A value of 0.5 means
+ * the "internal" focal length become 2/3 of the actual camera. */
+ float overscan;
+
float opacity;
short thickness;
@@ -987,6 +998,9 @@ typedef struct LineartGpencilModifierData {
/* eLineArtGPencilModifierFlags, modifier internal state. */
int flags;
+ /* Move strokes towards camera to avoid clipping while preserve depth for the viewport. */
+ float stroke_offset;
+
/* Runtime data. */
/* Because we can potentially only compute features lines once per modifier stack (Use Cache), we
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index d7e62d04af2..58473313ebc 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -70,6 +70,7 @@ typedef enum eLineartEdgeFlag {
/** Limited to 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */
} eLineartEdgeFlag;
-#define LRT_EDGE_FLAG_ALL_TYPE 0x3f
+#define LRT_EDGE_FLAG_ALL_TYPE 0x7f
+#define LRT_EDGE_FLAG_TYPE_MAX_BITS 7
#endif
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index b7354aaa724..ac18116cc84 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -163,6 +163,10 @@ typedef enum eMaterialLineArtFlags {
LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS = (1 << 1),
} eMaterialLineArtFlags;
+#define LRT_TRANSPARENCY_BITS_DNA 0x3f /* Bits 1<<0 to 1<<6. */
+#define LRT_TRANSPARENCY_BITS 0xfc /* Bits 1<<2 to 1<<7. */
+#define LRT_OCCLUSION_EFFECTIVE_BITS 0x03 /* Bits 1<<0 to 1<<1. */
+
typedef struct Material {
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index f1c05079d9c..5b3b06fb3a8 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -2885,7 +2885,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
srna, "Line Art Modifier", "Generate line art strokes from selected source");
RNA_def_struct_sdna(srna, "LineartGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
-
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);