LineArt: Shadow and related functionalities.

This patch includes the full shadow functionality for LineArt:

- Light contour and cast shadow lines.
- Lit/shaded region selection.
- Enclosed light/shadow shape calculation.
- Silhouette/anti-silhouette selection.
- Intersection priority based on shadow edge identifier.

Reviewed By: Sebastian Parborg (zeddb)

Differential Revision: https://developer.blender.org/D15109
This commit is contained in:
YimingWu 2022-06-29 22:54:29 +08:00
parent 2ac5b55289
commit 6dd8ceef2a
Notes: blender-bot 2023-02-13 15:19:53 +01:00
Referenced by issue #98498, LineArt shadow functionality design notes
26 changed files with 2782 additions and 345 deletions

View File

@ -96,6 +96,12 @@ class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
if i == 3:
row = col.row(align=True)
row = layout.row(heading="Intersection Priority")
row.prop(collection, "use_lineart_intersection_priority", text="")
subrow = row.row()
subrow.active = collection.use_lineart_intersection_priority
subrow.prop(collection, "lineart_intersection_priority", text="")
class COLLECTION_PT_collection_custom_props(CollectionButtonsPanel, PropertyPanel, Panel):
_context_path = "collection"

View File

@ -287,6 +287,12 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
row = layout.row(align=True, heading="Custom Occlusion")
row.prop(lineart, "mat_occlusion", text="Levels")
row = layout.row(heading="Intersection Priority")
row.prop(lineart, "use_intersection_priority_override", text="")
subrow = row.row()
subrow.active = lineart.use_intersection_priority_override
subrow.prop(lineart, "intersection_priority", text="")
classes = (
MATERIAL_MT_context_menu,

View File

@ -317,6 +317,12 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
subrow.active = lineart.use_crease_override
subrow.prop(lineart, "crease_threshold", slider=True, text="")
row = layout.row(heading="Intersection Priority")
row.prop(lineart, "use_intersection_priority_override", text="")
subrow = row.row()
subrow.active = lineart.use_intersection_priority_override
subrow.prop(lineart, "intersection_priority", text="")
class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel):
#bl_label = "Object Motion Paths"

View File

@ -383,6 +383,8 @@ typedef struct GpencilLineartLimitInfo {
char min_level;
char max_level;
short edge_types;
char shadow_selection;
char silhouette_selection;
} GpencilLineartLimitInfo;
GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const struct Object *ob);

View File

@ -221,6 +221,9 @@ GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const Object *ob
info.max_level = MAX2(info.max_level,
(lmd->use_multiple_levels ? lmd->level_end : lmd->level_start));
info.edge_types |= lmd->edge_types;
info.shadow_selection = MAX2(lmd->shadow_selection, info.shadow_selection);
info.silhouette_selection = MAX2(lmd->silhouette_selection, info.silhouette_selection);
is_first = false;
}
}
}
@ -237,11 +240,15 @@ void BKE_gpencil_set_lineart_modifier_limits(GpencilModifierData *md,
lmd->level_start_override = info->min_level;
lmd->level_end_override = info->max_level;
lmd->edge_types_override = info->edge_types;
lmd->shadow_selection_override = info->shadow_selection;
lmd->shadow_use_silhouette_override = info->silhouette_selection;
}
else {
lmd->level_start_override = lmd->level_start;
lmd->level_end_override = lmd->level_end;
lmd->edge_types_override = lmd->edge_types;
lmd->shadow_selection_override = lmd->shadow_selection;
lmd->shadow_use_silhouette_override = lmd->silhouette_selection;
}
}

View File

@ -40,6 +40,10 @@ MINLINE void swap_v2_v2(float a[2], float b[2]);
MINLINE void swap_v3_v3(float a[3], float b[3]);
MINLINE void swap_v4_v4(float a[4], float b[4]);
MINLINE void swap_v2_v2_db(double a[2], double b[2]);
MINLINE void swap_v3_v3_db(double a[3], double b[3]);
MINLINE void swap_v4_v4_db(double a[4], double b[4]);
/* unsigned char */
MINLINE void copy_v2_v2_uchar(unsigned char r[2], const unsigned char a[2]);

View File

@ -316,6 +316,27 @@ MINLINE void swap_v4_v4(float a[4], float b[4])
SWAP(float, a[3], b[3]);
}
MINLINE void swap_v2_v2_db(double a[2], double b[2])
{
SWAP(double, a[0], b[0]);
SWAP(double, a[1], b[1]);
}
MINLINE void swap_v3_v3_db(double a[3], double b[3])
{
SWAP(double, a[0], b[0]);
SWAP(double, a[1], b[1]);
SWAP(double, a[2], b[2]);
}
MINLINE void swap_v4_v4_db(double a[4], double b[4])
{
SWAP(double, a[0], b[0]);
SWAP(double, a[1], b[1]);
SWAP(double, a[2], b[2]);
SWAP(double, a[3], b[3]);
}
/* float args -> vec */
MINLINE void copy_v2_fl2(float v[2], float x, float y)

View File

@ -3126,6 +3126,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
FOREACH_NODETREE_END;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
LISTBASE_FOREACH (GpencilModifierData *, gpd, &ob->greasepencil_modifiers) {
if (gpd->type == eGpencilModifierType_Lineart) {
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)gpd;
lmd->shadow_camera_near = 0.1f;
lmd->shadow_camera_far = 200.0f;
lmd->shadow_camera_size = 200.0f;
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 303, 2)) {

View File

@ -68,6 +68,7 @@ set(SRC
intern/lineart/lineart_cpp_bridge.cc
intern/lineart/lineart_cpu.c
intern/lineart/lineart_ops.c
intern/lineart/lineart_shadow.c
intern/lineart/lineart_util.c
intern/lineart/MOD_lineart.h

View File

@ -88,6 +88,8 @@ static void generate_strokes_actual(
lmd->intersection_mask,
lmd->thickness,
lmd->opacity,
lmd->shadow_selection,
lmd->silhouette_selection,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);
@ -193,6 +195,7 @@ static void bakeModifier(Main *UNUSED(bmain),
* modifiers in the stack. */
lmd->edge_types_override = lmd->edge_types;
lmd->level_end_override = lmd->level_end;
lmd->shadow_selection_override = lmd->shadow_selection;
MOD_lineart_compute_feature_lines(
depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT)));
@ -263,6 +266,10 @@ static void updateDepsgraph(GpencilModifierData *md,
DEG_add_object_relation(
ctx->node, ctx->scene->camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier");
}
if (lmd->light_contour_object) {
DEG_add_object_relation(
ctx->node, lmd->light_contour_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
}
}
static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
@ -274,6 +281,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
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)
@ -340,31 +348,107 @@ static void edge_types_panel_draw(const bContext *UNUSED(C), Panel *panel)
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 has_light = RNA_pointer_get(ptr, "light_contour_object").data != NULL;
uiLayoutSetEnabled(layout, !is_baked);
uiLayoutSetPropSep(layout, true);
uiLayout *sub = uiLayoutRow(layout, false);
uiLayoutSetActive(sub, has_light);
uiItemR(sub, ptr, "shadow_region_filtering", 0, IFACE_("Illumination Filtering"), ICON_NONE);
uiLayout *col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "use_contour", 0, IFACE_("Contour"), ICON_NONE);
uiItemR(col, ptr, "use_loose", 0, IFACE_("Loose"), ICON_NONE);
uiItemR(col, ptr, "use_material", 0, IFACE_("Material Borders"), ICON_NONE);
uiItemR(col, ptr, "use_edge_mark", 0, IFACE_("Edge Marks"), ICON_NONE);
uiItemR(col, ptr, "use_intersection", 0, IFACE_("Intersections"), ICON_NONE);
sub = uiLayoutRowWithHeading(col, false, IFACE_("Create"));
uiItemR(sub, ptr, "use_contour", 0, "", ICON_NONE);
uiLayout *sub = uiLayoutRowWithHeading(col, false, IFACE_("Crease"));
uiLayout *entry = uiLayoutRow(sub, true);
uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_contour"));
uiItemR(entry, ptr, "silhouette_filtering", 0, "", ICON_NONE);
const int silhouette_filtering = RNA_enum_get(ptr, "silhouette_filtering");
if (silhouette_filtering != LRT_SILHOUETTE_FILTER_NONE) {
uiItemR(entry, ptr, "use_invert_silhouette", 0, "", ICON_ARROW_LEFTRIGHT);
}
sub = uiLayoutRow(col, false);
uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE);
uiLayout *entry = uiLayoutRow(sub, false);
entry = uiLayoutColumn(sub, false);
uiItemL(entry, IFACE_("Crease"), ICON_NONE);
uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_crease") || is_first);
if (use_cache && !is_first) {
uiItemL(entry, IFACE_("Angle Cached"), ICON_INFO);
uiItemL(entry, IFACE_("Crease Angle Cached"), ICON_INFO);
}
else {
uiItemR(entry, ptr, "crease_threshold", UI_ITEM_R_SLIDER, " ", ICON_NONE);
uiItemR(entry,
ptr,
"crease_threshold",
UI_ITEM_R_SLIDER | UI_ITEM_R_FORCE_BLANK_DECORATE,
IFACE_("Default Angle"),
ICON_NONE);
}
uiItemR(layout, ptr, "use_overlap_edge_type_support", 0, IFACE_("Allow Overlap"), ICON_NONE);
uiItemR(col, ptr, "use_intersection", 0, IFACE_("Intersections"), ICON_NONE);
uiItemR(col, ptr, "use_material", 0, IFACE_("Material Borders"), ICON_NONE);
uiItemR(col, ptr, "use_edge_mark", 0, IFACE_("Edge Marks"), ICON_NONE);
uiItemR(col, ptr, "use_loose", 0, IFACE_("Loose"), ICON_NONE);
entry = uiLayoutColumn(col, false);
uiLayoutSetActive(entry, has_light);
sub = uiLayoutRow(entry, false);
uiItemR(sub, ptr, "use_light_contour", 0, IFACE_("Light Contour"), ICON_NONE);
uiItemR(entry, ptr, "use_shadow", 0, IFACE_("Cast Shadow"), ICON_NONE);
uiItemL(layout, IFACE_("Options"), ICON_NONE);
sub = uiLayoutColumn(layout, false);
if (use_cache && !is_first) {
uiItemL(sub, IFACE_("Type overlapping cached"), ICON_INFO);
}
else {
uiItemR(sub,
ptr,
"use_overlap_edge_type_support",
0,
IFACE_("Allow Overlapping Types"),
ICON_NONE);
}
}
static void options_light_reference_draw(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_cache = RNA_boolean_get(ptr, "use_cache");
const bool has_light = RNA_pointer_get(ptr, "light_contour_object").data != NULL;
const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data);
uiLayoutSetPropSep(layout, true);
uiLayoutSetEnabled(layout, !is_baked);
if (use_cache && !is_first) {
uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO);
return;
}
uiItemR(layout, ptr, "light_contour_object", 0, NULL, ICON_NONE);
uiLayout *remaining = uiLayoutColumn(layout, false);
uiLayoutSetActive(remaining, has_light);
uiItemR(remaining, ptr, "shadow_camera_size", 0, NULL, ICON_NONE);
uiLayout *col = uiLayoutColumn(remaining, true);
uiItemR(col, ptr, "shadow_camera_near", 0, "Near", ICON_NONE);
uiItemR(col, ptr, "shadow_camera_far", 0, "Far", ICON_NONE);
uiItemR(layout, ptr, "shadow_enclosed_shapes", 0, IFACE_("Eclosed Shapes"), ICON_NONE);
}
static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
@ -584,7 +668,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(col, ptr, "use_fuzzy_intersections", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_fuzzy_all", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_loose_edge_chain", 0, IFACE_("Loose Edges"), ICON_NONE);
uiItemR(col, ptr, "use_loose_as_contour", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_loose_as_contour", 0, IFACE_("Loose Edges As Contour"), ICON_NONE);
uiItemR(col, ptr, "use_detail_preserve", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_geometry_space_chain", 0, IFACE_("Geometry Space"), ICON_NONE);
@ -695,6 +779,12 @@ static void panelRegister(ARegionType *region_type)
gpencil_modifier_subpanel_register(
region_type, "edge_types", "Edge Types", NULL, edge_types_panel_draw, panel_type);
gpencil_modifier_subpanel_register(region_type,
"light_reference",
"Light Reference",
NULL,
options_light_reference_draw,
panel_type);
gpencil_modifier_subpanel_register(
region_type, "geometry", "Geometry Processing", NULL, options_panel_draw, panel_type);
PanelType *occlusion_panel = gpencil_modifier_subpanel_register(

View File

@ -41,6 +41,12 @@ typedef struct LineartTriangle {
uint8_t mat_occlusion;
uint8_t flags; /* #eLineartTriangleFlags */
/* target_reference = (obi->obindex | triangle_index) */
/* higher 12 bits-------^ ^-----index in object, lower 20 bits */
uint32_t target_reference;
uint8_t intersection_priority;
/**
* Only use single link list, because we don't need to go back in order.
* This variable is also reused to store the pointer to adjacent lines of this triangle before
@ -66,6 +72,7 @@ typedef enum eLineArtElementNodeFlag {
LRT_ELEMENT_IS_ADDITIONAL = (1 << 0),
LRT_ELEMENT_BORDER_ONLY = (1 << 1),
LRT_ELEMENT_NO_INTERSECTION = (1 << 2),
LRT_ELEMENT_INTERSECTION_DATA = (1 << 3),
} eLineArtElementNodeFlag;
typedef struct LineartElementLinkNode {
@ -75,52 +82,80 @@ typedef struct LineartElementLinkNode {
void *object_ref;
eLineArtElementNodeFlag flags;
/* For edge element link nodes, used for shadow edge matching. */
int obindex;
/** Per object value, always set, if not enabled by #ObjectLineArt, then it's set to global. */
float crease_threshold;
} LineartElementLinkNode;
typedef struct LineartEdgeSegment {
struct LineartEdgeSegment *next, *prev;
/** at==0: left at==1: right (this is in 2D projected space) */
double at;
/** Occlusion level after "at" point */
/** The point after which a property of the segment is changed, e.g. occlusion/material mask etc.
* ratio==0: v1 ratio==1: v2 (this is in 2D projected space), */
double ratio;
/** Occlusion level after "ratio" point */
uint8_t occlusion;
/* Used to filter line art occlusion edges */
uint8_t material_mask_bits;
/* Lit/shaded flag for shadow is stored here.
* TODO(Yiming): Transfer material masks from shadow results
* onto here so then we can even filter transparent shadows. */
uint32_t shadow_mask_bits;
} LineartEdgeSegment;
typedef struct LineartShadowEdge {
struct LineartShadowEdge *next, *prev;
/* Two end points in framebuffer coordinates viewed from the light source. */
double fbc1[4], fbc2[4];
double g1[3], g2[3];
bool orig1, orig2;
struct LineartEdge *e_ref;
struct LineartEdge *e_ref_light_contour;
struct LineartEdgeSegment *es_ref; /* Only for 3rd stage casting. */
ListBase shadow_segments;
} LineartShadowEdge;
enum eLineartShadowSegmentFlag {
LRT_SHADOW_CASTED = 1,
LRT_SHADOW_FACING_LIGHT = 2,
};
/* Represents a cutting point on a #LineartShadowEdge */
typedef struct LineartShadowSegment {
struct LineartShadowSegment *next, *prev;
/* eLineartShadowSegmentFlag */
int flag;
/* The point after which a property of the segment is changed. e.g. shadow mask/target_ref etc.
* Coordinates in NDC during shadow caluclation but transformed to global linear before cutting
* onto edges during the loading stage of the "actual" rendering. */
double ratio;
/* Left and right pos, because when casting shadows at some point there will be
* non-continuous cuts, see #lineart_shadow_edge_cut for detailed explaination. */
double fbc1[4], fbc2[4];
/* Global position. */
double g1[4], g2[4];
uint32_t target_reference;
uint32_t shadow_mask_bits;
} LineartShadowSegment;
typedef struct LineartVert {
double gloc[3];
double fbcoord[4];
/* Scene global index. */
int index;
/**
* Intersection data flag is here, when LRT_VERT_HAS_INTERSECTION_DATA is set,
* size of the struct is extended to include intersection data.
* See #eLineArtVertFlags.
*/
uint8_t flag;
} LineartVert;
typedef struct LineartVertIntersection {
struct LineartVert base;
/** Use vert index because we only use this to check vertex equal. This way we save 8 Bytes. */
int isec1, isec2;
struct LineartTriangle *intersecting_with;
} LineartVertIntersection;
typedef enum eLineArtVertFlags {
LRT_VERT_HAS_INTERSECTION_DATA = (1 << 0),
LRT_VERT_EDGE_USED = (1 << 1),
} eLineArtVertFlags;
typedef struct LineartEdge {
struct LineartVert *v1, *v2;
/** These two variables are also used to specify original edge and segment during 3rd stage
* reprojection, So we can easily find out the line which results come from. */
struct LineartTriangle *t1, *t2;
ListBase segments;
int8_t min_occ;
@ -128,6 +163,19 @@ typedef struct LineartEdge {
uint16_t flags;
uint8_t intersection_mask;
/** Matches the shadow result, used to determine whether a line is in the shadow or not.
* #edge_identifier usages:
* - Intersection lines:
* ((e->t1->target_reference << 32) | e->t2->target_reference);
* - Other lines: LRT_EDGE_IDENTIFIER(obi, e);
* - After shadow calculation: (search the shadow result and set reference to that);
*/
uint64_t edge_identifier;
/** - Light contour: original_e->t1->target_reference | original_e->t2->target_reference.
* - Cast shadow: triangle_projected_onto->target_reference. */
uint64_t target_reference;
/**
* Still need this entry because culled lines will not add to object
* #LineartElementLinkNode node (known as `eln` internally).
@ -146,8 +194,8 @@ typedef struct LineartEdgeChain {
float length;
/** Used when re-connecting and grease-pencil stroke generation. */
int8_t picked;
int8_t level;
uint8_t picked;
uint8_t level;
/** Chain now only contains one type of segments */
int type;
@ -155,8 +203,10 @@ typedef struct LineartEdgeChain {
int loop_id;
uint8_t material_mask_bits;
uint8_t intersection_mask;
uint32_t shadow_mask_bits;
struct Object *object_ref;
struct Object *silhouette_backdrop;
} LineartEdgeChain;
typedef struct LineartEdgeChainItem {
@ -170,6 +220,7 @@ typedef struct LineartEdgeChainItem {
uint8_t occlusion;
uint8_t material_mask_bits;
uint8_t intersection_mask;
uint32_t shadow_mask_bits;
size_t index;
} LineartEdgeChainItem;
@ -201,6 +252,11 @@ enum eLineArtTileRecursiveLimit {
#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100
#define LRT_TILE_EDGE_COUNT_INITIAL 32
enum eLineartShadowCameraType {
LRT_SHADOW_CAMERA_DIRECTIONAL = 1,
LRT_SHADOW_CAMERA_POINT = 2,
};
typedef struct LineartPendingEdges {
LineartEdge **array;
int max;
@ -216,6 +272,14 @@ typedef struct LineartData {
LineartStaticMemPool render_data_pool;
/* A pointer to LineartCache::chain_data_pool, which acts as a cache for edge chains. */
LineartStaticMemPool *chain_data_pool;
/* Reference to LineartCache::shadow_data_pool, stay available until the final round of line art
* calculation is finished. */
LineartStaticMemPool *shadow_data_pool;
/* Storing shadow edge eln, array, and cuts for shadow information, so it's avaliable when line
* art runs the second time for occlusion. Either a reference to LineartCache::shadow_data_pool
* (shadow stage) or a reference to LineartData::render_data_pool (final stage). */
LineartStaticMemPool *edge_data_pool;
struct _qtree {
@ -230,7 +294,7 @@ typedef struct LineartData {
struct LineartBoundingArea *initials;
uint32_t tile_count;
uint32_t initial_tile_count;
} qtree;
@ -267,6 +331,14 @@ typedef struct LineartData {
bool use_edge_marks;
bool use_intersections;
bool use_loose;
bool use_light_contour;
bool use_shadow;
bool use_contour_secondary; /* From viewing camera, during shadow calculation. */
int shadow_selection; /* Needs to be numeric because it's not just on/off. */
bool shadow_enclose_shapes;
bool shadow_use_silhouette;
bool fuzzy_intersections;
bool fuzzy_everything;
bool allow_boundaries;
@ -289,13 +361,21 @@ typedef struct LineartData {
bool chain_preserve_details;
bool do_shadow_cast;
bool light_reference_available;
/* Keep an copy of these data so when line art is running it's self-contained. */
bool cam_is_persp;
bool cam_is_persp_secondary; /* "Secondary" ones are from viewing camera (as opposed to shadow
camera), during shadow calculation. */
float cam_obmat[4][4];
float cam_obmat_secondary[4][4];
double camera_pos[3];
double camera_pos_secondary[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;
@ -303,7 +383,7 @@ typedef struct LineartData {
float chain_smooth_tolerance;
double view_vector[3];
double view_vector_secondary[3]; /* For shadow. */
} conf;
LineartElementLinkNode *isect_scheduled_up_to;
@ -313,22 +393,32 @@ typedef struct LineartData {
struct LineartPendingEdges pending_edges;
int scheduled_count;
/* Intermediate shadow results, list of LineartShadowEdge */
LineartShadowEdge *shadow_edges;
int shadow_edges_count;
ListBase chains;
ListBase wasted_cuts;
ListBase wasted_shadow_cuts;
SpinLock lock_cuts;
SpinLock lock_task;
} LineartData;
typedef struct LineartCache {
/** Separate memory pool for chain data, this goes to the cache, so when we free the main pool,
* chains will still be available. */
/** Separate memory pool for chain data and shadow, this goes to the cache, so when we free the
* main pool, chains and shadows will still be available. */
LineartStaticMemPool chain_data_pool;
LineartStaticMemPool shadow_data_pool;
/** A copy of ld->chains so we have that data available after ld has been destroyed. */
ListBase chains;
/** Shadow-computed feature lines from original meshes to be matched with the second load of
* meshes thus providing lit/shade info in the second run of line art. */
ListBase shadow_elns;
/** Cache only contains edge types specified in this variable. */
uint16_t all_enabled_edge_types;
} LineartCache;
@ -348,6 +438,14 @@ typedef enum eLineartTriangleFlags {
LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5),
} eLineartTriangleFlags;
#define LRT_SHADOW_MASK_UNDEFINED 0
#define LRT_SHADOW_MASK_LIT (1 << 0)
#define LRT_SHADOW_MASK_SHADED (1 << 1)
#define LRT_SHADOW_MASK_ENCLOSED_SHAPE (1 << 2)
#define LRT_SHADOW_MASK_INHIBITED (1 << 3)
#define LRT_SHADOW_SILHOUETTE_ERASED_GROUP (1 << 4)
#define LRT_SHADOW_SILHOUETTE_ERASED_OBJECT (1 << 5)
/**
* Controls how many edges a worker thread is processing at one request.
* There's no significant performance impact on choosing different values.
@ -368,6 +466,14 @@ typedef struct LineartRenderTaskInfo {
} LineartRenderTaskInfo;
#define LRT_OBINDEX_SHIFT 20
#define LRT_OBINDEX_LOWER 0x0FFFFF /* Lower 20 bits. */
#define LRT_OBINDEX_HIGHER 0xFFF00000 /* Higher 12 bits. */
#define LRT_EDGE_IDENTIFIER(obi, e) \
(((uint64_t)(obi->obindex | (e->v1->index & LRT_OBINDEX_LOWER)) << 32) | \
(obi->obindex | (e->v2->index & LRT_OBINDEX_LOWER)))
#define LRT_LIGHT_CONTOUR_TARGET 0xFFFFFFFF
typedef struct LineartObjectInfo {
struct LineartObjectInfo *next;
struct Object *original_ob;
@ -378,8 +484,12 @@ typedef struct LineartObjectInfo {
LineartElementLinkNode *v_eln;
int usage;
uint8_t override_intersection_mask;
uint8_t intersection_priority;
int global_i_offset;
/* Shifted LRT_OBINDEX_SHIFT bits to be combined with object triangle index. */
int obindex;
bool free_use_mesh;
/** NOTE: Data inside #pending_edges are allocated with MEM_xxx call instead of in pool. */
@ -394,6 +504,7 @@ typedef struct LineartObjectLoadTaskInfo {
LineartObjectInfo *pending;
/* Used to spread the load across several threads. This can not overflow. */
uint64_t total_faces;
ListBase *shadow_elns;
} LineartObjectLoadTaskInfo;
/**
@ -466,6 +577,10 @@ typedef struct LineartBoundingArea {
#define LRT_DOUBLE_CLOSE_ENOUGH_TRI(a, b) \
(((a) + DBL_TRIANGLE_LIM) >= (b) && ((a)-DBL_TRIANGLE_LIM) <= (b))
#define LRT_CLOSE_LOOSER_v3(a, b) \
(LRT_DOUBLE_CLOSE_LOOSER(a[0], b[0]) && LRT_DOUBLE_CLOSE_LOOSER(a[1], b[1]) && \
LRT_DOUBLE_CLOSE_LOOSER(a[2], b[2]))
/* Notes on this function:
*
* r_ratio: The ratio on segment a1-a2. When r_ratio is very close to zero or one, it
@ -478,10 +593,10 @@ typedef struct LineartBoundingArea {
* segment a is shared with segment b. If it's a1 then r_ratio is 0, else then r_ratio is 1. This
* extra information is needed for line art occlusion stage to work correctly in such cases.
*/
BLI_INLINE int lineart_intersect_seg_seg(const double *a1,
const double *a2,
const double *b1,
const double *b2,
BLI_INLINE int lineart_intersect_seg_seg(const double a1[2],
const double a2[2],
const double b1[2],
const double b2[2],
double *r_ratio,
bool *r_aligned)
{
@ -622,6 +737,99 @@ BLI_INLINE int lineart_intersect_seg_seg(const double *a1,
#endif
}
/* This is a special convenience function to lineart_intersect_seg_seg which will return true when
* the intersection point falls in the range of a1-a2 but not necessarily in the range of b1-b2. */
BLI_INLINE int lineart_line_isec_2d_ignore_line2pos(const double a1[2],
const double a2[2],
const double b1[2],
const double b2[2],
double *r_a_ratio)
{
/* The define here is used to check how vector or slope method handles boundary cases. The result
* of lim(div->0) and lim(k->0) could both produce some unwanted flickers in line art, the
* influence of which is still not fully understood, so keep the switch there for futher
* investigations. */
#define USE_VECTOR_LINE_INTERSECTION_IGN
#ifdef USE_VECTOR_LINE_INTERSECTION_IGN
/* from isect_line_line_v2_point() */
double s10[2], s32[2];
double div;
sub_v2_v2v2_db(s10, a2, a1);
sub_v2_v2v2_db(s32, b2, b1);
div = cross_v2v2_db(s10, s32);
if (div != 0.0f) {
const double u = cross_v2v2_db(a2, a1);
const double v = cross_v2v2_db(b2, b1);
const double rx = ((s32[0] * u) - (s10[0] * v)) / div;
const double ry = ((s32[1] * u) - (s10[1] * v)) / div;
if (fabs(a2[0] - a1[0]) > fabs(a2[1] - a1[1])) {
*r_a_ratio = ratiod(a1[0], a2[0], rx);
if ((*r_a_ratio) >= -DBL_EDGE_LIM && (*r_a_ratio) <= 1 + DBL_EDGE_LIM) {
return 1;
}
return 0;
}
*r_a_ratio = ratiod(a1[1], a2[1], ry);
if ((*r_a_ratio) >= -DBL_EDGE_LIM && (*r_a_ratio) <= 1 + DBL_EDGE_LIM) {
return 1;
}
return 0;
}
return 0;
#else
double k1, k2;
double x;
double y;
double ratio;
double x_diff = (a2[0] - a1[0]);
double x_diff2 = (b2[0] - b1[0]);
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff, 0)) {
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
*r_a_ratio = 0;
return 0;
}
double r2 = ratiod(b1[0], b2[0], a1[0]);
x = interpd(b2[0], b1[0], r2);
y = interpd(b2[1], b1[1], r2);
*r_a_ratio = ratio = ratiod(a1[1], a2[1], y);
}
else {
if (LRT_DOUBLE_CLOSE_ENOUGH(x_diff2, 0)) {
ratio = ratiod(a1[0], a2[0], b1[0]);
x = interpd(a2[0], a1[0], ratio);
*r_a_ratio = ratio;
}
else {
k1 = (a2[1] - a1[1]) / x_diff;
k2 = (b2[1] - b1[1]) / x_diff2;
if ((k1 == k2))
return 0;
x = (a1[1] - b1[1] - k1 * a1[0] + k2 * b1[0]) / (k2 - k1);
ratio = (x - a1[0]) / x_diff;
*r_a_ratio = ratio;
}
}
if (ratio <= 0 || ratio >= 1)
return 0;
return 1;
#endif
}
struct Depsgraph;
struct LineartGpencilModifierData;
struct LineartData;
@ -646,6 +854,7 @@ void MOD_lineart_chain_clip_at_border(LineartData *ld);
void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad);
void MOD_lineart_smooth_chains(LineartData *ld, float tolerance);
void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool use_custom_camera);
void MOD_lineart_chain_find_silhouette_backdrop_objects(LineartData *ld);
int MOD_lineart_chain_count(const LineartEdgeChain *ec);
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc);
@ -694,6 +903,8 @@ void MOD_lineart_gpencil_generate(LineartCache *cache,
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);

View File

@ -17,13 +17,16 @@
#define LRT_OTHER_VERT(e, vt) ((vt) == (e)->v1 ? (e)->v2 : ((vt) == (e)->v2 ? (e)->v1 : NULL))
struct Object;
/* Get a connected line, only for lines who has the exact given vert, or (in the case of
* intersection lines) who has a vert that has the exact same position. */
static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
LineartVert *vt,
LineartVert **new_vt,
int match_flag,
uint8_t match_isec_mask)
uint8_t match_isec_mask,
struct Object *match_isec_object)
{
for (int i = 0; i < ba->line_count; i++) {
LineartEdge *n_e = ba->linked_lines[i];
@ -46,6 +49,9 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
}
if (n_e->flags & LRT_EDGE_FLAG_INTERSECTION) {
if (n_e->object_ref != match_isec_object) {
continue;
}
if (vt->fbcoord[0] == n_e->v1->fbcoord[0] && vt->fbcoord[1] == n_e->v1->fbcoord[1]) {
*new_vt = LRT_OTHER_VERT(n_e, n_e->v1);
return n_e;
@ -87,12 +93,13 @@ static bool lineart_point_overlapping(LineartEdgeChainItem *eci,
static LineartEdgeChainItem *lineart_chain_append_point(LineartData *ld,
LineartEdgeChain *ec,
float *fbcoord,
float *gpos,
float *normal,
float fbcoord[4],
float gpos[3],
float normal[3],
uint8_t type,
int level,
uint8_t material_mask_bits,
uint32_t shadow_mask_bits,
size_t index)
{
LineartEdgeChainItem *eci;
@ -105,6 +112,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartData *ld,
old_eci->line_type = type;
old_eci->occlusion = level;
old_eci->material_mask_bits = material_mask_bits;
old_eci->shadow_mask_bits = shadow_mask_bits;
return old_eci;
}
@ -117,6 +125,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartData *ld,
eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
eci->occlusion = level;
eci->material_mask_bits = material_mask_bits;
eci->shadow_mask_bits = shadow_mask_bits;
BLI_addtail(&ec->chain, eci);
return eci;
@ -124,12 +133,13 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartData *ld,
static LineartEdgeChainItem *lineart_chain_prepend_point(LineartData *ld,
LineartEdgeChain *ec,
float *fbcoord,
float *gpos,
float *normal,
float fbcoord[4],
float gpos[3],
float normal[3],
uint8_t type,
int level,
uint8_t material_mask_bits,
uint32_t shadow_mask_bits,
size_t index)
{
LineartEdgeChainItem *eci;
@ -147,6 +157,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartData *ld,
eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
eci->occlusion = level;
eci->material_mask_bits = material_mask_bits;
eci->shadow_mask_bits = shadow_mask_bits;
BLI_addhead(&ec->chain, eci);
return eci;
@ -160,6 +171,7 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
LineartEdgeSegment *es;
int last_occlusion;
uint8_t last_transparency;
uint32_t last_shadow;
/* Used when converting from double. */
float use_fbcoord[4];
float use_gpos[3];
@ -219,9 +231,10 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
e->flags,
es->occlusion,
es->material_mask_bits,
es->shadow_mask_bits,
e->v1->index);
while (ba && (new_e = lineart_line_get_connected(
ba, new_vt, &new_vt, e->flags, e->intersection_mask))) {
ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref))) {
new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_e->t1 || new_e->t2) {
@ -243,8 +256,8 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
for (es = new_e->segments.last; es; es = es->prev) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
@ -256,21 +269,24 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
new_e->flags,
es->occlusion,
es->material_mask_bits,
es->shadow_mask_bits,
new_e->v1->index);
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
}
}
else if (new_vt == new_e->v2) {
es = new_e->segments.first;
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
es = es->next;
for (; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
@ -282,9 +298,11 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
new_e->flags,
last_occlusion,
last_transparency,
last_shadow,
new_e->v2->index);
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
}
VERT_COORD_TO_FLOAT(new_e->v2);
lineart_chain_prepend_point(ld,
@ -295,6 +313,7 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
new_e->flags,
last_occlusion,
last_transparency,
last_shadow,
new_e->v2->index);
}
ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]);
@ -318,13 +337,14 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
/* Step 2: Adding all cuts from the given line, so we can continue connecting the right side
* of the line. */
es = e->segments.first;
last_occlusion = ((LineartEdgeSegment *)es)->occlusion;
last_transparency = ((LineartEdgeSegment *)es)->material_mask_bits;
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
for (es = es->next; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = e->v1->fbcoord, *rfb = e->v2->fbcoord;
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at);
double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->ratio);
interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at);
use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
@ -336,9 +356,11 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
e->flags,
es->occlusion,
es->material_mask_bits,
es->shadow_mask_bits,
e->v1->index);
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
}
VERT_COORD_TO_FLOAT(e->v2)
lineart_chain_append_point(ld,
@ -349,13 +371,14 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
e->flags,
last_occlusion,
last_transparency,
last_shadow,
e->v2->index);
/* Step 3: grow right. */
ba = MOD_lineart_get_bounding_area(ld, e->v2->fbcoord[0], e->v2->fbcoord[1]);
new_vt = e->v2;
while (ba && (new_e = lineart_line_get_connected(
ba, new_vt, &new_vt, e->flags, e->intersection_mask))) {
ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref))) {
new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_e->t1 || new_e->t2) {
@ -381,18 +404,21 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
es = new_e->segments.last;
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
/* Fix leading vertex occlusion. */
eci->occlusion = last_occlusion;
eci->material_mask_bits = last_transparency;
eci->shadow_mask_bits = last_shadow;
for (es = new_e->segments.last; es; es = es->prev) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
last_occlusion = es->prev ? es->prev->occlusion : last_occlusion;
last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency;
last_shadow = es->prev ? es->prev->shadow_mask_bits : last_shadow;
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(ld,
ec,
@ -402,6 +428,7 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
new_e->flags,
last_occlusion,
last_transparency,
last_shadow,
new_e->v1->index);
}
}
@ -409,14 +436,16 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
es = new_e->segments.first;
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
eci->occlusion = last_occlusion;
eci->material_mask_bits = last_transparency;
eci->shadow_mask_bits = last_shadow;
es = es->next;
for (; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
@ -428,9 +457,11 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
new_e->flags,
es->occlusion,
es->material_mask_bits,
es->shadow_mask_bits,
new_e->v2->index);
last_occlusion = es->occlusion;
last_transparency = es->material_mask_bits;
last_shadow = es->shadow_mask_bits;
}
VERT_COORD_TO_FLOAT(new_e->v2)
lineart_chain_append_point(ld,
@ -441,6 +472,7 @@ void MOD_lineart_chain_feature_lines(LineartData *ld)
new_e->flags,
last_occlusion,
last_transparency,
last_shadow,
new_e->v2->index);
}
ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]);
@ -509,7 +541,7 @@ static void lineart_bounding_area_link_point_recursive(LineartData *ld,
{
if (root->child == NULL) {
LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized(
&root->linked_chains, &ld->render_data_pool, ec, sizeof(LineartChainRegisterEntry));
&root->linked_chains, ld->chain_data_pool, ec, sizeof(LineartChainRegisterEntry));
cre->eci = eci;
@ -565,6 +597,7 @@ static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec,
int fixed_occ = last_matching_eci->occlusion;
uint8_t fixed_mask = last_matching_eci->material_mask_bits;
uint32_t fixed_shadow = last_matching_eci->shadow_mask_bits;
LineartEdgeChainItem *can_skip_to = NULL;
LineartEdgeChainItem *last_eci = last_matching_eci;
@ -579,7 +612,8 @@ static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec,
if (eci->occlusion < fixed_occ) {
break;
}
if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ) {
if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ &&
eci->shadow_mask_bits == fixed_shadow) {
can_skip_to = eci;
}
}
@ -589,12 +623,14 @@ static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec,
LineartEdgeChainItem *next_eci;
for (LineartEdgeChainItem *eci = last_matching_eci->next; eci != can_skip_to; eci = next_eci) {
next_eci = eci->next;
if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ) {
if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ &&
eci->shadow_mask_bits == fixed_shadow) {
continue;
}
if (preserve_details) {
eci->material_mask_bits = fixed_mask;
eci->occlusion = fixed_occ;
eci->shadow_mask_bits = fixed_shadow;
}
else {
BLI_remlink(&ec->chain, eci);
@ -628,11 +664,14 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld)
LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
int fixed_occ = first_eci->occlusion;
uint8_t fixed_mask = first_eci->material_mask_bits;
uint32_t fixed_shadow = first_eci->shadow_mask_bits;
ec->level = fixed_occ;
ec->material_mask_bits = fixed_mask;
ec->shadow_mask_bits = fixed_shadow;
for (eci = first_eci->next; eci; eci = next_eci) {
next_eci = eci->next;
if (eci->occlusion != fixed_occ || eci->material_mask_bits != fixed_mask) {
if (eci->occlusion != fixed_occ || eci->material_mask_bits != fixed_mask ||
eci->shadow_mask_bits != fixed_shadow) {
if (next_eci) {
if (lineart_point_overlapping(next_eci, eci->pos[0], eci->pos[1], 1e-5)) {
continue;
@ -649,6 +688,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld)
/* Set the same occlusion level for the end vertex, so when further connection is needed
* the backwards occlusion info is also correct. */
eci->occlusion = fixed_occ;
eci->shadow_mask_bits = fixed_shadow;
eci->material_mask_bits = fixed_mask;
/* No need to split at the last point anyway. */
break;
@ -670,6 +710,7 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld)
eci->line_type,
fixed_occ,
fixed_mask,
fixed_shadow,
eci->index);
new_ec->object_ref = ec->object_ref;
new_ec->type = ec->type;
@ -677,8 +718,10 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld)
ec = new_ec;
fixed_occ = eci->occlusion;
fixed_mask = eci->material_mask_bits;
fixed_shadow = eci->shadow_mask_bits;
ec->level = fixed_occ;
ec->material_mask_bits = fixed_mask;
ec->shadow_mask_bits = fixed_shadow;
}
}
}
@ -749,6 +792,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartData *ld,
int occlusion,
uint8_t material_mask_bits,
uint8_t isec_mask,
uint32_t shadow_mask,
int loop_id,
float dist,
float *result_new_len,
@ -779,7 +823,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartData *ld,
}
if (cre->ec == ec || (!cre->ec->chain.first) || (cre->ec->level != occlusion) ||
(cre->ec->material_mask_bits != material_mask_bits) ||
(cre->ec->intersection_mask != isec_mask)) {
(cre->ec->intersection_mask != isec_mask) || (cre->ec->shadow_mask_bits != shadow_mask)) {
continue;
}
if (!ld->conf.fuzzy_everything) {
@ -826,6 +870,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartData *ld,
occlusion, \
material_mask_bits, \
isec_mask, \
shadow_mask, \
loop_id, \
dist, \
&adjacent_new_len, \
@ -856,8 +901,9 @@ void MOD_lineart_chain_connect(LineartData *ld)
LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre;
float dist = ld->conf.chaining_image_threshold;
float dist_l, dist_r;
int occlusion, reverse_main, loop_id;
uint8_t material_mask_bits, isec_mask;
int reverse_main, loop_id;
uint8_t occlusion, material_mask_bits, isec_mask;
uint32_t shadow_mask;
ListBase swap = {0};
if (ld->conf.chaining_image_threshold < 0.0001) {
@ -871,7 +917,7 @@ void MOD_lineart_chain_connect(LineartData *ld)
while ((ec = BLI_pophead(&swap)) != NULL) {
ec->next = ec->prev = NULL;
if (ec->picked) {
if (ec->picked || ec->chain.first == ec->chain.last) {
continue;
}
BLI_addtail(&ld->chains, ec);
@ -884,6 +930,7 @@ void MOD_lineart_chain_connect(LineartData *ld)
occlusion = ec->level;
material_mask_bits = ec->material_mask_bits;
isec_mask = ec->intersection_mask;
shadow_mask = ec->shadow_mask_bits;
eci_l = ec->chain.first;
eci_r = ec->chain.last;
@ -896,6 +943,7 @@ void MOD_lineart_chain_connect(LineartData *ld)
occlusion,
material_mask_bits,
isec_mask,
shadow_mask,
loop_id,
dist,
&dist_l,
@ -907,6 +955,7 @@ void MOD_lineart_chain_connect(LineartData *ld)
occlusion,
material_mask_bits,
isec_mask,
shadow_mask,
loop_id,
dist,
&dist_r,
@ -1072,20 +1121,22 @@ static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartData *ld
LineartEdgeChainItem *eci_outside)
{
float isec[2];
float LU[2] = {-1.0f, 1.0f}, LB[2] = {-1.0f, -1.0f}, RU[2] = {1.0f, 1.0f}, RB[2] = {1.0f, -1.0f};
/* l: left, r: right, b: bottom, u: top. */
float ref_lu[2] = {-1.0f, 1.0f}, ref_lb[2] = {-1.0f, -1.0f}, ref_ru[2] = {1.0f, 1.0f},
ref_rb[2] = {1.0f, -1.0f};
bool found = false;
LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside;
if (eci2->pos[0] < -1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, LB, isec) > 0);
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lu, ref_lb, isec) > 0);
}
if (!found && eci2->pos[0] > 1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, RU, RB, isec) > 0);
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_ru, ref_rb, isec) > 0);
}
if (!found && eci2->pos[1] < -1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LB, RB, isec) > 0);
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lb, ref_rb, isec) > 0);
}
if (!found && eci2->pos[1] > 1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, RU, isec) > 0);
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lu, ref_ru, isec) > 0);
}
if (UNLIKELY(!found)) {
@ -1126,7 +1177,7 @@ void MOD_lineart_chain_clip_at_border(LineartData *ld)
LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
is_inside = LRT_ECI_INSIDE(first_eci) ? true : false;
if (!is_inside) {
ec->picked = true;
ec->picked = 1;
}
for (eci = first_eci->next; eci; eci = next_eci) {
next_eci = eci->next;
@ -1219,6 +1270,7 @@ void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad)
eci->line_type,
ec->level,
eci->material_mask_bits,
eci->shadow_mask_bits,
eci->index);
new_ec->object_ref = ec->object_ref;
new_ec->type = ec->type;
@ -1226,6 +1278,7 @@ void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad)
new_ec->loop_id = ec->loop_id;
new_ec->intersection_mask = ec->intersection_mask;
new_ec->material_mask_bits = ec->material_mask_bits;
new_ec->shadow_mask_bits = ec->shadow_mask_bits;
ec = new_ec;
}
}
@ -1270,3 +1323,19 @@ void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool u
}
}
}
void MOD_lineart_chain_find_silhouette_backdrop_objects(LineartData *ld)
{
LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) {
if (ec->type == LRT_EDGE_FLAG_CONTOUR &&
ec->shadow_mask_bits & LRT_SHADOW_SILHOUETTE_ERASED_GROUP) {
uint32_t target = ec->shadow_mask_bits & LRT_OBINDEX_HIGHER;
LineartElementLinkNode *eln = lineart_find_matching_eln(&ld->geom.line_buffer_pointers,
target);
if (!eln) {
continue;
}
ec->silhouette_backdrop = eln->object_ref;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -66,14 +66,16 @@ int lineart_count_intersection_segment_count(struct LineartData *ld);
void lineart_count_and_print_render_buffer_memory(struct LineartData *ld);
#define LRT_ITER_ALL_LINES_BEGIN \
LineartEdge *e; \
for (int i = 0; i < ld->pending_edges.next; i++) { \
e = ld->pending_edges.array[i];
{ \
LineartEdge *e; \
for (int __i = 0; __i < ld->pending_edges.next; __i++) { \
e = ld->pending_edges.array[__i];
#define LRT_ITER_ALL_LINES_NEXT ; /* Doesn't do anything now with new array setup. */
#define LRT_ITER_ALL_LINES_END \
LRT_ITER_ALL_LINES_NEXT \
} \
}
#define LRT_BOUND_AREA_CROSSES(b1, b2) \
@ -83,11 +85,94 @@ void lineart_count_and_print_render_buffer_memory(struct LineartData *ld);
* performance under current algorithm. */
#define LRT_BA_ROWS 10
#define LRT_EDGE_BA_MARCHING_BEGIN(fb1, fb2) \
double x = fb1[0], y = fb1[1]; \
LineartBoundingArea *ba = lineart_edge_first_bounding_area(ld, fb1, fb2); \
LineartBoundingArea *nba = ba; \
double k = (fb2[1] - fb1[1]) / (fb2[0] - fb1[0] + 1e-30); \
int positive_x = (fb2[0] - fb1[0]) > 0 ? 1 : (fb2[0] == fb1[0] ? 0 : -1); \
int positive_y = (fb2[1] - fb1[1]) > 0 ? 1 : (fb2[1] == fb1[1] ? 0 : -1); \
while (nba)
#define LRT_EDGE_BA_MARCHING_NEXT(fb1, fb2) \
/* Marching along `e->v1` to `e->v2`, searching each possible bounding areas it may touch. */ \
nba = lineart_bounding_area_next(nba, fb1, fb2, x, y, k, positive_x, positive_y, &x, &y);
#define LRT_EDGE_BA_MARCHING_END
void lineart_main_occlusion_begin(struct LineartData *ld);
void lineart_main_cull_triangles(struct LineartData *ld, bool clip_far);
void lineart_main_free_adjacent_data(struct LineartData *ld);
void lineart_main_perspective_division(struct LineartData *ld);
void lineart_main_discard_out_of_frame_edges(struct LineartData *ld);
void lineart_main_load_geometries(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *camera,
struct LineartData *ld,
bool allow_duplicates,
bool do_shadow_casting,
struct ListBase *shadow_elns);
void lineart_main_get_view_vector(struct LineartData *ld);
void lineart_main_bounding_area_make_initial(struct LineartData *ld);
void lineart_main_bounding_areas_connect_post(struct LineartData *ld);
void lineart_main_clear_linked_edges(struct LineartData *ld);
void lineart_main_link_lines(struct LineartData *ld);
void lineart_main_add_triangles(struct LineartData *ld);
bool lineart_main_try_generate_shadow(struct Depsgraph *depsgraph,
struct Scene *scene,
struct LineartData *original_ld,
struct LineartGpencilModifierData *lmd,
struct LineartStaticMemPool *shadow_data_pool,
struct LineartElementLinkNode **r_veln,
struct LineartElementLinkNode **r_eeln,
struct ListBase *r_calculated_edges_eln_list,
struct LineartData **r_shadow_ld_if_reproject);
void lineart_main_make_enclosed_shapes(struct LineartData *ld, struct LineartData *shadow_ld);
void lineart_main_transform_and_add_shadow(struct LineartData *ld,
struct LineartElementLinkNode *veln,
struct LineartElementLinkNode *eeln);
LineartElementLinkNode *lineart_find_matching_eln(struct ListBase *shadow_elns, int obindex);
LineartEdge *lineart_find_matching_edge(struct LineartElementLinkNode *shadow_eln,
uint64_t edge_identifier);
void lineart_register_shadow_cuts(struct LineartData *ld,
struct LineartEdge *e,
struct LineartEdge *shadow_edge);
void lineart_register_intersection_shadow_cuts(struct LineartData *ld,
struct ListBase *shadow_elns);
bool lineart_edge_from_triangle(const struct LineartTriangle *tri,
const struct LineartEdge *e,
bool allow_overlapping_edges);
LineartBoundingArea *lineart_edge_first_bounding_area(struct LineartData *ld,
double *fbcoord1,
double *fbcoord2);
LineartBoundingArea *lineart_bounding_area_next(struct LineartBoundingArea *_this,
double *fbcoord1,
double *fbcoord2,
double x,
double y,
double k,
int positive_x,
int positive_y,
double *next_x,
double *next_y);
void lineart_edge_cut(struct LineartData *ld,
struct LineartEdge *e,
double start,
double end,
uchar material_mask_bits,
uchar mat_occlusion,
uint32_t shadow_bits);
void lineart_add_edge_to_array(struct LineartPendingEdges *pe, struct LineartEdge *e);
void lineart_finalize_object_edge_array_reserve(struct LineartPendingEdges *pe, int count);
void lineart_destroy_render_data_keep_init(struct LineartData *ld);
#ifdef __cplusplus
extern "C" {
#endif
void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length);
void lineart_sort_adjacent_items(struct LineartAdjacentEdge *ai, int length);
#ifdef __cplusplus
}

View File

@ -132,6 +132,8 @@ static bool bake_strokes(Object *ob,
lmd->intersection_mask,
lmd->thickness,
lmd->opacity,
lmd->shadow_selection,
lmd->silhouette_selection,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,7 @@ enum eCollectionLineArt_Usage {
enum eCollectionLineArt_Flags {
COLLECTION_LRT_USE_INTERSECTION_MASK = (1 << 0),
COLLECTION_LRT_USE_INTERSECTION_PRIORITY = (1 << 1),
};
typedef struct Collection {
@ -62,7 +63,8 @@ typedef struct Collection {
short lineart_usage; /* eCollectionLineArt_Usage */
unsigned char lineart_flags; /* eCollectionLineArt_Flags */
unsigned char lineart_intersection_mask;
char _pad[6];
unsigned char lineart_intersection_priority;
char _pad[5];
int16_t color_tag;

View File

@ -296,7 +296,7 @@
#define _DNA_DEFAULT_LineartGpencilModifierData \
{ \
.edge_types = LRT_EDGE_FLAG_ALL_TYPE, \
.edge_types = LRT_EDGE_FLAG_INIT_TYPE, \
.thickness = 25, \
.opacity = 1.0f, \
.flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \
@ -306,7 +306,12 @@
/* Do not split by default, this is for better chaining quality. */ \
.angle_splitting_threshold = 0.0f, \
.chaining_image_threshold = 0.001f, \
.chain_smooth_tolerance = 0.2f,\
.stroke_depth_offset = 0.05,\
.chain_smooth_tolerance = 0.0f,\
.overscan = 0.1f,\
.shadow_camera_near = 0.1f, \
.shadow_camera_far = 200.0f, \
.shadow_camera_size = 200.0f, \
.stroke_depth_offset = 0.05,\
}

View File

@ -998,6 +998,18 @@ typedef enum eLineartGpencilModifierSource {
LRT_SOURCE_SCENE = 2,
} eLineartGpencilModifierSource;
typedef enum eLineartGpencilModifierShadowFilter {
LRT_SHADOW_FILTER_NONE = 0,
LRT_SHADOW_FILTER_LIT = 1,
LRT_SHADOW_FILTER_SHADED = 2,
} eLineartGpencilModifierShadowFilter;
typedef enum eLineartGpencilModifierSilhouetteFilter {
LRT_SILHOUETTE_FILTER_NONE = 0,
LRT_SILHOUETTE_FILTER_GROUP = (1 << 0),
LRT_SILHOUETTE_FILTER_INDIVIDUAL = (1 << 1),
} eLineartGpencilModifierSilhouetteFilter;
/* This enum is for modifier internal state only. */
typedef enum eLineArtGPencilModifierFlags {
/* These two moved to #eLineartMainFlags to keep consistent with flag variable purpose. */
@ -1008,6 +1020,7 @@ typedef enum eLineArtGPencilModifierFlags {
LRT_GPENCIL_USE_CACHE = (1 << 4),
LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5),
LRT_GPENCIL_INVERT_COLLECTION = (1 << 6),
LRT_GPENCIL_INVERT_SILHOUETTE_FILTER = (1 << 7),
} eLineArtGPencilModifierFlags;
typedef enum eLineartGpencilMaskSwitches {
@ -1024,8 +1037,7 @@ struct LineartCache;
typedef struct LineartGpencilModifierData {
GpencilModifierData modifier;
/** Line type enable flags, bits in #eLineartEdgeFlag. */
short edge_types;
uint16_t edge_types; /* line type enable flags, bits in eLineartEdgeFlag */
/** Object or Collection, from #eLineartGpencilModifierSource. */
char source_type;
@ -1035,6 +1047,7 @@ typedef struct LineartGpencilModifierData {
short level_end;
struct Object *source_camera;
struct Object *light_contour_object;
struct Object *source_object;
struct Collection *source_collection;
@ -1049,14 +1062,19 @@ typedef struct LineartGpencilModifierData {
char source_vertex_group[64];
char vgname[64];
/**
* Camera focal length is divided by `1 + overscan`, before calculation, which give a wider FOV,
* this doesn't change coordinates range internally (-1, 1), but makes the calculated frame
/* 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.
*/
* the "internal" focal length become 2/3 of the actual camera. */
float overscan;
/* Values for point light and directional (sun) light. */
/* For point light, fov always gonna be 120 deg horizontal, with 3 "cameras" covering 360 deg. */
float shadow_camera_fov;
float shadow_camera_size;
float shadow_camera_near;
float shadow_camera_far;
float opacity;
short thickness;
@ -1064,7 +1082,9 @@ typedef struct LineartGpencilModifierData {
unsigned char material_mask_bits;
unsigned char intersection_mask;
char _pad[3];
unsigned char shadow_selection;
unsigned char silhouette_selection;
char _pad[1];
/** `0..1` range for cosine angle */
float crease_threshold;
@ -1078,7 +1098,7 @@ typedef struct LineartGpencilModifierData {
/* CPU mode */
float chaining_image_threshold;
/* Ported from SceneLineArt flags. */
/* eLineartMainFlags, for one time calculation. */
int calculation_flags;
/* #eLineArtGPencilModifierFlags, modifier internal state. */
@ -1095,6 +1115,10 @@ typedef struct LineartGpencilModifierData {
char level_start_override;
char level_end_override;
short edge_types_override;
char shadow_selection_override;
char shadow_use_silhouette_override;
char _pad2[6];
struct LineartCache *cache;
/* Keep a pointer to the render buffer so we can call destroy from ModifierData. */

View File

@ -38,6 +38,8 @@ typedef enum eLineartMainFlags {
LRT_USE_BACK_FACE_CULLING = (1 << 19),
LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20),
LRT_CHAIN_PRESERVE_DETAILS = (1 << 22),
LRT_SHADOW_ENCLOSED_SHAPES = (1 << 23),
LRT_SHADOW_USE_SILHOUETTE = (1 << 24),
} eLineartMainFlags;
typedef enum eLineartEdgeFlag {
@ -47,14 +49,19 @@ typedef enum eLineartEdgeFlag {
LRT_EDGE_FLAG_MATERIAL = (1 << 3),
LRT_EDGE_FLAG_INTERSECTION = (1 << 4),
LRT_EDGE_FLAG_LOOSE = (1 << 5),
LRT_EDGE_FLAG_LIGHT_CONTOUR = (1 << 6),
/* LRT_EDGE_FLAG_FOR_FUTURE = (1 << 7), */
/**
* It's a legacy limit of 8 bits for feature lines that come from original mesh edges. It should
* not be needed in current object loading scheme, but might still be relevant if we are to
* implement edit-mesh loading, so don't exceed 8 bits just yet.
*/
LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 8),
LRT_EDGE_FLAG_CLIPPED = (1 << 9),
/* It's a legacy limit of 8 bits for feature lines that come from original mesh edges. It should
not be needed in current object loading scheme, but might still be relevant if we are to
impelment EditMesh loading, so don't exceed 8 bits just yet. */
LRT_EDGE_FLAG_PROJECTED_SHADOW = (1 << 8),
/* To determine an edge to be occluded from the front or back face it's lying on. */
LRT_EDGE_FLAG_SHADOW_FACING_LIGHT = (1 << 9),
/** Also used as discarded line mark. */
LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 10),
LRT_EDGE_FLAG_CLIPPED = (1 << 11),
/** Used to specify contor from viewing camera when computing shadows. */
LRT_EDGE_FLAG_CONTOUR_SECONDARY = (1 << 12),
/** Limited to 16 bits for the entire thing. */
/** For object loading code to use only. */
@ -63,4 +70,6 @@ typedef enum eLineartEdgeFlag {
LRT_EDGE_FLAG_NEXT_IS_DUPLICATION = (1 << 15),
} eLineartEdgeFlag;
#define LRT_EDGE_FLAG_ALL_TYPE 0x3f
#define LRT_EDGE_FLAG_ALL_TYPE 0x01ff
#define LRT_EDGE_FLAG_INIT_TYPE 0x37 /* Without material & light contour */
#define LRT_EDGE_FLAG_TYPE_MAX_BITS 7

View File

@ -150,14 +150,15 @@ typedef struct MaterialLineArt {
/** Maximum 255 levels of equivalent occlusion. */
unsigned char mat_occlusion;
unsigned char _pad[2];
unsigned char intersection_priority;
char _pad;
} MaterialLineArt;
typedef enum eMaterialLineArtFlags {
LRT_MATERIAL_MASK_ENABLED = (1 << 0),
/* Deprecated, kept for versioning code. */
LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS = (1 << 1),
LRT_MATERIAL_CUSTOM_INTERSECTION_PRIORITY = (1 << 2),
} eMaterialLineArtFlags;
typedef struct Material {

View File

@ -215,6 +215,10 @@ typedef struct ObjectLineArt {
/** if OBJECT_LRT_OWN_CREASE is set */
float crease_threshold;
unsigned char intersection_priority;
char _pad[7];
} ObjectLineArt;
/**
@ -231,6 +235,7 @@ enum eObjectLineArt_Usage {
enum eObjectLineArt_Flags {
OBJECT_LRT_OWN_CREASE = (1 << 0),
OBJECT_LRT_OWN_INTERSECTION_PRIORITY = (1 << 1),
};
typedef struct Object {

View File

@ -549,6 +549,22 @@ void RNA_def_collections(BlenderRNA *brna)
prop, "Masks", "Intersection generated by this collection will have this mask value");
RNA_def_property_update(prop, NC_SCENE, NULL);
prop = RNA_def_property(srna, "lineart_intersection_priority", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 0, 255);
RNA_def_property_ui_text(prop,
"Intersection Priority",
"The intersection line will be included into the object with the "
"higher intersection priority value");
RNA_def_property_update(prop, NC_SCENE, NULL);
prop = RNA_def_property(srna, "use_lineart_intersection_priority", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(
prop, NULL, "lineart_flags", COLLECTION_LRT_USE_INTERSECTION_PRIORITY);
RNA_def_property_ui_text(
prop, "Use Intersection Priority", "Assign intersection priority value for this collection");
RNA_def_property_update(prop, NC_SCENE, NULL);
prop = RNA_def_property(srna, "color_tag", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "color_tag");
RNA_def_property_enum_funcs(

View File

@ -3196,6 +3196,20 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem modifier_lineart_shadow_region_filtering[] = {
{LRT_SHADOW_FILTER_NONE, "NONE", 0, "None", ""},
{LRT_SHADOW_FILTER_LIT, "LIT", 0, "Lit", ""},
{LRT_SHADOW_FILTER_SHADED, "SHADED", 0, "Shaded", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem modifier_lineart_silhouette_filtering[] = {
{LRT_SILHOUETTE_FILTER_NONE, "NONE", 0, "Contour", ""},
{LRT_SILHOUETTE_FILTER_GROUP, "GROUP", 0, "Silhouette", ""},
{LRT_SILHOUETTE_FILTER_INDIVIDUAL, "INDIVIDUAL", 0, "Individual Silhouette", ""},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "LineartGpencilModifier", "GpencilModifier");
RNA_def_struct_ui_text(
srna, "Line Art Modifier", "Generate line art strokes from selected source");
@ -3208,7 +3222,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_CUSTOM_CAMERA);
RNA_def_property_ui_text(
prop, "Use Custom Camera", "Use custom camera instead of the active camera");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR);
@ -3250,8 +3264,11 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop = RNA_def_property(srna, "crease_threshold", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_range(prop, 0, DEG2RAD(180.0f));
RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(180.0f), 0.01f, 1);
RNA_def_property_ui_text(
prop, "Crease Threshold", "Angles smaller than this will be treated as creases");
RNA_def_property_ui_text(prop,
"Crease Threshold",
"Angles smaller than this will be treated as creases. Crease angle "
"priority: object line art crease override > mesh auto smooth angle > "
"line art default crease.");
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "split_angle", PROP_FLOAT, PROP_ANGLE);
@ -3367,6 +3384,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop, "Camera Object", "Use specified camera object for generating line art");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "light_contour_object", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Light Object", "Use this light object to generate light contour");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_lineart_source_type);
RNA_def_property_ui_text(prop, "Source Type", "Line art stroke source type");
@ -3417,6 +3442,41 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use Intersection", "Generate strokes from intersections");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_light_contour", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "edge_types", LRT_EDGE_FLAG_LIGHT_CONTOUR);
RNA_def_property_ui_text(prop,
"Use Light Contour",
"Generate light/shadow separation lines from a reference light object");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "edge_types", LRT_EDGE_FLAG_PROJECTED_SHADOW);
RNA_def_property_ui_text(
prop, "Use Shadow", "Project contour lines using a light shource object");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "shadow_region_filtering", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "shadow_selection");
RNA_def_property_enum_items(prop, modifier_lineart_shadow_region_filtering);
RNA_def_property_ui_text(prop,
"Shadow Region Filtering",
"Select feature lines that comes from lit or shaded regions. Will not "
"affect cast shadow and light contour since they are at the border");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "shadow_enclosed_shapes", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_SHADOW_ENCLOSED_SHAPES);
RNA_def_property_ui_text(prop,
"Shadow Enclosed Shapes",
"Reproject visible lines again to get enclosed shadow shapes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "silhouette_filtering", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "silhouette_selection");
RNA_def_property_enum_items(prop, modifier_lineart_silhouette_filtering);
RNA_def_property_ui_text(prop, "Silhouette Filtering", "Select contour or silhouette");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "use_multiple_levels", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_multiple_levels", 0);
RNA_def_property_ui_text(
@ -3558,6 +3618,27 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
"different occlusion levels than when disabled");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "shadow_camera_near", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop, "Shadow Camera Near", "Near clipping distance of shadow camera");
RNA_def_property_ui_range(prop, 0.0f, 500.0f, 0.1f, 2);
RNA_def_property_range(prop, 0.0f, 10000.0f);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "shadow_camera_far", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop, "Shadow Camera Far", "Far clipping distance of shadow camera");
RNA_def_property_ui_range(prop, 0.0f, 500.0f, 0.1f, 2);
RNA_def_property_range(prop, 0.0f, 10000.0f);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "shadow_camera_size", PROP_FLOAT, PROP_NONE);
RNA_def_property_ui_text(prop,
"Shadow Camera Size",
"This value represent the \"Orthographic Scale\" of an ortho camera."
"If the camera is put at the lamps position with this scale, it will "
"represent the coverage of the shadow \"camera\" ");
RNA_def_property_ui_range(prop, 0.0f, 500.0f, 0.1f, 2);
RNA_def_property_range(prop, 0.0f, 10000.0f);
prop = RNA_def_property(srna, "use_invert_collection", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_INVERT_COLLECTION);
RNA_def_property_ui_text(prop,
@ -3565,6 +3646,11 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
"Select everything except lines from specified collection");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_invert_silhouette", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_INVERT_SILHOUETTE_FILTER);
RNA_def_property_ui_text(prop, "Invert Silhouette Filtering", "Select anti-silhouette lines");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
RNA_define_lib_overridable(false);
}

View File

@ -754,6 +754,22 @@ static void rna_def_material_lineart(BlenderRNA *brna)
"Effectiveness",
"Faces with this material will behave as if it has set number of layers in occlusion");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "intersection_priority", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 0, 255);
RNA_def_property_ui_text(prop,
"Intersection Priority",
"The intersection line will be included into the object with the "
"higher intersection priority value");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
prop = RNA_def_property(srna, "use_intersection_priority_override", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, 0);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_MATERIAL_CUSTOM_INTERSECTION_PRIORITY);
RNA_def_property_ui_text(prop,
"Use Intersection Priority",
"Override object and collection intersection priority value");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update");
}
void RNA_def_material(BlenderRNA *brna)

View File

@ -2950,6 +2950,22 @@ static void rna_def_object_lineart(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(180.0f), 0.01f, 1);
RNA_def_property_ui_text(prop, "Crease", "Angles smaller than this will be treated as creases");
RNA_def_property_update(prop, 0, "rna_object_lineart_update");
prop = RNA_def_property(srna, "use_intersection_priority_override", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", OBJECT_LRT_OWN_INTERSECTION_PRIORITY);
RNA_def_property_ui_text(
prop,
"Use Intersection Priority",
"Use this object's intersection priority to override collection setting");
RNA_def_property_update(prop, 0, "rna_object_lineart_update");
prop = RNA_def_property(srna, "intersection_priority", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 0, 255);
RNA_def_property_ui_text(prop,
"Intersection Priority",
"The intersection line will be included into the object with the "
"higher intersection priority value");
RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_object_lineart_update");
}
static void rna_def_object_visibility(StructRNA *srna)